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 cancel_at(
66 +
auto
67 -
    capy::IoAwaitable auto&& op,
67 +
cancel_at(capy::IoAwaitable auto&& op, timer& t, timer::time_point deadline)
68 -
    timer& t,
 
69 -
    timer::time_point deadline)
 
70  
{
68  
{
71 -
    return detail::cancel_at_awaitable<
69 +
    return detail::cancel_at_awaitable<std::decay_t<decltype(op)>, timer>(
72 -
        std::decay_t<decltype(op)>, timer>(
 
73  
        std::forward<decltype(op)>(op), t, deadline);
70  
        std::forward<decltype(op)>(op), t, deadline);
74  
}
71  
}
75  

72  

76  
/** Cancel an operation if it does not complete within a duration.
73  
/** Cancel an operation if it does not complete within a duration.
77  

74  

78  
    Equivalent to `cancel_at( op, t, clock::now() + timeout )`.
75  
    Equivalent to `cancel_at( op, t, clock::now() + timeout )`.
79  

76  

80  
    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
81  
    outlive the returned awaitable. Do not issue overlapping waits
78  
    outlive the returned awaitable. Do not issue overlapping waits
82  
    on the same timer.
79  
    on the same timer.
83  

80  

84  
    @par Completion Conditions
81  
    @par Completion Conditions
85  
    The returned awaitable resumes when either:
82  
    The returned awaitable resumes when either:
86  
    @li The inner operation completes (successfully or with error).
83  
    @li The inner operation completes (successfully or with error).
87  
    @li The timeout elapses and the inner operation is cancelled.
84  
    @li The timeout elapses and the inner operation is cancelled.
88  
    @li The caller's stop token is triggered, cancelling both.
85  
    @li The caller's stop token is triggered, cancelling both.
89  

86  

90  
    @par Error Conditions
87  
    @par Error Conditions
91  
    @li On timeout or parent cancellation, the inner operation
88  
    @li On timeout or parent cancellation, the inner operation
92  
        completes with an error equal to `capy::cond::canceled`.
89  
        completes with an error equal to `capy::cond::canceled`.
93  
    @li All other errors are propagated from the inner operation.
90  
    @li All other errors are propagated from the inner operation.
94  

91  

95  
    @par Example
92  
    @par Example
96  
    @code
93  
    @code
97  
    timer timeout_timer( ioc );
94  
    timer timeout_timer( ioc );
98  
    auto [ec, n] = co_await cancel_after(
95  
    auto [ec, n] = co_await cancel_after(
99  
        sock.read_some( buf ), timeout_timer, 5s );
96  
        sock.read_some( buf ), timeout_timer, 5s );
100  
    if (ec == capy::cond::canceled)
97  
    if (ec == capy::cond::canceled)
101  
        // timed out
98  
        // timed out
102  
    @endcode
99  
    @endcode
103  

100  

104  
    @param op The inner I/O awaitable to wrap.
101  
    @param op The inner I/O awaitable to wrap.
105  
    @param t The timer to use for the timeout. Must outlive
102  
    @param t The timer to use for the timeout. Must outlive
106  
        the returned awaitable.
103  
        the returned awaitable.
107  
    @param timeout The relative duration after which to cancel.
104  
    @param timeout The relative duration after which to cancel.
108  

105  

109  
    @return An awaitable whose result matches @p op's result type.
106  
    @return An awaitable whose result matches @p op's result type.
110  

107  

111  
    @see cancel_at
108  
    @see cancel_at
112  
*/
109  
*/
113 -
auto cancel_after(
110 +
auto
114 -
    capy::IoAwaitable auto&& op,
111 +
cancel_after(capy::IoAwaitable auto&& op, timer& t, timer::duration timeout)
115 -
    timer& t,
 
116 -
    timer::duration timeout)
 
117  
{
112  
{
118  
    return cancel_at(
113  
    return cancel_at(
119 -
        std::forward<decltype(op)>(op), t,
114 +
        std::forward<decltype(op)>(op), t, timer::clock_type::now() + timeout);
120 -
        timer::clock_type::now() + timeout);
 
121  
}
115  
}
122  

116  

123  
/** Cancel an operation if it does not complete by a deadline.
117  
/** Cancel an operation if it does not complete by a deadline.
124  

118  

125  
    Convenience overload that creates a @ref timer internally.
119  
    Convenience overload that creates a @ref timer internally.
126  
    Otherwise identical to the explicit-timer overload.
120  
    Otherwise identical to the explicit-timer overload.
127  

121  

128  
    @par Completion Conditions
122  
    @par Completion Conditions
129  
    The returned awaitable resumes when either:
123  
    The returned awaitable resumes when either:
130  
    @li The inner operation completes (successfully or with error).
124  
    @li The inner operation completes (successfully or with error).
131  
    @li The deadline expires and the inner operation is cancelled.
125  
    @li The deadline expires and the inner operation is cancelled.
132  
    @li The caller's stop token is triggered, cancelling both.
126  
    @li The caller's stop token is triggered, cancelling both.
133  

127  

134  
    @par Error Conditions
128  
    @par Error Conditions
135  
    @li On timeout or parent cancellation, the inner operation
129  
    @li On timeout or parent cancellation, the inner operation
136  
        completes with an error equal to `capy::cond::canceled`.
130  
        completes with an error equal to `capy::cond::canceled`.
137  
    @li All other errors are propagated from the inner operation.
131  
    @li All other errors are propagated from the inner operation.
138  

132  

139  
    @note Creates a timer per call. Use the explicit-timer overload
133  
    @note Creates a timer per call. Use the explicit-timer overload
140  
        to amortize allocation across multiple timeouts.
134  
        to amortize allocation across multiple timeouts.
141  

135  

142  
    @par Example
136  
    @par Example
143  
    @code
137  
    @code
144  
    auto [ec, n] = co_await cancel_at(
138  
    auto [ec, n] = co_await cancel_at(
145  
        sock.read_some( buf ),
139  
        sock.read_some( buf ),
146  
        clock::now() + 5s );
140  
        clock::now() + 5s );
147  
    if (ec == capy::cond::canceled)
141  
    if (ec == capy::cond::canceled)
148  
        // timed out or parent cancelled
142  
        // timed out or parent cancelled
149  
    @endcode
143  
    @endcode
150  

144  

151  
    @param op The inner I/O awaitable to wrap.
145  
    @param op The inner I/O awaitable to wrap.
152  
    @param deadline The absolute time point at which to cancel.
146  
    @param deadline The absolute time point at which to cancel.
153  

147  

154  
    @return An awaitable whose result matches @p op's result type.
148  
    @return An awaitable whose result matches @p op's result type.
155  

149  

156  
    @see cancel_after
150  
    @see cancel_after
157  
*/
151  
*/
158 -
auto cancel_at(
152 +
auto
159 -
    capy::IoAwaitable auto&& op,
153 +
cancel_at(capy::IoAwaitable auto&& op, timer::time_point deadline)
160 -
    timer::time_point deadline)
 
161  
{
154  
{
162 -
    return detail::cancel_at_awaitable<
155 +
    return detail::cancel_at_awaitable<std::decay_t<decltype(op)>, timer, true>(
163 -
        std::decay_t<decltype(op)>, timer, true>(
 
164  
        std::forward<decltype(op)>(op), deadline);
156  
        std::forward<decltype(op)>(op), deadline);
165  
}
157  
}
166  

158  

167  
/** Cancel an operation if it does not complete within a duration.
159  
/** Cancel an operation if it does not complete within a duration.
168  

160  

169  
    Convenience overload that creates a @ref timer internally.
161  
    Convenience overload that creates a @ref timer internally.
170  
    Equivalent to `cancel_at( op, clock::now() + timeout )`.
162  
    Equivalent to `cancel_at( op, clock::now() + timeout )`.
171  

163  

172  
    @par Completion Conditions
164  
    @par Completion Conditions
173  
    The returned awaitable resumes when either:
165  
    The returned awaitable resumes when either:
174  
    @li The inner operation completes (successfully or with error).
166  
    @li The inner operation completes (successfully or with error).
175  
    @li The timeout elapses and the inner operation is cancelled.
167  
    @li The timeout elapses and the inner operation is cancelled.
176  
    @li The caller's stop token is triggered, cancelling both.
168  
    @li The caller's stop token is triggered, cancelling both.
177  

169  

178  
    @par Error Conditions
170  
    @par Error Conditions
179  
    @li On timeout or parent cancellation, the inner operation
171  
    @li On timeout or parent cancellation, the inner operation
180  
        completes with an error equal to `capy::cond::canceled`.
172  
        completes with an error equal to `capy::cond::canceled`.
181  
    @li All other errors are propagated from the inner operation.
173  
    @li All other errors are propagated from the inner operation.
182  

174  

183  
    @note Creates a timer per call. Use the explicit-timer overload
175  
    @note Creates a timer per call. Use the explicit-timer overload
184  
        to amortize allocation across multiple timeouts.
176  
        to amortize allocation across multiple timeouts.
185  

177  

186  
    @par Example
178  
    @par Example
187  
    @code
179  
    @code
188  
    auto [ec, n] = co_await cancel_after(
180  
    auto [ec, n] = co_await cancel_after(
189  
        sock.read_some( buf ), 5s );
181  
        sock.read_some( buf ), 5s );
190  
    if (ec == capy::cond::canceled)
182  
    if (ec == capy::cond::canceled)
191  
        // timed out
183  
        // timed out
192  
    @endcode
184  
    @endcode
193  

185  

194  
    @param op The inner I/O awaitable to wrap.
186  
    @param op The inner I/O awaitable to wrap.
195  
    @param timeout The relative duration after which to cancel.
187  
    @param timeout The relative duration after which to cancel.
196  

188  

197  
    @return An awaitable whose result matches @p op's result type.
189  
    @return An awaitable whose result matches @p op's result type.
198  

190  

199  
    @see cancel_at
191  
    @see cancel_at
200  
*/
192  
*/
201 -
auto cancel_after(
193 +
auto
202 -
    capy::IoAwaitable auto&& op,
194 +
cancel_after(capy::IoAwaitable auto&& op, timer::duration timeout)
203 -
    timer::duration timeout)
 
204  
{
195  
{
205  
    return cancel_at(
196  
    return cancel_at(
206 -
        std::forward<decltype(op)>(op),
197 +
        std::forward<decltype(op)>(op), timer::clock_type::now() + timeout);
207 -
        timer::clock_type::now() + timeout);
 
208  
}
198  
}
209  

199  

210  
} // namespace boost::corosio
200  
} // namespace boost::corosio
211  

201  

212  
#endif
202  
#endif