@@ -18,20 +18,54 @@ use crate::sys::sync as sys;
1818/// # Poisoning
1919///
2020/// The mutexes in this module implement a strategy called "poisoning" where a
21- /// mutex is considered poisoned whenever a thread panics while holding the
22- /// mutex. Once a mutex is poisoned, all other threads are unable to access the
23- /// data by default as it is likely tainted (some invariant is not being
24- /// upheld).
21+ /// mutex becomes poisoned if it recognizes that the thread holding it has
22+ /// panicked.
2523///
26- /// For a mutex, this means that the [`lock`] and [`try_lock`] methods return a
24+ /// Once a mutex is poisoned, all other threads are unable to access the data by
25+ /// default as it is likely tainted (some invariant is not being upheld). For a
26+ /// mutex, this means that the [`lock`] and [`try_lock`] methods return a
2727/// [`Result`] which indicates whether a mutex has been poisoned or not. Most
2828/// usage of a mutex will simply [`unwrap()`] these results, propagating panics
2929/// among threads to ensure that a possibly invalid invariant is not witnessed.
3030///
31- /// A poisoned mutex, however, does not prevent all access to the underlying
32- /// data. The [`PoisonError`] type has an [`into_inner`] method which will return
33- /// the guard that would have otherwise been returned on a successful lock. This
34- /// allows access to the data, despite the lock being poisoned.
31+ /// Poisoning is only advisory: the [`PoisonError`] type has an [`into_inner`]
32+ /// method which will return the guard that would have otherwise been returned
33+ /// on a successful lock. This allows access to the data, despite the lock being
34+ /// poisoned.
35+ ///
36+ /// In addition, the panic detection is not ideal, so even unpoisoned mutexes
37+ /// need to be handled with care, since certain panics may have been skipped.
38+ /// Therefore, `unsafe` code cannot rely on poisoning for soundness. Here's an
39+ /// example of **incorrect** use of poisoning:
40+ ///
41+ /// ```rust
42+ /// use std::sync::Mutex;
43+ ///
44+ /// struct MutexBox<T> {
45+ /// data: Mutex<*mut T>,
46+ /// }
47+ ///
48+ /// impl<T> MutexBox<T> {
49+ /// pub fn new(value: T) -> Self {
50+ /// Self {
51+ /// data: Mutex::new(Box::into_raw(Box::new(value))),
52+ /// }
53+ /// }
54+ ///
55+ /// pub fn replace_with(&self, f: impl FnOnce(T) -> T) {
56+ /// let ptr = self.data.lock().expect("poisoned");
57+ /// // While `f` is running, the data is moved out of `*ptr`. If `f`
58+ /// // panics, `*ptr` keeps pointing at a dropped value. The intention
59+ /// // is that this will poison the mutex, so the following calls to
60+ /// // `replace_with` will panic without reading `*ptr`. But since
61+ /// // poisoning is not guaranteed to occur, this can lead to
62+ /// // use-after-free.
63+ /// unsafe {
64+ /// (*ptr).write(f((*ptr).read()));
65+ /// }
66+ /// }
67+ /// }
68+ /// ```
3569///
3670/// [`new`]: Self::new
3771/// [`lock`]: Self::lock
0 commit comments