AtomicBitSet.h 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. /*
  2. * Copyright 2013-present Facebook, Inc.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #pragma once
  17. #include <array>
  18. #include <atomic>
  19. #include <cassert>
  20. #include <cstddef>
  21. #include <limits>
  22. #include <boost/noncopyable.hpp>
  23. #include <folly/Portability.h>
  24. namespace folly {
  25. /**
  26. * An atomic bitset of fixed size (specified at compile time).
  27. */
  28. template <size_t N>
  29. class AtomicBitSet : private boost::noncopyable {
  30. public:
  31. /**
  32. * Construct an AtomicBitSet; all bits are initially false.
  33. */
  34. AtomicBitSet();
  35. /**
  36. * Set bit idx to true, using the given memory order. Returns the
  37. * previous value of the bit.
  38. *
  39. * Note that the operation is a read-modify-write operation due to the use
  40. * of fetch_or.
  41. */
  42. bool set(size_t idx, std::memory_order order = std::memory_order_seq_cst);
  43. /**
  44. * Set bit idx to false, using the given memory order. Returns the
  45. * previous value of the bit.
  46. *
  47. * Note that the operation is a read-modify-write operation due to the use
  48. * of fetch_and.
  49. */
  50. bool reset(size_t idx, std::memory_order order = std::memory_order_seq_cst);
  51. /**
  52. * Set bit idx to the given value, using the given memory order. Returns
  53. * the previous value of the bit.
  54. *
  55. * Note that the operation is a read-modify-write operation due to the use
  56. * of fetch_and or fetch_or.
  57. *
  58. * Yes, this is an overload of set(), to keep as close to std::bitset's
  59. * interface as possible.
  60. */
  61. bool set(
  62. size_t idx,
  63. bool value,
  64. std::memory_order order = std::memory_order_seq_cst);
  65. /**
  66. * Read bit idx.
  67. */
  68. bool test(size_t idx, std::memory_order order = std::memory_order_seq_cst)
  69. const;
  70. /**
  71. * Same as test() with the default memory order.
  72. */
  73. bool operator[](size_t idx) const;
  74. /**
  75. * Return the size of the bitset.
  76. */
  77. constexpr size_t size() const {
  78. return N;
  79. }
  80. private:
  81. // Pick the largest lock-free type available
  82. #if (ATOMIC_LLONG_LOCK_FREE == 2)
  83. typedef unsigned long long BlockType;
  84. #elif (ATOMIC_LONG_LOCK_FREE == 2)
  85. typedef unsigned long BlockType;
  86. #else
  87. // Even if not lock free, what can we do?
  88. typedef unsigned int BlockType;
  89. #endif
  90. typedef std::atomic<BlockType> AtomicBlockType;
  91. static constexpr size_t kBitsPerBlock =
  92. std::numeric_limits<BlockType>::digits;
  93. static constexpr size_t blockIndex(size_t bit) {
  94. return bit / kBitsPerBlock;
  95. }
  96. static constexpr size_t bitOffset(size_t bit) {
  97. return bit % kBitsPerBlock;
  98. }
  99. // avoid casts
  100. static constexpr BlockType kOne = 1;
  101. std::array<AtomicBlockType, N> data_;
  102. };
  103. // value-initialize to zero
  104. template <size_t N>
  105. inline AtomicBitSet<N>::AtomicBitSet() : data_() {}
  106. template <size_t N>
  107. inline bool AtomicBitSet<N>::set(size_t idx, std::memory_order order) {
  108. assert(idx < N * kBitsPerBlock);
  109. BlockType mask = kOne << bitOffset(idx);
  110. return data_[blockIndex(idx)].fetch_or(mask, order) & mask;
  111. }
  112. template <size_t N>
  113. inline bool AtomicBitSet<N>::reset(size_t idx, std::memory_order order) {
  114. assert(idx < N * kBitsPerBlock);
  115. BlockType mask = kOne << bitOffset(idx);
  116. return data_[blockIndex(idx)].fetch_and(~mask, order) & mask;
  117. }
  118. template <size_t N>
  119. inline bool
  120. AtomicBitSet<N>::set(size_t idx, bool value, std::memory_order order) {
  121. return value ? set(idx, order) : reset(idx, order);
  122. }
  123. template <size_t N>
  124. inline bool AtomicBitSet<N>::test(size_t idx, std::memory_order order) const {
  125. assert(idx < N * kBitsPerBlock);
  126. BlockType mask = kOne << bitOffset(idx);
  127. return data_[blockIndex(idx)].load(order) & mask;
  128. }
  129. template <size_t N>
  130. inline bool AtomicBitSet<N>::operator[](size_t idx) const {
  131. return test(idx);
  132. }
  133. } // namespace folly