]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
1 | /* |
2 | * This file is open source software, licensed to you under the terms | |
3 | * of the Apache License, Version 2.0 (the "License"). See the NOTICE file | |
4 | * distributed with this work for additional information regarding copyright | |
5 | * ownership. You may not use this file except in compliance with the License. | |
6 | * | |
7 | * You may obtain a copy of the License at | |
8 | * | |
9 | * http://www.apache.org/licenses/LICENSE-2.0 | |
10 | * | |
11 | * Unless required by applicable law or agreed to in writing, | |
12 | * software distributed under the License is distributed on an | |
13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
14 | * KIND, either express or implied. See the License for the | |
15 | * specific language governing permissions and limitations | |
16 | * under the License. | |
17 | */ | |
18 | ||
19 | /* | |
20 | * Copyright (C) 2015 Cloudius Systems, Ltd. | |
21 | */ | |
22 | ||
23 | #include <seastar/core/thread.hh> | |
24 | #include <seastar/core/do_with.hh> | |
25 | #include <seastar/testing/test_case.hh> | |
26 | #include <seastar/testing/thread_test_case.hh> | |
27 | #include <seastar/core/sstring.hh> | |
28 | #include <seastar/core/reactor.hh> | |
29 | #include <seastar/core/semaphore.hh> | |
30 | #include <seastar/core/do_with.hh> | |
31 | #include <seastar/core/future-util.hh> | |
32 | #include <seastar/core/sleep.hh> | |
33 | #include <seastar/core/shared_mutex.hh> | |
34 | #include <boost/range/irange.hpp> | |
35 | ||
36 | using namespace seastar; | |
37 | using namespace std::chrono_literals; | |
38 | ||
39 | ||
40 | SEASTAR_TEST_CASE(test_semaphore_consume) { | |
41 | semaphore sem(0); | |
42 | sem.consume(1); | |
43 | BOOST_REQUIRE_EQUAL(sem.current(), 0u); | |
44 | BOOST_REQUIRE_EQUAL(sem.waiters(), 0u); | |
45 | ||
46 | BOOST_REQUIRE_EQUAL(sem.try_wait(0), false); | |
47 | auto fut = sem.wait(1); | |
48 | BOOST_REQUIRE_EQUAL(fut.available(), false); | |
49 | BOOST_REQUIRE_EQUAL(sem.waiters(), 1u); | |
50 | sem.signal(2); | |
51 | BOOST_REQUIRE_EQUAL(sem.waiters(), 0u); | |
52 | return make_ready_future<>(); | |
53 | } | |
54 | ||
55 | SEASTAR_TEST_CASE(test_semaphore_1) { | |
56 | return do_with(std::make_pair(semaphore(0), 0), [] (std::pair<semaphore, int>& x) { | |
9f95a23c | 57 | (void)x.first.wait().then([&x] { |
11fdf7f2 TL |
58 | x.second++; |
59 | }); | |
60 | x.first.signal(); | |
61 | return sleep(10ms).then([&x] { | |
62 | BOOST_REQUIRE_EQUAL(x.second, 1); | |
63 | }); | |
64 | }); | |
65 | } | |
66 | ||
67 | SEASTAR_TEST_CASE(test_semaphore_2) { | |
68 | return do_with(std::make_pair(semaphore(0), 0), [] (std::pair<semaphore, int>& x) { | |
9f95a23c | 69 | (void)x.first.wait().then([&x] { |
11fdf7f2 TL |
70 | x.second++; |
71 | }); | |
72 | return sleep(10ms).then([&x] { | |
73 | BOOST_REQUIRE_EQUAL(x.second, 0); | |
74 | }); | |
75 | }); | |
76 | } | |
77 | ||
78 | SEASTAR_TEST_CASE(test_semaphore_timeout_1) { | |
79 | return do_with(std::make_pair(semaphore(0), 0), [] (std::pair<semaphore, int>& x) { | |
9f95a23c | 80 | (void)x.first.wait(100ms).then([&x] { |
11fdf7f2 TL |
81 | x.second++; |
82 | }); | |
9f95a23c | 83 | (void)sleep(3ms).then([&x] { |
11fdf7f2 TL |
84 | x.first.signal(); |
85 | }); | |
9f95a23c | 86 | return sleep(200ms).then([&x] { |
11fdf7f2 TL |
87 | BOOST_REQUIRE_EQUAL(x.second, 1); |
88 | }); | |
89 | }); | |
90 | } | |
91 | ||
92 | SEASTAR_TEST_CASE(test_semaphore_timeout_2) { | |
93 | return do_with(std::make_pair(semaphore(0), 0), [] (std::pair<semaphore, int>& x) { | |
9f95a23c | 94 | (void)x.first.wait(3ms).then([&x] { |
11fdf7f2 TL |
95 | x.second++; |
96 | }); | |
9f95a23c | 97 | (void)sleep(100ms).then([&x] { |
11fdf7f2 TL |
98 | x.first.signal(); |
99 | }); | |
9f95a23c | 100 | return sleep(200ms).then([&x] { |
11fdf7f2 TL |
101 | BOOST_REQUIRE_EQUAL(x.second, 0); |
102 | }); | |
103 | }); | |
104 | } | |
105 | ||
106 | SEASTAR_TEST_CASE(test_semaphore_mix_1) { | |
107 | return do_with(std::make_pair(semaphore(0), 0), [] (std::pair<semaphore, int>& x) { | |
9f95a23c | 108 | (void)x.first.wait(30ms).then([&x] { |
11fdf7f2 TL |
109 | x.second++; |
110 | }); | |
9f95a23c | 111 | (void)x.first.wait().then([&x] { |
11fdf7f2 TL |
112 | x.second = 10; |
113 | }); | |
9f95a23c | 114 | (void)sleep(100ms).then([&x] { |
11fdf7f2 TL |
115 | x.first.signal(); |
116 | }); | |
9f95a23c | 117 | return sleep(200ms).then([&x] { |
11fdf7f2 TL |
118 | BOOST_REQUIRE_EQUAL(x.second, 10); |
119 | }); | |
120 | }); | |
121 | } | |
122 | ||
123 | SEASTAR_TEST_CASE(test_broken_semaphore) { | |
124 | auto sem = make_lw_shared<semaphore>(0); | |
125 | struct oops {}; | |
126 | auto check_result = [sem] (future<> f) { | |
127 | try { | |
128 | f.get(); | |
129 | BOOST_FAIL("expecting exception"); | |
130 | } catch (oops& x) { | |
131 | // ok | |
132 | return make_ready_future<>(); | |
133 | } catch (...) { | |
134 | BOOST_FAIL("wrong exception seen"); | |
135 | } | |
136 | BOOST_FAIL("unreachable"); | |
137 | return make_ready_future<>(); | |
138 | }; | |
139 | auto ret = sem->wait().then_wrapped(check_result); | |
140 | sem->broken(oops()); | |
141 | return sem->wait().then_wrapped(check_result).then([ret = std::move(ret)] () mutable { | |
142 | return std::move(ret); | |
143 | }); | |
144 | } | |
145 | ||
146 | SEASTAR_TEST_CASE(test_shared_mutex_exclusive) { | |
147 | return do_with(shared_mutex(), unsigned(0), [] (shared_mutex& sm, unsigned& counter) { | |
148 | return parallel_for_each(boost::irange(0, 10), [&sm, &counter] (int idx) { | |
149 | return with_lock(sm, [&counter] { | |
150 | BOOST_REQUIRE_EQUAL(counter, 0u); | |
151 | ++counter; | |
152 | return sleep(10ms).then([&counter] { | |
153 | --counter; | |
154 | BOOST_REQUIRE_EQUAL(counter, 0u); | |
155 | }); | |
156 | }); | |
157 | }); | |
158 | }); | |
159 | } | |
160 | ||
161 | SEASTAR_TEST_CASE(test_shared_mutex_shared) { | |
162 | return do_with(shared_mutex(), unsigned(0), [] (shared_mutex& sm, unsigned& counter) { | |
163 | auto running_in_parallel = [&sm, &counter] (int instance) { | |
164 | return with_shared(sm, [&counter] { | |
165 | ++counter; | |
166 | return sleep(10ms).then([&counter] { | |
167 | bool was_parallel = counter != 0; | |
168 | --counter; | |
169 | return was_parallel; | |
170 | }); | |
171 | }); | |
172 | }; | |
173 | return map_reduce(boost::irange(0, 100), running_in_parallel, false, std::bit_or<bool>()).then([&counter] (bool result) { | |
174 | BOOST_REQUIRE_EQUAL(result, true); | |
175 | BOOST_REQUIRE_EQUAL(counter, 0u); | |
176 | }); | |
177 | }); | |
178 | } | |
179 | ||
180 | SEASTAR_TEST_CASE(test_shared_mutex_mixed) { | |
181 | return do_with(shared_mutex(), unsigned(0), [] (shared_mutex& sm, unsigned& counter) { | |
182 | auto running_in_parallel = [&sm, &counter] (int instance) { | |
183 | return with_shared(sm, [&counter] { | |
184 | ++counter; | |
185 | return sleep(10ms).then([&counter] { | |
186 | bool was_parallel = counter != 0; | |
187 | --counter; | |
188 | return was_parallel; | |
189 | }); | |
190 | }); | |
191 | }; | |
192 | auto running_alone = [&sm, &counter] (int instance) { | |
193 | return with_lock(sm, [&counter] { | |
194 | BOOST_REQUIRE_EQUAL(counter, 0u); | |
195 | ++counter; | |
196 | return sleep(10ms).then([&counter] { | |
197 | --counter; | |
198 | BOOST_REQUIRE_EQUAL(counter, 0u); | |
199 | return true; | |
200 | }); | |
201 | }); | |
202 | }; | |
203 | auto run = [running_in_parallel, running_alone] (int instance) { | |
204 | if (instance % 9 == 0) { | |
205 | return running_alone(instance); | |
206 | } else { | |
207 | return running_in_parallel(instance); | |
208 | } | |
209 | }; | |
210 | return map_reduce(boost::irange(0, 100), run, false, std::bit_or<bool>()).then([&counter] (bool result) { | |
211 | BOOST_REQUIRE_EQUAL(result, true); | |
212 | BOOST_REQUIRE_EQUAL(counter, 0u); | |
213 | }); | |
214 | }); | |
215 | } | |
216 | ||
217 | ||
218 | SEASTAR_TEST_CASE(test_with_semaphore) { | |
219 | return do_with(semaphore(1), 0, [] (semaphore& sem, int& counter) { | |
220 | return with_semaphore(sem, 1, [&counter] { | |
221 | ++counter; | |
222 | }).then([&counter, &sem] () { | |
223 | return with_semaphore(sem, 1, [&counter] { | |
224 | ++counter; | |
225 | throw 123; | |
226 | }).then_wrapped([&counter] (auto&& fut) { | |
227 | BOOST_REQUIRE_EQUAL(counter, 2); | |
228 | BOOST_REQUIRE(fut.failed()); | |
229 | fut.ignore_ready_future(); | |
230 | }); | |
231 | }); | |
232 | }); | |
233 | } | |
234 | ||
235 | SEASTAR_THREAD_TEST_CASE(test_semaphore_units_splitting) { | |
236 | auto sm = semaphore(2); | |
237 | auto units = get_units(sm, 2, 1min).get0(); | |
238 | { | |
239 | BOOST_REQUIRE_EQUAL(sm.available_units(), 0); | |
240 | auto split = units.split(1); | |
241 | BOOST_REQUIRE_EQUAL(sm.available_units(), 0); | |
242 | } | |
243 | BOOST_REQUIRE_EQUAL(sm.available_units(), 1); | |
244 | units.~semaphore_units(); | |
245 | units = get_units(sm, 2, 1min).get0(); | |
246 | BOOST_REQUIRE_EQUAL(sm.available_units(), 0); | |
247 | BOOST_REQUIRE_THROW(units.split(10), std::invalid_argument); | |
248 | BOOST_REQUIRE_EQUAL(sm.available_units(), 0); | |
249 | } | |
9f95a23c TL |
250 | |
251 | SEASTAR_THREAD_TEST_CASE(test_named_semaphore_error) { | |
252 | auto sem = make_lw_shared<named_semaphore>(0, named_semaphore_exception_factory{"name_of_the_semaphore"}); | |
253 | auto check_result = [sem] (future<> f) { | |
254 | try { | |
255 | f.get(); | |
256 | BOOST_FAIL("Expecting an exception"); | |
257 | } catch (broken_named_semaphore& ex) { | |
258 | BOOST_REQUIRE_NE(std::string(ex.what()).find("name_of_the_semaphore"), std::string::npos); | |
259 | } catch (...) { | |
260 | BOOST_FAIL("Expected an instance of broken_named_semaphore with proper semaphore name"); | |
261 | } | |
262 | return make_ready_future<>(); | |
263 | }; | |
264 | auto ret = sem->wait().then_wrapped(check_result); | |
265 | sem->broken(); | |
266 | sem->wait().then_wrapped(check_result).then([ret = std::move(ret)] () mutable { | |
267 | return std::move(ret); | |
268 | }).get(); | |
269 | } | |
270 | ||
271 | SEASTAR_THREAD_TEST_CASE(test_named_semaphore_timeout) { | |
272 | auto sem = make_lw_shared<named_semaphore>(0, named_semaphore_exception_factory{"name_of_the_semaphore"}); | |
273 | ||
274 | auto f = sem->wait(named_semaphore::clock::now() + 1ms, 1); | |
275 | try { | |
276 | f.get(); | |
277 | BOOST_FAIL("Expecting an exception"); | |
278 | } catch (named_semaphore_timed_out& ex) { | |
279 | BOOST_REQUIRE_NE(std::string(ex.what()).find("name_of_the_semaphore"), std::string::npos); | |
280 | } catch (...) { | |
281 | BOOST_FAIL("Expected an instance of named_semaphore_timed_out with proper semaphore name"); | |
282 | } | |
283 | } |