cancel_safe_futures/sync/
poison.rs1use core::sync::atomic::{AtomicU8, Ordering};
6use std::{error, fmt, thread};
7
8pub struct PoisonError<T> {
16 guard: T,
17 flags: u8,
18}
19
20impl<T> PoisonError<T> {
21 fn new(guard: T, flags: u8) -> PoisonError<T> {
22 PoisonError { guard, flags }
23 }
24
25 #[inline]
28 pub fn is_panic(&self) -> bool {
29 self.flags & PANIC_POISON != 0
30 }
31
32 #[inline]
34 pub fn is_cancel(&self) -> bool {
35 self.flags & CANCEL_POISON != 0
36 }
37
38 #[inline]
41 pub fn into_inner(self) -> T {
42 self.guard
43 }
44
45 #[inline]
48 pub fn get_ref(&self) -> &T {
49 &self.guard
50 }
51
52 #[inline]
55 pub fn get_mut(&mut self) -> &mut T {
56 &mut self.guard
57 }
58}
59
60impl<T> fmt::Debug for PoisonError<T> {
61 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62 f.debug_struct("PoisonError")
63 .field("flags", &DebugFlags(self.flags))
64 .finish_non_exhaustive()
65 }
66}
67
68impl<T> fmt::Display for PoisonError<T> {
69 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70 write!(f, "poisoned lock {:?}", DebugFlags(self.flags))
71 }
72}
73
74impl<T> error::Error for PoisonError<T> {}
75
76pub enum TryLockError<T> {
83 Poisoned(PoisonError<T>),
86
87 WouldBlock,
90}
91
92impl<T> From<PoisonError<T>> for TryLockError<T> {
93 fn from(error: PoisonError<T>) -> TryLockError<T> {
94 TryLockError::Poisoned(error)
95 }
96}
97
98impl<T> fmt::Debug for TryLockError<T> {
99 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100 match self {
101 TryLockError::Poisoned(error) => f.debug_tuple("Poisoned").field(&error).finish(),
102 TryLockError::WouldBlock => f.debug_tuple("WouldBlock").finish(),
103 }
104 }
105}
106
107impl<T> fmt::Display for TryLockError<T> {
108 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
109 match self {
110 TryLockError::Poisoned(error) => {
111 write!(f, "poisoned lock {:?}", DebugFlags(error.flags))
112 }
113 TryLockError::WouldBlock => {
114 f.write_str("try_lock failed because the operation would block")
115 }
116 }
117 }
118}
119
120impl<T> error::Error for TryLockError<T> {
121 fn cause(&self) -> Option<&dyn error::Error> {
122 match self {
123 TryLockError::Poisoned(error) => Some(error),
124 TryLockError::WouldBlock => None,
125 }
126 }
127}
128
129pub type LockResult<Guard> = Result<Guard, PoisonError<Guard>>;
139
140pub type TryLockResult<Guard> = Result<Guard, TryLockError<Guard>>;
146
147pub const NO_POISON: u8 = 0;
148pub const PANIC_POISON: u8 = 1 << 0;
149pub const CANCEL_POISON: u8 = 1 << 1;
150
151pub struct Flag {
152 failed: AtomicU8,
153}
154
155impl fmt::Debug for Flag {
156 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
157 let flags = self.failed.load(Ordering::Relaxed);
158 DebugFlags(flags).fmt(f)
159 }
160}
161
162impl Flag {
172 #[inline]
173 pub const fn new() -> Flag {
174 Flag {
175 failed: AtomicU8::new(NO_POISON),
176 }
177 }
178
179 #[inline]
181 pub fn borrow(&self) -> LockResult<()> {
182 let flags = self.get_flags();
183 if flags > NO_POISON {
184 Err(PoisonError::new((), flags))
185 } else {
186 Ok(())
187 }
188 }
189
190 #[inline]
191 pub fn guard_assuming_no_poison(&self) -> Guard {
192 Guard {
193 panicking: thread::panicking(),
194 }
195 }
196
197 #[inline]
198 pub fn done(&self, guard: &Guard, cancel_poison: bool) {
199 let mut new_flags = 0;
200 if !guard.panicking && thread::panicking() {
201 new_flags |= PANIC_POISON;
202 }
203 if cancel_poison {
204 new_flags |= CANCEL_POISON;
205 }
206 self.failed.fetch_or(new_flags, Ordering::Relaxed);
207 }
208
209 #[inline]
210 pub fn get_flags(&self) -> u8 {
211 self.failed.load(Ordering::Relaxed)
212 }
213}
214
215pub struct Guard {
216 panicking: bool,
217}
218
219pub fn map_result<T, U, F>(result: LockResult<T>, f: F) -> LockResult<U>
220where
221 F: FnOnce(T) -> U,
222{
223 match result {
224 Ok(t) => Ok(f(t)),
225 Err(error) => {
226 let flags = error.flags;
227 Err(PoisonError::new(f(error.into_inner()), flags))
228 }
229 }
230}
231
232struct DebugFlags(u8);
233
234impl fmt::Debug for DebugFlags {
235 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
236 if self.0 == NO_POISON {
237 write!(f, "(not poisoned)")
238 } else {
239 let mut poisoned_by = Vec::new();
240 if self.0 & PANIC_POISON != 0 {
241 poisoned_by.push("panic");
242 }
243 if self.0 & CANCEL_POISON != 0 {
244 poisoned_by.push("async cancellation");
245 }
246
247 write!(f, "(poisoned by {})", poisoned_by.join(", "))
248 }
249 }
250}