---
# System prepended metadata

title: '`Poison<T>`'
tags: [Kitchen Sync]

---

# `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.