From 878f4fae4d3c6e64ab3824bf3fc012fbb5293a21 Mon Sep 17 00:00:00 2001 From: Botahamec Date: Wed, 22 May 2024 20:59:09 -0400 Subject: Documentation --- src/collection.rs | 2 + src/collection/boxed.rs | 231 +++++++++++++++++++++++++++++++ src/collection/owned.rs | 156 +++++++++++++++++++++ src/collection/ref.rs | 116 ++++++++++++++-- src/collection/retry.rs | 248 +++++++++++++++++++++++++++++++++- src/lockable.rs | 336 ++++------------------------------------------ src/mutex/guard.rs | 4 +- src/rwlock/read_guard.rs | 4 +- src/rwlock/read_lock.rs | 2 +- src/rwlock/write_guard.rs | 4 +- 10 files changed, 777 insertions(+), 326 deletions(-) (limited to 'src') diff --git a/src/collection.rs b/src/collection.rs index c51e3cf..5dc6946 100644 --- a/src/collection.rs +++ b/src/collection.rs @@ -103,6 +103,8 @@ pub struct RetryingLockCollection { } /// A RAII guard for a generic [`Lockable`] type. +/// +/// [`Lockable`]: `crate::lockable::Lockable` pub struct LockGuard<'key, Guard, Key: Keyable + 'key> { guard: Guard, key: Key, diff --git a/src/collection/boxed.rs b/src/collection/boxed.rs index ea840ab..224eedb 100644 --- a/src/collection/boxed.rs +++ b/src/collection/boxed.rs @@ -119,6 +119,19 @@ impl From for BoxedLockCollection { } impl BoxedLockCollection { + /// 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::{Mutex, LockCollection}; + /// + /// let data = (Mutex::new(0), Mutex::new("")); + /// let lock = LockCollection::new(data); + /// ``` #[must_use] pub fn new(data: L) -> Self { // safety: owned lockable types cannot contain duplicates @@ -127,6 +140,19 @@ impl BoxedLockCollection { } impl<'a, L: OwnedLockable> BoxedLockCollection<&'a 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::{Mutex, LockCollection}; + /// + /// let data = (Mutex::new(0), Mutex::new("")); + /// let lock = LockCollection::new_ref(&data); + /// ``` #[must_use] pub fn new_ref(data: &'a L) -> Self { // safety: owned lockable types cannot contain duplicates @@ -135,11 +161,31 @@ impl<'a, L: OwnedLockable> BoxedLockCollection<&'a L> { } impl BoxedLockCollection { + /// Creates a new collections of locks. + /// + /// # Safety + /// + /// This results in undefined behavior if any locks are presented twice + /// within this collection. + /// + /// # Examples + /// + /// ``` + /// use happylock::{Mutex, LockCollection}; + /// + /// let data1 = Mutex::new(0); + /// let data2 = Mutex::new(""); + /// + /// // safety: data1 and data2 refer to distinct mutexes + /// let data = (&data1, &data2); + /// let lock = unsafe { LockCollection::new_unchecked(&data) }; + /// ``` #[must_use] pub unsafe fn new_unchecked(data: L) -> Self { let data = Box::new(data); let mut locks = Vec::new(); data.get_ptrs(&mut locks); + locks.sort_by_key(|lock| std::ptr::from_ref(*lock).cast::<()>() as usize); // safety: the box will be dropped after the lock references, so it's // safe to just pretend they're static @@ -147,6 +193,23 @@ impl BoxedLockCollection { Self { data, locks } } + /// Creates a new collection of locks. + /// + /// This returns `None` if any locks are found twice in the given + /// collection. + /// + /// # Examples + /// + /// ``` + /// use happylock::{Mutex, LockCollection}; + /// + /// let data1 = Mutex::new(0); + /// let data2 = Mutex::new(""); + /// + /// // data1 and data2 refer to distinct mutexes, so this won't panic + /// let data = (&data1, &data2); + /// let lock = LockCollection::try_new(&data).unwrap(); + /// ``` #[must_use] pub fn try_new(data: L) -> Option { // safety: we are checking for duplicates before returning @@ -159,11 +222,48 @@ impl BoxedLockCollection { } } + /// Gets the underlying collection, consuming this collection. + /// + /// # Examples + /// + /// ``` + /// use happylock::{Mutex, ThreadKey, LockCollection}; + /// + /// let data1 = Mutex::new(42); + /// let data2 = Mutex::new(""); + /// + /// // data1 and data2 refer to distinct mutexes, so this won't panic + /// let data = (&data1, &data2); + /// let lock = LockCollection::try_new(&data).unwrap(); + /// + /// let key = ThreadKey::get().unwrap(); + /// let guard = lock.into_inner().0.lock(key); + /// assert_eq!(*guard, 42); + /// ``` #[must_use] pub fn into_inner(self) -> Box { self.data } + /// Locks the collection + /// + /// This function returns a guard that can be used to access the underlying + /// data. When the guard is dropped, the locks in the collection are also + /// dropped. + /// + /// # Examples + /// + /// ``` + /// use happylock::{Mutex, ThreadKey, LockCollection}; + /// + /// let key = ThreadKey::get().unwrap(); + /// let data = (Mutex::new(0), Mutex::new("")); + /// let lock = LockCollection::new(data); + /// + /// let mut guard = lock.lock(key); + /// *guard.0 += 1; + /// *guard.1 = "1"; + /// ``` pub fn lock<'g, 'key: 'g, Key: Keyable + 'key>( &'g self, key: Key, @@ -181,6 +281,30 @@ impl BoxedLockCollection { } } + /// Attempts to lock the without blocking. + /// + /// If successful, this method returns a guard that can be used to access + /// the data, and unlocks the data when it is dropped. Otherwise, `None` is + /// returned. + /// + /// # Examples + /// + /// ``` + /// use happylock::{Mutex, ThreadKey, LockCollection}; + /// + /// let key = ThreadKey::get().unwrap(); + /// let data = (Mutex::new(0), Mutex::new("")); + /// let lock = LockCollection::new(data); + /// + /// match lock.try_lock(key) { + /// Some(mut guard) => { + /// *guard.0 += 1; + /// *guard.1 = "1"; + /// }, + /// None => unreachable!(), + /// }; + /// + /// ``` pub fn try_lock<'g, 'key: 'g, Key: Keyable + 'key>( &'g self, key: Key, @@ -210,6 +334,23 @@ impl BoxedLockCollection { }) } + /// Unlocks the underlying lockable data type, returning the key that's + /// associated with it. + /// + /// # Examples + /// + /// ``` + /// use happylock::{Mutex, ThreadKey, LockCollection}; + /// + /// let key = ThreadKey::get().unwrap(); + /// let data = (Mutex::new(0), Mutex::new("")); + /// let lock = LockCollection::new(data); + /// + /// let mut guard = lock.lock(key); + /// *guard.0 += 1; + /// *guard.1 = "1"; + /// let key = LockCollection::<(Mutex, Mutex<&str>)>::unlock(guard); + /// ``` pub fn unlock<'key, Key: Keyable + 'key>(guard: LockGuard<'key, L::Guard<'_>, Key>) -> Key { drop(guard.guard); guard.key @@ -217,6 +358,25 @@ impl BoxedLockCollection { } impl BoxedLockCollection { + /// Locks the collection, so that other threads can still read from it + /// + /// This function returns a guard that can be used to access the underlying + /// data immutably. When the guard is dropped, the locks in the collection + /// are also dropped. + /// + /// # Examples + /// + /// ``` + /// use happylock::{RwLock, ThreadKey, LockCollection}; + /// + /// let key = ThreadKey::get().unwrap(); + /// let data = (RwLock::new(0), RwLock::new("")); + /// let lock = LockCollection::new(data); + /// + /// let mut guard = lock.read(key); + /// assert_eq!(*guard.0, 0); + /// assert_eq!(*guard.1, ""); + /// ``` pub fn read<'g, 'key: 'g, Key: Keyable + 'key>( &'g self, key: Key, @@ -234,6 +394,31 @@ impl BoxedLockCollection { } } + /// Attempts to lock the without blocking, in such a way that other threads + /// can still read from the collection. + /// + /// If successful, this method returns a guard that can be used to access + /// the data immutably, and unlocks the data when it is dropped. Otherwise, + /// `None` is returned. + /// + /// # Examples + /// + /// ``` + /// use happylock::{RwLock, ThreadKey, LockCollection}; + /// + /// let key = ThreadKey::get().unwrap(); + /// let data = (RwLock::new(5), RwLock::new("6")); + /// let lock = LockCollection::new(data); + /// + /// match lock.try_read(key) { + /// Some(mut guard) => { + /// assert_eq!(*guard.0, 5); + /// assert_eq!(*guard.1, "6"); + /// }, + /// None => unreachable!(), + /// }; + /// + /// ``` pub fn try_read<'g, 'key: 'g, Key: Keyable + 'key>( &'g self, key: Key, @@ -263,6 +448,21 @@ impl BoxedLockCollection { }) } + /// Unlocks the underlying lockable data type, returning the key that's + /// associated with it. + /// + /// # Examples + /// + /// ``` + /// use happylock::{RwLock, ThreadKey, LockCollection}; + /// + /// let key = ThreadKey::get().unwrap(); + /// let data = (RwLock::new(0), RwLock::new("")); + /// let lock = LockCollection::new(data); + /// + /// let mut guard = lock.read(key); + /// let key = LockCollection::<(RwLock, RwLock<&str>)>::unlock_read(guard); + /// ``` pub fn unlock_read<'key, Key: Keyable + 'key>( guard: LockGuard<'key, L::ReadGuard<'_>, Key>, ) -> Key { @@ -276,6 +476,22 @@ where &'a L: IntoIterator, { /// Returns an iterator over references to each value in the collection. + /// + /// # Examples + /// + /// ``` + /// use happylock::{Mutex, ThreadKey, LockCollection}; + /// + /// let key = ThreadKey::get().unwrap(); + /// let data = [Mutex::new(26), Mutex::new(1)]; + /// let lock = LockCollection::new(data); + /// + /// let mut iter = lock.iter(); + /// let mutex = iter.next().unwrap(); + /// let guard = mutex.lock(key); + /// + /// assert_eq!(*guard, 26); + /// ``` #[must_use] pub fn iter(&'a self) -> <&'a L as IntoIterator>::IntoIter { self.into_iter() @@ -288,6 +504,21 @@ where { /// Returns an iterator over mutable references to each value in the /// collection. + /// + /// # Examples + /// + /// ``` + /// use happylock::{Mutex, ThreadKey, LockCollection}; + /// + /// let key = ThreadKey::get().unwrap(); + /// let data = [Mutex::new(26), Mutex::new(1)]; + /// let mut lock = LockCollection::new(data); + /// + /// let mut iter = lock.iter_mut(); + /// let mutex = iter.next().unwrap(); + /// + /// assert_eq!(*mutex.as_mut(), 26); + /// ``` #[must_use] pub fn iter_mut(&'a mut self) -> <&'a mut L as IntoIterator>::IntoIter { self.into_iter() diff --git a/src/collection/owned.rs b/src/collection/owned.rs index d77d568..e1549b2 100644 --- a/src/collection/owned.rs +++ b/src/collection/owned.rs @@ -79,16 +79,67 @@ impl From for OwnedLockCollection { } impl OwnedLockCollection { + /// Creates a new collection of owned locks. + /// + /// Because the locks are owned, there's no need to do any checks for + /// duplicate values. The locks also don't need to be sorted by memory + /// address because they aren't used anywhere else. + /// + /// # Examples + /// + /// ``` + /// use happylock::Mutex; + /// use happylock::collection::OwnedLockCollection; + /// + /// let data = (Mutex::new(0), Mutex::new("")); + /// let lock = OwnedLockCollection::new(data); + /// ``` #[must_use] pub const fn new(data: L) -> Self { Self { data } } + /// Gets the underlying collection, consuming this collection. + /// + /// # Examples + /// + /// ``` + /// use happylock::{Mutex, ThreadKey}; + /// use happylock::collection::OwnedLockCollection; + /// + /// let data = (Mutex::new(42), Mutex::new("")); + /// let lock = OwnedLockCollection::new(data); + /// + /// let key = ThreadKey::get().unwrap(); + /// let inner = lock.into_inner(); + /// let guard = inner.0.lock(key); + /// assert_eq!(*guard, 42); + /// ``` #[must_use] pub fn into_inner(self) -> L { self.data } + /// Locks the collection + /// + /// This function returns a guard that can be used to access the underlying + /// data. When the guard is dropped, the locks in the collection are also + /// dropped. + /// + /// # Examples + /// + /// ``` + /// use happylock::{Mutex, ThreadKey}; + /// use happylock::collection::OwnedLockCollection; + /// + /// let key = ThreadKey::get().unwrap(); + /// let data = (Mutex::new(0), Mutex::new("")); + /// let lock = OwnedLockCollection::new(data); + /// + /// let mut guard = lock.lock(key); + /// *guard.0 += 1; + /// *guard.1 = "1"; + /// ``` pub fn lock<'g, 'key, Key: Keyable + 'key>( &'g self, key: Key, @@ -109,6 +160,31 @@ impl OwnedLockCollection { } } + /// Attempts to lock the without blocking. + /// + /// If successful, this method returns a guard that can be used to access + /// the data, and unlocks the data when it is dropped. Otherwise, `None` is + /// returned. + /// + /// # Examples + /// + /// ``` + /// use happylock::{Mutex, ThreadKey}; + /// use happylock::collection::OwnedLockCollection; + /// + /// let key = ThreadKey::get().unwrap(); + /// let data = (Mutex::new(0), Mutex::new("")); + /// let lock = OwnedLockCollection::new(data); + /// + /// match lock.try_lock(key) { + /// Some(mut guard) => { + /// *guard.0 += 1; + /// *guard.1 = "1"; + /// }, + /// None => unreachable!(), + /// }; + /// + /// ``` pub fn try_lock<'g, 'key: 'g, Key: Keyable + 'key>( &'g self, key: Key, @@ -139,6 +215,24 @@ impl OwnedLockCollection { }) } + /// Unlocks the underlying lockable data type, returning the key that's + /// associated with it. + /// + /// # Examples + /// + /// ``` + /// use happylock::{Mutex, ThreadKey}; + /// use happylock::collection::OwnedLockCollection; + /// + /// let key = ThreadKey::get().unwrap(); + /// let data = (Mutex::new(0), Mutex::new("")); + /// let lock = OwnedLockCollection::new(data); + /// + /// let mut guard = lock.lock(key); + /// *guard.0 += 1; + /// *guard.1 = "1"; + /// let key = OwnedLockCollection::<(Mutex, Mutex<&str>)>::unlock(guard); + /// ``` #[allow(clippy::missing_const_for_fn)] pub fn unlock<'g, 'key: 'g, Key: Keyable + 'key>( guard: LockGuard<'key, L::Guard<'g>, Key>, @@ -149,6 +243,26 @@ impl OwnedLockCollection { } impl OwnedLockCollection { + /// Locks the collection, so that other threads can still read from it + /// + /// This function returns a guard that can be used to access the underlying + /// data immutably. When the guard is dropped, the locks in the collection + /// are also dropped. + /// + /// # Examples + /// + /// ``` + /// use happylock::{RwLock, ThreadKey}; + /// use happylock::collection::OwnedLockCollection; + /// + /// let key = ThreadKey::get().unwrap(); + /// let data = (RwLock::new(0), RwLock::new("")); + /// let lock = OwnedLockCollection::new(data); + /// + /// let mut guard = lock.read(key); + /// assert_eq!(*guard.0, 0); + /// assert_eq!(*guard.1, ""); + /// ``` pub fn read<'g, 'key, Key: Keyable + 'key>( &'g self, key: Key, @@ -169,6 +283,32 @@ impl OwnedLockCollection { } } + /// Attempts to lock the without blocking, in such a way that other threads + /// can still read from the collection. + /// + /// If successful, this method returns a guard that can be used to access + /// the data immutably, and unlocks the data when it is dropped. Otherwise, + /// `None` is returned. + /// + /// # Examples + /// + /// ``` + /// use happylock::{RwLock, ThreadKey}; + /// use happylock::collection::OwnedLockCollection; + /// + /// let key = ThreadKey::get().unwrap(); + /// let data = (RwLock::new(5), RwLock::new("6")); + /// let lock = OwnedLockCollection::new(data); + /// + /// match lock.try_read(key) { + /// Some(mut guard) => { + /// assert_eq!(*guard.0, 5); + /// assert_eq!(*guard.1, "6"); + /// }, + /// None => unreachable!(), + /// }; + /// + /// ``` pub fn try_read<'g, 'key: 'g, Key: Keyable + 'key>( &'g self, key: Key, @@ -199,6 +339,22 @@ impl OwnedLockCollection { }) } + /// Unlocks the underlying lockable data type, returning the key that's + /// associated with it. + /// + /// # Examples + /// + /// ``` + /// use happylock::{RwLock, ThreadKey}; + /// use happylock::collection::OwnedLockCollection; + /// + /// let key = ThreadKey::get().unwrap(); + /// let data = (RwLock::new(0), RwLock::new("")); + /// let lock = OwnedLockCollection::new(data); + /// + /// let mut guard = lock.read(key); + /// let key = OwnedLockCollection::<(RwLock, RwLock<&str>)>::unlock_read(guard); + /// ``` #[allow(clippy::missing_const_for_fn)] pub fn unlock_read<'g, 'key: 'g, Key: Keyable + 'key>( guard: LockGuard<'key, L::ReadGuard<'g>, Key>, diff --git a/src/collection/ref.rs b/src/collection/ref.rs index 2e2883a..e5c548f 100644 --- a/src/collection/ref.rs +++ b/src/collection/ref.rs @@ -82,10 +82,11 @@ impl<'a, L: OwnedLockable> RefLockCollection<'a, L> { /// # Examples /// /// ``` - /// use happylock::{LockCollection, Mutex}; + /// use happylock::Mutex; + /// use happylock::collection::RefLockCollection; /// /// let data = (Mutex::new(0), Mutex::new("")); - /// let lock = LockCollection::new(&data); + /// let lock = RefLockCollection::new(&data); /// ``` #[must_use] pub fn new(data: &'a L) -> RefLockCollection { @@ -107,13 +108,15 @@ impl<'a, L: Lockable> RefLockCollection<'a, L> { /// # Examples /// /// ``` - /// use happylock::{LockCollection, Mutex}; + /// use happylock::Mutex; + /// use happylock::collection::RefLockCollection; /// /// let data1 = Mutex::new(0); /// let data2 = Mutex::new(""); /// /// // safety: data1 and data2 refer to distinct mutexes - /// let lock = unsafe { LockCollection::new_unchecked((&data1, &data2)) }; + /// let data = (&data1, &data2); + /// let lock = unsafe { RefLockCollection::new_unchecked(&data) }; /// ``` #[must_use] pub unsafe fn new_unchecked(data: &'a L) -> Self { @@ -131,13 +134,15 @@ impl<'a, L: Lockable> RefLockCollection<'a, L> { /// # Examples /// /// ``` - /// use happylock::{LockCollection, Mutex}; + /// use happylock::Mutex; + /// use happylock::collection::RefLockCollection; /// /// let data1 = Mutex::new(0); /// let data2 = Mutex::new(""); /// /// // data1 and data2 refer to distinct mutexes, so this won't panic - /// let lock = LockCollection::try_new((&data1, &data2)).unwrap(); + /// let data = (&data1, &data2); + /// let lock = RefLockCollection::try_new(&data).unwrap(); /// ``` #[must_use] pub fn try_new(data: &'a L) -> Option { @@ -158,10 +163,12 @@ impl<'a, L: Lockable> RefLockCollection<'a, L> { /// # Examples /// /// ``` - /// use happylock::{LockCollection, Mutex, ThreadKey}; + /// use happylock::{Mutex, ThreadKey}; + /// use happylock::collection::RefLockCollection; /// /// let key = ThreadKey::get().unwrap(); - /// let lock = LockCollection::new((Mutex::new(0), Mutex::new(""))); + /// let data = (Mutex::new(0), Mutex::new("")); + /// let lock = RefLockCollection::new(&data); /// /// let mut guard = lock.lock(key); /// *guard.0 += 1; @@ -193,10 +200,12 @@ impl<'a, L: Lockable> RefLockCollection<'a, L> { /// # Examples /// /// ``` - /// use happylock::{LockCollection, Mutex, ThreadKey}; + /// use happylock::{Mutex, ThreadKey}; + /// use happylock::collection::RefLockCollection; /// /// let key = ThreadKey::get().unwrap(); - /// let lock = LockCollection::new((Mutex::new(0), Mutex::new(""))); + /// let data = (Mutex::new(0), Mutex::new("")); + /// let lock = RefLockCollection::new(&data); /// /// match lock.try_lock(key) { /// Some(mut guard) => { @@ -242,15 +251,17 @@ impl<'a, L: Lockable> RefLockCollection<'a, L> { /// # Examples /// /// ``` - /// use happylock::{LockCollection, Mutex, ThreadKey}; + /// use happylock::{Mutex, ThreadKey}; + /// use happylock::collection::RefLockCollection; /// /// let key = ThreadKey::get().unwrap(); - /// let lock = LockCollection::new((Mutex::new(0), Mutex::new(""))); + /// let data = (Mutex::new(0), Mutex::new("")); + /// let lock = RefLockCollection::new(&data); /// /// let mut guard = lock.lock(key); /// *guard.0 += 1; /// *guard.1 = "1"; - /// let key = LockCollection::unlock(guard); + /// let key = RefLockCollection::<(Mutex, Mutex<&str>)>::unlock(guard); /// ``` #[allow(clippy::missing_const_for_fn)] pub fn unlock<'key: 'a, Key: Keyable + 'key>(guard: LockGuard<'key, L::Guard<'a>, Key>) -> Key { @@ -260,6 +271,26 @@ impl<'a, L: Lockable> RefLockCollection<'a, L> { } impl<'a, L: Sharable> RefLockCollection<'a, L> { + /// Locks the collection, so that other threads can still read from it + /// + /// This function returns a guard that can be used to access the underlying + /// data immutably. When the guard is dropped, the locks in the collection + /// are also dropped. + /// + /// # Examples + /// + /// ``` + /// use happylock::{RwLock, ThreadKey}; + /// use happylock::collection::RefLockCollection; + /// + /// let key = ThreadKey::get().unwrap(); + /// let data = (RwLock::new(0), RwLock::new("")); + /// let lock = RefLockCollection::new(&data); + /// + /// let mut guard = lock.read(key); + /// assert_eq!(*guard.0, 0); + /// assert_eq!(*guard.1, ""); + /// ``` pub fn read<'key: 'a, Key: Keyable + 'key>( &'a self, key: Key, @@ -277,6 +308,32 @@ impl<'a, L: Sharable> RefLockCollection<'a, L> { } } + /// Attempts to lock the without blocking, in such a way that other threads + /// can still read from the collection. + /// + /// If successful, this method returns a guard that can be used to access + /// the data immutably, and unlocks the data when it is dropped. Otherwise, + /// `None` is returned. + /// + /// # Examples + /// + /// ``` + /// use happylock::{RwLock, ThreadKey}; + /// use happylock::collection::RefLockCollection; + /// + /// let key = ThreadKey::get().unwrap(); + /// let data = (RwLock::new(5), RwLock::new("6")); + /// let lock = RefLockCollection::new(&data); + /// + /// match lock.try_read(key) { + /// Some(mut guard) => { + /// assert_eq!(*guard.0, 5); + /// assert_eq!(*guard.1, "6"); + /// }, + /// None => unreachable!(), + /// }; + /// + /// ``` pub fn try_read<'key: 'a, Key: Keyable + 'key>( &'a self, key: Key, @@ -306,6 +363,22 @@ impl<'a, L: Sharable> RefLockCollection<'a, L> { }) } + /// Unlocks the underlying lockable data type, returning the key that's + /// associated with it. + /// + /// # Examples + /// + /// ``` + /// use happylock::{RwLock, ThreadKey}; + /// use happylock::collection::RefLockCollection; + /// + /// let key = ThreadKey::get().unwrap(); + /// let data = (RwLock::new(0), RwLock::new("")); + /// let lock = RefLockCollection::new(&data); + /// + /// let mut guard = lock.read(key); + /// let key = RefLockCollection::<(RwLock, RwLock<&str>)>::unlock_read(guard); + /// ``` #[allow(clippy::missing_const_for_fn)] pub fn unlock_read<'key: 'a, Key: Keyable + 'key>( guard: LockGuard<'key, L::ReadGuard<'a>, Key>, @@ -320,6 +393,23 @@ where &'a L: IntoIterator, { /// Returns an iterator over references to each value in the collection. + /// + /// # Examples + /// + /// ``` + /// use happylock::{Mutex, ThreadKey}; + /// use happylock::collection::RefLockCollection; + /// + /// let key = ThreadKey::get().unwrap(); + /// let data = [Mutex::new(26), Mutex::new(1)]; + /// let lock = RefLockCollection::new(&data); + /// + /// let mut iter = lock.iter(); + /// let mutex = iter.next().unwrap(); + /// let guard = mutex.lock(key); + /// + /// assert_eq!(*guard, 26); + /// ``` #[must_use] pub fn iter(&'a self) -> <&'a L as IntoIterator>::IntoIter { self.into_iter() diff --git a/src/collection/retry.rs b/src/collection/retry.rs index d15d7d6..2b9b0a0 100644 --- a/src/collection/retry.rs +++ b/src/collection/retry.rs @@ -6,12 +6,13 @@ use std::marker::PhantomData; use super::{LockGuard, RetryingLockCollection}; +/// 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 mut locks_set = HashSet::new(); + let mut locks_set = HashSet::with_capacity(locks.len()); for lock in locks { if !locks_set.insert(lock) { return true; @@ -119,6 +120,21 @@ impl From for RetryingLockCollection { } impl RetryingLockCollection { + /// Creates a new collection of owned locks. + /// + /// Because the locks are owned, there's no need to do any checks for + /// duplicate values. The locks also don't need to be sorted by memory + /// address because they aren't used anywhere else. + /// + /// # Examples + /// + /// ``` + /// use happylock::Mutex; + /// use happylock::collection::RetryingLockCollection; + /// + /// let data = (Mutex::new(0), Mutex::new("")); + /// let lock = RetryingLockCollection::new(data); + /// ``` #[must_use] pub const fn new(data: L) -> Self { Self { data } @@ -126,6 +142,20 @@ impl RetryingLockCollection { } impl<'a, L: OwnedLockable> RetryingLockCollection<&'a 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::Mutex; + /// use happylock::collection::RetryingLockCollection; + /// + /// let data = (Mutex::new(0), Mutex::new("")); + /// let lock = RetryingLockCollection::new_ref(&data); + /// ``` #[must_use] pub const fn new_ref(data: &'a L) -> Self { Self { data } @@ -133,19 +163,95 @@ impl<'a, L: OwnedLockable> RetryingLockCollection<&'a L> { } impl RetryingLockCollection { + /// Creates a new collections of locks. + /// + /// # Safety + /// + /// This results in undefined behavior if any locks are presented twice + /// within this collection. + /// + /// # Examples + /// + /// ``` + /// use happylock::Mutex; + /// use happylock::collection::RetryingLockCollection; + /// + /// let data1 = Mutex::new(0); + /// let data2 = Mutex::new(""); + /// + /// // safety: data1 and data2 refer to distinct mutexes + /// let data = (&data1, &data2); + /// let lock = unsafe { RetryingLockCollection::new_unchecked(&data) }; + /// ``` #[must_use] pub const unsafe fn new_unchecked(data: L) -> Self { Self { data } } + /// Creates a new collection of locks. + /// + /// This returns `None` if any locks are found twice in the given + /// collection. + /// + /// # Examples + /// + /// ``` + /// use happylock::Mutex; + /// use happylock::collection::RetryingLockCollection; + /// + /// let data1 = Mutex::new(0); + /// let data2 = Mutex::new(""); + /// + /// // data1 and data2 refer to distinct mutexes, so this won't panic + /// let data = (&data1, &data2); + /// let lock = RetryingLockCollection::try_new(&data).unwrap(); + /// ``` + #[must_use] pub fn try_new(data: L) -> Option { - contains_duplicates(&data).then_some(Self { data }) + (!contains_duplicates(&data)).then_some(Self { data }) } + /// Gets the underlying collection, consuming this collection. + /// + /// # Examples + /// + /// ``` + /// use happylock::{Mutex, ThreadKey}; + /// use happylock::collection::RetryingLockCollection; + /// + /// let data = (Mutex::new(42), Mutex::new("")); + /// let lock = RetryingLockCollection::new(data); + /// + /// let key = ThreadKey::get().unwrap(); + /// let inner = lock.into_inner(); + /// let guard = inner.0.lock(key); + /// assert_eq!(*guard, 42); + /// ``` + #[must_use] pub fn into_inner(self) -> L { self.data } + /// Locks the collection + /// + /// This function returns a guard that can be used to access the underlying + /// data. When the guard is dropped, the locks in the collection are also + /// dropped. + /// + /// # Examples + /// + /// ``` + /// use happylock::{Mutex, ThreadKey}; + /// use happylock::collection::RetryingLockCollection; + /// + /// let key = ThreadKey::get().unwrap(); + /// let data = (Mutex::new(0), Mutex::new("")); + /// let lock = RetryingLockCollection::new(data); + /// + /// let mut guard = lock.lock(key); + /// *guard.0 += 1; + /// *guard.1 = "1"; + /// ``` pub fn lock<'g, 'key: 'g, Key: Keyable + 'key>( &'g self, key: Key, @@ -202,6 +308,31 @@ impl RetryingLockCollection { } } + /// Attempts to lock the without blocking. + /// + /// If successful, this method returns a guard that can be used to access + /// the data, and unlocks the data when it is dropped. Otherwise, `None` is + /// returned. + /// + /// # Examples + /// + /// ``` + /// use happylock::{Mutex, ThreadKey}; + /// use happylock::collection::RetryingLockCollection; + /// + /// let key = ThreadKey::get().unwrap(); + /// let data = (Mutex::new(0), Mutex::new("")); + /// let lock = RetryingLockCollection::new(data); + /// + /// match lock.try_lock(key) { + /// Some(mut guard) => { + /// *guard.0 += 1; + /// *guard.1 = "1"; + /// }, + /// None => unreachable!(), + /// }; + /// + /// ``` pub fn try_lock<'g, 'key: 'g, Key: Keyable + 'key>( &'g self, key: Key, @@ -241,6 +372,24 @@ impl RetryingLockCollection { }) } + /// Unlocks the underlying lockable data type, returning the key that's + /// associated with it. + /// + /// # Examples + /// + /// ``` + /// use happylock::{Mutex, ThreadKey}; + /// use happylock::collection::RetryingLockCollection; + /// + /// let key = ThreadKey::get().unwrap(); + /// let data = (Mutex::new(0), Mutex::new("")); + /// let lock = RetryingLockCollection::new(data); + /// + /// let mut guard = lock.lock(key); + /// *guard.0 += 1; + /// *guard.1 = "1"; + /// let key = RetryingLockCollection::<(Mutex, Mutex<&str>)>::unlock(guard); + /// ``` pub fn unlock<'key, Key: Keyable + 'key>(guard: LockGuard<'key, L::Guard<'_>, Key>) -> Key { drop(guard.guard); guard.key @@ -248,6 +397,26 @@ impl RetryingLockCollection { } impl RetryingLockCollection { + /// Locks the collection, so that other threads can still read from it + /// + /// This function returns a guard that can be used to access the underlying + /// data immutably. When the guard is dropped, the locks in the collection + /// are also dropped. + /// + /// # Examples + /// + /// ``` + /// use happylock::{RwLock, ThreadKey}; + /// use happylock::collection::RetryingLockCollection; + /// + /// let key = ThreadKey::get().unwrap(); + /// let data = (RwLock::new(0), RwLock::new("")); + /// let lock = RetryingLockCollection::new(data); + /// + /// let mut guard = lock.read(key); + /// assert_eq!(*guard.0, 0); + /// assert_eq!(*guard.1, ""); + /// ``` pub fn read<'g, 'key: 'g, Key: Keyable + 'key>( &'g self, key: Key, @@ -304,6 +473,32 @@ impl RetryingLockCollection { } } + /// Attempts to lock the without blocking, in such a way that other threads + /// can still read from the collection. + /// + /// If successful, this method returns a guard that can be used to access + /// the data immutably, and unlocks the data when it is dropped. Otherwise, + /// `None` is returned. + /// + /// # Examples + /// + /// ``` + /// use happylock::{RwLock, ThreadKey}; + /// use happylock::collection::RetryingLockCollection; + /// + /// let key = ThreadKey::get().unwrap(); + /// let data = (RwLock::new(5), RwLock::new("6")); + /// let lock = RetryingLockCollection::new(data); + /// + /// match lock.try_read(key) { + /// Some(mut guard) => { + /// assert_eq!(*guard.0, 5); + /// assert_eq!(*guard.1, "6"); + /// }, + /// None => unreachable!(), + /// }; + /// + /// ``` pub fn try_read<'g, 'key: 'g, Key: Keyable + 'key>( &'g self, key: Key, @@ -343,6 +538,22 @@ impl RetryingLockCollection { }) } + /// Unlocks the underlying lockable data type, returning the key that's + /// associated with it. + /// + /// # Examples + /// + /// ``` + /// use happylock::{RwLock, ThreadKey}; + /// use happylock::collection::RetryingLockCollection; + /// + /// let key = ThreadKey::get().unwrap(); + /// let data = (RwLock::new(0), RwLock::new("")); + /// let lock = RetryingLockCollection::new(data); + /// + /// let mut guard = lock.read(key); + /// let key = RetryingLockCollection::<(RwLock, RwLock<&str>)>::unlock_read(guard); + /// ``` pub fn unlock_read<'key, Key: Keyable + 'key>( guard: LockGuard<'key, L::ReadGuard<'_>, Key>, ) -> Key { @@ -356,6 +567,23 @@ where &'a L: IntoIterator, { /// Returns an iterator over references to each value in the collection. + /// + /// # Examples + /// + /// ``` + /// use happylock::{Mutex, ThreadKey}; + /// use happylock::collection::RetryingLockCollection; + /// + /// let key = ThreadKey::get().unwrap(); + /// let data = [Mutex::new(26), Mutex::new(1)]; + /// let lock = RetryingLockCollection::new(data); + /// + /// let mut iter = lock.iter(); + /// let mutex = iter.next().unwrap(); + /// let guard = mutex.lock(key); + /// + /// assert_eq!(*guard, 26); + /// ``` #[must_use] pub fn iter(&'a self) -> <&'a L as IntoIterator>::IntoIter { self.into_iter() @@ -368,6 +596,22 @@ where { /// Returns an iterator over mutable references to each value in the /// collection. + /// + /// # Examples + /// + /// ``` + /// use happylock::{Mutex, ThreadKey}; + /// use happylock::collection::RetryingLockCollection; + /// + /// let key = ThreadKey::get().unwrap(); + /// let data = [Mutex::new(26), Mutex::new(1)]; + /// let mut lock = RetryingLockCollection::new(data); + /// + /// let mut iter = lock.iter_mut(); + /// let mutex = iter.next().unwrap(); + /// + /// assert_eq!(*mutex.as_mut(), 26); + /// ``` #[must_use] pub fn iter_mut(&'a mut self) -> <&'a mut L as IntoIterator>::IntoIter { self.into_iter() diff --git a/src/lockable.rs b/src/lockable.rs index 2f98d3a..6b9c7c6 100644 --- a/src/lockable.rs +++ b/src/lockable.rs @@ -311,6 +311,8 @@ unsafe impl Lockable for &T { } } +unsafe impl Sharable for &T {} + unsafe impl Lockable for &mut T { type Guard<'g> = T::Guard<'g> where Self: 'g; @@ -329,323 +331,43 @@ unsafe impl Lockable for &mut T { } } -unsafe impl OwnedLockable for &mut T {} - -unsafe impl Lockable for (A,) { - type Guard<'g> = (A::Guard<'g>,) where Self: 'g; - - type ReadGuard<'g> = (A::ReadGuard<'g>,) where Self: 'g; - - fn get_ptrs<'a>(&'a self, ptrs: &mut Vec<&'a dyn RawLock>) { - self.0.get_ptrs(ptrs); - } - - unsafe fn guard(&self) -> Self::Guard<'_> { - (self.0.guard(),) - } - - unsafe fn read_guard(&self) -> Self::ReadGuard<'_> { - (self.0.read_guard(),) - } -} - -unsafe impl Lockable for (A, B) { - type Guard<'g> = (A::Guard<'g>, B::Guard<'g>) where Self: 'g; - - type ReadGuard<'g> = (A::ReadGuard<'g>, B::ReadGuard<'g>) where Self: 'g; - - fn get_ptrs<'a>(&'a self, ptrs: &mut Vec<&'a dyn RawLock>) { - self.0.get_ptrs(ptrs); - self.1.get_ptrs(ptrs); - } - - unsafe fn guard(&self) -> Self::Guard<'_> { - (self.0.guard(), self.1.guard()) - } - - unsafe fn read_guard(&self) -> Self::ReadGuard<'_> { - (self.0.read_guard(), self.1.read_guard()) - } -} - -unsafe impl Lockable for (A, B, C) { - type Guard<'g> = (A::Guard<'g>, B::Guard<'g>, C::Guard<'g>) where Self: 'g; - - type ReadGuard<'g> = (A::ReadGuard<'g>, B::ReadGuard<'g>, C::ReadGuard<'g>) where Self: 'g; - - fn get_ptrs<'a>(&'a self, ptrs: &mut Vec<&'a dyn RawLock>) { - self.0.get_ptrs(ptrs); - self.1.get_ptrs(ptrs); - self.2.get_ptrs(ptrs); - } - - unsafe fn guard(&self) -> Self::Guard<'_> { - (self.0.guard(), self.1.guard(), self.2.guard()) - } - - unsafe fn read_guard(&self) -> Self::ReadGuard<'_> { - ( - self.0.read_guard(), - self.1.read_guard(), - self.2.read_guard(), - ) - } -} - -unsafe impl Lockable for (A, B, C, D) { - type Guard<'g> = (A::Guard<'g>, B::Guard<'g>, C::Guard<'g>, D::Guard<'g>) where Self: 'g; - - type ReadGuard<'g> = ( - A::ReadGuard<'g>, - B::ReadGuard<'g>, - C::ReadGuard<'g>, - D::ReadGuard<'g>, - ) where Self: 'g; - - fn get_ptrs<'a>(&'a self, ptrs: &mut Vec<&'a dyn RawLock>) { - self.0.get_ptrs(ptrs); - self.1.get_ptrs(ptrs); - self.2.get_ptrs(ptrs); - self.3.get_ptrs(ptrs); - } - - unsafe fn guard(&self) -> Self::Guard<'_> { - ( - self.0.guard(), - self.1.guard(), - self.2.guard(), - self.3.guard(), - ) - } - - unsafe fn read_guard(&self) -> Self::ReadGuard<'_> { - ( - self.0.read_guard(), - self.1.read_guard(), - self.2.read_guard(), - self.3.read_guard(), - ) - } -} - -unsafe impl Lockable - for (A, B, C, D, E) -{ - type Guard<'g> = ( - A::Guard<'g>, - B::Guard<'g>, - C::Guard<'g>, - D::Guard<'g>, - E::Guard<'g>, - ) where Self: 'g; - - type ReadGuard<'g> = ( - A::ReadGuard<'g>, - B::ReadGuard<'g>, - C::ReadGuard<'g>, - D::ReadGuard<'g>, - E::ReadGuard<'g>, - ) where Self: 'g; - - fn get_ptrs<'a>(&'a self, ptrs: &mut Vec<&'a dyn RawLock>) { - 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 guard(&self) -> Self::Guard<'_> { - ( - self.0.guard(), - self.1.guard(), - self.2.guard(), - self.3.guard(), - self.4.guard(), - ) - } - - unsafe fn read_guard(&self) -> Self::ReadGuard<'_> { - ( - self.0.read_guard(), - self.1.read_guard(), - self.2.read_guard(), - self.3.read_guard(), - self.4.read_guard(), - ) - } -} - -unsafe impl Lockable - for (A, B, C, D, E, F) -{ - type Guard<'g> = ( - A::Guard<'g>, - B::Guard<'g>, - C::Guard<'g>, - D::Guard<'g>, - E::Guard<'g>, - F::Guard<'g>, - ) where Self: 'g; - - type ReadGuard<'g> = ( - A::ReadGuard<'g>, - B::ReadGuard<'g>, - C::ReadGuard<'g>, - D::ReadGuard<'g>, - E::ReadGuard<'g>, - F::ReadGuard<'g>, - ) where Self: 'g; - - fn get_ptrs<'a>(&'a self, ptrs: &mut Vec<&'a dyn RawLock>) { - 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(&self) -> Self::Guard<'_> { - ( - self.0.guard(), - self.1.guard(), - self.2.guard(), - self.3.guard(), - self.4.guard(), - self.5.guard(), - ) - } +unsafe impl Sharable for &mut T {} - unsafe fn read_guard(&self) -> Self::ReadGuard<'_> { - ( - self.0.read_guard(), - self.1.read_guard(), - self.2.read_guard(), - self.3.read_guard(), - self.4.read_guard(), - self.5.read_guard(), - ) - } -} - -unsafe impl - Lockable for (A, B, C, D, E, F, G) -{ - type Guard<'g> = ( - A::Guard<'g>, - B::Guard<'g>, - C::Guard<'g>, - D::Guard<'g>, - E::Guard<'g>, - F::Guard<'g>, - G::Guard<'g>, - ) where Self: 'g; - - type ReadGuard<'g> = ( - A::ReadGuard<'g>, - B::ReadGuard<'g>, - C::ReadGuard<'g>, - D::ReadGuard<'g>, - E::ReadGuard<'g>, - F::ReadGuard<'g>, - G::ReadGuard<'g>, - ) where Self: 'g; - - fn get_ptrs<'a>(&'a self, ptrs: &mut Vec<&'a dyn RawLock>) { - 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(&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 fn read_guard(&self) -> Self::ReadGuard<'_> { - ( - self.0.read_guard(), - self.1.read_guard(), - self.2.read_guard(), - self.3.read_guard(), - self.4.read_guard(), - self.5.read_guard(), - self.6.read_guard(), - ) - } -} - -unsafe impl Sharable for (A,) {} -unsafe impl Sharable for (A, B) {} - -unsafe impl Sharable for (A, B, C) {} - -unsafe impl Sharable for (A, B, C, D) {} +unsafe impl OwnedLockable for &mut T {} -unsafe impl Sharable - for (A, B, C, D, E) -{ -} +macro_rules! tuple_impls { + ($($generic:ident)*, $($value:tt)*) => { + unsafe impl<$($generic: Lockable,)*> Lockable for ($($generic,)*) { + type Guard<'g> = ($($generic::Guard<'g>,)*) where Self: 'g; -unsafe impl Sharable - for (A, B, C, D, E, F) -{ -} + type ReadGuard<'g> = ($($generic::ReadGuard<'g>,)*) where Self: 'g; -unsafe impl - Sharable for (A, B, C, D, E, F, G) -{ -} + fn get_ptrs<'a>(&'a self, ptrs: &mut Vec<&'a dyn RawLock>) { + self.0.get_ptrs(ptrs); + } -unsafe impl OwnedLockable for (A,) {} -unsafe impl OwnedLockable for (A, B) {} + unsafe fn guard(&self) -> Self::Guard<'_> { + ($(self.$value.guard(),)*) + } -unsafe impl OwnedLockable for (A, B, C) {} + unsafe fn read_guard(&self) -> Self::ReadGuard<'_> { + ($(self.$value.read_guard(),)*) + } + } -unsafe impl OwnedLockable - for (A, B, C, D) -{ -} + unsafe impl<$($generic: Sharable,)*> Sharable for ($($generic,)*) {} -unsafe impl - OwnedLockable for (A, B, C, D, E) -{ + unsafe impl<$($generic: OwnedLockable,)*> OwnedLockable for ($($generic,)*) {} + }; } -unsafe impl< - A: OwnedLockable, - B: OwnedLockable, - C: OwnedLockable, - D: OwnedLockable, - E: OwnedLockable, - F: OwnedLockable, - > OwnedLockable for (A, B, C, D, E, F) -{ -} - -unsafe impl< - A: OwnedLockable, - B: OwnedLockable, - C: OwnedLockable, - D: OwnedLockable, - E: OwnedLockable, - F: OwnedLockable, - G: OwnedLockable, - > OwnedLockable for (A, B, C, D, E, F, G) -{ -} +tuple_impls!(A, 0); +tuple_impls!(A B, 0 1); +tuple_impls!(A B C, 0 1 2); +tuple_impls!(A B C D, 0 1 2 3); +tuple_impls!(A B C D E, 0 1 2 3 4); +tuple_impls!(A B C D E F, 0 1 2 3 4 5); +tuple_impls!(A B C D E F G, 0 1 2 3 4 5 6); unsafe impl Lockable for [T; N] { type Guard<'g> = [T::Guard<'g>; N] where Self: 'g; diff --git a/src/mutex/guard.rs b/src/mutex/guard.rs index 35fe1f2..9e8e2e6 100644 --- a/src/mutex/guard.rs +++ b/src/mutex/guard.rs @@ -61,7 +61,9 @@ impl<'a, T: ?Sized + 'a, R: RawMutex> AsMut for MutexRef<'a, T, R> { } impl<'a, T: ?Sized + 'a, R: RawMutex> MutexRef<'a, T, R> { - pub unsafe fn new(mutex: &'a Mutex) -> Self { + /// Creates a reference to the underlying data of a mutex without + /// attempting to lock it or take ownership of the key. + pub(crate) unsafe fn new(mutex: &'a Mutex) -> Self { Self(mutex, PhantomData) } } diff --git a/src/rwlock/read_guard.rs b/src/rwlock/read_guard.rs index da3d101..e46078c 100644 --- a/src/rwlock/read_guard.rs +++ b/src/rwlock/read_guard.rs @@ -46,7 +46,9 @@ 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) -> Self { + /// Creates an immutable reference for the underlying data of an [`RwLock`] + /// without locking it or taking ownership of the key. + pub(crate) unsafe fn new(mutex: &'a RwLock) -> Self { Self(mutex, PhantomData) } } diff --git a/src/rwlock/read_lock.rs b/src/rwlock/read_lock.rs index 29042b5..4f2bc86 100644 --- a/src/rwlock/read_lock.rs +++ b/src/rwlock/read_lock.rs @@ -36,7 +36,7 @@ impl<'l, T, R> From<&'l RwLock> for ReadLock<'l, T, R> { impl<'l, T: ?Sized, R> AsRef> for ReadLock<'l, T, R> { fn as_ref(&self) -> &RwLock { - &self.0 + self.0 } } diff --git a/src/rwlock/write_guard.rs b/src/rwlock/write_guard.rs index c8dd58b..ec622d7 100644 --- a/src/rwlock/write_guard.rs +++ b/src/rwlock/write_guard.rs @@ -48,7 +48,9 @@ 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) -> Self { + /// Creates a reference to the underlying data of an [`RwLock`] without + /// locking or taking ownership of the key. + pub(crate) unsafe fn new(mutex: &'a RwLock) -> Self { Self(mutex, PhantomData) } } -- cgit v1.2.3