Commit 4b6ebaeb authored by Yedidya Feldblum's avatar Yedidya Feldblum Committed by Facebook Github Bot

try_call_once, a nothrow variant of call_once

Summary: [Folly] `try_call_once`, a nothrow variant of `call_once`. Takes a `noexcept` function which returns `bool`; the function returning `false` indicates failure and that the `once_flag` is not to be marked as as set, while the function returning `true` indicates success and that the `once_flag` is to be marked as set.

Reviewed By: andriigrynenko, luciang

Differential Revision: D18742590

fbshipit-source-id: 3b3a6fab9a328f7540cc6fee1452a0f1367b6e0f
parent 47d6f494
...@@ -57,6 +57,30 @@ call_once(basic_once_flag<Mutex, Atom>& flag, F&& f, Args&&... args) { ...@@ -57,6 +57,30 @@ call_once(basic_once_flag<Mutex, Atom>& flag, F&& f, Args&&... args) {
flag.call_once(std::forward<F>(f), std::forward<Args>(args)...); flag.call_once(std::forward<F>(f), std::forward<Args>(args)...);
} }
// try_call_once
//
// Like call_once, but using a boolean return type to signal pass/fail rather
// than throwing exceptions.
//
// Returns true if any previous call to try_call_once with the same once_flag
// has returned true or if any previous call to call_once with the same
// once_flag has completed without throwing an exception or if the function
// passed as an argument returns true; otherwise returns false.
//
// Note: This has no parallel in the std::once_flag interface.
template <
typename Mutex,
template <typename> class Atom,
typename F,
typename... Args>
FOLLY_NODISCARD FOLLY_ALWAYS_INLINE bool try_call_once(
basic_once_flag<Mutex, Atom>& flag,
F&& f,
Args&&... args) noexcept {
static_assert(is_nothrow_invocable_v<F, Args...>, "must be noexcept");
return flag.try_call_once(std::forward<F>(f), std::forward<Args>(args)...);
}
// test_once // test_once
// //
// Tests whether any invocation to call_once with the given flag has succeeded. // Tests whether any invocation to call_once with the given flag has succeeded.
...@@ -112,6 +136,33 @@ class basic_once_flag { ...@@ -112,6 +136,33 @@ class basic_once_flag {
called_.store(true, std::memory_order_release); called_.store(true, std::memory_order_release);
} }
template <
typename Mutex_,
template <typename> class Atom_,
typename F,
typename... Args>
friend bool
try_call_once(basic_once_flag<Mutex_, Atom_>&, F&&, Args&&...) noexcept;
template <typename F, typename... Args>
FOLLY_ALWAYS_INLINE bool try_call_once(F&& f, Args&&... args) noexcept {
if (LIKELY(called_.load(std::memory_order_acquire))) {
return true;
}
return try_call_once_slow(std::forward<F>(f), std::forward<Args>(args)...);
}
template <typename F, typename... Args>
FOLLY_NOINLINE bool try_call_once_slow(F&& f, Args&&... args) noexcept {
std::lock_guard<Mutex> lock(mutex_);
if (called_.load(std::memory_order_relaxed)) {
return true;
}
auto const pass = invoke(std::forward<F>(f), std::forward<Args>(args)...);
called_.store(pass, std::memory_order_release);
return pass;
}
Atom<bool> called_{false}; Atom<bool> called_{false};
Mutex mutex_; Mutex mutex_;
}; };
......
...@@ -116,3 +116,11 @@ TEST(FollyCallOnce, Lazy) { ...@@ -116,3 +116,11 @@ TEST(FollyCallOnce, Lazy) {
auto& num = *lazy.construct_or_fetch(false).check; auto& num = *lazy.construct_or_fetch(false).check;
EXPECT_EQ(7, num); EXPECT_EQ(7, num);
} }
TEST(FollyTryCallOnce, example) {
folly::once_flag once;
EXPECT_FALSE(folly::try_call_once(once, []() noexcept { return false; }));
EXPECT_FALSE(folly::test_once(once));
EXPECT_TRUE(folly::try_call_once(once, []() noexcept { return true; }));
EXPECT_TRUE(folly::test_once(once));
}
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