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