diff options
| author | Mica White <botahamec@gmail.com> | 2024-07-17 16:37:21 -0400 |
|---|---|---|
| committer | Mica White <botahamec@gmail.com> | 2024-07-21 12:55:32 -0400 |
| commit | bd64ff98530ea5f92ce528009d65203f0f6676fe (patch) | |
| tree | 27e6c6a2153e3a9c4ecf4087f473b89a01b25033 /src/poisonable | |
| parent | 32f972a26a0066291873445088718deec3ed4233 (diff) | |
Create Poisonable API
Diffstat (limited to 'src/poisonable')
| -rw-r--r-- | src/poisonable/error.rs | 47 | ||||
| -rw-r--r-- | src/poisonable/flag.rs | 36 | ||||
| -rw-r--r-- | src/poisonable/guard.rs | 92 | ||||
| -rw-r--r-- | src/poisonable/poisonable.rs | 139 |
4 files changed, 314 insertions, 0 deletions
diff --git a/src/poisonable/error.rs b/src/poisonable/error.rs new file mode 100644 index 0000000..98a167c --- /dev/null +++ b/src/poisonable/error.rs @@ -0,0 +1,47 @@ +use core::fmt; +use std::error::Error; + +use super::{PoisonError, PoisonGuard, TryLockPoisonableError}; + +impl<Guard> fmt::Debug for PoisonError<Guard> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("PoisonError").finish_non_exhaustive() + } +} + +impl<Guard> fmt::Display for PoisonError<Guard> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + "poisoned lock: another task failed inside".fmt(f) + } +} + +impl<Guard> Error for PoisonError<Guard> {} + +impl<'flag, 'key, G, Key> fmt::Debug for TryLockPoisonableError<'flag, 'key, G, Key> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + Self::Poisoned(..) => "Poisoned(..)".fmt(f), + Self::WouldBlock(_) => "WouldBlock".fmt(f), + } + } +} + +impl<'flag, 'key, G, Key> fmt::Display for TryLockPoisonableError<'flag, 'key, G, Key> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + Self::Poisoned(..) => "poisoned lock: another task failed inside", + Self::WouldBlock(_) => "try_lock failed because the operation would block", + } + .fmt(f) + } +} + +impl<'flag, 'key, G, Key> Error for TryLockPoisonableError<'flag, 'key, G, Key> {} + +impl<'flag, 'key, G, Key> From<PoisonError<PoisonGuard<'flag, 'key, G, Key>>> + for TryLockPoisonableError<'flag, 'key, G, Key> +{ + fn from(value: PoisonError<PoisonGuard<'flag, 'key, G, Key>>) -> Self { + Self::Poisoned(value) + } +} diff --git a/src/poisonable/flag.rs b/src/poisonable/flag.rs new file mode 100644 index 0000000..99e7e4f --- /dev/null +++ b/src/poisonable/flag.rs @@ -0,0 +1,36 @@ +use std::sync::atomic::AtomicBool; +use std::sync::atomic::Ordering::Relaxed; + +use super::PoisonFlag; + +impl PoisonFlag { + #[cfg(panic = "unwind")] + pub const fn new() -> Self { + Self(AtomicBool::new(false)) + } + + #[cfg(not(panic = "unwind"))] + pub const fn new() -> Self { + Self() + } + + #[cfg(panic = "unwind")] + pub fn is_poisoned(&self) -> bool { + self.0.load(Relaxed) + } + + #[cfg(not(panic = "unwind"))] + pub fn is_poisoned(&self) -> bool { + false + } + + #[cfg(panic = "unwind")] + pub fn clear_poison(&self) { + self.0.store(true, Relaxed) + } + + #[cfg(not(panic = "unwind"))] + pub fn clear_poison(&self) { + () + } +} diff --git a/src/poisonable/guard.rs b/src/poisonable/guard.rs new file mode 100644 index 0000000..97b0028 --- /dev/null +++ b/src/poisonable/guard.rs @@ -0,0 +1,92 @@ +use std::fmt::{Debug, Display}; +use std::ops::{Deref, DerefMut}; +use std::sync::atomic::Ordering::Relaxed; + +use crate::Keyable; + +use super::{PoisonGuard, PoisonRef}; + +impl<'flag, Guard> Drop for PoisonRef<'flag, Guard> { + fn drop(&mut self) { + #[cfg(panic = "unwind")] + if std::thread::panicking() { + self.flag.0.store(true, Relaxed); + } + } +} + +impl<'flag, Guard: Debug> Debug for PoisonRef<'flag, Guard> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + Debug::fmt(&**self, f) + } +} + +impl<'flag, Guard: Display> Display for PoisonRef<'flag, Guard> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + Display::fmt(&**self, f) + } +} + +impl<'flag, Guard> Deref for PoisonRef<'flag, Guard> { + type Target = Guard; + + fn deref(&self) -> &Self::Target { + &self.guard + } +} + +impl<'flag, Guard> DerefMut for PoisonRef<'flag, Guard> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.guard + } +} + +impl<'flag, Guard> AsRef<Guard> for PoisonRef<'flag, Guard> { + fn as_ref(&self) -> &Guard { + &self.guard + } +} + +impl<'flag, Guard> AsMut<Guard> for PoisonRef<'flag, Guard> { + fn as_mut(&mut self) -> &mut Guard { + &mut self.guard + } +} + +impl<'flag, 'key, Guard: Debug, Key: Keyable> Debug for PoisonGuard<'flag, 'key, Guard, Key> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + Debug::fmt(&**self, f) + } +} + +impl<'flag, 'key, Guard: Display, Key: Keyable> Display for PoisonGuard<'flag, 'key, Guard, Key> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + Display::fmt(&**self, f) + } +} + +impl<'flag, 'key, Guard, Key: Keyable> Deref for PoisonGuard<'flag, 'key, Guard, Key> { + type Target = Guard; + + fn deref(&self) -> &Self::Target { + &self.guard.guard + } +} + +impl<'flag, 'key, Guard, Key: Keyable> DerefMut for PoisonGuard<'flag, 'key, Guard, Key> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.guard.guard + } +} + +impl<'flag, 'key, Guard, Key: Keyable> AsRef<Guard> for PoisonGuard<'flag, 'key, Guard, Key> { + fn as_ref(&self) -> &Guard { + &self.guard.guard + } +} + +impl<'flag, 'key, Guard, Key: Keyable> AsMut<Guard> for PoisonGuard<'flag, 'key, Guard, Key> { + fn as_mut(&mut self) -> &mut Guard { + &mut self.guard.guard + } +} diff --git a/src/poisonable/poisonable.rs b/src/poisonable/poisonable.rs new file mode 100644 index 0000000..6c78346 --- /dev/null +++ b/src/poisonable/poisonable.rs @@ -0,0 +1,139 @@ +use std::marker::PhantomData; +use std::panic::{RefUnwindSafe, UnwindSafe}; + +use crate::lockable::{Lockable, RawLock}; +use crate::Keyable; + +use super::{ + PoisonError, PoisonFlag, PoisonGuard, PoisonRef, PoisonResult, Poisonable, + 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; + + 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 { + guard: self.inner.guard(), + flag: &self.poisoned, + }; + + if self.is_poisoned() { + Ok(ref_guard) + } else { + Err(PoisonError { guard: ref_guard }) + } + } + + unsafe fn read_guard(&self) -> Self::ReadGuard<'_> { + let ref_guard = PoisonRef { + guard: self.inner.read_guard(), + flag: &self.poisoned, + }; + + if self.is_poisoned() { + Ok(ref_guard) + } else { + Err(PoisonError { guard: ref_guard }) + } + } +} + +impl<L: Lockable + RawLock> From<L> for Poisonable<L> { + fn from(value: L) -> Self { + Self::new(value) + } +} + +impl<L: Lockable + RawLock> Poisonable<L> { + pub const fn new(value: L) -> Self { + Self { + inner: value, + poisoned: PoisonFlag::new(), + } + } + + unsafe fn guard<'flag, 'key, Key: Keyable + 'key>( + &'flag self, + key: Key, + ) -> PoisonResult<PoisonGuard<'flag, 'key, L::Guard<'flag>, Key>> { + let guard = PoisonGuard { + guard: PoisonRef { + guard: self.inner.guard(), + flag: &self.poisoned, + }, + key, + _phantom: PhantomData, + }; + + if self.is_poisoned() { + Ok(guard) + } else { + Err(PoisonError { guard }) + } + } + + pub fn lock<'flag, 'key, Key: Keyable + 'key>( + &'flag self, + key: Key, + ) -> PoisonResult<PoisonGuard<'flag, 'key, L::Guard<'flag>, Key>> { + unsafe { + self.inner.lock(); + self.guard(key) + } + } + + pub fn try_lock<'flag, 'key, Key: Keyable + 'key>( + &'flag self, + key: Key, + ) -> TryLockPoisonableResult<'flag, 'key, L::Guard<'flag>, Key> { + unsafe { + if self.inner.try_lock() { + Ok(self.guard(key)?) + } else { + Err(TryLockPoisonableError::WouldBlock(key)) + } + } + } + + pub fn unlock<'flag, 'key, Key: Keyable + 'key>( + guard: PoisonGuard<'flag, 'key, L::Guard<'flag>, Key>, + ) -> Key { + drop(guard.guard); + guard.key + } + + pub fn is_poisoned(&self) -> bool { + self.poisoned.is_poisoned() + } + + pub fn clear_poison(&self) { + self.poisoned.clear_poison() + } + + pub fn into_inner(self) -> PoisonResult<L> { + if self.is_poisoned() { + Err(PoisonError { guard: self.inner }) + } else { + Ok(self.inner) + } + } + + pub fn get_mut(&mut self) -> PoisonResult<&mut L> { + if self.is_poisoned() { + Err(PoisonError { + guard: &mut self.inner, + }) + } else { + Ok(&mut self.inner) + } + } +} + +impl<L: Lockable + RawLock> RefUnwindSafe for Poisonable<L> {} +impl<L: Lockable + RawLock> UnwindSafe for Poisonable<L> {} |
