This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// The minimum atomic size we believe to generate lock free code, i.e. all | |
// trivially copyable types not bigger this size generate lock free code. | |
static constexpr int kMinLockFreeAtomicSize = 8; | |
// The same as kMinLockFreeAtomicSize but maximum atomic size. As double words | |
// might use two registers, we want to dispatch the logic for them. | |
#if defined(ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD) | |
static constexpr int kMaxLockFreeAtomicSize = 16; | |
#else | |
static constexpr int kMaxLockFreeAtomicSize = 8; | |
#endif | |
// We can use atomic in cases when it fits in the register, trivially copyable | |
// in order to make memcpy operations. | |
template <typename T> | |
struct IsAtomicFlagTypeTrait { | |
static constexpr bool value = | |
(sizeof(T) <= kMaxLockFreeAtomicSize && | |
type_traits_internal::is_trivially_copyable<T>::value); | |
}; | |
// Clang does not always produce cmpxchg16b instruction when alignment of a 16 | |
// bytes type is not 16. | |
struct alignas(16) FlagsInternalTwoWordsType { | |
int64_t first; | |
int64_t second; | |
}; | |
constexpr bool operator==(const FlagsInternalTwoWordsType& that, | |
const FlagsInternalTwoWordsType& other) { | |
return that.first == other.first && that.second == other.second; | |
} | |
constexpr bool operator!=(const FlagsInternalTwoWordsType& that, | |
const FlagsInternalTwoWordsType& other) { | |
return !(that == other); | |
} | |
constexpr int64_t SmallAtomicInit() { return 0xababababababababll; } | |
template <typename T, typename S = void> | |
struct BestAtomicType { | |
using type = int64_t; | |
static constexpr int64_t AtomicInit() { return SmallAtomicInit(); } | |
}; | |
template <typename T> | |
struct BestAtomicType< | |
T, typename std::enable_if<(kMinLockFreeAtomicSize < sizeof(T) && | |
sizeof(T) <= kMaxLockFreeAtomicSize), | |
void>::type> { | |
using type = FlagsInternalTwoWordsType; | |
static constexpr FlagsInternalTwoWordsType AtomicInit() { | |
return {SmallAtomicInit(), SmallAtomicInit()}; | |
} | |
}; | |
template <typename T> | |
class Flag { | |
... | |
// For some types, a copy of the current value is kept in an atomically | |
// accessible field. | |
union Atomics { | |
// Using small atomic for small types. | |
std::atomic<int64_t> small_atomic; | |
template <typename T, | |
typename K = typename std::enable_if< | |
(sizeof(T) <= kMinLockFreeAtomicSize), void>::type> | |
int64_t load() const { | |
return small_atomic.load(std::memory_order_acquire); | |
} | |
#if defined(ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD) | |
// Using big atomics for big types. | |
std::atomic<FlagsInternalTwoWordsType> big_atomic; | |
template <typename T, typename K = typename std::enable_if< | |
(kMinLockFreeAtomicSize < sizeof(T) && | |
sizeof(T) <= kMaxLockFreeAtomicSize), | |
void>::type> | |
FlagsInternalTwoWordsType load() const { | |
return big_atomic.load(std::memory_order_acquire); | |
} | |
constexpr Atomics() | |
: big_atomic{FlagsInternalTwoWordsType{SmallAtomicInit(), | |
SmallAtomicInit()}} {} | |
#else | |
constexpr Atomics() : small_atomic{SmallAtomicInit()} {} | |
#endif | |
}; | |
Atomics atomics_{}; | |
... | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment