ATLAS Offline Software
ConcurrentBitset.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/ConcurrentBitset.icc
6  * @author scott snyder <snyder@bnl.gov>
7  * @date Nov, 2017
8  * @brief Variable-sized bitset allowing (mostly) concurrent access.
9  */
10 
11 
12 #include "CxxUtils/AthUnlikelyMacros.h"
13 
14 
15 namespace CxxUtils {
16 
17 
18 //*********************************************************************************
19 // Constructors, destructors, assignment.
20 
21 
22 /**
23  * @brief The number of bits that this container can hold.
24  */
25 inline
26 ConcurrentBitset::bit_t ConcurrentBitset::capacity() const
27 {
28  return (*m_impl).nbits();
29 }
30 
31 
32 /**
33  * @brief Count the number of 1 bits in the set.
34  */
35 inline
36 ConcurrentBitset::bit_t ConcurrentBitset::count() const
37 {
38  return (*m_impl).count();
39 }
40 
41 
42 /**
43  * @brief Count the number of 1 bits in the set.
44  *
45  * Note: If you regard this like a std::bitset, you would expect this to return
46  * the number of bits that the set can hold, while if you regard this like
47  * a set<bit_t>, then you would expect this to return the number of 1 bits.
48  * We follow the latter here.
49  */
50 inline
51 ConcurrentBitset::bit_t ConcurrentBitset::size() const
52 {
53  return (*m_impl).count();
54 }
55 
56 
57 /**
58  * @brief Test to see if a bit is set.
59  * @param bit Number of the bit to test.
60  * @return true if the bit is set; false otherwise.
61  *
62  * Returns false if @c bit is beyond the end of the set.
63  */
64 inline
65 bool ConcurrentBitset::test (bit_t bit) const
66 {
67  return (*m_impl).test (bit);
68 }
69 
70 /**
71  * @brief Test to see if a bit is set.
72  * @param bit Number of the bit to test.
73  * @return 1 if the bit is set; 0 otherwise.
74  *
75  * Returns 0 if @c bit is beyond the end of the set.
76  */
77 inline
78 size_t ConcurrentBitset::count (bit_t bit) const
79 {
80  return test (bit);
81 }
82 
83 
84 /**
85  * @brief Return true if there are no 1 bits in the set.
86  */
87 inline
88 bool ConcurrentBitset::empty() const
89 {
90  return none();
91 }
92 
93 
94 /**
95  * @brief Return true if there are no 1 bits in the set.
96  */
97 inline
98 bool ConcurrentBitset::none() const
99 {
100  return (*m_impl).none();
101 }
102 
103 
104 /**
105  * @brief Return true if all bits in the set are 1.
106  */
107 inline
108 bool ConcurrentBitset::all() const
109 {
110  return (*m_impl).all();
111 }
112 
113 
114 /**
115  * @brief Return true if there are any 1 bits in the set.
116  */
117 inline
118 bool ConcurrentBitset::any() const
119 {
120  return !none();
121 }
122 
123 
124 //*********************************************************************************
125 // Single-bit manipulation.
126 
127 
128 /**
129  * @brief Turn on one bit.
130  * @param bit The bit to turn on.
131  *
132  * Does nothing if @c bit beyond the end of the set.
133  */
134 inline
135 ConcurrentBitset& ConcurrentBitset::set (bit_t bit)
136 {
137  (*m_impl).set (bit);
138  return *this;
139 }
140 
141 
142 /**
143  * @brief Turn off one bit.
144  * @param bit The bit to turn off.
145  *
146  * Does nothing if @c bit beyond the end of the set.
147  */
148 inline
149 ConcurrentBitset& ConcurrentBitset::reset (bit_t bit)
150 {
151  (*m_impl).reset (bit);
152  return *this;
153 }
154 
155 
156 /**
157  * @brief Turn off one bit.
158  * @param bit The bit to turn off.
159  *
160  * Does nothing if @c bit beyond the end of the set.
161  */
162 inline
163 ConcurrentBitset& ConcurrentBitset::erase (bit_t bit)
164 {
165  (*m_impl).reset (bit);
166  return *this;
167 }
168 
169 
170 /**
171  * @brief Flip the value of one bit.
172  * @param bit The bit to turn flip.
173  *
174  * Does nothing if @c bit beyond the end of the set.
175  */
176 inline
177 ConcurrentBitset& ConcurrentBitset::flip (bit_t bit)
178 {
179  (*m_impl).flip (bit);
180  return *this;
181 }
182 
183 
184 /**
185  * @brief Set the value of one bit.
186  * @param bit The bit to turn set.
187  * @param val The value to which to set it.
188  *
189  * Does nothing if @c bit beyond the end of the set.
190  */
191 inline
192 ConcurrentBitset& ConcurrentBitset::set (bit_t bit, bool val)
193 {
194  if (val) {
195  (*m_impl).set (bit);
196  }
197  else {
198  (*m_impl).reset (bit);
199  }
200  return *this;
201 }
202 
203 
204 //*********************************************************************************
205 // Set operations.
206 
207 
208 /**
209  * @brief Clear all bits in the set.
210  *
211  * This operation is not necessarily atomic; a simultaneous read may be able
212  * to see the operation partially done.
213  */
214 inline
215 ConcurrentBitset& ConcurrentBitset::clear()
216 {
217  (*m_impl).clear();
218  return *this;
219 }
220 
221 
222 /**
223  * @brief Clear all bits in the set.
224  *
225  * This operation is not necessarily atomic; a simultaneous read may be able
226  * to see the operation partially done.
227  */
228 inline
229 ConcurrentBitset& ConcurrentBitset::reset()
230 {
231  (*m_impl).clear();
232  return *this;
233 }
234 
235 
236 /**
237  * @brief Turn on all bits in the set.
238  *
239  * This operation is not necessarily atomic; a simultaneous read may be able
240  * to see the operation partially done.
241  */
242 inline
243 ConcurrentBitset& ConcurrentBitset::set()
244 {
245  (*m_impl).set();
246  return *this;
247 }
248 
249 /**
250  * @brief Flip the state of all bits in the set.
251  *
252  * This operation is not necessarily atomic; a simultaneous read may be able
253  * to see the operation partially done.
254  */
255 inline
256 ConcurrentBitset& ConcurrentBitset::flip()
257 {
258  (*m_impl).flip();
259  return *this;
260 }
261 
262 
263 /**
264  * @brief AND this set with another set.
265  * @param other The other set.
266  *
267  * This operation is not necessarily atomic; a simultaneous read may be able
268  * to see the operation partially done.
269  */
270 inline
271 ConcurrentBitset& ConcurrentBitset::operator&= (const ConcurrentBitset& other)
272 {
273  (*m_impl).operate ([] (std::atomic<Block_t>& a, Block_t v) { a &= v; },
274  *other.m_impl);
275  return *this;
276 }
277 
278 
279 /**
280  * @brief OR this set with another set.
281  * @param other The other set.
282  *
283  * This operation is not necessarily atomic; a simultaneous read may be able
284  * to see the operation partially done.
285  */
286 inline
287 ConcurrentBitset& ConcurrentBitset::operator|= (const ConcurrentBitset& other)
288 {
289  (*m_impl).operate ([] (std::atomic<Block_t>& a, Block_t v) { a |= v; },
290  *other.m_impl);
291  return *this;
292 }
293 
294 
295 /**
296  * @brief XOR this set with another set.
297  * @param other The other set.
298  *
299  * This operation is not necessarily atomic; a simultaneous read may be able
300  * to see the operation partially done.
301  */
302 inline
303 ConcurrentBitset& ConcurrentBitset::operator^= (const ConcurrentBitset& other)
304 {
305  (*m_impl).operate ([] (std::atomic<Block_t>& a, Block_t v) { a ^= v; },
306  *other.m_impl);
307  return *this;
308 }
309 
310 
311 /**
312  * @brief Subtract another set from this set.
313  * @param other The other set.
314  *
315  * This is the same as (*this) &= ~other;
316  *
317  * This operation is not necessarily atomic; a simultaneous read may be able
318  * to see the operation partially done.
319  */
320 inline
321 ConcurrentBitset& ConcurrentBitset::operator-= (const ConcurrentBitset& other)
322 {
323  (*m_impl).operate ([] (std::atomic<Block_t>& a, Block_t v) { a &= ~v; },
324  *other.m_impl);
325  return *this;
326 }
327 
328 
329 /**
330  * @brief Return a new set that is the complement of this set.
331  */
332 inline
333 ConcurrentBitset ConcurrentBitset::operator~() const
334 {
335  ConcurrentBitset b = *this;
336  (*b.m_impl).flip();
337  return b;
338 }
339 
340 
341 //*********************************************************************************
342 // Comparison.
343 
344 
345 /**
346  * @brief Test two sets for equality.
347  * @param other The other set to test.
348  */
349 inline
350 bool ConcurrentBitset::operator== (const ConcurrentBitset& other) const
351 {
352  return (*m_impl) == (*other.m_impl);
353 }
354 
355 
356 /**
357  * @brief Test two sets for inequality.
358  * @param other The other set to test.
359  */
360 inline
361 bool ConcurrentBitset::operator!= (const ConcurrentBitset& other) const
362 {
363  return ! ((*m_impl) == (*other.m_impl));
364 }
365 
366 
367 //*********************************************************************************
368 // Insert.
369 
370 
371 /**
372  * @brief Set a bit to 1. Expand the set if needed.
373  * @param bit Number of the bit to set.
374  * @param new_nbits Hint for new size of set, if it needs to be expanded.
375  *
376  * If @c bit is past the end of the container, then the container will be
377  * expanded as needed.
378  *
379  * This operation is incompatible with any other simultaneous writes
380  * to the same set (reads are ok).
381  */
382 inline
383 ConcurrentBitset& ConcurrentBitset::insert (bit_t bit, bit_t new_nbits /*= 0*/)
384 {
385  if (bit >= (*m_impl).nbits()) {
386  if (new_nbits > bit)
387  ;
388  else if (bit < 64)
389  new_nbits = 128;
390  else
391  new_nbits = (bit * 2 + BLOCKSIZE-1) & ~(BLOCKSIZE-1);
392  expand (new_nbits);
393  }
394  set (bit);
395  return *this;
396 }
397 
398 
399 /**
400  * @brief Set several bits to 1. Expand the set if needed.
401  * @param beg Start of range of bits to set.
402  * @param end End of range of bits to set.
403  * @param new_nbits Hint for new size of set, if it needs to be expanded.
404  *
405  * The iteration range should be over something convertible to @c bit_t.
406  * If any bit is past the end of the container, then the container will be
407  * expanded as needed.
408  *
409  * This operation is incompatible with any other simultaneous writes
410  * to the same set (reads are ok).
411  *
412  * Example:
413  *@code
414  * std::vector<bit_t> bits { 4, 10, 12};
415  * CxxUtils::ConcurrentBitset bs = ...;
416  * bs.insert (bits.begin(), bits.end());
417  @endcode
418 */
419 template <class ITERATOR,
420  typename /*= typename std::enable_if<
421  std::is_base_of<
422  typename std::forward_iterator_tag,
423  typename std::iterator_traits<ITERATOR>::iterator_category
424  >::value>*/ >
425 inline
426 ConcurrentBitset&
427 ConcurrentBitset::insert (ITERATOR beg, ITERATOR end, bit_t new_nbits /*= 0*/)
428 {
429  for (; beg != end; ++beg) {
430  insert (*beg, new_nbits);
431  }
432  return *this;
433 }
434 
435 
436 /**
437  * @brief Set several bits to 1. Expand the set if needed.
438  * @param l List of bits to set.
439  * @param new_nbits Hint for new size of set, if it needs to be expanded.
440  *
441  * If any bit is past the end of the container, then the container will be
442  * expanded as needed.
443  *
444  * This operation is incompatible with any other simultaneous writes
445  * to the same set (reads are ok).
446  *
447  * Example:
448  *@code
449  * std::vector<bit_t> bits { 4, 10, 12};
450  * bs.insert ({4, 10, 12});
451  @endcode
452 */
453 inline
454 ConcurrentBitset&
455 ConcurrentBitset::insert (std::initializer_list<bit_t> l, bit_t new_nbits /*= 0*/)
456 {
457  auto max_it = std::max_element (l.begin(), l.end());
458  size_t bmax = max_it == l.end() ? 0 : *max_it + 1;
459  if (new_nbits > bmax) new_nbits = bmax;
460  expand (bmax);
461  for (size_t x : l) {
462  set (x);
463  }
464  return *this;
465 }
466 
467 
468 /**
469  * @brief Turn on bits listed in another set.
470  * @param other Set of bits to turn on.
471  *
472  * This is the same as @c operator|=, except that if the size of @c other is
473  * larger than this set, then this set will be expanded to match @c other.
474  *
475  * This operation is incompatible with any other simultaneous writes
476  * to the same set (reads are ok).
477  */
478 inline
479 ConcurrentBitset& ConcurrentBitset::insert (const ConcurrentBitset& other)
480 {
481  const Impl* otherImpl = other.m_impl;
482  expand (otherImpl->nbits());
483  (*m_impl).operate ([] (std::atomic<Block_t>& a, Block_t v) { a |= v; },
484  *otherImpl);
485  return *this;
486 }
487 
488 
489 //*********************************************************************************
490 // Array-like element access.
491 
492 
493 /**
494  * @brief Constructor.
495  * @param impl ConcurrentBitset implementation object.
496  * @param bit Bit number to which this reference refers.
497  */
498 inline
499 ConcurrentBitset::reference::reference (Impl& impl, bit_t bit)
500  : m_block (impl.block(bit)),
501  m_mask (1UL << (bit&MASK))
502 {
503 }
504 
505 
506 /**
507  * @brief Set the referenced bit to a given value.
508  * @param val Value to which to set the referenced bit.
509  */
510 inline
511 ConcurrentBitset::reference&
512 ConcurrentBitset::reference::operator= (bool val) noexcept
513 {
514  if (val)
515  *m_block |= m_mask;
516  else
517  *m_block &= ~m_mask;
518  return *this;
519 }
520 
521 
522 /**
523  * @brief Copy the value of another referenced bit.
524  * @param r The other reference.
525  *
526  * To allow:
527  *@code
528  * ConcurrentBitset& bs1 = ...;
529  * ConcurrentBitset& bs2 = ...;
530  * bs1[1] = bs2[1];
531  @endcode
532 */
533 inline
534 ConcurrentBitset::reference&
535 ConcurrentBitset::reference::operator= (const reference& r) noexcept
536 {
537  if (this != &r) {
538  *this = static_cast<bool> (r);
539  }
540  return *this;
541 }
542 
543 
544 /**
545  * @brief Return the value of the referenced bit.
546  */
547 inline
548 ConcurrentBitset::reference::operator bool() const noexcept
549 {
550  return (*m_block & m_mask) != 0;
551 }
552 
553 
554 /**
555  * @brief Return the complement of the value of the referenced bit.
556  */
557 inline
558 bool ConcurrentBitset::reference::operator~() const noexcept
559 {
560  return (*m_block & m_mask) == 0;
561 }
562 
563 
564 /**
565  * @brief Invert the referenced bit.
566  */
567 inline
568 ConcurrentBitset::reference&
569 ConcurrentBitset::reference::flip() noexcept
570 {
571  *m_block ^= m_mask;
572  return *this;
573 }
574 
575 
576 /**
577  * @brief Return the value of one bit.
578  * @param bit The number of the bit to test.
579  */
580 inline
581 bool ConcurrentBitset::operator[] (bit_t bit) const
582 {
583  return (*m_impl).test (bit);
584 }
585 
586 
587 /**
588  * @brief Return a reference to one bit.
589  * @param bit The number of the bit to reference.
590  *
591  * The reference will be invalidated by calls to @c insert() or @c operator=.
592  * Effects are undefined if @c bit is past the end of the set.
593  */
594 inline
595 ConcurrentBitset::reference ConcurrentBitset::operator[] (bit_t bit)
596 {
597  return reference (*m_impl, bit);
598 }
599 
600 
601 //*********************************************************************************
602 // Iterator operations.
603 
604 
605 /**
606  * @brief Constructor.
607  * @param cache Cached block at the current iteration point, shifted such
608  * that bit number @c bit has just been shifted off the right.
609  * @param bit Bit number the at which the iterator is currently pointing.
610  * @param data Pointer to the block at which the iterator is currently pointing.
611  * @param end One past the last block of the iteration range.
612  */
613 inline
614 ConcurrentBitset::const_iterator::const_iterator (Block_t cache,
615  bit_t bit,
616  const std::atomic<Block_t>* data,
617  const std::atomic<Block_t>* end)
618  : m_cache (cache), m_bit (bit), m_data(data), m_end (end)
619 {
620 }
621 
622 
623 /**
624  * @brief Return the bit number which the iterator is currently referencing.
625  */
626 inline
627 ConcurrentBitset::bit_t
628 ConcurrentBitset::const_iterator::operator*() const
629 {
630  return m_bit;
631 }
632 
633 
634 /**
635  * @brief Advance the iterator to the next set bit.
636  */
637 inline
638 ConcurrentBitset::const_iterator&
639 ConcurrentBitset::const_iterator::operator++()
640 {
641  // Are there any more bits set in this block?
642  Block_t cache = m_cache;
643  if (ATH_LIKELY (cache != 0)) {
644  // Yes --- find the first set bit.
645  // Shift until that bit just falls off the right and adjust
646  // the current position.
647  // We know that @c b will be less than @c BLOCKSIZE
648  // (avoiding undefined behavior), since at least one bit must have
649  // already been shifted off of the cache.
650  int b = std::countr_zero (cache)+1;
651  m_bit += b;
652  m_cache = cache >> b;
653  }
654  else {
655  // No, move to the next block.
656  // Bit number at the start of the next block.
657  unsigned int bit = (m_bit + BLOCKSIZE) & ~MASK;
658 
659  // Pointers to the next block, and the end of iteration.
660  const std::atomic<Block_t>* data = m_data + 1;
661  const std::atomic<Block_t>* end = m_end;
662 
663  // Iterate until we find a block with at least one bit set.
664  while (data < end) {
665  // Read the current block; test to see if there are any set bits.
666  cache = *data;
667  if (ATH_UNLIKELY (cache != 0)) {
668  // Found a block with at least one bit set. Which is the first?
669  int b = std::countr_zero (cache);
670 
671  // Adjust the current position.
672  m_bit = bit + b;
673  m_data = data;
674 
675  // Shift the bit found off of cache.
676  // Need to do it in two steps, because @c b might be @c BLOCKSIZE-1
677  // (and shifting by @c BLOCKSIZE is undefined).
678  m_cache = (cache>>1) >> b;
679  return *this;
680  }
681 
682  // Move to the next block.
683  ++data;
684  bit += BLOCKSIZE;
685  }
686 
687  // Reached the end without finding any more bits set.
688  // Mark that we hit the end.
689  m_bit = ~static_cast<bit_t>(0);
690  }
691 
692  return *this;
693 }
694 
695 
696 /**
697  * @brief Advance the iterator to the next set bit (postincrement).
698  */
699 inline
700 ConcurrentBitset::const_iterator
701 ConcurrentBitset::const_iterator::operator++(int)
702 {
703  const_iterator tmp = *this;
704  ++*this;
705  return tmp;
706 }
707 
708 
709 /**
710  * @brief Compare two iterators.
711  * @param other The other iterator to compare.
712  */
713 inline
714 bool ConcurrentBitset::const_iterator::operator== (const const_iterator& other) const
715 {
716  return m_bit == other.m_bit;
717 }
718 
719 
720 /**
721  * @brief Compare two iterators.
722  * @param other The other iterator to compare.
723  */
724 inline
725 bool ConcurrentBitset::const_iterator::operator!= (const const_iterator& other) const
726 {
727  return m_bit != other.m_bit;
728 }
729 
730 
731 /**
732  * @brief Return a begin iterator.
733  */
734 inline
735 ConcurrentBitset::const_iterator
736 ConcurrentBitset::begin() const
737 {
738  return (*m_impl).begin();
739 }
740 
741 
742 /**
743  * @brief Return an end iterator.
744  */
745 inline
746 ConcurrentBitset::const_iterator
747 ConcurrentBitset::end() const
748 {
749  return (*m_impl).end();
750 }
751 
752 
753 /**
754  * @brief If bit @c bit is set, return an iterator pointing to it.
755  * Otherwise, return an end iterator.
756  * @param bit Bit number to test.
757  */
758 inline
759 ConcurrentBitset::const_iterator
760 ConcurrentBitset::find (bit_t bit) const
761 {
762  return (*m_impl).find(bit);
763 }
764 
765 
766 //*********************************************************************************
767 // Implementation.
768 
769 
770 /**
771  * @brief Allocate an Impl structure.
772  * @param sz Size of an Impl structure.
773  * @param nbits Number of bits to allocate.
774  *
775  * The storage for the bits follows contiguously after the Impl structure itself
776  * (to save a memory reference); hence, we use a custom allocator.
777  */
778 inline
779 void* ConcurrentBitset::Impl::operator new (size_t /*sz*/,
780  ConcurrentBitset::bit_t nbits)
781 {
782  bit_t nblocks = nBlocks (nbits);
783  // The Impl structure contains one Block_t at the end.
784  return malloc (sizeof(Impl) + (nblocks-1)*sizeof(Block_t));
785 }
786 
787 
788 /*
789  * @brief Free an Impl structure.
790  * @param p Pointer to the object to free.
791  */
792 inline
793 void ConcurrentBitset::Impl::operator delete (void* p)
794 {
795  free (p);
796 }
797 
798 
799 /**
800  * @brief Find number of blocks needed to hold a given number of bits.
801  * @param nbits The number of bits.
802  */
803 inline
804 ConcurrentBitset::bit_t
805 ConcurrentBitset::nBlocks (bit_t nbits)
806 {
807  if (nbits == 0) return 1;
808  return (nbits+BLOCKSIZE-1) / BLOCKSIZE;
809 }
810 
811 
812 /**
813  * @brief Create a new, uninitialized implementation object.
814  * @param nbits Number of bits to allocate.
815  *
816  * This will allocate memory for the Impl object,
817  * but will does not run the constructor.
818  */
819 inline
820 ConcurrentBitset::Impl*
821 ConcurrentBitset::newImpl (bit_t nbits)
822 {
823  bit_t nblocks = nBlocks (nbits);
824  // The Impl structure contains one Block_t at the end.
825  return reinterpret_cast<Impl*>(malloc (sizeof(Impl) + (nblocks-1)*sizeof(Block_t)));
826 }
827 
828 
829 /**
830  * @brief Expand the container.
831  * @param new_nbits The desired new size of the container.
832  */
833 inline
834 void ConcurrentBitset::expand (bit_t new_nbits)
835 {
836  // Check the size of the container.
837  // Call the out-of-line portion if we actually need to expand.
838  if (new_nbits > (*m_impl).nbits()) {
839  expandOol (new_nbits);
840  }
841 }
842 
843 
844 /**
845  * @brief Constructor.
846  * @param nbits Number of bits in the set.
847  */
848 inline
849 ConcurrentBitset::Impl::Impl (bit_t nbits)
850  : m_nbits (nbits),
851  m_nblocks (nBlocks (nbits))
852 {
853  // Start with all bits 0.
854  clear();
855 }
856 
857 
858 /**
859  * @brief Copy constructor.
860  * @brief Other object to copy.
861  * @brief Number of bits to use for this container.
862  *
863  * If @c nbits is smaller than the size of @c other, then the size of @c other
864  * will be used instead.
865  */
866 inline
867 ConcurrentBitset::Impl::Impl (const Impl& other, bit_t nbits /*= 0*/)
868  : m_nbits (std::max (other.m_nbits, nbits)),
869  m_nblocks ((m_nbits+BLOCKSIZE-1) / BLOCKSIZE),
870  m_hwm (static_cast<size_t> (other.m_hwm))
871 {
872  // Copy, then clear the remainder.
873  // We don't care about the relative ordering, so to this with relaxed
874  // memory ordering, and add a barrier at the end.
875  for (bit_t i=0; i < other.m_nblocks; i++) {
876  m_data[i].store (other.m_data[i].load(std::memory_order_relaxed),
877  std::memory_order_relaxed);
878  }
879  for (bit_t i=other.m_nblocks; i < m_nblocks; i++) {
880  m_data[i].store (0, std::memory_order_relaxed);
881  }
882  std::atomic_thread_fence (std::memory_order_seq_cst);
883 }
884 
885 
886 /**
887  * @brief Copy from another instance.
888  * @param other Object from which to copy.
889  *
890  * This does not change the size of the container.
891  * If This container is larger than @c other, then the remainder will be
892  * filled with zeros. If @c other is larger than this container, then the
893  * remainder will be ignored.
894  */
895 inline
896 void ConcurrentBitset::Impl::assign (const Impl& other)
897 {
898  // Copy, then clear the remainder.
899  // We don't care about the relative ordering, so do this with relaxed
900  // memory ordering, and add a barrier at the end.
901  bit_t ncopy = std::min (m_nblocks, other.m_nblocks);
902  for (bit_t i=0; i < ncopy; i++) {
903  m_data[i].store (other.m_data[i].load(std::memory_order_relaxed),
904  std::memory_order_relaxed);
905  }
906  for (bit_t i=ncopy; i < m_nblocks; i++) {
907  m_data[i].store (0, std::memory_order_relaxed);
908  }
909  std::atomic_thread_fence (std::memory_order_seq_cst);
910 
911  // Copy hwm last. It can only increase, so as long as we're after the barrier,
912  // we should get something that's correct.
913  m_hwm = static_cast<bit_t> (other.m_hwm);
914 }
915 
916 
917 /**
918  * @brief Return the number of bits in the set.
919  */
920 inline
921 ConcurrentBitset::bit_t ConcurrentBitset::Impl::nbits() const
922 {
923  return m_nbits;
924 }
925 
926 
927 /**
928  * @brief Test to see if a bit is set.
929  * @param bit Number of the bit to test.
930  * @return true if the bit is set; false otherwise.
931  *
932  * Returns false if @c bit is beyond the end of the set.
933  */
934 inline
935 bool ConcurrentBitset::Impl::test (bit_t bit) const
936 {
937  if (bit >= m_nbits) return false;
938  bit_t pos = bit / BLOCKSIZE;
939  return (m_data[pos] & (1UL<<(bit&MASK))) != 0;
940 }
941 
942 
943 /**
944  * @brief Return a pointer to the block containing @c bit.
945  * @param bit Desired bit number.
946  *
947  * Returns nullptr if @c bit is past the end of the set.
948  */
949 inline
950 std::atomic<ConcurrentBitset::Block_t>*
951 ConcurrentBitset::Impl::block (bit_t bit)
952 {
953  if (bit >= m_nbits) return nullptr;
954  bit_t pos = bit / BLOCKSIZE;
955  return &m_data[pos];
956 }
957 
958 
959 /**
960  * @brief Turn on one bit.
961  * @param bit The bit to turn on.
962  *
963  * Does nothing if @c bit beyond the end of the set.
964  */
965 inline
966 void ConcurrentBitset::Impl::set (bit_t bit)
967 {
968  if (bit >= m_nbits) return;
969  bit_t pos = bit / BLOCKSIZE;
970  // Update HWM if pos is larger.
971  CxxUtils::atomic_fetch_max (&m_hwm, pos);
972  m_data[pos] |= 1UL<<(bit&MASK);
973 }
974 
975 
976 /**
977  * @brief Turn off one bit.
978  * @param bit The bit to turn off.
979  *
980  * Does nothing if @c bit beyond the end of the set.
981  */
982 inline
983 void ConcurrentBitset::Impl::reset (bit_t bit)
984 {
985  if (bit >= m_nbits) return;
986  bit_t pos = bit / BLOCKSIZE;
987  m_data[pos] &= ~(1UL<<(bit&MASK));
988 }
989 
990 
991 /**
992  * @brief Flip the value of one bit.
993  * @param bit The bit to turn flip.
994  *
995  * Does nothing if @c bit beyond the end of the set.
996  */
997 inline
998 void ConcurrentBitset::Impl::flip (bit_t bit)
999 {
1000  if (bit >= m_nbits) return;
1001  size_t pos = bit / BLOCKSIZE;
1002  // Update HWM if pos is larger.
1003  CxxUtils::atomic_fetch_max (&m_hwm, pos);
1004  m_data[pos] ^= 1UL<<(bit&MASK);
1005 }
1006 
1007 
1008 /**
1009  * @brief Clear all bits in the set.
1010  *
1011  * This operation is not necessarily atomic; a simultaneous read may be able
1012  * to see the operation partially done.
1013  */
1014 inline
1015 void ConcurrentBitset::Impl::clear()
1016 {
1017  for (bit_t i=0; i<m_nblocks; i++) {
1018  m_data[i].store (0, std::memory_order_relaxed);
1019  }
1020  std::atomic_thread_fence (std::memory_order_seq_cst);
1021  m_hwm = 0;
1022 }
1023 
1024 
1025 /**
1026  * @brief Apply a binary operation.
1027  * @param op Operation to apply.
1028  * @param other Second set for the operation.
1029  *
1030  * Each block B in this set is replaced by B OP OTHER, where OTHER is
1031  * the corresponding block in the other container. (If this set
1032  * is larger than @c other, then the trailing blocks will be 0.)
1033  */
1034 inline
1035 void ConcurrentBitset::Impl::operate (operator_t op, const Impl& other)
1036 {
1037  bit_t nblocks = std::min (m_nblocks, other.m_nblocks);
1038  bit_t hwm = m_hwm;
1039  for (bit_t i = 0; i < nblocks; i++) {
1040  op (m_data[i], other.m_data[i]);
1041  if (m_data[i]) hwm = i;
1042  }
1043  for (bit_t i = nblocks; i < m_nblocks; i++) {
1044  op (m_data[i], static_cast<Block_t> (0));
1045  if (m_data[i]) hwm = i;
1046  }
1047  CxxUtils::atomic_fetch_max (&m_hwm, hwm);
1048 }
1049 
1050 
1051 /**
1052  * @brief Compare with another set.
1053  * @param other Other set with which to compare.
1054  */
1055 inline
1056 bool ConcurrentBitset::Impl::operator== (const Impl& other) const
1057 {
1058  bit_t ntest = std::min (m_nblocks, other.m_nblocks);
1059  for (bit_t i = 0; i < ntest; i++) {
1060  if (m_data[i] != other.m_data[i]) return false;
1061  }
1062 
1063  for (bit_t i = ntest; i < m_nblocks; i++) {
1064  if (m_data[i] != 0) return false;
1065  }
1066 
1067  for (bit_t i = ntest; i < other.m_nblocks; i++) {
1068  if (other.m_data[i] != 0) return false;
1069  }
1070 
1071  return true;
1072 }
1073 
1074 
1075 /**
1076  * @brief Return an iterator referencing the first 1 bit.
1077  */
1078 inline
1079 ConcurrentBitset::const_iterator
1080 ConcurrentBitset::Impl::begin() const
1081 {
1082  // Set the iterator to just before the start of the container.
1083  // Then use the increment operator to search for the first 1.
1084  bit_t offs = m_nblocks ? (static_cast<bit_t>(m_hwm) + 1) : 0;
1085  const_iterator it (0,
1086  static_cast<bit_t>(-BLOCKSIZE),
1087  &m_data[0] - 1,
1088  &m_data[0] + offs);
1089  ++it;
1090  return it;
1091 }
1092 
1093 
1094 /**
1095  * @brief Return the end iterator.
1096  */
1097 inline
1098 ConcurrentBitset::const_iterator
1099 ConcurrentBitset::Impl::end() const
1100 {
1101  return const_iterator (0,
1102  ~static_cast<bit_t>(0),
1103  nullptr, nullptr);
1104 }
1105 
1106 
1107 /**
1108  * @brief If bit @c bit is set, return an iterator pointing to it.
1109  * Otherwise, return an end iterator.
1110  * @param bit Bit number to test.
1111  */
1112 inline
1113 ConcurrentBitset::const_iterator
1114 ConcurrentBitset::Impl::find (bit_t bit) const
1115 {
1116  if (test (bit)) {
1117  // The bit's set.
1118  // Construct an iterator pointing at this bit.
1119  bit_t pos = bit / BLOCKSIZE;
1120  const std::atomic<Block_t>* data = m_data + pos;
1121  Block_t cache = *data;
1122  bit_t offs = bit&MASK;
1123  cache >>= 1;
1124  if (offs > 0) {
1125  cache >>= offs;
1126  }
1127  return const_iterator (cache, bit, m_data+pos,
1128  &m_data[0] + m_hwm + 1);
1129  }
1130  return end();
1131 }
1132 
1133 
1134 } // namespace CxxUtils