diff options
| -rw-r--r-- | src/collection.rs | 2 | ||||
| -rw-r--r-- | src/collection/boxed.rs | 186 | ||||
| -rw-r--r-- | src/collection/owned.rs | 283 | ||||
| -rw-r--r-- | src/collection/ref.rs | 261 | ||||
| -rw-r--r-- | src/collection/retry.rs | 403 | ||||
| -rw-r--r-- | src/collection/utils.rs | 113 | ||||
| -rw-r--r-- | src/lockable.rs | 127 | ||||
| -rw-r--r-- | src/mutex.rs | 25 | ||||
| -rw-r--r-- | src/mutex/guard.rs | 6 | ||||
| -rw-r--r-- | src/mutex/mutex.rs | 85 | ||||
| -rw-r--r-- | src/poisonable.rs | 164 | ||||
| -rw-r--r-- | src/poisonable/poisonable.rs | 57 | ||||
| -rw-r--r-- | src/rwlock.rs | 323 | ||||
| -rw-r--r-- | src/rwlock/read_guard.rs | 4 | ||||
| -rw-r--r-- | src/rwlock/read_lock.rs | 54 | ||||
| -rw-r--r-- | src/rwlock/rwlock.rs | 115 | ||||
| -rw-r--r-- | src/rwlock/write_guard.rs | 6 | ||||
| -rw-r--r-- | src/rwlock/write_lock.rs | 52 | ||||
| -rw-r--r-- | src/thread.rs | 19 | ||||
| -rw-r--r-- | src/thread/scope.rst | 47 | ||||
| -rw-r--r-- | tarpaulin-report.html | 4 | ||||
| -rw-r--r-- | tests/evil_rwlock.rs | 18 | ||||
| -rw-r--r-- | tests/retry_rw.rs | 10 |
23 files changed, 1828 insertions, 536 deletions
diff --git a/src/collection.rs b/src/collection.rs index e50cc30..f8c31d7 100644 --- a/src/collection.rs +++ b/src/collection.rs @@ -7,7 +7,7 @@ mod guard; mod owned; mod r#ref; mod retry; -mod utils; +pub(crate) mod utils; /// Locks a collection of locks, which cannot be shared immutably. /// diff --git a/src/collection/boxed.rs b/src/collection/boxed.rs index 364ec97..1891119 100644 --- a/src/collection/boxed.rs +++ b/src/collection/boxed.rs @@ -4,7 +4,9 @@ use std::fmt::Debug; use crate::lockable::{Lockable, LockableIntoInner, OwnedLockable, RawLock, Sharable}; use crate::{Keyable, ThreadKey}; -use super::utils::ordered_contains_duplicates; +use super::utils::{ + ordered_contains_duplicates, scoped_read, scoped_try_read, scoped_try_write, scoped_write, +}; use super::{utils, BoxedLockCollection, LockGuard}; unsafe impl<L: Lockable> RawLock for BoxedLockCollection<L> { @@ -16,18 +18,18 @@ unsafe impl<L: Lockable> RawLock for BoxedLockCollection<L> { } } - unsafe fn raw_lock(&self) { - utils::ordered_lock(self.locks()) + unsafe fn raw_write(&self) { + utils::ordered_write(self.locks()) } - unsafe fn raw_try_lock(&self) -> bool { + unsafe fn raw_try_write(&self) -> bool { println!("{}", self.locks().len()); - utils::ordered_try_lock(self.locks()) + utils::ordered_try_write(self.locks()) } - unsafe fn raw_unlock(&self) { + unsafe fn raw_unlock_write(&self) { for lock in self.locks() { - lock.raw_unlock(); + lock.raw_unlock_write(); } } @@ -58,7 +60,7 @@ unsafe impl<L: Lockable> Lockable for BoxedLockCollection<L> { Self: 'a; fn get_ptrs<'a>(&'a self, ptrs: &mut Vec<&'a dyn RawLock>) { - ptrs.extend(self.locks()) + ptrs.push(self); } unsafe fn guard(&self) -> Self::Guard<'_> { @@ -156,7 +158,7 @@ impl<L> Drop for BoxedLockCollection<L> { } } -impl<T, L: AsRef<T>> AsRef<T> for BoxedLockCollection<L> { +impl<T: ?Sized, L: AsRef<T>> AsRef<T> for BoxedLockCollection<L> { fn as_ref(&self) -> &T { self.child().as_ref() } @@ -364,44 +366,16 @@ impl<L: Lockable> BoxedLockCollection<L> { } } - pub fn scoped_lock<R>(&self, key: impl Keyable, f: impl Fn(L::DataMut<'_>) -> R) -> R { - unsafe { - // safety: we have the thread key - self.raw_lock(); - - // safety: the data was just locked - let r = f(self.data_mut()); - - // safety: the collection is still locked - self.raw_unlock(); - - drop(key); // ensure the key stays alive long enough - - r - } + pub fn scoped_lock<'a, R>(&'a self, key: impl Keyable, f: impl Fn(L::DataMut<'a>) -> R) -> R { + scoped_write(self, key, f) } - pub fn scoped_try_lock<Key: Keyable, R>( - &self, + pub fn scoped_try_lock<'a, Key: Keyable, R>( + &'a self, key: Key, - f: impl Fn(L::DataMut<'_>) -> R, + f: impl Fn(L::DataMut<'a>) -> R, ) -> Result<R, Key> { - unsafe { - // safety: we have the thread key - if !self.raw_try_lock() { - return Err(key); - } - - // safety: we just locked the collection - let r = f(self.data_mut()); - - // safety: the collection is still locked - self.raw_unlock(); - - drop(key); // ensures the key stays valid long enough - - Ok(r) - } + scoped_try_write(self, key, f) } /// Locks the collection @@ -427,7 +401,7 @@ impl<L: Lockable> BoxedLockCollection<L> { pub fn lock(&self, key: ThreadKey) -> LockGuard<L::Guard<'_>> { unsafe { // safety: we have the thread key - self.raw_lock(); + self.raw_write(); LockGuard { // safety: we've already acquired the lock @@ -468,7 +442,7 @@ impl<L: Lockable> BoxedLockCollection<L> { /// ``` pub fn try_lock(&self, key: ThreadKey) -> Result<LockGuard<L::Guard<'_>>, ThreadKey> { let guard = unsafe { - if !self.raw_try_lock() { + if !self.raw_try_write() { return Err(key); } @@ -503,44 +477,16 @@ impl<L: Lockable> BoxedLockCollection<L> { } impl<L: Sharable> BoxedLockCollection<L> { - pub fn scoped_read<R>(&self, key: impl Keyable, f: impl Fn(L::DataRef<'_>) -> R) -> R { - unsafe { - // safety: we have the thread key - self.raw_read(); - - // safety: the data was just locked - let r = f(self.data_ref()); - - // safety: the collection is still locked - self.raw_unlock_read(); - - drop(key); // ensure the key stays alive long enough - - r - } + pub fn scoped_read<'a, R>(&'a self, key: impl Keyable, f: impl Fn(L::DataRef<'a>) -> R) -> R { + scoped_read(self, key, f) } - pub fn scoped_try_read<Key: Keyable, R>( - &self, + pub fn scoped_try_read<'a, Key: Keyable, R>( + &'a self, key: Key, - f: impl Fn(L::DataRef<'_>) -> R, + f: impl Fn(L::DataRef<'a>) -> R, ) -> Result<R, Key> { - unsafe { - // safety: we have the thread key - if !self.raw_try_read() { - return Err(key); - } - - // safety: we just locked the collection - let r = f(self.data_ref()); - - // safety: the collection is still locked - self.raw_unlock_read(); - - drop(key); // ensures the key stays valid long enough - - Ok(r) - } + scoped_try_read(self, key, f) } /// Locks the collection, so that other threads can still read from it @@ -765,6 +711,56 @@ mod tests { } #[test] + fn scoped_read_sees_changes() { + let mut key = ThreadKey::get().unwrap(); + let mutexes = [RwLock::new(24), RwLock::new(42)]; + let collection = BoxedLockCollection::new(mutexes); + collection.scoped_lock(&mut key, |guard| *guard[0] = 128); + + let sum = collection.scoped_read(&mut key, |guard| { + assert_eq!(*guard[0], 128); + assert_eq!(*guard[1], 42); + *guard[0] + *guard[1] + }); + + assert_eq!(sum, 128 + 42); + } + + #[test] + fn scoped_try_lock_can_fail() { + let key = ThreadKey::get().unwrap(); + let collection = BoxedLockCollection::new([Mutex::new(1), Mutex::new(2)]); + let guard = collection.lock(key); + + std::thread::scope(|s| { + s.spawn(|| { + let key = ThreadKey::get().unwrap(); + let r = collection.scoped_try_lock(key, |_| {}); + assert!(r.is_err()); + }); + }); + + drop(guard); + } + + #[test] + fn scoped_try_read_can_fail() { + let key = ThreadKey::get().unwrap(); + let collection = BoxedLockCollection::new([RwLock::new(1), RwLock::new(2)]); + let guard = collection.lock(key); + + std::thread::scope(|s| { + s.spawn(|| { + let key = ThreadKey::get().unwrap(); + let r = collection.scoped_try_read(key, |_| {}); + assert!(r.is_err()); + }); + }); + + drop(guard); + } + + #[test] fn try_lock_works() { let key = ThreadKey::get().unwrap(); let collection = BoxedLockCollection::new([Mutex::new(1), Mutex::new(2)]); @@ -884,15 +880,41 @@ mod tests { #[test] fn works_in_collection() { let key = ThreadKey::get().unwrap(); - let mutex1 = Mutex::new(0); - let mutex2 = Mutex::new(1); + let mutex1 = RwLock::new(0); + let mutex2 = RwLock::new(1); let collection = BoxedLockCollection::try_new(BoxedLockCollection::try_new([&mutex1, &mutex2]).unwrap()) .unwrap(); - let guard = collection.lock(key); + let mut guard = collection.lock(key); + assert!(mutex1.is_locked()); + assert!(mutex2.is_locked()); + assert_eq!(*guard[0], 0); + assert_eq!(*guard[1], 1); + *guard[0] = 2; + let key = BoxedLockCollection::<BoxedLockCollection<[&RwLock<_>; 2]>>::unlock(guard); + + let guard = collection.read(key); assert!(mutex1.is_locked()); assert!(mutex2.is_locked()); + assert_eq!(*guard[0], 2); + assert_eq!(*guard[1], 1); drop(guard); } + + #[test] + fn as_ref_works() { + let mutexes = [Mutex::new(0), Mutex::new(1)]; + let collection = BoxedLockCollection::new_ref(&mutexes); + + assert!(std::ptr::addr_eq(&mutexes, collection.as_ref())) + } + + #[test] + fn child() { + let mutexes = [Mutex::new(0), Mutex::new(1)]; + let collection = BoxedLockCollection::new_ref(&mutexes); + + assert!(std::ptr::addr_eq(&mutexes, *collection.child())) + } } diff --git a/src/collection/owned.rs b/src/collection/owned.rs index b9cf313..68170d1 100644 --- a/src/collection/owned.rs +++ b/src/collection/owned.rs @@ -3,6 +3,7 @@ use crate::lockable::{ }; use crate::{Keyable, ThreadKey}; +use super::utils::{scoped_read, scoped_try_read, scoped_try_write, scoped_write}; use super::{utils, LockGuard, OwnedLockCollection}; unsafe impl<L: Lockable> RawLock for OwnedLockCollection<L> { @@ -15,19 +16,19 @@ unsafe impl<L: Lockable> RawLock for OwnedLockCollection<L> { } } - unsafe fn raw_lock(&self) { - utils::ordered_lock(&utils::get_locks_unsorted(&self.data)) + unsafe fn raw_write(&self) { + utils::ordered_write(&utils::get_locks_unsorted(&self.data)) } - unsafe fn raw_try_lock(&self) -> bool { + unsafe fn raw_try_write(&self) -> bool { let locks = utils::get_locks_unsorted(&self.data); - utils::ordered_try_lock(&locks) + utils::ordered_try_write(&locks) } - unsafe fn raw_unlock(&self) { + unsafe fn raw_unlock_write(&self) { let locks = utils::get_locks_unsorted(&self.data); for lock in locks { - lock.raw_unlock(); + lock.raw_unlock_write(); } } @@ -62,7 +63,7 @@ unsafe impl<L: Lockable> Lockable for OwnedLockCollection<L> { #[mutants::skip] // It's hard to test lkocks in an OwnedLockCollection, because they're owned #[cfg(not(tarpaulin_include))] fn get_ptrs<'a>(&'a self, ptrs: &mut Vec<&'a dyn RawLock>) { - self.data.get_ptrs(ptrs) + ptrs.push(self) } unsafe fn guard(&self) -> Self::Guard<'_> { @@ -146,7 +147,7 @@ impl<E: OwnedLockable + Extend<L>, L: OwnedLockable> Extend<L> for OwnedLockColl // invariant that there is only one way to lock the collection. AsMut is fine, // because the collection can't be locked as long as the reference is valid. -impl<T, L: AsMut<T>> AsMut<T> for OwnedLockCollection<L> { +impl<T: ?Sized, L: AsMut<T>> AsMut<T> for OwnedLockCollection<L> { fn as_mut(&mut self) -> &mut T { self.data.as_mut() } @@ -185,44 +186,16 @@ impl<L: OwnedLockable> OwnedLockCollection<L> { Self { data } } - pub fn scoped_lock<R>(&self, key: impl Keyable, f: impl Fn(L::DataMut<'_>) -> R) -> R { - unsafe { - // safety: we have the thread key - self.raw_lock(); - - // safety: the data was just locked - let r = f(self.data_mut()); - - // safety: the collection is still locked - self.raw_unlock(); - - drop(key); // ensure the key stays alive long enough - - r - } + pub fn scoped_lock<'a, R>(&'a self, key: impl Keyable, f: impl Fn(L::DataMut<'a>) -> R) -> R { + scoped_write(self, key, f) } - pub fn scoped_try_lock<Key: Keyable, R>( - &self, + pub fn scoped_try_lock<'a, Key: Keyable, R>( + &'a self, key: Key, - f: impl Fn(L::DataMut<'_>) -> R, + f: impl Fn(L::DataMut<'a>) -> R, ) -> Result<R, Key> { - unsafe { - // safety: we have the thread key - if !self.raw_try_lock() { - return Err(key); - } - - // safety: we just locked the collection - let r = f(self.data_mut()); - - // safety: the collection is still locked - self.raw_unlock(); - - drop(key); // ensures the key stays valid long enough - - Ok(r) - } + scoped_try_write(self, key, f) } /// Locks the collection @@ -249,7 +222,7 @@ impl<L: OwnedLockable> OwnedLockCollection<L> { let guard = unsafe { // safety: we have the thread key, and these locks happen in a // predetermined order - self.raw_lock(); + self.raw_write(); // safety: we've locked all of this already self.data.guard() @@ -290,7 +263,7 @@ impl<L: OwnedLockable> OwnedLockCollection<L> { /// ``` pub fn try_lock(&self, key: ThreadKey) -> Result<LockGuard<L::Guard<'_>>, ThreadKey> { let guard = unsafe { - if !self.raw_try_lock() { + if !self.raw_try_write() { return Err(key); } @@ -327,44 +300,16 @@ impl<L: OwnedLockable> OwnedLockCollection<L> { } impl<L: Sharable> OwnedLockCollection<L> { - pub fn scoped_read<R>(&self, key: impl Keyable, f: impl Fn(L::DataRef<'_>) -> R) -> R { - unsafe { - // safety: we have the thread key - self.raw_read(); - - // safety: the data was just locked - let r = f(self.data_ref()); - - // safety: the collection is still locked - self.raw_unlock_read(); - - drop(key); // ensure the key stays alive long enough - - r - } + pub fn scoped_read<'a, R>(&'a self, key: impl Keyable, f: impl Fn(L::DataRef<'a>) -> R) -> R { + scoped_read(self, key, f) } - pub fn scoped_try_read<Key: Keyable, R>( - &self, + pub fn scoped_try_read<'a, Key: Keyable, R>( + &'a self, key: Key, - f: impl Fn(L::DataRef<'_>) -> R, + f: impl Fn(L::DataRef<'a>) -> R, ) -> Result<R, Key> { - unsafe { - // safety: we have the thread key - if !self.raw_try_read() { - return Err(key); - } - - // safety: we just locked the collection - let r = f(self.data_ref()); - - // safety: the collection is still locked - self.raw_unlock_read(); - - drop(key); // ensures the key stays valid long enough - - Ok(r) - } + scoped_try_read(self, key, f) } /// Locks the collection, so that other threads can still read from it @@ -554,7 +499,7 @@ impl<L: LockableIntoInner> OwnedLockCollection<L> { #[cfg(test)] mod tests { use super::*; - use crate::{Mutex, ThreadKey}; + use crate::{Mutex, RwLock, ThreadKey}; #[test] fn get_mut_applies_changes() { @@ -604,6 +549,63 @@ mod tests { } #[test] + fn scoped_read_works() { + let mut key = ThreadKey::get().unwrap(); + let collection = OwnedLockCollection::new([RwLock::new(24), RwLock::new(42)]); + let sum = collection.scoped_read(&mut key, |guard| guard[0] + guard[1]); + assert_eq!(sum, 24 + 42); + } + + #[test] + fn scoped_lock_works() { + let mut key = ThreadKey::get().unwrap(); + let collection = OwnedLockCollection::new([RwLock::new(24), RwLock::new(42)]); + collection.scoped_lock(&mut key, |guard| *guard[0] += *guard[1]); + + let sum = collection.scoped_lock(&mut key, |guard| { + assert_eq!(*guard[0], 24 + 42); + assert_eq!(*guard[1], 42); + *guard[0] + *guard[1] + }); + + assert_eq!(sum, 24 + 42 + 42); + } + + #[test] + fn scoped_try_lock_can_fail() { + let key = ThreadKey::get().unwrap(); + let collection = OwnedLockCollection::new([Mutex::new(1), Mutex::new(2)]); + let guard = collection.lock(key); + + std::thread::scope(|s| { + s.spawn(|| { + let key = ThreadKey::get().unwrap(); + let r = collection.scoped_try_lock(key, |_| {}); + assert!(r.is_err()); + }); + }); + + drop(guard); + } + + #[test] + fn scoped_try_read_can_fail() { + let key = ThreadKey::get().unwrap(); + let collection = OwnedLockCollection::new([RwLock::new(1), RwLock::new(2)]); + let guard = collection.lock(key); + + std::thread::scope(|s| { + s.spawn(|| { + let key = ThreadKey::get().unwrap(); + let r = collection.scoped_try_read(key, |_| {}); + assert!(r.is_err()); + }); + }); + + drop(guard); + } + + #[test] fn try_lock_works_on_unlocked() { let key = ThreadKey::get().unwrap(); let collection = OwnedLockCollection::new((Mutex::new(0), Mutex::new(1))); @@ -630,6 +632,74 @@ mod tests { } #[test] + fn try_read_succeeds_for_unlocked_collection() { + let key = ThreadKey::get().unwrap(); + let mutexes = [RwLock::new(24), RwLock::new(42)]; + let collection = OwnedLockCollection::new(mutexes); + let guard = collection.try_read(key).unwrap(); + assert_eq!(*guard[0], 24); + assert_eq!(*guard[1], 42); + } + + #[test] + fn try_read_fails_on_locked() { + let key = ThreadKey::get().unwrap(); + let collection = OwnedLockCollection::new((RwLock::new(0), RwLock::new(1))); + + std::thread::scope(|s| { + s.spawn(|| { + let key = ThreadKey::get().unwrap(); + #[allow(unused)] + let guard = collection.lock(key); + std::mem::forget(guard); + }); + }); + + assert!(collection.try_read(key).is_err()); + } + + #[test] + fn can_read_twice_on_different_threads() { + let key = ThreadKey::get().unwrap(); + let mutexes = [RwLock::new(24), RwLock::new(42)]; + let collection = OwnedLockCollection::new(mutexes); + + std::thread::scope(|s| { + s.spawn(|| { + let key = ThreadKey::get().unwrap(); + let guard = collection.read(key); + assert_eq!(*guard[0], 24); + assert_eq!(*guard[1], 42); + std::mem::forget(guard); + }); + }); + + let guard = collection.try_read(key).unwrap(); + assert_eq!(*guard[0], 24); + assert_eq!(*guard[1], 42); + } + + #[test] + fn unlock_collection_works() { + let key = ThreadKey::get().unwrap(); + let collection = OwnedLockCollection::new((Mutex::new("foo"), Mutex::new("bar"))); + let guard = collection.lock(key); + + let key = OwnedLockCollection::<(Mutex<_>, Mutex<_>)>::unlock(guard); + assert!(collection.try_lock(key).is_ok()) + } + + #[test] + fn read_unlock_collection_works() { + let key = ThreadKey::get().unwrap(); + let collection = OwnedLockCollection::new((RwLock::new("foo"), RwLock::new("bar"))); + let guard = collection.read(key); + + let key = OwnedLockCollection::<(&RwLock<_>, &RwLock<_>)>::unlock_read(guard); + assert!(collection.try_lock(key).is_ok()) + } + + #[test] fn default_works() { type MyCollection = OwnedLockCollection<(Mutex<i32>, Mutex<Option<i32>>, Mutex<String>)>; let collection = MyCollection::default(); @@ -649,4 +719,59 @@ mod tests { assert_eq!(collection.data.len(), 3); } + + #[test] + fn works_in_collection() { + let key = ThreadKey::get().unwrap(); + let collection = + OwnedLockCollection::new(OwnedLockCollection::new([RwLock::new(0), RwLock::new(1)])); + + let mut guard = collection.lock(key); + assert_eq!(*guard[0], 0); + assert_eq!(*guard[1], 1); + *guard[1] = 2; + + let key = OwnedLockCollection::<OwnedLockCollection<[RwLock<_>; 2]>>::unlock(guard); + let guard = collection.read(key); + assert_eq!(*guard[0], 0); + assert_eq!(*guard[1], 2); + } + + #[test] + fn as_mut_works() { + let mut mutexes = [Mutex::new(0), Mutex::new(1)]; + let mut collection = OwnedLockCollection::new(&mut mutexes); + + collection.as_mut()[0] = Mutex::new(42); + + assert_eq!(*collection.as_mut()[0].get_mut(), 42); + } + + #[test] + fn child_mut_works() { + let mut mutexes = [Mutex::new(0), Mutex::new(1)]; + let mut collection = OwnedLockCollection::new(&mut mutexes); + + collection.child_mut()[0] = Mutex::new(42); + + assert_eq!(*collection.child_mut()[0].get_mut(), 42); + } + + #[test] + fn into_child_works() { + let mutexes = [Mutex::new(0), Mutex::new(1)]; + let mut collection = OwnedLockCollection::new(mutexes); + + collection.child_mut()[0] = Mutex::new(42); + + assert_eq!( + *collection + .into_child() + .as_mut() + .get_mut(0) + .unwrap() + .get_mut(), + 42 + ); + } } diff --git a/src/collection/ref.rs b/src/collection/ref.rs index b68b72f..5f96533 100644 --- a/src/collection/ref.rs +++ b/src/collection/ref.rs @@ -3,7 +3,10 @@ use std::fmt::Debug; use crate::lockable::{Lockable, OwnedLockable, RawLock, Sharable}; use crate::{Keyable, ThreadKey}; -use super::utils::{get_locks, ordered_contains_duplicates}; +use super::utils::{ + get_locks, ordered_contains_duplicates, scoped_read, scoped_try_read, scoped_try_write, + scoped_write, +}; use super::{utils, LockGuard, RefLockCollection}; impl<'a, L> IntoIterator for &'a RefLockCollection<'a, L> @@ -27,17 +30,17 @@ unsafe impl<L: Lockable> RawLock for RefLockCollection<'_, L> { } } - unsafe fn raw_lock(&self) { - utils::ordered_lock(&self.locks) + unsafe fn raw_write(&self) { + utils::ordered_write(&self.locks) } - unsafe fn raw_try_lock(&self) -> bool { - utils::ordered_try_lock(&self.locks) + unsafe fn raw_try_write(&self) -> bool { + utils::ordered_try_write(&self.locks) } - unsafe fn raw_unlock(&self) { + unsafe fn raw_unlock_write(&self) { for lock in &self.locks { - lock.raw_unlock(); + lock.raw_unlock_write(); } } @@ -68,7 +71,7 @@ unsafe impl<L: Lockable> Lockable for RefLockCollection<'_, L> { Self: 'a; fn get_ptrs<'a>(&'a self, ptrs: &mut Vec<&'a dyn RawLock>) { - ptrs.extend_from_slice(&self.locks); + ptrs.push(self) } unsafe fn guard(&self) -> Self::Guard<'_> { @@ -100,7 +103,7 @@ unsafe impl<L: Sharable> Sharable for RefLockCollection<'_, L> { } } -impl<T, L: AsRef<T>> AsRef<T> for RefLockCollection<'_, L> { +impl<T: ?Sized, L: AsRef<T>> AsRef<T> for RefLockCollection<'_, L> { fn as_ref(&self) -> &T { self.data.as_ref() } @@ -234,44 +237,16 @@ impl<'a, L: Lockable> RefLockCollection<'a, L> { Some(Self { data, locks }) } - pub fn scoped_lock<R>(&self, key: impl Keyable, f: impl Fn(L::DataMut<'_>) -> R) -> R { - unsafe { - // safety: we have the thread key - self.raw_lock(); - - // safety: the data was just locked - let r = f(self.data_mut()); - - // safety: the collection is still locked - self.raw_unlock(); - - drop(key); // ensure the key stays alive long enough - - r - } + pub fn scoped_lock<'s, R>(&'s self, key: impl Keyable, f: impl Fn(L::DataMut<'s>) -> R) -> R { + scoped_write(self, key, f) } - pub fn scoped_try_lock<Key: Keyable, R>( - &self, + pub fn scoped_try_lock<'s, Key: Keyable, R>( + &'s self, key: Key, - f: impl Fn(L::DataMut<'_>) -> R, + f: impl Fn(L::DataMut<'s>) -> R, ) -> Result<R, Key> { - unsafe { - // safety: we have the thread key - if !self.raw_try_lock() { - return Err(key); - } - - // safety: we just locked the collection - let r = f(self.data_mut()); - - // safety: the collection is still locked - self.raw_unlock(); - - drop(key); // ensures the key stays valid long enough - - Ok(r) - } + scoped_try_write(self, key, f) } /// Locks the collection @@ -298,7 +273,7 @@ impl<'a, L: Lockable> RefLockCollection<'a, L> { pub fn lock(&self, key: ThreadKey) -> LockGuard<L::Guard<'_>> { let guard = unsafe { // safety: we have the thread key - self.raw_lock(); + self.raw_write(); // safety: we've locked all of this already self.data.guard() @@ -339,7 +314,7 @@ impl<'a, L: Lockable> RefLockCollection<'a, L> { /// ``` pub fn try_lock(&self, key: ThreadKey) -> Result<LockGuard<L::Guard<'_>>, ThreadKey> { let guard = unsafe { - if !self.raw_try_lock() { + if !self.raw_try_write() { return Err(key); } @@ -376,44 +351,16 @@ impl<'a, L: Lockable> RefLockCollection<'a, L> { } impl<L: Sharable> RefLockCollection<'_, L> { - pub fn scoped_read<R>(&self, key: impl Keyable, f: impl Fn(L::DataRef<'_>) -> R) -> R { - unsafe { - // safety: we have the thread key - self.raw_read(); - - // safety: the data was just locked - let r = f(self.data_ref()); - - // safety: the collection is still locked - self.raw_unlock_read(); - - drop(key); // ensure the key stays alive long enough - - r - } + pub fn scoped_read<'a, R>(&'a self, key: impl Keyable, f: impl Fn(L::DataRef<'a>) -> R) -> R { + scoped_read(self, key, f) } - pub fn scoped_try_read<Key: Keyable, R>( - &self, + pub fn scoped_try_read<'a, Key: Keyable, R>( + &'a self, key: Key, - f: impl Fn(L::DataRef<'_>) -> R, + f: impl Fn(L::DataRef<'a>) -> R, ) -> Result<R, Key> { - unsafe { - // safety: we have the thread key - if !self.raw_try_read() { - return Err(key); - } - - // safety: we just locked the collection - let r = f(self.data_ref()); - - // safety: the collection is still locked - self.raw_unlock_read(); - - drop(key); // ensures the key stays valid long enough - - Ok(r) - } + scoped_try_read(self, key, f) } /// Locks the collection, so that other threads can still read from it @@ -565,6 +512,88 @@ mod tests { } #[test] + fn from() { + let key = ThreadKey::get().unwrap(); + let mutexes = [Mutex::new("foo"), Mutex::new("bar"), Mutex::new("baz")]; + let collection = RefLockCollection::from(&mutexes); + let guard = collection.lock(key); + assert_eq!(*guard[0], "foo"); + assert_eq!(*guard[1], "bar"); + assert_eq!(*guard[2], "baz"); + } + + #[test] + fn scoped_lock_changes_collection() { + let mut key = ThreadKey::get().unwrap(); + let mutexes = [Mutex::new(24), Mutex::new(42)]; + let collection = RefLockCollection::new(&mutexes); + let sum = collection.scoped_lock(&mut key, |guard| { + *guard[0] = 128; + *guard[0] + *guard[1] + }); + + assert_eq!(sum, 128 + 42); + + let guard = collection.lock(key); + assert_eq!(*guard[0], 128); + assert_eq!(*guard[1], 42); + } + + #[test] + fn scoped_read_sees_changes() { + let mut key = ThreadKey::get().unwrap(); + let mutexes = [RwLock::new(24), RwLock::new(42)]; + let collection = RefLockCollection::new(&mutexes); + collection.scoped_lock(&mut key, |guard| { + *guard[0] = 128; + }); + + let sum = collection.scoped_read(&mut key, |guard| { + assert_eq!(*guard[0], 128); + assert_eq!(*guard[1], 42); + *guard[0] + *guard[1] + }); + + assert_eq!(sum, 128 + 42); + } + + #[test] + fn scoped_try_lock_can_fail() { + let key = ThreadKey::get().unwrap(); + let locks = [Mutex::new(1), Mutex::new(2)]; + let collection = RefLockCollection::new(&locks); + let guard = collection.lock(key); + + std::thread::scope(|s| { + s.spawn(|| { + let key = ThreadKey::get().unwrap(); + let r = collection.scoped_try_lock(key, |_| {}); + assert!(r.is_err()); + }); + }); + + drop(guard); + } + + #[test] + fn scoped_try_read_can_fail() { + let key = ThreadKey::get().unwrap(); + let locks = [RwLock::new(1), RwLock::new(2)]; + let collection = RefLockCollection::new(&locks); + let guard = collection.lock(key); + + std::thread::scope(|s| { + s.spawn(|| { + let key = ThreadKey::get().unwrap(); + let r = collection.scoped_try_read(key, |_| {}); + assert!(r.is_err()); + }); + }); + + drop(guard); + } + + #[test] fn try_lock_succeeds_for_unlocked_collection() { let key = ThreadKey::get().unwrap(); let mutexes = [Mutex::new(24), Mutex::new(42)]; @@ -644,17 +673,85 @@ mod tests { } #[test] + fn into_ref_iterator() { + let mut key = ThreadKey::get().unwrap(); + let mutexes = [Mutex::new(0), Mutex::new(1), Mutex::new(2)]; + let collection = RefLockCollection::new(&mutexes); + for (i, mutex) in (&collection).into_iter().enumerate() { + mutex.scoped_lock(&mut key, |val| assert_eq!(*val, i)) + } + } + + #[test] + fn ref_iterator() { + let mut key = ThreadKey::get().unwrap(); + let mutexes = [Mutex::new(0), Mutex::new(1), Mutex::new(2)]; + let collection = RefLockCollection::new(&mutexes); + for (i, mutex) in collection.iter().enumerate() { + mutex.scoped_lock(&mut key, |val| assert_eq!(*val, i)) + } + } + + #[test] fn works_in_collection() { let key = ThreadKey::get().unwrap(); - let mutex1 = Mutex::new(0); - let mutex2 = Mutex::new(1); + let mutex1 = RwLock::new(0); + let mutex2 = RwLock::new(1); let collection0 = [&mutex1, &mutex2]; let collection1 = RefLockCollection::try_new(&collection0).unwrap(); let collection = RefLockCollection::try_new(&collection1).unwrap(); - let guard = collection.lock(key); + let mut guard = collection.lock(key); assert!(mutex1.is_locked()); assert!(mutex2.is_locked()); + assert_eq!(*guard[0], 0); + assert_eq!(*guard[1], 1); + *guard[1] = 2; drop(guard); + + let key = ThreadKey::get().unwrap(); + let guard = collection.read(key); + assert!(mutex1.is_locked()); + assert!(mutex2.is_locked()); + assert_eq!(*guard[0], 0); + assert_eq!(*guard[1], 2); + } + + #[test] + fn unlock_collection_works() { + let key = ThreadKey::get().unwrap(); + let mutexes = (Mutex::new("foo"), Mutex::new("bar")); + let collection = RefLockCollection::new(&mutexes); + let guard = collection.lock(key); + + let key = RefLockCollection::<(Mutex<_>, Mutex<_>)>::unlock(guard); + assert!(collection.try_lock(key).is_ok()) + } + + #[test] + fn read_unlock_collection_works() { + let key = ThreadKey::get().unwrap(); + let locks = (RwLock::new("foo"), RwLock::new("bar")); + let collection = RefLockCollection::new(&locks); + let guard = collection.read(key); + + let key = RefLockCollection::<(&RwLock<_>, &RwLock<_>)>::unlock_read(guard); + assert!(collection.try_lock(key).is_ok()) + } + + #[test] + fn as_ref_works() { + let mutexes = [Mutex::new(0), Mutex::new(1)]; + let collection = RefLockCollection::new(&mutexes); + + assert!(std::ptr::addr_eq(&mutexes, collection.as_ref())) + } + + #[test] + fn child() { + let mutexes = [Mutex::new(0), Mutex::new(1)]; + let collection = RefLockCollection::new(&mutexes); + + assert!(std::ptr::addr_eq(&mutexes, collection.child())) } } diff --git a/src/collection/retry.rs b/src/collection/retry.rs index 775ea29..70e5183 100644 --- a/src/collection/retry.rs +++ b/src/collection/retry.rs @@ -9,7 +9,8 @@ use crate::lockable::{ use crate::{Keyable, ThreadKey}; use super::utils::{ - attempt_to_recover_locks_from_panic, attempt_to_recover_reads_from_panic, get_locks_unsorted, + attempt_to_recover_reads_from_panic, attempt_to_recover_writes_from_panic, get_locks_unsorted, + scoped_read, scoped_try_read, scoped_try_write, scoped_write, }; use super::{LockGuard, RetryingLockCollection}; @@ -40,7 +41,7 @@ unsafe impl<L: Lockable> RawLock for RetryingLockCollection<L> { } } - unsafe fn raw_lock(&self) { + unsafe fn raw_write(&self) { let locks = get_locks_unsorted(&self.data); if locks.is_empty() { @@ -57,7 +58,7 @@ unsafe impl<L: Lockable> RawLock for RetryingLockCollection<L> { // This prevents us from entering a spin loop waiting for // the same lock to be unlocked // safety: we have the thread key - locks[first_index.get()].raw_lock(); + locks[first_index.get()].raw_write(); for (i, lock) in locks.iter().enumerate() { if i == first_index.get() { // we've already locked this one @@ -69,15 +70,15 @@ unsafe impl<L: Lockable> RawLock for RetryingLockCollection<L> { // it does return false, then the lock function is called // immediately after, causing a panic // safety: we have the thread key - if lock.raw_try_lock() { + if lock.raw_try_write() { locked.set(locked.get() + 1); } else { // safety: we already locked all of these - attempt_to_recover_locks_from_panic(&locks[0..i]); + attempt_to_recover_writes_from_panic(&locks[0..i]); if first_index.get() >= i { // safety: this is already locked and can't be // unlocked by the previous loop - locks[first_index.get()].raw_unlock(); + locks[first_index.get()].raw_unlock_write(); } // nothing is locked anymore @@ -94,15 +95,15 @@ unsafe impl<L: Lockable> RawLock for RetryingLockCollection<L> { } }, || { - utils::attempt_to_recover_locks_from_panic(&locks[0..locked.get()]); + utils::attempt_to_recover_writes_from_panic(&locks[0..locked.get()]); if first_index.get() >= locked.get() { - locks[first_index.get()].raw_unlock(); + locks[first_index.get()].raw_unlock_write(); } }, ) } - unsafe fn raw_try_lock(&self) -> bool { + unsafe fn raw_try_write(&self) -> bool { let locks = get_locks_unsorted(&self.data); if locks.is_empty() { @@ -117,26 +118,26 @@ unsafe impl<L: Lockable> RawLock for RetryingLockCollection<L> { || unsafe { for (i, lock) in locks.iter().enumerate() { // safety: we have the thread key - if lock.raw_try_lock() { + if lock.raw_try_write() { locked.set(locked.get() + 1); } else { // safety: we already locked all of these - attempt_to_recover_locks_from_panic(&locks[0..i]); + attempt_to_recover_writes_from_panic(&locks[0..i]); return false; } } true }, - || utils::attempt_to_recover_locks_from_panic(&locks[0..locked.get()]), + || utils::attempt_to_recover_writes_from_panic(&locks[0..locked.get()]), ) } - unsafe fn raw_unlock(&self) { + unsafe fn raw_unlock_write(&self) { let locks = get_locks_unsorted(&self.data); for lock in locks { - lock.raw_unlock(); + lock.raw_unlock_write(); } } @@ -243,7 +244,7 @@ unsafe impl<L: Lockable> Lockable for RetryingLockCollection<L> { Self: 'a; fn get_ptrs<'a>(&'a self, ptrs: &mut Vec<&'a dyn RawLock>) { - self.data.get_ptrs(ptrs) + ptrs.push(self) } unsafe fn guard(&self) -> Self::Guard<'_> { @@ -347,13 +348,13 @@ impl<E: OwnedLockable + Extend<L>, L: OwnedLockable> Extend<L> for RetryingLockC } } -impl<T, L: AsRef<T>> AsRef<T> for RetryingLockCollection<L> { +impl<T: ?Sized, L: AsRef<T>> AsRef<T> for RetryingLockCollection<L> { fn as_ref(&self) -> &T { self.data.as_ref() } } -impl<T, L: AsMut<T>> AsMut<T> for RetryingLockCollection<L> { +impl<T: ?Sized, L: AsMut<T>> AsMut<T> for RetryingLockCollection<L> { fn as_mut(&mut self) -> &mut T { self.data.as_mut() } @@ -389,7 +390,8 @@ impl<L: OwnedLockable> RetryingLockCollection<L> { /// ``` #[must_use] pub const fn new(data: L) -> Self { - Self { data } + // safety: the data cannot cannot contain references + unsafe { Self::new_unchecked(data) } } } @@ -410,7 +412,8 @@ impl<'a, L: OwnedLockable> RetryingLockCollection<&'a L> { /// ``` #[must_use] pub const fn new_ref(data: &'a L) -> Self { - Self { data } + // safety: the data cannot cannot contain references + unsafe { Self::new_unchecked(data) } } } @@ -525,47 +528,20 @@ impl<L: Lockable> RetryingLockCollection<L> { /// ``` #[must_use] pub fn try_new(data: L) -> Option<Self> { - (!contains_duplicates(&data)).then_some(Self { data }) + // safety: the data is checked for duplicates before returning the collection + (!contains_duplicates(&data)).then_some(unsafe { Self::new_unchecked(data) }) } - pub fn scoped_lock<R>(&self, key: impl Keyable, f: impl Fn(L::DataMut<'_>) -> R) -> R { - unsafe { - // safety: we have the thread key - self.raw_lock(); - - // safety: the data was just locked - let r = f(self.data_mut()); - - // safety: the collection is still locked - self.raw_unlock(); - - drop(key); // ensure the key stays alive long enough - - r - } + pub fn scoped_lock<'a, R>(&'a self, key: impl Keyable, f: impl Fn(L::DataMut<'a>) -> R) -> R { + scoped_write(self, key, f) } - pub fn scoped_try_lock<Key: Keyable, R>( - &self, + pub fn scoped_try_lock<'a, Key: Keyable, R>( + &'a self, key: Key, - f: impl Fn(L::DataMut<'_>) -> R, + f: impl Fn(L::DataMut<'a>) -> R, ) -> Result<R, Key> { - unsafe { - // safety: we have the thread key - if !self.raw_try_lock() { - return Err(key); - } - - // safety: we just locked the collection - let r = f(self.data_mut()); - - // safety: the collection is still locked - self.raw_unlock(); - - drop(key); // ensures the key stays valid long enough - - Ok(r) - } + scoped_try_write(self, key, f) } /// Locks the collection @@ -591,7 +567,7 @@ impl<L: Lockable> RetryingLockCollection<L> { pub fn lock(&self, key: ThreadKey) -> LockGuard<L::Guard<'_>> { unsafe { // safety: we're taking the thread key - self.raw_lock(); + self.raw_write(); LockGuard { // safety: we just locked the collection @@ -634,7 +610,7 @@ impl<L: Lockable> RetryingLockCollection<L> { pub fn try_lock(&self, key: ThreadKey) -> Result<LockGuard<L::Guard<'_>>, ThreadKey> { unsafe { // safety: we're taking the thread key - if self.raw_try_lock() { + if self.raw_try_write() { Ok(LockGuard { // safety: we just succeeded in locking everything guard: self.guard(), @@ -671,44 +647,16 @@ impl<L: Lockable> RetryingLockCollection<L> { } impl<L: Sharable> RetryingLockCollection<L> { - pub fn scoped_read<R>(&self, key: impl Keyable, f: impl Fn(L::DataRef<'_>) -> R) -> R { - unsafe { - // safety: we have the thread key - self.raw_read(); - - // safety: the data was just locked - let r = f(self.data_ref()); - - // safety: the collection is still locked - self.raw_unlock_read(); - - drop(key); // ensure the key stays alive long enough - - r - } + pub fn scoped_read<'a, R>(&'a self, key: impl Keyable, f: impl Fn(L::DataRef<'a>) -> R) -> R { + scoped_read(self, key, f) } - pub fn scoped_try_read<Key: Keyable, R>( - &self, + pub fn scoped_try_read<'a, Key: Keyable, R>( + &'a self, key: Key, - f: impl Fn(L::DataRef<'_>) -> R, + f: impl Fn(L::DataRef<'a>) -> R, ) -> Result<R, Key> { - unsafe { - // safety: we have the thread key - if !self.raw_try_read() { - return Err(key); - } - - // safety: we just locked the collection - let r = f(self.data_ref()); - - // safety: the collection is still locked - self.raw_unlock_read(); - - drop(key); // ensures the key stays valid long enough - - Ok(r) - } + scoped_try_read(self, key, f) } /// Locks the collection, so that other threads can still read from it @@ -778,7 +726,7 @@ impl<L: Sharable> RetryingLockCollection<L> { pub fn try_read(&self, key: ThreadKey) -> Result<LockGuard<L::ReadGuard<'_>>, ThreadKey> { unsafe { // safety: we're taking the thread key - if !self.raw_try_lock() { + if !self.raw_try_read() { return Err(key); } @@ -911,7 +859,7 @@ where mod tests { use super::*; use crate::collection::BoxedLockCollection; - use crate::{LockCollection, Mutex, RwLock, ThreadKey}; + use crate::{Mutex, RwLock, ThreadKey}; #[test] fn nonduplicate_lock_references_are_allowed() { @@ -927,6 +875,159 @@ mod tests { } #[test] + #[allow(clippy::float_cmp)] + fn uses_correct_default() { + let collection = + RetryingLockCollection::<(RwLock<f64>, Mutex<Option<i32>>, Mutex<usize>)>::default(); + let tuple = collection.into_inner(); + assert_eq!(tuple.0, 0.0); + assert!(tuple.1.is_none()); + assert_eq!(tuple.2, 0) + } + + #[test] + fn from() { + let key = ThreadKey::get().unwrap(); + let collection = + RetryingLockCollection::from([Mutex::new("foo"), Mutex::new("bar"), Mutex::new("baz")]); + let guard = collection.lock(key); + assert_eq!(*guard[0], "foo"); + assert_eq!(*guard[1], "bar"); + assert_eq!(*guard[2], "baz"); + } + + #[test] + fn new_ref_works() { + let key = ThreadKey::get().unwrap(); + let mutexes = [Mutex::new(0), Mutex::new(1)]; + let collection = RetryingLockCollection::new_ref(&mutexes); + collection.scoped_lock(key, |guard| { + assert_eq!(*guard[0], 0); + assert_eq!(*guard[1], 1); + }) + } + + #[test] + fn scoped_read_sees_changes() { + let mut key = ThreadKey::get().unwrap(); + let mutexes = [RwLock::new(24), RwLock::new(42)]; + let collection = RetryingLockCollection::new(mutexes); + collection.scoped_lock(&mut key, |guard| *guard[0] = 128); + + let sum = collection.scoped_read(&mut key, |guard| { + assert_eq!(*guard[0], 128); + assert_eq!(*guard[1], 42); + *guard[0] + *guard[1] + }); + + assert_eq!(sum, 128 + 42); + } + + #[test] + fn get_mut_affects_scoped_read() { + let mut key = ThreadKey::get().unwrap(); + let mutexes = [RwLock::new(24), RwLock::new(42)]; + let mut collection = RetryingLockCollection::new(mutexes); + let guard = collection.get_mut(); + *guard[0] = 128; + + let sum = collection.scoped_read(&mut key, |guard| { + assert_eq!(*guard[0], 128); + assert_eq!(*guard[1], 42); + *guard[0] + *guard[1] + }); + + assert_eq!(sum, 128 + 42); + } + + #[test] + fn scoped_try_lock_can_fail() { + let key = ThreadKey::get().unwrap(); + let collection = RetryingLockCollection::new([Mutex::new(1), Mutex::new(2)]); + let guard = collection.lock(key); + + std::thread::scope(|s| { + s.spawn(|| { + let key = ThreadKey::get().unwrap(); + let r = collection.scoped_try_lock(key, |_| {}); + assert!(r.is_err()); + }); + }); + + drop(guard); + } + + #[test] + fn scoped_try_read_can_fail() { + let key = ThreadKey::get().unwrap(); + let collection = RetryingLockCollection::new([RwLock::new(1), RwLock::new(2)]); + let guard = collection.lock(key); + + std::thread::scope(|s| { + s.spawn(|| { + let key = ThreadKey::get().unwrap(); + let r = collection.scoped_try_read(key, |_| {}); + assert!(r.is_err()); + }); + }); + + drop(guard); + } + + #[test] + fn try_lock_works() { + let key = ThreadKey::get().unwrap(); + let collection = RetryingLockCollection::new([Mutex::new(1), Mutex::new(2)]); + let guard = collection.try_lock(key); + + std::thread::scope(|s| { + s.spawn(|| { + let key = ThreadKey::get().unwrap(); + let guard = collection.try_lock(key); + assert!(guard.is_err()); + }); + }); + + assert!(guard.is_ok()); + } + + #[test] + fn try_read_works() { + let key = ThreadKey::get().unwrap(); + let collection = RetryingLockCollection::new([RwLock::new(1), RwLock::new(2)]); + let guard = collection.try_read(key); + + std::thread::scope(|s| { + s.spawn(|| { + let key = ThreadKey::get().unwrap(); + let guard = collection.try_read(key); + assert!(guard.is_ok()); + }); + }); + + assert!(guard.is_ok()); + } + + #[test] + fn try_read_fails_for_locked_collection() { + let key = ThreadKey::get().unwrap(); + let mutexes = [RwLock::new(24), RwLock::new(42)]; + let collection = RetryingLockCollection::new_ref(&mutexes); + + std::thread::scope(|s| { + s.spawn(|| { + let key = ThreadKey::get().unwrap(); + let guard = mutexes[1].write(key); + assert_eq!(*guard, 42); + std::mem::forget(guard); + }); + }); + + let guard = collection.try_read(key); + assert!(guard.is_err()); + } + + #[test] fn locks_all_inner_mutexes() { let key = ThreadKey::get().unwrap(); let mutex1 = Mutex::new(0); @@ -974,6 +1075,55 @@ mod tests { } #[test] + fn from_iterator() { + let key = ThreadKey::get().unwrap(); + let collection: RetryingLockCollection<Vec<Mutex<&str>>> = + [Mutex::new("foo"), Mutex::new("bar"), Mutex::new("baz")] + .into_iter() + .collect(); + let guard = collection.lock(key); + assert_eq!(*guard[0], "foo"); + assert_eq!(*guard[1], "bar"); + assert_eq!(*guard[2], "baz"); + } + + #[test] + fn into_owned_iterator() { + let collection = RetryingLockCollection::new([Mutex::new(0), Mutex::new(1), Mutex::new(2)]); + for (i, mutex) in collection.into_iter().enumerate() { + assert_eq!(mutex.into_inner(), i); + } + } + + #[test] + fn into_ref_iterator() { + let mut key = ThreadKey::get().unwrap(); + let collection = RetryingLockCollection::new([Mutex::new(0), Mutex::new(1), Mutex::new(2)]); + for (i, mutex) in (&collection).into_iter().enumerate() { + mutex.scoped_lock(&mut key, |val| assert_eq!(*val, i)) + } + } + + #[test] + fn ref_iterator() { + let mut key = ThreadKey::get().unwrap(); + let collection = RetryingLockCollection::new([Mutex::new(0), Mutex::new(1), Mutex::new(2)]); + for (i, mutex) in collection.iter().enumerate() { + mutex.scoped_lock(&mut key, |val| assert_eq!(*val, i)) + } + } + + #[test] + fn mut_iterator() { + let mut key = ThreadKey::get().unwrap(); + let mut collection = + RetryingLockCollection::new([Mutex::new(0), Mutex::new(1), Mutex::new(2)]); + for (i, mutex) in collection.iter_mut().enumerate() { + mutex.scoped_lock(&mut key, |val| assert_eq!(*val, i)) + } + } + + #[test] fn extend_collection() { let mutex1 = Mutex::new(0); let mutex2 = Mutex::new(0); @@ -991,9 +1141,76 @@ mod tests { let guard = collection.lock(key); assert!(guard.len() == 0); - let key = LockCollection::<[RwLock<_>; 0]>::unlock(guard); + let key = RetryingLockCollection::<[RwLock<_>; 0]>::unlock(guard); + + let guard = collection.read(key); + assert!(guard.len() == 0); + } + + #[test] + fn read_empty_lock_collection() { + let key = ThreadKey::get().unwrap(); + let collection: RetryingLockCollection<[RwLock<i32>; 0]> = RetryingLockCollection::new([]); let guard = collection.read(key); assert!(guard.len() == 0); + let key = RetryingLockCollection::<[RwLock<_>; 0]>::unlock_read(guard); + + let guard = collection.lock(key); + assert!(guard.len() == 0); + } + + #[test] + fn as_ref_works() { + let mutexes = [Mutex::new(0), Mutex::new(1)]; + let collection = RetryingLockCollection::new_ref(&mutexes); + + assert!(std::ptr::addr_eq(&mutexes, collection.as_ref())) + } + + #[test] + fn as_mut_works() { + let mut mutexes = [Mutex::new(0), Mutex::new(1)]; + let mut collection = RetryingLockCollection::new(&mut mutexes); + + collection.as_mut()[0] = Mutex::new(42); + + assert_eq!(*collection.as_mut()[0].get_mut(), 42); + } + + #[test] + fn child() { + let mutexes = [Mutex::new(0), Mutex::new(1)]; + let collection = RetryingLockCollection::new_ref(&mutexes); + + assert!(std::ptr::addr_eq(&mutexes, *collection.child())) + } + + #[test] + fn child_mut_works() { + let mut mutexes = [Mutex::new(0), Mutex::new(1)]; + let mut collection = RetryingLockCollection::new(&mut mutexes); + + collection.child_mut()[0] = Mutex::new(42); + + assert_eq!(*collection.child_mut()[0].get_mut(), 42); + } + + #[test] + fn into_child_works() { + let mutexes = [Mutex::new(0), Mutex::new(1)]; + let mut collection = RetryingLockCollection::new(mutexes); + + collection.child_mut()[0] = Mutex::new(42); + + assert_eq!( + *collection + .into_child() + .as_mut() + .get_mut(0) + .unwrap() + .get_mut(), + 42 + ); } } diff --git a/src/collection/utils.rs b/src/collection/utils.rs index 1d96e5c..59a68da 100644 --- a/src/collection/utils.rs +++ b/src/collection/utils.rs @@ -1,7 +1,8 @@ use std::cell::Cell; use crate::handle_unwind::handle_unwind; -use crate::lockable::{Lockable, RawLock}; +use crate::lockable::{Lockable, RawLock, Sharable}; +use crate::Keyable; #[must_use] pub fn get_locks<L: Lockable>(data: &L) -> Vec<&dyn RawLock> { @@ -32,18 +33,18 @@ pub fn ordered_contains_duplicates(l: &[&dyn RawLock]) -> bool { } /// Lock a set of locks in the given order. It's UB to call this without a `ThreadKey` -pub unsafe fn ordered_lock(locks: &[&dyn RawLock]) { +pub unsafe fn ordered_write(locks: &[&dyn RawLock]) { // these will be unlocked in case of a panic let locked = Cell::new(0); handle_unwind( || { for lock in locks { - lock.raw_lock(); + lock.raw_write(); locked.set(locked.get() + 1); } }, - || attempt_to_recover_locks_from_panic(&locks[0..locked.get()]), + || attempt_to_recover_writes_from_panic(&locks[0..locked.get()]), ) } @@ -65,19 +66,19 @@ pub unsafe fn ordered_read(locks: &[&dyn RawLock]) { /// Locks the locks in the order they are given. This causes deadlock if the /// locks contain duplicates, or if this is called by multiple threads with the /// locks in different orders. -pub unsafe fn ordered_try_lock(locks: &[&dyn RawLock]) -> bool { +pub unsafe fn ordered_try_write(locks: &[&dyn RawLock]) -> bool { let locked = Cell::new(0); handle_unwind( || unsafe { for (i, lock) in locks.iter().enumerate() { // safety: we have the thread key - if lock.raw_try_lock() { + if lock.raw_try_write() { locked.set(locked.get() + 1); } else { for lock in &locks[0..i] { // safety: this lock was already acquired - lock.raw_unlock(); + lock.raw_unlock_write(); } return false; } @@ -87,7 +88,7 @@ pub unsafe fn ordered_try_lock(locks: &[&dyn RawLock]) -> bool { }, || // safety: everything in locked is locked - attempt_to_recover_locks_from_panic(&locks[0..locked.get()]), + attempt_to_recover_writes_from_panic(&locks[0..locked.get()]), ) } @@ -120,12 +121,104 @@ pub unsafe fn ordered_try_read(locks: &[&dyn RawLock]) -> bool { ) } +pub fn scoped_write<'a, L: RawLock + Lockable, R>( + collection: &'a L, + key: impl Keyable, + f: impl FnOnce(L::DataMut<'a>) -> R, +) -> R { + unsafe { + // safety: we have the key + collection.raw_write(); + + // safety: we just locked this + let r = f(collection.data_mut()); + + // this ensures the key is held long enough + drop(key); + + // safety: we've locked already, and aren't using the data again + collection.raw_unlock_write(); + + r + } +} + +pub fn scoped_try_write<'a, L: RawLock + Lockable, Key: Keyable, R>( + collection: &'a L, + key: Key, + f: impl FnOnce(L::DataMut<'a>) -> R, +) -> Result<R, Key> { + unsafe { + // safety: we have the key + if !collection.raw_try_write() { + return Err(key); + } + + // safety: we just locked this + let r = f(collection.data_mut()); + + // this ensures the key is held long enough + drop(key); + + // safety: we've locked already, and aren't using the data again + collection.raw_unlock_write(); + + Ok(r) + } +} + +pub fn scoped_read<'a, L: RawLock + Sharable, R>( + collection: &'a L, + key: impl Keyable, + f: impl FnOnce(L::DataRef<'a>) -> R, +) -> R { + unsafe { + // safety: we have the key + collection.raw_read(); + + // safety: we just locked this + let r = f(collection.data_ref()); + + // this ensures the key is held long enough + drop(key); + + // safety: we've locked already, and aren't using the data again + collection.raw_unlock_read(); + + r + } +} + +pub fn scoped_try_read<'a, L: RawLock + Sharable, Key: Keyable, R>( + collection: &'a L, + key: Key, + f: impl FnOnce(L::DataRef<'a>) -> R, +) -> Result<R, Key> { + unsafe { + // safety: we have the key + if !collection.raw_try_read() { + return Err(key); + } + + // safety: we just locked this + let r = f(collection.data_ref()); + + // this ensures the key is held long enough + drop(key); + + // safety: we've locked already, and aren't using the data again + collection.raw_unlock_read(); + + Ok(r) + } +} + /// Unlocks the already locked locks in order to recover from a panic -pub unsafe fn attempt_to_recover_locks_from_panic(locks: &[&dyn RawLock]) { +pub unsafe fn attempt_to_recover_writes_from_panic(locks: &[&dyn RawLock]) { handle_unwind( || { // safety: the caller assumes that these are already locked - locks.iter().for_each(|lock| lock.raw_unlock()); + locks.iter().for_each(|lock| lock.raw_unlock_write()); }, // if we get another panic in here, we'll just have to poison what remains || locks.iter().for_each(|l| l.poison()), diff --git a/src/lockable.rs b/src/lockable.rs index f125c02..94042ea 100644 --- a/src/lockable.rs +++ b/src/lockable.rs @@ -28,7 +28,7 @@ pub unsafe trait RawLock { /// value is alive. /// /// [`ThreadKey`]: `crate::ThreadKey` - unsafe fn raw_lock(&self); + unsafe fn raw_write(&self); /// Attempt to lock without blocking. /// @@ -41,14 +41,14 @@ pub unsafe trait RawLock { /// value is alive. /// /// [`ThreadKey`]: `crate::ThreadKey` - unsafe fn raw_try_lock(&self) -> bool; + unsafe fn raw_try_write(&self) -> bool; /// Releases the lock /// /// # Safety /// /// It is undefined behavior to use this if the lock is not acquired - unsafe fn raw_unlock(&self); + unsafe fn raw_unlock_write(&self); /// Blocks until the data the lock protects can be safely read. /// @@ -625,7 +625,7 @@ unsafe impl<T: OwnedLockable> OwnedLockable for Vec<T> {} #[cfg(test)] mod tests { use super::*; - use crate::{Mutex, RwLock}; + use crate::{LockCollection, Mutex, RwLock, ThreadKey}; #[test] fn mut_ref_get_ptrs() { @@ -719,6 +719,57 @@ mod tests { } #[test] + fn vec_guard_ref() { + let key = ThreadKey::get().unwrap(); + let locks = vec![RwLock::new(1), RwLock::new(2)]; + let collection = LockCollection::new(locks); + + let mut guard = collection.lock(key); + assert_eq!(*guard[0], 1); + assert_eq!(*guard[1], 2); + *guard[0] = 3; + + let key = LockCollection::<Vec<RwLock<_>>>::unlock(guard); + let guard = collection.read(key); + assert_eq!(*guard[0], 3); + assert_eq!(*guard[1], 2); + } + + #[test] + fn vec_data_mut() { + let mut key = ThreadKey::get().unwrap(); + let mutexes = vec![Mutex::new(1), Mutex::new(2)]; + let collection = LockCollection::new(mutexes); + collection.scoped_lock(&mut key, |guard| { + assert_eq!(*guard[0], 1); + assert_eq!(*guard[1], 2); + *guard[0] = 3; + }); + + collection.scoped_lock(&mut key, |guard| { + assert_eq!(*guard[0], 3); + assert_eq!(*guard[1], 2); + }) + } + + #[test] + fn vec_data_ref() { + let mut key = ThreadKey::get().unwrap(); + let mutexes = vec![RwLock::new(1), RwLock::new(2)]; + let collection = LockCollection::new(mutexes); + collection.scoped_lock(&mut key, |guard| { + assert_eq!(*guard[0], 1); + assert_eq!(*guard[1], 2); + *guard[0] = 3; + }); + + collection.scoped_read(&mut key, |guard| { + assert_eq!(*guard[0], 3); + assert_eq!(*guard[1], 2); + }) + } + + #[test] fn box_get_ptrs_empty() { let locks: Box<[Mutex<()>]> = Box::from([]); let mut lock_ptrs = Vec::new(); @@ -757,4 +808,72 @@ mod tests { assert_eq!(*lock_ptrs[0], 1); assert_eq!(*lock_ptrs[1], 2); } + + #[test] + fn box_guard_mut() { + let key = ThreadKey::get().unwrap(); + let x = [Mutex::new(1), Mutex::new(2)]; + let collection: LockCollection<Box<[Mutex<_>]>> = LockCollection::new(Box::new(x)); + + let mut guard = collection.lock(key); + assert_eq!(*guard[0], 1); + assert_eq!(*guard[1], 2); + *guard[0] = 3; + + let key = LockCollection::<Box<[Mutex<_>]>>::unlock(guard); + let guard = collection.lock(key); + assert_eq!(*guard[0], 3); + assert_eq!(*guard[1], 2); + } + + #[test] + fn box_data_mut() { + let mut key = ThreadKey::get().unwrap(); + let mutexes = vec![Mutex::new(1), Mutex::new(2)].into_boxed_slice(); + let collection = LockCollection::new(mutexes); + collection.scoped_lock(&mut key, |guard| { + assert_eq!(*guard[0], 1); + assert_eq!(*guard[1], 2); + *guard[0] = 3; + }); + + collection.scoped_lock(&mut key, |guard| { + assert_eq!(*guard[0], 3); + assert_eq!(*guard[1], 2); + }); + } + + #[test] + fn box_guard_ref() { + let key = ThreadKey::get().unwrap(); + let locks = [RwLock::new(1), RwLock::new(2)]; + let collection: LockCollection<Box<[RwLock<_>]>> = LockCollection::new(Box::new(locks)); + + let mut guard = collection.lock(key); + assert_eq!(*guard[0], 1); + assert_eq!(*guard[1], 2); + *guard[0] = 3; + + let key = LockCollection::<Box<[RwLock<_>]>>::unlock(guard); + let guard = collection.read(key); + assert_eq!(*guard[0], 3); + assert_eq!(*guard[1], 2); + } + + #[test] + fn box_data_ref() { + let mut key = ThreadKey::get().unwrap(); + let mutexes = vec![RwLock::new(1), RwLock::new(2)].into_boxed_slice(); + let collection = LockCollection::new(mutexes); + collection.scoped_lock(&mut key, |guard| { + assert_eq!(*guard[0], 1); + assert_eq!(*guard[1], 2); + *guard[0] = 3; + }); + + collection.scoped_read(&mut key, |guard| { + assert_eq!(*guard[0], 3); + assert_eq!(*guard[1], 2); + }); + } } diff --git a/src/mutex.rs b/src/mutex.rs index 2022501..413bd8a 100644 --- a/src/mutex.rs +++ b/src/mutex.rs @@ -136,10 +136,7 @@ pub struct Mutex<T: ?Sized, R> { /// A reference to a mutex that unlocks it when dropped. /// /// This is similar to [`MutexGuard`], except it does not hold a [`Keyable`]. -pub struct MutexRef<'a, T: ?Sized + 'a, R: RawMutex>( - &'a Mutex<T, R>, - PhantomData<(&'a mut T, R::GuardMarker)>, -); +pub struct MutexRef<'a, T: ?Sized + 'a, R: RawMutex>(&'a Mutex<T, R>, PhantomData<R::GuardMarker>); /// An RAII implementation of a “scoped lock” of a mutex. /// @@ -183,6 +180,26 @@ mod tests { } #[test] + fn from_works() { + let key = ThreadKey::get().unwrap(); + let mutex: crate::Mutex<_> = Mutex::from("Hello, world!"); + + let guard = mutex.lock(key); + assert_eq!(*guard, "Hello, world!"); + } + + #[test] + fn as_mut_works() { + let key = ThreadKey::get().unwrap(); + let mut mutex = crate::Mutex::from(42); + + let mut_ref = mutex.as_mut(); + *mut_ref = 24; + + mutex.scoped_lock(key, |guard| assert_eq!(*guard, 24)) + } + + #[test] fn display_works_for_guard() { let key = ThreadKey::get().unwrap(); let mutex: crate::Mutex<_> = Mutex::new("Hello, world!"); diff --git a/src/mutex/guard.rs b/src/mutex/guard.rs index 22e59c1..d88fded 100644 --- a/src/mutex/guard.rs +++ b/src/mutex/guard.rs @@ -39,7 +39,7 @@ impl<T: ?Sized, R: RawMutex> Drop for MutexRef<'_, T, R> { fn drop(&mut self) { // safety: this guard is being destroyed, so the data cannot be // accessed without locking again - unsafe { self.0.raw_unlock() } + unsafe { self.0.raw_unlock_write() } } } @@ -79,7 +79,7 @@ impl<'a, T: ?Sized, R: RawMutex> MutexRef<'a, T, R> { /// Creates a reference to the underlying data of a mutex without /// attempting to lock it or take ownership of the key. But it's also quite /// dangerous to drop. - pub(crate) unsafe fn new(mutex: &'a Mutex<T, R>) -> Self { + pub(crate) const unsafe fn new(mutex: &'a Mutex<T, R>) -> Self { Self(mutex, PhantomData) } } @@ -139,7 +139,7 @@ impl<'a, T: ?Sized, R: RawMutex> MutexGuard<'a, T, R> { /// Create a guard to the given mutex. Undefined if multiple guards to the /// same mutex exist at once. #[must_use] - pub(super) unsafe fn new(mutex: &'a Mutex<T, R>, thread_key: ThreadKey) -> Self { + pub(super) const unsafe fn new(mutex: &'a Mutex<T, R>, thread_key: ThreadKey) -> Self { Self { mutex: MutexRef(mutex, PhantomData), thread_key, diff --git a/src/mutex/mutex.rs b/src/mutex/mutex.rs index 1d8ce8b..f0fb680 100644 --- a/src/mutex/mutex.rs +++ b/src/mutex/mutex.rs @@ -5,6 +5,7 @@ use std::panic::AssertUnwindSafe; use lock_api::RawMutex; +use crate::collection::utils; use crate::handle_unwind::handle_unwind; use crate::lockable::{Lockable, LockableGetMut, LockableIntoInner, OwnedLockable, RawLock}; use crate::poisonable::PoisonFlag; @@ -17,7 +18,7 @@ unsafe impl<T: ?Sized, R: RawMutex> RawLock for Mutex<T, R> { self.poison.poison(); } - unsafe fn raw_lock(&self) { + unsafe fn raw_write(&self) { assert!(!self.poison.is_poisoned(), "The mutex has been killed"); // if the closure unwraps, then the mutex will be killed @@ -25,7 +26,7 @@ unsafe impl<T: ?Sized, R: RawMutex> RawLock for Mutex<T, R> { handle_unwind(|| this.raw.lock(), || self.poison()) } - unsafe fn raw_try_lock(&self) -> bool { + unsafe fn raw_try_write(&self) -> bool { if self.poison.is_poisoned() { return false; } @@ -35,7 +36,7 @@ unsafe impl<T: ?Sized, R: RawMutex> RawLock for Mutex<T, R> { handle_unwind(|| this.raw.try_lock(), || self.poison()) } - unsafe fn raw_unlock(&self) { + unsafe fn raw_unlock_write(&self) { // if the closure unwraps, then the mutex will be killed let this = AssertUnwindSafe(self); handle_unwind(|| this.raw.unlock(), || self.poison()) @@ -43,20 +44,26 @@ unsafe impl<T: ?Sized, R: RawMutex> RawLock for Mutex<T, R> { // this is the closest thing to a read we can get, but Sharable isn't // implemented for this + #[mutants::skip] + #[cfg(not(tarpaulin_include))] unsafe fn raw_read(&self) { - self.raw_lock() + self.raw_write() } + #[mutants::skip] + #[cfg(not(tarpaulin_include))] unsafe fn raw_try_read(&self) -> bool { - self.raw_try_lock() + self.raw_try_write() } + #[mutants::skip] + #[cfg(not(tarpaulin_include))] unsafe fn raw_unlock_read(&self) { - self.raw_unlock() + self.raw_unlock_write() } } -unsafe impl<T: Send, R: RawMutex + Send + Sync> Lockable for Mutex<T, R> { +unsafe impl<T, R: RawMutex> Lockable for Mutex<T, R> { type Guard<'g> = MutexRef<'g, T, R> where @@ -80,7 +87,7 @@ unsafe impl<T: Send, R: RawMutex + Send + Sync> Lockable for Mutex<T, R> { } } -impl<T: Send, R: RawMutex + Send + Sync> LockableIntoInner for Mutex<T, R> { +impl<T: Send, R: RawMutex> LockableIntoInner for Mutex<T, R> { type Inner = T; fn into_inner(self) -> Self::Inner { @@ -88,7 +95,7 @@ impl<T: Send, R: RawMutex + Send + Sync> LockableIntoInner for Mutex<T, R> { } } -impl<T: Send, R: RawMutex + Send + Sync> LockableGetMut for Mutex<T, R> { +impl<T: Send, R: RawMutex> LockableGetMut for Mutex<T, R> { type Inner<'a> = &'a mut T where @@ -99,7 +106,7 @@ impl<T: Send, R: RawMutex + Send + Sync> LockableGetMut for Mutex<T, R> { } } -unsafe impl<T: Send, R: RawMutex + Send + Sync> OwnedLockable for Mutex<T, R> {} +unsafe impl<T: Send, R: RawMutex> OwnedLockable for Mutex<T, R> {} impl<T, R: RawMutex> Mutex<T, R> { /// Create a new unlocked `Mutex`. @@ -140,7 +147,7 @@ impl<T, R: RawMutex> Mutex<T, R> { #[mutants::skip] #[cfg(not(tarpaulin_include))] -impl<T: ?Sized + Debug, R: RawMutex> Debug for Mutex<T, R> { +impl<T: Debug, R: RawMutex> Debug for Mutex<T, R> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { // safety: this is just a try lock, and the value is dropped // immediately after, so there's no risk of blocking ourselves @@ -222,45 +229,21 @@ impl<T: ?Sized, R> Mutex<T, R> { } } -impl<T: ?Sized, R: RawMutex> Mutex<T, R> { - pub fn scoped_lock<Ret>(&self, key: impl Keyable, f: impl FnOnce(&mut T) -> Ret) -> Ret { - unsafe { - // safety: we have the thread key - self.raw_lock(); - - // safety: the mutex was just locked - let r = f(self.data.get().as_mut().unwrap_unchecked()); - - // safety: we locked the mutex already - self.raw_unlock(); - - drop(key); // ensures we drop the key in the correct place - - r - } +impl<T, R: RawMutex> Mutex<T, R> { + pub fn scoped_lock<'a, Ret>( + &'a self, + key: impl Keyable, + f: impl FnOnce(&'a mut T) -> Ret, + ) -> Ret { + utils::scoped_write(self, key, f) } - pub fn scoped_try_lock<Key: Keyable, Ret>( - &self, + pub fn scoped_try_lock<'a, Key: Keyable, Ret>( + &'a self, key: Key, - f: impl FnOnce(&mut T) -> Ret, + f: impl FnOnce(&'a mut T) -> Ret, ) -> Result<Ret, Key> { - unsafe { - // safety: we have the thread key - if !self.raw_try_lock() { - return Err(key); - } - - // safety: the mutex was just locked - let r = f(self.data.get().as_mut().unwrap_unchecked()); - - // safety: we locked the mutex already - self.raw_unlock(); - - drop(key); // ensures we drop the key in the correct place - - Ok(r) - } + utils::scoped_try_write(self, key, f) } /// Block the thread until this mutex can be locked, and lock it. @@ -289,7 +272,7 @@ impl<T: ?Sized, R: RawMutex> Mutex<T, R> { pub fn lock(&self, key: ThreadKey) -> MutexGuard<'_, T, R> { unsafe { // safety: we have the thread key - self.raw_lock(); + self.raw_write(); // safety: we just locked the mutex MutexGuard::new(self, key) @@ -332,7 +315,7 @@ impl<T: ?Sized, R: RawMutex> Mutex<T, R> { pub fn try_lock(&self, key: ThreadKey) -> Result<MutexGuard<'_, T, R>, ThreadKey> { unsafe { // safety: we have the key to the mutex - if self.raw_try_lock() { + if self.raw_try_write() { // safety: we just locked the mutex Ok(MutexGuard::new(self, key)) } else { @@ -350,7 +333,7 @@ impl<T: ?Sized, R: RawMutex> Mutex<T, R> { /// Lock without a [`ThreadKey`]. It is undefined behavior to do this without /// owning the [`ThreadKey`]. pub(crate) unsafe fn try_lock_no_key(&self) -> Option<MutexRef<'_, T, R>> { - self.raw_try_lock().then_some(MutexRef(self, PhantomData)) + self.raw_try_write().then_some(MutexRef(self, PhantomData)) } /// Consumes the [`MutexGuard`], and consequently unlocks its `Mutex`. @@ -370,9 +353,7 @@ impl<T: ?Sized, R: RawMutex> Mutex<T, R> { /// ``` #[must_use] pub fn unlock(guard: MutexGuard<'_, T, R>) -> ThreadKey { - unsafe { - guard.mutex.0.raw_unlock(); - } + drop(guard.mutex); guard.thread_key } } diff --git a/src/poisonable.rs b/src/poisonable.rs index 8d5a810..f9b0622 100644 --- a/src/poisonable.rs +++ b/src/poisonable.rs @@ -99,7 +99,7 @@ mod tests { use super::*; use crate::lockable::Lockable; - use crate::{LockCollection, Mutex, ThreadKey}; + use crate::{LockCollection, Mutex, RwLock, ThreadKey}; #[test] fn locking_poisoned_mutex_returns_error_in_collection() { @@ -127,6 +127,31 @@ mod tests { } #[test] + fn locking_poisoned_rwlock_returns_error_in_collection() { + let key = ThreadKey::get().unwrap(); + let mutex = LockCollection::new(Poisonable::new(RwLock::new(42))); + + std::thread::scope(|s| { + s.spawn(|| { + let key = ThreadKey::get().unwrap(); + let mut guard1 = mutex.read(key); + let guard = guard1.as_deref_mut().unwrap(); + assert_eq!(**guard, 42); + panic!(); + + #[allow(unreachable_code)] + drop(guard1); + }) + .join() + .unwrap_err(); + }); + + let error = mutex.read(key); + let error = error.as_deref().unwrap_err(); + assert_eq!(***error.get_ref(), 42); + } + + #[test] fn non_poisoned_get_mut_is_ok() { let mut mutex = Poisonable::new(Mutex::new(42)); let guard = mutex.get_mut(); @@ -201,6 +226,118 @@ mod tests { } #[test] + fn scoped_lock_can_poison() { + let key = ThreadKey::get().unwrap(); + let mutex = Poisonable::new(Mutex::new(42)); + + let r = std::panic::catch_unwind(|| { + mutex.scoped_lock(key, |num| { + *num.unwrap() = 56; + panic!(); + }) + }); + assert!(r.is_err()); + + let key = ThreadKey::get().unwrap(); + assert!(mutex.is_poisoned()); + mutex.scoped_lock(key, |num| { + let Err(error) = num else { panic!() }; + mutex.clear_poison(); + let guard = error.into_inner(); + assert_eq!(*guard, 56); + }); + assert!(!mutex.is_poisoned()); + } + + #[test] + fn scoped_try_lock_can_fail() { + let key = ThreadKey::get().unwrap(); + let mutex = Poisonable::new(Mutex::new(42)); + let guard = mutex.lock(key); + + std::thread::scope(|s| { + s.spawn(|| { + let key = ThreadKey::get().unwrap(); + let r = mutex.scoped_try_lock(key, |_| {}); + assert!(r.is_err()); + }); + }); + + drop(guard); + } + + #[test] + fn scoped_try_lock_can_succeed() { + let rwlock = Poisonable::new(RwLock::new(42)); + + std::thread::scope(|s| { + s.spawn(|| { + let key = ThreadKey::get().unwrap(); + let r = rwlock.scoped_try_lock(key, |guard| { + assert_eq!(*guard.unwrap(), 42); + }); + assert!(r.is_ok()); + }); + }); + } + + #[test] + fn scoped_read_can_poison() { + let key = ThreadKey::get().unwrap(); + let mutex = Poisonable::new(RwLock::new(42)); + + let r = std::panic::catch_unwind(|| { + mutex.scoped_read(key, |num| { + assert_eq!(*num.unwrap(), 42); + panic!(); + }) + }); + assert!(r.is_err()); + + let key = ThreadKey::get().unwrap(); + assert!(mutex.is_poisoned()); + mutex.scoped_read(key, |num| { + let Err(error) = num else { panic!() }; + mutex.clear_poison(); + let guard = error.into_inner(); + assert_eq!(*guard, 42); + }); + assert!(!mutex.is_poisoned()); + } + + #[test] + fn scoped_try_read_can_fail() { + let key = ThreadKey::get().unwrap(); + let rwlock = Poisonable::new(RwLock::new(42)); + let guard = rwlock.lock(key); + + std::thread::scope(|s| { + s.spawn(|| { + let key = ThreadKey::get().unwrap(); + let r = rwlock.scoped_try_read(key, |_| {}); + assert!(r.is_err()); + }); + }); + + drop(guard); + } + + #[test] + fn scoped_try_read_can_succeed() { + let rwlock = Poisonable::new(RwLock::new(42)); + + std::thread::scope(|s| { + s.spawn(|| { + let key = ThreadKey::get().unwrap(); + let r = rwlock.scoped_try_read(key, |guard| { + assert_eq!(*guard.unwrap(), 42); + }); + assert!(r.is_ok()); + }); + }); + } + + #[test] fn display_works() { let key = ThreadKey::get().unwrap(); let mutex = Poisonable::new(Mutex::new("Hello, world!")); @@ -318,6 +455,31 @@ mod tests { } #[test] + fn clear_poison_for_poisoned_rwlock() { + let lock = Arc::new(Poisonable::new(RwLock::new(0))); + let c_mutex = Arc::clone(&lock); + + let _ = std::thread::spawn(move || { + let key = ThreadKey::get().unwrap(); + let lock = c_mutex.read(key).unwrap(); + assert_eq!(*lock, 42); + panic!(); // the mutex gets poisoned + }) + .join(); + + assert!(lock.is_poisoned()); + + let key = ThreadKey::get().unwrap(); + let _ = lock.lock(key).unwrap_or_else(|mut e| { + **e.get_mut() = 1; + lock.clear_poison(); + e.into_inner() + }); + + assert!(!lock.is_poisoned()); + } + + #[test] fn error_as_ref() { let mutex = Poisonable::new(Mutex::new("foo")); diff --git a/src/poisonable/poisonable.rs b/src/poisonable/poisonable.rs index efe4ed0..ff78330 100644 --- a/src/poisonable/poisonable.rs +++ b/src/poisonable/poisonable.rs @@ -1,5 +1,6 @@ use std::panic::{RefUnwindSafe, UnwindSafe}; +use crate::handle_unwind::handle_unwind; use crate::lockable::{ Lockable, LockableGetMut, LockableIntoInner, OwnedLockable, RawLock, Sharable, }; @@ -17,16 +18,16 @@ unsafe impl<L: Lockable + RawLock> RawLock for Poisonable<L> { self.inner.poison() } - unsafe fn raw_lock(&self) { - self.inner.raw_lock() + unsafe fn raw_write(&self) { + self.inner.raw_write() } - unsafe fn raw_try_lock(&self) -> bool { - self.inner.raw_try_lock() + unsafe fn raw_try_write(&self) -> bool { + self.inner.raw_try_write() } - unsafe fn raw_unlock(&self) { - self.inner.raw_unlock() + unsafe fn raw_unlock_write(&self) { + self.inner.raw_unlock_write() } unsafe fn raw_read(&self) { @@ -313,13 +314,19 @@ impl<L: Lockable + RawLock> Poisonable<L> { ) -> R { unsafe { // safety: we have the thread key - self.raw_lock(); + self.raw_write(); // safety: the data was just locked - let r = f(self.data_mut()); + let r = handle_unwind( + || f(self.data_mut()), + || { + self.poisoned.poison(); + self.raw_unlock_write(); + }, + ); // safety: the collection is still locked - self.raw_unlock(); + self.raw_unlock_write(); drop(key); // ensure the key stays alive long enough @@ -334,15 +341,21 @@ impl<L: Lockable + RawLock> Poisonable<L> { ) -> Result<R, Key> { unsafe { // safety: we have the thread key - if !self.raw_try_lock() { + if !self.raw_try_write() { return Err(key); } // safety: we just locked the collection - let r = f(self.data_mut()); + let r = handle_unwind( + || f(self.data_mut()), + || { + self.poisoned.poison(); + self.raw_unlock_write(); + }, + ); // safety: the collection is still locked - self.raw_unlock(); + self.raw_unlock_write(); drop(key); // ensures the key stays valid long enough @@ -383,7 +396,7 @@ impl<L: Lockable + RawLock> Poisonable<L> { /// ``` pub fn lock(&self, key: ThreadKey) -> PoisonResult<PoisonGuard<'_, L::Guard<'_>>> { unsafe { - self.inner.raw_lock(); + self.inner.raw_write(); self.guard(key) } } @@ -434,7 +447,7 @@ impl<L: Lockable + RawLock> Poisonable<L> { /// [`WouldBlock`]: `TryLockPoisonableError::WouldBlock` pub fn try_lock(&self, key: ThreadKey) -> TryLockPoisonableResult<'_, L::Guard<'_>> { unsafe { - if self.inner.raw_try_lock() { + if self.inner.raw_try_write() { Ok(self.guard(key)?) } else { Err(TryLockPoisonableError::WouldBlock(key)) @@ -487,7 +500,13 @@ impl<L: Sharable + RawLock> Poisonable<L> { self.raw_read(); // safety: the data was just locked - let r = f(self.data_ref()); + let r = handle_unwind( + || f(self.data_ref()), + || { + self.poisoned.poison(); + self.raw_unlock_read(); + }, + ); // safety: the collection is still locked self.raw_unlock_read(); @@ -510,7 +529,13 @@ impl<L: Sharable + RawLock> Poisonable<L> { } // safety: we just locked the collection - let r = f(self.data_ref()); + let r = handle_unwind( + || f(self.data_ref()), + || { + self.poisoned.poison(); + self.raw_unlock_read(); + }, + ); // safety: the collection is still locked self.raw_unlock_read(); diff --git a/src/rwlock.rs b/src/rwlock.rs index b604370..2d3dd85 100644 --- a/src/rwlock.rs +++ b/src/rwlock.rs @@ -75,7 +75,7 @@ pub struct WriteLock<'l, T: ?Sized, R>(&'l RwLock<T, R>); /// [`Keyable`]. pub struct RwLockReadRef<'a, T: ?Sized, R: RawRwLock>( &'a RwLock<T, R>, - PhantomData<(&'a mut T, R::GuardMarker)>, + PhantomData<R::GuardMarker>, ); /// RAII structure that unlocks the exclusive write access to a [`RwLock`] @@ -84,7 +84,7 @@ pub struct RwLockReadRef<'a, T: ?Sized, R: RawRwLock>( /// [`Keyable`]. pub struct RwLockWriteRef<'a, T: ?Sized, R: RawRwLock>( &'a RwLock<T, R>, - PhantomData<(&'a mut T, R::GuardMarker)>, + PhantomData<R::GuardMarker>, ); /// RAII structure used to release the shared read access of a lock when @@ -115,6 +115,8 @@ pub struct RwLockWriteGuard<'a, T: ?Sized, R: RawRwLock> { #[cfg(test)] mod tests { use crate::lockable::Lockable; + use crate::lockable::RawLock; + use crate::LockCollection; use crate::RwLock; use crate::ThreadKey; @@ -149,6 +151,33 @@ mod tests { } #[test] + fn read_lock_scoped_works() { + let mut key = ThreadKey::get().unwrap(); + let lock: crate::RwLock<_> = RwLock::new(42); + let reader = ReadLock::new(&lock); + + reader.scoped_lock(&mut key, |num| assert_eq!(*num, 42)); + } + + #[test] + fn read_lock_scoped_try_fails_during_write() { + let key = ThreadKey::get().unwrap(); + let lock: crate::RwLock<_> = RwLock::new(42); + let reader = ReadLock::new(&lock); + let guard = lock.write(key); + + std::thread::scope(|s| { + s.spawn(|| { + let key = ThreadKey::get().unwrap(); + let r = reader.scoped_try_lock(key, |_| {}); + assert!(r.is_err()); + }); + }); + + drop(guard); + } + + #[test] fn write_lock_unlocked_when_initialized() { let key = ThreadKey::get().unwrap(); let lock: crate::RwLock<_> = RwLock::new("Hello, world!"); @@ -165,7 +194,7 @@ mod tests { readlock.get_ptrs(&mut lock_ptrs); assert_eq!(lock_ptrs.len(), 1); - assert!(std::ptr::addr_eq(lock_ptrs[0], &rwlock)); + assert!(std::ptr::addr_eq(lock_ptrs[0], &readlock)); } #[test] @@ -176,7 +205,34 @@ mod tests { writelock.get_ptrs(&mut lock_ptrs); assert_eq!(lock_ptrs.len(), 1); - assert!(std::ptr::addr_eq(lock_ptrs[0], &rwlock)); + assert!(std::ptr::addr_eq(lock_ptrs[0], &writelock)); + } + + #[test] + fn write_lock_scoped_works() { + let mut key = ThreadKey::get().unwrap(); + let lock: crate::RwLock<_> = RwLock::new(42); + let writer = WriteLock::new(&lock); + + writer.scoped_lock(&mut key, |num| assert_eq!(*num, 42)); + } + + #[test] + fn write_lock_scoped_try_fails_during_write() { + let key = ThreadKey::get().unwrap(); + let lock: crate::RwLock<_> = RwLock::new(42); + let writer = WriteLock::new(&lock); + let guard = lock.write(key); + + std::thread::scope(|s| { + s.spawn(|| { + let key = ThreadKey::get().unwrap(); + let r = writer.scoped_try_lock(key, |_| {}); + assert!(r.is_err()); + }); + }); + + drop(guard); } #[test] @@ -226,6 +282,69 @@ mod tests { } #[test] + fn locked_after_scoped_write() { + let mut key = ThreadKey::get().unwrap(); + let lock = crate::RwLock::new("Hello, world!"); + + lock.scoped_write(&mut key, |guard| { + assert!(lock.is_locked()); + assert_eq!(*guard, "Hello, world!"); + + std::thread::scope(|s| { + s.spawn(|| { + let key = ThreadKey::get().unwrap(); + assert!(lock.try_read(key).is_err()); + }); + }) + }) + } + + #[test] + fn get_mut_works() { + let key = ThreadKey::get().unwrap(); + let mut lock = crate::RwLock::from(42); + + let mut_ref = lock.get_mut(); + *mut_ref = 24; + + lock.scoped_read(key, |guard| assert_eq!(*guard, 24)) + } + + #[test] + fn try_write_can_fail() { + let key = ThreadKey::get().unwrap(); + let lock = crate::RwLock::new("Hello"); + let guard = lock.write(key); + + std::thread::scope(|s| { + s.spawn(|| { + let key = ThreadKey::get().unwrap(); + let r = lock.try_write(key); + assert!(r.is_err()); + }); + }); + + drop(guard); + } + + #[test] + fn try_read_can_fail() { + let key = ThreadKey::get().unwrap(); + let lock = crate::RwLock::new("Hello"); + let guard = lock.write(key); + + std::thread::scope(|s| { + s.spawn(|| { + let key = ThreadKey::get().unwrap(); + let r = lock.try_read(key); + assert!(r.is_err()); + }); + }); + + drop(guard); + } + + #[test] fn read_display_works() { let key = ThreadKey::get().unwrap(); let lock: crate::RwLock<_> = RwLock::new("Hello, world!"); @@ -275,4 +394,200 @@ mod tests { assert!(!lock.is_locked()); } + + #[test] + fn unlock_write() { + let key = ThreadKey::get().unwrap(); + let lock = crate::RwLock::new("Hello, world"); + + let mut guard = lock.write(key); + *guard = "Goodbye, world!"; + let key = RwLock::unlock_write(guard); + + let guard = lock.read(key); + assert_eq!(*guard, "Goodbye, world!"); + } + + #[test] + fn unlock_read() { + let key = ThreadKey::get().unwrap(); + let lock = crate::RwLock::new("Hello, world"); + + let guard = lock.read(key); + assert_eq!(*guard, "Hello, world"); + let key = RwLock::unlock_read(guard); + + let guard = lock.write(key); + assert_eq!(*guard, "Hello, world"); + } + + #[test] + fn unlock_read_lock() { + let key = ThreadKey::get().unwrap(); + let lock = crate::RwLock::new("Hello, world"); + let reader = ReadLock::new(&lock); + + let guard = reader.lock(key); + let key = ReadLock::unlock(guard); + + lock.write(key); + } + + #[test] + fn unlock_write_lock() { + let key = ThreadKey::get().unwrap(); + let lock = crate::RwLock::new("Hello, world"); + let writer = WriteLock::from(&lock); + + let guard = writer.lock(key); + let key = WriteLock::unlock(guard); + + lock.write(key); + } + + #[test] + fn read_lock_in_collection() { + let mut key = ThreadKey::get().unwrap(); + let lock = crate::RwLock::new("hi"); + let collection = LockCollection::try_new(ReadLock::new(&lock)).unwrap(); + + collection.scoped_lock(&mut key, |guard| { + assert_eq!(*guard, "hi"); + }); + collection.scoped_read(&mut key, |guard| { + assert_eq!(*guard, "hi"); + }); + assert!(collection + .scoped_try_lock(&mut key, |guard| { + assert_eq!(*guard, "hi"); + }) + .is_ok()); + assert!(collection + .scoped_try_read(&mut key, |guard| { + assert_eq!(*guard, "hi"); + }) + .is_ok()); + + let guard = collection.lock(key); + assert_eq!(**guard, "hi"); + + let key = LockCollection::<ReadLock<_, _>>::unlock(guard); + let guard = collection.read(key); + assert_eq!(**guard, "hi"); + + let key = LockCollection::<ReadLock<_, _>>::unlock(guard); + let guard = lock.write(key); + + std::thread::scope(|s| { + s.spawn(|| { + let key = ThreadKey::get().unwrap(); + let guard = collection.try_lock(key); + assert!(guard.is_err()); + }); + s.spawn(|| { + let key = ThreadKey::get().unwrap(); + let guard = collection.try_read(key); + assert!(guard.is_err()); + }); + }); + + drop(guard); + } + + #[test] + fn write_lock_in_collection() { + let mut key = ThreadKey::get().unwrap(); + let lock = crate::RwLock::new("hi"); + let collection = LockCollection::try_new(WriteLock::new(&lock)).unwrap(); + + collection.scoped_lock(&mut key, |guard| { + assert_eq!(*guard, "hi"); + }); + assert!(collection + .scoped_try_lock(&mut key, |guard| { + assert_eq!(*guard, "hi"); + }) + .is_ok()); + + let guard = collection.lock(key); + assert_eq!(**guard, "hi"); + + let key = LockCollection::<WriteLock<_, _>>::unlock(guard); + let guard = lock.write(key); + + std::thread::scope(|s| { + s.spawn(|| { + let key = ThreadKey::get().unwrap(); + let guard = collection.try_lock(key); + assert!(guard.is_err()); + }); + }); + + drop(guard); + } + + #[test] + fn read_ref_as_ref() { + let key = ThreadKey::get().unwrap(); + let lock = LockCollection::new(crate::RwLock::new("hi")); + let guard = lock.read(key); + + assert_eq!(*(*guard).as_ref(), "hi"); + } + + #[test] + fn read_guard_as_ref() { + let key = ThreadKey::get().unwrap(); + let lock = crate::RwLock::new("hi"); + let guard = lock.read(key); + + assert_eq!(*guard.as_ref(), "hi"); + } + + #[test] + fn write_ref_as_ref() { + let key = ThreadKey::get().unwrap(); + let lock = LockCollection::new(crate::RwLock::new("hi")); + let guard = lock.lock(key); + + assert_eq!(*(*guard).as_ref(), "hi"); + } + + #[test] + fn write_guard_as_ref() { + let key = ThreadKey::get().unwrap(); + let lock = crate::RwLock::new("hi"); + let guard = lock.write(key); + + assert_eq!(*guard.as_ref(), "hi"); + } + + #[test] + fn write_guard_as_mut() { + let key = ThreadKey::get().unwrap(); + let lock = crate::RwLock::new("hi"); + let mut guard = lock.write(key); + + assert_eq!(*guard.as_mut(), "hi"); + *guard.as_mut() = "foo"; + assert_eq!(*guard.as_mut(), "foo"); + } + + #[test] + fn poison_read_lock() { + let lock = crate::RwLock::new("hi"); + let reader = ReadLock::new(&lock); + + reader.poison(); + assert!(lock.poison.is_poisoned()); + } + + #[test] + fn poison_write_lock() { + let lock = crate::RwLock::new("hi"); + let reader = WriteLock::new(&lock); + + reader.poison(); + assert!(lock.poison.is_poisoned()); + } } diff --git a/src/rwlock/read_guard.rs b/src/rwlock/read_guard.rs index 0d68c75..5b26c06 100644 --- a/src/rwlock/read_guard.rs +++ b/src/rwlock/read_guard.rs @@ -64,7 +64,7 @@ impl<'a, T: ?Sized, R: RawRwLock> RwLockReadRef<'a, T, R> { /// Creates an immutable reference for the underlying data of an [`RwLock`] /// without locking it or taking ownership of the key. #[must_use] - pub(crate) unsafe fn new(mutex: &'a RwLock<T, R>) -> Self { + pub(crate) const unsafe fn new(mutex: &'a RwLock<T, R>) -> Self { Self(mutex, PhantomData) } } @@ -109,7 +109,7 @@ impl<'a, T: ?Sized, R: RawRwLock> RwLockReadGuard<'a, T, R> { /// Create a guard to the given mutex. Undefined if multiple guards to the /// same mutex exist at once. #[must_use] - pub(super) unsafe fn new(rwlock: &'a RwLock<T, R>, thread_key: ThreadKey) -> Self { + pub(super) const unsafe fn new(rwlock: &'a RwLock<T, R>, thread_key: ThreadKey) -> Self { Self { rwlock: RwLockReadRef(rwlock, PhantomData), thread_key, diff --git a/src/rwlock/read_lock.rs b/src/rwlock/read_lock.rs index 05b184a..dd9e42f 100644 --- a/src/rwlock/read_lock.rs +++ b/src/rwlock/read_lock.rs @@ -3,11 +3,41 @@ use std::fmt::Debug; use lock_api::RawRwLock; use crate::lockable::{Lockable, RawLock, Sharable}; -use crate::ThreadKey; +use crate::{Keyable, ThreadKey}; use super::{ReadLock, RwLock, RwLockReadGuard, RwLockReadRef}; -unsafe impl<T: Send, R: RawRwLock + Send + Sync> Lockable for ReadLock<'_, T, R> { +unsafe impl<T, R: RawRwLock> RawLock for ReadLock<'_, T, R> { + fn poison(&self) { + self.0.poison() + } + + unsafe fn raw_write(&self) { + self.0.raw_read() + } + + unsafe fn raw_try_write(&self) -> bool { + self.0.raw_try_read() + } + + unsafe fn raw_unlock_write(&self) { + self.0.raw_unlock_read() + } + + unsafe fn raw_read(&self) { + self.0.raw_read() + } + + unsafe fn raw_try_read(&self) -> bool { + self.0.raw_try_read() + } + + unsafe fn raw_unlock_read(&self) { + self.0.raw_unlock_read() + } +} + +unsafe impl<T, R: RawRwLock> Lockable for ReadLock<'_, T, R> { type Guard<'g> = RwLockReadRef<'g, T, R> where @@ -19,7 +49,7 @@ unsafe impl<T: Send, R: RawRwLock + Send + Sync> Lockable for ReadLock<'_, T, R> Self: 'a; fn get_ptrs<'a>(&'a self, ptrs: &mut Vec<&'a dyn RawLock>) { - ptrs.push(self.as_ref()); + ptrs.push(self); } unsafe fn guard(&self) -> Self::Guard<'_> { @@ -31,7 +61,7 @@ unsafe impl<T: Send, R: RawRwLock + Send + Sync> Lockable for ReadLock<'_, T, R> } } -unsafe impl<T: Send, R: RawRwLock + Send + Sync> Sharable for ReadLock<'_, T, R> { +unsafe impl<T, R: RawRwLock> Sharable for ReadLock<'_, T, R> { type ReadGuard<'g> = RwLockReadRef<'g, T, R> where @@ -53,7 +83,7 @@ unsafe impl<T: Send, R: RawRwLock + Send + Sync> Sharable for ReadLock<'_, T, R> #[mutants::skip] #[cfg(not(tarpaulin_include))] -impl<T: ?Sized + Debug, R: RawRwLock> Debug for ReadLock<'_, T, R> { +impl<T: Debug, R: RawRwLock> Debug for ReadLock<'_, T, R> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { // safety: this is just a try lock, and the value is dropped // immediately after, so there's no risk of blocking ourselves @@ -104,7 +134,19 @@ impl<'l, T, R> ReadLock<'l, T, R> { } } -impl<T: ?Sized, R: RawRwLock> ReadLock<'_, T, R> { +impl<T, R: RawRwLock> ReadLock<'_, T, R> { + pub fn scoped_lock<'a, Ret>(&'a self, key: impl Keyable, f: impl Fn(&'a T) -> Ret) -> Ret { + self.0.scoped_read(key, f) + } + + pub fn scoped_try_lock<'a, Key: Keyable, Ret>( + &'a self, + key: Key, + f: impl Fn(&'a T) -> Ret, + ) -> Result<Ret, Key> { + self.0.scoped_try_read(key, f) + } + /// Locks the underlying [`RwLock`] with shared read access, blocking the /// current thread until it can be acquired. /// diff --git a/src/rwlock/rwlock.rs b/src/rwlock/rwlock.rs index 905ecf8..5f407d1 100644 --- a/src/rwlock/rwlock.rs +++ b/src/rwlock/rwlock.rs @@ -5,6 +5,7 @@ use std::panic::AssertUnwindSafe; use lock_api::RawRwLock; +use crate::collection::utils; use crate::handle_unwind::handle_unwind; use crate::lockable::{ Lockable, LockableGetMut, LockableIntoInner, OwnedLockable, RawLock, Sharable, @@ -18,7 +19,7 @@ unsafe impl<T: ?Sized, R: RawRwLock> RawLock for RwLock<T, R> { self.poison.poison(); } - unsafe fn raw_lock(&self) { + unsafe fn raw_write(&self) { assert!( !self.poison.is_poisoned(), "The read-write lock has been killed" @@ -29,7 +30,7 @@ unsafe impl<T: ?Sized, R: RawRwLock> RawLock for RwLock<T, R> { handle_unwind(|| this.raw.lock_exclusive(), || self.poison()) } - unsafe fn raw_try_lock(&self) -> bool { + unsafe fn raw_try_write(&self) -> bool { if self.poison.is_poisoned() { return false; } @@ -39,7 +40,7 @@ unsafe impl<T: ?Sized, R: RawRwLock> RawLock for RwLock<T, R> { handle_unwind(|| this.raw.try_lock_exclusive(), || self.poison()) } - unsafe fn raw_unlock(&self) { + unsafe fn raw_unlock_write(&self) { // if the closure unwraps, then the mutex will be killed let this = AssertUnwindSafe(self); handle_unwind(|| this.raw.unlock_exclusive(), || self.poison()) @@ -73,7 +74,7 @@ unsafe impl<T: ?Sized, R: RawRwLock> RawLock for RwLock<T, R> { } } -unsafe impl<T: Send, R: RawRwLock + Send + Sync> Lockable for RwLock<T, R> { +unsafe impl<T, R: RawRwLock> Lockable for RwLock<T, R> { type Guard<'g> = RwLockWriteRef<'g, T, R> where @@ -97,7 +98,7 @@ unsafe impl<T: Send, R: RawRwLock + Send + Sync> Lockable for RwLock<T, R> { } } -unsafe impl<T: Send, R: RawRwLock + Send + Sync> Sharable for RwLock<T, R> { +unsafe impl<T, R: RawRwLock> Sharable for RwLock<T, R> { type ReadGuard<'g> = RwLockReadRef<'g, T, R> where @@ -117,9 +118,9 @@ unsafe impl<T: Send, R: RawRwLock + Send + Sync> Sharable for RwLock<T, R> { } } -unsafe impl<T: Send, R: RawRwLock + Send + Sync> OwnedLockable for RwLock<T, R> {} +unsafe impl<T: Send, R: RawRwLock> OwnedLockable for RwLock<T, R> {} -impl<T: Send, R: RawRwLock + Send + Sync> LockableIntoInner for RwLock<T, R> { +impl<T: Send, R: RawRwLock> LockableIntoInner for RwLock<T, R> { type Inner = T; fn into_inner(self) -> Self::Inner { @@ -127,7 +128,7 @@ impl<T: Send, R: RawRwLock + Send + Sync> LockableIntoInner for RwLock<T, R> { } } -impl<T: Send, R: RawRwLock + Send + Sync> LockableGetMut for RwLock<T, R> { +impl<T: Send, R: RawRwLock> LockableGetMut for RwLock<T, R> { type Inner<'a> = &'a mut T where @@ -160,7 +161,7 @@ impl<T, R: RawRwLock> RwLock<T, R> { #[mutants::skip] #[cfg(not(tarpaulin_include))] -impl<T: ?Sized + Debug, R: RawRwLock> Debug for RwLock<T, R> { +impl<T: Debug, R: RawRwLock> Debug for RwLock<T, R> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { // safety: this is just a try lock, and the value is dropped // immediately after, so there's no risk of blocking ourselves @@ -247,85 +248,29 @@ impl<T: ?Sized, R> RwLock<T, R> { } } -impl<T: ?Sized, R: RawRwLock> RwLock<T, R> { - pub fn scoped_read<Ret>(&self, key: impl Keyable, f: impl Fn(&T) -> Ret) -> Ret { - unsafe { - // safety: we have the thread key - self.raw_read(); - - // safety: the rwlock was just locked - let r = f(self.data.get().as_ref().unwrap_unchecked()); - - // safety: the rwlock is already locked - self.raw_unlock_read(); - - drop(key); // ensure the key stays valid for long enough - - r - } +impl<T, R: RawRwLock> RwLock<T, R> { + pub fn scoped_read<'a, Ret>(&'a self, key: impl Keyable, f: impl Fn(&'a T) -> Ret) -> Ret { + utils::scoped_read(self, key, f) } - pub fn scoped_try_read<Key: Keyable, Ret>( - &self, + pub fn scoped_try_read<'a, Key: Keyable, Ret>( + &'a self, key: Key, - f: impl Fn(&T) -> Ret, + f: impl Fn(&'a T) -> Ret, ) -> Result<Ret, Key> { - unsafe { - // safety: we have the thread key - if !self.raw_try_read() { - return Err(key); - } - - // safety: the rwlock was just locked - let r = f(self.data.get().as_ref().unwrap_unchecked()); - - // safety: the rwlock is already locked - self.raw_unlock_read(); - - drop(key); // ensure the key stays valid for long enough - - Ok(r) - } + utils::scoped_try_read(self, key, f) } - pub fn scoped_write<Ret>(&self, key: impl Keyable, f: impl Fn(&mut T) -> Ret) -> Ret { - unsafe { - // safety: we have the thread key - self.raw_lock(); - - // safety: we just locked the rwlock - let r = f(self.data.get().as_mut().unwrap_unchecked()); - - // safety: the rwlock is already locked - self.raw_unlock(); - - drop(key); // ensure the key stays valid for long enough - - r - } + pub fn scoped_write<'a, Ret>(&'a self, key: impl Keyable, f: impl Fn(&'a mut T) -> Ret) -> Ret { + utils::scoped_write(self, key, f) } - pub fn scoped_try_write<Key: Keyable, Ret>( - &self, + pub fn scoped_try_write<'a, Key: Keyable, Ret>( + &'a self, key: Key, - f: impl Fn(&mut T) -> Ret, + f: impl Fn(&'a mut T) -> Ret, ) -> Result<Ret, Key> { - unsafe { - // safety: we have the thread key - if !self.raw_try_lock() { - return Err(key); - } - - // safety: the rwlock was just locked - let r = f(self.data.get().as_mut().unwrap_unchecked()); - - // safety: the rwlock is already locked - self.raw_unlock(); - - drop(key); // ensure the key stays valid for long enough - - Ok(r) - } + utils::scoped_try_write(self, key, f) } /// Locks this `RwLock` with shared read access, blocking the current @@ -426,7 +371,7 @@ impl<T: ?Sized, R: RawRwLock> RwLock<T, R> { /// without exclusive access to the key is undefined behavior. #[cfg(test)] pub(crate) unsafe fn try_write_no_key(&self) -> Option<RwLockWriteRef<'_, T, R>> { - if self.raw_try_lock() { + if self.raw_try_write() { // safety: the lock is locked first Some(RwLockWriteRef(self, PhantomData)) } else { @@ -463,7 +408,7 @@ impl<T: ?Sized, R: RawRwLock> RwLock<T, R> { /// [`ThreadKey`]: `crate::ThreadKey` pub fn write(&self, key: ThreadKey) -> RwLockWriteGuard<'_, T, R> { unsafe { - self.raw_lock(); + self.raw_write(); // safety: the lock is locked first RwLockWriteGuard::new(self, key) @@ -498,7 +443,7 @@ impl<T: ?Sized, R: RawRwLock> RwLock<T, R> { /// ``` pub fn try_write(&self, key: ThreadKey) -> Result<RwLockWriteGuard<'_, T, R>, ThreadKey> { unsafe { - if self.raw_try_lock() { + if self.raw_try_write() { // safety: the lock is locked first Ok(RwLockWriteGuard::new(self, key)) } else { @@ -533,9 +478,7 @@ impl<T: ?Sized, R: RawRwLock> RwLock<T, R> { /// ``` #[must_use] pub fn unlock_read(guard: RwLockReadGuard<'_, T, R>) -> ThreadKey { - unsafe { - guard.rwlock.0.raw_unlock_read(); - } + drop(guard.rwlock); guard.thread_key } @@ -560,9 +503,7 @@ impl<T: ?Sized, R: RawRwLock> RwLock<T, R> { /// ``` #[must_use] pub fn unlock_write(guard: RwLockWriteGuard<'_, T, R>) -> ThreadKey { - unsafe { - guard.rwlock.0.raw_unlock(); - } + drop(guard.rwlock); guard.thread_key } } diff --git a/src/rwlock/write_guard.rs b/src/rwlock/write_guard.rs index 3fabf8e..c7676b5 100644 --- a/src/rwlock/write_guard.rs +++ b/src/rwlock/write_guard.rs @@ -71,7 +71,7 @@ impl<T: ?Sized, R: RawRwLock> Drop for RwLockWriteRef<'_, T, R> { fn drop(&mut self) { // safety: this guard is being destroyed, so the data cannot be // accessed without locking again - unsafe { self.0.raw_unlock() } + unsafe { self.0.raw_unlock_write() } } } @@ -79,7 +79,7 @@ impl<'a, T: ?Sized + 'a, R: RawRwLock> RwLockWriteRef<'a, T, R> { /// Creates a reference to the underlying data of an [`RwLock`] without /// locking or taking ownership of the key. #[must_use] - pub(crate) unsafe fn new(mutex: &'a RwLock<T, R>) -> Self { + pub(crate) const unsafe fn new(mutex: &'a RwLock<T, R>) -> Self { Self(mutex, PhantomData) } } @@ -136,7 +136,7 @@ impl<'a, T: ?Sized + 'a, R: RawRwLock> RwLockWriteGuard<'a, T, R> { /// Create a guard to the given mutex. Undefined if multiple guards to the /// same mutex exist at once. #[must_use] - pub(super) unsafe fn new(rwlock: &'a RwLock<T, R>, thread_key: ThreadKey) -> Self { + pub(super) const unsafe fn new(rwlock: &'a RwLock<T, R>, thread_key: ThreadKey) -> Self { Self { rwlock: RwLockWriteRef(rwlock, PhantomData), thread_key, diff --git a/src/rwlock/write_lock.rs b/src/rwlock/write_lock.rs index 8a44a2d..5ae4dda 100644 --- a/src/rwlock/write_lock.rs +++ b/src/rwlock/write_lock.rs @@ -3,11 +3,41 @@ use std::fmt::Debug; use lock_api::RawRwLock; use crate::lockable::{Lockable, RawLock}; -use crate::ThreadKey; +use crate::{Keyable, ThreadKey}; use super::{RwLock, RwLockWriteGuard, RwLockWriteRef, WriteLock}; -unsafe impl<T: Send, R: RawRwLock + Send + Sync> Lockable for WriteLock<'_, T, R> { +unsafe impl<T, R: RawRwLock> RawLock for WriteLock<'_, T, R> { + fn poison(&self) { + self.0.poison() + } + + unsafe fn raw_write(&self) { + self.0.raw_write() + } + + unsafe fn raw_try_write(&self) -> bool { + self.0.raw_try_write() + } + + unsafe fn raw_unlock_write(&self) { + self.0.raw_unlock_write() + } + + unsafe fn raw_read(&self) { + self.0.raw_write() + } + + unsafe fn raw_try_read(&self) -> bool { + self.0.raw_try_write() + } + + unsafe fn raw_unlock_read(&self) { + self.0.raw_unlock_write() + } +} + +unsafe impl<T, R: RawRwLock> Lockable for WriteLock<'_, T, R> { type Guard<'g> = RwLockWriteRef<'g, T, R> where @@ -19,7 +49,7 @@ unsafe impl<T: Send, R: RawRwLock + Send + Sync> Lockable for WriteLock<'_, T, R Self: 'a; fn get_ptrs<'a>(&'a self, ptrs: &mut Vec<&'a dyn RawLock>) { - ptrs.push(self.as_ref()); + ptrs.push(self) } unsafe fn guard(&self) -> Self::Guard<'_> { @@ -36,7 +66,7 @@ unsafe impl<T: Send, R: RawRwLock + Send + Sync> Lockable for WriteLock<'_, T, R #[mutants::skip] #[cfg(not(tarpaulin_include))] -impl<T: ?Sized + Debug, R: RawRwLock> Debug for WriteLock<'_, T, R> { +impl<T: Debug, R: RawRwLock> Debug for WriteLock<'_, T, R> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { // safety: this is just a try lock, and the value is dropped // immediately after, so there's no risk of blocking ourselves @@ -89,7 +119,19 @@ impl<'l, T, R> WriteLock<'l, T, R> { } } -impl<T: ?Sized, R: RawRwLock> WriteLock<'_, T, R> { +impl<T, R: RawRwLock> WriteLock<'_, T, R> { + pub fn scoped_lock<'a, Ret>(&'a self, key: impl Keyable, f: impl Fn(&'a mut T) -> Ret) -> Ret { + self.0.scoped_write(key, f) + } + + pub fn scoped_try_lock<'a, Key: Keyable, Ret>( + &'a self, + key: Key, + f: impl Fn(&'a mut T) -> Ret, + ) -> Result<Ret, Key> { + self.0.scoped_try_write(key, f) + } + /// Locks the underlying [`RwLock`] with exclusive write access, blocking /// the current until it can be acquired. /// diff --git a/src/thread.rs b/src/thread.rs new file mode 100644 index 0000000..6e9c270 --- /dev/null +++ b/src/thread.rs @@ -0,0 +1,19 @@ +use std::marker::PhantomData; + +mod scope; + +#[derive(Debug)] +pub struct Scope<'scope, 'env: 'scope>(PhantomData<(&'env (), &'scope ())>); + +#[derive(Debug)] +pub struct ScopedJoinHandle<'scope, T> { + handle: std::thread::JoinHandle<T>, + _phantom: PhantomData<&'scope ()>, +} + +pub struct JoinHandle<T> { + handle: std::thread::JoinHandle<T>, + key: crate::ThreadKey, +} + +pub struct ThreadBuilder(std::thread::Builder); diff --git a/src/thread/scope.rst b/src/thread/scope.rst new file mode 100644 index 0000000..09319cb --- /dev/null +++ b/src/thread/scope.rst @@ -0,0 +1,47 @@ +use std::marker::PhantomData; + +use crate::{Keyable, ThreadKey}; + +use super::{Scope, ScopedJoinHandle}; + +pub fn scope<'env, F, T>(key: impl Keyable, f: F) -> T +where + F: for<'scope> FnOnce(&'scope Scope<'scope, 'env>) -> T, +{ + let scope = Scope(PhantomData); + let t = f(&scope); + drop(key); + t +} + +impl<'scope> Scope<'scope, '_> { + #[allow(clippy::unused_self)] + pub fn spawn<T: Send + 'scope>( + &self, + f: impl FnOnce(ThreadKey) -> T + Send + 'scope, + ) -> std::io::Result<ScopedJoinHandle<'scope, T>> { + unsafe { + // safety: the lifetimes ensure that the data lives long enough + let handle = std::thread::Builder::new().spawn_unchecked(|| { + // safety: the thread just started, so the key cannot be acquired yet + let key = ThreadKey::get().unwrap_unchecked(); + f(key) + })?; + + Ok(ScopedJoinHandle { + handle, + _phantom: PhantomData, + }) + } + } +} + +impl<T> ScopedJoinHandle<'_, T> { + pub fn is_finished(&self) -> bool { + self.handle.is_finished() + } + + pub fn join(self) -> std::thread::Result<T> { + self.handle.join() + } +} diff --git a/tarpaulin-report.html b/tarpaulin-report.html index 716e14a..bac34c6 100644 --- a/tarpaulin-report.html +++ b/tarpaulin-report.html @@ -118,8 +118,8 @@ <body> <div id="root"></div> <script> - var data = {"files":[{"path":["/","home","botahamec","Projects","happylock","examples","basic.rs"],"content":"use std::thread;\n\nuse happylock::{Mutex, ThreadKey};\n\nconst N: usize = 10;\n\nstatic DATA: Mutex\u003ci32\u003e = Mutex::new(0);\n\nfn main() {\n\tlet mut threads = Vec::new();\n\tfor _ in 0..N {\n\t\tlet th = thread::spawn(move || {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\tlet mut data = DATA.lock(key);\n\t\t\t*data += 1;\n\t\t});\n\t\tthreads.push(th);\n\t}\n\n\tfor th in threads {\n\t\t_ = th.join();\n\t}\n\n\tlet key = ThreadKey::get().unwrap();\n\tlet data = DATA.lock(key);\n\tprintln!(\"{data}\");\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","examples","dining_philosophers.rs"],"content":"use std::{thread, time::Duration};\n\nuse happylock::{collection, Mutex, ThreadKey};\n\nstatic PHILOSOPHERS: [Philosopher; 5] = [\n\tPhilosopher {\n\t\tname: \"Socrates\",\n\t\tleft: 0,\n\t\tright: 1,\n\t},\n\tPhilosopher {\n\t\tname: \"John Rawls\",\n\t\tleft: 1,\n\t\tright: 2,\n\t},\n\tPhilosopher {\n\t\tname: \"Jeremy Bentham\",\n\t\tleft: 2,\n\t\tright: 3,\n\t},\n\tPhilosopher {\n\t\tname: \"John Stuart Mill\",\n\t\tleft: 3,\n\t\tright: 4,\n\t},\n\tPhilosopher {\n\t\tname: \"Judith Butler\",\n\t\tleft: 4,\n\t\tright: 0,\n\t},\n];\n\nstatic FORKS: [Mutex\u003c()\u003e; 5] = [\n\tMutex::new(()),\n\tMutex::new(()),\n\tMutex::new(()),\n\tMutex::new(()),\n\tMutex::new(()),\n];\n\nstruct Philosopher {\n\tname: \u0026'static str,\n\tleft: usize,\n\tright: usize,\n}\n\nimpl Philosopher {\n\tfn cycle(\u0026self) {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tthread::sleep(Duration::from_secs(1));\n\n\t\t// safety: no philosopher asks for the same fork twice\n\t\tlet forks = [\u0026FORKS[self.left], \u0026FORKS[self.right]];\n\t\tlet forks = unsafe { collection::RefLockCollection::new_unchecked(\u0026forks) };\n\t\tlet forks = forks.lock(key);\n\t\tprintln!(\"{} is eating...\", self.name);\n\t\tthread::sleep(Duration::from_secs(1));\n\t\tprintln!(\"{} is done eating\", self.name);\n\t\tdrop(forks);\n\t}\n}\n\nfn main() {\n\tlet handles: Vec\u003c_\u003e = PHILOSOPHERS\n\t\t.iter()\n\t\t.map(|philosopher| thread::spawn(move || philosopher.cycle()))\n\t\t// The `collect` is absolutely necessary, because we're using lazy\n\t\t// iterators. If `collect` isn't used, then the thread won't spawn\n\t\t// until we try to join on it.\n\t\t.collect();\n\n\tfor handle in handles {\n\t\t_ = handle.join();\n\t}\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","examples","dining_philosophers_retry.rs"],"content":"use std::{thread, time::Duration};\n\nuse happylock::{collection, Mutex, ThreadKey};\n\nstatic PHILOSOPHERS: [Philosopher; 5] = [\n\tPhilosopher {\n\t\tname: \"Socrates\",\n\t\tleft: 0,\n\t\tright: 1,\n\t},\n\tPhilosopher {\n\t\tname: \"John Rawls\",\n\t\tleft: 1,\n\t\tright: 2,\n\t},\n\tPhilosopher {\n\t\tname: \"Jeremy Bentham\",\n\t\tleft: 2,\n\t\tright: 3,\n\t},\n\tPhilosopher {\n\t\tname: \"John Stuart Mill\",\n\t\tleft: 3,\n\t\tright: 4,\n\t},\n\tPhilosopher {\n\t\tname: \"Judith Butler\",\n\t\tleft: 4,\n\t\tright: 0,\n\t},\n];\n\nstatic FORKS: [Mutex\u003c()\u003e; 5] = [\n\tMutex::new(()),\n\tMutex::new(()),\n\tMutex::new(()),\n\tMutex::new(()),\n\tMutex::new(()),\n];\n\nstruct Philosopher {\n\tname: \u0026'static str,\n\tleft: usize,\n\tright: usize,\n}\n\nimpl Philosopher {\n\tfn cycle(\u0026self) {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tthread::sleep(Duration::from_secs(1));\n\n\t\t// safety: no philosopher asks for the same fork twice\n\t\tlet forks = [\u0026FORKS[self.left], \u0026FORKS[self.right]];\n\t\tlet forks = unsafe { collection::RetryingLockCollection::new_unchecked(\u0026forks) };\n\t\tlet forks = forks.lock(key);\n\t\tprintln!(\"{} is eating...\", self.name);\n\t\tthread::sleep(Duration::from_secs(1));\n\t\tprintln!(\"{} is done eating\", self.name);\n\t\tdrop(forks);\n\t}\n}\n\nfn main() {\n\tlet handles: Vec\u003c_\u003e = PHILOSOPHERS\n\t\t.iter()\n\t\t.map(|philosopher| thread::spawn(move || philosopher.cycle()))\n\t\t// The `collect` is absolutely necessary, because we're using lazy\n\t\t// iterators. If `collect` isn't used, then the thread won't spawn\n\t\t// until we try to join on it.\n\t\t.collect();\n\n\tfor handle in handles {\n\t\t_ = handle.join();\n\t}\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","examples","double_mutex.rs"],"content":"use std::thread;\n\nuse happylock::{collection::RefLockCollection, Mutex, ThreadKey};\n\nconst N: usize = 10;\n\nstatic DATA: (Mutex\u003ci32\u003e, Mutex\u003cString\u003e) = (Mutex::new(0), Mutex::new(String::new()));\n\nfn main() {\n\tlet mut threads = Vec::new();\n\tfor _ in 0..N {\n\t\tlet th = thread::spawn(move || {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\tlet lock = RefLockCollection::new(\u0026DATA);\n\t\t\tlet mut guard = lock.lock(key);\n\t\t\t*guard.1 = (100 - *guard.0).to_string();\n\t\t\t*guard.0 += 1;\n\t\t});\n\t\tthreads.push(th);\n\t}\n\n\tfor th in threads {\n\t\t_ = th.join();\n\t}\n\n\tlet key = ThreadKey::get().unwrap();\n\tlet data = RefLockCollection::new(\u0026DATA);\n\tlet data = data.lock(key);\n\tprintln!(\"{}\", data.0);\n\tprintln!(\"{}\", data.1);\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","examples","fibonacci.rs"],"content":"use happylock::{collection, LockCollection, Mutex, ThreadKey};\nuse std::thread;\n\nconst N: usize = 36;\n\nstatic DATA: [Mutex\u003ci32\u003e; 2] = [Mutex::new(0), Mutex::new(1)];\n\nfn main() {\n\tlet mut threads = Vec::new();\n\tfor _ in 0..N {\n\t\tlet th = thread::spawn(move || {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\n\t\t\t// a reference to a type that implements `OwnedLockable` will never\n\t\t\t// contain duplicates, so no duplicate checking is needed.\n\t\t\tlet collection = collection::RetryingLockCollection::new_ref(\u0026DATA);\n\t\t\tlet mut guard = collection.lock(key);\n\n\t\t\tlet x = *guard[1];\n\t\t\t*guard[1] += *guard[0];\n\t\t\t*guard[0] = x;\n\t\t});\n\t\tthreads.push(th);\n\t}\n\n\tfor thread in threads {\n\t\t_ = thread.join();\n\t}\n\n\tlet key = ThreadKey::get().unwrap();\n\tlet data = LockCollection::new_ref(\u0026DATA);\n\tlet data = data.lock(key);\n\tprintln!(\"{}\", data[0]);\n\tprintln!(\"{}\", data[1]);\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","examples","list.rs"],"content":"use std::thread;\n\nuse happylock::{collection::RefLockCollection, Mutex, ThreadKey};\n\nconst N: usize = 10;\n\nstatic DATA: [Mutex\u003cusize\u003e; 6] = [\n\tMutex::new(0),\n\tMutex::new(1),\n\tMutex::new(2),\n\tMutex::new(3),\n\tMutex::new(4),\n\tMutex::new(5),\n];\n\nstatic SEED: Mutex\u003cu32\u003e = Mutex::new(42);\n\nfn random(key: \u0026mut ThreadKey) -\u003e usize {\n\tSEED.scoped_lock(key, |seed| {\n\t\tlet x = *seed;\n\t\tlet x = x ^ (x \u003c\u003c 13);\n\t\tlet x = x ^ (x \u003e\u003e 17);\n\t\tlet x = x ^ (x \u003c\u003c 5);\n\t\t*seed = x;\n\t\tx as usize\n\t})\n}\n\nfn main() {\n\tlet mut threads = Vec::new();\n\tfor _ in 0..N {\n\t\tlet th = thread::spawn(move || {\n\t\t\tlet mut key = ThreadKey::get().unwrap();\n\t\t\tloop {\n\t\t\t\tlet mut data = Vec::new();\n\t\t\t\tfor _ in 0..3 {\n\t\t\t\t\tlet rand = random(\u0026mut key);\n\t\t\t\t\tdata.push(\u0026DATA[rand % 6]);\n\t\t\t\t}\n\n\t\t\t\tlet Some(lock) = RefLockCollection::try_new(\u0026data) else {\n\t\t\t\t\tcontinue;\n\t\t\t\t};\n\t\t\t\tlet mut guard = lock.lock(key);\n\t\t\t\t*guard[0] += *guard[1];\n\t\t\t\t*guard[1] += *guard[2];\n\t\t\t\t*guard[2] += *guard[0];\n\n\t\t\t\treturn;\n\t\t\t}\n\t\t});\n\t\tthreads.push(th);\n\t}\n\n\tfor th in threads {\n\t\t_ = th.join();\n\t}\n\n\tlet key = ThreadKey::get().unwrap();\n\tlet data = RefLockCollection::new(\u0026DATA);\n\tlet data = data.lock(key);\n\tfor val in \u0026*data {\n\t\tprintln!(\"{val}\");\n\t}\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","src","collection","boxed.rs"],"content":"use std::cell::UnsafeCell;\nuse std::fmt::Debug;\n\nuse crate::lockable::{Lockable, LockableIntoInner, OwnedLockable, RawLock, Sharable};\nuse crate::{Keyable, ThreadKey};\n\nuse super::utils::ordered_contains_duplicates;\nuse super::{utils, BoxedLockCollection, LockGuard};\n\nunsafe impl\u003cL: Lockable\u003e RawLock for BoxedLockCollection\u003cL\u003e {\n\t#[mutants::skip] // this should never be called\n\t#[cfg(not(tarpaulin_include))]\n\tfn poison(\u0026self) {\n\t\tfor lock in \u0026self.locks {\n\t\t\tlock.poison();\n\t\t}\n\t}\n\n\tunsafe fn raw_lock(\u0026self) {\n\t\tutils::ordered_lock(self.locks())\n\t}\n\n\tunsafe fn raw_try_lock(\u0026self) -\u003e bool {\n\t\tprintln!(\"{}\", self.locks().len());\n\t\tutils::ordered_try_lock(self.locks())\n\t}\n\n\tunsafe fn raw_unlock(\u0026self) {\n\t\tfor lock in self.locks() {\n\t\t\tlock.raw_unlock();\n\t\t}\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tutils::ordered_read(self.locks());\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tutils::ordered_try_read(self.locks())\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tfor lock in self.locks() {\n\t\t\tlock.raw_unlock_read();\n\t\t}\n\t}\n}\n\nunsafe impl\u003cL: Lockable\u003e Lockable for BoxedLockCollection\u003cL\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= L::Guard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= L::DataMut\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tptrs.extend(self.locks())\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tself.child().guard()\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.child().data_mut()\n\t}\n}\n\nunsafe impl\u003cL: Sharable\u003e Sharable for BoxedLockCollection\u003cL\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= L::ReadGuard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= L::DataRef\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tself.child().read_guard()\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tself.child().data_ref()\n\t}\n}\n\nunsafe impl\u003cL: OwnedLockable\u003e OwnedLockable for BoxedLockCollection\u003cL\u003e {}\n\n// LockableGetMut can't be implemented because that would create mutable and\n// immutable references to the same value at the same time.\n\nimpl\u003cL: LockableIntoInner\u003e LockableIntoInner for BoxedLockCollection\u003cL\u003e {\n\ttype Inner = L::Inner;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tLockableIntoInner::into_inner(self.into_child())\n\t}\n}\n\nimpl\u003cL\u003e IntoIterator for BoxedLockCollection\u003cL\u003e\nwhere\n\tL: IntoIterator,\n{\n\ttype Item = \u003cL as IntoIterator\u003e::Item;\n\ttype IntoIter = \u003cL as IntoIterator\u003e::IntoIter;\n\n\tfn into_iter(self) -\u003e Self::IntoIter {\n\t\tself.into_child().into_iter()\n\t}\n}\n\nimpl\u003c'a, L\u003e IntoIterator for \u0026'a BoxedLockCollection\u003cL\u003e\nwhere\n\t\u0026'a L: IntoIterator,\n{\n\ttype Item = \u003c\u0026'a L as IntoIterator\u003e::Item;\n\ttype IntoIter = \u003c\u0026'a L as IntoIterator\u003e::IntoIter;\n\n\tfn into_iter(self) -\u003e Self::IntoIter {\n\t\tself.child().into_iter()\n\t}\n}\n\nimpl\u003cL: OwnedLockable, I: FromIterator\u003cL\u003e + OwnedLockable\u003e FromIterator\u003cL\u003e\n\tfor BoxedLockCollection\u003cI\u003e\n{\n\tfn from_iter\u003cT: IntoIterator\u003cItem = L\u003e\u003e(iter: T) -\u003e Self {\n\t\tlet iter: I = iter.into_iter().collect();\n\t\tSelf::new(iter)\n\t}\n}\n\n// safety: the RawLocks must be send because they come from the Send Lockable\n#[allow(clippy::non_send_fields_in_send_ty)]\nunsafe impl\u003cL: Send\u003e Send for BoxedLockCollection\u003cL\u003e {}\nunsafe impl\u003cL: Sync\u003e Sync for BoxedLockCollection\u003cL\u003e {}\n\nimpl\u003cL\u003e Drop for BoxedLockCollection\u003cL\u003e {\n\t#[mutants::skip] // i can't test for a memory leak\n\t#[cfg(not(tarpaulin_include))]\n\tfn drop(\u0026mut self) {\n\t\tunsafe {\n\t\t\t// safety: this collection will never be locked again\n\t\t\tself.locks.clear();\n\t\t\t// safety: this was allocated using a box, and is now unique\n\t\t\tlet boxed: Box\u003cUnsafeCell\u003cL\u003e\u003e = Box::from_raw(self.data.cast_mut());\n\n\t\t\tdrop(boxed)\n\t\t}\n\t}\n}\n\nimpl\u003cT, L: AsRef\u003cT\u003e\u003e AsRef\u003cT\u003e for BoxedLockCollection\u003cL\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself.child().as_ref()\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cL: Debug\u003e Debug for BoxedLockCollection\u003cL\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tf.debug_struct(stringify!(BoxedLockCollection))\n\t\t\t.field(\"data\", \u0026self.data)\n\t\t\t.finish_non_exhaustive()\n\t}\n}\n\nimpl\u003cL: OwnedLockable + Default\u003e Default for BoxedLockCollection\u003cL\u003e {\n\tfn default() -\u003e Self {\n\t\tSelf::new(L::default())\n\t}\n}\n\nimpl\u003cL: OwnedLockable\u003e From\u003cL\u003e for BoxedLockCollection\u003cL\u003e {\n\tfn from(value: L) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\nimpl\u003cL\u003e BoxedLockCollection\u003cL\u003e {\n\t/// Gets the underlying collection, consuming this collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey, LockCollection};\n\t///\n\t/// let data1 = Mutex::new(42);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // data1 and data2 refer to distinct mutexes, so this won't panic\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = LockCollection::try_new(\u0026data).unwrap();\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let guard = lock.into_child().0.lock(key);\n\t/// assert_eq!(*guard, 42);\n\t/// ```\n\t#[must_use]\n\tpub fn into_child(mut self) -\u003e L {\n\t\tunsafe {\n\t\t\t// safety: this collection will never be used again\n\t\t\tstd::ptr::drop_in_place(\u0026mut self.locks);\n\t\t\t// safety: this was allocated using a box, and is now unique\n\t\t\tlet boxed: Box\u003cUnsafeCell\u003cL\u003e\u003e = Box::from_raw(self.data.cast_mut());\n\t\t\t// to prevent a double free\n\t\t\tstd::mem::forget(self);\n\n\t\t\tboxed.into_inner()\n\t\t}\n\t}\n\n\t// child_mut is immediate UB because it leads to mutable and immutable\n\t// references happening at the same time\n\n\t/// Gets an immutable reference to the underlying data\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey, LockCollection};\n\t///\n\t/// let data1 = Mutex::new(42);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // data1 and data2 refer to distinct mutexes, so this won't panic\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = LockCollection::try_new(\u0026data).unwrap();\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let guard = lock.child().0.lock(key);\n\t/// assert_eq!(*guard, 42);\n\t/// ```\n\t#[must_use]\n\tpub fn child(\u0026self) -\u003e \u0026L {\n\t\tunsafe {\n\t\t\tself.data\n\t\t\t\t.as_ref()\n\t\t\t\t.unwrap_unchecked()\n\t\t\t\t.get()\n\t\t\t\t.as_ref()\n\t\t\t\t.unwrap_unchecked()\n\t\t}\n\t}\n\n\t/// Gets the locks\n\tfn locks(\u0026self) -\u003e \u0026[\u0026dyn RawLock] {\n\t\t\u0026self.locks\n\t}\n}\n\nimpl\u003cL: OwnedLockable\u003e BoxedLockCollection\u003cL\u003e {\n\t/// Creates a new collection of owned locks.\n\t///\n\t/// Because the locks are owned, there's no need to do any checks for\n\t/// duplicate values.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t///\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t/// ```\n\t#[must_use]\n\tpub fn new(data: L) -\u003e Self {\n\t\t// safety: owned lockable types cannot contain duplicates\n\t\tunsafe { Self::new_unchecked(data) }\n\t}\n}\n\nimpl\u003c'a, L: OwnedLockable\u003e BoxedLockCollection\u003c\u0026'a L\u003e {\n\t/// Creates a new collection of owned locks.\n\t///\n\t/// Because the locks are owned, there's no need to do any checks for\n\t/// duplicate values.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t///\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = LockCollection::new_ref(\u0026data);\n\t/// ```\n\t#[must_use]\n\tpub fn new_ref(data: \u0026'a L) -\u003e Self {\n\t\t// safety: owned lockable types cannot contain duplicates\n\t\tunsafe { Self::new_unchecked(data) }\n\t}\n}\n\nimpl\u003cL: Lockable\u003e BoxedLockCollection\u003cL\u003e {\n\t/// Creates a new collections of locks.\n\t///\n\t/// # Safety\n\t///\n\t/// This results in undefined behavior if any locks are presented twice\n\t/// within this collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t///\n\t/// let data1 = Mutex::new(0);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // safety: data1 and data2 refer to distinct mutexes\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = unsafe { LockCollection::new_unchecked(\u0026data) };\n\t/// ```\n\t#[must_use]\n\tpub unsafe fn new_unchecked(data: L) -\u003e Self {\n\t\tlet data = Box::leak(Box::new(UnsafeCell::new(data)));\n\t\tlet data_ref = data.get().cast_const().as_ref().unwrap_unchecked();\n\n\t\tlet mut locks = Vec::new();\n\t\tdata_ref.get_ptrs(\u0026mut locks);\n\n\t\t// cast to *const () because fat pointers can't be converted to usize\n\t\tlocks.sort_by_key(|lock| (\u0026raw const **lock).cast::\u003c()\u003e() as usize);\n\n\t\t// safety we're just changing the lifetimes\n\t\tlet locks: Vec\u003c\u0026'static dyn RawLock\u003e = std::mem::transmute(locks);\n\t\tlet data = \u0026raw const *data;\n\t\tSelf { data, locks }\n\t}\n\n\t/// Creates a new collection of locks.\n\t///\n\t/// This returns `None` if any locks are found twice in the given\n\t/// collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t///\n\t/// let data1 = Mutex::new(0);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // data1 and data2 refer to distinct mutexes, so this won't panic\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = LockCollection::try_new(\u0026data).unwrap();\n\t/// ```\n\t#[must_use]\n\tpub fn try_new(data: L) -\u003e Option\u003cSelf\u003e {\n\t\t// safety: we are checking for duplicates before returning\n\t\tunsafe {\n\t\t\tlet this = Self::new_unchecked(data);\n\t\t\tif ordered_contains_duplicates(this.locks()) {\n\t\t\t\treturn None;\n\t\t\t}\n\t\t\tSome(this)\n\t\t}\n\t}\n\n\tpub fn scoped_lock\u003cR\u003e(\u0026self, key: impl Keyable, f: impl Fn(L::DataMut\u003c'_\u003e) -\u003e R) -\u003e R {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_lock();\n\n\t\t\t// safety: the data was just locked\n\t\t\tlet r = f(self.data_mut());\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock();\n\n\t\t\tdrop(key); // ensure the key stays alive long enough\n\n\t\t\tr\n\t\t}\n\t}\n\n\tpub fn scoped_try_lock\u003cKey: Keyable, R\u003e(\n\t\t\u0026self,\n\t\tkey: Key,\n\t\tf: impl Fn(L::DataMut\u003c'_\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_lock() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we just locked the collection\n\t\t\tlet r = f(self.data_mut());\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock();\n\n\t\t\tdrop(key); // ensures the key stays valid long enough\n\n\t\t\tOk(r)\n\t\t}\n\t}\n\n\t/// Locks the collection\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data. When the guard is dropped, the locks in the collection are also\n\t/// dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// ```\n\t#[must_use]\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::Guard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_lock();\n\n\t\t\tLockGuard {\n\t\t\t\t// safety: we've already acquired the lock\n\t\t\t\tguard: self.child().guard(),\n\t\t\t\tkey,\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Attempts to lock the without blocking.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// locks when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If any locks in the collection are already locked, then an error\n\t/// containing the given key is returned.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// match lock.try_lock(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::Guard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tlet guard = unsafe {\n\t\t\tif !self.raw_try_lock() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we've acquired the locks\n\t\t\tself.child().guard()\n\t\t};\n\n\t\tOk(LockGuard { guard, key })\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// let key = LockCollection::\u003c(Mutex\u003ci32\u003e, Mutex\u003c\u0026str\u003e)\u003e::unlock(guard);\n\t/// ```\n\tpub fn unlock(guard: LockGuard\u003cL::Guard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: Sharable\u003e BoxedLockCollection\u003cL\u003e {\n\tpub fn scoped_read\u003cR\u003e(\u0026self, key: impl Keyable, f: impl Fn(L::DataRef\u003c'_\u003e) -\u003e R) -\u003e R {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_read();\n\n\t\t\t// safety: the data was just locked\n\t\t\tlet r = f(self.data_ref());\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock_read();\n\n\t\t\tdrop(key); // ensure the key stays alive long enough\n\n\t\t\tr\n\t\t}\n\t}\n\n\tpub fn scoped_try_read\u003cKey: Keyable, R\u003e(\n\t\t\u0026self,\n\t\tkey: Key,\n\t\tf: impl Fn(L::DataRef\u003c'_\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_read() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we just locked the collection\n\t\t\tlet r = f(self.data_ref());\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock_read();\n\n\t\t\tdrop(key); // ensures the key stays valid long enough\n\n\t\t\tOk(r)\n\t\t}\n\t}\n\n\t/// Locks the collection, so that other threads can still read from it\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data immutably. When the guard is dropped, the locks in the collection\n\t/// are also dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// assert_eq!(*guard.0, 0);\n\t/// assert_eq!(*guard.1, \"\");\n\t/// ```\n\t#[must_use]\n\tpub fn read(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_read();\n\n\t\t\tLockGuard {\n\t\t\t\t// safety: we've already acquired the lock\n\t\t\t\tguard: self.child().read_guard(),\n\t\t\t\tkey,\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Attempts to lock the without blocking, in such a way that other threads\n\t/// can still read from the collection.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// shared access when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If any of the locks in the collection are already locked, then an error\n\t/// is returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(5), RwLock::new(\"6\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// match lock.try_read(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// assert_eq!(*guard.0, 5);\n\t/// assert_eq!(*guard.1, \"6\");\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_read(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tlet guard = unsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_read() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we've acquired the locks\n\t\t\tself.child().read_guard()\n\t\t};\n\n\t\tOk(LockGuard { guard, key })\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// let key = LockCollection::\u003c(RwLock\u003ci32\u003e, RwLock\u003c\u0026str\u003e)\u003e::unlock_read(guard);\n\t/// ```\n\tpub fn unlock_read(guard: LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: LockableIntoInner\u003e BoxedLockCollection\u003cL\u003e {\n\t/// Consumes this `BoxedLockCollection`, returning the underlying data.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t///\n\t/// let mutex = LockCollection::new([Mutex::new(0), Mutex::new(0)]);\n\t/// assert_eq!(mutex.into_inner(), [0, 0]);\n\t/// ```\n\t#[must_use]\n\tpub fn into_inner(self) -\u003e \u003cSelf as LockableIntoInner\u003e::Inner {\n\t\tLockableIntoInner::into_inner(self)\n\t}\n}\n\nimpl\u003c'a, L: 'a\u003e BoxedLockCollection\u003cL\u003e\nwhere\n\t\u0026'a L: IntoIterator,\n{\n\t/// Returns an iterator over references to each value in the collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = [Mutex::new(26), Mutex::new(1)];\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// let mut iter = lock.iter();\n\t/// let mutex = iter.next().unwrap();\n\t/// let guard = mutex.lock(key);\n\t///\n\t/// assert_eq!(*guard, 26);\n\t/// ```\n\t#[must_use]\n\tpub fn iter(\u0026'a self) -\u003e \u003c\u0026'a L as IntoIterator\u003e::IntoIter {\n\t\tself.into_iter()\n\t}\n}\n\n#[cfg(test)]\nmod tests {\n\tuse super::*;\n\tuse crate::{Mutex, RwLock, ThreadKey};\n\n\t#[test]\n\tfn from_iterator() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection: BoxedLockCollection\u003cVec\u003cMutex\u003c\u0026str\u003e\u003e\u003e =\n\t\t\t[Mutex::new(\"foo\"), Mutex::new(\"bar\"), Mutex::new(\"baz\")]\n\t\t\t\t.into_iter()\n\t\t\t\t.collect();\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], \"foo\");\n\t\tassert_eq!(*guard[1], \"bar\");\n\t\tassert_eq!(*guard[2], \"baz\");\n\t}\n\n\t#[test]\n\tfn from() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tBoxedLockCollection::from([Mutex::new(\"foo\"), Mutex::new(\"bar\"), Mutex::new(\"baz\")]);\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], \"foo\");\n\t\tassert_eq!(*guard[1], \"bar\");\n\t\tassert_eq!(*guard[2], \"baz\");\n\t}\n\n\t#[test]\n\tfn into_owned_iterator() {\n\t\tlet collection = BoxedLockCollection::new([Mutex::new(0), Mutex::new(1), Mutex::new(2)]);\n\t\tfor (i, mutex) in collection.into_iter().enumerate() {\n\t\t\tassert_eq!(mutex.into_inner(), i);\n\t\t}\n\t}\n\n\t#[test]\n\tfn into_ref_iterator() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::new([Mutex::new(0), Mutex::new(1), Mutex::new(2)]);\n\t\tfor (i, mutex) in (\u0026collection).into_iter().enumerate() {\n\t\t\tmutex.scoped_lock(\u0026mut key, |val| assert_eq!(*val, i))\n\t\t}\n\t}\n\n\t#[test]\n\tfn ref_iterator() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::new([Mutex::new(0), Mutex::new(1), Mutex::new(2)]);\n\t\tfor (i, mutex) in collection.iter().enumerate() {\n\t\t\tmutex.scoped_lock(\u0026mut key, |val| assert_eq!(*val, i))\n\t\t}\n\t}\n\n\t#[test]\n\t#[allow(clippy::float_cmp)]\n\tfn uses_correct_default() {\n\t\tlet collection =\n\t\t\tBoxedLockCollection::\u003c(Mutex\u003cf64\u003e, Mutex\u003cOption\u003ci32\u003e\u003e, Mutex\u003cusize\u003e)\u003e::default();\n\t\tlet tuple = collection.into_inner();\n\t\tassert_eq!(tuple.0, 0.0);\n\t\tassert!(tuple.1.is_none());\n\t\tassert_eq!(tuple.2, 0)\n\t}\n\n\t#[test]\n\tfn non_duplicates_allowed() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(1);\n\t\tassert!(BoxedLockCollection::try_new([\u0026mutex1, \u0026mutex2]).is_some())\n\t}\n\n\t#[test]\n\tfn duplicates_not_allowed() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tassert!(BoxedLockCollection::try_new([\u0026mutex1, \u0026mutex1]).is_none())\n\t}\n\n\t#[test]\n\tfn try_lock_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::new([Mutex::new(1), Mutex::new(2)]);\n\t\tlet guard = collection.try_lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_lock(key);\n\t\t\t\tassert!(guard.is_err());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn try_read_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::new([RwLock::new(1), RwLock::new(2)]);\n\t\tlet guard = collection.try_read(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_read(key);\n\t\t\t\tassert!(guard.is_ok());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn try_lock_fails_with_one_exclusive_lock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = [Mutex::new(1), Mutex::new(2)];\n\t\tlet collection = BoxedLockCollection::new_ref(\u0026locks);\n\t\tlet guard = locks[1].try_lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_lock(key);\n\t\t\t\tassert!(guard.is_err());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn try_read_fails_during_exclusive_lock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::new([RwLock::new(1), RwLock::new(2)]);\n\t\tlet guard = collection.try_lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_read(key);\n\t\t\t\tassert!(guard.is_err());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn try_read_fails_with_one_exclusive_lock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = [RwLock::new(1), RwLock::new(2)];\n\t\tlet collection = BoxedLockCollection::new_ref(\u0026locks);\n\t\tlet guard = locks[1].try_write(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_read(key);\n\t\t\t\tassert!(guard.is_err());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn unlock_collection_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex1 = Mutex::new(\"foo\");\n\t\tlet mutex2 = Mutex::new(\"bar\");\n\t\tlet collection = BoxedLockCollection::try_new((\u0026mutex1, \u0026mutex2)).unwrap();\n\t\tlet guard = collection.lock(key);\n\t\tlet key = BoxedLockCollection::\u003c(\u0026Mutex\u003c_\u003e, \u0026Mutex\u003c_\u003e)\u003e::unlock(guard);\n\n\t\tassert!(mutex1.try_lock(key).is_ok())\n\t}\n\n\t#[test]\n\tfn read_unlock_collection_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock1 = RwLock::new(\"foo\");\n\t\tlet lock2 = RwLock::new(\"bar\");\n\t\tlet collection = BoxedLockCollection::try_new((\u0026lock1, \u0026lock2)).unwrap();\n\t\tlet guard = collection.read(key);\n\t\tlet key = BoxedLockCollection::\u003c(\u0026RwLock\u003c_\u003e, \u0026RwLock\u003c_\u003e)\u003e::unlock_read(guard);\n\n\t\tassert!(lock1.try_write(key).is_ok())\n\t}\n\n\t#[test]\n\tfn into_inner_works() {\n\t\tlet collection = BoxedLockCollection::new((Mutex::new(\"Hello\"), Mutex::new(47)));\n\t\tassert_eq!(collection.into_inner(), (\"Hello\", 47))\n\t}\n\n\t#[test]\n\tfn works_in_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(1);\n\t\tlet collection =\n\t\t\tBoxedLockCollection::try_new(BoxedLockCollection::try_new([\u0026mutex1, \u0026mutex2]).unwrap())\n\t\t\t\t.unwrap();\n\n\t\tlet guard = collection.lock(key);\n\t\tassert!(mutex1.is_locked());\n\t\tassert!(mutex2.is_locked());\n\t\tdrop(guard);\n\t}\n}\n","traces":[{"line":19,"address":[212256],"length":1,"stats":{"Line":15}},{"line":20,"address":[181893],"length":1,"stats":{"Line":15}},{"line":23,"address":[658000,658208,657792],"length":1,"stats":{"Line":4}},{"line":24,"address":[658225,657809,658017,658318,657902,658110],"length":1,"stats":{"Line":8}},{"line":25,"address":[657960,658168,658376],"length":1,"stats":{"Line":4}},{"line":28,"address":[],"length":0,"stats":{"Line":0}},{"line":29,"address":[],"length":0,"stats":{"Line":0}},{"line":30,"address":[],"length":0,"stats":{"Line":0}},{"line":34,"address":[210784],"length":1,"stats":{"Line":2}},{"line":35,"address":[658421],"length":1,"stats":{"Line":2}},{"line":38,"address":[],"length":0,"stats":{"Line":3}},{"line":39,"address":[241141],"length":1,"stats":{"Line":3}},{"line":42,"address":[],"length":0,"stats":{"Line":0}},{"line":43,"address":[],"length":0,"stats":{"Line":0}},{"line":44,"address":[],"length":0,"stats":{"Line":0}},{"line":60,"address":[658512],"length":1,"stats":{"Line":1}},{"line":61,"address":[658530],"length":1,"stats":{"Line":1}},{"line":64,"address":[658560],"length":1,"stats":{"Line":1}},{"line":65,"address":[658592],"length":1,"stats":{"Line":1}},{"line":68,"address":[],"length":0,"stats":{"Line":0}},{"line":69,"address":[],"length":0,"stats":{"Line":0}},{"line":84,"address":[],"length":0,"stats":{"Line":0}},{"line":85,"address":[],"length":0,"stats":{"Line":0}},{"line":88,"address":[],"length":0,"stats":{"Line":0}},{"line":89,"address":[],"length":0,"stats":{"Line":0}},{"line":101,"address":[],"length":0,"stats":{"Line":2}},{"line":102,"address":[658638,658702],"length":1,"stats":{"Line":2}},{"line":113,"address":[],"length":0,"stats":{"Line":1}},{"line":114,"address":[],"length":0,"stats":{"Line":1}},{"line":125,"address":[658816],"length":1,"stats":{"Line":1}},{"line":126,"address":[],"length":0,"stats":{"Line":1}},{"line":133,"address":[],"length":0,"stats":{"Line":1}},{"line":134,"address":[],"length":0,"stats":{"Line":1}},{"line":135,"address":[658895],"length":1,"stats":{"Line":1}},{"line":160,"address":[],"length":0,"stats":{"Line":0}},{"line":161,"address":[],"length":0,"stats":{"Line":0}},{"line":176,"address":[658928],"length":1,"stats":{"Line":1}},{"line":177,"address":[],"length":0,"stats":{"Line":1}},{"line":182,"address":[658992],"length":1,"stats":{"Line":1}},{"line":183,"address":[659000],"length":1,"stats":{"Line":1}},{"line":207,"address":[659409,659456,659901,659887,660321,660335,659423,659024,659936],"length":1,"stats":{"Line":3}},{"line":210,"address":[659966,659054,659486],"length":1,"stats":{"Line":3}},{"line":212,"address":[],"length":0,"stats":{"Line":3}},{"line":214,"address":[],"length":0,"stats":{"Line":3}},{"line":216,"address":[659819,660253,660178,659266,659341,659698],"length":1,"stats":{"Line":3}},{"line":242,"address":[212960],"length":1,"stats":{"Line":18}},{"line":244,"address":[241673],"length":1,"stats":{"Line":18}},{"line":254,"address":[],"length":0,"stats":{"Line":26}},{"line":255,"address":[211573],"length":1,"stats":{"Line":26}},{"line":274,"address":[],"length":0,"stats":{"Line":12}},{"line":276,"address":[],"length":0,"stats":{"Line":14}},{"line":295,"address":[662240,662272,662304],"length":1,"stats":{"Line":3}},{"line":297,"address":[],"length":0,"stats":{"Line":3}},{"line":322,"address":[241610,241637,241168],"length":1,"stats":{"Line":28}},{"line":323,"address":[212412,212305],"length":1,"stats":{"Line":57}},{"line":324,"address":[212452],"length":1,"stats":{"Line":28}},{"line":326,"address":[230527],"length":1,"stats":{"Line":29}},{"line":327,"address":[211038],"length":1,"stats":{"Line":29}},{"line":330,"address":[148442,148432],"length":1,"stats":{"Line":85}},{"line":333,"address":[212620],"length":1,"stats":{"Line":31}},{"line":334,"address":[212667],"length":1,"stats":{"Line":31}},{"line":356,"address":[242006,241760],"length":1,"stats":{"Line":11}},{"line":359,"address":[204043],"length":1,"stats":{"Line":11}},{"line":360,"address":[182786,182725],"length":1,"stats":{"Line":24}},{"line":361,"address":[241970],"length":1,"stats":{"Line":1}},{"line":363,"address":[211712],"length":1,"stats":{"Line":11}},{"line":367,"address":[],"length":0,"stats":{"Line":0}},{"line":370,"address":[],"length":0,"stats":{"Line":0}},{"line":373,"address":[],"length":0,"stats":{"Line":0}},{"line":376,"address":[],"length":0,"stats":{"Line":0}},{"line":380,"address":[],"length":0,"stats":{"Line":0}},{"line":391,"address":[],"length":0,"stats":{"Line":0}},{"line":392,"address":[],"length":0,"stats":{"Line":0}},{"line":396,"address":[],"length":0,"stats":{"Line":0}},{"line":399,"address":[],"length":0,"stats":{"Line":0}},{"line":403,"address":[],"length":0,"stats":{"Line":0}},{"line":427,"address":[674117,673104,673552,673376,673264,673987,673856,673070,672928,673235,673518,673653,673680,674144,673357,673822,674245,674016],"length":1,"stats":{"Line":15}},{"line":430,"address":[230848],"length":1,"stats":{"Line":15}},{"line":434,"address":[673176,673448,673606,673318,673928,674198,674070,673752,673000],"length":1,"stats":{"Line":12}},{"line":469,"address":[204288,204495],"length":1,"stats":{"Line":4}},{"line":471,"address":[674720,674766,674304,674350,674512,674558],"length":1,"stats":{"Line":7}},{"line":472,"address":[674777,674361,674569],"length":1,"stats":{"Line":2}},{"line":476,"address":[204425,204395],"length":1,"stats":{"Line":4}},{"line":479,"address":[204437],"length":1,"stats":{"Line":2}},{"line":499,"address":[675168,675403,674960,675296,675210,675044,675072,675232,674992,674896,675147,675328],"length":1,"stats":{"Line":8}},{"line":500,"address":[],"length":0,"stats":{"Line":8}},{"line":501,"address":[],"length":0,"stats":{"Line":0}},{"line":506,"address":[],"length":0,"stats":{"Line":0}},{"line":509,"address":[],"length":0,"stats":{"Line":0}},{"line":512,"address":[],"length":0,"stats":{"Line":0}},{"line":515,"address":[],"length":0,"stats":{"Line":0}},{"line":519,"address":[],"length":0,"stats":{"Line":0}},{"line":530,"address":[],"length":0,"stats":{"Line":0}},{"line":531,"address":[],"length":0,"stats":{"Line":0}},{"line":535,"address":[],"length":0,"stats":{"Line":0}},{"line":538,"address":[],"length":0,"stats":{"Line":0}},{"line":542,"address":[],"length":0,"stats":{"Line":0}},{"line":566,"address":[675424,675525],"length":1,"stats":{"Line":2}},{"line":569,"address":[],"length":0,"stats":{"Line":2}},{"line":573,"address":[675478],"length":1,"stats":{"Line":1}},{"line":609,"address":[],"length":0,"stats":{"Line":3}},{"line":612,"address":[],"length":0,"stats":{"Line":5}},{"line":613,"address":[],"length":0,"stats":{"Line":2}},{"line":617,"address":[],"length":0,"stats":{"Line":2}},{"line":620,"address":[],"length":0,"stats":{"Line":1}},{"line":638,"address":[],"length":0,"stats":{"Line":1}},{"line":639,"address":[675982],"length":1,"stats":{"Line":1}},{"line":640,"address":[],"length":0,"stats":{"Line":0}},{"line":656,"address":[],"length":0,"stats":{"Line":2}},{"line":657,"address":[],"length":0,"stats":{"Line":2}},{"line":683,"address":[],"length":0,"stats":{"Line":1}},{"line":684,"address":[],"length":0,"stats":{"Line":1}}],"covered":76,"coverable":112},{"path":["/","home","botahamec","Projects","happylock","src","collection","guard.rs"],"content":"use std::fmt::{Debug, Display};\nuse std::hash::Hash;\nuse std::ops::{Deref, DerefMut};\n\nuse super::LockGuard;\n\n#[mutants::skip] // hashing involves RNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard: Hash\u003e Hash for LockGuard\u003cGuard\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.guard.hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard: Debug\u003e Debug for LockGuard\u003cGuard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cGuard: Display\u003e Display for LockGuard\u003cGuard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cGuard\u003e Deref for LockGuard\u003cGuard\u003e {\n\ttype Target = Guard;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t\u0026self.guard\n\t}\n}\n\nimpl\u003cGuard\u003e DerefMut for LockGuard\u003cGuard\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t\u0026mut self.guard\n\t}\n}\n\nimpl\u003cGuard\u003e AsRef\u003cGuard\u003e for LockGuard\u003cGuard\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026Guard {\n\t\t\u0026self.guard\n\t}\n}\n\nimpl\u003cGuard\u003e AsMut\u003cGuard\u003e for LockGuard\u003cGuard\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut Guard {\n\t\t\u0026mut self.guard\n\t}\n}\n\n#[cfg(test)]\nmod tests {\n\tuse crate::collection::OwnedLockCollection;\n\tuse crate::{LockCollection, Mutex, RwLock, ThreadKey};\n\n\t#[test]\n\tfn guard_display_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = OwnedLockCollection::new(RwLock::new(\"Hello, world!\"));\n\t\tlet guard = lock.read(key);\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn deref_mut_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = (Mutex::new(1), Mutex::new(2));\n\t\tlet lock = LockCollection::new_ref(\u0026locks);\n\t\tlet mut guard = lock.lock(key);\n\t\t*guard.0 = 3;\n\t\tlet key = LockCollection::\u003c(Mutex\u003c_\u003e, Mutex\u003c_\u003e)\u003e::unlock(guard);\n\n\t\tlet guard = locks.0.lock(key);\n\t\tassert_eq!(*guard, 3);\n\t\tlet key = Mutex::unlock(guard);\n\n\t\tlet guard = locks.1.lock(key);\n\t\tassert_eq!(*guard, 2);\n\t}\n\n\t#[test]\n\tfn as_ref_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = (Mutex::new(1), Mutex::new(2));\n\t\tlet lock = LockCollection::new_ref(\u0026locks);\n\t\tlet mut guard = lock.lock(key);\n\t\t*guard.0 = 3;\n\t\tlet key = LockCollection::\u003c(Mutex\u003c_\u003e, Mutex\u003c_\u003e)\u003e::unlock(guard);\n\n\t\tlet guard = locks.0.lock(key);\n\t\tassert_eq!(guard.as_ref(), \u00263);\n\t\tlet key = Mutex::unlock(guard);\n\n\t\tlet guard = locks.1.lock(key);\n\t\tassert_eq!(guard.as_ref(), \u00262);\n\t}\n\n\t#[test]\n\tfn as_mut_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = (Mutex::new(1), Mutex::new(2));\n\t\tlet lock = LockCollection::new_ref(\u0026locks);\n\t\tlet mut guard = lock.lock(key);\n\t\tlet guard_mut = guard.as_mut();\n\t\t*guard_mut.0 = 3;\n\t\tlet key = LockCollection::\u003c(Mutex\u003c_\u003e, Mutex\u003c_\u003e)\u003e::unlock(guard);\n\n\t\tlet guard = locks.0.lock(key);\n\t\tassert_eq!(guard.as_ref(), \u00263);\n\t\tlet key = Mutex::unlock(guard);\n\n\t\tlet guard = locks.1.lock(key);\n\t\tassert_eq!(guard.as_ref(), \u00262);\n\t}\n}\n","traces":[{"line":24,"address":[],"length":0,"stats":{"Line":1}},{"line":25,"address":[],"length":0,"stats":{"Line":1}},{"line":32,"address":[],"length":0,"stats":{"Line":12}},{"line":33,"address":[],"length":0,"stats":{"Line":0}},{"line":38,"address":[],"length":0,"stats":{"Line":3}},{"line":39,"address":[],"length":0,"stats":{"Line":0}},{"line":44,"address":[],"length":0,"stats":{"Line":2}},{"line":45,"address":[],"length":0,"stats":{"Line":0}},{"line":50,"address":[],"length":0,"stats":{"Line":4}},{"line":51,"address":[],"length":0,"stats":{"Line":0}}],"covered":6,"coverable":10},{"path":["/","home","botahamec","Projects","happylock","src","collection","owned.rs"],"content":"use crate::lockable::{\n\tLockable, LockableGetMut, LockableIntoInner, OwnedLockable, RawLock, Sharable,\n};\nuse crate::{Keyable, ThreadKey};\n\nuse super::{utils, LockGuard, OwnedLockCollection};\n\nunsafe impl\u003cL: Lockable\u003e RawLock for OwnedLockCollection\u003cL\u003e {\n\t#[mutants::skip] // this should never run\n\t#[cfg(not(tarpaulin_include))]\n\tfn poison(\u0026self) {\n\t\tlet locks = utils::get_locks_unsorted(\u0026self.data);\n\t\tfor lock in locks {\n\t\t\tlock.poison();\n\t\t}\n\t}\n\n\tunsafe fn raw_lock(\u0026self) {\n\t\tutils::ordered_lock(\u0026utils::get_locks_unsorted(\u0026self.data))\n\t}\n\n\tunsafe fn raw_try_lock(\u0026self) -\u003e bool {\n\t\tlet locks = utils::get_locks_unsorted(\u0026self.data);\n\t\tutils::ordered_try_lock(\u0026locks)\n\t}\n\n\tunsafe fn raw_unlock(\u0026self) {\n\t\tlet locks = utils::get_locks_unsorted(\u0026self.data);\n\t\tfor lock in locks {\n\t\t\tlock.raw_unlock();\n\t\t}\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tutils::ordered_read(\u0026utils::get_locks_unsorted(\u0026self.data))\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tlet locks = utils::get_locks_unsorted(\u0026self.data);\n\t\tutils::ordered_try_read(\u0026locks)\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tlet locks = utils::get_locks_unsorted(\u0026self.data);\n\t\tfor lock in locks {\n\t\t\tlock.raw_unlock_read();\n\t\t}\n\t}\n}\n\nunsafe impl\u003cL: Lockable\u003e Lockable for OwnedLockCollection\u003cL\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= L::Guard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= L::DataMut\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\t#[mutants::skip] // It's hard to test lkocks in an OwnedLockCollection, because they're owned\n\t#[cfg(not(tarpaulin_include))]\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tself.data.get_ptrs(ptrs)\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tself.data.guard()\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.data.data_mut()\n\t}\n}\n\nimpl\u003cL: LockableGetMut\u003e LockableGetMut for OwnedLockCollection\u003cL\u003e {\n\ttype Inner\u003c'a\u003e\n\t\t= L::Inner\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tself.data.get_mut()\n\t}\n}\n\nimpl\u003cL: LockableIntoInner\u003e LockableIntoInner for OwnedLockCollection\u003cL\u003e {\n\ttype Inner = L::Inner;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tself.data.into_inner()\n\t}\n}\n\nunsafe impl\u003cL: Sharable\u003e Sharable for OwnedLockCollection\u003cL\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= L::ReadGuard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= L::DataRef\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tself.data.read_guard()\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tself.data.data_ref()\n\t}\n}\n\nunsafe impl\u003cL: OwnedLockable\u003e OwnedLockable for OwnedLockCollection\u003cL\u003e {}\n\nimpl\u003cL\u003e IntoIterator for OwnedLockCollection\u003cL\u003e\nwhere\n\tL: IntoIterator,\n{\n\ttype Item = \u003cL as IntoIterator\u003e::Item;\n\ttype IntoIter = \u003cL as IntoIterator\u003e::IntoIter;\n\n\tfn into_iter(self) -\u003e Self::IntoIter {\n\t\tself.data.into_iter()\n\t}\n}\n\nimpl\u003cL: OwnedLockable, I: FromIterator\u003cL\u003e + OwnedLockable\u003e FromIterator\u003cL\u003e\n\tfor OwnedLockCollection\u003cI\u003e\n{\n\tfn from_iter\u003cT: IntoIterator\u003cItem = L\u003e\u003e(iter: T) -\u003e Self {\n\t\tlet iter: I = iter.into_iter().collect();\n\t\tSelf::new(iter)\n\t}\n}\n\nimpl\u003cE: OwnedLockable + Extend\u003cL\u003e, L: OwnedLockable\u003e Extend\u003cL\u003e for OwnedLockCollection\u003cE\u003e {\n\tfn extend\u003cT: IntoIterator\u003cItem = L\u003e\u003e(\u0026mut self, iter: T) {\n\t\tself.data.extend(iter)\n\t}\n}\n\n// AsRef can't be implemented because an impl of AsRef\u003cL\u003e for L could break the\n// invariant that there is only one way to lock the collection. AsMut is fine,\n// because the collection can't be locked as long as the reference is valid.\n\nimpl\u003cT, L: AsMut\u003cT\u003e\u003e AsMut\u003cT\u003e for OwnedLockCollection\u003cL\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself.data.as_mut()\n\t}\n}\n\nimpl\u003cL: OwnedLockable + Default\u003e Default for OwnedLockCollection\u003cL\u003e {\n\tfn default() -\u003e Self {\n\t\tSelf::new(L::default())\n\t}\n}\n\nimpl\u003cL: OwnedLockable\u003e From\u003cL\u003e for OwnedLockCollection\u003cL\u003e {\n\tfn from(value: L) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\nimpl\u003cL: OwnedLockable\u003e OwnedLockCollection\u003cL\u003e {\n\t/// Creates a new collection of owned locks.\n\t///\n\t/// Because the locks are owned, there's no need to do any checks for\n\t/// duplicate values. The locks also don't need to be sorted by memory\n\t/// address because they aren't used anywhere else.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t/// ```\n\t#[must_use]\n\tpub const fn new(data: L) -\u003e Self {\n\t\tSelf { data }\n\t}\n\n\tpub fn scoped_lock\u003cR\u003e(\u0026self, key: impl Keyable, f: impl Fn(L::DataMut\u003c'_\u003e) -\u003e R) -\u003e R {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_lock();\n\n\t\t\t// safety: the data was just locked\n\t\t\tlet r = f(self.data_mut());\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock();\n\n\t\t\tdrop(key); // ensure the key stays alive long enough\n\n\t\t\tr\n\t\t}\n\t}\n\n\tpub fn scoped_try_lock\u003cKey: Keyable, R\u003e(\n\t\t\u0026self,\n\t\tkey: Key,\n\t\tf: impl Fn(L::DataMut\u003c'_\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_lock() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we just locked the collection\n\t\t\tlet r = f(self.data_mut());\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock();\n\n\t\t\tdrop(key); // ensures the key stays valid long enough\n\n\t\t\tOk(r)\n\t\t}\n\t}\n\n\t/// Locks the collection\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data. When the guard is dropped, the locks in the collection are also\n\t/// dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// ```\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::Guard\u003c'_\u003e\u003e {\n\t\tlet guard = unsafe {\n\t\t\t// safety: we have the thread key, and these locks happen in a\n\t\t\t// predetermined order\n\t\t\tself.raw_lock();\n\n\t\t\t// safety: we've locked all of this already\n\t\t\tself.data.guard()\n\t\t};\n\n\t\tLockGuard { guard, key }\n\t}\n\n\t/// Attempts to lock the without blocking.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// locks when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If any of the locks in this collection are already locked, this returns\n\t/// an error containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// match lock.try_lock(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::Guard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tlet guard = unsafe {\n\t\t\tif !self.raw_try_lock() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we've acquired the locks\n\t\t\tself.data.guard()\n\t\t};\n\n\t\tOk(LockGuard { guard, key })\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// let key = OwnedLockCollection::\u003c(Mutex\u003ci32\u003e, Mutex\u003c\u0026str\u003e)\u003e::unlock(guard);\n\t/// ```\n\t#[allow(clippy::missing_const_for_fn)]\n\tpub fn unlock(guard: LockGuard\u003cL::Guard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: Sharable\u003e OwnedLockCollection\u003cL\u003e {\n\tpub fn scoped_read\u003cR\u003e(\u0026self, key: impl Keyable, f: impl Fn(L::DataRef\u003c'_\u003e) -\u003e R) -\u003e R {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_read();\n\n\t\t\t// safety: the data was just locked\n\t\t\tlet r = f(self.data_ref());\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock_read();\n\n\t\t\tdrop(key); // ensure the key stays alive long enough\n\n\t\t\tr\n\t\t}\n\t}\n\n\tpub fn scoped_try_read\u003cKey: Keyable, R\u003e(\n\t\t\u0026self,\n\t\tkey: Key,\n\t\tf: impl Fn(L::DataRef\u003c'_\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_read() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we just locked the collection\n\t\t\tlet r = f(self.data_ref());\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock_read();\n\n\t\t\tdrop(key); // ensures the key stays valid long enough\n\n\t\t\tOk(r)\n\t\t}\n\t}\n\n\t/// Locks the collection, so that other threads can still read from it\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data immutably. When the guard is dropped, the locks in the collection\n\t/// are also dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// assert_eq!(*guard.0, 0);\n\t/// assert_eq!(*guard.1, \"\");\n\t/// ```\n\tpub fn read(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_read();\n\n\t\t\tLockGuard {\n\t\t\t\t// safety: we've already acquired the lock\n\t\t\t\tguard: self.data.read_guard(),\n\t\t\t\tkey,\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Attempts to lock the without blocking, in such a way that other threads\n\t/// can still read from the collection.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// shared access when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If any of the locks in this collection can't be acquired, then an error\n\t/// is returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(5), RwLock::new(\"6\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// match lock.try_read(key) {\n\t/// Some(mut guard) =\u003e {\n\t/// assert_eq!(*guard.0, 5);\n\t/// assert_eq!(*guard.1, \"6\");\n\t/// },\n\t/// None =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_read(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tlet guard = unsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_read() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we've acquired the locks\n\t\t\tself.data.read_guard()\n\t\t};\n\n\t\tOk(LockGuard { guard, key })\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// let key = OwnedLockCollection::\u003c(RwLock\u003ci32\u003e, RwLock\u003c\u0026str\u003e)\u003e::unlock_read(guard);\n\t/// ```\n\t#[allow(clippy::missing_const_for_fn)]\n\tpub fn unlock_read(guard: LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL\u003e OwnedLockCollection\u003cL\u003e {\n\t/// Gets the underlying collection, consuming this collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let data = (Mutex::new(42), Mutex::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let inner = lock.into_child();\n\t/// let guard = inner.0.lock(key);\n\t/// assert_eq!(*guard, 42);\n\t/// ```\n\t#[must_use]\n\tpub fn into_child(self) -\u003e L {\n\t\tself.data\n\t}\n\n\t/// Gets a mutable reference to the underlying collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let data = (Mutex::new(42), Mutex::new(\"\"));\n\t/// let mut lock = OwnedLockCollection::new(data);\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut inner = lock.child_mut();\n\t/// let guard = inner.0.get_mut();\n\t/// assert_eq!(*guard, 42);\n\t/// ```\n\t#[must_use]\n\tpub fn child_mut(\u0026mut self) -\u003e \u0026mut L {\n\t\t\u0026mut self.data\n\t}\n}\n\nimpl\u003cL: LockableGetMut\u003e OwnedLockCollection\u003cL\u003e {\n\t/// Gets a mutable reference to the data behind this `OwnedLockCollection`.\n\t///\n\t/// Since this call borrows the `OwnedLockCollection` mutably, no actual\n\t/// locking needs to take place - the mutable borrow statically guarantees\n\t/// no locks exist.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let mut mutex = OwnedLockCollection::new([Mutex::new(0), Mutex::new(0)]);\n\t/// assert_eq!(mutex.get_mut(), [\u0026mut 0, \u0026mut 0]);\n\t/// ```\n\tpub fn get_mut(\u0026mut self) -\u003e L::Inner\u003c'_\u003e {\n\t\tLockableGetMut::get_mut(self)\n\t}\n}\n\nimpl\u003cL: LockableIntoInner\u003e OwnedLockCollection\u003cL\u003e {\n\t/// Consumes this `OwnedLockCollection`, returning the underlying data.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let mutex = OwnedLockCollection::new([Mutex::new(0), Mutex::new(0)]);\n\t/// assert_eq!(mutex.into_inner(), [0, 0]);\n\t/// ```\n\t#[must_use]\n\tpub fn into_inner(self) -\u003e L::Inner {\n\t\tLockableIntoInner::into_inner(self)\n\t}\n}\n\n#[cfg(test)]\nmod tests {\n\tuse super::*;\n\tuse crate::{Mutex, ThreadKey};\n\n\t#[test]\n\tfn get_mut_applies_changes() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mut collection = OwnedLockCollection::new([Mutex::new(\"foo\"), Mutex::new(\"bar\")]);\n\t\tassert_eq!(*collection.get_mut()[0], \"foo\");\n\t\tassert_eq!(*collection.get_mut()[1], \"bar\");\n\t\t*collection.get_mut()[0] = \"baz\";\n\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], \"baz\");\n\t\tassert_eq!(*guard[1], \"bar\");\n\t}\n\n\t#[test]\n\tfn into_inner_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::from([Mutex::new(\"foo\")]);\n\t\tlet mut guard = collection.lock(key);\n\t\t*guard[0] = \"bar\";\n\t\tdrop(guard);\n\n\t\tlet array = collection.into_inner();\n\t\tassert_eq!(array.len(), 1);\n\t\tassert_eq!(array[0], \"bar\");\n\t}\n\n\t#[test]\n\tfn from_into_iter_is_correct() {\n\t\tlet array = [Mutex::new(0), Mutex::new(1), Mutex::new(2), Mutex::new(3)];\n\t\tlet mut collection: OwnedLockCollection\u003cVec\u003cMutex\u003cusize\u003e\u003e\u003e = array.into_iter().collect();\n\t\tassert_eq!(collection.get_mut().len(), 4);\n\t\tfor (i, lock) in collection.into_iter().enumerate() {\n\t\t\tassert_eq!(lock.into_inner(), i);\n\t\t}\n\t}\n\n\t#[test]\n\tfn from_iter_is_correct() {\n\t\tlet array = [Mutex::new(0), Mutex::new(1), Mutex::new(2), Mutex::new(3)];\n\t\tlet mut collection: OwnedLockCollection\u003cVec\u003cMutex\u003cusize\u003e\u003e\u003e = array.into_iter().collect();\n\t\tlet collection: \u0026mut Vec\u003c_\u003e = collection.as_mut();\n\t\tassert_eq!(collection.len(), 4);\n\t\tfor (i, lock) in collection.iter_mut().enumerate() {\n\t\t\tassert_eq!(*lock.get_mut(), i);\n\t\t}\n\t}\n\n\t#[test]\n\tfn try_lock_works_on_unlocked() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new((Mutex::new(0), Mutex::new(1)));\n\t\tlet guard = collection.try_lock(key).unwrap();\n\t\tassert_eq!(*guard.0, 0);\n\t\tassert_eq!(*guard.1, 1);\n\t}\n\n\t#[test]\n\tfn try_lock_fails_on_locked() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new((Mutex::new(0), Mutex::new(1)));\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\t#[allow(unused)]\n\t\t\t\tlet guard = collection.lock(key);\n\t\t\t\tstd::mem::forget(guard);\n\t\t\t});\n\t\t});\n\n\t\tassert!(collection.try_lock(key).is_err());\n\t}\n\n\t#[test]\n\tfn default_works() {\n\t\ttype MyCollection = OwnedLockCollection\u003c(Mutex\u003ci32\u003e, Mutex\u003cOption\u003ci32\u003e\u003e, Mutex\u003cString\u003e)\u003e;\n\t\tlet collection = MyCollection::default();\n\t\tlet inner = collection.into_inner();\n\t\tassert_eq!(inner.0, 0);\n\t\tassert_eq!(inner.1, None);\n\t\tassert_eq!(inner.2, String::new());\n\t}\n\n\t#[test]\n\tfn can_be_extended() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(1);\n\t\tlet mut collection = OwnedLockCollection::new(vec![mutex1, mutex2]);\n\n\t\tcollection.extend([Mutex::new(2)]);\n\n\t\tassert_eq!(collection.data.len(), 3);\n\t}\n}\n","traces":[{"line":18,"address":[],"length":0,"stats":{"Line":3}},{"line":19,"address":[676533,676597,676853,676661,676725,676789],"length":1,"stats":{"Line":6}},{"line":22,"address":[],"length":0,"stats":{"Line":1}},{"line":23,"address":[],"length":0,"stats":{"Line":1}},{"line":24,"address":[],"length":0,"stats":{"Line":2}},{"line":27,"address":[],"length":0,"stats":{"Line":0}},{"line":28,"address":[],"length":0,"stats":{"Line":0}},{"line":29,"address":[],"length":0,"stats":{"Line":0}},{"line":30,"address":[],"length":0,"stats":{"Line":0}},{"line":34,"address":[],"length":0,"stats":{"Line":1}},{"line":35,"address":[],"length":0,"stats":{"Line":2}},{"line":38,"address":[],"length":0,"stats":{"Line":0}},{"line":39,"address":[],"length":0,"stats":{"Line":0}},{"line":40,"address":[],"length":0,"stats":{"Line":0}},{"line":43,"address":[],"length":0,"stats":{"Line":0}},{"line":44,"address":[],"length":0,"stats":{"Line":0}},{"line":45,"address":[],"length":0,"stats":{"Line":0}},{"line":46,"address":[],"length":0,"stats":{"Line":0}},{"line":68,"address":[],"length":0,"stats":{"Line":0}},{"line":69,"address":[],"length":0,"stats":{"Line":0}},{"line":72,"address":[],"length":0,"stats":{"Line":0}},{"line":73,"address":[],"length":0,"stats":{"Line":0}},{"line":83,"address":[],"length":0,"stats":{"Line":2}},{"line":84,"address":[],"length":0,"stats":{"Line":2}},{"line":91,"address":[],"length":0,"stats":{"Line":2}},{"line":92,"address":[],"length":0,"stats":{"Line":2}},{"line":107,"address":[],"length":0,"stats":{"Line":0}},{"line":108,"address":[],"length":0,"stats":{"Line":0}},{"line":111,"address":[],"length":0,"stats":{"Line":0}},{"line":112,"address":[],"length":0,"stats":{"Line":0}},{"line":125,"address":[],"length":0,"stats":{"Line":1}},{"line":126,"address":[],"length":0,"stats":{"Line":1}},{"line":133,"address":[],"length":0,"stats":{"Line":2}},{"line":134,"address":[],"length":0,"stats":{"Line":2}},{"line":135,"address":[],"length":0,"stats":{"Line":2}},{"line":140,"address":[],"length":0,"stats":{"Line":1}},{"line":141,"address":[],"length":0,"stats":{"Line":1}},{"line":150,"address":[],"length":0,"stats":{"Line":1}},{"line":151,"address":[],"length":0,"stats":{"Line":1}},{"line":156,"address":[],"length":0,"stats":{"Line":1}},{"line":157,"address":[677565],"length":1,"stats":{"Line":1}},{"line":162,"address":[],"length":0,"stats":{"Line":1}},{"line":163,"address":[],"length":0,"stats":{"Line":1}},{"line":184,"address":[],"length":0,"stats":{"Line":9}},{"line":188,"address":[],"length":0,"stats":{"Line":0}},{"line":191,"address":[],"length":0,"stats":{"Line":0}},{"line":194,"address":[],"length":0,"stats":{"Line":0}},{"line":197,"address":[],"length":0,"stats":{"Line":0}},{"line":201,"address":[],"length":0,"stats":{"Line":0}},{"line":212,"address":[],"length":0,"stats":{"Line":0}},{"line":213,"address":[],"length":0,"stats":{"Line":0}},{"line":217,"address":[],"length":0,"stats":{"Line":0}},{"line":220,"address":[],"length":0,"stats":{"Line":0}},{"line":224,"address":[],"length":0,"stats":{"Line":0}},{"line":248,"address":[],"length":0,"stats":{"Line":3}},{"line":252,"address":[],"length":0,"stats":{"Line":3}},{"line":255,"address":[],"length":0,"stats":{"Line":3}},{"line":291,"address":[],"length":0,"stats":{"Line":1}},{"line":293,"address":[],"length":0,"stats":{"Line":2}},{"line":294,"address":[],"length":0,"stats":{"Line":1}},{"line":298,"address":[678351,678393],"length":1,"stats":{"Line":2}},{"line":301,"address":[],"length":0,"stats":{"Line":1}},{"line":323,"address":[],"length":0,"stats":{"Line":0}},{"line":324,"address":[],"length":0,"stats":{"Line":0}},{"line":325,"address":[],"length":0,"stats":{"Line":0}},{"line":330,"address":[],"length":0,"stats":{"Line":0}},{"line":333,"address":[],"length":0,"stats":{"Line":0}},{"line":336,"address":[],"length":0,"stats":{"Line":0}},{"line":339,"address":[],"length":0,"stats":{"Line":0}},{"line":343,"address":[],"length":0,"stats":{"Line":0}},{"line":354,"address":[],"length":0,"stats":{"Line":0}},{"line":355,"address":[],"length":0,"stats":{"Line":0}},{"line":359,"address":[],"length":0,"stats":{"Line":0}},{"line":362,"address":[],"length":0,"stats":{"Line":0}},{"line":366,"address":[],"length":0,"stats":{"Line":0}},{"line":390,"address":[],"length":0,"stats":{"Line":1}},{"line":393,"address":[],"length":0,"stats":{"Line":1}},{"line":397,"address":[],"length":0,"stats":{"Line":1}},{"line":434,"address":[],"length":0,"stats":{"Line":0}},{"line":437,"address":[],"length":0,"stats":{"Line":0}},{"line":438,"address":[],"length":0,"stats":{"Line":0}},{"line":442,"address":[],"length":0,"stats":{"Line":0}},{"line":445,"address":[],"length":0,"stats":{"Line":0}},{"line":465,"address":[],"length":0,"stats":{"Line":0}},{"line":466,"address":[],"length":0,"stats":{"Line":0}},{"line":467,"address":[],"length":0,"stats":{"Line":0}},{"line":489,"address":[],"length":0,"stats":{"Line":0}},{"line":490,"address":[],"length":0,"stats":{"Line":0}},{"line":510,"address":[],"length":0,"stats":{"Line":0}},{"line":511,"address":[],"length":0,"stats":{"Line":0}},{"line":531,"address":[],"length":0,"stats":{"Line":2}},{"line":532,"address":[],"length":0,"stats":{"Line":2}},{"line":549,"address":[],"length":0,"stats":{"Line":2}},{"line":550,"address":[],"length":0,"stats":{"Line":2}}],"covered":40,"coverable":94},{"path":["/","home","botahamec","Projects","happylock","src","collection","ref.rs"],"content":"use std::fmt::Debug;\n\nuse crate::lockable::{Lockable, OwnedLockable, RawLock, Sharable};\nuse crate::{Keyable, ThreadKey};\n\nuse super::utils::{get_locks, ordered_contains_duplicates};\nuse super::{utils, LockGuard, RefLockCollection};\n\nimpl\u003c'a, L\u003e IntoIterator for \u0026'a RefLockCollection\u003c'a, L\u003e\nwhere\n\t\u0026'a L: IntoIterator,\n{\n\ttype Item = \u003c\u0026'a L as IntoIterator\u003e::Item;\n\ttype IntoIter = \u003c\u0026'a L as IntoIterator\u003e::IntoIter;\n\n\tfn into_iter(self) -\u003e Self::IntoIter {\n\t\tself.data.into_iter()\n\t}\n}\n\nunsafe impl\u003cL: Lockable\u003e RawLock for RefLockCollection\u003c'_, L\u003e {\n\t#[mutants::skip] // this should never run\n\t#[cfg(not(tarpaulin_include))]\n\tfn poison(\u0026self) {\n\t\tfor lock in \u0026self.locks {\n\t\t\tlock.poison();\n\t\t}\n\t}\n\n\tunsafe fn raw_lock(\u0026self) {\n\t\tutils::ordered_lock(\u0026self.locks)\n\t}\n\n\tunsafe fn raw_try_lock(\u0026self) -\u003e bool {\n\t\tutils::ordered_try_lock(\u0026self.locks)\n\t}\n\n\tunsafe fn raw_unlock(\u0026self) {\n\t\tfor lock in \u0026self.locks {\n\t\t\tlock.raw_unlock();\n\t\t}\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tutils::ordered_read(\u0026self.locks)\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tutils::ordered_try_read(\u0026self.locks)\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tfor lock in \u0026self.locks {\n\t\t\tlock.raw_unlock_read();\n\t\t}\n\t}\n}\n\nunsafe impl\u003cL: Lockable\u003e Lockable for RefLockCollection\u003c'_, L\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= L::Guard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= L::DataMut\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tptrs.extend_from_slice(\u0026self.locks);\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tself.data.guard()\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.data.data_mut()\n\t}\n}\n\nunsafe impl\u003cL: Sharable\u003e Sharable for RefLockCollection\u003c'_, L\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= L::ReadGuard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= L::DataRef\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tself.data.read_guard()\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tself.data.data_ref()\n\t}\n}\n\nimpl\u003cT, L: AsRef\u003cT\u003e\u003e AsRef\u003cT\u003e for RefLockCollection\u003c'_, L\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself.data.as_ref()\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cL: Debug\u003e Debug for RefLockCollection\u003c'_, L\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tf.debug_struct(stringify!(RefLockCollection))\n\t\t\t.field(\"data\", self.data)\n\t\t\t.finish_non_exhaustive()\n\t}\n}\n\n// safety: the RawLocks must be send because they come from the Send Lockable\n#[allow(clippy::non_send_fields_in_send_ty)]\nunsafe impl\u003cL: Send\u003e Send for RefLockCollection\u003c'_, L\u003e {}\nunsafe impl\u003cL: Sync\u003e Sync for RefLockCollection\u003c'_, L\u003e {}\n\nimpl\u003c'a, L: OwnedLockable + Default\u003e From\u003c\u0026'a L\u003e for RefLockCollection\u003c'a, L\u003e {\n\tfn from(value: \u0026'a L) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\nimpl\u003c'a, L: OwnedLockable\u003e RefLockCollection\u003c'a, L\u003e {\n\t/// Creates a new collection of owned locks.\n\t///\n\t/// Because the locks are owned, there's no need to do any checks for\n\t/// duplicate values.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t/// ```\n\t#[must_use]\n\tpub fn new(data: \u0026'a L) -\u003e Self {\n\t\tRefLockCollection {\n\t\t\tlocks: get_locks(data),\n\t\t\tdata,\n\t\t}\n\t}\n}\n\nimpl\u003cL\u003e RefLockCollection\u003c'_, L\u003e {\n\t/// Gets an immutable reference to the underlying data\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let data1 = Mutex::new(42);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // data1 and data2 refer to distinct mutexes, so this won't panic\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = RefLockCollection::try_new(\u0026data).unwrap();\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let guard = lock.child().0.lock(key);\n\t/// assert_eq!(*guard, 42);\n\t/// ```\n\t#[must_use]\n\tpub const fn child(\u0026self) -\u003e \u0026L {\n\t\tself.data\n\t}\n}\n\nimpl\u003c'a, L: Lockable\u003e RefLockCollection\u003c'a, L\u003e {\n\t/// Creates a new collections of locks.\n\t///\n\t/// # Safety\n\t///\n\t/// This results in undefined behavior if any locks are presented twice\n\t/// within this collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let data1 = Mutex::new(0);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // safety: data1 and data2 refer to distinct mutexes\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = unsafe { RefLockCollection::new_unchecked(\u0026data) };\n\t/// ```\n\t#[must_use]\n\tpub unsafe fn new_unchecked(data: \u0026'a L) -\u003e Self {\n\t\tSelf {\n\t\t\tdata,\n\t\t\tlocks: get_locks(data),\n\t\t}\n\t}\n\n\t/// Creates a new collection of locks.\n\t///\n\t/// This returns `None` if any locks are found twice in the given\n\t/// collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let data1 = Mutex::new(0);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // data1 and data2 refer to distinct mutexes, so this won't panic\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = RefLockCollection::try_new(\u0026data).unwrap();\n\t/// ```\n\t#[must_use]\n\tpub fn try_new(data: \u0026'a L) -\u003e Option\u003cSelf\u003e {\n\t\tlet locks = get_locks(data);\n\t\tif ordered_contains_duplicates(\u0026locks) {\n\t\t\treturn None;\n\t\t}\n\n\t\tSome(Self { data, locks })\n\t}\n\n\tpub fn scoped_lock\u003cR\u003e(\u0026self, key: impl Keyable, f: impl Fn(L::DataMut\u003c'_\u003e) -\u003e R) -\u003e R {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_lock();\n\n\t\t\t// safety: the data was just locked\n\t\t\tlet r = f(self.data_mut());\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock();\n\n\t\t\tdrop(key); // ensure the key stays alive long enough\n\n\t\t\tr\n\t\t}\n\t}\n\n\tpub fn scoped_try_lock\u003cKey: Keyable, R\u003e(\n\t\t\u0026self,\n\t\tkey: Key,\n\t\tf: impl Fn(L::DataMut\u003c'_\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_lock() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we just locked the collection\n\t\t\tlet r = f(self.data_mut());\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock();\n\n\t\t\tdrop(key); // ensures the key stays valid long enough\n\n\t\t\tOk(r)\n\t\t}\n\t}\n\n\t/// Locks the collection\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data. When the guard is dropped, the locks in the collection are also\n\t/// dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// ```\n\t#[must_use]\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::Guard\u003c'_\u003e\u003e {\n\t\tlet guard = unsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_lock();\n\n\t\t\t// safety: we've locked all of this already\n\t\t\tself.data.guard()\n\t\t};\n\n\t\tLockGuard { guard, key }\n\t}\n\n\t/// Attempts to lock the without blocking.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// locks when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If any of the locks in the collection are already locked, then an error\n\t/// is returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// match lock.try_lock(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::Guard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tlet guard = unsafe {\n\t\t\tif !self.raw_try_lock() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we've acquired the locks\n\t\t\tself.data.guard()\n\t\t};\n\n\t\tOk(LockGuard { guard, key })\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// let key = RefLockCollection::\u003c(Mutex\u003ci32\u003e, Mutex\u003c\u0026str\u003e)\u003e::unlock(guard);\n\t/// ```\n\t#[allow(clippy::missing_const_for_fn)]\n\tpub fn unlock(guard: LockGuard\u003cL::Guard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: Sharable\u003e RefLockCollection\u003c'_, L\u003e {\n\tpub fn scoped_read\u003cR\u003e(\u0026self, key: impl Keyable, f: impl Fn(L::DataRef\u003c'_\u003e) -\u003e R) -\u003e R {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_read();\n\n\t\t\t// safety: the data was just locked\n\t\t\tlet r = f(self.data_ref());\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock_read();\n\n\t\t\tdrop(key); // ensure the key stays alive long enough\n\n\t\t\tr\n\t\t}\n\t}\n\n\tpub fn scoped_try_read\u003cKey: Keyable, R\u003e(\n\t\t\u0026self,\n\t\tkey: Key,\n\t\tf: impl Fn(L::DataRef\u003c'_\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_read() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we just locked the collection\n\t\t\tlet r = f(self.data_ref());\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock_read();\n\n\t\t\tdrop(key); // ensures the key stays valid long enough\n\n\t\t\tOk(r)\n\t\t}\n\t}\n\n\t/// Locks the collection, so that other threads can still read from it\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data immutably. When the guard is dropped, the locks in the collection\n\t/// are also dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// assert_eq!(*guard.0, 0);\n\t/// assert_eq!(*guard.1, \"\");\n\t/// ```\n\t#[must_use]\n\tpub fn read(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_read();\n\n\t\t\tLockGuard {\n\t\t\t\t// safety: we've already acquired the lock\n\t\t\t\tguard: self.data.read_guard(),\n\t\t\t\tkey,\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Attempts to lock the without blocking, in such a way that other threads\n\t/// can still read from the collection.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// shared access when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If any of the locks in the collection are already locked, then an error\n\t/// is returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(5), RwLock::new(\"6\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// match lock.try_read(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// assert_eq!(*guard.0, 5);\n\t/// assert_eq!(*guard.1, \"6\");\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_read(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tlet guard = unsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_read() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we've acquired the locks\n\t\t\tself.data.read_guard()\n\t\t};\n\n\t\tOk(LockGuard { guard, key })\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// let key = RefLockCollection::\u003c(RwLock\u003ci32\u003e, RwLock\u003c\u0026str\u003e)\u003e::unlock_read(guard);\n\t/// ```\n\t#[allow(clippy::missing_const_for_fn)]\n\tpub fn unlock_read(guard: LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003c'a, L: 'a\u003e RefLockCollection\u003c'a, L\u003e\nwhere\n\t\u0026'a L: IntoIterator,\n{\n\t/// Returns an iterator over references to each value in the collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = [Mutex::new(26), Mutex::new(1)];\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// let mut iter = lock.iter();\n\t/// let mutex = iter.next().unwrap();\n\t/// let guard = mutex.lock(key);\n\t///\n\t/// assert_eq!(*guard, 26);\n\t/// ```\n\t#[must_use]\n\tpub fn iter(\u0026'a self) -\u003e \u003c\u0026'a L as IntoIterator\u003e::IntoIter {\n\t\tself.into_iter()\n\t}\n}\n\n#[cfg(test)]\nmod tests {\n\tuse super::*;\n\tuse crate::{Mutex, RwLock, ThreadKey};\n\n\t#[test]\n\tfn non_duplicates_allowed() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(1);\n\t\tassert!(RefLockCollection::try_new(\u0026[\u0026mutex1, \u0026mutex2]).is_some())\n\t}\n\n\t#[test]\n\tfn duplicates_not_allowed() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tassert!(RefLockCollection::try_new(\u0026[\u0026mutex1, \u0026mutex1]).is_none())\n\t}\n\n\t#[test]\n\tfn try_lock_succeeds_for_unlocked_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [Mutex::new(24), Mutex::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\t\tlet guard = collection.try_lock(key).unwrap();\n\t\tassert_eq!(*guard[0], 24);\n\t\tassert_eq!(*guard[1], 42);\n\t}\n\n\t#[test]\n\tfn try_lock_fails_for_locked_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [Mutex::new(24), Mutex::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = mutexes[1].lock(key);\n\t\t\t\tassert_eq!(*guard, 42);\n\t\t\t\tstd::mem::forget(guard);\n\t\t\t});\n\t\t});\n\n\t\tlet guard = collection.try_lock(key);\n\t\tassert!(guard.is_err());\n\t}\n\n\t#[test]\n\tfn try_read_succeeds_for_unlocked_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\t\tlet guard = collection.try_read(key).unwrap();\n\t\tassert_eq!(*guard[0], 24);\n\t\tassert_eq!(*guard[1], 42);\n\t}\n\n\t#[test]\n\tfn try_read_fails_for_locked_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = mutexes[1].write(key);\n\t\t\t\tassert_eq!(*guard, 42);\n\t\t\t\tstd::mem::forget(guard);\n\t\t\t});\n\t\t});\n\n\t\tlet guard = collection.try_read(key);\n\t\tassert!(guard.is_err());\n\t}\n\n\t#[test]\n\tfn can_read_twice_on_different_threads() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.read(key);\n\t\t\t\tassert_eq!(*guard[0], 24);\n\t\t\t\tassert_eq!(*guard[1], 42);\n\t\t\t\tstd::mem::forget(guard);\n\t\t\t});\n\t\t});\n\n\t\tlet guard = collection.try_read(key).unwrap();\n\t\tassert_eq!(*guard[0], 24);\n\t\tassert_eq!(*guard[1], 42);\n\t}\n\n\t#[test]\n\tfn works_in_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(1);\n\t\tlet collection0 = [\u0026mutex1, \u0026mutex2];\n\t\tlet collection1 = RefLockCollection::try_new(\u0026collection0).unwrap();\n\t\tlet collection = RefLockCollection::try_new(\u0026collection1).unwrap();\n\n\t\tlet guard = collection.lock(key);\n\t\tassert!(mutex1.is_locked());\n\t\tassert!(mutex2.is_locked());\n\t\tdrop(guard);\n\t}\n}\n","traces":[{"line":16,"address":[],"length":0,"stats":{"Line":0}},{"line":17,"address":[],"length":0,"stats":{"Line":0}},{"line":30,"address":[],"length":0,"stats":{"Line":1}},{"line":31,"address":[],"length":0,"stats":{"Line":1}},{"line":34,"address":[678688],"length":1,"stats":{"Line":1}},{"line":35,"address":[],"length":0,"stats":{"Line":1}},{"line":38,"address":[],"length":0,"stats":{"Line":0}},{"line":39,"address":[],"length":0,"stats":{"Line":0}},{"line":40,"address":[],"length":0,"stats":{"Line":0}},{"line":44,"address":[678720],"length":1,"stats":{"Line":1}},{"line":45,"address":[],"length":0,"stats":{"Line":1}},{"line":48,"address":[678752],"length":1,"stats":{"Line":1}},{"line":49,"address":[678757],"length":1,"stats":{"Line":1}},{"line":52,"address":[],"length":0,"stats":{"Line":0}},{"line":53,"address":[],"length":0,"stats":{"Line":0}},{"line":54,"address":[],"length":0,"stats":{"Line":0}},{"line":70,"address":[678784],"length":1,"stats":{"Line":1}},{"line":71,"address":[],"length":0,"stats":{"Line":1}},{"line":74,"address":[],"length":0,"stats":{"Line":1}},{"line":75,"address":[678849],"length":1,"stats":{"Line":1}},{"line":78,"address":[],"length":0,"stats":{"Line":0}},{"line":79,"address":[],"length":0,"stats":{"Line":0}},{"line":94,"address":[],"length":0,"stats":{"Line":0}},{"line":95,"address":[],"length":0,"stats":{"Line":0}},{"line":98,"address":[],"length":0,"stats":{"Line":0}},{"line":99,"address":[],"length":0,"stats":{"Line":0}},{"line":104,"address":[],"length":0,"stats":{"Line":0}},{"line":105,"address":[],"length":0,"stats":{"Line":0}},{"line":125,"address":[],"length":0,"stats":{"Line":0}},{"line":126,"address":[],"length":0,"stats":{"Line":0}},{"line":146,"address":[],"length":0,"stats":{"Line":4}},{"line":148,"address":[],"length":0,"stats":{"Line":3}},{"line":175,"address":[],"length":0,"stats":{"Line":0}},{"line":176,"address":[],"length":0,"stats":{"Line":0}},{"line":202,"address":[],"length":0,"stats":{"Line":0}},{"line":205,"address":[],"length":0,"stats":{"Line":0}},{"line":228,"address":[679408,679072,679372,679708],"length":1,"stats":{"Line":3}},{"line":229,"address":[],"length":0,"stats":{"Line":3}},{"line":230,"address":[679122,679186,679458,679522],"length":1,"stats":{"Line":8}},{"line":231,"address":[],"length":0,"stats":{"Line":1}},{"line":234,"address":[679553,679217],"length":1,"stats":{"Line":3}},{"line":237,"address":[],"length":0,"stats":{"Line":0}},{"line":240,"address":[],"length":0,"stats":{"Line":0}},{"line":243,"address":[],"length":0,"stats":{"Line":0}},{"line":246,"address":[],"length":0,"stats":{"Line":0}},{"line":250,"address":[],"length":0,"stats":{"Line":0}},{"line":261,"address":[],"length":0,"stats":{"Line":0}},{"line":262,"address":[],"length":0,"stats":{"Line":0}},{"line":266,"address":[],"length":0,"stats":{"Line":0}},{"line":269,"address":[],"length":0,"stats":{"Line":0}},{"line":273,"address":[],"length":0,"stats":{"Line":0}},{"line":298,"address":[],"length":0,"stats":{"Line":1}},{"line":301,"address":[],"length":0,"stats":{"Line":1}},{"line":304,"address":[],"length":0,"stats":{"Line":1}},{"line":340,"address":[],"length":0,"stats":{"Line":1}},{"line":342,"address":[],"length":0,"stats":{"Line":2}},{"line":343,"address":[679974],"length":1,"stats":{"Line":1}},{"line":347,"address":[],"length":0,"stats":{"Line":1}},{"line":350,"address":[],"length":0,"stats":{"Line":1}},{"line":372,"address":[],"length":0,"stats":{"Line":0}},{"line":373,"address":[],"length":0,"stats":{"Line":0}},{"line":374,"address":[],"length":0,"stats":{"Line":0}},{"line":379,"address":[],"length":0,"stats":{"Line":0}},{"line":382,"address":[],"length":0,"stats":{"Line":0}},{"line":385,"address":[],"length":0,"stats":{"Line":0}},{"line":388,"address":[],"length":0,"stats":{"Line":0}},{"line":392,"address":[],"length":0,"stats":{"Line":0}},{"line":403,"address":[],"length":0,"stats":{"Line":0}},{"line":404,"address":[],"length":0,"stats":{"Line":0}},{"line":408,"address":[],"length":0,"stats":{"Line":0}},{"line":411,"address":[],"length":0,"stats":{"Line":0}},{"line":415,"address":[],"length":0,"stats":{"Line":0}},{"line":440,"address":[],"length":0,"stats":{"Line":1}},{"line":443,"address":[],"length":0,"stats":{"Line":1}},{"line":447,"address":[],"length":0,"stats":{"Line":1}},{"line":484,"address":[],"length":0,"stats":{"Line":1}},{"line":487,"address":[],"length":0,"stats":{"Line":2}},{"line":488,"address":[],"length":0,"stats":{"Line":1}},{"line":492,"address":[],"length":0,"stats":{"Line":1}},{"line":495,"address":[],"length":0,"stats":{"Line":1}},{"line":515,"address":[],"length":0,"stats":{"Line":0}},{"line":516,"address":[],"length":0,"stats":{"Line":0}},{"line":517,"address":[],"length":0,"stats":{"Line":0}},{"line":544,"address":[],"length":0,"stats":{"Line":0}},{"line":545,"address":[],"length":0,"stats":{"Line":0}}],"covered":35,"coverable":85},{"path":["/","home","botahamec","Projects","happylock","src","collection","retry.rs"],"content":"use std::cell::Cell;\nuse std::collections::HashSet;\n\nuse crate::collection::utils;\nuse crate::handle_unwind::handle_unwind;\nuse crate::lockable::{\n\tLockable, LockableGetMut, LockableIntoInner, OwnedLockable, RawLock, Sharable,\n};\nuse crate::{Keyable, ThreadKey};\n\nuse super::utils::{\n\tattempt_to_recover_locks_from_panic, attempt_to_recover_reads_from_panic, get_locks_unsorted,\n};\nuse super::{LockGuard, RetryingLockCollection};\n\n/// Checks that a collection contains no duplicate references to a lock.\nfn contains_duplicates\u003cL: Lockable\u003e(data: L) -\u003e bool {\n\tlet mut locks = Vec::new();\n\tdata.get_ptrs(\u0026mut locks);\n\t// cast to *const () so that the v-table pointers are not used for hashing\n\tlet locks = locks.into_iter().map(|l| (\u0026raw const *l).cast::\u003c()\u003e());\n\n\tlet mut locks_set = HashSet::with_capacity(locks.len());\n\tfor lock in locks {\n\t\tif !locks_set.insert(lock) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tfalse\n}\n\nunsafe impl\u003cL: Lockable\u003e RawLock for RetryingLockCollection\u003cL\u003e {\n\t#[mutants::skip] // this should never run\n\t#[cfg(not(tarpaulin_include))]\n\tfn poison(\u0026self) {\n\t\tlet locks = get_locks_unsorted(\u0026self.data);\n\t\tfor lock in locks {\n\t\t\tlock.poison();\n\t\t}\n\t}\n\n\tunsafe fn raw_lock(\u0026self) {\n\t\tlet locks = get_locks_unsorted(\u0026self.data);\n\n\t\tif locks.is_empty() {\n\t\t\t// this probably prevents a panic later\n\t\t\treturn;\n\t\t}\n\n\t\t// these will be unlocked in case of a panic\n\t\tlet first_index = Cell::new(0);\n\t\tlet locked = Cell::new(0);\n\t\thandle_unwind(\n\t\t\t|| unsafe {\n\t\t\t\t'outer: loop {\n\t\t\t\t\t// This prevents us from entering a spin loop waiting for\n\t\t\t\t\t// the same lock to be unlocked\n\t\t\t\t\t// safety: we have the thread key\n\t\t\t\t\tlocks[first_index.get()].raw_lock();\n\t\t\t\t\tfor (i, lock) in locks.iter().enumerate() {\n\t\t\t\t\t\tif i == first_index.get() {\n\t\t\t\t\t\t\t// we've already locked this one\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// If the lock has been killed, then this returns false\n\t\t\t\t\t\t// instead of panicking. This sounds like a problem, but if\n\t\t\t\t\t\t// it does return false, then the lock function is called\n\t\t\t\t\t\t// immediately after, causing a panic\n\t\t\t\t\t\t// safety: we have the thread key\n\t\t\t\t\t\tif lock.raw_try_lock() {\n\t\t\t\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// safety: we already locked all of these\n\t\t\t\t\t\t\tattempt_to_recover_locks_from_panic(\u0026locks[0..i]);\n\t\t\t\t\t\t\tif first_index.get() \u003e= i {\n\t\t\t\t\t\t\t\t// safety: this is already locked and can't be\n\t\t\t\t\t\t\t\t// unlocked by the previous loop\n\t\t\t\t\t\t\t\tlocks[first_index.get()].raw_unlock();\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// nothing is locked anymore\n\t\t\t\t\t\t\tlocked.set(0);\n\n\t\t\t\t\t\t\t// call lock on this to prevent a spin loop\n\t\t\t\t\t\t\tfirst_index.set(i);\n\t\t\t\t\t\t\tcontinue 'outer;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// safety: we locked all the data\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t},\n\t\t\t|| {\n\t\t\t\tutils::attempt_to_recover_locks_from_panic(\u0026locks[0..locked.get()]);\n\t\t\t\tif first_index.get() \u003e= locked.get() {\n\t\t\t\t\tlocks[first_index.get()].raw_unlock();\n\t\t\t\t}\n\t\t\t},\n\t\t)\n\t}\n\n\tunsafe fn raw_try_lock(\u0026self) -\u003e bool {\n\t\tlet locks = get_locks_unsorted(\u0026self.data);\n\n\t\tif locks.is_empty() {\n\t\t\t// this is an interesting case, but it doesn't give us access to\n\t\t\t// any data, and can't possibly cause a deadlock\n\t\t\treturn true;\n\t\t}\n\n\t\t// these will be unlocked in case of a panic\n\t\tlet locked = Cell::new(0);\n\t\thandle_unwind(\n\t\t\t|| unsafe {\n\t\t\t\tfor (i, lock) in locks.iter().enumerate() {\n\t\t\t\t\t// safety: we have the thread key\n\t\t\t\t\tif lock.raw_try_lock() {\n\t\t\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// safety: we already locked all of these\n\t\t\t\t\t\tattempt_to_recover_locks_from_panic(\u0026locks[0..i]);\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ttrue\n\t\t\t},\n\t\t\t|| utils::attempt_to_recover_locks_from_panic(\u0026locks[0..locked.get()]),\n\t\t)\n\t}\n\n\tunsafe fn raw_unlock(\u0026self) {\n\t\tlet locks = get_locks_unsorted(\u0026self.data);\n\n\t\tfor lock in locks {\n\t\t\tlock.raw_unlock();\n\t\t}\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tlet locks = get_locks_unsorted(\u0026self.data);\n\n\t\tif locks.is_empty() {\n\t\t\t// this probably prevents a panic later\n\t\t\treturn;\n\t\t}\n\n\t\tlet locked = Cell::new(0);\n\t\tlet first_index = Cell::new(0);\n\t\thandle_unwind(\n\t\t\t|| 'outer: loop {\n\t\t\t\t// safety: we have the thread key\n\t\t\t\tlocks[first_index.get()].raw_read();\n\t\t\t\tfor (i, lock) in locks.iter().enumerate() {\n\t\t\t\t\tif i == first_index.get() {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// safety: we have the thread key\n\t\t\t\t\tif lock.raw_try_read() {\n\t\t\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// safety: we already locked all of these\n\t\t\t\t\t\tattempt_to_recover_reads_from_panic(\u0026locks[0..i]);\n\n\t\t\t\t\t\tif first_index.get() \u003e= i {\n\t\t\t\t\t\t\t// safety: this is already locked and can't be unlocked\n\t\t\t\t\t\t\t// by the previous loop\n\t\t\t\t\t\t\tlocks[first_index.get()].raw_unlock_read();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// these are no longer locked\n\t\t\t\t\t\tlocked.set(0);\n\n\t\t\t\t\t\t// don't go into a spin loop, wait for this one to lock\n\t\t\t\t\t\tfirst_index.set(i);\n\t\t\t\t\t\tcontinue 'outer;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// safety: we locked all the data\n\t\t\t\tbreak;\n\t\t\t},\n\t\t\t|| {\n\t\t\t\tutils::attempt_to_recover_reads_from_panic(\u0026locks[0..locked.get()]);\n\t\t\t\tif first_index.get() \u003e= locked.get() {\n\t\t\t\t\tlocks[first_index.get()].raw_unlock_read();\n\t\t\t\t}\n\t\t\t},\n\t\t)\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tlet locks = get_locks_unsorted(\u0026self.data);\n\n\t\tif locks.is_empty() {\n\t\t\t// this is an interesting case, but it doesn't give us access to\n\t\t\t// any data, and can't possibly cause a deadlock\n\t\t\treturn true;\n\t\t}\n\n\t\tlet locked = Cell::new(0);\n\t\thandle_unwind(\n\t\t\t|| unsafe {\n\t\t\t\tfor (i, lock) in locks.iter().enumerate() {\n\t\t\t\t\t// safety: we have the thread key\n\t\t\t\t\tif lock.raw_try_read() {\n\t\t\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// safety: we already locked all of these\n\t\t\t\t\t\tattempt_to_recover_reads_from_panic(\u0026locks[0..i]);\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ttrue\n\t\t\t},\n\t\t\t|| utils::attempt_to_recover_reads_from_panic(\u0026locks[0..locked.get()]),\n\t\t)\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tlet locks = get_locks_unsorted(\u0026self.data);\n\n\t\tfor lock in locks {\n\t\t\tlock.raw_unlock_read();\n\t\t}\n\t}\n}\n\nunsafe impl\u003cL: Lockable\u003e Lockable for RetryingLockCollection\u003cL\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= L::Guard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= L::DataMut\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tself.data.get_ptrs(ptrs)\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tself.data.guard()\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.data.data_mut()\n\t}\n}\n\nunsafe impl\u003cL: Sharable\u003e Sharable for RetryingLockCollection\u003cL\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= L::ReadGuard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= L::DataRef\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tself.data.read_guard()\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tself.data.data_ref()\n\t}\n}\n\nunsafe impl\u003cL: OwnedLockable\u003e OwnedLockable for RetryingLockCollection\u003cL\u003e {}\n\nimpl\u003cL: LockableGetMut\u003e LockableGetMut for RetryingLockCollection\u003cL\u003e {\n\ttype Inner\u003c'a\u003e\n\t\t= L::Inner\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tself.data.get_mut()\n\t}\n}\n\nimpl\u003cL: LockableIntoInner\u003e LockableIntoInner for RetryingLockCollection\u003cL\u003e {\n\ttype Inner = L::Inner;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tself.data.into_inner()\n\t}\n}\n\nimpl\u003cL\u003e IntoIterator for RetryingLockCollection\u003cL\u003e\nwhere\n\tL: IntoIterator,\n{\n\ttype Item = \u003cL as IntoIterator\u003e::Item;\n\ttype IntoIter = \u003cL as IntoIterator\u003e::IntoIter;\n\n\tfn into_iter(self) -\u003e Self::IntoIter {\n\t\tself.data.into_iter()\n\t}\n}\n\nimpl\u003c'a, L\u003e IntoIterator for \u0026'a RetryingLockCollection\u003cL\u003e\nwhere\n\t\u0026'a L: IntoIterator,\n{\n\ttype Item = \u003c\u0026'a L as IntoIterator\u003e::Item;\n\ttype IntoIter = \u003c\u0026'a L as IntoIterator\u003e::IntoIter;\n\n\tfn into_iter(self) -\u003e Self::IntoIter {\n\t\tself.data.into_iter()\n\t}\n}\n\nimpl\u003c'a, L\u003e IntoIterator for \u0026'a mut RetryingLockCollection\u003cL\u003e\nwhere\n\t\u0026'a mut L: IntoIterator,\n{\n\ttype Item = \u003c\u0026'a mut L as IntoIterator\u003e::Item;\n\ttype IntoIter = \u003c\u0026'a mut L as IntoIterator\u003e::IntoIter;\n\n\tfn into_iter(self) -\u003e Self::IntoIter {\n\t\tself.data.into_iter()\n\t}\n}\n\nimpl\u003cL: OwnedLockable, I: FromIterator\u003cL\u003e + OwnedLockable\u003e FromIterator\u003cL\u003e\n\tfor RetryingLockCollection\u003cI\u003e\n{\n\tfn from_iter\u003cT: IntoIterator\u003cItem = L\u003e\u003e(iter: T) -\u003e Self {\n\t\tlet iter: I = iter.into_iter().collect();\n\t\tSelf::new(iter)\n\t}\n}\n\nimpl\u003cE: OwnedLockable + Extend\u003cL\u003e, L: OwnedLockable\u003e Extend\u003cL\u003e for RetryingLockCollection\u003cE\u003e {\n\tfn extend\u003cT: IntoIterator\u003cItem = L\u003e\u003e(\u0026mut self, iter: T) {\n\t\tself.data.extend(iter)\n\t}\n}\n\nimpl\u003cT, L: AsRef\u003cT\u003e\u003e AsRef\u003cT\u003e for RetryingLockCollection\u003cL\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself.data.as_ref()\n\t}\n}\n\nimpl\u003cT, L: AsMut\u003cT\u003e\u003e AsMut\u003cT\u003e for RetryingLockCollection\u003cL\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself.data.as_mut()\n\t}\n}\n\nimpl\u003cL: OwnedLockable + Default\u003e Default for RetryingLockCollection\u003cL\u003e {\n\tfn default() -\u003e Self {\n\t\tSelf::new(L::default())\n\t}\n}\n\nimpl\u003cL: OwnedLockable\u003e From\u003cL\u003e for RetryingLockCollection\u003cL\u003e {\n\tfn from(value: L) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\nimpl\u003cL: OwnedLockable\u003e RetryingLockCollection\u003cL\u003e {\n\t/// Creates a new collection of owned locks.\n\t///\n\t/// Because the locks are owned, there's no need to do any checks for\n\t/// duplicate values. The locks also don't need to be sorted by memory\n\t/// address because they aren't used anywhere else.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t/// ```\n\t#[must_use]\n\tpub const fn new(data: L) -\u003e Self {\n\t\tSelf { data }\n\t}\n}\n\nimpl\u003c'a, L: OwnedLockable\u003e RetryingLockCollection\u003c\u0026'a L\u003e {\n\t/// Creates a new collection of owned locks.\n\t///\n\t/// Because the locks are owned, there's no need to do any checks for\n\t/// duplicate values.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new_ref(\u0026data);\n\t/// ```\n\t#[must_use]\n\tpub const fn new_ref(data: \u0026'a L) -\u003e Self {\n\t\tSelf { data }\n\t}\n}\n\nimpl\u003cL\u003e RetryingLockCollection\u003cL\u003e {\n\t/// Creates a new collections of locks.\n\t///\n\t/// # Safety\n\t///\n\t/// This results in undefined behavior if any locks are presented twice\n\t/// within this collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let data1 = Mutex::new(0);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // safety: data1 and data2 refer to distinct mutexes\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = unsafe { RetryingLockCollection::new_unchecked(\u0026data) };\n\t/// ```\n\t#[must_use]\n\tpub const unsafe fn new_unchecked(data: L) -\u003e Self {\n\t\tSelf { data }\n\t}\n\n\t/// Gets an immutable reference to the underlying collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let data = (Mutex::new(42), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let inner = lock.child();\n\t/// let guard = inner.0.lock(key);\n\t/// assert_eq!(*guard, 42);\n\t/// ```\n\t#[must_use]\n\tpub const fn child(\u0026self) -\u003e \u0026L {\n\t\t\u0026self.data\n\t}\n\n\t/// Gets a mutable reference to the underlying collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let data = (Mutex::new(42), Mutex::new(\"\"));\n\t/// let mut lock = RetryingLockCollection::new(data);\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut inner = lock.child_mut();\n\t/// let guard = inner.0.get_mut();\n\t/// assert_eq!(*guard, 42);\n\t/// ```\n\t#[must_use]\n\tpub fn child_mut(\u0026mut self) -\u003e \u0026mut L {\n\t\t\u0026mut self.data\n\t}\n\n\t/// Gets the underlying collection, consuming this collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let data = (Mutex::new(42), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let inner = lock.into_child();\n\t/// let guard = inner.0.lock(key);\n\t/// assert_eq!(*guard, 42);\n\t/// ```\n\t#[must_use]\n\tpub fn into_child(self) -\u003e L {\n\t\tself.data\n\t}\n}\n\nimpl\u003cL: Lockable\u003e RetryingLockCollection\u003cL\u003e {\n\t/// Creates a new collection of locks.\n\t///\n\t/// This returns `None` if any locks are found twice in the given\n\t/// collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let data1 = Mutex::new(0);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // data1 and data2 refer to distinct mutexes, so this won't panic\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = RetryingLockCollection::try_new(\u0026data).unwrap();\n\t/// ```\n\t#[must_use]\n\tpub fn try_new(data: L) -\u003e Option\u003cSelf\u003e {\n\t\t(!contains_duplicates(\u0026data)).then_some(Self { data })\n\t}\n\n\tpub fn scoped_lock\u003cR\u003e(\u0026self, key: impl Keyable, f: impl Fn(L::DataMut\u003c'_\u003e) -\u003e R) -\u003e R {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_lock();\n\n\t\t\t// safety: the data was just locked\n\t\t\tlet r = f(self.data_mut());\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock();\n\n\t\t\tdrop(key); // ensure the key stays alive long enough\n\n\t\t\tr\n\t\t}\n\t}\n\n\tpub fn scoped_try_lock\u003cKey: Keyable, R\u003e(\n\t\t\u0026self,\n\t\tkey: Key,\n\t\tf: impl Fn(L::DataMut\u003c'_\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_lock() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we just locked the collection\n\t\t\tlet r = f(self.data_mut());\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock();\n\n\t\t\tdrop(key); // ensures the key stays valid long enough\n\n\t\t\tOk(r)\n\t\t}\n\t}\n\n\t/// Locks the collection\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data. When the guard is dropped, the locks in the collection are also\n\t/// dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// ```\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::Guard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\t// safety: we're taking the thread key\n\t\t\tself.raw_lock();\n\n\t\t\tLockGuard {\n\t\t\t\t// safety: we just locked the collection\n\t\t\t\tguard: self.guard(),\n\t\t\t\tkey,\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Attempts to lock the without blocking.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// locks when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If any of the locks in the collection are already locked, then an error\n\t/// is returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// match lock.try_lock(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::Guard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tunsafe {\n\t\t\t// safety: we're taking the thread key\n\t\t\tif self.raw_try_lock() {\n\t\t\t\tOk(LockGuard {\n\t\t\t\t\t// safety: we just succeeded in locking everything\n\t\t\t\t\tguard: self.guard(),\n\t\t\t\t\tkey,\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\tErr(key)\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// let key = RetryingLockCollection::\u003c(Mutex\u003ci32\u003e, Mutex\u003c\u0026str\u003e)\u003e::unlock(guard);\n\t/// ```\n\tpub fn unlock(guard: LockGuard\u003cL::Guard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: Sharable\u003e RetryingLockCollection\u003cL\u003e {\n\tpub fn scoped_read\u003cR\u003e(\u0026self, key: impl Keyable, f: impl Fn(L::DataRef\u003c'_\u003e) -\u003e R) -\u003e R {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_read();\n\n\t\t\t// safety: the data was just locked\n\t\t\tlet r = f(self.data_ref());\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock_read();\n\n\t\t\tdrop(key); // ensure the key stays alive long enough\n\n\t\t\tr\n\t\t}\n\t}\n\n\tpub fn scoped_try_read\u003cKey: Keyable, R\u003e(\n\t\t\u0026self,\n\t\tkey: Key,\n\t\tf: impl Fn(L::DataRef\u003c'_\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_read() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we just locked the collection\n\t\t\tlet r = f(self.data_ref());\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock_read();\n\n\t\t\tdrop(key); // ensures the key stays valid long enough\n\n\t\t\tOk(r)\n\t\t}\n\t}\n\n\t/// Locks the collection, so that other threads can still read from it\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data immutably. When the guard is dropped, the locks in the collection\n\t/// are also dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// assert_eq!(*guard.0, 0);\n\t/// assert_eq!(*guard.1, \"\");\n\t/// ```\n\tpub fn read(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\t// safety: we're taking the thread key\n\t\t\tself.raw_read();\n\n\t\t\tLockGuard {\n\t\t\t\t// safety: we just locked the collection\n\t\t\t\tguard: self.read_guard(),\n\t\t\t\tkey,\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Attempts to lock the without blocking, in such a way that other threads\n\t/// can still read from the collection.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// shared access when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If shared access cannot be acquired at this time, then an error is\n\t/// returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(5), RwLock::new(\"6\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// match lock.try_read(key) {\n\t/// Some(mut guard) =\u003e {\n\t/// assert_eq!(*guard.0, 5);\n\t/// assert_eq!(*guard.1, \"6\");\n\t/// },\n\t/// None =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_read(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tunsafe {\n\t\t\t// safety: we're taking the thread key\n\t\t\tif !self.raw_try_lock() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\tOk(LockGuard {\n\t\t\t\t// safety: we just succeeded in locking everything\n\t\t\t\tguard: self.read_guard(),\n\t\t\t\tkey,\n\t\t\t})\n\t\t}\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// let key = RetryingLockCollection::\u003c(RwLock\u003ci32\u003e, RwLock\u003c\u0026str\u003e)\u003e::unlock_read(guard);\n\t/// ```\n\tpub fn unlock_read(guard: LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: LockableGetMut\u003e RetryingLockCollection\u003cL\u003e {\n\t/// Gets a mutable reference to the data behind this\n\t/// `RetryingLockCollection`.\n\t///\n\t/// Since this call borrows the `RetryingLockCollection` mutably, no actual\n\t/// locking needs to take place - the mutable borrow statically guarantees\n\t/// no locks exist.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let mut mutex = RetryingLockCollection::new([Mutex::new(0), Mutex::new(0)]);\n\t/// assert_eq!(mutex.get_mut(), [\u0026mut 0, \u0026mut 0]);\n\t/// ```\n\tpub fn get_mut(\u0026mut self) -\u003e L::Inner\u003c'_\u003e {\n\t\tLockableGetMut::get_mut(self)\n\t}\n}\n\nimpl\u003cL: LockableIntoInner\u003e RetryingLockCollection\u003cL\u003e {\n\t/// Consumes this `RetryingLockCollection`, returning the underlying data.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let mutex = RetryingLockCollection::new([Mutex::new(0), Mutex::new(0)]);\n\t/// assert_eq!(mutex.into_inner(), [0, 0]);\n\t/// ```\n\tpub fn into_inner(self) -\u003e L::Inner {\n\t\tLockableIntoInner::into_inner(self)\n\t}\n}\n\nimpl\u003c'a, L: 'a\u003e RetryingLockCollection\u003cL\u003e\nwhere\n\t\u0026'a L: IntoIterator,\n{\n\t/// Returns an iterator over references to each value in the collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = [Mutex::new(26), Mutex::new(1)];\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let mut iter = lock.iter();\n\t/// let mutex = iter.next().unwrap();\n\t/// let guard = mutex.lock(key);\n\t///\n\t/// assert_eq!(*guard, 26);\n\t/// ```\n\t#[must_use]\n\tpub fn iter(\u0026'a self) -\u003e \u003c\u0026'a L as IntoIterator\u003e::IntoIter {\n\t\tself.into_iter()\n\t}\n}\n\nimpl\u003c'a, L: 'a\u003e RetryingLockCollection\u003cL\u003e\nwhere\n\t\u0026'a mut L: IntoIterator,\n{\n\t/// Returns an iterator over mutable references to each value in the\n\t/// collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = [Mutex::new(26), Mutex::new(1)];\n\t/// let mut lock = RetryingLockCollection::new(data);\n\t///\n\t/// let mut iter = lock.iter_mut();\n\t/// let mutex = iter.next().unwrap();\n\t///\n\t/// assert_eq!(*mutex.as_mut(), 26);\n\t/// ```\n\t#[must_use]\n\tpub fn iter_mut(\u0026'a mut self) -\u003e \u003c\u0026'a mut L as IntoIterator\u003e::IntoIter {\n\t\tself.into_iter()\n\t}\n}\n\n#[cfg(test)]\nmod tests {\n\tuse super::*;\n\tuse crate::collection::BoxedLockCollection;\n\tuse crate::{LockCollection, Mutex, RwLock, ThreadKey};\n\n\t#[test]\n\tfn nonduplicate_lock_references_are_allowed() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(0);\n\t\tassert!(RetryingLockCollection::try_new([\u0026mutex1, \u0026mutex2]).is_some());\n\t}\n\n\t#[test]\n\tfn duplicate_lock_references_are_disallowed() {\n\t\tlet mutex = Mutex::new(0);\n\t\tassert!(RetryingLockCollection::try_new([\u0026mutex, \u0026mutex]).is_none());\n\t}\n\n\t#[test]\n\tfn locks_all_inner_mutexes() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(0);\n\t\tlet collection = RetryingLockCollection::try_new([\u0026mutex1, \u0026mutex2]).unwrap();\n\n\t\tlet guard = collection.lock(key);\n\n\t\tassert!(mutex1.is_locked());\n\t\tassert!(mutex2.is_locked());\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn locks_all_inner_rwlocks() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet rwlock1 = RwLock::new(0);\n\t\tlet rwlock2 = RwLock::new(0);\n\t\tlet collection = RetryingLockCollection::try_new([\u0026rwlock1, \u0026rwlock2]).unwrap();\n\n\t\tlet guard = collection.read(key);\n\n\t\tassert!(rwlock1.is_locked());\n\t\tassert!(rwlock2.is_locked());\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn works_with_other_collections() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(0);\n\t\tlet collection = BoxedLockCollection::try_new(\n\t\t\tRetryingLockCollection::try_new([\u0026mutex1, \u0026mutex2]).unwrap(),\n\t\t)\n\t\t.unwrap();\n\n\t\tlet guard = collection.lock(key);\n\n\t\tassert!(mutex1.is_locked());\n\t\tassert!(mutex2.is_locked());\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn extend_collection() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(0);\n\t\tlet mut collection = RetryingLockCollection::new(vec![mutex1]);\n\n\t\tcollection.extend([mutex2]);\n\n\t\tassert_eq!(collection.into_inner().len(), 2);\n\t}\n\n\t#[test]\n\tfn lock_empty_lock_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection: RetryingLockCollection\u003c[RwLock\u003ci32\u003e; 0]\u003e = RetryingLockCollection::new([]);\n\n\t\tlet guard = collection.lock(key);\n\t\tassert!(guard.len() == 0);\n\t\tlet key = LockCollection::\u003c[RwLock\u003c_\u003e; 0]\u003e::unlock(guard);\n\n\t\tlet guard = collection.read(key);\n\t\tassert!(guard.len() == 0);\n\t}\n}\n","traces":[{"line":17,"address":[202728,202688,203413],"length":1,"stats":{"Line":11}},{"line":18,"address":[175916],"length":1,"stats":{"Line":12}},{"line":19,"address":[203787],"length":1,"stats":{"Line":11}},{"line":21,"address":[204508,203850,204480],"length":1,"stats":{"Line":35}},{"line":23,"address":[171870,171936],"length":1,"stats":{"Line":23}},{"line":24,"address":[],"length":0,"stats":{"Line":46}},{"line":25,"address":[],"length":0,"stats":{"Line":23}},{"line":26,"address":[],"length":0,"stats":{"Line":1}},{"line":30,"address":[185952],"length":1,"stats":{"Line":12}},{"line":43,"address":[183208,182960],"length":1,"stats":{"Line":6}},{"line":44,"address":[182988],"length":1,"stats":{"Line":6}},{"line":46,"address":[156198,156250],"length":1,"stats":{"Line":12}},{"line":48,"address":[],"length":0,"stats":{"Line":0}},{"line":52,"address":[183056,183093],"length":1,"stats":{"Line":10}},{"line":53,"address":[183098],"length":1,"stats":{"Line":5}},{"line":55,"address":[231533],"length":1,"stats":{"Line":10}},{"line":56,"address":[],"length":0,"stats":{"Line":0}},{"line":60,"address":[152001],"length":1,"stats":{"Line":5}},{"line":61,"address":[152054,152206],"length":1,"stats":{"Line":10}},{"line":62,"address":[185048],"length":1,"stats":{"Line":5}},{"line":64,"address":[],"length":0,"stats":{"Line":0}},{"line":72,"address":[242328],"length":1,"stats":{"Line":5}},{"line":73,"address":[543962,544666,544106,544522],"length":1,"stats":{"Line":8}},{"line":76,"address":[152304],"length":1,"stats":{"Line":1}},{"line":77,"address":[205316],"length":1,"stats":{"Line":1}},{"line":80,"address":[185251],"length":1,"stats":{"Line":1}},{"line":84,"address":[152407],"length":1,"stats":{"Line":1}},{"line":87,"address":[205407],"length":1,"stats":{"Line":1}},{"line":88,"address":[],"length":0,"stats":{"Line":0}},{"line":93,"address":[],"length":0,"stats":{"Line":0}},{"line":96,"address":[213526],"length":1,"stats":{"Line":6}},{"line":97,"address":[204793],"length":1,"stats":{"Line":1}},{"line":98,"address":[241924],"length":1,"stats":{"Line":1}},{"line":99,"address":[204905],"length":1,"stats":{"Line":1}},{"line":105,"address":[156141,155952],"length":1,"stats":{"Line":3}},{"line":106,"address":[],"length":0,"stats":{"Line":3}},{"line":108,"address":[156030,155984],"length":1,"stats":{"Line":6}},{"line":111,"address":[204648],"length":1,"stats":{"Line":0}},{"line":115,"address":[242356,242398],"length":1,"stats":{"Line":6}},{"line":117,"address":[151376],"length":1,"stats":{"Line":3}},{"line":118,"address":[],"length":0,"stats":{"Line":6}},{"line":120,"address":[],"length":0,"stats":{"Line":3}},{"line":121,"address":[151644],"length":1,"stats":{"Line":3}},{"line":124,"address":[202484],"length":1,"stats":{"Line":1}},{"line":125,"address":[151637],"length":1,"stats":{"Line":1}},{"line":129,"address":[151528],"length":1,"stats":{"Line":0}},{"line":131,"address":[202608,202622],"length":1,"stats":{"Line":4}},{"line":135,"address":[],"length":0,"stats":{"Line":1}},{"line":136,"address":[155426,155698],"length":1,"stats":{"Line":1}},{"line":138,"address":[],"length":0,"stats":{"Line":3}},{"line":139,"address":[155912,155640],"length":1,"stats":{"Line":1}},{"line":143,"address":[],"length":0,"stats":{"Line":4}},{"line":144,"address":[211884],"length":1,"stats":{"Line":4}},{"line":146,"address":[],"length":0,"stats":{"Line":8}},{"line":148,"address":[],"length":0,"stats":{"Line":0}},{"line":151,"address":[681093,681328,681365,681056],"length":1,"stats":{"Line":6}},{"line":152,"address":[],"length":0,"stats":{"Line":3}},{"line":154,"address":[151469],"length":1,"stats":{"Line":6}},{"line":156,"address":[171105],"length":1,"stats":{"Line":3}},{"line":157,"address":[203198,203046],"length":1,"stats":{"Line":6}},{"line":158,"address":[203240],"length":1,"stats":{"Line":3}},{"line":159,"address":[],"length":0,"stats":{"Line":0}},{"line":163,"address":[],"length":0,"stats":{"Line":3}},{"line":164,"address":[],"length":0,"stats":{"Line":4}},{"line":167,"address":[171408],"length":1,"stats":{"Line":1}},{"line":169,"address":[203332],"length":1,"stats":{"Line":1}},{"line":172,"address":[545518,546078],"length":1,"stats":{"Line":0}},{"line":176,"address":[545474,546034],"length":1,"stats":{"Line":1}},{"line":179,"address":[171535],"length":1,"stats":{"Line":1}},{"line":180,"address":[],"length":0,"stats":{"Line":0}},{"line":185,"address":[],"length":0,"stats":{"Line":0}},{"line":187,"address":[546176,546352],"length":1,"stats":{"Line":4}},{"line":188,"address":[546361,546185],"length":1,"stats":{"Line":1}},{"line":189,"address":[546419,546243],"length":1,"stats":{"Line":1}},{"line":190,"address":[171033],"length":1,"stats":{"Line":1}},{"line":196,"address":[],"length":0,"stats":{"Line":0}},{"line":197,"address":[],"length":0,"stats":{"Line":0}},{"line":199,"address":[],"length":0,"stats":{"Line":0}},{"line":202,"address":[],"length":0,"stats":{"Line":0}},{"line":205,"address":[],"length":0,"stats":{"Line":0}},{"line":207,"address":[],"length":0,"stats":{"Line":0}},{"line":208,"address":[],"length":0,"stats":{"Line":0}},{"line":210,"address":[],"length":0,"stats":{"Line":0}},{"line":211,"address":[],"length":0,"stats":{"Line":0}},{"line":214,"address":[],"length":0,"stats":{"Line":0}},{"line":215,"address":[],"length":0,"stats":{"Line":0}},{"line":219,"address":[],"length":0,"stats":{"Line":0}},{"line":221,"address":[],"length":0,"stats":{"Line":0}},{"line":225,"address":[],"length":0,"stats":{"Line":0}},{"line":226,"address":[],"length":0,"stats":{"Line":0}},{"line":228,"address":[],"length":0,"stats":{"Line":0}},{"line":229,"address":[],"length":0,"stats":{"Line":0}},{"line":245,"address":[],"length":0,"stats":{"Line":1}},{"line":246,"address":[],"length":0,"stats":{"Line":1}},{"line":249,"address":[204752],"length":1,"stats":{"Line":4}},{"line":250,"address":[681553,681573],"length":1,"stats":{"Line":4}},{"line":253,"address":[],"length":0,"stats":{"Line":1}},{"line":254,"address":[156481,156449],"length":1,"stats":{"Line":1}},{"line":269,"address":[],"length":0,"stats":{"Line":3}},{"line":270,"address":[681601,681621],"length":1,"stats":{"Line":3}},{"line":273,"address":[],"length":0,"stats":{"Line":0}},{"line":274,"address":[],"length":0,"stats":{"Line":0}},{"line":286,"address":[],"length":0,"stats":{"Line":0}},{"line":287,"address":[],"length":0,"stats":{"Line":0}},{"line":294,"address":[],"length":0,"stats":{"Line":1}},{"line":295,"address":[],"length":0,"stats":{"Line":1}},{"line":306,"address":[],"length":0,"stats":{"Line":0}},{"line":307,"address":[],"length":0,"stats":{"Line":0}},{"line":318,"address":[],"length":0,"stats":{"Line":0}},{"line":319,"address":[],"length":0,"stats":{"Line":0}},{"line":330,"address":[],"length":0,"stats":{"Line":0}},{"line":331,"address":[],"length":0,"stats":{"Line":0}},{"line":338,"address":[],"length":0,"stats":{"Line":0}},{"line":339,"address":[],"length":0,"stats":{"Line":0}},{"line":340,"address":[],"length":0,"stats":{"Line":0}},{"line":345,"address":[],"length":0,"stats":{"Line":1}},{"line":346,"address":[],"length":0,"stats":{"Line":1}},{"line":351,"address":[],"length":0,"stats":{"Line":0}},{"line":352,"address":[],"length":0,"stats":{"Line":0}},{"line":357,"address":[],"length":0,"stats":{"Line":0}},{"line":358,"address":[],"length":0,"stats":{"Line":0}},{"line":363,"address":[],"length":0,"stats":{"Line":0}},{"line":364,"address":[],"length":0,"stats":{"Line":0}},{"line":369,"address":[],"length":0,"stats":{"Line":0}},{"line":370,"address":[],"length":0,"stats":{"Line":0}},{"line":391,"address":[],"length":0,"stats":{"Line":1}},{"line":412,"address":[],"length":0,"stats":{"Line":0}},{"line":439,"address":[],"length":0,"stats":{"Line":0}},{"line":460,"address":[],"length":0,"stats":{"Line":0}},{"line":461,"address":[],"length":0,"stats":{"Line":0}},{"line":481,"address":[],"length":0,"stats":{"Line":0}},{"line":482,"address":[],"length":0,"stats":{"Line":0}},{"line":502,"address":[],"length":0,"stats":{"Line":0}},{"line":503,"address":[],"length":0,"stats":{"Line":0}},{"line":527,"address":[242512,242668],"length":1,"stats":{"Line":11}},{"line":528,"address":[151785,151844],"length":1,"stats":{"Line":24}},{"line":531,"address":[156496,156685],"length":1,"stats":{"Line":1}},{"line":534,"address":[],"length":0,"stats":{"Line":1}},{"line":537,"address":[156574],"length":1,"stats":{"Line":1}},{"line":540,"address":[156623],"length":1,"stats":{"Line":1}},{"line":542,"address":[156656],"length":1,"stats":{"Line":1}},{"line":544,"address":[],"length":0,"stats":{"Line":0}},{"line":548,"address":[156704,156913],"length":1,"stats":{"Line":1}},{"line":555,"address":[156773,156727],"length":1,"stats":{"Line":2}},{"line":556,"address":[156784],"length":1,"stats":{"Line":1}},{"line":560,"address":[156805,156822],"length":1,"stats":{"Line":0}},{"line":563,"address":[156853],"length":1,"stats":{"Line":0}},{"line":565,"address":[156883],"length":1,"stats":{"Line":0}},{"line":567,"address":[156895],"length":1,"stats":{"Line":0}},{"line":591,"address":[],"length":0,"stats":{"Line":5}},{"line":594,"address":[183294],"length":1,"stats":{"Line":5}},{"line":598,"address":[],"length":0,"stats":{"Line":4}},{"line":634,"address":[204960,205152],"length":1,"stats":{"Line":1}},{"line":637,"address":[],"length":0,"stats":{"Line":1}},{"line":638,"address":[],"length":0,"stats":{"Line":0}},{"line":640,"address":[205072],"length":1,"stats":{"Line":0}},{"line":641,"address":[],"length":0,"stats":{"Line":0}},{"line":644,"address":[],"length":0,"stats":{"Line":0}},{"line":667,"address":[],"length":0,"stats":{"Line":0}},{"line":668,"address":[],"length":0,"stats":{"Line":0}},{"line":669,"address":[],"length":0,"stats":{"Line":0}},{"line":674,"address":[],"length":0,"stats":{"Line":0}},{"line":677,"address":[],"length":0,"stats":{"Line":0}},{"line":680,"address":[],"length":0,"stats":{"Line":0}},{"line":683,"address":[],"length":0,"stats":{"Line":0}},{"line":687,"address":[],"length":0,"stats":{"Line":0}},{"line":698,"address":[],"length":0,"stats":{"Line":0}},{"line":699,"address":[],"length":0,"stats":{"Line":0}},{"line":703,"address":[],"length":0,"stats":{"Line":0}},{"line":706,"address":[],"length":0,"stats":{"Line":0}},{"line":710,"address":[],"length":0,"stats":{"Line":0}},{"line":734,"address":[151600,151726],"length":1,"stats":{"Line":4}},{"line":737,"address":[682510,682384],"length":1,"stats":{"Line":4}},{"line":741,"address":[212238],"length":1,"stats":{"Line":3}},{"line":778,"address":[],"length":0,"stats":{"Line":1}},{"line":781,"address":[],"length":0,"stats":{"Line":1}},{"line":782,"address":[],"length":0,"stats":{"Line":0}},{"line":785,"address":[242822],"length":1,"stats":{"Line":0}},{"line":787,"address":[],"length":0,"stats":{"Line":0}},{"line":788,"address":[],"length":0,"stats":{"Line":0}},{"line":809,"address":[],"length":0,"stats":{"Line":0}},{"line":810,"address":[],"length":0,"stats":{"Line":0}},{"line":811,"address":[],"length":0,"stats":{"Line":0}},{"line":832,"address":[],"length":0,"stats":{"Line":0}},{"line":833,"address":[],"length":0,"stats":{"Line":0}},{"line":849,"address":[],"length":0,"stats":{"Line":1}},{"line":850,"address":[],"length":0,"stats":{"Line":1}},{"line":877,"address":[],"length":0,"stats":{"Line":0}},{"line":878,"address":[],"length":0,"stats":{"Line":0}},{"line":905,"address":[],"length":0,"stats":{"Line":0}},{"line":906,"address":[],"length":0,"stats":{"Line":0}}],"covered":98,"coverable":191},{"path":["/","home","botahamec","Projects","happylock","src","collection","utils.rs"],"content":"use std::cell::Cell;\n\nuse crate::handle_unwind::handle_unwind;\nuse crate::lockable::{Lockable, RawLock};\n\n#[must_use]\npub fn get_locks\u003cL: Lockable\u003e(data: \u0026L) -\u003e Vec\u003c\u0026dyn RawLock\u003e {\n\tlet mut locks = Vec::new();\n\tdata.get_ptrs(\u0026mut locks);\n\tlocks.sort_by_key(|lock| \u0026raw const **lock);\n\tlocks\n}\n\n#[must_use]\npub fn get_locks_unsorted\u003cL: Lockable\u003e(data: \u0026L) -\u003e Vec\u003c\u0026dyn RawLock\u003e {\n\tlet mut locks = Vec::new();\n\tdata.get_ptrs(\u0026mut locks);\n\tlocks\n}\n\n/// returns `true` if the sorted list contains a duplicate\n#[must_use]\npub fn ordered_contains_duplicates(l: \u0026[\u0026dyn RawLock]) -\u003e bool {\n\tif l.is_empty() {\n\t\t// Return early to prevent panic in the below call to `windows`\n\t\treturn false;\n\t}\n\n\tl.windows(2)\n\t\t// NOTE: addr_eq is necessary because eq would also compare the v-table pointers\n\t\t.any(|window| std::ptr::addr_eq(window[0], window[1]))\n}\n\n/// Lock a set of locks in the given order. It's UB to call this without a `ThreadKey`\npub unsafe fn ordered_lock(locks: \u0026[\u0026dyn RawLock]) {\n\t// these will be unlocked in case of a panic\n\tlet locked = Cell::new(0);\n\n\thandle_unwind(\n\t\t|| {\n\t\t\tfor lock in locks {\n\t\t\t\tlock.raw_lock();\n\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t}\n\t\t},\n\t\t|| attempt_to_recover_locks_from_panic(\u0026locks[0..locked.get()]),\n\t)\n}\n\n/// Lock a set of locks in the given order. It's UB to call this without a `ThreadKey`\npub unsafe fn ordered_read(locks: \u0026[\u0026dyn RawLock]) {\n\tlet locked = Cell::new(0);\n\n\thandle_unwind(\n\t\t|| {\n\t\t\tfor lock in locks {\n\t\t\t\tlock.raw_read();\n\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t}\n\t\t},\n\t\t|| attempt_to_recover_reads_from_panic(\u0026locks[0..locked.get()]),\n\t)\n}\n\n/// Locks the locks in the order they are given. This causes deadlock if the\n/// locks contain duplicates, or if this is called by multiple threads with the\n/// locks in different orders.\npub unsafe fn ordered_try_lock(locks: \u0026[\u0026dyn RawLock]) -\u003e bool {\n\tlet locked = Cell::new(0);\n\n\thandle_unwind(\n\t\t|| unsafe {\n\t\t\tfor (i, lock) in locks.iter().enumerate() {\n\t\t\t\t// safety: we have the thread key\n\t\t\t\tif lock.raw_try_lock() {\n\t\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t\t} else {\n\t\t\t\t\tfor lock in \u0026locks[0..i] {\n\t\t\t\t\t\t// safety: this lock was already acquired\n\t\t\t\t\t\tlock.raw_unlock();\n\t\t\t\t\t}\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttrue\n\t\t},\n\t\t||\n\t\t// safety: everything in locked is locked\n\t\tattempt_to_recover_locks_from_panic(\u0026locks[0..locked.get()]),\n\t)\n}\n\n/// Locks the locks in the order they are given. This causes deadlock if this\n/// is called by multiple threads with the locks in different orders.\npub unsafe fn ordered_try_read(locks: \u0026[\u0026dyn RawLock]) -\u003e bool {\n\t// these will be unlocked in case of a panic\n\tlet locked = Cell::new(0);\n\n\thandle_unwind(\n\t\t|| unsafe {\n\t\t\tfor (i, lock) in locks.iter().enumerate() {\n\t\t\t\t// safety: we have the thread key\n\t\t\t\tif lock.raw_try_read() {\n\t\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t\t} else {\n\t\t\t\t\tfor lock in \u0026locks[0..i] {\n\t\t\t\t\t\t// safety: this lock was already acquired\n\t\t\t\t\t\tlock.raw_unlock_read();\n\t\t\t\t\t}\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttrue\n\t\t},\n\t\t||\n\t\t// safety: everything in locked is locked\n\t\tattempt_to_recover_reads_from_panic(\u0026locks[0..locked.get()]),\n\t)\n}\n\n/// Unlocks the already locked locks in order to recover from a panic\npub unsafe fn attempt_to_recover_locks_from_panic(locks: \u0026[\u0026dyn RawLock]) {\n\thandle_unwind(\n\t\t|| {\n\t\t\t// safety: the caller assumes that these are already locked\n\t\t\tlocks.iter().for_each(|lock| lock.raw_unlock());\n\t\t},\n\t\t// if we get another panic in here, we'll just have to poison what remains\n\t\t|| locks.iter().for_each(|l| l.poison()),\n\t)\n}\n\n/// Unlocks the already locked locks in order to recover from a panic\npub unsafe fn attempt_to_recover_reads_from_panic(locked: \u0026[\u0026dyn RawLock]) {\n\thandle_unwind(\n\t\t|| {\n\t\t\t// safety: the caller assumes these are already locked\n\t\t\tlocked.iter().for_each(|lock| lock.raw_unlock_read());\n\t\t},\n\t\t// if we get another panic in here, we'll just have to poison what remains\n\t\t|| locked.iter().for_each(|l| l.poison()),\n\t)\n}\n\n#[cfg(test)]\nmod tests {\n\tuse crate::collection::utils::ordered_contains_duplicates;\n\n\t#[test]\n\tfn empty_array_does_not_contain_duplicates() {\n\t\tassert!(!ordered_contains_duplicates(\u0026[]))\n\t}\n}\n","traces":[{"line":7,"address":[641844,641104,641652,641296,641488,641460,641268,641680],"length":1,"stats":{"Line":7}},{"line":8,"address":[],"length":0,"stats":{"Line":5}},{"line":9,"address":[],"length":0,"stats":{"Line":7}},{"line":10,"address":[],"length":0,"stats":{"Line":17}},{"line":11,"address":[],"length":0,"stats":{"Line":8}},{"line":15,"address":[232656,232781],"length":1,"stats":{"Line":16}},{"line":16,"address":[151170,151026],"length":1,"stats":{"Line":16}},{"line":17,"address":[170689],"length":1,"stats":{"Line":16}},{"line":18,"address":[137870],"length":1,"stats":{"Line":16}},{"line":23,"address":[537680],"length":1,"stats":{"Line":9}},{"line":24,"address":[525560],"length":1,"stats":{"Line":9}},{"line":26,"address":[941331],"length":1,"stats":{"Line":1}},{"line":29,"address":[551468],"length":1,"stats":{"Line":9}},{"line":31,"address":[538941,539059,538912],"length":1,"stats":{"Line":28}},{"line":35,"address":[941360],"length":1,"stats":{"Line":4}},{"line":37,"address":[541662],"length":1,"stats":{"Line":5}},{"line":40,"address":[643200],"length":1,"stats":{"Line":6}},{"line":41,"address":[543049,542974],"length":1,"stats":{"Line":12}},{"line":42,"address":[448195],"length":1,"stats":{"Line":6}},{"line":43,"address":[539213],"length":1,"stats":{"Line":5}},{"line":46,"address":[543159,543349,543152],"length":1,"stats":{"Line":14}},{"line":51,"address":[537888],"length":1,"stats":{"Line":2}},{"line":52,"address":[941470],"length":1,"stats":{"Line":2}},{"line":55,"address":[527376],"length":1,"stats":{"Line":2}},{"line":56,"address":[553353,553278],"length":1,"stats":{"Line":4}},{"line":57,"address":[643697],"length":1,"stats":{"Line":2}},{"line":58,"address":[544397],"length":1,"stats":{"Line":2}},{"line":61,"address":[512229,512032,512039],"length":1,"stats":{"Line":5}},{"line":68,"address":[537984],"length":1,"stats":{"Line":2}},{"line":69,"address":[510327],"length":1,"stats":{"Line":2}},{"line":72,"address":[542799],"length":1,"stats":{"Line":5}},{"line":73,"address":[545341,545199],"length":1,"stats":{"Line":6}},{"line":75,"address":[539778],"length":1,"stats":{"Line":3}},{"line":76,"address":[554288,554154],"length":1,"stats":{"Line":6}},{"line":78,"address":[528030,528362,528301,528237],"length":1,"stats":{"Line":4}},{"line":80,"address":[644580],"length":1,"stats":{"Line":1}},{"line":82,"address":[644554],"length":1,"stats":{"Line":1}},{"line":86,"address":[540086],"length":1,"stats":{"Line":2}},{"line":88,"address":[544432],"length":1,"stats":{"Line":3}},{"line":90,"address":[544439],"length":1,"stats":{"Line":1}},{"line":96,"address":[454704],"length":1,"stats":{"Line":2}},{"line":98,"address":[543367],"length":1,"stats":{"Line":2}},{"line":101,"address":[537791],"length":1,"stats":{"Line":4}},{"line":102,"address":[513135,513277],"length":1,"stats":{"Line":4}},{"line":104,"address":[540642],"length":1,"stats":{"Line":2}},{"line":105,"address":[513728,513594],"length":1,"stats":{"Line":2}},{"line":107,"address":[545226,544894,545165,545101],"length":1,"stats":{"Line":4}},{"line":109,"address":[546634],"length":1,"stats":{"Line":1}},{"line":111,"address":[541008],"length":1,"stats":{"Line":1}},{"line":115,"address":[449942],"length":1,"stats":{"Line":1}},{"line":117,"address":[542931],"length":1,"stats":{"Line":3}},{"line":119,"address":[513957,513767],"length":1,"stats":{"Line":2}},{"line":124,"address":[454816],"length":1,"stats":{"Line":7}},{"line":126,"address":[555408],"length":1,"stats":{"Line":8}},{"line":128,"address":[541360,541326,541374],"length":1,"stats":{"Line":20}},{"line":131,"address":[555536,555488,555502,555550],"length":1,"stats":{"Line":4}},{"line":136,"address":[543504],"length":1,"stats":{"Line":3}},{"line":138,"address":[555568],"length":1,"stats":{"Line":4}},{"line":140,"address":[547120,547134,547086],"length":1,"stats":{"Line":8}},{"line":143,"address":[450910,450958,450944,450896],"length":1,"stats":{"Line":4}}],"covered":60,"coverable":60},{"path":["/","home","botahamec","Projects","happylock","src","collection.rs"],"content":"use std::cell::UnsafeCell;\n\nuse crate::{lockable::RawLock, ThreadKey};\n\nmod boxed;\nmod guard;\nmod owned;\nmod r#ref;\nmod retry;\nmod utils;\n\n/// Locks a collection of locks, which cannot be shared immutably.\n///\n/// This could be a tuple of [`Lockable`] types, an array, or a `Vec`. But it\n/// can be safely locked without causing a deadlock.\n///\n/// The data in this collection is guaranteed to not contain duplicates because\n/// `L` must always implement [`OwnedLockable`]. The underlying data may not be\n/// immutably referenced and locked. Because of this, there is no need for\n/// sorting the locks in the collection, or checking for duplicates, because it\n/// can be guaranteed that until the underlying collection is mutated (which\n/// requires releasing all acquired locks in the collection to do), then the\n/// locks will stay in the same order and be locked in that order, preventing\n/// cyclic wait.\n///\n/// [`Lockable`]: `crate::lockable::Lockable`\n/// [`OwnedLockable`]: `crate::lockable::OwnedLockable`\n\n// this type caches the idea that no immutable references to the underlying\n// collection exist\n#[derive(Debug)]\npub struct OwnedLockCollection\u003cL\u003e {\n\tdata: L,\n}\n\n/// Locks a reference to a collection of locks, by sorting them by memory\n/// address.\n///\n/// This could be a tuple of [`Lockable`] types, an array, or a `Vec`. But it\n/// can be safely locked without causing a deadlock.\n///\n/// Upon construction, it must be confirmed that the collection contains no\n/// duplicate locks. This can be done by either using [`OwnedLockable`] or by\n/// checking. Regardless of how this is done, the locks will be sorted by their\n/// memory address before locking them. The sorted order of the locks is stored\n/// within this collection.\n///\n/// Unlike [`BoxedLockCollection`], this type does not allocate memory for the\n/// data, although it does allocate memory for the sorted list of lock\n/// references. This makes it slightly faster, but lifetimes must be handled.\n///\n/// [`Lockable`]: `crate::lockable::Lockable`\n/// [`OwnedLockable`]: `crate::lockable::OwnedLockable`\n//\n// This type was born when I eventually realized that I needed a self\n// referential structure. That used boxing, so I elected to make a more\n// efficient implementation (polonius please save us)\n//\n// This type caches the sorting order of the locks and the fact that it doesn't\n// contain any duplicates.\npub struct RefLockCollection\u003c'a, L\u003e {\n\tdata: \u0026'a L,\n\tlocks: Vec\u003c\u0026'a dyn RawLock\u003e,\n}\n\n/// Locks a collection of locks, stored in the heap, by sorting them by memory\n/// address.\n///\n/// This could be a tuple of [`Lockable`] types, an array, or a `Vec`. But it\n/// can be safely locked without causing a deadlock.\n///\n/// Upon construction, it must be confirmed that the collection contains no\n/// duplicate locks. This can be done by either using [`OwnedLockable`] or by\n/// checking. Regardless of how this is done, the locks will be sorted by their\n/// memory address before locking them. The sorted order of the locks is stored\n/// within this collection.\n///\n/// Unlike [`RefLockCollection`], this is a self-referential type which boxes\n/// the data that is given to it. This means no lifetimes are necessary on the\n/// type itself, but it is slightly slower because of the memory allocation.\n///\n/// [`Lockable`]: `crate::lockable::Lockable`\n/// [`OwnedLockable`]: `crate::lockable::OwnedLockable`\n//\n// This type caches the sorting order of the locks and the fact that it doesn't\n// contain any duplicates.\npub struct BoxedLockCollection\u003cL\u003e {\n\tdata: *const UnsafeCell\u003cL\u003e,\n\tlocks: Vec\u003c\u0026'static dyn RawLock\u003e,\n}\n\n/// Locks a collection of locks using a retrying algorithm.\n///\n/// This could be a tuple of [`Lockable`] types, an array, or a `Vec`. But it\n/// can be safely locked without causing a deadlock.\n///\n/// The data in this collection is guaranteed to not contain duplicates, but it\n/// also not be sorted. In some cases the lack of sorting can increase\n/// performance. However, in most cases, this collection will be slower. Cyclic\n/// wait is not guaranteed here, so the locking algorithm must release all its\n/// locks if one of the lock attempts blocks. This results in wasted time and\n/// potential [livelocking].\n///\n/// However, one case where this might be faster than [`RefLockCollection`] is\n/// when the first lock in the collection is always the first in any\n/// collection, and the other locks in the collection are always locked after\n/// that first lock is acquired. This means that as soon as it is locked, there\n/// will be no need to unlock it later on subsequent lock attempts, because\n/// they will always succeed.\n///\n/// [`Lockable`]: `crate::lockable::Lockable`\n/// [`OwnedLockable`]: `crate::lockable::OwnedLockable`\n/// [livelocking]: https://en.wikipedia.org/wiki/Deadlock#Livelock\n//\n// This type caches the fact that there are no duplicates\n#[derive(Debug)]\npub struct RetryingLockCollection\u003cL\u003e {\n\tdata: L,\n}\n\n/// A RAII guard for a generic [`Lockable`] type.\n///\n/// [`Lockable`]: `crate::lockable::Lockable`\npub struct LockGuard\u003cGuard\u003e {\n\tguard: Guard,\n\tkey: ThreadKey,\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","src","handle_unwind.rs"],"content":"use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};\n\n/// Runs `try_fn`. If it unwinds, it will run `catch` and then continue\n/// unwinding. This is used instead of `scopeguard` to ensure the `catch`\n/// function doesn't run if the thread is already panicking. The unwind\n/// must specifically be caused by the `try_fn`\npub fn handle_unwind\u003cR, F: FnOnce() -\u003e R, G: FnOnce()\u003e(try_fn: F, catch: G) -\u003e R {\n\tlet try_fn = AssertUnwindSafe(try_fn);\n\tcatch_unwind(try_fn).unwrap_or_else(|e| {\n\t\tcatch();\n\t\tresume_unwind(e)\n\t})\n}\n","traces":[{"line":7,"address":[550136,549288,549808,549984,549472,549622,549648,549794,549458,549136,549312,549958],"length":1,"stats":{"Line":149}},{"line":8,"address":[203778,204601,204034,204162,204290,203506,204443,204889,205026,205154,203641,203906,204745],"length":1,"stats":{"Line":121}},{"line":9,"address":[205041,203521,205353,204177,205507,204233,206377,205648,203977,203698,204305,204946,205609,204361,206032,206288,204049,205993,205379,205520,205264,203661,203793,204909,205225,205763,205635,205097,205481,205392,205776,204658,205891,206121,206160,206275,206403,206532,203849,205169,206889,206633,204468,206544,206508,206761,203921,206416,206800,206147,206249,205737,205865,204802,204105,204765,206787,203577,206659,206915,204510,206672,206019,205904,204621],"length":1,"stats":{"Line":307}},{"line":10,"address":[560749,560215,560061,560487,560605,560333],"length":1,"stats":{"Line":28}},{"line":11,"address":[551901,552173,552317,551629,551761,552033],"length":1,"stats":{"Line":24}}],"covered":5,"coverable":5},{"path":["/","home","botahamec","Projects","happylock","src","key.rs"],"content":"use std::cell::{Cell, LazyCell};\nuse std::fmt::{self, Debug};\nuse std::marker::PhantomData;\n\nuse sealed::Sealed;\n\n// Sealed to prevent other key types from being implemented. Otherwise, this\n// would almost instant undefined behavior.\nmod sealed {\n\tuse super::ThreadKey;\n\n\tpub trait Sealed {}\n\timpl Sealed for ThreadKey {}\n\timpl Sealed for \u0026mut ThreadKey {}\n}\n\nthread_local! {\n\tstatic KEY: LazyCell\u003cKeyCell\u003e = LazyCell::new(KeyCell::default);\n}\n\n/// The key for the current thread.\n///\n/// Only one of these exist per thread. To get the current thread's key, call\n/// [`ThreadKey::get`]. If the `ThreadKey` is dropped, it can be re-obtained.\npub struct ThreadKey {\n\tphantom: PhantomData\u003c*const ()\u003e, // implement !Send and !Sync\n}\n\n/// Allows the type to be used as a key for a lock\n///\n/// # Safety\n///\n/// Only one value which implements this trait may be allowed to exist at a\n/// time. Creating a new `Keyable` value requires making any other `Keyable`\n/// values invalid.\npub unsafe trait Keyable: Sealed {}\nunsafe impl Keyable for ThreadKey {}\n// the ThreadKey can't be moved while a mutable reference to it exists\nunsafe impl Keyable for \u0026mut ThreadKey {}\n\n// Implementing this means we can allow `MutexGuard` to be Sync\n// Safety: a \u0026ThreadKey is useless by design.\nunsafe impl Sync for ThreadKey {}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl Debug for ThreadKey {\n\tfn fmt(\u0026self, f: \u0026mut fmt::Formatter\u003c'_\u003e) -\u003e fmt::Result {\n\t\twrite!(f, \"ThreadKey\")\n\t}\n}\n\n// If you lose the thread key, you can get it back by calling ThreadKey::get\nimpl Drop for ThreadKey {\n\tfn drop(\u0026mut self) {\n\t\t// safety: a thread key cannot be acquired without creating the lock\n\t\t// safety: the key is lost, so it's safe to unlock the cell\n\t\tunsafe { KEY.with(|key| key.force_unlock()) }\n\t}\n}\n\nimpl ThreadKey {\n\t/// Get the current thread's `ThreadKey`, if it's not already taken.\n\t///\n\t/// The first time this is called, it will successfully return a\n\t/// `ThreadKey`. However, future calls to this function on the same thread\n\t/// will return [`None`], unless the key is dropped or unlocked first.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::ThreadKey;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// ```\n\t#[must_use]\n\tpub fn get() -\u003e Option\u003cSelf\u003e {\n\t\t// safety: we just acquired the lock\n\t\t// safety: if this code changes, check to ensure the requirement for\n\t\t// the Drop implementation is still true\n\t\tKEY.with(|key| {\n\t\t\tkey.try_lock().then_some(Self {\n\t\t\t\tphantom: PhantomData,\n\t\t\t})\n\t\t})\n\t}\n}\n\n/// A dumb lock that's just a wrapper for an [`AtomicBool`].\n#[derive(Default)]\nstruct KeyCell {\n\tis_locked: Cell\u003cbool\u003e,\n}\n\nimpl KeyCell {\n\t/// Attempt to lock the `KeyCell`. This is not a fair lock.\n\t#[must_use]\n\tpub fn try_lock(\u0026self) -\u003e bool {\n\t\t!self.is_locked.replace(true)\n\t}\n\n\t/// Forcibly unlocks the `KeyCell`. This should only be called if the key\n\t/// from this `KeyCell` has been \"lost\".\n\tpub unsafe fn force_unlock(\u0026self) {\n\t\tself.is_locked.set(false);\n\t}\n}\n\n#[cfg(test)]\nmod tests {\n\tuse super::*;\n\n\t#[test]\n\tfn thread_key_returns_some_on_first_call() {\n\t\tassert!(ThreadKey::get().is_some());\n\t}\n\n\t#[test]\n\tfn thread_key_returns_none_on_second_call() {\n\t\tlet key = ThreadKey::get();\n\t\tassert!(ThreadKey::get().is_none());\n\t\tdrop(key);\n\t}\n\n\t#[test]\n\tfn dropping_thread_key_allows_reobtaining() {\n\t\tdrop(ThreadKey::get());\n\t\tassert!(ThreadKey::get().is_some())\n\t}\n}\n","traces":[{"line":18,"address":[539432],"length":1,"stats":{"Line":26}},{"line":55,"address":[551392],"length":1,"stats":{"Line":12}},{"line":58,"address":[525509],"length":1,"stats":{"Line":39}},{"line":77,"address":[506896],"length":1,"stats":{"Line":26}},{"line":81,"address":[556240],"length":1,"stats":{"Line":44}},{"line":82,"address":[514825],"length":1,"stats":{"Line":27}},{"line":98,"address":[539376],"length":1,"stats":{"Line":27}},{"line":99,"address":[539861],"length":1,"stats":{"Line":27}},{"line":104,"address":[539408],"length":1,"stats":{"Line":13}},{"line":105,"address":[439589],"length":1,"stats":{"Line":13}}],"covered":10,"coverable":10},{"path":["/","home","botahamec","Projects","happylock","src","lib.rs"],"content":"#![warn(clippy::pedantic)]\n#![warn(clippy::nursery)]\n#![allow(clippy::module_name_repetitions)]\n#![allow(clippy::declare_interior_mutable_const)]\n#![allow(clippy::semicolon_if_nothing_returned)]\n#![allow(clippy::module_inception)]\n#![allow(clippy::single_match_else)]\n\n//! As it turns out, the Rust borrow checker is powerful enough that, if the\n//! standard library supported it, we could've made deadlocks undefined\n//! behavior. This library currently serves as a proof of concept for how that\n//! would work.\n//!\n//! # Theory\n//!\n//! There are four conditions necessary for a deadlock to occur. In order to\n//! prevent deadlocks, we just need to prevent one of the following:\n//!\n//! 1. mutual exclusion\n//! 2. non-preemptive allocation\n//! 3. circular wait\n//! 4. **partial allocation**\n//!\n//! This library seeks to solve **partial allocation** by requiring total\n//! allocation. All the resources a thread needs must be allocated at the same\n//! time. In order to request new resources, the old resources must be dropped\n//! first. Requesting multiple resources at once is atomic. You either get all\n//! the requested resources or none at all.\n//!\n//! As an optimization, this library also often prevents **circular wait**.\n//! Many collections sort the locks in order of their memory address. As long\n//! as the locks are always acquired in that order, then time doesn't need to\n//! be wasted on releasing locks after a failure and re-acquiring them later.\n//!\n//! # Examples\n//!\n//! Simple example:\n//! ```\n//! use std::thread;\n//! use happylock::{Mutex, ThreadKey};\n//!\n//! const N: usize = 10;\n//!\n//! static DATA: Mutex\u003ci32\u003e = Mutex::new(0);\n//!\n//! for _ in 0..N {\n//! thread::spawn(move || {\n//! // each thread gets one thread key\n//! let key = ThreadKey::get().unwrap();\n//!\n//! // unlocking a mutex requires a ThreadKey\n//! let mut data = DATA.lock(key);\n//! *data += 1;\n//!\n//! // the key is unlocked at the end of the scope\n//! });\n//! }\n//!\n//! let key = ThreadKey::get().unwrap();\n//! let data = DATA.lock(key);\n//! println!(\"{}\", *data);\n//! ```\n//!\n//! To lock multiple mutexes at a time, create a [`LockCollection`]:\n//!\n//! ```\n//! use std::thread;\n//! use happylock::{LockCollection, Mutex, ThreadKey};\n//!\n//! const N: usize = 10;\n//!\n//! static DATA_1: Mutex\u003ci32\u003e = Mutex::new(0);\n//! static DATA_2: Mutex\u003cString\u003e = Mutex::new(String::new());\n//!\n//! for _ in 0..N {\n//! thread::spawn(move || {\n//! let key = ThreadKey::get().unwrap();\n//!\n//! // happylock ensures at runtime there are no duplicate locks\n//! let collection = LockCollection::try_new((\u0026DATA_1, \u0026DATA_2)).unwrap();\n//! let mut guard = collection.lock(key);\n//!\n//! *guard.1 = (100 - *guard.0).to_string();\n//! *guard.0 += 1;\n//! });\n//! }\n//!\n//! let key = ThreadKey::get().unwrap();\n//! let data = LockCollection::try_new((\u0026DATA_1, \u0026DATA_2)).unwrap();\n//! let data = data.lock(key);\n//! println!(\"{}\", *data.0);\n//! println!(\"{}\", *data.1);\n//! ```\n//!\n//! In many cases, the [`LockCollection::new`] or [`LockCollection::new_ref`]\n//! method can be used, improving performance.\n//!\n//! ```rust\n//! use std::thread;\n//! use happylock::{LockCollection, Mutex, ThreadKey};\n//!\n//! const N: usize = 32;\n//!\n//! static DATA: [Mutex\u003ci32\u003e; 2] = [Mutex::new(0), Mutex::new(1)];\n//!\n//! for _ in 0..N {\n//! thread::spawn(move || {\n//! let key = ThreadKey::get().unwrap();\n//!\n//! // a reference to a type that implements `OwnedLockable` will never\n//! // contain duplicates, so no duplicate checking is needed.\n//! let collection = LockCollection::new_ref(\u0026DATA);\n//! let mut guard = collection.lock(key);\n//!\n//! let x = *guard[1];\n//! *guard[1] += *guard[0];\n//! *guard[0] = x;\n//! });\n//! }\n//!\n//! let key = ThreadKey::get().unwrap();\n//! let data = LockCollection::new_ref(\u0026DATA);\n//! let data = data.lock(key);\n//! println!(\"{}\", data[0]);\n//! println!(\"{}\", data[1]);\n//! ```\n//!\n//! # Performance\n//!\n//! **The `ThreadKey` is a mostly-zero cost abstraction.** It doesn't use any\n//! memory, and it doesn't really exist at run-time. The only cost comes from\n//! calling `ThreadKey::get()`, because the function has to ensure at runtime\n//! that the key hasn't already been taken. Dropping the key will also have a\n//! small cost.\n//!\n//! **Consider [`OwnedLockCollection`].** This will almost always be the\n//! fastest lock collection. It doesn't expose the underlying collection\n//! immutably, which means that it will always be locked in the same order, and\n//! doesn't need any sorting.\n//!\n//! **Avoid [`LockCollection::try_new`].** This constructor will check to make\n//! sure that the collection contains no duplicate locks. In most cases, this\n//! is O(nlogn), where n is the number of locks in the collections but in the\n//! case of [`RetryingLockCollection`], it's close to O(n).\n//! [`LockCollection::new`] and [`LockCollection::new_ref`] don't need these\n//! checks because they use [`OwnedLockable`], which is guaranteed to be unique\n//! as long as it is accessible. As a last resort,\n//! [`LockCollection::new_unchecked`] doesn't do this check, but is unsafe to\n//! call.\n//!\n//! **Know how to use [`RetryingLockCollection`].** This collection doesn't do\n//! any sorting, but uses a wasteful lock algorithm. It can't rely on the order\n//! of the locks to be the same across threads, so if it finds a lock that it\n//! can't acquire without blocking, it'll first release all of the locks it\n//! already acquired to avoid blocking other threads. This is wasteful because\n//! this algorithm may end up re-acquiring the same lock multiple times. To\n//! avoid this, ensure that (1) the first lock in the collection is always the\n//! first lock in any collection it appears in, and (2) the other locks in the\n//! collection are always preceded by that first lock. This will prevent any\n//! wasted time from re-acquiring locks. If you're unsure, [`LockCollection`]\n//! is a sensible default.\n//!\n//! [`OwnedLockable`]: `lockable::OwnedLockable`\n//! [`OwnedLockCollection`]: `collection::OwnedLockCollection`\n//! [`RetryingLockCollection`]: `collection::RetryingLockCollection`\n\nmod handle_unwind;\nmod key;\n\npub mod collection;\npub mod lockable;\npub mod mutex;\npub mod poisonable;\npub mod rwlock;\n\npub use key::{Keyable, ThreadKey};\n\n#[cfg(feature = \"spin\")]\npub use mutex::SpinLock;\n\n// Personally, I think re-exports look ugly in the rust documentation, so I\n// went with type aliases instead.\n\n/// A collection of locks that can be acquired simultaneously.\n///\n/// This re-exports [`BoxedLockCollection`] as a sensible default.\n///\n/// [`BoxedLockCollection`]: collection::BoxedLockCollection\npub type LockCollection\u003cL\u003e = collection::BoxedLockCollection\u003cL\u003e;\n\n/// A re-export for [`poisonable::Poisonable`]\npub type Poisonable\u003cL\u003e = poisonable::Poisonable\u003cL\u003e;\n\n/// A mutual exclusion primitive useful for protecting shared data, which cannot deadlock.\n///\n/// By default, this uses `parking_lot` as a backend.\n#[cfg(feature = \"parking_lot\")]\npub type Mutex\u003cT\u003e = mutex::Mutex\u003cT, parking_lot::RawMutex\u003e;\n\n/// A reader-writer lock\n///\n/// By default, this uses `parking_lot` as a backend.\n#[cfg(feature = \"parking_lot\")]\npub type RwLock\u003cT\u003e = rwlock::RwLock\u003cT, parking_lot::RawRwLock\u003e;\n","traces":[{"line":197,"address":[424262],"length":1,"stats":{"Line":10}}],"covered":1,"coverable":1},{"path":["/","home","botahamec","Projects","happylock","src","lockable.rs"],"content":"use std::mem::MaybeUninit;\n\n/// A raw lock type that may be locked and unlocked\n///\n/// # Safety\n///\n/// A deadlock must never occur. The `unlock` method must correctly unlock the\n/// data. The `get_ptrs` method must be implemented correctly. The `Output`\n/// must be unlocked when it is dropped.\n//\n// Why not use a RawRwLock? Because that would be semantically incorrect, and I\n// don't want an INIT or GuardMarker associated item.\n// Originally, RawLock had a sister trait: RawSharableLock. I removed it\n// because it'd be difficult to implement a separate type that takes a\n// different kind of RawLock. But now the Sharable marker trait is needed to\n// indicate if reads can be used.\npub unsafe trait RawLock {\n\t/// Causes all subsequent calls to the `lock` function on this lock to\n\t/// panic. This does not affect anything currently holding the lock.\n\tfn poison(\u0026self);\n\n\t/// Blocks until the lock is acquired\n\t///\n\t/// # Safety\n\t///\n\t/// It is undefined behavior to use this without ownership or mutable\n\t/// access to the [`ThreadKey`], which should last as long as the return\n\t/// value is alive.\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\tunsafe fn raw_lock(\u0026self);\n\n\t/// Attempt to lock without blocking.\n\t///\n\t/// Returns `true` if successful, `false` otherwise.\n\t///\n\t/// # Safety\n\t///\n\t/// It is undefined behavior to use this without ownership or mutable\n\t/// access to the [`ThreadKey`], which should last as long as the return\n\t/// value is alive.\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\tunsafe fn raw_try_lock(\u0026self) -\u003e bool;\n\n\t/// Releases the lock\n\t///\n\t/// # Safety\n\t///\n\t/// It is undefined behavior to use this if the lock is not acquired\n\tunsafe fn raw_unlock(\u0026self);\n\n\t/// Blocks until the data the lock protects can be safely read.\n\t///\n\t/// Some locks, but not all, will allow multiple readers at once. If\n\t/// multiple readers are allowed for a [`Lockable`] type, then the\n\t/// [`Sharable`] marker trait should be implemented.\n\t///\n\t/// # Safety\n\t///\n\t/// It is undefined behavior to use this without ownership or mutable\n\t/// access to the [`ThreadKey`], which should last as long as the return\n\t/// value is alive.\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\tunsafe fn raw_read(\u0026self);\n\n\t// Attempt to read without blocking.\n\t///\n\t/// Returns `true` if successful, `false` otherwise.\n\t///\n\t/// Some locks, but not all, will allow multiple readers at once. If\n\t/// multiple readers are allowed for a [`Lockable`] type, then the\n\t/// [`Sharable`] marker trait should be implemented.\n\t///\n\t/// # Safety\n\t///\n\t/// It is undefined behavior to use this without ownership or mutable\n\t/// access to the [`ThreadKey`], which should last as long as the return\n\t/// value is alive.\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool;\n\n\t/// Releases the lock after calling `read`.\n\t///\n\t/// # Safety\n\t///\n\t/// It is undefined behavior to use this if the read lock is not acquired\n\tunsafe fn raw_unlock_read(\u0026self);\n}\n\n/// A type that may be locked and unlocked.\n///\n/// This trait is usually implemented on collections of [`RawLock`]s. For\n/// example, a `Vec\u003cMutex\u003ci32\u003e\u003e`.\n///\n/// # Safety\n///\n/// Acquiring the locks returned by `get_ptrs` must allow access to the values\n/// returned by `guard`.\n///\n/// Dropping the `Guard` must unlock those same locks.\n///\n/// The order of the resulting list from `get_ptrs` must be deterministic. As\n/// long as the value is not mutated, the references must always be in the same\n/// order.\npub unsafe trait Lockable {\n\t/// The exclusive guard that does not hold a key\n\ttype Guard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\t/// Yields a list of references to the [`RawLock`]s contained within this\n\t/// value.\n\t///\n\t/// These reference locks which must be locked before acquiring a guard,\n\t/// and unlocked when the guard is dropped. The order of the resulting list\n\t/// is deterministic. As long as the value is not mutated, the references\n\t/// will always be in the same order.\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e);\n\n\t/// Returns a guard that can be used to access the underlying data mutably.\n\t///\n\t/// # Safety\n\t///\n\t/// All locks given by calling [`Lockable::get_ptrs`] must be locked\n\t/// exclusively before calling this function. The locks must not be\n\t/// unlocked until this guard is dropped.\n\t#[must_use]\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e;\n\n\t#[must_use]\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e;\n}\n\n/// Allows a lock to be accessed by multiple readers.\n///\n/// # Safety\n///\n/// Acquiring shared access to the locks returned by `get_ptrs` must allow\n/// shared access to the values returned by `read_guard`.\n///\n/// Dropping the `ReadGuard` must unlock those same locks.\npub unsafe trait Sharable: Lockable {\n\t/// The shared guard type that does not hold a key\n\ttype ReadGuard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\t/// Returns a guard that can be used to immutably access the underlying\n\t/// data.\n\t///\n\t/// # Safety\n\t///\n\t/// All locks given by calling [`Lockable::get_ptrs`] must be locked using\n\t/// [`RawLock::raw_read`] before calling this function. The locks must not be\n\t/// unlocked until this guard is dropped.\n\t#[must_use]\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e;\n\n\t#[must_use]\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e;\n}\n\n/// A type that may be locked and unlocked, and is known to be the only valid\n/// instance of the lock.\n///\n/// # Safety\n///\n/// There must not be any two values which can unlock the value at the same\n/// time, i.e., this must either be an owned value or a mutable reference.\npub unsafe trait OwnedLockable: Lockable {}\n\n/// A trait which indicates that `into_inner` is a valid operation for a\n/// [`Lockable`].\n///\n/// This is used for types like [`Poisonable`] to access the inner value of a\n/// lock. [`Poisonable::into_inner`] calls [`LockableIntoInner::into_inner`] to\n/// return a mutable reference of the inner value. This isn't implemented for\n/// some `Lockable`s, such as `\u0026[T]`.\n///\n/// [`Poisonable`]: `crate::Poisonable`\n/// [`Poisonable::into_inner`]: `crate::poisonable::Poisonable::into_inner`\npub trait LockableIntoInner: Lockable {\n\t/// The inner type that is behind the lock\n\ttype Inner;\n\n\t/// Consumes the lock, returning the underlying the lock.\n\tfn into_inner(self) -\u003e Self::Inner;\n}\n\n/// A trait which indicates that `as_mut` is a valid operation for a\n/// [`Lockable`].\n///\n/// This is used for types like [`Poisonable`] to access the inner value of a\n/// lock. [`Poisonable::get_mut`] calls [`LockableGetMut::get_mut`] to return a\n/// mutable reference of the inner value. This isn't implemented for some\n/// `Lockable`s, such as `\u0026[T]`.\n///\n/// [`Poisonable`]: `crate::Poisonable`\n/// [`Poisonable::get_mut`]: `crate::poisonable::Poisonable::get_mut`\npub trait LockableGetMut: Lockable {\n\t/// The inner type that is behind the lock\n\ttype Inner\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\t/// Returns a mutable reference to the underlying data.\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e;\n}\n\nunsafe impl\u003cT: Lockable\u003e Lockable for \u0026T {\n\ttype Guard\u003c'g\u003e\n\t\t= T::Guard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= T::DataMut\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\t(*self).get_ptrs(ptrs);\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\t(*self).guard()\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\t(*self).data_mut()\n\t}\n}\n\nunsafe impl\u003cT: Sharable\u003e Sharable for \u0026T {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= T::ReadGuard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= T::DataRef\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\t(*self).read_guard()\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\t(*self).data_ref()\n\t}\n}\n\nunsafe impl\u003cT: Lockable\u003e Lockable for \u0026mut T {\n\ttype Guard\u003c'g\u003e\n\t\t= T::Guard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= T::DataMut\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\t(**self).get_ptrs(ptrs)\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\t(**self).guard()\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\t(**self).data_mut()\n\t}\n}\n\nimpl\u003cT: LockableGetMut\u003e LockableGetMut for \u0026mut T {\n\ttype Inner\u003c'a\u003e\n\t\t= T::Inner\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\t(*self).get_mut()\n\t}\n}\n\nunsafe impl\u003cT: Sharable\u003e Sharable for \u0026mut T {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= T::ReadGuard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= T::DataRef\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\t(**self).read_guard()\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\t(**self).data_ref()\n\t}\n}\n\nunsafe impl\u003cT: OwnedLockable\u003e OwnedLockable for \u0026mut T {}\n\n/// Implements `Lockable`, `Sharable`, and `OwnedLockable` for tuples\n/// ex: `tuple_impls!(A B C, 0 1 2);`\nmacro_rules! tuple_impls {\n\t($($generic:ident)*, $($value:tt)*) =\u003e {\n\t\tunsafe impl\u003c$($generic: Lockable,)*\u003e Lockable for ($($generic,)*) {\n\t\t\ttype Guard\u003c'g\u003e = ($($generic::Guard\u003c'g\u003e,)*) where Self: 'g;\n\n\t\t\ttype DataMut\u003c'a\u003e = ($($generic::DataMut\u003c'a\u003e,)*) where Self: 'a;\n\n\t\t\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\t\t\t$(self.$value.get_ptrs(ptrs));*\n\t\t\t}\n\n\t\t\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\t\t\t// It's weird that this works\n\t\t\t\t// I don't think any other way of doing it compiles\n\t\t\t\t($(self.$value.guard(),)*)\n\t\t\t}\n\n\t\t\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\t\t\t($(self.$value.data_mut(),)*)\n\t\t\t}\n\t\t}\n\n\t\timpl\u003c$($generic: LockableGetMut,)*\u003e LockableGetMut for ($($generic,)*) {\n\t\t\ttype Inner\u003c'a\u003e = ($($generic::Inner\u003c'a\u003e,)*) where Self: 'a;\n\n\t\t\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\t\t\t($(self.$value.get_mut(),)*)\n\t\t\t}\n\t\t}\n\n\t\timpl\u003c$($generic: LockableIntoInner,)*\u003e LockableIntoInner for ($($generic,)*) {\n\t\t\ttype Inner = ($($generic::Inner,)*);\n\n\t\t\tfn into_inner(self) -\u003e Self::Inner {\n\t\t\t\t($(self.$value.into_inner(),)*)\n\t\t\t}\n\t\t}\n\n\t\tunsafe impl\u003c$($generic: Sharable,)*\u003e Sharable for ($($generic,)*) {\n\t\t\ttype ReadGuard\u003c'g\u003e = ($($generic::ReadGuard\u003c'g\u003e,)*) where Self: 'g;\n\n\t\t\ttype DataRef\u003c'a\u003e = ($($generic::DataRef\u003c'a\u003e,)*) where Self: 'a;\n\n\t\t\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\t\t\t($(self.$value.read_guard(),)*)\n\t\t\t}\n\n\t\t\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\t\t\t($(self.$value.data_ref(),)*)\n\t\t\t}\n\t\t}\n\n\t\tunsafe impl\u003c$($generic: OwnedLockable,)*\u003e OwnedLockable for ($($generic,)*) {}\n\t};\n}\n\ntuple_impls!(A, 0);\ntuple_impls!(A B, 0 1);\ntuple_impls!(A B C, 0 1 2);\ntuple_impls!(A B C D, 0 1 2 3);\ntuple_impls!(A B C D E, 0 1 2 3 4);\ntuple_impls!(A B C D E F, 0 1 2 3 4 5);\ntuple_impls!(A B C D E F G, 0 1 2 3 4 5 6);\n\nunsafe impl\u003cT: Lockable, const N: usize\u003e Lockable for [T; N] {\n\ttype Guard\u003c'g\u003e\n\t\t= [T::Guard\u003c'g\u003e; N]\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= [T::DataMut\u003c'a\u003e; N]\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tfor lock in self {\n\t\t\tlock.get_ptrs(ptrs);\n\t\t}\n\t}\n\n\tunsafe fn guard\u003c'g\u003e(\u0026'g self) -\u003e Self::Guard\u003c'g\u003e {\n\t\t// The MaybeInit helper functions for arrays aren't stable yet, so\n\t\t// we'll just have to implement it ourselves\n\t\tlet mut guards = MaybeUninit::\u003c[MaybeUninit\u003cT::Guard\u003c'g\u003e\u003e; N]\u003e::uninit().assume_init();\n\t\tfor i in 0..N {\n\t\t\tguards[i].write(self[i].guard());\n\t\t}\n\n\t\tguards.map(|g| g.assume_init())\n\t}\n\n\tunsafe fn data_mut\u003c'a\u003e(\u0026'a self) -\u003e Self::DataMut\u003c'a\u003e {\n\t\tlet mut guards = MaybeUninit::\u003c[MaybeUninit\u003cT::DataMut\u003c'a\u003e\u003e; N]\u003e::uninit().assume_init();\n\t\tfor i in 0..N {\n\t\t\tguards[i].write(self[i].data_mut());\n\t\t}\n\n\t\tguards.map(|g| g.assume_init())\n\t}\n}\n\nimpl\u003cT: LockableGetMut, const N: usize\u003e LockableGetMut for [T; N] {\n\ttype Inner\u003c'a\u003e\n\t\t= [T::Inner\u003c'a\u003e; N]\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tunsafe {\n\t\t\tlet mut guards = MaybeUninit::\u003c[MaybeUninit\u003cT::Inner\u003c'_\u003e\u003e; N]\u003e::uninit().assume_init();\n\t\t\tfor (i, lock) in self.iter_mut().enumerate() {\n\t\t\t\tguards[i].write(lock.get_mut());\n\t\t\t}\n\n\t\t\tguards.map(|g| g.assume_init())\n\t\t}\n\t}\n}\n\nimpl\u003cT: LockableIntoInner, const N: usize\u003e LockableIntoInner for [T; N] {\n\ttype Inner = [T::Inner; N];\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tunsafe {\n\t\t\tlet mut guards = MaybeUninit::\u003c[MaybeUninit\u003cT::Inner\u003e; N]\u003e::uninit().assume_init();\n\t\t\tfor (i, lock) in self.into_iter().enumerate() {\n\t\t\t\tguards[i].write(lock.into_inner());\n\t\t\t}\n\n\t\t\tguards.map(|g| g.assume_init())\n\t\t}\n\t}\n}\n\nunsafe impl\u003cT: Sharable, const N: usize\u003e Sharable for [T; N] {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= [T::ReadGuard\u003c'g\u003e; N]\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= [T::DataRef\u003c'a\u003e; N]\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard\u003c'g\u003e(\u0026'g self) -\u003e Self::ReadGuard\u003c'g\u003e {\n\t\tlet mut guards = MaybeUninit::\u003c[MaybeUninit\u003cT::ReadGuard\u003c'g\u003e\u003e; N]\u003e::uninit().assume_init();\n\t\tfor i in 0..N {\n\t\t\tguards[i].write(self[i].read_guard());\n\t\t}\n\n\t\tguards.map(|g| g.assume_init())\n\t}\n\n\tunsafe fn data_ref\u003c'a\u003e(\u0026'a self) -\u003e Self::DataRef\u003c'a\u003e {\n\t\tlet mut guards = MaybeUninit::\u003c[MaybeUninit\u003cT::DataRef\u003c'a\u003e\u003e; N]\u003e::uninit().assume_init();\n\t\tfor i in 0..N {\n\t\t\tguards[i].write(self[i].data_ref());\n\t\t}\n\n\t\tguards.map(|g| g.assume_init())\n\t}\n}\n\nunsafe impl\u003cT: OwnedLockable, const N: usize\u003e OwnedLockable for [T; N] {}\n\nunsafe impl\u003cT: Lockable\u003e Lockable for Box\u003c[T]\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= Box\u003c[T::Guard\u003c'g\u003e]\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= Box\u003c[T::DataMut\u003c'a\u003e]\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tfor lock in self {\n\t\t\tlock.get_ptrs(ptrs);\n\t\t}\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tself.iter().map(|lock| lock.guard()).collect()\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.iter().map(|lock| lock.data_mut()).collect()\n\t}\n}\n\nimpl\u003cT: LockableGetMut + 'static\u003e LockableGetMut for Box\u003c[T]\u003e {\n\ttype Inner\u003c'a\u003e\n\t\t= Box\u003c[T::Inner\u003c'a\u003e]\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tself.iter_mut().map(LockableGetMut::get_mut).collect()\n\t}\n}\n\nunsafe impl\u003cT: Sharable\u003e Sharable for Box\u003c[T]\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= Box\u003c[T::ReadGuard\u003c'g\u003e]\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= Box\u003c[T::DataRef\u003c'a\u003e]\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tself.iter().map(|lock| lock.read_guard()).collect()\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tself.iter().map(|lock| lock.data_ref()).collect()\n\t}\n}\n\nunsafe impl\u003cT: Sharable\u003e Sharable for Vec\u003cT\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= Box\u003c[T::ReadGuard\u003c'g\u003e]\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= Box\u003c[T::DataRef\u003c'a\u003e]\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tself.iter().map(|lock| lock.read_guard()).collect()\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tself.iter().map(|lock| lock.data_ref()).collect()\n\t}\n}\n\nunsafe impl\u003cT: OwnedLockable\u003e OwnedLockable for Box\u003c[T]\u003e {}\n\nunsafe impl\u003cT: Lockable\u003e Lockable for Vec\u003cT\u003e {\n\t// There's no reason why I'd ever want to extend a list of lock guards\n\ttype Guard\u003c'g\u003e\n\t\t= Box\u003c[T::Guard\u003c'g\u003e]\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= Box\u003c[T::DataMut\u003c'a\u003e]\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tfor lock in self {\n\t\t\tlock.get_ptrs(ptrs);\n\t\t}\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tself.iter().map(|lock| lock.guard()).collect()\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.iter().map(|lock| lock.data_mut()).collect()\n\t}\n}\n\n// I'd make a generic impl\u003cT: Lockable, I: IntoIterator\u003cItem=T\u003e\u003e Lockable for I\n// but I think that'd require sealing up this trait\n\n// TODO: using edition 2024, impl LockableIntoInner for Box\u003c[T]\u003e\n\nimpl\u003cT: LockableGetMut + 'static\u003e LockableGetMut for Vec\u003cT\u003e {\n\ttype Inner\u003c'a\u003e\n\t\t= Box\u003c[T::Inner\u003c'a\u003e]\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tself.iter_mut().map(LockableGetMut::get_mut).collect()\n\t}\n}\n\nimpl\u003cT: LockableIntoInner\u003e LockableIntoInner for Vec\u003cT\u003e {\n\ttype Inner = Box\u003c[T::Inner]\u003e;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tself.into_iter()\n\t\t\t.map(LockableIntoInner::into_inner)\n\t\t\t.collect()\n\t}\n}\n\nunsafe impl\u003cT: OwnedLockable\u003e OwnedLockable for Vec\u003cT\u003e {}\n\n#[cfg(test)]\nmod tests {\n\tuse super::*;\n\tuse crate::{Mutex, RwLock};\n\n\t#[test]\n\tfn mut_ref_get_ptrs() {\n\t\tlet mut rwlock = RwLock::new(5);\n\t\tlet mutref = \u0026mut rwlock;\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tmutref.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tassert!(std::ptr::addr_eq(lock_ptrs[0], mutref));\n\t}\n\n\t#[test]\n\tfn array_get_ptrs_empty() {\n\t\tlet locks: [Mutex\u003c()\u003e; 0] = [];\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert!(lock_ptrs.is_empty());\n\t}\n\n\t#[test]\n\tfn array_get_ptrs_length_one() {\n\t\tlet locks: [Mutex\u003ci32\u003e; 1] = [Mutex::new(1)];\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[0], locks[0].raw())) }\n\t}\n\n\t#[test]\n\tfn array_get_ptrs_length_two() {\n\t\tlet locks: [Mutex\u003ci32\u003e; 2] = [Mutex::new(1), Mutex::new(2)];\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 2);\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[0], locks[0].raw())) }\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[1], locks[1].raw())) }\n\t}\n\n\t#[test]\n\tfn vec_get_ptrs_empty() {\n\t\tlet locks: Vec\u003cMutex\u003c()\u003e\u003e = Vec::new();\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert!(lock_ptrs.is_empty());\n\t}\n\n\t#[test]\n\tfn vec_get_ptrs_length_one() {\n\t\tlet locks: Vec\u003cMutex\u003ci32\u003e\u003e = vec![Mutex::new(1)];\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[0], locks[0].raw())) }\n\t}\n\n\t#[test]\n\tfn vec_get_ptrs_length_two() {\n\t\tlet locks: Vec\u003cMutex\u003ci32\u003e\u003e = vec![Mutex::new(1), Mutex::new(2)];\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 2);\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[0], locks[0].raw())) }\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[1], locks[1].raw())) }\n\t}\n\n\t#[test]\n\tfn vec_as_mut() {\n\t\tlet mut locks: Vec\u003cMutex\u003ci32\u003e\u003e = vec![Mutex::new(1), Mutex::new(2)];\n\t\tlet lock_ptrs = LockableGetMut::get_mut(\u0026mut locks);\n\n\t\tassert_eq!(lock_ptrs.len(), 2);\n\t\tassert_eq!(*lock_ptrs[0], 1);\n\t\tassert_eq!(*lock_ptrs[1], 2);\n\t}\n\n\t#[test]\n\tfn vec_into_inner() {\n\t\tlet locks: Vec\u003cMutex\u003ci32\u003e\u003e = vec![Mutex::new(1), Mutex::new(2)];\n\t\tlet lock_ptrs = LockableIntoInner::into_inner(locks);\n\n\t\tassert_eq!(lock_ptrs.len(), 2);\n\t\tassert_eq!(lock_ptrs[0], 1);\n\t\tassert_eq!(lock_ptrs[1], 2);\n\t}\n\n\t#[test]\n\tfn box_get_ptrs_empty() {\n\t\tlet locks: Box\u003c[Mutex\u003c()\u003e]\u003e = Box::from([]);\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert!(lock_ptrs.is_empty());\n\t}\n\n\t#[test]\n\tfn box_get_ptrs_length_one() {\n\t\tlet locks: Box\u003c[Mutex\u003ci32\u003e]\u003e = vec![Mutex::new(1)].into_boxed_slice();\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[0], locks[0].raw())) }\n\t}\n\n\t#[test]\n\tfn box_get_ptrs_length_two() {\n\t\tlet locks: Box\u003c[Mutex\u003ci32\u003e]\u003e = vec![Mutex::new(1), Mutex::new(2)].into_boxed_slice();\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 2);\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[0], locks[0].raw())) }\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[1], locks[1].raw())) }\n\t}\n\n\t#[test]\n\tfn box_as_mut() {\n\t\tlet mut locks: Box\u003c[Mutex\u003ci32\u003e]\u003e = vec![Mutex::new(1), Mutex::new(2)].into_boxed_slice();\n\t\tlet lock_ptrs = LockableGetMut::get_mut(\u0026mut locks);\n\n\t\tassert_eq!(lock_ptrs.len(), 2);\n\t\tassert_eq!(*lock_ptrs[0], 1);\n\t\tassert_eq!(*lock_ptrs[1], 2);\n\t}\n}\n","traces":[{"line":232,"address":[831952,831888,831984,831920,832016],"length":1,"stats":{"Line":50}},{"line":233,"address":[176542,176574,176510],"length":1,"stats":{"Line":50}},{"line":236,"address":[220000,219984],"length":1,"stats":{"Line":9}},{"line":237,"address":[237653,237669,237637],"length":1,"stats":{"Line":9}},{"line":240,"address":[],"length":0,"stats":{"Line":1}},{"line":241,"address":[136261],"length":1,"stats":{"Line":1}},{"line":256,"address":[154784,154768],"length":1,"stats":{"Line":3}},{"line":257,"address":[],"length":0,"stats":{"Line":3}},{"line":260,"address":[],"length":0,"stats":{"Line":0}},{"line":261,"address":[],"length":0,"stats":{"Line":0}},{"line":276,"address":[],"length":0,"stats":{"Line":1}},{"line":277,"address":[711118],"length":1,"stats":{"Line":1}},{"line":280,"address":[],"length":0,"stats":{"Line":0}},{"line":281,"address":[],"length":0,"stats":{"Line":0}},{"line":284,"address":[],"length":0,"stats":{"Line":0}},{"line":285,"address":[],"length":0,"stats":{"Line":0}},{"line":295,"address":[],"length":0,"stats":{"Line":0}},{"line":296,"address":[],"length":0,"stats":{"Line":0}},{"line":311,"address":[],"length":0,"stats":{"Line":0}},{"line":312,"address":[],"length":0,"stats":{"Line":0}},{"line":315,"address":[],"length":0,"stats":{"Line":0}},{"line":316,"address":[],"length":0,"stats":{"Line":0}},{"line":331,"address":[223440],"length":1,"stats":{"Line":12}},{"line":332,"address":[154824],"length":1,"stats":{"Line":18}},{"line":335,"address":[143376,143567],"length":1,"stats":{"Line":6}},{"line":338,"address":[842254,842366],"length":1,"stats":{"Line":4}},{"line":357,"address":[842800,842464,843105,842696,843120,843493,843522],"length":1,"stats":{"Line":3}},{"line":358,"address":[843260,842591,842822,842908,843142,842486],"length":1,"stats":{"Line":6}},{"line":367,"address":[716416,716504],"length":1,"stats":{"Line":1}},{"line":368,"address":[223552],"length":1,"stats":{"Line":1}},{"line":399,"address":[],"length":0,"stats":{"Line":18}},{"line":400,"address":[160579,160514],"length":1,"stats":{"Line":36}},{"line":401,"address":[],"length":0,"stats":{"Line":18}},{"line":405,"address":[],"length":0,"stats":{"Line":7}},{"line":408,"address":[],"length":0,"stats":{"Line":2}},{"line":409,"address":[],"length":0,"stats":{"Line":13}},{"line":410,"address":[],"length":0,"stats":{"Line":12}},{"line":413,"address":[711853,711538],"length":1,"stats":{"Line":19}},{"line":416,"address":[136784,136368],"length":1,"stats":{"Line":1}},{"line":417,"address":[],"length":0,"stats":{"Line":0}},{"line":418,"address":[136968,136476,136866,136600],"length":1,"stats":{"Line":2}},{"line":419,"address":[],"length":0,"stats":{"Line":2}},{"line":422,"address":[186055,186087,186032,186064],"length":1,"stats":{"Line":3}},{"line":432,"address":[],"length":0,"stats":{"Line":1}},{"line":434,"address":[],"length":0,"stats":{"Line":0}},{"line":435,"address":[835098,835290],"length":1,"stats":{"Line":2}},{"line":436,"address":[],"length":0,"stats":{"Line":2}},{"line":439,"address":[700112,700135],"length":1,"stats":{"Line":3}},{"line":447,"address":[],"length":0,"stats":{"Line":1}},{"line":449,"address":[835478],"length":1,"stats":{"Line":1}},{"line":450,"address":[],"length":0,"stats":{"Line":4}},{"line":451,"address":[835950,835888],"length":1,"stats":{"Line":2}},{"line":454,"address":[835904],"length":1,"stats":{"Line":3}},{"line":470,"address":[160608],"length":1,"stats":{"Line":4}},{"line":471,"address":[],"length":0,"stats":{"Line":0}},{"line":472,"address":[712872,712504,712094,712402,712156,712770],"length":1,"stats":{"Line":7}},{"line":473,"address":[160858,160988],"length":1,"stats":{"Line":6}},{"line":476,"address":[160775],"length":1,"stats":{"Line":10}},{"line":479,"address":[],"length":0,"stats":{"Line":0}},{"line":480,"address":[],"length":0,"stats":{"Line":0}},{"line":481,"address":[],"length":0,"stats":{"Line":0}},{"line":482,"address":[],"length":0,"stats":{"Line":0}},{"line":485,"address":[],"length":0,"stats":{"Line":0}},{"line":502,"address":[],"length":0,"stats":{"Line":3}},{"line":503,"address":[],"length":0,"stats":{"Line":5}},{"line":504,"address":[],"length":0,"stats":{"Line":2}},{"line":508,"address":[],"length":0,"stats":{"Line":0}},{"line":509,"address":[],"length":0,"stats":{"Line":0}},{"line":512,"address":[],"length":0,"stats":{"Line":0}},{"line":513,"address":[],"length":0,"stats":{"Line":0}},{"line":523,"address":[193120],"length":1,"stats":{"Line":1}},{"line":524,"address":[],"length":0,"stats":{"Line":1}},{"line":539,"address":[],"length":0,"stats":{"Line":0}},{"line":540,"address":[],"length":0,"stats":{"Line":0}},{"line":543,"address":[],"length":0,"stats":{"Line":0}},{"line":544,"address":[],"length":0,"stats":{"Line":0}},{"line":559,"address":[],"length":0,"stats":{"Line":0}},{"line":560,"address":[],"length":0,"stats":{"Line":0}},{"line":563,"address":[],"length":0,"stats":{"Line":0}},{"line":564,"address":[],"length":0,"stats":{"Line":0}},{"line":582,"address":[783232,783008,783120],"length":1,"stats":{"Line":4}},{"line":583,"address":[],"length":0,"stats":{"Line":7}},{"line":584,"address":[],"length":0,"stats":{"Line":3}},{"line":588,"address":[],"length":0,"stats":{"Line":1}},{"line":589,"address":[],"length":0,"stats":{"Line":3}},{"line":592,"address":[],"length":0,"stats":{"Line":0}},{"line":593,"address":[],"length":0,"stats":{"Line":0}},{"line":608,"address":[],"length":0,"stats":{"Line":2}},{"line":609,"address":[],"length":0,"stats":{"Line":2}},{"line":616,"address":[],"length":0,"stats":{"Line":1}},{"line":617,"address":[],"length":0,"stats":{"Line":1}},{"line":618,"address":[],"length":0,"stats":{"Line":0}}],"covered":57,"coverable":92},{"path":["/","home","botahamec","Projects","happylock","src","mutex","guard.rs"],"content":"use std::fmt::{Debug, Display};\nuse std::hash::Hash;\nuse std::marker::PhantomData;\nuse std::ops::{Deref, DerefMut};\n\nuse lock_api::RawMutex;\n\nuse crate::lockable::RawLock;\nuse crate::ThreadKey;\n\nuse super::{Mutex, MutexGuard, MutexRef};\n\n// These impls make things slightly easier because now you can use\n// `println!(\"{guard}\")` instead of `println!(\"{}\", *guard)`\n\n#[mutants::skip] // hashing involves RNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Hash + ?Sized, R: RawMutex\u003e Hash for MutexRef\u003c'_, T, R\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.deref().hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug + ?Sized, R: RawMutex\u003e Debug for MutexRef\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: Display + ?Sized, R: RawMutex\u003e Display for MutexRef\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e Drop for MutexRef\u003c'_, T, R\u003e {\n\tfn drop(\u0026mut self) {\n\t\t// safety: this guard is being destroyed, so the data cannot be\n\t\t// accessed without locking again\n\t\tunsafe { self.0.raw_unlock() }\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e Deref for MutexRef\u003c'_, T, R\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t// safety: this is the only type that can use `value`, and there's\n\t\t// a reference to this type, so there cannot be any mutable\n\t\t// references to this value.\n\t\tunsafe { \u0026*self.0.data.get() }\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e DerefMut for MutexRef\u003c'_, T, R\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t// safety: this is the only type that can use `value`, and we have a\n\t\t// mutable reference to this type, so there cannot be any other\n\t\t// references to this value.\n\t\tunsafe { \u0026mut *self.0.data.get() }\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e AsRef\u003cT\u003e for MutexRef\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e AsMut\u003cT\u003e for MutexRef\u003c'_, T, R\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself\n\t}\n}\n\nimpl\u003c'a, T: ?Sized, R: RawMutex\u003e MutexRef\u003c'a, T, R\u003e {\n\t/// Creates a reference to the underlying data of a mutex without\n\t/// attempting to lock it or take ownership of the key. But it's also quite\n\t/// dangerous to drop.\n\tpub(crate) unsafe fn new(mutex: \u0026'a Mutex\u003cT, R\u003e) -\u003e Self {\n\t\tSelf(mutex, PhantomData)\n\t}\n}\n\n// it's kinda annoying to re-implement some of this stuff on guards\n// there's nothing i can do about that\n\n#[mutants::skip] // hashing involves RNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Hash + ?Sized, R: RawMutex\u003e Hash for MutexGuard\u003c'_, T, R\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.deref().hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug + ?Sized, R: RawMutex\u003e Debug for MutexGuard\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: Display + ?Sized, R: RawMutex\u003e Display for MutexGuard\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e Deref for MutexGuard\u003c'_, T, R\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t\u0026self.mutex\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e DerefMut for MutexGuard\u003c'_, T, R\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t\u0026mut self.mutex\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e AsRef\u003cT\u003e for MutexGuard\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e AsMut\u003cT\u003e for MutexGuard\u003c'_, T, R\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself\n\t}\n}\n\nimpl\u003c'a, T: ?Sized, R: RawMutex\u003e MutexGuard\u003c'a, T, R\u003e {\n\t/// Create a guard to the given mutex. Undefined if multiple guards to the\n\t/// same mutex exist at once.\n\t#[must_use]\n\tpub(super) unsafe fn new(mutex: \u0026'a Mutex\u003cT, R\u003e, thread_key: ThreadKey) -\u003e Self {\n\t\tSelf {\n\t\t\tmutex: MutexRef(mutex, PhantomData),\n\t\t\tthread_key,\n\t\t}\n\t}\n}\n\nunsafe impl\u003cT: ?Sized + Sync, R: RawMutex + Sync\u003e Sync for MutexRef\u003c'_, T, R\u003e {}\n","traces":[{"line":33,"address":[836224],"length":1,"stats":{"Line":1}},{"line":34,"address":[],"length":0,"stats":{"Line":1}},{"line":39,"address":[225920],"length":1,"stats":{"Line":6}},{"line":42,"address":[225925],"length":1,"stats":{"Line":7}},{"line":49,"address":[836272,836304],"length":1,"stats":{"Line":4}},{"line":53,"address":[143765],"length":1,"stats":{"Line":4}},{"line":58,"address":[836336,836368],"length":1,"stats":{"Line":4}},{"line":62,"address":[836373,836341],"length":1,"stats":{"Line":5}},{"line":67,"address":[],"length":0,"stats":{"Line":2}},{"line":68,"address":[],"length":0,"stats":{"Line":2}},{"line":73,"address":[],"length":0,"stats":{"Line":1}},{"line":74,"address":[],"length":0,"stats":{"Line":1}},{"line":82,"address":[238144,238112,238128],"length":1,"stats":{"Line":6}},{"line":83,"address":[],"length":0,"stats":{"Line":0}},{"line":107,"address":[],"length":0,"stats":{"Line":1}},{"line":108,"address":[],"length":0,"stats":{"Line":1}},{"line":115,"address":[],"length":0,"stats":{"Line":2}},{"line":116,"address":[],"length":0,"stats":{"Line":2}},{"line":121,"address":[137424],"length":1,"stats":{"Line":2}},{"line":122,"address":[137429],"length":1,"stats":{"Line":2}},{"line":127,"address":[836576],"length":1,"stats":{"Line":2}},{"line":128,"address":[],"length":0,"stats":{"Line":2}},{"line":133,"address":[],"length":0,"stats":{"Line":1}},{"line":134,"address":[],"length":0,"stats":{"Line":1}},{"line":142,"address":[138368],"length":1,"stats":{"Line":4}},{"line":144,"address":[],"length":0,"stats":{"Line":0}}],"covered":24,"coverable":26},{"path":["/","home","botahamec","Projects","happylock","src","mutex","mutex.rs"],"content":"use std::cell::UnsafeCell;\nuse std::fmt::Debug;\nuse std::marker::PhantomData;\nuse std::panic::AssertUnwindSafe;\n\nuse lock_api::RawMutex;\n\nuse crate::handle_unwind::handle_unwind;\nuse crate::lockable::{Lockable, LockableGetMut, LockableIntoInner, OwnedLockable, RawLock};\nuse crate::poisonable::PoisonFlag;\nuse crate::{Keyable, ThreadKey};\n\nuse super::{Mutex, MutexGuard, MutexRef};\n\nunsafe impl\u003cT: ?Sized, R: RawMutex\u003e RawLock for Mutex\u003cT, R\u003e {\n\tfn poison(\u0026self) {\n\t\tself.poison.poison();\n\t}\n\n\tunsafe fn raw_lock(\u0026self) {\n\t\tassert!(!self.poison.is_poisoned(), \"The mutex has been killed\");\n\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.lock(), || self.poison())\n\t}\n\n\tunsafe fn raw_try_lock(\u0026self) -\u003e bool {\n\t\tif self.poison.is_poisoned() {\n\t\t\treturn false;\n\t\t}\n\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.try_lock(), || self.poison())\n\t}\n\n\tunsafe fn raw_unlock(\u0026self) {\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.unlock(), || self.poison())\n\t}\n\n\t// this is the closest thing to a read we can get, but Sharable isn't\n\t// implemented for this\n\tunsafe fn raw_read(\u0026self) {\n\t\tself.raw_lock()\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tself.raw_try_lock()\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tself.raw_unlock()\n\t}\n}\n\nunsafe impl\u003cT: Send, R: RawMutex + Send + Sync\u003e Lockable for Mutex\u003cT, R\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= MutexRef\u003c'g, T, R\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= \u0026'a mut T\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tptrs.push(self);\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tMutexRef::new(self)\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.data.get().as_mut().unwrap_unchecked()\n\t}\n}\n\nimpl\u003cT: Send, R: RawMutex + Send + Sync\u003e LockableIntoInner for Mutex\u003cT, R\u003e {\n\ttype Inner = T;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tself.into_inner()\n\t}\n}\n\nimpl\u003cT: Send, R: RawMutex + Send + Sync\u003e LockableGetMut for Mutex\u003cT, R\u003e {\n\ttype Inner\u003c'a\u003e\n\t\t= \u0026'a mut T\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tself.get_mut()\n\t}\n}\n\nunsafe impl\u003cT: Send, R: RawMutex + Send + Sync\u003e OwnedLockable for Mutex\u003cT, R\u003e {}\n\nimpl\u003cT, R: RawMutex\u003e Mutex\u003cT, R\u003e {\n\t/// Create a new unlocked `Mutex`.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t///\n\t/// let mutex = Mutex::new(0);\n\t/// ```\n\t#[must_use]\n\tpub const fn new(data: T) -\u003e Self {\n\t\tSelf {\n\t\t\traw: R::INIT,\n\t\t\tpoison: PoisonFlag::new(),\n\t\t\tdata: UnsafeCell::new(data),\n\t\t}\n\t}\n\n\t/// Returns the raw underlying mutex.\n\t///\n\t/// Note that you will most likely need to import the [`RawMutex`] trait\n\t/// from `lock_api` to be able to call functions on the raw mutex.\n\t///\n\t/// # Safety\n\t///\n\t/// This method is unsafe because it allows unlocking a mutex while still\n\t/// holding a reference to a [`MutexGuard`], and locking a mutex without\n\t/// holding the [`ThreadKey`].\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\t#[must_use]\n\tpub const unsafe fn raw(\u0026self) -\u003e \u0026R {\n\t\t\u0026self.raw\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: ?Sized + Debug, R: RawMutex\u003e Debug for Mutex\u003cT, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t// safety: this is just a try lock, and the value is dropped\n\t\t// immediately after, so there's no risk of blocking ourselves\n\t\t// or any other threads\n\t\t// when i implement try_clone this code will become less unsafe\n\t\tif let Some(value) = unsafe { self.try_lock_no_key() } {\n\t\t\tf.debug_struct(\"Mutex\").field(\"data\", \u0026\u0026*value).finish()\n\t\t} else {\n\t\t\tstruct LockedPlaceholder;\n\t\t\timpl Debug for LockedPlaceholder {\n\t\t\t\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t\t\t\tf.write_str(\"\u003clocked\u003e\")\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tf.debug_struct(\"Mutex\")\n\t\t\t\t.field(\"data\", \u0026LockedPlaceholder)\n\t\t\t\t.finish()\n\t\t}\n\t}\n}\n\nimpl\u003cT: Default, R: RawMutex\u003e Default for Mutex\u003cT, R\u003e {\n\tfn default() -\u003e Self {\n\t\tSelf::new(T::default())\n\t}\n}\n\nimpl\u003cT, R: RawMutex\u003e From\u003cT\u003e for Mutex\u003cT, R\u003e {\n\tfn from(value: T) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\n// We don't need a `get_mut` because we don't have mutex poisoning. Hurray!\n// We have it anyway for documentation\nimpl\u003cT: ?Sized, R\u003e AsMut\u003cT\u003e for Mutex\u003cT, R\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself.get_mut()\n\t}\n}\n\nimpl\u003cT, R\u003e Mutex\u003cT, R\u003e {\n\t/// Consumes this mutex, returning the underlying data.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t///\n\t/// let mutex = Mutex::new(0);\n\t/// assert_eq!(mutex.into_inner(), 0);\n\t/// ```\n\t#[must_use]\n\tpub fn into_inner(self) -\u003e T {\n\t\tself.data.into_inner()\n\t}\n}\n\nimpl\u003cT: ?Sized, R\u003e Mutex\u003cT, R\u003e {\n\t/// Returns a mutable reference to the underlying data.\n\t///\n\t/// Since this call borrows `Mutex` mutably, no actual locking is taking\n\t/// place. The mutable borrow statically guarantees that no locks exist.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, Mutex};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut mutex = Mutex::new(0);\n\t/// *mutex.get_mut() = 10;\n\t/// assert_eq!(*mutex.lock(key), 10);\n\t/// ```\n\t#[must_use]\n\tpub fn get_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself.data.get_mut()\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e Mutex\u003cT, R\u003e {\n\tpub fn scoped_lock\u003cRet\u003e(\u0026self, key: impl Keyable, f: impl FnOnce(\u0026mut T) -\u003e Ret) -\u003e Ret {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_lock();\n\n\t\t\t// safety: the mutex was just locked\n\t\t\tlet r = f(self.data.get().as_mut().unwrap_unchecked());\n\n\t\t\t// safety: we locked the mutex already\n\t\t\tself.raw_unlock();\n\n\t\t\tdrop(key); // ensures we drop the key in the correct place\n\n\t\t\tr\n\t\t}\n\t}\n\n\tpub fn scoped_try_lock\u003cKey: Keyable, Ret\u003e(\n\t\t\u0026self,\n\t\tkey: Key,\n\t\tf: impl FnOnce(\u0026mut T) -\u003e Ret,\n\t) -\u003e Result\u003cRet, Key\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_lock() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: the mutex was just locked\n\t\t\tlet r = f(self.data.get().as_mut().unwrap_unchecked());\n\n\t\t\t// safety: we locked the mutex already\n\t\t\tself.raw_unlock();\n\n\t\t\tdrop(key); // ensures we drop the key in the correct place\n\n\t\t\tOk(r)\n\t\t}\n\t}\n\n\t/// Block the thread until this mutex can be locked, and lock it.\n\t///\n\t/// Upon returning, the thread is the only thread with a lock on the\n\t/// `Mutex`. A [`MutexGuard`] is returned to allow a scoped unlock of this\n\t/// `Mutex`. When the guard is dropped, this `Mutex` will unlock.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::{thread, sync::Arc};\n\t/// use happylock::{Mutex, ThreadKey};\n\t///\n\t/// let mutex = Arc::new(Mutex::new(0));\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t///\n\t/// thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// *c_mutex.lock(key) = 10;\n\t/// }).join().expect(\"thread::spawn failed\");\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// assert_eq!(*mutex.lock(key), 10);\n\t/// ```\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e MutexGuard\u003c'_, T, R\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_lock();\n\n\t\t\t// safety: we just locked the mutex\n\t\t\tMutexGuard::new(self, key)\n\t\t}\n\t}\n\n\t/// Attempts to lock the `Mutex` without blocking.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// lock when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If the mutex could not be acquired because it is already locked, then\n\t/// this call will return an error containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::{thread, sync::Arc};\n\t/// use happylock::{Mutex, ThreadKey};\n\t///\n\t/// let mutex = Arc::new(Mutex::new(0));\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t///\n\t/// thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut lock = c_mutex.try_lock(key);\n\t/// if let Ok(mut lock) = lock {\n\t/// *lock = 10;\n\t/// } else {\n\t/// println!(\"try_lock failed\");\n\t/// }\n\t/// }).join().expect(\"thread::spawn failed\");\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// assert_eq!(*mutex.lock(key), 10);\n\t/// ```\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e Result\u003cMutexGuard\u003c'_, T, R\u003e, ThreadKey\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the key to the mutex\n\t\t\tif self.raw_try_lock() {\n\t\t\t\t// safety: we just locked the mutex\n\t\t\t\tOk(MutexGuard::new(self, key))\n\t\t\t} else {\n\t\t\t\tErr(key)\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Returns `true` if the mutex is currently locked\n\t#[cfg(test)]\n\tpub(crate) fn is_locked(\u0026self) -\u003e bool {\n\t\tself.raw.is_locked()\n\t}\n\n\t/// Lock without a [`ThreadKey`]. It is undefined behavior to do this without\n\t/// owning the [`ThreadKey`].\n\tpub(crate) unsafe fn try_lock_no_key(\u0026self) -\u003e Option\u003cMutexRef\u003c'_, T, R\u003e\u003e {\n\t\tself.raw_try_lock().then_some(MutexRef(self, PhantomData))\n\t}\n\n\t/// Consumes the [`MutexGuard`], and consequently unlocks its `Mutex`.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, Mutex};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mutex = Mutex::new(0);\n\t///\n\t/// let mut guard = mutex.lock(key);\n\t/// *guard += 20;\n\t///\n\t/// let key = Mutex::unlock(guard);\n\t/// ```\n\t#[must_use]\n\tpub fn unlock(guard: MutexGuard\u003c'_, T, R\u003e) -\u003e ThreadKey {\n\t\tunsafe {\n\t\t\tguard.mutex.0.raw_unlock();\n\t\t}\n\t\tguard.thread_key\n\t}\n}\n\nunsafe impl\u003cR: RawMutex + Send, T: ?Sized + Send\u003e Send for Mutex\u003cT, R\u003e {}\nunsafe impl\u003cR: RawMutex + Sync, T: ?Sized + Send\u003e Sync for Mutex\u003cT, R\u003e {}\n","traces":[{"line":16,"address":[223136,223168],"length":1,"stats":{"Line":6}},{"line":17,"address":[223173,223141],"length":1,"stats":{"Line":6}},{"line":20,"address":[137744],"length":1,"stats":{"Line":13}},{"line":21,"address":[223214,223326,223365,223253],"length":1,"stats":{"Line":14}},{"line":24,"address":[146689,146577],"length":1,"stats":{"Line":15}},{"line":25,"address":[223238,223350],"length":1,"stats":{"Line":55}},{"line":28,"address":[137600],"length":1,"stats":{"Line":11}},{"line":29,"address":[240974,240814,240894],"length":1,"stats":{"Line":13}},{"line":30,"address":[222968,223048],"length":1,"stats":{"Line":7}},{"line":34,"address":[837840,837520,837440,837600,837680,837760],"length":1,"stats":{"Line":12}},{"line":35,"address":[240998,240914,240838],"length":1,"stats":{"Line":42}},{"line":38,"address":[222848,222880],"length":1,"stats":{"Line":14}},{"line":40,"address":[137580],"length":1,"stats":{"Line":15}},{"line":41,"address":[837937,838001,837905,838065,838033,837969],"length":1,"stats":{"Line":52}},{"line":46,"address":[137856],"length":1,"stats":{"Line":0}},{"line":47,"address":[146773,146789],"length":1,"stats":{"Line":0}},{"line":50,"address":[137680],"length":1,"stats":{"Line":0}},{"line":51,"address":[223077,223093],"length":1,"stats":{"Line":0}},{"line":54,"address":[241104,241088,241120],"length":1,"stats":{"Line":0}},{"line":55,"address":[137701],"length":1,"stats":{"Line":0}},{"line":70,"address":[241696,241760,241632],"length":1,"stats":{"Line":23}},{"line":71,"address":[137945],"length":1,"stats":{"Line":24}},{"line":74,"address":[241600,241584,241616],"length":1,"stats":{"Line":4}},{"line":75,"address":[838757,838773],"length":1,"stats":{"Line":4}},{"line":78,"address":[137872],"length":1,"stats":{"Line":1}},{"line":79,"address":[137881],"length":1,"stats":{"Line":1}},{"line":86,"address":[838784,838880,838864,838800,838896,838848],"length":1,"stats":{"Line":6}},{"line":87,"address":[],"length":0,"stats":{"Line":6}},{"line":97,"address":[],"length":0,"stats":{"Line":3}},{"line":98,"address":[838933,838949,838965],"length":1,"stats":{"Line":3}},{"line":115,"address":[],"length":0,"stats":{"Line":17}},{"line":118,"address":[839014,839207,839741,839314,839789,839576,839058,839163,839621,839937,839991,839366],"length":1,"stats":{"Line":34}},{"line":119,"address":[146135,145991],"length":1,"stats":{"Line":17}},{"line":136,"address":[],"length":0,"stats":{"Line":1}},{"line":137,"address":[],"length":0,"stats":{"Line":0}},{"line":167,"address":[840192,840112,840240,840272,840160],"length":1,"stats":{"Line":5}},{"line":168,"address":[840116,840168,840206,840248,840285],"length":1,"stats":{"Line":5}},{"line":173,"address":[],"length":0,"stats":{"Line":0}},{"line":174,"address":[],"length":0,"stats":{"Line":0}},{"line":181,"address":[],"length":0,"stats":{"Line":0}},{"line":182,"address":[],"length":0,"stats":{"Line":0}},{"line":198,"address":[],"length":0,"stats":{"Line":2}},{"line":199,"address":[],"length":0,"stats":{"Line":6}},{"line":220,"address":[],"length":0,"stats":{"Line":3}},{"line":221,"address":[],"length":0,"stats":{"Line":3}},{"line":226,"address":[],"length":0,"stats":{"Line":3}},{"line":229,"address":[],"length":0,"stats":{"Line":3}},{"line":232,"address":[840801,841121,841273,840953,840775,841095],"length":1,"stats":{"Line":3}},{"line":235,"address":[],"length":0,"stats":{"Line":3}},{"line":237,"address":[138633],"length":1,"stats":{"Line":3}},{"line":239,"address":[],"length":0,"stats":{"Line":0}},{"line":243,"address":[220448,221836,221856,220780,221504,222208,221132,222540,222188,220800,221484,221152],"length":1,"stats":{"Line":18}},{"line":250,"address":[238608,238888,239240,238256,239312,239944,240016,238184,238536,238960,239592,239664],"length":1,"stats":{"Line":36}},{"line":251,"address":[143899,145659,144603,144251,145307,144955],"length":1,"stats":{"Line":8}},{"line":255,"address":[221468,222332,222368,220924,221628,221664,222016,221276,220960,220572,221312,222172,222524,220608,221116,220764,221980,221820],"length":1,"stats":{"Line":10}},{"line":258,"address":[221057,220705,221761,221409,222113,222465],"length":1,"stats":{"Line":10}},{"line":260,"address":[239154,239506,238802,239858,240210,238450],"length":1,"stats":{"Line":10}},{"line":262,"address":[238462,239166,239518,239870,240222,238814],"length":1,"stats":{"Line":10}},{"line":289,"address":[139030,138944,139056],"length":1,"stats":{"Line":8}},{"line":292,"address":[137454],"length":1,"stats":{"Line":8}},{"line":295,"address":[],"length":0,"stats":{"Line":8}},{"line":332,"address":[],"length":0,"stats":{"Line":2}},{"line":335,"address":[],"length":0,"stats":{"Line":6}},{"line":337,"address":[],"length":0,"stats":{"Line":4}},{"line":339,"address":[],"length":0,"stats":{"Line":0}},{"line":346,"address":[841888,841904],"length":1,"stats":{"Line":2}},{"line":347,"address":[],"length":0,"stats":{"Line":2}},{"line":352,"address":[],"length":0,"stats":{"Line":2}},{"line":353,"address":[],"length":0,"stats":{"Line":2}},{"line":372,"address":[],"length":0,"stats":{"Line":3}},{"line":374,"address":[],"length":0,"stats":{"Line":3}},{"line":376,"address":[],"length":0,"stats":{"Line":0}}],"covered":58,"coverable":72},{"path":["/","home","botahamec","Projects","happylock","src","mutex.rs"],"content":"use std::cell::UnsafeCell;\nuse std::marker::PhantomData;\n\nuse lock_api::RawMutex;\n\nuse crate::poisonable::PoisonFlag;\nuse crate::ThreadKey;\n\nmod guard;\nmod mutex;\n\n/// A spinning mutex\n#[cfg(feature = \"spin\")]\npub type SpinLock\u003cT\u003e = Mutex\u003cT, spin::Mutex\u003c()\u003e\u003e;\n\n/// A parking lot mutex\n#[cfg(feature = \"parking_lot\")]\npub type ParkingMutex\u003cT\u003e = Mutex\u003cT, parking_lot::RawMutex\u003e;\n\n/// A mutual exclusion primitive useful for protecting shared data, which\n/// cannot deadlock.\n///\n/// This mutex will block threads waiting for the lock to become available.\n/// Each mutex has a type parameter which represents the data that it is\n/// protecting. The data can only be accessed through the [`MutexGuard`]s\n/// returned from [`lock`] and [`try_lock`], which guarantees that the data is\n/// only ever accessed when the mutex is locked.\n///\n/// Locking the mutex on a thread that already locked it is impossible, due to\n/// the requirement of the [`ThreadKey`]. Therefore, this will never deadlock.\n///\n/// # Examples\n///\n/// ```\n/// use std::sync::Arc;\n/// use std::thread;\n/// use std::sync::mpsc;\n///\n/// use happylock::{Mutex, ThreadKey};\n///\n/// // Spawn a few threads to increment a shared variable (non-atomically),\n/// // and let the main thread know once all increments are done.\n/// //\n/// // Here we're using an Arc to share memory among threads, and the data\n/// // inside the Arc is protected with a mutex.\n/// const N: usize = 10;\n///\n/// let data = Arc::new(Mutex::new(0));\n///\n/// let (tx, rx) = mpsc::channel();\n/// for _ in 0..N {\n/// let (data, tx) = (Arc::clone(\u0026data), tx.clone());\n/// thread::spawn(move || {\n/// let key = ThreadKey::get().unwrap();\n/// let mut data = data.lock(key);\n/// *data += 1;\n/// if *data == N {\n/// tx.send(()).unwrap();\n/// }\n/// // the lock is unlocked\n/// });\n/// }\n///\n/// rx.recv().unwrap();\n/// ```\n///\n/// To unlock a mutex guard sooner than the end of the enclosing scope, either\n/// create an inner scope, drop the guard manually, or call [`Mutex::unlock`].\n///\n/// ```\n/// use std::sync::Arc;\n/// use std::thread;\n///\n/// use happylock::{Mutex, ThreadKey};\n///\n/// const N: usize = 3;\n///\n/// let data_mutex = Arc::new(Mutex::new(vec![1, 2, 3, 4]));\n/// let res_mutex = Arc::new(Mutex::new(0));\n///\n/// let mut threads = Vec::with_capacity(N);\n/// (0..N).for_each(|_| {\n/// let data_mutex_clone = Arc::clone(\u0026data_mutex);\n/// let res_mutex_clone = Arc::clone(\u0026res_mutex);\n///\n/// threads.push(thread::spawn(move || {\n/// let mut key = ThreadKey::get().unwrap();\n///\n/// // Here we use a block to limit the lifetime of the lock guard.\n/// let result = data_mutex_clone.scoped_lock(\u0026mut key, |data| {\n/// let result = data.iter().fold(0, |acc, x| acc + x * 2);\n/// data.push(result);\n/// result\n/// // The mutex guard gets dropped here, so the lock is released\n/// });\n/// // The thread key is available again\n/// *res_mutex_clone.lock(key) += result;\n/// }));\n/// });\n///\n/// let key = ThreadKey::get().unwrap();\n/// let mut data = data_mutex.lock(key);\n/// let result = data.iter().fold(0, |acc, x| acc + x * 2);\n/// data.push(result);\n///\n/// // We drop the `data` explicitly because it's not necessary anymore. This\n/// // allows other threads to start working on the data immediately. Dropping\n/// // the data also gives us access to the thread key, so we can lock\n/// // another mutex.\n/// let key = Mutex::unlock(data);\n///\n/// // Here the mutex guard is not assigned to a variable and so, even if the\n/// // scope does not end after this line, the mutex is still released: there is\n/// // no deadlock.\n/// *res_mutex.lock(key) += result;\n///\n/// threads.into_iter().for_each(|thread| {\n/// thread\n/// .join()\n/// .expect(\"The thread creating or execution failed !\")\n/// });\n///\n/// let key = ThreadKey::get().unwrap();\n/// assert_eq!(*res_mutex.lock(key), 800);\n/// ```\n///\n/// [`lock`]: `Mutex::lock`\n/// [`try_lock`]: `Mutex::try_lock`\n/// [`ThreadKey`]: `crate::ThreadKey`\npub struct Mutex\u003cT: ?Sized, R\u003e {\n\traw: R,\n\tpoison: PoisonFlag,\n\tdata: UnsafeCell\u003cT\u003e,\n}\n\n/// A reference to a mutex that unlocks it when dropped.\n///\n/// This is similar to [`MutexGuard`], except it does not hold a [`Keyable`].\npub struct MutexRef\u003c'a, T: ?Sized + 'a, R: RawMutex\u003e(\n\t\u0026'a Mutex\u003cT, R\u003e,\n\tPhantomData\u003c(\u0026'a mut T, R::GuardMarker)\u003e,\n);\n\n/// An RAII implementation of a “scoped lock” of a mutex.\n///\n/// When this structure is dropped (falls out of scope), the lock will be\n/// unlocked.\n///\n/// This is created by calling the [`lock`] and [`try_lock`] methods on [`Mutex`]\n///\n/// [`lock`]: `Mutex::lock`\n/// [`try_lock`]: `Mutex::try_lock`\n//\n// This is the most lifetime-intensive thing I've ever written. Can I graduate\n// from borrow checker university now?\npub struct MutexGuard\u003c'a, T: ?Sized + 'a, R: RawMutex\u003e {\n\tmutex: MutexRef\u003c'a, T, R\u003e, // this way we don't need to re-implement Drop\n\tthread_key: ThreadKey,\n}\n\n#[cfg(test)]\nmod tests {\n\tuse crate::{LockCollection, ThreadKey};\n\n\tuse super::*;\n\n\t#[test]\n\tfn unlocked_when_initialized() {\n\t\tlet lock: crate::Mutex\u003c_\u003e = Mutex::new(\"Hello, world!\");\n\n\t\tassert!(!lock.is_locked());\n\t}\n\n\t#[test]\n\tfn locked_after_read() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::Mutex\u003c_\u003e = Mutex::new(\"Hello, world!\");\n\n\t\tlet guard = lock.lock(key);\n\n\t\tassert!(lock.is_locked());\n\t\tdrop(guard)\n\t}\n\n\t#[test]\n\tfn display_works_for_guard() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex: crate::Mutex\u003c_\u003e = Mutex::new(\"Hello, world!\");\n\t\tlet guard = mutex.lock(key);\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn display_works_for_ref() {\n\t\tlet mutex: crate::Mutex\u003c_\u003e = Mutex::new(\"Hello, world!\");\n\t\tlet guard = unsafe { mutex.try_lock_no_key().unwrap() };\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn ref_as_mut() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = LockCollection::new(crate::Mutex::new(0));\n\t\tlet mut guard = collection.lock(key);\n\t\tlet guard_mut = guard.as_mut().as_mut();\n\n\t\t*guard_mut = 3;\n\t\tlet key = LockCollection::\u003ccrate::Mutex\u003c_\u003e\u003e::unlock(guard);\n\n\t\tlet guard = collection.lock(key);\n\n\t\tassert_eq!(guard.as_ref().as_ref(), \u00263);\n\t}\n\n\t#[test]\n\tfn guard_as_mut() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = crate::Mutex::new(0);\n\t\tlet mut guard = mutex.lock(key);\n\t\tlet guard_mut = guard.as_mut();\n\n\t\t*guard_mut = 3;\n\t\tlet key = Mutex::unlock(guard);\n\n\t\tlet guard = mutex.lock(key);\n\n\t\tassert_eq!(guard.as_ref(), \u00263);\n\t}\n\n\t#[test]\n\tfn dropping_guard_releases_mutex() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex: crate::Mutex\u003c_\u003e = Mutex::new(\"Hello, world!\");\n\n\t\tlet guard = mutex.lock(key);\n\t\tdrop(guard);\n\n\t\tassert!(!mutex.is_locked());\n\t}\n\n\t#[test]\n\tfn dropping_ref_releases_mutex() {\n\t\tlet mutex: crate::Mutex\u003c_\u003e = Mutex::new(\"Hello, world!\");\n\n\t\tlet guard = unsafe { mutex.try_lock_no_key().unwrap() };\n\t\tdrop(guard);\n\n\t\tassert!(!mutex.is_locked());\n\t}\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","src","poisonable","error.rs"],"content":"use core::fmt;\nuse std::error::Error;\n\nuse super::{PoisonError, PoisonGuard, TryLockPoisonableError};\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard\u003e fmt::Debug for PoisonError\u003cGuard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut fmt::Formatter\u003c'_\u003e) -\u003e fmt::Result {\n\t\tf.debug_struct(\"PoisonError\").finish_non_exhaustive()\n\t}\n}\n\nimpl\u003cGuard\u003e fmt::Display for PoisonError\u003cGuard\u003e {\n\t#[cfg_attr(test, mutants::skip)]\n\t#[cfg(not(tarpaulin_include))]\n\tfn fmt(\u0026self, f: \u0026mut fmt::Formatter\u003c'_\u003e) -\u003e fmt::Result {\n\t\t\"poisoned lock: another task failed inside\".fmt(f)\n\t}\n}\n\nimpl\u003cGuard\u003e AsRef\u003cGuard\u003e for PoisonError\u003cGuard\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026Guard {\n\t\tself.get_ref()\n\t}\n}\n\nimpl\u003cGuard\u003e AsMut\u003cGuard\u003e for PoisonError\u003cGuard\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut Guard {\n\t\tself.get_mut()\n\t}\n}\n\nimpl\u003cGuard\u003e Error for PoisonError\u003cGuard\u003e {}\n\nimpl\u003cGuard\u003e PoisonError\u003cGuard\u003e {\n\t/// Creates a `PoisonError`\n\t///\n\t/// This is generally created by methods like [`Poisonable::lock`].\n\t///\n\t/// ```\n\t/// use happylock::poisonable::PoisonError;\n\t///\n\t/// let error = PoisonError::new(\"oh no\");\n\t/// ```\n\t///\n\t/// [`Poisonable::lock`]: `crate::poisonable::Poisonable::lock`\n\t#[must_use]\n\tpub const fn new(guard: Guard) -\u003e Self {\n\t\tSelf { guard }\n\t}\n\n\t/// Consumes the error indicating that a lock is poisonmed, returning the\n\t/// underlying guard to allow access regardless.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::collections::HashSet;\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let mutex = Arc::new(Poisonable::new(Mutex::new(HashSet::new())));\n\t///\n\t/// // poison the mutex\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t/// let _ = thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut data = c_mutex.lock(key).unwrap();\n\t/// data.insert(10);\n\t/// panic!();\n\t/// }).join();\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let p_err = mutex.lock(key).unwrap_err();\n\t/// let data = p_err.into_inner();\n\t/// println!(\"recovered {} items\", data.len());\n\t/// ```\n\t#[must_use]\n\tpub fn into_inner(self) -\u003e Guard {\n\t\tself.guard\n\t}\n\n\t/// Reaches into this error indicating that a lock is poisoned, returning a\n\t/// reference to the underlying guard to allow access regardless.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::collections::HashSet;\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t/// use happylock::poisonable::PoisonGuard;\n\t///\n\t/// let mutex = Arc::new(Poisonable::new(Mutex::new(HashSet::new())));\n\t///\n\t/// // poison the mutex\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t/// let _ = thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut data = c_mutex.lock(key).unwrap();\n\t/// data.insert(10);\n\t/// panic!();\n\t/// }).join();\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let p_err = mutex.lock(key).unwrap_err();\n\t/// let data: \u0026PoisonGuard\u003c_, _\u003e = p_err.get_ref();\n\t/// println!(\"recovered {} items\", data.len());\n\t/// ```\n\t#[must_use]\n\tpub const fn get_ref(\u0026self) -\u003e \u0026Guard {\n\t\t\u0026self.guard\n\t}\n\n\t/// Reaches into this error indicating that a lock is poisoned, returning a\n\t/// mutable reference to the underlying guard to allow access regardless.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::collections::HashSet;\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let mutex = Arc::new(Poisonable::new(Mutex::new(HashSet::new())));\n\t///\n\t/// // poison the mutex\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t/// let _ = thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut data = c_mutex.lock(key).unwrap();\n\t/// data.insert(10);\n\t/// panic!();\n\t/// }).join();\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut p_err = mutex.lock(key).unwrap_err();\n\t/// let data = p_err.get_mut();\n\t/// data.insert(20);\n\t/// println!(\"recovered {} items\", data.len());\n\t/// ```\n\t#[must_use]\n\tpub fn get_mut(\u0026mut self) -\u003e \u0026mut Guard {\n\t\t\u0026mut self.guard\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cG\u003e fmt::Debug for TryLockPoisonableError\u003c'_, G\u003e {\n\tfn fmt(\u0026self, f: \u0026mut fmt::Formatter\u003c'_\u003e) -\u003e fmt::Result {\n\t\tmatch *self {\n\t\t\tSelf::Poisoned(..) =\u003e \"Poisoned(..)\".fmt(f),\n\t\t\tSelf::WouldBlock(_) =\u003e \"WouldBlock\".fmt(f),\n\t\t}\n\t}\n}\n\nimpl\u003cG\u003e fmt::Display for TryLockPoisonableError\u003c'_, G\u003e {\n\t#[cfg_attr(test, mutants::skip)]\n\t#[cfg(not(tarpaulin_include))]\n\tfn fmt(\u0026self, f: \u0026mut fmt::Formatter\u003c'_\u003e) -\u003e fmt::Result {\n\t\tmatch *self {\n\t\t\tSelf::Poisoned(..) =\u003e \"poisoned lock: another task failed inside\",\n\t\t\tSelf::WouldBlock(_) =\u003e \"try_lock failed because the operation would block\",\n\t\t}\n\t\t.fmt(f)\n\t}\n}\n\nimpl\u003cG\u003e Error for TryLockPoisonableError\u003c'_, G\u003e {}\n\nimpl\u003c'flag, G\u003e From\u003cPoisonError\u003cPoisonGuard\u003c'flag, G\u003e\u003e\u003e for TryLockPoisonableError\u003c'flag, G\u003e {\n\tfn from(value: PoisonError\u003cPoisonGuard\u003c'flag, G\u003e\u003e) -\u003e Self {\n\t\tSelf::Poisoned(value)\n\t}\n}\n","traces":[{"line":23,"address":[157824],"length":1,"stats":{"Line":1}},{"line":24,"address":[],"length":0,"stats":{"Line":1}},{"line":29,"address":[],"length":0,"stats":{"Line":1}},{"line":30,"address":[],"length":0,"stats":{"Line":1}},{"line":49,"address":[],"length":0,"stats":{"Line":7}},{"line":82,"address":[],"length":0,"stats":{"Line":4}},{"line":83,"address":[],"length":0,"stats":{"Line":1}},{"line":116,"address":[],"length":0,"stats":{"Line":3}},{"line":117,"address":[],"length":0,"stats":{"Line":0}},{"line":150,"address":[],"length":0,"stats":{"Line":2}},{"line":151,"address":[],"length":0,"stats":{"Line":0}},{"line":181,"address":[],"length":0,"stats":{"Line":1}},{"line":182,"address":[],"length":0,"stats":{"Line":1}}],"covered":11,"coverable":13},{"path":["/","home","botahamec","Projects","happylock","src","poisonable","flag.rs"],"content":"#[cfg(panic = \"unwind\")]\nuse std::sync::atomic::{AtomicBool, Ordering::Relaxed};\n\nuse super::PoisonFlag;\n\n#[cfg(panic = \"unwind\")]\nimpl PoisonFlag {\n\tpub const fn new() -\u003e Self {\n\t\tSelf(AtomicBool::new(false))\n\t}\n\n\tpub fn is_poisoned(\u0026self) -\u003e bool {\n\t\tself.0.load(Relaxed)\n\t}\n\n\tpub fn clear_poison(\u0026self) {\n\t\tself.0.store(false, Relaxed)\n\t}\n\n\tpub fn poison(\u0026self) {\n\t\tself.0.store(true, Relaxed);\n\t}\n}\n\n#[cfg(not(panic = \"unwind\"))]\nimpl PoisonFlag {\n\tpub const fn new() -\u003e Self {\n\t\tSelf()\n\t}\n\n\t#[mutants::skip] // None of the tests have panic = \"abort\", so this can't be tested\n\t#[cfg(not(tarpaulin_include))]\n\tpub fn is_poisoned(\u0026self) -\u003e bool {\n\t\tfalse\n\t}\n\n\tpub fn clear_poison(\u0026self) {\n\t\t()\n\t}\n\n\tpub fn poison(\u0026self) {\n\t\t()\n\t}\n}\n","traces":[{"line":8,"address":[539072],"length":1,"stats":{"Line":13}},{"line":9,"address":[506625],"length":1,"stats":{"Line":15}},{"line":12,"address":[538208],"length":1,"stats":{"Line":12}},{"line":13,"address":[539129],"length":1,"stats":{"Line":16}},{"line":16,"address":[539632],"length":1,"stats":{"Line":1}},{"line":17,"address":[538249],"length":1,"stats":{"Line":1}},{"line":20,"address":[534064],"length":1,"stats":{"Line":8}},{"line":21,"address":[538281],"length":1,"stats":{"Line":12}}],"covered":8,"coverable":8},{"path":["/","home","botahamec","Projects","happylock","src","poisonable","guard.rs"],"content":"use std::fmt::{Debug, Display};\nuse std::hash::Hash;\nuse std::marker::PhantomData;\nuse std::ops::{Deref, DerefMut};\n\nuse super::{PoisonFlag, PoisonGuard, PoisonRef};\n\nimpl\u003c'a, Guard\u003e PoisonRef\u003c'a, Guard\u003e {\n\t// This is used so that we don't keep accidentally adding the flag reference\n\tpub(super) const fn new(flag: \u0026'a PoisonFlag, guard: Guard) -\u003e Self {\n\t\tSelf {\n\t\t\tguard,\n\t\t\t#[cfg(panic = \"unwind\")]\n\t\t\tflag,\n\t\t\t_phantom: PhantomData,\n\t\t}\n\t}\n}\n\nimpl\u003cGuard\u003e Drop for PoisonRef\u003c'_, Guard\u003e {\n\tfn drop(\u0026mut self) {\n\t\t#[cfg(panic = \"unwind\")]\n\t\tif std::thread::panicking() {\n\t\t\tself.flag.poison();\n\t\t}\n\t}\n}\n\n#[mutants::skip] // hashing involves RNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard: Hash\u003e Hash for PoisonRef\u003c'_, Guard\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.guard.hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard: Debug\u003e Debug for PoisonRef\u003c'_, Guard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cGuard: Display\u003e Display for PoisonRef\u003c'_, Guard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cGuard\u003e Deref for PoisonRef\u003c'_, Guard\u003e {\n\ttype Target = Guard;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t\u0026self.guard\n\t}\n}\n\nimpl\u003cGuard\u003e DerefMut for PoisonRef\u003c'_, Guard\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t\u0026mut self.guard\n\t}\n}\n\nimpl\u003cGuard\u003e AsRef\u003cGuard\u003e for PoisonRef\u003c'_, Guard\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026Guard {\n\t\t\u0026self.guard\n\t}\n}\n\nimpl\u003cGuard\u003e AsMut\u003cGuard\u003e for PoisonRef\u003c'_, Guard\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut Guard {\n\t\t\u0026mut self.guard\n\t}\n}\n\n#[mutants::skip] // hashing involves RNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard: Hash\u003e Hash for PoisonGuard\u003c'_, Guard\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.guard.hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard: Debug\u003e Debug for PoisonGuard\u003c'_, Guard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026self.guard, f)\n\t}\n}\n\nimpl\u003cGuard: Display\u003e Display for PoisonGuard\u003c'_, Guard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026self.guard, f)\n\t}\n}\n\nimpl\u003cT, Guard: Deref\u003cTarget = T\u003e\u003e Deref for PoisonGuard\u003c'_, Guard\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t#[allow(clippy::explicit_auto_deref)] // fixing this results in a compiler error\n\t\t\u0026*self.guard.guard\n\t}\n}\n\nimpl\u003cT, Guard: DerefMut\u003cTarget = T\u003e\u003e DerefMut for PoisonGuard\u003c'_, Guard\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t#[allow(clippy::explicit_auto_deref)] // fixing this results in a compiler error\n\t\t\u0026mut *self.guard.guard\n\t}\n}\n\nimpl\u003cGuard\u003e AsRef\u003cGuard\u003e for PoisonGuard\u003c'_, Guard\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026Guard {\n\t\t\u0026self.guard.guard\n\t}\n}\n\nimpl\u003cGuard\u003e AsMut\u003cGuard\u003e for PoisonGuard\u003c'_, Guard\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut Guard {\n\t\t\u0026mut self.guard.guard\n\t}\n}\n","traces":[{"line":10,"address":[158336,158304],"length":1,"stats":{"Line":2}},{"line":21,"address":[],"length":0,"stats":{"Line":2}},{"line":22,"address":[],"length":0,"stats":{"Line":0}},{"line":23,"address":[],"length":0,"stats":{"Line":2}},{"line":24,"address":[],"length":0,"stats":{"Line":3}},{"line":46,"address":[],"length":0,"stats":{"Line":1}},{"line":47,"address":[],"length":0,"stats":{"Line":1}},{"line":54,"address":[],"length":0,"stats":{"Line":2}},{"line":55,"address":[],"length":0,"stats":{"Line":0}},{"line":60,"address":[],"length":0,"stats":{"Line":1}},{"line":61,"address":[],"length":0,"stats":{"Line":0}},{"line":66,"address":[],"length":0,"stats":{"Line":1}},{"line":67,"address":[],"length":0,"stats":{"Line":0}},{"line":72,"address":[],"length":0,"stats":{"Line":1}},{"line":73,"address":[],"length":0,"stats":{"Line":0}},{"line":94,"address":[],"length":0,"stats":{"Line":1}},{"line":95,"address":[],"length":0,"stats":{"Line":1}},{"line":102,"address":[],"length":0,"stats":{"Line":1}},{"line":104,"address":[],"length":0,"stats":{"Line":1}},{"line":109,"address":[],"length":0,"stats":{"Line":2}},{"line":111,"address":[],"length":0,"stats":{"Line":2}},{"line":116,"address":[],"length":0,"stats":{"Line":1}},{"line":117,"address":[],"length":0,"stats":{"Line":0}},{"line":122,"address":[],"length":0,"stats":{"Line":1}},{"line":123,"address":[],"length":0,"stats":{"Line":0}}],"covered":18,"coverable":25},{"path":["/","home","botahamec","Projects","happylock","src","poisonable","poisonable.rs"],"content":"use std::panic::{RefUnwindSafe, UnwindSafe};\n\nuse crate::lockable::{\n\tLockable, LockableGetMut, LockableIntoInner, OwnedLockable, RawLock, Sharable,\n};\nuse crate::{Keyable, ThreadKey};\n\nuse super::{\n\tPoisonError, PoisonFlag, PoisonGuard, PoisonRef, PoisonResult, Poisonable,\n\tTryLockPoisonableError, TryLockPoisonableResult,\n};\n\nunsafe impl\u003cL: Lockable + RawLock\u003e RawLock for Poisonable\u003cL\u003e {\n\t#[mutants::skip] // this should never run\n\t#[cfg(not(tarpaulin_include))]\n\tfn poison(\u0026self) {\n\t\tself.inner.poison()\n\t}\n\n\tunsafe fn raw_lock(\u0026self) {\n\t\tself.inner.raw_lock()\n\t}\n\n\tunsafe fn raw_try_lock(\u0026self) -\u003e bool {\n\t\tself.inner.raw_try_lock()\n\t}\n\n\tunsafe fn raw_unlock(\u0026self) {\n\t\tself.inner.raw_unlock()\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tself.inner.raw_read()\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tself.inner.raw_try_read()\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tself.inner.raw_unlock_read()\n\t}\n}\n\nunsafe impl\u003cL: Lockable\u003e Lockable for Poisonable\u003cL\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= PoisonResult\u003cPoisonRef\u003c'g, L::Guard\u003c'g\u003e\u003e\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= PoisonResult\u003cL::DataMut\u003c'a\u003e\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tself.inner.get_ptrs(ptrs)\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tlet ref_guard = PoisonRef::new(\u0026self.poisoned, self.inner.guard());\n\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(ref_guard))\n\t\t} else {\n\t\t\tOk(ref_guard)\n\t\t}\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(self.inner.data_mut()))\n\t\t} else {\n\t\t\tOk(self.inner.data_mut())\n\t\t}\n\t}\n}\n\nunsafe impl\u003cL: Sharable\u003e Sharable for Poisonable\u003cL\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= PoisonResult\u003cPoisonRef\u003c'g, L::ReadGuard\u003c'g\u003e\u003e\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= PoisonResult\u003cL::DataRef\u003c'a\u003e\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tlet ref_guard = PoisonRef::new(\u0026self.poisoned, self.inner.read_guard());\n\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(ref_guard))\n\t\t} else {\n\t\t\tOk(ref_guard)\n\t\t}\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(self.inner.data_ref()))\n\t\t} else {\n\t\t\tOk(self.inner.data_ref())\n\t\t}\n\t}\n}\n\nunsafe impl\u003cL: OwnedLockable\u003e OwnedLockable for Poisonable\u003cL\u003e {}\n\n// AsMut won't work here because we don't strictly return a \u0026mut T\n// LockableGetMut is the next best thing\nimpl\u003cL: LockableGetMut\u003e LockableGetMut for Poisonable\u003cL\u003e {\n\ttype Inner\u003c'a\u003e\n\t\t= PoisonResult\u003cL::Inner\u003c'a\u003e\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(self.inner.get_mut()))\n\t\t} else {\n\t\t\tOk(self.inner.get_mut())\n\t\t}\n\t}\n}\n\nimpl\u003cL: LockableIntoInner\u003e LockableIntoInner for Poisonable\u003cL\u003e {\n\ttype Inner = PoisonResult\u003cL::Inner\u003e;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(self.inner.into_inner()))\n\t\t} else {\n\t\t\tOk(self.inner.into_inner())\n\t\t}\n\t}\n}\n\nimpl\u003cL\u003e From\u003cL\u003e for Poisonable\u003cL\u003e {\n\tfn from(value: L) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\nimpl\u003cL\u003e Poisonable\u003cL\u003e {\n\t/// Creates a new `Poisonable`\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, Poisonable};\n\t///\n\t/// let mutex = Poisonable::new(Mutex::new(0));\n\t/// ```\n\tpub const fn new(value: L) -\u003e Self {\n\t\tSelf {\n\t\t\tinner: value,\n\t\t\tpoisoned: PoisonFlag::new(),\n\t\t}\n\t}\n\n\t/// Determines whether the mutex is poisoned.\n\t///\n\t/// If another thread is active, the mutex can still become poisoned at any\n\t/// time. You should not trust a `false` value for program correctness\n\t/// without additional synchronization.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let mutex = Arc::new(Poisonable::new(Mutex::new(0)));\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t///\n\t/// let _ = thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let _lock = c_mutex.lock(key).unwrap();\n\t/// panic!(); // the mutex gets poisoned\n\t/// }).join();\n\t///\n\t/// assert_eq!(mutex.is_poisoned(), true);\n\t/// ```\n\tpub fn is_poisoned(\u0026self) -\u003e bool {\n\t\tself.poisoned.is_poisoned()\n\t}\n\n\t/// Clear the poisoned state from a lock.\n\t///\n\t/// If the lock is poisoned, it will remain poisoned until this function\n\t/// is called. This allows recovering from a poisoned state and marking\n\t/// that it has recovered. For example, if the value is overwritten by a\n\t/// known-good value, then the lock can be marked as un-poisoned. Or\n\t/// possibly, the value could by inspected to determine if it is in a\n\t/// consistent state, and if so the poison is removed.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let mutex = Arc::new(Poisonable::new(Mutex::new(0)));\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t///\n\t/// let _ = thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let _lock = c_mutex.lock(key).unwrap();\n\t/// panic!(); // the mutex gets poisoned\n\t/// }).join();\n\t///\n\t/// assert_eq!(mutex.is_poisoned(), true);\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let x = mutex.lock(key).unwrap_or_else(|mut e| {\n\t/// **e.get_mut() = 1;\n\t/// mutex.clear_poison();\n\t/// e.into_inner()\n\t/// });\n\t///\n\t/// assert_eq!(mutex.is_poisoned(), false);\n\t/// assert_eq!(*x, 1);\n\t/// ```\n\tpub fn clear_poison(\u0026self) {\n\t\tself.poisoned.clear_poison()\n\t}\n\n\t/// Consumes this `Poisonable`, returning the underlying lock.\n\t///\n\t/// This consumes the `Poisonable` and returns ownership of the lock, which\n\t/// means that the `Poisonable` can still be `RefUnwindSafe`.\n\t///\n\t/// # Errors\n\t///\n\t/// If another user of this lock panicked while holding the lock, then this\n\t/// call will return an error instead.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, Poisonable};\n\t///\n\t/// let mutex = Poisonable::new(Mutex::new(0));\n\t/// assert_eq!(mutex.into_child().unwrap().into_inner(), 0);\n\t/// ```\n\tpub fn into_child(self) -\u003e PoisonResult\u003cL\u003e {\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(self.inner))\n\t\t} else {\n\t\t\tOk(self.inner)\n\t\t}\n\t}\n\n\t/// Returns a mutable reference to the underlying lock.\n\t///\n\t/// This can be implemented while still being `RefUnwindSafe` because\n\t/// it requires a mutable reference.\n\t///\n\t/// # Errors\n\t///\n\t/// If another user of this lock panicked while holding the lock, then\n\t/// this call will return an error instead.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut mutex = Poisonable::new(Mutex::new(0));\n\t/// *mutex.child_mut().unwrap().as_mut() = 10;\n\t/// assert_eq!(*mutex.lock(key).unwrap(), 10);\n\t/// ```\n\tpub fn child_mut(\u0026mut self) -\u003e PoisonResult\u003c\u0026mut L\u003e {\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(\u0026mut self.inner))\n\t\t} else {\n\t\t\tOk(\u0026mut self.inner)\n\t\t}\n\t}\n\n\t// NOTE: `child_ref` isn't implemented because it would make this not `RefUnwindSafe`\n\t//\n}\n\nimpl\u003cL: Lockable\u003e Poisonable\u003cL\u003e {\n\t/// Creates a guard for the poisonable, without locking it\n\tunsafe fn guard(\u0026self, key: ThreadKey) -\u003e PoisonResult\u003cPoisonGuard\u003c'_, L::Guard\u003c'_\u003e\u003e\u003e {\n\t\tlet guard = PoisonGuard {\n\t\t\tguard: PoisonRef::new(\u0026self.poisoned, self.inner.guard()),\n\t\t\tkey,\n\t\t};\n\n\t\tif self.is_poisoned() {\n\t\t\treturn Err(PoisonError::new(guard));\n\t\t}\n\n\t\tOk(guard)\n\t}\n}\n\nimpl\u003cL: Lockable + RawLock\u003e Poisonable\u003cL\u003e {\n\tpub fn scoped_lock\u003c'a, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: impl Keyable,\n\t\tf: impl Fn(\u003cSelf as Lockable\u003e::DataMut\u003c'a\u003e) -\u003e R,\n\t) -\u003e R {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_lock();\n\n\t\t\t// safety: the data was just locked\n\t\t\tlet r = f(self.data_mut());\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock();\n\n\t\t\tdrop(key); // ensure the key stays alive long enough\n\n\t\t\tr\n\t\t}\n\t}\n\n\tpub fn scoped_try_lock\u003c'a, Key: Keyable, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(\u003cSelf as Lockable\u003e::DataMut\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_lock() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we just locked the collection\n\t\t\tlet r = f(self.data_mut());\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock();\n\n\t\t\tdrop(key); // ensures the key stays valid long enough\n\n\t\t\tOk(r)\n\t\t}\n\t}\n\n\t/// Acquires the lock, blocking the current thread until it is ok to do so.\n\t///\n\t/// This function will block the current thread until it is available to\n\t/// acquire the mutex. Upon returning, the thread is the only thread with\n\t/// the lock held. An RAII guard is returned to allow scoped unlock of the\n\t/// lock. When the guard goes out of scope, the mutex will be unlocked.\n\t///\n\t/// # Errors\n\t///\n\t/// If another use of this mutex panicked while holding the mutex, then\n\t/// this call will return an error once the mutex is acquired.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let mutex = Arc::new(Poisonable::new(Mutex::new(0)));\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t///\n\t/// thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// *c_mutex.lock(key).unwrap() = 10;\n\t/// }).join().expect(\"thread::spawn failed\");\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// assert_eq!(*mutex.lock(key).unwrap(), 10);\n\t/// ```\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e PoisonResult\u003cPoisonGuard\u003c'_, L::Guard\u003c'_\u003e\u003e\u003e {\n\t\tunsafe {\n\t\t\tself.inner.raw_lock();\n\t\t\tself.guard(key)\n\t\t}\n\t}\n\n\t/// Attempts to acquire this lock.\n\t///\n\t/// If the lock could not be acquired at this time, then [`Err`] is\n\t/// returned. Otherwise, an RAII guard is returned. The lock will be\n\t/// unlocked when the guard is dropped.\n\t///\n\t/// This function does not block.\n\t///\n\t/// # Errors\n\t///\n\t/// If another user of this mutex panicked while holding the mutex, then\n\t/// this call will return the [`Poisoned`] error if the mutex would\n\t/// otherwise be acquired.\n\t///\n\t/// If the mutex could not be acquired because it is already locked, then\n\t/// this call will return the [`WouldBlock`] error.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let mutex = Arc::new(Poisonable::new(Mutex::new(0)));\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t///\n\t/// thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut lock = c_mutex.try_lock(key);\n\t/// if let Ok(mut mutex) = lock {\n\t/// *mutex = 10;\n\t/// } else {\n\t/// println!(\"try_lock failed\");\n\t/// }\n\t/// }).join().expect(\"thread::spawn failed\");\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// assert_eq!(*mutex.lock(key).unwrap(), 10);\n\t/// ```\n\t///\n\t/// [`Poisoned`]: `TryLockPoisonableError::Poisoned`\n\t/// [`WouldBlock`]: `TryLockPoisonableError::WouldBlock`\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e TryLockPoisonableResult\u003c'_, L::Guard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\tif self.inner.raw_try_lock() {\n\t\t\t\tOk(self.guard(key)?)\n\t\t\t} else {\n\t\t\t\tErr(TryLockPoisonableError::WouldBlock(key))\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Consumes the [`PoisonGuard`], and consequently unlocks its `Poisonable`.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, Mutex, Poisonable};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mutex = Poisonable::new(Mutex::new(0));\n\t///\n\t/// let mut guard = mutex.lock(key).unwrap();\n\t/// *guard += 20;\n\t///\n\t/// let key = Poisonable::\u003cMutex\u003c_\u003e\u003e::unlock(guard);\n\t/// ```\n\tpub fn unlock\u003c'flag\u003e(guard: PoisonGuard\u003c'flag, L::Guard\u003c'flag\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: Sharable + RawLock\u003e Poisonable\u003cL\u003e {\n\tunsafe fn read_guard(\u0026self, key: ThreadKey) -\u003e PoisonResult\u003cPoisonGuard\u003c'_, L::ReadGuard\u003c'_\u003e\u003e\u003e {\n\t\tlet guard = PoisonGuard {\n\t\t\tguard: PoisonRef::new(\u0026self.poisoned, self.inner.read_guard()),\n\t\t\tkey,\n\t\t};\n\n\t\tif self.is_poisoned() {\n\t\t\treturn Err(PoisonError::new(guard));\n\t\t}\n\n\t\tOk(guard)\n\t}\n\n\tpub fn scoped_read\u003c'a, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: impl Keyable,\n\t\tf: impl Fn(\u003cSelf as Sharable\u003e::DataRef\u003c'a\u003e) -\u003e R,\n\t) -\u003e R {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_read();\n\n\t\t\t// safety: the data was just locked\n\t\t\tlet r = f(self.data_ref());\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock_read();\n\n\t\t\tdrop(key); // ensure the key stays alive long enough\n\n\t\t\tr\n\t\t}\n\t}\n\n\tpub fn scoped_try_read\u003c'a, Key: Keyable, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(\u003cSelf as Sharable\u003e::DataRef\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_read() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we just locked the collection\n\t\t\tlet r = f(self.data_ref());\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock_read();\n\n\t\t\tdrop(key); // ensures the key stays valid long enough\n\n\t\t\tOk(r)\n\t\t}\n\t}\n\n\t/// Locks with shared read access, blocking the current thread until it can\n\t/// be acquired.\n\t///\n\t/// This function will block the current thread until there are no writers\n\t/// which hold the lock. This method does not provide any guarantee with\n\t/// respect to the ordering of contentious readers or writers will acquire\n\t/// the lock.\n\t///\n\t/// # Errors\n\t///\n\t/// If another use of this lock panicked while holding the lock, then\n\t/// this call will return an error once the lock is acquired.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t///\n\t/// use happylock::{RwLock, Poisonable, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = Arc::new(Poisonable::new(RwLock::new(0)));\n\t/// let c_lock = Arc::clone(\u0026lock);\n\t///\n\t/// let n = lock.read(key).unwrap();\n\t/// assert_eq!(*n, 0);\n\t///\n\t/// thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// assert!(c_lock.read(key).is_ok());\n\t/// }).join().expect(\"thread::spawn failed\");\n\t/// ```\n\tpub fn read(\u0026self, key: ThreadKey) -\u003e PoisonResult\u003cPoisonGuard\u003c'_, L::ReadGuard\u003c'_\u003e\u003e\u003e {\n\t\tunsafe {\n\t\t\tself.inner.raw_read();\n\t\t\tself.read_guard(key)\n\t\t}\n\t}\n\n\t/// Attempts to acquire the lock with shared read access.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is returned.\n\t/// Otherwise, an RAII guard is returned which will release the shared access\n\t/// when it is dropped.\n\t///\n\t/// This function does not block.\n\t///\n\t/// This function does not provide any guarantees with respect to the ordering\n\t/// of whether contentious readers or writers will acquire the lock first.\n\t///\n\t/// # Errors\n\t///\n\t/// This function will return the [`Poisoned`] error if the lock is\n\t/// poisoned. A [`Poisonable`] is poisoned whenever a writer panics while\n\t/// holding an exclusive lock. `Poisoned` will only be returned if the lock\n\t/// would have otherwise been acquired.\n\t///\n\t/// This function will return the [`WouldBlock`] error if the lock could\n\t/// not be acquired because it was already locked exclusively.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Poisonable, RwLock, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = Poisonable::new(RwLock::new(1));\n\t///\n\t/// match lock.try_read(key) {\n\t/// Ok(n) =\u003e assert_eq!(*n, 1),\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t/// ```\n\t///\n\t/// [`Poisoned`]: `TryLockPoisonableError::Poisoned`\n\t/// [`WouldBlock`]: `TryLockPoisonableError::WouldBlock`\n\tpub fn try_read(\u0026self, key: ThreadKey) -\u003e TryLockPoisonableResult\u003c'_, L::ReadGuard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\tif self.inner.raw_try_read() {\n\t\t\t\tOk(self.read_guard(key)?)\n\t\t\t} else {\n\t\t\t\tErr(TryLockPoisonableError::WouldBlock(key))\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Consumes the [`PoisonGuard`], and consequently unlocks its underlying lock.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, RwLock, Poisonable};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = Poisonable::new(RwLock::new(0));\n\t///\n\t/// let mut guard = lock.read(key).unwrap();\n\t/// let key = Poisonable::\u003cRwLock\u003c_\u003e\u003e::unlock_read(guard);\n\t/// ```\n\tpub fn unlock_read\u003c'flag\u003e(guard: PoisonGuard\u003c'flag, L::ReadGuard\u003c'flag\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: LockableIntoInner\u003e Poisonable\u003cL\u003e {\n\t/// Consumes this `Poisonable`, returning the underlying data.\n\t///\n\t/// # Errors\n\t///\n\t/// If another user of this lock panicked while holding the lock, then this\n\t/// call will return an error instead.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, Poisonable};\n\t///\n\t/// let mutex = Poisonable::new(Mutex::new(0));\n\t/// assert_eq!(mutex.into_inner().unwrap(), 0);\n\t/// ```\n\tpub fn into_inner(self) -\u003e PoisonResult\u003cL::Inner\u003e {\n\t\tLockableIntoInner::into_inner(self)\n\t}\n}\n\nimpl\u003cL: LockableGetMut + RawLock\u003e Poisonable\u003cL\u003e {\n\t/// Returns a mutable reference to the underlying data.\n\t///\n\t/// Since this call borrows the `Poisonable` mutably, no actual locking\n\t/// needs to take place - the mutable borrow statically guarantees no locks\n\t/// exist.\n\t///\n\t/// # Errors\n\t///\n\t/// If another user of this lock panicked while holding the lock, then\n\t/// this call will return an error instead.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut mutex = Poisonable::new(Mutex::new(0));\n\t/// *mutex.get_mut().unwrap() = 10;\n\t/// assert_eq!(*mutex.lock(key).unwrap(), 10);\n\t/// ```\n\tpub fn get_mut(\u0026mut self) -\u003e PoisonResult\u003cL::Inner\u003c'_\u003e\u003e {\n\t\tLockableGetMut::get_mut(self)\n\t}\n}\n\nimpl\u003cL: UnwindSafe\u003e RefUnwindSafe for Poisonable\u003cL\u003e {}\nimpl\u003cL: UnwindSafe\u003e UnwindSafe for Poisonable\u003cL\u003e {}\n","traces":[{"line":20,"address":[],"length":0,"stats":{"Line":0}},{"line":21,"address":[],"length":0,"stats":{"Line":0}},{"line":24,"address":[],"length":0,"stats":{"Line":0}},{"line":25,"address":[],"length":0,"stats":{"Line":0}},{"line":28,"address":[],"length":0,"stats":{"Line":0}},{"line":29,"address":[],"length":0,"stats":{"Line":0}},{"line":32,"address":[],"length":0,"stats":{"Line":0}},{"line":33,"address":[],"length":0,"stats":{"Line":0}},{"line":36,"address":[],"length":0,"stats":{"Line":0}},{"line":37,"address":[],"length":0,"stats":{"Line":0}},{"line":40,"address":[],"length":0,"stats":{"Line":0}},{"line":41,"address":[],"length":0,"stats":{"Line":0}},{"line":56,"address":[],"length":0,"stats":{"Line":3}},{"line":57,"address":[158654,158622],"length":1,"stats":{"Line":3}},{"line":60,"address":[],"length":0,"stats":{"Line":3}},{"line":61,"address":[],"length":0,"stats":{"Line":3}},{"line":63,"address":[159135,158759,159212,158806,159047,158847,158924,159094],"length":1,"stats":{"Line":10}},{"line":64,"address":[],"length":0,"stats":{"Line":2}},{"line":66,"address":[],"length":0,"stats":{"Line":3}},{"line":70,"address":[],"length":0,"stats":{"Line":0}},{"line":71,"address":[],"length":0,"stats":{"Line":0}},{"line":72,"address":[],"length":0,"stats":{"Line":0}},{"line":74,"address":[],"length":0,"stats":{"Line":0}},{"line":90,"address":[],"length":0,"stats":{"Line":0}},{"line":91,"address":[],"length":0,"stats":{"Line":0}},{"line":93,"address":[],"length":0,"stats":{"Line":0}},{"line":94,"address":[],"length":0,"stats":{"Line":0}},{"line":96,"address":[],"length":0,"stats":{"Line":0}},{"line":100,"address":[],"length":0,"stats":{"Line":0}},{"line":101,"address":[],"length":0,"stats":{"Line":0}},{"line":102,"address":[],"length":0,"stats":{"Line":0}},{"line":104,"address":[],"length":0,"stats":{"Line":0}},{"line":119,"address":[159248],"length":1,"stats":{"Line":1}},{"line":120,"address":[],"length":0,"stats":{"Line":2}},{"line":121,"address":[159302],"length":1,"stats":{"Line":1}},{"line":123,"address":[],"length":0,"stats":{"Line":1}},{"line":131,"address":[],"length":0,"stats":{"Line":1}},{"line":132,"address":[159374,159446,159657],"length":1,"stats":{"Line":3}},{"line":133,"address":[],"length":0,"stats":{"Line":2}},{"line":135,"address":[],"length":0,"stats":{"Line":2}},{"line":141,"address":[159680],"length":1,"stats":{"Line":2}},{"line":142,"address":[],"length":0,"stats":{"Line":2}},{"line":156,"address":[],"length":0,"stats":{"Line":3}},{"line":159,"address":[],"length":0,"stats":{"Line":6}},{"line":188,"address":[],"length":0,"stats":{"Line":2}},{"line":189,"address":[],"length":0,"stats":{"Line":2}},{"line":230,"address":[],"length":0,"stats":{"Line":2}},{"line":231,"address":[],"length":0,"stats":{"Line":2}},{"line":252,"address":[160351,160080],"length":1,"stats":{"Line":1}},{"line":253,"address":[],"length":0,"stats":{"Line":4}},{"line":254,"address":[],"length":0,"stats":{"Line":2}},{"line":256,"address":[],"length":0,"stats":{"Line":1}},{"line":280,"address":[],"length":0,"stats":{"Line":0}},{"line":281,"address":[],"length":0,"stats":{"Line":0}},{"line":282,"address":[],"length":0,"stats":{"Line":0}},{"line":284,"address":[],"length":0,"stats":{"Line":0}},{"line":294,"address":[],"length":0,"stats":{"Line":2}},{"line":296,"address":[],"length":0,"stats":{"Line":4}},{"line":300,"address":[],"length":0,"stats":{"Line":4}},{"line":301,"address":[],"length":0,"stats":{"Line":4}},{"line":304,"address":[],"length":0,"stats":{"Line":2}},{"line":316,"address":[],"length":0,"stats":{"Line":0}},{"line":319,"address":[],"length":0,"stats":{"Line":0}},{"line":322,"address":[],"length":0,"stats":{"Line":0}},{"line":326,"address":[],"length":0,"stats":{"Line":0}},{"line":337,"address":[],"length":0,"stats":{"Line":0}},{"line":338,"address":[],"length":0,"stats":{"Line":0}},{"line":342,"address":[],"length":0,"stats":{"Line":0}},{"line":345,"address":[],"length":0,"stats":{"Line":0}},{"line":349,"address":[],"length":0,"stats":{"Line":0}},{"line":384,"address":[],"length":0,"stats":{"Line":3}},{"line":386,"address":[],"length":0,"stats":{"Line":2}},{"line":387,"address":[],"length":0,"stats":{"Line":2}},{"line":435,"address":[],"length":0,"stats":{"Line":0}},{"line":437,"address":[],"length":0,"stats":{"Line":0}},{"line":438,"address":[],"length":0,"stats":{"Line":0}},{"line":440,"address":[],"length":0,"stats":{"Line":0}},{"line":460,"address":[],"length":0,"stats":{"Line":1}},{"line":461,"address":[],"length":0,"stats":{"Line":1}},{"line":462,"address":[],"length":0,"stats":{"Line":0}},{"line":467,"address":[],"length":0,"stats":{"Line":0}},{"line":469,"address":[],"length":0,"stats":{"Line":0}},{"line":473,"address":[],"length":0,"stats":{"Line":0}},{"line":474,"address":[],"length":0,"stats":{"Line":0}},{"line":477,"address":[],"length":0,"stats":{"Line":0}},{"line":487,"address":[],"length":0,"stats":{"Line":0}},{"line":490,"address":[],"length":0,"stats":{"Line":0}},{"line":493,"address":[],"length":0,"stats":{"Line":0}},{"line":497,"address":[],"length":0,"stats":{"Line":0}},{"line":508,"address":[],"length":0,"stats":{"Line":0}},{"line":509,"address":[],"length":0,"stats":{"Line":0}},{"line":513,"address":[],"length":0,"stats":{"Line":0}},{"line":516,"address":[],"length":0,"stats":{"Line":0}},{"line":520,"address":[],"length":0,"stats":{"Line":0}},{"line":557,"address":[],"length":0,"stats":{"Line":0}},{"line":559,"address":[],"length":0,"stats":{"Line":0}},{"line":560,"address":[],"length":0,"stats":{"Line":0}},{"line":601,"address":[],"length":0,"stats":{"Line":0}},{"line":603,"address":[],"length":0,"stats":{"Line":0}},{"line":604,"address":[],"length":0,"stats":{"Line":0}},{"line":606,"address":[],"length":0,"stats":{"Line":0}},{"line":624,"address":[],"length":0,"stats":{"Line":0}},{"line":625,"address":[],"length":0,"stats":{"Line":0}},{"line":626,"address":[],"length":0,"stats":{"Line":0}},{"line":646,"address":[],"length":0,"stats":{"Line":1}},{"line":647,"address":[],"length":0,"stats":{"Line":1}},{"line":673,"address":[],"length":0,"stats":{"Line":1}},{"line":674,"address":[],"length":0,"stats":{"Line":1}}],"covered":41,"coverable":108},{"path":["/","home","botahamec","Projects","happylock","src","poisonable.rs"],"content":"use std::marker::PhantomData;\nuse std::sync::atomic::AtomicBool;\n\nuse crate::ThreadKey;\n\nmod error;\nmod flag;\nmod guard;\nmod poisonable;\n\n/// A flag indicating if a lock is poisoned or not. The implementation differs\n/// depending on whether panics are set to unwind or abort.\n#[derive(Debug, Default)]\npub(crate) struct PoisonFlag(#[cfg(panic = \"unwind\")] AtomicBool);\n\n/// A wrapper around [`Lockable`] types which will enable poisoning.\n///\n/// A lock is \"poisoned\" when the thread panics while holding the lock. Once a\n/// lock is poisoned, all other threads are unable to access the data by\n/// default, because the data may be tainted (some invariant of the data might\n/// not be upheld).\n///\n/// The [`lock`] and [`try_lock`] methods return a [`Result`] which indicates\n/// whether the lock has been poisoned or not. The [`PoisonError`] type has an\n/// [`into_inner`] method which will return the guard that normally would have\n/// been returned for a successful lock. This allows access to the data,\n/// despite the lock being poisoned.\n///\n/// Alternatively, there is also a [`clear_poison`] method, which should\n/// indicate that all invariants of the underlying data are upheld, so that\n/// subsequent calls may still return [`Ok`].\n///\n/// [`Lockable`]: `crate::lockable::Lockable`\n/// [`lock`]: `Poisonable::lock`\n/// [`try_lock`]: `Poisonable::try_lock`\n/// [`into_inner`]: `PoisonError::into_inner`\n/// [`clear_poison`]: `Poisonable::clear_poison`\n#[derive(Debug, Default)]\npub struct Poisonable\u003cL\u003e {\n\tinner: L,\n\tpoisoned: PoisonFlag,\n}\n\n/// An RAII guard for a [`Poisonable`].\n///\n/// This is similar to a [`PoisonGuard`], except that it does not hold a\n/// [`Keyable`]\n///\n/// [`Keyable`]: `crate::Keyable`\npub struct PoisonRef\u003c'a, G\u003e {\n\tguard: G,\n\t#[cfg(panic = \"unwind\")]\n\tflag: \u0026'a PoisonFlag,\n\t_phantom: PhantomData\u003c\u0026'a ()\u003e,\n}\n\n/// An RAII guard for a [`Poisonable`].\n///\n/// This is created by calling methods like [`Poisonable::lock`].\npub struct PoisonGuard\u003c'a, G\u003e {\n\tguard: PoisonRef\u003c'a, G\u003e,\n\tkey: ThreadKey,\n}\n\n/// A type of error which can be returned when acquiring a [`Poisonable`] lock.\npub struct PoisonError\u003cGuard\u003e {\n\tguard: Guard,\n}\n\n/// An enumeration of possible errors associated with\n/// [`TryLockPoisonableResult`] which can occur while trying to acquire a lock\n/// (i.e.: [`Poisonable::try_lock`]).\npub enum TryLockPoisonableError\u003c'flag, G\u003e {\n\tPoisoned(PoisonError\u003cPoisonGuard\u003c'flag, G\u003e\u003e),\n\tWouldBlock(ThreadKey),\n}\n\n/// A type alias for the result of a lock method which can poisoned.\n///\n/// The [`Ok`] variant of this result indicates that the primitive was not\n/// poisoned, and the primitive was poisoned. Note that the [`Err`] variant\n/// *also* carries the associated guard, and it can be acquired through the\n/// [`into_inner`] method.\n///\n/// [`into_inner`]: `PoisonError::into_inner`\npub type PoisonResult\u003cGuard\u003e = Result\u003cGuard, PoisonError\u003cGuard\u003e\u003e;\n\n/// A type alias for the result of a nonblocking locking method.\n///\n/// For more information, see [`PoisonResult`]. A `TryLockPoisonableResult`\n/// doesn't necessarily hold the associated guard in the [`Err`] type as the\n/// lock might not have been acquired for other reasons.\npub type TryLockPoisonableResult\u003c'flag, G\u003e =\n\tResult\u003cPoisonGuard\u003c'flag, G\u003e, TryLockPoisonableError\u003c'flag, G\u003e\u003e;\n\n#[cfg(test)]\nmod tests {\n\tuse std::sync::Arc;\n\n\tuse super::*;\n\tuse crate::lockable::Lockable;\n\tuse crate::{LockCollection, Mutex, ThreadKey};\n\n\t#[test]\n\tfn locking_poisoned_mutex_returns_error_in_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = LockCollection::new(Poisonable::new(Mutex::new(42)));\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet mut guard1 = mutex.lock(key);\n\t\t\t\tlet guard = guard1.as_deref_mut().unwrap();\n\t\t\t\tassert_eq!(**guard, 42);\n\t\t\t\tpanic!();\n\n\t\t\t\t#[allow(unreachable_code)]\n\t\t\t\tdrop(guard1);\n\t\t\t})\n\t\t\t.join()\n\t\t\t.unwrap_err();\n\t\t});\n\n\t\tlet error = mutex.lock(key);\n\t\tlet error = error.as_deref().unwrap_err();\n\t\tassert_eq!(***error.get_ref(), 42);\n\t}\n\n\t#[test]\n\tfn non_poisoned_get_mut_is_ok() {\n\t\tlet mut mutex = Poisonable::new(Mutex::new(42));\n\t\tlet guard = mutex.get_mut();\n\t\tassert!(guard.is_ok());\n\t\tassert_eq!(*guard.unwrap(), 42);\n\t}\n\n\t#[test]\n\tfn non_poisoned_get_mut_is_err() {\n\t\tlet mut mutex = Poisonable::new(Mutex::new(42));\n\n\t\tlet _ = std::panic::catch_unwind(|| {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t#[allow(unused_variables)]\n\t\t\tlet guard = mutex.lock(key);\n\t\t\tpanic!();\n\t\t\t#[allow(unreachable_code)]\n\t\t\tdrop(guard);\n\t\t});\n\n\t\tlet guard = mutex.get_mut();\n\t\tassert!(guard.is_err());\n\t\tassert_eq!(**guard.unwrap_err().get_ref(), 42);\n\t}\n\n\t#[test]\n\tfn unpoisoned_into_inner() {\n\t\tlet mutex = Poisonable::new(Mutex::new(\"foo\"));\n\t\tassert_eq!(mutex.into_inner().unwrap(), \"foo\");\n\t}\n\n\t#[test]\n\tfn poisoned_into_inner() {\n\t\tlet mutex = Poisonable::from(Mutex::new(\"foo\"));\n\n\t\tstd::panic::catch_unwind(|| {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t#[allow(unused_variables)]\n\t\t\tlet guard = mutex.lock(key);\n\t\t\tpanic!();\n\t\t\t#[allow(unreachable_code)]\n\t\t\tdrop(guard);\n\t\t})\n\t\t.unwrap_err();\n\n\t\tlet error = mutex.into_inner().unwrap_err();\n\t\tassert_eq!(error.into_inner(), \"foo\");\n\t}\n\n\t#[test]\n\tfn unpoisoned_into_child() {\n\t\tlet mutex = Poisonable::new(Mutex::new(\"foo\"));\n\t\tassert_eq!(mutex.into_child().unwrap().into_inner(), \"foo\");\n\t}\n\n\t#[test]\n\tfn poisoned_into_child() {\n\t\tlet mutex = Poisonable::from(Mutex::new(\"foo\"));\n\n\t\tstd::panic::catch_unwind(|| {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t#[allow(unused_variables)]\n\t\t\tlet guard = mutex.lock(key);\n\t\t\tpanic!();\n\t\t\t#[allow(unreachable_code)]\n\t\t\tdrop(guard);\n\t\t})\n\t\t.unwrap_err();\n\n\t\tlet error = mutex.into_child().unwrap_err();\n\t\tassert_eq!(error.into_inner().into_inner(), \"foo\");\n\t}\n\n\t#[test]\n\tfn display_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = Poisonable::new(Mutex::new(\"Hello, world!\"));\n\n\t\tlet guard = mutex.lock(key).unwrap();\n\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\");\n\t}\n\n\t#[test]\n\tfn ref_as_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = LockCollection::new(Poisonable::new(Mutex::new(\"foo\")));\n\t\tlet guard = collection.lock(key);\n\t\tlet Ok(ref guard) = guard.as_ref() else {\n\t\t\tpanic!()\n\t\t};\n\t\tassert_eq!(**guard.as_ref(), \"foo\");\n\t}\n\n\t#[test]\n\tfn ref_as_mut() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = LockCollection::new(Poisonable::new(Mutex::new(\"foo\")));\n\t\tlet mut guard1 = collection.lock(key);\n\t\tlet Ok(ref mut guard) = guard1.as_mut() else {\n\t\t\tpanic!()\n\t\t};\n\t\tlet guard = guard.as_mut();\n\t\t**guard = \"bar\";\n\n\t\tlet key = LockCollection::\u003cPoisonable\u003cMutex\u003c_\u003e\u003e\u003e::unlock(guard1);\n\t\tlet guard = collection.lock(key);\n\t\tlet guard = guard.as_deref().unwrap();\n\t\tassert_eq!(*guard.as_ref(), \"bar\");\n\t}\n\n\t#[test]\n\tfn guard_as_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = Poisonable::new(Mutex::new(\"foo\"));\n\t\tlet guard = collection.lock(key);\n\t\tlet Ok(ref guard) = guard.as_ref() else {\n\t\t\tpanic!()\n\t\t};\n\t\tassert_eq!(**guard.as_ref(), \"foo\");\n\t}\n\n\t#[test]\n\tfn guard_as_mut() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = Poisonable::new(Mutex::new(\"foo\"));\n\t\tlet mut guard1 = mutex.lock(key);\n\t\tlet Ok(ref mut guard) = guard1.as_mut() else {\n\t\t\tpanic!()\n\t\t};\n\t\tlet guard = guard.as_mut();\n\t\t**guard = \"bar\";\n\n\t\tlet key = Poisonable::\u003cMutex\u003c_\u003e\u003e::unlock(guard1.unwrap());\n\t\tlet guard = mutex.lock(key);\n\t\tlet guard = guard.as_deref().unwrap();\n\t\tassert_eq!(*guard, \"bar\");\n\t}\n\n\t#[test]\n\tfn deref_mut_in_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = LockCollection::new(Poisonable::new(Mutex::new(42)));\n\t\tlet mut guard1 = collection.lock(key);\n\t\tlet Ok(ref mut guard) = guard1.as_mut() else {\n\t\t\tpanic!()\n\t\t};\n\t\t// TODO make this more convenient\n\t\tassert_eq!(***guard, 42);\n\t\t***guard = 24;\n\n\t\tlet key = LockCollection::\u003cPoisonable\u003cMutex\u003c_\u003e\u003e\u003e::unlock(guard1);\n\t\t_ = collection.lock(key);\n\t}\n\n\t#[test]\n\tfn get_ptrs() {\n\t\tlet mutex = Mutex::new(5);\n\t\tlet poisonable = Poisonable::new(mutex);\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tpoisonable.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tassert!(std::ptr::addr_eq(lock_ptrs[0], \u0026poisonable.inner));\n\t}\n\n\t#[test]\n\tfn clear_poison_for_poisoned_mutex() {\n\t\tlet mutex = Arc::new(Poisonable::new(Mutex::new(0)));\n\t\tlet c_mutex = Arc::clone(\u0026mutex);\n\n\t\tlet _ = std::thread::spawn(move || {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\tlet _lock = c_mutex.lock(key).unwrap();\n\t\t\tpanic!(); // the mutex gets poisoned\n\t\t})\n\t\t.join();\n\n\t\tassert!(mutex.is_poisoned());\n\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet _ = mutex.lock(key).unwrap_or_else(|mut e| {\n\t\t\t**e.get_mut() = 1;\n\t\t\tmutex.clear_poison();\n\t\t\te.into_inner()\n\t\t});\n\n\t\tassert!(!mutex.is_poisoned());\n\t}\n\n\t#[test]\n\tfn error_as_ref() {\n\t\tlet mutex = Poisonable::new(Mutex::new(\"foo\"));\n\n\t\tlet _ = std::panic::catch_unwind(|| {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t#[allow(unused_variables)]\n\t\t\tlet guard = mutex.lock(key);\n\t\t\tpanic!();\n\n\t\t\t#[allow(unknown_lints)]\n\t\t\t#[allow(unreachable_code)]\n\t\t\tdrop(guard);\n\t\t});\n\n\t\tassert!(mutex.is_poisoned());\n\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet error = mutex.lock(key).unwrap_err();\n\t\tassert_eq!(\u0026***error.as_ref(), \"foo\");\n\t}\n\n\t#[test]\n\tfn error_as_mut() {\n\t\tlet mutex = Poisonable::new(Mutex::new(\"foo\"));\n\n\t\tlet _ = std::panic::catch_unwind(|| {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t#[allow(unused_variables)]\n\t\t\tlet guard = mutex.lock(key);\n\t\t\tpanic!();\n\n\t\t\t#[allow(unknown_lints)]\n\t\t\t#[allow(unreachable_code)]\n\t\t\tdrop(guard);\n\t\t});\n\n\t\tassert!(mutex.is_poisoned());\n\n\t\tlet key: ThreadKey = ThreadKey::get().unwrap();\n\t\tlet mut error = mutex.lock(key).unwrap_err();\n\t\tlet error1 = error.as_mut();\n\t\t**error1 = \"bar\";\n\t\tlet key = Poisonable::\u003cMutex\u003c_\u003e\u003e::unlock(error.into_inner());\n\n\t\tmutex.clear_poison();\n\t\tlet guard = mutex.lock(key).unwrap();\n\t\tassert_eq!(\u0026**guard, \"bar\");\n\t}\n\n\t#[test]\n\tfn try_error_from_lock_error() {\n\t\tlet mutex = Poisonable::new(Mutex::new(\"foo\"));\n\n\t\tlet _ = std::panic::catch_unwind(|| {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t#[allow(unused_variables)]\n\t\t\tlet guard = mutex.lock(key);\n\t\t\tpanic!();\n\n\t\t\t#[allow(unknown_lints)]\n\t\t\t#[allow(unreachable_code)]\n\t\t\tdrop(guard);\n\t\t});\n\n\t\tassert!(mutex.is_poisoned());\n\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet error = mutex.lock(key).unwrap_err();\n\t\tlet error = TryLockPoisonableError::from(error);\n\n\t\tlet TryLockPoisonableError::Poisoned(error) = error else {\n\t\t\tpanic!()\n\t\t};\n\t\tassert_eq!(\u0026**error.into_inner(), \"foo\");\n\t}\n\n\t#[test]\n\tfn new_poisonable_is_not_poisoned() {\n\t\tlet mutex = Poisonable::new(Mutex::new(42));\n\t\tassert!(!mutex.is_poisoned());\n\t}\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","src","rwlock","read_guard.rs"],"content":"use std::fmt::{Debug, Display};\nuse std::hash::Hash;\nuse std::marker::PhantomData;\nuse std::ops::Deref;\n\nuse lock_api::RawRwLock;\n\nuse crate::lockable::RawLock;\nuse crate::ThreadKey;\n\nuse super::{RwLock, RwLockReadGuard, RwLockReadRef};\n\n// These impls make things slightly easier because now you can use\n// `println!(\"{guard}\")` instead of `println!(\"{}\", *guard)`\n\n#[mutants::skip] // hashing involves PRNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Hash + ?Sized, R: RawRwLock\u003e Hash for RwLockReadRef\u003c'_, T, R\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.deref().hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug + ?Sized, R: RawRwLock\u003e Debug for RwLockReadRef\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: Display + ?Sized, R: RawRwLock\u003e Display for RwLockReadRef\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e Deref for RwLockReadRef\u003c'_, T, R\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t// safety: this is the only type that can use `value`, and there's\n\t\t// a reference to this type, so there cannot be any mutable\n\t\t// references to this value.\n\t\tunsafe { \u0026*self.0.data.get() }\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e AsRef\u003cT\u003e for RwLockReadRef\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e Drop for RwLockReadRef\u003c'_, T, R\u003e {\n\tfn drop(\u0026mut self) {\n\t\t// safety: this guard is being destroyed, so the data cannot be\n\t\t// accessed without locking again\n\t\tunsafe { self.0.raw_unlock_read() }\n\t}\n}\n\nimpl\u003c'a, T: ?Sized, R: RawRwLock\u003e RwLockReadRef\u003c'a, T, R\u003e {\n\t/// Creates an immutable reference for the underlying data of an [`RwLock`]\n\t/// without locking it or taking ownership of the key.\n\t#[must_use]\n\tpub(crate) unsafe fn new(mutex: \u0026'a RwLock\u003cT, R\u003e) -\u003e Self {\n\t\tSelf(mutex, PhantomData)\n\t}\n}\n\n#[mutants::skip] // hashing involves PRNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Hash + ?Sized, R: RawRwLock\u003e Hash for RwLockReadGuard\u003c'_, T, R\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.deref().hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug + ?Sized, R: RawRwLock\u003e Debug for RwLockReadGuard\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: Display + ?Sized, R: RawRwLock\u003e Display for RwLockReadGuard\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e Deref for RwLockReadGuard\u003c'_, T, R\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t\u0026self.rwlock\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e AsRef\u003cT\u003e for RwLockReadGuard\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself\n\t}\n}\n\nimpl\u003c'a, T: ?Sized, R: RawRwLock\u003e RwLockReadGuard\u003c'a, T, R\u003e {\n\t/// Create a guard to the given mutex. Undefined if multiple guards to the\n\t/// same mutex exist at once.\n\t#[must_use]\n\tpub(super) unsafe fn new(rwlock: \u0026'a RwLock\u003cT, R\u003e, thread_key: ThreadKey) -\u003e Self {\n\t\tSelf {\n\t\t\trwlock: RwLockReadRef(rwlock, PhantomData),\n\t\t\tthread_key,\n\t\t}\n\t}\n}\n\nunsafe impl\u003cT: ?Sized + Sync, R: RawRwLock + Sync\u003e Sync for RwLockReadRef\u003c'_, T, R\u003e {}\n","traces":[{"line":33,"address":[715888],"length":1,"stats":{"Line":1}},{"line":34,"address":[],"length":0,"stats":{"Line":1}},{"line":41,"address":[],"length":0,"stats":{"Line":3}},{"line":45,"address":[],"length":0,"stats":{"Line":3}},{"line":50,"address":[],"length":0,"stats":{"Line":0}},{"line":51,"address":[],"length":0,"stats":{"Line":0}},{"line":56,"address":[168880,168912,168896],"length":1,"stats":{"Line":3}},{"line":59,"address":[],"length":0,"stats":{"Line":3}},{"line":67,"address":[223776,223744,223760],"length":1,"stats":{"Line":3}},{"line":68,"address":[],"length":0,"stats":{"Line":0}},{"line":89,"address":[],"length":0,"stats":{"Line":1}},{"line":90,"address":[],"length":0,"stats":{"Line":1}},{"line":97,"address":[],"length":0,"stats":{"Line":1}},{"line":98,"address":[],"length":0,"stats":{"Line":1}},{"line":103,"address":[],"length":0,"stats":{"Line":0}},{"line":104,"address":[],"length":0,"stats":{"Line":0}},{"line":112,"address":[],"length":0,"stats":{"Line":3}},{"line":114,"address":[],"length":0,"stats":{"Line":0}}],"covered":12,"coverable":18},{"path":["/","home","botahamec","Projects","happylock","src","rwlock","read_lock.rs"],"content":"use std::fmt::Debug;\n\nuse lock_api::RawRwLock;\n\nuse crate::lockable::{Lockable, RawLock, Sharable};\nuse crate::ThreadKey;\n\nuse super::{ReadLock, RwLock, RwLockReadGuard, RwLockReadRef};\n\nunsafe impl\u003cT: Send, R: RawRwLock + Send + Sync\u003e Lockable for ReadLock\u003c'_, T, R\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= RwLockReadRef\u003c'g, T, R\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= \u0026'a T\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tptrs.push(self.as_ref());\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tRwLockReadRef::new(self.as_ref())\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.0.data_ref()\n\t}\n}\n\nunsafe impl\u003cT: Send, R: RawRwLock + Send + Sync\u003e Sharable for ReadLock\u003c'_, T, R\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= RwLockReadRef\u003c'g, T, R\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= \u0026'a T\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tRwLockReadRef::new(self.as_ref())\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tself.0.data_ref()\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: ?Sized + Debug, R: RawRwLock\u003e Debug for ReadLock\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t// safety: this is just a try lock, and the value is dropped\n\t\t// immediately after, so there's no risk of blocking ourselves\n\t\t// or any other threads\n\t\tif let Some(value) = unsafe { self.try_lock_no_key() } {\n\t\t\tf.debug_struct(\"ReadLock\").field(\"data\", \u0026\u0026*value).finish()\n\t\t} else {\n\t\t\tstruct LockedPlaceholder;\n\t\t\timpl Debug for LockedPlaceholder {\n\t\t\t\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t\t\t\tf.write_str(\"\u003clocked\u003e\")\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tf.debug_struct(\"ReadLock\")\n\t\t\t\t.field(\"data\", \u0026LockedPlaceholder)\n\t\t\t\t.finish()\n\t\t}\n\t}\n}\n\nimpl\u003c'l, T, R\u003e From\u003c\u0026'l RwLock\u003cT, R\u003e\u003e for ReadLock\u003c'l, T, R\u003e {\n\tfn from(value: \u0026'l RwLock\u003cT, R\u003e) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\nimpl\u003cT: ?Sized, R\u003e AsRef\u003cRwLock\u003cT, R\u003e\u003e for ReadLock\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026RwLock\u003cT, R\u003e {\n\t\tself.0\n\t}\n}\n\nimpl\u003c'l, T, R\u003e ReadLock\u003c'l, T, R\u003e {\n\t/// Creates a new `ReadLock` which accesses the given [`RwLock`]\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{rwlock::ReadLock, RwLock};\n\t///\n\t/// let lock = RwLock::new(5);\n\t/// let read_lock = ReadLock::new(\u0026lock);\n\t/// ```\n\t#[must_use]\n\tpub const fn new(rwlock: \u0026'l RwLock\u003cT, R\u003e) -\u003e Self {\n\t\tSelf(rwlock)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e ReadLock\u003c'_, T, R\u003e {\n\t/// Locks the underlying [`RwLock`] with shared read access, blocking the\n\t/// current thread until it can be acquired.\n\t///\n\t/// The calling thread will be blocked until there are no more writers\n\t/// which hold the lock. There may be other readers currently inside the\n\t/// lock when this method returns.\n\t///\n\t/// Returns an RAII guard which will release this thread's shared access\n\t/// once it is dropped.\n\t///\n\t/// Because this method takes a [`ThreadKey`], it's not possible for this\n\t/// method to cause a deadlock.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::rwlock::ReadLock;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock: RwLock\u003c_\u003e = RwLock::new(1);\n\t/// let reader = ReadLock::new(\u0026lock);\n\t///\n\t/// let n = reader.lock(key);\n\t/// assert_eq!(*n, 1);\n\t/// ```\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\t#[must_use]\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e RwLockReadGuard\u003c'_, T, R\u003e {\n\t\tself.0.read(key)\n\t}\n\n\t/// Attempts to acquire the underlying [`RwLock`] with shared read access\n\t/// without blocking.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// shared access when it is dropped.\n\t///\n\t/// This function does not provide any guarantees with respect to the\n\t/// ordering of whether contentious readers or writers will acquire the\n\t/// lock first.\n\t///\n\t/// # Errors\n\t///\n\t/// If the `RwLock` could not be acquired because it was already locked\n\t/// exclusively, then an error will be returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::rwlock::ReadLock;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(1);\n\t/// let reader = ReadLock::new(\u0026lock);\n\t///\n\t/// match reader.try_lock(key) {\n\t/// Ok(n) =\u003e assert_eq!(*n, 1),\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t/// ```\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e Result\u003cRwLockReadGuard\u003c'_, T, R\u003e, ThreadKey\u003e {\n\t\tself.0.try_read(key)\n\t}\n\n\t/// Attempts to create an exclusive lock without a key. Locking this\n\t/// without exclusive access to the key is undefined behavior.\n\tpub(crate) unsafe fn try_lock_no_key(\u0026self) -\u003e Option\u003cRwLockReadRef\u003c'_, T, R\u003e\u003e {\n\t\tself.0.try_read_no_key()\n\t}\n\n\t/// Immediately drops the guard, and consequently releases the shared lock\n\t/// on the underlying [`RwLock`].\n\t///\n\t/// This function is equivalent to calling [`drop`] on the guard, except\n\t/// that it returns the key that was used to create it. Alternately, the\n\t/// guard will be automatically dropped when it goes out of scope.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::rwlock::ReadLock;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(0);\n\t/// let reader = ReadLock::new(\u0026lock);\n\t///\n\t/// let mut guard = reader.lock(key);\n\t/// assert_eq!(*guard, 0);\n\t/// let key = ReadLock::unlock(guard);\n\t/// ```\n\t#[must_use]\n\tpub fn unlock(guard: RwLockReadGuard\u003c'_, T, R\u003e) -\u003e ThreadKey {\n\t\tRwLock::unlock_read(guard)\n\t}\n}\n","traces":[{"line":21,"address":[],"length":0,"stats":{"Line":1}},{"line":22,"address":[],"length":0,"stats":{"Line":1}},{"line":25,"address":[],"length":0,"stats":{"Line":0}},{"line":26,"address":[],"length":0,"stats":{"Line":0}},{"line":29,"address":[],"length":0,"stats":{"Line":0}},{"line":30,"address":[],"length":0,"stats":{"Line":0}},{"line":45,"address":[],"length":0,"stats":{"Line":0}},{"line":46,"address":[],"length":0,"stats":{"Line":0}},{"line":49,"address":[],"length":0,"stats":{"Line":0}},{"line":50,"address":[],"length":0,"stats":{"Line":0}},{"line":79,"address":[],"length":0,"stats":{"Line":1}},{"line":80,"address":[],"length":0,"stats":{"Line":1}},{"line":85,"address":[],"length":0,"stats":{"Line":1}},{"line":86,"address":[],"length":0,"stats":{"Line":1}},{"line":102,"address":[],"length":0,"stats":{"Line":3}},{"line":103,"address":[],"length":0,"stats":{"Line":0}},{"line":139,"address":[],"length":0,"stats":{"Line":1}},{"line":140,"address":[],"length":0,"stats":{"Line":1}},{"line":174,"address":[],"length":0,"stats":{"Line":1}},{"line":175,"address":[],"length":0,"stats":{"Line":1}},{"line":180,"address":[],"length":0,"stats":{"Line":0}},{"line":181,"address":[],"length":0,"stats":{"Line":0}},{"line":206,"address":[],"length":0,"stats":{"Line":0}},{"line":207,"address":[],"length":0,"stats":{"Line":0}}],"covered":11,"coverable":24},{"path":["/","home","botahamec","Projects","happylock","src","rwlock","rwlock.rs"],"content":"use std::cell::UnsafeCell;\nuse std::fmt::Debug;\nuse std::marker::PhantomData;\nuse std::panic::AssertUnwindSafe;\n\nuse lock_api::RawRwLock;\n\nuse crate::handle_unwind::handle_unwind;\nuse crate::lockable::{\n\tLockable, LockableGetMut, LockableIntoInner, OwnedLockable, RawLock, Sharable,\n};\nuse crate::{Keyable, ThreadKey};\n\nuse super::{PoisonFlag, RwLock, RwLockReadGuard, RwLockReadRef, RwLockWriteGuard, RwLockWriteRef};\n\nunsafe impl\u003cT: ?Sized, R: RawRwLock\u003e RawLock for RwLock\u003cT, R\u003e {\n\tfn poison(\u0026self) {\n\t\tself.poison.poison();\n\t}\n\n\tunsafe fn raw_lock(\u0026self) {\n\t\tassert!(\n\t\t\t!self.poison.is_poisoned(),\n\t\t\t\"The read-write lock has been killed\"\n\t\t);\n\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.lock_exclusive(), || self.poison())\n\t}\n\n\tunsafe fn raw_try_lock(\u0026self) -\u003e bool {\n\t\tif self.poison.is_poisoned() {\n\t\t\treturn false;\n\t\t}\n\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.try_lock_exclusive(), || self.poison())\n\t}\n\n\tunsafe fn raw_unlock(\u0026self) {\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.unlock_exclusive(), || self.poison())\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tassert!(\n\t\t\t!self.poison.is_poisoned(),\n\t\t\t\"The read-write lock has been killed\"\n\t\t);\n\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.lock_shared(), || self.poison())\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tif self.poison.is_poisoned() {\n\t\t\treturn false;\n\t\t}\n\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.try_lock_shared(), || self.poison())\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.unlock_shared(), || self.poison())\n\t}\n}\n\nunsafe impl\u003cT: Send, R: RawRwLock + Send + Sync\u003e Lockable for RwLock\u003cT, R\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= RwLockWriteRef\u003c'g, T, R\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= \u0026'a mut T\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tptrs.push(self);\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tRwLockWriteRef::new(self)\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.data.get().as_mut().unwrap_unchecked()\n\t}\n}\n\nunsafe impl\u003cT: Send, R: RawRwLock + Send + Sync\u003e Sharable for RwLock\u003cT, R\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= RwLockReadRef\u003c'g, T, R\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= \u0026'a T\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tRwLockReadRef::new(self)\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tself.data.get().as_ref().unwrap_unchecked()\n\t}\n}\n\nunsafe impl\u003cT: Send, R: RawRwLock + Send + Sync\u003e OwnedLockable for RwLock\u003cT, R\u003e {}\n\nimpl\u003cT: Send, R: RawRwLock + Send + Sync\u003e LockableIntoInner for RwLock\u003cT, R\u003e {\n\ttype Inner = T;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tself.into_inner()\n\t}\n}\n\nimpl\u003cT: Send, R: RawRwLock + Send + Sync\u003e LockableGetMut for RwLock\u003cT, R\u003e {\n\ttype Inner\u003c'a\u003e\n\t\t= \u0026'a mut T\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tAsMut::as_mut(self)\n\t}\n}\n\nimpl\u003cT, R: RawRwLock\u003e RwLock\u003cT, R\u003e {\n\t/// Creates a new instance of an `RwLock\u003cT\u003e` which is unlocked.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::RwLock;\n\t///\n\t/// let lock = RwLock::new(5);\n\t/// ```\n\t#[must_use]\n\tpub const fn new(data: T) -\u003e Self {\n\t\tSelf {\n\t\t\tdata: UnsafeCell::new(data),\n\t\t\tpoison: PoisonFlag::new(),\n\t\t\traw: R::INIT,\n\t\t}\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: ?Sized + Debug, R: RawRwLock\u003e Debug for RwLock\u003cT, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t// safety: this is just a try lock, and the value is dropped\n\t\t// immediately after, so there's no risk of blocking ourselves\n\t\t// or any other threads\n\t\tif let Some(value) = unsafe { self.try_read_no_key() } {\n\t\t\tf.debug_struct(\"RwLock\").field(\"data\", \u0026\u0026*value).finish()\n\t\t} else {\n\t\t\tstruct LockedPlaceholder;\n\t\t\timpl Debug for LockedPlaceholder {\n\t\t\t\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t\t\t\tf.write_str(\"\u003clocked\u003e\")\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tf.debug_struct(\"RwLock\")\n\t\t\t\t.field(\"data\", \u0026LockedPlaceholder)\n\t\t\t\t.finish()\n\t\t}\n\t}\n}\n\nimpl\u003cT: Default, R: RawRwLock\u003e Default for RwLock\u003cT, R\u003e {\n\tfn default() -\u003e Self {\n\t\tSelf::new(T::default())\n\t}\n}\n\nimpl\u003cT, R: RawRwLock\u003e From\u003cT\u003e for RwLock\u003cT, R\u003e {\n\tfn from(value: T) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\n// We don't need a `get_mut` because we don't have mutex poisoning. Hurray!\n// This is safe because you can't have a mutable reference to the lock if it's\n// locked. Being locked requires an immutable reference because of the guard.\nimpl\u003cT: ?Sized, R\u003e AsMut\u003cT\u003e for RwLock\u003cT, R\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself.data.get_mut()\n\t}\n}\n\nimpl\u003cT, R\u003e RwLock\u003cT, R\u003e {\n\t/// Consumes this `RwLock`, returning the underlying data.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t///\n\t/// let lock = RwLock::new(String::new());\n\t/// {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut s = lock.write(key);\n\t/// *s = \"modified\".to_owned();\n\t/// }\n\t/// assert_eq!(lock.into_inner(), \"modified\");\n\t/// ```\n\t#[must_use]\n\tpub fn into_inner(self) -\u003e T {\n\t\tself.data.into_inner()\n\t}\n}\n\nimpl\u003cT: ?Sized, R\u003e RwLock\u003cT, R\u003e {\n\t/// Returns a mutable reference to the underlying data.\n\t///\n\t/// Since this call borrows `RwLock` mutably, no actual locking is taking\n\t/// place. The mutable borrow statically guarantees that no locks exist.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, RwLock};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut mutex = RwLock::new(0);\n\t/// *mutex.get_mut() = 10;\n\t/// assert_eq!(*mutex.read(key), 10);\n\t/// ```\n\t#[must_use]\n\tpub fn get_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself.data.get_mut()\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e RwLock\u003cT, R\u003e {\n\tpub fn scoped_read\u003cRet\u003e(\u0026self, key: impl Keyable, f: impl Fn(\u0026T) -\u003e Ret) -\u003e Ret {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_read();\n\n\t\t\t// safety: the rwlock was just locked\n\t\t\tlet r = f(self.data.get().as_ref().unwrap_unchecked());\n\n\t\t\t// safety: the rwlock is already locked\n\t\t\tself.raw_unlock_read();\n\n\t\t\tdrop(key); // ensure the key stays valid for long enough\n\n\t\t\tr\n\t\t}\n\t}\n\n\tpub fn scoped_try_read\u003cKey: Keyable, Ret\u003e(\n\t\t\u0026self,\n\t\tkey: Key,\n\t\tf: impl Fn(\u0026T) -\u003e Ret,\n\t) -\u003e Result\u003cRet, Key\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_read() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: the rwlock was just locked\n\t\t\tlet r = f(self.data.get().as_ref().unwrap_unchecked());\n\n\t\t\t// safety: the rwlock is already locked\n\t\t\tself.raw_unlock_read();\n\n\t\t\tdrop(key); // ensure the key stays valid for long enough\n\n\t\t\tOk(r)\n\t\t}\n\t}\n\n\tpub fn scoped_write\u003cRet\u003e(\u0026self, key: impl Keyable, f: impl Fn(\u0026mut T) -\u003e Ret) -\u003e Ret {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_lock();\n\n\t\t\t// safety: we just locked the rwlock\n\t\t\tlet r = f(self.data.get().as_mut().unwrap_unchecked());\n\n\t\t\t// safety: the rwlock is already locked\n\t\t\tself.raw_unlock();\n\n\t\t\tdrop(key); // ensure the key stays valid for long enough\n\n\t\t\tr\n\t\t}\n\t}\n\n\tpub fn scoped_try_write\u003cKey: Keyable, Ret\u003e(\n\t\t\u0026self,\n\t\tkey: Key,\n\t\tf: impl Fn(\u0026mut T) -\u003e Ret,\n\t) -\u003e Result\u003cRet, Key\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_lock() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: the rwlock was just locked\n\t\t\tlet r = f(self.data.get().as_mut().unwrap_unchecked());\n\n\t\t\t// safety: the rwlock is already locked\n\t\t\tself.raw_unlock();\n\n\t\t\tdrop(key); // ensure the key stays valid for long enough\n\n\t\t\tOk(r)\n\t\t}\n\t}\n\n\t/// Locks this `RwLock` with shared read access, blocking the current\n\t/// thread until it can be acquired.\n\t///\n\t/// The calling thread will be blocked until there are no more writers\n\t/// which hold the lock. There may be other readers currently inside the\n\t/// lock when this method returns.\n\t///\n\t/// Returns an RAII guard which will release this thread's shared access\n\t/// once it is dropped.\n\t///\n\t/// Because this method takes a [`ThreadKey`], it's not possible for this\n\t/// method to cause a deadlock.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t/// use happylock::{RwLock, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = Arc::new(RwLock::new(1));\n\t/// let c_lock = Arc::clone(\u0026lock);\n\t///\n\t/// let n = lock.read(key);\n\t/// assert_eq!(*n, 1);\n\t///\n\t/// thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let r = c_lock.read(key);\n\t/// }).join().unwrap();\n\t/// ```\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\tpub fn read(\u0026self, key: ThreadKey) -\u003e RwLockReadGuard\u003c'_, T, R\u003e {\n\t\tunsafe {\n\t\t\tself.raw_read();\n\n\t\t\t// safety: the lock is locked first\n\t\t\tRwLockReadGuard::new(self, key)\n\t\t}\n\t}\n\n\t/// Attempts to acquire this `RwLock` with shared read access without\n\t/// blocking.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// shared access when it is dropped.\n\t///\n\t/// This function does not provide any guarantees with respect to the\n\t/// ordering of whether contentious readers or writers will acquire the\n\t/// lock first.\n\t///\n\t/// # Errors\n\t///\n\t/// If the `RwLock` could not be acquired because it was already locked\n\t/// exclusively, then an error will be returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(1);\n\t///\n\t/// match lock.try_read(key) {\n\t/// Ok(n) =\u003e assert_eq!(*n, 1),\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t/// ```\n\tpub fn try_read(\u0026self, key: ThreadKey) -\u003e Result\u003cRwLockReadGuard\u003c'_, T, R\u003e, ThreadKey\u003e {\n\t\tunsafe {\n\t\t\tif self.raw_try_read() {\n\t\t\t\t// safety: the lock is locked first\n\t\t\t\tOk(RwLockReadGuard::new(self, key))\n\t\t\t} else {\n\t\t\t\tErr(key)\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Attempts to create a shared lock without a key. Locking this without\n\t/// exclusive access to the key is undefined behavior.\n\tpub(crate) unsafe fn try_read_no_key(\u0026self) -\u003e Option\u003cRwLockReadRef\u003c'_, T, R\u003e\u003e {\n\t\tif self.raw_try_read() {\n\t\t\t// safety: the lock is locked first\n\t\t\tSome(RwLockReadRef(self, PhantomData))\n\t\t} else {\n\t\t\tNone\n\t\t}\n\t}\n\n\t/// Attempts to create an exclusive lock without a key. Locking this\n\t/// without exclusive access to the key is undefined behavior.\n\t#[cfg(test)]\n\tpub(crate) unsafe fn try_write_no_key(\u0026self) -\u003e Option\u003cRwLockWriteRef\u003c'_, T, R\u003e\u003e {\n\t\tif self.raw_try_lock() {\n\t\t\t// safety: the lock is locked first\n\t\t\tSome(RwLockWriteRef(self, PhantomData))\n\t\t} else {\n\t\t\tNone\n\t\t}\n\t}\n\n\t/// Locks this `RwLock` with exclusive write access, blocking the current\n\t/// until it can be acquired.\n\t///\n\t/// This function will not return while other writers or readers currently\n\t/// have access to the lock.\n\t///\n\t/// Returns an RAII guard which will drop the write access of this `RwLock`\n\t/// when dropped.\n\t///\n\t/// Because this method takes a [`ThreadKey`], it's not possible for this\n\t/// method to cause a deadlock.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, RwLock};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(1);\n\t///\n\t/// match lock.try_write(key) {\n\t/// Ok(n) =\u003e assert_eq!(*n, 1),\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t/// ```\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\tpub fn write(\u0026self, key: ThreadKey) -\u003e RwLockWriteGuard\u003c'_, T, R\u003e {\n\t\tunsafe {\n\t\t\tself.raw_lock();\n\n\t\t\t// safety: the lock is locked first\n\t\t\tRwLockWriteGuard::new(self, key)\n\t\t}\n\t}\n\n\t/// Attempts to lock this `RwLock` with exclusive write access.\n\t///\n\t/// This function does not block. If the lock could not be acquired at this\n\t/// time, then `None` is returned. Otherwise, an RAII guard is returned\n\t/// which will release the lock when it is dropped.\n\t///\n\t/// This function does not provide any guarantees with respect to the\n\t/// ordering of whether contentious readers or writers will acquire the\n\t/// lock first.\n\t///\n\t/// # Errors\n\t///\n\t/// If the `RwLock` could not be acquired because it was already locked,\n\t/// then an error will be returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(1);\n\t///\n\t/// let n = lock.read(key);\n\t/// assert_eq!(*n, 1);\n\t/// ```\n\tpub fn try_write(\u0026self, key: ThreadKey) -\u003e Result\u003cRwLockWriteGuard\u003c'_, T, R\u003e, ThreadKey\u003e {\n\t\tunsafe {\n\t\t\tif self.raw_try_lock() {\n\t\t\t\t// safety: the lock is locked first\n\t\t\t\tOk(RwLockWriteGuard::new(self, key))\n\t\t\t} else {\n\t\t\t\tErr(key)\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Returns `true` if the rwlock is currently locked in any way\n\t#[cfg(test)]\n\tpub(crate) fn is_locked(\u0026self) -\u003e bool {\n\t\tself.raw.is_locked()\n\t}\n\n\t/// Immediately drops the guard, and consequently releases the shared lock.\n\t///\n\t/// This function is equivalent to calling [`drop`] on the guard, except\n\t/// that it returns the key that was used to create it. Alternately, the\n\t/// guard will be automatically dropped when it goes out of scope.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(0);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// assert_eq!(*guard, 0);\n\t/// let key = RwLock::unlock_read(guard);\n\t/// ```\n\t#[must_use]\n\tpub fn unlock_read(guard: RwLockReadGuard\u003c'_, T, R\u003e) -\u003e ThreadKey {\n\t\tunsafe {\n\t\t\tguard.rwlock.0.raw_unlock_read();\n\t\t}\n\t\tguard.thread_key\n\t}\n\n\t/// Immediately drops the guard, and consequently releases the exclusive\n\t/// lock.\n\t///\n\t/// This function is equivalent to calling [`drop`] on the guard, except\n\t/// that it returns the key that was used to create it. Alternately, the\n\t/// guard will be automatically dropped when it goes out of scope.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(0);\n\t///\n\t/// let mut guard = lock.write(key);\n\t/// *guard += 20;\n\t/// let key = RwLock::unlock_write(guard);\n\t/// ```\n\t#[must_use]\n\tpub fn unlock_write(guard: RwLockWriteGuard\u003c'_, T, R\u003e) -\u003e ThreadKey {\n\t\tunsafe {\n\t\t\tguard.rwlock.0.raw_unlock();\n\t\t}\n\t\tguard.thread_key\n\t}\n}\n\nunsafe impl\u003cR: RawRwLock + Send, T: ?Sized + Send\u003e Send for RwLock\u003cT, R\u003e {}\nunsafe impl\u003cR: RawRwLock + Sync, T: ?Sized + Send\u003e Sync for RwLock\u003cT, R\u003e {}\n","traces":[{"line":17,"address":[179264,179296],"length":1,"stats":{"Line":5}},{"line":18,"address":[161541],"length":1,"stats":{"Line":5}},{"line":21,"address":[157536,157648],"length":1,"stats":{"Line":7}},{"line":22,"address":[713102,713214],"length":1,"stats":{"Line":5}},{"line":23,"address":[],"length":0,"stats":{"Line":0}},{"line":24,"address":[],"length":0,"stats":{"Line":0}},{"line":28,"address":[179473,179361],"length":1,"stats":{"Line":8}},{"line":29,"address":[180384,180437,180448,180400,180405,180453,180432,180389],"length":1,"stats":{"Line":20}},{"line":32,"address":[225888,226048,225968],"length":1,"stats":{"Line":11}},{"line":33,"address":[713406,713326],"length":1,"stats":{"Line":11}},{"line":34,"address":[179016,178936],"length":1,"stats":{"Line":5}},{"line":38,"address":[157201,157121],"length":1,"stats":{"Line":8}},{"line":39,"address":[216485,216437,216432,216453,216448,216501,216496,216480],"length":1,"stats":{"Line":26}},{"line":42,"address":[157024,157056],"length":1,"stats":{"Line":9}},{"line":44,"address":[157068,157036],"length":1,"stats":{"Line":9}},{"line":45,"address":[216336,216341,216357,216352,216389,216421,216416,216384],"length":1,"stats":{"Line":31}},{"line":48,"address":[161680],"length":1,"stats":{"Line":5}},{"line":49,"address":[179605,179717],"length":1,"stats":{"Line":5}},{"line":50,"address":[],"length":0,"stats":{"Line":0}},{"line":51,"address":[],"length":0,"stats":{"Line":0}},{"line":55,"address":[],"length":0,"stats":{"Line":6}},{"line":56,"address":[226998,226898,227110],"length":1,"stats":{"Line":20}},{"line":59,"address":[157248,157328],"length":1,"stats":{"Line":7}},{"line":60,"address":[],"length":0,"stats":{"Line":7}},{"line":61,"address":[],"length":0,"stats":{"Line":2}},{"line":65,"address":[161457],"length":1,"stats":{"Line":7}},{"line":66,"address":[197696,197728,197733,197685,197616,197653,197701,197648,197680,197749,197744,197621],"length":1,"stats":{"Line":26}},{"line":69,"address":[713920,713952],"length":1,"stats":{"Line":5}},{"line":71,"address":[157420,157452],"length":1,"stats":{"Line":7}},{"line":72,"address":[179217,179249],"length":1,"stats":{"Line":25}},{"line":87,"address":[714048,713984],"length":1,"stats":{"Line":19}},{"line":88,"address":[714009,714073],"length":1,"stats":{"Line":19}},{"line":91,"address":[714112],"length":1,"stats":{"Line":3}},{"line":92,"address":[179781,179797],"length":1,"stats":{"Line":3}},{"line":95,"address":[],"length":0,"stats":{"Line":0}},{"line":96,"address":[],"length":0,"stats":{"Line":0}},{"line":111,"address":[714128,714144],"length":1,"stats":{"Line":3}},{"line":112,"address":[],"length":0,"stats":{"Line":3}},{"line":115,"address":[],"length":0,"stats":{"Line":0}},{"line":116,"address":[],"length":0,"stats":{"Line":0}},{"line":125,"address":[],"length":0,"stats":{"Line":0}},{"line":126,"address":[],"length":0,"stats":{"Line":0}},{"line":136,"address":[],"length":0,"stats":{"Line":0}},{"line":137,"address":[],"length":0,"stats":{"Line":0}},{"line":152,"address":[178729,178688,178601,178560],"length":1,"stats":{"Line":18}},{"line":154,"address":[],"length":0,"stats":{"Line":0}},{"line":155,"address":[178586,178714,178646,178774],"length":1,"stats":{"Line":36}},{"line":186,"address":[],"length":0,"stats":{"Line":0}},{"line":187,"address":[],"length":0,"stats":{"Line":0}},{"line":192,"address":[],"length":0,"stats":{"Line":1}},{"line":193,"address":[],"length":0,"stats":{"Line":1}},{"line":201,"address":[],"length":0,"stats":{"Line":0}},{"line":202,"address":[],"length":0,"stats":{"Line":0}},{"line":223,"address":[],"length":0,"stats":{"Line":0}},{"line":224,"address":[],"length":0,"stats":{"Line":0}},{"line":245,"address":[],"length":0,"stats":{"Line":0}},{"line":246,"address":[],"length":0,"stats":{"Line":0}},{"line":251,"address":[],"length":0,"stats":{"Line":0}},{"line":254,"address":[],"length":0,"stats":{"Line":0}},{"line":257,"address":[],"length":0,"stats":{"Line":0}},{"line":260,"address":[],"length":0,"stats":{"Line":0}},{"line":264,"address":[],"length":0,"stats":{"Line":0}},{"line":268,"address":[156205,156477,155408,155952,155933,156224,156496,155680,155661,155389,156749,155136],"length":1,"stats":{"Line":6}},{"line":275,"address":[156022,156294,156520,156566,155206,155478,155160,155704,155432,155750,155976,156248],"length":1,"stats":{"Line":12}},{"line":276,"address":[155489,155761,156033,155217,156305,156577],"length":1,"stats":{"Line":2}},{"line":280,"address":[],"length":0,"stats":{"Line":8}},{"line":283,"address":[156411,155595,156683,155323,155867,156139],"length":1,"stats":{"Line":4}},{"line":285,"address":[156172,156444,155628,156716,155356,155900],"length":1,"stats":{"Line":4}},{"line":287,"address":[156728,155368,155912,156456,155640,156184],"length":1,"stats":{"Line":4}},{"line":291,"address":[],"length":0,"stats":{"Line":0}},{"line":294,"address":[],"length":0,"stats":{"Line":0}},{"line":297,"address":[],"length":0,"stats":{"Line":0}},{"line":300,"address":[],"length":0,"stats":{"Line":0}},{"line":304,"address":[],"length":0,"stats":{"Line":0}},{"line":308,"address":[223792,224064,224589,224861,224880,225133,225152,225405,224336,224317,224045,224608],"length":1,"stats":{"Line":12}},{"line":315,"address":[177496,177768,177542,177224,178086,177270,178040,178312,178358,176998,177814,176952],"length":1,"stats":{"Line":24}},{"line":316,"address":[],"length":0,"stats":{"Line":6}},{"line":320,"address":[],"length":0,"stats":{"Line":12}},{"line":323,"address":[224795,225067,225339,224251,223979,224523],"length":1,"stats":{"Line":6}},{"line":325,"address":[224284,224556,224828,224012,225100,225372],"length":1,"stats":{"Line":6}},{"line":327,"address":[224840,225384,224568,225112,224024,224296],"length":1,"stats":{"Line":6}},{"line":365,"address":[],"length":0,"stats":{"Line":2}},{"line":367,"address":[],"length":0,"stats":{"Line":2}},{"line":370,"address":[],"length":0,"stats":{"Line":2}},{"line":403,"address":[],"length":0,"stats":{"Line":1}},{"line":405,"address":[714638,714688,714750,714708],"length":1,"stats":{"Line":3}},{"line":407,"address":[714745,714715],"length":1,"stats":{"Line":2}},{"line":409,"address":[],"length":0,"stats":{"Line":0}},{"line":416,"address":[],"length":0,"stats":{"Line":2}},{"line":417,"address":[714797,714815],"length":1,"stats":{"Line":2}},{"line":419,"address":[714821],"length":1,"stats":{"Line":2}},{"line":421,"address":[],"length":0,"stats":{"Line":0}},{"line":428,"address":[],"length":0,"stats":{"Line":1}},{"line":429,"address":[],"length":0,"stats":{"Line":1}},{"line":431,"address":[],"length":0,"stats":{"Line":1}},{"line":433,"address":[],"length":0,"stats":{"Line":0}},{"line":464,"address":[161270,161184,161296],"length":1,"stats":{"Line":5}},{"line":466,"address":[161198],"length":1,"stats":{"Line":5}},{"line":469,"address":[161245],"length":1,"stats":{"Line":6}},{"line":499,"address":[],"length":0,"stats":{"Line":2}},{"line":501,"address":[],"length":0,"stats":{"Line":6}},{"line":503,"address":[],"length":0,"stats":{"Line":4}},{"line":505,"address":[],"length":0,"stats":{"Line":0}},{"line":512,"address":[],"length":0,"stats":{"Line":2}},{"line":513,"address":[],"length":0,"stats":{"Line":2}},{"line":535,"address":[],"length":0,"stats":{"Line":0}},{"line":537,"address":[],"length":0,"stats":{"Line":0}},{"line":539,"address":[],"length":0,"stats":{"Line":0}},{"line":562,"address":[],"length":0,"stats":{"Line":0}},{"line":564,"address":[],"length":0,"stats":{"Line":0}},{"line":566,"address":[],"length":0,"stats":{"Line":0}}],"covered":70,"coverable":111},{"path":["/","home","botahamec","Projects","happylock","src","rwlock","write_guard.rs"],"content":"use std::fmt::{Debug, Display};\nuse std::hash::Hash;\nuse std::marker::PhantomData;\nuse std::ops::{Deref, DerefMut};\n\nuse lock_api::RawRwLock;\n\nuse crate::lockable::RawLock;\nuse crate::ThreadKey;\n\nuse super::{RwLock, RwLockWriteGuard, RwLockWriteRef};\n\n// These impls make things slightly easier because now you can use\n// `println!(\"{guard}\")` instead of `println!(\"{}\", *guard)`\n\n#[mutants::skip] // hashing involves PRNG and is difficult to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Hash + ?Sized, R: RawRwLock\u003e Hash for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.deref().hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug + ?Sized, R: RawRwLock\u003e Debug for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: Display + ?Sized, R: RawRwLock\u003e Display for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e Deref for RwLockWriteRef\u003c'_, T, R\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t// safety: this is the only type that can use `value`, and there's\n\t\t// a reference to this type, so there cannot be any mutable\n\t\t// references to this value.\n\t\tunsafe { \u0026*self.0.data.get() }\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e DerefMut for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t// safety: this is the only type that can use `value`, and we have a\n\t\t// mutable reference to this type, so there cannot be any other\n\t\t// references to this value.\n\t\tunsafe { \u0026mut *self.0.data.get() }\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e AsRef\u003cT\u003e for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e AsMut\u003cT\u003e for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e Drop for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn drop(\u0026mut self) {\n\t\t// safety: this guard is being destroyed, so the data cannot be\n\t\t// accessed without locking again\n\t\tunsafe { self.0.raw_unlock() }\n\t}\n}\n\nimpl\u003c'a, T: ?Sized + 'a, R: RawRwLock\u003e RwLockWriteRef\u003c'a, T, R\u003e {\n\t/// Creates a reference to the underlying data of an [`RwLock`] without\n\t/// locking or taking ownership of the key.\n\t#[must_use]\n\tpub(crate) unsafe fn new(mutex: \u0026'a RwLock\u003cT, R\u003e) -\u003e Self {\n\t\tSelf(mutex, PhantomData)\n\t}\n}\n\n#[mutants::skip] // hashing involves PRNG and is difficult to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Hash + ?Sized, R: RawRwLock\u003e Hash for RwLockWriteGuard\u003c'_, T, R\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.deref().hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug + ?Sized, R: RawRwLock\u003e Debug for RwLockWriteGuard\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: Display + ?Sized, R: RawRwLock\u003e Display for RwLockWriteGuard\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e Deref for RwLockWriteGuard\u003c'_, T, R\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t\u0026self.rwlock\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e DerefMut for RwLockWriteGuard\u003c'_, T, R\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t\u0026mut self.rwlock\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e AsRef\u003cT\u003e for RwLockWriteGuard\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e AsMut\u003cT\u003e for RwLockWriteGuard\u003c'_, T, R\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself\n\t}\n}\n\nimpl\u003c'a, T: ?Sized + 'a, R: RawRwLock\u003e RwLockWriteGuard\u003c'a, T, R\u003e {\n\t/// Create a guard to the given mutex. Undefined if multiple guards to the\n\t/// same mutex exist at once.\n\t#[must_use]\n\tpub(super) unsafe fn new(rwlock: \u0026'a RwLock\u003cT, R\u003e, thread_key: ThreadKey) -\u003e Self {\n\t\tSelf {\n\t\t\trwlock: RwLockWriteRef(rwlock, PhantomData),\n\t\t\tthread_key,\n\t\t}\n\t}\n}\n\nunsafe impl\u003cT: ?Sized + Sync, R: RawRwLock + Sync\u003e Sync for RwLockWriteRef\u003c'_, T, R\u003e {}\n","traces":[{"line":33,"address":[716112],"length":1,"stats":{"Line":1}},{"line":34,"address":[],"length":0,"stats":{"Line":1}},{"line":41,"address":[],"length":0,"stats":{"Line":3}},{"line":45,"address":[161077],"length":1,"stats":{"Line":3}},{"line":50,"address":[161120],"length":1,"stats":{"Line":1}},{"line":54,"address":[161125],"length":1,"stats":{"Line":1}},{"line":59,"address":[],"length":0,"stats":{"Line":0}},{"line":60,"address":[],"length":0,"stats":{"Line":0}},{"line":65,"address":[],"length":0,"stats":{"Line":0}},{"line":66,"address":[],"length":0,"stats":{"Line":0}},{"line":71,"address":[268368,268384],"length":1,"stats":{"Line":5}},{"line":74,"address":[199429],"length":1,"stats":{"Line":6}},{"line":82,"address":[],"length":0,"stats":{"Line":3}},{"line":83,"address":[],"length":0,"stats":{"Line":0}},{"line":104,"address":[],"length":0,"stats":{"Line":1}},{"line":105,"address":[],"length":0,"stats":{"Line":1}},{"line":112,"address":[],"length":0,"stats":{"Line":3}},{"line":113,"address":[],"length":0,"stats":{"Line":3}},{"line":118,"address":[],"length":0,"stats":{"Line":1}},{"line":119,"address":[161157],"length":1,"stats":{"Line":1}},{"line":124,"address":[],"length":0,"stats":{"Line":0}},{"line":125,"address":[],"length":0,"stats":{"Line":0}},{"line":130,"address":[],"length":0,"stats":{"Line":0}},{"line":131,"address":[],"length":0,"stats":{"Line":0}},{"line":139,"address":[],"length":0,"stats":{"Line":3}},{"line":141,"address":[],"length":0,"stats":{"Line":0}}],"covered":16,"coverable":26},{"path":["/","home","botahamec","Projects","happylock","src","rwlock","write_lock.rs"],"content":"use std::fmt::Debug;\n\nuse lock_api::RawRwLock;\n\nuse crate::lockable::{Lockable, RawLock};\nuse crate::ThreadKey;\n\nuse super::{RwLock, RwLockWriteGuard, RwLockWriteRef, WriteLock};\n\nunsafe impl\u003cT: Send, R: RawRwLock + Send + Sync\u003e Lockable for WriteLock\u003c'_, T, R\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= RwLockWriteRef\u003c'g, T, R\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= \u0026'a mut T\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tptrs.push(self.as_ref());\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tRwLockWriteRef::new(self.as_ref())\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.0.data_mut()\n\t}\n}\n\n// Technically, the exclusive locks can also be shared, but there's currently\n// no way to express that. I don't think I want to ever express that.\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: ?Sized + Debug, R: RawRwLock\u003e Debug for WriteLock\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t// safety: this is just a try lock, and the value is dropped\n\t\t// immediately after, so there's no risk of blocking ourselves\n\t\t// or any other threads\n\t\t// It makes zero sense to try using an exclusive lock for this, so this\n\t\t// is the only time when WriteLock does a read.\n\t\tif let Some(value) = unsafe { self.0.try_read_no_key() } {\n\t\t\tf.debug_struct(\"WriteLock\").field(\"data\", \u0026\u0026*value).finish()\n\t\t} else {\n\t\t\tstruct LockedPlaceholder;\n\t\t\timpl Debug for LockedPlaceholder {\n\t\t\t\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t\t\t\tf.write_str(\"\u003clocked\u003e\")\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tf.debug_struct(\"WriteLock\")\n\t\t\t\t.field(\"data\", \u0026LockedPlaceholder)\n\t\t\t\t.finish()\n\t\t}\n\t}\n}\n\nimpl\u003c'l, T, R\u003e From\u003c\u0026'l RwLock\u003cT, R\u003e\u003e for WriteLock\u003c'l, T, R\u003e {\n\tfn from(value: \u0026'l RwLock\u003cT, R\u003e) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\nimpl\u003cT: ?Sized, R\u003e AsRef\u003cRwLock\u003cT, R\u003e\u003e for WriteLock\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026RwLock\u003cT, R\u003e {\n\t\tself.0\n\t}\n}\n\nimpl\u003c'l, T, R\u003e WriteLock\u003c'l, T, R\u003e {\n\t/// Creates a new `WriteLock` which accesses the given [`RwLock`]\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{rwlock::WriteLock, RwLock};\n\t///\n\t/// let lock = RwLock::new(5);\n\t/// let write_lock = WriteLock::new(\u0026lock);\n\t/// ```\n\t#[must_use]\n\tpub const fn new(rwlock: \u0026'l RwLock\u003cT, R\u003e) -\u003e Self {\n\t\tSelf(rwlock)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e WriteLock\u003c'_, T, R\u003e {\n\t/// Locks the underlying [`RwLock`] with exclusive write access, blocking\n\t/// the current until it can be acquired.\n\t///\n\t/// This function will not return while other writers or readers currently\n\t/// have access to the lock.\n\t///\n\t/// Returns an RAII guard which will drop the write access of this `RwLock`\n\t/// when dropped.\n\t///\n\t/// Because this method takes a [`ThreadKey`], it's not possible for this\n\t/// method to cause a deadlock.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, RwLock};\n\t/// use happylock::rwlock::WriteLock;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(1);\n\t/// let writer = WriteLock::new(\u0026lock);\n\t///\n\t/// let mut n = writer.lock(key);\n\t/// *n += 2;\n\t/// ```\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\t#[must_use]\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e RwLockWriteGuard\u003c'_, T, R\u003e {\n\t\tself.0.write(key)\n\t}\n\n\t/// Attempts to lock the underlying [`RwLock`] with exclusive write access.\n\t///\n\t/// This function does not block. If the lock could not be acquired at this\n\t/// time, then `None` is returned. Otherwise, an RAII guard is returned\n\t/// which will release the lock when it is dropped.\n\t///\n\t/// This function does not provide any guarantees with respect to the\n\t/// ordering of whether contentious readers or writers will acquire the\n\t/// lock first.\n\t///\n\t/// # Errors\n\t///\n\t/// If the [`RwLock`] could not be acquired because it was already locked,\n\t/// then an error will be returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::rwlock::WriteLock;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(1);\n\t/// let writer = WriteLock::new(\u0026lock);\n\t///\n\t/// match writer.try_lock(key) {\n\t/// Ok(n) =\u003e assert_eq!(*n, 1),\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t/// ```\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e Result\u003cRwLockWriteGuard\u003c'_, T, R\u003e, ThreadKey\u003e {\n\t\tself.0.try_write(key)\n\t}\n\n\t// There's no `try_lock_no_key`. Instead, `try_read_no_key` is called on\n\t// the referenced `RwLock`.\n\n\t/// Immediately drops the guard, and consequently releases the exclusive\n\t/// lock on the underlying [`RwLock`].\n\t///\n\t/// This function is equivalent to calling [`drop`] on the guard, except\n\t/// that it returns the key that was used to create it. Alternately, the\n\t/// guard will be automatically dropped when it goes out of scope.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::rwlock::WriteLock;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(0);\n\t/// let writer = WriteLock::new(\u0026lock);\n\t///\n\t/// let mut guard = writer.lock(key);\n\t/// *guard += 20;\n\t/// let key = WriteLock::unlock(guard);\n\t/// ```\n\t#[must_use]\n\tpub fn unlock(guard: RwLockWriteGuard\u003c'_, T, R\u003e) -\u003e ThreadKey {\n\t\tRwLock::unlock_write(guard)\n\t}\n}\n","traces":[{"line":21,"address":[715712],"length":1,"stats":{"Line":1}},{"line":22,"address":[],"length":0,"stats":{"Line":1}},{"line":25,"address":[],"length":0,"stats":{"Line":0}},{"line":26,"address":[],"length":0,"stats":{"Line":0}},{"line":29,"address":[],"length":0,"stats":{"Line":0}},{"line":30,"address":[],"length":0,"stats":{"Line":0}},{"line":64,"address":[],"length":0,"stats":{"Line":0}},{"line":65,"address":[],"length":0,"stats":{"Line":0}},{"line":70,"address":[],"length":0,"stats":{"Line":1}},{"line":71,"address":[],"length":0,"stats":{"Line":1}},{"line":87,"address":[],"length":0,"stats":{"Line":3}},{"line":88,"address":[],"length":0,"stats":{"Line":0}},{"line":121,"address":[],"length":0,"stats":{"Line":1}},{"line":122,"address":[],"length":0,"stats":{"Line":1}},{"line":155,"address":[],"length":0,"stats":{"Line":1}},{"line":156,"address":[],"length":0,"stats":{"Line":1}},{"line":184,"address":[],"length":0,"stats":{"Line":0}},{"line":185,"address":[],"length":0,"stats":{"Line":0}}],"covered":9,"coverable":18},{"path":["/","home","botahamec","Projects","happylock","src","rwlock.rs"],"content":"use std::cell::UnsafeCell;\nuse std::marker::PhantomData;\n\nuse lock_api::RawRwLock;\n\nuse crate::poisonable::PoisonFlag;\nuse crate::ThreadKey;\n\nmod rwlock;\n\nmod read_lock;\nmod write_lock;\n\nmod read_guard;\nmod write_guard;\n\n#[cfg(feature = \"spin\")]\npub type SpinRwLock\u003cT\u003e = RwLock\u003cT, spin::RwLock\u003c()\u003e\u003e;\n\n#[cfg(feature = \"parking_lot\")]\npub type ParkingRwLock\u003cT\u003e = RwLock\u003cT, parking_lot::RawRwLock\u003e;\n\n/// A reader-writer lock\n///\n/// This type of lock allows a number of readers or at most one writer at any\n/// point in time. The write portion of this lock typically allows modification\n/// of the underlying data (exclusive access) and the read portion of this lock\n/// typically allows for read-only access (shared access).\n///\n/// In comparison, a [`Mutex`] does not distinguish between readers or writers\n/// that acquire the lock, therefore blocking any threads waiting for the lock\n/// to become available. An `RwLock` will allow any number of readers to\n/// acquire the lock as long as a writer is not holding the lock.\n///\n/// The type parameter T represents the data that this lock protects. It is\n/// required that T satisfies [`Send`] to be shared across threads and [`Sync`]\n/// to allow concurrent access through readers. The RAII guard returned from\n/// the locking methods implement [`Deref`] (and [`DerefMut`] for the `write`\n/// methods) to allow access to the content of the lock.\n///\n/// Locking the mutex on a thread that already locked it is impossible, due to\n/// the requirement of the [`ThreadKey`]. Therefore, this will never deadlock.\n///\n/// [`ThreadKey`]: `crate::ThreadKey`\n/// [`Mutex`]: `crate::mutex::Mutex`\n/// [`Deref`]: `std::ops::Deref`\n/// [`DerefMut`]: `std::ops::DerefMut`\npub struct RwLock\u003cT: ?Sized, R\u003e {\n\traw: R,\n\tpoison: PoisonFlag,\n\tdata: UnsafeCell\u003cT\u003e,\n}\n\n/// Grants read access to an [`RwLock`]\n///\n/// This structure is designed to be used in a [`LockCollection`] to indicate\n/// that only read access is needed to the data.\n///\n/// [`LockCollection`]: `crate::LockCollection`\n#[repr(transparent)]\npub struct ReadLock\u003c'l, T: ?Sized, R\u003e(\u0026'l RwLock\u003cT, R\u003e);\n\n/// Grants write access to an [`RwLock`]\n///\n/// This structure is designed to be used in a [`LockCollection`] to indicate\n/// that write access is needed to the data.\n///\n/// [`LockCollection`]: `crate::LockCollection`\n#[repr(transparent)]\npub struct WriteLock\u003c'l, T: ?Sized, R\u003e(\u0026'l RwLock\u003cT, R\u003e);\n\n/// RAII structure that unlocks the shared read access to a [`RwLock`]\n///\n/// This is similar to [`RwLockReadRef`], except it does not hold a\n/// [`Keyable`].\npub struct RwLockReadRef\u003c'a, T: ?Sized, R: RawRwLock\u003e(\n\t\u0026'a RwLock\u003cT, R\u003e,\n\tPhantomData\u003c(\u0026'a mut T, R::GuardMarker)\u003e,\n);\n\n/// RAII structure that unlocks the exclusive write access to a [`RwLock`]\n///\n/// This is similar to [`RwLockWriteRef`], except it does not hold a\n/// [`Keyable`].\npub struct RwLockWriteRef\u003c'a, T: ?Sized, R: RawRwLock\u003e(\n\t\u0026'a RwLock\u003cT, R\u003e,\n\tPhantomData\u003c(\u0026'a mut T, R::GuardMarker)\u003e,\n);\n\n/// RAII structure used to release the shared read access of a lock when\n/// dropped.\n///\n/// This structure is created by the [`read`] and [`try_read`] methods on\n/// [`RwLock`].\n///\n/// [`read`]: `RwLock::read`\n/// [`try_read`]: `RwLock::try_read`\npub struct RwLockReadGuard\u003c'a, T: ?Sized, R: RawRwLock\u003e {\n\trwlock: RwLockReadRef\u003c'a, T, R\u003e,\n\tthread_key: ThreadKey,\n}\n\n/// RAII structure used to release the exclusive write access of a lock when\n/// dropped.\n///\n/// This structure is created by the [`write`] and [`try_write`] methods on\n/// [`RwLock`]\n///\n/// [`try_write`]: `RwLock::try_write`\npub struct RwLockWriteGuard\u003c'a, T: ?Sized, R: RawRwLock\u003e {\n\trwlock: RwLockWriteRef\u003c'a, T, R\u003e,\n\tthread_key: ThreadKey,\n}\n\n#[cfg(test)]\nmod tests {\n\tuse crate::lockable::Lockable;\n\tuse crate::RwLock;\n\tuse crate::ThreadKey;\n\n\tuse super::*;\n\n\t#[test]\n\tfn unlocked_when_initialized() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\n\t\tassert!(!lock.is_locked());\n\t\tassert!(lock.try_write(key).is_ok());\n\t}\n\n\t#[test]\n\tfn read_lock_unlocked_when_initialized() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet reader = ReadLock::new(\u0026lock);\n\n\t\tassert!(reader.try_lock(key).is_ok());\n\t}\n\n\t#[test]\n\tfn read_lock_from_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::from(\"Hello, world!\");\n\t\tlet reader = ReadLock::from(\u0026lock);\n\n\t\tlet guard = reader.lock(key);\n\t\tassert_eq!(*guard, \"Hello, world!\");\n\t}\n\n\t#[test]\n\tfn write_lock_unlocked_when_initialized() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet writer = WriteLock::new(\u0026lock);\n\n\t\tassert!(writer.try_lock(key).is_ok());\n\t}\n\n\t#[test]\n\tfn read_lock_get_ptrs() {\n\t\tlet rwlock = RwLock::new(5);\n\t\tlet readlock = ReadLock::new(\u0026rwlock);\n\t\tlet mut lock_ptrs = Vec::new();\n\t\treadlock.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tassert!(std::ptr::addr_eq(lock_ptrs[0], \u0026rwlock));\n\t}\n\n\t#[test]\n\tfn write_lock_get_ptrs() {\n\t\tlet rwlock = RwLock::new(5);\n\t\tlet writelock = WriteLock::new(\u0026rwlock);\n\t\tlet mut lock_ptrs = Vec::new();\n\t\twritelock.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tassert!(std::ptr::addr_eq(lock_ptrs[0], \u0026rwlock));\n\t}\n\n\t#[test]\n\tfn locked_after_read() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\n\t\tlet guard = lock.read(key);\n\n\t\tassert!(lock.is_locked());\n\t\tdrop(guard)\n\t}\n\n\t#[test]\n\tfn locked_after_using_read_lock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet reader = ReadLock::new(\u0026lock);\n\n\t\tlet guard = reader.lock(key);\n\n\t\tassert!(lock.is_locked());\n\t\tdrop(guard)\n\t}\n\n\t#[test]\n\tfn locked_after_write() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\n\t\tlet guard = lock.write(key);\n\n\t\tassert!(lock.is_locked());\n\t\tdrop(guard)\n\t}\n\n\t#[test]\n\tfn locked_after_using_write_lock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet writer = WriteLock::new(\u0026lock);\n\n\t\tlet guard = writer.lock(key);\n\n\t\tassert!(lock.is_locked());\n\t\tdrop(guard)\n\t}\n\n\t#[test]\n\tfn read_display_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet guard = lock.read(key);\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn write_display_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet guard = lock.write(key);\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn read_ref_display_works() {\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet guard = unsafe { lock.try_read_no_key().unwrap() };\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn write_ref_display_works() {\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet guard = unsafe { lock.try_write_no_key().unwrap() };\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn dropping_read_ref_releases_rwlock() {\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\n\t\tlet guard = unsafe { lock.try_read_no_key().unwrap() };\n\t\tdrop(guard);\n\n\t\tassert!(!lock.is_locked());\n\t}\n\n\t#[test]\n\tfn dropping_write_guard_releases_rwlock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\n\t\tlet guard = lock.write(key);\n\t\tdrop(guard);\n\n\t\tassert!(!lock.is_locked());\n\t}\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","evil_mutex.rs"],"content":"use std::sync::Arc;\n\nuse happylock::collection::{BoxedLockCollection, RetryingLockCollection};\nuse happylock::mutex::Mutex;\nuse happylock::ThreadKey;\nuse lock_api::{GuardNoSend, RawMutex};\n\nstruct EvilMutex {\n\tinner: parking_lot::RawMutex,\n}\n\nunsafe impl RawMutex for EvilMutex {\n\t#[allow(clippy::declare_interior_mutable_const)]\n\tconst INIT: Self = Self {\n\t\tinner: parking_lot::RawMutex::INIT,\n\t};\n\n\ttype GuardMarker = GuardNoSend;\n\n\tfn lock(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn try_lock(\u0026self) -\u003e bool {\n\t\tself.inner.try_lock()\n\t}\n\n\tunsafe fn unlock(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n}\n\n#[test]\nfn boxed_mutexes() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet good_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(5));\n\tlet evil_mutex: Arc\u003cMutex\u003ci32, EvilMutex\u003e\u003e = Arc::new(Mutex::new(7));\n\tlet useless_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(10));\n\tlet c_good = Arc::clone(\u0026good_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\t_ = collection.lock(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(good_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n\tassert!(evil_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n}\n\n#[test]\nfn retrying_mutexes() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet good_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(5));\n\tlet evil_mutex: Arc\u003cMutex\u003ci32, EvilMutex\u003e\u003e = Arc::new(Mutex::new(7));\n\tlet useless_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(10));\n\tlet c_good = Arc::clone(\u0026good_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tRetryingLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\tcollection.lock(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(good_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n\tassert!(evil_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","evil_rwlock.rs"],"content":"use std::sync::Arc;\n\nuse happylock::collection::{BoxedLockCollection, RetryingLockCollection};\nuse happylock::rwlock::RwLock;\nuse happylock::ThreadKey;\nuse lock_api::{GuardNoSend, RawRwLock};\n\nstruct EvilRwLock {\n\tinner: parking_lot::RawRwLock,\n}\n\nunsafe impl RawRwLock for EvilRwLock {\n\t#[allow(clippy::declare_interior_mutable_const)]\n\tconst INIT: Self = Self {\n\t\tinner: parking_lot::RawRwLock::INIT,\n\t};\n\n\ttype GuardMarker = GuardNoSend;\n\n\tfn lock_shared(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn try_lock_shared(\u0026self) -\u003e bool {\n\t\tself.inner.try_lock_shared()\n\t}\n\n\tunsafe fn unlock_shared(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn lock_exclusive(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn try_lock_exclusive(\u0026self) -\u003e bool {\n\t\tself.inner.try_lock_exclusive()\n\t}\n\n\tunsafe fn unlock_exclusive(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n}\n\n#[test]\nfn boxed_rwlocks() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet good_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(5));\n\tlet evil_mutex: Arc\u003cRwLock\u003ci32, EvilRwLock\u003e\u003e = Arc::new(RwLock::new(7));\n\tlet useless_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(10));\n\tlet c_good = Arc::clone(\u0026good_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\t_ = collection.lock(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(good_mutex.scoped_try_write(\u0026mut key, |_| {}).is_ok());\n\tassert!(evil_mutex.scoped_try_write(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_write(\u0026mut key, |_| {}).is_ok());\n}\n\n#[test]\nfn retrying_rwlocks() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet good_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(5));\n\tlet evil_mutex: Arc\u003cRwLock\u003ci32, EvilRwLock\u003e\u003e = Arc::new(RwLock::new(7));\n\tlet useless_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(10));\n\tlet c_good = Arc::clone(\u0026good_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tRetryingLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\tcollection.lock(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(good_mutex.scoped_try_write(\u0026mut key, |_| {}).is_ok());\n\tassert!(evil_mutex.scoped_try_write(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_write(\u0026mut key, |_| {}).is_ok());\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","evil_try_mutex.rs"],"content":"use std::sync::Arc;\n\nuse happylock::{\n\tcollection::{BoxedLockCollection, RetryingLockCollection},\n\tmutex::Mutex,\n\tThreadKey,\n};\nuse lock_api::{GuardNoSend, RawMutex};\n\nstruct EvilMutex {\n\tinner: parking_lot::RawMutex,\n}\n\nunsafe impl RawMutex for EvilMutex {\n\t#[allow(clippy::declare_interior_mutable_const)]\n\tconst INIT: Self = Self {\n\t\tinner: parking_lot::RawMutex::INIT,\n\t};\n\n\ttype GuardMarker = GuardNoSend;\n\n\tfn lock(\u0026self) {\n\t\tself.inner.lock()\n\t}\n\n\tfn try_lock(\u0026self) -\u003e bool {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tunsafe fn unlock(\u0026self) {\n\t\tself.inner.unlock()\n\t}\n}\n\n#[test]\nfn boxed_mutexes() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet good_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(5));\n\tlet evil_mutex: Arc\u003cMutex\u003ci32, EvilMutex\u003e\u003e = Arc::new(Mutex::new(7));\n\tlet useless_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(10));\n\tlet c_good = Arc::clone(\u0026good_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\tlet g = collection.try_lock(key);\n\t\tprintln!(\"{}\", g.unwrap().1);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(good_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n\tassert!(evil_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n}\n\n#[test]\nfn retrying_mutexes() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet good_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(5));\n\tlet evil_mutex: Arc\u003cMutex\u003ci32, EvilMutex\u003e\u003e = Arc::new(Mutex::new(7));\n\tlet useless_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(10));\n\tlet c_good = Arc::clone(\u0026good_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tRetryingLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\tlet _ = collection.try_lock(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(good_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n\tassert!(evil_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","evil_try_rwlock.rs"],"content":"use std::sync::Arc;\n\nuse happylock::collection::{BoxedLockCollection, RetryingLockCollection};\nuse happylock::rwlock::RwLock;\nuse happylock::ThreadKey;\nuse lock_api::{GuardNoSend, RawRwLock};\n\nstruct EvilRwLock {\n\tinner: parking_lot::RawRwLock,\n}\n\nunsafe impl RawRwLock for EvilRwLock {\n\t#[allow(clippy::declare_interior_mutable_const)]\n\tconst INIT: Self = Self {\n\t\tinner: parking_lot::RawRwLock::INIT,\n\t};\n\n\ttype GuardMarker = GuardNoSend;\n\n\tfn lock_shared(\u0026self) {\n\t\tself.inner.lock_shared()\n\t}\n\n\tfn try_lock_shared(\u0026self) -\u003e bool {\n\t\tpanic!(\"mwahahahaha\")\n\t}\n\n\tunsafe fn unlock_shared(\u0026self) {\n\t\tself.inner.unlock_shared()\n\t}\n\n\tfn lock_exclusive(\u0026self) {\n\t\tself.inner.lock_exclusive()\n\t}\n\n\tfn try_lock_exclusive(\u0026self) -\u003e bool {\n\t\tpanic!(\"mwahahahaha\")\n\t}\n\n\tunsafe fn unlock_exclusive(\u0026self) {\n\t\tself.inner.unlock_exclusive()\n\t}\n}\n\n#[test]\nfn boxed_rwlocks() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet good_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(5));\n\tlet evil_mutex: Arc\u003cRwLock\u003ci32, EvilRwLock\u003e\u003e = Arc::new(RwLock::new(7));\n\tlet useless_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(10));\n\tlet c_good = Arc::clone(\u0026good_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\tlet _ = collection.try_read(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(good_mutex.scoped_try_read(\u0026mut key, |_| {}).is_ok());\n\tassert!(evil_mutex.scoped_try_read(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_read(\u0026mut key, |_| {}).is_ok());\n}\n\n#[test]\nfn retrying_rwlocks() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet good_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(5));\n\tlet evil_mutex: Arc\u003cRwLock\u003ci32, EvilRwLock\u003e\u003e = Arc::new(RwLock::new(7));\n\tlet useless_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(10));\n\tlet c_good = Arc::clone(\u0026good_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tRetryingLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\t_ = collection.try_read(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(good_mutex.scoped_try_read(\u0026mut key, |_| {}).is_ok());\n\tassert!(evil_mutex.scoped_try_read(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_read(\u0026mut key, |_| {}).is_ok());\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","evil_unlock_mutex.rs"],"content":"use std::sync::Arc;\n\nuse happylock::collection::{BoxedLockCollection, RetryingLockCollection};\nuse happylock::mutex::Mutex;\nuse happylock::ThreadKey;\nuse lock_api::{GuardNoSend, RawMutex};\n\nstruct KindaEvilMutex {\n\tinner: parking_lot::RawMutex,\n}\n\nstruct EvilMutex {}\n\nunsafe impl RawMutex for KindaEvilMutex {\n\t#[allow(clippy::declare_interior_mutable_const)]\n\tconst INIT: Self = Self {\n\t\tinner: parking_lot::RawMutex::INIT,\n\t};\n\n\ttype GuardMarker = GuardNoSend;\n\n\tfn lock(\u0026self) {\n\t\tself.inner.lock()\n\t}\n\n\tfn try_lock(\u0026self) -\u003e bool {\n\t\tself.inner.try_lock()\n\t}\n\n\tunsafe fn unlock(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n}\n\nunsafe impl RawMutex for EvilMutex {\n\t#[allow(clippy::declare_interior_mutable_const)]\n\tconst INIT: Self = Self {};\n\n\ttype GuardMarker = GuardNoSend;\n\n\tfn lock(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn try_lock(\u0026self) -\u003e bool {\n\t\tpanic!(\"mwahahahaha\")\n\t}\n\n\tunsafe fn unlock(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n}\n\n#[test]\nfn boxed_mutexes() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet kinda_evil_mutex: Arc\u003cMutex\u003ci32, KindaEvilMutex\u003e\u003e = Arc::new(Mutex::new(5));\n\tlet evil_mutex: Arc\u003cMutex\u003ci32, EvilMutex\u003e\u003e = Arc::new(Mutex::new(7));\n\tlet useless_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(10));\n\tlet c_good = Arc::clone(\u0026kinda_evil_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\t_ = collection.lock(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(kinda_evil_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n\tassert!(evil_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n}\n\n#[test]\nfn retrying_mutexes() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet kinda_evil_mutex: Arc\u003cMutex\u003ci32, KindaEvilMutex\u003e\u003e = Arc::new(Mutex::new(5));\n\tlet evil_mutex: Arc\u003cMutex\u003ci32, EvilMutex\u003e\u003e = Arc::new(Mutex::new(7));\n\tlet useless_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(10));\n\tlet c_good = Arc::clone(\u0026kinda_evil_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tRetryingLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\tcollection.lock(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(kinda_evil_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n\tassert!(evil_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","evil_unlock_rwlock.rs"],"content":"use std::sync::Arc;\n\nuse happylock::collection::{BoxedLockCollection, RetryingLockCollection};\nuse happylock::rwlock::RwLock;\nuse happylock::ThreadKey;\nuse lock_api::{GuardNoSend, RawRwLock};\n\nstruct KindaEvilRwLock {\n\tinner: parking_lot::RawRwLock,\n}\n\nstruct EvilRwLock {}\n\nunsafe impl RawRwLock for KindaEvilRwLock {\n\t#[allow(clippy::declare_interior_mutable_const)]\n\tconst INIT: Self = Self {\n\t\tinner: parking_lot::RawRwLock::INIT,\n\t};\n\n\ttype GuardMarker = GuardNoSend;\n\n\tfn lock_shared(\u0026self) {\n\t\tself.inner.lock_shared()\n\t}\n\n\tfn try_lock_shared(\u0026self) -\u003e bool {\n\t\tself.inner.try_lock_shared()\n\t}\n\n\tunsafe fn unlock_shared(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn lock_exclusive(\u0026self) {\n\t\tself.inner.lock_exclusive()\n\t}\n\n\tfn try_lock_exclusive(\u0026self) -\u003e bool {\n\t\tself.inner.try_lock_exclusive()\n\t}\n\n\tunsafe fn unlock_exclusive(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n}\n\nunsafe impl RawRwLock for EvilRwLock {\n\t#[allow(clippy::declare_interior_mutable_const)]\n\tconst INIT: Self = Self {};\n\n\ttype GuardMarker = GuardNoSend;\n\n\tfn lock_shared(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn try_lock_shared(\u0026self) -\u003e bool {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tunsafe fn unlock_shared(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn lock_exclusive(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn try_lock_exclusive(\u0026self) -\u003e bool {\n\t\tpanic!(\"mwahahahaha\")\n\t}\n\n\tunsafe fn unlock_exclusive(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n}\n\n#[test]\nfn boxed_rwlocks() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet kinda_evil_mutex: RwLock\u003ci32, KindaEvilRwLock\u003e = RwLock::new(5);\n\tlet evil_mutex: RwLock\u003ci32, EvilRwLock\u003e = RwLock::new(7);\n\tlet useless_mutex: RwLock\u003ci32, parking_lot::RawRwLock\u003e = RwLock::new(10);\n\n\tlet r = std::thread::scope(|s| {\n\t\tlet r = s\n\t\t\t.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet collection =\n\t\t\t\t\tBoxedLockCollection::try_new((\u0026kinda_evil_mutex, \u0026evil_mutex, \u0026useless_mutex))\n\t\t\t\t\t\t.unwrap();\n\t\t\t\t_ = collection.read(key);\n\t\t\t})\n\t\t\t.join();\n\n\t\tr\n\t});\n\n\tassert!(r.is_err());\n\tassert!(kinda_evil_mutex.scoped_try_write(\u0026mut key, |_| {}).is_err());\n\tassert!(evil_mutex.scoped_try_write(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_write(\u0026mut key, |_| {}).is_ok());\n}\n\n#[test]\nfn retrying_rwlocks() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet kinda_evil_mutex: Arc\u003cRwLock\u003ci32, KindaEvilRwLock\u003e\u003e = Arc::new(RwLock::new(5));\n\tlet evil_mutex: Arc\u003cRwLock\u003ci32, EvilRwLock\u003e\u003e = Arc::new(RwLock::new(7));\n\tlet useless_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(10));\n\tlet c_good = Arc::clone(\u0026kinda_evil_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tRetryingLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\tcollection.read(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(kinda_evil_mutex.scoped_try_write(\u0026mut key, |_| {}).is_err());\n\tassert!(evil_mutex.scoped_try_write(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_write(\u0026mut key, |_| {}).is_ok());\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","forget.rs"],"content":"use happylock::{Mutex, ThreadKey};\n\n#[test]\nfn no_new_threadkey_when_forgetting_lock() {\n\tlet key = ThreadKey::get().unwrap();\n\tlet mutex = Mutex::new(\"foo\".to_string());\n\n\tlet guard = mutex.lock(key);\n\tstd::mem::forget(guard);\n\n\tassert!(ThreadKey::get().is_none());\n}\n\n#[test]\nfn no_new_threadkey_in_scoped_lock() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet mutex = Mutex::new(\"foo\".to_string());\n\n\tmutex.scoped_lock(\u0026mut key, |_| {\n\t\tassert!(ThreadKey::get().is_none());\n\t});\n\n\tmutex.lock(key);\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","retry.rs"],"content":"use std::time::Duration;\n\nuse happylock::{collection::RetryingLockCollection, Mutex, ThreadKey};\n\nstatic MUTEX_1: Mutex\u003ci32\u003e = Mutex::new(1);\nstatic MUTEX_2: Mutex\u003ci32\u003e = Mutex::new(2);\nstatic MUTEX_3: Mutex\u003ci32\u003e = Mutex::new(3);\n\nfn thread_1() {\n\tlet key = ThreadKey::get().unwrap();\n\tlet mut guard = MUTEX_2.lock(key);\n\tstd::thread::sleep(Duration::from_millis(100));\n\t*guard = 5;\n}\n\nfn thread_2() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tstd::thread::sleep(Duration::from_millis(50));\n\tlet collection = RetryingLockCollection::try_new([\u0026MUTEX_1, \u0026MUTEX_2, \u0026MUTEX_3]).unwrap();\n\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\tassert_eq!(*guard[0], 4);\n\t\tassert_eq!(*guard[1], 5);\n\t\tassert_eq!(*guard[2], 3);\n\t});\n}\n\nfn thread_3() {\n\tlet key = ThreadKey::get().unwrap();\n\tstd::thread::sleep(Duration::from_millis(75));\n\tlet mut guard = MUTEX_1.lock(key);\n\tstd::thread::sleep(Duration::from_millis(100));\n\t*guard = 4;\n}\n\nfn thread_4() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tstd::thread::sleep(Duration::from_millis(25));\n\tlet collection = RetryingLockCollection::try_new([\u0026MUTEX_1, \u0026MUTEX_2]).unwrap();\n\tassert!(collection.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n}\n\n#[test]\nfn retries() {\n\tlet t1 = std::thread::spawn(thread_1);\n\tlet t2 = std::thread::spawn(thread_2);\n\tlet t3 = std::thread::spawn(thread_3);\n\tlet t4 = std::thread::spawn(thread_4);\n\n\tt1.join().unwrap();\n\tt2.join().unwrap();\n\tt3.join().unwrap();\n\tt4.join().unwrap();\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","retry_rw.rs"],"content":"use std::time::Duration;\n\nuse happylock::{collection::RetryingLockCollection, RwLock, ThreadKey};\n\nstatic RWLOCK_1: RwLock\u003ci32\u003e = RwLock::new(1);\nstatic RWLOCK_2: RwLock\u003ci32\u003e = RwLock::new(2);\nstatic RWLOCK_3: RwLock\u003ci32\u003e = RwLock::new(3);\n\nfn thread_1() {\n\tlet key = ThreadKey::get().unwrap();\n\tlet mut guard = RWLOCK_2.write(key);\n\tstd::thread::sleep(Duration::from_millis(75));\n\tassert_eq!(*guard, 2);\n\t*guard = 5;\n}\n\nfn thread_2() {\n\tlet key = ThreadKey::get().unwrap();\n\tlet collection = RetryingLockCollection::try_new([\u0026RWLOCK_1, \u0026RWLOCK_2, \u0026RWLOCK_3]).unwrap();\n\tstd::thread::sleep(Duration::from_millis(25));\n\tlet guard = collection.read(key);\n\tassert_eq!(*guard[0], 1);\n\tassert_eq!(*guard[1], 5);\n\tassert_eq!(*guard[2], 3);\n}\n\n#[test]\nfn retries() {\n\tlet t1 = std::thread::spawn(thread_1);\n\tlet t2 = std::thread::spawn(thread_2);\n\n\tt1.join().unwrap();\n\tt2.join().unwrap();\n}\n","traces":[],"covered":0,"coverable":0}]}; - var previousData = {"files":[{"path":["/","home","botahamec","Projects","happylock","examples","basic.rs"],"content":"use std::thread;\n\nuse happylock::{Mutex, ThreadKey};\n\nconst N: usize = 10;\n\nstatic DATA: Mutex\u003ci32\u003e = Mutex::new(0);\n\nfn main() {\n\tlet mut threads = Vec::new();\n\tfor _ in 0..N {\n\t\tlet th = thread::spawn(move || {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\tlet mut data = DATA.lock(key);\n\t\t\t*data += 1;\n\t\t});\n\t\tthreads.push(th);\n\t}\n\n\tfor th in threads {\n\t\t_ = th.join();\n\t}\n\n\tlet key = ThreadKey::get().unwrap();\n\tlet data = DATA.lock(key);\n\tprintln!(\"{data}\");\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","examples","dining_philosophers.rs"],"content":"use std::{thread, time::Duration};\n\nuse happylock::{collection, Mutex, ThreadKey};\n\nstatic PHILOSOPHERS: [Philosopher; 5] = [\n\tPhilosopher {\n\t\tname: \"Socrates\",\n\t\tleft: 0,\n\t\tright: 1,\n\t},\n\tPhilosopher {\n\t\tname: \"John Rawls\",\n\t\tleft: 1,\n\t\tright: 2,\n\t},\n\tPhilosopher {\n\t\tname: \"Jeremy Bentham\",\n\t\tleft: 2,\n\t\tright: 3,\n\t},\n\tPhilosopher {\n\t\tname: \"John Stuart Mill\",\n\t\tleft: 3,\n\t\tright: 4,\n\t},\n\tPhilosopher {\n\t\tname: \"Judith Butler\",\n\t\tleft: 4,\n\t\tright: 0,\n\t},\n];\n\nstatic FORKS: [Mutex\u003c()\u003e; 5] = [\n\tMutex::new(()),\n\tMutex::new(()),\n\tMutex::new(()),\n\tMutex::new(()),\n\tMutex::new(()),\n];\n\nstruct Philosopher {\n\tname: \u0026'static str,\n\tleft: usize,\n\tright: usize,\n}\n\nimpl Philosopher {\n\tfn cycle(\u0026self) {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tthread::sleep(Duration::from_secs(1));\n\n\t\t// safety: no philosopher asks for the same fork twice\n\t\tlet forks = [\u0026FORKS[self.left], \u0026FORKS[self.right]];\n\t\tlet forks = unsafe { collection::RefLockCollection::new_unchecked(\u0026forks) };\n\t\tlet forks = forks.lock(key);\n\t\tprintln!(\"{} is eating...\", self.name);\n\t\tthread::sleep(Duration::from_secs(1));\n\t\tprintln!(\"{} is done eating\", self.name);\n\t\tdrop(forks);\n\t}\n}\n\nfn main() {\n\tlet handles: Vec\u003c_\u003e = PHILOSOPHERS\n\t\t.iter()\n\t\t.map(|philosopher| thread::spawn(move || philosopher.cycle()))\n\t\t// The `collect` is absolutely necessary, because we're using lazy\n\t\t// iterators. If `collect` isn't used, then the thread won't spawn\n\t\t// until we try to join on it.\n\t\t.collect();\n\n\tfor handle in handles {\n\t\t_ = handle.join();\n\t}\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","examples","dining_philosophers_retry.rs"],"content":"use std::{thread, time::Duration};\n\nuse happylock::{collection, Mutex, ThreadKey};\n\nstatic PHILOSOPHERS: [Philosopher; 5] = [\n\tPhilosopher {\n\t\tname: \"Socrates\",\n\t\tleft: 0,\n\t\tright: 1,\n\t},\n\tPhilosopher {\n\t\tname: \"John Rawls\",\n\t\tleft: 1,\n\t\tright: 2,\n\t},\n\tPhilosopher {\n\t\tname: \"Jeremy Bentham\",\n\t\tleft: 2,\n\t\tright: 3,\n\t},\n\tPhilosopher {\n\t\tname: \"John Stuart Mill\",\n\t\tleft: 3,\n\t\tright: 4,\n\t},\n\tPhilosopher {\n\t\tname: \"Judith Butler\",\n\t\tleft: 4,\n\t\tright: 0,\n\t},\n];\n\nstatic FORKS: [Mutex\u003c()\u003e; 5] = [\n\tMutex::new(()),\n\tMutex::new(()),\n\tMutex::new(()),\n\tMutex::new(()),\n\tMutex::new(()),\n];\n\nstruct Philosopher {\n\tname: \u0026'static str,\n\tleft: usize,\n\tright: usize,\n}\n\nimpl Philosopher {\n\tfn cycle(\u0026self) {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tthread::sleep(Duration::from_secs(1));\n\n\t\t// safety: no philosopher asks for the same fork twice\n\t\tlet forks = [\u0026FORKS[self.left], \u0026FORKS[self.right]];\n\t\tlet forks = unsafe { collection::RetryingLockCollection::new_unchecked(\u0026forks) };\n\t\tlet forks = forks.lock(key);\n\t\tprintln!(\"{} is eating...\", self.name);\n\t\tthread::sleep(Duration::from_secs(1));\n\t\tprintln!(\"{} is done eating\", self.name);\n\t\tdrop(forks);\n\t}\n}\n\nfn main() {\n\tlet handles: Vec\u003c_\u003e = PHILOSOPHERS\n\t\t.iter()\n\t\t.map(|philosopher| thread::spawn(move || philosopher.cycle()))\n\t\t// The `collect` is absolutely necessary, because we're using lazy\n\t\t// iterators. If `collect` isn't used, then the thread won't spawn\n\t\t// until we try to join on it.\n\t\t.collect();\n\n\tfor handle in handles {\n\t\t_ = handle.join();\n\t}\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","examples","double_mutex.rs"],"content":"use std::thread;\n\nuse happylock::{collection::RefLockCollection, Mutex, ThreadKey};\n\nconst N: usize = 10;\n\nstatic DATA: (Mutex\u003ci32\u003e, Mutex\u003cString\u003e) = (Mutex::new(0), Mutex::new(String::new()));\n\nfn main() {\n\tlet mut threads = Vec::new();\n\tfor _ in 0..N {\n\t\tlet th = thread::spawn(move || {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\tlet lock = RefLockCollection::new(\u0026DATA);\n\t\t\tlet mut guard = lock.lock(key);\n\t\t\t*guard.1 = (100 - *guard.0).to_string();\n\t\t\t*guard.0 += 1;\n\t\t});\n\t\tthreads.push(th);\n\t}\n\n\tfor th in threads {\n\t\t_ = th.join();\n\t}\n\n\tlet key = ThreadKey::get().unwrap();\n\tlet data = RefLockCollection::new(\u0026DATA);\n\tlet data = data.lock(key);\n\tprintln!(\"{}\", data.0);\n\tprintln!(\"{}\", data.1);\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","examples","fibonacci.rs"],"content":"use happylock::{collection, LockCollection, Mutex, ThreadKey};\nuse std::thread;\n\nconst N: usize = 36;\n\nstatic DATA: [Mutex\u003ci32\u003e; 2] = [Mutex::new(0), Mutex::new(1)];\n\nfn main() {\n\tlet mut threads = Vec::new();\n\tfor _ in 0..N {\n\t\tlet th = thread::spawn(move || {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\n\t\t\t// a reference to a type that implements `OwnedLockable` will never\n\t\t\t// contain duplicates, so no duplicate checking is needed.\n\t\t\tlet collection = collection::RetryingLockCollection::new_ref(\u0026DATA);\n\t\t\tlet mut guard = collection.lock(key);\n\n\t\t\tlet x = *guard[1];\n\t\t\t*guard[1] += *guard[0];\n\t\t\t*guard[0] = x;\n\t\t});\n\t\tthreads.push(th);\n\t}\n\n\tfor thread in threads {\n\t\t_ = thread.join();\n\t}\n\n\tlet key = ThreadKey::get().unwrap();\n\tlet data = LockCollection::new_ref(\u0026DATA);\n\tlet data = data.lock(key);\n\tprintln!(\"{}\", data[0]);\n\tprintln!(\"{}\", data[1]);\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","examples","list.rs"],"content":"use std::thread;\n\nuse happylock::{collection::RefLockCollection, Mutex, ThreadKey};\n\nconst N: usize = 10;\n\nstatic DATA: [Mutex\u003cusize\u003e; 6] = [\n\tMutex::new(0),\n\tMutex::new(1),\n\tMutex::new(2),\n\tMutex::new(3),\n\tMutex::new(4),\n\tMutex::new(5),\n];\n\nstatic SEED: Mutex\u003cu32\u003e = Mutex::new(42);\n\nfn random(key: \u0026mut ThreadKey) -\u003e usize {\n\tSEED.scoped_lock(key, |seed| {\n\t\tlet x = *seed;\n\t\tlet x = x ^ (x \u003c\u003c 13);\n\t\tlet x = x ^ (x \u003e\u003e 17);\n\t\tlet x = x ^ (x \u003c\u003c 5);\n\t\t*seed = x;\n\t\tx as usize\n\t})\n}\n\nfn main() {\n\tlet mut threads = Vec::new();\n\tfor _ in 0..N {\n\t\tlet th = thread::spawn(move || {\n\t\t\tlet mut key = ThreadKey::get().unwrap();\n\t\t\tloop {\n\t\t\t\tlet mut data = Vec::new();\n\t\t\t\tfor _ in 0..3 {\n\t\t\t\t\tlet rand = random(\u0026mut key);\n\t\t\t\t\tdata.push(\u0026DATA[rand % 6]);\n\t\t\t\t}\n\n\t\t\t\tlet Some(lock) = RefLockCollection::try_new(\u0026data) else {\n\t\t\t\t\tcontinue;\n\t\t\t\t};\n\t\t\t\tlet mut guard = lock.lock(key);\n\t\t\t\t*guard[0] += *guard[1];\n\t\t\t\t*guard[1] += *guard[2];\n\t\t\t\t*guard[2] += *guard[0];\n\n\t\t\t\treturn;\n\t\t\t}\n\t\t});\n\t\tthreads.push(th);\n\t}\n\n\tfor th in threads {\n\t\t_ = th.join();\n\t}\n\n\tlet key = ThreadKey::get().unwrap();\n\tlet data = RefLockCollection::new(\u0026DATA);\n\tlet data = data.lock(key);\n\tfor val in \u0026*data {\n\t\tprintln!(\"{val}\");\n\t}\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","src","collection","boxed.rs"],"content":"use std::cell::UnsafeCell;\nuse std::fmt::Debug;\n\nuse crate::lockable::{Lockable, LockableIntoInner, OwnedLockable, RawLock, Sharable};\nuse crate::{Keyable, ThreadKey};\n\nuse super::utils::ordered_contains_duplicates;\nuse super::{utils, BoxedLockCollection, LockGuard};\n\nunsafe impl\u003cL: Lockable\u003e RawLock for BoxedLockCollection\u003cL\u003e {\n\t#[mutants::skip] // this should never be called\n\t#[cfg(not(tarpaulin_include))]\n\tfn poison(\u0026self) {\n\t\tfor lock in \u0026self.locks {\n\t\t\tlock.poison();\n\t\t}\n\t}\n\n\tunsafe fn raw_lock(\u0026self) {\n\t\tutils::ordered_lock(self.locks())\n\t}\n\n\tunsafe fn raw_try_lock(\u0026self) -\u003e bool {\n\t\tprintln!(\"{}\", self.locks().len());\n\t\tutils::ordered_try_lock(self.locks())\n\t}\n\n\tunsafe fn raw_unlock(\u0026self) {\n\t\tfor lock in self.locks() {\n\t\t\tlock.raw_unlock();\n\t\t}\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tutils::ordered_read(self.locks());\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tutils::ordered_try_read(self.locks())\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tfor lock in self.locks() {\n\t\t\tlock.raw_unlock_read();\n\t\t}\n\t}\n}\n\nunsafe impl\u003cL: Lockable\u003e Lockable for BoxedLockCollection\u003cL\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= L::Guard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= L::DataMut\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tptrs.extend(self.locks())\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tself.child().guard()\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.child().data_mut()\n\t}\n}\n\nunsafe impl\u003cL: Sharable\u003e Sharable for BoxedLockCollection\u003cL\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= L::ReadGuard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= L::DataRef\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tself.child().read_guard()\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tself.child().data_ref()\n\t}\n}\n\nunsafe impl\u003cL: OwnedLockable\u003e OwnedLockable for BoxedLockCollection\u003cL\u003e {}\n\n// LockableGetMut can't be implemented because that would create mutable and\n// immutable references to the same value at the same time.\n\nimpl\u003cL: LockableIntoInner\u003e LockableIntoInner for BoxedLockCollection\u003cL\u003e {\n\ttype Inner = L::Inner;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tLockableIntoInner::into_inner(self.into_child())\n\t}\n}\n\nimpl\u003cL\u003e IntoIterator for BoxedLockCollection\u003cL\u003e\nwhere\n\tL: IntoIterator,\n{\n\ttype Item = \u003cL as IntoIterator\u003e::Item;\n\ttype IntoIter = \u003cL as IntoIterator\u003e::IntoIter;\n\n\tfn into_iter(self) -\u003e Self::IntoIter {\n\t\tself.into_child().into_iter()\n\t}\n}\n\nimpl\u003c'a, L\u003e IntoIterator for \u0026'a BoxedLockCollection\u003cL\u003e\nwhere\n\t\u0026'a L: IntoIterator,\n{\n\ttype Item = \u003c\u0026'a L as IntoIterator\u003e::Item;\n\ttype IntoIter = \u003c\u0026'a L as IntoIterator\u003e::IntoIter;\n\n\tfn into_iter(self) -\u003e Self::IntoIter {\n\t\tself.child().into_iter()\n\t}\n}\n\nimpl\u003cL: OwnedLockable, I: FromIterator\u003cL\u003e + OwnedLockable\u003e FromIterator\u003cL\u003e\n\tfor BoxedLockCollection\u003cI\u003e\n{\n\tfn from_iter\u003cT: IntoIterator\u003cItem = L\u003e\u003e(iter: T) -\u003e Self {\n\t\tlet iter: I = iter.into_iter().collect();\n\t\tSelf::new(iter)\n\t}\n}\n\n// safety: the RawLocks must be send because they come from the Send Lockable\n#[allow(clippy::non_send_fields_in_send_ty)]\nunsafe impl\u003cL: Send\u003e Send for BoxedLockCollection\u003cL\u003e {}\nunsafe impl\u003cL: Sync\u003e Sync for BoxedLockCollection\u003cL\u003e {}\n\nimpl\u003cL\u003e Drop for BoxedLockCollection\u003cL\u003e {\n\t#[mutants::skip] // i can't test for a memory leak\n\t#[cfg(not(tarpaulin_include))]\n\tfn drop(\u0026mut self) {\n\t\tunsafe {\n\t\t\t// safety: this collection will never be locked again\n\t\t\tself.locks.clear();\n\t\t\t// safety: this was allocated using a box, and is now unique\n\t\t\tlet boxed: Box\u003cUnsafeCell\u003cL\u003e\u003e = Box::from_raw(self.data.cast_mut());\n\n\t\t\tdrop(boxed)\n\t\t}\n\t}\n}\n\nimpl\u003cT, L: AsRef\u003cT\u003e\u003e AsRef\u003cT\u003e for BoxedLockCollection\u003cL\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself.child().as_ref()\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cL: Debug\u003e Debug for BoxedLockCollection\u003cL\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tf.debug_struct(stringify!(BoxedLockCollection))\n\t\t\t.field(\"data\", \u0026self.data)\n\t\t\t.finish_non_exhaustive()\n\t}\n}\n\nimpl\u003cL: OwnedLockable + Default\u003e Default for BoxedLockCollection\u003cL\u003e {\n\tfn default() -\u003e Self {\n\t\tSelf::new(L::default())\n\t}\n}\n\nimpl\u003cL: OwnedLockable\u003e From\u003cL\u003e for BoxedLockCollection\u003cL\u003e {\n\tfn from(value: L) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\nimpl\u003cL\u003e BoxedLockCollection\u003cL\u003e {\n\t/// Gets the underlying collection, consuming this collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey, LockCollection};\n\t///\n\t/// let data1 = Mutex::new(42);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // data1 and data2 refer to distinct mutexes, so this won't panic\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = LockCollection::try_new(\u0026data).unwrap();\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let guard = lock.into_child().0.lock(key);\n\t/// assert_eq!(*guard, 42);\n\t/// ```\n\t#[must_use]\n\tpub fn into_child(mut self) -\u003e L {\n\t\tunsafe {\n\t\t\t// safety: this collection will never be used again\n\t\t\tstd::ptr::drop_in_place(\u0026mut self.locks);\n\t\t\t// safety: this was allocated using a box, and is now unique\n\t\t\tlet boxed: Box\u003cUnsafeCell\u003cL\u003e\u003e = Box::from_raw(self.data.cast_mut());\n\t\t\t// to prevent a double free\n\t\t\tstd::mem::forget(self);\n\n\t\t\tboxed.into_inner()\n\t\t}\n\t}\n\n\t// child_mut is immediate UB because it leads to mutable and immutable\n\t// references happening at the same time\n\n\t/// Gets an immutable reference to the underlying data\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey, LockCollection};\n\t///\n\t/// let data1 = Mutex::new(42);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // data1 and data2 refer to distinct mutexes, so this won't panic\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = LockCollection::try_new(\u0026data).unwrap();\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let guard = lock.child().0.lock(key);\n\t/// assert_eq!(*guard, 42);\n\t/// ```\n\t#[must_use]\n\tpub fn child(\u0026self) -\u003e \u0026L {\n\t\tunsafe {\n\t\t\tself.data\n\t\t\t\t.as_ref()\n\t\t\t\t.unwrap_unchecked()\n\t\t\t\t.get()\n\t\t\t\t.as_ref()\n\t\t\t\t.unwrap_unchecked()\n\t\t}\n\t}\n\n\t/// Gets the locks\n\tfn locks(\u0026self) -\u003e \u0026[\u0026dyn RawLock] {\n\t\t\u0026self.locks\n\t}\n}\n\nimpl\u003cL: OwnedLockable\u003e BoxedLockCollection\u003cL\u003e {\n\t/// Creates a new collection of owned locks.\n\t///\n\t/// Because the locks are owned, there's no need to do any checks for\n\t/// duplicate values.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t///\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t/// ```\n\t#[must_use]\n\tpub fn new(data: L) -\u003e Self {\n\t\t// safety: owned lockable types cannot contain duplicates\n\t\tunsafe { Self::new_unchecked(data) }\n\t}\n}\n\nimpl\u003c'a, L: OwnedLockable\u003e BoxedLockCollection\u003c\u0026'a L\u003e {\n\t/// Creates a new collection of owned locks.\n\t///\n\t/// Because the locks are owned, there's no need to do any checks for\n\t/// duplicate values.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t///\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = LockCollection::new_ref(\u0026data);\n\t/// ```\n\t#[must_use]\n\tpub fn new_ref(data: \u0026'a L) -\u003e Self {\n\t\t// safety: owned lockable types cannot contain duplicates\n\t\tunsafe { Self::new_unchecked(data) }\n\t}\n}\n\nimpl\u003cL: Lockable\u003e BoxedLockCollection\u003cL\u003e {\n\t/// Creates a new collections of locks.\n\t///\n\t/// # Safety\n\t///\n\t/// This results in undefined behavior if any locks are presented twice\n\t/// within this collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t///\n\t/// let data1 = Mutex::new(0);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // safety: data1 and data2 refer to distinct mutexes\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = unsafe { LockCollection::new_unchecked(\u0026data) };\n\t/// ```\n\t#[must_use]\n\tpub unsafe fn new_unchecked(data: L) -\u003e Self {\n\t\tlet data = Box::leak(Box::new(UnsafeCell::new(data)));\n\t\tlet data_ref = data.get().cast_const().as_ref().unwrap_unchecked();\n\n\t\tlet mut locks = Vec::new();\n\t\tdata_ref.get_ptrs(\u0026mut locks);\n\n\t\t// cast to *const () because fat pointers can't be converted to usize\n\t\tlocks.sort_by_key(|lock| (\u0026raw const **lock).cast::\u003c()\u003e() as usize);\n\n\t\t// safety we're just changing the lifetimes\n\t\tlet locks: Vec\u003c\u0026'static dyn RawLock\u003e = std::mem::transmute(locks);\n\t\tlet data = \u0026raw const *data;\n\t\tSelf { data, locks }\n\t}\n\n\t/// Creates a new collection of locks.\n\t///\n\t/// This returns `None` if any locks are found twice in the given\n\t/// collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t///\n\t/// let data1 = Mutex::new(0);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // data1 and data2 refer to distinct mutexes, so this won't panic\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = LockCollection::try_new(\u0026data).unwrap();\n\t/// ```\n\t#[must_use]\n\tpub fn try_new(data: L) -\u003e Option\u003cSelf\u003e {\n\t\t// safety: we are checking for duplicates before returning\n\t\tunsafe {\n\t\t\tlet this = Self::new_unchecked(data);\n\t\t\tif ordered_contains_duplicates(this.locks()) {\n\t\t\t\treturn None;\n\t\t\t}\n\t\t\tSome(this)\n\t\t}\n\t}\n\n\tpub fn scoped_lock\u003cR\u003e(\u0026self, key: impl Keyable, f: impl Fn(L::DataMut\u003c'_\u003e) -\u003e R) -\u003e R {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_lock();\n\n\t\t\t// safety: the data was just locked\n\t\t\tlet r = f(self.data_mut());\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock();\n\n\t\t\tdrop(key); // ensure the key stays alive long enough\n\n\t\t\tr\n\t\t}\n\t}\n\n\tpub fn scoped_try_lock\u003cKey: Keyable, R\u003e(\n\t\t\u0026self,\n\t\tkey: Key,\n\t\tf: impl Fn(L::DataMut\u003c'_\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_lock() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we just locked the collection\n\t\t\tlet r = f(self.data_mut());\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock();\n\n\t\t\tdrop(key); // ensures the key stays valid long enough\n\n\t\t\tOk(r)\n\t\t}\n\t}\n\n\t/// Locks the collection\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data. When the guard is dropped, the locks in the collection are also\n\t/// dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// ```\n\t#[must_use]\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::Guard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_lock();\n\n\t\t\tLockGuard {\n\t\t\t\t// safety: we've already acquired the lock\n\t\t\t\tguard: self.child().guard(),\n\t\t\t\tkey,\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Attempts to lock the without blocking.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// locks when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If any locks in the collection are already locked, then an error\n\t/// containing the given key is returned.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// match lock.try_lock(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::Guard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tlet guard = unsafe {\n\t\t\tif !self.raw_try_lock() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we've acquired the locks\n\t\t\tself.child().guard()\n\t\t};\n\n\t\tOk(LockGuard { guard, key })\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// let key = LockCollection::\u003c(Mutex\u003ci32\u003e, Mutex\u003c\u0026str\u003e)\u003e::unlock(guard);\n\t/// ```\n\tpub fn unlock(guard: LockGuard\u003cL::Guard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: Sharable\u003e BoxedLockCollection\u003cL\u003e {\n\tpub fn scoped_read\u003cR\u003e(\u0026self, key: impl Keyable, f: impl Fn(L::DataRef\u003c'_\u003e) -\u003e R) -\u003e R {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_read();\n\n\t\t\t// safety: the data was just locked\n\t\t\tlet r = f(self.data_ref());\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock_read();\n\n\t\t\tdrop(key); // ensure the key stays alive long enough\n\n\t\t\tr\n\t\t}\n\t}\n\n\tpub fn scoped_try_read\u003cKey: Keyable, R\u003e(\n\t\t\u0026self,\n\t\tkey: Key,\n\t\tf: impl Fn(L::DataRef\u003c'_\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_read() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we just locked the collection\n\t\t\tlet r = f(self.data_ref());\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock_read();\n\n\t\t\tdrop(key); // ensures the key stays valid long enough\n\n\t\t\tOk(r)\n\t\t}\n\t}\n\n\t/// Locks the collection, so that other threads can still read from it\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data immutably. When the guard is dropped, the locks in the collection\n\t/// are also dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// assert_eq!(*guard.0, 0);\n\t/// assert_eq!(*guard.1, \"\");\n\t/// ```\n\t#[must_use]\n\tpub fn read(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_read();\n\n\t\t\tLockGuard {\n\t\t\t\t// safety: we've already acquired the lock\n\t\t\t\tguard: self.child().read_guard(),\n\t\t\t\tkey,\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Attempts to lock the without blocking, in such a way that other threads\n\t/// can still read from the collection.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// shared access when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If any of the locks in the collection are already locked, then an error\n\t/// is returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(5), RwLock::new(\"6\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// match lock.try_read(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// assert_eq!(*guard.0, 5);\n\t/// assert_eq!(*guard.1, \"6\");\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_read(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tlet guard = unsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_read() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we've acquired the locks\n\t\t\tself.child().read_guard()\n\t\t};\n\n\t\tOk(LockGuard { guard, key })\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// let key = LockCollection::\u003c(RwLock\u003ci32\u003e, RwLock\u003c\u0026str\u003e)\u003e::unlock_read(guard);\n\t/// ```\n\tpub fn unlock_read(guard: LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: LockableIntoInner\u003e BoxedLockCollection\u003cL\u003e {\n\t/// Consumes this `BoxedLockCollection`, returning the underlying data.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t///\n\t/// let mutex = LockCollection::new([Mutex::new(0), Mutex::new(0)]);\n\t/// assert_eq!(mutex.into_inner(), [0, 0]);\n\t/// ```\n\t#[must_use]\n\tpub fn into_inner(self) -\u003e \u003cSelf as LockableIntoInner\u003e::Inner {\n\t\tLockableIntoInner::into_inner(self)\n\t}\n}\n\nimpl\u003c'a, L: 'a\u003e BoxedLockCollection\u003cL\u003e\nwhere\n\t\u0026'a L: IntoIterator,\n{\n\t/// Returns an iterator over references to each value in the collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = [Mutex::new(26), Mutex::new(1)];\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// let mut iter = lock.iter();\n\t/// let mutex = iter.next().unwrap();\n\t/// let guard = mutex.lock(key);\n\t///\n\t/// assert_eq!(*guard, 26);\n\t/// ```\n\t#[must_use]\n\tpub fn iter(\u0026'a self) -\u003e \u003c\u0026'a L as IntoIterator\u003e::IntoIter {\n\t\tself.into_iter()\n\t}\n}\n\n#[cfg(test)]\nmod tests {\n\tuse super::*;\n\tuse crate::{Mutex, RwLock, ThreadKey};\n\n\t#[test]\n\tfn from_iterator() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection: BoxedLockCollection\u003cVec\u003cMutex\u003c\u0026str\u003e\u003e\u003e =\n\t\t\t[Mutex::new(\"foo\"), Mutex::new(\"bar\"), Mutex::new(\"baz\")]\n\t\t\t\t.into_iter()\n\t\t\t\t.collect();\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], \"foo\");\n\t\tassert_eq!(*guard[1], \"bar\");\n\t\tassert_eq!(*guard[2], \"baz\");\n\t}\n\n\t#[test]\n\tfn from() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tBoxedLockCollection::from([Mutex::new(\"foo\"), Mutex::new(\"bar\"), Mutex::new(\"baz\")]);\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], \"foo\");\n\t\tassert_eq!(*guard[1], \"bar\");\n\t\tassert_eq!(*guard[2], \"baz\");\n\t}\n\n\t#[test]\n\tfn into_owned_iterator() {\n\t\tlet collection = BoxedLockCollection::new([Mutex::new(0), Mutex::new(1), Mutex::new(2)]);\n\t\tfor (i, mutex) in collection.into_iter().enumerate() {\n\t\t\tassert_eq!(mutex.into_inner(), i);\n\t\t}\n\t}\n\n\t#[test]\n\tfn into_ref_iterator() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::new([Mutex::new(0), Mutex::new(1), Mutex::new(2)]);\n\t\tfor (i, mutex) in (\u0026collection).into_iter().enumerate() {\n\t\t\tmutex.scoped_lock(\u0026mut key, |val| assert_eq!(*val, i))\n\t\t}\n\t}\n\n\t#[test]\n\tfn ref_iterator() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::new([Mutex::new(0), Mutex::new(1), Mutex::new(2)]);\n\t\tfor (i, mutex) in collection.iter().enumerate() {\n\t\t\tmutex.scoped_lock(\u0026mut key, |val| assert_eq!(*val, i))\n\t\t}\n\t}\n\n\t#[test]\n\t#[allow(clippy::float_cmp)]\n\tfn uses_correct_default() {\n\t\tlet collection =\n\t\t\tBoxedLockCollection::\u003c(Mutex\u003cf64\u003e, Mutex\u003cOption\u003ci32\u003e\u003e, Mutex\u003cusize\u003e)\u003e::default();\n\t\tlet tuple = collection.into_inner();\n\t\tassert_eq!(tuple.0, 0.0);\n\t\tassert!(tuple.1.is_none());\n\t\tassert_eq!(tuple.2, 0)\n\t}\n\n\t#[test]\n\tfn non_duplicates_allowed() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(1);\n\t\tassert!(BoxedLockCollection::try_new([\u0026mutex1, \u0026mutex2]).is_some())\n\t}\n\n\t#[test]\n\tfn duplicates_not_allowed() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tassert!(BoxedLockCollection::try_new([\u0026mutex1, \u0026mutex1]).is_none())\n\t}\n\n\t#[test]\n\tfn try_lock_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::new([Mutex::new(1), Mutex::new(2)]);\n\t\tlet guard = collection.try_lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_lock(key);\n\t\t\t\tassert!(guard.is_err());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn try_read_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::new([RwLock::new(1), RwLock::new(2)]);\n\t\tlet guard = collection.try_read(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_read(key);\n\t\t\t\tassert!(guard.is_ok());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn try_lock_fails_with_one_exclusive_lock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = [Mutex::new(1), Mutex::new(2)];\n\t\tlet collection = BoxedLockCollection::new_ref(\u0026locks);\n\t\tlet guard = locks[1].try_lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_lock(key);\n\t\t\t\tassert!(guard.is_err());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn try_read_fails_during_exclusive_lock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::new([RwLock::new(1), RwLock::new(2)]);\n\t\tlet guard = collection.try_lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_read(key);\n\t\t\t\tassert!(guard.is_err());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn try_read_fails_with_one_exclusive_lock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = [RwLock::new(1), RwLock::new(2)];\n\t\tlet collection = BoxedLockCollection::new_ref(\u0026locks);\n\t\tlet guard = locks[1].try_write(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_read(key);\n\t\t\t\tassert!(guard.is_err());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn unlock_collection_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex1 = Mutex::new(\"foo\");\n\t\tlet mutex2 = Mutex::new(\"bar\");\n\t\tlet collection = BoxedLockCollection::try_new((\u0026mutex1, \u0026mutex2)).unwrap();\n\t\tlet guard = collection.lock(key);\n\t\tlet key = BoxedLockCollection::\u003c(\u0026Mutex\u003c_\u003e, \u0026Mutex\u003c_\u003e)\u003e::unlock(guard);\n\n\t\tassert!(mutex1.try_lock(key).is_ok())\n\t}\n\n\t#[test]\n\tfn read_unlock_collection_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock1 = RwLock::new(\"foo\");\n\t\tlet lock2 = RwLock::new(\"bar\");\n\t\tlet collection = BoxedLockCollection::try_new((\u0026lock1, \u0026lock2)).unwrap();\n\t\tlet guard = collection.read(key);\n\t\tlet key = BoxedLockCollection::\u003c(\u0026RwLock\u003c_\u003e, \u0026RwLock\u003c_\u003e)\u003e::unlock_read(guard);\n\n\t\tassert!(lock1.try_write(key).is_ok())\n\t}\n\n\t#[test]\n\tfn into_inner_works() {\n\t\tlet collection = BoxedLockCollection::new((Mutex::new(\"Hello\"), Mutex::new(47)));\n\t\tassert_eq!(collection.into_inner(), (\"Hello\", 47))\n\t}\n\n\t#[test]\n\tfn works_in_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(1);\n\t\tlet collection =\n\t\t\tBoxedLockCollection::try_new(BoxedLockCollection::try_new([\u0026mutex1, \u0026mutex2]).unwrap())\n\t\t\t\t.unwrap();\n\n\t\tlet guard = collection.lock(key);\n\t\tassert!(mutex1.is_locked());\n\t\tassert!(mutex2.is_locked());\n\t\tdrop(guard);\n\t}\n}\n","traces":[{"line":19,"address":[230288],"length":1,"stats":{"Line":13}},{"line":20,"address":[230293],"length":1,"stats":{"Line":13}},{"line":23,"address":[658208,657792,658000],"length":1,"stats":{"Line":4}},{"line":24,"address":[658318,657809,658110,658017,658225,657902],"length":1,"stats":{"Line":8}},{"line":25,"address":[658168,657960,658376],"length":1,"stats":{"Line":4}},{"line":28,"address":[],"length":0,"stats":{"Line":0}},{"line":29,"address":[],"length":0,"stats":{"Line":0}},{"line":30,"address":[],"length":0,"stats":{"Line":0}},{"line":34,"address":[210784],"length":1,"stats":{"Line":2}},{"line":35,"address":[658421],"length":1,"stats":{"Line":2}},{"line":38,"address":[],"length":0,"stats":{"Line":3}},{"line":39,"address":[241141],"length":1,"stats":{"Line":3}},{"line":42,"address":[],"length":0,"stats":{"Line":0}},{"line":43,"address":[],"length":0,"stats":{"Line":0}},{"line":44,"address":[],"length":0,"stats":{"Line":0}},{"line":60,"address":[658512],"length":1,"stats":{"Line":1}},{"line":61,"address":[658530],"length":1,"stats":{"Line":1}},{"line":64,"address":[658560],"length":1,"stats":{"Line":1}},{"line":65,"address":[658592],"length":1,"stats":{"Line":1}},{"line":68,"address":[],"length":0,"stats":{"Line":0}},{"line":69,"address":[],"length":0,"stats":{"Line":0}},{"line":84,"address":[],"length":0,"stats":{"Line":0}},{"line":85,"address":[],"length":0,"stats":{"Line":0}},{"line":88,"address":[],"length":0,"stats":{"Line":0}},{"line":89,"address":[],"length":0,"stats":{"Line":0}},{"line":101,"address":[],"length":0,"stats":{"Line":2}},{"line":102,"address":[658702,658638],"length":1,"stats":{"Line":2}},{"line":113,"address":[],"length":0,"stats":{"Line":1}},{"line":114,"address":[],"length":0,"stats":{"Line":1}},{"line":125,"address":[658816],"length":1,"stats":{"Line":1}},{"line":126,"address":[],"length":0,"stats":{"Line":1}},{"line":133,"address":[],"length":0,"stats":{"Line":1}},{"line":134,"address":[],"length":0,"stats":{"Line":1}},{"line":135,"address":[658895],"length":1,"stats":{"Line":1}},{"line":160,"address":[],"length":0,"stats":{"Line":0}},{"line":161,"address":[],"length":0,"stats":{"Line":0}},{"line":176,"address":[658928],"length":1,"stats":{"Line":1}},{"line":177,"address":[],"length":0,"stats":{"Line":1}},{"line":182,"address":[658992],"length":1,"stats":{"Line":1}},{"line":183,"address":[659000],"length":1,"stats":{"Line":1}},{"line":207,"address":[659456,660335,659409,659423,660321,659936,659901,659887,659024],"length":1,"stats":{"Line":3}},{"line":210,"address":[659486,659966,659054],"length":1,"stats":{"Line":3}},{"line":212,"address":[],"length":0,"stats":{"Line":3}},{"line":214,"address":[],"length":0,"stats":{"Line":3}},{"line":216,"address":[660253,659341,659266,659819,660178,659698],"length":1,"stats":{"Line":3}},{"line":242,"address":[230992],"length":1,"stats":{"Line":16}},{"line":244,"address":[],"length":0,"stats":{"Line":16}},{"line":254,"address":[231072],"length":1,"stats":{"Line":23}},{"line":255,"address":[],"length":0,"stats":{"Line":23}},{"line":274,"address":[],"length":0,"stats":{"Line":13}},{"line":276,"address":[],"length":0,"stats":{"Line":13}},{"line":295,"address":[662272,662240,662304],"length":1,"stats":{"Line":3}},{"line":297,"address":[],"length":0,"stats":{"Line":3}},{"line":322,"address":[203893,203866,203424],"length":1,"stats":{"Line":27}},{"line":323,"address":[203548,203441],"length":1,"stats":{"Line":58}},{"line":324,"address":[230484],"length":1,"stats":{"Line":29}},{"line":326,"address":[211023],"length":1,"stats":{"Line":29}},{"line":327,"address":[241390],"length":1,"stats":{"Line":29}},{"line":330,"address":[152192,152202],"length":1,"stats":{"Line":77}},{"line":333,"address":[230652],"length":1,"stats":{"Line":27}},{"line":334,"address":[203803],"length":1,"stats":{"Line":29}},{"line":356,"address":[182688,182934],"length":1,"stats":{"Line":11}},{"line":359,"address":[182715],"length":1,"stats":{"Line":11}},{"line":360,"address":[204053,204114],"length":1,"stats":{"Line":22}},{"line":361,"address":[182898],"length":1,"stats":{"Line":1}},{"line":363,"address":[],"length":0,"stats":{"Line":11}},{"line":367,"address":[],"length":0,"stats":{"Line":0}},{"line":370,"address":[],"length":0,"stats":{"Line":0}},{"line":373,"address":[],"length":0,"stats":{"Line":0}},{"line":376,"address":[],"length":0,"stats":{"Line":0}},{"line":380,"address":[],"length":0,"stats":{"Line":0}},{"line":391,"address":[],"length":0,"stats":{"Line":0}},{"line":392,"address":[],"length":0,"stats":{"Line":0}},{"line":396,"address":[],"length":0,"stats":{"Line":0}},{"line":399,"address":[],"length":0,"stats":{"Line":0}},{"line":403,"address":[],"length":0,"stats":{"Line":0}},{"line":427,"address":[674117,673680,673822,673104,673856,673552,673653,673264,673070,674144,672928,674245,673518,673987,674016,673376,673357,673235],"length":1,"stats":{"Line":13}},{"line":430,"address":[],"length":0,"stats":{"Line":13}},{"line":434,"address":[673000,673928,674198,673318,673176,673752,673448,673606,674070],"length":1,"stats":{"Line":10}},{"line":469,"address":[],"length":0,"stats":{"Line":4}},{"line":471,"address":[674512,674720,674304,674558,674766,674350],"length":1,"stats":{"Line":7}},{"line":472,"address":[674361,674777,674569],"length":1,"stats":{"Line":2}},{"line":476,"address":[],"length":0,"stats":{"Line":4}},{"line":479,"address":[],"length":0,"stats":{"Line":2}},{"line":499,"address":[674896,674960,675232,675328,675168,675296,675403,675147,674992,675044,675210,675072],"length":1,"stats":{"Line":6}},{"line":500,"address":[],"length":0,"stats":{"Line":6}},{"line":501,"address":[],"length":0,"stats":{"Line":0}},{"line":506,"address":[],"length":0,"stats":{"Line":0}},{"line":509,"address":[],"length":0,"stats":{"Line":0}},{"line":512,"address":[],"length":0,"stats":{"Line":0}},{"line":515,"address":[],"length":0,"stats":{"Line":0}},{"line":519,"address":[],"length":0,"stats":{"Line":0}},{"line":530,"address":[],"length":0,"stats":{"Line":0}},{"line":531,"address":[],"length":0,"stats":{"Line":0}},{"line":535,"address":[],"length":0,"stats":{"Line":0}},{"line":538,"address":[],"length":0,"stats":{"Line":0}},{"line":542,"address":[],"length":0,"stats":{"Line":0}},{"line":566,"address":[675424,675525],"length":1,"stats":{"Line":2}},{"line":569,"address":[],"length":0,"stats":{"Line":2}},{"line":573,"address":[675478],"length":1,"stats":{"Line":1}},{"line":609,"address":[],"length":0,"stats":{"Line":3}},{"line":612,"address":[242114,242064],"length":1,"stats":{"Line":5}},{"line":613,"address":[],"length":0,"stats":{"Line":2}},{"line":617,"address":[],"length":0,"stats":{"Line":2}},{"line":620,"address":[],"length":0,"stats":{"Line":1}},{"line":638,"address":[],"length":0,"stats":{"Line":1}},{"line":639,"address":[675982],"length":1,"stats":{"Line":1}},{"line":640,"address":[],"length":0,"stats":{"Line":0}},{"line":656,"address":[],"length":0,"stats":{"Line":2}},{"line":657,"address":[],"length":0,"stats":{"Line":2}},{"line":683,"address":[],"length":0,"stats":{"Line":1}},{"line":684,"address":[],"length":0,"stats":{"Line":1}}],"covered":76,"coverable":112},{"path":["/","home","botahamec","Projects","happylock","src","collection","guard.rs"],"content":"use std::fmt::{Debug, Display};\nuse std::hash::Hash;\nuse std::ops::{Deref, DerefMut};\n\nuse super::LockGuard;\n\n#[mutants::skip] // hashing involves RNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard: Hash\u003e Hash for LockGuard\u003cGuard\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.guard.hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard: Debug\u003e Debug for LockGuard\u003cGuard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cGuard: Display\u003e Display for LockGuard\u003cGuard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cGuard\u003e Deref for LockGuard\u003cGuard\u003e {\n\ttype Target = Guard;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t\u0026self.guard\n\t}\n}\n\nimpl\u003cGuard\u003e DerefMut for LockGuard\u003cGuard\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t\u0026mut self.guard\n\t}\n}\n\nimpl\u003cGuard\u003e AsRef\u003cGuard\u003e for LockGuard\u003cGuard\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026Guard {\n\t\t\u0026self.guard\n\t}\n}\n\nimpl\u003cGuard\u003e AsMut\u003cGuard\u003e for LockGuard\u003cGuard\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut Guard {\n\t\t\u0026mut self.guard\n\t}\n}\n\n#[cfg(test)]\nmod tests {\n\tuse crate::collection::OwnedLockCollection;\n\tuse crate::{LockCollection, Mutex, RwLock, ThreadKey};\n\n\t#[test]\n\tfn guard_display_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = OwnedLockCollection::new(RwLock::new(\"Hello, world!\"));\n\t\tlet guard = lock.read(key);\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn deref_mut_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = (Mutex::new(1), Mutex::new(2));\n\t\tlet lock = LockCollection::new_ref(\u0026locks);\n\t\tlet mut guard = lock.lock(key);\n\t\t*guard.0 = 3;\n\t\tlet key = LockCollection::\u003c(Mutex\u003c_\u003e, Mutex\u003c_\u003e)\u003e::unlock(guard);\n\n\t\tlet guard = locks.0.lock(key);\n\t\tassert_eq!(*guard, 3);\n\t\tlet key = Mutex::unlock(guard);\n\n\t\tlet guard = locks.1.lock(key);\n\t\tassert_eq!(*guard, 2);\n\t}\n\n\t#[test]\n\tfn as_ref_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = (Mutex::new(1), Mutex::new(2));\n\t\tlet lock = LockCollection::new_ref(\u0026locks);\n\t\tlet mut guard = lock.lock(key);\n\t\t*guard.0 = 3;\n\t\tlet key = LockCollection::\u003c(Mutex\u003c_\u003e, Mutex\u003c_\u003e)\u003e::unlock(guard);\n\n\t\tlet guard = locks.0.lock(key);\n\t\tassert_eq!(guard.as_ref(), \u00263);\n\t\tlet key = Mutex::unlock(guard);\n\n\t\tlet guard = locks.1.lock(key);\n\t\tassert_eq!(guard.as_ref(), \u00262);\n\t}\n\n\t#[test]\n\tfn as_mut_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = (Mutex::new(1), Mutex::new(2));\n\t\tlet lock = LockCollection::new_ref(\u0026locks);\n\t\tlet mut guard = lock.lock(key);\n\t\tlet guard_mut = guard.as_mut();\n\t\t*guard_mut.0 = 3;\n\t\tlet key = LockCollection::\u003c(Mutex\u003c_\u003e, Mutex\u003c_\u003e)\u003e::unlock(guard);\n\n\t\tlet guard = locks.0.lock(key);\n\t\tassert_eq!(guard.as_ref(), \u00263);\n\t\tlet key = Mutex::unlock(guard);\n\n\t\tlet guard = locks.1.lock(key);\n\t\tassert_eq!(guard.as_ref(), \u00262);\n\t}\n}\n","traces":[{"line":24,"address":[],"length":0,"stats":{"Line":1}},{"line":25,"address":[],"length":0,"stats":{"Line":1}},{"line":32,"address":[],"length":0,"stats":{"Line":12}},{"line":33,"address":[],"length":0,"stats":{"Line":0}},{"line":38,"address":[],"length":0,"stats":{"Line":3}},{"line":39,"address":[],"length":0,"stats":{"Line":0}},{"line":44,"address":[],"length":0,"stats":{"Line":2}},{"line":45,"address":[],"length":0,"stats":{"Line":0}},{"line":50,"address":[],"length":0,"stats":{"Line":4}},{"line":51,"address":[],"length":0,"stats":{"Line":0}}],"covered":6,"coverable":10},{"path":["/","home","botahamec","Projects","happylock","src","collection","owned.rs"],"content":"use crate::lockable::{\n\tLockable, LockableGetMut, LockableIntoInner, OwnedLockable, RawLock, Sharable,\n};\nuse crate::{Keyable, ThreadKey};\n\nuse super::{utils, LockGuard, OwnedLockCollection};\n\nunsafe impl\u003cL: Lockable\u003e RawLock for OwnedLockCollection\u003cL\u003e {\n\t#[mutants::skip] // this should never run\n\t#[cfg(not(tarpaulin_include))]\n\tfn poison(\u0026self) {\n\t\tlet locks = utils::get_locks_unsorted(\u0026self.data);\n\t\tfor lock in locks {\n\t\t\tlock.poison();\n\t\t}\n\t}\n\n\tunsafe fn raw_lock(\u0026self) {\n\t\tutils::ordered_lock(\u0026utils::get_locks_unsorted(\u0026self.data))\n\t}\n\n\tunsafe fn raw_try_lock(\u0026self) -\u003e bool {\n\t\tlet locks = utils::get_locks_unsorted(\u0026self.data);\n\t\tutils::ordered_try_lock(\u0026locks)\n\t}\n\n\tunsafe fn raw_unlock(\u0026self) {\n\t\tlet locks = utils::get_locks_unsorted(\u0026self.data);\n\t\tfor lock in locks {\n\t\t\tlock.raw_unlock();\n\t\t}\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tutils::ordered_read(\u0026utils::get_locks_unsorted(\u0026self.data))\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tlet locks = utils::get_locks_unsorted(\u0026self.data);\n\t\tutils::ordered_try_read(\u0026locks)\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tlet locks = utils::get_locks_unsorted(\u0026self.data);\n\t\tfor lock in locks {\n\t\t\tlock.raw_unlock_read();\n\t\t}\n\t}\n}\n\nunsafe impl\u003cL: Lockable\u003e Lockable for OwnedLockCollection\u003cL\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= L::Guard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= L::DataMut\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\t#[mutants::skip] // It's hard to test lkocks in an OwnedLockCollection, because they're owned\n\t#[cfg(not(tarpaulin_include))]\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tself.data.get_ptrs(ptrs)\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tself.data.guard()\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.data.data_mut()\n\t}\n}\n\nimpl\u003cL: LockableGetMut\u003e LockableGetMut for OwnedLockCollection\u003cL\u003e {\n\ttype Inner\u003c'a\u003e\n\t\t= L::Inner\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tself.data.get_mut()\n\t}\n}\n\nimpl\u003cL: LockableIntoInner\u003e LockableIntoInner for OwnedLockCollection\u003cL\u003e {\n\ttype Inner = L::Inner;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tself.data.into_inner()\n\t}\n}\n\nunsafe impl\u003cL: Sharable\u003e Sharable for OwnedLockCollection\u003cL\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= L::ReadGuard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= L::DataRef\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tself.data.read_guard()\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tself.data.data_ref()\n\t}\n}\n\nunsafe impl\u003cL: OwnedLockable\u003e OwnedLockable for OwnedLockCollection\u003cL\u003e {}\n\nimpl\u003cL\u003e IntoIterator for OwnedLockCollection\u003cL\u003e\nwhere\n\tL: IntoIterator,\n{\n\ttype Item = \u003cL as IntoIterator\u003e::Item;\n\ttype IntoIter = \u003cL as IntoIterator\u003e::IntoIter;\n\n\tfn into_iter(self) -\u003e Self::IntoIter {\n\t\tself.data.into_iter()\n\t}\n}\n\nimpl\u003cL: OwnedLockable, I: FromIterator\u003cL\u003e + OwnedLockable\u003e FromIterator\u003cL\u003e\n\tfor OwnedLockCollection\u003cI\u003e\n{\n\tfn from_iter\u003cT: IntoIterator\u003cItem = L\u003e\u003e(iter: T) -\u003e Self {\n\t\tlet iter: I = iter.into_iter().collect();\n\t\tSelf::new(iter)\n\t}\n}\n\nimpl\u003cE: OwnedLockable + Extend\u003cL\u003e, L: OwnedLockable\u003e Extend\u003cL\u003e for OwnedLockCollection\u003cE\u003e {\n\tfn extend\u003cT: IntoIterator\u003cItem = L\u003e\u003e(\u0026mut self, iter: T) {\n\t\tself.data.extend(iter)\n\t}\n}\n\n// AsRef can't be implemented because an impl of AsRef\u003cL\u003e for L could break the\n// invariant that there is only one way to lock the collection. AsMut is fine,\n// because the collection can't be locked as long as the reference is valid.\n\nimpl\u003cT, L: AsMut\u003cT\u003e\u003e AsMut\u003cT\u003e for OwnedLockCollection\u003cL\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself.data.as_mut()\n\t}\n}\n\nimpl\u003cL: OwnedLockable + Default\u003e Default for OwnedLockCollection\u003cL\u003e {\n\tfn default() -\u003e Self {\n\t\tSelf::new(L::default())\n\t}\n}\n\nimpl\u003cL: OwnedLockable\u003e From\u003cL\u003e for OwnedLockCollection\u003cL\u003e {\n\tfn from(value: L) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\nimpl\u003cL: OwnedLockable\u003e OwnedLockCollection\u003cL\u003e {\n\t/// Creates a new collection of owned locks.\n\t///\n\t/// Because the locks are owned, there's no need to do any checks for\n\t/// duplicate values. The locks also don't need to be sorted by memory\n\t/// address because they aren't used anywhere else.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t/// ```\n\t#[must_use]\n\tpub const fn new(data: L) -\u003e Self {\n\t\tSelf { data }\n\t}\n\n\tpub fn scoped_lock\u003cR\u003e(\u0026self, key: impl Keyable, f: impl Fn(L::DataMut\u003c'_\u003e) -\u003e R) -\u003e R {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_lock();\n\n\t\t\t// safety: the data was just locked\n\t\t\tlet r = f(self.data_mut());\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock();\n\n\t\t\tdrop(key); // ensure the key stays alive long enough\n\n\t\t\tr\n\t\t}\n\t}\n\n\tpub fn scoped_try_lock\u003cKey: Keyable, R\u003e(\n\t\t\u0026self,\n\t\tkey: Key,\n\t\tf: impl Fn(L::DataMut\u003c'_\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_lock() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we just locked the collection\n\t\t\tlet r = f(self.data_mut());\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock();\n\n\t\t\tdrop(key); // ensures the key stays valid long enough\n\n\t\t\tOk(r)\n\t\t}\n\t}\n\n\t/// Locks the collection\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data. When the guard is dropped, the locks in the collection are also\n\t/// dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// ```\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::Guard\u003c'_\u003e\u003e {\n\t\tlet guard = unsafe {\n\t\t\t// safety: we have the thread key, and these locks happen in a\n\t\t\t// predetermined order\n\t\t\tself.raw_lock();\n\n\t\t\t// safety: we've locked all of this already\n\t\t\tself.data.guard()\n\t\t};\n\n\t\tLockGuard { guard, key }\n\t}\n\n\t/// Attempts to lock the without blocking.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// locks when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If any of the locks in this collection are already locked, this returns\n\t/// an error containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// match lock.try_lock(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::Guard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tlet guard = unsafe {\n\t\t\tif !self.raw_try_lock() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we've acquired the locks\n\t\t\tself.data.guard()\n\t\t};\n\n\t\tOk(LockGuard { guard, key })\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// let key = OwnedLockCollection::\u003c(Mutex\u003ci32\u003e, Mutex\u003c\u0026str\u003e)\u003e::unlock(guard);\n\t/// ```\n\t#[allow(clippy::missing_const_for_fn)]\n\tpub fn unlock(guard: LockGuard\u003cL::Guard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: Sharable\u003e OwnedLockCollection\u003cL\u003e {\n\tpub fn scoped_read\u003cR\u003e(\u0026self, key: impl Keyable, f: impl Fn(L::DataRef\u003c'_\u003e) -\u003e R) -\u003e R {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_read();\n\n\t\t\t// safety: the data was just locked\n\t\t\tlet r = f(self.data_ref());\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock_read();\n\n\t\t\tdrop(key); // ensure the key stays alive long enough\n\n\t\t\tr\n\t\t}\n\t}\n\n\tpub fn scoped_try_read\u003cKey: Keyable, R\u003e(\n\t\t\u0026self,\n\t\tkey: Key,\n\t\tf: impl Fn(L::DataRef\u003c'_\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_read() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we just locked the collection\n\t\t\tlet r = f(self.data_ref());\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock_read();\n\n\t\t\tdrop(key); // ensures the key stays valid long enough\n\n\t\t\tOk(r)\n\t\t}\n\t}\n\n\t/// Locks the collection, so that other threads can still read from it\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data immutably. When the guard is dropped, the locks in the collection\n\t/// are also dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// assert_eq!(*guard.0, 0);\n\t/// assert_eq!(*guard.1, \"\");\n\t/// ```\n\tpub fn read(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_read();\n\n\t\t\tLockGuard {\n\t\t\t\t// safety: we've already acquired the lock\n\t\t\t\tguard: self.data.read_guard(),\n\t\t\t\tkey,\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Attempts to lock the without blocking, in such a way that other threads\n\t/// can still read from the collection.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// shared access when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If any of the locks in this collection can't be acquired, then an error\n\t/// is returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(5), RwLock::new(\"6\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// match lock.try_read(key) {\n\t/// Some(mut guard) =\u003e {\n\t/// assert_eq!(*guard.0, 5);\n\t/// assert_eq!(*guard.1, \"6\");\n\t/// },\n\t/// None =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_read(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tlet guard = unsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_read() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we've acquired the locks\n\t\t\tself.data.read_guard()\n\t\t};\n\n\t\tOk(LockGuard { guard, key })\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// let key = OwnedLockCollection::\u003c(RwLock\u003ci32\u003e, RwLock\u003c\u0026str\u003e)\u003e::unlock_read(guard);\n\t/// ```\n\t#[allow(clippy::missing_const_for_fn)]\n\tpub fn unlock_read(guard: LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL\u003e OwnedLockCollection\u003cL\u003e {\n\t/// Gets the underlying collection, consuming this collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let data = (Mutex::new(42), Mutex::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let inner = lock.into_child();\n\t/// let guard = inner.0.lock(key);\n\t/// assert_eq!(*guard, 42);\n\t/// ```\n\t#[must_use]\n\tpub fn into_child(self) -\u003e L {\n\t\tself.data\n\t}\n\n\t/// Gets a mutable reference to the underlying collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let data = (Mutex::new(42), Mutex::new(\"\"));\n\t/// let mut lock = OwnedLockCollection::new(data);\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut inner = lock.child_mut();\n\t/// let guard = inner.0.get_mut();\n\t/// assert_eq!(*guard, 42);\n\t/// ```\n\t#[must_use]\n\tpub fn child_mut(\u0026mut self) -\u003e \u0026mut L {\n\t\t\u0026mut self.data\n\t}\n}\n\nimpl\u003cL: LockableGetMut\u003e OwnedLockCollection\u003cL\u003e {\n\t/// Gets a mutable reference to the data behind this `OwnedLockCollection`.\n\t///\n\t/// Since this call borrows the `OwnedLockCollection` mutably, no actual\n\t/// locking needs to take place - the mutable borrow statically guarantees\n\t/// no locks exist.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let mut mutex = OwnedLockCollection::new([Mutex::new(0), Mutex::new(0)]);\n\t/// assert_eq!(mutex.get_mut(), [\u0026mut 0, \u0026mut 0]);\n\t/// ```\n\tpub fn get_mut(\u0026mut self) -\u003e L::Inner\u003c'_\u003e {\n\t\tLockableGetMut::get_mut(self)\n\t}\n}\n\nimpl\u003cL: LockableIntoInner\u003e OwnedLockCollection\u003cL\u003e {\n\t/// Consumes this `OwnedLockCollection`, returning the underlying data.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let mutex = OwnedLockCollection::new([Mutex::new(0), Mutex::new(0)]);\n\t/// assert_eq!(mutex.into_inner(), [0, 0]);\n\t/// ```\n\t#[must_use]\n\tpub fn into_inner(self) -\u003e L::Inner {\n\t\tLockableIntoInner::into_inner(self)\n\t}\n}\n\n#[cfg(test)]\nmod tests {\n\tuse super::*;\n\tuse crate::{Mutex, ThreadKey};\n\n\t#[test]\n\tfn get_mut_applies_changes() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mut collection = OwnedLockCollection::new([Mutex::new(\"foo\"), Mutex::new(\"bar\")]);\n\t\tassert_eq!(*collection.get_mut()[0], \"foo\");\n\t\tassert_eq!(*collection.get_mut()[1], \"bar\");\n\t\t*collection.get_mut()[0] = \"baz\";\n\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], \"baz\");\n\t\tassert_eq!(*guard[1], \"bar\");\n\t}\n\n\t#[test]\n\tfn into_inner_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::from([Mutex::new(\"foo\")]);\n\t\tlet mut guard = collection.lock(key);\n\t\t*guard[0] = \"bar\";\n\t\tdrop(guard);\n\n\t\tlet array = collection.into_inner();\n\t\tassert_eq!(array.len(), 1);\n\t\tassert_eq!(array[0], \"bar\");\n\t}\n\n\t#[test]\n\tfn from_into_iter_is_correct() {\n\t\tlet array = [Mutex::new(0), Mutex::new(1), Mutex::new(2), Mutex::new(3)];\n\t\tlet mut collection: OwnedLockCollection\u003cVec\u003cMutex\u003cusize\u003e\u003e\u003e = array.into_iter().collect();\n\t\tassert_eq!(collection.get_mut().len(), 4);\n\t\tfor (i, lock) in collection.into_iter().enumerate() {\n\t\t\tassert_eq!(lock.into_inner(), i);\n\t\t}\n\t}\n\n\t#[test]\n\tfn from_iter_is_correct() {\n\t\tlet array = [Mutex::new(0), Mutex::new(1), Mutex::new(2), Mutex::new(3)];\n\t\tlet mut collection: OwnedLockCollection\u003cVec\u003cMutex\u003cusize\u003e\u003e\u003e = array.into_iter().collect();\n\t\tlet collection: \u0026mut Vec\u003c_\u003e = collection.as_mut();\n\t\tassert_eq!(collection.len(), 4);\n\t\tfor (i, lock) in collection.iter_mut().enumerate() {\n\t\t\tassert_eq!(*lock.get_mut(), i);\n\t\t}\n\t}\n\n\t#[test]\n\tfn try_lock_works_on_unlocked() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new((Mutex::new(0), Mutex::new(1)));\n\t\tlet guard = collection.try_lock(key).unwrap();\n\t\tassert_eq!(*guard.0, 0);\n\t\tassert_eq!(*guard.1, 1);\n\t}\n\n\t#[test]\n\tfn try_lock_fails_on_locked() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new((Mutex::new(0), Mutex::new(1)));\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\t#[allow(unused)]\n\t\t\t\tlet guard = collection.lock(key);\n\t\t\t\tstd::mem::forget(guard);\n\t\t\t});\n\t\t});\n\n\t\tassert!(collection.try_lock(key).is_err());\n\t}\n\n\t#[test]\n\tfn default_works() {\n\t\ttype MyCollection = OwnedLockCollection\u003c(Mutex\u003ci32\u003e, Mutex\u003cOption\u003ci32\u003e\u003e, Mutex\u003cString\u003e)\u003e;\n\t\tlet collection = MyCollection::default();\n\t\tlet inner = collection.into_inner();\n\t\tassert_eq!(inner.0, 0);\n\t\tassert_eq!(inner.1, None);\n\t\tassert_eq!(inner.2, String::new());\n\t}\n\n\t#[test]\n\tfn can_be_extended() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(1);\n\t\tlet mut collection = OwnedLockCollection::new(vec![mutex1, mutex2]);\n\n\t\tcollection.extend([Mutex::new(2)]);\n\n\t\tassert_eq!(collection.data.len(), 3);\n\t}\n}\n","traces":[{"line":18,"address":[],"length":0,"stats":{"Line":3}},{"line":19,"address":[676853,676597,676789,676533,676725,676661],"length":1,"stats":{"Line":6}},{"line":22,"address":[],"length":0,"stats":{"Line":1}},{"line":23,"address":[],"length":0,"stats":{"Line":1}},{"line":24,"address":[],"length":0,"stats":{"Line":2}},{"line":27,"address":[],"length":0,"stats":{"Line":0}},{"line":28,"address":[],"length":0,"stats":{"Line":0}},{"line":29,"address":[],"length":0,"stats":{"Line":0}},{"line":30,"address":[],"length":0,"stats":{"Line":0}},{"line":34,"address":[],"length":0,"stats":{"Line":1}},{"line":35,"address":[],"length":0,"stats":{"Line":2}},{"line":38,"address":[],"length":0,"stats":{"Line":0}},{"line":39,"address":[],"length":0,"stats":{"Line":0}},{"line":40,"address":[],"length":0,"stats":{"Line":0}},{"line":43,"address":[],"length":0,"stats":{"Line":0}},{"line":44,"address":[],"length":0,"stats":{"Line":0}},{"line":45,"address":[],"length":0,"stats":{"Line":0}},{"line":46,"address":[],"length":0,"stats":{"Line":0}},{"line":68,"address":[],"length":0,"stats":{"Line":0}},{"line":69,"address":[],"length":0,"stats":{"Line":0}},{"line":72,"address":[],"length":0,"stats":{"Line":0}},{"line":73,"address":[],"length":0,"stats":{"Line":0}},{"line":83,"address":[],"length":0,"stats":{"Line":2}},{"line":84,"address":[],"length":0,"stats":{"Line":2}},{"line":91,"address":[],"length":0,"stats":{"Line":2}},{"line":92,"address":[],"length":0,"stats":{"Line":2}},{"line":107,"address":[],"length":0,"stats":{"Line":0}},{"line":108,"address":[],"length":0,"stats":{"Line":0}},{"line":111,"address":[],"length":0,"stats":{"Line":0}},{"line":112,"address":[],"length":0,"stats":{"Line":0}},{"line":125,"address":[],"length":0,"stats":{"Line":1}},{"line":126,"address":[],"length":0,"stats":{"Line":1}},{"line":133,"address":[],"length":0,"stats":{"Line":2}},{"line":134,"address":[],"length":0,"stats":{"Line":2}},{"line":135,"address":[],"length":0,"stats":{"Line":2}},{"line":140,"address":[],"length":0,"stats":{"Line":1}},{"line":141,"address":[],"length":0,"stats":{"Line":1}},{"line":150,"address":[],"length":0,"stats":{"Line":1}},{"line":151,"address":[],"length":0,"stats":{"Line":1}},{"line":156,"address":[],"length":0,"stats":{"Line":1}},{"line":157,"address":[677565],"length":1,"stats":{"Line":1}},{"line":162,"address":[],"length":0,"stats":{"Line":1}},{"line":163,"address":[],"length":0,"stats":{"Line":1}},{"line":184,"address":[],"length":0,"stats":{"Line":9}},{"line":188,"address":[],"length":0,"stats":{"Line":0}},{"line":191,"address":[],"length":0,"stats":{"Line":0}},{"line":194,"address":[],"length":0,"stats":{"Line":0}},{"line":197,"address":[],"length":0,"stats":{"Line":0}},{"line":201,"address":[],"length":0,"stats":{"Line":0}},{"line":212,"address":[],"length":0,"stats":{"Line":0}},{"line":213,"address":[],"length":0,"stats":{"Line":0}},{"line":217,"address":[],"length":0,"stats":{"Line":0}},{"line":220,"address":[],"length":0,"stats":{"Line":0}},{"line":224,"address":[],"length":0,"stats":{"Line":0}},{"line":248,"address":[],"length":0,"stats":{"Line":3}},{"line":252,"address":[],"length":0,"stats":{"Line":3}},{"line":255,"address":[],"length":0,"stats":{"Line":3}},{"line":291,"address":[],"length":0,"stats":{"Line":1}},{"line":293,"address":[],"length":0,"stats":{"Line":2}},{"line":294,"address":[],"length":0,"stats":{"Line":1}},{"line":298,"address":[678351,678393],"length":1,"stats":{"Line":2}},{"line":301,"address":[],"length":0,"stats":{"Line":1}},{"line":323,"address":[],"length":0,"stats":{"Line":0}},{"line":324,"address":[],"length":0,"stats":{"Line":0}},{"line":325,"address":[],"length":0,"stats":{"Line":0}},{"line":330,"address":[],"length":0,"stats":{"Line":0}},{"line":333,"address":[],"length":0,"stats":{"Line":0}},{"line":336,"address":[],"length":0,"stats":{"Line":0}},{"line":339,"address":[],"length":0,"stats":{"Line":0}},{"line":343,"address":[],"length":0,"stats":{"Line":0}},{"line":354,"address":[],"length":0,"stats":{"Line":0}},{"line":355,"address":[],"length":0,"stats":{"Line":0}},{"line":359,"address":[],"length":0,"stats":{"Line":0}},{"line":362,"address":[],"length":0,"stats":{"Line":0}},{"line":366,"address":[],"length":0,"stats":{"Line":0}},{"line":390,"address":[],"length":0,"stats":{"Line":1}},{"line":393,"address":[],"length":0,"stats":{"Line":1}},{"line":397,"address":[],"length":0,"stats":{"Line":1}},{"line":434,"address":[],"length":0,"stats":{"Line":0}},{"line":437,"address":[],"length":0,"stats":{"Line":0}},{"line":438,"address":[],"length":0,"stats":{"Line":0}},{"line":442,"address":[],"length":0,"stats":{"Line":0}},{"line":445,"address":[],"length":0,"stats":{"Line":0}},{"line":465,"address":[],"length":0,"stats":{"Line":0}},{"line":466,"address":[],"length":0,"stats":{"Line":0}},{"line":467,"address":[],"length":0,"stats":{"Line":0}},{"line":489,"address":[],"length":0,"stats":{"Line":0}},{"line":490,"address":[],"length":0,"stats":{"Line":0}},{"line":510,"address":[],"length":0,"stats":{"Line":0}},{"line":511,"address":[],"length":0,"stats":{"Line":0}},{"line":531,"address":[],"length":0,"stats":{"Line":2}},{"line":532,"address":[],"length":0,"stats":{"Line":2}},{"line":549,"address":[],"length":0,"stats":{"Line":2}},{"line":550,"address":[],"length":0,"stats":{"Line":2}}],"covered":40,"coverable":94},{"path":["/","home","botahamec","Projects","happylock","src","collection","ref.rs"],"content":"use std::fmt::Debug;\n\nuse crate::lockable::{Lockable, OwnedLockable, RawLock, Sharable};\nuse crate::{Keyable, ThreadKey};\n\nuse super::utils::{get_locks, ordered_contains_duplicates};\nuse super::{utils, LockGuard, RefLockCollection};\n\nimpl\u003c'a, L\u003e IntoIterator for \u0026'a RefLockCollection\u003c'a, L\u003e\nwhere\n\t\u0026'a L: IntoIterator,\n{\n\ttype Item = \u003c\u0026'a L as IntoIterator\u003e::Item;\n\ttype IntoIter = \u003c\u0026'a L as IntoIterator\u003e::IntoIter;\n\n\tfn into_iter(self) -\u003e Self::IntoIter {\n\t\tself.data.into_iter()\n\t}\n}\n\nunsafe impl\u003cL: Lockable\u003e RawLock for RefLockCollection\u003c'_, L\u003e {\n\t#[mutants::skip] // this should never run\n\t#[cfg(not(tarpaulin_include))]\n\tfn poison(\u0026self) {\n\t\tfor lock in \u0026self.locks {\n\t\t\tlock.poison();\n\t\t}\n\t}\n\n\tunsafe fn raw_lock(\u0026self) {\n\t\tutils::ordered_lock(\u0026self.locks)\n\t}\n\n\tunsafe fn raw_try_lock(\u0026self) -\u003e bool {\n\t\tutils::ordered_try_lock(\u0026self.locks)\n\t}\n\n\tunsafe fn raw_unlock(\u0026self) {\n\t\tfor lock in \u0026self.locks {\n\t\t\tlock.raw_unlock();\n\t\t}\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tutils::ordered_read(\u0026self.locks)\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tutils::ordered_try_read(\u0026self.locks)\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tfor lock in \u0026self.locks {\n\t\t\tlock.raw_unlock_read();\n\t\t}\n\t}\n}\n\nunsafe impl\u003cL: Lockable\u003e Lockable for RefLockCollection\u003c'_, L\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= L::Guard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= L::DataMut\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tptrs.extend_from_slice(\u0026self.locks);\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tself.data.guard()\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.data.data_mut()\n\t}\n}\n\nunsafe impl\u003cL: Sharable\u003e Sharable for RefLockCollection\u003c'_, L\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= L::ReadGuard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= L::DataRef\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tself.data.read_guard()\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tself.data.data_ref()\n\t}\n}\n\nimpl\u003cT, L: AsRef\u003cT\u003e\u003e AsRef\u003cT\u003e for RefLockCollection\u003c'_, L\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself.data.as_ref()\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cL: Debug\u003e Debug for RefLockCollection\u003c'_, L\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tf.debug_struct(stringify!(RefLockCollection))\n\t\t\t.field(\"data\", self.data)\n\t\t\t.finish_non_exhaustive()\n\t}\n}\n\n// safety: the RawLocks must be send because they come from the Send Lockable\n#[allow(clippy::non_send_fields_in_send_ty)]\nunsafe impl\u003cL: Send\u003e Send for RefLockCollection\u003c'_, L\u003e {}\nunsafe impl\u003cL: Sync\u003e Sync for RefLockCollection\u003c'_, L\u003e {}\n\nimpl\u003c'a, L: OwnedLockable + Default\u003e From\u003c\u0026'a L\u003e for RefLockCollection\u003c'a, L\u003e {\n\tfn from(value: \u0026'a L) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\nimpl\u003c'a, L: OwnedLockable\u003e RefLockCollection\u003c'a, L\u003e {\n\t/// Creates a new collection of owned locks.\n\t///\n\t/// Because the locks are owned, there's no need to do any checks for\n\t/// duplicate values.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t/// ```\n\t#[must_use]\n\tpub fn new(data: \u0026'a L) -\u003e Self {\n\t\tRefLockCollection {\n\t\t\tlocks: get_locks(data),\n\t\t\tdata,\n\t\t}\n\t}\n}\n\nimpl\u003cL\u003e RefLockCollection\u003c'_, L\u003e {\n\t/// Gets an immutable reference to the underlying data\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let data1 = Mutex::new(42);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // data1 and data2 refer to distinct mutexes, so this won't panic\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = RefLockCollection::try_new(\u0026data).unwrap();\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let guard = lock.child().0.lock(key);\n\t/// assert_eq!(*guard, 42);\n\t/// ```\n\t#[must_use]\n\tpub const fn child(\u0026self) -\u003e \u0026L {\n\t\tself.data\n\t}\n}\n\nimpl\u003c'a, L: Lockable\u003e RefLockCollection\u003c'a, L\u003e {\n\t/// Creates a new collections of locks.\n\t///\n\t/// # Safety\n\t///\n\t/// This results in undefined behavior if any locks are presented twice\n\t/// within this collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let data1 = Mutex::new(0);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // safety: data1 and data2 refer to distinct mutexes\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = unsafe { RefLockCollection::new_unchecked(\u0026data) };\n\t/// ```\n\t#[must_use]\n\tpub unsafe fn new_unchecked(data: \u0026'a L) -\u003e Self {\n\t\tSelf {\n\t\t\tdata,\n\t\t\tlocks: get_locks(data),\n\t\t}\n\t}\n\n\t/// Creates a new collection of locks.\n\t///\n\t/// This returns `None` if any locks are found twice in the given\n\t/// collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let data1 = Mutex::new(0);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // data1 and data2 refer to distinct mutexes, so this won't panic\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = RefLockCollection::try_new(\u0026data).unwrap();\n\t/// ```\n\t#[must_use]\n\tpub fn try_new(data: \u0026'a L) -\u003e Option\u003cSelf\u003e {\n\t\tlet locks = get_locks(data);\n\t\tif ordered_contains_duplicates(\u0026locks) {\n\t\t\treturn None;\n\t\t}\n\n\t\tSome(Self { data, locks })\n\t}\n\n\tpub fn scoped_lock\u003cR\u003e(\u0026self, key: impl Keyable, f: impl Fn(L::DataMut\u003c'_\u003e) -\u003e R) -\u003e R {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_lock();\n\n\t\t\t// safety: the data was just locked\n\t\t\tlet r = f(self.data_mut());\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock();\n\n\t\t\tdrop(key); // ensure the key stays alive long enough\n\n\t\t\tr\n\t\t}\n\t}\n\n\tpub fn scoped_try_lock\u003cKey: Keyable, R\u003e(\n\t\t\u0026self,\n\t\tkey: Key,\n\t\tf: impl Fn(L::DataMut\u003c'_\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_lock() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we just locked the collection\n\t\t\tlet r = f(self.data_mut());\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock();\n\n\t\t\tdrop(key); // ensures the key stays valid long enough\n\n\t\t\tOk(r)\n\t\t}\n\t}\n\n\t/// Locks the collection\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data. When the guard is dropped, the locks in the collection are also\n\t/// dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// ```\n\t#[must_use]\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::Guard\u003c'_\u003e\u003e {\n\t\tlet guard = unsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_lock();\n\n\t\t\t// safety: we've locked all of this already\n\t\t\tself.data.guard()\n\t\t};\n\n\t\tLockGuard { guard, key }\n\t}\n\n\t/// Attempts to lock the without blocking.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// locks when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If any of the locks in the collection are already locked, then an error\n\t/// is returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// match lock.try_lock(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::Guard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tlet guard = unsafe {\n\t\t\tif !self.raw_try_lock() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we've acquired the locks\n\t\t\tself.data.guard()\n\t\t};\n\n\t\tOk(LockGuard { guard, key })\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// let key = RefLockCollection::\u003c(Mutex\u003ci32\u003e, Mutex\u003c\u0026str\u003e)\u003e::unlock(guard);\n\t/// ```\n\t#[allow(clippy::missing_const_for_fn)]\n\tpub fn unlock(guard: LockGuard\u003cL::Guard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: Sharable\u003e RefLockCollection\u003c'_, L\u003e {\n\tpub fn scoped_read\u003cR\u003e(\u0026self, key: impl Keyable, f: impl Fn(L::DataRef\u003c'_\u003e) -\u003e R) -\u003e R {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_read();\n\n\t\t\t// safety: the data was just locked\n\t\t\tlet r = f(self.data_ref());\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock_read();\n\n\t\t\tdrop(key); // ensure the key stays alive long enough\n\n\t\t\tr\n\t\t}\n\t}\n\n\tpub fn scoped_try_read\u003cKey: Keyable, R\u003e(\n\t\t\u0026self,\n\t\tkey: Key,\n\t\tf: impl Fn(L::DataRef\u003c'_\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_read() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we just locked the collection\n\t\t\tlet r = f(self.data_ref());\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock_read();\n\n\t\t\tdrop(key); // ensures the key stays valid long enough\n\n\t\t\tOk(r)\n\t\t}\n\t}\n\n\t/// Locks the collection, so that other threads can still read from it\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data immutably. When the guard is dropped, the locks in the collection\n\t/// are also dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// assert_eq!(*guard.0, 0);\n\t/// assert_eq!(*guard.1, \"\");\n\t/// ```\n\t#[must_use]\n\tpub fn read(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_read();\n\n\t\t\tLockGuard {\n\t\t\t\t// safety: we've already acquired the lock\n\t\t\t\tguard: self.data.read_guard(),\n\t\t\t\tkey,\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Attempts to lock the without blocking, in such a way that other threads\n\t/// can still read from the collection.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// shared access when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If any of the locks in the collection are already locked, then an error\n\t/// is returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(5), RwLock::new(\"6\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// match lock.try_read(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// assert_eq!(*guard.0, 5);\n\t/// assert_eq!(*guard.1, \"6\");\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_read(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tlet guard = unsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_read() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we've acquired the locks\n\t\t\tself.data.read_guard()\n\t\t};\n\n\t\tOk(LockGuard { guard, key })\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// let key = RefLockCollection::\u003c(RwLock\u003ci32\u003e, RwLock\u003c\u0026str\u003e)\u003e::unlock_read(guard);\n\t/// ```\n\t#[allow(clippy::missing_const_for_fn)]\n\tpub fn unlock_read(guard: LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003c'a, L: 'a\u003e RefLockCollection\u003c'a, L\u003e\nwhere\n\t\u0026'a L: IntoIterator,\n{\n\t/// Returns an iterator over references to each value in the collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = [Mutex::new(26), Mutex::new(1)];\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// let mut iter = lock.iter();\n\t/// let mutex = iter.next().unwrap();\n\t/// let guard = mutex.lock(key);\n\t///\n\t/// assert_eq!(*guard, 26);\n\t/// ```\n\t#[must_use]\n\tpub fn iter(\u0026'a self) -\u003e \u003c\u0026'a L as IntoIterator\u003e::IntoIter {\n\t\tself.into_iter()\n\t}\n}\n\n#[cfg(test)]\nmod tests {\n\tuse super::*;\n\tuse crate::{Mutex, RwLock, ThreadKey};\n\n\t#[test]\n\tfn non_duplicates_allowed() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(1);\n\t\tassert!(RefLockCollection::try_new(\u0026[\u0026mutex1, \u0026mutex2]).is_some())\n\t}\n\n\t#[test]\n\tfn duplicates_not_allowed() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tassert!(RefLockCollection::try_new(\u0026[\u0026mutex1, \u0026mutex1]).is_none())\n\t}\n\n\t#[test]\n\tfn try_lock_succeeds_for_unlocked_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [Mutex::new(24), Mutex::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\t\tlet guard = collection.try_lock(key).unwrap();\n\t\tassert_eq!(*guard[0], 24);\n\t\tassert_eq!(*guard[1], 42);\n\t}\n\n\t#[test]\n\tfn try_lock_fails_for_locked_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [Mutex::new(24), Mutex::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = mutexes[1].lock(key);\n\t\t\t\tassert_eq!(*guard, 42);\n\t\t\t\tstd::mem::forget(guard);\n\t\t\t});\n\t\t});\n\n\t\tlet guard = collection.try_lock(key);\n\t\tassert!(guard.is_err());\n\t}\n\n\t#[test]\n\tfn try_read_succeeds_for_unlocked_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\t\tlet guard = collection.try_read(key).unwrap();\n\t\tassert_eq!(*guard[0], 24);\n\t\tassert_eq!(*guard[1], 42);\n\t}\n\n\t#[test]\n\tfn try_read_fails_for_locked_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = mutexes[1].write(key);\n\t\t\t\tassert_eq!(*guard, 42);\n\t\t\t\tstd::mem::forget(guard);\n\t\t\t});\n\t\t});\n\n\t\tlet guard = collection.try_read(key);\n\t\tassert!(guard.is_err());\n\t}\n\n\t#[test]\n\tfn can_read_twice_on_different_threads() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.read(key);\n\t\t\t\tassert_eq!(*guard[0], 24);\n\t\t\t\tassert_eq!(*guard[1], 42);\n\t\t\t\tstd::mem::forget(guard);\n\t\t\t});\n\t\t});\n\n\t\tlet guard = collection.try_read(key).unwrap();\n\t\tassert_eq!(*guard[0], 24);\n\t\tassert_eq!(*guard[1], 42);\n\t}\n\n\t#[test]\n\tfn works_in_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(1);\n\t\tlet collection0 = [\u0026mutex1, \u0026mutex2];\n\t\tlet collection1 = RefLockCollection::try_new(\u0026collection0).unwrap();\n\t\tlet collection = RefLockCollection::try_new(\u0026collection1).unwrap();\n\n\t\tlet guard = collection.lock(key);\n\t\tassert!(mutex1.is_locked());\n\t\tassert!(mutex2.is_locked());\n\t\tdrop(guard);\n\t}\n}\n","traces":[{"line":16,"address":[],"length":0,"stats":{"Line":0}},{"line":17,"address":[],"length":0,"stats":{"Line":0}},{"line":30,"address":[],"length":0,"stats":{"Line":1}},{"line":31,"address":[],"length":0,"stats":{"Line":1}},{"line":34,"address":[678688],"length":1,"stats":{"Line":1}},{"line":35,"address":[],"length":0,"stats":{"Line":1}},{"line":38,"address":[],"length":0,"stats":{"Line":0}},{"line":39,"address":[],"length":0,"stats":{"Line":0}},{"line":40,"address":[],"length":0,"stats":{"Line":0}},{"line":44,"address":[678720],"length":1,"stats":{"Line":1}},{"line":45,"address":[],"length":0,"stats":{"Line":1}},{"line":48,"address":[678752],"length":1,"stats":{"Line":1}},{"line":49,"address":[678757],"length":1,"stats":{"Line":1}},{"line":52,"address":[],"length":0,"stats":{"Line":0}},{"line":53,"address":[],"length":0,"stats":{"Line":0}},{"line":54,"address":[],"length":0,"stats":{"Line":0}},{"line":70,"address":[678784],"length":1,"stats":{"Line":1}},{"line":71,"address":[],"length":0,"stats":{"Line":1}},{"line":74,"address":[],"length":0,"stats":{"Line":1}},{"line":75,"address":[678849],"length":1,"stats":{"Line":1}},{"line":78,"address":[],"length":0,"stats":{"Line":0}},{"line":79,"address":[],"length":0,"stats":{"Line":0}},{"line":94,"address":[],"length":0,"stats":{"Line":0}},{"line":95,"address":[],"length":0,"stats":{"Line":0}},{"line":98,"address":[],"length":0,"stats":{"Line":0}},{"line":99,"address":[],"length":0,"stats":{"Line":0}},{"line":104,"address":[],"length":0,"stats":{"Line":0}},{"line":105,"address":[],"length":0,"stats":{"Line":0}},{"line":125,"address":[],"length":0,"stats":{"Line":0}},{"line":126,"address":[],"length":0,"stats":{"Line":0}},{"line":146,"address":[],"length":0,"stats":{"Line":5}},{"line":148,"address":[],"length":0,"stats":{"Line":5}},{"line":175,"address":[],"length":0,"stats":{"Line":0}},{"line":176,"address":[],"length":0,"stats":{"Line":0}},{"line":202,"address":[],"length":0,"stats":{"Line":0}},{"line":205,"address":[],"length":0,"stats":{"Line":0}},{"line":228,"address":[679072,679372,679708,679408],"length":1,"stats":{"Line":4}},{"line":229,"address":[],"length":0,"stats":{"Line":4}},{"line":230,"address":[679122,679522,679458,679186],"length":1,"stats":{"Line":6}},{"line":231,"address":[],"length":0,"stats":{"Line":1}},{"line":234,"address":[679217,679553],"length":1,"stats":{"Line":3}},{"line":237,"address":[],"length":0,"stats":{"Line":0}},{"line":240,"address":[],"length":0,"stats":{"Line":0}},{"line":243,"address":[],"length":0,"stats":{"Line":0}},{"line":246,"address":[],"length":0,"stats":{"Line":0}},{"line":250,"address":[],"length":0,"stats":{"Line":0}},{"line":261,"address":[],"length":0,"stats":{"Line":0}},{"line":262,"address":[],"length":0,"stats":{"Line":0}},{"line":266,"address":[],"length":0,"stats":{"Line":0}},{"line":269,"address":[],"length":0,"stats":{"Line":0}},{"line":273,"address":[],"length":0,"stats":{"Line":0}},{"line":298,"address":[],"length":0,"stats":{"Line":1}},{"line":301,"address":[],"length":0,"stats":{"Line":1}},{"line":304,"address":[],"length":0,"stats":{"Line":1}},{"line":340,"address":[],"length":0,"stats":{"Line":1}},{"line":342,"address":[],"length":0,"stats":{"Line":2}},{"line":343,"address":[679974],"length":1,"stats":{"Line":1}},{"line":347,"address":[],"length":0,"stats":{"Line":1}},{"line":350,"address":[],"length":0,"stats":{"Line":1}},{"line":372,"address":[],"length":0,"stats":{"Line":0}},{"line":373,"address":[],"length":0,"stats":{"Line":0}},{"line":374,"address":[],"length":0,"stats":{"Line":0}},{"line":379,"address":[],"length":0,"stats":{"Line":0}},{"line":382,"address":[],"length":0,"stats":{"Line":0}},{"line":385,"address":[],"length":0,"stats":{"Line":0}},{"line":388,"address":[],"length":0,"stats":{"Line":0}},{"line":392,"address":[],"length":0,"stats":{"Line":0}},{"line":403,"address":[],"length":0,"stats":{"Line":0}},{"line":404,"address":[],"length":0,"stats":{"Line":0}},{"line":408,"address":[],"length":0,"stats":{"Line":0}},{"line":411,"address":[],"length":0,"stats":{"Line":0}},{"line":415,"address":[],"length":0,"stats":{"Line":0}},{"line":440,"address":[],"length":0,"stats":{"Line":1}},{"line":443,"address":[],"length":0,"stats":{"Line":1}},{"line":447,"address":[],"length":0,"stats":{"Line":1}},{"line":484,"address":[],"length":0,"stats":{"Line":1}},{"line":487,"address":[],"length":0,"stats":{"Line":2}},{"line":488,"address":[],"length":0,"stats":{"Line":1}},{"line":492,"address":[],"length":0,"stats":{"Line":1}},{"line":495,"address":[],"length":0,"stats":{"Line":1}},{"line":515,"address":[],"length":0,"stats":{"Line":0}},{"line":516,"address":[],"length":0,"stats":{"Line":0}},{"line":517,"address":[],"length":0,"stats":{"Line":0}},{"line":544,"address":[],"length":0,"stats":{"Line":0}},{"line":545,"address":[],"length":0,"stats":{"Line":0}}],"covered":35,"coverable":85},{"path":["/","home","botahamec","Projects","happylock","src","collection","retry.rs"],"content":"use std::cell::Cell;\nuse std::collections::HashSet;\n\nuse crate::collection::utils;\nuse crate::handle_unwind::handle_unwind;\nuse crate::lockable::{\n\tLockable, LockableGetMut, LockableIntoInner, OwnedLockable, RawLock, Sharable,\n};\nuse crate::{Keyable, ThreadKey};\n\nuse super::utils::{\n\tattempt_to_recover_locks_from_panic, attempt_to_recover_reads_from_panic, get_locks_unsorted,\n};\nuse super::{LockGuard, RetryingLockCollection};\n\n/// Checks that a collection contains no duplicate references to a lock.\nfn contains_duplicates\u003cL: Lockable\u003e(data: L) -\u003e bool {\n\tlet mut locks = Vec::new();\n\tdata.get_ptrs(\u0026mut locks);\n\t// cast to *const () so that the v-table pointers are not used for hashing\n\tlet locks = locks.into_iter().map(|l| (\u0026raw const *l).cast::\u003c()\u003e());\n\n\tlet mut locks_set = HashSet::with_capacity(locks.len());\n\tfor lock in locks {\n\t\tif !locks_set.insert(lock) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tfalse\n}\n\nunsafe impl\u003cL: Lockable\u003e RawLock for RetryingLockCollection\u003cL\u003e {\n\t#[mutants::skip] // this should never run\n\t#[cfg(not(tarpaulin_include))]\n\tfn poison(\u0026self) {\n\t\tlet locks = get_locks_unsorted(\u0026self.data);\n\t\tfor lock in locks {\n\t\t\tlock.poison();\n\t\t}\n\t}\n\n\tunsafe fn raw_lock(\u0026self) {\n\t\tlet locks = get_locks_unsorted(\u0026self.data);\n\n\t\tif locks.is_empty() {\n\t\t\t// this probably prevents a panic later\n\t\t\treturn;\n\t\t}\n\n\t\t// these will be unlocked in case of a panic\n\t\tlet first_index = Cell::new(0);\n\t\tlet locked = Cell::new(0);\n\t\thandle_unwind(\n\t\t\t|| unsafe {\n\t\t\t\t'outer: loop {\n\t\t\t\t\t// This prevents us from entering a spin loop waiting for\n\t\t\t\t\t// the same lock to be unlocked\n\t\t\t\t\t// safety: we have the thread key\n\t\t\t\t\tlocks[first_index.get()].raw_lock();\n\t\t\t\t\tfor (i, lock) in locks.iter().enumerate() {\n\t\t\t\t\t\tif i == first_index.get() {\n\t\t\t\t\t\t\t// we've already locked this one\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// If the lock has been killed, then this returns false\n\t\t\t\t\t\t// instead of panicking. This sounds like a problem, but if\n\t\t\t\t\t\t// it does return false, then the lock function is called\n\t\t\t\t\t\t// immediately after, causing a panic\n\t\t\t\t\t\t// safety: we have the thread key\n\t\t\t\t\t\tif lock.raw_try_lock() {\n\t\t\t\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// safety: we already locked all of these\n\t\t\t\t\t\t\tattempt_to_recover_locks_from_panic(\u0026locks[0..i]);\n\t\t\t\t\t\t\tif first_index.get() \u003e= i {\n\t\t\t\t\t\t\t\t// safety: this is already locked and can't be\n\t\t\t\t\t\t\t\t// unlocked by the previous loop\n\t\t\t\t\t\t\t\tlocks[first_index.get()].raw_unlock();\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// nothing is locked anymore\n\t\t\t\t\t\t\tlocked.set(0);\n\n\t\t\t\t\t\t\t// call lock on this to prevent a spin loop\n\t\t\t\t\t\t\tfirst_index.set(i);\n\t\t\t\t\t\t\tcontinue 'outer;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// safety: we locked all the data\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t},\n\t\t\t|| {\n\t\t\t\tutils::attempt_to_recover_locks_from_panic(\u0026locks[0..locked.get()]);\n\t\t\t\tif first_index.get() \u003e= locked.get() {\n\t\t\t\t\tlocks[first_index.get()].raw_unlock();\n\t\t\t\t}\n\t\t\t},\n\t\t)\n\t}\n\n\tunsafe fn raw_try_lock(\u0026self) -\u003e bool {\n\t\tlet locks = get_locks_unsorted(\u0026self.data);\n\n\t\tif locks.is_empty() {\n\t\t\t// this is an interesting case, but it doesn't give us access to\n\t\t\t// any data, and can't possibly cause a deadlock\n\t\t\treturn true;\n\t\t}\n\n\t\t// these will be unlocked in case of a panic\n\t\tlet locked = Cell::new(0);\n\t\thandle_unwind(\n\t\t\t|| unsafe {\n\t\t\t\tfor (i, lock) in locks.iter().enumerate() {\n\t\t\t\t\t// safety: we have the thread key\n\t\t\t\t\tif lock.raw_try_lock() {\n\t\t\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// safety: we already locked all of these\n\t\t\t\t\t\tattempt_to_recover_locks_from_panic(\u0026locks[0..i]);\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ttrue\n\t\t\t},\n\t\t\t|| utils::attempt_to_recover_locks_from_panic(\u0026locks[0..locked.get()]),\n\t\t)\n\t}\n\n\tunsafe fn raw_unlock(\u0026self) {\n\t\tlet locks = get_locks_unsorted(\u0026self.data);\n\n\t\tfor lock in locks {\n\t\t\tlock.raw_unlock();\n\t\t}\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tlet locks = get_locks_unsorted(\u0026self.data);\n\n\t\tif locks.is_empty() {\n\t\t\t// this probably prevents a panic later\n\t\t\treturn;\n\t\t}\n\n\t\tlet locked = Cell::new(0);\n\t\tlet first_index = Cell::new(0);\n\t\thandle_unwind(\n\t\t\t|| 'outer: loop {\n\t\t\t\t// safety: we have the thread key\n\t\t\t\tlocks[first_index.get()].raw_read();\n\t\t\t\tfor (i, lock) in locks.iter().enumerate() {\n\t\t\t\t\tif i == first_index.get() {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// safety: we have the thread key\n\t\t\t\t\tif lock.raw_try_read() {\n\t\t\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// safety: we already locked all of these\n\t\t\t\t\t\tattempt_to_recover_reads_from_panic(\u0026locks[0..i]);\n\n\t\t\t\t\t\tif first_index.get() \u003e= i {\n\t\t\t\t\t\t\t// safety: this is already locked and can't be unlocked\n\t\t\t\t\t\t\t// by the previous loop\n\t\t\t\t\t\t\tlocks[first_index.get()].raw_unlock_read();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// these are no longer locked\n\t\t\t\t\t\tlocked.set(0);\n\n\t\t\t\t\t\t// don't go into a spin loop, wait for this one to lock\n\t\t\t\t\t\tfirst_index.set(i);\n\t\t\t\t\t\tcontinue 'outer;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// safety: we locked all the data\n\t\t\t\tbreak;\n\t\t\t},\n\t\t\t|| {\n\t\t\t\tutils::attempt_to_recover_reads_from_panic(\u0026locks[0..locked.get()]);\n\t\t\t\tif first_index.get() \u003e= locked.get() {\n\t\t\t\t\tlocks[first_index.get()].raw_unlock_read();\n\t\t\t\t}\n\t\t\t},\n\t\t)\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tlet locks = get_locks_unsorted(\u0026self.data);\n\n\t\tif locks.is_empty() {\n\t\t\t// this is an interesting case, but it doesn't give us access to\n\t\t\t// any data, and can't possibly cause a deadlock\n\t\t\treturn true;\n\t\t}\n\n\t\tlet locked = Cell::new(0);\n\t\thandle_unwind(\n\t\t\t|| unsafe {\n\t\t\t\tfor (i, lock) in locks.iter().enumerate() {\n\t\t\t\t\t// safety: we have the thread key\n\t\t\t\t\tif lock.raw_try_read() {\n\t\t\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// safety: we already locked all of these\n\t\t\t\t\t\tattempt_to_recover_reads_from_panic(\u0026locks[0..i]);\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ttrue\n\t\t\t},\n\t\t\t|| utils::attempt_to_recover_reads_from_panic(\u0026locks[0..locked.get()]),\n\t\t)\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tlet locks = get_locks_unsorted(\u0026self.data);\n\n\t\tfor lock in locks {\n\t\t\tlock.raw_unlock_read();\n\t\t}\n\t}\n}\n\nunsafe impl\u003cL: Lockable\u003e Lockable for RetryingLockCollection\u003cL\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= L::Guard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= L::DataMut\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tself.data.get_ptrs(ptrs)\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tself.data.guard()\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.data.data_mut()\n\t}\n}\n\nunsafe impl\u003cL: Sharable\u003e Sharable for RetryingLockCollection\u003cL\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= L::ReadGuard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= L::DataRef\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tself.data.read_guard()\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tself.data.data_ref()\n\t}\n}\n\nunsafe impl\u003cL: OwnedLockable\u003e OwnedLockable for RetryingLockCollection\u003cL\u003e {}\n\nimpl\u003cL: LockableGetMut\u003e LockableGetMut for RetryingLockCollection\u003cL\u003e {\n\ttype Inner\u003c'a\u003e\n\t\t= L::Inner\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tself.data.get_mut()\n\t}\n}\n\nimpl\u003cL: LockableIntoInner\u003e LockableIntoInner for RetryingLockCollection\u003cL\u003e {\n\ttype Inner = L::Inner;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tself.data.into_inner()\n\t}\n}\n\nimpl\u003cL\u003e IntoIterator for RetryingLockCollection\u003cL\u003e\nwhere\n\tL: IntoIterator,\n{\n\ttype Item = \u003cL as IntoIterator\u003e::Item;\n\ttype IntoIter = \u003cL as IntoIterator\u003e::IntoIter;\n\n\tfn into_iter(self) -\u003e Self::IntoIter {\n\t\tself.data.into_iter()\n\t}\n}\n\nimpl\u003c'a, L\u003e IntoIterator for \u0026'a RetryingLockCollection\u003cL\u003e\nwhere\n\t\u0026'a L: IntoIterator,\n{\n\ttype Item = \u003c\u0026'a L as IntoIterator\u003e::Item;\n\ttype IntoIter = \u003c\u0026'a L as IntoIterator\u003e::IntoIter;\n\n\tfn into_iter(self) -\u003e Self::IntoIter {\n\t\tself.data.into_iter()\n\t}\n}\n\nimpl\u003c'a, L\u003e IntoIterator for \u0026'a mut RetryingLockCollection\u003cL\u003e\nwhere\n\t\u0026'a mut L: IntoIterator,\n{\n\ttype Item = \u003c\u0026'a mut L as IntoIterator\u003e::Item;\n\ttype IntoIter = \u003c\u0026'a mut L as IntoIterator\u003e::IntoIter;\n\n\tfn into_iter(self) -\u003e Self::IntoIter {\n\t\tself.data.into_iter()\n\t}\n}\n\nimpl\u003cL: OwnedLockable, I: FromIterator\u003cL\u003e + OwnedLockable\u003e FromIterator\u003cL\u003e\n\tfor RetryingLockCollection\u003cI\u003e\n{\n\tfn from_iter\u003cT: IntoIterator\u003cItem = L\u003e\u003e(iter: T) -\u003e Self {\n\t\tlet iter: I = iter.into_iter().collect();\n\t\tSelf::new(iter)\n\t}\n}\n\nimpl\u003cE: OwnedLockable + Extend\u003cL\u003e, L: OwnedLockable\u003e Extend\u003cL\u003e for RetryingLockCollection\u003cE\u003e {\n\tfn extend\u003cT: IntoIterator\u003cItem = L\u003e\u003e(\u0026mut self, iter: T) {\n\t\tself.data.extend(iter)\n\t}\n}\n\nimpl\u003cT, L: AsRef\u003cT\u003e\u003e AsRef\u003cT\u003e for RetryingLockCollection\u003cL\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself.data.as_ref()\n\t}\n}\n\nimpl\u003cT, L: AsMut\u003cT\u003e\u003e AsMut\u003cT\u003e for RetryingLockCollection\u003cL\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself.data.as_mut()\n\t}\n}\n\nimpl\u003cL: OwnedLockable + Default\u003e Default for RetryingLockCollection\u003cL\u003e {\n\tfn default() -\u003e Self {\n\t\tSelf::new(L::default())\n\t}\n}\n\nimpl\u003cL: OwnedLockable\u003e From\u003cL\u003e for RetryingLockCollection\u003cL\u003e {\n\tfn from(value: L) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\nimpl\u003cL: OwnedLockable\u003e RetryingLockCollection\u003cL\u003e {\n\t/// Creates a new collection of owned locks.\n\t///\n\t/// Because the locks are owned, there's no need to do any checks for\n\t/// duplicate values. The locks also don't need to be sorted by memory\n\t/// address because they aren't used anywhere else.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t/// ```\n\t#[must_use]\n\tpub const fn new(data: L) -\u003e Self {\n\t\tSelf { data }\n\t}\n}\n\nimpl\u003c'a, L: OwnedLockable\u003e RetryingLockCollection\u003c\u0026'a L\u003e {\n\t/// Creates a new collection of owned locks.\n\t///\n\t/// Because the locks are owned, there's no need to do any checks for\n\t/// duplicate values.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new_ref(\u0026data);\n\t/// ```\n\t#[must_use]\n\tpub const fn new_ref(data: \u0026'a L) -\u003e Self {\n\t\tSelf { data }\n\t}\n}\n\nimpl\u003cL\u003e RetryingLockCollection\u003cL\u003e {\n\t/// Creates a new collections of locks.\n\t///\n\t/// # Safety\n\t///\n\t/// This results in undefined behavior if any locks are presented twice\n\t/// within this collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let data1 = Mutex::new(0);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // safety: data1 and data2 refer to distinct mutexes\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = unsafe { RetryingLockCollection::new_unchecked(\u0026data) };\n\t/// ```\n\t#[must_use]\n\tpub const unsafe fn new_unchecked(data: L) -\u003e Self {\n\t\tSelf { data }\n\t}\n\n\t/// Gets an immutable reference to the underlying collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let data = (Mutex::new(42), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let inner = lock.child();\n\t/// let guard = inner.0.lock(key);\n\t/// assert_eq!(*guard, 42);\n\t/// ```\n\t#[must_use]\n\tpub const fn child(\u0026self) -\u003e \u0026L {\n\t\t\u0026self.data\n\t}\n\n\t/// Gets a mutable reference to the underlying collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let data = (Mutex::new(42), Mutex::new(\"\"));\n\t/// let mut lock = RetryingLockCollection::new(data);\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut inner = lock.child_mut();\n\t/// let guard = inner.0.get_mut();\n\t/// assert_eq!(*guard, 42);\n\t/// ```\n\t#[must_use]\n\tpub fn child_mut(\u0026mut self) -\u003e \u0026mut L {\n\t\t\u0026mut self.data\n\t}\n\n\t/// Gets the underlying collection, consuming this collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let data = (Mutex::new(42), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let inner = lock.into_child();\n\t/// let guard = inner.0.lock(key);\n\t/// assert_eq!(*guard, 42);\n\t/// ```\n\t#[must_use]\n\tpub fn into_child(self) -\u003e L {\n\t\tself.data\n\t}\n}\n\nimpl\u003cL: Lockable\u003e RetryingLockCollection\u003cL\u003e {\n\t/// Creates a new collection of locks.\n\t///\n\t/// This returns `None` if any locks are found twice in the given\n\t/// collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let data1 = Mutex::new(0);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // data1 and data2 refer to distinct mutexes, so this won't panic\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = RetryingLockCollection::try_new(\u0026data).unwrap();\n\t/// ```\n\t#[must_use]\n\tpub fn try_new(data: L) -\u003e Option\u003cSelf\u003e {\n\t\t(!contains_duplicates(\u0026data)).then_some(Self { data })\n\t}\n\n\tpub fn scoped_lock\u003cR\u003e(\u0026self, key: impl Keyable, f: impl Fn(L::DataMut\u003c'_\u003e) -\u003e R) -\u003e R {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_lock();\n\n\t\t\t// safety: the data was just locked\n\t\t\tlet r = f(self.data_mut());\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock();\n\n\t\t\tdrop(key); // ensure the key stays alive long enough\n\n\t\t\tr\n\t\t}\n\t}\n\n\tpub fn scoped_try_lock\u003cKey: Keyable, R\u003e(\n\t\t\u0026self,\n\t\tkey: Key,\n\t\tf: impl Fn(L::DataMut\u003c'_\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_lock() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we just locked the collection\n\t\t\tlet r = f(self.data_mut());\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock();\n\n\t\t\tdrop(key); // ensures the key stays valid long enough\n\n\t\t\tOk(r)\n\t\t}\n\t}\n\n\t/// Locks the collection\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data. When the guard is dropped, the locks in the collection are also\n\t/// dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// ```\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::Guard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\t// safety: we're taking the thread key\n\t\t\tself.raw_lock();\n\n\t\t\tLockGuard {\n\t\t\t\t// safety: we just locked the collection\n\t\t\t\tguard: self.guard(),\n\t\t\t\tkey,\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Attempts to lock the without blocking.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// locks when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If any of the locks in the collection are already locked, then an error\n\t/// is returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// match lock.try_lock(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::Guard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tunsafe {\n\t\t\t// safety: we're taking the thread key\n\t\t\tif self.raw_try_lock() {\n\t\t\t\tOk(LockGuard {\n\t\t\t\t\t// safety: we just succeeded in locking everything\n\t\t\t\t\tguard: self.guard(),\n\t\t\t\t\tkey,\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\tErr(key)\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// let key = RetryingLockCollection::\u003c(Mutex\u003ci32\u003e, Mutex\u003c\u0026str\u003e)\u003e::unlock(guard);\n\t/// ```\n\tpub fn unlock(guard: LockGuard\u003cL::Guard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: Sharable\u003e RetryingLockCollection\u003cL\u003e {\n\tpub fn scoped_read\u003cR\u003e(\u0026self, key: impl Keyable, f: impl Fn(L::DataRef\u003c'_\u003e) -\u003e R) -\u003e R {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_read();\n\n\t\t\t// safety: the data was just locked\n\t\t\tlet r = f(self.data_ref());\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock_read();\n\n\t\t\tdrop(key); // ensure the key stays alive long enough\n\n\t\t\tr\n\t\t}\n\t}\n\n\tpub fn scoped_try_read\u003cKey: Keyable, R\u003e(\n\t\t\u0026self,\n\t\tkey: Key,\n\t\tf: impl Fn(L::DataRef\u003c'_\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_read() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we just locked the collection\n\t\t\tlet r = f(self.data_ref());\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock_read();\n\n\t\t\tdrop(key); // ensures the key stays valid long enough\n\n\t\t\tOk(r)\n\t\t}\n\t}\n\n\t/// Locks the collection, so that other threads can still read from it\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data immutably. When the guard is dropped, the locks in the collection\n\t/// are also dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// assert_eq!(*guard.0, 0);\n\t/// assert_eq!(*guard.1, \"\");\n\t/// ```\n\tpub fn read(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\t// safety: we're taking the thread key\n\t\t\tself.raw_read();\n\n\t\t\tLockGuard {\n\t\t\t\t// safety: we just locked the collection\n\t\t\t\tguard: self.read_guard(),\n\t\t\t\tkey,\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Attempts to lock the without blocking, in such a way that other threads\n\t/// can still read from the collection.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// shared access when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If shared access cannot be acquired at this time, then an error is\n\t/// returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(5), RwLock::new(\"6\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// match lock.try_read(key) {\n\t/// Some(mut guard) =\u003e {\n\t/// assert_eq!(*guard.0, 5);\n\t/// assert_eq!(*guard.1, \"6\");\n\t/// },\n\t/// None =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_read(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tunsafe {\n\t\t\t// safety: we're taking the thread key\n\t\t\tif !self.raw_try_lock() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\tOk(LockGuard {\n\t\t\t\t// safety: we just succeeded in locking everything\n\t\t\t\tguard: self.read_guard(),\n\t\t\t\tkey,\n\t\t\t})\n\t\t}\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// let key = RetryingLockCollection::\u003c(RwLock\u003ci32\u003e, RwLock\u003c\u0026str\u003e)\u003e::unlock_read(guard);\n\t/// ```\n\tpub fn unlock_read(guard: LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: LockableGetMut\u003e RetryingLockCollection\u003cL\u003e {\n\t/// Gets a mutable reference to the data behind this\n\t/// `RetryingLockCollection`.\n\t///\n\t/// Since this call borrows the `RetryingLockCollection` mutably, no actual\n\t/// locking needs to take place - the mutable borrow statically guarantees\n\t/// no locks exist.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let mut mutex = RetryingLockCollection::new([Mutex::new(0), Mutex::new(0)]);\n\t/// assert_eq!(mutex.get_mut(), [\u0026mut 0, \u0026mut 0]);\n\t/// ```\n\tpub fn get_mut(\u0026mut self) -\u003e L::Inner\u003c'_\u003e {\n\t\tLockableGetMut::get_mut(self)\n\t}\n}\n\nimpl\u003cL: LockableIntoInner\u003e RetryingLockCollection\u003cL\u003e {\n\t/// Consumes this `RetryingLockCollection`, returning the underlying data.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let mutex = RetryingLockCollection::new([Mutex::new(0), Mutex::new(0)]);\n\t/// assert_eq!(mutex.into_inner(), [0, 0]);\n\t/// ```\n\tpub fn into_inner(self) -\u003e L::Inner {\n\t\tLockableIntoInner::into_inner(self)\n\t}\n}\n\nimpl\u003c'a, L: 'a\u003e RetryingLockCollection\u003cL\u003e\nwhere\n\t\u0026'a L: IntoIterator,\n{\n\t/// Returns an iterator over references to each value in the collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = [Mutex::new(26), Mutex::new(1)];\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let mut iter = lock.iter();\n\t/// let mutex = iter.next().unwrap();\n\t/// let guard = mutex.lock(key);\n\t///\n\t/// assert_eq!(*guard, 26);\n\t/// ```\n\t#[must_use]\n\tpub fn iter(\u0026'a self) -\u003e \u003c\u0026'a L as IntoIterator\u003e::IntoIter {\n\t\tself.into_iter()\n\t}\n}\n\nimpl\u003c'a, L: 'a\u003e RetryingLockCollection\u003cL\u003e\nwhere\n\t\u0026'a mut L: IntoIterator,\n{\n\t/// Returns an iterator over mutable references to each value in the\n\t/// collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = [Mutex::new(26), Mutex::new(1)];\n\t/// let mut lock = RetryingLockCollection::new(data);\n\t///\n\t/// let mut iter = lock.iter_mut();\n\t/// let mutex = iter.next().unwrap();\n\t///\n\t/// assert_eq!(*mutex.as_mut(), 26);\n\t/// ```\n\t#[must_use]\n\tpub fn iter_mut(\u0026'a mut self) -\u003e \u003c\u0026'a mut L as IntoIterator\u003e::IntoIter {\n\t\tself.into_iter()\n\t}\n}\n\n#[cfg(test)]\nmod tests {\n\tuse super::*;\n\tuse crate::collection::BoxedLockCollection;\n\tuse crate::{LockCollection, Mutex, RwLock, ThreadKey};\n\n\t#[test]\n\tfn nonduplicate_lock_references_are_allowed() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(0);\n\t\tassert!(RetryingLockCollection::try_new([\u0026mutex1, \u0026mutex2]).is_some());\n\t}\n\n\t#[test]\n\tfn duplicate_lock_references_are_disallowed() {\n\t\tlet mutex = Mutex::new(0);\n\t\tassert!(RetryingLockCollection::try_new([\u0026mutex, \u0026mutex]).is_none());\n\t}\n\n\t#[test]\n\tfn locks_all_inner_mutexes() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(0);\n\t\tlet collection = RetryingLockCollection::try_new([\u0026mutex1, \u0026mutex2]).unwrap();\n\n\t\tlet guard = collection.lock(key);\n\n\t\tassert!(mutex1.is_locked());\n\t\tassert!(mutex2.is_locked());\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn locks_all_inner_rwlocks() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet rwlock1 = RwLock::new(0);\n\t\tlet rwlock2 = RwLock::new(0);\n\t\tlet collection = RetryingLockCollection::try_new([\u0026rwlock1, \u0026rwlock2]).unwrap();\n\n\t\tlet guard = collection.read(key);\n\n\t\tassert!(rwlock1.is_locked());\n\t\tassert!(rwlock2.is_locked());\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn works_with_other_collections() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(0);\n\t\tlet collection = BoxedLockCollection::try_new(\n\t\t\tRetryingLockCollection::try_new([\u0026mutex1, \u0026mutex2]).unwrap(),\n\t\t)\n\t\t.unwrap();\n\n\t\tlet guard = collection.lock(key);\n\n\t\tassert!(mutex1.is_locked());\n\t\tassert!(mutex2.is_locked());\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn extend_collection() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(0);\n\t\tlet mut collection = RetryingLockCollection::new(vec![mutex1]);\n\n\t\tcollection.extend([mutex2]);\n\n\t\tassert_eq!(collection.into_inner().len(), 2);\n\t}\n\n\t#[test]\n\tfn lock_empty_lock_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection: RetryingLockCollection\u003c[RwLock\u003ci32\u003e; 0]\u003e = RetryingLockCollection::new([]);\n\n\t\tlet guard = collection.lock(key);\n\t\tassert!(guard.len() == 0);\n\t\tlet key = LockCollection::\u003c[RwLock\u003c_\u003e; 0]\u003e::unlock(guard);\n\n\t\tlet guard = collection.read(key);\n\t\tassert!(guard.len() == 0);\n\t}\n}\n","traces":[{"line":17,"address":[205520,206245,205560],"length":1,"stats":{"Line":12}},{"line":18,"address":[],"length":0,"stats":{"Line":12}},{"line":19,"address":[202763],"length":1,"stats":{"Line":12}},{"line":21,"address":[153450,152682,154080,154140,154108,154112],"length":1,"stats":{"Line":36}},{"line":23,"address":[171936,171870],"length":1,"stats":{"Line":24}},{"line":24,"address":[204104,204007,204285,204243],"length":1,"stats":{"Line":47}},{"line":25,"address":[243189,243254],"length":1,"stats":{"Line":24}},{"line":26,"address":[186016],"length":1,"stats":{"Line":1}},{"line":30,"address":[176512],"length":1,"stats":{"Line":11}},{"line":43,"address":[],"length":0,"stats":{"Line":6}},{"line":44,"address":[],"length":0,"stats":{"Line":6}},{"line":46,"address":[],"length":0,"stats":{"Line":12}},{"line":48,"address":[],"length":0,"stats":{"Line":0}},{"line":52,"address":[213424,213461],"length":1,"stats":{"Line":10}},{"line":53,"address":[231498],"length":1,"stats":{"Line":5}},{"line":55,"address":[],"length":0,"stats":{"Line":10}},{"line":56,"address":[],"length":0,"stats":{"Line":0}},{"line":60,"address":[242049],"length":1,"stats":{"Line":5}},{"line":61,"address":[152054,152206],"length":1,"stats":{"Line":10}},{"line":62,"address":[242296],"length":1,"stats":{"Line":5}},{"line":64,"address":[],"length":0,"stats":{"Line":0}},{"line":72,"address":[242328],"length":1,"stats":{"Line":5}},{"line":73,"address":[544522,544666,544106,543962],"length":1,"stats":{"Line":8}},{"line":76,"address":[185104],"length":1,"stats":{"Line":1}},{"line":77,"address":[205316],"length":1,"stats":{"Line":1}},{"line":80,"address":[185251],"length":1,"stats":{"Line":1}},{"line":84,"address":[152407],"length":1,"stats":{"Line":1}},{"line":87,"address":[152431],"length":1,"stats":{"Line":1}},{"line":88,"address":[],"length":0,"stats":{"Line":0}},{"line":93,"address":[],"length":0,"stats":{"Line":0}},{"line":96,"address":[151808],"length":1,"stats":{"Line":6}},{"line":97,"address":[241865],"length":1,"stats":{"Line":1}},{"line":98,"address":[],"length":0,"stats":{"Line":1}},{"line":99,"address":[204905],"length":1,"stats":{"Line":1}},{"line":105,"address":[],"length":0,"stats":{"Line":3}},{"line":106,"address":[204566],"length":1,"stats":{"Line":3}},{"line":108,"address":[],"length":0,"stats":{"Line":6}},{"line":111,"address":[],"length":0,"stats":{"Line":0}},{"line":115,"address":[],"length":0,"stats":{"Line":6}},{"line":117,"address":[],"length":0,"stats":{"Line":3}},{"line":118,"address":[151535,151393],"length":1,"stats":{"Line":6}},{"line":120,"address":[175745],"length":1,"stats":{"Line":3}},{"line":121,"address":[],"length":0,"stats":{"Line":3}},{"line":124,"address":[202484],"length":1,"stats":{"Line":1}},{"line":125,"address":[],"length":0,"stats":{"Line":1}},{"line":129,"address":[151528],"length":1,"stats":{"Line":0}},{"line":131,"address":[175486,175472],"length":1,"stats":{"Line":4}},{"line":135,"address":[155680,155408,155925,155653],"length":1,"stats":{"Line":1}},{"line":136,"address":[155426,155698],"length":1,"stats":{"Line":1}},{"line":138,"address":[155560,155436,155886,155708,155832,155614],"length":1,"stats":{"Line":3}},{"line":139,"address":[],"length":0,"stats":{"Line":1}},{"line":143,"address":[151544,151296],"length":1,"stats":{"Line":4}},{"line":144,"address":[151324],"length":1,"stats":{"Line":4}},{"line":146,"address":[],"length":0,"stats":{"Line":8}},{"line":148,"address":[],"length":0,"stats":{"Line":0}},{"line":151,"address":[681093,681056,681365,681328],"length":1,"stats":{"Line":6}},{"line":152,"address":[],"length":0,"stats":{"Line":3}},{"line":154,"address":[151469],"length":1,"stats":{"Line":6}},{"line":156,"address":[171105],"length":1,"stats":{"Line":3}},{"line":157,"address":[203046,203198],"length":1,"stats":{"Line":6}},{"line":158,"address":[171352],"length":1,"stats":{"Line":3}},{"line":159,"address":[],"length":0,"stats":{"Line":0}},{"line":163,"address":[],"length":0,"stats":{"Line":3}},{"line":164,"address":[171471,171615],"length":1,"stats":{"Line":4}},{"line":167,"address":[203296],"length":1,"stats":{"Line":1}},{"line":169,"address":[203332],"length":1,"stats":{"Line":1}},{"line":172,"address":[545518,546078],"length":1,"stats":{"Line":0}},{"line":176,"address":[546034,545474],"length":1,"stats":{"Line":1}},{"line":179,"address":[171535],"length":1,"stats":{"Line":1}},{"line":180,"address":[],"length":0,"stats":{"Line":0}},{"line":185,"address":[],"length":0,"stats":{"Line":0}},{"line":187,"address":[546352,546176],"length":1,"stats":{"Line":4}},{"line":188,"address":[546185,546361],"length":1,"stats":{"Line":1}},{"line":189,"address":[546419,546243],"length":1,"stats":{"Line":1}},{"line":190,"address":[171033],"length":1,"stats":{"Line":1}},{"line":196,"address":[],"length":0,"stats":{"Line":0}},{"line":197,"address":[],"length":0,"stats":{"Line":0}},{"line":199,"address":[],"length":0,"stats":{"Line":0}},{"line":202,"address":[],"length":0,"stats":{"Line":0}},{"line":205,"address":[],"length":0,"stats":{"Line":0}},{"line":207,"address":[],"length":0,"stats":{"Line":0}},{"line":208,"address":[],"length":0,"stats":{"Line":0}},{"line":210,"address":[],"length":0,"stats":{"Line":0}},{"line":211,"address":[],"length":0,"stats":{"Line":0}},{"line":214,"address":[],"length":0,"stats":{"Line":0}},{"line":215,"address":[],"length":0,"stats":{"Line":0}},{"line":219,"address":[],"length":0,"stats":{"Line":0}},{"line":221,"address":[],"length":0,"stats":{"Line":0}},{"line":225,"address":[],"length":0,"stats":{"Line":0}},{"line":226,"address":[],"length":0,"stats":{"Line":0}},{"line":228,"address":[],"length":0,"stats":{"Line":0}},{"line":229,"address":[],"length":0,"stats":{"Line":0}},{"line":245,"address":[],"length":0,"stats":{"Line":1}},{"line":246,"address":[],"length":0,"stats":{"Line":1}},{"line":249,"address":[],"length":0,"stats":{"Line":4}},{"line":250,"address":[681573,681553],"length":1,"stats":{"Line":4}},{"line":253,"address":[156432,156464],"length":1,"stats":{"Line":1}},{"line":254,"address":[156481,156449],"length":1,"stats":{"Line":1}},{"line":269,"address":[],"length":0,"stats":{"Line":3}},{"line":270,"address":[681621,681601],"length":1,"stats":{"Line":3}},{"line":273,"address":[],"length":0,"stats":{"Line":0}},{"line":274,"address":[],"length":0,"stats":{"Line":0}},{"line":286,"address":[],"length":0,"stats":{"Line":0}},{"line":287,"address":[],"length":0,"stats":{"Line":0}},{"line":294,"address":[],"length":0,"stats":{"Line":1}},{"line":295,"address":[],"length":0,"stats":{"Line":1}},{"line":306,"address":[],"length":0,"stats":{"Line":0}},{"line":307,"address":[],"length":0,"stats":{"Line":0}},{"line":318,"address":[],"length":0,"stats":{"Line":0}},{"line":319,"address":[],"length":0,"stats":{"Line":0}},{"line":330,"address":[],"length":0,"stats":{"Line":0}},{"line":331,"address":[],"length":0,"stats":{"Line":0}},{"line":338,"address":[],"length":0,"stats":{"Line":0}},{"line":339,"address":[],"length":0,"stats":{"Line":0}},{"line":340,"address":[],"length":0,"stats":{"Line":0}},{"line":345,"address":[],"length":0,"stats":{"Line":1}},{"line":346,"address":[],"length":0,"stats":{"Line":1}},{"line":351,"address":[],"length":0,"stats":{"Line":0}},{"line":352,"address":[],"length":0,"stats":{"Line":0}},{"line":357,"address":[],"length":0,"stats":{"Line":0}},{"line":358,"address":[],"length":0,"stats":{"Line":0}},{"line":363,"address":[],"length":0,"stats":{"Line":0}},{"line":364,"address":[],"length":0,"stats":{"Line":0}},{"line":369,"address":[],"length":0,"stats":{"Line":0}},{"line":370,"address":[],"length":0,"stats":{"Line":0}},{"line":391,"address":[],"length":0,"stats":{"Line":1}},{"line":412,"address":[],"length":0,"stats":{"Line":0}},{"line":439,"address":[],"length":0,"stats":{"Line":0}},{"line":460,"address":[],"length":0,"stats":{"Line":0}},{"line":461,"address":[],"length":0,"stats":{"Line":0}},{"line":481,"address":[],"length":0,"stats":{"Line":0}},{"line":482,"address":[],"length":0,"stats":{"Line":0}},{"line":502,"address":[],"length":0,"stats":{"Line":0}},{"line":503,"address":[],"length":0,"stats":{"Line":0}},{"line":527,"address":[],"length":0,"stats":{"Line":12}},{"line":528,"address":[212345,212404],"length":1,"stats":{"Line":24}},{"line":531,"address":[],"length":0,"stats":{"Line":1}},{"line":534,"address":[],"length":0,"stats":{"Line":1}},{"line":537,"address":[156574],"length":1,"stats":{"Line":1}},{"line":540,"address":[156623],"length":1,"stats":{"Line":1}},{"line":542,"address":[156656],"length":1,"stats":{"Line":1}},{"line":544,"address":[],"length":0,"stats":{"Line":0}},{"line":548,"address":[156704,156913],"length":1,"stats":{"Line":1}},{"line":555,"address":[156727,156773],"length":1,"stats":{"Line":2}},{"line":556,"address":[],"length":0,"stats":{"Line":1}},{"line":560,"address":[],"length":0,"stats":{"Line":0}},{"line":563,"address":[156853],"length":1,"stats":{"Line":0}},{"line":565,"address":[156883],"length":1,"stats":{"Line":0}},{"line":567,"address":[156895],"length":1,"stats":{"Line":0}},{"line":591,"address":[231790,231664],"length":1,"stats":{"Line":5}},{"line":594,"address":[231694],"length":1,"stats":{"Line":5}},{"line":598,"address":[213710],"length":1,"stats":{"Line":4}},{"line":634,"address":[],"length":0,"stats":{"Line":1}},{"line":637,"address":[205042,205150,205060,204992],"length":1,"stats":{"Line":1}},{"line":638,"address":[205094],"length":1,"stats":{"Line":0}},{"line":640,"address":[205072],"length":1,"stats":{"Line":0}},{"line":641,"address":[],"length":0,"stats":{"Line":0}},{"line":644,"address":[205053],"length":1,"stats":{"Line":0}},{"line":667,"address":[],"length":0,"stats":{"Line":0}},{"line":668,"address":[],"length":0,"stats":{"Line":0}},{"line":669,"address":[],"length":0,"stats":{"Line":0}},{"line":674,"address":[],"length":0,"stats":{"Line":0}},{"line":677,"address":[],"length":0,"stats":{"Line":0}},{"line":680,"address":[],"length":0,"stats":{"Line":0}},{"line":683,"address":[],"length":0,"stats":{"Line":0}},{"line":687,"address":[],"length":0,"stats":{"Line":0}},{"line":698,"address":[],"length":0,"stats":{"Line":0}},{"line":699,"address":[],"length":0,"stats":{"Line":0}},{"line":703,"address":[],"length":0,"stats":{"Line":0}},{"line":706,"address":[],"length":0,"stats":{"Line":0}},{"line":710,"address":[],"length":0,"stats":{"Line":0}},{"line":734,"address":[212286,212160],"length":1,"stats":{"Line":4}},{"line":737,"address":[682510,682384],"length":1,"stats":{"Line":4}},{"line":741,"address":[151678],"length":1,"stats":{"Line":3}},{"line":778,"address":[],"length":0,"stats":{"Line":1}},{"line":781,"address":[242770,242720],"length":1,"stats":{"Line":1}},{"line":782,"address":[],"length":0,"stats":{"Line":0}},{"line":785,"address":[242822],"length":1,"stats":{"Line":0}},{"line":787,"address":[],"length":0,"stats":{"Line":0}},{"line":788,"address":[],"length":0,"stats":{"Line":0}},{"line":809,"address":[],"length":0,"stats":{"Line":0}},{"line":810,"address":[],"length":0,"stats":{"Line":0}},{"line":811,"address":[],"length":0,"stats":{"Line":0}},{"line":832,"address":[],"length":0,"stats":{"Line":0}},{"line":833,"address":[],"length":0,"stats":{"Line":0}},{"line":849,"address":[],"length":0,"stats":{"Line":1}},{"line":850,"address":[],"length":0,"stats":{"Line":1}},{"line":877,"address":[],"length":0,"stats":{"Line":0}},{"line":878,"address":[],"length":0,"stats":{"Line":0}},{"line":905,"address":[],"length":0,"stats":{"Line":0}},{"line":906,"address":[],"length":0,"stats":{"Line":0}}],"covered":98,"coverable":191},{"path":["/","home","botahamec","Projects","happylock","src","collection","utils.rs"],"content":"use std::cell::Cell;\n\nuse crate::handle_unwind::handle_unwind;\nuse crate::lockable::{Lockable, RawLock};\n\n#[must_use]\npub fn get_locks\u003cL: Lockable\u003e(data: \u0026L) -\u003e Vec\u003c\u0026dyn RawLock\u003e {\n\tlet mut locks = Vec::new();\n\tdata.get_ptrs(\u0026mut locks);\n\tlocks.sort_by_key(|lock| \u0026raw const **lock);\n\tlocks\n}\n\n#[must_use]\npub fn get_locks_unsorted\u003cL: Lockable\u003e(data: \u0026L) -\u003e Vec\u003c\u0026dyn RawLock\u003e {\n\tlet mut locks = Vec::new();\n\tdata.get_ptrs(\u0026mut locks);\n\tlocks\n}\n\n/// returns `true` if the sorted list contains a duplicate\n#[must_use]\npub fn ordered_contains_duplicates(l: \u0026[\u0026dyn RawLock]) -\u003e bool {\n\tif l.is_empty() {\n\t\t// Return early to prevent panic in the below call to `windows`\n\t\treturn false;\n\t}\n\n\tl.windows(2)\n\t\t// NOTE: addr_eq is necessary because eq would also compare the v-table pointers\n\t\t.any(|window| std::ptr::addr_eq(window[0], window[1]))\n}\n\n/// Lock a set of locks in the given order. It's UB to call this without a `ThreadKey`\npub unsafe fn ordered_lock(locks: \u0026[\u0026dyn RawLock]) {\n\t// these will be unlocked in case of a panic\n\tlet locked = Cell::new(0);\n\n\thandle_unwind(\n\t\t|| {\n\t\t\tfor lock in locks {\n\t\t\t\tlock.raw_lock();\n\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t}\n\t\t},\n\t\t|| attempt_to_recover_locks_from_panic(\u0026locks[0..locked.get()]),\n\t)\n}\n\n/// Lock a set of locks in the given order. It's UB to call this without a `ThreadKey`\npub unsafe fn ordered_read(locks: \u0026[\u0026dyn RawLock]) {\n\tlet locked = Cell::new(0);\n\n\thandle_unwind(\n\t\t|| {\n\t\t\tfor lock in locks {\n\t\t\t\tlock.raw_read();\n\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t}\n\t\t},\n\t\t|| attempt_to_recover_reads_from_panic(\u0026locks[0..locked.get()]),\n\t)\n}\n\n/// Locks the locks in the order they are given. This causes deadlock if the\n/// locks contain duplicates, or if this is called by multiple threads with the\n/// locks in different orders.\npub unsafe fn ordered_try_lock(locks: \u0026[\u0026dyn RawLock]) -\u003e bool {\n\tlet locked = Cell::new(0);\n\n\thandle_unwind(\n\t\t|| unsafe {\n\t\t\tfor (i, lock) in locks.iter().enumerate() {\n\t\t\t\t// safety: we have the thread key\n\t\t\t\tif lock.raw_try_lock() {\n\t\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t\t} else {\n\t\t\t\t\tfor lock in \u0026locks[0..i] {\n\t\t\t\t\t\t// safety: this lock was already acquired\n\t\t\t\t\t\tlock.raw_unlock();\n\t\t\t\t\t}\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttrue\n\t\t},\n\t\t||\n\t\t// safety: everything in locked is locked\n\t\tattempt_to_recover_locks_from_panic(\u0026locks[0..locked.get()]),\n\t)\n}\n\n/// Locks the locks in the order they are given. This causes deadlock if this\n/// is called by multiple threads with the locks in different orders.\npub unsafe fn ordered_try_read(locks: \u0026[\u0026dyn RawLock]) -\u003e bool {\n\t// these will be unlocked in case of a panic\n\tlet locked = Cell::new(0);\n\n\thandle_unwind(\n\t\t|| unsafe {\n\t\t\tfor (i, lock) in locks.iter().enumerate() {\n\t\t\t\t// safety: we have the thread key\n\t\t\t\tif lock.raw_try_read() {\n\t\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t\t} else {\n\t\t\t\t\tfor lock in \u0026locks[0..i] {\n\t\t\t\t\t\t// safety: this lock was already acquired\n\t\t\t\t\t\tlock.raw_unlock_read();\n\t\t\t\t\t}\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttrue\n\t\t},\n\t\t||\n\t\t// safety: everything in locked is locked\n\t\tattempt_to_recover_reads_from_panic(\u0026locks[0..locked.get()]),\n\t)\n}\n\n/// Unlocks the already locked locks in order to recover from a panic\npub unsafe fn attempt_to_recover_locks_from_panic(locks: \u0026[\u0026dyn RawLock]) {\n\thandle_unwind(\n\t\t|| {\n\t\t\t// safety: the caller assumes that these are already locked\n\t\t\tlocks.iter().for_each(|lock| lock.raw_unlock());\n\t\t},\n\t\t// if we get another panic in here, we'll just have to poison what remains\n\t\t|| locks.iter().for_each(|l| l.poison()),\n\t)\n}\n\n/// Unlocks the already locked locks in order to recover from a panic\npub unsafe fn attempt_to_recover_reads_from_panic(locked: \u0026[\u0026dyn RawLock]) {\n\thandle_unwind(\n\t\t|| {\n\t\t\t// safety: the caller assumes these are already locked\n\t\t\tlocked.iter().for_each(|lock| lock.raw_unlock_read());\n\t\t},\n\t\t// if we get another panic in here, we'll just have to poison what remains\n\t\t|| locked.iter().for_each(|l| l.poison()),\n\t)\n}\n\n#[cfg(test)]\nmod tests {\n\tuse crate::collection::utils::ordered_contains_duplicates;\n\n\t#[test]\n\tfn empty_array_does_not_contain_duplicates() {\n\t\tassert!(!ordered_contains_duplicates(\u0026[]))\n\t}\n}\n","traces":[{"line":7,"address":[641680,641296,641652,641460,641844,641268,641104,641488],"length":1,"stats":{"Line":9}},{"line":8,"address":[],"length":0,"stats":{"Line":9}},{"line":9,"address":[],"length":0,"stats":{"Line":9}},{"line":10,"address":[],"length":0,"stats":{"Line":27}},{"line":11,"address":[],"length":0,"stats":{"Line":9}},{"line":15,"address":[137245,137120],"length":1,"stats":{"Line":16}},{"line":16,"address":[137810],"length":1,"stats":{"Line":16}},{"line":17,"address":[232705],"length":1,"stats":{"Line":16}},{"line":18,"address":[151086,151230],"length":1,"stats":{"Line":16}},{"line":23,"address":[542928],"length":1,"stats":{"Line":7}},{"line":24,"address":[510024],"length":1,"stats":{"Line":7}},{"line":26,"address":[941331],"length":1,"stats":{"Line":1}},{"line":29,"address":[542492],"length":1,"stats":{"Line":7}},{"line":31,"address":[544160,544189,544307],"length":1,"stats":{"Line":21}},{"line":35,"address":[941360],"length":1,"stats":{"Line":4}},{"line":37,"address":[541662],"length":1,"stats":{"Line":5}},{"line":40,"address":[643200],"length":1,"stats":{"Line":6}},{"line":41,"address":[543049,542974],"length":1,"stats":{"Line":12}},{"line":42,"address":[448195],"length":1,"stats":{"Line":6}},{"line":43,"address":[544461],"length":1,"stats":{"Line":5}},{"line":46,"address":[543152,543159,543349],"length":1,"stats":{"Line":15}},{"line":51,"address":[543136],"length":1,"stats":{"Line":2}},{"line":52,"address":[941470],"length":1,"stats":{"Line":2}},{"line":55,"address":[511840],"length":1,"stats":{"Line":2}},{"line":56,"address":[544302,544377],"length":1,"stats":{"Line":4}},{"line":57,"address":[643697],"length":1,"stats":{"Line":2}},{"line":58,"address":[539629],"length":1,"stats":{"Line":2}},{"line":61,"address":[527575,527568,527765],"length":1,"stats":{"Line":5}},{"line":68,"address":[543232],"length":1,"stats":{"Line":2}},{"line":69,"address":[525863],"length":1,"stats":{"Line":2}},{"line":72,"address":[538031],"length":1,"stats":{"Line":5}},{"line":73,"address":[539741,539599],"length":1,"stats":{"Line":6}},{"line":75,"address":[553874],"length":1,"stats":{"Line":3}},{"line":76,"address":[545312,545178],"length":1,"stats":{"Line":6}},{"line":78,"address":[512826,512765,512494,512701],"length":1,"stats":{"Line":4}},{"line":80,"address":[644580],"length":1,"stats":{"Line":1}},{"line":82,"address":[644554],"length":1,"stats":{"Line":1}},{"line":86,"address":[545334],"length":1,"stats":{"Line":2}},{"line":88,"address":[544432],"length":1,"stats":{"Line":3}},{"line":90,"address":[544439],"length":1,"stats":{"Line":1}},{"line":96,"address":[454704],"length":1,"stats":{"Line":2}},{"line":98,"address":[537767],"length":1,"stats":{"Line":2}},{"line":101,"address":[551887],"length":1,"stats":{"Line":4}},{"line":102,"address":[528813,528671],"length":1,"stats":{"Line":4}},{"line":104,"address":[554738],"length":1,"stats":{"Line":2}},{"line":105,"address":[529130,529264],"length":1,"stats":{"Line":2}},{"line":107,"address":[545165,545101,545226,544894],"length":1,"stats":{"Line":4}},{"line":109,"address":[541034],"length":1,"stats":{"Line":1}},{"line":111,"address":[555104],"length":1,"stats":{"Line":1}},{"line":115,"address":[449942],"length":1,"stats":{"Line":1}},{"line":117,"address":[538163],"length":1,"stats":{"Line":3}},{"line":119,"address":[529493,529303],"length":1,"stats":{"Line":2}},{"line":124,"address":[454816],"length":1,"stats":{"Line":7}},{"line":126,"address":[546432],"length":1,"stats":{"Line":8}},{"line":128,"address":[555422,555470,555456],"length":1,"stats":{"Line":20}},{"line":131,"address":[546526,546560,546574,546512],"length":1,"stats":{"Line":4}},{"line":136,"address":[537904],"length":1,"stats":{"Line":3}},{"line":138,"address":[546592],"length":1,"stats":{"Line":4}},{"line":140,"address":[541520,541486,541534],"length":1,"stats":{"Line":8}},{"line":143,"address":[450958,450896,450944,450910],"length":1,"stats":{"Line":4}}],"covered":60,"coverable":60},{"path":["/","home","botahamec","Projects","happylock","src","collection.rs"],"content":"use std::cell::UnsafeCell;\n\nuse crate::{lockable::RawLock, ThreadKey};\n\nmod boxed;\nmod guard;\nmod owned;\nmod r#ref;\nmod retry;\nmod utils;\n\n/// Locks a collection of locks, which cannot be shared immutably.\n///\n/// This could be a tuple of [`Lockable`] types, an array, or a `Vec`. But it\n/// can be safely locked without causing a deadlock.\n///\n/// The data in this collection is guaranteed to not contain duplicates because\n/// `L` must always implement [`OwnedLockable`]. The underlying data may not be\n/// immutably referenced and locked. Because of this, there is no need for\n/// sorting the locks in the collection, or checking for duplicates, because it\n/// can be guaranteed that until the underlying collection is mutated (which\n/// requires releasing all acquired locks in the collection to do), then the\n/// locks will stay in the same order and be locked in that order, preventing\n/// cyclic wait.\n///\n/// [`Lockable`]: `crate::lockable::Lockable`\n/// [`OwnedLockable`]: `crate::lockable::OwnedLockable`\n\n// this type caches the idea that no immutable references to the underlying\n// collection exist\n#[derive(Debug)]\npub struct OwnedLockCollection\u003cL\u003e {\n\tdata: L,\n}\n\n/// Locks a reference to a collection of locks, by sorting them by memory\n/// address.\n///\n/// This could be a tuple of [`Lockable`] types, an array, or a `Vec`. But it\n/// can be safely locked without causing a deadlock.\n///\n/// Upon construction, it must be confirmed that the collection contains no\n/// duplicate locks. This can be done by either using [`OwnedLockable`] or by\n/// checking. Regardless of how this is done, the locks will be sorted by their\n/// memory address before locking them. The sorted order of the locks is stored\n/// within this collection.\n///\n/// Unlike [`BoxedLockCollection`], this type does not allocate memory for the\n/// data, although it does allocate memory for the sorted list of lock\n/// references. This makes it slightly faster, but lifetimes must be handled.\n///\n/// [`Lockable`]: `crate::lockable::Lockable`\n/// [`OwnedLockable`]: `crate::lockable::OwnedLockable`\n//\n// This type was born when I eventually realized that I needed a self\n// referential structure. That used boxing, so I elected to make a more\n// efficient implementation (polonius please save us)\n//\n// This type caches the sorting order of the locks and the fact that it doesn't\n// contain any duplicates.\npub struct RefLockCollection\u003c'a, L\u003e {\n\tdata: \u0026'a L,\n\tlocks: Vec\u003c\u0026'a dyn RawLock\u003e,\n}\n\n/// Locks a collection of locks, stored in the heap, by sorting them by memory\n/// address.\n///\n/// This could be a tuple of [`Lockable`] types, an array, or a `Vec`. But it\n/// can be safely locked without causing a deadlock.\n///\n/// Upon construction, it must be confirmed that the collection contains no\n/// duplicate locks. This can be done by either using [`OwnedLockable`] or by\n/// checking. Regardless of how this is done, the locks will be sorted by their\n/// memory address before locking them. The sorted order of the locks is stored\n/// within this collection.\n///\n/// Unlike [`RefLockCollection`], this is a self-referential type which boxes\n/// the data that is given to it. This means no lifetimes are necessary on the\n/// type itself, but it is slightly slower because of the memory allocation.\n///\n/// [`Lockable`]: `crate::lockable::Lockable`\n/// [`OwnedLockable`]: `crate::lockable::OwnedLockable`\n//\n// This type caches the sorting order of the locks and the fact that it doesn't\n// contain any duplicates.\npub struct BoxedLockCollection\u003cL\u003e {\n\tdata: *const UnsafeCell\u003cL\u003e,\n\tlocks: Vec\u003c\u0026'static dyn RawLock\u003e,\n}\n\n/// Locks a collection of locks using a retrying algorithm.\n///\n/// This could be a tuple of [`Lockable`] types, an array, or a `Vec`. But it\n/// can be safely locked without causing a deadlock.\n///\n/// The data in this collection is guaranteed to not contain duplicates, but it\n/// also not be sorted. In some cases the lack of sorting can increase\n/// performance. However, in most cases, this collection will be slower. Cyclic\n/// wait is not guaranteed here, so the locking algorithm must release all its\n/// locks if one of the lock attempts blocks. This results in wasted time and\n/// potential [livelocking].\n///\n/// However, one case where this might be faster than [`RefLockCollection`] is\n/// when the first lock in the collection is always the first in any\n/// collection, and the other locks in the collection are always locked after\n/// that first lock is acquired. This means that as soon as it is locked, there\n/// will be no need to unlock it later on subsequent lock attempts, because\n/// they will always succeed.\n///\n/// [`Lockable`]: `crate::lockable::Lockable`\n/// [`OwnedLockable`]: `crate::lockable::OwnedLockable`\n/// [livelocking]: https://en.wikipedia.org/wiki/Deadlock#Livelock\n//\n// This type caches the fact that there are no duplicates\n#[derive(Debug)]\npub struct RetryingLockCollection\u003cL\u003e {\n\tdata: L,\n}\n\n/// A RAII guard for a generic [`Lockable`] type.\n///\n/// [`Lockable`]: `crate::lockable::Lockable`\npub struct LockGuard\u003cGuard\u003e {\n\tguard: Guard,\n\tkey: ThreadKey,\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","src","handle_unwind.rs"],"content":"use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};\n\n/// Runs `try_fn`. If it unwinds, it will run `catch` and then continue\n/// unwinding. This is used instead of `scopeguard` to ensure the `catch`\n/// function doesn't run if the thread is already panicking. The unwind\n/// must specifically be caused by the `try_fn`\npub fn handle_unwind\u003cR, F: FnOnce() -\u003e R, G: FnOnce()\u003e(try_fn: F, catch: G) -\u003e R {\n\tlet try_fn = AssertUnwindSafe(try_fn);\n\tcatch_unwind(try_fn).unwrap_or_else(|e| {\n\t\tcatch();\n\t\tresume_unwind(e)\n\t})\n}\n","traces":[{"line":7,"address":[549794,549808,549622,549958,549984,549472,549458,549288,549136,549312,550136,549648],"length":1,"stats":{"Line":148}},{"line":8,"address":[167275,166994,166585,166857,166450,167122,166722],"length":1,"stats":{"Line":120}},{"line":9,"address":[167065,168176,167137,167193,166465,167792,167536,168036,167009,167342,168265,166737,166877,168012,167523,167408,167300,167907,167881,167664,167920,166521,168163,167651,166914,167779,167625,168137,167497,168048,168291,166605,166793,167753,166642],"length":1,"stats":{"Line":299}},{"line":10,"address":[551773,551629,551511,551357,551239,551085],"length":1,"stats":{"Line":28}},{"line":11,"address":[546717,546433,546161,546029,546573,546301],"length":1,"stats":{"Line":24}}],"covered":5,"coverable":5},{"path":["/","home","botahamec","Projects","happylock","src","key.rs"],"content":"use std::cell::{Cell, LazyCell};\nuse std::fmt::{self, Debug};\nuse std::marker::PhantomData;\n\nuse sealed::Sealed;\n\n// Sealed to prevent other key types from being implemented. Otherwise, this\n// would almost instant undefined behavior.\nmod sealed {\n\tuse super::ThreadKey;\n\n\tpub trait Sealed {}\n\timpl Sealed for ThreadKey {}\n\timpl Sealed for \u0026mut ThreadKey {}\n}\n\nthread_local! {\n\tstatic KEY: LazyCell\u003cKeyCell\u003e = LazyCell::new(KeyCell::default);\n}\n\n/// The key for the current thread.\n///\n/// Only one of these exist per thread. To get the current thread's key, call\n/// [`ThreadKey::get`]. If the `ThreadKey` is dropped, it can be re-obtained.\npub struct ThreadKey {\n\tphantom: PhantomData\u003c*const ()\u003e, // implement !Send and !Sync\n}\n\n/// Allows the type to be used as a key for a lock\n///\n/// # Safety\n///\n/// Only one value which implements this trait may be allowed to exist at a\n/// time. Creating a new `Keyable` value requires making any other `Keyable`\n/// values invalid.\npub unsafe trait Keyable: Sealed {}\nunsafe impl Keyable for ThreadKey {}\n// the ThreadKey can't be moved while a mutable reference to it exists\nunsafe impl Keyable for \u0026mut ThreadKey {}\n\n// Implementing this means we can allow `MutexGuard` to be Sync\n// Safety: a \u0026ThreadKey is useless by design.\nunsafe impl Sync for ThreadKey {}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl Debug for ThreadKey {\n\tfn fmt(\u0026self, f: \u0026mut fmt::Formatter\u003c'_\u003e) -\u003e fmt::Result {\n\t\twrite!(f, \"ThreadKey\")\n\t}\n}\n\n// If you lose the thread key, you can get it back by calling ThreadKey::get\nimpl Drop for ThreadKey {\n\tfn drop(\u0026mut self) {\n\t\t// safety: a thread key cannot be acquired without creating the lock\n\t\t// safety: the key is lost, so it's safe to unlock the cell\n\t\tunsafe { KEY.with(|key| key.force_unlock()) }\n\t}\n}\n\nimpl ThreadKey {\n\t/// Get the current thread's `ThreadKey`, if it's not already taken.\n\t///\n\t/// The first time this is called, it will successfully return a\n\t/// `ThreadKey`. However, future calls to this function on the same thread\n\t/// will return [`None`], unless the key is dropped or unlocked first.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::ThreadKey;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// ```\n\t#[must_use]\n\tpub fn get() -\u003e Option\u003cSelf\u003e {\n\t\t// safety: we just acquired the lock\n\t\t// safety: if this code changes, check to ensure the requirement for\n\t\t// the Drop implementation is still true\n\t\tKEY.with(|key| {\n\t\t\tkey.try_lock().then_some(Self {\n\t\t\t\tphantom: PhantomData,\n\t\t\t})\n\t\t})\n\t}\n}\n\n/// A dumb lock that's just a wrapper for an [`AtomicBool`].\n#[derive(Default)]\nstruct KeyCell {\n\tis_locked: Cell\u003cbool\u003e,\n}\n\nimpl KeyCell {\n\t/// Attempt to lock the `KeyCell`. This is not a fair lock.\n\t#[must_use]\n\tpub fn try_lock(\u0026self) -\u003e bool {\n\t\t!self.is_locked.replace(true)\n\t}\n\n\t/// Forcibly unlocks the `KeyCell`. This should only be called if the key\n\t/// from this `KeyCell` has been \"lost\".\n\tpub unsafe fn force_unlock(\u0026self) {\n\t\tself.is_locked.set(false);\n\t}\n}\n\n#[cfg(test)]\nmod tests {\n\tuse super::*;\n\n\t#[test]\n\tfn thread_key_returns_some_on_first_call() {\n\t\tassert!(ThreadKey::get().is_some());\n\t}\n\n\t#[test]\n\tfn thread_key_returns_none_on_second_call() {\n\t\tlet key = ThreadKey::get();\n\t\tassert!(ThreadKey::get().is_none());\n\t\tdrop(key);\n\t}\n\n\t#[test]\n\tfn dropping_thread_key_allows_reobtaining() {\n\t\tdrop(ThreadKey::get());\n\t\tassert!(ThreadKey::get().is_some())\n\t}\n}\n","traces":[{"line":18,"address":[534664],"length":1,"stats":{"Line":29}},{"line":55,"address":[542416],"length":1,"stats":{"Line":12}},{"line":58,"address":[509973],"length":1,"stats":{"Line":39}},{"line":77,"address":[522432],"length":1,"stats":{"Line":29}},{"line":81,"address":[547264],"length":1,"stats":{"Line":58}},{"line":82,"address":[530361],"length":1,"stats":{"Line":29}},{"line":98,"address":[534608],"length":1,"stats":{"Line":29}},{"line":99,"address":[534261],"length":1,"stats":{"Line":29}},{"line":104,"address":[534640],"length":1,"stats":{"Line":13}},{"line":105,"address":[439589],"length":1,"stats":{"Line":13}}],"covered":10,"coverable":10},{"path":["/","home","botahamec","Projects","happylock","src","lib.rs"],"content":"#![warn(clippy::pedantic)]\n#![warn(clippy::nursery)]\n#![allow(clippy::module_name_repetitions)]\n#![allow(clippy::declare_interior_mutable_const)]\n#![allow(clippy::semicolon_if_nothing_returned)]\n#![allow(clippy::module_inception)]\n#![allow(clippy::single_match_else)]\n\n//! As it turns out, the Rust borrow checker is powerful enough that, if the\n//! standard library supported it, we could've made deadlocks undefined\n//! behavior. This library currently serves as a proof of concept for how that\n//! would work.\n//!\n//! # Theory\n//!\n//! There are four conditions necessary for a deadlock to occur. In order to\n//! prevent deadlocks, we just need to prevent one of the following:\n//!\n//! 1. mutual exclusion\n//! 2. non-preemptive allocation\n//! 3. circular wait\n//! 4. **partial allocation**\n//!\n//! This library seeks to solve **partial allocation** by requiring total\n//! allocation. All the resources a thread needs must be allocated at the same\n//! time. In order to request new resources, the old resources must be dropped\n//! first. Requesting multiple resources at once is atomic. You either get all\n//! the requested resources or none at all.\n//!\n//! As an optimization, this library also often prevents **circular wait**.\n//! Many collections sort the locks in order of their memory address. As long\n//! as the locks are always acquired in that order, then time doesn't need to\n//! be wasted on releasing locks after a failure and re-acquiring them later.\n//!\n//! # Examples\n//!\n//! Simple example:\n//! ```\n//! use std::thread;\n//! use happylock::{Mutex, ThreadKey};\n//!\n//! const N: usize = 10;\n//!\n//! static DATA: Mutex\u003ci32\u003e = Mutex::new(0);\n//!\n//! for _ in 0..N {\n//! thread::spawn(move || {\n//! // each thread gets one thread key\n//! let key = ThreadKey::get().unwrap();\n//!\n//! // unlocking a mutex requires a ThreadKey\n//! let mut data = DATA.lock(key);\n//! *data += 1;\n//!\n//! // the key is unlocked at the end of the scope\n//! });\n//! }\n//!\n//! let key = ThreadKey::get().unwrap();\n//! let data = DATA.lock(key);\n//! println!(\"{}\", *data);\n//! ```\n//!\n//! To lock multiple mutexes at a time, create a [`LockCollection`]:\n//!\n//! ```\n//! use std::thread;\n//! use happylock::{LockCollection, Mutex, ThreadKey};\n//!\n//! const N: usize = 10;\n//!\n//! static DATA_1: Mutex\u003ci32\u003e = Mutex::new(0);\n//! static DATA_2: Mutex\u003cString\u003e = Mutex::new(String::new());\n//!\n//! for _ in 0..N {\n//! thread::spawn(move || {\n//! let key = ThreadKey::get().unwrap();\n//!\n//! // happylock ensures at runtime there are no duplicate locks\n//! let collection = LockCollection::try_new((\u0026DATA_1, \u0026DATA_2)).unwrap();\n//! let mut guard = collection.lock(key);\n//!\n//! *guard.1 = (100 - *guard.0).to_string();\n//! *guard.0 += 1;\n//! });\n//! }\n//!\n//! let key = ThreadKey::get().unwrap();\n//! let data = LockCollection::try_new((\u0026DATA_1, \u0026DATA_2)).unwrap();\n//! let data = data.lock(key);\n//! println!(\"{}\", *data.0);\n//! println!(\"{}\", *data.1);\n//! ```\n//!\n//! In many cases, the [`LockCollection::new`] or [`LockCollection::new_ref`]\n//! method can be used, improving performance.\n//!\n//! ```rust\n//! use std::thread;\n//! use happylock::{LockCollection, Mutex, ThreadKey};\n//!\n//! const N: usize = 32;\n//!\n//! static DATA: [Mutex\u003ci32\u003e; 2] = [Mutex::new(0), Mutex::new(1)];\n//!\n//! for _ in 0..N {\n//! thread::spawn(move || {\n//! let key = ThreadKey::get().unwrap();\n//!\n//! // a reference to a type that implements `OwnedLockable` will never\n//! // contain duplicates, so no duplicate checking is needed.\n//! let collection = LockCollection::new_ref(\u0026DATA);\n//! let mut guard = collection.lock(key);\n//!\n//! let x = *guard[1];\n//! *guard[1] += *guard[0];\n//! *guard[0] = x;\n//! });\n//! }\n//!\n//! let key = ThreadKey::get().unwrap();\n//! let data = LockCollection::new_ref(\u0026DATA);\n//! let data = data.lock(key);\n//! println!(\"{}\", data[0]);\n//! println!(\"{}\", data[1]);\n//! ```\n//!\n//! # Performance\n//!\n//! **The `ThreadKey` is a mostly-zero cost abstraction.** It doesn't use any\n//! memory, and it doesn't really exist at run-time. The only cost comes from\n//! calling `ThreadKey::get()`, because the function has to ensure at runtime\n//! that the key hasn't already been taken. Dropping the key will also have a\n//! small cost.\n//!\n//! **Consider [`OwnedLockCollection`].** This will almost always be the\n//! fastest lock collection. It doesn't expose the underlying collection\n//! immutably, which means that it will always be locked in the same order, and\n//! doesn't need any sorting.\n//!\n//! **Avoid [`LockCollection::try_new`].** This constructor will check to make\n//! sure that the collection contains no duplicate locks. In most cases, this\n//! is O(nlogn), where n is the number of locks in the collections but in the\n//! case of [`RetryingLockCollection`], it's close to O(n).\n//! [`LockCollection::new`] and [`LockCollection::new_ref`] don't need these\n//! checks because they use [`OwnedLockable`], which is guaranteed to be unique\n//! as long as it is accessible. As a last resort,\n//! [`LockCollection::new_unchecked`] doesn't do this check, but is unsafe to\n//! call.\n//!\n//! **Know how to use [`RetryingLockCollection`].** This collection doesn't do\n//! any sorting, but uses a wasteful lock algorithm. It can't rely on the order\n//! of the locks to be the same across threads, so if it finds a lock that it\n//! can't acquire without blocking, it'll first release all of the locks it\n//! already acquired to avoid blocking other threads. This is wasteful because\n//! this algorithm may end up re-acquiring the same lock multiple times. To\n//! avoid this, ensure that (1) the first lock in the collection is always the\n//! first lock in any collection it appears in, and (2) the other locks in the\n//! collection are always preceded by that first lock. This will prevent any\n//! wasted time from re-acquiring locks. If you're unsure, [`LockCollection`]\n//! is a sensible default.\n//!\n//! [`OwnedLockable`]: `lockable::OwnedLockable`\n//! [`OwnedLockCollection`]: `collection::OwnedLockCollection`\n//! [`RetryingLockCollection`]: `collection::RetryingLockCollection`\n\nmod handle_unwind;\nmod key;\n\npub mod collection;\npub mod lockable;\npub mod mutex;\npub mod poisonable;\npub mod rwlock;\n\npub use key::{Keyable, ThreadKey};\n\n#[cfg(feature = \"spin\")]\npub use mutex::SpinLock;\n\n// Personally, I think re-exports look ugly in the rust documentation, so I\n// went with type aliases instead.\n\n/// A collection of locks that can be acquired simultaneously.\n///\n/// This re-exports [`BoxedLockCollection`] as a sensible default.\n///\n/// [`BoxedLockCollection`]: collection::BoxedLockCollection\npub type LockCollection\u003cL\u003e = collection::BoxedLockCollection\u003cL\u003e;\n\n/// A re-export for [`poisonable::Poisonable`]\npub type Poisonable\u003cL\u003e = poisonable::Poisonable\u003cL\u003e;\n\n/// A mutual exclusion primitive useful for protecting shared data, which cannot deadlock.\n///\n/// By default, this uses `parking_lot` as a backend.\n#[cfg(feature = \"parking_lot\")]\npub type Mutex\u003cT\u003e = mutex::Mutex\u003cT, parking_lot::RawMutex\u003e;\n\n/// A reader-writer lock\n///\n/// By default, this uses `parking_lot` as a backend.\n#[cfg(feature = \"parking_lot\")]\npub type RwLock\u003cT\u003e = rwlock::RwLock\u003cT, parking_lot::RawRwLock\u003e;\n","traces":[{"line":197,"address":[424262],"length":1,"stats":{"Line":10}}],"covered":1,"coverable":1},{"path":["/","home","botahamec","Projects","happylock","src","lockable.rs"],"content":"use std::mem::MaybeUninit;\n\n/// A raw lock type that may be locked and unlocked\n///\n/// # Safety\n///\n/// A deadlock must never occur. The `unlock` method must correctly unlock the\n/// data. The `get_ptrs` method must be implemented correctly. The `Output`\n/// must be unlocked when it is dropped.\n//\n// Why not use a RawRwLock? Because that would be semantically incorrect, and I\n// don't want an INIT or GuardMarker associated item.\n// Originally, RawLock had a sister trait: RawSharableLock. I removed it\n// because it'd be difficult to implement a separate type that takes a\n// different kind of RawLock. But now the Sharable marker trait is needed to\n// indicate if reads can be used.\npub unsafe trait RawLock {\n\t/// Causes all subsequent calls to the `lock` function on this lock to\n\t/// panic. This does not affect anything currently holding the lock.\n\tfn poison(\u0026self);\n\n\t/// Blocks until the lock is acquired\n\t///\n\t/// # Safety\n\t///\n\t/// It is undefined behavior to use this without ownership or mutable\n\t/// access to the [`ThreadKey`], which should last as long as the return\n\t/// value is alive.\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\tunsafe fn raw_lock(\u0026self);\n\n\t/// Attempt to lock without blocking.\n\t///\n\t/// Returns `true` if successful, `false` otherwise.\n\t///\n\t/// # Safety\n\t///\n\t/// It is undefined behavior to use this without ownership or mutable\n\t/// access to the [`ThreadKey`], which should last as long as the return\n\t/// value is alive.\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\tunsafe fn raw_try_lock(\u0026self) -\u003e bool;\n\n\t/// Releases the lock\n\t///\n\t/// # Safety\n\t///\n\t/// It is undefined behavior to use this if the lock is not acquired\n\tunsafe fn raw_unlock(\u0026self);\n\n\t/// Blocks until the data the lock protects can be safely read.\n\t///\n\t/// Some locks, but not all, will allow multiple readers at once. If\n\t/// multiple readers are allowed for a [`Lockable`] type, then the\n\t/// [`Sharable`] marker trait should be implemented.\n\t///\n\t/// # Safety\n\t///\n\t/// It is undefined behavior to use this without ownership or mutable\n\t/// access to the [`ThreadKey`], which should last as long as the return\n\t/// value is alive.\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\tunsafe fn raw_read(\u0026self);\n\n\t// Attempt to read without blocking.\n\t///\n\t/// Returns `true` if successful, `false` otherwise.\n\t///\n\t/// Some locks, but not all, will allow multiple readers at once. If\n\t/// multiple readers are allowed for a [`Lockable`] type, then the\n\t/// [`Sharable`] marker trait should be implemented.\n\t///\n\t/// # Safety\n\t///\n\t/// It is undefined behavior to use this without ownership or mutable\n\t/// access to the [`ThreadKey`], which should last as long as the return\n\t/// value is alive.\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool;\n\n\t/// Releases the lock after calling `read`.\n\t///\n\t/// # Safety\n\t///\n\t/// It is undefined behavior to use this if the read lock is not acquired\n\tunsafe fn raw_unlock_read(\u0026self);\n}\n\n/// A type that may be locked and unlocked.\n///\n/// This trait is usually implemented on collections of [`RawLock`]s. For\n/// example, a `Vec\u003cMutex\u003ci32\u003e\u003e`.\n///\n/// # Safety\n///\n/// Acquiring the locks returned by `get_ptrs` must allow access to the values\n/// returned by `guard`.\n///\n/// Dropping the `Guard` must unlock those same locks.\n///\n/// The order of the resulting list from `get_ptrs` must be deterministic. As\n/// long as the value is not mutated, the references must always be in the same\n/// order.\npub unsafe trait Lockable {\n\t/// The exclusive guard that does not hold a key\n\ttype Guard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\t/// Yields a list of references to the [`RawLock`]s contained within this\n\t/// value.\n\t///\n\t/// These reference locks which must be locked before acquiring a guard,\n\t/// and unlocked when the guard is dropped. The order of the resulting list\n\t/// is deterministic. As long as the value is not mutated, the references\n\t/// will always be in the same order.\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e);\n\n\t/// Returns a guard that can be used to access the underlying data mutably.\n\t///\n\t/// # Safety\n\t///\n\t/// All locks given by calling [`Lockable::get_ptrs`] must be locked\n\t/// exclusively before calling this function. The locks must not be\n\t/// unlocked until this guard is dropped.\n\t#[must_use]\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e;\n\n\t#[must_use]\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e;\n}\n\n/// Allows a lock to be accessed by multiple readers.\n///\n/// # Safety\n///\n/// Acquiring shared access to the locks returned by `get_ptrs` must allow\n/// shared access to the values returned by `read_guard`.\n///\n/// Dropping the `ReadGuard` must unlock those same locks.\npub unsafe trait Sharable: Lockable {\n\t/// The shared guard type that does not hold a key\n\ttype ReadGuard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\t/// Returns a guard that can be used to immutably access the underlying\n\t/// data.\n\t///\n\t/// # Safety\n\t///\n\t/// All locks given by calling [`Lockable::get_ptrs`] must be locked using\n\t/// [`RawLock::raw_read`] before calling this function. The locks must not be\n\t/// unlocked until this guard is dropped.\n\t#[must_use]\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e;\n\n\t#[must_use]\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e;\n}\n\n/// A type that may be locked and unlocked, and is known to be the only valid\n/// instance of the lock.\n///\n/// # Safety\n///\n/// There must not be any two values which can unlock the value at the same\n/// time, i.e., this must either be an owned value or a mutable reference.\npub unsafe trait OwnedLockable: Lockable {}\n\n/// A trait which indicates that `into_inner` is a valid operation for a\n/// [`Lockable`].\n///\n/// This is used for types like [`Poisonable`] to access the inner value of a\n/// lock. [`Poisonable::into_inner`] calls [`LockableIntoInner::into_inner`] to\n/// return a mutable reference of the inner value. This isn't implemented for\n/// some `Lockable`s, such as `\u0026[T]`.\n///\n/// [`Poisonable`]: `crate::Poisonable`\n/// [`Poisonable::into_inner`]: `crate::poisonable::Poisonable::into_inner`\npub trait LockableIntoInner: Lockable {\n\t/// The inner type that is behind the lock\n\ttype Inner;\n\n\t/// Consumes the lock, returning the underlying the lock.\n\tfn into_inner(self) -\u003e Self::Inner;\n}\n\n/// A trait which indicates that `as_mut` is a valid operation for a\n/// [`Lockable`].\n///\n/// This is used for types like [`Poisonable`] to access the inner value of a\n/// lock. [`Poisonable::get_mut`] calls [`LockableGetMut::get_mut`] to return a\n/// mutable reference of the inner value. This isn't implemented for some\n/// `Lockable`s, such as `\u0026[T]`.\n///\n/// [`Poisonable`]: `crate::Poisonable`\n/// [`Poisonable::get_mut`]: `crate::poisonable::Poisonable::get_mut`\npub trait LockableGetMut: Lockable {\n\t/// The inner type that is behind the lock\n\ttype Inner\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\t/// Returns a mutable reference to the underlying data.\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e;\n}\n\nunsafe impl\u003cT: Lockable\u003e Lockable for \u0026T {\n\ttype Guard\u003c'g\u003e\n\t\t= T::Guard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= T::DataMut\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\t(*self).get_ptrs(ptrs);\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\t(*self).guard()\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\t(*self).data_mut()\n\t}\n}\n\nunsafe impl\u003cT: Sharable\u003e Sharable for \u0026T {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= T::ReadGuard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= T::DataRef\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\t(*self).read_guard()\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\t(*self).data_ref()\n\t}\n}\n\nunsafe impl\u003cT: Lockable\u003e Lockable for \u0026mut T {\n\ttype Guard\u003c'g\u003e\n\t\t= T::Guard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= T::DataMut\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\t(**self).get_ptrs(ptrs)\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\t(**self).guard()\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\t(**self).data_mut()\n\t}\n}\n\nimpl\u003cT: LockableGetMut\u003e LockableGetMut for \u0026mut T {\n\ttype Inner\u003c'a\u003e\n\t\t= T::Inner\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\t(*self).get_mut()\n\t}\n}\n\nunsafe impl\u003cT: Sharable\u003e Sharable for \u0026mut T {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= T::ReadGuard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= T::DataRef\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\t(**self).read_guard()\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\t(**self).data_ref()\n\t}\n}\n\nunsafe impl\u003cT: OwnedLockable\u003e OwnedLockable for \u0026mut T {}\n\n/// Implements `Lockable`, `Sharable`, and `OwnedLockable` for tuples\n/// ex: `tuple_impls!(A B C, 0 1 2);`\nmacro_rules! tuple_impls {\n\t($($generic:ident)*, $($value:tt)*) =\u003e {\n\t\tunsafe impl\u003c$($generic: Lockable,)*\u003e Lockable for ($($generic,)*) {\n\t\t\ttype Guard\u003c'g\u003e = ($($generic::Guard\u003c'g\u003e,)*) where Self: 'g;\n\n\t\t\ttype DataMut\u003c'a\u003e = ($($generic::DataMut\u003c'a\u003e,)*) where Self: 'a;\n\n\t\t\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\t\t\t$(self.$value.get_ptrs(ptrs));*\n\t\t\t}\n\n\t\t\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\t\t\t// It's weird that this works\n\t\t\t\t// I don't think any other way of doing it compiles\n\t\t\t\t($(self.$value.guard(),)*)\n\t\t\t}\n\n\t\t\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\t\t\t($(self.$value.data_mut(),)*)\n\t\t\t}\n\t\t}\n\n\t\timpl\u003c$($generic: LockableGetMut,)*\u003e LockableGetMut for ($($generic,)*) {\n\t\t\ttype Inner\u003c'a\u003e = ($($generic::Inner\u003c'a\u003e,)*) where Self: 'a;\n\n\t\t\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\t\t\t($(self.$value.get_mut(),)*)\n\t\t\t}\n\t\t}\n\n\t\timpl\u003c$($generic: LockableIntoInner,)*\u003e LockableIntoInner for ($($generic,)*) {\n\t\t\ttype Inner = ($($generic::Inner,)*);\n\n\t\t\tfn into_inner(self) -\u003e Self::Inner {\n\t\t\t\t($(self.$value.into_inner(),)*)\n\t\t\t}\n\t\t}\n\n\t\tunsafe impl\u003c$($generic: Sharable,)*\u003e Sharable for ($($generic,)*) {\n\t\t\ttype ReadGuard\u003c'g\u003e = ($($generic::ReadGuard\u003c'g\u003e,)*) where Self: 'g;\n\n\t\t\ttype DataRef\u003c'a\u003e = ($($generic::DataRef\u003c'a\u003e,)*) where Self: 'a;\n\n\t\t\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\t\t\t($(self.$value.read_guard(),)*)\n\t\t\t}\n\n\t\t\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\t\t\t($(self.$value.data_ref(),)*)\n\t\t\t}\n\t\t}\n\n\t\tunsafe impl\u003c$($generic: OwnedLockable,)*\u003e OwnedLockable for ($($generic,)*) {}\n\t};\n}\n\ntuple_impls!(A, 0);\ntuple_impls!(A B, 0 1);\ntuple_impls!(A B C, 0 1 2);\ntuple_impls!(A B C D, 0 1 2 3);\ntuple_impls!(A B C D E, 0 1 2 3 4);\ntuple_impls!(A B C D E F, 0 1 2 3 4 5);\ntuple_impls!(A B C D E F G, 0 1 2 3 4 5 6);\n\nunsafe impl\u003cT: Lockable, const N: usize\u003e Lockable for [T; N] {\n\ttype Guard\u003c'g\u003e\n\t\t= [T::Guard\u003c'g\u003e; N]\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= [T::DataMut\u003c'a\u003e; N]\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tfor lock in self {\n\t\t\tlock.get_ptrs(ptrs);\n\t\t}\n\t}\n\n\tunsafe fn guard\u003c'g\u003e(\u0026'g self) -\u003e Self::Guard\u003c'g\u003e {\n\t\t// The MaybeInit helper functions for arrays aren't stable yet, so\n\t\t// we'll just have to implement it ourselves\n\t\tlet mut guards = MaybeUninit::\u003c[MaybeUninit\u003cT::Guard\u003c'g\u003e\u003e; N]\u003e::uninit().assume_init();\n\t\tfor i in 0..N {\n\t\t\tguards[i].write(self[i].guard());\n\t\t}\n\n\t\tguards.map(|g| g.assume_init())\n\t}\n\n\tunsafe fn data_mut\u003c'a\u003e(\u0026'a self) -\u003e Self::DataMut\u003c'a\u003e {\n\t\tlet mut guards = MaybeUninit::\u003c[MaybeUninit\u003cT::DataMut\u003c'a\u003e\u003e; N]\u003e::uninit().assume_init();\n\t\tfor i in 0..N {\n\t\t\tguards[i].write(self[i].data_mut());\n\t\t}\n\n\t\tguards.map(|g| g.assume_init())\n\t}\n}\n\nimpl\u003cT: LockableGetMut, const N: usize\u003e LockableGetMut for [T; N] {\n\ttype Inner\u003c'a\u003e\n\t\t= [T::Inner\u003c'a\u003e; N]\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tunsafe {\n\t\t\tlet mut guards = MaybeUninit::\u003c[MaybeUninit\u003cT::Inner\u003c'_\u003e\u003e; N]\u003e::uninit().assume_init();\n\t\t\tfor (i, lock) in self.iter_mut().enumerate() {\n\t\t\t\tguards[i].write(lock.get_mut());\n\t\t\t}\n\n\t\t\tguards.map(|g| g.assume_init())\n\t\t}\n\t}\n}\n\nimpl\u003cT: LockableIntoInner, const N: usize\u003e LockableIntoInner for [T; N] {\n\ttype Inner = [T::Inner; N];\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tunsafe {\n\t\t\tlet mut guards = MaybeUninit::\u003c[MaybeUninit\u003cT::Inner\u003e; N]\u003e::uninit().assume_init();\n\t\t\tfor (i, lock) in self.into_iter().enumerate() {\n\t\t\t\tguards[i].write(lock.into_inner());\n\t\t\t}\n\n\t\t\tguards.map(|g| g.assume_init())\n\t\t}\n\t}\n}\n\nunsafe impl\u003cT: Sharable, const N: usize\u003e Sharable for [T; N] {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= [T::ReadGuard\u003c'g\u003e; N]\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= [T::DataRef\u003c'a\u003e; N]\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard\u003c'g\u003e(\u0026'g self) -\u003e Self::ReadGuard\u003c'g\u003e {\n\t\tlet mut guards = MaybeUninit::\u003c[MaybeUninit\u003cT::ReadGuard\u003c'g\u003e\u003e; N]\u003e::uninit().assume_init();\n\t\tfor i in 0..N {\n\t\t\tguards[i].write(self[i].read_guard());\n\t\t}\n\n\t\tguards.map(|g| g.assume_init())\n\t}\n\n\tunsafe fn data_ref\u003c'a\u003e(\u0026'a self) -\u003e Self::DataRef\u003c'a\u003e {\n\t\tlet mut guards = MaybeUninit::\u003c[MaybeUninit\u003cT::DataRef\u003c'a\u003e\u003e; N]\u003e::uninit().assume_init();\n\t\tfor i in 0..N {\n\t\t\tguards[i].write(self[i].data_ref());\n\t\t}\n\n\t\tguards.map(|g| g.assume_init())\n\t}\n}\n\nunsafe impl\u003cT: OwnedLockable, const N: usize\u003e OwnedLockable for [T; N] {}\n\nunsafe impl\u003cT: Lockable\u003e Lockable for Box\u003c[T]\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= Box\u003c[T::Guard\u003c'g\u003e]\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= Box\u003c[T::DataMut\u003c'a\u003e]\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tfor lock in self {\n\t\t\tlock.get_ptrs(ptrs);\n\t\t}\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tself.iter().map(|lock| lock.guard()).collect()\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.iter().map(|lock| lock.data_mut()).collect()\n\t}\n}\n\nimpl\u003cT: LockableGetMut + 'static\u003e LockableGetMut for Box\u003c[T]\u003e {\n\ttype Inner\u003c'a\u003e\n\t\t= Box\u003c[T::Inner\u003c'a\u003e]\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tself.iter_mut().map(LockableGetMut::get_mut).collect()\n\t}\n}\n\nunsafe impl\u003cT: Sharable\u003e Sharable for Box\u003c[T]\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= Box\u003c[T::ReadGuard\u003c'g\u003e]\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= Box\u003c[T::DataRef\u003c'a\u003e]\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tself.iter().map(|lock| lock.read_guard()).collect()\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tself.iter().map(|lock| lock.data_ref()).collect()\n\t}\n}\n\nunsafe impl\u003cT: Sharable\u003e Sharable for Vec\u003cT\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= Box\u003c[T::ReadGuard\u003c'g\u003e]\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= Box\u003c[T::DataRef\u003c'a\u003e]\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tself.iter().map(|lock| lock.read_guard()).collect()\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tself.iter().map(|lock| lock.data_ref()).collect()\n\t}\n}\n\nunsafe impl\u003cT: OwnedLockable\u003e OwnedLockable for Box\u003c[T]\u003e {}\n\nunsafe impl\u003cT: Lockable\u003e Lockable for Vec\u003cT\u003e {\n\t// There's no reason why I'd ever want to extend a list of lock guards\n\ttype Guard\u003c'g\u003e\n\t\t= Box\u003c[T::Guard\u003c'g\u003e]\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= Box\u003c[T::DataMut\u003c'a\u003e]\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tfor lock in self {\n\t\t\tlock.get_ptrs(ptrs);\n\t\t}\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tself.iter().map(|lock| lock.guard()).collect()\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.iter().map(|lock| lock.data_mut()).collect()\n\t}\n}\n\n// I'd make a generic impl\u003cT: Lockable, I: IntoIterator\u003cItem=T\u003e\u003e Lockable for I\n// but I think that'd require sealing up this trait\n\n// TODO: using edition 2024, impl LockableIntoInner for Box\u003c[T]\u003e\n\nimpl\u003cT: LockableGetMut + 'static\u003e LockableGetMut for Vec\u003cT\u003e {\n\ttype Inner\u003c'a\u003e\n\t\t= Box\u003c[T::Inner\u003c'a\u003e]\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tself.iter_mut().map(LockableGetMut::get_mut).collect()\n\t}\n}\n\nimpl\u003cT: LockableIntoInner\u003e LockableIntoInner for Vec\u003cT\u003e {\n\ttype Inner = Box\u003c[T::Inner]\u003e;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tself.into_iter()\n\t\t\t.map(LockableIntoInner::into_inner)\n\t\t\t.collect()\n\t}\n}\n\nunsafe impl\u003cT: OwnedLockable\u003e OwnedLockable for Vec\u003cT\u003e {}\n\n#[cfg(test)]\nmod tests {\n\tuse super::*;\n\tuse crate::{Mutex, RwLock};\n\n\t#[test]\n\tfn mut_ref_get_ptrs() {\n\t\tlet mut rwlock = RwLock::new(5);\n\t\tlet mutref = \u0026mut rwlock;\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tmutref.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tassert!(std::ptr::addr_eq(lock_ptrs[0], mutref));\n\t}\n\n\t#[test]\n\tfn array_get_ptrs_empty() {\n\t\tlet locks: [Mutex\u003c()\u003e; 0] = [];\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert!(lock_ptrs.is_empty());\n\t}\n\n\t#[test]\n\tfn array_get_ptrs_length_one() {\n\t\tlet locks: [Mutex\u003ci32\u003e; 1] = [Mutex::new(1)];\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[0], locks[0].raw())) }\n\t}\n\n\t#[test]\n\tfn array_get_ptrs_length_two() {\n\t\tlet locks: [Mutex\u003ci32\u003e; 2] = [Mutex::new(1), Mutex::new(2)];\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 2);\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[0], locks[0].raw())) }\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[1], locks[1].raw())) }\n\t}\n\n\t#[test]\n\tfn vec_get_ptrs_empty() {\n\t\tlet locks: Vec\u003cMutex\u003c()\u003e\u003e = Vec::new();\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert!(lock_ptrs.is_empty());\n\t}\n\n\t#[test]\n\tfn vec_get_ptrs_length_one() {\n\t\tlet locks: Vec\u003cMutex\u003ci32\u003e\u003e = vec![Mutex::new(1)];\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[0], locks[0].raw())) }\n\t}\n\n\t#[test]\n\tfn vec_get_ptrs_length_two() {\n\t\tlet locks: Vec\u003cMutex\u003ci32\u003e\u003e = vec![Mutex::new(1), Mutex::new(2)];\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 2);\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[0], locks[0].raw())) }\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[1], locks[1].raw())) }\n\t}\n\n\t#[test]\n\tfn vec_as_mut() {\n\t\tlet mut locks: Vec\u003cMutex\u003ci32\u003e\u003e = vec![Mutex::new(1), Mutex::new(2)];\n\t\tlet lock_ptrs = LockableGetMut::get_mut(\u0026mut locks);\n\n\t\tassert_eq!(lock_ptrs.len(), 2);\n\t\tassert_eq!(*lock_ptrs[0], 1);\n\t\tassert_eq!(*lock_ptrs[1], 2);\n\t}\n\n\t#[test]\n\tfn vec_into_inner() {\n\t\tlet locks: Vec\u003cMutex\u003ci32\u003e\u003e = vec![Mutex::new(1), Mutex::new(2)];\n\t\tlet lock_ptrs = LockableIntoInner::into_inner(locks);\n\n\t\tassert_eq!(lock_ptrs.len(), 2);\n\t\tassert_eq!(lock_ptrs[0], 1);\n\t\tassert_eq!(lock_ptrs[1], 2);\n\t}\n\n\t#[test]\n\tfn box_get_ptrs_empty() {\n\t\tlet locks: Box\u003c[Mutex\u003c()\u003e]\u003e = Box::from([]);\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert!(lock_ptrs.is_empty());\n\t}\n\n\t#[test]\n\tfn box_get_ptrs_length_one() {\n\t\tlet locks: Box\u003c[Mutex\u003ci32\u003e]\u003e = vec![Mutex::new(1)].into_boxed_slice();\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[0], locks[0].raw())) }\n\t}\n\n\t#[test]\n\tfn box_get_ptrs_length_two() {\n\t\tlet locks: Box\u003c[Mutex\u003ci32\u003e]\u003e = vec![Mutex::new(1), Mutex::new(2)].into_boxed_slice();\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 2);\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[0], locks[0].raw())) }\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[1], locks[1].raw())) }\n\t}\n\n\t#[test]\n\tfn box_as_mut() {\n\t\tlet mut locks: Box\u003c[Mutex\u003ci32\u003e]\u003e = vec![Mutex::new(1), Mutex::new(2)].into_boxed_slice();\n\t\tlet lock_ptrs = LockableGetMut::get_mut(\u0026mut locks);\n\n\t\tassert_eq!(lock_ptrs.len(), 2);\n\t\tassert_eq!(*lock_ptrs[0], 1);\n\t\tassert_eq!(*lock_ptrs[1], 2);\n\t}\n}\n","traces":[{"line":232,"address":[832016,831888,831984,831952,831920],"length":1,"stats":{"Line":49}},{"line":233,"address":[220094,220030,220062],"length":1,"stats":{"Line":49}},{"line":236,"address":[176464,176480],"length":1,"stats":{"Line":7}},{"line":237,"address":[143253,143269],"length":1,"stats":{"Line":7}},{"line":240,"address":[],"length":0,"stats":{"Line":1}},{"line":241,"address":[136261],"length":1,"stats":{"Line":1}},{"line":256,"address":[160480],"length":1,"stats":{"Line":3}},{"line":257,"address":[160485],"length":1,"stats":{"Line":3}},{"line":260,"address":[],"length":0,"stats":{"Line":0}},{"line":261,"address":[],"length":0,"stats":{"Line":0}},{"line":276,"address":[],"length":0,"stats":{"Line":1}},{"line":277,"address":[711118],"length":1,"stats":{"Line":1}},{"line":280,"address":[],"length":0,"stats":{"Line":0}},{"line":281,"address":[],"length":0,"stats":{"Line":0}},{"line":284,"address":[],"length":0,"stats":{"Line":0}},{"line":285,"address":[],"length":0,"stats":{"Line":0}},{"line":295,"address":[],"length":0,"stats":{"Line":0}},{"line":296,"address":[],"length":0,"stats":{"Line":0}},{"line":311,"address":[],"length":0,"stats":{"Line":0}},{"line":312,"address":[],"length":0,"stats":{"Line":0}},{"line":315,"address":[],"length":0,"stats":{"Line":0}},{"line":316,"address":[],"length":0,"stats":{"Line":0}},{"line":331,"address":[220336],"length":1,"stats":{"Line":11}},{"line":332,"address":[238056],"length":1,"stats":{"Line":17}},{"line":335,"address":[220112,220303],"length":1,"stats":{"Line":4}},{"line":338,"address":[842366,842254],"length":1,"stats":{"Line":4}},{"line":357,"address":[842696,843120,843522,842800,842464,843105,843493],"length":1,"stats":{"Line":3}},{"line":358,"address":[842822,843142,842591,843260,842486,842908],"length":1,"stats":{"Line":6}},{"line":367,"address":[716416,716504],"length":1,"stats":{"Line":1}},{"line":368,"address":[223552],"length":1,"stats":{"Line":1}},{"line":399,"address":[],"length":0,"stats":{"Line":17}},{"line":400,"address":[],"length":0,"stats":{"Line":34}},{"line":401,"address":[160589],"length":1,"stats":{"Line":17}},{"line":405,"address":[],"length":0,"stats":{"Line":7}},{"line":408,"address":[],"length":0,"stats":{"Line":2}},{"line":409,"address":[],"length":0,"stats":{"Line":13}},{"line":410,"address":[],"length":0,"stats":{"Line":12}},{"line":413,"address":[711538,711853],"length":1,"stats":{"Line":19}},{"line":416,"address":[],"length":0,"stats":{"Line":1}},{"line":417,"address":[],"length":0,"stats":{"Line":0}},{"line":418,"address":[],"length":0,"stats":{"Line":2}},{"line":419,"address":[136618,136748,137116,136986],"length":1,"stats":{"Line":2}},{"line":422,"address":[136925,136535],"length":1,"stats":{"Line":3}},{"line":432,"address":[],"length":0,"stats":{"Line":1}},{"line":434,"address":[],"length":0,"stats":{"Line":0}},{"line":435,"address":[835098,835290],"length":1,"stats":{"Line":2}},{"line":436,"address":[],"length":0,"stats":{"Line":2}},{"line":439,"address":[835238],"length":1,"stats":{"Line":3}},{"line":447,"address":[],"length":0,"stats":{"Line":1}},{"line":449,"address":[835478],"length":1,"stats":{"Line":1}},{"line":450,"address":[],"length":0,"stats":{"Line":4}},{"line":451,"address":[835888,835950],"length":1,"stats":{"Line":2}},{"line":454,"address":[835904],"length":1,"stats":{"Line":3}},{"line":470,"address":[160608],"length":1,"stats":{"Line":4}},{"line":471,"address":[],"length":0,"stats":{"Line":0}},{"line":472,"address":[712156,712504,712770,712402,712094,712872],"length":1,"stats":{"Line":7}},{"line":473,"address":[],"length":0,"stats":{"Line":6}},{"line":476,"address":[201888,201911],"length":1,"stats":{"Line":10}},{"line":479,"address":[],"length":0,"stats":{"Line":0}},{"line":480,"address":[],"length":0,"stats":{"Line":0}},{"line":481,"address":[],"length":0,"stats":{"Line":0}},{"line":482,"address":[],"length":0,"stats":{"Line":0}},{"line":485,"address":[],"length":0,"stats":{"Line":0}},{"line":502,"address":[],"length":0,"stats":{"Line":3}},{"line":503,"address":[],"length":0,"stats":{"Line":5}},{"line":504,"address":[],"length":0,"stats":{"Line":2}},{"line":508,"address":[],"length":0,"stats":{"Line":0}},{"line":509,"address":[],"length":0,"stats":{"Line":0}},{"line":512,"address":[],"length":0,"stats":{"Line":0}},{"line":513,"address":[],"length":0,"stats":{"Line":0}},{"line":523,"address":[193120],"length":1,"stats":{"Line":1}},{"line":524,"address":[],"length":0,"stats":{"Line":1}},{"line":539,"address":[],"length":0,"stats":{"Line":0}},{"line":540,"address":[],"length":0,"stats":{"Line":0}},{"line":543,"address":[],"length":0,"stats":{"Line":0}},{"line":544,"address":[],"length":0,"stats":{"Line":0}},{"line":559,"address":[],"length":0,"stats":{"Line":0}},{"line":560,"address":[],"length":0,"stats":{"Line":0}},{"line":563,"address":[],"length":0,"stats":{"Line":0}},{"line":564,"address":[],"length":0,"stats":{"Line":0}},{"line":582,"address":[783008,783232,783120],"length":1,"stats":{"Line":4}},{"line":583,"address":[],"length":0,"stats":{"Line":7}},{"line":584,"address":[],"length":0,"stats":{"Line":3}},{"line":588,"address":[],"length":0,"stats":{"Line":1}},{"line":589,"address":[],"length":0,"stats":{"Line":3}},{"line":592,"address":[],"length":0,"stats":{"Line":0}},{"line":593,"address":[],"length":0,"stats":{"Line":0}},{"line":608,"address":[],"length":0,"stats":{"Line":2}},{"line":609,"address":[],"length":0,"stats":{"Line":2}},{"line":616,"address":[],"length":0,"stats":{"Line":1}},{"line":617,"address":[],"length":0,"stats":{"Line":1}},{"line":618,"address":[],"length":0,"stats":{"Line":0}}],"covered":57,"coverable":92},{"path":["/","home","botahamec","Projects","happylock","src","mutex","guard.rs"],"content":"use std::fmt::{Debug, Display};\nuse std::hash::Hash;\nuse std::marker::PhantomData;\nuse std::ops::{Deref, DerefMut};\n\nuse lock_api::RawMutex;\n\nuse crate::lockable::RawLock;\nuse crate::ThreadKey;\n\nuse super::{Mutex, MutexGuard, MutexRef};\n\n// These impls make things slightly easier because now you can use\n// `println!(\"{guard}\")` instead of `println!(\"{}\", *guard)`\n\n#[mutants::skip] // hashing involves RNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Hash + ?Sized, R: RawMutex\u003e Hash for MutexRef\u003c'_, T, R\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.deref().hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug + ?Sized, R: RawMutex\u003e Debug for MutexRef\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: Display + ?Sized, R: RawMutex\u003e Display for MutexRef\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e Drop for MutexRef\u003c'_, T, R\u003e {\n\tfn drop(\u0026mut self) {\n\t\t// safety: this guard is being destroyed, so the data cannot be\n\t\t// accessed without locking again\n\t\tunsafe { self.0.raw_unlock() }\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e Deref for MutexRef\u003c'_, T, R\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t// safety: this is the only type that can use `value`, and there's\n\t\t// a reference to this type, so there cannot be any mutable\n\t\t// references to this value.\n\t\tunsafe { \u0026*self.0.data.get() }\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e DerefMut for MutexRef\u003c'_, T, R\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t// safety: this is the only type that can use `value`, and we have a\n\t\t// mutable reference to this type, so there cannot be any other\n\t\t// references to this value.\n\t\tunsafe { \u0026mut *self.0.data.get() }\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e AsRef\u003cT\u003e for MutexRef\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e AsMut\u003cT\u003e for MutexRef\u003c'_, T, R\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself\n\t}\n}\n\nimpl\u003c'a, T: ?Sized, R: RawMutex\u003e MutexRef\u003c'a, T, R\u003e {\n\t/// Creates a reference to the underlying data of a mutex without\n\t/// attempting to lock it or take ownership of the key. But it's also quite\n\t/// dangerous to drop.\n\tpub(crate) unsafe fn new(mutex: \u0026'a Mutex\u003cT, R\u003e) -\u003e Self {\n\t\tSelf(mutex, PhantomData)\n\t}\n}\n\n// it's kinda annoying to re-implement some of this stuff on guards\n// there's nothing i can do about that\n\n#[mutants::skip] // hashing involves RNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Hash + ?Sized, R: RawMutex\u003e Hash for MutexGuard\u003c'_, T, R\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.deref().hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug + ?Sized, R: RawMutex\u003e Debug for MutexGuard\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: Display + ?Sized, R: RawMutex\u003e Display for MutexGuard\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e Deref for MutexGuard\u003c'_, T, R\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t\u0026self.mutex\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e DerefMut for MutexGuard\u003c'_, T, R\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t\u0026mut self.mutex\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e AsRef\u003cT\u003e for MutexGuard\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e AsMut\u003cT\u003e for MutexGuard\u003c'_, T, R\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself\n\t}\n}\n\nimpl\u003c'a, T: ?Sized, R: RawMutex\u003e MutexGuard\u003c'a, T, R\u003e {\n\t/// Create a guard to the given mutex. Undefined if multiple guards to the\n\t/// same mutex exist at once.\n\t#[must_use]\n\tpub(super) unsafe fn new(mutex: \u0026'a Mutex\u003cT, R\u003e, thread_key: ThreadKey) -\u003e Self {\n\t\tSelf {\n\t\t\tmutex: MutexRef(mutex, PhantomData),\n\t\t\tthread_key,\n\t\t}\n\t}\n}\n\nunsafe impl\u003cT: ?Sized + Sync, R: RawMutex + Sync\u003e Sync for MutexRef\u003c'_, T, R\u003e {}\n","traces":[{"line":33,"address":[836224],"length":1,"stats":{"Line":1}},{"line":34,"address":[],"length":0,"stats":{"Line":1}},{"line":39,"address":[197152,197168,197184],"length":1,"stats":{"Line":6}},{"line":42,"address":[225925],"length":1,"stats":{"Line":6}},{"line":49,"address":[836272,836304],"length":1,"stats":{"Line":2}},{"line":53,"address":[143765],"length":1,"stats":{"Line":3}},{"line":58,"address":[836336,836368],"length":1,"stats":{"Line":3}},{"line":62,"address":[836341,836373],"length":1,"stats":{"Line":3}},{"line":67,"address":[],"length":0,"stats":{"Line":2}},{"line":68,"address":[],"length":0,"stats":{"Line":2}},{"line":73,"address":[],"length":0,"stats":{"Line":1}},{"line":74,"address":[],"length":0,"stats":{"Line":1}},{"line":82,"address":[220432,220416],"length":1,"stats":{"Line":6}},{"line":83,"address":[],"length":0,"stats":{"Line":0}},{"line":107,"address":[],"length":0,"stats":{"Line":1}},{"line":108,"address":[],"length":0,"stats":{"Line":1}},{"line":115,"address":[],"length":0,"stats":{"Line":2}},{"line":116,"address":[],"length":0,"stats":{"Line":3}},{"line":121,"address":[],"length":0,"stats":{"Line":2}},{"line":122,"address":[],"length":0,"stats":{"Line":2}},{"line":127,"address":[836576],"length":1,"stats":{"Line":1}},{"line":128,"address":[],"length":0,"stats":{"Line":1}},{"line":133,"address":[],"length":0,"stats":{"Line":1}},{"line":134,"address":[],"length":0,"stats":{"Line":1}},{"line":142,"address":[138368],"length":1,"stats":{"Line":4}},{"line":144,"address":[],"length":0,"stats":{"Line":0}}],"covered":24,"coverable":26},{"path":["/","home","botahamec","Projects","happylock","src","mutex","mutex.rs"],"content":"use std::cell::UnsafeCell;\nuse std::fmt::Debug;\nuse std::marker::PhantomData;\nuse std::panic::AssertUnwindSafe;\n\nuse lock_api::RawMutex;\n\nuse crate::handle_unwind::handle_unwind;\nuse crate::lockable::{Lockable, LockableGetMut, LockableIntoInner, OwnedLockable, RawLock};\nuse crate::poisonable::PoisonFlag;\nuse crate::{Keyable, ThreadKey};\n\nuse super::{Mutex, MutexGuard, MutexRef};\n\nunsafe impl\u003cT: ?Sized, R: RawMutex\u003e RawLock for Mutex\u003cT, R\u003e {\n\tfn poison(\u0026self) {\n\t\tself.poison.poison();\n\t}\n\n\tunsafe fn raw_lock(\u0026self) {\n\t\tassert!(!self.poison.is_poisoned(), \"The mutex has been killed\");\n\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.lock(), || self.poison())\n\t}\n\n\tunsafe fn raw_try_lock(\u0026self) -\u003e bool {\n\t\tif self.poison.is_poisoned() {\n\t\t\treturn false;\n\t\t}\n\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.try_lock(), || self.poison())\n\t}\n\n\tunsafe fn raw_unlock(\u0026self) {\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.unlock(), || self.poison())\n\t}\n\n\t// this is the closest thing to a read we can get, but Sharable isn't\n\t// implemented for this\n\tunsafe fn raw_read(\u0026self) {\n\t\tself.raw_lock()\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tself.raw_try_lock()\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tself.raw_unlock()\n\t}\n}\n\nunsafe impl\u003cT: Send, R: RawMutex + Send + Sync\u003e Lockable for Mutex\u003cT, R\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= MutexRef\u003c'g, T, R\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= \u0026'a mut T\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tptrs.push(self);\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tMutexRef::new(self)\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.data.get().as_mut().unwrap_unchecked()\n\t}\n}\n\nimpl\u003cT: Send, R: RawMutex + Send + Sync\u003e LockableIntoInner for Mutex\u003cT, R\u003e {\n\ttype Inner = T;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tself.into_inner()\n\t}\n}\n\nimpl\u003cT: Send, R: RawMutex + Send + Sync\u003e LockableGetMut for Mutex\u003cT, R\u003e {\n\ttype Inner\u003c'a\u003e\n\t\t= \u0026'a mut T\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tself.get_mut()\n\t}\n}\n\nunsafe impl\u003cT: Send, R: RawMutex + Send + Sync\u003e OwnedLockable for Mutex\u003cT, R\u003e {}\n\nimpl\u003cT, R: RawMutex\u003e Mutex\u003cT, R\u003e {\n\t/// Create a new unlocked `Mutex`.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t///\n\t/// let mutex = Mutex::new(0);\n\t/// ```\n\t#[must_use]\n\tpub const fn new(data: T) -\u003e Self {\n\t\tSelf {\n\t\t\traw: R::INIT,\n\t\t\tpoison: PoisonFlag::new(),\n\t\t\tdata: UnsafeCell::new(data),\n\t\t}\n\t}\n\n\t/// Returns the raw underlying mutex.\n\t///\n\t/// Note that you will most likely need to import the [`RawMutex`] trait\n\t/// from `lock_api` to be able to call functions on the raw mutex.\n\t///\n\t/// # Safety\n\t///\n\t/// This method is unsafe because it allows unlocking a mutex while still\n\t/// holding a reference to a [`MutexGuard`], and locking a mutex without\n\t/// holding the [`ThreadKey`].\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\t#[must_use]\n\tpub const unsafe fn raw(\u0026self) -\u003e \u0026R {\n\t\t\u0026self.raw\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: ?Sized + Debug, R: RawMutex\u003e Debug for Mutex\u003cT, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t// safety: this is just a try lock, and the value is dropped\n\t\t// immediately after, so there's no risk of blocking ourselves\n\t\t// or any other threads\n\t\t// when i implement try_clone this code will become less unsafe\n\t\tif let Some(value) = unsafe { self.try_lock_no_key() } {\n\t\t\tf.debug_struct(\"Mutex\").field(\"data\", \u0026\u0026*value).finish()\n\t\t} else {\n\t\t\tstruct LockedPlaceholder;\n\t\t\timpl Debug for LockedPlaceholder {\n\t\t\t\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t\t\t\tf.write_str(\"\u003clocked\u003e\")\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tf.debug_struct(\"Mutex\")\n\t\t\t\t.field(\"data\", \u0026LockedPlaceholder)\n\t\t\t\t.finish()\n\t\t}\n\t}\n}\n\nimpl\u003cT: Default, R: RawMutex\u003e Default for Mutex\u003cT, R\u003e {\n\tfn default() -\u003e Self {\n\t\tSelf::new(T::default())\n\t}\n}\n\nimpl\u003cT, R: RawMutex\u003e From\u003cT\u003e for Mutex\u003cT, R\u003e {\n\tfn from(value: T) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\n// We don't need a `get_mut` because we don't have mutex poisoning. Hurray!\n// We have it anyway for documentation\nimpl\u003cT: ?Sized, R\u003e AsMut\u003cT\u003e for Mutex\u003cT, R\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself.get_mut()\n\t}\n}\n\nimpl\u003cT, R\u003e Mutex\u003cT, R\u003e {\n\t/// Consumes this mutex, returning the underlying data.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t///\n\t/// let mutex = Mutex::new(0);\n\t/// assert_eq!(mutex.into_inner(), 0);\n\t/// ```\n\t#[must_use]\n\tpub fn into_inner(self) -\u003e T {\n\t\tself.data.into_inner()\n\t}\n}\n\nimpl\u003cT: ?Sized, R\u003e Mutex\u003cT, R\u003e {\n\t/// Returns a mutable reference to the underlying data.\n\t///\n\t/// Since this call borrows `Mutex` mutably, no actual locking is taking\n\t/// place. The mutable borrow statically guarantees that no locks exist.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, Mutex};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut mutex = Mutex::new(0);\n\t/// *mutex.get_mut() = 10;\n\t/// assert_eq!(*mutex.lock(key), 10);\n\t/// ```\n\t#[must_use]\n\tpub fn get_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself.data.get_mut()\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e Mutex\u003cT, R\u003e {\n\tpub fn scoped_lock\u003cRet\u003e(\u0026self, key: impl Keyable, f: impl FnOnce(\u0026mut T) -\u003e Ret) -\u003e Ret {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_lock();\n\n\t\t\t// safety: the mutex was just locked\n\t\t\tlet r = f(self.data.get().as_mut().unwrap_unchecked());\n\n\t\t\t// safety: we locked the mutex already\n\t\t\tself.raw_unlock();\n\n\t\t\tdrop(key); // ensures we drop the key in the correct place\n\n\t\t\tr\n\t\t}\n\t}\n\n\tpub fn scoped_try_lock\u003cKey: Keyable, Ret\u003e(\n\t\t\u0026self,\n\t\tkey: Key,\n\t\tf: impl FnOnce(\u0026mut T) -\u003e Ret,\n\t) -\u003e Result\u003cRet, Key\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_lock() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: the mutex was just locked\n\t\t\tlet r = f(self.data.get().as_mut().unwrap_unchecked());\n\n\t\t\t// safety: we locked the mutex already\n\t\t\tself.raw_unlock();\n\n\t\t\tdrop(key); // ensures we drop the key in the correct place\n\n\t\t\tOk(r)\n\t\t}\n\t}\n\n\t/// Block the thread until this mutex can be locked, and lock it.\n\t///\n\t/// Upon returning, the thread is the only thread with a lock on the\n\t/// `Mutex`. A [`MutexGuard`] is returned to allow a scoped unlock of this\n\t/// `Mutex`. When the guard is dropped, this `Mutex` will unlock.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::{thread, sync::Arc};\n\t/// use happylock::{Mutex, ThreadKey};\n\t///\n\t/// let mutex = Arc::new(Mutex::new(0));\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t///\n\t/// thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// *c_mutex.lock(key) = 10;\n\t/// }).join().expect(\"thread::spawn failed\");\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// assert_eq!(*mutex.lock(key), 10);\n\t/// ```\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e MutexGuard\u003c'_, T, R\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_lock();\n\n\t\t\t// safety: we just locked the mutex\n\t\t\tMutexGuard::new(self, key)\n\t\t}\n\t}\n\n\t/// Attempts to lock the `Mutex` without blocking.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// lock when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If the mutex could not be acquired because it is already locked, then\n\t/// this call will return an error containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::{thread, sync::Arc};\n\t/// use happylock::{Mutex, ThreadKey};\n\t///\n\t/// let mutex = Arc::new(Mutex::new(0));\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t///\n\t/// thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut lock = c_mutex.try_lock(key);\n\t/// if let Ok(mut lock) = lock {\n\t/// *lock = 10;\n\t/// } else {\n\t/// println!(\"try_lock failed\");\n\t/// }\n\t/// }).join().expect(\"thread::spawn failed\");\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// assert_eq!(*mutex.lock(key), 10);\n\t/// ```\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e Result\u003cMutexGuard\u003c'_, T, R\u003e, ThreadKey\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the key to the mutex\n\t\t\tif self.raw_try_lock() {\n\t\t\t\t// safety: we just locked the mutex\n\t\t\t\tOk(MutexGuard::new(self, key))\n\t\t\t} else {\n\t\t\t\tErr(key)\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Returns `true` if the mutex is currently locked\n\t#[cfg(test)]\n\tpub(crate) fn is_locked(\u0026self) -\u003e bool {\n\t\tself.raw.is_locked()\n\t}\n\n\t/// Lock without a [`ThreadKey`]. It is undefined behavior to do this without\n\t/// owning the [`ThreadKey`].\n\tpub(crate) unsafe fn try_lock_no_key(\u0026self) -\u003e Option\u003cMutexRef\u003c'_, T, R\u003e\u003e {\n\t\tself.raw_try_lock().then_some(MutexRef(self, PhantomData))\n\t}\n\n\t/// Consumes the [`MutexGuard`], and consequently unlocks its `Mutex`.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, Mutex};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mutex = Mutex::new(0);\n\t///\n\t/// let mut guard = mutex.lock(key);\n\t/// *guard += 20;\n\t///\n\t/// let key = Mutex::unlock(guard);\n\t/// ```\n\t#[must_use]\n\tpub fn unlock(guard: MutexGuard\u003c'_, T, R\u003e) -\u003e ThreadKey {\n\t\tunsafe {\n\t\t\tguard.mutex.0.raw_unlock();\n\t\t}\n\t\tguard.thread_key\n\t}\n}\n\nunsafe impl\u003cR: RawMutex + Send, T: ?Sized + Send\u003e Send for Mutex\u003cT, R\u003e {}\nunsafe impl\u003cR: RawMutex + Sync, T: ?Sized + Send\u003e Sync for Mutex\u003cT, R\u003e {}\n","traces":[{"line":16,"address":[146512,146480],"length":1,"stats":{"Line":6}},{"line":17,"address":[146517,146485],"length":1,"stats":{"Line":6}},{"line":20,"address":[],"length":0,"stats":{"Line":11}},{"line":21,"address":[146670,146709,146558,146597],"length":1,"stats":{"Line":11}},{"line":24,"address":[223345,223233],"length":1,"stats":{"Line":14}},{"line":25,"address":[171632,171600,171557,171552,171605,171573,171637,171568],"length":1,"stats":{"Line":47}},{"line":28,"address":[137600],"length":1,"stats":{"Line":11}},{"line":29,"address":[240974,240814,240894],"length":1,"stats":{"Line":13}},{"line":30,"address":[146392,146312],"length":1,"stats":{"Line":7}},{"line":34,"address":[837600,837440,837840,837680,837520,837760],"length":1,"stats":{"Line":12}},{"line":35,"address":[240914,240998,240838],"length":1,"stats":{"Line":42}},{"line":38,"address":[146224,146192],"length":1,"stats":{"Line":14}},{"line":40,"address":[],"length":0,"stats":{"Line":15}},{"line":41,"address":[837937,837905,837969,838001,838033,838065],"length":1,"stats":{"Line":54}},{"line":46,"address":[137856],"length":1,"stats":{"Line":0}},{"line":47,"address":[223445,223429],"length":1,"stats":{"Line":0}},{"line":50,"address":[],"length":0,"stats":{"Line":0}},{"line":51,"address":[146421,146437],"length":1,"stats":{"Line":0}},{"line":54,"address":[241104,241088,241120],"length":1,"stats":{"Line":0}},{"line":55,"address":[],"length":0,"stats":{"Line":0}},{"line":70,"address":[241760,241696,241632],"length":1,"stats":{"Line":22}},{"line":71,"address":[],"length":0,"stats":{"Line":22}},{"line":74,"address":[241584,241600,241616],"length":1,"stats":{"Line":4}},{"line":75,"address":[838773,838757],"length":1,"stats":{"Line":4}},{"line":78,"address":[137872],"length":1,"stats":{"Line":1}},{"line":79,"address":[137881],"length":1,"stats":{"Line":1}},{"line":86,"address":[838864,838784,838800,838880,838896,838848],"length":1,"stats":{"Line":6}},{"line":87,"address":[],"length":0,"stats":{"Line":6}},{"line":97,"address":[],"length":0,"stats":{"Line":3}},{"line":98,"address":[838965,838933,838949],"length":1,"stats":{"Line":3}},{"line":115,"address":[],"length":0,"stats":{"Line":22}},{"line":118,"address":[839937,839621,839576,839014,839163,839058,839789,839741,839207,839991,839366,839314],"length":1,"stats":{"Line":44}},{"line":119,"address":[222791,222647],"length":1,"stats":{"Line":22}},{"line":136,"address":[],"length":0,"stats":{"Line":1}},{"line":137,"address":[],"length":0,"stats":{"Line":0}},{"line":167,"address":[840240,840112,840272,840192,840160],"length":1,"stats":{"Line":5}},{"line":168,"address":[840285,840168,840206,840248,840116],"length":1,"stats":{"Line":5}},{"line":173,"address":[],"length":0,"stats":{"Line":0}},{"line":174,"address":[],"length":0,"stats":{"Line":0}},{"line":181,"address":[],"length":0,"stats":{"Line":0}},{"line":182,"address":[],"length":0,"stats":{"Line":0}},{"line":198,"address":[],"length":0,"stats":{"Line":2}},{"line":199,"address":[],"length":0,"stats":{"Line":6}},{"line":220,"address":[],"length":0,"stats":{"Line":3}},{"line":221,"address":[],"length":0,"stats":{"Line":3}},{"line":226,"address":[138384,138666],"length":1,"stats":{"Line":3}},{"line":229,"address":[138408],"length":1,"stats":{"Line":3}},{"line":232,"address":[841121,841273,840801,840953,840775,841095],"length":1,"stats":{"Line":3}},{"line":235,"address":[],"length":0,"stats":{"Line":3}},{"line":237,"address":[138633],"length":1,"stats":{"Line":3}},{"line":239,"address":[],"length":0,"stats":{"Line":0}},{"line":243,"address":[144848,145180,144496,145552,143792,144124,145200,144144,144476,145532,145884,144828],"length":1,"stats":{"Line":18}},{"line":250,"address":[239592,240016,238184,239664,238536,239240,238960,238608,238256,238888,239312,239944],"length":1,"stats":{"Line":36}},{"line":251,"address":[221611,221259,220555,221963,222315,220907],"length":1,"stats":{"Line":8}},{"line":255,"address":[144972,145324,145008,145712,144108,144620,144268,145164,143916,144460,145676,144656,145868,145360,144304,143952,145516,144812],"length":1,"stats":{"Line":10}},{"line":258,"address":[144753,145105,145809,144049,144401,145457],"length":1,"stats":{"Line":10}},{"line":260,"address":[239858,238450,239506,240210,238802,239154],"length":1,"stats":{"Line":10}},{"line":262,"address":[238814,240222,239166,239518,238462,239870],"length":1,"stats":{"Line":10}},{"line":289,"address":[],"length":0,"stats":{"Line":6}},{"line":292,"address":[138958],"length":1,"stats":{"Line":7}},{"line":295,"address":[137501],"length":1,"stats":{"Line":6}},{"line":332,"address":[],"length":0,"stats":{"Line":2}},{"line":335,"address":[],"length":0,"stats":{"Line":6}},{"line":337,"address":[],"length":0,"stats":{"Line":4}},{"line":339,"address":[],"length":0,"stats":{"Line":0}},{"line":346,"address":[841888,841904],"length":1,"stats":{"Line":2}},{"line":347,"address":[],"length":0,"stats":{"Line":2}},{"line":352,"address":[],"length":0,"stats":{"Line":2}},{"line":353,"address":[],"length":0,"stats":{"Line":2}},{"line":372,"address":[],"length":0,"stats":{"Line":1}},{"line":374,"address":[],"length":0,"stats":{"Line":2}},{"line":376,"address":[],"length":0,"stats":{"Line":0}}],"covered":58,"coverable":72},{"path":["/","home","botahamec","Projects","happylock","src","mutex.rs"],"content":"use std::cell::UnsafeCell;\nuse std::marker::PhantomData;\n\nuse lock_api::RawMutex;\n\nuse crate::poisonable::PoisonFlag;\nuse crate::ThreadKey;\n\nmod guard;\nmod mutex;\n\n/// A spinning mutex\n#[cfg(feature = \"spin\")]\npub type SpinLock\u003cT\u003e = Mutex\u003cT, spin::Mutex\u003c()\u003e\u003e;\n\n/// A parking lot mutex\n#[cfg(feature = \"parking_lot\")]\npub type ParkingMutex\u003cT\u003e = Mutex\u003cT, parking_lot::RawMutex\u003e;\n\n/// A mutual exclusion primitive useful for protecting shared data, which\n/// cannot deadlock.\n///\n/// This mutex will block threads waiting for the lock to become available.\n/// Each mutex has a type parameter which represents the data that it is\n/// protecting. The data can only be accessed through the [`MutexGuard`]s\n/// returned from [`lock`] and [`try_lock`], which guarantees that the data is\n/// only ever accessed when the mutex is locked.\n///\n/// Locking the mutex on a thread that already locked it is impossible, due to\n/// the requirement of the [`ThreadKey`]. Therefore, this will never deadlock.\n///\n/// # Examples\n///\n/// ```\n/// use std::sync::Arc;\n/// use std::thread;\n/// use std::sync::mpsc;\n///\n/// use happylock::{Mutex, ThreadKey};\n///\n/// // Spawn a few threads to increment a shared variable (non-atomically),\n/// // and let the main thread know once all increments are done.\n/// //\n/// // Here we're using an Arc to share memory among threads, and the data\n/// // inside the Arc is protected with a mutex.\n/// const N: usize = 10;\n///\n/// let data = Arc::new(Mutex::new(0));\n///\n/// let (tx, rx) = mpsc::channel();\n/// for _ in 0..N {\n/// let (data, tx) = (Arc::clone(\u0026data), tx.clone());\n/// thread::spawn(move || {\n/// let key = ThreadKey::get().unwrap();\n/// let mut data = data.lock(key);\n/// *data += 1;\n/// if *data == N {\n/// tx.send(()).unwrap();\n/// }\n/// // the lock is unlocked\n/// });\n/// }\n///\n/// rx.recv().unwrap();\n/// ```\n///\n/// To unlock a mutex guard sooner than the end of the enclosing scope, either\n/// create an inner scope, drop the guard manually, or call [`Mutex::unlock`].\n///\n/// ```\n/// use std::sync::Arc;\n/// use std::thread;\n///\n/// use happylock::{Mutex, ThreadKey};\n///\n/// const N: usize = 3;\n///\n/// let data_mutex = Arc::new(Mutex::new(vec![1, 2, 3, 4]));\n/// let res_mutex = Arc::new(Mutex::new(0));\n///\n/// let mut threads = Vec::with_capacity(N);\n/// (0..N).for_each(|_| {\n/// let data_mutex_clone = Arc::clone(\u0026data_mutex);\n/// let res_mutex_clone = Arc::clone(\u0026res_mutex);\n///\n/// threads.push(thread::spawn(move || {\n/// let mut key = ThreadKey::get().unwrap();\n///\n/// // Here we use a block to limit the lifetime of the lock guard.\n/// let result = data_mutex_clone.scoped_lock(\u0026mut key, |data| {\n/// let result = data.iter().fold(0, |acc, x| acc + x * 2);\n/// data.push(result);\n/// result\n/// // The mutex guard gets dropped here, so the lock is released\n/// });\n/// // The thread key is available again\n/// *res_mutex_clone.lock(key) += result;\n/// }));\n/// });\n///\n/// let key = ThreadKey::get().unwrap();\n/// let mut data = data_mutex.lock(key);\n/// let result = data.iter().fold(0, |acc, x| acc + x * 2);\n/// data.push(result);\n///\n/// // We drop the `data` explicitly because it's not necessary anymore. This\n/// // allows other threads to start working on the data immediately. Dropping\n/// // the data also gives us access to the thread key, so we can lock\n/// // another mutex.\n/// let key = Mutex::unlock(data);\n///\n/// // Here the mutex guard is not assigned to a variable and so, even if the\n/// // scope does not end after this line, the mutex is still released: there is\n/// // no deadlock.\n/// *res_mutex.lock(key) += result;\n///\n/// threads.into_iter().for_each(|thread| {\n/// thread\n/// .join()\n/// .expect(\"The thread creating or execution failed !\")\n/// });\n///\n/// let key = ThreadKey::get().unwrap();\n/// assert_eq!(*res_mutex.lock(key), 800);\n/// ```\n///\n/// [`lock`]: `Mutex::lock`\n/// [`try_lock`]: `Mutex::try_lock`\n/// [`ThreadKey`]: `crate::ThreadKey`\npub struct Mutex\u003cT: ?Sized, R\u003e {\n\traw: R,\n\tpoison: PoisonFlag,\n\tdata: UnsafeCell\u003cT\u003e,\n}\n\n/// A reference to a mutex that unlocks it when dropped.\n///\n/// This is similar to [`MutexGuard`], except it does not hold a [`Keyable`].\npub struct MutexRef\u003c'a, T: ?Sized + 'a, R: RawMutex\u003e(\n\t\u0026'a Mutex\u003cT, R\u003e,\n\tPhantomData\u003c(\u0026'a mut T, R::GuardMarker)\u003e,\n);\n\n/// An RAII implementation of a “scoped lock” of a mutex.\n///\n/// When this structure is dropped (falls out of scope), the lock will be\n/// unlocked.\n///\n/// This is created by calling the [`lock`] and [`try_lock`] methods on [`Mutex`]\n///\n/// [`lock`]: `Mutex::lock`\n/// [`try_lock`]: `Mutex::try_lock`\n//\n// This is the most lifetime-intensive thing I've ever written. Can I graduate\n// from borrow checker university now?\npub struct MutexGuard\u003c'a, T: ?Sized + 'a, R: RawMutex\u003e {\n\tmutex: MutexRef\u003c'a, T, R\u003e, // this way we don't need to re-implement Drop\n\tthread_key: ThreadKey,\n}\n\n#[cfg(test)]\nmod tests {\n\tuse crate::{LockCollection, ThreadKey};\n\n\tuse super::*;\n\n\t#[test]\n\tfn unlocked_when_initialized() {\n\t\tlet lock: crate::Mutex\u003c_\u003e = Mutex::new(\"Hello, world!\");\n\n\t\tassert!(!lock.is_locked());\n\t}\n\n\t#[test]\n\tfn locked_after_read() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::Mutex\u003c_\u003e = Mutex::new(\"Hello, world!\");\n\n\t\tlet guard = lock.lock(key);\n\n\t\tassert!(lock.is_locked());\n\t\tdrop(guard)\n\t}\n\n\t#[test]\n\tfn display_works_for_guard() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex: crate::Mutex\u003c_\u003e = Mutex::new(\"Hello, world!\");\n\t\tlet guard = mutex.lock(key);\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn display_works_for_ref() {\n\t\tlet mutex: crate::Mutex\u003c_\u003e = Mutex::new(\"Hello, world!\");\n\t\tlet guard = unsafe { mutex.try_lock_no_key().unwrap() };\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn ref_as_mut() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = LockCollection::new(crate::Mutex::new(0));\n\t\tlet mut guard = collection.lock(key);\n\t\tlet guard_mut = guard.as_mut().as_mut();\n\n\t\t*guard_mut = 3;\n\t\tlet key = LockCollection::\u003ccrate::Mutex\u003c_\u003e\u003e::unlock(guard);\n\n\t\tlet guard = collection.lock(key);\n\n\t\tassert_eq!(guard.as_ref().as_ref(), \u00263);\n\t}\n\n\t#[test]\n\tfn guard_as_mut() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = crate::Mutex::new(0);\n\t\tlet mut guard = mutex.lock(key);\n\t\tlet guard_mut = guard.as_mut();\n\n\t\t*guard_mut = 3;\n\t\tlet key = Mutex::unlock(guard);\n\n\t\tlet guard = mutex.lock(key);\n\n\t\tassert_eq!(guard.as_ref(), \u00263);\n\t}\n\n\t#[test]\n\tfn dropping_guard_releases_mutex() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex: crate::Mutex\u003c_\u003e = Mutex::new(\"Hello, world!\");\n\n\t\tlet guard = mutex.lock(key);\n\t\tdrop(guard);\n\n\t\tassert!(!mutex.is_locked());\n\t}\n\n\t#[test]\n\tfn dropping_ref_releases_mutex() {\n\t\tlet mutex: crate::Mutex\u003c_\u003e = Mutex::new(\"Hello, world!\");\n\n\t\tlet guard = unsafe { mutex.try_lock_no_key().unwrap() };\n\t\tdrop(guard);\n\n\t\tassert!(!mutex.is_locked());\n\t}\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","src","poisonable","error.rs"],"content":"use core::fmt;\nuse std::error::Error;\n\nuse super::{PoisonError, PoisonGuard, TryLockPoisonableError};\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard\u003e fmt::Debug for PoisonError\u003cGuard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut fmt::Formatter\u003c'_\u003e) -\u003e fmt::Result {\n\t\tf.debug_struct(\"PoisonError\").finish_non_exhaustive()\n\t}\n}\n\nimpl\u003cGuard\u003e fmt::Display for PoisonError\u003cGuard\u003e {\n\t#[cfg_attr(test, mutants::skip)]\n\t#[cfg(not(tarpaulin_include))]\n\tfn fmt(\u0026self, f: \u0026mut fmt::Formatter\u003c'_\u003e) -\u003e fmt::Result {\n\t\t\"poisoned lock: another task failed inside\".fmt(f)\n\t}\n}\n\nimpl\u003cGuard\u003e AsRef\u003cGuard\u003e for PoisonError\u003cGuard\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026Guard {\n\t\tself.get_ref()\n\t}\n}\n\nimpl\u003cGuard\u003e AsMut\u003cGuard\u003e for PoisonError\u003cGuard\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut Guard {\n\t\tself.get_mut()\n\t}\n}\n\nimpl\u003cGuard\u003e Error for PoisonError\u003cGuard\u003e {}\n\nimpl\u003cGuard\u003e PoisonError\u003cGuard\u003e {\n\t/// Creates a `PoisonError`\n\t///\n\t/// This is generally created by methods like [`Poisonable::lock`].\n\t///\n\t/// ```\n\t/// use happylock::poisonable::PoisonError;\n\t///\n\t/// let error = PoisonError::new(\"oh no\");\n\t/// ```\n\t///\n\t/// [`Poisonable::lock`]: `crate::poisonable::Poisonable::lock`\n\t#[must_use]\n\tpub const fn new(guard: Guard) -\u003e Self {\n\t\tSelf { guard }\n\t}\n\n\t/// Consumes the error indicating that a lock is poisonmed, returning the\n\t/// underlying guard to allow access regardless.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::collections::HashSet;\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let mutex = Arc::new(Poisonable::new(Mutex::new(HashSet::new())));\n\t///\n\t/// // poison the mutex\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t/// let _ = thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut data = c_mutex.lock(key).unwrap();\n\t/// data.insert(10);\n\t/// panic!();\n\t/// }).join();\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let p_err = mutex.lock(key).unwrap_err();\n\t/// let data = p_err.into_inner();\n\t/// println!(\"recovered {} items\", data.len());\n\t/// ```\n\t#[must_use]\n\tpub fn into_inner(self) -\u003e Guard {\n\t\tself.guard\n\t}\n\n\t/// Reaches into this error indicating that a lock is poisoned, returning a\n\t/// reference to the underlying guard to allow access regardless.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::collections::HashSet;\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t/// use happylock::poisonable::PoisonGuard;\n\t///\n\t/// let mutex = Arc::new(Poisonable::new(Mutex::new(HashSet::new())));\n\t///\n\t/// // poison the mutex\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t/// let _ = thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut data = c_mutex.lock(key).unwrap();\n\t/// data.insert(10);\n\t/// panic!();\n\t/// }).join();\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let p_err = mutex.lock(key).unwrap_err();\n\t/// let data: \u0026PoisonGuard\u003c_, _\u003e = p_err.get_ref();\n\t/// println!(\"recovered {} items\", data.len());\n\t/// ```\n\t#[must_use]\n\tpub const fn get_ref(\u0026self) -\u003e \u0026Guard {\n\t\t\u0026self.guard\n\t}\n\n\t/// Reaches into this error indicating that a lock is poisoned, returning a\n\t/// mutable reference to the underlying guard to allow access regardless.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::collections::HashSet;\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let mutex = Arc::new(Poisonable::new(Mutex::new(HashSet::new())));\n\t///\n\t/// // poison the mutex\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t/// let _ = thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut data = c_mutex.lock(key).unwrap();\n\t/// data.insert(10);\n\t/// panic!();\n\t/// }).join();\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut p_err = mutex.lock(key).unwrap_err();\n\t/// let data = p_err.get_mut();\n\t/// data.insert(20);\n\t/// println!(\"recovered {} items\", data.len());\n\t/// ```\n\t#[must_use]\n\tpub fn get_mut(\u0026mut self) -\u003e \u0026mut Guard {\n\t\t\u0026mut self.guard\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cG\u003e fmt::Debug for TryLockPoisonableError\u003c'_, G\u003e {\n\tfn fmt(\u0026self, f: \u0026mut fmt::Formatter\u003c'_\u003e) -\u003e fmt::Result {\n\t\tmatch *self {\n\t\t\tSelf::Poisoned(..) =\u003e \"Poisoned(..)\".fmt(f),\n\t\t\tSelf::WouldBlock(_) =\u003e \"WouldBlock\".fmt(f),\n\t\t}\n\t}\n}\n\nimpl\u003cG\u003e fmt::Display for TryLockPoisonableError\u003c'_, G\u003e {\n\t#[cfg_attr(test, mutants::skip)]\n\t#[cfg(not(tarpaulin_include))]\n\tfn fmt(\u0026self, f: \u0026mut fmt::Formatter\u003c'_\u003e) -\u003e fmt::Result {\n\t\tmatch *self {\n\t\t\tSelf::Poisoned(..) =\u003e \"poisoned lock: another task failed inside\",\n\t\t\tSelf::WouldBlock(_) =\u003e \"try_lock failed because the operation would block\",\n\t\t}\n\t\t.fmt(f)\n\t}\n}\n\nimpl\u003cG\u003e Error for TryLockPoisonableError\u003c'_, G\u003e {}\n\nimpl\u003c'flag, G\u003e From\u003cPoisonError\u003cPoisonGuard\u003c'flag, G\u003e\u003e\u003e for TryLockPoisonableError\u003c'flag, G\u003e {\n\tfn from(value: PoisonError\u003cPoisonGuard\u003c'flag, G\u003e\u003e) -\u003e Self {\n\t\tSelf::Poisoned(value)\n\t}\n}\n","traces":[{"line":23,"address":[157824],"length":1,"stats":{"Line":1}},{"line":24,"address":[],"length":0,"stats":{"Line":1}},{"line":29,"address":[],"length":0,"stats":{"Line":1}},{"line":30,"address":[],"length":0,"stats":{"Line":1}},{"line":49,"address":[],"length":0,"stats":{"Line":7}},{"line":82,"address":[],"length":0,"stats":{"Line":4}},{"line":83,"address":[],"length":0,"stats":{"Line":1}},{"line":116,"address":[],"length":0,"stats":{"Line":3}},{"line":117,"address":[],"length":0,"stats":{"Line":0}},{"line":150,"address":[],"length":0,"stats":{"Line":2}},{"line":151,"address":[],"length":0,"stats":{"Line":0}},{"line":181,"address":[],"length":0,"stats":{"Line":1}},{"line":182,"address":[],"length":0,"stats":{"Line":1}}],"covered":11,"coverable":13},{"path":["/","home","botahamec","Projects","happylock","src","poisonable","flag.rs"],"content":"#[cfg(panic = \"unwind\")]\nuse std::sync::atomic::{AtomicBool, Ordering::Relaxed};\n\nuse super::PoisonFlag;\n\n#[cfg(panic = \"unwind\")]\nimpl PoisonFlag {\n\tpub const fn new() -\u003e Self {\n\t\tSelf(AtomicBool::new(false))\n\t}\n\n\tpub fn is_poisoned(\u0026self) -\u003e bool {\n\t\tself.0.load(Relaxed)\n\t}\n\n\tpub fn clear_poison(\u0026self) {\n\t\tself.0.store(false, Relaxed)\n\t}\n\n\tpub fn poison(\u0026self) {\n\t\tself.0.store(true, Relaxed);\n\t}\n}\n\n#[cfg(not(panic = \"unwind\"))]\nimpl PoisonFlag {\n\tpub const fn new() -\u003e Self {\n\t\tSelf()\n\t}\n\n\t#[mutants::skip] // None of the tests have panic = \"abort\", so this can't be tested\n\t#[cfg(not(tarpaulin_include))]\n\tpub fn is_poisoned(\u0026self) -\u003e bool {\n\t\tfalse\n\t}\n\n\tpub fn clear_poison(\u0026self) {\n\t\t()\n\t}\n\n\tpub fn poison(\u0026self) {\n\t\t()\n\t}\n}\n","traces":[{"line":8,"address":[534304],"length":1,"stats":{"Line":15}},{"line":9,"address":[522161],"length":1,"stats":{"Line":15}},{"line":12,"address":[538208],"length":1,"stats":{"Line":12}},{"line":13,"address":[534361],"length":1,"stats":{"Line":16}},{"line":16,"address":[534032],"length":1,"stats":{"Line":1}},{"line":17,"address":[538249],"length":1,"stats":{"Line":1}},{"line":20,"address":[548160],"length":1,"stats":{"Line":8}},{"line":21,"address":[538281],"length":1,"stats":{"Line":10}}],"covered":8,"coverable":8},{"path":["/","home","botahamec","Projects","happylock","src","poisonable","guard.rs"],"content":"use std::fmt::{Debug, Display};\nuse std::hash::Hash;\nuse std::marker::PhantomData;\nuse std::ops::{Deref, DerefMut};\n\nuse super::{PoisonFlag, PoisonGuard, PoisonRef};\n\nimpl\u003c'a, Guard\u003e PoisonRef\u003c'a, Guard\u003e {\n\t// This is used so that we don't keep accidentally adding the flag reference\n\tpub(super) const fn new(flag: \u0026'a PoisonFlag, guard: Guard) -\u003e Self {\n\t\tSelf {\n\t\t\tguard,\n\t\t\t#[cfg(panic = \"unwind\")]\n\t\t\tflag,\n\t\t\t_phantom: PhantomData,\n\t\t}\n\t}\n}\n\nimpl\u003cGuard\u003e Drop for PoisonRef\u003c'_, Guard\u003e {\n\tfn drop(\u0026mut self) {\n\t\t#[cfg(panic = \"unwind\")]\n\t\tif std::thread::panicking() {\n\t\t\tself.flag.poison();\n\t\t}\n\t}\n}\n\n#[mutants::skip] // hashing involves RNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard: Hash\u003e Hash for PoisonRef\u003c'_, Guard\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.guard.hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard: Debug\u003e Debug for PoisonRef\u003c'_, Guard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cGuard: Display\u003e Display for PoisonRef\u003c'_, Guard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cGuard\u003e Deref for PoisonRef\u003c'_, Guard\u003e {\n\ttype Target = Guard;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t\u0026self.guard\n\t}\n}\n\nimpl\u003cGuard\u003e DerefMut for PoisonRef\u003c'_, Guard\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t\u0026mut self.guard\n\t}\n}\n\nimpl\u003cGuard\u003e AsRef\u003cGuard\u003e for PoisonRef\u003c'_, Guard\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026Guard {\n\t\t\u0026self.guard\n\t}\n}\n\nimpl\u003cGuard\u003e AsMut\u003cGuard\u003e for PoisonRef\u003c'_, Guard\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut Guard {\n\t\t\u0026mut self.guard\n\t}\n}\n\n#[mutants::skip] // hashing involves RNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard: Hash\u003e Hash for PoisonGuard\u003c'_, Guard\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.guard.hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard: Debug\u003e Debug for PoisonGuard\u003c'_, Guard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026self.guard, f)\n\t}\n}\n\nimpl\u003cGuard: Display\u003e Display for PoisonGuard\u003c'_, Guard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026self.guard, f)\n\t}\n}\n\nimpl\u003cT, Guard: Deref\u003cTarget = T\u003e\u003e Deref for PoisonGuard\u003c'_, Guard\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t#[allow(clippy::explicit_auto_deref)] // fixing this results in a compiler error\n\t\t\u0026*self.guard.guard\n\t}\n}\n\nimpl\u003cT, Guard: DerefMut\u003cTarget = T\u003e\u003e DerefMut for PoisonGuard\u003c'_, Guard\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t#[allow(clippy::explicit_auto_deref)] // fixing this results in a compiler error\n\t\t\u0026mut *self.guard.guard\n\t}\n}\n\nimpl\u003cGuard\u003e AsRef\u003cGuard\u003e for PoisonGuard\u003c'_, Guard\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026Guard {\n\t\t\u0026self.guard.guard\n\t}\n}\n\nimpl\u003cGuard\u003e AsMut\u003cGuard\u003e for PoisonGuard\u003c'_, Guard\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut Guard {\n\t\t\u0026mut self.guard.guard\n\t}\n}\n","traces":[{"line":10,"address":[158336,158304],"length":1,"stats":{"Line":3}},{"line":21,"address":[],"length":0,"stats":{"Line":5}},{"line":22,"address":[],"length":0,"stats":{"Line":0}},{"line":23,"address":[],"length":0,"stats":{"Line":3}},{"line":24,"address":[],"length":0,"stats":{"Line":5}},{"line":46,"address":[],"length":0,"stats":{"Line":1}},{"line":47,"address":[],"length":0,"stats":{"Line":1}},{"line":54,"address":[],"length":0,"stats":{"Line":2}},{"line":55,"address":[],"length":0,"stats":{"Line":0}},{"line":60,"address":[],"length":0,"stats":{"Line":1}},{"line":61,"address":[],"length":0,"stats":{"Line":0}},{"line":66,"address":[158464],"length":1,"stats":{"Line":1}},{"line":67,"address":[],"length":0,"stats":{"Line":0}},{"line":72,"address":[],"length":0,"stats":{"Line":1}},{"line":73,"address":[],"length":0,"stats":{"Line":0}},{"line":78,"address":[],"length":0,"stats":{"Line":0}},{"line":79,"address":[],"length":0,"stats":{"Line":0}},{"line":84,"address":[],"length":0,"stats":{"Line":0}},{"line":85,"address":[],"length":0,"stats":{"Line":0}},{"line":92,"address":[],"length":0,"stats":{"Line":0}},{"line":93,"address":[],"length":0,"stats":{"Line":0}},{"line":114,"address":[],"length":0,"stats":{"Line":1}},{"line":115,"address":[],"length":0,"stats":{"Line":1}},{"line":122,"address":[],"length":0,"stats":{"Line":1}},{"line":124,"address":[],"length":0,"stats":{"Line":1}},{"line":129,"address":[],"length":0,"stats":{"Line":2}},{"line":131,"address":[],"length":0,"stats":{"Line":2}},{"line":136,"address":[],"length":0,"stats":{"Line":1}},{"line":137,"address":[],"length":0,"stats":{"Line":0}},{"line":142,"address":[],"length":0,"stats":{"Line":1}},{"line":143,"address":[],"length":0,"stats":{"Line":0}}],"covered":18,"coverable":31},{"path":["/","home","botahamec","Projects","happylock","src","poisonable","poisonable.rs"],"content":"use std::panic::{RefUnwindSafe, UnwindSafe};\n\nuse crate::lockable::{\n\tLockable, LockableGetMut, LockableIntoInner, OwnedLockable, RawLock, Sharable,\n};\nuse crate::{Keyable, ThreadKey};\n\nuse super::{\n\tPoisonError, PoisonFlag, PoisonGuard, PoisonRef, PoisonResult, Poisonable,\n\tTryLockPoisonableError, TryLockPoisonableResult,\n};\n\nunsafe impl\u003cL: Lockable + RawLock\u003e RawLock for Poisonable\u003cL\u003e {\n\t#[mutants::skip] // this should never run\n\t#[cfg(not(tarpaulin_include))]\n\tfn poison(\u0026self) {\n\t\tself.inner.poison()\n\t}\n\n\tunsafe fn raw_lock(\u0026self) {\n\t\tself.inner.raw_lock()\n\t}\n\n\tunsafe fn raw_try_lock(\u0026self) -\u003e bool {\n\t\tself.inner.raw_try_lock()\n\t}\n\n\tunsafe fn raw_unlock(\u0026self) {\n\t\tself.inner.raw_unlock()\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tself.inner.raw_read()\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tself.inner.raw_try_read()\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tself.inner.raw_unlock_read()\n\t}\n}\n\nunsafe impl\u003cL: Lockable\u003e Lockable for Poisonable\u003cL\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= PoisonResult\u003cPoisonRef\u003c'g, L::Guard\u003c'g\u003e\u003e\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= PoisonResult\u003cL::DataMut\u003c'a\u003e\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tself.inner.get_ptrs(ptrs)\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tlet ref_guard = PoisonRef::new(\u0026self.poisoned, self.inner.guard());\n\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(ref_guard))\n\t\t} else {\n\t\t\tOk(ref_guard)\n\t\t}\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(self.inner.data_mut()))\n\t\t} else {\n\t\t\tOk(self.inner.data_mut())\n\t\t}\n\t}\n}\n\nunsafe impl\u003cL: Sharable\u003e Sharable for Poisonable\u003cL\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= PoisonResult\u003cPoisonRef\u003c'g, L::ReadGuard\u003c'g\u003e\u003e\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= PoisonResult\u003cL::DataRef\u003c'a\u003e\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tlet ref_guard = PoisonRef::new(\u0026self.poisoned, self.inner.read_guard());\n\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(ref_guard))\n\t\t} else {\n\t\t\tOk(ref_guard)\n\t\t}\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(self.inner.data_ref()))\n\t\t} else {\n\t\t\tOk(self.inner.data_ref())\n\t\t}\n\t}\n}\n\nunsafe impl\u003cL: OwnedLockable\u003e OwnedLockable for Poisonable\u003cL\u003e {}\n\n// AsMut won't work here because we don't strictly return a \u0026mut T\n// LockableGetMut is the next best thing\nimpl\u003cL: LockableGetMut\u003e LockableGetMut for Poisonable\u003cL\u003e {\n\ttype Inner\u003c'a\u003e\n\t\t= PoisonResult\u003cL::Inner\u003c'a\u003e\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(self.inner.get_mut()))\n\t\t} else {\n\t\t\tOk(self.inner.get_mut())\n\t\t}\n\t}\n}\n\nimpl\u003cL: LockableIntoInner\u003e LockableIntoInner for Poisonable\u003cL\u003e {\n\ttype Inner = PoisonResult\u003cL::Inner\u003e;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(self.inner.into_inner()))\n\t\t} else {\n\t\t\tOk(self.inner.into_inner())\n\t\t}\n\t}\n}\n\nimpl\u003cL\u003e From\u003cL\u003e for Poisonable\u003cL\u003e {\n\tfn from(value: L) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\nimpl\u003cL\u003e Poisonable\u003cL\u003e {\n\t/// Creates a new `Poisonable`\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, Poisonable};\n\t///\n\t/// let mutex = Poisonable::new(Mutex::new(0));\n\t/// ```\n\tpub const fn new(value: L) -\u003e Self {\n\t\tSelf {\n\t\t\tinner: value,\n\t\t\tpoisoned: PoisonFlag::new(),\n\t\t}\n\t}\n\n\t/// Determines whether the mutex is poisoned.\n\t///\n\t/// If another thread is active, the mutex can still become poisoned at any\n\t/// time. You should not trust a `false` value for program correctness\n\t/// without additional synchronization.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let mutex = Arc::new(Poisonable::new(Mutex::new(0)));\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t///\n\t/// let _ = thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let _lock = c_mutex.lock(key).unwrap();\n\t/// panic!(); // the mutex gets poisoned\n\t/// }).join();\n\t///\n\t/// assert_eq!(mutex.is_poisoned(), true);\n\t/// ```\n\tpub fn is_poisoned(\u0026self) -\u003e bool {\n\t\tself.poisoned.is_poisoned()\n\t}\n\n\t/// Clear the poisoned state from a lock.\n\t///\n\t/// If the lock is poisoned, it will remain poisoned until this function\n\t/// is called. This allows recovering from a poisoned state and marking\n\t/// that it has recovered. For example, if the value is overwritten by a\n\t/// known-good value, then the lock can be marked as un-poisoned. Or\n\t/// possibly, the value could by inspected to determine if it is in a\n\t/// consistent state, and if so the poison is removed.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let mutex = Arc::new(Poisonable::new(Mutex::new(0)));\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t///\n\t/// let _ = thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let _lock = c_mutex.lock(key).unwrap();\n\t/// panic!(); // the mutex gets poisoned\n\t/// }).join();\n\t///\n\t/// assert_eq!(mutex.is_poisoned(), true);\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let x = mutex.lock(key).unwrap_or_else(|mut e| {\n\t/// **e.get_mut() = 1;\n\t/// mutex.clear_poison();\n\t/// e.into_inner()\n\t/// });\n\t///\n\t/// assert_eq!(mutex.is_poisoned(), false);\n\t/// assert_eq!(*x, 1);\n\t/// ```\n\tpub fn clear_poison(\u0026self) {\n\t\tself.poisoned.clear_poison()\n\t}\n\n\t/// Consumes this `Poisonable`, returning the underlying lock.\n\t///\n\t/// This consumes the `Poisonable` and returns ownership of the lock, which\n\t/// means that the `Poisonable` can still be `RefUnwindSafe`.\n\t///\n\t/// # Errors\n\t///\n\t/// If another user of this lock panicked while holding the lock, then this\n\t/// call will return an error instead.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, Poisonable};\n\t///\n\t/// let mutex = Poisonable::new(Mutex::new(0));\n\t/// assert_eq!(mutex.into_child().unwrap().into_inner(), 0);\n\t/// ```\n\tpub fn into_child(self) -\u003e PoisonResult\u003cL\u003e {\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(self.inner))\n\t\t} else {\n\t\t\tOk(self.inner)\n\t\t}\n\t}\n\n\t/// Returns a mutable reference to the underlying lock.\n\t///\n\t/// This can be implemented while still being `RefUnwindSafe` because\n\t/// it requires a mutable reference.\n\t///\n\t/// # Errors\n\t///\n\t/// If another user of this lock panicked while holding the lock, then\n\t/// this call will return an error instead.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut mutex = Poisonable::new(Mutex::new(0));\n\t/// *mutex.child_mut().unwrap().as_mut() = 10;\n\t/// assert_eq!(*mutex.lock(key).unwrap(), 10);\n\t/// ```\n\tpub fn child_mut(\u0026mut self) -\u003e PoisonResult\u003c\u0026mut L\u003e {\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(\u0026mut self.inner))\n\t\t} else {\n\t\t\tOk(\u0026mut self.inner)\n\t\t}\n\t}\n\n\t// NOTE: `child_ref` isn't implemented because it would make this not `RefUnwindSafe`\n\t//\n}\n\nimpl\u003cL: Lockable\u003e Poisonable\u003cL\u003e {\n\t/// Creates a guard for the poisonable, without locking it\n\tunsafe fn guard(\u0026self, key: ThreadKey) -\u003e PoisonResult\u003cPoisonGuard\u003c'_, L::Guard\u003c'_\u003e\u003e\u003e {\n\t\tlet guard = PoisonGuard {\n\t\t\tguard: PoisonRef::new(\u0026self.poisoned, self.inner.guard()),\n\t\t\tkey,\n\t\t};\n\n\t\tif self.is_poisoned() {\n\t\t\treturn Err(PoisonError::new(guard));\n\t\t}\n\n\t\tOk(guard)\n\t}\n}\n\nimpl\u003cL: Lockable + RawLock\u003e Poisonable\u003cL\u003e {\n\tpub fn scoped_lock\u003c'a, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: impl Keyable,\n\t\tf: impl Fn(\u003cSelf as Lockable\u003e::DataMut\u003c'a\u003e) -\u003e R,\n\t) -\u003e R {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_lock();\n\n\t\t\t// safety: the data was just locked\n\t\t\tlet r = f(self.data_mut());\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock();\n\n\t\t\tdrop(key); // ensure the key stays alive long enough\n\n\t\t\tr\n\t\t}\n\t}\n\n\tpub fn scoped_try_lock\u003c'a, Key: Keyable, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(\u003cSelf as Lockable\u003e::DataMut\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_lock() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we just locked the collection\n\t\t\tlet r = f(self.data_mut());\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock();\n\n\t\t\tdrop(key); // ensures the key stays valid long enough\n\n\t\t\tOk(r)\n\t\t}\n\t}\n\n\t/// Acquires the lock, blocking the current thread until it is ok to do so.\n\t///\n\t/// This function will block the current thread until it is available to\n\t/// acquire the mutex. Upon returning, the thread is the only thread with\n\t/// the lock held. An RAII guard is returned to allow scoped unlock of the\n\t/// lock. When the guard goes out of scope, the mutex will be unlocked.\n\t///\n\t/// # Errors\n\t///\n\t/// If another use of this mutex panicked while holding the mutex, then\n\t/// this call will return an error once the mutex is acquired.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let mutex = Arc::new(Poisonable::new(Mutex::new(0)));\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t///\n\t/// thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// *c_mutex.lock(key).unwrap() = 10;\n\t/// }).join().expect(\"thread::spawn failed\");\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// assert_eq!(*mutex.lock(key).unwrap(), 10);\n\t/// ```\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e PoisonResult\u003cPoisonGuard\u003c'_, L::Guard\u003c'_\u003e\u003e\u003e {\n\t\tunsafe {\n\t\t\tself.inner.raw_lock();\n\t\t\tself.guard(key)\n\t\t}\n\t}\n\n\t/// Attempts to acquire this lock.\n\t///\n\t/// If the lock could not be acquired at this time, then [`Err`] is\n\t/// returned. Otherwise, an RAII guard is returned. The lock will be\n\t/// unlocked when the guard is dropped.\n\t///\n\t/// This function does not block.\n\t///\n\t/// # Errors\n\t///\n\t/// If another user of this mutex panicked while holding the mutex, then\n\t/// this call will return the [`Poisoned`] error if the mutex would\n\t/// otherwise be acquired.\n\t///\n\t/// If the mutex could not be acquired because it is already locked, then\n\t/// this call will return the [`WouldBlock`] error.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let mutex = Arc::new(Poisonable::new(Mutex::new(0)));\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t///\n\t/// thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut lock = c_mutex.try_lock(key);\n\t/// if let Ok(mut mutex) = lock {\n\t/// *mutex = 10;\n\t/// } else {\n\t/// println!(\"try_lock failed\");\n\t/// }\n\t/// }).join().expect(\"thread::spawn failed\");\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// assert_eq!(*mutex.lock(key).unwrap(), 10);\n\t/// ```\n\t///\n\t/// [`Poisoned`]: `TryLockPoisonableError::Poisoned`\n\t/// [`WouldBlock`]: `TryLockPoisonableError::WouldBlock`\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e TryLockPoisonableResult\u003c'_, L::Guard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\tif self.inner.raw_try_lock() {\n\t\t\t\tOk(self.guard(key)?)\n\t\t\t} else {\n\t\t\t\tErr(TryLockPoisonableError::WouldBlock(key))\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Consumes the [`PoisonGuard`], and consequently unlocks its `Poisonable`.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, Mutex, Poisonable};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mutex = Poisonable::new(Mutex::new(0));\n\t///\n\t/// let mut guard = mutex.lock(key).unwrap();\n\t/// *guard += 20;\n\t///\n\t/// let key = Poisonable::\u003cMutex\u003c_\u003e\u003e::unlock(guard);\n\t/// ```\n\tpub fn unlock\u003c'flag\u003e(guard: PoisonGuard\u003c'flag, L::Guard\u003c'flag\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: Sharable + RawLock\u003e Poisonable\u003cL\u003e {\n\tunsafe fn read_guard(\u0026self, key: ThreadKey) -\u003e PoisonResult\u003cPoisonGuard\u003c'_, L::ReadGuard\u003c'_\u003e\u003e\u003e {\n\t\tlet guard = PoisonGuard {\n\t\t\tguard: PoisonRef::new(\u0026self.poisoned, self.inner.read_guard()),\n\t\t\tkey,\n\t\t};\n\n\t\tif self.is_poisoned() {\n\t\t\treturn Err(PoisonError::new(guard));\n\t\t}\n\n\t\tOk(guard)\n\t}\n\n\tpub fn scoped_read\u003c'a, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: impl Keyable,\n\t\tf: impl Fn(\u003cSelf as Sharable\u003e::DataRef\u003c'a\u003e) -\u003e R,\n\t) -\u003e R {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_read();\n\n\t\t\t// safety: the data was just locked\n\t\t\tlet r = f(self.data_ref());\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock_read();\n\n\t\t\tdrop(key); // ensure the key stays alive long enough\n\n\t\t\tr\n\t\t}\n\t}\n\n\tpub fn scoped_try_read\u003c'a, Key: Keyable, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(\u003cSelf as Sharable\u003e::DataRef\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_read() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we just locked the collection\n\t\t\tlet r = f(self.data_ref());\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock_read();\n\n\t\t\tdrop(key); // ensures the key stays valid long enough\n\n\t\t\tOk(r)\n\t\t}\n\t}\n\n\t/// Locks with shared read access, blocking the current thread until it can\n\t/// be acquired.\n\t///\n\t/// This function will block the current thread until there are no writers\n\t/// which hold the lock. This method does not provide any guarantee with\n\t/// respect to the ordering of contentious readers or writers will acquire\n\t/// the lock.\n\t///\n\t/// # Errors\n\t///\n\t/// If another use of this lock panicked while holding the lock, then\n\t/// this call will return an error once the lock is acquired.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t///\n\t/// use happylock::{RwLock, Poisonable, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = Arc::new(Poisonable::new(RwLock::new(0)));\n\t/// let c_lock = Arc::clone(\u0026lock);\n\t///\n\t/// let n = lock.read(key).unwrap();\n\t/// assert_eq!(*n, 0);\n\t///\n\t/// thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// assert!(c_lock.read(key).is_ok());\n\t/// }).join().expect(\"thread::spawn failed\");\n\t/// ```\n\tpub fn read(\u0026self, key: ThreadKey) -\u003e PoisonResult\u003cPoisonGuard\u003c'_, L::ReadGuard\u003c'_\u003e\u003e\u003e {\n\t\tunsafe {\n\t\t\tself.inner.raw_read();\n\t\t\tself.read_guard(key)\n\t\t}\n\t}\n\n\t/// Attempts to acquire the lock with shared read access.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is returned.\n\t/// Otherwise, an RAII guard is returned which will release the shared access\n\t/// when it is dropped.\n\t///\n\t/// This function does not block.\n\t///\n\t/// This function does not provide any guarantees with respect to the ordering\n\t/// of whether contentious readers or writers will acquire the lock first.\n\t///\n\t/// # Errors\n\t///\n\t/// This function will return the [`Poisoned`] error if the lock is\n\t/// poisoned. A [`Poisonable`] is poisoned whenever a writer panics while\n\t/// holding an exclusive lock. `Poisoned` will only be returned if the lock\n\t/// would have otherwise been acquired.\n\t///\n\t/// This function will return the [`WouldBlock`] error if the lock could\n\t/// not be acquired because it was already locked exclusively.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Poisonable, RwLock, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = Poisonable::new(RwLock::new(1));\n\t///\n\t/// match lock.try_read(key) {\n\t/// Ok(n) =\u003e assert_eq!(*n, 1),\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t/// ```\n\t///\n\t/// [`Poisoned`]: `TryLockPoisonableError::Poisoned`\n\t/// [`WouldBlock`]: `TryLockPoisonableError::WouldBlock`\n\tpub fn try_read(\u0026self, key: ThreadKey) -\u003e TryLockPoisonableResult\u003c'_, L::ReadGuard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\tif self.inner.raw_try_read() {\n\t\t\t\tOk(self.read_guard(key)?)\n\t\t\t} else {\n\t\t\t\tErr(TryLockPoisonableError::WouldBlock(key))\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Consumes the [`PoisonGuard`], and consequently unlocks its underlying lock.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, RwLock, Poisonable};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = Poisonable::new(RwLock::new(0));\n\t///\n\t/// let mut guard = lock.read(key).unwrap();\n\t/// let key = Poisonable::\u003cRwLock\u003c_\u003e\u003e::unlock_read(guard);\n\t/// ```\n\tpub fn unlock_read\u003c'flag\u003e(guard: PoisonGuard\u003c'flag, L::ReadGuard\u003c'flag\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: LockableIntoInner\u003e Poisonable\u003cL\u003e {\n\t/// Consumes this `Poisonable`, returning the underlying data.\n\t///\n\t/// # Errors\n\t///\n\t/// If another user of this lock panicked while holding the lock, then this\n\t/// call will return an error instead.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, Poisonable};\n\t///\n\t/// let mutex = Poisonable::new(Mutex::new(0));\n\t/// assert_eq!(mutex.into_inner().unwrap(), 0);\n\t/// ```\n\tpub fn into_inner(self) -\u003e PoisonResult\u003cL::Inner\u003e {\n\t\tLockableIntoInner::into_inner(self)\n\t}\n}\n\nimpl\u003cL: LockableGetMut + RawLock\u003e Poisonable\u003cL\u003e {\n\t/// Returns a mutable reference to the underlying data.\n\t///\n\t/// Since this call borrows the `Poisonable` mutably, no actual locking\n\t/// needs to take place - the mutable borrow statically guarantees no locks\n\t/// exist.\n\t///\n\t/// # Errors\n\t///\n\t/// If another user of this lock panicked while holding the lock, then\n\t/// this call will return an error instead.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut mutex = Poisonable::new(Mutex::new(0));\n\t/// *mutex.get_mut().unwrap() = 10;\n\t/// assert_eq!(*mutex.lock(key).unwrap(), 10);\n\t/// ```\n\tpub fn get_mut(\u0026mut self) -\u003e PoisonResult\u003cL::Inner\u003c'_\u003e\u003e {\n\t\tLockableGetMut::get_mut(self)\n\t}\n}\n\nimpl\u003cL: UnwindSafe\u003e RefUnwindSafe for Poisonable\u003cL\u003e {}\nimpl\u003cL: UnwindSafe\u003e UnwindSafe for Poisonable\u003cL\u003e {}\n","traces":[{"line":20,"address":[],"length":0,"stats":{"Line":0}},{"line":21,"address":[],"length":0,"stats":{"Line":0}},{"line":24,"address":[],"length":0,"stats":{"Line":0}},{"line":25,"address":[],"length":0,"stats":{"Line":0}},{"line":28,"address":[],"length":0,"stats":{"Line":0}},{"line":29,"address":[],"length":0,"stats":{"Line":0}},{"line":32,"address":[],"length":0,"stats":{"Line":0}},{"line":33,"address":[],"length":0,"stats":{"Line":0}},{"line":36,"address":[],"length":0,"stats":{"Line":0}},{"line":37,"address":[],"length":0,"stats":{"Line":0}},{"line":40,"address":[],"length":0,"stats":{"Line":0}},{"line":41,"address":[],"length":0,"stats":{"Line":0}},{"line":56,"address":[],"length":0,"stats":{"Line":3}},{"line":57,"address":[158654,158622],"length":1,"stats":{"Line":3}},{"line":60,"address":[],"length":0,"stats":{"Line":3}},{"line":61,"address":[],"length":0,"stats":{"Line":3}},{"line":63,"address":[158806,158759,158847,159094,159135,158924,159212,159047],"length":1,"stats":{"Line":10}},{"line":64,"address":[],"length":0,"stats":{"Line":2}},{"line":66,"address":[],"length":0,"stats":{"Line":3}},{"line":70,"address":[],"length":0,"stats":{"Line":0}},{"line":71,"address":[],"length":0,"stats":{"Line":0}},{"line":72,"address":[],"length":0,"stats":{"Line":0}},{"line":74,"address":[],"length":0,"stats":{"Line":0}},{"line":90,"address":[],"length":0,"stats":{"Line":0}},{"line":91,"address":[],"length":0,"stats":{"Line":0}},{"line":93,"address":[],"length":0,"stats":{"Line":0}},{"line":94,"address":[],"length":0,"stats":{"Line":0}},{"line":96,"address":[],"length":0,"stats":{"Line":0}},{"line":100,"address":[],"length":0,"stats":{"Line":0}},{"line":101,"address":[],"length":0,"stats":{"Line":0}},{"line":102,"address":[],"length":0,"stats":{"Line":0}},{"line":104,"address":[],"length":0,"stats":{"Line":0}},{"line":119,"address":[159248],"length":1,"stats":{"Line":1}},{"line":120,"address":[],"length":0,"stats":{"Line":2}},{"line":121,"address":[159302],"length":1,"stats":{"Line":1}},{"line":123,"address":[],"length":0,"stats":{"Line":1}},{"line":131,"address":[],"length":0,"stats":{"Line":1}},{"line":132,"address":[159657,159446,159374],"length":1,"stats":{"Line":3}},{"line":133,"address":[],"length":0,"stats":{"Line":2}},{"line":135,"address":[],"length":0,"stats":{"Line":2}},{"line":141,"address":[159680],"length":1,"stats":{"Line":2}},{"line":142,"address":[],"length":0,"stats":{"Line":2}},{"line":156,"address":[],"length":0,"stats":{"Line":13}},{"line":159,"address":[],"length":0,"stats":{"Line":23}},{"line":188,"address":[],"length":0,"stats":{"Line":8}},{"line":189,"address":[],"length":0,"stats":{"Line":8}},{"line":230,"address":[],"length":0,"stats":{"Line":2}},{"line":231,"address":[],"length":0,"stats":{"Line":2}},{"line":252,"address":[160080,160351],"length":1,"stats":{"Line":1}},{"line":253,"address":[],"length":0,"stats":{"Line":4}},{"line":254,"address":[],"length":0,"stats":{"Line":2}},{"line":256,"address":[],"length":0,"stats":{"Line":1}},{"line":280,"address":[],"length":0,"stats":{"Line":0}},{"line":281,"address":[],"length":0,"stats":{"Line":0}},{"line":282,"address":[],"length":0,"stats":{"Line":0}},{"line":284,"address":[],"length":0,"stats":{"Line":0}},{"line":294,"address":[],"length":0,"stats":{"Line":5}},{"line":296,"address":[],"length":0,"stats":{"Line":16}},{"line":300,"address":[],"length":0,"stats":{"Line":16}},{"line":301,"address":[],"length":0,"stats":{"Line":6}},{"line":304,"address":[],"length":0,"stats":{"Line":8}},{"line":316,"address":[],"length":0,"stats":{"Line":0}},{"line":319,"address":[],"length":0,"stats":{"Line":0}},{"line":322,"address":[],"length":0,"stats":{"Line":0}},{"line":326,"address":[],"length":0,"stats":{"Line":0}},{"line":337,"address":[],"length":0,"stats":{"Line":0}},{"line":338,"address":[],"length":0,"stats":{"Line":0}},{"line":342,"address":[],"length":0,"stats":{"Line":0}},{"line":345,"address":[],"length":0,"stats":{"Line":0}},{"line":349,"address":[],"length":0,"stats":{"Line":0}},{"line":384,"address":[],"length":0,"stats":{"Line":8}},{"line":386,"address":[],"length":0,"stats":{"Line":8}},{"line":387,"address":[],"length":0,"stats":{"Line":3}},{"line":435,"address":[],"length":0,"stats":{"Line":0}},{"line":437,"address":[],"length":0,"stats":{"Line":0}},{"line":438,"address":[],"length":0,"stats":{"Line":0}},{"line":440,"address":[],"length":0,"stats":{"Line":0}},{"line":460,"address":[],"length":0,"stats":{"Line":1}},{"line":461,"address":[],"length":0,"stats":{"Line":1}},{"line":462,"address":[],"length":0,"stats":{"Line":0}},{"line":467,"address":[],"length":0,"stats":{"Line":0}},{"line":469,"address":[],"length":0,"stats":{"Line":0}},{"line":473,"address":[],"length":0,"stats":{"Line":0}},{"line":474,"address":[],"length":0,"stats":{"Line":0}},{"line":477,"address":[],"length":0,"stats":{"Line":0}},{"line":487,"address":[],"length":0,"stats":{"Line":0}},{"line":490,"address":[],"length":0,"stats":{"Line":0}},{"line":493,"address":[],"length":0,"stats":{"Line":0}},{"line":497,"address":[],"length":0,"stats":{"Line":0}},{"line":508,"address":[],"length":0,"stats":{"Line":0}},{"line":509,"address":[],"length":0,"stats":{"Line":0}},{"line":513,"address":[],"length":0,"stats":{"Line":0}},{"line":516,"address":[],"length":0,"stats":{"Line":0}},{"line":520,"address":[],"length":0,"stats":{"Line":0}},{"line":557,"address":[],"length":0,"stats":{"Line":0}},{"line":559,"address":[],"length":0,"stats":{"Line":0}},{"line":560,"address":[],"length":0,"stats":{"Line":0}},{"line":601,"address":[],"length":0,"stats":{"Line":0}},{"line":603,"address":[],"length":0,"stats":{"Line":0}},{"line":604,"address":[],"length":0,"stats":{"Line":0}},{"line":606,"address":[],"length":0,"stats":{"Line":0}},{"line":624,"address":[],"length":0,"stats":{"Line":0}},{"line":625,"address":[],"length":0,"stats":{"Line":0}},{"line":626,"address":[],"length":0,"stats":{"Line":0}},{"line":646,"address":[],"length":0,"stats":{"Line":1}},{"line":647,"address":[],"length":0,"stats":{"Line":1}},{"line":673,"address":[],"length":0,"stats":{"Line":1}},{"line":674,"address":[],"length":0,"stats":{"Line":1}}],"covered":41,"coverable":108},{"path":["/","home","botahamec","Projects","happylock","src","poisonable.rs"],"content":"use std::marker::PhantomData;\nuse std::sync::atomic::AtomicBool;\n\nuse crate::ThreadKey;\n\nmod error;\nmod flag;\nmod guard;\nmod poisonable;\n\n/// A flag indicating if a lock is poisoned or not. The implementation differs\n/// depending on whether panics are set to unwind or abort.\n#[derive(Debug, Default)]\npub(crate) struct PoisonFlag(#[cfg(panic = \"unwind\")] AtomicBool);\n\n/// A wrapper around [`Lockable`] types which will enable poisoning.\n///\n/// A lock is \"poisoned\" when the thread panics while holding the lock. Once a\n/// lock is poisoned, all other threads are unable to access the data by\n/// default, because the data may be tainted (some invariant of the data might\n/// not be upheld).\n///\n/// The [`lock`] and [`try_lock`] methods return a [`Result`] which indicates\n/// whether the lock has been poisoned or not. The [`PoisonError`] type has an\n/// [`into_inner`] method which will return the guard that normally would have\n/// been returned for a successful lock. This allows access to the data,\n/// despite the lock being poisoned.\n///\n/// Alternatively, there is also a [`clear_poison`] method, which should\n/// indicate that all invariants of the underlying data are upheld, so that\n/// subsequent calls may still return [`Ok`].\n///\n/// [`Lockable`]: `crate::lockable::Lockable`\n/// [`lock`]: `Poisonable::lock`\n/// [`try_lock`]: `Poisonable::try_lock`\n/// [`into_inner`]: `PoisonError::into_inner`\n/// [`clear_poison`]: `Poisonable::clear_poison`\n#[derive(Debug, Default)]\npub struct Poisonable\u003cL\u003e {\n\tinner: L,\n\tpoisoned: PoisonFlag,\n}\n\n/// An RAII guard for a [`Poisonable`].\n///\n/// This is similar to a [`PoisonGuard`], except that it does not hold a\n/// [`Keyable`]\n///\n/// [`Keyable`]: `crate::Keyable`\npub struct PoisonRef\u003c'a, G\u003e {\n\tguard: G,\n\t#[cfg(panic = \"unwind\")]\n\tflag: \u0026'a PoisonFlag,\n\t_phantom: PhantomData\u003c\u0026'a ()\u003e,\n}\n\n/// An RAII guard for a [`Poisonable`].\n///\n/// This is created by calling methods like [`Poisonable::lock`].\npub struct PoisonGuard\u003c'a, G\u003e {\n\tguard: PoisonRef\u003c'a, G\u003e,\n\tkey: ThreadKey,\n}\n\n/// A type of error which can be returned when acquiring a [`Poisonable`] lock.\npub struct PoisonError\u003cGuard\u003e {\n\tguard: Guard,\n}\n\n/// An enumeration of possible errors associated with\n/// [`TryLockPoisonableResult`] which can occur while trying to acquire a lock\n/// (i.e.: [`Poisonable::try_lock`]).\npub enum TryLockPoisonableError\u003c'flag, G\u003e {\n\tPoisoned(PoisonError\u003cPoisonGuard\u003c'flag, G\u003e\u003e),\n\tWouldBlock(ThreadKey),\n}\n\n/// A type alias for the result of a lock method which can poisoned.\n///\n/// The [`Ok`] variant of this result indicates that the primitive was not\n/// poisoned, and the primitive was poisoned. Note that the [`Err`] variant\n/// *also* carries the associated guard, and it can be acquired through the\n/// [`into_inner`] method.\n///\n/// [`into_inner`]: `PoisonError::into_inner`\npub type PoisonResult\u003cGuard\u003e = Result\u003cGuard, PoisonError\u003cGuard\u003e\u003e;\n\n/// A type alias for the result of a nonblocking locking method.\n///\n/// For more information, see [`PoisonResult`]. A `TryLockPoisonableResult`\n/// doesn't necessarily hold the associated guard in the [`Err`] type as the\n/// lock might not have been acquired for other reasons.\npub type TryLockPoisonableResult\u003c'flag, G\u003e =\n\tResult\u003cPoisonGuard\u003c'flag, G\u003e, TryLockPoisonableError\u003c'flag, G\u003e\u003e;\n\n#[cfg(test)]\nmod tests {\n\tuse std::sync::Arc;\n\n\tuse super::*;\n\tuse crate::lockable::Lockable;\n\tuse crate::{LockCollection, Mutex, ThreadKey};\n\n\t#[test]\n\tfn locking_poisoned_mutex_returns_error_in_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = LockCollection::new(Poisonable::new(Mutex::new(42)));\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet mut guard1 = mutex.lock(key);\n\t\t\t\tlet guard = guard1.as_deref_mut().unwrap();\n\t\t\t\tassert_eq!(**guard, 42);\n\t\t\t\tpanic!();\n\n\t\t\t\t#[allow(unreachable_code)]\n\t\t\t\tdrop(guard1);\n\t\t\t})\n\t\t\t.join()\n\t\t\t.unwrap_err();\n\t\t});\n\n\t\tlet error = mutex.lock(key);\n\t\tlet error = error.as_deref().unwrap_err();\n\t\tassert_eq!(***error.get_ref(), 42);\n\t}\n\n\t#[test]\n\tfn non_poisoned_get_mut_is_ok() {\n\t\tlet mut mutex = Poisonable::new(Mutex::new(42));\n\t\tlet guard = mutex.get_mut();\n\t\tassert!(guard.is_ok());\n\t\tassert_eq!(*guard.unwrap(), 42);\n\t}\n\n\t#[test]\n\tfn non_poisoned_get_mut_is_err() {\n\t\tlet mut mutex = Poisonable::new(Mutex::new(42));\n\n\t\tlet _ = std::panic::catch_unwind(|| {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t#[allow(unused_variables)]\n\t\t\tlet guard = mutex.lock(key);\n\t\t\tpanic!();\n\t\t\t#[allow(unreachable_code)]\n\t\t\tdrop(guard);\n\t\t});\n\n\t\tlet guard = mutex.get_mut();\n\t\tassert!(guard.is_err());\n\t\tassert_eq!(**guard.unwrap_err().get_ref(), 42);\n\t}\n\n\t#[test]\n\tfn unpoisoned_into_inner() {\n\t\tlet mutex = Poisonable::new(Mutex::new(\"foo\"));\n\t\tassert_eq!(mutex.into_inner().unwrap(), \"foo\");\n\t}\n\n\t#[test]\n\tfn poisoned_into_inner() {\n\t\tlet mutex = Poisonable::from(Mutex::new(\"foo\"));\n\n\t\tstd::panic::catch_unwind(|| {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t#[allow(unused_variables)]\n\t\t\tlet guard = mutex.lock(key);\n\t\t\tpanic!();\n\t\t\t#[allow(unreachable_code)]\n\t\t\tdrop(guard);\n\t\t})\n\t\t.unwrap_err();\n\n\t\tlet error = mutex.into_inner().unwrap_err();\n\t\tassert_eq!(error.into_inner(), \"foo\");\n\t}\n\n\t#[test]\n\tfn unpoisoned_into_child() {\n\t\tlet mutex = Poisonable::new(Mutex::new(\"foo\"));\n\t\tassert_eq!(mutex.into_child().unwrap().into_inner(), \"foo\");\n\t}\n\n\t#[test]\n\tfn poisoned_into_child() {\n\t\tlet mutex = Poisonable::from(Mutex::new(\"foo\"));\n\n\t\tstd::panic::catch_unwind(|| {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t#[allow(unused_variables)]\n\t\t\tlet guard = mutex.lock(key);\n\t\t\tpanic!();\n\t\t\t#[allow(unreachable_code)]\n\t\t\tdrop(guard);\n\t\t})\n\t\t.unwrap_err();\n\n\t\tlet error = mutex.into_child().unwrap_err();\n\t\tassert_eq!(error.into_inner().into_inner(), \"foo\");\n\t}\n\n\t#[test]\n\tfn display_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = Poisonable::new(Mutex::new(\"Hello, world!\"));\n\n\t\tlet guard = mutex.lock(key).unwrap();\n\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\");\n\t}\n\n\t#[test]\n\tfn ref_as_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = LockCollection::new(Poisonable::new(Mutex::new(\"foo\")));\n\t\tlet guard = collection.lock(key);\n\t\tlet Ok(ref guard) = guard.as_ref() else {\n\t\t\tpanic!()\n\t\t};\n\t\tassert_eq!(**guard.as_ref(), \"foo\");\n\t}\n\n\t#[test]\n\tfn ref_as_mut() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = LockCollection::new(Poisonable::new(Mutex::new(\"foo\")));\n\t\tlet mut guard1 = collection.lock(key);\n\t\tlet Ok(ref mut guard) = guard1.as_mut() else {\n\t\t\tpanic!()\n\t\t};\n\t\tlet guard = guard.as_mut();\n\t\t**guard = \"bar\";\n\n\t\tlet key = LockCollection::\u003cPoisonable\u003cMutex\u003c_\u003e\u003e\u003e::unlock(guard1);\n\t\tlet guard = collection.lock(key);\n\t\tlet guard = guard.as_deref().unwrap();\n\t\tassert_eq!(*guard.as_ref(), \"bar\");\n\t}\n\n\t#[test]\n\tfn guard_as_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = Poisonable::new(Mutex::new(\"foo\"));\n\t\tlet guard = collection.lock(key);\n\t\tlet Ok(ref guard) = guard.as_ref() else {\n\t\t\tpanic!()\n\t\t};\n\t\tassert_eq!(**guard.as_ref(), \"foo\");\n\t}\n\n\t#[test]\n\tfn guard_as_mut() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = Poisonable::new(Mutex::new(\"foo\"));\n\t\tlet mut guard1 = mutex.lock(key);\n\t\tlet Ok(ref mut guard) = guard1.as_mut() else {\n\t\t\tpanic!()\n\t\t};\n\t\tlet guard = guard.as_mut();\n\t\t**guard = \"bar\";\n\n\t\tlet key = Poisonable::\u003cMutex\u003c_\u003e\u003e::unlock(guard1.unwrap());\n\t\tlet guard = mutex.lock(key);\n\t\tlet guard = guard.as_deref().unwrap();\n\t\tassert_eq!(*guard, \"bar\");\n\t}\n\n\t#[test]\n\tfn deref_mut_in_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = LockCollection::new(Poisonable::new(Mutex::new(42)));\n\t\tlet mut guard1 = collection.lock(key);\n\t\tlet Ok(ref mut guard) = guard1.as_mut() else {\n\t\t\tpanic!()\n\t\t};\n\t\t// TODO make this more convenient\n\t\tassert_eq!(***guard, 42);\n\t\t***guard = 24;\n\n\t\tlet key = LockCollection::\u003cPoisonable\u003cMutex\u003c_\u003e\u003e\u003e::unlock(guard1);\n\t\t_ = collection.lock(key);\n\t}\n\n\t#[test]\n\tfn get_ptrs() {\n\t\tlet mutex = Mutex::new(5);\n\t\tlet poisonable = Poisonable::new(mutex);\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tpoisonable.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tassert!(std::ptr::addr_eq(lock_ptrs[0], \u0026poisonable.inner));\n\t}\n\n\t#[test]\n\tfn clear_poison_for_poisoned_mutex() {\n\t\tlet mutex = Arc::new(Poisonable::new(Mutex::new(0)));\n\t\tlet c_mutex = Arc::clone(\u0026mutex);\n\n\t\tlet _ = std::thread::spawn(move || {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\tlet _lock = c_mutex.lock(key).unwrap();\n\t\t\tpanic!(); // the mutex gets poisoned\n\t\t})\n\t\t.join();\n\n\t\tassert!(mutex.is_poisoned());\n\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet _ = mutex.lock(key).unwrap_or_else(|mut e| {\n\t\t\t**e.get_mut() = 1;\n\t\t\tmutex.clear_poison();\n\t\t\te.into_inner()\n\t\t});\n\n\t\tassert!(!mutex.is_poisoned());\n\t}\n\n\t#[test]\n\tfn error_as_ref() {\n\t\tlet mutex = Poisonable::new(Mutex::new(\"foo\"));\n\n\t\tlet _ = std::panic::catch_unwind(|| {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t#[allow(unused_variables)]\n\t\t\tlet guard = mutex.lock(key);\n\t\t\tpanic!();\n\n\t\t\t#[allow(unknown_lints)]\n\t\t\t#[allow(unreachable_code)]\n\t\t\tdrop(guard);\n\t\t});\n\n\t\tassert!(mutex.is_poisoned());\n\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet error = mutex.lock(key).unwrap_err();\n\t\tassert_eq!(\u0026***error.as_ref(), \"foo\");\n\t}\n\n\t#[test]\n\tfn error_as_mut() {\n\t\tlet mutex = Poisonable::new(Mutex::new(\"foo\"));\n\n\t\tlet _ = std::panic::catch_unwind(|| {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t#[allow(unused_variables)]\n\t\t\tlet guard = mutex.lock(key);\n\t\t\tpanic!();\n\n\t\t\t#[allow(unknown_lints)]\n\t\t\t#[allow(unreachable_code)]\n\t\t\tdrop(guard);\n\t\t});\n\n\t\tassert!(mutex.is_poisoned());\n\n\t\tlet key: ThreadKey = ThreadKey::get().unwrap();\n\t\tlet mut error = mutex.lock(key).unwrap_err();\n\t\tlet error1 = error.as_mut();\n\t\t**error1 = \"bar\";\n\t\tlet key = Poisonable::\u003cMutex\u003c_\u003e\u003e::unlock(error.into_inner());\n\n\t\tmutex.clear_poison();\n\t\tlet guard = mutex.lock(key).unwrap();\n\t\tassert_eq!(\u0026**guard, \"bar\");\n\t}\n\n\t#[test]\n\tfn try_error_from_lock_error() {\n\t\tlet mutex = Poisonable::new(Mutex::new(\"foo\"));\n\n\t\tlet _ = std::panic::catch_unwind(|| {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t#[allow(unused_variables)]\n\t\t\tlet guard = mutex.lock(key);\n\t\t\tpanic!();\n\n\t\t\t#[allow(unknown_lints)]\n\t\t\t#[allow(unreachable_code)]\n\t\t\tdrop(guard);\n\t\t});\n\n\t\tassert!(mutex.is_poisoned());\n\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet error = mutex.lock(key).unwrap_err();\n\t\tlet error = TryLockPoisonableError::from(error);\n\n\t\tlet TryLockPoisonableError::Poisoned(error) = error else {\n\t\t\tpanic!()\n\t\t};\n\t\tassert_eq!(\u0026**error.into_inner(), \"foo\");\n\t}\n\n\t#[test]\n\tfn new_poisonable_is_not_poisoned() {\n\t\tlet mutex = Poisonable::new(Mutex::new(42));\n\t\tassert!(!mutex.is_poisoned());\n\t}\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","src","rwlock","read_guard.rs"],"content":"use std::fmt::{Debug, Display};\nuse std::hash::Hash;\nuse std::marker::PhantomData;\nuse std::ops::Deref;\n\nuse lock_api::RawRwLock;\n\nuse crate::lockable::RawLock;\nuse crate::ThreadKey;\n\nuse super::{RwLock, RwLockReadGuard, RwLockReadRef};\n\n// These impls make things slightly easier because now you can use\n// `println!(\"{guard}\")` instead of `println!(\"{}\", *guard)`\n\n#[mutants::skip] // hashing involves PRNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Hash + ?Sized, R: RawRwLock\u003e Hash for RwLockReadRef\u003c'_, T, R\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.deref().hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug + ?Sized, R: RawRwLock\u003e Debug for RwLockReadRef\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: Display + ?Sized, R: RawRwLock\u003e Display for RwLockReadRef\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e Deref for RwLockReadRef\u003c'_, T, R\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t// safety: this is the only type that can use `value`, and there's\n\t\t// a reference to this type, so there cannot be any mutable\n\t\t// references to this value.\n\t\tunsafe { \u0026*self.0.data.get() }\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e AsRef\u003cT\u003e for RwLockReadRef\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e Drop for RwLockReadRef\u003c'_, T, R\u003e {\n\tfn drop(\u0026mut self) {\n\t\t// safety: this guard is being destroyed, so the data cannot be\n\t\t// accessed without locking again\n\t\tunsafe { self.0.raw_unlock_read() }\n\t}\n}\n\nimpl\u003c'a, T: ?Sized, R: RawRwLock\u003e RwLockReadRef\u003c'a, T, R\u003e {\n\t/// Creates an immutable reference for the underlying data of an [`RwLock`]\n\t/// without locking it or taking ownership of the key.\n\t#[must_use]\n\tpub(crate) unsafe fn new(mutex: \u0026'a RwLock\u003cT, R\u003e) -\u003e Self {\n\t\tSelf(mutex, PhantomData)\n\t}\n}\n\n#[mutants::skip] // hashing involves PRNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Hash + ?Sized, R: RawRwLock\u003e Hash for RwLockReadGuard\u003c'_, T, R\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.deref().hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug + ?Sized, R: RawRwLock\u003e Debug for RwLockReadGuard\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: Display + ?Sized, R: RawRwLock\u003e Display for RwLockReadGuard\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e Deref for RwLockReadGuard\u003c'_, T, R\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t\u0026self.rwlock\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e AsRef\u003cT\u003e for RwLockReadGuard\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself\n\t}\n}\n\nimpl\u003c'a, T: ?Sized, R: RawRwLock\u003e RwLockReadGuard\u003c'a, T, R\u003e {\n\t/// Create a guard to the given mutex. Undefined if multiple guards to the\n\t/// same mutex exist at once.\n\t#[must_use]\n\tpub(super) unsafe fn new(rwlock: \u0026'a RwLock\u003cT, R\u003e, thread_key: ThreadKey) -\u003e Self {\n\t\tSelf {\n\t\t\trwlock: RwLockReadRef(rwlock, PhantomData),\n\t\t\tthread_key,\n\t\t}\n\t}\n}\n\nunsafe impl\u003cT: ?Sized + Sync, R: RawRwLock + Sync\u003e Sync for RwLockReadRef\u003c'_, T, R\u003e {}\n","traces":[{"line":33,"address":[715888],"length":1,"stats":{"Line":1}},{"line":34,"address":[],"length":0,"stats":{"Line":1}},{"line":41,"address":[],"length":0,"stats":{"Line":3}},{"line":45,"address":[],"length":0,"stats":{"Line":3}},{"line":50,"address":[],"length":0,"stats":{"Line":0}},{"line":51,"address":[],"length":0,"stats":{"Line":0}},{"line":56,"address":[168880,168896,168912],"length":1,"stats":{"Line":3}},{"line":59,"address":[],"length":0,"stats":{"Line":3}},{"line":67,"address":[223760,223776,223744],"length":1,"stats":{"Line":3}},{"line":68,"address":[],"length":0,"stats":{"Line":0}},{"line":89,"address":[],"length":0,"stats":{"Line":1}},{"line":90,"address":[],"length":0,"stats":{"Line":1}},{"line":97,"address":[],"length":0,"stats":{"Line":1}},{"line":98,"address":[],"length":0,"stats":{"Line":1}},{"line":103,"address":[],"length":0,"stats":{"Line":0}},{"line":104,"address":[],"length":0,"stats":{"Line":0}},{"line":112,"address":[],"length":0,"stats":{"Line":3}},{"line":114,"address":[],"length":0,"stats":{"Line":0}}],"covered":12,"coverable":18},{"path":["/","home","botahamec","Projects","happylock","src","rwlock","read_lock.rs"],"content":"use std::fmt::Debug;\n\nuse lock_api::RawRwLock;\n\nuse crate::lockable::{Lockable, RawLock, Sharable};\nuse crate::ThreadKey;\n\nuse super::{ReadLock, RwLock, RwLockReadGuard, RwLockReadRef};\n\nunsafe impl\u003cT: Send, R: RawRwLock + Send + Sync\u003e Lockable for ReadLock\u003c'_, T, R\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= RwLockReadRef\u003c'g, T, R\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= \u0026'a T\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tptrs.push(self.as_ref());\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tRwLockReadRef::new(self.as_ref())\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.0.data_ref()\n\t}\n}\n\nunsafe impl\u003cT: Send, R: RawRwLock + Send + Sync\u003e Sharable for ReadLock\u003c'_, T, R\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= RwLockReadRef\u003c'g, T, R\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= \u0026'a T\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tRwLockReadRef::new(self.as_ref())\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tself.0.data_ref()\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: ?Sized + Debug, R: RawRwLock\u003e Debug for ReadLock\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t// safety: this is just a try lock, and the value is dropped\n\t\t// immediately after, so there's no risk of blocking ourselves\n\t\t// or any other threads\n\t\tif let Some(value) = unsafe { self.try_lock_no_key() } {\n\t\t\tf.debug_struct(\"ReadLock\").field(\"data\", \u0026\u0026*value).finish()\n\t\t} else {\n\t\t\tstruct LockedPlaceholder;\n\t\t\timpl Debug for LockedPlaceholder {\n\t\t\t\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t\t\t\tf.write_str(\"\u003clocked\u003e\")\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tf.debug_struct(\"ReadLock\")\n\t\t\t\t.field(\"data\", \u0026LockedPlaceholder)\n\t\t\t\t.finish()\n\t\t}\n\t}\n}\n\nimpl\u003c'l, T, R\u003e From\u003c\u0026'l RwLock\u003cT, R\u003e\u003e for ReadLock\u003c'l, T, R\u003e {\n\tfn from(value: \u0026'l RwLock\u003cT, R\u003e) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\nimpl\u003cT: ?Sized, R\u003e AsRef\u003cRwLock\u003cT, R\u003e\u003e for ReadLock\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026RwLock\u003cT, R\u003e {\n\t\tself.0\n\t}\n}\n\nimpl\u003c'l, T, R\u003e ReadLock\u003c'l, T, R\u003e {\n\t/// Creates a new `ReadLock` which accesses the given [`RwLock`]\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{rwlock::ReadLock, RwLock};\n\t///\n\t/// let lock = RwLock::new(5);\n\t/// let read_lock = ReadLock::new(\u0026lock);\n\t/// ```\n\t#[must_use]\n\tpub const fn new(rwlock: \u0026'l RwLock\u003cT, R\u003e) -\u003e Self {\n\t\tSelf(rwlock)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e ReadLock\u003c'_, T, R\u003e {\n\t/// Locks the underlying [`RwLock`] with shared read access, blocking the\n\t/// current thread until it can be acquired.\n\t///\n\t/// The calling thread will be blocked until there are no more writers\n\t/// which hold the lock. There may be other readers currently inside the\n\t/// lock when this method returns.\n\t///\n\t/// Returns an RAII guard which will release this thread's shared access\n\t/// once it is dropped.\n\t///\n\t/// Because this method takes a [`ThreadKey`], it's not possible for this\n\t/// method to cause a deadlock.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::rwlock::ReadLock;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock: RwLock\u003c_\u003e = RwLock::new(1);\n\t/// let reader = ReadLock::new(\u0026lock);\n\t///\n\t/// let n = reader.lock(key);\n\t/// assert_eq!(*n, 1);\n\t/// ```\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\t#[must_use]\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e RwLockReadGuard\u003c'_, T, R\u003e {\n\t\tself.0.read(key)\n\t}\n\n\t/// Attempts to acquire the underlying [`RwLock`] with shared read access\n\t/// without blocking.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// shared access when it is dropped.\n\t///\n\t/// This function does not provide any guarantees with respect to the\n\t/// ordering of whether contentious readers or writers will acquire the\n\t/// lock first.\n\t///\n\t/// # Errors\n\t///\n\t/// If the `RwLock` could not be acquired because it was already locked\n\t/// exclusively, then an error will be returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::rwlock::ReadLock;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(1);\n\t/// let reader = ReadLock::new(\u0026lock);\n\t///\n\t/// match reader.try_lock(key) {\n\t/// Ok(n) =\u003e assert_eq!(*n, 1),\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t/// ```\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e Result\u003cRwLockReadGuard\u003c'_, T, R\u003e, ThreadKey\u003e {\n\t\tself.0.try_read(key)\n\t}\n\n\t/// Attempts to create an exclusive lock without a key. Locking this\n\t/// without exclusive access to the key is undefined behavior.\n\tpub(crate) unsafe fn try_lock_no_key(\u0026self) -\u003e Option\u003cRwLockReadRef\u003c'_, T, R\u003e\u003e {\n\t\tself.0.try_read_no_key()\n\t}\n\n\t/// Immediately drops the guard, and consequently releases the shared lock\n\t/// on the underlying [`RwLock`].\n\t///\n\t/// This function is equivalent to calling [`drop`] on the guard, except\n\t/// that it returns the key that was used to create it. Alternately, the\n\t/// guard will be automatically dropped when it goes out of scope.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::rwlock::ReadLock;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(0);\n\t/// let reader = ReadLock::new(\u0026lock);\n\t///\n\t/// let mut guard = reader.lock(key);\n\t/// assert_eq!(*guard, 0);\n\t/// let key = ReadLock::unlock(guard);\n\t/// ```\n\t#[must_use]\n\tpub fn unlock(guard: RwLockReadGuard\u003c'_, T, R\u003e) -\u003e ThreadKey {\n\t\tRwLock::unlock_read(guard)\n\t}\n}\n","traces":[{"line":21,"address":[],"length":0,"stats":{"Line":1}},{"line":22,"address":[],"length":0,"stats":{"Line":1}},{"line":25,"address":[],"length":0,"stats":{"Line":0}},{"line":26,"address":[],"length":0,"stats":{"Line":0}},{"line":29,"address":[],"length":0,"stats":{"Line":0}},{"line":30,"address":[],"length":0,"stats":{"Line":0}},{"line":45,"address":[],"length":0,"stats":{"Line":0}},{"line":46,"address":[],"length":0,"stats":{"Line":0}},{"line":49,"address":[],"length":0,"stats":{"Line":0}},{"line":50,"address":[],"length":0,"stats":{"Line":0}},{"line":79,"address":[],"length":0,"stats":{"Line":1}},{"line":80,"address":[],"length":0,"stats":{"Line":1}},{"line":85,"address":[],"length":0,"stats":{"Line":1}},{"line":86,"address":[],"length":0,"stats":{"Line":1}},{"line":102,"address":[],"length":0,"stats":{"Line":3}},{"line":103,"address":[],"length":0,"stats":{"Line":0}},{"line":139,"address":[],"length":0,"stats":{"Line":1}},{"line":140,"address":[],"length":0,"stats":{"Line":1}},{"line":174,"address":[],"length":0,"stats":{"Line":1}},{"line":175,"address":[],"length":0,"stats":{"Line":1}},{"line":180,"address":[],"length":0,"stats":{"Line":0}},{"line":181,"address":[],"length":0,"stats":{"Line":0}},{"line":206,"address":[],"length":0,"stats":{"Line":0}},{"line":207,"address":[],"length":0,"stats":{"Line":0}}],"covered":11,"coverable":24},{"path":["/","home","botahamec","Projects","happylock","src","rwlock","rwlock.rs"],"content":"use std::cell::UnsafeCell;\nuse std::fmt::Debug;\nuse std::marker::PhantomData;\nuse std::panic::AssertUnwindSafe;\n\nuse lock_api::RawRwLock;\n\nuse crate::handle_unwind::handle_unwind;\nuse crate::lockable::{\n\tLockable, LockableGetMut, LockableIntoInner, OwnedLockable, RawLock, Sharable,\n};\nuse crate::{Keyable, ThreadKey};\n\nuse super::{PoisonFlag, RwLock, RwLockReadGuard, RwLockReadRef, RwLockWriteGuard, RwLockWriteRef};\n\nunsafe impl\u003cT: ?Sized, R: RawRwLock\u003e RawLock for RwLock\u003cT, R\u003e {\n\tfn poison(\u0026self) {\n\t\tself.poison.poison();\n\t}\n\n\tunsafe fn raw_lock(\u0026self) {\n\t\tassert!(\n\t\t\t!self.poison.is_poisoned(),\n\t\t\t\"The read-write lock has been killed\"\n\t\t);\n\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.lock_exclusive(), || self.poison())\n\t}\n\n\tunsafe fn raw_try_lock(\u0026self) -\u003e bool {\n\t\tif self.poison.is_poisoned() {\n\t\t\treturn false;\n\t\t}\n\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.try_lock_exclusive(), || self.poison())\n\t}\n\n\tunsafe fn raw_unlock(\u0026self) {\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.unlock_exclusive(), || self.poison())\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tassert!(\n\t\t\t!self.poison.is_poisoned(),\n\t\t\t\"The read-write lock has been killed\"\n\t\t);\n\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.lock_shared(), || self.poison())\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tif self.poison.is_poisoned() {\n\t\t\treturn false;\n\t\t}\n\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.try_lock_shared(), || self.poison())\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.unlock_shared(), || self.poison())\n\t}\n}\n\nunsafe impl\u003cT: Send, R: RawRwLock + Send + Sync\u003e Lockable for RwLock\u003cT, R\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= RwLockWriteRef\u003c'g, T, R\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= \u0026'a mut T\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tptrs.push(self);\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tRwLockWriteRef::new(self)\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.data.get().as_mut().unwrap_unchecked()\n\t}\n}\n\nunsafe impl\u003cT: Send, R: RawRwLock + Send + Sync\u003e Sharable for RwLock\u003cT, R\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= RwLockReadRef\u003c'g, T, R\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= \u0026'a T\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tRwLockReadRef::new(self)\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tself.data.get().as_ref().unwrap_unchecked()\n\t}\n}\n\nunsafe impl\u003cT: Send, R: RawRwLock + Send + Sync\u003e OwnedLockable for RwLock\u003cT, R\u003e {}\n\nimpl\u003cT: Send, R: RawRwLock + Send + Sync\u003e LockableIntoInner for RwLock\u003cT, R\u003e {\n\ttype Inner = T;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tself.into_inner()\n\t}\n}\n\nimpl\u003cT: Send, R: RawRwLock + Send + Sync\u003e LockableGetMut for RwLock\u003cT, R\u003e {\n\ttype Inner\u003c'a\u003e\n\t\t= \u0026'a mut T\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tAsMut::as_mut(self)\n\t}\n}\n\nimpl\u003cT, R: RawRwLock\u003e RwLock\u003cT, R\u003e {\n\t/// Creates a new instance of an `RwLock\u003cT\u003e` which is unlocked.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::RwLock;\n\t///\n\t/// let lock = RwLock::new(5);\n\t/// ```\n\t#[must_use]\n\tpub const fn new(data: T) -\u003e Self {\n\t\tSelf {\n\t\t\tdata: UnsafeCell::new(data),\n\t\t\tpoison: PoisonFlag::new(),\n\t\t\traw: R::INIT,\n\t\t}\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: ?Sized + Debug, R: RawRwLock\u003e Debug for RwLock\u003cT, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t// safety: this is just a try lock, and the value is dropped\n\t\t// immediately after, so there's no risk of blocking ourselves\n\t\t// or any other threads\n\t\tif let Some(value) = unsafe { self.try_read_no_key() } {\n\t\t\tf.debug_struct(\"RwLock\").field(\"data\", \u0026\u0026*value).finish()\n\t\t} else {\n\t\t\tstruct LockedPlaceholder;\n\t\t\timpl Debug for LockedPlaceholder {\n\t\t\t\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t\t\t\tf.write_str(\"\u003clocked\u003e\")\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tf.debug_struct(\"RwLock\")\n\t\t\t\t.field(\"data\", \u0026LockedPlaceholder)\n\t\t\t\t.finish()\n\t\t}\n\t}\n}\n\nimpl\u003cT: Default, R: RawRwLock\u003e Default for RwLock\u003cT, R\u003e {\n\tfn default() -\u003e Self {\n\t\tSelf::new(T::default())\n\t}\n}\n\nimpl\u003cT, R: RawRwLock\u003e From\u003cT\u003e for RwLock\u003cT, R\u003e {\n\tfn from(value: T) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\n// We don't need a `get_mut` because we don't have mutex poisoning. Hurray!\n// This is safe because you can't have a mutable reference to the lock if it's\n// locked. Being locked requires an immutable reference because of the guard.\nimpl\u003cT: ?Sized, R\u003e AsMut\u003cT\u003e for RwLock\u003cT, R\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself.data.get_mut()\n\t}\n}\n\nimpl\u003cT, R\u003e RwLock\u003cT, R\u003e {\n\t/// Consumes this `RwLock`, returning the underlying data.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t///\n\t/// let lock = RwLock::new(String::new());\n\t/// {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut s = lock.write(key);\n\t/// *s = \"modified\".to_owned();\n\t/// }\n\t/// assert_eq!(lock.into_inner(), \"modified\");\n\t/// ```\n\t#[must_use]\n\tpub fn into_inner(self) -\u003e T {\n\t\tself.data.into_inner()\n\t}\n}\n\nimpl\u003cT: ?Sized, R\u003e RwLock\u003cT, R\u003e {\n\t/// Returns a mutable reference to the underlying data.\n\t///\n\t/// Since this call borrows `RwLock` mutably, no actual locking is taking\n\t/// place. The mutable borrow statically guarantees that no locks exist.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, RwLock};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut mutex = RwLock::new(0);\n\t/// *mutex.get_mut() = 10;\n\t/// assert_eq!(*mutex.read(key), 10);\n\t/// ```\n\t#[must_use]\n\tpub fn get_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself.data.get_mut()\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e RwLock\u003cT, R\u003e {\n\tpub fn scoped_read\u003cRet\u003e(\u0026self, key: impl Keyable, f: impl Fn(\u0026T) -\u003e Ret) -\u003e Ret {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_read();\n\n\t\t\t// safety: the rwlock was just locked\n\t\t\tlet r = f(self.data.get().as_ref().unwrap_unchecked());\n\n\t\t\t// safety: the rwlock is already locked\n\t\t\tself.raw_unlock_read();\n\n\t\t\tdrop(key); // ensure the key stays valid for long enough\n\n\t\t\tr\n\t\t}\n\t}\n\n\tpub fn scoped_try_read\u003cKey: Keyable, Ret\u003e(\n\t\t\u0026self,\n\t\tkey: Key,\n\t\tf: impl Fn(\u0026T) -\u003e Ret,\n\t) -\u003e Result\u003cRet, Key\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_read() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: the rwlock was just locked\n\t\t\tlet r = f(self.data.get().as_ref().unwrap_unchecked());\n\n\t\t\t// safety: the rwlock is already locked\n\t\t\tself.raw_unlock_read();\n\n\t\t\tdrop(key); // ensure the key stays valid for long enough\n\n\t\t\tOk(r)\n\t\t}\n\t}\n\n\tpub fn scoped_write\u003cRet\u003e(\u0026self, key: impl Keyable, f: impl Fn(\u0026mut T) -\u003e Ret) -\u003e Ret {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_lock();\n\n\t\t\t// safety: we just locked the rwlock\n\t\t\tlet r = f(self.data.get().as_mut().unwrap_unchecked());\n\n\t\t\t// safety: the rwlock is already locked\n\t\t\tself.raw_unlock();\n\n\t\t\tdrop(key); // ensure the key stays valid for long enough\n\n\t\t\tr\n\t\t}\n\t}\n\n\tpub fn scoped_try_write\u003cKey: Keyable, Ret\u003e(\n\t\t\u0026self,\n\t\tkey: Key,\n\t\tf: impl Fn(\u0026mut T) -\u003e Ret,\n\t) -\u003e Result\u003cRet, Key\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_lock() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: the rwlock was just locked\n\t\t\tlet r = f(self.data.get().as_mut().unwrap_unchecked());\n\n\t\t\t// safety: the rwlock is already locked\n\t\t\tself.raw_unlock();\n\n\t\t\tdrop(key); // ensure the key stays valid for long enough\n\n\t\t\tOk(r)\n\t\t}\n\t}\n\n\t/// Locks this `RwLock` with shared read access, blocking the current\n\t/// thread until it can be acquired.\n\t///\n\t/// The calling thread will be blocked until there are no more writers\n\t/// which hold the lock. There may be other readers currently inside the\n\t/// lock when this method returns.\n\t///\n\t/// Returns an RAII guard which will release this thread's shared access\n\t/// once it is dropped.\n\t///\n\t/// Because this method takes a [`ThreadKey`], it's not possible for this\n\t/// method to cause a deadlock.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t/// use happylock::{RwLock, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = Arc::new(RwLock::new(1));\n\t/// let c_lock = Arc::clone(\u0026lock);\n\t///\n\t/// let n = lock.read(key);\n\t/// assert_eq!(*n, 1);\n\t///\n\t/// thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let r = c_lock.read(key);\n\t/// }).join().unwrap();\n\t/// ```\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\tpub fn read(\u0026self, key: ThreadKey) -\u003e RwLockReadGuard\u003c'_, T, R\u003e {\n\t\tunsafe {\n\t\t\tself.raw_read();\n\n\t\t\t// safety: the lock is locked first\n\t\t\tRwLockReadGuard::new(self, key)\n\t\t}\n\t}\n\n\t/// Attempts to acquire this `RwLock` with shared read access without\n\t/// blocking.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// shared access when it is dropped.\n\t///\n\t/// This function does not provide any guarantees with respect to the\n\t/// ordering of whether contentious readers or writers will acquire the\n\t/// lock first.\n\t///\n\t/// # Errors\n\t///\n\t/// If the `RwLock` could not be acquired because it was already locked\n\t/// exclusively, then an error will be returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(1);\n\t///\n\t/// match lock.try_read(key) {\n\t/// Ok(n) =\u003e assert_eq!(*n, 1),\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t/// ```\n\tpub fn try_read(\u0026self, key: ThreadKey) -\u003e Result\u003cRwLockReadGuard\u003c'_, T, R\u003e, ThreadKey\u003e {\n\t\tunsafe {\n\t\t\tif self.raw_try_read() {\n\t\t\t\t// safety: the lock is locked first\n\t\t\t\tOk(RwLockReadGuard::new(self, key))\n\t\t\t} else {\n\t\t\t\tErr(key)\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Attempts to create a shared lock without a key. Locking this without\n\t/// exclusive access to the key is undefined behavior.\n\tpub(crate) unsafe fn try_read_no_key(\u0026self) -\u003e Option\u003cRwLockReadRef\u003c'_, T, R\u003e\u003e {\n\t\tif self.raw_try_read() {\n\t\t\t// safety: the lock is locked first\n\t\t\tSome(RwLockReadRef(self, PhantomData))\n\t\t} else {\n\t\t\tNone\n\t\t}\n\t}\n\n\t/// Attempts to create an exclusive lock without a key. Locking this\n\t/// without exclusive access to the key is undefined behavior.\n\t#[cfg(test)]\n\tpub(crate) unsafe fn try_write_no_key(\u0026self) -\u003e Option\u003cRwLockWriteRef\u003c'_, T, R\u003e\u003e {\n\t\tif self.raw_try_lock() {\n\t\t\t// safety: the lock is locked first\n\t\t\tSome(RwLockWriteRef(self, PhantomData))\n\t\t} else {\n\t\t\tNone\n\t\t}\n\t}\n\n\t/// Locks this `RwLock` with exclusive write access, blocking the current\n\t/// until it can be acquired.\n\t///\n\t/// This function will not return while other writers or readers currently\n\t/// have access to the lock.\n\t///\n\t/// Returns an RAII guard which will drop the write access of this `RwLock`\n\t/// when dropped.\n\t///\n\t/// Because this method takes a [`ThreadKey`], it's not possible for this\n\t/// method to cause a deadlock.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, RwLock};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(1);\n\t///\n\t/// match lock.try_write(key) {\n\t/// Ok(n) =\u003e assert_eq!(*n, 1),\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t/// ```\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\tpub fn write(\u0026self, key: ThreadKey) -\u003e RwLockWriteGuard\u003c'_, T, R\u003e {\n\t\tunsafe {\n\t\t\tself.raw_lock();\n\n\t\t\t// safety: the lock is locked first\n\t\t\tRwLockWriteGuard::new(self, key)\n\t\t}\n\t}\n\n\t/// Attempts to lock this `RwLock` with exclusive write access.\n\t///\n\t/// This function does not block. If the lock could not be acquired at this\n\t/// time, then `None` is returned. Otherwise, an RAII guard is returned\n\t/// which will release the lock when it is dropped.\n\t///\n\t/// This function does not provide any guarantees with respect to the\n\t/// ordering of whether contentious readers or writers will acquire the\n\t/// lock first.\n\t///\n\t/// # Errors\n\t///\n\t/// If the `RwLock` could not be acquired because it was already locked,\n\t/// then an error will be returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(1);\n\t///\n\t/// let n = lock.read(key);\n\t/// assert_eq!(*n, 1);\n\t/// ```\n\tpub fn try_write(\u0026self, key: ThreadKey) -\u003e Result\u003cRwLockWriteGuard\u003c'_, T, R\u003e, ThreadKey\u003e {\n\t\tunsafe {\n\t\t\tif self.raw_try_lock() {\n\t\t\t\t// safety: the lock is locked first\n\t\t\t\tOk(RwLockWriteGuard::new(self, key))\n\t\t\t} else {\n\t\t\t\tErr(key)\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Returns `true` if the rwlock is currently locked in any way\n\t#[cfg(test)]\n\tpub(crate) fn is_locked(\u0026self) -\u003e bool {\n\t\tself.raw.is_locked()\n\t}\n\n\t/// Immediately drops the guard, and consequently releases the shared lock.\n\t///\n\t/// This function is equivalent to calling [`drop`] on the guard, except\n\t/// that it returns the key that was used to create it. Alternately, the\n\t/// guard will be automatically dropped when it goes out of scope.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(0);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// assert_eq!(*guard, 0);\n\t/// let key = RwLock::unlock_read(guard);\n\t/// ```\n\t#[must_use]\n\tpub fn unlock_read(guard: RwLockReadGuard\u003c'_, T, R\u003e) -\u003e ThreadKey {\n\t\tunsafe {\n\t\t\tguard.rwlock.0.raw_unlock_read();\n\t\t}\n\t\tguard.thread_key\n\t}\n\n\t/// Immediately drops the guard, and consequently releases the exclusive\n\t/// lock.\n\t///\n\t/// This function is equivalent to calling [`drop`] on the guard, except\n\t/// that it returns the key that was used to create it. Alternately, the\n\t/// guard will be automatically dropped when it goes out of scope.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(0);\n\t///\n\t/// let mut guard = lock.write(key);\n\t/// *guard += 20;\n\t/// let key = RwLock::unlock_write(guard);\n\t/// ```\n\t#[must_use]\n\tpub fn unlock_write(guard: RwLockWriteGuard\u003c'_, T, R\u003e) -\u003e ThreadKey {\n\t\tunsafe {\n\t\t\tguard.rwlock.0.raw_unlock();\n\t\t}\n\t\tguard.thread_key\n\t}\n}\n\nunsafe impl\u003cR: RawRwLock + Send, T: ?Sized + Send\u003e Send for RwLock\u003cT, R\u003e {}\nunsafe impl\u003cR: RawRwLock + Sync, T: ?Sized + Send\u003e Sync for RwLock\u003cT, R\u003e {}\n","traces":[{"line":17,"address":[179264,179296],"length":1,"stats":{"Line":5}},{"line":18,"address":[161541],"length":1,"stats":{"Line":5}},{"line":21,"address":[157536,157648],"length":1,"stats":{"Line":6}},{"line":22,"address":[713214,713102],"length":1,"stats":{"Line":4}},{"line":23,"address":[],"length":0,"stats":{"Line":0}},{"line":24,"address":[],"length":0,"stats":{"Line":0}},{"line":28,"address":[179361,179473],"length":1,"stats":{"Line":7}},{"line":29,"address":[180400,180453,180389,180448,180405,180437,180384,180432],"length":1,"stats":{"Line":23}},{"line":32,"address":[225968,225888,226048],"length":1,"stats":{"Line":11}},{"line":33,"address":[713406,713326],"length":1,"stats":{"Line":11}},{"line":34,"address":[179016,178936],"length":1,"stats":{"Line":5}},{"line":38,"address":[157201,157121],"length":1,"stats":{"Line":8}},{"line":39,"address":[216432,216501,216437,216480,216448,216496,216453,216485],"length":1,"stats":{"Line":26}},{"line":42,"address":[157024,157056],"length":1,"stats":{"Line":8}},{"line":44,"address":[157036,157068],"length":1,"stats":{"Line":8}},{"line":45,"address":[216341,216421,216352,216416,216336,216389,216357,216384],"length":1,"stats":{"Line":28}},{"line":48,"address":[161680],"length":1,"stats":{"Line":5}},{"line":49,"address":[179717,179605],"length":1,"stats":{"Line":5}},{"line":50,"address":[],"length":0,"stats":{"Line":0}},{"line":51,"address":[],"length":0,"stats":{"Line":0}},{"line":55,"address":[],"length":0,"stats":{"Line":6}},{"line":56,"address":[226898,227110,226998],"length":1,"stats":{"Line":20}},{"line":59,"address":[157248,157328],"length":1,"stats":{"Line":7}},{"line":60,"address":[],"length":0,"stats":{"Line":7}},{"line":61,"address":[],"length":0,"stats":{"Line":2}},{"line":65,"address":[161457],"length":1,"stats":{"Line":7}},{"line":66,"address":[197680,197685,197701,197648,197733,197749,197696,197744,197616,197653,197728,197621],"length":1,"stats":{"Line":26}},{"line":69,"address":[713920,713952],"length":1,"stats":{"Line":5}},{"line":71,"address":[157452,157420],"length":1,"stats":{"Line":7}},{"line":72,"address":[180357,180293,180352,180341,180304,180336,180288,180309],"length":1,"stats":{"Line":25}},{"line":87,"address":[714048,713984],"length":1,"stats":{"Line":19}},{"line":88,"address":[714073,714009],"length":1,"stats":{"Line":19}},{"line":91,"address":[714112],"length":1,"stats":{"Line":3}},{"line":92,"address":[179797,179781],"length":1,"stats":{"Line":3}},{"line":95,"address":[],"length":0,"stats":{"Line":0}},{"line":96,"address":[],"length":0,"stats":{"Line":0}},{"line":111,"address":[714144,714128],"length":1,"stats":{"Line":3}},{"line":112,"address":[],"length":0,"stats":{"Line":3}},{"line":115,"address":[],"length":0,"stats":{"Line":0}},{"line":116,"address":[],"length":0,"stats":{"Line":0}},{"line":125,"address":[],"length":0,"stats":{"Line":0}},{"line":126,"address":[],"length":0,"stats":{"Line":0}},{"line":136,"address":[],"length":0,"stats":{"Line":0}},{"line":137,"address":[],"length":0,"stats":{"Line":0}},{"line":152,"address":[178601,178560,178688,178729],"length":1,"stats":{"Line":18}},{"line":154,"address":[],"length":0,"stats":{"Line":0}},{"line":155,"address":[178646,178714,178586,178774],"length":1,"stats":{"Line":36}},{"line":186,"address":[],"length":0,"stats":{"Line":0}},{"line":187,"address":[],"length":0,"stats":{"Line":0}},{"line":192,"address":[],"length":0,"stats":{"Line":1}},{"line":193,"address":[],"length":0,"stats":{"Line":1}},{"line":201,"address":[],"length":0,"stats":{"Line":0}},{"line":202,"address":[],"length":0,"stats":{"Line":0}},{"line":223,"address":[],"length":0,"stats":{"Line":0}},{"line":224,"address":[],"length":0,"stats":{"Line":0}},{"line":245,"address":[],"length":0,"stats":{"Line":0}},{"line":246,"address":[],"length":0,"stats":{"Line":0}},{"line":251,"address":[],"length":0,"stats":{"Line":0}},{"line":254,"address":[],"length":0,"stats":{"Line":0}},{"line":257,"address":[],"length":0,"stats":{"Line":0}},{"line":260,"address":[],"length":0,"stats":{"Line":0}},{"line":264,"address":[],"length":0,"stats":{"Line":0}},{"line":268,"address":[155680,156496,155136,155408,155952,156749,156224,155389,156205,156477,155933,155661],"length":1,"stats":{"Line":6}},{"line":275,"address":[156566,156022,155206,155478,155750,155432,155160,155976,156520,155704,156248,156294],"length":1,"stats":{"Line":12}},{"line":276,"address":[155217,155489,156033,156305,156577,155761],"length":1,"stats":{"Line":2}},{"line":280,"address":[],"length":0,"stats":{"Line":8}},{"line":283,"address":[155867,155595,156683,156139,156411,155323],"length":1,"stats":{"Line":4}},{"line":285,"address":[156444,156172,156716,155356,155628,155900],"length":1,"stats":{"Line":4}},{"line":287,"address":[155640,155912,156728,155368,156184,156456],"length":1,"stats":{"Line":4}},{"line":291,"address":[],"length":0,"stats":{"Line":0}},{"line":294,"address":[],"length":0,"stats":{"Line":0}},{"line":297,"address":[],"length":0,"stats":{"Line":0}},{"line":300,"address":[],"length":0,"stats":{"Line":0}},{"line":304,"address":[],"length":0,"stats":{"Line":0}},{"line":308,"address":[224317,224608,225133,225152,224861,224880,225405,224589,224064,223792,224336,224045],"length":1,"stats":{"Line":12}},{"line":315,"address":[176952,178086,177814,177768,176998,177224,177270,178358,177542,177496,178040,178312],"length":1,"stats":{"Line":24}},{"line":316,"address":[],"length":0,"stats":{"Line":6}},{"line":320,"address":[],"length":0,"stats":{"Line":12}},{"line":323,"address":[224523,223979,225067,225339,224251,224795],"length":1,"stats":{"Line":6}},{"line":325,"address":[224012,224828,225372,224284,225100,224556],"length":1,"stats":{"Line":6}},{"line":327,"address":[224296,225384,225112,224840,224024,224568],"length":1,"stats":{"Line":6}},{"line":365,"address":[],"length":0,"stats":{"Line":2}},{"line":367,"address":[],"length":0,"stats":{"Line":2}},{"line":370,"address":[],"length":0,"stats":{"Line":2}},{"line":403,"address":[],"length":0,"stats":{"Line":1}},{"line":405,"address":[714638,714750,714688,714708],"length":1,"stats":{"Line":3}},{"line":407,"address":[714745,714715],"length":1,"stats":{"Line":2}},{"line":409,"address":[],"length":0,"stats":{"Line":0}},{"line":416,"address":[],"length":0,"stats":{"Line":2}},{"line":417,"address":[714797,714815],"length":1,"stats":{"Line":2}},{"line":419,"address":[714821],"length":1,"stats":{"Line":2}},{"line":421,"address":[],"length":0,"stats":{"Line":0}},{"line":428,"address":[],"length":0,"stats":{"Line":1}},{"line":429,"address":[],"length":0,"stats":{"Line":1}},{"line":431,"address":[],"length":0,"stats":{"Line":1}},{"line":433,"address":[],"length":0,"stats":{"Line":0}},{"line":464,"address":[161184,161270,161296],"length":1,"stats":{"Line":4}},{"line":466,"address":[161198],"length":1,"stats":{"Line":4}},{"line":469,"address":[161245],"length":1,"stats":{"Line":5}},{"line":499,"address":[],"length":0,"stats":{"Line":2}},{"line":501,"address":[],"length":0,"stats":{"Line":6}},{"line":503,"address":[],"length":0,"stats":{"Line":4}},{"line":505,"address":[],"length":0,"stats":{"Line":0}},{"line":512,"address":[],"length":0,"stats":{"Line":3}},{"line":513,"address":[],"length":0,"stats":{"Line":3}},{"line":535,"address":[],"length":0,"stats":{"Line":0}},{"line":537,"address":[],"length":0,"stats":{"Line":0}},{"line":539,"address":[],"length":0,"stats":{"Line":0}},{"line":562,"address":[],"length":0,"stats":{"Line":0}},{"line":564,"address":[],"length":0,"stats":{"Line":0}},{"line":566,"address":[],"length":0,"stats":{"Line":0}}],"covered":70,"coverable":111},{"path":["/","home","botahamec","Projects","happylock","src","rwlock","write_guard.rs"],"content":"use std::fmt::{Debug, Display};\nuse std::hash::Hash;\nuse std::marker::PhantomData;\nuse std::ops::{Deref, DerefMut};\n\nuse lock_api::RawRwLock;\n\nuse crate::lockable::RawLock;\nuse crate::ThreadKey;\n\nuse super::{RwLock, RwLockWriteGuard, RwLockWriteRef};\n\n// These impls make things slightly easier because now you can use\n// `println!(\"{guard}\")` instead of `println!(\"{}\", *guard)`\n\n#[mutants::skip] // hashing involves PRNG and is difficult to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Hash + ?Sized, R: RawRwLock\u003e Hash for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.deref().hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug + ?Sized, R: RawRwLock\u003e Debug for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: Display + ?Sized, R: RawRwLock\u003e Display for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e Deref for RwLockWriteRef\u003c'_, T, R\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t// safety: this is the only type that can use `value`, and there's\n\t\t// a reference to this type, so there cannot be any mutable\n\t\t// references to this value.\n\t\tunsafe { \u0026*self.0.data.get() }\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e DerefMut for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t// safety: this is the only type that can use `value`, and we have a\n\t\t// mutable reference to this type, so there cannot be any other\n\t\t// references to this value.\n\t\tunsafe { \u0026mut *self.0.data.get() }\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e AsRef\u003cT\u003e for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e AsMut\u003cT\u003e for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e Drop for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn drop(\u0026mut self) {\n\t\t// safety: this guard is being destroyed, so the data cannot be\n\t\t// accessed without locking again\n\t\tunsafe { self.0.raw_unlock() }\n\t}\n}\n\nimpl\u003c'a, T: ?Sized + 'a, R: RawRwLock\u003e RwLockWriteRef\u003c'a, T, R\u003e {\n\t/// Creates a reference to the underlying data of an [`RwLock`] without\n\t/// locking or taking ownership of the key.\n\t#[must_use]\n\tpub(crate) unsafe fn new(mutex: \u0026'a RwLock\u003cT, R\u003e) -\u003e Self {\n\t\tSelf(mutex, PhantomData)\n\t}\n}\n\n#[mutants::skip] // hashing involves PRNG and is difficult to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Hash + ?Sized, R: RawRwLock\u003e Hash for RwLockWriteGuard\u003c'_, T, R\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.deref().hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug + ?Sized, R: RawRwLock\u003e Debug for RwLockWriteGuard\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: Display + ?Sized, R: RawRwLock\u003e Display for RwLockWriteGuard\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e Deref for RwLockWriteGuard\u003c'_, T, R\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t\u0026self.rwlock\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e DerefMut for RwLockWriteGuard\u003c'_, T, R\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t\u0026mut self.rwlock\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e AsRef\u003cT\u003e for RwLockWriteGuard\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e AsMut\u003cT\u003e for RwLockWriteGuard\u003c'_, T, R\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself\n\t}\n}\n\nimpl\u003c'a, T: ?Sized + 'a, R: RawRwLock\u003e RwLockWriteGuard\u003c'a, T, R\u003e {\n\t/// Create a guard to the given mutex. Undefined if multiple guards to the\n\t/// same mutex exist at once.\n\t#[must_use]\n\tpub(super) unsafe fn new(rwlock: \u0026'a RwLock\u003cT, R\u003e, thread_key: ThreadKey) -\u003e Self {\n\t\tSelf {\n\t\t\trwlock: RwLockWriteRef(rwlock, PhantomData),\n\t\t\tthread_key,\n\t\t}\n\t}\n}\n\nunsafe impl\u003cT: ?Sized + Sync, R: RawRwLock + Sync\u003e Sync for RwLockWriteRef\u003c'_, T, R\u003e {}\n","traces":[{"line":33,"address":[716112],"length":1,"stats":{"Line":1}},{"line":34,"address":[],"length":0,"stats":{"Line":1}},{"line":41,"address":[],"length":0,"stats":{"Line":4}},{"line":45,"address":[161077],"length":1,"stats":{"Line":4}},{"line":50,"address":[161120],"length":1,"stats":{"Line":1}},{"line":54,"address":[161125],"length":1,"stats":{"Line":1}},{"line":59,"address":[],"length":0,"stats":{"Line":0}},{"line":60,"address":[],"length":0,"stats":{"Line":0}},{"line":65,"address":[],"length":0,"stats":{"Line":0}},{"line":66,"address":[],"length":0,"stats":{"Line":0}},{"line":71,"address":[268368,268384],"length":1,"stats":{"Line":5}},{"line":74,"address":[199429],"length":1,"stats":{"Line":5}},{"line":82,"address":[],"length":0,"stats":{"Line":3}},{"line":83,"address":[],"length":0,"stats":{"Line":0}},{"line":104,"address":[],"length":0,"stats":{"Line":1}},{"line":105,"address":[],"length":0,"stats":{"Line":1}},{"line":112,"address":[],"length":0,"stats":{"Line":3}},{"line":113,"address":[],"length":0,"stats":{"Line":3}},{"line":118,"address":[],"length":0,"stats":{"Line":1}},{"line":119,"address":[161157],"length":1,"stats":{"Line":1}},{"line":124,"address":[],"length":0,"stats":{"Line":0}},{"line":125,"address":[],"length":0,"stats":{"Line":0}},{"line":130,"address":[],"length":0,"stats":{"Line":0}},{"line":131,"address":[],"length":0,"stats":{"Line":0}},{"line":139,"address":[],"length":0,"stats":{"Line":3}},{"line":141,"address":[],"length":0,"stats":{"Line":0}}],"covered":16,"coverable":26},{"path":["/","home","botahamec","Projects","happylock","src","rwlock","write_lock.rs"],"content":"use std::fmt::Debug;\n\nuse lock_api::RawRwLock;\n\nuse crate::lockable::{Lockable, RawLock};\nuse crate::ThreadKey;\n\nuse super::{RwLock, RwLockWriteGuard, RwLockWriteRef, WriteLock};\n\nunsafe impl\u003cT: Send, R: RawRwLock + Send + Sync\u003e Lockable for WriteLock\u003c'_, T, R\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= RwLockWriteRef\u003c'g, T, R\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= \u0026'a mut T\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tptrs.push(self.as_ref());\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tRwLockWriteRef::new(self.as_ref())\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.0.data_mut()\n\t}\n}\n\n// Technically, the exclusive locks can also be shared, but there's currently\n// no way to express that. I don't think I want to ever express that.\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: ?Sized + Debug, R: RawRwLock\u003e Debug for WriteLock\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t// safety: this is just a try lock, and the value is dropped\n\t\t// immediately after, so there's no risk of blocking ourselves\n\t\t// or any other threads\n\t\t// It makes zero sense to try using an exclusive lock for this, so this\n\t\t// is the only time when WriteLock does a read.\n\t\tif let Some(value) = unsafe { self.0.try_read_no_key() } {\n\t\t\tf.debug_struct(\"WriteLock\").field(\"data\", \u0026\u0026*value).finish()\n\t\t} else {\n\t\t\tstruct LockedPlaceholder;\n\t\t\timpl Debug for LockedPlaceholder {\n\t\t\t\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t\t\t\tf.write_str(\"\u003clocked\u003e\")\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tf.debug_struct(\"WriteLock\")\n\t\t\t\t.field(\"data\", \u0026LockedPlaceholder)\n\t\t\t\t.finish()\n\t\t}\n\t}\n}\n\nimpl\u003c'l, T, R\u003e From\u003c\u0026'l RwLock\u003cT, R\u003e\u003e for WriteLock\u003c'l, T, R\u003e {\n\tfn from(value: \u0026'l RwLock\u003cT, R\u003e) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\nimpl\u003cT: ?Sized, R\u003e AsRef\u003cRwLock\u003cT, R\u003e\u003e for WriteLock\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026RwLock\u003cT, R\u003e {\n\t\tself.0\n\t}\n}\n\nimpl\u003c'l, T, R\u003e WriteLock\u003c'l, T, R\u003e {\n\t/// Creates a new `WriteLock` which accesses the given [`RwLock`]\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{rwlock::WriteLock, RwLock};\n\t///\n\t/// let lock = RwLock::new(5);\n\t/// let write_lock = WriteLock::new(\u0026lock);\n\t/// ```\n\t#[must_use]\n\tpub const fn new(rwlock: \u0026'l RwLock\u003cT, R\u003e) -\u003e Self {\n\t\tSelf(rwlock)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e WriteLock\u003c'_, T, R\u003e {\n\t/// Locks the underlying [`RwLock`] with exclusive write access, blocking\n\t/// the current until it can be acquired.\n\t///\n\t/// This function will not return while other writers or readers currently\n\t/// have access to the lock.\n\t///\n\t/// Returns an RAII guard which will drop the write access of this `RwLock`\n\t/// when dropped.\n\t///\n\t/// Because this method takes a [`ThreadKey`], it's not possible for this\n\t/// method to cause a deadlock.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, RwLock};\n\t/// use happylock::rwlock::WriteLock;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(1);\n\t/// let writer = WriteLock::new(\u0026lock);\n\t///\n\t/// let mut n = writer.lock(key);\n\t/// *n += 2;\n\t/// ```\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\t#[must_use]\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e RwLockWriteGuard\u003c'_, T, R\u003e {\n\t\tself.0.write(key)\n\t}\n\n\t/// Attempts to lock the underlying [`RwLock`] with exclusive write access.\n\t///\n\t/// This function does not block. If the lock could not be acquired at this\n\t/// time, then `None` is returned. Otherwise, an RAII guard is returned\n\t/// which will release the lock when it is dropped.\n\t///\n\t/// This function does not provide any guarantees with respect to the\n\t/// ordering of whether contentious readers or writers will acquire the\n\t/// lock first.\n\t///\n\t/// # Errors\n\t///\n\t/// If the [`RwLock`] could not be acquired because it was already locked,\n\t/// then an error will be returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::rwlock::WriteLock;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(1);\n\t/// let writer = WriteLock::new(\u0026lock);\n\t///\n\t/// match writer.try_lock(key) {\n\t/// Ok(n) =\u003e assert_eq!(*n, 1),\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t/// ```\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e Result\u003cRwLockWriteGuard\u003c'_, T, R\u003e, ThreadKey\u003e {\n\t\tself.0.try_write(key)\n\t}\n\n\t// There's no `try_lock_no_key`. Instead, `try_read_no_key` is called on\n\t// the referenced `RwLock`.\n\n\t/// Immediately drops the guard, and consequently releases the exclusive\n\t/// lock on the underlying [`RwLock`].\n\t///\n\t/// This function is equivalent to calling [`drop`] on the guard, except\n\t/// that it returns the key that was used to create it. Alternately, the\n\t/// guard will be automatically dropped when it goes out of scope.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::rwlock::WriteLock;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(0);\n\t/// let writer = WriteLock::new(\u0026lock);\n\t///\n\t/// let mut guard = writer.lock(key);\n\t/// *guard += 20;\n\t/// let key = WriteLock::unlock(guard);\n\t/// ```\n\t#[must_use]\n\tpub fn unlock(guard: RwLockWriteGuard\u003c'_, T, R\u003e) -\u003e ThreadKey {\n\t\tRwLock::unlock_write(guard)\n\t}\n}\n","traces":[{"line":21,"address":[715712],"length":1,"stats":{"Line":1}},{"line":22,"address":[],"length":0,"stats":{"Line":1}},{"line":25,"address":[],"length":0,"stats":{"Line":0}},{"line":26,"address":[],"length":0,"stats":{"Line":0}},{"line":29,"address":[],"length":0,"stats":{"Line":0}},{"line":30,"address":[],"length":0,"stats":{"Line":0}},{"line":64,"address":[],"length":0,"stats":{"Line":0}},{"line":65,"address":[],"length":0,"stats":{"Line":0}},{"line":70,"address":[],"length":0,"stats":{"Line":1}},{"line":71,"address":[],"length":0,"stats":{"Line":1}},{"line":87,"address":[],"length":0,"stats":{"Line":2}},{"line":88,"address":[],"length":0,"stats":{"Line":0}},{"line":121,"address":[],"length":0,"stats":{"Line":1}},{"line":122,"address":[],"length":0,"stats":{"Line":1}},{"line":155,"address":[],"length":0,"stats":{"Line":1}},{"line":156,"address":[],"length":0,"stats":{"Line":1}},{"line":184,"address":[],"length":0,"stats":{"Line":0}},{"line":185,"address":[],"length":0,"stats":{"Line":0}}],"covered":9,"coverable":18},{"path":["/","home","botahamec","Projects","happylock","src","rwlock.rs"],"content":"use std::cell::UnsafeCell;\nuse std::marker::PhantomData;\n\nuse lock_api::RawRwLock;\n\nuse crate::poisonable::PoisonFlag;\nuse crate::ThreadKey;\n\nmod rwlock;\n\nmod read_lock;\nmod write_lock;\n\nmod read_guard;\nmod write_guard;\n\n#[cfg(feature = \"spin\")]\npub type SpinRwLock\u003cT\u003e = RwLock\u003cT, spin::RwLock\u003c()\u003e\u003e;\n\n#[cfg(feature = \"parking_lot\")]\npub type ParkingRwLock\u003cT\u003e = RwLock\u003cT, parking_lot::RawRwLock\u003e;\n\n/// A reader-writer lock\n///\n/// This type of lock allows a number of readers or at most one writer at any\n/// point in time. The write portion of this lock typically allows modification\n/// of the underlying data (exclusive access) and the read portion of this lock\n/// typically allows for read-only access (shared access).\n///\n/// In comparison, a [`Mutex`] does not distinguish between readers or writers\n/// that acquire the lock, therefore blocking any threads waiting for the lock\n/// to become available. An `RwLock` will allow any number of readers to\n/// acquire the lock as long as a writer is not holding the lock.\n///\n/// The type parameter T represents the data that this lock protects. It is\n/// required that T satisfies [`Send`] to be shared across threads and [`Sync`]\n/// to allow concurrent access through readers. The RAII guard returned from\n/// the locking methods implement [`Deref`] (and [`DerefMut`] for the `write`\n/// methods) to allow access to the content of the lock.\n///\n/// Locking the mutex on a thread that already locked it is impossible, due to\n/// the requirement of the [`ThreadKey`]. Therefore, this will never deadlock.\n///\n/// [`ThreadKey`]: `crate::ThreadKey`\n/// [`Mutex`]: `crate::mutex::Mutex`\n/// [`Deref`]: `std::ops::Deref`\n/// [`DerefMut`]: `std::ops::DerefMut`\npub struct RwLock\u003cT: ?Sized, R\u003e {\n\traw: R,\n\tpoison: PoisonFlag,\n\tdata: UnsafeCell\u003cT\u003e,\n}\n\n/// Grants read access to an [`RwLock`]\n///\n/// This structure is designed to be used in a [`LockCollection`] to indicate\n/// that only read access is needed to the data.\n///\n/// [`LockCollection`]: `crate::LockCollection`\n#[repr(transparent)]\npub struct ReadLock\u003c'l, T: ?Sized, R\u003e(\u0026'l RwLock\u003cT, R\u003e);\n\n/// Grants write access to an [`RwLock`]\n///\n/// This structure is designed to be used in a [`LockCollection`] to indicate\n/// that write access is needed to the data.\n///\n/// [`LockCollection`]: `crate::LockCollection`\n#[repr(transparent)]\npub struct WriteLock\u003c'l, T: ?Sized, R\u003e(\u0026'l RwLock\u003cT, R\u003e);\n\n/// RAII structure that unlocks the shared read access to a [`RwLock`]\n///\n/// This is similar to [`RwLockReadRef`], except it does not hold a\n/// [`Keyable`].\npub struct RwLockReadRef\u003c'a, T: ?Sized, R: RawRwLock\u003e(\n\t\u0026'a RwLock\u003cT, R\u003e,\n\tPhantomData\u003c(\u0026'a mut T, R::GuardMarker)\u003e,\n);\n\n/// RAII structure that unlocks the exclusive write access to a [`RwLock`]\n///\n/// This is similar to [`RwLockWriteRef`], except it does not hold a\n/// [`Keyable`].\npub struct RwLockWriteRef\u003c'a, T: ?Sized, R: RawRwLock\u003e(\n\t\u0026'a RwLock\u003cT, R\u003e,\n\tPhantomData\u003c(\u0026'a mut T, R::GuardMarker)\u003e,\n);\n\n/// RAII structure used to release the shared read access of a lock when\n/// dropped.\n///\n/// This structure is created by the [`read`] and [`try_read`] methods on\n/// [`RwLock`].\n///\n/// [`read`]: `RwLock::read`\n/// [`try_read`]: `RwLock::try_read`\npub struct RwLockReadGuard\u003c'a, T: ?Sized, R: RawRwLock\u003e {\n\trwlock: RwLockReadRef\u003c'a, T, R\u003e,\n\tthread_key: ThreadKey,\n}\n\n/// RAII structure used to release the exclusive write access of a lock when\n/// dropped.\n///\n/// This structure is created by the [`write`] and [`try_write`] methods on\n/// [`RwLock`]\n///\n/// [`try_write`]: `RwLock::try_write`\npub struct RwLockWriteGuard\u003c'a, T: ?Sized, R: RawRwLock\u003e {\n\trwlock: RwLockWriteRef\u003c'a, T, R\u003e,\n\tthread_key: ThreadKey,\n}\n\n#[cfg(test)]\nmod tests {\n\tuse crate::lockable::Lockable;\n\tuse crate::RwLock;\n\tuse crate::ThreadKey;\n\n\tuse super::*;\n\n\t#[test]\n\tfn unlocked_when_initialized() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\n\t\tassert!(!lock.is_locked());\n\t\tassert!(lock.try_write(key).is_ok());\n\t}\n\n\t#[test]\n\tfn read_lock_unlocked_when_initialized() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet reader = ReadLock::new(\u0026lock);\n\n\t\tassert!(reader.try_lock(key).is_ok());\n\t}\n\n\t#[test]\n\tfn read_lock_from_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::from(\"Hello, world!\");\n\t\tlet reader = ReadLock::from(\u0026lock);\n\n\t\tlet guard = reader.lock(key);\n\t\tassert_eq!(*guard, \"Hello, world!\");\n\t}\n\n\t#[test]\n\tfn write_lock_unlocked_when_initialized() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet writer = WriteLock::new(\u0026lock);\n\n\t\tassert!(writer.try_lock(key).is_ok());\n\t}\n\n\t#[test]\n\tfn read_lock_get_ptrs() {\n\t\tlet rwlock = RwLock::new(5);\n\t\tlet readlock = ReadLock::new(\u0026rwlock);\n\t\tlet mut lock_ptrs = Vec::new();\n\t\treadlock.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tassert!(std::ptr::addr_eq(lock_ptrs[0], \u0026rwlock));\n\t}\n\n\t#[test]\n\tfn write_lock_get_ptrs() {\n\t\tlet rwlock = RwLock::new(5);\n\t\tlet writelock = WriteLock::new(\u0026rwlock);\n\t\tlet mut lock_ptrs = Vec::new();\n\t\twritelock.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tassert!(std::ptr::addr_eq(lock_ptrs[0], \u0026rwlock));\n\t}\n\n\t#[test]\n\tfn locked_after_read() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\n\t\tlet guard = lock.read(key);\n\n\t\tassert!(lock.is_locked());\n\t\tdrop(guard)\n\t}\n\n\t#[test]\n\tfn locked_after_using_read_lock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet reader = ReadLock::new(\u0026lock);\n\n\t\tlet guard = reader.lock(key);\n\n\t\tassert!(lock.is_locked());\n\t\tdrop(guard)\n\t}\n\n\t#[test]\n\tfn locked_after_write() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\n\t\tlet guard = lock.write(key);\n\n\t\tassert!(lock.is_locked());\n\t\tdrop(guard)\n\t}\n\n\t#[test]\n\tfn locked_after_using_write_lock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet writer = WriteLock::new(\u0026lock);\n\n\t\tlet guard = writer.lock(key);\n\n\t\tassert!(lock.is_locked());\n\t\tdrop(guard)\n\t}\n\n\t#[test]\n\tfn read_display_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet guard = lock.read(key);\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn write_display_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet guard = lock.write(key);\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn read_ref_display_works() {\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet guard = unsafe { lock.try_read_no_key().unwrap() };\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn write_ref_display_works() {\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet guard = unsafe { lock.try_write_no_key().unwrap() };\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn dropping_read_ref_releases_rwlock() {\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\n\t\tlet guard = unsafe { lock.try_read_no_key().unwrap() };\n\t\tdrop(guard);\n\n\t\tassert!(!lock.is_locked());\n\t}\n\n\t#[test]\n\tfn dropping_write_guard_releases_rwlock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\n\t\tlet guard = lock.write(key);\n\t\tdrop(guard);\n\n\t\tassert!(!lock.is_locked());\n\t}\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","evil_mutex.rs"],"content":"use std::sync::Arc;\n\nuse happylock::collection::{BoxedLockCollection, RetryingLockCollection};\nuse happylock::mutex::Mutex;\nuse happylock::ThreadKey;\nuse lock_api::{GuardNoSend, RawMutex};\n\nstruct EvilMutex {\n\tinner: parking_lot::RawMutex,\n}\n\nunsafe impl RawMutex for EvilMutex {\n\t#[allow(clippy::declare_interior_mutable_const)]\n\tconst INIT: Self = Self {\n\t\tinner: parking_lot::RawMutex::INIT,\n\t};\n\n\ttype GuardMarker = GuardNoSend;\n\n\tfn lock(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn try_lock(\u0026self) -\u003e bool {\n\t\tself.inner.try_lock()\n\t}\n\n\tunsafe fn unlock(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n}\n\n#[test]\nfn boxed_mutexes() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet good_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(5));\n\tlet evil_mutex: Arc\u003cMutex\u003ci32, EvilMutex\u003e\u003e = Arc::new(Mutex::new(7));\n\tlet useless_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(10));\n\tlet c_good = Arc::clone(\u0026good_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\t_ = collection.lock(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(good_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n\tassert!(evil_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n}\n\n#[test]\nfn retrying_mutexes() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet good_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(5));\n\tlet evil_mutex: Arc\u003cMutex\u003ci32, EvilMutex\u003e\u003e = Arc::new(Mutex::new(7));\n\tlet useless_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(10));\n\tlet c_good = Arc::clone(\u0026good_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tRetryingLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\tcollection.lock(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(good_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n\tassert!(evil_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","evil_rwlock.rs"],"content":"use std::sync::Arc;\n\nuse happylock::collection::{BoxedLockCollection, RetryingLockCollection};\nuse happylock::rwlock::RwLock;\nuse happylock::ThreadKey;\nuse lock_api::{GuardNoSend, RawRwLock};\n\nstruct EvilRwLock {\n\tinner: parking_lot::RawRwLock,\n}\n\nunsafe impl RawRwLock for EvilRwLock {\n\t#[allow(clippy::declare_interior_mutable_const)]\n\tconst INIT: Self = Self {\n\t\tinner: parking_lot::RawRwLock::INIT,\n\t};\n\n\ttype GuardMarker = GuardNoSend;\n\n\tfn lock_shared(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn try_lock_shared(\u0026self) -\u003e bool {\n\t\tself.inner.try_lock_shared()\n\t}\n\n\tunsafe fn unlock_shared(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn lock_exclusive(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn try_lock_exclusive(\u0026self) -\u003e bool {\n\t\tself.inner.try_lock_exclusive()\n\t}\n\n\tunsafe fn unlock_exclusive(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n}\n\n#[test]\nfn boxed_rwlocks() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet good_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(5));\n\tlet evil_mutex: Arc\u003cRwLock\u003ci32, EvilRwLock\u003e\u003e = Arc::new(RwLock::new(7));\n\tlet useless_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(10));\n\tlet c_good = Arc::clone(\u0026good_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\t_ = collection.lock(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(good_mutex.scoped_try_write(\u0026mut key, |_| {}).is_ok());\n\tassert!(evil_mutex.scoped_try_write(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_write(\u0026mut key, |_| {}).is_ok());\n}\n\n#[test]\nfn retrying_rwlocks() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet good_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(5));\n\tlet evil_mutex: Arc\u003cRwLock\u003ci32, EvilRwLock\u003e\u003e = Arc::new(RwLock::new(7));\n\tlet useless_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(10));\n\tlet c_good = Arc::clone(\u0026good_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tRetryingLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\tcollection.lock(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(good_mutex.scoped_try_write(\u0026mut key, |_| {}).is_ok());\n\tassert!(evil_mutex.scoped_try_write(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_write(\u0026mut key, |_| {}).is_ok());\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","evil_try_mutex.rs"],"content":"use std::sync::Arc;\n\nuse happylock::{\n\tcollection::{BoxedLockCollection, RetryingLockCollection},\n\tmutex::Mutex,\n\tThreadKey,\n};\nuse lock_api::{GuardNoSend, RawMutex};\n\nstruct EvilMutex {\n\tinner: parking_lot::RawMutex,\n}\n\nunsafe impl RawMutex for EvilMutex {\n\t#[allow(clippy::declare_interior_mutable_const)]\n\tconst INIT: Self = Self {\n\t\tinner: parking_lot::RawMutex::INIT,\n\t};\n\n\ttype GuardMarker = GuardNoSend;\n\n\tfn lock(\u0026self) {\n\t\tself.inner.lock()\n\t}\n\n\tfn try_lock(\u0026self) -\u003e bool {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tunsafe fn unlock(\u0026self) {\n\t\tself.inner.unlock()\n\t}\n}\n\n#[test]\nfn boxed_mutexes() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet good_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(5));\n\tlet evil_mutex: Arc\u003cMutex\u003ci32, EvilMutex\u003e\u003e = Arc::new(Mutex::new(7));\n\tlet useless_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(10));\n\tlet c_good = Arc::clone(\u0026good_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\tlet g = collection.try_lock(key);\n\t\tprintln!(\"{}\", g.unwrap().1);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(good_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n\tassert!(evil_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n}\n\n#[test]\nfn retrying_mutexes() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet good_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(5));\n\tlet evil_mutex: Arc\u003cMutex\u003ci32, EvilMutex\u003e\u003e = Arc::new(Mutex::new(7));\n\tlet useless_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(10));\n\tlet c_good = Arc::clone(\u0026good_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tRetryingLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\tlet _ = collection.try_lock(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(good_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n\tassert!(evil_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","evil_try_rwlock.rs"],"content":"use std::sync::Arc;\n\nuse happylock::collection::{BoxedLockCollection, RetryingLockCollection};\nuse happylock::rwlock::RwLock;\nuse happylock::ThreadKey;\nuse lock_api::{GuardNoSend, RawRwLock};\n\nstruct EvilRwLock {\n\tinner: parking_lot::RawRwLock,\n}\n\nunsafe impl RawRwLock for EvilRwLock {\n\t#[allow(clippy::declare_interior_mutable_const)]\n\tconst INIT: Self = Self {\n\t\tinner: parking_lot::RawRwLock::INIT,\n\t};\n\n\ttype GuardMarker = GuardNoSend;\n\n\tfn lock_shared(\u0026self) {\n\t\tself.inner.lock_shared()\n\t}\n\n\tfn try_lock_shared(\u0026self) -\u003e bool {\n\t\tpanic!(\"mwahahahaha\")\n\t}\n\n\tunsafe fn unlock_shared(\u0026self) {\n\t\tself.inner.unlock_shared()\n\t}\n\n\tfn lock_exclusive(\u0026self) {\n\t\tself.inner.lock_exclusive()\n\t}\n\n\tfn try_lock_exclusive(\u0026self) -\u003e bool {\n\t\tpanic!(\"mwahahahaha\")\n\t}\n\n\tunsafe fn unlock_exclusive(\u0026self) {\n\t\tself.inner.unlock_exclusive()\n\t}\n}\n\n#[test]\nfn boxed_rwlocks() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet good_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(5));\n\tlet evil_mutex: Arc\u003cRwLock\u003ci32, EvilRwLock\u003e\u003e = Arc::new(RwLock::new(7));\n\tlet useless_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(10));\n\tlet c_good = Arc::clone(\u0026good_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\tlet _ = collection.try_read(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(good_mutex.scoped_try_read(\u0026mut key, |_| {}).is_ok());\n\tassert!(evil_mutex.scoped_try_read(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_read(\u0026mut key, |_| {}).is_ok());\n}\n\n#[test]\nfn retrying_rwlocks() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet good_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(5));\n\tlet evil_mutex: Arc\u003cRwLock\u003ci32, EvilRwLock\u003e\u003e = Arc::new(RwLock::new(7));\n\tlet useless_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(10));\n\tlet c_good = Arc::clone(\u0026good_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tRetryingLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\t_ = collection.try_read(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(good_mutex.scoped_try_read(\u0026mut key, |_| {}).is_ok());\n\tassert!(evil_mutex.scoped_try_read(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_read(\u0026mut key, |_| {}).is_ok());\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","evil_unlock_mutex.rs"],"content":"use std::sync::Arc;\n\nuse happylock::collection::{BoxedLockCollection, RetryingLockCollection};\nuse happylock::mutex::Mutex;\nuse happylock::ThreadKey;\nuse lock_api::{GuardNoSend, RawMutex};\n\nstruct KindaEvilMutex {\n\tinner: parking_lot::RawMutex,\n}\n\nstruct EvilMutex {}\n\nunsafe impl RawMutex for KindaEvilMutex {\n\t#[allow(clippy::declare_interior_mutable_const)]\n\tconst INIT: Self = Self {\n\t\tinner: parking_lot::RawMutex::INIT,\n\t};\n\n\ttype GuardMarker = GuardNoSend;\n\n\tfn lock(\u0026self) {\n\t\tself.inner.lock()\n\t}\n\n\tfn try_lock(\u0026self) -\u003e bool {\n\t\tself.inner.try_lock()\n\t}\n\n\tunsafe fn unlock(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n}\n\nunsafe impl RawMutex for EvilMutex {\n\t#[allow(clippy::declare_interior_mutable_const)]\n\tconst INIT: Self = Self {};\n\n\ttype GuardMarker = GuardNoSend;\n\n\tfn lock(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn try_lock(\u0026self) -\u003e bool {\n\t\tpanic!(\"mwahahahaha\")\n\t}\n\n\tunsafe fn unlock(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n}\n\n#[test]\nfn boxed_mutexes() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet kinda_evil_mutex: Arc\u003cMutex\u003ci32, KindaEvilMutex\u003e\u003e = Arc::new(Mutex::new(5));\n\tlet evil_mutex: Arc\u003cMutex\u003ci32, EvilMutex\u003e\u003e = Arc::new(Mutex::new(7));\n\tlet useless_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(10));\n\tlet c_good = Arc::clone(\u0026kinda_evil_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\t_ = collection.lock(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(kinda_evil_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n\tassert!(evil_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n}\n\n#[test]\nfn retrying_mutexes() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet kinda_evil_mutex: Arc\u003cMutex\u003ci32, KindaEvilMutex\u003e\u003e = Arc::new(Mutex::new(5));\n\tlet evil_mutex: Arc\u003cMutex\u003ci32, EvilMutex\u003e\u003e = Arc::new(Mutex::new(7));\n\tlet useless_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(10));\n\tlet c_good = Arc::clone(\u0026kinda_evil_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tRetryingLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\tcollection.lock(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(kinda_evil_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n\tassert!(evil_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","evil_unlock_rwlock.rs"],"content":"use std::sync::Arc;\n\nuse happylock::collection::{BoxedLockCollection, RetryingLockCollection};\nuse happylock::rwlock::RwLock;\nuse happylock::ThreadKey;\nuse lock_api::{GuardNoSend, RawRwLock};\n\nstruct KindaEvilRwLock {\n\tinner: parking_lot::RawRwLock,\n}\n\nstruct EvilRwLock {}\n\nunsafe impl RawRwLock for KindaEvilRwLock {\n\t#[allow(clippy::declare_interior_mutable_const)]\n\tconst INIT: Self = Self {\n\t\tinner: parking_lot::RawRwLock::INIT,\n\t};\n\n\ttype GuardMarker = GuardNoSend;\n\n\tfn lock_shared(\u0026self) {\n\t\tself.inner.lock_shared()\n\t}\n\n\tfn try_lock_shared(\u0026self) -\u003e bool {\n\t\tself.inner.try_lock_shared()\n\t}\n\n\tunsafe fn unlock_shared(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn lock_exclusive(\u0026self) {\n\t\tself.inner.lock_exclusive()\n\t}\n\n\tfn try_lock_exclusive(\u0026self) -\u003e bool {\n\t\tself.inner.try_lock_exclusive()\n\t}\n\n\tunsafe fn unlock_exclusive(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n}\n\nunsafe impl RawRwLock for EvilRwLock {\n\t#[allow(clippy::declare_interior_mutable_const)]\n\tconst INIT: Self = Self {};\n\n\ttype GuardMarker = GuardNoSend;\n\n\tfn lock_shared(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn try_lock_shared(\u0026self) -\u003e bool {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tunsafe fn unlock_shared(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn lock_exclusive(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn try_lock_exclusive(\u0026self) -\u003e bool {\n\t\tpanic!(\"mwahahahaha\")\n\t}\n\n\tunsafe fn unlock_exclusive(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n}\n\n#[test]\nfn boxed_rwlocks() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet kinda_evil_mutex: RwLock\u003ci32, KindaEvilRwLock\u003e = RwLock::new(5);\n\tlet evil_mutex: RwLock\u003ci32, EvilRwLock\u003e = RwLock::new(7);\n\tlet useless_mutex: RwLock\u003ci32, parking_lot::RawRwLock\u003e = RwLock::new(10);\n\n\tlet r = std::thread::scope(|s| {\n\t\tlet r = s\n\t\t\t.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet collection =\n\t\t\t\t\tBoxedLockCollection::try_new((\u0026kinda_evil_mutex, \u0026evil_mutex, \u0026useless_mutex))\n\t\t\t\t\t\t.unwrap();\n\t\t\t\t_ = collection.read(key);\n\t\t\t})\n\t\t\t.join();\n\n\t\tr\n\t});\n\n\tassert!(r.is_err());\n\tassert!(kinda_evil_mutex.scoped_try_write(\u0026mut key, |_| {}).is_err());\n\tassert!(evil_mutex.scoped_try_write(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_write(\u0026mut key, |_| {}).is_ok());\n}\n\n#[test]\nfn retrying_rwlocks() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet kinda_evil_mutex: Arc\u003cRwLock\u003ci32, KindaEvilRwLock\u003e\u003e = Arc::new(RwLock::new(5));\n\tlet evil_mutex: Arc\u003cRwLock\u003ci32, EvilRwLock\u003e\u003e = Arc::new(RwLock::new(7));\n\tlet useless_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(10));\n\tlet c_good = Arc::clone(\u0026kinda_evil_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tRetryingLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\tcollection.read(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(kinda_evil_mutex.scoped_try_write(\u0026mut key, |_| {}).is_err());\n\tassert!(evil_mutex.scoped_try_write(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_write(\u0026mut key, |_| {}).is_ok());\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","forget.rs"],"content":"use happylock::{Mutex, ThreadKey};\n\n#[test]\nfn no_new_threadkey_when_forgetting_lock() {\n\tlet key = ThreadKey::get().unwrap();\n\tlet mutex = Mutex::new(\"foo\".to_string());\n\n\tlet guard = mutex.lock(key);\n\tstd::mem::forget(guard);\n\n\tassert!(ThreadKey::get().is_none());\n}\n\n#[test]\nfn no_new_threadkey_in_scoped_lock() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet mutex = Mutex::new(\"foo\".to_string());\n\n\tmutex.scoped_lock(\u0026mut key, |_| {\n\t\tassert!(ThreadKey::get().is_none());\n\t});\n\n\tmutex.lock(key);\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","retry.rs"],"content":"use std::time::Duration;\n\nuse happylock::{collection::RetryingLockCollection, Mutex, ThreadKey};\n\nstatic MUTEX_1: Mutex\u003ci32\u003e = Mutex::new(1);\nstatic MUTEX_2: Mutex\u003ci32\u003e = Mutex::new(2);\nstatic MUTEX_3: Mutex\u003ci32\u003e = Mutex::new(3);\n\nfn thread_1() {\n\tlet key = ThreadKey::get().unwrap();\n\tlet mut guard = MUTEX_2.lock(key);\n\tstd::thread::sleep(Duration::from_millis(100));\n\t*guard = 5;\n}\n\nfn thread_2() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tstd::thread::sleep(Duration::from_millis(50));\n\tlet collection = RetryingLockCollection::try_new([\u0026MUTEX_1, \u0026MUTEX_2, \u0026MUTEX_3]).unwrap();\n\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\tassert_eq!(*guard[0], 4);\n\t\tassert_eq!(*guard[1], 5);\n\t\tassert_eq!(*guard[2], 3);\n\t});\n}\n\nfn thread_3() {\n\tlet key = ThreadKey::get().unwrap();\n\tstd::thread::sleep(Duration::from_millis(75));\n\tlet mut guard = MUTEX_1.lock(key);\n\tstd::thread::sleep(Duration::from_millis(100));\n\t*guard = 4;\n}\n\nfn thread_4() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tstd::thread::sleep(Duration::from_millis(25));\n\tlet collection = RetryingLockCollection::try_new([\u0026MUTEX_1, \u0026MUTEX_2]).unwrap();\n\tassert!(collection.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n}\n\n#[test]\nfn retries() {\n\tlet t1 = std::thread::spawn(thread_1);\n\tlet t2 = std::thread::spawn(thread_2);\n\tlet t3 = std::thread::spawn(thread_3);\n\tlet t4 = std::thread::spawn(thread_4);\n\n\tt1.join().unwrap();\n\tt2.join().unwrap();\n\tt3.join().unwrap();\n\tt4.join().unwrap();\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","retry_rw.rs"],"content":"use std::time::Duration;\n\nuse happylock::{collection::RetryingLockCollection, RwLock, ThreadKey};\n\nstatic RWLOCK_1: RwLock\u003ci32\u003e = RwLock::new(1);\nstatic RWLOCK_2: RwLock\u003ci32\u003e = RwLock::new(2);\nstatic RWLOCK_3: RwLock\u003ci32\u003e = RwLock::new(3);\n\nfn thread_1() {\n\tlet key = ThreadKey::get().unwrap();\n\tlet mut guard = RWLOCK_2.write(key);\n\tstd::thread::sleep(Duration::from_millis(75));\n\tassert_eq!(*guard, 2);\n\t*guard = 5;\n}\n\nfn thread_2() {\n\tlet key = ThreadKey::get().unwrap();\n\tlet collection = RetryingLockCollection::try_new([\u0026RWLOCK_1, \u0026RWLOCK_2, \u0026RWLOCK_3]).unwrap();\n\tstd::thread::sleep(Duration::from_millis(25));\n\tlet guard = collection.read(key);\n\tassert_eq!(*guard[0], 1);\n\tassert_eq!(*guard[1], 5);\n\tassert_eq!(*guard[2], 3);\n}\n\n#[test]\nfn retries() {\n\tlet t1 = std::thread::spawn(thread_1);\n\tlet t2 = std::thread::spawn(thread_2);\n\n\tt1.join().unwrap();\n\tt2.join().unwrap();\n}\n","traces":[],"covered":0,"coverable":0}]}; + var data = {"files":[{"path":["/","home","botahamec","Projects","happylock","examples","basic.rs"],"content":"use std::thread;\n\nuse happylock::{Mutex, ThreadKey};\n\nconst N: usize = 10;\n\nstatic DATA: Mutex\u003ci32\u003e = Mutex::new(0);\n\nfn main() {\n\tlet mut threads = Vec::new();\n\tfor _ in 0..N {\n\t\tlet th = thread::spawn(move || {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\tlet mut data = DATA.lock(key);\n\t\t\t*data += 1;\n\t\t});\n\t\tthreads.push(th);\n\t}\n\n\tfor th in threads {\n\t\t_ = th.join();\n\t}\n\n\tlet key = ThreadKey::get().unwrap();\n\tlet data = DATA.lock(key);\n\tprintln!(\"{data}\");\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","examples","dining_philosophers.rs"],"content":"use std::{thread, time::Duration};\n\nuse happylock::{collection, Mutex, ThreadKey};\n\nstatic PHILOSOPHERS: [Philosopher; 5] = [\n\tPhilosopher {\n\t\tname: \"Socrates\",\n\t\tleft: 0,\n\t\tright: 1,\n\t},\n\tPhilosopher {\n\t\tname: \"John Rawls\",\n\t\tleft: 1,\n\t\tright: 2,\n\t},\n\tPhilosopher {\n\t\tname: \"Jeremy Bentham\",\n\t\tleft: 2,\n\t\tright: 3,\n\t},\n\tPhilosopher {\n\t\tname: \"John Stuart Mill\",\n\t\tleft: 3,\n\t\tright: 4,\n\t},\n\tPhilosopher {\n\t\tname: \"Judith Butler\",\n\t\tleft: 4,\n\t\tright: 0,\n\t},\n];\n\nstatic FORKS: [Mutex\u003c()\u003e; 5] = [\n\tMutex::new(()),\n\tMutex::new(()),\n\tMutex::new(()),\n\tMutex::new(()),\n\tMutex::new(()),\n];\n\nstruct Philosopher {\n\tname: \u0026'static str,\n\tleft: usize,\n\tright: usize,\n}\n\nimpl Philosopher {\n\tfn cycle(\u0026self) {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tthread::sleep(Duration::from_secs(1));\n\n\t\t// safety: no philosopher asks for the same fork twice\n\t\tlet forks = [\u0026FORKS[self.left], \u0026FORKS[self.right]];\n\t\tlet forks = unsafe { collection::RefLockCollection::new_unchecked(\u0026forks) };\n\t\tlet forks = forks.lock(key);\n\t\tprintln!(\"{} is eating...\", self.name);\n\t\tthread::sleep(Duration::from_secs(1));\n\t\tprintln!(\"{} is done eating\", self.name);\n\t\tdrop(forks);\n\t}\n}\n\nfn main() {\n\tlet handles: Vec\u003c_\u003e = PHILOSOPHERS\n\t\t.iter()\n\t\t.map(|philosopher| thread::spawn(move || philosopher.cycle()))\n\t\t// The `collect` is absolutely necessary, because we're using lazy\n\t\t// iterators. If `collect` isn't used, then the thread won't spawn\n\t\t// until we try to join on it.\n\t\t.collect();\n\n\tfor handle in handles {\n\t\t_ = handle.join();\n\t}\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","examples","dining_philosophers_retry.rs"],"content":"use std::{thread, time::Duration};\n\nuse happylock::{collection, Mutex, ThreadKey};\n\nstatic PHILOSOPHERS: [Philosopher; 5] = [\n\tPhilosopher {\n\t\tname: \"Socrates\",\n\t\tleft: 0,\n\t\tright: 1,\n\t},\n\tPhilosopher {\n\t\tname: \"John Rawls\",\n\t\tleft: 1,\n\t\tright: 2,\n\t},\n\tPhilosopher {\n\t\tname: \"Jeremy Bentham\",\n\t\tleft: 2,\n\t\tright: 3,\n\t},\n\tPhilosopher {\n\t\tname: \"John Stuart Mill\",\n\t\tleft: 3,\n\t\tright: 4,\n\t},\n\tPhilosopher {\n\t\tname: \"Judith Butler\",\n\t\tleft: 4,\n\t\tright: 0,\n\t},\n];\n\nstatic FORKS: [Mutex\u003c()\u003e; 5] = [\n\tMutex::new(()),\n\tMutex::new(()),\n\tMutex::new(()),\n\tMutex::new(()),\n\tMutex::new(()),\n];\n\nstruct Philosopher {\n\tname: \u0026'static str,\n\tleft: usize,\n\tright: usize,\n}\n\nimpl Philosopher {\n\tfn cycle(\u0026self) {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tthread::sleep(Duration::from_secs(1));\n\n\t\t// safety: no philosopher asks for the same fork twice\n\t\tlet forks = [\u0026FORKS[self.left], \u0026FORKS[self.right]];\n\t\tlet forks = unsafe { collection::RetryingLockCollection::new_unchecked(\u0026forks) };\n\t\tlet forks = forks.lock(key);\n\t\tprintln!(\"{} is eating...\", self.name);\n\t\tthread::sleep(Duration::from_secs(1));\n\t\tprintln!(\"{} is done eating\", self.name);\n\t\tdrop(forks);\n\t}\n}\n\nfn main() {\n\tlet handles: Vec\u003c_\u003e = PHILOSOPHERS\n\t\t.iter()\n\t\t.map(|philosopher| thread::spawn(move || philosopher.cycle()))\n\t\t// The `collect` is absolutely necessary, because we're using lazy\n\t\t// iterators. If `collect` isn't used, then the thread won't spawn\n\t\t// until we try to join on it.\n\t\t.collect();\n\n\tfor handle in handles {\n\t\t_ = handle.join();\n\t}\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","examples","double_mutex.rs"],"content":"use std::thread;\n\nuse happylock::{collection::RefLockCollection, Mutex, ThreadKey};\n\nconst N: usize = 10;\n\nstatic DATA: (Mutex\u003ci32\u003e, Mutex\u003cString\u003e) = (Mutex::new(0), Mutex::new(String::new()));\n\nfn main() {\n\tlet mut threads = Vec::new();\n\tfor _ in 0..N {\n\t\tlet th = thread::spawn(move || {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\tlet lock = RefLockCollection::new(\u0026DATA);\n\t\t\tlet mut guard = lock.lock(key);\n\t\t\t*guard.1 = (100 - *guard.0).to_string();\n\t\t\t*guard.0 += 1;\n\t\t});\n\t\tthreads.push(th);\n\t}\n\n\tfor th in threads {\n\t\t_ = th.join();\n\t}\n\n\tlet key = ThreadKey::get().unwrap();\n\tlet data = RefLockCollection::new(\u0026DATA);\n\tlet data = data.lock(key);\n\tprintln!(\"{}\", data.0);\n\tprintln!(\"{}\", data.1);\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","examples","fibonacci.rs"],"content":"use happylock::{collection, LockCollection, Mutex, ThreadKey};\nuse std::thread;\n\nconst N: usize = 36;\n\nstatic DATA: [Mutex\u003ci32\u003e; 2] = [Mutex::new(0), Mutex::new(1)];\n\nfn main() {\n\tlet mut threads = Vec::new();\n\tfor _ in 0..N {\n\t\tlet th = thread::spawn(move || {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\n\t\t\t// a reference to a type that implements `OwnedLockable` will never\n\t\t\t// contain duplicates, so no duplicate checking is needed.\n\t\t\tlet collection = collection::RetryingLockCollection::new_ref(\u0026DATA);\n\t\t\tlet mut guard = collection.lock(key);\n\n\t\t\tlet x = *guard[1];\n\t\t\t*guard[1] += *guard[0];\n\t\t\t*guard[0] = x;\n\t\t});\n\t\tthreads.push(th);\n\t}\n\n\tfor thread in threads {\n\t\t_ = thread.join();\n\t}\n\n\tlet key = ThreadKey::get().unwrap();\n\tlet data = LockCollection::new_ref(\u0026DATA);\n\tlet data = data.lock(key);\n\tprintln!(\"{}\", data[0]);\n\tprintln!(\"{}\", data[1]);\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","examples","list.rs"],"content":"use std::thread;\n\nuse happylock::{collection::RefLockCollection, Mutex, ThreadKey};\n\nconst N: usize = 10;\n\nstatic DATA: [Mutex\u003cusize\u003e; 6] = [\n\tMutex::new(0),\n\tMutex::new(1),\n\tMutex::new(2),\n\tMutex::new(3),\n\tMutex::new(4),\n\tMutex::new(5),\n];\n\nstatic SEED: Mutex\u003cu32\u003e = Mutex::new(42);\n\nfn random(key: \u0026mut ThreadKey) -\u003e usize {\n\tSEED.scoped_lock(key, |seed| {\n\t\tlet x = *seed;\n\t\tlet x = x ^ (x \u003c\u003c 13);\n\t\tlet x = x ^ (x \u003e\u003e 17);\n\t\tlet x = x ^ (x \u003c\u003c 5);\n\t\t*seed = x;\n\t\tx as usize\n\t})\n}\n\nfn main() {\n\tlet mut threads = Vec::new();\n\tfor _ in 0..N {\n\t\tlet th = thread::spawn(move || {\n\t\t\tlet mut key = ThreadKey::get().unwrap();\n\t\t\tloop {\n\t\t\t\tlet mut data = Vec::new();\n\t\t\t\tfor _ in 0..3 {\n\t\t\t\t\tlet rand = random(\u0026mut key);\n\t\t\t\t\tdata.push(\u0026DATA[rand % 6]);\n\t\t\t\t}\n\n\t\t\t\tlet Some(lock) = RefLockCollection::try_new(\u0026data) else {\n\t\t\t\t\tcontinue;\n\t\t\t\t};\n\t\t\t\tlet mut guard = lock.lock(key);\n\t\t\t\t*guard[0] += *guard[1];\n\t\t\t\t*guard[1] += *guard[2];\n\t\t\t\t*guard[2] += *guard[0];\n\n\t\t\t\treturn;\n\t\t\t}\n\t\t});\n\t\tthreads.push(th);\n\t}\n\n\tfor th in threads {\n\t\t_ = th.join();\n\t}\n\n\tlet key = ThreadKey::get().unwrap();\n\tlet data = RefLockCollection::new(\u0026DATA);\n\tlet data = data.lock(key);\n\tfor val in \u0026*data {\n\t\tprintln!(\"{val}\");\n\t}\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","src","collection","boxed.rs"],"content":"use std::cell::UnsafeCell;\nuse std::fmt::Debug;\n\nuse crate::lockable::{Lockable, LockableIntoInner, OwnedLockable, RawLock, Sharable};\nuse crate::{Keyable, ThreadKey};\n\nuse super::utils::{\n\tordered_contains_duplicates, scoped_read, scoped_try_read, scoped_try_write, scoped_write,\n};\nuse super::{utils, BoxedLockCollection, LockGuard};\n\nunsafe impl\u003cL: Lockable\u003e RawLock for BoxedLockCollection\u003cL\u003e {\n\t#[mutants::skip] // this should never be called\n\t#[cfg(not(tarpaulin_include))]\n\tfn poison(\u0026self) {\n\t\tfor lock in \u0026self.locks {\n\t\t\tlock.poison();\n\t\t}\n\t}\n\n\tunsafe fn raw_write(\u0026self) {\n\t\tutils::ordered_write(self.locks())\n\t}\n\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool {\n\t\tprintln!(\"{}\", self.locks().len());\n\t\tutils::ordered_try_write(self.locks())\n\t}\n\n\tunsafe fn raw_unlock_write(\u0026self) {\n\t\tfor lock in self.locks() {\n\t\t\tlock.raw_unlock_write();\n\t\t}\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tutils::ordered_read(self.locks());\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tutils::ordered_try_read(self.locks())\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tfor lock in self.locks() {\n\t\t\tlock.raw_unlock_read();\n\t\t}\n\t}\n}\n\nunsafe impl\u003cL: Lockable\u003e Lockable for BoxedLockCollection\u003cL\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= L::Guard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= L::DataMut\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tptrs.push(self);\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tself.child().guard()\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.child().data_mut()\n\t}\n}\n\nunsafe impl\u003cL: Sharable\u003e Sharable for BoxedLockCollection\u003cL\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= L::ReadGuard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= L::DataRef\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tself.child().read_guard()\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tself.child().data_ref()\n\t}\n}\n\nunsafe impl\u003cL: OwnedLockable\u003e OwnedLockable for BoxedLockCollection\u003cL\u003e {}\n\n// LockableGetMut can't be implemented because that would create mutable and\n// immutable references to the same value at the same time.\n\nimpl\u003cL: LockableIntoInner\u003e LockableIntoInner for BoxedLockCollection\u003cL\u003e {\n\ttype Inner = L::Inner;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tLockableIntoInner::into_inner(self.into_child())\n\t}\n}\n\nimpl\u003cL\u003e IntoIterator for BoxedLockCollection\u003cL\u003e\nwhere\n\tL: IntoIterator,\n{\n\ttype Item = \u003cL as IntoIterator\u003e::Item;\n\ttype IntoIter = \u003cL as IntoIterator\u003e::IntoIter;\n\n\tfn into_iter(self) -\u003e Self::IntoIter {\n\t\tself.into_child().into_iter()\n\t}\n}\n\nimpl\u003c'a, L\u003e IntoIterator for \u0026'a BoxedLockCollection\u003cL\u003e\nwhere\n\t\u0026'a L: IntoIterator,\n{\n\ttype Item = \u003c\u0026'a L as IntoIterator\u003e::Item;\n\ttype IntoIter = \u003c\u0026'a L as IntoIterator\u003e::IntoIter;\n\n\tfn into_iter(self) -\u003e Self::IntoIter {\n\t\tself.child().into_iter()\n\t}\n}\n\nimpl\u003cL: OwnedLockable, I: FromIterator\u003cL\u003e + OwnedLockable\u003e FromIterator\u003cL\u003e\n\tfor BoxedLockCollection\u003cI\u003e\n{\n\tfn from_iter\u003cT: IntoIterator\u003cItem = L\u003e\u003e(iter: T) -\u003e Self {\n\t\tlet iter: I = iter.into_iter().collect();\n\t\tSelf::new(iter)\n\t}\n}\n\n// safety: the RawLocks must be send because they come from the Send Lockable\n#[allow(clippy::non_send_fields_in_send_ty)]\nunsafe impl\u003cL: Send\u003e Send for BoxedLockCollection\u003cL\u003e {}\nunsafe impl\u003cL: Sync\u003e Sync for BoxedLockCollection\u003cL\u003e {}\n\nimpl\u003cL\u003e Drop for BoxedLockCollection\u003cL\u003e {\n\t#[mutants::skip] // i can't test for a memory leak\n\t#[cfg(not(tarpaulin_include))]\n\tfn drop(\u0026mut self) {\n\t\tunsafe {\n\t\t\t// safety: this collection will never be locked again\n\t\t\tself.locks.clear();\n\t\t\t// safety: this was allocated using a box, and is now unique\n\t\t\tlet boxed: Box\u003cUnsafeCell\u003cL\u003e\u003e = Box::from_raw(self.data.cast_mut());\n\n\t\t\tdrop(boxed)\n\t\t}\n\t}\n}\n\nimpl\u003cT: ?Sized, L: AsRef\u003cT\u003e\u003e AsRef\u003cT\u003e for BoxedLockCollection\u003cL\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself.child().as_ref()\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cL: Debug\u003e Debug for BoxedLockCollection\u003cL\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tf.debug_struct(stringify!(BoxedLockCollection))\n\t\t\t.field(\"data\", \u0026self.data)\n\t\t\t.finish_non_exhaustive()\n\t}\n}\n\nimpl\u003cL: OwnedLockable + Default\u003e Default for BoxedLockCollection\u003cL\u003e {\n\tfn default() -\u003e Self {\n\t\tSelf::new(L::default())\n\t}\n}\n\nimpl\u003cL: OwnedLockable\u003e From\u003cL\u003e for BoxedLockCollection\u003cL\u003e {\n\tfn from(value: L) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\nimpl\u003cL\u003e BoxedLockCollection\u003cL\u003e {\n\t/// Gets the underlying collection, consuming this collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey, LockCollection};\n\t///\n\t/// let data1 = Mutex::new(42);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // data1 and data2 refer to distinct mutexes, so this won't panic\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = LockCollection::try_new(\u0026data).unwrap();\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let guard = lock.into_child().0.lock(key);\n\t/// assert_eq!(*guard, 42);\n\t/// ```\n\t#[must_use]\n\tpub fn into_child(mut self) -\u003e L {\n\t\tunsafe {\n\t\t\t// safety: this collection will never be used again\n\t\t\tstd::ptr::drop_in_place(\u0026mut self.locks);\n\t\t\t// safety: this was allocated using a box, and is now unique\n\t\t\tlet boxed: Box\u003cUnsafeCell\u003cL\u003e\u003e = Box::from_raw(self.data.cast_mut());\n\t\t\t// to prevent a double free\n\t\t\tstd::mem::forget(self);\n\n\t\t\tboxed.into_inner()\n\t\t}\n\t}\n\n\t// child_mut is immediate UB because it leads to mutable and immutable\n\t// references happening at the same time\n\n\t/// Gets an immutable reference to the underlying data\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey, LockCollection};\n\t///\n\t/// let data1 = Mutex::new(42);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // data1 and data2 refer to distinct mutexes, so this won't panic\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = LockCollection::try_new(\u0026data).unwrap();\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let guard = lock.child().0.lock(key);\n\t/// assert_eq!(*guard, 42);\n\t/// ```\n\t#[must_use]\n\tpub fn child(\u0026self) -\u003e \u0026L {\n\t\tunsafe {\n\t\t\tself.data\n\t\t\t\t.as_ref()\n\t\t\t\t.unwrap_unchecked()\n\t\t\t\t.get()\n\t\t\t\t.as_ref()\n\t\t\t\t.unwrap_unchecked()\n\t\t}\n\t}\n\n\t/// Gets the locks\n\tfn locks(\u0026self) -\u003e \u0026[\u0026dyn RawLock] {\n\t\t\u0026self.locks\n\t}\n}\n\nimpl\u003cL: OwnedLockable\u003e BoxedLockCollection\u003cL\u003e {\n\t/// Creates a new collection of owned locks.\n\t///\n\t/// Because the locks are owned, there's no need to do any checks for\n\t/// duplicate values.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t///\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t/// ```\n\t#[must_use]\n\tpub fn new(data: L) -\u003e Self {\n\t\t// safety: owned lockable types cannot contain duplicates\n\t\tunsafe { Self::new_unchecked(data) }\n\t}\n}\n\nimpl\u003c'a, L: OwnedLockable\u003e BoxedLockCollection\u003c\u0026'a L\u003e {\n\t/// Creates a new collection of owned locks.\n\t///\n\t/// Because the locks are owned, there's no need to do any checks for\n\t/// duplicate values.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t///\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = LockCollection::new_ref(\u0026data);\n\t/// ```\n\t#[must_use]\n\tpub fn new_ref(data: \u0026'a L) -\u003e Self {\n\t\t// safety: owned lockable types cannot contain duplicates\n\t\tunsafe { Self::new_unchecked(data) }\n\t}\n}\n\nimpl\u003cL: Lockable\u003e BoxedLockCollection\u003cL\u003e {\n\t/// Creates a new collections of locks.\n\t///\n\t/// # Safety\n\t///\n\t/// This results in undefined behavior if any locks are presented twice\n\t/// within this collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t///\n\t/// let data1 = Mutex::new(0);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // safety: data1 and data2 refer to distinct mutexes\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = unsafe { LockCollection::new_unchecked(\u0026data) };\n\t/// ```\n\t#[must_use]\n\tpub unsafe fn new_unchecked(data: L) -\u003e Self {\n\t\tlet data = Box::leak(Box::new(UnsafeCell::new(data)));\n\t\tlet data_ref = data.get().cast_const().as_ref().unwrap_unchecked();\n\n\t\tlet mut locks = Vec::new();\n\t\tdata_ref.get_ptrs(\u0026mut locks);\n\n\t\t// cast to *const () because fat pointers can't be converted to usize\n\t\tlocks.sort_by_key(|lock| (\u0026raw const **lock).cast::\u003c()\u003e() as usize);\n\n\t\t// safety we're just changing the lifetimes\n\t\tlet locks: Vec\u003c\u0026'static dyn RawLock\u003e = std::mem::transmute(locks);\n\t\tlet data = \u0026raw const *data;\n\t\tSelf { data, locks }\n\t}\n\n\t/// Creates a new collection of locks.\n\t///\n\t/// This returns `None` if any locks are found twice in the given\n\t/// collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t///\n\t/// let data1 = Mutex::new(0);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // data1 and data2 refer to distinct mutexes, so this won't panic\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = LockCollection::try_new(\u0026data).unwrap();\n\t/// ```\n\t#[must_use]\n\tpub fn try_new(data: L) -\u003e Option\u003cSelf\u003e {\n\t\t// safety: we are checking for duplicates before returning\n\t\tunsafe {\n\t\t\tlet this = Self::new_unchecked(data);\n\t\t\tif ordered_contains_duplicates(this.locks()) {\n\t\t\t\treturn None;\n\t\t\t}\n\t\t\tSome(this)\n\t\t}\n\t}\n\n\tpub fn scoped_lock\u003c'a, R\u003e(\u0026'a self, key: impl Keyable, f: impl Fn(L::DataMut\u003c'a\u003e) -\u003e R) -\u003e R {\n\t\tscoped_write(self, key, f)\n\t}\n\n\tpub fn scoped_try_lock\u003c'a, Key: Keyable, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(L::DataMut\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tscoped_try_write(self, key, f)\n\t}\n\n\t/// Locks the collection\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data. When the guard is dropped, the locks in the collection are also\n\t/// dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// ```\n\t#[must_use]\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::Guard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_write();\n\n\t\t\tLockGuard {\n\t\t\t\t// safety: we've already acquired the lock\n\t\t\t\tguard: self.child().guard(),\n\t\t\t\tkey,\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Attempts to lock the without blocking.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// locks when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If any locks in the collection are already locked, then an error\n\t/// containing the given key is returned.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// match lock.try_lock(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::Guard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tlet guard = unsafe {\n\t\t\tif !self.raw_try_write() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we've acquired the locks\n\t\t\tself.child().guard()\n\t\t};\n\n\t\tOk(LockGuard { guard, key })\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// let key = LockCollection::\u003c(Mutex\u003ci32\u003e, Mutex\u003c\u0026str\u003e)\u003e::unlock(guard);\n\t/// ```\n\tpub fn unlock(guard: LockGuard\u003cL::Guard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: Sharable\u003e BoxedLockCollection\u003cL\u003e {\n\tpub fn scoped_read\u003c'a, R\u003e(\u0026'a self, key: impl Keyable, f: impl Fn(L::DataRef\u003c'a\u003e) -\u003e R) -\u003e R {\n\t\tscoped_read(self, key, f)\n\t}\n\n\tpub fn scoped_try_read\u003c'a, Key: Keyable, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(L::DataRef\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tscoped_try_read(self, key, f)\n\t}\n\n\t/// Locks the collection, so that other threads can still read from it\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data immutably. When the guard is dropped, the locks in the collection\n\t/// are also dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// assert_eq!(*guard.0, 0);\n\t/// assert_eq!(*guard.1, \"\");\n\t/// ```\n\t#[must_use]\n\tpub fn read(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_read();\n\n\t\t\tLockGuard {\n\t\t\t\t// safety: we've already acquired the lock\n\t\t\t\tguard: self.child().read_guard(),\n\t\t\t\tkey,\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Attempts to lock the without blocking, in such a way that other threads\n\t/// can still read from the collection.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// shared access when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If any of the locks in the collection are already locked, then an error\n\t/// is returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(5), RwLock::new(\"6\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// match lock.try_read(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// assert_eq!(*guard.0, 5);\n\t/// assert_eq!(*guard.1, \"6\");\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_read(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tlet guard = unsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_read() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we've acquired the locks\n\t\t\tself.child().read_guard()\n\t\t};\n\n\t\tOk(LockGuard { guard, key })\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// let key = LockCollection::\u003c(RwLock\u003ci32\u003e, RwLock\u003c\u0026str\u003e)\u003e::unlock_read(guard);\n\t/// ```\n\tpub fn unlock_read(guard: LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: LockableIntoInner\u003e BoxedLockCollection\u003cL\u003e {\n\t/// Consumes this `BoxedLockCollection`, returning the underlying data.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t///\n\t/// let mutex = LockCollection::new([Mutex::new(0), Mutex::new(0)]);\n\t/// assert_eq!(mutex.into_inner(), [0, 0]);\n\t/// ```\n\t#[must_use]\n\tpub fn into_inner(self) -\u003e \u003cSelf as LockableIntoInner\u003e::Inner {\n\t\tLockableIntoInner::into_inner(self)\n\t}\n}\n\nimpl\u003c'a, L: 'a\u003e BoxedLockCollection\u003cL\u003e\nwhere\n\t\u0026'a L: IntoIterator,\n{\n\t/// Returns an iterator over references to each value in the collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = [Mutex::new(26), Mutex::new(1)];\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// let mut iter = lock.iter();\n\t/// let mutex = iter.next().unwrap();\n\t/// let guard = mutex.lock(key);\n\t///\n\t/// assert_eq!(*guard, 26);\n\t/// ```\n\t#[must_use]\n\tpub fn iter(\u0026'a self) -\u003e \u003c\u0026'a L as IntoIterator\u003e::IntoIter {\n\t\tself.into_iter()\n\t}\n}\n\n#[cfg(test)]\nmod tests {\n\tuse super::*;\n\tuse crate::{Mutex, RwLock, ThreadKey};\n\n\t#[test]\n\tfn from_iterator() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection: BoxedLockCollection\u003cVec\u003cMutex\u003c\u0026str\u003e\u003e\u003e =\n\t\t\t[Mutex::new(\"foo\"), Mutex::new(\"bar\"), Mutex::new(\"baz\")]\n\t\t\t\t.into_iter()\n\t\t\t\t.collect();\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], \"foo\");\n\t\tassert_eq!(*guard[1], \"bar\");\n\t\tassert_eq!(*guard[2], \"baz\");\n\t}\n\n\t#[test]\n\tfn from() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tBoxedLockCollection::from([Mutex::new(\"foo\"), Mutex::new(\"bar\"), Mutex::new(\"baz\")]);\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], \"foo\");\n\t\tassert_eq!(*guard[1], \"bar\");\n\t\tassert_eq!(*guard[2], \"baz\");\n\t}\n\n\t#[test]\n\tfn into_owned_iterator() {\n\t\tlet collection = BoxedLockCollection::new([Mutex::new(0), Mutex::new(1), Mutex::new(2)]);\n\t\tfor (i, mutex) in collection.into_iter().enumerate() {\n\t\t\tassert_eq!(mutex.into_inner(), i);\n\t\t}\n\t}\n\n\t#[test]\n\tfn into_ref_iterator() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::new([Mutex::new(0), Mutex::new(1), Mutex::new(2)]);\n\t\tfor (i, mutex) in (\u0026collection).into_iter().enumerate() {\n\t\t\tmutex.scoped_lock(\u0026mut key, |val| assert_eq!(*val, i))\n\t\t}\n\t}\n\n\t#[test]\n\tfn ref_iterator() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::new([Mutex::new(0), Mutex::new(1), Mutex::new(2)]);\n\t\tfor (i, mutex) in collection.iter().enumerate() {\n\t\t\tmutex.scoped_lock(\u0026mut key, |val| assert_eq!(*val, i))\n\t\t}\n\t}\n\n\t#[test]\n\t#[allow(clippy::float_cmp)]\n\tfn uses_correct_default() {\n\t\tlet collection =\n\t\t\tBoxedLockCollection::\u003c(Mutex\u003cf64\u003e, Mutex\u003cOption\u003ci32\u003e\u003e, Mutex\u003cusize\u003e)\u003e::default();\n\t\tlet tuple = collection.into_inner();\n\t\tassert_eq!(tuple.0, 0.0);\n\t\tassert!(tuple.1.is_none());\n\t\tassert_eq!(tuple.2, 0)\n\t}\n\n\t#[test]\n\tfn non_duplicates_allowed() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(1);\n\t\tassert!(BoxedLockCollection::try_new([\u0026mutex1, \u0026mutex2]).is_some())\n\t}\n\n\t#[test]\n\tfn duplicates_not_allowed() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tassert!(BoxedLockCollection::try_new([\u0026mutex1, \u0026mutex1]).is_none())\n\t}\n\n\t#[test]\n\tfn scoped_read_sees_changes() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = BoxedLockCollection::new(mutexes);\n\t\tcollection.scoped_lock(\u0026mut key, |guard| *guard[0] = 128);\n\n\t\tlet sum = collection.scoped_read(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 128);\n\t\t\tassert_eq!(*guard[1], 42);\n\t\t\t*guard[0] + *guard[1]\n\t\t});\n\n\t\tassert_eq!(sum, 128 + 42);\n\t}\n\n\t#[test]\n\tfn scoped_try_lock_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::new([Mutex::new(1), Mutex::new(2)]);\n\t\tlet guard = collection.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = collection.scoped_try_lock(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn scoped_try_read_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::new([RwLock::new(1), RwLock::new(2)]);\n\t\tlet guard = collection.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = collection.scoped_try_read(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn try_lock_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::new([Mutex::new(1), Mutex::new(2)]);\n\t\tlet guard = collection.try_lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_lock(key);\n\t\t\t\tassert!(guard.is_err());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn try_read_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::new([RwLock::new(1), RwLock::new(2)]);\n\t\tlet guard = collection.try_read(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_read(key);\n\t\t\t\tassert!(guard.is_ok());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn try_lock_fails_with_one_exclusive_lock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = [Mutex::new(1), Mutex::new(2)];\n\t\tlet collection = BoxedLockCollection::new_ref(\u0026locks);\n\t\tlet guard = locks[1].try_lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_lock(key);\n\t\t\t\tassert!(guard.is_err());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn try_read_fails_during_exclusive_lock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::new([RwLock::new(1), RwLock::new(2)]);\n\t\tlet guard = collection.try_lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_read(key);\n\t\t\t\tassert!(guard.is_err());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn try_read_fails_with_one_exclusive_lock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = [RwLock::new(1), RwLock::new(2)];\n\t\tlet collection = BoxedLockCollection::new_ref(\u0026locks);\n\t\tlet guard = locks[1].try_write(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_read(key);\n\t\t\t\tassert!(guard.is_err());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn unlock_collection_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex1 = Mutex::new(\"foo\");\n\t\tlet mutex2 = Mutex::new(\"bar\");\n\t\tlet collection = BoxedLockCollection::try_new((\u0026mutex1, \u0026mutex2)).unwrap();\n\t\tlet guard = collection.lock(key);\n\t\tlet key = BoxedLockCollection::\u003c(\u0026Mutex\u003c_\u003e, \u0026Mutex\u003c_\u003e)\u003e::unlock(guard);\n\n\t\tassert!(mutex1.try_lock(key).is_ok())\n\t}\n\n\t#[test]\n\tfn read_unlock_collection_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock1 = RwLock::new(\"foo\");\n\t\tlet lock2 = RwLock::new(\"bar\");\n\t\tlet collection = BoxedLockCollection::try_new((\u0026lock1, \u0026lock2)).unwrap();\n\t\tlet guard = collection.read(key);\n\t\tlet key = BoxedLockCollection::\u003c(\u0026RwLock\u003c_\u003e, \u0026RwLock\u003c_\u003e)\u003e::unlock_read(guard);\n\n\t\tassert!(lock1.try_write(key).is_ok())\n\t}\n\n\t#[test]\n\tfn into_inner_works() {\n\t\tlet collection = BoxedLockCollection::new((Mutex::new(\"Hello\"), Mutex::new(47)));\n\t\tassert_eq!(collection.into_inner(), (\"Hello\", 47))\n\t}\n\n\t#[test]\n\tfn works_in_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex1 = RwLock::new(0);\n\t\tlet mutex2 = RwLock::new(1);\n\t\tlet collection =\n\t\t\tBoxedLockCollection::try_new(BoxedLockCollection::try_new([\u0026mutex1, \u0026mutex2]).unwrap())\n\t\t\t\t.unwrap();\n\n\t\tlet mut guard = collection.lock(key);\n\t\tassert!(mutex1.is_locked());\n\t\tassert!(mutex2.is_locked());\n\t\tassert_eq!(*guard[0], 0);\n\t\tassert_eq!(*guard[1], 1);\n\t\t*guard[0] = 2;\n\t\tlet key = BoxedLockCollection::\u003cBoxedLockCollection\u003c[\u0026RwLock\u003c_\u003e; 2]\u003e\u003e::unlock(guard);\n\n\t\tlet guard = collection.read(key);\n\t\tassert!(mutex1.is_locked());\n\t\tassert!(mutex2.is_locked());\n\t\tassert_eq!(*guard[0], 2);\n\t\tassert_eq!(*guard[1], 1);\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn as_ref_works() {\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet collection = BoxedLockCollection::new_ref(\u0026mutexes);\n\n\t\tassert!(std::ptr::addr_eq(\u0026mutexes, collection.as_ref()))\n\t}\n\n\t#[test]\n\tfn child() {\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet collection = BoxedLockCollection::new_ref(\u0026mutexes);\n\n\t\tassert!(std::ptr::addr_eq(\u0026mutexes, *collection.child()))\n\t}\n}\n","traces":[{"line":21,"address":[226016],"length":1,"stats":{"Line":24}},{"line":22,"address":[],"length":0,"stats":{"Line":23}},{"line":25,"address":[1547968,1548096,1548352,1548480,1547840,1548224],"length":1,"stats":{"Line":6}},{"line":26,"address":[1548109,1547981,1548237,1547853,1548365,1548493],"length":1,"stats":{"Line":6}},{"line":27,"address":[1548319,1548063,1548191,1548447,1548575,1547935],"length":1,"stats":{"Line":6}},{"line":30,"address":[],"length":0,"stats":{"Line":7}},{"line":31,"address":[],"length":0,"stats":{"Line":14}},{"line":32,"address":[],"length":0,"stats":{"Line":7}},{"line":36,"address":[1549840,1549872,1549680,1549808,1549648,1549712,1549616,1549776,1549744],"length":1,"stats":{"Line":10}},{"line":37,"address":[180901],"length":1,"stats":{"Line":10}},{"line":40,"address":[189216],"length":1,"stats":{"Line":4}},{"line":41,"address":[1550005,1549941,1549973,1549909],"length":1,"stats":{"Line":4}},{"line":44,"address":[1550368,1550480,1550144,1550256,1550032],"length":1,"stats":{"Line":4}},{"line":45,"address":[],"length":0,"stats":{"Line":8}},{"line":46,"address":[],"length":0,"stats":{"Line":4}},{"line":62,"address":[],"length":0,"stats":{"Line":1}},{"line":63,"address":[],"length":0,"stats":{"Line":1}},{"line":66,"address":[1550656],"length":1,"stats":{"Line":1}},{"line":67,"address":[],"length":0,"stats":{"Line":1}},{"line":70,"address":[],"length":0,"stats":{"Line":7}},{"line":71,"address":[],"length":0,"stats":{"Line":7}},{"line":86,"address":[],"length":0,"stats":{"Line":1}},{"line":87,"address":[],"length":0,"stats":{"Line":1}},{"line":90,"address":[1551104,1551136,1551232,1551200],"length":1,"stats":{"Line":4}},{"line":91,"address":[],"length":0,"stats":{"Line":4}},{"line":103,"address":[],"length":0,"stats":{"Line":2}},{"line":104,"address":[1551278,1551342],"length":1,"stats":{"Line":2}},{"line":115,"address":[],"length":0,"stats":{"Line":1}},{"line":116,"address":[],"length":0,"stats":{"Line":1}},{"line":127,"address":[],"length":0,"stats":{"Line":1}},{"line":128,"address":[1551461],"length":1,"stats":{"Line":1}},{"line":135,"address":[],"length":0,"stats":{"Line":1}},{"line":136,"address":[],"length":0,"stats":{"Line":1}},{"line":137,"address":[1551535],"length":1,"stats":{"Line":1}},{"line":162,"address":[],"length":0,"stats":{"Line":1}},{"line":163,"address":[1551573],"length":1,"stats":{"Line":1}},{"line":178,"address":[],"length":0,"stats":{"Line":1}},{"line":179,"address":[1551614],"length":1,"stats":{"Line":1}},{"line":184,"address":[],"length":0,"stats":{"Line":1}},{"line":185,"address":[],"length":0,"stats":{"Line":1}},{"line":209,"address":[],"length":0,"stats":{"Line":3}},{"line":212,"address":[],"length":0,"stats":{"Line":3}},{"line":214,"address":[],"length":0,"stats":{"Line":3}},{"line":216,"address":[],"length":0,"stats":{"Line":3}},{"line":218,"address":[],"length":0,"stats":{"Line":3}},{"line":244,"address":[172560],"length":1,"stats":{"Line":25}},{"line":246,"address":[226729],"length":1,"stats":{"Line":27}},{"line":256,"address":[],"length":0,"stats":{"Line":37}},{"line":257,"address":[],"length":0,"stats":{"Line":37}},{"line":276,"address":[],"length":0,"stats":{"Line":16}},{"line":278,"address":[],"length":0,"stats":{"Line":19}},{"line":297,"address":[1555984,1555952,1555920],"length":1,"stats":{"Line":4}},{"line":299,"address":[],"length":0,"stats":{"Line":4}},{"line":324,"address":[226048,226490,226517],"length":1,"stats":{"Line":38}},{"line":325,"address":[180945,181052],"length":1,"stats":{"Line":80}},{"line":326,"address":[],"length":0,"stats":{"Line":41}},{"line":328,"address":[150063],"length":1,"stats":{"Line":41}},{"line":329,"address":[202526],"length":1,"stats":{"Line":39}},{"line":332,"address":[],"length":0,"stats":{"Line":103}},{"line":335,"address":[1557371,1565542,1564555,1563545,1568036,1560398,1558881,1559372,1561483,1562001,1565057,1569068,1556369,1562996,1569564,1568585,1558382,1560875,1557851,1562474,1556876,1566028,1566523,1564052,1566986,1567466,1559850],"length":1,"stats":{"Line":42}},{"line":336,"address":[150235],"length":1,"stats":{"Line":41}},{"line":358,"address":[226816,227062],"length":1,"stats":{"Line":15}},{"line":361,"address":[1570587,1570040,1570859,1571424,1570315,1569760,1571691,1571144],"length":1,"stats":{"Line":15}},{"line":362,"address":[181733,181794],"length":1,"stats":{"Line":30}},{"line":363,"address":[190050],"length":1,"stats":{"Line":1}},{"line":365,"address":[1570956,1569857,1571788,1570684,1570412,1571521,1571244,1570140],"length":1,"stats":{"Line":14}},{"line":369,"address":[],"length":0,"stats":{"Line":9}},{"line":370,"address":[],"length":0,"stats":{"Line":9}},{"line":373,"address":[1572224,1572256,1572288],"length":1,"stats":{"Line":3}},{"line":378,"address":[],"length":0,"stats":{"Line":3}},{"line":401,"address":[226690,226544],"length":1,"stats":{"Line":22}},{"line":404,"address":[1572334,1574286,1572880,1574174,1572558,1573344,1574046,1573502,1573630,1574576,1573040,1572446,1573776,1572704,1574416,1573198,1573918],"length":1,"stats":{"Line":21}},{"line":408,"address":[226620],"length":1,"stats":{"Line":19}},{"line":443,"address":[],"length":0,"stats":{"Line":6}},{"line":445,"address":[1574944,1574782,1575177,1575502,1575358,1575134,1575312,1575545,1574990,1574736],"length":1,"stats":{"Line":11}},{"line":446,"address":[1575183,1575369,1575551,1575001,1574793],"length":1,"stats":{"Line":4}},{"line":450,"address":[203275,203305],"length":1,"stats":{"Line":4}},{"line":453,"address":[1575425,1575616,1575057,1574849,1575248],"length":1,"stats":{"Line":2}},{"line":473,"address":[],"length":0,"stats":{"Line":11}},{"line":474,"address":[],"length":0,"stats":{"Line":12}},{"line":475,"address":[],"length":0,"stats":{"Line":0}},{"line":480,"address":[],"length":0,"stats":{"Line":4}},{"line":481,"address":[],"length":0,"stats":{"Line":4}},{"line":484,"address":[1576784,1576816],"length":1,"stats":{"Line":2}},{"line":489,"address":[],"length":0,"stats":{"Line":2}},{"line":512,"address":[],"length":0,"stats":{"Line":8}},{"line":515,"address":[181456],"length":1,"stats":{"Line":8}},{"line":519,"address":[1577718,1577448,1577126,1577590,1576902,1577014,1577272],"length":1,"stats":{"Line":7}},{"line":555,"address":[190112,190319],"length":1,"stats":{"Line":4}},{"line":558,"address":[190144,190194],"length":1,"stats":{"Line":7}},{"line":559,"address":[190205],"length":1,"stats":{"Line":3}},{"line":563,"address":[1578103,1578314,1577895,1578133,1577925,1578287],"length":1,"stats":{"Line":2}},{"line":566,"address":[190261],"length":1,"stats":{"Line":1}},{"line":584,"address":[],"length":0,"stats":{"Line":1}},{"line":585,"address":[1578382],"length":1,"stats":{"Line":1}},{"line":586,"address":[],"length":0,"stats":{"Line":0}},{"line":602,"address":[],"length":0,"stats":{"Line":2}},{"line":603,"address":[],"length":0,"stats":{"Line":2}},{"line":629,"address":[],"length":0,"stats":{"Line":1}},{"line":630,"address":[],"length":0,"stats":{"Line":1}}],"covered":98,"coverable":100},{"path":["/","home","botahamec","Projects","happylock","src","collection","guard.rs"],"content":"use std::fmt::{Debug, Display};\nuse std::hash::Hash;\nuse std::ops::{Deref, DerefMut};\n\nuse super::LockGuard;\n\n#[mutants::skip] // hashing involves RNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard: Hash\u003e Hash for LockGuard\u003cGuard\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.guard.hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard: Debug\u003e Debug for LockGuard\u003cGuard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cGuard: Display\u003e Display for LockGuard\u003cGuard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cGuard\u003e Deref for LockGuard\u003cGuard\u003e {\n\ttype Target = Guard;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t\u0026self.guard\n\t}\n}\n\nimpl\u003cGuard\u003e DerefMut for LockGuard\u003cGuard\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t\u0026mut self.guard\n\t}\n}\n\nimpl\u003cGuard\u003e AsRef\u003cGuard\u003e for LockGuard\u003cGuard\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026Guard {\n\t\t\u0026self.guard\n\t}\n}\n\nimpl\u003cGuard\u003e AsMut\u003cGuard\u003e for LockGuard\u003cGuard\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut Guard {\n\t\t\u0026mut self.guard\n\t}\n}\n\n#[cfg(test)]\nmod tests {\n\tuse crate::collection::OwnedLockCollection;\n\tuse crate::{LockCollection, Mutex, RwLock, ThreadKey};\n\n\t#[test]\n\tfn guard_display_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = OwnedLockCollection::new(RwLock::new(\"Hello, world!\"));\n\t\tlet guard = lock.read(key);\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn deref_mut_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = (Mutex::new(1), Mutex::new(2));\n\t\tlet lock = LockCollection::new_ref(\u0026locks);\n\t\tlet mut guard = lock.lock(key);\n\t\t*guard.0 = 3;\n\t\tlet key = LockCollection::\u003c(Mutex\u003c_\u003e, Mutex\u003c_\u003e)\u003e::unlock(guard);\n\n\t\tlet guard = locks.0.lock(key);\n\t\tassert_eq!(*guard, 3);\n\t\tlet key = Mutex::unlock(guard);\n\n\t\tlet guard = locks.1.lock(key);\n\t\tassert_eq!(*guard, 2);\n\t}\n\n\t#[test]\n\tfn as_ref_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = (Mutex::new(1), Mutex::new(2));\n\t\tlet lock = LockCollection::new_ref(\u0026locks);\n\t\tlet mut guard = lock.lock(key);\n\t\t*guard.0 = 3;\n\t\tlet key = LockCollection::\u003c(Mutex\u003c_\u003e, Mutex\u003c_\u003e)\u003e::unlock(guard);\n\n\t\tlet guard = locks.0.lock(key);\n\t\tassert_eq!(guard.as_ref(), \u00263);\n\t\tlet key = Mutex::unlock(guard);\n\n\t\tlet guard = locks.1.lock(key);\n\t\tassert_eq!(guard.as_ref(), \u00262);\n\t}\n\n\t#[test]\n\tfn as_mut_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = (Mutex::new(1), Mutex::new(2));\n\t\tlet lock = LockCollection::new_ref(\u0026locks);\n\t\tlet mut guard = lock.lock(key);\n\t\tlet guard_mut = guard.as_mut();\n\t\t*guard_mut.0 = 3;\n\t\tlet key = LockCollection::\u003c(Mutex\u003c_\u003e, Mutex\u003c_\u003e)\u003e::unlock(guard);\n\n\t\tlet guard = locks.0.lock(key);\n\t\tassert_eq!(guard.as_ref(), \u00263);\n\t\tlet key = Mutex::unlock(guard);\n\n\t\tlet guard = locks.1.lock(key);\n\t\tassert_eq!(guard.as_ref(), \u00262);\n\t}\n}\n","traces":[{"line":24,"address":[],"length":0,"stats":{"Line":1}},{"line":25,"address":[],"length":0,"stats":{"Line":1}},{"line":32,"address":[],"length":0,"stats":{"Line":18}},{"line":33,"address":[],"length":0,"stats":{"Line":0}},{"line":38,"address":[],"length":0,"stats":{"Line":8}},{"line":39,"address":[],"length":0,"stats":{"Line":0}},{"line":44,"address":[],"length":0,"stats":{"Line":2}},{"line":45,"address":[],"length":0,"stats":{"Line":0}},{"line":50,"address":[],"length":0,"stats":{"Line":4}},{"line":51,"address":[],"length":0,"stats":{"Line":0}}],"covered":6,"coverable":10},{"path":["/","home","botahamec","Projects","happylock","src","collection","owned.rs"],"content":"use crate::lockable::{\n\tLockable, LockableGetMut, LockableIntoInner, OwnedLockable, RawLock, Sharable,\n};\nuse crate::{Keyable, ThreadKey};\n\nuse super::utils::{scoped_read, scoped_try_read, scoped_try_write, scoped_write};\nuse super::{utils, LockGuard, OwnedLockCollection};\n\nunsafe impl\u003cL: Lockable\u003e RawLock for OwnedLockCollection\u003cL\u003e {\n\t#[mutants::skip] // this should never run\n\t#[cfg(not(tarpaulin_include))]\n\tfn poison(\u0026self) {\n\t\tlet locks = utils::get_locks_unsorted(\u0026self.data);\n\t\tfor lock in locks {\n\t\t\tlock.poison();\n\t\t}\n\t}\n\n\tunsafe fn raw_write(\u0026self) {\n\t\tutils::ordered_write(\u0026utils::get_locks_unsorted(\u0026self.data))\n\t}\n\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool {\n\t\tlet locks = utils::get_locks_unsorted(\u0026self.data);\n\t\tutils::ordered_try_write(\u0026locks)\n\t}\n\n\tunsafe fn raw_unlock_write(\u0026self) {\n\t\tlet locks = utils::get_locks_unsorted(\u0026self.data);\n\t\tfor lock in locks {\n\t\t\tlock.raw_unlock_write();\n\t\t}\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tutils::ordered_read(\u0026utils::get_locks_unsorted(\u0026self.data))\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tlet locks = utils::get_locks_unsorted(\u0026self.data);\n\t\tutils::ordered_try_read(\u0026locks)\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tlet locks = utils::get_locks_unsorted(\u0026self.data);\n\t\tfor lock in locks {\n\t\t\tlock.raw_unlock_read();\n\t\t}\n\t}\n}\n\nunsafe impl\u003cL: Lockable\u003e Lockable for OwnedLockCollection\u003cL\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= L::Guard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= L::DataMut\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\t#[mutants::skip] // It's hard to test lkocks in an OwnedLockCollection, because they're owned\n\t#[cfg(not(tarpaulin_include))]\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tptrs.push(self)\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tself.data.guard()\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.data.data_mut()\n\t}\n}\n\nimpl\u003cL: LockableGetMut\u003e LockableGetMut for OwnedLockCollection\u003cL\u003e {\n\ttype Inner\u003c'a\u003e\n\t\t= L::Inner\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tself.data.get_mut()\n\t}\n}\n\nimpl\u003cL: LockableIntoInner\u003e LockableIntoInner for OwnedLockCollection\u003cL\u003e {\n\ttype Inner = L::Inner;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tself.data.into_inner()\n\t}\n}\n\nunsafe impl\u003cL: Sharable\u003e Sharable for OwnedLockCollection\u003cL\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= L::ReadGuard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= L::DataRef\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tself.data.read_guard()\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tself.data.data_ref()\n\t}\n}\n\nunsafe impl\u003cL: OwnedLockable\u003e OwnedLockable for OwnedLockCollection\u003cL\u003e {}\n\nimpl\u003cL\u003e IntoIterator for OwnedLockCollection\u003cL\u003e\nwhere\n\tL: IntoIterator,\n{\n\ttype Item = \u003cL as IntoIterator\u003e::Item;\n\ttype IntoIter = \u003cL as IntoIterator\u003e::IntoIter;\n\n\tfn into_iter(self) -\u003e Self::IntoIter {\n\t\tself.data.into_iter()\n\t}\n}\n\nimpl\u003cL: OwnedLockable, I: FromIterator\u003cL\u003e + OwnedLockable\u003e FromIterator\u003cL\u003e\n\tfor OwnedLockCollection\u003cI\u003e\n{\n\tfn from_iter\u003cT: IntoIterator\u003cItem = L\u003e\u003e(iter: T) -\u003e Self {\n\t\tlet iter: I = iter.into_iter().collect();\n\t\tSelf::new(iter)\n\t}\n}\n\nimpl\u003cE: OwnedLockable + Extend\u003cL\u003e, L: OwnedLockable\u003e Extend\u003cL\u003e for OwnedLockCollection\u003cE\u003e {\n\tfn extend\u003cT: IntoIterator\u003cItem = L\u003e\u003e(\u0026mut self, iter: T) {\n\t\tself.data.extend(iter)\n\t}\n}\n\n// AsRef can't be implemented because an impl of AsRef\u003cL\u003e for L could break the\n// invariant that there is only one way to lock the collection. AsMut is fine,\n// because the collection can't be locked as long as the reference is valid.\n\nimpl\u003cT: ?Sized, L: AsMut\u003cT\u003e\u003e AsMut\u003cT\u003e for OwnedLockCollection\u003cL\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself.data.as_mut()\n\t}\n}\n\nimpl\u003cL: OwnedLockable + Default\u003e Default for OwnedLockCollection\u003cL\u003e {\n\tfn default() -\u003e Self {\n\t\tSelf::new(L::default())\n\t}\n}\n\nimpl\u003cL: OwnedLockable\u003e From\u003cL\u003e for OwnedLockCollection\u003cL\u003e {\n\tfn from(value: L) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\nimpl\u003cL: OwnedLockable\u003e OwnedLockCollection\u003cL\u003e {\n\t/// Creates a new collection of owned locks.\n\t///\n\t/// Because the locks are owned, there's no need to do any checks for\n\t/// duplicate values. The locks also don't need to be sorted by memory\n\t/// address because they aren't used anywhere else.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t/// ```\n\t#[must_use]\n\tpub const fn new(data: L) -\u003e Self {\n\t\tSelf { data }\n\t}\n\n\tpub fn scoped_lock\u003c'a, R\u003e(\u0026'a self, key: impl Keyable, f: impl Fn(L::DataMut\u003c'a\u003e) -\u003e R) -\u003e R {\n\t\tscoped_write(self, key, f)\n\t}\n\n\tpub fn scoped_try_lock\u003c'a, Key: Keyable, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(L::DataMut\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tscoped_try_write(self, key, f)\n\t}\n\n\t/// Locks the collection\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data. When the guard is dropped, the locks in the collection are also\n\t/// dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// ```\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::Guard\u003c'_\u003e\u003e {\n\t\tlet guard = unsafe {\n\t\t\t// safety: we have the thread key, and these locks happen in a\n\t\t\t// predetermined order\n\t\t\tself.raw_write();\n\n\t\t\t// safety: we've locked all of this already\n\t\t\tself.data.guard()\n\t\t};\n\n\t\tLockGuard { guard, key }\n\t}\n\n\t/// Attempts to lock the without blocking.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// locks when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If any of the locks in this collection are already locked, this returns\n\t/// an error containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// match lock.try_lock(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::Guard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tlet guard = unsafe {\n\t\t\tif !self.raw_try_write() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we've acquired the locks\n\t\t\tself.data.guard()\n\t\t};\n\n\t\tOk(LockGuard { guard, key })\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// let key = OwnedLockCollection::\u003c(Mutex\u003ci32\u003e, Mutex\u003c\u0026str\u003e)\u003e::unlock(guard);\n\t/// ```\n\t#[allow(clippy::missing_const_for_fn)]\n\tpub fn unlock(guard: LockGuard\u003cL::Guard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: Sharable\u003e OwnedLockCollection\u003cL\u003e {\n\tpub fn scoped_read\u003c'a, R\u003e(\u0026'a self, key: impl Keyable, f: impl Fn(L::DataRef\u003c'a\u003e) -\u003e R) -\u003e R {\n\t\tscoped_read(self, key, f)\n\t}\n\n\tpub fn scoped_try_read\u003c'a, Key: Keyable, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(L::DataRef\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tscoped_try_read(self, key, f)\n\t}\n\n\t/// Locks the collection, so that other threads can still read from it\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data immutably. When the guard is dropped, the locks in the collection\n\t/// are also dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// assert_eq!(*guard.0, 0);\n\t/// assert_eq!(*guard.1, \"\");\n\t/// ```\n\tpub fn read(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_read();\n\n\t\t\tLockGuard {\n\t\t\t\t// safety: we've already acquired the lock\n\t\t\t\tguard: self.data.read_guard(),\n\t\t\t\tkey,\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Attempts to lock the without blocking, in such a way that other threads\n\t/// can still read from the collection.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// shared access when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If any of the locks in this collection can't be acquired, then an error\n\t/// is returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(5), RwLock::new(\"6\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// match lock.try_read(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// assert_eq!(*guard.0, 5);\n\t/// assert_eq!(*guard.1, \"6\");\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_read(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tlet guard = unsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_read() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we've acquired the locks\n\t\t\tself.data.read_guard()\n\t\t};\n\n\t\tOk(LockGuard { guard, key })\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// let key = OwnedLockCollection::\u003c(RwLock\u003ci32\u003e, RwLock\u003c\u0026str\u003e)\u003e::unlock_read(guard);\n\t/// ```\n\t#[allow(clippy::missing_const_for_fn)]\n\tpub fn unlock_read(guard: LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL\u003e OwnedLockCollection\u003cL\u003e {\n\t/// Gets the underlying collection, consuming this collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let data = (Mutex::new(42), Mutex::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let inner = lock.into_child();\n\t/// let guard = inner.0.lock(key);\n\t/// assert_eq!(*guard, 42);\n\t/// ```\n\t#[must_use]\n\tpub fn into_child(self) -\u003e L {\n\t\tself.data\n\t}\n\n\t/// Gets a mutable reference to the underlying collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let data = (Mutex::new(42), Mutex::new(\"\"));\n\t/// let mut lock = OwnedLockCollection::new(data);\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut inner = lock.child_mut();\n\t/// let guard = inner.0.get_mut();\n\t/// assert_eq!(*guard, 42);\n\t/// ```\n\t#[must_use]\n\tpub fn child_mut(\u0026mut self) -\u003e \u0026mut L {\n\t\t\u0026mut self.data\n\t}\n}\n\nimpl\u003cL: LockableGetMut\u003e OwnedLockCollection\u003cL\u003e {\n\t/// Gets a mutable reference to the data behind this `OwnedLockCollection`.\n\t///\n\t/// Since this call borrows the `OwnedLockCollection` mutably, no actual\n\t/// locking needs to take place - the mutable borrow statically guarantees\n\t/// no locks exist.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let mut mutex = OwnedLockCollection::new([Mutex::new(0), Mutex::new(0)]);\n\t/// assert_eq!(mutex.get_mut(), [\u0026mut 0, \u0026mut 0]);\n\t/// ```\n\tpub fn get_mut(\u0026mut self) -\u003e L::Inner\u003c'_\u003e {\n\t\tLockableGetMut::get_mut(self)\n\t}\n}\n\nimpl\u003cL: LockableIntoInner\u003e OwnedLockCollection\u003cL\u003e {\n\t/// Consumes this `OwnedLockCollection`, returning the underlying data.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let mutex = OwnedLockCollection::new([Mutex::new(0), Mutex::new(0)]);\n\t/// assert_eq!(mutex.into_inner(), [0, 0]);\n\t/// ```\n\t#[must_use]\n\tpub fn into_inner(self) -\u003e L::Inner {\n\t\tLockableIntoInner::into_inner(self)\n\t}\n}\n\n#[cfg(test)]\nmod tests {\n\tuse super::*;\n\tuse crate::{Mutex, RwLock, ThreadKey};\n\n\t#[test]\n\tfn get_mut_applies_changes() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mut collection = OwnedLockCollection::new([Mutex::new(\"foo\"), Mutex::new(\"bar\")]);\n\t\tassert_eq!(*collection.get_mut()[0], \"foo\");\n\t\tassert_eq!(*collection.get_mut()[1], \"bar\");\n\t\t*collection.get_mut()[0] = \"baz\";\n\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], \"baz\");\n\t\tassert_eq!(*guard[1], \"bar\");\n\t}\n\n\t#[test]\n\tfn into_inner_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::from([Mutex::new(\"foo\")]);\n\t\tlet mut guard = collection.lock(key);\n\t\t*guard[0] = \"bar\";\n\t\tdrop(guard);\n\n\t\tlet array = collection.into_inner();\n\t\tassert_eq!(array.len(), 1);\n\t\tassert_eq!(array[0], \"bar\");\n\t}\n\n\t#[test]\n\tfn from_into_iter_is_correct() {\n\t\tlet array = [Mutex::new(0), Mutex::new(1), Mutex::new(2), Mutex::new(3)];\n\t\tlet mut collection: OwnedLockCollection\u003cVec\u003cMutex\u003cusize\u003e\u003e\u003e = array.into_iter().collect();\n\t\tassert_eq!(collection.get_mut().len(), 4);\n\t\tfor (i, lock) in collection.into_iter().enumerate() {\n\t\t\tassert_eq!(lock.into_inner(), i);\n\t\t}\n\t}\n\n\t#[test]\n\tfn from_iter_is_correct() {\n\t\tlet array = [Mutex::new(0), Mutex::new(1), Mutex::new(2), Mutex::new(3)];\n\t\tlet mut collection: OwnedLockCollection\u003cVec\u003cMutex\u003cusize\u003e\u003e\u003e = array.into_iter().collect();\n\t\tlet collection: \u0026mut Vec\u003c_\u003e = collection.as_mut();\n\t\tassert_eq!(collection.len(), 4);\n\t\tfor (i, lock) in collection.iter_mut().enumerate() {\n\t\t\tassert_eq!(*lock.get_mut(), i);\n\t\t}\n\t}\n\n\t#[test]\n\tfn scoped_read_works() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new([RwLock::new(24), RwLock::new(42)]);\n\t\tlet sum = collection.scoped_read(\u0026mut key, |guard| guard[0] + guard[1]);\n\t\tassert_eq!(sum, 24 + 42);\n\t}\n\n\t#[test]\n\tfn scoped_lock_works() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new([RwLock::new(24), RwLock::new(42)]);\n\t\tcollection.scoped_lock(\u0026mut key, |guard| *guard[0] += *guard[1]);\n\n\t\tlet sum = collection.scoped_lock(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 24 + 42);\n\t\t\tassert_eq!(*guard[1], 42);\n\t\t\t*guard[0] + *guard[1]\n\t\t});\n\n\t\tassert_eq!(sum, 24 + 42 + 42);\n\t}\n\n\t#[test]\n\tfn scoped_try_lock_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new([Mutex::new(1), Mutex::new(2)]);\n\t\tlet guard = collection.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = collection.scoped_try_lock(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn scoped_try_read_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new([RwLock::new(1), RwLock::new(2)]);\n\t\tlet guard = collection.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = collection.scoped_try_read(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn try_lock_works_on_unlocked() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new((Mutex::new(0), Mutex::new(1)));\n\t\tlet guard = collection.try_lock(key).unwrap();\n\t\tassert_eq!(*guard.0, 0);\n\t\tassert_eq!(*guard.1, 1);\n\t}\n\n\t#[test]\n\tfn try_lock_fails_on_locked() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new((Mutex::new(0), Mutex::new(1)));\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\t#[allow(unused)]\n\t\t\t\tlet guard = collection.lock(key);\n\t\t\t\tstd::mem::forget(guard);\n\t\t\t});\n\t\t});\n\n\t\tassert!(collection.try_lock(key).is_err());\n\t}\n\n\t#[test]\n\tfn try_read_succeeds_for_unlocked_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = OwnedLockCollection::new(mutexes);\n\t\tlet guard = collection.try_read(key).unwrap();\n\t\tassert_eq!(*guard[0], 24);\n\t\tassert_eq!(*guard[1], 42);\n\t}\n\n\t#[test]\n\tfn try_read_fails_on_locked() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new((RwLock::new(0), RwLock::new(1)));\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\t#[allow(unused)]\n\t\t\t\tlet guard = collection.lock(key);\n\t\t\t\tstd::mem::forget(guard);\n\t\t\t});\n\t\t});\n\n\t\tassert!(collection.try_read(key).is_err());\n\t}\n\n\t#[test]\n\tfn can_read_twice_on_different_threads() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = OwnedLockCollection::new(mutexes);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.read(key);\n\t\t\t\tassert_eq!(*guard[0], 24);\n\t\t\t\tassert_eq!(*guard[1], 42);\n\t\t\t\tstd::mem::forget(guard);\n\t\t\t});\n\t\t});\n\n\t\tlet guard = collection.try_read(key).unwrap();\n\t\tassert_eq!(*guard[0], 24);\n\t\tassert_eq!(*guard[1], 42);\n\t}\n\n\t#[test]\n\tfn unlock_collection_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new((Mutex::new(\"foo\"), Mutex::new(\"bar\")));\n\t\tlet guard = collection.lock(key);\n\n\t\tlet key = OwnedLockCollection::\u003c(Mutex\u003c_\u003e, Mutex\u003c_\u003e)\u003e::unlock(guard);\n\t\tassert!(collection.try_lock(key).is_ok())\n\t}\n\n\t#[test]\n\tfn read_unlock_collection_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new((RwLock::new(\"foo\"), RwLock::new(\"bar\")));\n\t\tlet guard = collection.read(key);\n\n\t\tlet key = OwnedLockCollection::\u003c(\u0026RwLock\u003c_\u003e, \u0026RwLock\u003c_\u003e)\u003e::unlock_read(guard);\n\t\tassert!(collection.try_lock(key).is_ok())\n\t}\n\n\t#[test]\n\tfn default_works() {\n\t\ttype MyCollection = OwnedLockCollection\u003c(Mutex\u003ci32\u003e, Mutex\u003cOption\u003ci32\u003e\u003e, Mutex\u003cString\u003e)\u003e;\n\t\tlet collection = MyCollection::default();\n\t\tlet inner = collection.into_inner();\n\t\tassert_eq!(inner.0, 0);\n\t\tassert_eq!(inner.1, None);\n\t\tassert_eq!(inner.2, String::new());\n\t}\n\n\t#[test]\n\tfn can_be_extended() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(1);\n\t\tlet mut collection = OwnedLockCollection::new(vec![mutex1, mutex2]);\n\n\t\tcollection.extend([Mutex::new(2)]);\n\n\t\tassert_eq!(collection.data.len(), 3);\n\t}\n\n\t#[test]\n\tfn works_in_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tOwnedLockCollection::new(OwnedLockCollection::new([RwLock::new(0), RwLock::new(1)]));\n\n\t\tlet mut guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], 0);\n\t\tassert_eq!(*guard[1], 1);\n\t\t*guard[1] = 2;\n\n\t\tlet key = OwnedLockCollection::\u003cOwnedLockCollection\u003c[RwLock\u003c_\u003e; 2]\u003e\u003e::unlock(guard);\n\t\tlet guard = collection.read(key);\n\t\tassert_eq!(*guard[0], 0);\n\t\tassert_eq!(*guard[1], 2);\n\t}\n\n\t#[test]\n\tfn as_mut_works() {\n\t\tlet mut mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet mut collection = OwnedLockCollection::new(\u0026mut mutexes);\n\n\t\tcollection.as_mut()[0] = Mutex::new(42);\n\n\t\tassert_eq!(*collection.as_mut()[0].get_mut(), 42);\n\t}\n\n\t#[test]\n\tfn child_mut_works() {\n\t\tlet mut mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet mut collection = OwnedLockCollection::new(\u0026mut mutexes);\n\n\t\tcollection.child_mut()[0] = Mutex::new(42);\n\n\t\tassert_eq!(*collection.child_mut()[0].get_mut(), 42);\n\t}\n\n\t#[test]\n\tfn into_child_works() {\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet mut collection = OwnedLockCollection::new(mutexes);\n\n\t\tcollection.child_mut()[0] = Mutex::new(42);\n\n\t\tassert_eq!(\n\t\t\t*collection\n\t\t\t\t.into_child()\n\t\t\t\t.as_mut()\n\t\t\t\t.get_mut(0)\n\t\t\t\t.unwrap()\n\t\t\t\t.get_mut(),\n\t\t\t42\n\t\t);\n\t}\n}\n","traces":[{"line":19,"address":[1579307,1579456,1579072,1579947,1579563,1579200,1580075,1579584,1579712,1579819,1579840,1579691,1579968,1579328,1579179,1579435],"length":1,"stats":{"Line":8}},{"line":20,"address":[],"length":0,"stats":{"Line":16}},{"line":23,"address":[],"length":0,"stats":{"Line":5}},{"line":24,"address":[],"length":0,"stats":{"Line":4}},{"line":25,"address":[],"length":0,"stats":{"Line":9}},{"line":28,"address":[1581061,1581088,1581333,1580816],"length":1,"stats":{"Line":1}},{"line":29,"address":[],"length":0,"stats":{"Line":1}},{"line":30,"address":[],"length":0,"stats":{"Line":3}},{"line":31,"address":[],"length":0,"stats":{"Line":1}},{"line":35,"address":[],"length":0,"stats":{"Line":4}},{"line":36,"address":[],"length":0,"stats":{"Line":8}},{"line":39,"address":[],"length":0,"stats":{"Line":2}},{"line":40,"address":[1581894,1582038],"length":1,"stats":{"Line":2}},{"line":41,"address":[1581904,1582048,1582103,1581959],"length":1,"stats":{"Line":4}},{"line":44,"address":[],"length":0,"stats":{"Line":1}},{"line":45,"address":[],"length":0,"stats":{"Line":1}},{"line":46,"address":[1582312,1582366,1582188],"length":1,"stats":{"Line":3}},{"line":47,"address":[1582392],"length":1,"stats":{"Line":1}},{"line":69,"address":[],"length":0,"stats":{"Line":1}},{"line":70,"address":[],"length":0,"stats":{"Line":1}},{"line":73,"address":[],"length":0,"stats":{"Line":1}},{"line":74,"address":[],"length":0,"stats":{"Line":1}},{"line":84,"address":[1582528,1582544],"length":1,"stats":{"Line":2}},{"line":85,"address":[1582533,1582561],"length":1,"stats":{"Line":2}},{"line":92,"address":[],"length":0,"stats":{"Line":2}},{"line":93,"address":[],"length":0,"stats":{"Line":2}},{"line":108,"address":[],"length":0,"stats":{"Line":1}},{"line":109,"address":[1582721],"length":1,"stats":{"Line":1}},{"line":112,"address":[1582736],"length":1,"stats":{"Line":1}},{"line":113,"address":[1582753],"length":1,"stats":{"Line":1}},{"line":126,"address":[],"length":0,"stats":{"Line":1}},{"line":127,"address":[1582780],"length":1,"stats":{"Line":1}},{"line":134,"address":[1582832],"length":1,"stats":{"Line":1}},{"line":135,"address":[],"length":0,"stats":{"Line":1}},{"line":136,"address":[],"length":0,"stats":{"Line":1}},{"line":141,"address":[],"length":0,"stats":{"Line":1}},{"line":142,"address":[],"length":0,"stats":{"Line":1}},{"line":151,"address":[],"length":0,"stats":{"Line":2}},{"line":152,"address":[],"length":0,"stats":{"Line":2}},{"line":157,"address":[],"length":0,"stats":{"Line":1}},{"line":158,"address":[],"length":0,"stats":{"Line":1}},{"line":163,"address":[],"length":0,"stats":{"Line":1}},{"line":164,"address":[],"length":0,"stats":{"Line":1}},{"line":185,"address":[1583424,1583392,1583312,1583536,1583072,1583216,1583264,1583104,1583280,1583456,1583488,1583136,1583184,1583360],"length":1,"stats":{"Line":14}},{"line":189,"address":[],"length":0,"stats":{"Line":2}},{"line":190,"address":[],"length":0,"stats":{"Line":2}},{"line":193,"address":[1583632],"length":1,"stats":{"Line":1}},{"line":198,"address":[],"length":0,"stats":{"Line":1}},{"line":221,"address":[1583792,1584192,1584336,1584452,1584164,1583888,1584016,1584586,1583920,1584608,1584048,1584480,1583760,1583664,1584724,1584308],"length":1,"stats":{"Line":8}},{"line":225,"address":[1583934,1584368,1584224,1584494,1584080,1583806,1583678,1584640],"length":1,"stats":{"Line":8}},{"line":228,"address":[1584685,1583846,1584125,1584534,1583718,1584269,1583974,1584413],"length":1,"stats":{"Line":8}},{"line":264,"address":[1585104,1585071,1584752,1585247,1584895,1584928],"length":1,"stats":{"Line":3}},{"line":266,"address":[1585118,1585161,1584985,1584809,1584942,1584766],"length":1,"stats":{"Line":6}},{"line":267,"address":[],"length":0,"stats":{"Line":1}},{"line":271,"address":[1585225,1584873,1585049,1584831,1585007,1585183],"length":1,"stats":{"Line":6}},{"line":274,"address":[],"length":0,"stats":{"Line":3}},{"line":296,"address":[1585280,1585440,1585376,1585346],"length":1,"stats":{"Line":2}},{"line":297,"address":[],"length":0,"stats":{"Line":2}},{"line":298,"address":[],"length":0,"stats":{"Line":0}},{"line":303,"address":[],"length":0,"stats":{"Line":1}},{"line":304,"address":[1585485],"length":1,"stats":{"Line":1}},{"line":307,"address":[1585504],"length":1,"stats":{"Line":1}},{"line":312,"address":[],"length":0,"stats":{"Line":1}},{"line":335,"address":[],"length":0,"stats":{"Line":4}},{"line":338,"address":[],"length":0,"stats":{"Line":4}},{"line":342,"address":[],"length":0,"stats":{"Line":4}},{"line":379,"address":[],"length":0,"stats":{"Line":2}},{"line":382,"address":[],"length":0,"stats":{"Line":4}},{"line":383,"address":[],"length":0,"stats":{"Line":1}},{"line":387,"address":[],"length":0,"stats":{"Line":1}},{"line":390,"address":[],"length":0,"stats":{"Line":1}},{"line":410,"address":[],"length":0,"stats":{"Line":1}},{"line":411,"address":[],"length":0,"stats":{"Line":1}},{"line":412,"address":[],"length":0,"stats":{"Line":0}},{"line":434,"address":[],"length":0,"stats":{"Line":1}},{"line":435,"address":[],"length":0,"stats":{"Line":1}},{"line":455,"address":[],"length":0,"stats":{"Line":2}},{"line":456,"address":[],"length":0,"stats":{"Line":0}},{"line":476,"address":[],"length":0,"stats":{"Line":2}},{"line":477,"address":[],"length":0,"stats":{"Line":2}},{"line":494,"address":[],"length":0,"stats":{"Line":2}},{"line":495,"address":[],"length":0,"stats":{"Line":2}}],"covered":79,"coverable":82},{"path":["/","home","botahamec","Projects","happylock","src","collection","ref.rs"],"content":"use std::fmt::Debug;\n\nuse crate::lockable::{Lockable, OwnedLockable, RawLock, Sharable};\nuse crate::{Keyable, ThreadKey};\n\nuse super::utils::{\n\tget_locks, ordered_contains_duplicates, scoped_read, scoped_try_read, scoped_try_write,\n\tscoped_write,\n};\nuse super::{utils, LockGuard, RefLockCollection};\n\nimpl\u003c'a, L\u003e IntoIterator for \u0026'a RefLockCollection\u003c'a, L\u003e\nwhere\n\t\u0026'a L: IntoIterator,\n{\n\ttype Item = \u003c\u0026'a L as IntoIterator\u003e::Item;\n\ttype IntoIter = \u003c\u0026'a L as IntoIterator\u003e::IntoIter;\n\n\tfn into_iter(self) -\u003e Self::IntoIter {\n\t\tself.data.into_iter()\n\t}\n}\n\nunsafe impl\u003cL: Lockable\u003e RawLock for RefLockCollection\u003c'_, L\u003e {\n\t#[mutants::skip] // this should never run\n\t#[cfg(not(tarpaulin_include))]\n\tfn poison(\u0026self) {\n\t\tfor lock in \u0026self.locks {\n\t\t\tlock.poison();\n\t\t}\n\t}\n\n\tunsafe fn raw_write(\u0026self) {\n\t\tutils::ordered_write(\u0026self.locks)\n\t}\n\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool {\n\t\tutils::ordered_try_write(\u0026self.locks)\n\t}\n\n\tunsafe fn raw_unlock_write(\u0026self) {\n\t\tfor lock in \u0026self.locks {\n\t\t\tlock.raw_unlock_write();\n\t\t}\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tutils::ordered_read(\u0026self.locks)\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tutils::ordered_try_read(\u0026self.locks)\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tfor lock in \u0026self.locks {\n\t\t\tlock.raw_unlock_read();\n\t\t}\n\t}\n}\n\nunsafe impl\u003cL: Lockable\u003e Lockable for RefLockCollection\u003c'_, L\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= L::Guard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= L::DataMut\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tptrs.push(self)\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tself.data.guard()\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.data.data_mut()\n\t}\n}\n\nunsafe impl\u003cL: Sharable\u003e Sharable for RefLockCollection\u003c'_, L\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= L::ReadGuard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= L::DataRef\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tself.data.read_guard()\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tself.data.data_ref()\n\t}\n}\n\nimpl\u003cT: ?Sized, L: AsRef\u003cT\u003e\u003e AsRef\u003cT\u003e for RefLockCollection\u003c'_, L\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself.data.as_ref()\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cL: Debug\u003e Debug for RefLockCollection\u003c'_, L\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tf.debug_struct(stringify!(RefLockCollection))\n\t\t\t.field(\"data\", self.data)\n\t\t\t.finish_non_exhaustive()\n\t}\n}\n\n// safety: the RawLocks must be send because they come from the Send Lockable\n#[allow(clippy::non_send_fields_in_send_ty)]\nunsafe impl\u003cL: Send\u003e Send for RefLockCollection\u003c'_, L\u003e {}\nunsafe impl\u003cL: Sync\u003e Sync for RefLockCollection\u003c'_, L\u003e {}\n\nimpl\u003c'a, L: OwnedLockable + Default\u003e From\u003c\u0026'a L\u003e for RefLockCollection\u003c'a, L\u003e {\n\tfn from(value: \u0026'a L) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\nimpl\u003c'a, L: OwnedLockable\u003e RefLockCollection\u003c'a, L\u003e {\n\t/// Creates a new collection of owned locks.\n\t///\n\t/// Because the locks are owned, there's no need to do any checks for\n\t/// duplicate values.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t/// ```\n\t#[must_use]\n\tpub fn new(data: \u0026'a L) -\u003e Self {\n\t\tRefLockCollection {\n\t\t\tlocks: get_locks(data),\n\t\t\tdata,\n\t\t}\n\t}\n}\n\nimpl\u003cL\u003e RefLockCollection\u003c'_, L\u003e {\n\t/// Gets an immutable reference to the underlying data\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let data1 = Mutex::new(42);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // data1 and data2 refer to distinct mutexes, so this won't panic\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = RefLockCollection::try_new(\u0026data).unwrap();\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let guard = lock.child().0.lock(key);\n\t/// assert_eq!(*guard, 42);\n\t/// ```\n\t#[must_use]\n\tpub const fn child(\u0026self) -\u003e \u0026L {\n\t\tself.data\n\t}\n}\n\nimpl\u003c'a, L: Lockable\u003e RefLockCollection\u003c'a, L\u003e {\n\t/// Creates a new collections of locks.\n\t///\n\t/// # Safety\n\t///\n\t/// This results in undefined behavior if any locks are presented twice\n\t/// within this collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let data1 = Mutex::new(0);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // safety: data1 and data2 refer to distinct mutexes\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = unsafe { RefLockCollection::new_unchecked(\u0026data) };\n\t/// ```\n\t#[must_use]\n\tpub unsafe fn new_unchecked(data: \u0026'a L) -\u003e Self {\n\t\tSelf {\n\t\t\tdata,\n\t\t\tlocks: get_locks(data),\n\t\t}\n\t}\n\n\t/// Creates a new collection of locks.\n\t///\n\t/// This returns `None` if any locks are found twice in the given\n\t/// collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let data1 = Mutex::new(0);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // data1 and data2 refer to distinct mutexes, so this won't panic\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = RefLockCollection::try_new(\u0026data).unwrap();\n\t/// ```\n\t#[must_use]\n\tpub fn try_new(data: \u0026'a L) -\u003e Option\u003cSelf\u003e {\n\t\tlet locks = get_locks(data);\n\t\tif ordered_contains_duplicates(\u0026locks) {\n\t\t\treturn None;\n\t\t}\n\n\t\tSome(Self { data, locks })\n\t}\n\n\tpub fn scoped_lock\u003c's, R\u003e(\u0026's self, key: impl Keyable, f: impl Fn(L::DataMut\u003c's\u003e) -\u003e R) -\u003e R {\n\t\tscoped_write(self, key, f)\n\t}\n\n\tpub fn scoped_try_lock\u003c's, Key: Keyable, R\u003e(\n\t\t\u0026's self,\n\t\tkey: Key,\n\t\tf: impl Fn(L::DataMut\u003c's\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tscoped_try_write(self, key, f)\n\t}\n\n\t/// Locks the collection\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data. When the guard is dropped, the locks in the collection are also\n\t/// dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// ```\n\t#[must_use]\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::Guard\u003c'_\u003e\u003e {\n\t\tlet guard = unsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_write();\n\n\t\t\t// safety: we've locked all of this already\n\t\t\tself.data.guard()\n\t\t};\n\n\t\tLockGuard { guard, key }\n\t}\n\n\t/// Attempts to lock the without blocking.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// locks when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If any of the locks in the collection are already locked, then an error\n\t/// is returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// match lock.try_lock(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::Guard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tlet guard = unsafe {\n\t\t\tif !self.raw_try_write() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we've acquired the locks\n\t\t\tself.data.guard()\n\t\t};\n\n\t\tOk(LockGuard { guard, key })\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// let key = RefLockCollection::\u003c(Mutex\u003ci32\u003e, Mutex\u003c\u0026str\u003e)\u003e::unlock(guard);\n\t/// ```\n\t#[allow(clippy::missing_const_for_fn)]\n\tpub fn unlock(guard: LockGuard\u003cL::Guard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: Sharable\u003e RefLockCollection\u003c'_, L\u003e {\n\tpub fn scoped_read\u003c'a, R\u003e(\u0026'a self, key: impl Keyable, f: impl Fn(L::DataRef\u003c'a\u003e) -\u003e R) -\u003e R {\n\t\tscoped_read(self, key, f)\n\t}\n\n\tpub fn scoped_try_read\u003c'a, Key: Keyable, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(L::DataRef\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tscoped_try_read(self, key, f)\n\t}\n\n\t/// Locks the collection, so that other threads can still read from it\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data immutably. When the guard is dropped, the locks in the collection\n\t/// are also dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// assert_eq!(*guard.0, 0);\n\t/// assert_eq!(*guard.1, \"\");\n\t/// ```\n\t#[must_use]\n\tpub fn read(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_read();\n\n\t\t\tLockGuard {\n\t\t\t\t// safety: we've already acquired the lock\n\t\t\t\tguard: self.data.read_guard(),\n\t\t\t\tkey,\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Attempts to lock the without blocking, in such a way that other threads\n\t/// can still read from the collection.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// shared access when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If any of the locks in the collection are already locked, then an error\n\t/// is returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(5), RwLock::new(\"6\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// match lock.try_read(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// assert_eq!(*guard.0, 5);\n\t/// assert_eq!(*guard.1, \"6\");\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_read(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tlet guard = unsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_read() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we've acquired the locks\n\t\t\tself.data.read_guard()\n\t\t};\n\n\t\tOk(LockGuard { guard, key })\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// let key = RefLockCollection::\u003c(RwLock\u003ci32\u003e, RwLock\u003c\u0026str\u003e)\u003e::unlock_read(guard);\n\t/// ```\n\t#[allow(clippy::missing_const_for_fn)]\n\tpub fn unlock_read(guard: LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003c'a, L: 'a\u003e RefLockCollection\u003c'a, L\u003e\nwhere\n\t\u0026'a L: IntoIterator,\n{\n\t/// Returns an iterator over references to each value in the collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = [Mutex::new(26), Mutex::new(1)];\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// let mut iter = lock.iter();\n\t/// let mutex = iter.next().unwrap();\n\t/// let guard = mutex.lock(key);\n\t///\n\t/// assert_eq!(*guard, 26);\n\t/// ```\n\t#[must_use]\n\tpub fn iter(\u0026'a self) -\u003e \u003c\u0026'a L as IntoIterator\u003e::IntoIter {\n\t\tself.into_iter()\n\t}\n}\n\n#[cfg(test)]\nmod tests {\n\tuse super::*;\n\tuse crate::{Mutex, RwLock, ThreadKey};\n\n\t#[test]\n\tfn non_duplicates_allowed() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(1);\n\t\tassert!(RefLockCollection::try_new(\u0026[\u0026mutex1, \u0026mutex2]).is_some())\n\t}\n\n\t#[test]\n\tfn duplicates_not_allowed() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tassert!(RefLockCollection::try_new(\u0026[\u0026mutex1, \u0026mutex1]).is_none())\n\t}\n\n\t#[test]\n\tfn from() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [Mutex::new(\"foo\"), Mutex::new(\"bar\"), Mutex::new(\"baz\")];\n\t\tlet collection = RefLockCollection::from(\u0026mutexes);\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], \"foo\");\n\t\tassert_eq!(*guard[1], \"bar\");\n\t\tassert_eq!(*guard[2], \"baz\");\n\t}\n\n\t#[test]\n\tfn scoped_lock_changes_collection() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [Mutex::new(24), Mutex::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\t\tlet sum = collection.scoped_lock(\u0026mut key, |guard| {\n\t\t\t*guard[0] = 128;\n\t\t\t*guard[0] + *guard[1]\n\t\t});\n\n\t\tassert_eq!(sum, 128 + 42);\n\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], 128);\n\t\tassert_eq!(*guard[1], 42);\n\t}\n\n\t#[test]\n\tfn scoped_read_sees_changes() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\t\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\t\t*guard[0] = 128;\n\t\t});\n\n\t\tlet sum = collection.scoped_read(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 128);\n\t\t\tassert_eq!(*guard[1], 42);\n\t\t\t*guard[0] + *guard[1]\n\t\t});\n\n\t\tassert_eq!(sum, 128 + 42);\n\t}\n\n\t#[test]\n\tfn scoped_try_lock_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = [Mutex::new(1), Mutex::new(2)];\n\t\tlet collection = RefLockCollection::new(\u0026locks);\n\t\tlet guard = collection.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = collection.scoped_try_lock(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn scoped_try_read_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = [RwLock::new(1), RwLock::new(2)];\n\t\tlet collection = RefLockCollection::new(\u0026locks);\n\t\tlet guard = collection.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = collection.scoped_try_read(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn try_lock_succeeds_for_unlocked_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [Mutex::new(24), Mutex::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\t\tlet guard = collection.try_lock(key).unwrap();\n\t\tassert_eq!(*guard[0], 24);\n\t\tassert_eq!(*guard[1], 42);\n\t}\n\n\t#[test]\n\tfn try_lock_fails_for_locked_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [Mutex::new(24), Mutex::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = mutexes[1].lock(key);\n\t\t\t\tassert_eq!(*guard, 42);\n\t\t\t\tstd::mem::forget(guard);\n\t\t\t});\n\t\t});\n\n\t\tlet guard = collection.try_lock(key);\n\t\tassert!(guard.is_err());\n\t}\n\n\t#[test]\n\tfn try_read_succeeds_for_unlocked_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\t\tlet guard = collection.try_read(key).unwrap();\n\t\tassert_eq!(*guard[0], 24);\n\t\tassert_eq!(*guard[1], 42);\n\t}\n\n\t#[test]\n\tfn try_read_fails_for_locked_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = mutexes[1].write(key);\n\t\t\t\tassert_eq!(*guard, 42);\n\t\t\t\tstd::mem::forget(guard);\n\t\t\t});\n\t\t});\n\n\t\tlet guard = collection.try_read(key);\n\t\tassert!(guard.is_err());\n\t}\n\n\t#[test]\n\tfn can_read_twice_on_different_threads() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.read(key);\n\t\t\t\tassert_eq!(*guard[0], 24);\n\t\t\t\tassert_eq!(*guard[1], 42);\n\t\t\t\tstd::mem::forget(guard);\n\t\t\t});\n\t\t});\n\n\t\tlet guard = collection.try_read(key).unwrap();\n\t\tassert_eq!(*guard[0], 24);\n\t\tassert_eq!(*guard[1], 42);\n\t}\n\n\t#[test]\n\tfn into_ref_iterator() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1), Mutex::new(2)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\t\tfor (i, mutex) in (\u0026collection).into_iter().enumerate() {\n\t\t\tmutex.scoped_lock(\u0026mut key, |val| assert_eq!(*val, i))\n\t\t}\n\t}\n\n\t#[test]\n\tfn ref_iterator() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1), Mutex::new(2)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\t\tfor (i, mutex) in collection.iter().enumerate() {\n\t\t\tmutex.scoped_lock(\u0026mut key, |val| assert_eq!(*val, i))\n\t\t}\n\t}\n\n\t#[test]\n\tfn works_in_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex1 = RwLock::new(0);\n\t\tlet mutex2 = RwLock::new(1);\n\t\tlet collection0 = [\u0026mutex1, \u0026mutex2];\n\t\tlet collection1 = RefLockCollection::try_new(\u0026collection0).unwrap();\n\t\tlet collection = RefLockCollection::try_new(\u0026collection1).unwrap();\n\n\t\tlet mut guard = collection.lock(key);\n\t\tassert!(mutex1.is_locked());\n\t\tassert!(mutex2.is_locked());\n\t\tassert_eq!(*guard[0], 0);\n\t\tassert_eq!(*guard[1], 1);\n\t\t*guard[1] = 2;\n\t\tdrop(guard);\n\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet guard = collection.read(key);\n\t\tassert!(mutex1.is_locked());\n\t\tassert!(mutex2.is_locked());\n\t\tassert_eq!(*guard[0], 0);\n\t\tassert_eq!(*guard[1], 2);\n\t}\n\n\t#[test]\n\tfn unlock_collection_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = (Mutex::new(\"foo\"), Mutex::new(\"bar\"));\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\t\tlet guard = collection.lock(key);\n\n\t\tlet key = RefLockCollection::\u003c(Mutex\u003c_\u003e, Mutex\u003c_\u003e)\u003e::unlock(guard);\n\t\tassert!(collection.try_lock(key).is_ok())\n\t}\n\n\t#[test]\n\tfn read_unlock_collection_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = (RwLock::new(\"foo\"), RwLock::new(\"bar\"));\n\t\tlet collection = RefLockCollection::new(\u0026locks);\n\t\tlet guard = collection.read(key);\n\n\t\tlet key = RefLockCollection::\u003c(\u0026RwLock\u003c_\u003e, \u0026RwLock\u003c_\u003e)\u003e::unlock_read(guard);\n\t\tassert!(collection.try_lock(key).is_ok())\n\t}\n\n\t#[test]\n\tfn as_ref_works() {\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\n\t\tassert!(std::ptr::addr_eq(\u0026mutexes, collection.as_ref()))\n\t}\n\n\t#[test]\n\tfn child() {\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\n\t\tassert!(std::ptr::addr_eq(\u0026mutexes, collection.child()))\n\t}\n}\n","traces":[{"line":19,"address":[1586672],"length":1,"stats":{"Line":1}},{"line":20,"address":[],"length":0,"stats":{"Line":1}},{"line":33,"address":[],"length":0,"stats":{"Line":6}},{"line":34,"address":[],"length":0,"stats":{"Line":6}},{"line":37,"address":[],"length":0,"stats":{"Line":3}},{"line":38,"address":[],"length":0,"stats":{"Line":3}},{"line":41,"address":[1587104,1587008,1587200],"length":1,"stats":{"Line":2}},{"line":42,"address":[],"length":0,"stats":{"Line":4}},{"line":43,"address":[],"length":0,"stats":{"Line":2}},{"line":47,"address":[],"length":0,"stats":{"Line":4}},{"line":48,"address":[1587301,1587333,1587397,1587365],"length":1,"stats":{"Line":4}},{"line":51,"address":[],"length":0,"stats":{"Line":1}},{"line":52,"address":[],"length":0,"stats":{"Line":1}},{"line":55,"address":[],"length":0,"stats":{"Line":1}},{"line":56,"address":[],"length":0,"stats":{"Line":2}},{"line":57,"address":[],"length":0,"stats":{"Line":1}},{"line":73,"address":[],"length":0,"stats":{"Line":1}},{"line":74,"address":[],"length":0,"stats":{"Line":1}},{"line":77,"address":[],"length":0,"stats":{"Line":1}},{"line":78,"address":[],"length":0,"stats":{"Line":1}},{"line":81,"address":[],"length":0,"stats":{"Line":2}},{"line":82,"address":[],"length":0,"stats":{"Line":2}},{"line":97,"address":[],"length":0,"stats":{"Line":1}},{"line":98,"address":[],"length":0,"stats":{"Line":1}},{"line":101,"address":[],"length":0,"stats":{"Line":1}},{"line":102,"address":[],"length":0,"stats":{"Line":1}},{"line":107,"address":[],"length":0,"stats":{"Line":1}},{"line":108,"address":[],"length":0,"stats":{"Line":1}},{"line":128,"address":[],"length":0,"stats":{"Line":1}},{"line":129,"address":[],"length":0,"stats":{"Line":1}},{"line":149,"address":[],"length":0,"stats":{"Line":6}},{"line":151,"address":[],"length":0,"stats":{"Line":6}},{"line":178,"address":[],"length":0,"stats":{"Line":1}},{"line":179,"address":[],"length":0,"stats":{"Line":1}},{"line":205,"address":[],"length":0,"stats":{"Line":0}},{"line":208,"address":[],"length":0,"stats":{"Line":0}},{"line":231,"address":[],"length":0,"stats":{"Line":3}},{"line":232,"address":[],"length":0,"stats":{"Line":3}},{"line":233,"address":[],"length":0,"stats":{"Line":6}},{"line":234,"address":[],"length":0,"stats":{"Line":1}},{"line":237,"address":[],"length":0,"stats":{"Line":3}},{"line":240,"address":[],"length":0,"stats":{"Line":2}},{"line":241,"address":[],"length":0,"stats":{"Line":2}},{"line":244,"address":[1589696],"length":1,"stats":{"Line":1}},{"line":249,"address":[],"length":0,"stats":{"Line":1}},{"line":273,"address":[],"length":0,"stats":{"Line":5}},{"line":276,"address":[],"length":0,"stats":{"Line":5}},{"line":279,"address":[],"length":0,"stats":{"Line":5}},{"line":315,"address":[],"length":0,"stats":{"Line":3}},{"line":317,"address":[],"length":0,"stats":{"Line":6}},{"line":318,"address":[],"length":0,"stats":{"Line":1}},{"line":322,"address":[],"length":0,"stats":{"Line":5}},{"line":325,"address":[],"length":0,"stats":{"Line":3}},{"line":347,"address":[],"length":0,"stats":{"Line":1}},{"line":348,"address":[1591006],"length":1,"stats":{"Line":1}},{"line":349,"address":[],"length":0,"stats":{"Line":0}},{"line":354,"address":[],"length":0,"stats":{"Line":1}},{"line":355,"address":[],"length":0,"stats":{"Line":1}},{"line":358,"address":[1591120],"length":1,"stats":{"Line":1}},{"line":363,"address":[1591129],"length":1,"stats":{"Line":1}},{"line":387,"address":[],"length":0,"stats":{"Line":3}},{"line":390,"address":[],"length":0,"stats":{"Line":3}},{"line":394,"address":[],"length":0,"stats":{"Line":3}},{"line":431,"address":[1591552,1591722],"length":1,"stats":{"Line":1}},{"line":434,"address":[],"length":0,"stats":{"Line":2}},{"line":435,"address":[1591638],"length":1,"stats":{"Line":1}},{"line":439,"address":[],"length":0,"stats":{"Line":1}},{"line":442,"address":[1591683],"length":1,"stats":{"Line":1}},{"line":462,"address":[],"length":0,"stats":{"Line":1}},{"line":463,"address":[],"length":0,"stats":{"Line":1}},{"line":464,"address":[],"length":0,"stats":{"Line":0}},{"line":491,"address":[],"length":0,"stats":{"Line":1}},{"line":492,"address":[],"length":0,"stats":{"Line":1}}],"covered":69,"coverable":73},{"path":["/","home","botahamec","Projects","happylock","src","collection","retry.rs"],"content":"use std::cell::Cell;\nuse std::collections::HashSet;\n\nuse crate::collection::utils;\nuse crate::handle_unwind::handle_unwind;\nuse crate::lockable::{\n\tLockable, LockableGetMut, LockableIntoInner, OwnedLockable, RawLock, Sharable,\n};\nuse crate::{Keyable, ThreadKey};\n\nuse super::utils::{\n\tattempt_to_recover_reads_from_panic, attempt_to_recover_writes_from_panic, get_locks_unsorted,\n\tscoped_read, scoped_try_read, scoped_try_write, scoped_write,\n};\nuse super::{LockGuard, RetryingLockCollection};\n\n/// Checks that a collection contains no duplicate references to a lock.\nfn contains_duplicates\u003cL: Lockable\u003e(data: L) -\u003e bool {\n\tlet mut locks = Vec::new();\n\tdata.get_ptrs(\u0026mut locks);\n\t// cast to *const () so that the v-table pointers are not used for hashing\n\tlet locks = locks.into_iter().map(|l| (\u0026raw const *l).cast::\u003c()\u003e());\n\n\tlet mut locks_set = HashSet::with_capacity(locks.len());\n\tfor lock in locks {\n\t\tif !locks_set.insert(lock) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tfalse\n}\n\nunsafe impl\u003cL: Lockable\u003e RawLock for RetryingLockCollection\u003cL\u003e {\n\t#[mutants::skip] // this should never run\n\t#[cfg(not(tarpaulin_include))]\n\tfn poison(\u0026self) {\n\t\tlet locks = get_locks_unsorted(\u0026self.data);\n\t\tfor lock in locks {\n\t\t\tlock.poison();\n\t\t}\n\t}\n\n\tunsafe fn raw_write(\u0026self) {\n\t\tlet locks = get_locks_unsorted(\u0026self.data);\n\n\t\tif locks.is_empty() {\n\t\t\t// this probably prevents a panic later\n\t\t\treturn;\n\t\t}\n\n\t\t// these will be unlocked in case of a panic\n\t\tlet first_index = Cell::new(0);\n\t\tlet locked = Cell::new(0);\n\t\thandle_unwind(\n\t\t\t|| unsafe {\n\t\t\t\t'outer: loop {\n\t\t\t\t\t// This prevents us from entering a spin loop waiting for\n\t\t\t\t\t// the same lock to be unlocked\n\t\t\t\t\t// safety: we have the thread key\n\t\t\t\t\tlocks[first_index.get()].raw_write();\n\t\t\t\t\tfor (i, lock) in locks.iter().enumerate() {\n\t\t\t\t\t\tif i == first_index.get() {\n\t\t\t\t\t\t\t// we've already locked this one\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// If the lock has been killed, then this returns false\n\t\t\t\t\t\t// instead of panicking. This sounds like a problem, but if\n\t\t\t\t\t\t// it does return false, then the lock function is called\n\t\t\t\t\t\t// immediately after, causing a panic\n\t\t\t\t\t\t// safety: we have the thread key\n\t\t\t\t\t\tif lock.raw_try_write() {\n\t\t\t\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// safety: we already locked all of these\n\t\t\t\t\t\t\tattempt_to_recover_writes_from_panic(\u0026locks[0..i]);\n\t\t\t\t\t\t\tif first_index.get() \u003e= i {\n\t\t\t\t\t\t\t\t// safety: this is already locked and can't be\n\t\t\t\t\t\t\t\t// unlocked by the previous loop\n\t\t\t\t\t\t\t\tlocks[first_index.get()].raw_unlock_write();\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// nothing is locked anymore\n\t\t\t\t\t\t\tlocked.set(0);\n\n\t\t\t\t\t\t\t// call lock on this to prevent a spin loop\n\t\t\t\t\t\t\tfirst_index.set(i);\n\t\t\t\t\t\t\tcontinue 'outer;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// safety: we locked all the data\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t},\n\t\t\t|| {\n\t\t\t\tutils::attempt_to_recover_writes_from_panic(\u0026locks[0..locked.get()]);\n\t\t\t\tif first_index.get() \u003e= locked.get() {\n\t\t\t\t\tlocks[first_index.get()].raw_unlock_write();\n\t\t\t\t}\n\t\t\t},\n\t\t)\n\t}\n\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool {\n\t\tlet locks = get_locks_unsorted(\u0026self.data);\n\n\t\tif locks.is_empty() {\n\t\t\t// this is an interesting case, but it doesn't give us access to\n\t\t\t// any data, and can't possibly cause a deadlock\n\t\t\treturn true;\n\t\t}\n\n\t\t// these will be unlocked in case of a panic\n\t\tlet locked = Cell::new(0);\n\t\thandle_unwind(\n\t\t\t|| unsafe {\n\t\t\t\tfor (i, lock) in locks.iter().enumerate() {\n\t\t\t\t\t// safety: we have the thread key\n\t\t\t\t\tif lock.raw_try_write() {\n\t\t\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// safety: we already locked all of these\n\t\t\t\t\t\tattempt_to_recover_writes_from_panic(\u0026locks[0..i]);\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ttrue\n\t\t\t},\n\t\t\t|| utils::attempt_to_recover_writes_from_panic(\u0026locks[0..locked.get()]),\n\t\t)\n\t}\n\n\tunsafe fn raw_unlock_write(\u0026self) {\n\t\tlet locks = get_locks_unsorted(\u0026self.data);\n\n\t\tfor lock in locks {\n\t\t\tlock.raw_unlock_write();\n\t\t}\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tlet locks = get_locks_unsorted(\u0026self.data);\n\n\t\tif locks.is_empty() {\n\t\t\t// this probably prevents a panic later\n\t\t\treturn;\n\t\t}\n\n\t\tlet locked = Cell::new(0);\n\t\tlet first_index = Cell::new(0);\n\t\thandle_unwind(\n\t\t\t|| 'outer: loop {\n\t\t\t\t// safety: we have the thread key\n\t\t\t\tlocks[first_index.get()].raw_read();\n\t\t\t\tfor (i, lock) in locks.iter().enumerate() {\n\t\t\t\t\tif i == first_index.get() {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// safety: we have the thread key\n\t\t\t\t\tif lock.raw_try_read() {\n\t\t\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// safety: we already locked all of these\n\t\t\t\t\t\tattempt_to_recover_reads_from_panic(\u0026locks[0..i]);\n\n\t\t\t\t\t\tif first_index.get() \u003e= i {\n\t\t\t\t\t\t\t// safety: this is already locked and can't be unlocked\n\t\t\t\t\t\t\t// by the previous loop\n\t\t\t\t\t\t\tlocks[first_index.get()].raw_unlock_read();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// these are no longer locked\n\t\t\t\t\t\tlocked.set(0);\n\n\t\t\t\t\t\t// don't go into a spin loop, wait for this one to lock\n\t\t\t\t\t\tfirst_index.set(i);\n\t\t\t\t\t\tcontinue 'outer;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// safety: we locked all the data\n\t\t\t\tbreak;\n\t\t\t},\n\t\t\t|| {\n\t\t\t\tutils::attempt_to_recover_reads_from_panic(\u0026locks[0..locked.get()]);\n\t\t\t\tif first_index.get() \u003e= locked.get() {\n\t\t\t\t\tlocks[first_index.get()].raw_unlock_read();\n\t\t\t\t}\n\t\t\t},\n\t\t)\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tlet locks = get_locks_unsorted(\u0026self.data);\n\n\t\tif locks.is_empty() {\n\t\t\t// this is an interesting case, but it doesn't give us access to\n\t\t\t// any data, and can't possibly cause a deadlock\n\t\t\treturn true;\n\t\t}\n\n\t\tlet locked = Cell::new(0);\n\t\thandle_unwind(\n\t\t\t|| unsafe {\n\t\t\t\tfor (i, lock) in locks.iter().enumerate() {\n\t\t\t\t\t// safety: we have the thread key\n\t\t\t\t\tif lock.raw_try_read() {\n\t\t\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// safety: we already locked all of these\n\t\t\t\t\t\tattempt_to_recover_reads_from_panic(\u0026locks[0..i]);\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ttrue\n\t\t\t},\n\t\t\t|| utils::attempt_to_recover_reads_from_panic(\u0026locks[0..locked.get()]),\n\t\t)\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tlet locks = get_locks_unsorted(\u0026self.data);\n\n\t\tfor lock in locks {\n\t\t\tlock.raw_unlock_read();\n\t\t}\n\t}\n}\n\nunsafe impl\u003cL: Lockable\u003e Lockable for RetryingLockCollection\u003cL\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= L::Guard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= L::DataMut\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tptrs.push(self)\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tself.data.guard()\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.data.data_mut()\n\t}\n}\n\nunsafe impl\u003cL: Sharable\u003e Sharable for RetryingLockCollection\u003cL\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= L::ReadGuard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= L::DataRef\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tself.data.read_guard()\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tself.data.data_ref()\n\t}\n}\n\nunsafe impl\u003cL: OwnedLockable\u003e OwnedLockable for RetryingLockCollection\u003cL\u003e {}\n\nimpl\u003cL: LockableGetMut\u003e LockableGetMut for RetryingLockCollection\u003cL\u003e {\n\ttype Inner\u003c'a\u003e\n\t\t= L::Inner\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tself.data.get_mut()\n\t}\n}\n\nimpl\u003cL: LockableIntoInner\u003e LockableIntoInner for RetryingLockCollection\u003cL\u003e {\n\ttype Inner = L::Inner;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tself.data.into_inner()\n\t}\n}\n\nimpl\u003cL\u003e IntoIterator for RetryingLockCollection\u003cL\u003e\nwhere\n\tL: IntoIterator,\n{\n\ttype Item = \u003cL as IntoIterator\u003e::Item;\n\ttype IntoIter = \u003cL as IntoIterator\u003e::IntoIter;\n\n\tfn into_iter(self) -\u003e Self::IntoIter {\n\t\tself.data.into_iter()\n\t}\n}\n\nimpl\u003c'a, L\u003e IntoIterator for \u0026'a RetryingLockCollection\u003cL\u003e\nwhere\n\t\u0026'a L: IntoIterator,\n{\n\ttype Item = \u003c\u0026'a L as IntoIterator\u003e::Item;\n\ttype IntoIter = \u003c\u0026'a L as IntoIterator\u003e::IntoIter;\n\n\tfn into_iter(self) -\u003e Self::IntoIter {\n\t\tself.data.into_iter()\n\t}\n}\n\nimpl\u003c'a, L\u003e IntoIterator for \u0026'a mut RetryingLockCollection\u003cL\u003e\nwhere\n\t\u0026'a mut L: IntoIterator,\n{\n\ttype Item = \u003c\u0026'a mut L as IntoIterator\u003e::Item;\n\ttype IntoIter = \u003c\u0026'a mut L as IntoIterator\u003e::IntoIter;\n\n\tfn into_iter(self) -\u003e Self::IntoIter {\n\t\tself.data.into_iter()\n\t}\n}\n\nimpl\u003cL: OwnedLockable, I: FromIterator\u003cL\u003e + OwnedLockable\u003e FromIterator\u003cL\u003e\n\tfor RetryingLockCollection\u003cI\u003e\n{\n\tfn from_iter\u003cT: IntoIterator\u003cItem = L\u003e\u003e(iter: T) -\u003e Self {\n\t\tlet iter: I = iter.into_iter().collect();\n\t\tSelf::new(iter)\n\t}\n}\n\nimpl\u003cE: OwnedLockable + Extend\u003cL\u003e, L: OwnedLockable\u003e Extend\u003cL\u003e for RetryingLockCollection\u003cE\u003e {\n\tfn extend\u003cT: IntoIterator\u003cItem = L\u003e\u003e(\u0026mut self, iter: T) {\n\t\tself.data.extend(iter)\n\t}\n}\n\nimpl\u003cT: ?Sized, L: AsRef\u003cT\u003e\u003e AsRef\u003cT\u003e for RetryingLockCollection\u003cL\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself.data.as_ref()\n\t}\n}\n\nimpl\u003cT: ?Sized, L: AsMut\u003cT\u003e\u003e AsMut\u003cT\u003e for RetryingLockCollection\u003cL\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself.data.as_mut()\n\t}\n}\n\nimpl\u003cL: OwnedLockable + Default\u003e Default for RetryingLockCollection\u003cL\u003e {\n\tfn default() -\u003e Self {\n\t\tSelf::new(L::default())\n\t}\n}\n\nimpl\u003cL: OwnedLockable\u003e From\u003cL\u003e for RetryingLockCollection\u003cL\u003e {\n\tfn from(value: L) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\nimpl\u003cL: OwnedLockable\u003e RetryingLockCollection\u003cL\u003e {\n\t/// Creates a new collection of owned locks.\n\t///\n\t/// Because the locks are owned, there's no need to do any checks for\n\t/// duplicate values. The locks also don't need to be sorted by memory\n\t/// address because they aren't used anywhere else.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t/// ```\n\t#[must_use]\n\tpub const fn new(data: L) -\u003e Self {\n\t\t// safety: the data cannot cannot contain references\n\t\tunsafe { Self::new_unchecked(data) }\n\t}\n}\n\nimpl\u003c'a, L: OwnedLockable\u003e RetryingLockCollection\u003c\u0026'a L\u003e {\n\t/// Creates a new collection of owned locks.\n\t///\n\t/// Because the locks are owned, there's no need to do any checks for\n\t/// duplicate values.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new_ref(\u0026data);\n\t/// ```\n\t#[must_use]\n\tpub const fn new_ref(data: \u0026'a L) -\u003e Self {\n\t\t// safety: the data cannot cannot contain references\n\t\tunsafe { Self::new_unchecked(data) }\n\t}\n}\n\nimpl\u003cL\u003e RetryingLockCollection\u003cL\u003e {\n\t/// Creates a new collections of locks.\n\t///\n\t/// # Safety\n\t///\n\t/// This results in undefined behavior if any locks are presented twice\n\t/// within this collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let data1 = Mutex::new(0);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // safety: data1 and data2 refer to distinct mutexes\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = unsafe { RetryingLockCollection::new_unchecked(\u0026data) };\n\t/// ```\n\t#[must_use]\n\tpub const unsafe fn new_unchecked(data: L) -\u003e Self {\n\t\tSelf { data }\n\t}\n\n\t/// Gets an immutable reference to the underlying collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let data = (Mutex::new(42), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let inner = lock.child();\n\t/// let guard = inner.0.lock(key);\n\t/// assert_eq!(*guard, 42);\n\t/// ```\n\t#[must_use]\n\tpub const fn child(\u0026self) -\u003e \u0026L {\n\t\t\u0026self.data\n\t}\n\n\t/// Gets a mutable reference to the underlying collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let data = (Mutex::new(42), Mutex::new(\"\"));\n\t/// let mut lock = RetryingLockCollection::new(data);\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut inner = lock.child_mut();\n\t/// let guard = inner.0.get_mut();\n\t/// assert_eq!(*guard, 42);\n\t/// ```\n\t#[must_use]\n\tpub fn child_mut(\u0026mut self) -\u003e \u0026mut L {\n\t\t\u0026mut self.data\n\t}\n\n\t/// Gets the underlying collection, consuming this collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let data = (Mutex::new(42), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let inner = lock.into_child();\n\t/// let guard = inner.0.lock(key);\n\t/// assert_eq!(*guard, 42);\n\t/// ```\n\t#[must_use]\n\tpub fn into_child(self) -\u003e L {\n\t\tself.data\n\t}\n}\n\nimpl\u003cL: Lockable\u003e RetryingLockCollection\u003cL\u003e {\n\t/// Creates a new collection of locks.\n\t///\n\t/// This returns `None` if any locks are found twice in the given\n\t/// collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let data1 = Mutex::new(0);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // data1 and data2 refer to distinct mutexes, so this won't panic\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = RetryingLockCollection::try_new(\u0026data).unwrap();\n\t/// ```\n\t#[must_use]\n\tpub fn try_new(data: L) -\u003e Option\u003cSelf\u003e {\n\t\t// safety: the data is checked for duplicates before returning the collection\n\t\t(!contains_duplicates(\u0026data)).then_some(unsafe { Self::new_unchecked(data) })\n\t}\n\n\tpub fn scoped_lock\u003c'a, R\u003e(\u0026'a self, key: impl Keyable, f: impl Fn(L::DataMut\u003c'a\u003e) -\u003e R) -\u003e R {\n\t\tscoped_write(self, key, f)\n\t}\n\n\tpub fn scoped_try_lock\u003c'a, Key: Keyable, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(L::DataMut\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tscoped_try_write(self, key, f)\n\t}\n\n\t/// Locks the collection\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data. When the guard is dropped, the locks in the collection are also\n\t/// dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// ```\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::Guard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\t// safety: we're taking the thread key\n\t\t\tself.raw_write();\n\n\t\t\tLockGuard {\n\t\t\t\t// safety: we just locked the collection\n\t\t\t\tguard: self.guard(),\n\t\t\t\tkey,\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Attempts to lock the without blocking.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// locks when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If any of the locks in the collection are already locked, then an error\n\t/// is returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// match lock.try_lock(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::Guard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tunsafe {\n\t\t\t// safety: we're taking the thread key\n\t\t\tif self.raw_try_write() {\n\t\t\t\tOk(LockGuard {\n\t\t\t\t\t// safety: we just succeeded in locking everything\n\t\t\t\t\tguard: self.guard(),\n\t\t\t\t\tkey,\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\tErr(key)\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// let key = RetryingLockCollection::\u003c(Mutex\u003ci32\u003e, Mutex\u003c\u0026str\u003e)\u003e::unlock(guard);\n\t/// ```\n\tpub fn unlock(guard: LockGuard\u003cL::Guard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: Sharable\u003e RetryingLockCollection\u003cL\u003e {\n\tpub fn scoped_read\u003c'a, R\u003e(\u0026'a self, key: impl Keyable, f: impl Fn(L::DataRef\u003c'a\u003e) -\u003e R) -\u003e R {\n\t\tscoped_read(self, key, f)\n\t}\n\n\tpub fn scoped_try_read\u003c'a, Key: Keyable, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(L::DataRef\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tscoped_try_read(self, key, f)\n\t}\n\n\t/// Locks the collection, so that other threads can still read from it\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data immutably. When the guard is dropped, the locks in the collection\n\t/// are also dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// assert_eq!(*guard.0, 0);\n\t/// assert_eq!(*guard.1, \"\");\n\t/// ```\n\tpub fn read(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\t// safety: we're taking the thread key\n\t\t\tself.raw_read();\n\n\t\t\tLockGuard {\n\t\t\t\t// safety: we just locked the collection\n\t\t\t\tguard: self.read_guard(),\n\t\t\t\tkey,\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Attempts to lock the without blocking, in such a way that other threads\n\t/// can still read from the collection.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// shared access when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If shared access cannot be acquired at this time, then an error is\n\t/// returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(5), RwLock::new(\"6\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// match lock.try_read(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// assert_eq!(*guard.0, 5);\n\t/// assert_eq!(*guard.1, \"6\");\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_read(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tunsafe {\n\t\t\t// safety: we're taking the thread key\n\t\t\tif !self.raw_try_read() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\tOk(LockGuard {\n\t\t\t\t// safety: we just succeeded in locking everything\n\t\t\t\tguard: self.read_guard(),\n\t\t\t\tkey,\n\t\t\t})\n\t\t}\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// let key = RetryingLockCollection::\u003c(RwLock\u003ci32\u003e, RwLock\u003c\u0026str\u003e)\u003e::unlock_read(guard);\n\t/// ```\n\tpub fn unlock_read(guard: LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: LockableGetMut\u003e RetryingLockCollection\u003cL\u003e {\n\t/// Gets a mutable reference to the data behind this\n\t/// `RetryingLockCollection`.\n\t///\n\t/// Since this call borrows the `RetryingLockCollection` mutably, no actual\n\t/// locking needs to take place - the mutable borrow statically guarantees\n\t/// no locks exist.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let mut mutex = RetryingLockCollection::new([Mutex::new(0), Mutex::new(0)]);\n\t/// assert_eq!(mutex.get_mut(), [\u0026mut 0, \u0026mut 0]);\n\t/// ```\n\tpub fn get_mut(\u0026mut self) -\u003e L::Inner\u003c'_\u003e {\n\t\tLockableGetMut::get_mut(self)\n\t}\n}\n\nimpl\u003cL: LockableIntoInner\u003e RetryingLockCollection\u003cL\u003e {\n\t/// Consumes this `RetryingLockCollection`, returning the underlying data.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let mutex = RetryingLockCollection::new([Mutex::new(0), Mutex::new(0)]);\n\t/// assert_eq!(mutex.into_inner(), [0, 0]);\n\t/// ```\n\tpub fn into_inner(self) -\u003e L::Inner {\n\t\tLockableIntoInner::into_inner(self)\n\t}\n}\n\nimpl\u003c'a, L: 'a\u003e RetryingLockCollection\u003cL\u003e\nwhere\n\t\u0026'a L: IntoIterator,\n{\n\t/// Returns an iterator over references to each value in the collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = [Mutex::new(26), Mutex::new(1)];\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let mut iter = lock.iter();\n\t/// let mutex = iter.next().unwrap();\n\t/// let guard = mutex.lock(key);\n\t///\n\t/// assert_eq!(*guard, 26);\n\t/// ```\n\t#[must_use]\n\tpub fn iter(\u0026'a self) -\u003e \u003c\u0026'a L as IntoIterator\u003e::IntoIter {\n\t\tself.into_iter()\n\t}\n}\n\nimpl\u003c'a, L: 'a\u003e RetryingLockCollection\u003cL\u003e\nwhere\n\t\u0026'a mut L: IntoIterator,\n{\n\t/// Returns an iterator over mutable references to each value in the\n\t/// collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = [Mutex::new(26), Mutex::new(1)];\n\t/// let mut lock = RetryingLockCollection::new(data);\n\t///\n\t/// let mut iter = lock.iter_mut();\n\t/// let mutex = iter.next().unwrap();\n\t///\n\t/// assert_eq!(*mutex.as_mut(), 26);\n\t/// ```\n\t#[must_use]\n\tpub fn iter_mut(\u0026'a mut self) -\u003e \u003c\u0026'a mut L as IntoIterator\u003e::IntoIter {\n\t\tself.into_iter()\n\t}\n}\n\n#[cfg(test)]\nmod tests {\n\tuse super::*;\n\tuse crate::collection::BoxedLockCollection;\n\tuse crate::{Mutex, RwLock, ThreadKey};\n\n\t#[test]\n\tfn nonduplicate_lock_references_are_allowed() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(0);\n\t\tassert!(RetryingLockCollection::try_new([\u0026mutex1, \u0026mutex2]).is_some());\n\t}\n\n\t#[test]\n\tfn duplicate_lock_references_are_disallowed() {\n\t\tlet mutex = Mutex::new(0);\n\t\tassert!(RetryingLockCollection::try_new([\u0026mutex, \u0026mutex]).is_none());\n\t}\n\n\t#[test]\n\t#[allow(clippy::float_cmp)]\n\tfn uses_correct_default() {\n\t\tlet collection =\n\t\t\tRetryingLockCollection::\u003c(RwLock\u003cf64\u003e, Mutex\u003cOption\u003ci32\u003e\u003e, Mutex\u003cusize\u003e)\u003e::default();\n\t\tlet tuple = collection.into_inner();\n\t\tassert_eq!(tuple.0, 0.0);\n\t\tassert!(tuple.1.is_none());\n\t\tassert_eq!(tuple.2, 0)\n\t}\n\n\t#[test]\n\tfn from() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tRetryingLockCollection::from([Mutex::new(\"foo\"), Mutex::new(\"bar\"), Mutex::new(\"baz\")]);\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], \"foo\");\n\t\tassert_eq!(*guard[1], \"bar\");\n\t\tassert_eq!(*guard[2], \"baz\");\n\t}\n\n\t#[test]\n\tfn new_ref_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet collection = RetryingLockCollection::new_ref(\u0026mutexes);\n\t\tcollection.scoped_lock(key, |guard| {\n\t\t\tassert_eq!(*guard[0], 0);\n\t\t\tassert_eq!(*guard[1], 1);\n\t\t})\n\t}\n\n\t#[test]\n\tfn scoped_read_sees_changes() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = RetryingLockCollection::new(mutexes);\n\t\tcollection.scoped_lock(\u0026mut key, |guard| *guard[0] = 128);\n\n\t\tlet sum = collection.scoped_read(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 128);\n\t\t\tassert_eq!(*guard[1], 42);\n\t\t\t*guard[0] + *guard[1]\n\t\t});\n\n\t\tassert_eq!(sum, 128 + 42);\n\t}\n\n\t#[test]\n\tfn get_mut_affects_scoped_read() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet mut collection = RetryingLockCollection::new(mutexes);\n\t\tlet guard = collection.get_mut();\n\t\t*guard[0] = 128;\n\n\t\tlet sum = collection.scoped_read(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 128);\n\t\t\tassert_eq!(*guard[1], 42);\n\t\t\t*guard[0] + *guard[1]\n\t\t});\n\n\t\tassert_eq!(sum, 128 + 42);\n\t}\n\n\t#[test]\n\tfn scoped_try_lock_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = RetryingLockCollection::new([Mutex::new(1), Mutex::new(2)]);\n\t\tlet guard = collection.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = collection.scoped_try_lock(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn scoped_try_read_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = RetryingLockCollection::new([RwLock::new(1), RwLock::new(2)]);\n\t\tlet guard = collection.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = collection.scoped_try_read(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn try_lock_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = RetryingLockCollection::new([Mutex::new(1), Mutex::new(2)]);\n\t\tlet guard = collection.try_lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_lock(key);\n\t\t\t\tassert!(guard.is_err());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn try_read_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = RetryingLockCollection::new([RwLock::new(1), RwLock::new(2)]);\n\t\tlet guard = collection.try_read(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_read(key);\n\t\t\t\tassert!(guard.is_ok());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn try_read_fails_for_locked_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = RetryingLockCollection::new_ref(\u0026mutexes);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = mutexes[1].write(key);\n\t\t\t\tassert_eq!(*guard, 42);\n\t\t\t\tstd::mem::forget(guard);\n\t\t\t});\n\t\t});\n\n\t\tlet guard = collection.try_read(key);\n\t\tassert!(guard.is_err());\n\t}\n\n\t#[test]\n\tfn locks_all_inner_mutexes() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(0);\n\t\tlet collection = RetryingLockCollection::try_new([\u0026mutex1, \u0026mutex2]).unwrap();\n\n\t\tlet guard = collection.lock(key);\n\n\t\tassert!(mutex1.is_locked());\n\t\tassert!(mutex2.is_locked());\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn locks_all_inner_rwlocks() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet rwlock1 = RwLock::new(0);\n\t\tlet rwlock2 = RwLock::new(0);\n\t\tlet collection = RetryingLockCollection::try_new([\u0026rwlock1, \u0026rwlock2]).unwrap();\n\n\t\tlet guard = collection.read(key);\n\n\t\tassert!(rwlock1.is_locked());\n\t\tassert!(rwlock2.is_locked());\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn works_with_other_collections() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(0);\n\t\tlet collection = BoxedLockCollection::try_new(\n\t\t\tRetryingLockCollection::try_new([\u0026mutex1, \u0026mutex2]).unwrap(),\n\t\t)\n\t\t.unwrap();\n\n\t\tlet guard = collection.lock(key);\n\n\t\tassert!(mutex1.is_locked());\n\t\tassert!(mutex2.is_locked());\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn from_iterator() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection: RetryingLockCollection\u003cVec\u003cMutex\u003c\u0026str\u003e\u003e\u003e =\n\t\t\t[Mutex::new(\"foo\"), Mutex::new(\"bar\"), Mutex::new(\"baz\")]\n\t\t\t\t.into_iter()\n\t\t\t\t.collect();\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], \"foo\");\n\t\tassert_eq!(*guard[1], \"bar\");\n\t\tassert_eq!(*guard[2], \"baz\");\n\t}\n\n\t#[test]\n\tfn into_owned_iterator() {\n\t\tlet collection = RetryingLockCollection::new([Mutex::new(0), Mutex::new(1), Mutex::new(2)]);\n\t\tfor (i, mutex) in collection.into_iter().enumerate() {\n\t\t\tassert_eq!(mutex.into_inner(), i);\n\t\t}\n\t}\n\n\t#[test]\n\tfn into_ref_iterator() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet collection = RetryingLockCollection::new([Mutex::new(0), Mutex::new(1), Mutex::new(2)]);\n\t\tfor (i, mutex) in (\u0026collection).into_iter().enumerate() {\n\t\t\tmutex.scoped_lock(\u0026mut key, |val| assert_eq!(*val, i))\n\t\t}\n\t}\n\n\t#[test]\n\tfn ref_iterator() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet collection = RetryingLockCollection::new([Mutex::new(0), Mutex::new(1), Mutex::new(2)]);\n\t\tfor (i, mutex) in collection.iter().enumerate() {\n\t\t\tmutex.scoped_lock(\u0026mut key, |val| assert_eq!(*val, i))\n\t\t}\n\t}\n\n\t#[test]\n\tfn mut_iterator() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mut collection =\n\t\t\tRetryingLockCollection::new([Mutex::new(0), Mutex::new(1), Mutex::new(2)]);\n\t\tfor (i, mutex) in collection.iter_mut().enumerate() {\n\t\t\tmutex.scoped_lock(\u0026mut key, |val| assert_eq!(*val, i))\n\t\t}\n\t}\n\n\t#[test]\n\tfn extend_collection() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(0);\n\t\tlet mut collection = RetryingLockCollection::new(vec![mutex1]);\n\n\t\tcollection.extend([mutex2]);\n\n\t\tassert_eq!(collection.into_inner().len(), 2);\n\t}\n\n\t#[test]\n\tfn lock_empty_lock_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection: RetryingLockCollection\u003c[RwLock\u003ci32\u003e; 0]\u003e = RetryingLockCollection::new([]);\n\n\t\tlet guard = collection.lock(key);\n\t\tassert!(guard.len() == 0);\n\t\tlet key = RetryingLockCollection::\u003c[RwLock\u003c_\u003e; 0]\u003e::unlock(guard);\n\n\t\tlet guard = collection.read(key);\n\t\tassert!(guard.len() == 0);\n\t}\n\n\t#[test]\n\tfn read_empty_lock_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection: RetryingLockCollection\u003c[RwLock\u003ci32\u003e; 0]\u003e = RetryingLockCollection::new([]);\n\n\t\tlet guard = collection.read(key);\n\t\tassert!(guard.len() == 0);\n\t\tlet key = RetryingLockCollection::\u003c[RwLock\u003c_\u003e; 0]\u003e::unlock_read(guard);\n\n\t\tlet guard = collection.lock(key);\n\t\tassert!(guard.len() == 0);\n\t}\n\n\t#[test]\n\tfn as_ref_works() {\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet collection = RetryingLockCollection::new_ref(\u0026mutexes);\n\n\t\tassert!(std::ptr::addr_eq(\u0026mutexes, collection.as_ref()))\n\t}\n\n\t#[test]\n\tfn as_mut_works() {\n\t\tlet mut mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet mut collection = RetryingLockCollection::new(\u0026mut mutexes);\n\n\t\tcollection.as_mut()[0] = Mutex::new(42);\n\n\t\tassert_eq!(*collection.as_mut()[0].get_mut(), 42);\n\t}\n\n\t#[test]\n\tfn child() {\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet collection = RetryingLockCollection::new_ref(\u0026mutexes);\n\n\t\tassert!(std::ptr::addr_eq(\u0026mutexes, *collection.child()))\n\t}\n\n\t#[test]\n\tfn child_mut_works() {\n\t\tlet mut mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet mut collection = RetryingLockCollection::new(\u0026mut mutexes);\n\n\t\tcollection.child_mut()[0] = Mutex::new(42);\n\n\t\tassert_eq!(*collection.child_mut()[0].get_mut(), 42);\n\t}\n\n\t#[test]\n\tfn into_child_works() {\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet mut collection = RetryingLockCollection::new(mutexes);\n\n\t\tcollection.child_mut()[0] = Mutex::new(42);\n\n\t\tassert_eq!(\n\t\t\t*collection\n\t\t\t\t.into_child()\n\t\t\t\t.as_mut()\n\t\t\t\t.get_mut(0)\n\t\t\t\t.unwrap()\n\t\t\t\t.get_mut(),\n\t\t\t42\n\t\t);\n\t}\n}\n","traces":[{"line":18,"address":[200936,200896,201621],"length":1,"stats":{"Line":11}},{"line":19,"address":[1092524,1093292],"length":1,"stats":{"Line":11}},{"line":20,"address":[1093355,1092587],"length":1,"stats":{"Line":11}},{"line":22,"address":[201664,201034,201692],"length":1,"stats":{"Line":33}},{"line":24,"address":[178622,178688],"length":1,"stats":{"Line":22}},{"line":25,"address":[203288,203427,203469,203191],"length":1,"stats":{"Line":45}},{"line":26,"address":[1093942,1093109,1093877,1093174],"length":1,"stats":{"Line":22}},{"line":27,"address":[179072],"length":1,"stats":{"Line":1}},{"line":31,"address":[179008],"length":1,"stats":{"Line":11}},{"line":44,"address":[],"length":0,"stats":{"Line":11}},{"line":45,"address":[227116],"length":1,"stats":{"Line":11}},{"line":47,"address":[172966,173018],"length":1,"stats":{"Line":22}},{"line":49,"address":[],"length":0,"stats":{"Line":0}},{"line":53,"address":[227221,227184],"length":1,"stats":{"Line":20}},{"line":54,"address":[227226],"length":1,"stats":{"Line":10}},{"line":56,"address":[219661],"length":1,"stats":{"Line":20}},{"line":57,"address":[],"length":0,"stats":{"Line":0}},{"line":61,"address":[210577],"length":1,"stats":{"Line":10}},{"line":62,"address":[185006,184854],"length":1,"stats":{"Line":20}},{"line":63,"address":[],"length":0,"stats":{"Line":10}},{"line":65,"address":[],"length":0,"stats":{"Line":0}},{"line":73,"address":[212728],"length":1,"stats":{"Line":10}},{"line":74,"address":[1094490,1095194,1095610,1096314,1097434,1096874,1097290,1097850,1096730,1097994,1095754,1094634,1095050,1096170],"length":1,"stats":{"Line":18}},{"line":77,"address":[212752],"length":1,"stats":{"Line":1}},{"line":78,"address":[210916],"length":1,"stats":{"Line":1}},{"line":81,"address":[212899],"length":1,"stats":{"Line":1}},{"line":85,"address":[140775],"length":1,"stats":{"Line":1}},{"line":88,"address":[212879],"length":1,"stats":{"Line":1}},{"line":89,"address":[],"length":0,"stats":{"Line":0}},{"line":94,"address":[],"length":0,"stats":{"Line":0}},{"line":97,"address":[212992],"length":1,"stats":{"Line":11}},{"line":98,"address":[140921],"length":1,"stats":{"Line":1}},{"line":99,"address":[213060],"length":1,"stats":{"Line":1}},{"line":100,"address":[],"length":0,"stats":{"Line":1}},{"line":106,"address":[203424,203613],"length":1,"stats":{"Line":3}},{"line":107,"address":[],"length":0,"stats":{"Line":3}},{"line":109,"address":[],"length":0,"stats":{"Line":6}},{"line":112,"address":[],"length":0,"stats":{"Line":0}},{"line":116,"address":[203508,203550],"length":1,"stats":{"Line":6}},{"line":118,"address":[200464],"length":1,"stats":{"Line":3}},{"line":119,"address":[184273,184415],"length":1,"stats":{"Line":6}},{"line":121,"address":[200657],"length":1,"stats":{"Line":3}},{"line":122,"address":[200732],"length":1,"stats":{"Line":3}},{"line":125,"address":[1099824,1099488],"length":1,"stats":{"Line":2}},{"line":126,"address":[184517],"length":1,"stats":{"Line":3}},{"line":130,"address":[184408],"length":1,"stats":{"Line":1}},{"line":132,"address":[1099936,1099950,1100016,1100030],"length":1,"stats":{"Line":2}},{"line":136,"address":[219461,219189,218944,219216],"length":1,"stats":{"Line":3}},{"line":137,"address":[],"length":0,"stats":{"Line":3}},{"line":139,"address":[],"length":0,"stats":{"Line":9}},{"line":140,"address":[],"length":0,"stats":{"Line":3}},{"line":144,"address":[182216,181968],"length":1,"stats":{"Line":5}},{"line":145,"address":[181996],"length":1,"stats":{"Line":5}},{"line":147,"address":[1595846,1596118,1595898,1595626,1595354,1595574,1595302,1596170],"length":1,"stats":{"Line":10}},{"line":149,"address":[],"length":0,"stats":{"Line":0}},{"line":152,"address":[202864,202901],"length":1,"stats":{"Line":8}},{"line":153,"address":[182106],"length":1,"stats":{"Line":4}},{"line":155,"address":[177664],"length":1,"stats":{"Line":8}},{"line":157,"address":[1100113,1100673,1101233,1101793],"length":1,"stats":{"Line":4}},{"line":158,"address":[206502,206654],"length":1,"stats":{"Line":8}},{"line":159,"address":[177928],"length":1,"stats":{"Line":4}},{"line":160,"address":[],"length":0,"stats":{"Line":0}},{"line":164,"address":[],"length":0,"stats":{"Line":4}},{"line":165,"address":[1100618,1101738,1101178,1102154,1101034,1100474,1101594,1102298],"length":1,"stats":{"Line":6}},{"line":168,"address":[206752],"length":1,"stats":{"Line":1}},{"line":170,"address":[1101567,1100447,1101007,1102127],"length":1,"stats":{"Line":1}},{"line":173,"address":[206899],"length":1,"stats":{"Line":1}},{"line":177,"address":[206855],"length":1,"stats":{"Line":1}},{"line":180,"address":[206879],"length":1,"stats":{"Line":1}},{"line":181,"address":[],"length":0,"stats":{"Line":0}},{"line":186,"address":[],"length":0,"stats":{"Line":0}},{"line":188,"address":[182166],"length":1,"stats":{"Line":5}},{"line":189,"address":[],"length":0,"stats":{"Line":1}},{"line":190,"address":[178292],"length":1,"stats":{"Line":1}},{"line":191,"address":[178345],"length":1,"stats":{"Line":1}},{"line":197,"address":[190352,190541],"length":1,"stats":{"Line":3}},{"line":198,"address":[1596374,1596790,1596582],"length":1,"stats":{"Line":3}},{"line":200,"address":[1596846,1596638,1596800,1596430,1596592,1596384],"length":1,"stats":{"Line":6}},{"line":203,"address":[],"length":0,"stats":{"Line":0}},{"line":206,"address":[190436,190478],"length":1,"stats":{"Line":6}},{"line":208,"address":[202464],"length":1,"stats":{"Line":3}},{"line":209,"address":[202481,202623],"length":1,"stats":{"Line":6}},{"line":211,"address":[],"length":0,"stats":{"Line":3}},{"line":212,"address":[202732],"length":1,"stats":{"Line":3}},{"line":215,"address":[1103264,1103936,1103600],"length":1,"stats":{"Line":2}},{"line":216,"address":[],"length":0,"stats":{"Line":2}},{"line":220,"address":[1103188,1103524,1103860],"length":1,"stats":{"Line":1}},{"line":222,"address":[1104128,1104048,1104208,1104222,1104142,1104062],"length":1,"stats":{"Line":2}},{"line":226,"address":[1596976,1597493,1597248,1597221],"length":1,"stats":{"Line":1}},{"line":227,"address":[1596994,1597266],"length":1,"stats":{"Line":1}},{"line":229,"address":[],"length":0,"stats":{"Line":3}},{"line":230,"address":[],"length":0,"stats":{"Line":1}},{"line":246,"address":[],"length":0,"stats":{"Line":1}},{"line":247,"address":[],"length":0,"stats":{"Line":1}},{"line":250,"address":[151168],"length":1,"stats":{"Line":8}},{"line":251,"address":[],"length":0,"stats":{"Line":9}},{"line":254,"address":[],"length":0,"stats":{"Line":3}},{"line":255,"address":[],"length":0,"stats":{"Line":3}},{"line":270,"address":[],"length":0,"stats":{"Line":4}},{"line":271,"address":[190577],"length":1,"stats":{"Line":4}},{"line":274,"address":[1597952],"length":1,"stats":{"Line":1}},{"line":275,"address":[1597969],"length":1,"stats":{"Line":1}},{"line":287,"address":[1597984],"length":1,"stats":{"Line":1}},{"line":288,"address":[1598001],"length":1,"stats":{"Line":1}},{"line":295,"address":[],"length":0,"stats":{"Line":2}},{"line":296,"address":[1598077,1598020],"length":1,"stats":{"Line":2}},{"line":307,"address":[1598128],"length":1,"stats":{"Line":1}},{"line":308,"address":[1598142],"length":1,"stats":{"Line":1}},{"line":319,"address":[1598192],"length":1,"stats":{"Line":1}},{"line":320,"address":[1598197],"length":1,"stats":{"Line":1}},{"line":331,"address":[1598208],"length":1,"stats":{"Line":1}},{"line":332,"address":[],"length":0,"stats":{"Line":1}},{"line":339,"address":[1598224],"length":1,"stats":{"Line":1}},{"line":340,"address":[],"length":0,"stats":{"Line":1}},{"line":341,"address":[],"length":0,"stats":{"Line":1}},{"line":346,"address":[1598304],"length":1,"stats":{"Line":1}},{"line":347,"address":[],"length":0,"stats":{"Line":1}},{"line":352,"address":[],"length":0,"stats":{"Line":1}},{"line":353,"address":[1598373],"length":1,"stats":{"Line":1}},{"line":358,"address":[],"length":0,"stats":{"Line":1}},{"line":359,"address":[],"length":0,"stats":{"Line":1}},{"line":364,"address":[1598400],"length":1,"stats":{"Line":1}},{"line":365,"address":[],"length":0,"stats":{"Line":1}},{"line":370,"address":[],"length":0,"stats":{"Line":1}},{"line":371,"address":[],"length":0,"stats":{"Line":1}},{"line":392,"address":[],"length":0,"stats":{"Line":9}},{"line":394,"address":[1598488,1598520,1598552,1598609,1598680,1598712,1598648,1598584,1598629],"length":1,"stats":{"Line":9}},{"line":414,"address":[1598736,1598752],"length":1,"stats":{"Line":3}},{"line":416,"address":[1598741,1598757],"length":1,"stats":{"Line":3}},{"line":442,"address":[],"length":0,"stats":{"Line":23}},{"line":463,"address":[1599136],"length":1,"stats":{"Line":1}},{"line":464,"address":[],"length":0,"stats":{"Line":0}},{"line":484,"address":[],"length":0,"stats":{"Line":2}},{"line":485,"address":[],"length":0,"stats":{"Line":0}},{"line":505,"address":[],"length":0,"stats":{"Line":1}},{"line":506,"address":[],"length":0,"stats":{"Line":1}},{"line":530,"address":[151392,151557],"length":1,"stats":{"Line":11}},{"line":532,"address":[],"length":0,"stats":{"Line":22}},{"line":535,"address":[1599600,1599568],"length":1,"stats":{"Line":3}},{"line":536,"address":[],"length":0,"stats":{"Line":3}},{"line":539,"address":[219920],"length":1,"stats":{"Line":2}},{"line":544,"address":[1599641],"length":1,"stats":{"Line":2}},{"line":567,"address":[227550,227424],"length":1,"stats":{"Line":9}},{"line":570,"address":[1599952,1600094,1600240,1600366,1599678,1599808],"length":1,"stats":{"Line":9}},{"line":574,"address":[227502],"length":1,"stats":{"Line":8}},{"line":610,"address":[203888,204080],"length":1,"stats":{"Line":2}},{"line":613,"address":[1600541,1600523,1600612,1600480],"length":1,"stats":{"Line":5}},{"line":614,"address":[],"length":0,"stats":{"Line":1}},{"line":616,"address":[204000],"length":1,"stats":{"Line":1}},{"line":617,"address":[],"length":0,"stats":{"Line":0}},{"line":620,"address":[203981],"length":1,"stats":{"Line":1}},{"line":643,"address":[],"length":0,"stats":{"Line":1}},{"line":644,"address":[],"length":0,"stats":{"Line":1}},{"line":645,"address":[],"length":0,"stats":{"Line":0}},{"line":650,"address":[],"length":0,"stats":{"Line":2}},{"line":651,"address":[],"length":0,"stats":{"Line":2}},{"line":654,"address":[1600768],"length":1,"stats":{"Line":1}},{"line":659,"address":[1600777],"length":1,"stats":{"Line":1}},{"line":682,"address":[],"length":0,"stats":{"Line":4}},{"line":685,"address":[1600814,1600928],"length":1,"stats":{"Line":4}},{"line":689,"address":[],"length":0,"stats":{"Line":3}},{"line":726,"address":[],"length":0,"stats":{"Line":3}},{"line":729,"address":[],"length":0,"stats":{"Line":5}},{"line":730,"address":[190909],"length":1,"stats":{"Line":1}},{"line":733,"address":[190950],"length":1,"stats":{"Line":1}},{"line":735,"address":[190928],"length":1,"stats":{"Line":1}},{"line":736,"address":[],"length":0,"stats":{"Line":0}},{"line":757,"address":[],"length":0,"stats":{"Line":1}},{"line":758,"address":[1601428],"length":1,"stats":{"Line":1}},{"line":759,"address":[],"length":0,"stats":{"Line":0}},{"line":780,"address":[],"length":0,"stats":{"Line":1}},{"line":781,"address":[],"length":0,"stats":{"Line":1}},{"line":797,"address":[],"length":0,"stats":{"Line":2}},{"line":798,"address":[],"length":0,"stats":{"Line":2}},{"line":825,"address":[],"length":0,"stats":{"Line":1}},{"line":826,"address":[],"length":0,"stats":{"Line":1}},{"line":853,"address":[],"length":0,"stats":{"Line":1}},{"line":854,"address":[],"length":0,"stats":{"Line":1}}],"covered":161,"coverable":178},{"path":["/","home","botahamec","Projects","happylock","src","collection","utils.rs"],"content":"use std::cell::Cell;\n\nuse crate::handle_unwind::handle_unwind;\nuse crate::lockable::{Lockable, RawLock, Sharable};\nuse crate::Keyable;\n\n#[must_use]\npub fn get_locks\u003cL: Lockable\u003e(data: \u0026L) -\u003e Vec\u003c\u0026dyn RawLock\u003e {\n\tlet mut locks = Vec::new();\n\tdata.get_ptrs(\u0026mut locks);\n\tlocks.sort_by_key(|lock| \u0026raw const **lock);\n\tlocks\n}\n\n#[must_use]\npub fn get_locks_unsorted\u003cL: Lockable\u003e(data: \u0026L) -\u003e Vec\u003c\u0026dyn RawLock\u003e {\n\tlet mut locks = Vec::new();\n\tdata.get_ptrs(\u0026mut locks);\n\tlocks\n}\n\n/// returns `true` if the sorted list contains a duplicate\n#[must_use]\npub fn ordered_contains_duplicates(l: \u0026[\u0026dyn RawLock]) -\u003e bool {\n\tif l.is_empty() {\n\t\t// Return early to prevent panic in the below call to `windows`\n\t\treturn false;\n\t}\n\n\tl.windows(2)\n\t\t// NOTE: addr_eq is necessary because eq would also compare the v-table pointers\n\t\t.any(|window| std::ptr::addr_eq(window[0], window[1]))\n}\n\n/// Lock a set of locks in the given order. It's UB to call this without a `ThreadKey`\npub unsafe fn ordered_write(locks: \u0026[\u0026dyn RawLock]) {\n\t// these will be unlocked in case of a panic\n\tlet locked = Cell::new(0);\n\n\thandle_unwind(\n\t\t|| {\n\t\t\tfor lock in locks {\n\t\t\t\tlock.raw_write();\n\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t}\n\t\t},\n\t\t|| attempt_to_recover_writes_from_panic(\u0026locks[0..locked.get()]),\n\t)\n}\n\n/// Lock a set of locks in the given order. It's UB to call this without a `ThreadKey`\npub unsafe fn ordered_read(locks: \u0026[\u0026dyn RawLock]) {\n\tlet locked = Cell::new(0);\n\n\thandle_unwind(\n\t\t|| {\n\t\t\tfor lock in locks {\n\t\t\t\tlock.raw_read();\n\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t}\n\t\t},\n\t\t|| attempt_to_recover_reads_from_panic(\u0026locks[0..locked.get()]),\n\t)\n}\n\n/// Locks the locks in the order they are given. This causes deadlock if the\n/// locks contain duplicates, or if this is called by multiple threads with the\n/// locks in different orders.\npub unsafe fn ordered_try_write(locks: \u0026[\u0026dyn RawLock]) -\u003e bool {\n\tlet locked = Cell::new(0);\n\n\thandle_unwind(\n\t\t|| unsafe {\n\t\t\tfor (i, lock) in locks.iter().enumerate() {\n\t\t\t\t// safety: we have the thread key\n\t\t\t\tif lock.raw_try_write() {\n\t\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t\t} else {\n\t\t\t\t\tfor lock in \u0026locks[0..i] {\n\t\t\t\t\t\t// safety: this lock was already acquired\n\t\t\t\t\t\tlock.raw_unlock_write();\n\t\t\t\t\t}\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttrue\n\t\t},\n\t\t||\n\t\t// safety: everything in locked is locked\n\t\tattempt_to_recover_writes_from_panic(\u0026locks[0..locked.get()]),\n\t)\n}\n\n/// Locks the locks in the order they are given. This causes deadlock if this\n/// is called by multiple threads with the locks in different orders.\npub unsafe fn ordered_try_read(locks: \u0026[\u0026dyn RawLock]) -\u003e bool {\n\t// these will be unlocked in case of a panic\n\tlet locked = Cell::new(0);\n\n\thandle_unwind(\n\t\t|| unsafe {\n\t\t\tfor (i, lock) in locks.iter().enumerate() {\n\t\t\t\t// safety: we have the thread key\n\t\t\t\tif lock.raw_try_read() {\n\t\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t\t} else {\n\t\t\t\t\tfor lock in \u0026locks[0..i] {\n\t\t\t\t\t\t// safety: this lock was already acquired\n\t\t\t\t\t\tlock.raw_unlock_read();\n\t\t\t\t\t}\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttrue\n\t\t},\n\t\t||\n\t\t// safety: everything in locked is locked\n\t\tattempt_to_recover_reads_from_panic(\u0026locks[0..locked.get()]),\n\t)\n}\n\npub fn scoped_write\u003c'a, L: RawLock + Lockable, R\u003e(\n\tcollection: \u0026'a L,\n\tkey: impl Keyable,\n\tf: impl FnOnce(L::DataMut\u003c'a\u003e) -\u003e R,\n) -\u003e R {\n\tunsafe {\n\t\t// safety: we have the key\n\t\tcollection.raw_write();\n\n\t\t// safety: we just locked this\n\t\tlet r = f(collection.data_mut());\n\n\t\t// this ensures the key is held long enough\n\t\tdrop(key);\n\n\t\t// safety: we've locked already, and aren't using the data again\n\t\tcollection.raw_unlock_write();\n\n\t\tr\n\t}\n}\n\npub fn scoped_try_write\u003c'a, L: RawLock + Lockable, Key: Keyable, R\u003e(\n\tcollection: \u0026'a L,\n\tkey: Key,\n\tf: impl FnOnce(L::DataMut\u003c'a\u003e) -\u003e R,\n) -\u003e Result\u003cR, Key\u003e {\n\tunsafe {\n\t\t// safety: we have the key\n\t\tif !collection.raw_try_write() {\n\t\t\treturn Err(key);\n\t\t}\n\n\t\t// safety: we just locked this\n\t\tlet r = f(collection.data_mut());\n\n\t\t// this ensures the key is held long enough\n\t\tdrop(key);\n\n\t\t// safety: we've locked already, and aren't using the data again\n\t\tcollection.raw_unlock_write();\n\n\t\tOk(r)\n\t}\n}\n\npub fn scoped_read\u003c'a, L: RawLock + Sharable, R\u003e(\n\tcollection: \u0026'a L,\n\tkey: impl Keyable,\n\tf: impl FnOnce(L::DataRef\u003c'a\u003e) -\u003e R,\n) -\u003e R {\n\tunsafe {\n\t\t// safety: we have the key\n\t\tcollection.raw_read();\n\n\t\t// safety: we just locked this\n\t\tlet r = f(collection.data_ref());\n\n\t\t// this ensures the key is held long enough\n\t\tdrop(key);\n\n\t\t// safety: we've locked already, and aren't using the data again\n\t\tcollection.raw_unlock_read();\n\n\t\tr\n\t}\n}\n\npub fn scoped_try_read\u003c'a, L: RawLock + Sharable, Key: Keyable, R\u003e(\n\tcollection: \u0026'a L,\n\tkey: Key,\n\tf: impl FnOnce(L::DataRef\u003c'a\u003e) -\u003e R,\n) -\u003e Result\u003cR, Key\u003e {\n\tunsafe {\n\t\t// safety: we have the key\n\t\tif !collection.raw_try_read() {\n\t\t\treturn Err(key);\n\t\t}\n\n\t\t// safety: we just locked this\n\t\tlet r = f(collection.data_ref());\n\n\t\t// this ensures the key is held long enough\n\t\tdrop(key);\n\n\t\t// safety: we've locked already, and aren't using the data again\n\t\tcollection.raw_unlock_read();\n\n\t\tOk(r)\n\t}\n}\n\n/// Unlocks the already locked locks in order to recover from a panic\npub unsafe fn attempt_to_recover_writes_from_panic(locks: \u0026[\u0026dyn RawLock]) {\n\thandle_unwind(\n\t\t|| {\n\t\t\t// safety: the caller assumes that these are already locked\n\t\t\tlocks.iter().for_each(|lock| lock.raw_unlock_write());\n\t\t},\n\t\t// if we get another panic in here, we'll just have to poison what remains\n\t\t|| locks.iter().for_each(|l| l.poison()),\n\t)\n}\n\n/// Unlocks the already locked locks in order to recover from a panic\npub unsafe fn attempt_to_recover_reads_from_panic(locked: \u0026[\u0026dyn RawLock]) {\n\thandle_unwind(\n\t\t|| {\n\t\t\t// safety: the caller assumes these are already locked\n\t\t\tlocked.iter().for_each(|lock| lock.raw_unlock_read());\n\t\t},\n\t\t// if we get another panic in here, we'll just have to poison what remains\n\t\t|| locked.iter().for_each(|l| l.poison()),\n\t)\n}\n\n#[cfg(test)]\nmod tests {\n\tuse crate::collection::utils::ordered_contains_duplicates;\n\n\t#[test]\n\tfn empty_array_does_not_contain_duplicates() {\n\t\tassert!(!ordered_contains_duplicates(\u0026[]))\n\t}\n}\n","traces":[{"line":8,"address":[],"length":0,"stats":{"Line":9}},{"line":9,"address":[],"length":0,"stats":{"Line":9}},{"line":10,"address":[426641,427217,426449,426065,427409,427025,426257,426833,425873],"length":1,"stats":{"Line":9}},{"line":11,"address":[],"length":0,"stats":{"Line":26}},{"line":12,"address":[],"length":0,"stats":{"Line":9}},{"line":16,"address":[159885,159760],"length":1,"stats":{"Line":26}},{"line":17,"address":[],"length":0,"stats":{"Line":26}},{"line":18,"address":[],"length":0,"stats":{"Line":26}},{"line":19,"address":[],"length":0,"stats":{"Line":26}},{"line":24,"address":[536384],"length":1,"stats":{"Line":8}},{"line":25,"address":[453224],"length":1,"stats":{"Line":8}},{"line":27,"address":[538422],"length":1,"stats":{"Line":1}},{"line":30,"address":[535275],"length":1,"stats":{"Line":8}},{"line":32,"address":[526845,526963,526816],"length":1,"stats":{"Line":25}},{"line":36,"address":[532832],"length":1,"stats":{"Line":4}},{"line":38,"address":[517230],"length":1,"stats":{"Line":5}},{"line":41,"address":[518608],"length":1,"stats":{"Line":7}},{"line":42,"address":[430567,430494],"length":1,"stats":{"Line":14}},{"line":43,"address":[527107],"length":1,"stats":{"Line":7}},{"line":44,"address":[557021],"length":1,"stats":{"Line":5}},{"line":47,"address":[565413,565232,565239],"length":1,"stats":{"Line":16}},{"line":52,"address":[535440],"length":1,"stats":{"Line":2}},{"line":53,"address":[547230],"length":1,"stats":{"Line":2}},{"line":56,"address":[430864],"length":1,"stats":{"Line":2}},{"line":57,"address":[430951,430878],"length":1,"stats":{"Line":4}},{"line":58,"address":[430961],"length":1,"stats":{"Line":2}},{"line":59,"address":[548749],"length":1,"stats":{"Line":2}},{"line":62,"address":[538573],"length":1,"stats":{"Line":5}},{"line":69,"address":[547312],"length":1,"stats":{"Line":2}},{"line":70,"address":[509031],"length":1,"stats":{"Line":2}},{"line":73,"address":[543424],"length":1,"stats":{"Line":4}},{"line":74,"address":[549197,549055],"length":1,"stats":{"Line":4}},{"line":76,"address":[557906],"length":1,"stats":{"Line":2}},{"line":77,"address":[547680,547546],"length":1,"stats":{"Line":4}},{"line":79,"address":[543917,543662,543978,543853],"length":1,"stats":{"Line":4}},{"line":81,"address":[566410],"length":1,"stats":{"Line":1}},{"line":83,"address":[549584],"length":1,"stats":{"Line":1}},{"line":87,"address":[543574],"length":1,"stats":{"Line":1}},{"line":89,"address":[566464],"length":1,"stats":{"Line":3}},{"line":91,"address":[528439,528613],"length":1,"stats":{"Line":2}},{"line":97,"address":[517520],"length":1,"stats":{"Line":2}},{"line":99,"address":[533159],"length":1,"stats":{"Line":2}},{"line":102,"address":[528640],"length":1,"stats":{"Line":4}},{"line":103,"address":[548077,547935],"length":1,"stats":{"Line":4}},{"line":105,"address":[449794],"length":1,"stats":{"Line":3}},{"line":106,"address":[450192,450058],"length":1,"stats":{"Line":4}},{"line":108,"address":[558782,558973,559098,559037],"length":1,"stats":{"Line":4}},{"line":110,"address":[548490],"length":1,"stats":{"Line":1}},{"line":112,"address":[548464],"length":1,"stats":{"Line":1}},{"line":116,"address":[528790],"length":1,"stats":{"Line":1}},{"line":118,"address":[548544],"length":1,"stats":{"Line":3}},{"line":120,"address":[544887,545061],"length":1,"stats":{"Line":2}},{"line":124,"address":[183024,183274],"length":1,"stats":{"Line":27}},{"line":131,"address":[140040],"length":1,"stats":{"Line":27}},{"line":134,"address":[],"length":0,"stats":{"Line":27}},{"line":137,"address":[183209],"length":1,"stats":{"Line":27}},{"line":140,"address":[140216],"length":1,"stats":{"Line":27}},{"line":142,"address":[],"length":0,"stats":{"Line":0}},{"line":146,"address":[439886,439296,440485,439600,440800,440168,440208,440496,439574,439864,441390,440190,441078,441104,439904,440782,440463,441368,440760],"length":1,"stats":{"Line":38}},{"line":153,"address":[213952,215472,214488,214864,214560,213880,215400,214184,214256,215096,215168,214792],"length":1,"stats":{"Line":76}},{"line":154,"address":[188747,188443,189051,187531,188139,187835],"length":1,"stats":{"Line":20}},{"line":158,"address":[180326,180796,180022,179580,179611,180492,180188,180219,179307,179718,179884,180934,179276,179414,179915,180523,180827,180630],"length":1,"stats":{"Line":18}},{"line":161,"address":[225454,224238,224846,225758,224542,225150],"length":1,"stats":{"Line":18}},{"line":164,"address":[],"length":0,"stats":{"Line":18}},{"line":166,"address":[225499,224283,224587,224891,225803,225195],"length":1,"stats":{"Line":18}},{"line":170,"address":[443488,443475,444003,442448,443731,441908,441667,443744,442976,441680,442435,443216,442163,442933,442707,442720,442955,441920,443204,442176,441408],"length":1,"stats":{"Line":10}},{"line":177,"address":[442472,442734,443240,441944,443512,441704,442200,441432,443768,443000],"length":1,"stats":{"Line":10}},{"line":180,"address":[441773,443069,443309,441892,442269,443188,441501,441651,442917,443581,442691,443715,442419,442803,442013,442541,442147,443837,443987,443459],"length":1,"stats":{"Line":10}},{"line":183,"address":[441597,442637,443665,443933,443141,441845,442097,442870,442365,443405],"length":1,"stats":{"Line":10}},{"line":186,"address":[],"length":0,"stats":{"Line":10}},{"line":188,"address":[],"length":0,"stats":{"Line":0}},{"line":192,"address":[228016,228928,229206,229510,228902,229232,229536,228598,228320,229814,228294,228624],"length":1,"stats":{"Line":12}},{"line":199,"address":[],"length":0,"stats":{"Line":24}},{"line":200,"address":[],"length":0,"stats":{"Line":7}},{"line":204,"address":[229387,229083,229494,229691,228475,228748,229356,229052,228582,228140,229660,228171,228278,228886,228444,229190,229798,228779],"length":1,"stats":{"Line":5}},{"line":207,"address":[228526,228830,228222,229438,229742,229134],"length":1,"stats":{"Line":5}},{"line":210,"address":[],"length":0,"stats":{"Line":5}},{"line":212,"address":[229483,228571,228267,229179,228875,229787],"length":1,"stats":{"Line":5}},{"line":217,"address":[536912],"length":1,"stats":{"Line":6}},{"line":219,"address":[544656],"length":1,"stats":{"Line":8}},{"line":221,"address":[545102,545136,545150],"length":1,"stats":{"Line":20}},{"line":224,"address":[559470,559518,559456,559504],"length":1,"stats":{"Line":4}},{"line":229,"address":[517680],"length":1,"stats":{"Line":4}},{"line":231,"address":[529632],"length":1,"stats":{"Line":4}},{"line":233,"address":[545262,545296,545310],"length":1,"stats":{"Line":12}},{"line":236,"address":[544958,544910,544944,544896],"length":1,"stats":{"Line":4}}],"covered":84,"coverable":86},{"path":["/","home","botahamec","Projects","happylock","src","collection.rs"],"content":"use std::cell::UnsafeCell;\n\nuse crate::{lockable::RawLock, ThreadKey};\n\nmod boxed;\nmod guard;\nmod owned;\nmod r#ref;\nmod retry;\npub(crate) mod utils;\n\n/// Locks a collection of locks, which cannot be shared immutably.\n///\n/// This could be a tuple of [`Lockable`] types, an array, or a `Vec`. But it\n/// can be safely locked without causing a deadlock.\n///\n/// The data in this collection is guaranteed to not contain duplicates because\n/// `L` must always implement [`OwnedLockable`]. The underlying data may not be\n/// immutably referenced and locked. Because of this, there is no need for\n/// sorting the locks in the collection, or checking for duplicates, because it\n/// can be guaranteed that until the underlying collection is mutated (which\n/// requires releasing all acquired locks in the collection to do), then the\n/// locks will stay in the same order and be locked in that order, preventing\n/// cyclic wait.\n///\n/// [`Lockable`]: `crate::lockable::Lockable`\n/// [`OwnedLockable`]: `crate::lockable::OwnedLockable`\n\n// this type caches the idea that no immutable references to the underlying\n// collection exist\n#[derive(Debug)]\npub struct OwnedLockCollection\u003cL\u003e {\n\tdata: L,\n}\n\n/// Locks a reference to a collection of locks, by sorting them by memory\n/// address.\n///\n/// This could be a tuple of [`Lockable`] types, an array, or a `Vec`. But it\n/// can be safely locked without causing a deadlock.\n///\n/// Upon construction, it must be confirmed that the collection contains no\n/// duplicate locks. This can be done by either using [`OwnedLockable`] or by\n/// checking. Regardless of how this is done, the locks will be sorted by their\n/// memory address before locking them. The sorted order of the locks is stored\n/// within this collection.\n///\n/// Unlike [`BoxedLockCollection`], this type does not allocate memory for the\n/// data, although it does allocate memory for the sorted list of lock\n/// references. This makes it slightly faster, but lifetimes must be handled.\n///\n/// [`Lockable`]: `crate::lockable::Lockable`\n/// [`OwnedLockable`]: `crate::lockable::OwnedLockable`\n//\n// This type was born when I eventually realized that I needed a self\n// referential structure. That used boxing, so I elected to make a more\n// efficient implementation (polonius please save us)\n//\n// This type caches the sorting order of the locks and the fact that it doesn't\n// contain any duplicates.\npub struct RefLockCollection\u003c'a, L\u003e {\n\tdata: \u0026'a L,\n\tlocks: Vec\u003c\u0026'a dyn RawLock\u003e,\n}\n\n/// Locks a collection of locks, stored in the heap, by sorting them by memory\n/// address.\n///\n/// This could be a tuple of [`Lockable`] types, an array, or a `Vec`. But it\n/// can be safely locked without causing a deadlock.\n///\n/// Upon construction, it must be confirmed that the collection contains no\n/// duplicate locks. This can be done by either using [`OwnedLockable`] or by\n/// checking. Regardless of how this is done, the locks will be sorted by their\n/// memory address before locking them. The sorted order of the locks is stored\n/// within this collection.\n///\n/// Unlike [`RefLockCollection`], this is a self-referential type which boxes\n/// the data that is given to it. This means no lifetimes are necessary on the\n/// type itself, but it is slightly slower because of the memory allocation.\n///\n/// [`Lockable`]: `crate::lockable::Lockable`\n/// [`OwnedLockable`]: `crate::lockable::OwnedLockable`\n//\n// This type caches the sorting order of the locks and the fact that it doesn't\n// contain any duplicates.\npub struct BoxedLockCollection\u003cL\u003e {\n\tdata: *const UnsafeCell\u003cL\u003e,\n\tlocks: Vec\u003c\u0026'static dyn RawLock\u003e,\n}\n\n/// Locks a collection of locks using a retrying algorithm.\n///\n/// This could be a tuple of [`Lockable`] types, an array, or a `Vec`. But it\n/// can be safely locked without causing a deadlock.\n///\n/// The data in this collection is guaranteed to not contain duplicates, but it\n/// also not be sorted. In some cases the lack of sorting can increase\n/// performance. However, in most cases, this collection will be slower. Cyclic\n/// wait is not guaranteed here, so the locking algorithm must release all its\n/// locks if one of the lock attempts blocks. This results in wasted time and\n/// potential [livelocking].\n///\n/// However, one case where this might be faster than [`RefLockCollection`] is\n/// when the first lock in the collection is always the first in any\n/// collection, and the other locks in the collection are always locked after\n/// that first lock is acquired. This means that as soon as it is locked, there\n/// will be no need to unlock it later on subsequent lock attempts, because\n/// they will always succeed.\n///\n/// [`Lockable`]: `crate::lockable::Lockable`\n/// [`OwnedLockable`]: `crate::lockable::OwnedLockable`\n/// [livelocking]: https://en.wikipedia.org/wiki/Deadlock#Livelock\n//\n// This type caches the fact that there are no duplicates\n#[derive(Debug)]\npub struct RetryingLockCollection\u003cL\u003e {\n\tdata: L,\n}\n\n/// A RAII guard for a generic [`Lockable`] type.\n///\n/// [`Lockable`]: `crate::lockable::Lockable`\npub struct LockGuard\u003cGuard\u003e {\n\tguard: Guard,\n\tkey: ThreadKey,\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","src","handle_unwind.rs"],"content":"use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};\n\n/// Runs `try_fn`. If it unwinds, it will run `catch` and then continue\n/// unwinding. This is used instead of `scopeguard` to ensure the `catch`\n/// function doesn't run if the thread is already panicking. The unwind\n/// must specifically be caused by the `try_fn`\npub fn handle_unwind\u003cR, F: FnOnce() -\u003e R, G: FnOnce()\u003e(try_fn: F, catch: G) -\u003e R {\n\tlet try_fn = AssertUnwindSafe(try_fn);\n\tcatch_unwind(try_fn).unwrap_or_else(|e| {\n\t\tcatch();\n\t\tresume_unwind(e)\n\t})\n}\n","traces":[{"line":7,"address":[509474,510000,509840,509638,510152,510326,509816,509488,509664,509986,509328,510176],"length":1,"stats":{"Line":151}},{"line":8,"address":[234713,234578,234283,234978,234850,235106,234441],"length":1,"stats":{"Line":118}},{"line":9,"address":[235177,235984,235945,235216,234921,235331,235600,235971,234649,235344,235856,234461,235049,234308,234993,235305,235121,235689,235715,234498,236099,235561,234350,235817,234733,234593,235436,236073,235472,235460,234865,235728,235587,234770,235843],"length":1,"stats":{"Line":301}},{"line":10,"address":[538487,538615,538333,538045,538733,538189],"length":1,"stats":{"Line":28}},{"line":11,"address":[549421,548877,548733,549153,549281,549021],"length":1,"stats":{"Line":24}}],"covered":5,"coverable":5},{"path":["/","home","botahamec","Projects","happylock","src","key.rs"],"content":"use std::cell::{Cell, LazyCell};\nuse std::fmt::{self, Debug};\nuse std::marker::PhantomData;\n\nuse sealed::Sealed;\n\n// Sealed to prevent other key types from being implemented. Otherwise, this\n// would almost instant undefined behavior.\nmod sealed {\n\tuse super::ThreadKey;\n\n\tpub trait Sealed {}\n\timpl Sealed for ThreadKey {}\n\timpl Sealed for \u0026mut ThreadKey {}\n}\n\nthread_local! {\n\tstatic KEY: LazyCell\u003cKeyCell\u003e = LazyCell::new(KeyCell::default);\n}\n\n/// The key for the current thread.\n///\n/// Only one of these exist per thread. To get the current thread's key, call\n/// [`ThreadKey::get`]. If the `ThreadKey` is dropped, it can be re-obtained.\npub struct ThreadKey {\n\tphantom: PhantomData\u003c*const ()\u003e, // implement !Send and !Sync\n}\n\n/// Allows the type to be used as a key for a lock\n///\n/// # Safety\n///\n/// Only one value which implements this trait may be allowed to exist at a\n/// time. Creating a new `Keyable` value requires making any other `Keyable`\n/// values invalid.\npub unsafe trait Keyable: Sealed {}\nunsafe impl Keyable for ThreadKey {}\n// the ThreadKey can't be moved while a mutable reference to it exists\nunsafe impl Keyable for \u0026mut ThreadKey {}\n\n// Implementing this means we can allow `MutexGuard` to be Sync\n// Safety: a \u0026ThreadKey is useless by design.\nunsafe impl Sync for ThreadKey {}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl Debug for ThreadKey {\n\tfn fmt(\u0026self, f: \u0026mut fmt::Formatter\u003c'_\u003e) -\u003e fmt::Result {\n\t\twrite!(f, \"ThreadKey\")\n\t}\n}\n\n// If you lose the thread key, you can get it back by calling ThreadKey::get\nimpl Drop for ThreadKey {\n\tfn drop(\u0026mut self) {\n\t\t// safety: a thread key cannot be acquired without creating the lock\n\t\t// safety: the key is lost, so it's safe to unlock the cell\n\t\tunsafe { KEY.with(|key| key.force_unlock()) }\n\t}\n}\n\nimpl ThreadKey {\n\t/// Get the current thread's `ThreadKey`, if it's not already taken.\n\t///\n\t/// The first time this is called, it will successfully return a\n\t/// `ThreadKey`. However, future calls to this function on the same thread\n\t/// will return [`None`], unless the key is dropped or unlocked first.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::ThreadKey;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// ```\n\t#[must_use]\n\tpub fn get() -\u003e Option\u003cSelf\u003e {\n\t\t// safety: we just acquired the lock\n\t\t// safety: if this code changes, check to ensure the requirement for\n\t\t// the Drop implementation is still true\n\t\tKEY.with(|key| {\n\t\t\tkey.try_lock().then_some(Self {\n\t\t\t\tphantom: PhantomData,\n\t\t\t})\n\t\t})\n\t}\n}\n\n/// A dumb lock that's just a wrapper for an [`AtomicBool`].\n#[derive(Default)]\nstruct KeyCell {\n\tis_locked: Cell\u003cbool\u003e,\n}\n\nimpl KeyCell {\n\t/// Attempt to lock the `KeyCell`. This is not a fair lock.\n\t#[must_use]\n\tpub fn try_lock(\u0026self) -\u003e bool {\n\t\t!self.is_locked.replace(true)\n\t}\n\n\t/// Forcibly unlocks the `KeyCell`. This should only be called if the key\n\t/// from this `KeyCell` has been \"lost\".\n\tpub unsafe fn force_unlock(\u0026self) {\n\t\tself.is_locked.set(false);\n\t}\n}\n\n#[cfg(test)]\nmod tests {\n\tuse super::*;\n\n\t#[test]\n\tfn thread_key_returns_some_on_first_call() {\n\t\tassert!(ThreadKey::get().is_some());\n\t}\n\n\t#[test]\n\tfn thread_key_returns_none_on_second_call() {\n\t\tlet key = ThreadKey::get();\n\t\tassert!(ThreadKey::get().is_none());\n\t\tdrop(key);\n\t}\n\n\t#[test]\n\tfn dropping_thread_key_allows_reobtaining() {\n\t\tdrop(ThreadKey::get());\n\t\tassert!(ThreadKey::get().is_some())\n\t}\n}\n","traces":[{"line":18,"address":[536840],"length":1,"stats":{"Line":23}},{"line":55,"address":[534960],"length":1,"stats":{"Line":11}},{"line":58,"address":[515685],"length":1,"stats":{"Line":36}},{"line":77,"address":[542368],"length":1,"stats":{"Line":21}},{"line":81,"address":[540928],"length":1,"stats":{"Line":42}},{"line":82,"address":[542889],"length":1,"stats":{"Line":22}},{"line":98,"address":[536784],"length":1,"stats":{"Line":23}},{"line":99,"address":[551077],"length":1,"stats":{"Line":23}},{"line":104,"address":[536816],"length":1,"stats":{"Line":12}},{"line":105,"address":[439893],"length":1,"stats":{"Line":12}}],"covered":10,"coverable":10},{"path":["/","home","botahamec","Projects","happylock","src","lib.rs"],"content":"#![warn(clippy::pedantic)]\n#![warn(clippy::nursery)]\n#![allow(clippy::module_name_repetitions)]\n#![allow(clippy::declare_interior_mutable_const)]\n#![allow(clippy::semicolon_if_nothing_returned)]\n#![allow(clippy::module_inception)]\n#![allow(clippy::single_match_else)]\n\n//! As it turns out, the Rust borrow checker is powerful enough that, if the\n//! standard library supported it, we could've made deadlocks undefined\n//! behavior. This library currently serves as a proof of concept for how that\n//! would work.\n//!\n//! # Theory\n//!\n//! There are four conditions necessary for a deadlock to occur. In order to\n//! prevent deadlocks, we just need to prevent one of the following:\n//!\n//! 1. mutual exclusion\n//! 2. non-preemptive allocation\n//! 3. circular wait\n//! 4. **partial allocation**\n//!\n//! This library seeks to solve **partial allocation** by requiring total\n//! allocation. All the resources a thread needs must be allocated at the same\n//! time. In order to request new resources, the old resources must be dropped\n//! first. Requesting multiple resources at once is atomic. You either get all\n//! the requested resources or none at all.\n//!\n//! As an optimization, this library also often prevents **circular wait**.\n//! Many collections sort the locks in order of their memory address. As long\n//! as the locks are always acquired in that order, then time doesn't need to\n//! be wasted on releasing locks after a failure and re-acquiring them later.\n//!\n//! # Examples\n//!\n//! Simple example:\n//! ```\n//! use std::thread;\n//! use happylock::{Mutex, ThreadKey};\n//!\n//! const N: usize = 10;\n//!\n//! static DATA: Mutex\u003ci32\u003e = Mutex::new(0);\n//!\n//! for _ in 0..N {\n//! thread::spawn(move || {\n//! // each thread gets one thread key\n//! let key = ThreadKey::get().unwrap();\n//!\n//! // unlocking a mutex requires a ThreadKey\n//! let mut data = DATA.lock(key);\n//! *data += 1;\n//!\n//! // the key is unlocked at the end of the scope\n//! });\n//! }\n//!\n//! let key = ThreadKey::get().unwrap();\n//! let data = DATA.lock(key);\n//! println!(\"{}\", *data);\n//! ```\n//!\n//! To lock multiple mutexes at a time, create a [`LockCollection`]:\n//!\n//! ```\n//! use std::thread;\n//! use happylock::{LockCollection, Mutex, ThreadKey};\n//!\n//! const N: usize = 10;\n//!\n//! static DATA_1: Mutex\u003ci32\u003e = Mutex::new(0);\n//! static DATA_2: Mutex\u003cString\u003e = Mutex::new(String::new());\n//!\n//! for _ in 0..N {\n//! thread::spawn(move || {\n//! let key = ThreadKey::get().unwrap();\n//!\n//! // happylock ensures at runtime there are no duplicate locks\n//! let collection = LockCollection::try_new((\u0026DATA_1, \u0026DATA_2)).unwrap();\n//! let mut guard = collection.lock(key);\n//!\n//! *guard.1 = (100 - *guard.0).to_string();\n//! *guard.0 += 1;\n//! });\n//! }\n//!\n//! let key = ThreadKey::get().unwrap();\n//! let data = LockCollection::try_new((\u0026DATA_1, \u0026DATA_2)).unwrap();\n//! let data = data.lock(key);\n//! println!(\"{}\", *data.0);\n//! println!(\"{}\", *data.1);\n//! ```\n//!\n//! In many cases, the [`LockCollection::new`] or [`LockCollection::new_ref`]\n//! method can be used, improving performance.\n//!\n//! ```rust\n//! use std::thread;\n//! use happylock::{LockCollection, Mutex, ThreadKey};\n//!\n//! const N: usize = 32;\n//!\n//! static DATA: [Mutex\u003ci32\u003e; 2] = [Mutex::new(0), Mutex::new(1)];\n//!\n//! for _ in 0..N {\n//! thread::spawn(move || {\n//! let key = ThreadKey::get().unwrap();\n//!\n//! // a reference to a type that implements `OwnedLockable` will never\n//! // contain duplicates, so no duplicate checking is needed.\n//! let collection = LockCollection::new_ref(\u0026DATA);\n//! let mut guard = collection.lock(key);\n//!\n//! let x = *guard[1];\n//! *guard[1] += *guard[0];\n//! *guard[0] = x;\n//! });\n//! }\n//!\n//! let key = ThreadKey::get().unwrap();\n//! let data = LockCollection::new_ref(\u0026DATA);\n//! let data = data.lock(key);\n//! println!(\"{}\", data[0]);\n//! println!(\"{}\", data[1]);\n//! ```\n//!\n//! # Performance\n//!\n//! **The `ThreadKey` is a mostly-zero cost abstraction.** It doesn't use any\n//! memory, and it doesn't really exist at run-time. The only cost comes from\n//! calling `ThreadKey::get()`, because the function has to ensure at runtime\n//! that the key hasn't already been taken. Dropping the key will also have a\n//! small cost.\n//!\n//! **Consider [`OwnedLockCollection`].** This will almost always be the\n//! fastest lock collection. It doesn't expose the underlying collection\n//! immutably, which means that it will always be locked in the same order, and\n//! doesn't need any sorting.\n//!\n//! **Avoid [`LockCollection::try_new`].** This constructor will check to make\n//! sure that the collection contains no duplicate locks. In most cases, this\n//! is O(nlogn), where n is the number of locks in the collections but in the\n//! case of [`RetryingLockCollection`], it's close to O(n).\n//! [`LockCollection::new`] and [`LockCollection::new_ref`] don't need these\n//! checks because they use [`OwnedLockable`], which is guaranteed to be unique\n//! as long as it is accessible. As a last resort,\n//! [`LockCollection::new_unchecked`] doesn't do this check, but is unsafe to\n//! call.\n//!\n//! **Know how to use [`RetryingLockCollection`].** This collection doesn't do\n//! any sorting, but uses a wasteful lock algorithm. It can't rely on the order\n//! of the locks to be the same across threads, so if it finds a lock that it\n//! can't acquire without blocking, it'll first release all of the locks it\n//! already acquired to avoid blocking other threads. This is wasteful because\n//! this algorithm may end up re-acquiring the same lock multiple times. To\n//! avoid this, ensure that (1) the first lock in the collection is always the\n//! first lock in any collection it appears in, and (2) the other locks in the\n//! collection are always preceded by that first lock. This will prevent any\n//! wasted time from re-acquiring locks. If you're unsure, [`LockCollection`]\n//! is a sensible default.\n//!\n//! [`OwnedLockable`]: `lockable::OwnedLockable`\n//! [`OwnedLockCollection`]: `collection::OwnedLockCollection`\n//! [`RetryingLockCollection`]: `collection::RetryingLockCollection`\n\nmod handle_unwind;\nmod key;\n\npub mod collection;\npub mod lockable;\npub mod mutex;\npub mod poisonable;\npub mod rwlock;\n\npub use key::{Keyable, ThreadKey};\n\n#[cfg(feature = \"spin\")]\npub use mutex::SpinLock;\n\n// Personally, I think re-exports look ugly in the rust documentation, so I\n// went with type aliases instead.\n\n/// A collection of locks that can be acquired simultaneously.\n///\n/// This re-exports [`BoxedLockCollection`] as a sensible default.\n///\n/// [`BoxedLockCollection`]: collection::BoxedLockCollection\npub type LockCollection\u003cL\u003e = collection::BoxedLockCollection\u003cL\u003e;\n\n/// A re-export for [`poisonable::Poisonable`]\npub type Poisonable\u003cL\u003e = poisonable::Poisonable\u003cL\u003e;\n\n/// A mutual exclusion primitive useful for protecting shared data, which cannot deadlock.\n///\n/// By default, this uses `parking_lot` as a backend.\n#[cfg(feature = \"parking_lot\")]\npub type Mutex\u003cT\u003e = mutex::Mutex\u003cT, parking_lot::RawMutex\u003e;\n\n/// A reader-writer lock\n///\n/// By default, this uses `parking_lot` as a backend.\n#[cfg(feature = \"parking_lot\")]\npub type RwLock\u003cT\u003e = rwlock::RwLock\u003cT, parking_lot::RawRwLock\u003e;\n","traces":[{"line":197,"address":[423062],"length":1,"stats":{"Line":10}}],"covered":1,"coverable":1},{"path":["/","home","botahamec","Projects","happylock","src","lockable.rs"],"content":"use std::mem::MaybeUninit;\n\n/// A raw lock type that may be locked and unlocked\n///\n/// # Safety\n///\n/// A deadlock must never occur. The `unlock` method must correctly unlock the\n/// data. The `get_ptrs` method must be implemented correctly. The `Output`\n/// must be unlocked when it is dropped.\n//\n// Why not use a RawRwLock? Because that would be semantically incorrect, and I\n// don't want an INIT or GuardMarker associated item.\n// Originally, RawLock had a sister trait: RawSharableLock. I removed it\n// because it'd be difficult to implement a separate type that takes a\n// different kind of RawLock. But now the Sharable marker trait is needed to\n// indicate if reads can be used.\npub unsafe trait RawLock {\n\t/// Causes all subsequent calls to the `lock` function on this lock to\n\t/// panic. This does not affect anything currently holding the lock.\n\tfn poison(\u0026self);\n\n\t/// Blocks until the lock is acquired\n\t///\n\t/// # Safety\n\t///\n\t/// It is undefined behavior to use this without ownership or mutable\n\t/// access to the [`ThreadKey`], which should last as long as the return\n\t/// value is alive.\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\tunsafe fn raw_write(\u0026self);\n\n\t/// Attempt to lock without blocking.\n\t///\n\t/// Returns `true` if successful, `false` otherwise.\n\t///\n\t/// # Safety\n\t///\n\t/// It is undefined behavior to use this without ownership or mutable\n\t/// access to the [`ThreadKey`], which should last as long as the return\n\t/// value is alive.\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool;\n\n\t/// Releases the lock\n\t///\n\t/// # Safety\n\t///\n\t/// It is undefined behavior to use this if the lock is not acquired\n\tunsafe fn raw_unlock_write(\u0026self);\n\n\t/// Blocks until the data the lock protects can be safely read.\n\t///\n\t/// Some locks, but not all, will allow multiple readers at once. If\n\t/// multiple readers are allowed for a [`Lockable`] type, then the\n\t/// [`Sharable`] marker trait should be implemented.\n\t///\n\t/// # Safety\n\t///\n\t/// It is undefined behavior to use this without ownership or mutable\n\t/// access to the [`ThreadKey`], which should last as long as the return\n\t/// value is alive.\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\tunsafe fn raw_read(\u0026self);\n\n\t// Attempt to read without blocking.\n\t///\n\t/// Returns `true` if successful, `false` otherwise.\n\t///\n\t/// Some locks, but not all, will allow multiple readers at once. If\n\t/// multiple readers are allowed for a [`Lockable`] type, then the\n\t/// [`Sharable`] marker trait should be implemented.\n\t///\n\t/// # Safety\n\t///\n\t/// It is undefined behavior to use this without ownership or mutable\n\t/// access to the [`ThreadKey`], which should last as long as the return\n\t/// value is alive.\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool;\n\n\t/// Releases the lock after calling `read`.\n\t///\n\t/// # Safety\n\t///\n\t/// It is undefined behavior to use this if the read lock is not acquired\n\tunsafe fn raw_unlock_read(\u0026self);\n}\n\n/// A type that may be locked and unlocked.\n///\n/// This trait is usually implemented on collections of [`RawLock`]s. For\n/// example, a `Vec\u003cMutex\u003ci32\u003e\u003e`.\n///\n/// # Safety\n///\n/// Acquiring the locks returned by `get_ptrs` must allow access to the values\n/// returned by `guard`.\n///\n/// Dropping the `Guard` must unlock those same locks.\n///\n/// The order of the resulting list from `get_ptrs` must be deterministic. As\n/// long as the value is not mutated, the references must always be in the same\n/// order.\npub unsafe trait Lockable {\n\t/// The exclusive guard that does not hold a key\n\ttype Guard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\t/// Yields a list of references to the [`RawLock`]s contained within this\n\t/// value.\n\t///\n\t/// These reference locks which must be locked before acquiring a guard,\n\t/// and unlocked when the guard is dropped. The order of the resulting list\n\t/// is deterministic. As long as the value is not mutated, the references\n\t/// will always be in the same order.\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e);\n\n\t/// Returns a guard that can be used to access the underlying data mutably.\n\t///\n\t/// # Safety\n\t///\n\t/// All locks given by calling [`Lockable::get_ptrs`] must be locked\n\t/// exclusively before calling this function. The locks must not be\n\t/// unlocked until this guard is dropped.\n\t#[must_use]\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e;\n\n\t#[must_use]\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e;\n}\n\n/// Allows a lock to be accessed by multiple readers.\n///\n/// # Safety\n///\n/// Acquiring shared access to the locks returned by `get_ptrs` must allow\n/// shared access to the values returned by `read_guard`.\n///\n/// Dropping the `ReadGuard` must unlock those same locks.\npub unsafe trait Sharable: Lockable {\n\t/// The shared guard type that does not hold a key\n\ttype ReadGuard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\t/// Returns a guard that can be used to immutably access the underlying\n\t/// data.\n\t///\n\t/// # Safety\n\t///\n\t/// All locks given by calling [`Lockable::get_ptrs`] must be locked using\n\t/// [`RawLock::raw_read`] before calling this function. The locks must not be\n\t/// unlocked until this guard is dropped.\n\t#[must_use]\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e;\n\n\t#[must_use]\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e;\n}\n\n/// A type that may be locked and unlocked, and is known to be the only valid\n/// instance of the lock.\n///\n/// # Safety\n///\n/// There must not be any two values which can unlock the value at the same\n/// time, i.e., this must either be an owned value or a mutable reference.\npub unsafe trait OwnedLockable: Lockable {}\n\n/// A trait which indicates that `into_inner` is a valid operation for a\n/// [`Lockable`].\n///\n/// This is used for types like [`Poisonable`] to access the inner value of a\n/// lock. [`Poisonable::into_inner`] calls [`LockableIntoInner::into_inner`] to\n/// return a mutable reference of the inner value. This isn't implemented for\n/// some `Lockable`s, such as `\u0026[T]`.\n///\n/// [`Poisonable`]: `crate::Poisonable`\n/// [`Poisonable::into_inner`]: `crate::poisonable::Poisonable::into_inner`\npub trait LockableIntoInner: Lockable {\n\t/// The inner type that is behind the lock\n\ttype Inner;\n\n\t/// Consumes the lock, returning the underlying the lock.\n\tfn into_inner(self) -\u003e Self::Inner;\n}\n\n/// A trait which indicates that `as_mut` is a valid operation for a\n/// [`Lockable`].\n///\n/// This is used for types like [`Poisonable`] to access the inner value of a\n/// lock. [`Poisonable::get_mut`] calls [`LockableGetMut::get_mut`] to return a\n/// mutable reference of the inner value. This isn't implemented for some\n/// `Lockable`s, such as `\u0026[T]`.\n///\n/// [`Poisonable`]: `crate::Poisonable`\n/// [`Poisonable::get_mut`]: `crate::poisonable::Poisonable::get_mut`\npub trait LockableGetMut: Lockable {\n\t/// The inner type that is behind the lock\n\ttype Inner\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\t/// Returns a mutable reference to the underlying data.\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e;\n}\n\nunsafe impl\u003cT: Lockable\u003e Lockable for \u0026T {\n\ttype Guard\u003c'g\u003e\n\t\t= T::Guard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= T::DataMut\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\t(*self).get_ptrs(ptrs);\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\t(*self).guard()\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\t(*self).data_mut()\n\t}\n}\n\nunsafe impl\u003cT: Sharable\u003e Sharable for \u0026T {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= T::ReadGuard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= T::DataRef\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\t(*self).read_guard()\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\t(*self).data_ref()\n\t}\n}\n\nunsafe impl\u003cT: Lockable\u003e Lockable for \u0026mut T {\n\ttype Guard\u003c'g\u003e\n\t\t= T::Guard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= T::DataMut\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\t(**self).get_ptrs(ptrs)\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\t(**self).guard()\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\t(**self).data_mut()\n\t}\n}\n\nimpl\u003cT: LockableGetMut\u003e LockableGetMut for \u0026mut T {\n\ttype Inner\u003c'a\u003e\n\t\t= T::Inner\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\t(*self).get_mut()\n\t}\n}\n\nunsafe impl\u003cT: Sharable\u003e Sharable for \u0026mut T {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= T::ReadGuard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= T::DataRef\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\t(**self).read_guard()\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\t(**self).data_ref()\n\t}\n}\n\nunsafe impl\u003cT: OwnedLockable\u003e OwnedLockable for \u0026mut T {}\n\n/// Implements `Lockable`, `Sharable`, and `OwnedLockable` for tuples\n/// ex: `tuple_impls!(A B C, 0 1 2);`\nmacro_rules! tuple_impls {\n\t($($generic:ident)*, $($value:tt)*) =\u003e {\n\t\tunsafe impl\u003c$($generic: Lockable,)*\u003e Lockable for ($($generic,)*) {\n\t\t\ttype Guard\u003c'g\u003e = ($($generic::Guard\u003c'g\u003e,)*) where Self: 'g;\n\n\t\t\ttype DataMut\u003c'a\u003e = ($($generic::DataMut\u003c'a\u003e,)*) where Self: 'a;\n\n\t\t\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\t\t\t$(self.$value.get_ptrs(ptrs));*\n\t\t\t}\n\n\t\t\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\t\t\t// It's weird that this works\n\t\t\t\t// I don't think any other way of doing it compiles\n\t\t\t\t($(self.$value.guard(),)*)\n\t\t\t}\n\n\t\t\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\t\t\t($(self.$value.data_mut(),)*)\n\t\t\t}\n\t\t}\n\n\t\timpl\u003c$($generic: LockableGetMut,)*\u003e LockableGetMut for ($($generic,)*) {\n\t\t\ttype Inner\u003c'a\u003e = ($($generic::Inner\u003c'a\u003e,)*) where Self: 'a;\n\n\t\t\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\t\t\t($(self.$value.get_mut(),)*)\n\t\t\t}\n\t\t}\n\n\t\timpl\u003c$($generic: LockableIntoInner,)*\u003e LockableIntoInner for ($($generic,)*) {\n\t\t\ttype Inner = ($($generic::Inner,)*);\n\n\t\t\tfn into_inner(self) -\u003e Self::Inner {\n\t\t\t\t($(self.$value.into_inner(),)*)\n\t\t\t}\n\t\t}\n\n\t\tunsafe impl\u003c$($generic: Sharable,)*\u003e Sharable for ($($generic,)*) {\n\t\t\ttype ReadGuard\u003c'g\u003e = ($($generic::ReadGuard\u003c'g\u003e,)*) where Self: 'g;\n\n\t\t\ttype DataRef\u003c'a\u003e = ($($generic::DataRef\u003c'a\u003e,)*) where Self: 'a;\n\n\t\t\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\t\t\t($(self.$value.read_guard(),)*)\n\t\t\t}\n\n\t\t\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\t\t\t($(self.$value.data_ref(),)*)\n\t\t\t}\n\t\t}\n\n\t\tunsafe impl\u003c$($generic: OwnedLockable,)*\u003e OwnedLockable for ($($generic,)*) {}\n\t};\n}\n\ntuple_impls!(A, 0);\ntuple_impls!(A B, 0 1);\ntuple_impls!(A B C, 0 1 2);\ntuple_impls!(A B C D, 0 1 2 3);\ntuple_impls!(A B C D E, 0 1 2 3 4);\ntuple_impls!(A B C D E F, 0 1 2 3 4 5);\ntuple_impls!(A B C D E F G, 0 1 2 3 4 5 6);\n\nunsafe impl\u003cT: Lockable, const N: usize\u003e Lockable for [T; N] {\n\ttype Guard\u003c'g\u003e\n\t\t= [T::Guard\u003c'g\u003e; N]\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= [T::DataMut\u003c'a\u003e; N]\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tfor lock in self {\n\t\t\tlock.get_ptrs(ptrs);\n\t\t}\n\t}\n\n\tunsafe fn guard\u003c'g\u003e(\u0026'g self) -\u003e Self::Guard\u003c'g\u003e {\n\t\t// The MaybeInit helper functions for arrays aren't stable yet, so\n\t\t// we'll just have to implement it ourselves\n\t\tlet mut guards = MaybeUninit::\u003c[MaybeUninit\u003cT::Guard\u003c'g\u003e\u003e; N]\u003e::uninit().assume_init();\n\t\tfor i in 0..N {\n\t\t\tguards[i].write(self[i].guard());\n\t\t}\n\n\t\tguards.map(|g| g.assume_init())\n\t}\n\n\tunsafe fn data_mut\u003c'a\u003e(\u0026'a self) -\u003e Self::DataMut\u003c'a\u003e {\n\t\tlet mut guards = MaybeUninit::\u003c[MaybeUninit\u003cT::DataMut\u003c'a\u003e\u003e; N]\u003e::uninit().assume_init();\n\t\tfor i in 0..N {\n\t\t\tguards[i].write(self[i].data_mut());\n\t\t}\n\n\t\tguards.map(|g| g.assume_init())\n\t}\n}\n\nimpl\u003cT: LockableGetMut, const N: usize\u003e LockableGetMut for [T; N] {\n\ttype Inner\u003c'a\u003e\n\t\t= [T::Inner\u003c'a\u003e; N]\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tunsafe {\n\t\t\tlet mut guards = MaybeUninit::\u003c[MaybeUninit\u003cT::Inner\u003c'_\u003e\u003e; N]\u003e::uninit().assume_init();\n\t\t\tfor (i, lock) in self.iter_mut().enumerate() {\n\t\t\t\tguards[i].write(lock.get_mut());\n\t\t\t}\n\n\t\t\tguards.map(|g| g.assume_init())\n\t\t}\n\t}\n}\n\nimpl\u003cT: LockableIntoInner, const N: usize\u003e LockableIntoInner for [T; N] {\n\ttype Inner = [T::Inner; N];\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tunsafe {\n\t\t\tlet mut guards = MaybeUninit::\u003c[MaybeUninit\u003cT::Inner\u003e; N]\u003e::uninit().assume_init();\n\t\t\tfor (i, lock) in self.into_iter().enumerate() {\n\t\t\t\tguards[i].write(lock.into_inner());\n\t\t\t}\n\n\t\t\tguards.map(|g| g.assume_init())\n\t\t}\n\t}\n}\n\nunsafe impl\u003cT: Sharable, const N: usize\u003e Sharable for [T; N] {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= [T::ReadGuard\u003c'g\u003e; N]\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= [T::DataRef\u003c'a\u003e; N]\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard\u003c'g\u003e(\u0026'g self) -\u003e Self::ReadGuard\u003c'g\u003e {\n\t\tlet mut guards = MaybeUninit::\u003c[MaybeUninit\u003cT::ReadGuard\u003c'g\u003e\u003e; N]\u003e::uninit().assume_init();\n\t\tfor i in 0..N {\n\t\t\tguards[i].write(self[i].read_guard());\n\t\t}\n\n\t\tguards.map(|g| g.assume_init())\n\t}\n\n\tunsafe fn data_ref\u003c'a\u003e(\u0026'a self) -\u003e Self::DataRef\u003c'a\u003e {\n\t\tlet mut guards = MaybeUninit::\u003c[MaybeUninit\u003cT::DataRef\u003c'a\u003e\u003e; N]\u003e::uninit().assume_init();\n\t\tfor i in 0..N {\n\t\t\tguards[i].write(self[i].data_ref());\n\t\t}\n\n\t\tguards.map(|g| g.assume_init())\n\t}\n}\n\nunsafe impl\u003cT: OwnedLockable, const N: usize\u003e OwnedLockable for [T; N] {}\n\nunsafe impl\u003cT: Lockable\u003e Lockable for Box\u003c[T]\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= Box\u003c[T::Guard\u003c'g\u003e]\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= Box\u003c[T::DataMut\u003c'a\u003e]\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tfor lock in self {\n\t\t\tlock.get_ptrs(ptrs);\n\t\t}\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tself.iter().map(|lock| lock.guard()).collect()\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.iter().map(|lock| lock.data_mut()).collect()\n\t}\n}\n\nimpl\u003cT: LockableGetMut + 'static\u003e LockableGetMut for Box\u003c[T]\u003e {\n\ttype Inner\u003c'a\u003e\n\t\t= Box\u003c[T::Inner\u003c'a\u003e]\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tself.iter_mut().map(LockableGetMut::get_mut).collect()\n\t}\n}\n\nunsafe impl\u003cT: Sharable\u003e Sharable for Box\u003c[T]\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= Box\u003c[T::ReadGuard\u003c'g\u003e]\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= Box\u003c[T::DataRef\u003c'a\u003e]\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tself.iter().map(|lock| lock.read_guard()).collect()\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tself.iter().map(|lock| lock.data_ref()).collect()\n\t}\n}\n\nunsafe impl\u003cT: Sharable\u003e Sharable for Vec\u003cT\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= Box\u003c[T::ReadGuard\u003c'g\u003e]\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= Box\u003c[T::DataRef\u003c'a\u003e]\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tself.iter().map(|lock| lock.read_guard()).collect()\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tself.iter().map(|lock| lock.data_ref()).collect()\n\t}\n}\n\nunsafe impl\u003cT: OwnedLockable\u003e OwnedLockable for Box\u003c[T]\u003e {}\n\nunsafe impl\u003cT: Lockable\u003e Lockable for Vec\u003cT\u003e {\n\t// There's no reason why I'd ever want to extend a list of lock guards\n\ttype Guard\u003c'g\u003e\n\t\t= Box\u003c[T::Guard\u003c'g\u003e]\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= Box\u003c[T::DataMut\u003c'a\u003e]\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tfor lock in self {\n\t\t\tlock.get_ptrs(ptrs);\n\t\t}\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tself.iter().map(|lock| lock.guard()).collect()\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.iter().map(|lock| lock.data_mut()).collect()\n\t}\n}\n\n// I'd make a generic impl\u003cT: Lockable, I: IntoIterator\u003cItem=T\u003e\u003e Lockable for I\n// but I think that'd require sealing up this trait\n\n// TODO: using edition 2024, impl LockableIntoInner for Box\u003c[T]\u003e\n\nimpl\u003cT: LockableGetMut + 'static\u003e LockableGetMut for Vec\u003cT\u003e {\n\ttype Inner\u003c'a\u003e\n\t\t= Box\u003c[T::Inner\u003c'a\u003e]\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tself.iter_mut().map(LockableGetMut::get_mut).collect()\n\t}\n}\n\nimpl\u003cT: LockableIntoInner\u003e LockableIntoInner for Vec\u003cT\u003e {\n\ttype Inner = Box\u003c[T::Inner]\u003e;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tself.into_iter()\n\t\t\t.map(LockableIntoInner::into_inner)\n\t\t\t.collect()\n\t}\n}\n\nunsafe impl\u003cT: OwnedLockable\u003e OwnedLockable for Vec\u003cT\u003e {}\n\n#[cfg(test)]\nmod tests {\n\tuse super::*;\n\tuse crate::{LockCollection, Mutex, RwLock, ThreadKey};\n\n\t#[test]\n\tfn mut_ref_get_ptrs() {\n\t\tlet mut rwlock = RwLock::new(5);\n\t\tlet mutref = \u0026mut rwlock;\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tmutref.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tassert!(std::ptr::addr_eq(lock_ptrs[0], mutref));\n\t}\n\n\t#[test]\n\tfn array_get_ptrs_empty() {\n\t\tlet locks: [Mutex\u003c()\u003e; 0] = [];\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert!(lock_ptrs.is_empty());\n\t}\n\n\t#[test]\n\tfn array_get_ptrs_length_one() {\n\t\tlet locks: [Mutex\u003ci32\u003e; 1] = [Mutex::new(1)];\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[0], locks[0].raw())) }\n\t}\n\n\t#[test]\n\tfn array_get_ptrs_length_two() {\n\t\tlet locks: [Mutex\u003ci32\u003e; 2] = [Mutex::new(1), Mutex::new(2)];\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 2);\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[0], locks[0].raw())) }\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[1], locks[1].raw())) }\n\t}\n\n\t#[test]\n\tfn vec_get_ptrs_empty() {\n\t\tlet locks: Vec\u003cMutex\u003c()\u003e\u003e = Vec::new();\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert!(lock_ptrs.is_empty());\n\t}\n\n\t#[test]\n\tfn vec_get_ptrs_length_one() {\n\t\tlet locks: Vec\u003cMutex\u003ci32\u003e\u003e = vec![Mutex::new(1)];\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[0], locks[0].raw())) }\n\t}\n\n\t#[test]\n\tfn vec_get_ptrs_length_two() {\n\t\tlet locks: Vec\u003cMutex\u003ci32\u003e\u003e = vec![Mutex::new(1), Mutex::new(2)];\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 2);\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[0], locks[0].raw())) }\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[1], locks[1].raw())) }\n\t}\n\n\t#[test]\n\tfn vec_as_mut() {\n\t\tlet mut locks: Vec\u003cMutex\u003ci32\u003e\u003e = vec![Mutex::new(1), Mutex::new(2)];\n\t\tlet lock_ptrs = LockableGetMut::get_mut(\u0026mut locks);\n\n\t\tassert_eq!(lock_ptrs.len(), 2);\n\t\tassert_eq!(*lock_ptrs[0], 1);\n\t\tassert_eq!(*lock_ptrs[1], 2);\n\t}\n\n\t#[test]\n\tfn vec_into_inner() {\n\t\tlet locks: Vec\u003cMutex\u003ci32\u003e\u003e = vec![Mutex::new(1), Mutex::new(2)];\n\t\tlet lock_ptrs = LockableIntoInner::into_inner(locks);\n\n\t\tassert_eq!(lock_ptrs.len(), 2);\n\t\tassert_eq!(lock_ptrs[0], 1);\n\t\tassert_eq!(lock_ptrs[1], 2);\n\t}\n\n\t#[test]\n\tfn vec_guard_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = vec![RwLock::new(1), RwLock::new(2)];\n\t\tlet collection = LockCollection::new(locks);\n\n\t\tlet mut guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], 1);\n\t\tassert_eq!(*guard[1], 2);\n\t\t*guard[0] = 3;\n\n\t\tlet key = LockCollection::\u003cVec\u003cRwLock\u003c_\u003e\u003e\u003e::unlock(guard);\n\t\tlet guard = collection.read(key);\n\t\tassert_eq!(*guard[0], 3);\n\t\tassert_eq!(*guard[1], 2);\n\t}\n\n\t#[test]\n\tfn vec_data_mut() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = vec![Mutex::new(1), Mutex::new(2)];\n\t\tlet collection = LockCollection::new(mutexes);\n\t\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 1);\n\t\t\tassert_eq!(*guard[1], 2);\n\t\t\t*guard[0] = 3;\n\t\t});\n\n\t\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 3);\n\t\t\tassert_eq!(*guard[1], 2);\n\t\t})\n\t}\n\n\t#[test]\n\tfn vec_data_ref() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = vec![RwLock::new(1), RwLock::new(2)];\n\t\tlet collection = LockCollection::new(mutexes);\n\t\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 1);\n\t\t\tassert_eq!(*guard[1], 2);\n\t\t\t*guard[0] = 3;\n\t\t});\n\n\t\tcollection.scoped_read(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 3);\n\t\t\tassert_eq!(*guard[1], 2);\n\t\t})\n\t}\n\n\t#[test]\n\tfn box_get_ptrs_empty() {\n\t\tlet locks: Box\u003c[Mutex\u003c()\u003e]\u003e = Box::from([]);\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert!(lock_ptrs.is_empty());\n\t}\n\n\t#[test]\n\tfn box_get_ptrs_length_one() {\n\t\tlet locks: Box\u003c[Mutex\u003ci32\u003e]\u003e = vec![Mutex::new(1)].into_boxed_slice();\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[0], locks[0].raw())) }\n\t}\n\n\t#[test]\n\tfn box_get_ptrs_length_two() {\n\t\tlet locks: Box\u003c[Mutex\u003ci32\u003e]\u003e = vec![Mutex::new(1), Mutex::new(2)].into_boxed_slice();\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 2);\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[0], locks[0].raw())) }\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[1], locks[1].raw())) }\n\t}\n\n\t#[test]\n\tfn box_as_mut() {\n\t\tlet mut locks: Box\u003c[Mutex\u003ci32\u003e]\u003e = vec![Mutex::new(1), Mutex::new(2)].into_boxed_slice();\n\t\tlet lock_ptrs = LockableGetMut::get_mut(\u0026mut locks);\n\n\t\tassert_eq!(lock_ptrs.len(), 2);\n\t\tassert_eq!(*lock_ptrs[0], 1);\n\t\tassert_eq!(*lock_ptrs[1], 2);\n\t}\n\n\t#[test]\n\tfn box_guard_mut() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet x = [Mutex::new(1), Mutex::new(2)];\n\t\tlet collection: LockCollection\u003cBox\u003c[Mutex\u003c_\u003e]\u003e\u003e = LockCollection::new(Box::new(x));\n\n\t\tlet mut guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], 1);\n\t\tassert_eq!(*guard[1], 2);\n\t\t*guard[0] = 3;\n\n\t\tlet key = LockCollection::\u003cBox\u003c[Mutex\u003c_\u003e]\u003e\u003e::unlock(guard);\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], 3);\n\t\tassert_eq!(*guard[1], 2);\n\t}\n\n\t#[test]\n\tfn box_data_mut() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = vec![Mutex::new(1), Mutex::new(2)].into_boxed_slice();\n\t\tlet collection = LockCollection::new(mutexes);\n\t\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 1);\n\t\t\tassert_eq!(*guard[1], 2);\n\t\t\t*guard[0] = 3;\n\t\t});\n\n\t\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 3);\n\t\t\tassert_eq!(*guard[1], 2);\n\t\t});\n\t}\n\n\t#[test]\n\tfn box_guard_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = [RwLock::new(1), RwLock::new(2)];\n\t\tlet collection: LockCollection\u003cBox\u003c[RwLock\u003c_\u003e]\u003e\u003e = LockCollection::new(Box::new(locks));\n\n\t\tlet mut guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], 1);\n\t\tassert_eq!(*guard[1], 2);\n\t\t*guard[0] = 3;\n\n\t\tlet key = LockCollection::\u003cBox\u003c[RwLock\u003c_\u003e]\u003e\u003e::unlock(guard);\n\t\tlet guard = collection.read(key);\n\t\tassert_eq!(*guard[0], 3);\n\t\tassert_eq!(*guard[1], 2);\n\t}\n\n\t#[test]\n\tfn box_data_ref() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = vec![RwLock::new(1), RwLock::new(2)].into_boxed_slice();\n\t\tlet collection = LockCollection::new(mutexes);\n\t\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 1);\n\t\t\tassert_eq!(*guard[1], 2);\n\t\t\t*guard[0] = 3;\n\t\t});\n\n\t\tcollection.scoped_read(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 3);\n\t\t\tassert_eq!(*guard[1], 2);\n\t\t});\n\t}\n}\n","traces":[{"line":232,"address":[215504,215472,215440],"length":1,"stats":{"Line":43}},{"line":233,"address":[247342,247310,247406,247374],"length":1,"stats":{"Line":45}},{"line":236,"address":[962544],"length":1,"stats":{"Line":8}},{"line":237,"address":[174309,174325],"length":1,"stats":{"Line":9}},{"line":240,"address":[216880],"length":1,"stats":{"Line":2}},{"line":241,"address":[],"length":0,"stats":{"Line":2}},{"line":256,"address":[247456,247424,247440],"length":1,"stats":{"Line":3}},{"line":257,"address":[962629,962565,962593],"length":1,"stats":{"Line":3}},{"line":260,"address":[],"length":0,"stats":{"Line":0}},{"line":261,"address":[],"length":0,"stats":{"Line":0}},{"line":276,"address":[],"length":0,"stats":{"Line":1}},{"line":277,"address":[],"length":0,"stats":{"Line":1}},{"line":280,"address":[],"length":0,"stats":{"Line":0}},{"line":281,"address":[],"length":0,"stats":{"Line":0}},{"line":284,"address":[],"length":0,"stats":{"Line":0}},{"line":285,"address":[],"length":0,"stats":{"Line":0}},{"line":295,"address":[],"length":0,"stats":{"Line":0}},{"line":296,"address":[],"length":0,"stats":{"Line":0}},{"line":311,"address":[],"length":0,"stats":{"Line":0}},{"line":312,"address":[],"length":0,"stats":{"Line":0}},{"line":315,"address":[],"length":0,"stats":{"Line":0}},{"line":316,"address":[],"length":0,"stats":{"Line":0}},{"line":331,"address":[142016],"length":1,"stats":{"Line":14}},{"line":332,"address":[215592],"length":1,"stats":{"Line":18}},{"line":335,"address":[163071,162880],"length":1,"stats":{"Line":7}},{"line":338,"address":[971838,971726],"length":1,"stats":{"Line":8}},{"line":357,"address":[972272,972619],"length":1,"stats":{"Line":4}},{"line":358,"address":[1293020,1292598,1292934,1293254,1293372,1292703],"length":1,"stats":{"Line":8}},{"line":367,"address":[972048,971936,972024,972136,972160,972248],"length":1,"stats":{"Line":2}},{"line":368,"address":[247584],"length":1,"stats":{"Line":2}},{"line":399,"address":[1282288,1282736,1282960,1282400,1282176,1282624,1282512,1282848],"length":1,"stats":{"Line":18}},{"line":400,"address":[217906,217794,217859,217971],"length":1,"stats":{"Line":34}},{"line":401,"address":[1282605,1283053,1282829,1282269,1282493,1282717,1282381,1282941],"length":1,"stats":{"Line":16}},{"line":405,"address":[1283072,1283440,1283808,1284672,1284256],"length":1,"stats":{"Line":8}},{"line":408,"address":[],"length":0,"stats":{"Line":2}},{"line":409,"address":[963090,963458,963560,963820,963192,963758],"length":1,"stats":{"Line":17}},{"line":410,"address":[1283642,1284874,1283274,1284506,1283404,1283772,1284214,1285004,1284636,1284084],"length":1,"stats":{"Line":16}},{"line":413,"address":[],"length":0,"stats":{"Line":25}},{"line":416,"address":[1285040],"length":1,"stats":{"Line":3}},{"line":417,"address":[],"length":0,"stats":{"Line":0}},{"line":418,"address":[964066,964168],"length":1,"stats":{"Line":6}},{"line":419,"address":[1285242,1285372],"length":1,"stats":{"Line":6}},{"line":422,"address":[189408,189431,189440,189463],"length":1,"stats":{"Line":9}},{"line":432,"address":[964352],"length":1,"stats":{"Line":2}},{"line":434,"address":[],"length":0,"stats":{"Line":0}},{"line":435,"address":[1285498,1285690],"length":1,"stats":{"Line":4}},{"line":436,"address":[964676,964762],"length":1,"stats":{"Line":4}},{"line":439,"address":[1285638],"length":1,"stats":{"Line":6}},{"line":447,"address":[],"length":0,"stats":{"Line":1}},{"line":449,"address":[1285878],"length":1,"stats":{"Line":1}},{"line":450,"address":[],"length":0,"stats":{"Line":4}},{"line":451,"address":[1286350,1286288],"length":1,"stats":{"Line":2}},{"line":454,"address":[1286304],"length":1,"stats":{"Line":3}},{"line":470,"address":[],"length":0,"stats":{"Line":4}},{"line":471,"address":[],"length":0,"stats":{"Line":0}},{"line":472,"address":[965490,965592,965122,964814,965224,964876],"length":1,"stats":{"Line":7}},{"line":473,"address":[],"length":0,"stats":{"Line":6}},{"line":476,"address":[606199,606144,606167,606112,606135,606176],"length":1,"stats":{"Line":10}},{"line":479,"address":[],"length":0,"stats":{"Line":1}},{"line":480,"address":[],"length":0,"stats":{"Line":0}},{"line":481,"address":[965960,965858],"length":1,"stats":{"Line":2}},{"line":482,"address":[965978,966108],"length":1,"stats":{"Line":3}},{"line":485,"address":[965917],"length":1,"stats":{"Line":3}},{"line":502,"address":[],"length":0,"stats":{"Line":3}},{"line":503,"address":[],"length":0,"stats":{"Line":5}},{"line":504,"address":[994941,995053,995165],"length":1,"stats":{"Line":2}},{"line":508,"address":[],"length":0,"stats":{"Line":2}},{"line":509,"address":[995240,995192],"length":1,"stats":{"Line":6}},{"line":512,"address":[],"length":0,"stats":{"Line":2}},{"line":513,"address":[],"length":0,"stats":{"Line":6}},{"line":523,"address":[995376],"length":1,"stats":{"Line":1}},{"line":524,"address":[],"length":0,"stats":{"Line":1}},{"line":539,"address":[],"length":0,"stats":{"Line":1}},{"line":540,"address":[],"length":0,"stats":{"Line":3}},{"line":543,"address":[995472],"length":1,"stats":{"Line":1}},{"line":544,"address":[],"length":0,"stats":{"Line":3}},{"line":559,"address":[],"length":0,"stats":{"Line":1}},{"line":560,"address":[532597],"length":1,"stats":{"Line":3}},{"line":563,"address":[],"length":0,"stats":{"Line":1}},{"line":564,"address":[532645],"length":1,"stats":{"Line":3}},{"line":582,"address":[],"length":0,"stats":{"Line":5}},{"line":583,"address":[],"length":0,"stats":{"Line":11}},{"line":584,"address":[],"length":0,"stats":{"Line":4}},{"line":588,"address":[],"length":0,"stats":{"Line":2}},{"line":589,"address":[533189,533141],"length":1,"stats":{"Line":6}},{"line":592,"address":[],"length":0,"stats":{"Line":2}},{"line":593,"address":[],"length":0,"stats":{"Line":6}},{"line":608,"address":[],"length":0,"stats":{"Line":2}},{"line":609,"address":[],"length":0,"stats":{"Line":2}},{"line":616,"address":[],"length":0,"stats":{"Line":1}},{"line":617,"address":[],"length":0,"stats":{"Line":1}},{"line":618,"address":[],"length":0,"stats":{"Line":0}}],"covered":75,"coverable":92},{"path":["/","home","botahamec","Projects","happylock","src","mutex","guard.rs"],"content":"use std::fmt::{Debug, Display};\nuse std::hash::Hash;\nuse std::marker::PhantomData;\nuse std::ops::{Deref, DerefMut};\n\nuse lock_api::RawMutex;\n\nuse crate::lockable::RawLock;\nuse crate::ThreadKey;\n\nuse super::{Mutex, MutexGuard, MutexRef};\n\n// These impls make things slightly easier because now you can use\n// `println!(\"{guard}\")` instead of `println!(\"{}\", *guard)`\n\n#[mutants::skip] // hashing involves RNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Hash + ?Sized, R: RawMutex\u003e Hash for MutexRef\u003c'_, T, R\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.deref().hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug + ?Sized, R: RawMutex\u003e Debug for MutexRef\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: Display + ?Sized, R: RawMutex\u003e Display for MutexRef\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e Drop for MutexRef\u003c'_, T, R\u003e {\n\tfn drop(\u0026mut self) {\n\t\t// safety: this guard is being destroyed, so the data cannot be\n\t\t// accessed without locking again\n\t\tunsafe { self.0.raw_unlock_write() }\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e Deref for MutexRef\u003c'_, T, R\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t// safety: this is the only type that can use `value`, and there's\n\t\t// a reference to this type, so there cannot be any mutable\n\t\t// references to this value.\n\t\tunsafe { \u0026*self.0.data.get() }\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e DerefMut for MutexRef\u003c'_, T, R\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t// safety: this is the only type that can use `value`, and we have a\n\t\t// mutable reference to this type, so there cannot be any other\n\t\t// references to this value.\n\t\tunsafe { \u0026mut *self.0.data.get() }\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e AsRef\u003cT\u003e for MutexRef\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e AsMut\u003cT\u003e for MutexRef\u003c'_, T, R\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself\n\t}\n}\n\nimpl\u003c'a, T: ?Sized, R: RawMutex\u003e MutexRef\u003c'a, T, R\u003e {\n\t/// Creates a reference to the underlying data of a mutex without\n\t/// attempting to lock it or take ownership of the key. But it's also quite\n\t/// dangerous to drop.\n\tpub(crate) const unsafe fn new(mutex: \u0026'a Mutex\u003cT, R\u003e) -\u003e Self {\n\t\tSelf(mutex, PhantomData)\n\t}\n}\n\n// it's kinda annoying to re-implement some of this stuff on guards\n// there's nothing i can do about that\n\n#[mutants::skip] // hashing involves RNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Hash + ?Sized, R: RawMutex\u003e Hash for MutexGuard\u003c'_, T, R\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.deref().hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug + ?Sized, R: RawMutex\u003e Debug for MutexGuard\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: Display + ?Sized, R: RawMutex\u003e Display for MutexGuard\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e Deref for MutexGuard\u003c'_, T, R\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t\u0026self.mutex\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e DerefMut for MutexGuard\u003c'_, T, R\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t\u0026mut self.mutex\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e AsRef\u003cT\u003e for MutexGuard\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e AsMut\u003cT\u003e for MutexGuard\u003c'_, T, R\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself\n\t}\n}\n\nimpl\u003c'a, T: ?Sized, R: RawMutex\u003e MutexGuard\u003c'a, T, R\u003e {\n\t/// Create a guard to the given mutex. Undefined if multiple guards to the\n\t/// same mutex exist at once.\n\t#[must_use]\n\tpub(super) const unsafe fn new(mutex: \u0026'a Mutex\u003cT, R\u003e, thread_key: ThreadKey) -\u003e Self {\n\t\tSelf {\n\t\t\tmutex: MutexRef(mutex, PhantomData),\n\t\t\tthread_key,\n\t\t}\n\t}\n}\n\nunsafe impl\u003cT: ?Sized + Sync, R: RawMutex + Sync\u003e Sync for MutexRef\u003c'_, T, R\u003e {}\n","traces":[{"line":33,"address":[1286624],"length":1,"stats":{"Line":1}},{"line":34,"address":[],"length":0,"stats":{"Line":1}},{"line":39,"address":[153792],"length":1,"stats":{"Line":6}},{"line":42,"address":[153797],"length":1,"stats":{"Line":7}},{"line":49,"address":[1286704,1286672],"length":1,"stats":{"Line":2}},{"line":53,"address":[163269],"length":1,"stats":{"Line":3}},{"line":58,"address":[1286736,1286768],"length":1,"stats":{"Line":3}},{"line":62,"address":[1286741,1286773],"length":1,"stats":{"Line":4}},{"line":67,"address":[],"length":0,"stats":{"Line":2}},{"line":68,"address":[],"length":0,"stats":{"Line":2}},{"line":73,"address":[],"length":0,"stats":{"Line":1}},{"line":74,"address":[],"length":0,"stats":{"Line":1}},{"line":82,"address":[142112,142096],"length":1,"stats":{"Line":6}},{"line":83,"address":[],"length":0,"stats":{"Line":0}},{"line":107,"address":[],"length":0,"stats":{"Line":1}},{"line":108,"address":[],"length":0,"stats":{"Line":1}},{"line":115,"address":[],"length":0,"stats":{"Line":2}},{"line":116,"address":[],"length":0,"stats":{"Line":3}},{"line":121,"address":[218048],"length":1,"stats":{"Line":2}},{"line":122,"address":[218053],"length":1,"stats":{"Line":2}},{"line":127,"address":[1286976],"length":1,"stats":{"Line":1}},{"line":128,"address":[],"length":0,"stats":{"Line":1}},{"line":133,"address":[],"length":0,"stats":{"Line":1}},{"line":134,"address":[],"length":0,"stats":{"Line":1}},{"line":142,"address":[140544],"length":1,"stats":{"Line":4}},{"line":144,"address":[],"length":0,"stats":{"Line":0}}],"covered":24,"coverable":26},{"path":["/","home","botahamec","Projects","happylock","src","mutex","mutex.rs"],"content":"use std::cell::UnsafeCell;\nuse std::fmt::Debug;\nuse std::marker::PhantomData;\nuse std::panic::AssertUnwindSafe;\n\nuse lock_api::RawMutex;\n\nuse crate::collection::utils;\nuse crate::handle_unwind::handle_unwind;\nuse crate::lockable::{Lockable, LockableGetMut, LockableIntoInner, OwnedLockable, RawLock};\nuse crate::poisonable::PoisonFlag;\nuse crate::{Keyable, ThreadKey};\n\nuse super::{Mutex, MutexGuard, MutexRef};\n\nunsafe impl\u003cT: ?Sized, R: RawMutex\u003e RawLock for Mutex\u003cT, R\u003e {\n\tfn poison(\u0026self) {\n\t\tself.poison.poison();\n\t}\n\n\tunsafe fn raw_write(\u0026self) {\n\t\tassert!(!self.poison.is_poisoned(), \"The mutex has been killed\");\n\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.lock(), || self.poison())\n\t}\n\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool {\n\t\tif self.poison.is_poisoned() {\n\t\t\treturn false;\n\t\t}\n\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.try_lock(), || self.poison())\n\t}\n\n\tunsafe fn raw_unlock_write(\u0026self) {\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.unlock(), || self.poison())\n\t}\n\n\t// this is the closest thing to a read we can get, but Sharable isn't\n\t// implemented for this\n\t#[mutants::skip]\n\t#[cfg(not(tarpaulin_include))]\n\tunsafe fn raw_read(\u0026self) {\n\t\tself.raw_write()\n\t}\n\n\t#[mutants::skip]\n\t#[cfg(not(tarpaulin_include))]\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tself.raw_try_write()\n\t}\n\n\t#[mutants::skip]\n\t#[cfg(not(tarpaulin_include))]\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tself.raw_unlock_write()\n\t}\n}\n\nunsafe impl\u003cT, R: RawMutex\u003e Lockable for Mutex\u003cT, R\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= MutexRef\u003c'g, T, R\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= \u0026'a mut T\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tptrs.push(self);\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tMutexRef::new(self)\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.data.get().as_mut().unwrap_unchecked()\n\t}\n}\n\nimpl\u003cT: Send, R: RawMutex\u003e LockableIntoInner for Mutex\u003cT, R\u003e {\n\ttype Inner = T;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tself.into_inner()\n\t}\n}\n\nimpl\u003cT: Send, R: RawMutex\u003e LockableGetMut for Mutex\u003cT, R\u003e {\n\ttype Inner\u003c'a\u003e\n\t\t= \u0026'a mut T\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tself.get_mut()\n\t}\n}\n\nunsafe impl\u003cT: Send, R: RawMutex\u003e OwnedLockable for Mutex\u003cT, R\u003e {}\n\nimpl\u003cT, R: RawMutex\u003e Mutex\u003cT, R\u003e {\n\t/// Create a new unlocked `Mutex`.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t///\n\t/// let mutex = Mutex::new(0);\n\t/// ```\n\t#[must_use]\n\tpub const fn new(data: T) -\u003e Self {\n\t\tSelf {\n\t\t\traw: R::INIT,\n\t\t\tpoison: PoisonFlag::new(),\n\t\t\tdata: UnsafeCell::new(data),\n\t\t}\n\t}\n\n\t/// Returns the raw underlying mutex.\n\t///\n\t/// Note that you will most likely need to import the [`RawMutex`] trait\n\t/// from `lock_api` to be able to call functions on the raw mutex.\n\t///\n\t/// # Safety\n\t///\n\t/// This method is unsafe because it allows unlocking a mutex while still\n\t/// holding a reference to a [`MutexGuard`], and locking a mutex without\n\t/// holding the [`ThreadKey`].\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\t#[must_use]\n\tpub const unsafe fn raw(\u0026self) -\u003e \u0026R {\n\t\t\u0026self.raw\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug, R: RawMutex\u003e Debug for Mutex\u003cT, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t// safety: this is just a try lock, and the value is dropped\n\t\t// immediately after, so there's no risk of blocking ourselves\n\t\t// or any other threads\n\t\t// when i implement try_clone this code will become less unsafe\n\t\tif let Some(value) = unsafe { self.try_lock_no_key() } {\n\t\t\tf.debug_struct(\"Mutex\").field(\"data\", \u0026\u0026*value).finish()\n\t\t} else {\n\t\t\tstruct LockedPlaceholder;\n\t\t\timpl Debug for LockedPlaceholder {\n\t\t\t\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t\t\t\tf.write_str(\"\u003clocked\u003e\")\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tf.debug_struct(\"Mutex\")\n\t\t\t\t.field(\"data\", \u0026LockedPlaceholder)\n\t\t\t\t.finish()\n\t\t}\n\t}\n}\n\nimpl\u003cT: Default, R: RawMutex\u003e Default for Mutex\u003cT, R\u003e {\n\tfn default() -\u003e Self {\n\t\tSelf::new(T::default())\n\t}\n}\n\nimpl\u003cT, R: RawMutex\u003e From\u003cT\u003e for Mutex\u003cT, R\u003e {\n\tfn from(value: T) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\n// We don't need a `get_mut` because we don't have mutex poisoning. Hurray!\n// We have it anyway for documentation\nimpl\u003cT: ?Sized, R\u003e AsMut\u003cT\u003e for Mutex\u003cT, R\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself.get_mut()\n\t}\n}\n\nimpl\u003cT, R\u003e Mutex\u003cT, R\u003e {\n\t/// Consumes this mutex, returning the underlying data.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t///\n\t/// let mutex = Mutex::new(0);\n\t/// assert_eq!(mutex.into_inner(), 0);\n\t/// ```\n\t#[must_use]\n\tpub fn into_inner(self) -\u003e T {\n\t\tself.data.into_inner()\n\t}\n}\n\nimpl\u003cT: ?Sized, R\u003e Mutex\u003cT, R\u003e {\n\t/// Returns a mutable reference to the underlying data.\n\t///\n\t/// Since this call borrows `Mutex` mutably, no actual locking is taking\n\t/// place. The mutable borrow statically guarantees that no locks exist.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, Mutex};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut mutex = Mutex::new(0);\n\t/// *mutex.get_mut() = 10;\n\t/// assert_eq!(*mutex.lock(key), 10);\n\t/// ```\n\t#[must_use]\n\tpub fn get_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself.data.get_mut()\n\t}\n}\n\nimpl\u003cT, R: RawMutex\u003e Mutex\u003cT, R\u003e {\n\tpub fn scoped_lock\u003c'a, Ret\u003e(\n\t\t\u0026'a self,\n\t\tkey: impl Keyable,\n\t\tf: impl FnOnce(\u0026'a mut T) -\u003e Ret,\n\t) -\u003e Ret {\n\t\tutils::scoped_write(self, key, f)\n\t}\n\n\tpub fn scoped_try_lock\u003c'a, Key: Keyable, Ret\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl FnOnce(\u0026'a mut T) -\u003e Ret,\n\t) -\u003e Result\u003cRet, Key\u003e {\n\t\tutils::scoped_try_write(self, key, f)\n\t}\n\n\t/// Block the thread until this mutex can be locked, and lock it.\n\t///\n\t/// Upon returning, the thread is the only thread with a lock on the\n\t/// `Mutex`. A [`MutexGuard`] is returned to allow a scoped unlock of this\n\t/// `Mutex`. When the guard is dropped, this `Mutex` will unlock.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::{thread, sync::Arc};\n\t/// use happylock::{Mutex, ThreadKey};\n\t///\n\t/// let mutex = Arc::new(Mutex::new(0));\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t///\n\t/// thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// *c_mutex.lock(key) = 10;\n\t/// }).join().expect(\"thread::spawn failed\");\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// assert_eq!(*mutex.lock(key), 10);\n\t/// ```\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e MutexGuard\u003c'_, T, R\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_write();\n\n\t\t\t// safety: we just locked the mutex\n\t\t\tMutexGuard::new(self, key)\n\t\t}\n\t}\n\n\t/// Attempts to lock the `Mutex` without blocking.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// lock when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If the mutex could not be acquired because it is already locked, then\n\t/// this call will return an error containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::{thread, sync::Arc};\n\t/// use happylock::{Mutex, ThreadKey};\n\t///\n\t/// let mutex = Arc::new(Mutex::new(0));\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t///\n\t/// thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut lock = c_mutex.try_lock(key);\n\t/// if let Ok(mut lock) = lock {\n\t/// *lock = 10;\n\t/// } else {\n\t/// println!(\"try_lock failed\");\n\t/// }\n\t/// }).join().expect(\"thread::spawn failed\");\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// assert_eq!(*mutex.lock(key), 10);\n\t/// ```\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e Result\u003cMutexGuard\u003c'_, T, R\u003e, ThreadKey\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the key to the mutex\n\t\t\tif self.raw_try_write() {\n\t\t\t\t// safety: we just locked the mutex\n\t\t\t\tOk(MutexGuard::new(self, key))\n\t\t\t} else {\n\t\t\t\tErr(key)\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Returns `true` if the mutex is currently locked\n\t#[cfg(test)]\n\tpub(crate) fn is_locked(\u0026self) -\u003e bool {\n\t\tself.raw.is_locked()\n\t}\n\n\t/// Lock without a [`ThreadKey`]. It is undefined behavior to do this without\n\t/// owning the [`ThreadKey`].\n\tpub(crate) unsafe fn try_lock_no_key(\u0026self) -\u003e Option\u003cMutexRef\u003c'_, T, R\u003e\u003e {\n\t\tself.raw_try_write().then_some(MutexRef(self, PhantomData))\n\t}\n\n\t/// Consumes the [`MutexGuard`], and consequently unlocks its `Mutex`.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, Mutex};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mutex = Mutex::new(0);\n\t///\n\t/// let mut guard = mutex.lock(key);\n\t/// *guard += 20;\n\t///\n\t/// let key = Mutex::unlock(guard);\n\t/// ```\n\t#[must_use]\n\tpub fn unlock(guard: MutexGuard\u003c'_, T, R\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.mutex);\n\t\tguard.thread_key\n\t}\n}\n\nunsafe impl\u003cR: RawMutex + Send, T: ?Sized + Send\u003e Send for Mutex\u003cT, R\u003e {}\nunsafe impl\u003cR: RawMutex + Sync, T: ?Sized + Send\u003e Sync for Mutex\u003cT, R\u003e {}\n","traces":[{"line":17,"address":[164096,164064],"length":1,"stats":{"Line":6}},{"line":18,"address":[229573,229541,229525],"length":1,"stats":{"Line":6}},{"line":21,"address":[218384],"length":1,"stats":{"Line":11}},{"line":22,"address":[],"length":0,"stats":{"Line":9}},{"line":25,"address":[218417],"length":1,"stats":{"Line":11}},{"line":26,"address":[141078],"length":1,"stats":{"Line":39}},{"line":29,"address":[163888,163808],"length":1,"stats":{"Line":11}},{"line":30,"address":[163822,163902],"length":1,"stats":{"Line":11}},{"line":31,"address":[229188,229272,229352],"length":1,"stats":{"Line":6}},{"line":35,"address":[218241],"length":1,"stats":{"Line":11}},{"line":36,"address":[163926,163846],"length":1,"stats":{"Line":37}},{"line":39,"address":[218304],"length":1,"stats":{"Line":13}},{"line":41,"address":[140988],"length":1,"stats":{"Line":13}},{"line":42,"address":[991824,991760,991888,992000,991792,991797,991920,991925,991957,992016,992032,991989,991973,992021,991861,991952,991856,991893,992037,991765,991984,992005,991829,991968],"length":1,"stats":{"Line":46}},{"line":77,"address":[143344,143408],"length":1,"stats":{"Line":19}},{"line":78,"address":[1288697,1288569,1288505,1288761,1288825,1288633],"length":1,"stats":{"Line":21}},{"line":81,"address":[143216,143232],"length":1,"stats":{"Line":6}},{"line":82,"address":[143237,143221],"length":1,"stats":{"Line":6}},{"line":85,"address":[141152],"length":1,"stats":{"Line":8}},{"line":86,"address":[141161],"length":1,"stats":{"Line":8}},{"line":93,"address":[1289008,1289088,1288992,1289024,1289072,1289040],"length":1,"stats":{"Line":6}},{"line":94,"address":[],"length":0,"stats":{"Line":6}},{"line":104,"address":[1289168,1289152,1289136],"length":1,"stats":{"Line":3}},{"line":105,"address":[1289157,1289173,1289141],"length":1,"stats":{"Line":3}},{"line":122,"address":[1289958,1290000,1289744,1289376,1289536,1290126,1289726,1289520,1289980,1289356,1290144,1289184,1290290],"length":1,"stats":{"Line":22}},{"line":125,"address":[163659,163707,163515,163563],"length":1,"stats":{"Line":44}},{"line":126,"address":[1290241,1289470,1290083,1289651,1289842,1289289],"length":1,"stats":{"Line":22}},{"line":143,"address":[1290304],"length":1,"stats":{"Line":1}},{"line":144,"address":[],"length":0,"stats":{"Line":0}},{"line":174,"address":[],"length":0,"stats":{"Line":5}},{"line":175,"address":[1290382,1290472,1290504,1290420,1290333],"length":1,"stats":{"Line":5}},{"line":180,"address":[],"length":0,"stats":{"Line":2}},{"line":181,"address":[],"length":0,"stats":{"Line":2}},{"line":188,"address":[],"length":0,"stats":{"Line":1}},{"line":189,"address":[1290629],"length":1,"stats":{"Line":1}},{"line":205,"address":[],"length":0,"stats":{"Line":2}},{"line":206,"address":[],"length":0,"stats":{"Line":6}},{"line":227,"address":[1290896,1290960,1290928],"length":1,"stats":{"Line":3}},{"line":228,"address":[],"length":0,"stats":{"Line":3}},{"line":233,"address":[140560],"length":1,"stats":{"Line":9}},{"line":238,"address":[1291234,1291010,1291042,1291074,1291106,1291170,1291129,1291202],"length":1,"stats":{"Line":9}},{"line":241,"address":[163424,163296,163392,163360,163328,163456],"length":1,"stats":{"Line":18}},{"line":246,"address":[228573,228509,228605,228477,228637,228541],"length":1,"stats":{"Line":18}},{"line":272,"address":[218176,218150,218064],"length":1,"stats":{"Line":4}},{"line":275,"address":[1291262,1291390],"length":1,"stats":{"Line":5}},{"line":278,"address":[140909],"length":1,"stats":{"Line":4}},{"line":315,"address":[],"length":0,"stats":{"Line":2}},{"line":318,"address":[],"length":0,"stats":{"Line":6}},{"line":320,"address":[1291785,1291595,1291625,1291755],"length":1,"stats":{"Line":4}},{"line":322,"address":[],"length":0,"stats":{"Line":0}},{"line":329,"address":[1291840,1291824],"length":1,"stats":{"Line":2}},{"line":330,"address":[],"length":0,"stats":{"Line":2}},{"line":335,"address":[],"length":0,"stats":{"Line":1}},{"line":336,"address":[],"length":0,"stats":{"Line":1}},{"line":355,"address":[],"length":0,"stats":{"Line":1}},{"line":356,"address":[],"length":0,"stats":{"Line":2}},{"line":357,"address":[],"length":0,"stats":{"Line":0}}],"covered":54,"coverable":57},{"path":["/","home","botahamec","Projects","happylock","src","mutex.rs"],"content":"use std::cell::UnsafeCell;\nuse std::marker::PhantomData;\n\nuse lock_api::RawMutex;\n\nuse crate::poisonable::PoisonFlag;\nuse crate::ThreadKey;\n\nmod guard;\nmod mutex;\n\n/// A spinning mutex\n#[cfg(feature = \"spin\")]\npub type SpinLock\u003cT\u003e = Mutex\u003cT, spin::Mutex\u003c()\u003e\u003e;\n\n/// A parking lot mutex\n#[cfg(feature = \"parking_lot\")]\npub type ParkingMutex\u003cT\u003e = Mutex\u003cT, parking_lot::RawMutex\u003e;\n\n/// A mutual exclusion primitive useful for protecting shared data, which\n/// cannot deadlock.\n///\n/// This mutex will block threads waiting for the lock to become available.\n/// Each mutex has a type parameter which represents the data that it is\n/// protecting. The data can only be accessed through the [`MutexGuard`]s\n/// returned from [`lock`] and [`try_lock`], which guarantees that the data is\n/// only ever accessed when the mutex is locked.\n///\n/// Locking the mutex on a thread that already locked it is impossible, due to\n/// the requirement of the [`ThreadKey`]. Therefore, this will never deadlock.\n///\n/// # Examples\n///\n/// ```\n/// use std::sync::Arc;\n/// use std::thread;\n/// use std::sync::mpsc;\n///\n/// use happylock::{Mutex, ThreadKey};\n///\n/// // Spawn a few threads to increment a shared variable (non-atomically),\n/// // and let the main thread know once all increments are done.\n/// //\n/// // Here we're using an Arc to share memory among threads, and the data\n/// // inside the Arc is protected with a mutex.\n/// const N: usize = 10;\n///\n/// let data = Arc::new(Mutex::new(0));\n///\n/// let (tx, rx) = mpsc::channel();\n/// for _ in 0..N {\n/// let (data, tx) = (Arc::clone(\u0026data), tx.clone());\n/// thread::spawn(move || {\n/// let key = ThreadKey::get().unwrap();\n/// let mut data = data.lock(key);\n/// *data += 1;\n/// if *data == N {\n/// tx.send(()).unwrap();\n/// }\n/// // the lock is unlocked\n/// });\n/// }\n///\n/// rx.recv().unwrap();\n/// ```\n///\n/// To unlock a mutex guard sooner than the end of the enclosing scope, either\n/// create an inner scope, drop the guard manually, or call [`Mutex::unlock`].\n///\n/// ```\n/// use std::sync::Arc;\n/// use std::thread;\n///\n/// use happylock::{Mutex, ThreadKey};\n///\n/// const N: usize = 3;\n///\n/// let data_mutex = Arc::new(Mutex::new(vec![1, 2, 3, 4]));\n/// let res_mutex = Arc::new(Mutex::new(0));\n///\n/// let mut threads = Vec::with_capacity(N);\n/// (0..N).for_each(|_| {\n/// let data_mutex_clone = Arc::clone(\u0026data_mutex);\n/// let res_mutex_clone = Arc::clone(\u0026res_mutex);\n///\n/// threads.push(thread::spawn(move || {\n/// let mut key = ThreadKey::get().unwrap();\n///\n/// // Here we use a block to limit the lifetime of the lock guard.\n/// let result = data_mutex_clone.scoped_lock(\u0026mut key, |data| {\n/// let result = data.iter().fold(0, |acc, x| acc + x * 2);\n/// data.push(result);\n/// result\n/// // The mutex guard gets dropped here, so the lock is released\n/// });\n/// // The thread key is available again\n/// *res_mutex_clone.lock(key) += result;\n/// }));\n/// });\n///\n/// let key = ThreadKey::get().unwrap();\n/// let mut data = data_mutex.lock(key);\n/// let result = data.iter().fold(0, |acc, x| acc + x * 2);\n/// data.push(result);\n///\n/// // We drop the `data` explicitly because it's not necessary anymore. This\n/// // allows other threads to start working on the data immediately. Dropping\n/// // the data also gives us access to the thread key, so we can lock\n/// // another mutex.\n/// let key = Mutex::unlock(data);\n///\n/// // Here the mutex guard is not assigned to a variable and so, even if the\n/// // scope does not end after this line, the mutex is still released: there is\n/// // no deadlock.\n/// *res_mutex.lock(key) += result;\n///\n/// threads.into_iter().for_each(|thread| {\n/// thread\n/// .join()\n/// .expect(\"The thread creating or execution failed !\")\n/// });\n///\n/// let key = ThreadKey::get().unwrap();\n/// assert_eq!(*res_mutex.lock(key), 800);\n/// ```\n///\n/// [`lock`]: `Mutex::lock`\n/// [`try_lock`]: `Mutex::try_lock`\n/// [`ThreadKey`]: `crate::ThreadKey`\npub struct Mutex\u003cT: ?Sized, R\u003e {\n\traw: R,\n\tpoison: PoisonFlag,\n\tdata: UnsafeCell\u003cT\u003e,\n}\n\n/// A reference to a mutex that unlocks it when dropped.\n///\n/// This is similar to [`MutexGuard`], except it does not hold a [`Keyable`].\npub struct MutexRef\u003c'a, T: ?Sized + 'a, R: RawMutex\u003e(\u0026'a Mutex\u003cT, R\u003e, PhantomData\u003cR::GuardMarker\u003e);\n\n/// An RAII implementation of a “scoped lock” of a mutex.\n///\n/// When this structure is dropped (falls out of scope), the lock will be\n/// unlocked.\n///\n/// This is created by calling the [`lock`] and [`try_lock`] methods on [`Mutex`]\n///\n/// [`lock`]: `Mutex::lock`\n/// [`try_lock`]: `Mutex::try_lock`\n//\n// This is the most lifetime-intensive thing I've ever written. Can I graduate\n// from borrow checker university now?\npub struct MutexGuard\u003c'a, T: ?Sized + 'a, R: RawMutex\u003e {\n\tmutex: MutexRef\u003c'a, T, R\u003e, // this way we don't need to re-implement Drop\n\tthread_key: ThreadKey,\n}\n\n#[cfg(test)]\nmod tests {\n\tuse crate::{LockCollection, ThreadKey};\n\n\tuse super::*;\n\n\t#[test]\n\tfn unlocked_when_initialized() {\n\t\tlet lock: crate::Mutex\u003c_\u003e = Mutex::new(\"Hello, world!\");\n\n\t\tassert!(!lock.is_locked());\n\t}\n\n\t#[test]\n\tfn locked_after_read() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::Mutex\u003c_\u003e = Mutex::new(\"Hello, world!\");\n\n\t\tlet guard = lock.lock(key);\n\n\t\tassert!(lock.is_locked());\n\t\tdrop(guard)\n\t}\n\n\t#[test]\n\tfn from_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex: crate::Mutex\u003c_\u003e = Mutex::from(\"Hello, world!\");\n\n\t\tlet guard = mutex.lock(key);\n\t\tassert_eq!(*guard, \"Hello, world!\");\n\t}\n\n\t#[test]\n\tfn as_mut_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mut mutex = crate::Mutex::from(42);\n\n\t\tlet mut_ref = mutex.as_mut();\n\t\t*mut_ref = 24;\n\n\t\tmutex.scoped_lock(key, |guard| assert_eq!(*guard, 24))\n\t}\n\n\t#[test]\n\tfn display_works_for_guard() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex: crate::Mutex\u003c_\u003e = Mutex::new(\"Hello, world!\");\n\t\tlet guard = mutex.lock(key);\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn display_works_for_ref() {\n\t\tlet mutex: crate::Mutex\u003c_\u003e = Mutex::new(\"Hello, world!\");\n\t\tlet guard = unsafe { mutex.try_lock_no_key().unwrap() };\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn ref_as_mut() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = LockCollection::new(crate::Mutex::new(0));\n\t\tlet mut guard = collection.lock(key);\n\t\tlet guard_mut = guard.as_mut().as_mut();\n\n\t\t*guard_mut = 3;\n\t\tlet key = LockCollection::\u003ccrate::Mutex\u003c_\u003e\u003e::unlock(guard);\n\n\t\tlet guard = collection.lock(key);\n\n\t\tassert_eq!(guard.as_ref().as_ref(), \u00263);\n\t}\n\n\t#[test]\n\tfn guard_as_mut() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = crate::Mutex::new(0);\n\t\tlet mut guard = mutex.lock(key);\n\t\tlet guard_mut = guard.as_mut();\n\n\t\t*guard_mut = 3;\n\t\tlet key = Mutex::unlock(guard);\n\n\t\tlet guard = mutex.lock(key);\n\n\t\tassert_eq!(guard.as_ref(), \u00263);\n\t}\n\n\t#[test]\n\tfn dropping_guard_releases_mutex() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex: crate::Mutex\u003c_\u003e = Mutex::new(\"Hello, world!\");\n\n\t\tlet guard = mutex.lock(key);\n\t\tdrop(guard);\n\n\t\tassert!(!mutex.is_locked());\n\t}\n\n\t#[test]\n\tfn dropping_ref_releases_mutex() {\n\t\tlet mutex: crate::Mutex\u003c_\u003e = Mutex::new(\"Hello, world!\");\n\n\t\tlet guard = unsafe { mutex.try_lock_no_key().unwrap() };\n\t\tdrop(guard);\n\n\t\tassert!(!mutex.is_locked());\n\t}\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","src","poisonable","error.rs"],"content":"use core::fmt;\nuse std::error::Error;\n\nuse super::{PoisonError, PoisonGuard, TryLockPoisonableError};\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard\u003e fmt::Debug for PoisonError\u003cGuard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut fmt::Formatter\u003c'_\u003e) -\u003e fmt::Result {\n\t\tf.debug_struct(\"PoisonError\").finish_non_exhaustive()\n\t}\n}\n\nimpl\u003cGuard\u003e fmt::Display for PoisonError\u003cGuard\u003e {\n\t#[cfg_attr(test, mutants::skip)]\n\t#[cfg(not(tarpaulin_include))]\n\tfn fmt(\u0026self, f: \u0026mut fmt::Formatter\u003c'_\u003e) -\u003e fmt::Result {\n\t\t\"poisoned lock: another task failed inside\".fmt(f)\n\t}\n}\n\nimpl\u003cGuard\u003e AsRef\u003cGuard\u003e for PoisonError\u003cGuard\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026Guard {\n\t\tself.get_ref()\n\t}\n}\n\nimpl\u003cGuard\u003e AsMut\u003cGuard\u003e for PoisonError\u003cGuard\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut Guard {\n\t\tself.get_mut()\n\t}\n}\n\nimpl\u003cGuard\u003e Error for PoisonError\u003cGuard\u003e {}\n\nimpl\u003cGuard\u003e PoisonError\u003cGuard\u003e {\n\t/// Creates a `PoisonError`\n\t///\n\t/// This is generally created by methods like [`Poisonable::lock`].\n\t///\n\t/// ```\n\t/// use happylock::poisonable::PoisonError;\n\t///\n\t/// let error = PoisonError::new(\"oh no\");\n\t/// ```\n\t///\n\t/// [`Poisonable::lock`]: `crate::poisonable::Poisonable::lock`\n\t#[must_use]\n\tpub const fn new(guard: Guard) -\u003e Self {\n\t\tSelf { guard }\n\t}\n\n\t/// Consumes the error indicating that a lock is poisonmed, returning the\n\t/// underlying guard to allow access regardless.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::collections::HashSet;\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let mutex = Arc::new(Poisonable::new(Mutex::new(HashSet::new())));\n\t///\n\t/// // poison the mutex\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t/// let _ = thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut data = c_mutex.lock(key).unwrap();\n\t/// data.insert(10);\n\t/// panic!();\n\t/// }).join();\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let p_err = mutex.lock(key).unwrap_err();\n\t/// let data = p_err.into_inner();\n\t/// println!(\"recovered {} items\", data.len());\n\t/// ```\n\t#[must_use]\n\tpub fn into_inner(self) -\u003e Guard {\n\t\tself.guard\n\t}\n\n\t/// Reaches into this error indicating that a lock is poisoned, returning a\n\t/// reference to the underlying guard to allow access regardless.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::collections::HashSet;\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t/// use happylock::poisonable::PoisonGuard;\n\t///\n\t/// let mutex = Arc::new(Poisonable::new(Mutex::new(HashSet::new())));\n\t///\n\t/// // poison the mutex\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t/// let _ = thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut data = c_mutex.lock(key).unwrap();\n\t/// data.insert(10);\n\t/// panic!();\n\t/// }).join();\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let p_err = mutex.lock(key).unwrap_err();\n\t/// let data: \u0026PoisonGuard\u003c_\u003e = p_err.get_ref();\n\t/// println!(\"recovered {} items\", data.len());\n\t/// ```\n\t#[must_use]\n\tpub const fn get_ref(\u0026self) -\u003e \u0026Guard {\n\t\t\u0026self.guard\n\t}\n\n\t/// Reaches into this error indicating that a lock is poisoned, returning a\n\t/// mutable reference to the underlying guard to allow access regardless.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::collections::HashSet;\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let mutex = Arc::new(Poisonable::new(Mutex::new(HashSet::new())));\n\t///\n\t/// // poison the mutex\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t/// let _ = thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut data = c_mutex.lock(key).unwrap();\n\t/// data.insert(10);\n\t/// panic!();\n\t/// }).join();\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut p_err = mutex.lock(key).unwrap_err();\n\t/// let data = p_err.get_mut();\n\t/// data.insert(20);\n\t/// println!(\"recovered {} items\", data.len());\n\t/// ```\n\t#[must_use]\n\tpub fn get_mut(\u0026mut self) -\u003e \u0026mut Guard {\n\t\t\u0026mut self.guard\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cG\u003e fmt::Debug for TryLockPoisonableError\u003c'_, G\u003e {\n\tfn fmt(\u0026self, f: \u0026mut fmt::Formatter\u003c'_\u003e) -\u003e fmt::Result {\n\t\tmatch *self {\n\t\t\tSelf::Poisoned(..) =\u003e \"Poisoned(..)\".fmt(f),\n\t\t\tSelf::WouldBlock(_) =\u003e \"WouldBlock\".fmt(f),\n\t\t}\n\t}\n}\n\nimpl\u003cG\u003e fmt::Display for TryLockPoisonableError\u003c'_, G\u003e {\n\t#[cfg_attr(test, mutants::skip)]\n\t#[cfg(not(tarpaulin_include))]\n\tfn fmt(\u0026self, f: \u0026mut fmt::Formatter\u003c'_\u003e) -\u003e fmt::Result {\n\t\tmatch *self {\n\t\t\tSelf::Poisoned(..) =\u003e \"poisoned lock: another task failed inside\",\n\t\t\tSelf::WouldBlock(_) =\u003e \"try_lock failed because the operation would block\",\n\t\t}\n\t\t.fmt(f)\n\t}\n}\n\nimpl\u003cG\u003e Error for TryLockPoisonableError\u003c'_, G\u003e {}\n\nimpl\u003c'flag, G\u003e From\u003cPoisonError\u003cPoisonGuard\u003c'flag, G\u003e\u003e\u003e for TryLockPoisonableError\u003c'flag, G\u003e {\n\tfn from(value: PoisonError\u003cPoisonGuard\u003c'flag, G\u003e\u003e) -\u003e Self {\n\t\tSelf::Poisoned(value)\n\t}\n}\n","traces":[{"line":23,"address":[863392],"length":1,"stats":{"Line":1}},{"line":24,"address":[],"length":0,"stats":{"Line":1}},{"line":29,"address":[],"length":0,"stats":{"Line":1}},{"line":30,"address":[],"length":0,"stats":{"Line":1}},{"line":49,"address":[],"length":0,"stats":{"Line":10}},{"line":82,"address":[],"length":0,"stats":{"Line":7}},{"line":83,"address":[],"length":0,"stats":{"Line":1}},{"line":116,"address":[],"length":0,"stats":{"Line":4}},{"line":117,"address":[],"length":0,"stats":{"Line":0}},{"line":150,"address":[],"length":0,"stats":{"Line":3}},{"line":151,"address":[],"length":0,"stats":{"Line":0}},{"line":181,"address":[],"length":0,"stats":{"Line":1}},{"line":182,"address":[],"length":0,"stats":{"Line":1}}],"covered":11,"coverable":13},{"path":["/","home","botahamec","Projects","happylock","src","poisonable","flag.rs"],"content":"#[cfg(panic = \"unwind\")]\nuse std::sync::atomic::{AtomicBool, Ordering::Relaxed};\n\nuse super::PoisonFlag;\n\n#[cfg(panic = \"unwind\")]\nimpl PoisonFlag {\n\tpub const fn new() -\u003e Self {\n\t\tSelf(AtomicBool::new(false))\n\t}\n\n\tpub fn is_poisoned(\u0026self) -\u003e bool {\n\t\tself.0.load(Relaxed)\n\t}\n\n\tpub fn clear_poison(\u0026self) {\n\t\tself.0.store(false, Relaxed)\n\t}\n\n\tpub fn poison(\u0026self) {\n\t\tself.0.store(true, Relaxed);\n\t}\n}\n\n#[cfg(not(panic = \"unwind\"))]\nimpl PoisonFlag {\n\tpub const fn new() -\u003e Self {\n\t\tSelf()\n\t}\n\n\t#[mutants::skip] // None of the tests have panic = \"abort\", so this can't be tested\n\t#[cfg(not(tarpaulin_include))]\n\tpub fn is_poisoned(\u0026self) -\u003e bool {\n\t\tfalse\n\t}\n\n\tpub fn clear_poison(\u0026self) {\n\t\t()\n\t}\n\n\tpub fn poison(\u0026self) {\n\t\t()\n\t}\n}\n","traces":[{"line":8,"address":[532416],"length":1,"stats":{"Line":19}},{"line":9,"address":[538033],"length":1,"stats":{"Line":19}},{"line":12,"address":[508448],"length":1,"stats":{"Line":11}},{"line":13,"address":[532473],"length":1,"stats":{"Line":11}},{"line":16,"address":[546784],"length":1,"stats":{"Line":1}},{"line":17,"address":[508489],"length":1,"stats":{"Line":1}},{"line":20,"address":[554944],"length":1,"stats":{"Line":8}},{"line":21,"address":[508521],"length":1,"stats":{"Line":8}}],"covered":8,"coverable":8},{"path":["/","home","botahamec","Projects","happylock","src","poisonable","guard.rs"],"content":"use std::fmt::{Debug, Display};\nuse std::hash::Hash;\nuse std::marker::PhantomData;\nuse std::ops::{Deref, DerefMut};\n\nuse super::{PoisonFlag, PoisonGuard, PoisonRef};\n\nimpl\u003c'a, Guard\u003e PoisonRef\u003c'a, Guard\u003e {\n\t// This is used so that we don't keep accidentally adding the flag reference\n\tpub(super) const fn new(flag: \u0026'a PoisonFlag, guard: Guard) -\u003e Self {\n\t\tSelf {\n\t\t\tguard,\n\t\t\t#[cfg(panic = \"unwind\")]\n\t\t\tflag,\n\t\t\t_phantom: PhantomData,\n\t\t}\n\t}\n}\n\nimpl\u003cGuard\u003e Drop for PoisonRef\u003c'_, Guard\u003e {\n\tfn drop(\u0026mut self) {\n\t\t#[cfg(panic = \"unwind\")]\n\t\tif std::thread::panicking() {\n\t\t\tself.flag.poison();\n\t\t}\n\t}\n}\n\n#[mutants::skip] // hashing involves RNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard: Hash\u003e Hash for PoisonRef\u003c'_, Guard\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.guard.hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard: Debug\u003e Debug for PoisonRef\u003c'_, Guard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cGuard: Display\u003e Display for PoisonRef\u003c'_, Guard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cGuard\u003e Deref for PoisonRef\u003c'_, Guard\u003e {\n\ttype Target = Guard;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t\u0026self.guard\n\t}\n}\n\nimpl\u003cGuard\u003e DerefMut for PoisonRef\u003c'_, Guard\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t\u0026mut self.guard\n\t}\n}\n\nimpl\u003cGuard\u003e AsRef\u003cGuard\u003e for PoisonRef\u003c'_, Guard\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026Guard {\n\t\t\u0026self.guard\n\t}\n}\n\nimpl\u003cGuard\u003e AsMut\u003cGuard\u003e for PoisonRef\u003c'_, Guard\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut Guard {\n\t\t\u0026mut self.guard\n\t}\n}\n\n#[mutants::skip] // hashing involves RNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard: Hash\u003e Hash for PoisonGuard\u003c'_, Guard\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.guard.hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard: Debug\u003e Debug for PoisonGuard\u003c'_, Guard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026self.guard, f)\n\t}\n}\n\nimpl\u003cGuard: Display\u003e Display for PoisonGuard\u003c'_, Guard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026self.guard, f)\n\t}\n}\n\nimpl\u003cT, Guard: Deref\u003cTarget = T\u003e\u003e Deref for PoisonGuard\u003c'_, Guard\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t#[allow(clippy::explicit_auto_deref)] // fixing this results in a compiler error\n\t\t\u0026*self.guard.guard\n\t}\n}\n\nimpl\u003cT, Guard: DerefMut\u003cTarget = T\u003e\u003e DerefMut for PoisonGuard\u003c'_, Guard\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t#[allow(clippy::explicit_auto_deref)] // fixing this results in a compiler error\n\t\t\u0026mut *self.guard.guard\n\t}\n}\n\nimpl\u003cGuard\u003e AsRef\u003cGuard\u003e for PoisonGuard\u003c'_, Guard\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026Guard {\n\t\t\u0026self.guard.guard\n\t}\n}\n\nimpl\u003cGuard\u003e AsMut\u003cGuard\u003e for PoisonGuard\u003c'_, Guard\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut Guard {\n\t\t\u0026mut self.guard.guard\n\t}\n}\n","traces":[{"line":10,"address":[864176,864080,864144,864112],"length":1,"stats":{"Line":5}},{"line":21,"address":[],"length":0,"stats":{"Line":4}},{"line":22,"address":[],"length":0,"stats":{"Line":0}},{"line":23,"address":[],"length":0,"stats":{"Line":5}},{"line":24,"address":[],"length":0,"stats":{"Line":4}},{"line":46,"address":[],"length":0,"stats":{"Line":1}},{"line":47,"address":[],"length":0,"stats":{"Line":1}},{"line":54,"address":[],"length":0,"stats":{"Line":3}},{"line":55,"address":[],"length":0,"stats":{"Line":0}},{"line":60,"address":[],"length":0,"stats":{"Line":2}},{"line":61,"address":[],"length":0,"stats":{"Line":0}},{"line":66,"address":[],"length":0,"stats":{"Line":1}},{"line":67,"address":[],"length":0,"stats":{"Line":0}},{"line":72,"address":[],"length":0,"stats":{"Line":1}},{"line":73,"address":[],"length":0,"stats":{"Line":0}},{"line":94,"address":[],"length":0,"stats":{"Line":1}},{"line":95,"address":[],"length":0,"stats":{"Line":1}},{"line":102,"address":[],"length":0,"stats":{"Line":2}},{"line":104,"address":[],"length":0,"stats":{"Line":2}},{"line":109,"address":[],"length":0,"stats":{"Line":3}},{"line":111,"address":[],"length":0,"stats":{"Line":3}},{"line":116,"address":[],"length":0,"stats":{"Line":1}},{"line":117,"address":[],"length":0,"stats":{"Line":0}},{"line":122,"address":[],"length":0,"stats":{"Line":1}},{"line":123,"address":[],"length":0,"stats":{"Line":0}}],"covered":18,"coverable":25},{"path":["/","home","botahamec","Projects","happylock","src","poisonable","poisonable.rs"],"content":"use std::panic::{RefUnwindSafe, UnwindSafe};\n\nuse crate::handle_unwind::handle_unwind;\nuse crate::lockable::{\n\tLockable, LockableGetMut, LockableIntoInner, OwnedLockable, RawLock, Sharable,\n};\nuse crate::{Keyable, ThreadKey};\n\nuse super::{\n\tPoisonError, PoisonFlag, PoisonGuard, PoisonRef, PoisonResult, Poisonable,\n\tTryLockPoisonableError, TryLockPoisonableResult,\n};\n\nunsafe impl\u003cL: Lockable + RawLock\u003e RawLock for Poisonable\u003cL\u003e {\n\t#[mutants::skip] // this should never run\n\t#[cfg(not(tarpaulin_include))]\n\tfn poison(\u0026self) {\n\t\tself.inner.poison()\n\t}\n\n\tunsafe fn raw_write(\u0026self) {\n\t\tself.inner.raw_write()\n\t}\n\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool {\n\t\tself.inner.raw_try_write()\n\t}\n\n\tunsafe fn raw_unlock_write(\u0026self) {\n\t\tself.inner.raw_unlock_write()\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tself.inner.raw_read()\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tself.inner.raw_try_read()\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tself.inner.raw_unlock_read()\n\t}\n}\n\nunsafe impl\u003cL: Lockable\u003e Lockable for Poisonable\u003cL\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= PoisonResult\u003cPoisonRef\u003c'g, L::Guard\u003c'g\u003e\u003e\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= PoisonResult\u003cL::DataMut\u003c'a\u003e\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tself.inner.get_ptrs(ptrs)\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tlet ref_guard = PoisonRef::new(\u0026self.poisoned, self.inner.guard());\n\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(ref_guard))\n\t\t} else {\n\t\t\tOk(ref_guard)\n\t\t}\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(self.inner.data_mut()))\n\t\t} else {\n\t\t\tOk(self.inner.data_mut())\n\t\t}\n\t}\n}\n\nunsafe impl\u003cL: Sharable\u003e Sharable for Poisonable\u003cL\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= PoisonResult\u003cPoisonRef\u003c'g, L::ReadGuard\u003c'g\u003e\u003e\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= PoisonResult\u003cL::DataRef\u003c'a\u003e\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tlet ref_guard = PoisonRef::new(\u0026self.poisoned, self.inner.read_guard());\n\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(ref_guard))\n\t\t} else {\n\t\t\tOk(ref_guard)\n\t\t}\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(self.inner.data_ref()))\n\t\t} else {\n\t\t\tOk(self.inner.data_ref())\n\t\t}\n\t}\n}\n\nunsafe impl\u003cL: OwnedLockable\u003e OwnedLockable for Poisonable\u003cL\u003e {}\n\n// AsMut won't work here because we don't strictly return a \u0026mut T\n// LockableGetMut is the next best thing\nimpl\u003cL: LockableGetMut\u003e LockableGetMut for Poisonable\u003cL\u003e {\n\ttype Inner\u003c'a\u003e\n\t\t= PoisonResult\u003cL::Inner\u003c'a\u003e\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(self.inner.get_mut()))\n\t\t} else {\n\t\t\tOk(self.inner.get_mut())\n\t\t}\n\t}\n}\n\nimpl\u003cL: LockableIntoInner\u003e LockableIntoInner for Poisonable\u003cL\u003e {\n\ttype Inner = PoisonResult\u003cL::Inner\u003e;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(self.inner.into_inner()))\n\t\t} else {\n\t\t\tOk(self.inner.into_inner())\n\t\t}\n\t}\n}\n\nimpl\u003cL\u003e From\u003cL\u003e for Poisonable\u003cL\u003e {\n\tfn from(value: L) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\nimpl\u003cL\u003e Poisonable\u003cL\u003e {\n\t/// Creates a new `Poisonable`\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, Poisonable};\n\t///\n\t/// let mutex = Poisonable::new(Mutex::new(0));\n\t/// ```\n\tpub const fn new(value: L) -\u003e Self {\n\t\tSelf {\n\t\t\tinner: value,\n\t\t\tpoisoned: PoisonFlag::new(),\n\t\t}\n\t}\n\n\t/// Determines whether the mutex is poisoned.\n\t///\n\t/// If another thread is active, the mutex can still become poisoned at any\n\t/// time. You should not trust a `false` value for program correctness\n\t/// without additional synchronization.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let mutex = Arc::new(Poisonable::new(Mutex::new(0)));\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t///\n\t/// let _ = thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let _lock = c_mutex.lock(key).unwrap();\n\t/// panic!(); // the mutex gets poisoned\n\t/// }).join();\n\t///\n\t/// assert_eq!(mutex.is_poisoned(), true);\n\t/// ```\n\tpub fn is_poisoned(\u0026self) -\u003e bool {\n\t\tself.poisoned.is_poisoned()\n\t}\n\n\t/// Clear the poisoned state from a lock.\n\t///\n\t/// If the lock is poisoned, it will remain poisoned until this function\n\t/// is called. This allows recovering from a poisoned state and marking\n\t/// that it has recovered. For example, if the value is overwritten by a\n\t/// known-good value, then the lock can be marked as un-poisoned. Or\n\t/// possibly, the value could by inspected to determine if it is in a\n\t/// consistent state, and if so the poison is removed.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let mutex = Arc::new(Poisonable::new(Mutex::new(0)));\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t///\n\t/// let _ = thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let _lock = c_mutex.lock(key).unwrap();\n\t/// panic!(); // the mutex gets poisoned\n\t/// }).join();\n\t///\n\t/// assert_eq!(mutex.is_poisoned(), true);\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let x = mutex.lock(key).unwrap_or_else(|mut e| {\n\t/// **e.get_mut() = 1;\n\t/// mutex.clear_poison();\n\t/// e.into_inner()\n\t/// });\n\t///\n\t/// assert_eq!(mutex.is_poisoned(), false);\n\t/// assert_eq!(*x, 1);\n\t/// ```\n\tpub fn clear_poison(\u0026self) {\n\t\tself.poisoned.clear_poison()\n\t}\n\n\t/// Consumes this `Poisonable`, returning the underlying lock.\n\t///\n\t/// This consumes the `Poisonable` and returns ownership of the lock, which\n\t/// means that the `Poisonable` can still be `RefUnwindSafe`.\n\t///\n\t/// # Errors\n\t///\n\t/// If another user of this lock panicked while holding the lock, then this\n\t/// call will return an error instead.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, Poisonable};\n\t///\n\t/// let mutex = Poisonable::new(Mutex::new(0));\n\t/// assert_eq!(mutex.into_child().unwrap().into_inner(), 0);\n\t/// ```\n\tpub fn into_child(self) -\u003e PoisonResult\u003cL\u003e {\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(self.inner))\n\t\t} else {\n\t\t\tOk(self.inner)\n\t\t}\n\t}\n\n\t/// Returns a mutable reference to the underlying lock.\n\t///\n\t/// This can be implemented while still being `RefUnwindSafe` because\n\t/// it requires a mutable reference.\n\t///\n\t/// # Errors\n\t///\n\t/// If another user of this lock panicked while holding the lock, then\n\t/// this call will return an error instead.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut mutex = Poisonable::new(Mutex::new(0));\n\t/// *mutex.child_mut().unwrap().as_mut() = 10;\n\t/// assert_eq!(*mutex.lock(key).unwrap(), 10);\n\t/// ```\n\tpub fn child_mut(\u0026mut self) -\u003e PoisonResult\u003c\u0026mut L\u003e {\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(\u0026mut self.inner))\n\t\t} else {\n\t\t\tOk(\u0026mut self.inner)\n\t\t}\n\t}\n\n\t// NOTE: `child_ref` isn't implemented because it would make this not `RefUnwindSafe`\n\t//\n}\n\nimpl\u003cL: Lockable\u003e Poisonable\u003cL\u003e {\n\t/// Creates a guard for the poisonable, without locking it\n\tunsafe fn guard(\u0026self, key: ThreadKey) -\u003e PoisonResult\u003cPoisonGuard\u003c'_, L::Guard\u003c'_\u003e\u003e\u003e {\n\t\tlet guard = PoisonGuard {\n\t\t\tguard: PoisonRef::new(\u0026self.poisoned, self.inner.guard()),\n\t\t\tkey,\n\t\t};\n\n\t\tif self.is_poisoned() {\n\t\t\treturn Err(PoisonError::new(guard));\n\t\t}\n\n\t\tOk(guard)\n\t}\n}\n\nimpl\u003cL: Lockable + RawLock\u003e Poisonable\u003cL\u003e {\n\tpub fn scoped_lock\u003c'a, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: impl Keyable,\n\t\tf: impl Fn(\u003cSelf as Lockable\u003e::DataMut\u003c'a\u003e) -\u003e R,\n\t) -\u003e R {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_write();\n\n\t\t\t// safety: the data was just locked\n\t\t\tlet r = handle_unwind(\n\t\t\t\t|| f(self.data_mut()),\n\t\t\t\t|| {\n\t\t\t\t\tself.poisoned.poison();\n\t\t\t\t\tself.raw_unlock_write();\n\t\t\t\t},\n\t\t\t);\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock_write();\n\n\t\t\tdrop(key); // ensure the key stays alive long enough\n\n\t\t\tr\n\t\t}\n\t}\n\n\tpub fn scoped_try_lock\u003c'a, Key: Keyable, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(\u003cSelf as Lockable\u003e::DataMut\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_write() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we just locked the collection\n\t\t\tlet r = handle_unwind(\n\t\t\t\t|| f(self.data_mut()),\n\t\t\t\t|| {\n\t\t\t\t\tself.poisoned.poison();\n\t\t\t\t\tself.raw_unlock_write();\n\t\t\t\t},\n\t\t\t);\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock_write();\n\n\t\t\tdrop(key); // ensures the key stays valid long enough\n\n\t\t\tOk(r)\n\t\t}\n\t}\n\n\t/// Acquires the lock, blocking the current thread until it is ok to do so.\n\t///\n\t/// This function will block the current thread until it is available to\n\t/// acquire the mutex. Upon returning, the thread is the only thread with\n\t/// the lock held. An RAII guard is returned to allow scoped unlock of the\n\t/// lock. When the guard goes out of scope, the mutex will be unlocked.\n\t///\n\t/// # Errors\n\t///\n\t/// If another use of this mutex panicked while holding the mutex, then\n\t/// this call will return an error once the mutex is acquired.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let mutex = Arc::new(Poisonable::new(Mutex::new(0)));\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t///\n\t/// thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// *c_mutex.lock(key).unwrap() = 10;\n\t/// }).join().expect(\"thread::spawn failed\");\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// assert_eq!(*mutex.lock(key).unwrap(), 10);\n\t/// ```\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e PoisonResult\u003cPoisonGuard\u003c'_, L::Guard\u003c'_\u003e\u003e\u003e {\n\t\tunsafe {\n\t\t\tself.inner.raw_write();\n\t\t\tself.guard(key)\n\t\t}\n\t}\n\n\t/// Attempts to acquire this lock.\n\t///\n\t/// If the lock could not be acquired at this time, then [`Err`] is\n\t/// returned. Otherwise, an RAII guard is returned. The lock will be\n\t/// unlocked when the guard is dropped.\n\t///\n\t/// This function does not block.\n\t///\n\t/// # Errors\n\t///\n\t/// If another user of this mutex panicked while holding the mutex, then\n\t/// this call will return the [`Poisoned`] error if the mutex would\n\t/// otherwise be acquired.\n\t///\n\t/// If the mutex could not be acquired because it is already locked, then\n\t/// this call will return the [`WouldBlock`] error.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let mutex = Arc::new(Poisonable::new(Mutex::new(0)));\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t///\n\t/// thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut lock = c_mutex.try_lock(key);\n\t/// if let Ok(mut mutex) = lock {\n\t/// *mutex = 10;\n\t/// } else {\n\t/// println!(\"try_lock failed\");\n\t/// }\n\t/// }).join().expect(\"thread::spawn failed\");\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// assert_eq!(*mutex.lock(key).unwrap(), 10);\n\t/// ```\n\t///\n\t/// [`Poisoned`]: `TryLockPoisonableError::Poisoned`\n\t/// [`WouldBlock`]: `TryLockPoisonableError::WouldBlock`\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e TryLockPoisonableResult\u003c'_, L::Guard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\tif self.inner.raw_try_write() {\n\t\t\t\tOk(self.guard(key)?)\n\t\t\t} else {\n\t\t\t\tErr(TryLockPoisonableError::WouldBlock(key))\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Consumes the [`PoisonGuard`], and consequently unlocks its `Poisonable`.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, Mutex, Poisonable};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mutex = Poisonable::new(Mutex::new(0));\n\t///\n\t/// let mut guard = mutex.lock(key).unwrap();\n\t/// *guard += 20;\n\t///\n\t/// let key = Poisonable::\u003cMutex\u003c_\u003e\u003e::unlock(guard);\n\t/// ```\n\tpub fn unlock\u003c'flag\u003e(guard: PoisonGuard\u003c'flag, L::Guard\u003c'flag\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: Sharable + RawLock\u003e Poisonable\u003cL\u003e {\n\tunsafe fn read_guard(\u0026self, key: ThreadKey) -\u003e PoisonResult\u003cPoisonGuard\u003c'_, L::ReadGuard\u003c'_\u003e\u003e\u003e {\n\t\tlet guard = PoisonGuard {\n\t\t\tguard: PoisonRef::new(\u0026self.poisoned, self.inner.read_guard()),\n\t\t\tkey,\n\t\t};\n\n\t\tif self.is_poisoned() {\n\t\t\treturn Err(PoisonError::new(guard));\n\t\t}\n\n\t\tOk(guard)\n\t}\n\n\tpub fn scoped_read\u003c'a, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: impl Keyable,\n\t\tf: impl Fn(\u003cSelf as Sharable\u003e::DataRef\u003c'a\u003e) -\u003e R,\n\t) -\u003e R {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_read();\n\n\t\t\t// safety: the data was just locked\n\t\t\tlet r = handle_unwind(\n\t\t\t\t|| f(self.data_ref()),\n\t\t\t\t|| {\n\t\t\t\t\tself.poisoned.poison();\n\t\t\t\t\tself.raw_unlock_read();\n\t\t\t\t},\n\t\t\t);\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock_read();\n\n\t\t\tdrop(key); // ensure the key stays alive long enough\n\n\t\t\tr\n\t\t}\n\t}\n\n\tpub fn scoped_try_read\u003c'a, Key: Keyable, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(\u003cSelf as Sharable\u003e::DataRef\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_read() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we just locked the collection\n\t\t\tlet r = handle_unwind(\n\t\t\t\t|| f(self.data_ref()),\n\t\t\t\t|| {\n\t\t\t\t\tself.poisoned.poison();\n\t\t\t\t\tself.raw_unlock_read();\n\t\t\t\t},\n\t\t\t);\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock_read();\n\n\t\t\tdrop(key); // ensures the key stays valid long enough\n\n\t\t\tOk(r)\n\t\t}\n\t}\n\n\t/// Locks with shared read access, blocking the current thread until it can\n\t/// be acquired.\n\t///\n\t/// This function will block the current thread until there are no writers\n\t/// which hold the lock. This method does not provide any guarantee with\n\t/// respect to the ordering of contentious readers or writers will acquire\n\t/// the lock.\n\t///\n\t/// # Errors\n\t///\n\t/// If another use of this lock panicked while holding the lock, then\n\t/// this call will return an error once the lock is acquired.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t///\n\t/// use happylock::{RwLock, Poisonable, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = Arc::new(Poisonable::new(RwLock::new(0)));\n\t/// let c_lock = Arc::clone(\u0026lock);\n\t///\n\t/// let n = lock.read(key).unwrap();\n\t/// assert_eq!(*n, 0);\n\t///\n\t/// thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// assert!(c_lock.read(key).is_ok());\n\t/// }).join().expect(\"thread::spawn failed\");\n\t/// ```\n\tpub fn read(\u0026self, key: ThreadKey) -\u003e PoisonResult\u003cPoisonGuard\u003c'_, L::ReadGuard\u003c'_\u003e\u003e\u003e {\n\t\tunsafe {\n\t\t\tself.inner.raw_read();\n\t\t\tself.read_guard(key)\n\t\t}\n\t}\n\n\t/// Attempts to acquire the lock with shared read access.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is returned.\n\t/// Otherwise, an RAII guard is returned which will release the shared access\n\t/// when it is dropped.\n\t///\n\t/// This function does not block.\n\t///\n\t/// This function does not provide any guarantees with respect to the ordering\n\t/// of whether contentious readers or writers will acquire the lock first.\n\t///\n\t/// # Errors\n\t///\n\t/// This function will return the [`Poisoned`] error if the lock is\n\t/// poisoned. A [`Poisonable`] is poisoned whenever a writer panics while\n\t/// holding an exclusive lock. `Poisoned` will only be returned if the lock\n\t/// would have otherwise been acquired.\n\t///\n\t/// This function will return the [`WouldBlock`] error if the lock could\n\t/// not be acquired because it was already locked exclusively.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Poisonable, RwLock, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = Poisonable::new(RwLock::new(1));\n\t///\n\t/// match lock.try_read(key) {\n\t/// Ok(n) =\u003e assert_eq!(*n, 1),\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t/// ```\n\t///\n\t/// [`Poisoned`]: `TryLockPoisonableError::Poisoned`\n\t/// [`WouldBlock`]: `TryLockPoisonableError::WouldBlock`\n\tpub fn try_read(\u0026self, key: ThreadKey) -\u003e TryLockPoisonableResult\u003c'_, L::ReadGuard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\tif self.inner.raw_try_read() {\n\t\t\t\tOk(self.read_guard(key)?)\n\t\t\t} else {\n\t\t\t\tErr(TryLockPoisonableError::WouldBlock(key))\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Consumes the [`PoisonGuard`], and consequently unlocks its underlying lock.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, RwLock, Poisonable};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = Poisonable::new(RwLock::new(0));\n\t///\n\t/// let mut guard = lock.read(key).unwrap();\n\t/// let key = Poisonable::\u003cRwLock\u003c_\u003e\u003e::unlock_read(guard);\n\t/// ```\n\tpub fn unlock_read\u003c'flag\u003e(guard: PoisonGuard\u003c'flag, L::ReadGuard\u003c'flag\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: LockableIntoInner\u003e Poisonable\u003cL\u003e {\n\t/// Consumes this `Poisonable`, returning the underlying data.\n\t///\n\t/// # Errors\n\t///\n\t/// If another user of this lock panicked while holding the lock, then this\n\t/// call will return an error instead.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, Poisonable};\n\t///\n\t/// let mutex = Poisonable::new(Mutex::new(0));\n\t/// assert_eq!(mutex.into_inner().unwrap(), 0);\n\t/// ```\n\tpub fn into_inner(self) -\u003e PoisonResult\u003cL::Inner\u003e {\n\t\tLockableIntoInner::into_inner(self)\n\t}\n}\n\nimpl\u003cL: LockableGetMut + RawLock\u003e Poisonable\u003cL\u003e {\n\t/// Returns a mutable reference to the underlying data.\n\t///\n\t/// Since this call borrows the `Poisonable` mutably, no actual locking\n\t/// needs to take place - the mutable borrow statically guarantees no locks\n\t/// exist.\n\t///\n\t/// # Errors\n\t///\n\t/// If another user of this lock panicked while holding the lock, then\n\t/// this call will return an error instead.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut mutex = Poisonable::new(Mutex::new(0));\n\t/// *mutex.get_mut().unwrap() = 10;\n\t/// assert_eq!(*mutex.lock(key).unwrap(), 10);\n\t/// ```\n\tpub fn get_mut(\u0026mut self) -\u003e PoisonResult\u003cL::Inner\u003c'_\u003e\u003e {\n\t\tLockableGetMut::get_mut(self)\n\t}\n}\n\nimpl\u003cL: UnwindSafe\u003e RefUnwindSafe for Poisonable\u003cL\u003e {}\nimpl\u003cL: UnwindSafe\u003e UnwindSafe for Poisonable\u003cL\u003e {}\n","traces":[{"line":21,"address":[],"length":0,"stats":{"Line":1}},{"line":22,"address":[864517],"length":1,"stats":{"Line":1}},{"line":25,"address":[],"length":0,"stats":{"Line":2}},{"line":26,"address":[],"length":0,"stats":{"Line":2}},{"line":29,"address":[],"length":0,"stats":{"Line":2}},{"line":30,"address":[864581,864565],"length":1,"stats":{"Line":2}},{"line":33,"address":[],"length":0,"stats":{"Line":1}},{"line":34,"address":[],"length":0,"stats":{"Line":1}},{"line":37,"address":[],"length":0,"stats":{"Line":2}},{"line":38,"address":[],"length":0,"stats":{"Line":2}},{"line":41,"address":[],"length":0,"stats":{"Line":1}},{"line":42,"address":[],"length":0,"stats":{"Line":1}},{"line":57,"address":[],"length":0,"stats":{"Line":4}},{"line":58,"address":[],"length":0,"stats":{"Line":4}},{"line":61,"address":[],"length":0,"stats":{"Line":3}},{"line":62,"address":[],"length":0,"stats":{"Line":3}},{"line":64,"address":[],"length":0,"stats":{"Line":11}},{"line":65,"address":[864913,865201,865261,864973],"length":1,"stats":{"Line":2}},{"line":67,"address":[],"length":0,"stats":{"Line":3}},{"line":71,"address":[],"length":0,"stats":{"Line":2}},{"line":72,"address":[],"length":0,"stats":{"Line":4}},{"line":73,"address":[865366,865462],"length":1,"stats":{"Line":1}},{"line":75,"address":[],"length":0,"stats":{"Line":2}},{"line":91,"address":[865758,865780,865504],"length":1,"stats":{"Line":1}},{"line":92,"address":[],"length":0,"stats":{"Line":1}},{"line":94,"address":[],"length":0,"stats":{"Line":4}},{"line":95,"address":[],"length":0,"stats":{"Line":2}},{"line":97,"address":[865649],"length":1,"stats":{"Line":1}},{"line":101,"address":[],"length":0,"stats":{"Line":1}},{"line":102,"address":[],"length":0,"stats":{"Line":2}},{"line":103,"address":[],"length":0,"stats":{"Line":1}},{"line":105,"address":[],"length":0,"stats":{"Line":1}},{"line":120,"address":[],"length":0,"stats":{"Line":1}},{"line":121,"address":[],"length":0,"stats":{"Line":3}},{"line":122,"address":[865942],"length":1,"stats":{"Line":1}},{"line":124,"address":[],"length":0,"stats":{"Line":1}},{"line":132,"address":[866299,865984],"length":1,"stats":{"Line":1}},{"line":133,"address":[],"length":0,"stats":{"Line":3}},{"line":134,"address":[],"length":0,"stats":{"Line":2}},{"line":136,"address":[],"length":0,"stats":{"Line":2}},{"line":142,"address":[],"length":0,"stats":{"Line":1}},{"line":143,"address":[],"length":0,"stats":{"Line":1}},{"line":157,"address":[],"length":0,"stats":{"Line":3}},{"line":160,"address":[],"length":0,"stats":{"Line":7}},{"line":189,"address":[],"length":0,"stats":{"Line":4}},{"line":190,"address":[866757,866789,866821],"length":1,"stats":{"Line":4}},{"line":231,"address":[],"length":0,"stats":{"Line":3}},{"line":232,"address":[866853,866869,866885],"length":1,"stats":{"Line":3}},{"line":253,"address":[],"length":0,"stats":{"Line":1}},{"line":254,"address":[],"length":0,"stats":{"Line":4}},{"line":255,"address":[],"length":0,"stats":{"Line":2}},{"line":257,"address":[867002],"length":1,"stats":{"Line":1}},{"line":281,"address":[],"length":0,"stats":{"Line":0}},{"line":282,"address":[],"length":0,"stats":{"Line":0}},{"line":283,"address":[],"length":0,"stats":{"Line":0}},{"line":285,"address":[],"length":0,"stats":{"Line":0}},{"line":295,"address":[],"length":0,"stats":{"Line":4}},{"line":297,"address":[867234,868138,867650,867306,867722,868066],"length":1,"stats":{"Line":8}},{"line":301,"address":[867774,868190,867358,868240,867408,867824],"length":1,"stats":{"Line":8}},{"line":302,"address":[867872,867456,868288,868348,867516,867932],"length":1,"stats":{"Line":8}},{"line":305,"address":[867835,867419,868251],"length":1,"stats":{"Line":4}},{"line":310,"address":[868608,868432,868562,868755,868584,868733],"length":1,"stats":{"Line":3}},{"line":317,"address":[],"length":0,"stats":{"Line":2}},{"line":321,"address":[],"length":0,"stats":{"Line":4}},{"line":322,"address":[825328,825360],"length":1,"stats":{"Line":1}},{"line":323,"address":[825365,825333],"length":1,"stats":{"Line":1}},{"line":324,"address":[825346,825378],"length":1,"stats":{"Line":1}},{"line":329,"address":[],"length":0,"stats":{"Line":1}},{"line":331,"address":[868536,868707],"length":1,"stats":{"Line":1}},{"line":333,"address":[],"length":0,"stats":{"Line":0}},{"line":337,"address":[868930,868952,869160,868768,868976,869138],"length":1,"stats":{"Line":2}},{"line":344,"address":[868825,868782,869033,868990],"length":1,"stats":{"Line":4}},{"line":345,"address":[],"length":0,"stats":{"Line":1}},{"line":350,"address":[],"length":0,"stats":{"Line":2}},{"line":351,"address":[825552,825520],"length":1,"stats":{"Line":0}},{"line":352,"address":[825525,825557],"length":1,"stats":{"Line":0}},{"line":353,"address":[],"length":0,"stats":{"Line":0}},{"line":358,"address":[],"length":0,"stats":{"Line":1}},{"line":360,"address":[868904,869112],"length":1,"stats":{"Line":1}},{"line":362,"address":[869124,868916],"length":1,"stats":{"Line":1}},{"line":397,"address":[869328,869600,869578,869184,869312,869434,869456,869290,869472],"length":1,"stats":{"Line":3}},{"line":399,"address":[],"length":0,"stats":{"Line":3}},{"line":400,"address":[869412,869268,869556],"length":1,"stats":{"Line":3}},{"line":448,"address":[],"length":0,"stats":{"Line":0}},{"line":450,"address":[],"length":0,"stats":{"Line":0}},{"line":451,"address":[],"length":0,"stats":{"Line":0}},{"line":453,"address":[],"length":0,"stats":{"Line":0}},{"line":473,"address":[],"length":0,"stats":{"Line":1}},{"line":474,"address":[869630],"length":1,"stats":{"Line":1}},{"line":475,"address":[],"length":0,"stats":{"Line":0}},{"line":480,"address":[869712,870081],"length":1,"stats":{"Line":1}},{"line":482,"address":[869834,869762],"length":1,"stats":{"Line":2}},{"line":486,"address":[869886,869936],"length":1,"stats":{"Line":2}},{"line":487,"address":[],"length":0,"stats":{"Line":0}},{"line":490,"address":[869947],"length":1,"stats":{"Line":1}},{"line":493,"address":[870128,870288,870440,870275,870418,870253],"length":1,"stats":{"Line":3}},{"line":500,"address":[870142,870307],"length":1,"stats":{"Line":2}},{"line":504,"address":[],"length":0,"stats":{"Line":4}},{"line":505,"address":[],"length":0,"stats":{"Line":1}},{"line":506,"address":[],"length":0,"stats":{"Line":1}},{"line":507,"address":[825762,825730],"length":1,"stats":{"Line":1}},{"line":512,"address":[],"length":0,"stats":{"Line":1}},{"line":514,"address":[870227,870392],"length":1,"stats":{"Line":1}},{"line":516,"address":[],"length":0,"stats":{"Line":0}},{"line":520,"address":[870464,870672,870834,870856,870626,870648],"length":1,"stats":{"Line":2}},{"line":527,"address":[870478,870521,870686,870729],"length":1,"stats":{"Line":4}},{"line":528,"address":[],"length":0,"stats":{"Line":1}},{"line":533,"address":[],"length":0,"stats":{"Line":2}},{"line":534,"address":[],"length":0,"stats":{"Line":0}},{"line":535,"address":[825909,825941],"length":1,"stats":{"Line":0}},{"line":536,"address":[],"length":0,"stats":{"Line":0}},{"line":541,"address":[],"length":0,"stats":{"Line":1}},{"line":543,"address":[870808,870600],"length":1,"stats":{"Line":1}},{"line":545,"address":[],"length":0,"stats":{"Line":1}},{"line":582,"address":[],"length":0,"stats":{"Line":1}},{"line":584,"address":[],"length":0,"stats":{"Line":1}},{"line":585,"address":[],"length":0,"stats":{"Line":1}},{"line":626,"address":[],"length":0,"stats":{"Line":0}},{"line":628,"address":[],"length":0,"stats":{"Line":0}},{"line":629,"address":[],"length":0,"stats":{"Line":0}},{"line":631,"address":[],"length":0,"stats":{"Line":0}},{"line":649,"address":[],"length":0,"stats":{"Line":0}},{"line":650,"address":[],"length":0,"stats":{"Line":0}},{"line":651,"address":[],"length":0,"stats":{"Line":0}},{"line":671,"address":[],"length":0,"stats":{"Line":1}},{"line":672,"address":[],"length":0,"stats":{"Line":1}},{"line":698,"address":[],"length":0,"stats":{"Line":1}},{"line":699,"address":[],"length":0,"stats":{"Line":1}}],"covered":103,"coverable":128},{"path":["/","home","botahamec","Projects","happylock","src","poisonable.rs"],"content":"use std::marker::PhantomData;\nuse std::sync::atomic::AtomicBool;\n\nuse crate::ThreadKey;\n\nmod error;\nmod flag;\nmod guard;\nmod poisonable;\n\n/// A flag indicating if a lock is poisoned or not. The implementation differs\n/// depending on whether panics are set to unwind or abort.\n#[derive(Debug, Default)]\npub(crate) struct PoisonFlag(#[cfg(panic = \"unwind\")] AtomicBool);\n\n/// A wrapper around [`Lockable`] types which will enable poisoning.\n///\n/// A lock is \"poisoned\" when the thread panics while holding the lock. Once a\n/// lock is poisoned, all other threads are unable to access the data by\n/// default, because the data may be tainted (some invariant of the data might\n/// not be upheld).\n///\n/// The [`lock`] and [`try_lock`] methods return a [`Result`] which indicates\n/// whether the lock has been poisoned or not. The [`PoisonError`] type has an\n/// [`into_inner`] method which will return the guard that normally would have\n/// been returned for a successful lock. This allows access to the data,\n/// despite the lock being poisoned.\n///\n/// Alternatively, there is also a [`clear_poison`] method, which should\n/// indicate that all invariants of the underlying data are upheld, so that\n/// subsequent calls may still return [`Ok`].\n///\n/// [`Lockable`]: `crate::lockable::Lockable`\n/// [`lock`]: `Poisonable::lock`\n/// [`try_lock`]: `Poisonable::try_lock`\n/// [`into_inner`]: `PoisonError::into_inner`\n/// [`clear_poison`]: `Poisonable::clear_poison`\n#[derive(Debug, Default)]\npub struct Poisonable\u003cL\u003e {\n\tinner: L,\n\tpoisoned: PoisonFlag,\n}\n\n/// An RAII guard for a [`Poisonable`].\n///\n/// This is similar to a [`PoisonGuard`], except that it does not hold a\n/// [`Keyable`]\n///\n/// [`Keyable`]: `crate::Keyable`\npub struct PoisonRef\u003c'a, G\u003e {\n\tguard: G,\n\t#[cfg(panic = \"unwind\")]\n\tflag: \u0026'a PoisonFlag,\n\t_phantom: PhantomData\u003c\u0026'a ()\u003e,\n}\n\n/// An RAII guard for a [`Poisonable`].\n///\n/// This is created by calling methods like [`Poisonable::lock`].\npub struct PoisonGuard\u003c'a, G\u003e {\n\tguard: PoisonRef\u003c'a, G\u003e,\n\tkey: ThreadKey,\n}\n\n/// A type of error which can be returned when acquiring a [`Poisonable`] lock.\npub struct PoisonError\u003cGuard\u003e {\n\tguard: Guard,\n}\n\n/// An enumeration of possible errors associated with\n/// [`TryLockPoisonableResult`] which can occur while trying to acquire a lock\n/// (i.e.: [`Poisonable::try_lock`]).\npub enum TryLockPoisonableError\u003c'flag, G\u003e {\n\tPoisoned(PoisonError\u003cPoisonGuard\u003c'flag, G\u003e\u003e),\n\tWouldBlock(ThreadKey),\n}\n\n/// A type alias for the result of a lock method which can poisoned.\n///\n/// The [`Ok`] variant of this result indicates that the primitive was not\n/// poisoned, and the primitive was poisoned. Note that the [`Err`] variant\n/// *also* carries the associated guard, and it can be acquired through the\n/// [`into_inner`] method.\n///\n/// [`into_inner`]: `PoisonError::into_inner`\npub type PoisonResult\u003cGuard\u003e = Result\u003cGuard, PoisonError\u003cGuard\u003e\u003e;\n\n/// A type alias for the result of a nonblocking locking method.\n///\n/// For more information, see [`PoisonResult`]. A `TryLockPoisonableResult`\n/// doesn't necessarily hold the associated guard in the [`Err`] type as the\n/// lock might not have been acquired for other reasons.\npub type TryLockPoisonableResult\u003c'flag, G\u003e =\n\tResult\u003cPoisonGuard\u003c'flag, G\u003e, TryLockPoisonableError\u003c'flag, G\u003e\u003e;\n\n#[cfg(test)]\nmod tests {\n\tuse std::sync::Arc;\n\n\tuse super::*;\n\tuse crate::lockable::Lockable;\n\tuse crate::{LockCollection, Mutex, RwLock, ThreadKey};\n\n\t#[test]\n\tfn locking_poisoned_mutex_returns_error_in_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = LockCollection::new(Poisonable::new(Mutex::new(42)));\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet mut guard1 = mutex.lock(key);\n\t\t\t\tlet guard = guard1.as_deref_mut().unwrap();\n\t\t\t\tassert_eq!(**guard, 42);\n\t\t\t\tpanic!();\n\n\t\t\t\t#[allow(unreachable_code)]\n\t\t\t\tdrop(guard1);\n\t\t\t})\n\t\t\t.join()\n\t\t\t.unwrap_err();\n\t\t});\n\n\t\tlet error = mutex.lock(key);\n\t\tlet error = error.as_deref().unwrap_err();\n\t\tassert_eq!(***error.get_ref(), 42);\n\t}\n\n\t#[test]\n\tfn locking_poisoned_rwlock_returns_error_in_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = LockCollection::new(Poisonable::new(RwLock::new(42)));\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet mut guard1 = mutex.read(key);\n\t\t\t\tlet guard = guard1.as_deref_mut().unwrap();\n\t\t\t\tassert_eq!(**guard, 42);\n\t\t\t\tpanic!();\n\n\t\t\t\t#[allow(unreachable_code)]\n\t\t\t\tdrop(guard1);\n\t\t\t})\n\t\t\t.join()\n\t\t\t.unwrap_err();\n\t\t});\n\n\t\tlet error = mutex.read(key);\n\t\tlet error = error.as_deref().unwrap_err();\n\t\tassert_eq!(***error.get_ref(), 42);\n\t}\n\n\t#[test]\n\tfn non_poisoned_get_mut_is_ok() {\n\t\tlet mut mutex = Poisonable::new(Mutex::new(42));\n\t\tlet guard = mutex.get_mut();\n\t\tassert!(guard.is_ok());\n\t\tassert_eq!(*guard.unwrap(), 42);\n\t}\n\n\t#[test]\n\tfn non_poisoned_get_mut_is_err() {\n\t\tlet mut mutex = Poisonable::new(Mutex::new(42));\n\n\t\tlet _ = std::panic::catch_unwind(|| {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t#[allow(unused_variables)]\n\t\t\tlet guard = mutex.lock(key);\n\t\t\tpanic!();\n\t\t\t#[allow(unreachable_code)]\n\t\t\tdrop(guard);\n\t\t});\n\n\t\tlet guard = mutex.get_mut();\n\t\tassert!(guard.is_err());\n\t\tassert_eq!(**guard.unwrap_err().get_ref(), 42);\n\t}\n\n\t#[test]\n\tfn unpoisoned_into_inner() {\n\t\tlet mutex = Poisonable::new(Mutex::new(\"foo\"));\n\t\tassert_eq!(mutex.into_inner().unwrap(), \"foo\");\n\t}\n\n\t#[test]\n\tfn poisoned_into_inner() {\n\t\tlet mutex = Poisonable::from(Mutex::new(\"foo\"));\n\n\t\tstd::panic::catch_unwind(|| {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t#[allow(unused_variables)]\n\t\t\tlet guard = mutex.lock(key);\n\t\t\tpanic!();\n\t\t\t#[allow(unreachable_code)]\n\t\t\tdrop(guard);\n\t\t})\n\t\t.unwrap_err();\n\n\t\tlet error = mutex.into_inner().unwrap_err();\n\t\tassert_eq!(error.into_inner(), \"foo\");\n\t}\n\n\t#[test]\n\tfn unpoisoned_into_child() {\n\t\tlet mutex = Poisonable::new(Mutex::new(\"foo\"));\n\t\tassert_eq!(mutex.into_child().unwrap().into_inner(), \"foo\");\n\t}\n\n\t#[test]\n\tfn poisoned_into_child() {\n\t\tlet mutex = Poisonable::from(Mutex::new(\"foo\"));\n\n\t\tstd::panic::catch_unwind(|| {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t#[allow(unused_variables)]\n\t\t\tlet guard = mutex.lock(key);\n\t\t\tpanic!();\n\t\t\t#[allow(unreachable_code)]\n\t\t\tdrop(guard);\n\t\t})\n\t\t.unwrap_err();\n\n\t\tlet error = mutex.into_child().unwrap_err();\n\t\tassert_eq!(error.into_inner().into_inner(), \"foo\");\n\t}\n\n\t#[test]\n\tfn scoped_lock_can_poison() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = Poisonable::new(Mutex::new(42));\n\n\t\tlet r = std::panic::catch_unwind(|| {\n\t\t\tmutex.scoped_lock(key, |num| {\n\t\t\t\t*num.unwrap() = 56;\n\t\t\t\tpanic!();\n\t\t\t})\n\t\t});\n\t\tassert!(r.is_err());\n\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tassert!(mutex.is_poisoned());\n\t\tmutex.scoped_lock(key, |num| {\n\t\t\tlet Err(error) = num else { panic!() };\n\t\t\tmutex.clear_poison();\n\t\t\tlet guard = error.into_inner();\n\t\t\tassert_eq!(*guard, 56);\n\t\t});\n\t\tassert!(!mutex.is_poisoned());\n\t}\n\n\t#[test]\n\tfn scoped_try_lock_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = Poisonable::new(Mutex::new(42));\n\t\tlet guard = mutex.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = mutex.scoped_try_lock(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn scoped_try_lock_can_succeed() {\n\t\tlet rwlock = Poisonable::new(RwLock::new(42));\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = rwlock.scoped_try_lock(key, |guard| {\n\t\t\t\t\tassert_eq!(*guard.unwrap(), 42);\n\t\t\t\t});\n\t\t\t\tassert!(r.is_ok());\n\t\t\t});\n\t\t});\n\t}\n\n\t#[test]\n\tfn scoped_read_can_poison() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = Poisonable::new(RwLock::new(42));\n\n\t\tlet r = std::panic::catch_unwind(|| {\n\t\t\tmutex.scoped_read(key, |num| {\n\t\t\t\tassert_eq!(*num.unwrap(), 42);\n\t\t\t\tpanic!();\n\t\t\t})\n\t\t});\n\t\tassert!(r.is_err());\n\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tassert!(mutex.is_poisoned());\n\t\tmutex.scoped_read(key, |num| {\n\t\t\tlet Err(error) = num else { panic!() };\n\t\t\tmutex.clear_poison();\n\t\t\tlet guard = error.into_inner();\n\t\t\tassert_eq!(*guard, 42);\n\t\t});\n\t\tassert!(!mutex.is_poisoned());\n\t}\n\n\t#[test]\n\tfn scoped_try_read_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet rwlock = Poisonable::new(RwLock::new(42));\n\t\tlet guard = rwlock.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = rwlock.scoped_try_read(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn scoped_try_read_can_succeed() {\n\t\tlet rwlock = Poisonable::new(RwLock::new(42));\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = rwlock.scoped_try_read(key, |guard| {\n\t\t\t\t\tassert_eq!(*guard.unwrap(), 42);\n\t\t\t\t});\n\t\t\t\tassert!(r.is_ok());\n\t\t\t});\n\t\t});\n\t}\n\n\t#[test]\n\tfn display_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = Poisonable::new(Mutex::new(\"Hello, world!\"));\n\n\t\tlet guard = mutex.lock(key).unwrap();\n\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\");\n\t}\n\n\t#[test]\n\tfn ref_as_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = LockCollection::new(Poisonable::new(Mutex::new(\"foo\")));\n\t\tlet guard = collection.lock(key);\n\t\tlet Ok(ref guard) = guard.as_ref() else {\n\t\t\tpanic!()\n\t\t};\n\t\tassert_eq!(**guard.as_ref(), \"foo\");\n\t}\n\n\t#[test]\n\tfn ref_as_mut() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = LockCollection::new(Poisonable::new(Mutex::new(\"foo\")));\n\t\tlet mut guard1 = collection.lock(key);\n\t\tlet Ok(ref mut guard) = guard1.as_mut() else {\n\t\t\tpanic!()\n\t\t};\n\t\tlet guard = guard.as_mut();\n\t\t**guard = \"bar\";\n\n\t\tlet key = LockCollection::\u003cPoisonable\u003cMutex\u003c_\u003e\u003e\u003e::unlock(guard1);\n\t\tlet guard = collection.lock(key);\n\t\tlet guard = guard.as_deref().unwrap();\n\t\tassert_eq!(*guard.as_ref(), \"bar\");\n\t}\n\n\t#[test]\n\tfn guard_as_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = Poisonable::new(Mutex::new(\"foo\"));\n\t\tlet guard = collection.lock(key);\n\t\tlet Ok(ref guard) = guard.as_ref() else {\n\t\t\tpanic!()\n\t\t};\n\t\tassert_eq!(**guard.as_ref(), \"foo\");\n\t}\n\n\t#[test]\n\tfn guard_as_mut() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = Poisonable::new(Mutex::new(\"foo\"));\n\t\tlet mut guard1 = mutex.lock(key);\n\t\tlet Ok(ref mut guard) = guard1.as_mut() else {\n\t\t\tpanic!()\n\t\t};\n\t\tlet guard = guard.as_mut();\n\t\t**guard = \"bar\";\n\n\t\tlet key = Poisonable::\u003cMutex\u003c_\u003e\u003e::unlock(guard1.unwrap());\n\t\tlet guard = mutex.lock(key);\n\t\tlet guard = guard.as_deref().unwrap();\n\t\tassert_eq!(*guard, \"bar\");\n\t}\n\n\t#[test]\n\tfn deref_mut_in_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = LockCollection::new(Poisonable::new(Mutex::new(42)));\n\t\tlet mut guard1 = collection.lock(key);\n\t\tlet Ok(ref mut guard) = guard1.as_mut() else {\n\t\t\tpanic!()\n\t\t};\n\t\t// TODO make this more convenient\n\t\tassert_eq!(***guard, 42);\n\t\t***guard = 24;\n\n\t\tlet key = LockCollection::\u003cPoisonable\u003cMutex\u003c_\u003e\u003e\u003e::unlock(guard1);\n\t\t_ = collection.lock(key);\n\t}\n\n\t#[test]\n\tfn get_ptrs() {\n\t\tlet mutex = Mutex::new(5);\n\t\tlet poisonable = Poisonable::new(mutex);\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tpoisonable.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tassert!(std::ptr::addr_eq(lock_ptrs[0], \u0026poisonable.inner));\n\t}\n\n\t#[test]\n\tfn clear_poison_for_poisoned_mutex() {\n\t\tlet mutex = Arc::new(Poisonable::new(Mutex::new(0)));\n\t\tlet c_mutex = Arc::clone(\u0026mutex);\n\n\t\tlet _ = std::thread::spawn(move || {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\tlet _lock = c_mutex.lock(key).unwrap();\n\t\t\tpanic!(); // the mutex gets poisoned\n\t\t})\n\t\t.join();\n\n\t\tassert!(mutex.is_poisoned());\n\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet _ = mutex.lock(key).unwrap_or_else(|mut e| {\n\t\t\t**e.get_mut() = 1;\n\t\t\tmutex.clear_poison();\n\t\t\te.into_inner()\n\t\t});\n\n\t\tassert!(!mutex.is_poisoned());\n\t}\n\n\t#[test]\n\tfn clear_poison_for_poisoned_rwlock() {\n\t\tlet lock = Arc::new(Poisonable::new(RwLock::new(0)));\n\t\tlet c_mutex = Arc::clone(\u0026lock);\n\n\t\tlet _ = std::thread::spawn(move || {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\tlet lock = c_mutex.read(key).unwrap();\n\t\t\tassert_eq!(*lock, 42);\n\t\t\tpanic!(); // the mutex gets poisoned\n\t\t})\n\t\t.join();\n\n\t\tassert!(lock.is_poisoned());\n\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet _ = lock.lock(key).unwrap_or_else(|mut e| {\n\t\t\t**e.get_mut() = 1;\n\t\t\tlock.clear_poison();\n\t\t\te.into_inner()\n\t\t});\n\n\t\tassert!(!lock.is_poisoned());\n\t}\n\n\t#[test]\n\tfn error_as_ref() {\n\t\tlet mutex = Poisonable::new(Mutex::new(\"foo\"));\n\n\t\tlet _ = std::panic::catch_unwind(|| {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t#[allow(unused_variables)]\n\t\t\tlet guard = mutex.lock(key);\n\t\t\tpanic!();\n\n\t\t\t#[allow(unknown_lints)]\n\t\t\t#[allow(unreachable_code)]\n\t\t\tdrop(guard);\n\t\t});\n\n\t\tassert!(mutex.is_poisoned());\n\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet error = mutex.lock(key).unwrap_err();\n\t\tassert_eq!(\u0026***error.as_ref(), \"foo\");\n\t}\n\n\t#[test]\n\tfn error_as_mut() {\n\t\tlet mutex = Poisonable::new(Mutex::new(\"foo\"));\n\n\t\tlet _ = std::panic::catch_unwind(|| {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t#[allow(unused_variables)]\n\t\t\tlet guard = mutex.lock(key);\n\t\t\tpanic!();\n\n\t\t\t#[allow(unknown_lints)]\n\t\t\t#[allow(unreachable_code)]\n\t\t\tdrop(guard);\n\t\t});\n\n\t\tassert!(mutex.is_poisoned());\n\n\t\tlet key: ThreadKey = ThreadKey::get().unwrap();\n\t\tlet mut error = mutex.lock(key).unwrap_err();\n\t\tlet error1 = error.as_mut();\n\t\t**error1 = \"bar\";\n\t\tlet key = Poisonable::\u003cMutex\u003c_\u003e\u003e::unlock(error.into_inner());\n\n\t\tmutex.clear_poison();\n\t\tlet guard = mutex.lock(key).unwrap();\n\t\tassert_eq!(\u0026**guard, \"bar\");\n\t}\n\n\t#[test]\n\tfn try_error_from_lock_error() {\n\t\tlet mutex = Poisonable::new(Mutex::new(\"foo\"));\n\n\t\tlet _ = std::panic::catch_unwind(|| {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t#[allow(unused_variables)]\n\t\t\tlet guard = mutex.lock(key);\n\t\t\tpanic!();\n\n\t\t\t#[allow(unknown_lints)]\n\t\t\t#[allow(unreachable_code)]\n\t\t\tdrop(guard);\n\t\t});\n\n\t\tassert!(mutex.is_poisoned());\n\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet error = mutex.lock(key).unwrap_err();\n\t\tlet error = TryLockPoisonableError::from(error);\n\n\t\tlet TryLockPoisonableError::Poisoned(error) = error else {\n\t\t\tpanic!()\n\t\t};\n\t\tassert_eq!(\u0026**error.into_inner(), \"foo\");\n\t}\n\n\t#[test]\n\tfn new_poisonable_is_not_poisoned() {\n\t\tlet mutex = Poisonable::new(Mutex::new(42));\n\t\tassert!(!mutex.is_poisoned());\n\t}\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","src","rwlock","read_guard.rs"],"content":"use std::fmt::{Debug, Display};\nuse std::hash::Hash;\nuse std::marker::PhantomData;\nuse std::ops::Deref;\n\nuse lock_api::RawRwLock;\n\nuse crate::lockable::RawLock;\nuse crate::ThreadKey;\n\nuse super::{RwLock, RwLockReadGuard, RwLockReadRef};\n\n// These impls make things slightly easier because now you can use\n// `println!(\"{guard}\")` instead of `println!(\"{}\", *guard)`\n\n#[mutants::skip] // hashing involves PRNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Hash + ?Sized, R: RawRwLock\u003e Hash for RwLockReadRef\u003c'_, T, R\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.deref().hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug + ?Sized, R: RawRwLock\u003e Debug for RwLockReadRef\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: Display + ?Sized, R: RawRwLock\u003e Display for RwLockReadRef\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e Deref for RwLockReadRef\u003c'_, T, R\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t// safety: this is the only type that can use `value`, and there's\n\t\t// a reference to this type, so there cannot be any mutable\n\t\t// references to this value.\n\t\tunsafe { \u0026*self.0.data.get() }\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e AsRef\u003cT\u003e for RwLockReadRef\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e Drop for RwLockReadRef\u003c'_, T, R\u003e {\n\tfn drop(\u0026mut self) {\n\t\t// safety: this guard is being destroyed, so the data cannot be\n\t\t// accessed without locking again\n\t\tunsafe { self.0.raw_unlock_read() }\n\t}\n}\n\nimpl\u003c'a, T: ?Sized, R: RawRwLock\u003e RwLockReadRef\u003c'a, T, R\u003e {\n\t/// Creates an immutable reference for the underlying data of an [`RwLock`]\n\t/// without locking it or taking ownership of the key.\n\t#[must_use]\n\tpub(crate) const unsafe fn new(mutex: \u0026'a RwLock\u003cT, R\u003e) -\u003e Self {\n\t\tSelf(mutex, PhantomData)\n\t}\n}\n\n#[mutants::skip] // hashing involves PRNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Hash + ?Sized, R: RawRwLock\u003e Hash for RwLockReadGuard\u003c'_, T, R\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.deref().hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug + ?Sized, R: RawRwLock\u003e Debug for RwLockReadGuard\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: Display + ?Sized, R: RawRwLock\u003e Display for RwLockReadGuard\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e Deref for RwLockReadGuard\u003c'_, T, R\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t\u0026self.rwlock\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e AsRef\u003cT\u003e for RwLockReadGuard\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself\n\t}\n}\n\nimpl\u003c'a, T: ?Sized, R: RawRwLock\u003e RwLockReadGuard\u003c'a, T, R\u003e {\n\t/// Create a guard to the given mutex. Undefined if multiple guards to the\n\t/// same mutex exist at once.\n\t#[must_use]\n\tpub(super) const unsafe fn new(rwlock: \u0026'a RwLock\u003cT, R\u003e, thread_key: ThreadKey) -\u003e Self {\n\t\tSelf {\n\t\t\trwlock: RwLockReadRef(rwlock, PhantomData),\n\t\t\tthread_key,\n\t\t}\n\t}\n}\n\nunsafe impl\u003cT: ?Sized + Sync, R: RawRwLock + Sync\u003e Sync for RwLockReadRef\u003c'_, T, R\u003e {}\n","traces":[{"line":33,"address":[970880],"length":1,"stats":{"Line":1}},{"line":34,"address":[],"length":0,"stats":{"Line":1}},{"line":41,"address":[],"length":0,"stats":{"Line":3}},{"line":45,"address":[970933,970965],"length":1,"stats":{"Line":3}},{"line":50,"address":[],"length":0,"stats":{"Line":1}},{"line":51,"address":[],"length":0,"stats":{"Line":1}},{"line":56,"address":[240432,240416],"length":1,"stats":{"Line":3}},{"line":59,"address":[149253,149269,149285],"length":1,"stats":{"Line":3}},{"line":67,"address":[],"length":0,"stats":{"Line":3}},{"line":68,"address":[],"length":0,"stats":{"Line":0}},{"line":89,"address":[],"length":0,"stats":{"Line":1}},{"line":90,"address":[],"length":0,"stats":{"Line":1}},{"line":97,"address":[],"length":0,"stats":{"Line":1}},{"line":98,"address":[],"length":0,"stats":{"Line":1}},{"line":103,"address":[],"length":0,"stats":{"Line":1}},{"line":104,"address":[],"length":0,"stats":{"Line":1}},{"line":112,"address":[],"length":0,"stats":{"Line":1}},{"line":114,"address":[],"length":0,"stats":{"Line":0}}],"covered":16,"coverable":18},{"path":["/","home","botahamec","Projects","happylock","src","rwlock","read_lock.rs"],"content":"use std::fmt::Debug;\n\nuse lock_api::RawRwLock;\n\nuse crate::lockable::{Lockable, RawLock, Sharable};\nuse crate::{Keyable, ThreadKey};\n\nuse super::{ReadLock, RwLock, RwLockReadGuard, RwLockReadRef};\n\nunsafe impl\u003cT, R: RawRwLock\u003e RawLock for ReadLock\u003c'_, T, R\u003e {\n\tfn poison(\u0026self) {\n\t\tself.0.poison()\n\t}\n\n\tunsafe fn raw_write(\u0026self) {\n\t\tself.0.raw_read()\n\t}\n\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool {\n\t\tself.0.raw_try_read()\n\t}\n\n\tunsafe fn raw_unlock_write(\u0026self) {\n\t\tself.0.raw_unlock_read()\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tself.0.raw_read()\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tself.0.raw_try_read()\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tself.0.raw_unlock_read()\n\t}\n}\n\nunsafe impl\u003cT, R: RawRwLock\u003e Lockable for ReadLock\u003c'_, T, R\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= RwLockReadRef\u003c'g, T, R\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= \u0026'a T\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tptrs.push(self);\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tRwLockReadRef::new(self.as_ref())\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.0.data_ref()\n\t}\n}\n\nunsafe impl\u003cT, R: RawRwLock\u003e Sharable for ReadLock\u003c'_, T, R\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= RwLockReadRef\u003c'g, T, R\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= \u0026'a T\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tRwLockReadRef::new(self.as_ref())\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tself.0.data_ref()\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug, R: RawRwLock\u003e Debug for ReadLock\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t// safety: this is just a try lock, and the value is dropped\n\t\t// immediately after, so there's no risk of blocking ourselves\n\t\t// or any other threads\n\t\tif let Some(value) = unsafe { self.try_lock_no_key() } {\n\t\t\tf.debug_struct(\"ReadLock\").field(\"data\", \u0026\u0026*value).finish()\n\t\t} else {\n\t\t\tstruct LockedPlaceholder;\n\t\t\timpl Debug for LockedPlaceholder {\n\t\t\t\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t\t\t\tf.write_str(\"\u003clocked\u003e\")\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tf.debug_struct(\"ReadLock\")\n\t\t\t\t.field(\"data\", \u0026LockedPlaceholder)\n\t\t\t\t.finish()\n\t\t}\n\t}\n}\n\nimpl\u003c'l, T, R\u003e From\u003c\u0026'l RwLock\u003cT, R\u003e\u003e for ReadLock\u003c'l, T, R\u003e {\n\tfn from(value: \u0026'l RwLock\u003cT, R\u003e) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\nimpl\u003cT: ?Sized, R\u003e AsRef\u003cRwLock\u003cT, R\u003e\u003e for ReadLock\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026RwLock\u003cT, R\u003e {\n\t\tself.0\n\t}\n}\n\nimpl\u003c'l, T, R\u003e ReadLock\u003c'l, T, R\u003e {\n\t/// Creates a new `ReadLock` which accesses the given [`RwLock`]\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{rwlock::ReadLock, RwLock};\n\t///\n\t/// let lock = RwLock::new(5);\n\t/// let read_lock = ReadLock::new(\u0026lock);\n\t/// ```\n\t#[must_use]\n\tpub const fn new(rwlock: \u0026'l RwLock\u003cT, R\u003e) -\u003e Self {\n\t\tSelf(rwlock)\n\t}\n}\n\nimpl\u003cT, R: RawRwLock\u003e ReadLock\u003c'_, T, R\u003e {\n\tpub fn scoped_lock\u003c'a, Ret\u003e(\u0026'a self, key: impl Keyable, f: impl Fn(\u0026'a T) -\u003e Ret) -\u003e Ret {\n\t\tself.0.scoped_read(key, f)\n\t}\n\n\tpub fn scoped_try_lock\u003c'a, Key: Keyable, Ret\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(\u0026'a T) -\u003e Ret,\n\t) -\u003e Result\u003cRet, Key\u003e {\n\t\tself.0.scoped_try_read(key, f)\n\t}\n\n\t/// Locks the underlying [`RwLock`] with shared read access, blocking the\n\t/// current thread until it can be acquired.\n\t///\n\t/// The calling thread will be blocked until there are no more writers\n\t/// which hold the lock. There may be other readers currently inside the\n\t/// lock when this method returns.\n\t///\n\t/// Returns an RAII guard which will release this thread's shared access\n\t/// once it is dropped.\n\t///\n\t/// Because this method takes a [`ThreadKey`], it's not possible for this\n\t/// method to cause a deadlock.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::rwlock::ReadLock;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock: RwLock\u003c_\u003e = RwLock::new(1);\n\t/// let reader = ReadLock::new(\u0026lock);\n\t///\n\t/// let n = reader.lock(key);\n\t/// assert_eq!(*n, 1);\n\t/// ```\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\t#[must_use]\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e RwLockReadGuard\u003c'_, T, R\u003e {\n\t\tself.0.read(key)\n\t}\n\n\t/// Attempts to acquire the underlying [`RwLock`] with shared read access\n\t/// without blocking.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// shared access when it is dropped.\n\t///\n\t/// This function does not provide any guarantees with respect to the\n\t/// ordering of whether contentious readers or writers will acquire the\n\t/// lock first.\n\t///\n\t/// # Errors\n\t///\n\t/// If the `RwLock` could not be acquired because it was already locked\n\t/// exclusively, then an error will be returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::rwlock::ReadLock;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(1);\n\t/// let reader = ReadLock::new(\u0026lock);\n\t///\n\t/// match reader.try_lock(key) {\n\t/// Ok(n) =\u003e assert_eq!(*n, 1),\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t/// ```\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e Result\u003cRwLockReadGuard\u003c'_, T, R\u003e, ThreadKey\u003e {\n\t\tself.0.try_read(key)\n\t}\n\n\t/// Attempts to create an exclusive lock without a key. Locking this\n\t/// without exclusive access to the key is undefined behavior.\n\tpub(crate) unsafe fn try_lock_no_key(\u0026self) -\u003e Option\u003cRwLockReadRef\u003c'_, T, R\u003e\u003e {\n\t\tself.0.try_read_no_key()\n\t}\n\n\t/// Immediately drops the guard, and consequently releases the shared lock\n\t/// on the underlying [`RwLock`].\n\t///\n\t/// This function is equivalent to calling [`drop`] on the guard, except\n\t/// that it returns the key that was used to create it. Alternately, the\n\t/// guard will be automatically dropped when it goes out of scope.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::rwlock::ReadLock;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(0);\n\t/// let reader = ReadLock::new(\u0026lock);\n\t///\n\t/// let mut guard = reader.lock(key);\n\t/// assert_eq!(*guard, 0);\n\t/// let key = ReadLock::unlock(guard);\n\t/// ```\n\t#[must_use]\n\tpub fn unlock(guard: RwLockReadGuard\u003c'_, T, R\u003e) -\u003e ThreadKey {\n\t\tRwLock::unlock_read(guard)\n\t}\n}\n","traces":[{"line":11,"address":[969488,969504],"length":1,"stats":{"Line":1}},{"line":12,"address":[],"length":0,"stats":{"Line":1}},{"line":15,"address":[],"length":0,"stats":{"Line":1}},{"line":16,"address":[],"length":0,"stats":{"Line":1}},{"line":19,"address":[969552,969584],"length":1,"stats":{"Line":1}},{"line":20,"address":[],"length":0,"stats":{"Line":1}},{"line":23,"address":[969616,969632],"length":1,"stats":{"Line":1}},{"line":24,"address":[],"length":0,"stats":{"Line":1}},{"line":27,"address":[],"length":0,"stats":{"Line":1}},{"line":28,"address":[],"length":0,"stats":{"Line":1}},{"line":31,"address":[],"length":0,"stats":{"Line":1}},{"line":32,"address":[],"length":0,"stats":{"Line":1}},{"line":35,"address":[],"length":0,"stats":{"Line":1}},{"line":36,"address":[],"length":0,"stats":{"Line":1}},{"line":51,"address":[],"length":0,"stats":{"Line":2}},{"line":52,"address":[969801,969865],"length":1,"stats":{"Line":2}},{"line":55,"address":[],"length":0,"stats":{"Line":1}},{"line":56,"address":[],"length":0,"stats":{"Line":1}},{"line":59,"address":[],"length":0,"stats":{"Line":1}},{"line":60,"address":[],"length":0,"stats":{"Line":1}},{"line":75,"address":[],"length":0,"stats":{"Line":1}},{"line":76,"address":[],"length":0,"stats":{"Line":1}},{"line":79,"address":[],"length":0,"stats":{"Line":1}},{"line":80,"address":[],"length":0,"stats":{"Line":1}},{"line":109,"address":[],"length":0,"stats":{"Line":1}},{"line":110,"address":[],"length":0,"stats":{"Line":1}},{"line":115,"address":[],"length":0,"stats":{"Line":1}},{"line":116,"address":[],"length":0,"stats":{"Line":1}},{"line":132,"address":[970048,970032],"length":1,"stats":{"Line":2}},{"line":133,"address":[],"length":0,"stats":{"Line":0}},{"line":138,"address":[],"length":0,"stats":{"Line":1}},{"line":139,"address":[],"length":0,"stats":{"Line":1}},{"line":142,"address":[970096],"length":1,"stats":{"Line":1}},{"line":147,"address":[970105],"length":1,"stats":{"Line":1}},{"line":181,"address":[],"length":0,"stats":{"Line":1}},{"line":182,"address":[],"length":0,"stats":{"Line":1}},{"line":216,"address":[],"length":0,"stats":{"Line":1}},{"line":217,"address":[],"length":0,"stats":{"Line":1}},{"line":222,"address":[],"length":0,"stats":{"Line":0}},{"line":223,"address":[],"length":0,"stats":{"Line":0}},{"line":248,"address":[],"length":0,"stats":{"Line":1}},{"line":249,"address":[],"length":0,"stats":{"Line":1}}],"covered":39,"coverable":42},{"path":["/","home","botahamec","Projects","happylock","src","rwlock","rwlock.rs"],"content":"use std::cell::UnsafeCell;\nuse std::fmt::Debug;\nuse std::marker::PhantomData;\nuse std::panic::AssertUnwindSafe;\n\nuse lock_api::RawRwLock;\n\nuse crate::collection::utils;\nuse crate::handle_unwind::handle_unwind;\nuse crate::lockable::{\n\tLockable, LockableGetMut, LockableIntoInner, OwnedLockable, RawLock, Sharable,\n};\nuse crate::{Keyable, ThreadKey};\n\nuse super::{PoisonFlag, RwLock, RwLockReadGuard, RwLockReadRef, RwLockWriteGuard, RwLockWriteRef};\n\nunsafe impl\u003cT: ?Sized, R: RawRwLock\u003e RawLock for RwLock\u003cT, R\u003e {\n\tfn poison(\u0026self) {\n\t\tself.poison.poison();\n\t}\n\n\tunsafe fn raw_write(\u0026self) {\n\t\tassert!(\n\t\t\t!self.poison.is_poisoned(),\n\t\t\t\"The read-write lock has been killed\"\n\t\t);\n\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.lock_exclusive(), || self.poison())\n\t}\n\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool {\n\t\tif self.poison.is_poisoned() {\n\t\t\treturn false;\n\t\t}\n\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.try_lock_exclusive(), || self.poison())\n\t}\n\n\tunsafe fn raw_unlock_write(\u0026self) {\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.unlock_exclusive(), || self.poison())\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tassert!(\n\t\t\t!self.poison.is_poisoned(),\n\t\t\t\"The read-write lock has been killed\"\n\t\t);\n\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.lock_shared(), || self.poison())\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tif self.poison.is_poisoned() {\n\t\t\treturn false;\n\t\t}\n\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.try_lock_shared(), || self.poison())\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.unlock_shared(), || self.poison())\n\t}\n}\n\nunsafe impl\u003cT, R: RawRwLock\u003e Lockable for RwLock\u003cT, R\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= RwLockWriteRef\u003c'g, T, R\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= \u0026'a mut T\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tptrs.push(self);\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tRwLockWriteRef::new(self)\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.data.get().as_mut().unwrap_unchecked()\n\t}\n}\n\nunsafe impl\u003cT, R: RawRwLock\u003e Sharable for RwLock\u003cT, R\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= RwLockReadRef\u003c'g, T, R\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= \u0026'a T\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tRwLockReadRef::new(self)\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tself.data.get().as_ref().unwrap_unchecked()\n\t}\n}\n\nunsafe impl\u003cT: Send, R: RawRwLock\u003e OwnedLockable for RwLock\u003cT, R\u003e {}\n\nimpl\u003cT: Send, R: RawRwLock\u003e LockableIntoInner for RwLock\u003cT, R\u003e {\n\ttype Inner = T;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tself.into_inner()\n\t}\n}\n\nimpl\u003cT: Send, R: RawRwLock\u003e LockableGetMut for RwLock\u003cT, R\u003e {\n\ttype Inner\u003c'a\u003e\n\t\t= \u0026'a mut T\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tAsMut::as_mut(self)\n\t}\n}\n\nimpl\u003cT, R: RawRwLock\u003e RwLock\u003cT, R\u003e {\n\t/// Creates a new instance of an `RwLock\u003cT\u003e` which is unlocked.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::RwLock;\n\t///\n\t/// let lock = RwLock::new(5);\n\t/// ```\n\t#[must_use]\n\tpub const fn new(data: T) -\u003e Self {\n\t\tSelf {\n\t\t\tdata: UnsafeCell::new(data),\n\t\t\tpoison: PoisonFlag::new(),\n\t\t\traw: R::INIT,\n\t\t}\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug, R: RawRwLock\u003e Debug for RwLock\u003cT, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t// safety: this is just a try lock, and the value is dropped\n\t\t// immediately after, so there's no risk of blocking ourselves\n\t\t// or any other threads\n\t\tif let Some(value) = unsafe { self.try_read_no_key() } {\n\t\t\tf.debug_struct(\"RwLock\").field(\"data\", \u0026\u0026*value).finish()\n\t\t} else {\n\t\t\tstruct LockedPlaceholder;\n\t\t\timpl Debug for LockedPlaceholder {\n\t\t\t\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t\t\t\tf.write_str(\"\u003clocked\u003e\")\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tf.debug_struct(\"RwLock\")\n\t\t\t\t.field(\"data\", \u0026LockedPlaceholder)\n\t\t\t\t.finish()\n\t\t}\n\t}\n}\n\nimpl\u003cT: Default, R: RawRwLock\u003e Default for RwLock\u003cT, R\u003e {\n\tfn default() -\u003e Self {\n\t\tSelf::new(T::default())\n\t}\n}\n\nimpl\u003cT, R: RawRwLock\u003e From\u003cT\u003e for RwLock\u003cT, R\u003e {\n\tfn from(value: T) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\n// We don't need a `get_mut` because we don't have mutex poisoning. Hurray!\n// This is safe because you can't have a mutable reference to the lock if it's\n// locked. Being locked requires an immutable reference because of the guard.\nimpl\u003cT: ?Sized, R\u003e AsMut\u003cT\u003e for RwLock\u003cT, R\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself.data.get_mut()\n\t}\n}\n\nimpl\u003cT, R\u003e RwLock\u003cT, R\u003e {\n\t/// Consumes this `RwLock`, returning the underlying data.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t///\n\t/// let lock = RwLock::new(String::new());\n\t/// {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut s = lock.write(key);\n\t/// *s = \"modified\".to_owned();\n\t/// }\n\t/// assert_eq!(lock.into_inner(), \"modified\");\n\t/// ```\n\t#[must_use]\n\tpub fn into_inner(self) -\u003e T {\n\t\tself.data.into_inner()\n\t}\n}\n\nimpl\u003cT: ?Sized, R\u003e RwLock\u003cT, R\u003e {\n\t/// Returns a mutable reference to the underlying data.\n\t///\n\t/// Since this call borrows `RwLock` mutably, no actual locking is taking\n\t/// place. The mutable borrow statically guarantees that no locks exist.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, RwLock};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut mutex = RwLock::new(0);\n\t/// *mutex.get_mut() = 10;\n\t/// assert_eq!(*mutex.read(key), 10);\n\t/// ```\n\t#[must_use]\n\tpub fn get_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself.data.get_mut()\n\t}\n}\n\nimpl\u003cT, R: RawRwLock\u003e RwLock\u003cT, R\u003e {\n\tpub fn scoped_read\u003c'a, Ret\u003e(\u0026'a self, key: impl Keyable, f: impl Fn(\u0026'a T) -\u003e Ret) -\u003e Ret {\n\t\tutils::scoped_read(self, key, f)\n\t}\n\n\tpub fn scoped_try_read\u003c'a, Key: Keyable, Ret\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(\u0026'a T) -\u003e Ret,\n\t) -\u003e Result\u003cRet, Key\u003e {\n\t\tutils::scoped_try_read(self, key, f)\n\t}\n\n\tpub fn scoped_write\u003c'a, Ret\u003e(\u0026'a self, key: impl Keyable, f: impl Fn(\u0026'a mut T) -\u003e Ret) -\u003e Ret {\n\t\tutils::scoped_write(self, key, f)\n\t}\n\n\tpub fn scoped_try_write\u003c'a, Key: Keyable, Ret\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(\u0026'a mut T) -\u003e Ret,\n\t) -\u003e Result\u003cRet, Key\u003e {\n\t\tutils::scoped_try_write(self, key, f)\n\t}\n\n\t/// Locks this `RwLock` with shared read access, blocking the current\n\t/// thread until it can be acquired.\n\t///\n\t/// The calling thread will be blocked until there are no more writers\n\t/// which hold the lock. There may be other readers currently inside the\n\t/// lock when this method returns.\n\t///\n\t/// Returns an RAII guard which will release this thread's shared access\n\t/// once it is dropped.\n\t///\n\t/// Because this method takes a [`ThreadKey`], it's not possible for this\n\t/// method to cause a deadlock.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t/// use happylock::{RwLock, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = Arc::new(RwLock::new(1));\n\t/// let c_lock = Arc::clone(\u0026lock);\n\t///\n\t/// let n = lock.read(key);\n\t/// assert_eq!(*n, 1);\n\t///\n\t/// thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let r = c_lock.read(key);\n\t/// }).join().unwrap();\n\t/// ```\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\tpub fn read(\u0026self, key: ThreadKey) -\u003e RwLockReadGuard\u003c'_, T, R\u003e {\n\t\tunsafe {\n\t\t\tself.raw_read();\n\n\t\t\t// safety: the lock is locked first\n\t\t\tRwLockReadGuard::new(self, key)\n\t\t}\n\t}\n\n\t/// Attempts to acquire this `RwLock` with shared read access without\n\t/// blocking.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// shared access when it is dropped.\n\t///\n\t/// This function does not provide any guarantees with respect to the\n\t/// ordering of whether contentious readers or writers will acquire the\n\t/// lock first.\n\t///\n\t/// # Errors\n\t///\n\t/// If the `RwLock` could not be acquired because it was already locked\n\t/// exclusively, then an error will be returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(1);\n\t///\n\t/// match lock.try_read(key) {\n\t/// Ok(n) =\u003e assert_eq!(*n, 1),\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t/// ```\n\tpub fn try_read(\u0026self, key: ThreadKey) -\u003e Result\u003cRwLockReadGuard\u003c'_, T, R\u003e, ThreadKey\u003e {\n\t\tunsafe {\n\t\t\tif self.raw_try_read() {\n\t\t\t\t// safety: the lock is locked first\n\t\t\t\tOk(RwLockReadGuard::new(self, key))\n\t\t\t} else {\n\t\t\t\tErr(key)\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Attempts to create a shared lock without a key. Locking this without\n\t/// exclusive access to the key is undefined behavior.\n\tpub(crate) unsafe fn try_read_no_key(\u0026self) -\u003e Option\u003cRwLockReadRef\u003c'_, T, R\u003e\u003e {\n\t\tif self.raw_try_read() {\n\t\t\t// safety: the lock is locked first\n\t\t\tSome(RwLockReadRef(self, PhantomData))\n\t\t} else {\n\t\t\tNone\n\t\t}\n\t}\n\n\t/// Attempts to create an exclusive lock without a key. Locking this\n\t/// without exclusive access to the key is undefined behavior.\n\t#[cfg(test)]\n\tpub(crate) unsafe fn try_write_no_key(\u0026self) -\u003e Option\u003cRwLockWriteRef\u003c'_, T, R\u003e\u003e {\n\t\tif self.raw_try_write() {\n\t\t\t// safety: the lock is locked first\n\t\t\tSome(RwLockWriteRef(self, PhantomData))\n\t\t} else {\n\t\t\tNone\n\t\t}\n\t}\n\n\t/// Locks this `RwLock` with exclusive write access, blocking the current\n\t/// until it can be acquired.\n\t///\n\t/// This function will not return while other writers or readers currently\n\t/// have access to the lock.\n\t///\n\t/// Returns an RAII guard which will drop the write access of this `RwLock`\n\t/// when dropped.\n\t///\n\t/// Because this method takes a [`ThreadKey`], it's not possible for this\n\t/// method to cause a deadlock.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, RwLock};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(1);\n\t///\n\t/// match lock.try_write(key) {\n\t/// Ok(n) =\u003e assert_eq!(*n, 1),\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t/// ```\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\tpub fn write(\u0026self, key: ThreadKey) -\u003e RwLockWriteGuard\u003c'_, T, R\u003e {\n\t\tunsafe {\n\t\t\tself.raw_write();\n\n\t\t\t// safety: the lock is locked first\n\t\t\tRwLockWriteGuard::new(self, key)\n\t\t}\n\t}\n\n\t/// Attempts to lock this `RwLock` with exclusive write access.\n\t///\n\t/// This function does not block. If the lock could not be acquired at this\n\t/// time, then `None` is returned. Otherwise, an RAII guard is returned\n\t/// which will release the lock when it is dropped.\n\t///\n\t/// This function does not provide any guarantees with respect to the\n\t/// ordering of whether contentious readers or writers will acquire the\n\t/// lock first.\n\t///\n\t/// # Errors\n\t///\n\t/// If the `RwLock` could not be acquired because it was already locked,\n\t/// then an error will be returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(1);\n\t///\n\t/// let n = lock.read(key);\n\t/// assert_eq!(*n, 1);\n\t/// ```\n\tpub fn try_write(\u0026self, key: ThreadKey) -\u003e Result\u003cRwLockWriteGuard\u003c'_, T, R\u003e, ThreadKey\u003e {\n\t\tunsafe {\n\t\t\tif self.raw_try_write() {\n\t\t\t\t// safety: the lock is locked first\n\t\t\t\tOk(RwLockWriteGuard::new(self, key))\n\t\t\t} else {\n\t\t\t\tErr(key)\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Returns `true` if the rwlock is currently locked in any way\n\t#[cfg(test)]\n\tpub(crate) fn is_locked(\u0026self) -\u003e bool {\n\t\tself.raw.is_locked()\n\t}\n\n\t/// Immediately drops the guard, and consequently releases the shared lock.\n\t///\n\t/// This function is equivalent to calling [`drop`] on the guard, except\n\t/// that it returns the key that was used to create it. Alternately, the\n\t/// guard will be automatically dropped when it goes out of scope.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(0);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// assert_eq!(*guard, 0);\n\t/// let key = RwLock::unlock_read(guard);\n\t/// ```\n\t#[must_use]\n\tpub fn unlock_read(guard: RwLockReadGuard\u003c'_, T, R\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.rwlock);\n\t\tguard.thread_key\n\t}\n\n\t/// Immediately drops the guard, and consequently releases the exclusive\n\t/// lock.\n\t///\n\t/// This function is equivalent to calling [`drop`] on the guard, except\n\t/// that it returns the key that was used to create it. Alternately, the\n\t/// guard will be automatically dropped when it goes out of scope.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(0);\n\t///\n\t/// let mut guard = lock.write(key);\n\t/// *guard += 20;\n\t/// let key = RwLock::unlock_write(guard);\n\t/// ```\n\t#[must_use]\n\tpub fn unlock_write(guard: RwLockWriteGuard\u003c'_, T, R\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.rwlock);\n\t\tguard.thread_key\n\t}\n}\n\nunsafe impl\u003cR: RawRwLock + Send, T: ?Sized + Send\u003e Send for RwLock\u003cT, R\u003e {}\nunsafe impl\u003cR: RawRwLock + Sync, T: ?Sized + Send\u003e Sync for RwLock\u003cT, R\u003e {}\n","traces":[{"line":18,"address":[],"length":0,"stats":{"Line":5}},{"line":19,"address":[175957,175989],"length":1,"stats":{"Line":6}},{"line":22,"address":[176240,176352],"length":1,"stats":{"Line":6}},{"line":23,"address":[217141,217253],"length":1,"stats":{"Line":3}},{"line":24,"address":[],"length":0,"stats":{"Line":0}},{"line":25,"address":[],"length":0,"stats":{"Line":0}},{"line":29,"address":[966208,966320],"length":1,"stats":{"Line":6}},{"line":30,"address":[210309,210325,210304,210320],"length":1,"stats":{"Line":23}},{"line":33,"address":[216512,216592],"length":1,"stats":{"Line":7}},{"line":34,"address":[966414,966494],"length":1,"stats":{"Line":7}},{"line":35,"address":[216568,216648],"length":1,"stats":{"Line":3}},{"line":39,"address":[248737,248817,248653],"length":1,"stats":{"Line":5}},{"line":40,"address":[248658,248742,248822],"length":1,"stats":{"Line":15}},{"line":43,"address":[216736,216768],"length":1,"stats":{"Line":6}},{"line":45,"address":[966604,966572],"length":1,"stats":{"Line":6}},{"line":46,"address":[1008069,1008080,1008064,1008000,1008037,1008032,1008085,1008005],"length":1,"stats":{"Line":21}},{"line":49,"address":[138640],"length":1,"stats":{"Line":5}},{"line":50,"address":[966788,966676],"length":1,"stats":{"Line":1}},{"line":51,"address":[],"length":0,"stats":{"Line":0}},{"line":52,"address":[],"length":0,"stats":{"Line":0}},{"line":56,"address":[249169,249277,249377],"length":1,"stats":{"Line":5}},{"line":57,"address":[176054,176166],"length":1,"stats":{"Line":18}},{"line":60,"address":[138384],"length":1,"stats":{"Line":6}},{"line":61,"address":[216446,216366],"length":1,"stats":{"Line":6}},{"line":62,"address":[],"length":0,"stats":{"Line":1}},{"line":66,"address":[248497,248413,248577],"length":1,"stats":{"Line":6}},{"line":67,"address":[147589,147557,147525,147552,147520,147584,147605,147600],"length":1,"stats":{"Line":23}},{"line":70,"address":[216704,216672],"length":1,"stats":{"Line":5}},{"line":72,"address":[967020,967052],"length":1,"stats":{"Line":5}},{"line":73,"address":[967057,967025],"length":1,"stats":{"Line":17}},{"line":88,"address":[249920,250048,249984],"length":1,"stats":{"Line":12}},{"line":89,"address":[217401,217337],"length":1,"stats":{"Line":12}},{"line":92,"address":[967200,967216],"length":1,"stats":{"Line":4}},{"line":93,"address":[],"length":0,"stats":{"Line":4}},{"line":96,"address":[967280,967232],"length":1,"stats":{"Line":4}},{"line":97,"address":[],"length":0,"stats":{"Line":4}},{"line":112,"address":[138928],"length":1,"stats":{"Line":3}},{"line":113,"address":[],"length":0,"stats":{"Line":3}},{"line":116,"address":[],"length":0,"stats":{"Line":3}},{"line":117,"address":[217481,217529],"length":1,"stats":{"Line":3}},{"line":126,"address":[],"length":0,"stats":{"Line":1}},{"line":127,"address":[],"length":0,"stats":{"Line":1}},{"line":137,"address":[],"length":0,"stats":{"Line":1}},{"line":138,"address":[967477],"length":1,"stats":{"Line":1}},{"line":153,"address":[967671,967530,967616,967813,967776,967488],"length":1,"stats":{"Line":17}},{"line":155,"address":[],"length":0,"stats":{"Line":0}},{"line":156,"address":[],"length":0,"stats":{"Line":34}},{"line":187,"address":[],"length":0,"stats":{"Line":1}},{"line":188,"address":[],"length":0,"stats":{"Line":1}},{"line":193,"address":[967936,967984],"length":1,"stats":{"Line":2}},{"line":194,"address":[968000,967957],"length":1,"stats":{"Line":2}},{"line":202,"address":[],"length":0,"stats":{"Line":1}},{"line":203,"address":[968024],"length":1,"stats":{"Line":1}},{"line":224,"address":[],"length":0,"stats":{"Line":0}},{"line":225,"address":[968048],"length":1,"stats":{"Line":1}},{"line":246,"address":[],"length":0,"stats":{"Line":1}},{"line":247,"address":[968088],"length":1,"stats":{"Line":1}},{"line":252,"address":[968112,968144],"length":1,"stats":{"Line":2}},{"line":253,"address":[968157,968121],"length":1,"stats":{"Line":2}},{"line":256,"address":[968176],"length":1,"stats":{"Line":7}},{"line":261,"address":[],"length":0,"stats":{"Line":7}},{"line":264,"address":[],"length":0,"stats":{"Line":2}},{"line":265,"address":[968226,968253],"length":1,"stats":{"Line":2}},{"line":268,"address":[247856,247984,247952,247920,247888,247824],"length":1,"stats":{"Line":13}},{"line":273,"address":[247933,247901,247837,247869,247965,247997],"length":1,"stats":{"Line":13}},{"line":310,"address":[],"length":0,"stats":{"Line":1}},{"line":312,"address":[],"length":0,"stats":{"Line":1}},{"line":315,"address":[968365],"length":1,"stats":{"Line":1}},{"line":348,"address":[968432,968582,968560],"length":1,"stats":{"Line":1}},{"line":350,"address":[],"length":0,"stats":{"Line":4}},{"line":352,"address":[968553,968523],"length":1,"stats":{"Line":2}},{"line":354,"address":[968502],"length":1,"stats":{"Line":1}},{"line":361,"address":[],"length":0,"stats":{"Line":1}},{"line":362,"address":[],"length":0,"stats":{"Line":1}},{"line":364,"address":[968629],"length":1,"stats":{"Line":1}},{"line":366,"address":[],"length":0,"stats":{"Line":0}},{"line":373,"address":[],"length":0,"stats":{"Line":1}},{"line":374,"address":[],"length":0,"stats":{"Line":1}},{"line":376,"address":[],"length":0,"stats":{"Line":1}},{"line":378,"address":[],"length":0,"stats":{"Line":0}},{"line":409,"address":[175462,175248,175360,175334,175376,175488],"length":1,"stats":{"Line":6}},{"line":411,"address":[175390,175262],"length":1,"stats":{"Line":5}},{"line":414,"address":[],"length":0,"stats":{"Line":4}},{"line":444,"address":[],"length":0,"stats":{"Line":2}},{"line":446,"address":[],"length":0,"stats":{"Line":7}},{"line":448,"address":[],"length":0,"stats":{"Line":4}},{"line":450,"address":[],"length":0,"stats":{"Line":1}},{"line":457,"address":[969296,969312],"length":1,"stats":{"Line":2}},{"line":458,"address":[],"length":0,"stats":{"Line":2}},{"line":480,"address":[969380,969328],"length":1,"stats":{"Line":1}},{"line":481,"address":[],"length":0,"stats":{"Line":1}},{"line":482,"address":[],"length":0,"stats":{"Line":0}},{"line":505,"address":[],"length":0,"stats":{"Line":1}},{"line":506,"address":[],"length":0,"stats":{"Line":1}},{"line":507,"address":[],"length":0,"stats":{"Line":0}}],"covered":85,"coverable":95},{"path":["/","home","botahamec","Projects","happylock","src","rwlock","write_guard.rs"],"content":"use std::fmt::{Debug, Display};\nuse std::hash::Hash;\nuse std::marker::PhantomData;\nuse std::ops::{Deref, DerefMut};\n\nuse lock_api::RawRwLock;\n\nuse crate::lockable::RawLock;\nuse crate::ThreadKey;\n\nuse super::{RwLock, RwLockWriteGuard, RwLockWriteRef};\n\n// These impls make things slightly easier because now you can use\n// `println!(\"{guard}\")` instead of `println!(\"{}\", *guard)`\n\n#[mutants::skip] // hashing involves PRNG and is difficult to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Hash + ?Sized, R: RawRwLock\u003e Hash for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.deref().hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug + ?Sized, R: RawRwLock\u003e Debug for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: Display + ?Sized, R: RawRwLock\u003e Display for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e Deref for RwLockWriteRef\u003c'_, T, R\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t// safety: this is the only type that can use `value`, and there's\n\t\t// a reference to this type, so there cannot be any mutable\n\t\t// references to this value.\n\t\tunsafe { \u0026*self.0.data.get() }\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e DerefMut for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t// safety: this is the only type that can use `value`, and we have a\n\t\t// mutable reference to this type, so there cannot be any other\n\t\t// references to this value.\n\t\tunsafe { \u0026mut *self.0.data.get() }\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e AsRef\u003cT\u003e for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e AsMut\u003cT\u003e for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e Drop for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn drop(\u0026mut self) {\n\t\t// safety: this guard is being destroyed, so the data cannot be\n\t\t// accessed without locking again\n\t\tunsafe { self.0.raw_unlock_write() }\n\t}\n}\n\nimpl\u003c'a, T: ?Sized + 'a, R: RawRwLock\u003e RwLockWriteRef\u003c'a, T, R\u003e {\n\t/// Creates a reference to the underlying data of an [`RwLock`] without\n\t/// locking or taking ownership of the key.\n\t#[must_use]\n\tpub(crate) const unsafe fn new(mutex: \u0026'a RwLock\u003cT, R\u003e) -\u003e Self {\n\t\tSelf(mutex, PhantomData)\n\t}\n}\n\n#[mutants::skip] // hashing involves PRNG and is difficult to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Hash + ?Sized, R: RawRwLock\u003e Hash for RwLockWriteGuard\u003c'_, T, R\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.deref().hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug + ?Sized, R: RawRwLock\u003e Debug for RwLockWriteGuard\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: Display + ?Sized, R: RawRwLock\u003e Display for RwLockWriteGuard\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e Deref for RwLockWriteGuard\u003c'_, T, R\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t\u0026self.rwlock\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e DerefMut for RwLockWriteGuard\u003c'_, T, R\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t\u0026mut self.rwlock\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e AsRef\u003cT\u003e for RwLockWriteGuard\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e AsMut\u003cT\u003e for RwLockWriteGuard\u003c'_, T, R\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself\n\t}\n}\n\nimpl\u003c'a, T: ?Sized + 'a, R: RawRwLock\u003e RwLockWriteGuard\u003c'a, T, R\u003e {\n\t/// Create a guard to the given mutex. Undefined if multiple guards to the\n\t/// same mutex exist at once.\n\t#[must_use]\n\tpub(super) const unsafe fn new(rwlock: \u0026'a RwLock\u003cT, R\u003e, thread_key: ThreadKey) -\u003e Self {\n\t\tSelf {\n\t\t\trwlock: RwLockWriteRef(rwlock, PhantomData),\n\t\t\tthread_key,\n\t\t}\n\t}\n}\n\nunsafe impl\u003cT: ?Sized + Sync, R: RawRwLock + Sync\u003e Sync for RwLockWriteRef\u003c'_, T, R\u003e {}\n","traces":[{"line":33,"address":[971136],"length":1,"stats":{"Line":1}},{"line":34,"address":[],"length":0,"stats":{"Line":1}},{"line":41,"address":[],"length":0,"stats":{"Line":3}},{"line":45,"address":[138149],"length":1,"stats":{"Line":3}},{"line":50,"address":[138192],"length":1,"stats":{"Line":3}},{"line":54,"address":[],"length":0,"stats":{"Line":3}},{"line":59,"address":[],"length":0,"stats":{"Line":1}},{"line":60,"address":[],"length":0,"stats":{"Line":1}},{"line":65,"address":[],"length":0,"stats":{"Line":0}},{"line":66,"address":[],"length":0,"stats":{"Line":0}},{"line":71,"address":[],"length":0,"stats":{"Line":5}},{"line":74,"address":[244309,244293],"length":1,"stats":{"Line":6}},{"line":82,"address":[],"length":0,"stats":{"Line":4}},{"line":83,"address":[],"length":0,"stats":{"Line":0}},{"line":104,"address":[971360],"length":1,"stats":{"Line":1}},{"line":105,"address":[],"length":0,"stats":{"Line":1}},{"line":112,"address":[138176],"length":1,"stats":{"Line":3}},{"line":113,"address":[],"length":0,"stats":{"Line":3}},{"line":118,"address":[],"length":0,"stats":{"Line":2}},{"line":119,"address":[138229],"length":1,"stats":{"Line":2}},{"line":124,"address":[],"length":0,"stats":{"Line":1}},{"line":125,"address":[],"length":0,"stats":{"Line":1}},{"line":130,"address":[],"length":0,"stats":{"Line":1}},{"line":131,"address":[],"length":0,"stats":{"Line":1}},{"line":139,"address":[],"length":0,"stats":{"Line":4}},{"line":141,"address":[],"length":0,"stats":{"Line":0}}],"covered":22,"coverable":26},{"path":["/","home","botahamec","Projects","happylock","src","rwlock","write_lock.rs"],"content":"use std::fmt::Debug;\n\nuse lock_api::RawRwLock;\n\nuse crate::lockable::{Lockable, RawLock};\nuse crate::{Keyable, ThreadKey};\n\nuse super::{RwLock, RwLockWriteGuard, RwLockWriteRef, WriteLock};\n\nunsafe impl\u003cT, R: RawRwLock\u003e RawLock for WriteLock\u003c'_, T, R\u003e {\n\tfn poison(\u0026self) {\n\t\tself.0.poison()\n\t}\n\n\tunsafe fn raw_write(\u0026self) {\n\t\tself.0.raw_write()\n\t}\n\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool {\n\t\tself.0.raw_try_write()\n\t}\n\n\tunsafe fn raw_unlock_write(\u0026self) {\n\t\tself.0.raw_unlock_write()\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tself.0.raw_write()\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tself.0.raw_try_write()\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tself.0.raw_unlock_write()\n\t}\n}\n\nunsafe impl\u003cT, R: RawRwLock\u003e Lockable for WriteLock\u003c'_, T, R\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= RwLockWriteRef\u003c'g, T, R\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= \u0026'a mut T\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tptrs.push(self)\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tRwLockWriteRef::new(self.as_ref())\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.0.data_mut()\n\t}\n}\n\n// Technically, the exclusive locks can also be shared, but there's currently\n// no way to express that. I don't think I want to ever express that.\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug, R: RawRwLock\u003e Debug for WriteLock\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t// safety: this is just a try lock, and the value is dropped\n\t\t// immediately after, so there's no risk of blocking ourselves\n\t\t// or any other threads\n\t\t// It makes zero sense to try using an exclusive lock for this, so this\n\t\t// is the only time when WriteLock does a read.\n\t\tif let Some(value) = unsafe { self.0.try_read_no_key() } {\n\t\t\tf.debug_struct(\"WriteLock\").field(\"data\", \u0026\u0026*value).finish()\n\t\t} else {\n\t\t\tstruct LockedPlaceholder;\n\t\t\timpl Debug for LockedPlaceholder {\n\t\t\t\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t\t\t\tf.write_str(\"\u003clocked\u003e\")\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tf.debug_struct(\"WriteLock\")\n\t\t\t\t.field(\"data\", \u0026LockedPlaceholder)\n\t\t\t\t.finish()\n\t\t}\n\t}\n}\n\nimpl\u003c'l, T, R\u003e From\u003c\u0026'l RwLock\u003cT, R\u003e\u003e for WriteLock\u003c'l, T, R\u003e {\n\tfn from(value: \u0026'l RwLock\u003cT, R\u003e) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\nimpl\u003cT: ?Sized, R\u003e AsRef\u003cRwLock\u003cT, R\u003e\u003e for WriteLock\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026RwLock\u003cT, R\u003e {\n\t\tself.0\n\t}\n}\n\nimpl\u003c'l, T, R\u003e WriteLock\u003c'l, T, R\u003e {\n\t/// Creates a new `WriteLock` which accesses the given [`RwLock`]\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{rwlock::WriteLock, RwLock};\n\t///\n\t/// let lock = RwLock::new(5);\n\t/// let write_lock = WriteLock::new(\u0026lock);\n\t/// ```\n\t#[must_use]\n\tpub const fn new(rwlock: \u0026'l RwLock\u003cT, R\u003e) -\u003e Self {\n\t\tSelf(rwlock)\n\t}\n}\n\nimpl\u003cT, R: RawRwLock\u003e WriteLock\u003c'_, T, R\u003e {\n\tpub fn scoped_lock\u003c'a, Ret\u003e(\u0026'a self, key: impl Keyable, f: impl Fn(\u0026'a mut T) -\u003e Ret) -\u003e Ret {\n\t\tself.0.scoped_write(key, f)\n\t}\n\n\tpub fn scoped_try_lock\u003c'a, Key: Keyable, Ret\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(\u0026'a mut T) -\u003e Ret,\n\t) -\u003e Result\u003cRet, Key\u003e {\n\t\tself.0.scoped_try_write(key, f)\n\t}\n\n\t/// Locks the underlying [`RwLock`] with exclusive write access, blocking\n\t/// the current until it can be acquired.\n\t///\n\t/// This function will not return while other writers or readers currently\n\t/// have access to the lock.\n\t///\n\t/// Returns an RAII guard which will drop the write access of this `RwLock`\n\t/// when dropped.\n\t///\n\t/// Because this method takes a [`ThreadKey`], it's not possible for this\n\t/// method to cause a deadlock.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, RwLock};\n\t/// use happylock::rwlock::WriteLock;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(1);\n\t/// let writer = WriteLock::new(\u0026lock);\n\t///\n\t/// let mut n = writer.lock(key);\n\t/// *n += 2;\n\t/// ```\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\t#[must_use]\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e RwLockWriteGuard\u003c'_, T, R\u003e {\n\t\tself.0.write(key)\n\t}\n\n\t/// Attempts to lock the underlying [`RwLock`] with exclusive write access.\n\t///\n\t/// This function does not block. If the lock could not be acquired at this\n\t/// time, then `None` is returned. Otherwise, an RAII guard is returned\n\t/// which will release the lock when it is dropped.\n\t///\n\t/// This function does not provide any guarantees with respect to the\n\t/// ordering of whether contentious readers or writers will acquire the\n\t/// lock first.\n\t///\n\t/// # Errors\n\t///\n\t/// If the [`RwLock`] could not be acquired because it was already locked,\n\t/// then an error will be returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::rwlock::WriteLock;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(1);\n\t/// let writer = WriteLock::new(\u0026lock);\n\t///\n\t/// match writer.try_lock(key) {\n\t/// Ok(n) =\u003e assert_eq!(*n, 1),\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t/// ```\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e Result\u003cRwLockWriteGuard\u003c'_, T, R\u003e, ThreadKey\u003e {\n\t\tself.0.try_write(key)\n\t}\n\n\t// There's no `try_lock_no_key`. Instead, `try_read_no_key` is called on\n\t// the referenced `RwLock`.\n\n\t/// Immediately drops the guard, and consequently releases the exclusive\n\t/// lock on the underlying [`RwLock`].\n\t///\n\t/// This function is equivalent to calling [`drop`] on the guard, except\n\t/// that it returns the key that was used to create it. Alternately, the\n\t/// guard will be automatically dropped when it goes out of scope.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::rwlock::WriteLock;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(0);\n\t/// let writer = WriteLock::new(\u0026lock);\n\t///\n\t/// let mut guard = writer.lock(key);\n\t/// *guard += 20;\n\t/// let key = WriteLock::unlock(guard);\n\t/// ```\n\t#[must_use]\n\tpub fn unlock(guard: RwLockWriteGuard\u003c'_, T, R\u003e) -\u003e ThreadKey {\n\t\tRwLock::unlock_write(guard)\n\t}\n}\n","traces":[{"line":11,"address":[970224,970208],"length":1,"stats":{"Line":1}},{"line":12,"address":[],"length":0,"stats":{"Line":1}},{"line":15,"address":[],"length":0,"stats":{"Line":1}},{"line":16,"address":[],"length":0,"stats":{"Line":1}},{"line":19,"address":[970272,970304],"length":1,"stats":{"Line":1}},{"line":20,"address":[],"length":0,"stats":{"Line":1}},{"line":23,"address":[],"length":0,"stats":{"Line":1}},{"line":24,"address":[],"length":0,"stats":{"Line":1}},{"line":27,"address":[],"length":0,"stats":{"Line":0}},{"line":28,"address":[],"length":0,"stats":{"Line":0}},{"line":31,"address":[],"length":0,"stats":{"Line":0}},{"line":32,"address":[],"length":0,"stats":{"Line":0}},{"line":35,"address":[],"length":0,"stats":{"Line":0}},{"line":36,"address":[],"length":0,"stats":{"Line":0}},{"line":51,"address":[],"length":0,"stats":{"Line":2}},{"line":52,"address":[],"length":0,"stats":{"Line":2}},{"line":55,"address":[],"length":0,"stats":{"Line":1}},{"line":56,"address":[],"length":0,"stats":{"Line":1}},{"line":59,"address":[970656],"length":1,"stats":{"Line":1}},{"line":60,"address":[],"length":0,"stats":{"Line":1}},{"line":94,"address":[],"length":0,"stats":{"Line":1}},{"line":95,"address":[],"length":0,"stats":{"Line":1}},{"line":100,"address":[970688],"length":1,"stats":{"Line":1}},{"line":101,"address":[],"length":0,"stats":{"Line":1}},{"line":117,"address":[970704,970720],"length":1,"stats":{"Line":3}},{"line":118,"address":[],"length":0,"stats":{"Line":0}},{"line":123,"address":[],"length":0,"stats":{"Line":1}},{"line":124,"address":[],"length":0,"stats":{"Line":1}},{"line":127,"address":[970768],"length":1,"stats":{"Line":1}},{"line":132,"address":[970777],"length":1,"stats":{"Line":1}},{"line":163,"address":[],"length":0,"stats":{"Line":1}},{"line":164,"address":[],"length":0,"stats":{"Line":1}},{"line":197,"address":[],"length":0,"stats":{"Line":1}},{"line":198,"address":[],"length":0,"stats":{"Line":1}},{"line":226,"address":[],"length":0,"stats":{"Line":1}},{"line":227,"address":[],"length":0,"stats":{"Line":1}}],"covered":29,"coverable":36},{"path":["/","home","botahamec","Projects","happylock","src","rwlock.rs"],"content":"use std::cell::UnsafeCell;\nuse std::marker::PhantomData;\n\nuse lock_api::RawRwLock;\n\nuse crate::poisonable::PoisonFlag;\nuse crate::ThreadKey;\n\nmod rwlock;\n\nmod read_lock;\nmod write_lock;\n\nmod read_guard;\nmod write_guard;\n\n#[cfg(feature = \"spin\")]\npub type SpinRwLock\u003cT\u003e = RwLock\u003cT, spin::RwLock\u003c()\u003e\u003e;\n\n#[cfg(feature = \"parking_lot\")]\npub type ParkingRwLock\u003cT\u003e = RwLock\u003cT, parking_lot::RawRwLock\u003e;\n\n/// A reader-writer lock\n///\n/// This type of lock allows a number of readers or at most one writer at any\n/// point in time. The write portion of this lock typically allows modification\n/// of the underlying data (exclusive access) and the read portion of this lock\n/// typically allows for read-only access (shared access).\n///\n/// In comparison, a [`Mutex`] does not distinguish between readers or writers\n/// that acquire the lock, therefore blocking any threads waiting for the lock\n/// to become available. An `RwLock` will allow any number of readers to\n/// acquire the lock as long as a writer is not holding the lock.\n///\n/// The type parameter T represents the data that this lock protects. It is\n/// required that T satisfies [`Send`] to be shared across threads and [`Sync`]\n/// to allow concurrent access through readers. The RAII guard returned from\n/// the locking methods implement [`Deref`] (and [`DerefMut`] for the `write`\n/// methods) to allow access to the content of the lock.\n///\n/// Locking the mutex on a thread that already locked it is impossible, due to\n/// the requirement of the [`ThreadKey`]. Therefore, this will never deadlock.\n///\n/// [`ThreadKey`]: `crate::ThreadKey`\n/// [`Mutex`]: `crate::mutex::Mutex`\n/// [`Deref`]: `std::ops::Deref`\n/// [`DerefMut`]: `std::ops::DerefMut`\npub struct RwLock\u003cT: ?Sized, R\u003e {\n\traw: R,\n\tpoison: PoisonFlag,\n\tdata: UnsafeCell\u003cT\u003e,\n}\n\n/// Grants read access to an [`RwLock`]\n///\n/// This structure is designed to be used in a [`LockCollection`] to indicate\n/// that only read access is needed to the data.\n///\n/// [`LockCollection`]: `crate::LockCollection`\n#[repr(transparent)]\npub struct ReadLock\u003c'l, T: ?Sized, R\u003e(\u0026'l RwLock\u003cT, R\u003e);\n\n/// Grants write access to an [`RwLock`]\n///\n/// This structure is designed to be used in a [`LockCollection`] to indicate\n/// that write access is needed to the data.\n///\n/// [`LockCollection`]: `crate::LockCollection`\n#[repr(transparent)]\npub struct WriteLock\u003c'l, T: ?Sized, R\u003e(\u0026'l RwLock\u003cT, R\u003e);\n\n/// RAII structure that unlocks the shared read access to a [`RwLock`]\n///\n/// This is similar to [`RwLockReadRef`], except it does not hold a\n/// [`Keyable`].\npub struct RwLockReadRef\u003c'a, T: ?Sized, R: RawRwLock\u003e(\n\t\u0026'a RwLock\u003cT, R\u003e,\n\tPhantomData\u003cR::GuardMarker\u003e,\n);\n\n/// RAII structure that unlocks the exclusive write access to a [`RwLock`]\n///\n/// This is similar to [`RwLockWriteRef`], except it does not hold a\n/// [`Keyable`].\npub struct RwLockWriteRef\u003c'a, T: ?Sized, R: RawRwLock\u003e(\n\t\u0026'a RwLock\u003cT, R\u003e,\n\tPhantomData\u003cR::GuardMarker\u003e,\n);\n\n/// RAII structure used to release the shared read access of a lock when\n/// dropped.\n///\n/// This structure is created by the [`read`] and [`try_read`] methods on\n/// [`RwLock`].\n///\n/// [`read`]: `RwLock::read`\n/// [`try_read`]: `RwLock::try_read`\npub struct RwLockReadGuard\u003c'a, T: ?Sized, R: RawRwLock\u003e {\n\trwlock: RwLockReadRef\u003c'a, T, R\u003e,\n\tthread_key: ThreadKey,\n}\n\n/// RAII structure used to release the exclusive write access of a lock when\n/// dropped.\n///\n/// This structure is created by the [`write`] and [`try_write`] methods on\n/// [`RwLock`]\n///\n/// [`try_write`]: `RwLock::try_write`\npub struct RwLockWriteGuard\u003c'a, T: ?Sized, R: RawRwLock\u003e {\n\trwlock: RwLockWriteRef\u003c'a, T, R\u003e,\n\tthread_key: ThreadKey,\n}\n\n#[cfg(test)]\nmod tests {\n\tuse crate::lockable::Lockable;\n\tuse crate::lockable::RawLock;\n\tuse crate::LockCollection;\n\tuse crate::RwLock;\n\tuse crate::ThreadKey;\n\n\tuse super::*;\n\n\t#[test]\n\tfn unlocked_when_initialized() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\n\t\tassert!(!lock.is_locked());\n\t\tassert!(lock.try_write(key).is_ok());\n\t}\n\n\t#[test]\n\tfn read_lock_unlocked_when_initialized() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet reader = ReadLock::new(\u0026lock);\n\n\t\tassert!(reader.try_lock(key).is_ok());\n\t}\n\n\t#[test]\n\tfn read_lock_from_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::from(\"Hello, world!\");\n\t\tlet reader = ReadLock::from(\u0026lock);\n\n\t\tlet guard = reader.lock(key);\n\t\tassert_eq!(*guard, \"Hello, world!\");\n\t}\n\n\t#[test]\n\tfn read_lock_scoped_works() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(42);\n\t\tlet reader = ReadLock::new(\u0026lock);\n\n\t\treader.scoped_lock(\u0026mut key, |num| assert_eq!(*num, 42));\n\t}\n\n\t#[test]\n\tfn read_lock_scoped_try_fails_during_write() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(42);\n\t\tlet reader = ReadLock::new(\u0026lock);\n\t\tlet guard = lock.write(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = reader.scoped_try_lock(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn write_lock_unlocked_when_initialized() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet writer = WriteLock::new(\u0026lock);\n\n\t\tassert!(writer.try_lock(key).is_ok());\n\t}\n\n\t#[test]\n\tfn read_lock_get_ptrs() {\n\t\tlet rwlock = RwLock::new(5);\n\t\tlet readlock = ReadLock::new(\u0026rwlock);\n\t\tlet mut lock_ptrs = Vec::new();\n\t\treadlock.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tassert!(std::ptr::addr_eq(lock_ptrs[0], \u0026readlock));\n\t}\n\n\t#[test]\n\tfn write_lock_get_ptrs() {\n\t\tlet rwlock = RwLock::new(5);\n\t\tlet writelock = WriteLock::new(\u0026rwlock);\n\t\tlet mut lock_ptrs = Vec::new();\n\t\twritelock.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tassert!(std::ptr::addr_eq(lock_ptrs[0], \u0026writelock));\n\t}\n\n\t#[test]\n\tfn write_lock_scoped_works() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(42);\n\t\tlet writer = WriteLock::new(\u0026lock);\n\n\t\twriter.scoped_lock(\u0026mut key, |num| assert_eq!(*num, 42));\n\t}\n\n\t#[test]\n\tfn write_lock_scoped_try_fails_during_write() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(42);\n\t\tlet writer = WriteLock::new(\u0026lock);\n\t\tlet guard = lock.write(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = writer.scoped_try_lock(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn locked_after_read() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\n\t\tlet guard = lock.read(key);\n\n\t\tassert!(lock.is_locked());\n\t\tdrop(guard)\n\t}\n\n\t#[test]\n\tfn locked_after_using_read_lock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet reader = ReadLock::new(\u0026lock);\n\n\t\tlet guard = reader.lock(key);\n\n\t\tassert!(lock.is_locked());\n\t\tdrop(guard)\n\t}\n\n\t#[test]\n\tfn locked_after_write() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\n\t\tlet guard = lock.write(key);\n\n\t\tassert!(lock.is_locked());\n\t\tdrop(guard)\n\t}\n\n\t#[test]\n\tfn locked_after_using_write_lock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet writer = WriteLock::new(\u0026lock);\n\n\t\tlet guard = writer.lock(key);\n\n\t\tassert!(lock.is_locked());\n\t\tdrop(guard)\n\t}\n\n\t#[test]\n\tfn locked_after_scoped_write() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"Hello, world!\");\n\n\t\tlock.scoped_write(\u0026mut key, |guard| {\n\t\t\tassert!(lock.is_locked());\n\t\t\tassert_eq!(*guard, \"Hello, world!\");\n\n\t\t\tstd::thread::scope(|s| {\n\t\t\t\ts.spawn(|| {\n\t\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\t\tassert!(lock.try_read(key).is_err());\n\t\t\t\t});\n\t\t\t})\n\t\t})\n\t}\n\n\t#[test]\n\tfn get_mut_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mut lock = crate::RwLock::from(42);\n\n\t\tlet mut_ref = lock.get_mut();\n\t\t*mut_ref = 24;\n\n\t\tlock.scoped_read(key, |guard| assert_eq!(*guard, 24))\n\t}\n\n\t#[test]\n\tfn try_write_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"Hello\");\n\t\tlet guard = lock.write(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = lock.try_write(key);\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn try_read_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"Hello\");\n\t\tlet guard = lock.write(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = lock.try_read(key);\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn read_display_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet guard = lock.read(key);\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn write_display_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet guard = lock.write(key);\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn read_ref_display_works() {\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet guard = unsafe { lock.try_read_no_key().unwrap() };\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn write_ref_display_works() {\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet guard = unsafe { lock.try_write_no_key().unwrap() };\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn dropping_read_ref_releases_rwlock() {\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\n\t\tlet guard = unsafe { lock.try_read_no_key().unwrap() };\n\t\tdrop(guard);\n\n\t\tassert!(!lock.is_locked());\n\t}\n\n\t#[test]\n\tfn dropping_write_guard_releases_rwlock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\n\t\tlet guard = lock.write(key);\n\t\tdrop(guard);\n\n\t\tassert!(!lock.is_locked());\n\t}\n\n\t#[test]\n\tfn unlock_write() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"Hello, world\");\n\n\t\tlet mut guard = lock.write(key);\n\t\t*guard = \"Goodbye, world!\";\n\t\tlet key = RwLock::unlock_write(guard);\n\n\t\tlet guard = lock.read(key);\n\t\tassert_eq!(*guard, \"Goodbye, world!\");\n\t}\n\n\t#[test]\n\tfn unlock_read() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"Hello, world\");\n\n\t\tlet guard = lock.read(key);\n\t\tassert_eq!(*guard, \"Hello, world\");\n\t\tlet key = RwLock::unlock_read(guard);\n\n\t\tlet guard = lock.write(key);\n\t\tassert_eq!(*guard, \"Hello, world\");\n\t}\n\n\t#[test]\n\tfn unlock_read_lock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"Hello, world\");\n\t\tlet reader = ReadLock::new(\u0026lock);\n\n\t\tlet guard = reader.lock(key);\n\t\tlet key = ReadLock::unlock(guard);\n\n\t\tlock.write(key);\n\t}\n\n\t#[test]\n\tfn unlock_write_lock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"Hello, world\");\n\t\tlet writer = WriteLock::from(\u0026lock);\n\n\t\tlet guard = writer.lock(key);\n\t\tlet key = WriteLock::unlock(guard);\n\n\t\tlock.write(key);\n\t}\n\n\t#[test]\n\tfn read_lock_in_collection() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"hi\");\n\t\tlet collection = LockCollection::try_new(ReadLock::new(\u0026lock)).unwrap();\n\n\t\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard, \"hi\");\n\t\t});\n\t\tcollection.scoped_read(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard, \"hi\");\n\t\t});\n\t\tassert!(collection\n\t\t\t.scoped_try_lock(\u0026mut key, |guard| {\n\t\t\t\tassert_eq!(*guard, \"hi\");\n\t\t\t})\n\t\t\t.is_ok());\n\t\tassert!(collection\n\t\t\t.scoped_try_read(\u0026mut key, |guard| {\n\t\t\t\tassert_eq!(*guard, \"hi\");\n\t\t\t})\n\t\t\t.is_ok());\n\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(**guard, \"hi\");\n\n\t\tlet key = LockCollection::\u003cReadLock\u003c_, _\u003e\u003e::unlock(guard);\n\t\tlet guard = collection.read(key);\n\t\tassert_eq!(**guard, \"hi\");\n\n\t\tlet key = LockCollection::\u003cReadLock\u003c_, _\u003e\u003e::unlock(guard);\n\t\tlet guard = lock.write(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_lock(key);\n\t\t\t\tassert!(guard.is_err());\n\t\t\t});\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_read(key);\n\t\t\t\tassert!(guard.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn write_lock_in_collection() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"hi\");\n\t\tlet collection = LockCollection::try_new(WriteLock::new(\u0026lock)).unwrap();\n\n\t\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard, \"hi\");\n\t\t});\n\t\tassert!(collection\n\t\t\t.scoped_try_lock(\u0026mut key, |guard| {\n\t\t\t\tassert_eq!(*guard, \"hi\");\n\t\t\t})\n\t\t\t.is_ok());\n\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(**guard, \"hi\");\n\n\t\tlet key = LockCollection::\u003cWriteLock\u003c_, _\u003e\u003e::unlock(guard);\n\t\tlet guard = lock.write(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_lock(key);\n\t\t\t\tassert!(guard.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn read_ref_as_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = LockCollection::new(crate::RwLock::new(\"hi\"));\n\t\tlet guard = lock.read(key);\n\n\t\tassert_eq!(*(*guard).as_ref(), \"hi\");\n\t}\n\n\t#[test]\n\tfn read_guard_as_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"hi\");\n\t\tlet guard = lock.read(key);\n\n\t\tassert_eq!(*guard.as_ref(), \"hi\");\n\t}\n\n\t#[test]\n\tfn write_ref_as_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = LockCollection::new(crate::RwLock::new(\"hi\"));\n\t\tlet guard = lock.lock(key);\n\n\t\tassert_eq!(*(*guard).as_ref(), \"hi\");\n\t}\n\n\t#[test]\n\tfn write_guard_as_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"hi\");\n\t\tlet guard = lock.write(key);\n\n\t\tassert_eq!(*guard.as_ref(), \"hi\");\n\t}\n\n\t#[test]\n\tfn write_guard_as_mut() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"hi\");\n\t\tlet mut guard = lock.write(key);\n\n\t\tassert_eq!(*guard.as_mut(), \"hi\");\n\t\t*guard.as_mut() = \"foo\";\n\t\tassert_eq!(*guard.as_mut(), \"foo\");\n\t}\n\n\t#[test]\n\tfn poison_read_lock() {\n\t\tlet lock = crate::RwLock::new(\"hi\");\n\t\tlet reader = ReadLock::new(\u0026lock);\n\n\t\treader.poison();\n\t\tassert!(lock.poison.is_poisoned());\n\t}\n\n\t#[test]\n\tfn poison_write_lock() {\n\t\tlet lock = crate::RwLock::new(\"hi\");\n\t\tlet reader = WriteLock::new(\u0026lock);\n\n\t\treader.poison();\n\t\tassert!(lock.poison.is_poisoned());\n\t}\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","src","thread.rs"],"content":"use std::marker::PhantomData;\n\nmod scope;\n\n#[derive(Debug)]\npub struct Scope\u003c'scope, 'env: 'scope\u003e(PhantomData\u003c(\u0026'env (), \u0026'scope ())\u003e);\n\n#[derive(Debug)]\npub struct ScopedJoinHandle\u003c'scope, T\u003e {\n\thandle: std::thread::JoinHandle\u003cT\u003e,\n\t_phantom: PhantomData\u003c\u0026'scope ()\u003e,\n}\n\npub struct JoinHandle\u003cT\u003e {\n\thandle: std::thread::JoinHandle\u003cT\u003e,\n\tkey: crate::ThreadKey,\n}\n\npub struct ThreadBuilder(std::thread::Builder);\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","evil_mutex.rs"],"content":"use std::sync::Arc;\n\nuse happylock::collection::{BoxedLockCollection, RetryingLockCollection};\nuse happylock::mutex::Mutex;\nuse happylock::ThreadKey;\nuse lock_api::{GuardNoSend, RawMutex};\n\nstruct EvilMutex {\n\tinner: parking_lot::RawMutex,\n}\n\nunsafe impl RawMutex for EvilMutex {\n\t#[allow(clippy::declare_interior_mutable_const)]\n\tconst INIT: Self = Self {\n\t\tinner: parking_lot::RawMutex::INIT,\n\t};\n\n\ttype GuardMarker = GuardNoSend;\n\n\tfn lock(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn try_lock(\u0026self) -\u003e bool {\n\t\tself.inner.try_lock()\n\t}\n\n\tunsafe fn unlock(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n}\n\n#[test]\nfn boxed_mutexes() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet good_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(5));\n\tlet evil_mutex: Arc\u003cMutex\u003ci32, EvilMutex\u003e\u003e = Arc::new(Mutex::new(7));\n\tlet useless_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(10));\n\tlet c_good = Arc::clone(\u0026good_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\t_ = collection.lock(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(good_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n\tassert!(evil_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n}\n\n#[test]\nfn retrying_mutexes() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet good_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(5));\n\tlet evil_mutex: Arc\u003cMutex\u003ci32, EvilMutex\u003e\u003e = Arc::new(Mutex::new(7));\n\tlet useless_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(10));\n\tlet c_good = Arc::clone(\u0026good_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tRetryingLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\tcollection.lock(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(good_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n\tassert!(evil_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","evil_rwlock.rs"],"content":"use std::panic::AssertUnwindSafe;\nuse std::sync::Arc;\n\nuse happylock::collection::{BoxedLockCollection, RetryingLockCollection};\nuse happylock::rwlock::RwLock;\nuse happylock::ThreadKey;\nuse lock_api::{GuardNoSend, RawRwLock};\n\nstruct EvilRwLock {\n\tinner: parking_lot::RawRwLock,\n}\n\nunsafe impl RawRwLock for EvilRwLock {\n\t#[allow(clippy::declare_interior_mutable_const)]\n\tconst INIT: Self = Self {\n\t\tinner: parking_lot::RawRwLock::INIT,\n\t};\n\n\ttype GuardMarker = GuardNoSend;\n\n\tfn lock_shared(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn try_lock_shared(\u0026self) -\u003e bool {\n\t\tself.inner.try_lock_shared()\n\t}\n\n\tunsafe fn unlock_shared(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn lock_exclusive(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn try_lock_exclusive(\u0026self) -\u003e bool {\n\t\tself.inner.try_lock_exclusive()\n\t}\n\n\tunsafe fn unlock_exclusive(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n}\n\n#[test]\nfn boxed_rwlocks() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet good_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(5));\n\tlet evil_mutex: Arc\u003cRwLock\u003ci32, EvilRwLock\u003e\u003e = Arc::new(RwLock::new(7));\n\tlet useless_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(10));\n\tlet c_good = Arc::clone(\u0026good_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\t_ = collection.lock(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(good_mutex.scoped_try_write(\u0026mut key, |_| {}).is_ok());\n\tassert!(evil_mutex.scoped_try_write(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_write(\u0026mut key, |_| {}).is_ok());\n\n\tstd::thread::scope(|s| {\n\t\ts.spawn(|| {\n\t\t\tlet evil_mutex = AssertUnwindSafe(evil_mutex);\n\t\t\tlet r = std::panic::catch_unwind(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tevil_mutex.write(key);\n\t\t\t});\n\n\t\t\tassert!(r.is_err());\n\t\t});\n\n\t\ts.spawn(|| {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\tgood_mutex.write(key);\n\t\t});\n\t});\n}\n\n#[test]\nfn retrying_rwlocks() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet good_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(5));\n\tlet evil_mutex: Arc\u003cRwLock\u003ci32, EvilRwLock\u003e\u003e = Arc::new(RwLock::new(7));\n\tlet useless_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(10));\n\tlet c_good = Arc::clone(\u0026good_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tRetryingLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\tcollection.lock(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(good_mutex.scoped_try_write(\u0026mut key, |_| {}).is_ok());\n\tassert!(evil_mutex.scoped_try_write(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_write(\u0026mut key, |_| {}).is_ok());\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","evil_try_mutex.rs"],"content":"use std::sync::Arc;\n\nuse happylock::{\n\tcollection::{BoxedLockCollection, RetryingLockCollection},\n\tmutex::Mutex,\n\tThreadKey,\n};\nuse lock_api::{GuardNoSend, RawMutex};\n\nstruct EvilMutex {\n\tinner: parking_lot::RawMutex,\n}\n\nunsafe impl RawMutex for EvilMutex {\n\t#[allow(clippy::declare_interior_mutable_const)]\n\tconst INIT: Self = Self {\n\t\tinner: parking_lot::RawMutex::INIT,\n\t};\n\n\ttype GuardMarker = GuardNoSend;\n\n\tfn lock(\u0026self) {\n\t\tself.inner.lock()\n\t}\n\n\tfn try_lock(\u0026self) -\u003e bool {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tunsafe fn unlock(\u0026self) {\n\t\tself.inner.unlock()\n\t}\n}\n\n#[test]\nfn boxed_mutexes() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet good_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(5));\n\tlet evil_mutex: Arc\u003cMutex\u003ci32, EvilMutex\u003e\u003e = Arc::new(Mutex::new(7));\n\tlet useless_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(10));\n\tlet c_good = Arc::clone(\u0026good_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\tlet g = collection.try_lock(key);\n\t\tprintln!(\"{}\", g.unwrap().1);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(good_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n\tassert!(evil_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n}\n\n#[test]\nfn retrying_mutexes() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet good_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(5));\n\tlet evil_mutex: Arc\u003cMutex\u003ci32, EvilMutex\u003e\u003e = Arc::new(Mutex::new(7));\n\tlet useless_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(10));\n\tlet c_good = Arc::clone(\u0026good_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tRetryingLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\tlet _ = collection.try_lock(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(good_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n\tassert!(evil_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","evil_try_rwlock.rs"],"content":"use std::sync::Arc;\n\nuse happylock::collection::{BoxedLockCollection, RetryingLockCollection};\nuse happylock::rwlock::RwLock;\nuse happylock::ThreadKey;\nuse lock_api::{GuardNoSend, RawRwLock};\n\nstruct EvilRwLock {\n\tinner: parking_lot::RawRwLock,\n}\n\nunsafe impl RawRwLock for EvilRwLock {\n\t#[allow(clippy::declare_interior_mutable_const)]\n\tconst INIT: Self = Self {\n\t\tinner: parking_lot::RawRwLock::INIT,\n\t};\n\n\ttype GuardMarker = GuardNoSend;\n\n\tfn lock_shared(\u0026self) {\n\t\tself.inner.lock_shared()\n\t}\n\n\tfn try_lock_shared(\u0026self) -\u003e bool {\n\t\tpanic!(\"mwahahahaha\")\n\t}\n\n\tunsafe fn unlock_shared(\u0026self) {\n\t\tself.inner.unlock_shared()\n\t}\n\n\tfn lock_exclusive(\u0026self) {\n\t\tself.inner.lock_exclusive()\n\t}\n\n\tfn try_lock_exclusive(\u0026self) -\u003e bool {\n\t\tpanic!(\"mwahahahaha\")\n\t}\n\n\tunsafe fn unlock_exclusive(\u0026self) {\n\t\tself.inner.unlock_exclusive()\n\t}\n}\n\n#[test]\nfn boxed_rwlocks() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet good_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(5));\n\tlet evil_mutex: Arc\u003cRwLock\u003ci32, EvilRwLock\u003e\u003e = Arc::new(RwLock::new(7));\n\tlet useless_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(10));\n\tlet c_good = Arc::clone(\u0026good_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\tlet _ = collection.try_read(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(good_mutex.scoped_try_read(\u0026mut key, |_| {}).is_ok());\n\tassert!(evil_mutex.scoped_try_read(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_read(\u0026mut key, |_| {}).is_ok());\n}\n\n#[test]\nfn retrying_rwlocks() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet good_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(5));\n\tlet evil_mutex: Arc\u003cRwLock\u003ci32, EvilRwLock\u003e\u003e = Arc::new(RwLock::new(7));\n\tlet useless_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(10));\n\tlet c_good = Arc::clone(\u0026good_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tRetryingLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\t_ = collection.try_read(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(good_mutex.scoped_try_read(\u0026mut key, |_| {}).is_ok());\n\tassert!(evil_mutex.scoped_try_read(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_read(\u0026mut key, |_| {}).is_ok());\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","evil_unlock_mutex.rs"],"content":"use std::sync::Arc;\n\nuse happylock::collection::{BoxedLockCollection, RetryingLockCollection};\nuse happylock::mutex::Mutex;\nuse happylock::ThreadKey;\nuse lock_api::{GuardNoSend, RawMutex};\n\nstruct KindaEvilMutex {\n\tinner: parking_lot::RawMutex,\n}\n\nstruct EvilMutex {}\n\nunsafe impl RawMutex for KindaEvilMutex {\n\t#[allow(clippy::declare_interior_mutable_const)]\n\tconst INIT: Self = Self {\n\t\tinner: parking_lot::RawMutex::INIT,\n\t};\n\n\ttype GuardMarker = GuardNoSend;\n\n\tfn lock(\u0026self) {\n\t\tself.inner.lock()\n\t}\n\n\tfn try_lock(\u0026self) -\u003e bool {\n\t\tself.inner.try_lock()\n\t}\n\n\tunsafe fn unlock(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n}\n\nunsafe impl RawMutex for EvilMutex {\n\t#[allow(clippy::declare_interior_mutable_const)]\n\tconst INIT: Self = Self {};\n\n\ttype GuardMarker = GuardNoSend;\n\n\tfn lock(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn try_lock(\u0026self) -\u003e bool {\n\t\tpanic!(\"mwahahahaha\")\n\t}\n\n\tunsafe fn unlock(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n}\n\n#[test]\nfn boxed_mutexes() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet kinda_evil_mutex: Arc\u003cMutex\u003ci32, KindaEvilMutex\u003e\u003e = Arc::new(Mutex::new(5));\n\tlet evil_mutex: Arc\u003cMutex\u003ci32, EvilMutex\u003e\u003e = Arc::new(Mutex::new(7));\n\tlet useless_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(10));\n\tlet c_good = Arc::clone(\u0026kinda_evil_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\t_ = collection.lock(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(kinda_evil_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n\tassert!(evil_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n}\n\n#[test]\nfn retrying_mutexes() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet kinda_evil_mutex: Arc\u003cMutex\u003ci32, KindaEvilMutex\u003e\u003e = Arc::new(Mutex::new(5));\n\tlet evil_mutex: Arc\u003cMutex\u003ci32, EvilMutex\u003e\u003e = Arc::new(Mutex::new(7));\n\tlet useless_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(10));\n\tlet c_good = Arc::clone(\u0026kinda_evil_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tRetryingLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\tcollection.lock(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(kinda_evil_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n\tassert!(evil_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","evil_unlock_rwlock.rs"],"content":"use std::sync::Arc;\n\nuse happylock::collection::{BoxedLockCollection, RetryingLockCollection};\nuse happylock::rwlock::RwLock;\nuse happylock::ThreadKey;\nuse lock_api::{GuardNoSend, RawRwLock};\n\nstruct KindaEvilRwLock {\n\tinner: parking_lot::RawRwLock,\n}\n\nstruct EvilRwLock {}\n\nunsafe impl RawRwLock for KindaEvilRwLock {\n\t#[allow(clippy::declare_interior_mutable_const)]\n\tconst INIT: Self = Self {\n\t\tinner: parking_lot::RawRwLock::INIT,\n\t};\n\n\ttype GuardMarker = GuardNoSend;\n\n\tfn lock_shared(\u0026self) {\n\t\tself.inner.lock_shared()\n\t}\n\n\tfn try_lock_shared(\u0026self) -\u003e bool {\n\t\tself.inner.try_lock_shared()\n\t}\n\n\tunsafe fn unlock_shared(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn lock_exclusive(\u0026self) {\n\t\tself.inner.lock_exclusive()\n\t}\n\n\tfn try_lock_exclusive(\u0026self) -\u003e bool {\n\t\tself.inner.try_lock_exclusive()\n\t}\n\n\tunsafe fn unlock_exclusive(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n}\n\nunsafe impl RawRwLock for EvilRwLock {\n\t#[allow(clippy::declare_interior_mutable_const)]\n\tconst INIT: Self = Self {};\n\n\ttype GuardMarker = GuardNoSend;\n\n\tfn lock_shared(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn try_lock_shared(\u0026self) -\u003e bool {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tunsafe fn unlock_shared(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn lock_exclusive(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn try_lock_exclusive(\u0026self) -\u003e bool {\n\t\tpanic!(\"mwahahahaha\")\n\t}\n\n\tunsafe fn unlock_exclusive(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n}\n\n#[test]\nfn boxed_rwlocks() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet kinda_evil_mutex: RwLock\u003ci32, KindaEvilRwLock\u003e = RwLock::new(5);\n\tlet evil_mutex: RwLock\u003ci32, EvilRwLock\u003e = RwLock::new(7);\n\tlet useless_mutex: RwLock\u003ci32, parking_lot::RawRwLock\u003e = RwLock::new(10);\n\n\tlet r = std::thread::scope(|s| {\n\t\tlet r = s\n\t\t\t.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet collection =\n\t\t\t\t\tBoxedLockCollection::try_new((\u0026kinda_evil_mutex, \u0026evil_mutex, \u0026useless_mutex))\n\t\t\t\t\t\t.unwrap();\n\t\t\t\t_ = collection.read(key);\n\t\t\t})\n\t\t\t.join();\n\n\t\tr\n\t});\n\n\tassert!(r.is_err());\n\tassert!(kinda_evil_mutex.scoped_try_write(\u0026mut key, |_| {}).is_err());\n\tassert!(evil_mutex.scoped_try_write(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_write(\u0026mut key, |_| {}).is_ok());\n}\n\n#[test]\nfn retrying_rwlocks() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet kinda_evil_mutex: Arc\u003cRwLock\u003ci32, KindaEvilRwLock\u003e\u003e = Arc::new(RwLock::new(5));\n\tlet evil_mutex: Arc\u003cRwLock\u003ci32, EvilRwLock\u003e\u003e = Arc::new(RwLock::new(7));\n\tlet useless_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(10));\n\tlet c_good = Arc::clone(\u0026kinda_evil_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tRetryingLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\tcollection.read(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(kinda_evil_mutex.scoped_try_write(\u0026mut key, |_| {}).is_err());\n\tassert!(evil_mutex.scoped_try_write(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_write(\u0026mut key, |_| {}).is_ok());\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","forget.rs"],"content":"use happylock::{Mutex, ThreadKey};\n\n#[test]\nfn no_new_threadkey_when_forgetting_lock() {\n\tlet key = ThreadKey::get().unwrap();\n\tlet mutex = Mutex::new(\"foo\".to_string());\n\n\tlet guard = mutex.lock(key);\n\tstd::mem::forget(guard);\n\n\tassert!(ThreadKey::get().is_none());\n}\n\n#[test]\nfn no_new_threadkey_in_scoped_lock() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet mutex = Mutex::new(\"foo\".to_string());\n\n\tmutex.scoped_lock(\u0026mut key, |_| {\n\t\tassert!(ThreadKey::get().is_none());\n\t});\n\n\tmutex.lock(key);\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","retry.rs"],"content":"use std::time::Duration;\n\nuse happylock::{collection::RetryingLockCollection, Mutex, ThreadKey};\n\nstatic MUTEX_1: Mutex\u003ci32\u003e = Mutex::new(1);\nstatic MUTEX_2: Mutex\u003ci32\u003e = Mutex::new(2);\nstatic MUTEX_3: Mutex\u003ci32\u003e = Mutex::new(3);\n\nfn thread_1() {\n\tlet key = ThreadKey::get().unwrap();\n\tlet mut guard = MUTEX_2.lock(key);\n\tstd::thread::sleep(Duration::from_millis(100));\n\t*guard = 5;\n}\n\nfn thread_2() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tstd::thread::sleep(Duration::from_millis(50));\n\tlet collection = RetryingLockCollection::try_new([\u0026MUTEX_1, \u0026MUTEX_2, \u0026MUTEX_3]).unwrap();\n\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\tassert_eq!(*guard[0], 4);\n\t\tassert_eq!(*guard[1], 5);\n\t\tassert_eq!(*guard[2], 3);\n\t});\n}\n\nfn thread_3() {\n\tlet key = ThreadKey::get().unwrap();\n\tstd::thread::sleep(Duration::from_millis(75));\n\tlet mut guard = MUTEX_1.lock(key);\n\tstd::thread::sleep(Duration::from_millis(100));\n\t*guard = 4;\n}\n\nfn thread_4() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tstd::thread::sleep(Duration::from_millis(25));\n\tlet collection = RetryingLockCollection::try_new([\u0026MUTEX_1, \u0026MUTEX_2]).unwrap();\n\tassert!(collection.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n}\n\n#[test]\nfn retries() {\n\tlet t1 = std::thread::spawn(thread_1);\n\tlet t2 = std::thread::spawn(thread_2);\n\tlet t3 = std::thread::spawn(thread_3);\n\tlet t4 = std::thread::spawn(thread_4);\n\n\tt1.join().unwrap();\n\tt2.join().unwrap();\n\tt3.join().unwrap();\n\tt4.join().unwrap();\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","retry_rw.rs"],"content":"use std::time::Duration;\n\nuse happylock::{collection::RetryingLockCollection, RwLock, ThreadKey};\n\nstatic RWLOCK_1: RwLock\u003ci32\u003e = RwLock::new(1);\nstatic RWLOCK_2: RwLock\u003ci32\u003e = RwLock::new(2);\nstatic RWLOCK_3: RwLock\u003ci32\u003e = RwLock::new(3);\n\nfn thread_1() {\n\tlet key = ThreadKey::get().unwrap();\n\tlet mut guard = RWLOCK_2.write(key);\n\tstd::thread::sleep(Duration::from_millis(75));\n\tassert_eq!(*guard, 2);\n\t*guard = 5;\n}\n\nfn thread_2() {\n\tlet key = ThreadKey::get().unwrap();\n\tlet collection = RetryingLockCollection::try_new([\u0026RWLOCK_1, \u0026RWLOCK_2, \u0026RWLOCK_3]).unwrap();\n\tstd::thread::sleep(Duration::from_millis(25));\n\tlet guard = collection.read(key);\n\tassert_eq!(*guard[0], 1);\n\tassert_eq!(*guard[1], 5);\n\tassert_eq!(*guard[2], 3);\n}\n\nfn thread_3() {\n\tlet key = ThreadKey::get().unwrap();\n\tstd::thread::sleep(Duration::from_millis(50));\n\tlet guard = RWLOCK_1.write(key);\n\tstd::thread::sleep(Duration::from_millis(50));\n\tassert_eq!(*guard, 1);\n}\n\n#[test]\nfn retries() {\n\tlet t1 = std::thread::spawn(thread_1);\n\tlet t2 = std::thread::spawn(thread_2);\n\tlet t3 = std::thread::spawn(thread_3);\n\n\tt1.join().unwrap();\n\tt2.join().unwrap();\n\tt3.join().unwrap();\n}\n","traces":[],"covered":0,"coverable":0}]}; + var previousData = {"files":[{"path":["/","home","botahamec","Projects","happylock","examples","basic.rs"],"content":"use std::thread;\n\nuse happylock::{Mutex, ThreadKey};\n\nconst N: usize = 10;\n\nstatic DATA: Mutex\u003ci32\u003e = Mutex::new(0);\n\nfn main() {\n\tlet mut threads = Vec::new();\n\tfor _ in 0..N {\n\t\tlet th = thread::spawn(move || {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\tlet mut data = DATA.lock(key);\n\t\t\t*data += 1;\n\t\t});\n\t\tthreads.push(th);\n\t}\n\n\tfor th in threads {\n\t\t_ = th.join();\n\t}\n\n\tlet key = ThreadKey::get().unwrap();\n\tlet data = DATA.lock(key);\n\tprintln!(\"{data}\");\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","examples","dining_philosophers.rs"],"content":"use std::{thread, time::Duration};\n\nuse happylock::{collection, Mutex, ThreadKey};\n\nstatic PHILOSOPHERS: [Philosopher; 5] = [\n\tPhilosopher {\n\t\tname: \"Socrates\",\n\t\tleft: 0,\n\t\tright: 1,\n\t},\n\tPhilosopher {\n\t\tname: \"John Rawls\",\n\t\tleft: 1,\n\t\tright: 2,\n\t},\n\tPhilosopher {\n\t\tname: \"Jeremy Bentham\",\n\t\tleft: 2,\n\t\tright: 3,\n\t},\n\tPhilosopher {\n\t\tname: \"John Stuart Mill\",\n\t\tleft: 3,\n\t\tright: 4,\n\t},\n\tPhilosopher {\n\t\tname: \"Judith Butler\",\n\t\tleft: 4,\n\t\tright: 0,\n\t},\n];\n\nstatic FORKS: [Mutex\u003c()\u003e; 5] = [\n\tMutex::new(()),\n\tMutex::new(()),\n\tMutex::new(()),\n\tMutex::new(()),\n\tMutex::new(()),\n];\n\nstruct Philosopher {\n\tname: \u0026'static str,\n\tleft: usize,\n\tright: usize,\n}\n\nimpl Philosopher {\n\tfn cycle(\u0026self) {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tthread::sleep(Duration::from_secs(1));\n\n\t\t// safety: no philosopher asks for the same fork twice\n\t\tlet forks = [\u0026FORKS[self.left], \u0026FORKS[self.right]];\n\t\tlet forks = unsafe { collection::RefLockCollection::new_unchecked(\u0026forks) };\n\t\tlet forks = forks.lock(key);\n\t\tprintln!(\"{} is eating...\", self.name);\n\t\tthread::sleep(Duration::from_secs(1));\n\t\tprintln!(\"{} is done eating\", self.name);\n\t\tdrop(forks);\n\t}\n}\n\nfn main() {\n\tlet handles: Vec\u003c_\u003e = PHILOSOPHERS\n\t\t.iter()\n\t\t.map(|philosopher| thread::spawn(move || philosopher.cycle()))\n\t\t// The `collect` is absolutely necessary, because we're using lazy\n\t\t// iterators. If `collect` isn't used, then the thread won't spawn\n\t\t// until we try to join on it.\n\t\t.collect();\n\n\tfor handle in handles {\n\t\t_ = handle.join();\n\t}\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","examples","dining_philosophers_retry.rs"],"content":"use std::{thread, time::Duration};\n\nuse happylock::{collection, Mutex, ThreadKey};\n\nstatic PHILOSOPHERS: [Philosopher; 5] = [\n\tPhilosopher {\n\t\tname: \"Socrates\",\n\t\tleft: 0,\n\t\tright: 1,\n\t},\n\tPhilosopher {\n\t\tname: \"John Rawls\",\n\t\tleft: 1,\n\t\tright: 2,\n\t},\n\tPhilosopher {\n\t\tname: \"Jeremy Bentham\",\n\t\tleft: 2,\n\t\tright: 3,\n\t},\n\tPhilosopher {\n\t\tname: \"John Stuart Mill\",\n\t\tleft: 3,\n\t\tright: 4,\n\t},\n\tPhilosopher {\n\t\tname: \"Judith Butler\",\n\t\tleft: 4,\n\t\tright: 0,\n\t},\n];\n\nstatic FORKS: [Mutex\u003c()\u003e; 5] = [\n\tMutex::new(()),\n\tMutex::new(()),\n\tMutex::new(()),\n\tMutex::new(()),\n\tMutex::new(()),\n];\n\nstruct Philosopher {\n\tname: \u0026'static str,\n\tleft: usize,\n\tright: usize,\n}\n\nimpl Philosopher {\n\tfn cycle(\u0026self) {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tthread::sleep(Duration::from_secs(1));\n\n\t\t// safety: no philosopher asks for the same fork twice\n\t\tlet forks = [\u0026FORKS[self.left], \u0026FORKS[self.right]];\n\t\tlet forks = unsafe { collection::RetryingLockCollection::new_unchecked(\u0026forks) };\n\t\tlet forks = forks.lock(key);\n\t\tprintln!(\"{} is eating...\", self.name);\n\t\tthread::sleep(Duration::from_secs(1));\n\t\tprintln!(\"{} is done eating\", self.name);\n\t\tdrop(forks);\n\t}\n}\n\nfn main() {\n\tlet handles: Vec\u003c_\u003e = PHILOSOPHERS\n\t\t.iter()\n\t\t.map(|philosopher| thread::spawn(move || philosopher.cycle()))\n\t\t// The `collect` is absolutely necessary, because we're using lazy\n\t\t// iterators. If `collect` isn't used, then the thread won't spawn\n\t\t// until we try to join on it.\n\t\t.collect();\n\n\tfor handle in handles {\n\t\t_ = handle.join();\n\t}\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","examples","double_mutex.rs"],"content":"use std::thread;\n\nuse happylock::{collection::RefLockCollection, Mutex, ThreadKey};\n\nconst N: usize = 10;\n\nstatic DATA: (Mutex\u003ci32\u003e, Mutex\u003cString\u003e) = (Mutex::new(0), Mutex::new(String::new()));\n\nfn main() {\n\tlet mut threads = Vec::new();\n\tfor _ in 0..N {\n\t\tlet th = thread::spawn(move || {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\tlet lock = RefLockCollection::new(\u0026DATA);\n\t\t\tlet mut guard = lock.lock(key);\n\t\t\t*guard.1 = (100 - *guard.0).to_string();\n\t\t\t*guard.0 += 1;\n\t\t});\n\t\tthreads.push(th);\n\t}\n\n\tfor th in threads {\n\t\t_ = th.join();\n\t}\n\n\tlet key = ThreadKey::get().unwrap();\n\tlet data = RefLockCollection::new(\u0026DATA);\n\tlet data = data.lock(key);\n\tprintln!(\"{}\", data.0);\n\tprintln!(\"{}\", data.1);\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","examples","fibonacci.rs"],"content":"use happylock::{collection, LockCollection, Mutex, ThreadKey};\nuse std::thread;\n\nconst N: usize = 36;\n\nstatic DATA: [Mutex\u003ci32\u003e; 2] = [Mutex::new(0), Mutex::new(1)];\n\nfn main() {\n\tlet mut threads = Vec::new();\n\tfor _ in 0..N {\n\t\tlet th = thread::spawn(move || {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\n\t\t\t// a reference to a type that implements `OwnedLockable` will never\n\t\t\t// contain duplicates, so no duplicate checking is needed.\n\t\t\tlet collection = collection::RetryingLockCollection::new_ref(\u0026DATA);\n\t\t\tlet mut guard = collection.lock(key);\n\n\t\t\tlet x = *guard[1];\n\t\t\t*guard[1] += *guard[0];\n\t\t\t*guard[0] = x;\n\t\t});\n\t\tthreads.push(th);\n\t}\n\n\tfor thread in threads {\n\t\t_ = thread.join();\n\t}\n\n\tlet key = ThreadKey::get().unwrap();\n\tlet data = LockCollection::new_ref(\u0026DATA);\n\tlet data = data.lock(key);\n\tprintln!(\"{}\", data[0]);\n\tprintln!(\"{}\", data[1]);\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","examples","list.rs"],"content":"use std::thread;\n\nuse happylock::{collection::RefLockCollection, Mutex, ThreadKey};\n\nconst N: usize = 10;\n\nstatic DATA: [Mutex\u003cusize\u003e; 6] = [\n\tMutex::new(0),\n\tMutex::new(1),\n\tMutex::new(2),\n\tMutex::new(3),\n\tMutex::new(4),\n\tMutex::new(5),\n];\n\nstatic SEED: Mutex\u003cu32\u003e = Mutex::new(42);\n\nfn random(key: \u0026mut ThreadKey) -\u003e usize {\n\tSEED.scoped_lock(key, |seed| {\n\t\tlet x = *seed;\n\t\tlet x = x ^ (x \u003c\u003c 13);\n\t\tlet x = x ^ (x \u003e\u003e 17);\n\t\tlet x = x ^ (x \u003c\u003c 5);\n\t\t*seed = x;\n\t\tx as usize\n\t})\n}\n\nfn main() {\n\tlet mut threads = Vec::new();\n\tfor _ in 0..N {\n\t\tlet th = thread::spawn(move || {\n\t\t\tlet mut key = ThreadKey::get().unwrap();\n\t\t\tloop {\n\t\t\t\tlet mut data = Vec::new();\n\t\t\t\tfor _ in 0..3 {\n\t\t\t\t\tlet rand = random(\u0026mut key);\n\t\t\t\t\tdata.push(\u0026DATA[rand % 6]);\n\t\t\t\t}\n\n\t\t\t\tlet Some(lock) = RefLockCollection::try_new(\u0026data) else {\n\t\t\t\t\tcontinue;\n\t\t\t\t};\n\t\t\t\tlet mut guard = lock.lock(key);\n\t\t\t\t*guard[0] += *guard[1];\n\t\t\t\t*guard[1] += *guard[2];\n\t\t\t\t*guard[2] += *guard[0];\n\n\t\t\t\treturn;\n\t\t\t}\n\t\t});\n\t\tthreads.push(th);\n\t}\n\n\tfor th in threads {\n\t\t_ = th.join();\n\t}\n\n\tlet key = ThreadKey::get().unwrap();\n\tlet data = RefLockCollection::new(\u0026DATA);\n\tlet data = data.lock(key);\n\tfor val in \u0026*data {\n\t\tprintln!(\"{val}\");\n\t}\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","src","collection","boxed.rs"],"content":"use std::cell::UnsafeCell;\nuse std::fmt::Debug;\n\nuse crate::lockable::{Lockable, LockableIntoInner, OwnedLockable, RawLock, Sharable};\nuse crate::{Keyable, ThreadKey};\n\nuse super::utils::{\n\tordered_contains_duplicates, scoped_read, scoped_try_read, scoped_try_write, scoped_write,\n};\nuse super::{utils, BoxedLockCollection, LockGuard};\n\nunsafe impl\u003cL: Lockable\u003e RawLock for BoxedLockCollection\u003cL\u003e {\n\t#[mutants::skip] // this should never be called\n\t#[cfg(not(tarpaulin_include))]\n\tfn poison(\u0026self) {\n\t\tfor lock in \u0026self.locks {\n\t\t\tlock.poison();\n\t\t}\n\t}\n\n\tunsafe fn raw_write(\u0026self) {\n\t\tutils::ordered_write(self.locks())\n\t}\n\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool {\n\t\tprintln!(\"{}\", self.locks().len());\n\t\tutils::ordered_try_write(self.locks())\n\t}\n\n\tunsafe fn raw_unlock_write(\u0026self) {\n\t\tfor lock in self.locks() {\n\t\t\tlock.raw_unlock_write();\n\t\t}\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tutils::ordered_read(self.locks());\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tutils::ordered_try_read(self.locks())\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tfor lock in self.locks() {\n\t\t\tlock.raw_unlock_read();\n\t\t}\n\t}\n}\n\nunsafe impl\u003cL: Lockable\u003e Lockable for BoxedLockCollection\u003cL\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= L::Guard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= L::DataMut\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tptrs.push(self);\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tself.child().guard()\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.child().data_mut()\n\t}\n}\n\nunsafe impl\u003cL: Sharable\u003e Sharable for BoxedLockCollection\u003cL\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= L::ReadGuard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= L::DataRef\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tself.child().read_guard()\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tself.child().data_ref()\n\t}\n}\n\nunsafe impl\u003cL: OwnedLockable\u003e OwnedLockable for BoxedLockCollection\u003cL\u003e {}\n\n// LockableGetMut can't be implemented because that would create mutable and\n// immutable references to the same value at the same time.\n\nimpl\u003cL: LockableIntoInner\u003e LockableIntoInner for BoxedLockCollection\u003cL\u003e {\n\ttype Inner = L::Inner;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tLockableIntoInner::into_inner(self.into_child())\n\t}\n}\n\nimpl\u003cL\u003e IntoIterator for BoxedLockCollection\u003cL\u003e\nwhere\n\tL: IntoIterator,\n{\n\ttype Item = \u003cL as IntoIterator\u003e::Item;\n\ttype IntoIter = \u003cL as IntoIterator\u003e::IntoIter;\n\n\tfn into_iter(self) -\u003e Self::IntoIter {\n\t\tself.into_child().into_iter()\n\t}\n}\n\nimpl\u003c'a, L\u003e IntoIterator for \u0026'a BoxedLockCollection\u003cL\u003e\nwhere\n\t\u0026'a L: IntoIterator,\n{\n\ttype Item = \u003c\u0026'a L as IntoIterator\u003e::Item;\n\ttype IntoIter = \u003c\u0026'a L as IntoIterator\u003e::IntoIter;\n\n\tfn into_iter(self) -\u003e Self::IntoIter {\n\t\tself.child().into_iter()\n\t}\n}\n\nimpl\u003cL: OwnedLockable, I: FromIterator\u003cL\u003e + OwnedLockable\u003e FromIterator\u003cL\u003e\n\tfor BoxedLockCollection\u003cI\u003e\n{\n\tfn from_iter\u003cT: IntoIterator\u003cItem = L\u003e\u003e(iter: T) -\u003e Self {\n\t\tlet iter: I = iter.into_iter().collect();\n\t\tSelf::new(iter)\n\t}\n}\n\n// safety: the RawLocks must be send because they come from the Send Lockable\n#[allow(clippy::non_send_fields_in_send_ty)]\nunsafe impl\u003cL: Send\u003e Send for BoxedLockCollection\u003cL\u003e {}\nunsafe impl\u003cL: Sync\u003e Sync for BoxedLockCollection\u003cL\u003e {}\n\nimpl\u003cL\u003e Drop for BoxedLockCollection\u003cL\u003e {\n\t#[mutants::skip] // i can't test for a memory leak\n\t#[cfg(not(tarpaulin_include))]\n\tfn drop(\u0026mut self) {\n\t\tunsafe {\n\t\t\t// safety: this collection will never be locked again\n\t\t\tself.locks.clear();\n\t\t\t// safety: this was allocated using a box, and is now unique\n\t\t\tlet boxed: Box\u003cUnsafeCell\u003cL\u003e\u003e = Box::from_raw(self.data.cast_mut());\n\n\t\t\tdrop(boxed)\n\t\t}\n\t}\n}\n\nimpl\u003cT: ?Sized, L: AsRef\u003cT\u003e\u003e AsRef\u003cT\u003e for BoxedLockCollection\u003cL\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself.child().as_ref()\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cL: Debug\u003e Debug for BoxedLockCollection\u003cL\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tf.debug_struct(stringify!(BoxedLockCollection))\n\t\t\t.field(\"data\", \u0026self.data)\n\t\t\t.finish_non_exhaustive()\n\t}\n}\n\nimpl\u003cL: OwnedLockable + Default\u003e Default for BoxedLockCollection\u003cL\u003e {\n\tfn default() -\u003e Self {\n\t\tSelf::new(L::default())\n\t}\n}\n\nimpl\u003cL: OwnedLockable\u003e From\u003cL\u003e for BoxedLockCollection\u003cL\u003e {\n\tfn from(value: L) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\nimpl\u003cL\u003e BoxedLockCollection\u003cL\u003e {\n\t/// Gets the underlying collection, consuming this collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey, LockCollection};\n\t///\n\t/// let data1 = Mutex::new(42);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // data1 and data2 refer to distinct mutexes, so this won't panic\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = LockCollection::try_new(\u0026data).unwrap();\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let guard = lock.into_child().0.lock(key);\n\t/// assert_eq!(*guard, 42);\n\t/// ```\n\t#[must_use]\n\tpub fn into_child(mut self) -\u003e L {\n\t\tunsafe {\n\t\t\t// safety: this collection will never be used again\n\t\t\tstd::ptr::drop_in_place(\u0026mut self.locks);\n\t\t\t// safety: this was allocated using a box, and is now unique\n\t\t\tlet boxed: Box\u003cUnsafeCell\u003cL\u003e\u003e = Box::from_raw(self.data.cast_mut());\n\t\t\t// to prevent a double free\n\t\t\tstd::mem::forget(self);\n\n\t\t\tboxed.into_inner()\n\t\t}\n\t}\n\n\t// child_mut is immediate UB because it leads to mutable and immutable\n\t// references happening at the same time\n\n\t/// Gets an immutable reference to the underlying data\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey, LockCollection};\n\t///\n\t/// let data1 = Mutex::new(42);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // data1 and data2 refer to distinct mutexes, so this won't panic\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = LockCollection::try_new(\u0026data).unwrap();\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let guard = lock.child().0.lock(key);\n\t/// assert_eq!(*guard, 42);\n\t/// ```\n\t#[must_use]\n\tpub fn child(\u0026self) -\u003e \u0026L {\n\t\tunsafe {\n\t\t\tself.data\n\t\t\t\t.as_ref()\n\t\t\t\t.unwrap_unchecked()\n\t\t\t\t.get()\n\t\t\t\t.as_ref()\n\t\t\t\t.unwrap_unchecked()\n\t\t}\n\t}\n\n\t/// Gets the locks\n\tfn locks(\u0026self) -\u003e \u0026[\u0026dyn RawLock] {\n\t\t\u0026self.locks\n\t}\n}\n\nimpl\u003cL: OwnedLockable\u003e BoxedLockCollection\u003cL\u003e {\n\t/// Creates a new collection of owned locks.\n\t///\n\t/// Because the locks are owned, there's no need to do any checks for\n\t/// duplicate values.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t///\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t/// ```\n\t#[must_use]\n\tpub fn new(data: L) -\u003e Self {\n\t\t// safety: owned lockable types cannot contain duplicates\n\t\tunsafe { Self::new_unchecked(data) }\n\t}\n}\n\nimpl\u003c'a, L: OwnedLockable\u003e BoxedLockCollection\u003c\u0026'a L\u003e {\n\t/// Creates a new collection of owned locks.\n\t///\n\t/// Because the locks are owned, there's no need to do any checks for\n\t/// duplicate values.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t///\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = LockCollection::new_ref(\u0026data);\n\t/// ```\n\t#[must_use]\n\tpub fn new_ref(data: \u0026'a L) -\u003e Self {\n\t\t// safety: owned lockable types cannot contain duplicates\n\t\tunsafe { Self::new_unchecked(data) }\n\t}\n}\n\nimpl\u003cL: Lockable\u003e BoxedLockCollection\u003cL\u003e {\n\t/// Creates a new collections of locks.\n\t///\n\t/// # Safety\n\t///\n\t/// This results in undefined behavior if any locks are presented twice\n\t/// within this collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t///\n\t/// let data1 = Mutex::new(0);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // safety: data1 and data2 refer to distinct mutexes\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = unsafe { LockCollection::new_unchecked(\u0026data) };\n\t/// ```\n\t#[must_use]\n\tpub unsafe fn new_unchecked(data: L) -\u003e Self {\n\t\tlet data = Box::leak(Box::new(UnsafeCell::new(data)));\n\t\tlet data_ref = data.get().cast_const().as_ref().unwrap_unchecked();\n\n\t\tlet mut locks = Vec::new();\n\t\tdata_ref.get_ptrs(\u0026mut locks);\n\n\t\t// cast to *const () because fat pointers can't be converted to usize\n\t\tlocks.sort_by_key(|lock| (\u0026raw const **lock).cast::\u003c()\u003e() as usize);\n\n\t\t// safety we're just changing the lifetimes\n\t\tlet locks: Vec\u003c\u0026'static dyn RawLock\u003e = std::mem::transmute(locks);\n\t\tlet data = \u0026raw const *data;\n\t\tSelf { data, locks }\n\t}\n\n\t/// Creates a new collection of locks.\n\t///\n\t/// This returns `None` if any locks are found twice in the given\n\t/// collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t///\n\t/// let data1 = Mutex::new(0);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // data1 and data2 refer to distinct mutexes, so this won't panic\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = LockCollection::try_new(\u0026data).unwrap();\n\t/// ```\n\t#[must_use]\n\tpub fn try_new(data: L) -\u003e Option\u003cSelf\u003e {\n\t\t// safety: we are checking for duplicates before returning\n\t\tunsafe {\n\t\t\tlet this = Self::new_unchecked(data);\n\t\t\tif ordered_contains_duplicates(this.locks()) {\n\t\t\t\treturn None;\n\t\t\t}\n\t\t\tSome(this)\n\t\t}\n\t}\n\n\tpub fn scoped_lock\u003c'a, R\u003e(\u0026'a self, key: impl Keyable, f: impl Fn(L::DataMut\u003c'a\u003e) -\u003e R) -\u003e R {\n\t\tscoped_write(self, key, f)\n\t}\n\n\tpub fn scoped_try_lock\u003c'a, Key: Keyable, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(L::DataMut\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tscoped_try_write(self, key, f)\n\t}\n\n\t/// Locks the collection\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data. When the guard is dropped, the locks in the collection are also\n\t/// dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// ```\n\t#[must_use]\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::Guard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_write();\n\n\t\t\tLockGuard {\n\t\t\t\t// safety: we've already acquired the lock\n\t\t\t\tguard: self.child().guard(),\n\t\t\t\tkey,\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Attempts to lock the without blocking.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// locks when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If any locks in the collection are already locked, then an error\n\t/// containing the given key is returned.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// match lock.try_lock(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::Guard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tlet guard = unsafe {\n\t\t\tif !self.raw_try_write() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we've acquired the locks\n\t\t\tself.child().guard()\n\t\t};\n\n\t\tOk(LockGuard { guard, key })\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// let key = LockCollection::\u003c(Mutex\u003ci32\u003e, Mutex\u003c\u0026str\u003e)\u003e::unlock(guard);\n\t/// ```\n\tpub fn unlock(guard: LockGuard\u003cL::Guard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: Sharable\u003e BoxedLockCollection\u003cL\u003e {\n\tpub fn scoped_read\u003c'a, R\u003e(\u0026'a self, key: impl Keyable, f: impl Fn(L::DataRef\u003c'a\u003e) -\u003e R) -\u003e R {\n\t\tscoped_read(self, key, f)\n\t}\n\n\tpub fn scoped_try_read\u003c'a, Key: Keyable, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(L::DataRef\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tscoped_try_read(self, key, f)\n\t}\n\n\t/// Locks the collection, so that other threads can still read from it\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data immutably. When the guard is dropped, the locks in the collection\n\t/// are also dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// assert_eq!(*guard.0, 0);\n\t/// assert_eq!(*guard.1, \"\");\n\t/// ```\n\t#[must_use]\n\tpub fn read(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_read();\n\n\t\t\tLockGuard {\n\t\t\t\t// safety: we've already acquired the lock\n\t\t\t\tguard: self.child().read_guard(),\n\t\t\t\tkey,\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Attempts to lock the without blocking, in such a way that other threads\n\t/// can still read from the collection.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// shared access when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If any of the locks in the collection are already locked, then an error\n\t/// is returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(5), RwLock::new(\"6\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// match lock.try_read(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// assert_eq!(*guard.0, 5);\n\t/// assert_eq!(*guard.1, \"6\");\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_read(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tlet guard = unsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_read() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we've acquired the locks\n\t\t\tself.child().read_guard()\n\t\t};\n\n\t\tOk(LockGuard { guard, key })\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// let key = LockCollection::\u003c(RwLock\u003ci32\u003e, RwLock\u003c\u0026str\u003e)\u003e::unlock_read(guard);\n\t/// ```\n\tpub fn unlock_read(guard: LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: LockableIntoInner\u003e BoxedLockCollection\u003cL\u003e {\n\t/// Consumes this `BoxedLockCollection`, returning the underlying data.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t///\n\t/// let mutex = LockCollection::new([Mutex::new(0), Mutex::new(0)]);\n\t/// assert_eq!(mutex.into_inner(), [0, 0]);\n\t/// ```\n\t#[must_use]\n\tpub fn into_inner(self) -\u003e \u003cSelf as LockableIntoInner\u003e::Inner {\n\t\tLockableIntoInner::into_inner(self)\n\t}\n}\n\nimpl\u003c'a, L: 'a\u003e BoxedLockCollection\u003cL\u003e\nwhere\n\t\u0026'a L: IntoIterator,\n{\n\t/// Returns an iterator over references to each value in the collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = [Mutex::new(26), Mutex::new(1)];\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// let mut iter = lock.iter();\n\t/// let mutex = iter.next().unwrap();\n\t/// let guard = mutex.lock(key);\n\t///\n\t/// assert_eq!(*guard, 26);\n\t/// ```\n\t#[must_use]\n\tpub fn iter(\u0026'a self) -\u003e \u003c\u0026'a L as IntoIterator\u003e::IntoIter {\n\t\tself.into_iter()\n\t}\n}\n\n#[cfg(test)]\nmod tests {\n\tuse super::*;\n\tuse crate::{Mutex, RwLock, ThreadKey};\n\n\t#[test]\n\tfn from_iterator() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection: BoxedLockCollection\u003cVec\u003cMutex\u003c\u0026str\u003e\u003e\u003e =\n\t\t\t[Mutex::new(\"foo\"), Mutex::new(\"bar\"), Mutex::new(\"baz\")]\n\t\t\t\t.into_iter()\n\t\t\t\t.collect();\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], \"foo\");\n\t\tassert_eq!(*guard[1], \"bar\");\n\t\tassert_eq!(*guard[2], \"baz\");\n\t}\n\n\t#[test]\n\tfn from() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tBoxedLockCollection::from([Mutex::new(\"foo\"), Mutex::new(\"bar\"), Mutex::new(\"baz\")]);\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], \"foo\");\n\t\tassert_eq!(*guard[1], \"bar\");\n\t\tassert_eq!(*guard[2], \"baz\");\n\t}\n\n\t#[test]\n\tfn into_owned_iterator() {\n\t\tlet collection = BoxedLockCollection::new([Mutex::new(0), Mutex::new(1), Mutex::new(2)]);\n\t\tfor (i, mutex) in collection.into_iter().enumerate() {\n\t\t\tassert_eq!(mutex.into_inner(), i);\n\t\t}\n\t}\n\n\t#[test]\n\tfn into_ref_iterator() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::new([Mutex::new(0), Mutex::new(1), Mutex::new(2)]);\n\t\tfor (i, mutex) in (\u0026collection).into_iter().enumerate() {\n\t\t\tmutex.scoped_lock(\u0026mut key, |val| assert_eq!(*val, i))\n\t\t}\n\t}\n\n\t#[test]\n\tfn ref_iterator() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::new([Mutex::new(0), Mutex::new(1), Mutex::new(2)]);\n\t\tfor (i, mutex) in collection.iter().enumerate() {\n\t\t\tmutex.scoped_lock(\u0026mut key, |val| assert_eq!(*val, i))\n\t\t}\n\t}\n\n\t#[test]\n\t#[allow(clippy::float_cmp)]\n\tfn uses_correct_default() {\n\t\tlet collection =\n\t\t\tBoxedLockCollection::\u003c(Mutex\u003cf64\u003e, Mutex\u003cOption\u003ci32\u003e\u003e, Mutex\u003cusize\u003e)\u003e::default();\n\t\tlet tuple = collection.into_inner();\n\t\tassert_eq!(tuple.0, 0.0);\n\t\tassert!(tuple.1.is_none());\n\t\tassert_eq!(tuple.2, 0)\n\t}\n\n\t#[test]\n\tfn non_duplicates_allowed() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(1);\n\t\tassert!(BoxedLockCollection::try_new([\u0026mutex1, \u0026mutex2]).is_some())\n\t}\n\n\t#[test]\n\tfn duplicates_not_allowed() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tassert!(BoxedLockCollection::try_new([\u0026mutex1, \u0026mutex1]).is_none())\n\t}\n\n\t#[test]\n\tfn scoped_read_sees_changes() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = BoxedLockCollection::new(mutexes);\n\t\tcollection.scoped_lock(\u0026mut key, |guard| *guard[0] = 128);\n\n\t\tlet sum = collection.scoped_read(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 128);\n\t\t\tassert_eq!(*guard[1], 42);\n\t\t\t*guard[0] + *guard[1]\n\t\t});\n\n\t\tassert_eq!(sum, 128 + 42);\n\t}\n\n\t#[test]\n\tfn scoped_try_lock_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::new([Mutex::new(1), Mutex::new(2)]);\n\t\tlet guard = collection.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = collection.scoped_try_lock(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn scoped_try_read_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::new([RwLock::new(1), RwLock::new(2)]);\n\t\tlet guard = collection.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = collection.scoped_try_read(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn try_lock_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::new([Mutex::new(1), Mutex::new(2)]);\n\t\tlet guard = collection.try_lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_lock(key);\n\t\t\t\tassert!(guard.is_err());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn try_read_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::new([RwLock::new(1), RwLock::new(2)]);\n\t\tlet guard = collection.try_read(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_read(key);\n\t\t\t\tassert!(guard.is_ok());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn try_lock_fails_with_one_exclusive_lock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = [Mutex::new(1), Mutex::new(2)];\n\t\tlet collection = BoxedLockCollection::new_ref(\u0026locks);\n\t\tlet guard = locks[1].try_lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_lock(key);\n\t\t\t\tassert!(guard.is_err());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn try_read_fails_during_exclusive_lock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::new([RwLock::new(1), RwLock::new(2)]);\n\t\tlet guard = collection.try_lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_read(key);\n\t\t\t\tassert!(guard.is_err());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn try_read_fails_with_one_exclusive_lock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = [RwLock::new(1), RwLock::new(2)];\n\t\tlet collection = BoxedLockCollection::new_ref(\u0026locks);\n\t\tlet guard = locks[1].try_write(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_read(key);\n\t\t\t\tassert!(guard.is_err());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn unlock_collection_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex1 = Mutex::new(\"foo\");\n\t\tlet mutex2 = Mutex::new(\"bar\");\n\t\tlet collection = BoxedLockCollection::try_new((\u0026mutex1, \u0026mutex2)).unwrap();\n\t\tlet guard = collection.lock(key);\n\t\tlet key = BoxedLockCollection::\u003c(\u0026Mutex\u003c_\u003e, \u0026Mutex\u003c_\u003e)\u003e::unlock(guard);\n\n\t\tassert!(mutex1.try_lock(key).is_ok())\n\t}\n\n\t#[test]\n\tfn read_unlock_collection_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock1 = RwLock::new(\"foo\");\n\t\tlet lock2 = RwLock::new(\"bar\");\n\t\tlet collection = BoxedLockCollection::try_new((\u0026lock1, \u0026lock2)).unwrap();\n\t\tlet guard = collection.read(key);\n\t\tlet key = BoxedLockCollection::\u003c(\u0026RwLock\u003c_\u003e, \u0026RwLock\u003c_\u003e)\u003e::unlock_read(guard);\n\n\t\tassert!(lock1.try_write(key).is_ok())\n\t}\n\n\t#[test]\n\tfn into_inner_works() {\n\t\tlet collection = BoxedLockCollection::new((Mutex::new(\"Hello\"), Mutex::new(47)));\n\t\tassert_eq!(collection.into_inner(), (\"Hello\", 47))\n\t}\n\n\t#[test]\n\tfn works_in_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex1 = RwLock::new(0);\n\t\tlet mutex2 = RwLock::new(1);\n\t\tlet collection =\n\t\t\tBoxedLockCollection::try_new(BoxedLockCollection::try_new([\u0026mutex1, \u0026mutex2]).unwrap())\n\t\t\t\t.unwrap();\n\n\t\tlet mut guard = collection.lock(key);\n\t\tassert!(mutex1.is_locked());\n\t\tassert!(mutex2.is_locked());\n\t\tassert_eq!(*guard[0], 0);\n\t\tassert_eq!(*guard[1], 1);\n\t\t*guard[0] = 2;\n\t\tlet key = BoxedLockCollection::\u003cBoxedLockCollection\u003c[\u0026RwLock\u003c_\u003e; 2]\u003e\u003e::unlock(guard);\n\n\t\tlet guard = collection.read(key);\n\t\tassert!(mutex1.is_locked());\n\t\tassert!(mutex2.is_locked());\n\t\tassert_eq!(*guard[0], 2);\n\t\tassert_eq!(*guard[1], 1);\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn as_ref_works() {\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet collection = BoxedLockCollection::new_ref(\u0026mutexes);\n\n\t\tassert!(std::ptr::addr_eq(\u0026mutexes, collection.as_ref()))\n\t}\n\n\t#[test]\n\tfn child() {\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet collection = BoxedLockCollection::new_ref(\u0026mutexes);\n\n\t\tassert!(std::ptr::addr_eq(\u0026mutexes, *collection.child()))\n\t}\n}\n","traces":[{"line":21,"address":[],"length":0,"stats":{"Line":24}},{"line":22,"address":[226021],"length":1,"stats":{"Line":23}},{"line":25,"address":[1544784,1544656,1544912,1544528,1545040],"length":1,"stats":{"Line":6}},{"line":26,"address":[1544669,1544797,1545053,1544541,1544925],"length":1,"stats":{"Line":6}},{"line":27,"address":[1544879,1545007,1544751,1545135,1544623],"length":1,"stats":{"Line":6}},{"line":30,"address":[],"length":0,"stats":{"Line":7}},{"line":31,"address":[],"length":0,"stats":{"Line":14}},{"line":32,"address":[],"length":0,"stats":{"Line":7}},{"line":36,"address":[1546096,1546128,1546288,1546192,1546064,1546256,1546160,1546224],"length":1,"stats":{"Line":9}},{"line":37,"address":[],"length":0,"stats":{"Line":9}},{"line":40,"address":[189216],"length":1,"stats":{"Line":4}},{"line":41,"address":[1546389,1546357,1546325],"length":1,"stats":{"Line":4}},{"line":44,"address":[1546416,1546752,1546528,1546640],"length":1,"stats":{"Line":4}},{"line":45,"address":[],"length":0,"stats":{"Line":8}},{"line":46,"address":[],"length":0,"stats":{"Line":4}},{"line":62,"address":[],"length":0,"stats":{"Line":1}},{"line":63,"address":[],"length":0,"stats":{"Line":1}},{"line":66,"address":[1546912],"length":1,"stats":{"Line":1}},{"line":67,"address":[],"length":0,"stats":{"Line":1}},{"line":70,"address":[],"length":0,"stats":{"Line":7}},{"line":71,"address":[],"length":0,"stats":{"Line":7}},{"line":86,"address":[],"length":0,"stats":{"Line":1}},{"line":87,"address":[],"length":0,"stats":{"Line":1}},{"line":90,"address":[1547456,1547360,1547392,1547488],"length":1,"stats":{"Line":4}},{"line":91,"address":[],"length":0,"stats":{"Line":4}},{"line":103,"address":[],"length":0,"stats":{"Line":2}},{"line":104,"address":[1547598,1547534],"length":1,"stats":{"Line":2}},{"line":115,"address":[],"length":0,"stats":{"Line":1}},{"line":116,"address":[],"length":0,"stats":{"Line":1}},{"line":127,"address":[],"length":0,"stats":{"Line":1}},{"line":128,"address":[1547717],"length":1,"stats":{"Line":1}},{"line":135,"address":[],"length":0,"stats":{"Line":1}},{"line":136,"address":[],"length":0,"stats":{"Line":1}},{"line":137,"address":[1547791],"length":1,"stats":{"Line":1}},{"line":162,"address":[],"length":0,"stats":{"Line":1}},{"line":163,"address":[1547829],"length":1,"stats":{"Line":1}},{"line":178,"address":[],"length":0,"stats":{"Line":1}},{"line":179,"address":[1547870],"length":1,"stats":{"Line":1}},{"line":184,"address":[],"length":0,"stats":{"Line":1}},{"line":185,"address":[],"length":0,"stats":{"Line":1}},{"line":209,"address":[],"length":0,"stats":{"Line":3}},{"line":212,"address":[],"length":0,"stats":{"Line":3}},{"line":214,"address":[],"length":0,"stats":{"Line":3}},{"line":216,"address":[],"length":0,"stats":{"Line":3}},{"line":218,"address":[],"length":0,"stats":{"Line":3}},{"line":244,"address":[181600],"length":1,"stats":{"Line":27}},{"line":246,"address":[189753],"length":1,"stats":{"Line":27}},{"line":256,"address":[202880],"length":1,"stats":{"Line":36}},{"line":257,"address":[202885],"length":1,"stats":{"Line":36}},{"line":276,"address":[],"length":0,"stats":{"Line":16}},{"line":278,"address":[],"length":0,"stats":{"Line":17}},{"line":297,"address":[1552176,1552208,1552240],"length":1,"stats":{"Line":4}},{"line":299,"address":[],"length":0,"stats":{"Line":4}},{"line":324,"address":[],"length":0,"stats":{"Line":37}},{"line":325,"address":[181052,180945],"length":1,"stats":{"Line":80}},{"line":326,"address":[181092],"length":1,"stats":{"Line":39}},{"line":328,"address":[150063],"length":1,"stats":{"Line":39}},{"line":329,"address":[202526],"length":1,"stats":{"Line":40}},{"line":332,"address":[162474,162464],"length":1,"stats":{"Line":105}},{"line":335,"address":[1562779,1558730,1558257,1553132,1560811,1560308,1565324,1556106,1556654,1554107,1553627,1562284,1559801,1564292,1555137,1555628,1552625,1561313,1565820,1557739,1564841,1554638,1561798,1563242,1557131,1559252,1563722],"length":1,"stats":{"Line":41}},{"line":336,"address":[150235],"length":1,"stats":{"Line":41}},{"line":358,"address":[172656,172902],"length":1,"stats":{"Line":15}},{"line":361,"address":[1567947,1566296,1566843,1566016,1566571,1567400,1567115,1567680],"length":1,"stats":{"Line":15}},{"line":362,"address":[],"length":0,"stats":{"Line":30}},{"line":363,"address":[190050],"length":1,"stats":{"Line":1}},{"line":365,"address":[1567212,1566396,1568044,1566113,1567500,1566668,1567777,1566940],"length":1,"stats":{"Line":14}},{"line":369,"address":[],"length":0,"stats":{"Line":9}},{"line":370,"address":[],"length":0,"stats":{"Line":9}},{"line":373,"address":[1568512,1568544,1568480],"length":1,"stats":{"Line":3}},{"line":378,"address":[],"length":0,"stats":{"Line":3}},{"line":401,"address":[226690,226544],"length":1,"stats":{"Line":23}},{"line":404,"address":[1569454,1570032,1570832,1570430,1569600,1569886,1568702,1570672,1569758,1568590,1569136,1570542,1570302,1568814,1569296,1570174,1568960],"length":1,"stats":{"Line":23}},{"line":408,"address":[172460],"length":1,"stats":{"Line":19}},{"line":443,"address":[],"length":0,"stats":{"Line":6}},{"line":445,"address":[1571568,1571801,1571246,1571038,1571390,1570992,1571200,1571614,1571433,1571758],"length":1,"stats":{"Line":11}},{"line":446,"address":[1571257,1571049,1571439,1571807,1571625],"length":1,"stats":{"Line":4}},{"line":450,"address":[203305,203275],"length":1,"stats":{"Line":4}},{"line":453,"address":[1571872,1571105,1571313,1571681,1571504],"length":1,"stats":{"Line":2}},{"line":473,"address":[],"length":0,"stats":{"Line":13}},{"line":474,"address":[],"length":0,"stats":{"Line":13}},{"line":475,"address":[],"length":0,"stats":{"Line":0}},{"line":480,"address":[],"length":0,"stats":{"Line":4}},{"line":481,"address":[],"length":0,"stats":{"Line":4}},{"line":484,"address":[1573072,1573040],"length":1,"stats":{"Line":2}},{"line":489,"address":[],"length":0,"stats":{"Line":2}},{"line":512,"address":[181570,181424],"length":1,"stats":{"Line":8}},{"line":515,"address":[],"length":0,"stats":{"Line":8}},{"line":519,"address":[1573704,1573528,1573974,1573270,1573382,1573846,1573158],"length":1,"stats":{"Line":7}},{"line":555,"address":[190112,190319],"length":1,"stats":{"Line":4}},{"line":558,"address":[190194,190144],"length":1,"stats":{"Line":8}},{"line":559,"address":[],"length":0,"stats":{"Line":3}},{"line":563,"address":[1574359,1574543,1574181,1574389,1574570,1574151],"length":1,"stats":{"Line":2}},{"line":566,"address":[190261],"length":1,"stats":{"Line":1}},{"line":584,"address":[],"length":0,"stats":{"Line":1}},{"line":585,"address":[1574638],"length":1,"stats":{"Line":1}},{"line":586,"address":[],"length":0,"stats":{"Line":0}},{"line":602,"address":[],"length":0,"stats":{"Line":2}},{"line":603,"address":[],"length":0,"stats":{"Line":2}},{"line":629,"address":[],"length":0,"stats":{"Line":1}},{"line":630,"address":[],"length":0,"stats":{"Line":1}}],"covered":98,"coverable":100},{"path":["/","home","botahamec","Projects","happylock","src","collection","guard.rs"],"content":"use std::fmt::{Debug, Display};\nuse std::hash::Hash;\nuse std::ops::{Deref, DerefMut};\n\nuse super::LockGuard;\n\n#[mutants::skip] // hashing involves RNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard: Hash\u003e Hash for LockGuard\u003cGuard\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.guard.hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard: Debug\u003e Debug for LockGuard\u003cGuard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cGuard: Display\u003e Display for LockGuard\u003cGuard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cGuard\u003e Deref for LockGuard\u003cGuard\u003e {\n\ttype Target = Guard;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t\u0026self.guard\n\t}\n}\n\nimpl\u003cGuard\u003e DerefMut for LockGuard\u003cGuard\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t\u0026mut self.guard\n\t}\n}\n\nimpl\u003cGuard\u003e AsRef\u003cGuard\u003e for LockGuard\u003cGuard\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026Guard {\n\t\t\u0026self.guard\n\t}\n}\n\nimpl\u003cGuard\u003e AsMut\u003cGuard\u003e for LockGuard\u003cGuard\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut Guard {\n\t\t\u0026mut self.guard\n\t}\n}\n\n#[cfg(test)]\nmod tests {\n\tuse crate::collection::OwnedLockCollection;\n\tuse crate::{LockCollection, Mutex, RwLock, ThreadKey};\n\n\t#[test]\n\tfn guard_display_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = OwnedLockCollection::new(RwLock::new(\"Hello, world!\"));\n\t\tlet guard = lock.read(key);\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn deref_mut_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = (Mutex::new(1), Mutex::new(2));\n\t\tlet lock = LockCollection::new_ref(\u0026locks);\n\t\tlet mut guard = lock.lock(key);\n\t\t*guard.0 = 3;\n\t\tlet key = LockCollection::\u003c(Mutex\u003c_\u003e, Mutex\u003c_\u003e)\u003e::unlock(guard);\n\n\t\tlet guard = locks.0.lock(key);\n\t\tassert_eq!(*guard, 3);\n\t\tlet key = Mutex::unlock(guard);\n\n\t\tlet guard = locks.1.lock(key);\n\t\tassert_eq!(*guard, 2);\n\t}\n\n\t#[test]\n\tfn as_ref_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = (Mutex::new(1), Mutex::new(2));\n\t\tlet lock = LockCollection::new_ref(\u0026locks);\n\t\tlet mut guard = lock.lock(key);\n\t\t*guard.0 = 3;\n\t\tlet key = LockCollection::\u003c(Mutex\u003c_\u003e, Mutex\u003c_\u003e)\u003e::unlock(guard);\n\n\t\tlet guard = locks.0.lock(key);\n\t\tassert_eq!(guard.as_ref(), \u00263);\n\t\tlet key = Mutex::unlock(guard);\n\n\t\tlet guard = locks.1.lock(key);\n\t\tassert_eq!(guard.as_ref(), \u00262);\n\t}\n\n\t#[test]\n\tfn as_mut_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = (Mutex::new(1), Mutex::new(2));\n\t\tlet lock = LockCollection::new_ref(\u0026locks);\n\t\tlet mut guard = lock.lock(key);\n\t\tlet guard_mut = guard.as_mut();\n\t\t*guard_mut.0 = 3;\n\t\tlet key = LockCollection::\u003c(Mutex\u003c_\u003e, Mutex\u003c_\u003e)\u003e::unlock(guard);\n\n\t\tlet guard = locks.0.lock(key);\n\t\tassert_eq!(guard.as_ref(), \u00263);\n\t\tlet key = Mutex::unlock(guard);\n\n\t\tlet guard = locks.1.lock(key);\n\t\tassert_eq!(guard.as_ref(), \u00262);\n\t}\n}\n","traces":[{"line":24,"address":[],"length":0,"stats":{"Line":1}},{"line":25,"address":[],"length":0,"stats":{"Line":1}},{"line":32,"address":[],"length":0,"stats":{"Line":18}},{"line":33,"address":[],"length":0,"stats":{"Line":0}},{"line":38,"address":[],"length":0,"stats":{"Line":8}},{"line":39,"address":[],"length":0,"stats":{"Line":0}},{"line":44,"address":[],"length":0,"stats":{"Line":2}},{"line":45,"address":[],"length":0,"stats":{"Line":0}},{"line":50,"address":[],"length":0,"stats":{"Line":4}},{"line":51,"address":[],"length":0,"stats":{"Line":0}}],"covered":6,"coverable":10},{"path":["/","home","botahamec","Projects","happylock","src","collection","owned.rs"],"content":"use crate::lockable::{\n\tLockable, LockableGetMut, LockableIntoInner, OwnedLockable, RawLock, Sharable,\n};\nuse crate::{Keyable, ThreadKey};\n\nuse super::utils::{scoped_read, scoped_try_read, scoped_try_write, scoped_write};\nuse super::{utils, LockGuard, OwnedLockCollection};\n\nunsafe impl\u003cL: Lockable\u003e RawLock for OwnedLockCollection\u003cL\u003e {\n\t#[mutants::skip] // this should never run\n\t#[cfg(not(tarpaulin_include))]\n\tfn poison(\u0026self) {\n\t\tlet locks = utils::get_locks_unsorted(\u0026self.data);\n\t\tfor lock in locks {\n\t\t\tlock.poison();\n\t\t}\n\t}\n\n\tunsafe fn raw_write(\u0026self) {\n\t\tutils::ordered_write(\u0026utils::get_locks_unsorted(\u0026self.data))\n\t}\n\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool {\n\t\tlet locks = utils::get_locks_unsorted(\u0026self.data);\n\t\tutils::ordered_try_write(\u0026locks)\n\t}\n\n\tunsafe fn raw_unlock_write(\u0026self) {\n\t\tlet locks = utils::get_locks_unsorted(\u0026self.data);\n\t\tfor lock in locks {\n\t\t\tlock.raw_unlock_write();\n\t\t}\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tutils::ordered_read(\u0026utils::get_locks_unsorted(\u0026self.data))\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tlet locks = utils::get_locks_unsorted(\u0026self.data);\n\t\tutils::ordered_try_read(\u0026locks)\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tlet locks = utils::get_locks_unsorted(\u0026self.data);\n\t\tfor lock in locks {\n\t\t\tlock.raw_unlock_read();\n\t\t}\n\t}\n}\n\nunsafe impl\u003cL: Lockable\u003e Lockable for OwnedLockCollection\u003cL\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= L::Guard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= L::DataMut\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\t#[mutants::skip] // It's hard to test lkocks in an OwnedLockCollection, because they're owned\n\t#[cfg(not(tarpaulin_include))]\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tptrs.push(self)\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tself.data.guard()\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.data.data_mut()\n\t}\n}\n\nimpl\u003cL: LockableGetMut\u003e LockableGetMut for OwnedLockCollection\u003cL\u003e {\n\ttype Inner\u003c'a\u003e\n\t\t= L::Inner\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tself.data.get_mut()\n\t}\n}\n\nimpl\u003cL: LockableIntoInner\u003e LockableIntoInner for OwnedLockCollection\u003cL\u003e {\n\ttype Inner = L::Inner;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tself.data.into_inner()\n\t}\n}\n\nunsafe impl\u003cL: Sharable\u003e Sharable for OwnedLockCollection\u003cL\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= L::ReadGuard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= L::DataRef\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tself.data.read_guard()\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tself.data.data_ref()\n\t}\n}\n\nunsafe impl\u003cL: OwnedLockable\u003e OwnedLockable for OwnedLockCollection\u003cL\u003e {}\n\nimpl\u003cL\u003e IntoIterator for OwnedLockCollection\u003cL\u003e\nwhere\n\tL: IntoIterator,\n{\n\ttype Item = \u003cL as IntoIterator\u003e::Item;\n\ttype IntoIter = \u003cL as IntoIterator\u003e::IntoIter;\n\n\tfn into_iter(self) -\u003e Self::IntoIter {\n\t\tself.data.into_iter()\n\t}\n}\n\nimpl\u003cL: OwnedLockable, I: FromIterator\u003cL\u003e + OwnedLockable\u003e FromIterator\u003cL\u003e\n\tfor OwnedLockCollection\u003cI\u003e\n{\n\tfn from_iter\u003cT: IntoIterator\u003cItem = L\u003e\u003e(iter: T) -\u003e Self {\n\t\tlet iter: I = iter.into_iter().collect();\n\t\tSelf::new(iter)\n\t}\n}\n\nimpl\u003cE: OwnedLockable + Extend\u003cL\u003e, L: OwnedLockable\u003e Extend\u003cL\u003e for OwnedLockCollection\u003cE\u003e {\n\tfn extend\u003cT: IntoIterator\u003cItem = L\u003e\u003e(\u0026mut self, iter: T) {\n\t\tself.data.extend(iter)\n\t}\n}\n\n// AsRef can't be implemented because an impl of AsRef\u003cL\u003e for L could break the\n// invariant that there is only one way to lock the collection. AsMut is fine,\n// because the collection can't be locked as long as the reference is valid.\n\nimpl\u003cT: ?Sized, L: AsMut\u003cT\u003e\u003e AsMut\u003cT\u003e for OwnedLockCollection\u003cL\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself.data.as_mut()\n\t}\n}\n\nimpl\u003cL: OwnedLockable + Default\u003e Default for OwnedLockCollection\u003cL\u003e {\n\tfn default() -\u003e Self {\n\t\tSelf::new(L::default())\n\t}\n}\n\nimpl\u003cL: OwnedLockable\u003e From\u003cL\u003e for OwnedLockCollection\u003cL\u003e {\n\tfn from(value: L) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\nimpl\u003cL: OwnedLockable\u003e OwnedLockCollection\u003cL\u003e {\n\t/// Creates a new collection of owned locks.\n\t///\n\t/// Because the locks are owned, there's no need to do any checks for\n\t/// duplicate values. The locks also don't need to be sorted by memory\n\t/// address because they aren't used anywhere else.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t/// ```\n\t#[must_use]\n\tpub const fn new(data: L) -\u003e Self {\n\t\tSelf { data }\n\t}\n\n\tpub fn scoped_lock\u003c'a, R\u003e(\u0026'a self, key: impl Keyable, f: impl Fn(L::DataMut\u003c'a\u003e) -\u003e R) -\u003e R {\n\t\tscoped_write(self, key, f)\n\t}\n\n\tpub fn scoped_try_lock\u003c'a, Key: Keyable, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(L::DataMut\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tscoped_try_write(self, key, f)\n\t}\n\n\t/// Locks the collection\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data. When the guard is dropped, the locks in the collection are also\n\t/// dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// ```\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::Guard\u003c'_\u003e\u003e {\n\t\tlet guard = unsafe {\n\t\t\t// safety: we have the thread key, and these locks happen in a\n\t\t\t// predetermined order\n\t\t\tself.raw_write();\n\n\t\t\t// safety: we've locked all of this already\n\t\t\tself.data.guard()\n\t\t};\n\n\t\tLockGuard { guard, key }\n\t}\n\n\t/// Attempts to lock the without blocking.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// locks when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If any of the locks in this collection are already locked, this returns\n\t/// an error containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// match lock.try_lock(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::Guard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tlet guard = unsafe {\n\t\t\tif !self.raw_try_write() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we've acquired the locks\n\t\t\tself.data.guard()\n\t\t};\n\n\t\tOk(LockGuard { guard, key })\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// let key = OwnedLockCollection::\u003c(Mutex\u003ci32\u003e, Mutex\u003c\u0026str\u003e)\u003e::unlock(guard);\n\t/// ```\n\t#[allow(clippy::missing_const_for_fn)]\n\tpub fn unlock(guard: LockGuard\u003cL::Guard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: Sharable\u003e OwnedLockCollection\u003cL\u003e {\n\tpub fn scoped_read\u003c'a, R\u003e(\u0026'a self, key: impl Keyable, f: impl Fn(L::DataRef\u003c'a\u003e) -\u003e R) -\u003e R {\n\t\tscoped_read(self, key, f)\n\t}\n\n\tpub fn scoped_try_read\u003c'a, Key: Keyable, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(L::DataRef\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tscoped_try_read(self, key, f)\n\t}\n\n\t/// Locks the collection, so that other threads can still read from it\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data immutably. When the guard is dropped, the locks in the collection\n\t/// are also dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// assert_eq!(*guard.0, 0);\n\t/// assert_eq!(*guard.1, \"\");\n\t/// ```\n\tpub fn read(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_read();\n\n\t\t\tLockGuard {\n\t\t\t\t// safety: we've already acquired the lock\n\t\t\t\tguard: self.data.read_guard(),\n\t\t\t\tkey,\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Attempts to lock the without blocking, in such a way that other threads\n\t/// can still read from the collection.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// shared access when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If any of the locks in this collection can't be acquired, then an error\n\t/// is returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(5), RwLock::new(\"6\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// match lock.try_read(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// assert_eq!(*guard.0, 5);\n\t/// assert_eq!(*guard.1, \"6\");\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_read(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tlet guard = unsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_read() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we've acquired the locks\n\t\t\tself.data.read_guard()\n\t\t};\n\n\t\tOk(LockGuard { guard, key })\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// let key = OwnedLockCollection::\u003c(RwLock\u003ci32\u003e, RwLock\u003c\u0026str\u003e)\u003e::unlock_read(guard);\n\t/// ```\n\t#[allow(clippy::missing_const_for_fn)]\n\tpub fn unlock_read(guard: LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL\u003e OwnedLockCollection\u003cL\u003e {\n\t/// Gets the underlying collection, consuming this collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let data = (Mutex::new(42), Mutex::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let inner = lock.into_child();\n\t/// let guard = inner.0.lock(key);\n\t/// assert_eq!(*guard, 42);\n\t/// ```\n\t#[must_use]\n\tpub fn into_child(self) -\u003e L {\n\t\tself.data\n\t}\n\n\t/// Gets a mutable reference to the underlying collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let data = (Mutex::new(42), Mutex::new(\"\"));\n\t/// let mut lock = OwnedLockCollection::new(data);\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut inner = lock.child_mut();\n\t/// let guard = inner.0.get_mut();\n\t/// assert_eq!(*guard, 42);\n\t/// ```\n\t#[must_use]\n\tpub fn child_mut(\u0026mut self) -\u003e \u0026mut L {\n\t\t\u0026mut self.data\n\t}\n}\n\nimpl\u003cL: LockableGetMut\u003e OwnedLockCollection\u003cL\u003e {\n\t/// Gets a mutable reference to the data behind this `OwnedLockCollection`.\n\t///\n\t/// Since this call borrows the `OwnedLockCollection` mutably, no actual\n\t/// locking needs to take place - the mutable borrow statically guarantees\n\t/// no locks exist.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let mut mutex = OwnedLockCollection::new([Mutex::new(0), Mutex::new(0)]);\n\t/// assert_eq!(mutex.get_mut(), [\u0026mut 0, \u0026mut 0]);\n\t/// ```\n\tpub fn get_mut(\u0026mut self) -\u003e L::Inner\u003c'_\u003e {\n\t\tLockableGetMut::get_mut(self)\n\t}\n}\n\nimpl\u003cL: LockableIntoInner\u003e OwnedLockCollection\u003cL\u003e {\n\t/// Consumes this `OwnedLockCollection`, returning the underlying data.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let mutex = OwnedLockCollection::new([Mutex::new(0), Mutex::new(0)]);\n\t/// assert_eq!(mutex.into_inner(), [0, 0]);\n\t/// ```\n\t#[must_use]\n\tpub fn into_inner(self) -\u003e L::Inner {\n\t\tLockableIntoInner::into_inner(self)\n\t}\n}\n\n#[cfg(test)]\nmod tests {\n\tuse super::*;\n\tuse crate::{Mutex, RwLock, ThreadKey};\n\n\t#[test]\n\tfn get_mut_applies_changes() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mut collection = OwnedLockCollection::new([Mutex::new(\"foo\"), Mutex::new(\"bar\")]);\n\t\tassert_eq!(*collection.get_mut()[0], \"foo\");\n\t\tassert_eq!(*collection.get_mut()[1], \"bar\");\n\t\t*collection.get_mut()[0] = \"baz\";\n\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], \"baz\");\n\t\tassert_eq!(*guard[1], \"bar\");\n\t}\n\n\t#[test]\n\tfn into_inner_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::from([Mutex::new(\"foo\")]);\n\t\tlet mut guard = collection.lock(key);\n\t\t*guard[0] = \"bar\";\n\t\tdrop(guard);\n\n\t\tlet array = collection.into_inner();\n\t\tassert_eq!(array.len(), 1);\n\t\tassert_eq!(array[0], \"bar\");\n\t}\n\n\t#[test]\n\tfn from_into_iter_is_correct() {\n\t\tlet array = [Mutex::new(0), Mutex::new(1), Mutex::new(2), Mutex::new(3)];\n\t\tlet mut collection: OwnedLockCollection\u003cVec\u003cMutex\u003cusize\u003e\u003e\u003e = array.into_iter().collect();\n\t\tassert_eq!(collection.get_mut().len(), 4);\n\t\tfor (i, lock) in collection.into_iter().enumerate() {\n\t\t\tassert_eq!(lock.into_inner(), i);\n\t\t}\n\t}\n\n\t#[test]\n\tfn from_iter_is_correct() {\n\t\tlet array = [Mutex::new(0), Mutex::new(1), Mutex::new(2), Mutex::new(3)];\n\t\tlet mut collection: OwnedLockCollection\u003cVec\u003cMutex\u003cusize\u003e\u003e\u003e = array.into_iter().collect();\n\t\tlet collection: \u0026mut Vec\u003c_\u003e = collection.as_mut();\n\t\tassert_eq!(collection.len(), 4);\n\t\tfor (i, lock) in collection.iter_mut().enumerate() {\n\t\t\tassert_eq!(*lock.get_mut(), i);\n\t\t}\n\t}\n\n\t#[test]\n\tfn scoped_read_works() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new([RwLock::new(24), RwLock::new(42)]);\n\t\tlet sum = collection.scoped_read(\u0026mut key, |guard| guard[0] + guard[1]);\n\t\tassert_eq!(sum, 24 + 42);\n\t}\n\n\t#[test]\n\tfn scoped_lock_works() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new([RwLock::new(24), RwLock::new(42)]);\n\t\tcollection.scoped_lock(\u0026mut key, |guard| *guard[0] += *guard[1]);\n\n\t\tlet sum = collection.scoped_lock(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 24 + 42);\n\t\t\tassert_eq!(*guard[1], 42);\n\t\t\t*guard[0] + *guard[1]\n\t\t});\n\n\t\tassert_eq!(sum, 24 + 42 + 42);\n\t}\n\n\t#[test]\n\tfn scoped_try_lock_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new([Mutex::new(1), Mutex::new(2)]);\n\t\tlet guard = collection.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = collection.scoped_try_lock(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn scoped_try_read_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new([RwLock::new(1), RwLock::new(2)]);\n\t\tlet guard = collection.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = collection.scoped_try_read(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn try_lock_works_on_unlocked() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new((Mutex::new(0), Mutex::new(1)));\n\t\tlet guard = collection.try_lock(key).unwrap();\n\t\tassert_eq!(*guard.0, 0);\n\t\tassert_eq!(*guard.1, 1);\n\t}\n\n\t#[test]\n\tfn try_lock_fails_on_locked() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new((Mutex::new(0), Mutex::new(1)));\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\t#[allow(unused)]\n\t\t\t\tlet guard = collection.lock(key);\n\t\t\t\tstd::mem::forget(guard);\n\t\t\t});\n\t\t});\n\n\t\tassert!(collection.try_lock(key).is_err());\n\t}\n\n\t#[test]\n\tfn try_read_succeeds_for_unlocked_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = OwnedLockCollection::new(mutexes);\n\t\tlet guard = collection.try_read(key).unwrap();\n\t\tassert_eq!(*guard[0], 24);\n\t\tassert_eq!(*guard[1], 42);\n\t}\n\n\t#[test]\n\tfn try_read_fails_on_locked() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new((RwLock::new(0), RwLock::new(1)));\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\t#[allow(unused)]\n\t\t\t\tlet guard = collection.lock(key);\n\t\t\t\tstd::mem::forget(guard);\n\t\t\t});\n\t\t});\n\n\t\tassert!(collection.try_read(key).is_err());\n\t}\n\n\t#[test]\n\tfn can_read_twice_on_different_threads() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = OwnedLockCollection::new(mutexes);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.read(key);\n\t\t\t\tassert_eq!(*guard[0], 24);\n\t\t\t\tassert_eq!(*guard[1], 42);\n\t\t\t\tstd::mem::forget(guard);\n\t\t\t});\n\t\t});\n\n\t\tlet guard = collection.try_read(key).unwrap();\n\t\tassert_eq!(*guard[0], 24);\n\t\tassert_eq!(*guard[1], 42);\n\t}\n\n\t#[test]\n\tfn unlock_collection_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new((Mutex::new(\"foo\"), Mutex::new(\"bar\")));\n\t\tlet guard = collection.lock(key);\n\n\t\tlet key = OwnedLockCollection::\u003c(Mutex\u003c_\u003e, Mutex\u003c_\u003e)\u003e::unlock(guard);\n\t\tassert!(collection.try_lock(key).is_ok())\n\t}\n\n\t#[test]\n\tfn read_unlock_collection_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new((RwLock::new(\"foo\"), RwLock::new(\"bar\")));\n\t\tlet guard = collection.read(key);\n\n\t\tlet key = OwnedLockCollection::\u003c(\u0026RwLock\u003c_\u003e, \u0026RwLock\u003c_\u003e)\u003e::unlock_read(guard);\n\t\tassert!(collection.try_lock(key).is_ok())\n\t}\n\n\t#[test]\n\tfn default_works() {\n\t\ttype MyCollection = OwnedLockCollection\u003c(Mutex\u003ci32\u003e, Mutex\u003cOption\u003ci32\u003e\u003e, Mutex\u003cString\u003e)\u003e;\n\t\tlet collection = MyCollection::default();\n\t\tlet inner = collection.into_inner();\n\t\tassert_eq!(inner.0, 0);\n\t\tassert_eq!(inner.1, None);\n\t\tassert_eq!(inner.2, String::new());\n\t}\n\n\t#[test]\n\tfn can_be_extended() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(1);\n\t\tlet mut collection = OwnedLockCollection::new(vec![mutex1, mutex2]);\n\n\t\tcollection.extend([Mutex::new(2)]);\n\n\t\tassert_eq!(collection.data.len(), 3);\n\t}\n\n\t#[test]\n\tfn works_in_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tOwnedLockCollection::new(OwnedLockCollection::new([RwLock::new(0), RwLock::new(1)]));\n\n\t\tlet mut guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], 0);\n\t\tassert_eq!(*guard[1], 1);\n\t\t*guard[1] = 2;\n\n\t\tlet key = OwnedLockCollection::\u003cOwnedLockCollection\u003c[RwLock\u003c_\u003e; 2]\u003e\u003e::unlock(guard);\n\t\tlet guard = collection.read(key);\n\t\tassert_eq!(*guard[0], 0);\n\t\tassert_eq!(*guard[1], 2);\n\t}\n\n\t#[test]\n\tfn as_mut_works() {\n\t\tlet mut mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet mut collection = OwnedLockCollection::new(\u0026mut mutexes);\n\n\t\tcollection.as_mut()[0] = Mutex::new(42);\n\n\t\tassert_eq!(*collection.as_mut()[0].get_mut(), 42);\n\t}\n\n\t#[test]\n\tfn child_mut_works() {\n\t\tlet mut mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet mut collection = OwnedLockCollection::new(\u0026mut mutexes);\n\n\t\tcollection.child_mut()[0] = Mutex::new(42);\n\n\t\tassert_eq!(*collection.child_mut()[0].get_mut(), 42);\n\t}\n\n\t#[test]\n\tfn into_child_works() {\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet mut collection = OwnedLockCollection::new(mutexes);\n\n\t\tcollection.child_mut()[0] = Mutex::new(42);\n\n\t\tassert_eq!(\n\t\t\t*collection\n\t\t\t\t.into_child()\n\t\t\t\t.as_mut()\n\t\t\t\t.get_mut(0)\n\t\t\t\t.unwrap()\n\t\t\t\t.get_mut(),\n\t\t\t42\n\t\t);\n\t}\n}\n","traces":[{"line":19,"address":[1576096,1575584,1575328,1576203,1575819,1575968,1575435,1575456,1576331,1575712,1575691,1575840,1575563,1576224,1576075,1575947],"length":1,"stats":{"Line":8}},{"line":20,"address":[],"length":0,"stats":{"Line":16}},{"line":23,"address":[],"length":0,"stats":{"Line":4}},{"line":24,"address":[],"length":0,"stats":{"Line":4}},{"line":25,"address":[],"length":0,"stats":{"Line":8}},{"line":28,"address":[1576928,1577173,1577445,1577200],"length":1,"stats":{"Line":1}},{"line":29,"address":[],"length":0,"stats":{"Line":1}},{"line":30,"address":[],"length":0,"stats":{"Line":3}},{"line":31,"address":[],"length":0,"stats":{"Line":1}},{"line":35,"address":[],"length":0,"stats":{"Line":4}},{"line":36,"address":[],"length":0,"stats":{"Line":8}},{"line":39,"address":[],"length":0,"stats":{"Line":2}},{"line":40,"address":[1578006,1578150],"length":1,"stats":{"Line":2}},{"line":41,"address":[1578016,1578160,1578071,1578215],"length":1,"stats":{"Line":4}},{"line":44,"address":[],"length":0,"stats":{"Line":1}},{"line":45,"address":[],"length":0,"stats":{"Line":1}},{"line":46,"address":[1578424,1578300,1578478],"length":1,"stats":{"Line":3}},{"line":47,"address":[1578504],"length":1,"stats":{"Line":1}},{"line":69,"address":[],"length":0,"stats":{"Line":1}},{"line":70,"address":[],"length":0,"stats":{"Line":1}},{"line":73,"address":[],"length":0,"stats":{"Line":1}},{"line":74,"address":[],"length":0,"stats":{"Line":1}},{"line":84,"address":[1578656,1578640],"length":1,"stats":{"Line":2}},{"line":85,"address":[1578673,1578645],"length":1,"stats":{"Line":2}},{"line":92,"address":[],"length":0,"stats":{"Line":2}},{"line":93,"address":[],"length":0,"stats":{"Line":2}},{"line":108,"address":[],"length":0,"stats":{"Line":1}},{"line":109,"address":[1578833],"length":1,"stats":{"Line":1}},{"line":112,"address":[1578848],"length":1,"stats":{"Line":1}},{"line":113,"address":[1578865],"length":1,"stats":{"Line":1}},{"line":126,"address":[],"length":0,"stats":{"Line":1}},{"line":127,"address":[1578892],"length":1,"stats":{"Line":1}},{"line":134,"address":[1578944],"length":1,"stats":{"Line":1}},{"line":135,"address":[],"length":0,"stats":{"Line":1}},{"line":136,"address":[],"length":0,"stats":{"Line":1}},{"line":141,"address":[],"length":0,"stats":{"Line":1}},{"line":142,"address":[],"length":0,"stats":{"Line":1}},{"line":151,"address":[],"length":0,"stats":{"Line":2}},{"line":152,"address":[],"length":0,"stats":{"Line":2}},{"line":157,"address":[],"length":0,"stats":{"Line":1}},{"line":158,"address":[],"length":0,"stats":{"Line":1}},{"line":163,"address":[],"length":0,"stats":{"Line":1}},{"line":164,"address":[],"length":0,"stats":{"Line":1}},{"line":185,"address":[1579392,1579248,1579648,1579504,1579296,1579216,1579600,1579184,1579568,1579328,1579424,1579536,1579472,1579376],"length":1,"stats":{"Line":15}},{"line":189,"address":[],"length":0,"stats":{"Line":2}},{"line":190,"address":[],"length":0,"stats":{"Line":2}},{"line":193,"address":[1579744],"length":1,"stats":{"Line":1}},{"line":198,"address":[],"length":0,"stats":{"Line":1}},{"line":221,"address":[1579776,1580000,1580836,1580304,1579872,1580032,1580720,1580276,1580448,1580160,1580128,1580420,1580698,1580564,1580592,1579904],"length":1,"stats":{"Line":8}},{"line":225,"address":[1580336,1580046,1580752,1580606,1579790,1579918,1580480,1580192],"length":1,"stats":{"Line":8}},{"line":228,"address":[1580086,1579830,1580381,1580525,1579958,1580797,1580646,1580237],"length":1,"stats":{"Line":8}},{"line":264,"address":[1580864,1581040,1581359,1581183,1581216,1581007],"length":1,"stats":{"Line":3}},{"line":266,"address":[1581230,1581097,1580878,1580921,1581273,1581054],"length":1,"stats":{"Line":6}},{"line":267,"address":[],"length":0,"stats":{"Line":1}},{"line":271,"address":[1581337,1581161,1581119,1581295,1580985,1580943],"length":1,"stats":{"Line":6}},{"line":274,"address":[],"length":0,"stats":{"Line":3}},{"line":296,"address":[1581458,1581392,1581488,1581552],"length":1,"stats":{"Line":2}},{"line":297,"address":[],"length":0,"stats":{"Line":2}},{"line":298,"address":[],"length":0,"stats":{"Line":0}},{"line":303,"address":[],"length":0,"stats":{"Line":1}},{"line":304,"address":[1581597],"length":1,"stats":{"Line":1}},{"line":307,"address":[1581616],"length":1,"stats":{"Line":1}},{"line":312,"address":[],"length":0,"stats":{"Line":1}},{"line":335,"address":[],"length":0,"stats":{"Line":4}},{"line":338,"address":[],"length":0,"stats":{"Line":4}},{"line":342,"address":[],"length":0,"stats":{"Line":4}},{"line":379,"address":[],"length":0,"stats":{"Line":2}},{"line":382,"address":[],"length":0,"stats":{"Line":4}},{"line":383,"address":[],"length":0,"stats":{"Line":1}},{"line":387,"address":[],"length":0,"stats":{"Line":1}},{"line":390,"address":[],"length":0,"stats":{"Line":1}},{"line":410,"address":[],"length":0,"stats":{"Line":1}},{"line":411,"address":[],"length":0,"stats":{"Line":1}},{"line":412,"address":[],"length":0,"stats":{"Line":0}},{"line":434,"address":[],"length":0,"stats":{"Line":1}},{"line":435,"address":[],"length":0,"stats":{"Line":1}},{"line":455,"address":[],"length":0,"stats":{"Line":2}},{"line":456,"address":[],"length":0,"stats":{"Line":0}},{"line":476,"address":[],"length":0,"stats":{"Line":2}},{"line":477,"address":[],"length":0,"stats":{"Line":2}},{"line":494,"address":[],"length":0,"stats":{"Line":2}},{"line":495,"address":[],"length":0,"stats":{"Line":2}}],"covered":79,"coverable":82},{"path":["/","home","botahamec","Projects","happylock","src","collection","ref.rs"],"content":"use std::fmt::Debug;\n\nuse crate::lockable::{Lockable, OwnedLockable, RawLock, Sharable};\nuse crate::{Keyable, ThreadKey};\n\nuse super::utils::{\n\tget_locks, ordered_contains_duplicates, scoped_read, scoped_try_read, scoped_try_write,\n\tscoped_write,\n};\nuse super::{utils, LockGuard, RefLockCollection};\n\nimpl\u003c'a, L\u003e IntoIterator for \u0026'a RefLockCollection\u003c'a, L\u003e\nwhere\n\t\u0026'a L: IntoIterator,\n{\n\ttype Item = \u003c\u0026'a L as IntoIterator\u003e::Item;\n\ttype IntoIter = \u003c\u0026'a L as IntoIterator\u003e::IntoIter;\n\n\tfn into_iter(self) -\u003e Self::IntoIter {\n\t\tself.data.into_iter()\n\t}\n}\n\nunsafe impl\u003cL: Lockable\u003e RawLock for RefLockCollection\u003c'_, L\u003e {\n\t#[mutants::skip] // this should never run\n\t#[cfg(not(tarpaulin_include))]\n\tfn poison(\u0026self) {\n\t\tfor lock in \u0026self.locks {\n\t\t\tlock.poison();\n\t\t}\n\t}\n\n\tunsafe fn raw_write(\u0026self) {\n\t\tutils::ordered_write(\u0026self.locks)\n\t}\n\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool {\n\t\tutils::ordered_try_write(\u0026self.locks)\n\t}\n\n\tunsafe fn raw_unlock_write(\u0026self) {\n\t\tfor lock in \u0026self.locks {\n\t\t\tlock.raw_unlock_write();\n\t\t}\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tutils::ordered_read(\u0026self.locks)\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tutils::ordered_try_read(\u0026self.locks)\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tfor lock in \u0026self.locks {\n\t\t\tlock.raw_unlock_read();\n\t\t}\n\t}\n}\n\nunsafe impl\u003cL: Lockable\u003e Lockable for RefLockCollection\u003c'_, L\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= L::Guard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= L::DataMut\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tptrs.push(self)\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tself.data.guard()\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.data.data_mut()\n\t}\n}\n\nunsafe impl\u003cL: Sharable\u003e Sharable for RefLockCollection\u003c'_, L\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= L::ReadGuard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= L::DataRef\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tself.data.read_guard()\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tself.data.data_ref()\n\t}\n}\n\nimpl\u003cT: ?Sized, L: AsRef\u003cT\u003e\u003e AsRef\u003cT\u003e for RefLockCollection\u003c'_, L\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself.data.as_ref()\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cL: Debug\u003e Debug for RefLockCollection\u003c'_, L\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tf.debug_struct(stringify!(RefLockCollection))\n\t\t\t.field(\"data\", self.data)\n\t\t\t.finish_non_exhaustive()\n\t}\n}\n\n// safety: the RawLocks must be send because they come from the Send Lockable\n#[allow(clippy::non_send_fields_in_send_ty)]\nunsafe impl\u003cL: Send\u003e Send for RefLockCollection\u003c'_, L\u003e {}\nunsafe impl\u003cL: Sync\u003e Sync for RefLockCollection\u003c'_, L\u003e {}\n\nimpl\u003c'a, L: OwnedLockable + Default\u003e From\u003c\u0026'a L\u003e for RefLockCollection\u003c'a, L\u003e {\n\tfn from(value: \u0026'a L) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\nimpl\u003c'a, L: OwnedLockable\u003e RefLockCollection\u003c'a, L\u003e {\n\t/// Creates a new collection of owned locks.\n\t///\n\t/// Because the locks are owned, there's no need to do any checks for\n\t/// duplicate values.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t/// ```\n\t#[must_use]\n\tpub fn new(data: \u0026'a L) -\u003e Self {\n\t\tRefLockCollection {\n\t\t\tlocks: get_locks(data),\n\t\t\tdata,\n\t\t}\n\t}\n}\n\nimpl\u003cL\u003e RefLockCollection\u003c'_, L\u003e {\n\t/// Gets an immutable reference to the underlying data\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let data1 = Mutex::new(42);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // data1 and data2 refer to distinct mutexes, so this won't panic\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = RefLockCollection::try_new(\u0026data).unwrap();\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let guard = lock.child().0.lock(key);\n\t/// assert_eq!(*guard, 42);\n\t/// ```\n\t#[must_use]\n\tpub const fn child(\u0026self) -\u003e \u0026L {\n\t\tself.data\n\t}\n}\n\nimpl\u003c'a, L: Lockable\u003e RefLockCollection\u003c'a, L\u003e {\n\t/// Creates a new collections of locks.\n\t///\n\t/// # Safety\n\t///\n\t/// This results in undefined behavior if any locks are presented twice\n\t/// within this collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let data1 = Mutex::new(0);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // safety: data1 and data2 refer to distinct mutexes\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = unsafe { RefLockCollection::new_unchecked(\u0026data) };\n\t/// ```\n\t#[must_use]\n\tpub unsafe fn new_unchecked(data: \u0026'a L) -\u003e Self {\n\t\tSelf {\n\t\t\tdata,\n\t\t\tlocks: get_locks(data),\n\t\t}\n\t}\n\n\t/// Creates a new collection of locks.\n\t///\n\t/// This returns `None` if any locks are found twice in the given\n\t/// collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let data1 = Mutex::new(0);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // data1 and data2 refer to distinct mutexes, so this won't panic\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = RefLockCollection::try_new(\u0026data).unwrap();\n\t/// ```\n\t#[must_use]\n\tpub fn try_new(data: \u0026'a L) -\u003e Option\u003cSelf\u003e {\n\t\tlet locks = get_locks(data);\n\t\tif ordered_contains_duplicates(\u0026locks) {\n\t\t\treturn None;\n\t\t}\n\n\t\tSome(Self { data, locks })\n\t}\n\n\tpub fn scoped_lock\u003c's, R\u003e(\u0026's self, key: impl Keyable, f: impl Fn(L::DataMut\u003c's\u003e) -\u003e R) -\u003e R {\n\t\tscoped_write(self, key, f)\n\t}\n\n\tpub fn scoped_try_lock\u003c's, Key: Keyable, R\u003e(\n\t\t\u0026's self,\n\t\tkey: Key,\n\t\tf: impl Fn(L::DataMut\u003c's\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tscoped_try_write(self, key, f)\n\t}\n\n\t/// Locks the collection\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data. When the guard is dropped, the locks in the collection are also\n\t/// dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// ```\n\t#[must_use]\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::Guard\u003c'_\u003e\u003e {\n\t\tlet guard = unsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_write();\n\n\t\t\t// safety: we've locked all of this already\n\t\t\tself.data.guard()\n\t\t};\n\n\t\tLockGuard { guard, key }\n\t}\n\n\t/// Attempts to lock the without blocking.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// locks when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If any of the locks in the collection are already locked, then an error\n\t/// is returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// match lock.try_lock(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::Guard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tlet guard = unsafe {\n\t\t\tif !self.raw_try_write() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we've acquired the locks\n\t\t\tself.data.guard()\n\t\t};\n\n\t\tOk(LockGuard { guard, key })\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// let key = RefLockCollection::\u003c(Mutex\u003ci32\u003e, Mutex\u003c\u0026str\u003e)\u003e::unlock(guard);\n\t/// ```\n\t#[allow(clippy::missing_const_for_fn)]\n\tpub fn unlock(guard: LockGuard\u003cL::Guard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: Sharable\u003e RefLockCollection\u003c'_, L\u003e {\n\tpub fn scoped_read\u003c'a, R\u003e(\u0026'a self, key: impl Keyable, f: impl Fn(L::DataRef\u003c'a\u003e) -\u003e R) -\u003e R {\n\t\tscoped_read(self, key, f)\n\t}\n\n\tpub fn scoped_try_read\u003c'a, Key: Keyable, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(L::DataRef\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tscoped_try_read(self, key, f)\n\t}\n\n\t/// Locks the collection, so that other threads can still read from it\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data immutably. When the guard is dropped, the locks in the collection\n\t/// are also dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// assert_eq!(*guard.0, 0);\n\t/// assert_eq!(*guard.1, \"\");\n\t/// ```\n\t#[must_use]\n\tpub fn read(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_read();\n\n\t\t\tLockGuard {\n\t\t\t\t// safety: we've already acquired the lock\n\t\t\t\tguard: self.data.read_guard(),\n\t\t\t\tkey,\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Attempts to lock the without blocking, in such a way that other threads\n\t/// can still read from the collection.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// shared access when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If any of the locks in the collection are already locked, then an error\n\t/// is returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(5), RwLock::new(\"6\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// match lock.try_read(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// assert_eq!(*guard.0, 5);\n\t/// assert_eq!(*guard.1, \"6\");\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_read(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tlet guard = unsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_read() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we've acquired the locks\n\t\t\tself.data.read_guard()\n\t\t};\n\n\t\tOk(LockGuard { guard, key })\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// let key = RefLockCollection::\u003c(RwLock\u003ci32\u003e, RwLock\u003c\u0026str\u003e)\u003e::unlock_read(guard);\n\t/// ```\n\t#[allow(clippy::missing_const_for_fn)]\n\tpub fn unlock_read(guard: LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003c'a, L: 'a\u003e RefLockCollection\u003c'a, L\u003e\nwhere\n\t\u0026'a L: IntoIterator,\n{\n\t/// Returns an iterator over references to each value in the collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = [Mutex::new(26), Mutex::new(1)];\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// let mut iter = lock.iter();\n\t/// let mutex = iter.next().unwrap();\n\t/// let guard = mutex.lock(key);\n\t///\n\t/// assert_eq!(*guard, 26);\n\t/// ```\n\t#[must_use]\n\tpub fn iter(\u0026'a self) -\u003e \u003c\u0026'a L as IntoIterator\u003e::IntoIter {\n\t\tself.into_iter()\n\t}\n}\n\n#[cfg(test)]\nmod tests {\n\tuse super::*;\n\tuse crate::{Mutex, RwLock, ThreadKey};\n\n\t#[test]\n\tfn non_duplicates_allowed() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(1);\n\t\tassert!(RefLockCollection::try_new(\u0026[\u0026mutex1, \u0026mutex2]).is_some())\n\t}\n\n\t#[test]\n\tfn duplicates_not_allowed() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tassert!(RefLockCollection::try_new(\u0026[\u0026mutex1, \u0026mutex1]).is_none())\n\t}\n\n\t#[test]\n\tfn from() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [Mutex::new(\"foo\"), Mutex::new(\"bar\"), Mutex::new(\"baz\")];\n\t\tlet collection = RefLockCollection::from(\u0026mutexes);\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], \"foo\");\n\t\tassert_eq!(*guard[1], \"bar\");\n\t\tassert_eq!(*guard[2], \"baz\");\n\t}\n\n\t#[test]\n\tfn scoped_lock_changes_collection() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [Mutex::new(24), Mutex::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\t\tlet sum = collection.scoped_lock(\u0026mut key, |guard| {\n\t\t\t*guard[0] = 128;\n\t\t\t*guard[0] + *guard[1]\n\t\t});\n\n\t\tassert_eq!(sum, 128 + 42);\n\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], 128);\n\t\tassert_eq!(*guard[1], 42);\n\t}\n\n\t#[test]\n\tfn scoped_read_sees_changes() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\t\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\t\t*guard[0] = 128;\n\t\t});\n\n\t\tlet sum = collection.scoped_read(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 128);\n\t\t\tassert_eq!(*guard[1], 42);\n\t\t\t*guard[0] + *guard[1]\n\t\t});\n\n\t\tassert_eq!(sum, 128 + 42);\n\t}\n\n\t#[test]\n\tfn scoped_try_lock_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = [Mutex::new(1), Mutex::new(2)];\n\t\tlet collection = RefLockCollection::new(\u0026locks);\n\t\tlet guard = collection.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = collection.scoped_try_lock(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn scoped_try_read_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = [RwLock::new(1), RwLock::new(2)];\n\t\tlet collection = RefLockCollection::new(\u0026locks);\n\t\tlet guard = collection.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = collection.scoped_try_read(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn try_lock_succeeds_for_unlocked_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [Mutex::new(24), Mutex::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\t\tlet guard = collection.try_lock(key).unwrap();\n\t\tassert_eq!(*guard[0], 24);\n\t\tassert_eq!(*guard[1], 42);\n\t}\n\n\t#[test]\n\tfn try_lock_fails_for_locked_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [Mutex::new(24), Mutex::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = mutexes[1].lock(key);\n\t\t\t\tassert_eq!(*guard, 42);\n\t\t\t\tstd::mem::forget(guard);\n\t\t\t});\n\t\t});\n\n\t\tlet guard = collection.try_lock(key);\n\t\tassert!(guard.is_err());\n\t}\n\n\t#[test]\n\tfn try_read_succeeds_for_unlocked_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\t\tlet guard = collection.try_read(key).unwrap();\n\t\tassert_eq!(*guard[0], 24);\n\t\tassert_eq!(*guard[1], 42);\n\t}\n\n\t#[test]\n\tfn try_read_fails_for_locked_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = mutexes[1].write(key);\n\t\t\t\tassert_eq!(*guard, 42);\n\t\t\t\tstd::mem::forget(guard);\n\t\t\t});\n\t\t});\n\n\t\tlet guard = collection.try_read(key);\n\t\tassert!(guard.is_err());\n\t}\n\n\t#[test]\n\tfn can_read_twice_on_different_threads() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.read(key);\n\t\t\t\tassert_eq!(*guard[0], 24);\n\t\t\t\tassert_eq!(*guard[1], 42);\n\t\t\t\tstd::mem::forget(guard);\n\t\t\t});\n\t\t});\n\n\t\tlet guard = collection.try_read(key).unwrap();\n\t\tassert_eq!(*guard[0], 24);\n\t\tassert_eq!(*guard[1], 42);\n\t}\n\n\t#[test]\n\tfn into_ref_iterator() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1), Mutex::new(2)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\t\tfor (i, mutex) in (\u0026collection).into_iter().enumerate() {\n\t\t\tmutex.scoped_lock(\u0026mut key, |val| assert_eq!(*val, i))\n\t\t}\n\t}\n\n\t#[test]\n\tfn ref_iterator() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1), Mutex::new(2)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\t\tfor (i, mutex) in collection.iter().enumerate() {\n\t\t\tmutex.scoped_lock(\u0026mut key, |val| assert_eq!(*val, i))\n\t\t}\n\t}\n\n\t#[test]\n\tfn works_in_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex1 = RwLock::new(0);\n\t\tlet mutex2 = RwLock::new(1);\n\t\tlet collection0 = [\u0026mutex1, \u0026mutex2];\n\t\tlet collection1 = RefLockCollection::try_new(\u0026collection0).unwrap();\n\t\tlet collection = RefLockCollection::try_new(\u0026collection1).unwrap();\n\n\t\tlet mut guard = collection.lock(key);\n\t\tassert!(mutex1.is_locked());\n\t\tassert!(mutex2.is_locked());\n\t\tassert_eq!(*guard[0], 0);\n\t\tassert_eq!(*guard[1], 1);\n\t\t*guard[1] = 2;\n\t\tdrop(guard);\n\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet guard = collection.read(key);\n\t\tassert!(mutex1.is_locked());\n\t\tassert!(mutex2.is_locked());\n\t\tassert_eq!(*guard[0], 0);\n\t\tassert_eq!(*guard[1], 2);\n\t}\n\n\t#[test]\n\tfn unlock_collection_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = (Mutex::new(\"foo\"), Mutex::new(\"bar\"));\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\t\tlet guard = collection.lock(key);\n\n\t\tlet key = RefLockCollection::\u003c(Mutex\u003c_\u003e, Mutex\u003c_\u003e)\u003e::unlock(guard);\n\t\tassert!(collection.try_lock(key).is_ok())\n\t}\n\n\t#[test]\n\tfn read_unlock_collection_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = (RwLock::new(\"foo\"), RwLock::new(\"bar\"));\n\t\tlet collection = RefLockCollection::new(\u0026locks);\n\t\tlet guard = collection.read(key);\n\n\t\tlet key = RefLockCollection::\u003c(\u0026RwLock\u003c_\u003e, \u0026RwLock\u003c_\u003e)\u003e::unlock_read(guard);\n\t\tassert!(collection.try_lock(key).is_ok())\n\t}\n\n\t#[test]\n\tfn as_ref_works() {\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\n\t\tassert!(std::ptr::addr_eq(\u0026mutexes, collection.as_ref()))\n\t}\n\n\t#[test]\n\tfn child() {\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\n\t\tassert!(std::ptr::addr_eq(\u0026mutexes, collection.child()))\n\t}\n}\n","traces":[{"line":19,"address":[1582784],"length":1,"stats":{"Line":1}},{"line":20,"address":[],"length":0,"stats":{"Line":1}},{"line":33,"address":[],"length":0,"stats":{"Line":6}},{"line":34,"address":[],"length":0,"stats":{"Line":6}},{"line":37,"address":[],"length":0,"stats":{"Line":4}},{"line":38,"address":[],"length":0,"stats":{"Line":4}},{"line":41,"address":[1583056,1583152],"length":1,"stats":{"Line":2}},{"line":42,"address":[],"length":0,"stats":{"Line":4}},{"line":43,"address":[],"length":0,"stats":{"Line":2}},{"line":47,"address":[],"length":0,"stats":{"Line":3}},{"line":48,"address":[1583285,1583253,1583317],"length":1,"stats":{"Line":3}},{"line":51,"address":[],"length":0,"stats":{"Line":1}},{"line":52,"address":[],"length":0,"stats":{"Line":1}},{"line":55,"address":[],"length":0,"stats":{"Line":1}},{"line":56,"address":[],"length":0,"stats":{"Line":2}},{"line":57,"address":[],"length":0,"stats":{"Line":1}},{"line":73,"address":[],"length":0,"stats":{"Line":1}},{"line":74,"address":[],"length":0,"stats":{"Line":1}},{"line":77,"address":[],"length":0,"stats":{"Line":1}},{"line":78,"address":[],"length":0,"stats":{"Line":1}},{"line":81,"address":[],"length":0,"stats":{"Line":2}},{"line":82,"address":[],"length":0,"stats":{"Line":2}},{"line":97,"address":[],"length":0,"stats":{"Line":1}},{"line":98,"address":[],"length":0,"stats":{"Line":1}},{"line":101,"address":[],"length":0,"stats":{"Line":1}},{"line":102,"address":[],"length":0,"stats":{"Line":1}},{"line":107,"address":[],"length":0,"stats":{"Line":1}},{"line":108,"address":[],"length":0,"stats":{"Line":1}},{"line":128,"address":[],"length":0,"stats":{"Line":1}},{"line":129,"address":[],"length":0,"stats":{"Line":1}},{"line":149,"address":[],"length":0,"stats":{"Line":6}},{"line":151,"address":[],"length":0,"stats":{"Line":6}},{"line":178,"address":[],"length":0,"stats":{"Line":1}},{"line":179,"address":[],"length":0,"stats":{"Line":1}},{"line":205,"address":[],"length":0,"stats":{"Line":0}},{"line":208,"address":[],"length":0,"stats":{"Line":0}},{"line":231,"address":[],"length":0,"stats":{"Line":3}},{"line":232,"address":[],"length":0,"stats":{"Line":3}},{"line":233,"address":[],"length":0,"stats":{"Line":8}},{"line":234,"address":[],"length":0,"stats":{"Line":1}},{"line":237,"address":[],"length":0,"stats":{"Line":3}},{"line":240,"address":[],"length":0,"stats":{"Line":2}},{"line":241,"address":[],"length":0,"stats":{"Line":2}},{"line":244,"address":[1585472],"length":1,"stats":{"Line":1}},{"line":249,"address":[],"length":0,"stats":{"Line":1}},{"line":273,"address":[],"length":0,"stats":{"Line":5}},{"line":276,"address":[],"length":0,"stats":{"Line":5}},{"line":279,"address":[],"length":0,"stats":{"Line":5}},{"line":315,"address":[],"length":0,"stats":{"Line":3}},{"line":317,"address":[],"length":0,"stats":{"Line":7}},{"line":318,"address":[],"length":0,"stats":{"Line":1}},{"line":322,"address":[],"length":0,"stats":{"Line":5}},{"line":325,"address":[],"length":0,"stats":{"Line":3}},{"line":347,"address":[],"length":0,"stats":{"Line":1}},{"line":348,"address":[1586782],"length":1,"stats":{"Line":1}},{"line":349,"address":[],"length":0,"stats":{"Line":0}},{"line":354,"address":[],"length":0,"stats":{"Line":1}},{"line":355,"address":[],"length":0,"stats":{"Line":1}},{"line":358,"address":[1586896],"length":1,"stats":{"Line":1}},{"line":363,"address":[1586905],"length":1,"stats":{"Line":1}},{"line":387,"address":[],"length":0,"stats":{"Line":3}},{"line":390,"address":[],"length":0,"stats":{"Line":3}},{"line":394,"address":[],"length":0,"stats":{"Line":3}},{"line":431,"address":[1587328,1587498],"length":1,"stats":{"Line":1}},{"line":434,"address":[],"length":0,"stats":{"Line":2}},{"line":435,"address":[1587414],"length":1,"stats":{"Line":1}},{"line":439,"address":[],"length":0,"stats":{"Line":1}},{"line":442,"address":[1587459],"length":1,"stats":{"Line":1}},{"line":462,"address":[],"length":0,"stats":{"Line":1}},{"line":463,"address":[],"length":0,"stats":{"Line":1}},{"line":464,"address":[],"length":0,"stats":{"Line":0}},{"line":491,"address":[],"length":0,"stats":{"Line":1}},{"line":492,"address":[],"length":0,"stats":{"Line":1}}],"covered":69,"coverable":73},{"path":["/","home","botahamec","Projects","happylock","src","collection","retry.rs"],"content":"use std::cell::Cell;\nuse std::collections::HashSet;\n\nuse crate::collection::utils;\nuse crate::handle_unwind::handle_unwind;\nuse crate::lockable::{\n\tLockable, LockableGetMut, LockableIntoInner, OwnedLockable, RawLock, Sharable,\n};\nuse crate::{Keyable, ThreadKey};\n\nuse super::utils::{\n\tattempt_to_recover_reads_from_panic, attempt_to_recover_writes_from_panic, get_locks_unsorted,\n\tscoped_read, scoped_try_read, scoped_try_write, scoped_write,\n};\nuse super::{LockGuard, RetryingLockCollection};\n\n/// Checks that a collection contains no duplicate references to a lock.\nfn contains_duplicates\u003cL: Lockable\u003e(data: L) -\u003e bool {\n\tlet mut locks = Vec::new();\n\tdata.get_ptrs(\u0026mut locks);\n\t// cast to *const () so that the v-table pointers are not used for hashing\n\tlet locks = locks.into_iter().map(|l| (\u0026raw const *l).cast::\u003c()\u003e());\n\n\tlet mut locks_set = HashSet::with_capacity(locks.len());\n\tfor lock in locks {\n\t\tif !locks_set.insert(lock) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tfalse\n}\n\nunsafe impl\u003cL: Lockable\u003e RawLock for RetryingLockCollection\u003cL\u003e {\n\t#[mutants::skip] // this should never run\n\t#[cfg(not(tarpaulin_include))]\n\tfn poison(\u0026self) {\n\t\tlet locks = get_locks_unsorted(\u0026self.data);\n\t\tfor lock in locks {\n\t\t\tlock.poison();\n\t\t}\n\t}\n\n\tunsafe fn raw_write(\u0026self) {\n\t\tlet locks = get_locks_unsorted(\u0026self.data);\n\n\t\tif locks.is_empty() {\n\t\t\t// this probably prevents a panic later\n\t\t\treturn;\n\t\t}\n\n\t\t// these will be unlocked in case of a panic\n\t\tlet first_index = Cell::new(0);\n\t\tlet locked = Cell::new(0);\n\t\thandle_unwind(\n\t\t\t|| unsafe {\n\t\t\t\t'outer: loop {\n\t\t\t\t\t// This prevents us from entering a spin loop waiting for\n\t\t\t\t\t// the same lock to be unlocked\n\t\t\t\t\t// safety: we have the thread key\n\t\t\t\t\tlocks[first_index.get()].raw_write();\n\t\t\t\t\tfor (i, lock) in locks.iter().enumerate() {\n\t\t\t\t\t\tif i == first_index.get() {\n\t\t\t\t\t\t\t// we've already locked this one\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// If the lock has been killed, then this returns false\n\t\t\t\t\t\t// instead of panicking. This sounds like a problem, but if\n\t\t\t\t\t\t// it does return false, then the lock function is called\n\t\t\t\t\t\t// immediately after, causing a panic\n\t\t\t\t\t\t// safety: we have the thread key\n\t\t\t\t\t\tif lock.raw_try_write() {\n\t\t\t\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// safety: we already locked all of these\n\t\t\t\t\t\t\tattempt_to_recover_writes_from_panic(\u0026locks[0..i]);\n\t\t\t\t\t\t\tif first_index.get() \u003e= i {\n\t\t\t\t\t\t\t\t// safety: this is already locked and can't be\n\t\t\t\t\t\t\t\t// unlocked by the previous loop\n\t\t\t\t\t\t\t\tlocks[first_index.get()].raw_unlock_write();\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// nothing is locked anymore\n\t\t\t\t\t\t\tlocked.set(0);\n\n\t\t\t\t\t\t\t// call lock on this to prevent a spin loop\n\t\t\t\t\t\t\tfirst_index.set(i);\n\t\t\t\t\t\t\tcontinue 'outer;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// safety: we locked all the data\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t},\n\t\t\t|| {\n\t\t\t\tutils::attempt_to_recover_writes_from_panic(\u0026locks[0..locked.get()]);\n\t\t\t\tif first_index.get() \u003e= locked.get() {\n\t\t\t\t\tlocks[first_index.get()].raw_unlock_write();\n\t\t\t\t}\n\t\t\t},\n\t\t)\n\t}\n\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool {\n\t\tlet locks = get_locks_unsorted(\u0026self.data);\n\n\t\tif locks.is_empty() {\n\t\t\t// this is an interesting case, but it doesn't give us access to\n\t\t\t// any data, and can't possibly cause a deadlock\n\t\t\treturn true;\n\t\t}\n\n\t\t// these will be unlocked in case of a panic\n\t\tlet locked = Cell::new(0);\n\t\thandle_unwind(\n\t\t\t|| unsafe {\n\t\t\t\tfor (i, lock) in locks.iter().enumerate() {\n\t\t\t\t\t// safety: we have the thread key\n\t\t\t\t\tif lock.raw_try_write() {\n\t\t\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// safety: we already locked all of these\n\t\t\t\t\t\tattempt_to_recover_writes_from_panic(\u0026locks[0..i]);\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ttrue\n\t\t\t},\n\t\t\t|| utils::attempt_to_recover_writes_from_panic(\u0026locks[0..locked.get()]),\n\t\t)\n\t}\n\n\tunsafe fn raw_unlock_write(\u0026self) {\n\t\tlet locks = get_locks_unsorted(\u0026self.data);\n\n\t\tfor lock in locks {\n\t\t\tlock.raw_unlock_write();\n\t\t}\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tlet locks = get_locks_unsorted(\u0026self.data);\n\n\t\tif locks.is_empty() {\n\t\t\t// this probably prevents a panic later\n\t\t\treturn;\n\t\t}\n\n\t\tlet locked = Cell::new(0);\n\t\tlet first_index = Cell::new(0);\n\t\thandle_unwind(\n\t\t\t|| 'outer: loop {\n\t\t\t\t// safety: we have the thread key\n\t\t\t\tlocks[first_index.get()].raw_read();\n\t\t\t\tfor (i, lock) in locks.iter().enumerate() {\n\t\t\t\t\tif i == first_index.get() {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// safety: we have the thread key\n\t\t\t\t\tif lock.raw_try_read() {\n\t\t\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// safety: we already locked all of these\n\t\t\t\t\t\tattempt_to_recover_reads_from_panic(\u0026locks[0..i]);\n\n\t\t\t\t\t\tif first_index.get() \u003e= i {\n\t\t\t\t\t\t\t// safety: this is already locked and can't be unlocked\n\t\t\t\t\t\t\t// by the previous loop\n\t\t\t\t\t\t\tlocks[first_index.get()].raw_unlock_read();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// these are no longer locked\n\t\t\t\t\t\tlocked.set(0);\n\n\t\t\t\t\t\t// don't go into a spin loop, wait for this one to lock\n\t\t\t\t\t\tfirst_index.set(i);\n\t\t\t\t\t\tcontinue 'outer;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// safety: we locked all the data\n\t\t\t\tbreak;\n\t\t\t},\n\t\t\t|| {\n\t\t\t\tutils::attempt_to_recover_reads_from_panic(\u0026locks[0..locked.get()]);\n\t\t\t\tif first_index.get() \u003e= locked.get() {\n\t\t\t\t\tlocks[first_index.get()].raw_unlock_read();\n\t\t\t\t}\n\t\t\t},\n\t\t)\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tlet locks = get_locks_unsorted(\u0026self.data);\n\n\t\tif locks.is_empty() {\n\t\t\t// this is an interesting case, but it doesn't give us access to\n\t\t\t// any data, and can't possibly cause a deadlock\n\t\t\treturn true;\n\t\t}\n\n\t\tlet locked = Cell::new(0);\n\t\thandle_unwind(\n\t\t\t|| unsafe {\n\t\t\t\tfor (i, lock) in locks.iter().enumerate() {\n\t\t\t\t\t// safety: we have the thread key\n\t\t\t\t\tif lock.raw_try_read() {\n\t\t\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// safety: we already locked all of these\n\t\t\t\t\t\tattempt_to_recover_reads_from_panic(\u0026locks[0..i]);\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ttrue\n\t\t\t},\n\t\t\t|| utils::attempt_to_recover_reads_from_panic(\u0026locks[0..locked.get()]),\n\t\t)\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tlet locks = get_locks_unsorted(\u0026self.data);\n\n\t\tfor lock in locks {\n\t\t\tlock.raw_unlock_read();\n\t\t}\n\t}\n}\n\nunsafe impl\u003cL: Lockable\u003e Lockable for RetryingLockCollection\u003cL\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= L::Guard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= L::DataMut\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tptrs.push(self)\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tself.data.guard()\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.data.data_mut()\n\t}\n}\n\nunsafe impl\u003cL: Sharable\u003e Sharable for RetryingLockCollection\u003cL\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= L::ReadGuard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= L::DataRef\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tself.data.read_guard()\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tself.data.data_ref()\n\t}\n}\n\nunsafe impl\u003cL: OwnedLockable\u003e OwnedLockable for RetryingLockCollection\u003cL\u003e {}\n\nimpl\u003cL: LockableGetMut\u003e LockableGetMut for RetryingLockCollection\u003cL\u003e {\n\ttype Inner\u003c'a\u003e\n\t\t= L::Inner\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tself.data.get_mut()\n\t}\n}\n\nimpl\u003cL: LockableIntoInner\u003e LockableIntoInner for RetryingLockCollection\u003cL\u003e {\n\ttype Inner = L::Inner;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tself.data.into_inner()\n\t}\n}\n\nimpl\u003cL\u003e IntoIterator for RetryingLockCollection\u003cL\u003e\nwhere\n\tL: IntoIterator,\n{\n\ttype Item = \u003cL as IntoIterator\u003e::Item;\n\ttype IntoIter = \u003cL as IntoIterator\u003e::IntoIter;\n\n\tfn into_iter(self) -\u003e Self::IntoIter {\n\t\tself.data.into_iter()\n\t}\n}\n\nimpl\u003c'a, L\u003e IntoIterator for \u0026'a RetryingLockCollection\u003cL\u003e\nwhere\n\t\u0026'a L: IntoIterator,\n{\n\ttype Item = \u003c\u0026'a L as IntoIterator\u003e::Item;\n\ttype IntoIter = \u003c\u0026'a L as IntoIterator\u003e::IntoIter;\n\n\tfn into_iter(self) -\u003e Self::IntoIter {\n\t\tself.data.into_iter()\n\t}\n}\n\nimpl\u003c'a, L\u003e IntoIterator for \u0026'a mut RetryingLockCollection\u003cL\u003e\nwhere\n\t\u0026'a mut L: IntoIterator,\n{\n\ttype Item = \u003c\u0026'a mut L as IntoIterator\u003e::Item;\n\ttype IntoIter = \u003c\u0026'a mut L as IntoIterator\u003e::IntoIter;\n\n\tfn into_iter(self) -\u003e Self::IntoIter {\n\t\tself.data.into_iter()\n\t}\n}\n\nimpl\u003cL: OwnedLockable, I: FromIterator\u003cL\u003e + OwnedLockable\u003e FromIterator\u003cL\u003e\n\tfor RetryingLockCollection\u003cI\u003e\n{\n\tfn from_iter\u003cT: IntoIterator\u003cItem = L\u003e\u003e(iter: T) -\u003e Self {\n\t\tlet iter: I = iter.into_iter().collect();\n\t\tSelf::new(iter)\n\t}\n}\n\nimpl\u003cE: OwnedLockable + Extend\u003cL\u003e, L: OwnedLockable\u003e Extend\u003cL\u003e for RetryingLockCollection\u003cE\u003e {\n\tfn extend\u003cT: IntoIterator\u003cItem = L\u003e\u003e(\u0026mut self, iter: T) {\n\t\tself.data.extend(iter)\n\t}\n}\n\nimpl\u003cT: ?Sized, L: AsRef\u003cT\u003e\u003e AsRef\u003cT\u003e for RetryingLockCollection\u003cL\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself.data.as_ref()\n\t}\n}\n\nimpl\u003cT: ?Sized, L: AsMut\u003cT\u003e\u003e AsMut\u003cT\u003e for RetryingLockCollection\u003cL\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself.data.as_mut()\n\t}\n}\n\nimpl\u003cL: OwnedLockable + Default\u003e Default for RetryingLockCollection\u003cL\u003e {\n\tfn default() -\u003e Self {\n\t\tSelf::new(L::default())\n\t}\n}\n\nimpl\u003cL: OwnedLockable\u003e From\u003cL\u003e for RetryingLockCollection\u003cL\u003e {\n\tfn from(value: L) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\nimpl\u003cL: OwnedLockable\u003e RetryingLockCollection\u003cL\u003e {\n\t/// Creates a new collection of owned locks.\n\t///\n\t/// Because the locks are owned, there's no need to do any checks for\n\t/// duplicate values. The locks also don't need to be sorted by memory\n\t/// address because they aren't used anywhere else.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t/// ```\n\t#[must_use]\n\tpub const fn new(data: L) -\u003e Self {\n\t\t// safety: the data cannot cannot contain references\n\t\tunsafe { Self::new_unchecked(data) }\n\t}\n}\n\nimpl\u003c'a, L: OwnedLockable\u003e RetryingLockCollection\u003c\u0026'a L\u003e {\n\t/// Creates a new collection of owned locks.\n\t///\n\t/// Because the locks are owned, there's no need to do any checks for\n\t/// duplicate values.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new_ref(\u0026data);\n\t/// ```\n\t#[must_use]\n\tpub const fn new_ref(data: \u0026'a L) -\u003e Self {\n\t\t// safety: the data cannot cannot contain references\n\t\tunsafe { Self::new_unchecked(data) }\n\t}\n}\n\nimpl\u003cL\u003e RetryingLockCollection\u003cL\u003e {\n\t/// Creates a new collections of locks.\n\t///\n\t/// # Safety\n\t///\n\t/// This results in undefined behavior if any locks are presented twice\n\t/// within this collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let data1 = Mutex::new(0);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // safety: data1 and data2 refer to distinct mutexes\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = unsafe { RetryingLockCollection::new_unchecked(\u0026data) };\n\t/// ```\n\t#[must_use]\n\tpub const unsafe fn new_unchecked(data: L) -\u003e Self {\n\t\tSelf { data }\n\t}\n\n\t/// Gets an immutable reference to the underlying collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let data = (Mutex::new(42), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let inner = lock.child();\n\t/// let guard = inner.0.lock(key);\n\t/// assert_eq!(*guard, 42);\n\t/// ```\n\t#[must_use]\n\tpub const fn child(\u0026self) -\u003e \u0026L {\n\t\t\u0026self.data\n\t}\n\n\t/// Gets a mutable reference to the underlying collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let data = (Mutex::new(42), Mutex::new(\"\"));\n\t/// let mut lock = RetryingLockCollection::new(data);\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut inner = lock.child_mut();\n\t/// let guard = inner.0.get_mut();\n\t/// assert_eq!(*guard, 42);\n\t/// ```\n\t#[must_use]\n\tpub fn child_mut(\u0026mut self) -\u003e \u0026mut L {\n\t\t\u0026mut self.data\n\t}\n\n\t/// Gets the underlying collection, consuming this collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let data = (Mutex::new(42), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let inner = lock.into_child();\n\t/// let guard = inner.0.lock(key);\n\t/// assert_eq!(*guard, 42);\n\t/// ```\n\t#[must_use]\n\tpub fn into_child(self) -\u003e L {\n\t\tself.data\n\t}\n}\n\nimpl\u003cL: Lockable\u003e RetryingLockCollection\u003cL\u003e {\n\t/// Creates a new collection of locks.\n\t///\n\t/// This returns `None` if any locks are found twice in the given\n\t/// collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let data1 = Mutex::new(0);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // data1 and data2 refer to distinct mutexes, so this won't panic\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = RetryingLockCollection::try_new(\u0026data).unwrap();\n\t/// ```\n\t#[must_use]\n\tpub fn try_new(data: L) -\u003e Option\u003cSelf\u003e {\n\t\t// safety: the data is checked for duplicates before returning the collection\n\t\t(!contains_duplicates(\u0026data)).then_some(unsafe { Self::new_unchecked(data) })\n\t}\n\n\tpub fn scoped_lock\u003c'a, R\u003e(\u0026'a self, key: impl Keyable, f: impl Fn(L::DataMut\u003c'a\u003e) -\u003e R) -\u003e R {\n\t\tscoped_write(self, key, f)\n\t}\n\n\tpub fn scoped_try_lock\u003c'a, Key: Keyable, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(L::DataMut\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tscoped_try_write(self, key, f)\n\t}\n\n\t/// Locks the collection\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data. When the guard is dropped, the locks in the collection are also\n\t/// dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// ```\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::Guard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\t// safety: we're taking the thread key\n\t\t\tself.raw_write();\n\n\t\t\tLockGuard {\n\t\t\t\t// safety: we just locked the collection\n\t\t\t\tguard: self.guard(),\n\t\t\t\tkey,\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Attempts to lock the without blocking.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// locks when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If any of the locks in the collection are already locked, then an error\n\t/// is returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// match lock.try_lock(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::Guard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tunsafe {\n\t\t\t// safety: we're taking the thread key\n\t\t\tif self.raw_try_write() {\n\t\t\t\tOk(LockGuard {\n\t\t\t\t\t// safety: we just succeeded in locking everything\n\t\t\t\t\tguard: self.guard(),\n\t\t\t\t\tkey,\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\tErr(key)\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// let key = RetryingLockCollection::\u003c(Mutex\u003ci32\u003e, Mutex\u003c\u0026str\u003e)\u003e::unlock(guard);\n\t/// ```\n\tpub fn unlock(guard: LockGuard\u003cL::Guard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: Sharable\u003e RetryingLockCollection\u003cL\u003e {\n\tpub fn scoped_read\u003c'a, R\u003e(\u0026'a self, key: impl Keyable, f: impl Fn(L::DataRef\u003c'a\u003e) -\u003e R) -\u003e R {\n\t\tscoped_read(self, key, f)\n\t}\n\n\tpub fn scoped_try_read\u003c'a, Key: Keyable, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(L::DataRef\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tscoped_try_read(self, key, f)\n\t}\n\n\t/// Locks the collection, so that other threads can still read from it\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data immutably. When the guard is dropped, the locks in the collection\n\t/// are also dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// assert_eq!(*guard.0, 0);\n\t/// assert_eq!(*guard.1, \"\");\n\t/// ```\n\tpub fn read(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\t// safety: we're taking the thread key\n\t\t\tself.raw_read();\n\n\t\t\tLockGuard {\n\t\t\t\t// safety: we just locked the collection\n\t\t\t\tguard: self.read_guard(),\n\t\t\t\tkey,\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Attempts to lock the without blocking, in such a way that other threads\n\t/// can still read from the collection.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// shared access when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If shared access cannot be acquired at this time, then an error is\n\t/// returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(5), RwLock::new(\"6\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// match lock.try_read(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// assert_eq!(*guard.0, 5);\n\t/// assert_eq!(*guard.1, \"6\");\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_read(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tunsafe {\n\t\t\t// safety: we're taking the thread key\n\t\t\tif !self.raw_try_read() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\tOk(LockGuard {\n\t\t\t\t// safety: we just succeeded in locking everything\n\t\t\t\tguard: self.read_guard(),\n\t\t\t\tkey,\n\t\t\t})\n\t\t}\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// let key = RetryingLockCollection::\u003c(RwLock\u003ci32\u003e, RwLock\u003c\u0026str\u003e)\u003e::unlock_read(guard);\n\t/// ```\n\tpub fn unlock_read(guard: LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: LockableGetMut\u003e RetryingLockCollection\u003cL\u003e {\n\t/// Gets a mutable reference to the data behind this\n\t/// `RetryingLockCollection`.\n\t///\n\t/// Since this call borrows the `RetryingLockCollection` mutably, no actual\n\t/// locking needs to take place - the mutable borrow statically guarantees\n\t/// no locks exist.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let mut mutex = RetryingLockCollection::new([Mutex::new(0), Mutex::new(0)]);\n\t/// assert_eq!(mutex.get_mut(), [\u0026mut 0, \u0026mut 0]);\n\t/// ```\n\tpub fn get_mut(\u0026mut self) -\u003e L::Inner\u003c'_\u003e {\n\t\tLockableGetMut::get_mut(self)\n\t}\n}\n\nimpl\u003cL: LockableIntoInner\u003e RetryingLockCollection\u003cL\u003e {\n\t/// Consumes this `RetryingLockCollection`, returning the underlying data.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let mutex = RetryingLockCollection::new([Mutex::new(0), Mutex::new(0)]);\n\t/// assert_eq!(mutex.into_inner(), [0, 0]);\n\t/// ```\n\tpub fn into_inner(self) -\u003e L::Inner {\n\t\tLockableIntoInner::into_inner(self)\n\t}\n}\n\nimpl\u003c'a, L: 'a\u003e RetryingLockCollection\u003cL\u003e\nwhere\n\t\u0026'a L: IntoIterator,\n{\n\t/// Returns an iterator over references to each value in the collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = [Mutex::new(26), Mutex::new(1)];\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let mut iter = lock.iter();\n\t/// let mutex = iter.next().unwrap();\n\t/// let guard = mutex.lock(key);\n\t///\n\t/// assert_eq!(*guard, 26);\n\t/// ```\n\t#[must_use]\n\tpub fn iter(\u0026'a self) -\u003e \u003c\u0026'a L as IntoIterator\u003e::IntoIter {\n\t\tself.into_iter()\n\t}\n}\n\nimpl\u003c'a, L: 'a\u003e RetryingLockCollection\u003cL\u003e\nwhere\n\t\u0026'a mut L: IntoIterator,\n{\n\t/// Returns an iterator over mutable references to each value in the\n\t/// collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = [Mutex::new(26), Mutex::new(1)];\n\t/// let mut lock = RetryingLockCollection::new(data);\n\t///\n\t/// let mut iter = lock.iter_mut();\n\t/// let mutex = iter.next().unwrap();\n\t///\n\t/// assert_eq!(*mutex.as_mut(), 26);\n\t/// ```\n\t#[must_use]\n\tpub fn iter_mut(\u0026'a mut self) -\u003e \u003c\u0026'a mut L as IntoIterator\u003e::IntoIter {\n\t\tself.into_iter()\n\t}\n}\n\n#[cfg(test)]\nmod tests {\n\tuse super::*;\n\tuse crate::collection::BoxedLockCollection;\n\tuse crate::{Mutex, RwLock, ThreadKey};\n\n\t#[test]\n\tfn nonduplicate_lock_references_are_allowed() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(0);\n\t\tassert!(RetryingLockCollection::try_new([\u0026mutex1, \u0026mutex2]).is_some());\n\t}\n\n\t#[test]\n\tfn duplicate_lock_references_are_disallowed() {\n\t\tlet mutex = Mutex::new(0);\n\t\tassert!(RetryingLockCollection::try_new([\u0026mutex, \u0026mutex]).is_none());\n\t}\n\n\t#[test]\n\t#[allow(clippy::float_cmp)]\n\tfn uses_correct_default() {\n\t\tlet collection =\n\t\t\tRetryingLockCollection::\u003c(RwLock\u003cf64\u003e, Mutex\u003cOption\u003ci32\u003e\u003e, Mutex\u003cusize\u003e)\u003e::default();\n\t\tlet tuple = collection.into_inner();\n\t\tassert_eq!(tuple.0, 0.0);\n\t\tassert!(tuple.1.is_none());\n\t\tassert_eq!(tuple.2, 0)\n\t}\n\n\t#[test]\n\tfn from() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tRetryingLockCollection::from([Mutex::new(\"foo\"), Mutex::new(\"bar\"), Mutex::new(\"baz\")]);\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], \"foo\");\n\t\tassert_eq!(*guard[1], \"bar\");\n\t\tassert_eq!(*guard[2], \"baz\");\n\t}\n\n\t#[test]\n\tfn new_ref_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet collection = RetryingLockCollection::new_ref(\u0026mutexes);\n\t\tcollection.scoped_lock(key, |guard| {\n\t\t\tassert_eq!(*guard[0], 0);\n\t\t\tassert_eq!(*guard[1], 1);\n\t\t})\n\t}\n\n\t#[test]\n\tfn scoped_read_sees_changes() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = RetryingLockCollection::new(mutexes);\n\t\tcollection.scoped_lock(\u0026mut key, |guard| *guard[0] = 128);\n\n\t\tlet sum = collection.scoped_read(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 128);\n\t\t\tassert_eq!(*guard[1], 42);\n\t\t\t*guard[0] + *guard[1]\n\t\t});\n\n\t\tassert_eq!(sum, 128 + 42);\n\t}\n\n\t#[test]\n\tfn get_mut_affects_scoped_read() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet mut collection = RetryingLockCollection::new(mutexes);\n\t\tlet guard = collection.get_mut();\n\t\t*guard[0] = 128;\n\n\t\tlet sum = collection.scoped_read(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 128);\n\t\t\tassert_eq!(*guard[1], 42);\n\t\t\t*guard[0] + *guard[1]\n\t\t});\n\n\t\tassert_eq!(sum, 128 + 42);\n\t}\n\n\t#[test]\n\tfn scoped_try_lock_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = RetryingLockCollection::new([Mutex::new(1), Mutex::new(2)]);\n\t\tlet guard = collection.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = collection.scoped_try_lock(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn scoped_try_read_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = RetryingLockCollection::new([RwLock::new(1), RwLock::new(2)]);\n\t\tlet guard = collection.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = collection.scoped_try_read(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn try_lock_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = RetryingLockCollection::new([Mutex::new(1), Mutex::new(2)]);\n\t\tlet guard = collection.try_lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_lock(key);\n\t\t\t\tassert!(guard.is_err());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn try_read_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = RetryingLockCollection::new([RwLock::new(1), RwLock::new(2)]);\n\t\tlet guard = collection.try_read(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_read(key);\n\t\t\t\tassert!(guard.is_ok());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn try_read_fails_for_locked_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = RetryingLockCollection::new_ref(\u0026mutexes);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = mutexes[1].write(key);\n\t\t\t\tassert_eq!(*guard, 42);\n\t\t\t\tstd::mem::forget(guard);\n\t\t\t});\n\t\t});\n\n\t\tlet guard = collection.try_read(key);\n\t\tassert!(guard.is_err());\n\t}\n\n\t#[test]\n\tfn locks_all_inner_mutexes() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(0);\n\t\tlet collection = RetryingLockCollection::try_new([\u0026mutex1, \u0026mutex2]).unwrap();\n\n\t\tlet guard = collection.lock(key);\n\n\t\tassert!(mutex1.is_locked());\n\t\tassert!(mutex2.is_locked());\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn locks_all_inner_rwlocks() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet rwlock1 = RwLock::new(0);\n\t\tlet rwlock2 = RwLock::new(0);\n\t\tlet collection = RetryingLockCollection::try_new([\u0026rwlock1, \u0026rwlock2]).unwrap();\n\n\t\tlet guard = collection.read(key);\n\n\t\tassert!(rwlock1.is_locked());\n\t\tassert!(rwlock2.is_locked());\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn works_with_other_collections() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(0);\n\t\tlet collection = BoxedLockCollection::try_new(\n\t\t\tRetryingLockCollection::try_new([\u0026mutex1, \u0026mutex2]).unwrap(),\n\t\t)\n\t\t.unwrap();\n\n\t\tlet guard = collection.lock(key);\n\n\t\tassert!(mutex1.is_locked());\n\t\tassert!(mutex2.is_locked());\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn from_iterator() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection: RetryingLockCollection\u003cVec\u003cMutex\u003c\u0026str\u003e\u003e\u003e =\n\t\t\t[Mutex::new(\"foo\"), Mutex::new(\"bar\"), Mutex::new(\"baz\")]\n\t\t\t\t.into_iter()\n\t\t\t\t.collect();\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], \"foo\");\n\t\tassert_eq!(*guard[1], \"bar\");\n\t\tassert_eq!(*guard[2], \"baz\");\n\t}\n\n\t#[test]\n\tfn into_owned_iterator() {\n\t\tlet collection = RetryingLockCollection::new([Mutex::new(0), Mutex::new(1), Mutex::new(2)]);\n\t\tfor (i, mutex) in collection.into_iter().enumerate() {\n\t\t\tassert_eq!(mutex.into_inner(), i);\n\t\t}\n\t}\n\n\t#[test]\n\tfn into_ref_iterator() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet collection = RetryingLockCollection::new([Mutex::new(0), Mutex::new(1), Mutex::new(2)]);\n\t\tfor (i, mutex) in (\u0026collection).into_iter().enumerate() {\n\t\t\tmutex.scoped_lock(\u0026mut key, |val| assert_eq!(*val, i))\n\t\t}\n\t}\n\n\t#[test]\n\tfn ref_iterator() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet collection = RetryingLockCollection::new([Mutex::new(0), Mutex::new(1), Mutex::new(2)]);\n\t\tfor (i, mutex) in collection.iter().enumerate() {\n\t\t\tmutex.scoped_lock(\u0026mut key, |val| assert_eq!(*val, i))\n\t\t}\n\t}\n\n\t#[test]\n\tfn mut_iterator() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mut collection =\n\t\t\tRetryingLockCollection::new([Mutex::new(0), Mutex::new(1), Mutex::new(2)]);\n\t\tfor (i, mutex) in collection.iter_mut().enumerate() {\n\t\t\tmutex.scoped_lock(\u0026mut key, |val| assert_eq!(*val, i))\n\t\t}\n\t}\n\n\t#[test]\n\tfn extend_collection() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(0);\n\t\tlet mut collection = RetryingLockCollection::new(vec![mutex1]);\n\n\t\tcollection.extend([mutex2]);\n\n\t\tassert_eq!(collection.into_inner().len(), 2);\n\t}\n\n\t#[test]\n\tfn lock_empty_lock_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection: RetryingLockCollection\u003c[RwLock\u003ci32\u003e; 0]\u003e = RetryingLockCollection::new([]);\n\n\t\tlet guard = collection.lock(key);\n\t\tassert!(guard.len() == 0);\n\t\tlet key = RetryingLockCollection::\u003c[RwLock\u003c_\u003e; 0]\u003e::unlock(guard);\n\n\t\tlet guard = collection.read(key);\n\t\tassert!(guard.len() == 0);\n\t}\n\n\t#[test]\n\tfn read_empty_lock_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection: RetryingLockCollection\u003c[RwLock\u003ci32\u003e; 0]\u003e = RetryingLockCollection::new([]);\n\n\t\tlet guard = collection.read(key);\n\t\tassert!(guard.len() == 0);\n\t\tlet key = RetryingLockCollection::\u003c[RwLock\u003c_\u003e; 0]\u003e::unlock_read(guard);\n\n\t\tlet guard = collection.lock(key);\n\t\tassert!(guard.len() == 0);\n\t}\n\n\t#[test]\n\tfn as_ref_works() {\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet collection = RetryingLockCollection::new_ref(\u0026mutexes);\n\n\t\tassert!(std::ptr::addr_eq(\u0026mutexes, collection.as_ref()))\n\t}\n\n\t#[test]\n\tfn as_mut_works() {\n\t\tlet mut mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet mut collection = RetryingLockCollection::new(\u0026mut mutexes);\n\n\t\tcollection.as_mut()[0] = Mutex::new(42);\n\n\t\tassert_eq!(*collection.as_mut()[0].get_mut(), 42);\n\t}\n\n\t#[test]\n\tfn child() {\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet collection = RetryingLockCollection::new_ref(\u0026mutexes);\n\n\t\tassert!(std::ptr::addr_eq(\u0026mutexes, *collection.child()))\n\t}\n\n\t#[test]\n\tfn child_mut_works() {\n\t\tlet mut mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet mut collection = RetryingLockCollection::new(\u0026mut mutexes);\n\n\t\tcollection.child_mut()[0] = Mutex::new(42);\n\n\t\tassert_eq!(*collection.child_mut()[0].get_mut(), 42);\n\t}\n\n\t#[test]\n\tfn into_child_works() {\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet mut collection = RetryingLockCollection::new(mutexes);\n\n\t\tcollection.child_mut()[0] = Mutex::new(42);\n\n\t\tassert_eq!(\n\t\t\t*collection\n\t\t\t\t.into_child()\n\t\t\t\t.as_mut()\n\t\t\t\t.get_mut(0)\n\t\t\t\t.unwrap()\n\t\t\t\t.get_mut(),\n\t\t\t42\n\t\t);\n\t}\n}\n","traces":[{"line":18,"address":[213893,213208,213168],"length":1,"stats":{"Line":11}},{"line":19,"address":[1090812,1091580],"length":1,"stats":{"Line":11}},{"line":20,"address":[1090875,1091643],"length":1,"stats":{"Line":11}},{"line":22,"address":[],"length":0,"stats":{"Line":33}},{"line":24,"address":[186400,185566,186334,185632],"length":1,"stats":{"Line":22}},{"line":25,"address":[201469,201427,201288,201191],"length":1,"stats":{"Line":44}},{"line":26,"address":[1092230,1092165,1091397,1091462],"length":1,"stats":{"Line":23}},{"line":27,"address":[186784,186016],"length":1,"stats":{"Line":1}},{"line":31,"address":[],"length":0,"stats":{"Line":11}},{"line":44,"address":[],"length":0,"stats":{"Line":11}},{"line":45,"address":[172956],"length":1,"stats":{"Line":11}},{"line":47,"address":[227126,227178],"length":1,"stats":{"Line":22}},{"line":49,"address":[],"length":0,"stats":{"Line":0}},{"line":53,"address":[],"length":0,"stats":{"Line":20}},{"line":54,"address":[219626],"length":1,"stats":{"Line":10}},{"line":56,"address":[184784],"length":1,"stats":{"Line":20}},{"line":57,"address":[],"length":0,"stats":{"Line":0}},{"line":61,"address":[212449],"length":1,"stats":{"Line":10}},{"line":62,"address":[184854,185006],"length":1,"stats":{"Line":20}},{"line":63,"address":[210824],"length":1,"stats":{"Line":10}},{"line":65,"address":[],"length":0,"stats":{"Line":0}},{"line":73,"address":[185080],"length":1,"stats":{"Line":10}},{"line":74,"address":[1096138,1095722,1093898,1095578,1094602,1096282,1093338,1092778,1093482,1092922,1094042,1094458,1095018,1095162],"length":1,"stats":{"Line":18}},{"line":77,"address":[210880],"length":1,"stats":{"Line":1}},{"line":78,"address":[185140],"length":1,"stats":{"Line":1}},{"line":81,"address":[185251],"length":1,"stats":{"Line":1}},{"line":85,"address":[140775],"length":1,"stats":{"Line":1}},{"line":88,"address":[212879],"length":1,"stats":{"Line":1}},{"line":89,"address":[],"length":0,"stats":{"Line":0}},{"line":94,"address":[],"length":0,"stats":{"Line":0}},{"line":97,"address":[184608],"length":1,"stats":{"Line":11}},{"line":98,"address":[140921],"length":1,"stats":{"Line":1}},{"line":99,"address":[213060],"length":1,"stats":{"Line":1}},{"line":100,"address":[],"length":0,"stats":{"Line":1}},{"line":106,"address":[218925,218736],"length":1,"stats":{"Line":3}},{"line":107,"address":[203446],"length":1,"stats":{"Line":3}},{"line":109,"address":[],"length":0,"stats":{"Line":6}},{"line":112,"address":[218840],"length":1,"stats":{"Line":0}},{"line":116,"address":[203550,203508],"length":1,"stats":{"Line":6}},{"line":118,"address":[200464],"length":1,"stats":{"Line":3}},{"line":119,"address":[184415,184273],"length":1,"stats":{"Line":6}},{"line":121,"address":[200657],"length":1,"stats":{"Line":3}},{"line":122,"address":[200732],"length":1,"stats":{"Line":3}},{"line":125,"address":[1097776],"length":1,"stats":{"Line":2}},{"line":126,"address":[200725],"length":1,"stats":{"Line":3}},{"line":130,"address":[184408],"length":1,"stats":{"Line":1}},{"line":132,"address":[1097888,1097902],"length":1,"stats":{"Line":2}},{"line":136,"address":[219216,219461,219189,218944],"length":1,"stats":{"Line":3}},{"line":137,"address":[219234,218962],"length":1,"stats":{"Line":3}},{"line":139,"address":[219096,219150,219422,218972,219368,219244],"length":1,"stats":{"Line":9}},{"line":140,"address":[],"length":0,"stats":{"Line":3}},{"line":144,"address":[202768,203016],"length":1,"stats":{"Line":5}},{"line":145,"address":[181996],"length":1,"stats":{"Line":5}},{"line":147,"address":[1590870,1590922,1590598,1590650,1591142,1591194],"length":1,"stats":{"Line":10}},{"line":149,"address":[],"length":0,"stats":{"Line":0}},{"line":152,"address":[],"length":0,"stats":{"Line":8}},{"line":153,"address":[182106],"length":1,"stats":{"Line":4}},{"line":155,"address":[202941],"length":1,"stats":{"Line":8}},{"line":157,"address":[1099105,1098545,1097985],"length":1,"stats":{"Line":4}},{"line":158,"address":[],"length":0,"stats":{"Line":8}},{"line":159,"address":[],"length":0,"stats":{"Line":4}},{"line":160,"address":[],"length":0,"stats":{"Line":0}},{"line":164,"address":[206728],"length":1,"stats":{"Line":4}},{"line":165,"address":[1099050,1098906,1098490,1098346,1099610,1099466],"length":1,"stats":{"Line":6}},{"line":168,"address":[177984],"length":1,"stats":{"Line":1}},{"line":170,"address":[1099439,1098879,1098319],"length":1,"stats":{"Line":1}},{"line":173,"address":[206899],"length":1,"stats":{"Line":1}},{"line":177,"address":[178087],"length":1,"stats":{"Line":1}},{"line":180,"address":[178111],"length":1,"stats":{"Line":1}},{"line":181,"address":[],"length":0,"stats":{"Line":0}},{"line":186,"address":[],"length":0,"stats":{"Line":0}},{"line":188,"address":[182166],"length":1,"stats":{"Line":5}},{"line":189,"address":[],"length":0,"stats":{"Line":1}},{"line":190,"address":[207060],"length":1,"stats":{"Line":1}},{"line":191,"address":[],"length":0,"stats":{"Line":1}},{"line":197,"address":[],"length":0,"stats":{"Line":3}},{"line":198,"address":[1591606,1591398],"length":1,"stats":{"Line":3}},{"line":200,"address":[1591408,1591616,1591454,1591662],"length":1,"stats":{"Line":6}},{"line":203,"address":[],"length":0,"stats":{"Line":0}},{"line":206,"address":[],"length":0,"stats":{"Line":8}},{"line":208,"address":[],"length":0,"stats":{"Line":4}},{"line":209,"address":[],"length":0,"stats":{"Line":8}},{"line":211,"address":[],"length":0,"stats":{"Line":4}},{"line":212,"address":[],"length":0,"stats":{"Line":3}},{"line":215,"address":[1100400,1100736],"length":1,"stats":{"Line":2}},{"line":216,"address":[202725],"length":1,"stats":{"Line":2}},{"line":220,"address":[1100324,1100660],"length":1,"stats":{"Line":1}},{"line":222,"address":[1100942,1100848,1100862,1100928],"length":1,"stats":{"Line":2}},{"line":226,"address":[1591792,1592037],"length":1,"stats":{"Line":1}},{"line":227,"address":[1591810],"length":1,"stats":{"Line":1}},{"line":229,"address":[],"length":0,"stats":{"Line":3}},{"line":230,"address":[],"length":0,"stats":{"Line":1}},{"line":246,"address":[],"length":0,"stats":{"Line":1}},{"line":247,"address":[],"length":0,"stats":{"Line":1}},{"line":250,"address":[151168],"length":1,"stats":{"Line":8}},{"line":251,"address":[],"length":0,"stats":{"Line":8}},{"line":254,"address":[],"length":0,"stats":{"Line":3}},{"line":255,"address":[219777,219809],"length":1,"stats":{"Line":3}},{"line":270,"address":[190560],"length":1,"stats":{"Line":4}},{"line":271,"address":[182257],"length":1,"stats":{"Line":4}},{"line":274,"address":[1592464],"length":1,"stats":{"Line":1}},{"line":275,"address":[1592481],"length":1,"stats":{"Line":1}},{"line":287,"address":[1592496],"length":1,"stats":{"Line":1}},{"line":288,"address":[1592513],"length":1,"stats":{"Line":1}},{"line":295,"address":[],"length":0,"stats":{"Line":2}},{"line":296,"address":[1592532,1592589],"length":1,"stats":{"Line":2}},{"line":307,"address":[1592640],"length":1,"stats":{"Line":1}},{"line":308,"address":[1592654],"length":1,"stats":{"Line":1}},{"line":319,"address":[1592704],"length":1,"stats":{"Line":1}},{"line":320,"address":[1592709],"length":1,"stats":{"Line":1}},{"line":331,"address":[1592720],"length":1,"stats":{"Line":1}},{"line":332,"address":[],"length":0,"stats":{"Line":1}},{"line":339,"address":[1592736],"length":1,"stats":{"Line":1}},{"line":340,"address":[],"length":0,"stats":{"Line":1}},{"line":341,"address":[],"length":0,"stats":{"Line":1}},{"line":346,"address":[1592816],"length":1,"stats":{"Line":1}},{"line":347,"address":[],"length":0,"stats":{"Line":1}},{"line":352,"address":[],"length":0,"stats":{"Line":1}},{"line":353,"address":[1592885],"length":1,"stats":{"Line":1}},{"line":358,"address":[],"length":0,"stats":{"Line":1}},{"line":359,"address":[],"length":0,"stats":{"Line":1}},{"line":364,"address":[1592912],"length":1,"stats":{"Line":1}},{"line":365,"address":[],"length":0,"stats":{"Line":1}},{"line":370,"address":[],"length":0,"stats":{"Line":1}},{"line":371,"address":[],"length":0,"stats":{"Line":1}},{"line":392,"address":[],"length":0,"stats":{"Line":9}},{"line":394,"address":[1593000,1593224,1593192,1593096,1593141,1593121,1593064,1593032,1593160],"length":1,"stats":{"Line":10}},{"line":414,"address":[1593248,1593264],"length":1,"stats":{"Line":3}},{"line":416,"address":[1593253,1593269],"length":1,"stats":{"Line":3}},{"line":442,"address":[190592],"length":1,"stats":{"Line":25}},{"line":463,"address":[1593648],"length":1,"stats":{"Line":1}},{"line":464,"address":[],"length":0,"stats":{"Line":0}},{"line":484,"address":[],"length":0,"stats":{"Line":2}},{"line":485,"address":[],"length":0,"stats":{"Line":0}},{"line":505,"address":[],"length":0,"stats":{"Line":1}},{"line":506,"address":[],"length":0,"stats":{"Line":1}},{"line":530,"address":[151557,151392],"length":1,"stats":{"Line":11}},{"line":532,"address":[203346,203291],"length":1,"stats":{"Line":23}},{"line":535,"address":[1594112,1594080],"length":1,"stats":{"Line":3}},{"line":536,"address":[],"length":0,"stats":{"Line":3}},{"line":539,"address":[219920],"length":1,"stats":{"Line":2}},{"line":544,"address":[1594153],"length":1,"stats":{"Line":2}},{"line":567,"address":[173390,173264],"length":1,"stats":{"Line":9}},{"line":570,"address":[1594878,1594606,1594190,1594752,1594464,1594320],"length":1,"stats":{"Line":9}},{"line":574,"address":[173342],"length":1,"stats":{"Line":8}},{"line":610,"address":[204080,203888],"length":1,"stats":{"Line":2}},{"line":613,"address":[1595124,1595053,1594992,1595035],"length":1,"stats":{"Line":5}},{"line":614,"address":[204022],"length":1,"stats":{"Line":1}},{"line":616,"address":[204000],"length":1,"stats":{"Line":1}},{"line":617,"address":[],"length":0,"stats":{"Line":0}},{"line":620,"address":[],"length":0,"stats":{"Line":1}},{"line":643,"address":[],"length":0,"stats":{"Line":1}},{"line":644,"address":[],"length":0,"stats":{"Line":1}},{"line":645,"address":[],"length":0,"stats":{"Line":0}},{"line":650,"address":[],"length":0,"stats":{"Line":2}},{"line":651,"address":[],"length":0,"stats":{"Line":2}},{"line":654,"address":[1595280],"length":1,"stats":{"Line":1}},{"line":659,"address":[1595289],"length":1,"stats":{"Line":1}},{"line":682,"address":[182430,182304],"length":1,"stats":{"Line":4}},{"line":685,"address":[1595440,1595326],"length":1,"stats":{"Line":4}},{"line":689,"address":[182382],"length":1,"stats":{"Line":3}},{"line":726,"address":[],"length":0,"stats":{"Line":3}},{"line":729,"address":[],"length":0,"stats":{"Line":5}},{"line":730,"address":[190909],"length":1,"stats":{"Line":1}},{"line":733,"address":[190950],"length":1,"stats":{"Line":1}},{"line":735,"address":[],"length":0,"stats":{"Line":1}},{"line":736,"address":[],"length":0,"stats":{"Line":0}},{"line":757,"address":[],"length":0,"stats":{"Line":1}},{"line":758,"address":[1595940],"length":1,"stats":{"Line":1}},{"line":759,"address":[],"length":0,"stats":{"Line":0}},{"line":780,"address":[],"length":0,"stats":{"Line":1}},{"line":781,"address":[],"length":0,"stats":{"Line":1}},{"line":797,"address":[],"length":0,"stats":{"Line":2}},{"line":798,"address":[],"length":0,"stats":{"Line":2}},{"line":825,"address":[],"length":0,"stats":{"Line":1}},{"line":826,"address":[],"length":0,"stats":{"Line":1}},{"line":853,"address":[],"length":0,"stats":{"Line":1}},{"line":854,"address":[],"length":0,"stats":{"Line":1}}],"covered":161,"coverable":178},{"path":["/","home","botahamec","Projects","happylock","src","collection","utils.rs"],"content":"use std::cell::Cell;\n\nuse crate::handle_unwind::handle_unwind;\nuse crate::lockable::{Lockable, RawLock, Sharable};\nuse crate::Keyable;\n\n#[must_use]\npub fn get_locks\u003cL: Lockable\u003e(data: \u0026L) -\u003e Vec\u003c\u0026dyn RawLock\u003e {\n\tlet mut locks = Vec::new();\n\tdata.get_ptrs(\u0026mut locks);\n\tlocks.sort_by_key(|lock| \u0026raw const **lock);\n\tlocks\n}\n\n#[must_use]\npub fn get_locks_unsorted\u003cL: Lockable\u003e(data: \u0026L) -\u003e Vec\u003c\u0026dyn RawLock\u003e {\n\tlet mut locks = Vec::new();\n\tdata.get_ptrs(\u0026mut locks);\n\tlocks\n}\n\n/// returns `true` if the sorted list contains a duplicate\n#[must_use]\npub fn ordered_contains_duplicates(l: \u0026[\u0026dyn RawLock]) -\u003e bool {\n\tif l.is_empty() {\n\t\t// Return early to prevent panic in the below call to `windows`\n\t\treturn false;\n\t}\n\n\tl.windows(2)\n\t\t// NOTE: addr_eq is necessary because eq would also compare the v-table pointers\n\t\t.any(|window| std::ptr::addr_eq(window[0], window[1]))\n}\n\n/// Lock a set of locks in the given order. It's UB to call this without a `ThreadKey`\npub unsafe fn ordered_write(locks: \u0026[\u0026dyn RawLock]) {\n\t// these will be unlocked in case of a panic\n\tlet locked = Cell::new(0);\n\n\thandle_unwind(\n\t\t|| {\n\t\t\tfor lock in locks {\n\t\t\t\tlock.raw_write();\n\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t}\n\t\t},\n\t\t|| attempt_to_recover_writes_from_panic(\u0026locks[0..locked.get()]),\n\t)\n}\n\n/// Lock a set of locks in the given order. It's UB to call this without a `ThreadKey`\npub unsafe fn ordered_read(locks: \u0026[\u0026dyn RawLock]) {\n\tlet locked = Cell::new(0);\n\n\thandle_unwind(\n\t\t|| {\n\t\t\tfor lock in locks {\n\t\t\t\tlock.raw_read();\n\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t}\n\t\t},\n\t\t|| attempt_to_recover_reads_from_panic(\u0026locks[0..locked.get()]),\n\t)\n}\n\n/// Locks the locks in the order they are given. This causes deadlock if the\n/// locks contain duplicates, or if this is called by multiple threads with the\n/// locks in different orders.\npub unsafe fn ordered_try_write(locks: \u0026[\u0026dyn RawLock]) -\u003e bool {\n\tlet locked = Cell::new(0);\n\n\thandle_unwind(\n\t\t|| unsafe {\n\t\t\tfor (i, lock) in locks.iter().enumerate() {\n\t\t\t\t// safety: we have the thread key\n\t\t\t\tif lock.raw_try_write() {\n\t\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t\t} else {\n\t\t\t\t\tfor lock in \u0026locks[0..i] {\n\t\t\t\t\t\t// safety: this lock was already acquired\n\t\t\t\t\t\tlock.raw_unlock_write();\n\t\t\t\t\t}\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttrue\n\t\t},\n\t\t||\n\t\t// safety: everything in locked is locked\n\t\tattempt_to_recover_writes_from_panic(\u0026locks[0..locked.get()]),\n\t)\n}\n\n/// Locks the locks in the order they are given. This causes deadlock if this\n/// is called by multiple threads with the locks in different orders.\npub unsafe fn ordered_try_read(locks: \u0026[\u0026dyn RawLock]) -\u003e bool {\n\t// these will be unlocked in case of a panic\n\tlet locked = Cell::new(0);\n\n\thandle_unwind(\n\t\t|| unsafe {\n\t\t\tfor (i, lock) in locks.iter().enumerate() {\n\t\t\t\t// safety: we have the thread key\n\t\t\t\tif lock.raw_try_read() {\n\t\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t\t} else {\n\t\t\t\t\tfor lock in \u0026locks[0..i] {\n\t\t\t\t\t\t// safety: this lock was already acquired\n\t\t\t\t\t\tlock.raw_unlock_read();\n\t\t\t\t\t}\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttrue\n\t\t},\n\t\t||\n\t\t// safety: everything in locked is locked\n\t\tattempt_to_recover_reads_from_panic(\u0026locks[0..locked.get()]),\n\t)\n}\n\npub fn scoped_write\u003c'a, L: RawLock + Lockable, R\u003e(\n\tcollection: \u0026'a L,\n\tkey: impl Keyable,\n\tf: impl FnOnce(L::DataMut\u003c'a\u003e) -\u003e R,\n) -\u003e R {\n\tunsafe {\n\t\t// safety: we have the key\n\t\tcollection.raw_write();\n\n\t\t// safety: we just locked this\n\t\tlet r = f(collection.data_mut());\n\n\t\t// this ensures the key is held long enough\n\t\tdrop(key);\n\n\t\t// safety: we've locked already, and aren't using the data again\n\t\tcollection.raw_unlock_write();\n\n\t\tr\n\t}\n}\n\npub fn scoped_try_write\u003c'a, L: RawLock + Lockable, Key: Keyable, R\u003e(\n\tcollection: \u0026'a L,\n\tkey: Key,\n\tf: impl FnOnce(L::DataMut\u003c'a\u003e) -\u003e R,\n) -\u003e Result\u003cR, Key\u003e {\n\tunsafe {\n\t\t// safety: we have the key\n\t\tif !collection.raw_try_write() {\n\t\t\treturn Err(key);\n\t\t}\n\n\t\t// safety: we just locked this\n\t\tlet r = f(collection.data_mut());\n\n\t\t// this ensures the key is held long enough\n\t\tdrop(key);\n\n\t\t// safety: we've locked already, and aren't using the data again\n\t\tcollection.raw_unlock_write();\n\n\t\tOk(r)\n\t}\n}\n\npub fn scoped_read\u003c'a, L: RawLock + Sharable, R\u003e(\n\tcollection: \u0026'a L,\n\tkey: impl Keyable,\n\tf: impl FnOnce(L::DataRef\u003c'a\u003e) -\u003e R,\n) -\u003e R {\n\tunsafe {\n\t\t// safety: we have the key\n\t\tcollection.raw_read();\n\n\t\t// safety: we just locked this\n\t\tlet r = f(collection.data_ref());\n\n\t\t// this ensures the key is held long enough\n\t\tdrop(key);\n\n\t\t// safety: we've locked already, and aren't using the data again\n\t\tcollection.raw_unlock_read();\n\n\t\tr\n\t}\n}\n\npub fn scoped_try_read\u003c'a, L: RawLock + Sharable, Key: Keyable, R\u003e(\n\tcollection: \u0026'a L,\n\tkey: Key,\n\tf: impl FnOnce(L::DataRef\u003c'a\u003e) -\u003e R,\n) -\u003e Result\u003cR, Key\u003e {\n\tunsafe {\n\t\t// safety: we have the key\n\t\tif !collection.raw_try_read() {\n\t\t\treturn Err(key);\n\t\t}\n\n\t\t// safety: we just locked this\n\t\tlet r = f(collection.data_ref());\n\n\t\t// this ensures the key is held long enough\n\t\tdrop(key);\n\n\t\t// safety: we've locked already, and aren't using the data again\n\t\tcollection.raw_unlock_read();\n\n\t\tOk(r)\n\t}\n}\n\n/// Unlocks the already locked locks in order to recover from a panic\npub unsafe fn attempt_to_recover_writes_from_panic(locks: \u0026[\u0026dyn RawLock]) {\n\thandle_unwind(\n\t\t|| {\n\t\t\t// safety: the caller assumes that these are already locked\n\t\t\tlocks.iter().for_each(|lock| lock.raw_unlock_write());\n\t\t},\n\t\t// if we get another panic in here, we'll just have to poison what remains\n\t\t|| locks.iter().for_each(|l| l.poison()),\n\t)\n}\n\n/// Unlocks the already locked locks in order to recover from a panic\npub unsafe fn attempt_to_recover_reads_from_panic(locked: \u0026[\u0026dyn RawLock]) {\n\thandle_unwind(\n\t\t|| {\n\t\t\t// safety: the caller assumes these are already locked\n\t\t\tlocked.iter().for_each(|lock| lock.raw_unlock_read());\n\t\t},\n\t\t// if we get another panic in here, we'll just have to poison what remains\n\t\t|| locked.iter().for_each(|l| l.poison()),\n\t)\n}\n\n#[cfg(test)]\nmod tests {\n\tuse crate::collection::utils::ordered_contains_duplicates;\n\n\t#[test]\n\tfn empty_array_does_not_contain_duplicates() {\n\t\tassert!(!ordered_contains_duplicates(\u0026[]))\n\t}\n}\n","traces":[{"line":8,"address":[],"length":0,"stats":{"Line":9}},{"line":9,"address":[],"length":0,"stats":{"Line":9}},{"line":10,"address":[426081,425505,426273,425313,424929,425121,426465,425697,425889],"length":1,"stats":{"Line":9}},{"line":11,"address":[],"length":0,"stats":{"Line":27}},{"line":12,"address":[],"length":0,"stats":{"Line":9}},{"line":16,"address":[159760,159885],"length":1,"stats":{"Line":26}},{"line":17,"address":[],"length":0,"stats":{"Line":26}},{"line":18,"address":[],"length":0,"stats":{"Line":26}},{"line":19,"address":[],"length":0,"stats":{"Line":26}},{"line":24,"address":[538336],"length":1,"stats":{"Line":8}},{"line":25,"address":[453224],"length":1,"stats":{"Line":8}},{"line":27,"address":[532806],"length":1,"stats":{"Line":1}},{"line":30,"address":[535115],"length":1,"stats":{"Line":8}},{"line":32,"address":[526816,526845,526963],"length":1,"stats":{"Line":24}},{"line":36,"address":[532400],"length":1,"stats":{"Line":4}},{"line":38,"address":[517230],"length":1,"stats":{"Line":4}},{"line":41,"address":[518608],"length":1,"stats":{"Line":4}},{"line":42,"address":[429550,429623],"length":1,"stats":{"Line":8}},{"line":43,"address":[527107],"length":1,"stats":{"Line":4}},{"line":44,"address":[557021],"length":1,"stats":{"Line":4}},{"line":47,"address":[555277],"length":1,"stats":{"Line":13}},{"line":52,"address":[535280],"length":1,"stats":{"Line":2}},{"line":53,"address":[547230],"length":1,"stats":{"Line":2}},{"line":56,"address":[429920],"length":1,"stats":{"Line":2}},{"line":57,"address":[429934,430007],"length":1,"stats":{"Line":4}},{"line":58,"address":[430017],"length":1,"stats":{"Line":2}},{"line":59,"address":[543133],"length":1,"stats":{"Line":2}},{"line":62,"address":[543216,543397,543223],"length":1,"stats":{"Line":5}},{"line":69,"address":[547312],"length":1,"stats":{"Line":2}},{"line":70,"address":[509031],"length":1,"stats":{"Line":2}},{"line":73,"address":[532639],"length":1,"stats":{"Line":6}},{"line":74,"address":[543581,543439],"length":1,"stats":{"Line":6}},{"line":76,"address":[557906],"length":1,"stats":{"Line":3}},{"line":77,"address":[549632,549498],"length":1,"stats":{"Line":4}},{"line":79,"address":[543485,543230,543421,543546],"length":1,"stats":{"Line":4}},{"line":81,"address":[566410],"length":1,"stats":{"Line":1}},{"line":83,"address":[543968],"length":1,"stats":{"Line":1}},{"line":87,"address":[543142],"length":1,"stats":{"Line":1}},{"line":89,"address":[555507],"length":1,"stats":{"Line":4}},{"line":91,"address":[528439,528613],"length":1,"stats":{"Line":2}},{"line":97,"address":[517520],"length":1,"stats":{"Line":2}},{"line":99,"address":[532727],"length":1,"stats":{"Line":2}},{"line":102,"address":[536847],"length":1,"stats":{"Line":6}},{"line":103,"address":[550029,549887],"length":1,"stats":{"Line":8}},{"line":105,"address":[520434],"length":1,"stats":{"Line":4}},{"line":106,"address":[548378,548512],"length":1,"stats":{"Line":6}},{"line":108,"address":[559037,559098,558973,558782],"length":1,"stats":{"Line":8}},{"line":110,"address":[550442],"length":1,"stats":{"Line":1}},{"line":112,"address":[550416],"length":1,"stats":{"Line":2}},{"line":116,"address":[548070],"length":1,"stats":{"Line":1}},{"line":118,"address":[538819],"length":1,"stats":{"Line":3}},{"line":120,"address":[545061,544887],"length":1,"stats":{"Line":2}},{"line":124,"address":[140016,140244],"length":1,"stats":{"Line":27}},{"line":131,"address":[183047],"length":1,"stats":{"Line":27}},{"line":134,"address":[183258,183115],"length":1,"stats":{"Line":27}},{"line":137,"address":[],"length":0,"stats":{"Line":27}},{"line":140,"address":[140216],"length":1,"stats":{"Line":27}},{"line":142,"address":[],"length":0,"stats":{"Line":0}},{"line":146,"address":[438920,438630,438942,439816,439519,440134,439246,439224,440160,438960,439838,440424,440446,438352,439856,439552,438656,439264,439541],"length":1,"stats":{"Line":38}},{"line":153,"address":[],"length":0,"stats":{"Line":76}},{"line":154,"address":[215483,213963,214571,214875,214267,215179],"length":1,"stats":{"Line":20}},{"line":158,"address":[188795,187883,187686,187852,188294,188460,187548,188764,188902,189068,188187,187579,187990,188491,188156,189206,189099,188598],"length":1,"stats":{"Line":18}},{"line":161,"address":[224542,225150,224238,224846,225454,225758],"length":1,"stats":{"Line":18}},{"line":164,"address":[179700,180612,180004,179396,180308,180916],"length":1,"stats":{"Line":18}},{"line":166,"address":[224891,224283,225803,224587,225195,225499],"length":1,"stats":{"Line":18}},{"line":170,"address":[442544,440464,443059,442272,441232,441989,441491,441219,440723,442032,442800,442260,440964,442011,440976,441776,440736,442531,442787,441504,441763],"length":1,"stats":{"Line":10}},{"line":177,"address":[442824,441256,440488,442568,440760,441000,441790,442296,441528,442056],"length":1,"stats":{"Line":10}},{"line":180,"address":[442893,441859,442125,442365,440829,442244,441475,441203,441069,442637,440948,441747,441325,443043,440557,441973,442515,441597,442771,440707],"length":1,"stats":{"Line":10}},{"line":183,"address":[441926,442197,442461,442989,441153,442721,441421,441693,440653,440901],"length":1,"stats":{"Line":10}},{"line":186,"address":[],"length":0,"stats":{"Line":10}},{"line":188,"address":[],"length":0,"stats":{"Line":0}},{"line":192,"address":[228598,228016,229536,229232,229814,228294,229206,229510,228320,228624,228928,228902],"length":1,"stats":{"Line":12}},{"line":199,"address":[],"length":0,"stats":{"Line":24}},{"line":200,"address":[229035,229339,228123,228427,229643,228731],"length":1,"stats":{"Line":7}},{"line":204,"address":[228582,229798,228779,229387,229190,229083,228475,229494,229660,229356,229052,228278,228444,228171,228748,228140,228886,229691],"length":1,"stats":{"Line":5}},{"line":207,"address":[],"length":0,"stats":{"Line":5}},{"line":210,"address":[228868,229172,229780,229476,228564,228260],"length":1,"stats":{"Line":5}},{"line":212,"address":[228875,228571,228267,229787,229179,229483],"length":1,"stats":{"Line":5}},{"line":217,"address":[538864],"length":1,"stats":{"Line":6}},{"line":219,"address":[548752],"length":1,"stats":{"Line":9}},{"line":221,"address":[548766,548814,548800],"length":1,"stats":{"Line":20}},{"line":224,"address":[544798,544750,544736,544784],"length":1,"stats":{"Line":4}},{"line":229,"address":[517680],"length":1,"stats":{"Line":4}},{"line":231,"address":[548912],"length":1,"stats":{"Line":5}},{"line":233,"address":[545296,545310,545262],"length":1,"stats":{"Line":15}},{"line":236,"address":[549054,548992,549040,549006],"length":1,"stats":{"Line":4}}],"covered":84,"coverable":86},{"path":["/","home","botahamec","Projects","happylock","src","collection.rs"],"content":"use std::cell::UnsafeCell;\n\nuse crate::{lockable::RawLock, ThreadKey};\n\nmod boxed;\nmod guard;\nmod owned;\nmod r#ref;\nmod retry;\npub(crate) mod utils;\n\n/// Locks a collection of locks, which cannot be shared immutably.\n///\n/// This could be a tuple of [`Lockable`] types, an array, or a `Vec`. But it\n/// can be safely locked without causing a deadlock.\n///\n/// The data in this collection is guaranteed to not contain duplicates because\n/// `L` must always implement [`OwnedLockable`]. The underlying data may not be\n/// immutably referenced and locked. Because of this, there is no need for\n/// sorting the locks in the collection, or checking for duplicates, because it\n/// can be guaranteed that until the underlying collection is mutated (which\n/// requires releasing all acquired locks in the collection to do), then the\n/// locks will stay in the same order and be locked in that order, preventing\n/// cyclic wait.\n///\n/// [`Lockable`]: `crate::lockable::Lockable`\n/// [`OwnedLockable`]: `crate::lockable::OwnedLockable`\n\n// this type caches the idea that no immutable references to the underlying\n// collection exist\n#[derive(Debug)]\npub struct OwnedLockCollection\u003cL\u003e {\n\tdata: L,\n}\n\n/// Locks a reference to a collection of locks, by sorting them by memory\n/// address.\n///\n/// This could be a tuple of [`Lockable`] types, an array, or a `Vec`. But it\n/// can be safely locked without causing a deadlock.\n///\n/// Upon construction, it must be confirmed that the collection contains no\n/// duplicate locks. This can be done by either using [`OwnedLockable`] or by\n/// checking. Regardless of how this is done, the locks will be sorted by their\n/// memory address before locking them. The sorted order of the locks is stored\n/// within this collection.\n///\n/// Unlike [`BoxedLockCollection`], this type does not allocate memory for the\n/// data, although it does allocate memory for the sorted list of lock\n/// references. This makes it slightly faster, but lifetimes must be handled.\n///\n/// [`Lockable`]: `crate::lockable::Lockable`\n/// [`OwnedLockable`]: `crate::lockable::OwnedLockable`\n//\n// This type was born when I eventually realized that I needed a self\n// referential structure. That used boxing, so I elected to make a more\n// efficient implementation (polonius please save us)\n//\n// This type caches the sorting order of the locks and the fact that it doesn't\n// contain any duplicates.\npub struct RefLockCollection\u003c'a, L\u003e {\n\tdata: \u0026'a L,\n\tlocks: Vec\u003c\u0026'a dyn RawLock\u003e,\n}\n\n/// Locks a collection of locks, stored in the heap, by sorting them by memory\n/// address.\n///\n/// This could be a tuple of [`Lockable`] types, an array, or a `Vec`. But it\n/// can be safely locked without causing a deadlock.\n///\n/// Upon construction, it must be confirmed that the collection contains no\n/// duplicate locks. This can be done by either using [`OwnedLockable`] or by\n/// checking. Regardless of how this is done, the locks will be sorted by their\n/// memory address before locking them. The sorted order of the locks is stored\n/// within this collection.\n///\n/// Unlike [`RefLockCollection`], this is a self-referential type which boxes\n/// the data that is given to it. This means no lifetimes are necessary on the\n/// type itself, but it is slightly slower because of the memory allocation.\n///\n/// [`Lockable`]: `crate::lockable::Lockable`\n/// [`OwnedLockable`]: `crate::lockable::OwnedLockable`\n//\n// This type caches the sorting order of the locks and the fact that it doesn't\n// contain any duplicates.\npub struct BoxedLockCollection\u003cL\u003e {\n\tdata: *const UnsafeCell\u003cL\u003e,\n\tlocks: Vec\u003c\u0026'static dyn RawLock\u003e,\n}\n\n/// Locks a collection of locks using a retrying algorithm.\n///\n/// This could be a tuple of [`Lockable`] types, an array, or a `Vec`. But it\n/// can be safely locked without causing a deadlock.\n///\n/// The data in this collection is guaranteed to not contain duplicates, but it\n/// also not be sorted. In some cases the lack of sorting can increase\n/// performance. However, in most cases, this collection will be slower. Cyclic\n/// wait is not guaranteed here, so the locking algorithm must release all its\n/// locks if one of the lock attempts blocks. This results in wasted time and\n/// potential [livelocking].\n///\n/// However, one case where this might be faster than [`RefLockCollection`] is\n/// when the first lock in the collection is always the first in any\n/// collection, and the other locks in the collection are always locked after\n/// that first lock is acquired. This means that as soon as it is locked, there\n/// will be no need to unlock it later on subsequent lock attempts, because\n/// they will always succeed.\n///\n/// [`Lockable`]: `crate::lockable::Lockable`\n/// [`OwnedLockable`]: `crate::lockable::OwnedLockable`\n/// [livelocking]: https://en.wikipedia.org/wiki/Deadlock#Livelock\n//\n// This type caches the fact that there are no duplicates\n#[derive(Debug)]\npub struct RetryingLockCollection\u003cL\u003e {\n\tdata: L,\n}\n\n/// A RAII guard for a generic [`Lockable`] type.\n///\n/// [`Lockable`]: `crate::lockable::Lockable`\npub struct LockGuard\u003cGuard\u003e {\n\tguard: Guard,\n\tkey: ThreadKey,\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","src","handle_unwind.rs"],"content":"use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};\n\n/// Runs `try_fn`. If it unwinds, it will run `catch` and then continue\n/// unwinding. This is used instead of `scopeguard` to ensure the `catch`\n/// function doesn't run if the thread is already panicking. The unwind\n/// must specifically be caused by the `try_fn`\npub fn handle_unwind\u003cR, F: FnOnce() -\u003e R, G: FnOnce()\u003e(try_fn: F, catch: G) -\u003e R {\n\tlet try_fn = AssertUnwindSafe(try_fn);\n\tcatch_unwind(try_fn).unwrap_or_else(|e| {\n\t\tcatch();\n\t\tresume_unwind(e)\n\t})\n}\n","traces":[{"line":7,"address":[509474,510326,510176,510000,509664,509840,510152,509488,509638,509986,509328,509816],"length":1,"stats":{"Line":157}},{"line":8,"address":[207922,208594,208201,208338,207753,208466,208057],"length":1,"stats":{"Line":126}},{"line":9,"address":[208353,208077,208936,209449,208832,209091,209104,208481,209347,208819,207993,208258,208976,208221,209219,208665,208960,208409,208537,208793,209488,209577,209475,207842,208114,209360,209232,208609,207937,209193,209065,208704,207785,209603,209321],"length":1,"stats":{"Line":312}},{"line":10,"address":[540567,540285,540439,540685,539997,540141],"length":1,"stats":{"Line":29}},{"line":11,"address":[549421,549281,549021,548877,548733,549153],"length":1,"stats":{"Line":25}}],"covered":5,"coverable":5},{"path":["/","home","botahamec","Projects","happylock","src","key.rs"],"content":"use std::cell::{Cell, LazyCell};\nuse std::fmt::{self, Debug};\nuse std::marker::PhantomData;\n\nuse sealed::Sealed;\n\n// Sealed to prevent other key types from being implemented. Otherwise, this\n// would almost instant undefined behavior.\nmod sealed {\n\tuse super::ThreadKey;\n\n\tpub trait Sealed {}\n\timpl Sealed for ThreadKey {}\n\timpl Sealed for \u0026mut ThreadKey {}\n}\n\nthread_local! {\n\tstatic KEY: LazyCell\u003cKeyCell\u003e = LazyCell::new(KeyCell::default);\n}\n\n/// The key for the current thread.\n///\n/// Only one of these exist per thread. To get the current thread's key, call\n/// [`ThreadKey::get`]. If the `ThreadKey` is dropped, it can be re-obtained.\npub struct ThreadKey {\n\tphantom: PhantomData\u003c*const ()\u003e, // implement !Send and !Sync\n}\n\n/// Allows the type to be used as a key for a lock\n///\n/// # Safety\n///\n/// Only one value which implements this trait may be allowed to exist at a\n/// time. Creating a new `Keyable` value requires making any other `Keyable`\n/// values invalid.\npub unsafe trait Keyable: Sealed {}\nunsafe impl Keyable for ThreadKey {}\n// the ThreadKey can't be moved while a mutable reference to it exists\nunsafe impl Keyable for \u0026mut ThreadKey {}\n\n// Implementing this means we can allow `MutexGuard` to be Sync\n// Safety: a \u0026ThreadKey is useless by design.\nunsafe impl Sync for ThreadKey {}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl Debug for ThreadKey {\n\tfn fmt(\u0026self, f: \u0026mut fmt::Formatter\u003c'_\u003e) -\u003e fmt::Result {\n\t\twrite!(f, \"ThreadKey\")\n\t}\n}\n\n// If you lose the thread key, you can get it back by calling ThreadKey::get\nimpl Drop for ThreadKey {\n\tfn drop(\u0026mut self) {\n\t\t// safety: a thread key cannot be acquired without creating the lock\n\t\t// safety: the key is lost, so it's safe to unlock the cell\n\t\tunsafe { KEY.with(|key| key.force_unlock()) }\n\t}\n}\n\nimpl ThreadKey {\n\t/// Get the current thread's `ThreadKey`, if it's not already taken.\n\t///\n\t/// The first time this is called, it will successfully return a\n\t/// `ThreadKey`. However, future calls to this function on the same thread\n\t/// will return [`None`], unless the key is dropped or unlocked first.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::ThreadKey;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// ```\n\t#[must_use]\n\tpub fn get() -\u003e Option\u003cSelf\u003e {\n\t\t// safety: we just acquired the lock\n\t\t// safety: if this code changes, check to ensure the requirement for\n\t\t// the Drop implementation is still true\n\t\tKEY.with(|key| {\n\t\t\tkey.try_lock().then_some(Self {\n\t\t\t\tphantom: PhantomData,\n\t\t\t})\n\t\t})\n\t}\n}\n\n/// A dumb lock that's just a wrapper for an [`AtomicBool`].\n#[derive(Default)]\nstruct KeyCell {\n\tis_locked: Cell\u003cbool\u003e,\n}\n\nimpl KeyCell {\n\t/// Attempt to lock the `KeyCell`. This is not a fair lock.\n\t#[must_use]\n\tpub fn try_lock(\u0026self) -\u003e bool {\n\t\t!self.is_locked.replace(true)\n\t}\n\n\t/// Forcibly unlocks the `KeyCell`. This should only be called if the key\n\t/// from this `KeyCell` has been \"lost\".\n\tpub unsafe fn force_unlock(\u0026self) {\n\t\tself.is_locked.set(false);\n\t}\n}\n\n#[cfg(test)]\nmod tests {\n\tuse super::*;\n\n\t#[test]\n\tfn thread_key_returns_some_on_first_call() {\n\t\tassert!(ThreadKey::get().is_some());\n\t}\n\n\t#[test]\n\tfn thread_key_returns_none_on_second_call() {\n\t\tlet key = ThreadKey::get();\n\t\tassert!(ThreadKey::get().is_none());\n\t\tdrop(key);\n\t}\n\n\t#[test]\n\tfn dropping_thread_key_allows_reobtaining() {\n\t\tdrop(ThreadKey::get());\n\t\tassert!(ThreadKey::get().is_some())\n\t}\n}\n","traces":[{"line":18,"address":[536408],"length":1,"stats":{"Line":21}},{"line":55,"address":[536912],"length":1,"stats":{"Line":13}},{"line":58,"address":[515685],"length":1,"stats":{"Line":39}},{"line":77,"address":[536752],"length":1,"stats":{"Line":20}},{"line":81,"address":[542880],"length":1,"stats":{"Line":40}},{"line":82,"address":[537273],"length":1,"stats":{"Line":20}},{"line":98,"address":[536352],"length":1,"stats":{"Line":19}},{"line":99,"address":[551077],"length":1,"stats":{"Line":20}},{"line":104,"address":[536384],"length":1,"stats":{"Line":12}},{"line":105,"address":[439893],"length":1,"stats":{"Line":13}}],"covered":10,"coverable":10},{"path":["/","home","botahamec","Projects","happylock","src","lib.rs"],"content":"#![warn(clippy::pedantic)]\n#![warn(clippy::nursery)]\n#![allow(clippy::module_name_repetitions)]\n#![allow(clippy::declare_interior_mutable_const)]\n#![allow(clippy::semicolon_if_nothing_returned)]\n#![allow(clippy::module_inception)]\n#![allow(clippy::single_match_else)]\n\n//! As it turns out, the Rust borrow checker is powerful enough that, if the\n//! standard library supported it, we could've made deadlocks undefined\n//! behavior. This library currently serves as a proof of concept for how that\n//! would work.\n//!\n//! # Theory\n//!\n//! There are four conditions necessary for a deadlock to occur. In order to\n//! prevent deadlocks, we just need to prevent one of the following:\n//!\n//! 1. mutual exclusion\n//! 2. non-preemptive allocation\n//! 3. circular wait\n//! 4. **partial allocation**\n//!\n//! This library seeks to solve **partial allocation** by requiring total\n//! allocation. All the resources a thread needs must be allocated at the same\n//! time. In order to request new resources, the old resources must be dropped\n//! first. Requesting multiple resources at once is atomic. You either get all\n//! the requested resources or none at all.\n//!\n//! As an optimization, this library also often prevents **circular wait**.\n//! Many collections sort the locks in order of their memory address. As long\n//! as the locks are always acquired in that order, then time doesn't need to\n//! be wasted on releasing locks after a failure and re-acquiring them later.\n//!\n//! # Examples\n//!\n//! Simple example:\n//! ```\n//! use std::thread;\n//! use happylock::{Mutex, ThreadKey};\n//!\n//! const N: usize = 10;\n//!\n//! static DATA: Mutex\u003ci32\u003e = Mutex::new(0);\n//!\n//! for _ in 0..N {\n//! thread::spawn(move || {\n//! // each thread gets one thread key\n//! let key = ThreadKey::get().unwrap();\n//!\n//! // unlocking a mutex requires a ThreadKey\n//! let mut data = DATA.lock(key);\n//! *data += 1;\n//!\n//! // the key is unlocked at the end of the scope\n//! });\n//! }\n//!\n//! let key = ThreadKey::get().unwrap();\n//! let data = DATA.lock(key);\n//! println!(\"{}\", *data);\n//! ```\n//!\n//! To lock multiple mutexes at a time, create a [`LockCollection`]:\n//!\n//! ```\n//! use std::thread;\n//! use happylock::{LockCollection, Mutex, ThreadKey};\n//!\n//! const N: usize = 10;\n//!\n//! static DATA_1: Mutex\u003ci32\u003e = Mutex::new(0);\n//! static DATA_2: Mutex\u003cString\u003e = Mutex::new(String::new());\n//!\n//! for _ in 0..N {\n//! thread::spawn(move || {\n//! let key = ThreadKey::get().unwrap();\n//!\n//! // happylock ensures at runtime there are no duplicate locks\n//! let collection = LockCollection::try_new((\u0026DATA_1, \u0026DATA_2)).unwrap();\n//! let mut guard = collection.lock(key);\n//!\n//! *guard.1 = (100 - *guard.0).to_string();\n//! *guard.0 += 1;\n//! });\n//! }\n//!\n//! let key = ThreadKey::get().unwrap();\n//! let data = LockCollection::try_new((\u0026DATA_1, \u0026DATA_2)).unwrap();\n//! let data = data.lock(key);\n//! println!(\"{}\", *data.0);\n//! println!(\"{}\", *data.1);\n//! ```\n//!\n//! In many cases, the [`LockCollection::new`] or [`LockCollection::new_ref`]\n//! method can be used, improving performance.\n//!\n//! ```rust\n//! use std::thread;\n//! use happylock::{LockCollection, Mutex, ThreadKey};\n//!\n//! const N: usize = 32;\n//!\n//! static DATA: [Mutex\u003ci32\u003e; 2] = [Mutex::new(0), Mutex::new(1)];\n//!\n//! for _ in 0..N {\n//! thread::spawn(move || {\n//! let key = ThreadKey::get().unwrap();\n//!\n//! // a reference to a type that implements `OwnedLockable` will never\n//! // contain duplicates, so no duplicate checking is needed.\n//! let collection = LockCollection::new_ref(\u0026DATA);\n//! let mut guard = collection.lock(key);\n//!\n//! let x = *guard[1];\n//! *guard[1] += *guard[0];\n//! *guard[0] = x;\n//! });\n//! }\n//!\n//! let key = ThreadKey::get().unwrap();\n//! let data = LockCollection::new_ref(\u0026DATA);\n//! let data = data.lock(key);\n//! println!(\"{}\", data[0]);\n//! println!(\"{}\", data[1]);\n//! ```\n//!\n//! # Performance\n//!\n//! **The `ThreadKey` is a mostly-zero cost abstraction.** It doesn't use any\n//! memory, and it doesn't really exist at run-time. The only cost comes from\n//! calling `ThreadKey::get()`, because the function has to ensure at runtime\n//! that the key hasn't already been taken. Dropping the key will also have a\n//! small cost.\n//!\n//! **Consider [`OwnedLockCollection`].** This will almost always be the\n//! fastest lock collection. It doesn't expose the underlying collection\n//! immutably, which means that it will always be locked in the same order, and\n//! doesn't need any sorting.\n//!\n//! **Avoid [`LockCollection::try_new`].** This constructor will check to make\n//! sure that the collection contains no duplicate locks. In most cases, this\n//! is O(nlogn), where n is the number of locks in the collections but in the\n//! case of [`RetryingLockCollection`], it's close to O(n).\n//! [`LockCollection::new`] and [`LockCollection::new_ref`] don't need these\n//! checks because they use [`OwnedLockable`], which is guaranteed to be unique\n//! as long as it is accessible. As a last resort,\n//! [`LockCollection::new_unchecked`] doesn't do this check, but is unsafe to\n//! call.\n//!\n//! **Know how to use [`RetryingLockCollection`].** This collection doesn't do\n//! any sorting, but uses a wasteful lock algorithm. It can't rely on the order\n//! of the locks to be the same across threads, so if it finds a lock that it\n//! can't acquire without blocking, it'll first release all of the locks it\n//! already acquired to avoid blocking other threads. This is wasteful because\n//! this algorithm may end up re-acquiring the same lock multiple times. To\n//! avoid this, ensure that (1) the first lock in the collection is always the\n//! first lock in any collection it appears in, and (2) the other locks in the\n//! collection are always preceded by that first lock. This will prevent any\n//! wasted time from re-acquiring locks. If you're unsure, [`LockCollection`]\n//! is a sensible default.\n//!\n//! [`OwnedLockable`]: `lockable::OwnedLockable`\n//! [`OwnedLockCollection`]: `collection::OwnedLockCollection`\n//! [`RetryingLockCollection`]: `collection::RetryingLockCollection`\n\nmod handle_unwind;\nmod key;\n\npub mod collection;\npub mod lockable;\npub mod mutex;\npub mod poisonable;\npub mod rwlock;\n\npub use key::{Keyable, ThreadKey};\n\n#[cfg(feature = \"spin\")]\npub use mutex::SpinLock;\n\n// Personally, I think re-exports look ugly in the rust documentation, so I\n// went with type aliases instead.\n\n/// A collection of locks that can be acquired simultaneously.\n///\n/// This re-exports [`BoxedLockCollection`] as a sensible default.\n///\n/// [`BoxedLockCollection`]: collection::BoxedLockCollection\npub type LockCollection\u003cL\u003e = collection::BoxedLockCollection\u003cL\u003e;\n\n/// A re-export for [`poisonable::Poisonable`]\npub type Poisonable\u003cL\u003e = poisonable::Poisonable\u003cL\u003e;\n\n/// A mutual exclusion primitive useful for protecting shared data, which cannot deadlock.\n///\n/// By default, this uses `parking_lot` as a backend.\n#[cfg(feature = \"parking_lot\")]\npub type Mutex\u003cT\u003e = mutex::Mutex\u003cT, parking_lot::RawMutex\u003e;\n\n/// A reader-writer lock\n///\n/// By default, this uses `parking_lot` as a backend.\n#[cfg(feature = \"parking_lot\")]\npub type RwLock\u003cT\u003e = rwlock::RwLock\u003cT, parking_lot::RawRwLock\u003e;\n","traces":[{"line":197,"address":[423062],"length":1,"stats":{"Line":10}}],"covered":1,"coverable":1},{"path":["/","home","botahamec","Projects","happylock","src","lockable.rs"],"content":"use std::mem::MaybeUninit;\n\n/// A raw lock type that may be locked and unlocked\n///\n/// # Safety\n///\n/// A deadlock must never occur. The `unlock` method must correctly unlock the\n/// data. The `get_ptrs` method must be implemented correctly. The `Output`\n/// must be unlocked when it is dropped.\n//\n// Why not use a RawRwLock? Because that would be semantically incorrect, and I\n// don't want an INIT or GuardMarker associated item.\n// Originally, RawLock had a sister trait: RawSharableLock. I removed it\n// because it'd be difficult to implement a separate type that takes a\n// different kind of RawLock. But now the Sharable marker trait is needed to\n// indicate if reads can be used.\npub unsafe trait RawLock {\n\t/// Causes all subsequent calls to the `lock` function on this lock to\n\t/// panic. This does not affect anything currently holding the lock.\n\tfn poison(\u0026self);\n\n\t/// Blocks until the lock is acquired\n\t///\n\t/// # Safety\n\t///\n\t/// It is undefined behavior to use this without ownership or mutable\n\t/// access to the [`ThreadKey`], which should last as long as the return\n\t/// value is alive.\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\tunsafe fn raw_write(\u0026self);\n\n\t/// Attempt to lock without blocking.\n\t///\n\t/// Returns `true` if successful, `false` otherwise.\n\t///\n\t/// # Safety\n\t///\n\t/// It is undefined behavior to use this without ownership or mutable\n\t/// access to the [`ThreadKey`], which should last as long as the return\n\t/// value is alive.\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool;\n\n\t/// Releases the lock\n\t///\n\t/// # Safety\n\t///\n\t/// It is undefined behavior to use this if the lock is not acquired\n\tunsafe fn raw_unlock_write(\u0026self);\n\n\t/// Blocks until the data the lock protects can be safely read.\n\t///\n\t/// Some locks, but not all, will allow multiple readers at once. If\n\t/// multiple readers are allowed for a [`Lockable`] type, then the\n\t/// [`Sharable`] marker trait should be implemented.\n\t///\n\t/// # Safety\n\t///\n\t/// It is undefined behavior to use this without ownership or mutable\n\t/// access to the [`ThreadKey`], which should last as long as the return\n\t/// value is alive.\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\tunsafe fn raw_read(\u0026self);\n\n\t// Attempt to read without blocking.\n\t///\n\t/// Returns `true` if successful, `false` otherwise.\n\t///\n\t/// Some locks, but not all, will allow multiple readers at once. If\n\t/// multiple readers are allowed for a [`Lockable`] type, then the\n\t/// [`Sharable`] marker trait should be implemented.\n\t///\n\t/// # Safety\n\t///\n\t/// It is undefined behavior to use this without ownership or mutable\n\t/// access to the [`ThreadKey`], which should last as long as the return\n\t/// value is alive.\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool;\n\n\t/// Releases the lock after calling `read`.\n\t///\n\t/// # Safety\n\t///\n\t/// It is undefined behavior to use this if the read lock is not acquired\n\tunsafe fn raw_unlock_read(\u0026self);\n}\n\n/// A type that may be locked and unlocked.\n///\n/// This trait is usually implemented on collections of [`RawLock`]s. For\n/// example, a `Vec\u003cMutex\u003ci32\u003e\u003e`.\n///\n/// # Safety\n///\n/// Acquiring the locks returned by `get_ptrs` must allow access to the values\n/// returned by `guard`.\n///\n/// Dropping the `Guard` must unlock those same locks.\n///\n/// The order of the resulting list from `get_ptrs` must be deterministic. As\n/// long as the value is not mutated, the references must always be in the same\n/// order.\npub unsafe trait Lockable {\n\t/// The exclusive guard that does not hold a key\n\ttype Guard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\t/// Yields a list of references to the [`RawLock`]s contained within this\n\t/// value.\n\t///\n\t/// These reference locks which must be locked before acquiring a guard,\n\t/// and unlocked when the guard is dropped. The order of the resulting list\n\t/// is deterministic. As long as the value is not mutated, the references\n\t/// will always be in the same order.\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e);\n\n\t/// Returns a guard that can be used to access the underlying data mutably.\n\t///\n\t/// # Safety\n\t///\n\t/// All locks given by calling [`Lockable::get_ptrs`] must be locked\n\t/// exclusively before calling this function. The locks must not be\n\t/// unlocked until this guard is dropped.\n\t#[must_use]\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e;\n\n\t#[must_use]\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e;\n}\n\n/// Allows a lock to be accessed by multiple readers.\n///\n/// # Safety\n///\n/// Acquiring shared access to the locks returned by `get_ptrs` must allow\n/// shared access to the values returned by `read_guard`.\n///\n/// Dropping the `ReadGuard` must unlock those same locks.\npub unsafe trait Sharable: Lockable {\n\t/// The shared guard type that does not hold a key\n\ttype ReadGuard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\t/// Returns a guard that can be used to immutably access the underlying\n\t/// data.\n\t///\n\t/// # Safety\n\t///\n\t/// All locks given by calling [`Lockable::get_ptrs`] must be locked using\n\t/// [`RawLock::raw_read`] before calling this function. The locks must not be\n\t/// unlocked until this guard is dropped.\n\t#[must_use]\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e;\n\n\t#[must_use]\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e;\n}\n\n/// A type that may be locked and unlocked, and is known to be the only valid\n/// instance of the lock.\n///\n/// # Safety\n///\n/// There must not be any two values which can unlock the value at the same\n/// time, i.e., this must either be an owned value or a mutable reference.\npub unsafe trait OwnedLockable: Lockable {}\n\n/// A trait which indicates that `into_inner` is a valid operation for a\n/// [`Lockable`].\n///\n/// This is used for types like [`Poisonable`] to access the inner value of a\n/// lock. [`Poisonable::into_inner`] calls [`LockableIntoInner::into_inner`] to\n/// return a mutable reference of the inner value. This isn't implemented for\n/// some `Lockable`s, such as `\u0026[T]`.\n///\n/// [`Poisonable`]: `crate::Poisonable`\n/// [`Poisonable::into_inner`]: `crate::poisonable::Poisonable::into_inner`\npub trait LockableIntoInner: Lockable {\n\t/// The inner type that is behind the lock\n\ttype Inner;\n\n\t/// Consumes the lock, returning the underlying the lock.\n\tfn into_inner(self) -\u003e Self::Inner;\n}\n\n/// A trait which indicates that `as_mut` is a valid operation for a\n/// [`Lockable`].\n///\n/// This is used for types like [`Poisonable`] to access the inner value of a\n/// lock. [`Poisonable::get_mut`] calls [`LockableGetMut::get_mut`] to return a\n/// mutable reference of the inner value. This isn't implemented for some\n/// `Lockable`s, such as `\u0026[T]`.\n///\n/// [`Poisonable`]: `crate::Poisonable`\n/// [`Poisonable::get_mut`]: `crate::poisonable::Poisonable::get_mut`\npub trait LockableGetMut: Lockable {\n\t/// The inner type that is behind the lock\n\ttype Inner\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\t/// Returns a mutable reference to the underlying data.\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e;\n}\n\nunsafe impl\u003cT: Lockable\u003e Lockable for \u0026T {\n\ttype Guard\u003c'g\u003e\n\t\t= T::Guard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= T::DataMut\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\t(*self).get_ptrs(ptrs);\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\t(*self).guard()\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\t(*self).data_mut()\n\t}\n}\n\nunsafe impl\u003cT: Sharable\u003e Sharable for \u0026T {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= T::ReadGuard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= T::DataRef\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\t(*self).read_guard()\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\t(*self).data_ref()\n\t}\n}\n\nunsafe impl\u003cT: Lockable\u003e Lockable for \u0026mut T {\n\ttype Guard\u003c'g\u003e\n\t\t= T::Guard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= T::DataMut\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\t(**self).get_ptrs(ptrs)\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\t(**self).guard()\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\t(**self).data_mut()\n\t}\n}\n\nimpl\u003cT: LockableGetMut\u003e LockableGetMut for \u0026mut T {\n\ttype Inner\u003c'a\u003e\n\t\t= T::Inner\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\t(*self).get_mut()\n\t}\n}\n\nunsafe impl\u003cT: Sharable\u003e Sharable for \u0026mut T {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= T::ReadGuard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= T::DataRef\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\t(**self).read_guard()\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\t(**self).data_ref()\n\t}\n}\n\nunsafe impl\u003cT: OwnedLockable\u003e OwnedLockable for \u0026mut T {}\n\n/// Implements `Lockable`, `Sharable`, and `OwnedLockable` for tuples\n/// ex: `tuple_impls!(A B C, 0 1 2);`\nmacro_rules! tuple_impls {\n\t($($generic:ident)*, $($value:tt)*) =\u003e {\n\t\tunsafe impl\u003c$($generic: Lockable,)*\u003e Lockable for ($($generic,)*) {\n\t\t\ttype Guard\u003c'g\u003e = ($($generic::Guard\u003c'g\u003e,)*) where Self: 'g;\n\n\t\t\ttype DataMut\u003c'a\u003e = ($($generic::DataMut\u003c'a\u003e,)*) where Self: 'a;\n\n\t\t\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\t\t\t$(self.$value.get_ptrs(ptrs));*\n\t\t\t}\n\n\t\t\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\t\t\t// It's weird that this works\n\t\t\t\t// I don't think any other way of doing it compiles\n\t\t\t\t($(self.$value.guard(),)*)\n\t\t\t}\n\n\t\t\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\t\t\t($(self.$value.data_mut(),)*)\n\t\t\t}\n\t\t}\n\n\t\timpl\u003c$($generic: LockableGetMut,)*\u003e LockableGetMut for ($($generic,)*) {\n\t\t\ttype Inner\u003c'a\u003e = ($($generic::Inner\u003c'a\u003e,)*) where Self: 'a;\n\n\t\t\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\t\t\t($(self.$value.get_mut(),)*)\n\t\t\t}\n\t\t}\n\n\t\timpl\u003c$($generic: LockableIntoInner,)*\u003e LockableIntoInner for ($($generic,)*) {\n\t\t\ttype Inner = ($($generic::Inner,)*);\n\n\t\t\tfn into_inner(self) -\u003e Self::Inner {\n\t\t\t\t($(self.$value.into_inner(),)*)\n\t\t\t}\n\t\t}\n\n\t\tunsafe impl\u003c$($generic: Sharable,)*\u003e Sharable for ($($generic,)*) {\n\t\t\ttype ReadGuard\u003c'g\u003e = ($($generic::ReadGuard\u003c'g\u003e,)*) where Self: 'g;\n\n\t\t\ttype DataRef\u003c'a\u003e = ($($generic::DataRef\u003c'a\u003e,)*) where Self: 'a;\n\n\t\t\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\t\t\t($(self.$value.read_guard(),)*)\n\t\t\t}\n\n\t\t\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\t\t\t($(self.$value.data_ref(),)*)\n\t\t\t}\n\t\t}\n\n\t\tunsafe impl\u003c$($generic: OwnedLockable,)*\u003e OwnedLockable for ($($generic,)*) {}\n\t};\n}\n\ntuple_impls!(A, 0);\ntuple_impls!(A B, 0 1);\ntuple_impls!(A B C, 0 1 2);\ntuple_impls!(A B C D, 0 1 2 3);\ntuple_impls!(A B C D E, 0 1 2 3 4);\ntuple_impls!(A B C D E F, 0 1 2 3 4 5);\ntuple_impls!(A B C D E F G, 0 1 2 3 4 5 6);\n\nunsafe impl\u003cT: Lockable, const N: usize\u003e Lockable for [T; N] {\n\ttype Guard\u003c'g\u003e\n\t\t= [T::Guard\u003c'g\u003e; N]\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= [T::DataMut\u003c'a\u003e; N]\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tfor lock in self {\n\t\t\tlock.get_ptrs(ptrs);\n\t\t}\n\t}\n\n\tunsafe fn guard\u003c'g\u003e(\u0026'g self) -\u003e Self::Guard\u003c'g\u003e {\n\t\t// The MaybeInit helper functions for arrays aren't stable yet, so\n\t\t// we'll just have to implement it ourselves\n\t\tlet mut guards = MaybeUninit::\u003c[MaybeUninit\u003cT::Guard\u003c'g\u003e\u003e; N]\u003e::uninit().assume_init();\n\t\tfor i in 0..N {\n\t\t\tguards[i].write(self[i].guard());\n\t\t}\n\n\t\tguards.map(|g| g.assume_init())\n\t}\n\n\tunsafe fn data_mut\u003c'a\u003e(\u0026'a self) -\u003e Self::DataMut\u003c'a\u003e {\n\t\tlet mut guards = MaybeUninit::\u003c[MaybeUninit\u003cT::DataMut\u003c'a\u003e\u003e; N]\u003e::uninit().assume_init();\n\t\tfor i in 0..N {\n\t\t\tguards[i].write(self[i].data_mut());\n\t\t}\n\n\t\tguards.map(|g| g.assume_init())\n\t}\n}\n\nimpl\u003cT: LockableGetMut, const N: usize\u003e LockableGetMut for [T; N] {\n\ttype Inner\u003c'a\u003e\n\t\t= [T::Inner\u003c'a\u003e; N]\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tunsafe {\n\t\t\tlet mut guards = MaybeUninit::\u003c[MaybeUninit\u003cT::Inner\u003c'_\u003e\u003e; N]\u003e::uninit().assume_init();\n\t\t\tfor (i, lock) in self.iter_mut().enumerate() {\n\t\t\t\tguards[i].write(lock.get_mut());\n\t\t\t}\n\n\t\t\tguards.map(|g| g.assume_init())\n\t\t}\n\t}\n}\n\nimpl\u003cT: LockableIntoInner, const N: usize\u003e LockableIntoInner for [T; N] {\n\ttype Inner = [T::Inner; N];\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tunsafe {\n\t\t\tlet mut guards = MaybeUninit::\u003c[MaybeUninit\u003cT::Inner\u003e; N]\u003e::uninit().assume_init();\n\t\t\tfor (i, lock) in self.into_iter().enumerate() {\n\t\t\t\tguards[i].write(lock.into_inner());\n\t\t\t}\n\n\t\t\tguards.map(|g| g.assume_init())\n\t\t}\n\t}\n}\n\nunsafe impl\u003cT: Sharable, const N: usize\u003e Sharable for [T; N] {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= [T::ReadGuard\u003c'g\u003e; N]\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= [T::DataRef\u003c'a\u003e; N]\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard\u003c'g\u003e(\u0026'g self) -\u003e Self::ReadGuard\u003c'g\u003e {\n\t\tlet mut guards = MaybeUninit::\u003c[MaybeUninit\u003cT::ReadGuard\u003c'g\u003e\u003e; N]\u003e::uninit().assume_init();\n\t\tfor i in 0..N {\n\t\t\tguards[i].write(self[i].read_guard());\n\t\t}\n\n\t\tguards.map(|g| g.assume_init())\n\t}\n\n\tunsafe fn data_ref\u003c'a\u003e(\u0026'a self) -\u003e Self::DataRef\u003c'a\u003e {\n\t\tlet mut guards = MaybeUninit::\u003c[MaybeUninit\u003cT::DataRef\u003c'a\u003e\u003e; N]\u003e::uninit().assume_init();\n\t\tfor i in 0..N {\n\t\t\tguards[i].write(self[i].data_ref());\n\t\t}\n\n\t\tguards.map(|g| g.assume_init())\n\t}\n}\n\nunsafe impl\u003cT: OwnedLockable, const N: usize\u003e OwnedLockable for [T; N] {}\n\nunsafe impl\u003cT: Lockable\u003e Lockable for Box\u003c[T]\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= Box\u003c[T::Guard\u003c'g\u003e]\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= Box\u003c[T::DataMut\u003c'a\u003e]\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tfor lock in self {\n\t\t\tlock.get_ptrs(ptrs);\n\t\t}\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tself.iter().map(|lock| lock.guard()).collect()\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.iter().map(|lock| lock.data_mut()).collect()\n\t}\n}\n\nimpl\u003cT: LockableGetMut + 'static\u003e LockableGetMut for Box\u003c[T]\u003e {\n\ttype Inner\u003c'a\u003e\n\t\t= Box\u003c[T::Inner\u003c'a\u003e]\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tself.iter_mut().map(LockableGetMut::get_mut).collect()\n\t}\n}\n\nunsafe impl\u003cT: Sharable\u003e Sharable for Box\u003c[T]\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= Box\u003c[T::ReadGuard\u003c'g\u003e]\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= Box\u003c[T::DataRef\u003c'a\u003e]\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tself.iter().map(|lock| lock.read_guard()).collect()\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tself.iter().map(|lock| lock.data_ref()).collect()\n\t}\n}\n\nunsafe impl\u003cT: Sharable\u003e Sharable for Vec\u003cT\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= Box\u003c[T::ReadGuard\u003c'g\u003e]\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= Box\u003c[T::DataRef\u003c'a\u003e]\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tself.iter().map(|lock| lock.read_guard()).collect()\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tself.iter().map(|lock| lock.data_ref()).collect()\n\t}\n}\n\nunsafe impl\u003cT: OwnedLockable\u003e OwnedLockable for Box\u003c[T]\u003e {}\n\nunsafe impl\u003cT: Lockable\u003e Lockable for Vec\u003cT\u003e {\n\t// There's no reason why I'd ever want to extend a list of lock guards\n\ttype Guard\u003c'g\u003e\n\t\t= Box\u003c[T::Guard\u003c'g\u003e]\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= Box\u003c[T::DataMut\u003c'a\u003e]\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tfor lock in self {\n\t\t\tlock.get_ptrs(ptrs);\n\t\t}\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tself.iter().map(|lock| lock.guard()).collect()\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.iter().map(|lock| lock.data_mut()).collect()\n\t}\n}\n\n// I'd make a generic impl\u003cT: Lockable, I: IntoIterator\u003cItem=T\u003e\u003e Lockable for I\n// but I think that'd require sealing up this trait\n\n// TODO: using edition 2024, impl LockableIntoInner for Box\u003c[T]\u003e\n\nimpl\u003cT: LockableGetMut + 'static\u003e LockableGetMut for Vec\u003cT\u003e {\n\ttype Inner\u003c'a\u003e\n\t\t= Box\u003c[T::Inner\u003c'a\u003e]\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tself.iter_mut().map(LockableGetMut::get_mut).collect()\n\t}\n}\n\nimpl\u003cT: LockableIntoInner\u003e LockableIntoInner for Vec\u003cT\u003e {\n\ttype Inner = Box\u003c[T::Inner]\u003e;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tself.into_iter()\n\t\t\t.map(LockableIntoInner::into_inner)\n\t\t\t.collect()\n\t}\n}\n\nunsafe impl\u003cT: OwnedLockable\u003e OwnedLockable for Vec\u003cT\u003e {}\n\n#[cfg(test)]\nmod tests {\n\tuse super::*;\n\tuse crate::{LockCollection, Mutex, RwLock, ThreadKey};\n\n\t#[test]\n\tfn mut_ref_get_ptrs() {\n\t\tlet mut rwlock = RwLock::new(5);\n\t\tlet mutref = \u0026mut rwlock;\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tmutref.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tassert!(std::ptr::addr_eq(lock_ptrs[0], mutref));\n\t}\n\n\t#[test]\n\tfn array_get_ptrs_empty() {\n\t\tlet locks: [Mutex\u003c()\u003e; 0] = [];\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert!(lock_ptrs.is_empty());\n\t}\n\n\t#[test]\n\tfn array_get_ptrs_length_one() {\n\t\tlet locks: [Mutex\u003ci32\u003e; 1] = [Mutex::new(1)];\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[0], locks[0].raw())) }\n\t}\n\n\t#[test]\n\tfn array_get_ptrs_length_two() {\n\t\tlet locks: [Mutex\u003ci32\u003e; 2] = [Mutex::new(1), Mutex::new(2)];\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 2);\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[0], locks[0].raw())) }\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[1], locks[1].raw())) }\n\t}\n\n\t#[test]\n\tfn vec_get_ptrs_empty() {\n\t\tlet locks: Vec\u003cMutex\u003c()\u003e\u003e = Vec::new();\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert!(lock_ptrs.is_empty());\n\t}\n\n\t#[test]\n\tfn vec_get_ptrs_length_one() {\n\t\tlet locks: Vec\u003cMutex\u003ci32\u003e\u003e = vec![Mutex::new(1)];\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[0], locks[0].raw())) }\n\t}\n\n\t#[test]\n\tfn vec_get_ptrs_length_two() {\n\t\tlet locks: Vec\u003cMutex\u003ci32\u003e\u003e = vec![Mutex::new(1), Mutex::new(2)];\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 2);\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[0], locks[0].raw())) }\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[1], locks[1].raw())) }\n\t}\n\n\t#[test]\n\tfn vec_as_mut() {\n\t\tlet mut locks: Vec\u003cMutex\u003ci32\u003e\u003e = vec![Mutex::new(1), Mutex::new(2)];\n\t\tlet lock_ptrs = LockableGetMut::get_mut(\u0026mut locks);\n\n\t\tassert_eq!(lock_ptrs.len(), 2);\n\t\tassert_eq!(*lock_ptrs[0], 1);\n\t\tassert_eq!(*lock_ptrs[1], 2);\n\t}\n\n\t#[test]\n\tfn vec_into_inner() {\n\t\tlet locks: Vec\u003cMutex\u003ci32\u003e\u003e = vec![Mutex::new(1), Mutex::new(2)];\n\t\tlet lock_ptrs = LockableIntoInner::into_inner(locks);\n\n\t\tassert_eq!(lock_ptrs.len(), 2);\n\t\tassert_eq!(lock_ptrs[0], 1);\n\t\tassert_eq!(lock_ptrs[1], 2);\n\t}\n\n\t#[test]\n\tfn vec_guard_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = vec![RwLock::new(1), RwLock::new(2)];\n\t\tlet collection = LockCollection::new(locks);\n\n\t\tlet mut guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], 1);\n\t\tassert_eq!(*guard[1], 2);\n\t\t*guard[0] = 3;\n\n\t\tlet key = LockCollection::\u003cVec\u003cRwLock\u003c_\u003e\u003e\u003e::unlock(guard);\n\t\tlet guard = collection.read(key);\n\t\tassert_eq!(*guard[0], 3);\n\t\tassert_eq!(*guard[1], 2);\n\t}\n\n\t#[test]\n\tfn vec_data_mut() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = vec![Mutex::new(1), Mutex::new(2)];\n\t\tlet collection = LockCollection::new(mutexes);\n\t\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 1);\n\t\t\tassert_eq!(*guard[1], 2);\n\t\t\t*guard[0] = 3;\n\t\t});\n\n\t\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 3);\n\t\t\tassert_eq!(*guard[1], 2);\n\t\t})\n\t}\n\n\t#[test]\n\tfn vec_data_ref() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = vec![RwLock::new(1), RwLock::new(2)];\n\t\tlet collection = LockCollection::new(mutexes);\n\t\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 1);\n\t\t\tassert_eq!(*guard[1], 2);\n\t\t\t*guard[0] = 3;\n\t\t});\n\n\t\tcollection.scoped_read(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 3);\n\t\t\tassert_eq!(*guard[1], 2);\n\t\t})\n\t}\n\n\t#[test]\n\tfn box_get_ptrs_empty() {\n\t\tlet locks: Box\u003c[Mutex\u003c()\u003e]\u003e = Box::from([]);\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert!(lock_ptrs.is_empty());\n\t}\n\n\t#[test]\n\tfn box_get_ptrs_length_one() {\n\t\tlet locks: Box\u003c[Mutex\u003ci32\u003e]\u003e = vec![Mutex::new(1)].into_boxed_slice();\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[0], locks[0].raw())) }\n\t}\n\n\t#[test]\n\tfn box_get_ptrs_length_two() {\n\t\tlet locks: Box\u003c[Mutex\u003ci32\u003e]\u003e = vec![Mutex::new(1), Mutex::new(2)].into_boxed_slice();\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 2);\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[0], locks[0].raw())) }\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[1], locks[1].raw())) }\n\t}\n\n\t#[test]\n\tfn box_as_mut() {\n\t\tlet mut locks: Box\u003c[Mutex\u003ci32\u003e]\u003e = vec![Mutex::new(1), Mutex::new(2)].into_boxed_slice();\n\t\tlet lock_ptrs = LockableGetMut::get_mut(\u0026mut locks);\n\n\t\tassert_eq!(lock_ptrs.len(), 2);\n\t\tassert_eq!(*lock_ptrs[0], 1);\n\t\tassert_eq!(*lock_ptrs[1], 2);\n\t}\n\n\t#[test]\n\tfn box_guard_mut() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet x = [Mutex::new(1), Mutex::new(2)];\n\t\tlet collection: LockCollection\u003cBox\u003c[Mutex\u003c_\u003e]\u003e\u003e = LockCollection::new(Box::new(x));\n\n\t\tlet mut guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], 1);\n\t\tassert_eq!(*guard[1], 2);\n\t\t*guard[0] = 3;\n\n\t\tlet key = LockCollection::\u003cBox\u003c[Mutex\u003c_\u003e]\u003e\u003e::unlock(guard);\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], 3);\n\t\tassert_eq!(*guard[1], 2);\n\t}\n\n\t#[test]\n\tfn box_data_mut() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = vec![Mutex::new(1), Mutex::new(2)].into_boxed_slice();\n\t\tlet collection = LockCollection::new(mutexes);\n\t\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 1);\n\t\t\tassert_eq!(*guard[1], 2);\n\t\t\t*guard[0] = 3;\n\t\t});\n\n\t\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 3);\n\t\t\tassert_eq!(*guard[1], 2);\n\t\t});\n\t}\n\n\t#[test]\n\tfn box_guard_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = [RwLock::new(1), RwLock::new(2)];\n\t\tlet collection: LockCollection\u003cBox\u003c[RwLock\u003c_\u003e]\u003e\u003e = LockCollection::new(Box::new(locks));\n\n\t\tlet mut guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], 1);\n\t\tassert_eq!(*guard[1], 2);\n\t\t*guard[0] = 3;\n\n\t\tlet key = LockCollection::\u003cBox\u003c[RwLock\u003c_\u003e]\u003e\u003e::unlock(guard);\n\t\tlet guard = collection.read(key);\n\t\tassert_eq!(*guard[0], 3);\n\t\tassert_eq!(*guard[1], 2);\n\t}\n\n\t#[test]\n\tfn box_data_ref() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = vec![RwLock::new(1), RwLock::new(2)].into_boxed_slice();\n\t\tlet collection = LockCollection::new(mutexes);\n\t\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 1);\n\t\t\tassert_eq!(*guard[1], 2);\n\t\t\t*guard[0] = 3;\n\t\t});\n\n\t\tcollection.scoped_read(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 3);\n\t\t\tassert_eq!(*guard[1], 2);\n\t\t});\n\t}\n}\n","traces":[{"line":232,"address":[162784,162816,162848],"length":1,"stats":{"Line":47}},{"line":233,"address":[247374,247342,247406,247310],"length":1,"stats":{"Line":46}},{"line":236,"address":[961984],"length":1,"stats":{"Line":10}},{"line":237,"address":[174309,174325],"length":1,"stats":{"Line":10}},{"line":240,"address":[],"length":0,"stats":{"Line":2}},{"line":241,"address":[216885],"length":1,"stats":{"Line":2}},{"line":256,"address":[215536,215552],"length":1,"stats":{"Line":3}},{"line":257,"address":[962005,962069,962033],"length":1,"stats":{"Line":3}},{"line":260,"address":[],"length":0,"stats":{"Line":0}},{"line":261,"address":[],"length":0,"stats":{"Line":0}},{"line":276,"address":[],"length":0,"stats":{"Line":1}},{"line":277,"address":[],"length":0,"stats":{"Line":1}},{"line":280,"address":[],"length":0,"stats":{"Line":0}},{"line":281,"address":[],"length":0,"stats":{"Line":0}},{"line":284,"address":[],"length":0,"stats":{"Line":0}},{"line":285,"address":[],"length":0,"stats":{"Line":0}},{"line":295,"address":[],"length":0,"stats":{"Line":0}},{"line":296,"address":[],"length":0,"stats":{"Line":0}},{"line":311,"address":[],"length":0,"stats":{"Line":0}},{"line":312,"address":[],"length":0,"stats":{"Line":0}},{"line":315,"address":[],"length":0,"stats":{"Line":0}},{"line":316,"address":[],"length":0,"stats":{"Line":0}},{"line":331,"address":[228336],"length":1,"stats":{"Line":16}},{"line":332,"address":[247496],"length":1,"stats":{"Line":19}},{"line":335,"address":[162880,163071],"length":1,"stats":{"Line":9}},{"line":338,"address":[1289086,1288974,1289198],"length":1,"stats":{"Line":9}},{"line":357,"address":[972059,971712],"length":1,"stats":{"Line":4}},{"line":358,"address":[1289423,1289654,1290092,1289318,1289974,1289740],"length":1,"stats":{"Line":8}},{"line":367,"address":[971488,971376,971464,971688,971600,971576],"length":1,"stats":{"Line":2}},{"line":368,"address":[215680],"length":1,"stats":{"Line":2}},{"line":399,"address":[962112,962336,962224],"length":1,"stats":{"Line":17}},{"line":400,"address":[217794,217859,217906,217971],"length":1,"stats":{"Line":32}},{"line":401,"address":[962205,962429,962317],"length":1,"stats":{"Line":15}},{"line":405,"address":[963184,962448,962816],"length":1,"stats":{"Line":8}},{"line":408,"address":[],"length":0,"stats":{"Line":2}},{"line":409,"address":[962898,962530,963260,963000,962632,963198],"length":1,"stats":{"Line":15}},{"line":410,"address":[962650,963275,963018,962780,963148,963390],"length":1,"stats":{"Line":14}},{"line":413,"address":[],"length":0,"stats":{"Line":22}},{"line":416,"address":[963424],"length":1,"stats":{"Line":3}},{"line":417,"address":[],"length":0,"stats":{"Line":0}},{"line":418,"address":[1281944,1281842],"length":1,"stats":{"Line":6}},{"line":419,"address":[963756,963626],"length":1,"stats":{"Line":6}},{"line":422,"address":[217133,217527],"length":1,"stats":{"Line":9}},{"line":432,"address":[1282128],"length":1,"stats":{"Line":2}},{"line":434,"address":[],"length":0,"stats":{"Line":0}},{"line":435,"address":[1282218,1282410],"length":1,"stats":{"Line":4}},{"line":436,"address":[1282452,1282538],"length":1,"stats":{"Line":4}},{"line":439,"address":[605472,605495,605440,605463],"length":1,"stats":{"Line":6}},{"line":447,"address":[],"length":0,"stats":{"Line":1}},{"line":449,"address":[1282598],"length":1,"stats":{"Line":1}},{"line":450,"address":[],"length":0,"stats":{"Line":4}},{"line":451,"address":[1283070,1283008],"length":1,"stats":{"Line":2}},{"line":454,"address":[1283024],"length":1,"stats":{"Line":3}},{"line":470,"address":[],"length":0,"stats":{"Line":4}},{"line":471,"address":[],"length":0,"stats":{"Line":0}},{"line":472,"address":[964664,964562,964930,964254,965032,964316],"length":1,"stats":{"Line":7}},{"line":473,"address":[],"length":0,"stats":{"Line":6}},{"line":476,"address":[964989,964621,964306],"length":1,"stats":{"Line":10}},{"line":479,"address":[],"length":0,"stats":{"Line":1}},{"line":480,"address":[],"length":0,"stats":{"Line":0}},{"line":481,"address":[965298,965400],"length":1,"stats":{"Line":2}},{"line":482,"address":[965418,965548],"length":1,"stats":{"Line":2}},{"line":485,"address":[965357],"length":1,"stats":{"Line":3}},{"line":502,"address":[],"length":0,"stats":{"Line":3}},{"line":503,"address":[],"length":0,"stats":{"Line":5}},{"line":504,"address":[994013,994125,993901],"length":1,"stats":{"Line":2}},{"line":508,"address":[],"length":0,"stats":{"Line":2}},{"line":509,"address":[605728,605753,605680,605705],"length":1,"stats":{"Line":6}},{"line":512,"address":[],"length":0,"stats":{"Line":2}},{"line":513,"address":[],"length":0,"stats":{"Line":6}},{"line":523,"address":[994336],"length":1,"stats":{"Line":1}},{"line":524,"address":[],"length":0,"stats":{"Line":1}},{"line":539,"address":[],"length":0,"stats":{"Line":1}},{"line":540,"address":[],"length":0,"stats":{"Line":3}},{"line":543,"address":[994432],"length":1,"stats":{"Line":1}},{"line":544,"address":[],"length":0,"stats":{"Line":3}},{"line":559,"address":[],"length":0,"stats":{"Line":1}},{"line":560,"address":[532437],"length":1,"stats":{"Line":3}},{"line":563,"address":[],"length":0,"stats":{"Line":1}},{"line":564,"address":[606041,606016],"length":1,"stats":{"Line":3}},{"line":582,"address":[],"length":0,"stats":{"Line":5}},{"line":583,"address":[],"length":0,"stats":{"Line":9}},{"line":584,"address":[],"length":0,"stats":{"Line":4}},{"line":588,"address":[],"length":0,"stats":{"Line":2}},{"line":589,"address":[606089,606064,606112,606137],"length":1,"stats":{"Line":6}},{"line":592,"address":[],"length":0,"stats":{"Line":2}},{"line":593,"address":[],"length":0,"stats":{"Line":6}},{"line":608,"address":[],"length":0,"stats":{"Line":2}},{"line":609,"address":[],"length":0,"stats":{"Line":2}},{"line":616,"address":[],"length":0,"stats":{"Line":1}},{"line":617,"address":[],"length":0,"stats":{"Line":1}},{"line":618,"address":[],"length":0,"stats":{"Line":0}}],"covered":75,"coverable":92},{"path":["/","home","botahamec","Projects","happylock","src","mutex","guard.rs"],"content":"use std::fmt::{Debug, Display};\nuse std::hash::Hash;\nuse std::marker::PhantomData;\nuse std::ops::{Deref, DerefMut};\n\nuse lock_api::RawMutex;\n\nuse crate::lockable::RawLock;\nuse crate::ThreadKey;\n\nuse super::{Mutex, MutexGuard, MutexRef};\n\n// These impls make things slightly easier because now you can use\n// `println!(\"{guard}\")` instead of `println!(\"{}\", *guard)`\n\n#[mutants::skip] // hashing involves RNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Hash + ?Sized, R: RawMutex\u003e Hash for MutexRef\u003c'_, T, R\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.deref().hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug + ?Sized, R: RawMutex\u003e Debug for MutexRef\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: Display + ?Sized, R: RawMutex\u003e Display for MutexRef\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e Drop for MutexRef\u003c'_, T, R\u003e {\n\tfn drop(\u0026mut self) {\n\t\t// safety: this guard is being destroyed, so the data cannot be\n\t\t// accessed without locking again\n\t\tunsafe { self.0.raw_unlock_write() }\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e Deref for MutexRef\u003c'_, T, R\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t// safety: this is the only type that can use `value`, and there's\n\t\t// a reference to this type, so there cannot be any mutable\n\t\t// references to this value.\n\t\tunsafe { \u0026*self.0.data.get() }\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e DerefMut for MutexRef\u003c'_, T, R\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t// safety: this is the only type that can use `value`, and we have a\n\t\t// mutable reference to this type, so there cannot be any other\n\t\t// references to this value.\n\t\tunsafe { \u0026mut *self.0.data.get() }\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e AsRef\u003cT\u003e for MutexRef\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e AsMut\u003cT\u003e for MutexRef\u003c'_, T, R\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself\n\t}\n}\n\nimpl\u003c'a, T: ?Sized, R: RawMutex\u003e MutexRef\u003c'a, T, R\u003e {\n\t/// Creates a reference to the underlying data of a mutex without\n\t/// attempting to lock it or take ownership of the key. But it's also quite\n\t/// dangerous to drop.\n\tpub(crate) const unsafe fn new(mutex: \u0026'a Mutex\u003cT, R\u003e) -\u003e Self {\n\t\tSelf(mutex, PhantomData)\n\t}\n}\n\n// it's kinda annoying to re-implement some of this stuff on guards\n// there's nothing i can do about that\n\n#[mutants::skip] // hashing involves RNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Hash + ?Sized, R: RawMutex\u003e Hash for MutexGuard\u003c'_, T, R\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.deref().hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug + ?Sized, R: RawMutex\u003e Debug for MutexGuard\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: Display + ?Sized, R: RawMutex\u003e Display for MutexGuard\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e Deref for MutexGuard\u003c'_, T, R\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t\u0026self.mutex\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e DerefMut for MutexGuard\u003c'_, T, R\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t\u0026mut self.mutex\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e AsRef\u003cT\u003e for MutexGuard\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e AsMut\u003cT\u003e for MutexGuard\u003c'_, T, R\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself\n\t}\n}\n\nimpl\u003c'a, T: ?Sized, R: RawMutex\u003e MutexGuard\u003c'a, T, R\u003e {\n\t/// Create a guard to the given mutex. Undefined if multiple guards to the\n\t/// same mutex exist at once.\n\t#[must_use]\n\tpub(super) const unsafe fn new(mutex: \u0026'a Mutex\u003cT, R\u003e, thread_key: ThreadKey) -\u003e Self {\n\t\tSelf {\n\t\t\tmutex: MutexRef(mutex, PhantomData),\n\t\t\tthread_key,\n\t\t}\n\t}\n}\n\nunsafe impl\u003cT: ?Sized + Sync, R: RawMutex + Sync\u003e Sync for MutexRef\u003c'_, T, R\u003e {}\n","traces":[{"line":33,"address":[1283344],"length":1,"stats":{"Line":1}},{"line":34,"address":[],"length":0,"stats":{"Line":1}},{"line":39,"address":[153792],"length":1,"stats":{"Line":6}},{"line":42,"address":[],"length":0,"stats":{"Line":7}},{"line":49,"address":[1283392,1283424],"length":1,"stats":{"Line":4}},{"line":53,"address":[163269],"length":1,"stats":{"Line":4}},{"line":58,"address":[1283488,1283456],"length":1,"stats":{"Line":5}},{"line":62,"address":[1283461,1283493],"length":1,"stats":{"Line":5}},{"line":67,"address":[],"length":0,"stats":{"Line":2}},{"line":68,"address":[],"length":0,"stats":{"Line":2}},{"line":73,"address":[],"length":0,"stats":{"Line":1}},{"line":74,"address":[],"length":0,"stats":{"Line":1}},{"line":82,"address":[],"length":0,"stats":{"Line":4}},{"line":83,"address":[],"length":0,"stats":{"Line":0}},{"line":107,"address":[],"length":0,"stats":{"Line":1}},{"line":108,"address":[],"length":0,"stats":{"Line":1}},{"line":115,"address":[],"length":0,"stats":{"Line":2}},{"line":116,"address":[],"length":0,"stats":{"Line":3}},{"line":121,"address":[],"length":0,"stats":{"Line":2}},{"line":122,"address":[],"length":0,"stats":{"Line":2}},{"line":127,"address":[1283696],"length":1,"stats":{"Line":2}},{"line":128,"address":[],"length":0,"stats":{"Line":2}},{"line":133,"address":[],"length":0,"stats":{"Line":1}},{"line":134,"address":[],"length":0,"stats":{"Line":1}},{"line":142,"address":[140544],"length":1,"stats":{"Line":4}},{"line":144,"address":[],"length":0,"stats":{"Line":0}}],"covered":24,"coverable":26},{"path":["/","home","botahamec","Projects","happylock","src","mutex","mutex.rs"],"content":"use std::cell::UnsafeCell;\nuse std::fmt::Debug;\nuse std::marker::PhantomData;\nuse std::panic::AssertUnwindSafe;\n\nuse lock_api::RawMutex;\n\nuse crate::collection::utils;\nuse crate::handle_unwind::handle_unwind;\nuse crate::lockable::{Lockable, LockableGetMut, LockableIntoInner, OwnedLockable, RawLock};\nuse crate::poisonable::PoisonFlag;\nuse crate::{Keyable, ThreadKey};\n\nuse super::{Mutex, MutexGuard, MutexRef};\n\nunsafe impl\u003cT: ?Sized, R: RawMutex\u003e RawLock for Mutex\u003cT, R\u003e {\n\tfn poison(\u0026self) {\n\t\tself.poison.poison();\n\t}\n\n\tunsafe fn raw_write(\u0026self) {\n\t\tassert!(!self.poison.is_poisoned(), \"The mutex has been killed\");\n\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.lock(), || self.poison())\n\t}\n\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool {\n\t\tif self.poison.is_poisoned() {\n\t\t\treturn false;\n\t\t}\n\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.try_lock(), || self.poison())\n\t}\n\n\tunsafe fn raw_unlock_write(\u0026self) {\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.unlock(), || self.poison())\n\t}\n\n\t// this is the closest thing to a read we can get, but Sharable isn't\n\t// implemented for this\n\t#[mutants::skip]\n\t#[cfg(not(tarpaulin_include))]\n\tunsafe fn raw_read(\u0026self) {\n\t\tself.raw_write()\n\t}\n\n\t#[mutants::skip]\n\t#[cfg(not(tarpaulin_include))]\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tself.raw_try_write()\n\t}\n\n\t#[mutants::skip]\n\t#[cfg(not(tarpaulin_include))]\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tself.raw_unlock_write()\n\t}\n}\n\nunsafe impl\u003cT, R: RawMutex\u003e Lockable for Mutex\u003cT, R\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= MutexRef\u003c'g, T, R\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= \u0026'a mut T\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tptrs.push(self);\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tMutexRef::new(self)\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.data.get().as_mut().unwrap_unchecked()\n\t}\n}\n\nimpl\u003cT: Send, R: RawMutex\u003e LockableIntoInner for Mutex\u003cT, R\u003e {\n\ttype Inner = T;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tself.into_inner()\n\t}\n}\n\nimpl\u003cT: Send, R: RawMutex\u003e LockableGetMut for Mutex\u003cT, R\u003e {\n\ttype Inner\u003c'a\u003e\n\t\t= \u0026'a mut T\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tself.get_mut()\n\t}\n}\n\nunsafe impl\u003cT: Send, R: RawMutex\u003e OwnedLockable for Mutex\u003cT, R\u003e {}\n\nimpl\u003cT, R: RawMutex\u003e Mutex\u003cT, R\u003e {\n\t/// Create a new unlocked `Mutex`.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t///\n\t/// let mutex = Mutex::new(0);\n\t/// ```\n\t#[must_use]\n\tpub const fn new(data: T) -\u003e Self {\n\t\tSelf {\n\t\t\traw: R::INIT,\n\t\t\tpoison: PoisonFlag::new(),\n\t\t\tdata: UnsafeCell::new(data),\n\t\t}\n\t}\n\n\t/// Returns the raw underlying mutex.\n\t///\n\t/// Note that you will most likely need to import the [`RawMutex`] trait\n\t/// from `lock_api` to be able to call functions on the raw mutex.\n\t///\n\t/// # Safety\n\t///\n\t/// This method is unsafe because it allows unlocking a mutex while still\n\t/// holding a reference to a [`MutexGuard`], and locking a mutex without\n\t/// holding the [`ThreadKey`].\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\t#[must_use]\n\tpub const unsafe fn raw(\u0026self) -\u003e \u0026R {\n\t\t\u0026self.raw\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug, R: RawMutex\u003e Debug for Mutex\u003cT, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t// safety: this is just a try lock, and the value is dropped\n\t\t// immediately after, so there's no risk of blocking ourselves\n\t\t// or any other threads\n\t\t// when i implement try_clone this code will become less unsafe\n\t\tif let Some(value) = unsafe { self.try_lock_no_key() } {\n\t\t\tf.debug_struct(\"Mutex\").field(\"data\", \u0026\u0026*value).finish()\n\t\t} else {\n\t\t\tstruct LockedPlaceholder;\n\t\t\timpl Debug for LockedPlaceholder {\n\t\t\t\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t\t\t\tf.write_str(\"\u003clocked\u003e\")\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tf.debug_struct(\"Mutex\")\n\t\t\t\t.field(\"data\", \u0026LockedPlaceholder)\n\t\t\t\t.finish()\n\t\t}\n\t}\n}\n\nimpl\u003cT: Default, R: RawMutex\u003e Default for Mutex\u003cT, R\u003e {\n\tfn default() -\u003e Self {\n\t\tSelf::new(T::default())\n\t}\n}\n\nimpl\u003cT, R: RawMutex\u003e From\u003cT\u003e for Mutex\u003cT, R\u003e {\n\tfn from(value: T) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\n// We don't need a `get_mut` because we don't have mutex poisoning. Hurray!\n// We have it anyway for documentation\nimpl\u003cT: ?Sized, R\u003e AsMut\u003cT\u003e for Mutex\u003cT, R\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself.get_mut()\n\t}\n}\n\nimpl\u003cT, R\u003e Mutex\u003cT, R\u003e {\n\t/// Consumes this mutex, returning the underlying data.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t///\n\t/// let mutex = Mutex::new(0);\n\t/// assert_eq!(mutex.into_inner(), 0);\n\t/// ```\n\t#[must_use]\n\tpub fn into_inner(self) -\u003e T {\n\t\tself.data.into_inner()\n\t}\n}\n\nimpl\u003cT: ?Sized, R\u003e Mutex\u003cT, R\u003e {\n\t/// Returns a mutable reference to the underlying data.\n\t///\n\t/// Since this call borrows `Mutex` mutably, no actual locking is taking\n\t/// place. The mutable borrow statically guarantees that no locks exist.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, Mutex};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut mutex = Mutex::new(0);\n\t/// *mutex.get_mut() = 10;\n\t/// assert_eq!(*mutex.lock(key), 10);\n\t/// ```\n\t#[must_use]\n\tpub fn get_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself.data.get_mut()\n\t}\n}\n\nimpl\u003cT, R: RawMutex\u003e Mutex\u003cT, R\u003e {\n\tpub fn scoped_lock\u003c'a, Ret\u003e(\n\t\t\u0026'a self,\n\t\tkey: impl Keyable,\n\t\tf: impl FnOnce(\u0026'a mut T) -\u003e Ret,\n\t) -\u003e Ret {\n\t\tutils::scoped_write(self, key, f)\n\t}\n\n\tpub fn scoped_try_lock\u003c'a, Key: Keyable, Ret\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl FnOnce(\u0026'a mut T) -\u003e Ret,\n\t) -\u003e Result\u003cRet, Key\u003e {\n\t\tutils::scoped_try_write(self, key, f)\n\t}\n\n\t/// Block the thread until this mutex can be locked, and lock it.\n\t///\n\t/// Upon returning, the thread is the only thread with a lock on the\n\t/// `Mutex`. A [`MutexGuard`] is returned to allow a scoped unlock of this\n\t/// `Mutex`. When the guard is dropped, this `Mutex` will unlock.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::{thread, sync::Arc};\n\t/// use happylock::{Mutex, ThreadKey};\n\t///\n\t/// let mutex = Arc::new(Mutex::new(0));\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t///\n\t/// thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// *c_mutex.lock(key) = 10;\n\t/// }).join().expect(\"thread::spawn failed\");\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// assert_eq!(*mutex.lock(key), 10);\n\t/// ```\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e MutexGuard\u003c'_, T, R\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_write();\n\n\t\t\t// safety: we just locked the mutex\n\t\t\tMutexGuard::new(self, key)\n\t\t}\n\t}\n\n\t/// Attempts to lock the `Mutex` without blocking.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// lock when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If the mutex could not be acquired because it is already locked, then\n\t/// this call will return an error containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::{thread, sync::Arc};\n\t/// use happylock::{Mutex, ThreadKey};\n\t///\n\t/// let mutex = Arc::new(Mutex::new(0));\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t///\n\t/// thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut lock = c_mutex.try_lock(key);\n\t/// if let Ok(mut lock) = lock {\n\t/// *lock = 10;\n\t/// } else {\n\t/// println!(\"try_lock failed\");\n\t/// }\n\t/// }).join().expect(\"thread::spawn failed\");\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// assert_eq!(*mutex.lock(key), 10);\n\t/// ```\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e Result\u003cMutexGuard\u003c'_, T, R\u003e, ThreadKey\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the key to the mutex\n\t\t\tif self.raw_try_write() {\n\t\t\t\t// safety: we just locked the mutex\n\t\t\t\tOk(MutexGuard::new(self, key))\n\t\t\t} else {\n\t\t\t\tErr(key)\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Returns `true` if the mutex is currently locked\n\t#[cfg(test)]\n\tpub(crate) fn is_locked(\u0026self) -\u003e bool {\n\t\tself.raw.is_locked()\n\t}\n\n\t/// Lock without a [`ThreadKey`]. It is undefined behavior to do this without\n\t/// owning the [`ThreadKey`].\n\tpub(crate) unsafe fn try_lock_no_key(\u0026self) -\u003e Option\u003cMutexRef\u003c'_, T, R\u003e\u003e {\n\t\tself.raw_try_write().then_some(MutexRef(self, PhantomData))\n\t}\n\n\t/// Consumes the [`MutexGuard`], and consequently unlocks its `Mutex`.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, Mutex};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mutex = Mutex::new(0);\n\t///\n\t/// let mut guard = mutex.lock(key);\n\t/// *guard += 20;\n\t///\n\t/// let key = Mutex::unlock(guard);\n\t/// ```\n\t#[must_use]\n\tpub fn unlock(guard: MutexGuard\u003c'_, T, R\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.mutex);\n\t\tguard.thread_key\n\t}\n}\n\nunsafe impl\u003cR: RawMutex + Send, T: ?Sized + Send\u003e Send for Mutex\u003cT, R\u003e {}\nunsafe impl\u003cR: RawMutex + Sync, T: ?Sized + Send\u003e Sync for Mutex\u003cT, R\u003e {}\n","traces":[{"line":17,"address":[229520,229536,229568],"length":1,"stats":{"Line":5}},{"line":18,"address":[142933,142901],"length":1,"stats":{"Line":6}},{"line":21,"address":[164160,164272],"length":1,"stats":{"Line":10}},{"line":22,"address":[164325,164286,164174,164213],"length":1,"stats":{"Line":11}},{"line":25,"address":[141073],"length":1,"stats":{"Line":11}},{"line":26,"address":[],"length":0,"stats":{"Line":40}},{"line":29,"address":[229296,229136,229216],"length":1,"stats":{"Line":10}},{"line":30,"address":[229150,229310,229230],"length":1,"stats":{"Line":12}},{"line":31,"address":[142696,142776],"length":1,"stats":{"Line":5}},{"line":35,"address":[218241],"length":1,"stats":{"Line":11}},{"line":36,"address":[192917,192837,192885,192965,192832,192880,192912,192848,192960,192853,192944,192949],"length":1,"stats":{"Line":39}},{"line":39,"address":[164032,164000],"length":1,"stats":{"Line":12}},{"line":41,"address":[218316],"length":1,"stats":{"Line":14}},{"line":42,"address":[990965,990757,990821,990933,990752,990997,990720,990928,990880,990981,990992,990848,990816,990853,990885,990784,990917,990725,990912,990976,990789,990944,990960,990949],"length":1,"stats":{"Line":48}},{"line":77,"address":[218544],"length":1,"stats":{"Line":19}},{"line":78,"address":[1285225,1285545,1285289,1285417,1285481,1285353],"length":1,"stats":{"Line":19}},{"line":81,"address":[164384,164400],"length":1,"stats":{"Line":4}},{"line":82,"address":[164389,164405],"length":1,"stats":{"Line":4}},{"line":85,"address":[218496],"length":1,"stats":{"Line":9}},{"line":86,"address":[],"length":0,"stats":{"Line":9}},{"line":93,"address":[1285808,1285792,1285744,1285712,1285760,1285728],"length":1,"stats":{"Line":6}},{"line":94,"address":[],"length":0,"stats":{"Line":6}},{"line":104,"address":[1285888,1285856,1285872],"length":1,"stats":{"Line":3}},{"line":105,"address":[1285861,1285893,1285877],"length":1,"stats":{"Line":3}},{"line":122,"address":[1286678,1286720,1285904,1286846,1286240,1286700,1286096,1286464,1286076,1286446,1286864,1287010,1286256],"length":1,"stats":{"Line":19}},{"line":125,"address":[228875,229019,228726,228827,228678,228971],"length":1,"stats":{"Line":38}},{"line":126,"address":[1286562,1286961,1286371,1286009,1286803,1286190],"length":1,"stats":{"Line":20}},{"line":143,"address":[1287024],"length":1,"stats":{"Line":1}},{"line":144,"address":[],"length":0,"stats":{"Line":0}},{"line":174,"address":[],"length":0,"stats":{"Line":5}},{"line":175,"address":[1287102,1287140,1287053,1287224,1287192],"length":1,"stats":{"Line":5}},{"line":180,"address":[],"length":0,"stats":{"Line":2}},{"line":181,"address":[],"length":0,"stats":{"Line":2}},{"line":188,"address":[],"length":0,"stats":{"Line":1}},{"line":189,"address":[1287349],"length":1,"stats":{"Line":1}},{"line":205,"address":[],"length":0,"stats":{"Line":2}},{"line":206,"address":[],"length":0,"stats":{"Line":6}},{"line":227,"address":[1287616,1287648,1287680],"length":1,"stats":{"Line":3}},{"line":228,"address":[],"length":0,"stats":{"Line":3}},{"line":233,"address":[140560],"length":1,"stats":{"Line":9}},{"line":238,"address":[1287826,1287890,1287954,1287849,1287922,1287730,1287762,1287794],"length":1,"stats":{"Line":9}},{"line":241,"address":[228496,228464,228592,228560,228624,228528],"length":1,"stats":{"Line":18}},{"line":246,"address":[142173,142301,142269,142141,142205,142237],"length":1,"stats":{"Line":18}},{"line":272,"address":[218064,218176,218150],"length":1,"stats":{"Line":6}},{"line":275,"address":[1288110,1287982],"length":1,"stats":{"Line":6}},{"line":278,"address":[],"length":0,"stats":{"Line":6}},{"line":315,"address":[],"length":0,"stats":{"Line":2}},{"line":318,"address":[],"length":0,"stats":{"Line":6}},{"line":320,"address":[1288505,1288475,1288315,1288345],"length":1,"stats":{"Line":4}},{"line":322,"address":[],"length":0,"stats":{"Line":0}},{"line":329,"address":[1288560,1288544],"length":1,"stats":{"Line":2}},{"line":330,"address":[],"length":0,"stats":{"Line":2}},{"line":335,"address":[],"length":0,"stats":{"Line":1}},{"line":336,"address":[],"length":0,"stats":{"Line":1}},{"line":355,"address":[],"length":0,"stats":{"Line":3}},{"line":356,"address":[],"length":0,"stats":{"Line":3}},{"line":357,"address":[],"length":0,"stats":{"Line":0}}],"covered":54,"coverable":57},{"path":["/","home","botahamec","Projects","happylock","src","mutex.rs"],"content":"use std::cell::UnsafeCell;\nuse std::marker::PhantomData;\n\nuse lock_api::RawMutex;\n\nuse crate::poisonable::PoisonFlag;\nuse crate::ThreadKey;\n\nmod guard;\nmod mutex;\n\n/// A spinning mutex\n#[cfg(feature = \"spin\")]\npub type SpinLock\u003cT\u003e = Mutex\u003cT, spin::Mutex\u003c()\u003e\u003e;\n\n/// A parking lot mutex\n#[cfg(feature = \"parking_lot\")]\npub type ParkingMutex\u003cT\u003e = Mutex\u003cT, parking_lot::RawMutex\u003e;\n\n/// A mutual exclusion primitive useful for protecting shared data, which\n/// cannot deadlock.\n///\n/// This mutex will block threads waiting for the lock to become available.\n/// Each mutex has a type parameter which represents the data that it is\n/// protecting. The data can only be accessed through the [`MutexGuard`]s\n/// returned from [`lock`] and [`try_lock`], which guarantees that the data is\n/// only ever accessed when the mutex is locked.\n///\n/// Locking the mutex on a thread that already locked it is impossible, due to\n/// the requirement of the [`ThreadKey`]. Therefore, this will never deadlock.\n///\n/// # Examples\n///\n/// ```\n/// use std::sync::Arc;\n/// use std::thread;\n/// use std::sync::mpsc;\n///\n/// use happylock::{Mutex, ThreadKey};\n///\n/// // Spawn a few threads to increment a shared variable (non-atomically),\n/// // and let the main thread know once all increments are done.\n/// //\n/// // Here we're using an Arc to share memory among threads, and the data\n/// // inside the Arc is protected with a mutex.\n/// const N: usize = 10;\n///\n/// let data = Arc::new(Mutex::new(0));\n///\n/// let (tx, rx) = mpsc::channel();\n/// for _ in 0..N {\n/// let (data, tx) = (Arc::clone(\u0026data), tx.clone());\n/// thread::spawn(move || {\n/// let key = ThreadKey::get().unwrap();\n/// let mut data = data.lock(key);\n/// *data += 1;\n/// if *data == N {\n/// tx.send(()).unwrap();\n/// }\n/// // the lock is unlocked\n/// });\n/// }\n///\n/// rx.recv().unwrap();\n/// ```\n///\n/// To unlock a mutex guard sooner than the end of the enclosing scope, either\n/// create an inner scope, drop the guard manually, or call [`Mutex::unlock`].\n///\n/// ```\n/// use std::sync::Arc;\n/// use std::thread;\n///\n/// use happylock::{Mutex, ThreadKey};\n///\n/// const N: usize = 3;\n///\n/// let data_mutex = Arc::new(Mutex::new(vec![1, 2, 3, 4]));\n/// let res_mutex = Arc::new(Mutex::new(0));\n///\n/// let mut threads = Vec::with_capacity(N);\n/// (0..N).for_each(|_| {\n/// let data_mutex_clone = Arc::clone(\u0026data_mutex);\n/// let res_mutex_clone = Arc::clone(\u0026res_mutex);\n///\n/// threads.push(thread::spawn(move || {\n/// let mut key = ThreadKey::get().unwrap();\n///\n/// // Here we use a block to limit the lifetime of the lock guard.\n/// let result = data_mutex_clone.scoped_lock(\u0026mut key, |data| {\n/// let result = data.iter().fold(0, |acc, x| acc + x * 2);\n/// data.push(result);\n/// result\n/// // The mutex guard gets dropped here, so the lock is released\n/// });\n/// // The thread key is available again\n/// *res_mutex_clone.lock(key) += result;\n/// }));\n/// });\n///\n/// let key = ThreadKey::get().unwrap();\n/// let mut data = data_mutex.lock(key);\n/// let result = data.iter().fold(0, |acc, x| acc + x * 2);\n/// data.push(result);\n///\n/// // We drop the `data` explicitly because it's not necessary anymore. This\n/// // allows other threads to start working on the data immediately. Dropping\n/// // the data also gives us access to the thread key, so we can lock\n/// // another mutex.\n/// let key = Mutex::unlock(data);\n///\n/// // Here the mutex guard is not assigned to a variable and so, even if the\n/// // scope does not end after this line, the mutex is still released: there is\n/// // no deadlock.\n/// *res_mutex.lock(key) += result;\n///\n/// threads.into_iter().for_each(|thread| {\n/// thread\n/// .join()\n/// .expect(\"The thread creating or execution failed !\")\n/// });\n///\n/// let key = ThreadKey::get().unwrap();\n/// assert_eq!(*res_mutex.lock(key), 800);\n/// ```\n///\n/// [`lock`]: `Mutex::lock`\n/// [`try_lock`]: `Mutex::try_lock`\n/// [`ThreadKey`]: `crate::ThreadKey`\npub struct Mutex\u003cT: ?Sized, R\u003e {\n\traw: R,\n\tpoison: PoisonFlag,\n\tdata: UnsafeCell\u003cT\u003e,\n}\n\n/// A reference to a mutex that unlocks it when dropped.\n///\n/// This is similar to [`MutexGuard`], except it does not hold a [`Keyable`].\npub struct MutexRef\u003c'a, T: ?Sized + 'a, R: RawMutex\u003e(\u0026'a Mutex\u003cT, R\u003e, PhantomData\u003cR::GuardMarker\u003e);\n\n/// An RAII implementation of a “scoped lock” of a mutex.\n///\n/// When this structure is dropped (falls out of scope), the lock will be\n/// unlocked.\n///\n/// This is created by calling the [`lock`] and [`try_lock`] methods on [`Mutex`]\n///\n/// [`lock`]: `Mutex::lock`\n/// [`try_lock`]: `Mutex::try_lock`\n//\n// This is the most lifetime-intensive thing I've ever written. Can I graduate\n// from borrow checker university now?\npub struct MutexGuard\u003c'a, T: ?Sized + 'a, R: RawMutex\u003e {\n\tmutex: MutexRef\u003c'a, T, R\u003e, // this way we don't need to re-implement Drop\n\tthread_key: ThreadKey,\n}\n\n#[cfg(test)]\nmod tests {\n\tuse crate::{LockCollection, ThreadKey};\n\n\tuse super::*;\n\n\t#[test]\n\tfn unlocked_when_initialized() {\n\t\tlet lock: crate::Mutex\u003c_\u003e = Mutex::new(\"Hello, world!\");\n\n\t\tassert!(!lock.is_locked());\n\t}\n\n\t#[test]\n\tfn locked_after_read() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::Mutex\u003c_\u003e = Mutex::new(\"Hello, world!\");\n\n\t\tlet guard = lock.lock(key);\n\n\t\tassert!(lock.is_locked());\n\t\tdrop(guard)\n\t}\n\n\t#[test]\n\tfn from_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex: crate::Mutex\u003c_\u003e = Mutex::from(\"Hello, world!\");\n\n\t\tlet guard = mutex.lock(key);\n\t\tassert_eq!(*guard, \"Hello, world!\");\n\t}\n\n\t#[test]\n\tfn as_mut_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mut mutex = crate::Mutex::from(42);\n\n\t\tlet mut_ref = mutex.as_mut();\n\t\t*mut_ref = 24;\n\n\t\tmutex.scoped_lock(key, |guard| assert_eq!(*guard, 24))\n\t}\n\n\t#[test]\n\tfn display_works_for_guard() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex: crate::Mutex\u003c_\u003e = Mutex::new(\"Hello, world!\");\n\t\tlet guard = mutex.lock(key);\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn display_works_for_ref() {\n\t\tlet mutex: crate::Mutex\u003c_\u003e = Mutex::new(\"Hello, world!\");\n\t\tlet guard = unsafe { mutex.try_lock_no_key().unwrap() };\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn ref_as_mut() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = LockCollection::new(crate::Mutex::new(0));\n\t\tlet mut guard = collection.lock(key);\n\t\tlet guard_mut = guard.as_mut().as_mut();\n\n\t\t*guard_mut = 3;\n\t\tlet key = LockCollection::\u003ccrate::Mutex\u003c_\u003e\u003e::unlock(guard);\n\n\t\tlet guard = collection.lock(key);\n\n\t\tassert_eq!(guard.as_ref().as_ref(), \u00263);\n\t}\n\n\t#[test]\n\tfn guard_as_mut() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = crate::Mutex::new(0);\n\t\tlet mut guard = mutex.lock(key);\n\t\tlet guard_mut = guard.as_mut();\n\n\t\t*guard_mut = 3;\n\t\tlet key = Mutex::unlock(guard);\n\n\t\tlet guard = mutex.lock(key);\n\n\t\tassert_eq!(guard.as_ref(), \u00263);\n\t}\n\n\t#[test]\n\tfn dropping_guard_releases_mutex() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex: crate::Mutex\u003c_\u003e = Mutex::new(\"Hello, world!\");\n\n\t\tlet guard = mutex.lock(key);\n\t\tdrop(guard);\n\n\t\tassert!(!mutex.is_locked());\n\t}\n\n\t#[test]\n\tfn dropping_ref_releases_mutex() {\n\t\tlet mutex: crate::Mutex\u003c_\u003e = Mutex::new(\"Hello, world!\");\n\n\t\tlet guard = unsafe { mutex.try_lock_no_key().unwrap() };\n\t\tdrop(guard);\n\n\t\tassert!(!mutex.is_locked());\n\t}\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","src","poisonable","error.rs"],"content":"use core::fmt;\nuse std::error::Error;\n\nuse super::{PoisonError, PoisonGuard, TryLockPoisonableError};\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard\u003e fmt::Debug for PoisonError\u003cGuard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut fmt::Formatter\u003c'_\u003e) -\u003e fmt::Result {\n\t\tf.debug_struct(\"PoisonError\").finish_non_exhaustive()\n\t}\n}\n\nimpl\u003cGuard\u003e fmt::Display for PoisonError\u003cGuard\u003e {\n\t#[cfg_attr(test, mutants::skip)]\n\t#[cfg(not(tarpaulin_include))]\n\tfn fmt(\u0026self, f: \u0026mut fmt::Formatter\u003c'_\u003e) -\u003e fmt::Result {\n\t\t\"poisoned lock: another task failed inside\".fmt(f)\n\t}\n}\n\nimpl\u003cGuard\u003e AsRef\u003cGuard\u003e for PoisonError\u003cGuard\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026Guard {\n\t\tself.get_ref()\n\t}\n}\n\nimpl\u003cGuard\u003e AsMut\u003cGuard\u003e for PoisonError\u003cGuard\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut Guard {\n\t\tself.get_mut()\n\t}\n}\n\nimpl\u003cGuard\u003e Error for PoisonError\u003cGuard\u003e {}\n\nimpl\u003cGuard\u003e PoisonError\u003cGuard\u003e {\n\t/// Creates a `PoisonError`\n\t///\n\t/// This is generally created by methods like [`Poisonable::lock`].\n\t///\n\t/// ```\n\t/// use happylock::poisonable::PoisonError;\n\t///\n\t/// let error = PoisonError::new(\"oh no\");\n\t/// ```\n\t///\n\t/// [`Poisonable::lock`]: `crate::poisonable::Poisonable::lock`\n\t#[must_use]\n\tpub const fn new(guard: Guard) -\u003e Self {\n\t\tSelf { guard }\n\t}\n\n\t/// Consumes the error indicating that a lock is poisonmed, returning the\n\t/// underlying guard to allow access regardless.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::collections::HashSet;\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let mutex = Arc::new(Poisonable::new(Mutex::new(HashSet::new())));\n\t///\n\t/// // poison the mutex\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t/// let _ = thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut data = c_mutex.lock(key).unwrap();\n\t/// data.insert(10);\n\t/// panic!();\n\t/// }).join();\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let p_err = mutex.lock(key).unwrap_err();\n\t/// let data = p_err.into_inner();\n\t/// println!(\"recovered {} items\", data.len());\n\t/// ```\n\t#[must_use]\n\tpub fn into_inner(self) -\u003e Guard {\n\t\tself.guard\n\t}\n\n\t/// Reaches into this error indicating that a lock is poisoned, returning a\n\t/// reference to the underlying guard to allow access regardless.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::collections::HashSet;\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t/// use happylock::poisonable::PoisonGuard;\n\t///\n\t/// let mutex = Arc::new(Poisonable::new(Mutex::new(HashSet::new())));\n\t///\n\t/// // poison the mutex\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t/// let _ = thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut data = c_mutex.lock(key).unwrap();\n\t/// data.insert(10);\n\t/// panic!();\n\t/// }).join();\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let p_err = mutex.lock(key).unwrap_err();\n\t/// let data: \u0026PoisonGuard\u003c_\u003e = p_err.get_ref();\n\t/// println!(\"recovered {} items\", data.len());\n\t/// ```\n\t#[must_use]\n\tpub const fn get_ref(\u0026self) -\u003e \u0026Guard {\n\t\t\u0026self.guard\n\t}\n\n\t/// Reaches into this error indicating that a lock is poisoned, returning a\n\t/// mutable reference to the underlying guard to allow access regardless.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::collections::HashSet;\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let mutex = Arc::new(Poisonable::new(Mutex::new(HashSet::new())));\n\t///\n\t/// // poison the mutex\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t/// let _ = thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut data = c_mutex.lock(key).unwrap();\n\t/// data.insert(10);\n\t/// panic!();\n\t/// }).join();\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut p_err = mutex.lock(key).unwrap_err();\n\t/// let data = p_err.get_mut();\n\t/// data.insert(20);\n\t/// println!(\"recovered {} items\", data.len());\n\t/// ```\n\t#[must_use]\n\tpub fn get_mut(\u0026mut self) -\u003e \u0026mut Guard {\n\t\t\u0026mut self.guard\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cG\u003e fmt::Debug for TryLockPoisonableError\u003c'_, G\u003e {\n\tfn fmt(\u0026self, f: \u0026mut fmt::Formatter\u003c'_\u003e) -\u003e fmt::Result {\n\t\tmatch *self {\n\t\t\tSelf::Poisoned(..) =\u003e \"Poisoned(..)\".fmt(f),\n\t\t\tSelf::WouldBlock(_) =\u003e \"WouldBlock\".fmt(f),\n\t\t}\n\t}\n}\n\nimpl\u003cG\u003e fmt::Display for TryLockPoisonableError\u003c'_, G\u003e {\n\t#[cfg_attr(test, mutants::skip)]\n\t#[cfg(not(tarpaulin_include))]\n\tfn fmt(\u0026self, f: \u0026mut fmt::Formatter\u003c'_\u003e) -\u003e fmt::Result {\n\t\tmatch *self {\n\t\t\tSelf::Poisoned(..) =\u003e \"poisoned lock: another task failed inside\",\n\t\t\tSelf::WouldBlock(_) =\u003e \"try_lock failed because the operation would block\",\n\t\t}\n\t\t.fmt(f)\n\t}\n}\n\nimpl\u003cG\u003e Error for TryLockPoisonableError\u003c'_, G\u003e {}\n\nimpl\u003c'flag, G\u003e From\u003cPoisonError\u003cPoisonGuard\u003c'flag, G\u003e\u003e\u003e for TryLockPoisonableError\u003c'flag, G\u003e {\n\tfn from(value: PoisonError\u003cPoisonGuard\u003c'flag, G\u003e\u003e) -\u003e Self {\n\t\tSelf::Poisoned(value)\n\t}\n}\n","traces":[{"line":23,"address":[862832],"length":1,"stats":{"Line":1}},{"line":24,"address":[],"length":0,"stats":{"Line":1}},{"line":29,"address":[],"length":0,"stats":{"Line":1}},{"line":30,"address":[],"length":0,"stats":{"Line":1}},{"line":49,"address":[],"length":0,"stats":{"Line":9}},{"line":82,"address":[],"length":0,"stats":{"Line":7}},{"line":83,"address":[],"length":0,"stats":{"Line":1}},{"line":116,"address":[],"length":0,"stats":{"Line":4}},{"line":117,"address":[],"length":0,"stats":{"Line":0}},{"line":150,"address":[],"length":0,"stats":{"Line":3}},{"line":151,"address":[],"length":0,"stats":{"Line":0}},{"line":181,"address":[],"length":0,"stats":{"Line":1}},{"line":182,"address":[],"length":0,"stats":{"Line":1}}],"covered":11,"coverable":13},{"path":["/","home","botahamec","Projects","happylock","src","poisonable","flag.rs"],"content":"#[cfg(panic = \"unwind\")]\nuse std::sync::atomic::{AtomicBool, Ordering::Relaxed};\n\nuse super::PoisonFlag;\n\n#[cfg(panic = \"unwind\")]\nimpl PoisonFlag {\n\tpub const fn new() -\u003e Self {\n\t\tSelf(AtomicBool::new(false))\n\t}\n\n\tpub fn is_poisoned(\u0026self) -\u003e bool {\n\t\tself.0.load(Relaxed)\n\t}\n\n\tpub fn clear_poison(\u0026self) {\n\t\tself.0.store(false, Relaxed)\n\t}\n\n\tpub fn poison(\u0026self) {\n\t\tself.0.store(true, Relaxed);\n\t}\n}\n\n#[cfg(not(panic = \"unwind\"))]\nimpl PoisonFlag {\n\tpub const fn new() -\u003e Self {\n\t\tSelf()\n\t}\n\n\t#[mutants::skip] // None of the tests have panic = \"abort\", so this can't be tested\n\t#[cfg(not(tarpaulin_include))]\n\tpub fn is_poisoned(\u0026self) -\u003e bool {\n\t\tfalse\n\t}\n\n\tpub fn clear_poison(\u0026self) {\n\t\t()\n\t}\n\n\tpub fn poison(\u0026self) {\n\t\t()\n\t}\n}\n","traces":[{"line":8,"address":[531984],"length":1,"stats":{"Line":12}},{"line":9,"address":[532417],"length":1,"stats":{"Line":14}},{"line":12,"address":[508448],"length":1,"stats":{"Line":12}},{"line":13,"address":[532041],"length":1,"stats":{"Line":11}},{"line":16,"address":[546784],"length":1,"stats":{"Line":1}},{"line":17,"address":[508489],"length":1,"stats":{"Line":1}},{"line":20,"address":[554944],"length":1,"stats":{"Line":9}},{"line":21,"address":[508521],"length":1,"stats":{"Line":9}}],"covered":8,"coverable":8},{"path":["/","home","botahamec","Projects","happylock","src","poisonable","guard.rs"],"content":"use std::fmt::{Debug, Display};\nuse std::hash::Hash;\nuse std::marker::PhantomData;\nuse std::ops::{Deref, DerefMut};\n\nuse super::{PoisonFlag, PoisonGuard, PoisonRef};\n\nimpl\u003c'a, Guard\u003e PoisonRef\u003c'a, Guard\u003e {\n\t// This is used so that we don't keep accidentally adding the flag reference\n\tpub(super) const fn new(flag: \u0026'a PoisonFlag, guard: Guard) -\u003e Self {\n\t\tSelf {\n\t\t\tguard,\n\t\t\t#[cfg(panic = \"unwind\")]\n\t\t\tflag,\n\t\t\t_phantom: PhantomData,\n\t\t}\n\t}\n}\n\nimpl\u003cGuard\u003e Drop for PoisonRef\u003c'_, Guard\u003e {\n\tfn drop(\u0026mut self) {\n\t\t#[cfg(panic = \"unwind\")]\n\t\tif std::thread::panicking() {\n\t\t\tself.flag.poison();\n\t\t}\n\t}\n}\n\n#[mutants::skip] // hashing involves RNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard: Hash\u003e Hash for PoisonRef\u003c'_, Guard\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.guard.hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard: Debug\u003e Debug for PoisonRef\u003c'_, Guard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cGuard: Display\u003e Display for PoisonRef\u003c'_, Guard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cGuard\u003e Deref for PoisonRef\u003c'_, Guard\u003e {\n\ttype Target = Guard;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t\u0026self.guard\n\t}\n}\n\nimpl\u003cGuard\u003e DerefMut for PoisonRef\u003c'_, Guard\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t\u0026mut self.guard\n\t}\n}\n\nimpl\u003cGuard\u003e AsRef\u003cGuard\u003e for PoisonRef\u003c'_, Guard\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026Guard {\n\t\t\u0026self.guard\n\t}\n}\n\nimpl\u003cGuard\u003e AsMut\u003cGuard\u003e for PoisonRef\u003c'_, Guard\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut Guard {\n\t\t\u0026mut self.guard\n\t}\n}\n\n#[mutants::skip] // hashing involves RNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard: Hash\u003e Hash for PoisonGuard\u003c'_, Guard\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.guard.hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard: Debug\u003e Debug for PoisonGuard\u003c'_, Guard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026self.guard, f)\n\t}\n}\n\nimpl\u003cGuard: Display\u003e Display for PoisonGuard\u003c'_, Guard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026self.guard, f)\n\t}\n}\n\nimpl\u003cT, Guard: Deref\u003cTarget = T\u003e\u003e Deref for PoisonGuard\u003c'_, Guard\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t#[allow(clippy::explicit_auto_deref)] // fixing this results in a compiler error\n\t\t\u0026*self.guard.guard\n\t}\n}\n\nimpl\u003cT, Guard: DerefMut\u003cTarget = T\u003e\u003e DerefMut for PoisonGuard\u003c'_, Guard\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t#[allow(clippy::explicit_auto_deref)] // fixing this results in a compiler error\n\t\t\u0026mut *self.guard.guard\n\t}\n}\n\nimpl\u003cGuard\u003e AsRef\u003cGuard\u003e for PoisonGuard\u003c'_, Guard\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026Guard {\n\t\t\u0026self.guard.guard\n\t}\n}\n\nimpl\u003cGuard\u003e AsMut\u003cGuard\u003e for PoisonGuard\u003c'_, Guard\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut Guard {\n\t\t\u0026mut self.guard.guard\n\t}\n}\n","traces":[{"line":10,"address":[863520,863552,863616,863584],"length":1,"stats":{"Line":4}},{"line":21,"address":[],"length":0,"stats":{"Line":4}},{"line":22,"address":[],"length":0,"stats":{"Line":0}},{"line":23,"address":[],"length":0,"stats":{"Line":4}},{"line":24,"address":[],"length":0,"stats":{"Line":3}},{"line":46,"address":[],"length":0,"stats":{"Line":1}},{"line":47,"address":[],"length":0,"stats":{"Line":1}},{"line":54,"address":[],"length":0,"stats":{"Line":3}},{"line":55,"address":[],"length":0,"stats":{"Line":0}},{"line":60,"address":[],"length":0,"stats":{"Line":2}},{"line":61,"address":[],"length":0,"stats":{"Line":0}},{"line":66,"address":[],"length":0,"stats":{"Line":1}},{"line":67,"address":[],"length":0,"stats":{"Line":0}},{"line":72,"address":[],"length":0,"stats":{"Line":1}},{"line":73,"address":[],"length":0,"stats":{"Line":0}},{"line":94,"address":[],"length":0,"stats":{"Line":1}},{"line":95,"address":[],"length":0,"stats":{"Line":1}},{"line":102,"address":[],"length":0,"stats":{"Line":2}},{"line":104,"address":[],"length":0,"stats":{"Line":2}},{"line":109,"address":[],"length":0,"stats":{"Line":3}},{"line":111,"address":[],"length":0,"stats":{"Line":3}},{"line":116,"address":[],"length":0,"stats":{"Line":1}},{"line":117,"address":[],"length":0,"stats":{"Line":0}},{"line":122,"address":[],"length":0,"stats":{"Line":1}},{"line":123,"address":[],"length":0,"stats":{"Line":0}}],"covered":18,"coverable":25},{"path":["/","home","botahamec","Projects","happylock","src","poisonable","poisonable.rs"],"content":"use std::panic::{RefUnwindSafe, UnwindSafe};\n\nuse crate::handle_unwind::handle_unwind;\nuse crate::lockable::{\n\tLockable, LockableGetMut, LockableIntoInner, OwnedLockable, RawLock, Sharable,\n};\nuse crate::{Keyable, ThreadKey};\n\nuse super::{\n\tPoisonError, PoisonFlag, PoisonGuard, PoisonRef, PoisonResult, Poisonable,\n\tTryLockPoisonableError, TryLockPoisonableResult,\n};\n\nunsafe impl\u003cL: Lockable + RawLock\u003e RawLock for Poisonable\u003cL\u003e {\n\t#[mutants::skip] // this should never run\n\t#[cfg(not(tarpaulin_include))]\n\tfn poison(\u0026self) {\n\t\tself.inner.poison()\n\t}\n\n\tunsafe fn raw_write(\u0026self) {\n\t\tself.inner.raw_write()\n\t}\n\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool {\n\t\tself.inner.raw_try_write()\n\t}\n\n\tunsafe fn raw_unlock_write(\u0026self) {\n\t\tself.inner.raw_unlock_write()\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tself.inner.raw_read()\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tself.inner.raw_try_read()\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tself.inner.raw_unlock_read()\n\t}\n}\n\nunsafe impl\u003cL: Lockable\u003e Lockable for Poisonable\u003cL\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= PoisonResult\u003cPoisonRef\u003c'g, L::Guard\u003c'g\u003e\u003e\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= PoisonResult\u003cL::DataMut\u003c'a\u003e\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tself.inner.get_ptrs(ptrs)\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tlet ref_guard = PoisonRef::new(\u0026self.poisoned, self.inner.guard());\n\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(ref_guard))\n\t\t} else {\n\t\t\tOk(ref_guard)\n\t\t}\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(self.inner.data_mut()))\n\t\t} else {\n\t\t\tOk(self.inner.data_mut())\n\t\t}\n\t}\n}\n\nunsafe impl\u003cL: Sharable\u003e Sharable for Poisonable\u003cL\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= PoisonResult\u003cPoisonRef\u003c'g, L::ReadGuard\u003c'g\u003e\u003e\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= PoisonResult\u003cL::DataRef\u003c'a\u003e\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tlet ref_guard = PoisonRef::new(\u0026self.poisoned, self.inner.read_guard());\n\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(ref_guard))\n\t\t} else {\n\t\t\tOk(ref_guard)\n\t\t}\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(self.inner.data_ref()))\n\t\t} else {\n\t\t\tOk(self.inner.data_ref())\n\t\t}\n\t}\n}\n\nunsafe impl\u003cL: OwnedLockable\u003e OwnedLockable for Poisonable\u003cL\u003e {}\n\n// AsMut won't work here because we don't strictly return a \u0026mut T\n// LockableGetMut is the next best thing\nimpl\u003cL: LockableGetMut\u003e LockableGetMut for Poisonable\u003cL\u003e {\n\ttype Inner\u003c'a\u003e\n\t\t= PoisonResult\u003cL::Inner\u003c'a\u003e\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(self.inner.get_mut()))\n\t\t} else {\n\t\t\tOk(self.inner.get_mut())\n\t\t}\n\t}\n}\n\nimpl\u003cL: LockableIntoInner\u003e LockableIntoInner for Poisonable\u003cL\u003e {\n\ttype Inner = PoisonResult\u003cL::Inner\u003e;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(self.inner.into_inner()))\n\t\t} else {\n\t\t\tOk(self.inner.into_inner())\n\t\t}\n\t}\n}\n\nimpl\u003cL\u003e From\u003cL\u003e for Poisonable\u003cL\u003e {\n\tfn from(value: L) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\nimpl\u003cL\u003e Poisonable\u003cL\u003e {\n\t/// Creates a new `Poisonable`\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, Poisonable};\n\t///\n\t/// let mutex = Poisonable::new(Mutex::new(0));\n\t/// ```\n\tpub const fn new(value: L) -\u003e Self {\n\t\tSelf {\n\t\t\tinner: value,\n\t\t\tpoisoned: PoisonFlag::new(),\n\t\t}\n\t}\n\n\t/// Determines whether the mutex is poisoned.\n\t///\n\t/// If another thread is active, the mutex can still become poisoned at any\n\t/// time. You should not trust a `false` value for program correctness\n\t/// without additional synchronization.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let mutex = Arc::new(Poisonable::new(Mutex::new(0)));\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t///\n\t/// let _ = thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let _lock = c_mutex.lock(key).unwrap();\n\t/// panic!(); // the mutex gets poisoned\n\t/// }).join();\n\t///\n\t/// assert_eq!(mutex.is_poisoned(), true);\n\t/// ```\n\tpub fn is_poisoned(\u0026self) -\u003e bool {\n\t\tself.poisoned.is_poisoned()\n\t}\n\n\t/// Clear the poisoned state from a lock.\n\t///\n\t/// If the lock is poisoned, it will remain poisoned until this function\n\t/// is called. This allows recovering from a poisoned state and marking\n\t/// that it has recovered. For example, if the value is overwritten by a\n\t/// known-good value, then the lock can be marked as un-poisoned. Or\n\t/// possibly, the value could by inspected to determine if it is in a\n\t/// consistent state, and if so the poison is removed.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let mutex = Arc::new(Poisonable::new(Mutex::new(0)));\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t///\n\t/// let _ = thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let _lock = c_mutex.lock(key).unwrap();\n\t/// panic!(); // the mutex gets poisoned\n\t/// }).join();\n\t///\n\t/// assert_eq!(mutex.is_poisoned(), true);\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let x = mutex.lock(key).unwrap_or_else(|mut e| {\n\t/// **e.get_mut() = 1;\n\t/// mutex.clear_poison();\n\t/// e.into_inner()\n\t/// });\n\t///\n\t/// assert_eq!(mutex.is_poisoned(), false);\n\t/// assert_eq!(*x, 1);\n\t/// ```\n\tpub fn clear_poison(\u0026self) {\n\t\tself.poisoned.clear_poison()\n\t}\n\n\t/// Consumes this `Poisonable`, returning the underlying lock.\n\t///\n\t/// This consumes the `Poisonable` and returns ownership of the lock, which\n\t/// means that the `Poisonable` can still be `RefUnwindSafe`.\n\t///\n\t/// # Errors\n\t///\n\t/// If another user of this lock panicked while holding the lock, then this\n\t/// call will return an error instead.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, Poisonable};\n\t///\n\t/// let mutex = Poisonable::new(Mutex::new(0));\n\t/// assert_eq!(mutex.into_child().unwrap().into_inner(), 0);\n\t/// ```\n\tpub fn into_child(self) -\u003e PoisonResult\u003cL\u003e {\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(self.inner))\n\t\t} else {\n\t\t\tOk(self.inner)\n\t\t}\n\t}\n\n\t/// Returns a mutable reference to the underlying lock.\n\t///\n\t/// This can be implemented while still being `RefUnwindSafe` because\n\t/// it requires a mutable reference.\n\t///\n\t/// # Errors\n\t///\n\t/// If another user of this lock panicked while holding the lock, then\n\t/// this call will return an error instead.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut mutex = Poisonable::new(Mutex::new(0));\n\t/// *mutex.child_mut().unwrap().as_mut() = 10;\n\t/// assert_eq!(*mutex.lock(key).unwrap(), 10);\n\t/// ```\n\tpub fn child_mut(\u0026mut self) -\u003e PoisonResult\u003c\u0026mut L\u003e {\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(\u0026mut self.inner))\n\t\t} else {\n\t\t\tOk(\u0026mut self.inner)\n\t\t}\n\t}\n\n\t// NOTE: `child_ref` isn't implemented because it would make this not `RefUnwindSafe`\n\t//\n}\n\nimpl\u003cL: Lockable\u003e Poisonable\u003cL\u003e {\n\t/// Creates a guard for the poisonable, without locking it\n\tunsafe fn guard(\u0026self, key: ThreadKey) -\u003e PoisonResult\u003cPoisonGuard\u003c'_, L::Guard\u003c'_\u003e\u003e\u003e {\n\t\tlet guard = PoisonGuard {\n\t\t\tguard: PoisonRef::new(\u0026self.poisoned, self.inner.guard()),\n\t\t\tkey,\n\t\t};\n\n\t\tif self.is_poisoned() {\n\t\t\treturn Err(PoisonError::new(guard));\n\t\t}\n\n\t\tOk(guard)\n\t}\n}\n\nimpl\u003cL: Lockable + RawLock\u003e Poisonable\u003cL\u003e {\n\tpub fn scoped_lock\u003c'a, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: impl Keyable,\n\t\tf: impl Fn(\u003cSelf as Lockable\u003e::DataMut\u003c'a\u003e) -\u003e R,\n\t) -\u003e R {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_write();\n\n\t\t\t// safety: the data was just locked\n\t\t\tlet r = handle_unwind(\n\t\t\t\t|| f(self.data_mut()),\n\t\t\t\t|| {\n\t\t\t\t\tself.poisoned.poison();\n\t\t\t\t\tself.raw_unlock_write();\n\t\t\t\t},\n\t\t\t);\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock_write();\n\n\t\t\tdrop(key); // ensure the key stays alive long enough\n\n\t\t\tr\n\t\t}\n\t}\n\n\tpub fn scoped_try_lock\u003c'a, Key: Keyable, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(\u003cSelf as Lockable\u003e::DataMut\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_write() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we just locked the collection\n\t\t\tlet r = handle_unwind(\n\t\t\t\t|| f(self.data_mut()),\n\t\t\t\t|| {\n\t\t\t\t\tself.poisoned.poison();\n\t\t\t\t\tself.raw_unlock_write();\n\t\t\t\t},\n\t\t\t);\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock_write();\n\n\t\t\tdrop(key); // ensures the key stays valid long enough\n\n\t\t\tOk(r)\n\t\t}\n\t}\n\n\t/// Acquires the lock, blocking the current thread until it is ok to do so.\n\t///\n\t/// This function will block the current thread until it is available to\n\t/// acquire the mutex. Upon returning, the thread is the only thread with\n\t/// the lock held. An RAII guard is returned to allow scoped unlock of the\n\t/// lock. When the guard goes out of scope, the mutex will be unlocked.\n\t///\n\t/// # Errors\n\t///\n\t/// If another use of this mutex panicked while holding the mutex, then\n\t/// this call will return an error once the mutex is acquired.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let mutex = Arc::new(Poisonable::new(Mutex::new(0)));\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t///\n\t/// thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// *c_mutex.lock(key).unwrap() = 10;\n\t/// }).join().expect(\"thread::spawn failed\");\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// assert_eq!(*mutex.lock(key).unwrap(), 10);\n\t/// ```\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e PoisonResult\u003cPoisonGuard\u003c'_, L::Guard\u003c'_\u003e\u003e\u003e {\n\t\tunsafe {\n\t\t\tself.inner.raw_write();\n\t\t\tself.guard(key)\n\t\t}\n\t}\n\n\t/// Attempts to acquire this lock.\n\t///\n\t/// If the lock could not be acquired at this time, then [`Err`] is\n\t/// returned. Otherwise, an RAII guard is returned. The lock will be\n\t/// unlocked when the guard is dropped.\n\t///\n\t/// This function does not block.\n\t///\n\t/// # Errors\n\t///\n\t/// If another user of this mutex panicked while holding the mutex, then\n\t/// this call will return the [`Poisoned`] error if the mutex would\n\t/// otherwise be acquired.\n\t///\n\t/// If the mutex could not be acquired because it is already locked, then\n\t/// this call will return the [`WouldBlock`] error.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let mutex = Arc::new(Poisonable::new(Mutex::new(0)));\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t///\n\t/// thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut lock = c_mutex.try_lock(key);\n\t/// if let Ok(mut mutex) = lock {\n\t/// *mutex = 10;\n\t/// } else {\n\t/// println!(\"try_lock failed\");\n\t/// }\n\t/// }).join().expect(\"thread::spawn failed\");\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// assert_eq!(*mutex.lock(key).unwrap(), 10);\n\t/// ```\n\t///\n\t/// [`Poisoned`]: `TryLockPoisonableError::Poisoned`\n\t/// [`WouldBlock`]: `TryLockPoisonableError::WouldBlock`\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e TryLockPoisonableResult\u003c'_, L::Guard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\tif self.inner.raw_try_write() {\n\t\t\t\tOk(self.guard(key)?)\n\t\t\t} else {\n\t\t\t\tErr(TryLockPoisonableError::WouldBlock(key))\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Consumes the [`PoisonGuard`], and consequently unlocks its `Poisonable`.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, Mutex, Poisonable};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mutex = Poisonable::new(Mutex::new(0));\n\t///\n\t/// let mut guard = mutex.lock(key).unwrap();\n\t/// *guard += 20;\n\t///\n\t/// let key = Poisonable::\u003cMutex\u003c_\u003e\u003e::unlock(guard);\n\t/// ```\n\tpub fn unlock\u003c'flag\u003e(guard: PoisonGuard\u003c'flag, L::Guard\u003c'flag\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: Sharable + RawLock\u003e Poisonable\u003cL\u003e {\n\tunsafe fn read_guard(\u0026self, key: ThreadKey) -\u003e PoisonResult\u003cPoisonGuard\u003c'_, L::ReadGuard\u003c'_\u003e\u003e\u003e {\n\t\tlet guard = PoisonGuard {\n\t\t\tguard: PoisonRef::new(\u0026self.poisoned, self.inner.read_guard()),\n\t\t\tkey,\n\t\t};\n\n\t\tif self.is_poisoned() {\n\t\t\treturn Err(PoisonError::new(guard));\n\t\t}\n\n\t\tOk(guard)\n\t}\n\n\tpub fn scoped_read\u003c'a, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: impl Keyable,\n\t\tf: impl Fn(\u003cSelf as Sharable\u003e::DataRef\u003c'a\u003e) -\u003e R,\n\t) -\u003e R {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_read();\n\n\t\t\t// safety: the data was just locked\n\t\t\tlet r = handle_unwind(\n\t\t\t\t|| f(self.data_ref()),\n\t\t\t\t|| {\n\t\t\t\t\tself.poisoned.poison();\n\t\t\t\t\tself.raw_unlock_read();\n\t\t\t\t},\n\t\t\t);\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock_read();\n\n\t\t\tdrop(key); // ensure the key stays alive long enough\n\n\t\t\tr\n\t\t}\n\t}\n\n\tpub fn scoped_try_read\u003c'a, Key: Keyable, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(\u003cSelf as Sharable\u003e::DataRef\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_read() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we just locked the collection\n\t\t\tlet r = handle_unwind(\n\t\t\t\t|| f(self.data_ref()),\n\t\t\t\t|| {\n\t\t\t\t\tself.poisoned.poison();\n\t\t\t\t\tself.raw_unlock_read();\n\t\t\t\t},\n\t\t\t);\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock_read();\n\n\t\t\tdrop(key); // ensures the key stays valid long enough\n\n\t\t\tOk(r)\n\t\t}\n\t}\n\n\t/// Locks with shared read access, blocking the current thread until it can\n\t/// be acquired.\n\t///\n\t/// This function will block the current thread until there are no writers\n\t/// which hold the lock. This method does not provide any guarantee with\n\t/// respect to the ordering of contentious readers or writers will acquire\n\t/// the lock.\n\t///\n\t/// # Errors\n\t///\n\t/// If another use of this lock panicked while holding the lock, then\n\t/// this call will return an error once the lock is acquired.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t///\n\t/// use happylock::{RwLock, Poisonable, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = Arc::new(Poisonable::new(RwLock::new(0)));\n\t/// let c_lock = Arc::clone(\u0026lock);\n\t///\n\t/// let n = lock.read(key).unwrap();\n\t/// assert_eq!(*n, 0);\n\t///\n\t/// thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// assert!(c_lock.read(key).is_ok());\n\t/// }).join().expect(\"thread::spawn failed\");\n\t/// ```\n\tpub fn read(\u0026self, key: ThreadKey) -\u003e PoisonResult\u003cPoisonGuard\u003c'_, L::ReadGuard\u003c'_\u003e\u003e\u003e {\n\t\tunsafe {\n\t\t\tself.inner.raw_read();\n\t\t\tself.read_guard(key)\n\t\t}\n\t}\n\n\t/// Attempts to acquire the lock with shared read access.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is returned.\n\t/// Otherwise, an RAII guard is returned which will release the shared access\n\t/// when it is dropped.\n\t///\n\t/// This function does not block.\n\t///\n\t/// This function does not provide any guarantees with respect to the ordering\n\t/// of whether contentious readers or writers will acquire the lock first.\n\t///\n\t/// # Errors\n\t///\n\t/// This function will return the [`Poisoned`] error if the lock is\n\t/// poisoned. A [`Poisonable`] is poisoned whenever a writer panics while\n\t/// holding an exclusive lock. `Poisoned` will only be returned if the lock\n\t/// would have otherwise been acquired.\n\t///\n\t/// This function will return the [`WouldBlock`] error if the lock could\n\t/// not be acquired because it was already locked exclusively.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Poisonable, RwLock, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = Poisonable::new(RwLock::new(1));\n\t///\n\t/// match lock.try_read(key) {\n\t/// Ok(n) =\u003e assert_eq!(*n, 1),\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t/// ```\n\t///\n\t/// [`Poisoned`]: `TryLockPoisonableError::Poisoned`\n\t/// [`WouldBlock`]: `TryLockPoisonableError::WouldBlock`\n\tpub fn try_read(\u0026self, key: ThreadKey) -\u003e TryLockPoisonableResult\u003c'_, L::ReadGuard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\tif self.inner.raw_try_read() {\n\t\t\t\tOk(self.read_guard(key)?)\n\t\t\t} else {\n\t\t\t\tErr(TryLockPoisonableError::WouldBlock(key))\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Consumes the [`PoisonGuard`], and consequently unlocks its underlying lock.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, RwLock, Poisonable};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = Poisonable::new(RwLock::new(0));\n\t///\n\t/// let mut guard = lock.read(key).unwrap();\n\t/// let key = Poisonable::\u003cRwLock\u003c_\u003e\u003e::unlock_read(guard);\n\t/// ```\n\tpub fn unlock_read\u003c'flag\u003e(guard: PoisonGuard\u003c'flag, L::ReadGuard\u003c'flag\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: LockableIntoInner\u003e Poisonable\u003cL\u003e {\n\t/// Consumes this `Poisonable`, returning the underlying data.\n\t///\n\t/// # Errors\n\t///\n\t/// If another user of this lock panicked while holding the lock, then this\n\t/// call will return an error instead.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, Poisonable};\n\t///\n\t/// let mutex = Poisonable::new(Mutex::new(0));\n\t/// assert_eq!(mutex.into_inner().unwrap(), 0);\n\t/// ```\n\tpub fn into_inner(self) -\u003e PoisonResult\u003cL::Inner\u003e {\n\t\tLockableIntoInner::into_inner(self)\n\t}\n}\n\nimpl\u003cL: LockableGetMut + RawLock\u003e Poisonable\u003cL\u003e {\n\t/// Returns a mutable reference to the underlying data.\n\t///\n\t/// Since this call borrows the `Poisonable` mutably, no actual locking\n\t/// needs to take place - the mutable borrow statically guarantees no locks\n\t/// exist.\n\t///\n\t/// # Errors\n\t///\n\t/// If another user of this lock panicked while holding the lock, then\n\t/// this call will return an error instead.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut mutex = Poisonable::new(Mutex::new(0));\n\t/// *mutex.get_mut().unwrap() = 10;\n\t/// assert_eq!(*mutex.lock(key).unwrap(), 10);\n\t/// ```\n\tpub fn get_mut(\u0026mut self) -\u003e PoisonResult\u003cL::Inner\u003c'_\u003e\u003e {\n\t\tLockableGetMut::get_mut(self)\n\t}\n}\n\nimpl\u003cL: UnwindSafe\u003e RefUnwindSafe for Poisonable\u003cL\u003e {}\nimpl\u003cL: UnwindSafe\u003e UnwindSafe for Poisonable\u003cL\u003e {}\n","traces":[{"line":21,"address":[],"length":0,"stats":{"Line":1}},{"line":22,"address":[863957],"length":1,"stats":{"Line":1}},{"line":25,"address":[],"length":0,"stats":{"Line":2}},{"line":26,"address":[],"length":0,"stats":{"Line":2}},{"line":29,"address":[],"length":0,"stats":{"Line":2}},{"line":30,"address":[864021,864005],"length":1,"stats":{"Line":2}},{"line":33,"address":[],"length":0,"stats":{"Line":1}},{"line":34,"address":[],"length":0,"stats":{"Line":1}},{"line":37,"address":[],"length":0,"stats":{"Line":1}},{"line":38,"address":[],"length":0,"stats":{"Line":1}},{"line":41,"address":[],"length":0,"stats":{"Line":1}},{"line":42,"address":[],"length":0,"stats":{"Line":1}},{"line":57,"address":[],"length":0,"stats":{"Line":4}},{"line":58,"address":[],"length":0,"stats":{"Line":4}},{"line":61,"address":[],"length":0,"stats":{"Line":2}},{"line":62,"address":[],"length":0,"stats":{"Line":2}},{"line":64,"address":[],"length":0,"stats":{"Line":7}},{"line":65,"address":[864413,864701,864641,864353],"length":1,"stats":{"Line":2}},{"line":67,"address":[],"length":0,"stats":{"Line":2}},{"line":71,"address":[],"length":0,"stats":{"Line":2}},{"line":72,"address":[],"length":0,"stats":{"Line":4}},{"line":73,"address":[864902,864806],"length":1,"stats":{"Line":1}},{"line":75,"address":[],"length":0,"stats":{"Line":2}},{"line":91,"address":[865220,864944,865198],"length":1,"stats":{"Line":1}},{"line":92,"address":[],"length":0,"stats":{"Line":1}},{"line":94,"address":[],"length":0,"stats":{"Line":4}},{"line":95,"address":[],"length":0,"stats":{"Line":2}},{"line":97,"address":[865089],"length":1,"stats":{"Line":1}},{"line":101,"address":[],"length":0,"stats":{"Line":1}},{"line":102,"address":[],"length":0,"stats":{"Line":2}},{"line":103,"address":[],"length":0,"stats":{"Line":1}},{"line":105,"address":[],"length":0,"stats":{"Line":1}},{"line":120,"address":[],"length":0,"stats":{"Line":1}},{"line":121,"address":[],"length":0,"stats":{"Line":2}},{"line":122,"address":[865382],"length":1,"stats":{"Line":1}},{"line":124,"address":[],"length":0,"stats":{"Line":1}},{"line":132,"address":[865424,865739],"length":1,"stats":{"Line":1}},{"line":133,"address":[],"length":0,"stats":{"Line":3}},{"line":134,"address":[],"length":0,"stats":{"Line":2}},{"line":136,"address":[],"length":0,"stats":{"Line":2}},{"line":142,"address":[],"length":0,"stats":{"Line":1}},{"line":143,"address":[],"length":0,"stats":{"Line":2}},{"line":157,"address":[],"length":0,"stats":{"Line":3}},{"line":160,"address":[],"length":0,"stats":{"Line":6}},{"line":189,"address":[],"length":0,"stats":{"Line":4}},{"line":190,"address":[866197,866229,866261],"length":1,"stats":{"Line":4}},{"line":231,"address":[],"length":0,"stats":{"Line":3}},{"line":232,"address":[866293,866309,866325],"length":1,"stats":{"Line":3}},{"line":253,"address":[],"length":0,"stats":{"Line":1}},{"line":254,"address":[],"length":0,"stats":{"Line":4}},{"line":255,"address":[],"length":0,"stats":{"Line":2}},{"line":257,"address":[866442],"length":1,"stats":{"Line":1}},{"line":281,"address":[],"length":0,"stats":{"Line":0}},{"line":282,"address":[],"length":0,"stats":{"Line":0}},{"line":283,"address":[],"length":0,"stats":{"Line":0}},{"line":285,"address":[],"length":0,"stats":{"Line":0}},{"line":295,"address":[],"length":0,"stats":{"Line":3}},{"line":297,"address":[867162,867090,867578,866746,867506,866674],"length":1,"stats":{"Line":6}},{"line":301,"address":[867630,866848,867264,866798,867214,867680],"length":1,"stats":{"Line":7}},{"line":302,"address":[866956,866896,867788,867372,867728,867312],"length":1,"stats":{"Line":6}},{"line":305,"address":[866859,867691,867275],"length":1,"stats":{"Line":4}},{"line":310,"address":[868002,867872,868173,868195,868048,868024],"length":1,"stats":{"Line":3}},{"line":317,"address":[],"length":0,"stats":{"Line":2}},{"line":321,"address":[],"length":0,"stats":{"Line":4}},{"line":322,"address":[824768,824800],"length":1,"stats":{"Line":1}},{"line":323,"address":[824805,824773],"length":1,"stats":{"Line":1}},{"line":324,"address":[824786,824818],"length":1,"stats":{"Line":1}},{"line":329,"address":[],"length":0,"stats":{"Line":1}},{"line":331,"address":[868147,867976],"length":1,"stats":{"Line":1}},{"line":333,"address":[],"length":0,"stats":{"Line":0}},{"line":337,"address":[868416,868392,868578,868208,868370,868600],"length":1,"stats":{"Line":2}},{"line":344,"address":[868265,868430,868222,868473],"length":1,"stats":{"Line":4}},{"line":345,"address":[],"length":0,"stats":{"Line":1}},{"line":350,"address":[],"length":0,"stats":{"Line":2}},{"line":351,"address":[824992,824960],"length":1,"stats":{"Line":0}},{"line":352,"address":[824997,824965],"length":1,"stats":{"Line":0}},{"line":353,"address":[],"length":0,"stats":{"Line":0}},{"line":358,"address":[],"length":0,"stats":{"Line":1}},{"line":360,"address":[868344,868552],"length":1,"stats":{"Line":1}},{"line":362,"address":[868564,868356],"length":1,"stats":{"Line":1}},{"line":397,"address":[868912,868768,868730,869018,869040,868624,868896,868752,868874],"length":1,"stats":{"Line":3}},{"line":399,"address":[],"length":0,"stats":{"Line":3}},{"line":400,"address":[868708,868852,868996],"length":1,"stats":{"Line":3}},{"line":448,"address":[],"length":0,"stats":{"Line":0}},{"line":450,"address":[],"length":0,"stats":{"Line":0}},{"line":451,"address":[],"length":0,"stats":{"Line":0}},{"line":453,"address":[],"length":0,"stats":{"Line":0}},{"line":473,"address":[],"length":0,"stats":{"Line":1}},{"line":474,"address":[869070],"length":1,"stats":{"Line":1}},{"line":475,"address":[],"length":0,"stats":{"Line":0}},{"line":480,"address":[869152,869521],"length":1,"stats":{"Line":1}},{"line":482,"address":[869274,869202],"length":1,"stats":{"Line":2}},{"line":486,"address":[869326,869376],"length":1,"stats":{"Line":2}},{"line":487,"address":[],"length":0,"stats":{"Line":0}},{"line":490,"address":[869387],"length":1,"stats":{"Line":1}},{"line":493,"address":[869880,869858,869728,869693,869568,869715],"length":1,"stats":{"Line":3}},{"line":500,"address":[869747,869582],"length":1,"stats":{"Line":2}},{"line":504,"address":[],"length":0,"stats":{"Line":4}},{"line":505,"address":[],"length":0,"stats":{"Line":1}},{"line":506,"address":[],"length":0,"stats":{"Line":1}},{"line":507,"address":[825170,825202],"length":1,"stats":{"Line":1}},{"line":512,"address":[],"length":0,"stats":{"Line":1}},{"line":514,"address":[869667,869832],"length":1,"stats":{"Line":1}},{"line":516,"address":[],"length":0,"stats":{"Line":0}},{"line":520,"address":[870296,870274,870112,869904,870066,870088],"length":1,"stats":{"Line":2}},{"line":527,"address":[870126,870169,869918,869961],"length":1,"stats":{"Line":4}},{"line":528,"address":[],"length":0,"stats":{"Line":1}},{"line":533,"address":[],"length":0,"stats":{"Line":2}},{"line":534,"address":[],"length":0,"stats":{"Line":0}},{"line":535,"address":[825381,825349],"length":1,"stats":{"Line":0}},{"line":536,"address":[],"length":0,"stats":{"Line":0}},{"line":541,"address":[],"length":0,"stats":{"Line":1}},{"line":543,"address":[870040,870248],"length":1,"stats":{"Line":1}},{"line":545,"address":[],"length":0,"stats":{"Line":1}},{"line":582,"address":[],"length":0,"stats":{"Line":1}},{"line":584,"address":[],"length":0,"stats":{"Line":1}},{"line":585,"address":[],"length":0,"stats":{"Line":1}},{"line":626,"address":[],"length":0,"stats":{"Line":0}},{"line":628,"address":[],"length":0,"stats":{"Line":0}},{"line":629,"address":[],"length":0,"stats":{"Line":0}},{"line":631,"address":[],"length":0,"stats":{"Line":0}},{"line":649,"address":[],"length":0,"stats":{"Line":0}},{"line":650,"address":[],"length":0,"stats":{"Line":0}},{"line":651,"address":[],"length":0,"stats":{"Line":0}},{"line":671,"address":[],"length":0,"stats":{"Line":1}},{"line":672,"address":[],"length":0,"stats":{"Line":1}},{"line":698,"address":[],"length":0,"stats":{"Line":1}},{"line":699,"address":[],"length":0,"stats":{"Line":1}}],"covered":103,"coverable":128},{"path":["/","home","botahamec","Projects","happylock","src","poisonable.rs"],"content":"use std::marker::PhantomData;\nuse std::sync::atomic::AtomicBool;\n\nuse crate::ThreadKey;\n\nmod error;\nmod flag;\nmod guard;\nmod poisonable;\n\n/// A flag indicating if a lock is poisoned or not. The implementation differs\n/// depending on whether panics are set to unwind or abort.\n#[derive(Debug, Default)]\npub(crate) struct PoisonFlag(#[cfg(panic = \"unwind\")] AtomicBool);\n\n/// A wrapper around [`Lockable`] types which will enable poisoning.\n///\n/// A lock is \"poisoned\" when the thread panics while holding the lock. Once a\n/// lock is poisoned, all other threads are unable to access the data by\n/// default, because the data may be tainted (some invariant of the data might\n/// not be upheld).\n///\n/// The [`lock`] and [`try_lock`] methods return a [`Result`] which indicates\n/// whether the lock has been poisoned or not. The [`PoisonError`] type has an\n/// [`into_inner`] method which will return the guard that normally would have\n/// been returned for a successful lock. This allows access to the data,\n/// despite the lock being poisoned.\n///\n/// Alternatively, there is also a [`clear_poison`] method, which should\n/// indicate that all invariants of the underlying data are upheld, so that\n/// subsequent calls may still return [`Ok`].\n///\n/// [`Lockable`]: `crate::lockable::Lockable`\n/// [`lock`]: `Poisonable::lock`\n/// [`try_lock`]: `Poisonable::try_lock`\n/// [`into_inner`]: `PoisonError::into_inner`\n/// [`clear_poison`]: `Poisonable::clear_poison`\n#[derive(Debug, Default)]\npub struct Poisonable\u003cL\u003e {\n\tinner: L,\n\tpoisoned: PoisonFlag,\n}\n\n/// An RAII guard for a [`Poisonable`].\n///\n/// This is similar to a [`PoisonGuard`], except that it does not hold a\n/// [`Keyable`]\n///\n/// [`Keyable`]: `crate::Keyable`\npub struct PoisonRef\u003c'a, G\u003e {\n\tguard: G,\n\t#[cfg(panic = \"unwind\")]\n\tflag: \u0026'a PoisonFlag,\n\t_phantom: PhantomData\u003c\u0026'a ()\u003e,\n}\n\n/// An RAII guard for a [`Poisonable`].\n///\n/// This is created by calling methods like [`Poisonable::lock`].\npub struct PoisonGuard\u003c'a, G\u003e {\n\tguard: PoisonRef\u003c'a, G\u003e,\n\tkey: ThreadKey,\n}\n\n/// A type of error which can be returned when acquiring a [`Poisonable`] lock.\npub struct PoisonError\u003cGuard\u003e {\n\tguard: Guard,\n}\n\n/// An enumeration of possible errors associated with\n/// [`TryLockPoisonableResult`] which can occur while trying to acquire a lock\n/// (i.e.: [`Poisonable::try_lock`]).\npub enum TryLockPoisonableError\u003c'flag, G\u003e {\n\tPoisoned(PoisonError\u003cPoisonGuard\u003c'flag, G\u003e\u003e),\n\tWouldBlock(ThreadKey),\n}\n\n/// A type alias for the result of a lock method which can poisoned.\n///\n/// The [`Ok`] variant of this result indicates that the primitive was not\n/// poisoned, and the primitive was poisoned. Note that the [`Err`] variant\n/// *also* carries the associated guard, and it can be acquired through the\n/// [`into_inner`] method.\n///\n/// [`into_inner`]: `PoisonError::into_inner`\npub type PoisonResult\u003cGuard\u003e = Result\u003cGuard, PoisonError\u003cGuard\u003e\u003e;\n\n/// A type alias for the result of a nonblocking locking method.\n///\n/// For more information, see [`PoisonResult`]. A `TryLockPoisonableResult`\n/// doesn't necessarily hold the associated guard in the [`Err`] type as the\n/// lock might not have been acquired for other reasons.\npub type TryLockPoisonableResult\u003c'flag, G\u003e =\n\tResult\u003cPoisonGuard\u003c'flag, G\u003e, TryLockPoisonableError\u003c'flag, G\u003e\u003e;\n\n#[cfg(test)]\nmod tests {\n\tuse std::sync::Arc;\n\n\tuse super::*;\n\tuse crate::lockable::Lockable;\n\tuse crate::{LockCollection, Mutex, RwLock, ThreadKey};\n\n\t#[test]\n\tfn locking_poisoned_mutex_returns_error_in_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = LockCollection::new(Poisonable::new(Mutex::new(42)));\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet mut guard1 = mutex.lock(key);\n\t\t\t\tlet guard = guard1.as_deref_mut().unwrap();\n\t\t\t\tassert_eq!(**guard, 42);\n\t\t\t\tpanic!();\n\n\t\t\t\t#[allow(unreachable_code)]\n\t\t\t\tdrop(guard1);\n\t\t\t})\n\t\t\t.join()\n\t\t\t.unwrap_err();\n\t\t});\n\n\t\tlet error = mutex.lock(key);\n\t\tlet error = error.as_deref().unwrap_err();\n\t\tassert_eq!(***error.get_ref(), 42);\n\t}\n\n\t#[test]\n\tfn locking_poisoned_rwlock_returns_error_in_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = LockCollection::new(Poisonable::new(RwLock::new(42)));\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet mut guard1 = mutex.read(key);\n\t\t\t\tlet guard = guard1.as_deref_mut().unwrap();\n\t\t\t\tassert_eq!(**guard, 42);\n\t\t\t\tpanic!();\n\n\t\t\t\t#[allow(unreachable_code)]\n\t\t\t\tdrop(guard1);\n\t\t\t})\n\t\t\t.join()\n\t\t\t.unwrap_err();\n\t\t});\n\n\t\tlet error = mutex.read(key);\n\t\tlet error = error.as_deref().unwrap_err();\n\t\tassert_eq!(***error.get_ref(), 42);\n\t}\n\n\t#[test]\n\tfn non_poisoned_get_mut_is_ok() {\n\t\tlet mut mutex = Poisonable::new(Mutex::new(42));\n\t\tlet guard = mutex.get_mut();\n\t\tassert!(guard.is_ok());\n\t\tassert_eq!(*guard.unwrap(), 42);\n\t}\n\n\t#[test]\n\tfn non_poisoned_get_mut_is_err() {\n\t\tlet mut mutex = Poisonable::new(Mutex::new(42));\n\n\t\tlet _ = std::panic::catch_unwind(|| {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t#[allow(unused_variables)]\n\t\t\tlet guard = mutex.lock(key);\n\t\t\tpanic!();\n\t\t\t#[allow(unreachable_code)]\n\t\t\tdrop(guard);\n\t\t});\n\n\t\tlet guard = mutex.get_mut();\n\t\tassert!(guard.is_err());\n\t\tassert_eq!(**guard.unwrap_err().get_ref(), 42);\n\t}\n\n\t#[test]\n\tfn unpoisoned_into_inner() {\n\t\tlet mutex = Poisonable::new(Mutex::new(\"foo\"));\n\t\tassert_eq!(mutex.into_inner().unwrap(), \"foo\");\n\t}\n\n\t#[test]\n\tfn poisoned_into_inner() {\n\t\tlet mutex = Poisonable::from(Mutex::new(\"foo\"));\n\n\t\tstd::panic::catch_unwind(|| {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t#[allow(unused_variables)]\n\t\t\tlet guard = mutex.lock(key);\n\t\t\tpanic!();\n\t\t\t#[allow(unreachable_code)]\n\t\t\tdrop(guard);\n\t\t})\n\t\t.unwrap_err();\n\n\t\tlet error = mutex.into_inner().unwrap_err();\n\t\tassert_eq!(error.into_inner(), \"foo\");\n\t}\n\n\t#[test]\n\tfn unpoisoned_into_child() {\n\t\tlet mutex = Poisonable::new(Mutex::new(\"foo\"));\n\t\tassert_eq!(mutex.into_child().unwrap().into_inner(), \"foo\");\n\t}\n\n\t#[test]\n\tfn poisoned_into_child() {\n\t\tlet mutex = Poisonable::from(Mutex::new(\"foo\"));\n\n\t\tstd::panic::catch_unwind(|| {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t#[allow(unused_variables)]\n\t\t\tlet guard = mutex.lock(key);\n\t\t\tpanic!();\n\t\t\t#[allow(unreachable_code)]\n\t\t\tdrop(guard);\n\t\t})\n\t\t.unwrap_err();\n\n\t\tlet error = mutex.into_child().unwrap_err();\n\t\tassert_eq!(error.into_inner().into_inner(), \"foo\");\n\t}\n\n\t#[test]\n\tfn scoped_lock_can_poison() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = Poisonable::new(Mutex::new(42));\n\n\t\tlet r = std::panic::catch_unwind(|| {\n\t\t\tmutex.scoped_lock(key, |num| {\n\t\t\t\t*num.unwrap() = 56;\n\t\t\t\tpanic!();\n\t\t\t})\n\t\t});\n\t\tassert!(r.is_err());\n\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tassert!(mutex.is_poisoned());\n\t\tmutex.scoped_lock(key, |num| {\n\t\t\tlet Err(error) = num else { panic!() };\n\t\t\tmutex.clear_poison();\n\t\t\tlet guard = error.into_inner();\n\t\t\tassert_eq!(*guard, 56);\n\t\t});\n\t\tassert!(!mutex.is_poisoned());\n\t}\n\n\t#[test]\n\tfn scoped_try_lock_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = Poisonable::new(Mutex::new(42));\n\t\tlet guard = mutex.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = mutex.scoped_try_lock(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn scoped_try_lock_can_succeed() {\n\t\tlet rwlock = Poisonable::new(RwLock::new(42));\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = rwlock.scoped_try_lock(key, |guard| {\n\t\t\t\t\tassert_eq!(*guard.unwrap(), 42);\n\t\t\t\t});\n\t\t\t\tassert!(r.is_ok());\n\t\t\t});\n\t\t});\n\t}\n\n\t#[test]\n\tfn scoped_read_can_poison() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = Poisonable::new(RwLock::new(42));\n\n\t\tlet r = std::panic::catch_unwind(|| {\n\t\t\tmutex.scoped_read(key, |num| {\n\t\t\t\tassert_eq!(*num.unwrap(), 42);\n\t\t\t\tpanic!();\n\t\t\t})\n\t\t});\n\t\tassert!(r.is_err());\n\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tassert!(mutex.is_poisoned());\n\t\tmutex.scoped_read(key, |num| {\n\t\t\tlet Err(error) = num else { panic!() };\n\t\t\tmutex.clear_poison();\n\t\t\tlet guard = error.into_inner();\n\t\t\tassert_eq!(*guard, 42);\n\t\t});\n\t\tassert!(!mutex.is_poisoned());\n\t}\n\n\t#[test]\n\tfn scoped_try_read_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet rwlock = Poisonable::new(RwLock::new(42));\n\t\tlet guard = rwlock.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = rwlock.scoped_try_read(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn scoped_try_read_can_succeed() {\n\t\tlet rwlock = Poisonable::new(RwLock::new(42));\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = rwlock.scoped_try_read(key, |guard| {\n\t\t\t\t\tassert_eq!(*guard.unwrap(), 42);\n\t\t\t\t});\n\t\t\t\tassert!(r.is_ok());\n\t\t\t});\n\t\t});\n\t}\n\n\t#[test]\n\tfn display_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = Poisonable::new(Mutex::new(\"Hello, world!\"));\n\n\t\tlet guard = mutex.lock(key).unwrap();\n\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\");\n\t}\n\n\t#[test]\n\tfn ref_as_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = LockCollection::new(Poisonable::new(Mutex::new(\"foo\")));\n\t\tlet guard = collection.lock(key);\n\t\tlet Ok(ref guard) = guard.as_ref() else {\n\t\t\tpanic!()\n\t\t};\n\t\tassert_eq!(**guard.as_ref(), \"foo\");\n\t}\n\n\t#[test]\n\tfn ref_as_mut() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = LockCollection::new(Poisonable::new(Mutex::new(\"foo\")));\n\t\tlet mut guard1 = collection.lock(key);\n\t\tlet Ok(ref mut guard) = guard1.as_mut() else {\n\t\t\tpanic!()\n\t\t};\n\t\tlet guard = guard.as_mut();\n\t\t**guard = \"bar\";\n\n\t\tlet key = LockCollection::\u003cPoisonable\u003cMutex\u003c_\u003e\u003e\u003e::unlock(guard1);\n\t\tlet guard = collection.lock(key);\n\t\tlet guard = guard.as_deref().unwrap();\n\t\tassert_eq!(*guard.as_ref(), \"bar\");\n\t}\n\n\t#[test]\n\tfn guard_as_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = Poisonable::new(Mutex::new(\"foo\"));\n\t\tlet guard = collection.lock(key);\n\t\tlet Ok(ref guard) = guard.as_ref() else {\n\t\t\tpanic!()\n\t\t};\n\t\tassert_eq!(**guard.as_ref(), \"foo\");\n\t}\n\n\t#[test]\n\tfn guard_as_mut() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = Poisonable::new(Mutex::new(\"foo\"));\n\t\tlet mut guard1 = mutex.lock(key);\n\t\tlet Ok(ref mut guard) = guard1.as_mut() else {\n\t\t\tpanic!()\n\t\t};\n\t\tlet guard = guard.as_mut();\n\t\t**guard = \"bar\";\n\n\t\tlet key = Poisonable::\u003cMutex\u003c_\u003e\u003e::unlock(guard1.unwrap());\n\t\tlet guard = mutex.lock(key);\n\t\tlet guard = guard.as_deref().unwrap();\n\t\tassert_eq!(*guard, \"bar\");\n\t}\n\n\t#[test]\n\tfn deref_mut_in_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = LockCollection::new(Poisonable::new(Mutex::new(42)));\n\t\tlet mut guard1 = collection.lock(key);\n\t\tlet Ok(ref mut guard) = guard1.as_mut() else {\n\t\t\tpanic!()\n\t\t};\n\t\t// TODO make this more convenient\n\t\tassert_eq!(***guard, 42);\n\t\t***guard = 24;\n\n\t\tlet key = LockCollection::\u003cPoisonable\u003cMutex\u003c_\u003e\u003e\u003e::unlock(guard1);\n\t\t_ = collection.lock(key);\n\t}\n\n\t#[test]\n\tfn get_ptrs() {\n\t\tlet mutex = Mutex::new(5);\n\t\tlet poisonable = Poisonable::new(mutex);\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tpoisonable.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tassert!(std::ptr::addr_eq(lock_ptrs[0], \u0026poisonable.inner));\n\t}\n\n\t#[test]\n\tfn clear_poison_for_poisoned_mutex() {\n\t\tlet mutex = Arc::new(Poisonable::new(Mutex::new(0)));\n\t\tlet c_mutex = Arc::clone(\u0026mutex);\n\n\t\tlet _ = std::thread::spawn(move || {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\tlet _lock = c_mutex.lock(key).unwrap();\n\t\t\tpanic!(); // the mutex gets poisoned\n\t\t})\n\t\t.join();\n\n\t\tassert!(mutex.is_poisoned());\n\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet _ = mutex.lock(key).unwrap_or_else(|mut e| {\n\t\t\t**e.get_mut() = 1;\n\t\t\tmutex.clear_poison();\n\t\t\te.into_inner()\n\t\t});\n\n\t\tassert!(!mutex.is_poisoned());\n\t}\n\n\t#[test]\n\tfn clear_poison_for_poisoned_rwlock() {\n\t\tlet lock = Arc::new(Poisonable::new(RwLock::new(0)));\n\t\tlet c_mutex = Arc::clone(\u0026lock);\n\n\t\tlet _ = std::thread::spawn(move || {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\tlet lock = c_mutex.read(key).unwrap();\n\t\t\tassert_eq!(*lock, 42);\n\t\t\tpanic!(); // the mutex gets poisoned\n\t\t})\n\t\t.join();\n\n\t\tassert!(lock.is_poisoned());\n\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet _ = lock.lock(key).unwrap_or_else(|mut e| {\n\t\t\t**e.get_mut() = 1;\n\t\t\tlock.clear_poison();\n\t\t\te.into_inner()\n\t\t});\n\n\t\tassert!(!lock.is_poisoned());\n\t}\n\n\t#[test]\n\tfn error_as_ref() {\n\t\tlet mutex = Poisonable::new(Mutex::new(\"foo\"));\n\n\t\tlet _ = std::panic::catch_unwind(|| {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t#[allow(unused_variables)]\n\t\t\tlet guard = mutex.lock(key);\n\t\t\tpanic!();\n\n\t\t\t#[allow(unknown_lints)]\n\t\t\t#[allow(unreachable_code)]\n\t\t\tdrop(guard);\n\t\t});\n\n\t\tassert!(mutex.is_poisoned());\n\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet error = mutex.lock(key).unwrap_err();\n\t\tassert_eq!(\u0026***error.as_ref(), \"foo\");\n\t}\n\n\t#[test]\n\tfn error_as_mut() {\n\t\tlet mutex = Poisonable::new(Mutex::new(\"foo\"));\n\n\t\tlet _ = std::panic::catch_unwind(|| {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t#[allow(unused_variables)]\n\t\t\tlet guard = mutex.lock(key);\n\t\t\tpanic!();\n\n\t\t\t#[allow(unknown_lints)]\n\t\t\t#[allow(unreachable_code)]\n\t\t\tdrop(guard);\n\t\t});\n\n\t\tassert!(mutex.is_poisoned());\n\n\t\tlet key: ThreadKey = ThreadKey::get().unwrap();\n\t\tlet mut error = mutex.lock(key).unwrap_err();\n\t\tlet error1 = error.as_mut();\n\t\t**error1 = \"bar\";\n\t\tlet key = Poisonable::\u003cMutex\u003c_\u003e\u003e::unlock(error.into_inner());\n\n\t\tmutex.clear_poison();\n\t\tlet guard = mutex.lock(key).unwrap();\n\t\tassert_eq!(\u0026**guard, \"bar\");\n\t}\n\n\t#[test]\n\tfn try_error_from_lock_error() {\n\t\tlet mutex = Poisonable::new(Mutex::new(\"foo\"));\n\n\t\tlet _ = std::panic::catch_unwind(|| {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t#[allow(unused_variables)]\n\t\t\tlet guard = mutex.lock(key);\n\t\t\tpanic!();\n\n\t\t\t#[allow(unknown_lints)]\n\t\t\t#[allow(unreachable_code)]\n\t\t\tdrop(guard);\n\t\t});\n\n\t\tassert!(mutex.is_poisoned());\n\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet error = mutex.lock(key).unwrap_err();\n\t\tlet error = TryLockPoisonableError::from(error);\n\n\t\tlet TryLockPoisonableError::Poisoned(error) = error else {\n\t\t\tpanic!()\n\t\t};\n\t\tassert_eq!(\u0026**error.into_inner(), \"foo\");\n\t}\n\n\t#[test]\n\tfn new_poisonable_is_not_poisoned() {\n\t\tlet mutex = Poisonable::new(Mutex::new(42));\n\t\tassert!(!mutex.is_poisoned());\n\t}\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","src","rwlock","read_guard.rs"],"content":"use std::fmt::{Debug, Display};\nuse std::hash::Hash;\nuse std::marker::PhantomData;\nuse std::ops::Deref;\n\nuse lock_api::RawRwLock;\n\nuse crate::lockable::RawLock;\nuse crate::ThreadKey;\n\nuse super::{RwLock, RwLockReadGuard, RwLockReadRef};\n\n// These impls make things slightly easier because now you can use\n// `println!(\"{guard}\")` instead of `println!(\"{}\", *guard)`\n\n#[mutants::skip] // hashing involves PRNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Hash + ?Sized, R: RawRwLock\u003e Hash for RwLockReadRef\u003c'_, T, R\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.deref().hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug + ?Sized, R: RawRwLock\u003e Debug for RwLockReadRef\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: Display + ?Sized, R: RawRwLock\u003e Display for RwLockReadRef\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e Deref for RwLockReadRef\u003c'_, T, R\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t// safety: this is the only type that can use `value`, and there's\n\t\t// a reference to this type, so there cannot be any mutable\n\t\t// references to this value.\n\t\tunsafe { \u0026*self.0.data.get() }\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e AsRef\u003cT\u003e for RwLockReadRef\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e Drop for RwLockReadRef\u003c'_, T, R\u003e {\n\tfn drop(\u0026mut self) {\n\t\t// safety: this guard is being destroyed, so the data cannot be\n\t\t// accessed without locking again\n\t\tunsafe { self.0.raw_unlock_read() }\n\t}\n}\n\nimpl\u003c'a, T: ?Sized, R: RawRwLock\u003e RwLockReadRef\u003c'a, T, R\u003e {\n\t/// Creates an immutable reference for the underlying data of an [`RwLock`]\n\t/// without locking it or taking ownership of the key.\n\t#[must_use]\n\tpub(crate) const unsafe fn new(mutex: \u0026'a RwLock\u003cT, R\u003e) -\u003e Self {\n\t\tSelf(mutex, PhantomData)\n\t}\n}\n\n#[mutants::skip] // hashing involves PRNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Hash + ?Sized, R: RawRwLock\u003e Hash for RwLockReadGuard\u003c'_, T, R\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.deref().hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug + ?Sized, R: RawRwLock\u003e Debug for RwLockReadGuard\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: Display + ?Sized, R: RawRwLock\u003e Display for RwLockReadGuard\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e Deref for RwLockReadGuard\u003c'_, T, R\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t\u0026self.rwlock\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e AsRef\u003cT\u003e for RwLockReadGuard\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself\n\t}\n}\n\nimpl\u003c'a, T: ?Sized, R: RawRwLock\u003e RwLockReadGuard\u003c'a, T, R\u003e {\n\t/// Create a guard to the given mutex. Undefined if multiple guards to the\n\t/// same mutex exist at once.\n\t#[must_use]\n\tpub(super) const unsafe fn new(rwlock: \u0026'a RwLock\u003cT, R\u003e, thread_key: ThreadKey) -\u003e Self {\n\t\tSelf {\n\t\t\trwlock: RwLockReadRef(rwlock, PhantomData),\n\t\t\tthread_key,\n\t\t}\n\t}\n}\n\nunsafe impl\u003cT: ?Sized + Sync, R: RawRwLock + Sync\u003e Sync for RwLockReadRef\u003c'_, T, R\u003e {}\n","traces":[{"line":33,"address":[970320],"length":1,"stats":{"Line":1}},{"line":34,"address":[],"length":0,"stats":{"Line":1}},{"line":41,"address":[],"length":0,"stats":{"Line":3}},{"line":45,"address":[970405,970373],"length":1,"stats":{"Line":3}},{"line":50,"address":[],"length":0,"stats":{"Line":1}},{"line":51,"address":[],"length":0,"stats":{"Line":1}},{"line":56,"address":[240400,240384],"length":1,"stats":{"Line":3}},{"line":59,"address":[237893,237909],"length":1,"stats":{"Line":3}},{"line":67,"address":[],"length":0,"stats":{"Line":3}},{"line":68,"address":[],"length":0,"stats":{"Line":0}},{"line":89,"address":[],"length":0,"stats":{"Line":1}},{"line":90,"address":[],"length":0,"stats":{"Line":1}},{"line":97,"address":[],"length":0,"stats":{"Line":2}},{"line":98,"address":[],"length":0,"stats":{"Line":2}},{"line":103,"address":[],"length":0,"stats":{"Line":1}},{"line":104,"address":[],"length":0,"stats":{"Line":1}},{"line":112,"address":[],"length":0,"stats":{"Line":1}},{"line":114,"address":[],"length":0,"stats":{"Line":0}}],"covered":16,"coverable":18},{"path":["/","home","botahamec","Projects","happylock","src","rwlock","read_lock.rs"],"content":"use std::fmt::Debug;\n\nuse lock_api::RawRwLock;\n\nuse crate::lockable::{Lockable, RawLock, Sharable};\nuse crate::{Keyable, ThreadKey};\n\nuse super::{ReadLock, RwLock, RwLockReadGuard, RwLockReadRef};\n\nunsafe impl\u003cT, R: RawRwLock\u003e RawLock for ReadLock\u003c'_, T, R\u003e {\n\tfn poison(\u0026self) {\n\t\tself.0.poison()\n\t}\n\n\tunsafe fn raw_write(\u0026self) {\n\t\tself.0.raw_read()\n\t}\n\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool {\n\t\tself.0.raw_try_read()\n\t}\n\n\tunsafe fn raw_unlock_write(\u0026self) {\n\t\tself.0.raw_unlock_read()\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tself.0.raw_read()\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tself.0.raw_try_read()\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tself.0.raw_unlock_read()\n\t}\n}\n\nunsafe impl\u003cT, R: RawRwLock\u003e Lockable for ReadLock\u003c'_, T, R\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= RwLockReadRef\u003c'g, T, R\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= \u0026'a T\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tptrs.push(self);\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tRwLockReadRef::new(self.as_ref())\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.0.data_ref()\n\t}\n}\n\nunsafe impl\u003cT, R: RawRwLock\u003e Sharable for ReadLock\u003c'_, T, R\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= RwLockReadRef\u003c'g, T, R\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= \u0026'a T\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tRwLockReadRef::new(self.as_ref())\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tself.0.data_ref()\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug, R: RawRwLock\u003e Debug for ReadLock\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t// safety: this is just a try lock, and the value is dropped\n\t\t// immediately after, so there's no risk of blocking ourselves\n\t\t// or any other threads\n\t\tif let Some(value) = unsafe { self.try_lock_no_key() } {\n\t\t\tf.debug_struct(\"ReadLock\").field(\"data\", \u0026\u0026*value).finish()\n\t\t} else {\n\t\t\tstruct LockedPlaceholder;\n\t\t\timpl Debug for LockedPlaceholder {\n\t\t\t\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t\t\t\tf.write_str(\"\u003clocked\u003e\")\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tf.debug_struct(\"ReadLock\")\n\t\t\t\t.field(\"data\", \u0026LockedPlaceholder)\n\t\t\t\t.finish()\n\t\t}\n\t}\n}\n\nimpl\u003c'l, T, R\u003e From\u003c\u0026'l RwLock\u003cT, R\u003e\u003e for ReadLock\u003c'l, T, R\u003e {\n\tfn from(value: \u0026'l RwLock\u003cT, R\u003e) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\nimpl\u003cT: ?Sized, R\u003e AsRef\u003cRwLock\u003cT, R\u003e\u003e for ReadLock\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026RwLock\u003cT, R\u003e {\n\t\tself.0\n\t}\n}\n\nimpl\u003c'l, T, R\u003e ReadLock\u003c'l, T, R\u003e {\n\t/// Creates a new `ReadLock` which accesses the given [`RwLock`]\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{rwlock::ReadLock, RwLock};\n\t///\n\t/// let lock = RwLock::new(5);\n\t/// let read_lock = ReadLock::new(\u0026lock);\n\t/// ```\n\t#[must_use]\n\tpub const fn new(rwlock: \u0026'l RwLock\u003cT, R\u003e) -\u003e Self {\n\t\tSelf(rwlock)\n\t}\n}\n\nimpl\u003cT, R: RawRwLock\u003e ReadLock\u003c'_, T, R\u003e {\n\tpub fn scoped_lock\u003c'a, Ret\u003e(\u0026'a self, key: impl Keyable, f: impl Fn(\u0026'a T) -\u003e Ret) -\u003e Ret {\n\t\tself.0.scoped_read(key, f)\n\t}\n\n\tpub fn scoped_try_lock\u003c'a, Key: Keyable, Ret\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(\u0026'a T) -\u003e Ret,\n\t) -\u003e Result\u003cRet, Key\u003e {\n\t\tself.0.scoped_try_read(key, f)\n\t}\n\n\t/// Locks the underlying [`RwLock`] with shared read access, blocking the\n\t/// current thread until it can be acquired.\n\t///\n\t/// The calling thread will be blocked until there are no more writers\n\t/// which hold the lock. There may be other readers currently inside the\n\t/// lock when this method returns.\n\t///\n\t/// Returns an RAII guard which will release this thread's shared access\n\t/// once it is dropped.\n\t///\n\t/// Because this method takes a [`ThreadKey`], it's not possible for this\n\t/// method to cause a deadlock.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::rwlock::ReadLock;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock: RwLock\u003c_\u003e = RwLock::new(1);\n\t/// let reader = ReadLock::new(\u0026lock);\n\t///\n\t/// let n = reader.lock(key);\n\t/// assert_eq!(*n, 1);\n\t/// ```\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\t#[must_use]\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e RwLockReadGuard\u003c'_, T, R\u003e {\n\t\tself.0.read(key)\n\t}\n\n\t/// Attempts to acquire the underlying [`RwLock`] with shared read access\n\t/// without blocking.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// shared access when it is dropped.\n\t///\n\t/// This function does not provide any guarantees with respect to the\n\t/// ordering of whether contentious readers or writers will acquire the\n\t/// lock first.\n\t///\n\t/// # Errors\n\t///\n\t/// If the `RwLock` could not be acquired because it was already locked\n\t/// exclusively, then an error will be returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::rwlock::ReadLock;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(1);\n\t/// let reader = ReadLock::new(\u0026lock);\n\t///\n\t/// match reader.try_lock(key) {\n\t/// Ok(n) =\u003e assert_eq!(*n, 1),\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t/// ```\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e Result\u003cRwLockReadGuard\u003c'_, T, R\u003e, ThreadKey\u003e {\n\t\tself.0.try_read(key)\n\t}\n\n\t/// Attempts to create an exclusive lock without a key. Locking this\n\t/// without exclusive access to the key is undefined behavior.\n\tpub(crate) unsafe fn try_lock_no_key(\u0026self) -\u003e Option\u003cRwLockReadRef\u003c'_, T, R\u003e\u003e {\n\t\tself.0.try_read_no_key()\n\t}\n\n\t/// Immediately drops the guard, and consequently releases the shared lock\n\t/// on the underlying [`RwLock`].\n\t///\n\t/// This function is equivalent to calling [`drop`] on the guard, except\n\t/// that it returns the key that was used to create it. Alternately, the\n\t/// guard will be automatically dropped when it goes out of scope.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::rwlock::ReadLock;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(0);\n\t/// let reader = ReadLock::new(\u0026lock);\n\t///\n\t/// let mut guard = reader.lock(key);\n\t/// assert_eq!(*guard, 0);\n\t/// let key = ReadLock::unlock(guard);\n\t/// ```\n\t#[must_use]\n\tpub fn unlock(guard: RwLockReadGuard\u003c'_, T, R\u003e) -\u003e ThreadKey {\n\t\tRwLock::unlock_read(guard)\n\t}\n}\n","traces":[{"line":11,"address":[968928,968944],"length":1,"stats":{"Line":1}},{"line":12,"address":[],"length":0,"stats":{"Line":1}},{"line":15,"address":[],"length":0,"stats":{"Line":1}},{"line":16,"address":[],"length":0,"stats":{"Line":1}},{"line":19,"address":[968992,969024],"length":1,"stats":{"Line":1}},{"line":20,"address":[],"length":0,"stats":{"Line":1}},{"line":23,"address":[969072,969056],"length":1,"stats":{"Line":1}},{"line":24,"address":[],"length":0,"stats":{"Line":1}},{"line":27,"address":[],"length":0,"stats":{"Line":1}},{"line":28,"address":[],"length":0,"stats":{"Line":1}},{"line":31,"address":[],"length":0,"stats":{"Line":1}},{"line":32,"address":[],"length":0,"stats":{"Line":1}},{"line":35,"address":[],"length":0,"stats":{"Line":1}},{"line":36,"address":[],"length":0,"stats":{"Line":1}},{"line":51,"address":[],"length":0,"stats":{"Line":2}},{"line":52,"address":[969305,969241],"length":1,"stats":{"Line":2}},{"line":55,"address":[],"length":0,"stats":{"Line":1}},{"line":56,"address":[],"length":0,"stats":{"Line":1}},{"line":59,"address":[],"length":0,"stats":{"Line":1}},{"line":60,"address":[],"length":0,"stats":{"Line":1}},{"line":75,"address":[],"length":0,"stats":{"Line":1}},{"line":76,"address":[],"length":0,"stats":{"Line":1}},{"line":79,"address":[],"length":0,"stats":{"Line":1}},{"line":80,"address":[],"length":0,"stats":{"Line":1}},{"line":109,"address":[],"length":0,"stats":{"Line":1}},{"line":110,"address":[],"length":0,"stats":{"Line":1}},{"line":115,"address":[],"length":0,"stats":{"Line":1}},{"line":116,"address":[],"length":0,"stats":{"Line":1}},{"line":132,"address":[969472,969488],"length":1,"stats":{"Line":2}},{"line":133,"address":[],"length":0,"stats":{"Line":0}},{"line":138,"address":[],"length":0,"stats":{"Line":1}},{"line":139,"address":[],"length":0,"stats":{"Line":1}},{"line":142,"address":[969536],"length":1,"stats":{"Line":1}},{"line":147,"address":[969545],"length":1,"stats":{"Line":1}},{"line":181,"address":[],"length":0,"stats":{"Line":1}},{"line":182,"address":[],"length":0,"stats":{"Line":1}},{"line":216,"address":[],"length":0,"stats":{"Line":1}},{"line":217,"address":[],"length":0,"stats":{"Line":1}},{"line":222,"address":[],"length":0,"stats":{"Line":0}},{"line":223,"address":[],"length":0,"stats":{"Line":0}},{"line":248,"address":[],"length":0,"stats":{"Line":1}},{"line":249,"address":[],"length":0,"stats":{"Line":1}}],"covered":39,"coverable":42},{"path":["/","home","botahamec","Projects","happylock","src","rwlock","rwlock.rs"],"content":"use std::cell::UnsafeCell;\nuse std::fmt::Debug;\nuse std::marker::PhantomData;\nuse std::panic::AssertUnwindSafe;\n\nuse lock_api::RawRwLock;\n\nuse crate::collection::utils;\nuse crate::handle_unwind::handle_unwind;\nuse crate::lockable::{\n\tLockable, LockableGetMut, LockableIntoInner, OwnedLockable, RawLock, Sharable,\n};\nuse crate::{Keyable, ThreadKey};\n\nuse super::{PoisonFlag, RwLock, RwLockReadGuard, RwLockReadRef, RwLockWriteGuard, RwLockWriteRef};\n\nunsafe impl\u003cT: ?Sized, R: RawRwLock\u003e RawLock for RwLock\u003cT, R\u003e {\n\tfn poison(\u0026self) {\n\t\tself.poison.poison();\n\t}\n\n\tunsafe fn raw_write(\u0026self) {\n\t\tassert!(\n\t\t\t!self.poison.is_poisoned(),\n\t\t\t\"The read-write lock has been killed\"\n\t\t);\n\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.lock_exclusive(), || self.poison())\n\t}\n\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool {\n\t\tif self.poison.is_poisoned() {\n\t\t\treturn false;\n\t\t}\n\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.try_lock_exclusive(), || self.poison())\n\t}\n\n\tunsafe fn raw_unlock_write(\u0026self) {\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.unlock_exclusive(), || self.poison())\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tassert!(\n\t\t\t!self.poison.is_poisoned(),\n\t\t\t\"The read-write lock has been killed\"\n\t\t);\n\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.lock_shared(), || self.poison())\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tif self.poison.is_poisoned() {\n\t\t\treturn false;\n\t\t}\n\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.try_lock_shared(), || self.poison())\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.unlock_shared(), || self.poison())\n\t}\n}\n\nunsafe impl\u003cT, R: RawRwLock\u003e Lockable for RwLock\u003cT, R\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= RwLockWriteRef\u003c'g, T, R\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= \u0026'a mut T\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tptrs.push(self);\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tRwLockWriteRef::new(self)\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.data.get().as_mut().unwrap_unchecked()\n\t}\n}\n\nunsafe impl\u003cT, R: RawRwLock\u003e Sharable for RwLock\u003cT, R\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= RwLockReadRef\u003c'g, T, R\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= \u0026'a T\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tRwLockReadRef::new(self)\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tself.data.get().as_ref().unwrap_unchecked()\n\t}\n}\n\nunsafe impl\u003cT: Send, R: RawRwLock\u003e OwnedLockable for RwLock\u003cT, R\u003e {}\n\nimpl\u003cT: Send, R: RawRwLock\u003e LockableIntoInner for RwLock\u003cT, R\u003e {\n\ttype Inner = T;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tself.into_inner()\n\t}\n}\n\nimpl\u003cT: Send, R: RawRwLock\u003e LockableGetMut for RwLock\u003cT, R\u003e {\n\ttype Inner\u003c'a\u003e\n\t\t= \u0026'a mut T\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tAsMut::as_mut(self)\n\t}\n}\n\nimpl\u003cT, R: RawRwLock\u003e RwLock\u003cT, R\u003e {\n\t/// Creates a new instance of an `RwLock\u003cT\u003e` which is unlocked.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::RwLock;\n\t///\n\t/// let lock = RwLock::new(5);\n\t/// ```\n\t#[must_use]\n\tpub const fn new(data: T) -\u003e Self {\n\t\tSelf {\n\t\t\tdata: UnsafeCell::new(data),\n\t\t\tpoison: PoisonFlag::new(),\n\t\t\traw: R::INIT,\n\t\t}\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug, R: RawRwLock\u003e Debug for RwLock\u003cT, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t// safety: this is just a try lock, and the value is dropped\n\t\t// immediately after, so there's no risk of blocking ourselves\n\t\t// or any other threads\n\t\tif let Some(value) = unsafe { self.try_read_no_key() } {\n\t\t\tf.debug_struct(\"RwLock\").field(\"data\", \u0026\u0026*value).finish()\n\t\t} else {\n\t\t\tstruct LockedPlaceholder;\n\t\t\timpl Debug for LockedPlaceholder {\n\t\t\t\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t\t\t\tf.write_str(\"\u003clocked\u003e\")\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tf.debug_struct(\"RwLock\")\n\t\t\t\t.field(\"data\", \u0026LockedPlaceholder)\n\t\t\t\t.finish()\n\t\t}\n\t}\n}\n\nimpl\u003cT: Default, R: RawRwLock\u003e Default for RwLock\u003cT, R\u003e {\n\tfn default() -\u003e Self {\n\t\tSelf::new(T::default())\n\t}\n}\n\nimpl\u003cT, R: RawRwLock\u003e From\u003cT\u003e for RwLock\u003cT, R\u003e {\n\tfn from(value: T) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\n// We don't need a `get_mut` because we don't have mutex poisoning. Hurray!\n// This is safe because you can't have a mutable reference to the lock if it's\n// locked. Being locked requires an immutable reference because of the guard.\nimpl\u003cT: ?Sized, R\u003e AsMut\u003cT\u003e for RwLock\u003cT, R\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself.data.get_mut()\n\t}\n}\n\nimpl\u003cT, R\u003e RwLock\u003cT, R\u003e {\n\t/// Consumes this `RwLock`, returning the underlying data.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t///\n\t/// let lock = RwLock::new(String::new());\n\t/// {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut s = lock.write(key);\n\t/// *s = \"modified\".to_owned();\n\t/// }\n\t/// assert_eq!(lock.into_inner(), \"modified\");\n\t/// ```\n\t#[must_use]\n\tpub fn into_inner(self) -\u003e T {\n\t\tself.data.into_inner()\n\t}\n}\n\nimpl\u003cT: ?Sized, R\u003e RwLock\u003cT, R\u003e {\n\t/// Returns a mutable reference to the underlying data.\n\t///\n\t/// Since this call borrows `RwLock` mutably, no actual locking is taking\n\t/// place. The mutable borrow statically guarantees that no locks exist.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, RwLock};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut mutex = RwLock::new(0);\n\t/// *mutex.get_mut() = 10;\n\t/// assert_eq!(*mutex.read(key), 10);\n\t/// ```\n\t#[must_use]\n\tpub fn get_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself.data.get_mut()\n\t}\n}\n\nimpl\u003cT, R: RawRwLock\u003e RwLock\u003cT, R\u003e {\n\tpub fn scoped_read\u003c'a, Ret\u003e(\u0026'a self, key: impl Keyable, f: impl Fn(\u0026'a T) -\u003e Ret) -\u003e Ret {\n\t\tutils::scoped_read(self, key, f)\n\t}\n\n\tpub fn scoped_try_read\u003c'a, Key: Keyable, Ret\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(\u0026'a T) -\u003e Ret,\n\t) -\u003e Result\u003cRet, Key\u003e {\n\t\tutils::scoped_try_read(self, key, f)\n\t}\n\n\tpub fn scoped_write\u003c'a, Ret\u003e(\u0026'a self, key: impl Keyable, f: impl Fn(\u0026'a mut T) -\u003e Ret) -\u003e Ret {\n\t\tutils::scoped_write(self, key, f)\n\t}\n\n\tpub fn scoped_try_write\u003c'a, Key: Keyable, Ret\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(\u0026'a mut T) -\u003e Ret,\n\t) -\u003e Result\u003cRet, Key\u003e {\n\t\tutils::scoped_try_write(self, key, f)\n\t}\n\n\t/// Locks this `RwLock` with shared read access, blocking the current\n\t/// thread until it can be acquired.\n\t///\n\t/// The calling thread will be blocked until there are no more writers\n\t/// which hold the lock. There may be other readers currently inside the\n\t/// lock when this method returns.\n\t///\n\t/// Returns an RAII guard which will release this thread's shared access\n\t/// once it is dropped.\n\t///\n\t/// Because this method takes a [`ThreadKey`], it's not possible for this\n\t/// method to cause a deadlock.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t/// use happylock::{RwLock, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = Arc::new(RwLock::new(1));\n\t/// let c_lock = Arc::clone(\u0026lock);\n\t///\n\t/// let n = lock.read(key);\n\t/// assert_eq!(*n, 1);\n\t///\n\t/// thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let r = c_lock.read(key);\n\t/// }).join().unwrap();\n\t/// ```\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\tpub fn read(\u0026self, key: ThreadKey) -\u003e RwLockReadGuard\u003c'_, T, R\u003e {\n\t\tunsafe {\n\t\t\tself.raw_read();\n\n\t\t\t// safety: the lock is locked first\n\t\t\tRwLockReadGuard::new(self, key)\n\t\t}\n\t}\n\n\t/// Attempts to acquire this `RwLock` with shared read access without\n\t/// blocking.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// shared access when it is dropped.\n\t///\n\t/// This function does not provide any guarantees with respect to the\n\t/// ordering of whether contentious readers or writers will acquire the\n\t/// lock first.\n\t///\n\t/// # Errors\n\t///\n\t/// If the `RwLock` could not be acquired because it was already locked\n\t/// exclusively, then an error will be returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(1);\n\t///\n\t/// match lock.try_read(key) {\n\t/// Ok(n) =\u003e assert_eq!(*n, 1),\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t/// ```\n\tpub fn try_read(\u0026self, key: ThreadKey) -\u003e Result\u003cRwLockReadGuard\u003c'_, T, R\u003e, ThreadKey\u003e {\n\t\tunsafe {\n\t\t\tif self.raw_try_read() {\n\t\t\t\t// safety: the lock is locked first\n\t\t\t\tOk(RwLockReadGuard::new(self, key))\n\t\t\t} else {\n\t\t\t\tErr(key)\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Attempts to create a shared lock without a key. Locking this without\n\t/// exclusive access to the key is undefined behavior.\n\tpub(crate) unsafe fn try_read_no_key(\u0026self) -\u003e Option\u003cRwLockReadRef\u003c'_, T, R\u003e\u003e {\n\t\tif self.raw_try_read() {\n\t\t\t// safety: the lock is locked first\n\t\t\tSome(RwLockReadRef(self, PhantomData))\n\t\t} else {\n\t\t\tNone\n\t\t}\n\t}\n\n\t/// Attempts to create an exclusive lock without a key. Locking this\n\t/// without exclusive access to the key is undefined behavior.\n\t#[cfg(test)]\n\tpub(crate) unsafe fn try_write_no_key(\u0026self) -\u003e Option\u003cRwLockWriteRef\u003c'_, T, R\u003e\u003e {\n\t\tif self.raw_try_write() {\n\t\t\t// safety: the lock is locked first\n\t\t\tSome(RwLockWriteRef(self, PhantomData))\n\t\t} else {\n\t\t\tNone\n\t\t}\n\t}\n\n\t/// Locks this `RwLock` with exclusive write access, blocking the current\n\t/// until it can be acquired.\n\t///\n\t/// This function will not return while other writers or readers currently\n\t/// have access to the lock.\n\t///\n\t/// Returns an RAII guard which will drop the write access of this `RwLock`\n\t/// when dropped.\n\t///\n\t/// Because this method takes a [`ThreadKey`], it's not possible for this\n\t/// method to cause a deadlock.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, RwLock};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(1);\n\t///\n\t/// match lock.try_write(key) {\n\t/// Ok(n) =\u003e assert_eq!(*n, 1),\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t/// ```\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\tpub fn write(\u0026self, key: ThreadKey) -\u003e RwLockWriteGuard\u003c'_, T, R\u003e {\n\t\tunsafe {\n\t\t\tself.raw_write();\n\n\t\t\t// safety: the lock is locked first\n\t\t\tRwLockWriteGuard::new(self, key)\n\t\t}\n\t}\n\n\t/// Attempts to lock this `RwLock` with exclusive write access.\n\t///\n\t/// This function does not block. If the lock could not be acquired at this\n\t/// time, then `None` is returned. Otherwise, an RAII guard is returned\n\t/// which will release the lock when it is dropped.\n\t///\n\t/// This function does not provide any guarantees with respect to the\n\t/// ordering of whether contentious readers or writers will acquire the\n\t/// lock first.\n\t///\n\t/// # Errors\n\t///\n\t/// If the `RwLock` could not be acquired because it was already locked,\n\t/// then an error will be returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(1);\n\t///\n\t/// let n = lock.read(key);\n\t/// assert_eq!(*n, 1);\n\t/// ```\n\tpub fn try_write(\u0026self, key: ThreadKey) -\u003e Result\u003cRwLockWriteGuard\u003c'_, T, R\u003e, ThreadKey\u003e {\n\t\tunsafe {\n\t\t\tif self.raw_try_write() {\n\t\t\t\t// safety: the lock is locked first\n\t\t\t\tOk(RwLockWriteGuard::new(self, key))\n\t\t\t} else {\n\t\t\t\tErr(key)\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Returns `true` if the rwlock is currently locked in any way\n\t#[cfg(test)]\n\tpub(crate) fn is_locked(\u0026self) -\u003e bool {\n\t\tself.raw.is_locked()\n\t}\n\n\t/// Immediately drops the guard, and consequently releases the shared lock.\n\t///\n\t/// This function is equivalent to calling [`drop`] on the guard, except\n\t/// that it returns the key that was used to create it. Alternately, the\n\t/// guard will be automatically dropped when it goes out of scope.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(0);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// assert_eq!(*guard, 0);\n\t/// let key = RwLock::unlock_read(guard);\n\t/// ```\n\t#[must_use]\n\tpub fn unlock_read(guard: RwLockReadGuard\u003c'_, T, R\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.rwlock);\n\t\tguard.thread_key\n\t}\n\n\t/// Immediately drops the guard, and consequently releases the exclusive\n\t/// lock.\n\t///\n\t/// This function is equivalent to calling [`drop`] on the guard, except\n\t/// that it returns the key that was used to create it. Alternately, the\n\t/// guard will be automatically dropped when it goes out of scope.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(0);\n\t///\n\t/// let mut guard = lock.write(key);\n\t/// *guard += 20;\n\t/// let key = RwLock::unlock_write(guard);\n\t/// ```\n\t#[must_use]\n\tpub fn unlock_write(guard: RwLockWriteGuard\u003c'_, T, R\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.rwlock);\n\t\tguard.thread_key\n\t}\n}\n\nunsafe impl\u003cR: RawRwLock + Send, T: ?Sized + Send\u003e Send for RwLock\u003cT, R\u003e {}\nunsafe impl\u003cR: RawRwLock + Sync, T: ?Sized + Send\u003e Sync for RwLock\u003cT, R\u003e {}\n","traces":[{"line":18,"address":[249088,249056,249120],"length":1,"stats":{"Line":8}},{"line":19,"address":[175989,175957],"length":1,"stats":{"Line":8}},{"line":22,"address":[176240,176352],"length":1,"stats":{"Line":5}},{"line":23,"address":[138805],"length":1,"stats":{"Line":2}},{"line":24,"address":[],"length":0,"stats":{"Line":0}},{"line":25,"address":[],"length":0,"stats":{"Line":0}},{"line":29,"address":[965760,965648],"length":1,"stats":{"Line":5}},{"line":30,"address":[249490,249590,249702],"length":1,"stats":{"Line":17}},{"line":33,"address":[138464],"length":1,"stats":{"Line":9}},{"line":34,"address":[965934,965854],"length":1,"stats":{"Line":9}},{"line":35,"address":[],"length":0,"stats":{"Line":5}},{"line":39,"address":[216545,216625],"length":1,"stats":{"Line":6}},{"line":40,"address":[217973,217941,217936,217968,217925,217920,217984,217989],"length":1,"stats":{"Line":16}},{"line":43,"address":[],"length":0,"stats":{"Line":6}},{"line":45,"address":[966044,966012],"length":1,"stats":{"Line":6}},{"line":46,"address":[1006885,1006912,1006848,1006880,1006917,1006933,1006853,1006928],"length":1,"stats":{"Line":21}},{"line":49,"address":[249344,249136,249248],"length":1,"stats":{"Line":5}},{"line":50,"address":[966228,966116],"length":1,"stats":{"Line":1}},{"line":51,"address":[],"length":0,"stats":{"Line":0}},{"line":52,"address":[],"length":0,"stats":{"Line":0}},{"line":56,"address":[217009,216897],"length":1,"stats":{"Line":5}},{"line":57,"address":[176166,176054],"length":1,"stats":{"Line":20}},{"line":60,"address":[248544,248384,248464],"length":1,"stats":{"Line":9}},{"line":61,"address":[138398],"length":1,"stats":{"Line":9}},{"line":62,"address":[138440],"length":1,"stats":{"Line":2}},{"line":66,"address":[216385,216465],"length":1,"stats":{"Line":9}},{"line":67,"address":[147557,147584,147525,147600,147605,147520,147552,147589],"length":1,"stats":{"Line":33}},{"line":70,"address":[248864,248928,248896],"length":1,"stats":{"Line":6}},{"line":72,"address":[966492,966460],"length":1,"stats":{"Line":7}},{"line":73,"address":[966465,966497],"length":1,"stats":{"Line":25}},{"line":88,"address":[217376,217312],"length":1,"stats":{"Line":16}},{"line":89,"address":[217337,217401],"length":1,"stats":{"Line":15}},{"line":92,"address":[966656,966640],"length":1,"stats":{"Line":4}},{"line":93,"address":[],"length":0,"stats":{"Line":4}},{"line":96,"address":[966672,966720],"length":1,"stats":{"Line":5}},{"line":97,"address":[249881,249785,249833],"length":1,"stats":{"Line":4}},{"line":112,"address":[217456,217440],"length":1,"stats":{"Line":3}},{"line":113,"address":[138933],"length":1,"stats":{"Line":3}},{"line":116,"address":[],"length":0,"stats":{"Line":4}},{"line":117,"address":[217529,217481],"length":1,"stats":{"Line":4}},{"line":126,"address":[],"length":0,"stats":{"Line":1}},{"line":127,"address":[],"length":0,"stats":{"Line":1}},{"line":137,"address":[],"length":0,"stats":{"Line":1}},{"line":138,"address":[966917],"length":1,"stats":{"Line":1}},{"line":153,"address":[966970,967253,967111,966928,967216,967056],"length":1,"stats":{"Line":15}},{"line":155,"address":[],"length":0,"stats":{"Line":0}},{"line":156,"address":[248042,248102,248209,248282,248160,248342],"length":1,"stats":{"Line":30}},{"line":187,"address":[],"length":0,"stats":{"Line":1}},{"line":188,"address":[],"length":0,"stats":{"Line":1}},{"line":193,"address":[967376,967424],"length":1,"stats":{"Line":2}},{"line":194,"address":[967397,967440],"length":1,"stats":{"Line":2}},{"line":202,"address":[],"length":0,"stats":{"Line":1}},{"line":203,"address":[967464],"length":1,"stats":{"Line":1}},{"line":224,"address":[],"length":0,"stats":{"Line":0}},{"line":225,"address":[967488],"length":1,"stats":{"Line":1}},{"line":246,"address":[],"length":0,"stats":{"Line":1}},{"line":247,"address":[967528],"length":1,"stats":{"Line":1}},{"line":252,"address":[967584,967552],"length":1,"stats":{"Line":2}},{"line":253,"address":[967561,967597],"length":1,"stats":{"Line":2}},{"line":256,"address":[967616],"length":1,"stats":{"Line":7}},{"line":261,"address":[215949,215981,216013,216077,215917,216045],"length":1,"stats":{"Line":7}},{"line":264,"address":[],"length":0,"stats":{"Line":2}},{"line":265,"address":[967666,967693],"length":1,"stats":{"Line":2}},{"line":268,"address":[247920,247856,247824,247984,247952,247888],"length":1,"stats":{"Line":13}},{"line":273,"address":[],"length":0,"stats":{"Line":13}},{"line":310,"address":[],"length":0,"stats":{"Line":1}},{"line":312,"address":[],"length":0,"stats":{"Line":1}},{"line":315,"address":[967805],"length":1,"stats":{"Line":1}},{"line":348,"address":[968000,968022,967872],"length":1,"stats":{"Line":1}},{"line":350,"address":[],"length":0,"stats":{"Line":4}},{"line":352,"address":[967993,967963],"length":1,"stats":{"Line":2}},{"line":354,"address":[967942],"length":1,"stats":{"Line":1}},{"line":361,"address":[],"length":0,"stats":{"Line":1}},{"line":362,"address":[],"length":0,"stats":{"Line":1}},{"line":364,"address":[968069],"length":1,"stats":{"Line":1}},{"line":366,"address":[],"length":0,"stats":{"Line":0}},{"line":373,"address":[],"length":0,"stats":{"Line":1}},{"line":374,"address":[],"length":0,"stats":{"Line":1}},{"line":376,"address":[],"length":0,"stats":{"Line":1}},{"line":378,"address":[],"length":0,"stats":{"Line":0}},{"line":409,"address":[175462,175334,175488,175248,175360,175376],"length":1,"stats":{"Line":6}},{"line":411,"address":[175262,175390],"length":1,"stats":{"Line":5}},{"line":414,"address":[],"length":0,"stats":{"Line":4}},{"line":444,"address":[],"length":0,"stats":{"Line":2}},{"line":446,"address":[],"length":0,"stats":{"Line":7}},{"line":448,"address":[],"length":0,"stats":{"Line":4}},{"line":450,"address":[],"length":0,"stats":{"Line":1}},{"line":457,"address":[968736,968752],"length":1,"stats":{"Line":2}},{"line":458,"address":[],"length":0,"stats":{"Line":2}},{"line":480,"address":[968768,968820],"length":1,"stats":{"Line":1}},{"line":481,"address":[],"length":0,"stats":{"Line":1}},{"line":482,"address":[],"length":0,"stats":{"Line":0}},{"line":505,"address":[],"length":0,"stats":{"Line":1}},{"line":506,"address":[],"length":0,"stats":{"Line":2}},{"line":507,"address":[],"length":0,"stats":{"Line":0}}],"covered":85,"coverable":95},{"path":["/","home","botahamec","Projects","happylock","src","rwlock","write_guard.rs"],"content":"use std::fmt::{Debug, Display};\nuse std::hash::Hash;\nuse std::marker::PhantomData;\nuse std::ops::{Deref, DerefMut};\n\nuse lock_api::RawRwLock;\n\nuse crate::lockable::RawLock;\nuse crate::ThreadKey;\n\nuse super::{RwLock, RwLockWriteGuard, RwLockWriteRef};\n\n// These impls make things slightly easier because now you can use\n// `println!(\"{guard}\")` instead of `println!(\"{}\", *guard)`\n\n#[mutants::skip] // hashing involves PRNG and is difficult to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Hash + ?Sized, R: RawRwLock\u003e Hash for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.deref().hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug + ?Sized, R: RawRwLock\u003e Debug for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: Display + ?Sized, R: RawRwLock\u003e Display for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e Deref for RwLockWriteRef\u003c'_, T, R\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t// safety: this is the only type that can use `value`, and there's\n\t\t// a reference to this type, so there cannot be any mutable\n\t\t// references to this value.\n\t\tunsafe { \u0026*self.0.data.get() }\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e DerefMut for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t// safety: this is the only type that can use `value`, and we have a\n\t\t// mutable reference to this type, so there cannot be any other\n\t\t// references to this value.\n\t\tunsafe { \u0026mut *self.0.data.get() }\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e AsRef\u003cT\u003e for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e AsMut\u003cT\u003e for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e Drop for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn drop(\u0026mut self) {\n\t\t// safety: this guard is being destroyed, so the data cannot be\n\t\t// accessed without locking again\n\t\tunsafe { self.0.raw_unlock_write() }\n\t}\n}\n\nimpl\u003c'a, T: ?Sized + 'a, R: RawRwLock\u003e RwLockWriteRef\u003c'a, T, R\u003e {\n\t/// Creates a reference to the underlying data of an [`RwLock`] without\n\t/// locking or taking ownership of the key.\n\t#[must_use]\n\tpub(crate) const unsafe fn new(mutex: \u0026'a RwLock\u003cT, R\u003e) -\u003e Self {\n\t\tSelf(mutex, PhantomData)\n\t}\n}\n\n#[mutants::skip] // hashing involves PRNG and is difficult to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Hash + ?Sized, R: RawRwLock\u003e Hash for RwLockWriteGuard\u003c'_, T, R\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.deref().hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug + ?Sized, R: RawRwLock\u003e Debug for RwLockWriteGuard\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: Display + ?Sized, R: RawRwLock\u003e Display for RwLockWriteGuard\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e Deref for RwLockWriteGuard\u003c'_, T, R\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t\u0026self.rwlock\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e DerefMut for RwLockWriteGuard\u003c'_, T, R\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t\u0026mut self.rwlock\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e AsRef\u003cT\u003e for RwLockWriteGuard\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e AsMut\u003cT\u003e for RwLockWriteGuard\u003c'_, T, R\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself\n\t}\n}\n\nimpl\u003c'a, T: ?Sized + 'a, R: RawRwLock\u003e RwLockWriteGuard\u003c'a, T, R\u003e {\n\t/// Create a guard to the given mutex. Undefined if multiple guards to the\n\t/// same mutex exist at once.\n\t#[must_use]\n\tpub(super) const unsafe fn new(rwlock: \u0026'a RwLock\u003cT, R\u003e, thread_key: ThreadKey) -\u003e Self {\n\t\tSelf {\n\t\t\trwlock: RwLockWriteRef(rwlock, PhantomData),\n\t\t\tthread_key,\n\t\t}\n\t}\n}\n\nunsafe impl\u003cT: ?Sized + Sync, R: RawRwLock + Sync\u003e Sync for RwLockWriteRef\u003c'_, T, R\u003e {}\n","traces":[{"line":33,"address":[970576],"length":1,"stats":{"Line":1}},{"line":34,"address":[],"length":0,"stats":{"Line":1}},{"line":41,"address":[],"length":0,"stats":{"Line":3}},{"line":45,"address":[138149],"length":1,"stats":{"Line":3}},{"line":50,"address":[138192],"length":1,"stats":{"Line":3}},{"line":54,"address":[],"length":0,"stats":{"Line":4}},{"line":59,"address":[],"length":0,"stats":{"Line":1}},{"line":60,"address":[],"length":0,"stats":{"Line":1}},{"line":65,"address":[],"length":0,"stats":{"Line":0}},{"line":66,"address":[],"length":0,"stats":{"Line":0}},{"line":71,"address":[],"length":0,"stats":{"Line":6}},{"line":74,"address":[244293,244309],"length":1,"stats":{"Line":6}},{"line":82,"address":[],"length":0,"stats":{"Line":4}},{"line":83,"address":[],"length":0,"stats":{"Line":0}},{"line":104,"address":[970800],"length":1,"stats":{"Line":1}},{"line":105,"address":[],"length":0,"stats":{"Line":1}},{"line":112,"address":[138176],"length":1,"stats":{"Line":3}},{"line":113,"address":[],"length":0,"stats":{"Line":3}},{"line":118,"address":[],"length":0,"stats":{"Line":2}},{"line":119,"address":[138229],"length":1,"stats":{"Line":2}},{"line":124,"address":[],"length":0,"stats":{"Line":1}},{"line":125,"address":[],"length":0,"stats":{"Line":1}},{"line":130,"address":[],"length":0,"stats":{"Line":1}},{"line":131,"address":[],"length":0,"stats":{"Line":1}},{"line":139,"address":[],"length":0,"stats":{"Line":4}},{"line":141,"address":[],"length":0,"stats":{"Line":0}}],"covered":22,"coverable":26},{"path":["/","home","botahamec","Projects","happylock","src","rwlock","write_lock.rs"],"content":"use std::fmt::Debug;\n\nuse lock_api::RawRwLock;\n\nuse crate::lockable::{Lockable, RawLock};\nuse crate::{Keyable, ThreadKey};\n\nuse super::{RwLock, RwLockWriteGuard, RwLockWriteRef, WriteLock};\n\nunsafe impl\u003cT, R: RawRwLock\u003e RawLock for WriteLock\u003c'_, T, R\u003e {\n\tfn poison(\u0026self) {\n\t\tself.0.poison()\n\t}\n\n\tunsafe fn raw_write(\u0026self) {\n\t\tself.0.raw_write()\n\t}\n\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool {\n\t\tself.0.raw_try_write()\n\t}\n\n\tunsafe fn raw_unlock_write(\u0026self) {\n\t\tself.0.raw_unlock_write()\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tself.0.raw_write()\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tself.0.raw_try_write()\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tself.0.raw_unlock_write()\n\t}\n}\n\nunsafe impl\u003cT, R: RawRwLock\u003e Lockable for WriteLock\u003c'_, T, R\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= RwLockWriteRef\u003c'g, T, R\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= \u0026'a mut T\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tptrs.push(self)\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tRwLockWriteRef::new(self.as_ref())\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.0.data_mut()\n\t}\n}\n\n// Technically, the exclusive locks can also be shared, but there's currently\n// no way to express that. I don't think I want to ever express that.\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug, R: RawRwLock\u003e Debug for WriteLock\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t// safety: this is just a try lock, and the value is dropped\n\t\t// immediately after, so there's no risk of blocking ourselves\n\t\t// or any other threads\n\t\t// It makes zero sense to try using an exclusive lock for this, so this\n\t\t// is the only time when WriteLock does a read.\n\t\tif let Some(value) = unsafe { self.0.try_read_no_key() } {\n\t\t\tf.debug_struct(\"WriteLock\").field(\"data\", \u0026\u0026*value).finish()\n\t\t} else {\n\t\t\tstruct LockedPlaceholder;\n\t\t\timpl Debug for LockedPlaceholder {\n\t\t\t\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t\t\t\tf.write_str(\"\u003clocked\u003e\")\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tf.debug_struct(\"WriteLock\")\n\t\t\t\t.field(\"data\", \u0026LockedPlaceholder)\n\t\t\t\t.finish()\n\t\t}\n\t}\n}\n\nimpl\u003c'l, T, R\u003e From\u003c\u0026'l RwLock\u003cT, R\u003e\u003e for WriteLock\u003c'l, T, R\u003e {\n\tfn from(value: \u0026'l RwLock\u003cT, R\u003e) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\nimpl\u003cT: ?Sized, R\u003e AsRef\u003cRwLock\u003cT, R\u003e\u003e for WriteLock\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026RwLock\u003cT, R\u003e {\n\t\tself.0\n\t}\n}\n\nimpl\u003c'l, T, R\u003e WriteLock\u003c'l, T, R\u003e {\n\t/// Creates a new `WriteLock` which accesses the given [`RwLock`]\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{rwlock::WriteLock, RwLock};\n\t///\n\t/// let lock = RwLock::new(5);\n\t/// let write_lock = WriteLock::new(\u0026lock);\n\t/// ```\n\t#[must_use]\n\tpub const fn new(rwlock: \u0026'l RwLock\u003cT, R\u003e) -\u003e Self {\n\t\tSelf(rwlock)\n\t}\n}\n\nimpl\u003cT, R: RawRwLock\u003e WriteLock\u003c'_, T, R\u003e {\n\tpub fn scoped_lock\u003c'a, Ret\u003e(\u0026'a self, key: impl Keyable, f: impl Fn(\u0026'a mut T) -\u003e Ret) -\u003e Ret {\n\t\tself.0.scoped_write(key, f)\n\t}\n\n\tpub fn scoped_try_lock\u003c'a, Key: Keyable, Ret\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(\u0026'a mut T) -\u003e Ret,\n\t) -\u003e Result\u003cRet, Key\u003e {\n\t\tself.0.scoped_try_write(key, f)\n\t}\n\n\t/// Locks the underlying [`RwLock`] with exclusive write access, blocking\n\t/// the current until it can be acquired.\n\t///\n\t/// This function will not return while other writers or readers currently\n\t/// have access to the lock.\n\t///\n\t/// Returns an RAII guard which will drop the write access of this `RwLock`\n\t/// when dropped.\n\t///\n\t/// Because this method takes a [`ThreadKey`], it's not possible for this\n\t/// method to cause a deadlock.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, RwLock};\n\t/// use happylock::rwlock::WriteLock;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(1);\n\t/// let writer = WriteLock::new(\u0026lock);\n\t///\n\t/// let mut n = writer.lock(key);\n\t/// *n += 2;\n\t/// ```\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\t#[must_use]\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e RwLockWriteGuard\u003c'_, T, R\u003e {\n\t\tself.0.write(key)\n\t}\n\n\t/// Attempts to lock the underlying [`RwLock`] with exclusive write access.\n\t///\n\t/// This function does not block. If the lock could not be acquired at this\n\t/// time, then `None` is returned. Otherwise, an RAII guard is returned\n\t/// which will release the lock when it is dropped.\n\t///\n\t/// This function does not provide any guarantees with respect to the\n\t/// ordering of whether contentious readers or writers will acquire the\n\t/// lock first.\n\t///\n\t/// # Errors\n\t///\n\t/// If the [`RwLock`] could not be acquired because it was already locked,\n\t/// then an error will be returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::rwlock::WriteLock;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(1);\n\t/// let writer = WriteLock::new(\u0026lock);\n\t///\n\t/// match writer.try_lock(key) {\n\t/// Ok(n) =\u003e assert_eq!(*n, 1),\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t/// ```\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e Result\u003cRwLockWriteGuard\u003c'_, T, R\u003e, ThreadKey\u003e {\n\t\tself.0.try_write(key)\n\t}\n\n\t// There's no `try_lock_no_key`. Instead, `try_read_no_key` is called on\n\t// the referenced `RwLock`.\n\n\t/// Immediately drops the guard, and consequently releases the exclusive\n\t/// lock on the underlying [`RwLock`].\n\t///\n\t/// This function is equivalent to calling [`drop`] on the guard, except\n\t/// that it returns the key that was used to create it. Alternately, the\n\t/// guard will be automatically dropped when it goes out of scope.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::rwlock::WriteLock;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(0);\n\t/// let writer = WriteLock::new(\u0026lock);\n\t///\n\t/// let mut guard = writer.lock(key);\n\t/// *guard += 20;\n\t/// let key = WriteLock::unlock(guard);\n\t/// ```\n\t#[must_use]\n\tpub fn unlock(guard: RwLockWriteGuard\u003c'_, T, R\u003e) -\u003e ThreadKey {\n\t\tRwLock::unlock_write(guard)\n\t}\n}\n","traces":[{"line":11,"address":[969664,969648],"length":1,"stats":{"Line":1}},{"line":12,"address":[],"length":0,"stats":{"Line":1}},{"line":15,"address":[],"length":0,"stats":{"Line":1}},{"line":16,"address":[],"length":0,"stats":{"Line":1}},{"line":19,"address":[969712,969744],"length":1,"stats":{"Line":1}},{"line":20,"address":[],"length":0,"stats":{"Line":1}},{"line":23,"address":[],"length":0,"stats":{"Line":1}},{"line":24,"address":[],"length":0,"stats":{"Line":1}},{"line":27,"address":[],"length":0,"stats":{"Line":0}},{"line":28,"address":[],"length":0,"stats":{"Line":0}},{"line":31,"address":[],"length":0,"stats":{"Line":0}},{"line":32,"address":[],"length":0,"stats":{"Line":0}},{"line":35,"address":[],"length":0,"stats":{"Line":0}},{"line":36,"address":[],"length":0,"stats":{"Line":0}},{"line":51,"address":[],"length":0,"stats":{"Line":2}},{"line":52,"address":[],"length":0,"stats":{"Line":2}},{"line":55,"address":[],"length":0,"stats":{"Line":1}},{"line":56,"address":[],"length":0,"stats":{"Line":1}},{"line":59,"address":[970096],"length":1,"stats":{"Line":1}},{"line":60,"address":[],"length":0,"stats":{"Line":1}},{"line":94,"address":[],"length":0,"stats":{"Line":1}},{"line":95,"address":[],"length":0,"stats":{"Line":1}},{"line":100,"address":[970128],"length":1,"stats":{"Line":1}},{"line":101,"address":[],"length":0,"stats":{"Line":1}},{"line":117,"address":[970160,970144],"length":1,"stats":{"Line":2}},{"line":118,"address":[],"length":0,"stats":{"Line":0}},{"line":123,"address":[],"length":0,"stats":{"Line":1}},{"line":124,"address":[],"length":0,"stats":{"Line":1}},{"line":127,"address":[970208],"length":1,"stats":{"Line":1}},{"line":132,"address":[970217],"length":1,"stats":{"Line":1}},{"line":163,"address":[],"length":0,"stats":{"Line":1}},{"line":164,"address":[],"length":0,"stats":{"Line":1}},{"line":197,"address":[],"length":0,"stats":{"Line":1}},{"line":198,"address":[],"length":0,"stats":{"Line":1}},{"line":226,"address":[],"length":0,"stats":{"Line":1}},{"line":227,"address":[],"length":0,"stats":{"Line":1}}],"covered":29,"coverable":36},{"path":["/","home","botahamec","Projects","happylock","src","rwlock.rs"],"content":"use std::cell::UnsafeCell;\nuse std::marker::PhantomData;\n\nuse lock_api::RawRwLock;\n\nuse crate::poisonable::PoisonFlag;\nuse crate::ThreadKey;\n\nmod rwlock;\n\nmod read_lock;\nmod write_lock;\n\nmod read_guard;\nmod write_guard;\n\n#[cfg(feature = \"spin\")]\npub type SpinRwLock\u003cT\u003e = RwLock\u003cT, spin::RwLock\u003c()\u003e\u003e;\n\n#[cfg(feature = \"parking_lot\")]\npub type ParkingRwLock\u003cT\u003e = RwLock\u003cT, parking_lot::RawRwLock\u003e;\n\n/// A reader-writer lock\n///\n/// This type of lock allows a number of readers or at most one writer at any\n/// point in time. The write portion of this lock typically allows modification\n/// of the underlying data (exclusive access) and the read portion of this lock\n/// typically allows for read-only access (shared access).\n///\n/// In comparison, a [`Mutex`] does not distinguish between readers or writers\n/// that acquire the lock, therefore blocking any threads waiting for the lock\n/// to become available. An `RwLock` will allow any number of readers to\n/// acquire the lock as long as a writer is not holding the lock.\n///\n/// The type parameter T represents the data that this lock protects. It is\n/// required that T satisfies [`Send`] to be shared across threads and [`Sync`]\n/// to allow concurrent access through readers. The RAII guard returned from\n/// the locking methods implement [`Deref`] (and [`DerefMut`] for the `write`\n/// methods) to allow access to the content of the lock.\n///\n/// Locking the mutex on a thread that already locked it is impossible, due to\n/// the requirement of the [`ThreadKey`]. Therefore, this will never deadlock.\n///\n/// [`ThreadKey`]: `crate::ThreadKey`\n/// [`Mutex`]: `crate::mutex::Mutex`\n/// [`Deref`]: `std::ops::Deref`\n/// [`DerefMut`]: `std::ops::DerefMut`\npub struct RwLock\u003cT: ?Sized, R\u003e {\n\traw: R,\n\tpoison: PoisonFlag,\n\tdata: UnsafeCell\u003cT\u003e,\n}\n\n/// Grants read access to an [`RwLock`]\n///\n/// This structure is designed to be used in a [`LockCollection`] to indicate\n/// that only read access is needed to the data.\n///\n/// [`LockCollection`]: `crate::LockCollection`\n#[repr(transparent)]\npub struct ReadLock\u003c'l, T: ?Sized, R\u003e(\u0026'l RwLock\u003cT, R\u003e);\n\n/// Grants write access to an [`RwLock`]\n///\n/// This structure is designed to be used in a [`LockCollection`] to indicate\n/// that write access is needed to the data.\n///\n/// [`LockCollection`]: `crate::LockCollection`\n#[repr(transparent)]\npub struct WriteLock\u003c'l, T: ?Sized, R\u003e(\u0026'l RwLock\u003cT, R\u003e);\n\n/// RAII structure that unlocks the shared read access to a [`RwLock`]\n///\n/// This is similar to [`RwLockReadRef`], except it does not hold a\n/// [`Keyable`].\npub struct RwLockReadRef\u003c'a, T: ?Sized, R: RawRwLock\u003e(\n\t\u0026'a RwLock\u003cT, R\u003e,\n\tPhantomData\u003cR::GuardMarker\u003e,\n);\n\n/// RAII structure that unlocks the exclusive write access to a [`RwLock`]\n///\n/// This is similar to [`RwLockWriteRef`], except it does not hold a\n/// [`Keyable`].\npub struct RwLockWriteRef\u003c'a, T: ?Sized, R: RawRwLock\u003e(\n\t\u0026'a RwLock\u003cT, R\u003e,\n\tPhantomData\u003cR::GuardMarker\u003e,\n);\n\n/// RAII structure used to release the shared read access of a lock when\n/// dropped.\n///\n/// This structure is created by the [`read`] and [`try_read`] methods on\n/// [`RwLock`].\n///\n/// [`read`]: `RwLock::read`\n/// [`try_read`]: `RwLock::try_read`\npub struct RwLockReadGuard\u003c'a, T: ?Sized, R: RawRwLock\u003e {\n\trwlock: RwLockReadRef\u003c'a, T, R\u003e,\n\tthread_key: ThreadKey,\n}\n\n/// RAII structure used to release the exclusive write access of a lock when\n/// dropped.\n///\n/// This structure is created by the [`write`] and [`try_write`] methods on\n/// [`RwLock`]\n///\n/// [`try_write`]: `RwLock::try_write`\npub struct RwLockWriteGuard\u003c'a, T: ?Sized, R: RawRwLock\u003e {\n\trwlock: RwLockWriteRef\u003c'a, T, R\u003e,\n\tthread_key: ThreadKey,\n}\n\n#[cfg(test)]\nmod tests {\n\tuse crate::lockable::Lockable;\n\tuse crate::lockable::RawLock;\n\tuse crate::LockCollection;\n\tuse crate::RwLock;\n\tuse crate::ThreadKey;\n\n\tuse super::*;\n\n\t#[test]\n\tfn unlocked_when_initialized() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\n\t\tassert!(!lock.is_locked());\n\t\tassert!(lock.try_write(key).is_ok());\n\t}\n\n\t#[test]\n\tfn read_lock_unlocked_when_initialized() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet reader = ReadLock::new(\u0026lock);\n\n\t\tassert!(reader.try_lock(key).is_ok());\n\t}\n\n\t#[test]\n\tfn read_lock_from_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::from(\"Hello, world!\");\n\t\tlet reader = ReadLock::from(\u0026lock);\n\n\t\tlet guard = reader.lock(key);\n\t\tassert_eq!(*guard, \"Hello, world!\");\n\t}\n\n\t#[test]\n\tfn read_lock_scoped_works() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(42);\n\t\tlet reader = ReadLock::new(\u0026lock);\n\n\t\treader.scoped_lock(\u0026mut key, |num| assert_eq!(*num, 42));\n\t}\n\n\t#[test]\n\tfn read_lock_scoped_try_fails_during_write() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(42);\n\t\tlet reader = ReadLock::new(\u0026lock);\n\t\tlet guard = lock.write(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = reader.scoped_try_lock(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn write_lock_unlocked_when_initialized() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet writer = WriteLock::new(\u0026lock);\n\n\t\tassert!(writer.try_lock(key).is_ok());\n\t}\n\n\t#[test]\n\tfn read_lock_get_ptrs() {\n\t\tlet rwlock = RwLock::new(5);\n\t\tlet readlock = ReadLock::new(\u0026rwlock);\n\t\tlet mut lock_ptrs = Vec::new();\n\t\treadlock.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tassert!(std::ptr::addr_eq(lock_ptrs[0], \u0026readlock));\n\t}\n\n\t#[test]\n\tfn write_lock_get_ptrs() {\n\t\tlet rwlock = RwLock::new(5);\n\t\tlet writelock = WriteLock::new(\u0026rwlock);\n\t\tlet mut lock_ptrs = Vec::new();\n\t\twritelock.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tassert!(std::ptr::addr_eq(lock_ptrs[0], \u0026writelock));\n\t}\n\n\t#[test]\n\tfn write_lock_scoped_works() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(42);\n\t\tlet writer = WriteLock::new(\u0026lock);\n\n\t\twriter.scoped_lock(\u0026mut key, |num| assert_eq!(*num, 42));\n\t}\n\n\t#[test]\n\tfn write_lock_scoped_try_fails_during_write() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(42);\n\t\tlet writer = WriteLock::new(\u0026lock);\n\t\tlet guard = lock.write(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = writer.scoped_try_lock(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn locked_after_read() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\n\t\tlet guard = lock.read(key);\n\n\t\tassert!(lock.is_locked());\n\t\tdrop(guard)\n\t}\n\n\t#[test]\n\tfn locked_after_using_read_lock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet reader = ReadLock::new(\u0026lock);\n\n\t\tlet guard = reader.lock(key);\n\n\t\tassert!(lock.is_locked());\n\t\tdrop(guard)\n\t}\n\n\t#[test]\n\tfn locked_after_write() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\n\t\tlet guard = lock.write(key);\n\n\t\tassert!(lock.is_locked());\n\t\tdrop(guard)\n\t}\n\n\t#[test]\n\tfn locked_after_using_write_lock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet writer = WriteLock::new(\u0026lock);\n\n\t\tlet guard = writer.lock(key);\n\n\t\tassert!(lock.is_locked());\n\t\tdrop(guard)\n\t}\n\n\t#[test]\n\tfn locked_after_scoped_write() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"Hello, world!\");\n\n\t\tlock.scoped_write(\u0026mut key, |guard| {\n\t\t\tassert!(lock.is_locked());\n\t\t\tassert_eq!(*guard, \"Hello, world!\");\n\n\t\t\tstd::thread::scope(|s| {\n\t\t\t\ts.spawn(|| {\n\t\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\t\tassert!(lock.try_read(key).is_err());\n\t\t\t\t});\n\t\t\t})\n\t\t})\n\t}\n\n\t#[test]\n\tfn get_mut_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mut lock = crate::RwLock::from(42);\n\n\t\tlet mut_ref = lock.get_mut();\n\t\t*mut_ref = 24;\n\n\t\tlock.scoped_read(key, |guard| assert_eq!(*guard, 24))\n\t}\n\n\t#[test]\n\tfn try_write_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"Hello\");\n\t\tlet guard = lock.write(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = lock.try_write(key);\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn try_read_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"Hello\");\n\t\tlet guard = lock.write(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = lock.try_read(key);\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn read_display_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet guard = lock.read(key);\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn write_display_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet guard = lock.write(key);\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn read_ref_display_works() {\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet guard = unsafe { lock.try_read_no_key().unwrap() };\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn write_ref_display_works() {\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet guard = unsafe { lock.try_write_no_key().unwrap() };\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn dropping_read_ref_releases_rwlock() {\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\n\t\tlet guard = unsafe { lock.try_read_no_key().unwrap() };\n\t\tdrop(guard);\n\n\t\tassert!(!lock.is_locked());\n\t}\n\n\t#[test]\n\tfn dropping_write_guard_releases_rwlock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\n\t\tlet guard = lock.write(key);\n\t\tdrop(guard);\n\n\t\tassert!(!lock.is_locked());\n\t}\n\n\t#[test]\n\tfn unlock_write() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"Hello, world\");\n\n\t\tlet mut guard = lock.write(key);\n\t\t*guard = \"Goodbye, world!\";\n\t\tlet key = RwLock::unlock_write(guard);\n\n\t\tlet guard = lock.read(key);\n\t\tassert_eq!(*guard, \"Goodbye, world!\");\n\t}\n\n\t#[test]\n\tfn unlock_read() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"Hello, world\");\n\n\t\tlet guard = lock.read(key);\n\t\tassert_eq!(*guard, \"Hello, world\");\n\t\tlet key = RwLock::unlock_read(guard);\n\n\t\tlet guard = lock.write(key);\n\t\tassert_eq!(*guard, \"Hello, world\");\n\t}\n\n\t#[test]\n\tfn unlock_read_lock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"Hello, world\");\n\t\tlet reader = ReadLock::new(\u0026lock);\n\n\t\tlet guard = reader.lock(key);\n\t\tlet key = ReadLock::unlock(guard);\n\n\t\tlock.write(key);\n\t}\n\n\t#[test]\n\tfn unlock_write_lock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"Hello, world\");\n\t\tlet writer = WriteLock::from(\u0026lock);\n\n\t\tlet guard = writer.lock(key);\n\t\tlet key = WriteLock::unlock(guard);\n\n\t\tlock.write(key);\n\t}\n\n\t#[test]\n\tfn read_lock_in_collection() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"hi\");\n\t\tlet collection = LockCollection::try_new(ReadLock::new(\u0026lock)).unwrap();\n\n\t\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard, \"hi\");\n\t\t});\n\t\tcollection.scoped_read(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard, \"hi\");\n\t\t});\n\t\tassert!(collection\n\t\t\t.scoped_try_lock(\u0026mut key, |guard| {\n\t\t\t\tassert_eq!(*guard, \"hi\");\n\t\t\t})\n\t\t\t.is_ok());\n\t\tassert!(collection\n\t\t\t.scoped_try_read(\u0026mut key, |guard| {\n\t\t\t\tassert_eq!(*guard, \"hi\");\n\t\t\t})\n\t\t\t.is_ok());\n\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(**guard, \"hi\");\n\n\t\tlet key = LockCollection::\u003cReadLock\u003c_, _\u003e\u003e::unlock(guard);\n\t\tlet guard = collection.read(key);\n\t\tassert_eq!(**guard, \"hi\");\n\n\t\tlet key = LockCollection::\u003cReadLock\u003c_, _\u003e\u003e::unlock(guard);\n\t\tlet guard = lock.write(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_lock(key);\n\t\t\t\tassert!(guard.is_err());\n\t\t\t});\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_read(key);\n\t\t\t\tassert!(guard.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn write_lock_in_collection() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"hi\");\n\t\tlet collection = LockCollection::try_new(WriteLock::new(\u0026lock)).unwrap();\n\n\t\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard, \"hi\");\n\t\t});\n\t\tassert!(collection\n\t\t\t.scoped_try_lock(\u0026mut key, |guard| {\n\t\t\t\tassert_eq!(*guard, \"hi\");\n\t\t\t})\n\t\t\t.is_ok());\n\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(**guard, \"hi\");\n\n\t\tlet key = LockCollection::\u003cWriteLock\u003c_, _\u003e\u003e::unlock(guard);\n\t\tlet guard = lock.write(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_lock(key);\n\t\t\t\tassert!(guard.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn read_ref_as_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = LockCollection::new(crate::RwLock::new(\"hi\"));\n\t\tlet guard = lock.read(key);\n\n\t\tassert_eq!(*(*guard).as_ref(), \"hi\");\n\t}\n\n\t#[test]\n\tfn read_guard_as_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"hi\");\n\t\tlet guard = lock.read(key);\n\n\t\tassert_eq!(*guard.as_ref(), \"hi\");\n\t}\n\n\t#[test]\n\tfn write_ref_as_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = LockCollection::new(crate::RwLock::new(\"hi\"));\n\t\tlet guard = lock.lock(key);\n\n\t\tassert_eq!(*(*guard).as_ref(), \"hi\");\n\t}\n\n\t#[test]\n\tfn write_guard_as_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"hi\");\n\t\tlet guard = lock.write(key);\n\n\t\tassert_eq!(*guard.as_ref(), \"hi\");\n\t}\n\n\t#[test]\n\tfn write_guard_as_mut() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"hi\");\n\t\tlet mut guard = lock.write(key);\n\n\t\tassert_eq!(*guard.as_mut(), \"hi\");\n\t\t*guard.as_mut() = \"foo\";\n\t\tassert_eq!(*guard.as_mut(), \"foo\");\n\t}\n\n\t#[test]\n\tfn poison_read_lock() {\n\t\tlet lock = crate::RwLock::new(\"hi\");\n\t\tlet reader = ReadLock::new(\u0026lock);\n\n\t\treader.poison();\n\t\tassert!(lock.poison.is_poisoned());\n\t}\n\n\t#[test]\n\tfn poison_write_lock() {\n\t\tlet lock = crate::RwLock::new(\"hi\");\n\t\tlet reader = WriteLock::new(\u0026lock);\n\n\t\treader.poison();\n\t\tassert!(lock.poison.is_poisoned());\n\t}\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","src","thread.rs"],"content":"use std::marker::PhantomData;\n\nmod scope;\n\n#[derive(Debug)]\npub struct Scope\u003c'scope, 'env: 'scope\u003e(PhantomData\u003c(\u0026'env (), \u0026'scope ())\u003e);\n\n#[derive(Debug)]\npub struct ScopedJoinHandle\u003c'scope, T\u003e {\n\thandle: std::thread::JoinHandle\u003cT\u003e,\n\t_phantom: PhantomData\u003c\u0026'scope ()\u003e,\n}\n\npub struct JoinHandle\u003cT\u003e {\n\thandle: std::thread::JoinHandle\u003cT\u003e,\n\tkey: crate::ThreadKey,\n}\n\npub struct ThreadBuilder(std::thread::Builder);\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","evil_mutex.rs"],"content":"use std::sync::Arc;\n\nuse happylock::collection::{BoxedLockCollection, RetryingLockCollection};\nuse happylock::mutex::Mutex;\nuse happylock::ThreadKey;\nuse lock_api::{GuardNoSend, RawMutex};\n\nstruct EvilMutex {\n\tinner: parking_lot::RawMutex,\n}\n\nunsafe impl RawMutex for EvilMutex {\n\t#[allow(clippy::declare_interior_mutable_const)]\n\tconst INIT: Self = Self {\n\t\tinner: parking_lot::RawMutex::INIT,\n\t};\n\n\ttype GuardMarker = GuardNoSend;\n\n\tfn lock(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn try_lock(\u0026self) -\u003e bool {\n\t\tself.inner.try_lock()\n\t}\n\n\tunsafe fn unlock(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n}\n\n#[test]\nfn boxed_mutexes() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet good_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(5));\n\tlet evil_mutex: Arc\u003cMutex\u003ci32, EvilMutex\u003e\u003e = Arc::new(Mutex::new(7));\n\tlet useless_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(10));\n\tlet c_good = Arc::clone(\u0026good_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\t_ = collection.lock(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(good_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n\tassert!(evil_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n}\n\n#[test]\nfn retrying_mutexes() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet good_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(5));\n\tlet evil_mutex: Arc\u003cMutex\u003ci32, EvilMutex\u003e\u003e = Arc::new(Mutex::new(7));\n\tlet useless_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(10));\n\tlet c_good = Arc::clone(\u0026good_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tRetryingLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\tcollection.lock(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(good_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n\tassert!(evil_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","evil_rwlock.rs"],"content":"use std::panic::AssertUnwindSafe;\nuse std::sync::Arc;\n\nuse happylock::collection::{BoxedLockCollection, RetryingLockCollection};\nuse happylock::rwlock::RwLock;\nuse happylock::ThreadKey;\nuse lock_api::{GuardNoSend, RawRwLock};\n\nstruct EvilRwLock {\n\tinner: parking_lot::RawRwLock,\n}\n\nunsafe impl RawRwLock for EvilRwLock {\n\t#[allow(clippy::declare_interior_mutable_const)]\n\tconst INIT: Self = Self {\n\t\tinner: parking_lot::RawRwLock::INIT,\n\t};\n\n\ttype GuardMarker = GuardNoSend;\n\n\tfn lock_shared(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn try_lock_shared(\u0026self) -\u003e bool {\n\t\tself.inner.try_lock_shared()\n\t}\n\n\tunsafe fn unlock_shared(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn lock_exclusive(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn try_lock_exclusive(\u0026self) -\u003e bool {\n\t\tself.inner.try_lock_exclusive()\n\t}\n\n\tunsafe fn unlock_exclusive(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n}\n\n#[test]\nfn boxed_rwlocks() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet good_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(5));\n\tlet evil_mutex: Arc\u003cRwLock\u003ci32, EvilRwLock\u003e\u003e = Arc::new(RwLock::new(7));\n\tlet useless_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(10));\n\tlet c_good = Arc::clone(\u0026good_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\t_ = collection.lock(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(good_mutex.scoped_try_write(\u0026mut key, |_| {}).is_ok());\n\tassert!(evil_mutex.scoped_try_write(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_write(\u0026mut key, |_| {}).is_ok());\n\n\tstd::thread::scope(|s| {\n\t\ts.spawn(|| {\n\t\t\tlet evil_mutex = AssertUnwindSafe(evil_mutex);\n\t\t\tlet r = std::panic::catch_unwind(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tevil_mutex.write(key);\n\t\t\t});\n\n\t\t\tassert!(r.is_err());\n\t\t});\n\n\t\ts.spawn(|| {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\tgood_mutex.write(key);\n\t\t});\n\t});\n}\n\n#[test]\nfn retrying_rwlocks() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet good_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(5));\n\tlet evil_mutex: Arc\u003cRwLock\u003ci32, EvilRwLock\u003e\u003e = Arc::new(RwLock::new(7));\n\tlet useless_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(10));\n\tlet c_good = Arc::clone(\u0026good_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tRetryingLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\tcollection.lock(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(good_mutex.scoped_try_write(\u0026mut key, |_| {}).is_ok());\n\tassert!(evil_mutex.scoped_try_write(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_write(\u0026mut key, |_| {}).is_ok());\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","evil_try_mutex.rs"],"content":"use std::sync::Arc;\n\nuse happylock::{\n\tcollection::{BoxedLockCollection, RetryingLockCollection},\n\tmutex::Mutex,\n\tThreadKey,\n};\nuse lock_api::{GuardNoSend, RawMutex};\n\nstruct EvilMutex {\n\tinner: parking_lot::RawMutex,\n}\n\nunsafe impl RawMutex for EvilMutex {\n\t#[allow(clippy::declare_interior_mutable_const)]\n\tconst INIT: Self = Self {\n\t\tinner: parking_lot::RawMutex::INIT,\n\t};\n\n\ttype GuardMarker = GuardNoSend;\n\n\tfn lock(\u0026self) {\n\t\tself.inner.lock()\n\t}\n\n\tfn try_lock(\u0026self) -\u003e bool {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tunsafe fn unlock(\u0026self) {\n\t\tself.inner.unlock()\n\t}\n}\n\n#[test]\nfn boxed_mutexes() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet good_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(5));\n\tlet evil_mutex: Arc\u003cMutex\u003ci32, EvilMutex\u003e\u003e = Arc::new(Mutex::new(7));\n\tlet useless_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(10));\n\tlet c_good = Arc::clone(\u0026good_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\tlet g = collection.try_lock(key);\n\t\tprintln!(\"{}\", g.unwrap().1);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(good_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n\tassert!(evil_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n}\n\n#[test]\nfn retrying_mutexes() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet good_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(5));\n\tlet evil_mutex: Arc\u003cMutex\u003ci32, EvilMutex\u003e\u003e = Arc::new(Mutex::new(7));\n\tlet useless_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(10));\n\tlet c_good = Arc::clone(\u0026good_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tRetryingLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\tlet _ = collection.try_lock(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(good_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n\tassert!(evil_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","evil_try_rwlock.rs"],"content":"use std::sync::Arc;\n\nuse happylock::collection::{BoxedLockCollection, RetryingLockCollection};\nuse happylock::rwlock::RwLock;\nuse happylock::ThreadKey;\nuse lock_api::{GuardNoSend, RawRwLock};\n\nstruct EvilRwLock {\n\tinner: parking_lot::RawRwLock,\n}\n\nunsafe impl RawRwLock for EvilRwLock {\n\t#[allow(clippy::declare_interior_mutable_const)]\n\tconst INIT: Self = Self {\n\t\tinner: parking_lot::RawRwLock::INIT,\n\t};\n\n\ttype GuardMarker = GuardNoSend;\n\n\tfn lock_shared(\u0026self) {\n\t\tself.inner.lock_shared()\n\t}\n\n\tfn try_lock_shared(\u0026self) -\u003e bool {\n\t\tpanic!(\"mwahahahaha\")\n\t}\n\n\tunsafe fn unlock_shared(\u0026self) {\n\t\tself.inner.unlock_shared()\n\t}\n\n\tfn lock_exclusive(\u0026self) {\n\t\tself.inner.lock_exclusive()\n\t}\n\n\tfn try_lock_exclusive(\u0026self) -\u003e bool {\n\t\tpanic!(\"mwahahahaha\")\n\t}\n\n\tunsafe fn unlock_exclusive(\u0026self) {\n\t\tself.inner.unlock_exclusive()\n\t}\n}\n\n#[test]\nfn boxed_rwlocks() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet good_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(5));\n\tlet evil_mutex: Arc\u003cRwLock\u003ci32, EvilRwLock\u003e\u003e = Arc::new(RwLock::new(7));\n\tlet useless_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(10));\n\tlet c_good = Arc::clone(\u0026good_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\tlet _ = collection.try_read(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(good_mutex.scoped_try_read(\u0026mut key, |_| {}).is_ok());\n\tassert!(evil_mutex.scoped_try_read(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_read(\u0026mut key, |_| {}).is_ok());\n}\n\n#[test]\nfn retrying_rwlocks() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet good_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(5));\n\tlet evil_mutex: Arc\u003cRwLock\u003ci32, EvilRwLock\u003e\u003e = Arc::new(RwLock::new(7));\n\tlet useless_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(10));\n\tlet c_good = Arc::clone(\u0026good_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tRetryingLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\t_ = collection.try_read(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(good_mutex.scoped_try_read(\u0026mut key, |_| {}).is_ok());\n\tassert!(evil_mutex.scoped_try_read(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_read(\u0026mut key, |_| {}).is_ok());\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","evil_unlock_mutex.rs"],"content":"use std::sync::Arc;\n\nuse happylock::collection::{BoxedLockCollection, RetryingLockCollection};\nuse happylock::mutex::Mutex;\nuse happylock::ThreadKey;\nuse lock_api::{GuardNoSend, RawMutex};\n\nstruct KindaEvilMutex {\n\tinner: parking_lot::RawMutex,\n}\n\nstruct EvilMutex {}\n\nunsafe impl RawMutex for KindaEvilMutex {\n\t#[allow(clippy::declare_interior_mutable_const)]\n\tconst INIT: Self = Self {\n\t\tinner: parking_lot::RawMutex::INIT,\n\t};\n\n\ttype GuardMarker = GuardNoSend;\n\n\tfn lock(\u0026self) {\n\t\tself.inner.lock()\n\t}\n\n\tfn try_lock(\u0026self) -\u003e bool {\n\t\tself.inner.try_lock()\n\t}\n\n\tunsafe fn unlock(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n}\n\nunsafe impl RawMutex for EvilMutex {\n\t#[allow(clippy::declare_interior_mutable_const)]\n\tconst INIT: Self = Self {};\n\n\ttype GuardMarker = GuardNoSend;\n\n\tfn lock(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn try_lock(\u0026self) -\u003e bool {\n\t\tpanic!(\"mwahahahaha\")\n\t}\n\n\tunsafe fn unlock(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n}\n\n#[test]\nfn boxed_mutexes() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet kinda_evil_mutex: Arc\u003cMutex\u003ci32, KindaEvilMutex\u003e\u003e = Arc::new(Mutex::new(5));\n\tlet evil_mutex: Arc\u003cMutex\u003ci32, EvilMutex\u003e\u003e = Arc::new(Mutex::new(7));\n\tlet useless_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(10));\n\tlet c_good = Arc::clone(\u0026kinda_evil_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\t_ = collection.lock(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(kinda_evil_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n\tassert!(evil_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n}\n\n#[test]\nfn retrying_mutexes() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet kinda_evil_mutex: Arc\u003cMutex\u003ci32, KindaEvilMutex\u003e\u003e = Arc::new(Mutex::new(5));\n\tlet evil_mutex: Arc\u003cMutex\u003ci32, EvilMutex\u003e\u003e = Arc::new(Mutex::new(7));\n\tlet useless_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(10));\n\tlet c_good = Arc::clone(\u0026kinda_evil_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tRetryingLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\tcollection.lock(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(kinda_evil_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n\tassert!(evil_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","evil_unlock_rwlock.rs"],"content":"use std::sync::Arc;\n\nuse happylock::collection::{BoxedLockCollection, RetryingLockCollection};\nuse happylock::rwlock::RwLock;\nuse happylock::ThreadKey;\nuse lock_api::{GuardNoSend, RawRwLock};\n\nstruct KindaEvilRwLock {\n\tinner: parking_lot::RawRwLock,\n}\n\nstruct EvilRwLock {}\n\nunsafe impl RawRwLock for KindaEvilRwLock {\n\t#[allow(clippy::declare_interior_mutable_const)]\n\tconst INIT: Self = Self {\n\t\tinner: parking_lot::RawRwLock::INIT,\n\t};\n\n\ttype GuardMarker = GuardNoSend;\n\n\tfn lock_shared(\u0026self) {\n\t\tself.inner.lock_shared()\n\t}\n\n\tfn try_lock_shared(\u0026self) -\u003e bool {\n\t\tself.inner.try_lock_shared()\n\t}\n\n\tunsafe fn unlock_shared(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn lock_exclusive(\u0026self) {\n\t\tself.inner.lock_exclusive()\n\t}\n\n\tfn try_lock_exclusive(\u0026self) -\u003e bool {\n\t\tself.inner.try_lock_exclusive()\n\t}\n\n\tunsafe fn unlock_exclusive(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n}\n\nunsafe impl RawRwLock for EvilRwLock {\n\t#[allow(clippy::declare_interior_mutable_const)]\n\tconst INIT: Self = Self {};\n\n\ttype GuardMarker = GuardNoSend;\n\n\tfn lock_shared(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn try_lock_shared(\u0026self) -\u003e bool {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tunsafe fn unlock_shared(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn lock_exclusive(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn try_lock_exclusive(\u0026self) -\u003e bool {\n\t\tpanic!(\"mwahahahaha\")\n\t}\n\n\tunsafe fn unlock_exclusive(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n}\n\n#[test]\nfn boxed_rwlocks() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet kinda_evil_mutex: RwLock\u003ci32, KindaEvilRwLock\u003e = RwLock::new(5);\n\tlet evil_mutex: RwLock\u003ci32, EvilRwLock\u003e = RwLock::new(7);\n\tlet useless_mutex: RwLock\u003ci32, parking_lot::RawRwLock\u003e = RwLock::new(10);\n\n\tlet r = std::thread::scope(|s| {\n\t\tlet r = s\n\t\t\t.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet collection =\n\t\t\t\t\tBoxedLockCollection::try_new((\u0026kinda_evil_mutex, \u0026evil_mutex, \u0026useless_mutex))\n\t\t\t\t\t\t.unwrap();\n\t\t\t\t_ = collection.read(key);\n\t\t\t})\n\t\t\t.join();\n\n\t\tr\n\t});\n\n\tassert!(r.is_err());\n\tassert!(kinda_evil_mutex.scoped_try_write(\u0026mut key, |_| {}).is_err());\n\tassert!(evil_mutex.scoped_try_write(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_write(\u0026mut key, |_| {}).is_ok());\n}\n\n#[test]\nfn retrying_rwlocks() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet kinda_evil_mutex: Arc\u003cRwLock\u003ci32, KindaEvilRwLock\u003e\u003e = Arc::new(RwLock::new(5));\n\tlet evil_mutex: Arc\u003cRwLock\u003ci32, EvilRwLock\u003e\u003e = Arc::new(RwLock::new(7));\n\tlet useless_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(10));\n\tlet c_good = Arc::clone(\u0026kinda_evil_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tRetryingLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\tcollection.read(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(kinda_evil_mutex.scoped_try_write(\u0026mut key, |_| {}).is_err());\n\tassert!(evil_mutex.scoped_try_write(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_write(\u0026mut key, |_| {}).is_ok());\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","forget.rs"],"content":"use happylock::{Mutex, ThreadKey};\n\n#[test]\nfn no_new_threadkey_when_forgetting_lock() {\n\tlet key = ThreadKey::get().unwrap();\n\tlet mutex = Mutex::new(\"foo\".to_string());\n\n\tlet guard = mutex.lock(key);\n\tstd::mem::forget(guard);\n\n\tassert!(ThreadKey::get().is_none());\n}\n\n#[test]\nfn no_new_threadkey_in_scoped_lock() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet mutex = Mutex::new(\"foo\".to_string());\n\n\tmutex.scoped_lock(\u0026mut key, |_| {\n\t\tassert!(ThreadKey::get().is_none());\n\t});\n\n\tmutex.lock(key);\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","retry.rs"],"content":"use std::time::Duration;\n\nuse happylock::{collection::RetryingLockCollection, Mutex, ThreadKey};\n\nstatic MUTEX_1: Mutex\u003ci32\u003e = Mutex::new(1);\nstatic MUTEX_2: Mutex\u003ci32\u003e = Mutex::new(2);\nstatic MUTEX_3: Mutex\u003ci32\u003e = Mutex::new(3);\n\nfn thread_1() {\n\tlet key = ThreadKey::get().unwrap();\n\tlet mut guard = MUTEX_2.lock(key);\n\tstd::thread::sleep(Duration::from_millis(100));\n\t*guard = 5;\n}\n\nfn thread_2() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tstd::thread::sleep(Duration::from_millis(50));\n\tlet collection = RetryingLockCollection::try_new([\u0026MUTEX_1, \u0026MUTEX_2, \u0026MUTEX_3]).unwrap();\n\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\tassert_eq!(*guard[0], 4);\n\t\tassert_eq!(*guard[1], 5);\n\t\tassert_eq!(*guard[2], 3);\n\t});\n}\n\nfn thread_3() {\n\tlet key = ThreadKey::get().unwrap();\n\tstd::thread::sleep(Duration::from_millis(75));\n\tlet mut guard = MUTEX_1.lock(key);\n\tstd::thread::sleep(Duration::from_millis(100));\n\t*guard = 4;\n}\n\nfn thread_4() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tstd::thread::sleep(Duration::from_millis(25));\n\tlet collection = RetryingLockCollection::try_new([\u0026MUTEX_1, \u0026MUTEX_2]).unwrap();\n\tassert!(collection.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n}\n\n#[test]\nfn retries() {\n\tlet t1 = std::thread::spawn(thread_1);\n\tlet t2 = std::thread::spawn(thread_2);\n\tlet t3 = std::thread::spawn(thread_3);\n\tlet t4 = std::thread::spawn(thread_4);\n\n\tt1.join().unwrap();\n\tt2.join().unwrap();\n\tt3.join().unwrap();\n\tt4.join().unwrap();\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","retry_rw.rs"],"content":"use std::time::Duration;\n\nuse happylock::{collection::RetryingLockCollection, RwLock, ThreadKey};\n\nstatic RWLOCK_1: RwLock\u003ci32\u003e = RwLock::new(1);\nstatic RWLOCK_2: RwLock\u003ci32\u003e = RwLock::new(2);\nstatic RWLOCK_3: RwLock\u003ci32\u003e = RwLock::new(3);\n\nfn thread_1() {\n\tlet key = ThreadKey::get().unwrap();\n\tlet mut guard = RWLOCK_2.write(key);\n\tstd::thread::sleep(Duration::from_millis(75));\n\tassert_eq!(*guard, 2);\n\t*guard = 5;\n}\n\nfn thread_2() {\n\tlet key = ThreadKey::get().unwrap();\n\tlet collection = RetryingLockCollection::try_new([\u0026RWLOCK_1, \u0026RWLOCK_2, \u0026RWLOCK_3]).unwrap();\n\tstd::thread::sleep(Duration::from_millis(25));\n\tlet guard = collection.read(key);\n\tassert_eq!(*guard[0], 1);\n\tassert_eq!(*guard[1], 5);\n\tassert_eq!(*guard[2], 3);\n}\n\nfn thread_3() {\n\tlet key = ThreadKey::get().unwrap();\n\tstd::thread::sleep(Duration::from_millis(50));\n\tlet guard = RWLOCK_1.write(key);\n\tstd::thread::sleep(Duration::from_millis(50));\n\tassert_eq!(*guard, 1);\n}\n\n#[test]\nfn retries() {\n\tlet t1 = std::thread::spawn(thread_1);\n\tlet t2 = std::thread::spawn(thread_2);\n\tlet t3 = std::thread::spawn(thread_3);\n\n\tt1.join().unwrap();\n\tt2.join().unwrap();\n\tt3.join().unwrap();\n}\n","traces":[],"covered":0,"coverable":0}]}; </script> <script crossorigin>/** @license React v16.13.1 * react.production.min.js diff --git a/tests/evil_rwlock.rs b/tests/evil_rwlock.rs index 9eed8a8..4be86a1 100644 --- a/tests/evil_rwlock.rs +++ b/tests/evil_rwlock.rs @@ -1,3 +1,4 @@ +use std::panic::AssertUnwindSafe; use std::sync::Arc; use happylock::collection::{BoxedLockCollection, RetryingLockCollection}; @@ -63,6 +64,23 @@ fn boxed_rwlocks() { assert!(good_mutex.scoped_try_write(&mut key, |_| {}).is_ok()); assert!(evil_mutex.scoped_try_write(&mut key, |_| {}).is_err()); assert!(useless_mutex.scoped_try_write(&mut key, |_| {}).is_ok()); + + std::thread::scope(|s| { + s.spawn(|| { + let evil_mutex = AssertUnwindSafe(evil_mutex); + let r = std::panic::catch_unwind(|| { + let key = ThreadKey::get().unwrap(); + evil_mutex.write(key); + }); + + assert!(r.is_err()); + }); + + s.spawn(|| { + let key = ThreadKey::get().unwrap(); + good_mutex.write(key); + }); + }); } #[test] diff --git a/tests/retry_rw.rs b/tests/retry_rw.rs index 976ab14..0a946b0 100644 --- a/tests/retry_rw.rs +++ b/tests/retry_rw.rs @@ -24,11 +24,21 @@ fn thread_2() { assert_eq!(*guard[2], 3); } +fn thread_3() { + let key = ThreadKey::get().unwrap(); + std::thread::sleep(Duration::from_millis(50)); + let guard = RWLOCK_1.write(key); + std::thread::sleep(Duration::from_millis(50)); + assert_eq!(*guard, 1); +} + #[test] fn retries() { let t1 = std::thread::spawn(thread_1); let t2 = std::thread::spawn(thread_2); + let t3 = std::thread::spawn(thread_3); t1.join().unwrap(); t2.join().unwrap(); + t3.join().unwrap(); } |
