#ifndef CONCURRENT_ID_MAP_HPP #define CONCURRENT_ID_MAP_HPP #include <boost/interprocess/sync/interprocess_upgradable_mutex.hpp> #include <boost/interprocess/sync/scoped_lock.hpp> #include <boost/interprocess/sync/sharable_lock.hpp> #include <unordered_map> namespace osrm::util { /** * This is a special purpose map for caching incrementing IDs */ template <typename KeyType, typename ValueType, typename HashType = std::hash<KeyType>> struct ConcurrentIDMap { static_assert(std::is_unsigned<ValueType>::value, "Only unsigned integer types are supported."); using UpgradableMutex = boost::interprocess::interprocess_upgradable_mutex; using ScopedReaderLock = boost::interprocess::sharable_lock<UpgradableMutex>; using ScopedWriterLock = boost::interprocess::scoped_lock<UpgradableMutex>; std::unordered_map<KeyType, ValueType, HashType> data; mutable UpgradableMutex mutex; ConcurrentIDMap() = default; ConcurrentIDMap(ConcurrentIDMap &&other) noexcept { if (this != &other) { ScopedWriterLock other_lock{other.mutex}; ScopedWriterLock lock{mutex}; data = std::move(other.data); } } ConcurrentIDMap &operator=(ConcurrentIDMap &&other) noexcept { if (this != &other) { ScopedWriterLock other_lock{other.mutex}; ScopedWriterLock lock{mutex}; data = std::move(other.data); } return *this; } const ValueType ConcurrentFindOrAdd(const KeyType &key) { { ScopedReaderLock sentry{mutex}; const auto result = data.find(key); if (result != data.end()) { return result->second; } } { ScopedWriterLock sentry{mutex}; const auto result = data.find(key); if (result != data.end()) { return result->second; } const auto id = static_cast<ValueType>(data.size()); data[key] = id; return id; } } }; } // namespace osrm::util #endif // CONCURRENT_ID_MAP_HPP