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