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   Wraps platform timer facilities via the io_context reactor. 45   Wraps platform timer facilities via the io_context reactor.
46   Operations dispatch to OS timer APIs (timerfd, IOCP timers, 46   Operations dispatch to OS timer APIs (timerfd, IOCP timers,
47   kqueue EVFILT_TIMER). 47   kqueue EVFILT_TIMER).
48   */ 48   */
49   class BOOST_COROSIO_DECL timer : public io_timer 49   class BOOST_COROSIO_DECL timer : public io_timer
50   { 50   {
51   public: 51   public:
52   /// Alias for backward compatibility. 52   /// Alias for backward compatibility.
53   using implementation = io_timer::implementation; 53   using implementation = io_timer::implementation;
54   54  
55   /** Destructor. 55   /** Destructor.
56   56  
57   Cancels any pending operations and releases timer resources. 57   Cancels any pending operations and releases timer resources.
58   */ 58   */
59   ~timer() override; 59   ~timer() override;
60   60  
61   /** Construct a timer from an execution context. 61   /** Construct a timer from an execution context.
62   62  
63   @param ctx The execution context that will own this timer. It 63   @param ctx The execution context that will own this timer. It
64   must be a corosio io_context; otherwise the constructor 64   must be a corosio io_context; otherwise the constructor
65   throws (a timer service is required). 65   throws (a timer service is required).
66   66  
67   @throws std::logic_error if @p ctx is not an io_context. 67   @throws std::logic_error if @p ctx is not an io_context.
68   */ 68   */
69   explicit timer(capy::execution_context& ctx); 69   explicit timer(capy::execution_context& ctx);
70   70  
71   /** Construct a timer with an initial absolute expiry time. 71   /** Construct a timer with an initial absolute expiry time.
72   72  
73   @param ctx The execution context that will own this timer. It 73   @param ctx The execution context that will own this timer. It
74   must be a corosio io_context; otherwise the constructor 74   must be a corosio io_context; otherwise the constructor
75   throws (a timer service is required). 75   throws (a timer service is required).
76   @param t The initial expiry time point. 76   @param t The initial expiry time point.
77   77  
78   @throws std::logic_error if @p ctx is not an io_context. 78   @throws std::logic_error if @p ctx is not an io_context.
79   */ 79   */
80   timer(capy::execution_context& ctx, time_point t); 80   timer(capy::execution_context& ctx, time_point t);
81   81  
82   /** Construct a timer with an initial relative expiry time. 82   /** Construct a timer with an initial relative expiry time.
83   83  
84   @param ctx The execution context that will own this timer. It 84   @param ctx The execution context that will own this timer. It
85   must be a corosio io_context; otherwise the constructor 85   must be a corosio io_context; otherwise the constructor
86   throws (a timer service is required). 86   throws (a timer service is required).
87   @param d The initial expiry duration relative to now. 87   @param d The initial expiry duration relative to now.
88   88  
89   @throws std::logic_error if @p ctx is not an io_context. 89   @throws std::logic_error if @p ctx is not an io_context.
90   */ 90   */
91   template<class Rep, class Period> 91   template<class Rep, class Period>
HITCBC 92   8 timer(capy::execution_context& ctx, std::chrono::duration<Rep, Period> d) 92   8 timer(capy::execution_context& ctx, std::chrono::duration<Rep, Period> d)
HITCBC 93   8 : timer(ctx) 93   8 : timer(ctx)
94   { 94   {
HITCBC 95   8 expires_after(d); 95   8 expires_after(d);
HITCBC 96   8 } 96   8 }
97   97  
98   /** Construct a timer from an executor. 98   /** Construct a timer from an executor.
99   99  
100   The timer is associated with the executor's context, which must 100   The timer is associated with the executor's context, which must
101   be a corosio io_context. 101   be a corosio io_context.
102   102  
103   @param ex The executor whose context will own this timer. 103   @param ex The executor whose context will own this timer.
104   104  
105   @throws std::logic_error if the executor's context is not an 105   @throws std::logic_error if the executor's context is not an
106   io_context. 106   io_context.
107   */ 107   */
108   template<class Ex> 108   template<class Ex>
109   requires(!std::same_as<std::remove_cvref_t<Ex>, timer>) && 109   requires(!std::same_as<std::remove_cvref_t<Ex>, timer>) &&
110   capy::Executor<Ex> 110   capy::Executor<Ex>
HITCBC 111   4 explicit timer(Ex const& ex) : timer(ex.context()) 111   4 explicit timer(Ex const& ex) : timer(ex.context())
112   { 112   {
HITCBC 113   2 } 113   2 }
114   114  
115   /** Construct a timer from an executor with an absolute expiry time. 115   /** Construct a timer from an executor with an absolute expiry time.
116   116  
117   @param ex The executor whose context will own this timer. 117   @param ex The executor whose context will own this timer.
118   @param t The initial expiry time point. 118   @param t The initial expiry time point.
119   119  
120   @throws std::logic_error if the executor's context is not an 120   @throws std::logic_error if the executor's context is not an
121   io_context. 121   io_context.
122   */ 122   */
123   template<class Ex> 123   template<class Ex>
124   requires capy::Executor<Ex> 124   requires capy::Executor<Ex>
HITCBC 125   2 timer(Ex const& ex, time_point t) : timer(ex.context(), t) 125   2 timer(Ex const& ex, time_point t) : timer(ex.context(), t)
126   { 126   {
HITCBC 127   2 } 127   2 }
128   128  
129   /** Construct a timer from an executor with a relative expiry time. 129   /** Construct a timer from an executor with a relative expiry time.
130   130  
131   @param ex The executor whose context will own this timer. 131   @param ex The executor whose context will own this timer.
132   @param d The initial expiry duration relative to now. 132   @param d The initial expiry duration relative to now.
133   133  
134   @throws std::logic_error if the executor's context is not an 134   @throws std::logic_error if the executor's context is not an
135   io_context. 135   io_context.
136   */ 136   */
137   template<class Ex, class Rep, class Period> 137   template<class Ex, class Rep, class Period>
138   requires capy::Executor<Ex> 138   requires capy::Executor<Ex>
HITCBC 139   2 timer(Ex const& ex, std::chrono::duration<Rep, Period> d) 139   2 timer(Ex const& ex, std::chrono::duration<Rep, Period> d)
HITCBC 140   2 : timer(ex.context(), d) 140   2 : timer(ex.context(), d)
141   { 141   {
HITCBC 142   2 } 142   2 }
143   143  
144   /** Move constructor. 144   /** Move constructor.
145   145  
146   Transfers ownership of the timer resources. 146   Transfers ownership of the timer resources.
147   147  
148   @param other The timer to move from. 148   @param other The timer to move from.
149   149  
150   @pre No awaitables returned by @p other's methods exist. 150   @pre No awaitables returned by @p other's methods exist.
151   @pre The execution context associated with @p other must 151   @pre The execution context associated with @p other must
152   outlive this timer. 152   outlive this timer.
153   */ 153   */
154   timer(timer&& other) noexcept; 154   timer(timer&& other) noexcept;
155   155  
156   /** Move assignment operator. 156   /** Move assignment operator.
157   157  
158   Closes any existing timer and transfers ownership. 158   Closes any existing timer and transfers ownership.
159   159  
160   @param other The timer to move from. 160   @param other The timer to move from.
161   161  
162   @pre No awaitables returned by either `*this` or @p other's 162   @pre No awaitables returned by either `*this` or @p other's
163   methods exist. 163   methods exist.
164   @pre The execution context associated with @p other must 164   @pre The execution context associated with @p other must
165   outlive this timer. 165   outlive this timer.
166   166  
167   @return Reference to this timer. 167   @return Reference to this timer.
168   */ 168   */
169   timer& operator=(timer&& other) noexcept; 169   timer& operator=(timer&& other) noexcept;
170   170  
171   timer(timer const&) = delete; 171   timer(timer const&) = delete;
172   timer& operator=(timer const&) = delete; 172   timer& operator=(timer const&) = delete;
173   173  
174   /** Cancel one pending asynchronous wait operation. 174   /** Cancel one pending asynchronous wait operation.
175   175  
176   The oldest pending wait is cancelled (FIFO order). It 176   The oldest pending wait is cancelled (FIFO order). It
177   completes with an error code that compares equal to 177   completes with an error code that compares equal to
178   `capy::cond::canceled`. 178   `capy::cond::canceled`.
179   179  
180   @return The number of operations that were cancelled (0 or 1). 180   @return The number of operations that were cancelled (0 or 1).
181   */ 181   */
HITCBC 182   4 std::size_t cancel_one() 182   4 std::size_t cancel_one()
183   { 183   {
HITCBC 184   4 if (!get().might_have_pending_waits_) 184   4 if (!get().might_have_pending_waits_)
HITCBC 185   2 return 0; 185   2 return 0;
HITCBC 186   2 return do_cancel_one(); 186   2 return do_cancel_one();
187   } 187   }
188   188  
189   /** Set the timer's expiry time as an absolute time. 189   /** Set the timer's expiry time as an absolute time.
190   190  
191   Any pending asynchronous wait operations will be cancelled. 191   Any pending asynchronous wait operations will be cancelled.
192   192  
193   @param t The expiry time to be used for the timer. 193   @param t The expiry time to be used for the timer.
194   194  
195   @return The number of pending operations that were cancelled. 195   @return The number of pending operations that were cancelled.
196   */ 196   */
HITCBC 197   54 std::size_t expires_at(time_point t) 197   54 std::size_t expires_at(time_point t)
198   { 198   {
HITCBC 199   54 auto& impl = get(); 199   54 auto& impl = get();
HITCBC 200   54 impl.expiry_ = t; 200   54 impl.expiry_ = t;
HITCBC 201   54 if (impl.heap_index_ == implementation::npos && 201   54 if (impl.heap_index_ == implementation::npos &&
HITCBC 202   50 !impl.might_have_pending_waits_) 202   50 !impl.might_have_pending_waits_)
HITCBC 203   50 return 0; 203   50 return 0;
HITCBC 204   4 return do_update_expiry(); 204   4 return do_update_expiry();
205   } 205   }
206   206  
207   /** Set the timer's expiry time relative to now. 207   /** Set the timer's expiry time relative to now.
208   208  
209   Any pending asynchronous wait operations will be cancelled. 209   Any pending asynchronous wait operations will be cancelled.
210   210  
211   @param d The expiry time relative to now. 211   @param d The expiry time relative to now.
212   212  
213   @return The number of pending operations that were cancelled. 213   @return The number of pending operations that were cancelled.
214   */ 214   */
HITCBC 215   5363 std::size_t expires_after(duration d) 215   3704 std::size_t expires_after(duration d)
216   { 216   {
HITCBC 217   5363 auto& impl = get(); 217   3704 auto& impl = get();
HITCBC 218   5363 if (d <= duration::zero()) 218   3704 if (d <= duration::zero())
HITCBC 219   6 impl.expiry_ = (time_point::min)(); 219   6 impl.expiry_ = (time_point::min)();
220   else 220   else
HITCBC 221   5357 impl.expiry_ = clock_type::now() + d; 221   3698 impl.expiry_ = clock_type::now() + d;
HITCBC 222   5363 if (impl.heap_index_ == implementation::npos && 222   3704 if (impl.heap_index_ == implementation::npos &&
HITCBC 223   5359 !impl.might_have_pending_waits_) 223   3700 !impl.might_have_pending_waits_)
HITCBC 224   5359 return 0; 224   3700 return 0;
HITCBC 225   4 return do_update_expiry(); 225   4 return do_update_expiry();
226   } 226   }
227   227  
228   /** Set the timer's expiry time relative to now. 228   /** Set the timer's expiry time relative to now.
229   229  
230   This is a convenience overload that accepts any duration type 230   This is a convenience overload that accepts any duration type
231   and converts it to the timer's native duration type. Any 231   and converts it to the timer's native duration type. Any
232   pending asynchronous wait operations will be cancelled. 232   pending asynchronous wait operations will be cancelled.
233   233  
234   @param d The expiry time relative to now. 234   @param d The expiry time relative to now.
235   235  
236   @return The number of pending operations that were cancelled. 236   @return The number of pending operations that were cancelled.
237   */ 237   */
238   template<class Rep, class Period> 238   template<class Rep, class Period>
HITCBC 239   5363 std::size_t expires_after(std::chrono::duration<Rep, Period> d) 239   3704 std::size_t expires_after(std::chrono::duration<Rep, Period> d)
240   { 240   {
HITCBC 241   5363 return expires_after(std::chrono::duration_cast<duration>(d)); 241   3704 return expires_after(std::chrono::duration_cast<duration>(d));
242   } 242   }
243   243  
244   protected: 244   protected:
245   explicit timer(handle h) noexcept : io_timer(std::move(h)) {} 245   explicit timer(handle h) noexcept : io_timer(std::move(h)) {}
246   246  
247   private: 247   private:
248   std::size_t do_cancel() override; 248   std::size_t do_cancel() override;
249   std::size_t do_cancel_one(); 249   std::size_t do_cancel_one();
250   std::size_t do_update_expiry(); 250   std::size_t do_update_expiry();
251   251  
252   /// Return the underlying implementation. 252   /// Return the underlying implementation.
HITCBC 253   5439 implementation& get() const noexcept 253   3780 implementation& get() const noexcept
254   { 254   {
HITCBC 255   5439 return *static_cast<implementation*>(h_.get()); 255   3780 return *static_cast<implementation*>(h_.get());
256   } 256   }
257   }; 257   };
258   258  
259   } // namespace boost::corosio 259   } // namespace boost::corosio
260   260  
261   #endif 261   #endif