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