@@ -230,7 +230,7 @@ use std::env::VarError;
230230use std:: fmt:: Debug ;
231231use std:: str:: FromStr ;
232232use std:: sync:: atomic:: { AtomicUsize , Ordering } ;
233- use std:: sync:: { Arc , Condvar , Mutex , MutexGuard , RwLock , TryLockError } ;
233+ use std:: sync:: { Arc , Condvar , Mutex , RwLock , TryLockError } ;
234234use std:: time:: { Duration , Instant } ;
235235use std:: { env, thread} ;
236236
@@ -421,7 +421,7 @@ impl FromStr for Action {
421421}
422422
423423#[ cfg_attr( feature = "cargo-clippy" , allow( clippy:: mutex_atomic) ) ]
424- #[ derive( Debug ) ]
424+ #[ derive( Debug , Default ) ]
425425struct FailPoint {
426426 pause : Mutex < bool > ,
427427 pause_notifier : Condvar ,
@@ -509,98 +509,115 @@ impl FailPoint {
509509/// Registry with failpoints configuration.
510510type Registry = HashMap < String , Arc < FailPoint > > ;
511511
512- #[ derive( Debug , Default ) ]
513- struct FailPointRegistry {
512+ /// Fail point registry. Each thread should be bound to exact one registry.
513+ /// Threads bound to the same registry share the same failpoints configuration.
514+ #[ derive( Debug , Default , Clone ) ]
515+ pub struct FailPointRegistry {
514516 // TODO: remove rwlock or store *mut FailPoint
515- registry : RwLock < Registry > ,
517+ registry : Arc < RwLock < Registry > > ,
516518}
517519
518- lazy_static:: lazy_static! {
519- static ref REGISTRY : FailPointRegistry = FailPointRegistry :: default ( ) ;
520- static ref SCENARIO : Mutex <& ' static FailPointRegistry > = Mutex :: new( & REGISTRY ) ;
520+ /// Generate a new failpoint registry. The new registry will inherit the
521+ /// global failpoints configuration.
522+ pub fn new_fail_group ( ) -> FailPointRegistry {
523+ let registry = REGISTRY_GLOBAL . registry . read ( ) . unwrap ( ) . clone ( ) ;
524+ FailPointRegistry {
525+ registry : Arc :: new ( RwLock :: new ( registry) ) ,
526+ }
521527}
522528
523- /// Test scenario with configured fail points.
524- #[ derive( Debug ) ]
525- pub struct FailScenario < ' a > {
526- scenario_guard : MutexGuard < ' a , & ' static FailPointRegistry > ,
527- }
529+ impl FailPointRegistry {
530+ /// Register the current thread to this failpoints registry.
531+ pub fn register_current ( & self ) {
532+ let id = thread:: current ( ) . id ( ) ;
533+ REGISTRY_GROUP
534+ . write ( )
535+ . unwrap ( )
536+ . insert ( id, self . registry . clone ( ) ) ;
537+ }
528538
529- impl < ' a > FailScenario < ' a > {
530- /// Set up the system for a fail points scenario.
531- ///
532- /// Configures all fail points specified in the `FAILPOINTS` environment variable.
533- /// It does not otherwise change any existing fail point configuration.
534- ///
535- /// The format of `FAILPOINTS` is `failpoint=actions;...`, where
536- /// `failpoint` is the name of the fail point. For more information
537- /// about fail point actions see the [`cfg`](fn.cfg.html) function and
538- /// the [`fail_point`](macro.fail_point.html) macro.
539- ///
540- /// `FAILPOINTS` may configure fail points that are not actually defined. In
541- /// this case the configuration has no effect.
542- ///
543- /// This function should generally be called prior to running a test with fail
544- /// points, and afterward paired with [`teardown`](#method.teardown).
545- ///
546- /// # Panics
547- ///
548- /// Panics if an action is not formatted correctly.
549- pub fn setup ( ) -> Self {
550- // Cleanup first, in case of previous failed/panic'ed test scenarios.
551- let scenario_guard = SCENARIO . lock ( ) . unwrap_or_else ( |e| e. into_inner ( ) ) ;
552- let mut registry = scenario_guard. registry . write ( ) . unwrap ( ) ;
553- Self :: cleanup ( & mut registry) ;
554-
555- let failpoints = match env:: var ( "FAILPOINTS" ) {
556- Ok ( s) => s,
557- Err ( VarError :: NotPresent ) => return Self { scenario_guard } ,
558- Err ( e) => panic ! ( "invalid failpoints: {:?}" , e) ,
559- } ;
560- for mut cfg in failpoints. trim ( ) . split ( ';' ) {
561- cfg = cfg. trim ( ) ;
562- if cfg. is_empty ( ) {
563- continue ;
564- }
565- let ( name, order) = partition ( cfg, '=' ) ;
566- match order {
567- None => panic ! ( "invalid failpoint: {:?}" , cfg) ,
568- Some ( order) => {
569- if let Err ( e) = set ( & mut registry, name. to_owned ( ) , order) {
570- panic ! ( "unable to configure failpoint \" {}\" : {}" , name, e) ;
571- }
572- }
573- }
574- }
575- Self { scenario_guard }
539+ /// Deregister the current thread to this failpoints registry.
540+ pub fn deregister_current ( & self ) {
541+ let id = thread:: current ( ) . id ( ) ;
542+ REGISTRY_GROUP . write ( ) . unwrap ( ) . remove ( & id) ;
576543 }
577544
578- /// Tear down the fail point system.
579- ///
580- /// Clears the configuration of all fail points. Any paused fail
581- /// points will be notified before they are deactivated.
582- ///
583- /// This function should generally be called after running a test with fail points.
584- /// Calling `teardown` without previously calling `setup` results in a no-op.
585- pub fn teardown ( self ) {
586- drop ( self )
545+ /// Clean registered fail points in this registry.
546+ pub fn cleanup ( & self ) {
547+ let mut registry = self . registry . write ( ) . unwrap ( ) ;
548+ cleanup ( & mut registry) ;
587549 }
550+ }
588551
589- /// Clean all registered fail points.
590- fn cleanup ( registry : & mut std:: sync:: RwLockWriteGuard < ' a , Registry > ) {
591- for p in registry. values ( ) {
592- // wake up all pause failpoint.
593- p. set_actions ( "" , vec ! [ ] ) ;
552+ lazy_static:: lazy_static! {
553+ static ref REGISTRY_GROUP : RwLock <HashMap <thread:: ThreadId , Arc <RwLock <Registry >>>> = Default :: default ( ) ;
554+ static ref REGISTRY_GLOBAL : FailPointRegistry = Default :: default ( ) ;
555+ }
556+
557+ /// Set up the system for a fail points scenario.
558+ ///
559+ /// Configures all fail points specified in the `FAILPOINTS` environment variable.
560+ /// It does not otherwise change any existing fail point configuration.
561+ ///
562+ /// The format of `FAILPOINTS` is `failpoint=actions;...`, where
563+ /// `failpoint` is the name of the fail point. For more information
564+ /// about fail point actions see the [`cfg`](fn.cfg.html) function and
565+ /// the [`fail_point`](macro.fail_point.html) macro.
566+ ///
567+ /// `FAILPOINTS` may configure fail points that are not actually defined. In
568+ /// this case the configuration has no effect.
569+ ///
570+ /// This function should generally be called prior to running a test with fail
571+ /// points, and afterward paired with [`teardown`](#method.teardown).
572+ ///
573+ /// # Panics
574+ ///
575+ /// Panics if an action is not formatted correctly.
576+ pub fn setup ( ) {
577+ let mut registry = REGISTRY_GLOBAL . registry . write ( ) . unwrap ( ) ;
578+ cleanup ( & mut registry) ;
579+
580+ let failpoints = match env:: var ( "FAILPOINTS" ) {
581+ Ok ( s) => s,
582+ Err ( VarError :: NotPresent ) => return ,
583+ Err ( e) => panic ! ( "invalid failpoints: {:?}" , e) ,
584+ } ;
585+ for mut cfg in failpoints. trim ( ) . split ( ';' ) {
586+ cfg = cfg. trim ( ) ;
587+ if cfg. is_empty ( ) {
588+ continue ;
589+ }
590+ let ( name, order) = partition ( cfg, '=' ) ;
591+ match order {
592+ None => panic ! ( "invalid failpoint: {:?}" , cfg) ,
593+ Some ( order) => {
594+ if let Err ( e) = set ( & mut registry, name. to_owned ( ) , order) {
595+ panic ! ( "unable to configure failpoint \" {}\" : {}" , name, e) ;
596+ }
597+ }
594598 }
595- registry. clear ( ) ;
596599 }
597600}
598601
599- impl < ' a > Drop for FailScenario < ' a > {
600- fn drop ( & mut self ) {
601- let mut registry = self . scenario_guard . registry . write ( ) . unwrap ( ) ;
602- Self :: cleanup ( & mut registry)
602+ /// Tear down the fail point system.
603+ ///
604+ /// Clears the configuration of all fail points. Any paused fail
605+ /// points will be notified before they are deactivated.
606+ ///
607+ /// This function should generally be called after running a test with fail points.
608+ /// Calling `teardown` without previously calling `setup` results in a no-op.
609+ pub fn teardown ( ) {
610+ let mut registry = REGISTRY_GLOBAL . registry . write ( ) . unwrap ( ) ;
611+ cleanup ( & mut registry) ;
612+ }
613+
614+ /// Clean all registered fail points.
615+ fn cleanup ( registry : & mut std:: sync:: RwLockWriteGuard < Registry > ) {
616+ for p in registry. values ( ) {
617+ // wake up all pause failpoint.
618+ p. set_actions ( "" , vec ! [ ] ) ;
603619 }
620+ registry. clear ( ) ;
604621}
605622
606623/// Returns whether code generation for failpoints is enabled.
@@ -616,7 +633,15 @@ pub const fn has_failpoints() -> bool {
616633///
617634/// Return a vector of `(name, actions)` pairs.
618635pub fn list ( ) -> Vec < ( String , String ) > {
619- let registry = REGISTRY . registry . read ( ) . unwrap ( ) ;
636+ let id = thread:: current ( ) . id ( ) ;
637+ let group = REGISTRY_GROUP . read ( ) . unwrap ( ) ;
638+
639+ let registry = group
640+ . get ( & id)
641+ . unwrap_or ( & REGISTRY_GLOBAL . registry )
642+ . read ( )
643+ . unwrap ( ) ;
644+
620645 registry
621646 . iter ( )
622647 . map ( |( name, fp) | ( name. to_string ( ) , fp. actions_str . read ( ) . unwrap ( ) . clone ( ) ) )
@@ -625,8 +650,16 @@ pub fn list() -> Vec<(String, String)> {
625650
626651#[ doc( hidden) ]
627652pub fn eval < R , F : FnOnce ( Option < String > ) -> R > ( name : & str , f : F ) -> Option < R > {
653+ let id = thread:: current ( ) . id ( ) ;
654+ let group = REGISTRY_GROUP . read ( ) . unwrap ( ) ;
655+
656+ let registry = group
657+ . get ( & id)
658+ . unwrap_or ( & REGISTRY_GLOBAL . registry )
659+ . read ( )
660+ . unwrap ( ) ;
661+
628662 let p = {
629- let registry = REGISTRY . registry . read ( ) . unwrap ( ) ;
630663 match registry. get ( name) {
631664 None => return None ,
632665 Some ( p) => p. clone ( ) ,
@@ -666,7 +699,14 @@ pub fn eval<R, F: FnOnce(Option<String>) -> R>(name: &str, f: F) -> Option<R> {
666699/// A call to `cfg` with a particular fail point name overwrites any existing actions for
667700/// that fail point, including those set via the `FAILPOINTS` environment variable.
668701pub fn cfg < S : Into < String > > ( name : S , actions : & str ) -> Result < ( ) , String > {
669- let mut registry = REGISTRY . registry . write ( ) . unwrap ( ) ;
702+ let id = thread:: current ( ) . id ( ) ;
703+ let group = REGISTRY_GROUP . read ( ) . unwrap ( ) ;
704+ let mut registry = group
705+ . get ( & id)
706+ . unwrap_or ( & REGISTRY_GLOBAL . registry )
707+ . write ( )
708+ . unwrap ( ) ;
709+
670710 set ( & mut registry, name. into ( ) , actions)
671711}
672712
@@ -679,7 +719,14 @@ where
679719 S : Into < String > ,
680720 F : Fn ( ) + Send + Sync + ' static ,
681721{
682- let mut registry = REGISTRY . registry . write ( ) . unwrap ( ) ;
722+ let id = thread:: current ( ) . id ( ) ;
723+ let group = REGISTRY_GROUP . read ( ) . unwrap ( ) ;
724+ let mut registry = group
725+ . get ( & id)
726+ . unwrap_or ( & REGISTRY_GLOBAL . registry )
727+ . write ( )
728+ . unwrap ( ) ;
729+
683730 let p = registry
684731 . entry ( name. into ( ) )
685732 . or_insert_with ( || Arc :: new ( FailPoint :: new ( ) ) ) ;
@@ -691,9 +738,17 @@ where
691738
692739/// Remove a fail point.
693740///
694- /// If the fail point doesn't exist, nothing will happen.
741+ /// If the fail point doesn't exist, it will try to delete the corresponding
742+ /// action in the `REGISTRY_GLOBAL`.
695743pub fn remove < S : AsRef < str > > ( name : S ) {
696- let mut registry = REGISTRY . registry . write ( ) . unwrap ( ) ;
744+ let id = thread:: current ( ) . id ( ) ;
745+ let group = REGISTRY_GROUP . read ( ) . unwrap ( ) ;
746+ let mut registry = group
747+ . get ( & id)
748+ . unwrap_or ( & REGISTRY_GLOBAL . registry )
749+ . write ( )
750+ . unwrap ( ) ;
751+
697752 if let Some ( p) = registry. remove ( name. as_ref ( ) ) {
698753 // wake up all pause failpoint.
699754 p. set_actions ( "" , vec ! [ ] ) ;
@@ -990,10 +1045,7 @@ mod tests {
9901045 }
9911046 }
9921047
993- // This case should be tested as integration case, but when calling `teardown` other cases
994- // like `test_pause` maybe also affected, so it's better keep it here.
9951048 #[ test]
996- #[ cfg_attr( not( feature = "failpoints" ) , ignore) ]
9971049 fn test_setup_and_teardown ( ) {
9981050 let f1 = || {
9991051 fail_point ! ( "setup_and_teardown1" , |_| 1 ) ;
@@ -1007,7 +1059,7 @@ mod tests {
10071059 "FAILPOINTS" ,
10081060 "setup_and_teardown1=return;setup_and_teardown2=pause;" ,
10091061 ) ;
1010- let scenario = FailScenario :: setup ( ) ;
1062+ setup ( ) ;
10111063 assert_eq ! ( f1( ) , 1 ) ;
10121064
10131065 let ( tx, rx) = mpsc:: channel ( ) ;
@@ -1016,7 +1068,7 @@ mod tests {
10161068 } ) ;
10171069 assert ! ( rx. recv_timeout( Duration :: from_millis( 500 ) ) . is_err( ) ) ;
10181070
1019- scenario . teardown ( ) ;
1071+ teardown ( ) ;
10201072 assert_eq ! ( rx. recv_timeout( Duration :: from_millis( 500 ) ) . unwrap( ) , 0 ) ;
10211073 assert_eq ! ( f1( ) , 0 ) ;
10221074 }
0 commit comments