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