100.00% Lines (35/35) 100.00% Functions (9/9)
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_TIMER_HPP 11   #ifndef BOOST_COROSIO_TIMER_HPP
12   #define BOOST_COROSIO_TIMER_HPP 12   #define BOOST_COROSIO_TIMER_HPP
13   13  
14   #include <boost/corosio/detail/config.hpp> 14   #include <boost/corosio/detail/config.hpp>
15   #include <boost/corosio/io/io_timer.hpp> 15   #include <boost/corosio/io/io_timer.hpp>
16   #include <boost/capy/ex/execution_context.hpp> 16   #include <boost/capy/ex/execution_context.hpp>
17   #include <boost/capy/concept/executor.hpp> 17   #include <boost/capy/concept/executor.hpp>
18   18  
19   #include <chrono> 19   #include <chrono>
20   #include <concepts> 20   #include <concepts>
21   #include <cstddef> 21   #include <cstddef>
22   #include <type_traits> 22   #include <type_traits>
23   23  
24   namespace boost::corosio { 24   namespace boost::corosio {
25   25  
26   /** An asynchronous timer for coroutine I/O. 26   /** An asynchronous timer for coroutine I/O.
27   27  
28   This class provides asynchronous timer operations that return 28   This class provides asynchronous timer operations that return
29   awaitable types. The timer can be used to schedule operations 29   awaitable types. The timer can be used to schedule operations
30   to occur after a specified duration or at a specific time point. 30   to occur after a specified duration or at a specific time point.
31   31  
32   Multiple coroutines may wait concurrently on the same timer. 32   Multiple coroutines may wait concurrently on the same timer.
33   When the timer expires, all waiters complete with success. When 33   When the timer expires, all waiters complete with success. When
34   the timer is cancelled, all waiters complete with an error that 34   the timer is cancelled, all waiters complete with an error that
35   compares equal to `capy::cond::canceled`. 35   compares equal to `capy::cond::canceled`.
36   36  
37   Each timer operation participates in the affine awaitable protocol, 37   Each timer operation participates in the affine awaitable protocol,
38   ensuring coroutines resume on the correct executor. 38   ensuring coroutines resume on the correct executor.
39   39  
40   @par Thread Safety 40   @par Thread Safety
41   Distinct objects: Safe.@n 41   Distinct objects: Safe.@n
42   Shared objects: Unsafe. 42   Shared objects: Unsafe.
43   43  
44   @par Semantics 44   @par Semantics
45   Timers are not backed by per-timer kernel objects. The io_context's 45   Timers are not backed by per-timer kernel objects. The io_context's
46   timer service keeps a process-side min-heap of pending expirations; 46   timer service keeps a process-side min-heap of pending expirations;
47   the nearest expiry drives the reactor's poll timeout, and expirations 47   the nearest expiry drives the reactor's poll timeout, and expirations
48   are processed in the run loop. 48   are processed in the run loop.
49   */ 49   */
50   class BOOST_COROSIO_DECL timer : public io_timer 50   class BOOST_COROSIO_DECL timer : public io_timer
51   { 51   {
52   public: 52   public:
53   /// Alias for backward compatibility. 53   /// Alias for backward compatibility.
54   using implementation = io_timer::implementation; 54   using implementation = io_timer::implementation;
55   55  
56   /** Destructor. 56   /** Destructor.
57   57  
58   Cancels any pending operations and releases timer resources. 58   Cancels any pending operations and releases timer resources.
59   */ 59   */
60   ~timer() override; 60   ~timer() override;
61   61  
62   /** Construct a timer from an execution context. 62   /** Construct a timer from an execution context.
63   63  
64   @param ctx The execution context that will own this timer. It 64   @param ctx The execution context that will own this timer. It
65   must be a corosio io_context; otherwise the constructor 65   must be a corosio io_context; otherwise the constructor
66   throws (a timer service is required). 66   throws (a timer service is required).
67   67  
68   @throws std::logic_error if @p ctx is not an io_context. 68   @throws std::logic_error if @p ctx is not an io_context.
69   */ 69   */
70   explicit timer(capy::execution_context& ctx); 70   explicit timer(capy::execution_context& ctx);
71   71  
72   /** Construct a timer with an initial absolute expiry time. 72   /** Construct a timer with an initial absolute expiry time.
73   73  
74   @param ctx The execution context that will own this timer. It 74   @param ctx The execution context that will own this timer. It
75   must be a corosio io_context; otherwise the constructor 75   must be a corosio io_context; otherwise the constructor
76   throws (a timer service is required). 76   throws (a timer service is required).
77   @param t The initial expiry time point. 77   @param t The initial expiry time point.
78   78  
79   @throws std::logic_error if @p ctx is not an io_context. 79   @throws std::logic_error if @p ctx is not an io_context.
80   */ 80   */
81   timer(capy::execution_context& ctx, time_point t); 81   timer(capy::execution_context& ctx, time_point t);
82   82  
83   /** Construct a timer with an initial relative expiry time. 83   /** Construct a timer with an initial relative expiry time.
84   84  
85   @param ctx The execution context that will own this timer. It 85   @param ctx The execution context that will own this timer. It
86   must be a corosio io_context; otherwise the constructor 86   must be a corosio io_context; otherwise the constructor
87   throws (a timer service is required). 87   throws (a timer service is required).
88   @param d The initial expiry duration relative to now. 88   @param d The initial expiry duration relative to now.
89   89  
90   @throws std::logic_error if @p ctx is not an io_context. 90   @throws std::logic_error if @p ctx is not an io_context.
91   */ 91   */
92   template<class Rep, class Period> 92   template<class Rep, class Period>
HITCBC 93   8 timer(capy::execution_context& ctx, std::chrono::duration<Rep, Period> d) 93   8 timer(capy::execution_context& ctx, std::chrono::duration<Rep, Period> d)
HITCBC 94   8 : timer(ctx) 94   8 : timer(ctx)
95   { 95   {
HITCBC 96   8 expires_after(d); 96   8 expires_after(d);
HITCBC 97   8 } 97   8 }
98   98  
99   /** Construct a timer from an executor. 99   /** Construct a timer from an executor.
100   100  
101   The timer is associated with the executor's context, which must 101   The timer is associated with the executor's context, which must
102   be a corosio io_context. 102   be a corosio io_context.
103   103  
104   @param ex The executor whose context will own this timer. 104   @param ex The executor whose context will own this timer.
105   105  
106   @throws std::logic_error if the executor's context is not an 106   @throws std::logic_error if the executor's context is not an
107   io_context. 107   io_context.
108   */ 108   */
109   template<class Ex> 109   template<class Ex>
110   requires(!std::same_as<std::remove_cvref_t<Ex>, timer>) && 110   requires(!std::same_as<std::remove_cvref_t<Ex>, timer>) &&
111   capy::Executor<Ex> 111   capy::Executor<Ex>
HITCBC 112   4 explicit timer(Ex const& ex) : timer(ex.context()) 112   4 explicit timer(Ex const& ex) : timer(ex.context())
113   { 113   {
HITCBC 114   2 } 114   2 }
115   115  
116   /** Construct a timer from an executor with an absolute expiry time. 116   /** Construct a timer from an executor with an absolute expiry time.
117   117  
118   @param ex The executor whose context will own this timer. 118   @param ex The executor whose context will own this timer.
119   @param t The initial expiry time point. 119   @param t The initial expiry time point.
120   120  
121   @throws std::logic_error if the executor's context is not an 121   @throws std::logic_error if the executor's context is not an
122   io_context. 122   io_context.
123   */ 123   */
124   template<class Ex> 124   template<class Ex>
125   requires capy::Executor<Ex> 125   requires capy::Executor<Ex>
HITCBC 126   2 timer(Ex const& ex, time_point t) : timer(ex.context(), t) 126   2 timer(Ex const& ex, time_point t) : timer(ex.context(), t)
127   { 127   {
HITCBC 128   2 } 128   2 }
129   129  
130   /** Construct a timer from an executor with a relative expiry time. 130   /** Construct a timer from an executor with a relative expiry time.
131   131  
132   @param ex The executor whose context will own this timer. 132   @param ex The executor whose context will own this timer.
133   @param d The initial expiry duration relative to now. 133   @param d The initial expiry duration relative to now.
134   134  
135   @throws std::logic_error if the executor's context is not an 135   @throws std::logic_error if the executor's context is not an
136   io_context. 136   io_context.
137   */ 137   */
138   template<class Ex, class Rep, class Period> 138   template<class Ex, class Rep, class Period>
139   requires capy::Executor<Ex> 139   requires capy::Executor<Ex>
HITCBC 140   2 timer(Ex const& ex, std::chrono::duration<Rep, Period> d) 140   2 timer(Ex const& ex, std::chrono::duration<Rep, Period> d)
HITCBC 141   2 : timer(ex.context(), d) 141   2 : timer(ex.context(), d)
142   { 142   {
HITCBC 143   2 } 143   2 }
144   144  
145   /** Move constructor. 145   /** Move constructor.
146   146  
147   Transfers ownership of the timer resources. 147   Transfers ownership of the timer resources.
148   148  
149   @param other The timer to move from. 149   @param other The timer to move from.
150   150  
151   @pre No awaitables returned by @p other's methods exist. 151   @pre No awaitables returned by @p other's methods exist.
152   @pre The execution context associated with @p other must 152   @pre The execution context associated with @p other must
153   outlive this timer. 153   outlive this timer.
154   */ 154   */
155   timer(timer&& other) noexcept; 155   timer(timer&& other) noexcept;
156   156  
157   /** Move assignment operator. 157   /** Move assignment operator.
158   158  
159   Closes any existing timer and transfers ownership. 159   Closes any existing timer and transfers ownership.
160   160  
161   @param other The timer to move from. 161   @param other The timer to move from.
162   162  
163   @pre No awaitables returned by either `*this` or @p other's 163   @pre No awaitables returned by either `*this` or @p other's
164   methods exist. 164   methods exist.
165   @pre The execution context associated with @p other must 165   @pre The execution context associated with @p other must
166   outlive this timer. 166   outlive this timer.
167   167  
168   @return Reference to this timer. 168   @return Reference to this timer.
169   */ 169   */
170   timer& operator=(timer&& other) noexcept; 170   timer& operator=(timer&& other) noexcept;
171   171  
172   timer(timer const&) = delete; 172   timer(timer const&) = delete;
173   timer& operator=(timer const&) = delete; 173   timer& operator=(timer const&) = delete;
174   174  
175   /** Cancel one pending asynchronous wait operation. 175   /** Cancel one pending asynchronous wait operation.
176   176  
177   The oldest pending wait is cancelled (FIFO order). It 177   The oldest pending wait is cancelled (FIFO order). It
178   completes with an error code that compares equal to 178   completes with an error code that compares equal to
179   `capy::cond::canceled`. 179   `capy::cond::canceled`.
180   180  
181   @return The number of operations that were cancelled (0 or 1). 181   @return The number of operations that were cancelled (0 or 1).
182   */ 182   */
HITCBC 183   4 std::size_t cancel_one() 183   4 std::size_t cancel_one()
184   { 184   {
HITCBC 185   4 if (!get().might_have_pending_waits_) 185   4 if (!get().might_have_pending_waits_)
HITCBC 186   2 return 0; 186   2 return 0;
HITCBC 187   2 return do_cancel_one(); 187   2 return do_cancel_one();
188   } 188   }
189   189  
190   /** Set the timer's expiry time as an absolute time. 190   /** Set the timer's expiry time as an absolute time.
191   191  
192   Any pending asynchronous wait operations will be cancelled. 192   Any pending asynchronous wait operations will be cancelled.
193   193  
194   @param t The expiry time to be used for the timer. 194   @param t The expiry time to be used for the timer.
195   195  
196   @return The number of pending operations that were cancelled. 196   @return The number of pending operations that were cancelled.
197   */ 197   */
HITCBC 198   54 std::size_t expires_at(time_point t) 198   54 std::size_t expires_at(time_point t)
199   { 199   {
HITCBC 200   54 auto& impl = get(); 200   54 auto& impl = get();
HITCBC 201   54 impl.expiry_ = t; 201   54 impl.expiry_ = t;
HITCBC 202   54 if (impl.heap_index_ == implementation::npos && 202   54 if (impl.heap_index_ == implementation::npos &&
HITCBC 203   50 !impl.might_have_pending_waits_) 203   50 !impl.might_have_pending_waits_)
HITCBC 204   50 return 0; 204   50 return 0;
HITCBC 205   4 return do_update_expiry(); 205   4 return do_update_expiry();
206   } 206   }
207   207  
208   /** Set the timer's expiry time relative to now. 208   /** Set the timer's expiry time relative to now.
209   209  
210   Any pending asynchronous wait operations will be cancelled. 210   Any pending asynchronous wait operations will be cancelled.
211   211  
212   @param d The expiry time relative to now. 212   @param d The expiry time relative to now.
213   213  
214   @return The number of pending operations that were cancelled. 214   @return The number of pending operations that were cancelled.
215   */ 215   */
HITCBC 216   8034 std::size_t expires_after(duration d) 216   8432 std::size_t expires_after(duration d)
217   { 217   {
HITCBC 218   8034 auto& impl = get(); 218   8432 auto& impl = get();
HITCBC 219   8034 if (d <= duration::zero()) 219   8432 if (d <= duration::zero())
HITCBC 220   6 impl.expiry_ = (time_point::min)(); 220   6 impl.expiry_ = (time_point::min)();
221   else 221   else
HITCBC 222   8028 impl.expiry_ = clock_type::now() + d; 222   8426 impl.expiry_ = clock_type::now() + d;
HITCBC 223   8034 if (impl.heap_index_ == implementation::npos && 223   8432 if (impl.heap_index_ == implementation::npos &&
HITCBC 224   8030 !impl.might_have_pending_waits_) 224   8428 !impl.might_have_pending_waits_)
HITCBC 225   8030 return 0; 225   8428 return 0;
HITCBC 226   4 return do_update_expiry(); 226   4 return do_update_expiry();
227   } 227   }
228   228  
229   /** Set the timer's expiry time relative to now. 229   /** Set the timer's expiry time relative to now.
230   230  
231   This is a convenience overload that accepts any duration type 231   This is a convenience overload that accepts any duration type
232   and converts it to the timer's native duration type. Any 232   and converts it to the timer's native duration type. Any
233   pending asynchronous wait operations will be cancelled. 233   pending asynchronous wait operations will be cancelled.
234   234  
235   @param d The expiry time relative to now. 235   @param d The expiry time relative to now.
236   236  
237   @return The number of pending operations that were cancelled. 237   @return The number of pending operations that were cancelled.
238   */ 238   */
239   template<class Rep, class Period> 239   template<class Rep, class Period>
HITCBC 240   8034 std::size_t expires_after(std::chrono::duration<Rep, Period> d) 240   8432 std::size_t expires_after(std::chrono::duration<Rep, Period> d)
241   { 241   {
HITCBC 242   8034 return expires_after(std::chrono::duration_cast<duration>(d)); 242   8432 return expires_after(std::chrono::duration_cast<duration>(d));
243   } 243   }
244   244  
245   protected: 245   protected:
246   explicit timer(handle h) noexcept : io_timer(std::move(h)) {} 246   explicit timer(handle h) noexcept : io_timer(std::move(h)) {}
247   247  
248   private: 248   private:
249   std::size_t do_cancel() override; 249   std::size_t do_cancel() override;
250   std::size_t do_cancel_one(); 250   std::size_t do_cancel_one();
251   std::size_t do_update_expiry(); 251   std::size_t do_update_expiry();
252   252  
253   /// Return the underlying implementation. 253   /// Return the underlying implementation.
HITCBC 254   8110 implementation& get() const noexcept 254   8508 implementation& get() const noexcept
255   { 255   {
HITCBC 256   8110 return *static_cast<implementation*>(h_.get()); 256   8508 return *static_cast<implementation*>(h_.get());
257   } 257   }
258   }; 258   };
259   259  
260   } // namespace boost::corosio 260   } // namespace boost::corosio
261   261  
262   #endif 262   #endif