diff options
| author | Botahamec <botahamec@outlook.com> | 2025-03-29 17:34:10 -0400 |
|---|---|---|
| committer | Botahamec <botahamec@outlook.com> | 2025-03-29 17:34:10 -0400 |
| commit | 17dab88a7b4bc86cf156a1e0ac1bac19e6f9f5c6 (patch) | |
| tree | 59f7ddaf743899be6b5ddd7ecf1b38882c50087d /src/rwlock | |
| parent | 3a299432173f98521d0aeb840dafb6710ce27f82 (diff) | |
Clean up existing documentation
Diffstat (limited to 'src/rwlock')
| -rw-r--r-- | src/rwlock/read_lock.rs | 251 | ||||
| -rw-r--r-- | src/rwlock/rwlock.rs | 74 | ||||
| -rw-r--r-- | src/rwlock/write_lock.rs | 229 |
3 files changed, 46 insertions, 508 deletions
diff --git a/src/rwlock/read_lock.rs b/src/rwlock/read_lock.rs deleted file mode 100644 index f13f2b9..0000000 --- a/src/rwlock/read_lock.rs +++ /dev/null @@ -1,251 +0,0 @@ -use std::fmt::Debug; - -use lock_api::RawRwLock; - -use crate::lockable::{Lockable, RawLock, Sharable}; -use crate::{Keyable, ThreadKey}; - -use super::{ReadLock, RwLock, RwLockReadGuard, RwLockReadRef}; - -unsafe impl<T, R: RawRwLock> RawLock for ReadLock<'_, T, R> { - fn poison(&self) { - self.0.poison() - } - - unsafe fn raw_write(&self) { - self.0.raw_read() - } - - unsafe fn raw_try_write(&self) -> bool { - self.0.raw_try_read() - } - - unsafe fn raw_unlock_write(&self) { - self.0.raw_unlock_read() - } - - unsafe fn raw_read(&self) { - self.0.raw_read() - } - - unsafe fn raw_try_read(&self) -> bool { - self.0.raw_try_read() - } - - unsafe fn raw_unlock_read(&self) { - self.0.raw_unlock_read() - } -} - -unsafe impl<T, R: RawRwLock> Lockable for ReadLock<'_, T, R> { - type Guard<'g> - = RwLockReadRef<'g, T, R> - where - Self: 'g; - - type DataMut<'a> - = &'a T - where - Self: 'a; - - fn get_ptrs<'a>(&'a self, ptrs: &mut Vec<&'a dyn RawLock>) { - ptrs.push(self.0); - } - - unsafe fn guard(&self) -> Self::Guard<'_> { - RwLockReadRef::new(self.as_ref()) - } - - unsafe fn data_mut(&self) -> Self::DataMut<'_> { - self.0.data_ref() - } -} - -unsafe impl<T, R: RawRwLock> Sharable for ReadLock<'_, T, R> { - type ReadGuard<'g> - = RwLockReadRef<'g, T, R> - where - Self: 'g; - - type DataRef<'a> - = &'a T - where - Self: 'a; - - unsafe fn read_guard(&self) -> Self::Guard<'_> { - RwLockReadRef::new(self.as_ref()) - } - - unsafe fn data_ref(&self) -> Self::DataRef<'_> { - self.0.data_ref() - } -} - -#[mutants::skip] -#[cfg(not(tarpaulin_include))] -impl<T: Debug, R: RawRwLock> Debug for ReadLock<'_, T, R> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - // safety: this is just a try lock, and the value is dropped - // immediately after, so there's no risk of blocking ourselves - // or any other threads - if let Some(value) = unsafe { self.try_lock_no_key() } { - f.debug_struct("ReadLock").field("data", &&*value).finish() - } else { - struct LockedPlaceholder; - impl Debug for LockedPlaceholder { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str("<locked>") - } - } - - f.debug_struct("ReadLock") - .field("data", &LockedPlaceholder) - .finish() - } - } -} - -impl<'l, T, R> From<&'l RwLock<T, R>> for ReadLock<'l, T, R> { - fn from(value: &'l RwLock<T, R>) -> Self { - Self::new(value) - } -} - -impl<T: ?Sized, R> AsRef<RwLock<T, R>> for ReadLock<'_, T, R> { - fn as_ref(&self) -> &RwLock<T, R> { - self.0 - } -} - -impl<'l, T, R> ReadLock<'l, T, R> { - /// Creates a new `ReadLock` which accesses the given [`RwLock`] - /// - /// # Examples - /// - /// ``` - /// use happylock::{rwlock::ReadLock, RwLock}; - /// - /// let lock = RwLock::new(5); - /// let read_lock = ReadLock::new(&lock); - /// ``` - #[must_use] - pub const fn new(rwlock: &'l RwLock<T, R>) -> Self { - Self(rwlock) - } -} - -impl<T, R: RawRwLock> ReadLock<'_, T, R> { - pub fn scoped_lock<'a, Ret>(&'a self, key: impl Keyable, f: impl Fn(&'a T) -> Ret) -> Ret { - self.0.scoped_read(key, f) - } - - pub fn scoped_try_lock<'a, Key: Keyable, Ret>( - &'a self, - key: Key, - f: impl Fn(&'a T) -> Ret, - ) -> Result<Ret, Key> { - self.0.scoped_try_read(key, f) - } - - /// Locks the underlying [`RwLock`] with shared read access, blocking the - /// current thread until it can be acquired. - /// - /// The calling thread will be blocked until there are no more writers - /// which hold the lock. There may be other readers currently inside the - /// lock when this method returns. - /// - /// Returns an RAII guard which will release this thread's shared access - /// once it is dropped. - /// - /// Because this method takes a [`ThreadKey`], it's not possible for this - /// method to cause a deadlock. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// use std::thread; - /// use happylock::{RwLock, ThreadKey}; - /// use happylock::rwlock::ReadLock; - /// - /// let key = ThreadKey::get().unwrap(); - /// let lock: RwLock<_> = RwLock::new(1); - /// let reader = ReadLock::new(&lock); - /// - /// let n = reader.lock(key); - /// assert_eq!(*n, 1); - /// ``` - /// - /// [`ThreadKey`]: `crate::ThreadKey` - #[must_use] - pub fn lock(&self, key: ThreadKey) -> RwLockReadGuard<'_, T, R> { - self.0.read(key) - } - - /// Attempts to acquire the underlying [`RwLock`] with shared read access - /// without blocking. - /// - /// If the access could not be granted at this time, then `Err` is - /// returned. Otherwise, an RAII guard is returned which will release the - /// shared access when it is dropped. - /// - /// This function does not provide any guarantees with respect to the - /// ordering of whether contentious readers or writers will acquire the - /// lock first. - /// - /// # Errors - /// - /// If the `RwLock` could not be acquired because it was already locked - /// exclusively, then an error will be returned containing the given key. - /// - /// # Examples - /// - /// ``` - /// use happylock::{RwLock, ThreadKey}; - /// use happylock::rwlock::ReadLock; - /// - /// let key = ThreadKey::get().unwrap(); - /// let lock = RwLock::new(1); - /// let reader = ReadLock::new(&lock); - /// - /// match reader.try_lock(key) { - /// Ok(n) => assert_eq!(*n, 1), - /// Err(_) => unreachable!(), - /// }; - /// ``` - pub fn try_lock(&self, key: ThreadKey) -> Result<RwLockReadGuard<'_, T, R>, ThreadKey> { - self.0.try_read(key) - } - - /// Attempts to create an exclusive lock without a key. Locking this - /// without exclusive access to the key is undefined behavior. - pub(crate) unsafe fn try_lock_no_key(&self) -> Option<RwLockReadRef<'_, T, R>> { - self.0.try_read_no_key() - } - - /// Immediately drops the guard, and consequently releases the shared lock - /// on the underlying [`RwLock`]. - /// - /// This function is equivalent to calling [`drop`] on the guard, except - /// that it returns the key that was used to create it. Alternately, the - /// guard will be automatically dropped when it goes out of scope. - /// - /// # Examples - /// - /// ``` - /// use happylock::{RwLock, ThreadKey}; - /// use happylock::rwlock::ReadLock; - /// - /// let key = ThreadKey::get().unwrap(); - /// let lock = RwLock::new(0); - /// let reader = ReadLock::new(&lock); - /// - /// let mut guard = reader.lock(key); - /// assert_eq!(*guard, 0); - /// let key = ReadLock::unlock(guard); - /// ``` - #[must_use] - pub fn unlock(guard: RwLockReadGuard<'_, T, R>) -> ThreadKey { - RwLock::unlock_read(guard) - } -} diff --git a/src/rwlock/rwlock.rs b/src/rwlock/rwlock.rs index f1cdca5..0dce710 100644 --- a/src/rwlock/rwlock.rs +++ b/src/rwlock/rwlock.rs @@ -147,6 +147,8 @@ impl<T, R: RawRwLock> RwLock<T, R> { /// use happylock::RwLock; /// /// let lock = RwLock::new(5); + /// + /// /// ``` #[must_use] pub const fn new(data: T) -> Self { @@ -211,9 +213,9 @@ impl<T, R> RwLock<T, R> { /// ``` /// use happylock::{RwLock, ThreadKey}; /// + /// let key = ThreadKey::get().unwrap(); /// let lock = RwLock::new(String::new()); /// { - /// let key = ThreadKey::get().unwrap(); /// let mut s = lock.write(key); /// *s = "modified".to_owned(); /// } @@ -228,7 +230,7 @@ impl<T, R> RwLock<T, R> { impl<T: ?Sized, R> RwLock<T, R> { /// Returns a mutable reference to the underlying data. /// - /// Since this call borrows `RwLock` mutably, no actual locking is taking + /// Since this call borrows `RwLock` mutably, no actual locking needs to take /// place. The mutable borrow statically guarantees that no locks exist. /// /// # Examples @@ -237,9 +239,9 @@ impl<T: ?Sized, R> RwLock<T, R> { /// use happylock::{ThreadKey, RwLock}; /// /// let key = ThreadKey::get().unwrap(); - /// let mut mutex = RwLock::new(0); - /// *mutex.get_mut() = 10; - /// assert_eq!(*mutex.read(key), 10); + /// let mut lock = RwLock::new(0); + /// *lock.get_mut() = 10; + /// assert_eq!(*lock.read(key), 10); /// ``` #[must_use] pub fn get_mut(&mut self) -> &mut T { @@ -349,7 +351,9 @@ impl<T: ?Sized, R: RawRwLock> RwLock<T, R> { /// /// The calling thread will be blocked until there are no more writers /// which hold the lock. There may be other readers currently inside the - /// lock when this method returns. + /// lock when this method returns. This method does not provide any guarantees + /// with respect to the ordering of whether contentious readers or writers + /// will acquire the lock first. /// /// Returns an RAII guard which will release this thread's shared access /// once it is dropped. @@ -360,21 +364,21 @@ impl<T: ?Sized, R: RawRwLock> RwLock<T, R> { /// # Examples /// /// ``` - /// use std::sync::Arc; /// use std::thread; /// use happylock::{RwLock, ThreadKey}; /// /// let key = ThreadKey::get().unwrap(); - /// let lock = Arc::new(RwLock::new(1)); - /// let c_lock = Arc::clone(&lock); + /// let lock = RwLock::new(1); /// /// let n = lock.read(key); /// assert_eq!(*n, 1); /// - /// thread::spawn(move || { - /// let key = ThreadKey::get().unwrap(); - /// let r = c_lock.read(key); - /// }).join().unwrap(); + /// thread::scope(|s| { + /// s.spawn(|| { + /// let key = ThreadKey::get().unwrap(); + /// let r = lock.read(key); + /// }); + /// }); /// ``` /// /// [`ThreadKey`]: `crate::ThreadKey` @@ -400,8 +404,8 @@ impl<T: ?Sized, R: RawRwLock> RwLock<T, R> { /// /// # Errors /// - /// If the `RwLock` could not be acquired because it was already locked - /// exclusively, then an error will be returned containing the given key. + /// This function will return an error containing the [`ThreadKey`] if the + /// `RwLock` could not be acquired because it was already locked exclusively. /// /// # Examples /// @@ -470,10 +474,11 @@ impl<T: ?Sized, R: RawRwLock> RwLock<T, R> { /// let key = ThreadKey::get().unwrap(); /// let lock = RwLock::new(1); /// - /// match lock.try_write(key) { - /// Ok(n) => assert_eq!(*n, 1), - /// Err(_) => unreachable!(), - /// }; + /// let mut n = lock.write(key); + /// *n = 2; + /// + /// let key = RwLock::unlock_write(n); + /// assert_eq!(*lock.read(key), 2); /// ``` /// /// [`ThreadKey`]: `crate::ThreadKey` @@ -486,10 +491,11 @@ impl<T: ?Sized, R: RawRwLock> RwLock<T, R> { } } - /// Attempts to lock this `RwLock` with exclusive write access. + /// Attempts to lock this `RwLock` with exclusive write access, without + /// blocking. /// /// This function does not block. If the lock could not be acquired at this - /// time, then `None` is returned. Otherwise, an RAII guard is returned + /// time, then `Err` is returned. Otherwise, an RAII guard is returned /// which will release the lock when it is dropped. /// /// This function does not provide any guarantees with respect to the @@ -498,8 +504,8 @@ impl<T: ?Sized, R: RawRwLock> RwLock<T, R> { /// /// # Errors /// - /// If the `RwLock` could not be acquired because it was already locked, - /// then an error will be returned containing the given key. + /// This function will return an error containing the [`ThreadKey`] if the + /// `RwLock` could not be acquired because it was already locked exclusively. /// /// # Examples /// @@ -509,8 +515,17 @@ impl<T: ?Sized, R: RawRwLock> RwLock<T, R> { /// let key = ThreadKey::get().unwrap(); /// let lock = RwLock::new(1); /// + /// let key = match lock.try_write(key) { + /// Ok(mut n) => { + /// assert_eq!(*n, 1); + /// *n = 2; + /// RwLock::unlock_write(n) + /// } + /// Err(_) => unreachable!(), + /// }; + /// /// let n = lock.read(key); - /// assert_eq!(*n, 1); + /// assert_eq!(*n, 2); /// ``` pub fn try_write(&self, key: ThreadKey) -> Result<RwLockWriteGuard<'_, T, R>, ThreadKey> { unsafe { @@ -532,7 +547,7 @@ impl<T: ?Sized, R: RawRwLock> RwLock<T, R> { /// Immediately drops the guard, and consequently releases the shared lock. /// /// This function is equivalent to calling [`drop`] on the guard, except - /// that it returns the key that was used to create it. Alternately, the + /// that it returns the key that was used to create it. Alternatively, the /// guard will be automatically dropped when it goes out of scope. /// /// # Examples @@ -556,9 +571,9 @@ impl<T: ?Sized, R: RawRwLock> RwLock<T, R> { /// Immediately drops the guard, and consequently releases the exclusive /// lock. /// - /// This function is equivalent to calling [`drop`] on the guard, except - /// that it returns the key that was used to create it. Alternately, the - /// guard will be automatically dropped when it goes out of scope. + /// This function is equivalent to calling [`drop`] on the guard, except that + /// it returns the key that was used to create it. Alternatively, the guard + /// will be automatically dropped when it goes out of scope. /// /// # Examples /// @@ -571,6 +586,9 @@ impl<T: ?Sized, R: RawRwLock> RwLock<T, R> { /// let mut guard = lock.write(key); /// *guard += 20; /// let key = RwLock::unlock_write(guard); + /// + /// let guard = lock.read(key); + /// assert_eq!(*guard, 20); /// ``` #[must_use] pub fn unlock_write(guard: RwLockWriteGuard<'_, T, R>) -> ThreadKey { diff --git a/src/rwlock/write_lock.rs b/src/rwlock/write_lock.rs deleted file mode 100644 index 6469a67..0000000 --- a/src/rwlock/write_lock.rs +++ /dev/null @@ -1,229 +0,0 @@ -use std::fmt::Debug; - -use lock_api::RawRwLock; - -use crate::lockable::{Lockable, RawLock}; -use crate::{Keyable, ThreadKey}; - -use super::{RwLock, RwLockWriteGuard, RwLockWriteRef, WriteLock}; - -unsafe impl<T, R: RawRwLock> RawLock for WriteLock<'_, T, R> { - fn poison(&self) { - self.0.poison() - } - - unsafe fn raw_write(&self) { - self.0.raw_write() - } - - unsafe fn raw_try_write(&self) -> bool { - self.0.raw_try_write() - } - - unsafe fn raw_unlock_write(&self) { - self.0.raw_unlock_write() - } - - unsafe fn raw_read(&self) { - self.0.raw_write() - } - - unsafe fn raw_try_read(&self) -> bool { - self.0.raw_try_write() - } - - unsafe fn raw_unlock_read(&self) { - self.0.raw_unlock_write() - } -} - -unsafe impl<T, R: RawRwLock> Lockable for WriteLock<'_, T, R> { - type Guard<'g> - = RwLockWriteRef<'g, T, R> - where - Self: 'g; - - type DataMut<'a> - = &'a mut T - where - Self: 'a; - - fn get_ptrs<'a>(&'a self, ptrs: &mut Vec<&'a dyn RawLock>) { - ptrs.push(self.0); - } - - unsafe fn guard(&self) -> Self::Guard<'_> { - RwLockWriteRef::new(self.as_ref()) - } - - unsafe fn data_mut(&self) -> Self::DataMut<'_> { - self.0.data_mut() - } -} - -// Technically, the exclusive locks can also be shared, but there's currently -// no way to express that. I don't think I want to ever express that. - -#[mutants::skip] -#[cfg(not(tarpaulin_include))] -impl<T: Debug, R: RawRwLock> Debug for WriteLock<'_, T, R> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - // safety: this is just a try lock, and the value is dropped - // immediately after, so there's no risk of blocking ourselves - // or any other threads - // It makes zero sense to try using an exclusive lock for this, so this - // is the only time when WriteLock does a read. - if let Some(value) = unsafe { self.0.try_read_no_key() } { - f.debug_struct("WriteLock").field("data", &&*value).finish() - } else { - struct LockedPlaceholder; - impl Debug for LockedPlaceholder { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str("<locked>") - } - } - - f.debug_struct("WriteLock") - .field("data", &LockedPlaceholder) - .finish() - } - } -} - -impl<'l, T, R> From<&'l RwLock<T, R>> for WriteLock<'l, T, R> { - fn from(value: &'l RwLock<T, R>) -> Self { - Self::new(value) - } -} - -impl<T: ?Sized, R> AsRef<RwLock<T, R>> for WriteLock<'_, T, R> { - fn as_ref(&self) -> &RwLock<T, R> { - self.0 - } -} - -impl<'l, T, R> WriteLock<'l, T, R> { - /// Creates a new `WriteLock` which accesses the given [`RwLock`] - /// - /// # Examples - /// - /// ``` - /// use happylock::{rwlock::WriteLock, RwLock}; - /// - /// let lock = RwLock::new(5); - /// let write_lock = WriteLock::new(&lock); - /// ``` - #[must_use] - pub const fn new(rwlock: &'l RwLock<T, R>) -> Self { - Self(rwlock) - } -} - -impl<T, R: RawRwLock> WriteLock<'_, T, R> { - pub fn scoped_lock<'a, Ret>(&'a self, key: impl Keyable, f: impl Fn(&'a mut T) -> Ret) -> Ret { - self.0.scoped_write(key, f) - } - - pub fn scoped_try_lock<'a, Key: Keyable, Ret>( - &'a self, - key: Key, - f: impl Fn(&'a mut T) -> Ret, - ) -> Result<Ret, Key> { - self.0.scoped_try_write(key, f) - } - - /// Locks the underlying [`RwLock`] with exclusive write access, blocking - /// the current until it can be acquired. - /// - /// This function will not return while other writers or readers currently - /// have access to the lock. - /// - /// Returns an RAII guard which will drop the write access of this `RwLock` - /// when dropped. - /// - /// Because this method takes a [`ThreadKey`], it's not possible for this - /// method to cause a deadlock. - /// - /// # Examples - /// - /// ``` - /// use happylock::{ThreadKey, RwLock}; - /// use happylock::rwlock::WriteLock; - /// - /// let key = ThreadKey::get().unwrap(); - /// let lock = RwLock::new(1); - /// let writer = WriteLock::new(&lock); - /// - /// let mut n = writer.lock(key); - /// *n += 2; - /// ``` - /// - /// [`ThreadKey`]: `crate::ThreadKey` - #[must_use] - pub fn lock(&self, key: ThreadKey) -> RwLockWriteGuard<'_, T, R> { - self.0.write(key) - } - - /// Attempts to lock the underlying [`RwLock`] with exclusive write access. - /// - /// This function does not block. If the lock could not be acquired at this - /// time, then `None` is returned. Otherwise, an RAII guard is returned - /// which will release the lock when it is dropped. - /// - /// This function does not provide any guarantees with respect to the - /// ordering of whether contentious readers or writers will acquire the - /// lock first. - /// - /// # Errors - /// - /// If the [`RwLock`] could not be acquired because it was already locked, - /// then an error will be returned containing the given key. - /// - /// # Examples - /// - /// ``` - /// use happylock::{RwLock, ThreadKey}; - /// use happylock::rwlock::WriteLock; - /// - /// let key = ThreadKey::get().unwrap(); - /// let lock = RwLock::new(1); - /// let writer = WriteLock::new(&lock); - /// - /// match writer.try_lock(key) { - /// Ok(n) => assert_eq!(*n, 1), - /// Err(_) => unreachable!(), - /// }; - /// ``` - pub fn try_lock(&self, key: ThreadKey) -> Result<RwLockWriteGuard<'_, T, R>, ThreadKey> { - self.0.try_write(key) - } - - // There's no `try_lock_no_key`. Instead, `try_read_no_key` is called on - // the referenced `RwLock`. - - /// Immediately drops the guard, and consequently releases the exclusive - /// lock on the underlying [`RwLock`]. - /// - /// This function is equivalent to calling [`drop`] on the guard, except - /// that it returns the key that was used to create it. Alternately, the - /// guard will be automatically dropped when it goes out of scope. - /// - /// # Examples - /// - /// ``` - /// use happylock::{RwLock, ThreadKey}; - /// use happylock::rwlock::WriteLock; - /// - /// let key = ThreadKey::get().unwrap(); - /// let lock = RwLock::new(0); - /// let writer = WriteLock::new(&lock); - /// - /// let mut guard = writer.lock(key); - /// *guard += 20; - /// let key = WriteLock::unlock(guard); - /// ``` - #[must_use] - pub fn unlock(guard: RwLockWriteGuard<'_, T, R>) -> ThreadKey { - RwLock::unlock_write(guard) - } -} |
