| 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 | |