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