@@ -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.
134+ * Function is thread safe, but it will throw an error if the timers thread is running.
114135 *
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 {
@@ -191,9 +203,27 @@ class TimersManager
191203 return removed;
192204 }
193205
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+
194222 /* *
195223 * @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+ *
197227 * @return TimersHeap owned timers corresponding to the current object
198228 */
199229 TimersHeap validate_and_lock ()
@@ -205,8 +235,9 @@ class TimersManager
205235
206236 while (it != weak_heap_.end ()) {
207237 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));
210241 it++;
211242 } else {
212243 // This timer went out of scope, remove it
@@ -229,11 +260,15 @@ class TimersManager
229260 /* *
230261 * @brief This function allows to recreate the heap of weak pointers
231262 * 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+ *
232266 * @param heap timers heap to store as weak pointers
233267 */
234268 void store (const TimersHeap & heap)
235269 {
236270 weak_heap_.clear ();
271+ // Note: we access private `owned_heap_` member field.
237272 for (auto t : heap.owned_heap_ ) {
238273 weak_heap_.push_back (t);
239274 }
@@ -254,7 +289,8 @@ class TimersManager
254289 /* *
255290 * @brief This class is the equivalent of WeakTimersHeap but with ownership of the timers.
256291 * 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.
258294 */
259295 class TimersHeap
260296 {
@@ -324,13 +360,15 @@ class TimersManager
324360 }
325361
326362 /* *
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) .
328364 */
329365 void heapify_root ()
330366 {
331367 // 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();
334372 // as it removes the need for the last push_heap
335373
336374 // Push the modified element (i.e. the current root) at the bottom of the heap
@@ -350,26 +388,21 @@ class TimersManager
350388 }
351389
352390 /* *
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
358393 */
359- void push_back (TimerPtr timer)
360- {
361- owned_heap_.push_back (timer);
362- }
394+ friend TimersHeap WeakTimersHeap::validate_and_lock ();
363395
364396 /* *
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
367399 */
368400 friend void WeakTimersHeap::store (const TimersHeap & heap);
369401
370402private:
371403 /* *
372404 * @brief Comparison function between timers.
405+ * Returns true if `a` expires after `b`.
373406 */
374407 static bool timer_greater (TimerPtr a, TimerPtr b)
375408 {
@@ -392,27 +425,23 @@ class TimersManager
392425 * @return std::chrono::nanoseconds to wait,
393426 * the returned value could be negative if the timer is already expired
394427 * or MAX_TIME if the heap is empty.
428+ * This function is not thread safe, acquire the timers_mutex_ before calling it.
395429 */
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 ();
403431
404432 /* *
405433 * @brief Executes all the timers currently ready when the function is invoked
406434 * 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.
408436 */
409- void execute_ready_timers_unsafe (TimersHeap & heap );
437+ void execute_ready_timers_unsafe ();
410438
411439 /* *
412440 * @brief Helper function that checks whether a timer was already ready
413- * at a specific timepoint
441+ * at a specific time point.
442+
414443 * @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
416445 * @return true if timer was ready at tp
417446 */
418447 bool timer_was_ready_at_tp (
@@ -424,7 +453,7 @@ class TimersManager
424453 return time_ready < tp;
425454 }
426455
427- // Thread used to run the timers monitoring and execution task
456+ // Thread used to run the timers execution task
428457 std::thread timers_thread_;
429458 // Protects access to timers
430459 std::mutex timers_mutex_;
0 commit comments