blob: 082aeaf49107ac0d341d56eb757bcada5d2d66f9 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#pragma once
#include <boost/optional.hpp>
#include <functional>
#include <mutex>
#include <unordered_map>
#include <utility>
#include <vector>
namespace pulsar {
// V must be default constructible and copyable
template <typename K, typename V>
class SynchronizedHashMap {
using MutexType = std::recursive_mutex;
using Lock = std::lock_guard<MutexType>;
public:
using OptValue = boost::optional<V>;
using PairVector = std::vector<std::pair<K, V>>;
using MapType = std::unordered_map<K, V>;
using Iterator = typename MapType::iterator;
SynchronizedHashMap() = default;
SynchronizedHashMap(const PairVector& pairs) {
for (auto&& kv : pairs) {
data_.emplace(kv.first, kv.second);
}
}
template <typename... Args>
std::pair<Iterator, bool> emplace(Args&&... args) {
Lock lock(mutex_);
return data_.emplace(std::forward<Args>(args)...);
}
void forEach(std::function<void(const K&, const V&)> f) const {
Lock lock(mutex_);
for (const auto& kv : data_) {
f(kv.first, kv.second);
}
}
void forEachValue(std::function<void(const V&)> f) const {
Lock lock(mutex_);
for (const auto& kv : data_) {
f(kv.second);
}
}
void clear() {
Lock lock(mutex_);
data_.clear();
}
// clear the map and apply `f` on each removed value
void clear(std::function<void(const K&, const V&)> f) {
MapType data = move();
for (auto&& kv : data) {
f(kv.first, kv.second);
}
}
OptValue find(const K& key) const {
Lock lock(mutex_);
auto it = data_.find(key);
if (it != data_.end()) {
return it->second;
} else {
return boost::none;
}
}
OptValue findFirstValueIf(std::function<bool(const V&)> f) const {
Lock lock(mutex_);
for (const auto& kv : data_) {
if (f(kv.second)) {
return kv.second;
}
}
return boost::none;
}
OptValue remove(const K& key) {
Lock lock(mutex_);
auto it = data_.find(key);
if (it != data_.end()) {
auto result = boost::make_optional(std::move(it->second));
data_.erase(it);
return result;
} else {
return boost::none;
}
}
// This method is only used for test
PairVector toPairVector() const {
Lock lock(mutex_);
PairVector pairs;
for (auto&& kv : data_) {
pairs.emplace_back(kv);
}
return pairs;
}
size_t size() const noexcept {
Lock lock(mutex_);
return data_.size();
}
MapType move() noexcept {
Lock lock(mutex_);
MapType data;
data_.swap(data);
return data;
}
private:
MapType data_;
// Use recursive_mutex to allow methods being called in `forEach`
mutable MutexType mutex_;
};
} // namespace pulsar