proxygen
SaturatingSemaphoreTest.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2017-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 
20 
22 
25 
26 template <bool MayBlock, template <typename> class Atom = std::atomic>
29  ASSERT_FALSE(f.ready());
32  std::chrono::steady_clock::now() + std::chrono::microseconds(1)));
34  std::chrono::steady_clock::now() + std::chrono::microseconds(1),
35  f.wait_options().spin_max(std::chrono::microseconds(1))));
36  f.post();
37  f.post();
38  f.wait();
39  f.wait(f.wait_options().spin_max(std::chrono::nanoseconds(100)));
40  ASSERT_TRUE(f.ready());
41  ASSERT_TRUE(f.try_wait());
43  std::chrono::steady_clock::now() + std::chrono::microseconds(1)));
44  f.wait();
45  f.reset();
47 }
48 
49 template <bool MayBlock, template <typename> class Atom = std::atomic>
50 void run_pingpong_test(int numRounds) {
52  std::array<WF, 17> flags;
53  WF& a = flags[0];
54  WF& b = flags[16]; // different cache line
55  auto thr = DSched::thread([&] {
56  for (int i = 0; i < numRounds; ++i) {
57  a.try_wait();
58  a.wait();
59  a.reset();
60  b.post();
61  }
62  });
63  for (int i = 0; i < numRounds; ++i) {
64  a.post();
65  b.try_wait();
66  b.wait();
67  b.reset();
68  }
69  DSched::join(thr);
70 }
71 
72 template <bool MayBlock, template <typename> class Atom = std::atomic>
73 void run_multi_poster_multi_waiter_test(int np, int nw) {
75  std::atomic<int> posted{0};
76  std::atomic<int> waited{0};
77  std::atomic<bool> go_post{false};
78  std::atomic<bool> go_wait{false};
79 
80  std::vector<std::thread> prod(np);
81  std::vector<std::thread> cons(nw);
82  for (int i = 0; i < np; ++i) {
83  prod[i] = DSched::thread([&] {
84  while (!go_post.load()) {
85  /* spin */;
86  }
87  f.post();
88  posted.fetch_add(1);
89  });
90  }
91 
92  for (int i = 0; i < nw; ++i) {
93  cons[i] = DSched::thread([&] {
94  ASSERT_FALSE(f.ready());
96  ASSERT_FALSE(f.try_wait_for(std::chrono::microseconds(1)));
98  std::chrono::steady_clock::now() + std::chrono::microseconds(1)));
100  std::chrono::steady_clock::now() + std::chrono::microseconds(1),
101  f.wait_options().spin_max(std::chrono::microseconds(0))));
102  waited.fetch_add(1);
103  while (!go_wait.load()) {
104  /* spin */;
105  }
106  ASSERT_TRUE(f.ready());
107  ASSERT_TRUE(f.try_wait());
108  ASSERT_TRUE(f.try_wait_for(std::chrono::microseconds(1)));
110  std::chrono::steady_clock::now() + std::chrono::microseconds(1)));
112  std::chrono::steady_clock::now() + std::chrono::microseconds(1),
113  f.wait_options().spin_max(std::chrono::microseconds(0))));
114  f.wait();
115  });
116  }
117 
118  while (waited.load() < nw) {
119  /* spin */;
120  }
121  go_post.store(true);
122  while (posted.load() < np) {
123  /* spin */;
124  }
125  go_wait.store(true);
126 
127  for (auto& t : prod) {
128  DSched::join(t);
129  }
130  for (auto& t : cons) {
131  DSched::join(t);
132  }
133 }
134 
136 
137 TEST(SaturatingSemaphore, basic_spin_only) {
138  run_basic_test<false>();
139 }
140 
141 TEST(SaturatingSemaphore, basic_may_block) {
142  run_basic_test<true>();
143 }
144 
145 TEST(SaturatingSemaphore, pingpong_spin_only) {
146  run_pingpong_test<false>(1000);
147 }
148 
149 TEST(SaturatingSemaphore, pingpong_may_block) {
150  run_pingpong_test<true>(1000);
151 }
152 
153 TEST(SaturatingSemaphore, multi_poster_multi_waiter_spin_only) {
154  run_multi_poster_multi_waiter_test<false>(1, 1);
155  run_multi_poster_multi_waiter_test<false>(1, 10);
156  run_multi_poster_multi_waiter_test<false>(10, 1);
157  run_multi_poster_multi_waiter_test<false>(10, 10);
158 }
159 
160 TEST(SaturatingSemaphore, multi_poster_multi_waiter_may_block) {
161  run_multi_poster_multi_waiter_test<true>(1, 1);
162  run_multi_poster_multi_waiter_test<true>(1, 10);
163  run_multi_poster_multi_waiter_test<true>(10, 1);
164  run_multi_poster_multi_waiter_test<true>(10, 10);
165 }
FOLLY_ALWAYS_INLINE bool ready() const noexcept
FOLLY_ALWAYS_INLINE void wait(const WaitOptions &opt=wait_options()) noexcept
auto f
flags
Definition: http_parser.h:127
void run_multi_poster_multi_waiter_test(int np, int nw)
char b
TEST(SaturatingSemaphore, basic_spin_only)
Tests.
void run_basic_test()
std::chrono::steady_clock::time_point now()
void run_pingpong_test(int numRounds)
static std::thread thread(Func &&func, Args &&...args)
static FOLLY_ALWAYS_INLINE WaitOptions wait_options()
FOLLY_ALWAYS_INLINE void post() noexcept
FOLLY_ALWAYS_INLINE bool try_wait_until(const std::chrono::time_point< Clock, Duration > &deadline, const WaitOptions &opt=wait_options()) noexcept
#define Atom
char a
FOLLY_ALWAYS_INLINE bool try_wait_for(const std::chrono::duration< Rep, Period > &duration, const WaitOptions &opt=wait_options()) noexcept
FOLLY_ALWAYS_INLINE bool try_wait() noexcept
#define ASSERT_FALSE(condition)
Definition: gtest.h:1868
static void join(std::thread &child)
#define ASSERT_TRUE(condition)
Definition: gtest.h:1865