@@ -40,22 +40,22 @@ namespace executors
40
40
*
41
41
* Timers management
42
42
* This class provides APIs to add and remove timers.
43
- * It keeps a list of weak pointers from added timers, and owns them only when
44
- * have expired and need to be executed.
43
+ * It keeps a list of weak pointers from added timers, and locks them only when
44
+ * they need to be executed or modified .
45
45
* Timers are kept ordered in a binary-heap priority queue.
46
46
* Calls to add/remove APIs will temporarily block the execution of the timers and
47
- * will require to reorder the internal priority queue of timers .
47
+ * will require to reorder the internal priority queue.
48
48
* Because of this, they have a not-negligible impact on the performance.
49
49
*
50
50
* Timers execution
51
51
* The most efficient implementation consists in letting a TimersManager object
52
52
* to spawn a thread where timers are monitored and periodically executed.
53
53
* Besides this, other APIs allow to either execute a single timer or all the
54
54
* currently ready ones.
55
- * This class assumes that the execute_callback API of the stored timer is never
56
- * called by other entities, but can only be called from here.
57
- * If this assumption is not respected, the heap property will be invalidated,
58
- * so timers may be executed out of order.
55
+ * This class assumes that the ` execute_callback()` API of the stored timers is never
56
+ * called by other entities, but it can only be called from here.
57
+ * If this assumption is not respected, the heap property may be invalidated,
58
+ * so timers may be executed out of order, without this object noticing it .
59
59
*
60
60
*/
61
61
class TimersManager
@@ -65,70 +65,80 @@ class TimersManager
65
65
66
66
/* *
67
67
* @brief Construct a new TimersManager object
68
+ *
69
+ * @param context custom context to be used.
70
+ * Shared ownership of the context is held until destruction.
68
71
*/
69
72
explicit TimersManager (std::shared_ptr<rclcpp::Context> context);
70
73
71
74
/* *
72
- * @brief Destruct the object making sure to stop thread and release memory.
75
+ * @brief Destruct the TimersManager object making sure to stop thread and release memory.
73
76
*/
74
77
~TimersManager ();
75
78
76
79
/* *
77
- * @brief Adds a new TimerBase::WeakPtr to the storage.
78
- * This object will store a weak pointer of the added timer
79
- * in a heap data structure.
80
- * @param timer the timer to be added
80
+ * @brief Adds a new timer to the storage, maintaining weak ownership of it .
81
+ * Function is thread safe and it can be called regardless of the state of the timers thread.
82
+ *
83
+ * @param timer the timer to add.
81
84
*/
82
85
void add_timer (rclcpp::TimerBase::SharedPtr timer);
83
86
84
87
/* *
85
- * @brief Starts a thread that takes care of executing timers added to this object.
88
+ * @brief Remove a single timer from the object storage.
89
+ * Will do nothing if the timer was not being stored here.
90
+ * Function is thread safe and it can be called regardless of the state of the timers thread.
91
+ *
92
+ * @param timer the timer to remove.
93
+ */
94
+ void remove_timer (rclcpp::TimerBase::SharedPtr timer);
95
+
96
+ /* *
97
+ * @brief Remove all the timers stored in the object.
98
+ * Function is thread safe and it can be called regardless of the state of the timers thread.
99
+ */
100
+ void clear ();
101
+
102
+ /* *
103
+ * @brief Starts a thread that takes care of executing the timers stored in this object.
104
+ * Function will throw an error if the timers thread was already running.
86
105
*/
87
106
void start ();
88
107
89
108
/* *
90
109
* @brief Stops the timers thread.
110
+ * Will do nothing if the timer thread was not running.
91
111
*/
92
112
void stop ();
93
113
94
114
/* *
95
- * @brief Executes all the timers currently ready when the function is invoked
96
- * while keeping the heap correctly sorted.
97
- * @return std::chrono::nanoseconds for next timer to expire,
98
- * the returned value could be negative if the timer is already expired
99
- * or MAX_TIME if the heap is empty.
115
+ * @brief Executes all the timers currently ready when the function was invoked.
116
+ * This function will lock all the stored timers throughout its duration.
117
+ * Function is thread safe, but it will throw an error if the timers thread is running.
100
118
*/
101
- std::chrono::nanoseconds execute_ready_timers ();
119
+ void execute_ready_timers ();
102
120
103
121
/* *
104
122
* @brief Executes head timer if ready at time point.
105
- * @param tp the timepoint to check for
106
- * @return true if head timer was ready at tp
123
+ * Function is thread safe, but it will throw an error if the timers thread is running.
124
+ *
125
+ * @param tp the time point to check for, where `max()` denotes that no check will be performed.
126
+ * @return true if head timer was ready at time point.
107
127
*/
108
128
bool execute_head_timer (
109
129
std::chrono::time_point<std::chrono::steady_clock> tp =
110
130
std::chrono::time_point<std::chrono::steady_clock>::max());
111
131
112
132
/* *
113
133
* @brief Get the amount of time before the next timer expires.
134
+ * Function is thread safe, but it will throw an error if the timers thread is running.
114
135
*
115
136
* @return std::chrono::nanoseconds to wait,
116
137
* the returned value could be negative if the timer is already expired
117
- * or MAX_TIME if the heap is empty .
138
+ * or MAX_TIME if there are no timers stored in the object .
118
139
*/
119
140
std::chrono::nanoseconds get_head_timeout ();
120
141
121
- /* *
122
- * @brief Remove all the timers stored in the object.
123
- */
124
- void clear ();
125
-
126
- /* *
127
- * @brief Remove a single timer stored in the object, passed as a shared_ptr.
128
- * @param timer the timer to remove.
129
- */
130
- void remove_timer (rclcpp::TimerBase::SharedPtr timer);
131
-
132
142
// This is what the TimersManager uses to denote a duration forever.
133
143
// We don't use std::chrono::nanoseconds::max because it will overflow.
134
144
// See https://en.cppreference.com/w/cpp/thread/condition_variable/wait_for
@@ -144,20 +154,22 @@ class TimersManager
144
154
class TimersHeap ;
145
155
146
156
/* *
147
- * @brief This class allows to store weak pointers to timers in a heap data structure.
157
+ * @brief This class allows to store weak pointers to timers in a heap-like data structure.
158
+ * The root of the heap is the timer that expires first.
159
+ * Since this class uses weak ownership, it is not guaranteed that it represents a valid heap
160
+ * at any point in time as timers could go out of scope, thus invalidating it.
148
161
* The "validate_and_lock" API allows to get ownership of the timers and also makes sure that
149
162
* the heap property is respected.
150
- * The root of the heap is the timer that expires first.
151
163
* This class is not thread safe and requires external mutexes to protect its usage.
152
164
*/
153
165
class WeakTimersHeap
154
166
{
155
167
public:
156
168
/* *
157
- * @brief Try to add a new timer to the heap.
158
- * After the addition, the heap property is preserved.
159
- * @param timer new timer to add
160
- * @return true if timer has been added, false if it was already there
169
+ * @brief Add a new timer to the heap. After the addition, the heap property is enforced .
170
+ *
171
+ * @param timer new timer to add.
172
+ * @return true if timer has been added, false if it was already there.
161
173
*/
162
174
bool add_timer (TimerPtr timer)
163
175
{
@@ -173,10 +185,10 @@ class TimersManager
173
185
}
174
186
175
187
/* *
176
- * @brief Try to remove a timer from the heap.
177
- * After the removal, the heap property is preserved.
178
- * @param timer timer to remove
179
- * @return true if timer has been removed, false if it was not there
188
+ * @brief Remove a timer from the heap. After the removal, the heap property is enforced .
189
+ *
190
+ * @param timer timer to remove.
191
+ * @return true if timer has been removed, false if it was not there.
180
192
*/
181
193
bool remove_timer (TimerPtr timer)
182
194
{
@@ -191,9 +203,27 @@ class TimersManager
191
203
return removed;
192
204
}
193
205
206
+ /* *
207
+ * @brief Returns a const reference to the front element
208
+ */
209
+ const WeakTimerPtr & front () const
210
+ {
211
+ return weak_heap_.front ();
212
+ }
213
+
214
+ /* *
215
+ * @brief Returns whether the heap is empty or not
216
+ */
217
+ bool empty () const
218
+ {
219
+ return weak_heap_.empty ();
220
+ }
221
+
194
222
/* *
195
223
* @brief This function restores the current object as a valid heap
196
- * and it also returns a locked version of it
224
+ * and it returns a locked version of it.
225
+ * It is the only public API to access and manipulate the stored timers.
226
+ *
197
227
* @return TimersHeap owned timers corresponding to the current object
198
228
*/
199
229
TimersHeap validate_and_lock ()
@@ -205,8 +235,9 @@ class TimersManager
205
235
206
236
while (it != weak_heap_.end ()) {
207
237
if (auto timer_shared_ptr = it->lock ()) {
208
- // This timer is valid, add it to the vector
209
- locked_heap.push_back (std::move (timer_shared_ptr));
238
+ // This timer is valid, add it to the locked heap
239
+ // Note: we access private `owned_heap_` member field.
240
+ locked_heap.owned_heap_ .push_back (std::move (timer_shared_ptr));
210
241
it++;
211
242
} else {
212
243
// This timer went out of scope, remove it
@@ -229,11 +260,15 @@ class TimersManager
229
260
/* *
230
261
* @brief This function allows to recreate the heap of weak pointers
231
262
* from an heap of owned pointers.
263
+ * It is required to be called after a locked TimersHeap generated from this object
264
+ * has been modified in any way (e.g. timers triggered, added, removed).
265
+ *
232
266
* @param heap timers heap to store as weak pointers
233
267
*/
234
268
void store (const TimersHeap & heap)
235
269
{
236
270
weak_heap_.clear ();
271
+ // Note: we access private `owned_heap_` member field.
237
272
for (auto t : heap.owned_heap_ ) {
238
273
weak_heap_.push_back (t);
239
274
}
@@ -254,7 +289,8 @@ class TimersManager
254
289
/* *
255
290
* @brief This class is the equivalent of WeakTimersHeap but with ownership of the timers.
256
291
* It can be generated by locking the weak version.
257
- * It provides operations to manipulate the heap
292
+ * It provides operations to manipulate the heap.
293
+ * This class is not thread safe and requires external mutexes to protect its usage.
258
294
*/
259
295
class TimersHeap
260
296
{
@@ -324,13 +360,15 @@ class TimersManager
324
360
}
325
361
326
362
/* *
327
- * @brief Restore a valid heap after the root value has been replaced.
363
+ * @brief Restore a valid heap after the root value has been replaced (e.g. timer triggered) .
328
364
*/
329
365
void heapify_root ()
330
366
{
331
367
// The following code is a more efficient version of doing
332
- // - pop_heap; pop_back;
333
- // - push_back; push_heap;
368
+ // pop_heap();
369
+ // pop_back();
370
+ // push_back();
371
+ // push_heap();
334
372
// as it removes the need for the last push_heap
335
373
336
374
// Push the modified element (i.e. the current root) at the bottom of the heap
@@ -350,26 +388,21 @@ class TimersManager
350
388
}
351
389
352
390
/* *
353
- * @brief Convenience function that allows to push an element at the bottom of the heap.
354
- * It will not perform any check on whether the heap remains valid or not.
355
- * Those checks are responsibility of the calling code.
356
- *
357
- * @param timer timer to push at the back of the data structure representing the heap
391
+ * @brief Friend declaration to allow the `validate_and_lock()` function to access the
392
+ * underlying heap container
358
393
*/
359
- void push_back (TimerPtr timer)
360
- {
361
- owned_heap_.push_back (timer);
362
- }
394
+ friend TimersHeap WeakTimersHeap::validate_and_lock ();
363
395
364
396
/* *
365
- * @brief Friend declaration to allow the store function to access the underlying
366
- * heap container
397
+ * @brief Friend declaration to allow the ` store()` function to access the
398
+ * underlying heap container
367
399
*/
368
400
friend void WeakTimersHeap::store (const TimersHeap & heap);
369
401
370
402
private:
371
403
/* *
372
404
* @brief Comparison function between timers.
405
+ * Returns true if `a` expires after `b`.
373
406
*/
374
407
static bool timer_greater (TimerPtr a, TimerPtr b)
375
408
{
@@ -392,27 +425,23 @@ class TimersManager
392
425
* @return std::chrono::nanoseconds to wait,
393
426
* the returned value could be negative if the timer is already expired
394
427
* or MAX_TIME if the heap is empty.
428
+ * This function is not thread safe, acquire the timers_mutex_ before calling it.
395
429
*/
396
- std::chrono::nanoseconds get_head_timeout_unsafe (const TimersHeap & heap)
397
- {
398
- if (heap.empty ()) {
399
- return MAX_TIME;
400
- }
401
- return (heap.front ())->time_until_trigger ();
402
- }
430
+ std::chrono::nanoseconds get_head_timeout_unsafe ();
403
431
404
432
/* *
405
433
* @brief Executes all the timers currently ready when the function is invoked
406
434
* while keeping the heap correctly sorted.
407
- * This function is not thread safe, acquire a mutex before calling it.
435
+ * This function is not thread safe, acquire the timers_mutex_ before calling it.
408
436
*/
409
- void execute_ready_timers_unsafe (TimersHeap & heap );
437
+ void execute_ready_timers_unsafe ();
410
438
411
439
/* *
412
440
* @brief Helper function that checks whether a timer was already ready
413
- * at a specific timepoint
441
+ * at a specific time point.
442
+
414
443
* @param timer a pointer to the timer to check for
415
- * @param tp the timepoint to check for
444
+ * @param tp the time point to check for
416
445
* @return true if timer was ready at tp
417
446
*/
418
447
bool timer_was_ready_at_tp (
@@ -424,7 +453,7 @@ class TimersManager
424
453
return time_ready < tp;
425
454
}
426
455
427
- // Thread used to run the timers monitoring and execution task
456
+ // Thread used to run the timers execution task
428
457
std::thread timers_thread_;
429
458
// Protects access to timers
430
459
std::mutex timers_mutex_;
0 commit comments