include/boost/corosio/delay.hpp

94.7% Lines (89/0/94) 79.5% List of functions (62/0/78)
delay.hpp
f(x) Functions (78)
Function Calls Lines Blocks
std::chrono::duration<long, std::ratio<1l, 1000000000l> > boost::corosio::detail::clamp_to_ns<double, std::ratio<1l, 1l> >(std::chrono::duration<double, std::ratio<1l, 1l> >) :39 2x 37.5% 21.0% std::chrono::duration<long, std::ratio<1l, 1000000000l> > boost::corosio::detail::clamp_to_ns<int, std::ratio<1l, 1000l> >(std::chrono::duration<int, std::ratio<1l, 1000l> >) :39 2x 100.0% 89.0% std::chrono::duration<long, std::ratio<1l, 1000000000l> > boost::corosio::detail::clamp_to_ns<long, std::ratio<1l, 1000000000l> >(std::chrono::duration<long, std::ratio<1l, 1000000000l> >) :39 4491x 100.0% 89.0% std::chrono::duration<long, std::ratio<1l, 1000000000l> > boost::corosio::detail::clamp_to_ns<long, std::ratio<1l, 1000000l> >(std::chrono::duration<long, std::ratio<1l, 1000000l> >) :39 6023x 100.0% 89.0% std::chrono::duration<long, std::ratio<1l, 1000000000l> > boost::corosio::detail::clamp_to_ns<long, std::ratio<1l, 1000l> >(std::chrono::duration<long, std::ratio<1l, 1000l> >) :39 12343x 100.0% 89.0% std::chrono::duration<long, std::ratio<1l, 1000000000l> > boost::corosio::detail::clamp_to_ns<long, std::ratio<1l, 1l> >(std::chrono::duration<long, std::ratio<1l, 1l> >) :39 49x 100.0% 89.0% std::chrono::duration<long, std::ratio<1l, 1000000000l> > boost::corosio::detail::clamp_to_ns<long, std::ratio<3600l, 1l> >(std::chrono::duration<long, std::ratio<3600l, 1l> >) :39 28x 100.0% 100.0% boost::corosio::detail::emplace_delay_timer(std::optional<boost::corosio::detail::timer>&, boost::capy::execution_context&) :61 14795x 66.7% 62.0% boost::corosio::delay_awaitable::delay_awaitable(std::chrono::duration<long, std::ratio<1l, 1000000000l> >) :128 18459x 100.0% 100.0% boost::corosio::delay_awaitable::delay_awaitable(std::chrono::time_point<std::chrono::_V2::steady_clock, std::chrono::duration<long, std::ratio<1l, 1000000000l> > >) :134 16x 100.0% 100.0% boost::corosio::delay_awaitable::delay_awaitable(boost::corosio::delay_awaitable&&) :143 20503x 100.0% 100.0% boost::corosio::delay_awaitable::await_ready() const :152 18473x 100.0% 100.0% boost::corosio::delay_awaitable::await_suspend(std::__n4861::coroutine_handle<void>, boost::capy::io_env const*) :159 18475x 100.0% 96.0% boost::corosio::delay_awaitable::await_resume() :186 18448x 100.0% 100.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::delay_test<boost::corosio::epoll_t{}>::testClockRearmStopRace()::race_traits>::next_wait(std::chrono::time_point<boost::corosio::test_clock, std::chrono::duration<long, std::ratio<1l, 1000000000l> > >) const :231 1x 100.0% 100.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::delay_test<boost::corosio::select_t{}>::testClockRearmStopRace()::race_traits>::next_wait(std::chrono::time_point<boost::corosio::test_clock, std::chrono::duration<long, std::ratio<1l, 1000000000l> > >) 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, std::ratio<1l, 1000000000l> > >) const :231 4x 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, std::ratio<1l, 1000000000l> > >) const :231 16x 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, std::ratio<1l, 1000000000l> > >) const :231 2x 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, std::ratio<1l, 1000000000l> > >) const :231 0 0.0% 0.0% boost::corosio::clock_delay_awaitable<std::chrono::_V2::system_clock, boost::corosio::alloc_fault_test::testTimerRearmRecovery()::capped_traits>::next_wait(std::chrono::time_point<std::chrono::_V2::system_clock, std::chrono::duration<long, std::ratio<1l, 1000000000l> > >) const :231 4458x 100.0% 100.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, std::ratio<1l, 1000000000l> > >) const :231 0 0.0% 0.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::delay_test<boost::corosio::epoll_t{}>::testClockRearmStopRace()::race_traits>::on_fire(void*) :240 1x 36.4% 29.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::delay_test<boost::corosio::select_t{}>::testClockRearmStopRace()::race_traits>::on_fire(void*) :240 0 0.0% 0.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::hold_traits>::on_fire(void*) :240 2x 36.4% 29.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::stepping_traits>::on_fire(void*) :240 16x 72.7% 82.0% boost::corosio::clock_delay_awaitable<boost::corosio::wall_clock, boost::corosio::wait_traits<boost::corosio::wall_clock> >::on_fire(void*) :240 2x 54.5% 53.0% boost::corosio::clock_delay_awaitable<std::chrono::_V2::system_clock, (anonymous namespace)::capped_traits>::on_fire(void*) :240 0 0.0% 0.0% boost::corosio::clock_delay_awaitable<std::chrono::_V2::system_clock, boost::corosio::alloc_fault_test::testTimerRearmRecovery()::capped_traits>::on_fire(void*) :240 4458x 72.7% 82.0% boost::corosio::clock_delay_awaitable<std::chrono::_V2::system_clock, boost::corosio::wait_traits<std::chrono::_V2::system_clock> >::on_fire(void*) :240 0 0.0% 0.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::delay_test<boost::corosio::epoll_t{}>::testClockRearmStopRace()::race_traits>::clock_delay_awaitable(std::chrono::time_point<boost::corosio::test_clock, std::chrono::duration<long, std::ratio<1l, 1000000000l> > >) :261 500x 100.0% 100.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::delay_test<boost::corosio::select_t{}>::testClockRearmStopRace()::race_traits>::clock_delay_awaitable(std::chrono::time_point<boost::corosio::test_clock, std::chrono::duration<long, std::ratio<1l, 1000000000l> > >) :261 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, std::ratio<1l, 1000000000l> > >) :261 8x 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, std::ratio<1l, 1000000000l> > >) :261 6x 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, std::ratio<1l, 1000000000l> > >) :261 2x 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, std::ratio<1l, 1000000000l> > >) :261 0 0.0% 0.0% boost::corosio::clock_delay_awaitable<std::chrono::_V2::system_clock, boost::corosio::alloc_fault_test::testTimerRearmRecovery()::capped_traits>::clock_delay_awaitable(std::chrono::time_point<std::chrono::_V2::system_clock, std::chrono::duration<long, std::ratio<1l, 1000000000l> > >) :261 237x 100.0% 100.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, std::ratio<1l, 1000000000l> > >) :261 0 0.0% 0.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::delay_test<boost::corosio::epoll_t{}>::testClockRearmStopRace()::race_traits>::clock_delay_awaitable(boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::delay_test<boost::corosio::epoll_t{}>::testClockRearmStopRace()::race_traits>&&) :269 500x 100.0% 100.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::delay_test<boost::corosio::select_t{}>::testClockRearmStopRace()::race_traits>::clock_delay_awaitable(boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::delay_test<boost::corosio::select_t{}>::testClockRearmStopRace()::race_traits>&&) :269 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>&&) :269 8x 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>&&) :269 6x 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> >&&) :269 2x 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>&&) :269 0 0.0% 0.0% boost::corosio::clock_delay_awaitable<std::chrono::_V2::system_clock, boost::corosio::alloc_fault_test::testTimerRearmRecovery()::capped_traits>::clock_delay_awaitable(boost::corosio::clock_delay_awaitable<std::chrono::_V2::system_clock, boost::corosio::alloc_fault_test::testTimerRearmRecovery()::capped_traits>&&) :269 237x 100.0% 100.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> >&&) :269 0 0.0% 0.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::delay_test<boost::corosio::epoll_t{}>::testClockRearmStopRace()::race_traits>::await_ready() const :283 500x 100.0% 100.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::delay_test<boost::corosio::select_t{}>::testClockRearmStopRace()::race_traits>::await_ready() const :283 500x 100.0% 100.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::hold_traits>::await_ready() const :283 8x 100.0% 100.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::stepping_traits>::await_ready() const :283 6x 100.0% 100.0% boost::corosio::clock_delay_awaitable<boost::corosio::wall_clock, boost::corosio::wait_traits<boost::corosio::wall_clock> >::await_ready() const :283 2x 100.0% 100.0% boost::corosio::clock_delay_awaitable<std::chrono::_V2::system_clock, (anonymous namespace)::capped_traits>::await_ready() const :283 0 0.0% 0.0% boost::corosio::clock_delay_awaitable<std::chrono::_V2::system_clock, boost::corosio::alloc_fault_test::testTimerRearmRecovery()::capped_traits>::await_ready() const :283 237x 100.0% 100.0% boost::corosio::clock_delay_awaitable<std::chrono::_V2::system_clock, boost::corosio::wait_traits<std::chrono::_V2::system_clock> >::await_ready() const :283 0 0.0% 0.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::delay_test<boost::corosio::epoll_t{}>::testClockRearmStopRace()::race_traits>::await_suspend(std::__n4861::coroutine_handle<void>, boost::capy::io_env const*) :290 500x 92.3% 95.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::delay_test<boost::corosio::select_t{}>::testClockRearmStopRace()::race_traits>::await_suspend(std::__n4861::coroutine_handle<void>, boost::capy::io_env const*) :290 500x 30.8% 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*) :290 8x 92.3% 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*) :290 6x 84.6% 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*) :290 2x 76.9% 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*) :290 0 0.0% 0.0% boost::corosio::clock_delay_awaitable<std::chrono::_V2::system_clock, boost::corosio::alloc_fault_test::testTimerRearmRecovery()::capped_traits>::await_suspend(std::__n4861::coroutine_handle<void>, boost::capy::io_env const*) :290 237x 76.9% 90.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*) :290 0 0.0% 0.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::delay_test<boost::corosio::epoll_t{}>::testClockRearmStopRace()::race_traits>::await_resume() :316 500x 83.3% 88.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::delay_test<boost::corosio::select_t{}>::testClockRearmStopRace()::race_traits>::await_resume() :316 500x 50.0% 50.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::hold_traits>::await_resume() :316 6x 83.3% 88.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::stepping_traits>::await_resume() :316 6x 83.3% 75.0% boost::corosio::clock_delay_awaitable<boost::corosio::wall_clock, boost::corosio::wait_traits<boost::corosio::wall_clock> >::await_resume() :316 2x 66.7% 62.0% boost::corosio::clock_delay_awaitable<std::chrono::_V2::system_clock, (anonymous namespace)::capped_traits>::await_resume() :316 0 0.0% 0.0% boost::corosio::clock_delay_awaitable<std::chrono::_V2::system_clock, boost::corosio::alloc_fault_test::testTimerRearmRecovery()::capped_traits>::await_resume() :316 237x 66.7% 62.0% boost::corosio::clock_delay_awaitable<std::chrono::_V2::system_clock, boost::corosio::wait_traits<std::chrono::_V2::system_clock> >::await_resume() :316 0 0.0% 0.0% boost::corosio::delay_awaitable boost::corosio::delay<double, std::ratio<1l, 1l> >(std::chrono::duration<double, std::ratio<1l, 1l> >) :342 2x 100.0% 100.0% boost::corosio::delay_awaitable boost::corosio::delay<int, std::ratio<1l, 1000l> >(std::chrono::duration<int, std::ratio<1l, 1000l> >) :342 2x 100.0% 100.0% boost::corosio::delay_awaitable boost::corosio::delay<long, std::ratio<1l, 1000000000l> >(std::chrono::duration<long, std::ratio<1l, 1000000000l> >) :342 10x 100.0% 100.0% boost::corosio::delay_awaitable boost::corosio::delay<long, std::ratio<1l, 1000l> >(std::chrono::duration<long, std::ratio<1l, 1000l> >) :342 9949x 100.0% 100.0% boost::corosio::delay_awaitable boost::corosio::delay<long, std::ratio<1l, 1l> >(std::chrono::duration<long, std::ratio<1l, 1l> >) :342 4x 100.0% 100.0% boost::corosio::delay_awaitable boost::corosio::delay<long, std::ratio<3600l, 1l> >(std::chrono::duration<long, std::ratio<3600l, 1l> >) :342 26x 100.0% 100.0% boost::corosio::delay(std::chrono::time_point<std::chrono::_V2::steady_clock, std::chrono::duration<long, std::ratio<1l, 1000000000l> > >) :358 16x 100.0% 100.0% auto boost::corosio::delay<boost::corosio::alloc_fault_test::testTimerRearmRecovery()::capped_traits, std::chrono::_V2::system_clock, std::chrono::duration<long, std::ratio<1l, 1000000000l> > >(std::chrono::time_point<std::chrono::_V2::system_clock, std::chrono::duration<long, std::ratio<1l, 1000000000l> > >) :392 237x 100.0% 100.0%
Line 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 22938x 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 2x if (dur != dur)
48 2x return nanoseconds::zero();
49 }
50 39325x return dsec(dur) >= dsec((nanoseconds::max)())
51 39325x ? (nanoseconds::max)()
52 45870x : dsec(dur) <= dsec((nanoseconds::min)())
53 22934x ? (nanoseconds::min)()
54 22936x : duration_cast<nanoseconds>(dur);
55 }
56
57 // A non-io_context executor cannot supply a timer service, and
58 // await_suspend is driven through a noexcept wrapper, so translate
59 // the service-lookup failure into a clear terminate.
60 inline void
61 14795x emplace_delay_timer(
62 std::optional<timer>& t, capy::execution_context& ctx)
63 {
64 try
65 {
66 14795x t.emplace(ctx);
67 }
68 2x catch(std::logic_error const&)
69 {
70 2x throw_logic_error(
71 "delay requires an io_context-backed executor");
72 2x }
73 catch(std::exception const& e)
74 {
75 throw_logic_error(e.what());
76 }
77 14793x }
78
79 } // namespace detail
80
81 /** IoAwaitable returned by @ref delay.
82
83 Suspends the calling coroutine until the deadline elapses or
84 the environment's stop token is activated, whichever comes
85 first. A deadline already elapsed at suspension, or a stop
86 token already active, resumes the coroutine inline, without
87 starting a timer (see Cancellation below). Otherwise the
88 coroutine resumes through the executor once the timer fires
89 or a mid-wait cancellation arrives.
90
91 Not intended to be named directly; use the @ref delay factory
92 overloads instead.
93
94 @par Preconditions
95 The awaiting coroutine's executor must belong to an
96 `io_context`. Any other execution context terminates with a
97 diagnostic, because silently running without a timer would
98 drop the requested delay.
99
100 @par Cancellation
101 If stop is already requested before suspension, the coroutine
102 resumes immediately with `error::canceled`. If stop is
103 requested while suspended, the pending wait is cancelled and
104 the coroutine resumes with `error::canceled`. Requesting stop
105 from another thread while the io_context runs in
106 single_threaded mode (auto-enabled at concurrency_hint == 1)
107 is not permitted by io_context's threading rules;
108 cross-thread cancellation requires a multi-threaded-capable
109 context.
110
111 @see delay
112 */
113 class delay_awaitable
114 {
115 // wait() names timer's private awaitable type; decltype is
116 // the only way to store it here.
117 using wait_type = decltype(std::declval<detail::timer&>().wait());
118
119 std::chrono::steady_clock::time_point deadline_{};
120 std::chrono::nanoseconds dur_{};
121 bool has_deadline_ = false;
122 bool canceled_ = false;
123 std::optional<detail::timer> timer_;
124 std::optional<wait_type> wait_;
125
126 public:
127 /// Construct an awaitable that waits for `dur` nanoseconds.
128 18459x explicit delay_awaitable(std::chrono::nanoseconds dur) noexcept
129 18459x : dur_(dur)
130 {
131 18459x }
132
133 /// Construct an awaitable that waits until `tp`.
134 16x explicit delay_awaitable(
135 std::chrono::steady_clock::time_point tp) noexcept
136 16x : deadline_(tp)
137 16x , has_deadline_(true)
138 {
139 16x }
140
141 /// Construct by transferring state from `other`.
142 // Only moved before await_suspend; wait_ is engaged after.
143 20503x delay_awaitable(delay_awaitable&&) = default;
144
145 delay_awaitable(delay_awaitable const&) = delete;
146 delay_awaitable& operator=(delay_awaitable const&) = delete;
147 delay_awaitable& operator=(delay_awaitable&&) = delete;
148
149 /// Return false unconditionally; see await_suspend.
150 // The elapsed-deadline fast path must run after the stop-token
151 // check, and only await_suspend receives the env carrying it.
152 18473x bool await_ready() const noexcept
153 {
154 18473x return false;
155 }
156
157 /// Resume inline if stopped or elapsed; else wait on a timer.
158 std::coroutine_handle<>
159 18475x await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
160 {
161 18475x if(env->stop_token.stop_requested())
162 {
163 3875x canceled_ = true;
164 3875x return h;
165 }
166
167 // Elapsed deadlines complete synchronously, but only once a
168 // pending stop request has already been ruled out above.
169 29186x if(has_deadline_ ?
170 14600x deadline_ <= std::chrono::steady_clock::now() :
171 14586x dur_.count() <= 0)
172 53x return h;
173
174 14547x detail::emplace_delay_timer(timer_, env->executor.context());
175
176 14545x if(has_deadline_)
177 12x timer_->expires_at(deadline_);
178 else
179 14533x timer_->expires_after(dur_);
180
181 14545x wait_.emplace(timer_->wait());
182 14545x return wait_->await_suspend(h, env);
183 }
184
185 /// Return empty on expiry, `error::canceled` if stop won.
186 18448x [[nodiscard]] capy::io_result<> await_resume() noexcept
187 {
188 18448x if(canceled_)
189 3875x return {capy::error::canceled};
190 14573x if(wait_)
191 14520x return wait_->await_resume();
192 53x return {};
193 }
194 };
195
196 /** IoAwaitable returned by the clock overloads of @ref delay.
197
198 Suspends the calling coroutine until `Clock::now()` reaches the
199 deadline or the environment's stop token is activated. The wait
200 is a sequence of steady-clock timer waits: after each expiry the
201 clock is re-read and, if the deadline is unreached, the same
202 frame-embedded waiter is re-published for the next
203 `Traits::to_wait_duration` cap — without resuming the coroutine
204 and without allocating.
205
206 Not intended to be named directly; use the @ref delay factory
207 overloads instead.
208
209 @par Preconditions
210 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 4481x next_wait(typename Clock::time_point now) const noexcept
232 {
233 4481x return detail::clamp_to_ns(
234 8962x Traits::to_wait_duration(deadline_ - now));
235 }
236
237 // Runs on the scheduler thread executing the completion op,
238 // before the continuation is posted, so the frame cannot die
239 // concurrently.
240 4479x static bool on_fire(void* ctx) noexcept
241 {
242 4479x auto* self = static_cast<clock_delay_awaitable*>(ctx);
243 // Canceled: resume and surface the error
244 4479x if(self->w_.ec_)
245 3x return false;
246 4476x auto now = Clock::now();
247 4476x if(now >= self->deadline_)
248 243x return false;
249 // Re-publish and return without touching the node again:
250 // the wait may complete on another thread immediately after.
251 4233x if(self->timer_->rearm_wait(self->w_, self->next_wait(now)))
252 4233x return true;
253 // Heap growth failed; finish the wait with an error rather
254 // than strand the frame with an unbalanced work count.
255 self->w_.ec_ = std::make_error_code(std::errc::not_enough_memory);
256 return false;
257 }
258
259 public:
260 /// Construct an awaitable that waits until `tp` on `Clock`.
261 1253x explicit clock_delay_awaitable(
262 typename Clock::time_point tp) noexcept
263 1253x : deadline_(tp)
264 {
265 1253x }
266
267 /// Construct by transferring the deadline from `other`.
268 // Only moved before await_suspend; w_ is quiescent until then.
269 1253x clock_delay_awaitable(clock_delay_awaitable&& other) noexcept
270 1253x : deadline_(other.deadline_)
271 {
272 1253x }
273
274 clock_delay_awaitable(clock_delay_awaitable const&) = delete;
275 clock_delay_awaitable&
276 operator=(clock_delay_awaitable const&) = delete;
277 clock_delay_awaitable&
278 operator=(clock_delay_awaitable&&) = delete;
279
280 /// Return false unconditionally; see await_suspend.
281 // The elapsed-deadline fast path must run after the stop-token
282 // check, and only await_suspend receives the env carrying it.
283 1253x bool await_ready() const noexcept
284 {
285 1253x return false;
286 }
287
288 /// Resume inline if stopped or reached; else wait on a timer.
289 std::coroutine_handle<>
290 1253x await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
291 {
292 1253x if(env->stop_token.stop_requested())
293 {
294 1003x canceled_ = true;
295 1003x return h;
296 }
297
298 250x auto now = Clock::now();
299 250x if(now >= deadline_)
300 2x return h;
301
302 248x detail::emplace_delay_timer(timer_, env->executor.context());
303
304 248x timer_->expires_after(next_wait(now));
305
306 248x w_.bind(h, *env);
307 248x w_.on_fire_ = &on_fire;
308 248x w_.on_fire_ctx_ = this;
309 // Never the elapsed fast path: a capped expiry that elapses
310 // before publication must still reach on_fire, not complete
311 // the clock wait early.
312 248x return timer_->publish_wait(w_);
313 }
314
315 /// Return empty on deadline, `error::canceled` if stop won.
316 1251x [[nodiscard]] capy::io_result<> await_resume() noexcept
317 {
318 1251x if(canceled_)
319 1003x return {capy::error::canceled};
320 248x if(timer_)
321 246x return {w_.ec_};
322 2x return {};
323 }
324 };
325
326 /** Suspend the current coroutine for a duration.
327
328 Returns an IoAwaitable that completes at or after the
329 specified duration, or earlier if the environment's stop
330 token is activated. Zero or negative durations complete
331 synchronously.
332
333 @par Example
334 @par !example duration
335
336 @param dur The duration to wait.
337
338 @return A @ref delay_awaitable yielding `io_result<>`.
339 */
340 template<typename Rep, typename Period>
341 [[nodiscard]] delay_awaitable
342 18457x delay(std::chrono::duration<Rep, Period> dur) noexcept
343 {
344 18457x return delay_awaitable(detail::clamp_to_ns(dur));
345 }
346
347 /** Suspend the current coroutine until a time point.
348
349 Returns an IoAwaitable that completes at or after `tp`, or
350 earlier if the environment's stop token is activated. Time
351 points already reached complete synchronously.
352
353 @param tp The steady-clock time point to wait until.
354
355 @return A @ref delay_awaitable yielding `io_result<>`.
356 */
357 [[nodiscard]] inline delay_awaitable
358 16x delay(std::chrono::steady_clock::time_point tp) noexcept
359 {
360 16x return delay_awaitable(tp);
361 }
362
363 /** Suspend the current coroutine until a time point on `Clock`.
364
365 Returns an IoAwaitable that completes at or after the first
366 observation of `Clock::now() >= tp`, or earlier if the
367 environment's stop token is activated. The wait is one or more
368 bounded steady-clock waits, re-reading `Clock::now()` after
369 each; `Traits::to_wait_duration` bounds each one. With the
370 default @ref wait_traits a single full-length wait is used, so
371 an adjustment of `Clock` mid-wait is observed only at natural
372 wakeup; supply capping traits to bound that latency. Time
373 points already reached complete synchronously.
374
375 @note `Clock::now()` and `Traits::to_wait_duration` are invoked
376 on the io_context's run thread and must not throw or block.
377
378 @par Example
379 @par !example system_clock_deadline
380
381 @tparam Traits The wait-traits policy; `void` selects
382 @ref wait_traits.
383
384 @param tp The time point to wait until.
385
386 @return A @ref clock_delay_awaitable yielding `io_result<>`.
387 */
388 template<class Traits = void, class Clock, class Duration>
389 requires (!std::same_as<Clock, std::chrono::steady_clock>) &&
390 (std::is_void_v<Traits> || WaitTraits<Traits, Clock>)
391 [[nodiscard]] auto
392 1253x delay(std::chrono::time_point<Clock, Duration> tp) noexcept
393 {
394 using traits_type = std::conditional_t<
395 std::is_void_v<Traits>, wait_traits<Clock>, Traits>;
396 // ceil preserves completes-at-or-after when Duration is coarser
397 // than the clock's native duration
398 return clock_delay_awaitable<Clock, traits_type>(
399 1253x std::chrono::ceil<typename Clock::duration>(tp));
400 }
401
402 } // namespace boost::corosio
403
404 #endif
405