ATLAS Offline Software
ConcurrentPtrSet.icc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration.
3  */
4 /**
5  * @file CxxUtils/ConcurrentPtrSet.icc
6  * @author scott snyder <snyder@bnl.gov>
7  * @date Jul, 2022
8  * @brief A set of pointers, alowing concurrent, lockless reads.
9  */
10 
11 
12 namespace CxxUtils {
13 
14 
15 #define T_CONCURRENTPTRSET template <class VALUE, template <class> class UPDATER> \
16  ATH_REQUIRES (detail::IsUpdater<UPDATER>)
17 
18 
19 #define CONCURRENTPTRSET ConcurrentPtrSet<VALUE, UPDATER>
20 
21 
22 /**
23  * @brief Constructor.
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.
29  */
30 T_CONCURRENTPTRSET
31 CONCURRENTPTRSET::ConcurrentPtrSet (Updater_t&& updater,
32  size_type capacity /*= 64*/,
33  const Context_t& ctx
34  /* = Updater_t::defaultContext()*/)
35  : m_impl (std::move (updater),
36  capacity,
37  Hasher(),
38  Matcher(),
39  ctx)
40 {
41 }
42 
43 
44 /**
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.
51  *
52  * (Not really a copy constructor since we need to pass @c updater.)
53  */
54 T_CONCURRENTPTRSET
55 CONCURRENTPTRSET::ConcurrentPtrSet (const ConcurrentPtrSet& other,
56  Updater_t&& updater,
57  size_type capacity /*= 64*/,
58  const Context_t& ctx
59  /*= Updater_t::defaultContext()*/)
60  : m_impl (std::move (updater),
61  capacity,
62  Hasher(),
63  Matcher(),
64  ctx)
65 {
66  for (const auto p : other) {
67  this->put (p, ctx);
68  }
69 }
70 
71 
72 /**
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.
81  */
82 T_CONCURRENTPTRSET
83 template <class InputIterator>
84 CONCURRENTPTRSET::ConcurrentPtrSet (InputIterator f,
85  InputIterator l,
86  Updater_t&& updater,
87  size_t capacity /*= 64*/,
88  const Context_t& ctx
89  /*= Updater_t::defaultContext()*/)
90  : m_impl (std::move (updater),
91  capacity,
92  Hasher(),
93  Matcher(),
94  ctx)
95 {
96  Lock_t lck = lock();
97  for (; f != l; ++f) {
98  this->put (lck, *f, ctx);
99  }
100 }
101 
102 
103 /**
104  * @brief Destructor.
105  */
106 T_CONCURRENTPTRSET
107 CONCURRENTPTRSET::~ConcurrentPtrSet()
108 {
109 }
110 
111 
112 /**
113  * @brief Return the number of items currently in the set.
114  */
115 T_CONCURRENTPTRSET
116 inline
117 auto CONCURRENTPTRSET::size() const -> size_type
118 {
119  return m_impl.size();
120 }
121 
122 
123 /**
124  * @brief Test if the set is currently empty.
125  */
126 T_CONCURRENTPTRSET
127 inline
128 bool CONCURRENTPTRSET::empty() const
129 {
130  return !m_impl.size();
131 }
132 
133 
134 /**
135  * @brief Return the current size (capacity) of the hash table.
136  */
137 T_CONCURRENTPTRSET
138 inline
139 auto CONCURRENTPTRSET::capacity() const -> size_t
140 {
141  return m_impl.capacity();
142 }
143 
144 
145 /**
146  * @brief Constructor.
147  * @param it Iterator of the underlying table.
148  */
149 T_CONCURRENTPTRSET
150 inline
151 CONCURRENTPTRSET::const_iterator::const_iterator (typename Impl_t::const_iterator it)
152  : m_impl (it)
153 {
154 }
155 
156 
157 /**
158  * @brief Test if this iterator is valid.
159  *
160  * This should be the same as testing for != end().
161  */
162 T_CONCURRENTPTRSET
163 inline
164 auto CONCURRENTPTRSET::const_iterator::valid() const -> bool
165 {
166  return m_impl.valid();
167 }
168 
169 
170 /**
171  * @brief iterator_facade requirement: Increment the iterator.
172  */
173 T_CONCURRENTPTRSET
174 inline
175 auto CONCURRENTPTRSET::const_iterator::increment() -> void
176 {
177  m_impl.next();
178 }
179 
180 
181 /**
182  * @brief iterator_facade requirement: Decrement the iterator.
183  */
184 T_CONCURRENTPTRSET
185 inline
186 auto CONCURRENTPTRSET::const_iterator::decrement() -> void
187 {
188  m_impl.prev();
189 }
190 
191 
192 /**
193  * @brief iterator_facade requirement: Dereference the iterator.
194  */
195 T_CONCURRENTPTRSET
196 inline
197 auto CONCURRENTPTRSET::const_iterator::dereference() const
198  -> key_type
199 {
200  return keyAsPtr (m_impl.key());
201 }
202 
203 
204 /**
205  * @brief iterator_facade requirement: Equality test.
206  */
207 T_CONCURRENTPTRSET
208 inline
209 auto CONCURRENTPTRSET::const_iterator::equal (const const_iterator& other) const
210  -> bool
211 {
212  return !(m_impl != other.m_impl);
213 }
214 
215 
216 /**
217  * @brief Return an iterator range covering the entire set.
218  */
219 T_CONCURRENTPTRSET
220 auto CONCURRENTPTRSET::range() const -> const_iterator_range
221 {
222  auto [begin, end] = m_impl.range();
223  return const_iterator_range (begin, end);
224 }
225 
226 
227 /**
228  * @brief Iterator at the start of the set.
229  */
230 T_CONCURRENTPTRSET
231 inline
232 auto CONCURRENTPTRSET::begin() const -> const_iterator
233 {
234  return const_iterator (m_impl.begin());
235 }
236 
237 
238 /**
239  * @brief Iterator at the end of the set.
240  */
241 T_CONCURRENTPTRSET
242 inline
243 auto CONCURRENTPTRSET::end() const -> const_iterator
244 {
245  return const_iterator (m_impl.end());
246 }
247 
248 
249 /**
250  * @brief Iterator at the start of the set.
251  */
252 T_CONCURRENTPTRSET
253 inline
254 auto CONCURRENTPTRSET::cbegin() const -> const_iterator
255 {
256  return begin();
257 }
258 
259 
260 /**
261  * @brief Iterator at the end of the set.
262  */
263 T_CONCURRENTPTRSET
264 inline
265 auto CONCURRENTPTRSET::cend() const -> const_iterator
266 {
267  return end();
268 }
269 
270 
271 /**
272  * @brief Test if a key is in the container.
273  * @param key The key to test.
274  */
275 T_CONCURRENTPTRSET
276 inline
277 bool CONCURRENTPTRSET::contains (const const_key_type key) const
278 {
279  return get(key).valid();
280 }
281 
282 
283 /**
284  * @brief Return the number of times a given key is in the container.
285  * @param key The key to test.
286  *
287  * Returns either 0 or 1, depending on whether or not the key is in the set.
288  */
289 T_CONCURRENTPTRSET
290 inline
291 auto CONCURRENTPTRSET::count (const const_key_type key) const -> size_type
292 {
293  return contains (key) ? 1 : 0;
294 }
295 
296 
297 /**
298  * @brief Look up an element in the set.
299  * @param key The element to find.
300  *
301  * Returns either an iterator referencing the found element or end().
302  */
303 T_CONCURRENTPTRSET
304 inline
305 auto CONCURRENTPTRSET::find (const const_key_type key) const -> const_iterator
306 {
307  return const_iterator (this->get (key));
308 }
309 
310 
311 /**
312  * @brief Return a range of iterators with entries matching @c key.
313  * @param key The element to find.
314  *
315  * As keys are unique in this container, this is either a single-element
316  * range, or both iterators are equal to end().
317  */
318 T_CONCURRENTPTRSET
319 auto CONCURRENTPTRSET::equal_range (const const_key_type key) const
320  -> std::pair<const_iterator, const_iterator>
321 {
322  const_iterator i1 = find (key);
323  const_iterator i2 = i1;
324  if (i2.valid()) {
325  ++i2;
326  }
327  return std::make_pair (i1, i2);
328 }
329 
330 
331 /**
332  * @brief Take a lock on the container.
333  *
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.
338  */
339 T_CONCURRENTPTRSET
340 inline
341 auto CONCURRENTPTRSET::lock() -> Lock_t
342 {
343  return m_impl.lock();
344 }
345 
346 
347 /**
348  * @brief Add an element to the set.
349  * @param key The key of the new item to add.
350  * @param ctx Execution context.
351  *
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
355  * was added.
356  */
357 T_CONCURRENTPTRSET
358 inline
359 auto CONCURRENTPTRSET::emplace (const key_type key,
360  const Context_t& ctx
361  /*= Updater_t::defaultContext()*/)
362  -> std::pair<const_iterator, bool>
363 {
364  return put (key, ctx);
365 }
366 
367 
368 /**
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.
373  *
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
377  * was added.
378  */
379 T_CONCURRENTPTRSET
380 inline
381 auto CONCURRENTPTRSET::emplace (const Lock_t& lock,
382  const key_type key,
383  const Context_t& ctx
384  /*= Updater_t::defaultContext()*/)
385  -> std::pair<const_iterator, bool>
386 {
387  return put (lock, key, ctx);
388 }
389 
390 
391 /**
392  * @brief Add an element to the set.
393  * @param p The item to add.
394  *
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
398  * was added.
399  *
400  * This is the same as emplace(). Use that for the case of external locking.
401  */
402 T_CONCURRENTPTRSET
403 inline
404 auto CONCURRENTPTRSET::insert (const key_type p)
405  -> std::pair<const_iterator, bool>
406 {
407  return emplace (p);
408 }
409 
410 
411 /**
412  * @brief Insert a range of elements to the set.
413  * @param first Start of the range.
414  * @param last End of the range.
415  */
416 T_CONCURRENTPTRSET
417 template <class InputIterator>
418 void CONCURRENTPTRSET::insert (InputIterator first, InputIterator last)
419 {
420  Lock_t lck = lock();
421  const Context_t& ctx = Updater_t::defaultContext();
422  for (; first != last; ++first) {
423  emplace (lck, *first, ctx);
424  }
425 }
426 
427 
428 /**
429  * @brief Increase the table capacity.
430  * @param capacity The new table capacity.
431  * @param ctx Execution context.
432  *
433  * No action will be taken if @c capacity is smaller
434  * than the current capacity.
435  */
436 T_CONCURRENTPTRSET
437 inline
438 void CONCURRENTPTRSET::reserve (size_type capacity,
439  const Context_t& ctx
440  /*= Updater_t::defaultContext()*/)
441 {
442  return m_impl.reserve (capacity, ctx);
443 }
444 
445 
446 /**
447  * @brief Increase the table capacity.
448  * @param capacity The new table capacity.
449  *
450  * No action will be taken if @c capacity is smaller
451  * than the current capacity.
452  */
453 T_CONCURRENTPTRSET
454 inline
455 void CONCURRENTPTRSET::rehash (size_type capacity)
456 {
457  return reserve (capacity);
458 }
459 
460 
461 /**
462  * @brief Erase the table and change the capacity.
463  * @param capacity The new table capacity.
464  * @param ctx Execution context.
465  */
466 T_CONCURRENTPTRSET
467 inline
468 void CONCURRENTPTRSET::clear (size_t capacity,
469  const Context_t& ctx /*= Updater_t::defaultContext()*/)
470 {
471  m_impl.clear (capacity, ctx);
472 }
473 
474 
475 /**
476  * @brief Erase the table (don't change the capacity).
477  * @param ctx Execution context.
478  */
479 T_CONCURRENTPTRSET
480 inline
481 void CONCURRENTPTRSET::clear (const Context_t& ctx /*= Updater_t::defaultContext()*/)
482 {
483  m_impl.clear (ctx);
484 }
485 
486 
487 /**
488  * @brief Called when this thread is no longer referencing anything
489  * from this container.
490  * @param ctx Execution context.
491  */
492 T_CONCURRENTPTRSET
493 inline
494 void CONCURRENTPTRSET::quiescent (const Context_t& ctx)
495 {
496  return m_impl.quiescent (ctx);
497 }
498 
499 
500 /**
501  * @brief Swap this container with another.
502  * @param other The container with which to swap.
503  *
504  * This will also call swap on the Updater object; hence, the Updater
505  * object must also support swap.
506  *
507  * This operation is NOT thread-safe. No other threads may be accessing
508  * either container during this operation.
509  */
510 T_CONCURRENTPTRSET
511 void CONCURRENTPTRSET::swap (ConcurrentPtrSet& other)
512 {
513  m_impl.swap (other.m_impl);
514 }
515 
516 
517 /**
518  * @brief Access the Updater instance.
519  */
520 T_CONCURRENTPTRSET
521 auto CONCURRENTPTRSET::updater() -> Updater_t&
522 {
523  return m_impl.updater();
524 }
525 
526 
527 /**
528  * @brief Convert an underlying key value to a pointer.
529  * @param val The underlying key value.
530  */
531 T_CONCURRENTPTRSET
532 inline
533 auto CONCURRENTPTRSET::keyAsPtr (val_t val) -> key_type
534 {
535  return reinterpret_cast<key_type> (val);
536 }
537 
538 
539 /**
540  * @brief Convert a pointer to an underlying key value.
541  * @param val The pointer.
542  */
543 T_CONCURRENTPTRSET
544 inline
545 auto CONCURRENTPTRSET::keyAsVal (const const_key_type p) -> val_t
546 {
547  return reinterpret_cast<val_t> (p);
548 }
549 
550 
551 /**
552  * @brief Do a lookup in the table.
553  * @param key The key to look up.
554  *
555  * Returns an iterator of the underlying map pointing at the found
556  * entry or end();
557  */
558 T_CONCURRENTPTRSET
559 auto CONCURRENTPTRSET::get (const const_key_type key) const
560  -> typename Impl_t::const_iterator
561 {
562  size_t hash = m_impl.hasher() (key);
563  return m_impl.get (keyAsVal(key), hash);
564 }
565 
566 
567 /**
568  * @brief Insert an entry in the table.
569  * @param key The new item to add.
570  * @param ctx Execution context.
571  *
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
574  * was added.
575  */
576 T_CONCURRENTPTRSET
577 auto CONCURRENTPTRSET::put (const key_type key,
578  const Context_t& ctx /*= Updater_t::defaultContext()*/)
579  -> std::pair<const_iterator, bool>
580 {
581  size_t hash = m_impl.hasher() (key);
582  auto [it, flag] = m_impl.put (keyAsVal(key), hash,
583  0,
584  false, ctx);
585  return std::make_pair (const_iterator (it), flag);
586 }
587 
588 
589 /**
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.
594  *
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
597  * was added.
598  */
599 T_CONCURRENTPTRSET
600 auto CONCURRENTPTRSET::put (const Lock_t& lock,
601  const key_type key,
602  const Context_t& ctx /*= Updater_t::defaultContext()*/)
603  -> std::pair<const_iterator, bool>
604 {
605  size_t hash = m_impl.hasher() (key);
606  auto [it, flag] = m_impl.put (lock,
607  keyAsVal(key), hash,
608  0,
609  false, ctx);
610  return std::make_pair (const_iterator (it), flag);
611 }
612 
613 
614 /**
615  * @brief Hash function from the underlying representation type.
616  */
617 T_CONCURRENTPTRSET
618 inline
619 auto CONCURRENTPTRSET::Hasher::operator() (const val_t p) const -> size_t
620 {
621  return m_hash (keyAsPtr(p));
622 }
623 
624 
625 /**
626  * @brief Hash function from a pointer.
627  */
628 T_CONCURRENTPTRSET
629 inline
630 auto CONCURRENTPTRSET::Hasher::operator() (const const_key_type p) const -> size_t
631 {
632  return m_hash (p);
633 }
634 
635 
636 /**
637  * @brief Compare two keys (as the underlying representation type) for equality.
638  */
639 T_CONCURRENTPTRSET
640 inline
641 auto CONCURRENTPTRSET::Matcher::operator() (const val_t a, const val_t b) const -> bool
642 {
643  return a == b;
644 }
645 
646 
647 #undef T_CONCURRENTPTRSET
648 #undef CONCURRENTPTRSET
649 
650 
651 } // namespace CxxUtils