# The customizing mutex type for `concurrent_hash_map`

Note

To enable this feature, define the `TBB_PREVIEW_CONCURRENT_HASH_MAP_EXTENSIONS` macro to 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

    #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:

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.

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();
                }
            }

            bool try_acquire(SharedMutexWrapper& mutex, bool write = true) {
                if (my_mutex_ptr) release();

                my_mutex_ptr = &mutex;

                bool result = false;

                if (my_writer_flag) {
                    result = my_mutex_ptr->my_mutex.try_lock();
                } else {
                    result = my_mutex_ptr->my_mutex.try_lock_shared();
                }

                if (result) my_writer_flag = write;
                return result;
            }

            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 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
    }