-
Notifications
You must be signed in to change notification settings - Fork 421
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
API Changes for Multihtreaded Events Executor #2466
Changes from all commits
5077974
03e5f9a
34e2a80
77799c9
f85ad12
c72e601
2ebcb49
da85eb0
3b8c558
163f727
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
@@ -242,42 +242,30 @@ class Executor | |||||||||
* spin_node_once to block indefinitely (the default behavior). A timeout of 0 causes this | ||||||||||
* function to be non-blocking. | ||||||||||
*/ | ||||||||||
template<typename RepT = int64_t, typename T = std::milli> | ||||||||||
void | ||||||||||
RCLCPP_PUBLIC | ||||||||||
virtual void | ||||||||||
spin_node_once( | ||||||||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node, | ||||||||||
std::chrono::duration<RepT, T> timeout = std::chrono::duration<RepT, T>(-1)) | ||||||||||
{ | ||||||||||
return spin_node_once_nanoseconds( | ||||||||||
node, | ||||||||||
std::chrono::duration_cast<std::chrono::nanoseconds>(timeout) | ||||||||||
); | ||||||||||
} | ||||||||||
const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr & node, | ||||||||||
std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1)); | ||||||||||
|
||||||||||
/// Convenience function which takes Node and forwards NodeBaseInterface. | ||||||||||
template<typename NodeT = rclcpp::Node, typename RepT = int64_t, typename T = std::milli> | ||||||||||
void | ||||||||||
RCLCPP_PUBLIC | ||||||||||
virtual void | ||||||||||
spin_node_once( | ||||||||||
std::shared_ptr<NodeT> node, | ||||||||||
std::chrono::duration<RepT, T> timeout = std::chrono::duration<RepT, T>(-1)) | ||||||||||
{ | ||||||||||
return spin_node_once_nanoseconds( | ||||||||||
node->get_node_base_interface(), | ||||||||||
std::chrono::duration_cast<std::chrono::nanoseconds>(timeout) | ||||||||||
); | ||||||||||
} | ||||||||||
const std::shared_ptr<rclcpp::Node> & node, | ||||||||||
std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1)); | ||||||||||
|
||||||||||
/// Add a node, complete all immediately available work, and remove the node. | ||||||||||
/** | ||||||||||
* \param[in] node Shared pointer to the node to add. | ||||||||||
*/ | ||||||||||
RCLCPP_PUBLIC | ||||||||||
void | ||||||||||
virtual void | ||||||||||
spin_node_some(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node); | ||||||||||
|
||||||||||
/// Convenience function which takes Node and forwards NodeBaseInterface. | ||||||||||
RCLCPP_PUBLIC | ||||||||||
void | ||||||||||
virtual void | ||||||||||
spin_node_some(std::shared_ptr<rclcpp::Node> node); | ||||||||||
|
||||||||||
/// Collect work once and execute all available work, optionally within a max duration. | ||||||||||
|
@@ -307,14 +295,14 @@ class Executor | |||||||||
* \param[in] node Shared pointer to the node to add. | ||||||||||
*/ | ||||||||||
RCLCPP_PUBLIC | ||||||||||
void | ||||||||||
virtual void | ||||||||||
spin_node_all( | ||||||||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node, | ||||||||||
std::chrono::nanoseconds max_duration); | ||||||||||
|
||||||||||
/// Convenience function which takes Node and forwards NodeBaseInterface. | ||||||||||
RCLCPP_PUBLIC | ||||||||||
void | ||||||||||
virtual void | ||||||||||
spin_node_all(std::shared_ptr<rclcpp::Node> node, std::chrono::nanoseconds max_duration); | ||||||||||
|
||||||||||
/// Collect and execute work repeatedly within a duration or until no more work is available. | ||||||||||
|
@@ -366,52 +354,11 @@ class Executor | |||||||||
const FutureT & future, | ||||||||||
std::chrono::duration<TimeRepT, TimeT> timeout = std::chrono::duration<TimeRepT, TimeT>(-1)) | ||||||||||
{ | ||||||||||
// TODO(wjwwood): does not work recursively; can't call spin_node_until_future_complete | ||||||||||
// inside a callback executed by an executor. | ||||||||||
|
||||||||||
// Check the future before entering the while loop. | ||||||||||
// If the future is already complete, don't try to spin. | ||||||||||
std::future_status status = future.wait_for(std::chrono::seconds(0)); | ||||||||||
if (status == std::future_status::ready) { | ||||||||||
return FutureReturnCode::SUCCESS; | ||||||||||
} | ||||||||||
|
||||||||||
auto end_time = std::chrono::steady_clock::now(); | ||||||||||
std::chrono::nanoseconds timeout_ns = std::chrono::duration_cast<std::chrono::nanoseconds>( | ||||||||||
timeout); | ||||||||||
if (timeout_ns > std::chrono::nanoseconds::zero()) { | ||||||||||
end_time += timeout_ns; | ||||||||||
} | ||||||||||
std::chrono::nanoseconds timeout_left = timeout_ns; | ||||||||||
|
||||||||||
if (spinning.exchange(true)) { | ||||||||||
throw std::runtime_error("spin_until_future_complete() called while already spinning"); | ||||||||||
return spin_until_future_complete_impl(std::chrono::duration_cast<std::chrono::nanoseconds>( | ||||||||||
timeout), [&future] (std::chrono::nanoseconds wait_time) { | ||||||||||
return future.wait_for(wait_time); | ||||||||||
} | ||||||||||
RCPPUTILS_SCOPE_EXIT(this->spinning.store(false); ); | ||||||||||
while (rclcpp::ok(this->context_) && spinning.load()) { | ||||||||||
// Do one item of work. | ||||||||||
spin_once_impl(timeout_left); | ||||||||||
|
||||||||||
// Check if the future is set, return SUCCESS if it is. | ||||||||||
status = future.wait_for(std::chrono::seconds(0)); | ||||||||||
if (status == std::future_status::ready) { | ||||||||||
return FutureReturnCode::SUCCESS; | ||||||||||
} | ||||||||||
// If the original timeout is < 0, then this is blocking, never TIMEOUT. | ||||||||||
if (timeout_ns < std::chrono::nanoseconds::zero()) { | ||||||||||
continue; | ||||||||||
} | ||||||||||
// Otherwise check if we still have time to wait, return TIMEOUT if not. | ||||||||||
auto now = std::chrono::steady_clock::now(); | ||||||||||
if (now >= end_time) { | ||||||||||
return FutureReturnCode::TIMEOUT; | ||||||||||
} | ||||||||||
// Subtract the elapsed time from the original timeout. | ||||||||||
timeout_left = std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - now); | ||||||||||
} | ||||||||||
|
||||||||||
// The future did not complete before ok() returned false, return INTERRUPTED. | ||||||||||
return FutureReturnCode::INTERRUPTED; | ||||||||||
); | ||||||||||
} | ||||||||||
|
||||||||||
/// Cancel any running spin* function, causing it to return. | ||||||||||
|
@@ -420,7 +367,7 @@ class Executor | |||||||||
* \throws std::runtime_error if there is an issue triggering the guard condition | ||||||||||
*/ | ||||||||||
RCLCPP_PUBLIC | ||||||||||
void | ||||||||||
virtual void | ||||||||||
cancel(); | ||||||||||
|
||||||||||
/// Returns true if the executor is currently spinning. | ||||||||||
|
@@ -433,6 +380,15 @@ class Executor | |||||||||
is_spinning(); | ||||||||||
|
||||||||||
protected: | ||||||||||
/// Constructor that will not initialize any non-trivial members. | ||||||||||
/** | ||||||||||
* This constructor is intended to be used by any derived executor | ||||||||||
* that explicitly does not want to use the default implementation provided | ||||||||||
* by this class. | ||||||||||
* This constructor is guaranteed to not modify the system state. | ||||||||||
*/ | ||||||||||
explicit Executor(const std::shared_ptr<rclcpp::Context> & context); | ||||||||||
|
||||||||||
/// Add a node to executor, execute the next available unit of work, and remove the node. | ||||||||||
/** | ||||||||||
* Implementation of spin_node_once using std::chrono::nanoseconds | ||||||||||
|
@@ -447,6 +403,21 @@ class Executor | |||||||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node, | ||||||||||
std::chrono::nanoseconds timeout); | ||||||||||
|
||||||||||
/// Spin (blocking) until the get_future_status returns ready, max_duration is reached, | ||||||||||
/// or rclcpp is interrupted. | ||||||||||
/** | ||||||||||
* \param[in] max_duration Optional duration parameter, which gets passed to Executor::spin_node_once. | ||||||||||
* `-1` is block forever, `0` is non-blocking. | ||||||||||
* If the time spent inside the blocking loop exceeds this timeout, return a TIMEOUT return | ||||||||||
* code. | ||||||||||
* \param[in] wait_for_future A function waiting for a future to become ready. | ||||||||||
* \return The return code, one of `SUCCESS`, `INTERRUPTED`, or `TIMEOUT`. | ||||||||||
*/ | ||||||||||
RCLCPP_PUBLIC | ||||||||||
virtual FutureReturnCode spin_until_future_complete_impl( | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Also, needs at least a minimal docstring. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As I said above, I'd consider dropping this change, but if you keep it, consider the name There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We got two colliding styles here, there is also There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added docs There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yeah, I'll leave it as-is, but I think this is related to the templated functions that take duration vs just taking nanoseconds, I still believe the templated approach is better, and in that case the narrowing to the non-template protected methods are truly just to cast it down to nanoseconds, so that makes sense in those cases. Likely if I had noticed I would have suggesting the newer functions follow that pattern, but it's not that important now. |
||||||||||
std::chrono::nanoseconds max_duration, | ||||||||||
const std::function<std::future_status(std::chrono::nanoseconds wait_time)> & wait_for_future); | ||||||||||
|
||||||||||
/// Collect work and execute available work, optionally within a duration. | ||||||||||
/** | ||||||||||
* Implementation of spin_some and spin_all. | ||||||||||
|
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.
Do you absolutely need to make these non-template? I'm afraid existing code may not compile against this if certain types of durations are passed.
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 don't really need to, but all other functions in the executor have the same signature and compile just fine. Any std::chono::duration type should auto convert to std::chrono::nanoseconds.
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.
Arguably the others taking only
nanoseconds
is a probably a bug, see: https://stackoverflow.com/a/22362867It seems like in C++20 we could have a function that takes
std::chrono::duration<auto>
, but we don't have that right now.I'll remove this change in my fix up.