diff options
| author | Mica White <botahamec@outlook.com> | 2024-03-07 11:23:10 -0500 |
|---|---|---|
| committer | Mica White <botahamec@outlook.com> | 2024-03-07 11:23:10 -0500 |
| commit | 56c6999e79dbc48f14ee5fb322828f79fc478d15 (patch) | |
| tree | 831dcec85ab364efbddc9ba0b54b81161a99cddb /src | |
| parent | 83fdb1048f6e5c10200110d1810a48e7a34c6cf4 (diff) | |
reorganization
Diffstat (limited to 'src')
| -rw-r--r-- | src/guard.rs | 171 | ||||
| -rw-r--r-- | src/lib.rs | 5 | ||||
| -rw-r--r-- | src/lockable.rs | 170 |
3 files changed, 176 insertions, 170 deletions
diff --git a/src/guard.rs b/src/guard.rs index 6007ebe..9758e63 100644 --- a/src/guard.rs +++ b/src/guard.rs @@ -1,173 +1,6 @@ -use std::{ - mem::MaybeUninit, - ops::{Deref, DerefMut}, -}; +use std::ops::{Deref, DerefMut}; -use crate::{ - mutex::{Mutex, MutexRef, RawMutex}, - ThreadKey, -}; - -mod sealed { - #[allow(clippy::wildcard_imports)] - use super::*; - pub trait Sealed {} - impl<'a, T, R: RawMutex + 'a> Sealed for Mutex<T, R> {} - impl<T: Sealed> Sealed for &T {} - impl<T: Sealed> Sealed for &mut T {} - impl<'a, A: Lockable<'a>, B: Lockable<'a>> Sealed for (A, B) {} - impl<'a, T: Lockable<'a>, const N: usize> Sealed for [T; N] {} -} - -pub trait Lockable<'a>: sealed::Sealed { - /// The output of the lock - type Output; - - /// Blocks until the lock is acquired - /// - /// # Safety - /// - /// It is undefined behavior to: - /// * Use this without ownership of the [`ThreadKey`], which should last as - /// long as the return value is alive. - /// * Call this on multiple locks without unlocking first. - unsafe fn lock(&'a self) -> Self::Output; - - /// Attempt to lock without blocking. - /// - /// Returns `Ok` if successful, `None` otherwise. - /// - /// # Safety - /// - /// It is undefined behavior to use this without ownership of the - /// [`ThreadKey`], which should last as long as the return value is alive. - unsafe fn try_lock(&'a self) -> Option<Self::Output>; - - /// Release the lock - fn unlock(guard: Self::Output); -} - -impl<'a, T: Lockable<'a>> Lockable<'a> for &T { - type Output = T::Output; - - unsafe fn lock(&'a self) -> Self::Output { - (*self).lock() - } - - unsafe fn try_lock(&'a self) -> Option<Self::Output> { - (*self).try_lock() - } - - #[allow(clippy::semicolon_if_nothing_returned)] - fn unlock(guard: Self::Output) { - T::unlock(guard) - } -} - -impl<'a, T: Lockable<'a>> Lockable<'a> for &mut T { - type Output = T::Output; - - unsafe fn lock(&'a self) -> Self::Output { - (**self).lock() - } - - unsafe fn try_lock(&'a self) -> Option<Self::Output> { - (**self).try_lock() - } - - #[allow(clippy::semicolon_if_nothing_returned)] - fn unlock(guard: Self::Output) { - T::unlock(guard) - } -} - -impl<'a, T: 'a, R: RawMutex + 'a> Lockable<'a> for Mutex<T, R> { - type Output = MutexRef<'a, T, R>; - - unsafe fn lock(&'a self) -> Self::Output { - self.lock_ref() - } - - unsafe fn try_lock(&'a self) -> Option<Self::Output> { - self.try_lock_ref() - } - - fn unlock(guard: Self::Output) { - drop(guard); - } -} - -impl<'a, A: Lockable<'a>, B: Lockable<'a>> Lockable<'a> for (A, B) { - type Output = (A::Output, B::Output); - - unsafe fn lock(&'a self) -> Self::Output { - loop { - let lock1 = self.0.lock(); - match self.1.try_lock() { - Some(lock2) => return (lock1, lock2), - None => A::unlock(lock1), - } - } - } - - unsafe fn try_lock(&'a self) -> Option<Self::Output> { - self.0.try_lock().and_then(|guard1| { - if let Some(guard2) = self.1.try_lock() { - Some((guard1, guard2)) - } else { - A::unlock(guard1); - None - } - }) - } - - fn unlock(guard: Self::Output) { - A::unlock(guard.0); - B::unlock(guard.1); - } -} - -impl<'a, T: Lockable<'a>, const N: usize> Lockable<'a> for [T; N] { - type Output = [T::Output; N]; - - unsafe fn lock(&'a self) -> Self::Output { - loop { - if let Some(guard) = self.try_lock() { - return guard; - } - } - } - - unsafe fn try_lock(&'a self) -> Option<Self::Output> { - unsafe fn unlock_partial<'a, T: Lockable<'a>, const N: usize>( - guards: [MaybeUninit<T::Output>; N], - upto: usize, - ) { - for (i, guard) in guards.into_iter().enumerate() { - if i == upto { - break; - } - T::unlock(guard.assume_init()); - } - } - - let mut outputs = MaybeUninit::<[MaybeUninit<T::Output>; N]>::uninit().assume_init(); - for i in 0..N { - if let Some(guard) = self[i].try_lock() { - outputs[i].write(guard) - } else { - unlock_partial::<T, N>(outputs, i); - return None; - }; - } - - Some(outputs.map(|mu| mu.assume_init())) - } - - fn unlock(guard: Self::Output) { - guard.map(T::unlock); - } -} +use crate::{lockable::Lockable, ThreadKey}; pub struct LockGuard<'a, L: Lockable<'a>> { guard: L::Output, @@ -12,11 +12,14 @@ use thread_local::ThreadLocal; mod guard; mod lock; +mod lockable; pub mod mutex; use lock::{Key, Lock}; -pub use guard::{LockGuard, Lockable}; +pub use guard::LockGuard; +pub use lockable::Lockable; +pub use mutex::Mutex; static KEY: Lazy<ThreadLocal<Lock>> = Lazy::new(ThreadLocal::new); diff --git a/src/lockable.rs b/src/lockable.rs new file mode 100644 index 0000000..9b754a3 --- /dev/null +++ b/src/lockable.rs @@ -0,0 +1,170 @@ +use std::mem::MaybeUninit; + +use crate::mutex::{Mutex, MutexRef, RawMutex}; + +mod sealed { + #[allow(clippy::wildcard_imports)] + use super::*; + pub trait Sealed {} + impl<'a, T, R: RawMutex + 'a> Sealed for Mutex<T, R> {} + impl<T: Sealed> Sealed for &T {} + impl<T: Sealed> Sealed for &mut T {} + impl<'a, A: Lockable<'a>, B: Lockable<'a>> Sealed for (A, B) {} + impl<'a, T: Lockable<'a>, const N: usize> Sealed for [T; N] {} +} + +/// A type that may be locked and unlocked +/// +/// # Safety +/// +/// A deadlock must never occur. The `unlock` method must correctly unlock the +/// data. +pub unsafe trait Lockable<'a>: sealed::Sealed { + /// The output of the lock + type Output; + + /// Blocks until the lock is acquired + /// + /// # Safety + /// + /// It is undefined behavior to: + /// * Use this without ownership of the [`ThreadKey`], which should last as + /// long as the return value is alive. + /// * Call this on multiple locks without unlocking first. + unsafe fn lock(&'a self) -> Self::Output; + + /// Attempt to lock without blocking. + /// + /// Returns `Ok` if successful, `None` otherwise. + /// + /// # Safety + /// + /// It is undefined behavior to use this without ownership of the + /// [`ThreadKey`], which should last as long as the return value is alive. + unsafe fn try_lock(&'a self) -> Option<Self::Output>; + + /// Release the lock + fn unlock(guard: Self::Output); +} + +unsafe impl<'a, T: Lockable<'a>> Lockable<'a> for &T { + type Output = T::Output; + + unsafe fn lock(&'a self) -> Self::Output { + (*self).lock() + } + + unsafe fn try_lock(&'a self) -> Option<Self::Output> { + (*self).try_lock() + } + + #[allow(clippy::semicolon_if_nothing_returned)] + fn unlock(guard: Self::Output) { + T::unlock(guard) + } +} + +unsafe impl<'a, T: Lockable<'a>> Lockable<'a> for &mut T { + type Output = T::Output; + + unsafe fn lock(&'a self) -> Self::Output { + (**self).lock() + } + + unsafe fn try_lock(&'a self) -> Option<Self::Output> { + (**self).try_lock() + } + + #[allow(clippy::semicolon_if_nothing_returned)] + fn unlock(guard: Self::Output) { + T::unlock(guard) + } +} + +unsafe impl<'a, T: 'a, R: RawMutex + 'a> Lockable<'a> for Mutex<T, R> { + type Output = MutexRef<'a, T, R>; + + unsafe fn lock(&'a self) -> Self::Output { + self.lock_ref() + } + + unsafe fn try_lock(&'a self) -> Option<Self::Output> { + self.try_lock_ref() + } + + fn unlock(guard: Self::Output) { + drop(guard); + } +} + +unsafe impl<'a, A: Lockable<'a>, B: Lockable<'a>> Lockable<'a> for (A, B) { + type Output = (A::Output, B::Output); + + unsafe fn lock(&'a self) -> Self::Output { + loop { + let lock1 = self.0.lock(); + match self.1.try_lock() { + Some(lock2) => return (lock1, lock2), + None => A::unlock(lock1), + } + } + } + + unsafe fn try_lock(&'a self) -> Option<Self::Output> { + self.0.try_lock().and_then(|guard1| { + if let Some(guard2) = self.1.try_lock() { + Some((guard1, guard2)) + } else { + A::unlock(guard1); + None + } + }) + } + + fn unlock(guard: Self::Output) { + A::unlock(guard.0); + B::unlock(guard.1); + } +} + +unsafe impl<'a, T: Lockable<'a>, const N: usize> Lockable<'a> for [T; N] { + type Output = [T::Output; N]; + + unsafe fn lock(&'a self) -> Self::Output { + loop { + if let Some(guard) = self.try_lock() { + return guard; + } + } + } + + unsafe fn try_lock(&'a self) -> Option<Self::Output> { + unsafe fn unlock_partial<'a, T: Lockable<'a>, const N: usize>( + guards: [MaybeUninit<T::Output>; N], + upto: usize, + ) { + for (i, guard) in guards.into_iter().enumerate() { + if i == upto { + break; + } + T::unlock(guard.assume_init()); + } + } + + let mut outputs = MaybeUninit::<[MaybeUninit<T::Output>; N]>::uninit().assume_init(); + for i in 0..N { + if let Some(guard) = self[i].try_lock() { + outputs[i].write(guard) + } else { + unlock_partial::<T, N>(outputs, i); + return None; + }; + } + + Some(outputs.map(|mu| mu.assume_init())) + } + + fn unlock(guard: Self::Output) { + guard.map(T::unlock); + } +} |
