use std::panic::{RefUnwindSafe, UnwindSafe}; use crate::handle_unwind::handle_unwind; use crate::lockable::{ Lockable, LockableGetMut, LockableIntoInner, OwnedLockable, RawLock, Sharable, }; use crate::{Keyable, ThreadKey}; use super::{ PoisonError, PoisonFlag, PoisonGuard, PoisonRef, PoisonResult, Poisonable, TryLockPoisonableError, TryLockPoisonableResult, }; unsafe impl RawLock for Poisonable { #[mutants::skip] // this should never run #[cfg(not(tarpaulin_include))] fn poison(&self) { self.inner.poison() } unsafe fn raw_write(&self) { self.inner.raw_write() } unsafe fn raw_try_write(&self) -> bool { self.inner.raw_try_write() } unsafe fn raw_unlock_write(&self) { self.inner.raw_unlock_write() } unsafe fn raw_read(&self) { self.inner.raw_read() } unsafe fn raw_try_read(&self) -> bool { self.inner.raw_try_read() } unsafe fn raw_unlock_read(&self) { self.inner.raw_unlock_read() } } unsafe impl Lockable for Poisonable { type Guard<'g> = PoisonResult>> where Self: 'g; type DataMut<'a> = PoisonResult> where Self: 'a; fn get_ptrs<'a>(&'a self, ptrs: &mut Vec<&'a dyn RawLock>) { self.inner.get_ptrs(ptrs) } unsafe fn guard(&self) -> Self::Guard<'_> { let ref_guard = PoisonRef::new(&self.poisoned, self.inner.guard()); if self.is_poisoned() { Err(PoisonError::new(ref_guard)) } else { Ok(ref_guard) } } unsafe fn data_mut(&self) -> Self::DataMut<'_> { if self.is_poisoned() { Err(PoisonError::new(self.inner.data_mut())) } else { Ok(self.inner.data_mut()) } } } unsafe impl Sharable for Poisonable { type ReadGuard<'g> = PoisonResult>> where Self: 'g; type DataRef<'a> = PoisonResult> where Self: 'a; unsafe fn read_guard(&self) -> Self::ReadGuard<'_> { let ref_guard = PoisonRef::new(&self.poisoned, self.inner.read_guard()); if self.is_poisoned() { Err(PoisonError::new(ref_guard)) } else { Ok(ref_guard) } } unsafe fn data_ref(&self) -> Self::DataRef<'_> { if self.is_poisoned() { Err(PoisonError::new(self.inner.data_ref())) } else { Ok(self.inner.data_ref()) } } } unsafe impl OwnedLockable for Poisonable {} // AsMut won't work here because we don't strictly return a &mut T // LockableGetMut is the next best thing impl LockableGetMut for Poisonable { type Inner<'a> = PoisonResult> where Self: 'a; fn get_mut(&mut self) -> Self::Inner<'_> { if self.is_poisoned() { Err(PoisonError::new(self.inner.get_mut())) } else { Ok(self.inner.get_mut()) } } } impl LockableIntoInner for Poisonable { type Inner = PoisonResult; fn into_inner(self) -> Self::Inner { if self.is_poisoned() { Err(PoisonError::new(self.inner.into_inner())) } else { Ok(self.inner.into_inner()) } } } impl From for Poisonable { fn from(value: L) -> Self { Self::new(value) } } impl Poisonable { /// Creates a new `Poisonable` /// /// # Examples /// /// ``` /// use happylock::{Mutex, Poisonable}; /// /// let mutex = Poisonable::new(Mutex::new(0)); /// ``` pub const fn new(value: L) -> Self { Self { inner: value, poisoned: PoisonFlag::new(), } } /// Determines whether the `Poisonable` is poisoned. /// /// If another thread is active, the `Poisonable` can still become poisoned at /// any time. You should not trust a `false` value for program correctness /// without additional synchronization. /// /// # Examples /// /// ``` /// use std::thread; /// /// use happylock::{Mutex, Poisonable, ThreadKey}; /// /// let mutex = Poisonable::new(Mutex::new(0)); /// /// thread::scope(|s| { /// let r = s.spawn(|| { /// let key = ThreadKey::get().unwrap(); /// let _lock = mutex.lock(key).unwrap(); /// panic!(); // the mutex gets poisoned /// }).join(); /// }); /// /// assert_eq!(mutex.is_poisoned(), true); /// ``` pub fn is_poisoned(&self) -> bool { self.poisoned.is_poisoned() } /// Clear the poisoned state from a lock. /// /// If the lock is poisoned, it will remain poisoned until this function /// is called. This allows recovering from a poisoned state and marking /// that it has recovered. For example, if the value is overwritten by a /// known-good value, then the lock can be marked as un-poisoned. Or /// possibly, the value could by inspected to determine if it is in a /// consistent state, and if so the poison is removed. /// /// # Examples /// /// ``` /// use std::thread; /// /// use happylock::{Mutex, Poisonable, ThreadKey}; /// /// let mutex = Poisonable::new(Mutex::new(0)); /// /// thread::scope(|s| { /// let r = s.spawn(|| { /// let key = ThreadKey::get().unwrap(); /// let _lock = mutex.lock(key).unwrap(); /// panic!(); // the mutex gets poisoned /// }).join(); /// }); /// /// assert_eq!(mutex.is_poisoned(), true); /// /// let key = ThreadKey::get().unwrap(); /// let x = mutex.lock(key).unwrap_or_else(|mut e| { /// **e.get_mut() = 1; /// mutex.clear_poison(); /// e.into_inner() /// }); /// /// assert_eq!(mutex.is_poisoned(), false); /// assert_eq!(*x, 1); /// ``` pub fn clear_poison(&self) { self.poisoned.clear_poison() } /// Consumes this `Poisonable`, returning the underlying lock. /// /// # Errors /// /// If another user of this lock panicked while holding the lock, then this /// call will return an error instead. /// /// # Examples /// /// ``` /// use happylock::{Mutex, Poisonable}; /// /// let mutex = Poisonable::new(Mutex::new(0)); /// assert_eq!(mutex.into_child().unwrap().into_inner(), 0); /// ``` pub fn into_child(self) -> PoisonResult { if self.is_poisoned() { Err(PoisonError::new(self.inner)) } else { Ok(self.inner) } } /// Returns a mutable reference to the underlying lock. /// /// # Errors /// /// If another user of this lock panicked while holding the lock, then /// this call will return an error instead. /// /// # Examples /// /// ``` /// use happylock::{Mutex, Poisonable, ThreadKey}; /// /// let key = ThreadKey::get().unwrap(); /// let mut mutex = Poisonable::new(Mutex::new(0)); /// *mutex.child_mut().unwrap().as_mut() = 10; /// assert_eq!(*mutex.lock(key).unwrap(), 10); /// ``` pub fn child_mut(&mut self) -> PoisonResult<&mut L> { if self.is_poisoned() { Err(PoisonError::new(&mut self.inner)) } else { Ok(&mut self.inner) } } // NOTE: `child_ref` isn't implemented because it would make this not `RefUnwindSafe` } impl Poisonable { /// Creates a guard for the poisonable, without locking it unsafe fn guard(&self, key: ThreadKey) -> PoisonResult>> { let guard = PoisonGuard { guard: PoisonRef::new(&self.poisoned, self.inner.guard()), key, }; if self.is_poisoned() { return Err(PoisonError::new(guard)); } Ok(guard) } } impl Poisonable { /// Acquires an exclusive lock, blocking until it is safe to do so, and then /// unlocks after the provided function returns. /// /// This function is useful to ensure that a `Poisonable` is never /// accidentally locked forever by leaking the guard. Even if the function /// panics, this function will gracefully notice the panic, poison the lock, /// and unlock. /// /// If the lock is poisoned, then an error will be passed into the provided /// function. /// /// # Panics /// /// This function will panic if the provided function also panics. However, /// `Poisonable` will be safely poisoned and any subsequent calls will pass /// `Err` into the given function. /// /// # Example /// /// ``` /// use happylock::{Poisonable, ThreadKey, RwLock}; /// /// let mut key = ThreadKey::get().unwrap(); /// let lock = Poisonable::new(RwLock::new(42)); /// /// let x = lock.scoped_lock(&mut key, |number| { /// *number.unwrap() /// }); /// assert_eq!(x, 42); /// ``` pub fn scoped_lock<'a, R>( &'a self, key: impl Keyable, f: impl FnOnce(::DataMut<'a>) -> R, ) -> R { unsafe { // safety: we have the thread key self.raw_write(); // safety: the data was just locked let r = handle_unwind( || f(self.data_mut()), || { self.poisoned.poison(); self.raw_unlock_write(); }, ); // safety: the collection is still locked self.raw_unlock_write(); drop(key); // ensure the key stays alive long enough r } } /// Attempts to acquire an exclusive lock to the `Poisonable` without /// blocking, and then unlocks it once the provided function returns. /// /// This function implements a non-blocking variant of [`scoped_lock`]. /// Unlike `scoped_lock`, if the `Poisonable` is not already unlocked, then /// the provided function will not run, and the given [`Keyable`] is returned. /// This method does not provide any guarantees with respect to the ordering /// of whether contentious readers or writers will acquire the lock first. /// /// If the lock is poisoned, then an error will be passed into the provided /// function. /// /// # Errors /// /// If the `Poisonable` is already locked, then the provided function will not /// run. `Err` is returned with the given key. /// /// # Panics /// /// If the provided function panics, then the panic will be bubbled up and /// rethrown. The `Poisonable` will also be gracefully unlocked, allowing the /// `Poisonable` to be locked again. /// /// # Examples /// /// ``` /// use happylock::{Poisonable, RwLock, ThreadKey}; /// /// let mut key = ThreadKey::get().unwrap(); /// let lock = Poisonable::new(RwLock::new(42)); /// /// let result = lock.scoped_try_lock(&mut key, |num| { /// *num.unwrap() /// }); /// /// match result { /// Ok(val) => println!("The number is {val}"), /// Err(_) => unreachable!(), /// } /// ``` pub fn scoped_try_lock<'a, Key: Keyable, R>( &'a self, key: Key, f: impl FnOnce(::DataMut<'a>) -> R, ) -> Result { unsafe { // safety: we have the thread key if !self.raw_try_write() { return Err(key); } // safety: we just locked the collection let r = handle_unwind( || f(self.data_mut()), || { self.poisoned.poison(); self.raw_unlock_write(); }, ); // safety: the collection is still locked self.raw_unlock_write(); drop(key); // ensures the key stays valid long enough Ok(r) } } /// Acquires the lock, blocking the current thread until it is ok to do so. /// /// This function will block the current thread until it is available to /// acquire the lock. Upon returning, the thread is the only thread with /// the lock held. An RAII guard is returned to allow scoped unlock of the /// lock. When the guard goes out of scope, the mutex will be unlocked. /// /// # Errors /// /// If another use of this lock panicked while holding the mutex, then /// this call will return an error once the mutex is acquired. /// /// # Examples /// /// ``` /// use std::thread; /// /// use happylock::{Mutex, Poisonable, ThreadKey}; /// /// let mutex = Poisonable::new(Mutex::new(0)); /// /// thread::scope(|s| { /// let r = s.spawn(|| { /// let key = ThreadKey::get().unwrap(); /// *mutex.lock(key).unwrap() = 10; /// }).join(); /// }); /// /// let key = ThreadKey::get().unwrap(); /// assert_eq!(*mutex.lock(key).unwrap(), 10); /// ``` pub fn lock(&self, key: ThreadKey) -> PoisonResult>> { unsafe { self.inner.raw_write(); self.guard(key) } } /// Attempts to acquire this lock. /// /// If the lock could not be acquired at this time, then [`Err`] is /// returned. Otherwise, an RAII guard is returned. The lock will be /// unlocked when the guard is dropped. /// /// This function does not block. /// /// # Errors /// /// If another user of this lock panicked while holding the lock, then this /// call will return the [`Poisoned`] error if the lock would otherwise be /// acquired. /// /// If the lock could not be acquired because it is already locked, then /// this call will return the [`WouldBlock`] error. /// /// # Examples /// /// ``` /// use std::thread; /// /// use happylock::{Mutex, Poisonable, ThreadKey}; /// /// let mutex = Poisonable::new(Mutex::new(0)); /// /// thread::scope(|s| { /// s.spawn(|| { /// let key = ThreadKey::get().unwrap(); /// let mut lock = mutex.try_lock(key); /// if let Ok(mut mutex) = lock { /// *mutex = 10; /// } else { /// println!("try_lock failed"); /// } /// }); /// }); /// /// let key = ThreadKey::get().unwrap(); /// assert_eq!(*mutex.lock(key).unwrap(), 10); /// ``` /// /// [`Poisoned`]: `TryLockPoisonableError::Poisoned` /// [`WouldBlock`]: `TryLockPoisonableError::WouldBlock` pub fn try_lock(&self, key: ThreadKey) -> TryLockPoisonableResult<'_, L::Guard<'_>> { unsafe { if self.inner.raw_try_write() { Ok(self.guard(key)?) } else { Err(TryLockPoisonableError::WouldBlock(key)) } } } /// Consumes the [`PoisonGuard`], and consequently unlocks its `Poisonable`. /// /// This function is equivalent to calling [`drop`] on the guard, except that /// it returns the key that was used to create it. Alternatively, the guard /// will be automatically dropped when it goes out of scope. /// /// # Examples /// /// ``` /// use happylock::{ThreadKey, Mutex, Poisonable}; /// /// let key = ThreadKey::get().unwrap(); /// let mutex = Poisonable::new(Mutex::new(0)); /// /// let mut guard = mutex.lock(key).unwrap(); /// *guard += 20; /// /// let key = Poisonable::>::unlock(guard); /// ``` pub fn unlock<'flag>(guard: PoisonGuard<'flag, L::Guard<'flag>>) -> ThreadKey { drop(guard.guard); guard.key } } impl Poisonable { unsafe fn read_guard(&self, key: ThreadKey) -> PoisonResult>> { let guard = PoisonGuard { guard: PoisonRef::new(&self.poisoned, self.inner.read_guard()), key, }; if self.is_poisoned() { return Err(PoisonError::new(guard)); } Ok(guard) } /// Acquires a shared lock, blocking until it is safe to do so, and then /// unlocks after the provided function returns. /// /// This function is useful to ensure that a `Poisonable` is never /// accidentally locked forever by leaking the `ReadGuard`. Even if the /// function panics, this function will gracefully notice the panic, and /// unlock. This function provides no guarantees with respect to the ordering /// of whether contentious readers or writers will acquire the lock first. /// /// If the lock is poisoned, then an error will be passed into the provided /// into the provided function. /// /// # Panics /// /// This function will panic if the provided function also panics. However, /// `Poisonable` will be safely unlocked in this case, allowing the /// `Poisonable` to be locked again later. /// /// # Example /// /// ``` /// use happylock::{Poisonable, ThreadKey, RwLock}; /// /// let mut key = ThreadKey::get().unwrap(); /// let lock = Poisonable::new(RwLock::new(42)); /// /// let x = lock.scoped_read(&mut key, |number| { /// *number.unwrap() /// }); /// assert_eq!(x, 42); /// ``` pub fn scoped_read<'a, R>( &'a self, key: impl Keyable, f: impl FnOnce(::DataRef<'a>) -> R, ) -> R { unsafe { // safety: we have the thread key self.raw_read(); // safety: the data was just locked let r = handle_unwind( || f(self.data_ref()), || { self.poisoned.poison(); self.raw_unlock_read(); }, ); // safety: the collection is still locked self.raw_unlock_read(); drop(key); // ensure the key stays alive long enough r } } /// Attempts to acquire a shared lock to the `Poisonable` without blocking, /// and then unlocks it once the provided function returns. /// /// This function implements a non-blocking variant of [`scoped_read`]. /// Unlike `scoped_read`, if the `Poisonable` is exclusively locked, then the /// provided function will not run, and the given [`Keyable`] is returned. /// This method provides no guarantees with respect to the ordering of whether /// contentious readers of writers will acquire the lock first. /// /// If the lock is poisoned, then an error will be passed into the provided /// function. /// /// # Errors /// /// If the `Poisonable` is already exclusively locked, then the provided /// function will not run. `Err` is returned with the given key. /// /// # Panics /// /// If the provided function panics, then the panic will be bubbled up and /// rethrown. The `Poisonable` will also be gracefully unlocked, allowing the /// `Poisonable` to be locked again. /// /// # Examples /// /// ``` /// use happylock::{Poisonable, RwLock, ThreadKey}; /// /// let mut key = ThreadKey::get().unwrap(); /// let lock = Poisonable::new(RwLock::new(42)); /// /// let result = lock.scoped_try_read(&mut key, |num| { /// *num.unwrap() /// }); /// /// match result { /// Ok(val) => println!("The number is {val}"), /// Err(_) => unreachable!(), /// } /// ``` pub fn scoped_try_read<'a, Key: Keyable, R>( &'a self, key: Key, f: impl FnOnce(::DataRef<'a>) -> R, ) -> Result { unsafe { // safety: we have the thread key if !self.raw_try_read() { return Err(key); } // safety: we just locked the collection let r = handle_unwind( || f(self.data_ref()), || { self.poisoned.poison(); self.raw_unlock_read(); }, ); // safety: the collection is still locked self.raw_unlock_read(); drop(key); // ensures the key stays valid long enough Ok(r) } } /// Locks with shared read access, blocking the current thread until it can /// be acquired. /// /// This function will block the current thread until there are no writers /// which hold the lock. This method does not provide any guarantee with /// respect to the ordering of contentious readers or writers will acquire /// the lock. /// /// # Errors /// /// This function will return an error if the `Poisonable` is poisoned. A /// `Poisonable` is poisoned whenever a thread panics while holding a lock. /// The failure will occur immediately after the lock has been acquired. The /// acquired lock guard will be contained in the returned error. /// /// # Examples /// /// ``` /// use std::thread; /// /// use happylock::{RwLock, Poisonable, ThreadKey}; /// /// let key = ThreadKey::get().unwrap(); /// let lock = Poisonable::new(RwLock::new(0)); /// /// let n = lock.read(key).unwrap(); /// assert_eq!(*n, 0); /// /// thread::scope(|s| { /// s.spawn(|| { /// let key = ThreadKey::get().unwrap(); /// let r = lock.read(key); /// assert!(r.is_ok()); /// }); /// }); /// ``` pub fn read(&self, key: ThreadKey) -> PoisonResult>> { unsafe { self.inner.raw_read(); self.read_guard(key) } } /// Attempts to acquire the lock with shared read access, without blocking the /// thread. /// /// If the access could not be granted at this time, then `Err` is returned. /// Otherwise, an RAII guard is returned which will release the shared access /// when it is dropped. /// /// This function does not provide any guarantees with respect to the ordering /// of whether contentious readers or writers will acquire the lock first. /// /// # Errors /// /// This function will return the [`Poisoned`] error if the lock is /// poisoned. A [`Poisonable`] is poisoned whenever a thread panics while /// holding a lock. `Poisoned` will only be returned if the lock would have /// otherwise been acquired. /// /// This function will return the [`WouldBlock`] error if the lock could /// not be acquired because it was already locked exclusively. /// /// # Examples /// /// ``` /// use happylock::{Poisonable, RwLock, ThreadKey}; /// /// let key = ThreadKey::get().unwrap(); /// let lock = Poisonable::new(RwLock::new(1)); /// /// match lock.try_read(key) { /// Ok(n) => assert_eq!(*n, 1), /// Err(_) => unreachable!(), /// }; /// ``` /// /// [`Poisoned`]: `TryLockPoisonableError::Poisoned` /// [`WouldBlock`]: `TryLockPoisonableError::WouldBlock` // TODO don't poison when holding shared lock pub fn try_read(&self, key: ThreadKey) -> TryLockPoisonableResult<'_, L::ReadGuard<'_>> { unsafe { if self.inner.raw_try_read() { Ok(self.read_guard(key)?) } else { Err(TryLockPoisonableError::WouldBlock(key)) } } } /// Consumes the [`PoisonGuard`], and consequently unlocks its `Poisonable`. /// /// This function is equivalent to calling [`drop`] on the guard, except that /// it returns the key that was used to create it. Alternatively, the guard /// will be automatically dropped when it goes out of scope. /// /// # Examples /// /// ``` /// use happylock::{ThreadKey, RwLock, Poisonable}; /// /// let key = ThreadKey::get().unwrap(); /// let lock = Poisonable::new(RwLock::new(20)); /// /// let mut guard = lock.read(key).unwrap(); /// assert_eq!(*guard, 20); /// /// let key = Poisonable::>::unlock_read(guard); /// ``` pub fn unlock_read<'flag>(guard: PoisonGuard<'flag, L::ReadGuard<'flag>>) -> ThreadKey { drop(guard.guard); guard.key } } impl Poisonable { /// Consumes this `Poisonable`, returning the underlying data. /// /// # Errors /// /// If another user of this lock panicked while holding the lock, then this /// call will return an error instead. A `Poisonable` is poisoned whenever a /// thread panics while holding a lock. /// /// # Examples /// /// ``` /// use happylock::{Mutex, Poisonable}; /// /// let mutex = Poisonable::new(Mutex::new(0)); /// assert_eq!(mutex.into_inner().unwrap(), 0); /// ``` pub fn into_inner(self) -> PoisonResult { LockableIntoInner::into_inner(self) } } impl Poisonable { /// Returns a mutable reference to the underlying data. /// /// Since this call borrows the `Poisonable` mutably, no actual locking /// needs to take place - the mutable borrow statically guarantees no locks /// exist. /// /// # Errors /// /// If another user of this lock panicked while holding the lock, then /// this call will return an error instead. A `Poisonable` is poisoned /// whenever a thread panics while holding a lock. /// /// # Examples /// /// ``` /// use happylock::{Mutex, Poisonable, ThreadKey}; /// /// let key = ThreadKey::get().unwrap(); /// let mut mutex = Poisonable::new(Mutex::new(0)); /// *mutex.get_mut().unwrap() = 10; /// assert_eq!(*mutex.lock(key).unwrap(), 10); /// ``` pub fn get_mut(&mut self) -> PoisonResult> { LockableGetMut::get_mut(self) } } impl RefUnwindSafe for Poisonable {} impl UnwindSafe for Poisonable {}