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