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(close_now_test
) {
60 return do_with(gate(), 0, 42, [] (gate
& g
, int& count
, int& expected
) {
62 auto close_gate
= deferred_close(g
);
64 for (auto i
= 0; i
< expected
; i
++) {
65 (void)with_gate(g
, [&count
] {
70 close_gate
.close_now();
71 BOOST_REQUIRE(g
.is_closed());
72 BOOST_REQUIRE_EQUAL(count
, expected
);
73 // gate must not be double-closed.
78 SEASTAR_TEST_CASE(with_closeable_test
) {
79 return do_with(0, 42, [] (int& count
, int& expected
) {
80 return with_closeable(gate(), [&] (gate
& g
) {
81 for (auto i
= 0; i
< expected
; i
++) {
82 (void)with_gate(g
, [&count
] {
87 }).then([&] (int res
) {
88 // res should be returned by the function called
90 BOOST_REQUIRE_EQUAL(res
, 17);
91 // closing the gate should wait for
92 // all background continuations to complete
93 BOOST_REQUIRE_EQUAL(count
, expected
);
98 SEASTAR_TEST_CASE(with_closeable_exception_test
) {
99 return do_with(0, 42, [] (int& count
, int& expected
) {
100 return with_closeable(gate(), [&] (gate
& g
) {
101 for (auto i
= 0; i
< expected
; i
++) {
102 (void)with_gate(g
, [&count
] {
106 throw expected_exception();
107 }).handle_exception_type([&] (const expected_exception
&) {
108 // closing the gate should also happen when func throws,
109 // waiting for all background continuations to complete
110 BOOST_REQUIRE_EQUAL(count
, expected
);
121 count_stops(int* ptr
= nullptr) noexcept
122 : _ptr(ptr
? ptr
: &_count
)
127 count_stops(count_stops
&& o
) noexcept
{
128 std::exchange(_count
, o
._count
);
129 if (o
._ptr
== &o
._count
) {
132 std::exchange(_ptr
, o
._ptr
);
136 future
<> stop() noexcept
{
138 return make_ready_future
<>();
141 int stopped() const noexcept
{
146 } // anonymous namespace
148 SEASTAR_TEST_CASE(deferred_stop_test
) {
149 return do_with(count_stops(), [] (count_stops
& cs
) {
151 auto stop_counting
= deferred_stop(cs
);
153 // cs.stop() should be called when stop_counting is destroyed
154 BOOST_REQUIRE_EQUAL(cs
.stopped(), 1);
159 SEASTAR_TEST_CASE(stop_now_test
) {
160 return do_with(count_stops(), [] (count_stops
& cs
) {
162 auto stop_counting
= deferred_stop(cs
);
164 stop_counting
.stop_now();
165 // cs.stop() should not be called again
166 // when stop_counting is destroyed
167 BOOST_REQUIRE_EQUAL(cs
.stopped(), 1);
169 // cs.stop() should be called exactly once
170 BOOST_REQUIRE_EQUAL(cs
.stopped(), 1);
175 SEASTAR_TEST_CASE(with_stoppable_test
) {
176 return do_with(0, [] (int& stopped
) {
177 return with_stoppable(count_stops(&stopped
), [] (count_stops
& cs
) {
179 }).then([&] (int res
) {
180 // res should be returned by the function called
181 // by with_closeable.
182 BOOST_REQUIRE_EQUAL(res
, 17);
183 // cs.stop() should be called before count_stops is destroyed
184 BOOST_REQUIRE_EQUAL(stopped
, 1);
189 SEASTAR_TEST_CASE(with_stoppable_exception_test
) {
190 return do_with(0, [] (int& stopped
) {
191 return with_stoppable(count_stops(&stopped
), [] (count_stops
& cs
) {
192 throw expected_exception();
193 }).handle_exception_type([&] (const expected_exception
&) {
194 // cs.stop() should be called before count_stops is destroyed
195 // also when func throws
196 BOOST_REQUIRE_EQUAL(stopped
, 1);
201 SEASTAR_THREAD_TEST_CASE(gate_holder_basic_test
) {
204 auto fut
= g
.close();
205 BOOST_CHECK(!fut
.available());
210 SEASTAR_THREAD_TEST_CASE(gate_holder_closed_test
) {
213 BOOST_REQUIRE_THROW(g
.hold(), gate_closed_exception
);
216 SEASTAR_THREAD_TEST_CASE(gate_holder_move_test
) {
219 auto fut
= g
.close();
220 BOOST_CHECK(!fut
.available());
221 auto gh1
= std::move(gh0
);
222 BOOST_CHECK(!fut
.available());
227 SEASTAR_THREAD_TEST_CASE(gate_holder_copy_test
) {
231 auto fut
= g
.close();
232 BOOST_CHECK(!fut
.available());
234 BOOST_CHECK(!fut
.available());
239 SEASTAR_THREAD_TEST_CASE(gate_holder_copy_and_move_test
) {
241 auto gh00
= g0
.hold();
243 auto fut0
= g0
.close();
244 BOOST_CHECK(!fut0
.available());
246 auto gh1
= g1
.hold();
247 auto fut1
= g1
.close();
248 BOOST_CHECK(!fut1
.available());
250 BOOST_CHECK(!fut0
.available());
251 BOOST_CHECK(!fut1
.available());
252 gh00
= std::move(gh1
);
254 BOOST_CHECK(!fut1
.available());
259 SEASTAR_THREAD_TEST_CASE(gate_holder_copy_after_close_test
) {
262 auto fut
= g
.close();
263 BOOST_CHECK(g
.is_closed());
264 gate::holder gh1
= gh0
;
265 BOOST_CHECK(!fut
.available());
267 BOOST_CHECK(!fut
.available());
272 SEASTAR_TEST_CASE(gate_holder_parallel_copy_test
) {
273 constexpr int expected
= 42;
274 return do_with(0, [expected
] (int& count
) {
275 return with_closeable(gate(), [&] (gate
& g
) {
277 // Copying the gate::holder in the lambda below should keep it open
278 // until all instances complete
279 (void)parallel_for_each(boost::irange(0, expected
), [&count
, gh
= gh
] (int) {
281 return make_ready_future
<>();
284 }).then([&, expected
] (int res
) {
285 // res should be returned by the function called
286 // by with_closeable.
287 BOOST_REQUIRE_EQUAL(res
, 17);
288 // closing the gate should wait for
289 // all background continuations to complete
290 BOOST_REQUIRE_EQUAL(count
, expected
);