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