ATLAS Offline Software
ConcurrentHashmapImpl.icc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration.
3  */
4 /**
5  * @file CxxUtils/ConcurrentHashmapImpl.h
6  * @author scott snyder <snyder@bnl.gov>
7  * @date Dec, 2020
8  * @brief Hash table allowing concurrent, lockless reads.
9  */
10 
11 
12 #include <cassert>
13 
14 
15 namespace CxxUtils {
16 namespace detail {
17 
18 
19 /**
20  * @brief Constructor.
21  * @param hash The hash of the entry we're looking for.
22  * @param mask Table mask; i.e., the table capacity - 1.
23  * @param maskBits Number of 1 bits in mask.
24  * @param probeLimit Maximum number of probes to try before failing.
25  */
26 template <unsigned ENTRIES_PER_CACHELINE>
27 inline
28 CHMTableIterator<ENTRIES_PER_CACHELINE>::CHMTableIterator
29  (size_t hash, size_t mask, size_t maskBits, size_t probeLimit)
30  // Mask limited to the table. Also mask off the part within a cache line.
31  : m_mask ((mask & ~ENTRIES_PER_CACHELINE_MASK)),
32  // Offset of hash within a cache line.
33  m_boffs (hash & ENTRIES_PER_CACHELINE_MASK),
34  // Starting increment between hash probes.
35  // Must be large enough to go to a new cache line, which is why
36  // we or in ENTRIES_PER_CACHELINE.
37  m_stride (((hash >> maskBits) | hash | ENTRIES_PER_CACHELINE) & ENTRIES_PER_CACHELINE_MASK),
38  m_probeLimit (probeLimit),
39  // Index at the start of the cache line containing hash.
40  m_bucket (hash & m_mask),
41  m_nprobes (0)
42 {
43 }
44 
45 
46 /**
47  * @brief Offset of the element currently being probed.
48  */
49 template <unsigned ENTRIES_PER_CACHELINE>
50 inline
51 size_t CHMTableIterator<ENTRIES_PER_CACHELINE>::offset() const
52 {
53  // m_bucket the starting index of the current cache line.
54  // Count within the cache line in a circular manner starting
55  // at m_boffs.
56  return m_bucket + ((m_nprobes + m_boffs)&ENTRIES_PER_CACHELINE_MASK);
57 }
58 
59 
60 /**
61  * @brief Return the number of probes performed so far.
62  */
63 template <unsigned ENTRIES_PER_CACHELINE>
64 inline
65 size_t CHMTableIterator<ENTRIES_PER_CACHELINE>::nprobes() const
66 {
67  return m_nprobes;
68 }
69 
70 
71 /**
72  * @brief Move to the next probe.
73  * Returns true if we should continue, or false if we've hit the maximum
74  * number of probes.
75  */
76 template <unsigned ENTRIES_PER_CACHELINE>
77 inline
78 bool CHMTableIterator<ENTRIES_PER_CACHELINE>::next()
79 {
80  // Increment number of probes and stop if we've hit the maximum.
81  if (++m_nprobes >= m_probeLimit) {
82  return false;
83  }
84  // We've finished a cache line if the low bits are back to 0.
85  if ((m_nprobes & ENTRIES_PER_CACHELINE_MASK) == 0) {
86  // Move to a different cacheline.
87  // cf. knuth AOCP exercise 6.4.20.
88  // By construction, the low bits (within the cacheline) of
89  // m_bucket, m_nprobes, and m_stride should all be 0.
90  m_bucket = (m_bucket + m_nprobes + m_stride) & m_mask;
91  }
92  return true;
93 }
94 
95 
96 //*****************************************************************************
97 
98 
99 #define T_CHMIMPL \
100  template <template <class> class UPDATER_, \
101  typename HASHER_, \
102  typename MATCHER_, \
103  uintptr_t NULLVAL_, \
104  uintptr_t TOMBSTONE_>
105 
106 #define CHMIMPL ConcurrentHashmapImpl<UPDATER_, HASHER_, MATCHER_, NULLVAL_, TOMBSTONE_>
107 
108 
109 /**
110  * @brief Constructor.
111  * @param capacity Number of entries in the table. Must be a power of 2.
112  * @param hasher Hash object to use.
113  * @param matcher Key match object to use.
114  */
115 T_CHMIMPL
116 CHMIMPL::Table::Table (size_t capacity,
117  const Hasher_t& hasher /*= Hasher_t()*/,
118  const Matcher_t& matcher /*= Matcher_t()*/)
119  : m_capacity (capacity),
120  m_maxProbe (capacity / 4),
121  m_mask (capacity-1),
122  m_maskBits (count_trailing_zeros (capacity)),
123  m_hasher (hasher),
124  m_matcher (matcher),
125  m_longestProbe (0)
126 {
127  // Clear all the keys.
128  for (size_t i = 0; i < capacity; i++) {
129  m_entries[i].m_key = nullval;
130  }
131 }
132 
133 
134 /**
135  * @brief Allocator for table objects.
136  * @param capacity Size of the table (must be a power of 2).
137  *
138  * Allocate with enough space for the table of entries.
139  * Also align on a cache line.
140  */
141 T_CHMIMPL
142 void* CHMIMPL::Table::operator new (size_t, size_t capacity)
143 {
144  void* memptr = nullptr;
145  // Allocate aligned memory block.
146  // The Table structure includes one entry at the end,
147  // so subtract 1 from capacity.
148  posix_memalign (&memptr, CACHELINE, sizeof(Table) + (capacity-1)*sizeof(entry_t));
149  if (!memptr) std::abort();
150  return memptr;
151 }
152 
153 
154 /**
155  * @brief Deallocator for table objects.
156  */
157 T_CHMIMPL
158 void CHMIMPL::Table::operator delete (void* p)
159 {
160  free (p);
161 }
162 
163 
164 /**
165  * @brief Find a table entry for reading.
166  * @param key The key for which to search.
167  * @param hash The hash of the key.
168  *
169  * Returns the matching entry, or nullptr.
170  */
171 T_CHMIMPL
172 size_t CHMIMPL::Table::probeRead (val_t key, size_t hash) const
173 {
174  // Iterate over table probes.
175  // We don't need to check more than the longest probe sequence
176  // used so far.
177  TableIterator it (hash, m_mask, m_maskBits, m_longestProbe);
178  do {
179  size_t offset = it.offset();
180  const entry_t* ent = m_entries + offset;
181  if (ent->m_key == nullval) {
182  // If we hit an empty key, report failure.
183  return INVALID;
184  }
185  if (m_matcher (ent->m_key, key)) {
186  // Found a matching key.
187  return offset;
188  }
189  } while (it.next());
190  // Too many probes --- return failure.
191  return INVALID;
192 }
193 
194 
195 /**
196  * @brief Find a table entry for writing.
197  * @param key The key for which to search.
198  * @param hash The hash of the key.
199  * @param entry[out] The entry found.
200  *
201  * If we find the entry, return true with @c entry pointing at it.
202  * If we don't find it, and there's still room in the table, return false
203  * with @c entry pointing at the next empty entry.
204  * Otherwise, return false with @c entry set to nullptr.
205  */
206 T_CHMIMPL
207 size_t CHMIMPL::Table::probeWrite (val_t key, size_t hash, bool& insert)
208 {
209  // Iterate over table probes.
210  TableIterator it (hash, m_mask, m_maskBits, m_maxProbe);
211  do {
212  size_t offset = it.offset();
213  entry_t* ent = m_entries + offset;
214  if (ent->m_key == nullval) {
215  // We hit an empty key; a new entry could be added here.
216  // Update the longest probe count.
217  CxxUtils::atomic_fetch_max (&m_longestProbe, it.nprobes()+1);
218  insert = true;
219  return offset;
220  }
221  if (m_matcher (ent->m_key, key)) {
222  // Found a matching key.
223  insert = false;
224  return offset;
225  }
226  } while (it.next());
227  // Too many probes --- return failure.
228  return INVALID;
229 }
230 
231 
232 /**
233  * @brief The number of entries in the table.
234  */
235 T_CHMIMPL
236 inline
237 size_t CHMIMPL::Table::capacity() const
238 {
239  return m_capacity;
240 }
241 
242 
243 /**
244  * @brief Return the entry for an offset.
245  * @param offset The index of the desired entry.
246  */
247 T_CHMIMPL
248 inline
249 const typename CHMIMPL::entry_t& CHMIMPL::Table::entry (size_t offset) const
250 {
251  return m_entries[offset];
252 }
253 
254 
255 /**
256  * @brief Return the entry for an offset (non-const).
257  * @param offset The index of the desired entry.
258  */
259 T_CHMIMPL
260 inline
261 typename CHMIMPL::entry_t& CHMIMPL::Table::entry (size_t offset)
262 {
263  return m_entries[offset];
264 }
265 
266 
267 //*****************************************************************************
268 
269 
270 /**
271  * @brief Constructor.
272  * @param updater Object used to manage memory
273  * (see comments at the start of the class).
274  * @param capacity Minimum initial table size.
275  * @param hasher Hash object to use.
276  * @param matcher Key match object to use.
277  * @param ctx Execution context.
278  */
279 T_CHMIMPL
280 CHMIMPL::ConcurrentHashmapImpl (Updater_t&& updater,
281  size_t capacity_in,
282  const Hasher_t& hasher,
283  const Matcher_t& matcher,
284  const typename Updater_t::Context_t& ctx)
285  : m_updater (std::move (updater)),
286  m_hasher (hasher),
287  m_matcher (matcher),
288  m_size (0),
289  m_erased (0)
290 {
291  // Round up capacity to a power of 2.
292  size_t capacity = round_up (capacity_in);
293 
294  // cppcheck-suppress noDestructor // false positive
295  m_table = new (capacity) Table (capacity, hasher, matcher);
296  m_updater.update (std::unique_ptr<Table> (m_table), ctx);
297 }
298 
299 
300 /**
301  * @brief Take a lock on the container.
302  *
303  * Take a lock on the container.
304  * The lock can then be passed to put(), allowing to factor out the locking
305  * when put() gets used in a loop. The lock will be released when the
306  * lock object is destroyed.
307  */
308 T_CHMIMPL
309 inline
310 typename CHMIMPL::Lock_t CHMIMPL::lock()
311 {
312  return Lock_t (m_mutex);
313 }
314 
315 
316 /**
317  * @brief Add an entry to the table, with external locking.
318  * @param lock The lock object returned from lock().
319  * @param key The key to insert.
320  * @param hash The hash of the key.
321  * @param val The value to insert.
322  * @param overwrite If true, then overwrite an existing entry.
323  * If false, an existing entry will not be changed.
324  * @param ctx Execution context.
325  *
326  * If the key already exists, then its value will be updated.
327  * Returns an iterator pointing at the entry and a flag which is
328  * true if a new element was added.
329  */
330 T_CHMIMPL
331 std::pair<typename CHMIMPL::const_iterator, bool>
332 CHMIMPL::put (const Lock_t& lock,
333  val_t key, size_t hash, val_t val, bool overwrite,
334  const typename Updater_t::Context_t& ctx)
335 {
336  assert (key != nullval && key != tombstone);
337 
338  do {
339  bool insert;
340  size_t offset = m_table->probeWrite (key, hash, insert);
341  if (offset != INVALID) {
342  entry_t& ent = m_table->entry (offset);
343  if (insert) {
344  // Found a place to put it.
345  // Be sure not to set the key until the value is in place.
346  ent.m_val = val;
347  ent.m_key = key;
348  ++m_size;
349  }
350  else {
351  // Found --- update the entry if wanted.
352  if (overwrite) {
353  if (val != ent.m_val) {
354  ent.m_val = val;
355  }
356  }
357  }
358  return std::make_pair (const_iterator (*m_table, offset), insert);
359  }
360 
361  // Need to grow the table.
362  } while (grow (lock, ctx));
363 
364  // grow() failed.
365  return std::make_pair (end(), false);
366 }
367 
368 
369 /**
370  * @brief Add an entry to the table.
371  * @param key The key to insert.
372  * @param hash The hash of the key.
373  * @param val The value to insert.
374  * @param overwrite If true, then overwrite an existing entry.
375  * If false, an existing entry will not be changed.
376  * @param ctx Execution context.
377  *
378  * If the key already exists, then its value will be updated.
379  * Returns an iterator pointing at the entry and a flag which is
380  * true if a new element was added.
381  */
382 T_CHMIMPL
383 inline
384 std::pair<typename CHMIMPL::const_iterator, bool>
385 CHMIMPL::put (val_t key, size_t hash, val_t val, bool overwrite,
386  const typename Updater_t::Context_t& ctx)
387 {
388  return put (lock(), key, hash, val, overwrite, ctx);
389 }
390 
391 
392 /**
393  * @brief Look up an entry in the table.
394  * @param key The key to find.
395  * @param hash The hash of the key.
396  *
397  * Returns an iterator pointing at the found entry, or end().
398  */
399 T_CHMIMPL
400 typename CHMIMPL::const_iterator CHMIMPL::get (val_t key, size_t hash) const
401 {
402  const Table& table = m_updater.get();
403  size_t offset = table.probeRead (key, hash);
404  // Offset will be -1 if not found --- invalid iterator.
405  return const_iterator (table, offset);
406 }
407 
408 
409 /**
410  * @brief Erase an entry from the table, with external locking.
411  * @param lock The lock object returned from lock().
412  * @param key The key to find.
413  * @param hash The hash of the key.
414  *
415  * Mark the corresponding entry as deleted.
416  * Return true on success, false on failure (key not found).
417  *
418  * The tombstone value must be different from the null value.
419  *
420  * Take care if the key or value types require memory allocation.
421  *
422  * This may cause the key type returned by an iterator to change
423  * asynchronously to the tombstone value.
424  **/
425 T_CHMIMPL
426 bool
427 CHMIMPL::erase (const Lock_t& /*lock*/, val_t key, size_t hash)
428 {
429  static_assert (nullval != tombstone);
430  const Table& table = m_updater.get();
431  size_t offset = table.probeRead (key, hash);
432  if (offset != INVALID) {
433  entry_t& ent = m_table->entry (offset);
434  ++m_erased;
435  --m_size;
436  ent.m_key = tombstone;
437  return true;
438  }
439  return false;
440 }
441 
442 
443 /**
444  * @brief Erase an entry from the table.
445  * @param key The key to find.
446  * @param hash The hash of the key.
447  *
448  * Mark the corresponding entry as deleted.
449  * Return true on success, false on failure (key not found).
450  *
451  * The tombstone value must be different from the null value.
452  *
453  * Take care if the key or value types require memory allocation.
454  *
455  * This may cause the key type returned by an iterator to change
456  * asynchronously to the tombstone value.
457  **/
458 T_CHMIMPL
459 inline
460 bool
461 CHMIMPL::erase (val_t key, size_t hash)
462 {
463  return erase (lock(), key, hash);
464 }
465 
466 
467 /**
468  * @brief Return the number of items currently stored.
469  */
470 T_CHMIMPL
471 size_t CHMIMPL::size() const
472 {
473  return m_size;
474 }
475 
476 
477 /**
478  * @brief Return the current table size.
479  */
480 T_CHMIMPL
481 size_t CHMIMPL::capacity() const
482 {
483  const Table& table = m_updater.get();
484  return table.capacity();
485 }
486 
487 
488 /**
489  * @brief The number of erased elements in the current table.
490  */
491 T_CHMIMPL
492 size_t CHMIMPL::erased() const
493 {
494  return m_erased;
495 }
496 
497 
498 /**
499  * @brief Return the hasher object.
500  */
501 T_CHMIMPL
502 inline
503 const typename CHMIMPL::Hasher_t& CHMIMPL::hasher() const
504 {
505  return m_hasher;
506 }
507 
508 
509 /**
510  * @brief Return the matcher object.
511  */
512 T_CHMIMPL
513 inline
514 const typename CHMIMPL::Matcher_t& CHMIMPL::matcher() const
515 {
516  return m_matcher;
517 }
518 
519 
520 /**
521  * @brief Constructor.
522  * @param table The table instance we're referencing.
523  * @param end If true, initialize this to an end iterator.
524  * Otherwise, initialize it to a a begin iterator.
525  */
526 T_CHMIMPL
527 inline
528 CHMIMPL::const_iterator::const_iterator (const Table& table, bool end)
529  : m_table (table),
530  m_offset (INVALID)
531 {
532  // For an end iterator, we want offset to be -1.
533  // For a begin iterator, we need to advance to the first non-null entry.
534  if (!end) {
535  next();
536  }
537 }
538 
539 
540 /**
541  * @brief Constructor.
542  * @param table The table instance we're referencing.
543  * @param offset Offset of the iterator within the table.
544  * (Must point at an occupied entry.)
545  */
546 T_CHMIMPL
547 CHMIMPL::const_iterator::const_iterator (const Table& table, size_t offset)
548  : m_table (table),
549  m_offset (offset)
550 {
551  assert (offset == INVALID || m_table.entry (offset).m_key != nullval);
552 }
553 
554 
555 /**
556  * @brief Advance the iterator to the next occupied entry.
557  */
558 T_CHMIMPL
559 inline
560 void CHMIMPL::const_iterator::next()
561 {
562  val_t key;
563  do {
564  ++m_offset;
565  if (m_offset >= m_table.capacity()) {
566  m_offset = INVALID;
567  break;
568  }
569  key = m_table.entry (m_offset).m_key;
570  } while (key == nullval || key == tombstone);
571 }
572 
573 
574 /**
575  * @brief Move the iterator back to the previous occupied entry.
576  */
577 T_CHMIMPL
578 inline
579 void CHMIMPL::const_iterator::prev()
580 {
581  if (m_offset == INVALID) {
582  m_offset = m_table.capacity();
583  }
584  val_t key;
585  do {
586  --m_offset;
587  if (m_offset >= m_table.capacity()) {
588  m_offset = INVALID;
589  break;
590  }
591  key = m_table.entry (m_offset).m_key;
592  } while (key == nullval || key == tombstone);
593 }
594 
595 
596 /**
597  * @brief Return the key for this iterator.
598  * If deletions are allowed, then the key may change asynchronously
599  * to the tombstone value.
600  */
601 T_CHMIMPL
602 inline
603 typename CHMIMPL::val_t CHMIMPL::const_iterator::key() const
604 {
605  return m_table.entry (m_offset).m_key;
606 }
607 
608 
609 /**
610  * @brief Return the value for this iterator.
611  */
612 T_CHMIMPL
613 inline
614 typename CHMIMPL::val_t CHMIMPL::const_iterator::value() const
615 {
616  return m_table.entry (m_offset).m_val;
617 }
618 
619 
620 /**
621  * @brief Compare two iterators.
622  */
623 T_CHMIMPL
624 inline
625 bool CHMIMPL::const_iterator::operator!= (const const_iterator& other) const
626 {
627  if (m_offset != other.m_offset) return true;
628  if (m_offset == INVALID) return false;
629  return &m_table != &other.m_table;
630 }
631 
632 
633 /**
634  * @brief Check that the iterator is valid (not pointing at the end).
635  */
636 T_CHMIMPL
637 inline
638 bool CHMIMPL::const_iterator::valid() const
639 {
640  return m_offset != INVALID;
641 }
642 
643 
644 /**
645  * @brief Return a range that can be used to iterate over the container.
646  */
647 T_CHMIMPL
648 inline
649 typename CHMIMPL::const_iterator_range CHMIMPL::range() const
650 {
651  const Table& table = m_updater.get();
652  return const_iterator_range (const_iterator (table, false),
653  const_iterator (table, true));
654 }
655 
656 
657 /**
658  * @brief A begin iterator for the container.
659  */
660 T_CHMIMPL
661 inline
662 typename CHMIMPL::const_iterator CHMIMPL::begin() const
663 {
664  const Table& table = m_updater.get();
665  return const_iterator (table, false);
666 }
667 
668 
669 /**
670  * @brief An end iterator for the container.
671  */
672 T_CHMIMPL
673 inline
674 typename CHMIMPL::const_iterator CHMIMPL::end() const
675 {
676  const Table& table = m_updater.get();
677  return const_iterator (table, true);
678 }
679 
680 
681 /**
682  * @brief Erase the table and change the capacity.
683  * @param capacity The new table capacity.
684  * @param ctx Execution context.
685  *
686  * Returns an iterator pointing at the start of the old table.
687  */
688 T_CHMIMPL
689 typename CHMIMPL::const_iterator
690 CHMIMPL::clear (size_t capacity,
691  const typename Updater_t::Context_t& ctx)
692 {
693  capacity = round_up (capacity);
694  Lock_t lock (m_mutex);
695  std::unique_ptr<Table> new_table (new (capacity) Table (capacity,
696  m_hasher,
697  m_matcher));
698 
699  // Save an iterator to the old table.
700  const_iterator it = begin();
701 
702  // Publish the new table.
703  m_size = 0;
704  m_erased = 0;
705  m_table = new_table.get();
706  m_updater.update (std::move (new_table), ctx);
707  return it;
708 }
709 
710 
711 /**
712  * @brief Erase the table (don't change the capacity).
713  * @param ctx Execution context.
714  *
715  * Returns an iterator pointing at the start of the old table.
716  */
717 T_CHMIMPL
718 typename CHMIMPL::const_iterator
719 CHMIMPL::clear (const typename Updater_t::Context_t& ctx)
720 {
721  const Table& table = m_updater.get();
722  // Possible race here in that the container capacity could increase
723  // before we take out the lock in clear(). In practice, this should
724  // not actually be a problem.
725  return clear (table.capacity(), ctx);
726 }
727 
728 
729 /**
730  * @brief Erase the table by filling it with nulls.
731  *
732  * This method is not safe to use concurrently --- no other threads
733  * may be accessing the container at the same time, either for read
734  * or write.
735  */
736 T_CHMIMPL
737 void CHMIMPL::forceClear()
738 {
739  Lock_t lock (m_mutex);
740  size_t cap = m_table->capacity();
741  for (size_t i = 0; i < cap; i++) {
742  m_table->entry(i).m_key.store (nullval, std::memory_order_relaxed);
743  }
744  m_size = 0;
745  m_erased = 0;
746  std::atomic_thread_fence (std::memory_order_seq_cst);
747 }
748 
749 
750 /**
751  * @brief Increase the table capacity.
752  * @param capacity The new table capacity.
753  * @param ctx Execution context.
754  *
755  * No action will be taken if @c capacity is smaller
756  * than the current capacity.
757  */
758 T_CHMIMPL
759 void CHMIMPL::reserve (size_t capacity,
760  const typename Updater_t::Context_t& ctx)
761 {
762  Lock_t lock (m_mutex);
763  if (capacity < m_table->capacity()) return;
764  grow (lock, round_up (capacity), ctx);
765 }
766 
767 
768 /**
769  * @brief Called when this thread is no longer referencing anything
770  * from this container.
771  * @param ctx Execution context.
772  */
773 T_CHMIMPL
774 void CHMIMPL::quiescent (const typename Updater_t::Context_t& ctx)
775 {
776  m_updater.quiescent (ctx);
777 }
778 
779 
780 /**
781  * @brief Swap this container with another.
782  * @param other The container with which to swap.
783  *
784  * This will also call swap on the Updater object; hence, the Updater
785  * object must also support swap. The Hasher and Matcher instances
786  * are NOT swapped.
787  *
788  * This operation is NOT thread-safe. No other threads may be accessing
789  * either container during this operation.
790  */
791 T_CHMIMPL
792 void CHMIMPL::swap (ConcurrentHashmapImpl& other)
793 {
794  // Shouldn't be needed since we specified that no other threads can be
795  // accessing this...
796  Lock_t lock (m_mutex);
797  Lock_t lock_other (other.m_mutex);
798 
799  m_updater.swap (other.m_updater);
800  std::swap (m_table, other.m_table);
801 
802  auto swap_atomic = [] (std::atomic<size_t>& a, std::atomic<size_t>& b)
803  {
804  size_t tmp = a.load (std::memory_order_relaxed);
805  a.store (b.load (std::memory_order_relaxed),
806  std::memory_order_relaxed);
807  b.store (tmp, std::memory_order_relaxed);
808  };
809 
810  swap_atomic (m_size, other.m_size);
811  swap_atomic (m_erased, other.m_erased);
812 }
813 
814 
815 /**
816  * @brief Access the Updater instance.
817  */
818 T_CHMIMPL
819 inline
820 typename CHMIMPL::Updater_t& CHMIMPL::updater()
821 {
822  return m_updater;
823 }
824 
825 
826 /**
827  * @brief Make the table larger.
828  * @param ctx Execution context.
829  *
830  * Must be holding a lock on the mutex to call this.
831  */
832 T_CHMIMPL
833 bool CHMIMPL::grow (const Lock_t& lock, const typename Updater_t::Context_t& ctx)
834 {
835  // Allocate a new table with twice the capacity, unless there
836  // have been many erasures.
837  size_t new_capacity = m_erased >= m_table->capacity()/2 ?
838  m_table->capacity() : 2*m_table->capacity();
839  return grow (lock, new_capacity, ctx);
840 }
841 
842 
843 /**
844  * @brief Make the table larger.
845  * @param new_capacity The new table capacity (must be a power of 2).
846  * @param ctx Execution context.
847  *
848  * Must be holding a lock on the mutex to call this.
849  */
850 T_CHMIMPL
851 bool CHMIMPL::grow (const Lock_t& /*lock*/,
852  size_t new_capacity,
853  const typename Updater_t::Context_t& ctx)
854 {
855  // The current table.
856  const Table& table = *m_table;
857 
858  std::unique_ptr<Table> new_table (new (new_capacity) Table (new_capacity,
859  m_hasher,
860  m_matcher));
861 
862  // Copy data from the existing table to the new one.
863  size_t capacity = table.capacity();
864  for (size_t i = 0; i < capacity; i++) {
865  const entry_t& ent = table.entry(i);
866  if (ent.m_key != nullval) {
867  size_t hash = m_hasher (ent.m_key);
868  bool insert;
869  size_t offset = new_table->probeWrite (ent.m_key, hash, insert);
870  if (offset == INVALID) {
871  std::abort();
872  }
873  entry_t& new_entry = new_table->entry (offset);
874  new_entry.m_key = static_cast<val_t> (ent.m_key);
875  new_entry.m_val = static_cast<val_t> (ent.m_val);
876  }
877  }
878 
879  m_erased = 0;
880 
881  // Publish the new table.
882  m_table = new_table.get();
883  m_updater.update (std::move (new_table), ctx);
884 
885  return true;
886 }
887 
888 
889 /**
890  * @brief Round up to a power of 2.
891  * https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
892  */
893 T_CHMIMPL
894 uint64_t CHMIMPL::round_up (uint64_t x)
895 {
896  if (x <= 64) return 64;
897  --x;
898  x |= (x>>1);
899  x |= (x>>2);
900  x |= (x>>4);
901  x |= (x>>8);
902  x |= (x>>16);
903  x |= (x>>32);
904  ++x;
905  return x;
906 }
907 
908 
909 #undef CHMIMPL
910 #undef T_CHMIMPL
911 
912 
913 
914 } // namespace detail
915 } // namespace CxxUtils