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