ATLAS Offline Software
Loading...
Searching...
No Matches
ConcurrentMap.icc
Go to the documentation of this file.
1/*
2 * Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration.
3 */
4/**
5 * @file CxxUtils/ConcurrentMap.icc
6 * @author scott snyder <snyder@bnl.gov>
7 * @date Jan, 2023
8 * @brief Hash map from integers/pointers allowing concurrent, lockless reads.
9 */
10
11
12namespace CxxUtils {
13
14
15#define T_CONCURRENTMAP template <class KEY, class VALUE, \
16 template <class> class UPDATER, \
17 class HASHER, \
18 class MATCHER, \
19 detail::ConcurrentHashmapVal_t NULLVAL, \
20 detail::ConcurrentHashmapVal_t TOMBSTONE> \
21 requires (detail::IsConcurrentHashmapPayload<KEY> && \
22 detail::IsConcurrentHashmapPayload<VALUE> && \
23 detail::IsUpdater<UPDATER> && \
24 detail::IsHash<HASHER, KEY> && \
25 detail::IsBinaryPredicate<MATCHER, KEY>)
26
27
28#define CONCURRENTMAP ConcurrentMap<KEY, VALUE, UPDATER, \
29 HASHER, MATCHER, \
30 NULLVAL, TOMBSTONE>
31
32
33/**
34 * @brief Constructor.
35 * @param updater Object used to manage memory
36 * (see comments at the start of the class).
37 * @param capacity The initial table capacity.
38 * (Will be rounded up to a power of two.)
39 * @param ctx Execution context.
40 */
41T_CONCURRENTMAP
42CONCURRENTMAP::ConcurrentMap (Updater_t&& updater,
43 size_type capacity /*= 64*/,
44 const Context_t& ctx
45 /* = Updater_t::defaultContext()*/)
46 : m_impl (std::move (updater),
47 capacity,
48 Hasher(),
49 Matcher(),
50 ctx)
51{
52}
53
54
55/**
56 * @brief Constructor from another map.
57 * @param updater Object used to manage memory
58 * (see comments at the start of the class).
59 * @param capacity The initial table capacity of the new table.
60 * (Will be rounded up to a power of two.)
61 * @param ctx Execution context.
62 *
63 * (Not really a copy constructor since we need to pass @c updater.)
64 */
65T_CONCURRENTMAP
66CONCURRENTMAP::ConcurrentMap (const ConcurrentMap& other,
67 Updater_t&& updater,
68 size_type capacity /*= 64*/,
69 const Context_t& ctx
70 /*= Updater_t::defaultContext()*/)
71 : m_impl (std::move (updater),
72 capacity,
73 Hasher(),
74 Matcher(),
75 ctx)
76{
77 // not using reference, because our iterator doesn't return a reference
78 for (const auto p : other) {
79 this->put (p.first, p.second, true, ctx);
80 }
81}
82
83
84/**
85 * @brief Constructor from a range.
86 * @param f Start iterator for the range.
87 * @param l End iterator for the range.
88 * @param updater Object used to manage memory
89 * (see comments at the start of the class).
90 * @param capacity The initial table capacity of the new table.
91 * (Will be rounded up to a power of two.)
92 * @param ctx Execution context.
93 *
94 * Constructor from a range of pairs.
95 */
96T_CONCURRENTMAP
97template <class InputIterator>
98CONCURRENTMAP::ConcurrentMap (InputIterator f,
99 InputIterator l,
100 Updater_t&& updater,
101 size_t capacity /*= 64*/,
102 const Context_t& ctx
103 /*= Updater_t::defaultContext()*/)
104 : m_impl (std::move (updater),
105 capacity,
106 Hasher(),
107 Matcher(),
108 ctx)
109{
110 Lock_t lck = lock();
111 for (; f != l; ++f) {
112 this->put (lck, f->first, f->second, true, ctx);
113 }
114}
115
116
117/**
118 * @brief Return the number of items currently in the map.
119 */
120T_CONCURRENTMAP
121inline
122auto CONCURRENTMAP::size() const -> size_type
123{
124 return m_impl.size();
125}
126
127
128/**
129 * @brief Test if the map is currently empty.
130 */
131T_CONCURRENTMAP
132inline
133bool CONCURRENTMAP::empty() const
134{
135 return !m_impl.size();
136}
137
138
139/**
140 * @brief Return the current size (capacity) of the hash table.
141 */
142T_CONCURRENTMAP
143inline
144auto CONCURRENTMAP::capacity() const -> size_t
145{
146 return m_impl.capacity();
147}
148
149
150/**
151 * @brief The number of erased elements in the current table.
152 */
153T_CONCURRENTMAP
154inline
155auto CONCURRENTMAP::erased() const -> size_t
156{
157 return m_impl.erased();
158}
159
160
161/**
162 * @brief Constructor.
163 * @param it Iterator of the underlying table.
164 */
165T_CONCURRENTMAP
166inline
167CONCURRENTMAP::const_iterator::const_iterator (typename Impl_t::const_iterator it)
168 : m_impl (it)
169{
170}
171
172
173/**
174 * @brief Test if this iterator is valid.
175 *
176 * This should be the same as testing for != end().
177 */
178T_CONCURRENTMAP
179inline
180auto CONCURRENTMAP::const_iterator::valid() const -> bool
181{
182 return m_impl.valid();
183}
184
185
186/**
187 * @brief iterator_facade requirement: Increment the iterator.
188 */
189T_CONCURRENTMAP
190inline
191auto CONCURRENTMAP::const_iterator::increment() -> void
192{
193 m_impl.next();
194}
195
196
197/**
198 * @brief iterator_facade requirement: Decrement the iterator.
199 */
200T_CONCURRENTMAP
201inline
202auto CONCURRENTMAP::const_iterator::decrement() -> void
203{
204 m_impl.prev();
205}
206
207
208/**
209 * @brief iterator_facade requirement: Equality test.
210 */
211T_CONCURRENTMAP
212inline
213auto CONCURRENTMAP::const_iterator::equal (const const_iterator& other) const
214 -> bool
215{
216 return !(m_impl != other.m_impl);
217}
218
219
220/**
221 * @brief iterator_facade requirement: Dereference the iterator.
222 */
223T_CONCURRENTMAP
224inline
225auto CONCURRENTMAP::const_iterator::dereference() const
226 -> const const_iterator_value
227{
228 return const_iterator_value (keyAsKey (m_impl.key()),
229 mappedAsMapped (m_impl.value()));
230}
231
232
233/**
234 * @brief Return an iterator range covering the entire map.
235 */
236T_CONCURRENTMAP
237auto CONCURRENTMAP::range() const -> const_iterator_range
238{
239 auto [begin, end] = m_impl.range();
240 return const_iterator_range (begin, end);
241}
242
243
244/**
245 * @brief Iterator at the start of the map.
246 */
247T_CONCURRENTMAP
248inline
249auto CONCURRENTMAP::begin() const -> const_iterator
250{
251 return const_iterator (m_impl.begin());
252}
253
254
255/**
256 * @brief Iterator at the end of the map.
257 */
258T_CONCURRENTMAP
259inline
260auto CONCURRENTMAP::end() const -> const_iterator
261{
262 return const_iterator (m_impl.end());
263}
264
265
266/**
267 * @brief Iterator at the start of the map.
268 */
269T_CONCURRENTMAP
270inline
271auto CONCURRENTMAP::cbegin() const -> const_iterator
272{
273 return begin();
274}
275
276
277/**
278 * @brief Iterator at the end of the map.
279 */
280T_CONCURRENTMAP
281inline
282auto CONCURRENTMAP::cend() const -> const_iterator
283{
284 return end();
285}
286
287
288/**
289 * @brief Test if a key is in the container.
290 * @param key The key to test.
291 */
292T_CONCURRENTMAP
293inline
294bool CONCURRENTMAP::contains (key_type key) const
295{
296 return get(key).valid();
297}
298
299
300/**
301 * @brief Return the number of times a given key is in the container.
302 * @param key The key to test.
303 *
304 * Returns either 0 or 1, depending on whether or not the key is in the map.
305 */
306T_CONCURRENTMAP
307inline
308auto CONCURRENTMAP::count (key_type key) const -> size_type
309{
310 return contains (key) ? 1 : 0;
311}
312
313
314/**
315 * @brief Look up an element in the map.
316 * @param key The element to find.
317 *
318 * Returns either an iterator referencing the found element or end().
319 */
320T_CONCURRENTMAP
321inline
322auto CONCURRENTMAP::find (key_type key) const -> const_iterator
323{
324 return const_iterator (this->get (key));
325}
326
327
328/**
329 * @brief Look up an element in the map.
330 * @param key The element to find.
331 *
332 * Returns the value associated with the key.
333 * Throws @c std::out_of_range if the key does not exist in the map.
334 */
335T_CONCURRENTMAP
336auto CONCURRENTMAP::at (key_type key) const -> mapped_type
337{
338 typename Impl_t::const_iterator it = this->get (key);
339 if (!it.valid()) {
340 throw std::out_of_range ("ConcurrentMap::at");
341 }
342 return mappedAsMapped (it.value());
343}
344
345
346/**
347 * @brief Return a range of iterators with entries matching @c key.
348 * @param key The element to find.
349 *
350 * As keys are unique in this container, this is either a single-element
351 * range, or both iterators are equal to end().
352 */
353T_CONCURRENTMAP
354auto CONCURRENTMAP::equal_range (key_type key) const
355 -> std::pair<const_iterator, const_iterator>
356{
357 const_iterator i1 = find (key);
358 const_iterator i2 = i1;
359 if (i2.valid()) {
360 ++i2;
361 }
362 return std::make_pair (i1, i2);
363}
364
365
366/**
367 * @brief Take a lock on the container.
368 *
369 * Take a lock on the container.
370 * The lock can then be passed to insertion methods, allowing to factor out
371 * the locking inside loops. The lock will be released when the
372 * lock object is destroyed.
373 */
374T_CONCURRENTMAP
375inline
376auto CONCURRENTMAP::lock() -> Lock_t
377{
378 return m_impl.lock();
379}
380
381
382/**
383 * @brief Add an element to the map.
384 * @param key The key of the new item to add.
385 * @param val The value of the new item to add.
386 * @param ctx Execution context.
387 *
388 * This will not overwrite an existing entry.
389 * The first element in the returned pair is an iterator referencing
390 * the added item. The second is a flag that is true if a new element
391 * was added.
392 */
393T_CONCURRENTMAP
394inline
395auto CONCURRENTMAP::emplace (key_type key,
396 mapped_type val,
397 const Context_t& ctx
398 /*= Updater_t::defaultContext()*/)
399 -> std::pair<const_iterator, bool>
400{
401 return put (key, val, false, ctx);
402}
403
404
405/**
406 * @brief Add an element to the map, with external locking.
407 * @param lock The lock object returned from lock().
408 * @param val The value of the new item to add.
409 * @param ctx Execution context.
410 *
411 * This will not overwrite an existing entry.
412 * The first element in the returned pair is an iterator referencing
413 * the added item. The second is a flag that is true if a new element
414 * was added.
415 */
416T_CONCURRENTMAP
417inline
418auto CONCURRENTMAP::emplace (const Lock_t& lock,
419 key_type key,
420 mapped_type val,
421 const Context_t& ctx
422 /*= Updater_t::defaultContext()*/)
423 -> std::pair<const_iterator, bool>
424{
425 return put (lock, key, val, false, ctx);
426}
427
428
429/**
430 * @brief Add an element to the map, or overwrite an existing one.
431 * @param key The key of the new item to add.
432 * @param val The value of the new item to add.
433 * @param ctx Execution context.
434 *
435 * This will overwrite an existing entry.
436 * The first element in the returned pair is an iterator referencing
437 * the added item. The second is a flag that is true if a new element
438 * was added.
439 */
440T_CONCURRENTMAP
441inline
442auto CONCURRENTMAP::insert_or_assign (key_type key,
443 mapped_type val,
444 const Context_t& ctx
445 /*= Updater_t::defaultContext()*/)
446 -> std::pair<const_iterator, bool>
447{
448 return put (key, val, true, ctx);
449}
450
451
452/**
453 * @brief Add an element to the map, or overwrite an existing one,
454 * with external locking.
455 * @param lock The lock object returned from lock().
456 * @param key The key of the new item to add.
457 * @param val The value of the new item to add.
458 * @param ctx Execution context.
459 *
460 * This will overwrite an existing entry.
461 * The first element in the returned pair is an iterator referencing
462 * the added item. The second is a flag that is true if a new element
463 * was added.
464 */
465T_CONCURRENTMAP
466inline
467auto CONCURRENTMAP::insert_or_assign (const Lock_t& lock,
468 key_type key,
469 mapped_type val,
470 const Context_t& ctx
471 /*= Updater_t::defaultContext()*/)
472 -> std::pair<const_iterator, bool>
473{
474 return put (lock, key, val, true, ctx);
475}
476
477
478/**
479 * @brief Add an element to the map.
480 * @param p The item to add.
481 * Should be a pair where first is the key
482 * and second is the integer value.
483 *
484 * This will not overwrite an existing entry.
485 * The first element in the returned pair is an iterator referencing
486 * the added item. The second is a flag that is true if a new element
487 * was added.
488 *
489 * For external locking, use emplace().
490 */
491T_CONCURRENTMAP
492template <class PAIR>
493inline
494auto CONCURRENTMAP::insert (const PAIR& p)
495 -> std::pair<const_iterator, bool>
496{
497 return emplace (p.first, p.second);
498}
499
500
501/**
502 * @brief Insert a range of elements to the map.
503 * @param first Start of the range.
504 * @param last End of the range.
505 *
506 * The range should be a sequence of pairs where first is the string key
507 * and second is the integer value.
508 */
509T_CONCURRENTMAP
510template <class InputIterator>
511void CONCURRENTMAP::insert (InputIterator first, InputIterator last)
512{
513 Lock_t lck = lock();
514 const Context_t& ctx = Updater_t::defaultContext();
515 for (; first != last; ++first) {
516 emplace (lck, first->first, first->second, ctx);
517 }
518}
519
520
521/**
522 * @brief Erase an entry from the table.
523 * @param key The key to erase.
524 *
525 * Mark the corresponding entry as deleted.
526 * Return true on success, false on failure (key not found).
527 *
528 * The tombstone value must be different from the null value.
529 *
530 * This may cause the key type returned by an iterator to change
531 * asynchronously to the tombstone value.
532 **/
533T_CONCURRENTMAP
534inline
535bool CONCURRENTMAP::erase (key_type key)
536{
537 val_t kval = keyAsVal (key);
538 size_t hash = m_impl.hasher() (kval);
539 return m_impl.erase (kval, hash);
540}
541
542
543/**
544 * @brief Erase an entry from the table, with external locking.
545 * @param lock The lock object returned from lock().
546 * @param key The key to erase.
547 *
548 * Mark the corresponding entry as deleted.
549 * Return true on success, false on failure (key not found).
550 *
551 * The tombstone value must be different from the null value.
552 *
553 * This may cause the key type returned by an iterator to change
554 * asynchronously to the tombstone value.
555 **/
556T_CONCURRENTMAP
557inline
558bool CONCURRENTMAP::erase (const Lock_t& lock, key_type key)
559{
560 val_t kval = keyAsVal (key);
561 size_t hash = m_impl.hasher() (kval);
562 return m_impl.erase (lock, kval, hash);
563}
564
565
566/**
567 * @brief Increase the table capacity.
568 * @param capacity The new table capacity.
569 * @param ctx Execution context.
570 *
571 * No action will be taken if @c capacity is smaller
572 * than the current capacity.
573 */
574T_CONCURRENTMAP
575inline
576void CONCURRENTMAP::reserve (size_type capacity,
577 const Context_t& ctx
578 /*= Updater_t::defaultContext()*/)
579{
580 return m_impl.reserve (capacity, ctx);
581}
582
583
584/**
585 * @brief Increase the table capacity.
586 * @param capacity The new table capacity.
587 *
588 * No action will be taken if @c capacity is smaller
589 * than the current capacity.
590 */
591T_CONCURRENTMAP
592inline
593void CONCURRENTMAP::rehash (size_type capacity)
594{
595 return reserve (capacity);
596}
597
598
599/**
600 * @brief Erase the table and change the capacity.
601 * @param capacity The new table capacity.
602 * @param ctx Execution context.
603 */
604T_CONCURRENTMAP
605inline
606void CONCURRENTMAP::clear (size_t capacity,
607 const Context_t& ctx /*= Updater_t::defaultContext()*/)
608{
609 m_impl.clear (capacity, ctx);
610}
611
612
613/**
614 * @brief Erase the table (don't change the capacity).
615 * @param ctx Execution context.
616 */
617T_CONCURRENTMAP
618inline
619void CONCURRENTMAP::clear (const Context_t& ctx /*= Updater_t::defaultContext()*/)
620{
621 m_impl.clear (ctx);
622}
623
624
625/**
626 * @brief Erase the table in-place.
627 *
628 * This method avoids allocating a new version of the table.
629 * However, it is not safe to use concurrently --- no other threads
630 * may be accessing the container at the same time, either for read
631 * or write.
632 */
633T_CONCURRENTMAP
634inline
635void CONCURRENTMAP::forceClear()
636{
637 m_impl.forceClear();
638}
639
640
641/**
642 * @brief Called when this thread is no longer referencing anything
643 * from this container.
644 * @param ctx Execution context.
645 */
646T_CONCURRENTMAP
647inline
648void CONCURRENTMAP::quiescent (const Context_t& ctx)
649{
650 return m_impl.quiescent (ctx);
651}
652
653
654/**
655 * @brief Swap this container with another.
656 * @param other The container with which to swap.
657 *
658 * This will also call swap on the Updater object; hence, the Updater
659 * object must also support swap. The Hasher and Matcher instances
660 * are NOT swapped.
661 *
662 * This operation is NOT thread-safe. No other threads may be accessing
663 * either container during this operation.
664 */
665T_CONCURRENTMAP
666void CONCURRENTMAP::swap (ConcurrentMap& other)
667{
668 return m_impl.swap (other.m_impl);
669}
670
671
672/**
673 * @brief Access the Updater instance.
674 */
675T_CONCURRENTMAP
676inline
677auto CONCURRENTMAP::updater() -> Updater_t&
678{
679 return m_impl.updater();
680}
681
682
683/**
684 * @brief Convert an underlying key value to this type's key value.
685 * @param val The underlying key value.
686 */
687T_CONCURRENTMAP
688inline
689auto CONCURRENTMAP::keyAsKey (val_t val) -> key_type
690{
691 return CxxUtils::detail::UIntConv<key_type>::uintToVal (val);
692}
693
694
695/**
696 * @brief Convert this type's key value to an underlying key value.
697 * @param k The key.
698 */
699T_CONCURRENTMAP
700inline
701auto CONCURRENTMAP::keyAsVal (key_type k) -> val_t
702{
703 return CxxUtils::detail::UIntConv<key_type>::valToUInt (k);
704}
705
706
707/**
708 * @brief Convert an underlying mapped value to this type's mapped value.
709 * @param val The underlying mapped value.
710 */
711T_CONCURRENTMAP
712inline
713auto CONCURRENTMAP::mappedAsMapped (val_t val) -> mapped_type
714{
715 return CxxUtils::detail::UIntConv<mapped_type>::uintToVal (val);
716}
717
718
719/**
720 * @brief Convert this type's mapped value to an underlying mapped value.
721 * @param val The mapped value.
722 */
723T_CONCURRENTMAP
724inline
725auto CONCURRENTMAP::mappedAsVal (mapped_type val) -> val_t
726{
727 return CxxUtils::detail::UIntConv<mapped_type>::valToUInt (val);
728}
729
730
731/**
732 * @brief Do a lookup in the table.
733 * @param key The key to look up.
734 *
735 * Returns an iterator of the underlying map pointing at the found
736 * entry or end();
737 */
738T_CONCURRENTMAP
739auto CONCURRENTMAP::get (key_type key) const
740 -> typename Impl_t::const_iterator
741{
742 val_t kval = keyAsVal (key);
743 size_t hash = m_impl.hasher() (kval);
744 return m_impl.get (kval, hash);
745}
746
747
748/**
749 * @brief Insert / overwrite an entry in the table.
750 * @param key The key of the new item to add.
751 * @param val The value of the new item to add.
752 * @param overwrite If true, allow overwriting an existing entry.
753 * @param ctx Execution context.
754 *
755 * The first element in the returned pair is an iterator referencing
756 * the added item. The second is a flag that is true if a new element
757 * was added.
758 */
759T_CONCURRENTMAP
760auto CONCURRENTMAP::put (key_type key,
761 mapped_type val,
762 bool overwrite /*= true*/,
763 const Context_t& ctx /*= Updater_t::defaultContext()*/)
764 -> std::pair<const_iterator, bool>
765{
766 val_t kval = keyAsVal (key);
767 size_t hash = m_impl.hasher() (kval);
768 auto [it, flag] = m_impl.put (kval, hash,
769 mappedAsVal (val),
770 overwrite, ctx);
771 return std::make_pair (const_iterator (it), flag);
772}
773
774
775/**
776 * @brief Insert / overwrite an entry in the table, with external locking.
777 * @param lock The lock object returned from lock().
778 * @param key The key of the new item to add.
779 * @param val The value of the new item to add.
780 * @param overwrite If true, allow overwriting an existing entry.
781 * @param ctx Execution context.
782 *
783 * The first element in the returned pair is an iterator referencing
784 * the added item. The second is a flag that is true if a new element
785 * was added.
786 */
787T_CONCURRENTMAP
788auto CONCURRENTMAP::put (const Lock_t& lock,
789 key_type key,
790 mapped_type val,
791 bool overwrite /*= true*/,
792 const Context_t& ctx /*= Updater_t::defaultContext()*/)
793 -> std::pair<const_iterator, bool>
794{
795 val_t kval = keyAsVal (key);
796 size_t hash = m_impl.hasher() (kval);
797 auto [it, flag] = m_impl.put (lock, kval, hash,
798 mappedAsVal (val),
799 overwrite, ctx);
800 return std::make_pair (const_iterator (it), flag);
801}
802
803
804#undef T_CONCURRENTMAP
805#undef CONCURRENTMAP
806
807
808} // namespace CxxUtils