@@ -40,22 +40,22 @@ namespace executors
4040 *
4141 * Timers management
4242 * 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 .
4545 * Timers are kept ordered in a binary-heap priority queue.
4646 * 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.
4848 * Because of this, they have a not-negligible impact on the performance.
4949 *
5050 * Timers execution
5151 * The most efficient implementation consists in letting a TimersManager object
5252 * to spawn a thread where timers are monitored and periodically executed.
5353 * Besides this, other APIs allow to either execute a single timer or all the
5454 * 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 .
5959 *
6060 */
6161class TimersManager
@@ -65,70 +65,80 @@ class TimersManager
6565
6666 /* *
6767 * @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.
6871 */
6972 explicit TimersManager (std::shared_ptr<rclcpp::Context> context);
7073
7174 /* *
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.
7376 */
7477 ~TimersManager ();
7578
7679 /* *
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.
8184 */
8285 void add_timer (rclcpp::TimerBase::SharedPtr timer);
8386
8487 /* *
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.
86105 */
87106 void start ();
88107
89108 /* *
90109 * @brief Stops the timers thread.
110+ * Will do nothing if the timer thread was not running.
91111 */
92112 void stop ();
93113
94114 /* *
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.
100118 */
101- std::chrono::nanoseconds execute_ready_timers ();
119+ void execute_ready_timers ();
102120
103121 /* *
104122 * @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.
107127 */
108128 bool execute_head_timer (
109129 std::chrono::time_point<std::chrono::steady_clock> tp =
110130 std::chrono::time_point<std::chrono::steady_clock>::max());
111131
112132 /* *
113133 * @brief Get the amount of time before the next timer expires.
114- *
134+ * Function is thread safe, but it will throw an error if the timers thread is running.
135+ *
115136 * @return std::chrono::nanoseconds to wait,
116137 * 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 .
118139 */
119140 std::chrono::nanoseconds get_head_timeout ();
120141
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-
132142 // This is what the TimersManager uses to denote a duration forever.
133143 // We don't use std::chrono::nanoseconds::max because it will overflow.
134144 // See https://en.cppreference.com/w/cpp/thread/condition_variable/wait_for
@@ -144,20 +154,22 @@ class TimersManager
144154 class TimersHeap ;
145155
146156 /* *
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.
148161 * The "validate_and_lock" API allows to get ownership of the timers and also makes sure that
149162 * the heap property is respected.
150- * The root of the heap is the timer that expires first.
151163 * This class is not thread safe and requires external mutexes to protect its usage.
152164 */
153165 class WeakTimersHeap
154166 {
155167public:
156168 /* *
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.
161173 */
162174 bool add_timer (TimerPtr timer)
163175 {
@@ -173,10 +185,10 @@ class TimersManager
173185 }
174186
175187 /* *
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.
180192 */
181193 bool remove_timer (TimerPtr timer)
182194 {
@@ -193,7 +205,9 @@ class TimersManager
193205
194206 /* *
195207 * @brief This function restores the current object as a valid heap
196- * and it also returns a locked version of it
208+ * and it returns a locked version of it.
209+ * It is the only public API to access the stored timers.
210+ *
197211 * @return TimersHeap owned timers corresponding to the current object
198212 */
199213 TimersHeap validate_and_lock ()
@@ -205,8 +219,9 @@ class TimersManager
205219
206220 while (it != weak_heap_.end ()) {
207221 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));
222+ // This timer is valid, add it to the locked heap
223+ // Note: we access private `owned_heap_` member field.
224+ locked_heap.owned_heap_ .push_back (std::move (timer_shared_ptr));
210225 it++;
211226 } else {
212227 // This timer went out of scope, remove it
@@ -229,11 +244,15 @@ class TimersManager
229244 /* *
230245 * @brief This function allows to recreate the heap of weak pointers
231246 * from an heap of owned pointers.
247+ * It is required to be called after a locked TimersHeap generated from this object
248+ * has been modified in any way (e.g. timers triggered, added, removed).
249+ *
232250 * @param heap timers heap to store as weak pointers
233251 */
234252 void store (const TimersHeap & heap)
235253 {
236254 weak_heap_.clear ();
255+ // Note: we access private `owned_heap_` member field.
237256 for (auto t : heap.owned_heap_ ) {
238257 weak_heap_.push_back (t);
239258 }
@@ -254,7 +273,8 @@ class TimersManager
254273 /* *
255274 * @brief This class is the equivalent of WeakTimersHeap but with ownership of the timers.
256275 * It can be generated by locking the weak version.
257- * It provides operations to manipulate the heap
276+ * It provides operations to manipulate the heap.
277+ * This class is not thread safe and requires external mutexes to protect its usage.
258278 */
259279 class TimersHeap
260280 {
@@ -324,13 +344,15 @@ class TimersManager
324344 }
325345
326346 /* *
327- * @brief Restore a valid heap after the root value has been replaced.
347+ * @brief Restore a valid heap after the root value has been replaced (e.g. timer triggered) .
328348 */
329349 void heapify_root ()
330350 {
331351 // The following code is a more efficient version of doing
332- // - pop_heap; pop_back;
333- // - push_back; push_heap;
352+ // pop_heap();
353+ // pop_back();
354+ // push_back();
355+ // push_heap();
334356 // as it removes the need for the last push_heap
335357
336358 // Push the modified element (i.e. the current root) at the bottom of the heap
@@ -350,26 +372,21 @@ class TimersManager
350372 }
351373
352374 /* *
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
375+ * @brief Friend declaration to allow the `validate_and_lock()` function to access the
376+ * underlying heap container
358377 */
359- void push_back (TimerPtr timer)
360- {
361- owned_heap_.push_back (timer);
362- }
378+ friend TimersHeap WeakTimersHeap::validate_and_lock ();
363379
364380 /* *
365- * @brief Friend declaration to allow the store function to access the underlying
366- * heap container
381+ * @brief Friend declaration to allow the ` store()` function to access the
382+ * underlying heap container
367383 */
368384 friend void WeakTimersHeap::store (const TimersHeap & heap);
369385
370386private:
371387 /* *
372388 * @brief Comparison function between timers.
389+ * Returns true if `a` expires after `b`.
373390 */
374391 static bool timer_greater (TimerPtr a, TimerPtr b)
375392 {
@@ -392,6 +409,7 @@ class TimersManager
392409 * @return std::chrono::nanoseconds to wait,
393410 * the returned value could be negative if the timer is already expired
394411 * or MAX_TIME if the heap is empty.
412+ * This function is not thread safe, acquire the timers_mutex_ before calling it.
395413 */
396414 std::chrono::nanoseconds get_head_timeout_unsafe (const TimersHeap & heap)
397415 {
@@ -404,15 +422,16 @@ class TimersManager
404422 /* *
405423 * @brief Executes all the timers currently ready when the function is invoked
406424 * while keeping the heap correctly sorted.
407- * This function is not thread safe, acquire a mutex before calling it.
425+ * This function is not thread safe, acquire the timers_mutex_ before calling it.
408426 */
409- void execute_ready_timers_unsafe (TimersHeap & heap );
427+ void execute_ready_timers_unsafe ();
410428
411429 /* *
412430 * @brief Helper function that checks whether a timer was already ready
413- * at a specific timepoint
431+ * at a specific time point.
432+
414433 * @param timer a pointer to the timer to check for
415- * @param tp the timepoint to check for
434+ * @param tp the time point to check for
416435 * @return true if timer was ready at tp
417436 */
418437 bool timer_was_ready_at_tp (
@@ -424,7 +443,7 @@ class TimersManager
424443 return time_ready < tp;
425444 }
426445
427- // Thread used to run the timers monitoring and execution task
446+ // Thread used to run the timers execution task
428447 std::thread timers_thread_;
429448 // Protects access to timers
430449 std::mutex timers_mutex_;
0 commit comments