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    #[allow(unreachable_code)]
33    pub fn borrow(&self) -> LockResult<()> {
34        if self.get() { Err(PoisonError::new(())) } else { Ok(()) }
35    }
36
37    #[allow(unreachable_code)]
38    pub fn guard(&self) -> LockResult<Guard> {
39        let ret = Guard {
40            #[cfg(panic="unwind")]
41            panicking: panicking(),
42        };
43        if self.get() { Err(PoisonError::new(ret)) } else { Ok(ret) }
44    }
45
46    #[cfg(panic="unwind")]
47    pub fn done(&self, guard: &Guard) {
48        if !guard.panicking && panicking() {
49            self.failed.store(true, Ordering::Relaxed);
50        }
51    }
52
53    #[cfg(not(panic="unwind"))]
54    pub fn done(&self, _guard: &Guard) {}
55
56    #[cfg(panic="unwind")]
57    pub fn get(&self) -> bool {
58        self.failed.load(Ordering::Relaxed)
59    }
60
61    #[cfg(not(panic="unwind"))]
62    pub fn get(&self) -> bool {
63        false
64    }
65
66    pub fn clear(&self) {
67        #[cfg(panic="unwind")]
68        self.failed.store(false, Ordering::Relaxed)
69    }
70}
71
72#[derive(Clone)]
73pub struct Guard {
74    #[cfg(panic="unwind")]
75    panicking: bool,
76}
77
78pub struct PoisonError<T> {
79    guard: T,
80    #[cfg(not(panic="unwind"))]
81    _never: !,
82}
83
84pub enum TryLockError<T> {
85    Poisoned(PoisonError<T>),
86    WouldBlock,
87}
88
89pub type LockResult<Guard> = Result<Guard, PoisonError<Guard>>;
90
91pub type TryLockResult<Guard> = Result<Guard, TryLockError<Guard>>;
92
93impl<T> fmt::Debug for PoisonError<T> {
94    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95        f.debug_struct("PoisonError").finish_non_exhaustive()
96    }
97}
98
99impl<T> fmt::Display for PoisonError<T> {
100    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101        "poisoned lock: another task failed inside".fmt(f)
102    }
103}
104
105impl<T> Error for PoisonError<T> { }
106
107impl<T> PoisonError<T> {
108    #[cfg(panic="unwind")]
109    pub fn new(guard: T) -> PoisonError<T> {
110        PoisonError { guard }
111    }
112
113    #[cfg(not(panic="unwind"))]
114    pub fn new(_guard: T) -> PoisonError<T> {
115        panic!("PoisonError created in a libstd built with panic=\"abort\"")
116    }
117
118    pub fn into_inner(self) -> T {
119        self.guard
120    }
121
122    pub fn get_ref(&self) -> &T {
123        &self.guard
124    }
125
126    pub fn get_mut(&mut self) -> &mut T {
127        &mut self.guard
128    }
129}
130
131impl<T> From<PoisonError<T>> for TryLockError<T> {
132    fn from(err: PoisonError<T>) -> TryLockError<T> {
133        TryLockError::Poisoned(err)
134    }
135}
136
137impl<T> fmt::Debug for TryLockError<T> {
138    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
139        match *self {
140            #[cfg(panic="unwind")]
141            TryLockError::Poisoned(..) => "Poisoned(..)".fmt(f),
142            #[cfg(not(panic="unwind"))]
143            TryLockError::Poisoned(ref p) => match p._never {},
144            TryLockError::WouldBlock => "WouldBlock".fmt(f),
145        }
146    }
147}
148
149impl<T> fmt::Display for TryLockError<T> {
150    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
151        match *self {
152            #[cfg(panic="unwind")]
153            TryLockError::Poisoned(..) => "poisoned lock: another task failed inside",
154            #[cfg(not(panic="unwind"))]
155            TryLockError::Poisoned(ref p) => match p._never {},
156            TryLockError::WouldBlock => "try_lock failed because the operation would block",
157        }
158        .fmt(f)
159    }
160}
161
162impl<T> Error for TryLockError<T> { }
163
164pub fn map_result<T, U, F>(result: LockResult<T>, f: F) -> LockResult<U>
165where
166    F: FnOnce(T) -> U,
167{
168    match result {
169        Ok(t) => Ok(f(t)),
170        #[cfg(panic="unwind")]
171        Err(PoisonError { guard }) => Err(PoisonError::new(f(guard))),
172    }
173}