-
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
Wait set groups #308
Comments
@sloretz Lines 38 to 57 in a2546fa
Rather than defining a new concept of a wait set group, maybe we could instead implement a second wait function that takes an array of rcl_wait_multiple(rcl_wait_set_t ** wait_sets, const size_t num_wait_sets, int64_t timeout); I think this gives us the same capabilities but with less plumbing. What do you think? |
@jacobperron That could work. One thing to be aware of is the wait sets need to be consolidated before being passed to Lines 543 to 549 in a2546fa
Maybe |
I guess we shouldn't modify the size of one of the input wait sets since the caller will check that after waiting. It doesn't seem like too much overhead to create a new wait set for the scope of the function where we collect all entities. Then we can call |
@jacobperron @sloretz I was just looking into rcl_ret_t rcl_wait_set_group(const rcl_wait_set_t ** wait_sets, const size_t num_wait_sets, rcl_wait_set_t * grouped_wait_set);
rcl_ret_t rcl_wait_set_ungroup(const rcl_wait_set_t * grouped_wait_set, rcl_wait_set_t ** wait_sets, const size_t num_wait_sets); Essentially splitting up the function @jacobperron wrote in three separate steps: grouping, waiting, ungrouping. I believe it'll make adapting client libraries simpler, see here and here. |
@hidmic is it possible to provide an example/pseudocode of how you imagine these functions could be used in the context of actions? |
@jacobperron so, for a generic example, I'll just reuse the one you wrote for #include <rcl/rcl.h>
// rcl_init() called successfully before here...
rcl_node_t node; // initialize this, see rcl_node_init()
rcl_subscription_t sub1; // initialize this, see rcl_subscription_init()
rcl_subscription_t sub2; // initialize this, see rcl_subscription_init()
rcl_guard_condition_t gc1; // initialize this, see rcl_guard_condition_init()
rcl_wait_set_t wait_sets[2]; // two wait sets in this example
wait_sets[0] = rcl_get_zero_initialized_wait_set();
wait_sets[1] = rcl_get_zero_initialized_wait_set();
rcl_ret_t ret = rcl_wait_set_init(&wait_sets[0] 1, 0, 0, 0, 0, rcl_get_default_allocator());
// ... error handling
ret = rcl_wait_set_init(&wait_sets[1] 1, 1, 0, 0, 0, rcl_get_default_allocator());
// ... error handling
do {
ret = rcl_wait_set_clear(&wait_sets[0]);
// ... error handling
ret = rcl_wait_set_add_subscription(&wait_sets[0], &sub1);
// ... error handling
ret = rcl_wait_set_clear(&wait_sets[1]);
// ... error handling
ret = rcl_wait_set_add_subscription(&wait_sets[1], &sub2);
// ... error handling
ret = rcl_wait_set_add_guard_condition(&wait_sets[1], &gc1);
// ... error handling
rcl_wait_set_t grouped_wait_set = rcl_get_zero_initialized_wait_set();
ret = rcl_wait_set_group(wait_sets, 2,&grouped_wait_set);
// ... error handling
ret = rcl_wait(grouped_wait_set, RCL_MS_TO_NS(1000)); // 1000ms == 1s, passed as ns
if (ret == RCL_RET_TIMEOUT) {
continue;
}
ret = rcl_wait_set_ungroup(&grouped_wait_set, wait_sets, 2);
// ... error handling
if (wait_sets[0].subscriptions[0]) {
// The subscription in the first wait set is ready...
}
if (wait_sets[1].subscriptions[0]) {
// The subscription in the second wait set is ready...
}
if (wait_sets[1].guard_conditions[0]) {
// The guard condition in the first wait set is ready...
}
} while(check_some_condition());
// ... fini node, and subscriptions and guard conditions...
ret = rcl_wait_set_fini(&wait_sets[0]);
// ... error handling
ret = rcl_wait_set_fini(&wait_sets[1]);
// ... error handling The real benefit comes when updating Actually, we can keep rcl_ret_t rcl_wait_multiple(rcl_wait_set_t wait_sets[], const size_t num_wait_sets, int64_t timeout) {
rcl_wait_set_t grouped_wait_set = rcl_get_zero_initialized_wait_set();
rcl_ret_t ret = rcl_wait_set_group(wait_sets, num_wait_sets, &grouped_wait_set);
// ... error handling
ret = rcl_wait(&group_wait_set, timeout);
// ... error handling
ret = rcl_wait_set_ungroup(&grouped_wait_set, wait_sets, num_wait_sets);
// ... error handling
return ret;
} |
Not a something we must do, I just thought it may be helpful considering API freeze is next Wednesday. |
Yeah, it doesn't seem like a bad idea. It might help with readability too since |
@hidmic Nevermind, I guess it only works if the input array of wait sets is the same as when rcl_wait_set_group was called. |
Yeap, it builds on top of the same underlying assumptions as |
As an alternative solution for working with actions and wait sets, we can instead modify the existing wait set API to return the index of the entity in the wait set when it is added. For example, adding a service: rcl_ret_t
rcl_wait_set_add_service(
rcl_wait_set_t * wait_set,
const rcl_service_t * service,
size_t * index); Then, the action server/client can have an implementation like so: rcl_ret_t
rcl_action_wait_set_add_action_server(
rcl_wait_set_t * wait_set,
const rcl_action_server_t * action_server)
{
// ... input error handling
rcl_ret_t ret = rcl_wait_set_add_service(
wait_set,
&action_server->impl->goal_service,
&action_server->impl->wait_set_goal_service_index);
// ... error handling
ret = rcl_wait_set_add_service(
wait_set,
&action_server->impl->cancel_service,
&action_server->impl->wait_set_cancel_service_index);
// ... error handling
ret = rcl_wait_set_add_service(
wait_set,
&action_server->impl->result_service,
&action_server->impl->wait_set_result_service_index);
// ... error handling
return RCL_RET_OK;
}
rcl_ret_t
rcl_action_server_wait_set_get_entities_ready(
const rcl_wait_set_t * wait_set,
const rcl_action_server_t * action_server,
bool * is_goal_request_ready,
bool * is_cancel_request_ready,
bool * is_result_request_ready)
{
// ... input error handling
const size_t goal_service_index = action_server->impl->wait_set_goal_service_index;
const size_t cancel_service_index = action_server->impl->wait_set_cancel_service_index;
const size_t result_service_index = action_server->impl->wait_set_result_service_index;
*is_goal_request_ready = (NULL != wait_set->services[goal_service_index]);
*is_cancel_request_ready = (NULL != wait_set->services[cancel_service_index]);
*is_result_request_ready = (NULL != wait_set->services[result_service_index]);
return RCL_RET_OK;
} |
Feature request
This is a request for an API on top of the current waitset api that considers groups of waitable entities.
Feature description
An API that covers groups of entities that can be waited on would be immediately useful for actions. A server and client consists of several waitable entities (servers, clients, maybe a timer). The API would allow a client library to add a group of waitable entities to the wait set, and then notify the action server when one of them becomes available.
Implementation considerations
This could be implemented in a package
rcl_grouped_waitset
on top of the existingrcl
APIStructs and methods to be added.
Example use
connects to #307
The text was updated successfully, but these errors were encountered: