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 8573 : explicit wait_awaitable(io_timer& t) noexcept : t_(t) {}
51 :
52 8549 : bool await_ready() const noexcept
53 : {
54 8549 : return token_.stop_requested();
55 : }
56 :
57 8563 : capy::io_result<> await_resume() const noexcept
58 : {
59 8563 : if (token_.stop_requested())
60 MIS 0 : return {capy::error::canceled};
61 HIT 8563 : return {ec_};
62 : }
63 :
64 8573 : auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
65 : -> std::coroutine_handle<>
66 : {
67 8573 : token_ = env->stop_token;
68 8573 : auto& impl = t_.get();
69 : // Inline fast path: already expired and not in the heap
70 17124 : if (impl.heap_index_ == implementation::npos &&
71 17098 : (impl.expiry_ == (time_point::min)() ||
72 17120 : impl.expiry_ <= clock_type::now()))
73 : {
74 273 : ec_ = {};
75 273 : token_ = {}; // match normal path so await_resume
76 : // returns ec_, not a stale stop check
77 273 : auto d = env->executor;
78 273 : d.post(h);
79 273 : return std::noop_coroutine();
80 : }
81 8300 : return impl.wait(h, env->executor, std::move(token_), &ec_);
82 : }
83 : };
84 :
85 : public:
86 : struct implementation : io_object::implementation
87 : {
88 : static constexpr std::size_t npos =
89 : (std::numeric_limits<std::size_t>::max)();
90 :
91 : std::chrono::steady_clock::time_point expiry_{};
92 : std::size_t heap_index_ = npos;
93 : bool might_have_pending_waits_ = false;
94 :
95 : virtual std::coroutine_handle<> wait(
96 : std::coroutine_handle<>,
97 : capy::executor_ref,
98 : std::stop_token,
99 : std::error_code*) = 0;
100 : };
101 :
102 : /// The clock type used for time operations.
103 : using clock_type = std::chrono::steady_clock;
104 :
105 : /// The time point type for absolute expiry times.
106 : using time_point = clock_type::time_point;
107 :
108 : /// The duration type for relative expiry times.
109 : using duration = clock_type::duration;
110 :
111 : /** Cancel all pending asynchronous wait operations.
112 :
113 : All outstanding operations complete with an error code that
114 : compares equal to `capy::cond::canceled`.
115 :
116 : @return The number of operations that were cancelled.
117 : */
118 20 : std::size_t cancel()
119 : {
120 20 : if (!get().might_have_pending_waits_)
121 12 : return 0;
122 8 : return do_cancel();
123 : }
124 :
125 : /** Return the timer's expiry time as an absolute time.
126 :
127 : @return The expiry time point. If no expiry has been set,
128 : returns a default-constructed time_point.
129 : */
130 38 : time_point expiry() const noexcept
131 : {
132 38 : return get().expiry_;
133 : }
134 :
135 : /** Wait for the timer to expire.
136 :
137 : Multiple coroutines may wait on the same timer concurrently.
138 : When the timer expires, all waiters complete with success.
139 :
140 : The operation supports cancellation via `std::stop_token` through
141 : the affine awaitable protocol. If the associated stop token is
142 : triggered, only that waiter completes with an error that
143 : compares equal to `capy::cond::canceled`; other waiters are
144 : unaffected.
145 :
146 : This timer must outlive the returned awaitable.
147 :
148 : @return An awaitable that completes with `io_result<>`.
149 : */
150 8573 : auto wait()
151 : {
152 8573 : return wait_awaitable(*this);
153 : }
154 :
155 : protected:
156 : /** Dispatch cancel to the concrete implementation.
157 :
158 : @return The number of operations that were cancelled.
159 : */
160 : virtual std::size_t do_cancel() = 0;
161 :
162 8576 : explicit io_timer(handle h) noexcept : io_object(std::move(h)) {}
163 :
164 : /// Move construct.
165 2 : io_timer(io_timer&& other) noexcept : io_object(std::move(other)) {}
166 :
167 : /// Move assign.
168 : io_timer& operator=(io_timer&& other) noexcept
169 : {
170 : if (this != &other)
171 : h_ = std::move(other.h_);
172 : return *this;
173 : }
174 :
175 : io_timer(io_timer const&) = delete;
176 : io_timer& operator=(io_timer const&) = delete;
177 :
178 : /// Return the underlying implementation.
179 8631 : implementation& get() const noexcept
180 : {
181 8631 : return *static_cast<implementation*>(h_.get());
182 : }
183 : };
184 :
185 : } // namespace boost::corosio
186 :
187 : #endif
|