diff options
| author | Mica White <botahamec@outlook.com> | 2024-03-09 16:53:12 -0500 |
|---|---|---|
| committer | Mica White <botahamec@outlook.com> | 2024-03-09 16:53:12 -0500 |
| commit | 6a54884b292987fc1371bf062c42e964b6a4b0fe (patch) | |
| tree | 7b4c20c4150522b0d6d8e3f5db8fbe17eb80abc5 /src | |
| parent | 8ea16a606bfcc1ba535f6cef3cb4c162f91d2eb0 (diff) | |
Pointer checks
Diffstat (limited to 'src')
| -rw-r--r-- | src/collection.rs | 104 | ||||
| -rw-r--r-- | src/guard.rs | 60 | ||||
| -rw-r--r-- | src/lib.rs | 4 | ||||
| -rw-r--r-- | src/lockable.rs | 252 | ||||
| -rw-r--r-- | src/rwlock.rs | 4 |
5 files changed, 207 insertions, 217 deletions
diff --git a/src/collection.rs b/src/collection.rs new file mode 100644 index 0000000..809f340 --- /dev/null +++ b/src/collection.rs @@ -0,0 +1,104 @@ +use std::{ + marker::PhantomData, + ops::{Deref, DerefMut}, +}; + +use crate::{key::Keyable, lockable::Lockable}; + +fn contains_duplicates(l: &[usize]) -> bool { + for i in 0..l.len() { + for j in 0..l.len() { + if i != j && l[i] == l[j] { + return true; + } + } + } + + false +} + +pub struct LockCollection<L> { + collection: L, +} + +/// A guard for a generic [`Lockable`] type. +pub struct LockGuard<'a, 'key: 'a, L: Lockable<'a>, Key: Keyable + 'key> { + guard: L::Output, + key: Key, + _phantom: PhantomData<&'key ()>, +} + +impl<L> LockCollection<L> { + /// Creates a new collections of locks. + /// + /// # Safety + /// + /// This results in undefined behavior if any locks are presented twice + /// within this collection. + pub const unsafe fn new_unchecked(collection: L) -> Self { + Self { collection } + } +} + +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. + pub fn new(collection: L) -> Option<Self> { + let ptrs = collection.get_ptrs(); + if contains_duplicates(&ptrs) { + return None; + } + + Some(Self { collection }) + } + + /// Locks the lockable type and returns a guard that can be used to access + /// the underlying data. + pub fn lock<'key: 'a, Key: Keyable + 'key>(&'a self, key: Key) -> LockGuard<'a, 'key, L, Key> { + LockGuard { + // safety: we have the thread's key + guard: unsafe { self.collection.lock() }, + key, + _phantom: PhantomData, + } + } + + /// Attempts to lock the guard without blocking. + /// + /// If successful, this method returns a guard that can be used to access + /// the data. Otherwise, `None` is returned. + pub fn try_lock<'key: 'a, Key: Keyable + 'key>( + &'a self, + key: Key, + ) -> Option<LockGuard<'a, 'key, L, Key>> { + // safety: we have the thread's key + unsafe { self.collection.try_lock() }.map(|guard| LockGuard { + guard, + key, + _phantom: PhantomData, + }) + } + + /// Unlocks the underlying lockable data type, returning the key that's + /// associated with it. + #[allow(clippy::missing_const_for_fn)] + pub fn unlock<'key: 'a, Key: Keyable + 'key>(guard: LockGuard<'a, 'key, L, Key>) -> Key { + drop(guard.guard); + guard.key + } +} + +impl<'a, 'key: 'a, L: Lockable<'a>, Key: Keyable> Deref for LockGuard<'a, 'key, L, Key> { + type Target = L::Output; + + fn deref(&self) -> &Self::Target { + &self.guard + } +} + +impl<'a, 'key: 'a, L: Lockable<'a>, Key: Keyable> DerefMut for LockGuard<'a, 'key, L, Key> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.guard + } +} diff --git a/src/guard.rs b/src/guard.rs deleted file mode 100644 index 12021e2..0000000 --- a/src/guard.rs +++ /dev/null @@ -1,60 +0,0 @@ -use std::{ - marker::PhantomData, - ops::{Deref, DerefMut}, -}; - -use crate::{key::Keyable, lockable::Lockable}; - -/// A guard for a generic [`Lockable`] type. -pub struct LockGuard<'a, 'key: 'a, L: Lockable<'a>, Key: Keyable + 'key> { - guard: L::Output, - key: Key, - _phantom: PhantomData<&'key ()>, -} - -impl<'a, 'key: 'a, L: Lockable<'a>, Key: Keyable> LockGuard<'a, 'key, L, Key> { - /// Locks the lockable type and returns a guard that can be used to access - /// the underlying data. - pub fn lock(lock: &'a L, key: Key) -> Self { - Self { - // safety: we have the thread's key - guard: unsafe { lock.lock() }, - key, - _phantom: PhantomData, - } - } - - /// Attempts to lock the guard without blocking. If successful, this method - /// returns a guard that can be used to access the data. Otherwise, the key - /// is given back as an error. - pub fn try_lock(lock: &'a L, key: Key) -> Option<Self> { - // safety: we have the thread's key - unsafe { lock.try_lock() }.map(|guard| Self { - guard, - key, - _phantom: PhantomData, - }) - } - - /// Unlocks the underlying lockable data type, returning the key that's - /// associated with it. - #[allow(clippy::missing_const_for_fn)] - pub fn unlock(guard: Self) -> Key { - L::unlock(guard.guard); - guard.key - } -} - -impl<'a, 'key: 'a, L: Lockable<'a>, Key: Keyable> Deref for LockGuard<'a, 'key, L, Key> { - type Target = L::Output; - - fn deref(&self) -> &Self::Target { - &self.guard - } -} - -impl<'a, 'key: 'a, L: Lockable<'a>, Key: Keyable> DerefMut for LockGuard<'a, 'key, L, Key> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.guard - } -} @@ -4,14 +4,14 @@ #![allow(clippy::declare_interior_mutable_const)] #![allow(clippy::semicolon_if_nothing_returned)] -mod guard; +mod collection; mod key; mod lockable; pub mod mutex; pub mod rwlock; -pub use guard::LockGuard; +pub use collection::LockCollection; pub use key::{Key, ThreadKey}; pub use lockable::Lockable; pub use mutex::SpinLock; diff --git a/src/lockable.rs b/src/lockable.rs index 050f856..ddcc1c8 100644 --- a/src/lockable.rs +++ b/src/lockable.rs @@ -34,11 +34,16 @@ mod sealed { /// # Safety /// /// A deadlock must never occur. The `unlock` method must correctly unlock the -/// data. +/// data. The `get_ptrs` method must be implemented correctly. The `Output` +/// must be unlocked when it is dropped. pub unsafe trait Lockable<'a>: sealed::Sealed { /// 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 + fn get_ptrs(&self) -> Vec<usize>; + /// Blocks until the lock is acquired /// /// # Safety @@ -63,14 +68,15 @@ pub unsafe trait Lockable<'a>: sealed::Sealed { /// /// [`ThreadKey`]: `crate::key::ThreadKey` 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; + fn get_ptrs(&self) -> Vec<usize> { + (*self).get_ptrs() + } + unsafe fn lock(&'a self) -> Self::Output { (*self).lock() } @@ -78,16 +84,15 @@ unsafe impl<'a, T: Lockable<'a>> Lockable<'a> for &T { 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; + fn get_ptrs(&self) -> Vec<usize> { + (**self).get_ptrs() + } + unsafe fn lock(&'a self) -> Self::Output { (**self).lock() } @@ -95,16 +100,15 @@ unsafe impl<'a, T: Lockable<'a>> Lockable<'a> for &mut T { 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>; + fn get_ptrs(&self) -> Vec<usize> { + vec![self as *const Self as usize] + } + unsafe fn lock(&'a self) -> Self::Output { self.lock_no_key() } @@ -112,15 +116,15 @@ unsafe impl<'a, T: 'a, R: RawMutex + 'a> Lockable<'a> for Mutex<T, R> { unsafe fn try_lock(&'a self) -> Option<Self::Output> { self.try_lock_no_key() } - - fn unlock(guard: Self::Output) { - drop(guard); - } } unsafe impl<'a, T: 'a, R: RawRwLock + 'a> Lockable<'a> for RwLock<T, R> { type Output = RwLockWriteRef<'a, T, R>; + fn get_ptrs(&self) -> Vec<usize> { + vec![self as *const Self as usize] + } + unsafe fn lock(&'a self) -> Self::Output { self.write_no_key() } @@ -128,15 +132,15 @@ unsafe impl<'a, T: 'a, R: RawRwLock + 'a> Lockable<'a> for RwLock<T, R> { unsafe fn try_lock(&'a self) -> Option<Self::Output> { self.try_write_no_key() } - - fn unlock(guard: Self::Output) { - drop(guard); - } } unsafe impl<'a, T: 'a, R: RawRwLock + 'a> Lockable<'a> for ReadLock<'a, T, R> { type Output = RwLockReadRef<'a, T, R>; + fn get_ptrs(&self) -> Vec<usize> { + vec![self.0 as *const RwLock<T, R> as usize] + } + unsafe fn lock(&'a self) -> Self::Output { self.lock_no_key() } @@ -144,15 +148,15 @@ unsafe impl<'a, T: 'a, R: RawRwLock + 'a> Lockable<'a> for ReadLock<'a, T, R> { unsafe fn try_lock(&'a self) -> Option<Self::Output> { self.try_lock_no_key() } - - fn unlock(guard: Self::Output) { - drop(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.0 as *const RwLock<T, R> as usize] + } + unsafe fn lock(&'a self) -> Self::Output { self.lock_no_key() } @@ -160,15 +164,17 @@ unsafe impl<'a, T: 'a, R: RawRwLock + 'a> Lockable<'a> for WriteLock<'a, T, R> { unsafe fn try_lock(&'a self) -> Option<Self::Output> { self.try_lock_no_key() } - - fn unlock(guard: Self::Output) { - drop(guard); - } } unsafe impl<'a, A: Lockable<'a>> Lockable<'a> for (A,) { type Output = (A::Output,); + fn get_ptrs(&self) -> Vec<usize> { + let mut ptrs = Vec::with_capacity(1); + ptrs.append(&mut self.0.get_ptrs()); + ptrs + } + unsafe fn lock(&'a self) -> Self::Output { (self.0.lock(),) } @@ -176,20 +182,22 @@ unsafe impl<'a, A: Lockable<'a>> Lockable<'a> for (A,) { unsafe fn try_lock(&'a self) -> Option<Self::Output> { self.0.try_lock().map(|a| (a,)) } - - fn unlock(guard: Self::Output) { - A::unlock(guard.0); - } } 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 + } + unsafe fn lock(&'a self) -> Self::Output { loop { let lock0 = self.0.lock(); let Some(lock1) = self.1.try_lock() else { - A::unlock(lock0); continue; }; @@ -202,32 +210,31 @@ unsafe impl<'a, A: Lockable<'a>, B: Lockable<'a>> Lockable<'a> for (A, B) { return None; }; let Some(lock1) = self.1.try_lock() else { - A::unlock(lock0); return None; }; Some((lock0, lock1)) } - - fn unlock(guard: Self::Output) { - A::unlock(guard.0); - B::unlock(guard.1); - } } 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 { - A::unlock(lock0); continue; }; let Some(lock2) = self.2.try_lock() else { - A::unlock(lock0); - B::unlock(lock1); continue; }; @@ -240,23 +247,14 @@ unsafe impl<'a, A: Lockable<'a>, B: Lockable<'a>, C: Lockable<'a>> Lockable<'a> return None; }; let Some(lock1) = self.1.try_lock() else { - A::unlock(lock0); return None; }; let Some(lock2) = self.2.try_lock() else { - A::unlock(lock0); - B::unlock(lock1); return None; }; Some((lock0, lock1, lock2)) } - - fn unlock(guard: Self::Output) { - A::unlock(guard.0); - B::unlock(guard.1); - C::unlock(guard.2); - } } unsafe impl<'a, A: Lockable<'a>, B: Lockable<'a>, C: Lockable<'a>, D: Lockable<'a>> Lockable<'a> @@ -264,22 +262,25 @@ unsafe impl<'a, A: Lockable<'a>, B: Lockable<'a>, C: Lockable<'a>, D: Lockable<' { 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 { - A::unlock(lock0); continue; }; let Some(lock2) = self.2.try_lock() else { - A::unlock(lock0); - B::unlock(lock1); continue; }; let Some(lock3) = self.3.try_lock() else { - A::unlock(lock0); - B::unlock(lock1); - C::unlock(lock2); continue; }; @@ -292,30 +293,17 @@ unsafe impl<'a, A: Lockable<'a>, B: Lockable<'a>, C: Lockable<'a>, D: Lockable<' return None; }; let Some(lock1) = self.1.try_lock() else { - A::unlock(lock0); return None; }; let Some(lock2) = self.2.try_lock() else { - A::unlock(lock0); - B::unlock(lock1); return None; }; let Some(lock3) = self.3.try_lock() else { - A::unlock(lock0); - B::unlock(lock1); - C::unlock(lock2); return None; }; Some((lock0, lock1, lock2, lock3)) } - - fn unlock(guard: Self::Output) { - A::unlock(guard.0); - B::unlock(guard.1); - C::unlock(guard.2); - D::unlock(guard.3); - } } unsafe impl<'a, A: Lockable<'a>, B: Lockable<'a>, C: Lockable<'a>, D: Lockable<'a>, E: Lockable<'a>> @@ -323,29 +311,29 @@ unsafe impl<'a, A: Lockable<'a>, B: Lockable<'a>, C: Lockable<'a>, D: Lockable<' { 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 { - A::unlock(lock0); continue; }; let Some(lock2) = self.2.try_lock() else { - A::unlock(lock0); - B::unlock(lock1); continue; }; let Some(lock3) = self.3.try_lock() else { - A::unlock(lock0); - B::unlock(lock1); - C::unlock(lock2); continue; }; let Some(lock4) = self.4.try_lock() else { - A::unlock(lock0); - B::unlock(lock1); - C::unlock(lock2); - D::unlock(lock3); continue; }; @@ -358,38 +346,20 @@ unsafe impl<'a, A: Lockable<'a>, B: Lockable<'a>, C: Lockable<'a>, D: Lockable<' return None; }; let Some(lock1) = self.1.try_lock() else { - A::unlock(lock0); return None; }; let Some(lock2) = self.2.try_lock() else { - A::unlock(lock0); - B::unlock(lock1); return None; }; let Some(lock3) = self.3.try_lock() else { - A::unlock(lock0); - B::unlock(lock1); - C::unlock(lock2); return None; }; let Some(lock4) = self.4.try_lock() else { - A::unlock(lock0); - B::unlock(lock1); - C::unlock(lock2); - D::unlock(lock3); return None; }; Some((lock0, lock1, lock2, lock3, lock4)) } - - fn unlock(guard: Self::Output) { - A::unlock(guard.0); - B::unlock(guard.1); - C::unlock(guard.2); - D::unlock(guard.3); - E::unlock(guard.4); - } } unsafe impl< @@ -411,37 +381,33 @@ unsafe impl< 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 { - A::unlock(lock0); continue; }; let Some(lock2) = self.2.try_lock() else { - A::unlock(lock0); - B::unlock(lock1); continue; }; let Some(lock3) = self.3.try_lock() else { - A::unlock(lock0); - B::unlock(lock1); - C::unlock(lock2); continue; }; let Some(lock4) = self.4.try_lock() else { - A::unlock(lock0); - B::unlock(lock1); - C::unlock(lock2); - D::unlock(lock3); continue; }; let Some(lock5) = self.5.try_lock() else { - A::unlock(lock0); - B::unlock(lock1); - C::unlock(lock2); - D::unlock(lock3); - E::unlock(lock4); continue; }; @@ -454,52 +420,36 @@ unsafe impl< return None; }; let Some(lock1) = self.1.try_lock() else { - A::unlock(lock0); return None; }; let Some(lock2) = self.2.try_lock() else { - A::unlock(lock0); - B::unlock(lock1); return None; }; let Some(lock3) = self.3.try_lock() else { - A::unlock(lock0); - B::unlock(lock1); - C::unlock(lock2); return None; }; let Some(lock4) = self.4.try_lock() else { - A::unlock(lock0); - B::unlock(lock1); - C::unlock(lock2); - D::unlock(lock3); return None; }; let Some(lock5) = self.5.try_lock() else { - A::unlock(lock0); - B::unlock(lock1); - C::unlock(lock2); - D::unlock(lock3); - E::unlock(lock4); return None; }; Some((lock0, lock1, lock2, lock3, lock4, lock5)) } - - fn unlock(guard: Self::Output) { - A::unlock(guard.0); - B::unlock(guard.1); - C::unlock(guard.2); - D::unlock(guard.3); - E::unlock(guard.4); - F::unlock(guard.5); - } } unsafe impl<'a, T: Lockable<'a>, const N: usize> Lockable<'a> for [T; N] { type Output = [T::Output; N]; + fn get_ptrs(&self) -> Vec<usize> { + let mut ptrs = Vec::with_capacity(N); + 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], @@ -509,7 +459,7 @@ unsafe impl<'a, T: Lockable<'a>, const N: usize> Lockable<'a> for [T; N] { if i == upto { break; } - T::unlock(guard.assume_init()); + drop(guard.assume_init()); } } @@ -542,7 +492,7 @@ unsafe impl<'a, T: Lockable<'a>, const N: usize> Lockable<'a> for [T; N] { if i == upto { break; } - T::unlock(guard.assume_init()); + drop(guard.assume_init()); } } @@ -558,15 +508,19 @@ unsafe impl<'a, T: Lockable<'a>, const N: usize> Lockable<'a> for [T; N] { Some(outputs.map(|mu| mu.assume_init())) } - - fn unlock(guard: Self::Output) { - guard.map(T::unlock); - } } unsafe impl<'a, T: Lockable<'a>> Lockable<'a> for Vec<T> { type Output = Vec<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 + } + unsafe fn lock(&'a self) -> Self::Output { 'outer: loop { let mut outputs = Vec::with_capacity(self.len()); @@ -579,7 +533,6 @@ unsafe impl<'a, T: Lockable<'a>> Lockable<'a> for Vec<T> { if let Some(guard) = lock.try_lock() { outputs.push(guard); } else { - Self::unlock(outputs); continue 'outer; }; } @@ -594,17 +547,10 @@ unsafe impl<'a, T: Lockable<'a>> Lockable<'a> for Vec<T> { if let Some(guard) = lock.try_lock() { outputs.push(guard); } else { - Self::unlock(outputs); return None; }; } Some(outputs) } - - fn unlock(guard: Self::Output) { - for guard in guard { - T::unlock(guard); - } - } } diff --git a/src/rwlock.rs b/src/rwlock.rs index 16ad3c3..722ca2f 100644 --- a/src/rwlock.rs +++ b/src/rwlock.rs @@ -17,9 +17,9 @@ pub struct RwLock<T: ?Sized, R> { value: UnsafeCell<T>, } -pub struct ReadLock<'a, T: ?Sized, R>(&'a RwLock<T, R>); +pub struct ReadLock<'a, T: ?Sized, R>(pub(crate) &'a RwLock<T, R>); -pub struct WriteLock<'a, T: ?Sized, R>(&'a RwLock<T, R>); +pub struct WriteLock<'a, T: ?Sized, R>(pub(crate) &'a RwLock<T, R>); pub struct RwLockReadRef<'a, T: ?Sized, R: RawRwLock>(&'a RwLock<T, R>); |
