| // This macro is the "source of truth" for the list of supported flag types we | |
| // expect to perform lock free operations on. Specifically it generates code, | |
| // a one argument macro operating on a type, supplied as a macro argument, for | |
| // each type in the list. | |
| #define ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(A) \ | |
| A(bool) \ | |
| A(short) \ | |
| A(unsigned short) \ | |
| A(int) \ | |
| A(unsigned int) \ | |
| A(long) \ | |
| A(unsigned long) \ | |
| A(long long) \ | |
| A(unsigned long long) \ | |
| A(double) \ | |
| A(float) | |
| constexpr int64_t AtomicInit() { return 0xababababababababll; } | |
| class FlagImpl { | |
| ... | |
| // Init this atomic with some garbage where | |
| // the collisions are negligible. | |
| std::atomic<int64_t> atomic_{AtomicInit()}; | |
| ... | |
| }; | |
| void FlagImpl::StoreAtomic() { | |
| // Get the size of a type. | |
| size_t data_size = Sizeof(op_); | |
| // If it fits into the word on 64 bit systems. | |
| if (data_size <= sizeof(int64_t)) { | |
| int64_t t = 0; | |
| // Copy it inside this atomic. | |
| std::memcpy(&t, cur_, data_size); | |
| atomic_.store(t, std::memory_order_release); | |
| } | |
| } | |
| template <typename T> | |
| bool AtomicGet(T* v) const { | |
| // Load the byte representation. | |
| const int64_t r = atomic_.load(std::memory_order_acquire); | |
| // If it is not atomic init which means that the atomic | |
| // value was initialized with something but not a garbage. | |
| if (r != flags_internal::AtomicInit()) { | |
| // Copy it inside the value. | |
| // Compilers are very smart enough to generate | |
| // couple of cmov, mov instructions when the memcpy size is known. | |
| std::memcpy(v, &r, sizeof(T)); | |
| // Return that we successfully returned a true value depending | |
| // on a "garbage heuristic". | |
| return true; | |
| } | |
| // In that case we do believe that the flag was not yet initialized. | |
| return false; | |
| } | |
| #define ABSL_FLAGS_ATOMIC_GET(T) \ | |
| T GetFlag(const absl::Flag<T>& flag) { \ | |
| T result; \ | |
| /* Try to get the heuristic */ \ | |
| if (flag.AtomicGet(&result)) { \ | |
| /* If it was initialized, return*/ \ | |
| return result; \ | |
| } \ | |
| /* Use slow mutex holding impl */ \ | |
| return flag.Get(); \ | |
| } | |
| ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(ABSL_FLAGS_ATOMIC_GET) | |
| // For other types use mutex holding implementation. | |
| template <typename T> | |
| ABSL_MUST_USE_RESULT T GetFlag(const absl::Flag<T>& flag) { | |
| return flag.Get(); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment