diff options
| author | Mica White <botahamec@outlook.com> | 2024-03-13 22:44:46 -0400 |
|---|---|---|
| committer | Mica White <botahamec@outlook.com> | 2024-03-13 22:44:46 -0400 |
| commit | 7bd236853ef5ae705328c8fdc492cf60fc6887c1 (patch) | |
| tree | ec4e9dced562fdae618b98ac704074c0ddc9cc41 /src | |
| parent | 7c6f49b6570669098938dc332a4f3e85dd3d217d (diff) | |
Lockable overhaul
Diffstat (limited to 'src')
| -rw-r--r-- | src/collection.rs | 32 | ||||
| -rw-r--r-- | src/collection/collection.rs | 180 | ||||
| -rw-r--r-- | src/collection/guard.rs | 2 | ||||
| -rw-r--r-- | src/lib.rs | 4 | ||||
| -rw-r--r-- | src/lockable.rs | 706 | ||||
| -rw-r--r-- | src/mutex/guard.rs | 6 | ||||
| -rw-r--r-- | src/mutex/mutex.rs | 26 | ||||
| -rw-r--r-- | src/rwlock/read_guard.rs | 6 | ||||
| -rw-r--r-- | src/rwlock/read_lock.rs | 6 | ||||
| -rw-r--r-- | src/rwlock/rwlock.rs | 32 | ||||
| -rw-r--r-- | src/rwlock/write_guard.rs | 6 | ||||
| -rw-r--r-- | src/rwlock/write_lock.rs | 6 |
12 files changed, 386 insertions, 626 deletions
diff --git a/src/collection.rs b/src/collection.rs index c6cbe2d..1c276a6 100644 --- a/src/collection.rs +++ b/src/collection.rs @@ -1,23 +1,41 @@ -use std::marker::PhantomData; +use std::marker::{PhantomData, PhantomPinned}; +use std::ptr::NonNull; -use crate::{key::Keyable, lockable::Lockable}; +use crate::{ + key::Keyable, + lockable::{Lock, Lockable}, +}; mod collection; mod guard; +pub struct OwnedLockCollection<L> { + data: L, +} + /// A type which can be locked. /// /// This could be a tuple of [`Lockable`] types, an array, or a `Vec`. But it -/// can be safely locked without causing a deadlock. To do this, it is very -/// important that no duplicate locks are included within. -#[derive(Debug, Clone, Copy)] -pub struct LockCollection<L> { +/// can be safely locked without causing a deadlock. +pub struct RefLockCollection<'a, L> { + locks: Vec<&'a dyn Lock>, + data: &'a L, +} + +pub struct BoxedLockCollection<L: 'static>(RefLockCollection<'static, L>); + +pub struct PinnedLockCollection<L> { + _unpin: PhantomPinned, data: L, + locks: Vec<NonNull<dyn Lock>>, } +unsafe impl<L: Send> Send for PinnedLockCollection<L> {} +unsafe impl<L: Sync> Sync for PinnedLockCollection<L> {} + /// A RAII guard for a generic [`Lockable`] type. pub struct LockGuard<'a, 'key: 'a, L: Lockable<'a>, Key: Keyable + 'key> { - guard: L::Output, + guard: L::Guard, key: Key, _phantom: PhantomData<&'key ()>, } diff --git a/src/collection/collection.rs b/src/collection/collection.rs index ae9f3f6..a8d25a5 100644 --- a/src/collection/collection.rs +++ b/src/collection/collection.rs @@ -1,63 +1,43 @@ use std::marker::PhantomData; -use crate::{key::Keyable, Lockable, OwnedLockable}; +use crate::{key::Keyable, lockable::Lock, Lockable, OwnedLockable}; -use super::{LockCollection, LockGuard}; +use super::{LockGuard, RefLockCollection}; -/// returns `true` if the list contains a duplicate -#[must_use] -fn contains_duplicates(l: &[usize]) -> bool { - for i in 0..l.len() { - for j in (i + 1)..l.len() { - if l[i] == l[j] { - return true; - } - } - } - - false +fn get_locks<'a, L: Lockable<'a> + 'a>(data: &'a L) -> Vec<&'a dyn Lock> { + let mut locks = Vec::new(); + data.get_ptrs(&mut locks); + locks.sort_by_key(|lock| std::ptr::from_ref(*lock)); + locks } -impl<'a, L: OwnedLockable<'a>> From<L> for LockCollection<L> { - fn from(value: L) -> Self { - Self::new(value) - } +/// returns `true` if the sorted list contains a duplicate +#[must_use] +fn contains_duplicates(l: &[&dyn Lock]) -> bool { + l.windows(2).any(|window| { + std::ptr::addr_eq(std::ptr::from_ref(window[0]), std::ptr::from_ref(window[1])) + }) } -impl<'a, L: Lockable<'a>> AsRef<L> for LockCollection<L> { +impl<'a, L: Lockable<'a>> AsRef<L> for RefLockCollection<'a, L> { fn as_ref(&self) -> &L { - &self.data - } -} - -impl<'a, L: Lockable<'a>> AsMut<L> for LockCollection<L> { - fn as_mut(&mut self) -> &mut L { - &mut self.data + self.data } } -impl<'a, L: Lockable<'a>> AsRef<Self> for LockCollection<L> { +impl<'a, L: Lockable<'a>> AsRef<Self> for RefLockCollection<'a, L> { fn as_ref(&self) -> &Self { self } } -impl<'a, L: Lockable<'a>> AsMut<Self> for LockCollection<L> { +impl<'a, L: Lockable<'a>> AsMut<Self> for RefLockCollection<'a, L> { fn as_mut(&mut self) -> &mut Self { self } } -impl<L: IntoIterator> IntoIterator for LockCollection<L> { - type Item = L::Item; - type IntoIter = L::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.data.into_iter() - } -} - -impl<'a, L> IntoIterator for &'a LockCollection<L> +impl<'a, L> IntoIterator for &'a RefLockCollection<'a, L> where &'a L: IntoIterator, { @@ -69,51 +49,7 @@ where } } -impl<'a, L> IntoIterator for &'a mut LockCollection<L> -where - &'a mut L: IntoIterator, -{ - type Item = <&'a mut L as IntoIterator>::Item; - type IntoIter = <&'a mut L as IntoIterator>::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.data.into_iter() - } -} - -impl<'a, L: OwnedLockable<'a>, I: FromIterator<L> + OwnedLockable<'a>> FromIterator<L> - for LockCollection<I> -{ - fn from_iter<T: IntoIterator<Item = L>>(iter: T) -> Self { - let iter: I = iter.into_iter().collect(); - Self::new(iter) - } -} - -impl<'a, E: OwnedLockable<'a> + Extend<L>, L: OwnedLockable<'a>> Extend<L> for LockCollection<E> { - fn extend<T: IntoIterator<Item = L>>(&mut self, iter: T) { - self.data.extend(iter) - } -} - -impl<'a, L: OwnedLockable<'a>> LockCollection<L> { - /// Creates a new collection of owned locks. - /// - /// Because the locks are owned, there's no need to do any checks for - /// duplicate values. - /// - /// # Examples - /// - /// ``` - /// use happylock::{LockCollection, Mutex}; - /// - /// let lock = LockCollection::new((Mutex::new(0), Mutex::new(""))); - /// ``` - #[must_use] - pub const fn new(data: L) -> Self { - Self { data } - } - +impl<'a, L: OwnedLockable<'a> + 'a> RefLockCollection<'a, L> { /// Creates a new collection of owned locks. /// /// Because the locks are owned, there's no need to do any checks for @@ -125,15 +61,18 @@ impl<'a, L: OwnedLockable<'a>> LockCollection<L> { /// use happylock::{LockCollection, Mutex}; /// /// let data = (Mutex::new(0), Mutex::new("")); - /// let lock = LockCollection::new_ref(&data); + /// let lock = LockCollection::new(&data); /// ``` #[must_use] - pub const fn new_ref(data: &L) -> LockCollection<&L> { - LockCollection { data } + pub fn new(data: &'a L) -> RefLockCollection<L> { + RefLockCollection { + locks: get_locks(data), + data, + } } } -impl<L> LockCollection<L> { +impl<'a, L: Lockable<'a>> RefLockCollection<'a, L> { /// Creates a new collections of locks. /// /// # Safety @@ -153,23 +92,18 @@ impl<L> LockCollection<L> { /// let lock = unsafe { LockCollection::new_unchecked((&data1, &data2)) }; /// ``` #[must_use] - pub const unsafe fn new_unchecked(data: L) -> Self { - Self { data } + pub unsafe fn new_unchecked(data: &'a L) -> Self { + Self { + data, + locks: get_locks(data), + } } -} -impl<'a, L: Lockable<'a>> LockCollection<L> { /// Creates a new collection of locks. /// /// This returns `None` if any locks are found twice in the given /// collection. /// - /// # Performance - /// - /// This does a check at runtime to make sure that the collection contains - /// no two copies of the same lock. This is an `O(n^2)` operation. Prefer - /// [`LockCollection::new`] or [`LockCollection::new_ref`] instead. - /// /// # Examples /// /// ``` @@ -182,13 +116,13 @@ impl<'a, L: Lockable<'a>> LockCollection<L> { /// let lock = LockCollection::try_new((&data1, &data2)).unwrap(); /// ``` #[must_use] - pub fn try_new(data: L) -> Option<Self> { - let ptrs = data.get_ptrs(); - if contains_duplicates(&ptrs) { + pub fn try_new(data: &'a L) -> Option<Self> { + let locks = get_locks(data); + if contains_duplicates(&locks) { return None; } - Some(Self { data }) + Some(Self { locks, data }) } /// Locks the collection @@ -210,9 +144,14 @@ impl<'a, L: Lockable<'a>> LockCollection<L> { /// *guard.1 = "1"; /// ``` pub fn lock<'key: 'a, Key: Keyable + 'key>(&'a self, key: Key) -> LockGuard<'a, 'key, L, Key> { + for lock in &self.locks { + // safety: we have the thread key + unsafe { lock.lock() }; + } + LockGuard { - // safety: we have the thread's key - guard: unsafe { self.data.lock() }, + // safety: we've already acquired the lock + guard: unsafe { self.data.guard() }, key, _phantom: PhantomData, } @@ -245,8 +184,25 @@ impl<'a, L: Lockable<'a>> LockCollection<L> { &'a self, key: Key, ) -> Option<LockGuard<'a, 'key, L, Key>> { - // safety: we have the thread's key - unsafe { self.data.try_lock() }.map(|guard| LockGuard { + let guard = unsafe { + for (i, lock) in self.locks.iter().enumerate() { + // safety: we have the thread key + let success = lock.try_lock(); + + if !success { + for lock in &self.locks[0..i] { + // safety: this lock was already acquired + lock.unlock(); + } + return None; + } + } + + // safety: we've acquired the locks + self.data.guard() + }; + + Some(LockGuard { guard, key, _phantom: PhantomData, @@ -276,23 +232,13 @@ impl<'a, L: Lockable<'a>> LockCollection<L> { } } -impl<'a, L: 'a> LockCollection<L> +impl<'a, L: 'a> RefLockCollection<'a, L> where &'a L: IntoIterator, { /// Returns an iterator over references to each value in the collection. + #[must_use] pub fn iter(&'a self) -> <&'a L as IntoIterator>::IntoIter { self.into_iter() } } - -impl<'a, L: 'a> LockCollection<L> -where - &'a mut L: IntoIterator, -{ - /// Returns an iterator over mutable references to each value in the - /// collection. - pub fn iter_mut(&'a mut self) -> <&'a mut L as IntoIterator>::IntoIter { - self.into_iter() - } -} diff --git a/src/collection/guard.rs b/src/collection/guard.rs index 110a935..3b98d29 100644 --- a/src/collection/guard.rs +++ b/src/collection/guard.rs @@ -5,7 +5,7 @@ use crate::{key::Keyable, Lockable}; use super::LockGuard; impl<'a, 'key: 'a, L: Lockable<'a>, Key: Keyable> Deref for LockGuard<'a, 'key, L, Key> { - type Target = L::Output; + type Target = L::Guard; fn deref(&self) -> &Self::Target { &self.guard @@ -106,14 +106,14 @@ //! println!("{}", *data.1); //! ``` -mod collection; mod key; mod lockable; +pub mod collection; pub mod mutex; pub mod rwlock; -pub use collection::LockCollection; +pub use collection::RefLockCollection; pub use key::{Keyable, ThreadKey}; pub use lockable::{Lockable, OwnedLockable}; diff --git a/src/lockable.rs b/src/lockable.rs index 086ab33..a09b84b 100644 --- a/src/lockable.rs +++ b/src/lockable.rs @@ -7,15 +7,6 @@ use crate::{ use lock_api::{RawMutex, RawRwLock}; -/// A type that may be locked and unlocked, and is known to be the only valid -/// instance of the lock. -/// -/// # Safety -/// -/// There must not be any two values which can unlock the value at the same -/// time, i.e., this must either be an owned value or a mutable reference. -pub unsafe trait OwnedLockable<'a>: Lockable<'a> {} - /// A type that may be locked and unlocked /// /// # Safety @@ -23,30 +14,21 @@ pub unsafe trait OwnedLockable<'a>: Lockable<'a> {} /// A deadlock must never occur. The `unlock` method must correctly unlock the /// data. The `get_ptrs` method must be implemented correctly. The `Output` /// must be unlocked when it is dropped. -pub unsafe trait Lockable<'a> { - /// The output of the lock - type Output; - - /// Returns a list of all pointers to locks. This is used to ensure that - /// the same lock isn't included twice - #[must_use] - fn get_ptrs(&self) -> Vec<usize>; - +pub unsafe trait Lock: Send + Sync { /// Blocks until the lock is acquired /// /// # Safety /// - /// It is undefined behavior to: - /// * Use this without ownership or mutable access to the [`ThreadKey`], - /// which should last as long as the return value is alive. - /// * Call this on multiple locks without unlocking first. + /// It is undefined behavior to use this without ownership or mutable + /// access to the [`ThreadKey`], which should last as long as the return + /// value is alive. /// /// [`ThreadKey`]: `crate::ThreadKey` - unsafe fn lock(&'a self) -> Self::Output; + unsafe fn lock(&self); /// Attempt to lock without blocking. /// - /// Returns `Some` if successful, `None` otherwise. + /// Returns `true` if successful, `false` otherwise. /// /// # Safety /// @@ -55,304 +37,225 @@ pub unsafe trait Lockable<'a> { /// value is alive. /// /// [`ThreadKey`]: `crate::ThreadKey` - unsafe fn try_lock(&'a self) -> Option<Self::Output>; + unsafe fn try_lock(&self) -> bool; + + /// Releases the lock + /// + /// # Safety + /// + /// It is undefined behavior to use this if the lock is not acquired + unsafe fn unlock(&self); +} + +pub unsafe trait Lockable<'a> { + /// The guard returned that does not hold a key + type Guard; + + fn get_ptrs(&'a self, ptrs: &mut Vec<&'a dyn Lock>); + + #[must_use] + unsafe fn guard(&'a self) -> Self::Guard; } -unsafe impl<'a, T: Lockable<'a>> Lockable<'a> for &T { - type Output = T::Output; +/// A type that may be locked and unlocked, and is known to be the only valid +/// instance of the lock. +/// +/// # Safety +/// +/// There must not be any two values which can unlock the value at the same +/// time, i.e., this must either be an owned value or a mutable reference. +pub unsafe trait OwnedLockable<'a>: Lockable<'a> {} - fn get_ptrs(&self) -> Vec<usize> { - (*self).get_ptrs() +unsafe impl<T: Send, R: RawMutex + Send + Sync> Lock for Mutex<T, R> { + unsafe fn lock(&self) { + self.raw().lock() } - unsafe fn lock(&'a self) -> Self::Output { - (*self).lock() + unsafe fn try_lock(&self) -> bool { + self.raw().try_lock() } - unsafe fn try_lock(&'a self) -> Option<Self::Output> { - (*self).try_lock() + unsafe fn unlock(&self) { + self.raw().unlock() } } -unsafe impl<'a, T: Lockable<'a>> Lockable<'a> for &mut T { - type Output = T::Output; - - fn get_ptrs(&self) -> Vec<usize> { - (**self).get_ptrs() +unsafe impl<T: Send, R: RawRwLock + Send + Sync> Lock for RwLock<T, R> { + unsafe fn lock(&self) { + self.raw().lock_exclusive() } - unsafe fn lock(&'a self) -> Self::Output { - (**self).lock() + unsafe fn try_lock(&self) -> bool { + self.raw().try_lock_exclusive() } - unsafe fn try_lock(&'a self) -> Option<Self::Output> { - (**self).try_lock() + unsafe fn unlock(&self) { + self.raw().unlock_exclusive() } } -unsafe impl<'a, T: OwnedLockable<'a>> OwnedLockable<'a> for &mut T {} +unsafe impl<'a, T: Send + 'a, R: RawMutex + Send + Sync + 'a> Lockable<'a> for Mutex<T, R> { + type Guard = MutexRef<'a, T, R>; -unsafe impl<'a, T: 'a, R: RawMutex + 'a> Lockable<'a> for Mutex<T, R> { - type Output = MutexRef<'a, T, R>; + fn get_ptrs(&'a self, ptrs: &mut Vec<&'a dyn Lock>) { + ptrs.push(self); + } - fn get_ptrs(&self) -> Vec<usize> { - vec![self as *const Self as usize] + unsafe fn guard(&'a self) -> Self::Guard { + MutexRef::new(self) } +} - unsafe fn lock(&'a self) -> Self::Output { - self.lock_no_key() +unsafe impl<'a, T: Send + 'a, R: RawRwLock + Send + Sync + 'a> Lockable<'a> for RwLock<T, R> { + type Guard = RwLockWriteRef<'a, T, R>; + + fn get_ptrs(&'a self, ptrs: &mut Vec<&'a dyn Lock>) { + ptrs.push(self); } - unsafe fn try_lock(&'a self) -> Option<Self::Output> { - self.try_lock_no_key() + unsafe fn guard(&'a self) -> Self::Guard { + RwLockWriteRef::new(self) } } -unsafe impl<'a, T: 'a, R: RawRwLock + 'a> Lockable<'a> for RwLock<T, R> { - type Output = RwLockWriteRef<'a, T, R>; +unsafe impl<'a, T: Send + 'a, R: RawMutex + Send + Sync + 'a> OwnedLockable<'a> for Mutex<T, R> {} - fn get_ptrs(&self) -> Vec<usize> { - vec![self as *const Self as usize] - } +unsafe impl<'a, T: Send + 'a, R: RawRwLock + Send + Sync + 'a> OwnedLockable<'a> for RwLock<T, R> {} + +unsafe impl<'a, T: Send + 'a, R: RawRwLock + Send + Sync + 'a> Lockable<'a> for ReadLock<'a, T, R> { + type Guard = RwLockReadRef<'a, T, R>; - unsafe fn lock(&'a self) -> Self::Output { - self.write_no_key() + fn get_ptrs(&'a self, ptrs: &mut Vec<&'a dyn Lock>) { + ptrs.push(self.as_ref()); } - unsafe fn try_lock(&'a self) -> Option<Self::Output> { - self.try_write_no_key() + unsafe fn guard(&'a self) -> Self::Guard { + RwLockReadRef::new(self.as_ref()) } } -unsafe impl<'a, T: 'a, R: RawMutex + 'a> OwnedLockable<'a> for Mutex<T, R> {} - -unsafe impl<'a, T: 'a, R: RawRwLock + 'a> OwnedLockable<'a> for RwLock<T, R> {} +unsafe impl<'a, T: Send + 'a, R: RawRwLock + Send + Sync + 'a> Lockable<'a> + for WriteLock<'a, T, R> +{ + type Guard = RwLockWriteRef<'a, T, R>; -unsafe impl<'a, T: 'a, R: RawRwLock + 'a> Lockable<'a> for ReadLock<'a, T, R> { - type Output = RwLockReadRef<'a, T, R>; + fn get_ptrs(&'a self, ptrs: &mut Vec<&'a dyn Lock>) { + ptrs.push(self.as_ref()); + } - fn get_ptrs(&self) -> Vec<usize> { - vec![self.as_ref() as *const RwLock<T, R> as usize] + unsafe fn guard(&'a self) -> Self::Guard { + RwLockWriteRef::new(self.as_ref()) } +} - unsafe fn lock(&'a self) -> Self::Output { - self.lock_no_key() +unsafe impl<'a, T: Lockable<'a>> Lockable<'a> for &'a T { + type Guard = T::Guard; + + fn get_ptrs(&self, ptrs: &mut Vec<&'a dyn Lock>) { + (*self).get_ptrs(ptrs); } - unsafe fn try_lock(&'a self) -> Option<Self::Output> { - self.try_lock_no_key() + unsafe fn guard(&self) -> Self::Guard { + (*self).guard() } } -unsafe impl<'a, T: 'a, R: RawRwLock + 'a> Lockable<'a> for WriteLock<'a, T, R> { - type Output = RwLockWriteRef<'a, T, R>; - - fn get_ptrs(&self) -> Vec<usize> { - vec![self.as_ref() as *const RwLock<T, R> as usize] - } +unsafe impl<'a, T: Lockable<'a>> Lockable<'a> for &mut T { + type Guard = T::Guard; - unsafe fn lock(&'a self) -> Self::Output { - self.lock_no_key() + fn get_ptrs(&'a self, ptrs: &mut Vec<&'a dyn Lock>) { + (**self).get_ptrs(ptrs) } - unsafe fn try_lock(&'a self) -> Option<Self::Output> { - self.try_lock_no_key() + unsafe fn guard(&'a self) -> Self::Guard { + (**self).guard() } } -unsafe impl<'a, A: Lockable<'a>> Lockable<'a> for (A,) { - type Output = (A::Output,); +unsafe impl<'a, T: OwnedLockable<'a>> OwnedLockable<'a> for &mut T {} - fn get_ptrs(&self) -> Vec<usize> { - let mut ptrs = Vec::with_capacity(1); - ptrs.append(&mut self.0.get_ptrs()); - ptrs - } +unsafe impl<'a, A: Lockable<'a>> Lockable<'a> for (A,) { + type Guard = (A::Guard,); - unsafe fn lock(&'a self) -> Self::Output { - (self.0.lock(),) + fn get_ptrs(&'a self, ptrs: &mut Vec<&'a dyn Lock>) { + self.0.get_ptrs(ptrs); } - unsafe fn try_lock(&'a self) -> Option<Self::Output> { - self.0.try_lock().map(|a| (a,)) + unsafe fn guard(&'a self) -> Self::Guard { + (self.0.guard(),) } } unsafe impl<'a, A: Lockable<'a>, B: Lockable<'a>> Lockable<'a> for (A, B) { - type Output = (A::Output, B::Output); - - fn get_ptrs(&self) -> Vec<usize> { - let mut ptrs = Vec::with_capacity(2); - ptrs.append(&mut self.0.get_ptrs()); - ptrs.append(&mut self.1.get_ptrs()); - ptrs - } + type Guard = (A::Guard, B::Guard); - unsafe fn lock(&'a self) -> Self::Output { - loop { - let lock0 = self.0.lock(); - let Some(lock1) = self.1.try_lock() else { - continue; - }; - - return (lock0, lock1); - } + fn get_ptrs(&'a self, ptrs: &mut Vec<&'a dyn Lock>) { + self.0.get_ptrs(ptrs); + self.1.get_ptrs(ptrs); } - unsafe fn try_lock(&'a self) -> Option<Self::Output> { - let Some(lock0) = self.0.try_lock() else { - return None; - }; - let Some(lock1) = self.1.try_lock() else { - return None; - }; - - Some((lock0, lock1)) + unsafe fn guard(&'a self) -> Self::Guard { + (self.0.guard(), self.1.guard()) } } unsafe impl<'a, A: Lockable<'a>, B: Lockable<'a>, C: Lockable<'a>> Lockable<'a> for (A, B, C) { - type Output = (A::Output, B::Output, C::Output); - - fn get_ptrs(&self) -> Vec<usize> { - let mut ptrs = Vec::with_capacity(3); - ptrs.append(&mut self.0.get_ptrs()); - ptrs.append(&mut self.1.get_ptrs()); - ptrs.append(&mut self.2.get_ptrs()); - ptrs - } - - unsafe fn lock(&'a self) -> Self::Output { - loop { - let lock0 = self.0.lock(); - let Some(lock1) = self.1.try_lock() else { - continue; - }; - let Some(lock2) = self.2.try_lock() else { - continue; - }; - - return (lock0, lock1, lock2); - } - } + type Guard = (A::Guard, B::Guard, C::Guard); - unsafe fn try_lock(&'a self) -> Option<Self::Output> { - let Some(lock0) = self.0.try_lock() else { - return None; - }; - let Some(lock1) = self.1.try_lock() else { - return None; - }; - let Some(lock2) = self.2.try_lock() else { - return None; - }; + fn get_ptrs(&'a self, ptrs: &mut Vec<&'a dyn Lock>) { + self.0.get_ptrs(ptrs); + self.1.get_ptrs(ptrs); + self.2.get_ptrs(ptrs); + } - Some((lock0, lock1, lock2)) + unsafe fn guard(&'a self) -> Self::Guard { + (self.0.guard(), self.1.guard(), self.2.guard()) } } unsafe impl<'a, A: Lockable<'a>, B: Lockable<'a>, C: Lockable<'a>, D: Lockable<'a>> Lockable<'a> for (A, B, C, D) { - type Output = (A::Output, B::Output, C::Output, D::Output); - - fn get_ptrs(&self) -> Vec<usize> { - let mut ptrs = Vec::with_capacity(4); - ptrs.append(&mut self.0.get_ptrs()); - ptrs.append(&mut self.1.get_ptrs()); - ptrs.append(&mut self.2.get_ptrs()); - ptrs.append(&mut self.3.get_ptrs()); - ptrs - } - - unsafe fn lock(&'a self) -> Self::Output { - loop { - let lock0 = self.0.lock(); - let Some(lock1) = self.1.try_lock() else { - continue; - }; - let Some(lock2) = self.2.try_lock() else { - continue; - }; - let Some(lock3) = self.3.try_lock() else { - continue; - }; - - return (lock0, lock1, lock2, lock3); - } - } + type Guard = (A::Guard, B::Guard, C::Guard, D::Guard); - unsafe fn try_lock(&'a self) -> Option<Self::Output> { - let Some(lock0) = self.0.try_lock() else { - return None; - }; - let Some(lock1) = self.1.try_lock() else { - return None; - }; - let Some(lock2) = self.2.try_lock() else { - return None; - }; - let Some(lock3) = self.3.try_lock() else { - return None; - }; + fn get_ptrs(&'a self, ptrs: &mut Vec<&'a dyn Lock>) { + self.0.get_ptrs(ptrs); + self.1.get_ptrs(ptrs); + self.2.get_ptrs(ptrs); + self.3.get_ptrs(ptrs); + } - Some((lock0, lock1, lock2, lock3)) + unsafe fn guard(&'a self) -> Self::Guard { + ( + self.0.guard(), + self.1.guard(), + self.2.guard(), + self.3.guard(), + ) } } unsafe impl<'a, A: Lockable<'a>, B: Lockable<'a>, C: Lockable<'a>, D: Lockable<'a>, E: Lockable<'a>> Lockable<'a> for (A, B, C, D, E) { - type Output = (A::Output, B::Output, C::Output, D::Output, E::Output); - - fn get_ptrs(&self) -> Vec<usize> { - let mut ptrs = Vec::with_capacity(5); - ptrs.append(&mut self.0.get_ptrs()); - ptrs.append(&mut self.1.get_ptrs()); - ptrs.append(&mut self.2.get_ptrs()); - ptrs.append(&mut self.3.get_ptrs()); - ptrs.append(&mut self.4.get_ptrs()); - ptrs - } - - unsafe fn lock(&'a self) -> Self::Output { - loop { - let lock0 = self.0.lock(); - let Some(lock1) = self.1.try_lock() else { - continue; - }; - let Some(lock2) = self.2.try_lock() else { - continue; - }; - let Some(lock3) = self.3.try_lock() else { - continue; - }; - let Some(lock4) = self.4.try_lock() else { - continue; - }; - - return (lock0, lock1, lock2, lock3, lock4); - } + type Guard = (A::Guard, B::Guard, C::Guard, D::Guard, E::Guard); + + fn get_ptrs(&'a self, ptrs: &mut Vec<&'a dyn Lock>) { + self.0.get_ptrs(ptrs); + self.1.get_ptrs(ptrs); + self.2.get_ptrs(ptrs); + self.3.get_ptrs(ptrs); + self.4.get_ptrs(ptrs); } - unsafe fn try_lock(&'a self) -> Option<Self::Output> { - let Some(lock0) = self.0.try_lock() else { - return None; - }; - let Some(lock1) = self.1.try_lock() else { - return None; - }; - let Some(lock2) = self.2.try_lock() else { - return None; - }; - let Some(lock3) = self.3.try_lock() else { - return None; - }; - let Some(lock4) = self.4.try_lock() else { - return None; - }; - - Some((lock0, lock1, lock2, lock3, lock4)) + unsafe fn guard(&'a self) -> Self::Guard { + ( + self.0.guard(), + self.1.guard(), + self.2.guard(), + self.3.guard(), + self.4.guard(), + ) } } @@ -366,79 +269,81 @@ unsafe impl< F: Lockable<'a>, > Lockable<'a> for (A, B, C, D, E, F) { - type Output = ( - A::Output, - B::Output, - C::Output, - D::Output, - E::Output, - F::Output, - ); - - fn get_ptrs(&self) -> Vec<usize> { - let mut ptrs = Vec::with_capacity(6); - ptrs.append(&mut self.0.get_ptrs()); - ptrs.append(&mut self.1.get_ptrs()); - ptrs.append(&mut self.2.get_ptrs()); - ptrs.append(&mut self.3.get_ptrs()); - ptrs.append(&mut self.4.get_ptrs()); - ptrs.append(&mut self.5.get_ptrs()); - ptrs - } - - unsafe fn lock(&'a self) -> Self::Output { - loop { - let lock0 = self.0.lock(); - let Some(lock1) = self.1.try_lock() else { - continue; - }; - let Some(lock2) = self.2.try_lock() else { - continue; - }; - let Some(lock3) = self.3.try_lock() else { - continue; - }; - let Some(lock4) = self.4.try_lock() else { - continue; - }; - let Some(lock5) = self.5.try_lock() else { - continue; - }; - - return (lock0, lock1, lock2, lock3, lock4, lock5); - } + type Guard = (A::Guard, B::Guard, C::Guard, D::Guard, E::Guard, F::Guard); + + fn get_ptrs(&'a self, ptrs: &mut Vec<&'a dyn Lock>) { + self.0.get_ptrs(ptrs); + self.1.get_ptrs(ptrs); + self.2.get_ptrs(ptrs); + self.3.get_ptrs(ptrs); + self.4.get_ptrs(ptrs); + self.5.get_ptrs(ptrs); + } + + unsafe fn guard(&'a self) -> Self::Guard { + ( + self.0.guard(), + self.1.guard(), + self.2.guard(), + self.3.guard(), + self.4.guard(), + self.5.guard(), + ) } +} + +unsafe impl< + 'a, + A: Lockable<'a>, + B: Lockable<'a>, + C: Lockable<'a>, + D: Lockable<'a>, + E: Lockable<'a>, + F: Lockable<'a>, + G: Lockable<'a>, + > Lockable<'a> for (A, B, C, D, E, F, G) +{ + type Guard = ( + A::Guard, + B::Guard, + C::Guard, + D::Guard, + E::Guard, + F::Guard, + G::Guard, + ); - unsafe fn try_lock(&'a self) -> Option<Self::Output> { - let Some(lock0) = self.0.try_lock() else { - return None; - }; - let Some(lock1) = self.1.try_lock() else { - return None; - }; - let Some(lock2) = self.2.try_lock() else { - return None; - }; - let Some(lock3) = self.3.try_lock() else { - return None; - }; - let Some(lock4) = self.4.try_lock() else { - return None; - }; - let Some(lock5) = self.5.try_lock() else { - return None; - }; - - Some((lock0, lock1, lock2, lock3, lock4, lock5)) + fn get_ptrs(&'a self, ptrs: &mut Vec<&'a dyn Lock>) { + self.0.get_ptrs(ptrs); + self.1.get_ptrs(ptrs); + self.2.get_ptrs(ptrs); + self.3.get_ptrs(ptrs); + self.4.get_ptrs(ptrs); + self.5.get_ptrs(ptrs); + self.6.get_ptrs(ptrs); + } + + unsafe fn guard(&'a self) -> Self::Guard { + ( + self.0.guard(), + self.1.guard(), + self.2.guard(), + self.3.guard(), + self.4.guard(), + self.5.guard(), + self.6.guard(), + ) } } unsafe impl<'a, A: OwnedLockable<'a>> OwnedLockable<'a> for (A,) {} unsafe impl<'a, A: OwnedLockable<'a>, B: OwnedLockable<'a>> OwnedLockable<'a> for (A, B) {} + unsafe impl<'a, A: OwnedLockable<'a>, B: OwnedLockable<'a>, C: OwnedLockable<'a>> OwnedLockable<'a> for (A, B, C) { } + unsafe impl< 'a, A: OwnedLockable<'a>, @@ -448,6 +353,7 @@ unsafe impl< > OwnedLockable<'a> for (A, B, C, D) { } + unsafe impl< 'a, A: OwnedLockable<'a>, @@ -458,6 +364,7 @@ unsafe impl< > OwnedLockable<'a> for (A, B, C, D, E) { } + unsafe impl< 'a, A: OwnedLockable<'a>, @@ -470,194 +377,73 @@ unsafe impl< { } +unsafe impl< + 'a, + A: OwnedLockable<'a>, + B: OwnedLockable<'a>, + C: OwnedLockable<'a>, + D: OwnedLockable<'a>, + E: OwnedLockable<'a>, + F: OwnedLockable<'a>, + G: OwnedLockable<'a>, + > OwnedLockable<'a> for (A, B, C, D, E, F, G) +{ +} + unsafe impl<'a, T: Lockable<'a>, const N: usize> Lockable<'a> for [T; N] { - type Output = [T::Output; N]; + type Guard = [T::Guard; N]; - fn get_ptrs(&self) -> Vec<usize> { - let mut ptrs = Vec::with_capacity(N); + fn get_ptrs(&'a self, ptrs: &mut Vec<&'a dyn Lock>) { for lock in self { - ptrs.append(&mut lock.get_ptrs()); - } - ptrs - } - - unsafe fn lock(&'a self) -> 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; - } - drop(guard.assume_init()); - } - } - - let mut first_idx = 0; - 'outer: loop { - let mut outputs = MaybeUninit::<[MaybeUninit<T::Output>; N]>::uninit().assume_init(); - if N == 0 { - return outputs.map(|mu| mu.assume_init()); - } - - outputs[0].write(self[0].lock()); - for i in 0..N { - if first_idx == i { - continue; - } - - match self[i].try_lock() { - Some(guard) => outputs[i].write(guard), - None => { - unlock_partial::<T, N>(outputs, i); - first_idx = i; - continue 'outer; - } - }; - } - - return outputs.map(|mu| mu.assume_init()); + lock.get_ptrs(ptrs); } } - 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; - } - drop(guard.assume_init()); - } - } - - let mut outputs = MaybeUninit::<[MaybeUninit<T::Output>; N]>::uninit().assume_init(); + unsafe fn guard(&'a self) -> Self::Guard { + let mut guards = MaybeUninit::<[MaybeUninit<T::Guard>; N]>::uninit().assume_init(); for i in 0..N { - match self[i].try_lock() { - Some(guard) => outputs[i].write(guard), - None => { - unlock_partial::<T, N>(outputs, i); - return None; - } - }; + guards[i].write(self[i].guard()); } - Some(outputs.map(|mu| mu.assume_init())) + guards.map(|g| g.assume_init()) } } unsafe impl<'a, T: Lockable<'a>> Lockable<'a> for Box<[T]> { - type Output = Box<[T::Output]>; - - fn get_ptrs(&self) -> Vec<usize> { - let mut ptrs = Vec::with_capacity(self.len()); - for lock in &**self { - ptrs.append(&mut lock.get_ptrs()); - } - ptrs - } + type Guard = Box<[T::Guard]>; - unsafe fn lock(&'a self) -> Self::Output { - let mut first_idx = 0; - if self.is_empty() { - return Box::new([]); - } - - 'outer: loop { - let mut outputs = Vec::with_capacity(self.len()); - - outputs.push(self[first_idx].lock()); - for (idx, lock) in self.iter().enumerate() { - if first_idx == idx { - continue; - } - - match lock.try_lock() { - Some(guard) => { - outputs.push(guard); - } - None => { - first_idx = idx; - continue 'outer; - } - }; - } - - return outputs.into_boxed_slice(); + fn get_ptrs(&'a self, ptrs: &mut Vec<&'a dyn Lock>) { + for lock in self.iter() { + lock.get_ptrs(ptrs); } } - unsafe fn try_lock(&'a self) -> Option<Self::Output> { - let mut outputs = Vec::with_capacity(self.len()); - for lock in &**self { - match lock.try_lock() { - Some(guard) => { - outputs.push(guard); - } - None => return None, - }; + unsafe fn guard(&'a self) -> Self::Guard { + let mut guards = Vec::new(); + for lock in self.iter() { + guards.push(lock.guard()); } - Some(outputs.into_boxed_slice()) + guards.into_boxed_slice() } } unsafe impl<'a, T: Lockable<'a>> Lockable<'a> for Vec<T> { - type Output = Vec<T::Output>; + type Guard = Vec<T::Guard>; - fn get_ptrs(&self) -> Vec<usize> { - let mut ptrs = Vec::with_capacity(self.len()); + fn get_ptrs(&'a self, ptrs: &mut Vec<&'a dyn Lock>) { for lock in self { - ptrs.append(&mut lock.get_ptrs()); - } - ptrs - } - - unsafe fn lock(&'a self) -> Self::Output { - let mut first_idx = 0; - if self.is_empty() { - return Vec::new(); - } - - 'outer: loop { - let mut outputs = Vec::with_capacity(self.len()); - - outputs.push(self[first_idx].lock()); - for (idx, lock) in self.iter().enumerate() { - if first_idx == idx { - continue; - } - - match lock.try_lock() { - Some(guard) => { - outputs.push(guard); - } - None => { - first_idx = idx; - continue 'outer; - } - }; - } - - return outputs; + lock.get_ptrs(ptrs); } } - unsafe fn try_lock(&'a self) -> Option<Self::Output> { - let mut outputs = Vec::with_capacity(self.len()); + unsafe fn guard(&'a self) -> Self::Guard { + let mut guards = Vec::new(); for lock in self { - match lock.try_lock() { - Some(guard) => { - outputs.push(guard); - } - None => return None, - }; + guards.push(lock.guard()); } - Some(outputs) + guards } } diff --git a/src/mutex/guard.rs b/src/mutex/guard.rs index c7f25e4..38ea125 100644 --- a/src/mutex/guard.rs +++ b/src/mutex/guard.rs @@ -35,6 +35,12 @@ impl<'a, T: ?Sized + 'a, R: RawMutex> DerefMut for MutexRef<'a, T, R> { } } +impl<'a, T: ?Sized + 'a, R: RawMutex> MutexRef<'a, T, R> { + pub unsafe fn new(mutex: &'a Mutex<T, R>) -> Self { + Self(mutex, PhantomData) + } +} + impl<'a, 'key: 'a, T: ?Sized + 'a, Key: Keyable, R: RawMutex> Deref for MutexGuard<'a, 'key, T, Key, R> { diff --git a/src/mutex/mutex.rs b/src/mutex/mutex.rs index 917ab78..3b8c221 100644 --- a/src/mutex/mutex.rs +++ b/src/mutex/mutex.rs @@ -24,6 +24,23 @@ impl<T, R: RawMutex> Mutex<T, R> { data: UnsafeCell::new(data), } } + + /// Returns the raw underlying mutex. + /// + /// Note that you will most likely need to import the [`RawMutex`] trait + /// from `lock_api` to be able to call functions on the raw mutex. + /// + /// # Safety + /// + /// This method is unsafe because it allows unlocking a mutex while still + /// holding a reference to a [`MutexGuard`], and locking a mutex without + /// holding the [`ThreadKey`]. + /// + /// [`ThreadKey`]: `crate::ThreadKey` + #[must_use] + pub const unsafe fn raw(&self) -> &R { + &self.raw + } } impl<T: ?Sized + Default, R: RawMutex> Default for Mutex<T, R> { @@ -138,15 +155,6 @@ impl<T: ?Sized, R: RawMutex> Mutex<T, R> { } } - /// Lock without a [`ThreadKey`]. You must exclusively own the - /// [`ThreadKey`] as long as the [`MutexRef`] is alive. This may cause - /// deadlock if called multiple times without unlocking first. - pub(crate) unsafe fn lock_no_key(&self) -> MutexRef<'_, T, R> { - self.raw.lock(); - - MutexRef(self, PhantomData) - } - /// Attempts to lock the `Mutex` without blocking. /// /// # Errors diff --git a/src/rwlock/read_guard.rs b/src/rwlock/read_guard.rs index 532a6e7..8428987 100644 --- a/src/rwlock/read_guard.rs +++ b/src/rwlock/read_guard.rs @@ -26,6 +26,12 @@ impl<'a, T: ?Sized + 'a, R: RawRwLock> Drop for RwLockReadRef<'a, T, R> { } } +impl<'a, T: ?Sized + 'a, R: RawRwLock> RwLockReadRef<'a, T, R> { + pub unsafe fn new(mutex: &'a RwLock<T, R>) -> Self { + Self(mutex, PhantomData) + } +} + impl<'a, 'key: 'a, T: ?Sized + 'a, Key: Keyable, R: RawRwLock> Deref for RwLockReadGuard<'a, 'key, T, Key, R> { diff --git a/src/rwlock/read_lock.rs b/src/rwlock/read_lock.rs index 176fc01..133ca7d 100644 --- a/src/rwlock/read_lock.rs +++ b/src/rwlock/read_lock.rs @@ -67,12 +67,6 @@ impl<'a, T: ?Sized, R: RawRwLock> ReadLock<'a, T, R> { self.0.read(key) } - /// Creates a shared lock without a key. Locking this without exclusive - /// access to the key is undefined behavior. - pub(crate) unsafe fn lock_no_key(&self) -> RwLockReadRef<'_, T, R> { - self.0.read_no_key() - } - /// Attempts to acquire the underlying [`RwLock`] with shared read access /// without blocking. pub fn try_lock<'s, 'key: 's, Key: Keyable + 'key>( diff --git a/src/rwlock/rwlock.rs b/src/rwlock/rwlock.rs index dc5ab30..d16befe 100644 --- a/src/rwlock/rwlock.rs +++ b/src/rwlock/rwlock.rs @@ -24,6 +24,20 @@ impl<T, R: RawRwLock> RwLock<T, R> { raw: R::INIT, } } + + /// Returns the underlying raw reader-writer lock object. + /// + /// Note that you will most likely need to import the [`RawRwLock`] trait + /// from `lock_api` to be able to call functions on the raw reader-writer + /// lock. + /// + /// # Safety + /// + /// This method is unsafe because it allows unlocking a mutex while + /// still holding a reference to a lock guard. + pub const unsafe fn raw(&self) -> &R { + &self.raw + } } impl<T: ?Sized + Default, R: RawRwLock> Default for RwLock<T, R> { @@ -155,15 +169,6 @@ impl<T: ?Sized, R: RawRwLock> RwLock<T, R> { } } - /// Creates a shared lock without a key. Locking this without exclusive - /// access to the key is undefined behavior. - pub(crate) unsafe fn read_no_key(&self) -> RwLockReadRef<'_, T, R> { - self.raw.lock_shared(); - - // safety: the lock is locked first - RwLockReadRef(self, PhantomData) - } - /// Attempts to acquire this `RwLock` with shared read access without /// blocking. /// @@ -246,15 +251,6 @@ impl<T: ?Sized, R: RawRwLock> RwLock<T, R> { } } - /// Creates an exclusive lock without a key. Locking this without exclusive - /// access to the key is undefined behavior. - pub(crate) unsafe fn write_no_key(&self) -> RwLockWriteRef<'_, T, R> { - self.raw.lock_exclusive(); - - // safety: the lock is locked first - RwLockWriteRef(self, PhantomData) - } - /// Attempts to lock this `RwLock` with exclusive write access. /// /// This function does not block. If the lock could not be acquired at this diff --git a/src/rwlock/write_guard.rs b/src/rwlock/write_guard.rs index 6549822..16b474e 100644 --- a/src/rwlock/write_guard.rs +++ b/src/rwlock/write_guard.rs @@ -35,6 +35,12 @@ impl<'a, T: ?Sized + 'a, R: RawRwLock> Drop for RwLockWriteRef<'a, T, R> { } } +impl<'a, T: ?Sized + 'a, R: RawRwLock> RwLockWriteRef<'a, T, R> { + pub unsafe fn new(mutex: &'a RwLock<T, R>) -> Self { + Self(mutex, PhantomData) + } +} + impl<'a, 'key: 'a, T: ?Sized + 'a, Key: Keyable, R: RawRwLock> Deref for RwLockWriteGuard<'a, 'key, T, Key, R> { diff --git a/src/rwlock/write_lock.rs b/src/rwlock/write_lock.rs index d7333ae..c6b4c24 100644 --- a/src/rwlock/write_lock.rs +++ b/src/rwlock/write_lock.rs @@ -67,12 +67,6 @@ impl<'a, T: ?Sized, R: RawRwLock> WriteLock<'a, T, R> { self.0.write(key) } - /// Creates an exclusive lock without a key. Locking this without exclusive - /// access to the key is undefined behavior. - pub(crate) unsafe fn lock_no_key(&self) -> RwLockWriteRef<'_, T, R> { - self.0.write_no_key() - } - /// Attempts to lock the underlying [`RwLock`] with exclusive write access. pub fn try_lock<'s, 'key: 's, Key: Keyable + 'key>( &'s self, |
