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_IO_IO_TIMER_HPP
12 : #define BOOST_COROSIO_IO_IO_TIMER_HPP
13 :
14 : #include <boost/corosio/detail/config.hpp>
15 : #include <boost/corosio/io/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/io_env.hpp>
20 :
21 : #include <chrono>
22 : #include <coroutine>
23 : #include <cstddef>
24 : #include <limits>
25 : #include <stop_token>
26 : #include <system_error>
27 :
28 : namespace boost::corosio {
29 :
30 : /** Abstract base for asynchronous timers.
31 :
32 : Provides the common timer interface: `wait`, `cancel`, and
33 : `expiry`. Concrete classes like @ref timer add the ability
34 : to set expiry times and cancel individual waiters.
35 :
36 : @par Thread Safety
37 : Distinct objects: Safe.
38 : Shared objects: Unsafe.
39 :
40 : @see timer, io_object
41 : */
42 : class BOOST_COROSIO_DECL io_timer : public io_object
43 : {
44 : struct wait_awaitable
45 : {
46 : io_timer& t_;
47 : std::stop_token token_;
48 : mutable std::error_code ec_;
49 :
50 HIT 7598 : explicit wait_awaitable(io_timer& t) noexcept : t_(t) {}
51 :
52 7574 : bool await_ready() const noexcept
53 : {
54 7574 : return token_.stop_requested();
55 : }
56 :
57 7588 : capy::io_result<> await_resume() const noexcept
58 : {
59 7588 : if (token_.stop_requested())
60 MIS 0 : return {capy::error::canceled};
61 HIT 7588 : return {ec_};
62 : }
63 :
64 7598 : auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
65 : -> std::coroutine_handle<>
66 : {
67 7598 : token_ = env->stop_token;
68 7598 : auto& impl = t_.get();
69 : // Inline fast path: already expired and not in the heap
70 15174 : if (impl.heap_index_ == implementation::npos &&
71 15148 : (impl.expiry_ == (time_point::min)() ||
72 15170 : impl.expiry_ <= clock_type::now()))
73 : {
74 279 : ec_ = {};
75 279 : token_ = {}; // match normal path so await_resume
76 : // returns ec_, not a stale stop check
77 279 : auto d = env->executor;
78 279 : d.post(h);
79 279 : return std::noop_coroutine();
80 : }
81 7319 : return impl.wait(h, env->executor, std::move(token_), &ec_);
82 : }
83 : };
84 :
85 : public:
86 : /** Backend interface for timer wait operations.
87 :
88 : Holds per-timer state (expiry, heap position) and provides
89 : the virtual `wait` entry point that concrete timer services
90 : override.
91 : */
92 : struct implementation : io_object::implementation
93 : {
94 : /// Sentinel value indicating the timer is not in the heap.
95 : static constexpr std::size_t npos =
96 : (std::numeric_limits<std::size_t>::max)();
97 :
98 : /// The absolute expiry time point.
99 : std::chrono::steady_clock::time_point expiry_{};
100 :
101 : /// Index in the timer service's min-heap, or `npos`.
102 : std::size_t heap_index_ = npos;
103 :
104 : /// True if `wait()` has been called since last cancel.
105 : bool might_have_pending_waits_ = false;
106 :
107 : /// Initiate an asynchronous wait for the timer to expire.
108 : virtual std::coroutine_handle<> wait(
109 : std::coroutine_handle<>,
110 : capy::executor_ref,
111 : std::stop_token,
112 : std::error_code*) = 0;
113 : };
114 :
115 : /// The clock type used for time operations.
116 : using clock_type = std::chrono::steady_clock;
117 :
118 : /// The time point type for absolute expiry times.
119 : using time_point = clock_type::time_point;
120 :
121 : /// The duration type for relative expiry times.
122 : using duration = clock_type::duration;
123 :
124 : /** Cancel all pending asynchronous wait operations.
125 :
126 : All outstanding operations complete with an error code that
127 : compares equal to `capy::cond::canceled`.
128 :
129 : @return The number of operations that were cancelled.
130 : */
131 20 : std::size_t cancel()
132 : {
133 20 : if (!get().might_have_pending_waits_)
134 12 : return 0;
135 8 : return do_cancel();
136 : }
137 :
138 : /** Return the timer's expiry time as an absolute time.
139 :
140 : @return The expiry time point. If no expiry has been set,
141 : returns a default-constructed time_point.
142 : */
143 38 : time_point expiry() const noexcept
144 : {
145 38 : return get().expiry_;
146 : }
147 :
148 : /** Wait for the timer to expire.
149 :
150 : Multiple coroutines may wait on the same timer concurrently.
151 : When the timer expires, all waiters complete with success.
152 :
153 : The operation supports cancellation via `std::stop_token` through
154 : the affine awaitable protocol. If the associated stop token is
155 : triggered, only that waiter completes with an error that
156 : compares equal to `capy::cond::canceled`; other waiters are
157 : unaffected.
158 :
159 : This timer must outlive the returned awaitable.
160 :
161 : @return An awaitable that completes with `io_result<>`.
162 : */
163 7598 : auto wait()
164 : {
165 7598 : return wait_awaitable(*this);
166 : }
167 :
168 : protected:
169 : /** Dispatch cancel to the concrete implementation.
170 :
171 : @return The number of operations that were cancelled.
172 : */
173 : virtual std::size_t do_cancel() = 0;
174 :
175 7601 : explicit io_timer(handle h) noexcept : io_object(std::move(h)) {}
176 :
177 : /// Move construct.
178 2 : io_timer(io_timer&& other) noexcept : io_object(std::move(other)) {}
179 :
180 : /// Move assign.
181 : io_timer& operator=(io_timer&& other) noexcept
182 : {
183 : if (this != &other)
184 : h_ = std::move(other.h_);
185 : return *this;
186 : }
187 :
188 : io_timer(io_timer const&) = delete;
189 : io_timer& operator=(io_timer const&) = delete;
190 :
191 : /// Return the underlying implementation.
192 7656 : implementation& get() const noexcept
193 : {
194 7656 : return *static_cast<implementation*>(h_.get());
195 : }
196 : };
197 :
198 : } // namespace boost::corosio
199 :
200 : #endif
|