@@ -5,13 +5,13 @@ use futures::channel::{
5
5
6
6
use std:: {
7
7
any:: Any ,
8
- sync:: { atomic:: { AtomicBool , Ordering } , Arc } ,
8
+ sync:: { atomic:: { AtomicBool , Ordering } , Arc , Mutex } ,
9
9
time:: { Duration , Instant } ,
10
10
} ;
11
11
12
12
use crate :: {
13
13
Context , Promise , RclrsError , WaitSet , Waitable , GuardCondition , ExecutorWorkerOptions ,
14
- PayloadTask ,
14
+ PayloadTask , WeakActivityListener , ActivityListenerCallback ,
15
15
} ;
16
16
17
17
/// This is a utility class that executors can use to easily run and manage
@@ -22,6 +22,7 @@ pub struct WaitSetRunner {
22
22
waitable_receiver : UnboundedReceiver < Waitable > ,
23
23
task_sender : UnboundedSender < PayloadTask > ,
24
24
task_receiver : UnboundedReceiver < PayloadTask > ,
25
+ activity_listeners : Arc < Mutex < Vec < WeakActivityListener > > > ,
25
26
guard_condition : Arc < GuardCondition > ,
26
27
payload : Box < dyn Any + Send > ,
27
28
}
@@ -62,6 +63,7 @@ impl WaitSetRunner {
62
63
waitable_receiver,
63
64
task_sender,
64
65
task_receiver,
66
+ activity_listeners : Arc :: default ( ) ,
65
67
guard_condition : worker_options. guard_condition ,
66
68
payload : worker_options. payload ,
67
69
}
@@ -79,6 +81,12 @@ impl WaitSetRunner {
79
81
self . task_sender . clone ( )
80
82
}
81
83
84
+ /// Get the group of senders that will be triggered each time the wait set
85
+ /// is woken up. This is used
86
+ pub fn activity_listeners ( & self ) -> Arc < Mutex < Vec < WeakActivityListener > > > {
87
+ Arc :: clone ( & self . activity_listeners )
88
+ }
89
+
82
90
/// Get the guard condition associated with the wait set of this runner.
83
91
pub fn guard_condition ( & self ) -> & Arc < GuardCondition > {
84
92
& self . guard_condition
@@ -111,6 +119,7 @@ impl WaitSetRunner {
111
119
/// will be triggered after the user-provided promise is resolved.
112
120
pub fn run_blocking ( & mut self , conditions : WaitSetRunConditions ) -> Result < ( ) , RclrsError > {
113
121
let mut first_spin = true ;
122
+ let mut listeners = Vec :: new ( ) ;
114
123
loop {
115
124
// TODO(@mxgrey): SmallVec would be better suited here if we are
116
125
// okay with adding that as a dependency.
@@ -124,7 +133,7 @@ impl WaitSetRunner {
124
133
}
125
134
126
135
while let Ok ( Some ( task) ) = self . task_receiver . try_next ( ) {
127
- task ( & mut self . payload ) ;
136
+ task ( & mut * self . payload ) ;
128
137
}
129
138
130
139
if conditions. only_next_available_work && !first_spin {
@@ -154,14 +163,57 @@ impl WaitSetRunner {
154
163
}
155
164
} ) ;
156
165
166
+ let mut at_least_one = false ;
157
167
self . wait_set . wait ( timeout, |executable| {
168
+ at_least_one = true ;
158
169
// SAFETY: The user of WaitSetRunner is responsible for ensuring
159
170
// the runner has the same payload type as the executables that
160
171
// are given to it.
161
172
unsafe {
162
- executable. execute ( & mut self . payload )
173
+ executable. execute ( & mut * self . payload )
163
174
}
164
175
} ) ?;
176
+
177
+ if at_least_one {
178
+ // We drain all listeners from activity_listeners to ensure that we
179
+ // don't get a deadlock from double-locking the activity_listeners
180
+ // mutex while executing one of the listeners. If the listener has
181
+ // access to the Worker<T> then it could attempt to add another
182
+ // listener while we have the vector locked, which would cause a
183
+ // deadlock.
184
+ listeners. extend (
185
+ self . activity_listeners . lock ( ) . unwrap ( ) . drain ( ..)
186
+ . filter_map ( |x| x. upgrade ( ) )
187
+ ) ;
188
+
189
+ for arc_listener in & listeners {
190
+ // We pull the callback out of its mutex entirely and release
191
+ // the lock on the mutex before executing the callback. Otherwise
192
+ // if the callback triggers its own WorkerActivity to change the
193
+ // callback then we would get a deadlock from double-locking the
194
+ // mutex.
195
+ let listener = { arc_listener. lock ( ) . unwrap ( ) . take ( ) } ;
196
+ if let Some ( mut listener) = listener {
197
+ match & mut listener {
198
+ ActivityListenerCallback :: Listen ( listen) => {
199
+ listen ( & mut * self . payload ) ;
200
+ }
201
+ ActivityListenerCallback :: Inert => {
202
+ // Do nothing
203
+ }
204
+ }
205
+
206
+ // We replace instead of assigning in case the callback
207
+ // inserted its own
208
+ arc_listener. lock ( ) . unwrap ( ) . replace ( listener) ;
209
+ }
210
+ }
211
+
212
+ self . activity_listeners . lock ( ) . unwrap ( ) . extend (
213
+ listeners. drain ( ..)
214
+ . map ( |x| Arc :: downgrade ( & x) )
215
+ ) ;
216
+ }
165
217
}
166
218
}
167
219
}
0 commit comments