proxygen
AtomicNotificationTest.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2018-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  */
18 
19 #include <thread>
20 
21 using namespace std::literals;
22 
23 namespace folly {
24 
25 namespace {
26 template <typename Integer>
27 void run_atomic_wait_basic() {
28  auto&& atomic = std::atomic<Integer>{0};
29 
30  auto&& one = std::thread{[&]() {
31  while (true) {
32  atomic_wait(&atomic, Integer{0});
33  if (atomic.load() == 1) {
34  break;
35  }
36  }
37  }};
38 
39  atomic.store(1);
40  atomic_notify_one(&atomic);
41 
42  one.join();
43 }
44 
45 template <typename Integer>
46 void run_atomic_wait_until_with_timeout() {
47  auto&& atomic = std::atomic<Integer>{0};
48 
49  auto&& one = std::thread{[&]() {
50  auto deadline = std::chrono::steady_clock::now() + 10ms;
51  while (true) {
52  auto result = atomic_wait_until(&atomic, Integer{0}, deadline);
53 
54  // Any sort of spurious wakeup caused due to aliasing should not be
55  // changing the value in the futex word in proper usage, so we can
56  // assert that the value should remain unchanged
57  EXPECT_TRUE(!atomic.load());
58  if (result == std::cv_status::timeout) {
60  break;
61  }
62  }
63  }};
64 
65  one.join();
66 }
67 
68 template <typename Integer>
69 void run_atomic_wait_until_with_notification() {
70  auto&& atomic = std::atomic<Integer>{0};
71 
72  auto&& one = std::thread{[&]() {
73  while (true) {
74  auto result = atomic_wait_until(
76 
77  // note that it is safe to check if we returned from the
78  // atomic_wait_until() call due to a timeout, futex aliasing can cause
79  // spurious wakeups due to address reuse, but will not cause spurious
80  // timeouts, since a futex word has only one timeout on the futex queue,
81  // and does not inherit timeout from a previous futex at the same
82  // address
83  EXPECT_TRUE(result != std::cv_status::timeout);
84  break;
85  }
86 
87  EXPECT_EQ(atomic.load(), 1);
88  }};
89 
90  atomic.store(1);
91  atomic_notify_one(&atomic);
92  one.join();
93 }
94 
95 class SimpleBaton {
96  public:
97  void wait() {
98  auto lck = std::unique_lock<std::mutex>{mutex_};
99  while (!signalled_) {
100  cv_.wait(lck);
101  }
102 
104  }
105 
106  bool try_wait() {
107  auto lck = std::unique_lock<std::mutex>{mutex_};
108  return signalled_;
109  }
110 
111  void post() {
112  auto lck = std::unique_lock<std::mutex>{mutex_};
113  signalled_ = true;
114  cv_.notify_one();
115  }
116 
117  private:
119  std::condition_variable cv_;
120  bool signalled_{false};
121 };
122 
123 template <typename Integer>
124 void run_atomic_aliasing() {
126  auto&& one = SimpleBaton{};
127  auto&& two = SimpleBaton{};
128 
129  auto threadOne = std::thread{[&]() {
130  while (true) {
131  one.wait();
132  atomic_wait(atomic.get_pointer(), Integer{0});
133  if (atomic->load() == 1) {
134  break;
135  }
136  }
137  }};
138 
139  atomic->store(1);
140  one.post();
141  threadOne.join();
142 
143  // reset the atomic variable
144  atomic.reset();
145  atomic.emplace(0);
146 
147  auto threadTwo = std::thread{[&]() {
148  atomic_wait(atomic.get_pointer(), Integer{0});
149  two.post();
150  }};
151 
152  while (!two.try_wait()) {
153  atomic_notify_one(atomic.get_pointer());
154  }
155 
156  threadTwo.join();
157 }
158 } // namespace
159 
160 TEST(AtomicWait, Basic) {
161  run_atomic_wait_basic<std::uint32_t>();
162 }
163 
164 TEST(AtomicWait, BasicNonStandardWidths) {
165  run_atomic_wait_basic<std::uint8_t>();
166  run_atomic_wait_basic<std::uint16_t>();
167  run_atomic_wait_basic<std::uint64_t>();
168 }
169 
170 TEST(AtomicWait, AtomicWaitUntilTimeout) {
171  run_atomic_wait_until_with_timeout<std::uint32_t>();
172 }
173 
174 TEST(AtomicWait, AtomicWaitUntilTimeoutNonStandardWidths) {
175  run_atomic_wait_until_with_timeout<std::uint8_t>();
176  run_atomic_wait_until_with_timeout<std::uint16_t>();
177  run_atomic_wait_until_with_timeout<std::uint64_t>();
178 }
179 
180 TEST(AtomicWait, AtomicWaitUntilNotified) {
181  run_atomic_wait_until_with_notification<std::uint32_t>();
182 }
183 
184 TEST(AtomicWait, AtomicWaitUntilNotifiedNonStandardWidths) {
185  run_atomic_wait_until_with_notification<std::uint8_t>();
186  run_atomic_wait_until_with_notification<std::uint16_t>();
187  run_atomic_wait_until_with_notification<std::uint64_t>();
188 }
189 
190 TEST(AtomicWait, AtomicWaitAliasing) {
191  run_atomic_aliasing<std::uint32_t>();
192 }
193 
194 TEST(AtomicWait, AtomicWaitAliasingNonStandardWidths) {
195  run_atomic_aliasing<std::uint8_t>();
196  run_atomic_aliasing<std::uint16_t>();
197  run_atomic_aliasing<std::uint64_t>();
198 }
199 
200 } // namespace folly
std::mutex mutex_
LogLevel max
Definition: LogLevel.cpp:31
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
std::chrono::steady_clock::time_point now()
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
in_place_tag in_place(in_place_tag={})
Definition: Utility.h:235
std::condition_variable cv_
bool wait(Waiter *waiter, bool shouldSleep, Waiter *&next)
TEST(AtomicWait, AtomicWaitAliasingNonStandardWidths)
void atomic_wait(const std::atomic< Integer > *atomic, Integer expected)
#define EXPECT_TRUE(condition)
Definition: gtest.h:1859
void atomic_notify_one(const std::atomic< Integer > *atomic)
std::mutex mutex
std::cv_status atomic_wait_until(const std::atomic< Integer > *atomic, Integer expected, const std::chrono::time_point< Clock, Duration > &deadline)
bool signalled_