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 (C) 2017 ScyllaDB Ltd.
26 #include <seastar/core/thread.hh>
27 #include <seastar/testing/test_case.hh>
28 #include <seastar/testing/thread_test_case.hh>
29 #include <seastar/testing/test_runner.hh>
30 #include <seastar/core/execution_stage.hh>
31 #include <seastar/core/sleep.hh>
33 using namespace std::chrono_literals
;
35 using namespace seastar
;
37 SEASTAR_TEST_CASE(test_create_stage_from_lvalue_function_object
) {
38 return seastar::async([] {
39 auto dont_move
= [obj
= make_shared
<int>(53)] { return *obj
; };
40 auto stage
= seastar::make_execution_stage("test", dont_move
);
41 BOOST_REQUIRE_EQUAL(stage().get0(), 53);
42 BOOST_REQUIRE_EQUAL(dont_move(), 53);
46 SEASTAR_TEST_CASE(test_create_stage_from_rvalue_function_object
) {
47 return seastar::async([] {
48 auto dont_copy
= [obj
= std::make_unique
<int>(42)] { return *obj
; };
49 auto stage
= seastar::make_execution_stage("test", std::move(dont_copy
));
50 BOOST_REQUIRE_EQUAL(stage().get0(), 42);
58 SEASTAR_TEST_CASE(test_create_stage_from_function
) {
59 return seastar::async([] {
60 auto stage
= seastar::make_execution_stage("test", func
);
61 BOOST_REQUIRE_EQUAL(stage().get0(), 64);
65 template<typename Function
, typename Verify
>
66 void test_simple_execution_stage(Function
&& func
, Verify
&& verify
) {
67 auto stage
= seastar::make_execution_stage("test", std::forward
<Function
>(func
));
70 std::default_random_engine
& gen
= testing::local_random_engine
;
71 std::uniform_int_distribution
<> dist(0, 100'000);
72 std::generate_n(std::back_inserter(vs
), 1'000, [&] { return dist(gen
); });
74 std::vector
<future
<int>> fs
;
76 fs
.emplace_back(stage(v
));
79 for (auto i
= 0u; i
< fs
.size(); i
++) {
80 verify(vs
[i
], std::move(fs
[i
]));
84 SEASTAR_TEST_CASE(test_simple_stage_returning_int
) {
85 return seastar::async([] {
86 test_simple_execution_stage([] (int x
) {
92 }, [] (int original
, future
<int> result
) {
94 BOOST_REQUIRE_EQUAL(original
* 2, result
.get0());
96 BOOST_REQUIRE_EXCEPTION(result
.get0(), int, [&] (int v
) { return original
== v
; });
102 SEASTAR_TEST_CASE(test_simple_stage_returning_future_int
) {
103 return seastar::async([] {
104 test_simple_execution_stage([] (int x
) {
106 return make_ready_future
<int>(x
* 2);
108 return make_exception_future
<int>(x
);
110 }, [] (int original
, future
<int> result
) {
112 BOOST_REQUIRE_EQUAL(original
* 2, result
.get0());
114 BOOST_REQUIRE_EXCEPTION(result
.get0(), int, [&] (int v
) { return original
== v
; });
121 void test_execution_stage_avoids_copy() {
122 auto stage
= seastar::make_execution_stage("test", [] (T obj
) {
123 return std::move(obj
);
131 SEASTAR_TEST_CASE(test_stage_moves_when_cannot_copy
) {
132 return seastar::async([] {
133 struct noncopyable_but_movable
{
134 noncopyable_but_movable() = default;
135 noncopyable_but_movable(const noncopyable_but_movable
&) = delete;
136 noncopyable_but_movable(noncopyable_but_movable
&&) = default;
139 test_execution_stage_avoids_copy
<noncopyable_but_movable
>();
143 SEASTAR_TEST_CASE(test_stage_prefers_move_to_copy
) {
144 return seastar::async([] {
145 struct copyable_and_movable
{
146 copyable_and_movable() = default;
147 copyable_and_movable(const copyable_and_movable
&) {
148 BOOST_FAIL("should not copy");
150 copyable_and_movable(copyable_and_movable
&&) = default;
153 test_execution_stage_avoids_copy
<copyable_and_movable
>();
157 SEASTAR_TEST_CASE(test_rref_decays_to_value
) {
158 return seastar::async([] {
159 auto stage
= seastar::make_execution_stage("test", [] (std::vector
<int>&& vec
) {
163 std::vector
<int> tmp
;
164 std::vector
<future
<size_t>> fs
;
165 for (auto i
= 0; i
< 100; i
++) {
167 fs
.emplace_back(stage(std::move(tmp
)));
168 tmp
= std::vector
<int>();
171 for (size_t i
= 0; i
< 100; i
++) {
172 BOOST_REQUIRE_EQUAL(fs
[i
].get0(), i
);
177 SEASTAR_TEST_CASE(test_lref_does_not_decay
) {
178 return seastar::async([] {
179 auto stage
= seastar::make_execution_stage("test", [] (int& v
) {
184 std::vector
<future
<>> fs
;
185 for (auto i
= 0; i
< 100; i
++) {
186 //fs.emplace_back(stage(value)); // should fail to compile
187 fs
.emplace_back(stage(seastar::ref(value
)));
190 for (auto&& f
: fs
) {
193 BOOST_REQUIRE_EQUAL(value
, 100);
197 SEASTAR_TEST_CASE(test_explicit_reference_wrapper_is_not_unwrapped
) {
198 return seastar::async([] {
199 auto stage
= seastar::make_execution_stage("test", [] (seastar::reference_wrapper
<int> v
) {
204 std::vector
<future
<>> fs
;
205 for (auto i
= 0; i
< 100; i
++) {
206 //fs.emplace_back(stage(value)); // should fail to compile
207 fs
.emplace_back(stage(seastar::ref(value
)));
210 for (auto&& f
: fs
) {
213 BOOST_REQUIRE_EQUAL(value
, 100);
217 SEASTAR_TEST_CASE(test_function_is_class_member
) {
218 return seastar::async([] {
222 return std::exchange(value
, x
);
226 auto stage
= seastar::make_execution_stage("test", &foo::member
);
229 std::vector
<future
<int>> fs
;
230 for (auto i
= 0; i
< 100; i
++) {
231 fs
.emplace_back(stage(&object
, i
));
234 for (auto i
= 0; i
< 100; i
++) {
235 BOOST_REQUIRE_EQUAL(fs
[i
].get0(), i
- 1);
237 BOOST_REQUIRE_EQUAL(object
.value
, 99);
241 SEASTAR_TEST_CASE(test_function_is_const_class_member
) {
242 return seastar::async([] {
249 auto stage
= seastar::make_execution_stage("test", &foo::member
);
252 BOOST_REQUIRE_EQUAL(stage(&object
).get0(), 999);
256 SEASTAR_TEST_CASE(test_stage_stats
) {
257 return seastar::async([] {
258 auto stage
= seastar::make_execution_stage("test", [] { });
260 BOOST_REQUIRE_EQUAL(stage
.get_stats().function_calls_enqueued
, 0u);
261 BOOST_REQUIRE_EQUAL(stage
.get_stats().function_calls_executed
, 0u);
263 auto fs
= std::vector
<future
<>>();
264 static constexpr auto call_count
= 53u;
265 for (auto i
= 0u; i
< call_count
; i
++) {
266 fs
.emplace_back(stage());
269 BOOST_REQUIRE_EQUAL(stage
.get_stats().function_calls_enqueued
, call_count
);
271 for (auto i
= 0u; i
< call_count
; i
++) {
273 BOOST_REQUIRE_GE(stage
.get_stats().tasks_scheduled
, 1u);
274 BOOST_REQUIRE_GE(stage
.get_stats().function_calls_executed
, i
);
276 BOOST_REQUIRE_EQUAL(stage
.get_stats().function_calls_executed
, call_count
);
280 SEASTAR_TEST_CASE(test_unique_stage_names_are_enforced
) {
281 return seastar::async([] {
283 auto stage
= seastar::make_execution_stage("test", [] {});
284 BOOST_REQUIRE_THROW(seastar::make_execution_stage("test", [] {}), std::invalid_argument
);
288 auto stage
= seastar::make_execution_stage("test", [] {});
293 SEASTAR_THREAD_TEST_CASE(test_inheriting_concrete_execution_stage
) {
294 auto sg1
= seastar::create_scheduling_group("sg1", 300).get0();
295 auto ksg1
= seastar::defer([&] { seastar::destroy_scheduling_group(sg1
).get(); });
296 auto sg2
= seastar::create_scheduling_group("sg2", 100).get0();
297 auto ksg2
= seastar::defer([&] { seastar::destroy_scheduling_group(sg2
).get(); });
298 auto check_sg
= [] (seastar::scheduling_group sg
) {
299 BOOST_REQUIRE(seastar::current_scheduling_group() == sg
);
301 auto es
= seastar::inheriting_concrete_execution_stage
<void, seastar::scheduling_group
>("stage", check_sg
);
302 auto make_attr
= [] (scheduling_group sg
) {
303 seastar::thread_attributes a
;
308 auto make_test_thread
= [&] (scheduling_group sg
) {
309 return seastar::thread(make_attr(sg
), [&, sg
] {
311 es(sg
).get(); // will check if executed with same sg
315 auto th1
= make_test_thread(sg1
);
316 auto th2
= make_test_thread(sg2
);
317 seastar::sleep(10ms
).get();
325 SEASTAR_THREAD_TEST_CASE(test_inheriting_concrete_execution_stage_reference_parameters
) {
326 // mostly a compile test, but take the opportunity to test that passing
327 // by reference preserves the address
328 auto check_ref
= [] (a_struct
& ref
, a_struct
* ptr
) {
329 BOOST_REQUIRE_EQUAL(&ref
, ptr
);
331 auto es
= seastar::inheriting_concrete_execution_stage
<void, a_struct
&, a_struct
*>("stage", check_ref
);
333 es(seastar::ref(obj
), &obj
).get();