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.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
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
19 * Copyright 2021 ScyllaDB
24 #include <boost/range/irange.hpp>
26 #include <seastar/testing/test_case.hh>
27 #include <seastar/testing/thread_test_case.hh>
29 #include <seastar/core/gate.hh>
30 #include <seastar/util/closeable.hh>
31 #include <seastar/core/loop.hh>
33 using namespace seastar
;
35 class expected_exception
: public std::runtime_error
{
37 expected_exception() : runtime_error("expected") {}
40 SEASTAR_TEST_CASE(deferred_close_test
) {
41 return do_with(gate(), 0, 42, [] (gate
& g
, int& count
, int& expected
) {
43 auto close_gate
= deferred_close(g
);
45 for (auto i
= 0; i
< expected
; i
++) {
46 (void)with_gate(g
, [&count
] {
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
);
59 SEASTAR_TEST_CASE(move_deferred_close_test
) {
60 return do_with(gate(), [] (gate
& g
) {
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());
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
70 BOOST_REQUIRE(g
.is_closed());
75 SEASTAR_TEST_CASE(close_now_test
) {
76 return do_with(gate(), 0, 42, [] (gate
& g
, int& count
, int& expected
) {
78 auto close_gate
= deferred_close(g
);
80 for (auto i
= 0; i
< expected
; i
++) {
81 (void)with_gate(g
, [&count
] {
86 close_gate
.close_now();
87 BOOST_REQUIRE(g
.is_closed());
88 BOOST_REQUIRE_EQUAL(count
, expected
);
89 // gate must not be double-closed.
94 SEASTAR_TEST_CASE(cancel_deferred_close_test
) {
97 auto close_gate
= deferred_close(g
);
100 g
.check(); // should not throw
101 return make_ready_future
<>();
104 SEASTAR_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
] {
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
);
124 SEASTAR_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
] {
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
);
147 count_stops(int* ptr
= nullptr) noexcept
148 : _ptr(ptr
? ptr
: &_count
)
153 count_stops(count_stops
&& o
) noexcept
{
154 std::exchange(_count
, o
._count
);
155 if (o
._ptr
== &o
._count
) {
158 std::exchange(_ptr
, o
._ptr
);
162 future
<> stop() noexcept
{
164 return make_ready_future
<>();
167 int stopped() const noexcept
{
172 } // anonymous namespace
174 SEASTAR_TEST_CASE(cancel_deferred_stop_test
) {
177 auto stop
= deferred_stop(cs
);
180 BOOST_REQUIRE_EQUAL(cs
.stopped(), 0);
181 return make_ready_future
<>();
184 SEASTAR_TEST_CASE(deferred_stop_test
) {
185 return do_with(count_stops(), [] (count_stops
& cs
) {
187 auto stop_counting
= deferred_stop(cs
);
189 // cs.stop() should be called when stop_counting is destroyed
190 BOOST_REQUIRE_EQUAL(cs
.stopped(), 1);
195 SEASTAR_TEST_CASE(move_deferred_stop_test
) {
196 return do_with(count_stops(), [] (count_stops
& cs
) {
198 auto stop
= make_shared(deferred_stop(cs
));
200 // cs.stop() should be called once and only once
201 // when stop is destroyed
202 BOOST_REQUIRE_EQUAL(cs
.stopped(), 1);
207 SEASTAR_TEST_CASE(stop_now_test
) {
208 return do_with(count_stops(), [] (count_stops
& cs
) {
210 auto stop_counting
= deferred_stop(cs
);
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);
217 // cs.stop() should be called exactly once
218 BOOST_REQUIRE_EQUAL(cs
.stopped(), 1);
223 SEASTAR_TEST_CASE(with_stoppable_test
) {
224 return do_with(0, [] (int& stopped
) {
225 return with_stoppable(count_stops(&stopped
), [] (count_stops
& cs
) {
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);
237 SEASTAR_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);
249 SEASTAR_THREAD_TEST_CASE(move_open_gate_test
) {
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);
259 BOOST_CHECK(!g1
.is_closed());
260 BOOST_CHECK(g2
.is_closed());
263 SEASTAR_THREAD_TEST_CASE(move_closing_gate_test
) {
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);
273 BOOST_CHECK(!g1
.is_closed());
274 BOOST_CHECK(g2
.is_closed());
277 SEASTAR_THREAD_TEST_CASE(move_closed_gate_test
) {
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());
288 SEASTAR_THREAD_TEST_CASE(gate_holder_basic_test
) {
291 auto fut
= g
.close();
292 BOOST_CHECK(!fut
.available());
297 SEASTAR_THREAD_TEST_CASE(gate_holder_closed_test
) {
300 BOOST_REQUIRE_THROW(g
.hold(), gate_closed_exception
);
303 SEASTAR_THREAD_TEST_CASE(gate_holder_move_test
) {
306 auto fut
= g
.close();
307 BOOST_CHECK(!fut
.available());
308 auto gh1
= std::move(gh0
);
309 BOOST_CHECK(!fut
.available());
314 SEASTAR_THREAD_TEST_CASE(gate_holder_copy_test
) {
318 auto fut
= g
.close();
319 BOOST_CHECK(!fut
.available());
321 BOOST_CHECK(!fut
.available());
326 SEASTAR_THREAD_TEST_CASE(gate_holder_copy_and_move_test
) {
328 auto gh00
= g0
.hold();
330 auto fut0
= g0
.close();
331 BOOST_CHECK(!fut0
.available());
333 auto gh1
= g1
.hold();
334 auto fut1
= g1
.close();
335 BOOST_CHECK(!fut1
.available());
337 BOOST_CHECK(!fut0
.available());
338 BOOST_CHECK(!fut1
.available());
339 gh00
= std::move(gh1
);
341 BOOST_CHECK(!fut1
.available());
346 SEASTAR_THREAD_TEST_CASE(gate_holder_copy_after_close_test
) {
349 auto fut
= g
.close();
350 BOOST_CHECK(g
.is_closed());
351 gate::holder gh1
= gh0
;
352 BOOST_CHECK(!fut
.available());
354 BOOST_CHECK(!fut
.available());
359 SEASTAR_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
) {
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) {
368 return make_ready_future
<>();
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
);