-
Notifications
You must be signed in to change notification settings - Fork 163
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
Implement is valid methods #103
Conversation
Currently, there is a small regression I don't know how to fix.
This happens if you send a publisher (and, oddly, a subscriber) a SIGINT. |
I think this might be related to this: #100 Basically one of the things we check is whether or not |
That definitely makes sense. Perhaps we could just catch |
https://github.com/ros2/rclcpp/blob/master/rclcpp/src/rclcpp/utilities.cpp#L111-L123 |
That looks really good to me. But, other than the regression, do these changes make sense to you? Did I miss any checks? I tried to conform with the macro calls used in each function's checking. |
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.
Also, missing from this list is client
(the counterpart to service
).
The other thing that these functions do not check, which I think should be included in validity checks, is whether or not rcl_shutdown
has been called since they were created. For the setters and getters this maybe too strict to check, again see #100, but for some functions like rcl_publish
it might be a reason to abort the function. See:
- https://github.com/ros2/rcl/blob/master/rcl/include/rcl/rcl.h#L127
- https://github.com/ros2/rcl/blob/master/rcl/include/rcl/rcl.h#L170
- https://github.com/ros2/rcl/blob/master/rcl/src/rcl/node.c#L190
Checking that might require that the instance id is retrieved while creating the entity (publisher or subscription or whatever) and storing it to be checked later in certain functions.
rcl/src/rcl/publisher.c
Outdated
@@ -94,10 +94,10 @@ rcl_publisher_init( | |||
rcl_ret_t | |||
rcl_publisher_fini(rcl_publisher_t * publisher, rcl_node_t * node) | |||
{ | |||
if (!rcl_publisher_is_valid(publisher)) return RCL_RET_PUBLISHER_INVALID; | |||
else if (!rcl_node_is_valid(node)) return RCL_RET_NODE_INVALID; |
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 always use {}
to enclose if like statements. You'll want to run our linters over this code to catch any other issues like this. You can either run the tests or you can just run the linters directly:
$ ament_uncrustify /path/to/ws/src/ros2/rcl
$ ament_cpplint /path/to/ws/src/ros2/rcl
Uncrustify can also automatically fix the style with the --reformat
option.
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.
Got it -- I'll use the linter before I push in the future.
rcl/src/rcl/publisher.c
Outdated
RCL_CHECK_ARGUMENT_FOR_NULL(publisher, RCL_RET_INVALID_ARGUMENT); | ||
RCL_CHECK_ARGUMENT_FOR_NULL(node, RCL_RET_INVALID_ARGUMENT); | ||
if (publisher->impl) { | ||
if (publisher->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.
Indentation is off here.
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.
Fixed
rcl/src/rcl/publisher.c
Outdated
RCL_SET_ERROR_MSG(rmw_get_error_string_safe()); | ||
return RCL_RET_ERROR; | ||
} | ||
return RCL_RET_OK; | ||
} return RCL_RET_OK; |
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.
return
should be on its own line.
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.
Fixed
rcl/src/rcl/publisher.c
Outdated
RCL_CHECK_ARGUMENT_FOR_NULL(publisher, false); | ||
RCL_CHECK_FOR_NULL_WITH_MSG(publisher->impl, | ||
"publisher implementation is invalid", | ||
return NULL); |
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.
Please wrap all or none of the arguments. I'm not sure if the linter will automatically correct this. This is what I would do:
RCL_CHECK_FOR_NULL_WITH_MSG(
publisher->impl, "publisher implementation is invalid", return NULL);
Or this:
RCL_CHECK_FOR_NULL_WITH_MSG(
publisher->impl,
"publisher implementation is invalid",
return NULL);
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 applies here and everywhere else that you made a change.
rcl/src/rcl/publisher.c
Outdated
publisher->impl, "publisher is invalid", return NULL); | ||
RCL_CHECK_FOR_NULL_WITH_MSG( | ||
publisher->impl->rmw_handle, "publisher is invalid", return NULL); | ||
if (!rcl_publisher_is_valid(publisher)) return NULL; |
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.
Unfortunately, I don't know if we can use the rcl_publisher_is_valid function you created here. The check has become stricter, before this it checked publisher and publisher->impl for NULL, but with this change it also checks that publisher->impl->rmw_handle is not NULL. That might be ok, but we recently ran into an issue which might indicate that this is too strict to simply access the data in the structure. See: #100
The new function makes sense in rcl_publish
and rcl_publisher_fini
, but for the "getters" (like this function and others) it might be too strict (again see #100).
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.
Ok -- I just removed the extra check. Now it just checks that publisher->impl
is not NULL
. I also changed it to return false
instead of NULL
, because I'm not sure why I had it as NULL
instead of false
.
rcl/src/rcl/service.c
Outdated
return false); | ||
RCL_CHECK_FOR_NULL_WITH_MSG(*service->impl->rmw_handle->service_name, | ||
"service name is invalid", | ||
return false); |
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.
What is this one checking? That the string isn't empty, i.e. the first character is not \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.
Also, why check the service name here, but not check the topic name in publisher and subscription? I'm not sure you need to check it anywhere. I don't see any way that it could end up invalid.
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.
Ok - I'll remove those checks.
Sorry for disappearing for a little there (first week back at school). When I ran uncrustify, I got the following:
I assume this is going to be Gentoo specific. If you'd like, I'll see if I can't find a patch for this. Update: same for the |
@wjwwood Resolved conflicts. Am I missing anything else with this? |
@allenh1 not sure, I'll catch up on it as soon as I can now that you've had a fresh look at it. One thing I can see is that there is not documentation on some of the new functions and the ones that have them just have a brief. If you don't have time to improve the docs I'll do it, but if you're looking for something to do I recommend taking some inspiration from the docs of the other functions and consider cases like what if |
ok -- will do. I'm getting ready to head out of town, so I might be a little lazy about it, but I'll definitely bother you about it at some point next week! |
@wjwwood Just got around to doing the documentation here. I also moved a check I saw in |
rcl/src/rcl/subscription.c
Outdated
@@ -186,13 +186,19 @@ rcl_subscription_get_rmw_handle(const rcl_subscription_t * subscription) | |||
bool | |||
rcl_subscription_is_valid(const rcl_subscription_t * subscription) | |||
{ | |||
const rcl_subscription_options_t * options = rcl_subscription_get_options(subscription); |
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.
Isn't this an infinite loop? rcl_subscription_get_options
calls this function.
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.
Uh -- wups. Let me just undo that then.
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.
You could split rcl_subscription_get_options
up like this:
return_type
_subscription_get_options(params...) {
// just return the options, no checks
return options
}
return_type
rcl_subscription_get_options(params....) {
// do checks, like call is_valid
return _subscription_get_options(params...);
}
Then you could use the internal _subscription_get_options
function in is_valid
.
Or since it is so simple you could just duplicate the logic to extract the options in both places.
Looks like client is still missing, from my review above:
Thanks for working on it 😄! |
@wjwwood I pushed the fix for the Next on my list is the client checks. After that, we should be good right? |
Have you run all of the tests (even for downstream packages)? If not I can show you how or we can start a CI job to test it for you.
When you've got that, I'll do another code review, and we'll run CI. If both of those don't turn anything up, then yeah I think this should be done. |
@allenh1 If you select the arrow and select "rebase and merge" you will see that this conflicts Edit: arg edited my comment rather than posting a new one 😖 . Ok I see that you pushed a branch of the same name on this repo but did not update the branch from where this PR is made. That's why this one still conflicts. I guess you can just force push on your fork to update this PR |
@mikaelarguedas I already did so, and it was definitely not a good time. I did, however, forget which remote I was working on. Let me force push the changes to the other one. @mikaelarguedas I too edited my comment XD |
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.
There are a few things that remain to be addressed. Also since I've seen a few tricky cases which aren't being covered (like accessing something that might be NULL), please add tests for these functions which test each of the cases so we can ensure they're correct and not just rely on review.
rcl/include/rcl/client.h
Outdated
@@ -370,6 +375,8 @@ RCL_WARN_UNUSED | |||
rmw_client_t * | |||
rcl_client_get_rmw_handle(const rcl_client_t * client); | |||
|
|||
bool |
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.
Needs docs still.
rcl/src/rcl/subscription.c
Outdated
bool | ||
rcl_subscription_is_valid(const rcl_subscription_t * subscription) | ||
{ | ||
const rcl_subscription_options_t * options = _subscription_get_options(subscription); |
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 will fail if subscription is NULL
.
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 is very true. Thank you.
@wjwwood Got it -- will do. |
…with a call to the function in the subscription code.
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 did another pass on this. I found a few more things/questions to address.
I'd say we should address these new comments before running CI again.
Sorry it took so long for me to review it again... Still digging out of emails.
rcl/include/rcl/client.h
Outdated
@@ -339,6 +339,11 @@ RCL_WARN_UNUSED | |||
const rcl_client_options_t * | |||
rcl_client_get_options(const rcl_client_t * client); | |||
|
|||
/// Non-safe get_options function. | |||
/* *INDENT-OFF* */ | |||
#define _client_get_options(client) &client->impl->options; |
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.
Does this need to be in the header? It would be better to put it in the .c
file if possible. It shouldn't be part of the public API if possible (even if it has the leading _
).
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.
Same applies to the similar functions in all the other headers of this 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.
Also, there is some inconsistency in that this kind of function is in client, pub, and sub, but not service. Which is strange.
rcl/include/rcl/client.h
Outdated
* The bool returned is `false` if: | ||
* - the argument `client` is `NULL` | ||
* - the argument's option pointer is `NULL` | ||
* - the argument's mw implementation is `NULL` |
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.
typo mw
-> rmw
rcl/include/rcl/publisher.h
Outdated
/** | ||
* The bool returned is `false` if: | ||
* - the argument `publisher` is `NULL` | ||
* - the argument's `publisher->impl` is `NULL` |
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.
The list of reasons for client is different (more exhaustive). I don't know if we need the list to be that exhaustive, but it ought to be consistent amongst similar elements.
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.
Is this sufficient?
/**
* The bool returned is `false` if the argument `publisher` is invalid.
* The bool returned is `true` otherwise.
*/
(not leaving out the details beneath, but I didn't retype them here for clarity)
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.
That's fine. As long as it is consistently done on each of the entities.
rcl/include/rcl/subscription.h
Outdated
@@ -307,6 +307,10 @@ RCL_PUBLIC | |||
RCL_WARN_UNUSED | |||
const rcl_subscription_options_t * | |||
rcl_subscription_get_options(const rcl_subscription_t * subscription); | |||
/// unsafe _subscription_get_options |
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.
Again, here but not every where. Should be removed here or added everywhere else.
rcl/src/rcl/client.c
Outdated
const rcl_client_options_t * options; | ||
RCL_CHECK_ARGUMENT_FOR_NULL(client, RCL_RET_INVALID_ARGUMENT, rcl_get_default_allocator()); | ||
options = _client_get_options(client); | ||
RCL_CHECK_FOR_NULL_WITH_MSG(options, "client is invalid", return false, options->allocator); |
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.
If options
is null, then you shouldn't access it to get the allocator. You should use rcl_get_default_allocator()
here and optionally you could use options->allocator
in the next null check.
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.
That's a very good point. Not sure why I thought that would be a good idea.
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.
No worries, probably a copy-paste error or something.
rcl/src/rcl/publisher.c
Outdated
publisher->impl, "publisher is invalid", return RCL_RET_PUBLISHER_INVALID, options->allocator); | ||
if (rmw_publish(publisher->impl->rmw_handle, ros_message) != RMW_RET_OK) { | ||
RCL_SET_ERROR_MSG(rmw_get_error_string_safe(), options->allocator); | ||
} else if (rmw_publish(publisher->impl->rmw_handle, ros_message) != RMW_RET_OK) { |
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 doesn't need to be an else if
since the previous if
returns.
rcl/src/rcl/service.c
Outdated
return false, | ||
rcl_get_default_allocator()); | ||
return true; | ||
} |
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 does this one not check the options of the service like it does for client and pub/sub?
rcl/src/rcl/subscription.c
Outdated
RCL_CHECK_FOR_NULL_WITH_MSG(options, | ||
"subscription implementation is invalid", | ||
return false, | ||
options->allocator); |
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.
Again, you shouldn't use options->allocator
if options
is null...
@wjwwood Thanks! I'll get to these shortly. |
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.
The code changes lgtm now. Thanks for iterating on it.
It would still be good to have some tests for these new "is valid" functions. That way we don't have to rely on my code review alone and so we can make sure the desired behavior doesn't accidentally get changes later.
But go ahead and check the linters locally and then run CI if you want.
After linting and whatnot, I ran the tests and hit the following:
I take this to mean that the checks I implemented are too aggressive. Should I be checking the options pointer? |
Maybe, I'm not sure. It could also be that these tests or the implementation of the lifecycle stuff is making an incorrect assumption. You'll just have to dig into the test failures and see what needs to change.
I think so, but that's just my impression without looking at it in detail. |
(I just changed one of the strings that said "subscription" in the service.c file to say "service" -- don't think this is worth re-triggering CI over) @wjwwood can I merge this after the CI is green? |
rcl/src/rcl/client.c
Outdated
{ | ||
const rcl_client_options_t * options; | ||
RCL_CHECK_ARGUMENT_FOR_NULL( | ||
client, RCL_RET_INVALID_ARGUMENT, rcl_get_default_allocator()); |
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.
RCL_RET_INVALID_ARGUMENT
? This function can only return bool
.
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.
Good catch! I believe that would have been the same as return 1
, which is very not correct.
rcl/src/rcl/publisher.c
Outdated
RCL_CHECK_FOR_NULL_WITH_MSG( | ||
publisher->impl, "publisher is invalid", return false, options->allocator); | ||
RCL_CHECK_FOR_NULL_WITH_MSG( | ||
publisher->impl->rmw_handle, "publisher is invalid", return false, options->allocator); |
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.
It would be better if the different checks use a different error message. Otherwise the user won't be able to distinguish the error case without additional debugging.
Same for the other *_is_valid
functions.
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.
That's a good point. I'll update other files as well.
* | ||
* \param[in] client pointer to the rcl client | ||
* \return `true` if `client` is valid, otherwise `false` | ||
*/ |
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.
Should the docblocks mention that in case these functions return false
an error message is being set?
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 have no idea -- @wjwwood what do you think?
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 normally don't mention that an error message is set, but in this case I think it makes sense because we're not returning an error code. I think we mention it explicitly in a few other functions where a pointer is returned (an error is set if NULL is returned instead). Can't hurt.
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.
Will the user need to reset the error message before being able to call the next function "safely"?
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.
Well it's always safe to leave the error message, but you do need to do it before calling something else if you don't want the overwriting an error message print. Elsewhere it is implied that a non RCL_RET_OK
return code means an error message is set. Here it's not so clear that one would be set by a false
return value. So I think it's appropriate.
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.
Ok. I just added a simple
/**
* In the case where `false` is to be returned, an
* error message is set.
*/
Should I re-run the CI?
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.
Are we expecting users to call these rcl_x_is_valid
functions? If no, I think the current state documenting that an error message is being set in the case the functions return false
is good.
If yes, I am not sure if that is a convenient API. I can imagine many cases where the caller doesn't care about the specific error message and will either have to write boiler plate code around every call to reset the error message or even forget about it (which will result in the error message being printed when attempted to be overwritten). Maybe in that case the functions should have a flag to optionally not set (or internall reset) the error message.
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.
My understanding is that these functions are just to clean up the code so that, if a check is added in the future, it only needs to go in one place.
But that may be completely off-base, so I'll defer to @wjwwood.
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 the interface is fine as-is. I don't expect people to use these functions often (only client library implementers) and I expect even fewer cases where a return of false doesn't trigger an error case or at least a logging event. Also resetting the error is a single line, so it's not much boiler plate.
Having another option to not set the error seems unnecessarily complicated for the number of times it would be used.
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.
Sounds good to me than. 👍
* \return `true` if `client` is valid, otherwise `false` | ||
*/ | ||
bool | ||
rcl_client_is_valid(const rcl_client_t * client); |
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.
Nit: missing empty line here
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.
fixed
I added an issue to add tests for these functions in the future: #150 |
Working on #81 .
This PR adds the
rcl_x_is_valid
calls for:Connect to #81