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
[foxy] feature allows change of message pointer #190
base: foxy
Are you sure you want to change the base?
Conversation
Codecov Report
@@ Coverage Diff @@
## foxy #190 +/- ##
==========================================
+ Coverage 59.94% 62.87% +2.93%
==========================================
Files 11 11
Lines 1016 1142 +126
Branches 331 367 +36
==========================================
+ Hits 609 718 +109
- Misses 275 280 +5
- Partials 132 144 +12
Continue to review full report at Codecov.
|
Signed-off-by: BrettRD <brettrd@brettrd.com>
Signed-off-by: BrettRD <brettrd@brettrd.com>
Signed-off-by: BrettRD <brettrd@brettrd.com>
7aca7e1
to
b3741b1
Compare
Signed-off-by: BrettRD <brettrd@brettrd.com>
Signed-off-by: BrettRD <brettrd@brettrd.com>
Could you add an example in Especially I'd like to understand, how data consistency is ensured: Consider the case, in which See my comment in the issue #186 |
This feature is intended to be used with complex messages where copy is too expensive and mesages need to be moved by pointer, and the application pre-allocates more than one message. Calling The respective subscription callback is the only sane place to call these functions. For most message types, this is unnecessary: For deeply nested messages, this pointer shuffling saves the user from executing a deep-copy, or implementing a complicated shallow-copy-and-re-nesting I'll build an example. |
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.
LGTM, just minor improvments and
- improve test cases (e.g. for other rcl_handles).
- an example in rclc_example package
- concept for data consistency (as discussed in the issue)
I have not completely understood, how you want to use this function in your application.
Do you want to change/swap the message every time after you have processed it? Or only once in the beginning? Does then the handle->data
pointer stay alive in the queue (which is processed in a different thread)?
{ | ||
if (rcl_handle == rclc_executor_handle_get_ptr(test_handle)) { | ||
return test_handle; | ||
if (NULL == executor) { |
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 might want to use this macro to test any pointer argument for NULL
in all added functions. Example:
RCL_CHECK_FOR_NULL_WITH_MSG(executor, "executor is NULL", return RCL_RET_INVALID_ARGUMENT);
Line 99 in 865b02b
RCL_CHECK_FOR_NULL_WITH_MSG(executor, "executor is NULL", return RCL_RET_INVALID_ARGUMENT); |
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 call, but that one needs to stay as a special-case; _rclc_executor_find_handle
does not return a rcl_ret_t
, so RCL_CHECK_FOR_NULL_WITH_MSG
would cause a return type mismatch compile error.
For this function, NULL
is a type-correct value with a valid meaning (no handle found), and every function that calls static rclc_executor_handle_t * _rclc_executor_find_handle()
already uses RCL_CHECK_FOR_NULL_WITH_MSG
to test the executor.
EXPECT_EQ(reduced_type, rclc_executor_handle_reduced_type(reduced_type)) << | ||
"idempotency violation"; | ||
} | ||
} |
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 should be a black box test. So actually, you did not test the functionality of rclc_executor_handle_reduced_type
that reduces SUBSCRIPTION_xxx
to SUBSCRIPTION
and ... CLIENT_xxx
to CLIENT
. A mafunctioning implementation could just reduce everything to NONE
and this test would always pass.
Sorry, to be picky, but I follow the principle of "test driven development". For every functionality there needs to be a proper test case that shows the correctness of the implementation (in black box testing). This might be tedious for these simple function, but when you start developing more complex software, good unit tests become very valuable.
I suggest sth like this:
full_type = SUBSCRIPTION;
reduced_type = rclc_executor_handle_reduced_type(full_type);
EXPECT_EQ(reduced_type, SUBSCRIPTION << "expected SUBSCRIPTION";
full_type = SUBSCRIPTION_WITH_CONTEXT;
reduced_type = rclc_executor_handle_reduced_type(full_type);
EXPECT_EQ(reduced_type, SUBSCRIPTION << "expected SUBSCRIPTION";
full_type = TIMER;
reduced_type = rclc_executor_handle_reduced_type(full_type);
EXPECT_EQ(reduced_type, TIMER << "expected TIMER";
full_type = CLIENT;
reduced_type = rclc_executor_handle_reduced_type(full_type);
EXPECT_EQ(reduced_type, CLIENT << "expected CLIENT";
full_type = CLIENT_WITH_REQ_ID;
reduced_type = rclc_executor_handle_reduced_type(full_type);
EXPECT_EQ(reduced_type, CLIENT << "expected 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.
I didn't want to put in an exhaustive test because an exhaustive test would look complete but (being a re-implementaton) would pass on all of the most likely failure cases, especially New types.
codecov only flagged the default
case as unexplored, and strict idempotency is that case's strongest property.
I'll add some more cases and explain that better in the test code.
EXPECT_EQ(RCL_RET_OK, rc) << rcl_get_error_string().str; | ||
} | ||
|
||
|
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.
LGTM, these test cases look great to me. Please also add test cases for client, service, timer, gc. I guess, you can very easily copy-and-paste these test cases to be used for other rcl_handles.
Signed-off-by: BrettRD <brettrd@brettrd.com>
@BrettRD are you still working on this pull request? |
I had to abandon micro-ros and ESP32 as a platform because the data handling implementations were too inefficient. This feature allows a subscriber to loan memory to the executor, and zero-copy move the subscription data into the esp-adf. I believe the requested changes to the tests are complete. |
Following #186 This PR adds:
rclc_executor_remove_subscription()
rclc_executor_add_client_with_context
andrclc_executor_add_guard_condition_with_context
API additions:
replace a message pointer when only the message pointer is known (useful for context-free callbacks)
rclc_executor_swap_subscription_message
rclc_executor_swap_client_response
rclc_executor_swap_service_request
change a message pointer when the rcl handle is known (preferred)
rclc_executor_change_subscription_message
rclc_executor_change_client_response
rclc_executor_change_service_request
Omissions:
rclc_executor_swap_service_response
requires slightly different machinery, and will be left out of this pull requestRemaining work:
The doc strings need adjustmentsto show some thread-safety caveats (run during respective callbacks or not during spin)The tests still need writing, as do regression teststo demonstrate the previous type-safety flawpreviously:
after writing this, I've realised that similar performance can come from changing only the variable-sized parts of a message without needing to interact with the executor, but this API still has some utility for complex nested messages