Skip to content

Commit ef45142

Browse files
committed
Sketch out action client as well
1 parent 484fc38 commit ef45142

File tree

1 file changed

+113
-4
lines changed

1 file changed

+113
-4
lines changed

rclrs/src/action.rs

Lines changed: 113 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,44 @@ pub enum CancelResponse {
2525
Accept = 2,
2626
}
2727

28+
// SAFETY: The functions accessing this type, including drop(), shouldn't care about the thread
29+
// they are running in. Therefore, this type can be safely sent to another thread.
30+
unsafe impl Send for rcl_action_client_t {}
31+
32+
// SAFETY: The functions accessing this type, including drop(), shouldn't care about the thread
33+
// they are running in. Therefore, this type can be safely sent to another thread.
34+
unsafe impl Send for rcl_action_server_t {}
35+
36+
/// Manage the lifecycle of an `rcl_action_client_t`, including managing its dependencies
37+
/// on `rcl_node_t` and `rcl_context_t` by ensuring that these dependencies are
38+
/// [dropped after][1] the `rcl_action_client_t`.
39+
///
40+
/// [1]: <https://doc.rust-lang.org/reference/destructors.html>
41+
pub struct ActionClientHandle {
42+
rcl_action_client: Mutex<rcl_action_client_t>,
43+
node_handle: Arc<NodeHandle>,
44+
pub(crate) in_use_by_wait_set: Arc<AtomicBool>,
45+
}
46+
47+
impl ActionClientHandle {
48+
pub(crate) fn lock(&self) -> MutexGuard<rcl_action_client_t> {
49+
self.rcl_action_client.lock().unwrap()
50+
}
51+
}
52+
53+
impl Drop for ActionClientHandle {
54+
fn drop(&mut self) {
55+
let rcl_action_client = self.rcl_action_client.get_mut().unwrap();
56+
let mut rcl_node = self.node_handle.rcl_node.lock().unwrap();
57+
let _lifecycle_lock = ENTITY_LIFECYCLE_MUTEX.lock().unwrap();
58+
// SAFETY: The entity lifecycle mutex is locked to protect against the risk of
59+
// global variables in the rmw implementation being unsafely modified during cleanup.
60+
unsafe {
61+
rcl_action_client_fini(rcl_action_client, &mut *rcl_node);
62+
}
63+
}
64+
}
65+
2866
/// Manage the lifecycle of an `rcl_action_server_t`, including managing its dependencies
2967
/// on `rcl_node_t` and `rcl_context_t` by ensuring that these dependencies are
3068
/// [dropped after][1] the `rcl_action_server_t`.
@@ -55,6 +93,16 @@ impl Drop for ActionServerHandle {
5593
}
5694
}
5795

96+
/// Trait to be implemented by concrete ActionClient structs.
97+
///
98+
/// See [`ActionClient<T>`] for an example
99+
pub trait ActionClientBase: Send + Sync {
100+
/// Internal function to get a reference to the `rcl` handle.
101+
fn handle(&self) -> &ActionClientHandle;
102+
// /// Tries to take a new request and run the callback with it.
103+
// fn execute(&self) -> Result<(), RclrsError>;
104+
}
105+
58106
/// Trait to be implemented by concrete ActionServer structs.
59107
///
60108
/// See [`ActionServer<T>`] for an example
@@ -69,7 +117,8 @@ pub struct ActionClient<T>
69117
where
70118
T: rosidl_runtime_rs::Action,
71119
{
72-
_marker: PhantomData<T>,
120+
_marker: PhantomData<fn() -> T>,
121+
pub(crate) handle: Arc<ActionClientHandle>,
73122
}
74123

75124
impl<T> ActionClient<T>
@@ -81,17 +130,68 @@ where
81130
where
82131
T: rosidl_runtime_rs::Action,
83132
{
133+
// SAFETY: Getting a zero-initialized value is always safe.
134+
let mut rcl_action_client = unsafe { rcl_action_get_zero_initialized_client() };
135+
let type_support = <T as rosidl_runtime_rs::Action>::get_type_support()
136+
as *const rosidl_action_type_support_t;
137+
let topic_c_string = CString::new(topic).map_err(|err| RclrsError::StringContainsNul {
138+
err,
139+
s: topic.into(),
140+
})?;
141+
142+
// SAFETY: No preconditions for this function.
143+
let action_client_options = unsafe { rcl_action_client_get_default_options() };
144+
145+
{
146+
let mut rcl_node = node_handle.rcl_node.lock().unwrap();
147+
let _lifecycle_lock = ENTITY_LIFECYCLE_MUTEX.lock().unwrap();
148+
149+
// SAFETY:
150+
// * The rcl_client was zero-initialized as expected by this function.
151+
// * The rcl_node is kept alive by the NodeHandle because it is a dependency of the client.
152+
// * The topic name and the options are copied by this function, so they can be dropped
153+
// afterwards.
154+
// * The entity lifecycle mutex is locked to protect against the risk of global
155+
// variables in the rmw implementation being unsafely modified during initialization.
156+
unsafe {
157+
rcl_action_client_init(
158+
&mut rcl_action_client,
159+
&mut *rcl_node,
160+
type_support,
161+
topic_c_string.as_ptr(),
162+
&action_client_options,
163+
)
164+
.ok()?;
165+
}
166+
}
167+
168+
let handle = Arc::new(ActionClientHandle {
169+
rcl_action_client: Mutex::new(rcl_action_client),
170+
node_handle,
171+
in_use_by_wait_set: Arc::new(AtomicBool::new(false)),
172+
});
173+
84174
Ok(Self {
85175
_marker: Default::default(),
176+
handle,
86177
})
87178
}
88179
}
89180

181+
impl<T> ActionClientBase for ActionClient<T>
182+
where
183+
T: rosidl_runtime_rs::Action,
184+
{
185+
fn handle(&self) -> &ActionClientHandle {
186+
&self.handle
187+
}
188+
}
189+
90190
pub struct ActionServer<T>
91191
where
92192
T: rosidl_runtime_rs::Action,
93193
{
94-
_marker: PhantomData<T>,
194+
_marker: PhantomData<fn() -> T>,
95195
pub(crate) handle: Arc<ActionServerHandle>,
96196
// goal_callback: (),
97197
// cancel_callback: (),
@@ -133,10 +233,10 @@ where
133233
rcl_action_server_init(
134234
&mut rcl_action_server,
135235
&mut *rcl_node,
136-
todo!(),
236+
todo!("pass in a rcl_clock_t"),
137237
type_support,
138238
topic_c_string.as_ptr(),
139-
&action_server_options as *const _,
239+
&action_server_options,
140240
)
141241
.ok()?;
142242
}
@@ -155,6 +255,15 @@ where
155255
}
156256
}
157257

258+
impl<T> ActionServerBase for ActionServer<T>
259+
where
260+
T: rosidl_runtime_rs::Action,
261+
{
262+
fn handle(&self) -> &ActionServerHandle {
263+
&self.handle
264+
}
265+
}
266+
158267
pub struct ServerGoalHandle<T>
159268
where
160269
T: rosidl_runtime_rs::Action,

0 commit comments

Comments
 (0)