From: Andrii Grynenko Date: Sat, 6 May 2017 18:01:13 +0000 (-0700) Subject: Fix folly::call_once X-Git-Tag: v2017.05.08.00 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=2efd71edf7128b31f31f4ee17a726f7b8474439f;p=folly.git Fix folly::call_once 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 --- diff --git a/folly/CallOnce.h b/folly/CallOnce.h index 77db6210..4dd06941 100644 --- a/folly/CallOnce.h +++ b/folly/CallOnce.h @@ -36,6 +36,7 @@ #include #include +#include namespace folly { @@ -54,7 +55,7 @@ class once_flag { private: std::atomic called_{false}; - std::once_flag std_once_flag_; + folly::SharedMutex mutex_; }; template @@ -71,9 +72,13 @@ call_once(once_flag& flag, Callable&& f, Args&&... args) { template void FOLLY_NOINLINE call_once_impl_no_inline(once_flag& flag, Callable&& f, Args&&... args) { - std::call_once(flag.std_once_flag_, - std::forward(f), - std::forward(args)...); + std::lock_guard lg(flag.mutex_); + if (flag.called_) { + return; + } + + std::forward(f)(std::forward(args)...); + flag.called_.store(true, std::memory_order_release); } } diff --git a/folly/SharedMutex.h b/folly/SharedMutex.h index 571af4a7..c13a6d6f 100644 --- a/folly/SharedMutex.h +++ b/folly/SharedMutex.h @@ -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; diff --git a/folly/detail/Futex.cpp b/folly/detail/Futex.cpp index fa628602..edaa279e 100644 --- a/folly/detail/Futex.cpp +++ b/folly/detail/Futex.cpp @@ -20,7 +20,6 @@ #include #include #include -#include #include #include @@ -187,22 +186,15 @@ struct EmulatedFutexBucket { boost::intrusive::list 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(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 bucketLock(bucket.mutex_); diff --git a/folly/test/CallOnceTest.cpp b/folly/test/CallOnceTest.cpp index 357b6973..e0dccd81 100644 --- a/folly/test/CallOnceTest.cpp +++ b/folly/test/CallOnceTest.cpp @@ -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;