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
67  
cancel_at(capy::IoAwaitable auto&& op, timer& t, timer::time_point deadline)
67  
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>(
70  
        std::forward<decltype(op)>(op), t, deadline);
70  
        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
111  
cancel_after(capy::IoAwaitable auto&& op, timer& t, timer::duration timeout)
111  
cancel_after(capy::IoAwaitable auto&& op, timer& t, timer::duration timeout)
112  
{
112  
{
113  
    return cancel_at(
113  
    return cancel_at(
114  
        std::forward<decltype(op)>(op), t, timer::clock_type::now() + timeout);
114  
        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  
    @par Example
136  
    @par Example
137  
    @code
137  
    @code
138  
    auto [ec, n] = co_await cancel_at(
138  
    auto [ec, n] = co_await cancel_at(
139  
        sock.read_some( buf ),
139  
        sock.read_some( buf ),
140  
        clock::now() + 5s );
140  
        clock::now() + 5s );
141  
    if (ec == capy::cond::canceled)
141  
    if (ec == capy::cond::canceled)
142  
        // timed out or parent cancelled
142  
        // timed out or parent cancelled
143  
    @endcode
143  
    @endcode
144  

144  

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

147  

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

149  

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

158  

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

160  

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

163  

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

169  

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

174  

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

177  

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

185  

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

188  

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

190  

191  
    @see cancel_at
191  
    @see cancel_at
192  
*/
192  
*/
193  
auto
193  
auto
194  
cancel_after(capy::IoAwaitable auto&& op, timer::duration timeout)
194  
cancel_after(capy::IoAwaitable auto&& op, timer::duration timeout)
195  
{
195  
{
196  
    return cancel_at(
196  
    return cancel_at(
197  
        std::forward<decltype(op)>(op), timer::clock_type::now() + timeout);
197  
        std::forward<decltype(op)>(op), timer::clock_type::now() + timeout);
198  
}
198  
}
199  

199  

200  
} // namespace boost::corosio
200  
} // namespace boost::corosio
201  

201  

202  
#endif
202  
#endif