ATLAS Offline Software
Loading...
Searching...
No Matches
ConcurrentPtrSet.icc
Go to the documentation of this file.
1/*
2 * Copyright (C) 2002-2025 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
12namespace CxxUtils {
13
14
15#define T_CONCURRENTPTRSET template <class VALUE, template <class> class UPDATER> \
16 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 */
30T_CONCURRENTPTRSET
31CONCURRENTPTRSET::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 */
54T_CONCURRENTPTRSET
55CONCURRENTPTRSET::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 */
82T_CONCURRENTPTRSET
83template <class InputIterator>
84CONCURRENTPTRSET::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 */
106T_CONCURRENTPTRSET
107CONCURRENTPTRSET::~ConcurrentPtrSet()
108{
109}
110
111
112/**
113 * @brief Return the number of items currently in the set.
114 */
115T_CONCURRENTPTRSET
116inline
117auto 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 */
126T_CONCURRENTPTRSET
127inline
128bool 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 */
137T_CONCURRENTPTRSET
138inline
139auto 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 */
149T_CONCURRENTPTRSET
150inline
151CONCURRENTPTRSET::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 */
162T_CONCURRENTPTRSET
163inline
164auto 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 */
173T_CONCURRENTPTRSET
174inline
175auto CONCURRENTPTRSET::const_iterator::increment() -> void
176{
177 m_impl.next();
178}
179
180
181/**
182 * @brief iterator_facade requirement: Decrement the iterator.
183 */
184T_CONCURRENTPTRSET
185inline
186auto CONCURRENTPTRSET::const_iterator::decrement() -> void
187{
188 m_impl.prev();
189}
190
191
192/**
193 * @brief iterator_facade requirement: Dereference the iterator.
194 */
195T_CONCURRENTPTRSET
196inline
197auto 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 */
207T_CONCURRENTPTRSET
208inline
209auto 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 */
219T_CONCURRENTPTRSET
220auto 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 */
230T_CONCURRENTPTRSET
231inline
232auto 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 */
241T_CONCURRENTPTRSET
242inline
243auto 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 */
252T_CONCURRENTPTRSET
253inline
254auto CONCURRENTPTRSET::cbegin() const -> const_iterator
255{
256 return begin();
257}
258
259
260/**
261 * @brief Iterator at the end of the set.
262 */
263T_CONCURRENTPTRSET
264inline
265auto 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 */
275T_CONCURRENTPTRSET
276inline
277bool 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 */
289T_CONCURRENTPTRSET
290inline
291auto 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 */
303T_CONCURRENTPTRSET
304inline
305auto 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 */
318T_CONCURRENTPTRSET
319auto 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 */
339T_CONCURRENTPTRSET
340inline
341auto 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 */
357T_CONCURRENTPTRSET
358inline
359auto 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 */
379T_CONCURRENTPTRSET
380inline
381auto 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 */
402T_CONCURRENTPTRSET
403inline
404auto 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 */
416T_CONCURRENTPTRSET
417template <class InputIterator>
418void 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 */
436T_CONCURRENTPTRSET
437inline
438void 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 */
453T_CONCURRENTPTRSET
454inline
455void 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 */
466T_CONCURRENTPTRSET
467inline
468void 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 */
479T_CONCURRENTPTRSET
480inline
481void 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 */
492T_CONCURRENTPTRSET
493inline
494void 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 */
510T_CONCURRENTPTRSET
511void CONCURRENTPTRSET::swap (ConcurrentPtrSet& other)
512{
513 m_impl.swap (other.m_impl);
514}
515
516
517/**
518 * @brief Access the Updater instance.
519 */
520T_CONCURRENTPTRSET
521auto 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 */
531T_CONCURRENTPTRSET
532inline
533auto 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 */
543T_CONCURRENTPTRSET
544inline
545auto 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 */
558T_CONCURRENTPTRSET
559auto 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 */
576T_CONCURRENTPTRSET
577auto 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 */
599T_CONCURRENTPTRSET
600auto 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 */
617T_CONCURRENTPTRSET
618inline
619auto 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 */
628T_CONCURRENTPTRSET
629inline
630auto 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 */
639T_CONCURRENTPTRSET
640inline
641auto 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