]>
Commit | Line | Data |
---|---|---|
11fdf7f2 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 (C) 2014 Cloudius Systems, Ltd. | |
20 | */ | |
21 | ||
20effc67 TL |
22 | #include <cstddef> |
23 | #include <exception> | |
11fdf7f2 TL |
24 | #include <seastar/testing/test_case.hh> |
25 | ||
f67539c2 | 26 | #include <seastar/core/reactor.hh> |
11fdf7f2 TL |
27 | #include <seastar/core/shared_ptr.hh> |
28 | #include <seastar/core/future-util.hh> | |
29 | #include <seastar/core/sleep.hh> | |
f67539c2 TL |
30 | #include <seastar/core/stream.hh> |
31 | #include <seastar/util/backtrace.hh> | |
11fdf7f2 TL |
32 | #include <seastar/core/do_with.hh> |
33 | #include <seastar/core/shared_future.hh> | |
f67539c2 | 34 | #include <seastar/core/manual_clock.hh> |
11fdf7f2 | 35 | #include <seastar/core/thread.hh> |
9f95a23c | 36 | #include <seastar/core/print.hh> |
20effc67 | 37 | #include <seastar/core/when_any.hh> |
1e59de90 | 38 | #include <seastar/core/when_all.hh> |
f67539c2 TL |
39 | #include <seastar/core/gate.hh> |
40 | #include <seastar/util/log.hh> | |
1e59de90 | 41 | #include <seastar/util/later.hh> |
11fdf7f2 | 42 | #include <boost/iterator/counting_iterator.hpp> |
9f95a23c | 43 | #include <seastar/testing/thread_test_case.hh> |
11fdf7f2 | 44 | |
f67539c2 TL |
45 | #include <boost/range/iterator_range.hpp> |
46 | #include <boost/range/irange.hpp> | |
47 | ||
48 | #include <seastar/core/internal/api-level.hh> | |
20effc67 TL |
49 | #include <stdexcept> |
50 | #include <unistd.h> | |
f67539c2 | 51 | |
11fdf7f2 TL |
52 | using namespace seastar; |
53 | using namespace std::chrono_literals; | |
54 | ||
f67539c2 TL |
55 | static_assert(std::is_nothrow_default_constructible_v<gate>, |
56 | "seastar::gate constructor must not throw"); | |
57 | static_assert(std::is_nothrow_move_constructible_v<gate>, | |
58 | "seastar::gate move constructor must not throw"); | |
59 | ||
60 | static_assert(std::is_nothrow_default_constructible_v<shared_future<>>); | |
61 | static_assert(std::is_nothrow_copy_constructible_v<shared_future<>>); | |
62 | static_assert(std::is_nothrow_move_constructible_v<shared_future<>>); | |
63 | ||
64 | static_assert(std::is_nothrow_move_constructible_v<shared_promise<>>); | |
65 | ||
66 | class expected_exception : public std::runtime_error { | |
11fdf7f2 TL |
67 | public: |
68 | expected_exception() : runtime_error("expected") {} | |
69 | }; | |
70 | ||
9f95a23c TL |
71 | #ifdef __clang__ |
72 | #pragma clang diagnostic push | |
73 | #pragma clang diagnostic ignored "-Wself-move" | |
74 | #endif | |
75 | SEASTAR_TEST_CASE(test_self_move) { | |
f67539c2 | 76 | future_state<std::tuple<std::unique_ptr<int>>> s1; |
9f95a23c TL |
77 | s1.set(std::make_unique<int>(42)); |
78 | s1 = std::move(s1); // no crash, but the value of s1 is not defined. | |
79 | ||
f67539c2 TL |
80 | #if SEASTAR_API_LEVEL < 5 |
81 | future_state<std::tuple<std::unique_ptr<int>>> s2; | |
82 | #else | |
9f95a23c | 83 | future_state<std::unique_ptr<int>> s2; |
f67539c2 | 84 | #endif |
9f95a23c TL |
85 | s2.set(std::make_unique<int>(42)); |
86 | std::swap(s2, s2); | |
f67539c2 | 87 | BOOST_REQUIRE_EQUAL(*std::move(s2).get0(), 42); |
9f95a23c TL |
88 | |
89 | promise<std::unique_ptr<int>> p1; | |
90 | p1.set_value(std::make_unique<int>(42)); | |
91 | p1 = std::move(p1); // no crash, but the value of p1 is not defined. | |
92 | ||
93 | promise<std::unique_ptr<int>> p2; | |
94 | p2.set_value(std::make_unique<int>(42)); | |
95 | std::swap(p2, p2); | |
96 | BOOST_REQUIRE_EQUAL(*p2.get_future().get0(), 42); | |
97 | ||
98 | auto f1 = make_ready_future<std::unique_ptr<int>>(std::make_unique<int>(42)); | |
99 | f1 = std::move(f1); // no crash, but the value of f1 is not defined. | |
100 | ||
101 | auto f2 = make_ready_future<std::unique_ptr<int>>(std::make_unique<int>(42)); | |
102 | std::swap(f2, f2); | |
103 | BOOST_REQUIRE_EQUAL(*f2.get0(), 42); | |
104 | ||
105 | return make_ready_future<>(); | |
106 | } | |
107 | #ifdef __clang__ | |
108 | #pragma clang diagnostic pop | |
109 | #endif | |
110 | ||
111 | static subscription<int> get_empty_subscription(std::function<future<> (int)> func) { | |
112 | stream<int> s; | |
113 | auto ret = s.listen(func); | |
114 | s.close(); | |
115 | return ret; | |
116 | } | |
117 | ||
118 | SEASTAR_TEST_CASE(test_stream) { | |
119 | auto sub = get_empty_subscription([](int x) { | |
120 | return make_ready_future<>(); | |
121 | }); | |
122 | return sub.done(); | |
123 | } | |
124 | ||
125 | SEASTAR_TEST_CASE(test_stream_drop_sub) { | |
126 | auto s = make_lw_shared<stream<int>>(); | |
f67539c2 | 127 | std::optional<future<>> ret; |
9f95a23c TL |
128 | { |
129 | auto sub = s->listen([](int x) { | |
130 | return make_ready_future<>(); | |
131 | }); | |
f67539c2 | 132 | ret = sub.done(); |
9f95a23c TL |
133 | // It is ok to drop the subscription when we only want the competition future. |
134 | } | |
135 | return s->produce(42).then([ret = std::move(*ret), s] () mutable { | |
136 | s->close(); | |
137 | return std::move(ret); | |
138 | }); | |
139 | } | |
140 | ||
f67539c2 TL |
141 | SEASTAR_TEST_CASE(test_reference) { |
142 | int a = 42; | |
143 | future<int&> orig = make_ready_future<int&>(a); | |
144 | future<int&> fut = std::move(orig); | |
145 | int& r = fut.get0(); | |
146 | r = 43; | |
147 | BOOST_REQUIRE_EQUAL(a, 43); | |
148 | return make_ready_future<>(); | |
149 | } | |
150 | ||
9f95a23c | 151 | SEASTAR_TEST_CASE(test_set_future_state_with_tuple) { |
f67539c2 | 152 | future_state<std::tuple<int>> s1; |
9f95a23c TL |
153 | promise<int> p1; |
154 | const std::tuple<int> v1(42); | |
155 | s1.set(v1); | |
156 | p1.set_value(v1); | |
157 | ||
9f95a23c TL |
158 | return make_ready_future<>(); |
159 | } | |
160 | ||
f67539c2 | 161 | SEASTAR_THREAD_TEST_CASE(test_set_value_make_exception_in_copy) { |
9f95a23c TL |
162 | struct throw_in_copy { |
163 | throw_in_copy() noexcept = default; | |
164 | throw_in_copy(throw_in_copy&& x) noexcept { | |
165 | } | |
166 | throw_in_copy(const throw_in_copy& x) { | |
167 | throw 42; | |
168 | } | |
169 | }; | |
170 | promise<throw_in_copy> p1; | |
171 | throw_in_copy v; | |
f67539c2 TL |
172 | p1.set_value(v); |
173 | BOOST_REQUIRE_THROW(p1.get_future().get(), int); | |
174 | } | |
175 | ||
176 | SEASTAR_THREAD_TEST_CASE(test_set_exception_in_constructor) { | |
177 | struct throw_in_constructor { | |
178 | throw_in_constructor() { | |
179 | throw 42; | |
180 | } | |
181 | }; | |
182 | future<throw_in_constructor> f = make_ready_future<throw_in_constructor>(); | |
183 | BOOST_REQUIRE(f.failed()); | |
184 | BOOST_REQUIRE_THROW(f.get(), int); | |
9f95a23c TL |
185 | } |
186 | ||
11fdf7f2 TL |
187 | SEASTAR_TEST_CASE(test_finally_is_called_on_success_and_failure) { |
188 | auto finally1 = make_shared<bool>(); | |
189 | auto finally2 = make_shared<bool>(); | |
190 | ||
191 | return make_ready_future().then([] { | |
192 | }).finally([=] { | |
193 | *finally1 = true; | |
194 | }).then([] { | |
195 | throw std::runtime_error(""); | |
196 | }).finally([=] { | |
197 | *finally2 = true; | |
198 | }).then_wrapped([=] (auto&& f) { | |
199 | BOOST_REQUIRE(*finally1); | |
200 | BOOST_REQUIRE(*finally2); | |
201 | ||
202 | // Should be failed. | |
203 | try { | |
204 | f.get(); | |
205 | BOOST_REQUIRE(false); | |
206 | } catch (...) {} | |
207 | }); | |
208 | } | |
209 | ||
210 | SEASTAR_TEST_CASE(test_get_on_promise) { | |
211 | auto p = promise<uint32_t>(); | |
212 | p.set_value(10); | |
213 | BOOST_REQUIRE_EQUAL(10u, p.get_future().get0()); | |
214 | return make_ready_future(); | |
215 | } | |
216 | ||
f67539c2 TL |
217 | // An exception class with a controlled what() overload |
218 | class test_exception : public std::exception { | |
219 | sstring _what; | |
220 | public: | |
221 | explicit test_exception(sstring what) : _what(std::move(what)) {} | |
222 | virtual const char* what() const noexcept override { | |
223 | return _what.c_str(); | |
224 | } | |
225 | }; | |
226 | ||
1e59de90 TL |
227 | SEASTAR_TEST_CASE(test_get_on_exceptional_promise) { |
228 | auto p = promise<>(); | |
229 | p.set_exception(test_exception("test")); | |
230 | BOOST_REQUIRE_THROW(p.get_future().get(), test_exception); | |
231 | return make_ready_future(); | |
232 | } | |
233 | ||
f67539c2 TL |
234 | static void check_finally_exception(std::exception_ptr ex) { |
235 | BOOST_REQUIRE_EQUAL(fmt::format("{}", ex), | |
236 | "seastar::nested_exception: test_exception (bar) (while cleaning up after test_exception (foo))"); | |
237 | try { | |
238 | // convert to the concrete type nested_exception | |
239 | std::rethrow_exception(ex); | |
240 | } catch (seastar::nested_exception& ex) { | |
241 | try { | |
242 | std::rethrow_exception(ex.inner); | |
243 | } catch (test_exception& inner) { | |
244 | BOOST_REQUIRE_EQUAL(inner.what(), "bar"); | |
245 | } | |
246 | try { | |
247 | ex.rethrow_nested(); | |
248 | } catch (test_exception& outer) { | |
249 | BOOST_REQUIRE_EQUAL(outer.what(), "foo"); | |
250 | } | |
251 | } | |
252 | } | |
253 | ||
254 | SEASTAR_TEST_CASE(test_finally_exception) { | |
255 | return make_ready_future<>().then([] { | |
256 | throw test_exception("foo"); | |
257 | }).finally([] { | |
258 | throw test_exception("bar"); | |
259 | }).handle_exception(check_finally_exception); | |
260 | } | |
261 | ||
262 | SEASTAR_TEST_CASE(test_finally_exceptional_future) { | |
263 | return make_ready_future<>().then([] { | |
264 | throw test_exception("foo"); | |
265 | }).finally([] { | |
266 | return make_exception_future<>(test_exception("bar")); | |
267 | }).handle_exception(check_finally_exception); | |
268 | } | |
269 | ||
11fdf7f2 TL |
270 | SEASTAR_TEST_CASE(test_finally_waits_for_inner) { |
271 | auto finally = make_shared<bool>(); | |
272 | auto p = make_shared<promise<>>(); | |
273 | ||
274 | auto f = make_ready_future().then([] { | |
275 | }).finally([=] { | |
276 | return p->get_future().then([=] { | |
277 | *finally = true; | |
278 | }); | |
279 | }).then([=] { | |
280 | BOOST_REQUIRE(*finally); | |
281 | }); | |
282 | BOOST_REQUIRE(!*finally); | |
283 | p->set_value(); | |
284 | return f; | |
285 | } | |
286 | ||
287 | SEASTAR_TEST_CASE(test_finally_is_called_on_success_and_failure__not_ready_to_armed) { | |
288 | auto finally1 = make_shared<bool>(); | |
289 | auto finally2 = make_shared<bool>(); | |
290 | ||
291 | promise<> p; | |
292 | auto f = p.get_future().finally([=] { | |
293 | *finally1 = true; | |
294 | }).then([] { | |
295 | throw std::runtime_error(""); | |
296 | }).finally([=] { | |
297 | *finally2 = true; | |
298 | }).then_wrapped([=] (auto &&f) { | |
299 | BOOST_REQUIRE(*finally1); | |
300 | BOOST_REQUIRE(*finally2); | |
301 | try { | |
302 | f.get(); | |
303 | } catch (...) {} // silence exceptional future ignored messages | |
304 | }); | |
305 | ||
306 | p.set_value(); | |
307 | return f; | |
308 | } | |
309 | ||
310 | SEASTAR_TEST_CASE(test_exception_from_finally_fails_the_target) { | |
311 | promise<> pr; | |
312 | auto f = pr.get_future().finally([=] { | |
313 | throw std::runtime_error(""); | |
314 | }).then([] { | |
315 | BOOST_REQUIRE(false); | |
316 | }).then_wrapped([] (auto&& f) { | |
317 | try { | |
318 | f.get(); | |
319 | } catch (...) {} // silence exceptional future ignored messages | |
320 | }); | |
321 | ||
322 | pr.set_value(); | |
323 | return f; | |
324 | } | |
325 | ||
326 | SEASTAR_TEST_CASE(test_exception_from_finally_fails_the_target_on_already_resolved) { | |
327 | return make_ready_future().finally([=] { | |
328 | throw std::runtime_error(""); | |
329 | }).then([] { | |
330 | BOOST_REQUIRE(false); | |
331 | }).then_wrapped([] (auto&& f) { | |
332 | try { | |
333 | f.get(); | |
334 | } catch (...) {} // silence exceptional future ignored messages | |
335 | }); | |
336 | } | |
337 | ||
338 | SEASTAR_TEST_CASE(test_exception_thrown_from_then_wrapped_causes_future_to_fail) { | |
339 | return make_ready_future().then_wrapped([] (auto&& f) { | |
340 | throw std::runtime_error(""); | |
341 | }).then_wrapped([] (auto&& f) { | |
342 | try { | |
343 | f.get(); | |
344 | BOOST_REQUIRE(false); | |
345 | } catch (...) {} | |
346 | }); | |
347 | } | |
348 | ||
349 | SEASTAR_TEST_CASE(test_exception_thrown_from_then_wrapped_causes_future_to_fail__async_case) { | |
350 | promise<> p; | |
351 | ||
352 | auto f = p.get_future().then_wrapped([] (auto&& f) { | |
353 | throw std::runtime_error(""); | |
354 | }).then_wrapped([] (auto&& f) { | |
355 | try { | |
356 | f.get(); | |
357 | BOOST_REQUIRE(false); | |
358 | } catch (...) {} | |
359 | }); | |
360 | ||
361 | p.set_value(); | |
362 | ||
363 | return f; | |
364 | } | |
365 | ||
366 | SEASTAR_TEST_CASE(test_failing_intermediate_promise_should_fail_the_master_future) { | |
367 | promise<> p1; | |
368 | promise<> p2; | |
369 | ||
9f95a23c | 370 | auto f = p1.get_future().then([f = p2.get_future()] () mutable { |
11fdf7f2 TL |
371 | return std::move(f); |
372 | }).then([] { | |
373 | BOOST_REQUIRE(false); | |
374 | }); | |
375 | ||
376 | p1.set_value(); | |
377 | p2.set_exception(std::runtime_error("boom")); | |
378 | ||
379 | return std::move(f).then_wrapped([](auto&& f) { | |
380 | try { | |
381 | f.get(); | |
382 | BOOST_REQUIRE(false); | |
383 | } catch (...) {} | |
384 | }); | |
385 | } | |
386 | ||
387 | SEASTAR_TEST_CASE(test_future_forwarding__not_ready_to_unarmed) { | |
388 | promise<> p1; | |
389 | promise<> p2; | |
390 | ||
391 | auto f1 = p1.get_future(); | |
392 | auto f2 = p2.get_future(); | |
393 | ||
394 | f1.forward_to(std::move(p2)); | |
395 | ||
396 | BOOST_REQUIRE(!f2.available()); | |
397 | ||
398 | auto called = f2.then([] {}); | |
399 | ||
400 | p1.set_value(); | |
401 | return called; | |
402 | } | |
403 | ||
404 | SEASTAR_TEST_CASE(test_future_forwarding__not_ready_to_armed) { | |
405 | promise<> p1; | |
406 | promise<> p2; | |
407 | ||
408 | auto f1 = p1.get_future(); | |
409 | auto f2 = p2.get_future(); | |
410 | ||
411 | auto called = f2.then([] {}); | |
412 | ||
413 | f1.forward_to(std::move(p2)); | |
414 | ||
415 | BOOST_REQUIRE(!f2.available()); | |
416 | ||
417 | p1.set_value(); | |
418 | ||
419 | return called; | |
420 | } | |
421 | ||
422 | SEASTAR_TEST_CASE(test_future_forwarding__ready_to_unarmed) { | |
423 | promise<> p2; | |
424 | ||
425 | auto f1 = make_ready_future<>(); | |
426 | auto f2 = p2.get_future(); | |
427 | ||
428 | std::move(f1).forward_to(std::move(p2)); | |
429 | BOOST_REQUIRE(f2.available()); | |
430 | ||
431 | return std::move(f2).then_wrapped([] (future<> f) { | |
432 | BOOST_REQUIRE(!f.failed()); | |
433 | }); | |
434 | } | |
435 | ||
436 | SEASTAR_TEST_CASE(test_future_forwarding__ready_to_armed) { | |
437 | promise<> p2; | |
438 | ||
439 | auto f1 = make_ready_future<>(); | |
440 | auto f2 = p2.get_future(); | |
441 | ||
442 | auto called = std::move(f2).then([] {}); | |
443 | ||
444 | BOOST_REQUIRE(f1.available()); | |
445 | ||
446 | f1.forward_to(std::move(p2)); | |
447 | return called; | |
448 | } | |
449 | ||
450 | static void forward_dead_unarmed_promise_with_dead_future_to(promise<>& p) { | |
451 | promise<> p2; | |
452 | p.get_future().forward_to(std::move(p2)); | |
453 | } | |
454 | ||
455 | SEASTAR_TEST_CASE(test_future_forwarding__ready_to_unarmed_soon_to_be_dead) { | |
456 | promise<> p1; | |
457 | forward_dead_unarmed_promise_with_dead_future_to(p1); | |
458 | make_ready_future<>().forward_to(std::move(p1)); | |
459 | return make_ready_future<>(); | |
460 | } | |
461 | ||
462 | SEASTAR_TEST_CASE(test_exception_can_be_thrown_from_do_until_body) { | |
463 | return do_until([] { return false; }, [] { | |
464 | throw expected_exception(); | |
465 | return now(); | |
466 | }).then_wrapped([] (auto&& f) { | |
467 | try { | |
468 | f.get(); | |
469 | BOOST_FAIL("should have failed"); | |
470 | } catch (const expected_exception& e) { | |
471 | // expected | |
472 | } | |
473 | }); | |
474 | } | |
475 | ||
20effc67 TL |
476 | SEASTAR_TEST_CASE(test_exception_can_be_thrown_from_do_until_condition) { |
477 | return do_until([] { throw expected_exception(); return false; }, [] { | |
478 | return now(); | |
479 | }).then_wrapped([] (auto&& f) { | |
480 | try { | |
481 | f.get(); | |
482 | BOOST_FAIL("should have failed"); | |
483 | } catch (const expected_exception& e) { | |
484 | // expected | |
485 | } | |
486 | }); | |
487 | } | |
488 | ||
11fdf7f2 TL |
489 | SEASTAR_TEST_CASE(test_bare_value_can_be_returned_from_callback) { |
490 | return now().then([] { | |
491 | return 3; | |
492 | }).then([] (int x) { | |
493 | BOOST_REQUIRE(x == 3); | |
494 | }); | |
495 | } | |
496 | ||
497 | SEASTAR_TEST_CASE(test_when_all_iterator_range) { | |
498 | std::vector<future<size_t>> futures; | |
499 | for (size_t i = 0; i != 1000000; ++i) { | |
f67539c2 TL |
500 | // Use a mix of available and unavailable futures to exercise |
501 | // both paths in when_all(). | |
1e59de90 | 502 | auto fut = (i % 2) == 0 ? make_ready_future<>() : yield(); |
f67539c2 | 503 | futures.push_back(fut.then([i] { return i; })); |
11fdf7f2 TL |
504 | } |
505 | // Verify the above statement is correct | |
506 | BOOST_REQUIRE(!std::all_of(futures.begin(), futures.end(), | |
507 | [] (auto& f) { return f.available(); })); | |
508 | auto p = make_shared(std::move(futures)); | |
509 | return when_all(p->begin(), p->end()).then([p] (std::vector<future<size_t>> ret) { | |
510 | BOOST_REQUIRE(std::all_of(ret.begin(), ret.end(), [] (auto& f) { return f.available(); })); | |
f67539c2 | 511 | BOOST_REQUIRE(std::all_of(ret.begin(), ret.end(), [&ret] (auto& f) { return f.get0() == size_t(&f - ret.data()); })); |
11fdf7f2 TL |
512 | }); |
513 | } | |
514 | ||
20effc67 TL |
515 | // helper function for when_any tests |
516 | template<typename Container> | |
517 | future<> when_all_but_one_succeed(Container& futures, size_t leave_out) | |
518 | { | |
519 | auto sz = futures.size(); | |
520 | assert(sz >= 1); | |
521 | assert(leave_out < sz); | |
522 | std::vector<future<size_t>> all_but_one_tmp; | |
523 | all_but_one_tmp.reserve(sz - 1); | |
524 | for (size_t i = 0 ; i < sz; i++){ | |
525 | if (i == leave_out) { continue; } | |
526 | all_but_one_tmp.push_back(std::move(futures[i])); | |
527 | } | |
528 | auto all_but_one = make_shared(std::move(all_but_one_tmp)); | |
529 | return when_all_succeed(all_but_one->begin(), all_but_one->end()).then([all_but_one] (auto&& _) { | |
530 | return make_ready_future<>(); | |
531 | }); | |
532 | } | |
533 | ||
534 | SEASTAR_TEST_CASE(test_when_any_iterator_range_i) { | |
535 | std::vector<future<size_t>> futures; | |
536 | for (size_t i = 0; i != 100; ++i) { | |
1e59de90 | 537 | auto fut = yield(); |
20effc67 TL |
538 | futures.push_back(fut.then([i] { return i; })); |
539 | } | |
540 | ||
541 | // Verify the above statement is correct | |
542 | BOOST_REQUIRE(std::all_of(futures.begin(), futures.end(), [](auto &f) { return !f.available(); })); | |
543 | ||
544 | auto p = make_shared(std::move(futures)); | |
545 | return seastar::when_any(p->begin(), p->end()).then([p](auto &&ret_obj) { | |
546 | BOOST_REQUIRE(ret_obj.futures[ret_obj.index].available()); | |
547 | BOOST_REQUIRE(ret_obj.futures[ret_obj.index].get0() == ret_obj.index); | |
548 | return when_all_but_one_succeed(ret_obj.futures, ret_obj.index); | |
549 | }); | |
550 | } | |
551 | ||
552 | SEASTAR_TEST_CASE(test_when_any_iterator_range_ii) { | |
553 | std::vector<future<size_t>> futures; | |
554 | for (size_t i = 0; i != 100; ++i) { | |
555 | if (i == 42) { | |
556 | auto fut = seastar::make_ready_future<>(); | |
557 | futures.push_back(fut.then([i] { return i; })); | |
558 | } else { | |
559 | auto fut = seastar::sleep(100ms); | |
560 | futures.push_back(fut.then([i] { return i; })); | |
561 | } | |
562 | } | |
563 | auto p = make_shared(std::move(futures)); | |
564 | return seastar::when_any(p->begin(), p->end()).then([p](auto &&ret_obj) { | |
565 | BOOST_REQUIRE(ret_obj.futures[ret_obj.index].available()); | |
566 | BOOST_REQUIRE(ret_obj.futures[ret_obj.index].get0() == ret_obj.index); | |
567 | BOOST_REQUIRE(ret_obj.index == 42); | |
568 | return when_all_but_one_succeed(ret_obj.futures, ret_obj.index); | |
569 | }); | |
570 | } | |
571 | ||
572 | SEASTAR_TEST_CASE(test_when_any_iterator_range_iii) { | |
573 | std::vector<future<size_t>> futures; | |
574 | for (size_t i = 0; i != 100; ++i) { | |
575 | if (i == 42) { | |
576 | auto fut = seastar::sleep(5ms); | |
577 | futures.push_back(fut.then([i] { return i; })); | |
578 | } else { | |
579 | auto fut = seastar::sleep(100ms); | |
580 | futures.push_back(fut.then([i] { return i; })); | |
581 | } | |
582 | } | |
583 | auto p = make_shared(std::move(futures)); | |
584 | return seastar::when_any(p->begin(), p->end()).then([p](auto &&ret_obj) { | |
585 | BOOST_REQUIRE(ret_obj.futures[ret_obj.index].available()); | |
586 | BOOST_REQUIRE(ret_obj.futures[ret_obj.index].get0() == ret_obj.index); | |
587 | BOOST_REQUIRE(ret_obj.index == 42); | |
588 | return when_all_but_one_succeed(ret_obj.futures, ret_obj.index); | |
589 | }); | |
590 | } | |
591 | ||
592 | SEASTAR_TEST_CASE(test_when_any_iterator_range_iv) { | |
593 | std::vector<future<size_t>> futures; | |
594 | for (size_t i = 0; i != 100; ++i) { | |
595 | if (i == 42) { | |
1e59de90 | 596 | auto fut = yield().then([] { return seastar::make_exception_future(std::runtime_error("test")); } ); |
20effc67 TL |
597 | futures.push_back(fut.then([i] { return i; })); |
598 | } else { | |
599 | auto fut = seastar::sleep(100ms); | |
600 | futures.push_back(fut.then([i] { return i; })); | |
601 | } | |
602 | } | |
603 | auto p = make_shared(std::move(futures)); | |
604 | return seastar::when_any(p->begin(), p->end()).then([p](auto &&ret_obj) { | |
605 | BOOST_REQUIRE(ret_obj.futures[ret_obj.index].available()); | |
606 | BOOST_REQUIRE_THROW(ret_obj.futures[ret_obj.index].get(), std::runtime_error); | |
607 | return when_all_but_one_succeed(ret_obj.futures, ret_obj.index); | |
608 | }); | |
609 | } | |
610 | ||
1e59de90 TL |
611 | SEASTAR_TEST_CASE(test_when_any_variadic_i) |
612 | { | |
613 | auto f_int = yield().then([] { return make_ready_future<int>(42); }); | |
614 | auto f_string = sleep(100ms).then([] { return make_ready_future<sstring>("hello"); }); | |
615 | auto f_l33tspeak = sleep(100ms).then([] { | |
616 | return make_ready_future<std::tuple<char, int, int, char, char, int, char>>( | |
617 | std::make_tuple('s', 3, 4, 's', 't', 4, 'r')); | |
618 | }); | |
619 | return when_any(std::move(f_int), std::move(f_string), std::move(f_l33tspeak)).then([](auto&& wa_result) { | |
620 | BOOST_REQUIRE(wa_result.index == 0); | |
621 | auto [one, two, three] = std::move(wa_result.futures); | |
622 | BOOST_REQUIRE(one.get0() == 42); | |
623 | return when_all_succeed(std::move(two), std::move(three)).then([](auto _) { return seastar::make_ready_future<>(); }); | |
624 | }); | |
625 | } | |
626 | ||
627 | SEASTAR_TEST_CASE(test_when_any_variadic_ii) | |
628 | { | |
629 | struct foo { | |
630 | int bar = 86; | |
631 | }; | |
632 | ||
633 | auto f_int = sleep(100ms).then([] { return make_ready_future<int>(42); }); | |
634 | auto f_foo = sleep(75ms).then([] { return make_ready_future<foo>(); }); | |
635 | auto f_string = sleep(1ms).then([] { return make_ready_future<sstring>("hello"); }); | |
636 | auto f_l33tspeak = sleep(50ms).then([] { | |
637 | return make_ready_future<std::tuple<char, int, int, char, char, int, char>>( | |
638 | std::make_tuple('s', 3, 4, 's', 't', 4, 'r')); | |
639 | }); | |
640 | return when_any(std::move(f_int), std::move(f_foo), std::move(f_string), std::move(f_l33tspeak)) | |
641 | .then([](auto&& wa_result) { | |
642 | BOOST_REQUIRE(wa_result.index == 2); | |
643 | auto [one, two, three, four] = std::move(wa_result.futures); | |
644 | BOOST_REQUIRE(three.get0() == "hello"); | |
645 | return when_any(std::move(one), std::move(two), std::move(four)).then([](auto wa_nextresult) { | |
646 | auto [one, two, four] = std::move(wa_nextresult.futures); | |
647 | BOOST_REQUIRE(wa_nextresult.index == 2); | |
648 | BOOST_REQUIRE(four.get0() == std::make_tuple('s', 3, 4, 's', 't', 4, 'r')); | |
649 | return when_any(std::move(one), std::move(two)).then([](auto wa_result) { | |
650 | auto [one, two] = std::move(wa_result.futures); | |
651 | BOOST_REQUIRE(wa_result.index == 1); | |
652 | BOOST_REQUIRE(two.get0().bar == foo{}.bar); | |
653 | return one.then([](int x) { BOOST_REQUIRE(x == 42); }); | |
654 | }); | |
655 | }); | |
656 | }); | |
657 | } | |
658 | ||
11fdf7f2 TL |
659 | SEASTAR_TEST_CASE(test_map_reduce) { |
660 | auto square = [] (long x) { return make_ready_future<long>(x*x); }; | |
661 | long n = 1000; | |
662 | return map_reduce(boost::make_counting_iterator<long>(0), boost::make_counting_iterator<long>(n), | |
663 | square, long(0), std::plus<long>()).then([n] (auto result) { | |
664 | auto m = n - 1; // counting does not include upper bound | |
665 | BOOST_REQUIRE_EQUAL(result, (m * (m + 1) * (2*m + 1)) / 6); | |
666 | }); | |
667 | } | |
668 | ||
f67539c2 TL |
669 | SEASTAR_TEST_CASE(test_map_reduce_simple) { |
670 | return do_with(0L, [] (auto& res) { | |
671 | long n = 10; | |
672 | return map_reduce(boost::make_counting_iterator<long>(0), boost::make_counting_iterator<long>(n), | |
673 | [] (long x) { return x; }, | |
674 | [&res] (long x) { res += x; }).then([n, &res] { | |
675 | long expected = (n * (n - 1)) / 2; | |
676 | BOOST_REQUIRE_EQUAL(res, expected); | |
677 | }); | |
678 | }); | |
679 | } | |
680 | ||
681 | SEASTAR_TEST_CASE(test_map_reduce_tuple) { | |
682 | return do_with(0L, 0L, [] (auto& res0, auto& res1) { | |
683 | long n = 10; | |
684 | return map_reduce(boost::make_counting_iterator<long>(0), boost::make_counting_iterator<long>(n), | |
685 | [] (long x) { return std::tuple<long, long>(x, -x); }, | |
686 | [&res0, &res1] (std::tuple<long, long> t) { res0 += std::get<0>(t); res1 += std::get<1>(t); }).then([n, &res0, &res1] { | |
687 | long expected = (n * (n - 1)) / 2; | |
688 | BOOST_REQUIRE_EQUAL(res0, expected); | |
689 | BOOST_REQUIRE_EQUAL(res1, -expected); | |
690 | }); | |
691 | }); | |
692 | } | |
693 | ||
1e59de90 TL |
694 | SEASTAR_TEST_CASE(test_map_reduce_lifetime) { |
695 | struct map { | |
696 | bool destroyed = false; | |
697 | ~map() { | |
698 | destroyed = true; | |
699 | } | |
700 | auto operator()(long x) { | |
701 | return yield().then([this, x] { | |
702 | BOOST_REQUIRE(!destroyed); | |
703 | return x; | |
704 | }); | |
705 | } | |
706 | }; | |
707 | struct reduce { | |
708 | long& res; | |
709 | bool destroyed = false; | |
710 | ~reduce() { | |
711 | destroyed = true; | |
712 | } | |
713 | auto operator()(long x) { | |
714 | return yield().then([this, x] { | |
715 | BOOST_REQUIRE(!destroyed); | |
716 | res += x; | |
717 | }); | |
718 | } | |
719 | }; | |
720 | return do_with(0L, [] (auto& res) { | |
721 | long n = 10; | |
722 | return map_reduce(boost::make_counting_iterator<long>(0), boost::make_counting_iterator<long>(n), | |
723 | map{}, reduce{res}).then([n, &res] { | |
724 | long expected = (n * (n - 1)) / 2; | |
725 | BOOST_REQUIRE_EQUAL(res, expected); | |
726 | }); | |
727 | }); | |
728 | } | |
729 | ||
730 | SEASTAR_TEST_CASE(test_map_reduce0_lifetime) { | |
731 | struct map { | |
732 | bool destroyed = false; | |
733 | ~map() { | |
734 | destroyed = true; | |
735 | } | |
736 | auto operator()(long x) { | |
737 | return yield().then([this, x] { | |
738 | BOOST_REQUIRE(!destroyed); | |
739 | return x; | |
740 | }); | |
741 | } | |
742 | }; | |
743 | struct reduce { | |
744 | bool destroyed = false; | |
745 | ~reduce() { | |
746 | destroyed = true; | |
747 | } | |
748 | auto operator()(long res, long x) { | |
749 | BOOST_REQUIRE(!destroyed); | |
750 | return res + x; | |
751 | } | |
752 | }; | |
753 | long n = 10; | |
754 | return map_reduce(boost::make_counting_iterator<long>(0), boost::make_counting_iterator<long>(n), | |
755 | map{}, 0L, reduce{}).then([n] (long res) { | |
756 | long expected = (n * (n - 1)) / 2; | |
757 | BOOST_REQUIRE_EQUAL(res, expected); | |
758 | }); | |
759 | } | |
760 | ||
11fdf7f2 TL |
761 | // This test doesn't actually test anything - it just waits for the future |
762 | // returned by sleep to complete. However, a bug we had in sleep() caused | |
763 | // this test to fail the sanitizer in the debug build, so this is a useful | |
764 | // regression test. | |
765 | SEASTAR_TEST_CASE(test_sleep) { | |
766 | return sleep(std::chrono::milliseconds(100)); | |
767 | } | |
768 | ||
769 | SEASTAR_TEST_CASE(test_do_with_1) { | |
770 | return do_with(1, [] (int& one) { | |
771 | BOOST_REQUIRE_EQUAL(one, 1); | |
772 | return make_ready_future<>(); | |
773 | }); | |
774 | } | |
775 | ||
776 | SEASTAR_TEST_CASE(test_do_with_2) { | |
777 | return do_with(1, 2L, [] (int& one, long two) { | |
778 | BOOST_REQUIRE_EQUAL(one, 1); | |
779 | BOOST_REQUIRE_EQUAL(two, 2); | |
780 | return make_ready_future<>(); | |
781 | }); | |
782 | } | |
783 | ||
784 | SEASTAR_TEST_CASE(test_do_with_3) { | |
785 | return do_with(1, 2L, 3, [] (int& one, long two, int three) { | |
786 | BOOST_REQUIRE_EQUAL(one, 1); | |
787 | BOOST_REQUIRE_EQUAL(two, 2); | |
788 | BOOST_REQUIRE_EQUAL(three, 3); | |
789 | return make_ready_future<>(); | |
790 | }); | |
791 | } | |
792 | ||
793 | SEASTAR_TEST_CASE(test_do_with_4) { | |
794 | return do_with(1, 2L, 3, 4, [] (int& one, long two, int three, int four) { | |
795 | BOOST_REQUIRE_EQUAL(one, 1); | |
796 | BOOST_REQUIRE_EQUAL(two, 2); | |
797 | BOOST_REQUIRE_EQUAL(three, 3); | |
798 | BOOST_REQUIRE_EQUAL(four, 4); | |
799 | return make_ready_future<>(); | |
800 | }); | |
801 | } | |
802 | ||
f67539c2 TL |
803 | SEASTAR_TEST_CASE(test_do_with_5) { |
804 | using func = noncopyable_function<void()>; | |
805 | return do_with(func([] {}), [] (func&) { | |
806 | return make_ready_future<>(); | |
807 | }); | |
808 | } | |
809 | ||
810 | SEASTAR_TEST_CASE(test_do_with_6) { | |
811 | const int x = 42; | |
812 | return do_with(int(42), x, [](int&, int&) { | |
813 | return make_ready_future<>(); | |
814 | }); | |
815 | } | |
816 | ||
817 | SEASTAR_TEST_CASE(test_do_with_7) { | |
818 | const int x = 42; | |
819 | return do_with(x, [](int&) { | |
820 | return make_ready_future<>(); | |
821 | }); | |
822 | } | |
823 | ||
11fdf7f2 TL |
824 | SEASTAR_TEST_CASE(test_do_while_stopping_immediately) { |
825 | return do_with(int(0), [] (int& count) { | |
826 | return repeat([&count] { | |
827 | ++count; | |
828 | return stop_iteration::yes; | |
829 | }).then([&count] { | |
830 | BOOST_REQUIRE(count == 1); | |
831 | }); | |
832 | }); | |
833 | } | |
834 | ||
835 | SEASTAR_TEST_CASE(test_do_while_stopping_after_two_iterations) { | |
836 | return do_with(int(0), [] (int& count) { | |
837 | return repeat([&count] { | |
838 | ++count; | |
839 | return count == 2 ? stop_iteration::yes : stop_iteration::no; | |
840 | }).then([&count] { | |
841 | BOOST_REQUIRE(count == 2); | |
842 | }); | |
843 | }); | |
844 | } | |
845 | ||
846 | SEASTAR_TEST_CASE(test_do_while_failing_in_the_first_step) { | |
847 | return repeat([] { | |
848 | throw expected_exception(); | |
849 | return stop_iteration::no; | |
850 | }).then_wrapped([](auto&& f) { | |
851 | try { | |
852 | f.get(); | |
853 | BOOST_FAIL("should not happen"); | |
854 | } catch (const expected_exception&) { | |
855 | // expected | |
856 | } | |
857 | }); | |
858 | } | |
859 | ||
860 | SEASTAR_TEST_CASE(test_do_while_failing_in_the_second_step) { | |
861 | return do_with(int(0), [] (int& count) { | |
862 | return repeat([&count] { | |
863 | ++count; | |
864 | if (count > 1) { | |
865 | throw expected_exception(); | |
866 | } | |
1e59de90 | 867 | return yield().then([] { return stop_iteration::no; }); |
11fdf7f2 TL |
868 | }).then_wrapped([&count](auto&& f) { |
869 | try { | |
870 | f.get(); | |
871 | BOOST_FAIL("should not happen"); | |
872 | } catch (const expected_exception&) { | |
873 | BOOST_REQUIRE(count == 2); | |
874 | } | |
875 | }); | |
876 | }); | |
877 | } | |
878 | ||
879 | SEASTAR_TEST_CASE(test_parallel_for_each) { | |
880 | return async([] { | |
881 | // empty | |
882 | parallel_for_each(std::vector<int>(), [] (int) -> future<> { | |
883 | BOOST_FAIL("should not reach"); | |
884 | abort(); | |
885 | }).get(); | |
886 | ||
887 | // immediate result | |
888 | auto range = boost::copy_range<std::vector<int>>(boost::irange(1, 6)); | |
889 | auto sum = 0; | |
890 | parallel_for_each(range, [&sum] (int v) { | |
891 | sum += v; | |
892 | return make_ready_future<>(); | |
893 | }).get(); | |
894 | BOOST_REQUIRE_EQUAL(sum, 15); | |
895 | ||
896 | // all suspend | |
897 | sum = 0; | |
898 | parallel_for_each(range, [&sum] (int v) { | |
1e59de90 | 899 | return yield().then([&sum, v] { |
11fdf7f2 TL |
900 | sum += v; |
901 | }); | |
902 | }).get(); | |
903 | BOOST_REQUIRE_EQUAL(sum, 15); | |
904 | ||
905 | // throws immediately | |
9f95a23c | 906 | BOOST_CHECK_EXCEPTION(parallel_for_each(range, [] (int) -> future<> { |
11fdf7f2 TL |
907 | throw 5; |
908 | }).get(), int, [] (int v) { return v == 5; }); | |
909 | ||
910 | // throws after suspension | |
9f95a23c | 911 | BOOST_CHECK_EXCEPTION(parallel_for_each(range, [] (int) { |
1e59de90 | 912 | return yield().then([] { |
11fdf7f2 TL |
913 | throw 5; |
914 | }); | |
915 | }).get(), int, [] (int v) { return v == 5; }); | |
916 | }); | |
917 | } | |
918 | ||
919 | SEASTAR_TEST_CASE(test_parallel_for_each_early_failure) { | |
920 | return do_with(0, [] (int& counter) { | |
921 | return parallel_for_each(boost::irange(0, 11000), [&counter] (int i) { | |
922 | using namespace std::chrono_literals; | |
923 | // force scheduling | |
924 | return sleep((i % 31 + 1) * 1ms).then([&counter, i] { | |
925 | ++counter; | |
926 | if (i % 1777 == 1337) { | |
927 | return make_exception_future<>(i); | |
928 | } | |
929 | return make_ready_future<>(); | |
930 | }); | |
931 | }).then_wrapped([&counter] (future<> f) { | |
932 | BOOST_REQUIRE_EQUAL(counter, 11000); | |
933 | BOOST_REQUIRE(f.failed()); | |
934 | try { | |
935 | f.get(); | |
936 | BOOST_FAIL("wanted an exception"); | |
937 | } catch (int i) { | |
938 | BOOST_REQUIRE(i % 1777 == 1337); | |
939 | } catch (...) { | |
940 | BOOST_FAIL("bad exception type"); | |
941 | } | |
942 | }); | |
943 | }); | |
944 | } | |
945 | ||
946 | SEASTAR_TEST_CASE(test_parallel_for_each_waits_for_all_fibers_even_if_one_of_them_failed) { | |
947 | auto can_exit = make_lw_shared<bool>(false); | |
948 | return parallel_for_each(boost::irange(0, 2), [can_exit] (int i) { | |
1e59de90 | 949 | return yield().then([i, can_exit] { |
11fdf7f2 TL |
950 | if (i == 1) { |
951 | throw expected_exception(); | |
952 | } else { | |
953 | using namespace std::chrono_literals; | |
954 | return sleep(300ms).then([can_exit] { | |
955 | *can_exit = true; | |
956 | }); | |
957 | } | |
958 | }); | |
959 | }).then_wrapped([can_exit] (auto&& f) { | |
960 | try { | |
961 | f.get(); | |
962 | } catch (...) { | |
963 | // expected | |
964 | } | |
965 | BOOST_REQUIRE(*can_exit); | |
966 | }); | |
967 | } | |
968 | ||
f67539c2 TL |
969 | SEASTAR_THREAD_TEST_CASE(test_parallel_for_each_broken_promise) { |
970 | auto fut = [] { | |
971 | std::vector<promise<>> v(2); | |
972 | return parallel_for_each(v, [] (promise<>& p) { | |
973 | return p.get_future(); | |
11fdf7f2 | 974 | }); |
f67539c2 TL |
975 | }(); |
976 | BOOST_CHECK_THROW(fut.get(), broken_promise); | |
977 | } | |
978 | ||
979 | SEASTAR_THREAD_TEST_CASE(test_repeat_broken_promise) { | |
980 | auto get_fut = [] { | |
981 | promise<stop_iteration> pr; | |
982 | return pr.get_future(); | |
983 | }; | |
984 | ||
985 | future<> r = repeat([fut = get_fut()] () mutable { | |
986 | return std::move(fut); | |
11fdf7f2 | 987 | }); |
f67539c2 TL |
988 | |
989 | BOOST_CHECK_THROW(r.get(), broken_promise); | |
11fdf7f2 TL |
990 | } |
991 | ||
f67539c2 | 992 | #ifndef SEASTAR_SHUFFLE_TASK_QUEUE |
11fdf7f2 TL |
993 | SEASTAR_TEST_CASE(test_high_priority_task_runs_in_the_middle_of_loops) { |
994 | auto counter = make_lw_shared<int>(0); | |
995 | auto flag = make_lw_shared<bool>(false); | |
996 | return repeat([counter, flag] { | |
997 | if (*counter == 1) { | |
998 | BOOST_REQUIRE(*flag); | |
999 | return stop_iteration::yes; | |
1000 | } | |
1001 | engine().add_high_priority_task(make_task([flag] { | |
1002 | *flag = true; | |
1003 | })); | |
1004 | ++(*counter); | |
1005 | return stop_iteration::no; | |
1006 | }); | |
1007 | } | |
1008 | #endif | |
1009 | ||
f67539c2 TL |
1010 | SEASTAR_TEST_CASE(futurize_invoke_val_exception) { |
1011 | return futurize_invoke([] (int arg) { throw expected_exception(); return arg; }, 1).then_wrapped([] (future<int> f) { | |
11fdf7f2 TL |
1012 | try { |
1013 | f.get(); | |
1014 | BOOST_FAIL("should have thrown"); | |
1015 | } catch (expected_exception& e) {} | |
1016 | }); | |
1017 | } | |
1018 | ||
f67539c2 TL |
1019 | SEASTAR_TEST_CASE(futurize_invoke_val_ok) { |
1020 | return futurize_invoke([] (int arg) { return arg * 2; }, 2).then_wrapped([] (future<int> f) { | |
11fdf7f2 TL |
1021 | try { |
1022 | auto x = f.get0(); | |
1023 | BOOST_REQUIRE_EQUAL(x, 4); | |
1024 | } catch (expected_exception& e) { | |
1025 | BOOST_FAIL("should not have thrown"); | |
1026 | } | |
1027 | }); | |
1028 | } | |
1029 | ||
f67539c2 TL |
1030 | SEASTAR_TEST_CASE(futurize_invoke_val_future_exception) { |
1031 | return futurize_invoke([] (int a) { | |
11fdf7f2 TL |
1032 | return sleep(std::chrono::milliseconds(100)).then([] { |
1033 | throw expected_exception(); | |
1034 | return make_ready_future<int>(0); | |
1035 | }); | |
1036 | }, 0).then_wrapped([] (future<int> f) { | |
1037 | try { | |
1038 | f.get(); | |
1039 | BOOST_FAIL("should have thrown"); | |
1040 | } catch (expected_exception& e) { } | |
1041 | }); | |
1042 | } | |
1043 | ||
f67539c2 TL |
1044 | SEASTAR_TEST_CASE(futurize_invoke_val_future_ok) { |
1045 | return futurize_invoke([] (int a) { | |
11fdf7f2 TL |
1046 | return sleep(std::chrono::milliseconds(100)).then([a] { |
1047 | return make_ready_future<int>(a * 100); | |
1048 | }); | |
1049 | }, 2).then_wrapped([] (future<int> f) { | |
1050 | try { | |
1051 | auto x = f.get0(); | |
1052 | BOOST_REQUIRE_EQUAL(x, 200); | |
1053 | } catch (expected_exception& e) { | |
1054 | BOOST_FAIL("should not have thrown"); | |
1055 | } | |
1056 | }); | |
1057 | } | |
f67539c2 TL |
1058 | SEASTAR_TEST_CASE(futurize_invoke_void_exception) { |
1059 | return futurize_invoke([] (auto arg) { throw expected_exception(); }, 0).then_wrapped([] (future<> f) { | |
11fdf7f2 TL |
1060 | try { |
1061 | f.get(); | |
1062 | BOOST_FAIL("should have thrown"); | |
1063 | } catch (expected_exception& e) {} | |
1064 | }); | |
1065 | } | |
1066 | ||
f67539c2 TL |
1067 | SEASTAR_TEST_CASE(futurize_invoke_void_ok) { |
1068 | return futurize_invoke([] (auto arg) { }, 0).then_wrapped([] (future<> f) { | |
11fdf7f2 TL |
1069 | try { |
1070 | f.get(); | |
1071 | } catch (expected_exception& e) { | |
1072 | BOOST_FAIL("should not have thrown"); | |
1073 | } | |
1074 | }); | |
1075 | } | |
1076 | ||
f67539c2 TL |
1077 | SEASTAR_TEST_CASE(futurize_invoke_void_future_exception) { |
1078 | return futurize_invoke([] (auto a) { | |
11fdf7f2 TL |
1079 | return sleep(std::chrono::milliseconds(100)).then([] { |
1080 | throw expected_exception(); | |
1081 | }); | |
1082 | }, 0).then_wrapped([] (future<> f) { | |
1083 | try { | |
1084 | f.get(); | |
1085 | BOOST_FAIL("should have thrown"); | |
1086 | } catch (expected_exception& e) { } | |
1087 | }); | |
1088 | } | |
1089 | ||
f67539c2 | 1090 | SEASTAR_TEST_CASE(futurize_invoke_void_future_ok) { |
11fdf7f2 | 1091 | auto a = make_lw_shared<int>(1); |
f67539c2 | 1092 | return futurize_invoke([] (int& a) { |
11fdf7f2 TL |
1093 | return sleep(std::chrono::milliseconds(100)).then([&a] { |
1094 | a *= 100; | |
1095 | }); | |
1096 | }, *a).then_wrapped([a] (future<> f) { | |
1097 | try { | |
1098 | f.get(); | |
1099 | BOOST_REQUIRE_EQUAL(*a, 100); | |
1100 | } catch (expected_exception& e) { | |
1101 | BOOST_FAIL("should not have thrown"); | |
1102 | } | |
1103 | }); | |
1104 | } | |
1105 | ||
9f95a23c TL |
1106 | SEASTAR_TEST_CASE(test_unused_shared_future_is_not_a_broken_future) { |
1107 | promise<> p; | |
1108 | shared_future<> s(p.get_future()); | |
1109 | return make_ready_future<>(); | |
1110 | } | |
1111 | ||
11fdf7f2 TL |
1112 | SEASTAR_TEST_CASE(test_shared_future_propagates_value_to_all) { |
1113 | return seastar::async([] { | |
1114 | promise<shared_ptr<int>> p; // shared_ptr<> to check it deals with emptyable types | |
1115 | shared_future<shared_ptr<int>> f(p.get_future()); | |
1116 | ||
1117 | auto f1 = f.get_future(); | |
1118 | auto f2 = f.get_future(); | |
1119 | ||
1120 | p.set_value(make_shared<int>(1)); | |
1121 | BOOST_REQUIRE(*f1.get0() == 1); | |
1122 | BOOST_REQUIRE(*f2.get0() == 1); | |
1123 | }); | |
1124 | } | |
1125 | ||
1126 | template<typename... T> | |
1127 | void check_fails_with_expected(future<T...> f) { | |
1128 | try { | |
1129 | f.get(); | |
1130 | BOOST_FAIL("Should have failed"); | |
1131 | } catch (expected_exception&) { | |
1132 | // expected | |
1133 | } | |
1134 | } | |
1135 | ||
1136 | SEASTAR_TEST_CASE(test_shared_future_propagates_value_to_copies) { | |
1137 | return seastar::async([] { | |
1138 | promise<int> p; | |
1139 | auto sf1 = shared_future<int>(p.get_future()); | |
1140 | auto sf2 = sf1; | |
1141 | ||
1142 | auto f1 = sf1.get_future(); | |
1143 | auto f2 = sf2.get_future(); | |
1144 | ||
1145 | p.set_value(1); | |
1146 | ||
1147 | BOOST_REQUIRE(f1.get0() == 1); | |
1148 | BOOST_REQUIRE(f2.get0() == 1); | |
1149 | }); | |
1150 | } | |
1151 | ||
1152 | SEASTAR_TEST_CASE(test_obtaining_future_from_shared_future_after_it_is_resolved) { | |
1153 | promise<int> p1; | |
1154 | promise<int> p2; | |
1155 | auto sf1 = shared_future<int>(p1.get_future()); | |
1156 | auto sf2 = shared_future<int>(p2.get_future()); | |
1157 | p1.set_value(1); | |
1158 | p2.set_exception(expected_exception()); | |
1159 | return sf2.get_future().then_wrapped([f1 = sf1.get_future()] (auto&& f) mutable { | |
1160 | check_fails_with_expected(std::move(f)); | |
1161 | return std::move(f1); | |
1162 | }).then_wrapped([] (auto&& f) { | |
1163 | BOOST_REQUIRE(f.get0() == 1); | |
1164 | }); | |
1165 | } | |
1166 | ||
1167 | SEASTAR_TEST_CASE(test_valueless_shared_future) { | |
1168 | return seastar::async([] { | |
1169 | promise<> p; | |
1170 | shared_future<> f(p.get_future()); | |
1171 | ||
1172 | auto f1 = f.get_future(); | |
1173 | auto f2 = f.get_future(); | |
1174 | ||
1175 | p.set_value(); | |
1176 | ||
1177 | f1.get(); | |
1178 | f2.get(); | |
1179 | }); | |
1180 | } | |
1181 | ||
1182 | SEASTAR_TEST_CASE(test_shared_future_propagates_errors_to_all) { | |
1183 | promise<int> p; | |
1184 | shared_future<int> f(p.get_future()); | |
1185 | ||
1186 | auto f1 = f.get_future(); | |
1187 | auto f2 = f.get_future(); | |
1188 | ||
1189 | p.set_exception(expected_exception()); | |
1190 | ||
1191 | return f1.then_wrapped([f2 = std::move(f2)] (auto&& f) mutable { | |
1192 | check_fails_with_expected(std::move(f)); | |
1193 | return std::move(f2); | |
1194 | }).then_wrapped([] (auto&& f) mutable { | |
1195 | check_fails_with_expected(std::move(f)); | |
1196 | }); | |
1197 | } | |
1198 | ||
9f95a23c TL |
1199 | SEASTAR_TEST_CASE(test_ignored_future_warning) { |
1200 | // This doesn't warn: | |
1201 | promise<> p; | |
1202 | p.set_exception(expected_exception()); | |
1203 | future<> f = p.get_future(); | |
1204 | f.ignore_ready_future(); | |
1205 | ||
1206 | // And by analogy, neither should this | |
1207 | shared_promise<> p2; | |
1208 | p2.set_exception(expected_exception()); | |
1209 | future<> f2 = p2.get_shared_future(); | |
1210 | f2.ignore_ready_future(); | |
1211 | return make_ready_future<>(); | |
1212 | } | |
1213 | ||
11fdf7f2 TL |
1214 | SEASTAR_TEST_CASE(test_futurize_from_tuple) { |
1215 | std::tuple<int> v1 = std::make_tuple(3); | |
1216 | std::tuple<> v2 = {}; | |
f67539c2 TL |
1217 | future<int> fut1 = futurize<int>::from_tuple(v1); |
1218 | future<> fut2 = futurize<void>::from_tuple(v2); | |
1219 | BOOST_REQUIRE(fut1.get0() == std::get<0>(v1)); | |
1220 | #if SEASTAR_API_LEVEL < 5 | |
1221 | BOOST_REQUIRE(fut2.get() == v2); | |
1222 | #endif | |
11fdf7f2 TL |
1223 | return make_ready_future<>(); |
1224 | } | |
1225 | ||
1226 | SEASTAR_TEST_CASE(test_repeat_until_value) { | |
1227 | return do_with(int(), [] (int& counter) { | |
f67539c2 | 1228 | return repeat_until_value([&counter] () -> future<std::optional<int>> { |
11fdf7f2 | 1229 | if (counter == 10000) { |
f67539c2 | 1230 | return make_ready_future<std::optional<int>>(counter); |
11fdf7f2 TL |
1231 | } else { |
1232 | ++counter; | |
f67539c2 | 1233 | return make_ready_future<std::optional<int>>(std::nullopt); |
11fdf7f2 TL |
1234 | } |
1235 | }).then([&counter] (int result) { | |
1236 | BOOST_REQUIRE(counter == 10000); | |
1237 | BOOST_REQUIRE(result == counter); | |
1238 | }); | |
1239 | }); | |
1240 | } | |
1241 | ||
9f95a23c | 1242 | SEASTAR_TEST_CASE(test_repeat_until_value_implicit_future) { |
f67539c2 | 1243 | // Same as above, but returning std::optional<int> instead of future<std::optional<int>> |
9f95a23c TL |
1244 | return do_with(int(), [] (int& counter) { |
1245 | return repeat_until_value([&counter] { | |
1246 | if (counter == 10000) { | |
f67539c2 | 1247 | return std::optional<int>(counter); |
9f95a23c TL |
1248 | } else { |
1249 | ++counter; | |
f67539c2 | 1250 | return std::optional<int>(std::nullopt); |
9f95a23c TL |
1251 | } |
1252 | }).then([&counter] (int result) { | |
1253 | BOOST_REQUIRE(counter == 10000); | |
1254 | BOOST_REQUIRE(result == counter); | |
1255 | }); | |
1256 | }); | |
1257 | } | |
1258 | ||
1259 | SEASTAR_TEST_CASE(test_repeat_until_value_exception) { | |
1260 | return repeat_until_value([] { | |
1261 | throw expected_exception(); | |
f67539c2 | 1262 | return std::optional<int>(43); |
9f95a23c TL |
1263 | }).then_wrapped([] (future<int> f) { |
1264 | check_fails_with_expected(std::move(f)); | |
1265 | }); | |
1266 | } | |
1267 | ||
11fdf7f2 | 1268 | SEASTAR_TEST_CASE(test_when_allx) { |
1e59de90 | 1269 | return when_all(yield(), yield(), make_ready_future()).discard_result(); |
11fdf7f2 TL |
1270 | } |
1271 | ||
9f95a23c TL |
1272 | // A noncopyable and nonmovable struct |
1273 | struct non_copy_non_move { | |
f67539c2 | 1274 | non_copy_non_move() = default; |
9f95a23c TL |
1275 | non_copy_non_move(non_copy_non_move&&) = delete; |
1276 | non_copy_non_move(const non_copy_non_move&) = delete; | |
1277 | }; | |
1278 | ||
1279 | SEASTAR_TEST_CASE(test_when_all_functions) { | |
f67539c2 | 1280 | auto f = [x = non_copy_non_move()] { |
9f95a23c TL |
1281 | (void)x; |
1282 | return make_ready_future<int>(42); | |
1283 | }; | |
1284 | return when_all(f, [] { | |
1285 | throw 42; | |
1286 | return make_ready_future<>(); | |
1e59de90 | 1287 | }, yield()).then([] (std::tuple<future<int>, future<>, future<>> res) { |
9f95a23c TL |
1288 | BOOST_REQUIRE_EQUAL(std::get<0>(res).get0(), 42); |
1289 | ||
1290 | BOOST_REQUIRE(std::get<1>(res).available()); | |
1291 | BOOST_REQUIRE(std::get<1>(res).failed()); | |
1292 | std::get<1>(res).ignore_ready_future(); | |
1293 | ||
1294 | BOOST_REQUIRE(std::get<2>(res).available()); | |
1295 | BOOST_REQUIRE(!std::get<2>(res).failed()); | |
1296 | return make_ready_future<>(); | |
1297 | }); | |
1298 | } | |
1299 | ||
1300 | SEASTAR_TEST_CASE(test_when_all_succeed_functions) { | |
f67539c2 | 1301 | auto f = [x = non_copy_non_move()] { |
9f95a23c TL |
1302 | (void)x; |
1303 | return make_ready_future<int>(42); | |
1304 | }; | |
1305 | return when_all_succeed(f, [] { | |
1306 | throw 42; | |
1307 | return make_ready_future<>(); | |
1e59de90 | 1308 | }, yield()).then_wrapped([] (auto res) { // type of `res` changes when SESTAR_API_LEVEL < 3 |
9f95a23c TL |
1309 | BOOST_REQUIRE(res.available()); |
1310 | BOOST_REQUIRE(res.failed()); | |
1311 | res.ignore_ready_future(); | |
1312 | return make_ready_future<>(); | |
1313 | }); | |
1314 | } | |
1315 | ||
11fdf7f2 TL |
1316 | template<typename E, typename... T> |
1317 | static void check_failed_with(future<T...>&& f) { | |
1318 | BOOST_REQUIRE(f.failed()); | |
1319 | try { | |
1320 | f.get(); | |
1321 | BOOST_FAIL("exception expected"); | |
1322 | } catch (const E& e) { | |
1323 | // expected | |
1324 | } catch (...) { | |
1325 | BOOST_FAIL(format("wrong exception: {}", std::current_exception())); | |
1326 | } | |
1327 | } | |
1328 | ||
1329 | template<typename... T> | |
1330 | static void check_timed_out(future<T...>&& f) { | |
1331 | check_failed_with<timed_out_error>(std::move(f)); | |
1332 | } | |
1333 | ||
1334 | SEASTAR_TEST_CASE(test_with_timeout_when_it_times_out) { | |
1335 | return seastar::async([] { | |
1336 | promise<> pr; | |
1337 | auto f = with_timeout(manual_clock::now() + 2s, pr.get_future()); | |
1338 | ||
1339 | BOOST_REQUIRE(!f.available()); | |
1340 | ||
1341 | manual_clock::advance(1s); | |
1e59de90 | 1342 | yield().get(); |
11fdf7f2 TL |
1343 | |
1344 | BOOST_REQUIRE(!f.available()); | |
1345 | ||
1346 | manual_clock::advance(1s); | |
1e59de90 | 1347 | yield().get(); |
11fdf7f2 TL |
1348 | |
1349 | check_timed_out(std::move(f)); | |
1350 | ||
1351 | pr.set_value(); | |
1352 | }); | |
1353 | } | |
1354 | ||
9f95a23c TL |
1355 | SEASTAR_THREAD_TEST_CASE(test_shared_future_get_future_after_timeout) { |
1356 | // This used to crash because shared_future checked if the list of | |
1357 | // pending futures was empty to decide if it had already called | |
1358 | // then_wrapped. If all pending futures timed out, it would call | |
1359 | // it again. | |
1360 | promise<> pr; | |
1361 | shared_future<with_clock<manual_clock>> sfut(pr.get_future()); | |
1362 | future<> fut1 = sfut.get_future(manual_clock::now() + 1s); | |
1363 | ||
1364 | manual_clock::advance(1s); | |
1365 | ||
1366 | check_timed_out(std::move(fut1)); | |
1367 | ||
1368 | future<> fut2 = sfut.get_future(manual_clock::now() + 1s); | |
1369 | manual_clock::advance(1s); | |
1370 | check_timed_out(std::move(fut2)); | |
1371 | ||
1372 | future<> fut3 = sfut.get_future(manual_clock::now() + 1s); | |
1373 | pr.set_value(); | |
f67539c2 | 1374 | fut3.get(); |
9f95a23c TL |
1375 | } |
1376 | ||
11fdf7f2 TL |
1377 | SEASTAR_TEST_CASE(test_custom_exception_factory_in_with_timeout) { |
1378 | return seastar::async([] { | |
1379 | class custom_error : public std::exception { | |
1380 | public: | |
1381 | virtual const char* what() const noexcept { | |
1382 | return "timedout"; | |
1383 | } | |
1384 | }; | |
1385 | struct my_exception_factory { | |
1386 | static auto timeout() { | |
1387 | return custom_error(); | |
1388 | } | |
1389 | }; | |
1390 | promise<> pr; | |
1391 | auto f = with_timeout<my_exception_factory>(manual_clock::now() + 1s, pr.get_future()); | |
1392 | ||
1393 | manual_clock::advance(1s); | |
1e59de90 | 1394 | yield().get(); |
11fdf7f2 TL |
1395 | |
1396 | check_failed_with<custom_error>(std::move(f)); | |
1397 | }); | |
1398 | } | |
1399 | ||
1400 | SEASTAR_TEST_CASE(test_with_timeout_when_it_does_not_time_out) { | |
1401 | return seastar::async([] { | |
1402 | { | |
1403 | promise<int> pr; | |
1404 | auto f = with_timeout(manual_clock::now() + 1s, pr.get_future()); | |
1405 | ||
1406 | pr.set_value(42); | |
1407 | ||
1408 | BOOST_REQUIRE_EQUAL(f.get0(), 42); | |
1409 | } | |
1410 | ||
1411 | // Check that timer was indeed cancelled | |
1412 | manual_clock::advance(1s); | |
1e59de90 | 1413 | yield().get(); |
11fdf7f2 TL |
1414 | }); |
1415 | } | |
1416 | ||
1e59de90 TL |
1417 | template<typename... T> |
1418 | static void check_aborted(future<T...>&& f) { | |
1419 | check_failed_with<abort_requested_exception>(std::move(f)); | |
1420 | } | |
1421 | ||
11fdf7f2 TL |
1422 | SEASTAR_TEST_CASE(test_shared_future_with_timeout) { |
1423 | return seastar::async([] { | |
1424 | shared_promise<with_clock<manual_clock>, int> pr; | |
1425 | auto f1 = pr.get_shared_future(manual_clock::now() + 1s); | |
1426 | auto f2 = pr.get_shared_future(manual_clock::now() + 2s); | |
1427 | auto f3 = pr.get_shared_future(); | |
1428 | ||
1429 | BOOST_REQUIRE(!f1.available()); | |
1430 | BOOST_REQUIRE(!f2.available()); | |
1431 | BOOST_REQUIRE(!f3.available()); | |
1432 | ||
1433 | manual_clock::advance(1s); | |
1e59de90 | 1434 | yield().get(); |
11fdf7f2 TL |
1435 | |
1436 | check_timed_out(std::move(f1)); | |
1437 | BOOST_REQUIRE(!f2.available()); | |
1438 | BOOST_REQUIRE(!f3.available()); | |
1439 | ||
1440 | manual_clock::advance(1s); | |
1e59de90 | 1441 | yield().get(); |
11fdf7f2 TL |
1442 | |
1443 | check_timed_out(std::move(f2)); | |
1444 | BOOST_REQUIRE(!f3.available()); | |
1445 | ||
1446 | pr.set_value(42); | |
1447 | ||
1448 | BOOST_REQUIRE_EQUAL(42, f3.get0()); | |
1449 | }); | |
1450 | } | |
1451 | ||
1e59de90 TL |
1452 | SEASTAR_THREAD_TEST_CASE(test_shared_future_with_abort) { |
1453 | abort_source as; | |
1454 | abort_source as2; | |
1455 | shared_promise<with_clock<manual_clock>, int> pr; | |
1456 | auto f1 = pr.get_shared_future(as); | |
1457 | auto f2 = pr.get_shared_future(as2); | |
1458 | auto f3 = pr.get_shared_future(); | |
1459 | ||
1460 | BOOST_REQUIRE(!f1.available()); | |
1461 | BOOST_REQUIRE(!f2.available()); | |
1462 | BOOST_REQUIRE(!f3.available()); | |
1463 | ||
1464 | as.request_abort(); | |
1465 | ||
1466 | check_aborted(std::move(f1)); | |
1467 | BOOST_REQUIRE(!f2.available()); | |
1468 | BOOST_REQUIRE(!f3.available()); | |
1469 | ||
1470 | as2.request_abort(); | |
1471 | ||
1472 | check_aborted(std::move(f2)); | |
1473 | BOOST_REQUIRE(!f3.available()); | |
1474 | ||
1475 | pr.set_value(42); | |
1476 | ||
1477 | BOOST_REQUIRE_EQUAL(42, f3.get0()); | |
1478 | ||
1479 | auto f4 = pr.get_shared_future(as); | |
1480 | BOOST_REQUIRE(f4.available()); | |
1481 | } | |
1482 | ||
f67539c2 TL |
1483 | #if SEASTAR_API_LEVEL < 4 |
1484 | #define THEN_UNPACK then | |
1485 | #else | |
1486 | #define THEN_UNPACK then_unpack | |
1487 | #endif | |
1488 | ||
11fdf7f2 TL |
1489 | SEASTAR_TEST_CASE(test_when_all_succeed_tuples) { |
1490 | return seastar::when_all_succeed( | |
1491 | make_ready_future<>(), | |
1492 | make_ready_future<sstring>("hello world"), | |
1493 | make_ready_future<int>(42), | |
1494 | make_ready_future<>(), | |
f67539c2 | 1495 | make_ready_future<std::tuple<int, sstring>>(std::tuple(84, "hi")), |
11fdf7f2 | 1496 | make_ready_future<bool>(true) |
f67539c2 | 1497 | ).THEN_UNPACK([] (sstring msg, int v, std::tuple<int, sstring> t, bool b) { |
11fdf7f2 TL |
1498 | BOOST_REQUIRE_EQUAL(msg, "hello world"); |
1499 | BOOST_REQUIRE_EQUAL(v, 42); | |
1500 | BOOST_REQUIRE_EQUAL(std::get<0>(t), 84); | |
1501 | BOOST_REQUIRE_EQUAL(std::get<1>(t), "hi"); | |
1502 | BOOST_REQUIRE_EQUAL(b, true); | |
1503 | ||
1504 | return seastar::when_all_succeed( | |
1505 | make_exception_future<>(42), | |
1506 | make_ready_future<sstring>("hello world"), | |
1507 | make_exception_future<int>(43), | |
1508 | make_ready_future<>() | |
f67539c2 | 1509 | ).THEN_UNPACK([] (sstring, int) { |
11fdf7f2 TL |
1510 | BOOST_FAIL("shouldn't reach"); |
1511 | return false; | |
1512 | }).handle_exception([] (auto excp) { | |
1513 | try { | |
1514 | std::rethrow_exception(excp); | |
1515 | } catch (int v) { | |
1516 | BOOST_REQUIRE(v == 42 || v == 43); | |
1517 | return true; | |
1518 | } catch (...) { } | |
1519 | return false; | |
1520 | }).then([] (auto ret) { | |
1521 | BOOST_REQUIRE(ret); | |
1522 | }); | |
1523 | }); | |
1524 | } | |
1525 | ||
1526 | SEASTAR_TEST_CASE(test_when_all_succeed_vector) { | |
1527 | std::vector<future<>> vecs; | |
1528 | vecs.emplace_back(make_ready_future<>()); | |
1529 | vecs.emplace_back(make_ready_future<>()); | |
1530 | vecs.emplace_back(make_ready_future<>()); | |
1531 | vecs.emplace_back(make_ready_future<>()); | |
1532 | return seastar::when_all_succeed(vecs.begin(), vecs.end()).then([] { | |
1533 | std::vector<future<>> vecs; | |
1534 | vecs.emplace_back(make_ready_future<>()); | |
1535 | vecs.emplace_back(make_ready_future<>()); | |
1536 | vecs.emplace_back(make_exception_future<>(42)); | |
1537 | vecs.emplace_back(make_exception_future<>(43)); | |
1538 | return seastar::when_all_succeed(vecs.begin(), vecs.end()); | |
1539 | }).then([] { | |
1540 | BOOST_FAIL("shouldn't reach"); | |
1541 | return false; | |
1542 | }).handle_exception([] (auto excp) { | |
1543 | try { | |
1544 | std::rethrow_exception(excp); | |
1545 | } catch (int v) { | |
1546 | BOOST_REQUIRE(v == 42 || v == 43); | |
1547 | return true; | |
1548 | } catch (...) { } | |
1549 | return false; | |
1550 | }).then([] (auto ret) { | |
1551 | BOOST_REQUIRE(ret); | |
1552 | ||
1553 | std::vector<future<int>> vecs; | |
1554 | vecs.emplace_back(make_ready_future<int>(1)); | |
1555 | vecs.emplace_back(make_ready_future<int>(2)); | |
1556 | vecs.emplace_back(make_ready_future<int>(3)); | |
1557 | return seastar::when_all_succeed(vecs.begin(), vecs.end()); | |
1558 | }).then([] (std::vector<int> vals) { | |
1559 | BOOST_REQUIRE_EQUAL(vals.size(), 3u); | |
1560 | BOOST_REQUIRE_EQUAL(vals[0], 1); | |
1561 | BOOST_REQUIRE_EQUAL(vals[1], 2); | |
1562 | BOOST_REQUIRE_EQUAL(vals[2], 3); | |
1563 | ||
1564 | std::vector<future<int>> vecs; | |
1565 | vecs.emplace_back(make_ready_future<int>(1)); | |
1566 | vecs.emplace_back(make_ready_future<int>(2)); | |
1567 | vecs.emplace_back(make_exception_future<int>(42)); | |
1568 | vecs.emplace_back(make_exception_future<int>(43)); | |
1569 | return seastar::when_all_succeed(vecs.begin(), vecs.end()); | |
1570 | }).then([] (std::vector<int>) { | |
1571 | BOOST_FAIL("shouldn't reach"); | |
1572 | return false; | |
1573 | }).handle_exception([] (auto excp) { | |
1574 | try { | |
1575 | std::rethrow_exception(excp); | |
1576 | } catch (int v) { | |
1577 | BOOST_REQUIRE(v == 42 || v == 43); | |
1578 | return true; | |
1579 | } catch (...) { } | |
1580 | return false; | |
1581 | }).then([] (auto ret) { | |
1582 | BOOST_REQUIRE(ret); | |
1583 | }); | |
1584 | } | |
1585 | ||
1586 | SEASTAR_TEST_CASE(test_futurize_mutable) { | |
1587 | int count = 0; | |
1588 | return seastar::repeat([count]() mutable { | |
1589 | ++count; | |
1590 | if (count == 3) { | |
1591 | return seastar::stop_iteration::yes; | |
1592 | } | |
1593 | return seastar::stop_iteration::no; | |
1594 | }); | |
1595 | } | |
9f95a23c TL |
1596 | |
1597 | SEASTAR_THREAD_TEST_CASE(test_broken_promises) { | |
f67539c2 TL |
1598 | std::optional<future<>> f; |
1599 | std::optional<future<>> f2; | |
9f95a23c TL |
1600 | { // Broken after attaching a continuation |
1601 | auto p = promise<>(); | |
1602 | f = p.get_future(); | |
1603 | f2 = f->then_wrapped([&] (future<> f3) { | |
1604 | BOOST_CHECK(f3.failed()); | |
1605 | BOOST_CHECK_THROW(f3.get(), broken_promise); | |
1606 | f = { }; | |
1607 | }); | |
1608 | } | |
1609 | f2->get(); | |
1610 | BOOST_CHECK(!f); | |
1611 | ||
1612 | { // Broken before attaching a continuation | |
1613 | auto p = promise<>(); | |
1614 | f = p.get_future(); | |
1615 | } | |
1616 | f->then_wrapped([&] (future<> f3) { | |
1617 | BOOST_CHECK(f3.failed()); | |
1618 | BOOST_CHECK_THROW(f3.get(), broken_promise); | |
1619 | f = { }; | |
1620 | }).get(); | |
1621 | BOOST_CHECK(!f); | |
1622 | ||
1623 | { // Broken before suspending a thread | |
1624 | auto p = promise<>(); | |
1625 | f = p.get_future(); | |
1626 | } | |
1627 | BOOST_CHECK_THROW(f->get(), broken_promise); | |
1628 | } | |
1629 | ||
1630 | SEASTAR_TEST_CASE(test_warn_on_broken_promise_with_no_future) { | |
1631 | // Example code where we expect a "Exceptional future ignored" | |
f67539c2 | 1632 | // warning. |
9f95a23c TL |
1633 | promise<> p; |
1634 | // Intentionally destroy the future | |
1635 | (void)p.get_future(); | |
f67539c2 TL |
1636 | |
1637 | with_allow_abandoned_failed_futures(1, [&] { | |
1638 | p.set_exception(std::runtime_error("foo")); | |
1639 | }); | |
1640 | ||
1641 | return make_ready_future<>(); | |
1642 | } | |
1643 | ||
1e59de90 TL |
1644 | SEASTAR_TEST_CASE(test_destroy_promise_after_state_take_value) { |
1645 | future<> f = make_ready_future<>(); | |
1646 | auto p = std::make_unique<seastar::promise<>>(); | |
1647 | f = p->get_future(); | |
1648 | p->set_value(); | |
1649 | auto g = f.then([] {}); | |
1650 | p.reset(); | |
1651 | return g; | |
1652 | } | |
1653 | ||
f67539c2 TL |
1654 | SEASTAR_THREAD_TEST_CASE(test_exception_future_with_backtrace) { |
1655 | int counter = 0; | |
1656 | auto inner = [&] (bool return_exception) mutable { | |
1657 | if (!return_exception) { | |
1658 | return make_ready_future<int>(++counter); | |
1659 | } else { | |
1660 | return make_exception_future_with_backtrace<int>(expected_exception()); | |
1661 | } | |
1662 | }; | |
1663 | auto outer = [&] (bool return_exception) { | |
1664 | return inner(return_exception).then([] (int i) { | |
1665 | return make_ready_future<int>(-i); | |
1666 | }); | |
1667 | }; | |
1668 | ||
1669 | BOOST_REQUIRE_EQUAL(outer(false).get0(), -1); | |
1670 | BOOST_REQUIRE_EQUAL(counter, 1); | |
1671 | ||
1672 | BOOST_CHECK_THROW(outer(true).get0(), expected_exception); | |
1673 | BOOST_REQUIRE_EQUAL(counter, 1); | |
1674 | ||
1675 | // Example code where we expect a "Exceptional future ignored" | |
1676 | // warning. | |
1677 | (void)outer(true).then_wrapped([](future<int> fut) { | |
1678 | with_allow_abandoned_failed_futures(1, [fut = std::move(fut)]() mutable { | |
1679 | auto foo = std::move(fut); | |
1680 | }); | |
1681 | }); | |
1682 | } | |
1683 | ||
1684 | class throw_on_move { | |
1685 | int _i; | |
1686 | public: | |
1687 | throw_on_move(int i = 0) noexcept { | |
1688 | _i = i; | |
1689 | } | |
1690 | throw_on_move(const throw_on_move&) = delete; | |
1691 | throw_on_move(throw_on_move&&) { | |
1692 | _i = -1; | |
1693 | throw expected_exception(); | |
1694 | } | |
1695 | ||
1696 | int value() const { | |
1697 | return _i; | |
1698 | } | |
1699 | }; | |
1700 | ||
1701 | SEASTAR_TEST_CASE(test_async_throw_on_move) { | |
1702 | return async([] (throw_on_move t) { | |
1703 | BOOST_CHECK(false); | |
1704 | }, throw_on_move()).handle_exception_type([] (const expected_exception&) { | |
1705 | return make_ready_future<>(); | |
1706 | }); | |
1707 | } | |
1708 | ||
1709 | future<> func4() { | |
1e59de90 | 1710 | return yield().then([] { |
f67539c2 TL |
1711 | seastar_logger.info("backtrace: {}", current_backtrace()); |
1712 | }); | |
1713 | } | |
1714 | ||
1715 | void func3() { | |
1716 | seastar::async([] { | |
1717 | func4().get(); | |
1718 | }).get(); | |
1719 | } | |
1720 | ||
1721 | future<> func2() { | |
1722 | return seastar::async([] { | |
1723 | func3(); | |
1724 | }); | |
1725 | } | |
1726 | ||
1727 | future<> func1() { | |
1e59de90 | 1728 | return yield().then([] { |
f67539c2 TL |
1729 | return func2(); |
1730 | }); | |
1731 | } | |
1732 | ||
1733 | SEASTAR_THREAD_TEST_CASE(test_backtracing) { | |
1734 | func1().get(); | |
1735 | } | |
1736 | ||
1737 | SEASTAR_THREAD_TEST_CASE(test_then_unpack) { | |
1738 | make_ready_future<std::tuple<>>().then_unpack([] () { | |
1739 | BOOST_REQUIRE(true); | |
1740 | }).get(); | |
1741 | make_ready_future<std::tuple<int>>(std::tuple<int>(1)).then_unpack([] (int x) { | |
1742 | BOOST_REQUIRE(x == 1); | |
1743 | }).get(); | |
1744 | make_ready_future<std::tuple<int, long>>(std::tuple<int, long>(1, 2)).then_unpack([] (int x, long y) { | |
1745 | BOOST_REQUIRE(x == 1 && y == 2); | |
1746 | }).get(); | |
1747 | make_ready_future<std::tuple<std::unique_ptr<int>>>(std::tuple(std::make_unique<int>(42))).then_unpack([] (std::unique_ptr<int> p1) { | |
1748 | BOOST_REQUIRE(*p1 == 42); | |
1749 | }).get(); | |
1750 | } | |
1751 | ||
1752 | future<> test_then_function_f() { | |
9f95a23c TL |
1753 | return make_ready_future<>(); |
1754 | } | |
f67539c2 TL |
1755 | |
1756 | SEASTAR_TEST_CASE(test_then_function) { | |
1757 | return make_ready_future<>().then(test_then_function_f); | |
1758 | } | |
1759 | ||
1760 | SEASTAR_THREAD_TEST_CASE(test_with_gate) { | |
1761 | gate g; | |
1762 | int counter = 0; | |
1763 | int gate_closed_errors = 0; | |
1764 | int other_errors = 0; | |
1765 | ||
1766 | // test normal operation when gate is opened | |
1767 | BOOST_CHECK_NO_THROW(with_gate(g, [&] { counter++; }).get()); | |
1768 | BOOST_REQUIRE_EQUAL(counter, 1); | |
1769 | ||
1770 | // test that an exception returned by the calling func | |
1771 | // is propagated to with_gate future | |
1772 | counter = gate_closed_errors = other_errors = 0; | |
1773 | BOOST_CHECK_NO_THROW(with_gate(g, [&] { | |
1774 | counter++; | |
1775 | return make_exception_future<>(expected_exception()); | |
1776 | }).handle_exception_type([&] (gate_closed_exception& e) { | |
1777 | gate_closed_errors++; | |
1778 | }).handle_exception([&] (std::exception_ptr) { | |
1779 | other_errors++; | |
1780 | }).get()); | |
1781 | BOOST_REQUIRE(counter); | |
1782 | BOOST_REQUIRE(!gate_closed_errors); | |
1783 | BOOST_REQUIRE(other_errors); | |
1784 | ||
1785 | g.close().get(); | |
1786 | ||
1787 | // test that with_gate.get() throws when the gate is closed | |
1788 | counter = gate_closed_errors = other_errors = 0; | |
1789 | BOOST_CHECK_THROW(with_gate(g, [&] { counter++; }).get(), gate_closed_exception); | |
1790 | BOOST_REQUIRE(!counter); | |
1791 | ||
1792 | // test that with_gate throws when the gate is closed | |
1793 | counter = gate_closed_errors = other_errors = 0; | |
1794 | BOOST_CHECK_THROW(with_gate(g, [&] { | |
1795 | counter++; | |
1796 | }).then_wrapped([&] (future<> f) { | |
1797 | auto eptr = f.get_exception(); | |
1798 | try { | |
1799 | std::rethrow_exception(eptr); | |
1800 | } catch (gate_closed_exception& e) { | |
1801 | gate_closed_errors++; | |
1802 | } catch (...) { | |
1803 | other_errors++; | |
1804 | } | |
1805 | }).get(), gate_closed_exception); | |
1806 | BOOST_REQUIRE(!counter); | |
1807 | BOOST_REQUIRE(!gate_closed_errors); | |
1808 | BOOST_REQUIRE(!other_errors); | |
1809 | ||
1810 | // test that try_with_gate returns gate_closed_exception when the gate is closed | |
1811 | counter = gate_closed_errors = other_errors = 0; | |
1812 | try_with_gate(g, [&] { counter++; }).handle_exception_type([&] (gate_closed_exception& e) { | |
1813 | gate_closed_errors++; | |
1814 | }).handle_exception([&] (std::exception_ptr) { | |
1815 | other_errors++; | |
1816 | }).get(); | |
1817 | BOOST_REQUIRE(!counter); | |
1818 | BOOST_REQUIRE(gate_closed_errors); | |
1819 | BOOST_REQUIRE(!other_errors); | |
1820 | } | |
1821 | ||
1822 | SEASTAR_THREAD_TEST_CASE(test_max_concurrent_for_each) { | |
1823 | BOOST_TEST_MESSAGE("empty range"); | |
1824 | max_concurrent_for_each(std::vector<int>(), 3, [] (int) { | |
1825 | BOOST_FAIL("should not reach"); | |
1826 | return make_exception_future<>(std::bad_function_call()); | |
1827 | }).get(); | |
1828 | ||
1829 | auto range = boost::copy_range<std::vector<int>>(boost::irange(1, 8)); | |
1830 | ||
1831 | BOOST_TEST_MESSAGE("iterator"); | |
1832 | auto sum = 0; | |
1833 | max_concurrent_for_each(range.begin(), range.end(), 3, [&sum] (int v) { | |
1834 | sum += v; | |
1835 | return make_ready_future<>(); | |
1836 | }).get(); | |
1837 | BOOST_REQUIRE_EQUAL(sum, 28); | |
1838 | ||
1839 | BOOST_TEST_MESSAGE("const iterator"); | |
1840 | sum = 0; | |
1841 | max_concurrent_for_each(range.cbegin(), range.cend(), 3, [&sum] (int v) { | |
1842 | sum += v; | |
1843 | return make_ready_future<>(); | |
1844 | }).get(); | |
1845 | BOOST_REQUIRE_EQUAL(sum, 28); | |
1846 | ||
1847 | BOOST_TEST_MESSAGE("reverse iterator"); | |
1848 | sum = 0; | |
1849 | max_concurrent_for_each(range.rbegin(), range.rend(), 3, [&sum] (int v) { | |
1850 | sum += v; | |
1851 | return make_ready_future<>(); | |
1852 | }).get(); | |
1853 | BOOST_REQUIRE_EQUAL(sum, 28); | |
1854 | ||
1855 | BOOST_TEST_MESSAGE("immediate result"); | |
1856 | sum = 0; | |
1857 | max_concurrent_for_each(range, 3, [&sum] (int v) { | |
1858 | sum += v; | |
1859 | return make_ready_future<>(); | |
1860 | }).get(); | |
1861 | BOOST_REQUIRE_EQUAL(sum, 28); | |
1862 | ||
1863 | BOOST_TEST_MESSAGE("suspend"); | |
1864 | sum = 0; | |
1865 | max_concurrent_for_each(range, 3, [&sum] (int v) { | |
1e59de90 | 1866 | return yield().then([&sum, v] { |
f67539c2 TL |
1867 | sum += v; |
1868 | }); | |
1869 | }).get(); | |
1870 | BOOST_REQUIRE_EQUAL(sum, 28); | |
1871 | ||
1872 | BOOST_TEST_MESSAGE("throw immediately"); | |
1873 | sum = 0; | |
1874 | BOOST_CHECK_EXCEPTION(max_concurrent_for_each(range, 3, [&sum] (int v) { | |
1875 | sum += v; | |
1876 | if (v == 1) { | |
1877 | throw 5; | |
1878 | } | |
1879 | return make_ready_future<>(); | |
1880 | }).get(), int, [] (int v) { return v == 5; }); | |
1881 | BOOST_REQUIRE_EQUAL(sum, 28); | |
1882 | ||
1883 | BOOST_TEST_MESSAGE("throw after suspension"); | |
1884 | sum = 0; | |
1885 | BOOST_CHECK_EXCEPTION(max_concurrent_for_each(range, 3, [&sum] (int v) { | |
1e59de90 | 1886 | return yield().then([&sum, v] { |
f67539c2 TL |
1887 | sum += v; |
1888 | if (v == 2) { | |
1889 | throw 5; | |
1890 | } | |
1891 | }); | |
1892 | }).get(), int, [] (int v) { return v == 5; }); | |
1893 | ||
1894 | BOOST_TEST_MESSAGE("concurrency higher than vector length"); | |
1895 | sum = 0; | |
1896 | max_concurrent_for_each(range, range.size() + 3, [&sum] (int v) { | |
1897 | sum += v; | |
1898 | return make_ready_future<>(); | |
1899 | }).get(); | |
1900 | BOOST_REQUIRE_EQUAL(sum, 28); | |
1901 | } | |
20effc67 TL |
1902 | |
1903 | SEASTAR_THREAD_TEST_CASE(test_for_each_set) { | |
1904 | std::bitset<32> s; | |
1905 | s.set(4); | |
1906 | s.set(0); | |
1907 | ||
1908 | auto range = bitsets::for_each_set(s); | |
1909 | unsigned res = 0; | |
1910 | do_for_each(range, [&res] (auto i) { | |
1911 | res |= 1 << i; | |
1912 | }).get(); | |
1913 | BOOST_REQUIRE_EQUAL(res, 17); | |
1914 | } | |
1e59de90 TL |
1915 | |
1916 | SEASTAR_THREAD_TEST_CASE(test_yield) { | |
1917 | bool flag = false; | |
1918 | auto one = yield().then([&] { | |
1919 | flag = true; | |
1920 | }); | |
1921 | BOOST_REQUIRE_EQUAL(flag, false); | |
1922 | one.get(); | |
1923 | BOOST_REQUIRE_EQUAL(flag, true); | |
1924 | ||
1925 | #ifndef SEASTAR_DEBUG | |
1926 | // same thing, with now(), but for non-DEBUG only, otherwise .then() doesn't | |
1927 | // use the ready-future fast-path and always schedules a task | |
1928 | flag = false; | |
1929 | auto two = now().then([&] { | |
1930 | flag = true; | |
1931 | }); | |
1932 | // now() does not yield | |
1933 | BOOST_REQUIRE_EQUAL(flag, true); | |
1934 | #endif | |
1935 | } | |
1936 | ||
1937 | // The seastar::make_exception_future() function has two distinct cases - it | |
1938 | // can create an exceptional future from an existing std::exception_ptr, or | |
1939 | // from an any object which will be wrapped in an std::exception_ptr using | |
1940 | // std::make_exception_ptr. We want to test here these two cases, as well | |
1941 | // what happens when the given parameter is almost a std::exception_ptr, | |
1942 | // just with different qualifiers, like && or const (see issue #1010). | |
1943 | SEASTAR_TEST_CASE(test_make_exception_future) { | |
1944 | // When make_exception_future() is given most types - like int and | |
1945 | // std::runtime_error - a copy of the given value get stored in the | |
1946 | // future (internally, it is wrapped using std::make_exception_ptr): | |
1947 | future<> f1 = make_exception_future<>(3); | |
1948 | BOOST_REQUIRE(f1.failed()); | |
1949 | BOOST_REQUIRE_THROW(f1.get(), int); | |
1950 | future<> f2 = make_exception_future<>(std::runtime_error("hello")); | |
1951 | BOOST_REQUIRE(f2.failed()); | |
1952 | BOOST_REQUIRE_THROW(f2.get(), std::runtime_error); | |
1953 | // However, if make_exception_future() is given an std::exception_ptr | |
1954 | // it behaves differently - the exception stored in the future will be | |
1955 | // the one held in the given exception_ptr - not the exception_ptr object | |
1956 | // itself. | |
1957 | std::exception_ptr e3 = std::make_exception_ptr(3); | |
1958 | future<> f3 = make_exception_future<>(e3); | |
1959 | BOOST_REQUIRE(f3.failed()); | |
1960 | BOOST_REQUIRE_THROW(f3.get(), int); // expecting int, not std::exception_ptr | |
1961 | // If make_exception_future() is given an std::exception_ptr by rvalue, | |
1962 | // it should also work correctly: | |
1963 | // An unnamed rvalue: | |
1964 | future<> f4 = make_exception_future<>(std::make_exception_ptr(3)); | |
1965 | BOOST_REQUIRE(f4.failed()); | |
1966 | BOOST_REQUIRE_THROW(f4.get(), int); // expecting int, not std::exception_ptr | |
1967 | // A rvalue reference (a move): | |
1968 | std::exception_ptr e5 = std::make_exception_ptr(3); | |
1969 | future<> f5 = make_exception_future<>(std::move(e5)); // note std::move() | |
1970 | BOOST_REQUIRE(f5.failed()); | |
1971 | BOOST_REQUIRE_THROW(f5.get(), int); // expecting int, not std::exception_ptr | |
1972 | // A rvalue reference to a *const* exception_ptr: | |
1973 | // Reproduces issue #1010 - a const exception_ptr sounds odd, but can | |
1974 | // happen accidentally when capturing an exception_ptr in a non-mutable | |
1975 | // lambda. | |
1976 | // Note that C++ is fine with std::move() being used on a const object, | |
1977 | // it will simply fall back to a copy instead of a move. And a copy does | |
1978 | // work (without std::move(), it works). | |
1979 | const std::exception_ptr e6 = std::make_exception_ptr(3); // note const! | |
1980 | future<> f6 = make_exception_future<>(std::move(e6)); // note std::move() | |
1981 | BOOST_REQUIRE(f6.failed()); | |
1982 | BOOST_REQUIRE_THROW(f6.get(), int); // expecting int, not std::exception_ptr | |
1983 | ||
1984 | return make_ready_future<>(); | |
1985 | } |