Skip to main content

coven_foundation/
locks.rs

1//! Taking a lock whose holder may have panicked (api.md E15).
2//!
3//! A panic while a `Mutex` is held, or an `RwLock` is held for writing,
4//! poisons it: std's way of saying the value may be half-changed. What that
5//! means depends on what the lock guards, so coven takes each lock with the
6//! helper that says it.
7//!
8//! - `lock_unpoisoned`: every change made under the lock is whole before
9//!   anything in it can panic, so a poisoned lock holds a whole value and is
10//!   used as it is.
11//! - `lock_or_abort`: state the whole process shares, which a change can
12//!   leave half-made. No store's handle covers it, so the process ends.
13//! - coven-database's `lock_or_fail_handle`: one open store's state, which a
14//!   change can leave half-made. That store's handle fails, and every later
15//!   call on it says so.
16
17use std::sync::{Condvar, Mutex, MutexGuard, PoisonError, RwLock};
18use std::sync::{RwLockReadGuard, RwLockWriteGuard};
19
20/// Lock `mutex`, whose value no panic can leave half-changed.
21pub fn lock_unpoisoned<T: ?Sized>(mutex: &Mutex<T>) -> MutexGuard<'_, T> {
22    mutex.lock().unwrap_or_else(PoisonError::into_inner)
23}
24
25/// Read `lock`, whose value no panic can leave half-changed.
26pub fn read_unpoisoned<T: ?Sized>(lock: &RwLock<T>) -> RwLockReadGuard<'_, T> {
27    lock.read().unwrap_or_else(PoisonError::into_inner)
28}
29
30/// Write `lock`, whose value no panic can leave half-changed.
31pub fn write_unpoisoned<T: ?Sized>(lock: &RwLock<T>) -> RwLockWriteGuard<'_, T> {
32    lock.write().unwrap_or_else(PoisonError::into_inner)
33}
34
35/// The value of `mutex`, whose value no panic can leave half-changed, as it
36/// goes.
37pub fn into_unpoisoned<T>(mutex: Mutex<T>) -> T {
38    mutex.into_inner().unwrap_or_else(PoisonError::into_inner)
39}
40
41/// Wait on `condvar` with `guard`, the guard of a lock whose value no panic
42/// can leave half-changed.
43pub fn wait_unpoisoned<'a, T>(condvar: &Condvar, guard: MutexGuard<'a, T>) -> MutexGuard<'a, T> {
44    condvar.wait(guard).unwrap_or_else(PoisonError::into_inner)
45}
46
47/// Lock `mutex`, named `what`: state the whole process shares. A panic while
48/// it was held may have left it half-changed, and nothing narrower than the
49/// process can be failed for it, so the process ends.
50pub fn lock_or_abort<'a, T: ?Sized>(mutex: &'a Mutex<T>, what: &'static str) -> MutexGuard<'a, T> {
51    mutex.lock().unwrap_or_else(|_| abort_on(what))
52}
53
54/// End the process: a panic left `what`, state it shares, half-changed.
55fn abort_on(what: &'static str) -> ! {
56    tracing::error!(what, "a panic left process-wide state half-changed; ending the process");
57    eprintln!("coven: a panic left {what} half-changed; ending the process");
58    std::process::abort()
59}
60
61/// End the process: `what`, state the whole process shares, breaks `rule`.
62/// Nothing narrower than the process can be failed for it, and a drop that
63/// finds it, perhaps during an unwind, must not panic.
64pub(crate) fn abort_broken(what: &'static str, rule: &'static str) -> ! {
65    tracing::error!(what, rule, "process-wide state broke its rule; ending the process");
66    eprintln!("coven: {what} broke its rule that {rule}; ending the process");
67    std::process::abort()
68}
69
70#[cfg(test)]
71#[path = "locks_tests.rs"]
72mod tests;