From 56c6999e79dbc48f14ee5fb322828f79fc478d15 Mon Sep 17 00:00:00 2001 From: Mica White Date: Thu, 7 Mar 2024 11:23:10 -0500 Subject: reorganization --- src/lockable.rs | 170 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 src/lockable.rs (limited to 'src/lockable.rs') 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 {} + impl Sealed for &T {} + impl 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; + + /// 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).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).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 { + type Output = MutexRef<'a, T, R>; + + unsafe fn lock(&'a self) -> Self::Output { + self.lock_ref() + } + + unsafe fn try_lock(&'a self) -> Option { + 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.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 { + unsafe fn unlock_partial<'a, T: Lockable<'a>, const N: usize>( + guards: [MaybeUninit; 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; N]>::uninit().assume_init(); + for i in 0..N { + if let Some(guard) = self[i].try_lock() { + outputs[i].write(guard) + } else { + unlock_partial::(outputs, i); + return None; + }; + } + + Some(outputs.map(|mu| mu.assume_init())) + } + + fn unlock(guard: Self::Output) { + guard.map(T::unlock); + } +} -- cgit v1.2.3