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