]> git.proxmox.com Git - ceph.git/blob - ceph/src/seastar/tests/unit/execution_stage_test.cc
import 15.2.0 Octopus source
[ceph.git] / ceph / src / seastar / tests / unit / execution_stage_test.cc
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 (C) 2017 ScyllaDB Ltd.
20 */
21
22 #include <algorithm>
23 #include <vector>
24 #include <chrono>
25
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>
32
33 using namespace std::chrono_literals;
34
35 using namespace seastar;
36
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);
43 });
44 }
45
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);
51 });
52 }
53
54 int func() {
55 return 64;
56 }
57
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);
62 });
63 }
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));
68
69 std::vector<int> vs;
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); });
73
74 std::vector<future<int>> fs;
75 for (auto v : vs) {
76 fs.emplace_back(stage(v));
77 }
78
79 for (auto i = 0u; i < fs.size(); i++) {
80 verify(vs[i], std::move(fs[i]));
81 }
82 }
83
84 SEASTAR_TEST_CASE(test_simple_stage_returning_int) {
85 return seastar::async([] {
86 test_simple_execution_stage([] (int x) {
87 if (x % 2) {
88 return x * 2;
89 } else {
90 throw x;
91 }
92 }, [] (int original, future<int> result) {
93 if (original % 2) {
94 BOOST_REQUIRE_EQUAL(original * 2, result.get0());
95 } else {
96 BOOST_REQUIRE_EXCEPTION(result.get0(), int, [&] (int v) { return original == v; });
97 }
98 });
99 });
100 }
101
102 SEASTAR_TEST_CASE(test_simple_stage_returning_future_int) {
103 return seastar::async([] {
104 test_simple_execution_stage([] (int x) {
105 if (x % 2) {
106 return make_ready_future<int>(x * 2);
107 } else {
108 return make_exception_future<int>(x);
109 }
110 }, [] (int original, future<int> result) {
111 if (original % 2) {
112 BOOST_REQUIRE_EQUAL(original * 2, result.get0());
113 } else {
114 BOOST_REQUIRE_EXCEPTION(result.get0(), int, [&] (int v) { return original == v; });
115 }
116 });
117 });
118 }
119
120 template<typename T>
121 void test_execution_stage_avoids_copy() {
122 auto stage = seastar::make_execution_stage("test", [] (T obj) {
123 return std::move(obj);
124 });
125
126 auto f = stage(T());
127 T obj = f.get0();
128 (void)obj;
129 }
130
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;
137 };
138
139 test_execution_stage_avoids_copy<noncopyable_but_movable>();
140 });
141 }
142
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");
149 }
150 copyable_and_movable(copyable_and_movable&&) = default;
151 };
152
153 test_execution_stage_avoids_copy<copyable_and_movable>();
154 });
155 }
156
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) {
160 return vec.size();
161 });
162
163 std::vector<int> tmp;
164 std::vector<future<size_t>> fs;
165 for (auto i = 0; i < 100; i++) {
166 tmp.resize(i);
167 fs.emplace_back(stage(std::move(tmp)));
168 tmp = std::vector<int>();
169 }
170
171 for (size_t i = 0; i < 100; i++) {
172 BOOST_REQUIRE_EQUAL(fs[i].get0(), i);
173 }
174 });
175 }
176
177 SEASTAR_TEST_CASE(test_lref_does_not_decay) {
178 return seastar::async([] {
179 auto stage = seastar::make_execution_stage("test", [] (int& v) {
180 v++;
181 });
182
183 int value = 0;
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)));
188 }
189
190 for (auto&& f : fs) {
191 f.get();
192 }
193 BOOST_REQUIRE_EQUAL(value, 100);
194 });
195 }
196
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) {
200 v.get()++;
201 });
202
203 int value = 0;
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)));
208 }
209
210 for (auto&& f : fs) {
211 f.get();
212 }
213 BOOST_REQUIRE_EQUAL(value, 100);
214 });
215 }
216
217 SEASTAR_TEST_CASE(test_function_is_class_member) {
218 return seastar::async([] {
219 struct foo {
220 int value = -1;
221 int member(int x) {
222 return std::exchange(value, x);
223 }
224 };
225
226 auto stage = seastar::make_execution_stage("test", &foo::member);
227
228 foo object;
229 std::vector<future<int>> fs;
230 for (auto i = 0; i < 100; i++) {
231 fs.emplace_back(stage(&object, i));
232 }
233
234 for (auto i = 0; i < 100; i++) {
235 BOOST_REQUIRE_EQUAL(fs[i].get0(), i - 1);
236 }
237 BOOST_REQUIRE_EQUAL(object.value, 99);
238 });
239 }
240
241 SEASTAR_TEST_CASE(test_function_is_const_class_member) {
242 return seastar::async([] {
243 struct foo {
244 int value = 999;
245 int member() const {
246 return value;
247 }
248 };
249 auto stage = seastar::make_execution_stage("test", &foo::member);
250
251 const foo object;
252 BOOST_REQUIRE_EQUAL(stage(&object).get0(), 999);
253 });
254 }
255
256 SEASTAR_TEST_CASE(test_stage_stats) {
257 return seastar::async([] {
258 auto stage = seastar::make_execution_stage("test", [] { });
259
260 BOOST_REQUIRE_EQUAL(stage.get_stats().function_calls_enqueued, 0u);
261 BOOST_REQUIRE_EQUAL(stage.get_stats().function_calls_executed, 0u);
262
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());
267 }
268
269 BOOST_REQUIRE_EQUAL(stage.get_stats().function_calls_enqueued, call_count);
270
271 for (auto i = 0u; i < call_count; i++) {
272 fs[i].get();
273 BOOST_REQUIRE_GE(stage.get_stats().tasks_scheduled, 1u);
274 BOOST_REQUIRE_GE(stage.get_stats().function_calls_executed, i);
275 }
276 BOOST_REQUIRE_EQUAL(stage.get_stats().function_calls_executed, call_count);
277 });
278 }
279
280 SEASTAR_TEST_CASE(test_unique_stage_names_are_enforced) {
281 return seastar::async([] {
282 {
283 auto stage = seastar::make_execution_stage("test", [] {});
284 BOOST_REQUIRE_THROW(seastar::make_execution_stage("test", [] {}), std::invalid_argument);
285 stage().get();
286 }
287
288 auto stage = seastar::make_execution_stage("test", [] {});
289 stage().get();
290 });
291 }
292
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);
300 };
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;
304 a.sched_group = sg;
305 return a;
306 };
307 bool done = false;
308 auto make_test_thread = [&] (scheduling_group sg) {
309 return seastar::thread(make_attr(sg), [&, sg] {
310 while (!done) {
311 es(sg).get(); // will check if executed with same sg
312 };
313 });
314 };
315 auto th1 = make_test_thread(sg1);
316 auto th2 = make_test_thread(sg2);
317 seastar::sleep(10ms).get();
318 done = true;
319 th1.join().get();
320 th2.join().get();
321 }
322
323 struct a_struct {};
324
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);
330 };
331 auto es = seastar::inheriting_concrete_execution_stage<void, a_struct&, a_struct*>("stage", check_ref);
332 a_struct obj;
333 es(seastar::ref(obj), &obj).get();
334 }