sync_no_std/
poison.rs

1use core::error::Error;
2use core::fmt;
3#[cfg(panic="unwind")]
4use core::sync::atomic::{AtomicBool, Ordering};
5#[cfg(panic="unwind")]
6use panicking::panicking;
7
8pub struct Flag {
9    #[cfg(panic="unwind")]
10    failed: AtomicBool,
11}
12
13// Note that the Ordering uses to access the `failed` field of `Flag` below is
14// always `Relaxed`, and that's because this isn't actually protecting any data,
15// it's just a flag whether we've panicked or not.
16//
17// The actual location that this matters is when a mutex is **locked** which is
18// where we have external synchronization ensuring that we see memory
19// reads/writes to this flag.
20//
21// As a result, if it matters, we should see the correct value for `failed` in
22// all cases.
23
24impl Flag {
25    pub const fn new() -> Flag {
26        Flag {
27            #[cfg(panic="unwind")]
28            failed: AtomicBool::new(false),
29        }
30    }
31
32    pub fn borrow(&self) -> LockResult<()> {
33        if self.get() { Err(PoisonError::new(())) } else { Ok(()) }
34    }
35
36    pub fn guard(&self) -> LockResult<Guard> {
37        let ret = Guard {
38            #[cfg(panic="unwind")]
39            panicking: panicking(),
40        };
41        if self.get() { Err(PoisonError::new(ret)) } else { Ok(ret) }
42    }
43
44    #[cfg(panic="unwind")]
45    pub fn done(&self, guard: &Guard) {
46        if !guard.panicking && panicking() {
47            self.failed.store(true, Ordering::Relaxed);
48        }
49    }
50
51    #[cfg(not(panic="unwind"))]
52    pub fn done(&self, _guard: &Guard) {}
53
54    #[cfg(panic="unwind")]
55    pub fn get(&self) -> bool {
56        self.failed.load(Ordering::Relaxed)
57    }
58
59    #[cfg(not(panic="unwind"))]
60    pub fn get(&self) -> bool {
61        false
62    }
63
64    pub fn clear(&self) {
65        #[cfg(panic="unwind")]
66        self.failed.store(false, Ordering::Relaxed)
67    }
68}
69
70#[derive(Clone)]
71pub struct Guard {
72    #[cfg(panic="unwind")]
73    panicking: bool,
74}
75
76pub struct PoisonError<T> {
77    guard: T,
78    #[cfg(not(panic="unwind"))]
79    _never: !,
80}
81
82pub enum TryLockError<T> {
83    Poisoned(PoisonError<T>),
84    WouldBlock,
85}
86
87pub type LockResult<Guard> = Result<Guard, PoisonError<Guard>>;
88
89pub type TryLockResult<Guard> = Result<Guard, TryLockError<Guard>>;
90
91impl<T> fmt::Debug for PoisonError<T> {
92    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93        f.debug_struct("PoisonError").finish_non_exhaustive()
94    }
95}
96
97impl<T> fmt::Display for PoisonError<T> {
98    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99        "poisoned lock: another task failed inside".fmt(f)
100    }
101}
102
103impl<T> Error for PoisonError<T> { }
104
105impl<T> PoisonError<T> {
106    #[cfg(panic="unwind")]
107    pub fn new(guard: T) -> PoisonError<T> {
108        PoisonError { guard }
109    }
110
111    #[cfg(not(panic="unwind"))]
112    pub fn new(_guard: T) -> PoisonError<T> {
113        panic!("PoisonError created in a libstd built with panic=\"abort\"")
114    }
115
116    pub fn into_inner(self) -> T {
117        self.guard
118    }
119
120    pub fn get_ref(&self) -> &T {
121        &self.guard
122    }
123
124    pub fn get_mut(&mut self) -> &mut T {
125        &mut self.guard
126    }
127}
128
129impl<T> From<PoisonError<T>> for TryLockError<T> {
130    fn from(err: PoisonError<T>) -> TryLockError<T> {
131        TryLockError::Poisoned(err)
132    }
133}
134
135impl<T> fmt::Debug for TryLockError<T> {
136    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137        match *self {
138            #[cfg(panic="unwind")]
139            TryLockError::Poisoned(..) => "Poisoned(..)".fmt(f),
140            #[cfg(not(panic="unwind"))]
141            TryLockError::Poisoned(ref p) => match p._never {},
142            TryLockError::WouldBlock => "WouldBlock".fmt(f),
143        }
144    }
145}
146
147impl<T> fmt::Display for TryLockError<T> {
148    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
149        match *self {
150            #[cfg(panic="unwind")]
151            TryLockError::Poisoned(..) => "poisoned lock: another task failed inside",
152            #[cfg(not(panic="unwind"))]
153            TryLockError::Poisoned(ref p) => match p._never {},
154            TryLockError::WouldBlock => "try_lock failed because the operation would block",
155        }
156        .fmt(f)
157    }
158}
159
160impl<T> Error for TryLockError<T> { }
161
162pub fn map_result<T, U, F>(result: LockResult<T>, f: F) -> LockResult<U>
163where
164    F: FnOnce(T) -> U,
165{
166    match result {
167        Ok(t) => Ok(f(t)),
168        #[cfg(panic="unwind")]
169        Err(PoisonError { guard }) => Err(PoisonError::new(f(guard))),
170    }
171}