From b2281e6aec631dc7c6d69edef9268ce7e00ed1dc Mon Sep 17 00:00:00 2001 From: Botahamec Date: Sat, 21 Dec 2024 11:27:09 -0500 Subject: Implement lock death, but without any usages --- src/collection.rs | 2 +- src/collection/boxed.rs | 46 +++++--- src/collection/guard.rs | 2 - src/collection/owned.rs | 41 ++++--- src/collection/ref.rs | 32 +++--- src/collection/retry.rs | 268 +++++++++++++------------------------------ src/collection/utils.rs | 8 +- src/lockable.rs | 170 +++------------------------ src/mutex.rs | 2 + src/mutex/guard.rs | 3 +- src/mutex/mutex.rs | 119 ++++++++++++++++--- src/poisonable.rs | 2 +- src/poisonable/flag.rs | 4 + src/poisonable/guard.rs | 3 +- src/poisonable/poisonable.rs | 36 +++--- src/rwlock.rs | 2 + src/rwlock/read_guard.rs | 3 +- src/rwlock/rwlock.rs | 131 +++++++++++++++++---- src/rwlock/write_guard.rs | 3 +- 19 files changed, 421 insertions(+), 456 deletions(-) diff --git a/src/collection.rs b/src/collection.rs index a8ae0b7..db68382 100644 --- a/src/collection.rs +++ b/src/collection.rs @@ -1,5 +1,5 @@ use std::cell::UnsafeCell; -use std::marker::{PhantomData, PhantomPinned}; +use std::marker::PhantomData; use crate::{key::Keyable, lockable::RawLock}; diff --git a/src/collection/boxed.rs b/src/collection/boxed.rs index c359098..bef3df2 100644 --- a/src/collection/boxed.rs +++ b/src/collection/boxed.rs @@ -21,43 +21,55 @@ fn contains_duplicates(l: &[&dyn RawLock]) -> bool { } unsafe impl RawLock for BoxedLockCollection { - unsafe fn lock(&self) { + fn kill(&self) { + for lock in &self.locks { + lock.kill(); + } + } + + unsafe fn raw_lock(&self) { for lock in self.locks() { - lock.lock(); + lock.raw_lock(); } } - unsafe fn try_lock(&self) -> bool { + unsafe fn raw_try_lock(&self) -> bool { utils::ordered_try_lock(self.locks()) } - unsafe fn unlock(&self) { + unsafe fn raw_unlock(&self) { for lock in self.locks() { - lock.unlock(); + lock.raw_unlock(); } } - unsafe fn read(&self) { + unsafe fn raw_read(&self) { for lock in self.locks() { - lock.read(); + lock.raw_read(); } } - unsafe fn try_read(&self) -> bool { + unsafe fn raw_try_read(&self) -> bool { utils::ordered_try_read(self.locks()) } - unsafe fn unlock_read(&self) { + unsafe fn raw_unlock_read(&self) { for lock in self.locks() { - lock.unlock_read(); + lock.raw_unlock_read(); } } } unsafe impl Lockable for BoxedLockCollection { - type Guard<'g> = L::Guard<'g> where Self: 'g; + type Guard<'g> + = L::Guard<'g> + where + Self: 'g; - type ReadGuard<'g> = L::ReadGuard<'g> where Self: 'g; + type ReadGuard<'g> + = L::ReadGuard<'g> + where + Self: 'g; fn get_ptrs<'a>(&'a self, ptrs: &mut Vec<&'a dyn RawLock>) { ptrs.extend(self.locks()) @@ -109,6 +121,8 @@ impl + OwnedLockable> FromIterator } } +// safety: the RawLocks must be send because they come from the Send Lockable +#[allow(clippy::non_send_fields_in_send_ty)] unsafe impl Send for BoxedLockCollection {} unsafe impl Sync for BoxedLockCollection {} @@ -261,11 +275,11 @@ impl BoxedLockCollection { data_ref.get_ptrs(&mut locks); // cast to *const () because fat pointers can't be converted to usize - locks.sort_by_key(|lock| (*lock as *const dyn RawLock).cast::<()>() as usize); + locks.sort_by_key(|lock| (&raw const **lock).cast::<()>() as usize); // safety we're just changing the lifetimes let locks: Vec<&'static dyn RawLock> = std::mem::transmute(locks); - let data = data as *const UnsafeCell; + let data = &raw const *data; Self { data, locks } } @@ -323,7 +337,7 @@ impl BoxedLockCollection { ) -> LockGuard<'key, L::Guard<'g>, Key> { for lock in self.locks() { // safety: we have the thread key - unsafe { lock.lock() }; + unsafe { lock.raw_lock() }; } LockGuard { @@ -427,7 +441,7 @@ impl BoxedLockCollection { ) -> LockGuard<'key, L::ReadGuard<'g>, Key> { for lock in self.locks() { // safety: we have the thread key - unsafe { lock.read() }; + unsafe { lock.raw_read() }; } LockGuard { diff --git a/src/collection/guard.rs b/src/collection/guard.rs index 70bb3a6..d604680 100644 --- a/src/collection/guard.rs +++ b/src/collection/guard.rs @@ -48,8 +48,6 @@ mod tests { use crate::collection::OwnedLockCollection; use crate::{RwLock, ThreadKey}; - use super::*; - #[test] fn guard_display_works() { let key = ThreadKey::get().unwrap(); diff --git a/src/collection/owned.rs b/src/collection/owned.rs index 9aa7460..69347cc 100644 --- a/src/collection/owned.rs +++ b/src/collection/owned.rs @@ -12,49 +12,62 @@ fn get_locks(data: &L) -> Vec<&dyn RawLock> { } unsafe impl RawLock for OwnedLockCollection { - unsafe fn lock(&self) { + fn kill(&self) { let locks = get_locks(&self.data); for lock in locks { - lock.lock(); + lock.kill(); } } - unsafe fn try_lock(&self) -> bool { + unsafe fn raw_lock(&self) { + let locks = get_locks(&self.data); + for lock in locks { + lock.raw_lock(); + } + } + + unsafe fn raw_try_lock(&self) -> bool { let locks = get_locks(&self.data); utils::ordered_try_lock(&locks) } - unsafe fn unlock(&self) { + unsafe fn raw_unlock(&self) { let locks = get_locks(&self.data); for lock in locks { - lock.unlock(); + lock.raw_unlock(); } } - unsafe fn read(&self) { + unsafe fn raw_read(&self) { let locks = get_locks(&self.data); for lock in locks { - lock.read(); + lock.raw_read(); } } - unsafe fn try_read(&self) -> bool { + unsafe fn raw_try_read(&self) -> bool { let locks = get_locks(&self.data); utils::ordered_try_read(&locks) } - unsafe fn unlock_read(&self) { + unsafe fn raw_unlock_read(&self) { let locks = get_locks(&self.data); for lock in locks { - lock.unlock_read(); + lock.raw_unlock_read(); } } } unsafe impl Lockable for OwnedLockCollection { - type Guard<'g> = L::Guard<'g> where Self: 'g; + type Guard<'g> + = L::Guard<'g> + where + Self: 'g; - type ReadGuard<'g> = L::ReadGuard<'g> where Self: 'g; + type ReadGuard<'g> + = L::ReadGuard<'g> + where + Self: 'g; fn get_ptrs<'a>(&'a self, ptrs: &mut Vec<&'a dyn RawLock>) { self.data.get_ptrs(ptrs) @@ -196,7 +209,7 @@ impl OwnedLockCollection { for lock in locks { // safety: we have the thread key, and these locks happen in a // predetermined order - unsafe { lock.lock() }; + unsafe { lock.raw_lock() }; } // safety: we've locked all of this already @@ -310,7 +323,7 @@ impl OwnedLockCollection { for lock in locks { // safety: we have the thread key, and these locks happen in a // predetermined order - unsafe { lock.read() }; + unsafe { lock.raw_read() }; } // safety: we've locked all of this already diff --git a/src/collection/ref.rs b/src/collection/ref.rs index 7c0d40a..0e2f057 100644 --- a/src/collection/ref.rs +++ b/src/collection/ref.rs @@ -10,7 +10,7 @@ use super::{utils, LockGuard, RefLockCollection}; pub fn get_locks(data: &L) -> Vec<&dyn RawLock> { let mut locks = Vec::new(); data.get_ptrs(&mut locks); - locks.sort_by_key(|lock| *lock as *const dyn RawLock); + locks.sort_by_key(|lock| &raw const **lock); locks } @@ -45,35 +45,41 @@ where } unsafe impl RawLock for RefLockCollection<'_, L> { - unsafe fn lock(&self) { + fn kill(&self) { for lock in &self.locks { - lock.lock(); + lock.kill(); } } - unsafe fn try_lock(&self) -> bool { + unsafe fn raw_lock(&self) { + for lock in &self.locks { + lock.raw_lock(); + } + } + + unsafe fn raw_try_lock(&self) -> bool { utils::ordered_try_lock(&self.locks) } - unsafe fn unlock(&self) { + unsafe fn raw_unlock(&self) { for lock in &self.locks { - lock.unlock(); + lock.raw_unlock(); } } - unsafe fn read(&self) { + unsafe fn raw_read(&self) { for lock in &self.locks { - lock.read(); + lock.raw_read(); } } - unsafe fn try_read(&self) -> bool { + unsafe fn raw_try_read(&self) -> bool { utils::ordered_try_read(&self.locks) } - unsafe fn unlock_read(&self) { + unsafe fn raw_unlock_read(&self) { for lock in &self.locks { - lock.unlock_read(); + lock.raw_unlock_read(); } } } @@ -225,7 +231,7 @@ impl<'a, L: Lockable> RefLockCollection<'a, L> { ) -> LockGuard<'key, L::Guard<'a>, Key> { for lock in &self.locks { // safety: we have the thread key - unsafe { lock.lock() }; + unsafe { lock.raw_lock() }; } LockGuard { @@ -333,7 +339,7 @@ impl<'a, L: Sharable> RefLockCollection<'a, L> { ) -> LockGuard<'key, L::ReadGuard<'a>, Key> { for lock in &self.locks { // safety: we have the thread key - unsafe { lock.read() }; + unsafe { lock.raw_read() }; } LockGuard { diff --git a/src/collection/retry.rs b/src/collection/retry.rs index 8a10fc3..05adc3e 100644 --- a/src/collection/retry.rs +++ b/src/collection/retry.rs @@ -8,11 +8,18 @@ use std::marker::PhantomData; use super::{LockGuard, RetryingLockCollection}; +/// Get all raw locks in the collection +fn get_locks(data: &L) -> Vec<&dyn RawLock> { + let mut locks = Vec::new(); + data.get_ptrs(&mut locks); + locks +} + /// Checks that a collection contains no duplicate references to a lock. fn contains_duplicates(data: L) -> bool { let mut locks = Vec::new(); data.get_ptrs(&mut locks); - let locks = locks.into_iter().map(|l| l as *const dyn RawLock); + let locks = locks.into_iter().map(|l| &raw const *l); let mut locks_set = HashSet::with_capacity(locks.len()); for lock in locks { @@ -24,11 +31,17 @@ fn contains_duplicates(data: L) -> bool { false } -unsafe impl RawLock for RetryingLockCollection { - unsafe fn lock(&self) { +unsafe impl RawLock for RetryingLockCollection { + fn kill(&self) { + let locks = get_locks(&self.data); + for lock in locks { + lock.kill(); + } + } + + unsafe fn raw_lock(&self) { let mut first_index = 0; - let mut locks = Vec::new(); - self.data.get_ptrs(&mut locks); + let locks = get_locks(&self.data); if locks.is_empty() { return; @@ -37,23 +50,27 @@ unsafe impl RawLock for RetryingLockCollection { unsafe { 'outer: loop { // safety: we have the thread key - locks[first_index].lock(); + locks[first_index].raw_lock(); for (i, lock) in locks.iter().enumerate() { if i == first_index { continue; } + // If the lock has been killed, then this returns false + // instead of panicking. This sounds like a problem, but if + // it does return false, then the lock function is called + // immediately after, causing a panic // safety: we have the thread key - if !lock.try_lock() { + if !lock.raw_try_lock() { for lock in locks.iter().take(i) { // safety: we already locked all of these - lock.unlock(); + lock.raw_unlock(); } if first_index >= i { // safety: this is already locked and can't be unlocked // by the previous loop - locks[first_index].unlock(); + locks[first_index].raw_unlock(); } first_index = i; @@ -67,9 +84,8 @@ unsafe impl RawLock for RetryingLockCollection { }; } - unsafe fn try_lock(&self) -> bool { - let mut locks = Vec::new(); - self.data.get_ptrs(&mut locks); + unsafe fn raw_try_lock(&self) -> bool { + let locks = get_locks(&self.data); if locks.is_empty() { return true; @@ -78,10 +94,10 @@ unsafe impl RawLock for RetryingLockCollection { unsafe { for (i, lock) in locks.iter().enumerate() { // safety: we have the thread key - if !lock.try_lock() { + if !lock.raw_try_lock() { for lock in locks.iter().take(i) { // safety: we already locked all of these - lock.unlock(); + lock.raw_unlock(); } return false; } @@ -91,39 +107,37 @@ unsafe impl RawLock for RetryingLockCollection { true } - unsafe fn unlock(&self) { - let mut locks = Vec::new(); - self.get_ptrs(&mut locks); + unsafe fn raw_unlock(&self) { + let locks = get_locks(&self.data); for lock in locks { - lock.unlock(); + lock.raw_unlock(); } } - unsafe fn read(&self) { + unsafe fn raw_read(&self) { let mut first_index = 0; - let mut locks = Vec::new(); - self.data.get_ptrs(&mut locks); + let locks = get_locks(&self.data); 'outer: loop { // safety: we have the thread key - locks[first_index].read(); + locks[first_index].raw_read(); for (i, lock) in locks.iter().enumerate() { if i == first_index { continue; } // safety: we have the thread key - if !lock.try_read() { + if !lock.raw_try_read() { for lock in locks.iter().take(i) { // safety: we already locked all of these - lock.unlock_read(); + lock.raw_unlock_read(); } if first_index >= i { // safety: this is already locked and can't be unlocked // by the previous loop - locks[first_index].unlock_read(); + locks[first_index].raw_unlock_read(); } first_index = i; @@ -133,9 +147,8 @@ unsafe impl RawLock for RetryingLockCollection { } } - unsafe fn try_read(&self) -> bool { - let mut locks = Vec::new(); - self.data.get_ptrs(&mut locks); + unsafe fn raw_try_read(&self) -> bool { + let locks = get_locks(&self.data); if locks.is_empty() { return true; @@ -144,10 +157,10 @@ unsafe impl RawLock for RetryingLockCollection { unsafe { for (i, lock) in locks.iter().enumerate() { // safety: we have the thread key - if !lock.try_read() { + if !lock.raw_try_read() { for lock in locks.iter().take(i) { // safety: we already locked all of these - lock.unlock_read(); + lock.raw_unlock_read(); } return false; } @@ -157,20 +170,25 @@ unsafe impl RawLock for RetryingLockCollection { true } - unsafe fn unlock_read(&self) { - let mut locks = Vec::new(); - self.get_ptrs(&mut locks); + unsafe fn raw_unlock_read(&self) { + let locks = get_locks(&self.data); for lock in locks { - lock.unlock_read(); + lock.raw_unlock_read(); } } } unsafe impl Lockable for RetryingLockCollection { - type Guard<'g> = L::Guard<'g> where Self: 'g; + type Guard<'g> + = L::Guard<'g> + where + Self: 'g; - type ReadGuard<'g> = L::ReadGuard<'g> where Self: 'g; + type ReadGuard<'g> + = L::ReadGuard<'g> + where + Self: 'g; fn get_ptrs<'a>(&'a self, ptrs: &mut Vec<&'a dyn RawLock>) { self.data.get_ptrs(ptrs) @@ -186,7 +204,8 @@ unsafe impl Lockable for RetryingLockCollection { } impl LockableAsMut for RetryingLockCollection { - type Inner<'a> = L::Inner<'a> + type Inner<'a> + = L::Inner<'a> where Self: 'a; @@ -419,55 +438,16 @@ impl RetryingLockCollection { &'g self, key: Key, ) -> LockGuard<'key, L::Guard<'g>, Key> { - let mut first_index = 0; - let mut locks = Vec::new(); - self.data.get_ptrs(&mut locks); + unsafe { + // safety: we're taking the thread key + self.raw_lock(); - if locks.is_empty() { - return LockGuard { - // safety: there's no data being returned - guard: unsafe { self.data.guard() }, + LockGuard { + // safety: we just locked the collection + guard: self.guard(), key, _phantom: PhantomData, - }; - } - - let guard = unsafe { - 'outer: loop { - // safety: we have the thread key - locks[first_index].lock(); - for (i, lock) in locks.iter().enumerate() { - if i == first_index { - continue; - } - - // safety: we have the thread key - if !lock.try_lock() { - for lock in locks.iter().take(i) { - // safety: we already locked all of these - lock.unlock(); - } - - if first_index >= i { - // safety: this is already locked and can't be unlocked - // by the previous loop - locks[first_index].unlock(); - } - - first_index = i; - continue 'outer; - } - } - - // safety: we locked all the data - break self.data.guard(); } - }; - - LockGuard { - guard, - key, - _phantom: PhantomData, } } @@ -500,39 +480,15 @@ impl RetryingLockCollection { &'g self, key: Key, ) -> Option, Key>> { - let mut locks = Vec::new(); - self.data.get_ptrs(&mut locks); - - if locks.is_empty() { - return Some(LockGuard { - // safety: there's no data being returned - guard: unsafe { self.data.guard() }, + unsafe { + // safety: we're taking the thread key + self.raw_try_lock().then(|| LockGuard { + // safety: we just succeeded in locking everything + guard: self.guard(), key, _phantom: PhantomData, - }); + }) } - - let guard = unsafe { - for (i, lock) in locks.iter().enumerate() { - // safety: we have the thread key - if !lock.try_lock() { - for lock in locks.iter().take(i) { - // safety: we already locked all of these - lock.unlock(); - } - return None; - } - } - - // safety: we locked all the data - self.data.guard() - }; - - Some(LockGuard { - guard, - key, - _phantom: PhantomData, - }) } /// Unlocks the underlying lockable data type, returning the key that's @@ -584,55 +540,16 @@ impl RetryingLockCollection { &'g self, key: Key, ) -> LockGuard<'key, L::ReadGuard<'g>, Key> { - let mut first_index = 0; - let mut locks = Vec::new(); - self.data.get_ptrs(&mut locks); + unsafe { + // safety: we're taking the thread key + self.raw_read(); - if locks.is_empty() { - return LockGuard { - // safety: there's no data being returned - guard: unsafe { self.data.read_guard() }, + LockGuard { + // safety: we just locked the collection + guard: self.read_guard(), key, _phantom: PhantomData, - }; - } - - let guard = unsafe { - 'outer: loop { - // safety: we have the thread key - locks[first_index].read(); - for (i, lock) in locks.iter().enumerate() { - if i == first_index { - continue; - } - - // safety: we have the thread key - if !lock.try_read() { - for lock in locks.iter().take(i) { - // safety: we already locked all of these - lock.unlock_read(); - } - - if first_index >= i { - // safety: this is already locked and can't be unlocked - // by the previous loop - locks[first_index].unlock_read(); - } - - first_index = i; - continue 'outer; - } - } - - // safety: we locked all the data - break self.data.read_guard(); } - }; - - LockGuard { - guard, - key, - _phantom: PhantomData, } } @@ -666,39 +583,15 @@ impl RetryingLockCollection { &'g self, key: Key, ) -> Option, Key>> { - let mut locks = Vec::new(); - self.data.get_ptrs(&mut locks); - - if locks.is_empty() { - return Some(LockGuard { - // safety: there's no data being returned - guard: unsafe { self.data.read_guard() }, + unsafe { + // safety: we're taking the thread key + self.raw_try_lock().then(|| LockGuard { + // safety: we just succeeded in locking everything + guard: self.read_guard(), key, _phantom: PhantomData, - }); + }) } - - let guard = unsafe { - for (i, lock) in locks.iter().enumerate() { - // safety: we have the thread key - if !lock.try_read() { - for lock in locks.iter().take(i) { - // safety: we already locked all of these - lock.unlock_read(); - } - return None; - } - } - - // safety: we locked all the data - self.data.read_guard() - }; - - Some(LockGuard { - guard, - key, - _phantom: PhantomData, - }) } /// Unlocks the underlying lockable data type, returning the key that's @@ -786,7 +679,6 @@ mod tests { use super::*; use crate::collection::BoxedLockCollection; use crate::{Mutex, RwLock, ThreadKey}; - use lock_api::{RawMutex, RawRwLock}; #[test] fn nonduplicate_lock_references_are_allowed() { diff --git a/src/collection/utils.rs b/src/collection/utils.rs index 8177c1d..c114541 100644 --- a/src/collection/utils.rs +++ b/src/collection/utils.rs @@ -7,12 +7,12 @@ pub unsafe fn ordered_try_lock(locks: &[&dyn RawLock]) -> bool { unsafe { for (i, lock) in locks.iter().enumerate() { // safety: we have the thread key - let success = lock.try_lock(); + let success = lock.raw_try_lock(); if !success { for lock in &locks[0..i] { // safety: this lock was already acquired - lock.unlock(); + lock.raw_unlock(); } return false; } @@ -28,12 +28,12 @@ pub unsafe fn ordered_try_read(locks: &[&dyn RawLock]) -> bool { unsafe { for (i, lock) in locks.iter().enumerate() { // safety: we have the thread key - let success = lock.try_read(); + let success = lock.raw_try_read(); if !success { for lock in &locks[0..i] { // safety: this lock was already acquired - lock.unlock_read(); + lock.raw_unlock_read(); } return false; } diff --git a/src/lockable.rs b/src/lockable.rs index 0066cb4..b098b25 100644 --- a/src/lockable.rs +++ b/src/lockable.rs @@ -1,11 +1,8 @@ use std::mem::MaybeUninit; -use crate::{ - mutex::{Mutex, MutexRef}, - rwlock::{ReadLock, RwLock, RwLockReadRef, RwLockWriteRef, WriteLock}, -}; +use crate::rwlock::{ReadLock, RwLock, RwLockReadRef, RwLockWriteRef, WriteLock}; -use lock_api::{RawMutex, RawRwLock}; +use lock_api::RawRwLock; /// A raw lock type that may be locked and unlocked /// @@ -21,7 +18,11 @@ use lock_api::{RawMutex, RawRwLock}; // because it'd be difficult to implement a separate type that takes a // different kind of RawLock. But now the Sharable marker trait is needed to // indicate if reads can be used. -pub unsafe trait RawLock: Send + Sync { +pub unsafe trait RawLock { + /// Causes all subsequent calls to the `lock` function on this lock to + /// panic. This does not affect anything currently holding the lock. + fn kill(&self); + /// Blocks until the lock is acquired /// /// # Safety @@ -31,7 +32,7 @@ pub unsafe trait RawLock: Send + Sync { /// value is alive. /// /// [`ThreadKey`]: `crate::ThreadKey` - unsafe fn lock(&self); + unsafe fn raw_lock(&self); /// Attempt to lock without blocking. /// @@ -44,14 +45,14 @@ pub unsafe trait RawLock: Send + Sync { /// value is alive. /// /// [`ThreadKey`]: `crate::ThreadKey` - unsafe fn try_lock(&self) -> bool; + unsafe fn raw_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); + unsafe fn raw_unlock(&self); /// Blocks until the data the lock protects can be safely read. /// @@ -66,7 +67,7 @@ pub unsafe trait RawLock: Send + Sync { /// value is alive. /// /// [`ThreadKey`]: `crate::ThreadKey` - unsafe fn read(&self); + unsafe fn raw_read(&self); // Attempt to read without blocking. /// @@ -83,14 +84,14 @@ pub unsafe trait RawLock: Send + Sync { /// value is alive. /// /// [`ThreadKey`]: `crate::ThreadKey` - unsafe fn try_read(&self) -> bool; + unsafe fn raw_try_read(&self) -> bool; /// Releases the lock after calling `read`. /// /// # Safety /// /// It is undefined behavior to use this if the read lock is not acquired - unsafe fn unlock_read(&self); + unsafe fn raw_unlock_read(&self); } /// A type that may be locked and unlocked. @@ -207,151 +208,6 @@ pub unsafe trait Sharable: Lockable {} /// time, i.e., this must either be an owned value or a mutable reference. pub unsafe trait OwnedLockable: Lockable {} -unsafe impl RawLock for Mutex { - unsafe fn lock(&self) { - self.raw().lock() - } - - unsafe fn try_lock(&self) -> bool { - self.raw().try_lock() - } - - unsafe fn unlock(&self) { - self.raw().unlock() - } - - // this is the closest thing to a read we can get, but Sharable isn't - // implemented for this - unsafe fn read(&self) { - self.raw().lock() - } - - unsafe fn try_read(&self) -> bool { - self.raw().try_lock() - } - - unsafe fn unlock_read(&self) { - self.raw().unlock() - } -} - -unsafe impl RawLock for RwLock { - unsafe fn lock(&self) { - self.raw().lock_exclusive() - } - - unsafe fn try_lock(&self) -> bool { - self.raw().try_lock_exclusive() - } - - unsafe fn unlock(&self) { - self.raw().unlock_exclusive() - } - - unsafe fn read(&self) { - self.raw().lock_shared() - } - - unsafe fn try_read(&self) -> bool { - self.raw().try_lock_shared() - } - - unsafe fn unlock_read(&self) { - self.raw().unlock_shared() - } -} - -unsafe impl Lockable for Mutex { - type Guard<'g> - = MutexRef<'g, T, R> - where - Self: 'g; - type ReadGuard<'g> - = MutexRef<'g, T, R> - where - Self: 'g; - - fn get_ptrs<'a>(&'a self, ptrs: &mut Vec<&'a dyn RawLock>) { - ptrs.push(self); - } - - unsafe fn guard(&self) -> Self::Guard<'_> { - MutexRef::new(self) - } - - unsafe fn read_guard(&self) -> Self::ReadGuard<'_> { - MutexRef::new(self) - } -} - -unsafe impl Lockable for RwLock { - type Guard<'g> - = RwLockWriteRef<'g, T, R> - where - Self: 'g; - - type ReadGuard<'g> - = RwLockReadRef<'g, T, R> - where - Self: 'g; - - fn get_ptrs<'a>(&'a self, ptrs: &mut Vec<&'a dyn RawLock>) { - ptrs.push(self); - } - - unsafe fn guard(&self) -> Self::Guard<'_> { - RwLockWriteRef::new(self) - } - - unsafe fn read_guard(&self) -> Self::ReadGuard<'_> { - RwLockReadRef::new(self) - } -} - -impl LockableIntoInner for Mutex { - type Inner = T; - - fn into_inner(self) -> Self::Inner { - self.into_inner() - } -} - -impl LockableAsMut for Mutex { - type Inner<'a> - = &'a mut T - where - Self: 'a; - - fn as_mut(&mut self) -> Self::Inner<'_> { - self.get_mut() - } -} - -impl LockableIntoInner for RwLock { - type Inner = T; - - fn into_inner(self) -> Self::Inner { - self.into_inner() - } -} - -impl LockableAsMut for RwLock { - type Inner<'a> - = &'a mut T - where - Self: 'a; - - fn as_mut(&mut self) -> Self::Inner<'_> { - AsMut::as_mut(self) - } -} - -unsafe impl Sharable for RwLock {} - -unsafe impl OwnedLockable for Mutex {} - -unsafe impl OwnedLockable for RwLock {} - unsafe impl Lockable for ReadLock<'_, T, R> { type Guard<'g> = RwLockReadRef<'g, T, R> diff --git a/src/mutex.rs b/src/mutex.rs index 51089c4..004e5d4 100644 --- a/src/mutex.rs +++ b/src/mutex.rs @@ -4,6 +4,7 @@ use std::marker::PhantomData; use lock_api::RawMutex; use crate::key::Keyable; +use crate::poisonable::PoisonFlag; mod guard; mod mutex; @@ -128,6 +129,7 @@ pub type ParkingMutex = Mutex; /// [`ThreadKey`]: `crate::ThreadKey` pub struct Mutex { raw: R, + poison: PoisonFlag, data: UnsafeCell, } diff --git a/src/mutex/guard.rs b/src/mutex/guard.rs index 8b2b1aa..c255996 100644 --- a/src/mutex/guard.rs +++ b/src/mutex/guard.rs @@ -5,6 +5,7 @@ use std::ops::{Deref, DerefMut}; use lock_api::RawMutex; use crate::key::Keyable; +use crate::lockable::RawLock; use super::{Mutex, MutexGuard, MutexRef}; @@ -27,7 +28,7 @@ impl<'a, T: ?Sized + 'a, R: RawMutex> Drop for MutexRef<'a, T, R> { fn drop(&mut self) { // safety: this guard is being destroyed, so the data cannot be // accessed without locking again - unsafe { self.0.force_unlock() } + unsafe { self.0.raw_unlock() } } } diff --git a/src/mutex/mutex.rs b/src/mutex/mutex.rs index 9707e2f..1130110 100644 --- a/src/mutex/mutex.rs +++ b/src/mutex/mutex.rs @@ -5,9 +5,99 @@ use std::marker::PhantomData; use lock_api::RawMutex; use crate::key::Keyable; +use crate::lockable::{Lockable, LockableAsMut, LockableIntoInner, OwnedLockable, RawLock}; +use crate::poisonable::PoisonFlag; use super::{Mutex, MutexGuard, MutexRef}; +unsafe impl RawLock for Mutex { + fn kill(&self) { + self.poison.poison(); + } + + unsafe fn raw_lock(&self) { + assert!(!self.poison.is_poisoned(), "The mutex has been killed"); + + self.raw.lock() + } + + unsafe fn raw_try_lock(&self) -> bool { + if self.poison.is_poisoned() { + return false; + } + + self.raw.try_lock() + } + + unsafe fn raw_unlock(&self) { + self.raw.unlock() + } + + // this is the closest thing to a read we can get, but Sharable isn't + // implemented for this + unsafe fn raw_read(&self) { + assert!(!self.poison.is_poisoned(), "The mutex has been killed"); + + self.raw.lock() + } + + unsafe fn raw_try_read(&self) -> bool { + if self.poison.is_poisoned() { + return false; + } + + self.raw.try_lock() + } + + unsafe fn raw_unlock_read(&self) { + self.raw.unlock() + } +} + +unsafe impl Lockable for Mutex { + type Guard<'g> + = MutexRef<'g, T, R> + where + Self: 'g; + type ReadGuard<'g> + = MutexRef<'g, T, R> + where + Self: 'g; + + fn get_ptrs<'a>(&'a self, ptrs: &mut Vec<&'a dyn RawLock>) { + ptrs.push(self); + } + + unsafe fn guard(&self) -> Self::Guard<'_> { + MutexRef::new(self) + } + + unsafe fn read_guard(&self) -> Self::ReadGuard<'_> { + MutexRef::new(self) + } +} + +impl LockableIntoInner for Mutex { + type Inner = T; + + fn into_inner(self) -> Self::Inner { + self.into_inner() + } +} + +impl LockableAsMut for Mutex { + type Inner<'a> + = &'a mut T + where + Self: 'a; + + fn as_mut(&mut self) -> Self::Inner<'_> { + self.get_mut() + } +} + +unsafe impl OwnedLockable for Mutex {} + impl Mutex { /// Create a new unlocked `Mutex`. /// @@ -22,6 +112,7 @@ impl Mutex { pub const fn new(data: T) -> Self { Self { raw: R::INIT, + poison: PoisonFlag::new(), data: UnsafeCell::new(data), } } @@ -153,7 +244,8 @@ impl Mutex { /// ``` pub fn lock<'s, 'k: 's, Key: Keyable>(&'s self, key: Key) -> MutexGuard<'s, 'k, T, Key, R> { unsafe { - self.raw.lock(); + // safety: we have the thread key + self.raw_lock(); // safety: we just locked the mutex MutexGuard::new(self, key) @@ -192,15 +284,16 @@ impl Mutex { &'s self, key: Key, ) -> Option> { - if self.raw.try_lock() { - // safety: we just locked the mutex - Some(unsafe { MutexGuard::new(self, key) }) - } else { - None + unsafe { + // safety: we have the key to the mutex + self.raw_try_lock().then(|| + // safety: we just locked the mutex + MutexGuard::new(self, key)) } } /// Returns `true` if the mutex is currently locked + #[cfg(test)] pub(crate) fn is_locked(&self) -> bool { self.raw.is_locked() } @@ -208,17 +301,7 @@ impl Mutex { /// Lock without a [`ThreadKey`]. It is undefined behavior to do this without /// owning the [`ThreadKey`]. pub(crate) unsafe fn try_lock_no_key(&self) -> Option> { - self.raw.try_lock().then_some(MutexRef(self, PhantomData)) - } - - /// Forcibly unlocks the `Lock`. - /// - /// # Safety - /// - /// This should only be called if there are no references to any - /// [`MutexGuard`]s for this mutex in the program. - pub(super) unsafe fn force_unlock(&self) { - self.raw.unlock(); + self.raw_try_lock().then_some(MutexRef(self, PhantomData)) } /// Consumes the [`MutexGuard`], and consequently unlocks its `Mutex`. @@ -238,7 +321,7 @@ impl Mutex { /// ``` pub fn unlock<'a, 'k: 'a, Key: Keyable + 'k>(guard: MutexGuard<'a, 'k, T, Key, R>) -> Key { unsafe { - guard.mutex.0.force_unlock(); + guard.mutex.0.raw_unlock(); } guard.thread_key } diff --git a/src/poisonable.rs b/src/poisonable.rs index ec6de82..f9152d1 100644 --- a/src/poisonable.rs +++ b/src/poisonable.rs @@ -9,7 +9,7 @@ mod poisonable; /// A flag indicating if a lock is poisoned or not. The implementation differs /// depending on whether panics are set to unwind or abort. #[derive(Debug, Default)] -struct PoisonFlag(#[cfg(panic = "unwind")] AtomicBool); +pub(crate) struct PoisonFlag(#[cfg(panic = "unwind")] AtomicBool); /// A wrapper around [`Lockable`] types which will enable poisoning. /// diff --git a/src/poisonable/flag.rs b/src/poisonable/flag.rs index be38a38..a692685 100644 --- a/src/poisonable/flag.rs +++ b/src/poisonable/flag.rs @@ -16,6 +16,10 @@ impl PoisonFlag { pub fn clear_poison(&self) { self.0.store(false, Relaxed) } + + pub fn poison(&self) { + self.0.store(true, Relaxed); + } } #[cfg(not(panic = "unwind"))] diff --git a/src/poisonable/guard.rs b/src/poisonable/guard.rs index 30a9b70..6438c2d 100644 --- a/src/poisonable/guard.rs +++ b/src/poisonable/guard.rs @@ -1,7 +1,6 @@ use std::fmt::{Debug, Display}; use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; -use std::sync::atomic::Ordering::Relaxed; use crate::Keyable; @@ -23,7 +22,7 @@ impl Drop for PoisonRef<'_, Guard> { fn drop(&mut self) { #[cfg(panic = "unwind")] if std::thread::panicking() { - self.flag.0.store(true, Relaxed); + self.flag.poison(); } } } diff --git a/src/poisonable/poisonable.rs b/src/poisonable/poisonable.rs index ddd8038..581fb2f 100644 --- a/src/poisonable/poisonable.rs +++ b/src/poisonable/poisonable.rs @@ -12,28 +12,32 @@ use super::{ }; unsafe impl RawLock for Poisonable { - unsafe fn lock(&self) { - self.inner.lock() + fn kill(&self) { + self.inner.kill() } - unsafe fn try_lock(&self) -> bool { - self.inner.try_lock() + unsafe fn raw_lock(&self) { + self.inner.raw_lock() } - unsafe fn unlock(&self) { - self.inner.unlock() + unsafe fn raw_try_lock(&self) -> bool { + self.inner.raw_try_lock() } - unsafe fn read(&self) { - self.inner.read() + unsafe fn raw_unlock(&self) { + self.inner.raw_unlock() } - unsafe fn try_read(&self) -> bool { - self.inner.try_read() + unsafe fn raw_read(&self) { + self.inner.raw_read() } - unsafe fn unlock_read(&self) { - self.inner.unlock_read() + unsafe fn raw_try_read(&self) -> bool { + self.inner.raw_try_read() + } + + unsafe fn raw_unlock_read(&self) { + self.inner.raw_unlock_read() } } @@ -285,7 +289,7 @@ impl Poisonable { key: Key, ) -> PoisonResult, Key>> { unsafe { - self.inner.lock(); + self.inner.raw_lock(); self.guard(key) } } @@ -339,7 +343,7 @@ impl Poisonable { key: Key, ) -> TryLockPoisonableResult<'flag, 'key, L::Guard<'flag>, Key> { unsafe { - if self.inner.try_lock() { + if self.inner.raw_try_lock() { Ok(self.guard(key)?) } else { Err(TryLockPoisonableError::WouldBlock(key)) @@ -426,7 +430,7 @@ impl Poisonable { key: Key, ) -> PoisonResult, Key>> { unsafe { - self.inner.read(); + self.inner.raw_read(); self.read_guard(key) } } @@ -473,7 +477,7 @@ impl Poisonable { key: Key, ) -> TryLockPoisonableResult<'flag, 'key, L::ReadGuard<'flag>, Key> { unsafe { - if self.inner.try_read() { + if self.inner.raw_try_read() { Ok(self.read_guard(key)?) } else { Err(TryLockPoisonableError::WouldBlock(key)) diff --git a/src/rwlock.rs b/src/rwlock.rs index c1d9a78..9b65a0b 100644 --- a/src/rwlock.rs +++ b/src/rwlock.rs @@ -4,6 +4,7 @@ use std::marker::PhantomData; use lock_api::RawRwLock; use crate::key::Keyable; +use crate::poisonable::PoisonFlag; mod rwlock; @@ -46,6 +47,7 @@ pub type ParkingRwLock = RwLock; /// [`DerefMut`]: `std::ops::DerefMut` pub struct RwLock { raw: R, + poison: PoisonFlag, data: UnsafeCell, } diff --git a/src/rwlock/read_guard.rs b/src/rwlock/read_guard.rs index 1eb8bfc..0668dca 100644 --- a/src/rwlock/read_guard.rs +++ b/src/rwlock/read_guard.rs @@ -5,6 +5,7 @@ use std::ops::Deref; use lock_api::RawRwLock; use crate::key::Keyable; +use crate::lockable::RawLock; use super::{RwLock, RwLockReadGuard, RwLockReadRef}; @@ -41,7 +42,7 @@ impl<'a, T: ?Sized + 'a, R: RawRwLock> Drop for RwLockReadRef<'a, T, R> { fn drop(&mut self) { // safety: this guard is being destroyed, so the data cannot be // accessed without locking again - unsafe { self.0.force_unlock_read() } + unsafe { self.0.raw_unlock_read() } } } diff --git a/src/rwlock/rwlock.rs b/src/rwlock/rwlock.rs index 063ab68..86005e3 100644 --- a/src/rwlock/rwlock.rs +++ b/src/rwlock/rwlock.rs @@ -4,8 +4,106 @@ use std::{cell::UnsafeCell, marker::PhantomData}; use lock_api::RawRwLock; use crate::key::Keyable; +use crate::lockable::{ + Lockable, LockableAsMut, LockableIntoInner, OwnedLockable, RawLock, Sharable, +}; -use super::{RwLock, RwLockReadGuard, RwLockReadRef, RwLockWriteGuard, RwLockWriteRef}; +use super::{PoisonFlag, RwLock, RwLockReadGuard, RwLockReadRef, RwLockWriteGuard, RwLockWriteRef}; + +unsafe impl RawLock for RwLock { + fn kill(&self) { + self.poison.poison(); + } + + unsafe fn raw_lock(&self) { + assert!( + !self.poison.is_poisoned(), + "The read-write lock has been killed" + ); + + self.raw.lock_exclusive() + } + + unsafe fn raw_try_lock(&self) -> bool { + if self.poison.is_poisoned() { + return false; + } + + self.raw.try_lock_exclusive() + } + + unsafe fn raw_unlock(&self) { + self.raw.unlock_exclusive() + } + + unsafe fn raw_read(&self) { + assert!( + !self.poison.is_poisoned(), + "The read-write lock has been killed" + ); + + self.raw.lock_shared() + } + + unsafe fn raw_try_read(&self) -> bool { + if self.poison.is_poisoned() { + return false; + } + + self.raw.try_lock_shared() + } + + unsafe fn raw_unlock_read(&self) { + self.raw.unlock_shared() + } +} + +unsafe impl Lockable for RwLock { + type Guard<'g> + = RwLockWriteRef<'g, T, R> + where + Self: 'g; + + type ReadGuard<'g> + = RwLockReadRef<'g, T, R> + where + Self: 'g; + + fn get_ptrs<'a>(&'a self, ptrs: &mut Vec<&'a dyn RawLock>) { + ptrs.push(self); + } + + unsafe fn guard(&self) -> Self::Guard<'_> { + RwLockWriteRef::new(self) + } + + unsafe fn read_guard(&self) -> Self::ReadGuard<'_> { + RwLockReadRef::new(self) + } +} + +impl LockableIntoInner for RwLock { + type Inner = T; + + fn into_inner(self) -> Self::Inner { + self.into_inner() + } +} + +impl LockableAsMut for RwLock { + type Inner<'a> + = &'a mut T + where + Self: 'a; + + fn as_mut(&mut self) -> Self::Inner<'_> { + AsMut::as_mut(self) + } +} + +unsafe impl Sharable for RwLock {} + +unsafe impl OwnedLockable for RwLock {} impl RwLock { /// Creates a new instance of an `RwLock` which is unlocked. @@ -21,6 +119,7 @@ impl RwLock { pub const fn new(data: T) -> Self { Self { data: UnsafeCell::new(data), + poison: PoisonFlag::new(), raw: R::INIT, } } @@ -142,7 +241,7 @@ impl RwLock { key: Key, ) -> RwLockReadGuard<'s, 'key, T, Key, R> { unsafe { - self.raw.lock_shared(); + self.raw_read(); // safety: the lock is locked first RwLockReadGuard::new(self, key) @@ -174,7 +273,7 @@ impl RwLock { key: Key, ) -> Option> { unsafe { - if self.raw.try_lock_shared() { + if self.raw_try_read() { // safety: the lock is locked first Some(RwLockReadGuard::new(self, key)) } else { @@ -186,7 +285,7 @@ impl RwLock { /// Attempts to create a shared lock without a key. Locking this without /// exclusive access to the key is undefined behavior. pub(crate) unsafe fn try_read_no_key(&self) -> Option> { - if self.raw.try_lock_shared() { + if self.raw_try_read() { // safety: the lock is locked first Some(RwLockReadRef(self, PhantomData)) } else { @@ -196,8 +295,9 @@ impl RwLock { /// Attempts to create an exclusive lock without a key. Locking this /// without exclusive access to the key is undefined behavior. + #[cfg(test)] pub(crate) unsafe fn try_write_no_key(&self) -> Option> { - if self.raw.try_lock_exclusive() { + if self.raw_try_lock() { // safety: the lock is locked first Some(RwLockWriteRef(self, PhantomData)) } else { @@ -235,7 +335,7 @@ impl RwLock { key: Key, ) -> RwLockWriteGuard<'s, 'key, T, Key, R> { unsafe { - self.raw.lock_exclusive(); + self.raw_lock(); // safety: the lock is locked first RwLockWriteGuard::new(self, key) @@ -268,7 +368,7 @@ impl RwLock { key: Key, ) -> Option> { unsafe { - if self.raw.try_lock_exclusive() { + if self.raw_try_lock() { // safety: the lock is locked first Some(RwLockWriteGuard::new(self, key)) } else { @@ -278,22 +378,11 @@ impl RwLock { } /// Returns `true` if the rwlock is currently locked in any way + #[cfg(test)] pub(crate) fn is_locked(&self) -> bool { self.raw.is_locked() } - /// Unlocks shared access on the `RwLock`. This is undefined behavior is - /// the data is still accessible. - pub(super) unsafe fn force_unlock_read(&self) { - self.raw.unlock_shared(); - } - - /// Unlocks exclusive access on the `RwLock`. This is undefined behavior is - /// the data is still accessible. - pub(super) unsafe fn force_unlock_write(&self) { - self.raw.unlock_exclusive(); - } - /// Immediately drops the guard, and consequently releases the shared lock. /// /// This function is equivalent to calling [`drop`] on the guard, except @@ -316,7 +405,7 @@ impl RwLock { guard: RwLockReadGuard<'_, 'key, T, Key, R>, ) -> Key { unsafe { - guard.rwlock.0.force_unlock_read(); + guard.rwlock.0.raw_unlock_read(); } guard.thread_key } @@ -344,7 +433,7 @@ impl RwLock { guard: RwLockWriteGuard<'_, 'key, T, Key, R>, ) -> Key { unsafe { - guard.rwlock.0.force_unlock_write(); + guard.rwlock.0.raw_unlock(); } guard.thread_key } diff --git a/src/rwlock/write_guard.rs b/src/rwlock/write_guard.rs index 9ffea39..31ed14a 100644 --- a/src/rwlock/write_guard.rs +++ b/src/rwlock/write_guard.rs @@ -5,6 +5,7 @@ use std::ops::{Deref, DerefMut}; use lock_api::RawRwLock; use crate::key::Keyable; +use crate::lockable::RawLock; use super::{RwLock, RwLockWriteGuard, RwLockWriteRef}; @@ -56,7 +57,7 @@ impl<'a, T: ?Sized + 'a, R: RawRwLock> Drop for RwLockWriteRef<'a, T, R> { fn drop(&mut self) { // safety: this guard is being destroyed, so the data cannot be // accessed without locking again - unsafe { self.0.force_unlock_write() } + unsafe { self.0.raw_unlock() } } } -- cgit v1.2.3