include/boost/corosio/detail/cancel_at_awaitable.hpp

98.1% Lines (51/52) 90.6% List of functions (29/32) 57.1% Branches (16/28)
f(x) Functions (32)
Function Calls Lines Branches Blocks
boost::corosio::detail::cancel_at_awaitable<boost::corosio::io_timer::wait_awaitable, boost::corosio::native_timer<boost::corosio::kqueue_t{}>, true>::stop_forwarder::operator()() const :66 0 100.0% 0.0% boost::corosio::detail::cancel_at_awaitable<boost::corosio::io_timer::wait_awaitable, boost::corosio::native_timer<boost::corosio::select_t{}>, true>::stop_forwarder::operator()() const :66 0 25.0% 0.0% boost::corosio::detail::cancel_at_awaitable<boost::corosio::io_timer::wait_awaitable, boost::corosio::timer, false>::stop_forwarder::operator()() const :66 2x 100.0% 100.0% boost::corosio::detail::cancel_at_awaitable<boost::corosio::io_timer::wait_awaitable, boost::corosio::timer, true>::stop_forwarder::operator()() const :66 0 100.0% 0.0% boost::corosio::detail::cancel_at_awaitable<boost::corosio::io_timer::wait_awaitable, boost::corosio::timer, false>::cancel_at_awaitable(boost::corosio::io_timer::wait_awaitable&&, boost::corosio::timer&, std::__1::chrono::time_point<std::__1::chrono::steady_clock, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>>>) :86 36x 100.0% 50.0% 100.0% boost::corosio::detail::cancel_at_awaitable<boost::corosio::io_timer::wait_awaitable, boost::corosio::native_timer<boost::corosio::kqueue_t{}>, true>::cancel_at_awaitable(boost::corosio::io_timer::wait_awaitable&&, std::__1::chrono::time_point<std::__1::chrono::steady_clock, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>>>) :95 6x 100.0% 100.0% boost::corosio::detail::cancel_at_awaitable<boost::corosio::io_timer::wait_awaitable, boost::corosio::native_timer<boost::corosio::select_t{}>, true>::cancel_at_awaitable(boost::corosio::io_timer::wait_awaitable&&, std::__1::chrono::time_point<std::__1::chrono::steady_clock, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>>>) :95 6x 100.0% 50.0% 100.0% boost::corosio::detail::cancel_at_awaitable<boost::corosio::io_timer::wait_awaitable, boost::corosio::timer, true>::cancel_at_awaitable(boost::corosio::io_timer::wait_awaitable&&, std::__1::chrono::time_point<std::__1::chrono::steady_clock, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>>>) :95 12x 100.0% 50.0% 100.0% boost::corosio::detail::cancel_at_awaitable<boost::corosio::io_timer::wait_awaitable, boost::corosio::native_timer<boost::corosio::kqueue_t{}>, true>::~cancel_at_awaitable() :102 12x 100.0% 100.0% boost::corosio::detail::cancel_at_awaitable<boost::corosio::io_timer::wait_awaitable, boost::corosio::native_timer<boost::corosio::select_t{}>, true>::~cancel_at_awaitable() :102 12x 100.0% 100.0% boost::corosio::detail::cancel_at_awaitable<boost::corosio::io_timer::wait_awaitable, boost::corosio::timer, false>::~cancel_at_awaitable() :102 72x 100.0% 100.0% boost::corosio::detail::cancel_at_awaitable<boost::corosio::io_timer::wait_awaitable, boost::corosio::timer, true>::~cancel_at_awaitable() :102 24x 100.0% 100.0% boost::corosio::detail::cancel_at_awaitable<boost::corosio::io_timer::wait_awaitable, boost::corosio::native_timer<boost::corosio::kqueue_t{}>, true>::cancel_at_awaitable(boost::corosio::detail::cancel_at_awaitable<boost::corosio::io_timer::wait_awaitable, boost::corosio::native_timer<boost::corosio::kqueue_t{}>, true>&&) :108 6x 100.0% 100.0% boost::corosio::detail::cancel_at_awaitable<boost::corosio::io_timer::wait_awaitable, boost::corosio::native_timer<boost::corosio::select_t{}>, true>::cancel_at_awaitable(boost::corosio::detail::cancel_at_awaitable<boost::corosio::io_timer::wait_awaitable, boost::corosio::native_timer<boost::corosio::select_t{}>, true>&&) :108 6x 100.0% 100.0% boost::corosio::detail::cancel_at_awaitable<boost::corosio::io_timer::wait_awaitable, boost::corosio::timer, false>::cancel_at_awaitable(boost::corosio::detail::cancel_at_awaitable<boost::corosio::io_timer::wait_awaitable, boost::corosio::timer, false>&&) :108 36x 100.0% 100.0% boost::corosio::detail::cancel_at_awaitable<boost::corosio::io_timer::wait_awaitable, boost::corosio::timer, true>::cancel_at_awaitable(boost::corosio::detail::cancel_at_awaitable<boost::corosio::io_timer::wait_awaitable, boost::corosio::timer, true>&&) :108 12x 100.0% 100.0% boost::corosio::detail::cancel_at_awaitable<boost::corosio::io_timer::wait_awaitable, boost::corosio::native_timer<boost::corosio::kqueue_t{}>, true>::await_ready() const :121 3x 100.0% 100.0% boost::corosio::detail::cancel_at_awaitable<boost::corosio::io_timer::wait_awaitable, boost::corosio::native_timer<boost::corosio::select_t{}>, true>::await_ready() const :121 3x 100.0% 100.0% boost::corosio::detail::cancel_at_awaitable<boost::corosio::io_timer::wait_awaitable, boost::corosio::timer, false>::await_ready() const :121 18x 100.0% 100.0% boost::corosio::detail::cancel_at_awaitable<boost::corosio::io_timer::wait_awaitable, boost::corosio::timer, true>::await_ready() const :121 6x 100.0% 100.0% boost::corosio::detail::cancel_at_awaitable<boost::corosio::io_timer::wait_awaitable, boost::corosio::native_timer<boost::corosio::kqueue_t{}>, true>::await_suspend(std::__1::coroutine_handle<void>, boost::capy::io_env const*) :126 3x 100.0% 50.0% boost::corosio::detail::cancel_at_awaitable<boost::corosio::io_timer::wait_awaitable, boost::corosio::native_timer<boost::corosio::select_t{}>, true>::await_suspend(std::__1::coroutine_handle<void>, boost::capy::io_env const*) :126 3x 92.3% 50.0% 50.0% boost::corosio::detail::cancel_at_awaitable<boost::corosio::io_timer::wait_awaitable, boost::corosio::timer, false>::await_suspend(std::__1::coroutine_handle<void>, boost::capy::io_env const*) :126 18x 100.0% 50.0% boost::corosio::detail::cancel_at_awaitable<boost::corosio::io_timer::wait_awaitable, boost::corosio::timer, true>::await_suspend(std::__1::coroutine_handle<void>, boost::capy::io_env const*) :126 6x 92.3% 50.0% 50.0% boost::corosio::detail::cancel_at_awaitable<boost::corosio::io_timer::wait_awaitable, boost::corosio::native_timer<boost::corosio::kqueue_t{}>, true>::await_resume() :152 3x 100.0% 100.0% boost::corosio::detail::cancel_at_awaitable<boost::corosio::io_timer::wait_awaitable, boost::corosio::native_timer<boost::corosio::select_t{}>, true>::await_resume() :152 3x 100.0% 100.0% boost::corosio::detail::cancel_at_awaitable<boost::corosio::io_timer::wait_awaitable, boost::corosio::timer, false>::await_resume() :152 18x 100.0% 100.0% boost::corosio::detail::cancel_at_awaitable<boost::corosio::io_timer::wait_awaitable, boost::corosio::timer, true>::await_resume() :152 6x 100.0% 100.0% boost::corosio::detail::cancel_at_awaitable<boost::corosio::io_timer::wait_awaitable, boost::corosio::native_timer<boost::corosio::kqueue_t{}>, true>::destroy_parent_cb() :160 9x 100.0% 100.0% boost::corosio::detail::cancel_at_awaitable<boost::corosio::io_timer::wait_awaitable, boost::corosio::native_timer<boost::corosio::select_t{}>, true>::destroy_parent_cb() :160 9x 100.0% 100.0% 100.0% boost::corosio::detail::cancel_at_awaitable<boost::corosio::io_timer::wait_awaitable, boost::corosio::timer, false>::destroy_parent_cb() :160 54x 100.0% 100.0% boost::corosio::detail::cancel_at_awaitable<boost::corosio::io_timer::wait_awaitable, boost::corosio::timer, true>::destroy_parent_cb() :160 18x 100.0% 100.0% 100.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_CANCEL_AT_AWAITABLE_HPP
11 #define BOOST_COROSIO_DETAIL_CANCEL_AT_AWAITABLE_HPP
12
13 #include <boost/corosio/detail/timeout_coro.hpp>
14 #include <boost/capy/ex/io_env.hpp>
15
16 #include <chrono>
17 #include <coroutine>
18 #include <new>
19 #include <optional>
20 #include <stop_token>
21 #include <type_traits>
22 #include <utility>
23
24 /* Races an inner IoAwaitable against a timer via a shared
25 stop_source. await_suspend arms the timer by launching a
26 fire-and-forget timeout_coro, then starts the inner op with
27 an interposed stop_token. Whichever completes first signals
28 the stop_source, cancelling the other.
29
30 Parent cancellation is forwarded through a stop_callback
31 stored in a placement-new buffer (stop_callback is not
32 movable, but the awaitable must be movable for
33 transform_awaiter). The buffer is inert during moves
34 (before await_suspend) and constructed in-place once the
35 awaitable is pinned on the coroutine frame.
36
37 The timeout_coro can outlive this awaitable — it owns its
38 env and self-destroys via suspend_never. When Owning is
39 false the caller-supplied timer must outlive both; when
40 Owning is true the timer lives in std::optional and is
41 constructed lazily in await_suspend. */
42
43 namespace boost::corosio::detail {
44
45 /** Awaitable adapter that cancels an inner operation after a deadline.
46
47 Races the inner awaitable against a timer. A shared stop_source
48 ties them together: whichever completes first cancels the other.
49 Parent cancellation is forwarded via stop_callback.
50
51 When @p Owning is `false` (default), the caller supplies a timer
52 reference that must outlive the awaitable. When @p Owning is
53 `true`, the timer is constructed internally in `await_suspend`
54 from the execution context in `io_env`.
55
56 @tparam A The inner IoAwaitable type (decayed).
57 @tparam Timer The timer type (`timer` or `native_timer<B>`).
58 @tparam Owning When `true`, the awaitable owns its timer.
59 */
60 template<typename A, typename Timer, bool Owning = false>
61 struct cancel_at_awaitable
62 {
63 struct stop_forwarder
64 {
65 std::stop_source* src_;
66 2x void operator()() const noexcept
67 {
68 2x src_->request_stop();
69 2x }
70 };
71
72 using time_point = std::chrono::steady_clock::time_point;
73 using stop_cb_type = std::stop_callback<stop_forwarder>;
74 using timer_storage =
75 std::conditional_t<Owning, std::optional<Timer>, Timer*>;
76
77 A inner_;
78 timer_storage timer_;
79 time_point deadline_;
80 std::stop_source stop_src_;
81 capy::io_env inner_env_;
82 alignas(stop_cb_type) unsigned char cb_buf_[sizeof(stop_cb_type)];
83 60x bool cb_active_ = false;
84
85 /// Construct with a caller-supplied timer reference.
86
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
54x cancel_at_awaitable(A&& inner, Timer& timer, time_point deadline)
87 requires(!Owning)
88 18x : inner_(std::move(inner))
89 18x , timer_(&timer)
90 18x , deadline_(deadline)
91 18x {
92 36x }
93
94 /// Construct without a timer (created in `await_suspend`).
95
6/12
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 9 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 3 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 3 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 3 times.
✗ Branch 11 not taken.
48x cancel_at_awaitable(A&& inner, time_point deadline)
96 requires Owning
97 12x : inner_(std::move(inner))
98 12x , deadline_(deadline)
99 12x {
100 24x }
101
102 120x ~cancel_at_awaitable()
103 60x {
104 60x destroy_parent_cb();
105 120x }
106
107 // Only moved before await_suspend, when cb_active_ is false
108 90x cancel_at_awaitable(cancel_at_awaitable&& o) noexcept(
109 std::is_nothrow_move_constructible_v<A>)
110 30x : inner_(std::move(o.inner_))
111 30x , timer_(std::move(o.timer_))
112 30x , deadline_(o.deadline_)
113 30x , stop_src_(std::move(o.stop_src_))
114 30x {
115 60x }
116
117 cancel_at_awaitable(cancel_at_awaitable const&) = delete;
118 cancel_at_awaitable& operator=(cancel_at_awaitable const&) = delete;
119 cancel_at_awaitable& operator=(cancel_at_awaitable&&) = delete;
120
121 30x bool await_ready() const noexcept
122 {
123 30x return false;
124 }
125
126 30x auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
127 {
128 if constexpr (Owning)
129 12x timer_.emplace(env->executor.context());
130
131 30x timer_->expires_at(deadline_);
132
133 // Launch fire-and-forget timeout (starts suspended)
134
2/4
✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
30x auto timeout = make_timeout(*timer_, stop_src_);
135
2/4
✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 21 times.
✗ Branch 3 not taken.
60x timeout.h_.promise().set_env_owned(
136 30x {env->executor, stop_src_.get_token(), env->frame_allocator});
137 // Runs synchronously until timer.wait() suspends
138 30x timeout.h_.resume();
139 // timeout goes out of scope; destructor is a no-op,
140 // the coroutine self-destroys via suspend_never
141
142 // Forward parent cancellation
143 30x new (cb_buf_) stop_cb_type(env->stop_token, stop_forwarder{&stop_src_});
144 30x cb_active_ = true;
145
146 // Start the inner op with our interposed stop_token
147 120x inner_env_ = {
148 90x env->executor, stop_src_.get_token(), env->frame_allocator};
149 30x return inner_.await_suspend(h, &inner_env_);
150 }
151
152 30x decltype(auto) await_resume()
153 {
154 // Cancel whichever is still pending (idempotent)
155 30x stop_src_.request_stop();
156 30x destroy_parent_cb();
157 30x return inner_.await_resume();
158 }
159
160 90x void destroy_parent_cb() noexcept
161 {
162
4/4
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 21 times.
✓ Branch 2 taken 18 times.
✓ Branch 3 taken 9 times.
90x if (cb_active_)
163 {
164 30x std::launder(reinterpret_cast<stop_cb_type*>(cb_buf_))
165 30x ->~stop_cb_type();
166 30x cb_active_ = false;
167 30x }
168 90x }
169 };
170
171 } // namespace boost::corosio::detail
172
173 #endif
174