Skip to content

Commit f6d11a7

Browse files
committed
Handle action server/client readiness in WaitSet and executor
Currently, action servers and clients that are ready in multiple ways are returned to the executor as a list of pairs, with one readiness mode per entry. This could alternatively be encoded as a bitfield or similar struct, with any given client/server only occurring once in the list. However, to ensure that the executor has control over execution order, we would need to expose individual `execute_readiness_mode()` methods from the client and server, rather than a unified `execute(Mode)` method. That's fine too, but something to keep in mind.
1 parent 98eef19 commit f6d11a7

File tree

5 files changed

+153
-9
lines changed

5 files changed

+153
-9
lines changed

rclrs/src/action.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
mod client;
1+
pub(crate) mod client;
22
pub(crate) mod server;
33
mod server_goal_handle;
44

rclrs/src/action/client.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,18 @@ impl Drop for ActionClientHandle {
4848
pub trait ActionClientBase: Send + Sync {
4949
/// Internal function to get a reference to the `rcl` handle.
5050
fn handle(&self) -> &ActionClientHandle;
51+
/// Returns the number of underlying entities for the action client.
5152
fn num_entities(&self) -> &WaitableNumEntities;
52-
// /// Tries to take a new request and run the callback with it.
53-
// fn execute(&self) -> Result<(), RclrsError>;
53+
/// Tries to run the callback for the given readiness mode.
54+
fn execute(&self, mode: ReadyMode) -> Result<(), RclrsError>;
55+
}
56+
57+
pub(crate) enum ReadyMode {
58+
Feedback,
59+
Status,
60+
GoalResponse,
61+
CancelResponse,
62+
ResultResponse,
5463
}
5564

5665
pub struct ActionClient<ActionT>
@@ -143,4 +152,8 @@ where
143152
fn num_entities(&self) -> &WaitableNumEntities {
144153
&self.num_entities
145154
}
155+
156+
fn execute(&self, mode: ReadyMode) -> Result<(), RclrsError> {
157+
todo!()
158+
}
146159
}

rclrs/src/action/server.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,17 @@ impl Drop for ActionServerHandle {
5050
pub trait ActionServerBase: Send + Sync {
5151
/// Internal function to get a reference to the `rcl` handle.
5252
fn handle(&self) -> &ActionServerHandle;
53+
/// Returns the number of underlying entities for the action server.
5354
fn num_entities(&self) -> &WaitableNumEntities;
54-
// /// Tries to take a new request and run the callback with it.
55-
// fn execute(&self) -> Result<(), RclrsError>;
55+
/// Tries to run the callback for the given readiness mode.
56+
fn execute(&self, mode: ReadyMode) -> Result<(), RclrsError>;
57+
}
58+
59+
pub(crate) enum ReadyMode {
60+
GoalRequest,
61+
CancelRequest,
62+
ResultRequest,
63+
GoalExpired,
5664
}
5765

5866
pub type GoalCallback<ActionT> = dyn Fn(GoalUuid, <ActionT as rosidl_runtime_rs::Action>::Goal) -> GoalResponse + 'static + Send + Sync;
@@ -163,4 +171,8 @@ where
163171
fn num_entities(&self) -> &WaitableNumEntities {
164172
&self.num_entities
165173
}
174+
175+
fn execute(&self, mode: ReadyMode) -> Result<(), RclrsError> {
176+
todo!()
177+
}
166178
}

rclrs/src/executor.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,14 @@ impl Executor {
7676
for ready_service in ready_entities.services {
7777
ready_service.execute()?;
7878
}
79+
80+
for (ready_action_client, mode) in ready_entities.action_clients {
81+
ready_action_client.execute(mode)?;
82+
}
83+
84+
for (ready_action_server, mode) in ready_entities.action_servers {
85+
ready_action_server.execute(mode)?;
86+
}
7987
}
8088

8189
// Clear out any nodes that have been dropped.

rclrs/src/wait.rs

Lines changed: 115 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818
use std::{sync::Arc, time::Duration, vec::Vec};
1919

2020
use crate::{
21+
action::{
22+
client::ReadyMode as ActionClientReadyMode, server::ReadyMode as ActionServerReadyMode,
23+
},
2124
error::{to_rclrs_result, RclReturnCode, RclrsError, ToResult},
2225
rcl_bindings::*,
2326
ActionClientBase, ActionServerBase, ClientBase, Context, ContextHandle, Node, ServiceBase,
@@ -66,6 +69,10 @@ pub struct ReadyEntities {
6669
pub guard_conditions: Vec<Arc<GuardCondition>>,
6770
/// A list of services that have potentially received requests.
6871
pub services: Vec<Arc<dyn ServiceBase>>,
72+
/// A list of action clients and the ways in which they are ready.
73+
pub action_clients: Vec<(Arc<dyn ActionClientBase>, ActionClientReadyMode)>,
74+
/// A list of action servers and the ways in which they are ready.
75+
pub action_servers: Vec<(Arc<dyn ActionServerBase>, ActionServerReadyMode)>,
6976
}
7077

7178
impl Drop for rcl_wait_set_t {
@@ -156,8 +163,12 @@ impl WaitSet {
156163
let mut num_services = live_services.len();
157164
let mut num_events = 0;
158165

159-
let action_client_entities = live_action_clients.iter().map(|client| client.num_entities());
160-
let action_server_entities = live_action_servers.iter().map(|server| server.num_entities());
166+
let action_client_entities = live_action_clients
167+
.iter()
168+
.map(|client| client.num_entities());
169+
let action_server_entities = live_action_servers
170+
.iter()
171+
.map(|server| server.num_entities());
161172
for num_entities in action_client_entities.chain(action_server_entities) {
162173
num_subscriptions += num_entities.num_subscriptions;
163174
num_timers += num_entities.num_timers;
@@ -451,8 +462,9 @@ impl WaitSet {
451462
};
452463
// SAFETY: The comments in rcl mention "This function cannot operate on the same wait set
453464
// in multiple threads, and the wait sets may not share content."
454-
// We cannot currently guarantee that the wait sets may not share content, but it is
455-
// mentioned in the doc comment for `add_subscription`.
465+
// By taking exclusive ownership of `self`, we can guarantee that the wait set is not in
466+
// use from another thread. We guarantee that waits sets may not share content using
467+
// `ExclusivityGuard`s on each entity added.
456468
// Also, the rcl_wait_set is obviously valid.
457469
match unsafe { rcl_wait(&mut self.handle.rcl_wait_set, timeout_ns) }.ok() {
458470
Ok(_) => (),
@@ -469,6 +481,8 @@ impl WaitSet {
469481
clients: Vec::new(),
470482
guard_conditions: Vec::new(),
471483
services: Vec::new(),
484+
action_clients: Vec::new(),
485+
action_servers: Vec::new(),
472486
};
473487
for (i, subscription) in self.subscriptions.iter().enumerate() {
474488
// SAFETY: The `subscriptions` entry is an array of pointers, and this dereferencing is
@@ -513,6 +527,103 @@ impl WaitSet {
513527
ready_entities.services.push(Arc::clone(&service.waitable));
514528
}
515529
}
530+
531+
for action_client in &self.action_clients {
532+
let mut is_feedback_ready = false;
533+
let mut is_status_ready = false;
534+
let mut is_goal_response_ready = false;
535+
let mut is_cancel_response_ready = false;
536+
let mut is_result_response_ready = false;
537+
// SAFETY: The wait set is exclusively owned by this function, which guarantees thread
538+
// safety.
539+
unsafe {
540+
rcl_action_client_wait_set_get_entities_ready(
541+
&self.handle.rcl_wait_set,
542+
&*action_client.waitable.handle().lock(),
543+
&mut is_feedback_ready,
544+
&mut is_status_ready,
545+
&mut is_goal_response_ready,
546+
&mut is_cancel_response_ready,
547+
&mut is_result_response_ready,
548+
)
549+
.ok()?;
550+
}
551+
if is_feedback_ready {
552+
ready_entities.action_clients.push((
553+
Arc::clone(&action_client.waitable),
554+
ActionClientReadyMode::Feedback,
555+
));
556+
}
557+
if is_status_ready {
558+
ready_entities.action_clients.push((
559+
Arc::clone(&action_client.waitable),
560+
ActionClientReadyMode::Status,
561+
));
562+
}
563+
if is_goal_response_ready {
564+
ready_entities.action_clients.push((
565+
Arc::clone(&action_client.waitable),
566+
ActionClientReadyMode::GoalResponse,
567+
));
568+
}
569+
if is_cancel_response_ready {
570+
ready_entities.action_clients.push((
571+
Arc::clone(&action_client.waitable),
572+
ActionClientReadyMode::CancelResponse,
573+
));
574+
}
575+
if is_result_response_ready {
576+
ready_entities.action_clients.push((
577+
Arc::clone(&action_client.waitable),
578+
ActionClientReadyMode::ResultResponse,
579+
));
580+
}
581+
}
582+
583+
for action_server in &self.action_servers {
584+
let mut is_goal_request_ready = false;
585+
let mut is_cancel_request_ready = false;
586+
let mut is_result_request_ready = false;
587+
let mut is_goal_expired = false;
588+
// SAFETY: The wait set is exclusively owned by this function, which guarantees thread
589+
// safety.
590+
unsafe {
591+
rcl_action_server_wait_set_get_entities_ready(
592+
&self.handle.rcl_wait_set,
593+
&*action_server.waitable.handle().lock(),
594+
&mut is_goal_request_ready,
595+
&mut is_cancel_request_ready,
596+
&mut is_result_request_ready,
597+
&mut is_goal_expired,
598+
)
599+
.ok()?;
600+
}
601+
if is_goal_request_ready {
602+
ready_entities.action_servers.push((
603+
Arc::clone(&action_server.waitable),
604+
ActionServerReadyMode::GoalRequest,
605+
));
606+
}
607+
if is_cancel_request_ready {
608+
ready_entities.action_servers.push((
609+
Arc::clone(&action_server.waitable),
610+
ActionServerReadyMode::CancelRequest,
611+
));
612+
}
613+
if is_result_request_ready {
614+
ready_entities.action_servers.push((
615+
Arc::clone(&action_server.waitable),
616+
ActionServerReadyMode::ResultRequest,
617+
));
618+
}
619+
if is_goal_expired {
620+
ready_entities.action_servers.push((
621+
Arc::clone(&action_server.waitable),
622+
ActionServerReadyMode::GoalExpired,
623+
));
624+
}
625+
}
626+
516627
Ok(ready_entities)
517628
}
518629
}

0 commit comments

Comments
 (0)