[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