#pragma once
#include <atomic>
#include <vector>
#include "rocksdb/rocksdb_namespace.h"
#include "test_util/sync_point.h"
#include "util/math.h"
namespace ROCKSDB_NAMESPACE {
template <typename UnderlyingT, typename DerivedT>
struct BitFields {
using U = UnderlyingT;
U underlying = 0;
static constexpr int kBitCount = sizeof(U) * 8;
using Derived = DerivedT;
template <typename BitFieldT>
void Set(typename BitFieldT::V value) {
static_assert(std::is_same_v<typename BitFieldT::Parent, Derived>);
Derived& derived = static_cast<Derived&>(*this);
BitFieldT::SetIn(derived, value);
}
template <typename BitFieldT>
constexpr Derived With(typename BitFieldT::V value) const {
static_assert(std::is_same_v<typename BitFieldT::Parent, Derived>);
Derived rv = static_cast<const Derived&>(*this);
BitFieldT::SetIn(rv, value);
return rv;
}
template <typename BitFieldT>
typename BitFieldT::V Get() const {
static_assert(std::is_same_v<typename BitFieldT::Parent, Derived>);
return BitFieldT::GetFrom(static_cast<const Derived&>(*this));
}
template <typename BitFieldT>
struct Reference {
explicit Reference(BitFields& bf) : bf_(bf) {}
Reference(const Reference&) = default;
Reference& operator=(const Reference&) = default;
Reference(Reference&&) = default;
Reference& operator=(Reference&&) = default;
void operator=(typename BitFieldT::V value) { bf_.Set<BitFieldT>(value); }
void operator+=(typename BitFieldT::V value) {
bf_.Set<BitFieldT>(bf_.Get<BitFieldT>() + value);
}
void operator-=(typename BitFieldT::V value) {
bf_.Set<BitFieldT>(bf_.Get<BitFieldT>() - value);
}
void operator|=(typename BitFieldT::V value) {
bf_.Set<BitFieldT>(bf_.Get<BitFieldT>() | value);
}
void operator&=(typename BitFieldT::V value) {
bf_.Set<BitFieldT>(bf_.Get<BitFieldT>() & value);
}
private:
BitFields& bf_;
};
template <typename BitFieldT>
Reference<BitFieldT> Ref() {
return Reference<BitFieldT>(*this);
}
bool operator==(const BitFields& other) const = default;
bool operator!=(const BitFields& other) const = default;
};
template <typename BitFieldsT>
struct OrTransformer {
using U = typename BitFieldsT::U;
U to_or = 0;
OrTransformer<BitFieldsT> operator+(
const OrTransformer<BitFieldsT>& other) const {
return OrTransformer<BitFieldsT>{to_or | other.to_or};
}
};
template <typename BitFieldsT>
struct AndTransformer {
using U = typename BitFieldsT::U;
U to_and = 0;
AndTransformer<BitFieldsT> operator+(
const AndTransformer<BitFieldsT>& other) const {
return AndTransformer<BitFieldsT>{to_and & other.to_and};
}
};
template <typename BitFieldsT>
struct AddTransformer {
using U = typename BitFieldsT::U;
U to_add = 0;
#ifndef NDEBUG
struct Precondition {
U mask; U piece; };
std::vector<Precondition> preconditions;
#endif void AssertPreconditions([[maybe_unused]] U from) const {
#ifndef NDEBUG
for (auto p : preconditions) {
U tmp = (from & p.mask) + p.piece;
testable_assert((tmp & ~p.mask) == 0);
}
#endif }
AddTransformer<BitFieldsT> operator+(
const AddTransformer<BitFieldsT>& other) const {
AddTransformer<BitFieldsT> rv{to_add + other.to_add};
#ifndef NDEBUG
rv.preconditions = preconditions;
rv.preconditions.insert(rv.preconditions.end(), other.preconditions.begin(),
other.preconditions.end());
#endif return rv;
}
};
struct NoPrevBitField {
NoPrevBitField() = delete;
static constexpr int kEndBit = 0;
};
template <typename BitFieldsT, typename PrevField>
struct BoolBitField {
using Parent = BitFieldsT;
using ParentBase =
BitFields<typename BitFieldsT::U, typename BitFieldsT::Derived>;
using U = typename BitFieldsT::U;
using V = bool;
static constexpr int kBitOffset = PrevField::kEndBit;
static constexpr int kEndBit = kBitOffset + 1;
static_assert(kBitOffset >= 0 && kEndBit <= BitFieldsT::kBitCount);
BoolBitField() = delete;
static bool GetFrom(const ParentBase& bf) {
return (bf.underlying & (U{1} << kBitOffset)) != 0;
}
static void SetIn(ParentBase& bf, bool value) {
bf.underlying =
(bf.underlying & ~(U{1} << kBitOffset)) | (U{value} << kBitOffset);
}
static OrTransformer<BitFieldsT> SetTransform() { return Or(true); }
static OrTransformer<BitFieldsT> Or(bool b) {
return OrTransformer<BitFieldsT>{U{b} << kBitOffset};
}
static AndTransformer<BitFieldsT> ClearTransform() { return And(false); }
static AndTransformer<BitFieldsT> And(bool b) {
return AndTransformer<BitFieldsT>{~(U{!b} << kBitOffset)};
}
};
template <typename BitFieldsT, int kBitCount, typename PrevField>
struct UnsignedBitField {
using Parent = BitFieldsT;
using U = typename BitFieldsT::U;
using V = std::conditional_t<
kBitCount <= 8, uint8_t,
std::conditional_t<
kBitCount <= 16, uint16_t,
std::conditional_t<kBitCount <= 32, uint32_t, uint64_t>>>;
static constexpr int kBitOffset = PrevField::kEndBit;
static constexpr int kEndBit = kBitOffset + kBitCount;
static_assert(kBitCount >= 1);
static_assert(kBitCount <= 64);
static_assert(kBitOffset >= 0 && kEndBit <= BitFieldsT::kBitCount);
static constexpr bool kIncludesTopBit = (kEndBit == BitFieldsT::kBitCount);
static constexpr V kMask = (V{1} << (kBitCount - 1) << 1) - 1;
UnsignedBitField() = delete;
static V GetFrom(const BitFieldsT& bf) {
return BitwiseAnd(bf.underlying >> kBitOffset, kMask);
}
static void SetIn(BitFieldsT& bf, V value) {
bf.underlying &= ~(static_cast<U>(kMask) << kBitOffset);
bf.underlying |= static_cast<U>(value & kMask) << kBitOffset;
}
static AndTransformer<BitFieldsT> ClearTransform() {
return AndTransformer<BitFieldsT>{~(static_cast<U>(kMask) << kBitOffset)};
}
static AndTransformer<BitFieldsT> AndTransform(V value) {
assert((value & ~kMask) == 0);
return AndTransformer<BitFieldsT>{
~(static_cast<U>(value ^ kMask) << kBitOffset)};
}
static OrTransformer<BitFieldsT> OrTransform(V value) {
assert((value & ~kMask) == 0);
return OrTransformer<BitFieldsT>{static_cast<U>(value) << kBitOffset};
}
static AddTransformer<BitFieldsT> PlusTransformPromiseNoOverflow(V value) {
static_assert(!kIncludesTopBit);
AddTransformer<BitFieldsT> rv{static_cast<U>(value) << kBitOffset};
#ifndef NDEBUG
rv.preconditions.push_back(
{static_cast<U>(kMask) << kBitOffset, rv.to_add});
#endif return rv;
}
static AddTransformer<BitFieldsT> PlusTransformIgnoreOverflow(V value) {
static_assert(kIncludesTopBit);
AddTransformer<BitFieldsT> rv{static_cast<U>(value) << kBitOffset};
return rv;
}
static AddTransformer<BitFieldsT> MinusTransformPromiseNoUnderflow(V value) {
static_assert(!kIncludesTopBit);
AddTransformer<BitFieldsT> rv{U{0} - (static_cast<U>(value) << kBitOffset)};
#ifndef NDEBUG
rv.preconditions.push_back(
{static_cast<U>(kMask) << kBitOffset, rv.to_add});
#endif return rv;
}
static AddTransformer<BitFieldsT> MinusTransformIgnoreUnderflow(V value) {
static_assert(kIncludesTopBit);
AddTransformer<BitFieldsT> rv{U{0} - (static_cast<U>(value) << kBitOffset)};
return rv;
}
};
template <typename BitFieldsT>
class RelaxedBitFieldsAtomic {
public:
using U = typename BitFieldsT::U;
explicit RelaxedBitFieldsAtomic(BitFieldsT initial = {})
: v_(initial.underlying) {}
void StoreRelaxed(BitFieldsT desired) {
v_.store(desired.underlying, std::memory_order_relaxed);
}
BitFieldsT LoadRelaxed() const {
return BitFieldsT{v_.load(std::memory_order_relaxed)};
}
bool CasWeakRelaxed(BitFieldsT& expected, BitFieldsT desired) {
return v_.compare_exchange_weak(expected.underlying, desired.underlying,
std::memory_order_relaxed);
}
bool CasStrongRelaxed(BitFieldsT& expected, BitFieldsT desired) {
return v_.compare_exchange_strong(expected.underlying, desired.underlying,
std::memory_order_relaxed);
}
BitFieldsT ExchangeRelaxed(BitFieldsT desired) {
return BitFieldsT{
v_.exchange(desired.underlying, std::memory_order_relaxed)};
}
void ApplyRelaxed(const OrTransformer<BitFieldsT>& transform,
BitFieldsT* before = nullptr, BitFieldsT* after = nullptr) {
ApplyImpl<std::memory_order_relaxed>(transform, before, after);
}
void ApplyRelaxed(const AndTransformer<BitFieldsT>& transform,
BitFieldsT* before = nullptr, BitFieldsT* after = nullptr) {
ApplyImpl<std::memory_order_relaxed>(transform, before, after);
}
void ApplyRelaxed(const AddTransformer<BitFieldsT>& transform,
BitFieldsT* before = nullptr, BitFieldsT* after = nullptr) {
ApplyImpl<std::memory_order_relaxed>(transform, before, after);
}
protected: template <std::memory_order kOrder>
void ApplyImpl(const OrTransformer<BitFieldsT>& transform,
BitFieldsT* before = nullptr, BitFieldsT* after = nullptr) {
U before_val = v_.fetch_or(transform.to_or, kOrder);
if (before) {
before->underlying = before_val;
}
if (after) {
after->underlying = before_val | transform.to_or;
}
}
template <std::memory_order kOrder>
void ApplyImpl(const AndTransformer<BitFieldsT>& transform,
BitFieldsT* before = nullptr, BitFieldsT* after = nullptr) {
U before_val = v_.fetch_and(transform.to_and, kOrder);
if (before) {
before->underlying = before_val;
}
if (after) {
after->underlying = before_val & transform.to_and;
}
}
template <std::memory_order kOrder>
void ApplyImpl(const AddTransformer<BitFieldsT>& transform,
BitFieldsT* before = nullptr, BitFieldsT* after = nullptr) {
U before_val = v_.fetch_add(transform.to_add, kOrder);
transform.AssertPreconditions(before_val);
if (before) {
before->underlying = before_val;
}
if (after) {
after->underlying = before_val + transform.to_add;
}
}
protected: std::atomic<U> v_;
};
template <typename BitFieldsT>
class BitFieldsAtomic : public RelaxedBitFieldsAtomic<BitFieldsT> {
public:
using Base = RelaxedBitFieldsAtomic<BitFieldsT>;
using U = typename BitFieldsT::U;
explicit BitFieldsAtomic(BitFieldsT initial = {}) : Base(initial) {}
void Store(BitFieldsT desired) {
Base::v_.store(desired.underlying, std::memory_order_release);
}
BitFieldsT Load() const {
return BitFieldsT{Base::v_.load(std::memory_order_acquire)};
}
bool CasWeak(BitFieldsT& expected, BitFieldsT desired) {
return Base::v_.compare_exchange_weak(
expected.underlying, desired.underlying, std::memory_order_acq_rel);
}
bool CasStrong(BitFieldsT& expected, BitFieldsT desired) {
return Base::v_.compare_exchange_strong(
expected.underlying, desired.underlying, std::memory_order_acq_rel);
}
BitFieldsT Exchange(BitFieldsT desired) {
return BitFieldsT{
Base::v_.exchange(desired.underlying, std::memory_order_acq_rel)};
}
void Apply(const OrTransformer<BitFieldsT>& transform,
BitFieldsT* before = nullptr, BitFieldsT* after = nullptr) {
Base::template ApplyImpl<std::memory_order_acq_rel>(transform, before,
after);
}
void Apply(const AndTransformer<BitFieldsT>& transform,
BitFieldsT* before = nullptr, BitFieldsT* after = nullptr) {
Base::template ApplyImpl<std::memory_order_acq_rel>(transform, before,
after);
}
void Apply(const AddTransformer<BitFieldsT>& transform,
BitFieldsT* before = nullptr, BitFieldsT* after = nullptr) {
Base::template ApplyImpl<std::memory_order_acq_rel>(transform, before,
after);
}
};
}