[PATCH] Removing the static initializer in ManagedStatic.cpp by using llvm_call_once to initialize the ManagedStatic mutex.
David Majnemer
david.majnemer at gmail.com
Thu Oct 23 14:08:50 PDT 2014
I would instead recommend we do something along the lines of:
```
#include <atomic>
#include <thread>
enum InitStatus {
Done = -1,
Uninitialized = 0,
Wait = 1
};
void call_once(std::atomic<InitStatus> &Initialized, void (*fptr)(void)) {
InitStatus Status = Initialized.load(std::memory_order_relaxed);
for (;;) {
// Figure out which state we are in.
if (Status == Done) {
// We are at Done, no further transitions are possible.
// This acquire fence pairs with the release fense below.
std::atomic_thread_fence(std::memory_order_acquire);
return;
}
if (Status == Wait) {
// We are waiting for a Wait -> Done transition.
std::this_thread::yield();
// Reload status.
Status = Initialized.load(std::memory_order_relaxed);
continue;
}
// Attempt an Uninitialized -> Wait transition.
// If we succeed, perform initialization.
// If we do not, Status will be updated with our new state.
Status = Uninitialized;
bool ShouldInit = std::atomic_compare_exchange_weak_explicit(
&Initialized, &Status, Wait, std::memory_order_relaxed,
std::memory_order_relaxed);
if (ShouldInit) {
(*fptr)();
std::atomic_thread_fence(std::memory_order_release);
Initialized.store(Done, std::memory_order_relaxed);
}
}
}
```
This approach ends up with no fences in the actual assembly with either GCC or clang when targeting X86-64. It has the additional benefit of not dirtying any cache lines if initialization has occurred long before.
================
Comment at: include/llvm/Support/Threading.h:70
@@ +69,3 @@
+ // Visual Studio bug id #811192.
+ static volatile sys::cas_flag initialized = 0;
+ sys::cas_flag old_val = sys::CompareAndSwap(&initialized, 1, 0);
----------------
`volatile` doesn't really make sense here.
================
Comment at: include/llvm/Support/Threading.h:71
@@ +70,3 @@
+ static volatile sys::cas_flag initialized = 0;
+ sys::cas_flag old_val = sys::CompareAndSwap(&initialized, 1, 0);
+ if (old_val == 0) {
----------------
This is very expensive, you are performing a RMW operation even if nothing has changed.
================
Comment at: include/llvm/Support/Threading.h:77-82
@@ +76,8 @@
+ } else {
+ sys::cas_flag tmp = initialized;
+ sys::MemoryFence();
+ while (tmp != 2) {
+ tmp = initialized;
+ sys::MemoryFence();
+ }
+ }
----------------
Let's assume that loading `initialized` will somehow turn into a relaxed load. Why bother with the fence? You could just fence after:
while (initialized != 2);
sys::MemoryFence();
This will result in far, far fewer fences should this ever be contended.
http://reviews.llvm.org/D5922
More information about the llvm-commits
mailing list