-
Notifications
You must be signed in to change notification settings - Fork 487
Updating client API to be able to remove pending requests #1728
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
Conversation
Signed-off-by: Ivan Santiago Paunovic <[email protected]>
Signed-off-by: Ivan Santiago Paunovic <[email protected]>
I'm planning to write a more complete example of how to do this, but I would like first to get some early feedback. |
| operator Future &() {return impl_;} | ||
|
|
||
| /// Deprecated, use take_future() instead. | ||
| /** | ||
| * Allow implicit conversions to `std::future` by value. | ||
| * \deprecated | ||
| */ | ||
| [[deprecated("FutureAndRequestId: use take_future() instead of an implicit conversion")]] | ||
| operator Future() {return impl_;} |
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 think I actually don't need this two custom implicit conversions, but actually one to SharedFuture to avoid breaking pre-existing code.
I'm taking a look locally.
| private: | ||
| Future impl_; | ||
| int64_t req_id_; |
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 think we can make this two members public, this is simply a pair there's no invariant here.
The future like methods were added to avoid breaking pre-existing code.
It's not a bad idea to deprecate all of them though, that way we don't break pre-existing code but users get a warning and they take a look to remove_pending_request().
I finally found some tests in
Note: this isn't that easy.
If anyone feels that's a better solution than breaking this use case, I can update the PR. |
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.
Though I still think it's weird, you typically either want a callback or a future, not both.
Is there a technical reason for not providing both, or is it just aesthetic? We could still maintain API stability and document that the user can (or should) use the returned future to prune the request if there's a timeout.
The approach looks okay to me. Have you thought about a way to handle pruning requests internally (so the user doesn't have to worry about it)? Alternatively, have you consider something like this: #1697 (comment) ?
It's mainly the second, but it's also performance.
We cannot remove the pending request with the future, that's why I created the
If we later add this, we could in that case only return the request id and not a future.
Yes but that doesn't fit well with the async_send_request API.
Yes, I think that solution is interesting. Long term, I would rather have some simpler wrappers of the rcl handles that don't know anything about callbacks at all, and then on top of that we could build a Condition (similar as waitable) and a task concept. Subscription sub{node, topic_name, sub_opts};
Client cli{node, service_names, cli_opts};
executor.on_condition(sub.has_message_condition(), [&sub](TaskHandle handle) {
// take message and do something with it
// the TaskHandle can be used to e.g. cancel this task
// or maybe more powerful things, like swapping the condition the task is waiting on.
});
client.send_request(request);
auto complete_token = executor.on_condition_once(
cli.has_response_condition(),
[&cli](TaskHandle handle) {
// take and handle response here
});
executor.spin_until_complete(complete_token, timeout);Don't look into the details of the snippet above, but an approach like that has some benefits:
|
Signed-off-by: Ivan Santiago Paunovic <[email protected]> Co-authored-by: Jacob Perron <[email protected]>
Signed-off-by: Ivan Santiago Paunovic <[email protected]> Co-authored-by: Jacob Perron <[email protected]>
)" This reverts commit bf752c7.
)" This reverts commit bf752c7. Signed-off-by: Ivan Santiago Paunovic <[email protected]>
)" (#1733) This reverts commit bf752c7. Signed-off-by: Ivan Santiago Paunovic <[email protected]>
|
I clicked the merge button in the wrong window 🤦♂️. |
* Revert "Revert "Updating client API to be able to remove pending requests (#1728)" (#1733)" This reverts commit d5f3d35. Signed-off-by: Ivan Santiago Paunovic <[email protected]> * Address peer review comments Signed-off-by: Ivan Santiago Paunovic <[email protected]> * Fix tests in rclcpp_components, rclcpp_lifecycle Signed-off-by: Ivan Santiago Paunovic <[email protected]>
Fixes #1697.
This modifies a few things in the client API:
Though this breaks API, nobody was actually using that in the ROS 2 core.
If someone wants to have both a callback and a future, they can capture the future in a lambda and set it.
SharedFutureAndRequestIDclass, similar to theFutureAndRequestIDclass introduced here.Future, and instead have the two listed below.That wasn't done here to avoid having to create simultaneous PRs in other repos.
FutureAndRequestID.Though it breaks ABI, it has some implicit conversions to
std::futureto not break API.The implicit conversion by value was only added to not break existing code and generates a deprecation warning.
remove_pending_request()method, the return value of any of the async_send_request() overloads can be passed to it.Examples:
When using callbacks, you need to combine the client with a Timer to prune the old ones: