@@ -17,7 +17,7 @@ use std::fmt::Debug;
1717use std:: hash:: Hash ;
1818use std:: ops:: Add ;
1919use std:: sync:: atomic:: { AtomicU32 , AtomicU64 , Ordering } ;
20- use std:: sync:: { Arc , Mutex } ;
20+ use std:: sync:: { Arc , Mutex , MutexGuard } ;
2121use std:: time:: Duration ;
2222use tokio:: select;
2323use tokio:: sync:: Semaphore ;
5454 fetcher_semaphore : Semaphore ,
5555}
5656
57- #[ derive( Default , Serialize , Deserialize ) ]
57+ #[ derive( Debug , Default , Serialize , Deserialize ) ]
5858pub struct MultiTargetStats {
5959 known_runtimes : u32 ,
6060 starting_fetchers : u32 ,
@@ -109,6 +109,10 @@ pub trait MultiTargetHandlers<S> {
109109
110110 fn expired ( & self , target : & Arc < Target > ) ;
111111
112+ /// Called when a fetcher has no active runtimes.
113+ /// Beware: This function may be called at any time, e.g. another thread might be attempting to
114+ /// call add_runtime() right when no other runtime was left. Be careful with the locking here.
115+ /// Will not be called multiple times, unless this specific case was encountered.
112116 fn dead ( & self ) ;
113117}
114118
@@ -162,7 +166,12 @@ where
162166 uuid:: Uuid :: new_v4 ( ) . to_string ( )
163167 }
164168
165- fn remove_target ( self : & Arc < Self > , runtime_id : & str , target : & Arc < Target > ) {
169+ fn remove_target (
170+ self : & Arc < Self > ,
171+ runtime_id : & str ,
172+ target : & Arc < Target > ,
173+ runtimes : MutexGuard < HashMap < String , RuntimeInfo < N > > > ,
174+ ) {
166175 let mut services = self . services . lock ( ) . unwrap ( ) ;
167176 // "goto" like handling to drop the known_service borrow and be able to change services
168177 ' service_handling: {
@@ -181,7 +190,8 @@ where
181190 let target = target. clone ( ) ;
182191 tokio:: spawn ( async move {
183192 future. await ;
184- this. remove_target ( runtime_id. as_str ( ) , & target) ;
193+ let runtimes = this. runtimes . lock ( ) . unwrap ( ) ;
194+ this. remove_target ( runtime_id. as_str ( ) , & target, runtimes) ;
185195 } ) ;
186196 return ;
187197 }
@@ -213,7 +223,7 @@ where
213223 _ => {
214224 if * known_service. fetcher . runtime_id . lock ( ) . unwrap ( ) == runtime_id {
215225 ' changed_rt_id: {
216- for ( id, runtime) in self . runtimes . lock ( ) . unwrap ( ) . iter ( ) {
226+ for ( id, runtime) in runtimes. iter ( ) {
217227 if runtime. targets . len ( ) == 1
218228 && runtime. targets . contains_key ( target)
219229 {
@@ -366,7 +376,7 @@ where
366376 notify_target,
367377 targets : HashMap :: from ( [ ( target. clone ( ) , 1 ) ] ) ,
368378 } ;
369- self . add_target ( info . targets . len ( ) > 1 , e. key ( ) , target. clone ( ) ) ;
379+ self . add_target ( false , e. key ( ) , target. clone ( ) ) ;
370380 e. insert ( info) ;
371381 }
372382 }
@@ -375,31 +385,29 @@ where
375385
376386 pub fn delete_runtime ( self : & Arc < Self > , runtime_id : & str , target : & Arc < Target > ) {
377387 trace ! ( "Removing remote config runtime: {target:?} with runtime id {runtime_id}" ) ;
378- {
379- let mut runtimes = self . runtimes . lock ( ) . unwrap ( ) ;
380- let last_removed = {
381- let info = match runtimes. get_mut ( runtime_id) {
382- None => return ,
383- Some ( i) => i,
384- } ;
385- match info. targets . entry ( target. clone ( ) ) {
386- Entry :: Occupied ( mut e) => {
387- if * e. get ( ) == 1 {
388- e. remove ( ) ;
389- } else {
390- * e. get_mut ( ) -= 1 ;
391- return ;
392- }
388+ let mut runtimes = self . runtimes . lock ( ) . unwrap ( ) ;
389+ let last_removed = {
390+ let info = match runtimes. get_mut ( runtime_id) {
391+ None => return ,
392+ Some ( i) => i,
393+ } ;
394+ match info. targets . entry ( target. clone ( ) ) {
395+ Entry :: Occupied ( mut e) => {
396+ if * e. get ( ) == 1 {
397+ e. remove ( ) ;
398+ } else {
399+ * e. get_mut ( ) -= 1 ;
400+ return ;
393401 }
394- Entry :: Vacant ( _) => unreachable ! ( "Missing target runtime" ) ,
395402 }
396- info. targets . is_empty ( )
397- } ;
398- if last_removed {
399- runtimes. remove ( runtime_id) ;
403+ Entry :: Vacant ( _) => unreachable ! ( "Missing target runtime" ) ,
400404 }
405+ info. targets . is_empty ( )
406+ } ;
407+ if last_removed {
408+ runtimes. remove ( runtime_id) ;
401409 }
402- Self :: remove_target ( self , runtime_id, target) ;
410+ Self :: remove_target ( self , runtime_id, target, runtimes ) ;
403411 }
404412
405413 /// Sets the apply state on a stored file.
@@ -510,18 +518,18 @@ where
510518
511519 this. storage . storage . expired ( & fetcher. target ) ;
512520
513- {
521+ let is_dead = {
514522 // scope lock before await
515523 trace ! (
516524 "Remove {:?} from services map at fetcher end" ,
517525 fetcher. target
518526 ) ;
519527 let mut services = this. services . lock ( ) . unwrap ( ) ;
520528 services. remove ( & fetcher. target ) ;
521- if services. is_empty ( ) && this. pending_async_insertions . load ( Ordering :: Relaxed ) == 0
522- {
523- this . storage . storage . dead ( ) ;
524- }
529+ services. is_empty ( ) && this. pending_async_insertions . load ( Ordering :: Relaxed ) == 0
530+ } ;
531+ if is_dead {
532+ this . storage . storage . dead ( ) ;
525533 }
526534 remove_completer. complete ( ( ) ) . await ;
527535 } ) ;
@@ -570,7 +578,7 @@ where
570578 ( starting, active, inactive, removing)
571579 } ;
572580 MultiTargetStats {
573- known_runtimes : self . runtimes . lock ( ) . unwrap ( ) . len ( ) as u32 ,
581+ known_runtimes : self . active_runtimes ( ) as u32 ,
574582 starting_fetchers,
575583 active_fetchers,
576584 inactive_fetchers,
0 commit comments