-
Notifications
You must be signed in to change notification settings - Fork 425
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
feat(MultiThreadedExecutor): Added ability to handle exceptions from threads #2382
Conversation
daa95df
to
dee34bc
Compare
@wjwwood @mjcarroll @fujitatomoya Ping, I would like to see this merged for jazzy |
dee34bc
to
80ecbd8
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jmachowinski thanks for the PR. i think this is useful! a couple of minor comments. (if we merge this, i think it would be nice to update the demos/example aligned with this.)
51c3b85
to
7fd9c1a
Compare
68979e9
to
e62d7f6
Compare
e62d7f6
to
070d71e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm with green CI.
@alsora friendly ping.
rclcpp/include/rclcpp/experimental/executors/events_executor/events_executor.hpp
Outdated
Show resolved
Hide resolved
070d71e
to
d557d7b
Compare
@alsora can you check and resolve your comments above? |
@jmachowinski can you rebase to rolling? i would like to make sure CI is green with current HEAD especially at this release moment. after rebase, i need to start CI with https://gist.githubusercontent.com/fujitatomoya/fef2fe491b4f10fd5120cc3907fab525/raw/a05837c1b3c2ab1de927d222ef4fcb68fcd82850/ros2.repos |
Looks good to me |
d557d7b
to
cc6c0bc
Compare
rebased, note this currently fails the pipline, as it uncovered the bug reported in #2474 |
cc6c0bc
to
2a499bb
Compare
9d19aa8
to
9d20c70
Compare
9d20c70
to
8611bbf
Compare
@jmachowinski this is ready for CI with ros2/demos#670? just chekcing. |
…threads This commit adds external exception handling for the worker threads, allowing application code to implement custom exception handling. feat(Executor): Added spin API with exception handler. Signed-off-by: Janosch Machowinski <[email protected]>
Signed-off-by: Janosch Machowinski <[email protected]>
8611bbf
to
8e6c9e3
Compare
rebased to rolling I don't know why the CI failed, it builds for me using this file: |
I need to make some changes before merging this, but I cannot push to this branch, so I'll be opening a new pr. Please don't merge this one. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am going to fix up this pr and run CI on it as an option to merge, but I really think that it would be preferable to have an exception handler callback as an optional part of the ExecutorOptions instead, passing it during construction and/or setting it after construction with a new method, rather than modifying all of these spin functions and causing down stream changes.
Please don't merge this version of the pr, see: #2505
/** | ||
* \sa rclcpp::Executor:spin() for more details | ||
* \throws std::runtime_error when spin() called while already spinning | ||
* @param exception_handler will be called for every exception in the processing threads | ||
* | ||
* The exception_handler can be called from multiple threads at the same time. | ||
* The exception_handler shall rethrow the exception it if wants to terminate the program. | ||
*/ | ||
RCLCPP_PUBLIC | ||
virtual void | ||
spin(const std::function<void(const std::exception & e)> & exception_handler) = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would have liked to have had a non-virtual spin(... exception_handler = nullptr)
, that called a virtual and protected spin_impl(... exception_handler) = 0
, so that we didn't need two signatures. It would be easier to document and wouldn't require every executor to implement two functions, one of which is pretty much always be boilerplate calling the other.
However, I don't know if I have time to do that before the freeze, so I won't block this on it, but I wanted to make the comment somewhere.
/** | ||
* \sa rclcpp::Executor:spin() for more details | ||
* \throws std::runtime_error when spin() called while already spinning | ||
* @param exception_handler will be called for every exception in the processing threads |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't use \
everywhere (some other @
have slipped through reviews), but we should tend towards the desired state when possible, which is \
everywhere. I'll fix these up in my version of this pr.
/** | ||
* \sa rclcpp::Executor:spin() for more details |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs a "brief", with a /// ...
line before the block comment.
@@ -125,9 +125,14 @@ class TimersManager | |||
/** | |||
* @brief Starts a thread that takes care of executing the timers stored in this object. | |||
* Function will throw an error if the timers thread was already running. | |||
* | |||
* @param exception_handler if valid, the execution of the timer will be done in a try catch block, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll leave this @
, because this file uses that style, but the whole file should be updated (it mostly got a review pass for being in the experimental
namespace.
@@ -395,6 +395,69 @@ Executor::execute_any_executable(AnyExecutable & any_exec) | |||
any_exec.callback_group->can_be_taken_from().store(true); | |||
} | |||
|
|||
template<typename Function> | |||
void execute_guarded( | |||
const Function & fun, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick: use full variable names
const Function & fun, | |
const Function & function, |
|
||
void | ||
StaticSingleThreadedExecutor::spin( | ||
const std::function<void(const std::exception & e)> & exception_handler) | ||
{ | ||
if (spinning.exchange(true)) { | ||
throw std::runtime_error("spin() called while already spinning"); | ||
} | ||
RCPPUTILS_SCOPE_EXIT(this->spinning.store(false); ); | ||
|
||
// This is essentially the contents of the rclcpp::Executor::wait_for_work method, | ||
// except we need to keep the wait result to reproduce the StaticSingleThreadedExecutor | ||
// behavior. | ||
while (rclcpp::ok(this->context_) && spinning.load()) { | ||
this->spin_once_impl(std::chrono::nanoseconds(-1), exception_handler); | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should not copy-paste like this, we should have one call the other...
spinner_ = std::thread(&rclcpp::executors::SingleThreadedExecutor::spin, executor); | ||
spinner_ = std::thread([this]() {executor->spin();}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this change needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see it doesn't compile... this is the kind of change that suggests we should rethink the approach. The old style of calling it should still work I think. I'll leave it, but it's another point against this approach in my mind.
+1 for this. |
Another concern I've got after trying to shape this up for merge is that there's no way to indicate if an exception should cause the executor to stop or not. You can re-throw, but doing that in a thread will trigger termination, which by default will head towards Instead, I'd like to have seen either a return value from the exception handler that can tell the executor to stop or throw from the main thread or something, and/or pass the executor into the exception handler so that it can cancel it if desired. |
Closing in favor of the updated version #2505 |
This commit adds external exception handling for the worker threads, allowing application code to implement custom exception handling.