2 * Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration.
5 * @file CxxUtils/ConcurrentStrMap.icc
6 * @author scott snyder <snyder@bnl.gov>
8 * @brief Hash map from strings to integers allowing concurrent, lockless reads.
15 #define T_CONCURRENTSTRMAP template <class VALUE, template <class> class UPDATER> \
16 ATH_REQUIRES (detail::IsConcurrentHashmapPayload<VALUE> && \
17 detail::IsUpdater<UPDATER>)
19 #define CONCURRENTSTRMAP ConcurrentStrMap<VALUE, UPDATER>
24 * @param updater Object used to manage memory
25 * (see comments at the start of the class).
26 * @param capacity The initial table capacity.
27 * (Will be rounded up to a power of two.)
28 * @param ctx Execution context.
31 CONCURRENTSTRMAP::ConcurrentStrMap (Updater_t&& updater,
32 size_type capacity /*= 64*/,
34 /* = Updater_t::defaultContext()*/)
35 : m_impl (std::move (updater),
45 * @brief Constructor from another map.
46 * @param updater Object used to manage memory
47 * (see comments at the start of the class).
48 * @param capacity The initial table capacity of the new table.
49 * (Will be rounded up to a power of two.)
50 * @param ctx Execution context.
52 * (Not really a copy constructor since we need to pass @c updater.)
55 CONCURRENTSTRMAP::ConcurrentStrMap (const ConcurrentStrMap& other,
57 size_type capacity /*= 64*/,
59 /*= Updater_t::defaultContext()*/)
60 : m_impl (std::move (updater),
66 // not using reference, because our iterator doesn't return a reference
67 for (const auto p : other) {
68 this->emplace (p.first, p.second, ctx);
74 * @brief Constructor from a range.
75 * @param f Start iterator for the range.
76 * @param l End iterator for the range.
77 * @param updater Object used to manage memory
78 * (see comments at the start of the class).
79 * @param capacity The initial table capacity of the new table.
80 * (Will be rounded up to a power of two.)
81 * @param ctx Execution context.
83 * Constructor from a range of pairs.
86 template <class InputIterator>
87 CONCURRENTSTRMAP::ConcurrentStrMap (InputIterator f,
90 size_t capacity /*= 64*/,
92 /*= Updater_t::defaultContext()*/)
93 : m_impl (std::move (updater),
100 if constexpr (std::is_rvalue_reference_v<typename InputIterator::reference>)
102 for (; f != l; ++f) {
103 this->emplace (lck, std::move (f->first), f->second, ctx);
107 for (; f != l; ++f) {
108 this->emplace (lck, f->first, f->second, ctx);
118 CONCURRENTSTRMAP::~ConcurrentStrMap()
120 // Need to delete the strings that we've stored.
121 auto [begin, end] = m_impl.range();
122 while (begin != end) {
123 if (begin.key() != Impl_t::nullval) {
124 delete keyAsString (begin.key());
132 * @brief Return the number of items currently in the map.
136 auto CONCURRENTSTRMAP::size() const -> size_type
138 return m_impl.size();
143 * @brief Test if the map is currently empty.
147 bool CONCURRENTSTRMAP::empty() const
149 return !m_impl.size();
154 * @brief Return the current size (capacity) of the hash table.
158 auto CONCURRENTSTRMAP::capacity() const -> size_t
160 return m_impl.capacity();
165 * @brief Constructor.
166 * @param it Iterator of the underlying table.
170 CONCURRENTSTRMAP::const_iterator::const_iterator (typename Impl_t::const_iterator it)
177 * @brief Test if this iterator is valid.
179 * This should be the same as testing for != end().
183 auto CONCURRENTSTRMAP::const_iterator::valid() const -> bool
185 return m_impl.valid();
190 * @brief iterator_facade requirement: Increment the iterator.
194 auto CONCURRENTSTRMAP::const_iterator::increment() -> void
201 * @brief iterator_facade requirement: Decrement the iterator.
205 auto CONCURRENTSTRMAP::const_iterator::decrement() -> void
212 * @brief iterator_facade requirement: Dereference the iterator.
216 auto CONCURRENTSTRMAP::const_iterator::dereference() const
217 -> const const_iterator_value
219 return const_iterator_value (*keyAsString (m_impl.key()),
220 mappedAsMapped (m_impl.value()));
225 * @brief iterator_facade requirement: Equality test.
229 auto CONCURRENTSTRMAP::const_iterator::equal (const const_iterator& other) const
232 return !(m_impl != other.m_impl);
237 * @brief Return an iterator range covering the entire map.
240 auto CONCURRENTSTRMAP::range() const -> const_iterator_range
242 auto [begin, end] = m_impl.range();
243 return const_iterator_range (begin, end);
248 * @brief Iterator at the start of the map.
252 auto CONCURRENTSTRMAP::begin() const -> const_iterator
254 return const_iterator (m_impl.begin());
259 * @brief Iterator at the end of the map.
263 auto CONCURRENTSTRMAP::end() const -> const_iterator
265 return const_iterator (m_impl.end());
270 * @brief Iterator at the start of the map.
274 auto CONCURRENTSTRMAP::cbegin() const -> const_iterator
281 * @brief Iterator at the end of the map.
285 auto CONCURRENTSTRMAP::cend() const -> const_iterator
292 * @brief Test if a key is in the container.
293 * @param key The key to test.
297 bool CONCURRENTSTRMAP::contains (const key_type& key) const
299 return get(key).valid();
304 * @brief Return the number of times a given key is in the container.
305 * @param key The key to test.
307 * Returns either 0 or 1, depending on whether or not the key is in the map.
311 auto CONCURRENTSTRMAP::count (const key_type& key) const -> size_type
313 return contains (key) ? 1 : 0;
318 * @brief Look up an element in the map.
319 * @param key The element to find.
321 * Returns either an iterator referencing the found element or end().
325 auto CONCURRENTSTRMAP::find (const key_type& key) const -> const_iterator
327 return const_iterator (this->get (key));
332 * @brief Look up an element in the map.
333 * @param key The element to find.
335 * Returns the value associated with the key.
336 * Throws @c std::out_of_range if the key does not exist in the map.
339 auto CONCURRENTSTRMAP::at (const key_type& key) const -> mapped_type
341 typename Impl_t::const_iterator it = this->get (key);
343 throw std::out_of_range ("ConcurrentStrMap::at");
345 return mappedAsMapped (it.value());
350 * @brief Return a range of iterators with entries matching @c key.
351 * @param key The element to find.
353 * As keys are unique in this container, this is either a single-element
354 * range, or both iterators are equal to end().
357 auto CONCURRENTSTRMAP::equal_range (const key_type& key) const
358 -> std::pair<const_iterator, const_iterator>
360 const_iterator i1 = find (key);
361 const_iterator i2 = i1;
365 return std::make_pair (i1, i2);
370 * @brief Take a lock on the container.
372 * Take a lock on the container.
373 * The lock can then be passed to insertion methods, allowing to factor out
374 * the locking inside loops. The lock will be released when the
375 * lock object is destroyed.
379 auto CONCURRENTSTRMAP::lock() -> Lock_t
381 return m_impl.lock();
386 * @brief Add an element to the map.
387 * @param key The key of the new item to add.
388 * @param val The value of the new item to add.
389 * @param ctx Execution context.
391 * This will not overwrite an existing entry.
392 * The first element in the returned pair is an iterator referencing
393 * the added item. The second is a flag that is true if a new element
398 auto CONCURRENTSTRMAP::emplace (const key_type& key,
401 /*= Updater_t::defaultContext()*/)
402 -> std::pair<const_iterator, bool>
404 return put (std::make_unique<key_type> (key), val, false, ctx);
409 * @brief Add an element to the map, with external locking.
410 * @param lock The lock object returned from lock().
411 * @param key The key of the new item to add.
412 * @param val The value of the new item to add.
413 * @param ctx Execution context.
415 * This will not overwrite an existing entry.
416 * The first element in the returned pair is an iterator referencing
417 * the added item. The second is a flag that is true if a new element
422 auto CONCURRENTSTRMAP::emplace (const Lock_t& lock,
426 /*= Updater_t::defaultContext()*/)
427 -> std::pair<const_iterator, bool>
429 return put (lock, std::make_unique<key_type> (key), val, false, ctx);
434 * @brief Add an element to the map.
435 * @param key The key of the new item to add.
436 * @param val The value of the new item to add.
437 * @param ctx Execution context.
439 * This will not overwrite an existing entry.
440 * The first element in the returned pair is an iterator referencing
441 * the added item. The second is a flag that is true if a new element
446 auto CONCURRENTSTRMAP::emplace (key_type&& key,
449 /*= Updater_t::defaultContext()*/)
450 -> std::pair<const_iterator, bool>
452 return put (std::make_unique<key_type> (std::move (key)), val, false, ctx);
457 * @brief Add an element to the map, with external locking.
458 * @param lock The lock object returned from lock().
459 * @param key The key of the new item to add.
460 * @param val The value of the new item to add.
461 * @param ctx Execution context.
463 * This will not overwrite an existing entry.
464 * The first element in the returned pair is an iterator referencing
465 * the added item. The second is a flag that is true if a new element
470 auto CONCURRENTSTRMAP::emplace (const Lock_t& lock,
474 /*= Updater_t::defaultContext()*/)
475 -> std::pair<const_iterator, bool>
477 return put (lock, std::make_unique<key_type> (std::move (key)), val, false, ctx);
482 * @brief Add an element to the map, or overwrite an existing one.
483 * @param key The key of the new item to add.
484 * @param val The value of the new item to add.
485 * @param ctx Execution context.
487 * This will overwrite an existing entry.
488 * The first element in the returned pair is an iterator referencing
489 * the added item. The second is a flag that is true if a new element
494 auto CONCURRENTSTRMAP::insert_or_assign (const key_type& key,
497 /*= Updater_t::defaultContext()*/)
498 -> std::pair<const_iterator, bool>
500 return put (std::make_unique<key_type> (key), val, true, ctx);
505 * @brief Add an element to the map, or overwrite an existing one,
506 * with external locking.
507 * @param lock The lock object returned from lock().
508 * @param key The key of the new item to add.
509 * @param val The value of the new item to add.
510 * @param ctx Execution context.
512 * This will overwrite an existing entry.
513 * The first element in the returned pair is an iterator referencing
514 * the added item. The second is a flag that is true if a new element
519 auto CONCURRENTSTRMAP::insert_or_assign (const Lock_t& lock,
523 /*= Updater_t::defaultContext()*/)
524 -> std::pair<const_iterator, bool>
526 return put (lock, std::make_unique<key_type> (key), val, true, ctx);
531 * @brief Add an element to the map, or overwrite an existing one.
532 * @param key The key of the new item to add.
533 * @param val The value of the new item to add.
534 * @param ctx Execution context.
536 * This will overwrite an existing entry.
537 * The first element in the returned pair is an iterator referencing
538 * the added item. The second is a flag that is true if a new element
543 auto CONCURRENTSTRMAP::insert_or_assign (key_type&& key,
546 /*= Updater_t::defaultContext()*/)
547 -> std::pair<const_iterator, bool>
549 return put (std::make_unique<key_type> (std::move (key)), val, true, ctx);
554 * @brief Add an element to the map, or overwrite an existing one,
555 * with external locking.
556 * @param lock The lock object returned from lock().
557 * @param key The key of the new item to add.
558 * @param val The value of the new item to add.
559 * @param ctx Execution context.
561 * This will overwrite an existing entry.
562 * The first element in the returned pair is an iterator referencing
563 * the added item. The second is a flag that is true if a new element
568 auto CONCURRENTSTRMAP::insert_or_assign (const Lock_t& lock,
572 /*= Updater_t::defaultContext()*/)
573 -> std::pair<const_iterator, bool>
575 return put (lock, std::make_unique<key_type> (std::move (key)), val, true, ctx);
580 * @brief Add an element to the map.
581 * @param p The item to add.
582 * Should be a pair where first is the string key
583 * and second is the integer value.
584 * @param ctx Execution context.
586 * This will not overwrite an existing entry.
587 * The first element in the returned pair is an iterator referencing
588 * the added item. The second is a flag that is true if a new element
591 * For external locking, use emplace().
594 template <class PAIR>
596 auto CONCURRENTSTRMAP::insert (const PAIR& p,
597 const Context_t& ctx /*= Updater_t::defaultContext()*/)
598 -> std::pair<const_iterator, bool>
600 return emplace (p.first, p.second, ctx);
605 * @brief Add an element to the map.
606 * @param p The item to add.
607 * Should be a pair where first is the string key
608 * and second is the integer value.
609 * @param ctx Execution context.
611 * This will not overwrite an existing entry.
612 * The first element in the returned pair is an iterator referencing
613 * the added item. The second is a flag that is true if a new element
617 template <class PAIR>
619 auto CONCURRENTSTRMAP::insert (PAIR&& p,
620 const Context_t& ctx /*= Updater_t::defaultContext()*/)
621 -> std::pair<const_iterator, bool>
623 return emplace (std::move (p.first), p.second, ctx);
628 * @brief Insert a range of elements to the map.
629 * @param first Start of the range.
630 * @param last End of the range.
631 * @param ctx Execution context.
633 * The range should be a sequence of pairs where first is the string key
634 * and second is the integer value.
637 template <class InputIterator>
638 void CONCURRENTSTRMAP::insert (InputIterator first, InputIterator last,
639 const Context_t& ctx /*= Updater_t::defaultContext()*/)
642 if constexpr (std::is_rvalue_reference_v<typename InputIterator::reference>)
644 for (; first != last; ++first) {
645 emplace (lck, std::move (first->first), first->second, ctx);
649 for (; first != last; ++first) {
650 emplace (lck, first->first, first->second, ctx);
657 * @brief Increase the table capacity.
658 * @param capacity The new table capacity.
659 * @param ctx Execution context.
661 * No action will be taken if @c capacity is smaller
662 * than the current capacity.
666 void CONCURRENTSTRMAP::reserve (size_type capacity,
668 /*= Updater_t::defaultContext()*/)
670 return m_impl.reserve (capacity, ctx);
675 * @brief Increase the table capacity.
676 * @param capacity The new table capacity.
678 * No action will be taken if @c capacity is smaller
679 * than the current capacity.
683 void CONCURRENTSTRMAP::rehash (size_type capacity)
685 return reserve (capacity);
690 * @brief Called when this thread is no longer referencing anything
691 * from this container.
692 * @param ctx Execution context.
696 void CONCURRENTSTRMAP::quiescent (const Context_t& ctx)
698 return m_impl.quiescent (ctx);
703 * @brief Swap this container with another.
704 * @param other The container with which to swap.
706 * This will also call swap on the Updater object; hence, the Updater
707 * object must also support swap.
709 * This operation is NOT thread-safe. No other threads may be accessing
710 * either container during this operation.
713 void CONCURRENTSTRMAP::swap (ConcurrentStrMap& other)
715 m_impl.swap (other.m_impl);
720 * @brief Access the Updater instance.
723 auto CONCURRENTSTRMAP::updater() -> Updater_t&
725 return m_impl.updater();
730 * @brief Convert an underlying key value to a string pointer.
731 * @param val The underlying key value.
735 auto CONCURRENTSTRMAP::keyAsString (val_t val) -> const std::string*
737 return reinterpret_cast<std::string*> (val);
742 * @brief Convert a string pointer to an underlying key value.
743 * @param s The string pointer.
747 auto CONCURRENTSTRMAP::keyAsVal (const std::string* s) -> val_t
749 return reinterpret_cast<val_t> (s);
754 * @brief Convert an underlying mapped value to this type's mapped value.
755 * @param val The underlying mapped value.
759 auto CONCURRENTSTRMAP::mappedAsMapped (val_t val) -> mapped_type
761 return CxxUtils::detail::UIntConv<mapped_type>::uintToVal (val);
766 * @brief Convert this type's mapped value to an underlying mapped value.
767 * @param val The mapped value.
771 auto CONCURRENTSTRMAP::mappedAsVal (mapped_type val) -> val_t
773 return CxxUtils::detail::UIntConv<mapped_type>::valToUInt (val);
778 * @brief Do a lookup in the table.
779 * @param key The key to look up.
781 * Returns an iterator of the underlying map pointing at the found
785 auto CONCURRENTSTRMAP::get (const key_type& key) const
786 -> typename Impl_t::const_iterator
788 size_t hash = m_impl.hasher() (key);
789 return m_impl.get (keyAsVal(&key), hash);
794 * @brief Insert / overwrite an entry in the table.
795 * @param key The key of the new item to add.
796 * @param val The value of the new item to add.
797 * @param overwrite If true, allow overwriting an existing entry.
798 * @param ctx Execution context.
800 * The first element in the returned pair is an iterator referencing
801 * the added item. The second is a flag that is true if a new element
805 auto CONCURRENTSTRMAP::put (std::unique_ptr<key_type> key,
807 bool overwrite /*= true*/,
808 const Context_t& ctx /*= Updater_t::defaultContext()*/)
809 -> std::pair<const_iterator, bool>
811 size_t hash = m_impl.hasher() (*key);
812 auto [it, flag] = m_impl.put (keyAsVal(key.get()), hash,
815 if (flag) (void)key.release();
816 return std::make_pair (const_iterator (it), flag);
821 * @brief Insert / overwrite an entry in the table, with external locking.
822 * @param lock The lock object returned from lock().
823 * @param key The key of the new item to add.
824 * @param val The value of the new item to add.
825 * @param overwrite If true, allow overwriting an existing entry.
826 * @param ctx Execution context.
828 * The first element in the returned pair is an iterator referencing
829 * the added item. The second is a flag that is true if a new element
833 auto CONCURRENTSTRMAP::put (const Lock_t& lock,
834 std::unique_ptr<key_type> key,
836 bool overwrite /*= true*/,
837 const Context_t& ctx /*= Updater_t::defaultContext()*/)
838 -> std::pair<const_iterator, bool>
840 size_t hash = m_impl.hasher() (*key);
841 auto [it, flag] = m_impl.put (lock,
842 keyAsVal(key.get()), hash,
845 if (flag) (void)key.release();
846 return std::make_pair (const_iterator (it), flag);
851 * @brief Hash function from the underlying representation type.
855 auto CONCURRENTSTRMAP::Hasher::operator() (const val_t p) const -> size_t
857 return m_hash (*keyAsString(p));
862 * @brief Hash function from a std::string.
866 auto CONCURRENTSTRMAP::Hasher::operator() (const std::string& s) const -> size_t
873 * @brief Compare two keys (as the underlying representation type) for equality.
877 auto CONCURRENTSTRMAP::Matcher::operator() (const val_t a, const val_t b) const -> bool
879 // First test if the keys (pointers) themselves are equal.
880 if (a == b) return true;
881 // Otherwise, need to test the strings to which they point.
882 return *keyAsString(a) == *keyAsString(b);
886 #undef T_CONCURRENTSTRMAP
887 #undef CONCURRENTSTRMAP
890 } // namespace CxxUtils