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   7918 explicit wait_awaitable(io_timer& t) noexcept : t_(t) {} 52   6677 explicit wait_awaitable(io_timer& t) noexcept : t_(t) {}
53   53  
HITCBC 54   7894 bool await_ready() const noexcept 54   6653 bool await_ready() const noexcept
55   { 55   {
HITCBC 56   7894 return token_.stop_requested(); 56   6653 return token_.stop_requested();
57   } 57   }
58   58  
HITCBC 59   7902 capy::io_result<> await_resume() const noexcept 59   6661 capy::io_result<> await_resume() const noexcept
60   { 60   {
HITCBC 61   7902 if (token_.stop_requested()) 61   6661 if (token_.stop_requested())
MISUBC 62   return {capy::error::canceled}; 62   return {capy::error::canceled};
HITCBC 63   7902 return {ec_}; 63   6661 return {ec_};
64   } 64   }
65   65  
HITCBC 66   7918 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 66   6677 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
67   -> std::coroutine_handle<> 67   -> std::coroutine_handle<>
68   { 68   {
HITCBC 69   7918 token_ = env->stop_token; 69   6677 token_ = env->stop_token;
HITCBC 70   7918 cont_op_.cont.h = h; 70   6677 cont_op_.cont.h = h;
HITCBC 71   7918 auto& impl = t_.get(); 71   6677 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   15814 if (impl.heap_index_ == implementation::npos && 73   13332 if (impl.heap_index_ == implementation::npos &&
HITCBC 74   15788 (impl.expiry_ == (time_point::min)() || 74   13306 (impl.expiry_ == (time_point::min)() ||
HITCBC 75   15810 impl.expiry_ <= clock_type::now())) 75   13328 impl.expiry_ <= clock_type::now()))
76   { 76   {
HITCBC 77   205 ec_ = {}; 77   199 ec_ = {};
HITCBC 78   205 token_ = {}; // match normal path so await_resume 78   199 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   205 auto d = env->executor; 80   199 auto d = env->executor;
HITCBC 81   205 d.post(cont_op_.cont); 81   199 d.post(cont_op_.cont);
HITCBC 82   205 return std::noop_coroutine(); 82   199 return std::noop_coroutine();
83   } 83   }
HITCBC 84   7713 return impl.wait(h, env->executor, std::move(token_), &ec_, &cont_op_.cont); 84   6478 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   7918 auto wait() 167   6677 auto wait()
168   { 168   {
HITCBC 169   7918 return wait_awaitable(*this); 169   6677 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   7921 explicit io_timer(handle h) noexcept : io_object(std::move(h)) {} 179   6680 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   7976 implementation& get() const noexcept 196   6735 implementation& get() const noexcept
197   { 197   {
HITCBC 198   7976 return *static_cast<implementation*>(h_.get()); 198   6735 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