|
4 | 4 | #include "counter.hpp" |
5 | 5 | #include "crhandle/detachedhandle.hpp" |
6 | 6 | #include "crhandle/taskhandle.hpp" |
| 7 | +#include "dispatcher.hpp" |
7 | 8 |
|
8 | 9 | #include <deque> |
9 | 10 | #include <optional> |
@@ -79,6 +80,15 @@ struct TaskHandleFixture : public ::testing::Test |
79 | 80 | void await_suspend(stdcr::coroutine_handle<> h) { state.handle = h; } |
80 | 81 | void await_resume() {} |
81 | 82 | }; |
| 83 | + |
| 84 | + template <typename S> |
| 85 | + struct Canceler |
| 86 | + { |
| 87 | + S & state; |
| 88 | + bool await_ready() { return false; } |
| 89 | + void await_suspend(stdcr::coroutine_handle<> h) { state.handle = h; } |
| 90 | + void await_resume() { throw cr::CanceledException(); } |
| 91 | + }; |
82 | 92 | }; |
83 | 93 |
|
84 | 94 | TEST_F(TaskHandleFixture, task_runs_if_handle_is_alive) |
@@ -629,4 +639,67 @@ TEST_F(TaskHandleFixture, eager_task_resumes_its_continuation) |
629 | 639 | EXPECT_EQ(42, value); |
630 | 640 | } |
631 | 641 |
|
| 642 | +TEST_F(TaskHandleFixture, nested_lazy_tasks_can_be_canceled_top_down) |
| 643 | +{ |
| 644 | + using TaskType = cr::TaskHandle<void, ::ManualDispatcher::Executor>; |
| 645 | + |
| 646 | + ::ManualDispatcher dispatcher; |
| 647 | + |
| 648 | + struct State |
| 649 | + { |
| 650 | + bool beforeSuspend = false; |
| 651 | + bool afterSuspend = false; |
| 652 | + stdcr::coroutine_handle<> handle = nullptr; |
| 653 | + } state; |
| 654 | + |
| 655 | + static auto InnerTask = [](State & state) -> TaskType { |
| 656 | + co_await Canceler<State>{state}; |
| 657 | + }; |
| 658 | + |
| 659 | + static auto OuterTask = [](State & state) -> TaskType { |
| 660 | + state.beforeSuspend = true; |
| 661 | + co_await InnerTask(state); |
| 662 | + state.afterSuspend = true; |
| 663 | + }; |
| 664 | + |
| 665 | + auto task = OuterTask(state); |
| 666 | + task.Run(dispatcher.GetExecutor()); |
| 667 | + dispatcher.ProcessAll(); |
| 668 | + EXPECT_TRUE(state.handle); |
| 669 | + EXPECT_TRUE(state.beforeSuspend); |
| 670 | + EXPECT_FALSE(state.afterSuspend); |
| 671 | + EXPECT_TRUE(task); |
| 672 | + |
| 673 | + state.handle.resume(); |
| 674 | + EXPECT_TRUE(state.handle.done()); |
| 675 | + |
| 676 | + EXPECT_TRUE(dispatcher.ProcessOneTask()); |
| 677 | + EXPECT_FALSE(dispatcher.ProcessOneTask()); |
| 678 | + EXPECT_FALSE(state.afterSuspend); |
| 679 | + EXPECT_FALSE(task); |
| 680 | +} |
| 681 | + |
| 682 | +TEST_F(TaskHandleFixture, nested_eager_tasks_can_be_canceled_top_down) |
| 683 | +{ |
| 684 | + struct ImmediateCanceler |
| 685 | + { |
| 686 | + bool await_ready() { return true; } |
| 687 | + void await_suspend(stdcr::coroutine_handle<>) {} |
| 688 | + void await_resume() { throw cr::CanceledException(); } |
| 689 | + }; |
| 690 | + |
| 691 | + static auto InnerTask = []() -> cr::TaskHandle<void> { |
| 692 | + co_await ImmediateCanceler{}; |
| 693 | + }; |
| 694 | + |
| 695 | + static auto OuterTask = []() -> cr::TaskHandle<void> { |
| 696 | + co_await InnerTask(); |
| 697 | + }; |
| 698 | + |
| 699 | + auto task = OuterTask(); |
| 700 | + EXPECT_TRUE(task); |
| 701 | + task.Run(); |
| 702 | + EXPECT_FALSE(task); |
| 703 | +} |
| 704 | + |
632 | 705 | } // namespace |
0 commit comments