1/*
2 * Copyright 2016-present Facebook, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <deque>
18#include <mutex>
19#include <thread>
20#include <utility>
21
22#include <folly/Traits.h>
23#include <folly/portability/GTest.h>
24#include <folly/synchronization/CallOnce.h>
25
26static size_t const kNumThreads = 16;
27
28template <typename CallOnceFunc>
29void bm_impl(CallOnceFunc&& fn, size_t iters) {
30 std::deque<std::thread> threads;
31 for (size_t i = 0u; i < kNumThreads; ++i) {
32 threads.emplace_back([&fn, iters] {
33 for (size_t j = 0u; j < iters; ++j) {
34 fn();
35 }
36 });
37 }
38 for (std::thread& t : threads) {
39 t.join();
40 }
41}
42
43TEST(FollyCallOnce, Simple) {
44 folly::once_flag flag;
45 auto fn = [&](int* outp) { ++*outp; };
46 int out = 0;
47 ASSERT_FALSE(folly::test_once(folly::as_const(flag)));
48 folly::call_once(flag, fn, &out);
49 ASSERT_TRUE(folly::test_once(folly::as_const(flag)));
50 ASSERT_EQ(1, out);
51 folly::call_once(flag, fn, &out);
52 ASSERT_TRUE(folly::test_once(folly::as_const(flag)));
53 ASSERT_EQ(1, out);
54}
55
56TEST(FollyCallOnce, Exception) {
57 struct ExpectedException {};
58 folly::once_flag flag;
59 size_t numCalls = 0;
60 EXPECT_THROW(
61 folly::call_once(
62 flag,
63 [&] {
64 ++numCalls;
65 throw ExpectedException();
66 }),
67 ExpectedException);
68 ASSERT_FALSE(folly::test_once(folly::as_const(flag)));
69 EXPECT_EQ(1, numCalls);
70 folly::call_once(flag, [&] { ++numCalls; });
71 ASSERT_TRUE(folly::test_once(folly::as_const(flag)));
72 EXPECT_EQ(2, numCalls);
73}
74
75TEST(FollyCallOnce, Stress) {
76 for (int i = 0; i < 100; ++i) {
77 folly::once_flag flag;
78 int out = 0;
79 bm_impl([&] { folly::call_once(flag, [&] { ++out; }); }, 100);
80 ASSERT_EQ(1, out);
81 }
82}
83
84namespace {
85template <typename T>
86struct Lazy {
87 folly::aligned_storage_for_t<T> storage;
88 folly::once_flag once;
89
90 ~Lazy() {
91 if (folly::test_once(once)) {
92 reinterpret_cast<T&>(storage).~T();
93 }
94 }
95
96 template <typename... A>
97 T& construct_or_fetch(A&&... a) {
98 folly::call_once(once, [&] { new (&storage) T(std::forward<A>(a)...); });
99 return reinterpret_cast<T&>(storage);
100 }
101};
102struct MaybeRaise {
103 std::unique_ptr<int> check{std::make_unique<int>(7)};
104
105 explicit MaybeRaise(bool raise) {
106 if (raise) {
107 throw std::runtime_error("raise");
108 }
109 }
110};
111} // namespace
112
113TEST(FollyCallOnce, Lazy) {
114 Lazy<MaybeRaise> lazy;
115 EXPECT_THROW(lazy.construct_or_fetch(true), std::runtime_error);
116 auto& num = *lazy.construct_or_fetch(false).check;
117 EXPECT_EQ(7, num);
118}
119