diff options
| -rwxr-xr-x[-rw-r--r--] | .gitignore | 0 | ||||
| -rwxr-xr-x[-rw-r--r--] | .vscode/launch.json | 0 | ||||
| -rwxr-xr-x[-rw-r--r--] | Cargo.toml | 0 | ||||
| -rwxr-xr-x[-rw-r--r--] | LICENSE | 0 | ||||
| -rwxr-xr-x[-rw-r--r--] | README.md | 0 | ||||
| -rwxr-xr-x[-rw-r--r--] | examples/basic.rs | 0 | ||||
| -rwxr-xr-x[-rw-r--r--] | examples/dining_philosophers.rs | 0 | ||||
| -rwxr-xr-x[-rw-r--r--] | examples/dining_philosophers_retry.rs | 0 | ||||
| -rwxr-xr-x[-rw-r--r--] | examples/double_mutex.rs | 0 | ||||
| -rwxr-xr-x[-rw-r--r--] | examples/fibonacci.rs | 0 | ||||
| -rwxr-xr-x[-rw-r--r--] | examples/list.rs | 0 | ||||
| -rwxr-xr-x[-rw-r--r--] | happylock.md | 0 | ||||
| -rwxr-xr-x[-rw-r--r--] | package-lock.json | 0 | ||||
| -rwxr-xr-x[-rw-r--r--] | package.json | 0 | ||||
| -rwxr-xr-x[-rw-r--r--] | rustfmt.toml | 0 | ||||
| -rwxr-xr-x[-rw-r--r--] | src/collection.rs | 0 | ||||
| -rwxr-xr-x[-rw-r--r--] | src/collection/boxed.rs | 16 | ||||
| -rwxr-xr-x[-rw-r--r--] | src/collection/guard.rs | 0 | ||||
| -rwxr-xr-x[-rw-r--r--] | src/collection/owned.rs | 16 | ||||
| -rwxr-xr-x[-rw-r--r--] | src/collection/ref.rs | 16 | ||||
| -rwxr-xr-x[-rw-r--r--] | src/collection/retry.rs | 16 | ||||
| -rwxr-xr-x[-rw-r--r--] | src/collection/utils.rs | 0 | ||||
| -rwxr-xr-x[-rw-r--r--] | src/handle_unwind.rs | 0 | ||||
| -rwxr-xr-x[-rw-r--r--] | src/key.rs | 0 | ||||
| -rwxr-xr-x[-rw-r--r--] | src/lib.rs | 0 | ||||
| -rwxr-xr-x[-rw-r--r--] | src/lockable.rs | 0 | ||||
| -rwxr-xr-x[-rw-r--r--] | src/mutex.rs | 0 | ||||
| -rwxr-xr-x[-rw-r--r--] | src/mutex/guard.rs | 0 | ||||
| -rwxr-xr-x[-rw-r--r--] | src/mutex/mutex.rs | 62 | ||||
| -rwxr-xr-x[-rw-r--r--] | src/poisonable.rs | 0 | ||||
| -rwxr-xr-x[-rw-r--r--] | src/poisonable/error.rs | 0 | ||||
| -rwxr-xr-x[-rw-r--r--] | src/poisonable/flag.rs | 0 | ||||
| -rwxr-xr-x[-rw-r--r--] | src/poisonable/guard.rs | 0 | ||||
| -rwxr-xr-x[-rw-r--r--] | src/poisonable/poisonable.rs | 149 | ||||
| -rwxr-xr-x[-rw-r--r--] | src/rwlock.rs | 0 | ||||
| -rwxr-xr-x[-rw-r--r--] | src/rwlock/read_guard.rs | 0 | ||||
| -rwxr-xr-x[-rw-r--r--] | src/rwlock/rwlock.rs | 145 | ||||
| -rwxr-xr-x[-rw-r--r--] | src/rwlock/write_guard.rs | 0 | ||||
| -rwxr-xr-x[-rw-r--r--] | src/thread.rs | 0 | ||||
| -rwxr-xr-x[-rw-r--r--] | src/thread/scope.rst | 0 | ||||
| -rwxr-xr-x[-rw-r--r--] | tarpaulin-report.html | 0 | ||||
| -rwxr-xr-x[-rw-r--r--] | tests/evil_mutex.rs | 0 | ||||
| -rwxr-xr-x[-rw-r--r--] | tests/evil_rwlock.rs | 0 | ||||
| -rwxr-xr-x[-rw-r--r--] | tests/evil_try_mutex.rs | 0 | ||||
| -rwxr-xr-x[-rw-r--r--] | tests/evil_try_rwlock.rs | 0 | ||||
| -rwxr-xr-x[-rw-r--r--] | tests/evil_unlock_mutex.rs | 0 | ||||
| -rwxr-xr-x[-rw-r--r--] | tests/evil_unlock_rwlock.rs | 0 | ||||
| -rwxr-xr-x[-rw-r--r--] | tests/forget.rs | 0 | ||||
| -rwxr-xr-x[-rw-r--r--] | tests/retry.rs | 0 | ||||
| -rwxr-xr-x[-rw-r--r--] | tests/retry_rw.rs | 0 |
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/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 |
