summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/poisonable/poisonable.rs364
1 files changed, 274 insertions, 90 deletions
diff --git a/src/poisonable/poisonable.rs b/src/poisonable/poisonable.rs
index 2c7eeff..ddd8038 100644
--- a/src/poisonable/poisonable.rs
+++ b/src/poisonable/poisonable.rs
@@ -1,7 +1,9 @@
use std::marker::PhantomData;
use std::panic::{RefUnwindSafe, UnwindSafe};
-use crate::lockable::{Lockable, LockableAsMut, LockableIntoInner, RawLock};
+use crate::lockable::{
+ Lockable, LockableAsMut, LockableIntoInner, OwnedLockable, RawLock, Sharable,
+};
use crate::Keyable;
use super::{
@@ -9,9 +11,42 @@ use super::{
TryLockPoisonableError, TryLockPoisonableResult,
};
-unsafe impl<L: Lockable + RawLock> Lockable for Poisonable<L> {
- type Guard<'g> = PoisonResult<PoisonRef<'g, L::Guard<'g>>> where Self: 'g;
- type ReadGuard<'g> = PoisonResult<PoisonRef<'g, L::ReadGuard<'g>>> where Self: 'g;
+unsafe impl<L: Lockable + RawLock> RawLock for Poisonable<L> {
+ unsafe fn lock(&self) {
+ self.inner.lock()
+ }
+
+ unsafe fn try_lock(&self) -> bool {
+ self.inner.try_lock()
+ }
+
+ unsafe fn unlock(&self) {
+ self.inner.unlock()
+ }
+
+ unsafe fn read(&self) {
+ self.inner.read()
+ }
+
+ unsafe fn try_read(&self) -> bool {
+ self.inner.try_read()
+ }
+
+ unsafe fn unlock_read(&self) {
+ self.inner.unlock_read()
+ }
+}
+
+unsafe impl<L: Lockable> Lockable for Poisonable<L> {
+ type Guard<'g>
+ = PoisonResult<PoisonRef<'g, L::Guard<'g>>>
+ where
+ Self: 'g;
+
+ type ReadGuard<'g>
+ = PoisonResult<PoisonRef<'g, L::ReadGuard<'g>>>
+ where
+ Self: 'g;
fn get_ptrs<'a>(&'a self, ptrs: &mut Vec<&'a dyn RawLock>) {
self.inner.get_ptrs(ptrs)
@@ -38,13 +73,16 @@ unsafe impl<L: Lockable + RawLock> Lockable for Poisonable<L> {
}
}
-impl<L: Lockable + RawLock> From<L> for Poisonable<L> {
+unsafe impl<L: Sharable> Sharable for Poisonable<L> {}
+unsafe impl<L: OwnedLockable> OwnedLockable for Poisonable<L> {}
+
+impl<L> From<L> for Poisonable<L> {
fn from(value: L) -> Self {
Self::new(value)
}
}
-impl<L: Lockable + RawLock> Poisonable<L> {
+impl<L> Poisonable<L> {
/// Creates a new `Poisonable`
///
/// # Examples
@@ -61,6 +99,137 @@ impl<L: Lockable + RawLock> Poisonable<L> {
}
}
+ /// Determines whether the mutex is poisoned.
+ ///
+ /// If another thread is active, the mutex can still become poisoned at any
+ /// time. You should not trust a `false` value for program correctness
+ /// without additional synchronization.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::sync::Arc;
+ /// use std::thread;
+ ///
+ /// use happylock::{Mutex, Poisonable, ThreadKey};
+ ///
+ /// let mutex = Arc::new(Poisonable::new(Mutex::new(0)));
+ /// let c_mutex = Arc::clone(&mutex);
+ ///
+ /// let _ = thread::spawn(move || {
+ /// let key = ThreadKey::get().unwrap();
+ /// let _lock = c_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::sync::Arc;
+ /// use std::thread;
+ ///
+ /// use happylock::{Mutex, Poisonable, ThreadKey};
+ ///
+ /// let mutex = Arc::new(Poisonable::new(Mutex::new(0)));
+ /// let c_mutex = Arc::clone(&mutex);
+ ///
+ /// let _ = thread::spawn(move || {
+ /// let key = ThreadKey::get().unwrap();
+ /// let _lock = c_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.
+ ///
+ /// This consumes the `Poisonable` and returns ownership of the lock, which
+ /// means that the `Poisonable` can still be `RefUnwindSafe`.
+ ///
+ /// # 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<L> {
+ if self.is_poisoned() {
+ Err(PoisonError::new(self.inner))
+ } else {
+ Ok(self.inner)
+ }
+ }
+
+ /// Returns a mutable reference to the underlying lock.
+ ///
+ /// This can be implemented while still being `RefUnwindSafe` because
+ /// it requires a mutable reference.
+ ///
+ /// # 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<L: Lockable> Poisonable<L> {
+ /// Creates a guard for the poisonable, without locking it
unsafe fn guard<'flag, 'key, Key: Keyable + 'key>(
&'flag self,
key: Key,
@@ -77,7 +246,9 @@ impl<L: Lockable + RawLock> Poisonable<L> {
Ok(guard)
}
+}
+impl<L: Lockable + RawLock> Poisonable<L> {
/// 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
@@ -88,7 +259,7 @@ impl<L: Lockable + RawLock> Poisonable<L> {
/// # Errors
///
/// If another use of this mutex panicked while holding the mutex, then
- /// this call will return an error once thr mutex is acquired.
+ /// this call will return an error once the mutex is acquired.
///
/// # Examples
///
@@ -197,12 +368,38 @@ impl<L: Lockable + RawLock> Poisonable<L> {
drop(guard.guard);
guard.key
}
+}
- /// Determines whether the mutex is poisoned.
+impl<L: Sharable + RawLock> Poisonable<L> {
+ unsafe fn read_guard<'flag, 'key, Key: Keyable + 'key>(
+ &'flag self,
+ key: Key,
+ ) -> PoisonResult<PoisonGuard<'flag, 'key, L::ReadGuard<'flag>, Key>> {
+ let guard = PoisonGuard {
+ guard: PoisonRef::new(&self.poisoned, self.inner.read_guard()),
+ key,
+ _phantom: PhantomData,
+ };
+
+ if self.is_poisoned() {
+ return Err(PoisonError::new(guard));
+ }
+
+ Ok(guard)
+ }
+
+ /// Locks with shared read access, blocking the current thread until it can
+ /// be acquired.
///
- /// If another thread is active, the mutex can still become poisoned at any
- /// time. You should not trust a `false` value for program correctness
- /// without additional synchronization.
+ /// 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
+ ///
+ /// If another use of this lock panicked while holding the lock, then
+ /// this call will return an error once the lock is acquired.
///
/// # Examples
///
@@ -210,115 +407,102 @@ impl<L: Lockable + RawLock> Poisonable<L> {
/// use std::sync::Arc;
/// use std::thread;
///
- /// use happylock::{Mutex, Poisonable, ThreadKey};
+ /// use happylock::{RwLock, Poisonable, ThreadKey};
///
- /// let mutex = Arc::new(Poisonable::new(Mutex::new(0)));
- /// let c_mutex = Arc::clone(&mutex);
+ /// let key = ThreadKey::get().unwrap();
+ /// let lock = Arc::new(Poisonable::new(RwLock::new(0)));
+ /// let c_lock = Arc::clone(&lock);
///
- /// let _ = thread::spawn(move || {
- /// let key = ThreadKey::get().unwrap();
- /// let _lock = c_mutex.lock(key).unwrap();
- /// panic!(); // the mutex gets poisoned
- /// }).join();
+ /// let n = lock.read(key).unwrap();
+ /// assert_eq!(*n, 0);
///
- /// assert_eq!(mutex.is_poisoned(), true);
+ /// thread::spawn(move || {
+ /// let key = ThreadKey::get().unwrap();
+ /// assert!(c_lock.read(key).is_ok());
+ /// }).join().expect("thread::spawn failed");
/// ```
- pub fn is_poisoned(&self) -> bool {
- self.poisoned.is_poisoned()
+ pub fn read<'flag, 'key, Key: Keyable + 'key>(
+ &'flag self,
+ key: Key,
+ ) -> PoisonResult<PoisonGuard<'flag, 'key, L::ReadGuard<'flag>, Key>> {
+ unsafe {
+ self.inner.read();
+ self.read_guard(key)
+ }
}
- /// 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.
+ /// Attempts to acquire the lock with shared read access.
///
- /// # Examples
+ /// 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.
///
- /// ```
- /// use std::sync::Arc;
- /// use std::thread;
+ /// This function does not block.
///
- /// use happylock::{Mutex, Poisonable, ThreadKey};
+ /// This function does not provide any guarantees with respect to the ordering
+ /// of whether contentious readers or writers will acquire the lock first.
///
- /// let mutex = Arc::new(Poisonable::new(Mutex::new(0)));
- /// let c_mutex = Arc::clone(&mutex);
+ /// # Errors
///
- /// let _ = thread::spawn(move || {
- /// let key = ThreadKey::get().unwrap();
- /// let _lock = c_mutex.lock(key).unwrap();
- /// panic!(); // the mutex gets poisoned
- /// }).join();
+ /// This function will return the [`Poisoned`] error if the lock is
+ /// poisoned. A [`Poisonable`] is poisoned whenever a writer panics while
+ /// holding an exclusive lock. `Poisoned` will only be returned if the lock
+ /// would have otherwise been acquired.
///
- /// assert_eq!(mutex.is_poisoned(), true);
+ /// This function will return the [`WouldBlock`] error if the lock could
+ /// not be acquired because it was already locked exclusively.
///
- /// 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()
- /// });
+ /// # Examples
///
- /// 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.
+ /// use happylock::{Poisonable, RwLock, ThreadKey};
///
- /// # Errors
- ///
- /// If another user of this lock panicked while holding the lock, then this
- /// call will return an error instead.
- ///
- /// # Examples
+ /// 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!(),
+ /// };
/// ```
- /// use happylock::{Mutex, Poisonable};
///
- /// let mutex = Poisonable::new(Mutex::new(0));
- /// assert_eq!(mutex.into_inner_lock().unwrap().into_inner(), 0);
- /// ```
- pub fn into_inner_lock(self) -> PoisonResult<L> {
- if self.is_poisoned() {
- Err(PoisonError::new(self.inner))
- } else {
- Ok(self.inner)
+ /// [`Poisoned`]: `TryLockPoisonableError::Poisoned`
+ /// [`WouldBlock`]: `TryLockPoisonableError::WouldBlock`
+ pub fn try_read<'flag, 'key, Key: Keyable + 'key>(
+ &'flag self,
+ key: Key,
+ ) -> TryLockPoisonableResult<'flag, 'key, L::ReadGuard<'flag>, Key> {
+ unsafe {
+ if self.inner.try_read() {
+ Ok(self.read_guard(key)?)
+ } else {
+ Err(TryLockPoisonableError::WouldBlock(key))
+ }
}
}
- /// 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.
+ /// Consumes the [`PoisonGuard`], and consequently unlocks its underlying lock.
///
/// # Examples
///
/// ```
- /// use happylock::{Mutex, Poisonable, ThreadKey};
+ /// use happylock::{ThreadKey, RwLock, Poisonable};
///
/// let key = ThreadKey::get().unwrap();
- /// let mut mutex = Poisonable::new(Mutex::new(0));
- /// *mutex.inner_lock_mut().unwrap().as_mut() = 10;
- /// assert_eq!(*mutex.lock(key).unwrap(), 10);
+ /// let lock = Poisonable::new(RwLock::new(0));
+ ///
+ /// let mut guard = lock.read(key).unwrap();
+ /// let key = Poisonable::<RwLock<_>>::unlock_read(guard);
/// ```
- pub fn inner_lock_mut(&mut self) -> PoisonResult<&mut L> {
- if self.is_poisoned() {
- Err(PoisonError::new(&mut self.inner))
- } else {
- Ok(&mut self.inner)
- }
+ pub fn unlock_read<'flag, 'key, Key: Keyable + 'key>(
+ guard: PoisonGuard<'flag, 'key, L::ReadGuard<'flag>, Key>,
+ ) -> Key {
+ drop(guard.guard);
+ guard.key
}
}
-impl<L: LockableIntoInner + RawLock> Poisonable<L> {
+impl<L: LockableIntoInner> Poisonable<L> {
/// Consumes this `Poisonable`, returning the underlying data.
///
/// # Errors
@@ -374,5 +558,5 @@ impl<L: LockableAsMut + RawLock> Poisonable<L> {
}
}
-impl<L: Lockable + RawLock> RefUnwindSafe for Poisonable<L> {}
-impl<L: Lockable + RawLock> UnwindSafe for Poisonable<L> {}
+impl<L: UnwindSafe> RefUnwindSafe for Poisonable<L> {}
+impl<L: UnwindSafe> UnwindSafe for Poisonable<L> {}