Skip to main content

unlit_ecs/
sync.rs

1//! Poison-proof lock helpers, and the rules the locks are taken by.
2//!
3//! A lock's contents are only ever plain data here - a flag, a status, a
4//! message - so a panic while it was held leaves nothing inconsistent behind.
5//! Reading through these helpers instead of `expect`ing keeps one panic from
6//! turning a lock into a permanent failure for every later caller. That
7//! premise holds only while a lock covers data and nothing else, which is
8//! what the rules below are for:
9//!
10//! - A guard covers a critical section and no user code. A callback, a query
11//!   into a world, anything that may panic, runs before the lock is taken or
12//!   after it is dropped. Reading a batch out under its lock and running the
13//!   caller's closure without it is the shape to copy.
14//! - `Debug` and `Display` never wait on a lock. A value is printed from a
15//!   panic handler, a log line or a signal handler, sometimes while another
16//!   lock is held, so these implementations print the shape of the value
17//!   instead of reading it, and a derived one prints what the standard
18//!   library's own `Mutex` and `RwLock` impls offer - which is a try, not a
19//!   wait.
20//! - No lock is taken while another is held, so there is no order to keep
21//!   and no way for two of them to wait on each other. A place that needs
22//!   two takes the first, copies what it needs out of it, and drops it before
23//!   taking the second.
24
25use std::sync::{Mutex, MutexGuard, PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard};
26
27/// Lock `mutex`, ignoring poisoning.
28pub fn lock<T>(mutex: &Mutex<T>) -> MutexGuard<'_, T> {
29    mutex.lock().unwrap_or_else(PoisonError::into_inner)
30}
31
32/// Take `lock` for reading, ignoring poisoning.
33pub fn read<T>(lock: &RwLock<T>) -> RwLockReadGuard<'_, T> {
34    lock.read().unwrap_or_else(PoisonError::into_inner)
35}
36
37/// Take `lock` for writing, ignoring poisoning.
38pub fn write<T>(lock: &RwLock<T>) -> RwLockWriteGuard<'_, T> {
39    lock.write().unwrap_or_else(PoisonError::into_inner)
40}