VLink 2.0.0
A high-performance communication middleware
Loading...
Searching...
No Matches
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 Event-loop-driven periodic/one-shot timer with configurable priority.
27 *
28 * @details
29 * @c Timer integrates with a @c MessageLoop to deliver callbacks on its thread.
30 * Unlike platform timers, the callback runs serially with all other tasks posted to
31 * the same loop, so no extra synchronisation is needed inside the callback.
32 *
33 * Key features:
34 * - Repeating or one-shot mode (@c kInfinite for loop_count means repeat forever).
35 * - Optional strict mode: if the loop is busy and the next tick is missed, strict mode
36 * fires the missed callbacks immediately to maintain the schedule.
37 * - Priority support for @c kPriorityType message loops.
38 * - Minimum timer resolution is @c kMinInterval (10000) to prevent busy-wait overhead.
39 * - A detached @c Timer does not fire until attached to a loop via @c attach().
40 *
41 * Lifecycle:
42 * -# Construct a @c Timer (with or without a @c MessageLoop).
43 * -# Optionally call @c attach() to bind to a loop.
44 * -# Call @c start() to arm the timer.
45 * -# Call @c stop() to disarm; @c restart() to reset the countdown.
46 * -# Destruction automatically calls @c stop() and @c detach().
47 *
48 * @note
49 * - When @c interval_ms is zero, the interval falls back to @c kMinInterval (10000).
50 * - A timer is automatically stopped when its @c MessageLoop is destroyed.
51 * - @c call_once() is a convenience factory for fire-and-forget one-shot timers.
52 *
53 * @par Example
54 * @code
55 * vlink::MessageLoop loop;
56 * vlink::Timer timer(&loop, 500, vlink::Timer::kInfinite, []() {
57 * // called every 500 ms on the loop thread
58 * });
59 * timer.start();
60 * loop.run();
61 * @endcode
62 */
63
64#pragma once
65
66#include <cstdint>
67#include <functional>
68#include <memory>
69
70#include "./macros.h"
71
72namespace vlink {
73
74/**
75 * @class Timer
76 * @brief Event-loop-driven repeating or one-shot timer.
77 *
78 * @details
79 * Callbacks are dispatched on the thread of the associated @c MessageLoop.
80 */
81class VLINK_EXPORT Timer final {
82 public:
83 /**
84 * @brief Callback type invoked on each timer tick.
85 */
86 using Callback = std::function<void()>;
87
88 /**
89 * @brief Sentinel loop count meaning repeat indefinitely.
90 */
91 static constexpr int kInfinite{-1};
92
93 /**
94 * @brief Constructs a detached timer with no message loop, interval or callback.
95 *
96 * @details
97 * Must be configured with @c attach(), @c set_interval() and @c set_callback()
98 * before @c start() can be called.
99 */
100 explicit Timer();
101
102 /**
103 * @brief Constructs a timer attached to @p message_loop with no interval or callback yet.
104 *
105 * @param message_loop The @c MessageLoop to deliver callbacks on.
106 */
107 explicit Timer(class MessageLoop* message_loop);
108
109 /**
110 * @brief Constructs and fully configures a timer attached to a loop.
111 *
112 * @param message_loop The @c MessageLoop to deliver callbacks on.
113 * @param interval_ms Tick interval in milliseconds. When zero, falls back to @c kMinInterval (10000).
114 * @param loop_count Number of times to fire (@c kInfinite for indefinite repeat). Default: @c kInfinite.
115 * @param callback Callback to invoke on each tick. Default: nullptr.
116 */
117 explicit Timer(class MessageLoop* message_loop, uint32_t interval_ms, int32_t loop_count = kInfinite,
118 Callback&& callback = nullptr);
119
120 /**
121 * @brief Constructs a timer without attaching it to a loop.
122 *
123 * @details
124 * Call @c attach() before @c start().
125 *
126 * @param interval_ms Tick interval in milliseconds.
127 * @param loop_count Number of times to fire. Default: @c kInfinite.
128 * @param callback Callback to invoke on each tick. Default: nullptr.
129 */
130 explicit Timer(uint32_t interval_ms, int32_t loop_count = kInfinite, Callback&& callback = nullptr);
131
132 /**
133 * @brief Destructor. Stops the timer and detaches it from the loop.
134 */
136
137 /**
138 * @brief Posts a one-shot timer to a @c MessageLoop without creating a @c Timer object.
139 *
140 * @details
141 * The timer fires exactly once after @p interval_ms milliseconds and is then destroyed.
142 * Useful for simple delayed tasks where lifetime management of a @c Timer object is inconvenient.
143 *
144 * @param message_loop The @c MessageLoop to deliver the callback on.
145 * @param interval_ms Delay in milliseconds.
146 * @param callback Callback to invoke once.
147 * @param priority Priority of the timer task (default: 0).
148 * @return @c true if the timer was successfully posted.
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 Returns @c true if the timer is currently running (armed and has remaining ticks).
155 *
156 * @return @c true if the timer is active.
157 */
158 [[nodiscard]] bool is_active() const;
159
160 /**
161 * @brief Returns @c true if strict mode is enabled.
162 *
163 * @details
164 * In strict mode, missed ticks (due to a busy loop) are fired immediately on the
165 * next loop iteration to compensate for schedule drift.
166 *
167 * @return @c true if strict mode is on.
168 */
169 [[nodiscard]] bool is_strict() const;
170
171 /**
172 * @brief Returns the current tick interval in milliseconds.
173 *
174 * @return Interval in milliseconds.
175 */
176 [[nodiscard]] uint32_t get_interval() const;
177
178 /**
179 * @brief Returns the configured total loop count.
180 *
181 * @return Loop count, or @c kInfinite (-1) for indefinite repeat.
182 */
183 [[nodiscard]] int32_t get_loop_count() const;
184
185 /**
186 * @brief Returns the number of remaining ticks before the timer stops automatically.
187 *
188 * @return Remaining tick count, or @c kInfinite if repeating indefinitely.
189 */
190 [[nodiscard]] int32_t get_remain_loop_count() const;
191
192 /**
193 * @brief Returns the total number of times the callback has been invoked since @c start().
194 *
195 * @return Cumulative invocation count.
196 */
197 [[nodiscard]] uint64_t get_invoke_count() const;
198
199 /**
200 * @brief Returns the task dispatch priority.
201 *
202 * @details
203 * Relevant only when the timer is attached to a @c kPriorityType @c MessageLoop.
204 *
205 * @return Priority value (higher = dispatched sooner).
206 */
207 [[nodiscard]] uint16_t get_priority() const;
208
209 /**
210 * @brief Returns the @c MessageLoop this timer is attached to.
211 *
212 * @return Pointer to the associated loop, or @c nullptr if detached.
213 */
214 [[nodiscard]] class MessageLoop* get_message_loop() const;
215
216 /**
217 * @brief Attaches the timer to a @c MessageLoop.
218 *
219 * @details
220 * Must be called before @c start() if the timer was constructed without a loop.
221 * If the timer is already attached to a different loop it is detached first.
222 *
223 * @param message_loop Loop to attach to.
224 * @return @c true on success.
225 */
226 bool attach(class MessageLoop* message_loop);
227
228 /**
229 * @brief Detaches the timer from its @c MessageLoop without stopping it.
230 *
231 * @details
232 * After detaching the timer stops firing. It can be re-attached to a different loop.
233 *
234 * @return @c true on success.
235 */
236 bool detach();
237
238 /**
239 * @brief Arms and starts the timer.
240 *
241 * @details
242 * If @p callback is provided it replaces the previously set callback.
243 * The first tick fires after one full interval.
244 *
245 * @param callback Optional replacement callback. Default: nullptr (keep existing).
246 */
247 void start(Callback&& callback = nullptr);
248
249 /**
250 * @brief Resets the countdown to zero and continues firing.
251 *
252 * @details
253 * Equivalent to @c stop() followed by @c start() but also resets @c get_invoke_count().
254 */
255 void restart();
256
257 /**
258 * @brief Disarms the timer without destroying it.
259 *
260 * @details
261 * After @c stop(), @c is_active() returns @c false. Call @c start() to re-arm.
262 */
263 void stop();
264
265 /**
266 * @brief Enables or disables strict (catch-up) firing mode.
267 *
268 * @details
269 * When @c true, if the loop was busy and one or more ticks were missed, they are
270 * fired immediately on the next loop iteration. Default is @c false.
271 *
272 * @param strict @c true to enable catch-up firing.
273 */
274 void set_strict(bool strict);
275
276 /**
277 * @brief Changes the tick interval.
278 *
279 * @details
280 * Takes effect on the next @c start() or @c restart() call.
281 * When set to zero, the interval falls back to @c kMinInterval (10000).
282 *
283 * @param interval_ms New interval in milliseconds.
284 */
285 void set_interval(uint32_t interval_ms);
286
287 /**
288 * @brief Changes the total number of ticks.
289 *
290 * @param loop_count New loop count. Use @c kInfinite for indefinite repeat.
291 */
292 void set_loop_count(int32_t loop_count);
293
294 /**
295 * @brief Replaces the callback invoked on each tick.
296 *
297 * @param callback New callback.
298 */
299 void set_callback(Callback&& callback);
300
301 /**
302 * @brief Sets the dispatch priority for the timer task.
303 *
304 * @details
305 * Only effective when the associated loop is of type @c kPriorityType.
306 *
307 * @param priority Priority value (higher = earlier dispatch).
308 */
309 void set_priority(uint16_t priority);
310
311 private:
312 void run_callback();
313
314 void wait_for_idle();
315
316 void clear();
317
318 void force_to_start();
319
320 void set_remain_loop_count(int32_t loop_count) const;
321
322 void sub_remain_loop_count() const;
323
324 void set_invoke_count(uint64_t invoke_count) const;
325
326 uint64_t get_start_time() const;
327
328 bool is_once_type() const;
329
330 bool has_callback() const;
331
332 Callback take_callback();
333
334 friend class MessageLoop;
335 std::unique_ptr<struct TimerImpl> impl_;
336 static constexpr uint32_t kMinInterval{10'000U};
337
339};
340
341} // namespace vlink
Platform-independent macro definitions for the VLink library.
#define VLINK_EXPORT
Definition macros.h:85
#define VLINK_DISALLOW_COPY_AND_ASSIGN(classname)
Deletes the copy constructor and copy-assignment operator of classname.
Definition macros.h:184