2 * Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration.
5 * @file CxxUtils/ConcurrentMap.icc
6 * @author scott snyder <snyder@bnl.gov>
8 * @brief Hash map from integers/pointers allowing concurrent, lockless reads.
15 #define T_CONCURRENTMAP template <class KEY, class VALUE, \
16 template <class> class UPDATER, \
19 detail::ConcurrentHashmapVal_t NULLVAL, \
20 detail::ConcurrentHashmapVal_t TOMBSTONE> \
21 ATH_REQUIRES (detail::IsConcurrentHashmapPayload<KEY> && \
22 detail::IsConcurrentHashmapPayload<VALUE> && \
23 detail::IsUpdater<UPDATER> && \
24 detail::IsHash<HASHER, KEY> && \
25 detail::IsBinaryPredicate<MATCHER, KEY>)
28 #define CONCURRENTMAP ConcurrentMap<KEY, VALUE, UPDATER, \
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.
42 CONCURRENTMAP::ConcurrentMap (Updater_t&& updater,
43 size_type capacity /*= 64*/,
45 /* = Updater_t::defaultContext()*/)
46 : m_impl (std::move (updater),
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.
63 * (Not really a copy constructor since we need to pass @c updater.)
66 CONCURRENTMAP::ConcurrentMap (const ConcurrentMap& other,
68 size_type capacity /*= 64*/,
70 /*= Updater_t::defaultContext()*/)
71 : m_impl (std::move (updater),
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);
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.
94 * Constructor from a range of pairs.
97 template <class InputIterator>
98 CONCURRENTMAP::ConcurrentMap (InputIterator f,
101 size_t capacity /*= 64*/,
103 /*= Updater_t::defaultContext()*/)
104 : m_impl (std::move (updater),
111 for (; f != l; ++f) {
112 this->put (lck, f->first, f->second, true, ctx);
118 * @brief Return the number of items currently in the map.
122 auto CONCURRENTMAP::size() const -> size_type
124 return m_impl.size();
129 * @brief Test if the map is currently empty.
133 bool CONCURRENTMAP::empty() const
135 return !m_impl.size();
140 * @brief Return the current size (capacity) of the hash table.
144 auto CONCURRENTMAP::capacity() const -> size_t
146 return m_impl.capacity();
151 * @brief The number of erased elements in the current table.
155 auto CONCURRENTMAP::erased() const -> size_t
157 return m_impl.erased();
162 * @brief Constructor.
163 * @param it Iterator of the underlying table.
167 CONCURRENTMAP::const_iterator::const_iterator (typename Impl_t::const_iterator it)
174 * @brief Test if this iterator is valid.
176 * This should be the same as testing for != end().
180 auto CONCURRENTMAP::const_iterator::valid() const -> bool
182 return m_impl.valid();
187 * @brief iterator_facade requirement: Increment the iterator.
191 auto CONCURRENTMAP::const_iterator::increment() -> void
198 * @brief iterator_facade requirement: Decrement the iterator.
202 auto CONCURRENTMAP::const_iterator::decrement() -> void
209 * @brief iterator_facade requirement: Equality test.
213 auto CONCURRENTMAP::const_iterator::equal (const const_iterator& other) const
216 return !(m_impl != other.m_impl);
221 * @brief iterator_facade requirement: Dereference the iterator.
225 auto CONCURRENTMAP::const_iterator::dereference() const
226 -> const const_iterator_value
228 return const_iterator_value (keyAsKey (m_impl.key()),
229 mappedAsMapped (m_impl.value()));
234 * @brief Return an iterator range covering the entire map.
237 auto CONCURRENTMAP::range() const -> const_iterator_range
239 auto [begin, end] = m_impl.range();
240 return const_iterator_range (begin, end);
245 * @brief Iterator at the start of the map.
249 auto CONCURRENTMAP::begin() const -> const_iterator
251 return const_iterator (m_impl.begin());
256 * @brief Iterator at the end of the map.
260 auto CONCURRENTMAP::end() const -> const_iterator
262 return const_iterator (m_impl.end());
267 * @brief Iterator at the start of the map.
271 auto CONCURRENTMAP::cbegin() const -> const_iterator
278 * @brief Iterator at the end of the map.
282 auto CONCURRENTMAP::cend() const -> const_iterator
289 * @brief Test if a key is in the container.
290 * @param key The key to test.
294 bool CONCURRENTMAP::contains (key_type key) const
296 return get(key).valid();
301 * @brief Return the number of times a given key is in the container.
302 * @param key The key to test.
304 * Returns either 0 or 1, depending on whether or not the key is in the map.
308 auto CONCURRENTMAP::count (key_type key) const -> size_type
310 return contains (key) ? 1 : 0;
315 * @brief Look up an element in the map.
316 * @param key The element to find.
318 * Returns either an iterator referencing the found element or end().
322 auto CONCURRENTMAP::find (key_type key) const -> const_iterator
324 return const_iterator (this->get (key));
329 * @brief Look up an element in the map.
330 * @param key The element to find.
332 * Returns the value associated with the key.
333 * Throws @c std::out_of_range if the key does not exist in the map.
336 auto CONCURRENTMAP::at (key_type key) const -> mapped_type
338 typename Impl_t::const_iterator it = this->get (key);
340 throw std::out_of_range ("ConcurrentMap::at");
342 return mappedAsMapped (it.value());
347 * @brief Return a range of iterators with entries matching @c key.
348 * @param key The element to find.
350 * As keys are unique in this container, this is either a single-element
351 * range, or both iterators are equal to end().
354 auto CONCURRENTMAP::equal_range (key_type key) const
355 -> std::pair<const_iterator, const_iterator>
357 const_iterator i1 = find (key);
358 const_iterator i2 = i1;
362 return std::make_pair (i1, i2);
367 * @brief Take a lock on the container.
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.
376 auto CONCURRENTMAP::lock() -> Lock_t
378 return m_impl.lock();
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.
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
395 auto CONCURRENTMAP::emplace (key_type key,
398 /*= Updater_t::defaultContext()*/)
399 -> std::pair<const_iterator, bool>
401 return put (key, val, false, ctx);
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.
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
418 auto CONCURRENTMAP::emplace (const Lock_t& lock,
422 /*= Updater_t::defaultContext()*/)
423 -> std::pair<const_iterator, bool>
425 return put (lock, key, val, false, ctx);
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.
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
442 auto CONCURRENTMAP::insert_or_assign (key_type key,
445 /*= Updater_t::defaultContext()*/)
446 -> std::pair<const_iterator, bool>
448 return put (key, val, true, ctx);
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.
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
467 auto CONCURRENTMAP::insert_or_assign (const Lock_t& lock,
471 /*= Updater_t::defaultContext()*/)
472 -> std::pair<const_iterator, bool>
474 return put (lock, key, val, true, ctx);
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.
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
489 * For external locking, use emplace().
492 template <class PAIR>
494 auto CONCURRENTMAP::insert (const PAIR& p)
495 -> std::pair<const_iterator, bool>
497 return emplace (p.first, p.second);
502 * @brief Insert a range of elements to the map.
503 * @param first Start of the range.
504 * @param last End of the range.
506 * The range should be a sequence of pairs where first is the string key
507 * and second is the integer value.
510 template <class InputIterator>
511 void CONCURRENTMAP::insert (InputIterator first, InputIterator last)
514 const Context_t& ctx = Updater_t::defaultContext();
515 for (; first != last; ++first) {
516 emplace (lck, first->first, first->second, ctx);
522 * @brief Erase an entry from the table.
523 * @param key The key to erase.
525 * Mark the corresponding entry as deleted.
526 * Return true on success, false on failure (key not found).
528 * The tombstone value must be different from the null value.
530 * This may cause the key type returned by an iterator to change
531 * asynchronously to the tombstone value.
535 bool CONCURRENTMAP::erase (key_type key)
537 val_t kval = keyAsVal (key);
538 size_t hash = m_impl.hasher() (kval);
539 return m_impl.erase (kval, hash);
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.
548 * Mark the corresponding entry as deleted.
549 * Return true on success, false on failure (key not found).
551 * The tombstone value must be different from the null value.
553 * This may cause the key type returned by an iterator to change
554 * asynchronously to the tombstone value.
558 bool CONCURRENTMAP::erase (const Lock_t& lock, key_type key)
560 val_t kval = keyAsVal (key);
561 size_t hash = m_impl.hasher() (kval);
562 return m_impl.erase (lock, kval, hash);
567 * @brief Increase the table capacity.
568 * @param capacity The new table capacity.
569 * @param ctx Execution context.
571 * No action will be taken if @c capacity is smaller
572 * than the current capacity.
576 void CONCURRENTMAP::reserve (size_type capacity,
578 /*= Updater_t::defaultContext()*/)
580 return m_impl.reserve (capacity, ctx);
585 * @brief Increase the table capacity.
586 * @param capacity The new table capacity.
588 * No action will be taken if @c capacity is smaller
589 * than the current capacity.
593 void CONCURRENTMAP::rehash (size_type capacity)
595 return reserve (capacity);
600 * @brief Erase the table and change the capacity.
601 * @param capacity The new table capacity.
602 * @param ctx Execution context.
606 void CONCURRENTMAP::clear (size_t capacity,
607 const Context_t& ctx /*= Updater_t::defaultContext()*/)
609 m_impl.clear (capacity, ctx);
614 * @brief Erase the table (don't change the capacity).
615 * @param ctx Execution context.
619 void CONCURRENTMAP::clear (const Context_t& ctx /*= Updater_t::defaultContext()*/)
626 * @brief Erase the table in-place.
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
635 void CONCURRENTMAP::forceClear()
642 * @brief Called when this thread is no longer referencing anything
643 * from this container.
644 * @param ctx Execution context.
648 void CONCURRENTMAP::quiescent (const Context_t& ctx)
650 return m_impl.quiescent (ctx);
655 * @brief Swap this container with another.
656 * @param other The container with which to swap.
658 * This will also call swap on the Updater object; hence, the Updater
659 * object must also support swap. The Hasher and Matcher instances
662 * This operation is NOT thread-safe. No other threads may be accessing
663 * either container during this operation.
666 void CONCURRENTMAP::swap (ConcurrentMap& other)
668 return m_impl.swap (other.m_impl);
673 * @brief Access the Updater instance.
677 auto CONCURRENTMAP::updater() -> Updater_t&
679 return m_impl.updater();
684 * @brief Convert an underlying key value to this type's key value.
685 * @param val The underlying key value.
689 auto CONCURRENTMAP::keyAsKey (val_t val) -> key_type
691 return CxxUtils::detail::UIntConv<key_type>::uintToVal (val);
696 * @brief Convert this type's key value to an underlying key value.
701 auto CONCURRENTMAP::keyAsVal (key_type k) -> val_t
703 return CxxUtils::detail::UIntConv<key_type>::valToUInt (k);
708 * @brief Convert an underlying mapped value to this type's mapped value.
709 * @param val The underlying mapped value.
713 auto CONCURRENTMAP::mappedAsMapped (val_t val) -> mapped_type
715 return CxxUtils::detail::UIntConv<mapped_type>::uintToVal (val);
720 * @brief Convert this type's mapped value to an underlying mapped value.
721 * @param val The mapped value.
725 auto CONCURRENTMAP::mappedAsVal (mapped_type val) -> val_t
727 return CxxUtils::detail::UIntConv<mapped_type>::valToUInt (val);
732 * @brief Do a lookup in the table.
733 * @param key The key to look up.
735 * Returns an iterator of the underlying map pointing at the found
739 auto CONCURRENTMAP::get (key_type key) const
740 -> typename Impl_t::const_iterator
742 val_t kval = keyAsVal (key);
743 size_t hash = m_impl.hasher() (kval);
744 return m_impl.get (kval, hash);
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.
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
760 auto CONCURRENTMAP::put (key_type key,
762 bool overwrite /*= true*/,
763 const Context_t& ctx /*= Updater_t::defaultContext()*/)
764 -> std::pair<const_iterator, bool>
766 val_t kval = keyAsVal (key);
767 size_t hash = m_impl.hasher() (kval);
768 auto [it, flag] = m_impl.put (kval, hash,
771 return std::make_pair (const_iterator (it), flag);
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.
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
788 auto CONCURRENTMAP::put (const Lock_t& lock,
791 bool overwrite /*= true*/,
792 const Context_t& ctx /*= Updater_t::defaultContext()*/)
793 -> std::pair<const_iterator, bool>
795 val_t kval = keyAsVal (key);
796 size_t hash = m_impl.hasher() (kval);
797 auto [it, flag] = m_impl.put (lock, kval, hash,
800 return std::make_pair (const_iterator (it), flag);
804 #undef T_CONCURRENTMAP
808 } // namespace CxxUtils