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 @@
#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);
}
}
......@@ -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;
......
......@@ -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_);
......
......@@ -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;
......
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