]> git.proxmox.com Git - ceph.git/blame - ceph/src/seastar/tests/unit/closeable_test.cc
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / seastar / tests / unit / closeable_test.cc
CommitLineData
20effc67
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 * Copyright 2021 ScyllaDB
20 */
21
22#include <exception>
23
24#include <boost/range/irange.hpp>
25
26#include <seastar/testing/test_case.hh>
27#include <seastar/testing/thread_test_case.hh>
28
29#include <seastar/core/gate.hh>
30#include <seastar/util/closeable.hh>
31#include <seastar/core/loop.hh>
32
33using namespace seastar;
34
35class expected_exception : public std::runtime_error {
36public:
37 expected_exception() : runtime_error("expected") {}
38};
39
40SEASTAR_TEST_CASE(deferred_close_test) {
41 return do_with(gate(), 0, 42, [] (gate& g, int& count, int& expected) {
42 return async([&] {
43 auto close_gate = deferred_close(g);
44
45 for (auto i = 0; i < expected; i++) {
46 (void)with_gate(g, [&count] {
47 ++count;
48 });
49 }
50 }).then([&] {
51 // destroying close_gate should invoke g.close()
52 // and wait for all background continuations to complete
53 BOOST_REQUIRE(g.is_closed());
54 BOOST_REQUIRE_EQUAL(count, expected);
55 });
56 });
57}
58
1e59de90
TL
59SEASTAR_TEST_CASE(move_deferred_close_test) {
60 return do_with(gate(), [] (gate& g) {
61 return async([&] {
62 auto close_gate = make_shared(deferred_close(g));
63 // g.close() should not be called when deferred_close is moved away
64 BOOST_REQUIRE(!g.is_closed());
65 }).then([&] {
66 // Before this test is exercised, gate::close() would run into a
67 // assert failure when leaving previous continuation, if gate::close()
68 // is called twice, so this test only verifies the behavior with the
69 // release build.
70 BOOST_REQUIRE(g.is_closed());
71 });
72 });
73}
74
20effc67
TL
75SEASTAR_TEST_CASE(close_now_test) {
76 return do_with(gate(), 0, 42, [] (gate& g, int& count, int& expected) {
77 return async([&] {
78 auto close_gate = deferred_close(g);
79
80 for (auto i = 0; i < expected; i++) {
81 (void)with_gate(g, [&count] {
82 ++count;
83 });
84 }
85
86 close_gate.close_now();
87 BOOST_REQUIRE(g.is_closed());
88 BOOST_REQUIRE_EQUAL(count, expected);
89 // gate must not be double-closed.
90 });
91 });
92}
93
1e59de90
TL
94SEASTAR_TEST_CASE(cancel_deferred_close_test) {
95 gate g;
96 {
97 auto close_gate = deferred_close(g);
98 close_gate.cancel();
99 }
100 g.check(); // should not throw
101 return make_ready_future<>();
102}
103
20effc67
TL
104SEASTAR_TEST_CASE(with_closeable_test) {
105 return do_with(0, 42, [] (int& count, int& expected) {
106 return with_closeable(gate(), [&] (gate& g) {
107 for (auto i = 0; i < expected; i++) {
108 (void)with_gate(g, [&count] {
109 ++count;
110 });
111 }
112 return 17;
113 }).then([&] (int res) {
114 // res should be returned by the function called
115 // by with_closeable.
116 BOOST_REQUIRE_EQUAL(res, 17);
117 // closing the gate should wait for
118 // all background continuations to complete
119 BOOST_REQUIRE_EQUAL(count, expected);
120 });
121 });
122}
123
124SEASTAR_TEST_CASE(with_closeable_exception_test) {
125 return do_with(0, 42, [] (int& count, int& expected) {
126 return with_closeable(gate(), [&] (gate& g) {
127 for (auto i = 0; i < expected; i++) {
128 (void)with_gate(g, [&count] {
129 ++count;
130 });
131 }
132 throw expected_exception();
133 }).handle_exception_type([&] (const expected_exception&) {
134 // closing the gate should also happen when func throws,
135 // waiting for all background continuations to complete
136 BOOST_REQUIRE_EQUAL(count, expected);
137 });
138 });
139}
140
141namespace {
142
143class count_stops {
144 int _count = -1;
145 int* _ptr = nullptr;
146public:
147 count_stops(int* ptr = nullptr) noexcept
148 : _ptr(ptr ? ptr : &_count)
149 {
150 *_ptr = 0;
151 }
152
153 count_stops(count_stops&& o) noexcept {
154 std::exchange(_count, o._count);
155 if (o._ptr == &o._count) {
156 _ptr = &_count;
157 } else {
158 std::exchange(_ptr, o._ptr);
159 }
160 }
161
162 future<> stop() noexcept {
163 ++*_ptr;
164 return make_ready_future<>();
165 }
166
167 int stopped() const noexcept {
168 return *_ptr;
169 }
170};
171
172} // anonymous namespace
173
1e59de90
TL
174SEASTAR_TEST_CASE(cancel_deferred_stop_test) {
175 count_stops cs;
176 {
177 auto stop = deferred_stop(cs);
178 stop.cancel();
179 }
180 BOOST_REQUIRE_EQUAL(cs.stopped(), 0);
181 return make_ready_future<>();
182}
183
20effc67
TL
184SEASTAR_TEST_CASE(deferred_stop_test) {
185 return do_with(count_stops(), [] (count_stops& cs) {
186 return async([&] {
187 auto stop_counting = deferred_stop(cs);
188 }).then([&] {
189 // cs.stop() should be called when stop_counting is destroyed
190 BOOST_REQUIRE_EQUAL(cs.stopped(), 1);
191 });
192 });
193}
194
1e59de90
TL
195SEASTAR_TEST_CASE(move_deferred_stop_test) {
196 return do_with(count_stops(), [] (count_stops& cs) {
197 return async([&] {
198 auto stop = make_shared(deferred_stop(cs));
199 }).then([&] {
200 // cs.stop() should be called once and only once
201 // when stop is destroyed
202 BOOST_REQUIRE_EQUAL(cs.stopped(), 1);
203 });
204 });
205}
206
20effc67
TL
207SEASTAR_TEST_CASE(stop_now_test) {
208 return do_with(count_stops(), [] (count_stops& cs) {
209 return async([&] {
210 auto stop_counting = deferred_stop(cs);
211
212 stop_counting.stop_now();
213 // cs.stop() should not be called again
214 // when stop_counting is destroyed
215 BOOST_REQUIRE_EQUAL(cs.stopped(), 1);
216 }).then([&] {
217 // cs.stop() should be called exactly once
218 BOOST_REQUIRE_EQUAL(cs.stopped(), 1);
219 });
220 });
221}
222
223SEASTAR_TEST_CASE(with_stoppable_test) {
224 return do_with(0, [] (int& stopped) {
225 return with_stoppable(count_stops(&stopped), [] (count_stops& cs) {
226 return 17;
227 }).then([&] (int res) {
228 // res should be returned by the function called
229 // by with_closeable.
230 BOOST_REQUIRE_EQUAL(res, 17);
231 // cs.stop() should be called before count_stops is destroyed
232 BOOST_REQUIRE_EQUAL(stopped, 1);
233 });
234 });
235}
236
237SEASTAR_TEST_CASE(with_stoppable_exception_test) {
238 return do_with(0, [] (int& stopped) {
239 return with_stoppable(count_stops(&stopped), [] (count_stops& cs) {
240 throw expected_exception();
241 }).handle_exception_type([&] (const expected_exception&) {
242 // cs.stop() should be called before count_stops is destroyed
243 // also when func throws
244 BOOST_REQUIRE_EQUAL(stopped, 1);
245 });
246 });
247}
248
1e59de90
TL
249SEASTAR_THREAD_TEST_CASE(move_open_gate_test) {
250 gate g1;
251 g1.enter();
252 // move an open gate
253 gate g2 = std::move(g1);
254 // the state in g1 should be moved into g2
255 BOOST_CHECK_EQUAL(g1.get_count(), 0);
256 BOOST_REQUIRE_EQUAL(g2.get_count(), 1);
257 g2.leave();
258 g2.close().get();
259 BOOST_CHECK(!g1.is_closed());
260 BOOST_CHECK(g2.is_closed());
261}
262
263SEASTAR_THREAD_TEST_CASE(move_closing_gate_test) {
264 gate g1;
265 g1.enter();
266 auto fut = g1.close();
267 // move a closing gate
268 gate g2 = std::move(g1);
269 BOOST_CHECK_EQUAL(g1.get_count(), 0);
270 BOOST_REQUIRE_EQUAL(g2.get_count(), 1);
271 g2.leave();
272 fut.get();
273 BOOST_CHECK(!g1.is_closed());
274 BOOST_CHECK(g2.is_closed());
275}
276
277SEASTAR_THREAD_TEST_CASE(move_closed_gate_test) {
278 gate g1;
279 g1.close().get();
280 // move a closed gate
281 gate g2 = std::move(g1);
282 BOOST_CHECK_EQUAL(g1.get_count(), 0);
283 BOOST_CHECK_EQUAL(g2.get_count(), 0);
284 BOOST_CHECK(!g1.is_closed());
285 BOOST_CHECK(g2.is_closed());
286}
287
20effc67
TL
288SEASTAR_THREAD_TEST_CASE(gate_holder_basic_test) {
289 gate g;
290 auto gh = g.hold();
291 auto fut = g.close();
292 BOOST_CHECK(!fut.available());
293 gh.release();
294 fut.get();
295}
296
297SEASTAR_THREAD_TEST_CASE(gate_holder_closed_test) {
298 gate g;
299 g.close().get();
300 BOOST_REQUIRE_THROW(g.hold(), gate_closed_exception);
301}
302
303SEASTAR_THREAD_TEST_CASE(gate_holder_move_test) {
304 gate g;
305 auto gh0 = g.hold();
306 auto fut = g.close();
307 BOOST_CHECK(!fut.available());
308 auto gh1 = std::move(gh0);
309 BOOST_CHECK(!fut.available());
310 gh1.release();
311 fut.get();
312}
313
314SEASTAR_THREAD_TEST_CASE(gate_holder_copy_test) {
315 gate g;
316 auto gh0 = g.hold();
317 auto gh1 = gh0;
318 auto fut = g.close();
319 BOOST_CHECK(!fut.available());
320 gh0.release();
321 BOOST_CHECK(!fut.available());
322 gh1.release();
323 fut.get();
324}
325
326SEASTAR_THREAD_TEST_CASE(gate_holder_copy_and_move_test) {
327 gate g0;
328 auto gh00 = g0.hold();
329 auto gh01 = gh00;
330 auto fut0 = g0.close();
331 BOOST_CHECK(!fut0.available());
332 gate g1;
333 auto gh1 = g1.hold();
334 auto fut1 = g1.close();
335 BOOST_CHECK(!fut1.available());
336 gh01.release();
337 BOOST_CHECK(!fut0.available());
338 BOOST_CHECK(!fut1.available());
339 gh00 = std::move(gh1);
340 fut0.get();
341 BOOST_CHECK(!fut1.available());
342 gh00.release();
343 fut1.get();
344}
345
346SEASTAR_THREAD_TEST_CASE(gate_holder_copy_after_close_test) {
347 gate g;
348 auto gh0 = g.hold();
349 auto fut = g.close();
350 BOOST_CHECK(g.is_closed());
351 gate::holder gh1 = gh0;
352 BOOST_CHECK(!fut.available());
353 gh0.release();
354 BOOST_CHECK(!fut.available());
355 gh1.release();
356 fut.get();
357}
358
359SEASTAR_TEST_CASE(gate_holder_parallel_copy_test) {
360 constexpr int expected = 42;
361 return do_with(0, [expected] (int& count) {
362 return with_closeable(gate(), [&] (gate& g) {
363 auto gh = g.hold();
364 // Copying the gate::holder in the lambda below should keep it open
365 // until all instances complete
366 (void)parallel_for_each(boost::irange(0, expected), [&count, gh = gh] (int) {
367 count++;
368 return make_ready_future<>();
369 });
370 return 17;
371 }).then([&, expected] (int res) {
372 // res should be returned by the function called
373 // by with_closeable.
374 BOOST_REQUIRE_EQUAL(res, 17);
375 // closing the gate should wait for
376 // all background continuations to complete
377 BOOST_REQUIRE_EQUAL(count, expected);
378 });
379 });
380}