1.. _custom_mutex_chmap:
2
3The customizing mutex type for ``concurrent_hash_map``
4======================================================
5
6.. note::
7    To enable this feature, define the ``TBB_PREVIEW_CONCURRENT_HASH_MAP_EXTENSIONS`` macro to 1.
8
9.. contents::
10    :local:
11    :depth: 1
12
13Description
14***********
15
16oneTBB ``concurrnent_hash_map`` class uses reader-writer mutex
17to provide thread safety and avoid data races for insert, lookup, and erasure operations. This feature adds an extra template parameter
18for ``concurrent_hash_map`` that allows to customize the type of the reader-writer mutex.
19
20API
21***
22
23Header
24------
25
26.. code:: cpp
27
28    #include <oneapi/tbb/concurrent_hash_map.h>
29
30Synopsis
31--------
32
33.. code:: cpp
34
35    namespace oneapi {
36    namespace tbb {
37
38        template <typename Key, typename T,
39                typename HashCompare = tbb_hash_compare<Key>,
40                typename Allocator = tbb_allocator<std::pair<const Key, T>>,
41                typename Mutex = spin_rw_mutex>
42        class concurrent_hash_map {
43            using mutex_type = Mutex;
44        };
45
46    } // namespace tbb
47    } // namespace oneapi
48
49Type requirements
50-----------------
51
52The type of the mutex passed as a template argument for ``concurrent_hash_map`` should
53meet the requirements of `ReaderWriterMutex <https://spec.oneapi.com/versions/latest/elements/oneTBB/source/named_requirements/mutexes/rw_mutex.html>`_.
54It should also provide the following API:
55
56.. cpp:function:: bool ReaderWriterMutex::scoped_lock::is_writer() const;
57
58**Returns**: ``true`` if the ``scoped_lock`` object acquires the mutex as a writer, ``false`` otherwise.
59
60The behavior is undefined if the ``scoped_lock`` object does not acquire the mutex.
61
62``oneapi::tbb::spin_rw_mutex``, ``oneapi::tbb::speculative_spin_rw_mutex``, ``oneapi::tbb::queuing_rw_mutex``, ``oneapi::tbb::null_rw_mutex``,
63and ``oneapi::tbb::rw_mutex`` meet the requirements above.
64
65.. rubric:: Example
66
67The example below demonstrates how to wrap ``std::shared_mutex`` (C++17) to meet the requirements
68of `ReaderWriterMutex` and how to customize ``concurrent_hash_map`` to use this mutex.
69
70.. code:: cpp
71
72    #define TBB_PREVIEW_CONCURRENT_HASH_MAP_EXTENSIONS 1
73    #include "oneapi/tbb/concurrent_hash_map.h"
74    #include <shared_mutex>
75
76    class SharedMutexWrapper {
77    public:
78        // ReaderWriterMutex requirements
79
80        static constexpr bool is_rw_mutex = true;
81        static constexpr bool is_recursive_mutex = false;
82        static constexpr bool is_fair_mutex = false;
83
84        class scoped_lock {
85        public:
86            scoped_lock() : my_mutex_ptr(nullptr), my_writer_flag(false) {}
87            scoped_lock(SharedMutexWrapper& mutex, bool write = true)
88                : my_mutex_ptr(&mutex), my_writer_flag(write)
89            {
90                if (my_writer_flag) {
91                    my_mutex_ptr->my_mutex.lock();
92                } else {
93                    my_mutex_ptr->my_mutex.lock_shared();
94                }
95            }
96
97            ~scoped_lock() {
98                if (my_mutex_ptr) release();
99            }
100
101            void acquire(SharedMutexWrapper& mutex, bool write = true) {
102                if (my_mutex_ptr) release();
103
104                my_mutex_ptr = &mutex;
105                my_writer_flag = write;
106
107                if (my_writer_flag) {
108                    my_mutex_ptr->my_mutex.lock();
109                } else {
110                    my_mutex_ptr->my_mutex.lock_shared();
111                }
112            }
113
114            void release() {
115                if (my_writer_flag) {
116                    my_mutex_ptr->my_mutex.unlock();
117                } else {
118                    my_mutex_ptr->my_mutex.unlock_shared();
119                }
120            }
121
122            bool upgrade_to_writer() {
123                // std::shared_mutex does not have the upgrade/downgrade parallel_for_each_semantics
124                if (my_writer_flag) return true; // Already a writer
125
126                my_mutex_ptr->my_mutex.unlock_shared();
127                my_mutex_ptr->my_mutex.lock();
128                return false; // The lock was reacquired
129            }
130
131            bool downgrade_to_reader() {
132                if (!my_writer_flag) return true; // Already a reader
133
134                my_mutex_ptr->my_mutex.unlock();
135                my_mutex_ptr->my_mutex.lock_shared();
136                return false;
137            }
138
139            bool is_writer() const {
140                return my_writer_flag;
141            }
142
143        private:
144            SharedMutexWrapper* my_mutex_ptr;
145            bool                my_writer_flag;
146        };
147    private:
148        std::shared_mutex my_mutex;
149    }; // struct SharedMutexWrapper
150
151    int main() {
152        using map_type = oneapi::tbb::concurrent_hash_map<int, int,
153                                                          oneapi::tbb::tbb_hash_compare<int>,
154                                                          oneapi::tbb::tbb_allocator<std::pair<const int, int>>,
155                                                          SharedMutexWrapper>;
156
157        map_type map; // This object will use SharedMutexWrapper for thread safety of insert/find/erase operations
158    }
159