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

Add support for spin_until_complete (take 2) #2475

Open
wants to merge 5 commits into
base: rolling
Choose a base branch
from

Conversation

christophebedard
Copy link
Member

@christophebedard christophebedard commented Apr 2, 2024

Reverts #1956, so it un-reverts #1874 (with some major updates), see #1874 (comment)

Replaces #1957

Closes #1821

This adds spin_until_complete(condition, timeout).

This also adds rclcpp::Executor::spin_for(duration). Original Issue: #1821 (Added by @fujitatomoya)

This goes along with the following PRs:

Copy link
Member

@wjwwood wjwwood left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sorry to just now be commenting on this change (even though it's the second iteration), but I wanted to revoice some concerns about the way that the change is being made.

rclcpp/include/rclcpp/executor.hpp Outdated Show resolved Hide resolved
rclcpp/include/rclcpp/executor.hpp Outdated Show resolved Hide resolved
if (condition()) {
return FutureReturnCode::SUCCESS;
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This implementation is racy. If the condition switches after this line of code, and an infinite timeout is given, we got a dead lock.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's on the user since they provide the condition, but I can document it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently the function is named spin_until_complete. Therefore I would expect, I can pass it a std::future or any condition, and it will do the correct thing, and protect me from this kind of races.

This is clearly not the case. I think it is not even possible with the current API.
To solve this, we would also need to pass a std::unique_lock, like done here:
https://en.cppreference.com/w/cpp/thread/condition_variable/wait

Copy link
Contributor

@jmachowinski jmachowinski Apr 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, as a second option, one could limit the timeout of spin_once_impl to a third optional argument 'condition_recheck_interval', so that we get a bounded wait time.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait, I don't see how this can cause an unwanted infinite wait/spin.

It is assumed that condition() is initially false. If it is true from the get-go, then it just returns without spinning. Otherwise, it spins once before checking if condition() is true, otherwise it spins once again, and returns if it times out. If condition() never changes to true and timeout is negative, then yes it will spin infinitely, but that's intentional.

Am I missing something?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wjwwood to quote your here :
"The thing is that if we merge this just because Monday is coming up, then we have to come back and undo what we merged through a deprecation cycle, which is very undesirable."

I still haven't seen any example of how this API would be used in a real word application.
@SteveMacenski you want this API for "Make the APIs easier to use / understand without exposing the technical details under the hood for the Jr Eng / researcher / student that shouldn't be required to learn the details, and"

The currently proposed API does not reach this goal, as you need to be aware that you need to wake the wait, and to do this, you need to either have a known side effect of another API (which can change by any internal implementation change) or explicit manage a guard condition.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is true, and I stand by what I said, we don't want to merge things that we think will be obsolete immediately.

However, I believe that the situation where you're pulling my quote from (#2466 (comment)) and this one are quite different. First, it's not clear to me that what you've proposed here would actually replace the desire for the new API in this pr, so we may or may not deprecate what this pr adds even with the approach you suggested implemented and merged later. Whereas in the other situation, it's more of an A or B style design question. If we choose A now, we'll have a mess switching direction to B in the future, and it would never make sense to have both. Therefore it's more important to act when we're sure it's the right approach, unlike here, where adding a new function is by comparison low risk.

Copy link
Contributor

@jmachowinski jmachowinski Apr 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still believe, that the currently proposed API is a to big pitfall for end users.
E.g.

spin_until_complete([end = std::chrono::system_clock::now() + std::duration::Seconds(1)] ()
{
return end < std::chrono::system_clock::now();
});

will not work as expected. I also don't really see how the API will make the live of a user easier in its current state.

I also proposed

spin_until_complete(
    const std::function<bool(void)> & condition,
    std::chrono::duration<TimeRepT, TimeT> timeout = std::chrono::duration<TimeRepT, TimeT>(-1),
     std::chrono::duration<TimeRepT, TimeT> condition_check_interval = std::chrono::millisecons(10))

this approach, which is trivial to implements, and would make the situation way better.

Copy link
Member

@wjwwood wjwwood Apr 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also don't really see how the API will make the live of a user easier in its current state.

I also don't think the callback approach is really that helpful (but I think it can result in less code), for that you should talk with @SteveMacenski. But I still think it's no better or worse than using futures.

this approach, which is trivial to implements, and would make the situation way better.

Having the check interval (essentially polling) is bandaid, really the condition should always be attached to some activity that causes the spin to interrupt in an event driven way. If you want something like the check interval, you can just use the timeout argument and loop the function, or you could use an empty timer, etc. etc.

Most of the time this is used to wait for a Service Client to receive a response to its request, or something similar, and for that this function will work fine, and in fact the future-based version of this worked fine.


@christophebedard @fujitatomoya can you guys comment on whether or not to delay this until after the release or not?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also don't really see how the API will make the live of a user easier in its current state.

I also don't think the callback approach is really that helpful (but I think it can result in less code), for that you should talk with @SteveMacenski. But I still think it's no better or worse than using futures.

Just to be clear, I picked up this set of PRs mainly to finally push them across the finish line. I personally do not have a need for this API in itself, so I'd also like to hear what Steve (or any other potential user) has to say here.

@christophebedard @fujitatomoya can you guys comment on whether or not to delay this until after the release or not?

At this point I'd prefer to just push it to make sure to get it right.

christophebedard and others added 2 commits April 4, 2024 15:37
Co-authored-by: Hubert Liberacki <[email protected]>
Co-authored-by: Tomoya Fujita <[email protected]>
Signed-off-by: Hubert Liberacki <[email protected]>
Signed-off-by: Tomoya Fujita <[email protected]>
Signed-off-by: Christophe Bedard <[email protected]>
@christophebedard christophebedard force-pushed the christophebedard/revert-1956-revert-1874-hliberacki/timeout branch from 449c07a to 0fb8046 Compare April 4, 2024 23:03
@christophebedard christophebedard changed the title Add support for spin_until_timeout (take 2) Add support for spin_until_complete (take 2) Apr 9, 2024
rclcpp/test/rclcpp/test_executor.cpp Outdated Show resolved Hide resolved
rclcpp/test/rclcpp/test_executor.cpp Outdated Show resolved Hide resolved
@wjwwood
Copy link
Member

wjwwood commented Apr 9, 2024

I had a few questions/suggestions/comments, but generally the approach lgtm. I think once you've addressed them to your satisfaction @christophebedard, then it should be good to go.

Copy link
Member

@wjwwood wjwwood left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm, now that we've resolved the discussion about the API for now.

@wjwwood
Copy link
Member

wjwwood commented Apr 12, 2024

CI:

  • Linux Build Status
  • Linux-aarch64 Build Status
  • Windows Build Status

@christophebedard
Copy link
Member Author

Should we merge this now, or wait until the rclpy PR (ros2/rclpy#1268) has been reviewed and is ready to go too?

@wjwwood
Copy link
Member

wjwwood commented Apr 15, 2024

We should really merge them together, because I don't think it's good if we have rclpy and rclcpp deviate from one another.

@wjwwood
Copy link
Member

wjwwood commented Apr 15, 2024

My CI above did not include your changes for rclpy unfortunately. We'll have to run separate CI for that.

@roncapat
Copy link

Any news on this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

spin until timeout
7 participants