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
  137 + io_context (the deadline timer is built from it). Awaiting this
  138 + on a non-io_context executor is a precondition violation and
  139 + aborts; use the explicit-timer overload to construct the timer
  140 + yourself if you need a catchable error.
  141 +
136   @par Example 142   @par Example
137   @code 143   @code
138   auto [ec, n] = co_await cancel_at( 144   auto [ec, n] = co_await cancel_at(
139   sock.read_some( buf ), 145   sock.read_some( buf ),
140   clock::now() + 5s ); 146   clock::now() + 5s );
141   if (ec == capy::cond::canceled) 147   if (ec == capy::cond::canceled)
142   // timed out or parent cancelled 148   // timed out or parent cancelled
143   @endcode 149   @endcode
144   150  
145   @param op The inner I/O awaitable to wrap. 151   @param op The inner I/O awaitable to wrap.
146   @param deadline The absolute time point at which to cancel. 152   @param deadline The absolute time point at which to cancel.
147   153  
148   @return An awaitable whose result matches @p op's result type. 154   @return An awaitable whose result matches @p op's result type.
149   155  
150   @see cancel_after 156   @see cancel_after
151   */ 157   */
152   auto 158   auto
HITCBC 153   6 cancel_at(capy::IoAwaitable auto&& op, timer::time_point deadline) 159   6 cancel_at(capy::IoAwaitable auto&& op, timer::time_point deadline)
154   { 160   {
155   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 156   6 std::forward<decltype(op)>(op), deadline); 162   6 std::forward<decltype(op)>(op), deadline);
157   } 163   }
158   164  
159   /** Cancel an operation if it does not complete within a duration. 165   /** Cancel an operation if it does not complete within a duration.
160   166  
161   Convenience overload that creates a @ref timer internally. 167   Convenience overload that creates a @ref timer internally.
162   Equivalent to `cancel_at( op, clock::now() + timeout )`. 168   Equivalent to `cancel_at( op, clock::now() + timeout )`.
163   169  
164   @par Completion Conditions 170   @par Completion Conditions
165   The returned awaitable resumes when either: 171   The returned awaitable resumes when either:
166   @li The inner operation completes (successfully or with error). 172   @li The inner operation completes (successfully or with error).
167   @li The timeout elapses and the inner operation is cancelled. 173   @li The timeout elapses and the inner operation is cancelled.
168   @li The caller's stop token is triggered, cancelling both. 174   @li The caller's stop token is triggered, cancelling both.
169   175  
170   @par Error Conditions 176   @par Error Conditions
171   @li On timeout or parent cancellation, the inner operation 177   @li On timeout or parent cancellation, the inner operation
172   completes with an error equal to `capy::cond::canceled`. 178   completes with an error equal to `capy::cond::canceled`.
173   @li All other errors are propagated from the inner operation. 179   @li All other errors are propagated from the inner operation.
174   180  
175   @note Creates a timer per call. Use the explicit-timer overload 181   @note Creates a timer per call. Use the explicit-timer overload
176   to amortize allocation across multiple timeouts. 182   to amortize allocation across multiple timeouts.
  183 +
  184 + @note The awaiting coroutine's executor must be backed by an
  185 + io_context (the deadline timer is built from it). Awaiting this
  186 + on a non-io_context executor is a precondition violation and
  187 + aborts; use the explicit-timer overload to construct the timer
  188 + yourself if you need a catchable error.
177   189  
178   @par Example 190   @par Example
179   @code 191   @code
180   auto [ec, n] = co_await cancel_after( 192   auto [ec, n] = co_await cancel_after(
181   sock.read_some( buf ), 5s ); 193   sock.read_some( buf ), 5s );
182   if (ec == capy::cond::canceled) 194   if (ec == capy::cond::canceled)
183   // timed out 195   // timed out
184   @endcode 196   @endcode
185   197  
186   @param op The inner I/O awaitable to wrap. 198   @param op The inner I/O awaitable to wrap.
187   @param timeout The relative duration after which to cancel. 199   @param timeout The relative duration after which to cancel.
188   200  
189   @return An awaitable whose result matches @p op's result type. 201   @return An awaitable whose result matches @p op's result type.
190   202  
191   @see cancel_at 203   @see cancel_at
192   */ 204   */
193   auto 205   auto
HITCBC 194   4 cancel_after(capy::IoAwaitable auto&& op, timer::duration timeout) 206   4 cancel_after(capy::IoAwaitable auto&& op, timer::duration timeout)
195   { 207   {
196   return cancel_at( 208   return cancel_at(
HITCBC 197   4 std::forward<decltype(op)>(op), timer::clock_type::now() + timeout); 209   4 std::forward<decltype(op)>(op), timer::clock_type::now() + timeout);
198   } 210   }
199   211  
200   } // namespace boost::corosio 212   } // namespace boost::corosio
201   213  
202   #endif 214   #endif