|
| concurrent_hash_map () |
| Construct empty table.
|
|
| concurrent_hash_map (size_type n) |
| Construct empty table with n preallocated buckets. More...
|
|
| concurrent_hash_map (const concurrent_hash_map &table) |
| Copy constructor.
|
|
| concurrent_hash_map (concurrent_hash_map &&table) |
| Move constructor.
|
|
template<typename I > |
| concurrent_hash_map (I first, I last) |
| Construction table with copying iteration range.
|
|
| concurrent_hash_map (std::initializer_list< value_type > il) |
| Construct table with initializer list.
|
|
void | runtime_initialize () |
| Initialize persistent concurrent hash map after process restart. More...
|
|
concurrent_hash_map & | operator= (const concurrent_hash_map &table) |
| Assignment Not thread safe. More...
|
|
concurrent_hash_map & | operator= (std::initializer_list< value_type > il) |
| Assignment Not thread safe. More...
|
|
void | rehash (size_type n=0) |
| Rehashes and optionally resizes the whole table. More...
|
|
void | clear () |
| Clear hash map content Not thread safe. More...
|
|
| ~concurrent_hash_map () |
| Clear table and destroy it.
|
|
iterator | begin () |
|
iterator | end () |
|
const_iterator | begin () const |
|
const_iterator | end () const |
|
size_type | size () const |
|
bool | empty () const |
|
size_type | max_size () const |
| Upper bound on size.
|
|
size_type | bucket_count () const |
|
void | swap (concurrent_hash_map &table) |
| Swap two instances. More...
|
|
size_type | count (const Key &key) const |
|
template<typename K , typename = typename std::enable_if< concurrent_hash_map_internal:: has_transparent_key_equal<hasher>::value, K>::type> |
size_type | count (const K &key) const |
| This overload only participates in overload resolution if the qualified-id Hash::transparent_key_equal is valid and denotes a type. More...
|
|
bool | find (const_accessor &result, const Key &key) const |
| Find item and acquire a read lock on the item. More...
|
|
template<typename K , typename = typename std::enable_if< concurrent_hash_map_internal:: has_transparent_key_equal<hasher>::value, K>::type> |
bool | find (const_accessor &result, const K &key) const |
| Find item and acquire a read lock on the item. More...
|
|
bool | find (accessor &result, const Key &key) |
| Find item and acquire a write lock on the item. More...
|
|
template<typename K , typename = typename std::enable_if< concurrent_hash_map_internal:: has_transparent_key_equal<hasher>::value, K>::type> |
bool | find (accessor &result, const K &key) |
| Find item and acquire a write lock on the item. More...
|
|
bool | insert (const_accessor &result, const Key &key) |
| Insert item (if not already present) and acquire a read lock on the item. More...
|
|
bool | insert (accessor &result, const Key &key) |
| Insert item (if not already present) and acquire a write lock on the item. More...
|
|
bool | insert (const_accessor &result, const value_type &value) |
| Insert item by copying if there is no such key present already and acquire a read lock on the item. More...
|
|
bool | insert (accessor &result, const value_type &value) |
| Insert item by copying if there is no such key present already and acquire a write lock on the item. More...
|
|
bool | insert (const value_type &value) |
| Insert item by copying if there is no such key present already. More...
|
|
bool | insert (const_accessor &result, value_type &&value) |
| Insert item by copying if there is no such key present already and acquire a read lock on the item. More...
|
|
bool | insert (accessor &result, value_type &&value) |
| Insert item by copying if there is no such key present already and acquire a write lock on the item. More...
|
|
bool | insert (value_type &&value) |
| Insert item by copying if there is no such key present already. More...
|
|
template<typename I > |
void | insert (I first, I last) |
| Insert range [first, last) More...
|
|
void | insert (std::initializer_list< value_type > il) |
| Insert initializer list. More...
|
|
template<typename M > |
bool | insert_or_assign (const key_type &key, M &&obj) |
| Inserts item if there is no such key present already, assigns provided value otherwise. More...
|
|
template<typename M > |
bool | insert_or_assign (key_type &&key, M &&obj) |
| Inserts item if there is no such key present already, assigns provided value otherwise. More...
|
|
template<typename K , typename M , typename = typename std::enable_if< concurrent_hash_map_internal::has_transparent_key_equal< hasher>::value && std::is_constructible<key_type, K>::value, K>::type> |
bool | insert_or_assign (K &&key, M &&obj) |
| Inserts item if there is no such key-comparable type present already, assigns provided value otherwise. More...
|
|
bool | erase (const Key &key) |
| Remove element with corresponding key. More...
|
|
pobj_defrag_result | defragment (double start_percent=0, double amount_percent=100) |
| Defragment the given (by 'start_percent' and 'amount_percent') part of buckets of the hash map. More...
|
|
template<typename K , typename = typename std::enable_if< concurrent_hash_map_internal:: has_transparent_key_equal<hasher>::value, K>::type> |
bool | erase (const K &key) |
| Remove element with corresponding key. More...
|
|
template<typename Key, typename T, typename Hash, typename KeyEqual, typename MutexType, typename ScopedLockType>
class pmem::obj::concurrent_hash_map< Key, T, Hash, KeyEqual, MutexType, ScopedLockType >
Persistent memory aware implementation of Intel TBB concurrent_hash_map.
The implementation is based on a concurrent hash table algorithm (https://arxiv.org/ftp/arxiv/papers/1509/1509.02235.pdf) where elements assigned to buckets based on a hash code are calculated from a key. In addition to concurrent find, insert, and erase operations, the algorithm employs resizing and on-demand per-bucket rehashing. The hash table consists of an array of buckets, and each bucket consists of a list of nodes and a read-write lock to control concurrent access by multiple threads.
Each time, the pool with concurrent_hash_map is being opened, the concurrent_hash_map requires runtime_initialize() to be called (in order to recalculate mask and restore the size).
find(), insert(), erase() (and all overloads) are guaranteed to be thread-safe.
When a thread holds accessor to an element with a certain key, it is not allowed to call find, insert nor erase with that key.
MutexType defines type of read write lock used in concurrent_hash_map. ScopedLockType defines a mutex wrapper that provides RAII-style mechanism for owning a mutex. It should implement following methods and constructors: ScopedLockType() ScopedLockType(rw_mutex_type &m, bool write = true) void acquire(rw_mutex_type &m, bool write) void release() bool try_acquire(rw_mutex_type &m, bool write)
and optionally: bool upgrade_to_writer() bool downgrade_to_reader() bool is_writer (variable)
Implementing all optional methods and supplying is_writer variable can improve performance if MutexType supports efficient upgrading and downgrading operations.
Testing note: In some case, helgrind and drd might report lock ordering errors for concurrent_hash_map. This might happen when calling find, insert or erase while already holding an accessor to some element.
The typical usage example would be:
#include <iostream>
#include <vector>
const int THREADS_NUM = 30;
const bool remove_hashmap = true;
struct root {
};
int
main(int argc, char *argv[])
{
if (argc != 2)
std::cerr << "usage: " << argv[0] << " file-name" << std::endl;
auto path = argv[1];
auto r = pop.root();
if (r->pptr == nullptr) {
r->pptr = make_persistent<hashmap_type>();
});
} else {
pop.root()->pptr->runtime_initialize();
pop.root()->pptr->defragment();
}
auto &map = *pop.root()->pptr;
std::vector<std::thread> threads;
threads.reserve(static_cast<size_t>(THREADS_NUM));
for (int i = 0; i < THREADS_NUM / 3; ++i) {
threads.emplace_back([&]() {
for (int i = 0; i < 10 * THREADS_NUM; ++i) {
map.insert(hashmap_type::value_type(i, i));
}
});
}
for (int i = 0; i < THREADS_NUM / 3; ++i) {
threads.emplace_back([&]() {
for (int i = 0; i < 10 * THREADS_NUM; ++i) {
map.erase(i);
}
});
}
for (int i = 0; i < THREADS_NUM / 3; ++i) {
threads.emplace_back([&]() {
for (int i = 0; i < 10 * THREADS_NUM; ++i) {
hashmap_type::accessor acc;
bool res = map.find(acc, i);
if (res) {
assert(acc->first == i);
assert(acc->second >= i);
acc->second.get_rw() += 1;
pop.persist(acc->second);
}
}
});
}
for (auto &t : threads) {
t.join();
}
map.defragment();
map.clear();
if (remove_hashmap) {
map.free_data();
delete_persistent<hashmap_type>(pop.root()->pptr);
});
}
pop.close();
return 0;
}