Commit 932973bf authored by Andrii Grynenko's avatar Andrii Grynenko Committed by Facebook Github Bot

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
parent c822472d
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include <folly/Likely.h> #include <folly/Likely.h>
#include <folly/Portability.h> #include <folly/Portability.h>
#include <folly/SharedMutex.h>
namespace folly { namespace folly {
...@@ -54,7 +55,7 @@ class once_flag { ...@@ -54,7 +55,7 @@ class once_flag {
private: private:
std::atomic<bool> called_{false}; std::atomic<bool> called_{false};
std::once_flag std_once_flag_; folly::SharedMutex mutex_;
}; };
template <class Callable, class... Args> template <class Callable, class... Args>
...@@ -71,9 +72,13 @@ call_once(once_flag& flag, Callable&& f, Args&&... args) { ...@@ -71,9 +72,13 @@ call_once(once_flag& flag, Callable&& f, Args&&... args) {
template <class Callable, class... Args> template <class Callable, class... Args>
void FOLLY_NOINLINE void FOLLY_NOINLINE
call_once_impl_no_inline(once_flag& flag, Callable&& f, Args&&... args) { call_once_impl_no_inline(once_flag& flag, Callable&& f, Args&&... args) {
std::call_once(flag.std_once_flag_, std::lock_guard<folly::SharedMutex> lg(flag.mutex_);
std::forward<Callable>(f), if (flag.called_) {
std::forward<Args>(args)...); return;
}
std::forward<Callable>(f)(std::forward<Args>(args)...);
flag.called_.store(true, std::memory_order_release); flag.called_.store(true, std::memory_order_release);
} }
} }
...@@ -249,7 +249,7 @@ class SharedMutexImpl { ...@@ -249,7 +249,7 @@ class SharedMutexImpl {
class UpgradeHolder; class UpgradeHolder;
class WriteHolder; class WriteHolder;
constexpr SharedMutexImpl() : state_(0) {} constexpr SharedMutexImpl() noexcept : state_(0) {}
SharedMutexImpl(const SharedMutexImpl&) = delete; SharedMutexImpl(const SharedMutexImpl&) = delete;
SharedMutexImpl(SharedMutexImpl&&) = delete; SharedMutexImpl(SharedMutexImpl&&) = delete;
......
...@@ -20,7 +20,6 @@ ...@@ -20,7 +20,6 @@
#include <condition_variable> #include <condition_variable>
#include <mutex> #include <mutex>
#include <boost/intrusive/list.hpp> #include <boost/intrusive/list.hpp>
#include <folly/CallOnce.h>
#include <folly/Hash.h> #include <folly/Hash.h>
#include <folly/ScopeGuard.h> #include <folly/ScopeGuard.h>
...@@ -187,22 +186,15 @@ struct EmulatedFutexBucket { ...@@ -187,22 +186,15 @@ struct EmulatedFutexBucket {
boost::intrusive::list<EmulatedFutexWaitNode> waiters_; boost::intrusive::list<EmulatedFutexWaitNode> waiters_;
static const size_t kNumBuckets = 4096; static const size_t kNumBuckets = 4096;
static EmulatedFutexBucket* gBuckets;
static folly::once_flag gBucketInit;
static EmulatedFutexBucket& bucketFor(void* addr) { static EmulatedFutexBucket& bucketFor(void* addr) {
folly::call_once(gBucketInit, [](){ static auto gBuckets = new EmulatedFutexBucket[kNumBuckets];
gBuckets = new EmulatedFutexBucket[kNumBuckets];
});
uint64_t mixedBits = folly::hash::twang_mix64( uint64_t mixedBits = folly::hash::twang_mix64(
reinterpret_cast<uintptr_t>(addr)); reinterpret_cast<uintptr_t>(addr));
return gBuckets[mixedBits % kNumBuckets]; return gBuckets[mixedBits % kNumBuckets];
} }
}; };
EmulatedFutexBucket* EmulatedFutexBucket::gBuckets;
folly::once_flag EmulatedFutexBucket::gBucketInit;
int emulatedFutexWake(void* addr, int count, uint32_t waitMask) { int emulatedFutexWake(void* addr, int count, uint32_t waitMask) {
auto& bucket = EmulatedFutexBucket::bucketFor(addr); auto& bucket = EmulatedFutexBucket::bucketFor(addr);
std::unique_lock<std::mutex> bucketLock(bucket.mutex_); std::unique_lock<std::mutex> bucketLock(bucket.mutex_);
......
...@@ -50,6 +50,23 @@ TEST(FollyCallOnce, Simple) { ...@@ -50,6 +50,23 @@ TEST(FollyCallOnce, Simple) {
ASSERT_EQ(1, out); 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) { TEST(FollyCallOnce, Stress) {
for (int i = 0; i < 100; ++i) { for (int i = 0; i < 100; ++i) {
folly::once_flag flag; folly::once_flag flag;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment