2  * Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration.
 
    5  * @file CxxUtils/ConcurrentPtrSet.icc
 
    6  * @author scott snyder <snyder@bnl.gov>
 
    8  * @brief A set of pointers, alowing concurrent, lockless reads.
 
   15 #define T_CONCURRENTPTRSET template <class VALUE, template <class> class UPDATER> \
 
   16   requires (detail::IsUpdater<UPDATER>)
 
   19 #define CONCURRENTPTRSET ConcurrentPtrSet<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 CONCURRENTPTRSET::ConcurrentPtrSet (Updater_t&& updater,
 
   32                                     size_type capacity /*= 64*/,
 
   34                                       /* = Updater_t::defaultContext()*/) 
 
   35   : m_impl (std::move (updater),
 
   45  * @brief Constructor from another set.
 
   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 CONCURRENTPTRSET::ConcurrentPtrSet (const ConcurrentPtrSet& other,
 
   57                                     size_type capacity /*= 64*/,
 
   59                                       /*= Updater_t::defaultContext()*/)
 
   60   : m_impl (std::move (updater),
 
   66   for (const auto p : other) {
 
   73  * @brief Constructor from a range.
 
   74  * @param f Start iterator for the range.
 
   75  * @param l End iterator for the range.
 
   76  * @param updater Object used to manage memory
 
   77  *                (see comments at the start of the class).
 
   78  * @param capacity The initial table capacity of the new table.
 
   79  *                 (Will be rounded up to a power of two.)
 
   80  * @param ctx Execution context.
 
   83 template <class InputIterator>
 
   84 CONCURRENTPTRSET::ConcurrentPtrSet (InputIterator f,
 
   87                                     size_t capacity /*= 64*/,
 
   89                                       /*= Updater_t::defaultContext()*/)
 
   90   : m_impl (std::move (updater),
 
   98     this->put (lck, *f, ctx);
 
  107 CONCURRENTPTRSET::~ConcurrentPtrSet()
 
  113  * @brief Return the number of items currently in the set.
 
  117 auto CONCURRENTPTRSET::size() const -> size_type
 
  119   return m_impl.size();
 
  124  * @brief Test if the set is currently empty.
 
  128 bool CONCURRENTPTRSET::empty() const
 
  130   return !m_impl.size();
 
  135  * @brief Return the current size (capacity) of the hash table.
 
  139 auto CONCURRENTPTRSET::capacity() const -> size_t
 
  141   return m_impl.capacity();
 
  146  * @brief Constructor.
 
  147  * @param it Iterator of the underlying table.
 
  151 CONCURRENTPTRSET::const_iterator::const_iterator (typename Impl_t::const_iterator it)
 
  158  * @brief Test if this iterator is valid.
 
  160  * This should be the same as testing for != end().
 
  164 auto CONCURRENTPTRSET::const_iterator::valid() const -> bool
 
  166   return m_impl.valid();
 
  171  * @brief iterator_facade requirement: Increment the iterator.
 
  175 auto CONCURRENTPTRSET::const_iterator::increment() -> void
 
  182  * @brief iterator_facade requirement: Decrement the iterator.
 
  186 auto CONCURRENTPTRSET::const_iterator::decrement() -> void
 
  193  * @brief iterator_facade requirement: Dereference the iterator.
 
  197 auto CONCURRENTPTRSET::const_iterator::dereference() const
 
  200   return keyAsPtr (m_impl.key());
 
  205  * @brief iterator_facade requirement: Equality test.
 
  209 auto CONCURRENTPTRSET::const_iterator::equal (const const_iterator& other) const
 
  212   return !(m_impl != other.m_impl);
 
  217  * @brief Return an iterator range covering the entire set.
 
  220 auto CONCURRENTPTRSET::range() const -> const_iterator_range
 
  222   auto [begin, end] = m_impl.range();
 
  223   return const_iterator_range (begin, end);
 
  228  * @brief Iterator at the start of the set.
 
  232 auto CONCURRENTPTRSET::begin() const -> const_iterator
 
  234   return const_iterator (m_impl.begin());
 
  239  * @brief Iterator at the end of the set.
 
  243 auto CONCURRENTPTRSET::end() const -> const_iterator
 
  245   return const_iterator (m_impl.end());
 
  250  * @brief Iterator at the start of the set.
 
  254 auto CONCURRENTPTRSET::cbegin() const -> const_iterator
 
  261  * @brief Iterator at the end of the set.
 
  265 auto CONCURRENTPTRSET::cend() const -> const_iterator
 
  272  * @brief Test if a key is in the container.
 
  273  * @param key The key to test.
 
  277 bool CONCURRENTPTRSET::contains (const const_key_type key) const
 
  279   return get(key).valid();
 
  284  * @brief Return the number of times a given key is in the container.
 
  285  * @param key The key to test.
 
  287  * Returns either 0 or 1, depending on whether or not the key is in the set.
 
  291 auto CONCURRENTPTRSET::count (const const_key_type key) const -> size_type
 
  293   return contains (key) ? 1 : 0;
 
  298  * @brief Look up an element in the set.
 
  299  * @param key The element to find.
 
  301  * Returns either an iterator referencing the found element or end().
 
  305 auto CONCURRENTPTRSET::find (const const_key_type key) const -> const_iterator
 
  307   return const_iterator (this->get (key));
 
  312  * @brief Return a range of iterators with entries matching @c key.
 
  313  * @param key The element to find.
 
  315  * As keys are unique in this container, this is either a single-element
 
  316  * range, or both iterators are equal to end().
 
  319 auto CONCURRENTPTRSET::equal_range (const const_key_type key) const
 
  320   -> std::pair<const_iterator, const_iterator>
 
  322   const_iterator i1 = find (key);
 
  323   const_iterator i2 = i1;
 
  327   return std::make_pair (i1, i2);
 
  332  * @brief Take a lock on the container.
 
  334  * Take a lock on the container.
 
  335  * The lock can then be passed to insertion methods, allowing to factor out
 
  336  * the locking inside loops.  The lock will be released when the
 
  337  * lock object is destroyed.
 
  341 auto CONCURRENTPTRSET::lock() -> Lock_t
 
  343   return m_impl.lock();
 
  348  * @brief Add an element to the set.
 
  349  * @param key The key of the new item to add.
 
  350  * @param ctx Execution context.
 
  352  * This will not overwrite an existing entry.
 
  353  * The first element in the returned pair is an iterator referencing
 
  354  * the added item.  The second is a flag that is true if a new element
 
  359 auto CONCURRENTPTRSET::emplace (const key_type key,
 
  361                                   /*= Updater_t::defaultContext()*/)
 
  362   -> std::pair<const_iterator, bool>
 
  364   return put (key, ctx);
 
  369  * @brief Add an element to the set, with external locking.
 
  370  * @param lock The lock object returned from lock().
 
  371  * @param key The key of the new item to add.
 
  372  * @param ctx Execution context.
 
  374  * This will not overwrite an existing entry.
 
  375  * The first element in the returned pair is an iterator referencing
 
  376  * the added item.  The second is a flag that is true if a new element
 
  381 auto CONCURRENTPTRSET::emplace (const Lock_t& lock,
 
  384                                   /*= Updater_t::defaultContext()*/)
 
  385   -> std::pair<const_iterator, bool>
 
  387   return put (lock, key, ctx);
 
  392  * @brief Add an element to the set.
 
  393  * @param p The item to add.
 
  395  * This will not overwrite an existing entry.
 
  396  * The first element in the returned pair is an iterator referencing
 
  397  * the added item.  The second is a flag that is true if a new element
 
  400  * This is the same as emplace().  Use that for the case of external locking.
 
  404 auto CONCURRENTPTRSET::insert (const key_type p)
 
  405   -> std::pair<const_iterator, bool>
 
  412  * @brief Insert a range of elements to the set.
 
  413  * @param first Start of the range.
 
  414  * @param last End of the range.
 
  417 template <class InputIterator>
 
  418 void CONCURRENTPTRSET::insert (InputIterator first, InputIterator last)
 
  421   const Context_t& ctx = Updater_t::defaultContext();
 
  422   for (; first != last; ++first) {
 
  423     emplace (lck, *first, ctx);
 
  429  * @brief Increase the table capacity.
 
  430  * @param capacity The new table capacity.
 
  431  * @param ctx Execution context.
 
  433  * No action will be taken if @c capacity is smaller
 
  434  * than the current capacity.
 
  438 void CONCURRENTPTRSET::reserve (size_type capacity,
 
  440                                   /*= Updater_t::defaultContext()*/)
 
  442   return m_impl.reserve (capacity, ctx);
 
  447  * @brief Increase the table capacity.
 
  448  * @param capacity The new table capacity.
 
  450  * No action will be taken if @c capacity is smaller
 
  451  * than the current capacity.
 
  455 void CONCURRENTPTRSET::rehash (size_type capacity)
 
  457   return reserve (capacity);
 
  462  * @brief Erase the table and change the capacity.
 
  463  * @param capacity The new table capacity.
 
  464  * @param ctx Execution context.
 
  468 void CONCURRENTPTRSET::clear (size_t capacity,
 
  469                               const Context_t& ctx /*= Updater_t::defaultContext()*/)
 
  471   m_impl.clear (capacity, ctx);
 
  476  * @brief Erase the table (don't change the capacity).
 
  477  * @param ctx Execution context.
 
  481 void CONCURRENTPTRSET::clear (const Context_t& ctx /*= Updater_t::defaultContext()*/)
 
  488  * @brief Called when this thread is no longer referencing anything
 
  489  *        from this container.
 
  490  * @param ctx Execution context.
 
  494 void CONCURRENTPTRSET::quiescent (const Context_t& ctx)
 
  496   return m_impl.quiescent (ctx);
 
  501  * @brief Swap this container with another.
 
  502  * @param other The container with which to swap.
 
  504  * This will also call swap on the Updater object; hence, the Updater
 
  505  * object must also support swap.
 
  507  * This operation is NOT thread-safe.  No other threads may be accessing
 
  508  * either container during this operation.
 
  511 void CONCURRENTPTRSET::swap (ConcurrentPtrSet& other)
 
  513   m_impl.swap (other.m_impl);
 
  518  * @brief Access the Updater instance.
 
  521 auto CONCURRENTPTRSET::updater() -> Updater_t&
 
  523   return m_impl.updater();
 
  528  * @brief Convert an underlying key value to a pointer.
 
  529  * @param val The underlying key value.
 
  533 auto CONCURRENTPTRSET::keyAsPtr (val_t val) -> key_type
 
  535   return reinterpret_cast<key_type> (val);
 
  540  * @brief Convert a pointer to an underlying key value.
 
  541  * @param val The pointer.
 
  545 auto CONCURRENTPTRSET::keyAsVal (const const_key_type p) -> val_t
 
  547   return reinterpret_cast<val_t> (p);
 
  552  * @brief Do a lookup in the table.
 
  553  * @param key The key to look up.
 
  555  * Returns an iterator of the underlying map pointing at the found
 
  559 auto CONCURRENTPTRSET::get (const const_key_type key) const
 
  560   -> typename Impl_t::const_iterator
 
  562   size_t hash = m_impl.hasher() (key);
 
  563   return m_impl.get (keyAsVal(key), hash);
 
  568  * @brief Insert an entry in the table.
 
  569  * @param key The new item to add.
 
  570  * @param ctx Execution context.
 
  572  * The first element in the returned pair is an iterator referencing
 
  573  * the added item.  The second is a flag that is true if a new element
 
  577 auto CONCURRENTPTRSET::put (const key_type key,
 
  578                             const Context_t& ctx /*= Updater_t::defaultContext()*/)
 
  579   -> std::pair<const_iterator, bool>
 
  581   size_t hash = m_impl.hasher() (key);
 
  582   auto [it, flag] = m_impl.put (keyAsVal(key), hash,
 
  585   return std::make_pair (const_iterator (it), flag);
 
  590  * @brief Insert an entry in the table, with external locking.
 
  591  * @param lock The lock object returned from lock().
 
  592  * @param key The new item to add.
 
  593  * @param ctx Execution context.
 
  595  * The first element in the returned pair is an iterator referencing
 
  596  * the added item.  The second is a flag that is true if a new element
 
  600 auto CONCURRENTPTRSET::put (const Lock_t& lock,
 
  602                             const Context_t& ctx /*= Updater_t::defaultContext()*/)
 
  603   -> std::pair<const_iterator, bool>
 
  605   size_t hash = m_impl.hasher() (key);
 
  606   auto [it, flag] = m_impl.put (lock,
 
  610   return std::make_pair (const_iterator (it), flag);
 
  615  * @brief Hash function from the underlying representation type.
 
  619 auto CONCURRENTPTRSET::Hasher::operator() (const val_t p) const -> size_t
 
  621   return m_hash (keyAsPtr(p));
 
  626  * @brief Hash function from a pointer.
 
  630 auto CONCURRENTPTRSET::Hasher::operator() (const const_key_type p) const -> size_t
 
  637  * @brief Compare two keys (as the underlying representation type) for equality.
 
  641 auto CONCURRENTPTRSET::Matcher::operator() (const val_t a, const val_t b) const -> bool
 
  647 #undef T_CONCURRENTPTRSET
 
  648 #undef CONCURRENTPTRSET
 
  651 } // namespace CxxUtils