2 * Copyright 2017-present Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include <folly/synchronization/SaturatingSemaphore.h>
18 #include <folly/portability/GTest.h>
19 #include <folly/test/DeterministicSchedule.h>
21 /// Test helper functions
23 using folly::SaturatingSemaphore;
24 using DSched = folly::test::DeterministicSchedule;
26 template <bool MayBlock, template <typename> class Atom = std::atomic>
27 void run_basic_test() {
28 SaturatingSemaphore<MayBlock, Atom> f;
29 ASSERT_FALSE(f.ready());
30 ASSERT_FALSE(f.try_wait());
31 ASSERT_FALSE(f.try_wait_until(
32 std::chrono::steady_clock::now() + std::chrono::microseconds(1)));
33 ASSERT_FALSE(f.try_wait_until(
34 std::chrono::steady_clock::now() + std::chrono::microseconds(1),
35 f.wait_options().spin_max(std::chrono::microseconds(1))));
39 f.wait(f.wait_options().spin_max(std::chrono::nanoseconds(100)));
40 ASSERT_TRUE(f.ready());
41 ASSERT_TRUE(f.try_wait());
42 ASSERT_TRUE(f.try_wait_until(
43 std::chrono::steady_clock::now() + std::chrono::microseconds(1)));
46 ASSERT_FALSE(f.try_wait());
49 template <bool MayBlock, template <typename> class Atom = std::atomic>
50 void run_pingpong_test(int numRounds) {
51 using WF = SaturatingSemaphore<MayBlock, Atom>;
52 std::array<WF, 17> flags;
54 WF& b = flags[16]; // different cache line
55 auto thr = DSched::thread([&] {
56 for (int i = 0; i < numRounds; ++i) {
63 for (int i = 0; i < numRounds; ++i) {
72 template <bool MayBlock, template <typename> class Atom = std::atomic>
73 void run_multi_poster_multi_waiter_test(int np, int nw) {
74 SaturatingSemaphore<MayBlock, Atom> f;
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};
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()) {
92 for (int i = 0; i < nw; ++i) {
93 cons[i] = DSched::thread([&] {
94 ASSERT_FALSE(f.ready());
95 ASSERT_FALSE(f.try_wait());
96 ASSERT_FALSE(f.try_wait_for(std::chrono::microseconds(1)));
97 ASSERT_FALSE(f.try_wait_until(
98 std::chrono::steady_clock::now() + std::chrono::microseconds(1)));
99 ASSERT_FALSE(f.try_wait_until(
100 std::chrono::steady_clock::now() + std::chrono::microseconds(1),
101 f.wait_options().spin_max(std::chrono::microseconds(0))));
103 while (!go_wait.load()) {
106 ASSERT_TRUE(f.ready());
107 ASSERT_TRUE(f.try_wait());
108 ASSERT_TRUE(f.try_wait_for(std::chrono::microseconds(1)));
109 ASSERT_TRUE(f.try_wait_until(
110 std::chrono::steady_clock::now() + std::chrono::microseconds(1)));
111 ASSERT_TRUE(f.try_wait_until(
112 std::chrono::steady_clock::now() + std::chrono::microseconds(1),
113 f.wait_options().spin_max(std::chrono::microseconds(0))));
118 while (waited.load() < nw) {
122 while (posted.load() < np) {
127 for (auto& t : prod) {
130 for (auto& t : cons) {
137 TEST(SaturatingSemaphore, basic) {
138 run_basic_test<false>();
139 run_basic_test<true>();
142 TEST(SaturatingSemaphore, pingpong) {
143 run_pingpong_test<false>(1000);
144 run_pingpong_test<true>(1000);
147 TEST(SaturatingSemaphore, multi_poster_multi_waiter) {
148 run_multi_poster_multi_waiter_test<false>(1, 1);
149 run_multi_poster_multi_waiter_test<false>(1, 10);
150 run_multi_poster_multi_waiter_test<false>(10, 1);
151 run_multi_poster_multi_waiter_test<false>(10, 10);
152 run_multi_poster_multi_waiter_test<true>(1, 1);
153 run_multi_poster_multi_waiter_test<true>(1, 10);
154 run_multi_poster_multi_waiter_test<true>(10, 1);
155 run_multi_poster_multi_waiter_test<true>(10, 10);