summaryrefslogtreecommitdiff
path: root/src
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 /src
parent17dab88a7b4bc86cf156a1e0ac1bac19e6f9f5c6 (diff)
Stuff
Diffstat (limited to 'src')
-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
25 files changed, 395 insertions, 25 deletions
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