The customizing mutex type for concurrent_hash_map
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:- 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
}