VLink  2.0.0
A high-performance communication middleware
wheel_timer.h
浏览该文件的文档.
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 wheel_timer.h
26  * @brief Hashed timing wheel for managing very large pools of concurrent timeouts.
27  *
28  * @details
29  * @c vlink::WheelTimer implements the classic hashed-timing-wheel data structure that
30  * provides O(1) insertion, O(1) removal and O(k) per-tick expiry processing, where @c k
31  * is the number of timers in the current slot. It scales comfortably to tens or hundreds
32  * of thousands of independent timeouts (for example, a session manager or a connection
33  * keep-alive supervisor).
34  *
35  * Wheel layout for a wheel with @c S slots advancing every @c interval_ms milliseconds:
36  *
37  * @verbatim
38  * cursor
39  * v
40  * +-----+-----+-----+-----+-----+-----+-----+-----+ ... +-----+
41  * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | ... | S-1 |
42  * +-----+-----+-----+--+--+-----+-----+--+--+-----+ ... +-----+
43  * | |
44  * handler list: handler list:
45  * - {key, round=0, cb} - {key, round=2, cb}
46  * - {key, round=1, cb}
47  * @endverbatim
48  *
49  * Timers with timeouts longer than @c S @c * @c interval_ms wrap around using a round
50  * counter that is decremented on every cursor pass. Removal is O(1) via a key-to-slot
51  * map maintained alongside the wheel.
52  *
53  * Lifecycle:
54  * -# Construct with @c WheelTimer(slots, interval_ms).
55  * -# Call @c start() to launch the worker thread.
56  * -# Insert timers via @c add(); keep the returned @c Key for later removal.
57  * -# Call @c stop() to terminate the worker thread.
58  *
59  * @note
60  * - Expiry callbacks run on the wheel's worker thread; marshal results back through a
61  * @c MessageLoop or a lock when shared state is involved.
62  * - @c set_catchup_limit() bounds how many missed slots are processed per tick to keep
63  * a single iteration from blocking for too long after a system sleep.
64  *
65  * @par Example
66  * @code
67  * vlink::WheelTimer wheel(256, 10);
68  * wheel.start();
69  *
70  * auto key = wheel.add(1000, [](vlink::WheelTimer::Key k) {
71  * (void)k;
72  * });
73  *
74  * auto repeat_key = wheel.add(500, [](vlink::WheelTimer::Key k) { (void)k; }, 500);
75  *
76  * wheel.remove(key);
77  * wheel.stop();
78  * @endcode
79  */
80 
81 #pragma once
82 
83 #include <cstdint>
84 #include <memory>
85 
86 #include "./functional.h"
87 #include "./macros.h"
88 
89 namespace vlink {
90 
91 /**
92  * @class WheelTimer
93  * @brief O(1) hashed-timing-wheel scheduler backed by an internal worker thread.
94  *
95  * @details
96  * Owns a fixed-size slot array and advances a cursor every @c interval_ms milliseconds.
97  */
99  public:
100  /**
101  * @brief Opaque handle returned by @c add() and accepted by @c remove().
102  */
103  using Key = int64_t;
104 
105  /**
106  * @brief Callback signature invoked when a timer expires.
107  *
108  * @details
109  * The @c Key argument lets a single lambda manage multiple timers.
110  */
111  using Callback = Function<void(Key)>;
112 
113  /**
114  * @brief Constructs the wheel with the given resolution and capacity.
115  *
116  * @details
117  * Both @p slots and @p interval_ms must be greater than zero; invalid values log a
118  * fatal error and throw. Call @c start() to begin advancing the wheel.
119  *
120  * @param slots Number of buckets in the wheel. Larger values shorten the
121  * round counter for long timeouts.
122  * @param interval_ms Tick duration in milliseconds; sets the resolution of every timer.
123  */
124  explicit WheelTimer(uint32_t slots, uint32_t interval_ms);
125 
126  /**
127  * @brief Destructor. Calls @c stop() when the wheel is still running.
128  */
130 
131  /**
132  * @brief Starts the worker thread and begins advancing the wheel cursor.
133  */
134  void start();
135 
136  /**
137  * @brief Stops the wheel and joins the worker thread.
138  *
139  * @details
140  * Pending timers do not fire after @c stop() returns.
141  */
142  void stop();
143 
144  /**
145  * @brief Temporarily suspends timer dispatch without joining the worker thread.
146  *
147  * @details
148  * The worker remains alive but the cursor stops advancing. Use @c resume() to
149  * continue.
150  */
151  void pause();
152 
153  /**
154  * @brief Resumes a paused wheel; a no-op when not paused.
155  */
156  void resume();
157 
158  /**
159  * @brief Wakes the worker thread early when it is sleeping between ticks.
160  *
161  * @details
162  * Useful after inserting a very short timeout that should fire immediately.
163  */
164  void wakeup();
165 
166  /**
167  * @brief Reports whether the wheel is currently running.
168  *
169  * @return @c true between @c start() and @c stop().
170  */
171  [[nodiscard]] bool is_running() const;
172 
173  /**
174  * @brief Inserts a new timer into the wheel.
175  *
176  * @details
177  * The callback runs on the worker thread after @p timeout_ms milliseconds. When
178  * @p repeat_ms is non-zero the timer is re-armed at every expiry with @p repeat_ms as
179  * the next timeout.
180  *
181  * @param timeout_ms Initial delay in milliseconds (rounded up to a slot boundary).
182  * @param callback Function invoked on expiry.
183  * @param repeat_ms Re-arm interval in milliseconds; @c 0 selects one-shot. Default: @c 0.
184  * @return Unique key identifying this timer entry, or @c -1 on invalid input or key
185  * allocation failure.
186  */
187  Key add(uint32_t timeout_ms, Callback&& callback, uint32_t repeat_ms = 0);
188 
189  /**
190  * @brief Removes a timer before its callback runs.
191  *
192  * @param key Key returned by @c add().
193  * @return @c true when the timer existed and was removed.
194  */
195  bool remove(Key key);
196 
197  /**
198  * @brief Returns the approximate remaining time before a timer fires.
199  *
200  * @details
201  * The value is rounded to the wheel's tick resolution.
202  *
203  * @param key Key returned by @c add().
204  * @return Estimated remaining time in milliseconds, or @c 0 when @p key is unknown.
205  */
206  [[nodiscard]] uint32_t get_remaining_time(Key key) const;
207 
208  /**
209  * @brief Caps the number of catch-up slots processed in a single tick iteration.
210  *
211  * @details
212  * Prevents a single tick from blocking the worker for an unbounded duration when the
213  * wheel falls behind (for example, after the system wakes from sleep).
214  *
215  * @param max_slots_to_catch_up Maximum slots processed per tick cycle.
216  */
217  void set_catchup_limit(uint32_t max_slots_to_catch_up);
218 
219  private:
220  struct Impl;
221  std::shared_ptr<Impl> impl_;
222 
224 };
225 
226 } // 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
Definition: point_cloud.h:195