bit_fields.h 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. // Copyright (c) Meta Platforms, Inc. and affiliates.
  2. // This source code is licensed under both the GPLv2 (found in the
  3. // COPYING file in the root directory) and Apache 2.0 License
  4. // (found in the LICENSE.Apache file in the root directory).
  5. #pragma once
  6. #include <atomic>
  7. #include "rocksdb/rocksdb_namespace.h"
  8. #include "util/math.h"
  9. namespace ROCKSDB_NAMESPACE {
  10. // Declares a wrapper type around UnderlyingT that allows it to be divided up
  11. // into and accessed as bit fields. This is mostly intended to aid in packing
  12. // fields into atomic variables to reduce the need for locking in concurrent
  13. // code and/or to simplify reasoning on and accommodation of different
  14. // interesting, bug-prone interleavings. Convenient atomic wrappers
  15. // (RelaxedAtomic, AcqRelAtomic) are provided below to aid usage with atomics,
  16. // especially for CAS updates, but it is even possible to combine operations on
  17. // multiple bit fields into a single non-CAS atomic operation using Transforms
  18. // below.
  19. //
  20. // Unlike C/C++ bit fields, this implementation guarantees tight bit packing
  21. // so that all available lock-free atomic bits can be utilized.
  22. //
  23. // The specific bit fields are declared outside the declaration using
  24. // BoolBitField and UnsignedBitField below. Example usage:
  25. //
  26. // struct MyState : public BitFields<uint32_t, MyState> {
  27. // // Extra helper declarations and/or field type declarations
  28. // };
  29. //
  30. // // Starts with a 16-bit field returned as uint16_t
  31. // using Field1 = UnsignedBitField<MyState, 16, NoPrevBitField>;
  32. // using Field2 = BoolBitField<MyState, Field1>;
  33. // using Field3 = BoolBitField<MyState, Field2>;
  34. // using Field4 = UnsignedBitField<MyState, 5, Field3>; // 5 bits in a uint8_t
  35. //
  36. // // MyState{} is zero-initialized
  37. // auto state = MyState{}.With<Field1>(42U).With<Field2>(true);
  38. // state.Set<Field4>(3U);
  39. // state.Ref<Field1>() += state.Get<Field4>();
  40. //
  41. // Note that there's nothing preventing you from declaring overlapping fields
  42. // in the same 'MyState' family. This could be useful for variant types where
  43. // an earlier field determines which layout later fields are using. For example,
  44. // an alternate field after Field2:
  45. //
  46. // using Field3a = UnsignedBitField<State, 6, Field2>; // 6 bits in a uint8_t
  47. //
  48. template <typename UnderlyingT, typename DerivedT>
  49. struct BitFields {
  50. using U = UnderlyingT;
  51. U underlying = 0;
  52. static constexpr int kBitCount = sizeof(U) * 8;
  53. using Derived = DerivedT;
  54. // Modify a given field in place
  55. template <typename BitFieldT>
  56. void Set(typename BitFieldT::V value) {
  57. static_assert(std::is_same_v<typename BitFieldT::Parent, Derived>);
  58. Derived& derived = static_cast<Derived&>(*this);
  59. BitFieldT::SetIn(derived, value);
  60. }
  61. // Return a copy with the given field modified
  62. template <typename BitFieldT>
  63. Derived With(typename BitFieldT::V value) const {
  64. static_assert(std::is_same_v<typename BitFieldT::Parent, Derived>);
  65. Derived rv = static_cast<const Derived&>(*this);
  66. BitFieldT::SetIn(rv, value);
  67. return rv;
  68. }
  69. // Get the value of a field
  70. template <typename BitFieldT>
  71. typename BitFieldT::V Get() const {
  72. static_assert(std::is_same_v<typename BitFieldT::Parent, Derived>);
  73. return BitFieldT::GetFrom(static_cast<const Derived&>(*this));
  74. }
  75. // Reference and Ref() are not intended to behave as full references but to
  76. // provide a convenient way to do operations like +=, |=, etc. Get and Set
  77. // are preferred for simple operations.
  78. template <typename BitFieldT>
  79. struct Reference {
  80. explicit Reference(BitFields& bf) : bf_(bf) {}
  81. Reference(const Reference&) = default;
  82. Reference& operator=(const Reference&) = default;
  83. Reference(Reference&&) = default;
  84. Reference& operator=(Reference&&) = default;
  85. void operator=(typename BitFieldT::V value) { bf_.Set<BitFieldT>(value); }
  86. void operator+=(typename BitFieldT::V value) {
  87. bf_.Set<BitFieldT>(bf_.Get<BitFieldT>() + value);
  88. }
  89. void operator-=(typename BitFieldT::V value) {
  90. bf_.Set<BitFieldT>(bf_.Get<BitFieldT>() - value);
  91. }
  92. void operator|=(typename BitFieldT::V value) {
  93. bf_.Set<BitFieldT>(bf_.Get<BitFieldT>() | value);
  94. }
  95. void operator&=(typename BitFieldT::V value) {
  96. bf_.Set<BitFieldT>(bf_.Get<BitFieldT>() & value);
  97. }
  98. private:
  99. BitFields& bf_;
  100. };
  101. template <typename BitFieldT>
  102. Reference<BitFieldT> Ref() {
  103. return Reference<BitFieldT>(*this);
  104. }
  105. bool operator==(const BitFields& other) const = default;
  106. bool operator!=(const BitFields& other) const = default;
  107. };
  108. // For building atomic updates affecting one or more fields, assuming all the
  109. // updates are bitwise-or.
  110. template <typename BitFieldsT>
  111. struct OrTransform {
  112. using U = typename BitFieldsT::U;
  113. U to_or = 0;
  114. // + for general combine
  115. OrTransform<BitFieldsT> operator+(OrTransform<BitFieldsT> other) const {
  116. return OrTransform<BitFieldsT>{to_or | other.to_or};
  117. }
  118. };
  119. // For building atomic updates affecting one or more fields, assuming all the
  120. // updates are bitwise-and.
  121. template <typename BitFieldsT>
  122. struct AndTransform {
  123. using U = typename BitFieldsT::U;
  124. U to_and = 0;
  125. // + for general combine
  126. AndTransform<BitFieldsT> operator+(AndTransform<BitFieldsT> other) const {
  127. return AndTransform<BitFieldsT>{to_and & other.to_and};
  128. }
  129. };
  130. // TODO: AddTransfrom, which is more complicated due to possible overflow into
  131. // other fields etc.
  132. // Placeholder for PrevField for the first field
  133. struct NoPrevBitField {
  134. // no instances
  135. NoPrevBitField() = delete;
  136. static constexpr int kEndBit = 0;
  137. };
  138. // For declaring a single-bit field accessed as a boolean. See example above on
  139. // BitFields
  140. template <typename BitFieldsT, typename PrevField>
  141. struct BoolBitField {
  142. using Parent = BitFieldsT;
  143. using ParentBase =
  144. BitFields<typename BitFieldsT::U, typename BitFieldsT::Derived>;
  145. using U = typename BitFieldsT::U;
  146. using V = bool;
  147. static constexpr int kBitOffset = PrevField::kEndBit;
  148. static constexpr int kEndBit = kBitOffset + 1;
  149. static_assert(kBitOffset >= 0 && kEndBit <= BitFieldsT::kBitCount);
  150. // no instances
  151. BoolBitField() = delete;
  152. // NOTE: allow BitFieldsT to be derived from BitFields<> which can be
  153. // passed in here
  154. static bool GetFrom(const ParentBase& bf) {
  155. return (bf.underlying & (U{1} << kBitOffset)) != 0;
  156. }
  157. static void SetIn(ParentBase& bf, bool value) {
  158. bf.underlying =
  159. (bf.underlying & ~(U{1} << kBitOffset)) | (U{value} << kBitOffset);
  160. }
  161. static OrTransform<BitFieldsT> SetTransform() {
  162. return OrTransform<BitFieldsT>{U{1} << kBitOffset};
  163. }
  164. static AndTransform<BitFieldsT> ClearTransform() {
  165. return AndTransform<BitFieldsT>{~(U{1} << kBitOffset)};
  166. }
  167. };
  168. // For declaring a multi-bit field accessed as an unsigned int. See example
  169. // above on BitFields
  170. template <typename BitFieldsT, int kBitCount, typename PrevField>
  171. struct UnsignedBitField {
  172. using Parent = BitFieldsT;
  173. using U = typename BitFieldsT::U;
  174. // Smallest uint type that can fit kBitCount bits
  175. using V = std::conditional_t<
  176. kBitCount <= 8, uint8_t,
  177. std::conditional_t<
  178. kBitCount <= 16, uint16_t,
  179. std::conditional_t<kBitCount <= 32, uint32_t, uint64_t>>>;
  180. static constexpr int kBitOffset = PrevField::kEndBit;
  181. static constexpr int kEndBit = kBitOffset + kBitCount;
  182. static_assert(kBitCount >= 1);
  183. static_assert(kBitCount <= 64);
  184. static_assert(kBitOffset >= 0 && kEndBit <= BitFieldsT::kBitCount);
  185. static constexpr V kMask = (V{1} << (kBitCount - 1) << 1) - 1;
  186. // no instances
  187. UnsignedBitField() = delete;
  188. static V GetFrom(const BitFieldsT& bf) {
  189. return BitwiseAnd(bf.underlying >> kBitOffset, kMask);
  190. }
  191. static void SetIn(BitFieldsT& bf, V value) {
  192. bf.underlying &= ~(static_cast<U>(kMask) << kBitOffset);
  193. bf.underlying |= static_cast<U>(value & kMask) << kBitOffset;
  194. }
  195. static AndTransform<BitFieldsT> ClearTransform() {
  196. return AndTransform<BitFieldsT>{~(static_cast<U>(kMask) << kBitOffset)};
  197. }
  198. };
  199. // A handy wrapper for a relaxed atomic on some BitFields type (unlike
  200. // RelaxedAtomic for arithmetic types). For encapsulation, usual arithmetic
  201. // atomic operations are only available by calling Apply[Relaxed]() on
  202. // Transforms returned from field classes. Extending an example from BitFields:
  203. //
  204. // auto transform = Field2::ClearTransform() + Field4::ClearTransform();
  205. // MyState old_state;
  206. // my_atomic.ApplyRelaxed(transform, &old_state);
  207. // auto field2_before_clearing = old_state.Get<Field2>();
  208. //
  209. template <typename BitFieldsT>
  210. class RelaxedBitFieldsAtomic {
  211. public:
  212. using U = typename BitFieldsT::U;
  213. explicit RelaxedBitFieldsAtomic(BitFieldsT initial = {})
  214. : v_(initial.underlying) {}
  215. void StoreRelaxed(BitFieldsT desired) {
  216. v_.store(desired.underlying, std::memory_order_relaxed);
  217. }
  218. BitFieldsT LoadRelaxed() const {
  219. return BitFieldsT{v_.load(std::memory_order_relaxed)};
  220. }
  221. bool CasWeakRelaxed(BitFieldsT& expected, BitFieldsT desired) {
  222. return v_.compare_exchange_weak(expected.underlying, desired.underlying,
  223. std::memory_order_relaxed);
  224. }
  225. bool CasStrongRelaxed(BitFieldsT& expected, BitFieldsT desired) {
  226. return v_.compare_exchange_strong(expected.underlying, desired.underlying,
  227. std::memory_order_relaxed);
  228. }
  229. BitFieldsT ExchangeRelaxed(BitFieldsT desired) {
  230. return BitFieldsT{
  231. v_.exchange(desired.underlying, std::memory_order_relaxed)};
  232. }
  233. void ApplyRelaxed(OrTransform<BitFieldsT> transform,
  234. BitFieldsT* before = nullptr, BitFieldsT* after = nullptr) {
  235. U before_val = v_.fetch_or(transform.to_or, std::memory_order_relaxed);
  236. if (before) {
  237. before->underlying = before_val;
  238. }
  239. if (after) {
  240. after->underlying = before_val | transform.to_or;
  241. }
  242. }
  243. void ApplyRelaxed(AndTransform<BitFieldsT> transform,
  244. BitFieldsT* before = nullptr, BitFieldsT* after = nullptr) {
  245. U before_val = v_.fetch_and(transform.to_and, std::memory_order_relaxed);
  246. if (before) {
  247. before->underlying = before_val;
  248. }
  249. if (after) {
  250. after->underlying = before_val & transform.to_and;
  251. }
  252. }
  253. protected:
  254. std::atomic<U> v_;
  255. };
  256. // A handy wrapper for an aquire-release atomic (also relaxed semantics
  257. // available) on some BitFields type. See RelaxedBitFieldsAtomic for more info.
  258. template <typename BitFieldsT>
  259. class AcqRelBitFieldsAtomic : public RelaxedBitFieldsAtomic<BitFieldsT> {
  260. public:
  261. using Base = RelaxedBitFieldsAtomic<BitFieldsT>;
  262. using U = typename BitFieldsT::U;
  263. explicit AcqRelBitFieldsAtomic(BitFieldsT initial = {}) : Base(initial) {}
  264. void Store(BitFieldsT desired) {
  265. Base::v_.store(desired.underlying, std::memory_order_release);
  266. }
  267. BitFieldsT Load() const {
  268. return BitFieldsT{Base::v_.load(std::memory_order_acquire)};
  269. }
  270. bool CasWeak(BitFieldsT& expected, BitFieldsT desired) {
  271. return Base::v_.compare_exchange_weak(
  272. expected.underlying, desired.underlying, std::memory_order_acq_rel);
  273. }
  274. bool CasStrong(BitFieldsT& expected, BitFieldsT desired) {
  275. return Base::v_.compare_exchange_strong(
  276. expected.underlying, desired.underlying, std::memory_order_acq_rel);
  277. }
  278. BitFieldsT Exchange(BitFieldsT desired) {
  279. return BitFieldsT{
  280. Base::v_.exchange(desired.underlying, std::memory_order_acq_rel)};
  281. }
  282. void Apply(OrTransform<BitFieldsT> transform, BitFieldsT* before = nullptr,
  283. BitFieldsT* after = nullptr) {
  284. U before_val =
  285. Base::v_.fetch_or(transform.to_or, std::memory_order_acq_rel);
  286. if (before) {
  287. before->underlying = before_val;
  288. }
  289. if (after) {
  290. after->underlying = before_val | transform.to_or;
  291. }
  292. }
  293. void Apply(AndTransform<BitFieldsT> transform, BitFieldsT* before = nullptr,
  294. BitFieldsT* after = nullptr) {
  295. U before_val =
  296. Base::v_.fetch_and(transform.to_and, std::memory_order_acq_rel);
  297. if (before) {
  298. before->underlying = before_val;
  299. }
  300. if (after) {
  301. after->underlying = before_val & transform.to_and;
  302. }
  303. }
  304. };
  305. } // namespace ROCKSDB_NAMESPACE