TLA Line data Source code
1 : //
2 : // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3 : // Copyright (c) 2026 Steve Gerbino
4 : //
5 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 : //
8 : // Official repository: https://github.com/cppalliance/corosio
9 : //
10 :
11 : #ifndef BOOST_COROSIO_TIMER_HPP
12 : #define BOOST_COROSIO_TIMER_HPP
13 :
14 : #include <boost/corosio/detail/config.hpp>
15 : #include <boost/corosio/io/io_timer.hpp>
16 : #include <boost/capy/ex/execution_context.hpp>
17 : #include <boost/capy/concept/executor.hpp>
18 :
19 : #include <chrono>
20 : #include <concepts>
21 : #include <cstddef>
22 : #include <type_traits>
23 :
24 : namespace boost::corosio {
25 :
26 : /** An asynchronous timer for coroutine I/O.
27 :
28 : This class provides asynchronous timer operations that return
29 : awaitable types. The timer can be used to schedule operations
30 : to occur after a specified duration or at a specific time point.
31 :
32 : Multiple coroutines may wait concurrently on the same timer.
33 : When the timer expires, all waiters complete with success. When
34 : the timer is cancelled, all waiters complete with an error that
35 : compares equal to `capy::cond::canceled`.
36 :
37 : Each timer operation participates in the affine awaitable protocol,
38 : ensuring coroutines resume on the correct executor.
39 :
40 : @par Thread Safety
41 : Distinct objects: Safe.@n
42 : Shared objects: Unsafe.
43 :
44 : @par Semantics
45 : Wraps platform timer facilities via the io_context reactor.
46 : Operations dispatch to OS timer APIs (timerfd, IOCP timers,
47 : kqueue EVFILT_TIMER).
48 : */
49 : class BOOST_COROSIO_DECL timer : public io_timer
50 : {
51 : public:
52 : /// Alias for backward compatibility.
53 : using implementation = io_timer::implementation;
54 :
55 : /** Destructor.
56 :
57 : Cancels any pending operations and releases timer resources.
58 : */
59 : ~timer() override;
60 :
61 : /** Construct a timer from an execution context.
62 :
63 : @param ctx The execution context that will own this timer. It
64 : must be a corosio io_context; otherwise the constructor
65 : throws (a timer service is required).
66 :
67 : @throws std::logic_error if @p ctx is not an io_context.
68 : */
69 : explicit timer(capy::execution_context& ctx);
70 :
71 : /** Construct a timer with an initial absolute expiry time.
72 :
73 : @param ctx The execution context that will own this timer. It
74 : must be a corosio io_context; otherwise the constructor
75 : throws (a timer service is required).
76 : @param t The initial expiry time point.
77 :
78 : @throws std::logic_error if @p ctx is not an io_context.
79 : */
80 : timer(capy::execution_context& ctx, time_point t);
81 :
82 : /** Construct a timer with an initial relative expiry time.
83 :
84 : @param ctx The execution context that will own this timer. It
85 : must be a corosio io_context; otherwise the constructor
86 : throws (a timer service is required).
87 : @param d The initial expiry duration relative to now.
88 :
89 : @throws std::logic_error if @p ctx is not an io_context.
90 : */
91 : template<class Rep, class Period>
92 HIT 8 : timer(capy::execution_context& ctx, std::chrono::duration<Rep, Period> d)
93 8 : : timer(ctx)
94 : {
95 8 : expires_after(d);
96 8 : }
97 :
98 : /** Construct a timer from an executor.
99 :
100 : The timer is associated with the executor's context, which must
101 : be a corosio io_context.
102 :
103 : @param ex The executor whose context will own this timer.
104 :
105 : @throws std::logic_error if the executor's context is not an
106 : io_context.
107 : */
108 : template<class Ex>
109 : requires(!std::same_as<std::remove_cvref_t<Ex>, timer>) &&
110 : capy::Executor<Ex>
111 4 : explicit timer(Ex const& ex) : timer(ex.context())
112 : {
113 2 : }
114 :
115 : /** Construct a timer from an executor with an absolute expiry time.
116 :
117 : @param ex The executor whose context will own this timer.
118 : @param t The initial expiry time point.
119 :
120 : @throws std::logic_error if the executor's context is not an
121 : io_context.
122 : */
123 : template<class Ex>
124 : requires capy::Executor<Ex>
125 2 : timer(Ex const& ex, time_point t) : timer(ex.context(), t)
126 : {
127 2 : }
128 :
129 : /** Construct a timer from an executor with a relative expiry time.
130 :
131 : @param ex The executor whose context will own this timer.
132 : @param d The initial expiry duration relative to now.
133 :
134 : @throws std::logic_error if the executor's context is not an
135 : io_context.
136 : */
137 : template<class Ex, class Rep, class Period>
138 : requires capy::Executor<Ex>
139 2 : timer(Ex const& ex, std::chrono::duration<Rep, Period> d)
140 2 : : timer(ex.context(), d)
141 : {
142 2 : }
143 :
144 : /** Move constructor.
145 :
146 : Transfers ownership of the timer resources.
147 :
148 : @param other The timer to move from.
149 :
150 : @pre No awaitables returned by @p other's methods exist.
151 : @pre The execution context associated with @p other must
152 : outlive this timer.
153 : */
154 : timer(timer&& other) noexcept;
155 :
156 : /** Move assignment operator.
157 :
158 : Closes any existing timer and transfers ownership.
159 :
160 : @param other The timer to move from.
161 :
162 : @pre No awaitables returned by either `*this` or @p other's
163 : methods exist.
164 : @pre The execution context associated with @p other must
165 : outlive this timer.
166 :
167 : @return Reference to this timer.
168 : */
169 : timer& operator=(timer&& other) noexcept;
170 :
171 : timer(timer const&) = delete;
172 : timer& operator=(timer const&) = delete;
173 :
174 : /** Cancel one pending asynchronous wait operation.
175 :
176 : The oldest pending wait is cancelled (FIFO order). It
177 : completes with an error code that compares equal to
178 : `capy::cond::canceled`.
179 :
180 : @return The number of operations that were cancelled (0 or 1).
181 : */
182 4 : std::size_t cancel_one()
183 : {
184 4 : if (!get().might_have_pending_waits_)
185 2 : return 0;
186 2 : return do_cancel_one();
187 : }
188 :
189 : /** Set the timer's expiry time as an absolute time.
190 :
191 : Any pending asynchronous wait operations will be cancelled.
192 :
193 : @param t The expiry time to be used for the timer.
194 :
195 : @return The number of pending operations that were cancelled.
196 : */
197 54 : std::size_t expires_at(time_point t)
198 : {
199 54 : auto& impl = get();
200 54 : impl.expiry_ = t;
201 54 : if (impl.heap_index_ == implementation::npos &&
202 50 : !impl.might_have_pending_waits_)
203 50 : return 0;
204 4 : return do_update_expiry();
205 : }
206 :
207 : /** Set the timer's expiry time relative to now.
208 :
209 : Any pending asynchronous wait operations will be cancelled.
210 :
211 : @param d The expiry time relative to now.
212 :
213 : @return The number of pending operations that were cancelled.
214 : */
215 9077 : std::size_t expires_after(duration d)
216 : {
217 9077 : auto& impl = get();
218 9077 : if (d <= duration::zero())
219 6 : impl.expiry_ = (time_point::min)();
220 : else
221 9071 : impl.expiry_ = clock_type::now() + d;
222 9077 : if (impl.heap_index_ == implementation::npos &&
223 9073 : !impl.might_have_pending_waits_)
224 9073 : return 0;
225 4 : return do_update_expiry();
226 : }
227 :
228 : /** Set the timer's expiry time relative to now.
229 :
230 : This is a convenience overload that accepts any duration type
231 : and converts it to the timer's native duration type. Any
232 : pending asynchronous wait operations will be cancelled.
233 :
234 : @param d The expiry time relative to now.
235 :
236 : @return The number of pending operations that were cancelled.
237 : */
238 : template<class Rep, class Period>
239 9077 : std::size_t expires_after(std::chrono::duration<Rep, Period> d)
240 : {
241 9077 : return expires_after(std::chrono::duration_cast<duration>(d));
242 : }
243 :
244 : protected:
245 : explicit timer(handle h) noexcept : io_timer(std::move(h)) {}
246 :
247 : private:
248 : std::size_t do_cancel() override;
249 : std::size_t do_cancel_one();
250 : std::size_t do_update_expiry();
251 :
252 : /// Return the underlying implementation.
253 9153 : implementation& get() const noexcept
254 : {
255 9153 : return *static_cast<implementation*>(h_.get());
256 : }
257 : };
258 :
259 : } // namespace boost::corosio
260 :
261 : #endif
|