Last active
January 11, 2020 23:11
-
-
Save danlark1/bdbe54c359a66dbc55b1d8e44d42c6e3 to your computer and use it in GitHub Desktop.
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
// 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