1 +
//
 
2 +
// Copyright (c) 2026 Steve Gerbino
 
3 +
//
 
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)
 
6 +
//
 
7 +
// Official repository: https://github.com/cppalliance/corosio
 
8 +
//
 
9 +

 
10 +
#ifndef BOOST_COROSIO_CANCEL_HPP
 
11 +
#define BOOST_COROSIO_CANCEL_HPP
 
12 +

 
13 +
#include <boost/corosio/detail/cancel_at_awaitable.hpp>
 
14 +
#include <boost/corosio/timer.hpp>
 
15 +
#include <boost/capy/concept/io_awaitable.hpp>
 
16 +

 
17 +
#include <type_traits>
 
18 +
#include <utility>
 
19 +

 
20 +
namespace boost::corosio {
 
21 +

 
22 +
/** Cancel an operation if it does not complete by a deadline.
 
23 +

 
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
 
26 +
    completes with an error comparing equal to `capy::cond::canceled`.
 
27 +
    If the inner operation completes first, the timer is cancelled.
 
28 +

 
29 +
    Parent cancellation (from the caller's stop token) is forwarded
 
30 +
    to both the inner operation and the timeout timer.
 
31 +

 
32 +
    The timer's expiry is overwritten by this call. The timer must
 
33 +
    outlive the returned awaitable. Do not issue overlapping waits
 
34 +
    on the same timer.
 
35 +

 
36 +
    @par Completion Conditions
 
37 +
    The returned awaitable resumes when either:
 
38 +
    @li The inner operation completes (successfully or with error).
 
39 +
    @li The deadline expires and the inner operation is cancelled.
 
40 +
    @li The caller's stop token is triggered, cancelling both.
 
41 +

 
42 +
    @par Error Conditions
 
43 +
    @li On timeout or parent cancellation, the inner operation
 
44 +
        completes with an error equal to `capy::cond::canceled`.
 
45 +
    @li All other errors are propagated from the inner operation.
 
46 +

 
47 +
    @par Example
 
48 +
    @code
 
49 +
    timer timeout_timer( ioc );
 
50 +
    auto [ec, n] = co_await cancel_at(
 
51 +
        sock.read_some( buf ), timeout_timer,
 
52 +
        clock::now() + 5s );
 
53 +
    if (ec == capy::cond::canceled)
 
54 +
        // timed out or parent cancelled
 
55 +
    @endcode
 
56 +

 
57 +
    @param op The inner I/O awaitable to wrap.
 
58 +
    @param t The timer to use for the deadline. Must outlive
 
59 +
        the returned awaitable.
 
60 +
    @param deadline The absolute time point at which to cancel.
 
61 +

 
62 +
    @return An awaitable whose result matches @p op's result type.
 
63 +

 
64 +
    @see cancel_after
 
65 +
*/
 
66 +
auto cancel_at(
 
67 +
    capy::IoAwaitable auto&& op,
 
68 +
    timer& t,
 
69 +
    timer::time_point deadline)
 
70 +
{
 
71 +
    return detail::cancel_at_awaitable<
 
72 +
        std::decay_t<decltype(op)>, timer>(
 
73 +
        std::forward<decltype(op)>(op), t, deadline);
 
74 +
}
 
75 +

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

 
212 +
#endif