@@ -9,14 +9,23 @@ use super::{
9
9
} ;
10
10
use crate :: rcl_bindings:: * ;
11
11
use crate :: {
12
- Node , QoSProfile , RclReturnCode , RclrsError , SubscriptionBase , SubscriptionHandle , ToResult ,
12
+ Node , QoSProfile , RclReturnCode , RclrsError , ToResult , NodeHandle , WorkerCommands , WaitableLifecycle , SubscriptionHandle ,
13
13
} ;
14
14
15
15
/// Struct for receiving messages whose type is only known at runtime.
16
16
pub struct DynamicSubscription {
17
- pub ( crate ) handle : Arc < SubscriptionHandle > ,
18
- /// The callback function that runs when a message was received.
17
+ /// This handle is used to access the data that rcl holds for this subscription.
18
+ handle : Arc < SubscriptionHandle > ,
19
+ /// This allows us to replace the callback in the subscription task.
20
+ ///
21
+ /// Holding onto this sender will keep the subscription task alive. Once
22
+ /// this sender is dropped, the subscription task will end itself.
23
+ // callback: Arc<Mutex<AnySubscriptionCallback<T, Scope::Payload>>>,
19
24
pub callback : Mutex < Box < dyn FnMut ( DynamicMessage ) + ' static + Send > > ,
25
+ /// Holding onto this keeps the waiter for this subscription alive in the
26
+ /// wait set of the executor.
27
+ #[ allow( unused) ]
28
+ lifecycle : WaitableLifecycle ,
20
29
metadata : DynamicMessageMetadata ,
21
30
// This is the regular type support library, not the introspection one.
22
31
#[ allow( dead_code) ]
@@ -27,16 +36,18 @@ impl DynamicSubscription {
27
36
/// Creates a new dynamic subscription.
28
37
///
29
38
/// This is not a public function, by the same rationale as `Subscription::new()`.
30
- pub ( crate ) fn new < F > (
31
- node : & Node ,
39
+ pub ( crate ) fn create_dynamic < F > (
32
40
topic : & str ,
33
41
topic_type : & str ,
34
42
qos : QoSProfile ,
35
43
callback : F ,
36
- ) -> Result < Self , RclrsError >
44
+ node_handle : & Arc < NodeHandle > ,
45
+ commands : & Arc < WorkerCommands > ,
46
+ ) -> Result < Arc < Self > , RclrsError >
37
47
where
38
48
F : FnMut ( DynamicMessage ) + ' static + Send ,
39
49
{
50
+ // TODO(luca) a lot of duplication with nomral, refactor
40
51
// This loads the introspection type support library.
41
52
let metadata = DynamicMessageMetadata :: new ( topic_type) ?;
42
53
// However, we also need the regular type support library –
@@ -62,38 +73,62 @@ impl DynamicSubscription {
62
73
let rcl_node = & mut * node. rcl_node_mtx . lock ( ) . unwrap ( ) ;
63
74
64
75
// SAFETY: No preconditions for this function.
65
- let mut subscription_options = unsafe { rcl_subscription_get_default_options ( ) } ;
66
- subscription_options . qos = qos. into ( ) ;
76
+ let mut rcl_subscription_options = unsafe { rcl_subscription_get_default_options ( ) } ;
77
+ rcl_subscription_options . qos = qos. into ( ) ;
67
78
// SAFETY: Getting a zero-initialized value is always safe.
68
79
let mut rcl_subscription = unsafe { rcl_get_zero_initialized_subscription ( ) } ;
69
- unsafe {
70
- // SAFETY: The rcl_subscription is zero-initialized as expected by this function.
71
- // The rcl_node is kept alive because it is co-owned by the subscription.
72
- // The topic name and the options are copied by this function, so they can be dropped
73
- // afterwards.
74
- // TODO: type support?
75
- rcl_subscription_init (
76
- & mut rcl_subscription,
77
- rcl_node,
78
- type_support_ptr,
79
- topic_c_string. as_ptr ( ) ,
80
- & subscription_options,
81
- )
82
- . ok ( ) ?;
80
+ {
81
+ let rcl_node = node_handle. rcl_node . lock ( ) . unwrap ( ) ;
82
+ let _lifecycle_lock = ENTITY_LIFECYCLE_MUTEX . lock ( ) . unwrap ( ) ;
83
+ unsafe {
84
+ // SAFETY:
85
+ // * The rcl_subscription is zero-initialized as mandated by this function.
86
+ // * The rcl_node is kept alive by the NodeHandle because it is a dependency of the subscription.
87
+ // * The topic name and the options are copied by this function, so they can be dropped afterwards.
88
+ // * The entity lifecycle mutex is locked to protect against the risk of global
89
+ // variables in the rmw implementation being unsafely modified during cleanup.
90
+ rcl_subscription_init (
91
+ & mut rcl_subscription,
92
+ & * rcl_node,
93
+ type_support_ptr,
94
+ topic_c_string. as_ptr ( ) ,
95
+ & rcl_subscription_options,
96
+ )
97
+ . ok ( ) ?;
98
+ }
83
99
}
84
100
85
101
let handle = Arc :: new ( SubscriptionHandle {
86
- rcl_subscription_mtx : Mutex :: new ( rcl_subscription) ,
87
- rcl_node_mtx : node. rcl_node_mtx . clone ( ) ,
88
- in_use_by_wait_set : Arc :: new ( AtomicBool :: new ( false ) ) ,
102
+ rcl_subscription : Mutex :: new ( rcl_subscription) ,
103
+ node_handle : Arc :: clone ( node_handle) ,
89
104
} ) ;
90
105
106
+ let ( waitable, lifecycle) = Waitable :: new (
107
+ Box :: new ( SubscriptionExecutable {
108
+ handle : Arc :: clone ( & handle) ,
109
+ callback : Arc :: clone ( & callback) ,
110
+ commands : Arc :: clone ( commands) ,
111
+ } ) ,
112
+ Some ( Arc :: clone ( commands. get_guard_condition ( ) ) ) ,
113
+ ) ;
114
+ commands. add_to_wait_set ( waitable) ;
115
+
116
+ Ok ( Arc :: new ( Self {
117
+ handle,
118
+ callback,
119
+ lifecycle,
120
+ metadata,
121
+ type_support_library,
122
+ } ) )
123
+
124
+ /*
91
125
Ok(Self {
92
126
handle,
93
127
callback: Mutex::new(Box::new(callback)),
94
128
metadata,
95
129
type_support_library,
96
130
})
131
+ */
97
132
}
98
133
99
134
/// Returns the topic name of the subscription.
@@ -158,29 +193,6 @@ impl DynamicSubscription {
158
193
}
159
194
}
160
195
161
- impl SubscriptionBase for DynamicSubscription {
162
- fn handle ( & self ) -> & SubscriptionHandle {
163
- & self . handle
164
- }
165
-
166
- fn execute ( & self ) -> Result < ( ) , RclrsError > {
167
- let msg = match self . take ( ) {
168
- Ok ( msg) => msg,
169
- Err ( RclrsError :: RclError {
170
- code : RclReturnCode :: SubscriptionTakeFailed ,
171
- ..
172
- } ) => {
173
- // Spurious wakeup – this may happen even when a waitset indicated that this
174
- // subscription was ready, so it shouldn't be an error.
175
- return Ok ( ( ) ) ;
176
- }
177
- Err ( e) => return Err ( e) ,
178
- } ;
179
- ( * self . callback . lock ( ) . unwrap ( ) ) ( msg) ;
180
- Ok ( ( ) )
181
- }
182
- }
183
-
184
196
#[ cfg( test) ]
185
197
mod tests {
186
198
use super :: * ;
0 commit comments