Fix folly::call_once v2017.05.08.00
authorAndrii Grynenko <andrii@fb.com>
Sat, 6 May 2017 18:01:13 +0000 (11:01 -0700)
committerFacebook Github Bot <facebook-github-bot@users.noreply.github.com>
Sat, 6 May 2017 18:12:04 +0000 (11:12 -0700)
Summary: std::call_once implementation is broken if function throws. This fixes folly::call_once to not depend on std::call_once.

Reviewed By: yfeldblum

Differential Revision: D5015897

fbshipit-source-id: bcbda68becf0930cdbf0b09125cbee61d75c2015

folly/CallOnce.h
folly/SharedMutex.h
folly/detail/Futex.cpp
folly/test/CallOnceTest.cpp

index 77db621..4dd0694 100644 (file)
@@ -36,6 +36,7 @@
 
 #include <folly/Likely.h>
 #include <folly/Portability.h>
+#include <folly/SharedMutex.h>
 
 namespace folly {
 
@@ -54,7 +55,7 @@ class once_flag {
 
  private:
   std::atomic<bool> called_{false};
-  std::once_flag std_once_flag_;
+  folly::SharedMutex mutex_;
 };
 
 template <class Callable, class... Args>
@@ -71,9 +72,13 @@ call_once(once_flag& flag, Callable&& f, Args&&... args) {
 template <class Callable, class... Args>
 void FOLLY_NOINLINE
 call_once_impl_no_inline(once_flag& flag, Callable&& f, Args&&... args) {
-  std::call_once(flag.std_once_flag_,
-                 std::forward<Callable>(f),
-                 std::forward<Args>(args)...);
+  std::lock_guard<folly::SharedMutex> lg(flag.mutex_);
+  if (flag.called_) {
+    return;
+  }
+
+  std::forward<Callable>(f)(std::forward<Args>(args)...);
+
   flag.called_.store(true, std::memory_order_release);
 }
 }
index 571af4a..c13a6d6 100644 (file)
@@ -249,7 +249,7 @@ class SharedMutexImpl {
   class UpgradeHolder;
   class WriteHolder;
 
-  constexpr SharedMutexImpl() : state_(0) {}
+  constexpr SharedMutexImpl() noexcept : state_(0) {}
 
   SharedMutexImpl(const SharedMutexImpl&) = delete;
   SharedMutexImpl(SharedMutexImpl&&) = delete;
index fa62860..edaa279 100644 (file)
@@ -20,7 +20,6 @@
 #include <condition_variable>
 #include <mutex>
 #include <boost/intrusive/list.hpp>
-#include <folly/CallOnce.h>
 #include <folly/Hash.h>
 #include <folly/ScopeGuard.h>
 
@@ -187,22 +186,15 @@ struct EmulatedFutexBucket {
   boost::intrusive::list<EmulatedFutexWaitNode> waiters_;
 
   static const size_t kNumBuckets = 4096;
-  static EmulatedFutexBucket* gBuckets;
-  static folly::once_flag gBucketInit;
 
   static EmulatedFutexBucket& bucketFor(void* addr) {
-    folly::call_once(gBucketInit, [](){
-      gBuckets = new EmulatedFutexBucket[kNumBuckets];
-    });
+    static auto gBuckets = new EmulatedFutexBucket[kNumBuckets];
     uint64_t mixedBits = folly::hash::twang_mix64(
         reinterpret_cast<uintptr_t>(addr));
     return gBuckets[mixedBits % kNumBuckets];
   }
 };
 
-EmulatedFutexBucket* EmulatedFutexBucket::gBuckets;
-folly::once_flag EmulatedFutexBucket::gBucketInit;
-
 int emulatedFutexWake(void* addr, int count, uint32_t waitMask) {
   auto& bucket = EmulatedFutexBucket::bucketFor(addr);
   std::unique_lock<std::mutex> bucketLock(bucket.mutex_);
index 357b697..e0dccd8 100644 (file)
@@ -50,6 +50,23 @@ TEST(FollyCallOnce, Simple) {
   ASSERT_EQ(1, out);
 }
 
+TEST(FollyCallOnce, Exception) {
+  struct ExpectedException {};
+  folly::once_flag flag;
+  size_t numCalls = 0;
+  EXPECT_THROW(
+      folly::call_once(
+          flag,
+          [&] {
+            ++numCalls;
+            throw ExpectedException();
+          }),
+      ExpectedException);
+  EXPECT_EQ(1, numCalls);
+  folly::call_once(flag, [&] { ++numCalls; });
+  EXPECT_EQ(2, numCalls);
+}
+
 TEST(FollyCallOnce, Stress) {
   for (int i = 0; i < 100; ++i) {
     folly::once_flag flag;