include/boost/corosio/delay.hpp

97.1% Lines (102/105) 97.5% List of functions (78/80) 51.5% Branches (135/262)
delay.hpp
f(x) Functions (80)
Function Calls Lines Branches Blocks
std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>> boost::corosio::detail::clamp_to_ns<double, std::__1::ratio<1l, 1l>>(std::__1::chrono::duration<double, std::__1::ratio<1l, 1l>>) :39 2x 100.0% 37.3% 23.0% std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>> boost::corosio::detail::clamp_to_ns<int, std::__1::ratio<1l, 1000l>>(std::__1::chrono::duration<int, std::__1::ratio<1l, 1000l>>) :39 2x 100.0% – 80.0% std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>> boost::corosio::detail::clamp_to_ns<long long, std::__1::ratio<1l, 1000000000l>>(std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>>) :39 327x 100.0% – 80.0% std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>> boost::corosio::detail::clamp_to_ns<long long, std::__1::ratio<1l, 1000000l>>(std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000l>>) :39 7121x 100.0% – 80.0% std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>> boost::corosio::detail::clamp_to_ns<long long, std::__1::ratio<1l, 1000l>>(std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000l>>) :39 6968x 80.0% 43.2% 80.0% std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>> boost::corosio::detail::clamp_to_ns<long long, std::__1::ratio<1l, 1l>>(std::__1::chrono::duration<long long, std::__1::ratio<1l, 1l>>) :39 1151x 80.0% 33.3% 80.0% std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>> boost::corosio::detail::clamp_to_ns<long long, std::__1::ratio<3600l, 1l>>(std::__1::chrono::duration<long long, std::__1::ratio<3600l, 1l>>) :39 2x 100.0% – 73.0% std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>> boost::corosio::detail::clamp_to_ns<long, std::__1::ratio<3600l, 1l>>(std::__1::chrono::duration<long, std::__1::ratio<3600l, 1l>>) :39 26x 0.0% 0.0% 86.0% boost::corosio::detail::emplace_delay_timer(std::__1::optional<boost::corosio::detail::timer>&, boost::capy::execution_context&) :60 10329x 87.5% 50.0% 56.0% boost::corosio::delay_awaitable::~delay_awaitable() :106 60666x 100.0% 50.0% 100.0% boost::corosio::delay_awaitable::delay_awaitable(std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>>) :121 28304x 100.0% – 100.0% boost::corosio::delay_awaitable::delay_awaitable(std::__1::chrono::time_point<std::__1::chrono::steady_clock, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>>>) :126 32x 100.0% – 100.0% boost::corosio::delay_awaitable::delay_awaitable(boost::corosio::delay_awaitable&&) :134 32379x 100.0% – 100.0% boost::corosio::delay_awaitable::await_ready() const :146 14166x 100.0% – 100.0% boost::corosio::delay_awaitable::await_suspend(std::__1::coroutine_handle<void>, boost::capy::io_env const*) :161 14153x 100.0% 90.0% 86.0% boost::corosio::delay_awaitable::await_resume() :187 14126x 100.0% 100.0% 100.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::delay_test<boost::corosio::kqueue_t{}>::testClockRearmStopRace()::race_traits>::~clock_delay_awaitable() :223 1997x 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() :223 1994x 100.0% – 100.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::hold_traits>::~clock_delay_awaitable() :223 32x 100.0% – 100.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::stepping_traits>::~clock_delay_awaitable() :223 24x 100.0% – 100.0% boost::corosio::clock_delay_awaitable<boost::corosio::wall_clock, boost::corosio::wait_traits<boost::corosio::wall_clock>>::~clock_delay_awaitable() :223 8x 100.0% – 100.0% boost::corosio::clock_delay_awaitable<std::__1::chrono::system_clock, boost::corosio::alloc_fault_test::testTimerRearmRecovery()::capped_traits>::~clock_delay_awaitable() :223 276x 100.0% – 100.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::delay_test<boost::corosio::kqueue_t{}>::testClockRearmStopRace()::race_traits>::next_wait(std::__1::chrono::time_point<boost::corosio::test_clock, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>>>) const :231 0 100.0% – 0.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::delay_test<boost::corosio::select_t{}>::testClockRearmStopRace()::race_traits>::next_wait(std::__1::chrono::time_point<boost::corosio::test_clock, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>>>) const :231 295x 100.0% 40.0% 75.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::hold_traits>::next_wait(std::__1::chrono::time_point<boost::corosio::test_clock, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>>>) const :231 4x 100.0% – 75.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::stepping_traits>::next_wait(std::__1::chrono::time_point<boost::corosio::test_clock, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>>>) const :231 16x 100.0% – 75.0% boost::corosio::clock_delay_awaitable<boost::corosio::wall_clock, boost::corosio::wait_traits<boost::corosio::wall_clock>>::next_wait(std::__1::chrono::time_point<boost::corosio::wall_clock, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>>>) const :231 2x 100.0% – 75.0% boost::corosio::clock_delay_awaitable<std::__1::chrono::system_clock, boost::corosio::alloc_fault_test::testTimerRearmRecovery()::capped_traits>::next_wait(std::__1::chrono::time_point<std::__1::chrono::system_clock, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000l>>>) const :231 1128x 100.0% – 75.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::delay_test<boost::corosio::kqueue_t{}>::testClockRearmStopRace()::race_traits>::on_fire(void*) :239 0 100.0% – 0.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::delay_test<boost::corosio::select_t{}>::testClockRearmStopRace()::race_traits>::on_fire(void*) :239 295x 83.3% 38.2% 75.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::hold_traits>::on_fire(void*) :239 2x 100.0% – 37.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::stepping_traits>::on_fire(void*) :239 16x 100.0% – 70.0% boost::corosio::clock_delay_awaitable<boost::corosio::wall_clock, boost::corosio::wait_traits<boost::corosio::wall_clock>>::on_fire(void*) :239 2x 100.0% – 50.0% boost::corosio::clock_delay_awaitable<std::__1::chrono::system_clock, boost::corosio::alloc_fault_test::testTimerRearmRecovery()::capped_traits>::on_fire(void*) :239 1128x 75.0% 66.7% 75.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::delay_test<boost::corosio::kqueue_t{}>::testClockRearmStopRace()::race_traits>::clock_delay_awaitable(std::__1::chrono::time_point<boost::corosio::test_clock, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>>>) :260 1000x 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::__1::chrono::time_point<boost::corosio::test_clock, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>>>) :260 1000x 100.0% – 100.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::hold_traits>::clock_delay_awaitable(std::__1::chrono::time_point<boost::corosio::test_clock, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>>>) :260 16x 100.0% – 100.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::stepping_traits>::clock_delay_awaitable(std::__1::chrono::time_point<boost::corosio::test_clock, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>>>) :260 12x 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::__1::chrono::time_point<boost::corosio::wall_clock, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>>>) :260 4x 100.0% – 100.0% boost::corosio::clock_delay_awaitable<std::__1::chrono::system_clock, boost::corosio::alloc_fault_test::testTimerRearmRecovery()::capped_traits>::clock_delay_awaitable(std::__1::chrono::time_point<std::__1::chrono::system_clock, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000l>>>) :260 138x 100.0% – 100.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::delay_test<boost::corosio::kqueue_t{}>::testClockRearmStopRace()::race_traits>::clock_delay_awaitable(boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::delay_test<boost::corosio::kqueue_t{}>::testClockRearmStopRace()::race_traits>&&) :267 1000x 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>&&) :267 997x 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 16x 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 12x 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 4x 100.0% – 100.0% boost::corosio::clock_delay_awaitable<std::__1::chrono::system_clock, boost::corosio::alloc_fault_test::testTimerRearmRecovery()::capped_traits>::clock_delay_awaitable(boost::corosio::clock_delay_awaitable<std::__1::chrono::system_clock, boost::corosio::alloc_fault_test::testTimerRearmRecovery()::capped_traits>&&) :267 138x 100.0% – 100.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::delay_test<boost::corosio::kqueue_t{}>::testClockRearmStopRace()::race_traits>::await_ready() const :282 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 :282 497x 100.0% – 100.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::hold_traits>::await_ready() const :282 8x 100.0% – 100.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::stepping_traits>::await_ready() const :282 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 :282 2x 100.0% – 100.0% boost::corosio::clock_delay_awaitable<std::__1::chrono::system_clock, boost::corosio::alloc_fault_test::testTimerRearmRecovery()::capped_traits>::await_ready() const :282 69x 100.0% – 100.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::delay_test<boost::corosio::kqueue_t{}>::testClockRearmStopRace()::race_traits>::await_suspend(std::__1::coroutine_handle<void>, boost::capy::io_env const*) :297 500x 100.0% – 50.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::delay_test<boost::corosio::select_t{}>::testClockRearmStopRace()::race_traits>::await_suspend(std::__1::coroutine_handle<void>, boost::capy::io_env const*) :297 498x 100.0% 60.0% 83.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::hold_traits>::await_suspend(std::__1::coroutine_handle<void>, boost::capy::io_env const*) :297 8x 100.0% – 83.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::stepping_traits>::await_suspend(std::__1::coroutine_handle<void>, boost::capy::io_env const*) :297 6x 100.0% – 83.0% boost::corosio::clock_delay_awaitable<boost::corosio::wall_clock, boost::corosio::wait_traits<boost::corosio::wall_clock>>::await_suspend(std::__1::coroutine_handle<void>, boost::capy::io_env const*) :297 2x 100.0% – 66.0% boost::corosio::clock_delay_awaitable<std::__1::chrono::system_clock, boost::corosio::alloc_fault_test::testTimerRearmRecovery()::capped_traits>::await_suspend(std::__1::coroutine_handle<void>, boost::capy::io_env const*) :297 69x 78.6% 50.0% 66.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::delay_test<boost::corosio::kqueue_t{}>::testClockRearmStopRace()::race_traits>::await_resume() :323 500x 100.0% – 50.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::delay_test<boost::corosio::select_t{}>::testClockRearmStopRace()::race_traits>::await_resume() :323 500x 100.0% 60.0% 83.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::hold_traits>::await_resume() :323 6x 100.0% – 83.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::stepping_traits>::await_resume() :323 6x 100.0% – 83.0% boost::corosio::clock_delay_awaitable<boost::corosio::wall_clock, boost::corosio::wait_traits<boost::corosio::wall_clock>>::await_resume() :323 2x 100.0% – 66.0% boost::corosio::clock_delay_awaitable<std::__1::chrono::system_clock, boost::corosio::alloc_fault_test::testTimerRearmRecovery()::capped_traits>::await_resume() :323 69x 71.4% 50.0% 66.0% boost::corosio::delay_awaitable boost::corosio::delay<double, std::__1::ratio<1l, 1l>>(std::__1::chrono::duration<double, std::__1::ratio<1l, 1l>>) :349 2x 100.0% – 100.0% boost::corosio::delay_awaitable boost::corosio::delay<int, std::__1::ratio<1l, 1000l>>(std::__1::chrono::duration<int, std::__1::ratio<1l, 1000l>>) :349 2x 100.0% – 100.0% boost::corosio::delay_awaitable boost::corosio::delay<long long, std::__1::ratio<1l, 1000000000l>>(std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>>) :349 10x 100.0% – 100.0% boost::corosio::delay_awaitable boost::corosio::delay<long long, std::__1::ratio<1l, 1000000l>>(std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000l>>) :349 5993x 100.0% – 100.0% boost::corosio::delay_awaitable boost::corosio::delay<long long, std::__1::ratio<1l, 1000l>>(std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000l>>) :349 6965x 100.0% – 100.0% boost::corosio::delay_awaitable boost::corosio::delay<long long, std::__1::ratio<1l, 1l>>(std::__1::chrono::duration<long long, std::__1::ratio<1l, 1l>>) :349 1151x 100.0% – 100.0% boost::corosio::delay_awaitable boost::corosio::delay<long long, std::__1::ratio<3600l, 1l>>(std::__1::chrono::duration<long long, std::__1::ratio<3600l, 1l>>) :349 2x 100.0% – 100.0% boost::corosio::delay_awaitable boost::corosio::delay<long, std::__1::ratio<3600l, 1l>>(std::__1::chrono::duration<long, std::__1::ratio<3600l, 1l>>) :349 26x 0.0% – 100.0% boost::corosio::delay(std::__1::chrono::time_point<std::__1::chrono::steady_clock, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>>>) :365 16x 100.0% – 100.0% auto boost::corosio::delay<boost::corosio::alloc_fault_test::testTimerRearmRecovery()::capped_traits, std::__1::chrono::system_clock, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000l>>>(std::__1::chrono::time_point<std::__1::chrono::system_clock, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000l>>>) :404 69x 100.0% 50.0% 66.0% auto boost::corosio::delay<boost::corosio::delay_test<boost::corosio::kqueue_t{}>::testClockRearmStopRace()::race_traits, boost::corosio::test_clock, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>>>(std::__1::chrono::time_point<boost::corosio::test_clock, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>>>) :404 500x 100.0% – 66.0% auto boost::corosio::delay<boost::corosio::delay_test<boost::corosio::select_t{}>::testClockRearmStopRace()::race_traits, boost::corosio::test_clock, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>>>(std::__1::chrono::time_point<boost::corosio::test_clock, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>>>) :404 500x 100.0% 50.0% 66.0% auto boost::corosio::delay<boost::corosio::hold_traits, boost::corosio::test_clock, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>>>(std::__1::chrono::time_point<boost::corosio::test_clock, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>>>) :404 8x 100.0% – 66.0% auto boost::corosio::delay<boost::corosio::stepping_traits, boost::corosio::test_clock, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>>>(std::__1::chrono::time_point<boost::corosio::test_clock, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>>>) :404 4x 100.0% – 66.0% auto boost::corosio::delay<boost::corosio::stepping_traits, boost::corosio::test_clock, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000l>>>(std::__1::chrono::time_point<boost::corosio::test_clock, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000l>>>) :404 2x 100.0% – 66.0% auto boost::corosio::delay<void, boost::corosio::wall_clock, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>>>(std::__1::chrono::time_point<boost::corosio::wall_clock, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>>>) :404 2x 100.0% – 66.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 15599x 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
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2x if (dur != dur)
48 2x return nanoseconds::zero();
49 }
50
29/58
✓ Branch 0 taken 10697 times.
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
✓ Branch 1 taken 1128 times.
✓ Branch 2 taken 10699 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✓ Branch 3 taken 2258 times.
✓ Branch 4 taken 10699 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 10699 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 1149 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 1149 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 1149 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 1149 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 327 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 327 times.
✗ Branch 19 not taken.
✓ Branch 20 taken 327 times.
✗ Branch 21 not taken.
✓ Branch 22 taken 327 times.
✗ Branch 23 not taken.
✓ Branch 24 taken 26 times.
✗ Branch 25 not taken.
✓ Branch 26 taken 26 times.
✗ Branch 27 not taken.
✓ Branch 28 taken 26 times.
✗ Branch 29 not taken.
✓ Branch 30 taken 24 times.
✓ Branch 31 taken 2 times.
✓ Branch 32 taken 2 times.
✗ Branch 33 not taken.
✓ Branch 34 taken 2 times.
✗ Branch 35 not taken.
✓ Branch 36 taken 2 times.
✗ Branch 37 not taken.
✓ Branch 38 taken 2 times.
✗ Branch 39 not taken.
✓ Branch 40 taken 2 times.
✗ Branch 41 not taken.
✓ Branch 42 taken 2 times.
✗ Branch 43 not taken.
✓ Branch 44 taken 2 times.
✗ Branch 45 not taken.
✓ Branch 46 taken 2 times.
✗ Branch 47 not taken.
✗ Branch 48 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 51 not taken.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
31194x return dsec(dur) >= dsec((nanoseconds::max)()) ? (nanoseconds::max)()
51
29/58
✓ Branch 0 taken 10698 times.
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
✓ Branch 1 taken 1128 times.
✓ Branch 2 taken 10701 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
✓ Branch 3 taken 2258 times.
✓ Branch 4 taken 10700 times.
✓ Branch 5 taken 1 time.
✓ Branch 6 taken 10700 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 1149 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 1149 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 1149 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 1149 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 327 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 327 times.
✗ Branch 19 not taken.
✓ Branch 20 taken 327 times.
✗ Branch 21 not taken.
✓ Branch 22 taken 327 times.
✗ Branch 23 not taken.
✓ Branch 24 taken 24 times.
✗ Branch 25 not taken.
✓ Branch 26 taken 24 times.
✗ Branch 27 not taken.
✓ Branch 28 taken 24 times.
✗ Branch 29 not taken.
✓ Branch 30 taken 24 times.
✗ Branch 31 not taken.
✓ Branch 32 taken 2 times.
✗ Branch 33 not taken.
✓ Branch 34 taken 2 times.
✗ Branch 35 not taken.
✓ Branch 36 taken 2 times.
✗ Branch 37 not taken.
✓ Branch 38 taken 2 times.
✗ Branch 39 not taken.
✓ Branch 40 taken 2 times.
✗ Branch 41 not taken.
✓ Branch 42 taken 2 times.
✗ Branch 43 not taken.
✓ Branch 44 taken 2 times.
✗ Branch 45 not taken.
✗ Branch 46 not taken.
✓ Branch 47 taken 2 times.
✗ Branch 48 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 51 not taken.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
31178x : dsec(dur) <= dsec((nanoseconds::min)())
52 2x ? (nanoseconds::min)()
53
6/14
✓ Branch 0 taken 10701 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 1149 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 327 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 24 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 2 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
15588x : duration_cast<nanoseconds>(dur);
54 2x }
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 10329x emplace_delay_timer(std::optional<timer>& t, capy::execution_context& ctx)
61 {
62 try
63 {
64
2/2
✓ Branch 0 taken 10327 times.
✓ Branch 1 taken 2 times.
10329x t.emplace(ctx);
65 10331x }
66 catch (std::logic_error const&)
67 {
68
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2x throw_logic_error("delay requires an io_context-backed executor");
69 2x }
70 catch (std::exception const& e)
71 {
72 ✗ throw_logic_error(e.what());
73 2x }
74 10331x }
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
1/2
✓ Branch 0 taken 14152 times.
✗ Branch 1 not taken.
14152x std::chrono::steady_clock::time_point deadline_{};
113 16x std::chrono::nanoseconds dur_{};
114 14152x bool has_deadline_ = false;
115 14168x 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 28304x explicit delay_awaitable(std::chrono::nanoseconds dur) noexcept : dur_(dur)
122 14152x {
123 28304x }
124
125 /// Construct an awaitable that waits until `tp`.
126 48x explicit delay_awaitable(std::chrono::steady_clock::time_point tp) noexcept
127 16x : deadline_(tp)
128 16x , has_deadline_(true)
129 16x {
130 32x }
131
132 // Only moved before await_suspend; wait_ is engaged after.
133 /// Construct by transferring state from `other`.
134 32379x 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 14166x bool await_ready() const noexcept
147 {
148 14166x 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 14153x await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
162 {
163
2/2
✓ Branch 0 taken 3996 times.
✓ Branch 1 taken 10157 times.
14153x if (env->stop_token.stop_requested())
164 {
165 3996x canceled_ = true;
166 3996x return h;
167 }
168
169 // Elapsed deadlines complete synchronously, but only once a
170 // pending stop request has already been ruled out above.
171
4/4
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 10145 times.
✓ Branch 2 taken 9 times.
✓ Branch 3 taken 10148 times.
10157x if (has_deadline_ ? deadline_ <= std::chrono::steady_clock::now()
172 10145x : dur_.count() <= 0)
173 9x return h;
174
175 10148x detail::emplace_delay_timer(timer_, env->executor.context());
176
177
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 10136 times.
10148x if (has_deadline_)
178 12x timer_->expires_at(deadline_);
179 else
180 10136x timer_->expires_after(dur_);
181
182
1/2
✓ Branch 0 taken 10148 times.
✗ Branch 1 not taken.
10148x wait_.emplace(timer_->wait());
183 10148x return wait_->await_suspend(h, env);
184 14153x }
185
186 /// Return empty on expiry, `error::canceled` if stop won.
187 14126x [[nodiscard]] capy::io_result<> await_resume() noexcept
188 {
189
2/2
✓ Branch 0 taken 3993 times.
✓ Branch 1 taken 10133 times.
14126x if (canceled_)
190 3993x return {capy::error::canceled};
191
2/2
✓ Branch 0 taken 10124 times.
✓ Branch 1 taken 9 times.
10133x if (wait_)
192 10124x return wait_->await_resume();
193 9x return {};
194 14126x }
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 2169x bool canceled_ = false;
227 std::optional<detail::timer> timer_;
228 detail::waiter_node w_;
229
230 std::chrono::nanoseconds
231 1445x next_wait(typename Clock::time_point now) const noexcept
232 {
233
8/20
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 4 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 4 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✓ Branch 16 taken 295 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 295 times.
✗ Branch 19 not taken.
1445x 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 1443x static bool on_fire(void* ctx) noexcept
240 {
241 1443x auto* self = static_cast<clock_delay_awaitable*>(ctx);
242 // Canceled: resume and surface the error
243
5/10
✗ Branch 0 not taken.
✓ Branch 1 taken 1144 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 100 times.
✓ Branch 9 taken 195 times.
1443x if (self->w_.ec_)
244 102x return false;
245 1341x auto now = Clock::now();
246
7/14
✓ Branch 0 taken 85 times.
✓ Branch 1 taken 1059 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 12 times.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✓ Branch 13 taken 195 times.
1341x if (now >= self->deadline_)
247 75x return false;
248 // Re-publish and return without touching the node again:
249 // the wait may complete on another thread immediately after.
250
2/10
✓ Branch 0 taken 1071 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 195 times.
✗ Branch 9 not taken.
1266x if (self->timer_->rearm_wait(self->w_, self->next_wait(now)))
251 1266x 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 1443x }
257
258 public:
259 /// Construct an awaitable that waits until `tp` on `Clock`.
260 3255x explicit clock_delay_awaitable(typename Clock::time_point tp) noexcept
261 1085x : deadline_(tp)
262 1085x {
263 2170x }
264
265 // Only moved before await_suspend; w_ is quiescent until then.
266 /// Construct by transferring the deadline from `other`.
267 3251x clock_delay_awaitable(clock_delay_awaitable&& other) noexcept
268 1084x : deadline_(other.deadline_)
269 1083x {
270 2167x }
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 1082x bool await_ready() const noexcept
283 {
284 1082x 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 1083x await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
298 {
299
7/10
✗ Branch 0 not taken.
✓ Branch 1 taken 75 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 4 times.
✓ Branch 6 taken 500 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 398 times.
✓ Branch 9 taken 100 times.
1083x if (env->stop_token.stop_requested())
300 {
301 902x canceled_ = true;
302 902x return h;
303 }
304
305 181x auto now = Clock::now();
306
5/10
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 73 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 100 times.
181x if (now >= deadline_)
307 2x return h;
308
309 179x detail::emplace_delay_timer(timer_, env->executor.context());
310
311 179x timer_->expires_after(next_wait(now));
312
313 179x w_.bind(h, *env);
314 179x w_.on_fire_ = &on_fire;
315 179x 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 179x return timer_->publish_wait(w_);
320 1083x }
321
322 /// Return empty on deadline, `error::canceled` if stop won.
323 1083x [[nodiscard]] capy::io_result<> await_resume() noexcept
324 {
325
7/10
✗ Branch 0 not taken.
✓ Branch 1 taken 75 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 500 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 400 times.
✓ Branch 9 taken 100 times.
1083x if (canceled_)
326 904x return {capy::error::canceled};
327
5/10
✓ Branch 0 taken 73 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 100 times.
✗ Branch 9 not taken.
179x if (timer_)
328 177x return {w_.ec_};
329 2x return {};
330 1083x }
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 14151x delay(std::chrono::duration<Rep, Period> dur) noexcept
350 {
351 14151x 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 16x delay(std::chrono::steady_clock::time_point tp) noexcept
366 {
367 16x 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 1085x 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 1085x return clock_delay_awaitable<Clock, traits_type>(
411
6/12
✓ Branch 0 taken 73 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 8 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 500 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 500 times.
✗ Branch 11 not taken.
1085x std::chrono::ceil<typename Clock::duration>(tp));
412 }
413
414 } // namespace boost::corosio
415
416 #endif
417