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}