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

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