diff options
Diffstat (limited to 'src/poisonable/poisonable.rs')
| -rwxr-xr-x[-rw-r--r--] | src/poisonable/poisonable.rs | 149 |
1 files changed, 145 insertions, 4 deletions
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 |
