Visible to Intel only — GUID: GUID-AE01E995-1C5D-4ED8-8807-2210B156653B
Visible to Intel only — GUID: GUID-AE01E995-1C5D-4ED8-8807-2210B156653B
The customizing mutex type for concurrent_hash_map
Description
oneTBB concurrnent_hash_map class uses reader-writer mutex to provide thread safety and avoid data races for insert, lookup, and erasure operations. This feature adds an extra template parameter for concurrent_hash_map that allows to customize the type of the reader-writer mutex.
API
Header
#include <oneapi/tbb/concurrent_hash_map.h>
Synopsis
namespace oneapi { namespace tbb { template <typename Key, typename T, typename HashCompare = tbb_hash_compare<Key>, typename Allocator = tbb_allocator<std::pair<const Key, T>>, typename Mutex = spin_rw_mutex> class concurrent_hash_map { using mutex_type = Mutex; }; } // namespace tbb } // namespace oneapi
Type requirements
The type of the mutex passed as a template argument for concurrent_hash_map should meet the requirements of ReaderWriterMutex. It should also provide the following API:
boolReaderWriterMutex::scoped_lock::is_writer()const;
Returns: true if the scoped_lock object acquires the mutex as a writer, false otherwise.
The behavior is undefined if the scoped_lock object does not acquire the mutex.
oneapi::tbb::spin_rw_mutex, oneapi::tbb::speculative_spin_rw_mutex, oneapi::tbb::queuing_rw_mutex, oneapi::tbb::null_rw_mutex, and oneapi::tbb::rw_mutex meet the requirements above.
Example
The example below demonstrates how to wrap std::shared_mutex (C++17) to meet the requirements of ReaderWriterMutex and how to customize concurrent_hash_map to use this mutex.
#define TBB_PREVIEW_CONCURRENT_HASH_MAP_EXTENSIONS 1 #include "oneapi/tbb/concurrent_hash_map.h" #include <shared_mutex> class SharedMutexWrapper { public: // ReaderWriterMutex requirements static constexpr bool is_rw_mutex = true; static constexpr bool is_recursive_mutex = false; static constexpr bool is_fair_mutex = false; class scoped_lock { public: scoped_lock() : my_mutex_ptr(nullptr), my_writer_flag(false) {} scoped_lock(SharedMutexWrapper& mutex, bool write = true) : my_mutex_ptr(&mutex), my_writer_flag(write) { if (my_writer_flag) { my_mutex_ptr->my_mutex.lock(); } else { my_mutex_ptr->my_mutex.lock_shared(); } } ~scoped_lock() { if (my_mutex_ptr) release(); } void acquire(SharedMutexWrapper& mutex, bool write = true) { if (my_mutex_ptr) release(); my_mutex_ptr = &mutex; my_writer_flag = write; if (my_writer_flag) { my_mutex_ptr->my_mutex.lock(); } else { my_mutex_ptr->my_mutex.lock_shared(); } } void release() { if (my_writer_flag) { my_mutex_ptr->my_mutex.unlock(); } else { my_mutex_ptr->my_mutex.unlock_shared(); } } bool upgrade_to_writer() { // std::shared_mutex does not have the upgrade/downgrade parallel_for_each_semantics if (my_writer_flag) return true; // Already a writer my_mutex_ptr->my_mutex.unlock_shared(); my_mutex_ptr->my_mutex.lock(); return false; // The lock was reacquired } bool downgrade_to_reader() { if (!my_writer_flag) return true; // Already a reader my_mutex_ptr->my_mutex.unlock(); my_mutex_ptr->my_mutex.lock_shared(); return false; } bool is_writer() const { return my_writer_flag; } private: SharedMutexWrapper* my_mutex_ptr; bool my_writer_flag; }; private: std::shared_mutex my_mutex; }; // struct SharedMutexWrapper int main() { using map_type = oneapi::tbb::concurrent_hash_map<int, int, oneapi::tbb::tbb_hash_compare<int>, oneapi::tbb::tbb_allocator<std::pair<const int, int>>, SharedMutexWrapper>; map_type map; // This object will use SharedMutexWrapper for thread safety of insert/find/erase operations }