Skip to main content

fail_parallel/
lib.rs

1// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
2
3//! A fail point implementation for Rust.
4//!
5//! Fail points are code instrumentations that allow errors and other behavior
6//! to be injected dynamically at runtime, primarily for testing purposes. Fail
7//! points are flexible and can be configured to exhibit a variety of behavior,
8//! including panics, early returns, and sleeping. They can be controlled both
9//! programmatically and via the environment, and can be triggered
10//! conditionally and probabilistically.
11//!
12//! This crate is inspired by FreeBSD's
13//! [failpoints](https://freebsd.org/cgi/man.cgi?query=fail).
14//!
15//! ## Usage
16//!
17//! You can import the `fail_point!` macro from this module to inject dynamic failures.
18//!
19//! As an example, here's a simple program that uses a fail point to simulate an
20//! I/O panic:
21//!
22//! ```rust, ignore
23//! use crate::failpoints::{fail_point, FailScenario, FailPointRegistry};
24//! use std::sync::Arc;
25//!
26//! fn do_fallible_work(fp_registry: Arc<FailPointRegistry>) {
27//!     fail_point!(fp_registry, "read-dir");
28//!     let _dir: Vec<_> = std::fs::read_dir(".").unwrap().collect();
29//!     // ... do some work on the directory ...
30//! }
31//!
32//! let registry = Arc::new(FailPointRegistry::new());
33//! let scenario = FailScenario::setup(registry.clone());
34//! do_fallible_work(fp_registry.clone());
35//! scenario.teardown();
36//! println!("done");
37//! ```
38//!
39//! Here, the program calls `unwrap` on the result of `read_dir`, a function
40//! that returns a `Result`. In other words, this particular program expects
41//! this call to `read_dir` to always succeed. And in practice it almost always
42//! will, which makes the behavior of this program when `read_dir` fails
43//! difficult to test. By instrumenting the program with a fail point we can
44//! pretend that `read_dir` failed, causing the subsequent `unwrap` to panic,
45//! and allowing us to observe the program's behavior under failure conditions.
46//!
47//! When the program is run normally it just prints "done":
48//!
49//! ```sh
50//! $ cargo run --features fail/failpoints
51//!     Finished dev [unoptimized + debuginfo] target(s) in 0.01s
52//!      Running `target/debug/failpointtest`
53//! done
54//! ```
55//!
56//! But now, by setting the `FAILPOINTS` variable we can see what happens if the
57//! `read_dir` fails:
58//!
59//! ```sh
60//! FAILPOINTS=read-dir=panic cargo run --features fail/failpoints
61//!     Finished dev [unoptimized + debuginfo] target(s) in 0.01s
62//!      Running `target/debug/failpointtest`
63//! thread 'main' panicked at 'failpoint read-dir panic', /home/ubuntu/.cargo/registry/src/github.com-1ecc6299db9ec823/fail-0.2.0/src/lib.rs:286:25
64//! note: Run with `RUST_BACKTRACE=1` for a backtrace.
65//! ```
66//!
67//! ## Usage in tests
68//!
69//! The previous example triggers a fail point by modifying the `FAILPOINTS`
70//! environment variable. In practice, you'll often want to trigger fail points
71//! programmatically, in unit tests.
72//! Fail points are global resources, and Rust tests run in parallel,
73//! so tests that exercise fail points generally need to hold a lock to
74//! avoid interfering with each other. This is accomplished by `FailScenario`.
75//!
76//! Here's a basic pattern for writing unit tests tests with fail points:
77//!
78//! ```rust, ignore,no_run
79//! use crate::failpoints::{fail_point, FailScenario, FailPointRegistry};
80//! use std::sync::Arc;
81//!
82//! fn do_fallible_work(fp_registry: Arc<FailPointRegistry>) {
83//!     fail_point!(fp_registry, "read-dir");
84//!     let _dir: Vec<_> = std::fs::read_dir(".").unwrap().collect();
85//!     // ... do some work on the directory ...
86//! }
87//!
88//! #[test]
89//! #[should_panic]
90//! fn test_fallible_work() {
91//!     let fp_registry = Arc::new(FailPointRegistry::new());
92//!     fail::cfg(fp_registry.clone(), "read-dir", "panic").unwrap();
93//!
94//!     do_fallible_work(fp_registry.clone());
95//! }
96//! ```
97//!
98//! ## Early return
99//!
100//! The previous examples illustrate injecting panics via fail points, but
101//! panics aren't the only &mdash; or even the most common &mdash; error pattern
102//! in Rust. The more common type of error is propagated by `Result` return
103//! values, and fail points can inject those as well with "early returns". That
104//! is, when configuring a fail point as "return" (as opposed to "panic"), the
105//! fail point will immediately return from the function, optionally with a
106//! configurable value.
107//!
108//! The setup for early return requires a slightly diferent invocation of the
109//! `fail_point!` macro. To illustrate this, let's modify the `do_fallible_work`
110//! function we used earlier to return a `Result`:
111//!
112//! ```rust, ignore
113//! use crate::failpoints::{fail_point, FailScenario};
114//! use std::io;
115//! use std::sync::Arc;
116//!
117//! fn do_fallible_work(fp_registry: Arc<FailPointRegistry>) -> io::Result<()> {
118//!     fail_point!(fp_registry, "read-dir");
119//!     let _dir: Vec<_> = std::fs::read_dir(".")?.collect();
120//!     // ... do some work on the directory ...
121//!     Ok(())
122//! }
123//!
124//! fn main() -> io::Result<()> {
125//!     let fp_registry = Arc::new(FailPointRegistry::new());
126//!     do_fallible_work(fp_registry.clone())?;
127//!     println!("done");
128//!     Ok(())
129//! }
130//! ```
131//!
132//! This example has more proper Rust error handling, with no unwraps
133//! anywhere. Instead it uses `?` to propagate errors via the `Result` type
134//! return values. This is more realistic Rust code.
135//!
136//! The "read-dir" fail point though is not yet configured to support early
137//! return, so if we attempt to configure it to "return", we'll see an error
138//! like
139//!
140//! ```sh
141//! $ FAILPOINTS=read-dir=return cargo run --features fail/failpoints
142//!     Finished dev [unoptimized + debuginfo] target(s) in 0.13s
143//!      Running `target/debug/failpointtest`
144//! thread 'main' panicked at 'Return is not supported for the fail point "read-dir"', src/main.rs:7:5
145//! note: Run with `RUST_BACKTRACE=1` for a backtrace.
146//! ```
147//!
148//! This error tells us that the "read-dir" fail point is not defined correctly
149//! to support early return, and gives us the line number of that fail point.
150//! What we're missing in the fail point definition is code describring _how_ to
151//! return an error value, and the way we do this is by passing `fail_point!` a
152//! closure that returns the same type as the enclosing function.
153//!
154//! Here's a variation that does so:
155//!
156//! ```rust, ignore
157//! # use std::io;
158//! use std::sync::Arc;
159//!
160//! fn do_fallible_work(fp_registry: Arc<FailPointRegistry>) -> io::Result<()> {
161//!     fail::fail_point!(fp_registry, "read-dir", |_| {
162//!         Err(io::Error::new(io::ErrorKind::PermissionDenied, "error"))
163//!     });
164//!     let _dir: Vec<_> = std::fs::read_dir(".")?.collect();
165//!     // ... do some work on the directory ...
166//!     Ok(())
167//! }
168//! ```
169//!
170//! And now if the "read-dir" fail point is configured to "return" we get a
171//! different result:
172//!
173//! ```sh
174//! $ FAILPOINTS=read-dir=return cargo run --features fail/failpoints
175//!    Compiling failpointtest v0.1.0
176//!     Finished dev [unoptimized + debuginfo] target(s) in 2.38s
177//!      Running `target/debug/failpointtest`
178//! Error: Custom { kind: PermissionDenied, error: StringError("error") }
179//! ```
180//!
181//! This time, `do_fallible_work` returned the error defined in our closure,
182//! which propagated all the way up and out of main.
183//!
184//! ## Advanced usage
185//!
186//! That's the basics of fail points: defining them with `fail_point!`,
187//! configuring them with `FAILPOINTS` and `fail::cfg`, and configuring them to
188//! panic and return early. But that's not all they can do. To learn more see
189//! the documentation for [`cfg`](fn.cfg.html),
190//! [`cfg_callback`](fn.cfg_callback.html) and
191//! [`fail_point!`](macro.fail_point.html).
192//!
193//!
194//! ## Usage considerations
195//!
196//! For most effective fail point usage, keep in mind the following:
197//!
198//!  - Fail points are disabled by default and can be enabled via the `failpoints`
199//!    feature. When failpoints are disabled, no code is generated by the macro.
200//!  - Fail points might have the same name, in which case they take the
201//!    same actions. Be careful about duplicating fail point names, either within
202//!    a single crate, or across multiple crates.
203
204#![deny(missing_docs, missing_debug_implementations)]
205#![allow(warnings)]
206
207use std::collections::HashMap;
208use std::env::VarError;
209use std::fmt::Debug;
210use std::str::FromStr;
211use std::sync::atomic::Ordering::Relaxed;
212use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
213use std::sync::{Arc, Condvar, Mutex, RwLock, TryLockError};
214use std::time::{Duration, Instant};
215use std::{env, mem, thread};
216
217#[derive(Clone)]
218struct SyncCallback(Arc<dyn Fn() + Send + Sync>);
219
220impl Debug for SyncCallback {
221    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
222        f.write_str("SyncCallback()")
223    }
224}
225
226impl PartialEq for SyncCallback {
227    fn eq(&self, other: &Self) -> bool {
228        Arc::ptr_eq(&self.0, &other.0)
229    }
230}
231
232impl SyncCallback {
233    #[allow(dead_code)]
234    fn new(f: impl Fn() + Send + Sync + 'static) -> SyncCallback {
235        SyncCallback(Arc::new(f))
236    }
237
238    #[allow(dead_code)]
239    fn run(&self) {
240        let callback = &self.0;
241        callback();
242    }
243}
244
245/// Supported tasks.
246#[derive(Clone, Debug, PartialEq)]
247enum Task {
248    /// Do nothing.
249    Off,
250    /// Return the value.
251    Return(Option<String>),
252    /// Sleep for some milliseconds.
253    Sleep(u64),
254    /// Panic with the message.
255    Panic(Option<String>),
256    /// Print the message.
257    Print(Option<String>),
258    /// Sleep until other action is set.
259    Pause,
260    /// Yield the CPU.
261    Yield,
262    /// Busy waiting for some milliseconds.
263    Delay(u64),
264    /// Call callback function.
265    #[allow(dead_code)]
266    Callback(SyncCallback),
267}
268
269#[derive(Debug)]
270struct Action {
271    task: Task,
272    freq: f32,
273    count: Option<AtomicUsize>,
274}
275
276impl PartialEq for Action {
277    fn eq(&self, hs: &Action) -> bool {
278        if self.task != hs.task || self.freq != hs.freq {
279            return false;
280        }
281        if let Some(ref lhs) = self.count {
282            if let Some(ref rhs) = hs.count {
283                return lhs.load(Ordering::Relaxed) == rhs.load(Ordering::Relaxed);
284            }
285        } else if hs.count.is_none() {
286            return true;
287        }
288        false
289    }
290}
291
292impl Action {
293    fn new(task: Task, freq: f32, max_cnt: Option<usize>) -> Action {
294        Action {
295            task,
296            freq,
297            count: max_cnt.map(AtomicUsize::new),
298        }
299    }
300
301    #[allow(dead_code)]
302    fn from_callback(f: impl Fn() + Send + Sync + 'static) -> Action {
303        let task = Task::Callback(SyncCallback::new(f));
304        Action {
305            task,
306            freq: 1.0,
307            count: None,
308        }
309    }
310
311    #[allow(dead_code)]
312    fn get_task(&self) -> Option<Task> {
313        use rand::Rng;
314
315        if let Some(ref cnt) = self.count {
316            let c = cnt.load(Ordering::Acquire);
317            if c == 0 {
318                return None;
319            }
320        }
321        if self.freq < 1f32 && !rand::rng().gen_bool(f64::from(self.freq)) {
322            return None;
323        }
324        if let Some(ref ref_cnt) = self.count {
325            let mut cnt = ref_cnt.load(Ordering::Acquire);
326            loop {
327                if cnt == 0 {
328                    return None;
329                }
330                let new_cnt = cnt - 1;
331                match ref_cnt.compare_exchange_weak(
332                    cnt,
333                    new_cnt,
334                    Ordering::AcqRel,
335                    Ordering::Acquire,
336                ) {
337                    Ok(_) => break,
338                    Err(c) => cnt = c,
339                }
340            }
341        }
342        Some(self.task.clone())
343    }
344}
345
346fn partition(s: &str, pattern: char) -> (&str, Option<&str>) {
347    let mut splits = s.splitn(2, pattern);
348    (splits.next().unwrap(), splits.next())
349}
350
351impl FromStr for Action {
352    type Err = String;
353
354    /// Parse an action.
355    ///
356    /// `s` should be in the format `[p%][cnt*]task[(args)]`, `p%` is the frequency,
357    /// `cnt` is the max times the action can be triggered.
358    fn from_str(s: &str) -> Result<Action, String> {
359        let mut remain = s.trim();
360        let mut args = None;
361        // in case there is '%' in args, we need to parse it first.
362        let (first, second) = partition(remain, '(');
363        if let Some(second) = second {
364            remain = first;
365            if !second.ends_with(')') {
366                return Err("parentheses do not match".to_owned());
367            }
368            args = Some(&second[..second.len() - 1]);
369        }
370
371        let mut frequency = 1f32;
372        let (first, second) = partition(remain, '%');
373        if let Some(second) = second {
374            remain = second;
375            match first.parse::<f32>() {
376                Err(e) => return Err(format!("failed to parse frequency: {}", e)),
377                Ok(freq) => frequency = freq / 100.0,
378            }
379        }
380
381        let mut max_cnt = None;
382        let (first, second) = partition(remain, '*');
383        if let Some(second) = second {
384            remain = second;
385            match first.parse() {
386                Err(e) => return Err(format!("failed to parse count: {}", e)),
387                Ok(cnt) => max_cnt = Some(cnt),
388            }
389        }
390
391        let parse_timeout = || match args {
392            None => Err("sleep require timeout".to_owned()),
393            Some(timeout_str) => match timeout_str.parse() {
394                Err(e) => Err(format!("failed to parse timeout: {}", e)),
395                Ok(timeout) => Ok(timeout),
396            },
397        };
398
399        let task = match remain {
400            "off" => Task::Off,
401            "return" => Task::Return(args.map(str::to_owned)),
402            "sleep" => Task::Sleep(parse_timeout()?),
403            "panic" => Task::Panic(args.map(str::to_owned)),
404            "print" => Task::Print(args.map(str::to_owned)),
405            "pause" => Task::Pause,
406            "yield" => Task::Yield,
407            "delay" => Task::Delay(parse_timeout()?),
408            _ => return Err(format!("unrecognized command {:?}", remain)),
409        };
410
411        Ok(Action::new(task, frequency, max_cnt))
412    }
413}
414
415#[derive(Debug)]
416struct FailPoint {
417    actions: Mutex<ConfiguredActions>,
418    sync_notifier: Condvar,
419    async_notifier: AsyncNotifier,
420}
421
422#[derive(Debug)]
423struct AsyncNotifier {
424    tx: tokio::sync::watch::Sender<u64>,
425    rx: tokio::sync::watch::Receiver<u64>,
426}
427
428#[derive(Debug)]
429struct ConfiguredActions {
430    seq: u64,
431    actions_str: String,
432    actions: Vec<Action>,
433}
434
435impl ConfiguredActions {
436    fn empty(seq: u64) -> ConfiguredActions {
437        ConfiguredActions {
438            seq,
439            actions_str: String::new(),
440            actions: vec![],
441        }
442    }
443}
444
445impl AsyncNotifier {
446    fn new() -> AsyncNotifier {
447        let (tx, rx) = tokio::sync::watch::channel(0);
448        AsyncNotifier { tx, rx }
449    }
450}
451
452impl FailPoint {
453    #[allow(dead_code)]
454    fn new() -> FailPoint {
455        let initial_seq: u64 = 0;
456        let initial_actions = ConfiguredActions::empty(initial_seq);
457
458        FailPoint {
459            actions: Mutex::new(initial_actions),
460            sync_notifier: Condvar::new(),
461            async_notifier: AsyncNotifier::new(),
462        }
463    }
464
465    fn actions_str(&self) -> String {
466        let actions_guard = self.actions.lock().unwrap();
467        (*actions_guard).actions_str.clone()
468    }
469
470    fn set_actions(&self, actions_str: &str, actions: Vec<Action>) {
471        let mut actions_guard = self.actions.lock().unwrap();
472        let next_seq = (*actions_guard).seq + 1;
473        *actions_guard = ConfiguredActions {
474            seq: next_seq,
475            actions_str: actions_str.to_string(),
476            actions,
477        };
478        self.sync_notifier.notify_all();
479        self.async_notifier.tx.send(next_seq).unwrap();
480    }
481
482    #[allow(dead_code)]
483    #[allow(clippy::option_option)]
484    fn eval(&self, name: &str) -> Option<Option<String>> {
485        let (task_opt, action_seq) = self.next_task();
486        if let Some(task) = task_opt {
487            self.eval_task(action_seq, name, task)
488        } else {
489            None
490        }
491    }
492
493    fn eval_task(&self, action_seq: u64, name: &str, task: Task) -> Option<Option<String>> {
494        match task {
495            Task::Off => {}
496            Task::Return(s) => return Some(s),
497            Task::Sleep(t) => thread::sleep(Duration::from_millis(t)),
498            Task::Panic(msg) => match msg {
499                Some(ref msg) => panic!("{}", msg),
500                None => panic!("failpoint {} panic", name),
501            },
502            Task::Print(msg) => match msg {
503                Some(ref msg) => log::info!("{}", msg),
504                None => log::info!("failpoint {} executed.", name),
505            },
506            Task::Pause => {
507                let _unused = self
508                    .sync_notifier
509                    .wait_while(self.actions.lock().unwrap(), |guard| {
510                        (*guard).seq == action_seq
511                    })
512                    .unwrap();
513            }
514            Task::Yield => thread::yield_now(),
515            Task::Delay(t) => {
516                let timer = Instant::now();
517                let timeout = Duration::from_millis(t);
518                while timer.elapsed() < timeout {}
519            }
520            Task::Callback(f) => {
521                f.run();
522            }
523        }
524        None
525    }
526
527    #[allow(dead_code)]
528    #[allow(clippy::option_option)]
529    async fn eval_async(&self, name: &str) -> Option<Option<String>> {
530        let (task_opt, action_seq) = self.next_task();
531        if let Some(task) = task_opt {
532            self.eval_task_async(action_seq, name, task).await
533        } else {
534            None
535        }
536    }
537
538    fn next_task(&self) -> (Option<Task>, u64) {
539        let guard = self.actions.lock().unwrap();
540        let task = guard.actions.iter().filter_map(Action::get_task).next();
541        (task, (*guard).seq)
542    }
543
544    async fn eval_task_async(
545        &self,
546        action_seq: u64,
547        name: &str,
548        task: Task,
549    ) -> Option<Option<String>> {
550        match task {
551            Task::Off => {}
552            Task::Return(s) => return Some(s),
553            Task::Sleep(t) => tokio::time::sleep(Duration::from_millis(t)).await,
554            Task::Panic(msg) => match msg {
555                Some(ref msg) => panic!("{}", msg),
556                None => panic!("failpoint {} panic", name),
557            },
558            Task::Print(msg) => match msg {
559                Some(ref msg) => log::info!("{}", msg),
560                None => log::info!("failpoint {} executed.", name),
561            },
562            Task::Pause => {
563                let mut rx = self.async_notifier.rx.clone();
564                rx.wait_for(|val| *val != action_seq).await.unwrap();
565            }
566            Task::Yield => tokio::task::yield_now().await,
567            Task::Delay(t) => {
568                let timer = Instant::now();
569                let timeout = Duration::from_millis(t);
570                while timer.elapsed() < timeout {}
571            }
572            Task::Callback(f) => {
573                f.run();
574            }
575        }
576        None
577    }
578}
579
580/// Registry with failpoints configuration.
581type Registry = HashMap<String, Arc<FailPoint>>;
582
583/// A public failpoint registry that's meant to be used in tests.
584#[derive(Debug, Default)]
585pub struct FailPointRegistry {
586    // TODO: remove rwlock or store *mut FailPoint
587    registry: RwLock<Registry>,
588}
589
590impl FailPointRegistry {
591    /// Create a new fail point registry.
592    pub fn new() -> Self {
593        Self {
594            registry: RwLock::new(Registry::new()),
595        }
596    }
597}
598
599/// A failpoint registry and event sender used by [`fail_point_send!`].
600#[derive(Clone, Debug)]
601pub struct FailPointTx {
602    fp_registry: Arc<FailPointRegistry>,
603    event_tx: tokio::sync::mpsc::UnboundedSender<String>,
604}
605
606impl FailPointTx {
607    /// Creates a handle backed by an empty registry and a disconnected event channel.
608    pub fn dummy() -> Self {
609        let (event_tx, _) = tokio::sync::mpsc::unbounded_channel();
610        Self {
611            fp_registry: Arc::new(FailPointRegistry::new()),
612            event_tx,
613        }
614    }
615
616    #[doc(hidden)]
617    pub fn registry(&self) -> Arc<FailPointRegistry> {
618        self.fp_registry.clone()
619    }
620
621    #[doc(hidden)]
622    pub fn send(&self, event: String) {
623        let _ = self.event_tx.send(event);
624    }
625}
626
627/// Creates a failpoint event channel associated with `fp_registry`.
628///
629/// The returned [`FailPointTx`] can be passed to [`fail_point_send!`]. The
630/// receiver yields the name of each failpoint reached through that macro.
631pub fn fail_point_channel(
632    fp_registry: Arc<FailPointRegistry>,
633) -> (FailPointTx, tokio::sync::mpsc::UnboundedReceiver<String>) {
634    let (event_tx, event_rx) = tokio::sync::mpsc::unbounded_channel();
635    (
636        FailPointTx {
637            fp_registry,
638            event_tx,
639        },
640        event_rx,
641    )
642}
643
644/// Test scenario with configured fail points.
645#[derive(Debug)]
646pub struct FailScenario {
647    fp_registry: Arc<FailPointRegistry>,
648}
649
650impl FailScenario {
651    /// Set up the system for a fail points scenario.
652    ///
653    /// Configures all fail points specified in the `FAILPOINTS` environment variable.
654    /// It does not otherwise change any existing fail point configuration.
655    ///
656    /// The format of `FAILPOINTS` is `failpoint=actions;...`, where
657    /// `failpoint` is the name of the fail point. For more information
658    /// about fail point actions see the [`cfg`](fn.cfg.html) function and
659    /// the [`fail_point`](macro.fail_point.html) macro.
660    ///
661    /// `FAILPOINTS` may configure fail points that are not actually defined. In
662    /// this case the configuration has no effect.
663    ///
664    /// This function should generally be called prior to running a test with fail
665    /// points, and afterward paired with [`teardown`](#method.teardown).
666    ///
667    /// # Panics
668    ///
669    /// Panics if an action is not formatted correctly.
670    pub fn setup(fp_registry: Arc<FailPointRegistry>) -> Self {
671        // Cleanup first, in case of previous failed/panic'ed test scenarios.
672        let mut registry = fp_registry.registry.write().unwrap();
673        Self::cleanup(&mut registry);
674
675        let failpoints = match env::var("FAILPOINTS") {
676            Ok(s) => s,
677            Err(VarError::NotPresent) => {
678                return Self {
679                    fp_registry: fp_registry.clone(),
680                }
681            }
682            Err(e) => panic!("invalid failpoints: {:?}", e),
683        };
684        for mut cfg in failpoints.trim().split(';') {
685            cfg = cfg.trim();
686            if cfg.is_empty() {
687                continue;
688            }
689            let (name, order) = partition(cfg, '=');
690            match order {
691                None => panic!("invalid failpoint: {:?}", cfg),
692                Some(order) => {
693                    if let Err(e) = set(&mut registry, name.to_owned(), order) {
694                        panic!("unable to configure failpoint \"{}\": {}", name, e);
695                    }
696                }
697            }
698        }
699        Self {
700            fp_registry: fp_registry.clone(),
701        }
702    }
703
704    /// Tear down the fail point system.
705    ///
706    /// Clears the configuration of all fail points. Any paused fail
707    /// points will be notified before they are deactivated.
708    ///
709    /// This function should generally be called after running a test with fail points.
710    /// Calling `teardown` without previously calling `setup` results in a no-op.
711    pub fn teardown(self) {
712        drop(self)
713    }
714
715    /// Clean all registered fail points.
716    fn cleanup(registry: &mut std::sync::RwLockWriteGuard<Registry>) {
717        for p in registry.values() {
718            // wake up all pause failpoint.
719            p.set_actions("", vec![]);
720        }
721        registry.clear();
722    }
723}
724
725impl Drop for FailScenario {
726    fn drop(&mut self) {
727        let mut registry = self.fp_registry.registry.write().unwrap();
728        Self::cleanup(&mut registry)
729    }
730}
731
732/// Returns whether code generation for failpoints is enabled.
733///
734/// This function allows consumers to check (at runtime) whether the library
735/// was compiled with the (buildtime) `failpoints` feature, which enables
736/// code generation for failpoints.
737pub const fn has_failpoints() -> bool {
738    cfg!(feature = "failpoints")
739}
740
741/// Get all registered fail points.
742///
743/// Return a vector of `(name, actions)` pairs.
744pub fn list(fp_registry: Arc<FailPointRegistry>) -> Vec<(String, String)> {
745    let registry = fp_registry.registry.read().unwrap();
746    registry
747        .iter()
748        .map(|(name, fp)| (name.to_string(), fp.actions_str()))
749        .collect()
750}
751
752fn find_fail_point(fp_registry: Arc<FailPointRegistry>, name: &str) -> Option<Arc<FailPoint>> {
753    let registry = fp_registry.registry.read().unwrap();
754    registry.get(name).map(|p| p.clone())
755}
756
757#[doc(hidden)]
758pub fn eval<R, F: FnOnce(Option<String>) -> R>(
759    fp_registry: Arc<FailPointRegistry>,
760    name: &str,
761    f: F,
762) -> Option<R> {
763    if let Some(p) = find_fail_point(fp_registry, name) {
764        p.eval(name).map(f)
765    } else {
766        None
767    }
768}
769
770#[doc(hidden)]
771pub async fn eval_async<R, F: FnOnce(Option<String>) -> R>(
772    fp_registry: Arc<FailPointRegistry>,
773    name: &str,
774    f: F,
775) -> Option<R> {
776    if let Some(p) = find_fail_point(fp_registry, name) {
777        p.eval_async(name).await.map(f)
778    } else {
779        None
780    }
781}
782
783/// Configure the actions for a fail point at runtime.
784///
785/// Each fail point can be configured with a series of actions, specified by the
786/// `actions` argument. The format of `actions` is `action[->action...]`. When
787/// multiple actions are specified, an action will be checked only when its
788/// former action is not triggered.
789///
790/// The format of a single action is `[p%][cnt*]task[(arg)]`. `p%` is the
791/// expected probability that the action is triggered, and `cnt*` is the max
792/// times the action can be triggered. The supported values of `task` are:
793///
794/// - `off`, the fail point will do nothing.
795/// - `return(arg)`, return early when the fail point is triggered. `arg` is passed to `$e` (
796/// defined via the `fail_point!` macro) as a string.
797/// - `sleep(milliseconds)`, sleep for the specified time.
798/// - `panic(msg)`, panic with the message.
799/// - `print(msg)`, log the message, using the `log` crate, at the `info` level.
800/// - `pause`, sleep until other action is set to the fail point.
801/// - `yield`, yield the CPU.
802/// - `delay(milliseconds)`, busy waiting for the specified time.
803///
804/// For example, `20%3*print(still alive!)->panic` means the fail point has 20% chance to print a
805/// message "still alive!" and 80% chance to panic. And the message will be printed at most 3
806/// times.
807///
808/// The `FAILPOINTS` environment variable accepts this same syntax for its fail
809/// point actions.
810///
811/// A call to `cfg` with a particular fail point name overwrites any existing actions for
812/// that fail point, including those set via the `FAILPOINTS` environment variable.
813pub fn cfg<S: Into<String>>(
814    registry: Arc<FailPointRegistry>,
815    name: S,
816    actions: &str,
817) -> Result<(), String> {
818    let mut registry = registry.registry.write().unwrap();
819    set(&mut registry, name.into(), actions)
820}
821
822/// Configure the actions for a fail point at runtime.
823///
824/// Each fail point can be configured by a callback. Process will call this callback function
825/// when it meet this fail-point.
826pub fn cfg_callback<S, F>(registry: Arc<FailPointRegistry>, name: S, f: F) -> Result<(), String>
827where
828    S: Into<String>,
829    F: Fn() + Send + Sync + 'static,
830{
831    let mut registry = registry.registry.write().unwrap();
832    let p = registry
833        .entry(name.into())
834        .or_insert_with(|| Arc::new(FailPoint::new()));
835    let action = Action::from_callback(f);
836    let actions = vec![action];
837    p.set_actions("callback", actions);
838    Ok(())
839}
840
841/// Remove a fail point.
842///
843/// If the fail point doesn't exist, nothing will happen.
844pub fn remove<S: AsRef<str>>(fp_registry: Arc<FailPointRegistry>, name: S) {
845    let mut registry = fp_registry.registry.write().unwrap();
846    if let Some(p) = registry.remove(name.as_ref()) {
847        // wake up all pause failpoint.
848        p.set_actions("", vec![]);
849    }
850}
851
852/// Configure fail point in RAII style.
853#[derive(Debug)]
854pub struct FailGuard {
855    name: String,
856    registry: Arc<FailPointRegistry>,
857}
858
859impl Drop for FailGuard {
860    fn drop(&mut self) {
861        remove(self.registry.clone(), &self.name);
862    }
863}
864
865impl FailGuard {
866    /// Configure the actions for a fail point during the lifetime of the returning `FailGuard`.
867    ///
868    /// Read documentation of [`cfg`] for more details.
869    pub fn new<S: Into<String>>(
870        registry: Arc<FailPointRegistry>,
871        name: S,
872        actions: &str,
873    ) -> Result<FailGuard, String> {
874        let name = name.into();
875        cfg(registry.clone(), &name, actions)?;
876        Ok(FailGuard {
877            registry: registry.clone(),
878            name,
879        })
880    }
881
882    /// Configure the actions for a fail point during the lifetime of the returning `FailGuard`.
883    ///
884    /// Read documentation of [`cfg_callback`] for more details.
885    pub fn with_callback<S, F>(
886        registry: Arc<FailPointRegistry>,
887        name: S,
888        f: F,
889    ) -> Result<FailGuard, String>
890    where
891        S: Into<String>,
892        F: Fn() + Send + Sync + 'static,
893    {
894        let name = name.into();
895        cfg_callback(registry.clone(), &name, f)?;
896        Ok(FailGuard {
897            registry: registry.clone(),
898            name,
899        })
900    }
901}
902
903fn set(
904    registry: &mut HashMap<String, Arc<FailPoint>>,
905    name: String,
906    actions: &str,
907) -> Result<(), String> {
908    let actions_str = actions;
909    // `actions` are in the format of `failpoint[->failpoint...]`.
910    let actions = actions
911        .split("->")
912        .map(Action::from_str)
913        .collect::<Result<_, _>>()?;
914    // Please note that we can't figure out whether there is a failpoint named `name`,
915    // so we may insert a failpoint that doesn't exist at all.
916    let p = registry
917        .entry(name)
918        .or_insert_with(|| Arc::new(FailPoint::new()));
919    p.set_actions(actions_str, actions);
920    Ok(())
921}
922
923/// Define a fail point (requires `failpoints` feature).
924///
925/// The `fail_point!` macro has three forms, and they all take a name as the
926/// first argument. The simplest form takes only a name and is suitable for
927/// executing most fail point behavior, including panicking, but not for early
928/// return or conditional execution based on a local flag.
929///
930/// The three forms of fail points look as follows.
931///
932/// 1. A basic fail point:
933///
934/// ```rust, ignore
935/// # #[macro_use] extern crate fail;
936/// fn function_return_unit() {
937///     fail_point!("fail-point-1");
938/// }
939/// ```
940///
941/// This form of fail point can be configured to panic, print, sleep, pause, etc., but
942/// not to return from the function early.
943///
944/// 2. A fail point that may return early:
945///
946/// ```rust, ignore
947/// # #[macro_use] extern crate fail;
948/// fn function_return_value() -> u64 {
949///     fail_point!("fail-point-2", |r| r.map_or(2, |e| e.parse().unwrap()));
950///     0
951/// }
952/// ```
953///
954/// This form of fail point can additionally be configured to return early from
955/// the enclosing function. It accepts a closure, which itself accepts an
956/// `Option<String>`, and is expected to transform that argument into the early
957/// return value. The argument string is sourced from the fail point
958/// configuration string. For example configuring this "fail-point-2" as
959/// "return(100)" will execute the fail point closure, passing it a `Some` value
960/// containing a `String` equal to "100"; the closure then parses it into the
961/// return value.
962///
963/// 3. A fail point with conditional execution:
964///
965/// ```rust, ignore
966/// # #[macro_use] extern crate fail;
967/// fn function_conditional(enable: bool) {
968///     fail_point!("fail-point-3", enable, |_| {});
969/// }
970/// ```
971///
972/// In this final form, the second argument is a local boolean expression that
973/// must evaluate to `true` before the fail point is evaluated. The third
974/// argument is again an early-return closure.
975///
976/// The three macro arguments (or "designators") are called `$name`, `$cond`,
977/// and `$e`. `$name` must be `&str`, `$cond` must be a boolean expression,
978/// and`$e` must be a function or closure that accepts an `Option<String>` and
979/// returns the same type as the enclosing function.
980///
981/// For more examples see the [crate documentation](index.html). For more
982/// information about controlling fail points see the [`cfg`](fn.cfg.html)
983/// function.
984#[macro_export]
985#[cfg(feature = "failpoints")]
986macro_rules! fail_point {
987    ($registry:expr, $name:expr) => {{
988        $crate::eval($registry, $name, |_| {
989            panic!("Return is not supported for the fail point \"{}\"", $name);
990        });
991    }};
992    ($registry:expr, $name:expr, $e:expr) => {{
993        if let Some(res) = $crate::eval($registry, $name, $e) {
994            return res;
995        }
996    }};
997    ($registry:expr, $name:expr, $cond:expr, $e:expr) => {{
998        if $cond {
999            $crate::fail_point!($registry, $name, $e);
1000        }
1001    }};
1002}
1003
1004/// Define a fail point (requires `failpoints` feature).
1005///
1006/// The `fail_point_async!` macro is similar to `fail_point` except that it
1007/// can be safely used in an async function. Similar to `fail_point`, it
1008/// has three forms, and they all take a name as the
1009/// first argument. The simplest form takes only a name and is suitable for
1010/// executing most fail point behavior, including panicking, but not for early
1011/// return or conditional execution based on a local flag.
1012///
1013/// The three forms of fail points look as follows.
1014///
1015/// 1. A basic fail point:
1016///
1017/// ```rust, ignore
1018/// # #[macro_use] extern crate fail;
1019/// async fn function_return_unit() {
1020///     fail_point_async!("fail-point-1");
1021/// }
1022/// ```
1023///
1024/// This form of fail point can be configured to panic, print, sleep, pause, etc., but
1025/// not to return from the function early.
1026///
1027/// 2. A fail point that may return early:
1028///
1029/// ```rust, ignore
1030/// # #[macro_use] extern crate fail;
1031/// async fn function_return_value() -> u64 {
1032///     fail_point_async!("fail-point-2", |r| r.map_or(2, |e| e.parse().unwrap()));
1033///     0
1034/// }
1035/// ```
1036///
1037/// This form of fail point can additionally be configured to return early from
1038/// the enclosing function. It accepts a closure, which itself accepts an
1039/// `Option<String>`, and is expected to transform that argument into the early
1040/// return value. The argument string is sourced from the fail point
1041/// configuration string. For example configuring this "fail-point-2" as
1042/// "return(100)" will execute the fail point closure, passing it a `Some` value
1043/// containing a `String` equal to "100"; the closure then parses it into the
1044/// return value.
1045///
1046/// 3. A fail point with conditional execution:
1047///
1048/// ```rust, ignore
1049/// # #[macro_use] extern crate fail;
1050/// async fn function_conditional(enable: bool) {
1051///     fail_point_async!("fail-point-3", enable, |_| {});
1052/// }
1053/// ```
1054///
1055/// In this final form, the second argument is a local boolean expression that
1056/// must evaluate to `true` before the fail point is evaluated. The third
1057/// argument is again an early-return closure.
1058///
1059/// The three macro arguments (or "designators") are called `$name`, `$cond`,
1060/// and `$e`. `$name` must be `&str`, `$cond` must be a boolean expression,
1061/// and`$e` must be a function or closure that accepts an `Option<String>` and
1062/// returns the same type as the enclosing function.
1063///
1064/// For more examples see the [crate documentation](index.html). For more
1065/// information about controlling fail points see the [`cfg`](fn.cfg.html)
1066/// function.
1067#[macro_export]
1068#[cfg(feature = "failpoints")]
1069macro_rules! fail_point_async {
1070    ($registry:expr, $name:expr) => {{
1071        $crate::eval_async($registry, $name, |_| {
1072            panic!("Return is not supported for the fail point \"{}\"", $name);
1073        })
1074        .await;
1075    }};
1076    ($registry:expr, $name:expr, $e:expr) => {{
1077        if let Some(res) = $crate::eval_async($registry, $name, $e).await {
1078            return res;
1079        }
1080    }};
1081    ($registry:expr, $name:expr, $cond:expr, $e:expr) => {{
1082        if $cond {
1083            $crate::fail_point_async!($registry, $name, $e);
1084        }
1085    }};
1086}
1087
1088/// Define a fail point (disabled, see `failpoints` feature).
1089#[macro_export]
1090#[cfg(not(feature = "failpoints"))]
1091macro_rules! fail_point {
1092    ($registry:expr, $name:expr, $e:expr) => {{}};
1093    ($registry:expr, $name:expr) => {{}};
1094    ($registry:expr, $name:expr, $cond:expr, $e:expr) => {{}};
1095}
1096
1097/// Emit an event and evaluate a failpoint (requires the `failpoints` feature).
1098///
1099/// The first argument must be a [`FailPointTx`] and the second is the failpoint
1100/// name. An optional third argument is an early-return closure accepted by
1101/// [`fail_point!`].
1102#[macro_export]
1103#[cfg(feature = "failpoints")]
1104macro_rules! fail_point_send {
1105    ($fp_tx:expr, $name:expr) => {{
1106        let fp_tx = &$fp_tx;
1107        let name = $name.to_string();
1108        fp_tx.send(name.clone());
1109        $crate::fail_point!(fp_tx.registry(), name.as_str());
1110    }};
1111    ($fp_tx:expr, $name:expr, $e:expr) => {{
1112        let fp_tx = &$fp_tx;
1113        let name = $name.to_string();
1114        fp_tx.send(name.clone());
1115        $crate::fail_point!(fp_tx.registry(), name.as_str(), $e);
1116    }};
1117}
1118
1119/// Emit an event and evaluate a failpoint (disabled without the `failpoints` feature).
1120#[macro_export]
1121#[cfg(not(feature = "failpoints"))]
1122macro_rules! fail_point_send {
1123    ($fp_tx:expr, $name:expr) => {{}};
1124    ($fp_tx:expr, $name:expr, $e:expr) => {{}};
1125}
1126
1127#[cfg(test)]
1128mod tests {
1129    use super::*;
1130
1131    use std::sync::*;
1132
1133    #[test]
1134    fn test_has_failpoints() {
1135        assert_eq!(cfg!(feature = "failpoints"), has_failpoints());
1136    }
1137
1138    #[test]
1139    fn test_off() {
1140        let point = FailPoint::new();
1141        point.set_actions("", vec![Action::new(Task::Off, 1.0, None)]);
1142        assert!(point.eval("test_fail_point_off").is_none());
1143    }
1144
1145    #[test]
1146    fn test_return() {
1147        let point = FailPoint::new();
1148        point.set_actions("", vec![Action::new(Task::Return(None), 1.0, None)]);
1149        let res = point.eval("test_fail_point_return");
1150        assert_eq!(res, Some(None));
1151
1152        let ret = Some("test".to_owned());
1153        point.set_actions("", vec![Action::new(Task::Return(ret.clone()), 1.0, None)]);
1154        let res = point.eval("test_fail_point_return");
1155        assert_eq!(res, Some(ret));
1156    }
1157
1158    #[test]
1159    fn test_sleep() {
1160        let point = FailPoint::new();
1161        let timer = Instant::now();
1162        point.set_actions("", vec![Action::new(Task::Sleep(1000), 1.0, None)]);
1163        assert!(point.eval("test_fail_point_sleep").is_none());
1164        assert!(timer.elapsed() > Duration::from_millis(1000));
1165    }
1166
1167    #[should_panic]
1168    #[test]
1169    fn test_panic() {
1170        let point = FailPoint::new();
1171        point.set_actions("", vec![Action::new(Task::Panic(None), 1.0, None)]);
1172        point.eval("test_fail_point_panic");
1173    }
1174
1175    #[test]
1176    fn test_print() {
1177        struct LogCollector(Arc<Mutex<Vec<String>>>);
1178        impl log::Log for LogCollector {
1179            fn enabled(&self, _: &log::Metadata) -> bool {
1180                true
1181            }
1182            fn log(&self, record: &log::Record) {
1183                let mut buf = self.0.lock().unwrap();
1184                buf.push(format!("{}", record.args()));
1185            }
1186            fn flush(&self) {}
1187        }
1188
1189        let buffer = Arc::new(Mutex::new(vec![]));
1190        let collector = LogCollector(buffer.clone());
1191        log::set_max_level(log::LevelFilter::Info);
1192        log::set_boxed_logger(Box::new(collector)).unwrap();
1193
1194        let point = FailPoint::new();
1195        point.set_actions("", vec![Action::new(Task::Print(None), 1.0, None)]);
1196        assert!(point.eval("test_fail_point_print").is_none());
1197        let msg = buffer.lock().unwrap().pop().unwrap();
1198        assert_eq!(msg, "failpoint test_fail_point_print executed.");
1199    }
1200
1201    #[test]
1202    fn test_pause() {
1203        let point = Arc::new(FailPoint::new());
1204        point.set_actions("", vec![Action::new(Task::Pause, 1.0, None)]);
1205        let p = point.clone();
1206        let (tx, rx) = mpsc::channel();
1207        thread::spawn(move || {
1208            assert_eq!(p.eval("test_fail_point_pause"), None);
1209            tx.send(()).unwrap();
1210        });
1211        assert!(rx.recv_timeout(Duration::from_secs(1)).is_err());
1212        point.set_actions("", vec![Action::new(Task::Off, 1.0, None)]);
1213        rx.recv_timeout(Duration::from_secs(1)).unwrap();
1214    }
1215
1216    #[tokio::test]
1217    async fn test_async_pause() {
1218        let point = Arc::new(FailPoint::new());
1219        point.set_actions("", vec![Action::new(Task::Pause, 1.0, None)]);
1220        let p = point.clone();
1221        let (tx, mut rx) = tokio::sync::mpsc::channel(2);
1222        let handle = tokio::spawn(async move {
1223            assert_eq!(p.eval_async("test_fail_point_pause").await, None);
1224            tx.send(()).await.unwrap()
1225        });
1226        assert!(rx.try_recv().is_err());
1227        point.set_actions("", vec![Action::new(Task::Off, 1.0, None)]);
1228        rx.recv().await.unwrap();
1229    }
1230
1231    #[tokio::test(flavor = "current_thread", start_paused = true)]
1232    async fn test_async_sleep() {
1233        let value = Arc::new(AtomicU64::new(0));
1234
1235        fn spawn_sleep_task(
1236            sleep_duration_millis: u64,
1237            value: Arc<AtomicU64>,
1238            value_to_set: u64,
1239        ) -> tokio::task::JoinHandle<()> {
1240            let point = Arc::new(FailPoint::new());
1241            point.set_actions(
1242                "",
1243                vec![Action::new(Task::Sleep(sleep_duration_millis), 1.0, None)],
1244            );
1245            let p = point.clone();
1246            tokio::spawn(async move {
1247                assert_eq!(p.eval_async("test_fail_point_sleep").await, None);
1248                value.store(value_to_set, Relaxed);
1249            })
1250        }
1251
1252        let h1 = spawn_sleep_task(10, value.clone(), 10);
1253        let h2 = spawn_sleep_task(5, value.clone(), 5);
1254
1255        tokio::join!(h2);
1256        assert_eq!(value.load(Relaxed), 5);
1257        tokio::join!(h1);
1258        assert_eq!(value.load(Relaxed), 10);
1259    }
1260
1261    #[test]
1262    fn test_yield() {
1263        let point = FailPoint::new();
1264        point.set_actions("", vec![Action::new(Task::Yield, 1.0, None)]);
1265        assert!(point.eval("test_fail_point_yield").is_none());
1266    }
1267
1268    #[test]
1269    fn test_delay() {
1270        let point = FailPoint::new();
1271        let timer = Instant::now();
1272        point.set_actions("", vec![Action::new(Task::Delay(1000), 1.0, None)]);
1273        assert!(point.eval("test_fail_point_delay").is_none());
1274        assert!(timer.elapsed() > Duration::from_millis(1000));
1275    }
1276
1277    #[test]
1278    fn test_frequency_and_count() {
1279        let point = FailPoint::new();
1280        point.set_actions("", vec![Action::new(Task::Return(None), 0.8, Some(100))]);
1281        let mut count = 0;
1282        let mut times = 0f64;
1283        while count < 100 {
1284            if point.eval("test_fail_point_frequency").is_some() {
1285                count += 1;
1286            }
1287            times += 1f64;
1288        }
1289        assert!(100.0 / 0.9 < times && times < 100.0 / 0.7, "{}", times);
1290        for _ in 0..times as u64 {
1291            assert!(point.eval("test_fail_point_frequency").is_none());
1292        }
1293    }
1294
1295    #[test]
1296    fn test_parse() {
1297        let cases = vec![
1298            ("return", Action::new(Task::Return(None), 1.0, None)),
1299            (
1300                "return(64)",
1301                Action::new(Task::Return(Some("64".to_owned())), 1.0, None),
1302            ),
1303            ("5*return", Action::new(Task::Return(None), 1.0, Some(5))),
1304            ("25%return", Action::new(Task::Return(None), 0.25, None)),
1305            (
1306                "125%2*return",
1307                Action::new(Task::Return(None), 1.25, Some(2)),
1308            ),
1309            (
1310                "return(2%5)",
1311                Action::new(Task::Return(Some("2%5".to_owned())), 1.0, None),
1312            ),
1313            ("125%2*off", Action::new(Task::Off, 1.25, Some(2))),
1314            (
1315                "125%2*sleep(100)",
1316                Action::new(Task::Sleep(100), 1.25, Some(2)),
1317            ),
1318            (" 125%2*off ", Action::new(Task::Off, 1.25, Some(2))),
1319            ("125%2*panic", Action::new(Task::Panic(None), 1.25, Some(2))),
1320            (
1321                "125%2*panic(msg)",
1322                Action::new(Task::Panic(Some("msg".to_owned())), 1.25, Some(2)),
1323            ),
1324            ("125%2*print", Action::new(Task::Print(None), 1.25, Some(2))),
1325            (
1326                "125%2*print(msg)",
1327                Action::new(Task::Print(Some("msg".to_owned())), 1.25, Some(2)),
1328            ),
1329            ("125%2*pause", Action::new(Task::Pause, 1.25, Some(2))),
1330            ("125%2*yield", Action::new(Task::Yield, 1.25, Some(2))),
1331            ("125%2*delay(2)", Action::new(Task::Delay(2), 1.25, Some(2))),
1332        ];
1333        for (expr, exp) in cases {
1334            let res: Action = expr.parse().unwrap();
1335            assert_eq!(res, exp);
1336        }
1337
1338        let fail_cases = vec![
1339            "delay",
1340            "sleep",
1341            "Return",
1342            "ab%return",
1343            "ab*return",
1344            "return(msg",
1345            "unknown",
1346        ];
1347        for case in fail_cases {
1348            assert!(case.parse::<Action>().is_err());
1349        }
1350    }
1351
1352    // This case should be tested as integration case, but when calling `teardown` other cases
1353    // like `test_pause` maybe also affected, so it's better keep it here.
1354    #[test]
1355    #[cfg_attr(not(feature = "failpoints"), ignore)]
1356    fn test_setup_and_teardown() {
1357        let fp_registry = Arc::new(FailPointRegistry::default());
1358        let f1 = || {
1359            fail_point!(fp_registry.clone(), "setup_and_teardown1", |_| 1);
1360            0
1361        };
1362        let fp_registry_clone = fp_registry.clone();
1363        let f2 = || {
1364            fail_point!(fp_registry_clone, "setup_and_teardown2", |_| 2);
1365            0
1366        };
1367        /*env::set_var(
1368            "FAILPOINTS",
1369            "setup_and_teardown1=return;setup_and_teardown2=pause;",
1370        );*/
1371        cfg(fp_registry.clone(), "setup_and_teardown1", "return");
1372        cfg(fp_registry.clone(), "setup_and_teardown2", "pause");
1373        assert_eq!(f1(), 1);
1374
1375        let (tx, rx) = mpsc::channel();
1376        thread::spawn(move || {
1377            tx.send(f2()).unwrap();
1378        });
1379        assert!(rx.recv_timeout(Duration::from_millis(500)).is_err());
1380
1381        cfg(fp_registry.clone(), "setup_and_teardown1", "off");
1382        cfg(fp_registry.clone(), "setup_and_teardown2", "off");
1383
1384        assert_eq!(rx.recv_timeout(Duration::from_millis(500)).unwrap(), 0);
1385        assert_eq!(f1(), 0);
1386    }
1387}