Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow timers to keep up the intended rate in MultiThreadedExecutor #1516

Merged
merged 10 commits into from
Feb 17, 2021
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ class MultiThreadedExecutor : public rclcpp::Executor
std::chrono::nanoseconds next_exec_timeout_;

std::set<TimerBase::SharedPtr> scheduled_timers_;
std::mutex scheduled_timers_mutex_;
};

} // namespace executors
Expand Down
19 changes: 17 additions & 2 deletions rclcpp/src/rclcpp/executors/multi_threaded_executor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ MultiThreadedExecutor::run(size_t)
while (rclcpp::ok(this->context_) && spinning.load()) {
rclcpp::AnyExecutable any_exec;
{
std::lock_guard<std::mutex> wait_lock(wait_mutex_);
std::lock_guard<std::mutex> wait_guard{wait_mutex_};
if (!rclcpp::ok(this->context_) || !spinning.load()) {
return;
}
Expand All @@ -85,6 +85,13 @@ MultiThreadedExecutor::run(size_t)
}
if (any_exec.timer) {
ivanpauno marked this conversation as resolved.
Show resolved Hide resolved
// Guard against multiple threads getting the same timer.

// THIS MUST BE DONE WITH BOTH THE WAIT_MUTEX AND SCHEDULED TIMERS MUTEX TAKEN!!
// It might seem unnecessary, but you avoid the following race:
// Thread A: get timer, context switch.
// Thread B: get timer, insert scheduled timer, execute timer, remove scheduled timer.
// Thread A: execute timer.
std::lock_guard<std::mutex> scheduled_timers_guard{scheduled_timers_mutex_};
ivanpauno marked this conversation as resolved.
Show resolved Hide resolved
if (scheduled_timers_.count(any_exec.timer) != 0) {
// Make sure that any_exec's callback group is reset before
// the lock is released.
Expand All @@ -103,7 +110,15 @@ MultiThreadedExecutor::run(size_t)
execute_any_executable(any_exec);
ivanpauno marked this conversation as resolved.
Show resolved Hide resolved

if (any_exec.timer) {
std::lock_guard<std::mutex> wait_lock(wait_mutex_);
// DON'T DELETE THE scheduled_timers_mutex_ AND REPLACE IT WITH THE wait_mutex_ here.
// Now, this mutex will only compete with ONE worker thread that's is trying to insert
// a timer.
// (and also, N other worker threads also trying to delete a timer).
// If the wait_mutex_ is used here, this will compete with all the other worker threads!!!
// If we're waiting too long too remove the scheduled timer, maybe we are discarding a timer
// execution that we shouldn't!
// Of course, this can still happen if the callback is too long ...
std::lock_guard<std::mutex> scheduled_timers_guard{scheduled_timers_mutex_};
ivanpauno marked this conversation as resolved.
Show resolved Hide resolved
auto it = scheduled_timers_.find(any_exec.timer);
if (it != scheduled_timers_.end()) {
scheduled_timers_.erase(it);
Expand Down