@@ -19,12 +19,47 @@ use crate::error::{to_rclrs_result, RclReturnCode, RclrsError, ToResult};
19
19
use crate :: rcl_bindings:: * ;
20
20
use crate :: { ClientWaitable , Context , ServiceWaitable , SubscriptionWaitable } ;
21
21
22
- use std:: sync:: Arc ;
22
+ use std:: sync:: {
23
+ atomic:: { AtomicBool , Ordering } ,
24
+ Arc ,
25
+ } ;
23
26
use std:: time:: Duration ;
24
27
use std:: vec:: Vec ;
25
28
26
29
use parking_lot:: Mutex ;
27
30
31
+ /// A helper struct for tracking whether the waitable is currently in a wait set.
32
+ ///
33
+ /// When this struct is constructed, which happens when adding an entity to the wait set,
34
+ /// it checks that the atomic boolean is false and sets it to true.
35
+ /// When it is dropped, which happens when it is removed from the wait set,
36
+ /// or the wait set itself is dropped, it sets the atomic bool to false.
37
+ pub ( crate ) struct ExclusivityGuard < T > {
38
+ in_use_by_wait_set : Arc < AtomicBool > ,
39
+ waitable : T ,
40
+ }
41
+
42
+ impl < T > Drop for ExclusivityGuard < T > {
43
+ fn drop ( & mut self ) {
44
+ self . in_use_by_wait_set . store ( false , Ordering :: Relaxed )
45
+ }
46
+ }
47
+
48
+ impl < T > ExclusivityGuard < T > {
49
+ pub fn new ( waitable : T , in_use_by_wait_set : Arc < AtomicBool > ) -> Result < Self , RclrsError > {
50
+ if in_use_by_wait_set
51
+ . compare_exchange ( false , true , Ordering :: Relaxed , Ordering :: Relaxed )
52
+ . is_err ( )
53
+ {
54
+ return Err ( RclrsError :: AlreadyAddedToWaitSet ) ;
55
+ }
56
+ Ok ( Self {
57
+ in_use_by_wait_set,
58
+ waitable,
59
+ } )
60
+ }
61
+ }
62
+
28
63
/// Trait to be implemented by entities that can be waited on, like a [`Subscription`][1].
29
64
///
30
65
/// [1]: crate::Subscription
@@ -59,9 +94,9 @@ pub struct WaitSet {
59
94
// The subscriptions that are currently registered in the wait set.
60
95
// This correspondence is an invariant that must be maintained by all functions,
61
96
// even in the error case.
62
- pub ( crate ) subscriptions : Vec < Arc < dyn SubscriptionWaitable > > ,
63
- pub ( crate ) clients : Vec < Arc < dyn ClientWaitable > > ,
64
- pub ( crate ) services : Vec < Arc < dyn ServiceWaitable > > ,
97
+ pub ( crate ) subscriptions : Vec < ExclusivityGuard < Arc < dyn SubscriptionWaitable > > > ,
98
+ pub ( crate ) clients : Vec < ExclusivityGuard < Arc < dyn ClientWaitable > > > ,
99
+ pub ( crate ) services : Vec < ExclusivityGuard < Arc < dyn ServiceWaitable > > > ,
65
100
}
66
101
67
102
/// A list of entities that are ready, returned by [`WaitSet::wait`].
@@ -128,13 +163,14 @@ impl WaitSet {
128
163
129
164
/// Adds a client to the wait set.
130
165
///
131
- /// It is possible, but not useful, to add the same client twice.
132
- ///
133
- /// This will return an error if the number of clients in the wait set is larger than the
134
- /// capacity set in [`WaitSet::new`].
166
+ /// # Errors
167
+ /// - If the client was already added to this wait set or another one,
168
+ /// [`AlreadyAddedToWaitSet`][1] will be returned
169
+ /// - If the number of clients in the wait set is larger than the
170
+ /// capacity set in [`WaitSet::new`], [`WaitSetFull`][2] will be returned
135
171
///
136
- /// The same client must not be added to multiple wait sets, because that would make it
137
- /// unsafe to simultaneously wait on those wait sets.
172
+ /// [1]: crate::RclrsError
173
+ /// [2]: crate::RclReturnCode
138
174
pub fn add_client ( & mut self , client : Arc < dyn ClientWaitable > ) -> Result < ( ) , RclrsError > {
139
175
// SAFETY: The implementation of this trait for clients checks that the client
140
176
// has not already been added to a different wait set.
@@ -143,13 +179,14 @@ impl WaitSet {
143
179
144
180
/// Adds a service to the wait set.
145
181
///
146
- /// It is possible, but not useful, to add the same service twice.
147
- ///
148
- /// This will return an error if the number of services in the wait set is larger than the
149
- /// capacity set in [`WaitSet::new`].
182
+ /// # Errors
183
+ /// - If the service was already added to this wait set or another one,
184
+ /// [`AlreadyAddedToWaitSet`][1] will be returned
185
+ /// - If the number of services in the wait set is larger than the
186
+ /// capacity set in [`WaitSet::new`], [`WaitSetFull`][2] will be returned
150
187
///
151
- /// The same service must not be added to multiple wait sets, because that would make it
152
- /// unsafe to simultaneously wait on those wait sets.
188
+ /// [1]: crate::RclrsError
189
+ /// [2]: crate::RclReturnCode
153
190
pub fn add_service ( & mut self , service : Arc < dyn ServiceWaitable > ) -> Result < ( ) , RclrsError > {
154
191
// SAFETY: The implementation of this trait for services checks that the service
155
192
// has not already been added to a different wait set.
@@ -158,13 +195,14 @@ impl WaitSet {
158
195
159
196
/// Adds a subscription to the wait set.
160
197
///
161
- /// It is possible, but not useful, to add the same subscription twice.
162
- ///
163
- /// This will return an error if the number of subscriptions in the wait set is larger than the
164
- /// capacity set in [`WaitSet::new`].
198
+ /// # Errors
199
+ /// - If the subscription was already added to this wait set or another one,
200
+ /// [`AlreadyAddedToWaitSet`][1] will be returned
201
+ /// - If the number of subscriptions in the wait set is larger than the
202
+ /// capacity set in [`WaitSet::new`], [`WaitSetFull`][2] will be returned
165
203
///
166
- /// The same subscription must not be added to multiple wait sets, because that would make it
167
- /// unsafe to simultaneously wait on those wait sets.
204
+ /// [1]: crate::RclrsError
205
+ /// [2]: crate::RclReturnCode
168
206
pub fn add_subscription (
169
207
& mut self ,
170
208
subscription : Arc < dyn SubscriptionWaitable > ,
@@ -241,7 +279,9 @@ impl WaitSet {
241
279
// https://github.com/ros2/rcl/blob/35a31b00a12f259d492bf53c0701003bd7f1745c/rcl/include/rcl/wait.h#L419
242
280
let wait_set_entry = unsafe { * self . rcl_wait_set . subscriptions . add ( i) } ;
243
281
if !wait_set_entry. is_null ( ) {
244
- ready_entities. subscriptions . push ( subscription. clone ( ) ) ;
282
+ ready_entities
283
+ . subscriptions
284
+ . push ( Arc :: clone ( & subscription. waitable ) ) ;
245
285
}
246
286
}
247
287
for ( i, client) in self . clients . iter ( ) . enumerate ( ) {
@@ -250,7 +290,7 @@ impl WaitSet {
250
290
// https://github.com/ros2/rcl/blob/35a31b00a12f259d492bf53c0701003bd7f1745c/rcl/include/rcl/wait.h#L419
251
291
let wait_set_entry = unsafe { * self . rcl_wait_set . clients . add ( i) } ;
252
292
if !wait_set_entry. is_null ( ) {
253
- ready_entities. clients . push ( client . clone ( ) ) ;
293
+ ready_entities. clients . push ( Arc :: clone ( & client . waitable ) ) ;
254
294
}
255
295
}
256
296
for ( i, service) in self . services . iter ( ) . enumerate ( ) {
@@ -259,7 +299,7 @@ impl WaitSet {
259
299
// https://github.com/ros2/rcl/blob/35a31b00a12f259d492bf53c0701003bd7f1745c/rcl/include/rcl/wait.h#L419
260
300
let wait_set_entry = unsafe { * self . rcl_wait_set . services . add ( i) } ;
261
301
if !wait_set_entry. is_null ( ) {
262
- ready_entities. services . push ( service . clone ( ) ) ;
302
+ ready_entities. services . push ( Arc :: clone ( & service . waitable ) ) ;
263
303
}
264
304
}
265
305
Ok ( ready_entities)
0 commit comments