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