dispose/
abort.rs

1#![allow(clippy::module_name_repetitions)]
2
3use std::process::abort;
4
5use crate::{Disposable, Dispose};
6
7/// Abort the process if this value is dropped.
8///
9/// This struct is for bypassing an unwinding panic should something go horribly
10/// wrong in an application.  It is the heart of [`abort_on_panic`], and for
11/// most cases does not need to be used directly.
12///
13/// [`abort_on_panic`]: ./function.abort_on_panic.html
14#[derive(Debug)]
15#[allow(missing_copy_implementations)] // Construction is trivial, copying would be dangerous
16pub struct AbortCanary(());
17
18impl AbortCanary {
19    /// Construct a new canary.
20    ///
21    /// # Panics
22    /// The value produced by this function must be passed to [`release`] in
23    /// order to avoid aborting the process.
24    ///
25    /// [`release`]: ./struct.AbortCanary.html#method.release
26    #[must_use = "Dropping this value immediately will abort the process."]
27    pub fn new() -> Disposable<Self> { Disposable::new(Self(())) }
28
29    /// Release `canary`.  This will consume and drop it without aborting the
30    /// process.
31    pub fn release(canary: Disposable<Self>) { unsafe { Disposable::leak(canary) }; }
32}
33
34impl Dispose for AbortCanary {
35    fn dispose(self) { abort(); }
36}
37
38/// Abort the process if the provided closure panics.
39///
40/// This function internally constructs an [`AbortCanary`] and releases it after
41/// running `f`.
42///
43/// # Example
44/// The following panic will result in the process aborting:
45///
46/// ```should_panic
47/// # use dispose::abort_on_panic;
48/// abort_on_panic(|| panic!("oops!"));
49/// ```
50///
51/// [`AbortCanary`]: ./struct.AbortCanary.html
52pub fn abort_on_panic<T>(f: impl FnOnce() -> T) -> T {
53    let canary = AbortCanary::new();
54
55    let ret = f();
56
57    AbortCanary::release(canary);
58
59    ret
60}
61
62#[cfg(test)]
63mod test {
64    use std::panic::catch_unwind;
65
66    use super::*;
67
68    #[test]
69    fn safe_canary() {
70        let canary = AbortCanary::new();
71        AbortCanary::release(canary);
72    }
73
74    // TODO: how to test abort???
75    //       I have confirmed that uncommenting the commented tests does result
76    //       in the test process aborting, but currently the test runner can't
77    //       deal with that properly.
78
79    // #[test]
80    // #[should_panic]
81    // fn bad_canary() { let _canary = AbortCanary::new(); }
82
83    #[test]
84    fn safe_aop() { abort_on_panic(|| ()); }
85
86    // This should NOT panic
87    #[test]
88    fn sanity_check_aop() { catch_unwind(|| panic!()).ok(); }
89
90    // // This should panic
91    // #[test]
92    // #[should_panic]
93    // fn bad_aop() { catch_unwind(|| abort_on_panic(|| panic!())).ok(); }
94}