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

75  

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

77  

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

79  

80  
    The timer's expiry is overwritten by this call. The timer must
80  
    The timer's expiry is overwritten by this call. The timer must
81  
    outlive the returned awaitable. Do not issue overlapping waits
81  
    outlive the returned awaitable. Do not issue overlapping waits
82  
    on the same timer.
82  
    on the same timer.
83  

83  

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

89  

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

94  

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

103  

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

108  

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

110  

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

122  

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

124  

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

127  

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

133  

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

138  

139  
    @note Creates a timer per call. Use the explicit-timer overload
139  
    @note Creates a timer per call. Use the explicit-timer overload
140  
        to amortize allocation across multiple timeouts.
140  
        to amortize allocation across multiple timeouts.
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 cancel_at(
158  
auto cancel_at(
159  
    capy::IoAwaitable auto&& op,
159  
    capy::IoAwaitable auto&& op,
160  
    timer::time_point deadline)
160  
    timer::time_point deadline)
161  
{
161  
{
162  
    return detail::cancel_at_awaitable<
162  
    return detail::cancel_at_awaitable<
163  
        std::decay_t<decltype(op)>, timer, true>(
163  
        std::decay_t<decltype(op)>, timer, true>(
164  
        std::forward<decltype(op)>(op), deadline);
164  
        std::forward<decltype(op)>(op), deadline);
165  
}
165  
}
166  

166  

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

168  

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

171  

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

177  

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

182  

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

185  

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

193  

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

196  

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

198  

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

209  

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

211  

212  
#endif
212  
#endif