From 0c519b6c7801aa6a085551c8e144f0336e615870 Mon Sep 17 00:00:00 2001 From: Mica White Date: Sun, 10 Mar 2024 18:13:20 -0400 Subject: Better librarification --- src/collection.rs | 129 ++++++++++++++++++++++++++++++++++++++++++++++++------ src/key.rs | 11 ++--- src/lib.rs | 9 +++- src/lockable.rs | 79 +++++++++++++++++++++------------ src/mutex.rs | 29 ++++++++++-- src/rwlock.rs | 79 +++++++++++++++++++++++++++++---- 6 files changed, 275 insertions(+), 61 deletions(-) (limited to 'src') diff --git a/src/collection.rs b/src/collection.rs index 34de620..1ee5956 100644 --- a/src/collection.rs +++ b/src/collection.rs @@ -1,7 +1,5 @@ -use std::{ - marker::PhantomData, - ops::{Deref, DerefMut}, -}; +use std::marker::PhantomData; +use std::ops::{Deref, DerefMut}; use crate::{ key::Keyable, @@ -9,6 +7,7 @@ use crate::{ }; /// 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() { @@ -26,6 +25,7 @@ fn contains_duplicates(l: &[usize]) -> bool { /// 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 { collection: L, } @@ -37,15 +37,81 @@ pub struct LockGuard<'a, 'key: 'a, L: Lockable<'a>, Key: Keyable + 'key> { _phantom: PhantomData<&'key ()>, } -impl LockCollection { - /// 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: OwnedLockable<'a>> From for LockCollection { + fn from(value: L) -> Self { + Self::new(value) + } +} + +impl<'a, L: OwnedLockable<'a>> AsRef for LockCollection { + fn as_ref(&self) -> &L { + &self.collection + } +} + +impl<'a, L: OwnedLockable<'a>> AsMut for LockCollection { + fn as_mut(&mut self) -> &mut L { + &mut self.collection + } +} + +impl<'a, L: OwnedLockable<'a>> AsRef for LockCollection { + fn as_ref(&self) -> &Self { + self + } +} + +impl<'a, L: OwnedLockable<'a>> AsMut for LockCollection { + fn as_mut(&mut self) -> &mut Self { + self + } +} + +impl IntoIterator for LockCollection { + type Item = L::Item; + type IntoIter = L::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.collection.into_iter() + } +} + +impl<'a, L> IntoIterator for &'a LockCollection +where + &'a L: IntoIterator, +{ + type Item = <&'a L as IntoIterator>::Item; + type IntoIter = <&'a L as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.collection.into_iter() + } +} + +impl<'a, L> IntoIterator for &'a mut LockCollection +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.collection.into_iter() + } +} + +impl<'a, L: OwnedLockable<'a>, I: FromIterator + OwnedLockable<'a>> FromIterator + for LockCollection +{ + fn from_iter>(iter: T) -> Self { + let iter: I = iter.into_iter().collect(); + Self::new(iter) + } +} + +impl<'a, E: OwnedLockable<'a> + Extend, L: OwnedLockable<'a>> Extend for LockCollection { + fn extend>(&mut self, iter: T) { + self.collection.extend(iter) } } @@ -54,6 +120,7 @@ impl<'a, L: OwnedLockable<'a>> LockCollection { /// /// Because the locks are owned, there's no need to do any checks for /// duplicate values. + #[must_use] pub const fn new(collection: L) -> Self { Self { collection } } @@ -62,11 +129,25 @@ impl<'a, L: OwnedLockable<'a>> LockCollection { /// /// Because the locks are owned, there's no need to do any checks for /// duplicate values. + #[must_use] pub const fn new_ref(collection: &L) -> LockCollection<&L> { LockCollection { collection } } } +impl LockCollection { + /// Creates a new collections of locks. + /// + /// # Safety + /// + /// This results in undefined behavior if any locks are presented twice + /// within this collection. + #[must_use] + pub const unsafe fn new_unchecked(collection: L) -> Self { + Self { collection } + } +} + impl<'a, L: Lockable<'a>> LockCollection { /// Creates a new collection of locks. /// @@ -78,6 +159,7 @@ impl<'a, L: Lockable<'a>> LockCollection { /// 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. + #[must_use] pub fn try_new(collection: L) -> Option { let ptrs = collection.get_ptrs(); if contains_duplicates(&ptrs) { @@ -123,6 +205,27 @@ impl<'a, L: Lockable<'a>> LockCollection { } } +impl<'a, L: 'a> LockCollection +where + &'a L: IntoIterator, +{ + /// Returns an iterator over references to each value in the collection. + pub fn iter(&'a self) -> <&'a L as IntoIterator>::IntoIter { + self.into_iter() + } +} + +impl<'a, L: 'a> LockCollection +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() + } +} + impl<'a, 'key: 'a, L: Lockable<'a>, Key: Keyable> Deref for LockGuard<'a, 'key, L, Key> { type Target = L::Output; diff --git a/src/key.rs b/src/key.rs index 0297bc1..d951154 100644 --- a/src/key.rs +++ b/src/key.rs @@ -6,9 +6,10 @@ use once_cell::sync::Lazy; use thread_local::ThreadLocal; use self::sealed::Sealed; +use super::ThreadKey; mod sealed { - use super::ThreadKey; + use crate::ThreadKey; pub trait Sealed {} impl Sealed for ThreadKey {} impl Sealed for &mut ThreadKey {} @@ -16,12 +17,7 @@ mod sealed { static KEY: Lazy> = Lazy::new(ThreadLocal::new); -/// The key for the current thread. -/// -/// Only one of these exist per thread. To get the current thread's key, call -/// [`ThreadKey::lock`]. If the `ThreadKey` is dropped, it can be reobtained. -pub type ThreadKey = Key<'static>; - +/// A key that can be obtained and dropped pub struct Key<'a> { phantom: PhantomData<*const ()>, // implement !Send and !Sync lock: &'a AtomicLock, @@ -85,6 +81,7 @@ impl AtomicLock { /// /// This is not a fair lock. It is not recommended to call this function /// repeatedly in a loop. + #[must_use] pub fn try_lock(&self) -> Option { // safety: we just acquired the lock (!self.is_locked.swap(true, Ordering::Acquire)).then_some(Key { diff --git a/src/lib.rs b/src/lib.rs index 25b6d03..e99db7c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,17 +5,22 @@ #![allow(clippy::semicolon_if_nothing_returned)] mod collection; -mod key; mod lockable; +pub mod key; pub mod mutex; pub mod rwlock; pub use collection::LockCollection; -pub use key::{Key, ThreadKey}; pub use lockable::Lockable; pub use mutex::SpinLock; +/// The key for the current thread. +/// +/// Only one of these exist per thread. To get the current thread's key, call +/// [`ThreadKey::lock`]. If the `ThreadKey` is dropped, it can be reobtained. +pub type ThreadKey = key::Key<'static>; + /// A mutual exclusion primitive useful for protecting shared data, which cannot deadlock. /// /// By default, this uses `parking_lot` as a backend. diff --git a/src/lockable.rs b/src/lockable.rs index 3c217c4..d3367fa 100644 --- a/src/lockable.rs +++ b/src/lockable.rs @@ -7,28 +7,6 @@ use crate::{ use lock_api::{RawMutex, RawRwLock}; -mod sealed { - use super::Lockable as L; - #[allow(clippy::wildcard_imports)] - use super::*; - - pub trait Sealed {} - impl<'a, T, R: RawMutex + 'a> Sealed for Mutex {} - impl<'a, T, R: RawRwLock + 'a> Sealed for RwLock {} - impl<'a, T, R: RawRwLock + 'a> Sealed for ReadLock<'a, T, R> {} - impl<'a, T, R: RawRwLock + 'a> Sealed for WriteLock<'a, T, R> {} - impl Sealed for &T {} - impl Sealed for &mut T {} - impl<'a, A: L<'a>> Sealed for (A,) {} - impl<'a, A: L<'a>, B: L<'a>> Sealed for (A, B) {} - impl<'a, A: L<'a>, B: L<'a>, C: L<'a>> Sealed for (A, B, C) {} - impl<'a, A: L<'a>, B: L<'a>, C: L<'a>, D: L<'a>> Sealed for (A, B, C, D) {} - impl<'a, A: L<'a>, B: L<'a>, C: L<'a>, D: L<'a>, E: L<'a>> Sealed for (A, B, C, D, E) {} - impl<'a, A: L<'a>, B: L<'a>, C: L<'a>, D: L<'a>, E: L<'a>, F: L<'a>> Sealed for (A, B, C, D, E, F) {} - impl<'a, T: Lockable<'a>, const N: usize> Sealed for [T; N] {} - impl<'a, T: Lockable<'a>> Sealed for Vec {} -} - /// A type that may be locked and unlocked, and is known to be the only valid /// instance of the lock. /// @@ -45,12 +23,13 @@ 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>: sealed::Sealed { +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; /// Blocks until the lock is acquired @@ -62,7 +41,7 @@ pub unsafe trait Lockable<'a>: sealed::Sealed { /// which should last as long as the return value is alive. /// * Call this on multiple locks without unlocking first. /// - /// [`ThreadKey`]: `crate::key::ThreadKey` + /// [`ThreadKey`]: `crate::ThreadKey` unsafe fn lock(&'a self) -> Self::Output; /// Attempt to lock without blocking. @@ -75,7 +54,7 @@ pub unsafe trait Lockable<'a>: sealed::Sealed { /// access to the [`ThreadKey`], which should last as long as the return /// value is alive. /// - /// [`ThreadKey`]: `crate::key::ThreadKey` + /// [`ThreadKey`]: `crate::ThreadKey` unsafe fn try_lock(&'a self) -> Option; } @@ -153,7 +132,7 @@ 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 { - vec![self.0 as *const RwLock as usize] + vec![self.as_ref() as *const RwLock as usize] } unsafe fn lock(&'a self) -> Self::Output { @@ -169,7 +148,7 @@ 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 { - vec![self.0 as *const RwLock as usize] + vec![self.as_ref() as *const RwLock as usize] } unsafe fn lock(&'a self) -> Self::Output { @@ -562,6 +541,51 @@ unsafe impl<'a, T: Lockable<'a>, const N: usize> Lockable<'a> for [T; N] { } } +unsafe impl<'a, T: Lockable<'a>> Lockable<'a> for Box<[T]> { + type Output = Box<[T::Output]>; + + fn get_ptrs(&self) -> Vec { + 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()); + if self.is_empty() { + return outputs.into_boxed_slice(); + } + + outputs.push(self[0].lock()); + for lock in self.iter().skip(1) { + if let Some(guard) = lock.try_lock() { + outputs.push(guard); + } else { + continue 'outer; + }; + } + + return outputs.into_boxed_slice(); + } + } + + unsafe fn try_lock(&'a self) -> Option { + let mut outputs = Vec::with_capacity(self.len()); + for lock in &**self { + if let Some(guard) = lock.try_lock() { + outputs.push(guard); + } else { + return None; + }; + } + + Some(outputs.into_boxed_slice()) + } +} + unsafe impl<'a, T: Lockable<'a>> Lockable<'a> for Vec { type Output = Vec; @@ -608,4 +632,5 @@ unsafe impl<'a, T: Lockable<'a>> Lockable<'a> for Vec { } unsafe impl<'a, T: OwnedLockable<'a>, const N: usize> OwnedLockable<'a> for [T; N] {} +unsafe impl<'a, T: OwnedLockable<'a>> OwnedLockable<'a> for Box<[T]> {} unsafe impl<'a, T: OwnedLockable<'a>> OwnedLockable<'a> for Vec {} diff --git a/src/mutex.rs b/src/mutex.rs index c78d398..e7a439c 100644 --- a/src/mutex.rs +++ b/src/mutex.rs @@ -1,4 +1,5 @@ use std::cell::UnsafeCell; +use std::fmt::Debug; use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; @@ -97,6 +98,7 @@ impl<'a, 'key: 'a, T: ?Sized + 'a, Key: Keyable, R: RawMutex> DerefMut impl<'a, 'key: 'a, T: ?Sized + 'a, Key: Keyable, R: RawMutex> MutexGuard<'a, 'key, T, Key, R> { /// Create a guard to the given mutex. Undefined if multiple guards to the /// same mutex exist at once. + #[must_use] const unsafe fn new(mutex: &'a Mutex, thread_key: Key) -> Self { Self { mutex: MutexRef(mutex), @@ -116,6 +118,7 @@ impl Mutex { /// /// let mutex = Mutex::new(0); /// ``` + #[must_use] pub const fn new(value: T) -> Self { Self { raw: R::INIT, @@ -124,6 +127,24 @@ impl Mutex { } } +impl Debug for Mutex { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&format!("Mutex<{}>", std::any::type_name::())) + } +} + +impl From for Mutex { + fn from(value: T) -> Self { + Self::new(value) + } +} + +impl AsMut for Mutex { + fn as_mut(&mut self) -> &mut T { + self.get_mut() + } +} + impl Mutex { /// Consumes this mutex, returning the underlying data. /// @@ -134,7 +155,8 @@ impl Mutex { /// /// let mutex = Mutex::new(0); /// assert_eq!(mutex.into_inner(), 0); - /// ```` + /// ``` + #[must_use] pub fn into_inner(self) -> T { self.value.into_inner() } @@ -155,7 +177,8 @@ impl Mutex { /// let mut mutex = Mutex::new(0); /// *mutex.get_mut() = 10; /// assert_eq!(*mutex.lock(key), 10); - /// ```` + /// ``` + #[must_use] pub fn get_mut(&mut self) -> &mut T { self.value.get_mut() } @@ -283,4 +306,4 @@ impl Mutex { } unsafe impl Send for Mutex {} -unsafe impl Sync for Mutex {} +unsafe impl Sync for Mutex {} diff --git a/src/rwlock.rs b/src/rwlock.rs index 722ca2f..f5f0f2b 100644 --- a/src/rwlock.rs +++ b/src/rwlock.rs @@ -1,8 +1,7 @@ -use std::{ - cell::UnsafeCell, - marker::PhantomData, - ops::{Deref, DerefMut}, -}; +use std::cell::UnsafeCell; +use std::fmt::Debug; +use std::marker::PhantomData; +use std::ops::{Deref, DerefMut}; use lock_api::RawRwLock; @@ -17,9 +16,9 @@ pub struct RwLock { value: UnsafeCell, } -pub struct ReadLock<'a, T: ?Sized, R>(pub(crate) &'a RwLock); +pub struct ReadLock<'a, T: ?Sized, R>(&'a RwLock); -pub struct WriteLock<'a, T: ?Sized, R>(pub(crate) &'a RwLock); +pub struct WriteLock<'a, T: ?Sized, R>(&'a RwLock); pub struct RwLockReadRef<'a, T: ?Sized, R: RawRwLock>(&'a RwLock); @@ -37,6 +36,9 @@ pub struct RwLockWriteGuard<'a, 'key, T: ?Sized, Key: Keyable + 'key, R: RawRwLo _phantom: PhantomData<&'key ()>, } +unsafe impl Send for RwLock {} +unsafe impl Sync for RwLock {} + impl<'a, T: ?Sized + 'a, R: RawRwLock> Deref for RwLockReadRef<'a, T, R> { type Target = T; @@ -117,6 +119,7 @@ impl<'a, 'key: 'a, T: ?Sized + 'a, Key: Keyable, R: RawRwLock> { /// Create a guard to the given mutex. Undefined if multiple guards to the /// same mutex exist at once. + #[must_use] const unsafe fn new(rwlock: &'a RwLock, thread_key: Key) -> Self { Self { rwlock: RwLockReadRef(rwlock), @@ -131,6 +134,7 @@ impl<'a, 'key: 'a, T: ?Sized + 'a, Key: Keyable, R: RawRwLock> { /// Create a guard to the given mutex. Undefined if multiple guards to the /// same mutex exist at once. + #[must_use] const unsafe fn new(rwlock: &'a RwLock, thread_key: Key) -> Self { Self { rwlock: RwLockWriteRef(rwlock), @@ -141,6 +145,7 @@ impl<'a, 'key: 'a, T: ?Sized + 'a, Key: Keyable, R: RawRwLock> } impl RwLock { + #[must_use] pub const fn new(value: T) -> Self { Self { value: UnsafeCell::new(value), @@ -149,6 +154,60 @@ impl RwLock { } } +impl Debug for RwLock { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&format!("RwLock<{}>", std::any::type_name::())) + } +} + +impl From for RwLock { + fn from(value: T) -> Self { + Self::new(value) + } +} + +impl AsMut for RwLock { + fn as_mut(&mut self) -> &mut T { + self.get_mut() + } +} + +impl<'a, T: ?Sized, R> Debug for ReadLock<'a, T, R> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&format!("ReadLock<{}>", std::any::type_name::())) + } +} + +impl<'a, T: ?Sized, R> From<&'a RwLock> for ReadLock<'a, T, R> { + fn from(value: &'a RwLock) -> Self { + Self::new(value) + } +} + +impl<'a, T: ?Sized, R> AsRef> for ReadLock<'a, T, R> { + fn as_ref(&self) -> &RwLock { + self.0 + } +} + +impl<'a, T: ?Sized, R> Debug for WriteLock<'a, T, R> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&format!("WriteLock<{}>", std::any::type_name::())) + } +} + +impl<'a, T: ?Sized, R> From<&'a RwLock> for WriteLock<'a, T, R> { + fn from(value: &'a RwLock) -> Self { + Self::new(value) + } +} + +impl<'a, T: ?Sized, R> AsRef> for WriteLock<'a, T, R> { + fn as_ref(&self) -> &RwLock { + self.0 + } +} + impl RwLock { pub fn into_inner(self) -> T { self.value.into_inner() @@ -273,7 +332,8 @@ impl RwLock { } } -impl<'a, T, R> ReadLock<'a, T, R> { +impl<'a, T: ?Sized, R> ReadLock<'a, T, R> { + #[must_use] pub const fn new(rwlock: &'a RwLock) -> Self { Self(rwlock) } @@ -307,7 +367,8 @@ impl<'a, T: ?Sized, R: RawRwLock> ReadLock<'a, T, R> { } } -impl<'a, T, R> WriteLock<'a, T, R> { +impl<'a, T: ?Sized, R> WriteLock<'a, T, R> { + #[must_use] pub const fn new(rwlock: &'a RwLock) -> Self { Self(rwlock) } -- cgit v1.2.3