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

Implement is valid methods #103

Merged
merged 19 commits into from
Jul 18, 2017
Merged

Implement is valid methods #103

merged 19 commits into from
Jul 18, 2017

Conversation

allenh1
Copy link
Contributor

@allenh1 allenh1 commented Dec 29, 2016

Working on #81 .

This PR adds the rcl_x_is_valid calls for:

  • publishers
  • subscriptions
  • services
  • clients

Connect to #81

@allenh1
Copy link
Contributor Author

allenh1 commented Dec 29, 2016

Currently, there is a small regression I don't know how to fix.

Error in destruction of intra process rcl publisher handle: publisher implementation is invalid, at /home/allenh1/ros2_ws/src/rcl/rcl/src/rcl/publisher.c:161

This happens if you send a publisher (and, oddly, a subscriber) a SIGINT.

@wjwwood
Copy link
Member

wjwwood commented Jan 3, 2017

I think this might be related to this: #100

Basically one of the things we check is whether or not rcl_shutdown() has been called, and if so return an error code saying it is invalid. We may need to relax that check for "getters".

@allenh1
Copy link
Contributor Author

allenh1 commented Jan 4, 2017

That definitely makes sense. Perhaps we could just catch SIGINT and forward it to a proper shutdown function. I'll experiment with that later today.

@wjwwood
Copy link
Member

wjwwood commented Jan 4, 2017

SIGINT is already being caught, it's one of the responsibilities of the language specific client libraries, e.g. rclcpp or rclpy. Here is rclcpp's version of this that is done in rclcpp::init:

https://github.com/ros2/rclcpp/blob/master/rclcpp/src/rclcpp/utilities.cpp#L111-L123

@allenh1
Copy link
Contributor Author

allenh1 commented Jan 11, 2017

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.

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.

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:

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.

@@ -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;
Copy link
Member

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.

Copy link
Contributor Author

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_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) {
Copy link
Member

Choose a reason for hiding this comment

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

Indentation is off here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

RCL_SET_ERROR_MSG(rmw_get_error_string_safe());
return RCL_RET_ERROR;
}
return RCL_RET_OK;
} return RCL_RET_OK;
Copy link
Member

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

RCL_CHECK_ARGUMENT_FOR_NULL(publisher, false);
RCL_CHECK_FOR_NULL_WITH_MSG(publisher->impl,
"publisher implementation is invalid",
return NULL);
Copy link
Member

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);

Copy link
Member

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.

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;
Copy link
Member

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).

Copy link
Contributor Author

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.

return false);
RCL_CHECK_FOR_NULL_WITH_MSG(*service->impl->rmw_handle->service_name,
"service name is invalid",
return false);
Copy link
Member

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?

Copy link
Member

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.

Copy link
Contributor Author

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.

@allenh1
Copy link
Contributor Author

allenh1 commented Jan 18, 2017

Sorry for disappearing for a little there (first week back at school). When I ran uncrustify, I got the following:

allenh1@host-147-10 ~/ros2_ws $ ament_uncrustify src/rcl/
Traceback (most recent call last):
  File "/home/allenh1/ros2_ws/install/bin/ament_uncrustify", line 6, in <module>
    from pkg_resources import load_entry_point
  File "<frozen importlib._bootstrap>", line 969, in _find_and_load
  File "<frozen importlib._bootstrap>", line 958, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 664, in _load_unlocked
  File "<frozen importlib._bootstrap>", line 634, in _load_backward_compatible
  File "/usr/lib64/python3.5/site-packages/setuptools-32.2.0-py3.5.egg/pkg_resources/__init__.py", line 3019, in <module>
  File "/usr/lib64/python3.5/site-packages/setuptools-32.2.0-py3.5.egg/pkg_resources/__init__.py", line 3003, in _call_aside
  File "/usr/lib64/python3.5/site-packages/setuptools-32.2.0-py3.5.egg/pkg_resources/__init__.py", line 3032, in _initialize_master_working_set
  File "/usr/lib64/python3.5/site-packages/setuptools-32.2.0-py3.5.egg/pkg_resources/__init__.py", line 655, in _build_master
  File "/usr/lib64/python3.5/site-packages/setuptools-32.2.0-py3.5.egg/pkg_resources/__init__.py", line 963, in require
  File "/usr/lib64/python3.5/site-packages/setuptools-32.2.0-py3.5.egg/pkg_resources/__init__.py", line 849, in resolve
pkg_resources.DistributionNotFound: The 'ament-uncrustify' distribution was not found and is required by the application

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 ament_cpplint

@mikaelarguedas mikaelarguedas added the in review Waiting for review (Kanban column) label Apr 10, 2017
@allenh1
Copy link
Contributor Author

allenh1 commented May 16, 2017

@wjwwood Resolved conflicts. Am I missing anything else with this?

@wjwwood
Copy link
Member

wjwwood commented May 16, 2017

@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 NULL passed and in what cases is each of those considered invalid. I believe it would be best if at least those two categories of things were covered if not more.

@allenh1
Copy link
Contributor Author

allenh1 commented May 18, 2017

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!

@allenh1
Copy link
Contributor Author

allenh1 commented Jun 1, 2017

@wjwwood Just got around to doing the documentation here. I also moved a check I saw in rcl_subscription_get_topic_name to the rcl_subscription_is_valid function (checks that the option pointer is not NULL).

@@ -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);
Copy link
Member

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.

Copy link
Contributor Author

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.

Copy link
Member

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.

@wjwwood
Copy link
Member

wjwwood commented Jun 2, 2017

Looks like client is still missing, from my review above:

Also, missing from this list is client (the counterpart to service).

Thanks for working on it 😄!

@allenh1
Copy link
Contributor Author

allenh1 commented Jun 10, 2017

@wjwwood I pushed the fix for the rcl_subscription_get_options function if you wanna look at it. Tested it and it works (as far as I can tell).

Next on my list is the client checks. After that, we should be good right?

@wjwwood
Copy link
Member

wjwwood commented Jun 10, 2017

Tested it and it works (as far as I can tell).

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.

Next on my list is the client checks. After that, we should be good right?

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.

@wjwwood
Copy link
Member

wjwwood commented Jun 10, 2017

I started some CI for you:

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

@allenh1 allenh1 added in progress Actively being worked on (Kanban column) and removed in review Waiting for review (Kanban column) labels Jun 22, 2017
@allenh1
Copy link
Contributor Author

allenh1 commented Jun 22, 2017

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

@mikaelarguedas
Copy link
Member

mikaelarguedas commented Jun 22, 2017

@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

@allenh1
Copy link
Contributor Author

allenh1 commented Jun 22, 2017

@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

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.

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.

@@ -370,6 +375,8 @@ RCL_WARN_UNUSED
rmw_client_t *
rcl_client_get_rmw_handle(const rcl_client_t * client);

bool
Copy link
Member

Choose a reason for hiding this comment

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

Needs docs still.

bool
rcl_subscription_is_valid(const rcl_subscription_t * subscription)
{
const rcl_subscription_options_t * options = _subscription_get_options(subscription);
Copy link
Member

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.

Copy link
Contributor Author

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.

@allenh1
Copy link
Contributor Author

allenh1 commented Jun 22, 2017

@wjwwood Got it -- will do.

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 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.

@@ -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;
Copy link
Member

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 _).

Copy link
Member

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.

Copy link
Member

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.

* 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`
Copy link
Member

Choose a reason for hiding this comment

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

typo mw -> rmw

/**
* The bool returned is `false` if:
* - the argument `publisher` is `NULL`
* - the argument's `publisher->impl` is `NULL`
Copy link
Member

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.

Copy link
Contributor Author

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)

Copy link
Member

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.

@@ -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
Copy link
Member

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.

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);
Copy link
Member

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.

Copy link
Contributor Author

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.

Copy link
Member

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.

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) {
Copy link
Member

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.

return false,
rcl_get_default_allocator());
return true;
}
Copy link
Member

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_CHECK_FOR_NULL_WITH_MSG(options,
"subscription implementation is invalid",
return false,
options->allocator);
Copy link
Member

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...

@allenh1
Copy link
Contributor Author

allenh1 commented Jul 15, 2017

@wjwwood Thanks! I'll get to these shortly.

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.

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.

@allenh1
Copy link
Contributor Author

allenh1 commented Jul 17, 2017

After linting and whatnot, I ran the tests and hit the following:

2: Test command: /usr/bin/python3 "-u" "/home/allenh1/ros2_ws/install/share/ament_cmake_test/cmake/run_test.py" "/home/allenh1/ros2_ws/build/rcl_lifecycle/test_results/rcl_lifecycle/test_multiple_instances.gtest.xml" "--output-file" "/home/allenh1/ros2_ws/build/rcl_lifecycle/ament_cmake_gtest/test_multiple_instances.txt" "--command" "/home/allenh1/ros2_ws/build/rcl_lifecycle/test_multiple_instances" "--gtest_output=xml:/home/allenh1/ros2_ws/build/rcl_lifecycle/test_results/rcl_lifecycle/test_multiple_instances.gtest.xml"
2: Test timeout computed to be: 60
2: -- run_test.py: invoking following command in '/home/allenh1/ros2_ws/src/ros2/rcl/rcl_lifecycle':
2:  - /home/allenh1/ros2_ws/build/rcl_lifecycle/test_multiple_instances --gtest_output=xml:/home/allenh1/ros2_ws/build/rcl_lifecycle/test_results/rcl_lifecycle/test_multiple_instances.gtest.xml
2: Running main() from gtest_main.cc
2: [==========] Running 1 test from 1 test case.
2: [----------] Global test environment set-up.
2: [----------] 1 test from TestMultipleInstances
2: [ RUN      ] TestMultipleInstances.default_sequence_error_unresolved
2: 
2: >>> [rcutils|error_handling.c:148] rcutils_set_error_state()
2: This error state is being overwritten:
2: 
2:   'options argument is null, at /home/allenh1/ros2_ws/src/ros2/rcl/rcl/src/rcl/publisher.c:255'
2: 
2: with this new error message:
2: 
2:   'could not free lifecycle com interface. Leaking memory!
2: , at /home/allenh1/ros2_ws/src/ros2/rcl/rcl_lifecycle/src/rcl_lifecycle.c:104'
2: 
2: rcutils_reset_error() should be called after error handling to avoid this.
2: <<<
2: /home/allenh1/ros2_ws/src/ros2/rcl/rcl_lifecycle/test/test_multiple_instances.cpp:111: Failure
2: Value of: ret
2:   Actual: 1
2: Expected: 0
2: could not free lifecycle com interface. Leaking memory!
2: , at /home/allenh1/ros2_ws/src/ros2/rcl/rcl_lifecycle/src/rcl_lifecycle.c:104
2: 
2: >>> [rcutils|error_handling.c:148] rcutils_set_error_state()
2: This error state is being overwritten:
2: 
2:   'could not free lifecycle com interface. Leaking memory!
2: , at /home/allenh1/ros2_ws/src/ros2/rcl/rcl_lifecycle/src/rcl_lifecycle.c:104'
2: 
2: with this new error message:
2: 
2:   'options argument is null, at /home/allenh1/ros2_ws/src/ros2/rcl/rcl/src/rcl/publisher.c:255'
2: 
2: rcutils_reset_error() should be called after error handling to avoid this.
2: <<<
2: 
2: >>> [rcutils|error_handling.c:148] rcutils_set_error_state()
2: This error state is being overwritten:
2: 
2:   'options argument is null, at /home/allenh1/ros2_ws/src/ros2/rcl/rcl/src/rcl/publisher.c:255'
2: 
2: with this new error message:
2: 
2:   'could not free lifecycle com interface. Leaking memory!
2: , at /home/allenh1/ros2_ws/src/ros2/rcl/rcl_lifecycle/src/rcl_lifecycle.c:104'
2: 
2: rcutils_reset_error() should be called after error handling to avoid this.
2: <<<
2: /home/allenh1/ros2_ws/src/ros2/rcl/rcl_lifecycle/test/test_multiple_instances.cpp:113: Failure
2: Value of: ret
2:   Actual: 1
2: Expected: 0
2: could not free lifecycle com interface. Leaking memory!
2: , at /home/allenh1/ros2_ws/src/ros2/rcl/rcl_lifecycle/src/rcl_lifecycle.c:104
2: 
2: >>> [rcutils|error_handling.c:148] rcutils_set_error_state()
2: This error state is being overwritten:
2: 
2:   'could not free lifecycle com interface. Leaking memory!
2: , at /home/allenh1/ros2_ws/src/ros2/rcl/rcl_lifecycle/src/rcl_lifecycle.c:104'
2: 
2: with this new error message:
2: 
2:   'options argument is null, at /home/allenh1/ros2_ws/src/ros2/rcl/rcl/src/rcl/publisher.c:255'
2: 
2: rcutils_reset_error() should be called after error handling to avoid this.
2: <<<
2: 
2: >>> [rcutils|error_handling.c:148] rcutils_set_error_state()
2: This error state is being overwritten:
2: 
2:   'options argument is null, at /home/allenh1/ros2_ws/src/ros2/rcl/rcl/src/rcl/publisher.c:255'
2: 
2: with this new error message:
2: 
2:   'could not free lifecycle com interface. Leaking memory!
2: , at /home/allenh1/ros2_ws/src/ros2/rcl/rcl_lifecycle/src/rcl_lifecycle.c:104'
2: 
2: rcutils_reset_error() should be called after error handling to avoid this.
2: <<<
2: /home/allenh1/ros2_ws/src/ros2/rcl/rcl_lifecycle/test/test_multiple_instances.cpp:115: Failure
2: Value of: ret
2:   Actual: 1
2: Expected: 0
2: could not free lifecycle com interface. Leaking memory!
2: , at /home/allenh1/ros2_ws/src/ros2/rcl/rcl_lifecycle/src/rcl_lifecycle.c:104
2: [  FAILED  ] TestMultipleInstances.default_sequence_error_unresolved (3 ms)
2: [----------] 1 test from TestMultipleInstances (3 ms total)
2: 
2: [----------] Global test environment tear-down
2: [==========] 1 test from 1 test case ran. (3 ms total)
2: [  PASSED  ] 0 tests.
2: [  FAILED  ] 1 test, listed below:
2: [  FAILED  ] TestMultipleInstances.default_sequence_error_unresolved
2: 
2:  1 FAILED TEST
2: -- run_test.py: return code 1
2: -- run_test.py: verify result file '/home/allenh1/ros2_ws/build/rcl_lifecycle/test_results/rcl_lifecycle/test_multiple_instances.gtest.xml'
2/8 Test #2: test_multiple_instances ..........***Failed    0.08 sec

I take this to mean that the checks I implemented are too aggressive. Should I be checking the options pointer?

@wjwwood
Copy link
Member

wjwwood commented Jul 17, 2017

I take this to mean that the checks I implemented are too aggressive.

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.

Should I be checking the options pointer?

I think so, but that's just my impression without looking at it in detail.

@allenh1
Copy link
Contributor Author

allenh1 commented Jul 17, 2017

Found it. It was an unnecessary check in the publisher's fini function.

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

@allenh1 allenh1 added in review Waiting for review (Kanban column) and removed in progress Actively being worked on (Kanban column) labels Jul 17, 2017
@allenh1
Copy link
Contributor Author

allenh1 commented Jul 17, 2017

(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?

{
const rcl_client_options_t * options;
RCL_CHECK_ARGUMENT_FOR_NULL(
client, RCL_RET_INVALID_ARGUMENT, rcl_get_default_allocator());
Copy link
Member

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.

Copy link
Contributor Author

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_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);
Copy link
Member

@dirk-thomas dirk-thomas Jul 17, 2017

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.

Copy link
Contributor Author

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`
*/
Copy link
Member

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?

Copy link
Contributor Author

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?

Copy link
Member

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.

Copy link
Member

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"?

Copy link
Member

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.

Copy link
Contributor Author

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?

Copy link
Member

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.

Copy link
Contributor Author

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.

Copy link
Member

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.

Copy link
Member

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);
Copy link
Member

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

@allenh1
Copy link
Contributor Author

allenh1 commented Jul 18, 2017

Ok, here's the new CI.

(updated, after linting)

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

@allenh1 allenh1 merged commit af03d0c into ros2:master Jul 18, 2017
@allenh1 allenh1 removed the in review Waiting for review (Kanban column) label Jul 18, 2017
@allenh1 allenh1 deleted the 81-implement-is-valid-methods branch July 18, 2017 20:12
@wjwwood
Copy link
Member

wjwwood commented Jul 18, 2017

I added an issue to add tests for these functions in the future: #150

ivanpauno pushed a commit that referenced this pull request Jan 2, 2020
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.

4 participants