]>
Commit | Line | Data |
---|---|---|
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 | ||
24 | #include <seastar/core/shared_ptr.hh> | |
25 | #include <seastar/core/future-util.hh> | |
26 | #include <seastar/core/sleep.hh> | |
27 | #include <seastar/core/do_with.hh> | |
28 | #include <seastar/core/shared_future.hh> | |
29 | #include <seastar/core/thread.hh> | |
30 | #include <boost/iterator/counting_iterator.hpp> | |
31 | ||
32 | using namespace seastar; | |
33 | using namespace std::chrono_literals; | |
34 | ||
35 | class expected_exception : std::runtime_error { | |
36 | public: | |
37 | expected_exception() : runtime_error("expected") {} | |
38 | }; | |
39 | ||
40 | SEASTAR_TEST_CASE(test_finally_is_called_on_success_and_failure) { | |
41 | auto finally1 = make_shared<bool>(); | |
42 | auto finally2 = make_shared<bool>(); | |
43 | ||
44 | return make_ready_future().then([] { | |
45 | }).finally([=] { | |
46 | *finally1 = true; | |
47 | }).then([] { | |
48 | throw std::runtime_error(""); | |
49 | }).finally([=] { | |
50 | *finally2 = true; | |
51 | }).then_wrapped([=] (auto&& f) { | |
52 | BOOST_REQUIRE(*finally1); | |
53 | BOOST_REQUIRE(*finally2); | |
54 | ||
55 | // Should be failed. | |
56 | try { | |
57 | f.get(); | |
58 | BOOST_REQUIRE(false); | |
59 | } catch (...) {} | |
60 | }); | |
61 | } | |
62 | ||
63 | SEASTAR_TEST_CASE(test_get_on_promise) { | |
64 | auto p = promise<uint32_t>(); | |
65 | p.set_value(10); | |
66 | BOOST_REQUIRE_EQUAL(10u, p.get_future().get0()); | |
67 | return make_ready_future(); | |
68 | } | |
69 | ||
70 | SEASTAR_TEST_CASE(test_finally_waits_for_inner) { | |
71 | auto finally = make_shared<bool>(); | |
72 | auto p = make_shared<promise<>>(); | |
73 | ||
74 | auto f = make_ready_future().then([] { | |
75 | }).finally([=] { | |
76 | return p->get_future().then([=] { | |
77 | *finally = true; | |
78 | }); | |
79 | }).then([=] { | |
80 | BOOST_REQUIRE(*finally); | |
81 | }); | |
82 | BOOST_REQUIRE(!*finally); | |
83 | p->set_value(); | |
84 | return f; | |
85 | } | |
86 | ||
87 | SEASTAR_TEST_CASE(test_finally_is_called_on_success_and_failure__not_ready_to_armed) { | |
88 | auto finally1 = make_shared<bool>(); | |
89 | auto finally2 = make_shared<bool>(); | |
90 | ||
91 | promise<> p; | |
92 | auto f = p.get_future().finally([=] { | |
93 | *finally1 = true; | |
94 | }).then([] { | |
95 | throw std::runtime_error(""); | |
96 | }).finally([=] { | |
97 | *finally2 = true; | |
98 | }).then_wrapped([=] (auto &&f) { | |
99 | BOOST_REQUIRE(*finally1); | |
100 | BOOST_REQUIRE(*finally2); | |
101 | try { | |
102 | f.get(); | |
103 | } catch (...) {} // silence exceptional future ignored messages | |
104 | }); | |
105 | ||
106 | p.set_value(); | |
107 | return f; | |
108 | } | |
109 | ||
110 | SEASTAR_TEST_CASE(test_exception_from_finally_fails_the_target) { | |
111 | promise<> pr; | |
112 | auto f = pr.get_future().finally([=] { | |
113 | throw std::runtime_error(""); | |
114 | }).then([] { | |
115 | BOOST_REQUIRE(false); | |
116 | }).then_wrapped([] (auto&& f) { | |
117 | try { | |
118 | f.get(); | |
119 | } catch (...) {} // silence exceptional future ignored messages | |
120 | }); | |
121 | ||
122 | pr.set_value(); | |
123 | return f; | |
124 | } | |
125 | ||
126 | SEASTAR_TEST_CASE(test_exception_from_finally_fails_the_target_on_already_resolved) { | |
127 | return make_ready_future().finally([=] { | |
128 | throw std::runtime_error(""); | |
129 | }).then([] { | |
130 | BOOST_REQUIRE(false); | |
131 | }).then_wrapped([] (auto&& f) { | |
132 | try { | |
133 | f.get(); | |
134 | } catch (...) {} // silence exceptional future ignored messages | |
135 | }); | |
136 | } | |
137 | ||
138 | SEASTAR_TEST_CASE(test_exception_thrown_from_then_wrapped_causes_future_to_fail) { | |
139 | return make_ready_future().then_wrapped([] (auto&& f) { | |
140 | throw std::runtime_error(""); | |
141 | }).then_wrapped([] (auto&& f) { | |
142 | try { | |
143 | f.get(); | |
144 | BOOST_REQUIRE(false); | |
145 | } catch (...) {} | |
146 | }); | |
147 | } | |
148 | ||
149 | SEASTAR_TEST_CASE(test_exception_thrown_from_then_wrapped_causes_future_to_fail__async_case) { | |
150 | promise<> p; | |
151 | ||
152 | auto f = p.get_future().then_wrapped([] (auto&& f) { | |
153 | throw std::runtime_error(""); | |
154 | }).then_wrapped([] (auto&& f) { | |
155 | try { | |
156 | f.get(); | |
157 | BOOST_REQUIRE(false); | |
158 | } catch (...) {} | |
159 | }); | |
160 | ||
161 | p.set_value(); | |
162 | ||
163 | return f; | |
164 | } | |
165 | ||
166 | SEASTAR_TEST_CASE(test_failing_intermediate_promise_should_fail_the_master_future) { | |
167 | promise<> p1; | |
168 | promise<> p2; | |
169 | ||
170 | auto f = p1.get_future().then([f = std::move(p2.get_future())] () mutable { | |
171 | return std::move(f); | |
172 | }).then([] { | |
173 | BOOST_REQUIRE(false); | |
174 | }); | |
175 | ||
176 | p1.set_value(); | |
177 | p2.set_exception(std::runtime_error("boom")); | |
178 | ||
179 | return std::move(f).then_wrapped([](auto&& f) { | |
180 | try { | |
181 | f.get(); | |
182 | BOOST_REQUIRE(false); | |
183 | } catch (...) {} | |
184 | }); | |
185 | } | |
186 | ||
187 | SEASTAR_TEST_CASE(test_future_forwarding__not_ready_to_unarmed) { | |
188 | promise<> p1; | |
189 | promise<> p2; | |
190 | ||
191 | auto f1 = p1.get_future(); | |
192 | auto f2 = p2.get_future(); | |
193 | ||
194 | f1.forward_to(std::move(p2)); | |
195 | ||
196 | BOOST_REQUIRE(!f2.available()); | |
197 | ||
198 | auto called = f2.then([] {}); | |
199 | ||
200 | p1.set_value(); | |
201 | return called; | |
202 | } | |
203 | ||
204 | SEASTAR_TEST_CASE(test_future_forwarding__not_ready_to_armed) { | |
205 | promise<> p1; | |
206 | promise<> p2; | |
207 | ||
208 | auto f1 = p1.get_future(); | |
209 | auto f2 = p2.get_future(); | |
210 | ||
211 | auto called = f2.then([] {}); | |
212 | ||
213 | f1.forward_to(std::move(p2)); | |
214 | ||
215 | BOOST_REQUIRE(!f2.available()); | |
216 | ||
217 | p1.set_value(); | |
218 | ||
219 | return called; | |
220 | } | |
221 | ||
222 | SEASTAR_TEST_CASE(test_future_forwarding__ready_to_unarmed) { | |
223 | promise<> p2; | |
224 | ||
225 | auto f1 = make_ready_future<>(); | |
226 | auto f2 = p2.get_future(); | |
227 | ||
228 | std::move(f1).forward_to(std::move(p2)); | |
229 | BOOST_REQUIRE(f2.available()); | |
230 | ||
231 | return std::move(f2).then_wrapped([] (future<> f) { | |
232 | BOOST_REQUIRE(!f.failed()); | |
233 | }); | |
234 | } | |
235 | ||
236 | SEASTAR_TEST_CASE(test_future_forwarding__ready_to_armed) { | |
237 | promise<> p2; | |
238 | ||
239 | auto f1 = make_ready_future<>(); | |
240 | auto f2 = p2.get_future(); | |
241 | ||
242 | auto called = std::move(f2).then([] {}); | |
243 | ||
244 | BOOST_REQUIRE(f1.available()); | |
245 | ||
246 | f1.forward_to(std::move(p2)); | |
247 | return called; | |
248 | } | |
249 | ||
250 | static void forward_dead_unarmed_promise_with_dead_future_to(promise<>& p) { | |
251 | promise<> p2; | |
252 | p.get_future().forward_to(std::move(p2)); | |
253 | } | |
254 | ||
255 | SEASTAR_TEST_CASE(test_future_forwarding__ready_to_unarmed_soon_to_be_dead) { | |
256 | promise<> p1; | |
257 | forward_dead_unarmed_promise_with_dead_future_to(p1); | |
258 | make_ready_future<>().forward_to(std::move(p1)); | |
259 | return make_ready_future<>(); | |
260 | } | |
261 | ||
262 | SEASTAR_TEST_CASE(test_exception_can_be_thrown_from_do_until_body) { | |
263 | return do_until([] { return false; }, [] { | |
264 | throw expected_exception(); | |
265 | return now(); | |
266 | }).then_wrapped([] (auto&& f) { | |
267 | try { | |
268 | f.get(); | |
269 | BOOST_FAIL("should have failed"); | |
270 | } catch (const expected_exception& e) { | |
271 | // expected | |
272 | } | |
273 | }); | |
274 | } | |
275 | ||
276 | SEASTAR_TEST_CASE(test_bare_value_can_be_returned_from_callback) { | |
277 | return now().then([] { | |
278 | return 3; | |
279 | }).then([] (int x) { | |
280 | BOOST_REQUIRE(x == 3); | |
281 | }); | |
282 | } | |
283 | ||
284 | SEASTAR_TEST_CASE(test_when_all_iterator_range) { | |
285 | std::vector<future<size_t>> futures; | |
286 | for (size_t i = 0; i != 1000000; ++i) { | |
287 | // .then() usually returns a ready future, but sometimes it | |
288 | // doesn't, so call it a million times. This exercises both | |
289 | // available and unavailable paths in when_all(). | |
290 | futures.push_back(make_ready_future<>().then([i] { return i; })); | |
291 | } | |
292 | // Verify the above statement is correct | |
293 | BOOST_REQUIRE(!std::all_of(futures.begin(), futures.end(), | |
294 | [] (auto& f) { return f.available(); })); | |
295 | auto p = make_shared(std::move(futures)); | |
296 | return when_all(p->begin(), p->end()).then([p] (std::vector<future<size_t>> ret) { | |
297 | BOOST_REQUIRE(std::all_of(ret.begin(), ret.end(), [] (auto& f) { return f.available(); })); | |
298 | BOOST_REQUIRE(std::all_of(ret.begin(), ret.end(), [&ret] (auto& f) { return std::get<0>(f.get()) == size_t(&f - ret.data()); })); | |
299 | }); | |
300 | } | |
301 | ||
302 | SEASTAR_TEST_CASE(test_map_reduce) { | |
303 | auto square = [] (long x) { return make_ready_future<long>(x*x); }; | |
304 | long n = 1000; | |
305 | return map_reduce(boost::make_counting_iterator<long>(0), boost::make_counting_iterator<long>(n), | |
306 | square, long(0), std::plus<long>()).then([n] (auto result) { | |
307 | auto m = n - 1; // counting does not include upper bound | |
308 | BOOST_REQUIRE_EQUAL(result, (m * (m + 1) * (2*m + 1)) / 6); | |
309 | }); | |
310 | } | |
311 | ||
312 | // This test doesn't actually test anything - it just waits for the future | |
313 | // returned by sleep to complete. However, a bug we had in sleep() caused | |
314 | // this test to fail the sanitizer in the debug build, so this is a useful | |
315 | // regression test. | |
316 | SEASTAR_TEST_CASE(test_sleep) { | |
317 | return sleep(std::chrono::milliseconds(100)); | |
318 | } | |
319 | ||
320 | SEASTAR_TEST_CASE(test_do_with_1) { | |
321 | return do_with(1, [] (int& one) { | |
322 | BOOST_REQUIRE_EQUAL(one, 1); | |
323 | return make_ready_future<>(); | |
324 | }); | |
325 | } | |
326 | ||
327 | SEASTAR_TEST_CASE(test_do_with_2) { | |
328 | return do_with(1, 2L, [] (int& one, long two) { | |
329 | BOOST_REQUIRE_EQUAL(one, 1); | |
330 | BOOST_REQUIRE_EQUAL(two, 2); | |
331 | return make_ready_future<>(); | |
332 | }); | |
333 | } | |
334 | ||
335 | SEASTAR_TEST_CASE(test_do_with_3) { | |
336 | return do_with(1, 2L, 3, [] (int& one, long two, int three) { | |
337 | BOOST_REQUIRE_EQUAL(one, 1); | |
338 | BOOST_REQUIRE_EQUAL(two, 2); | |
339 | BOOST_REQUIRE_EQUAL(three, 3); | |
340 | return make_ready_future<>(); | |
341 | }); | |
342 | } | |
343 | ||
344 | SEASTAR_TEST_CASE(test_do_with_4) { | |
345 | return do_with(1, 2L, 3, 4, [] (int& one, long two, int three, int four) { | |
346 | BOOST_REQUIRE_EQUAL(one, 1); | |
347 | BOOST_REQUIRE_EQUAL(two, 2); | |
348 | BOOST_REQUIRE_EQUAL(three, 3); | |
349 | BOOST_REQUIRE_EQUAL(four, 4); | |
350 | return make_ready_future<>(); | |
351 | }); | |
352 | } | |
353 | ||
354 | SEASTAR_TEST_CASE(test_do_while_stopping_immediately) { | |
355 | return do_with(int(0), [] (int& count) { | |
356 | return repeat([&count] { | |
357 | ++count; | |
358 | return stop_iteration::yes; | |
359 | }).then([&count] { | |
360 | BOOST_REQUIRE(count == 1); | |
361 | }); | |
362 | }); | |
363 | } | |
364 | ||
365 | SEASTAR_TEST_CASE(test_do_while_stopping_after_two_iterations) { | |
366 | return do_with(int(0), [] (int& count) { | |
367 | return repeat([&count] { | |
368 | ++count; | |
369 | return count == 2 ? stop_iteration::yes : stop_iteration::no; | |
370 | }).then([&count] { | |
371 | BOOST_REQUIRE(count == 2); | |
372 | }); | |
373 | }); | |
374 | } | |
375 | ||
376 | SEASTAR_TEST_CASE(test_do_while_failing_in_the_first_step) { | |
377 | return repeat([] { | |
378 | throw expected_exception(); | |
379 | return stop_iteration::no; | |
380 | }).then_wrapped([](auto&& f) { | |
381 | try { | |
382 | f.get(); | |
383 | BOOST_FAIL("should not happen"); | |
384 | } catch (const expected_exception&) { | |
385 | // expected | |
386 | } | |
387 | }); | |
388 | } | |
389 | ||
390 | SEASTAR_TEST_CASE(test_do_while_failing_in_the_second_step) { | |
391 | return do_with(int(0), [] (int& count) { | |
392 | return repeat([&count] { | |
393 | ++count; | |
394 | if (count > 1) { | |
395 | throw expected_exception(); | |
396 | } | |
397 | return later().then([] { return stop_iteration::no; }); | |
398 | }).then_wrapped([&count](auto&& f) { | |
399 | try { | |
400 | f.get(); | |
401 | BOOST_FAIL("should not happen"); | |
402 | } catch (const expected_exception&) { | |
403 | BOOST_REQUIRE(count == 2); | |
404 | } | |
405 | }); | |
406 | }); | |
407 | } | |
408 | ||
409 | SEASTAR_TEST_CASE(test_parallel_for_each) { | |
410 | return async([] { | |
411 | // empty | |
412 | parallel_for_each(std::vector<int>(), [] (int) -> future<> { | |
413 | BOOST_FAIL("should not reach"); | |
414 | abort(); | |
415 | }).get(); | |
416 | ||
417 | // immediate result | |
418 | auto range = boost::copy_range<std::vector<int>>(boost::irange(1, 6)); | |
419 | auto sum = 0; | |
420 | parallel_for_each(range, [&sum] (int v) { | |
421 | sum += v; | |
422 | return make_ready_future<>(); | |
423 | }).get(); | |
424 | BOOST_REQUIRE_EQUAL(sum, 15); | |
425 | ||
426 | // all suspend | |
427 | sum = 0; | |
428 | parallel_for_each(range, [&sum] (int v) { | |
429 | return later().then([&sum, v] { | |
430 | sum += v; | |
431 | }); | |
432 | }).get(); | |
433 | BOOST_REQUIRE_EQUAL(sum, 15); | |
434 | ||
435 | // throws immediately | |
436 | BOOST_CHECK_EXCEPTION(parallel_for_each(range, [&sum] (int) -> future<> { | |
437 | throw 5; | |
438 | }).get(), int, [] (int v) { return v == 5; }); | |
439 | ||
440 | // throws after suspension | |
441 | BOOST_CHECK_EXCEPTION(parallel_for_each(range, [&sum] (int) { | |
442 | return later().then([] { | |
443 | throw 5; | |
444 | }); | |
445 | }).get(), int, [] (int v) { return v == 5; }); | |
446 | }); | |
447 | } | |
448 | ||
449 | SEASTAR_TEST_CASE(test_parallel_for_each_early_failure) { | |
450 | return do_with(0, [] (int& counter) { | |
451 | return parallel_for_each(boost::irange(0, 11000), [&counter] (int i) { | |
452 | using namespace std::chrono_literals; | |
453 | // force scheduling | |
454 | return sleep((i % 31 + 1) * 1ms).then([&counter, i] { | |
455 | ++counter; | |
456 | if (i % 1777 == 1337) { | |
457 | return make_exception_future<>(i); | |
458 | } | |
459 | return make_ready_future<>(); | |
460 | }); | |
461 | }).then_wrapped([&counter] (future<> f) { | |
462 | BOOST_REQUIRE_EQUAL(counter, 11000); | |
463 | BOOST_REQUIRE(f.failed()); | |
464 | try { | |
465 | f.get(); | |
466 | BOOST_FAIL("wanted an exception"); | |
467 | } catch (int i) { | |
468 | BOOST_REQUIRE(i % 1777 == 1337); | |
469 | } catch (...) { | |
470 | BOOST_FAIL("bad exception type"); | |
471 | } | |
472 | }); | |
473 | }); | |
474 | } | |
475 | ||
476 | SEASTAR_TEST_CASE(test_parallel_for_each_waits_for_all_fibers_even_if_one_of_them_failed) { | |
477 | auto can_exit = make_lw_shared<bool>(false); | |
478 | return parallel_for_each(boost::irange(0, 2), [can_exit] (int i) { | |
479 | return later().then([i, can_exit] { | |
480 | if (i == 1) { | |
481 | throw expected_exception(); | |
482 | } else { | |
483 | using namespace std::chrono_literals; | |
484 | return sleep(300ms).then([can_exit] { | |
485 | *can_exit = true; | |
486 | }); | |
487 | } | |
488 | }); | |
489 | }).then_wrapped([can_exit] (auto&& f) { | |
490 | try { | |
491 | f.get(); | |
492 | } catch (...) { | |
493 | // expected | |
494 | } | |
495 | BOOST_REQUIRE(*can_exit); | |
496 | }); | |
497 | } | |
498 | ||
499 | #ifndef SEASTAR_SHUFFLE_TASK_QUEUE | |
500 | SEASTAR_TEST_CASE(test_high_priority_task_runs_before_ready_continuations) { | |
501 | return now().then([] { | |
502 | auto flag = make_lw_shared<bool>(false); | |
503 | engine().add_high_priority_task(make_task([flag] { | |
504 | *flag = true; | |
505 | })); | |
506 | make_ready_future().then([flag] { | |
507 | BOOST_REQUIRE(*flag); | |
508 | }); | |
509 | }); | |
510 | } | |
511 | ||
512 | SEASTAR_TEST_CASE(test_high_priority_task_runs_in_the_middle_of_loops) { | |
513 | auto counter = make_lw_shared<int>(0); | |
514 | auto flag = make_lw_shared<bool>(false); | |
515 | return repeat([counter, flag] { | |
516 | if (*counter == 1) { | |
517 | BOOST_REQUIRE(*flag); | |
518 | return stop_iteration::yes; | |
519 | } | |
520 | engine().add_high_priority_task(make_task([flag] { | |
521 | *flag = true; | |
522 | })); | |
523 | ++(*counter); | |
524 | return stop_iteration::no; | |
525 | }); | |
526 | } | |
527 | #endif | |
528 | ||
529 | SEASTAR_TEST_CASE(futurize_apply_val_exception) { | |
530 | return futurize<int>::apply([] (int arg) { throw expected_exception(); return arg; }, 1).then_wrapped([] (future<int> f) { | |
531 | try { | |
532 | f.get(); | |
533 | BOOST_FAIL("should have thrown"); | |
534 | } catch (expected_exception& e) {} | |
535 | }); | |
536 | } | |
537 | ||
538 | SEASTAR_TEST_CASE(futurize_apply_val_ok) { | |
539 | return futurize<int>::apply([] (int arg) { return arg * 2; }, 2).then_wrapped([] (future<int> f) { | |
540 | try { | |
541 | auto x = f.get0(); | |
542 | BOOST_REQUIRE_EQUAL(x, 4); | |
543 | } catch (expected_exception& e) { | |
544 | BOOST_FAIL("should not have thrown"); | |
545 | } | |
546 | }); | |
547 | } | |
548 | ||
549 | SEASTAR_TEST_CASE(futurize_apply_val_future_exception) { | |
550 | return futurize<int>::apply([] (int a) { | |
551 | return sleep(std::chrono::milliseconds(100)).then([] { | |
552 | throw expected_exception(); | |
553 | return make_ready_future<int>(0); | |
554 | }); | |
555 | }, 0).then_wrapped([] (future<int> f) { | |
556 | try { | |
557 | f.get(); | |
558 | BOOST_FAIL("should have thrown"); | |
559 | } catch (expected_exception& e) { } | |
560 | }); | |
561 | } | |
562 | ||
563 | SEASTAR_TEST_CASE(futurize_apply_val_future_ok) { | |
564 | return futurize<int>::apply([] (int a) { | |
565 | return sleep(std::chrono::milliseconds(100)).then([a] { | |
566 | return make_ready_future<int>(a * 100); | |
567 | }); | |
568 | }, 2).then_wrapped([] (future<int> f) { | |
569 | try { | |
570 | auto x = f.get0(); | |
571 | BOOST_REQUIRE_EQUAL(x, 200); | |
572 | } catch (expected_exception& e) { | |
573 | BOOST_FAIL("should not have thrown"); | |
574 | } | |
575 | }); | |
576 | } | |
577 | SEASTAR_TEST_CASE(futurize_apply_void_exception) { | |
578 | return futurize<void>::apply([] (auto arg) { throw expected_exception(); }, 0).then_wrapped([] (future<> f) { | |
579 | try { | |
580 | f.get(); | |
581 | BOOST_FAIL("should have thrown"); | |
582 | } catch (expected_exception& e) {} | |
583 | }); | |
584 | } | |
585 | ||
586 | SEASTAR_TEST_CASE(futurize_apply_void_ok) { | |
587 | return futurize<void>::apply([] (auto arg) { }, 0).then_wrapped([] (future<> f) { | |
588 | try { | |
589 | f.get(); | |
590 | } catch (expected_exception& e) { | |
591 | BOOST_FAIL("should not have thrown"); | |
592 | } | |
593 | }); | |
594 | } | |
595 | ||
596 | SEASTAR_TEST_CASE(futurize_apply_void_future_exception) { | |
597 | return futurize<void>::apply([] (auto a) { | |
598 | return sleep(std::chrono::milliseconds(100)).then([] { | |
599 | throw expected_exception(); | |
600 | }); | |
601 | }, 0).then_wrapped([] (future<> f) { | |
602 | try { | |
603 | f.get(); | |
604 | BOOST_FAIL("should have thrown"); | |
605 | } catch (expected_exception& e) { } | |
606 | }); | |
607 | } | |
608 | ||
609 | SEASTAR_TEST_CASE(futurize_apply_void_future_ok) { | |
610 | auto a = make_lw_shared<int>(1); | |
611 | return futurize<void>::apply([] (int& a) { | |
612 | return sleep(std::chrono::milliseconds(100)).then([&a] { | |
613 | a *= 100; | |
614 | }); | |
615 | }, *a).then_wrapped([a] (future<> f) { | |
616 | try { | |
617 | f.get(); | |
618 | BOOST_REQUIRE_EQUAL(*a, 100); | |
619 | } catch (expected_exception& e) { | |
620 | BOOST_FAIL("should not have thrown"); | |
621 | } | |
622 | }); | |
623 | } | |
624 | ||
625 | SEASTAR_TEST_CASE(test_shared_future_propagates_value_to_all) { | |
626 | return seastar::async([] { | |
627 | promise<shared_ptr<int>> p; // shared_ptr<> to check it deals with emptyable types | |
628 | shared_future<shared_ptr<int>> f(p.get_future()); | |
629 | ||
630 | auto f1 = f.get_future(); | |
631 | auto f2 = f.get_future(); | |
632 | ||
633 | p.set_value(make_shared<int>(1)); | |
634 | BOOST_REQUIRE(*f1.get0() == 1); | |
635 | BOOST_REQUIRE(*f2.get0() == 1); | |
636 | }); | |
637 | } | |
638 | ||
639 | template<typename... T> | |
640 | void check_fails_with_expected(future<T...> f) { | |
641 | try { | |
642 | f.get(); | |
643 | BOOST_FAIL("Should have failed"); | |
644 | } catch (expected_exception&) { | |
645 | // expected | |
646 | } | |
647 | } | |
648 | ||
649 | SEASTAR_TEST_CASE(test_shared_future_propagates_value_to_copies) { | |
650 | return seastar::async([] { | |
651 | promise<int> p; | |
652 | auto sf1 = shared_future<int>(p.get_future()); | |
653 | auto sf2 = sf1; | |
654 | ||
655 | auto f1 = sf1.get_future(); | |
656 | auto f2 = sf2.get_future(); | |
657 | ||
658 | p.set_value(1); | |
659 | ||
660 | BOOST_REQUIRE(f1.get0() == 1); | |
661 | BOOST_REQUIRE(f2.get0() == 1); | |
662 | }); | |
663 | } | |
664 | ||
665 | SEASTAR_TEST_CASE(test_obtaining_future_from_shared_future_after_it_is_resolved) { | |
666 | promise<int> p1; | |
667 | promise<int> p2; | |
668 | auto sf1 = shared_future<int>(p1.get_future()); | |
669 | auto sf2 = shared_future<int>(p2.get_future()); | |
670 | p1.set_value(1); | |
671 | p2.set_exception(expected_exception()); | |
672 | return sf2.get_future().then_wrapped([f1 = sf1.get_future()] (auto&& f) mutable { | |
673 | check_fails_with_expected(std::move(f)); | |
674 | return std::move(f1); | |
675 | }).then_wrapped([] (auto&& f) { | |
676 | BOOST_REQUIRE(f.get0() == 1); | |
677 | }); | |
678 | } | |
679 | ||
680 | SEASTAR_TEST_CASE(test_valueless_shared_future) { | |
681 | return seastar::async([] { | |
682 | promise<> p; | |
683 | shared_future<> f(p.get_future()); | |
684 | ||
685 | auto f1 = f.get_future(); | |
686 | auto f2 = f.get_future(); | |
687 | ||
688 | p.set_value(); | |
689 | ||
690 | f1.get(); | |
691 | f2.get(); | |
692 | }); | |
693 | } | |
694 | ||
695 | SEASTAR_TEST_CASE(test_shared_future_propagates_errors_to_all) { | |
696 | promise<int> p; | |
697 | shared_future<int> f(p.get_future()); | |
698 | ||
699 | auto f1 = f.get_future(); | |
700 | auto f2 = f.get_future(); | |
701 | ||
702 | p.set_exception(expected_exception()); | |
703 | ||
704 | return f1.then_wrapped([f2 = std::move(f2)] (auto&& f) mutable { | |
705 | check_fails_with_expected(std::move(f)); | |
706 | return std::move(f2); | |
707 | }).then_wrapped([] (auto&& f) mutable { | |
708 | check_fails_with_expected(std::move(f)); | |
709 | }); | |
710 | } | |
711 | ||
712 | SEASTAR_TEST_CASE(test_futurize_from_tuple) { | |
713 | std::tuple<int> v1 = std::make_tuple(3); | |
714 | std::tuple<> v2 = {}; | |
715 | BOOST_REQUIRE(futurize<int>::from_tuple(v1).get() == v1); | |
716 | BOOST_REQUIRE(futurize<void>::from_tuple(v2).get() == v2); | |
717 | return make_ready_future<>(); | |
718 | } | |
719 | ||
720 | SEASTAR_TEST_CASE(test_repeat_until_value) { | |
721 | return do_with(int(), [] (int& counter) { | |
722 | return repeat_until_value([&counter] () -> future<compat::optional<int>> { | |
723 | if (counter == 10000) { | |
724 | return make_ready_future<compat::optional<int>>(counter); | |
725 | } else { | |
726 | ++counter; | |
727 | return make_ready_future<compat::optional<int>>(compat::nullopt); | |
728 | } | |
729 | }).then([&counter] (int result) { | |
730 | BOOST_REQUIRE(counter == 10000); | |
731 | BOOST_REQUIRE(result == counter); | |
732 | }); | |
733 | }); | |
734 | } | |
735 | ||
736 | SEASTAR_TEST_CASE(test_when_allx) { | |
737 | return when_all(later(), later(), make_ready_future()).discard_result(); | |
738 | } | |
739 | ||
740 | template<typename E, typename... T> | |
741 | static void check_failed_with(future<T...>&& f) { | |
742 | BOOST_REQUIRE(f.failed()); | |
743 | try { | |
744 | f.get(); | |
745 | BOOST_FAIL("exception expected"); | |
746 | } catch (const E& e) { | |
747 | // expected | |
748 | } catch (...) { | |
749 | BOOST_FAIL(format("wrong exception: {}", std::current_exception())); | |
750 | } | |
751 | } | |
752 | ||
753 | template<typename... T> | |
754 | static void check_timed_out(future<T...>&& f) { | |
755 | check_failed_with<timed_out_error>(std::move(f)); | |
756 | } | |
757 | ||
758 | SEASTAR_TEST_CASE(test_with_timeout_when_it_times_out) { | |
759 | return seastar::async([] { | |
760 | promise<> pr; | |
761 | auto f = with_timeout(manual_clock::now() + 2s, pr.get_future()); | |
762 | ||
763 | BOOST_REQUIRE(!f.available()); | |
764 | ||
765 | manual_clock::advance(1s); | |
766 | later().get(); | |
767 | ||
768 | BOOST_REQUIRE(!f.available()); | |
769 | ||
770 | manual_clock::advance(1s); | |
771 | later().get(); | |
772 | ||
773 | check_timed_out(std::move(f)); | |
774 | ||
775 | pr.set_value(); | |
776 | }); | |
777 | } | |
778 | ||
779 | SEASTAR_TEST_CASE(test_custom_exception_factory_in_with_timeout) { | |
780 | return seastar::async([] { | |
781 | class custom_error : public std::exception { | |
782 | public: | |
783 | virtual const char* what() const noexcept { | |
784 | return "timedout"; | |
785 | } | |
786 | }; | |
787 | struct my_exception_factory { | |
788 | static auto timeout() { | |
789 | return custom_error(); | |
790 | } | |
791 | }; | |
792 | promise<> pr; | |
793 | auto f = with_timeout<my_exception_factory>(manual_clock::now() + 1s, pr.get_future()); | |
794 | ||
795 | manual_clock::advance(1s); | |
796 | later().get(); | |
797 | ||
798 | check_failed_with<custom_error>(std::move(f)); | |
799 | }); | |
800 | } | |
801 | ||
802 | SEASTAR_TEST_CASE(test_with_timeout_when_it_does_not_time_out) { | |
803 | return seastar::async([] { | |
804 | { | |
805 | promise<int> pr; | |
806 | auto f = with_timeout(manual_clock::now() + 1s, pr.get_future()); | |
807 | ||
808 | pr.set_value(42); | |
809 | ||
810 | BOOST_REQUIRE_EQUAL(f.get0(), 42); | |
811 | } | |
812 | ||
813 | // Check that timer was indeed cancelled | |
814 | manual_clock::advance(1s); | |
815 | later().get(); | |
816 | }); | |
817 | } | |
818 | ||
819 | SEASTAR_TEST_CASE(test_shared_future_with_timeout) { | |
820 | return seastar::async([] { | |
821 | shared_promise<with_clock<manual_clock>, int> pr; | |
822 | auto f1 = pr.get_shared_future(manual_clock::now() + 1s); | |
823 | auto f2 = pr.get_shared_future(manual_clock::now() + 2s); | |
824 | auto f3 = pr.get_shared_future(); | |
825 | ||
826 | BOOST_REQUIRE(!f1.available()); | |
827 | BOOST_REQUIRE(!f2.available()); | |
828 | BOOST_REQUIRE(!f3.available()); | |
829 | ||
830 | manual_clock::advance(1s); | |
831 | later().get(); | |
832 | ||
833 | check_timed_out(std::move(f1)); | |
834 | BOOST_REQUIRE(!f2.available()); | |
835 | BOOST_REQUIRE(!f3.available()); | |
836 | ||
837 | manual_clock::advance(1s); | |
838 | later().get(); | |
839 | ||
840 | check_timed_out(std::move(f2)); | |
841 | BOOST_REQUIRE(!f3.available()); | |
842 | ||
843 | pr.set_value(42); | |
844 | ||
845 | BOOST_REQUIRE_EQUAL(42, f3.get0()); | |
846 | }); | |
847 | } | |
848 | ||
849 | SEASTAR_TEST_CASE(test_when_all_succeed_tuples) { | |
850 | return seastar::when_all_succeed( | |
851 | make_ready_future<>(), | |
852 | make_ready_future<sstring>("hello world"), | |
853 | make_ready_future<int>(42), | |
854 | make_ready_future<>(), | |
855 | make_ready_future<int, sstring>(84, "hi"), | |
856 | make_ready_future<bool>(true) | |
857 | ).then([] (sstring msg, int v, std::tuple<int, sstring> t, bool b) { | |
858 | BOOST_REQUIRE_EQUAL(msg, "hello world"); | |
859 | BOOST_REQUIRE_EQUAL(v, 42); | |
860 | BOOST_REQUIRE_EQUAL(std::get<0>(t), 84); | |
861 | BOOST_REQUIRE_EQUAL(std::get<1>(t), "hi"); | |
862 | BOOST_REQUIRE_EQUAL(b, true); | |
863 | ||
864 | return seastar::when_all_succeed( | |
865 | make_exception_future<>(42), | |
866 | make_ready_future<sstring>("hello world"), | |
867 | make_exception_future<int>(43), | |
868 | make_ready_future<>() | |
869 | ).then([] (sstring, int) { | |
870 | BOOST_FAIL("shouldn't reach"); | |
871 | return false; | |
872 | }).handle_exception([] (auto excp) { | |
873 | try { | |
874 | std::rethrow_exception(excp); | |
875 | } catch (int v) { | |
876 | BOOST_REQUIRE(v == 42 || v == 43); | |
877 | return true; | |
878 | } catch (...) { } | |
879 | return false; | |
880 | }).then([] (auto ret) { | |
881 | BOOST_REQUIRE(ret); | |
882 | }); | |
883 | }); | |
884 | } | |
885 | ||
886 | SEASTAR_TEST_CASE(test_when_all_succeed_vector) { | |
887 | std::vector<future<>> vecs; | |
888 | vecs.emplace_back(make_ready_future<>()); | |
889 | vecs.emplace_back(make_ready_future<>()); | |
890 | vecs.emplace_back(make_ready_future<>()); | |
891 | vecs.emplace_back(make_ready_future<>()); | |
892 | return seastar::when_all_succeed(vecs.begin(), vecs.end()).then([] { | |
893 | std::vector<future<>> vecs; | |
894 | vecs.emplace_back(make_ready_future<>()); | |
895 | vecs.emplace_back(make_ready_future<>()); | |
896 | vecs.emplace_back(make_exception_future<>(42)); | |
897 | vecs.emplace_back(make_exception_future<>(43)); | |
898 | return seastar::when_all_succeed(vecs.begin(), vecs.end()); | |
899 | }).then([] { | |
900 | BOOST_FAIL("shouldn't reach"); | |
901 | return false; | |
902 | }).handle_exception([] (auto excp) { | |
903 | try { | |
904 | std::rethrow_exception(excp); | |
905 | } catch (int v) { | |
906 | BOOST_REQUIRE(v == 42 || v == 43); | |
907 | return true; | |
908 | } catch (...) { } | |
909 | return false; | |
910 | }).then([] (auto ret) { | |
911 | BOOST_REQUIRE(ret); | |
912 | ||
913 | std::vector<future<int>> vecs; | |
914 | vecs.emplace_back(make_ready_future<int>(1)); | |
915 | vecs.emplace_back(make_ready_future<int>(2)); | |
916 | vecs.emplace_back(make_ready_future<int>(3)); | |
917 | return seastar::when_all_succeed(vecs.begin(), vecs.end()); | |
918 | }).then([] (std::vector<int> vals) { | |
919 | BOOST_REQUIRE_EQUAL(vals.size(), 3u); | |
920 | BOOST_REQUIRE_EQUAL(vals[0], 1); | |
921 | BOOST_REQUIRE_EQUAL(vals[1], 2); | |
922 | BOOST_REQUIRE_EQUAL(vals[2], 3); | |
923 | ||
924 | std::vector<future<int>> vecs; | |
925 | vecs.emplace_back(make_ready_future<int>(1)); | |
926 | vecs.emplace_back(make_ready_future<int>(2)); | |
927 | vecs.emplace_back(make_exception_future<int>(42)); | |
928 | vecs.emplace_back(make_exception_future<int>(43)); | |
929 | return seastar::when_all_succeed(vecs.begin(), vecs.end()); | |
930 | }).then([] (std::vector<int>) { | |
931 | BOOST_FAIL("shouldn't reach"); | |
932 | return false; | |
933 | }).handle_exception([] (auto excp) { | |
934 | try { | |
935 | std::rethrow_exception(excp); | |
936 | } catch (int v) { | |
937 | BOOST_REQUIRE(v == 42 || v == 43); | |
938 | return true; | |
939 | } catch (...) { } | |
940 | return false; | |
941 | }).then([] (auto ret) { | |
942 | BOOST_REQUIRE(ret); | |
943 | }); | |
944 | } | |
945 | ||
946 | SEASTAR_TEST_CASE(test_futurize_mutable) { | |
947 | int count = 0; | |
948 | return seastar::repeat([count]() mutable { | |
949 | ++count; | |
950 | if (count == 3) { | |
951 | return seastar::stop_iteration::yes; | |
952 | } | |
953 | return seastar::stop_iteration::no; | |
954 | }); | |
955 | } |