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