diff options
| author | Botahamec <botahamec@outlook.com> | 2025-02-28 16:09:11 -0500 |
|---|---|---|
| committer | Botahamec <botahamec@outlook.com> | 2025-02-28 16:09:11 -0500 |
| commit | 4ba03be97e6cc7e790bbc9bfc18caaa228c8a262 (patch) | |
| tree | a257184577a93ddf240aba698755c2886188788b | |
| parent | 4a5ec04a29cba07c5960792528bd66b0f99ee3ee (diff) | |
Scoped lock API
33 files changed, 3028 insertions, 762 deletions
diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..a8e32a8 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,382 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in library 'happylock'", + "cargo": { + "args": [ + "test", + "--no-run", + "--lib", + "--package=happylock" + ], + "filter": { + "name": "happylock", + "kind": "lib" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug example 'basic'", + "cargo": { + "args": [ + "build", + "--example=basic", + "--package=happylock" + ], + "filter": { + "name": "basic", + "kind": "example" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in example 'basic'", + "cargo": { + "args": [ + "test", + "--no-run", + "--example=basic", + "--package=happylock" + ], + "filter": { + "name": "basic", + "kind": "example" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug example 'dining_philosophers'", + "cargo": { + "args": [ + "build", + "--example=dining_philosophers", + "--package=happylock" + ], + "filter": { + "name": "dining_philosophers", + "kind": "example" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in example 'dining_philosophers'", + "cargo": { + "args": [ + "test", + "--no-run", + "--example=dining_philosophers", + "--package=happylock" + ], + "filter": { + "name": "dining_philosophers", + "kind": "example" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug example 'dining_philosophers_retry'", + "cargo": { + "args": [ + "build", + "--example=dining_philosophers_retry", + "--package=happylock" + ], + "filter": { + "name": "dining_philosophers_retry", + "kind": "example" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in example 'dining_philosophers_retry'", + "cargo": { + "args": [ + "test", + "--no-run", + "--example=dining_philosophers_retry", + "--package=happylock" + ], + "filter": { + "name": "dining_philosophers_retry", + "kind": "example" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug example 'double_mutex'", + "cargo": { + "args": [ + "build", + "--example=double_mutex", + "--package=happylock" + ], + "filter": { + "name": "double_mutex", + "kind": "example" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in example 'double_mutex'", + "cargo": { + "args": [ + "test", + "--no-run", + "--example=double_mutex", + "--package=happylock" + ], + "filter": { + "name": "double_mutex", + "kind": "example" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug example 'fibonacci'", + "cargo": { + "args": [ + "build", + "--example=fibonacci", + "--package=happylock" + ], + "filter": { + "name": "fibonacci", + "kind": "example" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in example 'fibonacci'", + "cargo": { + "args": [ + "test", + "--no-run", + "--example=fibonacci", + "--package=happylock" + ], + "filter": { + "name": "fibonacci", + "kind": "example" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug example 'list'", + "cargo": { + "args": [ + "build", + "--example=list", + "--package=happylock" + ], + "filter": { + "name": "list", + "kind": "example" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in example 'list'", + "cargo": { + "args": [ + "test", + "--no-run", + "--example=list", + "--package=happylock" + ], + "filter": { + "name": "list", + "kind": "example" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug integration test 'evil_mutex'", + "cargo": { + "args": [ + "test", + "--no-run", + "--test=evil_mutex", + "--package=happylock" + ], + "filter": { + "name": "evil_mutex", + "kind": "test" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug integration test 'evil_rwlock'", + "cargo": { + "args": [ + "test", + "--no-run", + "--test=evil_rwlock", + "--package=happylock" + ], + "filter": { + "name": "evil_rwlock", + "kind": "test" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug integration test 'evil_try_mutex'", + "cargo": { + "args": [ + "test", + "--no-run", + "--test=evil_try_mutex", + "--package=happylock" + ], + "filter": { + "name": "evil_try_mutex", + "kind": "test" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug integration test 'evil_try_rwlock'", + "cargo": { + "args": [ + "test", + "--no-run", + "--test=evil_try_rwlock", + "--package=happylock" + ], + "filter": { + "name": "evil_try_rwlock", + "kind": "test" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug integration test 'evil_unlock_mutex'", + "cargo": { + "args": [ + "test", + "--no-run", + "--test=evil_unlock_mutex", + "--package=happylock" + ], + "filter": { + "name": "evil_unlock_mutex", + "kind": "test" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug integration test 'evil_unlock_rwlock'", + "cargo": { + "args": [ + "test", + "--no-run", + "--test=evil_unlock_rwlock", + "--package=happylock" + ], + "filter": { + "name": "evil_unlock_rwlock", + "kind": "test" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug integration test 'retry'", + "cargo": { + "args": [ + "test", + "--no-run", + "--test=retry", + "--package=happylock" + ], + "filter": { + "name": "retry", + "kind": "test" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + } + ] +}
\ No newline at end of file diff --git a/examples/list.rs b/examples/list.rs index a649eeb..fb3a405 100644 --- a/examples/list.rs +++ b/examples/list.rs @@ -16,13 +16,14 @@ static DATA: [Mutex<usize>; 6] = [ static SEED: Mutex<u32> = Mutex::new(42); fn random(key: &mut ThreadKey) -> usize { - let mut seed = SEED.lock(key); - let x = *seed; - let x = x ^ (x << 13); - let x = x ^ (x >> 17); - let x = x ^ (x << 5); - *seed = x; - x as usize + SEED.scoped_lock(key, |seed| { + let x = *seed; + let x = x ^ (x << 13); + let x = x ^ (x >> 17); + let x = x ^ (x << 5); + *seed = x; + x as usize + }) } fn main() { @@ -40,7 +41,7 @@ fn main() { let Some(lock) = RefLockCollection::try_new(&data) else { continue; }; - let mut guard = lock.lock(&mut key); + let mut guard = lock.lock(key); *guard[0] += *guard[1]; *guard[1] += *guard[2]; *guard[2] += *guard[0]; diff --git a/src/collection.rs b/src/collection.rs index db68382..e50cc30 100644 --- a/src/collection.rs +++ b/src/collection.rs @@ -1,7 +1,6 @@ use std::cell::UnsafeCell; -use std::marker::PhantomData; -use crate::{key::Keyable, lockable::RawLock}; +use crate::{lockable::RawLock, ThreadKey}; mod boxed; mod guard; @@ -122,8 +121,7 @@ pub struct RetryingLockCollection<L> { /// A RAII guard for a generic [`Lockable`] type. /// /// [`Lockable`]: `crate::lockable::Lockable` -pub struct LockGuard<'key, Guard, Key: Keyable + 'key> { +pub struct LockGuard<Guard> { guard: Guard, - key: Key, - _phantom: PhantomData<&'key ()>, + key: ThreadKey, } diff --git a/src/collection/boxed.rs b/src/collection/boxed.rs index 0597e90..364ec97 100644 --- a/src/collection/boxed.rs +++ b/src/collection/boxed.rs @@ -1,25 +1,12 @@ use std::cell::UnsafeCell; use std::fmt::Debug; -use std::marker::PhantomData; use crate::lockable::{Lockable, LockableIntoInner, OwnedLockable, RawLock, Sharable}; -use crate::Keyable; +use crate::{Keyable, ThreadKey}; +use super::utils::ordered_contains_duplicates; use super::{utils, BoxedLockCollection, LockGuard}; -/// returns `true` if the sorted list contains a duplicate -#[must_use] -fn contains_duplicates(l: &[&dyn RawLock]) -> bool { - if l.is_empty() { - // Return early to prevent panic in the below call to `windows` - return false; - } - - l.windows(2) - // NOTE: addr_eq is necessary because eq would also compare the v-table pointers - .any(|window| std::ptr::addr_eq(window[0], window[1])) -} - unsafe impl<L: Lockable> RawLock for BoxedLockCollection<L> { #[mutants::skip] // this should never be called #[cfg(not(tarpaulin_include))] @@ -65,6 +52,11 @@ unsafe impl<L: Lockable> Lockable for BoxedLockCollection<L> { where Self: 'g; + type DataMut<'a> + = L::DataMut<'a> + where + Self: 'a; + fn get_ptrs<'a>(&'a self, ptrs: &mut Vec<&'a dyn RawLock>) { ptrs.extend(self.locks()) } @@ -72,6 +64,10 @@ unsafe impl<L: Lockable> Lockable for BoxedLockCollection<L> { unsafe fn guard(&self) -> Self::Guard<'_> { self.child().guard() } + + unsafe fn data_mut(&self) -> Self::DataMut<'_> { + self.child().data_mut() + } } unsafe impl<L: Sharable> Sharable for BoxedLockCollection<L> { @@ -80,9 +76,18 @@ unsafe impl<L: Sharable> Sharable for BoxedLockCollection<L> { where Self: 'g; + type DataRef<'a> + = L::DataRef<'a> + where + Self: 'a; + unsafe fn read_guard(&self) -> Self::ReadGuard<'_> { self.child().read_guard() } + + unsafe fn data_ref(&self) -> Self::DataRef<'_> { + self.child().data_ref() + } } unsafe impl<L: OwnedLockable> OwnedLockable for BoxedLockCollection<L> {} @@ -352,13 +357,53 @@ impl<L: Lockable> BoxedLockCollection<L> { // safety: we are checking for duplicates before returning unsafe { let this = Self::new_unchecked(data); - if contains_duplicates(this.locks()) { + if ordered_contains_duplicates(this.locks()) { return None; } Some(this) } } + 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_try_lock<Key: Keyable, R>( + &self, + key: Key, + f: impl Fn(L::DataMut<'_>) -> 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) + } + } + /// Locks the collection /// /// This function returns a guard that can be used to access the underlying @@ -378,10 +423,8 @@ impl<L: Lockable> BoxedLockCollection<L> { /// *guard.0 += 1; /// *guard.1 = "1"; /// ``` - pub fn lock<'g, 'key: 'g, Key: Keyable + 'key>( - &'g self, - key: Key, - ) -> LockGuard<'key, L::Guard<'g>, Key> { + #[must_use] + pub fn lock(&self, key: ThreadKey) -> LockGuard<L::Guard<'_>> { unsafe { // safety: we have the thread key self.raw_lock(); @@ -390,7 +433,6 @@ impl<L: Lockable> BoxedLockCollection<L> { // safety: we've already acquired the lock guard: self.child().guard(), key, - _phantom: PhantomData, } } } @@ -424,10 +466,7 @@ impl<L: Lockable> BoxedLockCollection<L> { /// }; /// /// ``` - pub fn try_lock<'g, 'key: 'g, Key: Keyable + 'key>( - &'g self, - key: Key, - ) -> Result<LockGuard<'key, L::Guard<'g>, Key>, Key> { + pub fn try_lock(&self, key: ThreadKey) -> Result<LockGuard<L::Guard<'_>>, ThreadKey> { let guard = unsafe { if !self.raw_try_lock() { return Err(key); @@ -437,11 +476,7 @@ impl<L: Lockable> BoxedLockCollection<L> { self.child().guard() }; - Ok(LockGuard { - guard, - key, - _phantom: PhantomData, - }) + Ok(LockGuard { guard, key }) } /// Unlocks the underlying lockable data type, returning the key that's @@ -461,13 +496,53 @@ impl<L: Lockable> BoxedLockCollection<L> { /// *guard.1 = "1"; /// let key = LockCollection::<(Mutex<i32>, Mutex<&str>)>::unlock(guard); /// ``` - pub fn unlock<'key, Key: Keyable + 'key>(guard: LockGuard<'key, L::Guard<'_>, Key>) -> Key { + pub fn unlock(guard: LockGuard<L::Guard<'_>>) -> ThreadKey { drop(guard.guard); guard.key } } 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_try_read<Key: Keyable, R>( + &self, + key: Key, + f: impl Fn(L::DataRef<'_>) -> 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) + } + } + /// Locks the collection, so that other threads can still read from it /// /// This function returns a guard that can be used to access the underlying @@ -487,10 +562,8 @@ impl<L: Sharable> BoxedLockCollection<L> { /// assert_eq!(*guard.0, 0); /// assert_eq!(*guard.1, ""); /// ``` - pub fn read<'g, 'key: 'g, Key: Keyable + 'key>( - &'g self, - key: Key, - ) -> LockGuard<'key, L::ReadGuard<'g>, Key> { + #[must_use] + pub fn read(&self, key: ThreadKey) -> LockGuard<L::ReadGuard<'_>> { unsafe { // safety: we have the thread key self.raw_read(); @@ -499,7 +572,6 @@ impl<L: Sharable> BoxedLockCollection<L> { // safety: we've already acquired the lock guard: self.child().read_guard(), key, - _phantom: PhantomData, } } } @@ -534,10 +606,7 @@ impl<L: Sharable> BoxedLockCollection<L> { /// }; /// /// ``` - pub fn try_read<'g, 'key: 'g, Key: Keyable + 'key>( - &'g self, - key: Key, - ) -> Result<LockGuard<'key, L::ReadGuard<'g>, Key>, Key> { + pub fn try_read(&self, key: ThreadKey) -> Result<LockGuard<L::ReadGuard<'_>>, ThreadKey> { let guard = unsafe { // safety: we have the thread key if !self.raw_try_read() { @@ -548,11 +617,7 @@ impl<L: Sharable> BoxedLockCollection<L> { self.child().read_guard() }; - Ok(LockGuard { - guard, - key, - _phantom: PhantomData, - }) + Ok(LockGuard { guard, key }) } /// Unlocks the underlying lockable data type, returning the key that's @@ -570,9 +635,7 @@ impl<L: Sharable> BoxedLockCollection<L> { /// let mut guard = lock.read(key); /// let key = LockCollection::<(RwLock<i32>, RwLock<&str>)>::unlock_read(guard); /// ``` - pub fn unlock_read<'key, Key: Keyable + 'key>( - guard: LockGuard<'key, L::ReadGuard<'_>, Key>, - ) -> Key { + pub fn unlock_read(guard: LockGuard<L::ReadGuard<'_>>) -> ThreadKey { drop(guard.guard); guard.key } @@ -635,7 +698,6 @@ mod tests { .into_iter() .collect(); let guard = collection.lock(key); - // TODO impl PartialEq<T> for MutexRef<T> assert_eq!(*guard[0], "foo"); assert_eq!(*guard[1], "bar"); assert_eq!(*guard[2], "baz"); @@ -647,7 +709,6 @@ mod tests { let collection = BoxedLockCollection::from([Mutex::new("foo"), Mutex::new("bar"), Mutex::new("baz")]); let guard = collection.lock(key); - // TODO impl PartialEq<T> for MutexRef<T> assert_eq!(*guard[0], "foo"); assert_eq!(*guard[1], "bar"); assert_eq!(*guard[2], "baz"); @@ -666,7 +727,7 @@ mod tests { let mut key = ThreadKey::get().unwrap(); let collection = BoxedLockCollection::new([Mutex::new(0), Mutex::new(1), Mutex::new(2)]); for (i, mutex) in (&collection).into_iter().enumerate() { - assert_eq!(*mutex.lock(&mut key), i); + mutex.scoped_lock(&mut key, |val| assert_eq!(*val, i)) } } @@ -675,7 +736,7 @@ mod tests { let mut key = ThreadKey::get().unwrap(); let collection = BoxedLockCollection::new([Mutex::new(0), Mutex::new(1), Mutex::new(2)]); for (i, mutex) in collection.iter().enumerate() { - assert_eq!(*mutex.lock(&mut key), i); + mutex.scoped_lock(&mut key, |val| assert_eq!(*val, i)) } } @@ -704,11 +765,6 @@ mod tests { } #[test] - fn contains_duplicates_empty() { - assert!(!contains_duplicates(&[])) - } - - #[test] fn try_lock_works() { let key = ThreadKey::get().unwrap(); let collection = BoxedLockCollection::new([Mutex::new(1), Mutex::new(2)]); diff --git a/src/collection/guard.rs b/src/collection/guard.rs index eea13ed..78d9895 100644 --- a/src/collection/guard.rs +++ b/src/collection/guard.rs @@ -2,41 +2,11 @@ use std::fmt::{Debug, Display}; use std::hash::Hash; use std::ops::{Deref, DerefMut}; -use crate::key::Keyable; - use super::LockGuard; -#[mutants::skip] // it's hard to get two guards safely -#[cfg(not(tarpaulin_include))] -impl<Guard: PartialEq, Key: Keyable> PartialEq for LockGuard<'_, Guard, Key> { - fn eq(&self, other: &Self) -> bool { - self.guard.eq(&other.guard) - } -} - -#[mutants::skip] // it's hard to get two guards safely -#[cfg(not(tarpaulin_include))] -impl<Guard: PartialOrd, Key: Keyable> PartialOrd for LockGuard<'_, Guard, Key> { - fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { - self.guard.partial_cmp(&other.guard) - } -} - -#[mutants::skip] // it's hard to get two guards safely -#[cfg(not(tarpaulin_include))] -impl<Guard: Eq, Key: Keyable> Eq for LockGuard<'_, Guard, Key> {} - -#[mutants::skip] // it's hard to get two guards safely -#[cfg(not(tarpaulin_include))] -impl<Guard: Ord, Key: Keyable> Ord for LockGuard<'_, Guard, Key> { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.guard.cmp(&other.guard) - } -} - #[mutants::skip] // hashing involves RNG and is hard to test #[cfg(not(tarpaulin_include))] -impl<Guard: Hash, Key: Keyable> Hash for LockGuard<'_, Guard, Key> { +impl<Guard: Hash> Hash for LockGuard<Guard> { fn hash<H: std::hash::Hasher>(&self, state: &mut H) { self.guard.hash(state) } @@ -44,19 +14,19 @@ impl<Guard: Hash, Key: Keyable> Hash for LockGuard<'_, Guard, Key> { #[mutants::skip] #[cfg(not(tarpaulin_include))] -impl<Guard: Debug, Key: Keyable> Debug for LockGuard<'_, Guard, Key> { +impl<Guard: Debug> Debug for LockGuard<Guard> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { Debug::fmt(&**self, f) } } -impl<Guard: Display, Key: Keyable> Display for LockGuard<'_, Guard, Key> { +impl<Guard: Display> Display for LockGuard<Guard> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { Display::fmt(&**self, f) } } -impl<Guard, Key: Keyable> Deref for LockGuard<'_, Guard, Key> { +impl<Guard> Deref for LockGuard<Guard> { type Target = Guard; fn deref(&self) -> &Self::Target { @@ -64,19 +34,19 @@ impl<Guard, Key: Keyable> Deref for LockGuard<'_, Guard, Key> { } } -impl<Guard, Key: Keyable> DerefMut for LockGuard<'_, Guard, Key> { +impl<Guard> DerefMut for LockGuard<Guard> { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.guard } } -impl<Guard, Key: Keyable> AsRef<Guard> for LockGuard<'_, Guard, Key> { +impl<Guard> AsRef<Guard> for LockGuard<Guard> { fn as_ref(&self) -> &Guard { &self.guard } } -impl<Guard, Key: Keyable> AsMut<Guard> for LockGuard<'_, Guard, Key> { +impl<Guard> AsMut<Guard> for LockGuard<Guard> { fn as_mut(&mut self) -> &mut Guard { &mut self.guard } @@ -97,56 +67,53 @@ mod tests { #[test] fn deref_mut_works() { - let mut key = ThreadKey::get().unwrap(); + let key = ThreadKey::get().unwrap(); let locks = (Mutex::new(1), Mutex::new(2)); let lock = LockCollection::new_ref(&locks); - let mut guard = lock.lock(&mut key); + let mut guard = lock.lock(key); *guard.0 = 3; - drop(guard); + let key = LockCollection::<(Mutex<_>, Mutex<_>)>::unlock(guard); - let guard = locks.0.lock(&mut key); + let guard = locks.0.lock(key); assert_eq!(*guard, 3); - drop(guard); + let key = Mutex::unlock(guard); - let guard = locks.1.lock(&mut key); + let guard = locks.1.lock(key); assert_eq!(*guard, 2); - drop(guard); } #[test] fn as_ref_works() { - let mut key = ThreadKey::get().unwrap(); + let key = ThreadKey::get().unwrap(); let locks = (Mutex::new(1), Mutex::new(2)); let lock = LockCollection::new_ref(&locks); - let mut guard = lock.lock(&mut key); + let mut guard = lock.lock(key); *guard.0 = 3; - drop(guard); + let key = LockCollection::<(Mutex<_>, Mutex<_>)>::unlock(guard); - let guard = locks.0.lock(&mut key); + let guard = locks.0.lock(key); assert_eq!(guard.as_ref(), &3); - drop(guard); + let key = Mutex::unlock(guard); - let guard = locks.1.lock(&mut key); + let guard = locks.1.lock(key); assert_eq!(guard.as_ref(), &2); - drop(guard); } #[test] fn as_mut_works() { - let mut key = ThreadKey::get().unwrap(); + let key = ThreadKey::get().unwrap(); let locks = (Mutex::new(1), Mutex::new(2)); let lock = LockCollection::new_ref(&locks); - let mut guard = lock.lock(&mut key); + let mut guard = lock.lock(key); let guard_mut = guard.as_mut(); *guard_mut.0 = 3; - drop(guard); + let key = LockCollection::<(Mutex<_>, Mutex<_>)>::unlock(guard); - let guard = locks.0.lock(&mut key); + let guard = locks.0.lock(key); assert_eq!(guard.as_ref(), &3); - drop(guard); + let key = Mutex::unlock(guard); - let guard = locks.1.lock(&mut key); + let guard = locks.1.lock(key); assert_eq!(guard.as_ref(), &2); - drop(guard); } } diff --git a/src/collection/owned.rs b/src/collection/owned.rs index c345b43..b9cf313 100644 --- a/src/collection/owned.rs +++ b/src/collection/owned.rs @@ -1,57 +1,47 @@ -use std::marker::PhantomData; - use crate::lockable::{ Lockable, LockableGetMut, LockableIntoInner, OwnedLockable, RawLock, Sharable, }; -use crate::Keyable; +use crate::{Keyable, ThreadKey}; use super::{utils, LockGuard, OwnedLockCollection}; -#[mutants::skip] // it's hard to test individual locks in an OwnedLockCollection -#[cfg(not(tarpaulin_include))] -fn get_locks<L: Lockable>(data: &L) -> Vec<&dyn RawLock> { - let mut locks = Vec::new(); - data.get_ptrs(&mut locks); - locks -} - unsafe impl<L: Lockable> RawLock for OwnedLockCollection<L> { #[mutants::skip] // this should never run #[cfg(not(tarpaulin_include))] fn poison(&self) { - let locks = get_locks(&self.data); + let locks = utils::get_locks_unsorted(&self.data); for lock in locks { lock.poison(); } } unsafe fn raw_lock(&self) { - utils::ordered_lock(&get_locks(&self.data)) + utils::ordered_lock(&utils::get_locks_unsorted(&self.data)) } unsafe fn raw_try_lock(&self) -> bool { - let locks = get_locks(&self.data); + let locks = utils::get_locks_unsorted(&self.data); utils::ordered_try_lock(&locks) } unsafe fn raw_unlock(&self) { - let locks = get_locks(&self.data); + let locks = utils::get_locks_unsorted(&self.data); for lock in locks { lock.raw_unlock(); } } unsafe fn raw_read(&self) { - utils::ordered_read(&get_locks(&self.data)) + utils::ordered_read(&utils::get_locks_unsorted(&self.data)) } unsafe fn raw_try_read(&self) -> bool { - let locks = get_locks(&self.data); + let locks = utils::get_locks_unsorted(&self.data); utils::ordered_try_read(&locks) } unsafe fn raw_unlock_read(&self) { - let locks = get_locks(&self.data); + let locks = utils::get_locks_unsorted(&self.data); for lock in locks { lock.raw_unlock_read(); } @@ -64,6 +54,11 @@ unsafe impl<L: Lockable> Lockable for OwnedLockCollection<L> { where Self: 'g; + type DataMut<'a> + = L::DataMut<'a> + where + Self: 'a; + #[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>) { @@ -73,6 +68,10 @@ unsafe impl<L: Lockable> Lockable for OwnedLockCollection<L> { unsafe fn guard(&self) -> Self::Guard<'_> { self.data.guard() } + + unsafe fn data_mut(&self) -> Self::DataMut<'_> { + self.data.data_mut() + } } impl<L: LockableGetMut> LockableGetMut for OwnedLockCollection<L> { @@ -100,9 +99,18 @@ unsafe impl<L: Sharable> Sharable for OwnedLockCollection<L> { where Self: 'g; + type DataRef<'a> + = L::DataRef<'a> + where + Self: 'a; + unsafe fn read_guard(&self) -> Self::ReadGuard<'_> { self.data.read_guard() } + + unsafe fn data_ref(&self) -> Self::DataRef<'_> { + self.data.data_ref() + } } unsafe impl<L: OwnedLockable> OwnedLockable for OwnedLockCollection<L> {} @@ -177,6 +185,46 @@ 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_try_lock<Key: Keyable, R>( + &self, + key: Key, + f: impl Fn(L::DataMut<'_>) -> 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) + } + } + /// Locks the collection /// /// This function returns a guard that can be used to access the underlying @@ -197,10 +245,7 @@ impl<L: OwnedLockable> OwnedLockCollection<L> { /// *guard.0 += 1; /// *guard.1 = "1"; /// ``` - pub fn lock<'g, 'key: 'g, Key: Keyable + 'key>( - &'g self, - key: Key, - ) -> LockGuard<'key, L::Guard<'g>, Key> { + pub fn lock(&self, key: ThreadKey) -> LockGuard<L::Guard<'_>> { let guard = unsafe { // safety: we have the thread key, and these locks happen in a // predetermined order @@ -210,11 +255,7 @@ impl<L: OwnedLockable> OwnedLockCollection<L> { self.data.guard() }; - LockGuard { - guard, - key, - _phantom: PhantomData, - } + LockGuard { guard, key } } /// Attempts to lock the without blocking. @@ -247,10 +288,7 @@ impl<L: OwnedLockable> OwnedLockCollection<L> { /// }; /// /// ``` - pub fn try_lock<'g, 'key: 'g, Key: Keyable + 'key>( - &'g self, - key: Key, - ) -> Result<LockGuard<'key, L::Guard<'g>, Key>, Key> { + pub fn try_lock(&self, key: ThreadKey) -> Result<LockGuard<L::Guard<'_>>, ThreadKey> { let guard = unsafe { if !self.raw_try_lock() { return Err(key); @@ -260,11 +298,7 @@ impl<L: OwnedLockable> OwnedLockCollection<L> { self.data.guard() }; - Ok(LockGuard { - guard, - key, - _phantom: PhantomData, - }) + Ok(LockGuard { guard, key }) } /// Unlocks the underlying lockable data type, returning the key that's @@ -286,15 +320,53 @@ impl<L: OwnedLockable> OwnedLockCollection<L> { /// let key = OwnedLockCollection::<(Mutex<i32>, Mutex<&str>)>::unlock(guard); /// ``` #[allow(clippy::missing_const_for_fn)] - pub fn unlock<'g, 'key: 'g, Key: Keyable + 'key>( - guard: LockGuard<'key, L::Guard<'g>, Key>, - ) -> Key { + pub fn unlock(guard: LockGuard<L::Guard<'_>>) -> ThreadKey { drop(guard.guard); guard.key } } 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_try_read<Key: Keyable, R>( + &self, + key: Key, + f: impl Fn(L::DataRef<'_>) -> 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) + } + } + /// Locks the collection, so that other threads can still read from it /// /// This function returns a guard that can be used to access the underlying @@ -315,10 +387,7 @@ impl<L: Sharable> OwnedLockCollection<L> { /// assert_eq!(*guard.0, 0); /// assert_eq!(*guard.1, ""); /// ``` - pub fn read<'g, 'key: 'g, Key: Keyable + 'key>( - &'g self, - key: Key, - ) -> LockGuard<'key, L::ReadGuard<'g>, Key> { + pub fn read(&self, key: ThreadKey) -> LockGuard<L::ReadGuard<'_>> { unsafe { // safety: we have the thread key self.raw_read(); @@ -327,7 +396,6 @@ impl<L: Sharable> OwnedLockCollection<L> { // safety: we've already acquired the lock guard: self.data.read_guard(), key, - _phantom: PhantomData, } } } @@ -355,33 +423,26 @@ impl<L: Sharable> OwnedLockCollection<L> { /// let lock = OwnedLockCollection::new(data); /// /// match lock.try_read(key) { - /// Some(mut guard) => { + /// Ok(mut guard) => { /// assert_eq!(*guard.0, 5); /// assert_eq!(*guard.1, "6"); /// }, - /// None => unreachable!(), + /// Err(_) => unreachable!(), /// }; /// /// ``` - pub fn try_read<'g, 'key: 'g, Key: Keyable + 'key>( - &'g self, - key: Key, - ) -> Option<LockGuard<'key, L::ReadGuard<'g>, Key>> { + pub fn try_read(&self, key: ThreadKey) -> Result<LockGuard<L::ReadGuard<'_>>, ThreadKey> { let guard = unsafe { // safety: we have the thread key if !self.raw_try_read() { - return None; + return Err(key); } // safety: we've acquired the locks self.data.read_guard() }; - Some(LockGuard { - guard, - key, - _phantom: PhantomData, - }) + Ok(LockGuard { guard, key }) } /// Unlocks the underlying lockable data type, returning the key that's @@ -401,9 +462,7 @@ impl<L: Sharable> OwnedLockCollection<L> { /// let key = OwnedLockCollection::<(RwLock<i32>, RwLock<&str>)>::unlock_read(guard); /// ``` #[allow(clippy::missing_const_for_fn)] - pub fn unlock_read<'g, 'key: 'g, Key: Keyable + 'key>( - guard: LockGuard<'key, L::ReadGuard<'g>, Key>, - ) -> Key { + pub fn unlock_read(guard: LockGuard<L::ReadGuard<'_>>) -> ThreadKey { drop(guard.guard); guard.key } @@ -495,7 +554,90 @@ impl<L: LockableIntoInner> OwnedLockCollection<L> { #[cfg(test)] mod tests { use super::*; - use crate::Mutex; + use crate::{Mutex, ThreadKey}; + + #[test] + fn get_mut_applies_changes() { + let key = ThreadKey::get().unwrap(); + let mut collection = OwnedLockCollection::new([Mutex::new("foo"), Mutex::new("bar")]); + assert_eq!(*collection.get_mut()[0], "foo"); + assert_eq!(*collection.get_mut()[1], "bar"); + *collection.get_mut()[0] = "baz"; + + let guard = collection.lock(key); + assert_eq!(*guard[0], "baz"); + assert_eq!(*guard[1], "bar"); + } + + #[test] + fn into_inner_works() { + let key = ThreadKey::get().unwrap(); + let collection = OwnedLockCollection::from([Mutex::new("foo")]); + let mut guard = collection.lock(key); + *guard[0] = "bar"; + drop(guard); + + let array = collection.into_inner(); + assert_eq!(array.len(), 1); + assert_eq!(array[0], "bar"); + } + + #[test] + fn from_into_iter_is_correct() { + let array = [Mutex::new(0), Mutex::new(1), Mutex::new(2), Mutex::new(3)]; + let mut collection: OwnedLockCollection<Vec<Mutex<usize>>> = array.into_iter().collect(); + assert_eq!(collection.get_mut().len(), 4); + for (i, lock) in collection.into_iter().enumerate() { + assert_eq!(lock.into_inner(), i); + } + } + + #[test] + fn from_iter_is_correct() { + let array = [Mutex::new(0), Mutex::new(1), Mutex::new(2), Mutex::new(3)]; + let mut collection: OwnedLockCollection<Vec<Mutex<usize>>> = array.into_iter().collect(); + let collection: &mut Vec<_> = collection.as_mut(); + assert_eq!(collection.len(), 4); + for (i, lock) in collection.iter_mut().enumerate() { + assert_eq!(*lock.get_mut(), i); + } + } + + #[test] + fn try_lock_works_on_unlocked() { + let key = ThreadKey::get().unwrap(); + let collection = OwnedLockCollection::new((Mutex::new(0), Mutex::new(1))); + let guard = collection.try_lock(key).unwrap(); + assert_eq!(*guard.0, 0); + assert_eq!(*guard.1, 1); + } + + #[test] + fn try_lock_fails_on_locked() { + let key = ThreadKey::get().unwrap(); + let collection = OwnedLockCollection::new((Mutex::new(0), Mutex::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_lock(key).is_err()); + } + + #[test] + fn default_works() { + type MyCollection = OwnedLockCollection<(Mutex<i32>, Mutex<Option<i32>>, Mutex<String>)>; + let collection = MyCollection::default(); + let inner = collection.into_inner(); + assert_eq!(inner.0, 0); + assert_eq!(inner.1, None); + assert_eq!(inner.2, String::new()); + } #[test] fn can_be_extended() { diff --git a/src/collection/ref.rs b/src/collection/ref.rs index c86f298..b68b72f 100644 --- a/src/collection/ref.rs +++ b/src/collection/ref.rs @@ -1,32 +1,11 @@ use std::fmt::Debug; -use std::marker::PhantomData; use crate::lockable::{Lockable, OwnedLockable, RawLock, Sharable}; -use crate::Keyable; +use crate::{Keyable, ThreadKey}; +use super::utils::{get_locks, ordered_contains_duplicates}; use super::{utils, LockGuard, RefLockCollection}; -#[must_use] -pub fn get_locks<L: Lockable>(data: &L) -> Vec<&dyn RawLock> { - let mut locks = Vec::new(); - data.get_ptrs(&mut locks); - locks.sort_by_key(|lock| &raw const **lock); - locks -} - -/// returns `true` if the sorted list contains a duplicate -#[must_use] -fn contains_duplicates(l: &[&dyn RawLock]) -> bool { - if l.is_empty() { - // Return early to prevent panic in the below call to `windows` - return false; - } - - l.windows(2) - // NOTE: addr_eq is necessary because eq would also compare the v-table pointers - .any(|window| std::ptr::addr_eq(window[0], window[1])) -} - impl<'a, L> IntoIterator for &'a RefLockCollection<'a, L> where &'a L: IntoIterator, @@ -83,6 +62,11 @@ unsafe impl<L: Lockable> Lockable for RefLockCollection<'_, L> { where Self: 'g; + type DataMut<'a> + = L::DataMut<'a> + where + Self: 'a; + fn get_ptrs<'a>(&'a self, ptrs: &mut Vec<&'a dyn RawLock>) { ptrs.extend_from_slice(&self.locks); } @@ -90,6 +74,10 @@ unsafe impl<L: Lockable> Lockable for RefLockCollection<'_, L> { unsafe fn guard(&self) -> Self::Guard<'_> { self.data.guard() } + + unsafe fn data_mut(&self) -> Self::DataMut<'_> { + self.data.data_mut() + } } unsafe impl<L: Sharable> Sharable for RefLockCollection<'_, L> { @@ -98,9 +86,18 @@ unsafe impl<L: Sharable> Sharable for RefLockCollection<'_, L> { where Self: 'g; + type DataRef<'a> + = L::DataRef<'a> + where + Self: 'a; + unsafe fn read_guard(&self) -> Self::ReadGuard<'_> { self.data.read_guard() } + + unsafe fn data_ref(&self) -> Self::DataRef<'_> { + self.data.data_ref() + } } impl<T, L: AsRef<T>> AsRef<T> for RefLockCollection<'_, L> { @@ -230,13 +227,53 @@ impl<'a, L: Lockable> RefLockCollection<'a, L> { #[must_use] pub fn try_new(data: &'a L) -> Option<Self> { let locks = get_locks(data); - if contains_duplicates(&locks) { + if ordered_contains_duplicates(&locks) { return None; } 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_try_lock<Key: Keyable, R>( + &self, + key: Key, + f: impl Fn(L::DataMut<'_>) -> 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) + } + } + /// Locks the collection /// /// This function returns a guard that can be used to access the underlying @@ -257,10 +294,8 @@ impl<'a, L: Lockable> RefLockCollection<'a, L> { /// *guard.0 += 1; /// *guard.1 = "1"; /// ``` - pub fn lock<'g, 'key: 'g, Key: Keyable + 'key>( - &'g self, - key: Key, - ) -> LockGuard<'key, L::Guard<'g>, Key> { + #[must_use] + pub fn lock(&self, key: ThreadKey) -> LockGuard<L::Guard<'_>> { let guard = unsafe { // safety: we have the thread key self.raw_lock(); @@ -269,11 +304,7 @@ impl<'a, L: Lockable> RefLockCollection<'a, L> { self.data.guard() }; - LockGuard { - guard, - key, - _phantom: PhantomData, - } + LockGuard { guard, key } } /// Attempts to lock the without blocking. @@ -306,10 +337,7 @@ impl<'a, L: Lockable> RefLockCollection<'a, L> { /// }; /// /// ``` - pub fn try_lock<'g, 'key: 'a, Key: Keyable + 'key>( - &'g self, - key: Key, - ) -> Result<LockGuard<'key, L::Guard<'g>, Key>, Key> { + pub fn try_lock(&self, key: ThreadKey) -> Result<LockGuard<L::Guard<'_>>, ThreadKey> { let guard = unsafe { if !self.raw_try_lock() { return Err(key); @@ -319,11 +347,7 @@ impl<'a, L: Lockable> RefLockCollection<'a, L> { self.data.guard() }; - Ok(LockGuard { - guard, - key, - _phantom: PhantomData, - }) + Ok(LockGuard { guard, key }) } /// Unlocks the underlying lockable data type, returning the key that's @@ -345,13 +369,53 @@ impl<'a, L: Lockable> RefLockCollection<'a, L> { /// let key = RefLockCollection::<(Mutex<i32>, Mutex<&str>)>::unlock(guard); /// ``` #[allow(clippy::missing_const_for_fn)] - pub fn unlock<'g, 'key, Key: Keyable + 'key>(guard: LockGuard<'key, L::Guard<'g>, Key>) -> Key { + pub fn unlock(guard: LockGuard<L::Guard<'_>>) -> ThreadKey { drop(guard.guard); guard.key } } -impl<'a, L: Sharable> 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_try_read<Key: Keyable, R>( + &self, + key: Key, + f: impl Fn(L::DataRef<'_>) -> 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) + } + } + /// Locks the collection, so that other threads can still read from it /// /// This function returns a guard that can be used to access the underlying @@ -372,10 +436,8 @@ impl<'a, L: Sharable> RefLockCollection<'a, L> { /// assert_eq!(*guard.0, 0); /// assert_eq!(*guard.1, ""); /// ``` - pub fn read<'g, 'key: 'g, Key: Keyable + 'key>( - &'g self, - key: Key, - ) -> LockGuard<'key, L::ReadGuard<'g>, Key> { + #[must_use] + pub fn read(&self, key: ThreadKey) -> LockGuard<L::ReadGuard<'_>> { unsafe { // safety: we have the thread key self.raw_read(); @@ -384,7 +446,6 @@ impl<'a, L: Sharable> RefLockCollection<'a, L> { // safety: we've already acquired the lock guard: self.data.read_guard(), key, - _phantom: PhantomData, } } } @@ -412,33 +473,26 @@ impl<'a, L: Sharable> RefLockCollection<'a, L> { /// let lock = RefLockCollection::new(&data); /// /// match lock.try_read(key) { - /// Some(mut guard) => { + /// Ok(mut guard) => { /// assert_eq!(*guard.0, 5); /// assert_eq!(*guard.1, "6"); /// }, - /// None => unreachable!(), + /// Err(_) => unreachable!(), /// }; /// /// ``` - pub fn try_read<'g, 'key: 'g, Key: Keyable + 'key>( - &'g self, - key: Key, - ) -> Option<LockGuard<'key, L::ReadGuard<'g>, Key>> { + pub fn try_read(&self, key: ThreadKey) -> Result<LockGuard<L::ReadGuard<'_>>, ThreadKey> { let guard = unsafe { // safety: we have the thread key if !self.raw_try_read() { - return None; + return Err(key); } // safety: we've acquired the locks self.data.read_guard() }; - Some(LockGuard { - guard, - key, - _phantom: PhantomData, - }) + Ok(LockGuard { guard, key }) } /// Unlocks the underlying lockable data type, returning the key that's @@ -458,9 +512,7 @@ impl<'a, L: Sharable> RefLockCollection<'a, L> { /// let key = RefLockCollection::<(RwLock<i32>, RwLock<&str>)>::unlock_read(guard); /// ``` #[allow(clippy::missing_const_for_fn)] - pub fn unlock_read<'key: 'a, Key: Keyable + 'key>( - guard: LockGuard<'key, L::ReadGuard<'a>, Key>, - ) -> Key { + pub fn unlock_read(guard: LockGuard<L::ReadGuard<'_>>) -> ThreadKey { drop(guard.guard); guard.key } @@ -497,7 +549,7 @@ where #[cfg(test)] mod tests { use super::*; - use crate::{Mutex, ThreadKey}; + use crate::{Mutex, RwLock, ThreadKey}; #[test] fn non_duplicates_allowed() { @@ -513,6 +565,85 @@ mod tests { } #[test] + fn try_lock_succeeds_for_unlocked_collection() { + let key = ThreadKey::get().unwrap(); + let mutexes = [Mutex::new(24), Mutex::new(42)]; + let collection = RefLockCollection::new(&mutexes); + let guard = collection.try_lock(key).unwrap(); + assert_eq!(*guard[0], 24); + assert_eq!(*guard[1], 42); + } + + #[test] + fn try_lock_fails_for_locked_collection() { + let key = ThreadKey::get().unwrap(); + let mutexes = [Mutex::new(24), Mutex::new(42)]; + let collection = RefLockCollection::new(&mutexes); + + std::thread::scope(|s| { + s.spawn(|| { + let key = ThreadKey::get().unwrap(); + let guard = mutexes[1].lock(key); + assert_eq!(*guard, 42); + std::mem::forget(guard); + }); + }); + + let guard = collection.try_lock(key); + assert!(guard.is_err()); + } + + #[test] + fn try_read_succeeds_for_unlocked_collection() { + let key = ThreadKey::get().unwrap(); + let mutexes = [RwLock::new(24), RwLock::new(42)]; + let collection = RefLockCollection::new(&mutexes); + let guard = collection.try_read(key).unwrap(); + assert_eq!(*guard[0], 24); + assert_eq!(*guard[1], 42); + } + + #[test] + fn try_read_fails_for_locked_collection() { + let key = ThreadKey::get().unwrap(); + let mutexes = [RwLock::new(24), RwLock::new(42)]; + let collection = RefLockCollection::new(&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 can_read_twice_on_different_threads() { + let key = ThreadKey::get().unwrap(); + let mutexes = [RwLock::new(24), RwLock::new(42)]; + let collection = RefLockCollection::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 works_in_collection() { let key = ThreadKey::get().unwrap(); let mutex1 = Mutex::new(0); diff --git a/src/collection/retry.rs b/src/collection/retry.rs index 331b669..775ea29 100644 --- a/src/collection/retry.rs +++ b/src/collection/retry.rs @@ -1,24 +1,18 @@ use std::cell::Cell; use std::collections::HashSet; -use std::marker::PhantomData; use crate::collection::utils; use crate::handle_unwind::handle_unwind; use crate::lockable::{ Lockable, LockableGetMut, LockableIntoInner, OwnedLockable, RawLock, Sharable, }; -use crate::Keyable; +use crate::{Keyable, ThreadKey}; -use super::utils::{attempt_to_recover_locks_from_panic, attempt_to_recover_reads_from_panic}; +use super::utils::{ + attempt_to_recover_locks_from_panic, attempt_to_recover_reads_from_panic, get_locks_unsorted, +}; use super::{LockGuard, RetryingLockCollection}; -/// Get all raw locks in the collection -fn get_locks<L: Lockable>(data: &L) -> Vec<&dyn RawLock> { - let mut locks = Vec::new(); - data.get_ptrs(&mut locks); - locks -} - /// Checks that a collection contains no duplicate references to a lock. fn contains_duplicates<L: Lockable>(data: L) -> bool { let mut locks = Vec::new(); @@ -40,14 +34,14 @@ unsafe impl<L: Lockable> RawLock for RetryingLockCollection<L> { #[mutants::skip] // this should never run #[cfg(not(tarpaulin_include))] fn poison(&self) { - let locks = get_locks(&self.data); + let locks = get_locks_unsorted(&self.data); for lock in locks { lock.poison(); } } unsafe fn raw_lock(&self) { - let locks = get_locks(&self.data); + let locks = get_locks_unsorted(&self.data); if locks.is_empty() { // this probably prevents a panic later @@ -109,7 +103,7 @@ unsafe impl<L: Lockable> RawLock for RetryingLockCollection<L> { } unsafe fn raw_try_lock(&self) -> bool { - let locks = get_locks(&self.data); + let locks = get_locks_unsorted(&self.data); if locks.is_empty() { // this is an interesting case, but it doesn't give us access to @@ -139,7 +133,7 @@ unsafe impl<L: Lockable> RawLock for RetryingLockCollection<L> { } unsafe fn raw_unlock(&self) { - let locks = get_locks(&self.data); + let locks = get_locks_unsorted(&self.data); for lock in locks { lock.raw_unlock(); @@ -147,7 +141,7 @@ unsafe impl<L: Lockable> RawLock for RetryingLockCollection<L> { } unsafe fn raw_read(&self) { - let locks = get_locks(&self.data); + let locks = get_locks_unsorted(&self.data); if locks.is_empty() { // this probably prevents a panic later @@ -200,7 +194,7 @@ unsafe impl<L: Lockable> RawLock for RetryingLockCollection<L> { } unsafe fn raw_try_read(&self) -> bool { - let locks = get_locks(&self.data); + let locks = get_locks_unsorted(&self.data); if locks.is_empty() { // this is an interesting case, but it doesn't give us access to @@ -229,7 +223,7 @@ unsafe impl<L: Lockable> RawLock for RetryingLockCollection<L> { } unsafe fn raw_unlock_read(&self) { - let locks = get_locks(&self.data); + let locks = get_locks_unsorted(&self.data); for lock in locks { lock.raw_unlock_read(); @@ -243,6 +237,11 @@ unsafe impl<L: Lockable> Lockable for RetryingLockCollection<L> { where Self: 'g; + type DataMut<'a> + = L::DataMut<'a> + where + Self: 'a; + fn get_ptrs<'a>(&'a self, ptrs: &mut Vec<&'a dyn RawLock>) { self.data.get_ptrs(ptrs) } @@ -250,6 +249,10 @@ unsafe impl<L: Lockable> Lockable for RetryingLockCollection<L> { unsafe fn guard(&self) -> Self::Guard<'_> { self.data.guard() } + + unsafe fn data_mut(&self) -> Self::DataMut<'_> { + self.data.data_mut() + } } unsafe impl<L: Sharable> Sharable for RetryingLockCollection<L> { @@ -258,9 +261,18 @@ unsafe impl<L: Sharable> Sharable for RetryingLockCollection<L> { where Self: 'g; + type DataRef<'a> + = L::DataRef<'a> + where + Self: 'a; + unsafe fn read_guard(&self) -> Self::ReadGuard<'_> { self.data.read_guard() } + + unsafe fn data_ref(&self) -> Self::DataRef<'_> { + self.data.data_ref() + } } unsafe impl<L: OwnedLockable> OwnedLockable for RetryingLockCollection<L> {} @@ -516,6 +528,46 @@ impl<L: Lockable> RetryingLockCollection<L> { (!contains_duplicates(&data)).then_some(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_try_lock<Key: Keyable, R>( + &self, + key: Key, + f: impl Fn(L::DataMut<'_>) -> 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) + } + } + /// Locks the collection /// /// This function returns a guard that can be used to access the underlying @@ -536,10 +588,7 @@ impl<L: Lockable> RetryingLockCollection<L> { /// *guard.0 += 1; /// *guard.1 = "1"; /// ``` - pub fn lock<'g, 'key: 'g, Key: Keyable + 'key>( - &'g self, - key: Key, - ) -> LockGuard<'key, L::Guard<'g>, Key> { + pub fn lock(&self, key: ThreadKey) -> LockGuard<L::Guard<'_>> { unsafe { // safety: we're taking the thread key self.raw_lock(); @@ -548,7 +597,6 @@ impl<L: Lockable> RetryingLockCollection<L> { // safety: we just locked the collection guard: self.guard(), key, - _phantom: PhantomData, } } } @@ -583,10 +631,7 @@ impl<L: Lockable> RetryingLockCollection<L> { /// }; /// /// ``` - pub fn try_lock<'g, 'key: 'g, Key: Keyable + 'key>( - &'g self, - key: Key, - ) -> Result<LockGuard<'key, L::Guard<'g>, Key>, Key> { + 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() { @@ -594,7 +639,6 @@ impl<L: Lockable> RetryingLockCollection<L> { // safety: we just succeeded in locking everything guard: self.guard(), key, - _phantom: PhantomData, }) } else { Err(key) @@ -620,13 +664,53 @@ impl<L: Lockable> RetryingLockCollection<L> { /// *guard.1 = "1"; /// let key = RetryingLockCollection::<(Mutex<i32>, Mutex<&str>)>::unlock(guard); /// ``` - pub fn unlock<'key, Key: Keyable + 'key>(guard: LockGuard<'key, L::Guard<'_>, Key>) -> Key { + pub fn unlock(guard: LockGuard<L::Guard<'_>>) -> ThreadKey { drop(guard.guard); guard.key } } 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_try_read<Key: Keyable, R>( + &self, + key: Key, + f: impl Fn(L::DataRef<'_>) -> 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) + } + } + /// Locks the collection, so that other threads can still read from it /// /// This function returns a guard that can be used to access the underlying @@ -647,10 +731,7 @@ impl<L: Sharable> RetryingLockCollection<L> { /// assert_eq!(*guard.0, 0); /// assert_eq!(*guard.1, ""); /// ``` - pub fn read<'g, 'key: 'g, Key: Keyable + 'key>( - &'g self, - key: Key, - ) -> LockGuard<'key, L::ReadGuard<'g>, Key> { + pub fn read(&self, key: ThreadKey) -> LockGuard<L::ReadGuard<'_>> { unsafe { // safety: we're taking the thread key self.raw_read(); @@ -659,7 +740,6 @@ impl<L: Sharable> RetryingLockCollection<L> { // safety: we just locked the collection guard: self.read_guard(), key, - _phantom: PhantomData, } } } @@ -687,25 +767,25 @@ impl<L: Sharable> RetryingLockCollection<L> { /// let lock = RetryingLockCollection::new(data); /// /// match lock.try_read(key) { - /// Some(mut guard) => { + /// Ok(mut guard) => { /// assert_eq!(*guard.0, 5); /// assert_eq!(*guard.1, "6"); /// }, - /// None => unreachable!(), + /// Err(_) => unreachable!(), /// }; /// /// ``` - pub fn try_read<'g, 'key: 'g, Key: Keyable + 'key>( - &'g self, - key: Key, - ) -> Option<LockGuard<'key, L::ReadGuard<'g>, Key>> { + pub fn try_read(&self, key: ThreadKey) -> Result<LockGuard<L::ReadGuard<'_>>, ThreadKey> { unsafe { // safety: we're taking the thread key - self.raw_try_lock().then(|| LockGuard { + if !self.raw_try_lock() { + return Err(key); + } + + Ok(LockGuard { // safety: we just succeeded in locking everything guard: self.read_guard(), key, - _phantom: PhantomData, }) } } @@ -726,9 +806,7 @@ impl<L: Sharable> RetryingLockCollection<L> { /// let mut guard = lock.read(key); /// let key = RetryingLockCollection::<(RwLock<i32>, RwLock<&str>)>::unlock_read(guard); /// ``` - pub fn unlock_read<'key, Key: Keyable + 'key>( - guard: LockGuard<'key, L::ReadGuard<'_>, Key>, - ) -> Key { + pub fn unlock_read(guard: LockGuard<L::ReadGuard<'_>>) -> ThreadKey { drop(guard.guard); guard.key } @@ -833,7 +911,7 @@ where mod tests { use super::*; use crate::collection::BoxedLockCollection; - use crate::{Mutex, RwLock, ThreadKey}; + use crate::{LockCollection, Mutex, RwLock, ThreadKey}; #[test] fn nonduplicate_lock_references_are_allowed() { @@ -869,7 +947,6 @@ mod tests { let rwlock1 = RwLock::new(0); let rwlock2 = RwLock::new(0); let collection = RetryingLockCollection::try_new([&rwlock1, &rwlock2]).unwrap(); - // TODO Poisonable::read let guard = collection.read(key); @@ -909,13 +986,14 @@ mod tests { #[test] fn lock_empty_lock_collection() { - let mut key = ThreadKey::get().unwrap(); + let key = ThreadKey::get().unwrap(); let collection: RetryingLockCollection<[RwLock<i32>; 0]> = RetryingLockCollection::new([]); - let guard = collection.lock(&mut key); + let guard = collection.lock(key); assert!(guard.len() == 0); + let key = LockCollection::<[RwLock<_>; 0]>::unlock(guard); - let guard = collection.read(&mut key); + let guard = collection.read(key); assert!(guard.len() == 0); } } diff --git a/src/collection/utils.rs b/src/collection/utils.rs index d6d50f4..1d96e5c 100644 --- a/src/collection/utils.rs +++ b/src/collection/utils.rs @@ -1,7 +1,35 @@ use std::cell::Cell; use crate::handle_unwind::handle_unwind; -use crate::lockable::RawLock; +use crate::lockable::{Lockable, RawLock}; + +#[must_use] +pub fn get_locks<L: Lockable>(data: &L) -> Vec<&dyn RawLock> { + let mut locks = Vec::new(); + data.get_ptrs(&mut locks); + locks.sort_by_key(|lock| &raw const **lock); + locks +} + +#[must_use] +pub fn get_locks_unsorted<L: Lockable>(data: &L) -> Vec<&dyn RawLock> { + let mut locks = Vec::new(); + data.get_ptrs(&mut locks); + locks +} + +/// returns `true` if the sorted list contains a duplicate +#[must_use] +pub fn ordered_contains_duplicates(l: &[&dyn RawLock]) -> bool { + if l.is_empty() { + // Return early to prevent panic in the below call to `windows` + return false; + } + + l.windows(2) + // NOTE: addr_eq is necessary because eq would also compare the v-table pointers + .any(|window| std::ptr::addr_eq(window[0], window[1])) +} /// 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]) { @@ -115,3 +143,13 @@ pub unsafe fn attempt_to_recover_reads_from_panic(locked: &[&dyn RawLock]) { || locked.iter().for_each(|l| l.poison()), ) } + +#[cfg(test)] +mod tests { + use crate::collection::utils::ordered_contains_duplicates; + + #[test] + fn empty_array_does_not_contain_duplicates() { + assert!(!ordered_contains_duplicates(&[])) + } +} diff --git a/src/lockable.rs b/src/lockable.rs index 4f7cfe5..f125c02 100644 --- a/src/lockable.rs +++ b/src/lockable.rs @@ -111,6 +111,10 @@ pub unsafe trait Lockable { where Self: 'g; + type DataMut<'a> + where + Self: 'a; + /// Yields a list of references to the [`RawLock`]s contained within this /// value. /// @@ -129,6 +133,9 @@ pub unsafe trait Lockable { /// unlocked until this guard is dropped. #[must_use] unsafe fn guard(&self) -> Self::Guard<'_>; + + #[must_use] + unsafe fn data_mut(&self) -> Self::DataMut<'_>; } /// Allows a lock to be accessed by multiple readers. @@ -145,6 +152,10 @@ pub unsafe trait Sharable: Lockable { where Self: 'g; + type DataRef<'a> + where + Self: 'a; + /// Returns a guard that can be used to immutably access the underlying /// data. /// @@ -155,6 +166,9 @@ pub unsafe trait Sharable: Lockable { /// unlocked until this guard is dropped. #[must_use] unsafe fn read_guard(&self) -> Self::ReadGuard<'_>; + + #[must_use] + unsafe fn data_ref(&self) -> Self::DataRef<'_>; } /// A type that may be locked and unlocked, and is known to be the only valid @@ -210,6 +224,11 @@ unsafe impl<T: Lockable> Lockable for &T { where Self: 'g; + type DataMut<'a> + = T::DataMut<'a> + where + Self: 'a; + fn get_ptrs<'a>(&'a self, ptrs: &mut Vec<&'a dyn RawLock>) { (*self).get_ptrs(ptrs); } @@ -217,6 +236,10 @@ unsafe impl<T: Lockable> Lockable for &T { unsafe fn guard(&self) -> Self::Guard<'_> { (*self).guard() } + + unsafe fn data_mut(&self) -> Self::DataMut<'_> { + (*self).data_mut() + } } unsafe impl<T: Sharable> Sharable for &T { @@ -225,9 +248,18 @@ unsafe impl<T: Sharable> Sharable for &T { where Self: 'g; + type DataRef<'a> + = T::DataRef<'a> + where + Self: 'a; + unsafe fn read_guard(&self) -> Self::ReadGuard<'_> { (*self).read_guard() } + + unsafe fn data_ref(&self) -> Self::DataRef<'_> { + (*self).data_ref() + } } unsafe impl<T: Lockable> Lockable for &mut T { @@ -236,6 +268,11 @@ unsafe impl<T: Lockable> Lockable for &mut T { where Self: 'g; + type DataMut<'a> + = T::DataMut<'a> + where + Self: 'a; + fn get_ptrs<'a>(&'a self, ptrs: &mut Vec<&'a dyn RawLock>) { (**self).get_ptrs(ptrs) } @@ -243,6 +280,10 @@ unsafe impl<T: Lockable> Lockable for &mut T { unsafe fn guard(&self) -> Self::Guard<'_> { (**self).guard() } + + unsafe fn data_mut(&self) -> Self::DataMut<'_> { + (**self).data_mut() + } } impl<T: LockableGetMut> LockableGetMut for &mut T { @@ -262,9 +303,18 @@ unsafe impl<T: Sharable> Sharable for &mut T { where Self: 'g; + type DataRef<'a> + = T::DataRef<'a> + where + Self: 'a; + unsafe fn read_guard(&self) -> Self::ReadGuard<'_> { (**self).read_guard() } + + unsafe fn data_ref(&self) -> Self::DataRef<'_> { + (**self).data_ref() + } } unsafe impl<T: OwnedLockable> OwnedLockable for &mut T {} @@ -276,6 +326,8 @@ macro_rules! tuple_impls { unsafe impl<$($generic: Lockable,)*> Lockable for ($($generic,)*) { type Guard<'g> = ($($generic::Guard<'g>,)*) where Self: 'g; + type DataMut<'a> = ($($generic::DataMut<'a>,)*) where Self: 'a; + fn get_ptrs<'a>(&'a self, ptrs: &mut Vec<&'a dyn RawLock>) { $(self.$value.get_ptrs(ptrs));* } @@ -285,6 +337,10 @@ macro_rules! tuple_impls { // I don't think any other way of doing it compiles ($(self.$value.guard(),)*) } + + unsafe fn data_mut(&self) -> Self::DataMut<'_> { + ($(self.$value.data_mut(),)*) + } } impl<$($generic: LockableGetMut,)*> LockableGetMut for ($($generic,)*) { @@ -306,9 +362,15 @@ macro_rules! tuple_impls { unsafe impl<$($generic: Sharable,)*> Sharable for ($($generic,)*) { type ReadGuard<'g> = ($($generic::ReadGuard<'g>,)*) where Self: 'g; + type DataRef<'a> = ($($generic::DataRef<'a>,)*) where Self: 'a; + unsafe fn read_guard(&self) -> Self::ReadGuard<'_> { ($(self.$value.read_guard(),)*) } + + unsafe fn data_ref(&self) -> Self::DataRef<'_> { + ($(self.$value.data_ref(),)*) + } } unsafe impl<$($generic: OwnedLockable,)*> OwnedLockable for ($($generic,)*) {} @@ -329,6 +391,11 @@ unsafe impl<T: Lockable, const N: usize> Lockable for [T; N] { where Self: 'g; + type DataMut<'a> + = [T::DataMut<'a>; N] + where + Self: 'a; + fn get_ptrs<'a>(&'a self, ptrs: &mut Vec<&'a dyn RawLock>) { for lock in self { lock.get_ptrs(ptrs); @@ -345,6 +412,15 @@ unsafe impl<T: Lockable, const N: usize> Lockable for [T; N] { guards.map(|g| g.assume_init()) } + + unsafe fn data_mut<'a>(&'a self) -> Self::DataMut<'a> { + let mut guards = MaybeUninit::<[MaybeUninit<T::DataMut<'a>>; N]>::uninit().assume_init(); + for i in 0..N { + guards[i].write(self[i].data_mut()); + } + + guards.map(|g| g.assume_init()) + } } impl<T: LockableGetMut, const N: usize> LockableGetMut for [T; N] { @@ -386,6 +462,11 @@ unsafe impl<T: Sharable, const N: usize> Sharable for [T; N] { where Self: 'g; + type DataRef<'a> + = [T::DataRef<'a>; N] + where + Self: 'a; + unsafe fn read_guard<'g>(&'g self) -> Self::ReadGuard<'g> { let mut guards = MaybeUninit::<[MaybeUninit<T::ReadGuard<'g>>; N]>::uninit().assume_init(); for i in 0..N { @@ -394,6 +475,15 @@ unsafe impl<T: Sharable, const N: usize> Sharable for [T; N] { guards.map(|g| g.assume_init()) } + + unsafe fn data_ref<'a>(&'a self) -> Self::DataRef<'a> { + let mut guards = MaybeUninit::<[MaybeUninit<T::DataRef<'a>>; N]>::uninit().assume_init(); + for i in 0..N { + guards[i].write(self[i].data_ref()); + } + + guards.map(|g| g.assume_init()) + } } unsafe impl<T: OwnedLockable, const N: usize> OwnedLockable for [T; N] {} @@ -404,6 +494,11 @@ unsafe impl<T: Lockable> Lockable for Box<[T]> { where Self: 'g; + type DataMut<'a> + = Box<[T::DataMut<'a>]> + where + Self: 'a; + fn get_ptrs<'a>(&'a self, ptrs: &mut Vec<&'a dyn RawLock>) { for lock in self { lock.get_ptrs(ptrs); @@ -413,6 +508,10 @@ unsafe impl<T: Lockable> Lockable for Box<[T]> { unsafe fn guard(&self) -> Self::Guard<'_> { self.iter().map(|lock| lock.guard()).collect() } + + unsafe fn data_mut(&self) -> Self::DataMut<'_> { + self.iter().map(|lock| lock.data_mut()).collect() + } } impl<T: LockableGetMut + 'static> LockableGetMut for Box<[T]> { @@ -432,9 +531,18 @@ unsafe impl<T: Sharable> Sharable for Box<[T]> { where Self: 'g; + type DataRef<'a> + = Box<[T::DataRef<'a>]> + where + Self: 'a; + unsafe fn read_guard(&self) -> Self::ReadGuard<'_> { self.iter().map(|lock| lock.read_guard()).collect() } + + unsafe fn data_ref(&self) -> Self::DataRef<'_> { + self.iter().map(|lock| lock.data_ref()).collect() + } } unsafe impl<T: Sharable> Sharable for Vec<T> { @@ -443,9 +551,18 @@ unsafe impl<T: Sharable> Sharable for Vec<T> { where Self: 'g; + type DataRef<'a> + = Box<[T::DataRef<'a>]> + where + Self: 'a; + unsafe fn read_guard(&self) -> Self::ReadGuard<'_> { self.iter().map(|lock| lock.read_guard()).collect() } + + unsafe fn data_ref(&self) -> Self::DataRef<'_> { + self.iter().map(|lock| lock.data_ref()).collect() + } } unsafe impl<T: OwnedLockable> OwnedLockable for Box<[T]> {} @@ -457,6 +574,11 @@ unsafe impl<T: Lockable> Lockable for Vec<T> { where Self: 'g; + type DataMut<'a> + = Box<[T::DataMut<'a>]> + where + Self: 'a; + fn get_ptrs<'a>(&'a self, ptrs: &mut Vec<&'a dyn RawLock>) { for lock in self { lock.get_ptrs(ptrs); @@ -466,6 +588,10 @@ unsafe impl<T: Lockable> Lockable for Vec<T> { unsafe fn guard(&self) -> Self::Guard<'_> { self.iter().map(|lock| lock.guard()).collect() } + + unsafe fn data_mut(&self) -> Self::DataMut<'_> { + self.iter().map(|lock| lock.data_mut()).collect() + } } // I'd make a generic impl<T: Lockable, I: IntoIterator<Item=T>> Lockable for I diff --git a/src/mutex.rs b/src/mutex.rs index d6cba7d..2022501 100644 --- a/src/mutex.rs +++ b/src/mutex.rs @@ -3,8 +3,8 @@ use std::marker::PhantomData; use lock_api::RawMutex; -use crate::key::Keyable; use crate::poisonable::PoisonFlag; +use crate::ThreadKey; mod guard; mod mutex; @@ -87,20 +87,19 @@ pub type ParkingMutex<T> = Mutex<T, parking_lot::RawMutex>; /// let mut key = ThreadKey::get().unwrap(); /// /// // Here we use a block to limit the lifetime of the lock guard. -/// let result = { -/// let mut data = data_mutex_clone.lock(&mut key); +/// let result = data_mutex_clone.scoped_lock(&mut key, |data| { /// let result = data.iter().fold(0, |acc, x| acc + x * 2); /// data.push(result); /// result /// // The mutex guard gets dropped here, so the lock is released -/// }; +/// }); /// // The thread key is available again /// *res_mutex_clone.lock(key) += result; /// })); /// }); /// -/// let mut key = ThreadKey::get().unwrap(); -/// let mut data = data_mutex.lock(&mut key); +/// let key = ThreadKey::get().unwrap(); +/// let mut data = data_mutex.lock(key); /// let result = data.iter().fold(0, |acc, x| acc + x * 2); /// data.push(result); /// @@ -108,12 +107,12 @@ pub type ParkingMutex<T> = Mutex<T, parking_lot::RawMutex>; /// // allows other threads to start working on the data immediately. Dropping /// // the data also gives us access to the thread key, so we can lock /// // another mutex. -/// drop(data); +/// let key = Mutex::unlock(data); /// /// // Here the mutex guard is not assigned to a variable and so, even if the /// // scope does not end after this line, the mutex is still released: there is /// // no deadlock. -/// *res_mutex.lock(&mut key) += result; +/// *res_mutex.lock(key) += result; /// /// threads.into_iter().for_each(|thread| { /// thread @@ -121,6 +120,7 @@ pub type ParkingMutex<T> = Mutex<T, parking_lot::RawMutex>; /// .expect("The thread creating or execution failed !") /// }); /// +/// let key = ThreadKey::get().unwrap(); /// assert_eq!(*res_mutex.lock(key), 800); /// ``` /// @@ -153,10 +153,9 @@ pub struct MutexRef<'a, T: ?Sized + 'a, R: RawMutex>( // // This is the most lifetime-intensive thing I've ever written. Can I graduate // from borrow checker university now? -pub struct MutexGuard<'a, 'key: 'a, T: ?Sized + 'a, Key: Keyable + 'key, R: RawMutex> { +pub struct MutexGuard<'a, T: ?Sized + 'a, R: RawMutex> { mutex: MutexRef<'a, T, R>, // this way we don't need to re-implement Drop - thread_key: Key, - _phantom: PhantomData<&'key ()>, + thread_key: ThreadKey, } #[cfg(test)] @@ -194,61 +193,46 @@ mod tests { #[test] fn display_works_for_ref() { let mutex: crate::Mutex<_> = Mutex::new("Hello, world!"); - let guard = unsafe { mutex.try_lock_no_key().unwrap() }; // TODO lock_no_key + let guard = unsafe { mutex.try_lock_no_key().unwrap() }; assert_eq!(guard.to_string(), "Hello, world!".to_string()); } #[test] - fn ord_works() { - let key = ThreadKey::get().unwrap(); - let mutex1: crate::Mutex<_> = Mutex::new(1); - let mutex2: crate::Mutex<_> = Mutex::new(2); - let mutex3: crate::Mutex<_> = Mutex::new(2); - let collection = LockCollection::try_new((&mutex1, &mutex2, &mutex3)).unwrap(); - - let guard = collection.lock(key); - assert!(guard.0 < guard.1); - assert!(guard.1 > guard.0); - assert!(guard.1 == guard.2); - assert!(guard.0 != guard.2) - } - - #[test] fn ref_as_mut() { - let mut key = ThreadKey::get().unwrap(); + let key = ThreadKey::get().unwrap(); let collection = LockCollection::new(crate::Mutex::new(0)); - let mut guard = collection.lock(&mut key); + let mut guard = collection.lock(key); let guard_mut = guard.as_mut().as_mut(); *guard_mut = 3; - drop(guard); + let key = LockCollection::<crate::Mutex<_>>::unlock(guard); - let guard = collection.lock(&mut key); + let guard = collection.lock(key); assert_eq!(guard.as_ref().as_ref(), &3); } #[test] fn guard_as_mut() { - let mut key = ThreadKey::get().unwrap(); + let key = ThreadKey::get().unwrap(); let mutex = crate::Mutex::new(0); - let mut guard = mutex.lock(&mut key); + let mut guard = mutex.lock(key); let guard_mut = guard.as_mut(); *guard_mut = 3; - drop(guard); + let key = Mutex::unlock(guard); - let guard = mutex.lock(&mut key); + let guard = mutex.lock(key); assert_eq!(guard.as_ref(), &3); } #[test] fn dropping_guard_releases_mutex() { - let mut key = ThreadKey::get().unwrap(); + let key = ThreadKey::get().unwrap(); let mutex: crate::Mutex<_> = Mutex::new("Hello, world!"); - let guard = mutex.lock(&mut key); + let guard = mutex.lock(key); drop(guard); assert!(!mutex.is_locked()); diff --git a/src/mutex/guard.rs b/src/mutex/guard.rs index 4e4d5f1..22e59c1 100644 --- a/src/mutex/guard.rs +++ b/src/mutex/guard.rs @@ -5,34 +5,14 @@ use std::ops::{Deref, DerefMut}; use lock_api::RawMutex; -use crate::key::Keyable; use crate::lockable::RawLock; +use crate::ThreadKey; use super::{Mutex, MutexGuard, MutexRef}; // These impls make things slightly easier because now you can use // `println!("{guard}")` instead of `println!("{}", *guard)` -impl<T: PartialEq + ?Sized, R: RawMutex> PartialEq for MutexRef<'_, T, R> { - fn eq(&self, other: &Self) -> bool { - self.deref().eq(&**other) - } -} - -impl<T: Eq + ?Sized, R: RawMutex> Eq for MutexRef<'_, T, R> {} - -impl<T: PartialOrd + ?Sized, R: RawMutex> PartialOrd for MutexRef<'_, T, R> { - fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { - self.deref().partial_cmp(&**other) - } -} - -impl<T: Ord + ?Sized, R: RawMutex> Ord for MutexRef<'_, T, R> { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.deref().cmp(&**other) - } -} - #[mutants::skip] // hashing involves RNG and is hard to test #[cfg(not(tarpaulin_include))] impl<T: Hash + ?Sized, R: RawMutex> Hash for MutexRef<'_, T, R> { @@ -107,39 +87,9 @@ impl<'a, T: ?Sized, R: RawMutex> MutexRef<'a, T, R> { // it's kinda annoying to re-implement some of this stuff on guards // there's nothing i can do about that -#[mutants::skip] // it's hard to get two guards safely -#[cfg(not(tarpaulin_include))] -impl<T: PartialEq + ?Sized, R: RawMutex, Key: Keyable> PartialEq for MutexGuard<'_, '_, T, Key, R> { - fn eq(&self, other: &Self) -> bool { - self.deref().eq(&**other) - } -} - -#[mutants::skip] // it's hard to get two guards safely -#[cfg(not(tarpaulin_include))] -impl<T: Eq + ?Sized, R: RawMutex, Key: Keyable> Eq for MutexGuard<'_, '_, T, Key, R> {} - -#[mutants::skip] // it's hard to get two guards safely -#[cfg(not(tarpaulin_include))] -impl<T: PartialOrd + ?Sized, R: RawMutex, Key: Keyable> PartialOrd - for MutexGuard<'_, '_, T, Key, R> -{ - fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { - self.deref().partial_cmp(&**other) - } -} - -#[mutants::skip] // it's hard to get two guards safely -#[cfg(not(tarpaulin_include))] -impl<T: Ord + ?Sized, R: RawMutex, Key: Keyable> Ord for MutexGuard<'_, '_, T, Key, R> { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.deref().cmp(&**other) - } -} - #[mutants::skip] // hashing involves RNG and is hard to test #[cfg(not(tarpaulin_include))] -impl<T: Hash + ?Sized, R: RawMutex, Key: Keyable> Hash for MutexGuard<'_, '_, T, Key, R> { +impl<T: Hash + ?Sized, R: RawMutex> Hash for MutexGuard<'_, T, R> { fn hash<H: std::hash::Hasher>(&self, state: &mut H) { self.deref().hash(state) } @@ -147,19 +97,19 @@ impl<T: Hash + ?Sized, R: RawMutex, Key: Keyable> Hash for MutexGuard<'_, '_, T, #[mutants::skip] #[cfg(not(tarpaulin_include))] -impl<T: Debug + ?Sized, Key: Keyable, R: RawMutex> Debug for MutexGuard<'_, '_, T, Key, R> { +impl<T: Debug + ?Sized, R: RawMutex> Debug for MutexGuard<'_, T, R> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { Debug::fmt(&**self, f) } } -impl<T: Display + ?Sized, Key: Keyable, R: RawMutex> Display for MutexGuard<'_, '_, T, Key, R> { +impl<T: Display + ?Sized, R: RawMutex> Display for MutexGuard<'_, T, R> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { Display::fmt(&**self, f) } } -impl<T: ?Sized, Key: Keyable, R: RawMutex> Deref for MutexGuard<'_, '_, T, Key, R> { +impl<T: ?Sized, R: RawMutex> Deref for MutexGuard<'_, T, R> { type Target = T; fn deref(&self) -> &Self::Target { @@ -167,33 +117,32 @@ impl<T: ?Sized, Key: Keyable, R: RawMutex> Deref for MutexGuard<'_, '_, T, Key, } } -impl<T: ?Sized, Key: Keyable, R: RawMutex> DerefMut for MutexGuard<'_, '_, T, Key, R> { +impl<T: ?Sized, R: RawMutex> DerefMut for MutexGuard<'_, T, R> { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.mutex } } -impl<T: ?Sized, Key: Keyable, R: RawMutex> AsRef<T> for MutexGuard<'_, '_, T, Key, R> { +impl<T: ?Sized, R: RawMutex> AsRef<T> for MutexGuard<'_, T, R> { fn as_ref(&self) -> &T { self } } -impl<T: ?Sized, Key: Keyable, R: RawMutex> AsMut<T> for MutexGuard<'_, '_, T, Key, R> { +impl<T: ?Sized, R: RawMutex> AsMut<T> for MutexGuard<'_, T, R> { fn as_mut(&mut self) -> &mut T { self } } -impl<'a, T: ?Sized, Key: Keyable, R: RawMutex> MutexGuard<'a, '_, T, Key, R> { +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: Key) -> Self { + pub(super) unsafe fn new(mutex: &'a Mutex<T, R>, thread_key: ThreadKey) -> Self { Self { mutex: MutexRef(mutex, PhantomData), thread_key, - _phantom: PhantomData, } } } diff --git a/src/mutex/mutex.rs b/src/mutex/mutex.rs index 0bd5286..1d8ce8b 100644 --- a/src/mutex/mutex.rs +++ b/src/mutex/mutex.rs @@ -6,9 +6,9 @@ use std::panic::AssertUnwindSafe; use lock_api::RawMutex; use crate::handle_unwind::handle_unwind; -use crate::key::Keyable; use crate::lockable::{Lockable, LockableGetMut, LockableIntoInner, OwnedLockable, RawLock}; use crate::poisonable::PoisonFlag; +use crate::{Keyable, ThreadKey}; use super::{Mutex, MutexGuard, MutexRef}; @@ -62,6 +62,11 @@ unsafe impl<T: Send, R: RawMutex + Send + Sync> Lockable for Mutex<T, R> { where Self: 'g; + type DataMut<'a> + = &'a mut T + where + Self: 'a; + fn get_ptrs<'a>(&'a self, ptrs: &mut Vec<&'a dyn RawLock>) { ptrs.push(self); } @@ -69,6 +74,10 @@ unsafe impl<T: Send, R: RawMutex + Send + Sync> Lockable for Mutex<T, R> { unsafe fn guard(&self) -> Self::Guard<'_> { MutexRef::new(self) } + + unsafe fn data_mut(&self) -> Self::DataMut<'_> { + self.data.get().as_mut().unwrap_unchecked() + } } impl<T: Send, R: RawMutex + Send + Sync> LockableIntoInner for Mutex<T, R> { @@ -214,6 +223,46 @@ 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 + } + } + + pub fn scoped_try_lock<Key: Keyable, Ret>( + &self, + key: Key, + f: impl FnOnce(&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) + } + } + /// Block the thread until this mutex can be locked, and lock it. /// /// Upon returning, the thread is the only thread with a lock on the @@ -237,7 +286,7 @@ impl<T: ?Sized, R: RawMutex> Mutex<T, R> { /// let key = ThreadKey::get().unwrap(); /// assert_eq!(*mutex.lock(key), 10); /// ``` - pub fn lock<'s, 'k: 's, Key: Keyable>(&'s self, key: Key) -> MutexGuard<'s, 'k, T, Key, R> { + pub fn lock(&self, key: ThreadKey) -> MutexGuard<'_, T, R> { unsafe { // safety: we have the thread key self.raw_lock(); @@ -280,10 +329,7 @@ impl<T: ?Sized, R: RawMutex> Mutex<T, R> { /// let key = ThreadKey::get().unwrap(); /// assert_eq!(*mutex.lock(key), 10); /// ``` - pub fn try_lock<'s, 'k: 's, Key: Keyable>( - &'s self, - key: Key, - ) -> Result<MutexGuard<'s, 'k, T, Key, R>, Key> { + 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() { @@ -322,7 +368,8 @@ impl<T: ?Sized, R: RawMutex> Mutex<T, R> { /// /// let key = Mutex::unlock(guard); /// ``` - pub fn unlock<'a, 'k: 'a, Key: Keyable + 'k>(guard: MutexGuard<'a, 'k, T, Key, R>) -> Key { + #[must_use] + pub fn unlock(guard: MutexGuard<'_, T, R>) -> ThreadKey { unsafe { guard.mutex.0.raw_unlock(); } diff --git a/src/poisonable.rs b/src/poisonable.rs index bb6ad28..8d5a810 100644 --- a/src/poisonable.rs +++ b/src/poisonable.rs @@ -1,6 +1,8 @@ use std::marker::PhantomData; use std::sync::atomic::AtomicBool; +use crate::ThreadKey; + mod error; mod flag; mod guard; @@ -55,10 +57,9 @@ pub struct PoisonRef<'a, G> { /// An RAII guard for a [`Poisonable`]. /// /// This is created by calling methods like [`Poisonable::lock`]. -pub struct PoisonGuard<'a, 'key: 'a, G, Key: 'key> { +pub struct PoisonGuard<'a, G> { guard: PoisonRef<'a, G>, - key: Key, - _phantom: PhantomData<&'key ()>, + key: ThreadKey, } /// A type of error which can be returned when acquiring a [`Poisonable`] lock. @@ -69,9 +70,9 @@ pub struct PoisonError<Guard> { /// An enumeration of possible errors associated with /// [`TryLockPoisonableResult`] which can occur while trying to acquire a lock /// (i.e.: [`Poisonable::try_lock`]). -pub enum TryLockPoisonableError<'flag, 'key: 'flag, G, Key: 'key> { - Poisoned(PoisonError<PoisonGuard<'flag, 'key, G, Key>>), - WouldBlock(Key), +pub enum TryLockPoisonableError<'flag, G> { + Poisoned(PoisonError<PoisonGuard<'flag, G>>), + WouldBlock(ThreadKey), } /// A type alias for the result of a lock method which can poisoned. @@ -89,8 +90,8 @@ pub type PoisonResult<Guard> = Result<Guard, PoisonError<Guard>>; /// For more information, see [`PoisonResult`]. A `TryLockPoisonableResult` /// doesn't necessarily hold the associated guard in the [`Err`] type as the /// lock might not have been acquired for other reasons. -pub type TryLockPoisonableResult<'flag, 'key, G, Key> = - Result<PoisonGuard<'flag, 'key, G, Key>, TryLockPoisonableError<'flag, 'key, G, Key>>; +pub type TryLockPoisonableResult<'flag, G> = + Result<PoisonGuard<'flag, G>, TryLockPoisonableError<'flag, G>>; #[cfg(test)] mod tests { @@ -101,6 +102,105 @@ mod tests { use crate::{LockCollection, Mutex, ThreadKey}; #[test] + fn locking_poisoned_mutex_returns_error_in_collection() { + let key = ThreadKey::get().unwrap(); + let mutex = LockCollection::new(Poisonable::new(Mutex::new(42))); + + std::thread::scope(|s| { + s.spawn(|| { + let key = ThreadKey::get().unwrap(); + let mut guard1 = mutex.lock(key); + let guard = guard1.as_deref_mut().unwrap(); + assert_eq!(**guard, 42); + panic!(); + + #[allow(unreachable_code)] + drop(guard1); + }) + .join() + .unwrap_err(); + }); + + let error = mutex.lock(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(); + assert!(guard.is_ok()); + assert_eq!(*guard.unwrap(), 42); + } + + #[test] + fn non_poisoned_get_mut_is_err() { + let mut mutex = Poisonable::new(Mutex::new(42)); + + let _ = std::panic::catch_unwind(|| { + let key = ThreadKey::get().unwrap(); + #[allow(unused_variables)] + let guard = mutex.lock(key); + panic!(); + #[allow(unreachable_code)] + drop(guard); + }); + + let guard = mutex.get_mut(); + assert!(guard.is_err()); + assert_eq!(**guard.unwrap_err().get_ref(), 42); + } + + #[test] + fn unpoisoned_into_inner() { + let mutex = Poisonable::new(Mutex::new("foo")); + assert_eq!(mutex.into_inner().unwrap(), "foo"); + } + + #[test] + fn poisoned_into_inner() { + let mutex = Poisonable::from(Mutex::new("foo")); + + std::panic::catch_unwind(|| { + let key = ThreadKey::get().unwrap(); + #[allow(unused_variables)] + let guard = mutex.lock(key); + panic!(); + #[allow(unreachable_code)] + drop(guard); + }) + .unwrap_err(); + + let error = mutex.into_inner().unwrap_err(); + assert_eq!(error.into_inner(), "foo"); + } + + #[test] + fn unpoisoned_into_child() { + let mutex = Poisonable::new(Mutex::new("foo")); + assert_eq!(mutex.into_child().unwrap().into_inner(), "foo"); + } + + #[test] + fn poisoned_into_child() { + let mutex = Poisonable::from(Mutex::new("foo")); + + std::panic::catch_unwind(|| { + let key = ThreadKey::get().unwrap(); + #[allow(unused_variables)] + let guard = mutex.lock(key); + panic!(); + #[allow(unreachable_code)] + drop(guard); + }) + .unwrap_err(); + + let error = mutex.into_child().unwrap_err(); + assert_eq!(error.into_inner().into_inner(), "foo"); + } + + #[test] fn display_works() { let key = ThreadKey::get().unwrap(); let mutex = Poisonable::new(Mutex::new("Hello, world!")); @@ -111,21 +211,75 @@ mod tests { } #[test] - fn ord_works() { + fn ref_as_ref() { + let key = ThreadKey::get().unwrap(); + let collection = LockCollection::new(Poisonable::new(Mutex::new("foo"))); + let guard = collection.lock(key); + let Ok(ref guard) = guard.as_ref() else { + panic!() + }; + assert_eq!(**guard.as_ref(), "foo"); + } + + #[test] + fn ref_as_mut() { let key = ThreadKey::get().unwrap(); - let lock1 = Poisonable::new(Mutex::new(1)); - let lock2 = Poisonable::new(Mutex::new(3)); - let lock3 = Poisonable::new(Mutex::new(3)); - let collection = LockCollection::try_new((&lock1, &lock2, &lock3)).unwrap(); + let collection = LockCollection::new(Poisonable::new(Mutex::new("foo"))); + let mut guard1 = collection.lock(key); + let Ok(ref mut guard) = guard1.as_mut() else { + panic!() + }; + let guard = guard.as_mut(); + **guard = "bar"; + + let key = LockCollection::<Poisonable<Mutex<_>>>::unlock(guard1); + let guard = collection.lock(key); + let guard = guard.as_deref().unwrap(); + assert_eq!(*guard.as_ref(), "bar"); + } + #[test] + fn guard_as_ref() { + let key = ThreadKey::get().unwrap(); + let collection = Poisonable::new(Mutex::new("foo")); let guard = collection.lock(key); - let guard1 = guard.0.as_ref().unwrap(); - let guard2 = guard.1.as_ref().unwrap(); - let guard3 = guard.2.as_ref().unwrap(); - assert_eq!(guard1.cmp(guard2), std::cmp::Ordering::Less); - assert_eq!(guard2.cmp(guard1), std::cmp::Ordering::Greater); - assert!(guard2 == guard3); - assert!(guard1 != guard3); + let Ok(ref guard) = guard.as_ref() else { + panic!() + }; + assert_eq!(**guard.as_ref(), "foo"); + } + + #[test] + fn guard_as_mut() { + let key = ThreadKey::get().unwrap(); + let mutex = Poisonable::new(Mutex::new("foo")); + let mut guard1 = mutex.lock(key); + let Ok(ref mut guard) = guard1.as_mut() else { + panic!() + }; + let guard = guard.as_mut(); + **guard = "bar"; + + let key = Poisonable::<Mutex<_>>::unlock(guard1.unwrap()); + let guard = mutex.lock(key); + let guard = guard.as_deref().unwrap(); + assert_eq!(*guard, "bar"); + } + + #[test] + fn deref_mut_in_collection() { + let key = ThreadKey::get().unwrap(); + let collection = LockCollection::new(Poisonable::new(Mutex::new(42))); + let mut guard1 = collection.lock(key); + let Ok(ref mut guard) = guard1.as_mut() else { + panic!() + }; + // TODO make this more convenient + assert_eq!(***guard, 42); + ***guard = 24; + + let key = LockCollection::<Poisonable<Mutex<_>>>::unlock(guard1); + _ = collection.lock(key); } #[test] @@ -202,18 +356,45 @@ mod tests { assert!(mutex.is_poisoned()); - let mut key = ThreadKey::get().unwrap(); - let mut error = mutex.lock(&mut key).unwrap_err(); + let key: ThreadKey = ThreadKey::get().unwrap(); + let mut error = mutex.lock(key).unwrap_err(); let error1 = error.as_mut(); **error1 = "bar"; - drop(error); + let key = Poisonable::<Mutex<_>>::unlock(error.into_inner()); mutex.clear_poison(); - let guard = mutex.lock(&mut key).unwrap(); + let guard = mutex.lock(key).unwrap(); assert_eq!(&**guard, "bar"); } #[test] + fn try_error_from_lock_error() { + let mutex = Poisonable::new(Mutex::new("foo")); + + let _ = std::panic::catch_unwind(|| { + let key = ThreadKey::get().unwrap(); + #[allow(unused_variables)] + let guard = mutex.lock(key); + panic!(); + + #[allow(unknown_lints)] + #[allow(unreachable_code)] + drop(guard); + }); + + assert!(mutex.is_poisoned()); + + let key = ThreadKey::get().unwrap(); + let error = mutex.lock(key).unwrap_err(); + let error = TryLockPoisonableError::from(error); + + let TryLockPoisonableError::Poisoned(error) = error else { + panic!() + }; + assert_eq!(&**error.into_inner(), "foo"); + } + + #[test] fn new_poisonable_is_not_poisoned() { let mutex = Poisonable::new(Mutex::new(42)); assert!(!mutex.is_poisoned()); diff --git a/src/poisonable/error.rs b/src/poisonable/error.rs index bff011d..b69df5d 100644 --- a/src/poisonable/error.rs +++ b/src/poisonable/error.rs @@ -109,7 +109,7 @@ impl<Guard> PoisonError<Guard> { /// /// let key = ThreadKey::get().unwrap(); /// let p_err = mutex.lock(key).unwrap_err(); - /// let data: &PoisonGuard<_, _> = p_err.get_ref(); + /// let data: &PoisonGuard<_> = p_err.get_ref(); /// println!("recovered {} items", data.len()); /// ``` #[must_use] @@ -154,7 +154,7 @@ impl<Guard> PoisonError<Guard> { #[mutants::skip] #[cfg(not(tarpaulin_include))] -impl<G, Key> fmt::Debug for TryLockPoisonableError<'_, '_, G, Key> { +impl<G> fmt::Debug for TryLockPoisonableError<'_, G> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { Self::Poisoned(..) => "Poisoned(..)".fmt(f), @@ -163,7 +163,7 @@ impl<G, Key> fmt::Debug for TryLockPoisonableError<'_, '_, G, Key> { } } -impl<G, Key> fmt::Display for TryLockPoisonableError<'_, '_, G, Key> { +impl<G> fmt::Display for TryLockPoisonableError<'_, G> { #[cfg_attr(test, mutants::skip)] #[cfg(not(tarpaulin_include))] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -175,12 +175,10 @@ impl<G, Key> fmt::Display for TryLockPoisonableError<'_, '_, G, Key> { } } -impl<G, Key> Error for TryLockPoisonableError<'_, '_, G, Key> {} +impl<G> Error for TryLockPoisonableError<'_, G> {} -impl<'flag, 'key, G, Key> From<PoisonError<PoisonGuard<'flag, 'key, G, Key>>> - for TryLockPoisonableError<'flag, 'key, G, Key> -{ - fn from(value: PoisonError<PoisonGuard<'flag, 'key, G, Key>>) -> Self { +impl<'flag, G> From<PoisonError<PoisonGuard<'flag, G>>> for TryLockPoisonableError<'flag, G> { + fn from(value: PoisonError<PoisonGuard<'flag, G>>) -> Self { Self::Poisoned(value) } } diff --git a/src/poisonable/guard.rs b/src/poisonable/guard.rs index 3f85d25..b887e2d 100644 --- a/src/poisonable/guard.rs +++ b/src/poisonable/guard.rs @@ -3,8 +3,6 @@ use std::hash::Hash; use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; -use crate::Keyable; - use super::{PoisonFlag, PoisonGuard, PoisonRef}; impl<'a, Guard> PoisonRef<'a, Guard> { @@ -28,26 +26,6 @@ impl<Guard> Drop for PoisonRef<'_, Guard> { } } -impl<Guard: PartialEq> PartialEq for PoisonRef<'_, Guard> { - fn eq(&self, other: &Self) -> bool { - self.guard.eq(&other.guard) - } -} - -impl<Guard: PartialOrd> PartialOrd for PoisonRef<'_, Guard> { - fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { - self.guard.partial_cmp(&other.guard) - } -} - -impl<Guard: Eq> Eq for PoisonRef<'_, Guard> {} - -impl<Guard: Ord> Ord for PoisonRef<'_, Guard> { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.guard.cmp(&other.guard) - } -} - #[mutants::skip] // hashing involves RNG and is hard to test #[cfg(not(tarpaulin_include))] impl<Guard: Hash> Hash for PoisonRef<'_, Guard> { @@ -96,37 +74,9 @@ impl<Guard> AsMut<Guard> for PoisonRef<'_, Guard> { } } -#[mutants::skip] // it's hard to get two guards safely -#[cfg(not(tarpaulin_include))] -impl<Guard: PartialEq, Key: Keyable> PartialEq for PoisonGuard<'_, '_, Guard, Key> { - fn eq(&self, other: &Self) -> bool { - self.guard.eq(&other.guard) - } -} - -#[mutants::skip] // it's hard to get two guards safely -#[cfg(not(tarpaulin_include))] -impl<Guard: PartialOrd, Key: Keyable> PartialOrd for PoisonGuard<'_, '_, Guard, Key> { - fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { - self.guard.partial_cmp(&other.guard) - } -} - -#[mutants::skip] // it's hard to get two guards safely -#[cfg(not(tarpaulin_include))] -impl<Guard: Eq, Key: Keyable> Eq for PoisonGuard<'_, '_, Guard, Key> {} - -#[mutants::skip] // it's hard to get two guards safely -#[cfg(not(tarpaulin_include))] -impl<Guard: Ord, Key: Keyable> Ord for PoisonGuard<'_, '_, Guard, Key> { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.guard.cmp(&other.guard) - } -} - #[mutants::skip] // hashing involves RNG and is hard to test #[cfg(not(tarpaulin_include))] -impl<Guard: Hash, Key: Keyable> Hash for PoisonGuard<'_, '_, Guard, Key> { +impl<Guard: Hash> Hash for PoisonGuard<'_, Guard> { fn hash<H: std::hash::Hasher>(&self, state: &mut H) { self.guard.hash(state) } @@ -134,19 +84,19 @@ impl<Guard: Hash, Key: Keyable> Hash for PoisonGuard<'_, '_, Guard, Key> { #[mutants::skip] #[cfg(not(tarpaulin_include))] -impl<Guard: Debug, Key: Keyable> Debug for PoisonGuard<'_, '_, Guard, Key> { +impl<Guard: Debug> Debug for PoisonGuard<'_, Guard> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { Debug::fmt(&self.guard, f) } } -impl<Guard: Display, Key: Keyable> Display for PoisonGuard<'_, '_, Guard, Key> { +impl<Guard: Display> Display for PoisonGuard<'_, Guard> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { Display::fmt(&self.guard, f) } } -impl<T, Guard: Deref<Target = T>, Key: Keyable> Deref for PoisonGuard<'_, '_, Guard, Key> { +impl<T, Guard: Deref<Target = T>> Deref for PoisonGuard<'_, Guard> { type Target = T; fn deref(&self) -> &Self::Target { @@ -155,20 +105,20 @@ impl<T, Guard: Deref<Target = T>, Key: Keyable> Deref for PoisonGuard<'_, '_, Gu } } -impl<T, Guard: DerefMut<Target = T>, Key: Keyable> DerefMut for PoisonGuard<'_, '_, Guard, Key> { +impl<T, Guard: DerefMut<Target = T>> DerefMut for PoisonGuard<'_, Guard> { fn deref_mut(&mut self) -> &mut Self::Target { #[allow(clippy::explicit_auto_deref)] // fixing this results in a compiler error &mut *self.guard.guard } } -impl<Guard, Key: Keyable> AsRef<Guard> for PoisonGuard<'_, '_, Guard, Key> { +impl<Guard> AsRef<Guard> for PoisonGuard<'_, Guard> { fn as_ref(&self) -> &Guard { &self.guard.guard } } -impl<Guard, Key: Keyable> AsMut<Guard> for PoisonGuard<'_, '_, Guard, Key> { +impl<Guard> AsMut<Guard> for PoisonGuard<'_, Guard> { fn as_mut(&mut self) -> &mut Guard { &mut self.guard.guard } diff --git a/src/poisonable/poisonable.rs b/src/poisonable/poisonable.rs index 2dac4bb..efe4ed0 100644 --- a/src/poisonable/poisonable.rs +++ b/src/poisonable/poisonable.rs @@ -1,10 +1,9 @@ -use std::marker::PhantomData; use std::panic::{RefUnwindSafe, UnwindSafe}; use crate::lockable::{ Lockable, LockableGetMut, LockableIntoInner, OwnedLockable, RawLock, Sharable, }; -use crate::Keyable; +use crate::{Keyable, ThreadKey}; use super::{ PoisonError, PoisonFlag, PoisonGuard, PoisonRef, PoisonResult, Poisonable, @@ -49,6 +48,11 @@ unsafe impl<L: Lockable> Lockable for Poisonable<L> { where Self: 'g; + type DataMut<'a> + = PoisonResult<L::DataMut<'a>> + where + Self: 'a; + fn get_ptrs<'a>(&'a self, ptrs: &mut Vec<&'a dyn RawLock>) { self.inner.get_ptrs(ptrs) } @@ -62,6 +66,14 @@ unsafe impl<L: Lockable> Lockable for Poisonable<L> { Ok(ref_guard) } } + + unsafe fn data_mut(&self) -> Self::DataMut<'_> { + if self.is_poisoned() { + Err(PoisonError::new(self.inner.data_mut())) + } else { + Ok(self.inner.data_mut()) + } + } } unsafe impl<L: Sharable> Sharable for Poisonable<L> { @@ -70,6 +82,11 @@ unsafe impl<L: Sharable> Sharable for Poisonable<L> { where Self: 'g; + type DataRef<'a> + = PoisonResult<L::DataRef<'a>> + where + Self: 'a; + unsafe fn read_guard(&self) -> Self::ReadGuard<'_> { let ref_guard = PoisonRef::new(&self.poisoned, self.inner.read_guard()); @@ -79,6 +96,14 @@ unsafe impl<L: Sharable> Sharable for Poisonable<L> { Ok(ref_guard) } } + + unsafe fn data_ref(&self) -> Self::DataRef<'_> { + if self.is_poisoned() { + Err(PoisonError::new(self.inner.data_ref())) + } else { + Ok(self.inner.data_ref()) + } + } } unsafe impl<L: OwnedLockable> OwnedLockable for Poisonable<L> {} @@ -266,14 +291,10 @@ impl<L> Poisonable<L> { impl<L: Lockable> Poisonable<L> { /// Creates a guard for the poisonable, without locking it - unsafe fn guard<'flag, 'key, Key: Keyable + 'key>( - &'flag self, - key: Key, - ) -> PoisonResult<PoisonGuard<'flag, 'key, L::Guard<'flag>, Key>> { + unsafe fn guard(&self, key: ThreadKey) -> PoisonResult<PoisonGuard<'_, L::Guard<'_>>> { let guard = PoisonGuard { guard: PoisonRef::new(&self.poisoned, self.inner.guard()), key, - _phantom: PhantomData, }; if self.is_poisoned() { @@ -285,6 +306,50 @@ impl<L: Lockable> Poisonable<L> { } impl<L: Lockable + RawLock> Poisonable<L> { + pub fn scoped_lock<'a, R>( + &'a self, + key: impl Keyable, + f: impl Fn(<Self as Lockable>::DataMut<'a>) -> 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_try_lock<'a, Key: Keyable, R>( + &'a self, + key: Key, + f: impl Fn(<Self as Lockable>::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) + } + } + /// Acquires the lock, blocking the current thread until it is ok to do so. /// /// This function will block the current thread until it is available to @@ -316,10 +381,7 @@ impl<L: Lockable + RawLock> Poisonable<L> { /// let key = ThreadKey::get().unwrap(); /// assert_eq!(*mutex.lock(key).unwrap(), 10); /// ``` - pub fn lock<'flag, 'key, Key: Keyable + 'key>( - &'flag self, - key: Key, - ) -> PoisonResult<PoisonGuard<'flag, 'key, L::Guard<'flag>, Key>> { + pub fn lock(&self, key: ThreadKey) -> PoisonResult<PoisonGuard<'_, L::Guard<'_>>> { unsafe { self.inner.raw_lock(); self.guard(key) @@ -370,10 +432,7 @@ impl<L: Lockable + RawLock> Poisonable<L> { /// /// [`Poisoned`]: `TryLockPoisonableError::Poisoned` /// [`WouldBlock`]: `TryLockPoisonableError::WouldBlock` - pub fn try_lock<'flag, 'key, Key: Keyable + 'key>( - &'flag self, - key: Key, - ) -> TryLockPoisonableResult<'flag, 'key, L::Guard<'flag>, Key> { + pub fn try_lock(&self, key: ThreadKey) -> TryLockPoisonableResult<'_, L::Guard<'_>> { unsafe { if self.inner.raw_try_lock() { Ok(self.guard(key)?) @@ -398,23 +457,17 @@ impl<L: Lockable + RawLock> Poisonable<L> { /// /// let key = Poisonable::<Mutex<_>>::unlock(guard); /// ``` - pub fn unlock<'flag, 'key, Key: Keyable + 'key>( - guard: PoisonGuard<'flag, 'key, L::Guard<'flag>, Key>, - ) -> Key { + pub fn unlock<'flag>(guard: PoisonGuard<'flag, L::Guard<'flag>>) -> ThreadKey { drop(guard.guard); guard.key } } impl<L: Sharable + RawLock> Poisonable<L> { - unsafe fn read_guard<'flag, 'key, Key: Keyable + 'key>( - &'flag self, - key: Key, - ) -> PoisonResult<PoisonGuard<'flag, 'key, L::ReadGuard<'flag>, Key>> { + unsafe fn read_guard(&self, key: ThreadKey) -> PoisonResult<PoisonGuard<'_, L::ReadGuard<'_>>> { let guard = PoisonGuard { guard: PoisonRef::new(&self.poisoned, self.inner.read_guard()), key, - _phantom: PhantomData, }; if self.is_poisoned() { @@ -424,6 +477,50 @@ impl<L: Sharable + RawLock> Poisonable<L> { Ok(guard) } + pub fn scoped_read<'a, R>( + &'a self, + key: impl Keyable, + f: impl Fn(<Self as Sharable>::DataRef<'a>) -> 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_try_read<'a, Key: Keyable, R>( + &'a self, + key: Key, + f: impl Fn(<Self as Sharable>::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) + } + } + /// Locks with shared read access, blocking the current thread until it can /// be acquired. /// @@ -457,10 +554,7 @@ impl<L: Sharable + RawLock> Poisonable<L> { /// assert!(c_lock.read(key).is_ok()); /// }).join().expect("thread::spawn failed"); /// ``` - pub fn read<'flag, 'key, Key: Keyable + 'key>( - &'flag self, - key: Key, - ) -> PoisonResult<PoisonGuard<'flag, 'key, L::ReadGuard<'flag>, Key>> { + pub fn read(&self, key: ThreadKey) -> PoisonResult<PoisonGuard<'_, L::ReadGuard<'_>>> { unsafe { self.inner.raw_read(); self.read_guard(key) @@ -504,10 +598,7 @@ impl<L: Sharable + RawLock> Poisonable<L> { /// /// [`Poisoned`]: `TryLockPoisonableError::Poisoned` /// [`WouldBlock`]: `TryLockPoisonableError::WouldBlock` - pub fn try_read<'flag, 'key, Key: Keyable + 'key>( - &'flag self, - key: Key, - ) -> TryLockPoisonableResult<'flag, 'key, L::ReadGuard<'flag>, Key> { + pub fn try_read(&self, key: ThreadKey) -> TryLockPoisonableResult<'_, L::ReadGuard<'_>> { unsafe { if self.inner.raw_try_read() { Ok(self.read_guard(key)?) @@ -530,9 +621,7 @@ impl<L: Sharable + RawLock> Poisonable<L> { /// let mut guard = lock.read(key).unwrap(); /// let key = Poisonable::<RwLock<_>>::unlock_read(guard); /// ``` - pub fn unlock_read<'flag, 'key, Key: Keyable + 'key>( - guard: PoisonGuard<'flag, 'key, L::ReadGuard<'flag>, Key>, - ) -> Key { + pub fn unlock_read<'flag>(guard: PoisonGuard<'flag, L::ReadGuard<'flag>>) -> ThreadKey { drop(guard.guard); guard.key } diff --git a/src/rwlock.rs b/src/rwlock.rs index f78e648..b604370 100644 --- a/src/rwlock.rs +++ b/src/rwlock.rs @@ -3,8 +3,8 @@ use std::marker::PhantomData; use lock_api::RawRwLock; -use crate::key::Keyable; use crate::poisonable::PoisonFlag; +use crate::ThreadKey; mod rwlock; @@ -95,10 +95,9 @@ pub struct RwLockWriteRef<'a, T: ?Sized, R: RawRwLock>( /// /// [`read`]: `RwLock::read` /// [`try_read`]: `RwLock::try_read` -pub struct RwLockReadGuard<'a, 'key: 'a, T: ?Sized, Key: Keyable + 'key, R: RawRwLock> { +pub struct RwLockReadGuard<'a, T: ?Sized, R: RawRwLock> { rwlock: RwLockReadRef<'a, T, R>, - thread_key: Key, - _phantom: PhantomData<&'key ()>, + thread_key: ThreadKey, } /// RAII structure used to release the exclusive write access of a lock when @@ -108,16 +107,14 @@ pub struct RwLockReadGuard<'a, 'key: 'a, T: ?Sized, Key: Keyable + 'key, R: RawR /// [`RwLock`] /// /// [`try_write`]: `RwLock::try_write` -pub struct RwLockWriteGuard<'a, 'key: 'a, T: ?Sized, Key: Keyable + 'key, R: RawRwLock> { +pub struct RwLockWriteGuard<'a, T: ?Sized, R: RawRwLock> { rwlock: RwLockWriteRef<'a, T, R>, - thread_key: Key, - _phantom: PhantomData<&'key ()>, + thread_key: ThreadKey, } #[cfg(test)] mod tests { use crate::lockable::Lockable; - use crate::LockCollection; use crate::RwLock; use crate::ThreadKey; @@ -142,6 +139,16 @@ mod tests { } #[test] + fn read_lock_from_works() { + let key = ThreadKey::get().unwrap(); + let lock: crate::RwLock<_> = RwLock::from("Hello, world!"); + let reader = ReadLock::from(&lock); + + let guard = reader.lock(key); + assert_eq!(*guard, "Hello, world!"); + } + + #[test] fn write_lock_unlocked_when_initialized() { let key = ThreadKey::get().unwrap(); let lock: crate::RwLock<_> = RwLock::new("Hello, world!"); @@ -235,21 +242,6 @@ mod tests { } #[test] - fn write_ord() { - let key = ThreadKey::get().unwrap(); - let lock1: crate::RwLock<_> = RwLock::new(1); - let lock2: crate::RwLock<_> = RwLock::new(5); - let lock3: crate::RwLock<_> = RwLock::new(5); - let collection = LockCollection::try_new((&lock1, &lock2, &lock3)).unwrap(); - let guard = collection.lock(key); - - assert!(guard.0 < guard.1); - assert!(guard.1 > guard.0); - assert!(guard.1 == guard.2); - assert!(guard.0 != guard.2); - } - - #[test] fn read_ref_display_works() { let lock: crate::RwLock<_> = RwLock::new("Hello, world!"); let guard = unsafe { lock.try_read_no_key().unwrap() }; @@ -264,21 +256,6 @@ mod tests { } #[test] - fn read_ord() { - let key = ThreadKey::get().unwrap(); - let lock1: crate::RwLock<_> = RwLock::new(1); - let lock2: crate::RwLock<_> = RwLock::new(5); - let lock3: crate::RwLock<_> = RwLock::new(5); - let collection = LockCollection::try_new((&lock1, &lock2, &lock3)).unwrap(); - let guard = collection.read(key); - - assert!(guard.0 < guard.1); - assert!(guard.1 > guard.0); - assert!(guard.1 == guard.2); - assert!(guard.0 != guard.2); - } - - #[test] fn dropping_read_ref_releases_rwlock() { let lock: crate::RwLock<_> = RwLock::new("Hello, world!"); @@ -290,10 +267,10 @@ mod tests { #[test] fn dropping_write_guard_releases_rwlock() { - let mut key = ThreadKey::get().unwrap(); + let key = ThreadKey::get().unwrap(); let lock: crate::RwLock<_> = RwLock::new("Hello, world!"); - let guard = lock.write(&mut key); + let guard = lock.write(key); drop(guard); assert!(!lock.is_locked()); diff --git a/src/rwlock/read_guard.rs b/src/rwlock/read_guard.rs index bd22837..0d68c75 100644 --- a/src/rwlock/read_guard.rs +++ b/src/rwlock/read_guard.rs @@ -5,34 +5,14 @@ use std::ops::Deref; use lock_api::RawRwLock; -use crate::key::Keyable; use crate::lockable::RawLock; +use crate::ThreadKey; use super::{RwLock, RwLockReadGuard, RwLockReadRef}; // These impls make things slightly easier because now you can use // `println!("{guard}")` instead of `println!("{}", *guard)` -impl<T: PartialEq + ?Sized, R: RawRwLock> PartialEq for RwLockReadRef<'_, T, R> { - fn eq(&self, other: &Self) -> bool { - self.deref().eq(&**other) - } -} - -impl<T: Eq + ?Sized, R: RawRwLock> Eq for RwLockReadRef<'_, T, R> {} - -impl<T: PartialOrd + ?Sized, R: RawRwLock> PartialOrd for RwLockReadRef<'_, T, R> { - fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { - self.deref().partial_cmp(&**other) - } -} - -impl<T: Ord + ?Sized, R: RawRwLock> Ord for RwLockReadRef<'_, T, R> { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.deref().cmp(&**other) - } -} - #[mutants::skip] // hashing involves PRNG and is hard to test #[cfg(not(tarpaulin_include))] impl<T: Hash + ?Sized, R: RawRwLock> Hash for RwLockReadRef<'_, T, R> { @@ -89,41 +69,9 @@ impl<'a, T: ?Sized, R: RawRwLock> RwLockReadRef<'a, T, R> { } } -#[mutants::skip] // it's hard to get two read guards safely -#[cfg(not(tarpaulin_include))] -impl<T: PartialEq + ?Sized, R: RawRwLock, Key: Keyable> PartialEq - for RwLockReadGuard<'_, '_, T, Key, R> -{ - fn eq(&self, other: &Self) -> bool { - self.deref().eq(&**other) - } -} - -#[mutants::skip] // it's hard to get two read guards safely -#[cfg(not(tarpaulin_include))] -impl<T: Eq + ?Sized, R: RawRwLock, Key: Keyable> Eq for RwLockReadGuard<'_, '_, T, Key, R> {} - -#[mutants::skip] // it's hard to get two read guards safely -#[cfg(not(tarpaulin_include))] -impl<T: PartialOrd + ?Sized, R: RawRwLock, Key: Keyable> PartialOrd - for RwLockReadGuard<'_, '_, T, Key, R> -{ - fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { - self.deref().partial_cmp(&**other) - } -} - -#[mutants::skip] // it's hard to get two read guards safely -#[cfg(not(tarpaulin_include))] -impl<T: Ord + ?Sized, R: RawRwLock, Key: Keyable> Ord for RwLockReadGuard<'_, '_, T, Key, R> { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.deref().cmp(&**other) - } -} - #[mutants::skip] // hashing involves PRNG and is hard to test #[cfg(not(tarpaulin_include))] -impl<T: Hash + ?Sized, R: RawRwLock, Key: Keyable> Hash for RwLockReadGuard<'_, '_, T, Key, R> { +impl<T: Hash + ?Sized, R: RawRwLock> Hash for RwLockReadGuard<'_, T, R> { fn hash<H: std::hash::Hasher>(&self, state: &mut H) { self.deref().hash(state) } @@ -131,21 +79,19 @@ impl<T: Hash + ?Sized, R: RawRwLock, Key: Keyable> Hash for RwLockReadGuard<'_, #[mutants::skip] #[cfg(not(tarpaulin_include))] -impl<T: Debug + ?Sized, Key: Keyable, R: RawRwLock> Debug for RwLockReadGuard<'_, '_, T, Key, R> { +impl<T: Debug + ?Sized, R: RawRwLock> Debug for RwLockReadGuard<'_, T, R> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { Debug::fmt(&**self, f) } } -impl<T: Display + ?Sized, Key: Keyable, R: RawRwLock> Display - for RwLockReadGuard<'_, '_, T, Key, R> -{ +impl<T: Display + ?Sized, R: RawRwLock> Display for RwLockReadGuard<'_, T, R> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { Display::fmt(&**self, f) } } -impl<T: ?Sized, Key: Keyable, R: RawRwLock> Deref for RwLockReadGuard<'_, '_, T, Key, R> { +impl<T: ?Sized, R: RawRwLock> Deref for RwLockReadGuard<'_, T, R> { type Target = T; fn deref(&self) -> &Self::Target { @@ -153,21 +99,20 @@ impl<T: ?Sized, Key: Keyable, R: RawRwLock> Deref for RwLockReadGuard<'_, '_, T, } } -impl<T: ?Sized, Key: Keyable, R: RawRwLock> AsRef<T> for RwLockReadGuard<'_, '_, T, Key, R> { +impl<T: ?Sized, R: RawRwLock> AsRef<T> for RwLockReadGuard<'_, T, R> { fn as_ref(&self) -> &T { self } } -impl<'a, T: ?Sized, Key: Keyable, R: RawRwLock> RwLockReadGuard<'a, '_, T, Key, R> { +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: Key) -> Self { + pub(super) unsafe fn new(rwlock: &'a RwLock<T, R>, thread_key: ThreadKey) -> Self { Self { rwlock: RwLockReadRef(rwlock, PhantomData), thread_key, - _phantom: PhantomData, } } } diff --git a/src/rwlock/read_lock.rs b/src/rwlock/read_lock.rs index 5dd83a7..05b184a 100644 --- a/src/rwlock/read_lock.rs +++ b/src/rwlock/read_lock.rs @@ -2,8 +2,8 @@ use std::fmt::Debug; use lock_api::RawRwLock; -use crate::key::Keyable; use crate::lockable::{Lockable, RawLock, Sharable}; +use crate::ThreadKey; use super::{ReadLock, RwLock, RwLockReadGuard, RwLockReadRef}; @@ -13,6 +13,11 @@ unsafe impl<T: Send, R: RawRwLock + Send + Sync> Lockable for ReadLock<'_, T, R> where Self: 'g; + type DataMut<'a> + = &'a T + where + Self: 'a; + fn get_ptrs<'a>(&'a self, ptrs: &mut Vec<&'a dyn RawLock>) { ptrs.push(self.as_ref()); } @@ -20,6 +25,10 @@ unsafe impl<T: Send, R: RawRwLock + Send + Sync> Lockable for ReadLock<'_, T, R> unsafe fn guard(&self) -> Self::Guard<'_> { RwLockReadRef::new(self.as_ref()) } + + unsafe fn data_mut(&self) -> Self::DataMut<'_> { + self.0.data_ref() + } } unsafe impl<T: Send, R: RawRwLock + Send + Sync> Sharable for ReadLock<'_, T, R> { @@ -28,9 +37,18 @@ unsafe impl<T: Send, R: RawRwLock + Send + Sync> Sharable for ReadLock<'_, T, R> where Self: 'g; + type DataRef<'a> + = &'a T + where + Self: 'a; + unsafe fn read_guard(&self) -> Self::Guard<'_> { RwLockReadRef::new(self.as_ref()) } + + unsafe fn data_ref(&self) -> Self::DataRef<'_> { + self.0.data_ref() + } } #[mutants::skip] @@ -117,10 +135,8 @@ impl<T: ?Sized, R: RawRwLock> ReadLock<'_, T, R> { /// ``` /// /// [`ThreadKey`]: `crate::ThreadKey` - pub fn lock<'s, 'key: 's, Key: Keyable + 'key>( - &'s self, - key: Key, - ) -> RwLockReadGuard<'s, 'key, T, Key, R> { + #[must_use] + pub fn lock(&self, key: ThreadKey) -> RwLockReadGuard<'_, T, R> { self.0.read(key) } @@ -155,10 +171,7 @@ impl<T: ?Sized, R: RawRwLock> ReadLock<'_, T, R> { /// Err(_) => unreachable!(), /// }; /// ``` - pub fn try_lock<'s, 'key: 's, Key: Keyable + 'key>( - &'s self, - key: Key, - ) -> Result<RwLockReadGuard<'s, 'key, T, Key, R>, Key> { + pub fn try_lock(&self, key: ThreadKey) -> Result<RwLockReadGuard<'_, T, R>, ThreadKey> { self.0.try_read(key) } @@ -189,7 +202,8 @@ impl<T: ?Sized, R: RawRwLock> ReadLock<'_, T, R> { /// assert_eq!(*guard, 0); /// let key = ReadLock::unlock(guard); /// ``` - pub fn unlock<'key, Key: Keyable + 'key>(guard: RwLockReadGuard<'_, 'key, T, Key, R>) -> Key { + #[must_use] + pub fn unlock(guard: RwLockReadGuard<'_, T, R>) -> ThreadKey { RwLock::unlock_read(guard) } } diff --git a/src/rwlock/rwlock.rs b/src/rwlock/rwlock.rs index 038e6c7..905ecf8 100644 --- a/src/rwlock/rwlock.rs +++ b/src/rwlock/rwlock.rs @@ -6,10 +6,10 @@ use std::panic::AssertUnwindSafe; use lock_api::RawRwLock; use crate::handle_unwind::handle_unwind; -use crate::key::Keyable; use crate::lockable::{ Lockable, LockableGetMut, LockableIntoInner, OwnedLockable, RawLock, Sharable, }; +use crate::{Keyable, ThreadKey}; use super::{PoisonFlag, RwLock, RwLockReadGuard, RwLockReadRef, RwLockWriteGuard, RwLockWriteRef}; @@ -79,6 +79,11 @@ unsafe impl<T: Send, R: RawRwLock + Send + Sync> Lockable for RwLock<T, R> { where Self: 'g; + type DataMut<'a> + = &'a mut T + where + Self: 'a; + fn get_ptrs<'a>(&'a self, ptrs: &mut Vec<&'a dyn RawLock>) { ptrs.push(self); } @@ -86,6 +91,10 @@ unsafe impl<T: Send, R: RawRwLock + Send + Sync> Lockable for RwLock<T, R> { unsafe fn guard(&self) -> Self::Guard<'_> { RwLockWriteRef::new(self) } + + unsafe fn data_mut(&self) -> Self::DataMut<'_> { + self.data.get().as_mut().unwrap_unchecked() + } } unsafe impl<T: Send, R: RawRwLock + Send + Sync> Sharable for RwLock<T, R> { @@ -94,9 +103,18 @@ unsafe impl<T: Send, R: RawRwLock + Send + Sync> Sharable for RwLock<T, R> { where Self: 'g; + type DataRef<'a> + = &'a T + where + Self: 'a; + unsafe fn read_guard(&self) -> Self::ReadGuard<'_> { RwLockReadRef::new(self) } + + unsafe fn data_ref(&self) -> Self::DataRef<'_> { + self.data.get().as_ref().unwrap_unchecked() + } } unsafe impl<T: Send, R: RawRwLock + Send + Sync> OwnedLockable for RwLock<T, R> {} @@ -230,6 +248,86 @@ 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 + } + } + + pub fn scoped_try_read<Key: Keyable, Ret>( + &self, + key: Key, + f: impl Fn(&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) + } + } + + 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_try_write<Key: Keyable, Ret>( + &self, + key: Key, + f: impl Fn(&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) + } + } + /// Locks this `RwLock` with shared read access, blocking the current /// thread until it can be acquired. /// @@ -264,10 +362,7 @@ impl<T: ?Sized, R: RawRwLock> RwLock<T, R> { /// ``` /// /// [`ThreadKey`]: `crate::ThreadKey` - pub fn read<'s, 'key: 's, Key: Keyable>( - &'s self, - key: Key, - ) -> RwLockReadGuard<'s, 'key, T, Key, R> { + pub fn read(&self, key: ThreadKey) -> RwLockReadGuard<'_, T, R> { unsafe { self.raw_read(); @@ -305,10 +400,7 @@ impl<T: ?Sized, R: RawRwLock> RwLock<T, R> { /// Err(_) => unreachable!(), /// }; /// ``` - pub fn try_read<'s, 'key: 's, Key: Keyable>( - &'s self, - key: Key, - ) -> Result<RwLockReadGuard<'s, 'key, T, Key, R>, Key> { + pub fn try_read(&self, key: ThreadKey) -> Result<RwLockReadGuard<'_, T, R>, ThreadKey> { unsafe { if self.raw_try_read() { // safety: the lock is locked first @@ -369,10 +461,7 @@ impl<T: ?Sized, R: RawRwLock> RwLock<T, R> { /// ``` /// /// [`ThreadKey`]: `crate::ThreadKey` - pub fn write<'s, 'key: 's, Key: Keyable>( - &'s self, - key: Key, - ) -> RwLockWriteGuard<'s, 'key, T, Key, R> { + pub fn write(&self, key: ThreadKey) -> RwLockWriteGuard<'_, T, R> { unsafe { self.raw_lock(); @@ -407,10 +496,7 @@ impl<T: ?Sized, R: RawRwLock> RwLock<T, R> { /// let n = lock.read(key); /// assert_eq!(*n, 1); /// ``` - pub fn try_write<'s, 'key: 's, Key: Keyable>( - &'s self, - key: Key, - ) -> Result<RwLockWriteGuard<'s, 'key, T, Key, R>, Key> { + pub fn try_write(&self, key: ThreadKey) -> Result<RwLockWriteGuard<'_, T, R>, ThreadKey> { unsafe { if self.raw_try_lock() { // safety: the lock is locked first @@ -445,9 +531,8 @@ impl<T: ?Sized, R: RawRwLock> RwLock<T, R> { /// assert_eq!(*guard, 0); /// let key = RwLock::unlock_read(guard); /// ``` - pub fn unlock_read<'key, Key: Keyable + 'key>( - guard: RwLockReadGuard<'_, 'key, T, Key, R>, - ) -> Key { + #[must_use] + pub fn unlock_read(guard: RwLockReadGuard<'_, T, R>) -> ThreadKey { unsafe { guard.rwlock.0.raw_unlock_read(); } @@ -473,9 +558,8 @@ impl<T: ?Sized, R: RawRwLock> RwLock<T, R> { /// *guard += 20; /// let key = RwLock::unlock_write(guard); /// ``` - pub fn unlock_write<'key, Key: Keyable + 'key>( - guard: RwLockWriteGuard<'_, 'key, T, Key, R>, - ) -> Key { + #[must_use] + pub fn unlock_write(guard: RwLockWriteGuard<'_, T, R>) -> ThreadKey { unsafe { guard.rwlock.0.raw_unlock(); } diff --git a/src/rwlock/write_guard.rs b/src/rwlock/write_guard.rs index c971260..3fabf8e 100644 --- a/src/rwlock/write_guard.rs +++ b/src/rwlock/write_guard.rs @@ -5,34 +5,14 @@ use std::ops::{Deref, DerefMut}; use lock_api::RawRwLock; -use crate::key::Keyable; use crate::lockable::RawLock; +use crate::ThreadKey; use super::{RwLock, RwLockWriteGuard, RwLockWriteRef}; // These impls make things slightly easier because now you can use // `println!("{guard}")` instead of `println!("{}", *guard)` -impl<T: PartialEq + ?Sized, R: RawRwLock> PartialEq for RwLockWriteRef<'_, T, R> { - fn eq(&self, other: &Self) -> bool { - self.deref().eq(&**other) - } -} - -impl<T: Eq + ?Sized, R: RawRwLock> Eq for RwLockWriteRef<'_, T, R> {} - -impl<T: PartialOrd + ?Sized, R: RawRwLock> PartialOrd for RwLockWriteRef<'_, T, R> { - fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { - self.deref().partial_cmp(&**other) - } -} - -impl<T: Ord + ?Sized, R: RawRwLock> Ord for RwLockWriteRef<'_, T, R> { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.deref().cmp(&**other) - } -} - #[mutants::skip] // hashing involves PRNG and is difficult to test #[cfg(not(tarpaulin_include))] impl<T: Hash + ?Sized, R: RawRwLock> Hash for RwLockWriteRef<'_, T, R> { @@ -104,41 +84,9 @@ impl<'a, T: ?Sized + 'a, R: RawRwLock> RwLockWriteRef<'a, T, R> { } } -#[mutants::skip] // it's hard to get two read guards safely -#[cfg(not(tarpaulin_include))] -impl<T: PartialEq + ?Sized, R: RawRwLock, Key: Keyable> PartialEq - for RwLockWriteGuard<'_, '_, T, Key, R> -{ - fn eq(&self, other: &Self) -> bool { - self.deref().eq(&**other) - } -} - -#[mutants::skip] // it's hard to get two read guards safely -#[cfg(not(tarpaulin_include))] -impl<T: Eq + ?Sized, R: RawRwLock, Key: Keyable> Eq for RwLockWriteGuard<'_, '_, T, Key, R> {} - -#[mutants::skip] // it's hard to get two read guards safely -#[cfg(not(tarpaulin_include))] -impl<T: PartialOrd + ?Sized, R: RawRwLock, Key: Keyable> PartialOrd - for RwLockWriteGuard<'_, '_, T, Key, R> -{ - fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { - self.deref().partial_cmp(&**other) - } -} - -#[mutants::skip] // it's hard to get two read guards safely -#[cfg(not(tarpaulin_include))] -impl<T: Ord + ?Sized, R: RawRwLock, Key: Keyable> Ord for RwLockWriteGuard<'_, '_, T, Key, R> { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.deref().cmp(&**other) - } -} - #[mutants::skip] // hashing involves PRNG and is difficult to test #[cfg(not(tarpaulin_include))] -impl<T: Hash + ?Sized, R: RawRwLock, Key: Keyable> Hash for RwLockWriteGuard<'_, '_, T, Key, R> { +impl<T: Hash + ?Sized, R: RawRwLock> Hash for RwLockWriteGuard<'_, T, R> { fn hash<H: std::hash::Hasher>(&self, state: &mut H) { self.deref().hash(state) } @@ -146,21 +94,19 @@ impl<T: Hash + ?Sized, R: RawRwLock, Key: Keyable> Hash for RwLockWriteGuard<'_, #[mutants::skip] #[cfg(not(tarpaulin_include))] -impl<T: Debug + ?Sized, Key: Keyable, R: RawRwLock> Debug for RwLockWriteGuard<'_, '_, T, Key, R> { +impl<T: Debug + ?Sized, R: RawRwLock> Debug for RwLockWriteGuard<'_, T, R> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { Debug::fmt(&**self, f) } } -impl<T: Display + ?Sized, Key: Keyable, R: RawRwLock> Display - for RwLockWriteGuard<'_, '_, T, Key, R> -{ +impl<T: Display + ?Sized, R: RawRwLock> Display for RwLockWriteGuard<'_, T, R> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { Display::fmt(&**self, f) } } -impl<T: ?Sized, Key: Keyable, R: RawRwLock> Deref for RwLockWriteGuard<'_, '_, T, Key, R> { +impl<T: ?Sized, R: RawRwLock> Deref for RwLockWriteGuard<'_, T, R> { type Target = T; fn deref(&self) -> &Self::Target { @@ -168,33 +114,32 @@ impl<T: ?Sized, Key: Keyable, R: RawRwLock> Deref for RwLockWriteGuard<'_, '_, T } } -impl<T: ?Sized, Key: Keyable, R: RawRwLock> DerefMut for RwLockWriteGuard<'_, '_, T, Key, R> { +impl<T: ?Sized, R: RawRwLock> DerefMut for RwLockWriteGuard<'_, T, R> { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.rwlock } } -impl<T: ?Sized, Key: Keyable, R: RawRwLock> AsRef<T> for RwLockWriteGuard<'_, '_, T, Key, R> { +impl<T: ?Sized, R: RawRwLock> AsRef<T> for RwLockWriteGuard<'_, T, R> { fn as_ref(&self) -> &T { self } } -impl<T: ?Sized, Key: Keyable, R: RawRwLock> AsMut<T> for RwLockWriteGuard<'_, '_, T, Key, R> { +impl<T: ?Sized, R: RawRwLock> AsMut<T> for RwLockWriteGuard<'_, T, R> { fn as_mut(&mut self) -> &mut T { self } } -impl<'a, T: ?Sized + 'a, Key: Keyable, R: RawRwLock> RwLockWriteGuard<'a, '_, T, Key, R> { +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: Key) -> Self { + pub(super) unsafe fn new(rwlock: &'a RwLock<T, R>, thread_key: ThreadKey) -> Self { Self { rwlock: RwLockWriteRef(rwlock, PhantomData), thread_key, - _phantom: PhantomData, } } } diff --git a/src/rwlock/write_lock.rs b/src/rwlock/write_lock.rs index cc96953..8a44a2d 100644 --- a/src/rwlock/write_lock.rs +++ b/src/rwlock/write_lock.rs @@ -2,8 +2,8 @@ use std::fmt::Debug; use lock_api::RawRwLock; -use crate::key::Keyable; use crate::lockable::{Lockable, RawLock}; +use crate::ThreadKey; use super::{RwLock, RwLockWriteGuard, RwLockWriteRef, WriteLock}; @@ -13,6 +13,11 @@ unsafe impl<T: Send, R: RawRwLock + Send + Sync> Lockable for WriteLock<'_, T, R where Self: 'g; + type DataMut<'a> + = &'a mut T + where + Self: 'a; + fn get_ptrs<'a>(&'a self, ptrs: &mut Vec<&'a dyn RawLock>) { ptrs.push(self.as_ref()); } @@ -20,6 +25,10 @@ unsafe impl<T: Send, R: RawRwLock + Send + Sync> Lockable for WriteLock<'_, T, R unsafe fn guard(&self) -> Self::Guard<'_> { RwLockWriteRef::new(self.as_ref()) } + + unsafe fn data_mut(&self) -> Self::DataMut<'_> { + self.0.data_mut() + } } // Technically, the exclusive locks can also be shared, but there's currently @@ -108,10 +117,8 @@ impl<T: ?Sized, R: RawRwLock> WriteLock<'_, T, R> { /// ``` /// /// [`ThreadKey`]: `crate::ThreadKey` - pub fn lock<'s, 'key: 's, Key: Keyable + 'key>( - &'s self, - key: Key, - ) -> RwLockWriteGuard<'s, 'key, T, Key, R> { + #[must_use] + pub fn lock(&self, key: ThreadKey) -> RwLockWriteGuard<'_, T, R> { self.0.write(key) } @@ -145,10 +152,7 @@ impl<T: ?Sized, R: RawRwLock> WriteLock<'_, T, R> { /// Err(_) => unreachable!(), /// }; /// ``` - pub fn try_lock<'s, 'key: 's, Key: Keyable + 'key>( - &'s self, - key: Key, - ) -> Result<RwLockWriteGuard<'s, 'key, T, Key, R>, Key> { + pub fn try_lock(&self, key: ThreadKey) -> Result<RwLockWriteGuard<'_, T, R>, ThreadKey> { self.0.try_write(key) } @@ -176,7 +180,8 @@ impl<T: ?Sized, R: RawRwLock> WriteLock<'_, T, R> { /// *guard += 20; /// let key = WriteLock::unlock(guard); /// ``` - pub fn unlock<'key, Key: Keyable + 'key>(guard: RwLockWriteGuard<'_, 'key, T, Key, R>) -> Key { + #[must_use] + pub fn unlock(guard: RwLockWriteGuard<'_, T, R>) -> ThreadKey { RwLock::unlock_write(guard) } } diff --git a/tarpaulin-report.html b/tarpaulin-report.html new file mode 100644 index 0000000..716e14a --- /dev/null +++ b/tarpaulin-report.html @@ -0,0 +1,671 @@ +<!doctype html> +<html> +<head> + <meta charset="utf-8"> + <style>html, body { + margin: 0; + padding: 0; +} + +.app { + margin: 10px; + padding: 0; +} + +.files-list { + margin: 10px 0 0; + width: 100%; + border-collapse: collapse; +} +.files-list__head { + border: 1px solid #999; +} +.files-list__head > tr > th { + padding: 10px; + border: 1px solid #999; + text-align: left; + font-weight: normal; + background: #ddd; +} +.files-list__body { +} +.files-list__file { + cursor: pointer; +} +.files-list__file:hover { + background: #ccf; +} +.files-list__file > td { + padding: 10px; + border: 1px solid #999; +} +.files-list__file > td:first-child::before { + content: '\01F4C4'; + margin-right: 1em; +} +.files-list__file_low { + background: #fcc; +} +.files-list__file_medium { + background: #ffc; +} +.files-list__file_high { + background: #cfc; +} +.files-list__file_folder > td:first-child::before { + content: '\01F4C1'; + margin-right: 1em; +} + +.file-header { + border: 1px solid #999; + display: flex; + justify-content: space-between; + align-items: center; + position: sticky; + top: 0; + background: white; +} + +.file-header__back { + margin: 10px; + cursor: pointer; + flex-shrink: 0; + flex-grow: 0; + text-decoration: underline; + color: #338; +} + +.file-header__name { + margin: 10px; + flex-shrink: 2; + flex-grow: 2; +} + +.file-header__stat { + margin: 10px; + flex-shrink: 0; + flex-grow: 0; +} + +.file-content { + margin: 10px 0 0; + border: 1px solid #999; + padding: 10px; + counter-reset: line; + display: flex; + flex-direction: column; +} + +.code-line::before { + content: counter(line); + margin-right: 10px; +} +.code-line { + margin: 0; + padding: 0.3em; + height: 1em; + counter-increment: line; +} +.code-line_covered { + background: #cfc; +} +.code-line_uncovered { + background: #fcc; +} +</style> +</head> +<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}]}; + </script> + <script crossorigin>/** @license React v16.13.1 + * react.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +'use strict';(function(d,r){"object"===typeof exports&&"undefined"!==typeof module?r(exports):"function"===typeof define&&define.amd?define(["exports"],r):(d=d||self,r(d.React={}))})(this,function(d){function r(a){for(var b="https://reactjs.org/docs/error-decoder.html?invariant="+a,c=1;c<arguments.length;c++)b+="&args[]="+encodeURIComponent(arguments[c]);return"Minified React error #"+a+"; visit "+b+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings."} +function w(a,b,c){this.props=a;this.context=b;this.refs=ba;this.updater=c||ca}function da(){}function L(a,b,c){this.props=a;this.context=b;this.refs=ba;this.updater=c||ca}function ea(a,b,c){var g,e={},fa=null,d=null;if(null!=b)for(g in void 0!==b.ref&&(d=b.ref),void 0!==b.key&&(fa=""+b.key),b)ha.call(b,g)&&!ia.hasOwnProperty(g)&&(e[g]=b[g]);var h=arguments.length-2;if(1===h)e.children=c;else if(1<h){for(var k=Array(h),f=0;f<h;f++)k[f]=arguments[f+2];e.children=k}if(a&&a.defaultProps)for(g in h=a.defaultProps, +h)void 0===e[g]&&(e[g]=h[g]);return{$$typeof:x,type:a,key:fa,ref:d,props:e,_owner:M.current}}function va(a,b){return{$$typeof:x,type:a.type,key:b,ref:a.ref,props:a.props,_owner:a._owner}}function N(a){return"object"===typeof a&&null!==a&&a.$$typeof===x}function wa(a){var b={"=":"=0",":":"=2"};return"$"+(""+a).replace(/[=:]/g,function(a){return b[a]})}function ja(a,b,c,g){if(C.length){var e=C.pop();e.result=a;e.keyPrefix=b;e.func=c;e.context=g;e.count=0;return e}return{result:a,keyPrefix:b,func:c, +context:g,count:0}}function ka(a){a.result=null;a.keyPrefix=null;a.func=null;a.context=null;a.count=0;10>C.length&&C.push(a)}function O(a,b,c,g){var e=typeof a;if("undefined"===e||"boolean"===e)a=null;var d=!1;if(null===a)d=!0;else switch(e){case "string":case "number":d=!0;break;case "object":switch(a.$$typeof){case x:case xa:d=!0}}if(d)return c(g,a,""===b?"."+P(a,0):b),1;d=0;b=""===b?".":b+":";if(Array.isArray(a))for(var f=0;f<a.length;f++){e=a[f];var h=b+P(e,f);d+=O(e,h,c,g)}else if(null===a|| +"object"!==typeof a?h=null:(h=la&&a[la]||a["@@iterator"],h="function"===typeof h?h:null),"function"===typeof h)for(a=h.call(a),f=0;!(e=a.next()).done;)e=e.value,h=b+P(e,f++),d+=O(e,h,c,g);else if("object"===e)throw c=""+a,Error(r(31,"[object Object]"===c?"object with keys {"+Object.keys(a).join(", ")+"}":c,""));return d}function Q(a,b,c){return null==a?0:O(a,"",b,c)}function P(a,b){return"object"===typeof a&&null!==a&&null!=a.key?wa(a.key):b.toString(36)}function ya(a,b,c){a.func.call(a.context,b, +a.count++)}function za(a,b,c){var g=a.result,e=a.keyPrefix;a=a.func.call(a.context,b,a.count++);Array.isArray(a)?R(a,g,c,function(a){return a}):null!=a&&(N(a)&&(a=va(a,e+(!a.key||b&&b.key===a.key?"":(""+a.key).replace(ma,"$&/")+"/")+c)),g.push(a))}function R(a,b,c,g,e){var d="";null!=c&&(d=(""+c).replace(ma,"$&/")+"/");b=ja(b,d,g,e);Q(a,za,b);ka(b)}function t(){var a=na.current;if(null===a)throw Error(r(321));return a}function S(a,b){var c=a.length;a.push(b);a:for(;;){var g=c-1>>>1,e=a[g];if(void 0!== +e&&0<D(e,b))a[g]=b,a[c]=e,c=g;else break a}}function n(a){a=a[0];return void 0===a?null:a}function E(a){var b=a[0];if(void 0!==b){var c=a.pop();if(c!==b){a[0]=c;a:for(var g=0,e=a.length;g<e;){var d=2*(g+1)-1,f=a[d],h=d+1,k=a[h];if(void 0!==f&&0>D(f,c))void 0!==k&&0>D(k,f)?(a[g]=k,a[h]=c,g=h):(a[g]=f,a[d]=c,g=d);else if(void 0!==k&&0>D(k,c))a[g]=k,a[h]=c,g=h;else break a}}return b}return null}function D(a,b){var c=a.sortIndex-b.sortIndex;return 0!==c?c:a.id-b.id}function F(a){for(var b=n(u);null!== +b;){if(null===b.callback)E(u);else if(b.startTime<=a)E(u),b.sortIndex=b.expirationTime,S(p,b);else break;b=n(u)}}function T(a){y=!1;F(a);if(!v)if(null!==n(p))v=!0,z(U);else{var b=n(u);null!==b&&G(T,b.startTime-a)}}function U(a,b){v=!1;y&&(y=!1,V());H=!0;var c=m;try{F(b);for(l=n(p);null!==l&&(!(l.expirationTime>b)||a&&!W());){var g=l.callback;if(null!==g){l.callback=null;m=l.priorityLevel;var e=g(l.expirationTime<=b);b=q();"function"===typeof e?l.callback=e:l===n(p)&&E(p);F(b)}else E(p);l=n(p)}if(null!== +l)var d=!0;else{var f=n(u);null!==f&&G(T,f.startTime-b);d=!1}return d}finally{l=null,m=c,H=!1}}function oa(a){switch(a){case 1:return-1;case 2:return 250;case 5:return 1073741823;case 4:return 1E4;default:return 5E3}}var f="function"===typeof Symbol&&Symbol.for,x=f?Symbol.for("react.element"):60103,xa=f?Symbol.for("react.portal"):60106,Aa=f?Symbol.for("react.fragment"):60107,Ba=f?Symbol.for("react.strict_mode"):60108,Ca=f?Symbol.for("react.profiler"):60114,Da=f?Symbol.for("react.provider"):60109, +Ea=f?Symbol.for("react.context"):60110,Fa=f?Symbol.for("react.forward_ref"):60112,Ga=f?Symbol.for("react.suspense"):60113,Ha=f?Symbol.for("react.memo"):60115,Ia=f?Symbol.for("react.lazy"):60116,la="function"===typeof Symbol&&Symbol.iterator,pa=Object.getOwnPropertySymbols,Ja=Object.prototype.hasOwnProperty,Ka=Object.prototype.propertyIsEnumerable,I=function(){try{if(!Object.assign)return!1;var a=new String("abc");a[5]="de";if("5"===Object.getOwnPropertyNames(a)[0])return!1;var b={};for(a=0;10>a;a++)b["_"+ +String.fromCharCode(a)]=a;if("0123456789"!==Object.getOwnPropertyNames(b).map(function(a){return b[a]}).join(""))return!1;var c={};"abcdefghijklmnopqrst".split("").forEach(function(a){c[a]=a});return"abcdefghijklmnopqrst"!==Object.keys(Object.assign({},c)).join("")?!1:!0}catch(g){return!1}}()?Object.assign:function(a,b){if(null===a||void 0===a)throw new TypeError("Object.assign cannot be called with null or undefined");var c=Object(a);for(var g,e=1;e<arguments.length;e++){var d=Object(arguments[e]); +for(var f in d)Ja.call(d,f)&&(c[f]=d[f]);if(pa){g=pa(d);for(var h=0;h<g.length;h++)Ka.call(d,g[h])&&(c[g[h]]=d[g[h]])}}return c},ca={isMounted:function(a){return!1},enqueueForceUpdate:function(a,b,c){},enqueueReplaceState:function(a,b,c,d){},enqueueSetState:function(a,b,c,d){}},ba={};w.prototype.isReactComponent={};w.prototype.setState=function(a,b){if("object"!==typeof a&&"function"!==typeof a&&null!=a)throw Error(r(85));this.updater.enqueueSetState(this,a,b,"setState")};w.prototype.forceUpdate= +function(a){this.updater.enqueueForceUpdate(this,a,"forceUpdate")};da.prototype=w.prototype;f=L.prototype=new da;f.constructor=L;I(f,w.prototype);f.isPureReactComponent=!0;var M={current:null},ha=Object.prototype.hasOwnProperty,ia={key:!0,ref:!0,__self:!0,__source:!0},ma=/\/+/g,C=[],na={current:null},X;if("undefined"===typeof window||"function"!==typeof MessageChannel){var A=null,qa=null,ra=function(){if(null!==A)try{var a=q();A(!0,a);A=null}catch(b){throw setTimeout(ra,0),b;}},La=Date.now();var q= +function(){return Date.now()-La};var z=function(a){null!==A?setTimeout(z,0,a):(A=a,setTimeout(ra,0))};var G=function(a,b){qa=setTimeout(a,b)};var V=function(){clearTimeout(qa)};var W=function(){return!1};f=X=function(){}}else{var Y=window.performance,sa=window.Date,Ma=window.setTimeout,Na=window.clearTimeout;"undefined"!==typeof console&&(f=window.cancelAnimationFrame,"function"!==typeof window.requestAnimationFrame&&console.error("This browser doesn't support requestAnimationFrame. Make sure that you load a polyfill in older browsers. https://fb.me/react-polyfills"), +"function"!==typeof f&&console.error("This browser doesn't support cancelAnimationFrame. Make sure that you load a polyfill in older browsers. https://fb.me/react-polyfills"));if("object"===typeof Y&&"function"===typeof Y.now)q=function(){return Y.now()};else{var Oa=sa.now();q=function(){return sa.now()-Oa}}var J=!1,K=null,Z=-1,ta=5,ua=0;W=function(){return q()>=ua};f=function(){};X=function(a){0>a||125<a?console.error("forceFrameRate takes a positive int between 0 and 125, forcing framerates higher than 125 fps is not unsupported"): +ta=0<a?Math.floor(1E3/a):5};var B=new MessageChannel,aa=B.port2;B.port1.onmessage=function(){if(null!==K){var a=q();ua=a+ta;try{K(!0,a)?aa.postMessage(null):(J=!1,K=null)}catch(b){throw aa.postMessage(null),b;}}else J=!1};z=function(a){K=a;J||(J=!0,aa.postMessage(null))};G=function(a,b){Z=Ma(function(){a(q())},b)};V=function(){Na(Z);Z=-1}}var p=[],u=[],Pa=1,l=null,m=3,H=!1,v=!1,y=!1,Qa=0;B={ReactCurrentDispatcher:na,ReactCurrentOwner:M,IsSomeRendererActing:{current:!1},assign:I};I(B,{Scheduler:{__proto__:null, +unstable_ImmediatePriority:1,unstable_UserBlockingPriority:2,unstable_NormalPriority:3,unstable_IdlePriority:5,unstable_LowPriority:4,unstable_runWithPriority:function(a,b){switch(a){case 1:case 2:case 3:case 4:case 5:break;default:a=3}var c=m;m=a;try{return b()}finally{m=c}},unstable_next:function(a){switch(m){case 1:case 2:case 3:var b=3;break;default:b=m}var c=m;m=b;try{return a()}finally{m=c}},unstable_scheduleCallback:function(a,b,c){var d=q();if("object"===typeof c&&null!==c){var e=c.delay; +e="number"===typeof e&&0<e?d+e:d;c="number"===typeof c.timeout?c.timeout:oa(a)}else c=oa(a),e=d;c=e+c;a={id:Pa++,callback:b,priorityLevel:a,startTime:e,expirationTime:c,sortIndex:-1};e>d?(a.sortIndex=e,S(u,a),null===n(p)&&a===n(u)&&(y?V():y=!0,G(T,e-d))):(a.sortIndex=c,S(p,a),v||H||(v=!0,z(U)));return a},unstable_cancelCallback:function(a){a.callback=null},unstable_wrapCallback:function(a){var b=m;return function(){var c=m;m=b;try{return a.apply(this,arguments)}finally{m=c}}},unstable_getCurrentPriorityLevel:function(){return m}, +unstable_shouldYield:function(){var a=q();F(a);var b=n(p);return b!==l&&null!==l&&null!==b&&null!==b.callback&&b.startTime<=a&&b.expirationTime<l.expirationTime||W()},unstable_requestPaint:f,unstable_continueExecution:function(){v||H||(v=!0,z(U))},unstable_pauseExecution:function(){},unstable_getFirstCallbackNode:function(){return n(p)},get unstable_now(){return q},get unstable_forceFrameRate(){return X},unstable_Profiling:null},SchedulerTracing:{__proto__:null,__interactionsRef:null,__subscriberRef:null, +unstable_clear:function(a){return a()},unstable_getCurrent:function(){return null},unstable_getThreadID:function(){return++Qa},unstable_trace:function(a,b,c){return c()},unstable_wrap:function(a){return a},unstable_subscribe:function(a){},unstable_unsubscribe:function(a){}}});d.Children={map:function(a,b,c){if(null==a)return a;var d=[];R(a,d,null,b,c);return d},forEach:function(a,b,c){if(null==a)return a;b=ja(null,null,b,c);Q(a,ya,b);ka(b)},count:function(a){return Q(a,function(){return null},null)}, +toArray:function(a){var b=[];R(a,b,null,function(a){return a});return b},only:function(a){if(!N(a))throw Error(r(143));return a}};d.Component=w;d.Fragment=Aa;d.Profiler=Ca;d.PureComponent=L;d.StrictMode=Ba;d.Suspense=Ga;d.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED=B;d.cloneElement=function(a,b,c){if(null===a||void 0===a)throw Error(r(267,a));var d=I({},a.props),e=a.key,f=a.ref,m=a._owner;if(null!=b){void 0!==b.ref&&(f=b.ref,m=M.current);void 0!==b.key&&(e=""+b.key);if(a.type&&a.type.defaultProps)var h= +a.type.defaultProps;for(k in b)ha.call(b,k)&&!ia.hasOwnProperty(k)&&(d[k]=void 0===b[k]&&void 0!==h?h[k]:b[k])}var k=arguments.length-2;if(1===k)d.children=c;else if(1<k){h=Array(k);for(var l=0;l<k;l++)h[l]=arguments[l+2];d.children=h}return{$$typeof:x,type:a.type,key:e,ref:f,props:d,_owner:m}};d.createContext=function(a,b){void 0===b&&(b=null);a={$$typeof:Ea,_calculateChangedBits:b,_currentValue:a,_currentValue2:a,_threadCount:0,Provider:null,Consumer:null};a.Provider={$$typeof:Da,_context:a};return a.Consumer= +a};d.createElement=ea;d.createFactory=function(a){var b=ea.bind(null,a);b.type=a;return b};d.createRef=function(){return{current:null}};d.forwardRef=function(a){return{$$typeof:Fa,render:a}};d.isValidElement=N;d.lazy=function(a){return{$$typeof:Ia,_ctor:a,_status:-1,_result:null}};d.memo=function(a,b){return{$$typeof:Ha,type:a,compare:void 0===b?null:b}};d.useCallback=function(a,b){return t().useCallback(a,b)};d.useContext=function(a,b){return t().useContext(a,b)};d.useDebugValue=function(a,b){}; +d.useEffect=function(a,b){return t().useEffect(a,b)};d.useImperativeHandle=function(a,b,c){return t().useImperativeHandle(a,b,c)};d.useLayoutEffect=function(a,b){return t().useLayoutEffect(a,b)};d.useMemo=function(a,b){return t().useMemo(a,b)};d.useReducer=function(a,b,c){return t().useReducer(a,b,c)};d.useRef=function(a){return t().useRef(a)};d.useState=function(a){return t().useState(a)};d.version="16.13.1"}); +</script> + <script crossorigin>/** @license React v16.13.1 + * react-dom.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +/* + Modernizr 3.0.0pre (Custom Build) | MIT +*/ +'use strict';(function(I,ea){"object"===typeof exports&&"undefined"!==typeof module?ea(exports,require("react")):"function"===typeof define&&define.amd?define(["exports","react"],ea):(I=I||self,ea(I.ReactDOM={},I.React))})(this,function(I,ea){function k(a){for(var b="https://reactjs.org/docs/error-decoder.html?invariant="+a,c=1;c<arguments.length;c++)b+="&args[]="+encodeURIComponent(arguments[c]);return"Minified React error #"+a+"; visit "+b+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings."} +function ji(a,b,c,d,e,f,g,h,m){yb=!1;gc=null;ki.apply(li,arguments)}function mi(a,b,c,d,e,f,g,h,m){ji.apply(this,arguments);if(yb){if(yb){var n=gc;yb=!1;gc=null}else throw Error(k(198));hc||(hc=!0,pd=n)}}function lf(a,b,c){var d=a.type||"unknown-event";a.currentTarget=mf(c);mi(d,b,void 0,a);a.currentTarget=null}function nf(){if(ic)for(var a in cb){var b=cb[a],c=ic.indexOf(a);if(!(-1<c))throw Error(k(96,a));if(!jc[c]){if(!b.extractEvents)throw Error(k(97,a));jc[c]=b;c=b.eventTypes;for(var d in c){var e= +void 0;var f=c[d],g=b,h=d;if(qd.hasOwnProperty(h))throw Error(k(99,h));qd[h]=f;var m=f.phasedRegistrationNames;if(m){for(e in m)m.hasOwnProperty(e)&&of(m[e],g,h);e=!0}else f.registrationName?(of(f.registrationName,g,h),e=!0):e=!1;if(!e)throw Error(k(98,d,a));}}}}function of(a,b,c){if(db[a])throw Error(k(100,a));db[a]=b;rd[a]=b.eventTypes[c].dependencies}function pf(a){var b=!1,c;for(c in a)if(a.hasOwnProperty(c)){var d=a[c];if(!cb.hasOwnProperty(c)||cb[c]!==d){if(cb[c])throw Error(k(102,c));cb[c]= +d;b=!0}}b&&nf()}function qf(a){if(a=rf(a)){if("function"!==typeof sd)throw Error(k(280));var b=a.stateNode;b&&(b=td(b),sd(a.stateNode,a.type,b))}}function sf(a){eb?fb?fb.push(a):fb=[a]:eb=a}function tf(){if(eb){var a=eb,b=fb;fb=eb=null;qf(a);if(b)for(a=0;a<b.length;a++)qf(b[a])}}function ud(){if(null!==eb||null!==fb)vd(),tf()}function uf(a,b,c){if(wd)return a(b,c);wd=!0;try{return vf(a,b,c)}finally{wd=!1,ud()}}function ni(a){if(wf.call(xf,a))return!0;if(wf.call(yf,a))return!1;if(oi.test(a))return xf[a]= +!0;yf[a]=!0;return!1}function pi(a,b,c,d){if(null!==c&&0===c.type)return!1;switch(typeof b){case "function":case "symbol":return!0;case "boolean":if(d)return!1;if(null!==c)return!c.acceptsBooleans;a=a.toLowerCase().slice(0,5);return"data-"!==a&&"aria-"!==a;default:return!1}}function qi(a,b,c,d){if(null===b||"undefined"===typeof b||pi(a,b,c,d))return!0;if(d)return!1;if(null!==c)switch(c.type){case 3:return!b;case 4:return!1===b;case 5:return isNaN(b);case 6:return isNaN(b)||1>b}return!1}function L(a, +b,c,d,e,f){this.acceptsBooleans=2===b||3===b||4===b;this.attributeName=d;this.attributeNamespace=e;this.mustUseProperty=c;this.propertyName=a;this.type=b;this.sanitizeURL=f}function xd(a,b,c,d){var e=E.hasOwnProperty(b)?E[b]:null;var f=null!==e?0===e.type:d?!1:!(2<b.length)||"o"!==b[0]&&"O"!==b[0]||"n"!==b[1]&&"N"!==b[1]?!1:!0;f||(qi(b,c,e,d)&&(c=null),d||null===e?ni(b)&&(null===c?a.removeAttribute(b):a.setAttribute(b,""+c)):e.mustUseProperty?a[e.propertyName]=null===c?3===e.type?!1:"":c:(b=e.attributeName, +d=e.attributeNamespace,null===c?a.removeAttribute(b):(e=e.type,c=3===e||4===e&&!0===c?"":""+c,d?a.setAttributeNS(d,b,c):a.setAttribute(b,c))))}function zb(a){if(null===a||"object"!==typeof a)return null;a=zf&&a[zf]||a["@@iterator"];return"function"===typeof a?a:null}function ri(a){if(-1===a._status){a._status=0;var b=a._ctor;b=b();a._result=b;b.then(function(b){0===a._status&&(b=b.default,a._status=1,a._result=b)},function(b){0===a._status&&(a._status=2,a._result=b)})}}function na(a){if(null==a)return null; +if("function"===typeof a)return a.displayName||a.name||null;if("string"===typeof a)return a;switch(a){case Ma:return"Fragment";case gb:return"Portal";case kc:return"Profiler";case Af:return"StrictMode";case lc:return"Suspense";case yd:return"SuspenseList"}if("object"===typeof a)switch(a.$$typeof){case Bf:return"Context.Consumer";case Cf:return"Context.Provider";case zd:var b=a.render;b=b.displayName||b.name||"";return a.displayName||(""!==b?"ForwardRef("+b+")":"ForwardRef");case Ad:return na(a.type); +case Df:return na(a.render);case Ef:if(a=1===a._status?a._result:null)return na(a)}return null}function Bd(a){var b="";do{a:switch(a.tag){case 3:case 4:case 6:case 7:case 10:case 9:var c="";break a;default:var d=a._debugOwner,e=a._debugSource,f=na(a.type);c=null;d&&(c=na(d.type));d=f;f="";e?f=" (at "+e.fileName.replace(si,"")+":"+e.lineNumber+")":c&&(f=" (created by "+c+")");c="\n in "+(d||"Unknown")+f}b+=c;a=a.return}while(a);return b}function va(a){switch(typeof a){case "boolean":case "number":case "object":case "string":case "undefined":return a; +default:return""}}function Ff(a){var b=a.type;return(a=a.nodeName)&&"input"===a.toLowerCase()&&("checkbox"===b||"radio"===b)}function ti(a){var b=Ff(a)?"checked":"value",c=Object.getOwnPropertyDescriptor(a.constructor.prototype,b),d=""+a[b];if(!a.hasOwnProperty(b)&&"undefined"!==typeof c&&"function"===typeof c.get&&"function"===typeof c.set){var e=c.get,f=c.set;Object.defineProperty(a,b,{configurable:!0,get:function(){return e.call(this)},set:function(a){d=""+a;f.call(this,a)}});Object.defineProperty(a, +b,{enumerable:c.enumerable});return{getValue:function(){return d},setValue:function(a){d=""+a},stopTracking:function(){a._valueTracker=null;delete a[b]}}}}function mc(a){a._valueTracker||(a._valueTracker=ti(a))}function Gf(a){if(!a)return!1;var b=a._valueTracker;if(!b)return!0;var c=b.getValue();var d="";a&&(d=Ff(a)?a.checked?"true":"false":a.value);a=d;return a!==c?(b.setValue(a),!0):!1}function Cd(a,b){var c=b.checked;return M({},b,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:null!= +c?c:a._wrapperState.initialChecked})}function Hf(a,b){var c=null==b.defaultValue?"":b.defaultValue,d=null!=b.checked?b.checked:b.defaultChecked;c=va(null!=b.value?b.value:c);a._wrapperState={initialChecked:d,initialValue:c,controlled:"checkbox"===b.type||"radio"===b.type?null!=b.checked:null!=b.value}}function If(a,b){b=b.checked;null!=b&&xd(a,"checked",b,!1)}function Dd(a,b){If(a,b);var c=va(b.value),d=b.type;if(null!=c)if("number"===d){if(0===c&&""===a.value||a.value!=c)a.value=""+c}else a.value!== +""+c&&(a.value=""+c);else if("submit"===d||"reset"===d){a.removeAttribute("value");return}b.hasOwnProperty("value")?Ed(a,b.type,c):b.hasOwnProperty("defaultValue")&&Ed(a,b.type,va(b.defaultValue));null==b.checked&&null!=b.defaultChecked&&(a.defaultChecked=!!b.defaultChecked)}function Jf(a,b,c){if(b.hasOwnProperty("value")||b.hasOwnProperty("defaultValue")){var d=b.type;if(!("submit"!==d&&"reset"!==d||void 0!==b.value&&null!==b.value))return;b=""+a._wrapperState.initialValue;c||b===a.value||(a.value= +b);a.defaultValue=b}c=a.name;""!==c&&(a.name="");a.defaultChecked=!!a._wrapperState.initialChecked;""!==c&&(a.name=c)}function Ed(a,b,c){if("number"!==b||a.ownerDocument.activeElement!==a)null==c?a.defaultValue=""+a._wrapperState.initialValue:a.defaultValue!==""+c&&(a.defaultValue=""+c)}function ui(a){var b="";ea.Children.forEach(a,function(a){null!=a&&(b+=a)});return b}function Fd(a,b){a=M({children:void 0},b);if(b=ui(b.children))a.children=b;return a}function hb(a,b,c,d){a=a.options;if(b){b={}; +for(var e=0;e<c.length;e++)b["$"+c[e]]=!0;for(c=0;c<a.length;c++)e=b.hasOwnProperty("$"+a[c].value),a[c].selected!==e&&(a[c].selected=e),e&&d&&(a[c].defaultSelected=!0)}else{c=""+va(c);b=null;for(e=0;e<a.length;e++){if(a[e].value===c){a[e].selected=!0;d&&(a[e].defaultSelected=!0);return}null!==b||a[e].disabled||(b=a[e])}null!==b&&(b.selected=!0)}}function Gd(a,b){if(null!=b.dangerouslySetInnerHTML)throw Error(k(91));return M({},b,{value:void 0,defaultValue:void 0,children:""+a._wrapperState.initialValue})} +function Kf(a,b){var c=b.value;if(null==c){c=b.children;b=b.defaultValue;if(null!=c){if(null!=b)throw Error(k(92));if(Array.isArray(c)){if(!(1>=c.length))throw Error(k(93));c=c[0]}b=c}null==b&&(b="");c=b}a._wrapperState={initialValue:va(c)}}function Lf(a,b){var c=va(b.value),d=va(b.defaultValue);null!=c&&(c=""+c,c!==a.value&&(a.value=c),null==b.defaultValue&&a.defaultValue!==c&&(a.defaultValue=c));null!=d&&(a.defaultValue=""+d)}function Mf(a,b){b=a.textContent;b===a._wrapperState.initialValue&&""!== +b&&null!==b&&(a.value=b)}function Nf(a){switch(a){case "svg":return"http://www.w3.org/2000/svg";case "math":return"http://www.w3.org/1998/Math/MathML";default:return"http://www.w3.org/1999/xhtml"}}function Hd(a,b){return null==a||"http://www.w3.org/1999/xhtml"===a?Nf(b):"http://www.w3.org/2000/svg"===a&&"foreignObject"===b?"http://www.w3.org/1999/xhtml":a}function nc(a,b){var c={};c[a.toLowerCase()]=b.toLowerCase();c["Webkit"+a]="webkit"+b;c["Moz"+a]="moz"+b;return c}function oc(a){if(Id[a])return Id[a]; +if(!ib[a])return a;var b=ib[a],c;for(c in b)if(b.hasOwnProperty(c)&&c in Of)return Id[a]=b[c];return a}function Jd(a){var b=Pf.get(a);void 0===b&&(b=new Map,Pf.set(a,b));return b}function Na(a){var b=a,c=a;if(a.alternate)for(;b.return;)b=b.return;else{a=b;do b=a,0!==(b.effectTag&1026)&&(c=b.return),a=b.return;while(a)}return 3===b.tag?c:null}function Qf(a){if(13===a.tag){var b=a.memoizedState;null===b&&(a=a.alternate,null!==a&&(b=a.memoizedState));if(null!==b)return b.dehydrated}return null}function Rf(a){if(Na(a)!== +a)throw Error(k(188));}function vi(a){var b=a.alternate;if(!b){b=Na(a);if(null===b)throw Error(k(188));return b!==a?null:a}for(var c=a,d=b;;){var e=c.return;if(null===e)break;var f=e.alternate;if(null===f){d=e.return;if(null!==d){c=d;continue}break}if(e.child===f.child){for(f=e.child;f;){if(f===c)return Rf(e),a;if(f===d)return Rf(e),b;f=f.sibling}throw Error(k(188));}if(c.return!==d.return)c=e,d=f;else{for(var g=!1,h=e.child;h;){if(h===c){g=!0;c=e;d=f;break}if(h===d){g=!0;d=e;c=f;break}h=h.sibling}if(!g){for(h= +f.child;h;){if(h===c){g=!0;c=f;d=e;break}if(h===d){g=!0;d=f;c=e;break}h=h.sibling}if(!g)throw Error(k(189));}}if(c.alternate!==d)throw Error(k(190));}if(3!==c.tag)throw Error(k(188));return c.stateNode.current===c?a:b}function Sf(a){a=vi(a);if(!a)return null;for(var b=a;;){if(5===b.tag||6===b.tag)return b;if(b.child)b.child.return=b,b=b.child;else{if(b===a)break;for(;!b.sibling;){if(!b.return||b.return===a)return null;b=b.return}b.sibling.return=b.return;b=b.sibling}}return null}function jb(a,b){if(null== +b)throw Error(k(30));if(null==a)return b;if(Array.isArray(a)){if(Array.isArray(b))return a.push.apply(a,b),a;a.push(b);return a}return Array.isArray(b)?[a].concat(b):[a,b]}function Kd(a,b,c){Array.isArray(a)?a.forEach(b,c):a&&b.call(c,a)}function pc(a){null!==a&&(Ab=jb(Ab,a));a=Ab;Ab=null;if(a){Kd(a,wi);if(Ab)throw Error(k(95));if(hc)throw a=pd,hc=!1,pd=null,a;}}function Ld(a){a=a.target||a.srcElement||window;a.correspondingUseElement&&(a=a.correspondingUseElement);return 3===a.nodeType?a.parentNode: +a}function Tf(a){if(!wa)return!1;a="on"+a;var b=a in document;b||(b=document.createElement("div"),b.setAttribute(a,"return;"),b="function"===typeof b[a]);return b}function Uf(a){a.topLevelType=null;a.nativeEvent=null;a.targetInst=null;a.ancestors.length=0;10>qc.length&&qc.push(a)}function Vf(a,b,c,d){if(qc.length){var e=qc.pop();e.topLevelType=a;e.eventSystemFlags=d;e.nativeEvent=b;e.targetInst=c;return e}return{topLevelType:a,eventSystemFlags:d,nativeEvent:b,targetInst:c,ancestors:[]}}function Wf(a){var b= +a.targetInst,c=b;do{if(!c){a.ancestors.push(c);break}var d=c;if(3===d.tag)d=d.stateNode.containerInfo;else{for(;d.return;)d=d.return;d=3!==d.tag?null:d.stateNode.containerInfo}if(!d)break;b=c.tag;5!==b&&6!==b||a.ancestors.push(c);c=Bb(d)}while(c);for(c=0;c<a.ancestors.length;c++){b=a.ancestors[c];var e=Ld(a.nativeEvent);d=a.topLevelType;var f=a.nativeEvent,g=a.eventSystemFlags;0===c&&(g|=64);for(var h=null,m=0;m<jc.length;m++){var n=jc[m];n&&(n=n.extractEvents(d,b,f,e,g))&&(h=jb(h,n))}pc(h)}}function Md(a, +b,c){if(!c.has(a)){switch(a){case "scroll":Cb(b,"scroll",!0);break;case "focus":case "blur":Cb(b,"focus",!0);Cb(b,"blur",!0);c.set("blur",null);c.set("focus",null);break;case "cancel":case "close":Tf(a)&&Cb(b,a,!0);break;case "invalid":case "submit":case "reset":break;default:-1===Db.indexOf(a)&&w(a,b)}c.set(a,null)}}function xi(a,b){var c=Jd(b);Nd.forEach(function(a){Md(a,b,c)});yi.forEach(function(a){Md(a,b,c)})}function Od(a,b,c,d,e){return{blockedOn:a,topLevelType:b,eventSystemFlags:c|32,nativeEvent:e, +container:d}}function Xf(a,b){switch(a){case "focus":case "blur":xa=null;break;case "dragenter":case "dragleave":ya=null;break;case "mouseover":case "mouseout":za=null;break;case "pointerover":case "pointerout":Eb.delete(b.pointerId);break;case "gotpointercapture":case "lostpointercapture":Fb.delete(b.pointerId)}}function Gb(a,b,c,d,e,f){if(null===a||a.nativeEvent!==f)return a=Od(b,c,d,e,f),null!==b&&(b=Hb(b),null!==b&&Yf(b)),a;a.eventSystemFlags|=d;return a}function zi(a,b,c,d,e){switch(b){case "focus":return xa= +Gb(xa,a,b,c,d,e),!0;case "dragenter":return ya=Gb(ya,a,b,c,d,e),!0;case "mouseover":return za=Gb(za,a,b,c,d,e),!0;case "pointerover":var f=e.pointerId;Eb.set(f,Gb(Eb.get(f)||null,a,b,c,d,e));return!0;case "gotpointercapture":return f=e.pointerId,Fb.set(f,Gb(Fb.get(f)||null,a,b,c,d,e)),!0}return!1}function Ai(a){var b=Bb(a.target);if(null!==b){var c=Na(b);if(null!==c)if(b=c.tag,13===b){if(b=Qf(c),null!==b){a.blockedOn=b;Pd(a.priority,function(){Bi(c)});return}}else if(3===b&&c.stateNode.hydrate){a.blockedOn= +3===c.tag?c.stateNode.containerInfo:null;return}}a.blockedOn=null}function rc(a){if(null!==a.blockedOn)return!1;var b=Qd(a.topLevelType,a.eventSystemFlags,a.container,a.nativeEvent);if(null!==b){var c=Hb(b);null!==c&&Yf(c);a.blockedOn=b;return!1}return!0}function Zf(a,b,c){rc(a)&&c.delete(b)}function Ci(){for(Rd=!1;0<fa.length;){var a=fa[0];if(null!==a.blockedOn){a=Hb(a.blockedOn);null!==a&&Di(a);break}var b=Qd(a.topLevelType,a.eventSystemFlags,a.container,a.nativeEvent);null!==b?a.blockedOn=b:fa.shift()}null!== +xa&&rc(xa)&&(xa=null);null!==ya&&rc(ya)&&(ya=null);null!==za&&rc(za)&&(za=null);Eb.forEach(Zf);Fb.forEach(Zf)}function Ib(a,b){a.blockedOn===b&&(a.blockedOn=null,Rd||(Rd=!0,$f(ag,Ci)))}function bg(a){if(0<fa.length){Ib(fa[0],a);for(var b=1;b<fa.length;b++){var c=fa[b];c.blockedOn===a&&(c.blockedOn=null)}}null!==xa&&Ib(xa,a);null!==ya&&Ib(ya,a);null!==za&&Ib(za,a);b=function(b){return Ib(b,a)};Eb.forEach(b);Fb.forEach(b);for(b=0;b<Jb.length;b++)c=Jb[b],c.blockedOn===a&&(c.blockedOn=null);for(;0<Jb.length&& +(b=Jb[0],null===b.blockedOn);)Ai(b),null===b.blockedOn&&Jb.shift()}function Sd(a,b){for(var c=0;c<a.length;c+=2){var d=a[c],e=a[c+1],f="on"+(e[0].toUpperCase()+e.slice(1));f={phasedRegistrationNames:{bubbled:f,captured:f+"Capture"},dependencies:[d],eventPriority:b};Td.set(d,b);cg.set(d,f);dg[e]=f}}function w(a,b){Cb(b,a,!1)}function Cb(a,b,c){var d=Td.get(b);switch(void 0===d?2:d){case 0:d=Ei.bind(null,b,1,a);break;case 1:d=Fi.bind(null,b,1,a);break;default:d=sc.bind(null,b,1,a)}c?a.addEventListener(b, +d,!0):a.addEventListener(b,d,!1)}function Ei(a,b,c,d){Oa||vd();var e=sc,f=Oa;Oa=!0;try{eg(e,a,b,c,d)}finally{(Oa=f)||ud()}}function Fi(a,b,c,d){Gi(Hi,sc.bind(null,a,b,c,d))}function sc(a,b,c,d){if(tc)if(0<fa.length&&-1<Nd.indexOf(a))a=Od(null,a,b,c,d),fa.push(a);else{var e=Qd(a,b,c,d);if(null===e)Xf(a,d);else if(-1<Nd.indexOf(a))a=Od(e,a,b,c,d),fa.push(a);else if(!zi(e,a,b,c,d)){Xf(a,d);a=Vf(a,d,null,b);try{uf(Wf,a)}finally{Uf(a)}}}}function Qd(a,b,c,d){c=Ld(d);c=Bb(c);if(null!==c){var e=Na(c);if(null=== +e)c=null;else{var f=e.tag;if(13===f){c=Qf(e);if(null!==c)return c;c=null}else if(3===f){if(e.stateNode.hydrate)return 3===e.tag?e.stateNode.containerInfo:null;c=null}else e!==c&&(c=null)}}a=Vf(a,d,c,b);try{uf(Wf,a)}finally{Uf(a)}return null}function fg(a,b,c){return null==b||"boolean"===typeof b||""===b?"":c||"number"!==typeof b||0===b||Kb.hasOwnProperty(a)&&Kb[a]?(""+b).trim():b+"px"}function gg(a,b){a=a.style;for(var c in b)if(b.hasOwnProperty(c)){var d=0===c.indexOf("--"),e=fg(c,b[c],d);"float"=== +c&&(c="cssFloat");d?a.setProperty(c,e):a[c]=e}}function Ud(a,b){if(b){if(Ii[a]&&(null!=b.children||null!=b.dangerouslySetInnerHTML))throw Error(k(137,a,""));if(null!=b.dangerouslySetInnerHTML){if(null!=b.children)throw Error(k(60));if(!("object"===typeof b.dangerouslySetInnerHTML&&"__html"in b.dangerouslySetInnerHTML))throw Error(k(61));}if(null!=b.style&&"object"!==typeof b.style)throw Error(k(62,""));}}function Vd(a,b){if(-1===a.indexOf("-"))return"string"===typeof b.is;switch(a){case "annotation-xml":case "color-profile":case "font-face":case "font-face-src":case "font-face-uri":case "font-face-format":case "font-face-name":case "missing-glyph":return!1; +default:return!0}}function oa(a,b){a=9===a.nodeType||11===a.nodeType?a:a.ownerDocument;var c=Jd(a);b=rd[b];for(var d=0;d<b.length;d++)Md(b[d],a,c)}function uc(){}function Wd(a){a=a||("undefined"!==typeof document?document:void 0);if("undefined"===typeof a)return null;try{return a.activeElement||a.body}catch(b){return a.body}}function hg(a){for(;a&&a.firstChild;)a=a.firstChild;return a}function ig(a,b){var c=hg(a);a=0;for(var d;c;){if(3===c.nodeType){d=a+c.textContent.length;if(a<=b&&d>=b)return{node:c, +offset:b-a};a=d}a:{for(;c;){if(c.nextSibling){c=c.nextSibling;break a}c=c.parentNode}c=void 0}c=hg(c)}}function jg(a,b){return a&&b?a===b?!0:a&&3===a.nodeType?!1:b&&3===b.nodeType?jg(a,b.parentNode):"contains"in a?a.contains(b):a.compareDocumentPosition?!!(a.compareDocumentPosition(b)&16):!1:!1}function kg(){for(var a=window,b=Wd();b instanceof a.HTMLIFrameElement;){try{var c="string"===typeof b.contentWindow.location.href}catch(d){c=!1}if(c)a=b.contentWindow;else break;b=Wd(a.document)}return b} +function Xd(a){var b=a&&a.nodeName&&a.nodeName.toLowerCase();return b&&("input"===b&&("text"===a.type||"search"===a.type||"tel"===a.type||"url"===a.type||"password"===a.type)||"textarea"===b||"true"===a.contentEditable)}function lg(a,b){switch(a){case "button":case "input":case "select":case "textarea":return!!b.autoFocus}return!1}function Yd(a,b){return"textarea"===a||"option"===a||"noscript"===a||"string"===typeof b.children||"number"===typeof b.children||"object"===typeof b.dangerouslySetInnerHTML&& +null!==b.dangerouslySetInnerHTML&&null!=b.dangerouslySetInnerHTML.__html}function kb(a){for(;null!=a;a=a.nextSibling){var b=a.nodeType;if(1===b||3===b)break}return a}function mg(a){a=a.previousSibling;for(var b=0;a;){if(8===a.nodeType){var c=a.data;if(c===ng||c===Zd||c===$d){if(0===b)return a;b--}else c===og&&b++}a=a.previousSibling}return null}function Bb(a){var b=a[Aa];if(b)return b;for(var c=a.parentNode;c;){if(b=c[Lb]||c[Aa]){c=b.alternate;if(null!==b.child||null!==c&&null!==c.child)for(a=mg(a);null!== +a;){if(c=a[Aa])return c;a=mg(a)}return b}a=c;c=a.parentNode}return null}function Hb(a){a=a[Aa]||a[Lb];return!a||5!==a.tag&&6!==a.tag&&13!==a.tag&&3!==a.tag?null:a}function Pa(a){if(5===a.tag||6===a.tag)return a.stateNode;throw Error(k(33));}function ae(a){return a[vc]||null}function pa(a){do a=a.return;while(a&&5!==a.tag);return a?a:null}function pg(a,b){var c=a.stateNode;if(!c)return null;var d=td(c);if(!d)return null;c=d[b];a:switch(b){case "onClick":case "onClickCapture":case "onDoubleClick":case "onDoubleClickCapture":case "onMouseDown":case "onMouseDownCapture":case "onMouseMove":case "onMouseMoveCapture":case "onMouseUp":case "onMouseUpCapture":case "onMouseEnter":(d= +!d.disabled)||(a=a.type,d=!("button"===a||"input"===a||"select"===a||"textarea"===a));a=!d;break a;default:a=!1}if(a)return null;if(c&&"function"!==typeof c)throw Error(k(231,b,typeof c));return c}function qg(a,b,c){if(b=pg(a,c.dispatchConfig.phasedRegistrationNames[b]))c._dispatchListeners=jb(c._dispatchListeners,b),c._dispatchInstances=jb(c._dispatchInstances,a)}function Ji(a){if(a&&a.dispatchConfig.phasedRegistrationNames){for(var b=a._targetInst,c=[];b;)c.push(b),b=pa(b);for(b=c.length;0<b--;)qg(c[b], +"captured",a);for(b=0;b<c.length;b++)qg(c[b],"bubbled",a)}}function be(a,b,c){a&&c&&c.dispatchConfig.registrationName&&(b=pg(a,c.dispatchConfig.registrationName))&&(c._dispatchListeners=jb(c._dispatchListeners,b),c._dispatchInstances=jb(c._dispatchInstances,a))}function Ki(a){a&&a.dispatchConfig.registrationName&&be(a._targetInst,null,a)}function lb(a){Kd(a,Ji)}function rg(){if(wc)return wc;var a,b=ce,c=b.length,d,e="value"in Ba?Ba.value:Ba.textContent,f=e.length;for(a=0;a<c&&b[a]===e[a];a++);var g= +c-a;for(d=1;d<=g&&b[c-d]===e[f-d];d++);return wc=e.slice(a,1<d?1-d:void 0)}function xc(){return!0}function yc(){return!1}function R(a,b,c,d){this.dispatchConfig=a;this._targetInst=b;this.nativeEvent=c;a=this.constructor.Interface;for(var e in a)a.hasOwnProperty(e)&&((b=a[e])?this[e]=b(c):"target"===e?this.target=d:this[e]=c[e]);this.isDefaultPrevented=(null!=c.defaultPrevented?c.defaultPrevented:!1===c.returnValue)?xc:yc;this.isPropagationStopped=yc;return this}function Li(a,b,c,d){if(this.eventPool.length){var e= +this.eventPool.pop();this.call(e,a,b,c,d);return e}return new this(a,b,c,d)}function Mi(a){if(!(a instanceof this))throw Error(k(279));a.destructor();10>this.eventPool.length&&this.eventPool.push(a)}function sg(a){a.eventPool=[];a.getPooled=Li;a.release=Mi}function tg(a,b){switch(a){case "keyup":return-1!==Ni.indexOf(b.keyCode);case "keydown":return 229!==b.keyCode;case "keypress":case "mousedown":case "blur":return!0;default:return!1}}function ug(a){a=a.detail;return"object"===typeof a&&"data"in +a?a.data:null}function Oi(a,b){switch(a){case "compositionend":return ug(b);case "keypress":if(32!==b.which)return null;vg=!0;return wg;case "textInput":return a=b.data,a===wg&&vg?null:a;default:return null}}function Pi(a,b){if(mb)return"compositionend"===a||!de&&tg(a,b)?(a=rg(),wc=ce=Ba=null,mb=!1,a):null;switch(a){case "paste":return null;case "keypress":if(!(b.ctrlKey||b.altKey||b.metaKey)||b.ctrlKey&&b.altKey){if(b.char&&1<b.char.length)return b.char;if(b.which)return String.fromCharCode(b.which)}return null; +case "compositionend":return xg&&"ko"!==b.locale?null:b.data;default:return null}}function yg(a){var b=a&&a.nodeName&&a.nodeName.toLowerCase();return"input"===b?!!Qi[a.type]:"textarea"===b?!0:!1}function zg(a,b,c){a=R.getPooled(Ag.change,a,b,c);a.type="change";sf(c);lb(a);return a}function Ri(a){pc(a)}function zc(a){var b=Pa(a);if(Gf(b))return a}function Si(a,b){if("change"===a)return b}function Bg(){Mb&&(Mb.detachEvent("onpropertychange",Cg),Nb=Mb=null)}function Cg(a){if("value"===a.propertyName&& +zc(Nb))if(a=zg(Nb,a,Ld(a)),Oa)pc(a);else{Oa=!0;try{ee(Ri,a)}finally{Oa=!1,ud()}}}function Ti(a,b,c){"focus"===a?(Bg(),Mb=b,Nb=c,Mb.attachEvent("onpropertychange",Cg)):"blur"===a&&Bg()}function Ui(a,b){if("selectionchange"===a||"keyup"===a||"keydown"===a)return zc(Nb)}function Vi(a,b){if("click"===a)return zc(b)}function Wi(a,b){if("input"===a||"change"===a)return zc(b)}function Xi(a){var b=this.nativeEvent;return b.getModifierState?b.getModifierState(a):(a=Yi[a])?!!b[a]:!1}function fe(a){return Xi} +function Zi(a,b){return a===b&&(0!==a||1/a===1/b)||a!==a&&b!==b}function Ob(a,b){if(Qa(a,b))return!0;if("object"!==typeof a||null===a||"object"!==typeof b||null===b)return!1;var c=Object.keys(a),d=Object.keys(b);if(c.length!==d.length)return!1;for(d=0;d<c.length;d++)if(!$i.call(b,c[d])||!Qa(a[c[d]],b[c[d]]))return!1;return!0}function Dg(a,b){var c=b.window===b?b.document:9===b.nodeType?b:b.ownerDocument;if(ge||null==nb||nb!==Wd(c))return null;c=nb;"selectionStart"in c&&Xd(c)?c={start:c.selectionStart, +end:c.selectionEnd}:(c=(c.ownerDocument&&c.ownerDocument.defaultView||window).getSelection(),c={anchorNode:c.anchorNode,anchorOffset:c.anchorOffset,focusNode:c.focusNode,focusOffset:c.focusOffset});return Pb&&Ob(Pb,c)?null:(Pb=c,a=R.getPooled(Eg.select,he,a,b),a.type="select",a.target=nb,lb(a),a)}function Ac(a){var b=a.keyCode;"charCode"in a?(a=a.charCode,0===a&&13===b&&(a=13)):a=b;10===a&&(a=13);return 32<=a||13===a?a:0}function q(a,b){0>ob||(a.current=ie[ob],ie[ob]=null,ob--)}function y(a,b,c){ob++; +ie[ob]=a.current;a.current=b}function pb(a,b){var c=a.type.contextTypes;if(!c)return Ca;var d=a.stateNode;if(d&&d.__reactInternalMemoizedUnmaskedChildContext===b)return d.__reactInternalMemoizedMaskedChildContext;var e={},f;for(f in c)e[f]=b[f];d&&(a=a.stateNode,a.__reactInternalMemoizedUnmaskedChildContext=b,a.__reactInternalMemoizedMaskedChildContext=e);return e}function N(a){a=a.childContextTypes;return null!==a&&void 0!==a}function Fg(a,b,c){if(B.current!==Ca)throw Error(k(168));y(B,b);y(G,c)} +function Gg(a,b,c){var d=a.stateNode;a=b.childContextTypes;if("function"!==typeof d.getChildContext)return c;d=d.getChildContext();for(var e in d)if(!(e in a))throw Error(k(108,na(b)||"Unknown",e));return M({},c,{},d)}function Bc(a){a=(a=a.stateNode)&&a.__reactInternalMemoizedMergedChildContext||Ca;Ra=B.current;y(B,a);y(G,G.current);return!0}function Hg(a,b,c){var d=a.stateNode;if(!d)throw Error(k(169));c?(a=Gg(a,b,Ra),d.__reactInternalMemoizedMergedChildContext=a,q(G),q(B),y(B,a)):q(G);y(G,c)}function Cc(){switch(aj()){case Dc:return 99; +case Ig:return 98;case Jg:return 97;case Kg:return 96;case Lg:return 95;default:throw Error(k(332));}}function Mg(a){switch(a){case 99:return Dc;case 98:return Ig;case 97:return Jg;case 96:return Kg;case 95:return Lg;default:throw Error(k(332));}}function Da(a,b){a=Mg(a);return bj(a,b)}function Ng(a,b,c){a=Mg(a);return je(a,b,c)}function Og(a){null===qa?(qa=[a],Ec=je(Dc,Pg)):qa.push(a);return Qg}function ha(){if(null!==Ec){var a=Ec;Ec=null;Rg(a)}Pg()}function Pg(){if(!ke&&null!==qa){ke=!0;var a=0; +try{var b=qa;Da(99,function(){for(;a<b.length;a++){var c=b[a];do c=c(!0);while(null!==c)}});qa=null}catch(c){throw null!==qa&&(qa=qa.slice(a+1)),je(Dc,ha),c;}finally{ke=!1}}}function Fc(a,b,c){c/=10;return 1073741821-(((1073741821-a+b/10)/c|0)+1)*c}function aa(a,b){if(a&&a.defaultProps){b=M({},b);a=a.defaultProps;for(var c in a)void 0===b[c]&&(b[c]=a[c])}return b}function le(){Gc=qb=Hc=null}function me(a){var b=Ic.current;q(Ic);a.type._context._currentValue=b}function Sg(a,b){for(;null!==a;){var c= +a.alternate;if(a.childExpirationTime<b)a.childExpirationTime=b,null!==c&&c.childExpirationTime<b&&(c.childExpirationTime=b);else if(null!==c&&c.childExpirationTime<b)c.childExpirationTime=b;else break;a=a.return}}function rb(a,b){Hc=a;Gc=qb=null;a=a.dependencies;null!==a&&null!==a.firstContext&&(a.expirationTime>=b&&(ia=!0),a.firstContext=null)}function W(a,b){if(Gc!==a&&!1!==b&&0!==b){if("number"!==typeof b||1073741823===b)Gc=a,b=1073741823;b={context:a,observedBits:b,next:null};if(null===qb){if(null=== +Hc)throw Error(k(308));qb=b;Hc.dependencies={expirationTime:0,firstContext:b,responders:null}}else qb=qb.next=b}return a._currentValue}function ne(a){a.updateQueue={baseState:a.memoizedState,baseQueue:null,shared:{pending:null},effects:null}}function oe(a,b){a=a.updateQueue;b.updateQueue===a&&(b.updateQueue={baseState:a.baseState,baseQueue:a.baseQueue,shared:a.shared,effects:a.effects})}function Ea(a,b){a={expirationTime:a,suspenseConfig:b,tag:Tg,payload:null,callback:null,next:null};return a.next= +a}function Fa(a,b){a=a.updateQueue;if(null!==a){a=a.shared;var c=a.pending;null===c?b.next=b:(b.next=c.next,c.next=b);a.pending=b}}function Ug(a,b){var c=a.alternate;null!==c&&oe(c,a);a=a.updateQueue;c=a.baseQueue;null===c?(a.baseQueue=b.next=b,b.next=b):(b.next=c.next,c.next=b)}function Qb(a,b,c,d){var e=a.updateQueue;Ga=!1;var f=e.baseQueue,g=e.shared.pending;if(null!==g){if(null!==f){var h=f.next;f.next=g.next;g.next=h}f=g;e.shared.pending=null;h=a.alternate;null!==h&&(h=h.updateQueue,null!==h&& +(h.baseQueue=g))}if(null!==f){h=f.next;var m=e.baseState,n=0,k=null,ba=null,l=null;if(null!==h){var p=h;do{g=p.expirationTime;if(g<d){var t={expirationTime:p.expirationTime,suspenseConfig:p.suspenseConfig,tag:p.tag,payload:p.payload,callback:p.callback,next:null};null===l?(ba=l=t,k=m):l=l.next=t;g>n&&(n=g)}else{null!==l&&(l=l.next={expirationTime:1073741823,suspenseConfig:p.suspenseConfig,tag:p.tag,payload:p.payload,callback:p.callback,next:null});Vg(g,p.suspenseConfig);a:{var q=a,r=p;g=b;t=c;switch(r.tag){case 1:q= +r.payload;if("function"===typeof q){m=q.call(t,m,g);break a}m=q;break a;case 3:q.effectTag=q.effectTag&-4097|64;case Tg:q=r.payload;g="function"===typeof q?q.call(t,m,g):q;if(null===g||void 0===g)break a;m=M({},m,g);break a;case Jc:Ga=!0}}null!==p.callback&&(a.effectTag|=32,g=e.effects,null===g?e.effects=[p]:g.push(p))}p=p.next;if(null===p||p===h)if(g=e.shared.pending,null===g)break;else p=f.next=g.next,g.next=h,e.baseQueue=f=g,e.shared.pending=null}while(1)}null===l?k=m:l.next=ba;e.baseState=k;e.baseQueue= +l;Kc(n);a.expirationTime=n;a.memoizedState=m}}function Wg(a,b,c){a=b.effects;b.effects=null;if(null!==a)for(b=0;b<a.length;b++){var d=a[b],e=d.callback;if(null!==e){d.callback=null;d=e;e=c;if("function"!==typeof d)throw Error(k(191,d));d.call(e)}}}function Lc(a,b,c,d){b=a.memoizedState;c=c(d,b);c=null===c||void 0===c?b:M({},b,c);a.memoizedState=c;0===a.expirationTime&&(a.updateQueue.baseState=c)}function Xg(a,b,c,d,e,f,g){a=a.stateNode;return"function"===typeof a.shouldComponentUpdate?a.shouldComponentUpdate(d, +f,g):b.prototype&&b.prototype.isPureReactComponent?!Ob(c,d)||!Ob(e,f):!0}function Yg(a,b,c){var d=!1,e=Ca;var f=b.contextType;"object"===typeof f&&null!==f?f=W(f):(e=N(b)?Ra:B.current,d=b.contextTypes,f=(d=null!==d&&void 0!==d)?pb(a,e):Ca);b=new b(c,f);a.memoizedState=null!==b.state&&void 0!==b.state?b.state:null;b.updater=Mc;a.stateNode=b;b._reactInternalFiber=a;d&&(a=a.stateNode,a.__reactInternalMemoizedUnmaskedChildContext=e,a.__reactInternalMemoizedMaskedChildContext=f);return b}function Zg(a, +b,c,d){a=b.state;"function"===typeof b.componentWillReceiveProps&&b.componentWillReceiveProps(c,d);"function"===typeof b.UNSAFE_componentWillReceiveProps&&b.UNSAFE_componentWillReceiveProps(c,d);b.state!==a&&Mc.enqueueReplaceState(b,b.state,null)}function pe(a,b,c,d){var e=a.stateNode;e.props=c;e.state=a.memoizedState;e.refs=$g;ne(a);var f=b.contextType;"object"===typeof f&&null!==f?e.context=W(f):(f=N(b)?Ra:B.current,e.context=pb(a,f));Qb(a,c,e,d);e.state=a.memoizedState;f=b.getDerivedStateFromProps; +"function"===typeof f&&(Lc(a,b,f,c),e.state=a.memoizedState);"function"===typeof b.getDerivedStateFromProps||"function"===typeof e.getSnapshotBeforeUpdate||"function"!==typeof e.UNSAFE_componentWillMount&&"function"!==typeof e.componentWillMount||(b=e.state,"function"===typeof e.componentWillMount&&e.componentWillMount(),"function"===typeof e.UNSAFE_componentWillMount&&e.UNSAFE_componentWillMount(),b!==e.state&&Mc.enqueueReplaceState(e,e.state,null),Qb(a,c,e,d),e.state=a.memoizedState);"function"=== +typeof e.componentDidMount&&(a.effectTag|=4)}function Rb(a,b,c){a=c.ref;if(null!==a&&"function"!==typeof a&&"object"!==typeof a){if(c._owner){c=c._owner;if(c){if(1!==c.tag)throw Error(k(309));var d=c.stateNode}if(!d)throw Error(k(147,a));var e=""+a;if(null!==b&&null!==b.ref&&"function"===typeof b.ref&&b.ref._stringRef===e)return b.ref;b=function(a){var b=d.refs;b===$g&&(b=d.refs={});null===a?delete b[e]:b[e]=a};b._stringRef=e;return b}if("string"!==typeof a)throw Error(k(284));if(!c._owner)throw Error(k(290, +a));}return a}function Nc(a,b){if("textarea"!==a.type)throw Error(k(31,"[object Object]"===Object.prototype.toString.call(b)?"object with keys {"+Object.keys(b).join(", ")+"}":b,""));}function ah(a){function b(b,c){if(a){var d=b.lastEffect;null!==d?(d.nextEffect=c,b.lastEffect=c):b.firstEffect=b.lastEffect=c;c.nextEffect=null;c.effectTag=8}}function c(c,d){if(!a)return null;for(;null!==d;)b(c,d),d=d.sibling;return null}function d(a,b){for(a=new Map;null!==b;)null!==b.key?a.set(b.key,b):a.set(b.index, +b),b=b.sibling;return a}function e(a,b){a=Sa(a,b);a.index=0;a.sibling=null;return a}function f(b,c,d){b.index=d;if(!a)return c;d=b.alternate;if(null!==d)return d=d.index,d<c?(b.effectTag=2,c):d;b.effectTag=2;return c}function g(b){a&&null===b.alternate&&(b.effectTag=2);return b}function h(a,b,c,d){if(null===b||6!==b.tag)return b=qe(c,a.mode,d),b.return=a,b;b=e(b,c);b.return=a;return b}function m(a,b,c,d){if(null!==b&&b.elementType===c.type)return d=e(b,c.props),d.ref=Rb(a,b,c),d.return=a,d;d=Oc(c.type, +c.key,c.props,null,a.mode,d);d.ref=Rb(a,b,c);d.return=a;return d}function n(a,b,c,d){if(null===b||4!==b.tag||b.stateNode.containerInfo!==c.containerInfo||b.stateNode.implementation!==c.implementation)return b=re(c,a.mode,d),b.return=a,b;b=e(b,c.children||[]);b.return=a;return b}function l(a,b,c,d,f){if(null===b||7!==b.tag)return b=Ha(c,a.mode,d,f),b.return=a,b;b=e(b,c);b.return=a;return b}function ba(a,b,c){if("string"===typeof b||"number"===typeof b)return b=qe(""+b,a.mode,c),b.return=a,b;if("object"=== +typeof b&&null!==b){switch(b.$$typeof){case Pc:return c=Oc(b.type,b.key,b.props,null,a.mode,c),c.ref=Rb(a,null,b),c.return=a,c;case gb:return b=re(b,a.mode,c),b.return=a,b}if(Qc(b)||zb(b))return b=Ha(b,a.mode,c,null),b.return=a,b;Nc(a,b)}return null}function p(a,b,c,d){var e=null!==b?b.key:null;if("string"===typeof c||"number"===typeof c)return null!==e?null:h(a,b,""+c,d);if("object"===typeof c&&null!==c){switch(c.$$typeof){case Pc:return c.key===e?c.type===Ma?l(a,b,c.props.children,d,e):m(a,b,c, +d):null;case gb:return c.key===e?n(a,b,c,d):null}if(Qc(c)||zb(c))return null!==e?null:l(a,b,c,d,null);Nc(a,c)}return null}function t(a,b,c,d,e){if("string"===typeof d||"number"===typeof d)return a=a.get(c)||null,h(b,a,""+d,e);if("object"===typeof d&&null!==d){switch(d.$$typeof){case Pc:return a=a.get(null===d.key?c:d.key)||null,d.type===Ma?l(b,a,d.props.children,e,d.key):m(b,a,d,e);case gb:return a=a.get(null===d.key?c:d.key)||null,n(b,a,d,e)}if(Qc(d)||zb(d))return a=a.get(c)||null,l(b,a,d,e,null); +Nc(b,d)}return null}function q(e,g,h,m){for(var n=null,k=null,l=g,r=g=0,C=null;null!==l&&r<h.length;r++){l.index>r?(C=l,l=null):C=l.sibling;var O=p(e,l,h[r],m);if(null===O){null===l&&(l=C);break}a&&l&&null===O.alternate&&b(e,l);g=f(O,g,r);null===k?n=O:k.sibling=O;k=O;l=C}if(r===h.length)return c(e,l),n;if(null===l){for(;r<h.length;r++)l=ba(e,h[r],m),null!==l&&(g=f(l,g,r),null===k?n=l:k.sibling=l,k=l);return n}for(l=d(e,l);r<h.length;r++)C=t(l,e,r,h[r],m),null!==C&&(a&&null!==C.alternate&&l.delete(null=== +C.key?r:C.key),g=f(C,g,r),null===k?n=C:k.sibling=C,k=C);a&&l.forEach(function(a){return b(e,a)});return n}function w(e,g,h,n){var m=zb(h);if("function"!==typeof m)throw Error(k(150));h=m.call(h);if(null==h)throw Error(k(151));for(var l=m=null,r=g,C=g=0,O=null,v=h.next();null!==r&&!v.done;C++,v=h.next()){r.index>C?(O=r,r=null):O=r.sibling;var q=p(e,r,v.value,n);if(null===q){null===r&&(r=O);break}a&&r&&null===q.alternate&&b(e,r);g=f(q,g,C);null===l?m=q:l.sibling=q;l=q;r=O}if(v.done)return c(e,r),m; +if(null===r){for(;!v.done;C++,v=h.next())v=ba(e,v.value,n),null!==v&&(g=f(v,g,C),null===l?m=v:l.sibling=v,l=v);return m}for(r=d(e,r);!v.done;C++,v=h.next())v=t(r,e,C,v.value,n),null!==v&&(a&&null!==v.alternate&&r.delete(null===v.key?C:v.key),g=f(v,g,C),null===l?m=v:l.sibling=v,l=v);a&&r.forEach(function(a){return b(e,a)});return m}return function(a,d,f,h){var m="object"===typeof f&&null!==f&&f.type===Ma&&null===f.key;m&&(f=f.props.children);var n="object"===typeof f&&null!==f;if(n)switch(f.$$typeof){case Pc:a:{n= +f.key;for(m=d;null!==m;){if(m.key===n){switch(m.tag){case 7:if(f.type===Ma){c(a,m.sibling);d=e(m,f.props.children);d.return=a;a=d;break a}break;default:if(m.elementType===f.type){c(a,m.sibling);d=e(m,f.props);d.ref=Rb(a,m,f);d.return=a;a=d;break a}}c(a,m);break}else b(a,m);m=m.sibling}f.type===Ma?(d=Ha(f.props.children,a.mode,h,f.key),d.return=a,a=d):(h=Oc(f.type,f.key,f.props,null,a.mode,h),h.ref=Rb(a,d,f),h.return=a,a=h)}return g(a);case gb:a:{for(m=f.key;null!==d;){if(d.key===m)if(4===d.tag&&d.stateNode.containerInfo=== +f.containerInfo&&d.stateNode.implementation===f.implementation){c(a,d.sibling);d=e(d,f.children||[]);d.return=a;a=d;break a}else{c(a,d);break}else b(a,d);d=d.sibling}d=re(f,a.mode,h);d.return=a;a=d}return g(a)}if("string"===typeof f||"number"===typeof f)return f=""+f,null!==d&&6===d.tag?(c(a,d.sibling),d=e(d,f),d.return=a,a=d):(c(a,d),d=qe(f,a.mode,h),d.return=a,a=d),g(a);if(Qc(f))return q(a,d,f,h);if(zb(f))return w(a,d,f,h);n&&Nc(a,f);if("undefined"===typeof f&&!m)switch(a.tag){case 1:case 0:throw a= +a.type,Error(k(152,a.displayName||a.name||"Component"));}return c(a,d)}}function Ta(a){if(a===Sb)throw Error(k(174));return a}function se(a,b){y(Tb,b);y(Ub,a);y(ja,Sb);a=b.nodeType;switch(a){case 9:case 11:b=(b=b.documentElement)?b.namespaceURI:Hd(null,"");break;default:a=8===a?b.parentNode:b,b=a.namespaceURI||null,a=a.tagName,b=Hd(b,a)}q(ja);y(ja,b)}function tb(a){q(ja);q(Ub);q(Tb)}function bh(a){Ta(Tb.current);var b=Ta(ja.current);var c=Hd(b,a.type);b!==c&&(y(Ub,a),y(ja,c))}function te(a){Ub.current=== +a&&(q(ja),q(Ub))}function Rc(a){for(var b=a;null!==b;){if(13===b.tag){var c=b.memoizedState;if(null!==c&&(c=c.dehydrated,null===c||c.data===$d||c.data===Zd))return b}else if(19===b.tag&&void 0!==b.memoizedProps.revealOrder){if(0!==(b.effectTag&64))return b}else if(null!==b.child){b.child.return=b;b=b.child;continue}if(b===a)break;for(;null===b.sibling;){if(null===b.return||b.return===a)return null;b=b.return}b.sibling.return=b.return;b=b.sibling}return null}function ue(a,b){return{responder:a,props:b}} +function S(){throw Error(k(321));}function ve(a,b){if(null===b)return!1;for(var c=0;c<b.length&&c<a.length;c++)if(!Qa(a[c],b[c]))return!1;return!0}function we(a,b,c,d,e,f){Ia=f;z=b;b.memoizedState=null;b.updateQueue=null;b.expirationTime=0;Sc.current=null===a||null===a.memoizedState?dj:ej;a=c(d,e);if(b.expirationTime===Ia){f=0;do{b.expirationTime=0;if(!(25>f))throw Error(k(301));f+=1;J=K=null;b.updateQueue=null;Sc.current=fj;a=c(d,e)}while(b.expirationTime===Ia)}Sc.current=Tc;b=null!==K&&null!==K.next; +Ia=0;J=K=z=null;Uc=!1;if(b)throw Error(k(300));return a}function ub(){var a={memoizedState:null,baseState:null,baseQueue:null,queue:null,next:null};null===J?z.memoizedState=J=a:J=J.next=a;return J}function vb(){if(null===K){var a=z.alternate;a=null!==a?a.memoizedState:null}else a=K.next;var b=null===J?z.memoizedState:J.next;if(null!==b)J=b,K=a;else{if(null===a)throw Error(k(310));K=a;a={memoizedState:K.memoizedState,baseState:K.baseState,baseQueue:K.baseQueue,queue:K.queue,next:null};null===J?z.memoizedState= +J=a:J=J.next=a}return J}function Ua(a,b){return"function"===typeof b?b(a):b}function Vc(a,b,c){b=vb();c=b.queue;if(null===c)throw Error(k(311));c.lastRenderedReducer=a;var d=K,e=d.baseQueue,f=c.pending;if(null!==f){if(null!==e){var g=e.next;e.next=f.next;f.next=g}d.baseQueue=e=f;c.pending=null}if(null!==e){e=e.next;d=d.baseState;var h=g=f=null,m=e;do{var n=m.expirationTime;if(n<Ia){var l={expirationTime:m.expirationTime,suspenseConfig:m.suspenseConfig,action:m.action,eagerReducer:m.eagerReducer,eagerState:m.eagerState, +next:null};null===h?(g=h=l,f=d):h=h.next=l;n>z.expirationTime&&(z.expirationTime=n,Kc(n))}else null!==h&&(h=h.next={expirationTime:1073741823,suspenseConfig:m.suspenseConfig,action:m.action,eagerReducer:m.eagerReducer,eagerState:m.eagerState,next:null}),Vg(n,m.suspenseConfig),d=m.eagerReducer===a?m.eagerState:a(d,m.action);m=m.next}while(null!==m&&m!==e);null===h?f=d:h.next=g;Qa(d,b.memoizedState)||(ia=!0);b.memoizedState=d;b.baseState=f;b.baseQueue=h;c.lastRenderedState=d}return[b.memoizedState, +c.dispatch]}function Wc(a,b,c){b=vb();c=b.queue;if(null===c)throw Error(k(311));c.lastRenderedReducer=a;var d=c.dispatch,e=c.pending,f=b.memoizedState;if(null!==e){c.pending=null;var g=e=e.next;do f=a(f,g.action),g=g.next;while(g!==e);Qa(f,b.memoizedState)||(ia=!0);b.memoizedState=f;null===b.baseQueue&&(b.baseState=f);c.lastRenderedState=f}return[f,d]}function xe(a){var b=ub();"function"===typeof a&&(a=a());b.memoizedState=b.baseState=a;a=b.queue={pending:null,dispatch:null,lastRenderedReducer:Ua, +lastRenderedState:a};a=a.dispatch=ch.bind(null,z,a);return[b.memoizedState,a]}function ye(a,b,c,d){a={tag:a,create:b,destroy:c,deps:d,next:null};b=z.updateQueue;null===b?(b={lastEffect:null},z.updateQueue=b,b.lastEffect=a.next=a):(c=b.lastEffect,null===c?b.lastEffect=a.next=a:(d=c.next,c.next=a,a.next=d,b.lastEffect=a));return a}function dh(a){return vb().memoizedState}function ze(a,b,c,d){var e=ub();z.effectTag|=a;e.memoizedState=ye(1|b,c,void 0,void 0===d?null:d)}function Ae(a,b,c,d){var e=vb(); +d=void 0===d?null:d;var f=void 0;if(null!==K){var g=K.memoizedState;f=g.destroy;if(null!==d&&ve(d,g.deps)){ye(b,c,f,d);return}}z.effectTag|=a;e.memoizedState=ye(1|b,c,f,d)}function eh(a,b){return ze(516,4,a,b)}function Xc(a,b){return Ae(516,4,a,b)}function fh(a,b){return Ae(4,2,a,b)}function gh(a,b){if("function"===typeof b)return a=a(),b(a),function(){b(null)};if(null!==b&&void 0!==b)return a=a(),b.current=a,function(){b.current=null}}function hh(a,b,c){c=null!==c&&void 0!==c?c.concat([a]):null; +return Ae(4,2,gh.bind(null,b,a),c)}function Be(a,b){}function ih(a,b){ub().memoizedState=[a,void 0===b?null:b];return a}function Yc(a,b){var c=vb();b=void 0===b?null:b;var d=c.memoizedState;if(null!==d&&null!==b&&ve(b,d[1]))return d[0];c.memoizedState=[a,b];return a}function jh(a,b){var c=vb();b=void 0===b?null:b;var d=c.memoizedState;if(null!==d&&null!==b&&ve(b,d[1]))return d[0];a=a();c.memoizedState=[a,b];return a}function Ce(a,b,c){var d=Cc();Da(98>d?98:d,function(){a(!0)});Da(97<d?97:d,function(){var d= +X.suspense;X.suspense=void 0===b?null:b;try{a(!1),c()}finally{X.suspense=d}})}function ch(a,b,c){var d=ka(),e=Vb.suspense;d=Va(d,a,e);e={expirationTime:d,suspenseConfig:e,action:c,eagerReducer:null,eagerState:null,next:null};var f=b.pending;null===f?e.next=e:(e.next=f.next,f.next=e);b.pending=e;f=a.alternate;if(a===z||null!==f&&f===z)Uc=!0,e.expirationTime=Ia,z.expirationTime=Ia;else{if(0===a.expirationTime&&(null===f||0===f.expirationTime)&&(f=b.lastRenderedReducer,null!==f))try{var g=b.lastRenderedState, +h=f(g,c);e.eagerReducer=f;e.eagerState=h;if(Qa(h,g))return}catch(m){}finally{}Ja(a,d)}}function kh(a,b){var c=la(5,null,null,0);c.elementType="DELETED";c.type="DELETED";c.stateNode=b;c.return=a;c.effectTag=8;null!==a.lastEffect?(a.lastEffect.nextEffect=c,a.lastEffect=c):a.firstEffect=a.lastEffect=c}function lh(a,b){switch(a.tag){case 5:var c=a.type;b=1!==b.nodeType||c.toLowerCase()!==b.nodeName.toLowerCase()?null:b;return null!==b?(a.stateNode=b,!0):!1;case 6:return b=""===a.pendingProps||3!==b.nodeType? +null:b,null!==b?(a.stateNode=b,!0):!1;case 13:return!1;default:return!1}}function De(a){if(Wa){var b=Ka;if(b){var c=b;if(!lh(a,b)){b=kb(c.nextSibling);if(!b||!lh(a,b)){a.effectTag=a.effectTag&-1025|2;Wa=!1;ra=a;return}kh(ra,c)}ra=a;Ka=kb(b.firstChild)}else a.effectTag=a.effectTag&-1025|2,Wa=!1,ra=a}}function mh(a){for(a=a.return;null!==a&&5!==a.tag&&3!==a.tag&&13!==a.tag;)a=a.return;ra=a}function Zc(a){if(a!==ra)return!1;if(!Wa)return mh(a),Wa=!0,!1;var b=a.type;if(5!==a.tag||"head"!==b&&"body"!== +b&&!Yd(b,a.memoizedProps))for(b=Ka;b;)kh(a,b),b=kb(b.nextSibling);mh(a);if(13===a.tag){a=a.memoizedState;a=null!==a?a.dehydrated:null;if(!a)throw Error(k(317));a:{a=a.nextSibling;for(b=0;a;){if(8===a.nodeType){var c=a.data;if(c===og){if(0===b){Ka=kb(a.nextSibling);break a}b--}else c!==ng&&c!==Zd&&c!==$d||b++}a=a.nextSibling}Ka=null}}else Ka=ra?kb(a.stateNode.nextSibling):null;return!0}function Ee(){Ka=ra=null;Wa=!1}function T(a,b,c,d){b.child=null===a?Fe(b,null,c,d):wb(b,a.child,c,d)}function nh(a, +b,c,d,e){c=c.render;var f=b.ref;rb(b,e);d=we(a,b,c,d,f,e);if(null!==a&&!ia)return b.updateQueue=a.updateQueue,b.effectTag&=-517,a.expirationTime<=e&&(a.expirationTime=0),sa(a,b,e);b.effectTag|=1;T(a,b,d,e);return b.child}function oh(a,b,c,d,e,f){if(null===a){var g=c.type;if("function"===typeof g&&!Ge(g)&&void 0===g.defaultProps&&null===c.compare&&void 0===c.defaultProps)return b.tag=15,b.type=g,ph(a,b,g,d,e,f);a=Oc(c.type,null,d,null,b.mode,f);a.ref=b.ref;a.return=b;return b.child=a}g=a.child;if(e< +f&&(e=g.memoizedProps,c=c.compare,c=null!==c?c:Ob,c(e,d)&&a.ref===b.ref))return sa(a,b,f);b.effectTag|=1;a=Sa(g,d);a.ref=b.ref;a.return=b;return b.child=a}function ph(a,b,c,d,e,f){return null!==a&&Ob(a.memoizedProps,d)&&a.ref===b.ref&&(ia=!1,e<f)?(b.expirationTime=a.expirationTime,sa(a,b,f)):He(a,b,c,d,f)}function qh(a,b){var c=b.ref;if(null===a&&null!==c||null!==a&&a.ref!==c)b.effectTag|=128}function He(a,b,c,d,e){var f=N(c)?Ra:B.current;f=pb(b,f);rb(b,e);c=we(a,b,c,d,f,e);if(null!==a&&!ia)return b.updateQueue= +a.updateQueue,b.effectTag&=-517,a.expirationTime<=e&&(a.expirationTime=0),sa(a,b,e);b.effectTag|=1;T(a,b,c,e);return b.child}function rh(a,b,c,d,e){if(N(c)){var f=!0;Bc(b)}else f=!1;rb(b,e);if(null===b.stateNode)null!==a&&(a.alternate=null,b.alternate=null,b.effectTag|=2),Yg(b,c,d),pe(b,c,d,e),d=!0;else if(null===a){var g=b.stateNode,h=b.memoizedProps;g.props=h;var m=g.context,n=c.contextType;"object"===typeof n&&null!==n?n=W(n):(n=N(c)?Ra:B.current,n=pb(b,n));var l=c.getDerivedStateFromProps,k="function"=== +typeof l||"function"===typeof g.getSnapshotBeforeUpdate;k||"function"!==typeof g.UNSAFE_componentWillReceiveProps&&"function"!==typeof g.componentWillReceiveProps||(h!==d||m!==n)&&Zg(b,g,d,n);Ga=!1;var p=b.memoizedState;g.state=p;Qb(b,d,g,e);m=b.memoizedState;h!==d||p!==m||G.current||Ga?("function"===typeof l&&(Lc(b,c,l,d),m=b.memoizedState),(h=Ga||Xg(b,c,h,d,p,m,n))?(k||"function"!==typeof g.UNSAFE_componentWillMount&&"function"!==typeof g.componentWillMount||("function"===typeof g.componentWillMount&& +g.componentWillMount(),"function"===typeof g.UNSAFE_componentWillMount&&g.UNSAFE_componentWillMount()),"function"===typeof g.componentDidMount&&(b.effectTag|=4)):("function"===typeof g.componentDidMount&&(b.effectTag|=4),b.memoizedProps=d,b.memoizedState=m),g.props=d,g.state=m,g.context=n,d=h):("function"===typeof g.componentDidMount&&(b.effectTag|=4),d=!1)}else g=b.stateNode,oe(a,b),h=b.memoizedProps,g.props=b.type===b.elementType?h:aa(b.type,h),m=g.context,n=c.contextType,"object"===typeof n&&null!== +n?n=W(n):(n=N(c)?Ra:B.current,n=pb(b,n)),l=c.getDerivedStateFromProps,(k="function"===typeof l||"function"===typeof g.getSnapshotBeforeUpdate)||"function"!==typeof g.UNSAFE_componentWillReceiveProps&&"function"!==typeof g.componentWillReceiveProps||(h!==d||m!==n)&&Zg(b,g,d,n),Ga=!1,m=b.memoizedState,g.state=m,Qb(b,d,g,e),p=b.memoizedState,h!==d||m!==p||G.current||Ga?("function"===typeof l&&(Lc(b,c,l,d),p=b.memoizedState),(l=Ga||Xg(b,c,h,d,m,p,n))?(k||"function"!==typeof g.UNSAFE_componentWillUpdate&& +"function"!==typeof g.componentWillUpdate||("function"===typeof g.componentWillUpdate&&g.componentWillUpdate(d,p,n),"function"===typeof g.UNSAFE_componentWillUpdate&&g.UNSAFE_componentWillUpdate(d,p,n)),"function"===typeof g.componentDidUpdate&&(b.effectTag|=4),"function"===typeof g.getSnapshotBeforeUpdate&&(b.effectTag|=256)):("function"!==typeof g.componentDidUpdate||h===a.memoizedProps&&m===a.memoizedState||(b.effectTag|=4),"function"!==typeof g.getSnapshotBeforeUpdate||h===a.memoizedProps&&m=== +a.memoizedState||(b.effectTag|=256),b.memoizedProps=d,b.memoizedState=p),g.props=d,g.state=p,g.context=n,d=l):("function"!==typeof g.componentDidUpdate||h===a.memoizedProps&&m===a.memoizedState||(b.effectTag|=4),"function"!==typeof g.getSnapshotBeforeUpdate||h===a.memoizedProps&&m===a.memoizedState||(b.effectTag|=256),d=!1);return Ie(a,b,c,d,f,e)}function Ie(a,b,c,d,e,f){qh(a,b);var g=0!==(b.effectTag&64);if(!d&&!g)return e&&Hg(b,c,!1),sa(a,b,f);d=b.stateNode;gj.current=b;var h=g&&"function"!==typeof c.getDerivedStateFromError? +null:d.render();b.effectTag|=1;null!==a&&g?(b.child=wb(b,a.child,null,f),b.child=wb(b,null,h,f)):T(a,b,h,f);b.memoizedState=d.state;e&&Hg(b,c,!0);return b.child}function sh(a){var b=a.stateNode;b.pendingContext?Fg(a,b.pendingContext,b.pendingContext!==b.context):b.context&&Fg(a,b.context,!1);se(a,b.containerInfo)}function th(a,b,c){var d=b.mode,e=b.pendingProps,f=D.current,g=!1,h;(h=0!==(b.effectTag&64))||(h=0!==(f&2)&&(null===a||null!==a.memoizedState));h?(g=!0,b.effectTag&=-65):null!==a&&null=== +a.memoizedState||void 0===e.fallback||!0===e.unstable_avoidThisFallback||(f|=1);y(D,f&1);if(null===a){void 0!==e.fallback&&De(b);if(g){g=e.fallback;e=Ha(null,d,0,null);e.return=b;if(0===(b.mode&2))for(a=null!==b.memoizedState?b.child.child:b.child,e.child=a;null!==a;)a.return=e,a=a.sibling;c=Ha(g,d,c,null);c.return=b;e.sibling=c;b.memoizedState=Je;b.child=e;return c}d=e.children;b.memoizedState=null;return b.child=Fe(b,null,d,c)}if(null!==a.memoizedState){a=a.child;d=a.sibling;if(g){e=e.fallback; +c=Sa(a,a.pendingProps);c.return=b;if(0===(b.mode&2)&&(g=null!==b.memoizedState?b.child.child:b.child,g!==a.child))for(c.child=g;null!==g;)g.return=c,g=g.sibling;d=Sa(d,e);d.return=b;c.sibling=d;c.childExpirationTime=0;b.memoizedState=Je;b.child=c;return d}c=wb(b,a.child,e.children,c);b.memoizedState=null;return b.child=c}a=a.child;if(g){g=e.fallback;e=Ha(null,d,0,null);e.return=b;e.child=a;null!==a&&(a.return=e);if(0===(b.mode&2))for(a=null!==b.memoizedState?b.child.child:b.child,e.child=a;null!== +a;)a.return=e,a=a.sibling;c=Ha(g,d,c,null);c.return=b;e.sibling=c;c.effectTag|=2;e.childExpirationTime=0;b.memoizedState=Je;b.child=e;return c}b.memoizedState=null;return b.child=wb(b,a,e.children,c)}function uh(a,b){a.expirationTime<b&&(a.expirationTime=b);var c=a.alternate;null!==c&&c.expirationTime<b&&(c.expirationTime=b);Sg(a.return,b)}function Ke(a,b,c,d,e,f){var g=a.memoizedState;null===g?a.memoizedState={isBackwards:b,rendering:null,renderingStartTime:0,last:d,tail:c,tailExpiration:0,tailMode:e, +lastEffect:f}:(g.isBackwards=b,g.rendering=null,g.renderingStartTime=0,g.last=d,g.tail=c,g.tailExpiration=0,g.tailMode=e,g.lastEffect=f)}function vh(a,b,c){var d=b.pendingProps,e=d.revealOrder,f=d.tail;T(a,b,d.children,c);d=D.current;if(0!==(d&2))d=d&1|2,b.effectTag|=64;else{if(null!==a&&0!==(a.effectTag&64))a:for(a=b.child;null!==a;){if(13===a.tag)null!==a.memoizedState&&uh(a,c);else if(19===a.tag)uh(a,c);else if(null!==a.child){a.child.return=a;a=a.child;continue}if(a===b)break a;for(;null===a.sibling;){if(null=== +a.return||a.return===b)break a;a=a.return}a.sibling.return=a.return;a=a.sibling}d&=1}y(D,d);if(0===(b.mode&2))b.memoizedState=null;else switch(e){case "forwards":c=b.child;for(e=null;null!==c;)a=c.alternate,null!==a&&null===Rc(a)&&(e=c),c=c.sibling;c=e;null===c?(e=b.child,b.child=null):(e=c.sibling,c.sibling=null);Ke(b,!1,e,c,f,b.lastEffect);break;case "backwards":c=null;e=b.child;for(b.child=null;null!==e;){a=e.alternate;if(null!==a&&null===Rc(a)){b.child=e;break}a=e.sibling;e.sibling=c;c=e;e=a}Ke(b, +!0,c,null,f,b.lastEffect);break;case "together":Ke(b,!1,null,null,void 0,b.lastEffect);break;default:b.memoizedState=null}return b.child}function sa(a,b,c){null!==a&&(b.dependencies=a.dependencies);var d=b.expirationTime;0!==d&&Kc(d);if(b.childExpirationTime<c)return null;if(null!==a&&b.child!==a.child)throw Error(k(153));if(null!==b.child){a=b.child;c=Sa(a,a.pendingProps);b.child=c;for(c.return=b;null!==a.sibling;)a=a.sibling,c=c.sibling=Sa(a,a.pendingProps),c.return=b;c.sibling=null}return b.child} +function $c(a,b){switch(a.tailMode){case "hidden":b=a.tail;for(var c=null;null!==b;)null!==b.alternate&&(c=b),b=b.sibling;null===c?a.tail=null:c.sibling=null;break;case "collapsed":c=a.tail;for(var d=null;null!==c;)null!==c.alternate&&(d=c),c=c.sibling;null===d?b||null===a.tail?a.tail=null:a.tail.sibling=null:d.sibling=null}}function hj(a,b,c){var d=b.pendingProps;switch(b.tag){case 2:case 16:case 15:case 0:case 11:case 7:case 8:case 12:case 9:case 14:return null;case 1:return N(b.type)&&(q(G),q(B)), +null;case 3:return tb(),q(G),q(B),c=b.stateNode,c.pendingContext&&(c.context=c.pendingContext,c.pendingContext=null),null!==a&&null!==a.child||!Zc(b)||(b.effectTag|=4),wh(b),null;case 5:te(b);c=Ta(Tb.current);var e=b.type;if(null!==a&&null!=b.stateNode)ij(a,b,e,d,c),a.ref!==b.ref&&(b.effectTag|=128);else{if(!d){if(null===b.stateNode)throw Error(k(166));return null}a=Ta(ja.current);if(Zc(b)){d=b.stateNode;e=b.type;var f=b.memoizedProps;d[Aa]=b;d[vc]=f;switch(e){case "iframe":case "object":case "embed":w("load", +d);break;case "video":case "audio":for(a=0;a<Db.length;a++)w(Db[a],d);break;case "source":w("error",d);break;case "img":case "image":case "link":w("error",d);w("load",d);break;case "form":w("reset",d);w("submit",d);break;case "details":w("toggle",d);break;case "input":Hf(d,f);w("invalid",d);oa(c,"onChange");break;case "select":d._wrapperState={wasMultiple:!!f.multiple};w("invalid",d);oa(c,"onChange");break;case "textarea":Kf(d,f),w("invalid",d),oa(c,"onChange")}Ud(e,f);a=null;for(var g in f)if(f.hasOwnProperty(g)){var h= +f[g];"children"===g?"string"===typeof h?d.textContent!==h&&(a=["children",h]):"number"===typeof h&&d.textContent!==""+h&&(a=["children",""+h]):db.hasOwnProperty(g)&&null!=h&&oa(c,g)}switch(e){case "input":mc(d);Jf(d,f,!0);break;case "textarea":mc(d);Mf(d);break;case "select":case "option":break;default:"function"===typeof f.onClick&&(d.onclick=uc)}c=a;b.updateQueue=c;null!==c&&(b.effectTag|=4)}else{g=9===c.nodeType?c:c.ownerDocument;"http://www.w3.org/1999/xhtml"===a&&(a=Nf(e));"http://www.w3.org/1999/xhtml"=== +a?"script"===e?(a=g.createElement("div"),a.innerHTML="<script>\x3c/script>",a=a.removeChild(a.firstChild)):"string"===typeof d.is?a=g.createElement(e,{is:d.is}):(a=g.createElement(e),"select"===e&&(g=a,d.multiple?g.multiple=!0:d.size&&(g.size=d.size))):a=g.createElementNS(a,e);a[Aa]=b;a[vc]=d;jj(a,b,!1,!1);b.stateNode=a;g=Vd(e,d);switch(e){case "iframe":case "object":case "embed":w("load",a);h=d;break;case "video":case "audio":for(h=0;h<Db.length;h++)w(Db[h],a);h=d;break;case "source":w("error",a); +h=d;break;case "img":case "image":case "link":w("error",a);w("load",a);h=d;break;case "form":w("reset",a);w("submit",a);h=d;break;case "details":w("toggle",a);h=d;break;case "input":Hf(a,d);h=Cd(a,d);w("invalid",a);oa(c,"onChange");break;case "option":h=Fd(a,d);break;case "select":a._wrapperState={wasMultiple:!!d.multiple};h=M({},d,{value:void 0});w("invalid",a);oa(c,"onChange");break;case "textarea":Kf(a,d);h=Gd(a,d);w("invalid",a);oa(c,"onChange");break;default:h=d}Ud(e,h);var m=h;for(f in m)if(m.hasOwnProperty(f)){var n= +m[f];"style"===f?gg(a,n):"dangerouslySetInnerHTML"===f?(n=n?n.__html:void 0,null!=n&&xh(a,n)):"children"===f?"string"===typeof n?("textarea"!==e||""!==n)&&Wb(a,n):"number"===typeof n&&Wb(a,""+n):"suppressContentEditableWarning"!==f&&"suppressHydrationWarning"!==f&&"autoFocus"!==f&&(db.hasOwnProperty(f)?null!=n&&oa(c,f):null!=n&&xd(a,f,n,g))}switch(e){case "input":mc(a);Jf(a,d,!1);break;case "textarea":mc(a);Mf(a);break;case "option":null!=d.value&&a.setAttribute("value",""+va(d.value));break;case "select":a.multiple= +!!d.multiple;c=d.value;null!=c?hb(a,!!d.multiple,c,!1):null!=d.defaultValue&&hb(a,!!d.multiple,d.defaultValue,!0);break;default:"function"===typeof h.onClick&&(a.onclick=uc)}lg(e,d)&&(b.effectTag|=4)}null!==b.ref&&(b.effectTag|=128)}return null;case 6:if(a&&null!=b.stateNode)kj(a,b,a.memoizedProps,d);else{if("string"!==typeof d&&null===b.stateNode)throw Error(k(166));c=Ta(Tb.current);Ta(ja.current);Zc(b)?(c=b.stateNode,d=b.memoizedProps,c[Aa]=b,c.nodeValue!==d&&(b.effectTag|=4)):(c=(9===c.nodeType? +c:c.ownerDocument).createTextNode(d),c[Aa]=b,b.stateNode=c)}return null;case 13:q(D);d=b.memoizedState;if(0!==(b.effectTag&64))return b.expirationTime=c,b;c=null!==d;d=!1;null===a?void 0!==b.memoizedProps.fallback&&Zc(b):(e=a.memoizedState,d=null!==e,c||null===e||(e=a.child.sibling,null!==e&&(f=b.firstEffect,null!==f?(b.firstEffect=e,e.nextEffect=f):(b.firstEffect=b.lastEffect=e,e.nextEffect=null),e.effectTag=8)));if(c&&!d&&0!==(b.mode&2))if(null===a&&!0!==b.memoizedProps.unstable_avoidThisFallback|| +0!==(D.current&1))F===Xa&&(F=ad);else{if(F===Xa||F===ad)F=bd;0!==Xb&&null!==U&&(Ya(U,P),yh(U,Xb))}if(c||d)b.effectTag|=4;return null;case 4:return tb(),wh(b),null;case 10:return me(b),null;case 17:return N(b.type)&&(q(G),q(B)),null;case 19:q(D);d=b.memoizedState;if(null===d)return null;e=0!==(b.effectTag&64);f=d.rendering;if(null===f)if(e)$c(d,!1);else{if(F!==Xa||null!==a&&0!==(a.effectTag&64))for(f=b.child;null!==f;){a=Rc(f);if(null!==a){b.effectTag|=64;$c(d,!1);e=a.updateQueue;null!==e&&(b.updateQueue= +e,b.effectTag|=4);null===d.lastEffect&&(b.firstEffect=null);b.lastEffect=d.lastEffect;for(d=b.child;null!==d;)e=d,f=c,e.effectTag&=2,e.nextEffect=null,e.firstEffect=null,e.lastEffect=null,a=e.alternate,null===a?(e.childExpirationTime=0,e.expirationTime=f,e.child=null,e.memoizedProps=null,e.memoizedState=null,e.updateQueue=null,e.dependencies=null):(e.childExpirationTime=a.childExpirationTime,e.expirationTime=a.expirationTime,e.child=a.child,e.memoizedProps=a.memoizedProps,e.memoizedState=a.memoizedState, +e.updateQueue=a.updateQueue,f=a.dependencies,e.dependencies=null===f?null:{expirationTime:f.expirationTime,firstContext:f.firstContext,responders:f.responders}),d=d.sibling;y(D,D.current&1|2);return b.child}f=f.sibling}}else{if(!e)if(a=Rc(f),null!==a){if(b.effectTag|=64,e=!0,c=a.updateQueue,null!==c&&(b.updateQueue=c,b.effectTag|=4),$c(d,!0),null===d.tail&&"hidden"===d.tailMode&&!f.alternate)return b=b.lastEffect=d.lastEffect,null!==b&&(b.nextEffect=null),null}else 2*Y()-d.renderingStartTime>d.tailExpiration&& +1<c&&(b.effectTag|=64,e=!0,$c(d,!1),b.expirationTime=b.childExpirationTime=c-1);d.isBackwards?(f.sibling=b.child,b.child=f):(c=d.last,null!==c?c.sibling=f:b.child=f,d.last=f)}return null!==d.tail?(0===d.tailExpiration&&(d.tailExpiration=Y()+500),c=d.tail,d.rendering=c,d.tail=c.sibling,d.lastEffect=b.lastEffect,d.renderingStartTime=Y(),c.sibling=null,b=D.current,y(D,e?b&1|2:b&1),c):null}throw Error(k(156,b.tag));}function lj(a,b){switch(a.tag){case 1:return N(a.type)&&(q(G),q(B)),b=a.effectTag,b&4096? +(a.effectTag=b&-4097|64,a):null;case 3:tb();q(G);q(B);b=a.effectTag;if(0!==(b&64))throw Error(k(285));a.effectTag=b&-4097|64;return a;case 5:return te(a),null;case 13:return q(D),b=a.effectTag,b&4096?(a.effectTag=b&-4097|64,a):null;case 19:return q(D),null;case 4:return tb(),null;case 10:return me(a),null;default:return null}}function Le(a,b){return{value:a,source:b,stack:Bd(b)}}function Me(a,b){var c=b.source,d=b.stack;null===d&&null!==c&&(d=Bd(c));null!==c&&na(c.type);b=b.value;null!==a&&1===a.tag&& +na(a.type);try{console.error(b)}catch(e){setTimeout(function(){throw e;})}}function mj(a,b){try{b.props=a.memoizedProps,b.state=a.memoizedState,b.componentWillUnmount()}catch(c){Za(a,c)}}function zh(a){var b=a.ref;if(null!==b)if("function"===typeof b)try{b(null)}catch(c){Za(a,c)}else b.current=null}function nj(a,b){switch(b.tag){case 0:case 11:case 15:case 22:return;case 1:if(b.effectTag&256&&null!==a){var c=a.memoizedProps,d=a.memoizedState;a=b.stateNode;b=a.getSnapshotBeforeUpdate(b.elementType=== +b.type?c:aa(b.type,c),d);a.__reactInternalSnapshotBeforeUpdate=b}return;case 3:case 5:case 6:case 4:case 17:return}throw Error(k(163));}function Ah(a,b){b=b.updateQueue;b=null!==b?b.lastEffect:null;if(null!==b){var c=b=b.next;do{if((c.tag&a)===a){var d=c.destroy;c.destroy=void 0;void 0!==d&&d()}c=c.next}while(c!==b)}}function Bh(a,b){b=b.updateQueue;b=null!==b?b.lastEffect:null;if(null!==b){var c=b=b.next;do{if((c.tag&a)===a){var d=c.create;c.destroy=d()}c=c.next}while(c!==b)}}function oj(a,b,c,d){switch(c.tag){case 0:case 11:case 15:case 22:Bh(3, +c);return;case 1:a=c.stateNode;c.effectTag&4&&(null===b?a.componentDidMount():(d=c.elementType===c.type?b.memoizedProps:aa(c.type,b.memoizedProps),a.componentDidUpdate(d,b.memoizedState,a.__reactInternalSnapshotBeforeUpdate)));b=c.updateQueue;null!==b&&Wg(c,b,a);return;case 3:b=c.updateQueue;if(null!==b){a=null;if(null!==c.child)switch(c.child.tag){case 5:a=c.child.stateNode;break;case 1:a=c.child.stateNode}Wg(c,b,a)}return;case 5:a=c.stateNode;null===b&&c.effectTag&4&&lg(c.type,c.memoizedProps)&& +a.focus();return;case 6:return;case 4:return;case 12:return;case 13:null===c.memoizedState&&(c=c.alternate,null!==c&&(c=c.memoizedState,null!==c&&(c=c.dehydrated,null!==c&&bg(c))));return;case 19:case 17:case 20:case 21:return}throw Error(k(163));}function Ch(a,b,c){"function"===typeof Ne&&Ne(b);switch(b.tag){case 0:case 11:case 14:case 15:case 22:a=b.updateQueue;if(null!==a&&(a=a.lastEffect,null!==a)){var d=a.next;Da(97<c?97:c,function(){var a=d;do{var c=a.destroy;if(void 0!==c){var g=b;try{c()}catch(h){Za(g, +h)}}a=a.next}while(a!==d)})}break;case 1:zh(b);c=b.stateNode;"function"===typeof c.componentWillUnmount&&mj(b,c);break;case 5:zh(b);break;case 4:Dh(a,b,c)}}function Eh(a){var b=a.alternate;a.return=null;a.child=null;a.memoizedState=null;a.updateQueue=null;a.dependencies=null;a.alternate=null;a.firstEffect=null;a.lastEffect=null;a.pendingProps=null;a.memoizedProps=null;a.stateNode=null;null!==b&&Eh(b)}function Fh(a){return 5===a.tag||3===a.tag||4===a.tag}function Gh(a){a:{for(var b=a.return;null!== +b;){if(Fh(b)){var c=b;break a}b=b.return}throw Error(k(160));}b=c.stateNode;switch(c.tag){case 5:var d=!1;break;case 3:b=b.containerInfo;d=!0;break;case 4:b=b.containerInfo;d=!0;break;default:throw Error(k(161));}c.effectTag&16&&(Wb(b,""),c.effectTag&=-17);a:b:for(c=a;;){for(;null===c.sibling;){if(null===c.return||Fh(c.return)){c=null;break a}c=c.return}c.sibling.return=c.return;for(c=c.sibling;5!==c.tag&&6!==c.tag&&18!==c.tag;){if(c.effectTag&2)continue b;if(null===c.child||4===c.tag)continue b; +else c.child.return=c,c=c.child}if(!(c.effectTag&2)){c=c.stateNode;break a}}d?Oe(a,c,b):Pe(a,c,b)}function Oe(a,b,c){var d=a.tag,e=5===d||6===d;if(e)a=e?a.stateNode:a.stateNode.instance,b?8===c.nodeType?c.parentNode.insertBefore(a,b):c.insertBefore(a,b):(8===c.nodeType?(b=c.parentNode,b.insertBefore(a,c)):(b=c,b.appendChild(a)),c=c._reactRootContainer,null!==c&&void 0!==c||null!==b.onclick||(b.onclick=uc));else if(4!==d&&(a=a.child,null!==a))for(Oe(a,b,c),a=a.sibling;null!==a;)Oe(a,b,c),a=a.sibling} +function Pe(a,b,c){var d=a.tag,e=5===d||6===d;if(e)a=e?a.stateNode:a.stateNode.instance,b?c.insertBefore(a,b):c.appendChild(a);else if(4!==d&&(a=a.child,null!==a))for(Pe(a,b,c),a=a.sibling;null!==a;)Pe(a,b,c),a=a.sibling}function Dh(a,b,c){for(var d=b,e=!1,f,g;;){if(!e){e=d.return;a:for(;;){if(null===e)throw Error(k(160));f=e.stateNode;switch(e.tag){case 5:g=!1;break a;case 3:f=f.containerInfo;g=!0;break a;case 4:f=f.containerInfo;g=!0;break a}e=e.return}e=!0}if(5===d.tag||6===d.tag){a:for(var h= +a,m=d,n=c,l=m;;)if(Ch(h,l,n),null!==l.child&&4!==l.tag)l.child.return=l,l=l.child;else{if(l===m)break a;for(;null===l.sibling;){if(null===l.return||l.return===m)break a;l=l.return}l.sibling.return=l.return;l=l.sibling}g?(h=f,m=d.stateNode,8===h.nodeType?h.parentNode.removeChild(m):h.removeChild(m)):f.removeChild(d.stateNode)}else if(4===d.tag){if(null!==d.child){f=d.stateNode.containerInfo;g=!0;d.child.return=d;d=d.child;continue}}else if(Ch(a,d,c),null!==d.child){d.child.return=d;d=d.child;continue}if(d=== +b)break;for(;null===d.sibling;){if(null===d.return||d.return===b)return;d=d.return;4===d.tag&&(e=!1)}d.sibling.return=d.return;d=d.sibling}}function Qe(a,b){switch(b.tag){case 0:case 11:case 14:case 15:case 22:Ah(3,b);return;case 1:return;case 5:var c=b.stateNode;if(null!=c){var d=b.memoizedProps,e=null!==a?a.memoizedProps:d;a=b.type;var f=b.updateQueue;b.updateQueue=null;if(null!==f){c[vc]=d;"input"===a&&"radio"===d.type&&null!=d.name&&If(c,d);Vd(a,e);b=Vd(a,d);for(e=0;e<f.length;e+=2){var g=f[e], +h=f[e+1];"style"===g?gg(c,h):"dangerouslySetInnerHTML"===g?xh(c,h):"children"===g?Wb(c,h):xd(c,g,h,b)}switch(a){case "input":Dd(c,d);break;case "textarea":Lf(c,d);break;case "select":b=c._wrapperState.wasMultiple,c._wrapperState.wasMultiple=!!d.multiple,a=d.value,null!=a?hb(c,!!d.multiple,a,!1):b!==!!d.multiple&&(null!=d.defaultValue?hb(c,!!d.multiple,d.defaultValue,!0):hb(c,!!d.multiple,d.multiple?[]:"",!1))}}}return;case 6:if(null===b.stateNode)throw Error(k(162));b.stateNode.nodeValue=b.memoizedProps; +return;case 3:b=b.stateNode;b.hydrate&&(b.hydrate=!1,bg(b.containerInfo));return;case 12:return;case 13:c=b;null===b.memoizedState?d=!1:(d=!0,c=b.child,Re=Y());if(null!==c)a:for(a=c;;){if(5===a.tag)f=a.stateNode,d?(f=f.style,"function"===typeof f.setProperty?f.setProperty("display","none","important"):f.display="none"):(f=a.stateNode,e=a.memoizedProps.style,e=void 0!==e&&null!==e&&e.hasOwnProperty("display")?e.display:null,f.style.display=fg("display",e));else if(6===a.tag)a.stateNode.nodeValue=d? +"":a.memoizedProps;else if(13===a.tag&&null!==a.memoizedState&&null===a.memoizedState.dehydrated){f=a.child.sibling;f.return=a;a=f;continue}else if(null!==a.child){a.child.return=a;a=a.child;continue}if(a===c)break;for(;null===a.sibling;){if(null===a.return||a.return===c)break a;a=a.return}a.sibling.return=a.return;a=a.sibling}Hh(b);return;case 19:Hh(b);return;case 17:return}throw Error(k(163));}function Hh(a){var b=a.updateQueue;if(null!==b){a.updateQueue=null;var c=a.stateNode;null===c&&(c=a.stateNode= +new pj);b.forEach(function(b){var d=qj.bind(null,a,b);c.has(b)||(c.add(b),b.then(d,d))})}}function Ih(a,b,c){c=Ea(c,null);c.tag=3;c.payload={element:null};var d=b.value;c.callback=function(){cd||(cd=!0,Se=d);Me(a,b)};return c}function Jh(a,b,c){c=Ea(c,null);c.tag=3;var d=a.type.getDerivedStateFromError;if("function"===typeof d){var e=b.value;c.payload=function(){Me(a,b);return d(e)}}var f=a.stateNode;null!==f&&"function"===typeof f.componentDidCatch&&(c.callback=function(){"function"!==typeof d&& +(null===La?La=new Set([this]):La.add(this),Me(a,b));var c=b.stack;this.componentDidCatch(b.value,{componentStack:null!==c?c:""})});return c}function ka(){return(p&(ca|ma))!==H?1073741821-(Y()/10|0):0!==dd?dd:dd=1073741821-(Y()/10|0)}function Va(a,b,c){b=b.mode;if(0===(b&2))return 1073741823;var d=Cc();if(0===(b&4))return 99===d?1073741823:1073741822;if((p&ca)!==H)return P;if(null!==c)a=Fc(a,c.timeoutMs|0||5E3,250);else switch(d){case 99:a=1073741823;break;case 98:a=Fc(a,150,100);break;case 97:case 96:a= +Fc(a,5E3,250);break;case 95:a=2;break;default:throw Error(k(326));}null!==U&&a===P&&--a;return a}function ed(a,b){a.expirationTime<b&&(a.expirationTime=b);var c=a.alternate;null!==c&&c.expirationTime<b&&(c.expirationTime=b);var d=a.return,e=null;if(null===d&&3===a.tag)e=a.stateNode;else for(;null!==d;){c=d.alternate;d.childExpirationTime<b&&(d.childExpirationTime=b);null!==c&&c.childExpirationTime<b&&(c.childExpirationTime=b);if(null===d.return&&3===d.tag){e=d.stateNode;break}d=d.return}null!==e&& +(U===e&&(Kc(b),F===bd&&Ya(e,P)),yh(e,b));return e}function fd(a){var b=a.lastExpiredTime;if(0!==b)return b;b=a.firstPendingTime;if(!Kh(a,b))return b;var c=a.lastPingedTime;a=a.nextKnownPendingLevel;a=c>a?c:a;return 2>=a&&b!==a?0:a}function V(a){if(0!==a.lastExpiredTime)a.callbackExpirationTime=1073741823,a.callbackPriority=99,a.callbackNode=Og(Te.bind(null,a));else{var b=fd(a),c=a.callbackNode;if(0===b)null!==c&&(a.callbackNode=null,a.callbackExpirationTime=0,a.callbackPriority=90);else{var d=ka(); +1073741823===b?d=99:1===b||2===b?d=95:(d=10*(1073741821-b)-10*(1073741821-d),d=0>=d?99:250>=d?98:5250>=d?97:95);if(null!==c){var e=a.callbackPriority;if(a.callbackExpirationTime===b&&e>=d)return;c!==Qg&&Rg(c)}a.callbackExpirationTime=b;a.callbackPriority=d;b=1073741823===b?Og(Te.bind(null,a)):Ng(d,Lh.bind(null,a),{timeout:10*(1073741821-b)-Y()});a.callbackNode=b}}}function Lh(a,b){dd=0;if(b)return b=ka(),Ue(a,b),V(a),null;var c=fd(a);if(0!==c){b=a.callbackNode;if((p&(ca|ma))!==H)throw Error(k(327)); +xb();a===U&&c===P||$a(a,c);if(null!==t){var d=p;p|=ca;var e=Mh();do try{rj();break}catch(h){Nh(a,h)}while(1);le();p=d;gd.current=e;if(F===hd)throw b=id,$a(a,c),Ya(a,c),V(a),b;if(null===t)switch(e=a.finishedWork=a.current.alternate,a.finishedExpirationTime=c,d=F,U=null,d){case Xa:case hd:throw Error(k(345));case Oh:Ue(a,2<c?2:c);break;case ad:Ya(a,c);d=a.lastSuspendedTime;c===d&&(a.nextKnownPendingLevel=Ve(e));if(1073741823===ta&&(e=Re+Ph-Y(),10<e)){if(jd){var f=a.lastPingedTime;if(0===f||f>=c){a.lastPingedTime= +c;$a(a,c);break}}f=fd(a);if(0!==f&&f!==c)break;if(0!==d&&d!==c){a.lastPingedTime=d;break}a.timeoutHandle=We(ab.bind(null,a),e);break}ab(a);break;case bd:Ya(a,c);d=a.lastSuspendedTime;c===d&&(a.nextKnownPendingLevel=Ve(e));if(jd&&(e=a.lastPingedTime,0===e||e>=c)){a.lastPingedTime=c;$a(a,c);break}e=fd(a);if(0!==e&&e!==c)break;if(0!==d&&d!==c){a.lastPingedTime=d;break}1073741823!==Yb?d=10*(1073741821-Yb)-Y():1073741823===ta?d=0:(d=10*(1073741821-ta)-5E3,e=Y(),c=10*(1073741821-c)-e,d=e-d,0>d&&(d=0),d= +(120>d?120:480>d?480:1080>d?1080:1920>d?1920:3E3>d?3E3:4320>d?4320:1960*sj(d/1960))-d,c<d&&(d=c));if(10<d){a.timeoutHandle=We(ab.bind(null,a),d);break}ab(a);break;case Xe:if(1073741823!==ta&&null!==kd){f=ta;var g=kd;d=g.busyMinDurationMs|0;0>=d?d=0:(e=g.busyDelayMs|0,f=Y()-(10*(1073741821-f)-(g.timeoutMs|0||5E3)),d=f<=e?0:e+d-f);if(10<d){Ya(a,c);a.timeoutHandle=We(ab.bind(null,a),d);break}}ab(a);break;default:throw Error(k(329));}V(a);if(a.callbackNode===b)return Lh.bind(null,a)}}return null}function Te(a){var b= +a.lastExpiredTime;b=0!==b?b:1073741823;if((p&(ca|ma))!==H)throw Error(k(327));xb();a===U&&b===P||$a(a,b);if(null!==t){var c=p;p|=ca;var d=Mh();do try{tj();break}catch(e){Nh(a,e)}while(1);le();p=c;gd.current=d;if(F===hd)throw c=id,$a(a,b),Ya(a,b),V(a),c;if(null!==t)throw Error(k(261));a.finishedWork=a.current.alternate;a.finishedExpirationTime=b;U=null;ab(a);V(a)}return null}function uj(){if(null!==bb){var a=bb;bb=null;a.forEach(function(a,c){Ue(c,a);V(c)});ha()}}function Qh(a,b){var c=p;p|=1;try{return a(b)}finally{p= +c,p===H&&ha()}}function Rh(a,b){var c=p;p&=-2;p|=Ye;try{return a(b)}finally{p=c,p===H&&ha()}}function $a(a,b){a.finishedWork=null;a.finishedExpirationTime=0;var c=a.timeoutHandle;-1!==c&&(a.timeoutHandle=-1,vj(c));if(null!==t)for(c=t.return;null!==c;){var d=c;switch(d.tag){case 1:d=d.type.childContextTypes;null!==d&&void 0!==d&&(q(G),q(B));break;case 3:tb();q(G);q(B);break;case 5:te(d);break;case 4:tb();break;case 13:q(D);break;case 19:q(D);break;case 10:me(d)}c=c.return}U=a;t=Sa(a.current,null); +P=b;F=Xa;id=null;Yb=ta=1073741823;kd=null;Xb=0;jd=!1}function Nh(a,b){do{try{le();Sc.current=Tc;if(Uc)for(var c=z.memoizedState;null!==c;){var d=c.queue;null!==d&&(d.pending=null);c=c.next}Ia=0;J=K=z=null;Uc=!1;if(null===t||null===t.return)return F=hd,id=b,t=null;a:{var e=a,f=t.return,g=t,h=b;b=P;g.effectTag|=2048;g.firstEffect=g.lastEffect=null;if(null!==h&&"object"===typeof h&&"function"===typeof h.then){var m=h;if(0===(g.mode&2)){var n=g.alternate;n?(g.updateQueue=n.updateQueue,g.memoizedState= +n.memoizedState,g.expirationTime=n.expirationTime):(g.updateQueue=null,g.memoizedState=null)}var l=0!==(D.current&1),k=f;do{var p;if(p=13===k.tag){var q=k.memoizedState;if(null!==q)p=null!==q.dehydrated?!0:!1;else{var w=k.memoizedProps;p=void 0===w.fallback?!1:!0!==w.unstable_avoidThisFallback?!0:l?!1:!0}}if(p){var y=k.updateQueue;if(null===y){var r=new Set;r.add(m);k.updateQueue=r}else y.add(m);if(0===(k.mode&2)){k.effectTag|=64;g.effectTag&=-2981;if(1===g.tag)if(null===g.alternate)g.tag=17;else{var O= +Ea(1073741823,null);O.tag=Jc;Fa(g,O)}g.expirationTime=1073741823;break a}h=void 0;g=b;var v=e.pingCache;null===v?(v=e.pingCache=new wj,h=new Set,v.set(m,h)):(h=v.get(m),void 0===h&&(h=new Set,v.set(m,h)));if(!h.has(g)){h.add(g);var x=xj.bind(null,e,m,g);m.then(x,x)}k.effectTag|=4096;k.expirationTime=b;break a}k=k.return}while(null!==k);h=Error((na(g.type)||"A React component")+" suspended while rendering, but no fallback UI was specified.\n\nAdd a <Suspense fallback=...> component higher in the tree to provide a loading indicator or placeholder to display."+ +Bd(g))}F!==Xe&&(F=Oh);h=Le(h,g);k=f;do{switch(k.tag){case 3:m=h;k.effectTag|=4096;k.expirationTime=b;var A=Ih(k,m,b);Ug(k,A);break a;case 1:m=h;var u=k.type,B=k.stateNode;if(0===(k.effectTag&64)&&("function"===typeof u.getDerivedStateFromError||null!==B&&"function"===typeof B.componentDidCatch&&(null===La||!La.has(B)))){k.effectTag|=4096;k.expirationTime=b;var H=Jh(k,m,b);Ug(k,H);break a}}k=k.return}while(null!==k)}t=Sh(t)}catch(cj){b=cj;continue}break}while(1)}function Mh(a){a=gd.current;gd.current= +Tc;return null===a?Tc:a}function Vg(a,b){a<ta&&2<a&&(ta=a);null!==b&&a<Yb&&2<a&&(Yb=a,kd=b)}function Kc(a){a>Xb&&(Xb=a)}function tj(){for(;null!==t;)t=Th(t)}function rj(){for(;null!==t&&!yj();)t=Th(t)}function Th(a){var b=zj(a.alternate,a,P);a.memoizedProps=a.pendingProps;null===b&&(b=Sh(a));Uh.current=null;return b}function Sh(a){t=a;do{var b=t.alternate;a=t.return;if(0===(t.effectTag&2048)){b=hj(b,t,P);if(1===P||1!==t.childExpirationTime){for(var c=0,d=t.child;null!==d;){var e=d.expirationTime, +f=d.childExpirationTime;e>c&&(c=e);f>c&&(c=f);d=d.sibling}t.childExpirationTime=c}if(null!==b)return b;null!==a&&0===(a.effectTag&2048)&&(null===a.firstEffect&&(a.firstEffect=t.firstEffect),null!==t.lastEffect&&(null!==a.lastEffect&&(a.lastEffect.nextEffect=t.firstEffect),a.lastEffect=t.lastEffect),1<t.effectTag&&(null!==a.lastEffect?a.lastEffect.nextEffect=t:a.firstEffect=t,a.lastEffect=t))}else{b=lj(t);if(null!==b)return b.effectTag&=2047,b;null!==a&&(a.firstEffect=a.lastEffect=null,a.effectTag|= +2048)}b=t.sibling;if(null!==b)return b;t=a}while(null!==t);F===Xa&&(F=Xe);return null}function Ve(a){var b=a.expirationTime;a=a.childExpirationTime;return b>a?b:a}function ab(a){var b=Cc();Da(99,Aj.bind(null,a,b));return null}function Aj(a,b){do xb();while(null!==Zb);if((p&(ca|ma))!==H)throw Error(k(327));var c=a.finishedWork,d=a.finishedExpirationTime;if(null===c)return null;a.finishedWork=null;a.finishedExpirationTime=0;if(c===a.current)throw Error(k(177));a.callbackNode=null;a.callbackExpirationTime= +0;a.callbackPriority=90;a.nextKnownPendingLevel=0;var e=Ve(c);a.firstPendingTime=e;d<=a.lastSuspendedTime?a.firstSuspendedTime=a.lastSuspendedTime=a.nextKnownPendingLevel=0:d<=a.firstSuspendedTime&&(a.firstSuspendedTime=d-1);d<=a.lastPingedTime&&(a.lastPingedTime=0);d<=a.lastExpiredTime&&(a.lastExpiredTime=0);a===U&&(t=U=null,P=0);1<c.effectTag?null!==c.lastEffect?(c.lastEffect.nextEffect=c,e=c.firstEffect):e=c:e=c.firstEffect;if(null!==e){var f=p;p|=ma;Uh.current=null;Ze=tc;var g=kg();if(Xd(g)){if("selectionStart"in +g)var h={start:g.selectionStart,end:g.selectionEnd};else a:{h=(h=g.ownerDocument)&&h.defaultView||window;var m=h.getSelection&&h.getSelection();if(m&&0!==m.rangeCount){h=m.anchorNode;var n=m.anchorOffset,q=m.focusNode;m=m.focusOffset;try{h.nodeType,q.nodeType}catch(sb){h=null;break a}var ba=0,w=-1,y=-1,B=0,D=0,r=g,z=null;b:for(;;){for(var v;;){r!==h||0!==n&&3!==r.nodeType||(w=ba+n);r!==q||0!==m&&3!==r.nodeType||(y=ba+m);3===r.nodeType&&(ba+=r.nodeValue.length);if(null===(v=r.firstChild))break;z=r; +r=v}for(;;){if(r===g)break b;z===h&&++B===n&&(w=ba);z===q&&++D===m&&(y=ba);if(null!==(v=r.nextSibling))break;r=z;z=r.parentNode}r=v}h=-1===w||-1===y?null:{start:w,end:y}}else h=null}h=h||{start:0,end:0}}else h=null;$e={activeElementDetached:null,focusedElem:g,selectionRange:h};tc=!1;l=e;do try{Bj()}catch(sb){if(null===l)throw Error(k(330));Za(l,sb);l=l.nextEffect}while(null!==l);l=e;do try{for(g=a,h=b;null!==l;){var x=l.effectTag;x&16&&Wb(l.stateNode,"");if(x&128){var A=l.alternate;if(null!==A){var u= +A.ref;null!==u&&("function"===typeof u?u(null):u.current=null)}}switch(x&1038){case 2:Gh(l);l.effectTag&=-3;break;case 6:Gh(l);l.effectTag&=-3;Qe(l.alternate,l);break;case 1024:l.effectTag&=-1025;break;case 1028:l.effectTag&=-1025;Qe(l.alternate,l);break;case 4:Qe(l.alternate,l);break;case 8:n=l,Dh(g,n,h),Eh(n)}l=l.nextEffect}}catch(sb){if(null===l)throw Error(k(330));Za(l,sb);l=l.nextEffect}while(null!==l);u=$e;A=kg();x=u.focusedElem;h=u.selectionRange;if(A!==x&&x&&x.ownerDocument&&jg(x.ownerDocument.documentElement, +x)){null!==h&&Xd(x)&&(A=h.start,u=h.end,void 0===u&&(u=A),"selectionStart"in x?(x.selectionStart=A,x.selectionEnd=Math.min(u,x.value.length)):(u=(A=x.ownerDocument||document)&&A.defaultView||window,u.getSelection&&(u=u.getSelection(),n=x.textContent.length,g=Math.min(h.start,n),h=void 0===h.end?g:Math.min(h.end,n),!u.extend&&g>h&&(n=h,h=g,g=n),n=ig(x,g),q=ig(x,h),n&&q&&(1!==u.rangeCount||u.anchorNode!==n.node||u.anchorOffset!==n.offset||u.focusNode!==q.node||u.focusOffset!==q.offset)&&(A=A.createRange(), +A.setStart(n.node,n.offset),u.removeAllRanges(),g>h?(u.addRange(A),u.extend(q.node,q.offset)):(A.setEnd(q.node,q.offset),u.addRange(A))))));A=[];for(u=x;u=u.parentNode;)1===u.nodeType&&A.push({element:u,left:u.scrollLeft,top:u.scrollTop});"function"===typeof x.focus&&x.focus();for(x=0;x<A.length;x++)u=A[x],u.element.scrollLeft=u.left,u.element.scrollTop=u.top}tc=!!Ze;$e=Ze=null;a.current=c;l=e;do try{for(x=a;null!==l;){var F=l.effectTag;F&36&&oj(x,l.alternate,l);if(F&128){A=void 0;var E=l.ref;if(null!== +E){var G=l.stateNode;switch(l.tag){case 5:A=G;break;default:A=G}"function"===typeof E?E(A):E.current=A}}l=l.nextEffect}}catch(sb){if(null===l)throw Error(k(330));Za(l,sb);l=l.nextEffect}while(null!==l);l=null;Cj();p=f}else a.current=c;if(ld)ld=!1,Zb=a,$b=b;else for(l=e;null!==l;)b=l.nextEffect,l.nextEffect=null,l=b;b=a.firstPendingTime;0===b&&(La=null);1073741823===b?a===af?ac++:(ac=0,af=a):ac=0;"function"===typeof bf&&bf(c.stateNode,d);V(a);if(cd)throw cd=!1,a=Se,Se=null,a;if((p&Ye)!==H)return null; +ha();return null}function Bj(){for(;null!==l;){var a=l.effectTag;0!==(a&256)&&nj(l.alternate,l);0===(a&512)||ld||(ld=!0,Ng(97,function(){xb();return null}));l=l.nextEffect}}function xb(){if(90!==$b){var a=97<$b?97:$b;$b=90;return Da(a,Dj)}}function Dj(){if(null===Zb)return!1;var a=Zb;Zb=null;if((p&(ca|ma))!==H)throw Error(k(331));var b=p;p|=ma;for(a=a.current.firstEffect;null!==a;){try{var c=a;if(0!==(c.effectTag&512))switch(c.tag){case 0:case 11:case 15:case 22:Ah(5,c),Bh(5,c)}}catch(d){if(null=== +a)throw Error(k(330));Za(a,d)}c=a.nextEffect;a.nextEffect=null;a=c}p=b;ha();return!0}function Vh(a,b,c){b=Le(c,b);b=Ih(a,b,1073741823);Fa(a,b);a=ed(a,1073741823);null!==a&&V(a)}function Za(a,b){if(3===a.tag)Vh(a,a,b);else for(var c=a.return;null!==c;){if(3===c.tag){Vh(c,a,b);break}else if(1===c.tag){var d=c.stateNode;if("function"===typeof c.type.getDerivedStateFromError||"function"===typeof d.componentDidCatch&&(null===La||!La.has(d))){a=Le(b,a);a=Jh(c,a,1073741823);Fa(c,a);c=ed(c,1073741823);null!== +c&&V(c);break}}c=c.return}}function xj(a,b,c){var d=a.pingCache;null!==d&&d.delete(b);U===a&&P===c?F===bd||F===ad&&1073741823===ta&&Y()-Re<Ph?$a(a,P):jd=!0:Kh(a,c)&&(b=a.lastPingedTime,0!==b&&b<c||(a.lastPingedTime=c,V(a)))}function qj(a,b){var c=a.stateNode;null!==c&&c.delete(b);b=0;0===b&&(b=ka(),b=Va(b,a,null));a=ed(a,b);null!==a&&V(a)}function Ej(a){if("undefined"===typeof __REACT_DEVTOOLS_GLOBAL_HOOK__)return!1;var b=__REACT_DEVTOOLS_GLOBAL_HOOK__;if(b.isDisabled||!b.supportsFiber)return!0;try{var c= +b.inject(a);bf=function(a,e){try{b.onCommitFiberRoot(c,a,void 0,64===(a.current.effectTag&64))}catch(f){}};Ne=function(a){try{b.onCommitFiberUnmount(c,a)}catch(e){}}}catch(d){}return!0}function Fj(a,b,c,d){this.tag=a;this.key=c;this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null;this.index=0;this.ref=null;this.pendingProps=b;this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null;this.mode=d;this.effectTag=0;this.lastEffect=this.firstEffect=this.nextEffect= +null;this.childExpirationTime=this.expirationTime=0;this.alternate=null}function Ge(a){a=a.prototype;return!(!a||!a.isReactComponent)}function Gj(a){if("function"===typeof a)return Ge(a)?1:0;if(void 0!==a&&null!==a){a=a.$$typeof;if(a===zd)return 11;if(a===Ad)return 14}return 2}function Sa(a,b){var c=a.alternate;null===c?(c=la(a.tag,b,a.key,a.mode),c.elementType=a.elementType,c.type=a.type,c.stateNode=a.stateNode,c.alternate=a,a.alternate=c):(c.pendingProps=b,c.effectTag=0,c.nextEffect=null,c.firstEffect= +null,c.lastEffect=null);c.childExpirationTime=a.childExpirationTime;c.expirationTime=a.expirationTime;c.child=a.child;c.memoizedProps=a.memoizedProps;c.memoizedState=a.memoizedState;c.updateQueue=a.updateQueue;b=a.dependencies;c.dependencies=null===b?null:{expirationTime:b.expirationTime,firstContext:b.firstContext,responders:b.responders};c.sibling=a.sibling;c.index=a.index;c.ref=a.ref;return c}function Oc(a,b,c,d,e,f){var g=2;d=a;if("function"===typeof a)Ge(a)&&(g=1);else if("string"===typeof a)g= +5;else a:switch(a){case Ma:return Ha(c.children,e,f,b);case Hj:g=8;e|=7;break;case Af:g=8;e|=1;break;case kc:return a=la(12,c,b,e|8),a.elementType=kc,a.type=kc,a.expirationTime=f,a;case lc:return a=la(13,c,b,e),a.type=lc,a.elementType=lc,a.expirationTime=f,a;case yd:return a=la(19,c,b,e),a.elementType=yd,a.expirationTime=f,a;default:if("object"===typeof a&&null!==a)switch(a.$$typeof){case Cf:g=10;break a;case Bf:g=9;break a;case zd:g=11;break a;case Ad:g=14;break a;case Ef:g=16;d=null;break a;case Df:g= +22;break a}throw Error(k(130,null==a?a:typeof a,""));}b=la(g,c,b,e);b.elementType=a;b.type=d;b.expirationTime=f;return b}function Ha(a,b,c,d){a=la(7,a,d,b);a.expirationTime=c;return a}function qe(a,b,c){a=la(6,a,null,b);a.expirationTime=c;return a}function re(a,b,c){b=la(4,null!==a.children?a.children:[],a.key,b);b.expirationTime=c;b.stateNode={containerInfo:a.containerInfo,pendingChildren:null,implementation:a.implementation};return b}function Ij(a,b,c){this.tag=b;this.current=null;this.containerInfo= +a;this.pingCache=this.pendingChildren=null;this.finishedExpirationTime=0;this.finishedWork=null;this.timeoutHandle=-1;this.pendingContext=this.context=null;this.hydrate=c;this.callbackNode=null;this.callbackPriority=90;this.lastExpiredTime=this.lastPingedTime=this.nextKnownPendingLevel=this.lastSuspendedTime=this.firstSuspendedTime=this.firstPendingTime=0}function Kh(a,b){var c=a.firstSuspendedTime;a=a.lastSuspendedTime;return 0!==c&&c>=b&&a<=b}function Ya(a,b){var c=a.firstSuspendedTime,d=a.lastSuspendedTime; +c<b&&(a.firstSuspendedTime=b);if(d>b||0===c)a.lastSuspendedTime=b;b<=a.lastPingedTime&&(a.lastPingedTime=0);b<=a.lastExpiredTime&&(a.lastExpiredTime=0)}function yh(a,b){b>a.firstPendingTime&&(a.firstPendingTime=b);var c=a.firstSuspendedTime;0!==c&&(b>=c?a.firstSuspendedTime=a.lastSuspendedTime=a.nextKnownPendingLevel=0:b>=a.lastSuspendedTime&&(a.lastSuspendedTime=b+1),b>a.nextKnownPendingLevel&&(a.nextKnownPendingLevel=b))}function Ue(a,b){var c=a.lastExpiredTime;if(0===c||c>b)a.lastExpiredTime=b} +function md(a,b,c,d){var e=b.current,f=ka(),g=Vb.suspense;f=Va(f,e,g);a:if(c){c=c._reactInternalFiber;b:{if(Na(c)!==c||1!==c.tag)throw Error(k(170));var h=c;do{switch(h.tag){case 3:h=h.stateNode.context;break b;case 1:if(N(h.type)){h=h.stateNode.__reactInternalMemoizedMergedChildContext;break b}}h=h.return}while(null!==h);throw Error(k(171));}if(1===c.tag){var m=c.type;if(N(m)){c=Gg(c,m,h);break a}}c=h}else c=Ca;null===b.context?b.context=c:b.pendingContext=c;b=Ea(f,g);b.payload={element:a};d=void 0=== +d?null:d;null!==d&&(b.callback=d);Fa(e,b);Ja(e,f);return f}function cf(a){a=a.current;if(!a.child)return null;switch(a.child.tag){case 5:return a.child.stateNode;default:return a.child.stateNode}}function Wh(a,b){a=a.memoizedState;null!==a&&null!==a.dehydrated&&a.retryTime<b&&(a.retryTime=b)}function df(a,b){Wh(a,b);(a=a.alternate)&&Wh(a,b)}function ef(a,b,c){c=null!=c&&!0===c.hydrate;var d=new Ij(a,b,c),e=la(3,null,null,2===b?7:1===b?3:0);d.current=e;e.stateNode=d;ne(e);a[Lb]=d.current;c&&0!==b&& +xi(a,9===a.nodeType?a:a.ownerDocument);this._internalRoot=d}function bc(a){return!(!a||1!==a.nodeType&&9!==a.nodeType&&11!==a.nodeType&&(8!==a.nodeType||" react-mount-point-unstable "!==a.nodeValue))}function Jj(a,b){b||(b=a?9===a.nodeType?a.documentElement:a.firstChild:null,b=!(!b||1!==b.nodeType||!b.hasAttribute("data-reactroot")));if(!b)for(var c;c=a.lastChild;)a.removeChild(c);return new ef(a,0,b?{hydrate:!0}:void 0)}function nd(a,b,c,d,e){var f=c._reactRootContainer;if(f){var g=f._internalRoot; +if("function"===typeof e){var h=e;e=function(){var a=cf(g);h.call(a)}}md(b,g,a,e)}else{f=c._reactRootContainer=Jj(c,d);g=f._internalRoot;if("function"===typeof e){var m=e;e=function(){var a=cf(g);m.call(a)}}Rh(function(){md(b,g,a,e)})}return cf(g)}function Kj(a,b,c){var d=3<arguments.length&&void 0!==arguments[3]?arguments[3]:null;return{$$typeof:gb,key:null==d?null:""+d,children:a,containerInfo:b,implementation:c}}function Xh(a,b){var c=2<arguments.length&&void 0!==arguments[2]?arguments[2]:null; +if(!bc(b))throw Error(k(200));return Kj(a,b,null,c)}if(!ea)throw Error(k(227));var ki=function(a,b,c,d,e,f,g,h,m){var n=Array.prototype.slice.call(arguments,3);try{b.apply(c,n)}catch(C){this.onError(C)}},yb=!1,gc=null,hc=!1,pd=null,li={onError:function(a){yb=!0;gc=a}},td=null,rf=null,mf=null,ic=null,cb={},jc=[],qd={},db={},rd={},wa=!("undefined"===typeof window||"undefined"===typeof window.document||"undefined"===typeof window.document.createElement),M=ea.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.assign, +sd=null,eb=null,fb=null,ee=function(a,b){return a(b)},eg=function(a,b,c,d,e){return a(b,c,d,e)},vd=function(){},vf=ee,Oa=!1,wd=!1,Z=ea.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.Scheduler,Lj=Z.unstable_cancelCallback,ff=Z.unstable_now,$f=Z.unstable_scheduleCallback,Mj=Z.unstable_shouldYield,Yh=Z.unstable_requestPaint,Pd=Z.unstable_runWithPriority,Nj=Z.unstable_getCurrentPriorityLevel,Oj=Z.unstable_ImmediatePriority,Zh=Z.unstable_UserBlockingPriority,ag=Z.unstable_NormalPriority,Pj=Z.unstable_LowPriority, +Qj=Z.unstable_IdlePriority,oi=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,wf=Object.prototype.hasOwnProperty,yf={},xf={},E={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(a){E[a]= +new L(a,0,!1,a,null,!1)});[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(a){var b=a[0];E[b]=new L(b,1,!1,a[1],null,!1)});["contentEditable","draggable","spellCheck","value"].forEach(function(a){E[a]=new L(a,2,!1,a.toLowerCase(),null,!1)});["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(a){E[a]=new L(a,2,!1,a,null,!1)});"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(a){E[a]= +new L(a,3,!1,a.toLowerCase(),null,!1)});["checked","multiple","muted","selected"].forEach(function(a){E[a]=new L(a,3,!0,a,null,!1)});["capture","download"].forEach(function(a){E[a]=new L(a,4,!1,a,null,!1)});["cols","rows","size","span"].forEach(function(a){E[a]=new L(a,6,!1,a,null,!1)});["rowSpan","start"].forEach(function(a){E[a]=new L(a,5,!1,a.toLowerCase(),null,!1)});var gf=/[\-:]([a-z])/g,hf=function(a){return a[1].toUpperCase()};"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(a){var b= +a.replace(gf,hf);E[b]=new L(b,1,!1,a,null,!1)});"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(a){var b=a.replace(gf,hf);E[b]=new L(b,1,!1,a,"http://www.w3.org/1999/xlink",!1)});["xml:base","xml:lang","xml:space"].forEach(function(a){var b=a.replace(gf,hf);E[b]=new L(b,1,!1,a,"http://www.w3.org/XML/1998/namespace",!1)});["tabIndex","crossOrigin"].forEach(function(a){E[a]=new L(a,1,!1,a.toLowerCase(),null,!1)});E.xlinkHref=new L("xlinkHref",1, +!1,"xlink:href","http://www.w3.org/1999/xlink",!0);["src","href","action","formAction"].forEach(function(a){E[a]=new L(a,1,!1,a.toLowerCase(),null,!0)});var da=ea.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;da.hasOwnProperty("ReactCurrentDispatcher")||(da.ReactCurrentDispatcher={current:null});da.hasOwnProperty("ReactCurrentBatchConfig")||(da.ReactCurrentBatchConfig={suspense:null});var si=/^(.*)[\\\/]/,Q="function"===typeof Symbol&&Symbol.for,Pc=Q?Symbol.for("react.element"):60103,gb=Q?Symbol.for("react.portal"): +60106,Ma=Q?Symbol.for("react.fragment"):60107,Af=Q?Symbol.for("react.strict_mode"):60108,kc=Q?Symbol.for("react.profiler"):60114,Cf=Q?Symbol.for("react.provider"):60109,Bf=Q?Symbol.for("react.context"):60110,Hj=Q?Symbol.for("react.concurrent_mode"):60111,zd=Q?Symbol.for("react.forward_ref"):60112,lc=Q?Symbol.for("react.suspense"):60113,yd=Q?Symbol.for("react.suspense_list"):60120,Ad=Q?Symbol.for("react.memo"):60115,Ef=Q?Symbol.for("react.lazy"):60116,Df=Q?Symbol.for("react.block"):60121,zf="function"=== +typeof Symbol&&Symbol.iterator,od,xh=function(a){return"undefined"!==typeof MSApp&&MSApp.execUnsafeLocalFunction?function(b,c,d,e){MSApp.execUnsafeLocalFunction(function(){return a(b,c,d,e)})}:a}(function(a,b){if("http://www.w3.org/2000/svg"!==a.namespaceURI||"innerHTML"in a)a.innerHTML=b;else{od=od||document.createElement("div");od.innerHTML="<svg>"+b.valueOf().toString()+"</svg>";for(b=od.firstChild;a.firstChild;)a.removeChild(a.firstChild);for(;b.firstChild;)a.appendChild(b.firstChild)}}),Wb=function(a, +b){if(b){var c=a.firstChild;if(c&&c===a.lastChild&&3===c.nodeType){c.nodeValue=b;return}}a.textContent=b},ib={animationend:nc("Animation","AnimationEnd"),animationiteration:nc("Animation","AnimationIteration"),animationstart:nc("Animation","AnimationStart"),transitionend:nc("Transition","TransitionEnd")},Id={},Of={};wa&&(Of=document.createElement("div").style,"AnimationEvent"in window||(delete ib.animationend.animation,delete ib.animationiteration.animation,delete ib.animationstart.animation),"TransitionEvent"in +window||delete ib.transitionend.transition);var $h=oc("animationend"),ai=oc("animationiteration"),bi=oc("animationstart"),ci=oc("transitionend"),Db="abort canplay canplaythrough durationchange emptied encrypted ended error loadeddata loadedmetadata loadstart pause play playing progress ratechange seeked seeking stalled suspend timeupdate volumechange waiting".split(" "),Pf=new ("function"===typeof WeakMap?WeakMap:Map),Ab=null,wi=function(a){if(a){var b=a._dispatchListeners,c=a._dispatchInstances; +if(Array.isArray(b))for(var d=0;d<b.length&&!a.isPropagationStopped();d++)lf(a,b[d],c[d]);else b&&lf(a,b,c);a._dispatchListeners=null;a._dispatchInstances=null;a.isPersistent()||a.constructor.release(a)}},qc=[],Rd=!1,fa=[],xa=null,ya=null,za=null,Eb=new Map,Fb=new Map,Jb=[],Nd="mousedown mouseup touchcancel touchend touchstart auxclick dblclick pointercancel pointerdown pointerup dragend dragstart drop compositionend compositionstart keydown keypress keyup input textInput close cancel copy cut paste click change contextmenu reset submit".split(" "), +yi="focus blur dragenter dragleave mouseover mouseout pointerover pointerout gotpointercapture lostpointercapture".split(" "),dg={},cg=new Map,Td=new Map,Rj=["abort","abort",$h,"animationEnd",ai,"animationIteration",bi,"animationStart","canplay","canPlay","canplaythrough","canPlayThrough","durationchange","durationChange","emptied","emptied","encrypted","encrypted","ended","ended","error","error","gotpointercapture","gotPointerCapture","load","load","loadeddata","loadedData","loadedmetadata","loadedMetadata", +"loadstart","loadStart","lostpointercapture","lostPointerCapture","playing","playing","progress","progress","seeking","seeking","stalled","stalled","suspend","suspend","timeupdate","timeUpdate",ci,"transitionEnd","waiting","waiting"];Sd("blur blur cancel cancel click click close close contextmenu contextMenu copy copy cut cut auxclick auxClick dblclick doubleClick dragend dragEnd dragstart dragStart drop drop focus focus input input invalid invalid keydown keyDown keypress keyPress keyup keyUp mousedown mouseDown mouseup mouseUp paste paste pause pause play play pointercancel pointerCancel pointerdown pointerDown pointerup pointerUp ratechange rateChange reset reset seeked seeked submit submit touchcancel touchCancel touchend touchEnd touchstart touchStart volumechange volumeChange".split(" "), +0);Sd("drag drag dragenter dragEnter dragexit dragExit dragleave dragLeave dragover dragOver mousemove mouseMove mouseout mouseOut mouseover mouseOver pointermove pointerMove pointerout pointerOut pointerover pointerOver scroll scroll toggle toggle touchmove touchMove wheel wheel".split(" "),1);Sd(Rj,2);(function(a,b){for(var c=0;c<a.length;c++)Td.set(a[c],b)})("change selectionchange textInput compositionstart compositionend compositionupdate".split(" "),0);var Hi=Zh,Gi=Pd,tc=!0,Kb={animationIterationCount:!0, +borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0, +strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},Sj=["Webkit","ms","Moz","O"];Object.keys(Kb).forEach(function(a){Sj.forEach(function(b){b=b+a.charAt(0).toUpperCase()+a.substring(1);Kb[b]=Kb[a]})});var Ii=M({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0}),ng="$",og="/$",$d="$?",Zd="$!",Ze=null,$e=null,We="function"===typeof setTimeout?setTimeout:void 0,vj="function"=== +typeof clearTimeout?clearTimeout:void 0,jf=Math.random().toString(36).slice(2),Aa="__reactInternalInstance$"+jf,vc="__reactEventHandlers$"+jf,Lb="__reactContainere$"+jf,Ba=null,ce=null,wc=null;M(R.prototype,{preventDefault:function(){this.defaultPrevented=!0;var a=this.nativeEvent;a&&(a.preventDefault?a.preventDefault():"unknown"!==typeof a.returnValue&&(a.returnValue=!1),this.isDefaultPrevented=xc)},stopPropagation:function(){var a=this.nativeEvent;a&&(a.stopPropagation?a.stopPropagation():"unknown"!== +typeof a.cancelBubble&&(a.cancelBubble=!0),this.isPropagationStopped=xc)},persist:function(){this.isPersistent=xc},isPersistent:yc,destructor:function(){var a=this.constructor.Interface,b;for(b in a)this[b]=null;this.nativeEvent=this._targetInst=this.dispatchConfig=null;this.isPropagationStopped=this.isDefaultPrevented=yc;this._dispatchInstances=this._dispatchListeners=null}});R.Interface={type:null,target:null,currentTarget:function(){return null},eventPhase:null,bubbles:null,cancelable:null,timeStamp:function(a){return a.timeStamp|| +Date.now()},defaultPrevented:null,isTrusted:null};R.extend=function(a){function b(){return c.apply(this,arguments)}var c=this,d=function(){};d.prototype=c.prototype;d=new d;M(d,b.prototype);b.prototype=d;b.prototype.constructor=b;b.Interface=M({},c.Interface,a);b.extend=c.extend;sg(b);return b};sg(R);var Tj=R.extend({data:null}),Uj=R.extend({data:null}),Ni=[9,13,27,32],de=wa&&"CompositionEvent"in window,cc=null;wa&&"documentMode"in document&&(cc=document.documentMode);var Vj=wa&&"TextEvent"in window&& +!cc,xg=wa&&(!de||cc&&8<cc&&11>=cc),wg=String.fromCharCode(32),ua={beforeInput:{phasedRegistrationNames:{bubbled:"onBeforeInput",captured:"onBeforeInputCapture"},dependencies:["compositionend","keypress","textInput","paste"]},compositionEnd:{phasedRegistrationNames:{bubbled:"onCompositionEnd",captured:"onCompositionEndCapture"},dependencies:"blur compositionend keydown keypress keyup mousedown".split(" ")},compositionStart:{phasedRegistrationNames:{bubbled:"onCompositionStart",captured:"onCompositionStartCapture"}, +dependencies:"blur compositionstart keydown keypress keyup mousedown".split(" ")},compositionUpdate:{phasedRegistrationNames:{bubbled:"onCompositionUpdate",captured:"onCompositionUpdateCapture"},dependencies:"blur compositionupdate keydown keypress keyup mousedown".split(" ")}},vg=!1,mb=!1,Wj={eventTypes:ua,extractEvents:function(a,b,c,d,e){var f;if(de)b:{switch(a){case "compositionstart":var g=ua.compositionStart;break b;case "compositionend":g=ua.compositionEnd;break b;case "compositionupdate":g= +ua.compositionUpdate;break b}g=void 0}else mb?tg(a,c)&&(g=ua.compositionEnd):"keydown"===a&&229===c.keyCode&&(g=ua.compositionStart);g?(xg&&"ko"!==c.locale&&(mb||g!==ua.compositionStart?g===ua.compositionEnd&&mb&&(f=rg()):(Ba=d,ce="value"in Ba?Ba.value:Ba.textContent,mb=!0)),e=Tj.getPooled(g,b,c,d),f?e.data=f:(f=ug(c),null!==f&&(e.data=f)),lb(e),f=e):f=null;(a=Vj?Oi(a,c):Pi(a,c))?(b=Uj.getPooled(ua.beforeInput,b,c,d),b.data=a,lb(b)):b=null;return null===f?b:null===b?f:[f,b]}},Qi={color:!0,date:!0, +datetime:!0,"datetime-local":!0,email:!0,month:!0,number:!0,password:!0,range:!0,search:!0,tel:!0,text:!0,time:!0,url:!0,week:!0},Ag={change:{phasedRegistrationNames:{bubbled:"onChange",captured:"onChangeCapture"},dependencies:"blur change click focus input keydown keyup selectionchange".split(" ")}},Mb=null,Nb=null,kf=!1;wa&&(kf=Tf("input")&&(!document.documentMode||9<document.documentMode));var Xj={eventTypes:Ag,_isInputEventSupported:kf,extractEvents:function(a,b,c,d,e){e=b?Pa(b):window;var f= +e.nodeName&&e.nodeName.toLowerCase();if("select"===f||"input"===f&&"file"===e.type)var g=Si;else if(yg(e))if(kf)g=Wi;else{g=Ui;var h=Ti}else(f=e.nodeName)&&"input"===f.toLowerCase()&&("checkbox"===e.type||"radio"===e.type)&&(g=Vi);if(g&&(g=g(a,b)))return zg(g,c,d);h&&h(a,e,b);"blur"===a&&(a=e._wrapperState)&&a.controlled&&"number"===e.type&&Ed(e,"number",e.value)}},dc=R.extend({view:null,detail:null}),Yi={Alt:"altKey",Control:"ctrlKey",Meta:"metaKey",Shift:"shiftKey"},di=0,ei=0,fi=!1,gi=!1,ec=dc.extend({screenX:null, +screenY:null,clientX:null,clientY:null,pageX:null,pageY:null,ctrlKey:null,shiftKey:null,altKey:null,metaKey:null,getModifierState:fe,button:null,buttons:null,relatedTarget:function(a){return a.relatedTarget||(a.fromElement===a.srcElement?a.toElement:a.fromElement)},movementX:function(a){if("movementX"in a)return a.movementX;var b=di;di=a.screenX;return fi?"mousemove"===a.type?a.screenX-b:0:(fi=!0,0)},movementY:function(a){if("movementY"in a)return a.movementY;var b=ei;ei=a.screenY;return gi?"mousemove"=== +a.type?a.screenY-b:0:(gi=!0,0)}}),hi=ec.extend({pointerId:null,width:null,height:null,pressure:null,tangentialPressure:null,tiltX:null,tiltY:null,twist:null,pointerType:null,isPrimary:null}),fc={mouseEnter:{registrationName:"onMouseEnter",dependencies:["mouseout","mouseover"]},mouseLeave:{registrationName:"onMouseLeave",dependencies:["mouseout","mouseover"]},pointerEnter:{registrationName:"onPointerEnter",dependencies:["pointerout","pointerover"]},pointerLeave:{registrationName:"onPointerLeave",dependencies:["pointerout", +"pointerover"]}},Yj={eventTypes:fc,extractEvents:function(a,b,c,d,e){var f="mouseover"===a||"pointerover"===a,g="mouseout"===a||"pointerout"===a;if(f&&0===(e&32)&&(c.relatedTarget||c.fromElement)||!g&&!f)return null;f=d.window===d?d:(f=d.ownerDocument)?f.defaultView||f.parentWindow:window;if(g){if(g=b,b=(b=c.relatedTarget||c.toElement)?Bb(b):null,null!==b){var h=Na(b);if(b!==h||5!==b.tag&&6!==b.tag)b=null}}else g=null;if(g===b)return null;if("mouseout"===a||"mouseover"===a){var m=ec;var n=fc.mouseLeave; +var l=fc.mouseEnter;var k="mouse"}else if("pointerout"===a||"pointerover"===a)m=hi,n=fc.pointerLeave,l=fc.pointerEnter,k="pointer";a=null==g?f:Pa(g);f=null==b?f:Pa(b);n=m.getPooled(n,g,c,d);n.type=k+"leave";n.target=a;n.relatedTarget=f;c=m.getPooled(l,b,c,d);c.type=k+"enter";c.target=f;c.relatedTarget=a;d=g;k=b;if(d&&k)a:{m=d;l=k;g=0;for(a=m;a;a=pa(a))g++;a=0;for(b=l;b;b=pa(b))a++;for(;0<g-a;)m=pa(m),g--;for(;0<a-g;)l=pa(l),a--;for(;g--;){if(m===l||m===l.alternate)break a;m=pa(m);l=pa(l)}m=null}else m= +null;l=m;for(m=[];d&&d!==l;){g=d.alternate;if(null!==g&&g===l)break;m.push(d);d=pa(d)}for(d=[];k&&k!==l;){g=k.alternate;if(null!==g&&g===l)break;d.push(k);k=pa(k)}for(k=0;k<m.length;k++)be(m[k],"bubbled",n);for(k=d.length;0<k--;)be(d[k],"captured",c);return 0===(e&64)?[n]:[n,c]}},Qa="function"===typeof Object.is?Object.is:Zi,$i=Object.prototype.hasOwnProperty,Zj=wa&&"documentMode"in document&&11>=document.documentMode,Eg={select:{phasedRegistrationNames:{bubbled:"onSelect",captured:"onSelectCapture"}, +dependencies:"blur contextmenu dragend focus keydown keyup mousedown mouseup selectionchange".split(" ")}},nb=null,he=null,Pb=null,ge=!1,ak={eventTypes:Eg,extractEvents:function(a,b,c,d,e,f){e=f||(d.window===d?d.document:9===d.nodeType?d:d.ownerDocument);if(!(f=!e)){a:{e=Jd(e);f=rd.onSelect;for(var g=0;g<f.length;g++)if(!e.has(f[g])){e=!1;break a}e=!0}f=!e}if(f)return null;e=b?Pa(b):window;switch(a){case "focus":if(yg(e)||"true"===e.contentEditable)nb=e,he=b,Pb=null;break;case "blur":Pb=he=nb=null; +break;case "mousedown":ge=!0;break;case "contextmenu":case "mouseup":case "dragend":return ge=!1,Dg(c,d);case "selectionchange":if(Zj)break;case "keydown":case "keyup":return Dg(c,d)}return null}},bk=R.extend({animationName:null,elapsedTime:null,pseudoElement:null}),ck=R.extend({clipboardData:function(a){return"clipboardData"in a?a.clipboardData:window.clipboardData}}),dk=dc.extend({relatedTarget:null}),ek={Esc:"Escape",Spacebar:" ",Left:"ArrowLeft",Up:"ArrowUp",Right:"ArrowRight",Down:"ArrowDown", +Del:"Delete",Win:"OS",Menu:"ContextMenu",Apps:"ContextMenu",Scroll:"ScrollLock",MozPrintableKey:"Unidentified"},fk={8:"Backspace",9:"Tab",12:"Clear",13:"Enter",16:"Shift",17:"Control",18:"Alt",19:"Pause",20:"CapsLock",27:"Escape",32:" ",33:"PageUp",34:"PageDown",35:"End",36:"Home",37:"ArrowLeft",38:"ArrowUp",39:"ArrowRight",40:"ArrowDown",45:"Insert",46:"Delete",112:"F1",113:"F2",114:"F3",115:"F4",116:"F5",117:"F6",118:"F7",119:"F8",120:"F9",121:"F10",122:"F11",123:"F12",144:"NumLock",145:"ScrollLock", +224:"Meta"},gk=dc.extend({key:function(a){if(a.key){var b=ek[a.key]||a.key;if("Unidentified"!==b)return b}return"keypress"===a.type?(a=Ac(a),13===a?"Enter":String.fromCharCode(a)):"keydown"===a.type||"keyup"===a.type?fk[a.keyCode]||"Unidentified":""},location:null,ctrlKey:null,shiftKey:null,altKey:null,metaKey:null,repeat:null,locale:null,getModifierState:fe,charCode:function(a){return"keypress"===a.type?Ac(a):0},keyCode:function(a){return"keydown"===a.type||"keyup"===a.type?a.keyCode:0},which:function(a){return"keypress"=== +a.type?Ac(a):"keydown"===a.type||"keyup"===a.type?a.keyCode:0}}),hk=ec.extend({dataTransfer:null}),ik=dc.extend({touches:null,targetTouches:null,changedTouches:null,altKey:null,metaKey:null,ctrlKey:null,shiftKey:null,getModifierState:fe}),jk=R.extend({propertyName:null,elapsedTime:null,pseudoElement:null}),kk=ec.extend({deltaX:function(a){return"deltaX"in a?a.deltaX:"wheelDeltaX"in a?-a.wheelDeltaX:0},deltaY:function(a){return"deltaY"in a?a.deltaY:"wheelDeltaY"in a?-a.wheelDeltaY:"wheelDelta"in a? +-a.wheelDelta:0},deltaZ:null,deltaMode:null}),lk={eventTypes:dg,extractEvents:function(a,b,c,d,e){e=cg.get(a);if(!e)return null;switch(a){case "keypress":if(0===Ac(c))return null;case "keydown":case "keyup":a=gk;break;case "blur":case "focus":a=dk;break;case "click":if(2===c.button)return null;case "auxclick":case "dblclick":case "mousedown":case "mousemove":case "mouseup":case "mouseout":case "mouseover":case "contextmenu":a=ec;break;case "drag":case "dragend":case "dragenter":case "dragexit":case "dragleave":case "dragover":case "dragstart":case "drop":a= +hk;break;case "touchcancel":case "touchend":case "touchmove":case "touchstart":a=ik;break;case $h:case ai:case bi:a=bk;break;case ci:a=jk;break;case "scroll":a=dc;break;case "wheel":a=kk;break;case "copy":case "cut":case "paste":a=ck;break;case "gotpointercapture":case "lostpointercapture":case "pointercancel":case "pointerdown":case "pointermove":case "pointerout":case "pointerover":case "pointerup":a=hi;break;default:a=R}b=a.getPooled(e,b,c,d);lb(b);return b}};(function(a){if(ic)throw Error(k(101)); +ic=Array.prototype.slice.call(a);nf()})("ResponderEventPlugin SimpleEventPlugin EnterLeaveEventPlugin ChangeEventPlugin SelectEventPlugin BeforeInputEventPlugin".split(" "));(function(a,b,c){td=a;rf=b;mf=c})(ae,Hb,Pa);pf({SimpleEventPlugin:lk,EnterLeaveEventPlugin:Yj,ChangeEventPlugin:Xj,SelectEventPlugin:ak,BeforeInputEventPlugin:Wj});var ie=[],ob=-1,Ca={},B={current:Ca},G={current:!1},Ra=Ca,bj=Pd,je=$f,Rg=Lj,aj=Nj,Dc=Oj,Ig=Zh,Jg=ag,Kg=Pj,Lg=Qj,Qg={},yj=Mj,Cj=void 0!==Yh?Yh:function(){},qa=null, +Ec=null,ke=!1,ii=ff(),Y=1E4>ii?ff:function(){return ff()-ii},Ic={current:null},Hc=null,qb=null,Gc=null,Tg=0,Jc=2,Ga=!1,Vb=da.ReactCurrentBatchConfig,$g=(new ea.Component).refs,Mc={isMounted:function(a){return(a=a._reactInternalFiber)?Na(a)===a:!1},enqueueSetState:function(a,b,c){a=a._reactInternalFiber;var d=ka(),e=Vb.suspense;d=Va(d,a,e);e=Ea(d,e);e.payload=b;void 0!==c&&null!==c&&(e.callback=c);Fa(a,e);Ja(a,d)},enqueueReplaceState:function(a,b,c){a=a._reactInternalFiber;var d=ka(),e=Vb.suspense; +d=Va(d,a,e);e=Ea(d,e);e.tag=1;e.payload=b;void 0!==c&&null!==c&&(e.callback=c);Fa(a,e);Ja(a,d)},enqueueForceUpdate:function(a,b){a=a._reactInternalFiber;var c=ka(),d=Vb.suspense;c=Va(c,a,d);d=Ea(c,d);d.tag=Jc;void 0!==b&&null!==b&&(d.callback=b);Fa(a,d);Ja(a,c)}},Qc=Array.isArray,wb=ah(!0),Fe=ah(!1),Sb={},ja={current:Sb},Ub={current:Sb},Tb={current:Sb},D={current:0},Sc=da.ReactCurrentDispatcher,X=da.ReactCurrentBatchConfig,Ia=0,z=null,K=null,J=null,Uc=!1,Tc={readContext:W,useCallback:S,useContext:S, +useEffect:S,useImperativeHandle:S,useLayoutEffect:S,useMemo:S,useReducer:S,useRef:S,useState:S,useDebugValue:S,useResponder:S,useDeferredValue:S,useTransition:S},dj={readContext:W,useCallback:ih,useContext:W,useEffect:eh,useImperativeHandle:function(a,b,c){c=null!==c&&void 0!==c?c.concat([a]):null;return ze(4,2,gh.bind(null,b,a),c)},useLayoutEffect:function(a,b){return ze(4,2,a,b)},useMemo:function(a,b){var c=ub();b=void 0===b?null:b;a=a();c.memoizedState=[a,b];return a},useReducer:function(a,b,c){var d= +ub();b=void 0!==c?c(b):b;d.memoizedState=d.baseState=b;a=d.queue={pending:null,dispatch:null,lastRenderedReducer:a,lastRenderedState:b};a=a.dispatch=ch.bind(null,z,a);return[d.memoizedState,a]},useRef:function(a){var b=ub();a={current:a};return b.memoizedState=a},useState:xe,useDebugValue:Be,useResponder:ue,useDeferredValue:function(a,b){var c=xe(a),d=c[0],e=c[1];eh(function(){var c=X.suspense;X.suspense=void 0===b?null:b;try{e(a)}finally{X.suspense=c}},[a,b]);return d},useTransition:function(a){var b= +xe(!1),c=b[0];b=b[1];return[ih(Ce.bind(null,b,a),[b,a]),c]}},ej={readContext:W,useCallback:Yc,useContext:W,useEffect:Xc,useImperativeHandle:hh,useLayoutEffect:fh,useMemo:jh,useReducer:Vc,useRef:dh,useState:function(a){return Vc(Ua)},useDebugValue:Be,useResponder:ue,useDeferredValue:function(a,b){var c=Vc(Ua),d=c[0],e=c[1];Xc(function(){var c=X.suspense;X.suspense=void 0===b?null:b;try{e(a)}finally{X.suspense=c}},[a,b]);return d},useTransition:function(a){var b=Vc(Ua),c=b[0];b=b[1];return[Yc(Ce.bind(null, +b,a),[b,a]),c]}},fj={readContext:W,useCallback:Yc,useContext:W,useEffect:Xc,useImperativeHandle:hh,useLayoutEffect:fh,useMemo:jh,useReducer:Wc,useRef:dh,useState:function(a){return Wc(Ua)},useDebugValue:Be,useResponder:ue,useDeferredValue:function(a,b){var c=Wc(Ua),d=c[0],e=c[1];Xc(function(){var c=X.suspense;X.suspense=void 0===b?null:b;try{e(a)}finally{X.suspense=c}},[a,b]);return d},useTransition:function(a){var b=Wc(Ua),c=b[0];b=b[1];return[Yc(Ce.bind(null,b,a),[b,a]),c]}},ra=null,Ka=null,Wa= +!1,gj=da.ReactCurrentOwner,ia=!1,Je={dehydrated:null,retryTime:0};var jj=function(a,b,c,d){for(c=b.child;null!==c;){if(5===c.tag||6===c.tag)a.appendChild(c.stateNode);else if(4!==c.tag&&null!==c.child){c.child.return=c;c=c.child;continue}if(c===b)break;for(;null===c.sibling;){if(null===c.return||c.return===b)return;c=c.return}c.sibling.return=c.return;c=c.sibling}};var wh=function(a){};var ij=function(a,b,c,d,e){var f=a.memoizedProps;if(f!==d){var g=b.stateNode;Ta(ja.current);a=null;switch(c){case "input":f= +Cd(g,f);d=Cd(g,d);a=[];break;case "option":f=Fd(g,f);d=Fd(g,d);a=[];break;case "select":f=M({},f,{value:void 0});d=M({},d,{value:void 0});a=[];break;case "textarea":f=Gd(g,f);d=Gd(g,d);a=[];break;default:"function"!==typeof f.onClick&&"function"===typeof d.onClick&&(g.onclick=uc)}Ud(c,d);var h,m;c=null;for(h in f)if(!d.hasOwnProperty(h)&&f.hasOwnProperty(h)&&null!=f[h])if("style"===h)for(m in g=f[h],g)g.hasOwnProperty(m)&&(c||(c={}),c[m]="");else"dangerouslySetInnerHTML"!==h&&"children"!==h&&"suppressContentEditableWarning"!== +h&&"suppressHydrationWarning"!==h&&"autoFocus"!==h&&(db.hasOwnProperty(h)?a||(a=[]):(a=a||[]).push(h,null));for(h in d){var k=d[h];g=null!=f?f[h]:void 0;if(d.hasOwnProperty(h)&&k!==g&&(null!=k||null!=g))if("style"===h)if(g){for(m in g)!g.hasOwnProperty(m)||k&&k.hasOwnProperty(m)||(c||(c={}),c[m]="");for(m in k)k.hasOwnProperty(m)&&g[m]!==k[m]&&(c||(c={}),c[m]=k[m])}else c||(a||(a=[]),a.push(h,c)),c=k;else"dangerouslySetInnerHTML"===h?(k=k?k.__html:void 0,g=g?g.__html:void 0,null!=k&&g!==k&&(a=a|| +[]).push(h,k)):"children"===h?g===k||"string"!==typeof k&&"number"!==typeof k||(a=a||[]).push(h,""+k):"suppressContentEditableWarning"!==h&&"suppressHydrationWarning"!==h&&(db.hasOwnProperty(h)?(null!=k&&oa(e,h),a||g===k||(a=[])):(a=a||[]).push(h,k))}c&&(a=a||[]).push("style",c);e=a;if(b.updateQueue=e)b.effectTag|=4}};var kj=function(a,b,c,d){c!==d&&(b.effectTag|=4)};var pj="function"===typeof WeakSet?WeakSet:Set,wj="function"===typeof WeakMap?WeakMap:Map,sj=Math.ceil,gd=da.ReactCurrentDispatcher, +Uh=da.ReactCurrentOwner,H=0,Ye=8,ca=16,ma=32,Xa=0,hd=1,Oh=2,ad=3,bd=4,Xe=5,p=H,U=null,t=null,P=0,F=Xa,id=null,ta=1073741823,Yb=1073741823,kd=null,Xb=0,jd=!1,Re=0,Ph=500,l=null,cd=!1,Se=null,La=null,ld=!1,Zb=null,$b=90,bb=null,ac=0,af=null,dd=0,Ja=function(a,b){if(50<ac)throw ac=0,af=null,Error(k(185));a=ed(a,b);if(null!==a){var c=Cc();1073741823===b?(p&Ye)!==H&&(p&(ca|ma))===H?Te(a):(V(a),p===H&&ha()):V(a);(p&4)===H||98!==c&&99!==c||(null===bb?bb=new Map([[a,b]]):(c=bb.get(a),(void 0===c||c>b)&&bb.set(a, +b)))}};var zj=function(a,b,c){var d=b.expirationTime;if(null!==a){var e=b.pendingProps;if(a.memoizedProps!==e||G.current)ia=!0;else{if(d<c){ia=!1;switch(b.tag){case 3:sh(b);Ee();break;case 5:bh(b);if(b.mode&4&&1!==c&&e.hidden)return b.expirationTime=b.childExpirationTime=1,null;break;case 1:N(b.type)&&Bc(b);break;case 4:se(b,b.stateNode.containerInfo);break;case 10:d=b.memoizedProps.value;e=b.type._context;y(Ic,e._currentValue);e._currentValue=d;break;case 13:if(null!==b.memoizedState){d=b.child.childExpirationTime; +if(0!==d&&d>=c)return th(a,b,c);y(D,D.current&1);b=sa(a,b,c);return null!==b?b.sibling:null}y(D,D.current&1);break;case 19:d=b.childExpirationTime>=c;if(0!==(a.effectTag&64)){if(d)return vh(a,b,c);b.effectTag|=64}e=b.memoizedState;null!==e&&(e.rendering=null,e.tail=null);y(D,D.current);if(!d)return null}return sa(a,b,c)}ia=!1}}else ia=!1;b.expirationTime=0;switch(b.tag){case 2:d=b.type;null!==a&&(a.alternate=null,b.alternate=null,b.effectTag|=2);a=b.pendingProps;e=pb(b,B.current);rb(b,c);e=we(null, +b,d,a,e,c);b.effectTag|=1;if("object"===typeof e&&null!==e&&"function"===typeof e.render&&void 0===e.$$typeof){b.tag=1;b.memoizedState=null;b.updateQueue=null;if(N(d)){var f=!0;Bc(b)}else f=!1;b.memoizedState=null!==e.state&&void 0!==e.state?e.state:null;ne(b);var g=d.getDerivedStateFromProps;"function"===typeof g&&Lc(b,d,g,a);e.updater=Mc;b.stateNode=e;e._reactInternalFiber=b;pe(b,d,a,c);b=Ie(null,b,d,!0,f,c)}else b.tag=0,T(null,b,e,c),b=b.child;return b;case 16:a:{e=b.elementType;null!==a&&(a.alternate= +null,b.alternate=null,b.effectTag|=2);a=b.pendingProps;ri(e);if(1!==e._status)throw e._result;e=e._result;b.type=e;f=b.tag=Gj(e);a=aa(e,a);switch(f){case 0:b=He(null,b,e,a,c);break a;case 1:b=rh(null,b,e,a,c);break a;case 11:b=nh(null,b,e,a,c);break a;case 14:b=oh(null,b,e,aa(e.type,a),d,c);break a}throw Error(k(306,e,""));}return b;case 0:return d=b.type,e=b.pendingProps,e=b.elementType===d?e:aa(d,e),He(a,b,d,e,c);case 1:return d=b.type,e=b.pendingProps,e=b.elementType===d?e:aa(d,e),rh(a,b,d,e,c); +case 3:sh(b);d=b.updateQueue;if(null===a||null===d)throw Error(k(282));d=b.pendingProps;e=b.memoizedState;e=null!==e?e.element:null;oe(a,b);Qb(b,d,null,c);d=b.memoizedState.element;if(d===e)Ee(),b=sa(a,b,c);else{if(e=b.stateNode.hydrate)Ka=kb(b.stateNode.containerInfo.firstChild),ra=b,e=Wa=!0;if(e)for(c=Fe(b,null,d,c),b.child=c;c;)c.effectTag=c.effectTag&-3|1024,c=c.sibling;else T(a,b,d,c),Ee();b=b.child}return b;case 5:return bh(b),null===a&&De(b),d=b.type,e=b.pendingProps,f=null!==a?a.memoizedProps: +null,g=e.children,Yd(d,e)?g=null:null!==f&&Yd(d,f)&&(b.effectTag|=16),qh(a,b),b.mode&4&&1!==c&&e.hidden?(b.expirationTime=b.childExpirationTime=1,b=null):(T(a,b,g,c),b=b.child),b;case 6:return null===a&&De(b),null;case 13:return th(a,b,c);case 4:return se(b,b.stateNode.containerInfo),d=b.pendingProps,null===a?b.child=wb(b,null,d,c):T(a,b,d,c),b.child;case 11:return d=b.type,e=b.pendingProps,e=b.elementType===d?e:aa(d,e),nh(a,b,d,e,c);case 7:return T(a,b,b.pendingProps,c),b.child;case 8:return T(a, +b,b.pendingProps.children,c),b.child;case 12:return T(a,b,b.pendingProps.children,c),b.child;case 10:a:{d=b.type._context;e=b.pendingProps;g=b.memoizedProps;f=e.value;var h=b.type._context;y(Ic,h._currentValue);h._currentValue=f;if(null!==g)if(h=g.value,f=Qa(h,f)?0:("function"===typeof d._calculateChangedBits?d._calculateChangedBits(h,f):1073741823)|0,0===f){if(g.children===e.children&&!G.current){b=sa(a,b,c);break a}}else for(h=b.child,null!==h&&(h.return=b);null!==h;){var m=h.dependencies;if(null!== +m){g=h.child;for(var l=m.firstContext;null!==l;){if(l.context===d&&0!==(l.observedBits&f)){1===h.tag&&(l=Ea(c,null),l.tag=Jc,Fa(h,l));h.expirationTime<c&&(h.expirationTime=c);l=h.alternate;null!==l&&l.expirationTime<c&&(l.expirationTime=c);Sg(h.return,c);m.expirationTime<c&&(m.expirationTime=c);break}l=l.next}}else g=10===h.tag?h.type===b.type?null:h.child:h.child;if(null!==g)g.return=h;else for(g=h;null!==g;){if(g===b){g=null;break}h=g.sibling;if(null!==h){h.return=g.return;g=h;break}g=g.return}h= +g}T(a,b,e.children,c);b=b.child}return b;case 9:return e=b.type,f=b.pendingProps,d=f.children,rb(b,c),e=W(e,f.unstable_observedBits),d=d(e),b.effectTag|=1,T(a,b,d,c),b.child;case 14:return e=b.type,f=aa(e,b.pendingProps),f=aa(e.type,f),oh(a,b,e,f,d,c);case 15:return ph(a,b,b.type,b.pendingProps,d,c);case 17:return d=b.type,e=b.pendingProps,e=b.elementType===d?e:aa(d,e),null!==a&&(a.alternate=null,b.alternate=null,b.effectTag|=2),b.tag=1,N(d)?(a=!0,Bc(b)):a=!1,rb(b,c),Yg(b,d,e),pe(b,d,e,c),Ie(null, +b,d,!0,a,c);case 19:return vh(a,b,c)}throw Error(k(156,b.tag));};var bf=null,Ne=null,la=function(a,b,c,d){return new Fj(a,b,c,d)};ef.prototype.render=function(a){md(a,this._internalRoot,null,null)};ef.prototype.unmount=function(){var a=this._internalRoot,b=a.containerInfo;md(null,a,null,function(){b[Lb]=null})};var Di=function(a){if(13===a.tag){var b=Fc(ka(),150,100);Ja(a,b);df(a,b)}};var Yf=function(a){13===a.tag&&(Ja(a,3),df(a,3))};var Bi=function(a){if(13===a.tag){var b=ka();b=Va(b,a,null);Ja(a, +b);df(a,b)}};sd=function(a,b,c){switch(b){case "input":Dd(a,c);b=c.name;if("radio"===c.type&&null!=b){for(c=a;c.parentNode;)c=c.parentNode;c=c.querySelectorAll("input[name="+JSON.stringify(""+b)+'][type="radio"]');for(b=0;b<c.length;b++){var d=c[b];if(d!==a&&d.form===a.form){var e=ae(d);if(!e)throw Error(k(90));Gf(d);Dd(d,e)}}}break;case "textarea":Lf(a,c);break;case "select":b=c.value,null!=b&&hb(a,!!c.multiple,b,!1)}};(function(a,b,c,d){ee=a;eg=b;vd=c;vf=d})(Qh,function(a,b,c,d,e){var f=p;p|=4; +try{return Da(98,a.bind(null,b,c,d,e))}finally{p=f,p===H&&ha()}},function(){(p&(1|ca|ma))===H&&(uj(),xb())},function(a,b){var c=p;p|=2;try{return a(b)}finally{p=c,p===H&&ha()}});var mk={Events:[Hb,Pa,ae,pf,qd,lb,function(a){Kd(a,Ki)},sf,tf,sc,pc,xb,{current:!1}]};(function(a){var b=a.findFiberByHostInstance;return Ej(M({},a,{overrideHookState:null,overrideProps:null,setSuspenseHandler:null,scheduleUpdate:null,currentDispatcherRef:da.ReactCurrentDispatcher,findHostInstanceByFiber:function(a){a=Sf(a); +return null===a?null:a.stateNode},findFiberByHostInstance:function(a){return b?b(a):null},findHostInstancesForRefresh:null,scheduleRefresh:null,scheduleRoot:null,setRefreshHandler:null,getCurrentFiber:null}))})({findFiberByHostInstance:Bb,bundleType:0,version:"16.13.1",rendererPackageName:"react-dom"});I.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED=mk;I.createPortal=Xh;I.findDOMNode=function(a){if(null==a)return null;if(1===a.nodeType)return a;var b=a._reactInternalFiber;if(void 0=== +b){if("function"===typeof a.render)throw Error(k(188));throw Error(k(268,Object.keys(a)));}a=Sf(b);a=null===a?null:a.stateNode;return a};I.flushSync=function(a,b){if((p&(ca|ma))!==H)throw Error(k(187));var c=p;p|=1;try{return Da(99,a.bind(null,b))}finally{p=c,ha()}};I.hydrate=function(a,b,c){if(!bc(b))throw Error(k(200));return nd(null,a,b,!0,c)};I.render=function(a,b,c){if(!bc(b))throw Error(k(200));return nd(null,a,b,!1,c)};I.unmountComponentAtNode=function(a){if(!bc(a))throw Error(k(40));return a._reactRootContainer? +(Rh(function(){nd(null,null,a,!1,function(){a._reactRootContainer=null;a[Lb]=null})}),!0):!1};I.unstable_batchedUpdates=Qh;I.unstable_createPortal=function(a,b){return Xh(a,b,2<arguments.length&&void 0!==arguments[2]?arguments[2]:null)};I.unstable_renderSubtreeIntoContainer=function(a,b,c,d){if(!bc(c))throw Error(k(200));if(null==a||void 0===a._reactInternalFiber)throw Error(k(38));return nd(a,b,c,!1,d)};I.version="16.13.1"}); +</script> + <script>const e = React.createElement; + +function pathToString(path) { + if (path[0] === '/') { + return '/' + path.slice(1).join('/'); + } else { + return path.join('/'); + } +} + +function findCommonPath(files) { + if (!files || !files.length) { + return []; + } + + function isPrefix(arr, prefix) { + if (arr.length < prefix.length) { + return false; + } + for (let i = prefix.length - 1; i >= 0; --i) { + if (arr[i] !== prefix[i]) { + return false; + } + } + return true; + } + + let commonPath = files[0].path.slice(0, -1); + while (commonPath.length) { + if (files.every(file => isPrefix(file.path, commonPath))) { + break; + } + commonPath.pop(); + } + return commonPath; +} + +function findFolders(files) { + if (!files || !files.length) { + return []; + } + + let folders = files.filter(file => file.path.length > 1).map(file => file.path[0]); + folders = [...new Set(folders)]; // unique + folders.sort(); + + folders = folders.map(folder => { + let filesInFolder = files + .filter(file => file.path[0] === folder) + .map(file => ({ + ...file, + path: file.path.slice(1), + parent: [...file.parent, file.path[0]], + })); + + const children = findFolders(filesInFolder); // recursion + + return { + is_folder: true, + path: [folder], + parent: files[0].parent, + children, + covered: children.reduce((sum, file) => sum + file.covered, 0), + coverable: children.reduce((sum, file) => sum + file.coverable, 0), + prevRun: { + covered: children.reduce((sum, file) => sum + file.prevRun.covered, 0), + coverable: children.reduce((sum, file) => sum + file.prevRun.coverable, 0), + } + }; + }); + + return [ + ...folders, + ...files.filter(file => file.path.length === 1), + ]; +} + +class App extends React.Component { + constructor(...args) { + super(...args); + + this.state = { + current: [], + }; + } + + componentDidMount() { + this.updateStateFromLocation(); + window.addEventListener("hashchange", () => this.updateStateFromLocation(), false); + } + + updateStateFromLocation() { + if (window.location.hash.length > 1) { + const current = window.location.hash.substr(1).split('/'); + this.setState({current}); + } else { + this.setState({current: []}); + } + } + + getCurrentPath() { + let file = this.props.root; + let path = [file]; + for (let p of this.state.current) { + file = file.children.find(file => file.path[0] === p); + if (!file) { + return path; + } + path.push(file); + } + return path; + } + + render() { + const path = this.getCurrentPath(); + const file = path[path.length - 1]; + + let w = null; + if (file.is_folder) { + w = e(FilesList, { + folder: file, + onSelectFile: this.selectFile.bind(this), + onBack: path.length > 1 ? this.back.bind(this) : null, + }); + } else { + w = e(DisplayFile, { + file, + onBack: this.back.bind(this), + }); + } + + return e('div', {className: 'app'}, w); + } + + selectFile(file) { + this.setState(({current}) => { + return {current: [...current, file.path[0]]}; + }, () => this.updateHash()); + } + + back(file) { + this.setState(({current}) => { + return {current: current.slice(0, current.length - 1)}; + }, () => this.updateHash()); + } + + updateHash() { + if (!this.state.current || !this.state.current.length) { + window.location = '#'; + } else { + window.location = '#' + this.state.current.join('/'); + } + } +} + +function FilesList({folder, onSelectFile, onBack}) { + let files = folder.children; + return e('div', {className: 'display-folder'}, + e(FileHeader, {file: folder, onBack}), + e('table', {className: 'files-list'}, + e('thead', {className: 'files-list__head'}, + e('tr', null, + e('th', null, "Path"), + e('th', null, "Coverage") + ) + ), + e('tbody', {className: 'files-list__body'}, + files.map(file => e(File, {file, onClick: onSelectFile})) + ) + ) + ); +} + +function File({file, onClick}) { + const coverage = file.coverable ? file.covered / file.coverable * 100 : -1; + const coverageDelta = file.prevRun && + (file.covered / file.coverable * 100 - file.prevRun.covered / file.prevRun.coverable * 100); + + return e('tr', { + className: 'files-list__file' + + (coverage >= 0 && coverage < 50 ? ' files-list__file_low': '') + + (coverage >= 50 && coverage < 80 ? ' files-list__file_medium': '') + + (coverage >= 80 ? ' files-list__file_high': '') + + (file.is_folder ? ' files-list__file_folder': ''), + onClick: () => onClick(file), + }, + e('td', null, e('a', null, pathToString(file.path))), + e('td', null, + file.covered + ' / ' + file.coverable + + (coverage >= 0 ? ' (' + coverage.toFixed(2) + '%)' : ''), + e('span', {title: 'Change from the previous run'}, + (coverageDelta ? ` (${coverageDelta > 0 ? '+' : ''}${coverageDelta.toFixed(2)}%)` : '')) + ) + ); +} + +function DisplayFile({file, onBack}) { + return e('div', {className: 'display-file'}, + e(FileHeader, {file, onBack}), + e(FileContent, {file}) + ); +} + +function FileHeader({file, onBack}) { + const coverage = file.covered / file.coverable * 100; + const coverageDelta = file.prevRun && (coverage - file.prevRun.covered / file.prevRun.coverable * 100); + + return e('div', {className: 'file-header'}, + onBack ? e('a', {className: 'file-header__back', onClick: onBack}, 'Back') : null, + e('div', {className: 'file-header__name'}, pathToString([...file.parent, ...file.path])), + e('div', {className: 'file-header__stat'}, + 'Covered: ' + file.covered + ' of ' + file.coverable + + (file.coverable ? ' (' + coverage.toFixed(2) + '%)' : ''), + e('span', {title: 'Change from the previous run'}, + (coverageDelta ? ` (${coverageDelta > 0 ? '+' : ''}${coverageDelta.toFixed(2)}%)` : '')) + ) + ); +} + +function FileContent({file}) { + return e('pre', {className: 'file-content'}, + file.content.split(/\r?\n/).map((line, index) => { + const trace = file.traces.find(trace => trace.line === index + 1); + const covered = trace && trace.stats.Line; + const uncovered = trace && !trace.stats.Line; + return e('code', { + className: 'code-line' + + (covered ? ' code-line_covered' : '') + + (uncovered ? ' code-line_uncovered' : ''), + title: trace ? JSON.stringify(trace.stats, null, 2) : null, + }, line); + }) + ); +} + +(function(){ + const commonPath = findCommonPath(data.files); + const prevFilesMap = new Map(); + + previousData && previousData.files.forEach((file) => { + const path = file.path.slice(commonPath.length).join('/'); + prevFilesMap.set(path, file); + }); + + const files = data.files.map((file) => { + const path = file.path.slice(commonPath.length); + const { covered = 0, coverable = 0 } = prevFilesMap.get(path.join('/')) || {}; + return { + ...file, + path, + parent: commonPath, + prevRun: { covered, coverable }, + }; + }); + + const children = findFolders(files); + + const root = { + is_folder: true, + children, + path: commonPath, + parent: [], + covered: children.reduce((sum, file) => sum + file.covered, 0), + coverable: children.reduce((sum, file) => sum + file.coverable, 0), + prevRun: { + covered: children.reduce((sum, file) => sum + file.prevRun.covered, 0), + coverable: children.reduce((sum, file) => sum + file.prevRun.coverable, 0), + } + }; + + ReactDOM.render(e(App, {root, prevFilesMap}), document.getElementById('root')); +}()); +</script> +</body> +</html>
\ No newline at end of file diff --git a/tests/evil_mutex.rs b/tests/evil_mutex.rs index 361fe4c..e10acc8 100644 --- a/tests/evil_mutex.rs +++ b/tests/evil_mutex.rs @@ -41,16 +41,16 @@ fn boxed_mutexes() { let c_useless = Arc::clone(&useless_mutex); let r = std::thread::spawn(move || { - let mut key = ThreadKey::get().unwrap(); + let key = ThreadKey::get().unwrap(); let collection = BoxedLockCollection::try_new((&*c_good, &*c_evil, &*c_useless)).unwrap(); - collection.lock(&mut key); + _ = collection.lock(key); }) .join(); assert!(r.is_err()); - assert!(good_mutex.try_lock(&mut key).is_ok()); - assert!(evil_mutex.try_lock(&mut key).is_err()); - assert!(useless_mutex.try_lock(&mut key).is_ok()); + assert!(good_mutex.scoped_try_lock(&mut key, |_| {}).is_ok()); + assert!(evil_mutex.scoped_try_lock(&mut key, |_| {}).is_err()); + assert!(useless_mutex.scoped_try_lock(&mut key, |_| {}).is_ok()); } #[test] @@ -64,15 +64,15 @@ fn retrying_mutexes() { let c_useless = Arc::clone(&useless_mutex); let r = std::thread::spawn(move || { - let mut key = ThreadKey::get().unwrap(); + let key = ThreadKey::get().unwrap(); let collection = RetryingLockCollection::try_new((&*c_good, &*c_evil, &*c_useless)).unwrap(); - collection.lock(&mut key); + collection.lock(key); }) .join(); assert!(r.is_err()); - assert!(good_mutex.try_lock(&mut key).is_ok()); - assert!(evil_mutex.try_lock(&mut key).is_err()); - assert!(useless_mutex.try_lock(&mut key).is_ok()); + assert!(good_mutex.scoped_try_lock(&mut key, |_| {}).is_ok()); + assert!(evil_mutex.scoped_try_lock(&mut key, |_| {}).is_err()); + assert!(useless_mutex.scoped_try_lock(&mut key, |_| {}).is_ok()); } diff --git a/tests/evil_rwlock.rs b/tests/evil_rwlock.rs index 234847c..9eed8a8 100644 --- a/tests/evil_rwlock.rs +++ b/tests/evil_rwlock.rs @@ -53,16 +53,16 @@ fn boxed_rwlocks() { let c_useless = Arc::clone(&useless_mutex); let r = std::thread::spawn(move || { - let mut key = ThreadKey::get().unwrap(); + let key = ThreadKey::get().unwrap(); let collection = BoxedLockCollection::try_new((&*c_good, &*c_evil, &*c_useless)).unwrap(); - collection.lock(&mut key); + _ = collection.lock(key); }) .join(); assert!(r.is_err()); - assert!(good_mutex.try_write(&mut key).is_ok()); - assert!(evil_mutex.try_write(&mut key).is_err()); - assert!(useless_mutex.try_write(&mut key).is_ok()); + 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()); } #[test] @@ -76,15 +76,15 @@ fn retrying_rwlocks() { let c_useless = Arc::clone(&useless_mutex); let r = std::thread::spawn(move || { - let mut key = ThreadKey::get().unwrap(); + let key = ThreadKey::get().unwrap(); let collection = RetryingLockCollection::try_new((&*c_good, &*c_evil, &*c_useless)).unwrap(); - collection.lock(&mut key); + collection.lock(key); }) .join(); assert!(r.is_err()); - assert!(good_mutex.try_write(&mut key).is_ok()); - assert!(evil_mutex.try_write(&mut key).is_err()); - assert!(useless_mutex.try_write(&mut key).is_ok()); + 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()); } diff --git a/tests/evil_try_mutex.rs b/tests/evil_try_mutex.rs new file mode 100644 index 0000000..5c31a91 --- /dev/null +++ b/tests/evil_try_mutex.rs @@ -0,0 +1,81 @@ +use std::sync::Arc; + +use happylock::{ + collection::{BoxedLockCollection, RetryingLockCollection}, + mutex::Mutex, + ThreadKey, +}; +use lock_api::{GuardNoSend, RawMutex}; + +struct EvilMutex { + inner: parking_lot::RawMutex, +} + +unsafe impl RawMutex for EvilMutex { + #[allow(clippy::declare_interior_mutable_const)] + const INIT: Self = Self { + inner: parking_lot::RawMutex::INIT, + }; + + type GuardMarker = GuardNoSend; + + fn lock(&self) { + self.inner.lock() + } + + fn try_lock(&self) -> bool { + panic!("mwahahahaha"); + } + + unsafe fn unlock(&self) { + self.inner.unlock() + } +} + +#[test] +fn boxed_mutexes() { + let mut key = ThreadKey::get().unwrap(); + let good_mutex: Arc<Mutex<i32, parking_lot::RawMutex>> = Arc::new(Mutex::new(5)); + let evil_mutex: Arc<Mutex<i32, EvilMutex>> = Arc::new(Mutex::new(7)); + let useless_mutex: Arc<Mutex<i32, parking_lot::RawMutex>> = Arc::new(Mutex::new(10)); + let c_good = Arc::clone(&good_mutex); + let c_evil = Arc::clone(&evil_mutex); + let c_useless = Arc::clone(&useless_mutex); + + let r = std::thread::spawn(move || { + let key = ThreadKey::get().unwrap(); + let collection = BoxedLockCollection::try_new((&*c_good, &*c_evil, &*c_useless)).unwrap(); + let g = collection.try_lock(key); + println!("{}", g.unwrap().1); + }) + .join(); + + assert!(r.is_err()); + assert!(good_mutex.scoped_try_lock(&mut key, |_| {}).is_ok()); + assert!(evil_mutex.scoped_try_lock(&mut key, |_| {}).is_err()); + assert!(useless_mutex.scoped_try_lock(&mut key, |_| {}).is_ok()); +} + +#[test] +fn retrying_mutexes() { + let mut key = ThreadKey::get().unwrap(); + let good_mutex: Arc<Mutex<i32, parking_lot::RawMutex>> = Arc::new(Mutex::new(5)); + let evil_mutex: Arc<Mutex<i32, EvilMutex>> = Arc::new(Mutex::new(7)); + let useless_mutex: Arc<Mutex<i32, parking_lot::RawMutex>> = Arc::new(Mutex::new(10)); + let c_good = Arc::clone(&good_mutex); + let c_evil = Arc::clone(&evil_mutex); + let c_useless = Arc::clone(&useless_mutex); + + let r = std::thread::spawn(move || { + let key = ThreadKey::get().unwrap(); + let collection = + RetryingLockCollection::try_new((&*c_good, &*c_evil, &*c_useless)).unwrap(); + let _ = collection.try_lock(key); + }) + .join(); + + assert!(r.is_err()); + assert!(good_mutex.scoped_try_lock(&mut key, |_| {}).is_ok()); + assert!(evil_mutex.scoped_try_lock(&mut key, |_| {}).is_err()); + assert!(useless_mutex.scoped_try_lock(&mut key, |_| {}).is_ok()); +} diff --git a/tests/evil_try_rwlock.rs b/tests/evil_try_rwlock.rs new file mode 100644 index 0000000..b00a666 --- /dev/null +++ b/tests/evil_try_rwlock.rs @@ -0,0 +1,90 @@ +use std::sync::Arc; + +use happylock::collection::{BoxedLockCollection, RetryingLockCollection}; +use happylock::rwlock::RwLock; +use happylock::ThreadKey; +use lock_api::{GuardNoSend, RawRwLock}; + +struct EvilRwLock { + inner: parking_lot::RawRwLock, +} + +unsafe impl RawRwLock for EvilRwLock { + #[allow(clippy::declare_interior_mutable_const)] + const INIT: Self = Self { + inner: parking_lot::RawRwLock::INIT, + }; + + type GuardMarker = GuardNoSend; + + fn lock_shared(&self) { + self.inner.lock_shared() + } + + fn try_lock_shared(&self) -> bool { + panic!("mwahahahaha") + } + + unsafe fn unlock_shared(&self) { + self.inner.unlock_shared() + } + + fn lock_exclusive(&self) { + self.inner.lock_exclusive() + } + + fn try_lock_exclusive(&self) -> bool { + panic!("mwahahahaha") + } + + unsafe fn unlock_exclusive(&self) { + self.inner.unlock_exclusive() + } +} + +#[test] +fn boxed_rwlocks() { + let mut key = ThreadKey::get().unwrap(); + let good_mutex: Arc<RwLock<i32, parking_lot::RawRwLock>> = Arc::new(RwLock::new(5)); + let evil_mutex: Arc<RwLock<i32, EvilRwLock>> = Arc::new(RwLock::new(7)); + let useless_mutex: Arc<RwLock<i32, parking_lot::RawRwLock>> = Arc::new(RwLock::new(10)); + let c_good = Arc::clone(&good_mutex); + let c_evil = Arc::clone(&evil_mutex); + let c_useless = Arc::clone(&useless_mutex); + + let r = std::thread::spawn(move || { + let key = ThreadKey::get().unwrap(); + let collection = BoxedLockCollection::try_new((&*c_good, &*c_evil, &*c_useless)).unwrap(); + let _ = collection.try_read(key); + }) + .join(); + + assert!(r.is_err()); + assert!(good_mutex.scoped_try_read(&mut key, |_| {}).is_ok()); + assert!(evil_mutex.scoped_try_read(&mut key, |_| {}).is_err()); + assert!(useless_mutex.scoped_try_read(&mut key, |_| {}).is_ok()); +} + +#[test] +fn retrying_rwlocks() { + let mut key = ThreadKey::get().unwrap(); + let good_mutex: Arc<RwLock<i32, parking_lot::RawRwLock>> = Arc::new(RwLock::new(5)); + let evil_mutex: Arc<RwLock<i32, EvilRwLock>> = Arc::new(RwLock::new(7)); + let useless_mutex: Arc<RwLock<i32, parking_lot::RawRwLock>> = Arc::new(RwLock::new(10)); + let c_good = Arc::clone(&good_mutex); + let c_evil = Arc::clone(&evil_mutex); + let c_useless = Arc::clone(&useless_mutex); + + let r = std::thread::spawn(move || { + let key = ThreadKey::get().unwrap(); + let collection = + RetryingLockCollection::try_new((&*c_good, &*c_evil, &*c_useless)).unwrap(); + _ = collection.try_read(key); + }) + .join(); + + assert!(r.is_err()); + assert!(good_mutex.scoped_try_read(&mut key, |_| {}).is_ok()); + assert!(evil_mutex.scoped_try_read(&mut key, |_| {}).is_err()); + assert!(useless_mutex.scoped_try_read(&mut key, |_| {}).is_ok()); +} diff --git a/tests/evil_unlock_mutex.rs b/tests/evil_unlock_mutex.rs new file mode 100644 index 0000000..ee12abc --- /dev/null +++ b/tests/evil_unlock_mutex.rs @@ -0,0 +1,99 @@ +use std::sync::Arc; + +use happylock::collection::{BoxedLockCollection, RetryingLockCollection}; +use happylock::mutex::Mutex; +use happylock::ThreadKey; +use lock_api::{GuardNoSend, RawMutex}; + +struct KindaEvilMutex { + inner: parking_lot::RawMutex, +} + +struct EvilMutex {} + +unsafe impl RawMutex for KindaEvilMutex { + #[allow(clippy::declare_interior_mutable_const)] + const INIT: Self = Self { + inner: parking_lot::RawMutex::INIT, + }; + + type GuardMarker = GuardNoSend; + + fn lock(&self) { + self.inner.lock() + } + + fn try_lock(&self) -> bool { + self.inner.try_lock() + } + + unsafe fn unlock(&self) { + panic!("mwahahahaha"); + } +} + +unsafe impl RawMutex for EvilMutex { + #[allow(clippy::declare_interior_mutable_const)] + const INIT: Self = Self {}; + + type GuardMarker = GuardNoSend; + + fn lock(&self) { + panic!("mwahahahaha"); + } + + fn try_lock(&self) -> bool { + panic!("mwahahahaha") + } + + unsafe fn unlock(&self) { + panic!("mwahahahaha"); + } +} + +#[test] +fn boxed_mutexes() { + let mut key = ThreadKey::get().unwrap(); + let kinda_evil_mutex: Arc<Mutex<i32, KindaEvilMutex>> = Arc::new(Mutex::new(5)); + let evil_mutex: Arc<Mutex<i32, EvilMutex>> = Arc::new(Mutex::new(7)); + let useless_mutex: Arc<Mutex<i32, parking_lot::RawMutex>> = Arc::new(Mutex::new(10)); + let c_good = Arc::clone(&kinda_evil_mutex); + let c_evil = Arc::clone(&evil_mutex); + let c_useless = Arc::clone(&useless_mutex); + + let r = std::thread::spawn(move || { + let key = ThreadKey::get().unwrap(); + let collection = BoxedLockCollection::try_new((&*c_good, &*c_evil, &*c_useless)).unwrap(); + _ = collection.lock(key); + }) + .join(); + + assert!(r.is_err()); + assert!(kinda_evil_mutex.scoped_try_lock(&mut key, |_| {}).is_err()); + assert!(evil_mutex.scoped_try_lock(&mut key, |_| {}).is_err()); + assert!(useless_mutex.scoped_try_lock(&mut key, |_| {}).is_ok()); +} + +#[test] +fn retrying_mutexes() { + let mut key = ThreadKey::get().unwrap(); + let kinda_evil_mutex: Arc<Mutex<i32, KindaEvilMutex>> = Arc::new(Mutex::new(5)); + let evil_mutex: Arc<Mutex<i32, EvilMutex>> = Arc::new(Mutex::new(7)); + let useless_mutex: Arc<Mutex<i32, parking_lot::RawMutex>> = Arc::new(Mutex::new(10)); + let c_good = Arc::clone(&kinda_evil_mutex); + let c_evil = Arc::clone(&evil_mutex); + let c_useless = Arc::clone(&useless_mutex); + + let r = std::thread::spawn(move || { + let key = ThreadKey::get().unwrap(); + let collection = + RetryingLockCollection::try_new((&*c_good, &*c_evil, &*c_useless)).unwrap(); + collection.lock(key); + }) + .join(); + + assert!(r.is_err()); + assert!(kinda_evil_mutex.scoped_try_lock(&mut key, |_| {}).is_err()); + assert!(evil_mutex.scoped_try_lock(&mut key, |_| {}).is_err()); + assert!(useless_mutex.scoped_try_lock(&mut key, |_| {}).is_ok()); +} diff --git a/tests/evil_unlock_rwlock.rs b/tests/evil_unlock_rwlock.rs new file mode 100644 index 0000000..58402c9 --- /dev/null +++ b/tests/evil_unlock_rwlock.rs @@ -0,0 +1,127 @@ +use std::sync::Arc; + +use happylock::collection::{BoxedLockCollection, RetryingLockCollection}; +use happylock::rwlock::RwLock; +use happylock::ThreadKey; +use lock_api::{GuardNoSend, RawRwLock}; + +struct KindaEvilRwLock { + inner: parking_lot::RawRwLock, +} + +struct EvilRwLock {} + +unsafe impl RawRwLock for KindaEvilRwLock { + #[allow(clippy::declare_interior_mutable_const)] + const INIT: Self = Self { + inner: parking_lot::RawRwLock::INIT, + }; + + type GuardMarker = GuardNoSend; + + fn lock_shared(&self) { + self.inner.lock_shared() + } + + fn try_lock_shared(&self) -> bool { + self.inner.try_lock_shared() + } + + unsafe fn unlock_shared(&self) { + panic!("mwahahahaha"); + } + + fn lock_exclusive(&self) { + self.inner.lock_exclusive() + } + + fn try_lock_exclusive(&self) -> bool { + self.inner.try_lock_exclusive() + } + + unsafe fn unlock_exclusive(&self) { + panic!("mwahahahaha"); + } +} + +unsafe impl RawRwLock for EvilRwLock { + #[allow(clippy::declare_interior_mutable_const)] + const INIT: Self = Self {}; + + type GuardMarker = GuardNoSend; + + fn lock_shared(&self) { + panic!("mwahahahaha"); + } + + fn try_lock_shared(&self) -> bool { + panic!("mwahahahaha"); + } + + unsafe fn unlock_shared(&self) { + panic!("mwahahahaha"); + } + + fn lock_exclusive(&self) { + panic!("mwahahahaha"); + } + + fn try_lock_exclusive(&self) -> bool { + panic!("mwahahahaha") + } + + unsafe fn unlock_exclusive(&self) { + panic!("mwahahahaha"); + } +} + +#[test] +fn boxed_rwlocks() { + let mut key = ThreadKey::get().unwrap(); + let kinda_evil_mutex: RwLock<i32, KindaEvilRwLock> = RwLock::new(5); + let evil_mutex: RwLock<i32, EvilRwLock> = RwLock::new(7); + let useless_mutex: RwLock<i32, parking_lot::RawRwLock> = RwLock::new(10); + + let r = std::thread::scope(|s| { + let r = s + .spawn(|| { + let key = ThreadKey::get().unwrap(); + let collection = + BoxedLockCollection::try_new((&kinda_evil_mutex, &evil_mutex, &useless_mutex)) + .unwrap(); + _ = collection.read(key); + }) + .join(); + + r + }); + + assert!(r.is_err()); + assert!(kinda_evil_mutex.scoped_try_write(&mut key, |_| {}).is_err()); + assert!(evil_mutex.scoped_try_write(&mut key, |_| {}).is_err()); + assert!(useless_mutex.scoped_try_write(&mut key, |_| {}).is_ok()); +} + +#[test] +fn retrying_rwlocks() { + let mut key = ThreadKey::get().unwrap(); + let kinda_evil_mutex: Arc<RwLock<i32, KindaEvilRwLock>> = Arc::new(RwLock::new(5)); + let evil_mutex: Arc<RwLock<i32, EvilRwLock>> = Arc::new(RwLock::new(7)); + let useless_mutex: Arc<RwLock<i32, parking_lot::RawRwLock>> = Arc::new(RwLock::new(10)); + let c_good = Arc::clone(&kinda_evil_mutex); + let c_evil = Arc::clone(&evil_mutex); + let c_useless = Arc::clone(&useless_mutex); + + let r = std::thread::spawn(move || { + let key = ThreadKey::get().unwrap(); + let collection = + RetryingLockCollection::try_new((&*c_good, &*c_evil, &*c_useless)).unwrap(); + collection.read(key); + }) + .join(); + + assert!(r.is_err()); + assert!(kinda_evil_mutex.scoped_try_write(&mut key, |_| {}).is_err()); + assert!(evil_mutex.scoped_try_write(&mut key, |_| {}).is_err()); + assert!(useless_mutex.scoped_try_write(&mut key, |_| {}).is_ok()); +} diff --git a/tests/forget.rs b/tests/forget.rs new file mode 100644 index 0000000..755bdb1 --- /dev/null +++ b/tests/forget.rs @@ -0,0 +1,24 @@ +use happylock::{Mutex, ThreadKey}; + +#[test] +fn no_new_threadkey_when_forgetting_lock() { + let key = ThreadKey::get().unwrap(); + let mutex = Mutex::new("foo".to_string()); + + let guard = mutex.lock(key); + std::mem::forget(guard); + + assert!(ThreadKey::get().is_none()); +} + +#[test] +fn no_new_threadkey_in_scoped_lock() { + let mut key = ThreadKey::get().unwrap(); + let mutex = Mutex::new("foo".to_string()); + + mutex.scoped_lock(&mut key, |_| { + assert!(ThreadKey::get().is_none()); + }); + + mutex.lock(key); +} diff --git a/tests/retry.rs b/tests/retry.rs new file mode 100644 index 0000000..64e0918 --- /dev/null +++ b/tests/retry.rs @@ -0,0 +1,53 @@ +use std::time::Duration; + +use happylock::{collection::RetryingLockCollection, Mutex, ThreadKey}; + +static MUTEX_1: Mutex<i32> = Mutex::new(1); +static MUTEX_2: Mutex<i32> = Mutex::new(2); +static MUTEX_3: Mutex<i32> = Mutex::new(3); + +fn thread_1() { + let key = ThreadKey::get().unwrap(); + let mut guard = MUTEX_2.lock(key); + std::thread::sleep(Duration::from_millis(100)); + *guard = 5; +} + +fn thread_2() { + let mut key = ThreadKey::get().unwrap(); + std::thread::sleep(Duration::from_millis(50)); + let collection = RetryingLockCollection::try_new([&MUTEX_1, &MUTEX_2, &MUTEX_3]).unwrap(); + collection.scoped_lock(&mut key, |guard| { + assert_eq!(*guard[0], 4); + assert_eq!(*guard[1], 5); + assert_eq!(*guard[2], 3); + }); +} + +fn thread_3() { + let key = ThreadKey::get().unwrap(); + std::thread::sleep(Duration::from_millis(75)); + let mut guard = MUTEX_1.lock(key); + std::thread::sleep(Duration::from_millis(100)); + *guard = 4; +} + +fn thread_4() { + let mut key = ThreadKey::get().unwrap(); + std::thread::sleep(Duration::from_millis(25)); + let collection = RetryingLockCollection::try_new([&MUTEX_1, &MUTEX_2]).unwrap(); + assert!(collection.scoped_try_lock(&mut key, |_| {}).is_err()); +} + +#[test] +fn retries() { + let t1 = std::thread::spawn(thread_1); + let t2 = std::thread::spawn(thread_2); + let t3 = std::thread::spawn(thread_3); + let t4 = std::thread::spawn(thread_4); + + t1.join().unwrap(); + t2.join().unwrap(); + t3.join().unwrap(); + t4.join().unwrap(); +} diff --git a/tests/retry_rw.rs b/tests/retry_rw.rs new file mode 100644 index 0000000..976ab14 --- /dev/null +++ b/tests/retry_rw.rs @@ -0,0 +1,34 @@ +use std::time::Duration; + +use happylock::{collection::RetryingLockCollection, RwLock, ThreadKey}; + +static RWLOCK_1: RwLock<i32> = RwLock::new(1); +static RWLOCK_2: RwLock<i32> = RwLock::new(2); +static RWLOCK_3: RwLock<i32> = RwLock::new(3); + +fn thread_1() { + let key = ThreadKey::get().unwrap(); + let mut guard = RWLOCK_2.write(key); + std::thread::sleep(Duration::from_millis(75)); + assert_eq!(*guard, 2); + *guard = 5; +} + +fn thread_2() { + let key = ThreadKey::get().unwrap(); + let collection = RetryingLockCollection::try_new([&RWLOCK_1, &RWLOCK_2, &RWLOCK_3]).unwrap(); + std::thread::sleep(Duration::from_millis(25)); + let guard = collection.read(key); + assert_eq!(*guard[0], 1); + assert_eq!(*guard[1], 5); + assert_eq!(*guard[2], 3); +} + +#[test] +fn retries() { + let t1 = std::thread::spawn(thread_1); + let t2 = std::thread::spawn(thread_2); + + t1.join().unwrap(); + t2.join().unwrap(); +} |
