From 1a614286128ee19f389eb3925b1f00c94b2c1544 Mon Sep 17 00:00:00 2001 From: Ben Deane Date: Wed, 4 Dec 2024 10:59:32 -0700 Subject: [PATCH] :art: Allow a sender to cause its own timeout with `timeout_after` Problem: - `timeout_after(s, to)` cannot cause its own timeout. - The ability to write a sender that causes its own timeout is useful for tests. Solution: - Start the timer sender before the sender to be timed out. - This means the time allowed includes the time for the sender logic itself, rather than excluding it. That's an arbitrary decision that's easy to account for in production code, and starting the timer first means that tests can easily rig the sender to cause its own timeout, for easy timeout test scenarios. Notes: - Compare: `incite_on`. `timeout_after` is similar in this respect. --- include/async/when_any.hpp | 7 +++---- test/timeout_after.cpp | 25 +++++++++++++++++++++++-- test/when_any.cpp | 8 ++++---- 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/include/async/when_any.hpp b/include/async/when_any.hpp index 06d0d51..7a23617 100644 --- a/include/async/when_any.hpp +++ b/include/async/when_any.hpp @@ -492,10 +492,9 @@ template struct pipeable { private: template Self> friend constexpr auto operator|(S &&s, Self &&self) -> async::sender auto { - return sender, 0>, - sub_sender>{std::forward(s), - std::forward(self).sndr}; + return sender, + sub_sender, 1>>{ + std::forward(self).sndr, std::forward(s)}; } }; } // namespace _when_any diff --git a/test/timeout_after.cpp b/test/timeout_after.cpp index 34b390b..7a35a4d 100644 --- a/test/timeout_after.cpp +++ b/test/timeout_after.cpp @@ -1,6 +1,7 @@ #include "detail/common.hpp" #include +#include #include #include #include @@ -118,6 +119,26 @@ TEST_CASE("timeout_after is pipeable", "[timeout_after]") { CHECK(async::timer_mgr::is_idle()); } +TEST_CASE("timeout_after can incite its own timeout", "[timeout_after]") { + current_time = tp_t{}; + int var{}; + auto s = + async::start_on(async::time_scheduler{1s}, async::just_result_of([] { + current_time = tp_t{3s}; + CHECK(not async::timer_mgr::is_idle()); + async::timer_mgr::service_task(); + return 42; + })); + auto to = s | async::timeout_after(2s, 17); + auto op = async::connect(to, error_receiver{[&](int i) { var = i; }}); + async::start(op); + + current_time = tp_t{1s}; + async::timer_mgr::service_task(); + CHECK(var == 17); + CHECK(async::timer_mgr::is_idle()); +} + namespace { struct alt_domain; using alt_timer_manager_t = async::generic_timer_manager>; @@ -169,8 +190,8 @@ TEST_CASE("timeout_after can time out on custom channel", "[timeout_after]") { async::timeout_after(s, 100ms, 1.0f); static_assert( std::same_as, - async::completion_signatures>); + async::completion_signatures>); } TEST_CASE("timeout_after can complete with value after timeout", diff --git a/test/when_any.cpp b/test/when_any.cpp index 69d92af..fb329d8 100644 --- a/test/when_any.cpp +++ b/test/when_any.cpp @@ -186,8 +186,8 @@ TEST_CASE("first_successful policy", "[when_any]") { TEST_CASE("first_complete policy", "[when_any]") { int value{}; - auto s1 = async::just_stopped(); - auto s2 = async::just(17); + auto s1 = async::just(17); + auto s2 = async::just_stopped(); auto w = async::stop_when(s1, s2); auto op = async::connect(w, stopped_receiver{[&] { @@ -247,7 +247,7 @@ TEST_CASE("stop_when is pipeable", "[when_any]") { value = i; }}); async::start(op); - CHECK(value == 42); + CHECK(value == 17); } TEST_CASE("stop_when is adaptor-pipeable", "[when_any]") { @@ -258,7 +258,7 @@ TEST_CASE("stop_when is adaptor-pipeable", "[when_any]") { value = i; }}); async::start(op); - CHECK(value == 42); + CHECK(value == 17); } TEST_CASE("when_any with zero args never completes", "[when_any]") {