|
| 1 | +use std::any::Any; |
1 | 2 | use std::boxed::Box;
|
2 | 3 | use std::ffi::{CStr, CString};
|
| 4 | +use std::ops::{Deref, DerefMut}; |
3 | 5 | use std::sync::atomic::AtomicBool;
|
4 | 6 | use std::sync::{Arc, Mutex};
|
5 | 7 |
|
| 8 | +use futures::future::BoxFuture; |
| 9 | + |
6 | 10 | use super::{
|
7 | 11 | get_type_support_handle, get_type_support_library, DynamicMessage, DynamicMessageMetadata,
|
8 | 12 | MessageStructure,
|
9 | 13 | };
|
10 | 14 | use crate::rcl_bindings::*;
|
11 | 15 | use crate::{
|
12 |
| - ENTITY_LIFECYCLE_MUTEX, Waitable, |
| 16 | + ENTITY_LIFECYCLE_MUTEX, Waitable, RclPrimitive, MessageInfo, RclPrimitiveKind, RclPrimitiveHandle, WorkScope, |
13 | 17 | Node, QoSProfile, RclReturnCode, RclrsError, ToResult, NodeHandle, WorkerCommands, WaitableLifecycle, SubscriptionHandle,
|
14 | 18 | };
|
15 | 19 |
|
| 20 | +struct DynamicSubscriptionExecutable<Payload> { |
| 21 | + handle: Arc<SubscriptionHandle>, |
| 22 | + callback: Arc<Mutex<DynamicSubscriptionCallback<Payload>>>, |
| 23 | + commands: Arc<WorkerCommands>, |
| 24 | + metadata: Arc<DynamicMessageMetadata>, |
| 25 | +} |
| 26 | + |
| 27 | +// TODO(luca) consider making these enums if we want different callback types |
| 28 | +// TODO(luca) make fields private |
| 29 | +pub struct NodeDynamicSubscriptionCallback(pub Box<dyn FnMut(DynamicMessage, MessageInfo) -> BoxFuture<'static, ()> + Send>); |
| 30 | +pub struct WorkerDynamicSubscriptionCallback<Payload>(pub Box<dyn FnMut(&mut Payload, DynamicMessage, MessageInfo) + Send>); |
| 31 | + |
| 32 | +impl Deref for NodeDynamicSubscriptionCallback { |
| 33 | + type Target = Box<dyn FnMut(DynamicMessage, MessageInfo)-> BoxFuture<'static, ()> + Send>; |
| 34 | + fn deref(&self) -> &Self::Target { |
| 35 | + &self.0 |
| 36 | + } |
| 37 | +} |
| 38 | + |
| 39 | +impl DerefMut for NodeDynamicSubscriptionCallback { |
| 40 | + fn deref_mut(&mut self) -> &mut Self::Target { |
| 41 | + &mut self.0 |
| 42 | + } |
| 43 | +} |
| 44 | + |
| 45 | +impl<Payload> Deref for WorkerDynamicSubscriptionCallback<Payload> { |
| 46 | + type Target = Box<dyn FnMut(&mut Payload, DynamicMessage, MessageInfo) + Send>; |
| 47 | + fn deref(&self) -> &Self::Target { |
| 48 | + &self.0 |
| 49 | + } |
| 50 | +} |
| 51 | + |
| 52 | +impl<Payload> DerefMut for WorkerDynamicSubscriptionCallback<Payload> { |
| 53 | + fn deref_mut(&mut self) -> &mut Self::Target { |
| 54 | + &mut self.0 |
| 55 | + } |
| 56 | +} |
| 57 | + |
| 58 | +//pub trait NodeDynamicSubscriptionCallback = FnMut(DynamicMessage, MessageInfo) -> BoxFuture<'static, ()> + Send; |
| 59 | +//pub trait WorkerDynamicSubscriptionCallback<Payload> = FnMut(&mut Payload, DynamicMessage, MessageInfo) + Send; |
| 60 | + |
| 61 | +pub enum DynamicSubscriptionCallback<Payload> { |
| 62 | + /// A callback with the message and the message info as arguments. |
| 63 | + Node(NodeDynamicSubscriptionCallback), |
| 64 | + /// A callback with the payload, message, and the message info as arguments. |
| 65 | + Worker(WorkerDynamicSubscriptionCallback<Payload>), |
| 66 | +} |
| 67 | + |
| 68 | +impl From<NodeDynamicSubscriptionCallback> for DynamicSubscriptionCallback<()> { |
| 69 | + fn from(value: NodeDynamicSubscriptionCallback) -> Self { |
| 70 | + DynamicSubscriptionCallback::Node(value) |
| 71 | + } |
| 72 | +} |
| 73 | + |
| 74 | +impl<Payload> From<WorkerDynamicSubscriptionCallback<Payload>> for DynamicSubscriptionCallback<Payload> { |
| 75 | + fn from(value: WorkerDynamicSubscriptionCallback<Payload>) -> Self { |
| 76 | + DynamicSubscriptionCallback::Worker(value) |
| 77 | + } |
| 78 | +} |
| 79 | + |
| 80 | +impl<Payload: 'static> DynamicSubscriptionCallback<Payload> { |
| 81 | + pub(super) fn execute( |
| 82 | + &mut self, |
| 83 | + executable: &DynamicSubscriptionExecutable<Payload>, |
| 84 | + any_payload: &mut dyn Any, |
| 85 | + commands: &WorkerCommands, |
| 86 | + ) -> Result<(), RclrsError> { |
| 87 | + let Some(payload) = any_payload.downcast_mut::<Payload>() else { |
| 88 | + return Err(RclrsError::InvalidPayload { |
| 89 | + expected: std::any::TypeId::of::<Payload>(), |
| 90 | + received: (*any_payload).type_id(), |
| 91 | + }); |
| 92 | + }; |
| 93 | + match self { |
| 94 | + Self::Node(cb) => { |
| 95 | + let (msg, msg_info) = executable.take()?; |
| 96 | + commands.run_async(cb(msg, msg_info)); |
| 97 | + } |
| 98 | + Self::Worker(cb) => { |
| 99 | + let (msg, msg_info) = executable.take()?; |
| 100 | + cb(payload, msg, msg_info); |
| 101 | + } |
| 102 | + } |
| 103 | + Ok(()) |
| 104 | + } |
| 105 | +} |
| 106 | + |
| 107 | +impl<Payload> DynamicSubscriptionExecutable<Payload> { |
| 108 | + pub fn take(&self) -> Result<(DynamicMessage, MessageInfo), RclrsError> { |
| 109 | + let mut dynamic_message = self.metadata.create()?; |
| 110 | + let rmw_message = dynamic_message.storage.as_mut_ptr(); |
| 111 | + let mut message_info = unsafe { rmw_get_zero_initialized_message_info() }; |
| 112 | + let rcl_subscription = &mut *self.handle.lock(); |
| 113 | + unsafe { |
| 114 | + // SAFETY: The first two pointers are valid/initialized, and do not need to be valid |
| 115 | + // beyond the function call. |
| 116 | + // The latter two pointers are explicitly allowed to be NULL. |
| 117 | + rcl_take( |
| 118 | + rcl_subscription, |
| 119 | + rmw_message as *mut _, |
| 120 | + &mut message_info, |
| 121 | + std::ptr::null_mut(), |
| 122 | + ) |
| 123 | + .ok()? |
| 124 | + }; |
| 125 | + Ok((dynamic_message, MessageInfo::from_rmw_message_info(&message_info))) |
| 126 | + } |
| 127 | +} |
| 128 | + |
| 129 | +impl<Payload: 'static> RclPrimitive for DynamicSubscriptionExecutable<Payload> |
| 130 | +{ |
| 131 | + unsafe fn execute(&mut self, payload: &mut dyn Any) -> Result<(), RclrsError> { |
| 132 | + self.callback |
| 133 | + .lock() |
| 134 | + .unwrap() |
| 135 | + .execute(&self, payload, &self.commands) |
| 136 | + } |
| 137 | + |
| 138 | + fn kind(&self) -> RclPrimitiveKind { |
| 139 | + RclPrimitiveKind::Subscription |
| 140 | + } |
| 141 | + |
| 142 | + fn handle(&self) -> RclPrimitiveHandle { |
| 143 | + RclPrimitiveHandle::Subscription(self.handle.lock()) |
| 144 | + } |
| 145 | +} |
| 146 | + |
16 | 147 | /// Struct for receiving messages whose type is only known at runtime.
|
17 |
| -pub struct DynamicSubscription { |
| 148 | +pub struct DynamicSubscription<Scope> |
| 149 | +where |
| 150 | + Scope: WorkScope, |
| 151 | +{ |
18 | 152 | /// This handle is used to access the data that rcl holds for this subscription.
|
19 | 153 | handle: Arc<SubscriptionHandle>,
|
20 | 154 | /// This allows us to replace the callback in the subscription task.
|
21 | 155 | ///
|
22 | 156 | /// Holding onto this sender will keep the subscription task alive. Once
|
23 | 157 | /// this sender is dropped, the subscription task will end itself.
|
24 |
| - pub callback: Arc<Mutex<AnySubscriptionCallback<T, Scope::Payload>>>, |
25 |
| - // pub callback: Mutex<Box<dyn FnMut(DynamicMessage) + 'static + Send>>, |
| 158 | + // pub callback: Arc<Mutex<AnySubscriptionCallback<T, Scope::Payload>>>, |
| 159 | + // pub callback: Arc<Mutex<Box<dyn FnMut(DynamicMessage) + 'static + Send>>>, |
| 160 | + callback: Arc<Mutex<DynamicSubscriptionCallback<Scope::Payload>>>, |
26 | 161 | /// Holding onto this keeps the waiter for this subscription alive in the
|
27 | 162 | /// wait set of the executor.
|
28 | 163 | #[allow(unused)]
|
29 | 164 | lifecycle: WaitableLifecycle,
|
30 |
| - metadata: DynamicMessageMetadata, |
| 165 | + metadata: Arc<DynamicMessageMetadata>, |
31 | 166 | // This is the regular type support library, not the introspection one.
|
32 | 167 | #[allow(dead_code)]
|
33 | 168 | type_support_library: Arc<libloading::Library>,
|
34 | 169 | }
|
35 | 170 |
|
36 |
| -impl DynamicSubscription { |
| 171 | +impl<Scope> DynamicSubscription<Scope> |
| 172 | +where |
| 173 | + Scope: WorkScope |
| 174 | +{ |
37 | 175 | /// Creates a new dynamic subscription.
|
38 | 176 | ///
|
39 | 177 | /// This is not a public function, by the same rationale as `Subscription::new()`.
|
40 |
| - pub(crate) fn new<F>( |
| 178 | + pub(crate) fn new( |
41 | 179 | topic: &str,
|
42 | 180 | topic_type: &str,
|
43 | 181 | qos: QoSProfile,
|
44 |
| - callback: F, |
| 182 | + callback: impl Into<DynamicSubscriptionCallback<Scope::Payload>>, |
45 | 183 | node_handle: &Arc<NodeHandle>,
|
46 | 184 | commands: &Arc<WorkerCommands>,
|
47 | 185 | ) -> Result<Arc<Self>, RclrsError>
|
48 |
| - where |
49 |
| - F: FnMut(DynamicMessage) + 'static + Send, |
50 | 186 | {
|
51 | 187 | // TODO(luca) a lot of duplication with nomral, refactor
|
52 | 188 | // This loads the introspection type support library.
|
@@ -103,13 +239,15 @@ impl DynamicSubscription {
|
103 | 239 | node_handle: Arc::clone(node_handle),
|
104 | 240 | });
|
105 | 241 |
|
106 |
| - let callback = Arc::new(Mutex::new(callback)); |
| 242 | + let callback = Arc::new(Mutex::new(callback.into())); |
| 243 | + let metadata = Arc::new(metadata); |
107 | 244 |
|
108 | 245 | let (waitable, lifecycle) = Waitable::new(
|
109 |
| - Box::new(SubscriptionExecutable { |
| 246 | + Box::new(DynamicSubscriptionExecutable { |
110 | 247 | handle: Arc::clone(&handle),
|
111 | 248 | callback: Arc::clone(&callback),
|
112 | 249 | commands: Arc::clone(commands),
|
| 250 | + metadata: Arc::clone(&metadata), |
113 | 251 | }),
|
114 | 252 | Some(Arc::clone(commands.get_guard_condition())),
|
115 | 253 | );
|
|
0 commit comments