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 | |
26 | static size_t const kNumThreads = 16; |
27 | |
28 | template <typename CallOnceFunc> |
29 | void 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 | |
43 | TEST(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 | |
56 | TEST(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 | |
75 | TEST(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 | |
84 | namespace { |
85 | template <typename T> |
86 | struct 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 | }; |
102 | struct 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 | |
113 | TEST(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 | |