include/boost/corosio/delay.hpp

94.4% Lines (85/90) 74.6% List of functions (53/71) 82.2% Branches (37/45)
delay.hpp
f(x) Functions (71)
Function Calls Lines Branches Blocks
std::chrono::duration<long long, std::ratio<1ll, 1000000000ll> > boost::corosio::detail::clamp_to_ns<double, std::ratio<1ll, 1ll> >(std::chrono::duration<double, std::ratio<1ll, 1ll> >) :39 1x 42.9% 16.7% 21.1% std::chrono::duration<long long, std::ratio<1ll, 1000000000ll> > boost::corosio::detail::clamp_to_ns<int, std::ratio<1ll, 1000ll> >(std::chrono::duration<int, std::ratio<1ll, 1000ll> >) :39 1x 100.0% 50.0% 88.9% std::chrono::duration<long long, std::ratio<1ll, 1000000000ll> > boost::corosio::detail::clamp_to_ns<long long, std::ratio<1ll, 1000000000ll> >(std::chrono::duration<long long, std::ratio<1ll, 1000000000ll> >) :39 16x 100.0% 50.0% 88.9% std::chrono::duration<long long, std::ratio<1ll, 1000000000ll> > boost::corosio::detail::clamp_to_ns<long long, std::ratio<1ll, 1000000ll> >(std::chrono::duration<long long, std::ratio<1ll, 1000000ll> >) :39 1180x 100.0% 50.0% 88.9% std::chrono::duration<long long, std::ratio<1ll, 1000000000ll> > boost::corosio::detail::clamp_to_ns<long long, std::ratio<1ll, 1000ll> >(std::chrono::duration<long long, std::ratio<1ll, 1000ll> >) :39 2239x 100.0% 50.0% 88.9% std::chrono::duration<long long, std::ratio<1ll, 1000000000ll> > boost::corosio::detail::clamp_to_ns<long long, std::ratio<1ll, 1ll> >(std::chrono::duration<long long, std::ratio<1ll, 1ll> >) :39 1003x 100.0% 50.0% 88.9% std::chrono::duration<long long, std::ratio<1ll, 1000000000ll> > boost::corosio::detail::clamp_to_ns<long long, std::ratio<3600ll, 1ll> >(std::chrono::duration<long long, std::ratio<3600ll, 1ll> >) :39 14x 100.0% 100.0% 100.0% boost::corosio::detail::emplace_delay_timer(std::optional<boost::corosio::detail::timer>&, boost::capy::execution_context&) :60 2447x 66.7% 50.0% 61.5% boost::corosio::delay_awaitable::delay_awaitable(std::chrono::duration<long long, std::ratio<1ll, 1000000000ll> >) :121 4444x 100.0% – 100.0% boost::corosio::delay_awaitable::delay_awaitable(std::chrono::time_point<std::chrono::_V2::steady_clock, std::chrono::duration<long long, std::ratio<1ll, 1000000000ll> > >) :126 13x 100.0% – 100.0% boost::corosio::delay_awaitable::delay_awaitable(boost::corosio::delay_awaitable&&) :134 5471x 100.0% – 100.0% boost::corosio::delay_awaitable::await_ready() const :146 4456x 100.0% – 100.0% boost::corosio::delay_awaitable::await_suspend(std::__n4861::coroutine_handle<void>, boost::capy::io_env const*) :161 4457x 100.0% 100.0% 100.0% boost::corosio::delay_awaitable::await_resume() :187 4424x 100.0% 100.0% 100.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::delay_test<boost::corosio::iocp_t{}>::testClockRearmStopRace()::race_traits>::next_wait(std::chrono::time_point<boost::corosio::test_clock, std::chrono::duration<long long, std::ratio<1ll, 1000000000ll> > >) const :231 0 0.0% – 0.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::hold_traits>::next_wait(std::chrono::time_point<boost::corosio::test_clock, std::chrono::duration<long long, std::ratio<1ll, 1000000000ll> > >) const :231 2x 100.0% – 100.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::stepping_traits>::next_wait(std::chrono::time_point<boost::corosio::test_clock, std::chrono::duration<long long, std::ratio<1ll, 1000000000ll> > >) const :231 8x 100.0% – 100.0% boost::corosio::clock_delay_awaitable<boost::corosio::wall_clock, boost::corosio::wait_traits<boost::corosio::wall_clock> >::next_wait(std::chrono::time_point<boost::corosio::wall_clock, std::chrono::duration<long long, std::ratio<1ll, 1000000000ll> > >) const :231 1x 100.0% – 100.0% boost::corosio::clock_delay_awaitable<std::chrono::_V2::system_clock, (anonymous namespace)::capped_traits>::next_wait(std::chrono::time_point<std::chrono::_V2::system_clock, std::chrono::duration<long long, std::ratio<1ll, 1000000000ll> > >) const :231 0 0.0% – 0.0% boost::corosio::clock_delay_awaitable<std::chrono::_V2::system_clock, boost::corosio::wait_traits<std::chrono::_V2::system_clock> >::next_wait(std::chrono::time_point<std::chrono::_V2::system_clock, std::chrono::duration<long long, std::ratio<1ll, 1000000000ll> > >) const :231 0 0.0% – 0.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::delay_test<boost::corosio::iocp_t{}>::testClockRearmStopRace()::race_traits>::on_fire(void*) :239 0 0.0% 0.0% 0.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::hold_traits>::on_fire(void*) :239 1x 36.4% 16.7% 29.4% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::stepping_traits>::on_fire(void*) :239 8x 72.7% 66.7% 82.4% boost::corosio::clock_delay_awaitable<boost::corosio::wall_clock, boost::corosio::wait_traits<boost::corosio::wall_clock> >::on_fire(void*) :239 1x 54.5% 33.3% 52.9% boost::corosio::clock_delay_awaitable<std::chrono::_V2::system_clock, (anonymous namespace)::capped_traits>::on_fire(void*) :239 0 0.0% 0.0% 0.0% boost::corosio::clock_delay_awaitable<std::chrono::_V2::system_clock, boost::corosio::wait_traits<std::chrono::_V2::system_clock> >::on_fire(void*) :239 0 0.0% 0.0% 0.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::delay_test<boost::corosio::iocp_t{}>::testClockRearmStopRace()::race_traits>::clock_delay_awaitable(std::chrono::time_point<boost::corosio::test_clock, std::chrono::duration<long long, std::ratio<1ll, 1000000000ll> > >) :260 500x 100.0% – 100.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::hold_traits>::clock_delay_awaitable(std::chrono::time_point<boost::corosio::test_clock, std::chrono::duration<long long, std::ratio<1ll, 1000000000ll> > >) :260 4x 100.0% – 100.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::stepping_traits>::clock_delay_awaitable(std::chrono::time_point<boost::corosio::test_clock, std::chrono::duration<long long, std::ratio<1ll, 1000000000ll> > >) :260 3x 100.0% – 100.0% boost::corosio::clock_delay_awaitable<boost::corosio::wall_clock, boost::corosio::wait_traits<boost::corosio::wall_clock> >::clock_delay_awaitable(std::chrono::time_point<boost::corosio::wall_clock, std::chrono::duration<long long, std::ratio<1ll, 1000000000ll> > >) :260 1x 100.0% – 100.0% boost::corosio::clock_delay_awaitable<std::chrono::_V2::system_clock, (anonymous namespace)::capped_traits>::clock_delay_awaitable(std::chrono::time_point<std::chrono::_V2::system_clock, std::chrono::duration<long long, std::ratio<1ll, 1000000000ll> > >) :260 0 0.0% – 0.0% boost::corosio::clock_delay_awaitable<std::chrono::_V2::system_clock, boost::corosio::wait_traits<std::chrono::_V2::system_clock> >::clock_delay_awaitable(std::chrono::time_point<std::chrono::_V2::system_clock, std::chrono::duration<long long, std::ratio<1ll, 1000000000ll> > >) :260 0 0.0% – 0.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::delay_test<boost::corosio::iocp_t{}>::testClockRearmStopRace()::race_traits>::clock_delay_awaitable(boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::delay_test<boost::corosio::iocp_t{}>::testClockRearmStopRace()::race_traits>&&) :267 500x 100.0% – 100.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::hold_traits>::clock_delay_awaitable(boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::hold_traits>&&) :267 4x 100.0% – 100.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::stepping_traits>::clock_delay_awaitable(boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::stepping_traits>&&) :267 3x 100.0% – 100.0% boost::corosio::clock_delay_awaitable<boost::corosio::wall_clock, boost::corosio::wait_traits<boost::corosio::wall_clock> >::clock_delay_awaitable(boost::corosio::clock_delay_awaitable<boost::corosio::wall_clock, boost::corosio::wait_traits<boost::corosio::wall_clock> >&&) :267 1x 100.0% – 100.0% boost::corosio::clock_delay_awaitable<std::chrono::_V2::system_clock, (anonymous namespace)::capped_traits>::clock_delay_awaitable(boost::corosio::clock_delay_awaitable<std::chrono::_V2::system_clock, (anonymous namespace)::capped_traits>&&) :267 0 0.0% – 0.0% boost::corosio::clock_delay_awaitable<std::chrono::_V2::system_clock, boost::corosio::wait_traits<std::chrono::_V2::system_clock> >::clock_delay_awaitable(boost::corosio::clock_delay_awaitable<std::chrono::_V2::system_clock, boost::corosio::wait_traits<std::chrono::_V2::system_clock> >&&) :267 0 0.0% – 0.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::delay_test<boost::corosio::iocp_t{}>::testClockRearmStopRace()::race_traits>::await_ready() const :282 500x 100.0% – 100.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::hold_traits>::await_ready() const :282 4x 100.0% – 100.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::stepping_traits>::await_ready() const :282 3x 100.0% – 100.0% boost::corosio::clock_delay_awaitable<boost::corosio::wall_clock, boost::corosio::wait_traits<boost::corosio::wall_clock> >::await_ready() const :282 1x 100.0% – 100.0% boost::corosio::clock_delay_awaitable<std::chrono::_V2::system_clock, (anonymous namespace)::capped_traits>::await_ready() const :282 0 0.0% – 0.0% boost::corosio::clock_delay_awaitable<std::chrono::_V2::system_clock, boost::corosio::wait_traits<std::chrono::_V2::system_clock> >::await_ready() const :282 0 0.0% – 0.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::delay_test<boost::corosio::iocp_t{}>::testClockRearmStopRace()::race_traits>::await_suspend(std::__n4861::coroutine_handle<void>, boost::capy::io_env const*) :297 500x 30.8% 12.5% 30.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::hold_traits>::await_suspend(std::__n4861::coroutine_handle<void>, boost::capy::io_env const*) :297 4x 92.3% 87.5% 95.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::stepping_traits>::await_suspend(std::__n4861::coroutine_handle<void>, boost::capy::io_env const*) :297 3x 84.6% 87.5% 95.0% boost::corosio::clock_delay_awaitable<boost::corosio::wall_clock, boost::corosio::wait_traits<boost::corosio::wall_clock> >::await_suspend(std::__n4861::coroutine_handle<void>, boost::capy::io_env const*) :297 1x 76.9% 75.0% 90.0% boost::corosio::clock_delay_awaitable<std::chrono::_V2::system_clock, (anonymous namespace)::capped_traits>::await_suspend(std::__n4861::coroutine_handle<void>, boost::capy::io_env const*) :297 0 0.0% 0.0% 0.0% boost::corosio::clock_delay_awaitable<std::chrono::_V2::system_clock, boost::corosio::wait_traits<std::chrono::_V2::system_clock> >::await_suspend(std::__n4861::coroutine_handle<void>, boost::capy::io_env const*) :297 0 0.0% 0.0% 0.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::delay_test<boost::corosio::iocp_t{}>::testClockRearmStopRace()::race_traits>::await_resume() :323 500x 50.0% 25.0% 50.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::hold_traits>::await_resume() :323 3x 83.3% 75.0% 87.5% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::stepping_traits>::await_resume() :323 3x 83.3% 75.0% 75.0% boost::corosio::clock_delay_awaitable<boost::corosio::wall_clock, boost::corosio::wait_traits<boost::corosio::wall_clock> >::await_resume() :323 1x 66.7% 50.0% 62.5% boost::corosio::clock_delay_awaitable<std::chrono::_V2::system_clock, (anonymous namespace)::capped_traits>::await_resume() :323 0 0.0% 0.0% 0.0% boost::corosio::clock_delay_awaitable<std::chrono::_V2::system_clock, boost::corosio::wait_traits<std::chrono::_V2::system_clock> >::await_resume() :323 0 0.0% 0.0% 0.0% boost::corosio::delay_awaitable boost::corosio::delay<double, std::ratio<1ll, 1ll> >(std::chrono::duration<double, std::ratio<1ll, 1ll> >) :349 1x 100.0% – 100.0% boost::corosio::delay_awaitable boost::corosio::delay<int, std::ratio<1ll, 1000ll> >(std::chrono::duration<int, std::ratio<1ll, 1000ll> >) :349 1x 100.0% – 100.0% boost::corosio::delay_awaitable boost::corosio::delay<long long, std::ratio<1ll, 1000000000ll> >(std::chrono::duration<long long, std::ratio<1ll, 1000000000ll> >) :349 5x 100.0% – 100.0% boost::corosio::delay_awaitable boost::corosio::delay<long long, std::ratio<1ll, 1000000ll> >(std::chrono::duration<long long, std::ratio<1ll, 1000000ll> >) :349 1180x 100.0% – 100.0% boost::corosio::delay_awaitable boost::corosio::delay<long long, std::ratio<1ll, 1000ll> >(std::chrono::duration<long long, std::ratio<1ll, 1000ll> >) :349 2239x 100.0% – 100.0% boost::corosio::delay_awaitable boost::corosio::delay<long long, std::ratio<1ll, 1ll> >(std::chrono::duration<long long, std::ratio<1ll, 1ll> >) :349 1003x 100.0% – 100.0% boost::corosio::delay_awaitable boost::corosio::delay<long long, std::ratio<3600ll, 1ll> >(std::chrono::duration<long long, std::ratio<3600ll, 1ll> >) :349 14x 100.0% – 100.0% boost::corosio::delay(std::chrono::time_point<std::chrono::_V2::steady_clock, std::chrono::duration<long long, std::ratio<1ll, 1000000000ll> > >) :365 13x 100.0% – 100.0% auto boost::corosio::delay<(anonymous namespace)::capped_traits, std::chrono::_V2::system_clock, std::chrono::duration<long long, std::ratio<1ll, 1000000000ll> > >(std::chrono::time_point<std::chrono::_V2::system_clock, std::chrono::duration<long long, std::ratio<1ll, 1000000000ll> > >) :404 0 0.0% – 0.0% auto boost::corosio::delay<boost::corosio::delay_test<boost::corosio::iocp_t{}>::testClockRearmStopRace()::race_traits, boost::corosio::test_clock, std::chrono::duration<long long, std::ratio<1ll, 1000000000ll> > >(std::chrono::time_point<boost::corosio::test_clock, std::chrono::duration<long long, std::ratio<1ll, 1000000000ll> > >) :404 500x 100.0% – 100.0% auto boost::corosio::delay<boost::corosio::hold_traits, boost::corosio::test_clock, std::chrono::duration<long long, std::ratio<1ll, 1000000000ll> > >(std::chrono::time_point<boost::corosio::test_clock, std::chrono::duration<long long, std::ratio<1ll, 1000000000ll> > >) :404 4x 100.0% – 100.0% auto boost::corosio::delay<boost::corosio::stepping_traits, boost::corosio::test_clock, std::chrono::duration<long long, std::ratio<1ll, 1000000000ll> > >(std::chrono::time_point<boost::corosio::test_clock, std::chrono::duration<long long, std::ratio<1ll, 1000000000ll> > >) :404 2x 100.0% – 100.0% auto boost::corosio::delay<boost::corosio::stepping_traits, boost::corosio::test_clock, std::chrono::duration<long long, std::ratio<1ll, 1000ll> > >(std::chrono::time_point<boost::corosio::test_clock, std::chrono::duration<long long, std::ratio<1ll, 1000ll> > >) :404 1x 100.0% – 100.0% auto boost::corosio::delay<void, boost::corosio::wall_clock, std::chrono::duration<long long, std::ratio<1ll, 1000000000ll> > >(std::chrono::time_point<boost::corosio::wall_clock, std::chrono::duration<long long, std::ratio<1ll, 1000000000ll> > >) :404 1x 100.0% – 100.0% auto boost::corosio::delay<void, std::chrono::_V2::system_clock, std::chrono::duration<long long, std::ratio<1ll, 1000000000ll> > >(std::chrono::time_point<std::chrono::_V2::system_clock, std::chrono::duration<long long, std::ratio<1ll, 1000000000ll> > >) :404 0 0.0% – 0.0%
Line Branch TLA Hits Source Code
1 //
2 // Copyright (c) 2026 Steve Gerbino
3 // Copyright (c) 2026 Michael Vandeberg
4 //
5 // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 //
8 // Official repository: https://github.com/cppalliance/corosio
9 //
10
11 #ifndef BOOST_COROSIO_DELAY_HPP
12 #define BOOST_COROSIO_DELAY_HPP
13
14 #include <boost/corosio/detail/config.hpp>
15 #include <boost/corosio/detail/except.hpp>
16 #include <boost/corosio/detail/timer.hpp>
17 #include <boost/corosio/wait_traits.hpp>
18 #include <boost/capy/error.hpp>
19 #include <boost/capy/ex/io_env.hpp>
20 #include <boost/capy/io_result.hpp>
21
22 #include <chrono>
23 #include <concepts>
24 #include <coroutine>
25 #include <exception>
26 #include <optional>
27 #include <stdexcept>
28 #include <system_error>
29 #include <type_traits>
30
31 namespace boost::corosio {
32
33 namespace detail {
34
35 // Narrow reps wrap if nanoseconds::max() is converted into them;
36 // a double comparison clamps safely in both directions.
37 template<typename Rep, typename Period>
38 std::chrono::nanoseconds
39 4454x clamp_to_ns(std::chrono::duration<Rep, Period> dur) noexcept
40 {
41 using namespace std::chrono;
42 using dsec = duration<double>;
43 if constexpr (std::is_floating_point_v<Rep>)
44 {
45 // NaN fails both clamp comparisons and would reach the
46 // cast; treat it as no wait rather than undefined behavior.
47
1/2
✓ Branch 3 → 4 taken 1 time.
✗ Branch 3 → 5 not taken.
1x if (dur != dur)
48 1x return nanoseconds::zero();
49 }
50
2/4
✓ Branch 7 → 8 taken 1 time.
✓ Branch 7 → 9 taken 4452 times.
✗ Branch 9 → 10 not taken.
✗ Branch 9 → 11 not taken.
4453x return dsec(dur) >= dsec((nanoseconds::max)()) ? (nanoseconds::max)()
51 8905x : dsec(dur) <= dsec((nanoseconds::min)())
52
2/4
✓ Branch 14 → 15 taken 1 time.
✓ Branch 14 → 16 taken 4451 times.
✗ Branch 15 → 16 not taken.
✗ Branch 15 → 17 not taken.
4452x ? (nanoseconds::min)()
53 4453x : duration_cast<nanoseconds>(dur);
54 }
55
56 // A non-io_context executor cannot supply a timer service, and
57 // await_suspend is driven through a noexcept wrapper, so translate
58 // the service-lookup failure into a clear terminate.
59 inline void
60 2447x emplace_delay_timer(std::optional<timer>& t, capy::execution_context& ctx)
61 {
62 try
63 {
64
1/1
✓ Branch 2 → 3 taken 2446 times.
2447x t.emplace(ctx);
65 }
66
1/3
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 1 time.
✗ Branch 4 → 8 not taken.
1x catch (std::logic_error const&)
67 {
68 1x throw_logic_error("delay requires an io_context-backed executor");
69 1x }
70 ✗ catch (std::exception const& e)
71 {
72 ✗ throw_logic_error(e.what());
73 ✗ }
74 2446x }
75
76 } // namespace detail
77
78 /** Suspends the calling coroutine until the deadline elapses or
79 the environment's stop token is activated, whichever comes
80 first. A deadline already elapsed at suspension, or a stop
81 token already active, resumes the coroutine inline, without
82 starting a timer (see Cancellation below). Otherwise the
83 coroutine resumes through the executor once the timer fires
84 or a mid-wait cancellation arrives.
85
86 Not intended to be named directly. Use the @ref delay factory
87 overloads instead.
88
89 @pre The awaiting coroutine's executor must belong to an
90 `io_context`. Any other execution context terminates with a
91 diagnostic, because silently running without a timer would
92 drop the requested delay.
93
94 @par Cancellation
95 If stop is already requested before suspension, the coroutine
96 resumes immediately with `error::canceled`. If stop is
97 requested while suspended, the pending wait is cancelled and
98 the coroutine resumes with `error::canceled`. Requesting stop from
99 another thread while the `io_context` runs in `single_threaded` mode is
100 not permitted by `io_context`'s threading rules. That mode is
101 auto-enabled at `concurrency_hint` == 1. Cross-thread cancellation
102 requires a multi-threaded-capable context.
103
104 @see delay
105 */
106 class delay_awaitable
107 {
108 // wait() names timer's private awaitable type; decltype is
109 // the only way to store it here.
110 using wait_type = decltype(std::declval<detail::timer&>().wait());
111
112 std::chrono::steady_clock::time_point deadline_{};
113 std::chrono::nanoseconds dur_{};
114 bool has_deadline_ = false;
115 bool canceled_ = false;
116 std::optional<detail::timer> timer_;
117 std::optional<wait_type> wait_;
118
119 public:
120 /// Construct an awaitable that waits for `dur` nanoseconds.
121 4444x explicit delay_awaitable(std::chrono::nanoseconds dur) noexcept : dur_(dur)
122 {
123 4444x }
124
125 /// Construct an awaitable that waits until `tp`.
126 13x explicit delay_awaitable(std::chrono::steady_clock::time_point tp) noexcept
127 13x : deadline_(tp)
128 13x , has_deadline_(true)
129 {
130 13x }
131
132 // Only moved before await_suspend; wait_ is engaged after.
133 /// Construct by transferring state from `other`.
134 5471x delay_awaitable(delay_awaitable&&) = default;
135
136 /// Copy construction is disabled; an awaitable owns its timer.
137 delay_awaitable(delay_awaitable const&) = delete;
138 /// Copy assignment is disabled; an awaitable owns its timer.
139 delay_awaitable& operator=(delay_awaitable const&) = delete;
140 /// Move assignment is disabled; an awaitable is moved only before it is awaited.
141 delay_awaitable& operator=(delay_awaitable&&) = delete;
142
143 /// Return false unconditionally; see await_suspend.
144 // The elapsed-deadline fast path must run after the stop-token
145 // check, and only await_suspend receives the env carrying it.
146 4456x bool await_ready() const noexcept
147 {
148 4456x return false;
149 }
150
151 /** Resume inline if stopped or elapsed; else wait on a timer.
152
153 @param h Coroutine handle to resume on completion.
154 @param env The I/O environment, carrying the executor, stop token
155 and frame allocator.
156
157 @return The handle to resume immediately, or `noop_coroutine()` when
158 the wait was published to the timer service.
159 */
160 std::coroutine_handle<>
161 4457x await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
162 {
163
2/2
✓ Branch 3 → 4 taken 2011 times.
✓ Branch 3 → 5 taken 2446 times.
4457x if (env->stop_token.stop_requested())
164 {
165 2011x canceled_ = true;
166 2011x return h;
167 }
168
169 // Elapsed deadlines complete synchronously, but only once a
170 // pending stop request has already been ruled out above.
171
5/5
✓ Branch 5 → 6 taken 12 times.
✓ Branch 5 → 10 taken 2434 times.
✓ Branch 7 → 8 taken 12 times.
✓ Branch 12 → 13 taken 4 times.
✓ Branch 12 → 14 taken 2442 times.
4880x if (has_deadline_ ? deadline_ <= std::chrono::steady_clock::now()
172 2434x : dur_.count() <= 0)
173 4x return h;
174
175 2442x detail::emplace_delay_timer(timer_, env->executor.context());
176
177
2/2
✓ Branch 16 → 17 taken 11 times.
✓ Branch 16 → 19 taken 2430 times.
2441x if (has_deadline_)
178 11x timer_->expires_at(deadline_);
179 else
180 2430x timer_->expires_after(dur_);
181
182 2441x wait_.emplace(timer_->wait());
183 2441x return wait_->await_suspend(h, env);
184 }
185
186 /// Return empty on expiry, `error::canceled` if stop won.
187 4424x [[nodiscard]] capy::io_result<> await_resume() noexcept
188 {
189
2/2
✓ Branch 2 → 3 taken 2011 times.
✓ Branch 2 → 5 taken 2413 times.
4424x if (canceled_)
190 2011x return {capy::error::canceled};
191
2/2
✓ Branch 6 → 7 taken 2409 times.
✓ Branch 6 → 9 taken 4 times.
2413x if (wait_)
192 2409x return wait_->await_resume();
193 4x return {};
194 }
195 };
196
197 /** Suspends the calling coroutine until `Clock::now()` reaches the
198 deadline or the environment's stop token is activated. The wait is a
199 sequence of steady-clock timer waits. After each expiry the clock is
200 re-read. If the deadline is unreached, the same frame-embedded
201 waiter is re-published for the next `Traits::to_wait_duration` cap.
202 That re-publish neither resumes the coroutine nor allocates.
203
204 Not intended to be named directly. Use the @ref delay factory
205 overloads instead.
206
207 @tparam Clock The clock the deadline is expressed in.
208 @tparam Traits The wait-traits policy bounding each steady-clock wait.
209
210 @pre The awaiting coroutine's executor must belong to an
211 `io_context`. Any other execution context terminates with a
212 diagnostic, because silently running without a timer would
213 drop the requested delay.
214
215 @par Cancellation
216 Identical to @ref delay_awaitable: stop already requested
217 resumes inline with `error::canceled`; stop while suspended
218 cancels the pending wait, including between re-arms.
219
220 @see delay, wait_traits
221 */
222 template<class Clock, class Traits>
223 class clock_delay_awaitable
224 {
225 typename Clock::time_point deadline_{};
226 bool canceled_ = false;
227 std::optional<detail::timer> timer_;
228 detail::waiter_node w_;
229
230 std::chrono::nanoseconds
231 11x next_wait(typename Clock::time_point now) const noexcept
232 {
233 11x return detail::clamp_to_ns(Traits::to_wait_duration(deadline_ - now));
234 }
235
236 // Runs on the scheduler thread executing the completion op,
237 // before the continuation is posted, so the frame cannot die
238 // concurrently.
239 10x static bool on_fire(void* ctx) noexcept
240 {
241 10x auto* self = static_cast<clock_delay_awaitable*>(ctx);
242 // Canceled: resume and surface the error
243
2/2
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 5 taken 9 times.
10x if (self->w_.ec_)
244 1x return false;
245 9x auto now = Clock::now();
246
2/2
✓ Branch 8 → 9 taken 3 times.
✓ Branch 8 → 10 taken 6 times.
9x if (now >= self->deadline_)
247 3x return false;
248 // Re-publish and return without touching the node again:
249 // the wait may complete on another thread immediately after.
250
1/2
✓ Branch 13 → 14 taken 6 times.
✗ Branch 13 → 15 not taken.
6x if (self->timer_->rearm_wait(self->w_, self->next_wait(now)))
251 6x return true;
252 // Heap growth failed; finish the wait with an error rather
253 // than strand the frame with an unbalanced work count.
254 ✗ self->w_.ec_ = std::make_error_code(std::errc::not_enough_memory);
255 ✗ return false;
256 }
257
258 public:
259 /// Construct an awaitable that waits until `tp` on `Clock`.
260 508x explicit clock_delay_awaitable(typename Clock::time_point tp) noexcept
261 508x : deadline_(tp)
262 {
263 508x }
264
265 // Only moved before await_suspend; w_ is quiescent until then.
266 /// Construct by transferring the deadline from `other`.
267 508x clock_delay_awaitable(clock_delay_awaitable&& other) noexcept
268 508x : deadline_(other.deadline_)
269 {
270 508x }
271
272 /// Copy construction is disabled; an awaitable owns its timer.
273 clock_delay_awaitable(clock_delay_awaitable const&) = delete;
274 /// Copy assignment is disabled; an awaitable owns its timer.
275 clock_delay_awaitable& operator=(clock_delay_awaitable const&) = delete;
276 /// Move assignment is disabled; an awaitable is moved only before it is awaited.
277 clock_delay_awaitable& operator=(clock_delay_awaitable&&) = delete;
278
279 /// Return false unconditionally; see await_suspend.
280 // The elapsed-deadline fast path must run after the stop-token
281 // check, and only await_suspend receives the env carrying it.
282 508x bool await_ready() const noexcept
283 {
284 508x return false;
285 }
286
287 /** Resume inline if stopped or reached; else wait on a timer.
288
289 @param h Coroutine handle to resume on completion.
290 @param env The I/O environment, carrying the executor, stop token
291 and frame allocator.
292
293 @return The handle to resume immediately, or `noop_coroutine()` when
294 the wait was published to the timer service.
295 */
296 std::coroutine_handle<>
297 508x await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
298 {
299
2/2
✓ Branch 3 → 4 taken 502 times.
✓ Branch 3 → 5 taken 6 times.
508x if (env->stop_token.stop_requested())
300 {
301 502x canceled_ = true;
302 502x return h;
303 }
304
305 6x auto now = Clock::now();
306
3/3
✓ Branch 6 → 7 taken 6 times.
✓ Branch 8 → 9 taken 1 time.
✓ Branch 8 → 10 taken 5 times.
6x if (now >= deadline_)
307 1x return h;
308
309
1/1
✓ Branch 11 → 12 taken 5 times.
5x detail::emplace_delay_timer(timer_, env->executor.context());
310
311
1/1
✓ Branch 14 → 15 taken 5 times.
5x timer_->expires_after(next_wait(now));
312
313 5x w_.bind(h, *env);
314 5x w_.on_fire_ = &on_fire;
315 5x w_.on_fire_ctx_ = this;
316 // Never the elapsed fast path: a capped expiry that elapses
317 // before publication must still reach on_fire, not complete
318 // the clock wait early.
319
1/1
✓ Branch 17 → 18 taken 5 times.
5x return timer_->publish_wait(w_);
320 }
321
322 /// Return empty on deadline, `error::canceled` if stop won.
323 507x [[nodiscard]] capy::io_result<> await_resume() noexcept
324 {
325
2/2
✓ Branch 2 → 3 taken 502 times.
✓ Branch 2 → 5 taken 5 times.
507x if (canceled_)
326 502x return {capy::error::canceled};
327
2/2
✓ Branch 6 → 7 taken 4 times.
✓ Branch 6 → 8 taken 1 time.
5x if (timer_)
328 4x return {w_.ec_};
329 1x return {};
330 }
331 };
332
333 /** Suspend the current coroutine for a duration.
334
335 Returns an IoAwaitable that completes at or after the
336 specified duration, or earlier if the environment's stop
337 token is activated. Zero or negative durations complete
338 synchronously.
339
340 @par Example
341 @par !example duration
342
343 @param dur The duration to wait.
344
345 @return A @ref delay_awaitable yielding `io_result<>`.
346 */
347 template<typename Rep, typename Period>
348 [[nodiscard]] delay_awaitable
349 4443x delay(std::chrono::duration<Rep, Period> dur) noexcept
350 {
351 4443x return delay_awaitable(detail::clamp_to_ns(dur));
352 }
353
354 /** Suspend the current coroutine until a time point.
355
356 Returns an IoAwaitable that completes at or after `tp`, or
357 earlier if the environment's stop token is activated. Time
358 points already reached complete synchronously.
359
360 @param tp The steady-clock time point to wait until.
361
362 @return A @ref delay_awaitable yielding `io_result<>`.
363 */
364 [[nodiscard]] inline delay_awaitable
365 13x delay(std::chrono::steady_clock::time_point tp) noexcept
366 {
367 13x return delay_awaitable(tp);
368 }
369
370 /** Suspend the current coroutine until a time point on `Clock`.
371
372 Returns an IoAwaitable that completes at or after the first
373 observation of `Clock::now() >= tp`, or earlier if the
374 environment's stop token is activated. The wait is one or more
375 bounded steady-clock waits, re-reading `Clock::now()` after
376 each. `Traits::to_wait_duration` bounds each one. With the default
377 @ref wait_traits a single full-length wait is used. An adjustment of
378 `Clock` mid-wait is therefore observed only at natural wakeup.
379 Supply capping traits to bound that latency. Time
380 points already reached complete synchronously.
381
382 @note `Clock::now()` and `Traits::to_wait_duration` are invoked
383 on the `io_context`'s run thread and must not throw or block.
384
385 @par Example
386 @par !example system_clock_deadline
387
388 @tparam Traits The wait-traits policy; `void` selects
389 @ref wait_traits.
390
391 @tparam Clock The clock type. This overload does not participate
392 when `Clock` is `std::chrono::steady_clock`. The dedicated
393 @ref delay overload taking a `steady_clock::time_point` handles
394 that case.
395
396 @param tp The time point to wait until.
397
398 @return A @ref clock_delay_awaitable yielding `io_result<>`.
399 */
400 template<class Traits = void, class Clock, class Duration>
401 requires(!std::same_as<Clock, std::chrono::steady_clock>) &&
402 (std::is_void_v<Traits> || WaitTraits<Traits, Clock>)
403 [[nodiscard]] auto
404 508x delay(std::chrono::time_point<Clock, Duration> tp) noexcept
405 {
406 using traits_type =
407 std::conditional_t<std::is_void_v<Traits>, wait_traits<Clock>, Traits>;
408 // ceil preserves completes-at-or-after when Duration is coarser
409 // than the clock's native duration
410 return clock_delay_awaitable<Clock, traits_type>(
411 508x std::chrono::ceil<typename Clock::duration>(tp));
412 }
413
414 } // namespace boost::corosio
415
416 #endif
417