| // 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