include/boost/corosio/delay.hpp

97.1% Lines (102 / 105) 53.1% Branches (139 / 262) 97.5% Functions (78 / 80)
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% 39.7% 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 274x 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 6660x 80.0% 50.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 6930x 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 870x 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 3252x 0.0% 0.0% 86.0% boost::corosio::detail::emplace_delay_timer(std::__1::optional<boost::corosio::detail::timer>&, boost::capy::execution_context&) :60 13252x 87.5% 50.0% 56.0% boost::corosio::delay_awaitable::~delay_awaitable() :106 70783x 100.0% 50.0% 100.0% boost::corosio::delay_awaitable::delay_awaitable(std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>>) :121 33363x 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 37452x 100.0% – 100.0% boost::corosio::delay_awaitable::await_ready() const :146 16694x 100.0% – 100.0% boost::corosio::delay_awaitable::await_suspend(std::__1::coroutine_handle<void>, boost::capy::await_context const*) :161 16676x 100.0% 90.0% 86.0% boost::corosio::delay_awaitable::await_resume() :187 13441x 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 1952x 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 1987x 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 264x 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 240x 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 1030x 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 240x 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 1030x 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 132x 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 984x 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 990x 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 132x 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 499x 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 499x 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 66x 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::await_context const*) :297 495x 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::await_context const*) :297 496x 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::await_context 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::await_context 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::await_context 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::await_context const*) :297 66x 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 495x 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 499x 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 66x 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 5630x 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 6914x 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 870x 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 3252x 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 66x 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 494x 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 498x 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/await_context.hpp>
20 #include <boost/capy/results.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 17992x 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
32/58
✓ Branch 0 taken 10346 times.
✗ Branch 0 not taken.
✓ Branch 1 taken 40 times.
✓ Branch 1 taken 1030 times.
✓ Branch 2 taken 10354 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
✓ Branch 3 taken 2176 times.
✓ Branch 4 taken 10348 times.
✓ Branch 5 taken 6 times.
✓ Branch 6 taken 10348 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 3258 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 3258 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 3258 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 3256 times.
✓ Branch 15 taken 2 times.
✓ Branch 16 taken 862 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 862 times.
✗ Branch 19 not taken.
✓ Branch 20 taken 862 times.
✗ Branch 21 not taken.
✓ Branch 22 taken 862 times.
✗ Branch 23 not taken.
✓ Branch 24 taken 272 times.
✓ Branch 25 taken 2 times.
✓ Branch 26 taken 272 times.
✗ Branch 27 not taken.
✓ Branch 28 taken 271 times.
✓ Branch 29 taken 1 time.
✓ Branch 30 taken 271 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 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.
35985x return dsec(dur) >= dsec((nanoseconds::max)()) ? (nanoseconds::max)()
51
30/58
✓ Branch 0 taken 10368 times.
✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
✓ Branch 1 taken 1030 times.
✓ Branch 2 taken 10369 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 time.
✓ Branch 3 taken 2176 times.
✓ Branch 4 taken 10371 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 10371 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 3256 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 3256 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 3256 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 3256 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 862 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 862 times.
✗ Branch 19 not taken.
✓ Branch 20 taken 862 times.
✗ Branch 21 not taken.
✓ Branch 22 taken 862 times.
✗ Branch 23 not taken.
✓ Branch 24 taken 272 times.
✓ Branch 25 taken 1 time.
✓ Branch 26 taken 272 times.
✗ Branch 27 not taken.
✓ Branch 28 taken 272 times.
✗ Branch 29 not taken.
✓ Branch 30 taken 272 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.
35913x : dsec(dur) <= dsec((nanoseconds::min)())
52 2x ? (nanoseconds::min)()
53
6/14
✓ Branch 0 taken 10368 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 3256 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 862 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 272 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.
17969x : 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 13252x emplace_delay_timer(std::optional<timer>& t, capy::execution_context& ctx)
61 {
62 try
63 {
64
2/2
✓ Branch 0 taken 13250 times.
✓ Branch 1 taken 2 times.
13252x t.emplace(ctx);
65 13254x }
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
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ throw_logic_error(e.what());
73 2x }
74 13254x }
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 @ref 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 16681 times.
✗ Branch 1 not taken.
16681x std::chrono::steady_clock::time_point deadline_{};
113 16x std::chrono::nanoseconds dur_{};
114 16681x bool has_deadline_ = false;
115 16697x 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 33363x explicit delay_awaitable(std::chrono::nanoseconds dur) noexcept : dur_(dur)
122 16682x {
123 33363x }
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 37452x 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 16694x bool await_ready() const noexcept
147 {
148 16694x 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 16676x await_suspend(std::coroutine_handle<> h, capy::await_context const* env)
162 {
163
2/2
✓ Branch 0 taken 3453 times.
✓ Branch 1 taken 13223 times.
16676x if (env->stop_token.stop_requested())
164 {
165 3453x canceled_ = true;
166 3453x 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 13211 times.
✓ Branch 2 taken 189 times.
✓ Branch 3 taken 13034 times.
13223x if (has_deadline_ ? deadline_ <= std::chrono::steady_clock::now()
172 13211x : dur_.count() <= 0)
173 189x return h;
174
175 13034x detail::emplace_delay_timer(timer_, env->executor.context());
176
177
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 13022 times.
13034x if (has_deadline_)
178 12x timer_->expires_at(deadline_);
179 else
180 13022x timer_->expires_after(dur_);
181
182
1/2
✓ Branch 0 taken 13034 times.
✗ Branch 1 not taken.
13034x wait_.emplace(timer_->wait());
183 13034x return wait_->await_suspend(h, env);
184 16676x }
185
186 /// Return empty on expiry, `error::canceled` if stop won.
187 13441x [[nodiscard]] capy::results<> await_resume() noexcept
188 {
189
2/2
✓ Branch 0 taken 3458 times.
✓ Branch 1 taken 9983 times.
13441x if (canceled_)
190 3458x return {capy::error::canceled};
191
2/2
✓ Branch 0 taken 9794 times.
✓ Branch 1 taken 189 times.
9983x if (wait_)
192 9794x return wait_->await_resume();
193 189x return {};
194 13441x }
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 @ref delay, @ref wait_traits
221 */
222 template<class Clock, class Traits>
223 class clock_delay_awaitable
224 {
225 typename Clock::time_point deadline_{};
226 2149x bool canceled_ = false;
227 std::optional<detail::timer> timer_;
228 detail::waiter_node w_;
229
230 std::chrono::nanoseconds
231 1292x 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 240 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 240 times.
✗ Branch 19 not taken.
1292x 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 dispatched, so the frame cannot die
238 // concurrently.
239 1290x static bool on_fire(void* ctx) noexcept
240 {
241 1290x 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 1046 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 140 times.
✓ Branch 9 taken 100 times.
1290x if (self->w_.ec_)
244 142x return false;
245 1148x auto now = Clock::now();
246
7/14
✓ Branch 0 taken 82 times.
✓ Branch 1 taken 964 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 100 times.
1148x if (now >= self->deadline_)
247 72x 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 976 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 100 times.
✗ Branch 9 not taken.
1076x if (self->timer_->rearm_wait(self->w_, self->next_wait(now)))
251 1076x 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 1290x }
257
258 public:
259 /// Construct an awaitable that waits until `tp` on `Clock`.
260 3246x explicit clock_delay_awaitable(typename Clock::time_point tp) noexcept
261 1082x : deadline_(tp)
262 1082x {
263 2164x }
264
265 // Only moved before await_suspend; w_ is quiescent until then.
266 /// Construct by transferring the deadline from `other`.
267 3205x clock_delay_awaitable(clock_delay_awaitable&& other) noexcept
268 1067x : deadline_(other.deadline_)
269 1071x {
270 2138x }
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 1080x bool await_ready() const noexcept
283 {
284 1080x 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 1073x await_suspend(std::coroutine_handle<> h, capy::await_context const* env)
298 {
299
7/10
✗ Branch 0 not taken.
✓ Branch 1 taken 72 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 4 times.
✓ Branch 6 taken 495 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 356 times.
✓ Branch 9 taken 140 times.
1073x if (env->stop_token.stop_requested())
300 {
301 855x canceled_ = true;
302 855x return h;
303 }
304
305 218x auto now = Clock::now();
306
5/10
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 70 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 140 times.
218x if (now >= deadline_)
307 2x return h;
308
309 216x detail::emplace_delay_timer(timer_, env->executor.context());
310
311 216x timer_->expires_after(next_wait(now));
312
313 216x w_.bind(h, *env);
314 216x w_.on_fire_ = &on_fire;
315 216x 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 216x return timer_->publish_wait(w_);
320 1073x }
321
322 /// Return empty on deadline, `error::canceled` if stop won.
323 1074x [[nodiscard]] capy::results<> await_resume() noexcept
324 {
325
7/10
✗ Branch 0 not taken.
✓ Branch 1 taken 72 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 495 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 359 times.
✓ Branch 9 taken 140 times.
1074x if (canceled_)
326 858x return {capy::error::canceled};
327
5/10
✓ Branch 0 taken 70 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 140 times.
✗ Branch 9 not taken.
216x if (timer_)
328 214x return {w_.ec_};
329 2x return {};
330 1074x }
331 };
332
333 /** Suspend the current coroutine for a duration.
334
335 Returns a ContextAwaitable 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 `results<>`.
346 */
347 template<typename Rep, typename Period>
348 [[nodiscard]] delay_awaitable
349 16682x delay(std::chrono::duration<Rep, Period> dur) noexcept
350 {
351 16682x return delay_awaitable(detail::clamp_to_ns(dur));
352 }
353
354 /** Suspend the current coroutine until a time point.
355
356 Returns a ContextAwaitable 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 `results<>`.
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 a ContextAwaitable 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 `results<>`.
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 1074x 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 1074x return clock_delay_awaitable<Clock, traits_type>(
411
6/12
✓ Branch 0 taken 70 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 494 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 498 times.
✗ Branch 11 not taken.
1074x std::chrono::ceil<typename Clock::duration>(tp));
412 }
413
414 } // namespace boost::corosio
415
416 #endif
417