2 * Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration.
5 * @file CxxUtils/ConcurrentToValMap.icc
6 * @author scott snyder <snyder@bnl.gov>
8 * @brief Hash map from pointers/integers to arbitrary objects allowing
9 * concurrent, lockless reads.
16 #define T_CONCURRENTTOVALMAP template <class KEY, class VALUE, \
17 template <class> class UPDATER, \
18 class HASHER, class MATCHER, \
19 detail::ConcurrentHashmapVal_t NULLVAL> \
20 requires (detail::IsConcurrentHashmapPayload<KEY> && \
21 detail::IsUpdater<UPDATER> && \
22 detail::IsHash<HASHER, KEY> && \
23 detail::IsBinaryPredicate<MATCHER, KEY>)
25 #define CONCURRENTTOVALMAP ConcurrentToValMap<KEY, VALUE, UPDATER, HASHER, MATCHER, NULLVAL>
30 * @param updater Object used to manage memory
31 * (see comments at the start of the class).
32 * @param capacity The initial table capacity.
33 * (Will be rounded up to a power of two.)
34 * @param ctx Execution context.
37 CONCURRENTTOVALMAP::ConcurrentToValMap (Updater_t&& updater,
38 size_type capacity /*= 64*/,
40 /* = Updater_t::defaultContext()*/)
41 : m_impl (std::move (updater),
51 * @brief Constructor from another map.
52 * @param updater Object used to manage memory
53 * (see comments at the start of the class).
54 * @param capacity The initial table capacity of the new table.
55 * (Will be rounded up to a power of two.)
56 * @param ctx Execution context.
58 * (Not really a copy constructor since we need to pass @c updater.)
61 CONCURRENTTOVALMAP::ConcurrentToValMap (const ConcurrentToValMap& other,
63 size_type capacity /*= 64*/,
65 /*= Updater_t::defaultContext()*/)
66 : m_impl (std::move (updater),
72 // not using reference, because our iterator doesn't return a reference
73 for (const auto p : other) {
74 this->put (p.first, std::make_unique<mapped_type> (p.second), ctx);
80 * @brief Constructor from a range.
81 * @param f Start iterator for the range.
82 * @param l End iterator for the range.
83 * @param updater Object used to manage memory
84 * (see comments at the start of the class).
85 * @param capacity The initial table capacity of the new table.
86 * (Will be rounded up to a power of two.)
87 * @param ctx Execution context.
89 * Constructor from a range of pairs.
92 template <class InputIterator>
93 CONCURRENTTOVALMAP::ConcurrentToValMap (InputIterator f,
96 size_t capacity /*= 64*/,
98 /*= Updater_t::defaultContext()*/)
99 : m_impl (std::move (updater),
105 if constexpr (std::is_rvalue_reference_v<typename InputIterator::reference>)
107 for (; f != l; ++f) {
108 emplace (f->first, std::move (f->second), ctx);
112 for (; f != l; ++f) {
113 emplace (f->first, f->second, ctx);
123 CONCURRENTTOVALMAP::~ConcurrentToValMap()
125 // Need to delete the values that we've stored.
126 auto [begin, end] = m_impl.range();
127 while (begin != end) {
128 if (begin.key() != Impl_t::nullval) {
129 delete mappedAsMapped (begin.value());
137 * @brief Return the number of items currently in the map.
141 auto CONCURRENTTOVALMAP::size() const -> size_type
143 return m_impl.size();
148 * @brief Test if the map is currently empty.
152 bool CONCURRENTTOVALMAP::empty() const
154 return !m_impl.size();
159 * @brief Return the current size (capacity) of the hash table.
163 auto CONCURRENTTOVALMAP::capacity() const -> size_t
165 return m_impl.capacity();
170 * @brief Constructor.
171 * @param it Iterator of the underlying table.
175 CONCURRENTTOVALMAP::const_iterator::const_iterator (typename Impl_t::const_iterator it)
182 * @brief Conversion from non-const iterator (for interoperability).
183 * @param other The other iterator.
187 CONCURRENTTOVALMAP::const_iterator::const_iterator (const iterator& other)
188 : m_impl (other.m_impl)
194 * @brief Test if this iterator is valid.
196 * This should be the same as testing for != end().
200 auto CONCURRENTTOVALMAP::const_iterator::valid() const -> bool
202 return m_impl.valid();
207 * @brief iterator_facade requirement: Increment the iterator.
211 auto CONCURRENTTOVALMAP::const_iterator::increment() -> void
218 * @brief iterator_facade requirement: Decrement the iterator.
222 auto CONCURRENTTOVALMAP::const_iterator::decrement() -> void
229 * @brief iterator_facade requirement: Dereference the iterator.
233 auto CONCURRENTTOVALMAP::const_iterator::dereference() const
234 -> const const_iterator_value
236 return const_iterator_value (keyAsKey (m_impl.key()),
237 *mappedAsMapped (m_impl.value()));
242 * @brief iterator_facade requirement: Equality test.
246 auto CONCURRENTTOVALMAP::const_iterator::equal (const const_iterator& other) const
249 return !(m_impl != other.m_impl);
254 * @brief iterator_facade requirement: Equality test.
258 auto CONCURRENTTOVALMAP::const_iterator::equal (const iterator& other) const
261 return !(m_impl != other.m_impl);
266 * @brief Constructor.
267 * @param it Iterator of the underlying table.
271 CONCURRENTTOVALMAP::iterator::iterator (typename Impl_t::const_iterator it)
278 * @brief Test if this iterator is valid.
280 * This should be the same as testing for != end().
284 auto CONCURRENTTOVALMAP::iterator::valid() const -> bool
286 return m_impl.valid();
291 * @brief iterator_facade requirement: Increment the iterator.
295 auto CONCURRENTTOVALMAP::iterator::increment() -> void
302 * @brief iterator_facade requirement: Decrement the iterator.
306 auto CONCURRENTTOVALMAP::iterator::decrement() -> void
313 * @brief iterator_facade requirement: Dereference the iterator.
317 auto CONCURRENTTOVALMAP::iterator::dereference() const
318 -> const iterator_value
320 return iterator_value (keyAsKey (m_impl.key()),
321 *mappedAsMapped (m_impl.value()));
326 * @brief iterator_facade requirement: Equality test.
330 auto CONCURRENTTOVALMAP::iterator::equal (const iterator& other) const
333 return !(m_impl != other.m_impl);
338 * @brief iterator_facade requirement: Equality test. (Interoperability.)
342 auto CONCURRENTTOVALMAP::iterator::equal (const const_iterator& other) const
345 return !(m_impl != other.m_impl);
350 * @brief Return an iterator range covering the entire map.
353 auto CONCURRENTTOVALMAP::range() const -> const_iterator_range
355 auto [begin, end] = m_impl.range();
356 return const_iterator_range (begin, end);
361 * @brief Return an iterator range covering the entire map.
363 * The mapped objects must themselves be thread-safe in order to make
364 * any changes to them through the returned iterators.
367 auto CONCURRENTTOVALMAP::range() -> iterator_range
369 auto [begin, end] = m_impl.range();
370 return iterator_range (begin, end);
375 * @brief Iterator at the start of the map.
379 auto CONCURRENTTOVALMAP::begin() const -> const_iterator
381 return const_iterator (m_impl.begin());
386 * @brief Iterator at the end of the map.
390 auto CONCURRENTTOVALMAP::end() const -> const_iterator
392 return const_iterator (m_impl.end());
397 * @brief Iterator at the start of the map.
399 * The mapped objects must themselves be thread-safe in order to make
400 * any changes to them through the returned iterators.
404 auto CONCURRENTTOVALMAP::begin() -> iterator
406 return iterator (m_impl.begin());
411 * @brief Iterator at the end of the map.
413 * The mapped objects must themselves be thread-safe in order to make
414 * any changes to them through the returned iterators.
418 auto CONCURRENTTOVALMAP::end() -> iterator
420 return iterator (m_impl.end());
425 * @brief Iterator at the start of the map.
429 auto CONCURRENTTOVALMAP::cbegin() const -> const_iterator
436 * @brief Iterator at the end of the map.
440 auto CONCURRENTTOVALMAP::cend() const -> const_iterator
447 * @brief Test if a key is in the container.
448 * @param key The key to test.
452 bool CONCURRENTTOVALMAP::contains (const key_type key) const
454 return get(key).valid();
459 * @brief Return the number of times a given key is in the container.
460 * @param key The key to test.
462 * Returns either 0 or 1, depending on whether or not the key is in the map.
466 auto CONCURRENTTOVALMAP::count (const key_type key) const -> size_type
468 return contains (key) ? 1 : 0;
473 * @brief Look up an element in the map.
474 * @param key The element to find.
476 * Returns either an iterator referencing the found element or end().
480 auto CONCURRENTTOVALMAP::find (const key_type key) const -> const_iterator
482 return const_iterator (this->get (key));
487 * @brief Look up an element in the map.
488 * @param key The element to find.
490 * Returns either an iterator referencing the found element or end().
492 * The mapped object must itself be thread-safe in order to make
493 * any changes to it through the returned iterator.
497 auto CONCURRENTTOVALMAP::find (const key_type key) -> iterator
499 return iterator (this->get (key));
504 * @brief Look up an element in the map.
505 * @param key The element to find.
507 * Returns the value associated with the key.
508 * Throws @c std::out_of_range if the key does not exist in the map.
511 auto CONCURRENTTOVALMAP::at (const key_type key) const -> const mapped_type&
513 typename Impl_t::const_iterator it = this->get (key);
515 throw std::out_of_range ("ConcurrentToValMap::at");
517 return *mappedAsMapped (it.value());
522 * @brief Look up an element in the map.
523 * @param key The element to find.
525 * Returns the value associated with the key.
526 * Throws @c std::out_of_range if the key does not exist in the map.
528 * This returns a non-const reference to the mapped object.
529 * The mapped object must be thread-safe itself in order to safely
530 * make any changes to it.
533 auto CONCURRENTTOVALMAP::at (const key_type key) -> mapped_type&
535 typename Impl_t::const_iterator it = this->get (key);
537 throw std::out_of_range ("ConcurrentToValMap::at");
539 return *mappedAsMapped (it.value());
544 * @brief Return a range of iterators with entries matching @c key.
545 * @param key The element to find.
547 * As keys are unique in this container, this is either a single-element
548 * range, or both iterators are equal to end().
551 auto CONCURRENTTOVALMAP::equal_range (const key_type key) const
552 -> std::pair<const_iterator, const_iterator>
554 const_iterator i1 = find (key);
555 const_iterator i2 = i1;
559 return std::make_pair (i1, i2);
564 * @brief Return a range of iterators with entries matching @c key.
565 * @param key The element to find.
567 * As keys are unique in this container, this is either a single-element
568 * range, or both iterators are equal to end().
570 * The mapped objects must themselves be thread-safe in order to make
571 * any changes to them through the returned iterators.
574 auto CONCURRENTTOVALMAP::equal_range (const key_type key)
575 -> std::pair<iterator, iterator>
577 iterator i1 = find (key);
582 return std::make_pair (i1, i2);
587 * @brief Add an element to the map.
588 * @param key The key of the new item to add.
589 * @param val The value of the new item to add.
590 * @param ctx Execution context.
592 * This will not overwrite an existing entry.
593 * The first element in the returned pair is an iterator referencing
594 * the added item. The second is a flag that is true if a new element
599 auto CONCURRENTTOVALMAP::emplace (key_type key,
600 const mapped_type& val,
602 /*= Updater_t::defaultContext()*/)
603 -> std::pair<const_iterator, bool>
605 return put (key, std::make_unique<mapped_type> (val), ctx);
610 * @brief Add an element to the map.
611 * @param key The key of the new item to add.
612 * @param val The value of the new item to add.
613 * @param ctx Execution context.
615 * This will not overwrite an existing entry.
616 * The first element in the returned pair is an iterator referencing
617 * the added item. The second is a flag that is true if a new element
622 auto CONCURRENTTOVALMAP::emplace (key_type key,
625 /*= Updater_t::defaultContext()*/)
626 -> std::pair<const_iterator, bool>
628 return put (key, std::make_unique<mapped_type> (std::move (val)), ctx);
633 * @brief Add an element to the map.
634 * @param key The key of the new item to add.
635 * @param val The value of the new item to add.
636 * @param ctx Execution context.
638 * This will not overwrite an existing entry.
639 * The first element in the returned pair is an iterator referencing
640 * the added item. The second is a flag that is true if a new element
645 auto CONCURRENTTOVALMAP::emplace (key_type key,
646 std::unique_ptr<mapped_type> val,
648 /*= Updater_t::defaultContext()*/)
649 -> std::pair<const_iterator, bool>
651 return put (key, std::move (val), ctx);
656 * @brief Add an element to the map.
657 * @param p The item to add.
658 * Should be a pair where first is the string key
659 * and second is the integer value.
660 * @param ctx Execution context.
662 * This will not overwrite an existing entry.
663 * The first element in the returned pair is an iterator referencing
664 * the added item. The second is a flag that is true if a new element
668 template <class PAIR>
670 auto CONCURRENTTOVALMAP::insert (const PAIR& p,
671 const Context_t& ctx /*= Updater_t::defaultContext()*/)
672 -> std::pair<const_iterator, bool>
674 return emplace (p.first, p.second, ctx);
679 * @brief Add an element to the map.
680 * @param p The item to add.
681 * Should be a pair where first is the string key
682 * and second is the integer value.
683 * @param ctx Execution context.
685 * This will not overwrite an existing entry.
686 * The first element in the returned pair is an iterator referencing
687 * the added item. The second is a flag that is true if a new element
691 template <class PAIR>
693 auto CONCURRENTTOVALMAP::insert (PAIR&& p,
694 const Context_t& ctx /*= Updater_t::defaultContext()*/)
695 -> std::pair<const_iterator, bool>
697 return emplace (p.first, std::move (p.second), ctx);
702 * @brief Insert a range of elements to the map.
703 * @param first Start of the range.
704 * @param last End of the range.
705 * @param ctx Execution context.
707 * The range should be a sequence of pairs where first is the string key
708 * and second is the integer value.
711 template <class InputIterator>
712 void CONCURRENTTOVALMAP::insert (InputIterator first, InputIterator last,
713 const Context_t& ctx /*= Updater_t::defaultContext()*/)
715 if constexpr (std::is_rvalue_reference_v<typename InputIterator::reference>)
717 for (; first != last; ++first) {
718 emplace (first->first, std::move (first->second), ctx);
722 for (; first != last; ++first) {
723 emplace (first->first, first->second, ctx);
730 * @brief Increase the table capacity.
731 * @param capacity The new table capacity.
732 * @param ctx Execution context.
734 * No action will be taken if @c capacity is smaller
735 * than the current capacity.
739 void CONCURRENTTOVALMAP::reserve (size_type capacity,
741 /*= Updater_t::defaultContext()*/)
743 return m_impl.reserve (capacity, ctx);
748 * @brief Increase the table capacity.
749 * @param capacity The new table capacity.
751 * No action will be taken if @c capacity is smaller
752 * than the current capacity.
756 void CONCURRENTTOVALMAP::rehash (size_type capacity)
758 return reserve (capacity);
763 * @brief Called when this thread is no longer referencing anything
764 * from this container.
765 * @param ctx Execution context.
769 void CONCURRENTTOVALMAP::quiescent (const Context_t& ctx)
771 return m_impl.quiescent (ctx);
776 * @brief Swap this container with another.
777 * @param other The container with which to swap.
779 * This will also call swap on the Updater object; hence, the Updater
780 * object must also support swap.
782 * This operation is NOT thread-safe. No other threads may be accessing
783 * either container during this operation.
786 void CONCURRENTTOVALMAP::swap (ConcurrentToValMap& other)
788 m_impl.swap (other.m_impl);
793 * @brief Access the Updater instance.
796 auto CONCURRENTTOVALMAP::updater() -> Updater_t&
798 return m_impl.updater();
803 * @brief Convert an underlying key value to this type's key value.
804 * @param val The underlying key value.
808 auto CONCURRENTTOVALMAP::keyAsKey (val_t val) -> key_type
810 return CxxUtils::detail::UIntConv<key_type>::uintToVal (val);
815 * @brief Convert this type's key value to an underlying key value.
820 auto CONCURRENTTOVALMAP::keyAsVal (key_type k) -> val_t
822 return CxxUtils::detail::UIntConv<key_type>::valToUInt (k);
827 * @brief Convert an underlying mapped value a pointer to
828 * this type's mapped value.
829 * @param val The underlying mapped value.
833 auto CONCURRENTTOVALMAP::mappedAsMapped (val_t val) -> mapped_type*
835 return CxxUtils::detail::UIntConv<mapped_type*>::uintToVal (val);
840 * @brief Convert this type's mapped value to an underlying mapped value.
841 * @param val The mapped value.
845 auto CONCURRENTTOVALMAP::mappedAsVal (mapped_type* val) -> val_t
847 return CxxUtils::detail::UIntConv<mapped_type*>::valToUInt (val);
852 * @brief Do a lookup in the table.
853 * @param key The key to look up.
855 * Returns an iterator of the underlying map pointing at the found
859 auto CONCURRENTTOVALMAP::get (const key_type key) const
860 -> typename Impl_t::const_iterator
862 val_t kval = keyAsVal (key);
863 size_t hash = m_impl.hasher() (kval);
864 return m_impl.get (keyAsVal(key), hash);
869 * @brief Insert an entry in the table.
870 * @param key The key of the new item to add.
871 * @param val The new mapped value to add.
872 * @param ctx Execution context.
874 * The first element in the returned pair is an iterator referencing
875 * the added item. The second is a flag that is true if a new element
879 auto CONCURRENTTOVALMAP::put (const key_type key,
880 std::unique_ptr<mapped_type> val,
881 const Context_t& ctx /*= Updater_t::defaultContext()*/)
882 -> std::pair<const_iterator, bool>
884 val_t kval = keyAsVal (key);
885 size_t hash = m_impl.hasher() (kval);
886 auto [it, flag] = m_impl.put (kval, hash,
887 mappedAsVal (val.get()),
889 if (flag) val.release();
890 return std::make_pair (const_iterator (it), flag);
894 #undef T_CONCURRENTTOVALMAP
895 #undef CONCURRENTTOVALMAP
898 } // namespace CxxUtils