Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
tpl_concurrent_hash_map.H
Go to the documentation of this file.
1
2/*
3 Aleph_w
4
5 Data structures & Algorithms
6 https://github.com/lrleon/Aleph-w
7
8 This file is part of Aleph-w library
9
10 Copyright (c) 2002-2026 Leandro Rabindranath Leon
11
12 Permission is hereby granted, free of charge, to any person obtaining a copy
13 of this software and associated documentation files (the "Software"), to deal
14 in the Software without restriction, including without limitation the rights
15 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 copies of the Software, and to permit persons to whom the Software is
17 furnished to do so, subject to the following conditions:
18
19 The above copyright notice and this permission notice shall be included in all
20 copies or substantial portions of the Software.
21
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 SOFTWARE.
29*/
30
117#ifndef TPL_CONCURRENT_HASH_MAP_H
118#define TPL_CONCURRENT_HASH_MAP_H
119
120#include <array>
121#include <memory>
122#include <mutex>
123#include <optional>
124#include <shared_mutex>
125#include <type_traits>
126#include <utility>
127
128#include <ah-errors.H>
129#include <tpl_dynSetHash.H>
130
131namespace Aleph
132{
133
150template <typename Key, typename T,
152 size_t Shards = 64>
154{
155 static_assert(Shards > 0, "ConcurrentHashMap requires at least one shard");
156
158
159public:
162
163private:
164 // Internal structure representing a single partition of the hash map.
165 // Each Shard is protected by its own shared_mutex. alignas(64) pads each
166 // Shard to its own cache line: without it, two Shard objects placed
167 // next to each other by the allocator could share a cache line, and
168 // then threads locking/unlocking *different* shards' mutexes under
169 // write contention would still ping-pong that line between cores
170 // (false sharing) -- defeating part of the point of sharding. 64 bytes
171 // matches the cache-line size assumed elsewhere in Aleph (see
172 // concurrency_utils.H's SpscQueue::head_/tail_).
173 struct alignas(64) Shard
174 {
175 mutable std::shared_mutex mutex;
177
178 Shard(Hash_Fct_Ptr hash_fct, const Cmp &cmp)
179 : map(Primes::DefaultPrime, hash_fct, cmp)
180 {}
181 };
182
183 std::array<std::unique_ptr<Shard>, Shards> shards_;
185
198 [[nodiscard]] Shard &shard_for(const Key &key) noexcept
199 {
200 return *shards_[hash_fct_(key) % Shards];
201 }
202
204 [[nodiscard]] const Shard &shard_for(const Key &key) const noexcept
205 {
206 return *shards_[hash_fct_(key) % Shards];
207 }
208
209public:
223 const Cmp &cmp = Cmp())
224 : hash_fct_(hash_fct)
225 {
226 for (auto &shard : shards_)
227 shard = std::make_unique<Shard>(hash_fct, cmp);
228 }
229
240
255 bool insert(const Key &key, const T &value)
256 {
257 Shard &shard = shard_for(key);
258 std::unique_lock lock(shard.mutex);
259 return shard.map.insert(key, value) != nullptr;
260 }
261
282 bool insert(const Key &key, T &&value)
283 {
284 Shard &shard = shard_for(key);
285 std::unique_lock lock(shard.mutex);
286 return shard.map.insert(key, std::move(value)) != nullptr;
287 }
288
306 void insert_or_assign(const Key &key, T value)
307 {
308 Shard &shard = shard_for(key);
309 std::unique_lock lock(shard.mutex);
310
311 // We do a double lookup here (search + insert) because DynMapHashTable
312 // requires checking for existence first to avoid duplicating or throwing.
313 auto *pair = shard.map.search(key);
314 if (pair != nullptr)
315 pair->second = std::move(value);
316 else
317 shard.map.insert(key, std::move(value));
318 }
319
331 bool erase(const Key &key)
332 {
333 Shard &shard = shard_for(key);
334 std::unique_lock lock(shard.mutex);
335
336 // remove_by_data() removes the already-located entry directly (via
337 // pointer arithmetic back to its owning bucket), unlike
338 // DynMapHashTable::remove(key), which re-searches internally and
339 // move-constructs the removed value only to have it discarded here --
340 // paying an extra lookup and a throwing-move risk for nothing.
341 auto *pair = shard.map.search(key);
342 if (pair == nullptr)
343 return false;
344
345 shard.map.remove_by_data(pair->second);
346 return true;
347 }
348
360 [[nodiscard]] bool contains(const Key &key) const
361 {
362 const Shard &shard = shard_for(key);
363 std::shared_lock lock(shard.mutex);
364 return shard.map.contains(key);
365 }
366
378 [[nodiscard]] std::optional<T> find_copy(const Key &key) const
379 requires std::is_copy_constructible_v<T>
380 {
381 const Shard &shard = shard_for(key);
382 std::shared_lock lock(shard.mutex);
383 const auto *pair = shard.map.search(key);
384 if (pair == nullptr)
385 return std::nullopt;
386 return pair->second;
387 }
388
408 template <typename F>
409 bool with_value(const Key &key, F &&f) const
410 {
411 using Result = std::invoke_result_t<F, const T &>;
412 static_assert(not std::is_reference_v<Result>,
413 "ConcurrentHashMap::with_value callback must not return a reference");
414 const Shard &shard = shard_for(key);
415 std::shared_lock lock(shard.mutex);
416 const auto *pair = shard.map.search(key);
417 if (pair == nullptr)
418 return false;
419 std::invoke(std::forward<F>(f), std::as_const(pair->second));
420 return true;
421 }
422
441 template <typename F>
442 bool with_value_mut(const Key &key, F &&f)
443 {
444 using Result = std::invoke_result_t<F, T &>;
445 static_assert(not std::is_reference_v<Result>,
446 "ConcurrentHashMap::with_value_mut callback must not return a reference");
447 Shard &shard = shard_for(key);
448 std::unique_lock lock(shard.mutex);
449 auto *pair = shard.map.search(key);
450 if (pair == nullptr)
451 return false;
452 std::invoke(std::forward<F>(f), pair->second);
453 return true;
454 }
455
467 void clear()
468 {
469 for (auto &shard : shards_)
470 {
471 std::unique_lock lock(shard->mutex);
472 shard->map.clear();
473 }
474 }
475
486 [[nodiscard]] size_t size() const
487 {
488 size_t total = 0;
489 for (const auto &shard : shards_)
490 {
491 std::shared_lock lock(shard->mutex);
492 total += shard->map.size();
493 }
494 return total;
495 }
496
505 [[nodiscard]] bool is_empty() const
506 {
507 for (const auto &shard : shards_)
508 {
509 std::shared_lock lock(shard->mutex);
510 if (not shard->map.is_empty())
511 return false;
512 }
513 return true;
514 }
515
535 {
537 size_t reserved = 0;
538 for (const auto &shard : shards_)
539 {
540 std::shared_lock lock(shard->mutex);
541 // Grow the reservation by this (already-locked) shard's own O(1)
542 // size *before* copying its entries in, so appends below never
543 // trigger a reallocation -- without a separate extra pass over
544 // every shard (and its lock) just to compute a grand total first.
545 reserved += shard->map.size();
546 result.reserve(reserved);
547 for (const auto &entry : shard->map)
548 result.append(entry);
549 }
550 return result;
551 }
552};
553
554} // namespace Aleph
555
556#endif // TPL_CONCURRENT_HASH_MAP_H
Exception handling system with formatted messages for Aleph-w.
size_t size_t int32_t value
Definition ca-c-api.h:116
Simple dynamic array with automatic resizing and functional operations.
Definition tpl_array.H:138
void reserve(size_t cap)
Reserves cap cells into the array.
Definition tpl_array.H:320
Sharded concurrent hash map: Shards independently-locked DynMapHashTable partitions,...
std::array< std::unique_ptr< Shard >, Shards > shards_
Shard & shard_for(const Key &key) noexcept
Resolve which shard owns key.
bool insert(const Key &key, T &&value)
Insert key with value moved in, only if key is absent.
bool contains(const Key &key) const
Check whether key is present.
void insert_or_assign(const Key &key, T value)
Insert key with value, or overwrite the existing value if key is already present.
bool is_empty() const
Check whether every shard is currently empty.
typename Map::Hash_Fct_Ptr Hash_Fct_Ptr
Hash function pointer type, matching DynMapHashTable's own.
bool with_value_mut(const Key &key, F &&f)
Invoke f with a mutable reference to key's value, while the owning shard is write-locked.
const Shard & shard_for(const Key &key) const noexcept
const overload of shard_for(const Key&).
size_t size() const
Return the total number of entries across all shards.
bool with_value(const Key &key, F &&f) const
Invoke f with a const reference to key's value, while the owning shard is read-locked.
ConcurrentHashMap & operator=(const ConcurrentHashMap &)=delete
Deleted copy assignment operator.
ConcurrentHashMap(const ConcurrentHashMap &)=delete
Deleted copy constructor: shards own non-copyable std::shared_mutex instances.
std::optional< T > find_copy(const Key &key) const
Look up key and return a copy of its value, if present.
ConcurrentHashMap & operator=(ConcurrentHashMap &&)=delete
Deleted move assignment operator.
bool insert(const Key &key, const T &value)
Insert key with a copy of value, only if key is absent.
void clear()
Remove every entry from every shard.
ConcurrentHashMap(Hash_Fct_Ptr hash_fct=dft_hash_ptr_fct< Key >, const Cmp &cmp=Cmp())
Construct an empty map with Shards independently-locked partitions.
bool erase(const Key &key)
Remove key if present.
ConcurrentHashMap(ConcurrentHashMap &&)=delete
Deleted move constructor: no defined ordering exists to safely transfer locked shards out from under ...
int cmp(const __gmp_expr< T, U > &expr1, const __gmp_expr< V, W > &expr2)
Definition gmpfrxx.h:4129
size_t blossom_maximum_cardinality_matching(const GT &g, DynDlist< typename GT::Arc * > &matching, SA sa=SA())
Alias of compute_maximum_cardinality_general_matching().
Definition Blossom.H:466
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
and
Check uniqueness with explicit hash + equality functors.
std::decay_t< typename HeadC::Item_Type > T
Definition ah-zip.H:105
std::pair< First, Second > pair
Alias to std::pair kept for backwards compatibility.
Definition ahPair.H:89
const unsigned long DefaultPrime
Default prime number used when no specific size is requested.
Definition primes.C:381
STL namespace.
Shard(Hash_Fct_Ptr hash_fct, const Cmp &cmp)
Dynamic set implementations based on hash tables.