Skip to content

Commit dbdebd1

Browse files
committed
Switch to shared state for action client/server
This matches the pattern used by the other endpoint structs.
1 parent fb06971 commit dbdebd1

File tree

5 files changed

+90
-34
lines changed

5 files changed

+90
-34
lines changed

rclrs/src/action.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ mod server_goal_handle;
55
use crate::rcl_bindings::RCL_ACTION_UUID_SIZE;
66
use std::fmt;
77

8-
pub use client::{ActionClient, ActionClientBase};
9-
pub use server::{ActionServer, ActionServerBase};
8+
pub use client::{ActionClient, ActionClientBase, ActionClientState};
9+
pub use server::{ActionServer, ActionServerBase, ActionServerState};
1010
pub use server_goal_handle::ServerGoalHandle;
1111

1212
/// A unique identifier for a goal request.

rclrs/src/action/client.rs

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::{
2-
error::ToResult, rcl_bindings::*, wait::WaitableNumEntities, Node, RclrsError,
2+
error::ToResult, rcl_bindings::*, wait::WaitableNumEntities, Node, NodeHandle, RclrsError,
33
ENTITY_LIFECYCLE_MUTEX,
44
};
55
use std::{
@@ -19,7 +19,7 @@ unsafe impl Send for rcl_action_client_t {}
1919
/// [1]: <https://doc.rust-lang.org/reference/destructors.html>
2020
pub struct ActionClientHandle {
2121
rcl_action_client: Mutex<rcl_action_client_t>,
22-
node: Node,
22+
node_handle: Arc<NodeHandle>,
2323
pub(crate) in_use_by_wait_set: Arc<AtomicBool>,
2424
}
2525

@@ -32,7 +32,7 @@ impl ActionClientHandle {
3232
impl Drop for ActionClientHandle {
3333
fn drop(&mut self) {
3434
let rcl_action_client = self.rcl_action_client.get_mut().unwrap();
35-
let mut rcl_node = self.node.handle.rcl_node.lock().unwrap();
35+
let mut rcl_node = self.node_handle.rcl_node.lock().unwrap();
3636
let _lifecycle_lock = ENTITY_LIFECYCLE_MUTEX.lock().unwrap();
3737
// SAFETY: The entity lifecycle mutex is locked to protect against the risk of
3838
// global variables in the rmw implementation being unsafely modified during cleanup.
@@ -62,16 +62,41 @@ pub(crate) enum ReadyMode {
6262
ResultResponse,
6363
}
6464

65-
pub struct ActionClient<ActionT>
65+
///
66+
/// Main class responsible for sending goals to a ROS action server.
67+
///
68+
/// Create a client using [`Node::create_action_client`][1].
69+
///
70+
/// Receiving feedback and results requires the node's executor to [spin][2].
71+
///
72+
/// [1]: crate::NodeState::create_action_client
73+
/// [2]: crate::spin
74+
pub type ActionClient<ActionT> = Arc<ActionClientState<ActionT>>;
75+
76+
/// The inner state of an [`ActionClient`].
77+
///
78+
/// This is public so that you can choose to create a [`Weak`][1] reference to it
79+
/// if you want to be able to refer to an [`ActionClient`] in a non-owning way. It is
80+
/// generally recommended to manage the `ActionClientState` inside of an [`Arc`],
81+
/// and [`ActionClient`] is provided as a convenience alias for that.
82+
///
83+
/// The public API of the [`ActionClient`] type is implemented via `ActionClientState`.
84+
///
85+
/// [1]: std::sync::Weak
86+
pub struct ActionClientState<ActionT>
6687
where
6788
ActionT: rosidl_runtime_rs::Action,
6889
{
6990
_marker: PhantomData<fn() -> ActionT>,
7091
pub(crate) handle: Arc<ActionClientHandle>,
7192
num_entities: WaitableNumEntities,
93+
/// Ensure the parent node remains alive as long as the subscription is held.
94+
/// This implementation will change in the future.
95+
#[allow(unused)]
96+
node: Node,
7297
}
7398

74-
impl<T> ActionClient<T>
99+
impl<T> ActionClientState<T>
75100
where
76101
T: rosidl_runtime_rs::Action,
77102
{
@@ -97,7 +122,7 @@ where
97122

98123
// SAFETY:
99124
// * The rcl_action_client was zero-initialized as expected by this function.
100-
// * The rcl_node is kept alive by the Node because it is a dependency of the action client.
125+
// * The rcl_node is kept alive by the NodeHandle because it is a dependency of the action client.
101126
// * The topic name and the options are copied by this function, so they can be dropped
102127
// afterwards.
103128
// * The entity lifecycle mutex is locked to protect against the risk of global
@@ -116,7 +141,7 @@ where
116141

117142
let handle = Arc::new(ActionClientHandle {
118143
rcl_action_client: Mutex::new(rcl_action_client),
119-
node: node.clone(),
144+
node_handle: Arc::clone(&node.handle),
120145
in_use_by_wait_set: Arc::new(AtomicBool::new(false)),
121146
});
122147

@@ -137,6 +162,7 @@ where
137162
_marker: Default::default(),
138163
handle,
139164
num_entities,
165+
node: Arc::clone(node),
140166
})
141167
}
142168

@@ -161,7 +187,7 @@ where
161187
}
162188
}
163189

164-
impl<T> ActionClientBase for ActionClient<T>
190+
impl<T> ActionClientBase for ActionClientState<T>
165191
where
166192
T: rosidl_runtime_rs::Action,
167193
{

rclrs/src/action/server.rs

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::{
33
error::{RclReturnCode, ToResult},
44
rcl_bindings::*,
55
wait::WaitableNumEntities,
6-
Clock, DropGuard, Node, RclrsError, ENTITY_LIFECYCLE_MUTEX,
6+
Clock, DropGuard, Node, NodeHandle, RclrsError, ENTITY_LIFECYCLE_MUTEX,
77
};
88
use rosidl_runtime_rs::{Action, ActionImpl, Message, Service};
99
use std::{
@@ -23,7 +23,7 @@ unsafe impl Send for rcl_action_server_t {}
2323
/// [1]: <https://doc.rust-lang.org/reference/destructors.html>
2424
pub struct ActionServerHandle {
2525
rcl_action_server: Mutex<rcl_action_server_t>,
26-
node: Node,
26+
node_handle: Arc<NodeHandle>,
2727
pub(crate) in_use_by_wait_set: Arc<AtomicBool>,
2828
}
2929

@@ -36,7 +36,7 @@ impl ActionServerHandle {
3636
impl Drop for ActionServerHandle {
3737
fn drop(&mut self) {
3838
let rcl_action_server = self.rcl_action_server.get_mut().unwrap();
39-
let mut rcl_node = self.node.handle.rcl_node.lock().unwrap();
39+
let mut rcl_node = self.node_handle.rcl_node.lock().unwrap();
4040
let _lifecycle_lock = ENTITY_LIFECYCLE_MUTEX.lock().unwrap();
4141
// SAFETY: The entity lifecycle mutex is locked to protect against the risk of
4242
// global variables in the rmw implementation being unsafely modified during cleanup.
@@ -69,7 +69,33 @@ pub type GoalCallback<ActionT> = dyn Fn(GoalUuid, <ActionT as rosidl_runtime_rs:
6969
pub type CancelCallback<ActionT> = dyn Fn(Arc<ServerGoalHandle<ActionT>>) -> CancelResponse + 'static + Send + Sync;
7070
pub type AcceptedCallback<ActionT> = dyn Fn(Arc<ServerGoalHandle<ActionT>>) + 'static + Send + Sync;
7171

72-
pub struct ActionServer<ActionT>
72+
/// An action server that can respond to requests sent by ROS action clients.
73+
///
74+
/// Create an action server using [`Node::create_action_server`][1].
75+
///
76+
/// ROS only supports having one server for any given fully-qualified
77+
/// action name. "Fully-qualified" means the namespace is also taken into account
78+
/// for uniqueness. A clone of an `ActionServer` will refer to the same server
79+
/// instance as the original. The underlying instance is tied to [`ActionServerState`]
80+
/// which implements the [`ActionServer`] API.
81+
///
82+
/// Responding to requests requires the node's executor to [spin][2].
83+
///
84+
/// [1]: crate::NodeState::create_action_server
85+
/// [2]: crate::spin
86+
pub type ActionServer<ActionT> = Arc<ActionServerState<ActionT>>;
87+
88+
/// The inner state of an [`ActionServer`].
89+
///
90+
/// This is public so that you can choose to create a [`Weak`][1] reference to it
91+
/// if you want to be able to refer to a [`ActionServer`] in a non-owning way. It is
92+
/// generally recommended to manage the `ActionServerState` inside of an [`Arc`],
93+
/// and [`ActionServer`] is provided as a convenience alias for that.
94+
///
95+
/// The public API of the [`ActionServer`] type is implemented via `ActionServerState`.
96+
///
97+
/// [1]: std::sync::Weak
98+
pub struct ActionServerState<ActionT>
7399
where
74100
ActionT: rosidl_runtime_rs::Action + rosidl_runtime_rs::ActionImpl,
75101
{
@@ -83,16 +109,19 @@ where
83109
goal_handles: Mutex<HashMap<GoalUuid, Arc<ServerGoalHandle<ActionT>>>>,
84110
goal_results: Mutex<HashMap<GoalUuid, <<ActionT::GetResultService as Service>::Response as Message>::RmwMsg>>,
85111
result_requests: Mutex<HashMap<GoalUuid, Vec<rmw_request_id_t>>>,
112+
/// Ensure the parent node remains alive as long as the subscription is held.
113+
/// This implementation will change in the future.
114+
#[allow(unused)]
115+
node: Node,
86116
}
87117

88-
impl<T> ActionServer<T>
118+
impl<T> ActionServerState<T>
89119
where
90120
T: rosidl_runtime_rs::Action + rosidl_runtime_rs::ActionImpl,
91121
{
92122
/// Creates a new action server.
93123
pub(crate) fn new(
94124
node: &Node,
95-
clock: Clock,
96125
topic: &str,
97126
goal_callback: impl Fn(GoalUuid, T::Goal) -> GoalResponse + 'static + Send + Sync,
98127
cancel_callback: impl Fn(Arc<ServerGoalHandle<T>>) -> CancelResponse + 'static + Send + Sync,
@@ -114,13 +143,14 @@ where
114143

115144
{
116145
let mut rcl_node = node.handle.rcl_node.lock().unwrap();
146+
let clock = node.get_clock();
117147
let rcl_clock = clock.rcl_clock();
118148
let mut rcl_clock = rcl_clock.lock().unwrap();
119149
let _lifecycle_lock = ENTITY_LIFECYCLE_MUTEX.lock().unwrap();
120150

121151
// SAFETY:
122152
// * The rcl_action_server is zero-initialized as mandated by this function.
123-
// * The rcl_node is kept alive by the Node because it is a dependency of the action server.
153+
// * The rcl_node is kept alive by the NodeHandle because it is a dependency of the action server.
124154
// * The topic name and the options are copied by this function, so they can be dropped
125155
// afterwards.
126156
// * The entity lifecycle mutex is locked to protect against the risk of global
@@ -140,7 +170,7 @@ where
140170

141171
let handle = Arc::new(ActionServerHandle {
142172
rcl_action_server: Mutex::new(rcl_action_server),
143-
node: node.clone(),
173+
node_handle: Arc::clone(&node.handle),
144174
in_use_by_wait_set: Arc::new(AtomicBool::new(false)),
145175
});
146176

@@ -166,6 +196,7 @@ where
166196
goal_handles: Mutex::new(HashMap::new()),
167197
goal_results: Mutex::new(HashMap::new()),
168198
result_requests: Mutex::new(HashMap::new()),
199+
node: node.clone(),
169200
})
170201
}
171202

@@ -684,7 +715,7 @@ where
684715
}
685716
}
686717

687-
impl<T> ActionServerBase for ActionServer<T>
718+
impl<T> ActionServerBase for ActionServerState<T>
688719
where
689720
T: rosidl_runtime_rs::Action + rosidl_runtime_rs::ActionImpl,
690721
{

rclrs/src/action/server_goal_handle.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::{action::ActionServer, rcl_bindings::*, GoalUuid, RclrsError, ToResult};
1+
use crate::{action::ActionServerState, rcl_bindings::*, GoalUuid, RclrsError, ToResult};
22
use std::sync::{Arc, Mutex, Weak};
33

44
// Values defined by `action_msgs/msg/GoalStatus`
@@ -25,7 +25,7 @@ where
2525
ActionT: rosidl_runtime_rs::Action + rosidl_runtime_rs::ActionImpl,
2626
{
2727
rcl_handle: Mutex<*mut rcl_action_goal_handle_t>,
28-
action_server: Weak<ActionServer<ActionT>>,
28+
action_server: Weak<ActionServerState<ActionT>>,
2929
goal_request: Arc<ActionT::Goal>,
3030
uuid: GoalUuid,
3131
}
@@ -44,7 +44,7 @@ where
4444
{
4545
pub(crate) fn new(
4646
rcl_handle: *mut rcl_action_goal_handle_t,
47-
action_server: Weak<ActionServer<ActionT>>,
47+
action_server: Weak<ActionServerState<ActionT>>,
4848
goal_request: Arc<ActionT::Goal>,
4949
uuid: GoalUuid,
5050
) -> Self {

rclrs/src/node.rs

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@ use std::{
1919
use rosidl_runtime_rs::Message;
2020

2121
use crate::{
22-
rcl_bindings::*, ActionClient, ActionClientBase, ActionServer, ActionServerBase,
23-
CancelResponse, Client, ClientBase, ClientOptions, ClientState, Clock, ContextHandle,
24-
GoalResponse, GoalUuid, GuardCondition, LogParams, Logger, ParameterBuilder,
25-
ParameterInterface, ParameterVariant, Parameters, Publisher, PublisherOptions, PublisherState,
26-
RclrsError, ServerGoalHandle, Service, ServiceBase, ServiceOptions, ServiceState, Subscription,
27-
SubscriptionBase, SubscriptionCallback, SubscriptionOptions, SubscriptionState, TimeSource,
28-
ToLogParams, ENTITY_LIFECYCLE_MUTEX,
22+
rcl_bindings::*, ActionClient, ActionClientBase, ActionClientState, ActionServer,
23+
ActionServerBase, ActionServerState, CancelResponse, Client, ClientBase, ClientOptions,
24+
ClientState, Clock, ContextHandle, GoalResponse, GoalUuid, GuardCondition, LogParams, Logger,
25+
ParameterBuilder, ParameterInterface, ParameterVariant, Parameters, Publisher,
26+
PublisherOptions, PublisherState, RclrsError, ServerGoalHandle, Service, ServiceBase,
27+
ServiceOptions, ServiceState, Subscription, SubscriptionBase, SubscriptionCallback,
28+
SubscriptionOptions, SubscriptionState, TimeSource, ToLogParams, ENTITY_LIFECYCLE_MUTEX,
2929
};
3030

3131
// SAFETY: The functions accessing this type, including drop(), shouldn't care about the thread
@@ -299,11 +299,11 @@ impl NodeState {
299299
pub fn create_action_client<T>(
300300
self: &Arc<Self>,
301301
topic: &str,
302-
) -> Result<Arc<ActionClient<T>>, RclrsError>
302+
) -> Result<ActionClient<T>, RclrsError>
303303
where
304304
T: rosidl_runtime_rs::Action,
305305
{
306-
let action_client = Arc::new(ActionClient::<T>::new(self, topic)?);
306+
let action_client = Arc::new(ActionClientState::<T>::new(self, topic)?);
307307
self.action_clients_mtx
308308
.lock()
309309
.unwrap()
@@ -321,16 +321,15 @@ impl NodeState {
321321
handle_goal: GoalCallback,
322322
handle_cancel: CancelCallback,
323323
handle_accepted: AcceptedCallback,
324-
) -> Result<Arc<ActionServer<ActionT>>, RclrsError>
324+
) -> Result<ActionServer<ActionT>, RclrsError>
325325
where
326326
ActionT: rosidl_runtime_rs::Action + rosidl_runtime_rs::ActionImpl,
327327
GoalCallback: Fn(GoalUuid, <ActionT as rosidl_runtime_rs::Action>::Goal) -> GoalResponse + 'static + Send + Sync,
328328
CancelCallback: Fn(Arc<ServerGoalHandle<ActionT>>) -> CancelResponse + 'static + Send + Sync,
329329
AcceptedCallback: Fn(Arc<ServerGoalHandle<ActionT>>) + 'static + Send + Sync,
330330
{
331-
let action_server = Arc::new(ActionServer::<ActionT>::new(
331+
let action_server = Arc::new(ActionServerState::<ActionT>::new(
332332
self,
333-
self.get_clock(),
334333
topic,
335334
handle_goal,
336335
handle_cancel,

0 commit comments

Comments
 (0)