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