2be96155129163caf9b27a3e9bff772dc76951c3
[folly.git] / folly / synchronization / test / SaturatingSemaphoreTest.cpp
1 /*
2  * Copyright 2017 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 <folly/synchronization/SaturatingSemaphore.h>
18 #include <folly/portability/GTest.h>
19 #include <folly/test/DeterministicSchedule.h>
20
21 /// Test helper functions
22
23 using folly::SaturatingSemaphore;
24 using DSched = folly::test::DeterministicSchedule;
25
26 template <bool MayBlock, template <typename> class Atom = std::atomic>
27 void run_basic_test() {
28   SaturatingSemaphore<MayBlock, Atom> f;
29   ASSERT_FALSE(f.try_wait());
30   ASSERT_FALSE(f.try_wait_until(
31       std::chrono::steady_clock::now() + std::chrono::microseconds(1)));
32   ASSERT_FALSE(f.try_wait_until(
33       std::chrono::steady_clock::now() + std::chrono::microseconds(1),
34       f.wait_options().pre_block(std::chrono::microseconds(1))));
35   f.post();
36   f.post();
37   f.wait();
38   f.wait(f.wait_options().pre_block(std::chrono::nanoseconds(100)));
39   ASSERT_TRUE(f.try_wait());
40   ASSERT_TRUE(f.try_wait_until(
41       std::chrono::steady_clock::now() + std::chrono::microseconds(1)));
42   f.wait();
43   f.reset();
44   ASSERT_FALSE(f.try_wait());
45 }
46
47 template <bool MayBlock, template <typename> class Atom = std::atomic>
48 void run_pingpong_test(int numRounds) {
49   using WF = SaturatingSemaphore<MayBlock, Atom>;
50   std::array<WF, 17> flags;
51   WF& a = flags[0];
52   WF& b = flags[16]; // different cache line
53   auto thr = DSched::thread([&] {
54     for (int i = 0; i < numRounds; ++i) {
55       a.try_wait();
56       a.wait();
57       a.reset();
58       b.post();
59     }
60   });
61   for (int i = 0; i < numRounds; ++i) {
62     a.post();
63     b.try_wait();
64     b.wait();
65     b.reset();
66   }
67   DSched::join(thr);
68 }
69
70 template <bool MayBlock, template <typename> class Atom = std::atomic>
71 void run_multi_poster_multi_waiter_test(int np, int nw) {
72   SaturatingSemaphore<MayBlock, Atom> f;
73   std::atomic<int> posted{0};
74   std::atomic<int> waited{0};
75   std::atomic<bool> go_post{false};
76   std::atomic<bool> go_wait{false};
77
78   std::vector<std::thread> prod(np);
79   std::vector<std::thread> cons(nw);
80   for (int i = 0; i < np; ++i) {
81     prod[i] = DSched::thread([&] {
82       while (!go_post.load()) {
83         /* spin */;
84       }
85       f.post();
86       posted.fetch_add(1);
87     });
88   }
89
90   for (int i = 0; i < nw; ++i) {
91     cons[i] = DSched::thread([&] {
92       ASSERT_FALSE(f.try_wait());
93       ASSERT_FALSE(f.try_wait_for(std::chrono::microseconds(1)));
94       ASSERT_FALSE(f.try_wait_until(
95           std::chrono::steady_clock::now() + std::chrono::microseconds(1)));
96       ASSERT_FALSE(f.try_wait_until(
97           std::chrono::steady_clock::now() + std::chrono::microseconds(1),
98           f.wait_options().pre_block(std::chrono::microseconds(0))));
99       waited.fetch_add(1);
100       while (!go_wait.load()) {
101         /* spin */;
102       }
103       ASSERT_TRUE(f.try_wait());
104       ASSERT_TRUE(f.try_wait_for(std::chrono::microseconds(1)));
105       ASSERT_TRUE(f.try_wait_until(
106           std::chrono::steady_clock::now() + std::chrono::microseconds(1)));
107       ASSERT_TRUE(f.try_wait_until(
108           std::chrono::steady_clock::now() + std::chrono::microseconds(1),
109           f.wait_options().pre_block(std::chrono::microseconds(0))));
110       f.wait();
111     });
112   }
113
114   while (waited.load() < nw) {
115     /* spin */;
116   }
117   go_post.store(true);
118   while (posted.load() < np) {
119     /* spin */;
120   }
121   go_wait.store(true);
122
123   for (auto& t : prod) {
124     DSched::join(t);
125   }
126   for (auto& t : cons) {
127     DSched::join(t);
128   }
129 }
130
131 /// Tests
132
133 TEST(SaturatingSemaphore, basic) {
134   run_basic_test<false>();
135   run_basic_test<true>();
136 }
137
138 TEST(SaturatingSemaphore, pingpong) {
139   run_pingpong_test<false>(1000);
140   run_pingpong_test<true>(1000);
141 }
142
143 TEST(SaturatingSemaphore, multi_poster_multi_waiter) {
144   run_multi_poster_multi_waiter_test<false>(1, 1);
145   run_multi_poster_multi_waiter_test<false>(1, 10);
146   run_multi_poster_multi_waiter_test<false>(10, 1);
147   run_multi_poster_multi_waiter_test<false>(10, 10);
148   run_multi_poster_multi_waiter_test<true>(1, 1);
149   run_multi_poster_multi_waiter_test<true>(1, 10);
150   run_multi_poster_multi_waiter_test<true>(10, 1);
151   run_multi_poster_multi_waiter_test<true>(10, 10);
152 }