summaryrefslogtreecommitdiff
path: root/src/rwlock
diff options
context:
space:
mode:
authorBotahamec <botahamec@outlook.com>2025-03-29 17:34:10 -0400
committerBotahamec <botahamec@outlook.com>2025-03-29 17:34:10 -0400
commit17dab88a7b4bc86cf156a1e0ac1bac19e6f9f5c6 (patch)
tree59f7ddaf743899be6b5ddd7ecf1b38882c50087d /src/rwlock
parent3a299432173f98521d0aeb840dafb6710ce27f82 (diff)
Clean up existing documentation
Diffstat (limited to 'src/rwlock')
-rw-r--r--src/rwlock/read_lock.rs251
-rw-r--r--src/rwlock/rwlock.rs74
-rw-r--r--src/rwlock/write_lock.rs229
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)
- }
-}