ATLAS Offline Software
Loading...
Searching...
No Matches
ConcurrentHashmapImpl.h
Go to the documentation of this file.
1// This file's extension implies that it's C, but it's really -*- C++ -*-.
2/*
3 * Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration.
4 */
11
12
13
14#ifndef CXXUTILS_CONCURRENTHASHMAPIMPL_H
15#define CXXUTILS_CONCURRENTHASHMAPIMPL_H
16
17
19#include "CxxUtils/concepts.h"
20#include <functional>
21#include <cstdint>
22#include <cstdlib>
23#include <atomic>
24#include <mutex>
25#include <memory>
26#include <new>
27
28
29class ConcurrentHashmapImplTest;
30
31
32namespace CxxUtils {
33namespace detail {
34
35
40using ConcurrentHashmapVal_t = uintptr_t;
41
42
48template <class T>
49concept IsConcurrentHashmapPayload = std::is_standard_layout_v<T> &&
50 std::is_trivial_v<T> &&
51 sizeof (T) <= sizeof (ConcurrentHashmapVal_t);
52
53
58 : public std::unique_lock<std::mutex>
59{
60public:
61 using std::unique_lock<std::mutex>::unique_lock;
62};
63
64
83template <unsigned ENTRIES_PER_CACHELINE>
85{
87 static constexpr size_t ENTRIES_PER_CACHELINE_MASK = ENTRIES_PER_CACHELINE-1;
88
96 CHMTableIterator (size_t hash, size_t mask, size_t maskBits, size_t probeLimit);
97
98
102 size_t offset() const;
103
104
108 size_t nprobes() const;
109
110
116 bool next();
117
118
119private:
122 const size_t m_mask;
124 const size_t m_boffs;
126 const size_t m_stride;
128 const size_t m_probeLimit;
130 size_t m_bucket;
132 size_t m_nprobes;
133};
134
135
194template <template <class> class UPDATER_,
195 typename HASHER_ = std::hash<uintptr_t>,
196 typename MATCHER_ = std::equal_to<uintptr_t>,
197 uintptr_t NULLVAL_ = 0,
198 uintptr_t TOMBSTONE_ = NULLVAL_>
200{
201public:
205 using Hasher_t = HASHER_;
207 using Matcher_t = MATCHER_;
209 static constexpr uintptr_t nullval = NULLVAL_;
211 static constexpr uintptr_t tombstone = TOMBSTONE_;
213 static constexpr size_t INVALID = static_cast<size_t>(-1);
214
217
218
219private:
221 struct entry_t {
222 std::atomic<val_t> m_key;
223 std::atomic<val_t> m_val;
224 };
225
227 static constexpr size_t CACHELINE = 64;
228
229 // Ensure that entries will evenly pack a cache line.
230 // If CACHELINE is a power of two, this implies that sizeof(entry_t)
231 // is as well.
232 static_assert (CACHELINE >= sizeof (entry_t) &&
233 CACHELINE % sizeof(entry_t) == 0);
234
236 static constexpr size_t ENTRIES_PER_CACHELINE = CACHELINE / sizeof(entry_t);
239
241 friend class ::ConcurrentHashmapImplTest;
242
243
253 class alignas(CACHELINE) Table
254 {
255 public:
257
258
266 const Hasher_t& hasher = Hasher_t(),
267 const Matcher_t& matcher = Matcher_t());
268
269
277 static void* operator new (size_t, size_t capacity);
278
279
283 void operator delete (void* p);
284
285
293 size_t probeRead (val_t key, size_t hash) const;
294
295
307 size_t probeWrite (val_t key, size_t hash, bool& insert);
308
309
313 size_t capacity() const;
314
315
320 const entry_t& entry (size_t offset) const;
321
322
327 entry_t& entry (size_t offset);
328
329
330 private:
332 const size_t m_capacity;
334 const size_t m_maxProbe;
336 const size_t m_mask;
338 const size_t m_maskBits;
344 std::atomic<size_t> m_longestProbe;
347 };
348
349
350public:
352 using Updater_t = UPDATER_<Table>;
354 using Context_t = typename Updater_t::Context_t;
355
356
357
368 size_t capacity_in,
369 const Hasher_t& hasher,
370 const Matcher_t& matcher,
371 const typename Updater_t::Context_t& ctx);
372
373
374 // Don't implement copying.
375 // This should be done by derived classes, if desired.
377 ConcurrentHashmapImpl& operator= (const ConcurrentHashmapImpl&) = delete;
378
379
384 size_t size() const;
385
386
390 size_t capacity() const;
391
392
396 size_t erased() const;
397
398
402 const Hasher_t& hasher() const;
403
404
408 const Matcher_t& matcher() const;
409
410
418 {
419 public:
426 const_iterator (const Table& table, bool end);
427
428
435 const_iterator (const Table& table, size_t offset);
436
437
441 void next();
442
443
447 void prev();
448
449
455 val_t key() const;
456
457
461 val_t value() const;
462
463
467 bool operator!= (const const_iterator& other) const;
468
469
473 bool valid() const;
474
475
476 private:
481 size_t m_offset;
482 };
483
484
494
495
509 std::pair<const_iterator, bool>
510 put (val_t key, size_t hash, val_t val,
511 bool overwrite,
512 const typename Updater_t::Context_t& ctx);
513
514
529 std::pair<const_iterator, bool>
530 put (const Lock_t& lock,
531 val_t key, size_t hash, val_t val,
532 bool overwrite,
533 const typename Updater_t::Context_t& ctx);
534
535
543 const_iterator get (val_t key, size_t hash) const;
544
545
561 bool erase (val_t key, size_t hash);
562
563
580 bool erase (const Lock_t& lock, val_t key, size_t hash);
581
582
584 using const_iterator_range = std::pair<const_iterator, const_iterator>;
585
586
591
592
597
598
603
604
613 const typename Updater_t::Context_t& ctx);
614
615
622 const_iterator clear (const typename Updater_t::Context_t& ctx);
623
624
633
634
643 void reserve (size_t capacity,
644 const typename Updater_t::Context_t& ctx);
645
646
652 void quiescent (const typename Updater_t::Context_t& ctx);
653
654
667
668
673
674
675private:
682 bool grow (const Lock_t& lock, const typename Updater_t::Context_t& ctx);
683
684
692 bool grow (const Lock_t& lock, size_t new_capacity, const typename Updater_t::Context_t& ctx);
693
694
695 static uint64_t round_up (uint64_t);
696
697
707 std::atomic<size_t> m_size;
709 std::atomic<size_t> m_erased;
711 std::mutex m_mutex;
712};
713
714
715} // namespace detail
716
717
718} // namespace CxxUtils
719
720
722
723
724#endif // not CXXUTILS_CONCURRENTHASHMAPIMPL_H
bool operator!=(const DataVector< T > &a, const DataVector< T > &b)
Based on operator==.
Atomic min/max functions.
const size_t m_mask
Mask for table indices (capacity-1).
CHMTableIterator< ENTRIES_PER_CACHELINE > TableIterator
const size_t m_maxProbe
Maximum number of probes allowed before resizing the table.
size_t probeRead(val_t key, size_t hash) const
Find a table entry for reading.
Table(size_t capacity, const Hasher_t &hasher=Hasher_t(), const Matcher_t &matcher=Matcher_t())
Constructor.
const entry_t & entry(size_t offset) const
Return the entry for an offset.
std::atomic< size_t > m_longestProbe
Longest probe needed so far.
const Matcher_t & m_matcher
The key match object.
size_t capacity() const
The number of entries in the table.
entry_t & entry(size_t offset)
Return the entry for an offset (non-const).
size_t probeWrite(val_t key, size_t hash, bool &insert)
Find a table entry for writing.
const size_t m_maskBits
Number of bits in the mask.
const size_t m_capacity
Number of entries in the table. Must be a power of 2.
entry_t m_entries[1]
The actual table entries.
Bidirectional iterator over occupied table entries.
void next()
Advance the iterator to the next occupied entry.
val_t key() const
Return the key for this iterator.
void prev()
Move the iterator back to the previous occupied entry.
const_iterator(const Table &table, bool end)
Constructor.
bool valid() const
Check that the iterator is valid (not pointing at the end).
const_iterator(const Table &table, size_t offset)
Constructor.
val_t value() const
Return the value for this iterator.
const Table & m_table
The table over which we're iterating.
Updater_t & updater()
Access the Updater instance.
Lock_t lock()
Take a lock on the container.
size_t capacity() const
Return the current table size.
size_t erased() const
The number of erased elements in the current table.
const_iterator get(val_t key, size_t hash) const
Look up an entry in the table.
ConcurrentHashmapImpl(const ConcurrentHashmapImpl &)=delete
bool erase(val_t key, size_t hash)
Erase an entry from the table.
static constexpr uintptr_t tombstone
Tombstone key value. Must be different from nullval to allow erasures.
void reserve(size_t capacity, const typename Updater_t::Context_t &ctx)
Increase the table capacity.
typename Updater_t::Context_t Context_t
Context type for the updater.
static constexpr size_t INVALID
Used to represent an invalid table index.
std::pair< const_iterator, bool > put(const Lock_t &lock, val_t key, size_t hash, val_t val, bool overwrite, const typename Updater_t::Context_t &ctx)
Add an entry to the table, with external locking.
UPDATER_< Table > Updater_t
Updater object.
HashmapLock Lock_t
Lock class used for external locking.
Updater_t m_updater
Updater object managing memory. See above.
bool grow(const Lock_t &lock, size_t new_capacity, const typename Updater_t::Context_t &ctx)
Make the table larger.
const Hasher_t & hasher() const
Return the hasher object.
static constexpr size_t ENTRIES_PER_CACHELINE_MASK
Mask for the cache line part of an index.
static constexpr size_t CACHELINE
Assumed length in bytes of one cache line.
std::atomic< size_t > m_erased
Number of entries that have been erased.
bool erase(const Lock_t &lock, val_t key, size_t hash)
Erase an entry from the table, with external locking.
static uint64_t round_up(uint64_t)
const Matcher_t & matcher() const
Return the matcher object.
std::atomic< size_t > m_size
Number of entries in the map.
const_iterator_range range() const
Return a range that can be used to iterate over the container.
std::mutex m_mutex
Mutex to serialize changes to the map.
void swap(ConcurrentHashmapImpl &other)
Swap this container with another.
const Matcher_t m_matcher
The key match object.
std::pair< const_iterator, const_iterator > const_iterator_range
Two iterators defining a range.
const_iterator clear(const typename Updater_t::Context_t &ctx)
Erase the table (don't change the capacity).
static constexpr uintptr_t nullval
Null key value.
size_t size() const
Return the number of items currently stored.
bool grow(const Lock_t &lock, const typename Updater_t::Context_t &ctx)
Make the table larger.
static constexpr size_t ENTRIES_PER_CACHELINE
Number of entries in one cache line.
const_iterator begin() const
A begin iterator for the container.
const_iterator end() const
An end iterator for the container.
ConcurrentHashmapImpl(Updater_t &&updater, size_t capacity_in, const Hasher_t &hasher, const Matcher_t &matcher, const typename Updater_t::Context_t &ctx)
Constructor.
Table * m_table
The current table instance. Must be holding the mutex to access this.
const_iterator clear(size_t capacity, const typename Updater_t::Context_t &ctx)
Erase the table and change the capacity.
std::pair< const_iterator, bool > put(val_t key, size_t hash, val_t val, bool overwrite, const typename Updater_t::Context_t &ctx)
Add an entry to the table.
ConcurrentHashmapVal_t val_t
Type used for keys and values — an unsigned big enough to hold a pointer.
void forceClear()
Erase the table by filling it with nulls.
void quiescent(const typename Updater_t::Context_t &ctx)
Called when this thread is no longer referencing anything from this container.
Helper to allow for external locking with put().
Concept for a value that can be saved in a concurrent hash map.
A couple standard-library related concepts.
uintptr_t ConcurrentHashmapVal_t
Type used for keys and values — an unsigned big enough to hold a pointer.
Helper to generate hash probes.
const size_t m_stride
Base increment between probes.
size_t offset() const
Offset of the element currently being probed.
const size_t m_boffs
Offset of the first entry we probe within its cacheline.
size_t nprobes() const
Return the number of probes performed so far.
const size_t m_mask
Mask for the table; i.e., capacity-1, but with the low bits corresponding to ENTRIES_PER_CACHELINE al...
static constexpr size_t ENTRIES_PER_CACHELINE_MASK
Mask for the within-cacheline part of indices.
CHMTableIterator(size_t hash, size_t mask, size_t maskBits, size_t probeLimit)
Constructor.
size_t m_bucket
Index of the start of the cacheline currently being probed.
size_t m_nprobes
Number of probes tried so far.
const size_t m_probeLimit
Maximum number of probes to try.
bool next()
Move to the next probe.