@@ -5,13 +5,13 @@ use futures::channel::{
55
66use std:: {
77 any:: Any ,
8- sync:: { atomic:: { AtomicBool , Ordering } , Arc } ,
8+ sync:: { atomic:: { AtomicBool , Ordering } , Arc , Mutex } ,
99 time:: { Duration , Instant } ,
1010} ;
1111
1212use crate :: {
1313 Context , Promise , RclrsError , WaitSet , Waitable , GuardCondition , ExecutorWorkerOptions ,
14- PayloadTask ,
14+ PayloadTask , WeakActivityListener , ActivityListenerCallback ,
1515} ;
1616
1717/// This is a utility class that executors can use to easily run and manage
@@ -22,6 +22,7 @@ pub struct WaitSetRunner {
2222 waitable_receiver : UnboundedReceiver < Waitable > ,
2323 task_sender : UnboundedSender < PayloadTask > ,
2424 task_receiver : UnboundedReceiver < PayloadTask > ,
25+ activity_listeners : Arc < Mutex < Vec < WeakActivityListener > > > ,
2526 guard_condition : Arc < GuardCondition > ,
2627 payload : Box < dyn Any + Send > ,
2728}
@@ -62,6 +63,7 @@ impl WaitSetRunner {
6263 waitable_receiver,
6364 task_sender,
6465 task_receiver,
66+ activity_listeners : Arc :: default ( ) ,
6567 guard_condition : worker_options. guard_condition ,
6668 payload : worker_options. payload ,
6769 }
@@ -79,6 +81,12 @@ impl WaitSetRunner {
7981 self . task_sender . clone ( )
8082 }
8183
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+
8290 /// Get the guard condition associated with the wait set of this runner.
8391 pub fn guard_condition ( & self ) -> & Arc < GuardCondition > {
8492 & self . guard_condition
@@ -111,6 +119,7 @@ impl WaitSetRunner {
111119 /// will be triggered after the user-provided promise is resolved.
112120 pub fn run_blocking ( & mut self , conditions : WaitSetRunConditions ) -> Result < ( ) , RclrsError > {
113121 let mut first_spin = true ;
122+ let mut listeners = Vec :: new ( ) ;
114123 loop {
115124 // TODO(@mxgrey): SmallVec would be better suited here if we are
116125 // okay with adding that as a dependency.
@@ -124,7 +133,7 @@ impl WaitSetRunner {
124133 }
125134
126135 while let Ok ( Some ( task) ) = self . task_receiver . try_next ( ) {
127- task ( & mut self . payload ) ;
136+ task ( & mut * self . payload ) ;
128137 }
129138
130139 if conditions. only_next_available_work && !first_spin {
@@ -154,14 +163,57 @@ impl WaitSetRunner {
154163 }
155164 } ) ;
156165
166+ let mut at_least_one = false ;
157167 self . wait_set . wait ( timeout, |executable| {
168+ at_least_one = true ;
158169 // SAFETY: The user of WaitSetRunner is responsible for ensuring
159170 // the runner has the same payload type as the executables that
160171 // are given to it.
161172 unsafe {
162- executable. execute ( & mut self . payload )
173+ executable. execute ( & mut * self . payload )
163174 }
164175 } ) ?;
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+ }
165217 }
166218 }
167219}
0 commit comments