summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMica White <botahamec@outlook.com>2025-12-08 20:03:40 -0500
committerMica White <botahamec@outlook.com>2025-12-08 20:03:40 -0500
commit8be852662a432d96553fcf7a9fc57c4f3c92baae (patch)
tree90c93f4d9d24538c64c7552a83ef8ae29172e78a
parent17dab88a7b4bc86cf156a1e0ac1bac19e6f9f5c6 (diff)
Stuff
-rwxr-xr-x[-rw-r--r--].gitignore0
-rwxr-xr-x[-rw-r--r--].vscode/launch.json0
-rwxr-xr-x[-rw-r--r--]Cargo.toml0
-rwxr-xr-x[-rw-r--r--]LICENSE0
-rwxr-xr-x[-rw-r--r--]README.md0
-rwxr-xr-x[-rw-r--r--]examples/basic.rs0
-rwxr-xr-x[-rw-r--r--]examples/dining_philosophers.rs0
-rwxr-xr-x[-rw-r--r--]examples/dining_philosophers_retry.rs0
-rwxr-xr-x[-rw-r--r--]examples/double_mutex.rs0
-rwxr-xr-x[-rw-r--r--]examples/fibonacci.rs0
-rwxr-xr-x[-rw-r--r--]examples/list.rs0
-rwxr-xr-x[-rw-r--r--]happylock.md0
-rwxr-xr-x[-rw-r--r--]package-lock.json0
-rwxr-xr-x[-rw-r--r--]package.json0
-rwxr-xr-x[-rw-r--r--]rustfmt.toml0
-rwxr-xr-x[-rw-r--r--]src/collection.rs0
-rwxr-xr-x[-rw-r--r--]src/collection/boxed.rs16
-rwxr-xr-x[-rw-r--r--]src/collection/guard.rs0
-rwxr-xr-x[-rw-r--r--]src/collection/owned.rs16
-rwxr-xr-x[-rw-r--r--]src/collection/ref.rs16
-rwxr-xr-x[-rw-r--r--]src/collection/retry.rs16
-rwxr-xr-x[-rw-r--r--]src/collection/utils.rs0
-rwxr-xr-x[-rw-r--r--]src/handle_unwind.rs0
-rwxr-xr-x[-rw-r--r--]src/key.rs0
-rwxr-xr-x[-rw-r--r--]src/lib.rs0
-rwxr-xr-x[-rw-r--r--]src/lockable.rs0
-rwxr-xr-x[-rw-r--r--]src/mutex.rs0
-rwxr-xr-x[-rw-r--r--]src/mutex/guard.rs0
-rwxr-xr-x[-rw-r--r--]src/mutex/mutex.rs62
-rwxr-xr-x[-rw-r--r--]src/poisonable.rs0
-rwxr-xr-x[-rw-r--r--]src/poisonable/error.rs0
-rwxr-xr-x[-rw-r--r--]src/poisonable/flag.rs0
-rwxr-xr-x[-rw-r--r--]src/poisonable/guard.rs0
-rwxr-xr-x[-rw-r--r--]src/poisonable/poisonable.rs149
-rwxr-xr-x[-rw-r--r--]src/rwlock.rs0
-rwxr-xr-x[-rw-r--r--]src/rwlock/read_guard.rs0
-rwxr-xr-x[-rw-r--r--]src/rwlock/rwlock.rs145
-rwxr-xr-x[-rw-r--r--]src/rwlock/write_guard.rs0
-rwxr-xr-x[-rw-r--r--]src/thread.rs0
-rwxr-xr-x[-rw-r--r--]src/thread/scope.rst0
-rwxr-xr-x[-rw-r--r--]tarpaulin-report.html0
-rwxr-xr-x[-rw-r--r--]tests/evil_mutex.rs0
-rwxr-xr-x[-rw-r--r--]tests/evil_rwlock.rs0
-rwxr-xr-x[-rw-r--r--]tests/evil_try_mutex.rs0
-rwxr-xr-x[-rw-r--r--]tests/evil_try_rwlock.rs0
-rwxr-xr-x[-rw-r--r--]tests/evil_unlock_mutex.rs0
-rwxr-xr-x[-rw-r--r--]tests/evil_unlock_rwlock.rs0
-rwxr-xr-x[-rw-r--r--]tests/forget.rs0
-rwxr-xr-x[-rw-r--r--]tests/retry.rs0
-rwxr-xr-x[-rw-r--r--]tests/retry_rw.rs0
50 files changed, 395 insertions, 25 deletions
diff --git a/.gitignore b/.gitignore
index 3730b37..3730b37 100644..100755
--- a/.gitignore
+++ b/.gitignore
diff --git a/.vscode/launch.json b/.vscode/launch.json
index a8e32a8..a8e32a8 100644..100755
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
diff --git a/Cargo.toml b/Cargo.toml
index 5c62d6a..5c62d6a 100644..100755
--- a/Cargo.toml
+++ b/Cargo.toml
diff --git a/LICENSE b/LICENSE
index 0e259d4..0e259d4 100644..100755
--- a/LICENSE
+++ b/LICENSE
diff --git a/README.md b/README.md
index bac3024..bac3024 100644..100755
--- a/README.md
+++ b/README.md
diff --git a/examples/basic.rs b/examples/basic.rs
index b9c5641..b9c5641 100644..100755
--- a/examples/basic.rs
+++ b/examples/basic.rs
diff --git a/examples/dining_philosophers.rs b/examples/dining_philosophers.rs
index dc4dd51..dc4dd51 100644..100755
--- a/examples/dining_philosophers.rs
+++ b/examples/dining_philosophers.rs
diff --git a/examples/dining_philosophers_retry.rs b/examples/dining_philosophers_retry.rs
index 4302bc7..4302bc7 100644..100755
--- a/examples/dining_philosophers_retry.rs
+++ b/examples/dining_philosophers_retry.rs
diff --git a/examples/double_mutex.rs b/examples/double_mutex.rs
index ea61f0d..ea61f0d 100644..100755
--- a/examples/double_mutex.rs
+++ b/examples/double_mutex.rs
diff --git a/examples/fibonacci.rs b/examples/fibonacci.rs
index 869ef71..869ef71 100644..100755
--- a/examples/fibonacci.rs
+++ b/examples/fibonacci.rs
diff --git a/examples/list.rs b/examples/list.rs
index fb3a405..fb3a405 100644..100755
--- a/examples/list.rs
+++ b/examples/list.rs
diff --git a/happylock.md b/happylock.md
index 5d7d51f..5d7d51f 100644..100755
--- a/happylock.md
+++ b/happylock.md
diff --git a/package-lock.json b/package-lock.json
index 0dc16d2..0dc16d2 100644..100755
--- a/package-lock.json
+++ b/package-lock.json
diff --git a/package.json b/package.json
index 0967ef4..0967ef4 100644..100755
--- a/package.json
+++ b/package.json
diff --git a/rustfmt.toml b/rustfmt.toml
index a49072a..a49072a 100644..100755
--- a/rustfmt.toml
+++ b/rustfmt.toml
diff --git a/src/collection.rs b/src/collection.rs
index 9c04fbb..9c04fbb 100644..100755
--- a/src/collection.rs
+++ b/src/collection.rs
diff --git a/src/collection/boxed.rs b/src/collection/boxed.rs
index 7767b31..3708c8b 100644..100755
--- a/src/collection/boxed.rs
+++ b/src/collection/boxed.rs
@@ -361,14 +361,18 @@ impl<L: Lockable> BoxedLockCollection<L> {
}
}
- pub fn scoped_lock<'a, R>(&'a self, key: impl Keyable, f: impl Fn(L::DataMut<'a>) -> R) -> R {
+ pub fn scoped_lock<'a, R>(
+ &'a self,
+ key: impl Keyable,
+ f: impl FnOnce(L::DataMut<'a>) -> R,
+ ) -> R {
scoped_write(self, key, f)
}
pub fn scoped_try_lock<'a, Key: Keyable, R>(
&'a self,
key: Key,
- f: impl Fn(L::DataMut<'a>) -> R,
+ f: impl FnOnce(L::DataMut<'a>) -> R,
) -> Result<R, Key> {
scoped_try_write(self, key, f)
}
@@ -473,14 +477,18 @@ impl<L: Lockable> BoxedLockCollection<L> {
}
impl<L: Sharable> BoxedLockCollection<L> {
- pub fn scoped_read<'a, R>(&'a self, key: impl Keyable, f: impl Fn(L::DataRef<'a>) -> R) -> R {
+ pub fn scoped_read<'a, R>(
+ &'a self,
+ key: impl Keyable,
+ f: impl FnOnce(L::DataRef<'a>) -> R,
+ ) -> R {
scoped_read(self, key, f)
}
pub fn scoped_try_read<'a, Key: Keyable, R>(
&'a self,
key: Key,
- f: impl Fn(L::DataRef<'a>) -> R,
+ f: impl FnOnce(L::DataRef<'a>) -> R,
) -> Result<R, Key> {
scoped_try_read(self, key, f)
}
diff --git a/src/collection/guard.rs b/src/collection/guard.rs
index ab66ffe..ab66ffe 100644..100755
--- a/src/collection/guard.rs
+++ b/src/collection/guard.rs
diff --git a/src/collection/owned.rs b/src/collection/owned.rs
index 38e6a16..d6889d1 100644..100755
--- a/src/collection/owned.rs
+++ b/src/collection/owned.rs
@@ -189,14 +189,18 @@ impl<L: OwnedLockable> OwnedLockCollection<L> {
Self { data }
}
- pub fn scoped_lock<'a, R>(&'a self, key: impl Keyable, f: impl Fn(L::DataMut<'a>) -> R) -> R {
+ pub fn scoped_lock<'a, R>(
+ &'a self,
+ key: impl Keyable,
+ f: impl FnOnce(L::DataMut<'a>) -> R,
+ ) -> R {
scoped_write(self, key, f)
}
pub fn scoped_try_lock<'a, Key: Keyable, R>(
&'a self,
key: Key,
- f: impl Fn(L::DataMut<'a>) -> R,
+ f: impl FnOnce(L::DataMut<'a>) -> R,
) -> Result<R, Key> {
scoped_try_write(self, key, f)
}
@@ -304,14 +308,18 @@ impl<L: OwnedLockable> OwnedLockCollection<L> {
}
impl<L: Sharable> OwnedLockCollection<L> {
- pub fn scoped_read<'a, R>(&'a self, key: impl Keyable, f: impl Fn(L::DataRef<'a>) -> R) -> R {
+ pub fn scoped_read<'a, R>(
+ &'a self,
+ key: impl Keyable,
+ f: impl FnOnce(L::DataRef<'a>) -> R,
+ ) -> R {
scoped_read(self, key, f)
}
pub fn scoped_try_read<'a, Key: Keyable, R>(
&'a self,
key: Key,
- f: impl Fn(L::DataRef<'a>) -> R,
+ f: impl FnOnce(L::DataRef<'a>) -> R,
) -> Result<R, Key> {
scoped_try_read(self, key, f)
}
diff --git a/src/collection/ref.rs b/src/collection/ref.rs
index e71624d..d180ab0 100644..100755
--- a/src/collection/ref.rs
+++ b/src/collection/ref.rs
@@ -240,14 +240,18 @@ impl<'a, L: Lockable> RefLockCollection<'a, L> {
Some(Self { data, locks })
}
- pub fn scoped_lock<'s, R>(&'s self, key: impl Keyable, f: impl Fn(L::DataMut<'s>) -> R) -> R {
+ pub fn scoped_lock<'s, R>(
+ &'s self,
+ key: impl Keyable,
+ f: impl FnOnce(L::DataMut<'s>) -> R,
+ ) -> R {
scoped_write(self, key, f)
}
pub fn scoped_try_lock<'s, Key: Keyable, R>(
&'s self,
key: Key,
- f: impl Fn(L::DataMut<'s>) -> R,
+ f: impl FnOnce(L::DataMut<'s>) -> R,
) -> Result<R, Key> {
scoped_try_write(self, key, f)
}
@@ -355,14 +359,18 @@ impl<'a, L: Lockable> RefLockCollection<'a, L> {
}
impl<L: Sharable> RefLockCollection<'_, L> {
- pub fn scoped_read<'a, R>(&'a self, key: impl Keyable, f: impl Fn(L::DataRef<'a>) -> R) -> R {
+ pub fn scoped_read<'a, R>(
+ &'a self,
+ key: impl Keyable,
+ f: impl FnOnce(L::DataRef<'a>) -> R,
+ ) -> R {
scoped_read(self, key, f)
}
pub fn scoped_try_read<'a, Key: Keyable, R>(
&'a self,
key: Key,
- f: impl Fn(L::DataRef<'a>) -> R,
+ f: impl FnOnce(L::DataRef<'a>) -> R,
) -> Result<R, Key> {
scoped_try_read(self, key, f)
}
diff --git a/src/collection/retry.rs b/src/collection/retry.rs
index 15f626d..e127c20 100644..100755
--- a/src/collection/retry.rs
+++ b/src/collection/retry.rs
@@ -534,14 +534,18 @@ impl<L: Lockable> RetryingLockCollection<L> {
(!contains_duplicates(&data)).then_some(unsafe { Self::new_unchecked(data) })
}
- pub fn scoped_lock<'a, R>(&'a self, key: impl Keyable, f: impl Fn(L::DataMut<'a>) -> R) -> R {
+ pub fn scoped_lock<'a, R>(
+ &'a self,
+ key: impl Keyable,
+ f: impl FnOnce(L::DataMut<'a>) -> R,
+ ) -> R {
scoped_write(self, key, f)
}
pub fn scoped_try_lock<'a, Key: Keyable, R>(
&'a self,
key: Key,
- f: impl Fn(L::DataMut<'a>) -> R,
+ f: impl FnOnce(L::DataMut<'a>) -> R,
) -> Result<R, Key> {
scoped_try_write(self, key, f)
}
@@ -649,14 +653,18 @@ impl<L: Lockable> RetryingLockCollection<L> {
}
impl<L: Sharable> RetryingLockCollection<L> {
- pub fn scoped_read<'a, R>(&'a self, key: impl Keyable, f: impl Fn(L::DataRef<'a>) -> R) -> R {
+ pub fn scoped_read<'a, R>(
+ &'a self,
+ key: impl Keyable,
+ f: impl FnOnce(L::DataRef<'a>) -> R,
+ ) -> R {
scoped_read(self, key, f)
}
pub fn scoped_try_read<'a, Key: Keyable, R>(
&'a self,
key: Key,
- f: impl Fn(L::DataRef<'a>) -> R,
+ f: impl FnOnce(L::DataRef<'a>) -> R,
) -> Result<R, Key> {
scoped_try_read(self, key, f)
}
diff --git a/src/collection/utils.rs b/src/collection/utils.rs
index 71a023e..71a023e 100644..100755
--- a/src/collection/utils.rs
+++ b/src/collection/utils.rs
diff --git a/src/handle_unwind.rs b/src/handle_unwind.rs
index 42b6fc5..42b6fc5 100644..100755
--- a/src/handle_unwind.rs
+++ b/src/handle_unwind.rs
diff --git a/src/key.rs b/src/key.rs
index b29245e..b29245e 100644..100755
--- a/src/key.rs
+++ b/src/key.rs
diff --git a/src/lib.rs b/src/lib.rs
index 2139d6b..2139d6b 100644..100755
--- a/src/lib.rs
+++ b/src/lib.rs
diff --git a/src/lockable.rs b/src/lockable.rs
index 43ff5c3..43ff5c3 100644..100755
--- a/src/lockable.rs
+++ b/src/lockable.rs
diff --git a/src/mutex.rs b/src/mutex.rs
index 0d6aa73..0d6aa73 100644..100755
--- a/src/mutex.rs
+++ b/src/mutex.rs
diff --git a/src/mutex/guard.rs b/src/mutex/guard.rs
index d88fded..d88fded 100644..100755
--- a/src/mutex/guard.rs
+++ b/src/mutex/guard.rs
diff --git a/src/mutex/mutex.rs b/src/mutex/mutex.rs
index 05b10db..475c3ae 100644..100755
--- a/src/mutex/mutex.rs
+++ b/src/mutex/mutex.rs
@@ -229,6 +229,33 @@ impl<T: ?Sized, R> Mutex<T, R> {
}
impl<T: ?Sized, R: RawMutex> Mutex<T, R> {
+ /// Acquires a lock on the mutex, blocking until it is safe to do so, and then
+ /// unlocks the mutex after the provided function returns.
+ ///
+ /// This function is useful to ensure that a Mutex is never accidentally
+ /// locked forever by leaking the `MutexGuard`. Even if the function panics,
+ /// this function will gracefully notice the panic, and unlock the mutex.
+ ///
+ /// # Panics
+ ///
+ /// This function will panic if the provided function also panics. However,
+ /// mutex will be safely unlocked in this case, allowing the mutex to be
+ /// locked again later.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use happylock::{ThreadKey, Mutex};
+ ///
+ /// let mut key = ThreadKey::get().unwrap();
+ /// let mutex = Mutex::new(42);
+ ///
+ /// let x = mutex.scoped_lock(&mut key, |number| {
+ /// *number += 5;
+ /// *number
+ /// });
+ /// assert_eq!(x, 47);
+ /// ```
pub fn scoped_lock<'a, Ret>(
&'a self,
key: impl Keyable,
@@ -254,6 +281,41 @@ impl<T: ?Sized, R: RawMutex> Mutex<T, R> {
}
}
+ /// Attempts to acquire the `Mutex` without blocking, and then unlocks it once
+ /// the provided function returns.
+ ///
+ /// This function implements a non-blocking variant of [`scoped_lock`]. Unlike
+ /// `scoped_lock`, if the mutex is not already unlocked, then the provided
+ /// function will not run, and the given [`Keyable`] is returned.
+ ///
+ /// # Errors
+ ///
+ /// If the mutex is already locked, then the provided function will not run.
+ /// `Err` is returned with the given key.
+ ///
+ /// # Panics
+ ///
+ /// If the provided function panics, then the panic will be bubbled up and
+ /// rethrown. The mutex will also be gracefully unlocked, allowing the mutex
+ /// to be locked again.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use happylock::{Mutex, ThreadKey};
+ ///
+ /// let mut key = ThreadKey::get().unwrap();
+ /// let mutex = Mutex::new(42);
+ ///
+ /// let result = mutex.scoped_try_lock(&mut key, |num| {
+ /// *num
+ /// });
+ ///
+ /// match result {
+ /// Ok(val) => println!("The number is {val}"),
+ /// Err(_) => unreachable!(),
+ /// }
+ /// ```
pub fn scoped_try_lock<'a, Key: Keyable, Ret>(
&'a self,
key: Key,
diff --git a/src/poisonable.rs b/src/poisonable.rs
index 74444db..74444db 100644..100755
--- a/src/poisonable.rs
+++ b/src/poisonable.rs
diff --git a/src/poisonable/error.rs b/src/poisonable/error.rs
index eed454b..eed454b 100644..100755
--- a/src/poisonable/error.rs
+++ b/src/poisonable/error.rs
diff --git a/src/poisonable/flag.rs b/src/poisonable/flag.rs
index 9186bbc..9186bbc 100644..100755
--- a/src/poisonable/flag.rs
+++ b/src/poisonable/flag.rs
diff --git a/src/poisonable/guard.rs b/src/poisonable/guard.rs
index b887e2d..b887e2d 100644..100755
--- a/src/poisonable/guard.rs
+++ b/src/poisonable/guard.rs
diff --git a/src/poisonable/poisonable.rs b/src/poisonable/poisonable.rs
index c098041..c8225df 100644..100755
--- a/src/poisonable/poisonable.rs
+++ b/src/poisonable/poisonable.rs
@@ -300,10 +300,40 @@ impl<L: Lockable> Poisonable<L> {
}
impl<L: Lockable + RawLock> Poisonable<L> {
+ /// Acquires an exclusive lock, blocking until it is safe to do so, and then
+ /// unlocks after the provided function returns.
+ ///
+ /// This function is useful to ensure that a `Poisonable` is never
+ /// accidentally locked forever by leaking the guard. Even if the function
+ /// panics, this function will gracefully notice the panic, poison the lock,
+ /// and unlock.
+ ///
+ /// If the lock is poisoned, then an error will be passed into the provided
+ /// function.
+ ///
+ /// # Panics
+ ///
+ /// This function will panic if the provided function also panics. However,
+ /// `Poisonable` will be safely poisoned and any subsequent calls will pass
+ /// `Err` into the given function.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use happylock::{Poisonable, ThreadKey, RwLock};
+ ///
+ /// let mut key = ThreadKey::get().unwrap();
+ /// let lock = Poisonable::new(RwLock::new(42));
+ ///
+ /// let x = lock.scoped_lock(&mut key, |number| {
+ /// *number.unwrap()
+ /// });
+ /// assert_eq!(x, 42);
+ /// ```
pub fn scoped_lock<'a, R>(
&'a self,
key: impl Keyable,
- f: impl Fn(<Self as Lockable>::DataMut<'a>) -> R,
+ f: impl FnOnce(<Self as Lockable>::DataMut<'a>) -> R,
) -> R {
unsafe {
// safety: we have the thread key
@@ -327,10 +357,50 @@ impl<L: Lockable + RawLock> Poisonable<L> {
}
}
+ /// Attempts to acquire an exclusive lock to the `Poisonable` without
+ /// blocking, and then unlocks it once the provided function returns.
+ ///
+ /// This function implements a non-blocking variant of [`scoped_lock`].
+ /// Unlike `scoped_lock`, if the `Poisonable` is not already unlocked, then
+ /// the provided function will not run, and the given [`Keyable`] is returned.
+ /// This method does not provide any guarantees with respect to the ordering
+ /// of whether contentious readers or writers will acquire the lock first.
+ ///
+ /// If the lock is poisoned, then an error will be passed into the provided
+ /// function.
+ ///
+ /// # Errors
+ ///
+ /// If the `Poisonable` is already locked, then the provided function will not
+ /// run. `Err` is returned with the given key.
+ ///
+ /// # Panics
+ ///
+ /// If the provided function panics, then the panic will be bubbled up and
+ /// rethrown. The `Poisonable` will also be gracefully unlocked, allowing the
+ /// `Poisonable` to be locked again.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use happylock::{Poisonable, RwLock, ThreadKey};
+ ///
+ /// let mut key = ThreadKey::get().unwrap();
+ /// let lock = Poisonable::new(RwLock::new(42));
+ ///
+ /// let result = lock.scoped_try_lock(&mut key, |num| {
+ /// *num.unwrap()
+ /// });
+ ///
+ /// match result {
+ /// Ok(val) => println!("The number is {val}"),
+ /// Err(_) => unreachable!(),
+ /// }
+ /// ```
pub fn scoped_try_lock<'a, Key: Keyable, R>(
&'a self,
key: Key,
- f: impl Fn(<Self as Lockable>::DataMut<'a>) -> R,
+ f: impl FnOnce(<Self as Lockable>::DataMut<'a>) -> R,
) -> Result<R, Key> {
unsafe {
// safety: we have the thread key
@@ -487,10 +557,41 @@ impl<L: Sharable + RawLock> Poisonable<L> {
Ok(guard)
}
+ /// Acquires a shared lock, blocking until it is safe to do so, and then
+ /// unlocks after the provided function returns.
+ ///
+ /// This function is useful to ensure that a `Poisonable` is never
+ /// accidentally locked forever by leaking the `ReadGuard`. Even if the
+ /// function panics, this function will gracefully notice the panic, and
+ /// unlock. This function provides no guarantees with respect to the ordering
+ /// of whether contentious readers or writers will acquire the lock first.
+ ///
+ /// If the lock is poisoned, then an error will be passed into the provided
+ /// into the provided function.
+ ///
+ /// # Panics
+ ///
+ /// This function will panic if the provided function also panics. However,
+ /// `Poisonable` will be safely unlocked in this case, allowing the
+ /// `Poisonable` to be locked again later.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use happylock::{Poisonable, ThreadKey, RwLock};
+ ///
+ /// let mut key = ThreadKey::get().unwrap();
+ /// let lock = Poisonable::new(RwLock::new(42));
+ ///
+ /// let x = lock.scoped_read(&mut key, |number| {
+ /// *number.unwrap()
+ /// });
+ /// assert_eq!(x, 42);
+ /// ```
pub fn scoped_read<'a, R>(
&'a self,
key: impl Keyable,
- f: impl Fn(<Self as Sharable>::DataRef<'a>) -> R,
+ f: impl FnOnce(<Self as Sharable>::DataRef<'a>) -> R,
) -> R {
unsafe {
// safety: we have the thread key
@@ -514,10 +615,50 @@ impl<L: Sharable + RawLock> Poisonable<L> {
}
}
+ /// Attempts to acquire a shared lock to the `Poisonable` without blocking,
+ /// and then unlocks it once the provided function returns.
+ ///
+ /// This function implements a non-blocking variant of [`scoped_read`].
+ /// Unlike `scoped_read`, if the `Poisonable` is exclusively locked, then the
+ /// provided function will not run, and the given [`Keyable`] is returned.
+ /// This method provides no guarantees with respect to the ordering of whether
+ /// contentious readers of writers will acquire the lock first.
+ ///
+ /// If the lock is poisoned, then an error will be passed into the provided
+ /// function.
+ ///
+ /// # Errors
+ ///
+ /// If the `Poisonable` is already exclusively locked, then the provided
+ /// function will not run. `Err` is returned with the given key.
+ ///
+ /// # Panics
+ ///
+ /// If the provided function panics, then the panic will be bubbled up and
+ /// rethrown. The `Poisonable` will also be gracefully unlocked, allowing the
+ /// `Poisonable` to be locked again.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use happylock::{Poisonable, RwLock, ThreadKey};
+ ///
+ /// let mut key = ThreadKey::get().unwrap();
+ /// let lock = Poisonable::new(RwLock::new(42));
+ ///
+ /// let result = lock.scoped_try_read(&mut key, |num| {
+ /// *num.unwrap()
+ /// });
+ ///
+ /// match result {
+ /// Ok(val) => println!("The number is {val}"),
+ /// Err(_) => unreachable!(),
+ /// }
+ /// ```
pub fn scoped_try_read<'a, Key: Keyable, R>(
&'a self,
key: Key,
- f: impl Fn(<Self as Sharable>::DataRef<'a>) -> R,
+ f: impl FnOnce(<Self as Sharable>::DataRef<'a>) -> R,
) -> Result<R, Key> {
unsafe {
// safety: we have the thread key
diff --git a/src/rwlock.rs b/src/rwlock.rs
index fca132d..fca132d 100644..100755
--- a/src/rwlock.rs
+++ b/src/rwlock.rs
diff --git a/src/rwlock/read_guard.rs b/src/rwlock/read_guard.rs
index 5b26c06..5b26c06 100644..100755
--- a/src/rwlock/read_guard.rs
+++ b/src/rwlock/read_guard.rs
diff --git a/src/rwlock/rwlock.rs b/src/rwlock/rwlock.rs
index 0dce710..98ac466 100644..100755
--- a/src/rwlock/rwlock.rs
+++ b/src/rwlock/rwlock.rs
@@ -250,7 +250,35 @@ impl<T: ?Sized, R> RwLock<T, R> {
}
impl<T: ?Sized, R: RawRwLock> RwLock<T, R> {
- pub fn scoped_read<'a, Ret>(&'a self, key: impl Keyable, f: impl Fn(&'a T) -> Ret) -> Ret {
+ /// Acquires a shared lock, blocking until it is safe to do so, and then
+ /// unlocks after the provided function returns.
+ ///
+ /// This function is useful to ensure that a `RwLock` is never accidentally
+ /// locked forever by leaking the `ReadGuard`. Even if the function panics,
+ /// this function will gracefully notice the panic, and unlock. This function
+ /// provides no guarantees with respect to the ordering of whether contentious
+ /// readers or writers will acquire the lock first.
+ ///
+ /// # Panics
+ ///
+ /// This function will panic if the provided function also panics. However,
+ /// `RwLock` will be safely unlocked in this case, allowing the `RwLock` to be
+ /// locked again later.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use happylock::{ThreadKey, RwLock};
+ ///
+ /// let mut key = ThreadKey::get().unwrap();
+ /// let lock = RwLock::new(42);
+ ///
+ /// let x = lock.scoped_read(&mut key, |number| {
+ /// *number
+ /// });
+ /// assert_eq!(x, 42);
+ /// ```
+ pub fn scoped_read<'a, Ret>(&'a self, key: impl Keyable, f: impl FnOnce(&'a T) -> Ret) -> Ret {
unsafe {
// safety: we have the key
self.raw_read();
@@ -271,10 +299,47 @@ impl<T: ?Sized, R: RawRwLock> RwLock<T, R> {
}
}
+ /// Attempts to acquire a shared lock to the `RwLock` without blocking,
+ /// and then unlocks it once the provided function returns.
+ ///
+ /// This function implements a non-blocking variant of [`scoped_read`].
+ /// Unlike `scoped_read`, if the `RwLock` is exclusively locked, then the
+ /// provided function will not run, and the given [`Keyable`] is returned.
+ /// This method provides no guarantees with respect to the ordering of whether
+ /// contentious readers of writers will acquire the lock first.
+ ///
+ /// # Errors
+ ///
+ /// If the `RwLock` is already exclusively locked, then the provided function
+ /// will not run. `Err` is returned with the given key.
+ ///
+ /// # Panics
+ ///
+ /// If the provided function panics, then the panic will be bubbled up and
+ /// rethrown. The `RwLock` will also be gracefully unlocked, allowing the
+ /// `RwLock` to be locked again.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use happylock::{RwLock, ThreadKey};
+ ///
+ /// let mut key = ThreadKey::get().unwrap();
+ /// let lock = RwLock::new(42);
+ ///
+ /// let result = lock.scoped_try_read(&mut key, |num| {
+ /// *num
+ /// });
+ ///
+ /// match result {
+ /// Ok(val) => println!("The number is {val}"),
+ /// Err(_) => unreachable!(),
+ /// }
+ /// ```
pub fn scoped_try_read<'a, Key: Keyable, Ret>(
&'a self,
key: Key,
- f: impl Fn(&'a T) -> Ret,
+ f: impl FnOnce(&'a T) -> Ret,
) -> Result<Ret, Key> {
unsafe {
// safety: we have the key
@@ -298,7 +363,40 @@ impl<T: ?Sized, R: RawRwLock> RwLock<T, R> {
}
}
- pub fn scoped_write<'a, Ret>(&'a self, key: impl Keyable, f: impl Fn(&'a mut T) -> Ret) -> Ret {
+ /// Acquires an exclusive lock, blocking until it is safe to do so, and then
+ /// unlocks after the provided function returns.
+ ///
+ /// This function is useful to ensure that a `RwLock` is never accidentally
+ /// locked forever by leaking the `WriteGuard`. Even if the function panics,
+ /// this function will gracefully notice the panic, and unlock. This method
+ /// does not provide any guarantees with respect to the ordering of whether
+ /// contentious readers or writers will acquire the lock first.
+ ///
+ /// # Panics
+ ///
+ /// This function will panic if the provided function also panics. However,
+ /// `RwLock` will be safely unlocked in this case, allowing the `RwLock` to be
+ /// locked again later.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use happylock::{ThreadKey, RwLock};
+ ///
+ /// let mut key = ThreadKey::get().unwrap();
+ /// let lock = RwLock::new(42);
+ ///
+ /// let x = lock.scoped_write(&mut key, |number| {
+ /// *number += 5;
+ /// *number
+ /// });
+ /// assert_eq!(x, 47);
+ /// ```
+ pub fn scoped_write<'a, Ret>(
+ &'a self,
+ key: impl Keyable,
+ f: impl FnOnce(&'a mut T) -> Ret,
+ ) -> Ret {
unsafe {
// safety: we have the key
self.raw_write();
@@ -319,10 +417,47 @@ impl<T: ?Sized, R: RawRwLock> RwLock<T, R> {
}
}
+ /// Attempts to acquire an exclusive lock to the `RwLock` without blocking,
+ /// and then unlocks it once the provided function returns.
+ ///
+ /// This function implements a non-blocking variant of [`scoped_write`].
+ /// Unlike `scoped_write`, if the `RwLock` is not already unlocked, then the
+ /// provided function will not run, and the given [`Keyable`] is returned.
+ /// This method 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` is already locked, then the provided function will not
+ /// run. `Err` is returned with the given key.
+ ///
+ /// # Panics
+ ///
+ /// If the provided function panics, then the panic will be bubbled up and
+ /// rethrown. The `RwLock` will also be gracefully unlocked, allowing the
+ /// `RwLock` to be locked again.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use happylock::{RwLock, ThreadKey};
+ ///
+ /// let mut key = ThreadKey::get().unwrap();
+ /// let lock = RwLock::new(42);
+ ///
+ /// let result = lock.scoped_try_write(&mut key, |num| {
+ /// *num
+ /// });
+ ///
+ /// match result {
+ /// Ok(val) => println!("The number is {val}"),
+ /// Err(_) => unreachable!(),
+ /// }
+ /// ```
pub fn scoped_try_write<'a, Key: Keyable, Ret>(
&'a self,
key: Key,
- f: impl Fn(&'a mut T) -> Ret,
+ f: impl FnOnce(&'a mut T) -> Ret,
) -> Result<Ret, Key> {
unsafe {
// safety: we have the key
@@ -518,7 +653,7 @@ impl<T: ?Sized, R: RawRwLock> RwLock<T, R> {
/// let key = match lock.try_write(key) {
/// Ok(mut n) => {
/// assert_eq!(*n, 1);
- /// *n = 2;
+ /// *n = 2;
/// RwLock::unlock_write(n)
/// }
/// Err(_) => unreachable!(),
diff --git a/src/rwlock/write_guard.rs b/src/rwlock/write_guard.rs
index c7676b5..c7676b5 100644..100755
--- a/src/rwlock/write_guard.rs
+++ b/src/rwlock/write_guard.rs
diff --git a/src/thread.rs b/src/thread.rs
index 6e9c270..6e9c270 100644..100755
--- a/src/thread.rs
+++ b/src/thread.rs
diff --git a/src/thread/scope.rst b/src/thread/scope.rst
index 09319cb..09319cb 100644..100755
--- a/src/thread/scope.rst
+++ b/src/thread/scope.rst
diff --git a/tarpaulin-report.html b/tarpaulin-report.html
index bac34c6..bac34c6 100644..100755
--- a/tarpaulin-report.html
+++ b/tarpaulin-report.html
diff --git a/tests/evil_mutex.rs b/tests/evil_mutex.rs
index e10acc8..e10acc8 100644..100755
--- a/tests/evil_mutex.rs
+++ b/tests/evil_mutex.rs
diff --git a/tests/evil_rwlock.rs b/tests/evil_rwlock.rs
index 4be86a1..4be86a1 100644..100755
--- a/tests/evil_rwlock.rs
+++ b/tests/evil_rwlock.rs
diff --git a/tests/evil_try_mutex.rs b/tests/evil_try_mutex.rs
index 5c31a91..5c31a91 100644..100755
--- a/tests/evil_try_mutex.rs
+++ b/tests/evil_try_mutex.rs
diff --git a/tests/evil_try_rwlock.rs b/tests/evil_try_rwlock.rs
index b00a666..b00a666 100644..100755
--- a/tests/evil_try_rwlock.rs
+++ b/tests/evil_try_rwlock.rs
diff --git a/tests/evil_unlock_mutex.rs b/tests/evil_unlock_mutex.rs
index ee12abc..ee12abc 100644..100755
--- a/tests/evil_unlock_mutex.rs
+++ b/tests/evil_unlock_mutex.rs
diff --git a/tests/evil_unlock_rwlock.rs b/tests/evil_unlock_rwlock.rs
index 58402c9..58402c9 100644..100755
--- a/tests/evil_unlock_rwlock.rs
+++ b/tests/evil_unlock_rwlock.rs
diff --git a/tests/forget.rs b/tests/forget.rs
index 755bdb1..755bdb1 100644..100755
--- a/tests/forget.rs
+++ b/tests/forget.rs
diff --git a/tests/retry.rs b/tests/retry.rs
index 64e0918..64e0918 100644..100755
--- a/tests/retry.rs
+++ b/tests/retry.rs
diff --git a/tests/retry_rw.rs b/tests/retry_rw.rs
index 0a946b0..0a946b0 100644..100755
--- a/tests/retry_rw.rs
+++ b/tests/retry_rw.rs