VLink  2.0.0
A high-performance communication middleware
timer.h
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2026 by Thun Lu. All rights reserved.
3  * Author: Thun Lu <thun.lu@zohomail.cn>
4  * Repo: https://github.com/thun-res/vlink
5  * _ __ __ _ __
6  * | | / / / / (_) ____ / /__
7  * | | / / / / / / / __ \ / //_/
8  * | |/ / / /___ / / / / / / / ,<
9  * |___/ /_____/ /_/ /_/ /_/ /_/|_|
10  *
11  * Licensed under the Apache License, Version 2.0 (the "License");
12  * you may not use this file except in compliance with the License.
13  * You may obtain a copy of the License at
14  *
15  * http://www.apache.org/licenses/LICENSE-2.0
16  *
17  * Unless required by applicable law or agreed to in writing, software
18  * distributed under the License is distributed on an "AS IS" BASIS,
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20  * See the License for the specific language governing permissions and
21  * limitations under the License.
22  */
23 
24 /**
25  * @file timer.h
26  * @brief MessageLoop-driven periodic or one-shot timer with priority support.
27  *
28  * @details
29  * @c vlink::Timer dispatches its callback on a @c MessageLoop thread, so the callback
30  * runs serially with every other task posted to the same loop and no additional
31  * synchronisation is required inside the callback body.
32  *
33  * Timer modes determined by @c loop_count:
34  *
35  * | Mode | @c loop_count value | Behaviour |
36  * | --------- | ------------------- | -------------------------------------------- |
37  * | One-shot | @c 1 | Fires exactly once after one full interval |
38  * | Counted | @c 2 ... @c INT_MAX | Fires the configured number of times |
39  * | Repeating | @c kInfinite (-1) | Fires forever until @c stop() is called |
40  *
41  * Key features:
42  * - Strict mode: when the loop falls behind, missed ticks are dispatched immediately on
43  * the next iteration so the long-term cadence is preserved.
44  * - Configurable dispatch priority for use with @c kPriorityType message loops.
45  * - When @c interval_ms is @c 0 the internal interval clamps to @c kMinInterval
46  * (10000 ns = 10 us) to avoid pathological busy-spinning.
47  * - The static @c call_once() helper posts a fire-and-forget one-shot without managing a
48  * @c Timer object.
49  *
50  * @par Lifecycle
51  * Construct -> @c attach() (if not done at construction) -> @c start() -> repeated ticks
52  * -> @c stop() / @c restart() / destruction.
53  *
54  * @par Example
55  * @code
56  * vlink::MessageLoop loop;
57  * vlink::Timer timer(&loop, 500, vlink::Timer::kInfinite, [] {
58  * handle_tick();
59  * });
60  * timer.start();
61  * loop.run();
62  * @endcode
63  */
64 
65 #pragma once
66 
67 #include <atomic>
68 #include <cstdint>
69 #include <memory>
70 
71 #include "./functional.h"
72 #include "./macros.h"
73 
74 namespace vlink {
75 
76 /**
77  * @class Timer
78  * @brief Periodic or one-shot timer that fires on an associated @c MessageLoop thread.
79  *
80  * @details
81  * Owns an internal implementation that integrates with the loop's tick scheduling so the
82  * callback runs in-line with every other task posted to the loop.
83  */
84 class VLINK_EXPORT Timer final {
85  public:
86  /**
87  * @brief Callback signature invoked on every tick.
88  */
89  using Callback = MoveFunction<void()>;
90 
91  /**
92  * @brief Sentinel @c loop_count value meaning fire forever.
93  */
94  static constexpr int kInfinite{-1};
95 
96  /**
97  * @brief Constructs a detached timer with a 1000 ms interval and no callback.
98  *
99  * @details
100  * Use @c attach() and @c set_callback() before @c start().
101  */
102  explicit Timer();
103 
104  /**
105  * @brief Constructs a timer attached to @p message_loop with the default interval and no callback.
106  *
107  * @param message_loop Loop that will dispatch ticks.
108  */
109  explicit Timer(class MessageLoop* message_loop);
110 
111  /**
112  * @brief Constructs a fully configured timer attached to a loop.
113  *
114  * @param message_loop Loop that will dispatch ticks.
115  * @param interval_ms Tick interval in milliseconds; @c 0 clamps to @c kMinInterval.
116  * @param loop_count Number of ticks to fire; @c kInfinite repeats forever. Default: @c kInfinite.
117  * @param callback Callback to install. Default: empty.
118  */
119  explicit Timer(class MessageLoop* message_loop, uint32_t interval_ms, int32_t loop_count = kInfinite,
120  Callback&& callback = nullptr);
121 
122  /**
123  * @brief Constructs a detached timer with explicit interval and loop count.
124  *
125  * @param interval_ms Tick interval in milliseconds; @c 0 clamps to @c kMinInterval.
126  * @param loop_count Number of ticks to fire; @c kInfinite repeats forever. Default: @c kInfinite.
127  * @param callback Callback to install. Default: empty.
128  */
129  explicit Timer(uint32_t interval_ms, int32_t loop_count = kInfinite, Callback&& callback = nullptr);
130 
131  /**
132  * @brief Destructor. Calls @c stop() and detaches the timer.
133  */
135 
136  /**
137  * @brief Posts a fire-and-forget one-shot timer to a loop.
138  *
139  * @details
140  * The timer fires exactly once @p interval_ms milliseconds later and is then destroyed
141  * automatically. Convenient when lifetime management of a long-lived @c Timer object
142  * would be inconvenient.
143  *
144  * @param message_loop Loop that will dispatch the tick.
145  * @param interval_ms Delay in milliseconds.
146  * @param callback Callback to invoke.
147  * @param priority Dispatch priority; @c 0 keeps the default. Default: @c 0.
148  * @return @c true on success.
149  */
150  static bool call_once(class MessageLoop* message_loop, uint32_t interval_ms, Callback&& callback,
151  uint16_t priority = 0);
152 
153  /**
154  * @brief Reports whether the timer is currently armed.
155  *
156  * @return @c true when the timer is running.
157  */
158  [[nodiscard]] bool is_active() const;
159 
160  /**
161  * @brief Reports whether strict catch-up mode is enabled.
162  *
163  * @return @c true when missed ticks are fired back-to-back.
164  */
165  [[nodiscard]] bool is_strict() const;
166 
167  /**
168  * @brief Returns the configured tick interval in milliseconds.
169  *
170  * @return Interval.
171  */
172  [[nodiscard]] uint32_t get_interval() const;
173 
174  /**
175  * @brief Returns the configured total tick count.
176  *
177  * @return Loop count; @c kInfinite for indefinite repetition.
178  */
179  [[nodiscard]] int32_t get_loop_count() const;
180 
181  /**
182  * @brief Returns the number of ticks remaining before automatic stop.
183  *
184  * @return Remaining tick count; @c kInfinite when repeating forever.
185  */
186  [[nodiscard]] int32_t get_remain_loop_count() const;
187 
188  /**
189  * @brief Returns the number of ticks dispatched since the last @c start() or @c restart().
190  *
191  * @return Dispatched tick count.
192  */
193  [[nodiscard]] uint64_t get_invoke_count() const;
194 
195  /**
196  * @brief Returns the dispatch priority used with @c kPriorityType loops.
197  *
198  * @return Priority value.
199  */
200  [[nodiscard]] uint16_t get_priority() const;
201 
202  /**
203  * @brief Returns the loop the timer is currently attached to.
204  *
205  * @return Loop pointer; @c nullptr when detached.
206  */
207  [[nodiscard]] class MessageLoop* get_message_loop() const;
208 
209  /**
210  * @brief Attaches the timer to @p message_loop.
211  *
212  * @details
213  * Must be called before @c start() when no loop was provided at construction. A null
214  * pointer logs a fatal error and throws. Re-attaching detaches from any previous loop.
215  *
216  * @param message_loop Loop to attach to.
217  * @return @c true on success.
218  */
219  bool attach(class MessageLoop* message_loop);
220 
221  /**
222  * @brief Detaches the timer from its loop and stops it first.
223  *
224  * @return @c true on success.
225  */
226  bool detach();
227 
228  /**
229  * @brief Arms the timer and starts dispatching ticks.
230  *
231  * @details
232  * When @p callback is non-null it replaces the existing callback. The first tick fires
233  * after one full interval, not immediately. Starting a timer with no callback is a
234  * no-op for dispatch purposes.
235  *
236  * @param callback Optional callback replacement. Default: keep the existing callback.
237  */
238  void start(Callback&& callback = nullptr);
239 
240  /**
241  * @brief Resets the tick count and re-arms the timer as if newly started.
242  */
243  void restart();
244 
245  /**
246  * @brief Disarms the timer without destroying it.
247  *
248  * @details
249  * After @c stop() the timer can be re-armed by another @c start().
250  */
251  void stop();
252 
253  /**
254  * @brief Enables or disables strict catch-up firing.
255  *
256  * @param strict @c true to fire missed ticks back-to-back. Default state is @c false.
257  */
258  void set_strict(bool strict);
259 
260  /**
261  * @brief Updates the tick interval.
262  *
263  * @details
264  * Takes effect immediately for an active timer: the processed tick count is recomputed
265  * against the existing start time and the loop is woken. @p interval_ms @c == @c 0
266  * clamps the internal interval to @c kMinInterval (10000 ns = 10 us).
267  *
268  * @param interval_ms New interval in milliseconds.
269  */
270  void set_interval(uint32_t interval_ms);
271 
272  /**
273  * @brief Updates the total tick count.
274  *
275  * @param loop_count New loop count; @c kInfinite for indefinite repetition.
276  */
277  void set_loop_count(int32_t loop_count);
278 
279  /**
280  * @brief Replaces the callback dispatched on every tick.
281  *
282  * @param callback New callback.
283  */
284  void set_callback(Callback&& callback);
285 
286  /**
287  * @brief Updates the dispatch priority.
288  *
289  * @details
290  * Only effective when the associated loop is of type @c kPriorityType.
291  *
292  * @param priority Priority value; higher fires sooner.
293  */
294  void set_priority(uint16_t priority);
295 
296  private:
297  void run_callback();
298 
299  void begin_in_flight();
300 
301  void end_in_flight();
302 
303  void wait_for_idle();
304 
305  void clear();
306 
307  void force_to_start();
308 
309  void set_remain_loop_count(int32_t loop_count) const;
310 
311  void sub_remain_loop_count() const;
312 
313  void set_invoke_count(uint64_t invoke_count) const;
314 
315  uint64_t get_start_time() const;
316 
317  bool is_once_type() const;
318 
319  bool has_callback() const;
320 
321  Callback take_callback();
322 
323  std::shared_ptr<std::atomic_bool> get_alive_flag() const;
324 
325  friend class MessageLoop;
326  struct Impl;
327  std::unique_ptr<Impl> impl_;
328  static constexpr uint32_t kMinInterval{10'000U};
329 
331 };
332 
333 } // namespace vlink
Pool-backed type-erased callables: copyable vlink::Function and move-only vlink::MoveFunction.
Cross-platform macros for visibility, branch hints, copy prevention, singletons and string helpers.
#define VLINK_EXPORT
Definition: macros.h:81
#define VLINK_DISALLOW_COPY_AND_ASSIGN(classname)
Deletes the copy constructor and copy-assignment operator of classname.
Definition: macros.h:174