1 | //! Synchronization objects that employ poisoning. |
2 | //! |
3 | //! # Poisoning |
4 | //! |
5 | //! All synchronization objects in this module implement a strategy called "poisoning" |
6 | //! where if a thread panics while holding the exclusive access granted by the primitive, |
7 | //! the state of the primitive is set to "poisoned". |
8 | //! This information is then propagated to all other threads |
9 | //! to signify that the data protected by this primitive is likely tainted |
10 | //! (some invariant is not being upheld). |
11 | //! |
12 | //! The specifics of how this "poisoned" state affects other threads |
13 | //! depend on the primitive. See [#Overview] bellow. |
14 | //! |
15 | //! For the alternative implementations that do not employ poisoning, |
16 | //! see `std::sys::nonpoisoning`. |
17 | //! |
18 | //! # Overview |
19 | //! |
20 | //! Below is a list of synchronization objects provided by this module |
21 | //! with a high-level overview for each object and a description |
22 | //! of how it employs "poisoning". |
23 | //! |
24 | //! - [`Condvar`]: Condition Variable, providing the ability to block |
25 | //! a thread while waiting for an event to occur. |
26 | //! |
27 | //! Condition variables are typically associated with |
28 | //! a boolean predicate (a condition) and a mutex. |
29 | //! This implementation is associated with [`poison::Mutex`](Mutex), |
30 | //! which employs poisoning. |
31 | //! For this reason, [`Condvar::wait()`] will return a [`LockResult`], |
32 | //! just like [`poison::Mutex::lock()`](Mutex::lock) does. |
33 | //! |
34 | //! - [`Mutex`]: Mutual Exclusion mechanism, which ensures that at |
35 | //! most one thread at a time is able to access some data. |
36 | //! |
37 | //! [`Mutex::lock()`] returns a [`LockResult`], |
38 | //! providing a way to deal with the poisoned state. |
39 | //! See [`Mutex`'s documentation](Mutex#poisoning) for more. |
40 | //! |
41 | //! - [`Once`]: A thread-safe way to run a piece of code only once. |
42 | //! Mostly useful for implementing one-time global initialization. |
43 | //! |
44 | //! [`Once`] is poisoned if the piece of code passed to |
45 | //! [`Once::call_once()`] or [`Once::call_once_force()`] panics. |
46 | //! When in poisoned state, subsequent calls to [`Once::call_once()`] will panic too. |
47 | //! [`Once::call_once_force()`] can be used to clear the poisoned state. |
48 | //! |
49 | //! - [`RwLock`]: Provides a mutual exclusion mechanism which allows |
50 | //! multiple readers at the same time, while allowing only one |
51 | //! writer at a time. In some cases, this can be more efficient than |
52 | //! a mutex. |
53 | //! |
54 | //! This implementation, like [`Mutex`], will become poisoned on a panic. |
55 | //! Note, however, that an `RwLock` may only be poisoned if a panic occurs |
56 | //! while it is locked exclusively (write mode). If a panic occurs in any reader, |
57 | //! then the lock will not be poisoned. |
58 | |
59 | // FIXME(sync_nonpoison) add links to sync::nonpoison to the doc comment above. |
60 | |
61 | #[stable (feature = "rust1" , since = "1.0.0" )] |
62 | pub use self::condvar::{Condvar, WaitTimeoutResult}; |
63 | #[unstable (feature = "mapped_lock_guards" , issue = "117108" )] |
64 | pub use self::mutex::MappedMutexGuard; |
65 | #[stable (feature = "rust1" , since = "1.0.0" )] |
66 | pub use self::mutex::{Mutex, MutexGuard}; |
67 | #[stable (feature = "rust1" , since = "1.0.0" )] |
68 | #[expect (deprecated)] |
69 | pub use self::once::ONCE_INIT; |
70 | #[stable (feature = "rust1" , since = "1.0.0" )] |
71 | pub use self::once::{Once, OnceState}; |
72 | #[unstable (feature = "mapped_lock_guards" , issue = "117108" )] |
73 | pub use self::rwlock::{MappedRwLockReadGuard, MappedRwLockWriteGuard}; |
74 | #[stable (feature = "rust1" , since = "1.0.0" )] |
75 | pub use self::rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}; |
76 | use crate::error::Error; |
77 | use crate::fmt; |
78 | #[cfg (panic = "unwind" )] |
79 | use crate::sync::atomic::{Atomic, AtomicBool, Ordering}; |
80 | #[cfg (panic = "unwind" )] |
81 | use crate::thread; |
82 | |
83 | mod condvar; |
84 | #[stable (feature = "rust1" , since = "1.0.0" )] |
85 | mod mutex; |
86 | pub(crate) mod once; |
87 | mod rwlock; |
88 | |
89 | pub(crate) struct Flag { |
90 | #[cfg (panic = "unwind" )] |
91 | failed: Atomic<bool>, |
92 | } |
93 | |
94 | // Note that the Ordering uses to access the `failed` field of `Flag` below is |
95 | // always `Relaxed`, and that's because this isn't actually protecting any data, |
96 | // it's just a flag whether we've panicked or not. |
97 | // |
98 | // The actual location that this matters is when a mutex is **locked** which is |
99 | // where we have external synchronization ensuring that we see memory |
100 | // reads/writes to this flag. |
101 | // |
102 | // As a result, if it matters, we should see the correct value for `failed` in |
103 | // all cases. |
104 | |
105 | impl Flag { |
106 | #[inline ] |
107 | pub const fn new() -> Flag { |
108 | Flag { |
109 | #[cfg (panic = "unwind" )] |
110 | failed: AtomicBool::new(false), |
111 | } |
112 | } |
113 | |
114 | /// Checks the flag for an unguarded borrow, where we only care about existing poison. |
115 | #[inline ] |
116 | pub fn borrow(&self) -> LockResult<()> { |
117 | if self.get() { Err(PoisonError::new(())) } else { Ok(()) } |
118 | } |
119 | |
120 | /// Checks the flag for a guarded borrow, where we may also set poison when `done`. |
121 | #[inline ] |
122 | pub fn guard(&self) -> LockResult<Guard> { |
123 | let ret = Guard { |
124 | #[cfg (panic = "unwind" )] |
125 | panicking: thread::panicking(), |
126 | }; |
127 | if self.get() { Err(PoisonError::new(ret)) } else { Ok(ret) } |
128 | } |
129 | |
130 | #[inline ] |
131 | #[cfg (panic = "unwind" )] |
132 | pub fn done(&self, guard: &Guard) { |
133 | if !guard.panicking && thread::panicking() { |
134 | self.failed.store(true, Ordering::Relaxed); |
135 | } |
136 | } |
137 | |
138 | #[inline ] |
139 | #[cfg (not(panic = "unwind" ))] |
140 | pub fn done(&self, _guard: &Guard) {} |
141 | |
142 | #[inline ] |
143 | #[cfg (panic = "unwind" )] |
144 | pub fn get(&self) -> bool { |
145 | self.failed.load(Ordering::Relaxed) |
146 | } |
147 | |
148 | #[inline (always)] |
149 | #[cfg (not(panic = "unwind" ))] |
150 | pub fn get(&self) -> bool { |
151 | false |
152 | } |
153 | |
154 | #[inline ] |
155 | pub fn clear(&self) { |
156 | #[cfg (panic = "unwind" )] |
157 | self.failed.store(false, Ordering::Relaxed) |
158 | } |
159 | } |
160 | |
161 | #[derive (Clone)] |
162 | pub(crate) struct Guard { |
163 | #[cfg (panic = "unwind" )] |
164 | panicking: bool, |
165 | } |
166 | |
167 | /// A type of error which can be returned whenever a lock is acquired. |
168 | /// |
169 | /// Both [`Mutex`]es and [`RwLock`]s are poisoned whenever a thread fails while the lock |
170 | /// is held. The precise semantics for when a lock is poisoned is documented on |
171 | /// each lock. For a lock in the poisoned state, unless the state is cleared manually, |
172 | /// all future acquisitions will return this error. |
173 | /// |
174 | /// # Examples |
175 | /// |
176 | /// ``` |
177 | /// use std::sync::{Arc, Mutex}; |
178 | /// use std::thread; |
179 | /// |
180 | /// let mutex = Arc::new(Mutex::new(1)); |
181 | /// |
182 | /// // poison the mutex |
183 | /// let c_mutex = Arc::clone(&mutex); |
184 | /// let _ = thread::spawn(move || { |
185 | /// let mut data = c_mutex.lock().unwrap(); |
186 | /// *data = 2; |
187 | /// panic!(); |
188 | /// }).join(); |
189 | /// |
190 | /// match mutex.lock() { |
191 | /// Ok(_) => unreachable!(), |
192 | /// Err(p_err) => { |
193 | /// let data = p_err.get_ref(); |
194 | /// println!("recovered: {data}" ); |
195 | /// } |
196 | /// }; |
197 | /// ``` |
198 | /// [`Mutex`]: crate::sync::Mutex |
199 | /// [`RwLock`]: crate::sync::RwLock |
200 | #[stable (feature = "rust1" , since = "1.0.0" )] |
201 | pub struct PoisonError<T> { |
202 | data: T, |
203 | #[cfg (not(panic = "unwind" ))] |
204 | _never: !, |
205 | } |
206 | |
207 | /// An enumeration of possible errors associated with a [`TryLockResult`] which |
208 | /// can occur while trying to acquire a lock, from the [`try_lock`] method on a |
209 | /// [`Mutex`] or the [`try_read`] and [`try_write`] methods on an [`RwLock`]. |
210 | /// |
211 | /// [`try_lock`]: crate::sync::Mutex::try_lock |
212 | /// [`try_read`]: crate::sync::RwLock::try_read |
213 | /// [`try_write`]: crate::sync::RwLock::try_write |
214 | /// [`Mutex`]: crate::sync::Mutex |
215 | /// [`RwLock`]: crate::sync::RwLock |
216 | #[stable (feature = "rust1" , since = "1.0.0" )] |
217 | pub enum TryLockError<T> { |
218 | /// The lock could not be acquired because another thread failed while holding |
219 | /// the lock. |
220 | #[stable (feature = "rust1" , since = "1.0.0" )] |
221 | Poisoned(#[stable (feature = "rust1" , since = "1.0.0" )] PoisonError<T>), |
222 | /// The lock could not be acquired at this time because the operation would |
223 | /// otherwise block. |
224 | #[stable (feature = "rust1" , since = "1.0.0" )] |
225 | WouldBlock, |
226 | } |
227 | |
228 | /// A type alias for the result of a lock method which can be poisoned. |
229 | /// |
230 | /// The [`Ok`] variant of this result indicates that the primitive was not |
231 | /// poisoned, and the operation result is contained within. The [`Err`] variant indicates |
232 | /// that the primitive was poisoned. Note that the [`Err`] variant *also* carries |
233 | /// an associated value assigned by the lock method, and it can be acquired through the |
234 | /// [`into_inner`] method. The semantics of the associated value depends on the corresponding |
235 | /// lock method. |
236 | /// |
237 | /// [`into_inner`]: PoisonError::into_inner |
238 | #[stable (feature = "rust1" , since = "1.0.0" )] |
239 | pub type LockResult<T> = Result<T, PoisonError<T>>; |
240 | |
241 | /// A type alias for the result of a nonblocking locking method. |
242 | /// |
243 | /// For more information, see [`LockResult`]. A `TryLockResult` doesn't |
244 | /// necessarily hold the associated guard in the [`Err`] type as the lock might not |
245 | /// have been acquired for other reasons. |
246 | #[stable (feature = "rust1" , since = "1.0.0" )] |
247 | pub type TryLockResult<Guard> = Result<Guard, TryLockError<Guard>>; |
248 | |
249 | #[stable (feature = "rust1" , since = "1.0.0" )] |
250 | impl<T> fmt::Debug for PoisonError<T> { |
251 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
252 | f.debug_struct(name:"PoisonError" ).finish_non_exhaustive() |
253 | } |
254 | } |
255 | |
256 | #[stable (feature = "rust1" , since = "1.0.0" )] |
257 | impl<T> fmt::Display for PoisonError<T> { |
258 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
259 | "poisoned lock: another task failed inside" .fmt(f) |
260 | } |
261 | } |
262 | |
263 | #[stable (feature = "rust1" , since = "1.0.0" )] |
264 | impl<T> Error for PoisonError<T> { |
265 | #[allow (deprecated)] |
266 | fn description(&self) -> &str { |
267 | "poisoned lock: another task failed inside" |
268 | } |
269 | } |
270 | |
271 | impl<T> PoisonError<T> { |
272 | /// Creates a `PoisonError`. |
273 | /// |
274 | /// This is generally created by methods like [`Mutex::lock`](crate::sync::Mutex::lock) |
275 | /// or [`RwLock::read`](crate::sync::RwLock::read). |
276 | /// |
277 | /// This method may panic if std was built with `panic="abort"`. |
278 | #[cfg (panic = "unwind" )] |
279 | #[stable (feature = "sync_poison" , since = "1.2.0" )] |
280 | pub fn new(data: T) -> PoisonError<T> { |
281 | PoisonError { data } |
282 | } |
283 | |
284 | /// Creates a `PoisonError`. |
285 | /// |
286 | /// This is generally created by methods like [`Mutex::lock`](crate::sync::Mutex::lock) |
287 | /// or [`RwLock::read`](crate::sync::RwLock::read). |
288 | /// |
289 | /// This method may panic if std was built with `panic="abort"`. |
290 | #[cfg (not(panic = "unwind" ))] |
291 | #[stable (feature = "sync_poison" , since = "1.2.0" )] |
292 | #[track_caller ] |
293 | pub fn new(_data: T) -> PoisonError<T> { |
294 | panic!("PoisonError created in a libstd built with panic= \"abort \"" ) |
295 | } |
296 | |
297 | /// Consumes this error indicating that a lock is poisoned, returning the |
298 | /// associated data. |
299 | /// |
300 | /// # Examples |
301 | /// |
302 | /// ``` |
303 | /// use std::collections::HashSet; |
304 | /// use std::sync::{Arc, Mutex}; |
305 | /// use std::thread; |
306 | /// |
307 | /// let mutex = Arc::new(Mutex::new(HashSet::new())); |
308 | /// |
309 | /// // poison the mutex |
310 | /// let c_mutex = Arc::clone(&mutex); |
311 | /// let _ = thread::spawn(move || { |
312 | /// let mut data = c_mutex.lock().unwrap(); |
313 | /// data.insert(10); |
314 | /// panic!(); |
315 | /// }).join(); |
316 | /// |
317 | /// let p_err = mutex.lock().unwrap_err(); |
318 | /// let data = p_err.into_inner(); |
319 | /// println!("recovered {} items" , data.len()); |
320 | /// ``` |
321 | #[stable (feature = "sync_poison" , since = "1.2.0" )] |
322 | pub fn into_inner(self) -> T { |
323 | self.data |
324 | } |
325 | |
326 | /// Reaches into this error indicating that a lock is poisoned, returning a |
327 | /// reference to the associated data. |
328 | #[stable (feature = "sync_poison" , since = "1.2.0" )] |
329 | pub fn get_ref(&self) -> &T { |
330 | &self.data |
331 | } |
332 | |
333 | /// Reaches into this error indicating that a lock is poisoned, returning a |
334 | /// mutable reference to the associated data. |
335 | #[stable (feature = "sync_poison" , since = "1.2.0" )] |
336 | pub fn get_mut(&mut self) -> &mut T { |
337 | &mut self.data |
338 | } |
339 | } |
340 | |
341 | #[stable (feature = "rust1" , since = "1.0.0" )] |
342 | impl<T> From<PoisonError<T>> for TryLockError<T> { |
343 | fn from(err: PoisonError<T>) -> TryLockError<T> { |
344 | TryLockError::Poisoned(err) |
345 | } |
346 | } |
347 | |
348 | #[stable (feature = "rust1" , since = "1.0.0" )] |
349 | impl<T> fmt::Debug for TryLockError<T> { |
350 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
351 | match *self { |
352 | #[cfg (panic = "unwind" )] |
353 | TryLockError::Poisoned(..) => "Poisoned(..)" .fmt(f), |
354 | #[cfg (not(panic = "unwind" ))] |
355 | TryLockError::Poisoned(ref p) => match p._never {}, |
356 | TryLockError::WouldBlock => "WouldBlock" .fmt(f), |
357 | } |
358 | } |
359 | } |
360 | |
361 | #[stable (feature = "rust1" , since = "1.0.0" )] |
362 | impl<T> fmt::Display for TryLockError<T> { |
363 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
364 | match&'static str *self { |
365 | #[cfg (panic = "unwind" )] |
366 | TryLockError::Poisoned(..) => "poisoned lock: another task failed inside" , |
367 | #[cfg (not(panic = "unwind" ))] |
368 | TryLockError::Poisoned(ref p) => match p._never {}, |
369 | TryLockError::WouldBlock => "try_lock failed because the operation would block" , |
370 | } |
371 | .fmt(f) |
372 | } |
373 | } |
374 | |
375 | #[stable (feature = "rust1" , since = "1.0.0" )] |
376 | impl<T> Error for TryLockError<T> { |
377 | #[allow (deprecated, deprecated_in_future)] |
378 | fn description(&self) -> &str { |
379 | match *self { |
380 | #[cfg (panic = "unwind" )] |
381 | TryLockError::Poisoned(ref p: &PoisonError) => p.description(), |
382 | #[cfg (not(panic = "unwind" ))] |
383 | TryLockError::Poisoned(ref p) => match p._never {}, |
384 | TryLockError::WouldBlock => "try_lock failed because the operation would block" , |
385 | } |
386 | } |
387 | |
388 | #[allow (deprecated)] |
389 | fn cause(&self) -> Option<&dyn Error> { |
390 | match *self { |
391 | #[cfg (panic = "unwind" )] |
392 | TryLockError::Poisoned(ref p: &PoisonError) => Some(p), |
393 | #[cfg (not(panic = "unwind" ))] |
394 | TryLockError::Poisoned(ref p) => match p._never {}, |
395 | _ => None, |
396 | } |
397 | } |
398 | } |
399 | |
400 | pub(crate) fn map_result<T, U, F>(result: LockResult<T>, f: F) -> LockResult<U> |
401 | where |
402 | F: FnOnce(T) -> U, |
403 | { |
404 | match result { |
405 | Ok(t: T) => Ok(f(t)), |
406 | #[cfg (panic = "unwind" )] |
407 | Err(PoisonError { data: T }) => Err(PoisonError::new(data:f(data))), |
408 | } |
409 | } |
410 | |