diff --git a/llvm/include/llvm/Support/Threading.h b/llvm/include/llvm/Support/Threading.h --- a/llvm/include/llvm/Support/Threading.h +++ b/llvm/include/llvm/Support/Threading.h @@ -41,8 +41,12 @@ #define LLVM_THREADING_USE_STD_CALL_ONCE 0 #endif +#define LLVM_THREADING_USE_ATOMIC_CALL_ONCE 1 + #if LLVM_THREADING_USE_STD_CALL_ONCE #include +#elif LLVM_THREADING_USE_ATOMIC_CALL_ONCE +#include #else #include "llvm/Support/Atomic.h" #endif @@ -98,7 +102,15 @@ /// This structure must be used as an opaque object. It is a struct to force /// autoinitialization and behave like std::once_flag. struct once_flag { +#if LLVM_THREADING_USE_ATOMIC_CALL_ONCE + + std::atomic status(Uninitialized); + +#else + volatile sys::cas_flag status = Uninitialized; + +#endif }; #endif @@ -120,6 +132,24 @@ #if LLVM_THREADING_USE_STD_CALL_ONCE std::call_once(flag, std::forward(F), std::forward(ArgList)...); +#elif LLVM_THREADING_USE_ATOMIC_CALL_ONCE + switch (flag.status.compare_exchange_strong(Uninitialized, Wait)) { + case Uninitialized: + // This is the first time the function is invoked. + std::forward(F)(std::forward(ArgList)...); + flag.status.store(Done); + break; + + case Wait: + while (flag.status.load() != Done) { + // Wait for function to finish. + } + break; + + case Done: + break; + } + TsanHappensAfter(&flag.status); #else // For other platforms we use a generic (if brittle) version based on our // atomics.