Skip to content

Commit 00666c1

Browse files
author
iRobot ROS
authored
Merge pull request #49 from irobot-ros/asoragna/cleanup
Asoragna/cleanup
2 parents 3a50ee8 + b72d124 commit 00666c1

File tree

7 files changed

+202
-179
lines changed

7 files changed

+202
-179
lines changed

rclcpp/include/rclcpp/executors/events_executor.hpp

+9-10
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,16 @@ namespace executors
3737

3838
/// Events executor implementation
3939
/**
40-
* This executor is a variation of the standard one that does not uses a waitset.
41-
* The executor uses an events queue and a timers manager to execute entities from its
40+
* This executor uses an events queue and a timers manager to execute entities from its
4241
* associated nodes and callback groups.
43-
* This provides improved performance as it allows to skip all the waitset maintenance operations.
42+
* The RMW listener APIs are used to collect new events.
43+
*
44+
* This executor tries to reduce as much as possible the amount of maintenance operations.
45+
* This allows to use customized `EventsQueue` classes to achieve different goals such
46+
* as very low CPU usage, bounded memory requirement, determinism, etc.
47+
*
48+
* The executor uses a weak ownership model and it locks entities only while executing
49+
* their related events.
4450
*
4551
* To run this executor:
4652
* rclcpp::executors::EventsExecutor executor;
@@ -204,8 +210,6 @@ class EventsExecutor : public rclcpp::Executor
204210
private:
205211
RCLCPP_DISABLE_COPY(EventsExecutor)
206212

207-
using EventQueue = std::queue<rmw_listener_event_t>;
208-
209213
// Executor callback: Push new events into the queue and trigger cv.
210214
// This function is called by the DDS entities when an event happened,
211215
// like a subscription receiving a message.
@@ -229,11 +233,6 @@ class EventsExecutor : public rclcpp::Executor
229233
this_executor->events_queue_cv_.notify_one();
230234
}
231235

232-
/// Extract and execute events from the queue until it is empty
233-
RCLCPP_PUBLIC
234-
void
235-
consume_all_events(EventQueue & queue);
236-
237236
// Execute a single event
238237
RCLCPP_PUBLIC
239238
void

rclcpp/include/rclcpp/executors/timers_manager.hpp

+102-73
Original file line numberDiff line numberDiff line change
@@ -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
*/
6161
class 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
{
155167
public:
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

370402
private:
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

Comments
 (0)