Skip to content

Commit 31e38ce

Browse files
committed
Add UUID->GoalHandle hash-map to action server
This requires a mutex to enable simultaneous goal acceptance in a multithreaded executor. We also make ServerGoalHandles implement Send and Sync, since they will be accessed by multiple threads during callbacks. Due to containing a raw pointer field, the type doesn't automatically implement Send/Sync; however, we guarantee that the uses of this pointer is safe and synchronized, allowing us to safely implement the traits.
1 parent 6da2e69 commit 31e38ce

File tree

2 files changed

+22
-17
lines changed

2 files changed

+22
-17
lines changed

rclrs/src/action/server.rs

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use crate::{
77
};
88
use rosidl_runtime_rs::{Action, ActionImpl, Message, Service};
99
use std::{
10+
collections::HashMap,
1011
ffi::CString,
1112
sync::{atomic::AtomicBool, Arc, Mutex, MutexGuard},
1213
};
@@ -77,6 +78,7 @@ where
7778
goal_callback: Box<GoalCallback<ActionT>>,
7879
cancel_callback: Box<CancelCallback<ActionT>>,
7980
accepted_callback: Box<AcceptedCallback<ActionT>>,
81+
goal_handles: Mutex<HashMap<GoalUuid, Arc<ServerGoalHandle<ActionT>>>>,
8082
}
8183

8284
impl<T> ActionServer<T>
@@ -157,6 +159,7 @@ where
157159
goal_callback: Box::new(goal_callback),
158160
cancel_callback: Box::new(cancel_callback),
159161
accepted_callback: Box::new(accepted_callback),
162+
goal_handles: Mutex::new(HashMap::new()),
160163
})
161164
}
162165

@@ -243,13 +246,13 @@ where
243246
return self.send_goal_response(request_id, false);
244247
}
245248

246-
// SAFETY: No preconditions
247-
let mut goal_info = unsafe { rcl_action_get_zero_initialized_goal_info() };
248-
// Populate the goal UUID; the other fields will be populated by rcl_action later on.
249-
// TODO(nwn): Check this claim.
250-
goal_info.goal_id.uuid = uuid.0;
251-
252249
let goal_handle = {
250+
// SAFETY: No preconditions
251+
let mut goal_info = unsafe { rcl_action_get_zero_initialized_goal_info() };
252+
// Only populate the goal UUID; the timestamp will be set internally by
253+
// rcl_action_accept_new_goal().
254+
goal_info.goal_id.uuid = uuid.0;
255+
253256
let server_handle = &mut *self.handle.lock();
254257
let goal_handle_ptr = unsafe {
255258
// SAFETY: The action server handle is locked and so synchronized with other
@@ -262,17 +265,17 @@ where
262265
// Other than rcl_get_error_string(), there's no indication what happened.
263266
panic!("Failed to accept goal");
264267
} else {
265-
ServerGoalHandle::<T>::new(
268+
Arc::new(ServerGoalHandle::<T>::new(
266269
goal_handle_ptr,
267-
todo!(""),
268-
GoalUuid(goal_info.goal_id.uuid),
269-
)
270+
todo!("Create an Arc holding the goal message"),
271+
uuid,
272+
))
270273
}
271274
};
272275

273276
self.send_goal_response(request_id, true)?;
274277

275-
// TODO: Add a UUID->goal_handle entry to a server goal map.
278+
self.goal_handles.lock().unwrap().insert(uuid, Arc::clone(&goal_handle));
276279

277280
if response == GoalResponse::AcceptAndExecute {
278281
goal_handle.execute()?;

rclrs/src/action/server_goal_handle.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,6 @@
11
use crate::{rcl_bindings::*, GoalUuid, RclrsError, ToResult};
22
use std::sync::{Arc, Mutex};
33

4-
// SAFETY: The functions accessing this type, including drop(), shouldn't care about the thread
5-
// they are running in. Therefore, this type can be safely sent to another thread.
6-
unsafe impl Send for rcl_action_goal_handle_t {}
7-
8-
unsafe impl Sync for rcl_action_goal_handle_t {}
9-
104
// Values defined by `action_msgs/msg/GoalStatus`
115
#[repr(i8)]
126
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
@@ -35,6 +29,14 @@ where
3529
uuid: GoalUuid,
3630
}
3731

32+
// SAFETY: Send/Sync are not automatically implemented due to the contained raw pointer
33+
// (specifically, `*mut rcl_action_goal_handle_t`). However, the `rcl_handle` field is wrapped in a
34+
// mutex, guaranteeing that the underlying data is never simultaneously accessed on the rclrs side
35+
// by multiple threads. Moreover, the rcl_action functions taking these handles are able to be run
36+
// from any thread.
37+
unsafe impl<ActionT> Send for ServerGoalHandle<ActionT> where ActionT: rosidl_runtime_rs::Action {}
38+
unsafe impl<ActionT> Sync for ServerGoalHandle<ActionT> where ActionT: rosidl_runtime_rs::Action {}
39+
3840
impl<ActionT> ServerGoalHandle<ActionT>
3941
where
4042
ActionT: rosidl_runtime_rs::Action,

0 commit comments

Comments
 (0)