libs/corosio/include/boost/corosio/timer.hpp

94.4% Lines (17/18) 100.0% Functions (10/10) 66.7% Branches (2/3)
libs/corosio/include/boost/corosio/timer.hpp
Line Branch Hits Source Code
1 //
2 // Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
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_TIMER_HPP
11 #define BOOST_COROSIO_TIMER_HPP
12
13 #include <boost/corosio/detail/config.hpp>
14 #include <boost/corosio/detail/except.hpp>
15 #include <boost/corosio/io_object.hpp>
16 #include <boost/capy/io_result.hpp>
17 #include <boost/capy/error.hpp>
18 #include <boost/capy/ex/executor_ref.hpp>
19 #include <boost/capy/ex/execution_context.hpp>
20 #include <boost/capy/concept/executor.hpp>
21 #include <system_error>
22
23 #include <chrono>
24 #include <concepts>
25 #include <coroutine>
26 #include <stop_token>
27 #include <type_traits>
28
29 namespace boost::corosio {
30
31 /** An asynchronous timer for coroutine I/O.
32
33 This class provides asynchronous timer operations that return
34 awaitable types. The timer can be used to schedule operations
35 to occur after a specified duration or at a specific time point.
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. A timer must not have concurrent wait
43 operations.
44
45 @par Semantics
46 Wraps platform timer facilities via the io_context reactor.
47 Operations dispatch to OS timer APIs (timerfd, IOCP timers,
48 kqueue EVFILT_TIMER).
49 */
50 class BOOST_COROSIO_DECL timer : public io_object
51 {
52 struct wait_awaitable
53 {
54 timer& t_;
55 std::stop_token token_;
56 mutable std::error_code ec_;
57
58 5185 explicit wait_awaitable(timer& t) noexcept : t_(t) {}
59
60 5185 bool await_ready() const noexcept
61 {
62 5185 return token_.stop_requested();
63 }
64
65 5185 capy::io_result<> await_resume() const noexcept
66 {
67
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 5185 times.
5185 if (token_.stop_requested())
68 return {capy::error::canceled};
69 5185 return {ec_};
70 }
71
72 template<typename Ex>
73 auto await_suspend(
74 std::coroutine_handle<> h,
75 Ex const& ex) -> std::coroutine_handle<>
76 {
77 t_.get().wait(h, ex, token_, &ec_);
78 return std::noop_coroutine();
79 }
80
81 template<typename Ex>
82 5185 auto await_suspend(
83 std::coroutine_handle<> h,
84 Ex const& ex,
85 std::stop_token token) -> std::coroutine_handle<>
86 {
87 5185 token_ = std::move(token);
88
1/1
✓ Branch 3 taken 5185 times.
5185 t_.get().wait(h, ex, token_, &ec_);
89 5185 return std::noop_coroutine();
90 }
91 };
92
93 public:
94 struct timer_impl : io_object_impl
95 {
96 virtual void wait(
97 std::coroutine_handle<>,
98 capy::executor_ref,
99 std::stop_token,
100 std::error_code*) = 0;
101 };
102
103 public:
104 /// The clock type used for time operations.
105 using clock_type = std::chrono::steady_clock;
106
107 /// The time point type for absolute expiry times.
108 using time_point = clock_type::time_point;
109
110 /// The duration type for relative expiry times.
111 using duration = clock_type::duration;
112
113 /** Destructor.
114
115 Cancels any pending operations and releases timer resources.
116 */
117 ~timer();
118
119 /** Construct a timer from an execution context.
120
121 @param ctx The execution context that will own this timer.
122 */
123 explicit timer(capy::execution_context& ctx);
124
125 /** Move constructor.
126
127 Transfers ownership of the timer resources.
128
129 @param other The timer to move from.
130 */
131 timer(timer&& other) noexcept;
132
133 /** Move assignment operator.
134
135 Closes any existing timer and transfers ownership.
136 The source and destination must share the same execution context.
137
138 @param other The timer to move from.
139
140 @return Reference to this timer.
141
142 @throws std::logic_error if the timers have different execution contexts.
143 */
144 timer& operator=(timer&& other);
145
146 timer(timer const&) = delete;
147 timer& operator=(timer const&) = delete;
148
149 /** Cancel any pending asynchronous operations.
150
151 All outstanding operations complete with an error code that
152 compares equal to `capy::cond::canceled`.
153 */
154 void cancel();
155
156 /** Get the timer's expiry time as an absolute time.
157
158 @return The expiry time point. If no expiry has been set,
159 returns a default-constructed time_point.
160 */
161 time_point expiry() const;
162
163 /** Set the timer's expiry time as an absolute time.
164
165 Any pending asynchronous wait operations will be cancelled.
166
167 @param t The expiry time to be used for the timer.
168 */
169 void expires_at(time_point t);
170
171 /** Set the timer's expiry time relative to now.
172
173 Any pending asynchronous wait operations will be cancelled.
174
175 @param d The expiry time relative to now.
176 */
177 void expires_after(duration d);
178
179 /** Set the timer's expiry time relative to now.
180
181 This is a convenience overload that accepts any duration type
182 and converts it to the timer's native duration type.
183
184 @param d The expiry time relative to now.
185 */
186 template<class Rep, class Period>
187 5199 void expires_after(std::chrono::duration<Rep, Period> d)
188 {
189 5199 expires_after(std::chrono::duration_cast<duration>(d));
190 5199 }
191
192 /** Wait for the timer to expire.
193
194 The operation supports cancellation via `std::stop_token` through
195 the affine awaitable protocol. If the associated stop token is
196 triggered, the operation completes immediately with an error
197 that compares equal to `capy::cond::canceled`.
198
199 @par Example
200 @code
201 timer t(ctx);
202 t.expires_after(std::chrono::seconds(5));
203 auto [ec] = co_await t.wait();
204 if (ec == capy::cond::canceled)
205 {
206 // Cancelled via stop_token or cancel()
207 co_return;
208 }
209 if (ec)
210 {
211 // Handle other errors
212 co_return;
213 }
214 // Timer expired
215 @endcode
216
217 @return An awaitable that completes with `io_result<>`.
218 Returns success (default error_code) when the timer expires,
219 or an error code on failure. Compare against error conditions
220 (e.g., `ec == capy::cond::canceled`) rather than error codes.
221
222 @par Preconditions
223 The timer must have an expiry time set via expires_at() or
224 expires_after().
225 */
226 5185 auto wait()
227 {
228 5185 return wait_awaitable(*this);
229 }
230
231 private:
232 15650 timer_impl& get() const noexcept
233 {
234 15650 return *static_cast<timer_impl*>(impl_);
235 }
236 };
237
238 } // namespace boost::corosio
239
240 #endif
241