summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMica White <botahamec@gmail.com>2024-12-26 12:06:47 -0500
committerMica White <botahamec@gmail.com>2024-12-26 13:23:40 -0500
commit9eec9ab94bbe5c9fbd52d5bbf393fe1ddcc6fc26 (patch)
treea55cb81db2167cd3caf3330d503c2e9cacd24fd4
parentdc16634f4abdb1e830d2749e64b419740702b302 (diff)
Documentation
-rw-r--r--src/collection/boxed.rs66
-rw-r--r--src/collection/owned.rs66
-rw-r--r--src/collection/ref.rs41
-rw-r--r--src/collection/retry.rs83
-rw-r--r--src/handle_unwind.rs5
-rw-r--r--src/mutex/mutex.rs7
-rw-r--r--src/poisonable.rs2
-rw-r--r--src/poisonable/poisonable.rs2
-rw-r--r--src/rwlock/read_lock.rs76
-rw-r--r--src/rwlock/rwlock.rs22
-rw-r--r--src/rwlock/write_lock.rs75
11 files changed, 407 insertions, 38 deletions
diff --git a/src/collection/boxed.rs b/src/collection/boxed.rs
index c0cc294..7a84b2a 100644
--- a/src/collection/boxed.rs
+++ b/src/collection/boxed.rs
@@ -211,6 +211,23 @@ impl<L> BoxedLockCollection<L> {
// references happening at the same time
/// Gets an immutable reference to the underlying data
+ ///
+ /// # 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.child().0.lock(key);
+ /// assert_eq!(*guard, 42);
+ /// ```
#[must_use]
pub fn child(&self) -> &L {
unsafe {
@@ -375,9 +392,14 @@ impl<L: Lockable> BoxedLockCollection<L> {
/// 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.
+ /// If the access could not be granted at this time, then `Err` is
+ /// returned. Otherwise, an RAII guard is returned which will release the
+ /// locks when it is dropped.
+ ///
+ /// # Errors
+ ///
+ /// If any locks in the collection are already locked, then an error
+ /// containing the given key is returned.
///
/// # Examples
///
@@ -480,9 +502,14 @@ impl<L: Sharable> BoxedLockCollection<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.
+ /// 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.
+ ///
+ /// # Errors
+ ///
+ /// If any of the locks in the collection are already locked, then an error
+ /// is returned containing the given key.
///
/// # Examples
///
@@ -494,29 +521,29 @@ impl<L: Sharable> BoxedLockCollection<L> {
/// let lock = LockCollection::new(data);
///
/// match lock.try_read(key) {
- /// Some(mut guard) => {
+ /// Ok(mut guard) => {
/// assert_eq!(*guard.0, 5);
/// assert_eq!(*guard.1, "6");
/// },
- /// None => unreachable!(),
+ /// Err(_) => unreachable!(),
/// };
///
/// ```
pub fn try_read<'g, 'key: 'g, Key: Keyable + 'key>(
&'g self,
key: Key,
- ) -> Option<LockGuard<'key, L::ReadGuard<'g>, Key>> {
+ ) -> Result<LockGuard<'key, L::ReadGuard<'g>, Key>, Key> {
let guard = unsafe {
// safety: we have the thread key
if !self.raw_try_read() {
- return None;
+ return Err(key);
}
// safety: we've acquired the locks
self.child().read_guard()
};
- Some(LockGuard {
+ Ok(LockGuard {
guard,
key,
_phantom: PhantomData,
@@ -546,6 +573,23 @@ impl<L: Sharable> BoxedLockCollection<L> {
}
}
+impl<L: LockableIntoInner> BoxedLockCollection<L> {
+ /// Consumes this `BoxedLockCollection`, returning the underlying data.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use happylock::{Mutex, LockCollection};
+ ///
+ /// let mutex = LockCollection::new([Mutex::new(0), Mutex::new(0)]);
+ /// assert_eq!(mutex.into_inner(), [0, 0]);
+ /// ```
+ #[must_use]
+ pub fn into_inner(self) -> <Self as LockableIntoInner>::Inner {
+ LockableIntoInner::into_inner(self)
+ }
+}
+
impl<'a, L: 'a> BoxedLockCollection<L>
where
&'a L: IntoIterator,
diff --git a/src/collection/owned.rs b/src/collection/owned.rs
index 3d6fa93..7436a6e 100644
--- a/src/collection/owned.rs
+++ b/src/collection/owned.rs
@@ -213,9 +213,14 @@ impl<L: OwnedLockable> OwnedLockCollection<L> {
/// 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.
+ /// If the access could not be granted at this time, then `Err` is
+ /// returned. Otherwise, an RAII guard is returned which will release the
+ /// locks when it is dropped.
+ ///
+ /// # Errors
+ ///
+ /// If any of the locks in this collection are already locked, this returns
+ /// an error containing the given key.
///
/// # Examples
///
@@ -324,9 +329,14 @@ impl<L: Sharable> OwnedLockCollection<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.
+ /// 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.
+ ///
+ /// # Errors
+ ///
+ /// If any of the locks in this collection can't be acquired, then an error
+ /// is returned containing the given key.
///
/// # Examples
///
@@ -415,18 +425,62 @@ impl<L> OwnedLockCollection<L> {
self.data
}
+ /// Gets a mutable reference to the underlying collection.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use happylock::{Mutex, ThreadKey};
+ /// use happylock::collection::OwnedLockCollection;
+ ///
+ /// let data = (Mutex::new(42), Mutex::new(""));
+ /// let mut lock = OwnedLockCollection::new(data);
+ ///
+ /// let key = ThreadKey::get().unwrap();
+ /// let mut inner = lock.child_mut();
+ /// let guard = inner.0.get_mut();
+ /// assert_eq!(*guard, 42);
+ /// ```
+ #[must_use]
pub fn child_mut(&mut self) -> &mut L {
&mut self.data
}
}
impl<L: LockableGetMut> OwnedLockCollection<L> {
+ /// Gets a mutable reference to the data behind this `OwnedLockCollection`.
+ ///
+ /// Since this call borrows the `OwnedLockCollection` mutably, no actual
+ /// locking needs to take place - the mutable borrow statically guarantees
+ /// no locks exist.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use happylock::{Mutex, LockCollection};
+ /// use happylock::collection::OwnedLockCollection;
+ ///
+ /// let mut mutex = OwnedLockCollection::new([Mutex::new(0), Mutex::new(0)]);
+ /// assert_eq!(mutex.get_mut(), [&mut 0, &mut 0]);
+ /// ```
pub fn get_mut(&mut self) -> L::Inner<'_> {
LockableGetMut::get_mut(self)
}
}
impl<L: LockableIntoInner> OwnedLockCollection<L> {
+ /// Consumes this `OwnedLockCollection`, returning the underlying data.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use happylock::{Mutex, LockCollection};
+ /// use happylock::collection::OwnedLockCollection;
+ ///
+ /// let mutex = OwnedLockCollection::new([Mutex::new(0), Mutex::new(0)]);
+ /// assert_eq!(mutex.into_inner(), [0, 0]);
+ /// ```
+ #[must_use]
pub fn into_inner(self) -> L::Inner {
LockableIntoInner::into_inner(self)
}
diff --git a/src/collection/ref.rs b/src/collection/ref.rs
index a9c3579..1e17412 100644
--- a/src/collection/ref.rs
+++ b/src/collection/ref.rs
@@ -151,6 +151,25 @@ impl<'a, L: OwnedLockable> RefLockCollection<'a, L> {
}
impl<L> RefLockCollection<'_, L> {
+ /// Gets an immutable reference to the underlying data
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use happylock::{Mutex, ThreadKey};
+ /// use happylock::collection::RefLockCollection;
+ ///
+ /// 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 = RefLockCollection::try_new(&data).unwrap();
+ ///
+ /// let key = ThreadKey::get().unwrap();
+ /// let guard = lock.child().0.lock(key);
+ /// assert_eq!(*guard, 42);
+ /// ```
#[must_use]
pub const fn child(&self) -> &L {
self.data
@@ -255,9 +274,14 @@ impl<'a, L: Lockable> RefLockCollection<'a, L> {
/// 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.
+ /// If the access could not be granted at this time, then `Err` is
+ /// returned. Otherwise, an RAII guard is returned which will release the
+ /// locks when it is dropped.
+ ///
+ /// # Errors
+ ///
+ /// If any of the locks in the collection are already locked, then an error
+ /// is returned containing the given key.
///
/// # Examples
///
@@ -364,9 +388,14 @@ 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.
+ /// 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.
+ ///
+ /// # Errors
+ ///
+ /// If any of the locks in the collection are already locked, then an error
+ /// is returned containing the given key.
///
/// # Examples
///
diff --git a/src/collection/retry.rs b/src/collection/retry.rs
index 83842f8..db09ebf 100644
--- a/src/collection/retry.rs
+++ b/src/collection/retry.rs
@@ -422,10 +422,44 @@ impl<L> RetryingLockCollection<L> {
Self { data }
}
+ /// Gets an immutable reference to the underlying 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.child();
+ /// let guard = inner.0.lock(key);
+ /// assert_eq!(*guard, 42);
+ /// ```
+ #[must_use]
pub const fn child(&self) -> &L {
&self.data
}
+ /// Gets a mutable reference to the underlying collection.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use happylock::{Mutex, ThreadKey};
+ /// use happylock::collection::RetryingLockCollection;
+ ///
+ /// let data = (Mutex::new(42), Mutex::new(""));
+ /// let mut lock = RetryingLockCollection::new(data);
+ ///
+ /// let key = ThreadKey::get().unwrap();
+ /// let mut inner = lock.child_mut();
+ /// let guard = inner.0.get_mut();
+ /// assert_eq!(*guard, 42);
+ /// ```
+ #[must_use]
pub fn child_mut(&mut self) -> &mut L {
&mut self.data
}
@@ -515,9 +549,14 @@ impl<L: Lockable> RetryingLockCollection<L> {
/// 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.
+ /// If the access could not be granted at this time, then `Err` is
+ /// returned. Otherwise, an RAII guard is returned which will release the
+ /// locks when it is dropped.
+ ///
+ /// # Errors
+ ///
+ /// If any of the locks in the collection are already locked, then an error
+ /// is returned containing the given key.
///
/// # Examples
///
@@ -622,9 +661,14 @@ impl<L: Sharable> RetryingLockCollection<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.
+ /// 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.
+ ///
+ /// # Errors
+ ///
+ /// If shared access cannot be acquired at this time, then an error is
+ /// returned containing the given key.
///
/// # Examples
///
@@ -685,12 +729,39 @@ impl<L: Sharable> RetryingLockCollection<L> {
}
impl<L: LockableGetMut> RetryingLockCollection<L> {
+ /// Gets a mutable reference to the data behind this
+ /// `RetryingLockCollection`.
+ ///
+ /// Since this call borrows the `RetryingLockCollection` mutably, no actual
+ /// locking needs to take place - the mutable borrow statically guarantees
+ /// no locks exist.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use happylock::{Mutex, LockCollection};
+ /// use happylock::collection::RetryingLockCollection;
+ ///
+ /// let mut mutex = RetryingLockCollection::new([Mutex::new(0), Mutex::new(0)]);
+ /// assert_eq!(mutex.get_mut(), [&mut 0, &mut 0]);
+ /// ```
pub fn get_mut(&mut self) -> L::Inner<'_> {
LockableGetMut::get_mut(self)
}
}
impl<L: LockableIntoInner> RetryingLockCollection<L> {
+ /// Consumes this `RetryingLockCollection`, returning the underlying data.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use happylock::{Mutex, LockCollection};
+ /// use happylock::collection::RetryingLockCollection;
+ ///
+ /// let mutex = RetryingLockCollection::new([Mutex::new(0), Mutex::new(0)]);
+ /// assert_eq!(mutex.into_inner(), [0, 0]);
+ /// ```
pub fn into_inner(self) -> L::Inner {
LockableIntoInner::into_inner(self)
}
diff --git a/src/handle_unwind.rs b/src/handle_unwind.rs
index 220d379..42b6fc5 100644
--- a/src/handle_unwind.rs
+++ b/src/handle_unwind.rs
@@ -1,6 +1,9 @@
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
-/// Runs `try_fn`. If it unwinds, it will run `catch` and then continue unwinding
+/// Runs `try_fn`. If it unwinds, it will run `catch` and then continue
+/// unwinding. This is used instead of `scopeguard` to ensure the `catch`
+/// function doesn't run if the thread is already panicking. The unwind
+/// must specifically be caused by the `try_fn`
pub fn handle_unwind<R, F: FnOnce() -> R, G: FnOnce()>(try_fn: F, catch: G) -> R {
let try_fn = AssertUnwindSafe(try_fn);
catch_unwind(try_fn).unwrap_or_else(|e| {
diff --git a/src/mutex/mutex.rs b/src/mutex/mutex.rs
index 505ff83..9b71ea4 100644
--- a/src/mutex/mutex.rs
+++ b/src/mutex/mutex.rs
@@ -247,9 +247,14 @@ impl<T: ?Sized, R: RawMutex> Mutex<T, R> {
/// Attempts to lock the `Mutex` 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
+ /// lock when it is dropped.
+ ///
/// # Errors
///
- /// Returns [`Err`] if the `Mutex` cannot be locked without blocking.
+ /// If the mutex could not be acquired because it is already locked, then
+ /// this call will return an error containing the given key.
///
/// # Examples
///
diff --git a/src/poisonable.rs b/src/poisonable.rs
index 6af9f9a..f9152d1 100644
--- a/src/poisonable.rs
+++ b/src/poisonable.rs
@@ -1,8 +1,6 @@
use std::marker::PhantomData;
use std::sync::atomic::AtomicBool;
-use crate::Keyable;
-
mod error;
mod flag;
mod guard;
diff --git a/src/poisonable/poisonable.rs b/src/poisonable/poisonable.rs
index ea51dd5..b49aa94 100644
--- a/src/poisonable/poisonable.rs
+++ b/src/poisonable/poisonable.rs
@@ -560,7 +560,7 @@ impl<L: LockableIntoInner> Poisonable<L> {
impl<L: LockableGetMut + RawLock> Poisonable<L> {
/// Returns a mutable reference to the underlying data.
///
- /// Since this call borrows the `Poisonable` mutable, no actual locking
+ /// Since this call borrows the `Poisonable` mutably, no actual locking
/// needs to take place - the mutable borrow statically guarantees no locks
/// exist.
///
diff --git a/src/rwlock/read_lock.rs b/src/rwlock/read_lock.rs
index d719a50..ae593e2 100644
--- a/src/rwlock/read_lock.rs
+++ b/src/rwlock/read_lock.rs
@@ -87,6 +87,34 @@ impl<'l, T, R> ReadLock<'l, T, R> {
impl<T: ?Sized, R: RawRwLock> ReadLock<'_, T, R> {
/// 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: &'static mut RwLock<_> = Box::leak(Box::new(RwLock::new(1)));
+ /// let reader = ReadLock::new(&lock);
+ ///
+ /// let n = reader.lock(key);
+ /// assert_eq!(*n, 1);
+ /// ```
+ ///
+ /// [`ThreadKey`]: `crate::ThreadKey`
pub fn lock<'s, 'key: 's, Key: Keyable + 'key>(
&'s self,
key: Key,
@@ -96,6 +124,35 @@ impl<T: ?Sized, R: RawRwLock> ReadLock<'_, T, R> {
/// 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<'s, 'key: 's, Key: Keyable + 'key>(
&'s self,
key: Key,
@@ -111,6 +168,25 @@ impl<T: ?Sized, R: RawRwLock> ReadLock<'_, T, R> {
/// 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);
+ /// ```
pub fn unlock<'key, Key: Keyable + 'key>(guard: RwLockReadGuard<'_, 'key, T, Key, R>) -> Key {
RwLock::unlock_read(guard)
}
diff --git a/src/rwlock/rwlock.rs b/src/rwlock/rwlock.rs
index 6432128..a249675 100644
--- a/src/rwlock/rwlock.rs
+++ b/src/rwlock/rwlock.rs
@@ -277,10 +277,19 @@ impl<T: ?Sized, R: RawRwLock> RwLock<T, R> {
/// Attempts to acquire this `RwLock` with shared read access without
/// blocking.
///
- /// If the access could not be granted at this time, then `None` is
+ /// 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
///
/// ```
@@ -351,8 +360,10 @@ impl<T: ?Sized, R: RawRwLock> RwLock<T, R> {
/// let key = ThreadKey::get().unwrap();
/// let lock = RwLock::new(1);
///
- /// let mut n = lock.write(key);
- /// *n += 2;
+ /// match lock.try_write(key) {
+ /// Ok(n) => assert_eq!(*n, 1),
+ /// Err(_) => unreachable!(),
+ /// };
/// ```
///
/// [`ThreadKey`]: `crate::ThreadKey`
@@ -378,6 +389,11 @@ impl<T: ?Sized, R: RawRwLock> RwLock<T, R> {
/// 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
///
/// ```
diff --git a/src/rwlock/write_lock.rs b/src/rwlock/write_lock.rs
index e9be750..ff00c06 100644
--- a/src/rwlock/write_lock.rs
+++ b/src/rwlock/write_lock.rs
@@ -81,6 +81,31 @@ impl<'l, T, R> WriteLock<'l, T, R> {
impl<T: ?Sized, R: RawRwLock> WriteLock<'_, T, R> {
/// 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`
pub fn lock<'s, 'key: 's, Key: Keyable + 'key>(
&'s self,
key: Key,
@@ -89,6 +114,35 @@ impl<T: ?Sized, R: RawRwLock> WriteLock<'_, T, R> {
}
/// 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<'s, 'key: 's, Key: Keyable + 'key>(
&'s self,
key: Key,
@@ -100,7 +154,26 @@ impl<T: ?Sized, R: RawRwLock> WriteLock<'_, T, R> {
// the referenced `RwLock`.
/// Immediately drops the guard, and consequently releases the exclusive
- /// lock.
+ /// 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);
+ /// ```
pub fn unlock<'key, Key: Keyable + 'key>(guard: RwLockWriteGuard<'_, 'key, T, Key, R>) -> Key {
RwLock::unlock_write(guard)
}