Line data Source code
1 : /// use with fail::cfg("$name", "return(2000)")
2 : ///
3 : /// The effect is similar to a "sleep(2000)" action, i.e. we sleep for the
4 : /// specified time (in milliseconds). The main difference is that we use async
5 : /// tokio sleep function. Another difference is that we print lines to the log,
6 : /// which can be useful in tests to check that the failpoint was hit.
7 : #[macro_export]
8 : macro_rules! __failpoint_sleep_millis_async {
9 : ($name:literal) => {{
10 : // If the failpoint is used with a "return" action, set should_sleep to the
11 : // returned value (as string). Otherwise it's set to None.
12 : let should_sleep = (|| {
13 : ::fail::fail_point!($name, |x| x);
14 : ::std::option::Option::None
15 : })();
16 :
17 : // Sleep if the action was a returned value
18 : if let ::std::option::Option::Some(duration_str) = should_sleep {
19 : $crate::failpoint_support::failpoint_sleep_helper($name, duration_str).await
20 : }
21 : }};
22 : }
23 : pub use __failpoint_sleep_millis_async as sleep_millis_async;
24 :
25 : // Helper function used by the macro. (A function has nicer scoping so we
26 : // don't need to decorate everything with "::")
27 : #[doc(hidden)]
28 15 : pub(crate) async fn failpoint_sleep_helper(name: &'static str, duration_str: String) {
29 15 : let millis = duration_str.parse::<u64>().unwrap();
30 15 : let d = std::time::Duration::from_millis(millis);
31 :
32 15 : tracing::info!("failpoint {:?}: sleeping for {:?}", name, d);
33 15 : tokio::time::sleep(d).await;
34 15 : tracing::info!("failpoint {:?}: sleep done", name);
35 15 : }
36 :
37 575 : pub fn init() -> fail::FailScenario<'static> {
38 575 : // The failpoints lib provides support for parsing the `FAILPOINTS` env var.
39 575 : // We want non-default behavior for `exit`, though, so, we handle it separately.
40 575 : //
41 575 : // Format for FAILPOINTS is "name=actions" separated by ";".
42 575 : let actions = std::env::var("FAILPOINTS");
43 575 : if actions.is_ok() {
44 10 : std::env::remove_var("FAILPOINTS");
45 565 : } else {
46 565 : // let the library handle non-utf8, or nothing for not present
47 565 : }
48 :
49 575 : let scenario = fail::FailScenario::setup();
50 :
51 575 : if let Ok(val) = actions {
52 10 : val.split(';')
53 10 : .enumerate()
54 10 : .map(|(i, s)| s.split_once('=').ok_or((i, s)))
55 10 : .for_each(|res| {
56 10 : let (name, actions) = match res {
57 10 : Ok(t) => t,
58 0 : Err((i, s)) => {
59 0 : panic!(
60 0 : "startup failpoints: missing action on the {}th failpoint; try `{s}=return`",
61 0 : i + 1,
62 0 : );
63 : }
64 : };
65 10 : if let Err(e) = apply_failpoint(name, actions) {
66 0 : panic!("startup failpoints: failed to apply failpoint {name}={actions}: {e}");
67 10 : }
68 10 : });
69 565 : }
70 :
71 575 : scenario
72 575 : }
73 :
74 261 : pub(crate) fn apply_failpoint(name: &str, actions: &str) -> Result<(), String> {
75 261 : if actions == "exit" {
76 7 : fail::cfg_callback(name, exit_failpoint)
77 : } else {
78 254 : fail::cfg(name, actions)
79 : }
80 261 : }
81 :
82 : #[inline(never)]
83 6 : fn exit_failpoint() {
84 6 : tracing::info!("Exit requested by failpoint");
85 6 : std::process::exit(1);
86 : }
|