2 * Copyright 2017 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.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))));
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)));
44 ASSERT_FALSE(f.try_wait());
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;
52 WF& b = flags[16]; // different cache line
53 auto thr = DSched::thread([&] {
54 for (int i = 0; i < numRounds; ++i) {
61 for (int i = 0; i < numRounds; ++i) {
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};
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()) {
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))));
100 while (!go_wait.load()) {
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))));
114 while (waited.load() < nw) {
118 while (posted.load() < np) {
123 for (auto& t : prod) {
126 for (auto& t : cons) {
133 TEST(SaturatingSemaphore, basic) {
134 run_basic_test<false>();
135 run_basic_test<true>();
138 TEST(SaturatingSemaphore, pingpong) {
139 run_pingpong_test<false>(1000);
140 run_pingpong_test<true>(1000);
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);