100.00% Lines (8/8) 100.00% Functions (4/4)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2026 Steve Gerbino 2   // Copyright (c) 2026 Steve Gerbino
3   // 3   //
4   // Distributed under the Boost Software License, Version 1.0. (See accompanying 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) 5   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6   // 6   //
7   // Official repository: https://github.com/cppalliance/corosio 7   // Official repository: https://github.com/cppalliance/corosio
8   // 8   //
9   9  
10   #ifndef BOOST_COROSIO_CANCEL_HPP 10   #ifndef BOOST_COROSIO_CANCEL_HPP
11   #define BOOST_COROSIO_CANCEL_HPP 11   #define BOOST_COROSIO_CANCEL_HPP
12   12  
13   #include <boost/corosio/detail/cancel_at_awaitable.hpp> 13   #include <boost/corosio/detail/cancel_at_awaitable.hpp>
14   #include <boost/corosio/timer.hpp> 14   #include <boost/corosio/timer.hpp>
15   #include <boost/capy/concept/io_awaitable.hpp> 15   #include <boost/capy/concept/io_awaitable.hpp>
16   16  
17   #include <type_traits> 17   #include <type_traits>
18   #include <utility> 18   #include <utility>
19   19  
20   namespace boost::corosio { 20   namespace boost::corosio {
21   21  
22   /** Cancel an operation if it does not complete by a deadline. 22   /** Cancel an operation if it does not complete by a deadline.
23   23  
24   Races @p op against the given timer. If the deadline is reached 24   Races @p op against the given timer. If the deadline is reached
25   first, the inner operation is cancelled via its stop token and 25   first, the inner operation is cancelled via its stop token and
26   completes with an error comparing equal to `capy::cond::canceled`. 26   completes with an error comparing equal to `capy::cond::canceled`.
27   If the inner operation completes first, the timer is cancelled. 27   If the inner operation completes first, the timer is cancelled.
28   28  
29   Parent cancellation (from the caller's stop token) is forwarded 29   Parent cancellation (from the caller's stop token) is forwarded
30   to both the inner operation and the timeout timer. 30   to both the inner operation and the timeout timer.
31   31  
32   The timer's expiry is overwritten by this call. The timer must 32   The timer's expiry is overwritten by this call. The timer must
33   outlive the returned awaitable. Do not issue overlapping waits 33   outlive the returned awaitable. Do not issue overlapping waits
34   on the same timer. 34   on the same timer.
35   35  
36   @par Completion Conditions 36   @par Completion Conditions
37   The returned awaitable resumes when either: 37   The returned awaitable resumes when either:
38   @li The inner operation completes (successfully or with error). 38   @li The inner operation completes (successfully or with error).
39   @li The deadline expires and the inner operation is cancelled. 39   @li The deadline expires and the inner operation is cancelled.
40   @li The caller's stop token is triggered, cancelling both. 40   @li The caller's stop token is triggered, cancelling both.
41   41  
42   @par Error Conditions 42   @par Error Conditions
43   @li On timeout or parent cancellation, the inner operation 43   @li On timeout or parent cancellation, the inner operation
44   completes with an error equal to `capy::cond::canceled`. 44   completes with an error equal to `capy::cond::canceled`.
45   @li All other errors are propagated from the inner operation. 45   @li All other errors are propagated from the inner operation.
46   46  
47   @par Example 47   @par Example
48   @code 48   @code
49   timer timeout_timer( ioc ); 49   timer timeout_timer( ioc );
50   auto [ec, n] = co_await cancel_at( 50   auto [ec, n] = co_await cancel_at(
51   sock.read_some( buf ), timeout_timer, 51   sock.read_some( buf ), timeout_timer,
52   clock::now() + 5s ); 52   clock::now() + 5s );
53   if (ec == capy::cond::canceled) 53   if (ec == capy::cond::canceled)
54   // timed out or parent cancelled 54   // timed out or parent cancelled
55   @endcode 55   @endcode
56   56  
57   @param op The inner I/O awaitable to wrap. 57   @param op The inner I/O awaitable to wrap.
58   @param t The timer to use for the deadline. Must outlive 58   @param t The timer to use for the deadline. Must outlive
59   the returned awaitable. 59   the returned awaitable.
60   @param deadline The absolute time point at which to cancel. 60   @param deadline The absolute time point at which to cancel.
61   61  
62   @return An awaitable whose result matches @p op's result type. 62   @return An awaitable whose result matches @p op's result type.
63   63  
64   @see cancel_after 64   @see cancel_after
65   */ 65   */
66   auto 66   auto
HITCBC 67   18 cancel_at(capy::IoAwaitable auto&& op, timer& t, timer::time_point deadline) 67   18 cancel_at(capy::IoAwaitable auto&& op, timer& t, timer::time_point deadline)
68   { 68   {
69   return detail::cancel_at_awaitable<std::decay_t<decltype(op)>, timer>( 69   return detail::cancel_at_awaitable<std::decay_t<decltype(op)>, timer>(
HITCBC 70   18 std::forward<decltype(op)>(op), t, deadline); 70   18 std::forward<decltype(op)>(op), t, deadline);
71   } 71   }
72   72  
73   /** Cancel an operation if it does not complete within a duration. 73   /** Cancel an operation if it does not complete within a duration.
74   74  
75   Equivalent to `cancel_at( op, t, clock::now() + timeout )`. 75   Equivalent to `cancel_at( op, t, clock::now() + timeout )`.
76   76  
77   The timer's expiry is overwritten by this call. The timer must 77   The timer's expiry is overwritten by this call. The timer must
78   outlive the returned awaitable. Do not issue overlapping waits 78   outlive the returned awaitable. Do not issue overlapping waits
79   on the same timer. 79   on the same timer.
80   80  
81   @par Completion Conditions 81   @par Completion Conditions
82   The returned awaitable resumes when either: 82   The returned awaitable resumes when either:
83   @li The inner operation completes (successfully or with error). 83   @li The inner operation completes (successfully or with error).
84   @li The timeout elapses and the inner operation is cancelled. 84   @li The timeout elapses and the inner operation is cancelled.
85   @li The caller's stop token is triggered, cancelling both. 85   @li The caller's stop token is triggered, cancelling both.
86   86  
87   @par Error Conditions 87   @par Error Conditions
88   @li On timeout or parent cancellation, the inner operation 88   @li On timeout or parent cancellation, the inner operation
89   completes with an error equal to `capy::cond::canceled`. 89   completes with an error equal to `capy::cond::canceled`.
90   @li All other errors are propagated from the inner operation. 90   @li All other errors are propagated from the inner operation.
91   91  
92   @par Example 92   @par Example
93   @code 93   @code
94   timer timeout_timer( ioc ); 94   timer timeout_timer( ioc );
95   auto [ec, n] = co_await cancel_after( 95   auto [ec, n] = co_await cancel_after(
96   sock.read_some( buf ), timeout_timer, 5s ); 96   sock.read_some( buf ), timeout_timer, 5s );
97   if (ec == capy::cond::canceled) 97   if (ec == capy::cond::canceled)
98   // timed out 98   // timed out
99   @endcode 99   @endcode
100   100  
101   @param op The inner I/O awaitable to wrap. 101   @param op The inner I/O awaitable to wrap.
102   @param t The timer to use for the timeout. Must outlive 102   @param t The timer to use for the timeout. Must outlive
103   the returned awaitable. 103   the returned awaitable.
104   @param timeout The relative duration after which to cancel. 104   @param timeout The relative duration after which to cancel.
105   105  
106   @return An awaitable whose result matches @p op's result type. 106   @return An awaitable whose result matches @p op's result type.
107   107  
108   @see cancel_at 108   @see cancel_at
109   */ 109   */
110   auto 110   auto
HITCBC 111   14 cancel_after(capy::IoAwaitable auto&& op, timer& t, timer::duration timeout) 111   14 cancel_after(capy::IoAwaitable auto&& op, timer& t, timer::duration timeout)
112   { 112   {
113   return cancel_at( 113   return cancel_at(
HITCBC 114   14 std::forward<decltype(op)>(op), t, timer::clock_type::now() + timeout); 114   14 std::forward<decltype(op)>(op), t, timer::clock_type::now() + timeout);
115   } 115   }
116   116  
117   /** Cancel an operation if it does not complete by a deadline. 117   /** Cancel an operation if it does not complete by a deadline.
118   118  
119   Convenience overload that creates a @ref timer internally. 119   Convenience overload that creates a @ref timer internally.
120   Otherwise identical to the explicit-timer overload. 120   Otherwise identical to the explicit-timer overload.
121   121  
122   @par Completion Conditions 122   @par Completion Conditions
123   The returned awaitable resumes when either: 123   The returned awaitable resumes when either:
124   @li The inner operation completes (successfully or with error). 124   @li The inner operation completes (successfully or with error).
125   @li The deadline expires and the inner operation is cancelled. 125   @li The deadline expires and the inner operation is cancelled.
126   @li The caller's stop token is triggered, cancelling both. 126   @li The caller's stop token is triggered, cancelling both.
127   127  
128   @par Error Conditions 128   @par Error Conditions
129   @li On timeout or parent cancellation, the inner operation 129   @li On timeout or parent cancellation, the inner operation
130   completes with an error equal to `capy::cond::canceled`. 130   completes with an error equal to `capy::cond::canceled`.
131   @li All other errors are propagated from the inner operation. 131   @li All other errors are propagated from the inner operation.
132   132  
133   @note Creates a timer per call. Use the explicit-timer overload 133   @note Creates a timer per call. Use the explicit-timer overload
134   to amortize allocation across multiple timeouts. 134   to amortize allocation across multiple timeouts.
135   135  
136   @note The awaiting coroutine's executor must be backed by an 136   @note The awaiting coroutine's executor must be backed by an
137   io_context (the deadline timer is built from it). Awaiting this 137   io_context (the deadline timer is built from it). Awaiting this
138   on a non-io_context executor is a precondition violation and 138   on a non-io_context executor is a precondition violation and
139   aborts; use the explicit-timer overload to construct the timer 139   aborts; use the explicit-timer overload to construct the timer
140   yourself if you need a catchable error. 140   yourself if you need a catchable error.
141   141  
142   @par Example 142   @par Example
143   @code 143   @code
144   auto [ec, n] = co_await cancel_at( 144   auto [ec, n] = co_await cancel_at(
145   sock.read_some( buf ), 145   sock.read_some( buf ),
146   clock::now() + 5s ); 146   clock::now() + 5s );
147   if (ec == capy::cond::canceled) 147   if (ec == capy::cond::canceled)
148   // timed out or parent cancelled 148   // timed out or parent cancelled
149   @endcode 149   @endcode
150   150  
151   @param op The inner I/O awaitable to wrap. 151   @param op The inner I/O awaitable to wrap.
152   @param deadline The absolute time point at which to cancel. 152   @param deadline The absolute time point at which to cancel.
153   153  
154   @return An awaitable whose result matches @p op's result type. 154   @return An awaitable whose result matches @p op's result type.
155   155  
156   @see cancel_after 156   @see cancel_after
157   */ 157   */
158   auto 158   auto
HITCBC 159   6 cancel_at(capy::IoAwaitable auto&& op, timer::time_point deadline) 159   6 cancel_at(capy::IoAwaitable auto&& op, timer::time_point deadline)
160   { 160   {
161   return detail::cancel_at_awaitable<std::decay_t<decltype(op)>, timer, true>( 161   return detail::cancel_at_awaitable<std::decay_t<decltype(op)>, timer, true>(
HITCBC 162   6 std::forward<decltype(op)>(op), deadline); 162   6 std::forward<decltype(op)>(op), deadline);
163   } 163   }
164   164  
165   /** Cancel an operation if it does not complete within a duration. 165   /** Cancel an operation if it does not complete within a duration.
166   166  
167   Convenience overload that creates a @ref timer internally. 167   Convenience overload that creates a @ref timer internally.
168   Equivalent to `cancel_at( op, clock::now() + timeout )`. 168   Equivalent to `cancel_at( op, clock::now() + timeout )`.
169   169  
170   @par Completion Conditions 170   @par Completion Conditions
171   The returned awaitable resumes when either: 171   The returned awaitable resumes when either:
172   @li The inner operation completes (successfully or with error). 172   @li The inner operation completes (successfully or with error).
173   @li The timeout elapses and the inner operation is cancelled. 173   @li The timeout elapses and the inner operation is cancelled.
174   @li The caller's stop token is triggered, cancelling both. 174   @li The caller's stop token is triggered, cancelling both.
175   175  
176   @par Error Conditions 176   @par Error Conditions
177   @li On timeout or parent cancellation, the inner operation 177   @li On timeout or parent cancellation, the inner operation
178   completes with an error equal to `capy::cond::canceled`. 178   completes with an error equal to `capy::cond::canceled`.
179   @li All other errors are propagated from the inner operation. 179   @li All other errors are propagated from the inner operation.
180   180  
181   @note Creates a timer per call. Use the explicit-timer overload 181   @note Creates a timer per call. Use the explicit-timer overload
182   to amortize allocation across multiple timeouts. 182   to amortize allocation across multiple timeouts.
183   183  
184   @note The awaiting coroutine's executor must be backed by an 184   @note The awaiting coroutine's executor must be backed by an
185   io_context (the deadline timer is built from it). Awaiting this 185   io_context (the deadline timer is built from it). Awaiting this
186   on a non-io_context executor is a precondition violation and 186   on a non-io_context executor is a precondition violation and
187   aborts; use the explicit-timer overload to construct the timer 187   aborts; use the explicit-timer overload to construct the timer
188   yourself if you need a catchable error. 188   yourself if you need a catchable error.
189   189  
190   @par Example 190   @par Example
191   @code 191   @code
192   auto [ec, n] = co_await cancel_after( 192   auto [ec, n] = co_await cancel_after(
193   sock.read_some( buf ), 5s ); 193   sock.read_some( buf ), 5s );
194   if (ec == capy::cond::canceled) 194   if (ec == capy::cond::canceled)
195   // timed out 195   // timed out
196   @endcode 196   @endcode
197   197  
198   @param op The inner I/O awaitable to wrap. 198   @param op The inner I/O awaitable to wrap.
199   @param timeout The relative duration after which to cancel. 199   @param timeout The relative duration after which to cancel.
200   200  
201   @return An awaitable whose result matches @p op's result type. 201   @return An awaitable whose result matches @p op's result type.
202   202  
203   @see cancel_at 203   @see cancel_at
204   */ 204   */
205   auto 205   auto
HITCBC 206   4 cancel_after(capy::IoAwaitable auto&& op, timer::duration timeout) 206   4 cancel_after(capy::IoAwaitable auto&& op, timer::duration timeout)
207   { 207   {
208   return cancel_at( 208   return cancel_at(
HITCBC 209   4 std::forward<decltype(op)>(op), timer::clock_type::now() + timeout); 209   4 std::forward<decltype(op)>(op), timer::clock_type::now() + timeout);
210   } 210   }
211   211  
212   } // namespace boost::corosio 212   } // namespace boost::corosio
213   213  
214   #endif 214   #endif