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 function for waiting on multiple wait sets together #330

Closed
wants to merge 9 commits into from

Conversation

jacobperron
Copy link
Member

Internally, the function collates all wait sets into a single wait set that is waited on.

Resolves #308

Internally, the function collates all wait sets into a single wait set that is waited on.
@jacobperron jacobperron added the in progress Actively being worked on (Kanban column) label Nov 14, 2018
@jacobperron
Copy link
Member Author

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

@jacobperron jacobperron added in review Waiting for review (Kanban column) and removed in progress Actively being worked on (Kanban column) labels Nov 14, 2018
Copy link
Contributor

@hidmic hidmic left a comment

Choose a reason for hiding this comment

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

Looking good! Left a few comments.

@@ -428,6 +427,119 @@ RCL_WARN_UNUSED
rcl_ret_t
rcl_wait(rcl_wait_set_t * wait_set, int64_t timeout);

/// Block until multiple wait sets are ready or until the timeout has been exceeded.
/**
* This function has similar behavior as rcl_wait(), but acts on multiple wait sets.
Copy link
Contributor

Choose a reason for hiding this comment

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

@jacobperron nit: similar behavior to?

@@ -435,3 +435,28 @@ TEST_F(CLASSNAME(WaitSetTestFixture, RMW_IMPLEMENTATION), guard_condition) {
EXPECT_EQ(RCL_RET_OK, f.get());
EXPECT_LE(std::abs(diff - trigger_diff.count()), TOLERANCE);
}

// Test rcl_wait_multiple with a positive finite timeout value (1ms)
TEST_F(CLASSNAME(WaitSetTestFixture, RMW_IMPLEMENTATION), finite_timeout_multiple) {
Copy link
Contributor

Choose a reason for hiding this comment

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

@jacobperron shouldn't we add a few more tests? Like, for negative timeout, for zero timeout, for invalid arguments, for invalid wait sets, with a dummy guard and a dummy timer in different wait sets to make sure things are being collated and then updated back properly, and so on. Not to repeat rcl_wait() tests, just to verify the new functionality is correct.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, I meant to, but forgot.

size_t number_of_timers = 0u;
size_t number_of_clients = 0u;
size_t number_of_services = 0u;
for (i = 0u; i < num_wait_sets; ++i) {
Copy link
Contributor

Choose a reason for hiding this comment

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

@jacobperron it could be that no wait set was provided, should we have a different rcl error message for that case (right now it'll say that wait sets are empty)?

Copy link
Member Author

Choose a reason for hiding this comment

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

My intention was to return RCL_RET_WAIT_SET_EMPTY thinking it is intuitive, but I can add a new return code e.g. RCL_RET_WAIT_SET_NONE.

Copy link
Contributor

Choose a reason for hiding this comment

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

Either that or RCL_RET_INVALID_ARGUMENT plus RCL_SET_ERROR_MSG() with a nice message.

number_of_clients == 0u &&
number_of_services == 0u)
{
RCL_SET_ERROR_MSG("wait set is empty");
Copy link
Contributor

Choose a reason for hiding this comment

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

@jacobperron consider "wait sets are empty" instead.

{
RCL_CHECK_ARGUMENT_FOR_NULL(wait_sets, RCL_RET_INVALID_ARGUMENT);
size_t i;
size_t j;
Copy link
Contributor

Choose a reason for hiding this comment

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

@jacobperron nit: I have a slight preference for declaring loop variables within the for statement. Won't block if you prefer to keep things C89-ish.

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 guess my head was in C89 mode. Since we're targeting C99, I'm happy to change it.

Copy link
Member Author

@jacobperron jacobperron Nov 15, 2018

Choose a reason for hiding this comment

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

I see now why I implemented this way. The existing code in this file follows a similar style:

rcl/rcl/src/rcl/wait.c

Lines 479 to 480 in f256da0

{ // scope to prevent i from colliding below
uint64_t i = 0;

size_t i;

Although, as a result there's that awkward scope to prevent variable name collisions... Maybe we should change those instances as well for consistency.

rcl_guard_condition_t * guard_condition = rcl_timer_get_guard_condition(wait_set->timers[j]);
if (NULL != guard_condition) {
// rcl_wait() will take care of moving these backwards and setting guard_condition_count.
const size_t index = wait_set->size_of_guard_conditions + (wait_set->impl->timer_index - 1);
Copy link
Contributor

Choose a reason for hiding this comment

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

@jacobperron not sure I follow how index would take different values across multiple iterations for the same wait_set.

Copy link
Member Author

Choose a reason for hiding this comment

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

It won't, this is a copy-paste bug.

size_t collated_service_index = 0u;
for (i = 0u; i < num_wait_sets; ++i) {
wait_set = &wait_sets[i];
for (j = 0u; j < wait_set->size_of_timers; ++j) {
Copy link
Contributor

Choose a reason for hiding this comment

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

@jacobperron nit: fine to use for loops, but we could use memcpy too (that depending on the compiler and target architecture may be more performant).

Copy link
Member Author

@jacobperron jacobperron Nov 15, 2018

Choose a reason for hiding this comment

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

Sure, I can do that. Do you think there are any issues with readability (and correctness)?

    memcpy(
      wait_set->timers,
      collated_wait_set.timers + collated_timer_index,
      sizeof(rcl_timer_t *) * wait_set->size_of_timers);

Copy link
Contributor

Choose a reason for hiding this comment

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

Ugh, that doesn't look nearly as clean as your current code. Nevermind, let's trust loop unrolling will work :).

Copy link
Member Author

Choose a reason for hiding this comment

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

👍

@jacobperron
Copy link
Member Author

@hidmic Thanks for the review! I think I've address all of your comments. Ready for another round of review.

@jacobperron
Copy link
Member Author

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

if (0u == num_wait_sets) {
RCL_SET_ERROR_MSG("number of wait sets must be greater than zero");
return RCL_RET_INVALID_ARGUMENT;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Could take a short cut and return rcl_wait() if 1u == num_wait_sets

rcl/src/rcl/wait.c Outdated Show resolved Hide resolved
ret = rcl_wait_set_init(&wait_sets[i], 0, 1, 1, 0, 0, rcl_get_default_allocator());
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str;

// Add a dummy guard condition to avoid an error
Copy link
Contributor

Choose a reason for hiding this comment

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

What error is the dummy guard condition avoiding?

Copy link
Member Author

Choose a reason for hiding this comment

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

That is a good question. I copied this over from the existing test involving a timer, without it the test segfaults 😨

I dug into it a bit more, and it seems like the rmw guard condition count isn't being incremented in rcl_wait_set_add_timer(). Adding a dummy guard condition appears to be a hack to get the count to increment...

I think it can be fixed easily by ensuring the rmw guard condition count is incremented when adding timers, but without understanding the reason for the current implementation I am hesitant to change it. @wjwwood, is this a bug or intentional?

Copy link
Member

Choose a reason for hiding this comment

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

I don't know where it was copied from, but here maybe the error could be avoided if you also update the values passed to rcl_wait_set_init() above?

Copy link
Member Author

@jacobperron jacobperron Nov 16, 2018

Choose a reason for hiding this comment

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

Nevermind, I see that the rmw guard condition count is incremented for timers in rcl_wait as the comment says (my bad):

++(rmw_gcs->guard_condition_count);

Setting the number of guard conditions to zero resolves the issue. I've also updated the tests for rcl_wait() that originally confused me. See e40a26b

@jacobperron
Copy link
Member Author

Reviews addressed.

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

@hidmic
Copy link
Contributor

hidmic commented Nov 20, 2018

@jacobperron are we pushing this one as-is or are you considering a re-implementation in terms of the feature introduced by #335 ?

@jacobperron
Copy link
Member Author

are we pushing this one as-is or are you considering a re-implementation in terms of the feature introduced by #335 ?

@hidmic I think this feature is not necessary anymore in favor of #335. If you agree, I will close this PR.

@hidmic
Copy link
Contributor

hidmic commented Nov 20, 2018

Makes total sense to me, let's close it.

@jacobperron jacobperron removed the in review Waiting for review (Kanban column) label Nov 20, 2018
@jacobperron jacobperron deleted the jacob/wait_multiple branch April 4, 2019 14:42
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.

5 participants