include/boost/corosio/detail/timeout_coro.hpp

97.1% Lines (33/34) 96.3% List of functions (26/27) 52.4% Branches (43/82)
f(x) Functions (27)
Function Calls Lines Branches Blocks
boost::corosio::detail::timeout_coro::promise_type::promise_type() :49 60x 100.0% 100.0% boost::corosio::detail::timeout_coro::promise_type::~promise_type() :49 60x 100.0% 100.0% boost::corosio::detail::timeout_coro::promise_type::set_env_owned(boost::capy::io_env) :59 30x 100.0% 100.0% boost::corosio::detail::timeout_coro::promise_type::get_return_object() :65 30x 100.0% 50.0% 66.0% boost::corosio::detail::timeout_coro::promise_type::initial_suspend() :71 30x 100.0% 100.0% boost::corosio::detail::timeout_coro::promise_type::final_suspend() :75 30x 100.0% 100.0% boost::corosio::detail::timeout_coro::promise_type::return_void() :79 30x 100.0% 100.0% boost::corosio::detail::timeout_coro::promise_type::unhandled_exception() :80 0 0.0% 0.0% boost::corosio::detail::timeout_coro::promise_type::transform_awaiter<boost::corosio::io_timer::wait_awaitable>::~transform_awaiter() :83 48x 100.0% 100.0% boost::corosio::detail::timeout_coro::promise_type::transform_awaiter<boost::corosio::native_timer<boost::corosio::kqueue_t{}>::native_wait_awaitable>::~transform_awaiter() :83 6x 100.0% 100.0% boost::corosio::detail::timeout_coro::promise_type::transform_awaiter<boost::corosio::native_timer<boost::corosio::select_t{}>::native_wait_awaitable>::~transform_awaiter() :83 6x 100.0% 100.0% boost::corosio::detail::timeout_coro::promise_type::transform_awaiter<boost::corosio::io_timer::wait_awaitable>::await_ready() :88 24x 100.0% 100.0% boost::corosio::detail::timeout_coro::promise_type::transform_awaiter<boost::corosio::native_timer<boost::corosio::kqueue_t{}>::native_wait_awaitable>::await_ready() :88 3x 100.0% 100.0% boost::corosio::detail::timeout_coro::promise_type::transform_awaiter<boost::corosio::native_timer<boost::corosio::select_t{}>::native_wait_awaitable>::await_ready() :88 3x 100.0% 100.0% boost::corosio::detail::timeout_coro::promise_type::transform_awaiter<boost::corosio::io_timer::wait_awaitable>::await_resume() :93 24x 100.0% 100.0% boost::corosio::detail::timeout_coro::promise_type::transform_awaiter<boost::corosio::native_timer<boost::corosio::kqueue_t{}>::native_wait_awaitable>::await_resume() :93 3x 100.0% 100.0% boost::corosio::detail::timeout_coro::promise_type::transform_awaiter<boost::corosio::native_timer<boost::corosio::select_t{}>::native_wait_awaitable>::await_resume() :93 3x 100.0% 100.0% auto boost::corosio::detail::timeout_coro::promise_type::transform_awaiter<boost::corosio::io_timer::wait_awaitable>::await_suspend<boost::corosio::detail::timeout_coro::promise_type>(std::__1::coroutine_handle<boost::corosio::detail::timeout_coro::promise_type>) :101 24x 100.0% 50.0% 66.0% auto boost::corosio::detail::timeout_coro::promise_type::transform_awaiter<boost::corosio::native_timer<boost::corosio::kqueue_t{}>::native_wait_awaitable>::await_suspend<boost::corosio::detail::timeout_coro::promise_type>(std::__1::coroutine_handle<boost::corosio::detail::timeout_coro::promise_type>) :101 3x 100.0% 66.0% auto boost::corosio::detail::timeout_coro::promise_type::transform_awaiter<boost::corosio::native_timer<boost::corosio::select_t{}>::native_wait_awaitable>::await_suspend<boost::corosio::detail::timeout_coro::promise_type>(std::__1::coroutine_handle<boost::corosio::detail::timeout_coro::promise_type>) :101 3x 100.0% 50.0% 66.0% auto boost::corosio::detail::timeout_coro::promise_type::transform_awaitable<boost::corosio::io_timer::wait_awaitable>(boost::corosio::io_timer::wait_awaitable&&) :116 24x 100.0% 100.0% auto boost::corosio::detail::timeout_coro::promise_type::transform_awaitable<boost::corosio::native_timer<boost::corosio::kqueue_t{}>::native_wait_awaitable>(boost::corosio::native_timer<boost::corosio::kqueue_t{}>::native_wait_awaitable&&) :116 3x 100.0% 100.0% auto boost::corosio::detail::timeout_coro::promise_type::transform_awaitable<boost::corosio::native_timer<boost::corosio::select_t{}>::native_wait_awaitable>(boost::corosio::native_timer<boost::corosio::select_t{}>::native_wait_awaitable&&) :116 3x 100.0% 100.0% boost::corosio::detail::timeout_coro::timeout_coro(std::__1::coroutine_handle<boost::corosio::detail::timeout_coro::promise_type>) :135 60x 100.0% 100.0% boost::corosio::detail::timeout_coro boost::corosio::detail::make_timeout<boost::corosio::native_timer<boost::corosio::kqueue_t{}>>(boost::corosio::native_timer<boost::corosio::kqueue_t{}>&, std::__1::stop_source) :171 3x 100.0% 58.0% boost::corosio::detail::timeout_coro boost::corosio::detail::make_timeout<boost::corosio::native_timer<boost::corosio::select_t{}>>(boost::corosio::native_timer<boost::corosio::select_t{}>&, std::__1::stop_source) :171 3x 100.0% 50.0% 58.0% boost::corosio::detail::timeout_coro boost::corosio::detail::make_timeout<boost::corosio::timer>(boost::corosio::timer&, std::__1::stop_source) :171 24x 100.0% 50.0% 60.0%
Line Branch TLA Hits Source Code
1 //
2 // Copyright (c) 2026 Steve Gerbino
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 // Official repository: https://github.com/cppalliance/corosio
8 //
9
10 #ifndef BOOST_COROSIO_DETAIL_TIMEOUT_CORO_HPP
11 #define BOOST_COROSIO_DETAIL_TIMEOUT_CORO_HPP
12
13 #include <boost/capy/concept/io_awaitable.hpp>
14 #include <boost/capy/ex/frame_allocator.hpp>
15 #include <boost/capy/ex/io_awaitable_promise_base.hpp>
16 #include <boost/capy/ex/io_env.hpp>
17
18 #include <coroutine>
19 #include <stop_token>
20 #include <type_traits>
21 #include <utility>
22
23 /* Self-destroying coroutine that awaits a timer and signals a
24 stop_source on expiry. Created suspended (initial_suspend =
25 suspend_always); the caller sets an owned io_env copy then
26 resumes, which runs synchronously until the timer wait suspends.
27 At final_suspend, suspend_never destroys the frame — the
28 timeout_coro destructor is intentionally a no-op since the
29 handle is dangling after self-destruction. If the coroutine is
30 still suspended at shutdown, the timer service drains it via
31 completion_op::destroy().
32
33 The promise reuses task<>'s transform_awaiter pattern (including
34 the MSVC symmetric-transfer workaround) to inject io_env into
35 IoAwaitable co_await expressions. */
36
37 namespace boost::corosio::detail {
38
39 /** Fire-and-forget coroutine for the timeout side of cancel_at.
40
41 The coroutine awaits a timer and signals a stop_source if the
42 timer fires without being cancelled. It self-destroys at
43 final_suspend via suspend_never.
44
45 @see make_timeout
46 */
47 struct timeout_coro
48 {
49 struct promise_type : capy::io_awaitable_promise_base<promise_type>
50 {
51 capy::io_env env_storage_;
52
53 /** Store an owned copy of the environment.
54
55 The timeout coroutine can outlive the cancel_at_awaitable
56 that created it, so it must own its env rather than
57 pointing to external storage.
58 */
59 30x void set_env_owned(capy::io_env env)
60 {
61 30x env_storage_ = std::move(env);
62 30x set_environment(&env_storage_);
63 30x }
64
65 30x timeout_coro get_return_object() noexcept
66 {
67 30x return timeout_coro{
68
1/2
✓ Branch 0 taken 30 times.
✗ Branch 1 not taken.
30x std::coroutine_handle<promise_type>::from_promise(*this)};
69 }
70
71 30x std::suspend_always initial_suspend() noexcept
72 {
73 30x return {};
74 }
75 30x std::suspend_never final_suspend() noexcept
76 {
77 30x return {};
78 }
79 30x void return_void() noexcept {}
80 void unhandled_exception() noexcept {}
81
82 template<class Awaitable>
83 struct transform_awaiter
84 {
85 std::decay_t<Awaitable> a_;
86 promise_type* p_;
87
88 30x bool await_ready() noexcept
89 {
90 30x return a_.await_ready();
91 }
92
93 30x decltype(auto) await_resume()
94 {
95 30x capy::set_current_frame_allocator(
96 30x p_->environment()->frame_allocator);
97 30x return a_.await_resume();
98 }
99
100 template<class Promise>
101 30x auto await_suspend(std::coroutine_handle<Promise> h) noexcept
102 {
103 #ifdef _MSC_VER
104 using R = decltype(a_.await_suspend(h, p_->environment()));
105 if constexpr (std::is_same_v<R, std::coroutine_handle<>>)
106 a_.await_suspend(h, p_->environment()).resume();
107 else
108 return a_.await_suspend(h, p_->environment());
109 #else
110
2/4
✓ Branch 0 taken 27 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
30x return a_.await_suspend(h, p_->environment());
111 #endif
112 }
113 };
114
115 template<class Awaitable>
116 30x auto transform_awaitable(Awaitable&& a)
117 {
118 using A = std::decay_t<Awaitable>;
119 if constexpr (capy::IoAwaitable<A>)
120 {
121 60x return transform_awaiter<Awaitable>{
122 30x std::forward<Awaitable>(a), this};
123 }
124 else
125 {
126 static_assert(sizeof(A) == 0, "requires IoAwaitable");
127 }
128 }
129 };
130
131 std::coroutine_handle<promise_type> h_;
132
133 timeout_coro() noexcept : h_(nullptr) {}
134
135 60x explicit timeout_coro(std::coroutine_handle<promise_type> h) noexcept
136 30x : h_(h)
137 30x {
138 60x }
139
140 // Self-destroying via suspend_never at final_suspend
141 ~timeout_coro() = default;
142
143 timeout_coro(timeout_coro const&) = delete;
144 timeout_coro& operator=(timeout_coro const&) = delete;
145
146 timeout_coro(timeout_coro&& o) noexcept : h_(o.h_)
147 {
148 o.h_ = nullptr;
149 }
150
151 timeout_coro& operator=(timeout_coro&& o) noexcept
152 {
153 h_ = o.h_;
154 o.h_ = nullptr;
155 return *this;
156 }
157 };
158
159 /** Create a fire-and-forget timeout coroutine.
160
161 Wait on the timer. If it fires without cancellation, signal
162 the stop source to cancel the paired inner operation.
163
164 @tparam Timer Timer type (`timer` or `native_timer<B>`).
165
166 @param t The timer to wait on (must have expiry set).
167 @param src Stop source to signal on timeout.
168 */
169 template<typename Timer>
170 timeout_coro
171
16/40
✓ Branch 0 taken 27 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 27 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 54 times.
✓ Branch 5 taken 81 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 27 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✓ Branch 15 taken 27 times.
✓ Branch 16 taken 51 times.
✓ Branch 17 taken 30 times.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✓ Branch 20 taken 3 times.
✗ Branch 21 not taken.
✓ Branch 22 taken 3 times.
✗ Branch 23 not taken.
✓ Branch 24 taken 6 times.
✓ Branch 25 taken 9 times.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✓ Branch 29 taken 3 times.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✓ Branch 35 taken 3 times.
✓ Branch 36 taken 3 times.
✓ Branch 37 taken 6 times.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
150x make_timeout(Timer& t, std::stop_source src)
172 30x {
173
20/32
✓ Branch 0 taken 81 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 33 times.
✓ Branch 3 taken 48 times.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 30 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
✓ Branch 8 taken 24 times.
✗ Branch 9 not taken.
✓ Branch 9 taken 24 times.
✓ Branch 10 taken 3 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 3 times.
✓ Branch 11 taken 24 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 3 times.
✓ Branch 14 taken 9 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 9 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 3 times.
✓ Branch 19 taken 6 times.
✗ Branch 20 not taken.
✓ Branch 21 taken 3 times.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✓ Branch 24 taken 3 times.
✓ Branch 25 taken 3 times.
✗ Branch 26 not taken.
✓ Branch 27 taken 3 times.
90x auto [ec] = co_await t.wait();
174
4/4
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 16 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 2 times.
30x if (!ec)
175 18x src.request_stop();
176 90x }
177
178 } // namespace boost::corosio::detail
179
180 #endif
181