lib/SynchronizedHashMap.h (102 lines of code) (raw):

/** * 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