TLA 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 CBC 16 : pub(crate) async fn failpoint_sleep_helper(name: &'static str, duration_str: String) {
29 16 : let millis = duration_str.parse::<u64>().unwrap();
30 16 : let d = std::time::Duration::from_millis(millis);
31 :
32 16 : tracing::info!("failpoint {:?}: sleeping for {:?}", name, d);
33 16 : tokio::time::sleep(d).await;
34 16 : tracing::info!("failpoint {:?}: sleep done", name);
35 16 : }
36 :
37 560 : pub fn init() -> fail::FailScenario<'static> {
38 560 : // The failpoints lib provides support for parsing the `FAILPOINTS` env var.
39 560 : // We want non-default behavior for `exit`, though, so, we handle it separately.
40 560 : //
41 560 : // Format for FAILPOINTS is "name=actions" separated by ";".
42 560 : let actions = std::env::var("FAILPOINTS");
43 560 : if actions.is_ok() {
44 11 : std::env::remove_var("FAILPOINTS");
45 549 : } else {
46 549 : // let the library handle non-utf8, or nothing for not present
47 549 : }
48 :
49 560 : let scenario = fail::FailScenario::setup();
50 :
51 560 : if let Ok(val) = actions {
52 11 : val.split(';')
53 11 : .enumerate()
54 11 : .map(|(i, s)| s.split_once('=').ok_or((i, s)))
55 11 : .for_each(|res| {
56 11 : let (name, actions) = match res {
57 11 : Ok(t) => t,
58 UBC 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 CBC 11 : if let Err(e) = apply_failpoint(name, actions) {
66 UBC 0 : panic!("startup failpoints: failed to apply failpoint {name}={actions}: {e}");
67 CBC 11 : }
68 11 : });
69 549 : }
70 :
71 560 : scenario
72 560 : }
73 :
74 224 : pub(crate) fn apply_failpoint(name: &str, actions: &str) -> Result<(), String> {
75 224 : if actions == "exit" {
76 9 : fail::cfg_callback(name, exit_failpoint)
77 : } else {
78 215 : fail::cfg(name, actions)
79 : }
80 224 : }
81 :
82 : #[inline(never)]
83 8 : fn exit_failpoint() {
84 8 : tracing::info!("Exit requested by failpoint");
85 8 : std::process::exit(1);
86 : }
|