1 | use crate::error::Error; |
2 | use crate::fmt; |
3 | use crate::sync::atomic::{AtomicBool, Ordering}; |
4 | use crate::thread; |
5 | |
6 | pub struct Flag { |
7 | failed: AtomicBool, |
8 | } |
9 | |
10 | // Note that the Ordering uses to access the `failed` field of `Flag` below is |
11 | // always `Relaxed`, and that's because this isn't actually protecting any data, |
12 | // it's just a flag whether we've panicked or not. |
13 | // |
14 | // The actual location that this matters is when a mutex is **locked** which is |
15 | // where we have external synchronization ensuring that we see memory |
16 | // reads/writes to this flag. |
17 | // |
18 | // As a result, if it matters, we should see the correct value for `failed` in |
19 | // all cases. |
20 | |
21 | impl Flag { |
22 | #[inline ] |
23 | pub const fn new() -> Flag { |
24 | Flag { failed: AtomicBool::new(false) } |
25 | } |
26 | |
27 | /// Check the flag for an unguarded borrow, where we only care about existing poison. |
28 | #[inline ] |
29 | pub fn borrow(&self) -> LockResult<()> { |
30 | if self.get() { Err(PoisonError::new(())) } else { Ok(()) } |
31 | } |
32 | |
33 | /// Check the flag for a guarded borrow, where we may also set poison when `done`. |
34 | #[inline ] |
35 | pub fn guard(&self) -> LockResult<Guard> { |
36 | let ret = Guard { panicking: thread::panicking() }; |
37 | if self.get() { Err(PoisonError::new(ret)) } else { Ok(ret) } |
38 | } |
39 | |
40 | #[inline ] |
41 | pub fn done(&self, guard: &Guard) { |
42 | if !guard.panicking && thread::panicking() { |
43 | self.failed.store(true, Ordering::Relaxed); |
44 | } |
45 | } |
46 | |
47 | #[inline ] |
48 | pub fn get(&self) -> bool { |
49 | self.failed.load(Ordering::Relaxed) |
50 | } |
51 | |
52 | #[inline ] |
53 | pub fn clear(&self) { |
54 | self.failed.store(false, Ordering::Relaxed) |
55 | } |
56 | } |
57 | |
58 | pub struct Guard { |
59 | panicking: bool, |
60 | } |
61 | |
62 | /// A type of error which can be returned whenever a lock is acquired. |
63 | /// |
64 | /// Both [`Mutex`]es and [`RwLock`]s are poisoned whenever a thread fails while the lock |
65 | /// is held. The precise semantics for when a lock is poisoned is documented on |
66 | /// each lock, but once a lock is poisoned then all future acquisitions will |
67 | /// return this error. |
68 | /// |
69 | /// # Examples |
70 | /// |
71 | /// ``` |
72 | /// use std::sync::{Arc, Mutex}; |
73 | /// use std::thread; |
74 | /// |
75 | /// let mutex = Arc::new(Mutex::new(1)); |
76 | /// |
77 | /// // poison the mutex |
78 | /// let c_mutex = Arc::clone(&mutex); |
79 | /// let _ = thread::spawn(move || { |
80 | /// let mut data = c_mutex.lock().unwrap(); |
81 | /// *data = 2; |
82 | /// panic!(); |
83 | /// }).join(); |
84 | /// |
85 | /// match mutex.lock() { |
86 | /// Ok(_) => unreachable!(), |
87 | /// Err(p_err) => { |
88 | /// let data = p_err.get_ref(); |
89 | /// println!("recovered: {data}" ); |
90 | /// } |
91 | /// }; |
92 | /// ``` |
93 | /// [`Mutex`]: crate::sync::Mutex |
94 | /// [`RwLock`]: crate::sync::RwLock |
95 | #[stable (feature = "rust1" , since = "1.0.0" )] |
96 | pub struct PoisonError<T> { |
97 | guard: T, |
98 | } |
99 | |
100 | /// An enumeration of possible errors associated with a [`TryLockResult`] which |
101 | /// can occur while trying to acquire a lock, from the [`try_lock`] method on a |
102 | /// [`Mutex`] or the [`try_read`] and [`try_write`] methods on an [`RwLock`]. |
103 | /// |
104 | /// [`try_lock`]: crate::sync::Mutex::try_lock |
105 | /// [`try_read`]: crate::sync::RwLock::try_read |
106 | /// [`try_write`]: crate::sync::RwLock::try_write |
107 | /// [`Mutex`]: crate::sync::Mutex |
108 | /// [`RwLock`]: crate::sync::RwLock |
109 | #[stable (feature = "rust1" , since = "1.0.0" )] |
110 | pub enum TryLockError<T> { |
111 | /// The lock could not be acquired because another thread failed while holding |
112 | /// the lock. |
113 | #[stable (feature = "rust1" , since = "1.0.0" )] |
114 | Poisoned(#[stable (feature = "rust1" , since = "1.0.0" )] PoisonError<T>), |
115 | /// The lock could not be acquired at this time because the operation would |
116 | /// otherwise block. |
117 | #[stable (feature = "rust1" , since = "1.0.0" )] |
118 | WouldBlock, |
119 | } |
120 | |
121 | /// A type alias for the result of a lock method which can be poisoned. |
122 | /// |
123 | /// The [`Ok`] variant of this result indicates that the primitive was not |
124 | /// poisoned, and the `Guard` is contained within. The [`Err`] variant indicates |
125 | /// that the primitive was poisoned. Note that the [`Err`] variant *also* carries |
126 | /// the associated guard, and it can be acquired through the [`into_inner`] |
127 | /// method. |
128 | /// |
129 | /// [`into_inner`]: PoisonError::into_inner |
130 | #[stable (feature = "rust1" , since = "1.0.0" )] |
131 | pub type LockResult<Guard> = Result<Guard, PoisonError<Guard>>; |
132 | |
133 | /// A type alias for the result of a nonblocking locking method. |
134 | /// |
135 | /// For more information, see [`LockResult`]. A `TryLockResult` doesn't |
136 | /// necessarily hold the associated guard in the [`Err`] type as the lock might not |
137 | /// have been acquired for other reasons. |
138 | #[stable (feature = "rust1" , since = "1.0.0" )] |
139 | pub type TryLockResult<Guard> = Result<Guard, TryLockError<Guard>>; |
140 | |
141 | #[stable (feature = "rust1" , since = "1.0.0" )] |
142 | impl<T> fmt::Debug for PoisonError<T> { |
143 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
144 | f.debug_struct(name:"PoisonError" ).finish_non_exhaustive() |
145 | } |
146 | } |
147 | |
148 | #[stable (feature = "rust1" , since = "1.0.0" )] |
149 | impl<T> fmt::Display for PoisonError<T> { |
150 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
151 | "poisoned lock: another task failed inside" .fmt(f) |
152 | } |
153 | } |
154 | |
155 | #[stable (feature = "rust1" , since = "1.0.0" )] |
156 | impl<T> Error for PoisonError<T> { |
157 | #[allow (deprecated)] |
158 | fn description(&self) -> &str { |
159 | "poisoned lock: another task failed inside" |
160 | } |
161 | } |
162 | |
163 | impl<T> PoisonError<T> { |
164 | /// Creates a `PoisonError`. |
165 | /// |
166 | /// This is generally created by methods like [`Mutex::lock`](crate::sync::Mutex::lock) |
167 | /// or [`RwLock::read`](crate::sync::RwLock::read). |
168 | #[stable (feature = "sync_poison" , since = "1.2.0" )] |
169 | pub fn new(guard: T) -> PoisonError<T> { |
170 | PoisonError { guard } |
171 | } |
172 | |
173 | /// Consumes this error indicating that a lock is poisoned, returning the |
174 | /// underlying guard to allow access regardless. |
175 | /// |
176 | /// # Examples |
177 | /// |
178 | /// ``` |
179 | /// use std::collections::HashSet; |
180 | /// use std::sync::{Arc, Mutex}; |
181 | /// use std::thread; |
182 | /// |
183 | /// let mutex = Arc::new(Mutex::new(HashSet::new())); |
184 | /// |
185 | /// // poison the mutex |
186 | /// let c_mutex = Arc::clone(&mutex); |
187 | /// let _ = thread::spawn(move || { |
188 | /// let mut data = c_mutex.lock().unwrap(); |
189 | /// data.insert(10); |
190 | /// panic!(); |
191 | /// }).join(); |
192 | /// |
193 | /// let p_err = mutex.lock().unwrap_err(); |
194 | /// let data = p_err.into_inner(); |
195 | /// println!("recovered {} items" , data.len()); |
196 | /// ``` |
197 | #[stable (feature = "sync_poison" , since = "1.2.0" )] |
198 | pub fn into_inner(self) -> T { |
199 | self.guard |
200 | } |
201 | |
202 | /// Reaches into this error indicating that a lock is poisoned, returning a |
203 | /// reference to the underlying guard to allow access regardless. |
204 | #[stable (feature = "sync_poison" , since = "1.2.0" )] |
205 | pub fn get_ref(&self) -> &T { |
206 | &self.guard |
207 | } |
208 | |
209 | /// Reaches into this error indicating that a lock is poisoned, returning a |
210 | /// mutable reference to the underlying guard to allow access regardless. |
211 | #[stable (feature = "sync_poison" , since = "1.2.0" )] |
212 | pub fn get_mut(&mut self) -> &mut T { |
213 | &mut self.guard |
214 | } |
215 | } |
216 | |
217 | #[stable (feature = "rust1" , since = "1.0.0" )] |
218 | impl<T> From<PoisonError<T>> for TryLockError<T> { |
219 | fn from(err: PoisonError<T>) -> TryLockError<T> { |
220 | TryLockError::Poisoned(err) |
221 | } |
222 | } |
223 | |
224 | #[stable (feature = "rust1" , since = "1.0.0" )] |
225 | impl<T> fmt::Debug for TryLockError<T> { |
226 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
227 | match *self { |
228 | TryLockError::Poisoned(..) => "Poisoned(..)" .fmt(f), |
229 | TryLockError::WouldBlock => "WouldBlock" .fmt(f), |
230 | } |
231 | } |
232 | } |
233 | |
234 | #[stable (feature = "rust1" , since = "1.0.0" )] |
235 | impl<T> fmt::Display for TryLockError<T> { |
236 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
237 | match&str *self { |
238 | TryLockError::Poisoned(..) => "poisoned lock: another task failed inside" , |
239 | TryLockError::WouldBlock => "try_lock failed because the operation would block" , |
240 | } |
241 | .fmt(f) |
242 | } |
243 | } |
244 | |
245 | #[stable (feature = "rust1" , since = "1.0.0" )] |
246 | impl<T> Error for TryLockError<T> { |
247 | #[allow (deprecated, deprecated_in_future)] |
248 | fn description(&self) -> &str { |
249 | match *self { |
250 | TryLockError::Poisoned(ref p: &PoisonError) => p.description(), |
251 | TryLockError::WouldBlock => "try_lock failed because the operation would block" , |
252 | } |
253 | } |
254 | |
255 | #[allow (deprecated)] |
256 | fn cause(&self) -> Option<&dyn Error> { |
257 | match *self { |
258 | TryLockError::Poisoned(ref p: &PoisonError) => Some(p), |
259 | _ => None, |
260 | } |
261 | } |
262 | } |
263 | |
264 | pub fn map_result<T, U, F>(result: LockResult<T>, f: F) -> LockResult<U> |
265 | where |
266 | F: FnOnce(T) -> U, |
267 | { |
268 | match result { |
269 | Ok(t: T) => Ok(f(t)), |
270 | Err(PoisonError { guard: T }) => Err(PoisonError::new(guard:f(guard))), |
271 | } |
272 | } |
273 | |