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

test_once

Summary:
[Folly] `test_once`, a way to check whether any call to `call_once` with a given `once_flag` has succeeded.

One example of use might be for exception-safety guarding object destruction when object construction is guarded by the `once_flag`, and when the user is interested in conserving per-object memory and wishes to avoid the extra 8-byte overhead of `std::optional`.

```lang=c++
template <typename T>
struct Lazy {
  folly::aligned_storage_for_t<T> storage;
  folly::once_flag once;

  ~Lazy() {
    if (folly::test_once(once)) {
      reinterpret_cast<T&>(storage).~T();
    }
  }

  template <typename... A>
  T& construct_or_fetch(A&&... a) {
    folly::call_once(once, [&] { new (&storage) T(std::forward<A>(a)...); });
    return reinterpret_cast<T&>(storage);
  }
};
```

Reviewed By: ovoietsa

Differential Revision: D13561365

fbshipit-source-id: 8376c154002f1546f099903c4dc6be94dd2def8e
parent 0c66e13e
......@@ -57,6 +57,19 @@ call_once(basic_once_flag<Mutex, Atom>& flag, F&& f, Args&&... args) {
flag.call_once(std::forward<F>(f), std::forward<Args>(args)...);
}
// test_once
//
// Tests whether any invocation to call_once with the given flag has succeeded.
//
// May help with space usage in certain esoteric scenarios compared with caller
// code tracking a separate and possibly-padded bool.
//
// Note: This is has no parallel in the std::once_flag interface.
template <typename Mutex, template <typename> class Atom>
FOLLY_ALWAYS_INLINE bool test_once(basic_once_flag<Mutex, Atom> const& flag) {
return flag.called_.load(std::memory_order_acquire);
}
// basic_once_flag
//
// The flag template to be used with call_once. Parameterizable by the mutex
......@@ -77,6 +90,9 @@ class basic_once_flag {
typename... Args>
friend void call_once(basic_once_flag<Mutex_, Atom_>&, F&&, Args&&...);
template <typename Mutex_, template <typename> class Atom_>
friend bool test_once(basic_once_flag<Mutex_, Atom_> const& flag);
template <typename F, typename... Args>
FOLLY_ALWAYS_INLINE void call_once(F&& f, Args&&... args) {
if (LIKELY(called_.load(std::memory_order_acquire))) {
......
......@@ -17,7 +17,9 @@
#include <deque>
#include <mutex>
#include <thread>
#include <utility>
#include <folly/Traits.h>
#include <folly/portability/GTest.h>
#include <folly/synchronization/CallOnce.h>
......@@ -42,8 +44,12 @@ TEST(FollyCallOnce, Simple) {
folly::once_flag flag;
auto fn = [&](int* outp) { ++*outp; };
int out = 0;
ASSERT_FALSE(folly::test_once(folly::as_const(flag)));
folly::call_once(flag, fn, &out);
ASSERT_TRUE(folly::test_once(folly::as_const(flag)));
ASSERT_EQ(1, out);
folly::call_once(flag, fn, &out);
ASSERT_TRUE(folly::test_once(folly::as_const(flag)));
ASSERT_EQ(1, out);
}
......@@ -59,8 +65,10 @@ TEST(FollyCallOnce, Exception) {
throw ExpectedException();
}),
ExpectedException);
ASSERT_FALSE(folly::test_once(folly::as_const(flag)));
EXPECT_EQ(1, numCalls);
folly::call_once(flag, [&] { ++numCalls; });
ASSERT_TRUE(folly::test_once(folly::as_const(flag)));
EXPECT_EQ(2, numCalls);
}
......@@ -72,3 +80,39 @@ TEST(FollyCallOnce, Stress) {
ASSERT_EQ(1, out);
}
}
namespace {
template <typename T>
struct Lazy {
folly::aligned_storage_for_t<T> storage;
folly::once_flag once;
~Lazy() {
if (folly::test_once(once)) {
reinterpret_cast<T&>(storage).~T();
}
}
template <typename... A>
T& construct_or_fetch(A&&... a) {
folly::call_once(once, [&] { new (&storage) T(std::forward<A>(a)...); });
return reinterpret_cast<T&>(storage);
}
};
struct MaybeRaise {
std::unique_ptr<int> check{std::make_unique<int>(7)};
explicit MaybeRaise(bool raise) {
if (raise) {
throw std::runtime_error("raise");
}
}
};
} // namespace
TEST(FollyCallOnce, Lazy) {
Lazy<MaybeRaise> lazy;
EXPECT_THROW(lazy.construct_or_fetch(true), std::runtime_error);
auto& num = *lazy.construct_or_fetch(false).check;
EXPECT_EQ(7, num);
}
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