include/boost/corosio/delay.hpp

97.1% Lines (102/105) 97.5% List of functions (78/80) 50.8% Branches (133/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 33x 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 6129x 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 7416x 80.0% 45.5% 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 46x 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 9243x 87.5% 50.0% 56.0% boost::corosio::delay_awaitable::~delay_awaitable() :110 54075x 100.0% 50.0% 100.0% boost::corosio::delay_awaitable::delay_awaitable(std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>>) :125 24995x 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>>>) :130 32x 100.0% 100.0% boost::corosio::delay_awaitable::delay_awaitable(boost::corosio::delay_awaitable&&) :138 29089x 100.0% 100.0% boost::corosio::delay_awaitable::await_ready() const :147 12508x 100.0% 100.0% boost::corosio::delay_awaitable::await_suspend(std::__1::coroutine_handle<void>, boost::capy::io_env const*) :154 12506x 100.0% 90.0% 86.0% boost::corosio::delay_awaitable::await_resume() :180 12490x 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() :217 2000x 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() :217 1983x 100.0% 100.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::hold_traits>::~clock_delay_awaitable() :217 32x 100.0% 100.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::stepping_traits>::~clock_delay_awaitable() :217 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() :217 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() :217 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 :225 1x 100.0% 75.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 :225 0 100.0% 40.0% 0.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 :225 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 :225 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 :225 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 :225 1116x 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*) :233 1x 100.0% 37.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::delay_test<boost::corosio::select_t{}>::testClockRearmStopRace()::race_traits>::on_fire(void*) :233 0 83.3% 29.4% 0.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::hold_traits>::on_fire(void*) :233 2x 100.0% 37.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::stepping_traits>::on_fire(void*) :233 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*) :233 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*) :233 1116x 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>>>) :254 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>>>) :254 999x 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>>>) :254 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>>>) :254 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>>>) :254 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>>>) :254 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>&&) :261 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>&&) :261 998x 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>&&) :261 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>&&) :261 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>>&&) :261 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>&&) :261 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 :273 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 :273 500x 100.0% 100.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::hold_traits>::await_ready() const :273 8x 100.0% 100.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::stepping_traits>::await_ready() const :273 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 :273 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 :273 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*) :280 500x 100.0% 83.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*) :280 500x 100.0% 60.0% 50.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*) :280 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*) :280 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*) :280 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*) :280 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() :306 500x 100.0% 83.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::delay_test<boost::corosio::select_t{}>::testClockRearmStopRace()::race_traits>::await_resume() :306 500x 100.0% 60.0% 50.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::hold_traits>::await_resume() :306 6x 100.0% 83.0% boost::corosio::clock_delay_awaitable<boost::corosio::test_clock, boost::corosio::stepping_traits>::await_resume() :306 6x 100.0% 83.0% boost::corosio::clock_delay_awaitable<boost::corosio::wall_clock, boost::corosio::wait_traits<boost::corosio::wall_clock>>::await_resume() :306 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() :306 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>>) :332 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>>) :332 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>>) :332 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>>) :332 5013x 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>>) :332 7395x 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>>) :332 46x 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>>) :332 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>>) :332 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>>>) :348 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>>>) :382 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>>>) :382 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>>>) :382 499x 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>>>) :382 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>>>) :382 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>>>) :382 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>>>) :382 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 13656x 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
30/58
✓ Branch 0 taken 10146 times.
✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
✓ Branch 1 taken 1116 times.
✓ Branch 2 taken 10150 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
✓ Branch 3 taken 2257 times.
✓ Branch 4 taken 10156 times.
✓ Branch 5 taken 6 times.
✓ Branch 6 taken 10156 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 44 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 44 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 44 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 44 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 33 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 33 times.
✗ Branch 19 not taken.
✓ Branch 20 taken 33 times.
✗ Branch 21 not taken.
✓ Branch 22 taken 33 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.
27304x return dsec(dur) >= dsec((nanoseconds::max)()) ? (nanoseconds::max)()
51
29/58
✓ Branch 0 taken 10150 times.
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
✓ Branch 1 taken 1116 times.
✓ Branch 2 taken 10154 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
✓ Branch 3 taken 2257 times.
✓ Branch 4 taken 10150 times.
✓ Branch 5 taken 4 times.
✓ Branch 6 taken 10150 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 44 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 44 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 44 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 44 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 33 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 33 times.
✗ Branch 19 not taken.
✓ Branch 20 taken 33 times.
✗ Branch 21 not taken.
✓ Branch 22 taken 33 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.
27264x : dsec(dur) <= dsec((nanoseconds::min)())
52 2x ? (nanoseconds::min)()
53
6/14
✓ Branch 0 taken 10154 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 44 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 33 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.
13626x : 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 9243x emplace_delay_timer(std::optional<timer>& t, capy::execution_context& ctx)
61 {
62 try
63 {
64
2/2
✓ Branch 0 taken 9241 times.
✓ Branch 1 taken 2 times.
9243x t.emplace(ctx);
65 9245x }
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 9245x }
75
76 } // namespace detail
77
78 /** IoAwaitable returned by @ref delay.
79
80 Suspends the calling coroutine until the deadline elapses or
81 the environment's stop token is activated, whichever comes
82 first. A deadline already elapsed at suspension, or a stop
83 token already active, resumes the coroutine inline, without
84 starting a timer (see Cancellation below). Otherwise the
85 coroutine resumes through the executor once the timer fires
86 or a mid-wait cancellation arrives.
87
88 Not intended to be named directly; use the @ref delay factory
89 overloads instead.
90
91 @par Preconditions
92 The awaiting coroutine's executor must belong to an
93 `io_context`. Any other execution context terminates with a
94 diagnostic, because silently running without a timer would
95 drop the requested delay.
96
97 @par Cancellation
98 If stop is already requested before suspension, the coroutine
99 resumes immediately with `error::canceled`. If stop is
100 requested while suspended, the pending wait is cancelled and
101 the coroutine resumes with `error::canceled`. Requesting stop
102 from another thread while the io_context runs in
103 single_threaded mode (auto-enabled at concurrency_hint == 1)
104 is not permitted by io_context's threading rules;
105 cross-thread cancellation requires a multi-threaded-capable
106 context.
107
108 @see delay
109 */
110 class delay_awaitable
111 {
112 // wait() names timer's private awaitable type; decltype is
113 // the only way to store it here.
114 using wait_type = decltype(std::declval<detail::timer&>().wait());
115
116
1/2
✓ Branch 0 taken 12496 times.
✗ Branch 1 not taken.
12496x std::chrono::steady_clock::time_point deadline_{};
117 16x std::chrono::nanoseconds dur_{};
118 12496x bool has_deadline_ = false;
119 12512x bool canceled_ = false;
120 std::optional<detail::timer> timer_;
121 std::optional<wait_type> wait_;
122
123 public:
124 /// Construct an awaitable that waits for `dur` nanoseconds.
125 24995x explicit delay_awaitable(std::chrono::nanoseconds dur) noexcept : dur_(dur)
126 12499x {
127 24995x }
128
129 /// Construct an awaitable that waits until `tp`.
130 48x explicit delay_awaitable(std::chrono::steady_clock::time_point tp) noexcept
131 16x : deadline_(tp)
132 16x , has_deadline_(true)
133 16x {
134 32x }
135
136 /// Construct by transferring state from `other`.
137 // Only moved before await_suspend; wait_ is engaged after.
138 29089x delay_awaitable(delay_awaitable&&) = default;
139
140 delay_awaitable(delay_awaitable const&) = delete;
141 delay_awaitable& operator=(delay_awaitable const&) = delete;
142 delay_awaitable& operator=(delay_awaitable&&) = delete;
143
144 /// Return false unconditionally; see await_suspend.
145 // The elapsed-deadline fast path must run after the stop-token
146 // check, and only await_suspend receives the env carrying it.
147 12508x bool await_ready() const noexcept
148 {
149 12508x return false;
150 }
151
152 /// Resume inline if stopped or elapsed; else wait on a timer.
153 std::coroutine_handle<>
154 12506x await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
155 {
156
2/2
✓ Branch 0 taken 3001 times.
✓ Branch 1 taken 9505 times.
12506x if (env->stop_token.stop_requested())
157 {
158 3001x canceled_ = true;
159 3001x return h;
160 }
161
162 // Elapsed deadlines complete synchronously, but only once a
163 // pending stop request has already been ruled out above.
164
4/4
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 9493 times.
✓ Branch 2 taken 344 times.
✓ Branch 3 taken 9161 times.
9505x if (has_deadline_ ? deadline_ <= std::chrono::steady_clock::now()
165 9493x : dur_.count() <= 0)
166 344x return h;
167
168 9161x detail::emplace_delay_timer(timer_, env->executor.context());
169
170
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 9149 times.
9161x if (has_deadline_)
171 12x timer_->expires_at(deadline_);
172 else
173 9149x timer_->expires_after(dur_);
174
175
1/2
✓ Branch 0 taken 9161 times.
✗ Branch 1 not taken.
9161x wait_.emplace(timer_->wait());
176 9161x return wait_->await_suspend(h, env);
177 12506x }
178
179 /// Return empty on expiry, `error::canceled` if stop won.
180 12490x [[nodiscard]] capy::io_result<> await_resume() noexcept
181 {
182
2/2
✓ Branch 0 taken 3009 times.
✓ Branch 1 taken 9481 times.
12490x if (canceled_)
183 3009x return {capy::error::canceled};
184
2/2
✓ Branch 0 taken 9137 times.
✓ Branch 1 taken 344 times.
9481x if (wait_)
185 9137x return wait_->await_resume();
186 344x return {};
187 12490x }
188 };
189
190 /** IoAwaitable returned by the clock overloads of @ref delay.
191
192 Suspends the calling coroutine until `Clock::now()` reaches the
193 deadline or the environment's stop token is activated. The wait
194 is a sequence of steady-clock timer waits: after each expiry the
195 clock is re-read and, if the deadline is unreached, the same
196 frame-embedded waiter is re-published for the next
197 `Traits::to_wait_duration` cap — without resuming the coroutine
198 and without allocating.
199
200 Not intended to be named directly; use the @ref delay factory
201 overloads instead.
202
203 @par Preconditions
204 The awaiting coroutine's executor must belong to an
205 `io_context`. Any other execution context terminates with a
206 diagnostic, because silently running without a timer would
207 drop the requested delay.
208
209 @par Cancellation
210 Identical to @ref delay_awaitable: stop already requested
211 resumes inline with `error::canceled`; stop while suspended
212 cancels the pending wait, including between re-arms.
213
214 @see delay, wait_traits
215 */
216 template<class Clock, class Traits>
217 class clock_delay_awaitable
218 {
219 typename Clock::time_point deadline_{};
220 2168x bool canceled_ = false;
221 std::optional<detail::timer> timer_;
222 detail::waiter_node w_;
223
224 std::chrono::nanoseconds
225 1139x next_wait(typename Clock::time_point now) const noexcept
226 {
227
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 taken 1 time.
✗ Branch 13 not taken.
✓ Branch 14 taken 1 time.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
1139x return detail::clamp_to_ns(Traits::to_wait_duration(deadline_ - now));
228 }
229
230 // Runs on the scheduler thread executing the completion op,
231 // before the continuation is posted, so the frame cannot die
232 // concurrently.
233 1137x static bool on_fire(void* ctx) noexcept
234 {
235 1137x auto* self = static_cast<clock_delay_awaitable*>(ctx);
236 // Canceled: resume and surface the error
237
4/10
✗ Branch 0 not taken.
✓ Branch 1 taken 1132 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 time.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
1137x if (self->w_.ec_)
238 3x return false;
239 1134x auto now = Clock::now();
240
6/14
✓ Branch 0 taken 85 times.
✓ Branch 1 taken 1047 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 not taken.
1134x if (now >= self->deadline_)
241 75x return false;
242 // Re-publish and return without touching the node again:
243 // the wait may complete on another thread immediately after.
244
1/10
✓ Branch 0 taken 1059 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 not taken.
✗ Branch 9 not taken.
1059x if (self->timer_->rearm_wait(self->w_, self->next_wait(now)))
245 1059x return true;
246 // Heap growth failed; finish the wait with an error rather
247 // than strand the frame with an unbalanced work count.
248 self->w_.ec_ = std::make_error_code(std::errc::not_enough_memory);
249 return false;
250 1137x }
251
252 public:
253 /// Construct an awaitable that waits until `tp` on `Clock`.
254 3253x explicit clock_delay_awaitable(typename Clock::time_point tp) noexcept
255 1084x : deadline_(tp)
256 1085x {
257 2169x }
258
259 /// Construct by transferring the deadline from `other`.
260 // Only moved before await_suspend; w_ is quiescent until then.
261 3252x clock_delay_awaitable(clock_delay_awaitable&& other) noexcept
262 1084x : deadline_(other.deadline_)
263 1084x {
264 2168x }
265
266 clock_delay_awaitable(clock_delay_awaitable const&) = delete;
267 clock_delay_awaitable& operator=(clock_delay_awaitable const&) = delete;
268 clock_delay_awaitable& operator=(clock_delay_awaitable&&) = delete;
269
270 /// Return false unconditionally; see await_suspend.
271 // The elapsed-deadline fast path must run after the stop-token
272 // check, and only await_suspend receives the env carrying it.
273 1085x bool await_ready() const noexcept
274 {
275 1085x return false;
276 }
277
278 /// Resume inline if stopped or reached; else wait on a timer.
279 std::coroutine_handle<>
280 1085x await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
281 {
282
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 499 times.
✓ Branch 7 taken 1 time.
✓ Branch 8 taken 500 times.
✗ Branch 9 not taken.
1085x if (env->stop_token.stop_requested())
283 {
284 1003x canceled_ = true;
285 1003x return h;
286 }
287
288 82x auto now = Clock::now();
289
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 taken 1 time.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
82x if (now >= deadline_)
290 2x return h;
291
292 80x detail::emplace_delay_timer(timer_, env->executor.context());
293
294 80x timer_->expires_after(next_wait(now));
295
296 80x w_.bind(h, *env);
297 80x w_.on_fire_ = &on_fire;
298 80x w_.on_fire_ctx_ = this;
299 // Never the elapsed fast path: a capped expiry that elapses
300 // before publication must still reach on_fire, not complete
301 // the clock wait early.
302 80x return timer_->publish_wait(w_);
303 1085x }
304
305 /// Return empty on deadline, `error::canceled` if stop won.
306 1083x [[nodiscard]] capy::io_result<> await_resume() noexcept
307 {
308
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 499 times.
✓ Branch 7 taken 1 time.
✓ Branch 8 taken 500 times.
✗ Branch 9 not taken.
1083x if (canceled_)
309 1003x return {capy::error::canceled};
310
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 taken 1 time.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
80x if (timer_)
311 78x return {w_.ec_};
312 2x return {};
313 1083x }
314 };
315
316 /** Suspend the current coroutine for a duration.
317
318 Returns an IoAwaitable that completes at or after the
319 specified duration, or earlier if the environment's stop
320 token is activated. Zero or negative durations complete
321 synchronously.
322
323 @par Example
324 @par !example duration
325
326 @param dur The duration to wait.
327
328 @return A @ref delay_awaitable yielding `io_result<>`.
329 */
330 template<typename Rep, typename Period>
331 [[nodiscard]] delay_awaitable
332 12496x delay(std::chrono::duration<Rep, Period> dur) noexcept
333 {
334 12496x return delay_awaitable(detail::clamp_to_ns(dur));
335 }
336
337 /** Suspend the current coroutine until a time point.
338
339 Returns an IoAwaitable that completes at or after `tp`, or
340 earlier if the environment's stop token is activated. Time
341 points already reached complete synchronously.
342
343 @param tp The steady-clock time point to wait until.
344
345 @return A @ref delay_awaitable yielding `io_result<>`.
346 */
347 [[nodiscard]] inline delay_awaitable
348 16x delay(std::chrono::steady_clock::time_point tp) noexcept
349 {
350 16x return delay_awaitable(tp);
351 }
352
353 /** Suspend the current coroutine until a time point on `Clock`.
354
355 Returns an IoAwaitable that completes at or after the first
356 observation of `Clock::now() >= tp`, or earlier if the
357 environment's stop token is activated. The wait is one or more
358 bounded steady-clock waits, re-reading `Clock::now()` after
359 each; `Traits::to_wait_duration` bounds each one. With the
360 default @ref wait_traits a single full-length wait is used, so
361 an adjustment of `Clock` mid-wait is observed only at natural
362 wakeup; supply capping traits to bound that latency. Time
363 points already reached complete synchronously.
364
365 @note `Clock::now()` and `Traits::to_wait_duration` are invoked
366 on the io_context's run thread and must not throw or block.
367
368 @par Example
369 @par !example system_clock_deadline
370
371 @tparam Traits The wait-traits policy; `void` selects
372 @ref wait_traits.
373
374 @param tp The time point to wait until.
375
376 @return A @ref clock_delay_awaitable yielding `io_result<>`.
377 */
378 template<class Traits = void, class Clock, class Duration>
379 requires(!std::same_as<Clock, std::chrono::steady_clock>) &&
380 (std::is_void_v<Traits> || WaitTraits<Traits, Clock>)
381 [[nodiscard]] auto
382 1084x delay(std::chrono::time_point<Clock, Duration> tp) noexcept
383 {
384 using traits_type =
385 std::conditional_t<std::is_void_v<Traits>, wait_traits<Clock>, Traits>;
386 // ceil preserves completes-at-or-after when Duration is coarser
387 // than the clock's native duration
388 1084x return clock_delay_awaitable<Clock, traits_type>(
389
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 499 times.
✗ Branch 11 not taken.
1084x std::chrono::ceil<typename Clock::duration>(tp));
390 }
391
392 } // namespace boost::corosio
393
394 #endif
395