# `Poison<T>` ###### tags: `Kitchen Sync` Use cases: - `mutex.lock().poison_guard().unwrap()` - `mutex.lock().poison_guard().unwrap_or_else(|p| p.unpoison(|v| { .. }))` - `mutex.lock().poison_guard().or_else(|p| p.try_unpoison(|v| Ok(())))` - `ONCE.get_or_init(|| Poison::catch_unwind(|| { .. }))` - `ONCE.get_or_init(|| Poison::try_catch_unwind(|guard| Ok(v)))` - `let p = file.poison_gard()?` The behavior of synchronization primitives with respect to poisoning is to _do the minimum necessary to uphold their contract_. For `Mutex<T>`, the contract is that only a single thread may hold the lock at a time. For `Once`, the contract is that only a single closure passed to `call_once` will ever get the chance to execute. ## Replacing `sync::Mutex<T>` with `lock::Mutex<Poison<T>>` With: ```rust // sync::Mutex<T> let g = mutex.lock().unwrap(); ``` it's not clear what exactly could go wrong. Is the problem that we couldn't acquire the lock? Instead, we can write: ```rust // lock::Mutex<Poison<T>> let g = mutex.lock().poison_guard().unwrap(); ``` which makes it more obvious that what we're asserting on is poisoning. It's unrelated to the lock itself. If we wanted to use `Mutex<()>`, instead of: ```rust // sync::Mutex<()> mutex.lock().unwrap() ``` we can write: ```rust // lock::Mutex<Poison> (we could consider `unwrap` methods directly on `Poison<()>` and implement `Try` for it?) mutex.lock().poison_guard().unwrap() ``` If we don't care, we simply write: ```rust // lock::Mutex<T> let g = mutex.lock(); ``` and panics that unwind through the lock's guard will become tainted. Without using `Poison<T>` there's no recovering from this. ## Replacing `sync::Once` with `lazy::Once<Poison<()>>` The `Once` type is a nifty little synchronization primitive that lets you call a closure exactly once. It also implements poisoning. Instead of: ```rust // sync::Once ONCE.call_once(|| { .. }) ``` we can write: ```rust // lazy::Once<Poison> ONCE.get_or_init(|| Poison::catch_unwind(|| { .. })).poison_guard().unwrap() ``` We could implement something specifically for `lazy::Once<Poison>`: ```rust // lazy::Once<()> ONCE.call_once(|| { .. }); // lazy::Once<Poison> ONCE.call_once(|| { .. }).poison_guard().unwrap() ``` # Refactoring `std::sync` (stage 1) Create new top-level modules: - `std::arc` containing `Arc<T>`. - `std::atomic` containing `Atomic*` (future: consider an `Atomic<T>`). - `std::lock`: - containing a new non-poisoning `Mutex<T>`. Reimplement `sync::Mutex<T>` in terms of `lock::Mutex<Poison<T>>`. Allow converting a `sync::MutexGuard<'a, T>` into a `lock::MutexGuard<'a, Poison<T>>`. - containing a new non-poisoning `RwLock<T>`. Reimplement `sync::RwLock<T>` in terms of `lock::RwLock<Poison<T>>`. Allow converting `sync::RwGuard<'a, T>`s into `lock::RwGuard<'a, Poison<T>>`s. Refactor `panic`: - Introduce a `Poison<T = ()>` type. Serving the use-cases of: - Observing and propagating panics and other early returns - Specifying consistent behavior on panics and poisoning for locks and other synchronization primitives - Deprecate `UnwindSafe`, `RefUnwindSafe`, and `AssertUnwindSafe`. - Remove the `UnwindSafe` bound from `panic::catch_unwind`. We can implement this plan then issue a crater run to see how much breakage we see just in libraries in the ecosystem. # Refactoring `std::sync` (stage 2) Deprecating `sync`: - Deprecate `Once` in favor of the `lazy` API. - Deprecate `sync` entirely. Refactor `lazy`: - Move `OnceCell` to `cell`. - Rename `Lazy` to `LazyCell` and move to `cell`. - Rename `SyncOnceCell` to `Once`. - Rename `SyncLazy` to `Lazy`. # Refactoring `std::sync` (stage 3) - `std::channel` containing some new `mpmc` API.