.. _custom_mutex_chmap: The customizing mutex type for ``concurrent_hash_map`` ====================================================== .. note:: To enable this feature, define the ``TBB_PREVIEW_CONCURRENT_HASH_MAP_EXTENSIONS`` macro to 1. .. contents:: :local: :depth: 1 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 ------ .. code:: cpp #include Synopsis -------- .. code:: cpp namespace oneapi { namespace tbb { template , typename Allocator = tbb_allocator>, 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: .. cpp:function:: bool ReaderWriterMutex::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. .. rubric:: 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. .. code:: cpp #define TBB_PREVIEW_CONCURRENT_HASH_MAP_EXTENSIONS 1 #include "oneapi/tbb/concurrent_hash_map.h" #include 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, oneapi::tbb::tbb_allocator>, SharedMutexWrapper>; map_type map; // This object will use SharedMutexWrapper for thread safety of insert/find/erase operations }