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 7973 : explicit wait_awaitable(io_timer& t) noexcept : t_(t) {}
51 :
52 7973 : bool await_ready() const noexcept
53 : {
54 7973 : return token_.stop_requested();
55 : }
56 :
57 7973 : capy::io_result<> await_resume() const noexcept
58 : {
59 7973 : if (token_.stop_requested())
60 MIS 0 : return {capy::error::canceled};
61 HIT 7973 : return {ec_};
62 : }
63 :
64 7973 : auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
65 : -> std::coroutine_handle<>
66 : {
67 7973 : token_ = env->stop_token;
68 7973 : auto& impl = t_.get();
69 : // Inline fast path: already expired and not in the heap
70 15924 : if (impl.heap_index_ == implementation::npos &&
71 15898 : (impl.expiry_ == (time_point::min)() ||
72 15920 : impl.expiry_ <= clock_type::now()))
73 : {
74 269 : ec_ = {};
75 269 : auto d = env->executor;
76 269 : d.post(h);
77 269 : return std::noop_coroutine();
78 : }
79 7704 : 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 : This timer must outlive the returned awaitable.
145 :
146 : @return An awaitable that completes with `io_result<>`.
147 : */
148 7973 : auto wait()
149 : {
150 7973 : return wait_awaitable(*this);
151 : }
152 :
153 : protected:
154 : /** Dispatch cancel to the concrete implementation.
155 :
156 : @return The number of operations that were cancelled.
157 : */
158 : virtual std::size_t do_cancel() = 0;
159 :
160 7984 : explicit io_timer(handle h) noexcept : io_object(std::move(h)) {}
161 :
162 : /// Move construct.
163 2 : io_timer(io_timer&& other) noexcept : io_object(std::move(other)) {}
164 :
165 : /// Move assign.
166 : io_timer& operator=(io_timer&& other) noexcept
167 : {
168 : if (this != &other)
169 : h_ = std::move(other.h_);
170 : return *this;
171 : }
172 :
173 : io_timer(io_timer const&) = delete;
174 : io_timer& operator=(io_timer const&) = delete;
175 :
176 : /// Return the underlying implementation.
177 8031 : implementation& get() const noexcept
178 : {
179 8031 : return *static_cast<implementation*>(h_.get());
180 : }
181 : };
182 :
183 : } // namespace boost::corosio
184 :
185 : #endif
|