summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMica White <botahamec@outlook.com>2026-08-26 20:46:31 -0400
committerMica White <botahamec@outlook.com>2026-08-26 20:46:31 -0400
commit55b3a2425b242fbc5c6e471f220eeb8b949e8751 (patch)
tree5ceb7910b60cc7331024b7ce1d49ec259e0302a8
parent6f6e030ea7edb9d155ebf21b5d42936c20801b50 (diff)
Add tests
-rwxr-xr-xsrc/collection/boxed.rs2
-rwxr-xr-xsrc/collection/owned.rs40
-rw-r--r--src/context.rs617
-rw-r--r--src/context/context.rs158
-rw-r--r--src/context/guard.rs (renamed from src/iterator/guard.rs)16
-rw-r--r--src/context/iterator.rs695
-rw-r--r--src/context/tuple.rs847
-rw-r--r--src/iterator.rs29
-rw-r--r--src/iterator/context.rs52
-rw-r--r--src/iterator/iterator.rs118
-rw-r--r--src/iterator/tuple.rs202
-rwxr-xr-xsrc/lib.rs5
-rwxr-xr-xsrc/lockable.rs6
-rwxr-xr-xsrc/mutex.rs2
-rwxr-xr-xsrc/poisonable/poisonable.rs22
-rwxr-xr-xtarpaulin-report.html297
16 files changed, 2606 insertions, 502 deletions
diff --git a/src/collection/boxed.rs b/src/collection/boxed.rs
index 1478120..66e6df0 100755
--- a/src/collection/boxed.rs
+++ b/src/collection/boxed.rs
@@ -721,6 +721,8 @@ impl<L: Sharable> BoxedLockCollection<L> {
}
}
+impl<L: OwnedLockable> BoxedLockCollection<L> {}
+
impl<L: LockableIntoInner> BoxedLockCollection<L> {
/// Consumes this `BoxedLockCollection`, returning the underlying data.
///
diff --git a/src/collection/owned.rs b/src/collection/owned.rs
index 07a5402..483bc09 100755
--- a/src/collection/owned.rs
+++ b/src/collection/owned.rs
@@ -1,4 +1,4 @@
-use crate::iterator::LockContext;
+use crate::context::LockContext;
use crate::lockable::{
Lockable, LockableGetMut, LockableIntoInner, OwnedLockable, RawLock, Sharable,
};
@@ -61,7 +61,7 @@ unsafe impl<L: Lockable> Lockable for OwnedLockCollection<L> {
where
Self: 'a;
- #[mutants::skip] // It's hard to test lkocks in an OwnedLockCollection, because they're owned
+ #[mutants::skip] // It's hard to test locks in an OwnedLockCollection, because they're owned
#[cfg(not(tarpaulin_include))]
fn get_ptrs<'a>(&'a self, ptrs: &mut Vec<&'a dyn RawLock>) {
// It's ok to use self here, because the values in the collection already
@@ -350,6 +350,38 @@ impl<L: OwnedLockable> OwnedLockCollection<L> {
Ok(LockGuard { guard, key })
}
+ /// Creates a context that can be used to iterate over the items in order.
+ ///
+ /// Sometimes, partial allocation of locks is useful. For example, you may
+ /// want to acquire a lock on the first element of a list before deciding if
+ /// the second element should be locked. This function creates a
+ /// [`LockContext`] which is capable of doing exactly that.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use happylock::{Mutex, ThreadKey};
+ /// use happylock::collection::OwnedLockCollection;
+ ///
+ /// let key = ThreadKey::get().unwrap();
+ /// let data = (Mutex::new(true), Mutex::new(42), Mutex::new(67));
+ /// let locks = OwnedLockCollection::new(data);
+ /// let mut ctx = locks.context();
+ /// let tuple = ctx.tuple(key);
+ ///
+ /// let (use_other, tuple) = tuple.lock_0();
+ /// let number = if **use_other {
+ /// tuple.lock_2().0
+ /// } else {
+ /// tuple.lock_1().0
+ /// };
+ /// assert_eq!(**number, 67);
+ /// ```
+ #[must_use]
+ pub const fn context(&self) -> LockContext<'_, L> {
+ LockContext::new(&self.child)
+ }
+
/// Unlocks the underlying lockable data type, returning the key that's
/// associated with it.
///
@@ -559,10 +591,6 @@ impl<L: Sharable> OwnedLockCollection<L> {
}
impl<L> OwnedLockCollection<L> {
- pub const fn context(&self) -> LockContext<'_, L> {
- LockContext::new(&self.child)
- }
-
/// Gets the underlying collection, consuming this collection.
///
/// # Examples
diff --git a/src/context.rs b/src/context.rs
new file mode 100644
index 0000000..2f9ca64
--- /dev/null
+++ b/src/context.rs
@@ -0,0 +1,617 @@
+use std::marker::PhantomData;
+
+use crate::ThreadKey;
+
+mod context;
+mod guard;
+pub mod iterator;
+pub mod tuple;
+
+/// Allows iterating over a lock collection, without locking every element at
+/// once.
+///
+/// Sometimes, partial allocation of locks is useful. For example, you may
+/// want to acquire a lock on the first element of a list before deciding if
+/// the second element should be locked. This function creates a
+/// [`LockContext`] which is capable of doing exactly that.
+///
+/// Upon using this context, the [`ThreadKey`] is stored inside this context.
+/// This ensures that nothing else, besides the types exposed by the context,
+/// can be locked until this context is dropped. To re-acquire the `ThreadKey`,
+/// call [`LockContext::unlock`].
+///
+/// A [`LockContext`] can be created by calling [`OwnedLockCollection::context`].
+///
+/// # Examples
+///
+/// Iterating through a tuple.
+///
+/// ```
+/// use happylock::{Mutex, ThreadKey};
+/// use happylock::collection::OwnedLockCollection;
+///
+/// let key = ThreadKey::get().unwrap();
+/// let data = (Mutex::new(true), Mutex::new(42), Mutex::new(67));
+/// let locks = OwnedLockCollection::new(data);
+/// let mut ctx = locks.context();
+/// let tuple = ctx.tuple(key);
+///
+/// let (use_other, tuple) = tuple.lock_0();
+/// let number = if **use_other {
+/// tuple.lock_2().0
+/// } else {
+/// tuple.lock_1().0
+/// };
+/// assert_eq!(**number, 67);
+/// ```
+///
+/// Iterating through a list
+///
+/// ```
+/// use happylock::{Mutex, ThreadKey};
+/// use happylock::collection::OwnedLockCollection;
+///
+/// let key = ThreadKey::get().unwrap();
+/// let data = [Mutex::new(1), Mutex::new(3), Mutex::new(8)];
+/// let locks = OwnedLockCollection::new(data);
+/// let mut ctx = locks.context();
+/// let mut iter = ctx.iter(key);
+///
+/// let mut sum = 0;
+/// while let Some(item) = iter.lock_next() {
+/// sum += **item;
+/// }
+///
+/// assert_eq!(sum, 12);
+/// ```
+///
+/// [`OwnedLockCollection::context`]: crate::collection::OwnedLockCollection::context
+pub struct LockContext<'l, L> {
+ key: Option<ThreadKey>,
+ lockable: &'l L,
+}
+
+/// Iterates through a collection of locks, allowing for partial allocation of
+/// locks, or for some locks to be skipped.
+///
+/// Sometimes, partial allocation of locks is useful. For example, you may
+/// want to acquire a lock on the first element of a list before deciding if
+/// the second element should be locked. If the list is iterable, then a
+/// [`LockingIterator`] is capable of doing exactly that.
+///
+/// A [`LockingIterator`] can be created by calling the [`LockContext::iter`]
+/// method.
+///
+/// # Example
+///
+/// ```
+/// use happylock::{Mutex, ThreadKey};
+/// use happylock::collection::OwnedLockCollection;
+///
+/// let key = ThreadKey::get().unwrap();
+/// let data = [Mutex::new(1), Mutex::new(3), Mutex::new(8)];
+/// let locks = OwnedLockCollection::new(data);
+/// let mut ctx = locks.context();
+/// let mut iter = ctx.iter(key);
+///
+/// let mut sum = 0;
+/// while let Some(item) = iter.lock_next() {
+/// sum += **item;
+/// }
+///
+/// assert_eq!(sum, 12);
+/// ```
+pub struct LockingIterator<'context, I, Outer = ()> {
+ key: &'context ThreadKey,
+ iterator: I,
+ outer: Outer,
+}
+
+/// Iterates through a tuple of locks, requiring that elements are only locked
+/// before any successive elements are locked.
+///
+/// Sometimes, partial allocation of locks is useful. For example, you may want
+/// to acquire a lock on one item before deciding if the second item should be
+/// locked. If the locks can be organized into a tuple, [`LockingTuple`] is
+/// capable of doing exactly that.
+///
+/// A [`LockingTuple`] can be created by calling the [`LockContext::iter`]
+/// method.
+///
+/// # Example
+///
+/// ```
+/// use happylock::{Mutex, ThreadKey};
+/// use happylock::collection::OwnedLockCollection;
+///
+/// let key = ThreadKey::get().unwrap();
+/// let data = (Mutex::new(true), Mutex::new(42), Mutex::new(67));
+/// let locks = OwnedLockCollection::new(data);
+/// let mut ctx = locks.context();
+/// let tuple = ctx.tuple(key);
+///
+/// let (use_other, tuple) = tuple.lock_0();
+/// let number = if **use_other {
+/// tuple.lock_2().0
+/// } else {
+/// tuple.lock_1().0
+/// };
+/// assert_eq!(**number, 67);
+/// ```
+pub struct LockingTuple<'context, L, C, Outer = ()> {
+ _lockable: PhantomData<L>,
+ key: &'context ThreadKey,
+ tuple: &'context C,
+ outer: Outer,
+}
+
+/// An RAII implementation of a “scoped lock”. When this structure
+/// is dropped (falls out of scope), the lock will be unlocked.
+///
+/// The data protected by the mutex can be accessed through this guard via its
+/// [`Deref`] and [`DerefMut`] implementations.
+///
+/// This is created by calling the [`lock`] and [`try_lock`] methods on [`Mutex`]
+///
+/// Unlike other guards in this crate, this guard holds a reference to a
+/// [`ThreadKey`], which is stored in the [`LockContext`]. This ensures that
+/// context cannot be dropped until all guards created with the context are
+/// dropped. The `ThreadKey` can be re-acquired by calling
+/// [`LockContext::unlock`].
+///
+/// [`Mutex`]: `crate::mutex::Mutex`
+/// [`Deref`]: `std::ops::Deref`
+/// [`DerefMut`]: `std::ops::DerefMut`
+/// [`lock`]: `crate::mutex::Mutex::lock`
+/// [`try_lock`]: `crate::Mutex::try_lock`
+pub struct ContextGuard<'a, Guard, Key> {
+ _key: &'a Key,
+ guard: Guard,
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::{
+ collection::OwnedLockCollection, context::iterator::TryLockNextError, Mutex, RwLock,
+ ThreadKey,
+ };
+
+ #[test]
+ fn display_works_for_guard() {
+ let key = ThreadKey::get().unwrap();
+ let collection = OwnedLockCollection::new((Mutex::new("Hello, world!"),));
+ let mut context = collection.context();
+ let tuple = context.tuple(key);
+ let (guard, _) = tuple.lock_0();
+ assert_eq!(guard.to_string(), "Hello, world!".to_string());
+ }
+
+ #[test]
+ fn try_lock_mut_works_single_element_tuple() {
+ let key = ThreadKey::get().unwrap();
+ let collection = OwnedLockCollection::new((RwLock::new(42),));
+ let mut context = collection.context();
+ let mut tuple = context.tuple(key);
+ let mut guard = tuple.try_lock_mut_0().unwrap();
+ **guard = 67;
+ std::thread::scope(|s| {
+ s.spawn(|| {
+ let key = ThreadKey::get().unwrap();
+ let mut context = collection.context();
+ let mut tuple = context.tuple(key);
+ let guard = tuple.try_read_mut_0();
+ assert!(guard.is_none());
+ });
+ });
+ drop(guard);
+
+ let result = tuple.try_read_mut_0().unwrap();
+ std::thread::scope(|s| {
+ s.spawn(|| {
+ let key = ThreadKey::get().unwrap();
+ let mut context = collection.context();
+ let mut tuple = context.tuple(key);
+ let guard = tuple.try_lock_mut_0();
+ assert!(guard.is_none());
+ drop(guard);
+ let guard = tuple.try_read_mut_0();
+ assert!(guard.is_some());
+ drop(guard);
+ });
+ });
+ assert_eq!(**result, 67);
+ }
+
+ #[test]
+ fn try_lock_mut_works_double_element_tuple() {
+ let key = ThreadKey::get().unwrap();
+ let collection = OwnedLockCollection::new((RwLock::new(42), RwLock::new(67)));
+ let mut context = collection.context();
+ let mut tuple = context.tuple(key);
+
+ let mut guard = tuple.try_lock_mut_0().unwrap();
+ **guard *= 2;
+ std::thread::scope(|s| {
+ s.spawn(|| {
+ let key = ThreadKey::get().unwrap();
+ let mut context = collection.context();
+ let mut tuple = context.tuple(key);
+ let guard = tuple.try_read_mut_0();
+ assert!(guard.is_none());
+ });
+ });
+ drop(guard);
+ let mut guard = tuple.try_lock_mut_1().unwrap();
+ **guard *= 2;
+ std::thread::scope(|s| {
+ s.spawn(|| {
+ let key = ThreadKey::get().unwrap();
+ let mut context = collection.context();
+ let mut tuple = context.tuple(key);
+ let guard = tuple.try_read_mut_1();
+ assert!(guard.is_none());
+ });
+ });
+ drop(guard);
+
+ let result = tuple.try_read_mut_0().unwrap();
+ assert_eq!(**result, 84);
+ std::thread::scope(|s| {
+ s.spawn(|| {
+ let key = ThreadKey::get().unwrap();
+ let mut context = collection.context();
+ let mut tuple = context.tuple(key);
+ let guard = tuple.try_lock_mut_0();
+ assert!(guard.is_none());
+ drop(guard);
+ let guard = tuple.try_read_mut_0();
+ assert!(guard.is_some());
+ drop(guard);
+ });
+ });
+ drop(result);
+ let result = tuple.try_read_mut_1().unwrap();
+ std::thread::scope(|s| {
+ s.spawn(|| {
+ let key = ThreadKey::get().unwrap();
+ let mut context = collection.context();
+ let mut tuple = context.tuple(key);
+ let guard = tuple.try_lock_mut_1();
+ assert!(guard.is_none());
+ drop(guard);
+ let guard = tuple.try_read_mut_1();
+ assert!(guard.is_some());
+ drop(guard);
+ });
+ });
+ assert_eq!(**result, 134);
+ }
+
+ #[test]
+ fn try_lock_mut_works_triple_element_tuple() {
+ let key = ThreadKey::get().unwrap();
+ let collection = OwnedLockCollection::new((RwLock::new(1), RwLock::new(2), RwLock::new(3)));
+ let mut context = collection.context();
+ let mut tuple = context.tuple(key);
+
+ let mut guard = tuple.try_lock_mut_0().unwrap();
+ **guard *= 2;
+ std::thread::scope(|s| {
+ s.spawn(|| {
+ let key = ThreadKey::get().unwrap();
+ let mut context = collection.context();
+ let mut tuple = context.tuple(key);
+ let guard = tuple.try_read_mut_0();
+ assert!(guard.is_none());
+ });
+ });
+ drop(guard);
+ let mut guard = tuple.try_lock_mut_1().unwrap();
+ **guard *= 2;
+ std::thread::scope(|s| {
+ s.spawn(|| {
+ let key = ThreadKey::get().unwrap();
+ let mut context = collection.context();
+ let mut tuple = context.tuple(key);
+ let guard = tuple.try_read_mut_1();
+ assert!(guard.is_none());
+ });
+ });
+ drop(guard);
+ let mut guard = tuple.try_lock_mut_2().unwrap();
+ **guard *= 2;
+ std::thread::scope(|s| {
+ s.spawn(|| {
+ let key = ThreadKey::get().unwrap();
+ let mut context = collection.context();
+ let mut tuple = context.tuple(key);
+ let guard = tuple.try_read_mut_2();
+ assert!(guard.is_none());
+ });
+ });
+ drop(guard);
+
+ let result = tuple.try_read_mut_0().unwrap();
+ assert_eq!(**result, 2);
+ std::thread::scope(|s| {
+ s.spawn(|| {
+ let key = ThreadKey::get().unwrap();
+ let mut context = collection.context();
+ let mut tuple = context.tuple(key);
+ let guard = tuple.try_lock_mut_0();
+ assert!(guard.is_none());
+ drop(guard);
+ let guard = tuple.try_read_mut_0();
+ assert!(guard.is_some());
+ drop(guard);
+ });
+ });
+ drop(result);
+ let result = tuple.try_read_mut_1().unwrap();
+ assert_eq!(**result, 4);
+ std::thread::scope(|s| {
+ s.spawn(|| {
+ let key = ThreadKey::get().unwrap();
+ let mut context = collection.context();
+ let mut tuple = context.tuple(key);
+ let guard = tuple.try_lock_mut_1();
+ assert!(guard.is_none());
+ drop(guard);
+ let guard = tuple.try_read_mut_1();
+ assert!(guard.is_some());
+ drop(guard);
+ });
+ });
+ drop(result);
+ let result = tuple.try_read_mut_2().unwrap();
+ std::thread::scope(|s| {
+ s.spawn(|| {
+ let key = ThreadKey::get().unwrap();
+ let mut context = collection.context();
+ let mut tuple = context.tuple(key);
+ let guard = tuple.try_lock_mut_2();
+ assert!(guard.is_none());
+ drop(guard);
+ let guard = tuple.try_read_mut_2();
+ assert!(guard.is_some());
+ drop(guard);
+ });
+ });
+ assert_eq!(**result, 6);
+ }
+
+ #[test]
+ fn basic_iteration() {
+ let key = ThreadKey::get().unwrap();
+ let data = [Mutex::new(1), Mutex::new(3), Mutex::new(8)];
+ let locks = OwnedLockCollection::new(data);
+ let mut ctx = locks.context();
+ let mut iter = ctx.iter(key);
+
+ let item = iter.lock_next().unwrap();
+ assert_eq!(**item, 1);
+ let item = iter.lock_next().unwrap();
+ assert_eq!(**item, 3);
+ let item = iter.lock_next().unwrap();
+ assert_eq!(**item, 8);
+ assert!(iter.lock_next().is_none());
+ }
+
+ #[test]
+ fn recurse_tuple_of_lists() {
+ let key = ThreadKey::get().unwrap();
+ let data = (
+ [Mutex::new(1), Mutex::new(2), Mutex::new(3)],
+ Mutex::new(true),
+ );
+ let locks = OwnedLockCollection::new(data);
+ let mut ctx = locks.context();
+ let tuple = ctx.tuple(key);
+ let mut iter = tuple.recurse_0_iter();
+
+ let item = iter.lock_next().unwrap();
+ assert_eq!(**item, 1);
+ let item = iter.lock_next().unwrap();
+ assert_eq!(**item, 2);
+ let item = iter.lock_next().unwrap();
+ assert_eq!(**item, 3);
+ assert!(iter.lock_next().is_none());
+
+ let tuple = iter.exit();
+ let (should_assert, _) = tuple.lock_1();
+ assert!(**should_assert);
+ }
+
+ #[test]
+ fn recurse_list_of_lists() {
+ let key = ThreadKey::get().unwrap();
+ let data = [
+ [Mutex::new(1), Mutex::new(2), Mutex::new(3)],
+ [Mutex::new(4), Mutex::new(5), Mutex::new(6)],
+ ];
+ let locks = OwnedLockCollection::new(data);
+ let mut ctx = locks.context();
+ let mut iter = ctx.iter(key);
+
+ let mut list = iter.recurse_next().unwrap();
+ let item = list.lock_next().unwrap();
+ assert_eq!(**item, 1);
+ let item = list.lock_next().unwrap();
+ assert_eq!(**item, 2);
+ let item = list.lock_next().unwrap();
+ assert_eq!(**item, 3);
+ assert!(list.lock_next().is_none());
+ iter = list.exit();
+ let mut list = iter.recurse_next().unwrap();
+ let item = list.lock_next().unwrap();
+ assert_eq!(**item, 4);
+ let item = list.lock_next().unwrap();
+ assert_eq!(**item, 5);
+ let item = list.lock_next().unwrap();
+ assert_eq!(**item, 6);
+ assert!(list.lock_next().is_none());
+ }
+
+ #[test]
+ fn recurse_last_list_of_lists() {
+ let key = ThreadKey::get().unwrap();
+ let data = [
+ [Mutex::new(1), Mutex::new(2), Mutex::new(3)],
+ [Mutex::new(4), Mutex::new(5), Mutex::new(6)],
+ ];
+ let locks = OwnedLockCollection::new(data);
+ let mut ctx = locks.context();
+ let iter = ctx.iter(key);
+
+ let mut list = iter.recurse_last().unwrap();
+ let item = list.lock_next().unwrap();
+ assert_eq!(**item, 4);
+ let item = list.lock_next().unwrap();
+ assert_eq!(**item, 5);
+ let item = list.lock_next().unwrap();
+ assert_eq!(**item, 6);
+ assert!(list.lock_next().is_none());
+ }
+
+ #[test]
+ fn recurse_last_list_of_empty_list() {
+ let key = ThreadKey::get().unwrap();
+ let data: [[Mutex<i32>; 0]; 0] = [];
+ let locks = OwnedLockCollection::new(data);
+ let mut ctx = locks.context();
+ let iter = ctx.iter(key);
+
+ let list = iter.recurse_last();
+ assert!(list.is_none())
+ }
+
+ #[test]
+ fn recurse_list_of_tuples() {
+ let key = ThreadKey::get().unwrap();
+ let data = [
+ (Mutex::new(true), Mutex::new(1)),
+ (Mutex::new(false), Mutex::new(2)),
+ (Mutex::new(true), Mutex::new(3)),
+ ];
+ let locks = OwnedLockCollection::new(data);
+ let mut ctx = locks.context();
+ let mut iter = ctx.iter(key);
+
+ let tuple = iter.recurse_next_tuple().unwrap();
+ let (should_count, mut tuple) = tuple.lock_0();
+ assert!(**should_count);
+ let num = tuple.lock_mut_1();
+ assert_eq!(**num, 1);
+ drop(num);
+ iter = tuple.exit();
+ let tuple = iter.recurse_next_tuple().unwrap();
+ let (should_count, mut tuple) = tuple.lock_0();
+ assert!(!**should_count);
+ let num = tuple.lock_mut_1();
+ assert_eq!(**num, 2);
+ drop(num);
+ iter = tuple.exit();
+ let tuple = iter.recurse_next_tuple().unwrap();
+ let (should_count, mut tuple) = tuple.lock_0();
+ assert!(**should_count);
+ let num = tuple.lock_mut_1();
+ assert_eq!(**num, 3);
+ drop(num);
+ iter = tuple.exit();
+ assert!(iter.recurse_next_tuple().is_none());
+ }
+
+ #[test]
+ fn recurse_last_list_of_tuples() {
+ let key = ThreadKey::get().unwrap();
+ let data = [
+ (Mutex::new(true), Mutex::new(1)),
+ (Mutex::new(false), Mutex::new(2)),
+ (Mutex::new(true), Mutex::new(3)),
+ ];
+ let locks = OwnedLockCollection::new(data);
+ let mut ctx = locks.context();
+ let iter = ctx.iter(key);
+
+ let tuple = iter.recurse_last_tuple().unwrap();
+ let (should_count, mut tuple) = tuple.lock_0();
+ if **should_count {
+ let num = tuple.lock_mut_1();
+ assert_eq!(**num, 3);
+ } else {
+ panic!();
+ }
+ }
+
+ #[test]
+ fn recurse_last_of_empty_list_of_tuples() {
+ let key = ThreadKey::get().unwrap();
+ let data: [(Mutex<bool>, Mutex<i32>); 0] = [];
+ let locks = OwnedLockCollection::new(data);
+ let mut ctx = locks.context();
+ let iter = ctx.iter(key);
+
+ let tuple = iter.recurse_last_tuple();
+ assert!(tuple.is_none());
+ }
+
+ #[test]
+ fn lock_last_of_list() {
+ let key = ThreadKey::get().unwrap();
+ let data = [Mutex::new(1), Mutex::new(3), Mutex::new(8)];
+ let locks = OwnedLockCollection::new(data);
+ let mut ctx = locks.context();
+ let iter = ctx.iter(key);
+ let last = iter.lock_last().unwrap();
+ assert_eq!(**last, 8);
+ }
+
+ #[test]
+ fn try_lock_next_works() {
+ let key = ThreadKey::get().unwrap();
+ let data = [Mutex::new(1), Mutex::new(3), Mutex::new(8)];
+ let locks = OwnedLockCollection::new(data);
+ let mut ctx = locks.context();
+ let mut iter = ctx.iter(key).peekable();
+
+ let item = iter.try_lock_next().unwrap();
+ std::thread::scope(|s| {
+ s.spawn(|| {
+ let key = ThreadKey::get().unwrap();
+ let mut context = locks.context();
+ let mut iter = context.iter(key).peekable();
+ let guard = iter.try_lock_next();
+ assert_eq!(guard.unwrap_err(), TryLockNextError::WouldBlock);
+ });
+ });
+ assert_eq!(**item, 1);
+ let item = iter.try_lock_next().unwrap();
+ std::thread::scope(|s| {
+ s.spawn(|| {
+ let key = ThreadKey::get().unwrap();
+ let mut context = locks.context();
+ let mut iter = context.iter(key).peekable();
+ iter.skip_next();
+ let guard = iter.try_lock_next();
+ assert_eq!(guard.unwrap_err(), TryLockNextError::WouldBlock);
+ });
+ });
+ assert_eq!(**item, 3);
+ let item = iter.try_lock_next().unwrap();
+ std::thread::scope(|s| {
+ s.spawn(|| {
+ let key = ThreadKey::get().unwrap();
+ let mut context = locks.context();
+ let mut iter = context.iter(key).peekable();
+ iter.skip_mut(2);
+ let guard = iter.try_lock_next();
+ assert_eq!(guard.unwrap_err(), TryLockNextError::WouldBlock);
+ });
+ });
+ assert_eq!(**item, 8);
+ assert_eq!(
+ iter.try_lock_next().unwrap_err(),
+ TryLockNextError::FinishedIteration
+ );
+ }
+}
diff --git a/src/context/context.rs b/src/context/context.rs
new file mode 100644
index 0000000..adc684f
--- /dev/null
+++ b/src/context/context.rs
@@ -0,0 +1,158 @@
+use std::marker::PhantomData;
+
+use crate::{
+ context::{LockContext, LockingIterator, LockingTuple},
+ lockable::{Lockable, OwnedLockable},
+ ThreadKey,
+};
+
+impl<'l, L> LockContext<'l, L> {
+ pub(crate) const fn new(lockable: &'l L) -> Self
+ where
+ L: OwnedLockable,
+ {
+ Self {
+ key: None,
+ lockable,
+ }
+ }
+
+ /// Unlocks all locks in the collection, returning the [`ThreadKey`].
+ ///
+ /// This requires a mutable reference to the context, so it cannot be called
+ /// without first dropping any [`ContextGuard`]s that reference this context.
+ /// This method will also return `None` if the context has not been locked
+ /// with a `ThreadKey`.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use happylock::{Mutex, ThreadKey};
+ /// use happylock::collection::OwnedLockCollection;
+ ///
+ /// let key = ThreadKey::get().unwrap();
+ /// let data = (Mutex::new(42), Mutex::new(true));
+ /// let locks = OwnedLockCollection::new(data);
+ /// let mut ctx = locks.context();
+ /// let tuple = ctx.tuple(key);
+ ///
+ /// let (use_other, tuple) = tuple.lock_1();
+ /// if **use_other {
+ /// drop(use_other);
+ /// drop(tuple);
+ /// let key = ctx.unlock().unwrap();
+ /// let tuple = ctx.tuple(key);
+ /// let (mut item, _) = tuple.lock_0();
+ /// **item = 67;
+ /// } else {
+ /// drop(use_other);
+ /// drop(tuple);
+ /// };
+ ///
+ /// let key = ctx.unlock().unwrap();
+ /// let tuple = ctx.tuple(key);
+ /// let (number, _) = tuple.lock_0();
+ /// assert_eq!(**number, 67);
+ /// ```
+ ///
+ /// [`ContextGuard`]: `crate::context::ContextGuard`
+ pub fn unlock(&mut self) -> Option<ThreadKey> {
+ self.key.take()
+ }
+}
+
+impl<L: Lockable> LockContext<'_, L> {
+ /// Creates a [`LockingTuple`], which can lock a subset of a tuple of locks,
+ /// in a specific order.
+ ///
+ /// Sometimes, partial allocation of locks is useful. For example, you may want
+ /// to acquire a lock on one item before deciding if the second item should be
+ /// locked. If the locks can be organized into a tuple, [`LockingTuple`] is
+ /// capable of doing exactly that.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use happylock::{Mutex, ThreadKey};
+ /// use happylock::collection::OwnedLockCollection;
+ ///
+ /// let key = ThreadKey::get().unwrap();
+ /// let data = (Mutex::new(true), Mutex::new(42), Mutex::new(67));
+ /// let locks = OwnedLockCollection::new(data);
+ /// let mut ctx = locks.context();
+ /// let tuple = ctx.tuple(key);
+ ///
+ /// let (use_other, tuple) = tuple.lock_0();
+ /// let number = if **use_other {
+ /// tuple.lock_2().0
+ /// } else {
+ /// tuple.lock_1().0
+ /// };
+ /// assert_eq!(**number, 67);
+ /// ```
+ pub fn tuple(&mut self, key: ThreadKey) -> LockingTuple<'_, L, L> {
+ unsafe {
+ self.key = Some(key);
+
+ LockingTuple {
+ _lockable: PhantomData,
+ // safety: we just inserted a key
+ key: self.key.as_ref().unwrap_unchecked(),
+ tuple: self.lockable,
+ outer: (),
+ }
+ }
+ }
+}
+
+impl<'l, L> LockContext<'l, L>
+where
+ &'l L: IntoIterator,
+{
+ /// Creates a [`LockingIterator`] to iterate through a collection of locks
+ /// without locking everything at once.
+ ///
+ /// Sometimes, partial allocation of locks is useful. For example, you may
+ /// want to acquire a lock on the first element of a list before deciding if
+ /// the second element should be locked. If the list is iterable, then a
+ /// [`LockingIterator`] is capable of doing exactly that.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use happylock::{Mutex, ThreadKey};
+ /// use happylock::collection::OwnedLockCollection;
+ ///
+ /// let key = ThreadKey::get().unwrap();
+ /// let data = [Mutex::new(1), Mutex::new(3), Mutex::new(8)];
+ /// let locks = OwnedLockCollection::new(data);
+ /// let mut ctx = locks.context();
+ /// let mut iter = ctx.iter(key);
+ ///
+ /// let mut sum = 0;
+ /// while let Some(item) = iter.lock_next() {
+ /// sum += **item;
+ /// }
+ ///
+ /// assert_eq!(sum, 12);
+ /// ```
+ // TODO: support scoped locks
+ // TODO: implement get_disjoint
+ // TODO: support some sort of index tower thing
+ #[expect(clippy::iter_not_returning_iterator)]
+ pub fn iter(
+ &mut self,
+ key: ThreadKey,
+ ) -> LockingIterator<'_, <&'l L as IntoIterator>::IntoIter> {
+ unsafe {
+ self.key = Some(key);
+
+ LockingIterator {
+ // safety: we just inserted a key
+ key: self.key.as_ref().unwrap_unchecked(),
+ iterator: self.lockable.into_iter(),
+ outer: (),
+ }
+ }
+ }
+}
diff --git a/src/iterator/guard.rs b/src/context/guard.rs
index cb220ac..0898c1f 100644
--- a/src/iterator/guard.rs
+++ b/src/context/guard.rs
@@ -2,11 +2,11 @@ use std::fmt::{Debug, Display};
use std::hash::Hash;
use std::ops::{Deref, DerefMut};
-use super::IteratorGuard;
+use super::ContextGuard;
#[mutants::skip] // hashing involves RNG and is hard to test
#[cfg(not(tarpaulin_include))]
-impl<Guard: Hash, Key> Hash for IteratorGuard<'_, Guard, Key> {
+impl<Guard: Hash, Key> Hash for ContextGuard<'_, Guard, Key> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.guard.hash(state)
}
@@ -19,19 +19,19 @@ impl<Guard: Hash, Key> Hash for IteratorGuard<'_, Guard, Key> {
#[mutants::skip]
#[cfg(not(tarpaulin_include))]
-impl<Guard: Debug, Key> Debug for IteratorGuard<'_, Guard, Key> {
+impl<Guard: Debug, Key> Debug for ContextGuard<'_, Guard, Key> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Debug::fmt(&**self, f)
}
}
-impl<Guard: Display, Key> Display for IteratorGuard<'_, Guard, Key> {
+impl<Guard: Display, Key> Display for ContextGuard<'_, Guard, Key> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Display::fmt(&**self, f)
}
}
-impl<Guard, Key> Deref for IteratorGuard<'_, Guard, Key> {
+impl<Guard, Key> Deref for ContextGuard<'_, Guard, Key> {
type Target = Guard;
fn deref(&self) -> &Self::Target {
@@ -39,19 +39,19 @@ impl<Guard, Key> Deref for IteratorGuard<'_, Guard, Key> {
}
}
-impl<Guard, Key> DerefMut for IteratorGuard<'_, Guard, Key> {
+impl<Guard, Key> DerefMut for ContextGuard<'_, Guard, Key> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.guard
}
}
-impl<Guard, Key> AsRef<Guard> for IteratorGuard<'_, Guard, Key> {
+impl<Guard, Key> AsRef<Guard> for ContextGuard<'_, Guard, Key> {
fn as_ref(&self) -> &Guard {
&self.guard
}
}
-impl<Guard, Key> AsMut<Guard> for IteratorGuard<'_, Guard, Key> {
+impl<Guard, Key> AsMut<Guard> for ContextGuard<'_, Guard, Key> {
fn as_mut(&mut self) -> &mut Guard {
&mut self.guard
}
diff --git a/src/context/iterator.rs b/src/context/iterator.rs
new file mode 100644
index 0000000..cc3bd7c
--- /dev/null
+++ b/src/context/iterator.rs
@@ -0,0 +1,695 @@
+use std::{
+ iter::{Fuse, Peekable, Skip, Take},
+ marker::PhantomData,
+};
+
+use super::{ContextGuard, LockingIterator};
+
+use crate::{
+ context::LockingTuple,
+ lockable::{Lockable, RawLock, Sharable},
+ ThreadKey,
+};
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum TryLockNextError {
+ FinishedIteration,
+ WouldBlock,
+}
+
+impl<'l, I, O> LockingIterator<'l, I, O> {
+ fn with_iterator<M>(self, f: impl FnOnce(I) -> M) -> LockingIterator<'l, M, O> {
+ LockingIterator {
+ key: self.key,
+ iterator: f(self.iterator),
+ outer: self.outer,
+ }
+ }
+
+ /// Exit out of the current scope of the locking iterator into the parent.
+ ///
+ /// After using one the recurse methods, it is possible to regain access to
+ /// the parent by exiting out of the scope of the child. Doing this will make
+ /// it impossible to re-enter this scope again.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use happylock::{Mutex, ThreadKey};
+ /// use happylock::collection::OwnedLockCollection;
+ ///
+ /// let key = ThreadKey::get().unwrap();
+ /// let data = ([Mutex::new(1), Mutex::new(2), Mutex::new(3)], Mutex::new(true));
+ /// let locks = OwnedLockCollection::new(data);
+ /// let mut ctx = locks.context();
+ /// let tuple = ctx.tuple(key);
+ /// let mut iter = tuple.recurse_0_iter();
+ ///
+ /// let mut sum = 0;
+ /// while let Some(item) = iter.lock_next() {
+ /// sum += **item;
+ /// }
+ ///
+ /// let tuple = iter.exit();
+ /// let (should_assert, _) = tuple.lock_1();
+ /// if **should_assert {
+ /// assert_eq!(sum, 6);
+ /// }
+ /// ```
+ pub fn exit(self) -> O {
+ self.outer
+ }
+}
+
+impl<'c, L: Iterator<Item = I>, I: IntoIterator, O> LockingIterator<'c, L, O> {
+ /// Create a new `LockingIterator` based on the next element in the iterator.
+ ///
+ /// If a list contains a list of locks, then this method can be used to
+ /// recurse into the next element of the list. To go back to the parent scope,
+ /// use [`LockingIterator::exit`] on the new list.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use happylock::{Mutex, ThreadKey};
+ /// use happylock::collection::OwnedLockCollection;
+ ///
+ /// let key = ThreadKey::get().unwrap();
+ /// let data = [
+ /// [Mutex::new(1), Mutex::new(2), Mutex::new(3)],
+ /// [Mutex::new(4), Mutex::new(5), Mutex::new(6)],
+ /// ];
+ /// let locks = OwnedLockCollection::new(data);
+ /// let mut ctx = locks.context();
+ /// let mut iter = ctx.iter(key);
+ ///
+ /// let mut sums = Vec::new();
+ /// while let Some(mut list) = iter.recurse_next() {
+ /// let mut sum = 0;
+ /// while let Some(item) = list.lock_next() {
+ /// sum += **item;
+ /// }
+ /// sums.push(sum);
+ /// iter = list.exit();
+ /// }
+ ///
+ /// assert_eq!(sums, vec![6, 15]);
+ /// ```
+ pub fn recurse_next(
+ mut self,
+ ) -> Option<LockingIterator<'c, <I as IntoIterator>::IntoIter, Self>> {
+ if let Some(iterator) = self.iterator.next() {
+ Some(LockingIterator {
+ key: self.key,
+ iterator: iterator.into_iter(),
+ outer: self,
+ })
+ } else {
+ None
+ }
+ }
+
+ /// Create a new `LockingIterator` based on the next element in the iterator.
+ ///
+ /// If a list contains a list of locks, then this method can be used to
+ /// recurse into the next element of the list. To go back to the parent scope,
+ /// use [`LockingIterator::exit`] on the new list.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use happylock::{Mutex, ThreadKey};
+ /// use happylock::collection::OwnedLockCollection;
+ ///
+ /// let key = ThreadKey::get().unwrap();
+ /// let data = [
+ /// [Mutex::new(1), Mutex::new(2), Mutex::new(3)],
+ /// [Mutex::new(4), Mutex::new(5), Mutex::new(6)],
+ /// ];
+ /// let locks = OwnedLockCollection::new(data);
+ /// let mut ctx = locks.context();
+ /// let iter = ctx.iter(key);
+ ///
+ /// let mut list = iter.recurse_last().unwrap();
+ /// let mut sum = 0;
+ /// while let Some(item) = list.lock_next() {
+ /// sum += **item;
+ /// }
+ ///
+ /// assert_eq!(sum, 15);
+ /// ```
+ pub fn recurse_last(self) -> Option<LockingIterator<'c, <I as IntoIterator>::IntoIter, O>> {
+ if let Some(iterator) = self.iterator.last() {
+ Some(LockingIterator {
+ key: self.key,
+ iterator: iterator.into_iter(),
+ outer: self.outer,
+ })
+ } else {
+ None
+ }
+ }
+}
+
+impl<'c, L: Iterator<Item = &'c T>, T: 'c, O> LockingIterator<'c, L, O> {
+ /// Create a new `LockingIterator` based on the next element in the iterator.
+ ///
+ /// If a list contains a list of locks, then this method can be used to
+ /// recurse into the next element of the list. To go back to the parent scope,
+ /// use [`LockingIterator::exit`] on the new list.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use happylock::{Mutex, ThreadKey};
+ /// use happylock::collection::OwnedLockCollection;
+ ///
+ /// let key = ThreadKey::get().unwrap();
+ /// let data = [
+ /// (Mutex::new(true), Mutex::new(1)),
+ /// (Mutex::new(false), Mutex::new(2)),
+ /// (Mutex::new(true), Mutex::new(3)),
+ /// ];
+ /// let locks = OwnedLockCollection::new(data);
+ /// let mut ctx = locks.context();
+ /// let mut iter = ctx.iter(key);
+ ///
+ /// let mut sum = 0;
+ /// while let Some(tuple) = iter.recurse_next_tuple() {
+ /// let (should_count, mut tuple) = tuple.lock_0();
+ /// if **should_count {
+ /// let num = tuple.lock_mut_1();
+ /// sum += **num;
+ /// }
+ /// iter = tuple.exit();
+ /// }
+ ///
+ /// assert_eq!(sum, 4);
+ /// ```
+ pub fn recurse_next_tuple(mut self) -> Option<LockingTuple<'c, T, T, Self>> {
+ if let Some(tuple) = self.iterator.next() {
+ Some(LockingTuple {
+ key: self.key,
+ _lockable: PhantomData,
+ tuple,
+ outer: self,
+ })
+ } else {
+ None
+ }
+ }
+
+ /// Create a new `LockingIterator` based on the next element in the iterator.
+ ///
+ /// If a list contains a list of locks, then this method can be used to
+ /// recurse into the next element of the list. To go back to the parent scope,
+ /// use [`LockingIterator::exit`] on the new list.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use happylock::{Mutex, ThreadKey};
+ /// use happylock::collection::OwnedLockCollection;
+ ///
+ /// let key = ThreadKey::get().unwrap();
+ /// let data = [
+ /// (Mutex::new(true), Mutex::new(1)),
+ /// (Mutex::new(false), Mutex::new(2)),
+ /// (Mutex::new(true), Mutex::new(3)),
+ /// ];
+ /// let locks = OwnedLockCollection::new(data);
+ /// let mut ctx = locks.context();
+ /// let iter = ctx.iter(key);
+ ///
+ /// let tuple = iter.recurse_last_tuple().unwrap();
+ /// let (should_count, mut tuple) = tuple.lock_0();
+ /// if **should_count {
+ /// let num = tuple.lock_mut_1();
+ /// assert_eq!(**num, 3);
+ /// } else {
+ /// panic!();
+ /// }
+ /// ```
+ pub fn recurse_last_tuple(self) -> Option<LockingTuple<'c, T, T, O>> {
+ if let Some(tuple) = self.iterator.last() {
+ Some(LockingTuple {
+ key: self.key,
+ _lockable: PhantomData,
+ tuple,
+ outer: self.outer,
+ })
+ } else {
+ None
+ }
+ }
+}
+
+impl<'c, L: 'c + Iterator<Item = &'c I>, I: 'c + RawLock + Lockable, O> LockingIterator<'c, L, O> {
+ /// Advances the iterator, locking the next element and returning a guard to
+ /// the inner data.
+ ///
+ /// Returns `None` when iteration is finished. Individual iterator
+ /// implementations may choose to resume iteration, and so calling `next()`
+ /// again may or may not eventually start returning `Some(Item)` again at some
+ /// point.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use happylock::{Mutex, ThreadKey};
+ /// use happylock::collection::OwnedLockCollection;
+ ///
+ /// let key = ThreadKey::get().unwrap();
+ /// let data = [Mutex::new(1), Mutex::new(3), Mutex::new(8)];
+ /// let locks = OwnedLockCollection::new(data);
+ /// let mut ctx = locks.context();
+ /// let mut iter = ctx.iter(key);
+ ///
+ /// let mut sum = 0;
+ /// while let Some(item) = iter.lock_next() {
+ /// sum += **item;
+ /// }
+ ///
+ /// assert_eq!(sum, 12);
+ /// ```
+ pub fn lock_next(&mut self) -> Option<ContextGuard<'c, <I as Lockable>::Guard<'c>, ThreadKey>> {
+ if let Some(lock) = self.iterator.next() {
+ unsafe {
+ lock.raw_write();
+ let guard = lock.guard();
+
+ Some(ContextGuard {
+ _key: self.key,
+ guard,
+ })
+ }
+ } else {
+ None
+ }
+ }
+
+ /// Consumes the iterator, returning the last element, without locking any
+ /// other elements.
+ ///
+ /// This method will evaluate the iterator until it returns `None`. While
+ /// doing so, it keeps track of the current element. After `None` is returned,
+ /// `lock_last()` will then lock the last element it saw and return the
+ /// lock's data.
+ ///
+ /// # Panics
+ ///
+ /// This function might panic if the iterator is infinite.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use happylock::{Mutex, ThreadKey};
+ /// use happylock::collection::OwnedLockCollection;
+ ///
+ /// let key = ThreadKey::get().unwrap();
+ /// let data = [Mutex::new(1), Mutex::new(3), Mutex::new(8)];
+ /// let locks = OwnedLockCollection::new(data);
+ /// let mut ctx = locks.context();
+ /// let iter = ctx.iter(key);
+ ///
+ /// let last = iter.lock_last().unwrap();
+ /// assert_eq!(**last, 8);
+ /// ```
+ pub fn lock_last(self) -> Option<ContextGuard<'c, <I as Lockable>::Guard<'c>, ThreadKey>> {
+ self.iterator.last().map(|lock| unsafe {
+ lock.raw_write();
+ let guard = lock.guard();
+
+ ContextGuard {
+ _key: self.key,
+ guard,
+ }
+ })
+ }
+}
+
+impl<'c, L: 'c + Iterator<Item = &'c I>, I: 'c + RawLock + Lockable, O>
+ LockingIterator<'c, Peekable<L>, O>
+{
+ /// Attempts to lock the next element and returning a guard to
+ /// the inner data.
+ ///
+ /// # Errors
+ ///
+ /// Returns `Err(TryLockNextError::FinishedIteration)` when iteration is
+ /// finished. Individual iterator implementations may choose to resume
+ /// iteration, and so calling `next()` again may or may not eventually start
+ /// returning `Some(Item)` again at some point.
+ ///
+ /// Returns `Err(TryLockNextError::WouldBlock)` if the next lock in the
+ /// iterator is already locked. This will not advance the iterator.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use happylock::{Mutex, ThreadKey};
+ /// use happylock::collection::OwnedLockCollection;
+ /// use happylock::context::iterator::TryLockNextError;
+ ///
+ /// let key = ThreadKey::get().unwrap();
+ /// let data = [Mutex::new(1), Mutex::new(3), Mutex::new(8)];
+ /// let locks = OwnedLockCollection::new(data);
+ /// let mut ctx = locks.context();
+ /// let mut iter = ctx.iter(key).peekable();
+ ///
+ /// let mut sum = 0;
+ /// loop {
+ /// match iter.try_lock_next() {
+ /// Ok(item) => sum += **item,
+ /// Err(TryLockNextError::WouldBlock) => continue,
+ /// Err(TryLockNextError::FinishedIteration) => break,
+ /// }
+ /// }
+ ///
+ /// assert_eq!(sum, 12);
+ /// ```
+ pub fn try_lock_next(
+ &mut self,
+ ) -> Result<ContextGuard<'c, <I as Lockable>::Guard<'c>, ThreadKey>, TryLockNextError> {
+ if let Some(lock) = self.iterator.peek().copied() {
+ unsafe {
+ if lock.raw_try_write() {
+ // safety: we just saw that there is a valid value
+ let lock = self.iterator.next().unwrap_unchecked();
+ let guard = lock.guard();
+
+ Ok(ContextGuard {
+ _key: self.key,
+ guard,
+ })
+ } else {
+ Err(TryLockNextError::WouldBlock)
+ }
+ }
+ } else {
+ Err(TryLockNextError::FinishedIteration)
+ }
+ }
+}
+
+impl<'c, L: 'c + Iterator<Item = &'c I>, I: 'c + RawLock + Sharable, O> LockingIterator<'c, L, O> {
+ /// Advances the iterator, acquiring a shared lock to the next element and
+ /// returning a guard to the inner data.
+ ///
+ /// Returns `None` when iteration is finished. Individual iterator
+ /// implementations may choose to resume iteration, and so calling `next()`
+ /// again may or may not eventually start returning `Some(Item)` again at some
+ /// point.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use happylock::{RwLock, ThreadKey};
+ /// use happylock::collection::OwnedLockCollection;
+ ///
+ /// let key = ThreadKey::get().unwrap();
+ /// let data = [RwLock::new(1), RwLock::new(3), RwLock::new(8)];
+ /// let locks = OwnedLockCollection::new(data);
+ /// let mut ctx = locks.context();
+ /// let mut iter = ctx.iter(key);
+ ///
+ /// let mut sum = 0;
+ /// while let Some(item) = iter.read_next() {
+ /// sum += **item;
+ /// }
+ ///
+ /// assert_eq!(sum, 12);
+ /// ```
+ pub fn read_next(
+ &mut self,
+ ) -> Option<ContextGuard<'c, <I as Sharable>::ReadGuard<'c>, ThreadKey>> {
+ if let Some(lock) = self.iterator.next() {
+ unsafe {
+ lock.raw_read();
+ let guard = lock.read_guard();
+
+ Some(ContextGuard {
+ _key: self.key,
+ guard,
+ })
+ }
+ } else {
+ None
+ }
+ }
+
+ /// Consumes the iterator, returning the last element with readonly access,
+ /// without locking any other elements.
+ ///
+ /// This method will evaluate the iterator until it returns `None`. While
+ /// doing so, it keeps track of the current element. After `None` is returned,
+ /// `lock_last()` will then lock the last element it saw and return the
+ /// lock's data.
+ ///
+ /// # Panics
+ ///
+ /// This function might panic if the iterator is infinite.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use happylock::{RwLock, ThreadKey};
+ /// use happylock::collection::OwnedLockCollection;
+ ///
+ /// let key = ThreadKey::get().unwrap();
+ /// let data = [RwLock::new(1), RwLock::new(3), RwLock::new(8)];
+ /// let locks = OwnedLockCollection::new(data);
+ /// let mut ctx = locks.context();
+ /// let iter = ctx.iter(key);
+ ///
+ /// let last = iter.read_last().unwrap();
+ /// assert_eq!(**last, 8);
+ /// ```
+ pub fn read_last(self) -> Option<ContextGuard<'c, <I as Sharable>::ReadGuard<'c>, ThreadKey>> {
+ self.iterator.last().map(|lock| unsafe {
+ lock.raw_read();
+ let guard = lock.read_guard();
+
+ ContextGuard {
+ _key: self.key,
+ guard,
+ }
+ })
+ }
+}
+
+impl<'c, L: 'c + Iterator<Item = &'c I>, I: 'c + RawLock + Sharable, O>
+ LockingIterator<'c, Peekable<L>, O>
+{
+ /// Attempts to acquire a shared lock the next element and returning a guard
+ /// to the inner data.
+ ///
+ /// # Errors
+ ///
+ /// Returns `Err(TryLockNextError::FinishedIteration)` when iteration is
+ /// finished. Individual iterator implementations may choose to resume
+ /// iteration, and so calling `next()` again may or may not eventually start
+ /// returning `Some(Item)` again at some point.
+ ///
+ /// Returns `Err(TryLockNextError::WouldBlock)` if the next lock in the
+ /// iterator is already locked. This will not advance the iterator.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use happylock::{RwLock, ThreadKey};
+ /// use happylock::collection::OwnedLockCollection;
+ /// use happylock::context::iterator::TryLockNextError;
+ ///
+ /// let key = ThreadKey::get().unwrap();
+ /// let data = [RwLock::new(1), RwLock::new(3), RwLock::new(8)];
+ /// let locks = OwnedLockCollection::new(data);
+ /// let mut ctx = locks.context();
+ /// let mut iter = ctx.iter(key).peekable();
+ ///
+ /// let mut sum = 0;
+ /// loop {
+ /// match iter.try_read_next() {
+ /// Ok(item) => sum += **item,
+ /// Err(TryLockNextError::WouldBlock) => continue,
+ /// Err(TryLockNextError::FinishedIteration) => break,
+ /// }
+ /// }
+ ///
+ /// assert_eq!(sum, 12);
+ /// ```
+ pub fn try_read_next(
+ &mut self,
+ ) -> Result<ContextGuard<'c, <I as Sharable>::ReadGuard<'c>, ThreadKey>, TryLockNextError> {
+ if let Some(lock) = self.iterator.peek().copied() {
+ unsafe {
+ if lock.raw_try_read() {
+ // safety: we just saw that there is a valid value
+ let lock = self.iterator.next().unwrap_unchecked();
+ let guard = lock.read_guard();
+
+ Ok(ContextGuard {
+ _key: self.key,
+ guard,
+ })
+ } else {
+ Err(TryLockNextError::WouldBlock)
+ }
+ }
+ } else {
+ Err(TryLockNextError::FinishedIteration)
+ }
+ }
+}
+
+impl<'l, L: Iterator, O> LockingIterator<'l, L, O> {
+ /// Advances the iterator, without locking the next element in the iterator.
+ ///
+ /// Returns `false` when iteration is finished. Individual iterator
+ /// implementations may choose to resume iteration, and so calling
+ /// `skip_next()` again may or may not eventually start returning `true` again
+ /// at some point.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use happylock::{Mutex, ThreadKey};
+ /// use happylock::collection::OwnedLockCollection;
+ ///
+ /// let key = ThreadKey::get().unwrap();
+ /// let data = [Mutex::new(1), Mutex::new(3), Mutex::new(8)];
+ /// let locks = OwnedLockCollection::new(data);
+ /// let mut ctx = locks.context();
+ /// let mut iter = ctx.iter(key);
+ ///
+ /// iter.skip_next();
+ /// assert!(iter.lock_next().is_some_and(|v| **v == 3));
+ /// ```
+ pub fn skip_next(&mut self) -> bool {
+ self.iterator.next().is_some()
+ }
+
+ /// Advances the iterator, skipping `n` elements without locking.
+ ///
+ /// See [`Iterator::skip`] for more information.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use happylock::{Mutex, ThreadKey};
+ /// use happylock::collection::OwnedLockCollection;
+ ///
+ /// let key = ThreadKey::get().unwrap();
+ /// let data = [Mutex::new(1), Mutex::new(3), Mutex::new(8)];
+ /// let locks = OwnedLockCollection::new(data);
+ /// let mut ctx = locks.context();
+ /// let mut iter = ctx.iter(key);
+ ///
+ /// iter.skip_mut(2);
+ /// assert!(iter.lock_next().is_some_and(|v| **v == 8));
+ /// ```
+ pub fn skip_mut(&mut self, n: usize) {
+ for _ in 0..n {
+ self.iterator.next();
+ }
+ }
+
+ /// Returns the bounds on the remaining length of the iterator.
+ ///
+ /// See [`Iterator::size_hint`] for more information.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use happylock::{Mutex, ThreadKey};
+ /// use happylock::collection::OwnedLockCollection;
+ ///
+ /// let key = ThreadKey::get().unwrap();
+ /// let data = [Mutex::new(1), Mutex::new(2), Mutex::new(3)];
+ /// let locks = OwnedLockCollection::new(data);
+ /// let mut ctx = locks.context();
+ /// let mut iter = ctx.iter(key);
+ ///
+ /// assert_eq!((3, Some(3)), iter.size_hint());
+ /// let _ = iter.skip_next();
+ /// assert_eq!((2, Some(2)), iter.size_hint());
+ /// ```
+ #[must_use]
+ pub fn size_hint(&self) -> (usize, Option<usize>) {
+ self.iterator.size_hint()
+ }
+
+ /// Creates a new [`LockingIterator`] that skips the first `n` elements.
+ ///
+ /// Unlike `skip_next` or `skip_mut`, this method does not modify the iterator
+ /// in place. Instead, it returns a new iterator which skips the first `n`
+ /// elements.
+ ///
+ /// See [`Iterator::skip`] for more information.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use happylock::{Mutex, ThreadKey};
+ /// use happylock::collection::OwnedLockCollection;
+ ///
+ /// let key = ThreadKey::get().unwrap();
+ /// let data = [Mutex::new(1), Mutex::new(2), Mutex::new(3)];
+ /// let locks = OwnedLockCollection::new(data);
+ /// let mut ctx = locks.context();
+ /// let mut iter = ctx.iter(key).skip(2);
+ ///
+ /// assert!(iter.lock_next().is_some_and(|v| **v == 3));
+ /// assert!(iter.lock_next().is_none());
+ /// ```
+ #[must_use]
+ pub fn skip(self, n: usize) -> LockingIterator<'l, Skip<L>, O> {
+ self.with_iterator(|i| i.skip(n))
+ }
+
+ /// Creates a new [`LockingIterator`] that yields only the first `n` elements,
+ /// or fewer if the iterator ends sooner.
+ ///
+ /// See [`Iterator::take`] for more information.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use happylock::{Mutex, ThreadKey};
+ /// use happylock::collection::OwnedLockCollection;
+ ///
+ /// let key = ThreadKey::get().unwrap();
+ /// let data = [Mutex::new(1), Mutex::new(2), Mutex::new(3)];
+ /// let locks = OwnedLockCollection::new(data);
+ /// let mut ctx = locks.context();
+ /// let mut iter = ctx.iter(key).take(2);
+ ///
+ /// assert!(iter.lock_next().is_some_and(|v| **v == 1));
+ /// assert!(iter.lock_next().is_some_and(|v| **v == 2));
+ /// assert!(iter.lock_next().is_none());
+ /// ```
+ #[must_use]
+ pub fn take(self, n: usize) -> LockingIterator<'l, Take<L>, O> {
+ self.with_iterator(|i| i.take(n))
+ }
+
+ /// Creates a new [`LockingIterator`] that ends after the first `None`
+ ///
+ /// See [`Iterator::fuse`] for more information
+ #[must_use]
+ pub fn fuse(self) -> LockingIterator<'l, Fuse<L>, O> {
+ self.with_iterator(Iterator::fuse)
+ }
+
+ /// Creates a new [`LockingIterator`] which has access to the
+ /// [`try_lock_next`] and/or [`try_read_next`] methods.
+ ///
+ /// See [`Iterator::peekable`] for more information
+ ///
+ /// [`try_lock_next`]: `LockingIterator::try_lock_next`
+ /// [`try_read_next`]: `LockingIterator::try_read_next`
+ #[must_use]
+ pub fn peekable(self) -> LockingIterator<'l, Peekable<L>, O> {
+ self.with_iterator(Iterator::peekable)
+ }
+}
diff --git a/src/context/tuple.rs b/src/context/tuple.rs
new file mode 100644
index 0000000..9bca72e
--- /dev/null
+++ b/src/context/tuple.rs
@@ -0,0 +1,847 @@
+use std::marker::PhantomData;
+
+use crate::{
+ context::{ContextGuard, LockingIterator, LockingTuple},
+ lockable::{Lockable, RawLock, Sharable},
+ ThreadKey,
+};
+
+impl<'c, A, B, O> LockingTuple<'c, A, B, O> {
+ fn transmute<C>(self) -> LockingTuple<'c, C, B, O> {
+ LockingTuple {
+ _lockable: PhantomData,
+ key: self.key,
+ tuple: self.tuple,
+ outer: self.outer,
+ }
+ }
+}
+
+macro_rules! lock_impl {
+ ($self: expr, $field: tt) => {
+ unsafe {
+ $self.tuple.$field.raw_write();
+ (
+ ContextGuard {
+ _key: &$self.key,
+ guard: $self.tuple.$field.guard(),
+ },
+ $self.transmute(),
+ )
+ }
+ };
+}
+
+macro_rules! try_lock_impl {
+ ($self: expr, $field: tt) => {
+ unsafe {
+ if $self.tuple.$field.raw_try_write() {
+ Ok((
+ ContextGuard {
+ _key: $self.key,
+ guard: $self.tuple.$field.guard(),
+ },
+ $self.transmute(),
+ ))
+ } else {
+ Err($self)
+ }
+ }
+ };
+}
+
+macro_rules! lock_mut_impl {
+ ($self: expr, $field: tt) => {
+ unsafe {
+ $self.tuple.$field.raw_write();
+ ContextGuard {
+ _key: $self.key,
+ guard: $self.tuple.$field.guard(),
+ }
+ }
+ };
+}
+
+macro_rules! try_lock_mut_impl {
+ ($self: expr, $field: tt) => {
+ unsafe {
+ if $self.tuple.$field.raw_try_write() {
+ Some(ContextGuard {
+ _key: $self.key,
+ guard: $self.tuple.$field.guard(),
+ })
+ } else {
+ None
+ }
+ }
+ };
+}
+
+macro_rules! read_impl {
+ ($self: expr, $field: tt) => {
+ unsafe {
+ $self.tuple.$field.raw_read();
+ (
+ ContextGuard {
+ _key: &$self.key,
+ guard: $self.tuple.$field.read_guard(),
+ },
+ $self.transmute(),
+ )
+ }
+ };
+}
+
+macro_rules! try_read_impl {
+ ($self: expr, $field: tt) => {
+ unsafe {
+ if $self.tuple.$field.raw_try_read() {
+ Ok((
+ ContextGuard {
+ _key: $self.key,
+ guard: $self.tuple.$field.read_guard(),
+ },
+ $self.transmute(),
+ ))
+ } else {
+ Err($self)
+ }
+ }
+ };
+}
+
+macro_rules! read_mut_impl {
+ ($self: expr, $field: tt) => {
+ unsafe {
+ $self.tuple.$field.raw_read();
+ ContextGuard {
+ _key: $self.key,
+ guard: $self.tuple.$field.read_guard(),
+ }
+ }
+ };
+}
+
+macro_rules! try_read_mut_impl {
+ ($self: expr, $field: tt) => {
+ unsafe {
+ if $self.tuple.$field.raw_try_read() {
+ Some(ContextGuard {
+ _key: $self.key,
+ guard: $self.tuple.$field.read_guard(),
+ })
+ } else {
+ None
+ }
+ }
+ };
+}
+
+macro_rules! recurse_impl {
+ ($self: expr, $field: tt) => {
+ LockingTuple {
+ _lockable: PhantomData,
+ key: $self.key,
+ tuple: &$self.tuple.$field,
+ outer: $self.transmute(),
+ }
+ };
+}
+
+macro_rules! recurse_iter_impl {
+ ($self: expr, $field: tt) => {
+ LockingIterator {
+ key: $self.key,
+ iterator: $self.tuple.$field.into_iter(),
+ outer: $self.transmute(),
+ }
+ };
+}
+
+type LockReturn<'a, 'context, Guarded, L, C, O> = (
+ ContextGuard<'a, <Guarded as Lockable>::Guard<'a>, ThreadKey>,
+ LockingTuple<'context, L, C, O>,
+);
+
+type TryLockReturn<'a, 'context, Guarded, L, C, O, This> =
+ Result<LockReturn<'a, 'context, Guarded, L, C, O>, This>;
+
+type ReadReturn<'a, 'context, Guarded, L, C, O> = (
+ ContextGuard<'a, <Guarded as Sharable>::ReadGuard<'a>, ThreadKey>,
+ LockingTuple<'context, L, C, O>,
+);
+
+type TryReadReturn<'a, 'context, Guarded, L, C, O, This> =
+ Result<ReadReturn<'a, 'context, Guarded, L, C, O>, This>;
+
+type RecurseReturn<'context, Inner, L, C, O> =
+ LockingTuple<'context, Inner, Inner, LockingTuple<'context, L, C, O>>;
+
+type RecurseIterReturn<'context, Inner, L, C, O> = LockingIterator<
+ 'context,
+ <&'context Inner as IntoIterator>::IntoIter,
+ LockingTuple<'context, L, C, O>,
+>;
+
+impl<T, C, Outer> LockingTuple<'_, T, C, Outer> {
+ /// Exit out of the current scope of the locking tuple into the parent.
+ pub fn exit(self) -> Outer {
+ self.outer
+ }
+}
+
+impl<'context, A: RawLock + Lockable, Outer> LockingTuple<'context, (A,), (A,), Outer> {
+ /// Lock the first element, and return a new tuple where the first element
+ /// is inaccessible.
+ #[must_use]
+ pub fn lock_0<'a>(self) -> LockReturn<'a, 'context, A, ((),), (A,), Outer>
+ where
+ 'context: 'a,
+ {
+ lock_impl!(self, 0)
+ }
+
+ /// Attempts to lock the first element without blocking, and return a new tuple
+ /// where the first element is inaccessible.
+ ///
+ /// # Errors
+ ///
+ /// If the element is already locked, `Err` is returned with the original
+ /// tuple.
+ pub fn try_lock_0<'a>(self) -> TryLockReturn<'a, 'context, A, ((),), (A,), Outer, Self>
+ where
+ 'context: 'a,
+ {
+ try_lock_impl!(self, 0)
+ }
+
+ /// Lock the first element. The tuple becomes unusable until the returned
+ /// guard is dropped.
+ #[must_use]
+ pub fn lock_mut_0(&mut self) -> ContextGuard<'_, A::Guard<'_>, ThreadKey> {
+ lock_mut_impl!(self, 0)
+ }
+
+ /// Attempts to lock the first element without blocking. If successful, the
+ /// tuple becomes unusable until the returned guard is dropped. If the element
+ /// is already locked, `None` is returned.
+ #[must_use]
+ pub fn try_lock_mut_0(&mut self) -> Option<ContextGuard<'_, A::Guard<'_>, ThreadKey>> {
+ try_lock_mut_impl!(self, 0)
+ }
+}
+
+impl<'context, A: RawLock + Sharable, Outer> LockingTuple<'context, (A,), (A,), Outer> {
+ /// Acquire a shared lock to the first element, and return a new tuple where
+ /// the first element is inaccessible.
+ #[must_use]
+ pub fn read_0<'a>(self) -> ReadReturn<'a, 'context, A, ((),), (A,), Outer>
+ where
+ 'context: 'a,
+ {
+ read_impl!(self, 0)
+ }
+
+ /// Attempts to acquire a shared lock the first element without blocking, and
+ /// return a new tuple where the first element is inaccessible.
+ ///
+ /// # Errors
+ ///
+ /// If the element is already exclusively locked, `Err` is returned with the original
+ /// tuple.
+ pub fn try_read_0<'a>(self) -> TryReadReturn<'a, 'context, A, ((),), (A,), Outer, Self>
+ where
+ 'context: 'a,
+ {
+ try_read_impl!(self, 0)
+ }
+
+ /// Acquire a shared lock to the first element. The tuple becomes unusable
+ /// until the returned guard is dropped.
+ #[must_use]
+ pub fn read_mut_0(&mut self) -> ContextGuard<'_, A::ReadGuard<'_>, ThreadKey> {
+ read_mut_impl!(self, 0)
+ }
+
+ /// Attempts to acquire a shared lock the first element without blocking. If
+ /// successful, the tuple becomes unusable until the returned guard is
+ /// dropped. If the element is already exclusively locked, `None` is returned.
+ #[must_use]
+ pub fn try_read_mut_0(&mut self) -> Option<ContextGuard<'_, A::ReadGuard<'_>, ThreadKey>> {
+ try_read_mut_impl!(self, 0)
+ }
+}
+
+impl<'context, A, O> LockingTuple<'context, (A,), (A,), O> {
+ /// Consume the tuple, and return the first element as a new tuple.
+ #[must_use]
+ pub fn recurse_0(self) -> RecurseReturn<'context, A, ((),), (A,), O> {
+ recurse_impl!(self, 0)
+ }
+
+ /// Consume the tuple, and return the first element as a locking iterator.
+ pub fn recurse_0_iter(self) -> RecurseIterReturn<'context, A, ((),), (A,), O>
+ where
+ &'context A: IntoIterator,
+ {
+ recurse_iter_impl!(self, 0)
+ }
+}
+
+impl<'context, A: RawLock + Lockable, B, B0, O> LockingTuple<'context, (A, B), (A, B0), O> {
+ /// Lock the first element, and return a new tuple where the first element
+ /// is inaccessible.
+ #[must_use]
+ pub fn lock_0<'a>(self) -> LockReturn<'a, 'context, A, ((), B), (A, B0), O>
+ where
+ 'context: 'a,
+ {
+ lock_impl!(self, 0)
+ }
+
+ /// Lock the first element. The tuple becomes unusable until the returned
+ /// guard is dropped.
+ #[must_use]
+ pub fn lock_mut_0(&mut self) -> ContextGuard<'_, A::Guard<'_>, ThreadKey> {
+ lock_mut_impl!(self, 0)
+ }
+
+ /// Attempts to lock the first element without blocking, and return a new tuple
+ /// where the first element is inaccessible.
+ ///
+ /// # Errors
+ ///
+ /// If the element is already locked, `Err` is returned with the original
+ /// tuple.
+ pub fn try_lock_0<'a>(self) -> TryLockReturn<'a, 'context, A, ((), B), (A, B0), O, Self>
+ where
+ 'context: 'a,
+ {
+ try_lock_impl!(self, 0)
+ }
+
+ /// Attempts to lock the first element without blocking. If successful, the
+ /// tuple becomes unusable until the returned guard is dropped. If the element
+ /// is already locked, `None` is returned.
+ #[must_use]
+ pub fn try_lock_mut_0(&mut self) -> Option<ContextGuard<'_, A::Guard<'_>, ThreadKey>> {
+ try_lock_mut_impl!(self, 0)
+ }
+}
+
+impl<'context, A: RawLock + Sharable, B, B0, O> LockingTuple<'context, (A, B), (A, B0), O> {
+ /// Acquire a shared lock to the first element, and return a new tuple where
+ /// the first element is inaccessible.
+ #[must_use]
+ pub fn read_0<'a>(self) -> ReadReturn<'a, 'context, A, ((), B), (A, B0), O>
+ where
+ 'context: 'a,
+ {
+ read_impl!(self, 0)
+ }
+
+ /// Attempts to acquire a shared lock the first element without blocking, and
+ /// return a new tuple where the first element is inaccessible.
+ ///
+ /// # Errors
+ ///
+ /// If the element is already exclusively locked, `Err` is returned with the original
+ /// tuple.
+ pub fn try_read_0<'a>(self) -> TryReadReturn<'a, 'context, A, ((), B), (A, B0), O, Self>
+ where
+ 'context: 'a,
+ {
+ try_read_impl!(self, 0)
+ }
+
+ /// Acquire a shared lock to the first element. The tuple becomes unusable
+ /// until the returned guard is dropped.
+ #[must_use]
+ pub fn read_mut_0(&mut self) -> ContextGuard<'_, A::ReadGuard<'_>, ThreadKey> {
+ read_mut_impl!(self, 0)
+ }
+
+ /// Attempts to acquire a shared lock the first element without blocking. If
+ /// successful, the tuple becomes unusable until the returned guard is
+ /// dropped. If the element is already exclusively locked, `None` is returned.
+ #[must_use]
+ pub fn try_read_mut_0(&mut self) -> Option<ContextGuard<'_, A::ReadGuard<'_>, ThreadKey>> {
+ try_read_mut_impl!(self, 0)
+ }
+}
+
+impl<'context, A, B, B0, O> LockingTuple<'context, (A, B), (A, B0), O> {
+ /// Consume the tuple, and return the first element as a new tuple.
+ #[must_use]
+ pub fn recurse_0(self) -> RecurseReturn<'context, A, ((), B), (A, B0), O> {
+ recurse_impl!(self, 0)
+ }
+
+ /// Consume the tuple, and return the first element as a locking iterator.
+ pub fn recurse_0_iter(self) -> RecurseIterReturn<'context, A, ((), B), (A, B0), O>
+ where
+ &'context A: IntoIterator,
+ {
+ recurse_iter_impl!(self, 0)
+ }
+}
+
+impl<'context, A: Lockable + RawLock, B, O> LockingTuple<'context, (A, B), (A, B), O> {
+ /// Lock the first element, and return the second element as a new tuple.
+ #[must_use]
+ pub fn lock_and_recurse<'a>(
+ self,
+ ) -> (
+ ContextGuard<'a, <A as Lockable>::Guard<'a>, ThreadKey>,
+ LockingTuple<'context, B, B, O>,
+ )
+ where
+ 'context: 'a,
+ {
+ unsafe {
+ self.tuple.0.raw_write();
+ (
+ ContextGuard {
+ _key: self.key,
+ guard: self.tuple.0.guard(),
+ },
+ LockingTuple {
+ _lockable: PhantomData,
+ key: self.key,
+ tuple: &self.tuple.1,
+ outer: self.outer,
+ },
+ )
+ }
+ }
+}
+
+impl<'context, A, A0, B: RawLock + Lockable, O> LockingTuple<'context, (A, B), (A0, B), O> {
+ /// Lock the second element, and return a new tuple where the first and second
+ /// elements are inaccessible.
+ #[must_use]
+ pub fn lock_1<'a>(self) -> LockReturn<'a, 'context, B, ((), ()), (A0, B), O>
+ where
+ 'context: 'a,
+ {
+ lock_impl!(self, 1)
+ }
+
+ /// Lock the second element. The tuple becomes unusable until the returned
+ /// guard is dropped.
+ #[must_use]
+ pub fn lock_mut_1(&mut self) -> ContextGuard<'_, B::Guard<'_>, ThreadKey> {
+ lock_mut_impl!(self, 1)
+ }
+
+ /// Attempts to lock the second element without blocking, and return a new tuple
+ /// where the first element is inaccessible.
+ ///
+ /// # Errors
+ ///
+ /// If the element is already locked, `Err` is returned with the original
+ /// tuple.
+ pub fn try_lock_1<'a>(self) -> TryLockReturn<'a, 'context, B, ((), ()), (A0, B), O, Self>
+ where
+ 'context: 'a,
+ {
+ try_lock_impl!(self, 1)
+ }
+
+ /// Attempts to lock the second element without blocking. If successful, the
+ /// tuple becomes unusable until the returned guard is dropped. If the element
+ /// is already locked, `None` is returned.
+ #[must_use]
+ pub fn try_lock_mut_1(&mut self) -> Option<ContextGuard<'_, B::Guard<'_>, ThreadKey>> {
+ try_lock_mut_impl!(self, 1)
+ }
+}
+
+impl<'context, A, A0, B: RawLock + Sharable, O> LockingTuple<'context, (A, B), (A0, B), O> {
+ /// Acquire a shared lock to the second element, and return a new tuple where
+ /// the second element is inaccessible.
+ #[must_use]
+ pub fn read_1<'a>(self) -> ReadReturn<'a, 'context, B, ((), ()), (A0, B), O>
+ where
+ 'context: 'a,
+ {
+ read_impl!(self, 1)
+ }
+
+ /// Attempts to acquire a shared lock the second element without blocking, and
+ /// return a new tuple where the second element is inaccessible.
+ ///
+ /// # Errors
+ ///
+ /// If the element is already exclusively locked, `Err` is returned with the original
+ /// tuple.
+ pub fn try_read_1<'a>(self) -> TryReadReturn<'a, 'context, B, ((), ()), (A0, B), O, Self>
+ where
+ 'context: 'a,
+ {
+ try_read_impl!(self, 1)
+ }
+
+ /// Acquire a shared lock to the second element. The tuple becomes unusable
+ /// until the returned guard is dropped.
+ #[must_use]
+ pub fn read_mut_1(&mut self) -> ContextGuard<'_, B::ReadGuard<'_>, ThreadKey> {
+ read_mut_impl!(self, 1)
+ }
+
+ /// Attempts to acquire a shared lock the second element without blocking. If
+ /// successful, the tuple becomes unusable until the returned guard is
+ /// dropped. If the element is already exclusively locked, `None` is returned.
+ #[must_use]
+ pub fn try_read_mut_1(&mut self) -> Option<ContextGuard<'_, B::ReadGuard<'_>, ThreadKey>> {
+ try_read_mut_impl!(self, 1)
+ }
+}
+
+impl<'context, A, A0, B, O> LockingTuple<'context, (A, B), (A0, B), O> {
+ /// Consume the tuple, and return the second element as a new tuple.
+ #[must_use]
+ pub fn recurse_1(self) -> RecurseReturn<'context, B, ((), ()), (A0, B), O> {
+ recurse_impl!(self, 1)
+ }
+
+ /// Consume the tuple, and return the second element as a locking iterator.
+ pub fn recurse_1_iter(self) -> RecurseIterReturn<'context, B, (A, ()), (A0, B), O>
+ where
+ &'context B: IntoIterator,
+ {
+ recurse_iter_impl!(self, 1)
+ }
+}
+
+impl<'context, A: RawLock + Lockable, B, B0, C, C0, O>
+ LockingTuple<'context, (A, B, C), (A, B0, C0), O>
+{
+ /// Lock the first element, and return a new tuple where the first element
+ /// is inaccessible.
+ #[must_use]
+ // The type is impossible to refactor, and I already wrote this function, so no point in removing it
+ #[expect(clippy::type_complexity)]
+ pub fn lock_0<'a>(self) -> LockReturn<'a, 'context, A, ((), B, C), (A, B0, C0), O>
+ where
+ 'context: 'a,
+ {
+ lock_impl!(self, 0)
+ }
+
+ /// Lock the first element. The tuple becomes unusable until the returned
+ /// guard is dropped.
+ #[must_use]
+ pub fn lock_mut_0(&mut self) -> ContextGuard<'_, A::Guard<'_>, ThreadKey> {
+ lock_mut_impl!(self, 0)
+ }
+
+ /// Attempts to lock the first element without blocking. If successful, the
+ /// tuple becomes unusable until the returned guard is dropped. If the element
+ /// is already locked, `None` is returned.
+ #[must_use]
+ pub fn try_lock_mut_0(&mut self) -> Option<ContextGuard<'_, A::Guard<'_>, ThreadKey>> {
+ try_lock_mut_impl!(self, 0)
+ }
+}
+
+impl<'context, A: RawLock + Sharable, B, B0, C, C0, O>
+ LockingTuple<'context, (A, B, C), (A, B0, C0), O>
+{
+ /// Acquire a shared lock to the first element, and return a new tuple where
+ /// the first element is inaccessible.
+ #[must_use]
+ // The type is impossible to refactor, and I already wrote this function, so no point in removing it
+ #[expect(clippy::type_complexity)]
+ pub fn read_0<'a>(self) -> ReadReturn<'a, 'context, A, ((), B, C), (A, B0, C0), O>
+ where
+ 'context: 'a,
+ {
+ read_impl!(self, 0)
+ }
+
+ /// Acquire a shared lock to the first element. The tuple becomes unusable
+ /// until the returned guard is dropped.
+ #[must_use]
+ pub fn read_mut_0(&mut self) -> ContextGuard<'_, A::ReadGuard<'_>, ThreadKey> {
+ read_mut_impl!(self, 0)
+ }
+
+ /// Attempts to acquire a shared lock the first element without blocking. If
+ /// successful, the tuple becomes unusable until the returned guard is
+ /// dropped. If the element is already exclusively locked, `None` is returned.
+ #[must_use]
+ pub fn try_read_mut_0(&mut self) -> Option<ContextGuard<'_, A::ReadGuard<'_>, ThreadKey>> {
+ try_read_mut_impl!(self, 0)
+ }
+}
+
+impl<'context, A, B, B0, C, C0, O> LockingTuple<'context, (A, B, C), (A, B0, C0), O> {
+ /// Consume the tuple, and return the first element as a new tuple.
+ #[must_use]
+ // The type is impossible to refactor, and I already wrote this function, so no point in removing it
+ #[expect(clippy::type_complexity)]
+ pub fn recurse_0(self) -> RecurseReturn<'context, A, ((), B, C), (A, B0, C0), O> {
+ recurse_impl!(self, 0)
+ }
+
+ /// Consume the tuple, and return the first element as a locking iterator.
+ // The type is impossible to refactor, and I already wrote this function, so no point in removing it
+ #[expect(clippy::type_complexity)]
+ pub fn recurse_0_iter(self) -> RecurseIterReturn<'context, A, ((), B, C), (A, B0, C0), O>
+ where
+ &'context A: IntoIterator,
+ {
+ recurse_iter_impl!(self, 0)
+ }
+}
+
+impl<'context, A, A0, B: RawLock + Lockable, C, C0, O>
+ LockingTuple<'context, (A, B, C), (A0, B, C0), O>
+{
+ /// Lock the second element, and return a new tuple where the first and second
+ /// elements are inaccessible.
+ #[must_use]
+ // The type is impossible to refactor, and I already wrote this function, so no point in removing it
+ #[expect(clippy::type_complexity)]
+ pub fn lock_1<'a>(self) -> LockReturn<'a, 'context, B, ((), (), C), (A0, B, C0), O>
+ where
+ 'context: 'a,
+ {
+ lock_impl!(self, 1)
+ }
+
+ /// Lock the second element. The tuple becomes unusable until the returned
+ /// guard is dropped.
+ #[must_use]
+ pub fn lock_mut_1(&mut self) -> ContextGuard<'_, B::Guard<'_>, ThreadKey> {
+ lock_mut_impl!(self, 1)
+ }
+
+ /// Attempts to lock the second element without blocking. If successful, the
+ /// tuple becomes unusable until the returned guard is dropped. If the element
+ /// is already locked, `None` is returned.
+ #[must_use]
+ pub fn try_lock_mut_1(&mut self) -> Option<ContextGuard<'_, B::Guard<'_>, ThreadKey>> {
+ try_lock_mut_impl!(self, 1)
+ }
+}
+
+impl<'context, A, A0, B: RawLock + Sharable, C, C0, O>
+ LockingTuple<'context, (A, B, C), (A0, B, C0), O>
+{
+ /// Acquire a shared lock to the second element, and return a new tuple where
+ /// the second element is inaccessible.
+ #[must_use]
+ // The type is impossible to refactor, and I already wrote this function, so no point in removing it
+ #[expect(clippy::type_complexity)]
+ pub fn read_1<'a>(self) -> ReadReturn<'a, 'context, B, ((), (), C), (A0, B, C0), O>
+ where
+ 'context: 'a,
+ {
+ read_impl!(self, 1)
+ }
+
+ /// Acquire a shared lock to the second element. The tuple becomes unusable
+ /// until the returned guard is dropped.
+ #[must_use]
+ pub fn read_mut_1(&mut self) -> ContextGuard<'_, B::ReadGuard<'_>, ThreadKey> {
+ read_mut_impl!(self, 1)
+ }
+
+ /// Attempts to acquire a shared lock the second element without blocking. If
+ /// successful, the tuple becomes unusable until the returned guard is
+ /// dropped. If the element is already exclusively locked, `None` is returned.
+ #[must_use]
+ pub fn try_read_mut_1(&mut self) -> Option<ContextGuard<'_, B::ReadGuard<'_>, ThreadKey>> {
+ try_read_mut_impl!(self, 1)
+ }
+}
+
+impl<'context, A, A0, B, C, C0, O> LockingTuple<'context, (A, B, C), (A0, B, C0), O> {
+ /// Consume the tuple, and return the second element as a new tuple.
+ #[must_use]
+ // The type is impossible to refactor, and I already wrote this function, so no point in removing it
+ #[expect(clippy::type_complexity)]
+ pub fn recurse_1(self) -> RecurseReturn<'context, B, ((), (), C), (A0, B, C0), O> {
+ recurse_impl!(self, 1)
+ }
+
+ /// Consume the tuple, and return the second element as a locking iterator.
+ // The type is impossible to refactor, and I already wrote this function, so no point in removing it
+ #[expect(clippy::type_complexity)]
+ pub fn recurse_1_iter(self) -> RecurseIterReturn<'context, B, ((), (), C), (A0, B, C0), O>
+ where
+ &'context B: IntoIterator,
+ {
+ recurse_iter_impl!(self, 1)
+ }
+}
+
+impl<'context, A, A0, B, B0, C: RawLock + Lockable, O>
+ LockingTuple<'context, (A, B, C), (A0, B0, C), O>
+{
+ /// Lock the third element, and return a new tuple where all elements are
+ /// inaccessible.
+ #[must_use]
+ // The type is impossible to refactor, and I already wrote this function, so no point in removing it
+ #[expect(clippy::type_complexity)]
+ pub fn lock_2<'a>(self) -> LockReturn<'a, 'context, C, ((), (), ()), (A0, B0, C), O>
+ where
+ 'context: 'a,
+ {
+ lock_impl!(self, 2)
+ }
+
+ /// Lock the third element. The tuple becomes unusable until the returned
+ /// guard is dropped.
+ #[must_use]
+ pub fn lock_mut_2(&mut self) -> ContextGuard<'_, C::Guard<'_>, ThreadKey> {
+ lock_mut_impl!(self, 2)
+ }
+
+ /// Attempts to lock the third element without blocking. If successful, the
+ /// tuple becomes unusable until the returned guard is dropped. If the element
+ /// is already locked, `None` is returned.
+ #[must_use]
+ pub fn try_lock_mut_2(&mut self) -> Option<ContextGuard<'_, C::Guard<'_>, ThreadKey>> {
+ try_lock_mut_impl!(self, 2)
+ }
+}
+
+impl<'context, A, A0, B, B0, C: RawLock + Sharable, O>
+ LockingTuple<'context, (A, B, C), (A0, B0, C), O>
+{
+ /// Acquire a shared lock to the third element, and return a new tuple where
+ /// the third element is inaccessible.
+ #[must_use]
+ // The type is impossible to refactor, and I already wrote this function, so no point in removing it
+ #[expect(clippy::type_complexity)]
+ pub fn read_2<'a>(self) -> ReadReturn<'a, 'context, C, ((), (), ()), (A0, B0, C), O>
+ where
+ 'context: 'a,
+ {
+ read_impl!(self, 2)
+ }
+
+ /// Acquire a shared lock to the third element. The tuple becomes unusable
+ /// until the returned guard is dropped.
+ #[must_use]
+ pub fn read_mut_2(&mut self) -> ContextGuard<'_, C::ReadGuard<'_>, ThreadKey> {
+ read_mut_impl!(self, 2)
+ }
+
+ /// Attempts to acquire a shared lock the third element without blocking. If
+ /// successful, the tuple becomes unusable until the returned guard is
+ /// dropped. If the element is already exclusively locked, `None` is returned.
+ #[must_use]
+ pub fn try_read_mut_2(&mut self) -> Option<ContextGuard<'_, C::ReadGuard<'_>, ThreadKey>> {
+ try_read_mut_impl!(self, 2)
+ }
+}
+
+impl<'context, A, A0, B, B0, C, O> LockingTuple<'context, (A, B, C), (A0, B0, C), O> {
+ /// Consume the tuple, and return the third element as a new tuple.
+ #[must_use]
+ // The type is impossible to refactor, and I already wrote this function, so no point in removing it
+ #[expect(clippy::type_complexity)]
+ pub fn recurse_2(self) -> RecurseReturn<'context, C, ((), (), ()), (A0, B0, C), O> {
+ recurse_impl!(self, 2)
+ }
+
+ /// Consume the tuple, and return the third element as a locking iterator.
+ // The type is impossible to refactor, and I already wrote this function, so no point in removing it
+ #[expect(clippy::type_complexity)]
+ pub fn recurse_2_iter(self) -> RecurseIterReturn<'context, C, ((), (), ()), (A0, B0, C), O>
+ where
+ &'context C: IntoIterator,
+ {
+ recurse_iter_impl!(self, 2)
+ }
+}
+
+impl<'context, A, B, B0, C, C0, D, D0, O> LockingTuple<'context, (A, B, C, D), (A, B0, C0, D0), O> {
+ /// Consume the tuple, and return the first element as a new tuple.
+ #[must_use]
+ // The type is impossible to refactor, and I already wrote this function, so no point in removing it
+ #[expect(clippy::type_complexity)]
+ pub fn recurse_0(self) -> RecurseReturn<'context, A, ((), B, C, D), (A, B0, C0, D0), O> {
+ recurse_impl!(self, 0)
+ }
+
+ /// Consume the tuple, and return the first element as a locking iterator.
+ // The type is impossible to refactor, and I already wrote this function, so no point in removing it
+ #[expect(clippy::type_complexity)]
+ pub fn recurse_0_iter(self) -> RecurseIterReturn<'context, A, ((), B, C, D), (A, B0, C0, D0), O>
+ where
+ &'context A: IntoIterator,
+ {
+ recurse_iter_impl!(self, 0)
+ }
+}
+
+impl<'context, A, A0, B, C, C0, D, D0, O> LockingTuple<'context, (A, B, C, D), (A0, B, C0, D0), O> {
+ /// Consume the tuple, and return the second element as a new tuple.
+ #[must_use]
+ // The type is impossible to refactor, and I already wrote this function, so no point in removing it
+ #[expect(clippy::type_complexity)]
+ pub fn recurse_1(self) -> RecurseReturn<'context, B, ((), (), C, D), (A0, B, C0, D0), O> {
+ recurse_impl!(self, 1)
+ }
+
+ /// Consume the tuple, and return the second element as a locking iterator.
+ // The type is impossible to refactor, and I already wrote this function, so no point in removing it
+ #[expect(clippy::type_complexity)]
+ pub fn recurse_1_iter(
+ self,
+ ) -> RecurseIterReturn<'context, B, ((), (), C, D), (A0, B, C0, D0), O>
+ where
+ &'context B: IntoIterator,
+ {
+ recurse_iter_impl!(self, 1)
+ }
+}
+
+impl<'context, A, A0, B, B0, C, D, D0, O> LockingTuple<'context, (A, B, C, D), (A0, B0, C, D0), O> {
+ /// Consume the tuple, and return the third element as a new tuple.
+ #[must_use]
+ // The type is impossible to refactor, and I already wrote this function, so no point in removing it
+ #[expect(clippy::type_complexity)]
+ pub fn recurse_2(self) -> RecurseReturn<'context, C, ((), (), (), D), (A0, B0, C, D0), O> {
+ recurse_impl!(self, 2)
+ }
+
+ /// Consume the tuple, and return the third element as a locking iterator.
+ // The type is impossible to refactor, and I already wrote this function, so no point in removing it
+ #[expect(clippy::type_complexity)]
+ pub fn recurse_2_iter(
+ self,
+ ) -> RecurseIterReturn<'context, C, ((), (), (), D), (A0, B0, C, D0), O>
+ where
+ &'context C: IntoIterator,
+ {
+ recurse_iter_impl!(self, 2)
+ }
+}
+
+impl<'context, A, A0, B, B0, C, C0, D, O> LockingTuple<'context, (A, B, C, D), (A0, B0, C0, D), O> {
+ /// Consume the tuple, and return the fourth element as a new tuple.
+ #[must_use]
+ // The type is impossible to refactor, and I already wrote this function, so no point in removing it
+ #[expect(clippy::type_complexity)]
+ pub fn recurse_3(self) -> RecurseReturn<'context, D, ((), (), (), ()), (A0, B0, C0, D), O> {
+ recurse_impl!(self, 3)
+ }
+
+ /// Consume the tuple, and return the first element as a locking iterator.
+ // The type is impossible to refactor, and I already wrote this function, so no point in removing it
+ #[expect(clippy::type_complexity)]
+ pub fn recurse_3_iter(
+ self,
+ ) -> RecurseIterReturn<'context, D, ((), (), (), ()), (A0, B0, C0, D), O>
+ where
+ &'context D: IntoIterator,
+ {
+ recurse_iter_impl!(self, 3)
+ }
+}
diff --git a/src/iterator.rs b/src/iterator.rs
deleted file mode 100644
index 79a2cc5..0000000
--- a/src/iterator.rs
+++ /dev/null
@@ -1,29 +0,0 @@
-use std::marker::PhantomData;
-
-use crate::ThreadKey;
-
-mod context;
-mod guard;
-mod iterator;
-mod tuple;
-
-pub struct LockingIterator<'context, I> {
- key: &'context ThreadKey,
- iterator: I,
-}
-
-pub struct LockingTuple<'context, L, C> {
- _lockable: PhantomData<L>,
- key: &'context ThreadKey,
- tuple: &'context C,
-}
-
-pub struct LockContext<'l, L> {
- key: Option<ThreadKey>,
- lockable: &'l L,
-}
-
-pub struct IteratorGuard<'a, Guard, Key> {
- _key: &'a Key,
- guard: Guard,
-}
diff --git a/src/iterator/context.rs b/src/iterator/context.rs
deleted file mode 100644
index a5c9123..0000000
--- a/src/iterator/context.rs
+++ /dev/null
@@ -1,52 +0,0 @@
-use std::marker::PhantomData;
-
-use crate::{
- iterator::{LockContext, LockingIterator, LockingTuple},
- lockable::Lockable,
- ThreadKey,
-};
-
-impl<'l, L> LockContext<'l, L> {
- pub(crate) const fn new(lockable: &'l L) -> Self {
- Self {
- key: None,
- lockable,
- }
- }
-}
-
-impl<L: Lockable> LockContext<'_, L> {
- pub fn tuple(&mut self, key: ThreadKey) -> LockingTuple<'_, L, L> {
- unsafe {
- self.key = Some(key);
-
- LockingTuple {
- _lockable: PhantomData,
- // safety: we just inserted a key
- key: self.key.as_ref().unwrap_unchecked(),
- tuple: self.lockable,
- }
- }
- }
-}
-
-impl<'l, L> LockContext<'l, L>
-where
- &'l L: IntoIterator,
-{
- #[expect(clippy::iter_not_returning_iterator)]
- pub fn iter(
- &mut self,
- key: ThreadKey,
- ) -> LockingIterator<'_, <&'l L as IntoIterator>::IntoIter> {
- unsafe {
- self.key = Some(key);
-
- LockingIterator {
- // safety: we just inserted a key
- key: self.key.as_ref().unwrap_unchecked(),
- iterator: self.lockable.into_iter(),
- }
- }
- }
-}
diff --git a/src/iterator/iterator.rs b/src/iterator/iterator.rs
deleted file mode 100644
index 316a276..0000000
--- a/src/iterator/iterator.rs
+++ /dev/null
@@ -1,118 +0,0 @@
-use std::iter::{Enumerate, Fuse, Skip, Take};
-
-use super::{IteratorGuard, LockingIterator};
-
-use crate::{
- lockable::{Lockable, RawLock, Sharable},
- ThreadKey,
-};
-
-impl<'l, I> LockingIterator<'l, I> {
- fn with_iterator<M>(self, f: impl FnOnce(I) -> M) -> LockingIterator<'l, M> {
- LockingIterator {
- key: self.key,
- iterator: f(self.iterator),
- }
- }
-}
-
-impl<'c, L: 'c + Iterator<Item = &'c I>, I: 'c + RawLock + Lockable> LockingIterator<'c, L> {
- pub fn lock_next(
- &mut self,
- ) -> Option<IteratorGuard<'c, <I as Lockable>::Guard<'c>, ThreadKey>> {
- if let Some(lock) = self.iterator.next() {
- unsafe {
- lock.raw_write();
- let guard = lock.guard();
-
- Some(IteratorGuard {
- _key: self.key,
- guard,
- })
- }
- } else {
- None
- }
- }
-
- pub fn lock_last(self) -> Option<IteratorGuard<'c, <I as Lockable>::Guard<'c>, ThreadKey>> {
- self.iterator.last().map(|lock| unsafe {
- lock.raw_write();
- let guard = lock.guard();
-
- IteratorGuard {
- _key: self.key,
- guard,
- }
- })
- }
-}
-
-impl<'c, L: 'c + Iterator<Item = &'c I>, I: 'c + RawLock + Sharable> LockingIterator<'c, L> {
- pub fn read_next(
- &mut self,
- ) -> Option<IteratorGuard<'c, <I as Sharable>::ReadGuard<'c>, ThreadKey>> {
- if let Some(lock) = self.iterator.next() {
- unsafe {
- lock.raw_read();
- let guard = lock.read_guard();
-
- Some(IteratorGuard {
- _key: self.key,
- guard,
- })
- }
- } else {
- None
- }
- }
-
- pub fn read_last(self) -> Option<IteratorGuard<'c, <I as Sharable>::ReadGuard<'c>, ThreadKey>> {
- self.iterator.last().map(|lock| unsafe {
- lock.raw_read();
- let guard = lock.read_guard();
-
- IteratorGuard {
- _key: self.key,
- guard,
- }
- })
- }
-}
-
-impl<'l, L: Iterator> LockingIterator<'l, L> {
- pub fn skip_next(&mut self) -> Option<L::Item> {
- self.iterator.next()
- }
-
- pub fn skip_mut(&mut self, n: usize) {
- for _ in 0..n {
- self.iterator.next();
- }
- }
-
- #[must_use]
- pub fn size_hint(&self) -> (usize, Option<usize>) {
- self.iterator.size_hint()
- }
-
- #[must_use]
- pub fn enumerate(self) -> LockingIterator<'l, Enumerate<L>> {
- self.with_iterator(Iterator::enumerate)
- }
-
- #[must_use]
- pub fn skip(self, n: usize) -> LockingIterator<'l, Skip<L>> {
- self.with_iterator(|i| i.skip(n))
- }
-
- #[must_use]
- pub fn take(self, n: usize) -> LockingIterator<'l, Take<L>> {
- self.with_iterator(|i| i.take(n))
- }
-
- #[must_use]
- pub fn fuse(self) -> LockingIterator<'l, Fuse<L>> {
- self.with_iterator(Iterator::fuse)
- }
-}
diff --git a/src/iterator/tuple.rs b/src/iterator/tuple.rs
deleted file mode 100644
index 855cdc7..0000000
--- a/src/iterator/tuple.rs
+++ /dev/null
@@ -1,202 +0,0 @@
-use std::marker::PhantomData;
-
-use crate::{
- iterator::{IteratorGuard, LockingTuple},
- lockable::{Lockable, RawLock},
- ThreadKey,
-};
-
-impl<'c, A, B> LockingTuple<'c, A, B> {
- const fn transmute<C>(self) -> LockingTuple<'c, C, B> {
- LockingTuple {
- _lockable: PhantomData,
- key: self.key,
- tuple: self.tuple,
- }
- }
-}
-
-macro_rules! lock_impl {
- ($self: expr, $field: tt) => {
- unsafe {
- $self.tuple.$field.raw_write();
- (
- IteratorGuard {
- _key: &$self.key,
- guard: $self.tuple.$field.guard(),
- },
- $self.transmute(),
- )
- }
- };
-}
-
-macro_rules! recurse_impl {
- ($self: expr, $field: tt) => {
- LockingTuple {
- _lockable: PhantomData,
- key: $self.key,
- tuple: &$self.tuple.$field,
- }
- };
-}
-
-type LockReturn<'a, 'context, Guarded, L, C> = (
- IteratorGuard<'a, <Guarded as Lockable>::Guard<'a>, ThreadKey>,
- LockingTuple<'context, L, C>,
-);
-
-impl<'context, A: RawLock + Lockable> LockingTuple<'context, (A,), (A,)> {
- #[must_use]
- pub fn lock_0<'a>(self) -> LockReturn<'a, 'context, A, ((),), (A,)>
- where
- 'context: 'a,
- {
- lock_impl!(self, 0)
- }
-}
-
-impl<'context, A> LockingTuple<'context, (A,), (A,)> {
- #[must_use]
- pub const fn recurse_0(self) -> LockingTuple<'context, A, A> {
- recurse_impl!(self, 0)
- }
-}
-
-impl<'context, A: RawLock + Lockable, B, B0> LockingTuple<'context, (A, B), (A, B0)> {
- #[must_use]
- pub fn lock_0<'a>(self) -> LockReturn<'a, 'context, A, ((), B), (A, B0)>
- where
- 'context: 'a,
- {
- lock_impl!(self, 0)
- }
-}
-
-impl<'context, A, B, B0> LockingTuple<'context, (A, B), (A, B0)> {
- #[must_use]
- pub const fn recurse_0(self) -> LockingTuple<'context, A, A> {
- recurse_impl!(self, 0)
- }
-}
-
-impl<'context, A: Lockable + RawLock, B> LockingTuple<'context, (A, B), (A, B)> {
- #[must_use]
- pub fn lock_and_recurse<'a>(
- self,
- ) -> (
- IteratorGuard<'a, <A as Lockable>::Guard<'a>, ThreadKey>,
- LockingTuple<'context, B, B>,
- )
- where
- 'context: 'a,
- {
- unsafe {
- self.tuple.0.raw_write();
- (
- IteratorGuard {
- _key: self.key,
- guard: self.tuple.0.guard(),
- },
- LockingTuple {
- _lockable: PhantomData,
- key: self.key,
- tuple: &self.tuple.1,
- },
- )
- }
- }
-}
-
-impl<'context, A, A0, B: RawLock + Lockable> LockingTuple<'context, (A, B), (A0, B)> {
- #[must_use]
- pub fn lock_1<'a>(self) -> LockReturn<'a, 'context, B, ((), ()), (A0, B)>
- where
- 'context: 'a,
- {
- lock_impl!(self, 1)
- }
-}
-
-impl<'context, A, A0, B> LockingTuple<'context, (A, B), (A0, B)> {
- #[must_use]
- pub const fn recurse_1(self) -> LockingTuple<'context, B, B> {
- recurse_impl!(self, 1)
- }
-}
-
-impl<'context, A: RawLock + Lockable, B, B0, C, C0> LockingTuple<'context, (A, B, C), (A, B0, C0)> {
- #[must_use]
- pub fn lock_0<'a>(self) -> LockReturn<'a, 'context, A, ((), B, C), (A, B0, C0)>
- where
- 'context: 'a,
- {
- lock_impl!(self, 0)
- }
-}
-
-impl<'context, A, B, B0, C, C0> LockingTuple<'context, (A, B, C), (A, B0, C0)> {
- #[must_use]
- pub const fn recurse_0(self) -> LockingTuple<'context, A, A> {
- recurse_impl!(self, 0)
- }
-}
-
-impl<'context, A, A0, B: RawLock + Lockable, C, C0> LockingTuple<'context, (A, B, C), (A0, B, C0)> {
- #[must_use]
- pub fn lock_1<'a>(self) -> LockReturn<'a, 'context, B, ((), (), C), (A0, B, C0)>
- where
- 'context: 'a,
- {
- lock_impl!(self, 1)
- }
-}
-
-impl<'context, A, A0, B, C, C0> LockingTuple<'context, (A, B, C), (A0, B, C0)> {
- #[must_use]
- pub const fn recurse_1(self) -> LockingTuple<'context, B, B> {
- recurse_impl!(self, 1)
- }
-}
-
-impl<'context, A, A0, B, B0, C: RawLock + Lockable> LockingTuple<'context, (A, B, C), (A0, B0, C)> {
- #[must_use]
- pub fn lock_2<'a>(self) -> LockReturn<'a, 'context, C, ((), (), ()), (A0, B0, C)>
- where
- 'context: 'a,
- {
- lock_impl!(self, 2)
- }
-}
-
-impl<'context, A, A0, B, B0, C> LockingTuple<'context, (A, B, C), (A0, B0, C)> {
- #[must_use]
- pub const fn recurse_2(self) -> LockingTuple<'context, C, C> {
- recurse_impl!(self, 2)
- }
-}
-
-impl<'context, A, B, B0, C, C0, D, D0> LockingTuple<'context, (A, B, C, D), (A, B0, C0, D0)> {
- #[must_use]
- pub const fn recurse_0(self) -> LockingTuple<'context, A, A> {
- recurse_impl!(self, 0)
- }
-}
-impl<'context, A, A0, B, C, C0, D, D0> LockingTuple<'context, (A, B, C, D), (A0, B, C0, D0)> {
- #[must_use]
- pub const fn recurse_1(self) -> LockingTuple<'context, B, B> {
- recurse_impl!(self, 1)
- }
-}
-impl<'context, A, A0, B, B0, C, D, D0> LockingTuple<'context, (A, B, C, D), (A0, B0, C, D0)> {
- #[must_use]
- pub const fn recurse_2(self) -> LockingTuple<'context, C, C> {
- recurse_impl!(self, 2)
- }
-}
-impl<'context, A, A0, B, B0, C, C0, D> LockingTuple<'context, (A, B, C, D), (A0, B0, C0, D)> {
- #[must_use]
- pub const fn recurse_3(self) -> LockingTuple<'context, D, D> {
- recurse_impl!(self, 3)
- }
-}
diff --git a/src/lib.rs b/src/lib.rs
index fc9e495..e6e5e4c 100755
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -198,7 +198,7 @@ mod handle_unwind;
mod key;
pub mod collection;
-pub mod iterator;
+pub mod context;
pub mod lockable;
pub mod mutex;
pub mod poisonable;
@@ -219,6 +219,9 @@ pub use mutex::SpinLock;
/// [`BoxedLockCollection`]: collection::BoxedLockCollection
pub type LockCollection<L> = collection::BoxedLockCollection<L>;
+/// A re-export for [`context::LockContext`]
+pub type LockContext<'l, L> = context::LockContext<'l, L>;
+
/// A re-export for [`poisonable::Poisonable`]
pub type Poisonable<L> = poisonable::Poisonable<L>;
diff --git a/src/lockable.rs b/src/lockable.rs
index badbe8e..705887f 100755
--- a/src/lockable.rs
+++ b/src/lockable.rs
@@ -197,6 +197,12 @@ pub unsafe trait Sharable: Lockable {
///
/// There must not be any two values which can unlock the value at the same
/// time, i.e., this must either be an owned value or a mutable reference.
+///
+/// The implementation of [`Lockable::get_ptrs`] must return the locks in the
+/// same order that they would be locked in if this lockable were passed into a
+/// [`LockContext`].
+///
+/// [`LockContext`]: `crate::context::LockContext`
pub unsafe trait OwnedLockable: Lockable {}
/// A trait which indicates that `into_inner` is a valid operation for a
diff --git a/src/mutex.rs b/src/mutex.rs
index 0d6aa73..d7b0176 100755
--- a/src/mutex.rs
+++ b/src/mutex.rs
@@ -170,6 +170,8 @@ pub struct MutexRef<'a, T: ?Sized + 'a, R: RawMutex>(&'a Mutex<T, R>, PhantomDat
//
// This is the most lifetime-intensive thing I've ever written. Can I graduate
// from borrow checker university now?
+//
+// As an update, I've now written `LockContext`. That was even more challenging
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: ThreadKey,
diff --git a/src/poisonable/poisonable.rs b/src/poisonable/poisonable.rs
index dd12cea..0134ff1 100755
--- a/src/poisonable/poisonable.rs
+++ b/src/poisonable/poisonable.rs
@@ -1,10 +1,11 @@
use std::panic::{RefUnwindSafe, UnwindSafe};
+use crate::collection::OwnedLockCollection;
use crate::handle_unwind::handle_unwind;
use crate::lockable::{
Lockable, LockableGetMut, LockableIntoInner, OwnedLockable, RawLock, Sharable,
};
-use crate::{Keyable, ThreadKey};
+use crate::{Keyable, LockContext, ThreadKey};
use super::{
PoisonError, PoisonFlag, PoisonGuard, PoisonRef, PoisonResult, Poisonable,
@@ -853,5 +854,24 @@ impl<L: LockableGetMut + RawLock> Poisonable<L> {
}
}
+impl<L: OwnedLockable> Poisonable<OwnedLockCollection<L>> {
+ /// Creates a context that can be used to iterate over the items in order.
+ ///
+ /// For more information, see [`OwnedLockCollection::context`].
+ ///
+ /// # Errors
+ ///
+ /// If another user of this lock panicked while holding the lock, then
+ /// this call will return an error instead. A `Poisonable` is poisoned
+ /// whenever a thread panics while holding a lock.
+ pub fn context(&self) -> PoisonResult<LockContext<'_, L>> {
+ if self.is_poisoned() {
+ Ok(self.inner.context())
+ } else {
+ Err(PoisonError::new(self.inner.context()))
+ }
+ }
+}
+
impl<L: UnwindSafe> RefUnwindSafe for Poisonable<L> {}
impl<L: UnwindSafe> UnwindSafe for Poisonable<L> {}
diff --git a/tarpaulin-report.html b/tarpaulin-report.html
index bac34c6..1525bec 100755
--- a/tarpaulin-report.html
+++ b/tarpaulin-report.html
@@ -2,9 +2,36 @@
<html>
<head>
<meta charset="utf-8">
- <style>html, body {
+ <style>:root {
+ --color: black;
+ --bg: white;
+ --head-bg: white;
+ --link: #338;
+
+ --blue: #ccf;
+ --red: #fcc;
+ --yellow: #ffc;
+ --green: #cfc;
+}
+
+[data-theme='dark'] {
+ --color: white;
+ --bg: black;
+ --head-bg: #333;
+ --link: #aaf;
+
+ --blue: #225;
+ --red: #522;
+ --yellow: #552;
+ --green: #252;
+}
+
+html,
+body {
margin: 0;
padding: 0;
+ color: var(--color);
+ background: var(--bg);
}
.app {
@@ -25,7 +52,7 @@
border: 1px solid #999;
text-align: left;
font-weight: normal;
- background: #ddd;
+ background: var(--head-bg);
}
.files-list__body {
}
@@ -33,7 +60,7 @@
cursor: pointer;
}
.files-list__file:hover {
- background: #ccf;
+ background: var(--blue);
}
.files-list__file > td {
padding: 10px;
@@ -44,13 +71,13 @@
margin-right: 1em;
}
.files-list__file_low {
- background: #fcc;
+ background: var(--red);
}
.files-list__file_medium {
- background: #ffc;
+ background: var(--yellow);
}
.files-list__file_high {
- background: #cfc;
+ background: var(--green);
}
.files-list__file_folder > td:first-child::before {
content: '\01F4C1';
@@ -64,7 +91,7 @@
align-items: center;
position: sticky;
top: 0;
- background: white;
+ background: var(--bg);
}
.file-header__back {
@@ -73,7 +100,7 @@
flex-shrink: 0;
flex-grow: 0;
text-decoration: underline;
- color: #338;
+ color: var(--link);
}
.file-header__name {
@@ -98,28 +125,76 @@
}
.code-line::before {
- content: counter(line);
- margin-right: 10px;
+ content: counter(line);
+ margin-right: 72px;
}
.code-line {
margin: 0;
- padding: 0.3em;
height: 1em;
counter-increment: line;
+
+ position: absolute;
+ padding: 0 0.3em 0.3em 0.3em;
+ display: inherit;
+ width: 100%;
}
.code-line_covered {
- background: #cfc;
+ background: var(--green);
}
.code-line_uncovered {
- background: #fcc;
+ background: var(--red);
+}
+
+.code-text-container {
+ position: relative;
+ height: 1em;
+ padding: 0.3em 0;
+}
+
+.cover-indicator {
+ display: flex;
+ width: 100%;
+ position: absolute;
+ justify-content: end;
+ height: 1em;
+ align-items: center;
+ padding: 0 0.3em 0.3em 0.3em;
+}
+
+.cover-indicator.check-cover::after {
+ content: "\2713";
+ font-weight: bold;
+ background-color: var(--green);
+ height: 1em;
+}
+
+.cover-indicator.no-cover::after {
+ content: "\2716";
+ font-weight: bold;
+ background-color: var(--red);
+ height: 1em;
+}
+
+.stat-line-hit {
+ max-width: 48px;
+ overflow: hidden;
+ font-weight: bold;
+ margin-right: 4px;
+ background-color: var(--green);
+ position: relative;
+ top: 0.1em;
+}
+
+#theme-toggle-label {
+ margin-left: 1ch;
}
</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::{\n\tordered_contains_duplicates, scoped_read, scoped_try_read, scoped_try_write, scoped_write,\n};\nuse super::{utils, BoxedLockCollection, LockGuard};\n\nunsafe impl\u003cL: Lockable\u003e RawLock for BoxedLockCollection\u003cL\u003e {\n\t#[mutants::skip] // this should never be called\n\t#[cfg(not(tarpaulin_include))]\n\tfn poison(\u0026self) {\n\t\tfor lock in \u0026self.locks {\n\t\t\tlock.poison();\n\t\t}\n\t}\n\n\tunsafe fn raw_write(\u0026self) {\n\t\tutils::ordered_write(self.locks())\n\t}\n\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool {\n\t\tprintln!(\"{}\", self.locks().len());\n\t\tutils::ordered_try_write(self.locks())\n\t}\n\n\tunsafe fn raw_unlock_write(\u0026self) {\n\t\tfor lock in self.locks() {\n\t\t\tlock.raw_unlock_write();\n\t\t}\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tutils::ordered_read(self.locks());\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tutils::ordered_try_read(self.locks())\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tfor lock in self.locks() {\n\t\t\tlock.raw_unlock_read();\n\t\t}\n\t}\n}\n\nunsafe impl\u003cL: Lockable\u003e Lockable for BoxedLockCollection\u003cL\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= L::Guard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= L::DataMut\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tptrs.push(self);\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tself.child().guard()\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.child().data_mut()\n\t}\n}\n\nunsafe impl\u003cL: Sharable\u003e Sharable for BoxedLockCollection\u003cL\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= L::ReadGuard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= L::DataRef\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tself.child().read_guard()\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tself.child().data_ref()\n\t}\n}\n\nunsafe impl\u003cL: OwnedLockable\u003e OwnedLockable for BoxedLockCollection\u003cL\u003e {}\n\n// LockableGetMut can't be implemented because that would create mutable and\n// immutable references to the same value at the same time.\n\nimpl\u003cL: LockableIntoInner\u003e LockableIntoInner for BoxedLockCollection\u003cL\u003e {\n\ttype Inner = L::Inner;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tLockableIntoInner::into_inner(self.into_child())\n\t}\n}\n\nimpl\u003cL\u003e IntoIterator for BoxedLockCollection\u003cL\u003e\nwhere\n\tL: IntoIterator,\n{\n\ttype Item = \u003cL as IntoIterator\u003e::Item;\n\ttype IntoIter = \u003cL as IntoIterator\u003e::IntoIter;\n\n\tfn into_iter(self) -\u003e Self::IntoIter {\n\t\tself.into_child().into_iter()\n\t}\n}\n\nimpl\u003c'a, L\u003e IntoIterator for \u0026'a BoxedLockCollection\u003cL\u003e\nwhere\n\t\u0026'a L: IntoIterator,\n{\n\ttype Item = \u003c\u0026'a L as IntoIterator\u003e::Item;\n\ttype IntoIter = \u003c\u0026'a L as IntoIterator\u003e::IntoIter;\n\n\tfn into_iter(self) -\u003e Self::IntoIter {\n\t\tself.child().into_iter()\n\t}\n}\n\nimpl\u003cL: OwnedLockable, I: FromIterator\u003cL\u003e + OwnedLockable\u003e FromIterator\u003cL\u003e\n\tfor BoxedLockCollection\u003cI\u003e\n{\n\tfn from_iter\u003cT: IntoIterator\u003cItem = L\u003e\u003e(iter: T) -\u003e Self {\n\t\tlet iter: I = iter.into_iter().collect();\n\t\tSelf::new(iter)\n\t}\n}\n\n// safety: the RawLocks must be send because they come from the Send Lockable\n#[allow(clippy::non_send_fields_in_send_ty)]\nunsafe impl\u003cL: Send\u003e Send for BoxedLockCollection\u003cL\u003e {}\nunsafe impl\u003cL: Sync\u003e Sync for BoxedLockCollection\u003cL\u003e {}\n\nimpl\u003cL\u003e Drop for BoxedLockCollection\u003cL\u003e {\n\t#[mutants::skip] // i can't test for a memory leak\n\t#[cfg(not(tarpaulin_include))]\n\tfn drop(\u0026mut self) {\n\t\tunsafe {\n\t\t\t// safety: this collection will never be locked again\n\t\t\tself.locks.clear();\n\t\t\t// safety: this was allocated using a box, and is now unique\n\t\t\tlet boxed: Box\u003cUnsafeCell\u003cL\u003e\u003e = Box::from_raw(self.data.cast_mut());\n\n\t\t\tdrop(boxed)\n\t\t}\n\t}\n}\n\nimpl\u003cT: ?Sized, L: AsRef\u003cT\u003e\u003e AsRef\u003cT\u003e for BoxedLockCollection\u003cL\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself.child().as_ref()\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cL: Debug\u003e Debug for BoxedLockCollection\u003cL\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tf.debug_struct(stringify!(BoxedLockCollection))\n\t\t\t.field(\"data\", \u0026self.data)\n\t\t\t.finish_non_exhaustive()\n\t}\n}\n\nimpl\u003cL: OwnedLockable + Default\u003e Default for BoxedLockCollection\u003cL\u003e {\n\tfn default() -\u003e Self {\n\t\tSelf::new(L::default())\n\t}\n}\n\nimpl\u003cL: OwnedLockable\u003e From\u003cL\u003e for BoxedLockCollection\u003cL\u003e {\n\tfn from(value: L) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\nimpl\u003cL\u003e BoxedLockCollection\u003cL\u003e {\n\t/// Gets the underlying collection, consuming this collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey, LockCollection};\n\t///\n\t/// let data1 = Mutex::new(42);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // data1 and data2 refer to distinct mutexes, so this won't panic\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = LockCollection::try_new(\u0026data).unwrap();\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let guard = lock.into_child().0.lock(key);\n\t/// assert_eq!(*guard, 42);\n\t/// ```\n\t#[must_use]\n\tpub fn into_child(mut self) -\u003e L {\n\t\tunsafe {\n\t\t\t// safety: this collection will never be used again\n\t\t\tstd::ptr::drop_in_place(\u0026mut self.locks);\n\t\t\t// safety: this was allocated using a box, and is now unique\n\t\t\tlet boxed: Box\u003cUnsafeCell\u003cL\u003e\u003e = Box::from_raw(self.data.cast_mut());\n\t\t\t// to prevent a double free\n\t\t\tstd::mem::forget(self);\n\n\t\t\tboxed.into_inner()\n\t\t}\n\t}\n\n\t// child_mut is immediate UB because it leads to mutable and immutable\n\t// references happening at the same time\n\n\t/// Gets an immutable reference to the underlying data\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey, LockCollection};\n\t///\n\t/// let data1 = Mutex::new(42);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // data1 and data2 refer to distinct mutexes, so this won't panic\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = LockCollection::try_new(\u0026data).unwrap();\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let guard = lock.child().0.lock(key);\n\t/// assert_eq!(*guard, 42);\n\t/// ```\n\t#[must_use]\n\tpub fn child(\u0026self) -\u003e \u0026L {\n\t\tunsafe {\n\t\t\tself.data\n\t\t\t\t.as_ref()\n\t\t\t\t.unwrap_unchecked()\n\t\t\t\t.get()\n\t\t\t\t.as_ref()\n\t\t\t\t.unwrap_unchecked()\n\t\t}\n\t}\n\n\t/// Gets the locks\n\tfn locks(\u0026self) -\u003e \u0026[\u0026dyn RawLock] {\n\t\t\u0026self.locks\n\t}\n}\n\nimpl\u003cL: OwnedLockable\u003e BoxedLockCollection\u003cL\u003e {\n\t/// Creates a new collection of owned locks.\n\t///\n\t/// Because the locks are owned, there's no need to do any checks for\n\t/// duplicate values.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t///\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t/// ```\n\t#[must_use]\n\tpub fn new(data: L) -\u003e Self {\n\t\t// safety: owned lockable types cannot contain duplicates\n\t\tunsafe { Self::new_unchecked(data) }\n\t}\n}\n\nimpl\u003c'a, L: OwnedLockable\u003e BoxedLockCollection\u003c\u0026'a L\u003e {\n\t/// Creates a new collection of owned locks.\n\t///\n\t/// Because the locks are owned, there's no need to do any checks for\n\t/// duplicate values.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t///\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = LockCollection::new_ref(\u0026data);\n\t/// ```\n\t#[must_use]\n\tpub fn new_ref(data: \u0026'a L) -\u003e Self {\n\t\t// safety: owned lockable types cannot contain duplicates\n\t\tunsafe { Self::new_unchecked(data) }\n\t}\n}\n\nimpl\u003cL: Lockable\u003e BoxedLockCollection\u003cL\u003e {\n\t/// Creates a new collections of locks.\n\t///\n\t/// # Safety\n\t///\n\t/// This results in undefined behavior if any locks are presented twice\n\t/// within this collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t///\n\t/// let data1 = Mutex::new(0);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // safety: data1 and data2 refer to distinct mutexes\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = unsafe { LockCollection::new_unchecked(\u0026data) };\n\t/// ```\n\t#[must_use]\n\tpub unsafe fn new_unchecked(data: L) -\u003e Self {\n\t\tlet data = Box::leak(Box::new(UnsafeCell::new(data)));\n\t\tlet data_ref = data.get().cast_const().as_ref().unwrap_unchecked();\n\n\t\tlet mut locks = Vec::new();\n\t\tdata_ref.get_ptrs(\u0026mut locks);\n\n\t\t// cast to *const () because fat pointers can't be converted to usize\n\t\tlocks.sort_by_key(|lock| (\u0026raw const **lock).cast::\u003c()\u003e() as usize);\n\n\t\t// safety we're just changing the lifetimes\n\t\tlet locks: Vec\u003c\u0026'static dyn RawLock\u003e = std::mem::transmute(locks);\n\t\tlet data = \u0026raw const *data;\n\t\tSelf { data, locks }\n\t}\n\n\t/// Creates a new collection of locks.\n\t///\n\t/// This returns `None` if any locks are found twice in the given\n\t/// collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t///\n\t/// let data1 = Mutex::new(0);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // data1 and data2 refer to distinct mutexes, so this won't panic\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = LockCollection::try_new(\u0026data).unwrap();\n\t/// ```\n\t#[must_use]\n\tpub fn try_new(data: L) -\u003e Option\u003cSelf\u003e {\n\t\t// safety: we are checking for duplicates before returning\n\t\tunsafe {\n\t\t\tlet this = Self::new_unchecked(data);\n\t\t\tif ordered_contains_duplicates(this.locks()) {\n\t\t\t\treturn None;\n\t\t\t}\n\t\t\tSome(this)\n\t\t}\n\t}\n\n\tpub fn scoped_lock\u003c'a, R\u003e(\u0026'a self, key: impl Keyable, f: impl Fn(L::DataMut\u003c'a\u003e) -\u003e R) -\u003e R {\n\t\tscoped_write(self, key, f)\n\t}\n\n\tpub fn scoped_try_lock\u003c'a, Key: Keyable, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(L::DataMut\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tscoped_try_write(self, key, f)\n\t}\n\n\t/// Locks the collection\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data. When the guard is dropped, the locks in the collection are also\n\t/// dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// ```\n\t#[must_use]\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::Guard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_write();\n\n\t\t\tLockGuard {\n\t\t\t\t// safety: we've already acquired the lock\n\t\t\t\tguard: self.child().guard(),\n\t\t\t\tkey,\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Attempts to lock the without blocking.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// locks when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If any locks in the collection are already locked, then an error\n\t/// containing the given key is returned.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// match lock.try_lock(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::Guard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tlet guard = unsafe {\n\t\t\tif !self.raw_try_write() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we've acquired the locks\n\t\t\tself.child().guard()\n\t\t};\n\n\t\tOk(LockGuard { guard, key })\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// let key = LockCollection::\u003c(Mutex\u003ci32\u003e, Mutex\u003c\u0026str\u003e)\u003e::unlock(guard);\n\t/// ```\n\tpub fn unlock(guard: LockGuard\u003cL::Guard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: Sharable\u003e BoxedLockCollection\u003cL\u003e {\n\tpub fn scoped_read\u003c'a, R\u003e(\u0026'a self, key: impl Keyable, f: impl Fn(L::DataRef\u003c'a\u003e) -\u003e R) -\u003e R {\n\t\tscoped_read(self, key, f)\n\t}\n\n\tpub fn scoped_try_read\u003c'a, Key: Keyable, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(L::DataRef\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tscoped_try_read(self, key, f)\n\t}\n\n\t/// Locks the collection, so that other threads can still read from it\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data immutably. When the guard is dropped, the locks in the collection\n\t/// are also dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// assert_eq!(*guard.0, 0);\n\t/// assert_eq!(*guard.1, \"\");\n\t/// ```\n\t#[must_use]\n\tpub fn read(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_read();\n\n\t\t\tLockGuard {\n\t\t\t\t// safety: we've already acquired the lock\n\t\t\t\tguard: self.child().read_guard(),\n\t\t\t\tkey,\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Attempts to lock the without blocking, in such a way that other threads\n\t/// can still read from the collection.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// shared access when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If any of the locks in the collection are already locked, then an error\n\t/// is returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(5), RwLock::new(\"6\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// match lock.try_read(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// assert_eq!(*guard.0, 5);\n\t/// assert_eq!(*guard.1, \"6\");\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_read(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tlet guard = unsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_read() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we've acquired the locks\n\t\t\tself.child().read_guard()\n\t\t};\n\n\t\tOk(LockGuard { guard, key })\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// let key = LockCollection::\u003c(RwLock\u003ci32\u003e, RwLock\u003c\u0026str\u003e)\u003e::unlock_read(guard);\n\t/// ```\n\tpub fn unlock_read(guard: LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: LockableIntoInner\u003e BoxedLockCollection\u003cL\u003e {\n\t/// Consumes this `BoxedLockCollection`, returning the underlying data.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t///\n\t/// let mutex = LockCollection::new([Mutex::new(0), Mutex::new(0)]);\n\t/// assert_eq!(mutex.into_inner(), [0, 0]);\n\t/// ```\n\t#[must_use]\n\tpub fn into_inner(self) -\u003e \u003cSelf as LockableIntoInner\u003e::Inner {\n\t\tLockableIntoInner::into_inner(self)\n\t}\n}\n\nimpl\u003c'a, L: 'a\u003e BoxedLockCollection\u003cL\u003e\nwhere\n\t\u0026'a L: IntoIterator,\n{\n\t/// Returns an iterator over references to each value in the collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = [Mutex::new(26), Mutex::new(1)];\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// let mut iter = lock.iter();\n\t/// let mutex = iter.next().unwrap();\n\t/// let guard = mutex.lock(key);\n\t///\n\t/// assert_eq!(*guard, 26);\n\t/// ```\n\t#[must_use]\n\tpub fn iter(\u0026'a self) -\u003e \u003c\u0026'a L as IntoIterator\u003e::IntoIter {\n\t\tself.into_iter()\n\t}\n}\n\n#[cfg(test)]\nmod tests {\n\tuse super::*;\n\tuse crate::{Mutex, RwLock, ThreadKey};\n\n\t#[test]\n\tfn from_iterator() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection: BoxedLockCollection\u003cVec\u003cMutex\u003c\u0026str\u003e\u003e\u003e =\n\t\t\t[Mutex::new(\"foo\"), Mutex::new(\"bar\"), Mutex::new(\"baz\")]\n\t\t\t\t.into_iter()\n\t\t\t\t.collect();\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], \"foo\");\n\t\tassert_eq!(*guard[1], \"bar\");\n\t\tassert_eq!(*guard[2], \"baz\");\n\t}\n\n\t#[test]\n\tfn from() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tBoxedLockCollection::from([Mutex::new(\"foo\"), Mutex::new(\"bar\"), Mutex::new(\"baz\")]);\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], \"foo\");\n\t\tassert_eq!(*guard[1], \"bar\");\n\t\tassert_eq!(*guard[2], \"baz\");\n\t}\n\n\t#[test]\n\tfn into_owned_iterator() {\n\t\tlet collection = BoxedLockCollection::new([Mutex::new(0), Mutex::new(1), Mutex::new(2)]);\n\t\tfor (i, mutex) in collection.into_iter().enumerate() {\n\t\t\tassert_eq!(mutex.into_inner(), i);\n\t\t}\n\t}\n\n\t#[test]\n\tfn into_ref_iterator() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::new([Mutex::new(0), Mutex::new(1), Mutex::new(2)]);\n\t\tfor (i, mutex) in (\u0026collection).into_iter().enumerate() {\n\t\t\tmutex.scoped_lock(\u0026mut key, |val| assert_eq!(*val, i))\n\t\t}\n\t}\n\n\t#[test]\n\tfn ref_iterator() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::new([Mutex::new(0), Mutex::new(1), Mutex::new(2)]);\n\t\tfor (i, mutex) in collection.iter().enumerate() {\n\t\t\tmutex.scoped_lock(\u0026mut key, |val| assert_eq!(*val, i))\n\t\t}\n\t}\n\n\t#[test]\n\t#[allow(clippy::float_cmp)]\n\tfn uses_correct_default() {\n\t\tlet collection =\n\t\t\tBoxedLockCollection::\u003c(Mutex\u003cf64\u003e, Mutex\u003cOption\u003ci32\u003e\u003e, Mutex\u003cusize\u003e)\u003e::default();\n\t\tlet tuple = collection.into_inner();\n\t\tassert_eq!(tuple.0, 0.0);\n\t\tassert!(tuple.1.is_none());\n\t\tassert_eq!(tuple.2, 0)\n\t}\n\n\t#[test]\n\tfn non_duplicates_allowed() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(1);\n\t\tassert!(BoxedLockCollection::try_new([\u0026mutex1, \u0026mutex2]).is_some())\n\t}\n\n\t#[test]\n\tfn duplicates_not_allowed() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tassert!(BoxedLockCollection::try_new([\u0026mutex1, \u0026mutex1]).is_none())\n\t}\n\n\t#[test]\n\tfn scoped_read_sees_changes() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = BoxedLockCollection::new(mutexes);\n\t\tcollection.scoped_lock(\u0026mut key, |guard| *guard[0] = 128);\n\n\t\tlet sum = collection.scoped_read(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 128);\n\t\t\tassert_eq!(*guard[1], 42);\n\t\t\t*guard[0] + *guard[1]\n\t\t});\n\n\t\tassert_eq!(sum, 128 + 42);\n\t}\n\n\t#[test]\n\tfn scoped_try_lock_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::new([Mutex::new(1), Mutex::new(2)]);\n\t\tlet guard = collection.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = collection.scoped_try_lock(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn scoped_try_read_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::new([RwLock::new(1), RwLock::new(2)]);\n\t\tlet guard = collection.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = collection.scoped_try_read(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn try_lock_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::new([Mutex::new(1), Mutex::new(2)]);\n\t\tlet guard = collection.try_lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_lock(key);\n\t\t\t\tassert!(guard.is_err());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn try_read_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::new([RwLock::new(1), RwLock::new(2)]);\n\t\tlet guard = collection.try_read(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_read(key);\n\t\t\t\tassert!(guard.is_ok());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn try_lock_fails_with_one_exclusive_lock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = [Mutex::new(1), Mutex::new(2)];\n\t\tlet collection = BoxedLockCollection::new_ref(\u0026locks);\n\t\tlet guard = locks[1].try_lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_lock(key);\n\t\t\t\tassert!(guard.is_err());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn try_read_fails_during_exclusive_lock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::new([RwLock::new(1), RwLock::new(2)]);\n\t\tlet guard = collection.try_lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_read(key);\n\t\t\t\tassert!(guard.is_err());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn try_read_fails_with_one_exclusive_lock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = [RwLock::new(1), RwLock::new(2)];\n\t\tlet collection = BoxedLockCollection::new_ref(\u0026locks);\n\t\tlet guard = locks[1].try_write(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_read(key);\n\t\t\t\tassert!(guard.is_err());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn unlock_collection_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex1 = Mutex::new(\"foo\");\n\t\tlet mutex2 = Mutex::new(\"bar\");\n\t\tlet collection = BoxedLockCollection::try_new((\u0026mutex1, \u0026mutex2)).unwrap();\n\t\tlet guard = collection.lock(key);\n\t\tlet key = BoxedLockCollection::\u003c(\u0026Mutex\u003c_\u003e, \u0026Mutex\u003c_\u003e)\u003e::unlock(guard);\n\n\t\tassert!(mutex1.try_lock(key).is_ok())\n\t}\n\n\t#[test]\n\tfn read_unlock_collection_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock1 = RwLock::new(\"foo\");\n\t\tlet lock2 = RwLock::new(\"bar\");\n\t\tlet collection = BoxedLockCollection::try_new((\u0026lock1, \u0026lock2)).unwrap();\n\t\tlet guard = collection.read(key);\n\t\tlet key = BoxedLockCollection::\u003c(\u0026RwLock\u003c_\u003e, \u0026RwLock\u003c_\u003e)\u003e::unlock_read(guard);\n\n\t\tassert!(lock1.try_write(key).is_ok())\n\t}\n\n\t#[test]\n\tfn into_inner_works() {\n\t\tlet collection = BoxedLockCollection::new((Mutex::new(\"Hello\"), Mutex::new(47)));\n\t\tassert_eq!(collection.into_inner(), (\"Hello\", 47))\n\t}\n\n\t#[test]\n\tfn works_in_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex1 = RwLock::new(0);\n\t\tlet mutex2 = RwLock::new(1);\n\t\tlet collection =\n\t\t\tBoxedLockCollection::try_new(BoxedLockCollection::try_new([\u0026mutex1, \u0026mutex2]).unwrap())\n\t\t\t\t.unwrap();\n\n\t\tlet mut guard = collection.lock(key);\n\t\tassert!(mutex1.is_locked());\n\t\tassert!(mutex2.is_locked());\n\t\tassert_eq!(*guard[0], 0);\n\t\tassert_eq!(*guard[1], 1);\n\t\t*guard[0] = 2;\n\t\tlet key = BoxedLockCollection::\u003cBoxedLockCollection\u003c[\u0026RwLock\u003c_\u003e; 2]\u003e\u003e::unlock(guard);\n\n\t\tlet guard = collection.read(key);\n\t\tassert!(mutex1.is_locked());\n\t\tassert!(mutex2.is_locked());\n\t\tassert_eq!(*guard[0], 2);\n\t\tassert_eq!(*guard[1], 1);\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn as_ref_works() {\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet collection = BoxedLockCollection::new_ref(\u0026mutexes);\n\n\t\tassert!(std::ptr::addr_eq(\u0026mutexes, collection.as_ref()))\n\t}\n\n\t#[test]\n\tfn child() {\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet collection = BoxedLockCollection::new_ref(\u0026mutexes);\n\n\t\tassert!(std::ptr::addr_eq(\u0026mutexes, *collection.child()))\n\t}\n}\n","traces":[{"line":21,"address":[226016],"length":1,"stats":{"Line":24}},{"line":22,"address":[],"length":0,"stats":{"Line":23}},{"line":25,"address":[1547968,1548096,1548352,1548480,1547840,1548224],"length":1,"stats":{"Line":6}},{"line":26,"address":[1548109,1547981,1548237,1547853,1548365,1548493],"length":1,"stats":{"Line":6}},{"line":27,"address":[1548319,1548063,1548191,1548447,1548575,1547935],"length":1,"stats":{"Line":6}},{"line":30,"address":[],"length":0,"stats":{"Line":7}},{"line":31,"address":[],"length":0,"stats":{"Line":14}},{"line":32,"address":[],"length":0,"stats":{"Line":7}},{"line":36,"address":[1549840,1549872,1549680,1549808,1549648,1549712,1549616,1549776,1549744],"length":1,"stats":{"Line":10}},{"line":37,"address":[180901],"length":1,"stats":{"Line":10}},{"line":40,"address":[189216],"length":1,"stats":{"Line":4}},{"line":41,"address":[1550005,1549941,1549973,1549909],"length":1,"stats":{"Line":4}},{"line":44,"address":[1550368,1550480,1550144,1550256,1550032],"length":1,"stats":{"Line":4}},{"line":45,"address":[],"length":0,"stats":{"Line":8}},{"line":46,"address":[],"length":0,"stats":{"Line":4}},{"line":62,"address":[],"length":0,"stats":{"Line":1}},{"line":63,"address":[],"length":0,"stats":{"Line":1}},{"line":66,"address":[1550656],"length":1,"stats":{"Line":1}},{"line":67,"address":[],"length":0,"stats":{"Line":1}},{"line":70,"address":[],"length":0,"stats":{"Line":7}},{"line":71,"address":[],"length":0,"stats":{"Line":7}},{"line":86,"address":[],"length":0,"stats":{"Line":1}},{"line":87,"address":[],"length":0,"stats":{"Line":1}},{"line":90,"address":[1551104,1551136,1551232,1551200],"length":1,"stats":{"Line":4}},{"line":91,"address":[],"length":0,"stats":{"Line":4}},{"line":103,"address":[],"length":0,"stats":{"Line":2}},{"line":104,"address":[1551278,1551342],"length":1,"stats":{"Line":2}},{"line":115,"address":[],"length":0,"stats":{"Line":1}},{"line":116,"address":[],"length":0,"stats":{"Line":1}},{"line":127,"address":[],"length":0,"stats":{"Line":1}},{"line":128,"address":[1551461],"length":1,"stats":{"Line":1}},{"line":135,"address":[],"length":0,"stats":{"Line":1}},{"line":136,"address":[],"length":0,"stats":{"Line":1}},{"line":137,"address":[1551535],"length":1,"stats":{"Line":1}},{"line":162,"address":[],"length":0,"stats":{"Line":1}},{"line":163,"address":[1551573],"length":1,"stats":{"Line":1}},{"line":178,"address":[],"length":0,"stats":{"Line":1}},{"line":179,"address":[1551614],"length":1,"stats":{"Line":1}},{"line":184,"address":[],"length":0,"stats":{"Line":1}},{"line":185,"address":[],"length":0,"stats":{"Line":1}},{"line":209,"address":[],"length":0,"stats":{"Line":3}},{"line":212,"address":[],"length":0,"stats":{"Line":3}},{"line":214,"address":[],"length":0,"stats":{"Line":3}},{"line":216,"address":[],"length":0,"stats":{"Line":3}},{"line":218,"address":[],"length":0,"stats":{"Line":3}},{"line":244,"address":[172560],"length":1,"stats":{"Line":25}},{"line":246,"address":[226729],"length":1,"stats":{"Line":27}},{"line":256,"address":[],"length":0,"stats":{"Line":37}},{"line":257,"address":[],"length":0,"stats":{"Line":37}},{"line":276,"address":[],"length":0,"stats":{"Line":16}},{"line":278,"address":[],"length":0,"stats":{"Line":19}},{"line":297,"address":[1555984,1555952,1555920],"length":1,"stats":{"Line":4}},{"line":299,"address":[],"length":0,"stats":{"Line":4}},{"line":324,"address":[226048,226490,226517],"length":1,"stats":{"Line":38}},{"line":325,"address":[180945,181052],"length":1,"stats":{"Line":80}},{"line":326,"address":[],"length":0,"stats":{"Line":41}},{"line":328,"address":[150063],"length":1,"stats":{"Line":41}},{"line":329,"address":[202526],"length":1,"stats":{"Line":39}},{"line":332,"address":[],"length":0,"stats":{"Line":103}},{"line":335,"address":[1557371,1565542,1564555,1563545,1568036,1560398,1558881,1559372,1561483,1562001,1565057,1569068,1556369,1562996,1569564,1568585,1558382,1560875,1557851,1562474,1556876,1566028,1566523,1564052,1566986,1567466,1559850],"length":1,"stats":{"Line":42}},{"line":336,"address":[150235],"length":1,"stats":{"Line":41}},{"line":358,"address":[226816,227062],"length":1,"stats":{"Line":15}},{"line":361,"address":[1570587,1570040,1570859,1571424,1570315,1569760,1571691,1571144],"length":1,"stats":{"Line":15}},{"line":362,"address":[181733,181794],"length":1,"stats":{"Line":30}},{"line":363,"address":[190050],"length":1,"stats":{"Line":1}},{"line":365,"address":[1570956,1569857,1571788,1570684,1570412,1571521,1571244,1570140],"length":1,"stats":{"Line":14}},{"line":369,"address":[],"length":0,"stats":{"Line":9}},{"line":370,"address":[],"length":0,"stats":{"Line":9}},{"line":373,"address":[1572224,1572256,1572288],"length":1,"stats":{"Line":3}},{"line":378,"address":[],"length":0,"stats":{"Line":3}},{"line":401,"address":[226690,226544],"length":1,"stats":{"Line":22}},{"line":404,"address":[1572334,1574286,1572880,1574174,1572558,1573344,1574046,1573502,1573630,1574576,1573040,1572446,1573776,1572704,1574416,1573198,1573918],"length":1,"stats":{"Line":21}},{"line":408,"address":[226620],"length":1,"stats":{"Line":19}},{"line":443,"address":[],"length":0,"stats":{"Line":6}},{"line":445,"address":[1574944,1574782,1575177,1575502,1575358,1575134,1575312,1575545,1574990,1574736],"length":1,"stats":{"Line":11}},{"line":446,"address":[1575183,1575369,1575551,1575001,1574793],"length":1,"stats":{"Line":4}},{"line":450,"address":[203275,203305],"length":1,"stats":{"Line":4}},{"line":453,"address":[1575425,1575616,1575057,1574849,1575248],"length":1,"stats":{"Line":2}},{"line":473,"address":[],"length":0,"stats":{"Line":11}},{"line":474,"address":[],"length":0,"stats":{"Line":12}},{"line":475,"address":[],"length":0,"stats":{"Line":0}},{"line":480,"address":[],"length":0,"stats":{"Line":4}},{"line":481,"address":[],"length":0,"stats":{"Line":4}},{"line":484,"address":[1576784,1576816],"length":1,"stats":{"Line":2}},{"line":489,"address":[],"length":0,"stats":{"Line":2}},{"line":512,"address":[],"length":0,"stats":{"Line":8}},{"line":515,"address":[181456],"length":1,"stats":{"Line":8}},{"line":519,"address":[1577718,1577448,1577126,1577590,1576902,1577014,1577272],"length":1,"stats":{"Line":7}},{"line":555,"address":[190112,190319],"length":1,"stats":{"Line":4}},{"line":558,"address":[190144,190194],"length":1,"stats":{"Line":7}},{"line":559,"address":[190205],"length":1,"stats":{"Line":3}},{"line":563,"address":[1578103,1578314,1577895,1578133,1577925,1578287],"length":1,"stats":{"Line":2}},{"line":566,"address":[190261],"length":1,"stats":{"Line":1}},{"line":584,"address":[],"length":0,"stats":{"Line":1}},{"line":585,"address":[1578382],"length":1,"stats":{"Line":1}},{"line":586,"address":[],"length":0,"stats":{"Line":0}},{"line":602,"address":[],"length":0,"stats":{"Line":2}},{"line":603,"address":[],"length":0,"stats":{"Line":2}},{"line":629,"address":[],"length":0,"stats":{"Line":1}},{"line":630,"address":[],"length":0,"stats":{"Line":1}}],"covered":98,"coverable":100},{"path":["/","home","botahamec","Projects","happylock","src","collection","guard.rs"],"content":"use std::fmt::{Debug, Display};\nuse std::hash::Hash;\nuse std::ops::{Deref, DerefMut};\n\nuse super::LockGuard;\n\n#[mutants::skip] // hashing involves RNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard: Hash\u003e Hash for LockGuard\u003cGuard\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.guard.hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard: Debug\u003e Debug for LockGuard\u003cGuard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cGuard: Display\u003e Display for LockGuard\u003cGuard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cGuard\u003e Deref for LockGuard\u003cGuard\u003e {\n\ttype Target = Guard;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t\u0026self.guard\n\t}\n}\n\nimpl\u003cGuard\u003e DerefMut for LockGuard\u003cGuard\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t\u0026mut self.guard\n\t}\n}\n\nimpl\u003cGuard\u003e AsRef\u003cGuard\u003e for LockGuard\u003cGuard\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026Guard {\n\t\t\u0026self.guard\n\t}\n}\n\nimpl\u003cGuard\u003e AsMut\u003cGuard\u003e for LockGuard\u003cGuard\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut Guard {\n\t\t\u0026mut self.guard\n\t}\n}\n\n#[cfg(test)]\nmod tests {\n\tuse crate::collection::OwnedLockCollection;\n\tuse crate::{LockCollection, Mutex, RwLock, ThreadKey};\n\n\t#[test]\n\tfn guard_display_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = OwnedLockCollection::new(RwLock::new(\"Hello, world!\"));\n\t\tlet guard = lock.read(key);\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn deref_mut_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = (Mutex::new(1), Mutex::new(2));\n\t\tlet lock = LockCollection::new_ref(\u0026locks);\n\t\tlet mut guard = lock.lock(key);\n\t\t*guard.0 = 3;\n\t\tlet key = LockCollection::\u003c(Mutex\u003c_\u003e, Mutex\u003c_\u003e)\u003e::unlock(guard);\n\n\t\tlet guard = locks.0.lock(key);\n\t\tassert_eq!(*guard, 3);\n\t\tlet key = Mutex::unlock(guard);\n\n\t\tlet guard = locks.1.lock(key);\n\t\tassert_eq!(*guard, 2);\n\t}\n\n\t#[test]\n\tfn as_ref_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = (Mutex::new(1), Mutex::new(2));\n\t\tlet lock = LockCollection::new_ref(\u0026locks);\n\t\tlet mut guard = lock.lock(key);\n\t\t*guard.0 = 3;\n\t\tlet key = LockCollection::\u003c(Mutex\u003c_\u003e, Mutex\u003c_\u003e)\u003e::unlock(guard);\n\n\t\tlet guard = locks.0.lock(key);\n\t\tassert_eq!(guard.as_ref(), \u00263);\n\t\tlet key = Mutex::unlock(guard);\n\n\t\tlet guard = locks.1.lock(key);\n\t\tassert_eq!(guard.as_ref(), \u00262);\n\t}\n\n\t#[test]\n\tfn as_mut_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = (Mutex::new(1), Mutex::new(2));\n\t\tlet lock = LockCollection::new_ref(\u0026locks);\n\t\tlet mut guard = lock.lock(key);\n\t\tlet guard_mut = guard.as_mut();\n\t\t*guard_mut.0 = 3;\n\t\tlet key = LockCollection::\u003c(Mutex\u003c_\u003e, Mutex\u003c_\u003e)\u003e::unlock(guard);\n\n\t\tlet guard = locks.0.lock(key);\n\t\tassert_eq!(guard.as_ref(), \u00263);\n\t\tlet key = Mutex::unlock(guard);\n\n\t\tlet guard = locks.1.lock(key);\n\t\tassert_eq!(guard.as_ref(), \u00262);\n\t}\n}\n","traces":[{"line":24,"address":[],"length":0,"stats":{"Line":1}},{"line":25,"address":[],"length":0,"stats":{"Line":1}},{"line":32,"address":[],"length":0,"stats":{"Line":18}},{"line":33,"address":[],"length":0,"stats":{"Line":0}},{"line":38,"address":[],"length":0,"stats":{"Line":8}},{"line":39,"address":[],"length":0,"stats":{"Line":0}},{"line":44,"address":[],"length":0,"stats":{"Line":2}},{"line":45,"address":[],"length":0,"stats":{"Line":0}},{"line":50,"address":[],"length":0,"stats":{"Line":4}},{"line":51,"address":[],"length":0,"stats":{"Line":0}}],"covered":6,"coverable":10},{"path":["/","home","botahamec","Projects","happylock","src","collection","owned.rs"],"content":"use crate::lockable::{\n\tLockable, LockableGetMut, LockableIntoInner, OwnedLockable, RawLock, Sharable,\n};\nuse crate::{Keyable, ThreadKey};\n\nuse super::utils::{scoped_read, scoped_try_read, scoped_try_write, scoped_write};\nuse super::{utils, LockGuard, OwnedLockCollection};\n\nunsafe impl\u003cL: Lockable\u003e RawLock for OwnedLockCollection\u003cL\u003e {\n\t#[mutants::skip] // this should never run\n\t#[cfg(not(tarpaulin_include))]\n\tfn poison(\u0026self) {\n\t\tlet locks = utils::get_locks_unsorted(\u0026self.data);\n\t\tfor lock in locks {\n\t\t\tlock.poison();\n\t\t}\n\t}\n\n\tunsafe fn raw_write(\u0026self) {\n\t\tutils::ordered_write(\u0026utils::get_locks_unsorted(\u0026self.data))\n\t}\n\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool {\n\t\tlet locks = utils::get_locks_unsorted(\u0026self.data);\n\t\tutils::ordered_try_write(\u0026locks)\n\t}\n\n\tunsafe fn raw_unlock_write(\u0026self) {\n\t\tlet locks = utils::get_locks_unsorted(\u0026self.data);\n\t\tfor lock in locks {\n\t\t\tlock.raw_unlock_write();\n\t\t}\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tutils::ordered_read(\u0026utils::get_locks_unsorted(\u0026self.data))\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tlet locks = utils::get_locks_unsorted(\u0026self.data);\n\t\tutils::ordered_try_read(\u0026locks)\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tlet locks = utils::get_locks_unsorted(\u0026self.data);\n\t\tfor lock in locks {\n\t\t\tlock.raw_unlock_read();\n\t\t}\n\t}\n}\n\nunsafe impl\u003cL: Lockable\u003e Lockable for OwnedLockCollection\u003cL\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= L::Guard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= L::DataMut\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\t#[mutants::skip] // It's hard to test lkocks in an OwnedLockCollection, because they're owned\n\t#[cfg(not(tarpaulin_include))]\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tptrs.push(self)\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tself.data.guard()\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.data.data_mut()\n\t}\n}\n\nimpl\u003cL: LockableGetMut\u003e LockableGetMut for OwnedLockCollection\u003cL\u003e {\n\ttype Inner\u003c'a\u003e\n\t\t= L::Inner\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tself.data.get_mut()\n\t}\n}\n\nimpl\u003cL: LockableIntoInner\u003e LockableIntoInner for OwnedLockCollection\u003cL\u003e {\n\ttype Inner = L::Inner;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tself.data.into_inner()\n\t}\n}\n\nunsafe impl\u003cL: Sharable\u003e Sharable for OwnedLockCollection\u003cL\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= L::ReadGuard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= L::DataRef\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tself.data.read_guard()\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tself.data.data_ref()\n\t}\n}\n\nunsafe impl\u003cL: OwnedLockable\u003e OwnedLockable for OwnedLockCollection\u003cL\u003e {}\n\nimpl\u003cL\u003e IntoIterator for OwnedLockCollection\u003cL\u003e\nwhere\n\tL: IntoIterator,\n{\n\ttype Item = \u003cL as IntoIterator\u003e::Item;\n\ttype IntoIter = \u003cL as IntoIterator\u003e::IntoIter;\n\n\tfn into_iter(self) -\u003e Self::IntoIter {\n\t\tself.data.into_iter()\n\t}\n}\n\nimpl\u003cL: OwnedLockable, I: FromIterator\u003cL\u003e + OwnedLockable\u003e FromIterator\u003cL\u003e\n\tfor OwnedLockCollection\u003cI\u003e\n{\n\tfn from_iter\u003cT: IntoIterator\u003cItem = L\u003e\u003e(iter: T) -\u003e Self {\n\t\tlet iter: I = iter.into_iter().collect();\n\t\tSelf::new(iter)\n\t}\n}\n\nimpl\u003cE: OwnedLockable + Extend\u003cL\u003e, L: OwnedLockable\u003e Extend\u003cL\u003e for OwnedLockCollection\u003cE\u003e {\n\tfn extend\u003cT: IntoIterator\u003cItem = L\u003e\u003e(\u0026mut self, iter: T) {\n\t\tself.data.extend(iter)\n\t}\n}\n\n// AsRef can't be implemented because an impl of AsRef\u003cL\u003e for L could break the\n// invariant that there is only one way to lock the collection. AsMut is fine,\n// because the collection can't be locked as long as the reference is valid.\n\nimpl\u003cT: ?Sized, L: AsMut\u003cT\u003e\u003e AsMut\u003cT\u003e for OwnedLockCollection\u003cL\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself.data.as_mut()\n\t}\n}\n\nimpl\u003cL: OwnedLockable + Default\u003e Default for OwnedLockCollection\u003cL\u003e {\n\tfn default() -\u003e Self {\n\t\tSelf::new(L::default())\n\t}\n}\n\nimpl\u003cL: OwnedLockable\u003e From\u003cL\u003e for OwnedLockCollection\u003cL\u003e {\n\tfn from(value: L) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\nimpl\u003cL: OwnedLockable\u003e OwnedLockCollection\u003cL\u003e {\n\t/// Creates a new collection of owned locks.\n\t///\n\t/// Because the locks are owned, there's no need to do any checks for\n\t/// duplicate values. The locks also don't need to be sorted by memory\n\t/// address because they aren't used anywhere else.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t/// ```\n\t#[must_use]\n\tpub const fn new(data: L) -\u003e Self {\n\t\tSelf { data }\n\t}\n\n\tpub fn scoped_lock\u003c'a, R\u003e(\u0026'a self, key: impl Keyable, f: impl Fn(L::DataMut\u003c'a\u003e) -\u003e R) -\u003e R {\n\t\tscoped_write(self, key, f)\n\t}\n\n\tpub fn scoped_try_lock\u003c'a, Key: Keyable, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(L::DataMut\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tscoped_try_write(self, key, f)\n\t}\n\n\t/// Locks the collection\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data. When the guard is dropped, the locks in the collection are also\n\t/// dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// ```\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::Guard\u003c'_\u003e\u003e {\n\t\tlet guard = unsafe {\n\t\t\t// safety: we have the thread key, and these locks happen in a\n\t\t\t// predetermined order\n\t\t\tself.raw_write();\n\n\t\t\t// safety: we've locked all of this already\n\t\t\tself.data.guard()\n\t\t};\n\n\t\tLockGuard { guard, key }\n\t}\n\n\t/// Attempts to lock the without blocking.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// locks when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If any of the locks in this collection are already locked, this returns\n\t/// an error containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// match lock.try_lock(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::Guard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tlet guard = unsafe {\n\t\t\tif !self.raw_try_write() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we've acquired the locks\n\t\t\tself.data.guard()\n\t\t};\n\n\t\tOk(LockGuard { guard, key })\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// let key = OwnedLockCollection::\u003c(Mutex\u003ci32\u003e, Mutex\u003c\u0026str\u003e)\u003e::unlock(guard);\n\t/// ```\n\t#[allow(clippy::missing_const_for_fn)]\n\tpub fn unlock(guard: LockGuard\u003cL::Guard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: Sharable\u003e OwnedLockCollection\u003cL\u003e {\n\tpub fn scoped_read\u003c'a, R\u003e(\u0026'a self, key: impl Keyable, f: impl Fn(L::DataRef\u003c'a\u003e) -\u003e R) -\u003e R {\n\t\tscoped_read(self, key, f)\n\t}\n\n\tpub fn scoped_try_read\u003c'a, Key: Keyable, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(L::DataRef\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tscoped_try_read(self, key, f)\n\t}\n\n\t/// Locks the collection, so that other threads can still read from it\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data immutably. When the guard is dropped, the locks in the collection\n\t/// are also dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// assert_eq!(*guard.0, 0);\n\t/// assert_eq!(*guard.1, \"\");\n\t/// ```\n\tpub fn read(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_read();\n\n\t\t\tLockGuard {\n\t\t\t\t// safety: we've already acquired the lock\n\t\t\t\tguard: self.data.read_guard(),\n\t\t\t\tkey,\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Attempts to lock the without blocking, in such a way that other threads\n\t/// can still read from the collection.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// shared access when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If any of the locks in this collection can't be acquired, then an error\n\t/// is returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(5), RwLock::new(\"6\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// match lock.try_read(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// assert_eq!(*guard.0, 5);\n\t/// assert_eq!(*guard.1, \"6\");\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_read(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tlet guard = unsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_read() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we've acquired the locks\n\t\t\tself.data.read_guard()\n\t\t};\n\n\t\tOk(LockGuard { guard, key })\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// let key = OwnedLockCollection::\u003c(RwLock\u003ci32\u003e, RwLock\u003c\u0026str\u003e)\u003e::unlock_read(guard);\n\t/// ```\n\t#[allow(clippy::missing_const_for_fn)]\n\tpub fn unlock_read(guard: LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL\u003e OwnedLockCollection\u003cL\u003e {\n\t/// Gets the underlying collection, consuming this collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let data = (Mutex::new(42), Mutex::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let inner = lock.into_child();\n\t/// let guard = inner.0.lock(key);\n\t/// assert_eq!(*guard, 42);\n\t/// ```\n\t#[must_use]\n\tpub fn into_child(self) -\u003e L {\n\t\tself.data\n\t}\n\n\t/// Gets a mutable reference to the underlying collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let data = (Mutex::new(42), Mutex::new(\"\"));\n\t/// let mut lock = OwnedLockCollection::new(data);\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut inner = lock.child_mut();\n\t/// let guard = inner.0.get_mut();\n\t/// assert_eq!(*guard, 42);\n\t/// ```\n\t#[must_use]\n\tpub fn child_mut(\u0026mut self) -\u003e \u0026mut L {\n\t\t\u0026mut self.data\n\t}\n}\n\nimpl\u003cL: LockableGetMut\u003e OwnedLockCollection\u003cL\u003e {\n\t/// Gets a mutable reference to the data behind this `OwnedLockCollection`.\n\t///\n\t/// Since this call borrows the `OwnedLockCollection` mutably, no actual\n\t/// locking needs to take place - the mutable borrow statically guarantees\n\t/// no locks exist.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let mut mutex = OwnedLockCollection::new([Mutex::new(0), Mutex::new(0)]);\n\t/// assert_eq!(mutex.get_mut(), [\u0026mut 0, \u0026mut 0]);\n\t/// ```\n\tpub fn get_mut(\u0026mut self) -\u003e L::Inner\u003c'_\u003e {\n\t\tLockableGetMut::get_mut(self)\n\t}\n}\n\nimpl\u003cL: LockableIntoInner\u003e OwnedLockCollection\u003cL\u003e {\n\t/// Consumes this `OwnedLockCollection`, returning the underlying data.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let mutex = OwnedLockCollection::new([Mutex::new(0), Mutex::new(0)]);\n\t/// assert_eq!(mutex.into_inner(), [0, 0]);\n\t/// ```\n\t#[must_use]\n\tpub fn into_inner(self) -\u003e L::Inner {\n\t\tLockableIntoInner::into_inner(self)\n\t}\n}\n\n#[cfg(test)]\nmod tests {\n\tuse super::*;\n\tuse crate::{Mutex, RwLock, ThreadKey};\n\n\t#[test]\n\tfn get_mut_applies_changes() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mut collection = OwnedLockCollection::new([Mutex::new(\"foo\"), Mutex::new(\"bar\")]);\n\t\tassert_eq!(*collection.get_mut()[0], \"foo\");\n\t\tassert_eq!(*collection.get_mut()[1], \"bar\");\n\t\t*collection.get_mut()[0] = \"baz\";\n\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], \"baz\");\n\t\tassert_eq!(*guard[1], \"bar\");\n\t}\n\n\t#[test]\n\tfn into_inner_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::from([Mutex::new(\"foo\")]);\n\t\tlet mut guard = collection.lock(key);\n\t\t*guard[0] = \"bar\";\n\t\tdrop(guard);\n\n\t\tlet array = collection.into_inner();\n\t\tassert_eq!(array.len(), 1);\n\t\tassert_eq!(array[0], \"bar\");\n\t}\n\n\t#[test]\n\tfn from_into_iter_is_correct() {\n\t\tlet array = [Mutex::new(0), Mutex::new(1), Mutex::new(2), Mutex::new(3)];\n\t\tlet mut collection: OwnedLockCollection\u003cVec\u003cMutex\u003cusize\u003e\u003e\u003e = array.into_iter().collect();\n\t\tassert_eq!(collection.get_mut().len(), 4);\n\t\tfor (i, lock) in collection.into_iter().enumerate() {\n\t\t\tassert_eq!(lock.into_inner(), i);\n\t\t}\n\t}\n\n\t#[test]\n\tfn from_iter_is_correct() {\n\t\tlet array = [Mutex::new(0), Mutex::new(1), Mutex::new(2), Mutex::new(3)];\n\t\tlet mut collection: OwnedLockCollection\u003cVec\u003cMutex\u003cusize\u003e\u003e\u003e = array.into_iter().collect();\n\t\tlet collection: \u0026mut Vec\u003c_\u003e = collection.as_mut();\n\t\tassert_eq!(collection.len(), 4);\n\t\tfor (i, lock) in collection.iter_mut().enumerate() {\n\t\t\tassert_eq!(*lock.get_mut(), i);\n\t\t}\n\t}\n\n\t#[test]\n\tfn scoped_read_works() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new([RwLock::new(24), RwLock::new(42)]);\n\t\tlet sum = collection.scoped_read(\u0026mut key, |guard| guard[0] + guard[1]);\n\t\tassert_eq!(sum, 24 + 42);\n\t}\n\n\t#[test]\n\tfn scoped_lock_works() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new([RwLock::new(24), RwLock::new(42)]);\n\t\tcollection.scoped_lock(\u0026mut key, |guard| *guard[0] += *guard[1]);\n\n\t\tlet sum = collection.scoped_lock(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 24 + 42);\n\t\t\tassert_eq!(*guard[1], 42);\n\t\t\t*guard[0] + *guard[1]\n\t\t});\n\n\t\tassert_eq!(sum, 24 + 42 + 42);\n\t}\n\n\t#[test]\n\tfn scoped_try_lock_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new([Mutex::new(1), Mutex::new(2)]);\n\t\tlet guard = collection.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = collection.scoped_try_lock(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn scoped_try_read_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new([RwLock::new(1), RwLock::new(2)]);\n\t\tlet guard = collection.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = collection.scoped_try_read(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn try_lock_works_on_unlocked() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new((Mutex::new(0), Mutex::new(1)));\n\t\tlet guard = collection.try_lock(key).unwrap();\n\t\tassert_eq!(*guard.0, 0);\n\t\tassert_eq!(*guard.1, 1);\n\t}\n\n\t#[test]\n\tfn try_lock_fails_on_locked() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new((Mutex::new(0), Mutex::new(1)));\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\t#[allow(unused)]\n\t\t\t\tlet guard = collection.lock(key);\n\t\t\t\tstd::mem::forget(guard);\n\t\t\t});\n\t\t});\n\n\t\tassert!(collection.try_lock(key).is_err());\n\t}\n\n\t#[test]\n\tfn try_read_succeeds_for_unlocked_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = OwnedLockCollection::new(mutexes);\n\t\tlet guard = collection.try_read(key).unwrap();\n\t\tassert_eq!(*guard[0], 24);\n\t\tassert_eq!(*guard[1], 42);\n\t}\n\n\t#[test]\n\tfn try_read_fails_on_locked() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new((RwLock::new(0), RwLock::new(1)));\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\t#[allow(unused)]\n\t\t\t\tlet guard = collection.lock(key);\n\t\t\t\tstd::mem::forget(guard);\n\t\t\t});\n\t\t});\n\n\t\tassert!(collection.try_read(key).is_err());\n\t}\n\n\t#[test]\n\tfn can_read_twice_on_different_threads() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = OwnedLockCollection::new(mutexes);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.read(key);\n\t\t\t\tassert_eq!(*guard[0], 24);\n\t\t\t\tassert_eq!(*guard[1], 42);\n\t\t\t\tstd::mem::forget(guard);\n\t\t\t});\n\t\t});\n\n\t\tlet guard = collection.try_read(key).unwrap();\n\t\tassert_eq!(*guard[0], 24);\n\t\tassert_eq!(*guard[1], 42);\n\t}\n\n\t#[test]\n\tfn unlock_collection_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new((Mutex::new(\"foo\"), Mutex::new(\"bar\")));\n\t\tlet guard = collection.lock(key);\n\n\t\tlet key = OwnedLockCollection::\u003c(Mutex\u003c_\u003e, Mutex\u003c_\u003e)\u003e::unlock(guard);\n\t\tassert!(collection.try_lock(key).is_ok())\n\t}\n\n\t#[test]\n\tfn read_unlock_collection_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new((RwLock::new(\"foo\"), RwLock::new(\"bar\")));\n\t\tlet guard = collection.read(key);\n\n\t\tlet key = OwnedLockCollection::\u003c(\u0026RwLock\u003c_\u003e, \u0026RwLock\u003c_\u003e)\u003e::unlock_read(guard);\n\t\tassert!(collection.try_lock(key).is_ok())\n\t}\n\n\t#[test]\n\tfn default_works() {\n\t\ttype MyCollection = OwnedLockCollection\u003c(Mutex\u003ci32\u003e, Mutex\u003cOption\u003ci32\u003e\u003e, Mutex\u003cString\u003e)\u003e;\n\t\tlet collection = MyCollection::default();\n\t\tlet inner = collection.into_inner();\n\t\tassert_eq!(inner.0, 0);\n\t\tassert_eq!(inner.1, None);\n\t\tassert_eq!(inner.2, String::new());\n\t}\n\n\t#[test]\n\tfn can_be_extended() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(1);\n\t\tlet mut collection = OwnedLockCollection::new(vec![mutex1, mutex2]);\n\n\t\tcollection.extend([Mutex::new(2)]);\n\n\t\tassert_eq!(collection.data.len(), 3);\n\t}\n\n\t#[test]\n\tfn works_in_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tOwnedLockCollection::new(OwnedLockCollection::new([RwLock::new(0), RwLock::new(1)]));\n\n\t\tlet mut guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], 0);\n\t\tassert_eq!(*guard[1], 1);\n\t\t*guard[1] = 2;\n\n\t\tlet key = OwnedLockCollection::\u003cOwnedLockCollection\u003c[RwLock\u003c_\u003e; 2]\u003e\u003e::unlock(guard);\n\t\tlet guard = collection.read(key);\n\t\tassert_eq!(*guard[0], 0);\n\t\tassert_eq!(*guard[1], 2);\n\t}\n\n\t#[test]\n\tfn as_mut_works() {\n\t\tlet mut mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet mut collection = OwnedLockCollection::new(\u0026mut mutexes);\n\n\t\tcollection.as_mut()[0] = Mutex::new(42);\n\n\t\tassert_eq!(*collection.as_mut()[0].get_mut(), 42);\n\t}\n\n\t#[test]\n\tfn child_mut_works() {\n\t\tlet mut mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet mut collection = OwnedLockCollection::new(\u0026mut mutexes);\n\n\t\tcollection.child_mut()[0] = Mutex::new(42);\n\n\t\tassert_eq!(*collection.child_mut()[0].get_mut(), 42);\n\t}\n\n\t#[test]\n\tfn into_child_works() {\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet mut collection = OwnedLockCollection::new(mutexes);\n\n\t\tcollection.child_mut()[0] = Mutex::new(42);\n\n\t\tassert_eq!(\n\t\t\t*collection\n\t\t\t\t.into_child()\n\t\t\t\t.as_mut()\n\t\t\t\t.get_mut(0)\n\t\t\t\t.unwrap()\n\t\t\t\t.get_mut(),\n\t\t\t42\n\t\t);\n\t}\n}\n","traces":[{"line":19,"address":[1579307,1579456,1579072,1579947,1579563,1579200,1580075,1579584,1579712,1579819,1579840,1579691,1579968,1579328,1579179,1579435],"length":1,"stats":{"Line":8}},{"line":20,"address":[],"length":0,"stats":{"Line":16}},{"line":23,"address":[],"length":0,"stats":{"Line":5}},{"line":24,"address":[],"length":0,"stats":{"Line":4}},{"line":25,"address":[],"length":0,"stats":{"Line":9}},{"line":28,"address":[1581061,1581088,1581333,1580816],"length":1,"stats":{"Line":1}},{"line":29,"address":[],"length":0,"stats":{"Line":1}},{"line":30,"address":[],"length":0,"stats":{"Line":3}},{"line":31,"address":[],"length":0,"stats":{"Line":1}},{"line":35,"address":[],"length":0,"stats":{"Line":4}},{"line":36,"address":[],"length":0,"stats":{"Line":8}},{"line":39,"address":[],"length":0,"stats":{"Line":2}},{"line":40,"address":[1581894,1582038],"length":1,"stats":{"Line":2}},{"line":41,"address":[1581904,1582048,1582103,1581959],"length":1,"stats":{"Line":4}},{"line":44,"address":[],"length":0,"stats":{"Line":1}},{"line":45,"address":[],"length":0,"stats":{"Line":1}},{"line":46,"address":[1582312,1582366,1582188],"length":1,"stats":{"Line":3}},{"line":47,"address":[1582392],"length":1,"stats":{"Line":1}},{"line":69,"address":[],"length":0,"stats":{"Line":1}},{"line":70,"address":[],"length":0,"stats":{"Line":1}},{"line":73,"address":[],"length":0,"stats":{"Line":1}},{"line":74,"address":[],"length":0,"stats":{"Line":1}},{"line":84,"address":[1582528,1582544],"length":1,"stats":{"Line":2}},{"line":85,"address":[1582533,1582561],"length":1,"stats":{"Line":2}},{"line":92,"address":[],"length":0,"stats":{"Line":2}},{"line":93,"address":[],"length":0,"stats":{"Line":2}},{"line":108,"address":[],"length":0,"stats":{"Line":1}},{"line":109,"address":[1582721],"length":1,"stats":{"Line":1}},{"line":112,"address":[1582736],"length":1,"stats":{"Line":1}},{"line":113,"address":[1582753],"length":1,"stats":{"Line":1}},{"line":126,"address":[],"length":0,"stats":{"Line":1}},{"line":127,"address":[1582780],"length":1,"stats":{"Line":1}},{"line":134,"address":[1582832],"length":1,"stats":{"Line":1}},{"line":135,"address":[],"length":0,"stats":{"Line":1}},{"line":136,"address":[],"length":0,"stats":{"Line":1}},{"line":141,"address":[],"length":0,"stats":{"Line":1}},{"line":142,"address":[],"length":0,"stats":{"Line":1}},{"line":151,"address":[],"length":0,"stats":{"Line":2}},{"line":152,"address":[],"length":0,"stats":{"Line":2}},{"line":157,"address":[],"length":0,"stats":{"Line":1}},{"line":158,"address":[],"length":0,"stats":{"Line":1}},{"line":163,"address":[],"length":0,"stats":{"Line":1}},{"line":164,"address":[],"length":0,"stats":{"Line":1}},{"line":185,"address":[1583424,1583392,1583312,1583536,1583072,1583216,1583264,1583104,1583280,1583456,1583488,1583136,1583184,1583360],"length":1,"stats":{"Line":14}},{"line":189,"address":[],"length":0,"stats":{"Line":2}},{"line":190,"address":[],"length":0,"stats":{"Line":2}},{"line":193,"address":[1583632],"length":1,"stats":{"Line":1}},{"line":198,"address":[],"length":0,"stats":{"Line":1}},{"line":221,"address":[1583792,1584192,1584336,1584452,1584164,1583888,1584016,1584586,1583920,1584608,1584048,1584480,1583760,1583664,1584724,1584308],"length":1,"stats":{"Line":8}},{"line":225,"address":[1583934,1584368,1584224,1584494,1584080,1583806,1583678,1584640],"length":1,"stats":{"Line":8}},{"line":228,"address":[1584685,1583846,1584125,1584534,1583718,1584269,1583974,1584413],"length":1,"stats":{"Line":8}},{"line":264,"address":[1585104,1585071,1584752,1585247,1584895,1584928],"length":1,"stats":{"Line":3}},{"line":266,"address":[1585118,1585161,1584985,1584809,1584942,1584766],"length":1,"stats":{"Line":6}},{"line":267,"address":[],"length":0,"stats":{"Line":1}},{"line":271,"address":[1585225,1584873,1585049,1584831,1585007,1585183],"length":1,"stats":{"Line":6}},{"line":274,"address":[],"length":0,"stats":{"Line":3}},{"line":296,"address":[1585280,1585440,1585376,1585346],"length":1,"stats":{"Line":2}},{"line":297,"address":[],"length":0,"stats":{"Line":2}},{"line":298,"address":[],"length":0,"stats":{"Line":0}},{"line":303,"address":[],"length":0,"stats":{"Line":1}},{"line":304,"address":[1585485],"length":1,"stats":{"Line":1}},{"line":307,"address":[1585504],"length":1,"stats":{"Line":1}},{"line":312,"address":[],"length":0,"stats":{"Line":1}},{"line":335,"address":[],"length":0,"stats":{"Line":4}},{"line":338,"address":[],"length":0,"stats":{"Line":4}},{"line":342,"address":[],"length":0,"stats":{"Line":4}},{"line":379,"address":[],"length":0,"stats":{"Line":2}},{"line":382,"address":[],"length":0,"stats":{"Line":4}},{"line":383,"address":[],"length":0,"stats":{"Line":1}},{"line":387,"address":[],"length":0,"stats":{"Line":1}},{"line":390,"address":[],"length":0,"stats":{"Line":1}},{"line":410,"address":[],"length":0,"stats":{"Line":1}},{"line":411,"address":[],"length":0,"stats":{"Line":1}},{"line":412,"address":[],"length":0,"stats":{"Line":0}},{"line":434,"address":[],"length":0,"stats":{"Line":1}},{"line":435,"address":[],"length":0,"stats":{"Line":1}},{"line":455,"address":[],"length":0,"stats":{"Line":2}},{"line":456,"address":[],"length":0,"stats":{"Line":0}},{"line":476,"address":[],"length":0,"stats":{"Line":2}},{"line":477,"address":[],"length":0,"stats":{"Line":2}},{"line":494,"address":[],"length":0,"stats":{"Line":2}},{"line":495,"address":[],"length":0,"stats":{"Line":2}}],"covered":79,"coverable":82},{"path":["/","home","botahamec","Projects","happylock","src","collection","ref.rs"],"content":"use std::fmt::Debug;\n\nuse crate::lockable::{Lockable, OwnedLockable, RawLock, Sharable};\nuse crate::{Keyable, ThreadKey};\n\nuse super::utils::{\n\tget_locks, ordered_contains_duplicates, scoped_read, scoped_try_read, scoped_try_write,\n\tscoped_write,\n};\nuse super::{utils, LockGuard, RefLockCollection};\n\nimpl\u003c'a, L\u003e IntoIterator for \u0026'a RefLockCollection\u003c'a, L\u003e\nwhere\n\t\u0026'a L: IntoIterator,\n{\n\ttype Item = \u003c\u0026'a L as IntoIterator\u003e::Item;\n\ttype IntoIter = \u003c\u0026'a L as IntoIterator\u003e::IntoIter;\n\n\tfn into_iter(self) -\u003e Self::IntoIter {\n\t\tself.data.into_iter()\n\t}\n}\n\nunsafe impl\u003cL: Lockable\u003e RawLock for RefLockCollection\u003c'_, L\u003e {\n\t#[mutants::skip] // this should never run\n\t#[cfg(not(tarpaulin_include))]\n\tfn poison(\u0026self) {\n\t\tfor lock in \u0026self.locks {\n\t\t\tlock.poison();\n\t\t}\n\t}\n\n\tunsafe fn raw_write(\u0026self) {\n\t\tutils::ordered_write(\u0026self.locks)\n\t}\n\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool {\n\t\tutils::ordered_try_write(\u0026self.locks)\n\t}\n\n\tunsafe fn raw_unlock_write(\u0026self) {\n\t\tfor lock in \u0026self.locks {\n\t\t\tlock.raw_unlock_write();\n\t\t}\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tutils::ordered_read(\u0026self.locks)\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tutils::ordered_try_read(\u0026self.locks)\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tfor lock in \u0026self.locks {\n\t\t\tlock.raw_unlock_read();\n\t\t}\n\t}\n}\n\nunsafe impl\u003cL: Lockable\u003e Lockable for RefLockCollection\u003c'_, L\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= L::Guard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= L::DataMut\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tptrs.push(self)\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tself.data.guard()\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.data.data_mut()\n\t}\n}\n\nunsafe impl\u003cL: Sharable\u003e Sharable for RefLockCollection\u003c'_, L\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= L::ReadGuard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= L::DataRef\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tself.data.read_guard()\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tself.data.data_ref()\n\t}\n}\n\nimpl\u003cT: ?Sized, L: AsRef\u003cT\u003e\u003e AsRef\u003cT\u003e for RefLockCollection\u003c'_, L\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself.data.as_ref()\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cL: Debug\u003e Debug for RefLockCollection\u003c'_, L\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tf.debug_struct(stringify!(RefLockCollection))\n\t\t\t.field(\"data\", self.data)\n\t\t\t.finish_non_exhaustive()\n\t}\n}\n\n// safety: the RawLocks must be send because they come from the Send Lockable\n#[allow(clippy::non_send_fields_in_send_ty)]\nunsafe impl\u003cL: Send\u003e Send for RefLockCollection\u003c'_, L\u003e {}\nunsafe impl\u003cL: Sync\u003e Sync for RefLockCollection\u003c'_, L\u003e {}\n\nimpl\u003c'a, L: OwnedLockable + Default\u003e From\u003c\u0026'a L\u003e for RefLockCollection\u003c'a, L\u003e {\n\tfn from(value: \u0026'a L) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\nimpl\u003c'a, L: OwnedLockable\u003e RefLockCollection\u003c'a, L\u003e {\n\t/// Creates a new collection of owned locks.\n\t///\n\t/// Because the locks are owned, there's no need to do any checks for\n\t/// duplicate values.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t/// ```\n\t#[must_use]\n\tpub fn new(data: \u0026'a L) -\u003e Self {\n\t\tRefLockCollection {\n\t\t\tlocks: get_locks(data),\n\t\t\tdata,\n\t\t}\n\t}\n}\n\nimpl\u003cL\u003e RefLockCollection\u003c'_, L\u003e {\n\t/// Gets an immutable reference to the underlying data\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let data1 = Mutex::new(42);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // data1 and data2 refer to distinct mutexes, so this won't panic\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = RefLockCollection::try_new(\u0026data).unwrap();\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let guard = lock.child().0.lock(key);\n\t/// assert_eq!(*guard, 42);\n\t/// ```\n\t#[must_use]\n\tpub const fn child(\u0026self) -\u003e \u0026L {\n\t\tself.data\n\t}\n}\n\nimpl\u003c'a, L: Lockable\u003e RefLockCollection\u003c'a, L\u003e {\n\t/// Creates a new collections of locks.\n\t///\n\t/// # Safety\n\t///\n\t/// This results in undefined behavior if any locks are presented twice\n\t/// within this collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let data1 = Mutex::new(0);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // safety: data1 and data2 refer to distinct mutexes\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = unsafe { RefLockCollection::new_unchecked(\u0026data) };\n\t/// ```\n\t#[must_use]\n\tpub unsafe fn new_unchecked(data: \u0026'a L) -\u003e Self {\n\t\tSelf {\n\t\t\tdata,\n\t\t\tlocks: get_locks(data),\n\t\t}\n\t}\n\n\t/// Creates a new collection of locks.\n\t///\n\t/// This returns `None` if any locks are found twice in the given\n\t/// collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let data1 = Mutex::new(0);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // data1 and data2 refer to distinct mutexes, so this won't panic\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = RefLockCollection::try_new(\u0026data).unwrap();\n\t/// ```\n\t#[must_use]\n\tpub fn try_new(data: \u0026'a L) -\u003e Option\u003cSelf\u003e {\n\t\tlet locks = get_locks(data);\n\t\tif ordered_contains_duplicates(\u0026locks) {\n\t\t\treturn None;\n\t\t}\n\n\t\tSome(Self { data, locks })\n\t}\n\n\tpub fn scoped_lock\u003c's, R\u003e(\u0026's self, key: impl Keyable, f: impl Fn(L::DataMut\u003c's\u003e) -\u003e R) -\u003e R {\n\t\tscoped_write(self, key, f)\n\t}\n\n\tpub fn scoped_try_lock\u003c's, Key: Keyable, R\u003e(\n\t\t\u0026's self,\n\t\tkey: Key,\n\t\tf: impl Fn(L::DataMut\u003c's\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tscoped_try_write(self, key, f)\n\t}\n\n\t/// Locks the collection\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data. When the guard is dropped, the locks in the collection are also\n\t/// dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// ```\n\t#[must_use]\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::Guard\u003c'_\u003e\u003e {\n\t\tlet guard = unsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_write();\n\n\t\t\t// safety: we've locked all of this already\n\t\t\tself.data.guard()\n\t\t};\n\n\t\tLockGuard { guard, key }\n\t}\n\n\t/// Attempts to lock the without blocking.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// locks when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If any of the locks in the collection are already locked, then an error\n\t/// is returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// match lock.try_lock(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::Guard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tlet guard = unsafe {\n\t\t\tif !self.raw_try_write() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we've acquired the locks\n\t\t\tself.data.guard()\n\t\t};\n\n\t\tOk(LockGuard { guard, key })\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// let key = RefLockCollection::\u003c(Mutex\u003ci32\u003e, Mutex\u003c\u0026str\u003e)\u003e::unlock(guard);\n\t/// ```\n\t#[allow(clippy::missing_const_for_fn)]\n\tpub fn unlock(guard: LockGuard\u003cL::Guard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: Sharable\u003e RefLockCollection\u003c'_, L\u003e {\n\tpub fn scoped_read\u003c'a, R\u003e(\u0026'a self, key: impl Keyable, f: impl Fn(L::DataRef\u003c'a\u003e) -\u003e R) -\u003e R {\n\t\tscoped_read(self, key, f)\n\t}\n\n\tpub fn scoped_try_read\u003c'a, Key: Keyable, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(L::DataRef\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tscoped_try_read(self, key, f)\n\t}\n\n\t/// Locks the collection, so that other threads can still read from it\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data immutably. When the guard is dropped, the locks in the collection\n\t/// are also dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// assert_eq!(*guard.0, 0);\n\t/// assert_eq!(*guard.1, \"\");\n\t/// ```\n\t#[must_use]\n\tpub fn read(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_read();\n\n\t\t\tLockGuard {\n\t\t\t\t// safety: we've already acquired the lock\n\t\t\t\tguard: self.data.read_guard(),\n\t\t\t\tkey,\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Attempts to lock the without blocking, in such a way that other threads\n\t/// can still read from the collection.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// shared access when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If any of the locks in the collection are already locked, then an error\n\t/// is returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(5), RwLock::new(\"6\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// match lock.try_read(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// assert_eq!(*guard.0, 5);\n\t/// assert_eq!(*guard.1, \"6\");\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_read(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tlet guard = unsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_read() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we've acquired the locks\n\t\t\tself.data.read_guard()\n\t\t};\n\n\t\tOk(LockGuard { guard, key })\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// let key = RefLockCollection::\u003c(RwLock\u003ci32\u003e, RwLock\u003c\u0026str\u003e)\u003e::unlock_read(guard);\n\t/// ```\n\t#[allow(clippy::missing_const_for_fn)]\n\tpub fn unlock_read(guard: LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003c'a, L: 'a\u003e RefLockCollection\u003c'a, L\u003e\nwhere\n\t\u0026'a L: IntoIterator,\n{\n\t/// Returns an iterator over references to each value in the collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = [Mutex::new(26), Mutex::new(1)];\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// let mut iter = lock.iter();\n\t/// let mutex = iter.next().unwrap();\n\t/// let guard = mutex.lock(key);\n\t///\n\t/// assert_eq!(*guard, 26);\n\t/// ```\n\t#[must_use]\n\tpub fn iter(\u0026'a self) -\u003e \u003c\u0026'a L as IntoIterator\u003e::IntoIter {\n\t\tself.into_iter()\n\t}\n}\n\n#[cfg(test)]\nmod tests {\n\tuse super::*;\n\tuse crate::{Mutex, RwLock, ThreadKey};\n\n\t#[test]\n\tfn non_duplicates_allowed() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(1);\n\t\tassert!(RefLockCollection::try_new(\u0026[\u0026mutex1, \u0026mutex2]).is_some())\n\t}\n\n\t#[test]\n\tfn duplicates_not_allowed() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tassert!(RefLockCollection::try_new(\u0026[\u0026mutex1, \u0026mutex1]).is_none())\n\t}\n\n\t#[test]\n\tfn from() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [Mutex::new(\"foo\"), Mutex::new(\"bar\"), Mutex::new(\"baz\")];\n\t\tlet collection = RefLockCollection::from(\u0026mutexes);\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], \"foo\");\n\t\tassert_eq!(*guard[1], \"bar\");\n\t\tassert_eq!(*guard[2], \"baz\");\n\t}\n\n\t#[test]\n\tfn scoped_lock_changes_collection() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [Mutex::new(24), Mutex::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\t\tlet sum = collection.scoped_lock(\u0026mut key, |guard| {\n\t\t\t*guard[0] = 128;\n\t\t\t*guard[0] + *guard[1]\n\t\t});\n\n\t\tassert_eq!(sum, 128 + 42);\n\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], 128);\n\t\tassert_eq!(*guard[1], 42);\n\t}\n\n\t#[test]\n\tfn scoped_read_sees_changes() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\t\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\t\t*guard[0] = 128;\n\t\t});\n\n\t\tlet sum = collection.scoped_read(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 128);\n\t\t\tassert_eq!(*guard[1], 42);\n\t\t\t*guard[0] + *guard[1]\n\t\t});\n\n\t\tassert_eq!(sum, 128 + 42);\n\t}\n\n\t#[test]\n\tfn scoped_try_lock_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = [Mutex::new(1), Mutex::new(2)];\n\t\tlet collection = RefLockCollection::new(\u0026locks);\n\t\tlet guard = collection.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = collection.scoped_try_lock(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn scoped_try_read_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = [RwLock::new(1), RwLock::new(2)];\n\t\tlet collection = RefLockCollection::new(\u0026locks);\n\t\tlet guard = collection.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = collection.scoped_try_read(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn try_lock_succeeds_for_unlocked_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [Mutex::new(24), Mutex::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\t\tlet guard = collection.try_lock(key).unwrap();\n\t\tassert_eq!(*guard[0], 24);\n\t\tassert_eq!(*guard[1], 42);\n\t}\n\n\t#[test]\n\tfn try_lock_fails_for_locked_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [Mutex::new(24), Mutex::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = mutexes[1].lock(key);\n\t\t\t\tassert_eq!(*guard, 42);\n\t\t\t\tstd::mem::forget(guard);\n\t\t\t});\n\t\t});\n\n\t\tlet guard = collection.try_lock(key);\n\t\tassert!(guard.is_err());\n\t}\n\n\t#[test]\n\tfn try_read_succeeds_for_unlocked_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\t\tlet guard = collection.try_read(key).unwrap();\n\t\tassert_eq!(*guard[0], 24);\n\t\tassert_eq!(*guard[1], 42);\n\t}\n\n\t#[test]\n\tfn try_read_fails_for_locked_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = mutexes[1].write(key);\n\t\t\t\tassert_eq!(*guard, 42);\n\t\t\t\tstd::mem::forget(guard);\n\t\t\t});\n\t\t});\n\n\t\tlet guard = collection.try_read(key);\n\t\tassert!(guard.is_err());\n\t}\n\n\t#[test]\n\tfn can_read_twice_on_different_threads() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.read(key);\n\t\t\t\tassert_eq!(*guard[0], 24);\n\t\t\t\tassert_eq!(*guard[1], 42);\n\t\t\t\tstd::mem::forget(guard);\n\t\t\t});\n\t\t});\n\n\t\tlet guard = collection.try_read(key).unwrap();\n\t\tassert_eq!(*guard[0], 24);\n\t\tassert_eq!(*guard[1], 42);\n\t}\n\n\t#[test]\n\tfn into_ref_iterator() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1), Mutex::new(2)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\t\tfor (i, mutex) in (\u0026collection).into_iter().enumerate() {\n\t\t\tmutex.scoped_lock(\u0026mut key, |val| assert_eq!(*val, i))\n\t\t}\n\t}\n\n\t#[test]\n\tfn ref_iterator() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1), Mutex::new(2)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\t\tfor (i, mutex) in collection.iter().enumerate() {\n\t\t\tmutex.scoped_lock(\u0026mut key, |val| assert_eq!(*val, i))\n\t\t}\n\t}\n\n\t#[test]\n\tfn works_in_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex1 = RwLock::new(0);\n\t\tlet mutex2 = RwLock::new(1);\n\t\tlet collection0 = [\u0026mutex1, \u0026mutex2];\n\t\tlet collection1 = RefLockCollection::try_new(\u0026collection0).unwrap();\n\t\tlet collection = RefLockCollection::try_new(\u0026collection1).unwrap();\n\n\t\tlet mut guard = collection.lock(key);\n\t\tassert!(mutex1.is_locked());\n\t\tassert!(mutex2.is_locked());\n\t\tassert_eq!(*guard[0], 0);\n\t\tassert_eq!(*guard[1], 1);\n\t\t*guard[1] = 2;\n\t\tdrop(guard);\n\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet guard = collection.read(key);\n\t\tassert!(mutex1.is_locked());\n\t\tassert!(mutex2.is_locked());\n\t\tassert_eq!(*guard[0], 0);\n\t\tassert_eq!(*guard[1], 2);\n\t}\n\n\t#[test]\n\tfn unlock_collection_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = (Mutex::new(\"foo\"), Mutex::new(\"bar\"));\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\t\tlet guard = collection.lock(key);\n\n\t\tlet key = RefLockCollection::\u003c(Mutex\u003c_\u003e, Mutex\u003c_\u003e)\u003e::unlock(guard);\n\t\tassert!(collection.try_lock(key).is_ok())\n\t}\n\n\t#[test]\n\tfn read_unlock_collection_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = (RwLock::new(\"foo\"), RwLock::new(\"bar\"));\n\t\tlet collection = RefLockCollection::new(\u0026locks);\n\t\tlet guard = collection.read(key);\n\n\t\tlet key = RefLockCollection::\u003c(\u0026RwLock\u003c_\u003e, \u0026RwLock\u003c_\u003e)\u003e::unlock_read(guard);\n\t\tassert!(collection.try_lock(key).is_ok())\n\t}\n\n\t#[test]\n\tfn as_ref_works() {\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\n\t\tassert!(std::ptr::addr_eq(\u0026mutexes, collection.as_ref()))\n\t}\n\n\t#[test]\n\tfn child() {\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\n\t\tassert!(std::ptr::addr_eq(\u0026mutexes, collection.child()))\n\t}\n}\n","traces":[{"line":19,"address":[1586672],"length":1,"stats":{"Line":1}},{"line":20,"address":[],"length":0,"stats":{"Line":1}},{"line":33,"address":[],"length":0,"stats":{"Line":6}},{"line":34,"address":[],"length":0,"stats":{"Line":6}},{"line":37,"address":[],"length":0,"stats":{"Line":3}},{"line":38,"address":[],"length":0,"stats":{"Line":3}},{"line":41,"address":[1587104,1587008,1587200],"length":1,"stats":{"Line":2}},{"line":42,"address":[],"length":0,"stats":{"Line":4}},{"line":43,"address":[],"length":0,"stats":{"Line":2}},{"line":47,"address":[],"length":0,"stats":{"Line":4}},{"line":48,"address":[1587301,1587333,1587397,1587365],"length":1,"stats":{"Line":4}},{"line":51,"address":[],"length":0,"stats":{"Line":1}},{"line":52,"address":[],"length":0,"stats":{"Line":1}},{"line":55,"address":[],"length":0,"stats":{"Line":1}},{"line":56,"address":[],"length":0,"stats":{"Line":2}},{"line":57,"address":[],"length":0,"stats":{"Line":1}},{"line":73,"address":[],"length":0,"stats":{"Line":1}},{"line":74,"address":[],"length":0,"stats":{"Line":1}},{"line":77,"address":[],"length":0,"stats":{"Line":1}},{"line":78,"address":[],"length":0,"stats":{"Line":1}},{"line":81,"address":[],"length":0,"stats":{"Line":2}},{"line":82,"address":[],"length":0,"stats":{"Line":2}},{"line":97,"address":[],"length":0,"stats":{"Line":1}},{"line":98,"address":[],"length":0,"stats":{"Line":1}},{"line":101,"address":[],"length":0,"stats":{"Line":1}},{"line":102,"address":[],"length":0,"stats":{"Line":1}},{"line":107,"address":[],"length":0,"stats":{"Line":1}},{"line":108,"address":[],"length":0,"stats":{"Line":1}},{"line":128,"address":[],"length":0,"stats":{"Line":1}},{"line":129,"address":[],"length":0,"stats":{"Line":1}},{"line":149,"address":[],"length":0,"stats":{"Line":6}},{"line":151,"address":[],"length":0,"stats":{"Line":6}},{"line":178,"address":[],"length":0,"stats":{"Line":1}},{"line":179,"address":[],"length":0,"stats":{"Line":1}},{"line":205,"address":[],"length":0,"stats":{"Line":0}},{"line":208,"address":[],"length":0,"stats":{"Line":0}},{"line":231,"address":[],"length":0,"stats":{"Line":3}},{"line":232,"address":[],"length":0,"stats":{"Line":3}},{"line":233,"address":[],"length":0,"stats":{"Line":6}},{"line":234,"address":[],"length":0,"stats":{"Line":1}},{"line":237,"address":[],"length":0,"stats":{"Line":3}},{"line":240,"address":[],"length":0,"stats":{"Line":2}},{"line":241,"address":[],"length":0,"stats":{"Line":2}},{"line":244,"address":[1589696],"length":1,"stats":{"Line":1}},{"line":249,"address":[],"length":0,"stats":{"Line":1}},{"line":273,"address":[],"length":0,"stats":{"Line":5}},{"line":276,"address":[],"length":0,"stats":{"Line":5}},{"line":279,"address":[],"length":0,"stats":{"Line":5}},{"line":315,"address":[],"length":0,"stats":{"Line":3}},{"line":317,"address":[],"length":0,"stats":{"Line":6}},{"line":318,"address":[],"length":0,"stats":{"Line":1}},{"line":322,"address":[],"length":0,"stats":{"Line":5}},{"line":325,"address":[],"length":0,"stats":{"Line":3}},{"line":347,"address":[],"length":0,"stats":{"Line":1}},{"line":348,"address":[1591006],"length":1,"stats":{"Line":1}},{"line":349,"address":[],"length":0,"stats":{"Line":0}},{"line":354,"address":[],"length":0,"stats":{"Line":1}},{"line":355,"address":[],"length":0,"stats":{"Line":1}},{"line":358,"address":[1591120],"length":1,"stats":{"Line":1}},{"line":363,"address":[1591129],"length":1,"stats":{"Line":1}},{"line":387,"address":[],"length":0,"stats":{"Line":3}},{"line":390,"address":[],"length":0,"stats":{"Line":3}},{"line":394,"address":[],"length":0,"stats":{"Line":3}},{"line":431,"address":[1591552,1591722],"length":1,"stats":{"Line":1}},{"line":434,"address":[],"length":0,"stats":{"Line":2}},{"line":435,"address":[1591638],"length":1,"stats":{"Line":1}},{"line":439,"address":[],"length":0,"stats":{"Line":1}},{"line":442,"address":[1591683],"length":1,"stats":{"Line":1}},{"line":462,"address":[],"length":0,"stats":{"Line":1}},{"line":463,"address":[],"length":0,"stats":{"Line":1}},{"line":464,"address":[],"length":0,"stats":{"Line":0}},{"line":491,"address":[],"length":0,"stats":{"Line":1}},{"line":492,"address":[],"length":0,"stats":{"Line":1}}],"covered":69,"coverable":73},{"path":["/","home","botahamec","Projects","happylock","src","collection","retry.rs"],"content":"use std::cell::Cell;\nuse std::collections::HashSet;\n\nuse crate::collection::utils;\nuse crate::handle_unwind::handle_unwind;\nuse crate::lockable::{\n\tLockable, LockableGetMut, LockableIntoInner, OwnedLockable, RawLock, Sharable,\n};\nuse crate::{Keyable, ThreadKey};\n\nuse super::utils::{\n\tattempt_to_recover_reads_from_panic, attempt_to_recover_writes_from_panic, get_locks_unsorted,\n\tscoped_read, scoped_try_read, scoped_try_write, scoped_write,\n};\nuse super::{LockGuard, RetryingLockCollection};\n\n/// Checks that a collection contains no duplicate references to a lock.\nfn contains_duplicates\u003cL: Lockable\u003e(data: L) -\u003e bool {\n\tlet mut locks = Vec::new();\n\tdata.get_ptrs(\u0026mut locks);\n\t// cast to *const () so that the v-table pointers are not used for hashing\n\tlet locks = locks.into_iter().map(|l| (\u0026raw const *l).cast::\u003c()\u003e());\n\n\tlet mut locks_set = HashSet::with_capacity(locks.len());\n\tfor lock in locks {\n\t\tif !locks_set.insert(lock) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tfalse\n}\n\nunsafe impl\u003cL: Lockable\u003e RawLock for RetryingLockCollection\u003cL\u003e {\n\t#[mutants::skip] // this should never run\n\t#[cfg(not(tarpaulin_include))]\n\tfn poison(\u0026self) {\n\t\tlet locks = get_locks_unsorted(\u0026self.data);\n\t\tfor lock in locks {\n\t\t\tlock.poison();\n\t\t}\n\t}\n\n\tunsafe fn raw_write(\u0026self) {\n\t\tlet locks = get_locks_unsorted(\u0026self.data);\n\n\t\tif locks.is_empty() {\n\t\t\t// this probably prevents a panic later\n\t\t\treturn;\n\t\t}\n\n\t\t// these will be unlocked in case of a panic\n\t\tlet first_index = Cell::new(0);\n\t\tlet locked = Cell::new(0);\n\t\thandle_unwind(\n\t\t\t|| unsafe {\n\t\t\t\t'outer: loop {\n\t\t\t\t\t// This prevents us from entering a spin loop waiting for\n\t\t\t\t\t// the same lock to be unlocked\n\t\t\t\t\t// safety: we have the thread key\n\t\t\t\t\tlocks[first_index.get()].raw_write();\n\t\t\t\t\tfor (i, lock) in locks.iter().enumerate() {\n\t\t\t\t\t\tif i == first_index.get() {\n\t\t\t\t\t\t\t// we've already locked this one\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// If the lock has been killed, then this returns false\n\t\t\t\t\t\t// instead of panicking. This sounds like a problem, but if\n\t\t\t\t\t\t// it does return false, then the lock function is called\n\t\t\t\t\t\t// immediately after, causing a panic\n\t\t\t\t\t\t// safety: we have the thread key\n\t\t\t\t\t\tif lock.raw_try_write() {\n\t\t\t\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// safety: we already locked all of these\n\t\t\t\t\t\t\tattempt_to_recover_writes_from_panic(\u0026locks[0..i]);\n\t\t\t\t\t\t\tif first_index.get() \u003e= i {\n\t\t\t\t\t\t\t\t// safety: this is already locked and can't be\n\t\t\t\t\t\t\t\t// unlocked by the previous loop\n\t\t\t\t\t\t\t\tlocks[first_index.get()].raw_unlock_write();\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// nothing is locked anymore\n\t\t\t\t\t\t\tlocked.set(0);\n\n\t\t\t\t\t\t\t// call lock on this to prevent a spin loop\n\t\t\t\t\t\t\tfirst_index.set(i);\n\t\t\t\t\t\t\tcontinue 'outer;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// safety: we locked all the data\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t},\n\t\t\t|| {\n\t\t\t\tutils::attempt_to_recover_writes_from_panic(\u0026locks[0..locked.get()]);\n\t\t\t\tif first_index.get() \u003e= locked.get() {\n\t\t\t\t\tlocks[first_index.get()].raw_unlock_write();\n\t\t\t\t}\n\t\t\t},\n\t\t)\n\t}\n\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool {\n\t\tlet locks = get_locks_unsorted(\u0026self.data);\n\n\t\tif locks.is_empty() {\n\t\t\t// this is an interesting case, but it doesn't give us access to\n\t\t\t// any data, and can't possibly cause a deadlock\n\t\t\treturn true;\n\t\t}\n\n\t\t// these will be unlocked in case of a panic\n\t\tlet locked = Cell::new(0);\n\t\thandle_unwind(\n\t\t\t|| unsafe {\n\t\t\t\tfor (i, lock) in locks.iter().enumerate() {\n\t\t\t\t\t// safety: we have the thread key\n\t\t\t\t\tif lock.raw_try_write() {\n\t\t\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// safety: we already locked all of these\n\t\t\t\t\t\tattempt_to_recover_writes_from_panic(\u0026locks[0..i]);\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ttrue\n\t\t\t},\n\t\t\t|| utils::attempt_to_recover_writes_from_panic(\u0026locks[0..locked.get()]),\n\t\t)\n\t}\n\n\tunsafe fn raw_unlock_write(\u0026self) {\n\t\tlet locks = get_locks_unsorted(\u0026self.data);\n\n\t\tfor lock in locks {\n\t\t\tlock.raw_unlock_write();\n\t\t}\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tlet locks = get_locks_unsorted(\u0026self.data);\n\n\t\tif locks.is_empty() {\n\t\t\t// this probably prevents a panic later\n\t\t\treturn;\n\t\t}\n\n\t\tlet locked = Cell::new(0);\n\t\tlet first_index = Cell::new(0);\n\t\thandle_unwind(\n\t\t\t|| 'outer: loop {\n\t\t\t\t// safety: we have the thread key\n\t\t\t\tlocks[first_index.get()].raw_read();\n\t\t\t\tfor (i, lock) in locks.iter().enumerate() {\n\t\t\t\t\tif i == first_index.get() {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// safety: we have the thread key\n\t\t\t\t\tif lock.raw_try_read() {\n\t\t\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// safety: we already locked all of these\n\t\t\t\t\t\tattempt_to_recover_reads_from_panic(\u0026locks[0..i]);\n\n\t\t\t\t\t\tif first_index.get() \u003e= i {\n\t\t\t\t\t\t\t// safety: this is already locked and can't be unlocked\n\t\t\t\t\t\t\t// by the previous loop\n\t\t\t\t\t\t\tlocks[first_index.get()].raw_unlock_read();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// these are no longer locked\n\t\t\t\t\t\tlocked.set(0);\n\n\t\t\t\t\t\t// don't go into a spin loop, wait for this one to lock\n\t\t\t\t\t\tfirst_index.set(i);\n\t\t\t\t\t\tcontinue 'outer;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// safety: we locked all the data\n\t\t\t\tbreak;\n\t\t\t},\n\t\t\t|| {\n\t\t\t\tutils::attempt_to_recover_reads_from_panic(\u0026locks[0..locked.get()]);\n\t\t\t\tif first_index.get() \u003e= locked.get() {\n\t\t\t\t\tlocks[first_index.get()].raw_unlock_read();\n\t\t\t\t}\n\t\t\t},\n\t\t)\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tlet locks = get_locks_unsorted(\u0026self.data);\n\n\t\tif locks.is_empty() {\n\t\t\t// this is an interesting case, but it doesn't give us access to\n\t\t\t// any data, and can't possibly cause a deadlock\n\t\t\treturn true;\n\t\t}\n\n\t\tlet locked = Cell::new(0);\n\t\thandle_unwind(\n\t\t\t|| unsafe {\n\t\t\t\tfor (i, lock) in locks.iter().enumerate() {\n\t\t\t\t\t// safety: we have the thread key\n\t\t\t\t\tif lock.raw_try_read() {\n\t\t\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// safety: we already locked all of these\n\t\t\t\t\t\tattempt_to_recover_reads_from_panic(\u0026locks[0..i]);\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ttrue\n\t\t\t},\n\t\t\t|| utils::attempt_to_recover_reads_from_panic(\u0026locks[0..locked.get()]),\n\t\t)\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tlet locks = get_locks_unsorted(\u0026self.data);\n\n\t\tfor lock in locks {\n\t\t\tlock.raw_unlock_read();\n\t\t}\n\t}\n}\n\nunsafe impl\u003cL: Lockable\u003e Lockable for RetryingLockCollection\u003cL\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= L::Guard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= L::DataMut\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tptrs.push(self)\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tself.data.guard()\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.data.data_mut()\n\t}\n}\n\nunsafe impl\u003cL: Sharable\u003e Sharable for RetryingLockCollection\u003cL\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= L::ReadGuard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= L::DataRef\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tself.data.read_guard()\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tself.data.data_ref()\n\t}\n}\n\nunsafe impl\u003cL: OwnedLockable\u003e OwnedLockable for RetryingLockCollection\u003cL\u003e {}\n\nimpl\u003cL: LockableGetMut\u003e LockableGetMut for RetryingLockCollection\u003cL\u003e {\n\ttype Inner\u003c'a\u003e\n\t\t= L::Inner\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tself.data.get_mut()\n\t}\n}\n\nimpl\u003cL: LockableIntoInner\u003e LockableIntoInner for RetryingLockCollection\u003cL\u003e {\n\ttype Inner = L::Inner;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tself.data.into_inner()\n\t}\n}\n\nimpl\u003cL\u003e IntoIterator for RetryingLockCollection\u003cL\u003e\nwhere\n\tL: IntoIterator,\n{\n\ttype Item = \u003cL as IntoIterator\u003e::Item;\n\ttype IntoIter = \u003cL as IntoIterator\u003e::IntoIter;\n\n\tfn into_iter(self) -\u003e Self::IntoIter {\n\t\tself.data.into_iter()\n\t}\n}\n\nimpl\u003c'a, L\u003e IntoIterator for \u0026'a RetryingLockCollection\u003cL\u003e\nwhere\n\t\u0026'a L: IntoIterator,\n{\n\ttype Item = \u003c\u0026'a L as IntoIterator\u003e::Item;\n\ttype IntoIter = \u003c\u0026'a L as IntoIterator\u003e::IntoIter;\n\n\tfn into_iter(self) -\u003e Self::IntoIter {\n\t\tself.data.into_iter()\n\t}\n}\n\nimpl\u003c'a, L\u003e IntoIterator for \u0026'a mut RetryingLockCollection\u003cL\u003e\nwhere\n\t\u0026'a mut L: IntoIterator,\n{\n\ttype Item = \u003c\u0026'a mut L as IntoIterator\u003e::Item;\n\ttype IntoIter = \u003c\u0026'a mut L as IntoIterator\u003e::IntoIter;\n\n\tfn into_iter(self) -\u003e Self::IntoIter {\n\t\tself.data.into_iter()\n\t}\n}\n\nimpl\u003cL: OwnedLockable, I: FromIterator\u003cL\u003e + OwnedLockable\u003e FromIterator\u003cL\u003e\n\tfor RetryingLockCollection\u003cI\u003e\n{\n\tfn from_iter\u003cT: IntoIterator\u003cItem = L\u003e\u003e(iter: T) -\u003e Self {\n\t\tlet iter: I = iter.into_iter().collect();\n\t\tSelf::new(iter)\n\t}\n}\n\nimpl\u003cE: OwnedLockable + Extend\u003cL\u003e, L: OwnedLockable\u003e Extend\u003cL\u003e for RetryingLockCollection\u003cE\u003e {\n\tfn extend\u003cT: IntoIterator\u003cItem = L\u003e\u003e(\u0026mut self, iter: T) {\n\t\tself.data.extend(iter)\n\t}\n}\n\nimpl\u003cT: ?Sized, L: AsRef\u003cT\u003e\u003e AsRef\u003cT\u003e for RetryingLockCollection\u003cL\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself.data.as_ref()\n\t}\n}\n\nimpl\u003cT: ?Sized, L: AsMut\u003cT\u003e\u003e AsMut\u003cT\u003e for RetryingLockCollection\u003cL\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself.data.as_mut()\n\t}\n}\n\nimpl\u003cL: OwnedLockable + Default\u003e Default for RetryingLockCollection\u003cL\u003e {\n\tfn default() -\u003e Self {\n\t\tSelf::new(L::default())\n\t}\n}\n\nimpl\u003cL: OwnedLockable\u003e From\u003cL\u003e for RetryingLockCollection\u003cL\u003e {\n\tfn from(value: L) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\nimpl\u003cL: OwnedLockable\u003e RetryingLockCollection\u003cL\u003e {\n\t/// Creates a new collection of owned locks.\n\t///\n\t/// Because the locks are owned, there's no need to do any checks for\n\t/// duplicate values. The locks also don't need to be sorted by memory\n\t/// address because they aren't used anywhere else.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t/// ```\n\t#[must_use]\n\tpub const fn new(data: L) -\u003e Self {\n\t\t// safety: the data cannot cannot contain references\n\t\tunsafe { Self::new_unchecked(data) }\n\t}\n}\n\nimpl\u003c'a, L: OwnedLockable\u003e RetryingLockCollection\u003c\u0026'a L\u003e {\n\t/// Creates a new collection of owned locks.\n\t///\n\t/// Because the locks are owned, there's no need to do any checks for\n\t/// duplicate values.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new_ref(\u0026data);\n\t/// ```\n\t#[must_use]\n\tpub const fn new_ref(data: \u0026'a L) -\u003e Self {\n\t\t// safety: the data cannot cannot contain references\n\t\tunsafe { Self::new_unchecked(data) }\n\t}\n}\n\nimpl\u003cL\u003e RetryingLockCollection\u003cL\u003e {\n\t/// Creates a new collections of locks.\n\t///\n\t/// # Safety\n\t///\n\t/// This results in undefined behavior if any locks are presented twice\n\t/// within this collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let data1 = Mutex::new(0);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // safety: data1 and data2 refer to distinct mutexes\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = unsafe { RetryingLockCollection::new_unchecked(\u0026data) };\n\t/// ```\n\t#[must_use]\n\tpub const unsafe fn new_unchecked(data: L) -\u003e Self {\n\t\tSelf { data }\n\t}\n\n\t/// Gets an immutable reference to the underlying collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let data = (Mutex::new(42), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let inner = lock.child();\n\t/// let guard = inner.0.lock(key);\n\t/// assert_eq!(*guard, 42);\n\t/// ```\n\t#[must_use]\n\tpub const fn child(\u0026self) -\u003e \u0026L {\n\t\t\u0026self.data\n\t}\n\n\t/// Gets a mutable reference to the underlying collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let data = (Mutex::new(42), Mutex::new(\"\"));\n\t/// let mut lock = RetryingLockCollection::new(data);\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut inner = lock.child_mut();\n\t/// let guard = inner.0.get_mut();\n\t/// assert_eq!(*guard, 42);\n\t/// ```\n\t#[must_use]\n\tpub fn child_mut(\u0026mut self) -\u003e \u0026mut L {\n\t\t\u0026mut self.data\n\t}\n\n\t/// Gets the underlying collection, consuming this collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let data = (Mutex::new(42), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let inner = lock.into_child();\n\t/// let guard = inner.0.lock(key);\n\t/// assert_eq!(*guard, 42);\n\t/// ```\n\t#[must_use]\n\tpub fn into_child(self) -\u003e L {\n\t\tself.data\n\t}\n}\n\nimpl\u003cL: Lockable\u003e RetryingLockCollection\u003cL\u003e {\n\t/// Creates a new collection of locks.\n\t///\n\t/// This returns `None` if any locks are found twice in the given\n\t/// collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let data1 = Mutex::new(0);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // data1 and data2 refer to distinct mutexes, so this won't panic\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = RetryingLockCollection::try_new(\u0026data).unwrap();\n\t/// ```\n\t#[must_use]\n\tpub fn try_new(data: L) -\u003e Option\u003cSelf\u003e {\n\t\t// safety: the data is checked for duplicates before returning the collection\n\t\t(!contains_duplicates(\u0026data)).then_some(unsafe { Self::new_unchecked(data) })\n\t}\n\n\tpub fn scoped_lock\u003c'a, R\u003e(\u0026'a self, key: impl Keyable, f: impl Fn(L::DataMut\u003c'a\u003e) -\u003e R) -\u003e R {\n\t\tscoped_write(self, key, f)\n\t}\n\n\tpub fn scoped_try_lock\u003c'a, Key: Keyable, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(L::DataMut\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tscoped_try_write(self, key, f)\n\t}\n\n\t/// Locks the collection\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data. When the guard is dropped, the locks in the collection are also\n\t/// dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// ```\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::Guard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\t// safety: we're taking the thread key\n\t\t\tself.raw_write();\n\n\t\t\tLockGuard {\n\t\t\t\t// safety: we just locked the collection\n\t\t\t\tguard: self.guard(),\n\t\t\t\tkey,\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Attempts to lock the without blocking.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// locks when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If any of the locks in the collection are already locked, then an error\n\t/// is returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// match lock.try_lock(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::Guard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tunsafe {\n\t\t\t// safety: we're taking the thread key\n\t\t\tif self.raw_try_write() {\n\t\t\t\tOk(LockGuard {\n\t\t\t\t\t// safety: we just succeeded in locking everything\n\t\t\t\t\tguard: self.guard(),\n\t\t\t\t\tkey,\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\tErr(key)\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// let key = RetryingLockCollection::\u003c(Mutex\u003ci32\u003e, Mutex\u003c\u0026str\u003e)\u003e::unlock(guard);\n\t/// ```\n\tpub fn unlock(guard: LockGuard\u003cL::Guard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: Sharable\u003e RetryingLockCollection\u003cL\u003e {\n\tpub fn scoped_read\u003c'a, R\u003e(\u0026'a self, key: impl Keyable, f: impl Fn(L::DataRef\u003c'a\u003e) -\u003e R) -\u003e R {\n\t\tscoped_read(self, key, f)\n\t}\n\n\tpub fn scoped_try_read\u003c'a, Key: Keyable, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(L::DataRef\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tscoped_try_read(self, key, f)\n\t}\n\n\t/// Locks the collection, so that other threads can still read from it\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data immutably. When the guard is dropped, the locks in the collection\n\t/// are also dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// assert_eq!(*guard.0, 0);\n\t/// assert_eq!(*guard.1, \"\");\n\t/// ```\n\tpub fn read(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\t// safety: we're taking the thread key\n\t\t\tself.raw_read();\n\n\t\t\tLockGuard {\n\t\t\t\t// safety: we just locked the collection\n\t\t\t\tguard: self.read_guard(),\n\t\t\t\tkey,\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Attempts to lock the without blocking, in such a way that other threads\n\t/// can still read from the collection.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// shared access when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If shared access cannot be acquired at this time, then an error is\n\t/// returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(5), RwLock::new(\"6\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// match lock.try_read(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// assert_eq!(*guard.0, 5);\n\t/// assert_eq!(*guard.1, \"6\");\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_read(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tunsafe {\n\t\t\t// safety: we're taking the thread key\n\t\t\tif !self.raw_try_read() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\tOk(LockGuard {\n\t\t\t\t// safety: we just succeeded in locking everything\n\t\t\t\tguard: self.read_guard(),\n\t\t\t\tkey,\n\t\t\t})\n\t\t}\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// let key = RetryingLockCollection::\u003c(RwLock\u003ci32\u003e, RwLock\u003c\u0026str\u003e)\u003e::unlock_read(guard);\n\t/// ```\n\tpub fn unlock_read(guard: LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: LockableGetMut\u003e RetryingLockCollection\u003cL\u003e {\n\t/// Gets a mutable reference to the data behind this\n\t/// `RetryingLockCollection`.\n\t///\n\t/// Since this call borrows the `RetryingLockCollection` mutably, no actual\n\t/// locking needs to take place - the mutable borrow statically guarantees\n\t/// no locks exist.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let mut mutex = RetryingLockCollection::new([Mutex::new(0), Mutex::new(0)]);\n\t/// assert_eq!(mutex.get_mut(), [\u0026mut 0, \u0026mut 0]);\n\t/// ```\n\tpub fn get_mut(\u0026mut self) -\u003e L::Inner\u003c'_\u003e {\n\t\tLockableGetMut::get_mut(self)\n\t}\n}\n\nimpl\u003cL: LockableIntoInner\u003e RetryingLockCollection\u003cL\u003e {\n\t/// Consumes this `RetryingLockCollection`, returning the underlying data.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let mutex = RetryingLockCollection::new([Mutex::new(0), Mutex::new(0)]);\n\t/// assert_eq!(mutex.into_inner(), [0, 0]);\n\t/// ```\n\tpub fn into_inner(self) -\u003e L::Inner {\n\t\tLockableIntoInner::into_inner(self)\n\t}\n}\n\nimpl\u003c'a, L: 'a\u003e RetryingLockCollection\u003cL\u003e\nwhere\n\t\u0026'a L: IntoIterator,\n{\n\t/// Returns an iterator over references to each value in the collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = [Mutex::new(26), Mutex::new(1)];\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let mut iter = lock.iter();\n\t/// let mutex = iter.next().unwrap();\n\t/// let guard = mutex.lock(key);\n\t///\n\t/// assert_eq!(*guard, 26);\n\t/// ```\n\t#[must_use]\n\tpub fn iter(\u0026'a self) -\u003e \u003c\u0026'a L as IntoIterator\u003e::IntoIter {\n\t\tself.into_iter()\n\t}\n}\n\nimpl\u003c'a, L: 'a\u003e RetryingLockCollection\u003cL\u003e\nwhere\n\t\u0026'a mut L: IntoIterator,\n{\n\t/// Returns an iterator over mutable references to each value in the\n\t/// collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = [Mutex::new(26), Mutex::new(1)];\n\t/// let mut lock = RetryingLockCollection::new(data);\n\t///\n\t/// let mut iter = lock.iter_mut();\n\t/// let mutex = iter.next().unwrap();\n\t///\n\t/// assert_eq!(*mutex.as_mut(), 26);\n\t/// ```\n\t#[must_use]\n\tpub fn iter_mut(\u0026'a mut self) -\u003e \u003c\u0026'a mut L as IntoIterator\u003e::IntoIter {\n\t\tself.into_iter()\n\t}\n}\n\n#[cfg(test)]\nmod tests {\n\tuse super::*;\n\tuse crate::collection::BoxedLockCollection;\n\tuse crate::{Mutex, RwLock, ThreadKey};\n\n\t#[test]\n\tfn nonduplicate_lock_references_are_allowed() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(0);\n\t\tassert!(RetryingLockCollection::try_new([\u0026mutex1, \u0026mutex2]).is_some());\n\t}\n\n\t#[test]\n\tfn duplicate_lock_references_are_disallowed() {\n\t\tlet mutex = Mutex::new(0);\n\t\tassert!(RetryingLockCollection::try_new([\u0026mutex, \u0026mutex]).is_none());\n\t}\n\n\t#[test]\n\t#[allow(clippy::float_cmp)]\n\tfn uses_correct_default() {\n\t\tlet collection =\n\t\t\tRetryingLockCollection::\u003c(RwLock\u003cf64\u003e, Mutex\u003cOption\u003ci32\u003e\u003e, Mutex\u003cusize\u003e)\u003e::default();\n\t\tlet tuple = collection.into_inner();\n\t\tassert_eq!(tuple.0, 0.0);\n\t\tassert!(tuple.1.is_none());\n\t\tassert_eq!(tuple.2, 0)\n\t}\n\n\t#[test]\n\tfn from() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tRetryingLockCollection::from([Mutex::new(\"foo\"), Mutex::new(\"bar\"), Mutex::new(\"baz\")]);\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], \"foo\");\n\t\tassert_eq!(*guard[1], \"bar\");\n\t\tassert_eq!(*guard[2], \"baz\");\n\t}\n\n\t#[test]\n\tfn new_ref_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet collection = RetryingLockCollection::new_ref(\u0026mutexes);\n\t\tcollection.scoped_lock(key, |guard| {\n\t\t\tassert_eq!(*guard[0], 0);\n\t\t\tassert_eq!(*guard[1], 1);\n\t\t})\n\t}\n\n\t#[test]\n\tfn scoped_read_sees_changes() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = RetryingLockCollection::new(mutexes);\n\t\tcollection.scoped_lock(\u0026mut key, |guard| *guard[0] = 128);\n\n\t\tlet sum = collection.scoped_read(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 128);\n\t\t\tassert_eq!(*guard[1], 42);\n\t\t\t*guard[0] + *guard[1]\n\t\t});\n\n\t\tassert_eq!(sum, 128 + 42);\n\t}\n\n\t#[test]\n\tfn get_mut_affects_scoped_read() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet mut collection = RetryingLockCollection::new(mutexes);\n\t\tlet guard = collection.get_mut();\n\t\t*guard[0] = 128;\n\n\t\tlet sum = collection.scoped_read(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 128);\n\t\t\tassert_eq!(*guard[1], 42);\n\t\t\t*guard[0] + *guard[1]\n\t\t});\n\n\t\tassert_eq!(sum, 128 + 42);\n\t}\n\n\t#[test]\n\tfn scoped_try_lock_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = RetryingLockCollection::new([Mutex::new(1), Mutex::new(2)]);\n\t\tlet guard = collection.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = collection.scoped_try_lock(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn scoped_try_read_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = RetryingLockCollection::new([RwLock::new(1), RwLock::new(2)]);\n\t\tlet guard = collection.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = collection.scoped_try_read(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn try_lock_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = RetryingLockCollection::new([Mutex::new(1), Mutex::new(2)]);\n\t\tlet guard = collection.try_lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_lock(key);\n\t\t\t\tassert!(guard.is_err());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn try_read_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = RetryingLockCollection::new([RwLock::new(1), RwLock::new(2)]);\n\t\tlet guard = collection.try_read(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_read(key);\n\t\t\t\tassert!(guard.is_ok());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn try_read_fails_for_locked_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = RetryingLockCollection::new_ref(\u0026mutexes);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = mutexes[1].write(key);\n\t\t\t\tassert_eq!(*guard, 42);\n\t\t\t\tstd::mem::forget(guard);\n\t\t\t});\n\t\t});\n\n\t\tlet guard = collection.try_read(key);\n\t\tassert!(guard.is_err());\n\t}\n\n\t#[test]\n\tfn locks_all_inner_mutexes() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(0);\n\t\tlet collection = RetryingLockCollection::try_new([\u0026mutex1, \u0026mutex2]).unwrap();\n\n\t\tlet guard = collection.lock(key);\n\n\t\tassert!(mutex1.is_locked());\n\t\tassert!(mutex2.is_locked());\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn locks_all_inner_rwlocks() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet rwlock1 = RwLock::new(0);\n\t\tlet rwlock2 = RwLock::new(0);\n\t\tlet collection = RetryingLockCollection::try_new([\u0026rwlock1, \u0026rwlock2]).unwrap();\n\n\t\tlet guard = collection.read(key);\n\n\t\tassert!(rwlock1.is_locked());\n\t\tassert!(rwlock2.is_locked());\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn works_with_other_collections() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(0);\n\t\tlet collection = BoxedLockCollection::try_new(\n\t\t\tRetryingLockCollection::try_new([\u0026mutex1, \u0026mutex2]).unwrap(),\n\t\t)\n\t\t.unwrap();\n\n\t\tlet guard = collection.lock(key);\n\n\t\tassert!(mutex1.is_locked());\n\t\tassert!(mutex2.is_locked());\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn from_iterator() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection: RetryingLockCollection\u003cVec\u003cMutex\u003c\u0026str\u003e\u003e\u003e =\n\t\t\t[Mutex::new(\"foo\"), Mutex::new(\"bar\"), Mutex::new(\"baz\")]\n\t\t\t\t.into_iter()\n\t\t\t\t.collect();\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], \"foo\");\n\t\tassert_eq!(*guard[1], \"bar\");\n\t\tassert_eq!(*guard[2], \"baz\");\n\t}\n\n\t#[test]\n\tfn into_owned_iterator() {\n\t\tlet collection = RetryingLockCollection::new([Mutex::new(0), Mutex::new(1), Mutex::new(2)]);\n\t\tfor (i, mutex) in collection.into_iter().enumerate() {\n\t\t\tassert_eq!(mutex.into_inner(), i);\n\t\t}\n\t}\n\n\t#[test]\n\tfn into_ref_iterator() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet collection = RetryingLockCollection::new([Mutex::new(0), Mutex::new(1), Mutex::new(2)]);\n\t\tfor (i, mutex) in (\u0026collection).into_iter().enumerate() {\n\t\t\tmutex.scoped_lock(\u0026mut key, |val| assert_eq!(*val, i))\n\t\t}\n\t}\n\n\t#[test]\n\tfn ref_iterator() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet collection = RetryingLockCollection::new([Mutex::new(0), Mutex::new(1), Mutex::new(2)]);\n\t\tfor (i, mutex) in collection.iter().enumerate() {\n\t\t\tmutex.scoped_lock(\u0026mut key, |val| assert_eq!(*val, i))\n\t\t}\n\t}\n\n\t#[test]\n\tfn mut_iterator() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mut collection =\n\t\t\tRetryingLockCollection::new([Mutex::new(0), Mutex::new(1), Mutex::new(2)]);\n\t\tfor (i, mutex) in collection.iter_mut().enumerate() {\n\t\t\tmutex.scoped_lock(\u0026mut key, |val| assert_eq!(*val, i))\n\t\t}\n\t}\n\n\t#[test]\n\tfn extend_collection() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(0);\n\t\tlet mut collection = RetryingLockCollection::new(vec![mutex1]);\n\n\t\tcollection.extend([mutex2]);\n\n\t\tassert_eq!(collection.into_inner().len(), 2);\n\t}\n\n\t#[test]\n\tfn lock_empty_lock_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection: RetryingLockCollection\u003c[RwLock\u003ci32\u003e; 0]\u003e = RetryingLockCollection::new([]);\n\n\t\tlet guard = collection.lock(key);\n\t\tassert!(guard.len() == 0);\n\t\tlet key = RetryingLockCollection::\u003c[RwLock\u003c_\u003e; 0]\u003e::unlock(guard);\n\n\t\tlet guard = collection.read(key);\n\t\tassert!(guard.len() == 0);\n\t}\n\n\t#[test]\n\tfn read_empty_lock_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection: RetryingLockCollection\u003c[RwLock\u003ci32\u003e; 0]\u003e = RetryingLockCollection::new([]);\n\n\t\tlet guard = collection.read(key);\n\t\tassert!(guard.len() == 0);\n\t\tlet key = RetryingLockCollection::\u003c[RwLock\u003c_\u003e; 0]\u003e::unlock_read(guard);\n\n\t\tlet guard = collection.lock(key);\n\t\tassert!(guard.len() == 0);\n\t}\n\n\t#[test]\n\tfn as_ref_works() {\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet collection = RetryingLockCollection::new_ref(\u0026mutexes);\n\n\t\tassert!(std::ptr::addr_eq(\u0026mutexes, collection.as_ref()))\n\t}\n\n\t#[test]\n\tfn as_mut_works() {\n\t\tlet mut mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet mut collection = RetryingLockCollection::new(\u0026mut mutexes);\n\n\t\tcollection.as_mut()[0] = Mutex::new(42);\n\n\t\tassert_eq!(*collection.as_mut()[0].get_mut(), 42);\n\t}\n\n\t#[test]\n\tfn child() {\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet collection = RetryingLockCollection::new_ref(\u0026mutexes);\n\n\t\tassert!(std::ptr::addr_eq(\u0026mutexes, *collection.child()))\n\t}\n\n\t#[test]\n\tfn child_mut_works() {\n\t\tlet mut mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet mut collection = RetryingLockCollection::new(\u0026mut mutexes);\n\n\t\tcollection.child_mut()[0] = Mutex::new(42);\n\n\t\tassert_eq!(*collection.child_mut()[0].get_mut(), 42);\n\t}\n\n\t#[test]\n\tfn into_child_works() {\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet mut collection = RetryingLockCollection::new(mutexes);\n\n\t\tcollection.child_mut()[0] = Mutex::new(42);\n\n\t\tassert_eq!(\n\t\t\t*collection\n\t\t\t\t.into_child()\n\t\t\t\t.as_mut()\n\t\t\t\t.get_mut(0)\n\t\t\t\t.unwrap()\n\t\t\t\t.get_mut(),\n\t\t\t42\n\t\t);\n\t}\n}\n","traces":[{"line":18,"address":[200936,200896,201621],"length":1,"stats":{"Line":11}},{"line":19,"address":[1092524,1093292],"length":1,"stats":{"Line":11}},{"line":20,"address":[1093355,1092587],"length":1,"stats":{"Line":11}},{"line":22,"address":[201664,201034,201692],"length":1,"stats":{"Line":33}},{"line":24,"address":[178622,178688],"length":1,"stats":{"Line":22}},{"line":25,"address":[203288,203427,203469,203191],"length":1,"stats":{"Line":45}},{"line":26,"address":[1093942,1093109,1093877,1093174],"length":1,"stats":{"Line":22}},{"line":27,"address":[179072],"length":1,"stats":{"Line":1}},{"line":31,"address":[179008],"length":1,"stats":{"Line":11}},{"line":44,"address":[],"length":0,"stats":{"Line":11}},{"line":45,"address":[227116],"length":1,"stats":{"Line":11}},{"line":47,"address":[172966,173018],"length":1,"stats":{"Line":22}},{"line":49,"address":[],"length":0,"stats":{"Line":0}},{"line":53,"address":[227221,227184],"length":1,"stats":{"Line":20}},{"line":54,"address":[227226],"length":1,"stats":{"Line":10}},{"line":56,"address":[219661],"length":1,"stats":{"Line":20}},{"line":57,"address":[],"length":0,"stats":{"Line":0}},{"line":61,"address":[210577],"length":1,"stats":{"Line":10}},{"line":62,"address":[185006,184854],"length":1,"stats":{"Line":20}},{"line":63,"address":[],"length":0,"stats":{"Line":10}},{"line":65,"address":[],"length":0,"stats":{"Line":0}},{"line":73,"address":[212728],"length":1,"stats":{"Line":10}},{"line":74,"address":[1094490,1095194,1095610,1096314,1097434,1096874,1097290,1097850,1096730,1097994,1095754,1094634,1095050,1096170],"length":1,"stats":{"Line":18}},{"line":77,"address":[212752],"length":1,"stats":{"Line":1}},{"line":78,"address":[210916],"length":1,"stats":{"Line":1}},{"line":81,"address":[212899],"length":1,"stats":{"Line":1}},{"line":85,"address":[140775],"length":1,"stats":{"Line":1}},{"line":88,"address":[212879],"length":1,"stats":{"Line":1}},{"line":89,"address":[],"length":0,"stats":{"Line":0}},{"line":94,"address":[],"length":0,"stats":{"Line":0}},{"line":97,"address":[212992],"length":1,"stats":{"Line":11}},{"line":98,"address":[140921],"length":1,"stats":{"Line":1}},{"line":99,"address":[213060],"length":1,"stats":{"Line":1}},{"line":100,"address":[],"length":0,"stats":{"Line":1}},{"line":106,"address":[203424,203613],"length":1,"stats":{"Line":3}},{"line":107,"address":[],"length":0,"stats":{"Line":3}},{"line":109,"address":[],"length":0,"stats":{"Line":6}},{"line":112,"address":[],"length":0,"stats":{"Line":0}},{"line":116,"address":[203508,203550],"length":1,"stats":{"Line":6}},{"line":118,"address":[200464],"length":1,"stats":{"Line":3}},{"line":119,"address":[184273,184415],"length":1,"stats":{"Line":6}},{"line":121,"address":[200657],"length":1,"stats":{"Line":3}},{"line":122,"address":[200732],"length":1,"stats":{"Line":3}},{"line":125,"address":[1099824,1099488],"length":1,"stats":{"Line":2}},{"line":126,"address":[184517],"length":1,"stats":{"Line":3}},{"line":130,"address":[184408],"length":1,"stats":{"Line":1}},{"line":132,"address":[1099936,1099950,1100016,1100030],"length":1,"stats":{"Line":2}},{"line":136,"address":[219461,219189,218944,219216],"length":1,"stats":{"Line":3}},{"line":137,"address":[],"length":0,"stats":{"Line":3}},{"line":139,"address":[],"length":0,"stats":{"Line":9}},{"line":140,"address":[],"length":0,"stats":{"Line":3}},{"line":144,"address":[182216,181968],"length":1,"stats":{"Line":5}},{"line":145,"address":[181996],"length":1,"stats":{"Line":5}},{"line":147,"address":[1595846,1596118,1595898,1595626,1595354,1595574,1595302,1596170],"length":1,"stats":{"Line":10}},{"line":149,"address":[],"length":0,"stats":{"Line":0}},{"line":152,"address":[202864,202901],"length":1,"stats":{"Line":8}},{"line":153,"address":[182106],"length":1,"stats":{"Line":4}},{"line":155,"address":[177664],"length":1,"stats":{"Line":8}},{"line":157,"address":[1100113,1100673,1101233,1101793],"length":1,"stats":{"Line":4}},{"line":158,"address":[206502,206654],"length":1,"stats":{"Line":8}},{"line":159,"address":[177928],"length":1,"stats":{"Line":4}},{"line":160,"address":[],"length":0,"stats":{"Line":0}},{"line":164,"address":[],"length":0,"stats":{"Line":4}},{"line":165,"address":[1100618,1101738,1101178,1102154,1101034,1100474,1101594,1102298],"length":1,"stats":{"Line":6}},{"line":168,"address":[206752],"length":1,"stats":{"Line":1}},{"line":170,"address":[1101567,1100447,1101007,1102127],"length":1,"stats":{"Line":1}},{"line":173,"address":[206899],"length":1,"stats":{"Line":1}},{"line":177,"address":[206855],"length":1,"stats":{"Line":1}},{"line":180,"address":[206879],"length":1,"stats":{"Line":1}},{"line":181,"address":[],"length":0,"stats":{"Line":0}},{"line":186,"address":[],"length":0,"stats":{"Line":0}},{"line":188,"address":[182166],"length":1,"stats":{"Line":5}},{"line":189,"address":[],"length":0,"stats":{"Line":1}},{"line":190,"address":[178292],"length":1,"stats":{"Line":1}},{"line":191,"address":[178345],"length":1,"stats":{"Line":1}},{"line":197,"address":[190352,190541],"length":1,"stats":{"Line":3}},{"line":198,"address":[1596374,1596790,1596582],"length":1,"stats":{"Line":3}},{"line":200,"address":[1596846,1596638,1596800,1596430,1596592,1596384],"length":1,"stats":{"Line":6}},{"line":203,"address":[],"length":0,"stats":{"Line":0}},{"line":206,"address":[190436,190478],"length":1,"stats":{"Line":6}},{"line":208,"address":[202464],"length":1,"stats":{"Line":3}},{"line":209,"address":[202481,202623],"length":1,"stats":{"Line":6}},{"line":211,"address":[],"length":0,"stats":{"Line":3}},{"line":212,"address":[202732],"length":1,"stats":{"Line":3}},{"line":215,"address":[1103264,1103936,1103600],"length":1,"stats":{"Line":2}},{"line":216,"address":[],"length":0,"stats":{"Line":2}},{"line":220,"address":[1103188,1103524,1103860],"length":1,"stats":{"Line":1}},{"line":222,"address":[1104128,1104048,1104208,1104222,1104142,1104062],"length":1,"stats":{"Line":2}},{"line":226,"address":[1596976,1597493,1597248,1597221],"length":1,"stats":{"Line":1}},{"line":227,"address":[1596994,1597266],"length":1,"stats":{"Line":1}},{"line":229,"address":[],"length":0,"stats":{"Line":3}},{"line":230,"address":[],"length":0,"stats":{"Line":1}},{"line":246,"address":[],"length":0,"stats":{"Line":1}},{"line":247,"address":[],"length":0,"stats":{"Line":1}},{"line":250,"address":[151168],"length":1,"stats":{"Line":8}},{"line":251,"address":[],"length":0,"stats":{"Line":9}},{"line":254,"address":[],"length":0,"stats":{"Line":3}},{"line":255,"address":[],"length":0,"stats":{"Line":3}},{"line":270,"address":[],"length":0,"stats":{"Line":4}},{"line":271,"address":[190577],"length":1,"stats":{"Line":4}},{"line":274,"address":[1597952],"length":1,"stats":{"Line":1}},{"line":275,"address":[1597969],"length":1,"stats":{"Line":1}},{"line":287,"address":[1597984],"length":1,"stats":{"Line":1}},{"line":288,"address":[1598001],"length":1,"stats":{"Line":1}},{"line":295,"address":[],"length":0,"stats":{"Line":2}},{"line":296,"address":[1598077,1598020],"length":1,"stats":{"Line":2}},{"line":307,"address":[1598128],"length":1,"stats":{"Line":1}},{"line":308,"address":[1598142],"length":1,"stats":{"Line":1}},{"line":319,"address":[1598192],"length":1,"stats":{"Line":1}},{"line":320,"address":[1598197],"length":1,"stats":{"Line":1}},{"line":331,"address":[1598208],"length":1,"stats":{"Line":1}},{"line":332,"address":[],"length":0,"stats":{"Line":1}},{"line":339,"address":[1598224],"length":1,"stats":{"Line":1}},{"line":340,"address":[],"length":0,"stats":{"Line":1}},{"line":341,"address":[],"length":0,"stats":{"Line":1}},{"line":346,"address":[1598304],"length":1,"stats":{"Line":1}},{"line":347,"address":[],"length":0,"stats":{"Line":1}},{"line":352,"address":[],"length":0,"stats":{"Line":1}},{"line":353,"address":[1598373],"length":1,"stats":{"Line":1}},{"line":358,"address":[],"length":0,"stats":{"Line":1}},{"line":359,"address":[],"length":0,"stats":{"Line":1}},{"line":364,"address":[1598400],"length":1,"stats":{"Line":1}},{"line":365,"address":[],"length":0,"stats":{"Line":1}},{"line":370,"address":[],"length":0,"stats":{"Line":1}},{"line":371,"address":[],"length":0,"stats":{"Line":1}},{"line":392,"address":[],"length":0,"stats":{"Line":9}},{"line":394,"address":[1598488,1598520,1598552,1598609,1598680,1598712,1598648,1598584,1598629],"length":1,"stats":{"Line":9}},{"line":414,"address":[1598736,1598752],"length":1,"stats":{"Line":3}},{"line":416,"address":[1598741,1598757],"length":1,"stats":{"Line":3}},{"line":442,"address":[],"length":0,"stats":{"Line":23}},{"line":463,"address":[1599136],"length":1,"stats":{"Line":1}},{"line":464,"address":[],"length":0,"stats":{"Line":0}},{"line":484,"address":[],"length":0,"stats":{"Line":2}},{"line":485,"address":[],"length":0,"stats":{"Line":0}},{"line":505,"address":[],"length":0,"stats":{"Line":1}},{"line":506,"address":[],"length":0,"stats":{"Line":1}},{"line":530,"address":[151392,151557],"length":1,"stats":{"Line":11}},{"line":532,"address":[],"length":0,"stats":{"Line":22}},{"line":535,"address":[1599600,1599568],"length":1,"stats":{"Line":3}},{"line":536,"address":[],"length":0,"stats":{"Line":3}},{"line":539,"address":[219920],"length":1,"stats":{"Line":2}},{"line":544,"address":[1599641],"length":1,"stats":{"Line":2}},{"line":567,"address":[227550,227424],"length":1,"stats":{"Line":9}},{"line":570,"address":[1599952,1600094,1600240,1600366,1599678,1599808],"length":1,"stats":{"Line":9}},{"line":574,"address":[227502],"length":1,"stats":{"Line":8}},{"line":610,"address":[203888,204080],"length":1,"stats":{"Line":2}},{"line":613,"address":[1600541,1600523,1600612,1600480],"length":1,"stats":{"Line":5}},{"line":614,"address":[],"length":0,"stats":{"Line":1}},{"line":616,"address":[204000],"length":1,"stats":{"Line":1}},{"line":617,"address":[],"length":0,"stats":{"Line":0}},{"line":620,"address":[203981],"length":1,"stats":{"Line":1}},{"line":643,"address":[],"length":0,"stats":{"Line":1}},{"line":644,"address":[],"length":0,"stats":{"Line":1}},{"line":645,"address":[],"length":0,"stats":{"Line":0}},{"line":650,"address":[],"length":0,"stats":{"Line":2}},{"line":651,"address":[],"length":0,"stats":{"Line":2}},{"line":654,"address":[1600768],"length":1,"stats":{"Line":1}},{"line":659,"address":[1600777],"length":1,"stats":{"Line":1}},{"line":682,"address":[],"length":0,"stats":{"Line":4}},{"line":685,"address":[1600814,1600928],"length":1,"stats":{"Line":4}},{"line":689,"address":[],"length":0,"stats":{"Line":3}},{"line":726,"address":[],"length":0,"stats":{"Line":3}},{"line":729,"address":[],"length":0,"stats":{"Line":5}},{"line":730,"address":[190909],"length":1,"stats":{"Line":1}},{"line":733,"address":[190950],"length":1,"stats":{"Line":1}},{"line":735,"address":[190928],"length":1,"stats":{"Line":1}},{"line":736,"address":[],"length":0,"stats":{"Line":0}},{"line":757,"address":[],"length":0,"stats":{"Line":1}},{"line":758,"address":[1601428],"length":1,"stats":{"Line":1}},{"line":759,"address":[],"length":0,"stats":{"Line":0}},{"line":780,"address":[],"length":0,"stats":{"Line":1}},{"line":781,"address":[],"length":0,"stats":{"Line":1}},{"line":797,"address":[],"length":0,"stats":{"Line":2}},{"line":798,"address":[],"length":0,"stats":{"Line":2}},{"line":825,"address":[],"length":0,"stats":{"Line":1}},{"line":826,"address":[],"length":0,"stats":{"Line":1}},{"line":853,"address":[],"length":0,"stats":{"Line":1}},{"line":854,"address":[],"length":0,"stats":{"Line":1}}],"covered":161,"coverable":178},{"path":["/","home","botahamec","Projects","happylock","src","collection","utils.rs"],"content":"use std::cell::Cell;\n\nuse crate::handle_unwind::handle_unwind;\nuse crate::lockable::{Lockable, RawLock, Sharable};\nuse crate::Keyable;\n\n#[must_use]\npub fn get_locks\u003cL: Lockable\u003e(data: \u0026L) -\u003e Vec\u003c\u0026dyn RawLock\u003e {\n\tlet mut locks = Vec::new();\n\tdata.get_ptrs(\u0026mut locks);\n\tlocks.sort_by_key(|lock| \u0026raw const **lock);\n\tlocks\n}\n\n#[must_use]\npub fn get_locks_unsorted\u003cL: Lockable\u003e(data: \u0026L) -\u003e Vec\u003c\u0026dyn RawLock\u003e {\n\tlet mut locks = Vec::new();\n\tdata.get_ptrs(\u0026mut locks);\n\tlocks\n}\n\n/// returns `true` if the sorted list contains a duplicate\n#[must_use]\npub fn ordered_contains_duplicates(l: \u0026[\u0026dyn RawLock]) -\u003e bool {\n\tif l.is_empty() {\n\t\t// Return early to prevent panic in the below call to `windows`\n\t\treturn false;\n\t}\n\n\tl.windows(2)\n\t\t// NOTE: addr_eq is necessary because eq would also compare the v-table pointers\n\t\t.any(|window| std::ptr::addr_eq(window[0], window[1]))\n}\n\n/// Lock a set of locks in the given order. It's UB to call this without a `ThreadKey`\npub unsafe fn ordered_write(locks: \u0026[\u0026dyn RawLock]) {\n\t// these will be unlocked in case of a panic\n\tlet locked = Cell::new(0);\n\n\thandle_unwind(\n\t\t|| {\n\t\t\tfor lock in locks {\n\t\t\t\tlock.raw_write();\n\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t}\n\t\t},\n\t\t|| attempt_to_recover_writes_from_panic(\u0026locks[0..locked.get()]),\n\t)\n}\n\n/// Lock a set of locks in the given order. It's UB to call this without a `ThreadKey`\npub unsafe fn ordered_read(locks: \u0026[\u0026dyn RawLock]) {\n\tlet locked = Cell::new(0);\n\n\thandle_unwind(\n\t\t|| {\n\t\t\tfor lock in locks {\n\t\t\t\tlock.raw_read();\n\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t}\n\t\t},\n\t\t|| attempt_to_recover_reads_from_panic(\u0026locks[0..locked.get()]),\n\t)\n}\n\n/// Locks the locks in the order they are given. This causes deadlock if the\n/// locks contain duplicates, or if this is called by multiple threads with the\n/// locks in different orders.\npub unsafe fn ordered_try_write(locks: \u0026[\u0026dyn RawLock]) -\u003e bool {\n\tlet locked = Cell::new(0);\n\n\thandle_unwind(\n\t\t|| unsafe {\n\t\t\tfor (i, lock) in locks.iter().enumerate() {\n\t\t\t\t// safety: we have the thread key\n\t\t\t\tif lock.raw_try_write() {\n\t\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t\t} else {\n\t\t\t\t\tfor lock in \u0026locks[0..i] {\n\t\t\t\t\t\t// safety: this lock was already acquired\n\t\t\t\t\t\tlock.raw_unlock_write();\n\t\t\t\t\t}\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttrue\n\t\t},\n\t\t||\n\t\t// safety: everything in locked is locked\n\t\tattempt_to_recover_writes_from_panic(\u0026locks[0..locked.get()]),\n\t)\n}\n\n/// Locks the locks in the order they are given. This causes deadlock if this\n/// is called by multiple threads with the locks in different orders.\npub unsafe fn ordered_try_read(locks: \u0026[\u0026dyn RawLock]) -\u003e bool {\n\t// these will be unlocked in case of a panic\n\tlet locked = Cell::new(0);\n\n\thandle_unwind(\n\t\t|| unsafe {\n\t\t\tfor (i, lock) in locks.iter().enumerate() {\n\t\t\t\t// safety: we have the thread key\n\t\t\t\tif lock.raw_try_read() {\n\t\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t\t} else {\n\t\t\t\t\tfor lock in \u0026locks[0..i] {\n\t\t\t\t\t\t// safety: this lock was already acquired\n\t\t\t\t\t\tlock.raw_unlock_read();\n\t\t\t\t\t}\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttrue\n\t\t},\n\t\t||\n\t\t// safety: everything in locked is locked\n\t\tattempt_to_recover_reads_from_panic(\u0026locks[0..locked.get()]),\n\t)\n}\n\npub fn scoped_write\u003c'a, L: RawLock + Lockable, R\u003e(\n\tcollection: \u0026'a L,\n\tkey: impl Keyable,\n\tf: impl FnOnce(L::DataMut\u003c'a\u003e) -\u003e R,\n) -\u003e R {\n\tunsafe {\n\t\t// safety: we have the key\n\t\tcollection.raw_write();\n\n\t\t// safety: we just locked this\n\t\tlet r = f(collection.data_mut());\n\n\t\t// this ensures the key is held long enough\n\t\tdrop(key);\n\n\t\t// safety: we've locked already, and aren't using the data again\n\t\tcollection.raw_unlock_write();\n\n\t\tr\n\t}\n}\n\npub fn scoped_try_write\u003c'a, L: RawLock + Lockable, Key: Keyable, R\u003e(\n\tcollection: \u0026'a L,\n\tkey: Key,\n\tf: impl FnOnce(L::DataMut\u003c'a\u003e) -\u003e R,\n) -\u003e Result\u003cR, Key\u003e {\n\tunsafe {\n\t\t// safety: we have the key\n\t\tif !collection.raw_try_write() {\n\t\t\treturn Err(key);\n\t\t}\n\n\t\t// safety: we just locked this\n\t\tlet r = f(collection.data_mut());\n\n\t\t// this ensures the key is held long enough\n\t\tdrop(key);\n\n\t\t// safety: we've locked already, and aren't using the data again\n\t\tcollection.raw_unlock_write();\n\n\t\tOk(r)\n\t}\n}\n\npub fn scoped_read\u003c'a, L: RawLock + Sharable, R\u003e(\n\tcollection: \u0026'a L,\n\tkey: impl Keyable,\n\tf: impl FnOnce(L::DataRef\u003c'a\u003e) -\u003e R,\n) -\u003e R {\n\tunsafe {\n\t\t// safety: we have the key\n\t\tcollection.raw_read();\n\n\t\t// safety: we just locked this\n\t\tlet r = f(collection.data_ref());\n\n\t\t// this ensures the key is held long enough\n\t\tdrop(key);\n\n\t\t// safety: we've locked already, and aren't using the data again\n\t\tcollection.raw_unlock_read();\n\n\t\tr\n\t}\n}\n\npub fn scoped_try_read\u003c'a, L: RawLock + Sharable, Key: Keyable, R\u003e(\n\tcollection: \u0026'a L,\n\tkey: Key,\n\tf: impl FnOnce(L::DataRef\u003c'a\u003e) -\u003e R,\n) -\u003e Result\u003cR, Key\u003e {\n\tunsafe {\n\t\t// safety: we have the key\n\t\tif !collection.raw_try_read() {\n\t\t\treturn Err(key);\n\t\t}\n\n\t\t// safety: we just locked this\n\t\tlet r = f(collection.data_ref());\n\n\t\t// this ensures the key is held long enough\n\t\tdrop(key);\n\n\t\t// safety: we've locked already, and aren't using the data again\n\t\tcollection.raw_unlock_read();\n\n\t\tOk(r)\n\t}\n}\n\n/// Unlocks the already locked locks in order to recover from a panic\npub unsafe fn attempt_to_recover_writes_from_panic(locks: \u0026[\u0026dyn RawLock]) {\n\thandle_unwind(\n\t\t|| {\n\t\t\t// safety: the caller assumes that these are already locked\n\t\t\tlocks.iter().for_each(|lock| lock.raw_unlock_write());\n\t\t},\n\t\t// if we get another panic in here, we'll just have to poison what remains\n\t\t|| locks.iter().for_each(|l| l.poison()),\n\t)\n}\n\n/// Unlocks the already locked locks in order to recover from a panic\npub unsafe fn attempt_to_recover_reads_from_panic(locked: \u0026[\u0026dyn RawLock]) {\n\thandle_unwind(\n\t\t|| {\n\t\t\t// safety: the caller assumes these are already locked\n\t\t\tlocked.iter().for_each(|lock| lock.raw_unlock_read());\n\t\t},\n\t\t// if we get another panic in here, we'll just have to poison what remains\n\t\t|| locked.iter().for_each(|l| l.poison()),\n\t)\n}\n\n#[cfg(test)]\nmod tests {\n\tuse crate::collection::utils::ordered_contains_duplicates;\n\n\t#[test]\n\tfn empty_array_does_not_contain_duplicates() {\n\t\tassert!(!ordered_contains_duplicates(\u0026[]))\n\t}\n}\n","traces":[{"line":8,"address":[],"length":0,"stats":{"Line":9}},{"line":9,"address":[],"length":0,"stats":{"Line":9}},{"line":10,"address":[426641,427217,426449,426065,427409,427025,426257,426833,425873],"length":1,"stats":{"Line":9}},{"line":11,"address":[],"length":0,"stats":{"Line":26}},{"line":12,"address":[],"length":0,"stats":{"Line":9}},{"line":16,"address":[159885,159760],"length":1,"stats":{"Line":26}},{"line":17,"address":[],"length":0,"stats":{"Line":26}},{"line":18,"address":[],"length":0,"stats":{"Line":26}},{"line":19,"address":[],"length":0,"stats":{"Line":26}},{"line":24,"address":[536384],"length":1,"stats":{"Line":8}},{"line":25,"address":[453224],"length":1,"stats":{"Line":8}},{"line":27,"address":[538422],"length":1,"stats":{"Line":1}},{"line":30,"address":[535275],"length":1,"stats":{"Line":8}},{"line":32,"address":[526845,526963,526816],"length":1,"stats":{"Line":25}},{"line":36,"address":[532832],"length":1,"stats":{"Line":4}},{"line":38,"address":[517230],"length":1,"stats":{"Line":5}},{"line":41,"address":[518608],"length":1,"stats":{"Line":7}},{"line":42,"address":[430567,430494],"length":1,"stats":{"Line":14}},{"line":43,"address":[527107],"length":1,"stats":{"Line":7}},{"line":44,"address":[557021],"length":1,"stats":{"Line":5}},{"line":47,"address":[565413,565232,565239],"length":1,"stats":{"Line":16}},{"line":52,"address":[535440],"length":1,"stats":{"Line":2}},{"line":53,"address":[547230],"length":1,"stats":{"Line":2}},{"line":56,"address":[430864],"length":1,"stats":{"Line":2}},{"line":57,"address":[430951,430878],"length":1,"stats":{"Line":4}},{"line":58,"address":[430961],"length":1,"stats":{"Line":2}},{"line":59,"address":[548749],"length":1,"stats":{"Line":2}},{"line":62,"address":[538573],"length":1,"stats":{"Line":5}},{"line":69,"address":[547312],"length":1,"stats":{"Line":2}},{"line":70,"address":[509031],"length":1,"stats":{"Line":2}},{"line":73,"address":[543424],"length":1,"stats":{"Line":4}},{"line":74,"address":[549197,549055],"length":1,"stats":{"Line":4}},{"line":76,"address":[557906],"length":1,"stats":{"Line":2}},{"line":77,"address":[547680,547546],"length":1,"stats":{"Line":4}},{"line":79,"address":[543917,543662,543978,543853],"length":1,"stats":{"Line":4}},{"line":81,"address":[566410],"length":1,"stats":{"Line":1}},{"line":83,"address":[549584],"length":1,"stats":{"Line":1}},{"line":87,"address":[543574],"length":1,"stats":{"Line":1}},{"line":89,"address":[566464],"length":1,"stats":{"Line":3}},{"line":91,"address":[528439,528613],"length":1,"stats":{"Line":2}},{"line":97,"address":[517520],"length":1,"stats":{"Line":2}},{"line":99,"address":[533159],"length":1,"stats":{"Line":2}},{"line":102,"address":[528640],"length":1,"stats":{"Line":4}},{"line":103,"address":[548077,547935],"length":1,"stats":{"Line":4}},{"line":105,"address":[449794],"length":1,"stats":{"Line":3}},{"line":106,"address":[450192,450058],"length":1,"stats":{"Line":4}},{"line":108,"address":[558782,558973,559098,559037],"length":1,"stats":{"Line":4}},{"line":110,"address":[548490],"length":1,"stats":{"Line":1}},{"line":112,"address":[548464],"length":1,"stats":{"Line":1}},{"line":116,"address":[528790],"length":1,"stats":{"Line":1}},{"line":118,"address":[548544],"length":1,"stats":{"Line":3}},{"line":120,"address":[544887,545061],"length":1,"stats":{"Line":2}},{"line":124,"address":[183024,183274],"length":1,"stats":{"Line":27}},{"line":131,"address":[140040],"length":1,"stats":{"Line":27}},{"line":134,"address":[],"length":0,"stats":{"Line":27}},{"line":137,"address":[183209],"length":1,"stats":{"Line":27}},{"line":140,"address":[140216],"length":1,"stats":{"Line":27}},{"line":142,"address":[],"length":0,"stats":{"Line":0}},{"line":146,"address":[439886,439296,440485,439600,440800,440168,440208,440496,439574,439864,441390,440190,441078,441104,439904,440782,440463,441368,440760],"length":1,"stats":{"Line":38}},{"line":153,"address":[213952,215472,214488,214864,214560,213880,215400,214184,214256,215096,215168,214792],"length":1,"stats":{"Line":76}},{"line":154,"address":[188747,188443,189051,187531,188139,187835],"length":1,"stats":{"Line":20}},{"line":158,"address":[180326,180796,180022,179580,179611,180492,180188,180219,179307,179718,179884,180934,179276,179414,179915,180523,180827,180630],"length":1,"stats":{"Line":18}},{"line":161,"address":[225454,224238,224846,225758,224542,225150],"length":1,"stats":{"Line":18}},{"line":164,"address":[],"length":0,"stats":{"Line":18}},{"line":166,"address":[225499,224283,224587,224891,225803,225195],"length":1,"stats":{"Line":18}},{"line":170,"address":[443488,443475,444003,442448,443731,441908,441667,443744,442976,441680,442435,443216,442163,442933,442707,442720,442955,441920,443204,442176,441408],"length":1,"stats":{"Line":10}},{"line":177,"address":[442472,442734,443240,441944,443512,441704,442200,441432,443768,443000],"length":1,"stats":{"Line":10}},{"line":180,"address":[441773,443069,443309,441892,442269,443188,441501,441651,442917,443581,442691,443715,442419,442803,442013,442541,442147,443837,443987,443459],"length":1,"stats":{"Line":10}},{"line":183,"address":[441597,442637,443665,443933,443141,441845,442097,442870,442365,443405],"length":1,"stats":{"Line":10}},{"line":186,"address":[],"length":0,"stats":{"Line":10}},{"line":188,"address":[],"length":0,"stats":{"Line":0}},{"line":192,"address":[228016,228928,229206,229510,228902,229232,229536,228598,228320,229814,228294,228624],"length":1,"stats":{"Line":12}},{"line":199,"address":[],"length":0,"stats":{"Line":24}},{"line":200,"address":[],"length":0,"stats":{"Line":7}},{"line":204,"address":[229387,229083,229494,229691,228475,228748,229356,229052,228582,228140,229660,228171,228278,228886,228444,229190,229798,228779],"length":1,"stats":{"Line":5}},{"line":207,"address":[228526,228830,228222,229438,229742,229134],"length":1,"stats":{"Line":5}},{"line":210,"address":[],"length":0,"stats":{"Line":5}},{"line":212,"address":[229483,228571,228267,229179,228875,229787],"length":1,"stats":{"Line":5}},{"line":217,"address":[536912],"length":1,"stats":{"Line":6}},{"line":219,"address":[544656],"length":1,"stats":{"Line":8}},{"line":221,"address":[545102,545136,545150],"length":1,"stats":{"Line":20}},{"line":224,"address":[559470,559518,559456,559504],"length":1,"stats":{"Line":4}},{"line":229,"address":[517680],"length":1,"stats":{"Line":4}},{"line":231,"address":[529632],"length":1,"stats":{"Line":4}},{"line":233,"address":[545262,545296,545310],"length":1,"stats":{"Line":12}},{"line":236,"address":[544958,544910,544944,544896],"length":1,"stats":{"Line":4}}],"covered":84,"coverable":86},{"path":["/","home","botahamec","Projects","happylock","src","collection.rs"],"content":"use std::cell::UnsafeCell;\n\nuse crate::{lockable::RawLock, ThreadKey};\n\nmod boxed;\nmod guard;\nmod owned;\nmod r#ref;\nmod retry;\npub(crate) mod utils;\n\n/// Locks a collection of locks, which cannot be shared immutably.\n///\n/// This could be a tuple of [`Lockable`] types, an array, or a `Vec`. But it\n/// can be safely locked without causing a deadlock.\n///\n/// The data in this collection is guaranteed to not contain duplicates because\n/// `L` must always implement [`OwnedLockable`]. The underlying data may not be\n/// immutably referenced and locked. Because of this, there is no need for\n/// sorting the locks in the collection, or checking for duplicates, because it\n/// can be guaranteed that until the underlying collection is mutated (which\n/// requires releasing all acquired locks in the collection to do), then the\n/// locks will stay in the same order and be locked in that order, preventing\n/// cyclic wait.\n///\n/// [`Lockable`]: `crate::lockable::Lockable`\n/// [`OwnedLockable`]: `crate::lockable::OwnedLockable`\n\n// this type caches the idea that no immutable references to the underlying\n// collection exist\n#[derive(Debug)]\npub struct OwnedLockCollection\u003cL\u003e {\n\tdata: L,\n}\n\n/// Locks a reference to a collection of locks, by sorting them by memory\n/// address.\n///\n/// This could be a tuple of [`Lockable`] types, an array, or a `Vec`. But it\n/// can be safely locked without causing a deadlock.\n///\n/// Upon construction, it must be confirmed that the collection contains no\n/// duplicate locks. This can be done by either using [`OwnedLockable`] or by\n/// checking. Regardless of how this is done, the locks will be sorted by their\n/// memory address before locking them. The sorted order of the locks is stored\n/// within this collection.\n///\n/// Unlike [`BoxedLockCollection`], this type does not allocate memory for the\n/// data, although it does allocate memory for the sorted list of lock\n/// references. This makes it slightly faster, but lifetimes must be handled.\n///\n/// [`Lockable`]: `crate::lockable::Lockable`\n/// [`OwnedLockable`]: `crate::lockable::OwnedLockable`\n//\n// This type was born when I eventually realized that I needed a self\n// referential structure. That used boxing, so I elected to make a more\n// efficient implementation (polonius please save us)\n//\n// This type caches the sorting order of the locks and the fact that it doesn't\n// contain any duplicates.\npub struct RefLockCollection\u003c'a, L\u003e {\n\tdata: \u0026'a L,\n\tlocks: Vec\u003c\u0026'a dyn RawLock\u003e,\n}\n\n/// Locks a collection of locks, stored in the heap, by sorting them by memory\n/// address.\n///\n/// This could be a tuple of [`Lockable`] types, an array, or a `Vec`. But it\n/// can be safely locked without causing a deadlock.\n///\n/// Upon construction, it must be confirmed that the collection contains no\n/// duplicate locks. This can be done by either using [`OwnedLockable`] or by\n/// checking. Regardless of how this is done, the locks will be sorted by their\n/// memory address before locking them. The sorted order of the locks is stored\n/// within this collection.\n///\n/// Unlike [`RefLockCollection`], this is a self-referential type which boxes\n/// the data that is given to it. This means no lifetimes are necessary on the\n/// type itself, but it is slightly slower because of the memory allocation.\n///\n/// [`Lockable`]: `crate::lockable::Lockable`\n/// [`OwnedLockable`]: `crate::lockable::OwnedLockable`\n//\n// This type caches the sorting order of the locks and the fact that it doesn't\n// contain any duplicates.\npub struct BoxedLockCollection\u003cL\u003e {\n\tdata: *const UnsafeCell\u003cL\u003e,\n\tlocks: Vec\u003c\u0026'static dyn RawLock\u003e,\n}\n\n/// Locks a collection of locks using a retrying algorithm.\n///\n/// This could be a tuple of [`Lockable`] types, an array, or a `Vec`. But it\n/// can be safely locked without causing a deadlock.\n///\n/// The data in this collection is guaranteed to not contain duplicates, but it\n/// also not be sorted. In some cases the lack of sorting can increase\n/// performance. However, in most cases, this collection will be slower. Cyclic\n/// wait is not guaranteed here, so the locking algorithm must release all its\n/// locks if one of the lock attempts blocks. This results in wasted time and\n/// potential [livelocking].\n///\n/// However, one case where this might be faster than [`RefLockCollection`] is\n/// when the first lock in the collection is always the first in any\n/// collection, and the other locks in the collection are always locked after\n/// that first lock is acquired. This means that as soon as it is locked, there\n/// will be no need to unlock it later on subsequent lock attempts, because\n/// they will always succeed.\n///\n/// [`Lockable`]: `crate::lockable::Lockable`\n/// [`OwnedLockable`]: `crate::lockable::OwnedLockable`\n/// [livelocking]: https://en.wikipedia.org/wiki/Deadlock#Livelock\n//\n// This type caches the fact that there are no duplicates\n#[derive(Debug)]\npub struct RetryingLockCollection\u003cL\u003e {\n\tdata: L,\n}\n\n/// A RAII guard for a generic [`Lockable`] type.\n///\n/// [`Lockable`]: `crate::lockable::Lockable`\npub struct LockGuard\u003cGuard\u003e {\n\tguard: Guard,\n\tkey: ThreadKey,\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","src","handle_unwind.rs"],"content":"use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};\n\n/// Runs `try_fn`. If it unwinds, it will run `catch` and then continue\n/// unwinding. This is used instead of `scopeguard` to ensure the `catch`\n/// function doesn't run if the thread is already panicking. The unwind\n/// must specifically be caused by the `try_fn`\npub fn handle_unwind\u003cR, F: FnOnce() -\u003e R, G: FnOnce()\u003e(try_fn: F, catch: G) -\u003e R {\n\tlet try_fn = AssertUnwindSafe(try_fn);\n\tcatch_unwind(try_fn).unwrap_or_else(|e| {\n\t\tcatch();\n\t\tresume_unwind(e)\n\t})\n}\n","traces":[{"line":7,"address":[509474,510000,509840,509638,510152,510326,509816,509488,509664,509986,509328,510176],"length":1,"stats":{"Line":151}},{"line":8,"address":[234713,234578,234283,234978,234850,235106,234441],"length":1,"stats":{"Line":118}},{"line":9,"address":[235177,235984,235945,235216,234921,235331,235600,235971,234649,235344,235856,234461,235049,234308,234993,235305,235121,235689,235715,234498,236099,235561,234350,235817,234733,234593,235436,236073,235472,235460,234865,235728,235587,234770,235843],"length":1,"stats":{"Line":301}},{"line":10,"address":[538487,538615,538333,538045,538733,538189],"length":1,"stats":{"Line":28}},{"line":11,"address":[549421,548877,548733,549153,549281,549021],"length":1,"stats":{"Line":24}}],"covered":5,"coverable":5},{"path":["/","home","botahamec","Projects","happylock","src","key.rs"],"content":"use std::cell::{Cell, LazyCell};\nuse std::fmt::{self, Debug};\nuse std::marker::PhantomData;\n\nuse sealed::Sealed;\n\n// Sealed to prevent other key types from being implemented. Otherwise, this\n// would almost instant undefined behavior.\nmod sealed {\n\tuse super::ThreadKey;\n\n\tpub trait Sealed {}\n\timpl Sealed for ThreadKey {}\n\timpl Sealed for \u0026mut ThreadKey {}\n}\n\nthread_local! {\n\tstatic KEY: LazyCell\u003cKeyCell\u003e = LazyCell::new(KeyCell::default);\n}\n\n/// The key for the current thread.\n///\n/// Only one of these exist per thread. To get the current thread's key, call\n/// [`ThreadKey::get`]. If the `ThreadKey` is dropped, it can be re-obtained.\npub struct ThreadKey {\n\tphantom: PhantomData\u003c*const ()\u003e, // implement !Send and !Sync\n}\n\n/// Allows the type to be used as a key for a lock\n///\n/// # Safety\n///\n/// Only one value which implements this trait may be allowed to exist at a\n/// time. Creating a new `Keyable` value requires making any other `Keyable`\n/// values invalid.\npub unsafe trait Keyable: Sealed {}\nunsafe impl Keyable for ThreadKey {}\n// the ThreadKey can't be moved while a mutable reference to it exists\nunsafe impl Keyable for \u0026mut ThreadKey {}\n\n// Implementing this means we can allow `MutexGuard` to be Sync\n// Safety: a \u0026ThreadKey is useless by design.\nunsafe impl Sync for ThreadKey {}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl Debug for ThreadKey {\n\tfn fmt(\u0026self, f: \u0026mut fmt::Formatter\u003c'_\u003e) -\u003e fmt::Result {\n\t\twrite!(f, \"ThreadKey\")\n\t}\n}\n\n// If you lose the thread key, you can get it back by calling ThreadKey::get\nimpl Drop for ThreadKey {\n\tfn drop(\u0026mut self) {\n\t\t// safety: a thread key cannot be acquired without creating the lock\n\t\t// safety: the key is lost, so it's safe to unlock the cell\n\t\tunsafe { KEY.with(|key| key.force_unlock()) }\n\t}\n}\n\nimpl ThreadKey {\n\t/// Get the current thread's `ThreadKey`, if it's not already taken.\n\t///\n\t/// The first time this is called, it will successfully return a\n\t/// `ThreadKey`. However, future calls to this function on the same thread\n\t/// will return [`None`], unless the key is dropped or unlocked first.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::ThreadKey;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// ```\n\t#[must_use]\n\tpub fn get() -\u003e Option\u003cSelf\u003e {\n\t\t// safety: we just acquired the lock\n\t\t// safety: if this code changes, check to ensure the requirement for\n\t\t// the Drop implementation is still true\n\t\tKEY.with(|key| {\n\t\t\tkey.try_lock().then_some(Self {\n\t\t\t\tphantom: PhantomData,\n\t\t\t})\n\t\t})\n\t}\n}\n\n/// A dumb lock that's just a wrapper for an [`AtomicBool`].\n#[derive(Default)]\nstruct KeyCell {\n\tis_locked: Cell\u003cbool\u003e,\n}\n\nimpl KeyCell {\n\t/// Attempt to lock the `KeyCell`. This is not a fair lock.\n\t#[must_use]\n\tpub fn try_lock(\u0026self) -\u003e bool {\n\t\t!self.is_locked.replace(true)\n\t}\n\n\t/// Forcibly unlocks the `KeyCell`. This should only be called if the key\n\t/// from this `KeyCell` has been \"lost\".\n\tpub unsafe fn force_unlock(\u0026self) {\n\t\tself.is_locked.set(false);\n\t}\n}\n\n#[cfg(test)]\nmod tests {\n\tuse super::*;\n\n\t#[test]\n\tfn thread_key_returns_some_on_first_call() {\n\t\tassert!(ThreadKey::get().is_some());\n\t}\n\n\t#[test]\n\tfn thread_key_returns_none_on_second_call() {\n\t\tlet key = ThreadKey::get();\n\t\tassert!(ThreadKey::get().is_none());\n\t\tdrop(key);\n\t}\n\n\t#[test]\n\tfn dropping_thread_key_allows_reobtaining() {\n\t\tdrop(ThreadKey::get());\n\t\tassert!(ThreadKey::get().is_some())\n\t}\n}\n","traces":[{"line":18,"address":[536840],"length":1,"stats":{"Line":23}},{"line":55,"address":[534960],"length":1,"stats":{"Line":11}},{"line":58,"address":[515685],"length":1,"stats":{"Line":36}},{"line":77,"address":[542368],"length":1,"stats":{"Line":21}},{"line":81,"address":[540928],"length":1,"stats":{"Line":42}},{"line":82,"address":[542889],"length":1,"stats":{"Line":22}},{"line":98,"address":[536784],"length":1,"stats":{"Line":23}},{"line":99,"address":[551077],"length":1,"stats":{"Line":23}},{"line":104,"address":[536816],"length":1,"stats":{"Line":12}},{"line":105,"address":[439893],"length":1,"stats":{"Line":12}}],"covered":10,"coverable":10},{"path":["/","home","botahamec","Projects","happylock","src","lib.rs"],"content":"#![warn(clippy::pedantic)]\n#![warn(clippy::nursery)]\n#![allow(clippy::module_name_repetitions)]\n#![allow(clippy::declare_interior_mutable_const)]\n#![allow(clippy::semicolon_if_nothing_returned)]\n#![allow(clippy::module_inception)]\n#![allow(clippy::single_match_else)]\n\n//! As it turns out, the Rust borrow checker is powerful enough that, if the\n//! standard library supported it, we could've made deadlocks undefined\n//! behavior. This library currently serves as a proof of concept for how that\n//! would work.\n//!\n//! # Theory\n//!\n//! There are four conditions necessary for a deadlock to occur. In order to\n//! prevent deadlocks, we just need to prevent one of the following:\n//!\n//! 1. mutual exclusion\n//! 2. non-preemptive allocation\n//! 3. circular wait\n//! 4. **partial allocation**\n//!\n//! This library seeks to solve **partial allocation** by requiring total\n//! allocation. All the resources a thread needs must be allocated at the same\n//! time. In order to request new resources, the old resources must be dropped\n//! first. Requesting multiple resources at once is atomic. You either get all\n//! the requested resources or none at all.\n//!\n//! As an optimization, this library also often prevents **circular wait**.\n//! Many collections sort the locks in order of their memory address. As long\n//! as the locks are always acquired in that order, then time doesn't need to\n//! be wasted on releasing locks after a failure and re-acquiring them later.\n//!\n//! # Examples\n//!\n//! Simple example:\n//! ```\n//! use std::thread;\n//! use happylock::{Mutex, ThreadKey};\n//!\n//! const N: usize = 10;\n//!\n//! static DATA: Mutex\u003ci32\u003e = Mutex::new(0);\n//!\n//! for _ in 0..N {\n//! thread::spawn(move || {\n//! // each thread gets one thread key\n//! let key = ThreadKey::get().unwrap();\n//!\n//! // unlocking a mutex requires a ThreadKey\n//! let mut data = DATA.lock(key);\n//! *data += 1;\n//!\n//! // the key is unlocked at the end of the scope\n//! });\n//! }\n//!\n//! let key = ThreadKey::get().unwrap();\n//! let data = DATA.lock(key);\n//! println!(\"{}\", *data);\n//! ```\n//!\n//! To lock multiple mutexes at a time, create a [`LockCollection`]:\n//!\n//! ```\n//! use std::thread;\n//! use happylock::{LockCollection, Mutex, ThreadKey};\n//!\n//! const N: usize = 10;\n//!\n//! static DATA_1: Mutex\u003ci32\u003e = Mutex::new(0);\n//! static DATA_2: Mutex\u003cString\u003e = Mutex::new(String::new());\n//!\n//! for _ in 0..N {\n//! thread::spawn(move || {\n//! let key = ThreadKey::get().unwrap();\n//!\n//! // happylock ensures at runtime there are no duplicate locks\n//! let collection = LockCollection::try_new((\u0026DATA_1, \u0026DATA_2)).unwrap();\n//! let mut guard = collection.lock(key);\n//!\n//! *guard.1 = (100 - *guard.0).to_string();\n//! *guard.0 += 1;\n//! });\n//! }\n//!\n//! let key = ThreadKey::get().unwrap();\n//! let data = LockCollection::try_new((\u0026DATA_1, \u0026DATA_2)).unwrap();\n//! let data = data.lock(key);\n//! println!(\"{}\", *data.0);\n//! println!(\"{}\", *data.1);\n//! ```\n//!\n//! In many cases, the [`LockCollection::new`] or [`LockCollection::new_ref`]\n//! method can be used, improving performance.\n//!\n//! ```rust\n//! use std::thread;\n//! use happylock::{LockCollection, Mutex, ThreadKey};\n//!\n//! const N: usize = 32;\n//!\n//! static DATA: [Mutex\u003ci32\u003e; 2] = [Mutex::new(0), Mutex::new(1)];\n//!\n//! for _ in 0..N {\n//! thread::spawn(move || {\n//! let key = ThreadKey::get().unwrap();\n//!\n//! // a reference to a type that implements `OwnedLockable` will never\n//! // contain duplicates, so no duplicate checking is needed.\n//! let collection = LockCollection::new_ref(\u0026DATA);\n//! let mut guard = collection.lock(key);\n//!\n//! let x = *guard[1];\n//! *guard[1] += *guard[0];\n//! *guard[0] = x;\n//! });\n//! }\n//!\n//! let key = ThreadKey::get().unwrap();\n//! let data = LockCollection::new_ref(\u0026DATA);\n//! let data = data.lock(key);\n//! println!(\"{}\", data[0]);\n//! println!(\"{}\", data[1]);\n//! ```\n//!\n//! # Performance\n//!\n//! **The `ThreadKey` is a mostly-zero cost abstraction.** It doesn't use any\n//! memory, and it doesn't really exist at run-time. The only cost comes from\n//! calling `ThreadKey::get()`, because the function has to ensure at runtime\n//! that the key hasn't already been taken. Dropping the key will also have a\n//! small cost.\n//!\n//! **Consider [`OwnedLockCollection`].** This will almost always be the\n//! fastest lock collection. It doesn't expose the underlying collection\n//! immutably, which means that it will always be locked in the same order, and\n//! doesn't need any sorting.\n//!\n//! **Avoid [`LockCollection::try_new`].** This constructor will check to make\n//! sure that the collection contains no duplicate locks. In most cases, this\n//! is O(nlogn), where n is the number of locks in the collections but in the\n//! case of [`RetryingLockCollection`], it's close to O(n).\n//! [`LockCollection::new`] and [`LockCollection::new_ref`] don't need these\n//! checks because they use [`OwnedLockable`], which is guaranteed to be unique\n//! as long as it is accessible. As a last resort,\n//! [`LockCollection::new_unchecked`] doesn't do this check, but is unsafe to\n//! call.\n//!\n//! **Know how to use [`RetryingLockCollection`].** This collection doesn't do\n//! any sorting, but uses a wasteful lock algorithm. It can't rely on the order\n//! of the locks to be the same across threads, so if it finds a lock that it\n//! can't acquire without blocking, it'll first release all of the locks it\n//! already acquired to avoid blocking other threads. This is wasteful because\n//! this algorithm may end up re-acquiring the same lock multiple times. To\n//! avoid this, ensure that (1) the first lock in the collection is always the\n//! first lock in any collection it appears in, and (2) the other locks in the\n//! collection are always preceded by that first lock. This will prevent any\n//! wasted time from re-acquiring locks. If you're unsure, [`LockCollection`]\n//! is a sensible default.\n//!\n//! [`OwnedLockable`]: `lockable::OwnedLockable`\n//! [`OwnedLockCollection`]: `collection::OwnedLockCollection`\n//! [`RetryingLockCollection`]: `collection::RetryingLockCollection`\n\nmod handle_unwind;\nmod key;\n\npub mod collection;\npub mod lockable;\npub mod mutex;\npub mod poisonable;\npub mod rwlock;\n\npub use key::{Keyable, ThreadKey};\n\n#[cfg(feature = \"spin\")]\npub use mutex::SpinLock;\n\n// Personally, I think re-exports look ugly in the rust documentation, so I\n// went with type aliases instead.\n\n/// A collection of locks that can be acquired simultaneously.\n///\n/// This re-exports [`BoxedLockCollection`] as a sensible default.\n///\n/// [`BoxedLockCollection`]: collection::BoxedLockCollection\npub type LockCollection\u003cL\u003e = collection::BoxedLockCollection\u003cL\u003e;\n\n/// A re-export for [`poisonable::Poisonable`]\npub type Poisonable\u003cL\u003e = poisonable::Poisonable\u003cL\u003e;\n\n/// A mutual exclusion primitive useful for protecting shared data, which cannot deadlock.\n///\n/// By default, this uses `parking_lot` as a backend.\n#[cfg(feature = \"parking_lot\")]\npub type Mutex\u003cT\u003e = mutex::Mutex\u003cT, parking_lot::RawMutex\u003e;\n\n/// A reader-writer lock\n///\n/// By default, this uses `parking_lot` as a backend.\n#[cfg(feature = \"parking_lot\")]\npub type RwLock\u003cT\u003e = rwlock::RwLock\u003cT, parking_lot::RawRwLock\u003e;\n","traces":[{"line":197,"address":[423062],"length":1,"stats":{"Line":10}}],"covered":1,"coverable":1},{"path":["/","home","botahamec","Projects","happylock","src","lockable.rs"],"content":"use std::mem::MaybeUninit;\n\n/// A raw lock type that may be locked and unlocked\n///\n/// # Safety\n///\n/// A deadlock must never occur. The `unlock` method must correctly unlock the\n/// data. The `get_ptrs` method must be implemented correctly. The `Output`\n/// must be unlocked when it is dropped.\n//\n// Why not use a RawRwLock? Because that would be semantically incorrect, and I\n// don't want an INIT or GuardMarker associated item.\n// Originally, RawLock had a sister trait: RawSharableLock. I removed it\n// because it'd be difficult to implement a separate type that takes a\n// different kind of RawLock. But now the Sharable marker trait is needed to\n// indicate if reads can be used.\npub unsafe trait RawLock {\n\t/// Causes all subsequent calls to the `lock` function on this lock to\n\t/// panic. This does not affect anything currently holding the lock.\n\tfn poison(\u0026self);\n\n\t/// Blocks until the lock is acquired\n\t///\n\t/// # Safety\n\t///\n\t/// It is undefined behavior to use this without ownership or mutable\n\t/// access to the [`ThreadKey`], which should last as long as the return\n\t/// value is alive.\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\tunsafe fn raw_write(\u0026self);\n\n\t/// Attempt to lock without blocking.\n\t///\n\t/// Returns `true` if successful, `false` otherwise.\n\t///\n\t/// # Safety\n\t///\n\t/// It is undefined behavior to use this without ownership or mutable\n\t/// access to the [`ThreadKey`], which should last as long as the return\n\t/// value is alive.\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool;\n\n\t/// Releases the lock\n\t///\n\t/// # Safety\n\t///\n\t/// It is undefined behavior to use this if the lock is not acquired\n\tunsafe fn raw_unlock_write(\u0026self);\n\n\t/// Blocks until the data the lock protects can be safely read.\n\t///\n\t/// Some locks, but not all, will allow multiple readers at once. If\n\t/// multiple readers are allowed for a [`Lockable`] type, then the\n\t/// [`Sharable`] marker trait should be implemented.\n\t///\n\t/// # Safety\n\t///\n\t/// It is undefined behavior to use this without ownership or mutable\n\t/// access to the [`ThreadKey`], which should last as long as the return\n\t/// value is alive.\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\tunsafe fn raw_read(\u0026self);\n\n\t// Attempt to read without blocking.\n\t///\n\t/// Returns `true` if successful, `false` otherwise.\n\t///\n\t/// Some locks, but not all, will allow multiple readers at once. If\n\t/// multiple readers are allowed for a [`Lockable`] type, then the\n\t/// [`Sharable`] marker trait should be implemented.\n\t///\n\t/// # Safety\n\t///\n\t/// It is undefined behavior to use this without ownership or mutable\n\t/// access to the [`ThreadKey`], which should last as long as the return\n\t/// value is alive.\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool;\n\n\t/// Releases the lock after calling `read`.\n\t///\n\t/// # Safety\n\t///\n\t/// It is undefined behavior to use this if the read lock is not acquired\n\tunsafe fn raw_unlock_read(\u0026self);\n}\n\n/// A type that may be locked and unlocked.\n///\n/// This trait is usually implemented on collections of [`RawLock`]s. For\n/// example, a `Vec\u003cMutex\u003ci32\u003e\u003e`.\n///\n/// # Safety\n///\n/// Acquiring the locks returned by `get_ptrs` must allow access to the values\n/// returned by `guard`.\n///\n/// Dropping the `Guard` must unlock those same locks.\n///\n/// The order of the resulting list from `get_ptrs` must be deterministic. As\n/// long as the value is not mutated, the references must always be in the same\n/// order.\npub unsafe trait Lockable {\n\t/// The exclusive guard that does not hold a key\n\ttype Guard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\t/// Yields a list of references to the [`RawLock`]s contained within this\n\t/// value.\n\t///\n\t/// These reference locks which must be locked before acquiring a guard,\n\t/// and unlocked when the guard is dropped. The order of the resulting list\n\t/// is deterministic. As long as the value is not mutated, the references\n\t/// will always be in the same order.\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e);\n\n\t/// Returns a guard that can be used to access the underlying data mutably.\n\t///\n\t/// # Safety\n\t///\n\t/// All locks given by calling [`Lockable::get_ptrs`] must be locked\n\t/// exclusively before calling this function. The locks must not be\n\t/// unlocked until this guard is dropped.\n\t#[must_use]\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e;\n\n\t#[must_use]\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e;\n}\n\n/// Allows a lock to be accessed by multiple readers.\n///\n/// # Safety\n///\n/// Acquiring shared access to the locks returned by `get_ptrs` must allow\n/// shared access to the values returned by `read_guard`.\n///\n/// Dropping the `ReadGuard` must unlock those same locks.\npub unsafe trait Sharable: Lockable {\n\t/// The shared guard type that does not hold a key\n\ttype ReadGuard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\t/// Returns a guard that can be used to immutably access the underlying\n\t/// data.\n\t///\n\t/// # Safety\n\t///\n\t/// All locks given by calling [`Lockable::get_ptrs`] must be locked using\n\t/// [`RawLock::raw_read`] before calling this function. The locks must not be\n\t/// unlocked until this guard is dropped.\n\t#[must_use]\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e;\n\n\t#[must_use]\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e;\n}\n\n/// A type that may be locked and unlocked, and is known to be the only valid\n/// instance of the lock.\n///\n/// # Safety\n///\n/// There must not be any two values which can unlock the value at the same\n/// time, i.e., this must either be an owned value or a mutable reference.\npub unsafe trait OwnedLockable: Lockable {}\n\n/// A trait which indicates that `into_inner` is a valid operation for a\n/// [`Lockable`].\n///\n/// This is used for types like [`Poisonable`] to access the inner value of a\n/// lock. [`Poisonable::into_inner`] calls [`LockableIntoInner::into_inner`] to\n/// return a mutable reference of the inner value. This isn't implemented for\n/// some `Lockable`s, such as `\u0026[T]`.\n///\n/// [`Poisonable`]: `crate::Poisonable`\n/// [`Poisonable::into_inner`]: `crate::poisonable::Poisonable::into_inner`\npub trait LockableIntoInner: Lockable {\n\t/// The inner type that is behind the lock\n\ttype Inner;\n\n\t/// Consumes the lock, returning the underlying the lock.\n\tfn into_inner(self) -\u003e Self::Inner;\n}\n\n/// A trait which indicates that `as_mut` is a valid operation for a\n/// [`Lockable`].\n///\n/// This is used for types like [`Poisonable`] to access the inner value of a\n/// lock. [`Poisonable::get_mut`] calls [`LockableGetMut::get_mut`] to return a\n/// mutable reference of the inner value. This isn't implemented for some\n/// `Lockable`s, such as `\u0026[T]`.\n///\n/// [`Poisonable`]: `crate::Poisonable`\n/// [`Poisonable::get_mut`]: `crate::poisonable::Poisonable::get_mut`\npub trait LockableGetMut: Lockable {\n\t/// The inner type that is behind the lock\n\ttype Inner\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\t/// Returns a mutable reference to the underlying data.\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e;\n}\n\nunsafe impl\u003cT: Lockable\u003e Lockable for \u0026T {\n\ttype Guard\u003c'g\u003e\n\t\t= T::Guard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= T::DataMut\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\t(*self).get_ptrs(ptrs);\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\t(*self).guard()\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\t(*self).data_mut()\n\t}\n}\n\nunsafe impl\u003cT: Sharable\u003e Sharable for \u0026T {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= T::ReadGuard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= T::DataRef\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\t(*self).read_guard()\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\t(*self).data_ref()\n\t}\n}\n\nunsafe impl\u003cT: Lockable\u003e Lockable for \u0026mut T {\n\ttype Guard\u003c'g\u003e\n\t\t= T::Guard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= T::DataMut\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\t(**self).get_ptrs(ptrs)\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\t(**self).guard()\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\t(**self).data_mut()\n\t}\n}\n\nimpl\u003cT: LockableGetMut\u003e LockableGetMut for \u0026mut T {\n\ttype Inner\u003c'a\u003e\n\t\t= T::Inner\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\t(*self).get_mut()\n\t}\n}\n\nunsafe impl\u003cT: Sharable\u003e Sharable for \u0026mut T {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= T::ReadGuard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= T::DataRef\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\t(**self).read_guard()\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\t(**self).data_ref()\n\t}\n}\n\nunsafe impl\u003cT: OwnedLockable\u003e OwnedLockable for \u0026mut T {}\n\n/// Implements `Lockable`, `Sharable`, and `OwnedLockable` for tuples\n/// ex: `tuple_impls!(A B C, 0 1 2);`\nmacro_rules! tuple_impls {\n\t($($generic:ident)*, $($value:tt)*) =\u003e {\n\t\tunsafe impl\u003c$($generic: Lockable,)*\u003e Lockable for ($($generic,)*) {\n\t\t\ttype Guard\u003c'g\u003e = ($($generic::Guard\u003c'g\u003e,)*) where Self: 'g;\n\n\t\t\ttype DataMut\u003c'a\u003e = ($($generic::DataMut\u003c'a\u003e,)*) where Self: 'a;\n\n\t\t\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\t\t\t$(self.$value.get_ptrs(ptrs));*\n\t\t\t}\n\n\t\t\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\t\t\t// It's weird that this works\n\t\t\t\t// I don't think any other way of doing it compiles\n\t\t\t\t($(self.$value.guard(),)*)\n\t\t\t}\n\n\t\t\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\t\t\t($(self.$value.data_mut(),)*)\n\t\t\t}\n\t\t}\n\n\t\timpl\u003c$($generic: LockableGetMut,)*\u003e LockableGetMut for ($($generic,)*) {\n\t\t\ttype Inner\u003c'a\u003e = ($($generic::Inner\u003c'a\u003e,)*) where Self: 'a;\n\n\t\t\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\t\t\t($(self.$value.get_mut(),)*)\n\t\t\t}\n\t\t}\n\n\t\timpl\u003c$($generic: LockableIntoInner,)*\u003e LockableIntoInner for ($($generic,)*) {\n\t\t\ttype Inner = ($($generic::Inner,)*);\n\n\t\t\tfn into_inner(self) -\u003e Self::Inner {\n\t\t\t\t($(self.$value.into_inner(),)*)\n\t\t\t}\n\t\t}\n\n\t\tunsafe impl\u003c$($generic: Sharable,)*\u003e Sharable for ($($generic,)*) {\n\t\t\ttype ReadGuard\u003c'g\u003e = ($($generic::ReadGuard\u003c'g\u003e,)*) where Self: 'g;\n\n\t\t\ttype DataRef\u003c'a\u003e = ($($generic::DataRef\u003c'a\u003e,)*) where Self: 'a;\n\n\t\t\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\t\t\t($(self.$value.read_guard(),)*)\n\t\t\t}\n\n\t\t\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\t\t\t($(self.$value.data_ref(),)*)\n\t\t\t}\n\t\t}\n\n\t\tunsafe impl\u003c$($generic: OwnedLockable,)*\u003e OwnedLockable for ($($generic,)*) {}\n\t};\n}\n\ntuple_impls!(A, 0);\ntuple_impls!(A B, 0 1);\ntuple_impls!(A B C, 0 1 2);\ntuple_impls!(A B C D, 0 1 2 3);\ntuple_impls!(A B C D E, 0 1 2 3 4);\ntuple_impls!(A B C D E F, 0 1 2 3 4 5);\ntuple_impls!(A B C D E F G, 0 1 2 3 4 5 6);\n\nunsafe impl\u003cT: Lockable, const N: usize\u003e Lockable for [T; N] {\n\ttype Guard\u003c'g\u003e\n\t\t= [T::Guard\u003c'g\u003e; N]\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= [T::DataMut\u003c'a\u003e; N]\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tfor lock in self {\n\t\t\tlock.get_ptrs(ptrs);\n\t\t}\n\t}\n\n\tunsafe fn guard\u003c'g\u003e(\u0026'g self) -\u003e Self::Guard\u003c'g\u003e {\n\t\t// The MaybeInit helper functions for arrays aren't stable yet, so\n\t\t// we'll just have to implement it ourselves\n\t\tlet mut guards = MaybeUninit::\u003c[MaybeUninit\u003cT::Guard\u003c'g\u003e\u003e; N]\u003e::uninit().assume_init();\n\t\tfor i in 0..N {\n\t\t\tguards[i].write(self[i].guard());\n\t\t}\n\n\t\tguards.map(|g| g.assume_init())\n\t}\n\n\tunsafe fn data_mut\u003c'a\u003e(\u0026'a self) -\u003e Self::DataMut\u003c'a\u003e {\n\t\tlet mut guards = MaybeUninit::\u003c[MaybeUninit\u003cT::DataMut\u003c'a\u003e\u003e; N]\u003e::uninit().assume_init();\n\t\tfor i in 0..N {\n\t\t\tguards[i].write(self[i].data_mut());\n\t\t}\n\n\t\tguards.map(|g| g.assume_init())\n\t}\n}\n\nimpl\u003cT: LockableGetMut, const N: usize\u003e LockableGetMut for [T; N] {\n\ttype Inner\u003c'a\u003e\n\t\t= [T::Inner\u003c'a\u003e; N]\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tunsafe {\n\t\t\tlet mut guards = MaybeUninit::\u003c[MaybeUninit\u003cT::Inner\u003c'_\u003e\u003e; N]\u003e::uninit().assume_init();\n\t\t\tfor (i, lock) in self.iter_mut().enumerate() {\n\t\t\t\tguards[i].write(lock.get_mut());\n\t\t\t}\n\n\t\t\tguards.map(|g| g.assume_init())\n\t\t}\n\t}\n}\n\nimpl\u003cT: LockableIntoInner, const N: usize\u003e LockableIntoInner for [T; N] {\n\ttype Inner = [T::Inner; N];\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tunsafe {\n\t\t\tlet mut guards = MaybeUninit::\u003c[MaybeUninit\u003cT::Inner\u003e; N]\u003e::uninit().assume_init();\n\t\t\tfor (i, lock) in self.into_iter().enumerate() {\n\t\t\t\tguards[i].write(lock.into_inner());\n\t\t\t}\n\n\t\t\tguards.map(|g| g.assume_init())\n\t\t}\n\t}\n}\n\nunsafe impl\u003cT: Sharable, const N: usize\u003e Sharable for [T; N] {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= [T::ReadGuard\u003c'g\u003e; N]\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= [T::DataRef\u003c'a\u003e; N]\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard\u003c'g\u003e(\u0026'g self) -\u003e Self::ReadGuard\u003c'g\u003e {\n\t\tlet mut guards = MaybeUninit::\u003c[MaybeUninit\u003cT::ReadGuard\u003c'g\u003e\u003e; N]\u003e::uninit().assume_init();\n\t\tfor i in 0..N {\n\t\t\tguards[i].write(self[i].read_guard());\n\t\t}\n\n\t\tguards.map(|g| g.assume_init())\n\t}\n\n\tunsafe fn data_ref\u003c'a\u003e(\u0026'a self) -\u003e Self::DataRef\u003c'a\u003e {\n\t\tlet mut guards = MaybeUninit::\u003c[MaybeUninit\u003cT::DataRef\u003c'a\u003e\u003e; N]\u003e::uninit().assume_init();\n\t\tfor i in 0..N {\n\t\t\tguards[i].write(self[i].data_ref());\n\t\t}\n\n\t\tguards.map(|g| g.assume_init())\n\t}\n}\n\nunsafe impl\u003cT: OwnedLockable, const N: usize\u003e OwnedLockable for [T; N] {}\n\nunsafe impl\u003cT: Lockable\u003e Lockable for Box\u003c[T]\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= Box\u003c[T::Guard\u003c'g\u003e]\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= Box\u003c[T::DataMut\u003c'a\u003e]\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tfor lock in self {\n\t\t\tlock.get_ptrs(ptrs);\n\t\t}\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tself.iter().map(|lock| lock.guard()).collect()\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.iter().map(|lock| lock.data_mut()).collect()\n\t}\n}\n\nimpl\u003cT: LockableGetMut + 'static\u003e LockableGetMut for Box\u003c[T]\u003e {\n\ttype Inner\u003c'a\u003e\n\t\t= Box\u003c[T::Inner\u003c'a\u003e]\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tself.iter_mut().map(LockableGetMut::get_mut).collect()\n\t}\n}\n\nunsafe impl\u003cT: Sharable\u003e Sharable for Box\u003c[T]\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= Box\u003c[T::ReadGuard\u003c'g\u003e]\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= Box\u003c[T::DataRef\u003c'a\u003e]\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tself.iter().map(|lock| lock.read_guard()).collect()\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tself.iter().map(|lock| lock.data_ref()).collect()\n\t}\n}\n\nunsafe impl\u003cT: Sharable\u003e Sharable for Vec\u003cT\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= Box\u003c[T::ReadGuard\u003c'g\u003e]\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= Box\u003c[T::DataRef\u003c'a\u003e]\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tself.iter().map(|lock| lock.read_guard()).collect()\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tself.iter().map(|lock| lock.data_ref()).collect()\n\t}\n}\n\nunsafe impl\u003cT: OwnedLockable\u003e OwnedLockable for Box\u003c[T]\u003e {}\n\nunsafe impl\u003cT: Lockable\u003e Lockable for Vec\u003cT\u003e {\n\t// There's no reason why I'd ever want to extend a list of lock guards\n\ttype Guard\u003c'g\u003e\n\t\t= Box\u003c[T::Guard\u003c'g\u003e]\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= Box\u003c[T::DataMut\u003c'a\u003e]\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tfor lock in self {\n\t\t\tlock.get_ptrs(ptrs);\n\t\t}\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tself.iter().map(|lock| lock.guard()).collect()\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.iter().map(|lock| lock.data_mut()).collect()\n\t}\n}\n\n// I'd make a generic impl\u003cT: Lockable, I: IntoIterator\u003cItem=T\u003e\u003e Lockable for I\n// but I think that'd require sealing up this trait\n\n// TODO: using edition 2024, impl LockableIntoInner for Box\u003c[T]\u003e\n\nimpl\u003cT: LockableGetMut + 'static\u003e LockableGetMut for Vec\u003cT\u003e {\n\ttype Inner\u003c'a\u003e\n\t\t= Box\u003c[T::Inner\u003c'a\u003e]\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tself.iter_mut().map(LockableGetMut::get_mut).collect()\n\t}\n}\n\nimpl\u003cT: LockableIntoInner\u003e LockableIntoInner for Vec\u003cT\u003e {\n\ttype Inner = Box\u003c[T::Inner]\u003e;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tself.into_iter()\n\t\t\t.map(LockableIntoInner::into_inner)\n\t\t\t.collect()\n\t}\n}\n\nunsafe impl\u003cT: OwnedLockable\u003e OwnedLockable for Vec\u003cT\u003e {}\n\n#[cfg(test)]\nmod tests {\n\tuse super::*;\n\tuse crate::{LockCollection, Mutex, RwLock, ThreadKey};\n\n\t#[test]\n\tfn mut_ref_get_ptrs() {\n\t\tlet mut rwlock = RwLock::new(5);\n\t\tlet mutref = \u0026mut rwlock;\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tmutref.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tassert!(std::ptr::addr_eq(lock_ptrs[0], mutref));\n\t}\n\n\t#[test]\n\tfn array_get_ptrs_empty() {\n\t\tlet locks: [Mutex\u003c()\u003e; 0] = [];\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert!(lock_ptrs.is_empty());\n\t}\n\n\t#[test]\n\tfn array_get_ptrs_length_one() {\n\t\tlet locks: [Mutex\u003ci32\u003e; 1] = [Mutex::new(1)];\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[0], locks[0].raw())) }\n\t}\n\n\t#[test]\n\tfn array_get_ptrs_length_two() {\n\t\tlet locks: [Mutex\u003ci32\u003e; 2] = [Mutex::new(1), Mutex::new(2)];\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 2);\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[0], locks[0].raw())) }\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[1], locks[1].raw())) }\n\t}\n\n\t#[test]\n\tfn vec_get_ptrs_empty() {\n\t\tlet locks: Vec\u003cMutex\u003c()\u003e\u003e = Vec::new();\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert!(lock_ptrs.is_empty());\n\t}\n\n\t#[test]\n\tfn vec_get_ptrs_length_one() {\n\t\tlet locks: Vec\u003cMutex\u003ci32\u003e\u003e = vec![Mutex::new(1)];\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[0], locks[0].raw())) }\n\t}\n\n\t#[test]\n\tfn vec_get_ptrs_length_two() {\n\t\tlet locks: Vec\u003cMutex\u003ci32\u003e\u003e = vec![Mutex::new(1), Mutex::new(2)];\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 2);\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[0], locks[0].raw())) }\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[1], locks[1].raw())) }\n\t}\n\n\t#[test]\n\tfn vec_as_mut() {\n\t\tlet mut locks: Vec\u003cMutex\u003ci32\u003e\u003e = vec![Mutex::new(1), Mutex::new(2)];\n\t\tlet lock_ptrs = LockableGetMut::get_mut(\u0026mut locks);\n\n\t\tassert_eq!(lock_ptrs.len(), 2);\n\t\tassert_eq!(*lock_ptrs[0], 1);\n\t\tassert_eq!(*lock_ptrs[1], 2);\n\t}\n\n\t#[test]\n\tfn vec_into_inner() {\n\t\tlet locks: Vec\u003cMutex\u003ci32\u003e\u003e = vec![Mutex::new(1), Mutex::new(2)];\n\t\tlet lock_ptrs = LockableIntoInner::into_inner(locks);\n\n\t\tassert_eq!(lock_ptrs.len(), 2);\n\t\tassert_eq!(lock_ptrs[0], 1);\n\t\tassert_eq!(lock_ptrs[1], 2);\n\t}\n\n\t#[test]\n\tfn vec_guard_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = vec![RwLock::new(1), RwLock::new(2)];\n\t\tlet collection = LockCollection::new(locks);\n\n\t\tlet mut guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], 1);\n\t\tassert_eq!(*guard[1], 2);\n\t\t*guard[0] = 3;\n\n\t\tlet key = LockCollection::\u003cVec\u003cRwLock\u003c_\u003e\u003e\u003e::unlock(guard);\n\t\tlet guard = collection.read(key);\n\t\tassert_eq!(*guard[0], 3);\n\t\tassert_eq!(*guard[1], 2);\n\t}\n\n\t#[test]\n\tfn vec_data_mut() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = vec![Mutex::new(1), Mutex::new(2)];\n\t\tlet collection = LockCollection::new(mutexes);\n\t\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 1);\n\t\t\tassert_eq!(*guard[1], 2);\n\t\t\t*guard[0] = 3;\n\t\t});\n\n\t\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 3);\n\t\t\tassert_eq!(*guard[1], 2);\n\t\t})\n\t}\n\n\t#[test]\n\tfn vec_data_ref() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = vec![RwLock::new(1), RwLock::new(2)];\n\t\tlet collection = LockCollection::new(mutexes);\n\t\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 1);\n\t\t\tassert_eq!(*guard[1], 2);\n\t\t\t*guard[0] = 3;\n\t\t});\n\n\t\tcollection.scoped_read(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 3);\n\t\t\tassert_eq!(*guard[1], 2);\n\t\t})\n\t}\n\n\t#[test]\n\tfn box_get_ptrs_empty() {\n\t\tlet locks: Box\u003c[Mutex\u003c()\u003e]\u003e = Box::from([]);\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert!(lock_ptrs.is_empty());\n\t}\n\n\t#[test]\n\tfn box_get_ptrs_length_one() {\n\t\tlet locks: Box\u003c[Mutex\u003ci32\u003e]\u003e = vec![Mutex::new(1)].into_boxed_slice();\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[0], locks[0].raw())) }\n\t}\n\n\t#[test]\n\tfn box_get_ptrs_length_two() {\n\t\tlet locks: Box\u003c[Mutex\u003ci32\u003e]\u003e = vec![Mutex::new(1), Mutex::new(2)].into_boxed_slice();\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 2);\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[0], locks[0].raw())) }\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[1], locks[1].raw())) }\n\t}\n\n\t#[test]\n\tfn box_as_mut() {\n\t\tlet mut locks: Box\u003c[Mutex\u003ci32\u003e]\u003e = vec![Mutex::new(1), Mutex::new(2)].into_boxed_slice();\n\t\tlet lock_ptrs = LockableGetMut::get_mut(\u0026mut locks);\n\n\t\tassert_eq!(lock_ptrs.len(), 2);\n\t\tassert_eq!(*lock_ptrs[0], 1);\n\t\tassert_eq!(*lock_ptrs[1], 2);\n\t}\n\n\t#[test]\n\tfn box_guard_mut() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet x = [Mutex::new(1), Mutex::new(2)];\n\t\tlet collection: LockCollection\u003cBox\u003c[Mutex\u003c_\u003e]\u003e\u003e = LockCollection::new(Box::new(x));\n\n\t\tlet mut guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], 1);\n\t\tassert_eq!(*guard[1], 2);\n\t\t*guard[0] = 3;\n\n\t\tlet key = LockCollection::\u003cBox\u003c[Mutex\u003c_\u003e]\u003e\u003e::unlock(guard);\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], 3);\n\t\tassert_eq!(*guard[1], 2);\n\t}\n\n\t#[test]\n\tfn box_data_mut() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = vec![Mutex::new(1), Mutex::new(2)].into_boxed_slice();\n\t\tlet collection = LockCollection::new(mutexes);\n\t\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 1);\n\t\t\tassert_eq!(*guard[1], 2);\n\t\t\t*guard[0] = 3;\n\t\t});\n\n\t\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 3);\n\t\t\tassert_eq!(*guard[1], 2);\n\t\t});\n\t}\n\n\t#[test]\n\tfn box_guard_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = [RwLock::new(1), RwLock::new(2)];\n\t\tlet collection: LockCollection\u003cBox\u003c[RwLock\u003c_\u003e]\u003e\u003e = LockCollection::new(Box::new(locks));\n\n\t\tlet mut guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], 1);\n\t\tassert_eq!(*guard[1], 2);\n\t\t*guard[0] = 3;\n\n\t\tlet key = LockCollection::\u003cBox\u003c[RwLock\u003c_\u003e]\u003e\u003e::unlock(guard);\n\t\tlet guard = collection.read(key);\n\t\tassert_eq!(*guard[0], 3);\n\t\tassert_eq!(*guard[1], 2);\n\t}\n\n\t#[test]\n\tfn box_data_ref() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = vec![RwLock::new(1), RwLock::new(2)].into_boxed_slice();\n\t\tlet collection = LockCollection::new(mutexes);\n\t\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 1);\n\t\t\tassert_eq!(*guard[1], 2);\n\t\t\t*guard[0] = 3;\n\t\t});\n\n\t\tcollection.scoped_read(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 3);\n\t\t\tassert_eq!(*guard[1], 2);\n\t\t});\n\t}\n}\n","traces":[{"line":232,"address":[215504,215472,215440],"length":1,"stats":{"Line":43}},{"line":233,"address":[247342,247310,247406,247374],"length":1,"stats":{"Line":45}},{"line":236,"address":[962544],"length":1,"stats":{"Line":8}},{"line":237,"address":[174309,174325],"length":1,"stats":{"Line":9}},{"line":240,"address":[216880],"length":1,"stats":{"Line":2}},{"line":241,"address":[],"length":0,"stats":{"Line":2}},{"line":256,"address":[247456,247424,247440],"length":1,"stats":{"Line":3}},{"line":257,"address":[962629,962565,962593],"length":1,"stats":{"Line":3}},{"line":260,"address":[],"length":0,"stats":{"Line":0}},{"line":261,"address":[],"length":0,"stats":{"Line":0}},{"line":276,"address":[],"length":0,"stats":{"Line":1}},{"line":277,"address":[],"length":0,"stats":{"Line":1}},{"line":280,"address":[],"length":0,"stats":{"Line":0}},{"line":281,"address":[],"length":0,"stats":{"Line":0}},{"line":284,"address":[],"length":0,"stats":{"Line":0}},{"line":285,"address":[],"length":0,"stats":{"Line":0}},{"line":295,"address":[],"length":0,"stats":{"Line":0}},{"line":296,"address":[],"length":0,"stats":{"Line":0}},{"line":311,"address":[],"length":0,"stats":{"Line":0}},{"line":312,"address":[],"length":0,"stats":{"Line":0}},{"line":315,"address":[],"length":0,"stats":{"Line":0}},{"line":316,"address":[],"length":0,"stats":{"Line":0}},{"line":331,"address":[142016],"length":1,"stats":{"Line":14}},{"line":332,"address":[215592],"length":1,"stats":{"Line":18}},{"line":335,"address":[163071,162880],"length":1,"stats":{"Line":7}},{"line":338,"address":[971838,971726],"length":1,"stats":{"Line":8}},{"line":357,"address":[972272,972619],"length":1,"stats":{"Line":4}},{"line":358,"address":[1293020,1292598,1292934,1293254,1293372,1292703],"length":1,"stats":{"Line":8}},{"line":367,"address":[972048,971936,972024,972136,972160,972248],"length":1,"stats":{"Line":2}},{"line":368,"address":[247584],"length":1,"stats":{"Line":2}},{"line":399,"address":[1282288,1282736,1282960,1282400,1282176,1282624,1282512,1282848],"length":1,"stats":{"Line":18}},{"line":400,"address":[217906,217794,217859,217971],"length":1,"stats":{"Line":34}},{"line":401,"address":[1282605,1283053,1282829,1282269,1282493,1282717,1282381,1282941],"length":1,"stats":{"Line":16}},{"line":405,"address":[1283072,1283440,1283808,1284672,1284256],"length":1,"stats":{"Line":8}},{"line":408,"address":[],"length":0,"stats":{"Line":2}},{"line":409,"address":[963090,963458,963560,963820,963192,963758],"length":1,"stats":{"Line":17}},{"line":410,"address":[1283642,1284874,1283274,1284506,1283404,1283772,1284214,1285004,1284636,1284084],"length":1,"stats":{"Line":16}},{"line":413,"address":[],"length":0,"stats":{"Line":25}},{"line":416,"address":[1285040],"length":1,"stats":{"Line":3}},{"line":417,"address":[],"length":0,"stats":{"Line":0}},{"line":418,"address":[964066,964168],"length":1,"stats":{"Line":6}},{"line":419,"address":[1285242,1285372],"length":1,"stats":{"Line":6}},{"line":422,"address":[189408,189431,189440,189463],"length":1,"stats":{"Line":9}},{"line":432,"address":[964352],"length":1,"stats":{"Line":2}},{"line":434,"address":[],"length":0,"stats":{"Line":0}},{"line":435,"address":[1285498,1285690],"length":1,"stats":{"Line":4}},{"line":436,"address":[964676,964762],"length":1,"stats":{"Line":4}},{"line":439,"address":[1285638],"length":1,"stats":{"Line":6}},{"line":447,"address":[],"length":0,"stats":{"Line":1}},{"line":449,"address":[1285878],"length":1,"stats":{"Line":1}},{"line":450,"address":[],"length":0,"stats":{"Line":4}},{"line":451,"address":[1286350,1286288],"length":1,"stats":{"Line":2}},{"line":454,"address":[1286304],"length":1,"stats":{"Line":3}},{"line":470,"address":[],"length":0,"stats":{"Line":4}},{"line":471,"address":[],"length":0,"stats":{"Line":0}},{"line":472,"address":[965490,965592,965122,964814,965224,964876],"length":1,"stats":{"Line":7}},{"line":473,"address":[],"length":0,"stats":{"Line":6}},{"line":476,"address":[606199,606144,606167,606112,606135,606176],"length":1,"stats":{"Line":10}},{"line":479,"address":[],"length":0,"stats":{"Line":1}},{"line":480,"address":[],"length":0,"stats":{"Line":0}},{"line":481,"address":[965960,965858],"length":1,"stats":{"Line":2}},{"line":482,"address":[965978,966108],"length":1,"stats":{"Line":3}},{"line":485,"address":[965917],"length":1,"stats":{"Line":3}},{"line":502,"address":[],"length":0,"stats":{"Line":3}},{"line":503,"address":[],"length":0,"stats":{"Line":5}},{"line":504,"address":[994941,995053,995165],"length":1,"stats":{"Line":2}},{"line":508,"address":[],"length":0,"stats":{"Line":2}},{"line":509,"address":[995240,995192],"length":1,"stats":{"Line":6}},{"line":512,"address":[],"length":0,"stats":{"Line":2}},{"line":513,"address":[],"length":0,"stats":{"Line":6}},{"line":523,"address":[995376],"length":1,"stats":{"Line":1}},{"line":524,"address":[],"length":0,"stats":{"Line":1}},{"line":539,"address":[],"length":0,"stats":{"Line":1}},{"line":540,"address":[],"length":0,"stats":{"Line":3}},{"line":543,"address":[995472],"length":1,"stats":{"Line":1}},{"line":544,"address":[],"length":0,"stats":{"Line":3}},{"line":559,"address":[],"length":0,"stats":{"Line":1}},{"line":560,"address":[532597],"length":1,"stats":{"Line":3}},{"line":563,"address":[],"length":0,"stats":{"Line":1}},{"line":564,"address":[532645],"length":1,"stats":{"Line":3}},{"line":582,"address":[],"length":0,"stats":{"Line":5}},{"line":583,"address":[],"length":0,"stats":{"Line":11}},{"line":584,"address":[],"length":0,"stats":{"Line":4}},{"line":588,"address":[],"length":0,"stats":{"Line":2}},{"line":589,"address":[533189,533141],"length":1,"stats":{"Line":6}},{"line":592,"address":[],"length":0,"stats":{"Line":2}},{"line":593,"address":[],"length":0,"stats":{"Line":6}},{"line":608,"address":[],"length":0,"stats":{"Line":2}},{"line":609,"address":[],"length":0,"stats":{"Line":2}},{"line":616,"address":[],"length":0,"stats":{"Line":1}},{"line":617,"address":[],"length":0,"stats":{"Line":1}},{"line":618,"address":[],"length":0,"stats":{"Line":0}}],"covered":75,"coverable":92},{"path":["/","home","botahamec","Projects","happylock","src","mutex","guard.rs"],"content":"use std::fmt::{Debug, Display};\nuse std::hash::Hash;\nuse std::marker::PhantomData;\nuse std::ops::{Deref, DerefMut};\n\nuse lock_api::RawMutex;\n\nuse crate::lockable::RawLock;\nuse crate::ThreadKey;\n\nuse super::{Mutex, MutexGuard, MutexRef};\n\n// These impls make things slightly easier because now you can use\n// `println!(\"{guard}\")` instead of `println!(\"{}\", *guard)`\n\n#[mutants::skip] // hashing involves RNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Hash + ?Sized, R: RawMutex\u003e Hash for MutexRef\u003c'_, T, R\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.deref().hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug + ?Sized, R: RawMutex\u003e Debug for MutexRef\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: Display + ?Sized, R: RawMutex\u003e Display for MutexRef\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e Drop for MutexRef\u003c'_, T, R\u003e {\n\tfn drop(\u0026mut self) {\n\t\t// safety: this guard is being destroyed, so the data cannot be\n\t\t// accessed without locking again\n\t\tunsafe { self.0.raw_unlock_write() }\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e Deref for MutexRef\u003c'_, T, R\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t// safety: this is the only type that can use `value`, and there's\n\t\t// a reference to this type, so there cannot be any mutable\n\t\t// references to this value.\n\t\tunsafe { \u0026*self.0.data.get() }\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e DerefMut for MutexRef\u003c'_, T, R\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t// safety: this is the only type that can use `value`, and we have a\n\t\t// mutable reference to this type, so there cannot be any other\n\t\t// references to this value.\n\t\tunsafe { \u0026mut *self.0.data.get() }\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e AsRef\u003cT\u003e for MutexRef\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e AsMut\u003cT\u003e for MutexRef\u003c'_, T, R\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself\n\t}\n}\n\nimpl\u003c'a, T: ?Sized, R: RawMutex\u003e MutexRef\u003c'a, T, R\u003e {\n\t/// Creates a reference to the underlying data of a mutex without\n\t/// attempting to lock it or take ownership of the key. But it's also quite\n\t/// dangerous to drop.\n\tpub(crate) const unsafe fn new(mutex: \u0026'a Mutex\u003cT, R\u003e) -\u003e Self {\n\t\tSelf(mutex, PhantomData)\n\t}\n}\n\n// it's kinda annoying to re-implement some of this stuff on guards\n// there's nothing i can do about that\n\n#[mutants::skip] // hashing involves RNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Hash + ?Sized, R: RawMutex\u003e Hash for MutexGuard\u003c'_, T, R\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.deref().hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug + ?Sized, R: RawMutex\u003e Debug for MutexGuard\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: Display + ?Sized, R: RawMutex\u003e Display for MutexGuard\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e Deref for MutexGuard\u003c'_, T, R\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t\u0026self.mutex\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e DerefMut for MutexGuard\u003c'_, T, R\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t\u0026mut self.mutex\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e AsRef\u003cT\u003e for MutexGuard\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e AsMut\u003cT\u003e for MutexGuard\u003c'_, T, R\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself\n\t}\n}\n\nimpl\u003c'a, T: ?Sized, R: RawMutex\u003e MutexGuard\u003c'a, T, R\u003e {\n\t/// Create a guard to the given mutex. Undefined if multiple guards to the\n\t/// same mutex exist at once.\n\t#[must_use]\n\tpub(super) const unsafe fn new(mutex: \u0026'a Mutex\u003cT, R\u003e, thread_key: ThreadKey) -\u003e Self {\n\t\tSelf {\n\t\t\tmutex: MutexRef(mutex, PhantomData),\n\t\t\tthread_key,\n\t\t}\n\t}\n}\n\nunsafe impl\u003cT: ?Sized + Sync, R: RawMutex + Sync\u003e Sync for MutexRef\u003c'_, T, R\u003e {}\n","traces":[{"line":33,"address":[1286624],"length":1,"stats":{"Line":1}},{"line":34,"address":[],"length":0,"stats":{"Line":1}},{"line":39,"address":[153792],"length":1,"stats":{"Line":6}},{"line":42,"address":[153797],"length":1,"stats":{"Line":7}},{"line":49,"address":[1286704,1286672],"length":1,"stats":{"Line":2}},{"line":53,"address":[163269],"length":1,"stats":{"Line":3}},{"line":58,"address":[1286736,1286768],"length":1,"stats":{"Line":3}},{"line":62,"address":[1286741,1286773],"length":1,"stats":{"Line":4}},{"line":67,"address":[],"length":0,"stats":{"Line":2}},{"line":68,"address":[],"length":0,"stats":{"Line":2}},{"line":73,"address":[],"length":0,"stats":{"Line":1}},{"line":74,"address":[],"length":0,"stats":{"Line":1}},{"line":82,"address":[142112,142096],"length":1,"stats":{"Line":6}},{"line":83,"address":[],"length":0,"stats":{"Line":0}},{"line":107,"address":[],"length":0,"stats":{"Line":1}},{"line":108,"address":[],"length":0,"stats":{"Line":1}},{"line":115,"address":[],"length":0,"stats":{"Line":2}},{"line":116,"address":[],"length":0,"stats":{"Line":3}},{"line":121,"address":[218048],"length":1,"stats":{"Line":2}},{"line":122,"address":[218053],"length":1,"stats":{"Line":2}},{"line":127,"address":[1286976],"length":1,"stats":{"Line":1}},{"line":128,"address":[],"length":0,"stats":{"Line":1}},{"line":133,"address":[],"length":0,"stats":{"Line":1}},{"line":134,"address":[],"length":0,"stats":{"Line":1}},{"line":142,"address":[140544],"length":1,"stats":{"Line":4}},{"line":144,"address":[],"length":0,"stats":{"Line":0}}],"covered":24,"coverable":26},{"path":["/","home","botahamec","Projects","happylock","src","mutex","mutex.rs"],"content":"use std::cell::UnsafeCell;\nuse std::fmt::Debug;\nuse std::marker::PhantomData;\nuse std::panic::AssertUnwindSafe;\n\nuse lock_api::RawMutex;\n\nuse crate::collection::utils;\nuse crate::handle_unwind::handle_unwind;\nuse crate::lockable::{Lockable, LockableGetMut, LockableIntoInner, OwnedLockable, RawLock};\nuse crate::poisonable::PoisonFlag;\nuse crate::{Keyable, ThreadKey};\n\nuse super::{Mutex, MutexGuard, MutexRef};\n\nunsafe impl\u003cT: ?Sized, R: RawMutex\u003e RawLock for Mutex\u003cT, R\u003e {\n\tfn poison(\u0026self) {\n\t\tself.poison.poison();\n\t}\n\n\tunsafe fn raw_write(\u0026self) {\n\t\tassert!(!self.poison.is_poisoned(), \"The mutex has been killed\");\n\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.lock(), || self.poison())\n\t}\n\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool {\n\t\tif self.poison.is_poisoned() {\n\t\t\treturn false;\n\t\t}\n\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.try_lock(), || self.poison())\n\t}\n\n\tunsafe fn raw_unlock_write(\u0026self) {\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.unlock(), || self.poison())\n\t}\n\n\t// this is the closest thing to a read we can get, but Sharable isn't\n\t// implemented for this\n\t#[mutants::skip]\n\t#[cfg(not(tarpaulin_include))]\n\tunsafe fn raw_read(\u0026self) {\n\t\tself.raw_write()\n\t}\n\n\t#[mutants::skip]\n\t#[cfg(not(tarpaulin_include))]\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tself.raw_try_write()\n\t}\n\n\t#[mutants::skip]\n\t#[cfg(not(tarpaulin_include))]\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tself.raw_unlock_write()\n\t}\n}\n\nunsafe impl\u003cT, R: RawMutex\u003e Lockable for Mutex\u003cT, R\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= MutexRef\u003c'g, T, R\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= \u0026'a mut T\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tptrs.push(self);\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tMutexRef::new(self)\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.data.get().as_mut().unwrap_unchecked()\n\t}\n}\n\nimpl\u003cT: Send, R: RawMutex\u003e LockableIntoInner for Mutex\u003cT, R\u003e {\n\ttype Inner = T;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tself.into_inner()\n\t}\n}\n\nimpl\u003cT: Send, R: RawMutex\u003e LockableGetMut for Mutex\u003cT, R\u003e {\n\ttype Inner\u003c'a\u003e\n\t\t= \u0026'a mut T\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tself.get_mut()\n\t}\n}\n\nunsafe impl\u003cT: Send, R: RawMutex\u003e OwnedLockable for Mutex\u003cT, R\u003e {}\n\nimpl\u003cT, R: RawMutex\u003e Mutex\u003cT, R\u003e {\n\t/// Create a new unlocked `Mutex`.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t///\n\t/// let mutex = Mutex::new(0);\n\t/// ```\n\t#[must_use]\n\tpub const fn new(data: T) -\u003e Self {\n\t\tSelf {\n\t\t\traw: R::INIT,\n\t\t\tpoison: PoisonFlag::new(),\n\t\t\tdata: UnsafeCell::new(data),\n\t\t}\n\t}\n\n\t/// Returns the raw underlying mutex.\n\t///\n\t/// Note that you will most likely need to import the [`RawMutex`] trait\n\t/// from `lock_api` to be able to call functions on the raw mutex.\n\t///\n\t/// # Safety\n\t///\n\t/// This method is unsafe because it allows unlocking a mutex while still\n\t/// holding a reference to a [`MutexGuard`], and locking a mutex without\n\t/// holding the [`ThreadKey`].\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\t#[must_use]\n\tpub const unsafe fn raw(\u0026self) -\u003e \u0026R {\n\t\t\u0026self.raw\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug, R: RawMutex\u003e Debug for Mutex\u003cT, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t// safety: this is just a try lock, and the value is dropped\n\t\t// immediately after, so there's no risk of blocking ourselves\n\t\t// or any other threads\n\t\t// when i implement try_clone this code will become less unsafe\n\t\tif let Some(value) = unsafe { self.try_lock_no_key() } {\n\t\t\tf.debug_struct(\"Mutex\").field(\"data\", \u0026\u0026*value).finish()\n\t\t} else {\n\t\t\tstruct LockedPlaceholder;\n\t\t\timpl Debug for LockedPlaceholder {\n\t\t\t\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t\t\t\tf.write_str(\"\u003clocked\u003e\")\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tf.debug_struct(\"Mutex\")\n\t\t\t\t.field(\"data\", \u0026LockedPlaceholder)\n\t\t\t\t.finish()\n\t\t}\n\t}\n}\n\nimpl\u003cT: Default, R: RawMutex\u003e Default for Mutex\u003cT, R\u003e {\n\tfn default() -\u003e Self {\n\t\tSelf::new(T::default())\n\t}\n}\n\nimpl\u003cT, R: RawMutex\u003e From\u003cT\u003e for Mutex\u003cT, R\u003e {\n\tfn from(value: T) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\n// We don't need a `get_mut` because we don't have mutex poisoning. Hurray!\n// We have it anyway for documentation\nimpl\u003cT: ?Sized, R\u003e AsMut\u003cT\u003e for Mutex\u003cT, R\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself.get_mut()\n\t}\n}\n\nimpl\u003cT, R\u003e Mutex\u003cT, R\u003e {\n\t/// Consumes this mutex, returning the underlying data.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t///\n\t/// let mutex = Mutex::new(0);\n\t/// assert_eq!(mutex.into_inner(), 0);\n\t/// ```\n\t#[must_use]\n\tpub fn into_inner(self) -\u003e T {\n\t\tself.data.into_inner()\n\t}\n}\n\nimpl\u003cT: ?Sized, R\u003e Mutex\u003cT, R\u003e {\n\t/// Returns a mutable reference to the underlying data.\n\t///\n\t/// Since this call borrows `Mutex` mutably, no actual locking is taking\n\t/// place. The mutable borrow statically guarantees that no locks exist.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, Mutex};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut mutex = Mutex::new(0);\n\t/// *mutex.get_mut() = 10;\n\t/// assert_eq!(*mutex.lock(key), 10);\n\t/// ```\n\t#[must_use]\n\tpub fn get_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself.data.get_mut()\n\t}\n}\n\nimpl\u003cT, R: RawMutex\u003e Mutex\u003cT, R\u003e {\n\tpub fn scoped_lock\u003c'a, Ret\u003e(\n\t\t\u0026'a self,\n\t\tkey: impl Keyable,\n\t\tf: impl FnOnce(\u0026'a mut T) -\u003e Ret,\n\t) -\u003e Ret {\n\t\tutils::scoped_write(self, key, f)\n\t}\n\n\tpub fn scoped_try_lock\u003c'a, Key: Keyable, Ret\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl FnOnce(\u0026'a mut T) -\u003e Ret,\n\t) -\u003e Result\u003cRet, Key\u003e {\n\t\tutils::scoped_try_write(self, key, f)\n\t}\n\n\t/// Block the thread until this mutex can be locked, and lock it.\n\t///\n\t/// Upon returning, the thread is the only thread with a lock on the\n\t/// `Mutex`. A [`MutexGuard`] is returned to allow a scoped unlock of this\n\t/// `Mutex`. When the guard is dropped, this `Mutex` will unlock.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::{thread, sync::Arc};\n\t/// use happylock::{Mutex, ThreadKey};\n\t///\n\t/// let mutex = Arc::new(Mutex::new(0));\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t///\n\t/// thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// *c_mutex.lock(key) = 10;\n\t/// }).join().expect(\"thread::spawn failed\");\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// assert_eq!(*mutex.lock(key), 10);\n\t/// ```\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e MutexGuard\u003c'_, T, R\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_write();\n\n\t\t\t// safety: we just locked the mutex\n\t\t\tMutexGuard::new(self, key)\n\t\t}\n\t}\n\n\t/// Attempts to lock the `Mutex` without blocking.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// lock when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If the mutex could not be acquired because it is already locked, then\n\t/// this call will return an error containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::{thread, sync::Arc};\n\t/// use happylock::{Mutex, ThreadKey};\n\t///\n\t/// let mutex = Arc::new(Mutex::new(0));\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t///\n\t/// thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut lock = c_mutex.try_lock(key);\n\t/// if let Ok(mut lock) = lock {\n\t/// *lock = 10;\n\t/// } else {\n\t/// println!(\"try_lock failed\");\n\t/// }\n\t/// }).join().expect(\"thread::spawn failed\");\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// assert_eq!(*mutex.lock(key), 10);\n\t/// ```\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e Result\u003cMutexGuard\u003c'_, T, R\u003e, ThreadKey\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the key to the mutex\n\t\t\tif self.raw_try_write() {\n\t\t\t\t// safety: we just locked the mutex\n\t\t\t\tOk(MutexGuard::new(self, key))\n\t\t\t} else {\n\t\t\t\tErr(key)\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Returns `true` if the mutex is currently locked\n\t#[cfg(test)]\n\tpub(crate) fn is_locked(\u0026self) -\u003e bool {\n\t\tself.raw.is_locked()\n\t}\n\n\t/// Lock without a [`ThreadKey`]. It is undefined behavior to do this without\n\t/// owning the [`ThreadKey`].\n\tpub(crate) unsafe fn try_lock_no_key(\u0026self) -\u003e Option\u003cMutexRef\u003c'_, T, R\u003e\u003e {\n\t\tself.raw_try_write().then_some(MutexRef(self, PhantomData))\n\t}\n\n\t/// Consumes the [`MutexGuard`], and consequently unlocks its `Mutex`.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, Mutex};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mutex = Mutex::new(0);\n\t///\n\t/// let mut guard = mutex.lock(key);\n\t/// *guard += 20;\n\t///\n\t/// let key = Mutex::unlock(guard);\n\t/// ```\n\t#[must_use]\n\tpub fn unlock(guard: MutexGuard\u003c'_, T, R\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.mutex);\n\t\tguard.thread_key\n\t}\n}\n\nunsafe impl\u003cR: RawMutex + Send, T: ?Sized + Send\u003e Send for Mutex\u003cT, R\u003e {}\nunsafe impl\u003cR: RawMutex + Sync, T: ?Sized + Send\u003e Sync for Mutex\u003cT, R\u003e {}\n","traces":[{"line":17,"address":[164096,164064],"length":1,"stats":{"Line":6}},{"line":18,"address":[229573,229541,229525],"length":1,"stats":{"Line":6}},{"line":21,"address":[218384],"length":1,"stats":{"Line":11}},{"line":22,"address":[],"length":0,"stats":{"Line":9}},{"line":25,"address":[218417],"length":1,"stats":{"Line":11}},{"line":26,"address":[141078],"length":1,"stats":{"Line":39}},{"line":29,"address":[163888,163808],"length":1,"stats":{"Line":11}},{"line":30,"address":[163822,163902],"length":1,"stats":{"Line":11}},{"line":31,"address":[229188,229272,229352],"length":1,"stats":{"Line":6}},{"line":35,"address":[218241],"length":1,"stats":{"Line":11}},{"line":36,"address":[163926,163846],"length":1,"stats":{"Line":37}},{"line":39,"address":[218304],"length":1,"stats":{"Line":13}},{"line":41,"address":[140988],"length":1,"stats":{"Line":13}},{"line":42,"address":[991824,991760,991888,992000,991792,991797,991920,991925,991957,992016,992032,991989,991973,992021,991861,991952,991856,991893,992037,991765,991984,992005,991829,991968],"length":1,"stats":{"Line":46}},{"line":77,"address":[143344,143408],"length":1,"stats":{"Line":19}},{"line":78,"address":[1288697,1288569,1288505,1288761,1288825,1288633],"length":1,"stats":{"Line":21}},{"line":81,"address":[143216,143232],"length":1,"stats":{"Line":6}},{"line":82,"address":[143237,143221],"length":1,"stats":{"Line":6}},{"line":85,"address":[141152],"length":1,"stats":{"Line":8}},{"line":86,"address":[141161],"length":1,"stats":{"Line":8}},{"line":93,"address":[1289008,1289088,1288992,1289024,1289072,1289040],"length":1,"stats":{"Line":6}},{"line":94,"address":[],"length":0,"stats":{"Line":6}},{"line":104,"address":[1289168,1289152,1289136],"length":1,"stats":{"Line":3}},{"line":105,"address":[1289157,1289173,1289141],"length":1,"stats":{"Line":3}},{"line":122,"address":[1289958,1290000,1289744,1289376,1289536,1290126,1289726,1289520,1289980,1289356,1290144,1289184,1290290],"length":1,"stats":{"Line":22}},{"line":125,"address":[163659,163707,163515,163563],"length":1,"stats":{"Line":44}},{"line":126,"address":[1290241,1289470,1290083,1289651,1289842,1289289],"length":1,"stats":{"Line":22}},{"line":143,"address":[1290304],"length":1,"stats":{"Line":1}},{"line":144,"address":[],"length":0,"stats":{"Line":0}},{"line":174,"address":[],"length":0,"stats":{"Line":5}},{"line":175,"address":[1290382,1290472,1290504,1290420,1290333],"length":1,"stats":{"Line":5}},{"line":180,"address":[],"length":0,"stats":{"Line":2}},{"line":181,"address":[],"length":0,"stats":{"Line":2}},{"line":188,"address":[],"length":0,"stats":{"Line":1}},{"line":189,"address":[1290629],"length":1,"stats":{"Line":1}},{"line":205,"address":[],"length":0,"stats":{"Line":2}},{"line":206,"address":[],"length":0,"stats":{"Line":6}},{"line":227,"address":[1290896,1290960,1290928],"length":1,"stats":{"Line":3}},{"line":228,"address":[],"length":0,"stats":{"Line":3}},{"line":233,"address":[140560],"length":1,"stats":{"Line":9}},{"line":238,"address":[1291234,1291010,1291042,1291074,1291106,1291170,1291129,1291202],"length":1,"stats":{"Line":9}},{"line":241,"address":[163424,163296,163392,163360,163328,163456],"length":1,"stats":{"Line":18}},{"line":246,"address":[228573,228509,228605,228477,228637,228541],"length":1,"stats":{"Line":18}},{"line":272,"address":[218176,218150,218064],"length":1,"stats":{"Line":4}},{"line":275,"address":[1291262,1291390],"length":1,"stats":{"Line":5}},{"line":278,"address":[140909],"length":1,"stats":{"Line":4}},{"line":315,"address":[],"length":0,"stats":{"Line":2}},{"line":318,"address":[],"length":0,"stats":{"Line":6}},{"line":320,"address":[1291785,1291595,1291625,1291755],"length":1,"stats":{"Line":4}},{"line":322,"address":[],"length":0,"stats":{"Line":0}},{"line":329,"address":[1291840,1291824],"length":1,"stats":{"Line":2}},{"line":330,"address":[],"length":0,"stats":{"Line":2}},{"line":335,"address":[],"length":0,"stats":{"Line":1}},{"line":336,"address":[],"length":0,"stats":{"Line":1}},{"line":355,"address":[],"length":0,"stats":{"Line":1}},{"line":356,"address":[],"length":0,"stats":{"Line":2}},{"line":357,"address":[],"length":0,"stats":{"Line":0}}],"covered":54,"coverable":57},{"path":["/","home","botahamec","Projects","happylock","src","mutex.rs"],"content":"use std::cell::UnsafeCell;\nuse std::marker::PhantomData;\n\nuse lock_api::RawMutex;\n\nuse crate::poisonable::PoisonFlag;\nuse crate::ThreadKey;\n\nmod guard;\nmod mutex;\n\n/// A spinning mutex\n#[cfg(feature = \"spin\")]\npub type SpinLock\u003cT\u003e = Mutex\u003cT, spin::Mutex\u003c()\u003e\u003e;\n\n/// A parking lot mutex\n#[cfg(feature = \"parking_lot\")]\npub type ParkingMutex\u003cT\u003e = Mutex\u003cT, parking_lot::RawMutex\u003e;\n\n/// A mutual exclusion primitive useful for protecting shared data, which\n/// cannot deadlock.\n///\n/// This mutex will block threads waiting for the lock to become available.\n/// Each mutex has a type parameter which represents the data that it is\n/// protecting. The data can only be accessed through the [`MutexGuard`]s\n/// returned from [`lock`] and [`try_lock`], which guarantees that the data is\n/// only ever accessed when the mutex is locked.\n///\n/// Locking the mutex on a thread that already locked it is impossible, due to\n/// the requirement of the [`ThreadKey`]. Therefore, this will never deadlock.\n///\n/// # Examples\n///\n/// ```\n/// use std::sync::Arc;\n/// use std::thread;\n/// use std::sync::mpsc;\n///\n/// use happylock::{Mutex, ThreadKey};\n///\n/// // Spawn a few threads to increment a shared variable (non-atomically),\n/// // and let the main thread know once all increments are done.\n/// //\n/// // Here we're using an Arc to share memory among threads, and the data\n/// // inside the Arc is protected with a mutex.\n/// const N: usize = 10;\n///\n/// let data = Arc::new(Mutex::new(0));\n///\n/// let (tx, rx) = mpsc::channel();\n/// for _ in 0..N {\n/// let (data, tx) = (Arc::clone(\u0026data), tx.clone());\n/// thread::spawn(move || {\n/// let key = ThreadKey::get().unwrap();\n/// let mut data = data.lock(key);\n/// *data += 1;\n/// if *data == N {\n/// tx.send(()).unwrap();\n/// }\n/// // the lock is unlocked\n/// });\n/// }\n///\n/// rx.recv().unwrap();\n/// ```\n///\n/// To unlock a mutex guard sooner than the end of the enclosing scope, either\n/// create an inner scope, drop the guard manually, or call [`Mutex::unlock`].\n///\n/// ```\n/// use std::sync::Arc;\n/// use std::thread;\n///\n/// use happylock::{Mutex, ThreadKey};\n///\n/// const N: usize = 3;\n///\n/// let data_mutex = Arc::new(Mutex::new(vec![1, 2, 3, 4]));\n/// let res_mutex = Arc::new(Mutex::new(0));\n///\n/// let mut threads = Vec::with_capacity(N);\n/// (0..N).for_each(|_| {\n/// let data_mutex_clone = Arc::clone(\u0026data_mutex);\n/// let res_mutex_clone = Arc::clone(\u0026res_mutex);\n///\n/// threads.push(thread::spawn(move || {\n/// let mut key = ThreadKey::get().unwrap();\n///\n/// // Here we use a block to limit the lifetime of the lock guard.\n/// let result = data_mutex_clone.scoped_lock(\u0026mut key, |data| {\n/// let result = data.iter().fold(0, |acc, x| acc + x * 2);\n/// data.push(result);\n/// result\n/// // The mutex guard gets dropped here, so the lock is released\n/// });\n/// // The thread key is available again\n/// *res_mutex_clone.lock(key) += result;\n/// }));\n/// });\n///\n/// let key = ThreadKey::get().unwrap();\n/// let mut data = data_mutex.lock(key);\n/// let result = data.iter().fold(0, |acc, x| acc + x * 2);\n/// data.push(result);\n///\n/// // We drop the `data` explicitly because it's not necessary anymore. This\n/// // allows other threads to start working on the data immediately. Dropping\n/// // the data also gives us access to the thread key, so we can lock\n/// // another mutex.\n/// let key = Mutex::unlock(data);\n///\n/// // Here the mutex guard is not assigned to a variable and so, even if the\n/// // scope does not end after this line, the mutex is still released: there is\n/// // no deadlock.\n/// *res_mutex.lock(key) += result;\n///\n/// threads.into_iter().for_each(|thread| {\n/// thread\n/// .join()\n/// .expect(\"The thread creating or execution failed !\")\n/// });\n///\n/// let key = ThreadKey::get().unwrap();\n/// assert_eq!(*res_mutex.lock(key), 800);\n/// ```\n///\n/// [`lock`]: `Mutex::lock`\n/// [`try_lock`]: `Mutex::try_lock`\n/// [`ThreadKey`]: `crate::ThreadKey`\npub struct Mutex\u003cT: ?Sized, R\u003e {\n\traw: R,\n\tpoison: PoisonFlag,\n\tdata: UnsafeCell\u003cT\u003e,\n}\n\n/// A reference to a mutex that unlocks it when dropped.\n///\n/// This is similar to [`MutexGuard`], except it does not hold a [`Keyable`].\npub struct MutexRef\u003c'a, T: ?Sized + 'a, R: RawMutex\u003e(\u0026'a Mutex\u003cT, R\u003e, PhantomData\u003cR::GuardMarker\u003e);\n\n/// An RAII implementation of a “scoped lock” of a mutex.\n///\n/// When this structure is dropped (falls out of scope), the lock will be\n/// unlocked.\n///\n/// This is created by calling the [`lock`] and [`try_lock`] methods on [`Mutex`]\n///\n/// [`lock`]: `Mutex::lock`\n/// [`try_lock`]: `Mutex::try_lock`\n//\n// This is the most lifetime-intensive thing I've ever written. Can I graduate\n// from borrow checker university now?\npub struct MutexGuard\u003c'a, T: ?Sized + 'a, R: RawMutex\u003e {\n\tmutex: MutexRef\u003c'a, T, R\u003e, // this way we don't need to re-implement Drop\n\tthread_key: ThreadKey,\n}\n\n#[cfg(test)]\nmod tests {\n\tuse crate::{LockCollection, ThreadKey};\n\n\tuse super::*;\n\n\t#[test]\n\tfn unlocked_when_initialized() {\n\t\tlet lock: crate::Mutex\u003c_\u003e = Mutex::new(\"Hello, world!\");\n\n\t\tassert!(!lock.is_locked());\n\t}\n\n\t#[test]\n\tfn locked_after_read() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::Mutex\u003c_\u003e = Mutex::new(\"Hello, world!\");\n\n\t\tlet guard = lock.lock(key);\n\n\t\tassert!(lock.is_locked());\n\t\tdrop(guard)\n\t}\n\n\t#[test]\n\tfn from_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex: crate::Mutex\u003c_\u003e = Mutex::from(\"Hello, world!\");\n\n\t\tlet guard = mutex.lock(key);\n\t\tassert_eq!(*guard, \"Hello, world!\");\n\t}\n\n\t#[test]\n\tfn as_mut_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mut mutex = crate::Mutex::from(42);\n\n\t\tlet mut_ref = mutex.as_mut();\n\t\t*mut_ref = 24;\n\n\t\tmutex.scoped_lock(key, |guard| assert_eq!(*guard, 24))\n\t}\n\n\t#[test]\n\tfn display_works_for_guard() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex: crate::Mutex\u003c_\u003e = Mutex::new(\"Hello, world!\");\n\t\tlet guard = mutex.lock(key);\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn display_works_for_ref() {\n\t\tlet mutex: crate::Mutex\u003c_\u003e = Mutex::new(\"Hello, world!\");\n\t\tlet guard = unsafe { mutex.try_lock_no_key().unwrap() };\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn ref_as_mut() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = LockCollection::new(crate::Mutex::new(0));\n\t\tlet mut guard = collection.lock(key);\n\t\tlet guard_mut = guard.as_mut().as_mut();\n\n\t\t*guard_mut = 3;\n\t\tlet key = LockCollection::\u003ccrate::Mutex\u003c_\u003e\u003e::unlock(guard);\n\n\t\tlet guard = collection.lock(key);\n\n\t\tassert_eq!(guard.as_ref().as_ref(), \u00263);\n\t}\n\n\t#[test]\n\tfn guard_as_mut() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = crate::Mutex::new(0);\n\t\tlet mut guard = mutex.lock(key);\n\t\tlet guard_mut = guard.as_mut();\n\n\t\t*guard_mut = 3;\n\t\tlet key = Mutex::unlock(guard);\n\n\t\tlet guard = mutex.lock(key);\n\n\t\tassert_eq!(guard.as_ref(), \u00263);\n\t}\n\n\t#[test]\n\tfn dropping_guard_releases_mutex() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex: crate::Mutex\u003c_\u003e = Mutex::new(\"Hello, world!\");\n\n\t\tlet guard = mutex.lock(key);\n\t\tdrop(guard);\n\n\t\tassert!(!mutex.is_locked());\n\t}\n\n\t#[test]\n\tfn dropping_ref_releases_mutex() {\n\t\tlet mutex: crate::Mutex\u003c_\u003e = Mutex::new(\"Hello, world!\");\n\n\t\tlet guard = unsafe { mutex.try_lock_no_key().unwrap() };\n\t\tdrop(guard);\n\n\t\tassert!(!mutex.is_locked());\n\t}\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","src","poisonable","error.rs"],"content":"use core::fmt;\nuse std::error::Error;\n\nuse super::{PoisonError, PoisonGuard, TryLockPoisonableError};\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard\u003e fmt::Debug for PoisonError\u003cGuard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut fmt::Formatter\u003c'_\u003e) -\u003e fmt::Result {\n\t\tf.debug_struct(\"PoisonError\").finish_non_exhaustive()\n\t}\n}\n\nimpl\u003cGuard\u003e fmt::Display for PoisonError\u003cGuard\u003e {\n\t#[cfg_attr(test, mutants::skip)]\n\t#[cfg(not(tarpaulin_include))]\n\tfn fmt(\u0026self, f: \u0026mut fmt::Formatter\u003c'_\u003e) -\u003e fmt::Result {\n\t\t\"poisoned lock: another task failed inside\".fmt(f)\n\t}\n}\n\nimpl\u003cGuard\u003e AsRef\u003cGuard\u003e for PoisonError\u003cGuard\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026Guard {\n\t\tself.get_ref()\n\t}\n}\n\nimpl\u003cGuard\u003e AsMut\u003cGuard\u003e for PoisonError\u003cGuard\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut Guard {\n\t\tself.get_mut()\n\t}\n}\n\nimpl\u003cGuard\u003e Error for PoisonError\u003cGuard\u003e {}\n\nimpl\u003cGuard\u003e PoisonError\u003cGuard\u003e {\n\t/// Creates a `PoisonError`\n\t///\n\t/// This is generally created by methods like [`Poisonable::lock`].\n\t///\n\t/// ```\n\t/// use happylock::poisonable::PoisonError;\n\t///\n\t/// let error = PoisonError::new(\"oh no\");\n\t/// ```\n\t///\n\t/// [`Poisonable::lock`]: `crate::poisonable::Poisonable::lock`\n\t#[must_use]\n\tpub const fn new(guard: Guard) -\u003e Self {\n\t\tSelf { guard }\n\t}\n\n\t/// Consumes the error indicating that a lock is poisonmed, returning the\n\t/// underlying guard to allow access regardless.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::collections::HashSet;\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let mutex = Arc::new(Poisonable::new(Mutex::new(HashSet::new())));\n\t///\n\t/// // poison the mutex\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t/// let _ = thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut data = c_mutex.lock(key).unwrap();\n\t/// data.insert(10);\n\t/// panic!();\n\t/// }).join();\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let p_err = mutex.lock(key).unwrap_err();\n\t/// let data = p_err.into_inner();\n\t/// println!(\"recovered {} items\", data.len());\n\t/// ```\n\t#[must_use]\n\tpub fn into_inner(self) -\u003e Guard {\n\t\tself.guard\n\t}\n\n\t/// Reaches into this error indicating that a lock is poisoned, returning a\n\t/// reference to the underlying guard to allow access regardless.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::collections::HashSet;\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t/// use happylock::poisonable::PoisonGuard;\n\t///\n\t/// let mutex = Arc::new(Poisonable::new(Mutex::new(HashSet::new())));\n\t///\n\t/// // poison the mutex\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t/// let _ = thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut data = c_mutex.lock(key).unwrap();\n\t/// data.insert(10);\n\t/// panic!();\n\t/// }).join();\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let p_err = mutex.lock(key).unwrap_err();\n\t/// let data: \u0026PoisonGuard\u003c_\u003e = p_err.get_ref();\n\t/// println!(\"recovered {} items\", data.len());\n\t/// ```\n\t#[must_use]\n\tpub const fn get_ref(\u0026self) -\u003e \u0026Guard {\n\t\t\u0026self.guard\n\t}\n\n\t/// Reaches into this error indicating that a lock is poisoned, returning a\n\t/// mutable reference to the underlying guard to allow access regardless.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::collections::HashSet;\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let mutex = Arc::new(Poisonable::new(Mutex::new(HashSet::new())));\n\t///\n\t/// // poison the mutex\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t/// let _ = thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut data = c_mutex.lock(key).unwrap();\n\t/// data.insert(10);\n\t/// panic!();\n\t/// }).join();\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut p_err = mutex.lock(key).unwrap_err();\n\t/// let data = p_err.get_mut();\n\t/// data.insert(20);\n\t/// println!(\"recovered {} items\", data.len());\n\t/// ```\n\t#[must_use]\n\tpub fn get_mut(\u0026mut self) -\u003e \u0026mut Guard {\n\t\t\u0026mut self.guard\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cG\u003e fmt::Debug for TryLockPoisonableError\u003c'_, G\u003e {\n\tfn fmt(\u0026self, f: \u0026mut fmt::Formatter\u003c'_\u003e) -\u003e fmt::Result {\n\t\tmatch *self {\n\t\t\tSelf::Poisoned(..) =\u003e \"Poisoned(..)\".fmt(f),\n\t\t\tSelf::WouldBlock(_) =\u003e \"WouldBlock\".fmt(f),\n\t\t}\n\t}\n}\n\nimpl\u003cG\u003e fmt::Display for TryLockPoisonableError\u003c'_, G\u003e {\n\t#[cfg_attr(test, mutants::skip)]\n\t#[cfg(not(tarpaulin_include))]\n\tfn fmt(\u0026self, f: \u0026mut fmt::Formatter\u003c'_\u003e) -\u003e fmt::Result {\n\t\tmatch *self {\n\t\t\tSelf::Poisoned(..) =\u003e \"poisoned lock: another task failed inside\",\n\t\t\tSelf::WouldBlock(_) =\u003e \"try_lock failed because the operation would block\",\n\t\t}\n\t\t.fmt(f)\n\t}\n}\n\nimpl\u003cG\u003e Error for TryLockPoisonableError\u003c'_, G\u003e {}\n\nimpl\u003c'flag, G\u003e From\u003cPoisonError\u003cPoisonGuard\u003c'flag, G\u003e\u003e\u003e for TryLockPoisonableError\u003c'flag, G\u003e {\n\tfn from(value: PoisonError\u003cPoisonGuard\u003c'flag, G\u003e\u003e) -\u003e Self {\n\t\tSelf::Poisoned(value)\n\t}\n}\n","traces":[{"line":23,"address":[863392],"length":1,"stats":{"Line":1}},{"line":24,"address":[],"length":0,"stats":{"Line":1}},{"line":29,"address":[],"length":0,"stats":{"Line":1}},{"line":30,"address":[],"length":0,"stats":{"Line":1}},{"line":49,"address":[],"length":0,"stats":{"Line":10}},{"line":82,"address":[],"length":0,"stats":{"Line":7}},{"line":83,"address":[],"length":0,"stats":{"Line":1}},{"line":116,"address":[],"length":0,"stats":{"Line":4}},{"line":117,"address":[],"length":0,"stats":{"Line":0}},{"line":150,"address":[],"length":0,"stats":{"Line":3}},{"line":151,"address":[],"length":0,"stats":{"Line":0}},{"line":181,"address":[],"length":0,"stats":{"Line":1}},{"line":182,"address":[],"length":0,"stats":{"Line":1}}],"covered":11,"coverable":13},{"path":["/","home","botahamec","Projects","happylock","src","poisonable","flag.rs"],"content":"#[cfg(panic = \"unwind\")]\nuse std::sync::atomic::{AtomicBool, Ordering::Relaxed};\n\nuse super::PoisonFlag;\n\n#[cfg(panic = \"unwind\")]\nimpl PoisonFlag {\n\tpub const fn new() -\u003e Self {\n\t\tSelf(AtomicBool::new(false))\n\t}\n\n\tpub fn is_poisoned(\u0026self) -\u003e bool {\n\t\tself.0.load(Relaxed)\n\t}\n\n\tpub fn clear_poison(\u0026self) {\n\t\tself.0.store(false, Relaxed)\n\t}\n\n\tpub fn poison(\u0026self) {\n\t\tself.0.store(true, Relaxed);\n\t}\n}\n\n#[cfg(not(panic = \"unwind\"))]\nimpl PoisonFlag {\n\tpub const fn new() -\u003e Self {\n\t\tSelf()\n\t}\n\n\t#[mutants::skip] // None of the tests have panic = \"abort\", so this can't be tested\n\t#[cfg(not(tarpaulin_include))]\n\tpub fn is_poisoned(\u0026self) -\u003e bool {\n\t\tfalse\n\t}\n\n\tpub fn clear_poison(\u0026self) {\n\t\t()\n\t}\n\n\tpub fn poison(\u0026self) {\n\t\t()\n\t}\n}\n","traces":[{"line":8,"address":[532416],"length":1,"stats":{"Line":19}},{"line":9,"address":[538033],"length":1,"stats":{"Line":19}},{"line":12,"address":[508448],"length":1,"stats":{"Line":11}},{"line":13,"address":[532473],"length":1,"stats":{"Line":11}},{"line":16,"address":[546784],"length":1,"stats":{"Line":1}},{"line":17,"address":[508489],"length":1,"stats":{"Line":1}},{"line":20,"address":[554944],"length":1,"stats":{"Line":8}},{"line":21,"address":[508521],"length":1,"stats":{"Line":8}}],"covered":8,"coverable":8},{"path":["/","home","botahamec","Projects","happylock","src","poisonable","guard.rs"],"content":"use std::fmt::{Debug, Display};\nuse std::hash::Hash;\nuse std::marker::PhantomData;\nuse std::ops::{Deref, DerefMut};\n\nuse super::{PoisonFlag, PoisonGuard, PoisonRef};\n\nimpl\u003c'a, Guard\u003e PoisonRef\u003c'a, Guard\u003e {\n\t// This is used so that we don't keep accidentally adding the flag reference\n\tpub(super) const fn new(flag: \u0026'a PoisonFlag, guard: Guard) -\u003e Self {\n\t\tSelf {\n\t\t\tguard,\n\t\t\t#[cfg(panic = \"unwind\")]\n\t\t\tflag,\n\t\t\t_phantom: PhantomData,\n\t\t}\n\t}\n}\n\nimpl\u003cGuard\u003e Drop for PoisonRef\u003c'_, Guard\u003e {\n\tfn drop(\u0026mut self) {\n\t\t#[cfg(panic = \"unwind\")]\n\t\tif std::thread::panicking() {\n\t\t\tself.flag.poison();\n\t\t}\n\t}\n}\n\n#[mutants::skip] // hashing involves RNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard: Hash\u003e Hash for PoisonRef\u003c'_, Guard\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.guard.hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard: Debug\u003e Debug for PoisonRef\u003c'_, Guard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cGuard: Display\u003e Display for PoisonRef\u003c'_, Guard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cGuard\u003e Deref for PoisonRef\u003c'_, Guard\u003e {\n\ttype Target = Guard;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t\u0026self.guard\n\t}\n}\n\nimpl\u003cGuard\u003e DerefMut for PoisonRef\u003c'_, Guard\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t\u0026mut self.guard\n\t}\n}\n\nimpl\u003cGuard\u003e AsRef\u003cGuard\u003e for PoisonRef\u003c'_, Guard\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026Guard {\n\t\t\u0026self.guard\n\t}\n}\n\nimpl\u003cGuard\u003e AsMut\u003cGuard\u003e for PoisonRef\u003c'_, Guard\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut Guard {\n\t\t\u0026mut self.guard\n\t}\n}\n\n#[mutants::skip] // hashing involves RNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard: Hash\u003e Hash for PoisonGuard\u003c'_, Guard\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.guard.hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard: Debug\u003e Debug for PoisonGuard\u003c'_, Guard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026self.guard, f)\n\t}\n}\n\nimpl\u003cGuard: Display\u003e Display for PoisonGuard\u003c'_, Guard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026self.guard, f)\n\t}\n}\n\nimpl\u003cT, Guard: Deref\u003cTarget = T\u003e\u003e Deref for PoisonGuard\u003c'_, Guard\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t#[allow(clippy::explicit_auto_deref)] // fixing this results in a compiler error\n\t\t\u0026*self.guard.guard\n\t}\n}\n\nimpl\u003cT, Guard: DerefMut\u003cTarget = T\u003e\u003e DerefMut for PoisonGuard\u003c'_, Guard\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t#[allow(clippy::explicit_auto_deref)] // fixing this results in a compiler error\n\t\t\u0026mut *self.guard.guard\n\t}\n}\n\nimpl\u003cGuard\u003e AsRef\u003cGuard\u003e for PoisonGuard\u003c'_, Guard\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026Guard {\n\t\t\u0026self.guard.guard\n\t}\n}\n\nimpl\u003cGuard\u003e AsMut\u003cGuard\u003e for PoisonGuard\u003c'_, Guard\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut Guard {\n\t\t\u0026mut self.guard.guard\n\t}\n}\n","traces":[{"line":10,"address":[864176,864080,864144,864112],"length":1,"stats":{"Line":5}},{"line":21,"address":[],"length":0,"stats":{"Line":4}},{"line":22,"address":[],"length":0,"stats":{"Line":0}},{"line":23,"address":[],"length":0,"stats":{"Line":5}},{"line":24,"address":[],"length":0,"stats":{"Line":4}},{"line":46,"address":[],"length":0,"stats":{"Line":1}},{"line":47,"address":[],"length":0,"stats":{"Line":1}},{"line":54,"address":[],"length":0,"stats":{"Line":3}},{"line":55,"address":[],"length":0,"stats":{"Line":0}},{"line":60,"address":[],"length":0,"stats":{"Line":2}},{"line":61,"address":[],"length":0,"stats":{"Line":0}},{"line":66,"address":[],"length":0,"stats":{"Line":1}},{"line":67,"address":[],"length":0,"stats":{"Line":0}},{"line":72,"address":[],"length":0,"stats":{"Line":1}},{"line":73,"address":[],"length":0,"stats":{"Line":0}},{"line":94,"address":[],"length":0,"stats":{"Line":1}},{"line":95,"address":[],"length":0,"stats":{"Line":1}},{"line":102,"address":[],"length":0,"stats":{"Line":2}},{"line":104,"address":[],"length":0,"stats":{"Line":2}},{"line":109,"address":[],"length":0,"stats":{"Line":3}},{"line":111,"address":[],"length":0,"stats":{"Line":3}},{"line":116,"address":[],"length":0,"stats":{"Line":1}},{"line":117,"address":[],"length":0,"stats":{"Line":0}},{"line":122,"address":[],"length":0,"stats":{"Line":1}},{"line":123,"address":[],"length":0,"stats":{"Line":0}}],"covered":18,"coverable":25},{"path":["/","home","botahamec","Projects","happylock","src","poisonable","poisonable.rs"],"content":"use std::panic::{RefUnwindSafe, UnwindSafe};\n\nuse crate::handle_unwind::handle_unwind;\nuse crate::lockable::{\n\tLockable, LockableGetMut, LockableIntoInner, OwnedLockable, RawLock, Sharable,\n};\nuse crate::{Keyable, ThreadKey};\n\nuse super::{\n\tPoisonError, PoisonFlag, PoisonGuard, PoisonRef, PoisonResult, Poisonable,\n\tTryLockPoisonableError, TryLockPoisonableResult,\n};\n\nunsafe impl\u003cL: Lockable + RawLock\u003e RawLock for Poisonable\u003cL\u003e {\n\t#[mutants::skip] // this should never run\n\t#[cfg(not(tarpaulin_include))]\n\tfn poison(\u0026self) {\n\t\tself.inner.poison()\n\t}\n\n\tunsafe fn raw_write(\u0026self) {\n\t\tself.inner.raw_write()\n\t}\n\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool {\n\t\tself.inner.raw_try_write()\n\t}\n\n\tunsafe fn raw_unlock_write(\u0026self) {\n\t\tself.inner.raw_unlock_write()\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tself.inner.raw_read()\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tself.inner.raw_try_read()\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tself.inner.raw_unlock_read()\n\t}\n}\n\nunsafe impl\u003cL: Lockable\u003e Lockable for Poisonable\u003cL\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= PoisonResult\u003cPoisonRef\u003c'g, L::Guard\u003c'g\u003e\u003e\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= PoisonResult\u003cL::DataMut\u003c'a\u003e\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tself.inner.get_ptrs(ptrs)\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tlet ref_guard = PoisonRef::new(\u0026self.poisoned, self.inner.guard());\n\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(ref_guard))\n\t\t} else {\n\t\t\tOk(ref_guard)\n\t\t}\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(self.inner.data_mut()))\n\t\t} else {\n\t\t\tOk(self.inner.data_mut())\n\t\t}\n\t}\n}\n\nunsafe impl\u003cL: Sharable\u003e Sharable for Poisonable\u003cL\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= PoisonResult\u003cPoisonRef\u003c'g, L::ReadGuard\u003c'g\u003e\u003e\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= PoisonResult\u003cL::DataRef\u003c'a\u003e\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tlet ref_guard = PoisonRef::new(\u0026self.poisoned, self.inner.read_guard());\n\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(ref_guard))\n\t\t} else {\n\t\t\tOk(ref_guard)\n\t\t}\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(self.inner.data_ref()))\n\t\t} else {\n\t\t\tOk(self.inner.data_ref())\n\t\t}\n\t}\n}\n\nunsafe impl\u003cL: OwnedLockable\u003e OwnedLockable for Poisonable\u003cL\u003e {}\n\n// AsMut won't work here because we don't strictly return a \u0026mut T\n// LockableGetMut is the next best thing\nimpl\u003cL: LockableGetMut\u003e LockableGetMut for Poisonable\u003cL\u003e {\n\ttype Inner\u003c'a\u003e\n\t\t= PoisonResult\u003cL::Inner\u003c'a\u003e\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(self.inner.get_mut()))\n\t\t} else {\n\t\t\tOk(self.inner.get_mut())\n\t\t}\n\t}\n}\n\nimpl\u003cL: LockableIntoInner\u003e LockableIntoInner for Poisonable\u003cL\u003e {\n\ttype Inner = PoisonResult\u003cL::Inner\u003e;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(self.inner.into_inner()))\n\t\t} else {\n\t\t\tOk(self.inner.into_inner())\n\t\t}\n\t}\n}\n\nimpl\u003cL\u003e From\u003cL\u003e for Poisonable\u003cL\u003e {\n\tfn from(value: L) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\nimpl\u003cL\u003e Poisonable\u003cL\u003e {\n\t/// Creates a new `Poisonable`\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, Poisonable};\n\t///\n\t/// let mutex = Poisonable::new(Mutex::new(0));\n\t/// ```\n\tpub const fn new(value: L) -\u003e Self {\n\t\tSelf {\n\t\t\tinner: value,\n\t\t\tpoisoned: PoisonFlag::new(),\n\t\t}\n\t}\n\n\t/// Determines whether the mutex is poisoned.\n\t///\n\t/// If another thread is active, the mutex can still become poisoned at any\n\t/// time. You should not trust a `false` value for program correctness\n\t/// without additional synchronization.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let mutex = Arc::new(Poisonable::new(Mutex::new(0)));\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t///\n\t/// let _ = thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let _lock = c_mutex.lock(key).unwrap();\n\t/// panic!(); // the mutex gets poisoned\n\t/// }).join();\n\t///\n\t/// assert_eq!(mutex.is_poisoned(), true);\n\t/// ```\n\tpub fn is_poisoned(\u0026self) -\u003e bool {\n\t\tself.poisoned.is_poisoned()\n\t}\n\n\t/// Clear the poisoned state from a lock.\n\t///\n\t/// If the lock is poisoned, it will remain poisoned until this function\n\t/// is called. This allows recovering from a poisoned state and marking\n\t/// that it has recovered. For example, if the value is overwritten by a\n\t/// known-good value, then the lock can be marked as un-poisoned. Or\n\t/// possibly, the value could by inspected to determine if it is in a\n\t/// consistent state, and if so the poison is removed.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let mutex = Arc::new(Poisonable::new(Mutex::new(0)));\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t///\n\t/// let _ = thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let _lock = c_mutex.lock(key).unwrap();\n\t/// panic!(); // the mutex gets poisoned\n\t/// }).join();\n\t///\n\t/// assert_eq!(mutex.is_poisoned(), true);\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let x = mutex.lock(key).unwrap_or_else(|mut e| {\n\t/// **e.get_mut() = 1;\n\t/// mutex.clear_poison();\n\t/// e.into_inner()\n\t/// });\n\t///\n\t/// assert_eq!(mutex.is_poisoned(), false);\n\t/// assert_eq!(*x, 1);\n\t/// ```\n\tpub fn clear_poison(\u0026self) {\n\t\tself.poisoned.clear_poison()\n\t}\n\n\t/// Consumes this `Poisonable`, returning the underlying lock.\n\t///\n\t/// This consumes the `Poisonable` and returns ownership of the lock, which\n\t/// means that the `Poisonable` can still be `RefUnwindSafe`.\n\t///\n\t/// # Errors\n\t///\n\t/// If another user of this lock panicked while holding the lock, then this\n\t/// call will return an error instead.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, Poisonable};\n\t///\n\t/// let mutex = Poisonable::new(Mutex::new(0));\n\t/// assert_eq!(mutex.into_child().unwrap().into_inner(), 0);\n\t/// ```\n\tpub fn into_child(self) -\u003e PoisonResult\u003cL\u003e {\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(self.inner))\n\t\t} else {\n\t\t\tOk(self.inner)\n\t\t}\n\t}\n\n\t/// Returns a mutable reference to the underlying lock.\n\t///\n\t/// This can be implemented while still being `RefUnwindSafe` because\n\t/// it requires a mutable reference.\n\t///\n\t/// # Errors\n\t///\n\t/// If another user of this lock panicked while holding the lock, then\n\t/// this call will return an error instead.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut mutex = Poisonable::new(Mutex::new(0));\n\t/// *mutex.child_mut().unwrap().as_mut() = 10;\n\t/// assert_eq!(*mutex.lock(key).unwrap(), 10);\n\t/// ```\n\tpub fn child_mut(\u0026mut self) -\u003e PoisonResult\u003c\u0026mut L\u003e {\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(\u0026mut self.inner))\n\t\t} else {\n\t\t\tOk(\u0026mut self.inner)\n\t\t}\n\t}\n\n\t// NOTE: `child_ref` isn't implemented because it would make this not `RefUnwindSafe`\n\t//\n}\n\nimpl\u003cL: Lockable\u003e Poisonable\u003cL\u003e {\n\t/// Creates a guard for the poisonable, without locking it\n\tunsafe fn guard(\u0026self, key: ThreadKey) -\u003e PoisonResult\u003cPoisonGuard\u003c'_, L::Guard\u003c'_\u003e\u003e\u003e {\n\t\tlet guard = PoisonGuard {\n\t\t\tguard: PoisonRef::new(\u0026self.poisoned, self.inner.guard()),\n\t\t\tkey,\n\t\t};\n\n\t\tif self.is_poisoned() {\n\t\t\treturn Err(PoisonError::new(guard));\n\t\t}\n\n\t\tOk(guard)\n\t}\n}\n\nimpl\u003cL: Lockable + RawLock\u003e Poisonable\u003cL\u003e {\n\tpub fn scoped_lock\u003c'a, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: impl Keyable,\n\t\tf: impl Fn(\u003cSelf as Lockable\u003e::DataMut\u003c'a\u003e) -\u003e R,\n\t) -\u003e R {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_write();\n\n\t\t\t// safety: the data was just locked\n\t\t\tlet r = handle_unwind(\n\t\t\t\t|| f(self.data_mut()),\n\t\t\t\t|| {\n\t\t\t\t\tself.poisoned.poison();\n\t\t\t\t\tself.raw_unlock_write();\n\t\t\t\t},\n\t\t\t);\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock_write();\n\n\t\t\tdrop(key); // ensure the key stays alive long enough\n\n\t\t\tr\n\t\t}\n\t}\n\n\tpub fn scoped_try_lock\u003c'a, Key: Keyable, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(\u003cSelf as Lockable\u003e::DataMut\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_write() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we just locked the collection\n\t\t\tlet r = handle_unwind(\n\t\t\t\t|| f(self.data_mut()),\n\t\t\t\t|| {\n\t\t\t\t\tself.poisoned.poison();\n\t\t\t\t\tself.raw_unlock_write();\n\t\t\t\t},\n\t\t\t);\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock_write();\n\n\t\t\tdrop(key); // ensures the key stays valid long enough\n\n\t\t\tOk(r)\n\t\t}\n\t}\n\n\t/// Acquires the lock, blocking the current thread until it is ok to do so.\n\t///\n\t/// This function will block the current thread until it is available to\n\t/// acquire the mutex. Upon returning, the thread is the only thread with\n\t/// the lock held. An RAII guard is returned to allow scoped unlock of the\n\t/// lock. When the guard goes out of scope, the mutex will be unlocked.\n\t///\n\t/// # Errors\n\t///\n\t/// If another use of this mutex panicked while holding the mutex, then\n\t/// this call will return an error once the mutex is acquired.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let mutex = Arc::new(Poisonable::new(Mutex::new(0)));\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t///\n\t/// thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// *c_mutex.lock(key).unwrap() = 10;\n\t/// }).join().expect(\"thread::spawn failed\");\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// assert_eq!(*mutex.lock(key).unwrap(), 10);\n\t/// ```\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e PoisonResult\u003cPoisonGuard\u003c'_, L::Guard\u003c'_\u003e\u003e\u003e {\n\t\tunsafe {\n\t\t\tself.inner.raw_write();\n\t\t\tself.guard(key)\n\t\t}\n\t}\n\n\t/// Attempts to acquire this lock.\n\t///\n\t/// If the lock could not be acquired at this time, then [`Err`] is\n\t/// returned. Otherwise, an RAII guard is returned. The lock will be\n\t/// unlocked when the guard is dropped.\n\t///\n\t/// This function does not block.\n\t///\n\t/// # Errors\n\t///\n\t/// If another user of this mutex panicked while holding the mutex, then\n\t/// this call will return the [`Poisoned`] error if the mutex would\n\t/// otherwise be acquired.\n\t///\n\t/// If the mutex could not be acquired because it is already locked, then\n\t/// this call will return the [`WouldBlock`] error.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let mutex = Arc::new(Poisonable::new(Mutex::new(0)));\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t///\n\t/// thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut lock = c_mutex.try_lock(key);\n\t/// if let Ok(mut mutex) = lock {\n\t/// *mutex = 10;\n\t/// } else {\n\t/// println!(\"try_lock failed\");\n\t/// }\n\t/// }).join().expect(\"thread::spawn failed\");\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// assert_eq!(*mutex.lock(key).unwrap(), 10);\n\t/// ```\n\t///\n\t/// [`Poisoned`]: `TryLockPoisonableError::Poisoned`\n\t/// [`WouldBlock`]: `TryLockPoisonableError::WouldBlock`\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e TryLockPoisonableResult\u003c'_, L::Guard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\tif self.inner.raw_try_write() {\n\t\t\t\tOk(self.guard(key)?)\n\t\t\t} else {\n\t\t\t\tErr(TryLockPoisonableError::WouldBlock(key))\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Consumes the [`PoisonGuard`], and consequently unlocks its `Poisonable`.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, Mutex, Poisonable};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mutex = Poisonable::new(Mutex::new(0));\n\t///\n\t/// let mut guard = mutex.lock(key).unwrap();\n\t/// *guard += 20;\n\t///\n\t/// let key = Poisonable::\u003cMutex\u003c_\u003e\u003e::unlock(guard);\n\t/// ```\n\tpub fn unlock\u003c'flag\u003e(guard: PoisonGuard\u003c'flag, L::Guard\u003c'flag\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: Sharable + RawLock\u003e Poisonable\u003cL\u003e {\n\tunsafe fn read_guard(\u0026self, key: ThreadKey) -\u003e PoisonResult\u003cPoisonGuard\u003c'_, L::ReadGuard\u003c'_\u003e\u003e\u003e {\n\t\tlet guard = PoisonGuard {\n\t\t\tguard: PoisonRef::new(\u0026self.poisoned, self.inner.read_guard()),\n\t\t\tkey,\n\t\t};\n\n\t\tif self.is_poisoned() {\n\t\t\treturn Err(PoisonError::new(guard));\n\t\t}\n\n\t\tOk(guard)\n\t}\n\n\tpub fn scoped_read\u003c'a, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: impl Keyable,\n\t\tf: impl Fn(\u003cSelf as Sharable\u003e::DataRef\u003c'a\u003e) -\u003e R,\n\t) -\u003e R {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_read();\n\n\t\t\t// safety: the data was just locked\n\t\t\tlet r = handle_unwind(\n\t\t\t\t|| f(self.data_ref()),\n\t\t\t\t|| {\n\t\t\t\t\tself.poisoned.poison();\n\t\t\t\t\tself.raw_unlock_read();\n\t\t\t\t},\n\t\t\t);\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock_read();\n\n\t\t\tdrop(key); // ensure the key stays alive long enough\n\n\t\t\tr\n\t\t}\n\t}\n\n\tpub fn scoped_try_read\u003c'a, Key: Keyable, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(\u003cSelf as Sharable\u003e::DataRef\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_read() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we just locked the collection\n\t\t\tlet r = handle_unwind(\n\t\t\t\t|| f(self.data_ref()),\n\t\t\t\t|| {\n\t\t\t\t\tself.poisoned.poison();\n\t\t\t\t\tself.raw_unlock_read();\n\t\t\t\t},\n\t\t\t);\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock_read();\n\n\t\t\tdrop(key); // ensures the key stays valid long enough\n\n\t\t\tOk(r)\n\t\t}\n\t}\n\n\t/// Locks with shared read access, blocking the current thread until it can\n\t/// be acquired.\n\t///\n\t/// This function will block the current thread until there are no writers\n\t/// which hold the lock. This method does not provide any guarantee with\n\t/// respect to the ordering of contentious readers or writers will acquire\n\t/// the lock.\n\t///\n\t/// # Errors\n\t///\n\t/// If another use of this lock panicked while holding the lock, then\n\t/// this call will return an error once the lock is acquired.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t///\n\t/// use happylock::{RwLock, Poisonable, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = Arc::new(Poisonable::new(RwLock::new(0)));\n\t/// let c_lock = Arc::clone(\u0026lock);\n\t///\n\t/// let n = lock.read(key).unwrap();\n\t/// assert_eq!(*n, 0);\n\t///\n\t/// thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// assert!(c_lock.read(key).is_ok());\n\t/// }).join().expect(\"thread::spawn failed\");\n\t/// ```\n\tpub fn read(\u0026self, key: ThreadKey) -\u003e PoisonResult\u003cPoisonGuard\u003c'_, L::ReadGuard\u003c'_\u003e\u003e\u003e {\n\t\tunsafe {\n\t\t\tself.inner.raw_read();\n\t\t\tself.read_guard(key)\n\t\t}\n\t}\n\n\t/// Attempts to acquire the lock with shared read access.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is returned.\n\t/// Otherwise, an RAII guard is returned which will release the shared access\n\t/// when it is dropped.\n\t///\n\t/// This function does not block.\n\t///\n\t/// This function does not provide any guarantees with respect to the ordering\n\t/// of whether contentious readers or writers will acquire the lock first.\n\t///\n\t/// # Errors\n\t///\n\t/// This function will return the [`Poisoned`] error if the lock is\n\t/// poisoned. A [`Poisonable`] is poisoned whenever a writer panics while\n\t/// holding an exclusive lock. `Poisoned` will only be returned if the lock\n\t/// would have otherwise been acquired.\n\t///\n\t/// This function will return the [`WouldBlock`] error if the lock could\n\t/// not be acquired because it was already locked exclusively.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Poisonable, RwLock, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = Poisonable::new(RwLock::new(1));\n\t///\n\t/// match lock.try_read(key) {\n\t/// Ok(n) =\u003e assert_eq!(*n, 1),\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t/// ```\n\t///\n\t/// [`Poisoned`]: `TryLockPoisonableError::Poisoned`\n\t/// [`WouldBlock`]: `TryLockPoisonableError::WouldBlock`\n\tpub fn try_read(\u0026self, key: ThreadKey) -\u003e TryLockPoisonableResult\u003c'_, L::ReadGuard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\tif self.inner.raw_try_read() {\n\t\t\t\tOk(self.read_guard(key)?)\n\t\t\t} else {\n\t\t\t\tErr(TryLockPoisonableError::WouldBlock(key))\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Consumes the [`PoisonGuard`], and consequently unlocks its underlying lock.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, RwLock, Poisonable};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = Poisonable::new(RwLock::new(0));\n\t///\n\t/// let mut guard = lock.read(key).unwrap();\n\t/// let key = Poisonable::\u003cRwLock\u003c_\u003e\u003e::unlock_read(guard);\n\t/// ```\n\tpub fn unlock_read\u003c'flag\u003e(guard: PoisonGuard\u003c'flag, L::ReadGuard\u003c'flag\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: LockableIntoInner\u003e Poisonable\u003cL\u003e {\n\t/// Consumes this `Poisonable`, returning the underlying data.\n\t///\n\t/// # Errors\n\t///\n\t/// If another user of this lock panicked while holding the lock, then this\n\t/// call will return an error instead.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, Poisonable};\n\t///\n\t/// let mutex = Poisonable::new(Mutex::new(0));\n\t/// assert_eq!(mutex.into_inner().unwrap(), 0);\n\t/// ```\n\tpub fn into_inner(self) -\u003e PoisonResult\u003cL::Inner\u003e {\n\t\tLockableIntoInner::into_inner(self)\n\t}\n}\n\nimpl\u003cL: LockableGetMut + RawLock\u003e Poisonable\u003cL\u003e {\n\t/// Returns a mutable reference to the underlying data.\n\t///\n\t/// Since this call borrows the `Poisonable` mutably, no actual locking\n\t/// needs to take place - the mutable borrow statically guarantees no locks\n\t/// exist.\n\t///\n\t/// # Errors\n\t///\n\t/// If another user of this lock panicked while holding the lock, then\n\t/// this call will return an error instead.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut mutex = Poisonable::new(Mutex::new(0));\n\t/// *mutex.get_mut().unwrap() = 10;\n\t/// assert_eq!(*mutex.lock(key).unwrap(), 10);\n\t/// ```\n\tpub fn get_mut(\u0026mut self) -\u003e PoisonResult\u003cL::Inner\u003c'_\u003e\u003e {\n\t\tLockableGetMut::get_mut(self)\n\t}\n}\n\nimpl\u003cL: UnwindSafe\u003e RefUnwindSafe for Poisonable\u003cL\u003e {}\nimpl\u003cL: UnwindSafe\u003e UnwindSafe for Poisonable\u003cL\u003e {}\n","traces":[{"line":21,"address":[],"length":0,"stats":{"Line":1}},{"line":22,"address":[864517],"length":1,"stats":{"Line":1}},{"line":25,"address":[],"length":0,"stats":{"Line":2}},{"line":26,"address":[],"length":0,"stats":{"Line":2}},{"line":29,"address":[],"length":0,"stats":{"Line":2}},{"line":30,"address":[864581,864565],"length":1,"stats":{"Line":2}},{"line":33,"address":[],"length":0,"stats":{"Line":1}},{"line":34,"address":[],"length":0,"stats":{"Line":1}},{"line":37,"address":[],"length":0,"stats":{"Line":2}},{"line":38,"address":[],"length":0,"stats":{"Line":2}},{"line":41,"address":[],"length":0,"stats":{"Line":1}},{"line":42,"address":[],"length":0,"stats":{"Line":1}},{"line":57,"address":[],"length":0,"stats":{"Line":4}},{"line":58,"address":[],"length":0,"stats":{"Line":4}},{"line":61,"address":[],"length":0,"stats":{"Line":3}},{"line":62,"address":[],"length":0,"stats":{"Line":3}},{"line":64,"address":[],"length":0,"stats":{"Line":11}},{"line":65,"address":[864913,865201,865261,864973],"length":1,"stats":{"Line":2}},{"line":67,"address":[],"length":0,"stats":{"Line":3}},{"line":71,"address":[],"length":0,"stats":{"Line":2}},{"line":72,"address":[],"length":0,"stats":{"Line":4}},{"line":73,"address":[865366,865462],"length":1,"stats":{"Line":1}},{"line":75,"address":[],"length":0,"stats":{"Line":2}},{"line":91,"address":[865758,865780,865504],"length":1,"stats":{"Line":1}},{"line":92,"address":[],"length":0,"stats":{"Line":1}},{"line":94,"address":[],"length":0,"stats":{"Line":4}},{"line":95,"address":[],"length":0,"stats":{"Line":2}},{"line":97,"address":[865649],"length":1,"stats":{"Line":1}},{"line":101,"address":[],"length":0,"stats":{"Line":1}},{"line":102,"address":[],"length":0,"stats":{"Line":2}},{"line":103,"address":[],"length":0,"stats":{"Line":1}},{"line":105,"address":[],"length":0,"stats":{"Line":1}},{"line":120,"address":[],"length":0,"stats":{"Line":1}},{"line":121,"address":[],"length":0,"stats":{"Line":3}},{"line":122,"address":[865942],"length":1,"stats":{"Line":1}},{"line":124,"address":[],"length":0,"stats":{"Line":1}},{"line":132,"address":[866299,865984],"length":1,"stats":{"Line":1}},{"line":133,"address":[],"length":0,"stats":{"Line":3}},{"line":134,"address":[],"length":0,"stats":{"Line":2}},{"line":136,"address":[],"length":0,"stats":{"Line":2}},{"line":142,"address":[],"length":0,"stats":{"Line":1}},{"line":143,"address":[],"length":0,"stats":{"Line":1}},{"line":157,"address":[],"length":0,"stats":{"Line":3}},{"line":160,"address":[],"length":0,"stats":{"Line":7}},{"line":189,"address":[],"length":0,"stats":{"Line":4}},{"line":190,"address":[866757,866789,866821],"length":1,"stats":{"Line":4}},{"line":231,"address":[],"length":0,"stats":{"Line":3}},{"line":232,"address":[866853,866869,866885],"length":1,"stats":{"Line":3}},{"line":253,"address":[],"length":0,"stats":{"Line":1}},{"line":254,"address":[],"length":0,"stats":{"Line":4}},{"line":255,"address":[],"length":0,"stats":{"Line":2}},{"line":257,"address":[867002],"length":1,"stats":{"Line":1}},{"line":281,"address":[],"length":0,"stats":{"Line":0}},{"line":282,"address":[],"length":0,"stats":{"Line":0}},{"line":283,"address":[],"length":0,"stats":{"Line":0}},{"line":285,"address":[],"length":0,"stats":{"Line":0}},{"line":295,"address":[],"length":0,"stats":{"Line":4}},{"line":297,"address":[867234,868138,867650,867306,867722,868066],"length":1,"stats":{"Line":8}},{"line":301,"address":[867774,868190,867358,868240,867408,867824],"length":1,"stats":{"Line":8}},{"line":302,"address":[867872,867456,868288,868348,867516,867932],"length":1,"stats":{"Line":8}},{"line":305,"address":[867835,867419,868251],"length":1,"stats":{"Line":4}},{"line":310,"address":[868608,868432,868562,868755,868584,868733],"length":1,"stats":{"Line":3}},{"line":317,"address":[],"length":0,"stats":{"Line":2}},{"line":321,"address":[],"length":0,"stats":{"Line":4}},{"line":322,"address":[825328,825360],"length":1,"stats":{"Line":1}},{"line":323,"address":[825365,825333],"length":1,"stats":{"Line":1}},{"line":324,"address":[825346,825378],"length":1,"stats":{"Line":1}},{"line":329,"address":[],"length":0,"stats":{"Line":1}},{"line":331,"address":[868536,868707],"length":1,"stats":{"Line":1}},{"line":333,"address":[],"length":0,"stats":{"Line":0}},{"line":337,"address":[868930,868952,869160,868768,868976,869138],"length":1,"stats":{"Line":2}},{"line":344,"address":[868825,868782,869033,868990],"length":1,"stats":{"Line":4}},{"line":345,"address":[],"length":0,"stats":{"Line":1}},{"line":350,"address":[],"length":0,"stats":{"Line":2}},{"line":351,"address":[825552,825520],"length":1,"stats":{"Line":0}},{"line":352,"address":[825525,825557],"length":1,"stats":{"Line":0}},{"line":353,"address":[],"length":0,"stats":{"Line":0}},{"line":358,"address":[],"length":0,"stats":{"Line":1}},{"line":360,"address":[868904,869112],"length":1,"stats":{"Line":1}},{"line":362,"address":[869124,868916],"length":1,"stats":{"Line":1}},{"line":397,"address":[869328,869600,869578,869184,869312,869434,869456,869290,869472],"length":1,"stats":{"Line":3}},{"line":399,"address":[],"length":0,"stats":{"Line":3}},{"line":400,"address":[869412,869268,869556],"length":1,"stats":{"Line":3}},{"line":448,"address":[],"length":0,"stats":{"Line":0}},{"line":450,"address":[],"length":0,"stats":{"Line":0}},{"line":451,"address":[],"length":0,"stats":{"Line":0}},{"line":453,"address":[],"length":0,"stats":{"Line":0}},{"line":473,"address":[],"length":0,"stats":{"Line":1}},{"line":474,"address":[869630],"length":1,"stats":{"Line":1}},{"line":475,"address":[],"length":0,"stats":{"Line":0}},{"line":480,"address":[869712,870081],"length":1,"stats":{"Line":1}},{"line":482,"address":[869834,869762],"length":1,"stats":{"Line":2}},{"line":486,"address":[869886,869936],"length":1,"stats":{"Line":2}},{"line":487,"address":[],"length":0,"stats":{"Line":0}},{"line":490,"address":[869947],"length":1,"stats":{"Line":1}},{"line":493,"address":[870128,870288,870440,870275,870418,870253],"length":1,"stats":{"Line":3}},{"line":500,"address":[870142,870307],"length":1,"stats":{"Line":2}},{"line":504,"address":[],"length":0,"stats":{"Line":4}},{"line":505,"address":[],"length":0,"stats":{"Line":1}},{"line":506,"address":[],"length":0,"stats":{"Line":1}},{"line":507,"address":[825762,825730],"length":1,"stats":{"Line":1}},{"line":512,"address":[],"length":0,"stats":{"Line":1}},{"line":514,"address":[870227,870392],"length":1,"stats":{"Line":1}},{"line":516,"address":[],"length":0,"stats":{"Line":0}},{"line":520,"address":[870464,870672,870834,870856,870626,870648],"length":1,"stats":{"Line":2}},{"line":527,"address":[870478,870521,870686,870729],"length":1,"stats":{"Line":4}},{"line":528,"address":[],"length":0,"stats":{"Line":1}},{"line":533,"address":[],"length":0,"stats":{"Line":2}},{"line":534,"address":[],"length":0,"stats":{"Line":0}},{"line":535,"address":[825909,825941],"length":1,"stats":{"Line":0}},{"line":536,"address":[],"length":0,"stats":{"Line":0}},{"line":541,"address":[],"length":0,"stats":{"Line":1}},{"line":543,"address":[870808,870600],"length":1,"stats":{"Line":1}},{"line":545,"address":[],"length":0,"stats":{"Line":1}},{"line":582,"address":[],"length":0,"stats":{"Line":1}},{"line":584,"address":[],"length":0,"stats":{"Line":1}},{"line":585,"address":[],"length":0,"stats":{"Line":1}},{"line":626,"address":[],"length":0,"stats":{"Line":0}},{"line":628,"address":[],"length":0,"stats":{"Line":0}},{"line":629,"address":[],"length":0,"stats":{"Line":0}},{"line":631,"address":[],"length":0,"stats":{"Line":0}},{"line":649,"address":[],"length":0,"stats":{"Line":0}},{"line":650,"address":[],"length":0,"stats":{"Line":0}},{"line":651,"address":[],"length":0,"stats":{"Line":0}},{"line":671,"address":[],"length":0,"stats":{"Line":1}},{"line":672,"address":[],"length":0,"stats":{"Line":1}},{"line":698,"address":[],"length":0,"stats":{"Line":1}},{"line":699,"address":[],"length":0,"stats":{"Line":1}}],"covered":103,"coverable":128},{"path":["/","home","botahamec","Projects","happylock","src","poisonable.rs"],"content":"use std::marker::PhantomData;\nuse std::sync::atomic::AtomicBool;\n\nuse crate::ThreadKey;\n\nmod error;\nmod flag;\nmod guard;\nmod poisonable;\n\n/// A flag indicating if a lock is poisoned or not. The implementation differs\n/// depending on whether panics are set to unwind or abort.\n#[derive(Debug, Default)]\npub(crate) struct PoisonFlag(#[cfg(panic = \"unwind\")] AtomicBool);\n\n/// A wrapper around [`Lockable`] types which will enable poisoning.\n///\n/// A lock is \"poisoned\" when the thread panics while holding the lock. Once a\n/// lock is poisoned, all other threads are unable to access the data by\n/// default, because the data may be tainted (some invariant of the data might\n/// not be upheld).\n///\n/// The [`lock`] and [`try_lock`] methods return a [`Result`] which indicates\n/// whether the lock has been poisoned or not. The [`PoisonError`] type has an\n/// [`into_inner`] method which will return the guard that normally would have\n/// been returned for a successful lock. This allows access to the data,\n/// despite the lock being poisoned.\n///\n/// Alternatively, there is also a [`clear_poison`] method, which should\n/// indicate that all invariants of the underlying data are upheld, so that\n/// subsequent calls may still return [`Ok`].\n///\n/// [`Lockable`]: `crate::lockable::Lockable`\n/// [`lock`]: `Poisonable::lock`\n/// [`try_lock`]: `Poisonable::try_lock`\n/// [`into_inner`]: `PoisonError::into_inner`\n/// [`clear_poison`]: `Poisonable::clear_poison`\n#[derive(Debug, Default)]\npub struct Poisonable\u003cL\u003e {\n\tinner: L,\n\tpoisoned: PoisonFlag,\n}\n\n/// An RAII guard for a [`Poisonable`].\n///\n/// This is similar to a [`PoisonGuard`], except that it does not hold a\n/// [`Keyable`]\n///\n/// [`Keyable`]: `crate::Keyable`\npub struct PoisonRef\u003c'a, G\u003e {\n\tguard: G,\n\t#[cfg(panic = \"unwind\")]\n\tflag: \u0026'a PoisonFlag,\n\t_phantom: PhantomData\u003c\u0026'a ()\u003e,\n}\n\n/// An RAII guard for a [`Poisonable`].\n///\n/// This is created by calling methods like [`Poisonable::lock`].\npub struct PoisonGuard\u003c'a, G\u003e {\n\tguard: PoisonRef\u003c'a, G\u003e,\n\tkey: ThreadKey,\n}\n\n/// A type of error which can be returned when acquiring a [`Poisonable`] lock.\npub struct PoisonError\u003cGuard\u003e {\n\tguard: Guard,\n}\n\n/// An enumeration of possible errors associated with\n/// [`TryLockPoisonableResult`] which can occur while trying to acquire a lock\n/// (i.e.: [`Poisonable::try_lock`]).\npub enum TryLockPoisonableError\u003c'flag, G\u003e {\n\tPoisoned(PoisonError\u003cPoisonGuard\u003c'flag, G\u003e\u003e),\n\tWouldBlock(ThreadKey),\n}\n\n/// A type alias for the result of a lock method which can poisoned.\n///\n/// The [`Ok`] variant of this result indicates that the primitive was not\n/// poisoned, and the primitive was poisoned. Note that the [`Err`] variant\n/// *also* carries the associated guard, and it can be acquired through the\n/// [`into_inner`] method.\n///\n/// [`into_inner`]: `PoisonError::into_inner`\npub type PoisonResult\u003cGuard\u003e = Result\u003cGuard, PoisonError\u003cGuard\u003e\u003e;\n\n/// A type alias for the result of a nonblocking locking method.\n///\n/// For more information, see [`PoisonResult`]. A `TryLockPoisonableResult`\n/// doesn't necessarily hold the associated guard in the [`Err`] type as the\n/// lock might not have been acquired for other reasons.\npub type TryLockPoisonableResult\u003c'flag, G\u003e =\n\tResult\u003cPoisonGuard\u003c'flag, G\u003e, TryLockPoisonableError\u003c'flag, G\u003e\u003e;\n\n#[cfg(test)]\nmod tests {\n\tuse std::sync::Arc;\n\n\tuse super::*;\n\tuse crate::lockable::Lockable;\n\tuse crate::{LockCollection, Mutex, RwLock, ThreadKey};\n\n\t#[test]\n\tfn locking_poisoned_mutex_returns_error_in_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = LockCollection::new(Poisonable::new(Mutex::new(42)));\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet mut guard1 = mutex.lock(key);\n\t\t\t\tlet guard = guard1.as_deref_mut().unwrap();\n\t\t\t\tassert_eq!(**guard, 42);\n\t\t\t\tpanic!();\n\n\t\t\t\t#[allow(unreachable_code)]\n\t\t\t\tdrop(guard1);\n\t\t\t})\n\t\t\t.join()\n\t\t\t.unwrap_err();\n\t\t});\n\n\t\tlet error = mutex.lock(key);\n\t\tlet error = error.as_deref().unwrap_err();\n\t\tassert_eq!(***error.get_ref(), 42);\n\t}\n\n\t#[test]\n\tfn locking_poisoned_rwlock_returns_error_in_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = LockCollection::new(Poisonable::new(RwLock::new(42)));\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet mut guard1 = mutex.read(key);\n\t\t\t\tlet guard = guard1.as_deref_mut().unwrap();\n\t\t\t\tassert_eq!(**guard, 42);\n\t\t\t\tpanic!();\n\n\t\t\t\t#[allow(unreachable_code)]\n\t\t\t\tdrop(guard1);\n\t\t\t})\n\t\t\t.join()\n\t\t\t.unwrap_err();\n\t\t});\n\n\t\tlet error = mutex.read(key);\n\t\tlet error = error.as_deref().unwrap_err();\n\t\tassert_eq!(***error.get_ref(), 42);\n\t}\n\n\t#[test]\n\tfn non_poisoned_get_mut_is_ok() {\n\t\tlet mut mutex = Poisonable::new(Mutex::new(42));\n\t\tlet guard = mutex.get_mut();\n\t\tassert!(guard.is_ok());\n\t\tassert_eq!(*guard.unwrap(), 42);\n\t}\n\n\t#[test]\n\tfn non_poisoned_get_mut_is_err() {\n\t\tlet mut mutex = Poisonable::new(Mutex::new(42));\n\n\t\tlet _ = std::panic::catch_unwind(|| {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t#[allow(unused_variables)]\n\t\t\tlet guard = mutex.lock(key);\n\t\t\tpanic!();\n\t\t\t#[allow(unreachable_code)]\n\t\t\tdrop(guard);\n\t\t});\n\n\t\tlet guard = mutex.get_mut();\n\t\tassert!(guard.is_err());\n\t\tassert_eq!(**guard.unwrap_err().get_ref(), 42);\n\t}\n\n\t#[test]\n\tfn unpoisoned_into_inner() {\n\t\tlet mutex = Poisonable::new(Mutex::new(\"foo\"));\n\t\tassert_eq!(mutex.into_inner().unwrap(), \"foo\");\n\t}\n\n\t#[test]\n\tfn poisoned_into_inner() {\n\t\tlet mutex = Poisonable::from(Mutex::new(\"foo\"));\n\n\t\tstd::panic::catch_unwind(|| {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t#[allow(unused_variables)]\n\t\t\tlet guard = mutex.lock(key);\n\t\t\tpanic!();\n\t\t\t#[allow(unreachable_code)]\n\t\t\tdrop(guard);\n\t\t})\n\t\t.unwrap_err();\n\n\t\tlet error = mutex.into_inner().unwrap_err();\n\t\tassert_eq!(error.into_inner(), \"foo\");\n\t}\n\n\t#[test]\n\tfn unpoisoned_into_child() {\n\t\tlet mutex = Poisonable::new(Mutex::new(\"foo\"));\n\t\tassert_eq!(mutex.into_child().unwrap().into_inner(), \"foo\");\n\t}\n\n\t#[test]\n\tfn poisoned_into_child() {\n\t\tlet mutex = Poisonable::from(Mutex::new(\"foo\"));\n\n\t\tstd::panic::catch_unwind(|| {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t#[allow(unused_variables)]\n\t\t\tlet guard = mutex.lock(key);\n\t\t\tpanic!();\n\t\t\t#[allow(unreachable_code)]\n\t\t\tdrop(guard);\n\t\t})\n\t\t.unwrap_err();\n\n\t\tlet error = mutex.into_child().unwrap_err();\n\t\tassert_eq!(error.into_inner().into_inner(), \"foo\");\n\t}\n\n\t#[test]\n\tfn scoped_lock_can_poison() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = Poisonable::new(Mutex::new(42));\n\n\t\tlet r = std::panic::catch_unwind(|| {\n\t\t\tmutex.scoped_lock(key, |num| {\n\t\t\t\t*num.unwrap() = 56;\n\t\t\t\tpanic!();\n\t\t\t})\n\t\t});\n\t\tassert!(r.is_err());\n\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tassert!(mutex.is_poisoned());\n\t\tmutex.scoped_lock(key, |num| {\n\t\t\tlet Err(error) = num else { panic!() };\n\t\t\tmutex.clear_poison();\n\t\t\tlet guard = error.into_inner();\n\t\t\tassert_eq!(*guard, 56);\n\t\t});\n\t\tassert!(!mutex.is_poisoned());\n\t}\n\n\t#[test]\n\tfn scoped_try_lock_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = Poisonable::new(Mutex::new(42));\n\t\tlet guard = mutex.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = mutex.scoped_try_lock(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn scoped_try_lock_can_succeed() {\n\t\tlet rwlock = Poisonable::new(RwLock::new(42));\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = rwlock.scoped_try_lock(key, |guard| {\n\t\t\t\t\tassert_eq!(*guard.unwrap(), 42);\n\t\t\t\t});\n\t\t\t\tassert!(r.is_ok());\n\t\t\t});\n\t\t});\n\t}\n\n\t#[test]\n\tfn scoped_read_can_poison() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = Poisonable::new(RwLock::new(42));\n\n\t\tlet r = std::panic::catch_unwind(|| {\n\t\t\tmutex.scoped_read(key, |num| {\n\t\t\t\tassert_eq!(*num.unwrap(), 42);\n\t\t\t\tpanic!();\n\t\t\t})\n\t\t});\n\t\tassert!(r.is_err());\n\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tassert!(mutex.is_poisoned());\n\t\tmutex.scoped_read(key, |num| {\n\t\t\tlet Err(error) = num else { panic!() };\n\t\t\tmutex.clear_poison();\n\t\t\tlet guard = error.into_inner();\n\t\t\tassert_eq!(*guard, 42);\n\t\t});\n\t\tassert!(!mutex.is_poisoned());\n\t}\n\n\t#[test]\n\tfn scoped_try_read_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet rwlock = Poisonable::new(RwLock::new(42));\n\t\tlet guard = rwlock.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = rwlock.scoped_try_read(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn scoped_try_read_can_succeed() {\n\t\tlet rwlock = Poisonable::new(RwLock::new(42));\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = rwlock.scoped_try_read(key, |guard| {\n\t\t\t\t\tassert_eq!(*guard.unwrap(), 42);\n\t\t\t\t});\n\t\t\t\tassert!(r.is_ok());\n\t\t\t});\n\t\t});\n\t}\n\n\t#[test]\n\tfn display_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = Poisonable::new(Mutex::new(\"Hello, world!\"));\n\n\t\tlet guard = mutex.lock(key).unwrap();\n\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\");\n\t}\n\n\t#[test]\n\tfn ref_as_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = LockCollection::new(Poisonable::new(Mutex::new(\"foo\")));\n\t\tlet guard = collection.lock(key);\n\t\tlet Ok(ref guard) = guard.as_ref() else {\n\t\t\tpanic!()\n\t\t};\n\t\tassert_eq!(**guard.as_ref(), \"foo\");\n\t}\n\n\t#[test]\n\tfn ref_as_mut() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = LockCollection::new(Poisonable::new(Mutex::new(\"foo\")));\n\t\tlet mut guard1 = collection.lock(key);\n\t\tlet Ok(ref mut guard) = guard1.as_mut() else {\n\t\t\tpanic!()\n\t\t};\n\t\tlet guard = guard.as_mut();\n\t\t**guard = \"bar\";\n\n\t\tlet key = LockCollection::\u003cPoisonable\u003cMutex\u003c_\u003e\u003e\u003e::unlock(guard1);\n\t\tlet guard = collection.lock(key);\n\t\tlet guard = guard.as_deref().unwrap();\n\t\tassert_eq!(*guard.as_ref(), \"bar\");\n\t}\n\n\t#[test]\n\tfn guard_as_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = Poisonable::new(Mutex::new(\"foo\"));\n\t\tlet guard = collection.lock(key);\n\t\tlet Ok(ref guard) = guard.as_ref() else {\n\t\t\tpanic!()\n\t\t};\n\t\tassert_eq!(**guard.as_ref(), \"foo\");\n\t}\n\n\t#[test]\n\tfn guard_as_mut() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = Poisonable::new(Mutex::new(\"foo\"));\n\t\tlet mut guard1 = mutex.lock(key);\n\t\tlet Ok(ref mut guard) = guard1.as_mut() else {\n\t\t\tpanic!()\n\t\t};\n\t\tlet guard = guard.as_mut();\n\t\t**guard = \"bar\";\n\n\t\tlet key = Poisonable::\u003cMutex\u003c_\u003e\u003e::unlock(guard1.unwrap());\n\t\tlet guard = mutex.lock(key);\n\t\tlet guard = guard.as_deref().unwrap();\n\t\tassert_eq!(*guard, \"bar\");\n\t}\n\n\t#[test]\n\tfn deref_mut_in_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = LockCollection::new(Poisonable::new(Mutex::new(42)));\n\t\tlet mut guard1 = collection.lock(key);\n\t\tlet Ok(ref mut guard) = guard1.as_mut() else {\n\t\t\tpanic!()\n\t\t};\n\t\t// TODO make this more convenient\n\t\tassert_eq!(***guard, 42);\n\t\t***guard = 24;\n\n\t\tlet key = LockCollection::\u003cPoisonable\u003cMutex\u003c_\u003e\u003e\u003e::unlock(guard1);\n\t\t_ = collection.lock(key);\n\t}\n\n\t#[test]\n\tfn get_ptrs() {\n\t\tlet mutex = Mutex::new(5);\n\t\tlet poisonable = Poisonable::new(mutex);\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tpoisonable.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tassert!(std::ptr::addr_eq(lock_ptrs[0], \u0026poisonable.inner));\n\t}\n\n\t#[test]\n\tfn clear_poison_for_poisoned_mutex() {\n\t\tlet mutex = Arc::new(Poisonable::new(Mutex::new(0)));\n\t\tlet c_mutex = Arc::clone(\u0026mutex);\n\n\t\tlet _ = std::thread::spawn(move || {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\tlet _lock = c_mutex.lock(key).unwrap();\n\t\t\tpanic!(); // the mutex gets poisoned\n\t\t})\n\t\t.join();\n\n\t\tassert!(mutex.is_poisoned());\n\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet _ = mutex.lock(key).unwrap_or_else(|mut e| {\n\t\t\t**e.get_mut() = 1;\n\t\t\tmutex.clear_poison();\n\t\t\te.into_inner()\n\t\t});\n\n\t\tassert!(!mutex.is_poisoned());\n\t}\n\n\t#[test]\n\tfn clear_poison_for_poisoned_rwlock() {\n\t\tlet lock = Arc::new(Poisonable::new(RwLock::new(0)));\n\t\tlet c_mutex = Arc::clone(\u0026lock);\n\n\t\tlet _ = std::thread::spawn(move || {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\tlet lock = c_mutex.read(key).unwrap();\n\t\t\tassert_eq!(*lock, 42);\n\t\t\tpanic!(); // the mutex gets poisoned\n\t\t})\n\t\t.join();\n\n\t\tassert!(lock.is_poisoned());\n\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet _ = lock.lock(key).unwrap_or_else(|mut e| {\n\t\t\t**e.get_mut() = 1;\n\t\t\tlock.clear_poison();\n\t\t\te.into_inner()\n\t\t});\n\n\t\tassert!(!lock.is_poisoned());\n\t}\n\n\t#[test]\n\tfn error_as_ref() {\n\t\tlet mutex = Poisonable::new(Mutex::new(\"foo\"));\n\n\t\tlet _ = std::panic::catch_unwind(|| {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t#[allow(unused_variables)]\n\t\t\tlet guard = mutex.lock(key);\n\t\t\tpanic!();\n\n\t\t\t#[allow(unknown_lints)]\n\t\t\t#[allow(unreachable_code)]\n\t\t\tdrop(guard);\n\t\t});\n\n\t\tassert!(mutex.is_poisoned());\n\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet error = mutex.lock(key).unwrap_err();\n\t\tassert_eq!(\u0026***error.as_ref(), \"foo\");\n\t}\n\n\t#[test]\n\tfn error_as_mut() {\n\t\tlet mutex = Poisonable::new(Mutex::new(\"foo\"));\n\n\t\tlet _ = std::panic::catch_unwind(|| {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t#[allow(unused_variables)]\n\t\t\tlet guard = mutex.lock(key);\n\t\t\tpanic!();\n\n\t\t\t#[allow(unknown_lints)]\n\t\t\t#[allow(unreachable_code)]\n\t\t\tdrop(guard);\n\t\t});\n\n\t\tassert!(mutex.is_poisoned());\n\n\t\tlet key: ThreadKey = ThreadKey::get().unwrap();\n\t\tlet mut error = mutex.lock(key).unwrap_err();\n\t\tlet error1 = error.as_mut();\n\t\t**error1 = \"bar\";\n\t\tlet key = Poisonable::\u003cMutex\u003c_\u003e\u003e::unlock(error.into_inner());\n\n\t\tmutex.clear_poison();\n\t\tlet guard = mutex.lock(key).unwrap();\n\t\tassert_eq!(\u0026**guard, \"bar\");\n\t}\n\n\t#[test]\n\tfn try_error_from_lock_error() {\n\t\tlet mutex = Poisonable::new(Mutex::new(\"foo\"));\n\n\t\tlet _ = std::panic::catch_unwind(|| {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t#[allow(unused_variables)]\n\t\t\tlet guard = mutex.lock(key);\n\t\t\tpanic!();\n\n\t\t\t#[allow(unknown_lints)]\n\t\t\t#[allow(unreachable_code)]\n\t\t\tdrop(guard);\n\t\t});\n\n\t\tassert!(mutex.is_poisoned());\n\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet error = mutex.lock(key).unwrap_err();\n\t\tlet error = TryLockPoisonableError::from(error);\n\n\t\tlet TryLockPoisonableError::Poisoned(error) = error else {\n\t\t\tpanic!()\n\t\t};\n\t\tassert_eq!(\u0026**error.into_inner(), \"foo\");\n\t}\n\n\t#[test]\n\tfn new_poisonable_is_not_poisoned() {\n\t\tlet mutex = Poisonable::new(Mutex::new(42));\n\t\tassert!(!mutex.is_poisoned());\n\t}\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","src","rwlock","read_guard.rs"],"content":"use std::fmt::{Debug, Display};\nuse std::hash::Hash;\nuse std::marker::PhantomData;\nuse std::ops::Deref;\n\nuse lock_api::RawRwLock;\n\nuse crate::lockable::RawLock;\nuse crate::ThreadKey;\n\nuse super::{RwLock, RwLockReadGuard, RwLockReadRef};\n\n// These impls make things slightly easier because now you can use\n// `println!(\"{guard}\")` instead of `println!(\"{}\", *guard)`\n\n#[mutants::skip] // hashing involves PRNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Hash + ?Sized, R: RawRwLock\u003e Hash for RwLockReadRef\u003c'_, T, R\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.deref().hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug + ?Sized, R: RawRwLock\u003e Debug for RwLockReadRef\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: Display + ?Sized, R: RawRwLock\u003e Display for RwLockReadRef\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e Deref for RwLockReadRef\u003c'_, T, R\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t// safety: this is the only type that can use `value`, and there's\n\t\t// a reference to this type, so there cannot be any mutable\n\t\t// references to this value.\n\t\tunsafe { \u0026*self.0.data.get() }\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e AsRef\u003cT\u003e for RwLockReadRef\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e Drop for RwLockReadRef\u003c'_, T, R\u003e {\n\tfn drop(\u0026mut self) {\n\t\t// safety: this guard is being destroyed, so the data cannot be\n\t\t// accessed without locking again\n\t\tunsafe { self.0.raw_unlock_read() }\n\t}\n}\n\nimpl\u003c'a, T: ?Sized, R: RawRwLock\u003e RwLockReadRef\u003c'a, T, R\u003e {\n\t/// Creates an immutable reference for the underlying data of an [`RwLock`]\n\t/// without locking it or taking ownership of the key.\n\t#[must_use]\n\tpub(crate) const unsafe fn new(mutex: \u0026'a RwLock\u003cT, R\u003e) -\u003e Self {\n\t\tSelf(mutex, PhantomData)\n\t}\n}\n\n#[mutants::skip] // hashing involves PRNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Hash + ?Sized, R: RawRwLock\u003e Hash for RwLockReadGuard\u003c'_, T, R\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.deref().hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug + ?Sized, R: RawRwLock\u003e Debug for RwLockReadGuard\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: Display + ?Sized, R: RawRwLock\u003e Display for RwLockReadGuard\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e Deref for RwLockReadGuard\u003c'_, T, R\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t\u0026self.rwlock\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e AsRef\u003cT\u003e for RwLockReadGuard\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself\n\t}\n}\n\nimpl\u003c'a, T: ?Sized, R: RawRwLock\u003e RwLockReadGuard\u003c'a, T, R\u003e {\n\t/// Create a guard to the given mutex. Undefined if multiple guards to the\n\t/// same mutex exist at once.\n\t#[must_use]\n\tpub(super) const unsafe fn new(rwlock: \u0026'a RwLock\u003cT, R\u003e, thread_key: ThreadKey) -\u003e Self {\n\t\tSelf {\n\t\t\trwlock: RwLockReadRef(rwlock, PhantomData),\n\t\t\tthread_key,\n\t\t}\n\t}\n}\n\nunsafe impl\u003cT: ?Sized + Sync, R: RawRwLock + Sync\u003e Sync for RwLockReadRef\u003c'_, T, R\u003e {}\n","traces":[{"line":33,"address":[970880],"length":1,"stats":{"Line":1}},{"line":34,"address":[],"length":0,"stats":{"Line":1}},{"line":41,"address":[],"length":0,"stats":{"Line":3}},{"line":45,"address":[970933,970965],"length":1,"stats":{"Line":3}},{"line":50,"address":[],"length":0,"stats":{"Line":1}},{"line":51,"address":[],"length":0,"stats":{"Line":1}},{"line":56,"address":[240432,240416],"length":1,"stats":{"Line":3}},{"line":59,"address":[149253,149269,149285],"length":1,"stats":{"Line":3}},{"line":67,"address":[],"length":0,"stats":{"Line":3}},{"line":68,"address":[],"length":0,"stats":{"Line":0}},{"line":89,"address":[],"length":0,"stats":{"Line":1}},{"line":90,"address":[],"length":0,"stats":{"Line":1}},{"line":97,"address":[],"length":0,"stats":{"Line":1}},{"line":98,"address":[],"length":0,"stats":{"Line":1}},{"line":103,"address":[],"length":0,"stats":{"Line":1}},{"line":104,"address":[],"length":0,"stats":{"Line":1}},{"line":112,"address":[],"length":0,"stats":{"Line":1}},{"line":114,"address":[],"length":0,"stats":{"Line":0}}],"covered":16,"coverable":18},{"path":["/","home","botahamec","Projects","happylock","src","rwlock","read_lock.rs"],"content":"use std::fmt::Debug;\n\nuse lock_api::RawRwLock;\n\nuse crate::lockable::{Lockable, RawLock, Sharable};\nuse crate::{Keyable, ThreadKey};\n\nuse super::{ReadLock, RwLock, RwLockReadGuard, RwLockReadRef};\n\nunsafe impl\u003cT, R: RawRwLock\u003e RawLock for ReadLock\u003c'_, T, R\u003e {\n\tfn poison(\u0026self) {\n\t\tself.0.poison()\n\t}\n\n\tunsafe fn raw_write(\u0026self) {\n\t\tself.0.raw_read()\n\t}\n\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool {\n\t\tself.0.raw_try_read()\n\t}\n\n\tunsafe fn raw_unlock_write(\u0026self) {\n\t\tself.0.raw_unlock_read()\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tself.0.raw_read()\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tself.0.raw_try_read()\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tself.0.raw_unlock_read()\n\t}\n}\n\nunsafe impl\u003cT, R: RawRwLock\u003e Lockable for ReadLock\u003c'_, T, R\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= RwLockReadRef\u003c'g, T, R\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= \u0026'a T\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tptrs.push(self);\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tRwLockReadRef::new(self.as_ref())\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.0.data_ref()\n\t}\n}\n\nunsafe impl\u003cT, R: RawRwLock\u003e Sharable for ReadLock\u003c'_, T, R\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= RwLockReadRef\u003c'g, T, R\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= \u0026'a T\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tRwLockReadRef::new(self.as_ref())\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tself.0.data_ref()\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug, R: RawRwLock\u003e Debug for ReadLock\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t// safety: this is just a try lock, and the value is dropped\n\t\t// immediately after, so there's no risk of blocking ourselves\n\t\t// or any other threads\n\t\tif let Some(value) = unsafe { self.try_lock_no_key() } {\n\t\t\tf.debug_struct(\"ReadLock\").field(\"data\", \u0026\u0026*value).finish()\n\t\t} else {\n\t\t\tstruct LockedPlaceholder;\n\t\t\timpl Debug for LockedPlaceholder {\n\t\t\t\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t\t\t\tf.write_str(\"\u003clocked\u003e\")\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tf.debug_struct(\"ReadLock\")\n\t\t\t\t.field(\"data\", \u0026LockedPlaceholder)\n\t\t\t\t.finish()\n\t\t}\n\t}\n}\n\nimpl\u003c'l, T, R\u003e From\u003c\u0026'l RwLock\u003cT, R\u003e\u003e for ReadLock\u003c'l, T, R\u003e {\n\tfn from(value: \u0026'l RwLock\u003cT, R\u003e) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\nimpl\u003cT: ?Sized, R\u003e AsRef\u003cRwLock\u003cT, R\u003e\u003e for ReadLock\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026RwLock\u003cT, R\u003e {\n\t\tself.0\n\t}\n}\n\nimpl\u003c'l, T, R\u003e ReadLock\u003c'l, T, R\u003e {\n\t/// Creates a new `ReadLock` which accesses the given [`RwLock`]\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{rwlock::ReadLock, RwLock};\n\t///\n\t/// let lock = RwLock::new(5);\n\t/// let read_lock = ReadLock::new(\u0026lock);\n\t/// ```\n\t#[must_use]\n\tpub const fn new(rwlock: \u0026'l RwLock\u003cT, R\u003e) -\u003e Self {\n\t\tSelf(rwlock)\n\t}\n}\n\nimpl\u003cT, R: RawRwLock\u003e ReadLock\u003c'_, T, R\u003e {\n\tpub fn scoped_lock\u003c'a, Ret\u003e(\u0026'a self, key: impl Keyable, f: impl Fn(\u0026'a T) -\u003e Ret) -\u003e Ret {\n\t\tself.0.scoped_read(key, f)\n\t}\n\n\tpub fn scoped_try_lock\u003c'a, Key: Keyable, Ret\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(\u0026'a T) -\u003e Ret,\n\t) -\u003e Result\u003cRet, Key\u003e {\n\t\tself.0.scoped_try_read(key, f)\n\t}\n\n\t/// Locks the underlying [`RwLock`] with shared read access, blocking the\n\t/// current thread until it can be acquired.\n\t///\n\t/// The calling thread will be blocked until there are no more writers\n\t/// which hold the lock. There may be other readers currently inside the\n\t/// lock when this method returns.\n\t///\n\t/// Returns an RAII guard which will release this thread's shared access\n\t/// once it is dropped.\n\t///\n\t/// Because this method takes a [`ThreadKey`], it's not possible for this\n\t/// method to cause a deadlock.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::rwlock::ReadLock;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock: RwLock\u003c_\u003e = RwLock::new(1);\n\t/// let reader = ReadLock::new(\u0026lock);\n\t///\n\t/// let n = reader.lock(key);\n\t/// assert_eq!(*n, 1);\n\t/// ```\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\t#[must_use]\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e RwLockReadGuard\u003c'_, T, R\u003e {\n\t\tself.0.read(key)\n\t}\n\n\t/// Attempts to acquire the underlying [`RwLock`] with shared read access\n\t/// without blocking.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// shared access when it is dropped.\n\t///\n\t/// This function does not provide any guarantees with respect to the\n\t/// ordering of whether contentious readers or writers will acquire the\n\t/// lock first.\n\t///\n\t/// # Errors\n\t///\n\t/// If the `RwLock` could not be acquired because it was already locked\n\t/// exclusively, then an error will be returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::rwlock::ReadLock;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(1);\n\t/// let reader = ReadLock::new(\u0026lock);\n\t///\n\t/// match reader.try_lock(key) {\n\t/// Ok(n) =\u003e assert_eq!(*n, 1),\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t/// ```\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e Result\u003cRwLockReadGuard\u003c'_, T, R\u003e, ThreadKey\u003e {\n\t\tself.0.try_read(key)\n\t}\n\n\t/// Attempts to create an exclusive lock without a key. Locking this\n\t/// without exclusive access to the key is undefined behavior.\n\tpub(crate) unsafe fn try_lock_no_key(\u0026self) -\u003e Option\u003cRwLockReadRef\u003c'_, T, R\u003e\u003e {\n\t\tself.0.try_read_no_key()\n\t}\n\n\t/// Immediately drops the guard, and consequently releases the shared lock\n\t/// on the underlying [`RwLock`].\n\t///\n\t/// This function is equivalent to calling [`drop`] on the guard, except\n\t/// that it returns the key that was used to create it. Alternately, the\n\t/// guard will be automatically dropped when it goes out of scope.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::rwlock::ReadLock;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(0);\n\t/// let reader = ReadLock::new(\u0026lock);\n\t///\n\t/// let mut guard = reader.lock(key);\n\t/// assert_eq!(*guard, 0);\n\t/// let key = ReadLock::unlock(guard);\n\t/// ```\n\t#[must_use]\n\tpub fn unlock(guard: RwLockReadGuard\u003c'_, T, R\u003e) -\u003e ThreadKey {\n\t\tRwLock::unlock_read(guard)\n\t}\n}\n","traces":[{"line":11,"address":[969488,969504],"length":1,"stats":{"Line":1}},{"line":12,"address":[],"length":0,"stats":{"Line":1}},{"line":15,"address":[],"length":0,"stats":{"Line":1}},{"line":16,"address":[],"length":0,"stats":{"Line":1}},{"line":19,"address":[969552,969584],"length":1,"stats":{"Line":1}},{"line":20,"address":[],"length":0,"stats":{"Line":1}},{"line":23,"address":[969616,969632],"length":1,"stats":{"Line":1}},{"line":24,"address":[],"length":0,"stats":{"Line":1}},{"line":27,"address":[],"length":0,"stats":{"Line":1}},{"line":28,"address":[],"length":0,"stats":{"Line":1}},{"line":31,"address":[],"length":0,"stats":{"Line":1}},{"line":32,"address":[],"length":0,"stats":{"Line":1}},{"line":35,"address":[],"length":0,"stats":{"Line":1}},{"line":36,"address":[],"length":0,"stats":{"Line":1}},{"line":51,"address":[],"length":0,"stats":{"Line":2}},{"line":52,"address":[969801,969865],"length":1,"stats":{"Line":2}},{"line":55,"address":[],"length":0,"stats":{"Line":1}},{"line":56,"address":[],"length":0,"stats":{"Line":1}},{"line":59,"address":[],"length":0,"stats":{"Line":1}},{"line":60,"address":[],"length":0,"stats":{"Line":1}},{"line":75,"address":[],"length":0,"stats":{"Line":1}},{"line":76,"address":[],"length":0,"stats":{"Line":1}},{"line":79,"address":[],"length":0,"stats":{"Line":1}},{"line":80,"address":[],"length":0,"stats":{"Line":1}},{"line":109,"address":[],"length":0,"stats":{"Line":1}},{"line":110,"address":[],"length":0,"stats":{"Line":1}},{"line":115,"address":[],"length":0,"stats":{"Line":1}},{"line":116,"address":[],"length":0,"stats":{"Line":1}},{"line":132,"address":[970048,970032],"length":1,"stats":{"Line":2}},{"line":133,"address":[],"length":0,"stats":{"Line":0}},{"line":138,"address":[],"length":0,"stats":{"Line":1}},{"line":139,"address":[],"length":0,"stats":{"Line":1}},{"line":142,"address":[970096],"length":1,"stats":{"Line":1}},{"line":147,"address":[970105],"length":1,"stats":{"Line":1}},{"line":181,"address":[],"length":0,"stats":{"Line":1}},{"line":182,"address":[],"length":0,"stats":{"Line":1}},{"line":216,"address":[],"length":0,"stats":{"Line":1}},{"line":217,"address":[],"length":0,"stats":{"Line":1}},{"line":222,"address":[],"length":0,"stats":{"Line":0}},{"line":223,"address":[],"length":0,"stats":{"Line":0}},{"line":248,"address":[],"length":0,"stats":{"Line":1}},{"line":249,"address":[],"length":0,"stats":{"Line":1}}],"covered":39,"coverable":42},{"path":["/","home","botahamec","Projects","happylock","src","rwlock","rwlock.rs"],"content":"use std::cell::UnsafeCell;\nuse std::fmt::Debug;\nuse std::marker::PhantomData;\nuse std::panic::AssertUnwindSafe;\n\nuse lock_api::RawRwLock;\n\nuse crate::collection::utils;\nuse crate::handle_unwind::handle_unwind;\nuse crate::lockable::{\n\tLockable, LockableGetMut, LockableIntoInner, OwnedLockable, RawLock, Sharable,\n};\nuse crate::{Keyable, ThreadKey};\n\nuse super::{PoisonFlag, RwLock, RwLockReadGuard, RwLockReadRef, RwLockWriteGuard, RwLockWriteRef};\n\nunsafe impl\u003cT: ?Sized, R: RawRwLock\u003e RawLock for RwLock\u003cT, R\u003e {\n\tfn poison(\u0026self) {\n\t\tself.poison.poison();\n\t}\n\n\tunsafe fn raw_write(\u0026self) {\n\t\tassert!(\n\t\t\t!self.poison.is_poisoned(),\n\t\t\t\"The read-write lock has been killed\"\n\t\t);\n\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.lock_exclusive(), || self.poison())\n\t}\n\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool {\n\t\tif self.poison.is_poisoned() {\n\t\t\treturn false;\n\t\t}\n\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.try_lock_exclusive(), || self.poison())\n\t}\n\n\tunsafe fn raw_unlock_write(\u0026self) {\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.unlock_exclusive(), || self.poison())\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tassert!(\n\t\t\t!self.poison.is_poisoned(),\n\t\t\t\"The read-write lock has been killed\"\n\t\t);\n\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.lock_shared(), || self.poison())\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tif self.poison.is_poisoned() {\n\t\t\treturn false;\n\t\t}\n\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.try_lock_shared(), || self.poison())\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.unlock_shared(), || self.poison())\n\t}\n}\n\nunsafe impl\u003cT, R: RawRwLock\u003e Lockable for RwLock\u003cT, R\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= RwLockWriteRef\u003c'g, T, R\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= \u0026'a mut T\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tptrs.push(self);\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tRwLockWriteRef::new(self)\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.data.get().as_mut().unwrap_unchecked()\n\t}\n}\n\nunsafe impl\u003cT, R: RawRwLock\u003e Sharable for RwLock\u003cT, R\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= RwLockReadRef\u003c'g, T, R\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= \u0026'a T\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tRwLockReadRef::new(self)\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tself.data.get().as_ref().unwrap_unchecked()\n\t}\n}\n\nunsafe impl\u003cT: Send, R: RawRwLock\u003e OwnedLockable for RwLock\u003cT, R\u003e {}\n\nimpl\u003cT: Send, R: RawRwLock\u003e LockableIntoInner for RwLock\u003cT, R\u003e {\n\ttype Inner = T;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tself.into_inner()\n\t}\n}\n\nimpl\u003cT: Send, R: RawRwLock\u003e LockableGetMut for RwLock\u003cT, R\u003e {\n\ttype Inner\u003c'a\u003e\n\t\t= \u0026'a mut T\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tAsMut::as_mut(self)\n\t}\n}\n\nimpl\u003cT, R: RawRwLock\u003e RwLock\u003cT, R\u003e {\n\t/// Creates a new instance of an `RwLock\u003cT\u003e` which is unlocked.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::RwLock;\n\t///\n\t/// let lock = RwLock::new(5);\n\t/// ```\n\t#[must_use]\n\tpub const fn new(data: T) -\u003e Self {\n\t\tSelf {\n\t\t\tdata: UnsafeCell::new(data),\n\t\t\tpoison: PoisonFlag::new(),\n\t\t\traw: R::INIT,\n\t\t}\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug, R: RawRwLock\u003e Debug for RwLock\u003cT, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t// safety: this is just a try lock, and the value is dropped\n\t\t// immediately after, so there's no risk of blocking ourselves\n\t\t// or any other threads\n\t\tif let Some(value) = unsafe { self.try_read_no_key() } {\n\t\t\tf.debug_struct(\"RwLock\").field(\"data\", \u0026\u0026*value).finish()\n\t\t} else {\n\t\t\tstruct LockedPlaceholder;\n\t\t\timpl Debug for LockedPlaceholder {\n\t\t\t\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t\t\t\tf.write_str(\"\u003clocked\u003e\")\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tf.debug_struct(\"RwLock\")\n\t\t\t\t.field(\"data\", \u0026LockedPlaceholder)\n\t\t\t\t.finish()\n\t\t}\n\t}\n}\n\nimpl\u003cT: Default, R: RawRwLock\u003e Default for RwLock\u003cT, R\u003e {\n\tfn default() -\u003e Self {\n\t\tSelf::new(T::default())\n\t}\n}\n\nimpl\u003cT, R: RawRwLock\u003e From\u003cT\u003e for RwLock\u003cT, R\u003e {\n\tfn from(value: T) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\n// We don't need a `get_mut` because we don't have mutex poisoning. Hurray!\n// This is safe because you can't have a mutable reference to the lock if it's\n// locked. Being locked requires an immutable reference because of the guard.\nimpl\u003cT: ?Sized, R\u003e AsMut\u003cT\u003e for RwLock\u003cT, R\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself.data.get_mut()\n\t}\n}\n\nimpl\u003cT, R\u003e RwLock\u003cT, R\u003e {\n\t/// Consumes this `RwLock`, returning the underlying data.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t///\n\t/// let lock = RwLock::new(String::new());\n\t/// {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut s = lock.write(key);\n\t/// *s = \"modified\".to_owned();\n\t/// }\n\t/// assert_eq!(lock.into_inner(), \"modified\");\n\t/// ```\n\t#[must_use]\n\tpub fn into_inner(self) -\u003e T {\n\t\tself.data.into_inner()\n\t}\n}\n\nimpl\u003cT: ?Sized, R\u003e RwLock\u003cT, R\u003e {\n\t/// Returns a mutable reference to the underlying data.\n\t///\n\t/// Since this call borrows `RwLock` mutably, no actual locking is taking\n\t/// place. The mutable borrow statically guarantees that no locks exist.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, RwLock};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut mutex = RwLock::new(0);\n\t/// *mutex.get_mut() = 10;\n\t/// assert_eq!(*mutex.read(key), 10);\n\t/// ```\n\t#[must_use]\n\tpub fn get_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself.data.get_mut()\n\t}\n}\n\nimpl\u003cT, R: RawRwLock\u003e RwLock\u003cT, R\u003e {\n\tpub fn scoped_read\u003c'a, Ret\u003e(\u0026'a self, key: impl Keyable, f: impl Fn(\u0026'a T) -\u003e Ret) -\u003e Ret {\n\t\tutils::scoped_read(self, key, f)\n\t}\n\n\tpub fn scoped_try_read\u003c'a, Key: Keyable, Ret\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(\u0026'a T) -\u003e Ret,\n\t) -\u003e Result\u003cRet, Key\u003e {\n\t\tutils::scoped_try_read(self, key, f)\n\t}\n\n\tpub fn scoped_write\u003c'a, Ret\u003e(\u0026'a self, key: impl Keyable, f: impl Fn(\u0026'a mut T) -\u003e Ret) -\u003e Ret {\n\t\tutils::scoped_write(self, key, f)\n\t}\n\n\tpub fn scoped_try_write\u003c'a, Key: Keyable, Ret\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(\u0026'a mut T) -\u003e Ret,\n\t) -\u003e Result\u003cRet, Key\u003e {\n\t\tutils::scoped_try_write(self, key, f)\n\t}\n\n\t/// Locks this `RwLock` with shared read access, blocking the current\n\t/// thread until it can be acquired.\n\t///\n\t/// The calling thread will be blocked until there are no more writers\n\t/// which hold the lock. There may be other readers currently inside the\n\t/// lock when this method returns.\n\t///\n\t/// Returns an RAII guard which will release this thread's shared access\n\t/// once it is dropped.\n\t///\n\t/// Because this method takes a [`ThreadKey`], it's not possible for this\n\t/// method to cause a deadlock.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t/// use happylock::{RwLock, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = Arc::new(RwLock::new(1));\n\t/// let c_lock = Arc::clone(\u0026lock);\n\t///\n\t/// let n = lock.read(key);\n\t/// assert_eq!(*n, 1);\n\t///\n\t/// thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let r = c_lock.read(key);\n\t/// }).join().unwrap();\n\t/// ```\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\tpub fn read(\u0026self, key: ThreadKey) -\u003e RwLockReadGuard\u003c'_, T, R\u003e {\n\t\tunsafe {\n\t\t\tself.raw_read();\n\n\t\t\t// safety: the lock is locked first\n\t\t\tRwLockReadGuard::new(self, key)\n\t\t}\n\t}\n\n\t/// Attempts to acquire this `RwLock` with shared read access without\n\t/// blocking.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// shared access when it is dropped.\n\t///\n\t/// This function does not provide any guarantees with respect to the\n\t/// ordering of whether contentious readers or writers will acquire the\n\t/// lock first.\n\t///\n\t/// # Errors\n\t///\n\t/// If the `RwLock` could not be acquired because it was already locked\n\t/// exclusively, then an error will be returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(1);\n\t///\n\t/// match lock.try_read(key) {\n\t/// Ok(n) =\u003e assert_eq!(*n, 1),\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t/// ```\n\tpub fn try_read(\u0026self, key: ThreadKey) -\u003e Result\u003cRwLockReadGuard\u003c'_, T, R\u003e, ThreadKey\u003e {\n\t\tunsafe {\n\t\t\tif self.raw_try_read() {\n\t\t\t\t// safety: the lock is locked first\n\t\t\t\tOk(RwLockReadGuard::new(self, key))\n\t\t\t} else {\n\t\t\t\tErr(key)\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Attempts to create a shared lock without a key. Locking this without\n\t/// exclusive access to the key is undefined behavior.\n\tpub(crate) unsafe fn try_read_no_key(\u0026self) -\u003e Option\u003cRwLockReadRef\u003c'_, T, R\u003e\u003e {\n\t\tif self.raw_try_read() {\n\t\t\t// safety: the lock is locked first\n\t\t\tSome(RwLockReadRef(self, PhantomData))\n\t\t} else {\n\t\t\tNone\n\t\t}\n\t}\n\n\t/// Attempts to create an exclusive lock without a key. Locking this\n\t/// without exclusive access to the key is undefined behavior.\n\t#[cfg(test)]\n\tpub(crate) unsafe fn try_write_no_key(\u0026self) -\u003e Option\u003cRwLockWriteRef\u003c'_, T, R\u003e\u003e {\n\t\tif self.raw_try_write() {\n\t\t\t// safety: the lock is locked first\n\t\t\tSome(RwLockWriteRef(self, PhantomData))\n\t\t} else {\n\t\t\tNone\n\t\t}\n\t}\n\n\t/// Locks this `RwLock` with exclusive write access, blocking the current\n\t/// until it can be acquired.\n\t///\n\t/// This function will not return while other writers or readers currently\n\t/// have access to the lock.\n\t///\n\t/// Returns an RAII guard which will drop the write access of this `RwLock`\n\t/// when dropped.\n\t///\n\t/// Because this method takes a [`ThreadKey`], it's not possible for this\n\t/// method to cause a deadlock.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, RwLock};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(1);\n\t///\n\t/// match lock.try_write(key) {\n\t/// Ok(n) =\u003e assert_eq!(*n, 1),\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t/// ```\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\tpub fn write(\u0026self, key: ThreadKey) -\u003e RwLockWriteGuard\u003c'_, T, R\u003e {\n\t\tunsafe {\n\t\t\tself.raw_write();\n\n\t\t\t// safety: the lock is locked first\n\t\t\tRwLockWriteGuard::new(self, key)\n\t\t}\n\t}\n\n\t/// Attempts to lock this `RwLock` with exclusive write access.\n\t///\n\t/// This function does not block. If the lock could not be acquired at this\n\t/// time, then `None` is returned. Otherwise, an RAII guard is returned\n\t/// which will release the lock when it is dropped.\n\t///\n\t/// This function does not provide any guarantees with respect to the\n\t/// ordering of whether contentious readers or writers will acquire the\n\t/// lock first.\n\t///\n\t/// # Errors\n\t///\n\t/// If the `RwLock` could not be acquired because it was already locked,\n\t/// then an error will be returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(1);\n\t///\n\t/// let n = lock.read(key);\n\t/// assert_eq!(*n, 1);\n\t/// ```\n\tpub fn try_write(\u0026self, key: ThreadKey) -\u003e Result\u003cRwLockWriteGuard\u003c'_, T, R\u003e, ThreadKey\u003e {\n\t\tunsafe {\n\t\t\tif self.raw_try_write() {\n\t\t\t\t// safety: the lock is locked first\n\t\t\t\tOk(RwLockWriteGuard::new(self, key))\n\t\t\t} else {\n\t\t\t\tErr(key)\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Returns `true` if the rwlock is currently locked in any way\n\t#[cfg(test)]\n\tpub(crate) fn is_locked(\u0026self) -\u003e bool {\n\t\tself.raw.is_locked()\n\t}\n\n\t/// Immediately drops the guard, and consequently releases the shared lock.\n\t///\n\t/// This function is equivalent to calling [`drop`] on the guard, except\n\t/// that it returns the key that was used to create it. Alternately, the\n\t/// guard will be automatically dropped when it goes out of scope.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(0);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// assert_eq!(*guard, 0);\n\t/// let key = RwLock::unlock_read(guard);\n\t/// ```\n\t#[must_use]\n\tpub fn unlock_read(guard: RwLockReadGuard\u003c'_, T, R\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.rwlock);\n\t\tguard.thread_key\n\t}\n\n\t/// Immediately drops the guard, and consequently releases the exclusive\n\t/// lock.\n\t///\n\t/// This function is equivalent to calling [`drop`] on the guard, except\n\t/// that it returns the key that was used to create it. Alternately, the\n\t/// guard will be automatically dropped when it goes out of scope.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(0);\n\t///\n\t/// let mut guard = lock.write(key);\n\t/// *guard += 20;\n\t/// let key = RwLock::unlock_write(guard);\n\t/// ```\n\t#[must_use]\n\tpub fn unlock_write(guard: RwLockWriteGuard\u003c'_, T, R\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.rwlock);\n\t\tguard.thread_key\n\t}\n}\n\nunsafe impl\u003cR: RawRwLock + Send, T: ?Sized + Send\u003e Send for RwLock\u003cT, R\u003e {}\nunsafe impl\u003cR: RawRwLock + Sync, T: ?Sized + Send\u003e Sync for RwLock\u003cT, R\u003e {}\n","traces":[{"line":18,"address":[],"length":0,"stats":{"Line":5}},{"line":19,"address":[175957,175989],"length":1,"stats":{"Line":6}},{"line":22,"address":[176240,176352],"length":1,"stats":{"Line":6}},{"line":23,"address":[217141,217253],"length":1,"stats":{"Line":3}},{"line":24,"address":[],"length":0,"stats":{"Line":0}},{"line":25,"address":[],"length":0,"stats":{"Line":0}},{"line":29,"address":[966208,966320],"length":1,"stats":{"Line":6}},{"line":30,"address":[210309,210325,210304,210320],"length":1,"stats":{"Line":23}},{"line":33,"address":[216512,216592],"length":1,"stats":{"Line":7}},{"line":34,"address":[966414,966494],"length":1,"stats":{"Line":7}},{"line":35,"address":[216568,216648],"length":1,"stats":{"Line":3}},{"line":39,"address":[248737,248817,248653],"length":1,"stats":{"Line":5}},{"line":40,"address":[248658,248742,248822],"length":1,"stats":{"Line":15}},{"line":43,"address":[216736,216768],"length":1,"stats":{"Line":6}},{"line":45,"address":[966604,966572],"length":1,"stats":{"Line":6}},{"line":46,"address":[1008069,1008080,1008064,1008000,1008037,1008032,1008085,1008005],"length":1,"stats":{"Line":21}},{"line":49,"address":[138640],"length":1,"stats":{"Line":5}},{"line":50,"address":[966788,966676],"length":1,"stats":{"Line":1}},{"line":51,"address":[],"length":0,"stats":{"Line":0}},{"line":52,"address":[],"length":0,"stats":{"Line":0}},{"line":56,"address":[249169,249277,249377],"length":1,"stats":{"Line":5}},{"line":57,"address":[176054,176166],"length":1,"stats":{"Line":18}},{"line":60,"address":[138384],"length":1,"stats":{"Line":6}},{"line":61,"address":[216446,216366],"length":1,"stats":{"Line":6}},{"line":62,"address":[],"length":0,"stats":{"Line":1}},{"line":66,"address":[248497,248413,248577],"length":1,"stats":{"Line":6}},{"line":67,"address":[147589,147557,147525,147552,147520,147584,147605,147600],"length":1,"stats":{"Line":23}},{"line":70,"address":[216704,216672],"length":1,"stats":{"Line":5}},{"line":72,"address":[967020,967052],"length":1,"stats":{"Line":5}},{"line":73,"address":[967057,967025],"length":1,"stats":{"Line":17}},{"line":88,"address":[249920,250048,249984],"length":1,"stats":{"Line":12}},{"line":89,"address":[217401,217337],"length":1,"stats":{"Line":12}},{"line":92,"address":[967200,967216],"length":1,"stats":{"Line":4}},{"line":93,"address":[],"length":0,"stats":{"Line":4}},{"line":96,"address":[967280,967232],"length":1,"stats":{"Line":4}},{"line":97,"address":[],"length":0,"stats":{"Line":4}},{"line":112,"address":[138928],"length":1,"stats":{"Line":3}},{"line":113,"address":[],"length":0,"stats":{"Line":3}},{"line":116,"address":[],"length":0,"stats":{"Line":3}},{"line":117,"address":[217481,217529],"length":1,"stats":{"Line":3}},{"line":126,"address":[],"length":0,"stats":{"Line":1}},{"line":127,"address":[],"length":0,"stats":{"Line":1}},{"line":137,"address":[],"length":0,"stats":{"Line":1}},{"line":138,"address":[967477],"length":1,"stats":{"Line":1}},{"line":153,"address":[967671,967530,967616,967813,967776,967488],"length":1,"stats":{"Line":17}},{"line":155,"address":[],"length":0,"stats":{"Line":0}},{"line":156,"address":[],"length":0,"stats":{"Line":34}},{"line":187,"address":[],"length":0,"stats":{"Line":1}},{"line":188,"address":[],"length":0,"stats":{"Line":1}},{"line":193,"address":[967936,967984],"length":1,"stats":{"Line":2}},{"line":194,"address":[968000,967957],"length":1,"stats":{"Line":2}},{"line":202,"address":[],"length":0,"stats":{"Line":1}},{"line":203,"address":[968024],"length":1,"stats":{"Line":1}},{"line":224,"address":[],"length":0,"stats":{"Line":0}},{"line":225,"address":[968048],"length":1,"stats":{"Line":1}},{"line":246,"address":[],"length":0,"stats":{"Line":1}},{"line":247,"address":[968088],"length":1,"stats":{"Line":1}},{"line":252,"address":[968112,968144],"length":1,"stats":{"Line":2}},{"line":253,"address":[968157,968121],"length":1,"stats":{"Line":2}},{"line":256,"address":[968176],"length":1,"stats":{"Line":7}},{"line":261,"address":[],"length":0,"stats":{"Line":7}},{"line":264,"address":[],"length":0,"stats":{"Line":2}},{"line":265,"address":[968226,968253],"length":1,"stats":{"Line":2}},{"line":268,"address":[247856,247984,247952,247920,247888,247824],"length":1,"stats":{"Line":13}},{"line":273,"address":[247933,247901,247837,247869,247965,247997],"length":1,"stats":{"Line":13}},{"line":310,"address":[],"length":0,"stats":{"Line":1}},{"line":312,"address":[],"length":0,"stats":{"Line":1}},{"line":315,"address":[968365],"length":1,"stats":{"Line":1}},{"line":348,"address":[968432,968582,968560],"length":1,"stats":{"Line":1}},{"line":350,"address":[],"length":0,"stats":{"Line":4}},{"line":352,"address":[968553,968523],"length":1,"stats":{"Line":2}},{"line":354,"address":[968502],"length":1,"stats":{"Line":1}},{"line":361,"address":[],"length":0,"stats":{"Line":1}},{"line":362,"address":[],"length":0,"stats":{"Line":1}},{"line":364,"address":[968629],"length":1,"stats":{"Line":1}},{"line":366,"address":[],"length":0,"stats":{"Line":0}},{"line":373,"address":[],"length":0,"stats":{"Line":1}},{"line":374,"address":[],"length":0,"stats":{"Line":1}},{"line":376,"address":[],"length":0,"stats":{"Line":1}},{"line":378,"address":[],"length":0,"stats":{"Line":0}},{"line":409,"address":[175462,175248,175360,175334,175376,175488],"length":1,"stats":{"Line":6}},{"line":411,"address":[175390,175262],"length":1,"stats":{"Line":5}},{"line":414,"address":[],"length":0,"stats":{"Line":4}},{"line":444,"address":[],"length":0,"stats":{"Line":2}},{"line":446,"address":[],"length":0,"stats":{"Line":7}},{"line":448,"address":[],"length":0,"stats":{"Line":4}},{"line":450,"address":[],"length":0,"stats":{"Line":1}},{"line":457,"address":[969296,969312],"length":1,"stats":{"Line":2}},{"line":458,"address":[],"length":0,"stats":{"Line":2}},{"line":480,"address":[969380,969328],"length":1,"stats":{"Line":1}},{"line":481,"address":[],"length":0,"stats":{"Line":1}},{"line":482,"address":[],"length":0,"stats":{"Line":0}},{"line":505,"address":[],"length":0,"stats":{"Line":1}},{"line":506,"address":[],"length":0,"stats":{"Line":1}},{"line":507,"address":[],"length":0,"stats":{"Line":0}}],"covered":85,"coverable":95},{"path":["/","home","botahamec","Projects","happylock","src","rwlock","write_guard.rs"],"content":"use std::fmt::{Debug, Display};\nuse std::hash::Hash;\nuse std::marker::PhantomData;\nuse std::ops::{Deref, DerefMut};\n\nuse lock_api::RawRwLock;\n\nuse crate::lockable::RawLock;\nuse crate::ThreadKey;\n\nuse super::{RwLock, RwLockWriteGuard, RwLockWriteRef};\n\n// These impls make things slightly easier because now you can use\n// `println!(\"{guard}\")` instead of `println!(\"{}\", *guard)`\n\n#[mutants::skip] // hashing involves PRNG and is difficult to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Hash + ?Sized, R: RawRwLock\u003e Hash for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.deref().hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug + ?Sized, R: RawRwLock\u003e Debug for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: Display + ?Sized, R: RawRwLock\u003e Display for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e Deref for RwLockWriteRef\u003c'_, T, R\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t// safety: this is the only type that can use `value`, and there's\n\t\t// a reference to this type, so there cannot be any mutable\n\t\t// references to this value.\n\t\tunsafe { \u0026*self.0.data.get() }\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e DerefMut for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t// safety: this is the only type that can use `value`, and we have a\n\t\t// mutable reference to this type, so there cannot be any other\n\t\t// references to this value.\n\t\tunsafe { \u0026mut *self.0.data.get() }\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e AsRef\u003cT\u003e for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e AsMut\u003cT\u003e for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e Drop for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn drop(\u0026mut self) {\n\t\t// safety: this guard is being destroyed, so the data cannot be\n\t\t// accessed without locking again\n\t\tunsafe { self.0.raw_unlock_write() }\n\t}\n}\n\nimpl\u003c'a, T: ?Sized + 'a, R: RawRwLock\u003e RwLockWriteRef\u003c'a, T, R\u003e {\n\t/// Creates a reference to the underlying data of an [`RwLock`] without\n\t/// locking or taking ownership of the key.\n\t#[must_use]\n\tpub(crate) const unsafe fn new(mutex: \u0026'a RwLock\u003cT, R\u003e) -\u003e Self {\n\t\tSelf(mutex, PhantomData)\n\t}\n}\n\n#[mutants::skip] // hashing involves PRNG and is difficult to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Hash + ?Sized, R: RawRwLock\u003e Hash for RwLockWriteGuard\u003c'_, T, R\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.deref().hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug + ?Sized, R: RawRwLock\u003e Debug for RwLockWriteGuard\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: Display + ?Sized, R: RawRwLock\u003e Display for RwLockWriteGuard\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e Deref for RwLockWriteGuard\u003c'_, T, R\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t\u0026self.rwlock\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e DerefMut for RwLockWriteGuard\u003c'_, T, R\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t\u0026mut self.rwlock\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e AsRef\u003cT\u003e for RwLockWriteGuard\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e AsMut\u003cT\u003e for RwLockWriteGuard\u003c'_, T, R\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself\n\t}\n}\n\nimpl\u003c'a, T: ?Sized + 'a, R: RawRwLock\u003e RwLockWriteGuard\u003c'a, T, R\u003e {\n\t/// Create a guard to the given mutex. Undefined if multiple guards to the\n\t/// same mutex exist at once.\n\t#[must_use]\n\tpub(super) const unsafe fn new(rwlock: \u0026'a RwLock\u003cT, R\u003e, thread_key: ThreadKey) -\u003e Self {\n\t\tSelf {\n\t\t\trwlock: RwLockWriteRef(rwlock, PhantomData),\n\t\t\tthread_key,\n\t\t}\n\t}\n}\n\nunsafe impl\u003cT: ?Sized + Sync, R: RawRwLock + Sync\u003e Sync for RwLockWriteRef\u003c'_, T, R\u003e {}\n","traces":[{"line":33,"address":[971136],"length":1,"stats":{"Line":1}},{"line":34,"address":[],"length":0,"stats":{"Line":1}},{"line":41,"address":[],"length":0,"stats":{"Line":3}},{"line":45,"address":[138149],"length":1,"stats":{"Line":3}},{"line":50,"address":[138192],"length":1,"stats":{"Line":3}},{"line":54,"address":[],"length":0,"stats":{"Line":3}},{"line":59,"address":[],"length":0,"stats":{"Line":1}},{"line":60,"address":[],"length":0,"stats":{"Line":1}},{"line":65,"address":[],"length":0,"stats":{"Line":0}},{"line":66,"address":[],"length":0,"stats":{"Line":0}},{"line":71,"address":[],"length":0,"stats":{"Line":5}},{"line":74,"address":[244309,244293],"length":1,"stats":{"Line":6}},{"line":82,"address":[],"length":0,"stats":{"Line":4}},{"line":83,"address":[],"length":0,"stats":{"Line":0}},{"line":104,"address":[971360],"length":1,"stats":{"Line":1}},{"line":105,"address":[],"length":0,"stats":{"Line":1}},{"line":112,"address":[138176],"length":1,"stats":{"Line":3}},{"line":113,"address":[],"length":0,"stats":{"Line":3}},{"line":118,"address":[],"length":0,"stats":{"Line":2}},{"line":119,"address":[138229],"length":1,"stats":{"Line":2}},{"line":124,"address":[],"length":0,"stats":{"Line":1}},{"line":125,"address":[],"length":0,"stats":{"Line":1}},{"line":130,"address":[],"length":0,"stats":{"Line":1}},{"line":131,"address":[],"length":0,"stats":{"Line":1}},{"line":139,"address":[],"length":0,"stats":{"Line":4}},{"line":141,"address":[],"length":0,"stats":{"Line":0}}],"covered":22,"coverable":26},{"path":["/","home","botahamec","Projects","happylock","src","rwlock","write_lock.rs"],"content":"use std::fmt::Debug;\n\nuse lock_api::RawRwLock;\n\nuse crate::lockable::{Lockable, RawLock};\nuse crate::{Keyable, ThreadKey};\n\nuse super::{RwLock, RwLockWriteGuard, RwLockWriteRef, WriteLock};\n\nunsafe impl\u003cT, R: RawRwLock\u003e RawLock for WriteLock\u003c'_, T, R\u003e {\n\tfn poison(\u0026self) {\n\t\tself.0.poison()\n\t}\n\n\tunsafe fn raw_write(\u0026self) {\n\t\tself.0.raw_write()\n\t}\n\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool {\n\t\tself.0.raw_try_write()\n\t}\n\n\tunsafe fn raw_unlock_write(\u0026self) {\n\t\tself.0.raw_unlock_write()\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tself.0.raw_write()\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tself.0.raw_try_write()\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tself.0.raw_unlock_write()\n\t}\n}\n\nunsafe impl\u003cT, R: RawRwLock\u003e Lockable for WriteLock\u003c'_, T, R\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= RwLockWriteRef\u003c'g, T, R\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= \u0026'a mut T\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tptrs.push(self)\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tRwLockWriteRef::new(self.as_ref())\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.0.data_mut()\n\t}\n}\n\n// Technically, the exclusive locks can also be shared, but there's currently\n// no way to express that. I don't think I want to ever express that.\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug, R: RawRwLock\u003e Debug for WriteLock\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t// safety: this is just a try lock, and the value is dropped\n\t\t// immediately after, so there's no risk of blocking ourselves\n\t\t// or any other threads\n\t\t// It makes zero sense to try using an exclusive lock for this, so this\n\t\t// is the only time when WriteLock does a read.\n\t\tif let Some(value) = unsafe { self.0.try_read_no_key() } {\n\t\t\tf.debug_struct(\"WriteLock\").field(\"data\", \u0026\u0026*value).finish()\n\t\t} else {\n\t\t\tstruct LockedPlaceholder;\n\t\t\timpl Debug for LockedPlaceholder {\n\t\t\t\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t\t\t\tf.write_str(\"\u003clocked\u003e\")\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tf.debug_struct(\"WriteLock\")\n\t\t\t\t.field(\"data\", \u0026LockedPlaceholder)\n\t\t\t\t.finish()\n\t\t}\n\t}\n}\n\nimpl\u003c'l, T, R\u003e From\u003c\u0026'l RwLock\u003cT, R\u003e\u003e for WriteLock\u003c'l, T, R\u003e {\n\tfn from(value: \u0026'l RwLock\u003cT, R\u003e) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\nimpl\u003cT: ?Sized, R\u003e AsRef\u003cRwLock\u003cT, R\u003e\u003e for WriteLock\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026RwLock\u003cT, R\u003e {\n\t\tself.0\n\t}\n}\n\nimpl\u003c'l, T, R\u003e WriteLock\u003c'l, T, R\u003e {\n\t/// Creates a new `WriteLock` which accesses the given [`RwLock`]\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{rwlock::WriteLock, RwLock};\n\t///\n\t/// let lock = RwLock::new(5);\n\t/// let write_lock = WriteLock::new(\u0026lock);\n\t/// ```\n\t#[must_use]\n\tpub const fn new(rwlock: \u0026'l RwLock\u003cT, R\u003e) -\u003e Self {\n\t\tSelf(rwlock)\n\t}\n}\n\nimpl\u003cT, R: RawRwLock\u003e WriteLock\u003c'_, T, R\u003e {\n\tpub fn scoped_lock\u003c'a, Ret\u003e(\u0026'a self, key: impl Keyable, f: impl Fn(\u0026'a mut T) -\u003e Ret) -\u003e Ret {\n\t\tself.0.scoped_write(key, f)\n\t}\n\n\tpub fn scoped_try_lock\u003c'a, Key: Keyable, Ret\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(\u0026'a mut T) -\u003e Ret,\n\t) -\u003e Result\u003cRet, Key\u003e {\n\t\tself.0.scoped_try_write(key, f)\n\t}\n\n\t/// Locks the underlying [`RwLock`] with exclusive write access, blocking\n\t/// the current until it can be acquired.\n\t///\n\t/// This function will not return while other writers or readers currently\n\t/// have access to the lock.\n\t///\n\t/// Returns an RAII guard which will drop the write access of this `RwLock`\n\t/// when dropped.\n\t///\n\t/// Because this method takes a [`ThreadKey`], it's not possible for this\n\t/// method to cause a deadlock.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, RwLock};\n\t/// use happylock::rwlock::WriteLock;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(1);\n\t/// let writer = WriteLock::new(\u0026lock);\n\t///\n\t/// let mut n = writer.lock(key);\n\t/// *n += 2;\n\t/// ```\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\t#[must_use]\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e RwLockWriteGuard\u003c'_, T, R\u003e {\n\t\tself.0.write(key)\n\t}\n\n\t/// Attempts to lock the underlying [`RwLock`] with exclusive write access.\n\t///\n\t/// This function does not block. If the lock could not be acquired at this\n\t/// time, then `None` is returned. Otherwise, an RAII guard is returned\n\t/// which will release the lock when it is dropped.\n\t///\n\t/// This function does not provide any guarantees with respect to the\n\t/// ordering of whether contentious readers or writers will acquire the\n\t/// lock first.\n\t///\n\t/// # Errors\n\t///\n\t/// If the [`RwLock`] could not be acquired because it was already locked,\n\t/// then an error will be returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::rwlock::WriteLock;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(1);\n\t/// let writer = WriteLock::new(\u0026lock);\n\t///\n\t/// match writer.try_lock(key) {\n\t/// Ok(n) =\u003e assert_eq!(*n, 1),\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t/// ```\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e Result\u003cRwLockWriteGuard\u003c'_, T, R\u003e, ThreadKey\u003e {\n\t\tself.0.try_write(key)\n\t}\n\n\t// There's no `try_lock_no_key`. Instead, `try_read_no_key` is called on\n\t// the referenced `RwLock`.\n\n\t/// Immediately drops the guard, and consequently releases the exclusive\n\t/// lock on the underlying [`RwLock`].\n\t///\n\t/// This function is equivalent to calling [`drop`] on the guard, except\n\t/// that it returns the key that was used to create it. Alternately, the\n\t/// guard will be automatically dropped when it goes out of scope.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::rwlock::WriteLock;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(0);\n\t/// let writer = WriteLock::new(\u0026lock);\n\t///\n\t/// let mut guard = writer.lock(key);\n\t/// *guard += 20;\n\t/// let key = WriteLock::unlock(guard);\n\t/// ```\n\t#[must_use]\n\tpub fn unlock(guard: RwLockWriteGuard\u003c'_, T, R\u003e) -\u003e ThreadKey {\n\t\tRwLock::unlock_write(guard)\n\t}\n}\n","traces":[{"line":11,"address":[970224,970208],"length":1,"stats":{"Line":1}},{"line":12,"address":[],"length":0,"stats":{"Line":1}},{"line":15,"address":[],"length":0,"stats":{"Line":1}},{"line":16,"address":[],"length":0,"stats":{"Line":1}},{"line":19,"address":[970272,970304],"length":1,"stats":{"Line":1}},{"line":20,"address":[],"length":0,"stats":{"Line":1}},{"line":23,"address":[],"length":0,"stats":{"Line":1}},{"line":24,"address":[],"length":0,"stats":{"Line":1}},{"line":27,"address":[],"length":0,"stats":{"Line":0}},{"line":28,"address":[],"length":0,"stats":{"Line":0}},{"line":31,"address":[],"length":0,"stats":{"Line":0}},{"line":32,"address":[],"length":0,"stats":{"Line":0}},{"line":35,"address":[],"length":0,"stats":{"Line":0}},{"line":36,"address":[],"length":0,"stats":{"Line":0}},{"line":51,"address":[],"length":0,"stats":{"Line":2}},{"line":52,"address":[],"length":0,"stats":{"Line":2}},{"line":55,"address":[],"length":0,"stats":{"Line":1}},{"line":56,"address":[],"length":0,"stats":{"Line":1}},{"line":59,"address":[970656],"length":1,"stats":{"Line":1}},{"line":60,"address":[],"length":0,"stats":{"Line":1}},{"line":94,"address":[],"length":0,"stats":{"Line":1}},{"line":95,"address":[],"length":0,"stats":{"Line":1}},{"line":100,"address":[970688],"length":1,"stats":{"Line":1}},{"line":101,"address":[],"length":0,"stats":{"Line":1}},{"line":117,"address":[970704,970720],"length":1,"stats":{"Line":3}},{"line":118,"address":[],"length":0,"stats":{"Line":0}},{"line":123,"address":[],"length":0,"stats":{"Line":1}},{"line":124,"address":[],"length":0,"stats":{"Line":1}},{"line":127,"address":[970768],"length":1,"stats":{"Line":1}},{"line":132,"address":[970777],"length":1,"stats":{"Line":1}},{"line":163,"address":[],"length":0,"stats":{"Line":1}},{"line":164,"address":[],"length":0,"stats":{"Line":1}},{"line":197,"address":[],"length":0,"stats":{"Line":1}},{"line":198,"address":[],"length":0,"stats":{"Line":1}},{"line":226,"address":[],"length":0,"stats":{"Line":1}},{"line":227,"address":[],"length":0,"stats":{"Line":1}}],"covered":29,"coverable":36},{"path":["/","home","botahamec","Projects","happylock","src","rwlock.rs"],"content":"use std::cell::UnsafeCell;\nuse std::marker::PhantomData;\n\nuse lock_api::RawRwLock;\n\nuse crate::poisonable::PoisonFlag;\nuse crate::ThreadKey;\n\nmod rwlock;\n\nmod read_lock;\nmod write_lock;\n\nmod read_guard;\nmod write_guard;\n\n#[cfg(feature = \"spin\")]\npub type SpinRwLock\u003cT\u003e = RwLock\u003cT, spin::RwLock\u003c()\u003e\u003e;\n\n#[cfg(feature = \"parking_lot\")]\npub type ParkingRwLock\u003cT\u003e = RwLock\u003cT, parking_lot::RawRwLock\u003e;\n\n/// A reader-writer lock\n///\n/// This type of lock allows a number of readers or at most one writer at any\n/// point in time. The write portion of this lock typically allows modification\n/// of the underlying data (exclusive access) and the read portion of this lock\n/// typically allows for read-only access (shared access).\n///\n/// In comparison, a [`Mutex`] does not distinguish between readers or writers\n/// that acquire the lock, therefore blocking any threads waiting for the lock\n/// to become available. An `RwLock` will allow any number of readers to\n/// acquire the lock as long as a writer is not holding the lock.\n///\n/// The type parameter T represents the data that this lock protects. It is\n/// required that T satisfies [`Send`] to be shared across threads and [`Sync`]\n/// to allow concurrent access through readers. The RAII guard returned from\n/// the locking methods implement [`Deref`] (and [`DerefMut`] for the `write`\n/// methods) to allow access to the content of the lock.\n///\n/// Locking the mutex on a thread that already locked it is impossible, due to\n/// the requirement of the [`ThreadKey`]. Therefore, this will never deadlock.\n///\n/// [`ThreadKey`]: `crate::ThreadKey`\n/// [`Mutex`]: `crate::mutex::Mutex`\n/// [`Deref`]: `std::ops::Deref`\n/// [`DerefMut`]: `std::ops::DerefMut`\npub struct RwLock\u003cT: ?Sized, R\u003e {\n\traw: R,\n\tpoison: PoisonFlag,\n\tdata: UnsafeCell\u003cT\u003e,\n}\n\n/// Grants read access to an [`RwLock`]\n///\n/// This structure is designed to be used in a [`LockCollection`] to indicate\n/// that only read access is needed to the data.\n///\n/// [`LockCollection`]: `crate::LockCollection`\n#[repr(transparent)]\npub struct ReadLock\u003c'l, T: ?Sized, R\u003e(\u0026'l RwLock\u003cT, R\u003e);\n\n/// Grants write access to an [`RwLock`]\n///\n/// This structure is designed to be used in a [`LockCollection`] to indicate\n/// that write access is needed to the data.\n///\n/// [`LockCollection`]: `crate::LockCollection`\n#[repr(transparent)]\npub struct WriteLock\u003c'l, T: ?Sized, R\u003e(\u0026'l RwLock\u003cT, R\u003e);\n\n/// RAII structure that unlocks the shared read access to a [`RwLock`]\n///\n/// This is similar to [`RwLockReadRef`], except it does not hold a\n/// [`Keyable`].\npub struct RwLockReadRef\u003c'a, T: ?Sized, R: RawRwLock\u003e(\n\t\u0026'a RwLock\u003cT, R\u003e,\n\tPhantomData\u003cR::GuardMarker\u003e,\n);\n\n/// RAII structure that unlocks the exclusive write access to a [`RwLock`]\n///\n/// This is similar to [`RwLockWriteRef`], except it does not hold a\n/// [`Keyable`].\npub struct RwLockWriteRef\u003c'a, T: ?Sized, R: RawRwLock\u003e(\n\t\u0026'a RwLock\u003cT, R\u003e,\n\tPhantomData\u003cR::GuardMarker\u003e,\n);\n\n/// RAII structure used to release the shared read access of a lock when\n/// dropped.\n///\n/// This structure is created by the [`read`] and [`try_read`] methods on\n/// [`RwLock`].\n///\n/// [`read`]: `RwLock::read`\n/// [`try_read`]: `RwLock::try_read`\npub struct RwLockReadGuard\u003c'a, T: ?Sized, R: RawRwLock\u003e {\n\trwlock: RwLockReadRef\u003c'a, T, R\u003e,\n\tthread_key: ThreadKey,\n}\n\n/// RAII structure used to release the exclusive write access of a lock when\n/// dropped.\n///\n/// This structure is created by the [`write`] and [`try_write`] methods on\n/// [`RwLock`]\n///\n/// [`try_write`]: `RwLock::try_write`\npub struct RwLockWriteGuard\u003c'a, T: ?Sized, R: RawRwLock\u003e {\n\trwlock: RwLockWriteRef\u003c'a, T, R\u003e,\n\tthread_key: ThreadKey,\n}\n\n#[cfg(test)]\nmod tests {\n\tuse crate::lockable::Lockable;\n\tuse crate::lockable::RawLock;\n\tuse crate::LockCollection;\n\tuse crate::RwLock;\n\tuse crate::ThreadKey;\n\n\tuse super::*;\n\n\t#[test]\n\tfn unlocked_when_initialized() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\n\t\tassert!(!lock.is_locked());\n\t\tassert!(lock.try_write(key).is_ok());\n\t}\n\n\t#[test]\n\tfn read_lock_unlocked_when_initialized() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet reader = ReadLock::new(\u0026lock);\n\n\t\tassert!(reader.try_lock(key).is_ok());\n\t}\n\n\t#[test]\n\tfn read_lock_from_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::from(\"Hello, world!\");\n\t\tlet reader = ReadLock::from(\u0026lock);\n\n\t\tlet guard = reader.lock(key);\n\t\tassert_eq!(*guard, \"Hello, world!\");\n\t}\n\n\t#[test]\n\tfn read_lock_scoped_works() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(42);\n\t\tlet reader = ReadLock::new(\u0026lock);\n\n\t\treader.scoped_lock(\u0026mut key, |num| assert_eq!(*num, 42));\n\t}\n\n\t#[test]\n\tfn read_lock_scoped_try_fails_during_write() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(42);\n\t\tlet reader = ReadLock::new(\u0026lock);\n\t\tlet guard = lock.write(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = reader.scoped_try_lock(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn write_lock_unlocked_when_initialized() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet writer = WriteLock::new(\u0026lock);\n\n\t\tassert!(writer.try_lock(key).is_ok());\n\t}\n\n\t#[test]\n\tfn read_lock_get_ptrs() {\n\t\tlet rwlock = RwLock::new(5);\n\t\tlet readlock = ReadLock::new(\u0026rwlock);\n\t\tlet mut lock_ptrs = Vec::new();\n\t\treadlock.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tassert!(std::ptr::addr_eq(lock_ptrs[0], \u0026readlock));\n\t}\n\n\t#[test]\n\tfn write_lock_get_ptrs() {\n\t\tlet rwlock = RwLock::new(5);\n\t\tlet writelock = WriteLock::new(\u0026rwlock);\n\t\tlet mut lock_ptrs = Vec::new();\n\t\twritelock.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tassert!(std::ptr::addr_eq(lock_ptrs[0], \u0026writelock));\n\t}\n\n\t#[test]\n\tfn write_lock_scoped_works() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(42);\n\t\tlet writer = WriteLock::new(\u0026lock);\n\n\t\twriter.scoped_lock(\u0026mut key, |num| assert_eq!(*num, 42));\n\t}\n\n\t#[test]\n\tfn write_lock_scoped_try_fails_during_write() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(42);\n\t\tlet writer = WriteLock::new(\u0026lock);\n\t\tlet guard = lock.write(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = writer.scoped_try_lock(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn locked_after_read() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\n\t\tlet guard = lock.read(key);\n\n\t\tassert!(lock.is_locked());\n\t\tdrop(guard)\n\t}\n\n\t#[test]\n\tfn locked_after_using_read_lock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet reader = ReadLock::new(\u0026lock);\n\n\t\tlet guard = reader.lock(key);\n\n\t\tassert!(lock.is_locked());\n\t\tdrop(guard)\n\t}\n\n\t#[test]\n\tfn locked_after_write() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\n\t\tlet guard = lock.write(key);\n\n\t\tassert!(lock.is_locked());\n\t\tdrop(guard)\n\t}\n\n\t#[test]\n\tfn locked_after_using_write_lock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet writer = WriteLock::new(\u0026lock);\n\n\t\tlet guard = writer.lock(key);\n\n\t\tassert!(lock.is_locked());\n\t\tdrop(guard)\n\t}\n\n\t#[test]\n\tfn locked_after_scoped_write() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"Hello, world!\");\n\n\t\tlock.scoped_write(\u0026mut key, |guard| {\n\t\t\tassert!(lock.is_locked());\n\t\t\tassert_eq!(*guard, \"Hello, world!\");\n\n\t\t\tstd::thread::scope(|s| {\n\t\t\t\ts.spawn(|| {\n\t\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\t\tassert!(lock.try_read(key).is_err());\n\t\t\t\t});\n\t\t\t})\n\t\t})\n\t}\n\n\t#[test]\n\tfn get_mut_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mut lock = crate::RwLock::from(42);\n\n\t\tlet mut_ref = lock.get_mut();\n\t\t*mut_ref = 24;\n\n\t\tlock.scoped_read(key, |guard| assert_eq!(*guard, 24))\n\t}\n\n\t#[test]\n\tfn try_write_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"Hello\");\n\t\tlet guard = lock.write(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = lock.try_write(key);\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn try_read_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"Hello\");\n\t\tlet guard = lock.write(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = lock.try_read(key);\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn read_display_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet guard = lock.read(key);\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn write_display_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet guard = lock.write(key);\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn read_ref_display_works() {\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet guard = unsafe { lock.try_read_no_key().unwrap() };\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn write_ref_display_works() {\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet guard = unsafe { lock.try_write_no_key().unwrap() };\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn dropping_read_ref_releases_rwlock() {\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\n\t\tlet guard = unsafe { lock.try_read_no_key().unwrap() };\n\t\tdrop(guard);\n\n\t\tassert!(!lock.is_locked());\n\t}\n\n\t#[test]\n\tfn dropping_write_guard_releases_rwlock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\n\t\tlet guard = lock.write(key);\n\t\tdrop(guard);\n\n\t\tassert!(!lock.is_locked());\n\t}\n\n\t#[test]\n\tfn unlock_write() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"Hello, world\");\n\n\t\tlet mut guard = lock.write(key);\n\t\t*guard = \"Goodbye, world!\";\n\t\tlet key = RwLock::unlock_write(guard);\n\n\t\tlet guard = lock.read(key);\n\t\tassert_eq!(*guard, \"Goodbye, world!\");\n\t}\n\n\t#[test]\n\tfn unlock_read() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"Hello, world\");\n\n\t\tlet guard = lock.read(key);\n\t\tassert_eq!(*guard, \"Hello, world\");\n\t\tlet key = RwLock::unlock_read(guard);\n\n\t\tlet guard = lock.write(key);\n\t\tassert_eq!(*guard, \"Hello, world\");\n\t}\n\n\t#[test]\n\tfn unlock_read_lock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"Hello, world\");\n\t\tlet reader = ReadLock::new(\u0026lock);\n\n\t\tlet guard = reader.lock(key);\n\t\tlet key = ReadLock::unlock(guard);\n\n\t\tlock.write(key);\n\t}\n\n\t#[test]\n\tfn unlock_write_lock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"Hello, world\");\n\t\tlet writer = WriteLock::from(\u0026lock);\n\n\t\tlet guard = writer.lock(key);\n\t\tlet key = WriteLock::unlock(guard);\n\n\t\tlock.write(key);\n\t}\n\n\t#[test]\n\tfn read_lock_in_collection() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"hi\");\n\t\tlet collection = LockCollection::try_new(ReadLock::new(\u0026lock)).unwrap();\n\n\t\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard, \"hi\");\n\t\t});\n\t\tcollection.scoped_read(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard, \"hi\");\n\t\t});\n\t\tassert!(collection\n\t\t\t.scoped_try_lock(\u0026mut key, |guard| {\n\t\t\t\tassert_eq!(*guard, \"hi\");\n\t\t\t})\n\t\t\t.is_ok());\n\t\tassert!(collection\n\t\t\t.scoped_try_read(\u0026mut key, |guard| {\n\t\t\t\tassert_eq!(*guard, \"hi\");\n\t\t\t})\n\t\t\t.is_ok());\n\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(**guard, \"hi\");\n\n\t\tlet key = LockCollection::\u003cReadLock\u003c_, _\u003e\u003e::unlock(guard);\n\t\tlet guard = collection.read(key);\n\t\tassert_eq!(**guard, \"hi\");\n\n\t\tlet key = LockCollection::\u003cReadLock\u003c_, _\u003e\u003e::unlock(guard);\n\t\tlet guard = lock.write(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_lock(key);\n\t\t\t\tassert!(guard.is_err());\n\t\t\t});\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_read(key);\n\t\t\t\tassert!(guard.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn write_lock_in_collection() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"hi\");\n\t\tlet collection = LockCollection::try_new(WriteLock::new(\u0026lock)).unwrap();\n\n\t\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard, \"hi\");\n\t\t});\n\t\tassert!(collection\n\t\t\t.scoped_try_lock(\u0026mut key, |guard| {\n\t\t\t\tassert_eq!(*guard, \"hi\");\n\t\t\t})\n\t\t\t.is_ok());\n\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(**guard, \"hi\");\n\n\t\tlet key = LockCollection::\u003cWriteLock\u003c_, _\u003e\u003e::unlock(guard);\n\t\tlet guard = lock.write(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_lock(key);\n\t\t\t\tassert!(guard.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn read_ref_as_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = LockCollection::new(crate::RwLock::new(\"hi\"));\n\t\tlet guard = lock.read(key);\n\n\t\tassert_eq!(*(*guard).as_ref(), \"hi\");\n\t}\n\n\t#[test]\n\tfn read_guard_as_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"hi\");\n\t\tlet guard = lock.read(key);\n\n\t\tassert_eq!(*guard.as_ref(), \"hi\");\n\t}\n\n\t#[test]\n\tfn write_ref_as_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = LockCollection::new(crate::RwLock::new(\"hi\"));\n\t\tlet guard = lock.lock(key);\n\n\t\tassert_eq!(*(*guard).as_ref(), \"hi\");\n\t}\n\n\t#[test]\n\tfn write_guard_as_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"hi\");\n\t\tlet guard = lock.write(key);\n\n\t\tassert_eq!(*guard.as_ref(), \"hi\");\n\t}\n\n\t#[test]\n\tfn write_guard_as_mut() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"hi\");\n\t\tlet mut guard = lock.write(key);\n\n\t\tassert_eq!(*guard.as_mut(), \"hi\");\n\t\t*guard.as_mut() = \"foo\";\n\t\tassert_eq!(*guard.as_mut(), \"foo\");\n\t}\n\n\t#[test]\n\tfn poison_read_lock() {\n\t\tlet lock = crate::RwLock::new(\"hi\");\n\t\tlet reader = ReadLock::new(\u0026lock);\n\n\t\treader.poison();\n\t\tassert!(lock.poison.is_poisoned());\n\t}\n\n\t#[test]\n\tfn poison_write_lock() {\n\t\tlet lock = crate::RwLock::new(\"hi\");\n\t\tlet reader = WriteLock::new(\u0026lock);\n\n\t\treader.poison();\n\t\tassert!(lock.poison.is_poisoned());\n\t}\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","src","thread.rs"],"content":"use std::marker::PhantomData;\n\nmod scope;\n\n#[derive(Debug)]\npub struct Scope\u003c'scope, 'env: 'scope\u003e(PhantomData\u003c(\u0026'env (), \u0026'scope ())\u003e);\n\n#[derive(Debug)]\npub struct ScopedJoinHandle\u003c'scope, T\u003e {\n\thandle: std::thread::JoinHandle\u003cT\u003e,\n\t_phantom: PhantomData\u003c\u0026'scope ()\u003e,\n}\n\npub struct JoinHandle\u003cT\u003e {\n\thandle: std::thread::JoinHandle\u003cT\u003e,\n\tkey: crate::ThreadKey,\n}\n\npub struct ThreadBuilder(std::thread::Builder);\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","evil_mutex.rs"],"content":"use std::sync::Arc;\n\nuse happylock::collection::{BoxedLockCollection, RetryingLockCollection};\nuse happylock::mutex::Mutex;\nuse happylock::ThreadKey;\nuse lock_api::{GuardNoSend, RawMutex};\n\nstruct EvilMutex {\n\tinner: parking_lot::RawMutex,\n}\n\nunsafe impl RawMutex for EvilMutex {\n\t#[allow(clippy::declare_interior_mutable_const)]\n\tconst INIT: Self = Self {\n\t\tinner: parking_lot::RawMutex::INIT,\n\t};\n\n\ttype GuardMarker = GuardNoSend;\n\n\tfn lock(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn try_lock(\u0026self) -\u003e bool {\n\t\tself.inner.try_lock()\n\t}\n\n\tunsafe fn unlock(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n}\n\n#[test]\nfn boxed_mutexes() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet good_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(5));\n\tlet evil_mutex: Arc\u003cMutex\u003ci32, EvilMutex\u003e\u003e = Arc::new(Mutex::new(7));\n\tlet useless_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(10));\n\tlet c_good = Arc::clone(\u0026good_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\t_ = collection.lock(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(good_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n\tassert!(evil_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n}\n\n#[test]\nfn retrying_mutexes() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet good_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(5));\n\tlet evil_mutex: Arc\u003cMutex\u003ci32, EvilMutex\u003e\u003e = Arc::new(Mutex::new(7));\n\tlet useless_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(10));\n\tlet c_good = Arc::clone(\u0026good_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tRetryingLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\tcollection.lock(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(good_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n\tassert!(evil_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","evil_rwlock.rs"],"content":"use std::panic::AssertUnwindSafe;\nuse std::sync::Arc;\n\nuse happylock::collection::{BoxedLockCollection, RetryingLockCollection};\nuse happylock::rwlock::RwLock;\nuse happylock::ThreadKey;\nuse lock_api::{GuardNoSend, RawRwLock};\n\nstruct EvilRwLock {\n\tinner: parking_lot::RawRwLock,\n}\n\nunsafe impl RawRwLock for EvilRwLock {\n\t#[allow(clippy::declare_interior_mutable_const)]\n\tconst INIT: Self = Self {\n\t\tinner: parking_lot::RawRwLock::INIT,\n\t};\n\n\ttype GuardMarker = GuardNoSend;\n\n\tfn lock_shared(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn try_lock_shared(\u0026self) -\u003e bool {\n\t\tself.inner.try_lock_shared()\n\t}\n\n\tunsafe fn unlock_shared(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn lock_exclusive(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn try_lock_exclusive(\u0026self) -\u003e bool {\n\t\tself.inner.try_lock_exclusive()\n\t}\n\n\tunsafe fn unlock_exclusive(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n}\n\n#[test]\nfn boxed_rwlocks() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet good_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(5));\n\tlet evil_mutex: Arc\u003cRwLock\u003ci32, EvilRwLock\u003e\u003e = Arc::new(RwLock::new(7));\n\tlet useless_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(10));\n\tlet c_good = Arc::clone(\u0026good_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\t_ = collection.lock(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(good_mutex.scoped_try_write(\u0026mut key, |_| {}).is_ok());\n\tassert!(evil_mutex.scoped_try_write(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_write(\u0026mut key, |_| {}).is_ok());\n\n\tstd::thread::scope(|s| {\n\t\ts.spawn(|| {\n\t\t\tlet evil_mutex = AssertUnwindSafe(evil_mutex);\n\t\t\tlet r = std::panic::catch_unwind(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tevil_mutex.write(key);\n\t\t\t});\n\n\t\t\tassert!(r.is_err());\n\t\t});\n\n\t\ts.spawn(|| {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\tgood_mutex.write(key);\n\t\t});\n\t});\n}\n\n#[test]\nfn retrying_rwlocks() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet good_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(5));\n\tlet evil_mutex: Arc\u003cRwLock\u003ci32, EvilRwLock\u003e\u003e = Arc::new(RwLock::new(7));\n\tlet useless_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(10));\n\tlet c_good = Arc::clone(\u0026good_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tRetryingLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\tcollection.lock(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(good_mutex.scoped_try_write(\u0026mut key, |_| {}).is_ok());\n\tassert!(evil_mutex.scoped_try_write(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_write(\u0026mut key, |_| {}).is_ok());\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","evil_try_mutex.rs"],"content":"use std::sync::Arc;\n\nuse happylock::{\n\tcollection::{BoxedLockCollection, RetryingLockCollection},\n\tmutex::Mutex,\n\tThreadKey,\n};\nuse lock_api::{GuardNoSend, RawMutex};\n\nstruct EvilMutex {\n\tinner: parking_lot::RawMutex,\n}\n\nunsafe impl RawMutex for EvilMutex {\n\t#[allow(clippy::declare_interior_mutable_const)]\n\tconst INIT: Self = Self {\n\t\tinner: parking_lot::RawMutex::INIT,\n\t};\n\n\ttype GuardMarker = GuardNoSend;\n\n\tfn lock(\u0026self) {\n\t\tself.inner.lock()\n\t}\n\n\tfn try_lock(\u0026self) -\u003e bool {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tunsafe fn unlock(\u0026self) {\n\t\tself.inner.unlock()\n\t}\n}\n\n#[test]\nfn boxed_mutexes() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet good_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(5));\n\tlet evil_mutex: Arc\u003cMutex\u003ci32, EvilMutex\u003e\u003e = Arc::new(Mutex::new(7));\n\tlet useless_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(10));\n\tlet c_good = Arc::clone(\u0026good_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\tlet g = collection.try_lock(key);\n\t\tprintln!(\"{}\", g.unwrap().1);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(good_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n\tassert!(evil_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n}\n\n#[test]\nfn retrying_mutexes() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet good_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(5));\n\tlet evil_mutex: Arc\u003cMutex\u003ci32, EvilMutex\u003e\u003e = Arc::new(Mutex::new(7));\n\tlet useless_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(10));\n\tlet c_good = Arc::clone(\u0026good_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tRetryingLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\tlet _ = collection.try_lock(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(good_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n\tassert!(evil_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","evil_try_rwlock.rs"],"content":"use std::sync::Arc;\n\nuse happylock::collection::{BoxedLockCollection, RetryingLockCollection};\nuse happylock::rwlock::RwLock;\nuse happylock::ThreadKey;\nuse lock_api::{GuardNoSend, RawRwLock};\n\nstruct EvilRwLock {\n\tinner: parking_lot::RawRwLock,\n}\n\nunsafe impl RawRwLock for EvilRwLock {\n\t#[allow(clippy::declare_interior_mutable_const)]\n\tconst INIT: Self = Self {\n\t\tinner: parking_lot::RawRwLock::INIT,\n\t};\n\n\ttype GuardMarker = GuardNoSend;\n\n\tfn lock_shared(\u0026self) {\n\t\tself.inner.lock_shared()\n\t}\n\n\tfn try_lock_shared(\u0026self) -\u003e bool {\n\t\tpanic!(\"mwahahahaha\")\n\t}\n\n\tunsafe fn unlock_shared(\u0026self) {\n\t\tself.inner.unlock_shared()\n\t}\n\n\tfn lock_exclusive(\u0026self) {\n\t\tself.inner.lock_exclusive()\n\t}\n\n\tfn try_lock_exclusive(\u0026self) -\u003e bool {\n\t\tpanic!(\"mwahahahaha\")\n\t}\n\n\tunsafe fn unlock_exclusive(\u0026self) {\n\t\tself.inner.unlock_exclusive()\n\t}\n}\n\n#[test]\nfn boxed_rwlocks() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet good_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(5));\n\tlet evil_mutex: Arc\u003cRwLock\u003ci32, EvilRwLock\u003e\u003e = Arc::new(RwLock::new(7));\n\tlet useless_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(10));\n\tlet c_good = Arc::clone(\u0026good_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\tlet _ = collection.try_read(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(good_mutex.scoped_try_read(\u0026mut key, |_| {}).is_ok());\n\tassert!(evil_mutex.scoped_try_read(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_read(\u0026mut key, |_| {}).is_ok());\n}\n\n#[test]\nfn retrying_rwlocks() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet good_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(5));\n\tlet evil_mutex: Arc\u003cRwLock\u003ci32, EvilRwLock\u003e\u003e = Arc::new(RwLock::new(7));\n\tlet useless_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(10));\n\tlet c_good = Arc::clone(\u0026good_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tRetryingLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\t_ = collection.try_read(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(good_mutex.scoped_try_read(\u0026mut key, |_| {}).is_ok());\n\tassert!(evil_mutex.scoped_try_read(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_read(\u0026mut key, |_| {}).is_ok());\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","evil_unlock_mutex.rs"],"content":"use std::sync::Arc;\n\nuse happylock::collection::{BoxedLockCollection, RetryingLockCollection};\nuse happylock::mutex::Mutex;\nuse happylock::ThreadKey;\nuse lock_api::{GuardNoSend, RawMutex};\n\nstruct KindaEvilMutex {\n\tinner: parking_lot::RawMutex,\n}\n\nstruct EvilMutex {}\n\nunsafe impl RawMutex for KindaEvilMutex {\n\t#[allow(clippy::declare_interior_mutable_const)]\n\tconst INIT: Self = Self {\n\t\tinner: parking_lot::RawMutex::INIT,\n\t};\n\n\ttype GuardMarker = GuardNoSend;\n\n\tfn lock(\u0026self) {\n\t\tself.inner.lock()\n\t}\n\n\tfn try_lock(\u0026self) -\u003e bool {\n\t\tself.inner.try_lock()\n\t}\n\n\tunsafe fn unlock(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n}\n\nunsafe impl RawMutex for EvilMutex {\n\t#[allow(clippy::declare_interior_mutable_const)]\n\tconst INIT: Self = Self {};\n\n\ttype GuardMarker = GuardNoSend;\n\n\tfn lock(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn try_lock(\u0026self) -\u003e bool {\n\t\tpanic!(\"mwahahahaha\")\n\t}\n\n\tunsafe fn unlock(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n}\n\n#[test]\nfn boxed_mutexes() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet kinda_evil_mutex: Arc\u003cMutex\u003ci32, KindaEvilMutex\u003e\u003e = Arc::new(Mutex::new(5));\n\tlet evil_mutex: Arc\u003cMutex\u003ci32, EvilMutex\u003e\u003e = Arc::new(Mutex::new(7));\n\tlet useless_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(10));\n\tlet c_good = Arc::clone(\u0026kinda_evil_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\t_ = collection.lock(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(kinda_evil_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n\tassert!(evil_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n}\n\n#[test]\nfn retrying_mutexes() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet kinda_evil_mutex: Arc\u003cMutex\u003ci32, KindaEvilMutex\u003e\u003e = Arc::new(Mutex::new(5));\n\tlet evil_mutex: Arc\u003cMutex\u003ci32, EvilMutex\u003e\u003e = Arc::new(Mutex::new(7));\n\tlet useless_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(10));\n\tlet c_good = Arc::clone(\u0026kinda_evil_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tRetryingLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\tcollection.lock(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(kinda_evil_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n\tassert!(evil_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","evil_unlock_rwlock.rs"],"content":"use std::sync::Arc;\n\nuse happylock::collection::{BoxedLockCollection, RetryingLockCollection};\nuse happylock::rwlock::RwLock;\nuse happylock::ThreadKey;\nuse lock_api::{GuardNoSend, RawRwLock};\n\nstruct KindaEvilRwLock {\n\tinner: parking_lot::RawRwLock,\n}\n\nstruct EvilRwLock {}\n\nunsafe impl RawRwLock for KindaEvilRwLock {\n\t#[allow(clippy::declare_interior_mutable_const)]\n\tconst INIT: Self = Self {\n\t\tinner: parking_lot::RawRwLock::INIT,\n\t};\n\n\ttype GuardMarker = GuardNoSend;\n\n\tfn lock_shared(\u0026self) {\n\t\tself.inner.lock_shared()\n\t}\n\n\tfn try_lock_shared(\u0026self) -\u003e bool {\n\t\tself.inner.try_lock_shared()\n\t}\n\n\tunsafe fn unlock_shared(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn lock_exclusive(\u0026self) {\n\t\tself.inner.lock_exclusive()\n\t}\n\n\tfn try_lock_exclusive(\u0026self) -\u003e bool {\n\t\tself.inner.try_lock_exclusive()\n\t}\n\n\tunsafe fn unlock_exclusive(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n}\n\nunsafe impl RawRwLock for EvilRwLock {\n\t#[allow(clippy::declare_interior_mutable_const)]\n\tconst INIT: Self = Self {};\n\n\ttype GuardMarker = GuardNoSend;\n\n\tfn lock_shared(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn try_lock_shared(\u0026self) -\u003e bool {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tunsafe fn unlock_shared(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn lock_exclusive(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn try_lock_exclusive(\u0026self) -\u003e bool {\n\t\tpanic!(\"mwahahahaha\")\n\t}\n\n\tunsafe fn unlock_exclusive(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n}\n\n#[test]\nfn boxed_rwlocks() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet kinda_evil_mutex: RwLock\u003ci32, KindaEvilRwLock\u003e = RwLock::new(5);\n\tlet evil_mutex: RwLock\u003ci32, EvilRwLock\u003e = RwLock::new(7);\n\tlet useless_mutex: RwLock\u003ci32, parking_lot::RawRwLock\u003e = RwLock::new(10);\n\n\tlet r = std::thread::scope(|s| {\n\t\tlet r = s\n\t\t\t.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet collection =\n\t\t\t\t\tBoxedLockCollection::try_new((\u0026kinda_evil_mutex, \u0026evil_mutex, \u0026useless_mutex))\n\t\t\t\t\t\t.unwrap();\n\t\t\t\t_ = collection.read(key);\n\t\t\t})\n\t\t\t.join();\n\n\t\tr\n\t});\n\n\tassert!(r.is_err());\n\tassert!(kinda_evil_mutex.scoped_try_write(\u0026mut key, |_| {}).is_err());\n\tassert!(evil_mutex.scoped_try_write(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_write(\u0026mut key, |_| {}).is_ok());\n}\n\n#[test]\nfn retrying_rwlocks() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet kinda_evil_mutex: Arc\u003cRwLock\u003ci32, KindaEvilRwLock\u003e\u003e = Arc::new(RwLock::new(5));\n\tlet evil_mutex: Arc\u003cRwLock\u003ci32, EvilRwLock\u003e\u003e = Arc::new(RwLock::new(7));\n\tlet useless_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(10));\n\tlet c_good = Arc::clone(\u0026kinda_evil_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tRetryingLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\tcollection.read(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(kinda_evil_mutex.scoped_try_write(\u0026mut key, |_| {}).is_err());\n\tassert!(evil_mutex.scoped_try_write(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_write(\u0026mut key, |_| {}).is_ok());\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","forget.rs"],"content":"use happylock::{Mutex, ThreadKey};\n\n#[test]\nfn no_new_threadkey_when_forgetting_lock() {\n\tlet key = ThreadKey::get().unwrap();\n\tlet mutex = Mutex::new(\"foo\".to_string());\n\n\tlet guard = mutex.lock(key);\n\tstd::mem::forget(guard);\n\n\tassert!(ThreadKey::get().is_none());\n}\n\n#[test]\nfn no_new_threadkey_in_scoped_lock() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet mutex = Mutex::new(\"foo\".to_string());\n\n\tmutex.scoped_lock(\u0026mut key, |_| {\n\t\tassert!(ThreadKey::get().is_none());\n\t});\n\n\tmutex.lock(key);\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","retry.rs"],"content":"use std::time::Duration;\n\nuse happylock::{collection::RetryingLockCollection, Mutex, ThreadKey};\n\nstatic MUTEX_1: Mutex\u003ci32\u003e = Mutex::new(1);\nstatic MUTEX_2: Mutex\u003ci32\u003e = Mutex::new(2);\nstatic MUTEX_3: Mutex\u003ci32\u003e = Mutex::new(3);\n\nfn thread_1() {\n\tlet key = ThreadKey::get().unwrap();\n\tlet mut guard = MUTEX_2.lock(key);\n\tstd::thread::sleep(Duration::from_millis(100));\n\t*guard = 5;\n}\n\nfn thread_2() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tstd::thread::sleep(Duration::from_millis(50));\n\tlet collection = RetryingLockCollection::try_new([\u0026MUTEX_1, \u0026MUTEX_2, \u0026MUTEX_3]).unwrap();\n\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\tassert_eq!(*guard[0], 4);\n\t\tassert_eq!(*guard[1], 5);\n\t\tassert_eq!(*guard[2], 3);\n\t});\n}\n\nfn thread_3() {\n\tlet key = ThreadKey::get().unwrap();\n\tstd::thread::sleep(Duration::from_millis(75));\n\tlet mut guard = MUTEX_1.lock(key);\n\tstd::thread::sleep(Duration::from_millis(100));\n\t*guard = 4;\n}\n\nfn thread_4() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tstd::thread::sleep(Duration::from_millis(25));\n\tlet collection = RetryingLockCollection::try_new([\u0026MUTEX_1, \u0026MUTEX_2]).unwrap();\n\tassert!(collection.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n}\n\n#[test]\nfn retries() {\n\tlet t1 = std::thread::spawn(thread_1);\n\tlet t2 = std::thread::spawn(thread_2);\n\tlet t3 = std::thread::spawn(thread_3);\n\tlet t4 = std::thread::spawn(thread_4);\n\n\tt1.join().unwrap();\n\tt2.join().unwrap();\n\tt3.join().unwrap();\n\tt4.join().unwrap();\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","retry_rw.rs"],"content":"use std::time::Duration;\n\nuse happylock::{collection::RetryingLockCollection, RwLock, ThreadKey};\n\nstatic RWLOCK_1: RwLock\u003ci32\u003e = RwLock::new(1);\nstatic RWLOCK_2: RwLock\u003ci32\u003e = RwLock::new(2);\nstatic RWLOCK_3: RwLock\u003ci32\u003e = RwLock::new(3);\n\nfn thread_1() {\n\tlet key = ThreadKey::get().unwrap();\n\tlet mut guard = RWLOCK_2.write(key);\n\tstd::thread::sleep(Duration::from_millis(75));\n\tassert_eq!(*guard, 2);\n\t*guard = 5;\n}\n\nfn thread_2() {\n\tlet key = ThreadKey::get().unwrap();\n\tlet collection = RetryingLockCollection::try_new([\u0026RWLOCK_1, \u0026RWLOCK_2, \u0026RWLOCK_3]).unwrap();\n\tstd::thread::sleep(Duration::from_millis(25));\n\tlet guard = collection.read(key);\n\tassert_eq!(*guard[0], 1);\n\tassert_eq!(*guard[1], 5);\n\tassert_eq!(*guard[2], 3);\n}\n\nfn thread_3() {\n\tlet key = ThreadKey::get().unwrap();\n\tstd::thread::sleep(Duration::from_millis(50));\n\tlet guard = RWLOCK_1.write(key);\n\tstd::thread::sleep(Duration::from_millis(50));\n\tassert_eq!(*guard, 1);\n}\n\n#[test]\nfn retries() {\n\tlet t1 = std::thread::spawn(thread_1);\n\tlet t2 = std::thread::spawn(thread_2);\n\tlet t3 = std::thread::spawn(thread_3);\n\n\tt1.join().unwrap();\n\tt2.join().unwrap();\n\tt3.join().unwrap();\n}\n","traces":[],"covered":0,"coverable":0}]};
- var previousData = {"files":[{"path":["/","home","botahamec","Projects","happylock","examples","basic.rs"],"content":"use std::thread;\n\nuse happylock::{Mutex, ThreadKey};\n\nconst N: usize = 10;\n\nstatic DATA: Mutex\u003ci32\u003e = Mutex::new(0);\n\nfn main() {\n\tlet mut threads = Vec::new();\n\tfor _ in 0..N {\n\t\tlet th = thread::spawn(move || {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\tlet mut data = DATA.lock(key);\n\t\t\t*data += 1;\n\t\t});\n\t\tthreads.push(th);\n\t}\n\n\tfor th in threads {\n\t\t_ = th.join();\n\t}\n\n\tlet key = ThreadKey::get().unwrap();\n\tlet data = DATA.lock(key);\n\tprintln!(\"{data}\");\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","examples","dining_philosophers.rs"],"content":"use std::{thread, time::Duration};\n\nuse happylock::{collection, Mutex, ThreadKey};\n\nstatic PHILOSOPHERS: [Philosopher; 5] = [\n\tPhilosopher {\n\t\tname: \"Socrates\",\n\t\tleft: 0,\n\t\tright: 1,\n\t},\n\tPhilosopher {\n\t\tname: \"John Rawls\",\n\t\tleft: 1,\n\t\tright: 2,\n\t},\n\tPhilosopher {\n\t\tname: \"Jeremy Bentham\",\n\t\tleft: 2,\n\t\tright: 3,\n\t},\n\tPhilosopher {\n\t\tname: \"John Stuart Mill\",\n\t\tleft: 3,\n\t\tright: 4,\n\t},\n\tPhilosopher {\n\t\tname: \"Judith Butler\",\n\t\tleft: 4,\n\t\tright: 0,\n\t},\n];\n\nstatic FORKS: [Mutex\u003c()\u003e; 5] = [\n\tMutex::new(()),\n\tMutex::new(()),\n\tMutex::new(()),\n\tMutex::new(()),\n\tMutex::new(()),\n];\n\nstruct Philosopher {\n\tname: \u0026'static str,\n\tleft: usize,\n\tright: usize,\n}\n\nimpl Philosopher {\n\tfn cycle(\u0026self) {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tthread::sleep(Duration::from_secs(1));\n\n\t\t// safety: no philosopher asks for the same fork twice\n\t\tlet forks = [\u0026FORKS[self.left], \u0026FORKS[self.right]];\n\t\tlet forks = unsafe { collection::RefLockCollection::new_unchecked(\u0026forks) };\n\t\tlet forks = forks.lock(key);\n\t\tprintln!(\"{} is eating...\", self.name);\n\t\tthread::sleep(Duration::from_secs(1));\n\t\tprintln!(\"{} is done eating\", self.name);\n\t\tdrop(forks);\n\t}\n}\n\nfn main() {\n\tlet handles: Vec\u003c_\u003e = PHILOSOPHERS\n\t\t.iter()\n\t\t.map(|philosopher| thread::spawn(move || philosopher.cycle()))\n\t\t// The `collect` is absolutely necessary, because we're using lazy\n\t\t// iterators. If `collect` isn't used, then the thread won't spawn\n\t\t// until we try to join on it.\n\t\t.collect();\n\n\tfor handle in handles {\n\t\t_ = handle.join();\n\t}\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","examples","dining_philosophers_retry.rs"],"content":"use std::{thread, time::Duration};\n\nuse happylock::{collection, Mutex, ThreadKey};\n\nstatic PHILOSOPHERS: [Philosopher; 5] = [\n\tPhilosopher {\n\t\tname: \"Socrates\",\n\t\tleft: 0,\n\t\tright: 1,\n\t},\n\tPhilosopher {\n\t\tname: \"John Rawls\",\n\t\tleft: 1,\n\t\tright: 2,\n\t},\n\tPhilosopher {\n\t\tname: \"Jeremy Bentham\",\n\t\tleft: 2,\n\t\tright: 3,\n\t},\n\tPhilosopher {\n\t\tname: \"John Stuart Mill\",\n\t\tleft: 3,\n\t\tright: 4,\n\t},\n\tPhilosopher {\n\t\tname: \"Judith Butler\",\n\t\tleft: 4,\n\t\tright: 0,\n\t},\n];\n\nstatic FORKS: [Mutex\u003c()\u003e; 5] = [\n\tMutex::new(()),\n\tMutex::new(()),\n\tMutex::new(()),\n\tMutex::new(()),\n\tMutex::new(()),\n];\n\nstruct Philosopher {\n\tname: \u0026'static str,\n\tleft: usize,\n\tright: usize,\n}\n\nimpl Philosopher {\n\tfn cycle(\u0026self) {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tthread::sleep(Duration::from_secs(1));\n\n\t\t// safety: no philosopher asks for the same fork twice\n\t\tlet forks = [\u0026FORKS[self.left], \u0026FORKS[self.right]];\n\t\tlet forks = unsafe { collection::RetryingLockCollection::new_unchecked(\u0026forks) };\n\t\tlet forks = forks.lock(key);\n\t\tprintln!(\"{} is eating...\", self.name);\n\t\tthread::sleep(Duration::from_secs(1));\n\t\tprintln!(\"{} is done eating\", self.name);\n\t\tdrop(forks);\n\t}\n}\n\nfn main() {\n\tlet handles: Vec\u003c_\u003e = PHILOSOPHERS\n\t\t.iter()\n\t\t.map(|philosopher| thread::spawn(move || philosopher.cycle()))\n\t\t// The `collect` is absolutely necessary, because we're using lazy\n\t\t// iterators. If `collect` isn't used, then the thread won't spawn\n\t\t// until we try to join on it.\n\t\t.collect();\n\n\tfor handle in handles {\n\t\t_ = handle.join();\n\t}\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","examples","double_mutex.rs"],"content":"use std::thread;\n\nuse happylock::{collection::RefLockCollection, Mutex, ThreadKey};\n\nconst N: usize = 10;\n\nstatic DATA: (Mutex\u003ci32\u003e, Mutex\u003cString\u003e) = (Mutex::new(0), Mutex::new(String::new()));\n\nfn main() {\n\tlet mut threads = Vec::new();\n\tfor _ in 0..N {\n\t\tlet th = thread::spawn(move || {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\tlet lock = RefLockCollection::new(\u0026DATA);\n\t\t\tlet mut guard = lock.lock(key);\n\t\t\t*guard.1 = (100 - *guard.0).to_string();\n\t\t\t*guard.0 += 1;\n\t\t});\n\t\tthreads.push(th);\n\t}\n\n\tfor th in threads {\n\t\t_ = th.join();\n\t}\n\n\tlet key = ThreadKey::get().unwrap();\n\tlet data = RefLockCollection::new(\u0026DATA);\n\tlet data = data.lock(key);\n\tprintln!(\"{}\", data.0);\n\tprintln!(\"{}\", data.1);\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","examples","fibonacci.rs"],"content":"use happylock::{collection, LockCollection, Mutex, ThreadKey};\nuse std::thread;\n\nconst N: usize = 36;\n\nstatic DATA: [Mutex\u003ci32\u003e; 2] = [Mutex::new(0), Mutex::new(1)];\n\nfn main() {\n\tlet mut threads = Vec::new();\n\tfor _ in 0..N {\n\t\tlet th = thread::spawn(move || {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\n\t\t\t// a reference to a type that implements `OwnedLockable` will never\n\t\t\t// contain duplicates, so no duplicate checking is needed.\n\t\t\tlet collection = collection::RetryingLockCollection::new_ref(\u0026DATA);\n\t\t\tlet mut guard = collection.lock(key);\n\n\t\t\tlet x = *guard[1];\n\t\t\t*guard[1] += *guard[0];\n\t\t\t*guard[0] = x;\n\t\t});\n\t\tthreads.push(th);\n\t}\n\n\tfor thread in threads {\n\t\t_ = thread.join();\n\t}\n\n\tlet key = ThreadKey::get().unwrap();\n\tlet data = LockCollection::new_ref(\u0026DATA);\n\tlet data = data.lock(key);\n\tprintln!(\"{}\", data[0]);\n\tprintln!(\"{}\", data[1]);\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","examples","list.rs"],"content":"use std::thread;\n\nuse happylock::{collection::RefLockCollection, Mutex, ThreadKey};\n\nconst N: usize = 10;\n\nstatic DATA: [Mutex\u003cusize\u003e; 6] = [\n\tMutex::new(0),\n\tMutex::new(1),\n\tMutex::new(2),\n\tMutex::new(3),\n\tMutex::new(4),\n\tMutex::new(5),\n];\n\nstatic SEED: Mutex\u003cu32\u003e = Mutex::new(42);\n\nfn random(key: \u0026mut ThreadKey) -\u003e usize {\n\tSEED.scoped_lock(key, |seed| {\n\t\tlet x = *seed;\n\t\tlet x = x ^ (x \u003c\u003c 13);\n\t\tlet x = x ^ (x \u003e\u003e 17);\n\t\tlet x = x ^ (x \u003c\u003c 5);\n\t\t*seed = x;\n\t\tx as usize\n\t})\n}\n\nfn main() {\n\tlet mut threads = Vec::new();\n\tfor _ in 0..N {\n\t\tlet th = thread::spawn(move || {\n\t\t\tlet mut key = ThreadKey::get().unwrap();\n\t\t\tloop {\n\t\t\t\tlet mut data = Vec::new();\n\t\t\t\tfor _ in 0..3 {\n\t\t\t\t\tlet rand = random(\u0026mut key);\n\t\t\t\t\tdata.push(\u0026DATA[rand % 6]);\n\t\t\t\t}\n\n\t\t\t\tlet Some(lock) = RefLockCollection::try_new(\u0026data) else {\n\t\t\t\t\tcontinue;\n\t\t\t\t};\n\t\t\t\tlet mut guard = lock.lock(key);\n\t\t\t\t*guard[0] += *guard[1];\n\t\t\t\t*guard[1] += *guard[2];\n\t\t\t\t*guard[2] += *guard[0];\n\n\t\t\t\treturn;\n\t\t\t}\n\t\t});\n\t\tthreads.push(th);\n\t}\n\n\tfor th in threads {\n\t\t_ = th.join();\n\t}\n\n\tlet key = ThreadKey::get().unwrap();\n\tlet data = RefLockCollection::new(\u0026DATA);\n\tlet data = data.lock(key);\n\tfor val in \u0026*data {\n\t\tprintln!(\"{val}\");\n\t}\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","src","collection","boxed.rs"],"content":"use std::cell::UnsafeCell;\nuse std::fmt::Debug;\n\nuse crate::lockable::{Lockable, LockableIntoInner, OwnedLockable, RawLock, Sharable};\nuse crate::{Keyable, ThreadKey};\n\nuse super::utils::{\n\tordered_contains_duplicates, scoped_read, scoped_try_read, scoped_try_write, scoped_write,\n};\nuse super::{utils, BoxedLockCollection, LockGuard};\n\nunsafe impl\u003cL: Lockable\u003e RawLock for BoxedLockCollection\u003cL\u003e {\n\t#[mutants::skip] // this should never be called\n\t#[cfg(not(tarpaulin_include))]\n\tfn poison(\u0026self) {\n\t\tfor lock in \u0026self.locks {\n\t\t\tlock.poison();\n\t\t}\n\t}\n\n\tunsafe fn raw_write(\u0026self) {\n\t\tutils::ordered_write(self.locks())\n\t}\n\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool {\n\t\tprintln!(\"{}\", self.locks().len());\n\t\tutils::ordered_try_write(self.locks())\n\t}\n\n\tunsafe fn raw_unlock_write(\u0026self) {\n\t\tfor lock in self.locks() {\n\t\t\tlock.raw_unlock_write();\n\t\t}\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tutils::ordered_read(self.locks());\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tutils::ordered_try_read(self.locks())\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tfor lock in self.locks() {\n\t\t\tlock.raw_unlock_read();\n\t\t}\n\t}\n}\n\nunsafe impl\u003cL: Lockable\u003e Lockable for BoxedLockCollection\u003cL\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= L::Guard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= L::DataMut\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tptrs.push(self);\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tself.child().guard()\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.child().data_mut()\n\t}\n}\n\nunsafe impl\u003cL: Sharable\u003e Sharable for BoxedLockCollection\u003cL\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= L::ReadGuard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= L::DataRef\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tself.child().read_guard()\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tself.child().data_ref()\n\t}\n}\n\nunsafe impl\u003cL: OwnedLockable\u003e OwnedLockable for BoxedLockCollection\u003cL\u003e {}\n\n// LockableGetMut can't be implemented because that would create mutable and\n// immutable references to the same value at the same time.\n\nimpl\u003cL: LockableIntoInner\u003e LockableIntoInner for BoxedLockCollection\u003cL\u003e {\n\ttype Inner = L::Inner;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tLockableIntoInner::into_inner(self.into_child())\n\t}\n}\n\nimpl\u003cL\u003e IntoIterator for BoxedLockCollection\u003cL\u003e\nwhere\n\tL: IntoIterator,\n{\n\ttype Item = \u003cL as IntoIterator\u003e::Item;\n\ttype IntoIter = \u003cL as IntoIterator\u003e::IntoIter;\n\n\tfn into_iter(self) -\u003e Self::IntoIter {\n\t\tself.into_child().into_iter()\n\t}\n}\n\nimpl\u003c'a, L\u003e IntoIterator for \u0026'a BoxedLockCollection\u003cL\u003e\nwhere\n\t\u0026'a L: IntoIterator,\n{\n\ttype Item = \u003c\u0026'a L as IntoIterator\u003e::Item;\n\ttype IntoIter = \u003c\u0026'a L as IntoIterator\u003e::IntoIter;\n\n\tfn into_iter(self) -\u003e Self::IntoIter {\n\t\tself.child().into_iter()\n\t}\n}\n\nimpl\u003cL: OwnedLockable, I: FromIterator\u003cL\u003e + OwnedLockable\u003e FromIterator\u003cL\u003e\n\tfor BoxedLockCollection\u003cI\u003e\n{\n\tfn from_iter\u003cT: IntoIterator\u003cItem = L\u003e\u003e(iter: T) -\u003e Self {\n\t\tlet iter: I = iter.into_iter().collect();\n\t\tSelf::new(iter)\n\t}\n}\n\n// safety: the RawLocks must be send because they come from the Send Lockable\n#[allow(clippy::non_send_fields_in_send_ty)]\nunsafe impl\u003cL: Send\u003e Send for BoxedLockCollection\u003cL\u003e {}\nunsafe impl\u003cL: Sync\u003e Sync for BoxedLockCollection\u003cL\u003e {}\n\nimpl\u003cL\u003e Drop for BoxedLockCollection\u003cL\u003e {\n\t#[mutants::skip] // i can't test for a memory leak\n\t#[cfg(not(tarpaulin_include))]\n\tfn drop(\u0026mut self) {\n\t\tunsafe {\n\t\t\t// safety: this collection will never be locked again\n\t\t\tself.locks.clear();\n\t\t\t// safety: this was allocated using a box, and is now unique\n\t\t\tlet boxed: Box\u003cUnsafeCell\u003cL\u003e\u003e = Box::from_raw(self.data.cast_mut());\n\n\t\t\tdrop(boxed)\n\t\t}\n\t}\n}\n\nimpl\u003cT: ?Sized, L: AsRef\u003cT\u003e\u003e AsRef\u003cT\u003e for BoxedLockCollection\u003cL\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself.child().as_ref()\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cL: Debug\u003e Debug for BoxedLockCollection\u003cL\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tf.debug_struct(stringify!(BoxedLockCollection))\n\t\t\t.field(\"data\", \u0026self.data)\n\t\t\t.finish_non_exhaustive()\n\t}\n}\n\nimpl\u003cL: OwnedLockable + Default\u003e Default for BoxedLockCollection\u003cL\u003e {\n\tfn default() -\u003e Self {\n\t\tSelf::new(L::default())\n\t}\n}\n\nimpl\u003cL: OwnedLockable\u003e From\u003cL\u003e for BoxedLockCollection\u003cL\u003e {\n\tfn from(value: L) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\nimpl\u003cL\u003e BoxedLockCollection\u003cL\u003e {\n\t/// Gets the underlying collection, consuming this collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey, LockCollection};\n\t///\n\t/// let data1 = Mutex::new(42);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // data1 and data2 refer to distinct mutexes, so this won't panic\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = LockCollection::try_new(\u0026data).unwrap();\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let guard = lock.into_child().0.lock(key);\n\t/// assert_eq!(*guard, 42);\n\t/// ```\n\t#[must_use]\n\tpub fn into_child(mut self) -\u003e L {\n\t\tunsafe {\n\t\t\t// safety: this collection will never be used again\n\t\t\tstd::ptr::drop_in_place(\u0026mut self.locks);\n\t\t\t// safety: this was allocated using a box, and is now unique\n\t\t\tlet boxed: Box\u003cUnsafeCell\u003cL\u003e\u003e = Box::from_raw(self.data.cast_mut());\n\t\t\t// to prevent a double free\n\t\t\tstd::mem::forget(self);\n\n\t\t\tboxed.into_inner()\n\t\t}\n\t}\n\n\t// child_mut is immediate UB because it leads to mutable and immutable\n\t// references happening at the same time\n\n\t/// Gets an immutable reference to the underlying data\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey, LockCollection};\n\t///\n\t/// let data1 = Mutex::new(42);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // data1 and data2 refer to distinct mutexes, so this won't panic\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = LockCollection::try_new(\u0026data).unwrap();\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let guard = lock.child().0.lock(key);\n\t/// assert_eq!(*guard, 42);\n\t/// ```\n\t#[must_use]\n\tpub fn child(\u0026self) -\u003e \u0026L {\n\t\tunsafe {\n\t\t\tself.data\n\t\t\t\t.as_ref()\n\t\t\t\t.unwrap_unchecked()\n\t\t\t\t.get()\n\t\t\t\t.as_ref()\n\t\t\t\t.unwrap_unchecked()\n\t\t}\n\t}\n\n\t/// Gets the locks\n\tfn locks(\u0026self) -\u003e \u0026[\u0026dyn RawLock] {\n\t\t\u0026self.locks\n\t}\n}\n\nimpl\u003cL: OwnedLockable\u003e BoxedLockCollection\u003cL\u003e {\n\t/// Creates a new collection of owned locks.\n\t///\n\t/// Because the locks are owned, there's no need to do any checks for\n\t/// duplicate values.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t///\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t/// ```\n\t#[must_use]\n\tpub fn new(data: L) -\u003e Self {\n\t\t// safety: owned lockable types cannot contain duplicates\n\t\tunsafe { Self::new_unchecked(data) }\n\t}\n}\n\nimpl\u003c'a, L: OwnedLockable\u003e BoxedLockCollection\u003c\u0026'a L\u003e {\n\t/// Creates a new collection of owned locks.\n\t///\n\t/// Because the locks are owned, there's no need to do any checks for\n\t/// duplicate values.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t///\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = LockCollection::new_ref(\u0026data);\n\t/// ```\n\t#[must_use]\n\tpub fn new_ref(data: \u0026'a L) -\u003e Self {\n\t\t// safety: owned lockable types cannot contain duplicates\n\t\tunsafe { Self::new_unchecked(data) }\n\t}\n}\n\nimpl\u003cL: Lockable\u003e BoxedLockCollection\u003cL\u003e {\n\t/// Creates a new collections of locks.\n\t///\n\t/// # Safety\n\t///\n\t/// This results in undefined behavior if any locks are presented twice\n\t/// within this collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t///\n\t/// let data1 = Mutex::new(0);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // safety: data1 and data2 refer to distinct mutexes\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = unsafe { LockCollection::new_unchecked(\u0026data) };\n\t/// ```\n\t#[must_use]\n\tpub unsafe fn new_unchecked(data: L) -\u003e Self {\n\t\tlet data = Box::leak(Box::new(UnsafeCell::new(data)));\n\t\tlet data_ref = data.get().cast_const().as_ref().unwrap_unchecked();\n\n\t\tlet mut locks = Vec::new();\n\t\tdata_ref.get_ptrs(\u0026mut locks);\n\n\t\t// cast to *const () because fat pointers can't be converted to usize\n\t\tlocks.sort_by_key(|lock| (\u0026raw const **lock).cast::\u003c()\u003e() as usize);\n\n\t\t// safety we're just changing the lifetimes\n\t\tlet locks: Vec\u003c\u0026'static dyn RawLock\u003e = std::mem::transmute(locks);\n\t\tlet data = \u0026raw const *data;\n\t\tSelf { data, locks }\n\t}\n\n\t/// Creates a new collection of locks.\n\t///\n\t/// This returns `None` if any locks are found twice in the given\n\t/// collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t///\n\t/// let data1 = Mutex::new(0);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // data1 and data2 refer to distinct mutexes, so this won't panic\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = LockCollection::try_new(\u0026data).unwrap();\n\t/// ```\n\t#[must_use]\n\tpub fn try_new(data: L) -\u003e Option\u003cSelf\u003e {\n\t\t// safety: we are checking for duplicates before returning\n\t\tunsafe {\n\t\t\tlet this = Self::new_unchecked(data);\n\t\t\tif ordered_contains_duplicates(this.locks()) {\n\t\t\t\treturn None;\n\t\t\t}\n\t\t\tSome(this)\n\t\t}\n\t}\n\n\tpub fn scoped_lock\u003c'a, R\u003e(\u0026'a self, key: impl Keyable, f: impl Fn(L::DataMut\u003c'a\u003e) -\u003e R) -\u003e R {\n\t\tscoped_write(self, key, f)\n\t}\n\n\tpub fn scoped_try_lock\u003c'a, Key: Keyable, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(L::DataMut\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tscoped_try_write(self, key, f)\n\t}\n\n\t/// Locks the collection\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data. When the guard is dropped, the locks in the collection are also\n\t/// dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// ```\n\t#[must_use]\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::Guard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_write();\n\n\t\t\tLockGuard {\n\t\t\t\t// safety: we've already acquired the lock\n\t\t\t\tguard: self.child().guard(),\n\t\t\t\tkey,\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Attempts to lock the without blocking.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// locks when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If any locks in the collection are already locked, then an error\n\t/// containing the given key is returned.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// match lock.try_lock(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::Guard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tlet guard = unsafe {\n\t\t\tif !self.raw_try_write() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we've acquired the locks\n\t\t\tself.child().guard()\n\t\t};\n\n\t\tOk(LockGuard { guard, key })\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// let key = LockCollection::\u003c(Mutex\u003ci32\u003e, Mutex\u003c\u0026str\u003e)\u003e::unlock(guard);\n\t/// ```\n\tpub fn unlock(guard: LockGuard\u003cL::Guard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: Sharable\u003e BoxedLockCollection\u003cL\u003e {\n\tpub fn scoped_read\u003c'a, R\u003e(\u0026'a self, key: impl Keyable, f: impl Fn(L::DataRef\u003c'a\u003e) -\u003e R) -\u003e R {\n\t\tscoped_read(self, key, f)\n\t}\n\n\tpub fn scoped_try_read\u003c'a, Key: Keyable, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(L::DataRef\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tscoped_try_read(self, key, f)\n\t}\n\n\t/// Locks the collection, so that other threads can still read from it\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data immutably. When the guard is dropped, the locks in the collection\n\t/// are also dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// assert_eq!(*guard.0, 0);\n\t/// assert_eq!(*guard.1, \"\");\n\t/// ```\n\t#[must_use]\n\tpub fn read(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_read();\n\n\t\t\tLockGuard {\n\t\t\t\t// safety: we've already acquired the lock\n\t\t\t\tguard: self.child().read_guard(),\n\t\t\t\tkey,\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Attempts to lock the without blocking, in such a way that other threads\n\t/// can still read from the collection.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// shared access when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If any of the locks in the collection are already locked, then an error\n\t/// is returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(5), RwLock::new(\"6\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// match lock.try_read(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// assert_eq!(*guard.0, 5);\n\t/// assert_eq!(*guard.1, \"6\");\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_read(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tlet guard = unsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_read() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we've acquired the locks\n\t\t\tself.child().read_guard()\n\t\t};\n\n\t\tOk(LockGuard { guard, key })\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// let key = LockCollection::\u003c(RwLock\u003ci32\u003e, RwLock\u003c\u0026str\u003e)\u003e::unlock_read(guard);\n\t/// ```\n\tpub fn unlock_read(guard: LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: LockableIntoInner\u003e BoxedLockCollection\u003cL\u003e {\n\t/// Consumes this `BoxedLockCollection`, returning the underlying data.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t///\n\t/// let mutex = LockCollection::new([Mutex::new(0), Mutex::new(0)]);\n\t/// assert_eq!(mutex.into_inner(), [0, 0]);\n\t/// ```\n\t#[must_use]\n\tpub fn into_inner(self) -\u003e \u003cSelf as LockableIntoInner\u003e::Inner {\n\t\tLockableIntoInner::into_inner(self)\n\t}\n}\n\nimpl\u003c'a, L: 'a\u003e BoxedLockCollection\u003cL\u003e\nwhere\n\t\u0026'a L: IntoIterator,\n{\n\t/// Returns an iterator over references to each value in the collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = [Mutex::new(26), Mutex::new(1)];\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// let mut iter = lock.iter();\n\t/// let mutex = iter.next().unwrap();\n\t/// let guard = mutex.lock(key);\n\t///\n\t/// assert_eq!(*guard, 26);\n\t/// ```\n\t#[must_use]\n\tpub fn iter(\u0026'a self) -\u003e \u003c\u0026'a L as IntoIterator\u003e::IntoIter {\n\t\tself.into_iter()\n\t}\n}\n\n#[cfg(test)]\nmod tests {\n\tuse super::*;\n\tuse crate::{Mutex, RwLock, ThreadKey};\n\n\t#[test]\n\tfn from_iterator() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection: BoxedLockCollection\u003cVec\u003cMutex\u003c\u0026str\u003e\u003e\u003e =\n\t\t\t[Mutex::new(\"foo\"), Mutex::new(\"bar\"), Mutex::new(\"baz\")]\n\t\t\t\t.into_iter()\n\t\t\t\t.collect();\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], \"foo\");\n\t\tassert_eq!(*guard[1], \"bar\");\n\t\tassert_eq!(*guard[2], \"baz\");\n\t}\n\n\t#[test]\n\tfn from() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tBoxedLockCollection::from([Mutex::new(\"foo\"), Mutex::new(\"bar\"), Mutex::new(\"baz\")]);\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], \"foo\");\n\t\tassert_eq!(*guard[1], \"bar\");\n\t\tassert_eq!(*guard[2], \"baz\");\n\t}\n\n\t#[test]\n\tfn into_owned_iterator() {\n\t\tlet collection = BoxedLockCollection::new([Mutex::new(0), Mutex::new(1), Mutex::new(2)]);\n\t\tfor (i, mutex) in collection.into_iter().enumerate() {\n\t\t\tassert_eq!(mutex.into_inner(), i);\n\t\t}\n\t}\n\n\t#[test]\n\tfn into_ref_iterator() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::new([Mutex::new(0), Mutex::new(1), Mutex::new(2)]);\n\t\tfor (i, mutex) in (\u0026collection).into_iter().enumerate() {\n\t\t\tmutex.scoped_lock(\u0026mut key, |val| assert_eq!(*val, i))\n\t\t}\n\t}\n\n\t#[test]\n\tfn ref_iterator() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::new([Mutex::new(0), Mutex::new(1), Mutex::new(2)]);\n\t\tfor (i, mutex) in collection.iter().enumerate() {\n\t\t\tmutex.scoped_lock(\u0026mut key, |val| assert_eq!(*val, i))\n\t\t}\n\t}\n\n\t#[test]\n\t#[allow(clippy::float_cmp)]\n\tfn uses_correct_default() {\n\t\tlet collection =\n\t\t\tBoxedLockCollection::\u003c(Mutex\u003cf64\u003e, Mutex\u003cOption\u003ci32\u003e\u003e, Mutex\u003cusize\u003e)\u003e::default();\n\t\tlet tuple = collection.into_inner();\n\t\tassert_eq!(tuple.0, 0.0);\n\t\tassert!(tuple.1.is_none());\n\t\tassert_eq!(tuple.2, 0)\n\t}\n\n\t#[test]\n\tfn non_duplicates_allowed() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(1);\n\t\tassert!(BoxedLockCollection::try_new([\u0026mutex1, \u0026mutex2]).is_some())\n\t}\n\n\t#[test]\n\tfn duplicates_not_allowed() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tassert!(BoxedLockCollection::try_new([\u0026mutex1, \u0026mutex1]).is_none())\n\t}\n\n\t#[test]\n\tfn scoped_read_sees_changes() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = BoxedLockCollection::new(mutexes);\n\t\tcollection.scoped_lock(\u0026mut key, |guard| *guard[0] = 128);\n\n\t\tlet sum = collection.scoped_read(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 128);\n\t\t\tassert_eq!(*guard[1], 42);\n\t\t\t*guard[0] + *guard[1]\n\t\t});\n\n\t\tassert_eq!(sum, 128 + 42);\n\t}\n\n\t#[test]\n\tfn scoped_try_lock_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::new([Mutex::new(1), Mutex::new(2)]);\n\t\tlet guard = collection.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = collection.scoped_try_lock(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn scoped_try_read_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::new([RwLock::new(1), RwLock::new(2)]);\n\t\tlet guard = collection.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = collection.scoped_try_read(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn try_lock_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::new([Mutex::new(1), Mutex::new(2)]);\n\t\tlet guard = collection.try_lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_lock(key);\n\t\t\t\tassert!(guard.is_err());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn try_read_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::new([RwLock::new(1), RwLock::new(2)]);\n\t\tlet guard = collection.try_read(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_read(key);\n\t\t\t\tassert!(guard.is_ok());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn try_lock_fails_with_one_exclusive_lock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = [Mutex::new(1), Mutex::new(2)];\n\t\tlet collection = BoxedLockCollection::new_ref(\u0026locks);\n\t\tlet guard = locks[1].try_lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_lock(key);\n\t\t\t\tassert!(guard.is_err());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn try_read_fails_during_exclusive_lock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::new([RwLock::new(1), RwLock::new(2)]);\n\t\tlet guard = collection.try_lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_read(key);\n\t\t\t\tassert!(guard.is_err());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn try_read_fails_with_one_exclusive_lock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = [RwLock::new(1), RwLock::new(2)];\n\t\tlet collection = BoxedLockCollection::new_ref(\u0026locks);\n\t\tlet guard = locks[1].try_write(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_read(key);\n\t\t\t\tassert!(guard.is_err());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn unlock_collection_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex1 = Mutex::new(\"foo\");\n\t\tlet mutex2 = Mutex::new(\"bar\");\n\t\tlet collection = BoxedLockCollection::try_new((\u0026mutex1, \u0026mutex2)).unwrap();\n\t\tlet guard = collection.lock(key);\n\t\tlet key = BoxedLockCollection::\u003c(\u0026Mutex\u003c_\u003e, \u0026Mutex\u003c_\u003e)\u003e::unlock(guard);\n\n\t\tassert!(mutex1.try_lock(key).is_ok())\n\t}\n\n\t#[test]\n\tfn read_unlock_collection_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock1 = RwLock::new(\"foo\");\n\t\tlet lock2 = RwLock::new(\"bar\");\n\t\tlet collection = BoxedLockCollection::try_new((\u0026lock1, \u0026lock2)).unwrap();\n\t\tlet guard = collection.read(key);\n\t\tlet key = BoxedLockCollection::\u003c(\u0026RwLock\u003c_\u003e, \u0026RwLock\u003c_\u003e)\u003e::unlock_read(guard);\n\n\t\tassert!(lock1.try_write(key).is_ok())\n\t}\n\n\t#[test]\n\tfn into_inner_works() {\n\t\tlet collection = BoxedLockCollection::new((Mutex::new(\"Hello\"), Mutex::new(47)));\n\t\tassert_eq!(collection.into_inner(), (\"Hello\", 47))\n\t}\n\n\t#[test]\n\tfn works_in_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex1 = RwLock::new(0);\n\t\tlet mutex2 = RwLock::new(1);\n\t\tlet collection =\n\t\t\tBoxedLockCollection::try_new(BoxedLockCollection::try_new([\u0026mutex1, \u0026mutex2]).unwrap())\n\t\t\t\t.unwrap();\n\n\t\tlet mut guard = collection.lock(key);\n\t\tassert!(mutex1.is_locked());\n\t\tassert!(mutex2.is_locked());\n\t\tassert_eq!(*guard[0], 0);\n\t\tassert_eq!(*guard[1], 1);\n\t\t*guard[0] = 2;\n\t\tlet key = BoxedLockCollection::\u003cBoxedLockCollection\u003c[\u0026RwLock\u003c_\u003e; 2]\u003e\u003e::unlock(guard);\n\n\t\tlet guard = collection.read(key);\n\t\tassert!(mutex1.is_locked());\n\t\tassert!(mutex2.is_locked());\n\t\tassert_eq!(*guard[0], 2);\n\t\tassert_eq!(*guard[1], 1);\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn as_ref_works() {\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet collection = BoxedLockCollection::new_ref(\u0026mutexes);\n\n\t\tassert!(std::ptr::addr_eq(\u0026mutexes, collection.as_ref()))\n\t}\n\n\t#[test]\n\tfn child() {\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet collection = BoxedLockCollection::new_ref(\u0026mutexes);\n\n\t\tassert!(std::ptr::addr_eq(\u0026mutexes, *collection.child()))\n\t}\n}\n","traces":[{"line":21,"address":[],"length":0,"stats":{"Line":24}},{"line":22,"address":[226021],"length":1,"stats":{"Line":23}},{"line":25,"address":[1544784,1544656,1544912,1544528,1545040],"length":1,"stats":{"Line":6}},{"line":26,"address":[1544669,1544797,1545053,1544541,1544925],"length":1,"stats":{"Line":6}},{"line":27,"address":[1544879,1545007,1544751,1545135,1544623],"length":1,"stats":{"Line":6}},{"line":30,"address":[],"length":0,"stats":{"Line":7}},{"line":31,"address":[],"length":0,"stats":{"Line":14}},{"line":32,"address":[],"length":0,"stats":{"Line":7}},{"line":36,"address":[1546096,1546128,1546288,1546192,1546064,1546256,1546160,1546224],"length":1,"stats":{"Line":9}},{"line":37,"address":[],"length":0,"stats":{"Line":9}},{"line":40,"address":[189216],"length":1,"stats":{"Line":4}},{"line":41,"address":[1546389,1546357,1546325],"length":1,"stats":{"Line":4}},{"line":44,"address":[1546416,1546752,1546528,1546640],"length":1,"stats":{"Line":4}},{"line":45,"address":[],"length":0,"stats":{"Line":8}},{"line":46,"address":[],"length":0,"stats":{"Line":4}},{"line":62,"address":[],"length":0,"stats":{"Line":1}},{"line":63,"address":[],"length":0,"stats":{"Line":1}},{"line":66,"address":[1546912],"length":1,"stats":{"Line":1}},{"line":67,"address":[],"length":0,"stats":{"Line":1}},{"line":70,"address":[],"length":0,"stats":{"Line":7}},{"line":71,"address":[],"length":0,"stats":{"Line":7}},{"line":86,"address":[],"length":0,"stats":{"Line":1}},{"line":87,"address":[],"length":0,"stats":{"Line":1}},{"line":90,"address":[1547456,1547360,1547392,1547488],"length":1,"stats":{"Line":4}},{"line":91,"address":[],"length":0,"stats":{"Line":4}},{"line":103,"address":[],"length":0,"stats":{"Line":2}},{"line":104,"address":[1547598,1547534],"length":1,"stats":{"Line":2}},{"line":115,"address":[],"length":0,"stats":{"Line":1}},{"line":116,"address":[],"length":0,"stats":{"Line":1}},{"line":127,"address":[],"length":0,"stats":{"Line":1}},{"line":128,"address":[1547717],"length":1,"stats":{"Line":1}},{"line":135,"address":[],"length":0,"stats":{"Line":1}},{"line":136,"address":[],"length":0,"stats":{"Line":1}},{"line":137,"address":[1547791],"length":1,"stats":{"Line":1}},{"line":162,"address":[],"length":0,"stats":{"Line":1}},{"line":163,"address":[1547829],"length":1,"stats":{"Line":1}},{"line":178,"address":[],"length":0,"stats":{"Line":1}},{"line":179,"address":[1547870],"length":1,"stats":{"Line":1}},{"line":184,"address":[],"length":0,"stats":{"Line":1}},{"line":185,"address":[],"length":0,"stats":{"Line":1}},{"line":209,"address":[],"length":0,"stats":{"Line":3}},{"line":212,"address":[],"length":0,"stats":{"Line":3}},{"line":214,"address":[],"length":0,"stats":{"Line":3}},{"line":216,"address":[],"length":0,"stats":{"Line":3}},{"line":218,"address":[],"length":0,"stats":{"Line":3}},{"line":244,"address":[181600],"length":1,"stats":{"Line":27}},{"line":246,"address":[189753],"length":1,"stats":{"Line":27}},{"line":256,"address":[202880],"length":1,"stats":{"Line":36}},{"line":257,"address":[202885],"length":1,"stats":{"Line":36}},{"line":276,"address":[],"length":0,"stats":{"Line":16}},{"line":278,"address":[],"length":0,"stats":{"Line":17}},{"line":297,"address":[1552176,1552208,1552240],"length":1,"stats":{"Line":4}},{"line":299,"address":[],"length":0,"stats":{"Line":4}},{"line":324,"address":[],"length":0,"stats":{"Line":37}},{"line":325,"address":[181052,180945],"length":1,"stats":{"Line":80}},{"line":326,"address":[181092],"length":1,"stats":{"Line":39}},{"line":328,"address":[150063],"length":1,"stats":{"Line":39}},{"line":329,"address":[202526],"length":1,"stats":{"Line":40}},{"line":332,"address":[162474,162464],"length":1,"stats":{"Line":105}},{"line":335,"address":[1562779,1558730,1558257,1553132,1560811,1560308,1565324,1556106,1556654,1554107,1553627,1562284,1559801,1564292,1555137,1555628,1552625,1561313,1565820,1557739,1564841,1554638,1561798,1563242,1557131,1559252,1563722],"length":1,"stats":{"Line":41}},{"line":336,"address":[150235],"length":1,"stats":{"Line":41}},{"line":358,"address":[172656,172902],"length":1,"stats":{"Line":15}},{"line":361,"address":[1567947,1566296,1566843,1566016,1566571,1567400,1567115,1567680],"length":1,"stats":{"Line":15}},{"line":362,"address":[],"length":0,"stats":{"Line":30}},{"line":363,"address":[190050],"length":1,"stats":{"Line":1}},{"line":365,"address":[1567212,1566396,1568044,1566113,1567500,1566668,1567777,1566940],"length":1,"stats":{"Line":14}},{"line":369,"address":[],"length":0,"stats":{"Line":9}},{"line":370,"address":[],"length":0,"stats":{"Line":9}},{"line":373,"address":[1568512,1568544,1568480],"length":1,"stats":{"Line":3}},{"line":378,"address":[],"length":0,"stats":{"Line":3}},{"line":401,"address":[226690,226544],"length":1,"stats":{"Line":23}},{"line":404,"address":[1569454,1570032,1570832,1570430,1569600,1569886,1568702,1570672,1569758,1568590,1569136,1570542,1570302,1568814,1569296,1570174,1568960],"length":1,"stats":{"Line":23}},{"line":408,"address":[172460],"length":1,"stats":{"Line":19}},{"line":443,"address":[],"length":0,"stats":{"Line":6}},{"line":445,"address":[1571568,1571801,1571246,1571038,1571390,1570992,1571200,1571614,1571433,1571758],"length":1,"stats":{"Line":11}},{"line":446,"address":[1571257,1571049,1571439,1571807,1571625],"length":1,"stats":{"Line":4}},{"line":450,"address":[203305,203275],"length":1,"stats":{"Line":4}},{"line":453,"address":[1571872,1571105,1571313,1571681,1571504],"length":1,"stats":{"Line":2}},{"line":473,"address":[],"length":0,"stats":{"Line":13}},{"line":474,"address":[],"length":0,"stats":{"Line":13}},{"line":475,"address":[],"length":0,"stats":{"Line":0}},{"line":480,"address":[],"length":0,"stats":{"Line":4}},{"line":481,"address":[],"length":0,"stats":{"Line":4}},{"line":484,"address":[1573072,1573040],"length":1,"stats":{"Line":2}},{"line":489,"address":[],"length":0,"stats":{"Line":2}},{"line":512,"address":[181570,181424],"length":1,"stats":{"Line":8}},{"line":515,"address":[],"length":0,"stats":{"Line":8}},{"line":519,"address":[1573704,1573528,1573974,1573270,1573382,1573846,1573158],"length":1,"stats":{"Line":7}},{"line":555,"address":[190112,190319],"length":1,"stats":{"Line":4}},{"line":558,"address":[190194,190144],"length":1,"stats":{"Line":8}},{"line":559,"address":[],"length":0,"stats":{"Line":3}},{"line":563,"address":[1574359,1574543,1574181,1574389,1574570,1574151],"length":1,"stats":{"Line":2}},{"line":566,"address":[190261],"length":1,"stats":{"Line":1}},{"line":584,"address":[],"length":0,"stats":{"Line":1}},{"line":585,"address":[1574638],"length":1,"stats":{"Line":1}},{"line":586,"address":[],"length":0,"stats":{"Line":0}},{"line":602,"address":[],"length":0,"stats":{"Line":2}},{"line":603,"address":[],"length":0,"stats":{"Line":2}},{"line":629,"address":[],"length":0,"stats":{"Line":1}},{"line":630,"address":[],"length":0,"stats":{"Line":1}}],"covered":98,"coverable":100},{"path":["/","home","botahamec","Projects","happylock","src","collection","guard.rs"],"content":"use std::fmt::{Debug, Display};\nuse std::hash::Hash;\nuse std::ops::{Deref, DerefMut};\n\nuse super::LockGuard;\n\n#[mutants::skip] // hashing involves RNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard: Hash\u003e Hash for LockGuard\u003cGuard\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.guard.hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard: Debug\u003e Debug for LockGuard\u003cGuard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cGuard: Display\u003e Display for LockGuard\u003cGuard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cGuard\u003e Deref for LockGuard\u003cGuard\u003e {\n\ttype Target = Guard;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t\u0026self.guard\n\t}\n}\n\nimpl\u003cGuard\u003e DerefMut for LockGuard\u003cGuard\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t\u0026mut self.guard\n\t}\n}\n\nimpl\u003cGuard\u003e AsRef\u003cGuard\u003e for LockGuard\u003cGuard\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026Guard {\n\t\t\u0026self.guard\n\t}\n}\n\nimpl\u003cGuard\u003e AsMut\u003cGuard\u003e for LockGuard\u003cGuard\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut Guard {\n\t\t\u0026mut self.guard\n\t}\n}\n\n#[cfg(test)]\nmod tests {\n\tuse crate::collection::OwnedLockCollection;\n\tuse crate::{LockCollection, Mutex, RwLock, ThreadKey};\n\n\t#[test]\n\tfn guard_display_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = OwnedLockCollection::new(RwLock::new(\"Hello, world!\"));\n\t\tlet guard = lock.read(key);\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn deref_mut_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = (Mutex::new(1), Mutex::new(2));\n\t\tlet lock = LockCollection::new_ref(\u0026locks);\n\t\tlet mut guard = lock.lock(key);\n\t\t*guard.0 = 3;\n\t\tlet key = LockCollection::\u003c(Mutex\u003c_\u003e, Mutex\u003c_\u003e)\u003e::unlock(guard);\n\n\t\tlet guard = locks.0.lock(key);\n\t\tassert_eq!(*guard, 3);\n\t\tlet key = Mutex::unlock(guard);\n\n\t\tlet guard = locks.1.lock(key);\n\t\tassert_eq!(*guard, 2);\n\t}\n\n\t#[test]\n\tfn as_ref_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = (Mutex::new(1), Mutex::new(2));\n\t\tlet lock = LockCollection::new_ref(\u0026locks);\n\t\tlet mut guard = lock.lock(key);\n\t\t*guard.0 = 3;\n\t\tlet key = LockCollection::\u003c(Mutex\u003c_\u003e, Mutex\u003c_\u003e)\u003e::unlock(guard);\n\n\t\tlet guard = locks.0.lock(key);\n\t\tassert_eq!(guard.as_ref(), \u00263);\n\t\tlet key = Mutex::unlock(guard);\n\n\t\tlet guard = locks.1.lock(key);\n\t\tassert_eq!(guard.as_ref(), \u00262);\n\t}\n\n\t#[test]\n\tfn as_mut_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = (Mutex::new(1), Mutex::new(2));\n\t\tlet lock = LockCollection::new_ref(\u0026locks);\n\t\tlet mut guard = lock.lock(key);\n\t\tlet guard_mut = guard.as_mut();\n\t\t*guard_mut.0 = 3;\n\t\tlet key = LockCollection::\u003c(Mutex\u003c_\u003e, Mutex\u003c_\u003e)\u003e::unlock(guard);\n\n\t\tlet guard = locks.0.lock(key);\n\t\tassert_eq!(guard.as_ref(), \u00263);\n\t\tlet key = Mutex::unlock(guard);\n\n\t\tlet guard = locks.1.lock(key);\n\t\tassert_eq!(guard.as_ref(), \u00262);\n\t}\n}\n","traces":[{"line":24,"address":[],"length":0,"stats":{"Line":1}},{"line":25,"address":[],"length":0,"stats":{"Line":1}},{"line":32,"address":[],"length":0,"stats":{"Line":18}},{"line":33,"address":[],"length":0,"stats":{"Line":0}},{"line":38,"address":[],"length":0,"stats":{"Line":8}},{"line":39,"address":[],"length":0,"stats":{"Line":0}},{"line":44,"address":[],"length":0,"stats":{"Line":2}},{"line":45,"address":[],"length":0,"stats":{"Line":0}},{"line":50,"address":[],"length":0,"stats":{"Line":4}},{"line":51,"address":[],"length":0,"stats":{"Line":0}}],"covered":6,"coverable":10},{"path":["/","home","botahamec","Projects","happylock","src","collection","owned.rs"],"content":"use crate::lockable::{\n\tLockable, LockableGetMut, LockableIntoInner, OwnedLockable, RawLock, Sharable,\n};\nuse crate::{Keyable, ThreadKey};\n\nuse super::utils::{scoped_read, scoped_try_read, scoped_try_write, scoped_write};\nuse super::{utils, LockGuard, OwnedLockCollection};\n\nunsafe impl\u003cL: Lockable\u003e RawLock for OwnedLockCollection\u003cL\u003e {\n\t#[mutants::skip] // this should never run\n\t#[cfg(not(tarpaulin_include))]\n\tfn poison(\u0026self) {\n\t\tlet locks = utils::get_locks_unsorted(\u0026self.data);\n\t\tfor lock in locks {\n\t\t\tlock.poison();\n\t\t}\n\t}\n\n\tunsafe fn raw_write(\u0026self) {\n\t\tutils::ordered_write(\u0026utils::get_locks_unsorted(\u0026self.data))\n\t}\n\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool {\n\t\tlet locks = utils::get_locks_unsorted(\u0026self.data);\n\t\tutils::ordered_try_write(\u0026locks)\n\t}\n\n\tunsafe fn raw_unlock_write(\u0026self) {\n\t\tlet locks = utils::get_locks_unsorted(\u0026self.data);\n\t\tfor lock in locks {\n\t\t\tlock.raw_unlock_write();\n\t\t}\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tutils::ordered_read(\u0026utils::get_locks_unsorted(\u0026self.data))\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tlet locks = utils::get_locks_unsorted(\u0026self.data);\n\t\tutils::ordered_try_read(\u0026locks)\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tlet locks = utils::get_locks_unsorted(\u0026self.data);\n\t\tfor lock in locks {\n\t\t\tlock.raw_unlock_read();\n\t\t}\n\t}\n}\n\nunsafe impl\u003cL: Lockable\u003e Lockable for OwnedLockCollection\u003cL\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= L::Guard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= L::DataMut\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\t#[mutants::skip] // It's hard to test lkocks in an OwnedLockCollection, because they're owned\n\t#[cfg(not(tarpaulin_include))]\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tptrs.push(self)\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tself.data.guard()\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.data.data_mut()\n\t}\n}\n\nimpl\u003cL: LockableGetMut\u003e LockableGetMut for OwnedLockCollection\u003cL\u003e {\n\ttype Inner\u003c'a\u003e\n\t\t= L::Inner\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tself.data.get_mut()\n\t}\n}\n\nimpl\u003cL: LockableIntoInner\u003e LockableIntoInner for OwnedLockCollection\u003cL\u003e {\n\ttype Inner = L::Inner;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tself.data.into_inner()\n\t}\n}\n\nunsafe impl\u003cL: Sharable\u003e Sharable for OwnedLockCollection\u003cL\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= L::ReadGuard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= L::DataRef\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tself.data.read_guard()\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tself.data.data_ref()\n\t}\n}\n\nunsafe impl\u003cL: OwnedLockable\u003e OwnedLockable for OwnedLockCollection\u003cL\u003e {}\n\nimpl\u003cL\u003e IntoIterator for OwnedLockCollection\u003cL\u003e\nwhere\n\tL: IntoIterator,\n{\n\ttype Item = \u003cL as IntoIterator\u003e::Item;\n\ttype IntoIter = \u003cL as IntoIterator\u003e::IntoIter;\n\n\tfn into_iter(self) -\u003e Self::IntoIter {\n\t\tself.data.into_iter()\n\t}\n}\n\nimpl\u003cL: OwnedLockable, I: FromIterator\u003cL\u003e + OwnedLockable\u003e FromIterator\u003cL\u003e\n\tfor OwnedLockCollection\u003cI\u003e\n{\n\tfn from_iter\u003cT: IntoIterator\u003cItem = L\u003e\u003e(iter: T) -\u003e Self {\n\t\tlet iter: I = iter.into_iter().collect();\n\t\tSelf::new(iter)\n\t}\n}\n\nimpl\u003cE: OwnedLockable + Extend\u003cL\u003e, L: OwnedLockable\u003e Extend\u003cL\u003e for OwnedLockCollection\u003cE\u003e {\n\tfn extend\u003cT: IntoIterator\u003cItem = L\u003e\u003e(\u0026mut self, iter: T) {\n\t\tself.data.extend(iter)\n\t}\n}\n\n// AsRef can't be implemented because an impl of AsRef\u003cL\u003e for L could break the\n// invariant that there is only one way to lock the collection. AsMut is fine,\n// because the collection can't be locked as long as the reference is valid.\n\nimpl\u003cT: ?Sized, L: AsMut\u003cT\u003e\u003e AsMut\u003cT\u003e for OwnedLockCollection\u003cL\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself.data.as_mut()\n\t}\n}\n\nimpl\u003cL: OwnedLockable + Default\u003e Default for OwnedLockCollection\u003cL\u003e {\n\tfn default() -\u003e Self {\n\t\tSelf::new(L::default())\n\t}\n}\n\nimpl\u003cL: OwnedLockable\u003e From\u003cL\u003e for OwnedLockCollection\u003cL\u003e {\n\tfn from(value: L) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\nimpl\u003cL: OwnedLockable\u003e OwnedLockCollection\u003cL\u003e {\n\t/// Creates a new collection of owned locks.\n\t///\n\t/// Because the locks are owned, there's no need to do any checks for\n\t/// duplicate values. The locks also don't need to be sorted by memory\n\t/// address because they aren't used anywhere else.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t/// ```\n\t#[must_use]\n\tpub const fn new(data: L) -\u003e Self {\n\t\tSelf { data }\n\t}\n\n\tpub fn scoped_lock\u003c'a, R\u003e(\u0026'a self, key: impl Keyable, f: impl Fn(L::DataMut\u003c'a\u003e) -\u003e R) -\u003e R {\n\t\tscoped_write(self, key, f)\n\t}\n\n\tpub fn scoped_try_lock\u003c'a, Key: Keyable, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(L::DataMut\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tscoped_try_write(self, key, f)\n\t}\n\n\t/// Locks the collection\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data. When the guard is dropped, the locks in the collection are also\n\t/// dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// ```\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::Guard\u003c'_\u003e\u003e {\n\t\tlet guard = unsafe {\n\t\t\t// safety: we have the thread key, and these locks happen in a\n\t\t\t// predetermined order\n\t\t\tself.raw_write();\n\n\t\t\t// safety: we've locked all of this already\n\t\t\tself.data.guard()\n\t\t};\n\n\t\tLockGuard { guard, key }\n\t}\n\n\t/// Attempts to lock the without blocking.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// locks when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If any of the locks in this collection are already locked, this returns\n\t/// an error containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// match lock.try_lock(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::Guard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tlet guard = unsafe {\n\t\t\tif !self.raw_try_write() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we've acquired the locks\n\t\t\tself.data.guard()\n\t\t};\n\n\t\tOk(LockGuard { guard, key })\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// let key = OwnedLockCollection::\u003c(Mutex\u003ci32\u003e, Mutex\u003c\u0026str\u003e)\u003e::unlock(guard);\n\t/// ```\n\t#[allow(clippy::missing_const_for_fn)]\n\tpub fn unlock(guard: LockGuard\u003cL::Guard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: Sharable\u003e OwnedLockCollection\u003cL\u003e {\n\tpub fn scoped_read\u003c'a, R\u003e(\u0026'a self, key: impl Keyable, f: impl Fn(L::DataRef\u003c'a\u003e) -\u003e R) -\u003e R {\n\t\tscoped_read(self, key, f)\n\t}\n\n\tpub fn scoped_try_read\u003c'a, Key: Keyable, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(L::DataRef\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tscoped_try_read(self, key, f)\n\t}\n\n\t/// Locks the collection, so that other threads can still read from it\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data immutably. When the guard is dropped, the locks in the collection\n\t/// are also dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// assert_eq!(*guard.0, 0);\n\t/// assert_eq!(*guard.1, \"\");\n\t/// ```\n\tpub fn read(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_read();\n\n\t\t\tLockGuard {\n\t\t\t\t// safety: we've already acquired the lock\n\t\t\t\tguard: self.data.read_guard(),\n\t\t\t\tkey,\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Attempts to lock the without blocking, in such a way that other threads\n\t/// can still read from the collection.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// shared access when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If any of the locks in this collection can't be acquired, then an error\n\t/// is returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(5), RwLock::new(\"6\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// match lock.try_read(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// assert_eq!(*guard.0, 5);\n\t/// assert_eq!(*guard.1, \"6\");\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_read(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tlet guard = unsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_read() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we've acquired the locks\n\t\t\tself.data.read_guard()\n\t\t};\n\n\t\tOk(LockGuard { guard, key })\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// let key = OwnedLockCollection::\u003c(RwLock\u003ci32\u003e, RwLock\u003c\u0026str\u003e)\u003e::unlock_read(guard);\n\t/// ```\n\t#[allow(clippy::missing_const_for_fn)]\n\tpub fn unlock_read(guard: LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL\u003e OwnedLockCollection\u003cL\u003e {\n\t/// Gets the underlying collection, consuming this collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let data = (Mutex::new(42), Mutex::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let inner = lock.into_child();\n\t/// let guard = inner.0.lock(key);\n\t/// assert_eq!(*guard, 42);\n\t/// ```\n\t#[must_use]\n\tpub fn into_child(self) -\u003e L {\n\t\tself.data\n\t}\n\n\t/// Gets a mutable reference to the underlying collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let data = (Mutex::new(42), Mutex::new(\"\"));\n\t/// let mut lock = OwnedLockCollection::new(data);\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut inner = lock.child_mut();\n\t/// let guard = inner.0.get_mut();\n\t/// assert_eq!(*guard, 42);\n\t/// ```\n\t#[must_use]\n\tpub fn child_mut(\u0026mut self) -\u003e \u0026mut L {\n\t\t\u0026mut self.data\n\t}\n}\n\nimpl\u003cL: LockableGetMut\u003e OwnedLockCollection\u003cL\u003e {\n\t/// Gets a mutable reference to the data behind this `OwnedLockCollection`.\n\t///\n\t/// Since this call borrows the `OwnedLockCollection` mutably, no actual\n\t/// locking needs to take place - the mutable borrow statically guarantees\n\t/// no locks exist.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let mut mutex = OwnedLockCollection::new([Mutex::new(0), Mutex::new(0)]);\n\t/// assert_eq!(mutex.get_mut(), [\u0026mut 0, \u0026mut 0]);\n\t/// ```\n\tpub fn get_mut(\u0026mut self) -\u003e L::Inner\u003c'_\u003e {\n\t\tLockableGetMut::get_mut(self)\n\t}\n}\n\nimpl\u003cL: LockableIntoInner\u003e OwnedLockCollection\u003cL\u003e {\n\t/// Consumes this `OwnedLockCollection`, returning the underlying data.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let mutex = OwnedLockCollection::new([Mutex::new(0), Mutex::new(0)]);\n\t/// assert_eq!(mutex.into_inner(), [0, 0]);\n\t/// ```\n\t#[must_use]\n\tpub fn into_inner(self) -\u003e L::Inner {\n\t\tLockableIntoInner::into_inner(self)\n\t}\n}\n\n#[cfg(test)]\nmod tests {\n\tuse super::*;\n\tuse crate::{Mutex, RwLock, ThreadKey};\n\n\t#[test]\n\tfn get_mut_applies_changes() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mut collection = OwnedLockCollection::new([Mutex::new(\"foo\"), Mutex::new(\"bar\")]);\n\t\tassert_eq!(*collection.get_mut()[0], \"foo\");\n\t\tassert_eq!(*collection.get_mut()[1], \"bar\");\n\t\t*collection.get_mut()[0] = \"baz\";\n\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], \"baz\");\n\t\tassert_eq!(*guard[1], \"bar\");\n\t}\n\n\t#[test]\n\tfn into_inner_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::from([Mutex::new(\"foo\")]);\n\t\tlet mut guard = collection.lock(key);\n\t\t*guard[0] = \"bar\";\n\t\tdrop(guard);\n\n\t\tlet array = collection.into_inner();\n\t\tassert_eq!(array.len(), 1);\n\t\tassert_eq!(array[0], \"bar\");\n\t}\n\n\t#[test]\n\tfn from_into_iter_is_correct() {\n\t\tlet array = [Mutex::new(0), Mutex::new(1), Mutex::new(2), Mutex::new(3)];\n\t\tlet mut collection: OwnedLockCollection\u003cVec\u003cMutex\u003cusize\u003e\u003e\u003e = array.into_iter().collect();\n\t\tassert_eq!(collection.get_mut().len(), 4);\n\t\tfor (i, lock) in collection.into_iter().enumerate() {\n\t\t\tassert_eq!(lock.into_inner(), i);\n\t\t}\n\t}\n\n\t#[test]\n\tfn from_iter_is_correct() {\n\t\tlet array = [Mutex::new(0), Mutex::new(1), Mutex::new(2), Mutex::new(3)];\n\t\tlet mut collection: OwnedLockCollection\u003cVec\u003cMutex\u003cusize\u003e\u003e\u003e = array.into_iter().collect();\n\t\tlet collection: \u0026mut Vec\u003c_\u003e = collection.as_mut();\n\t\tassert_eq!(collection.len(), 4);\n\t\tfor (i, lock) in collection.iter_mut().enumerate() {\n\t\t\tassert_eq!(*lock.get_mut(), i);\n\t\t}\n\t}\n\n\t#[test]\n\tfn scoped_read_works() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new([RwLock::new(24), RwLock::new(42)]);\n\t\tlet sum = collection.scoped_read(\u0026mut key, |guard| guard[0] + guard[1]);\n\t\tassert_eq!(sum, 24 + 42);\n\t}\n\n\t#[test]\n\tfn scoped_lock_works() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new([RwLock::new(24), RwLock::new(42)]);\n\t\tcollection.scoped_lock(\u0026mut key, |guard| *guard[0] += *guard[1]);\n\n\t\tlet sum = collection.scoped_lock(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 24 + 42);\n\t\t\tassert_eq!(*guard[1], 42);\n\t\t\t*guard[0] + *guard[1]\n\t\t});\n\n\t\tassert_eq!(sum, 24 + 42 + 42);\n\t}\n\n\t#[test]\n\tfn scoped_try_lock_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new([Mutex::new(1), Mutex::new(2)]);\n\t\tlet guard = collection.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = collection.scoped_try_lock(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn scoped_try_read_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new([RwLock::new(1), RwLock::new(2)]);\n\t\tlet guard = collection.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = collection.scoped_try_read(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn try_lock_works_on_unlocked() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new((Mutex::new(0), Mutex::new(1)));\n\t\tlet guard = collection.try_lock(key).unwrap();\n\t\tassert_eq!(*guard.0, 0);\n\t\tassert_eq!(*guard.1, 1);\n\t}\n\n\t#[test]\n\tfn try_lock_fails_on_locked() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new((Mutex::new(0), Mutex::new(1)));\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\t#[allow(unused)]\n\t\t\t\tlet guard = collection.lock(key);\n\t\t\t\tstd::mem::forget(guard);\n\t\t\t});\n\t\t});\n\n\t\tassert!(collection.try_lock(key).is_err());\n\t}\n\n\t#[test]\n\tfn try_read_succeeds_for_unlocked_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = OwnedLockCollection::new(mutexes);\n\t\tlet guard = collection.try_read(key).unwrap();\n\t\tassert_eq!(*guard[0], 24);\n\t\tassert_eq!(*guard[1], 42);\n\t}\n\n\t#[test]\n\tfn try_read_fails_on_locked() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new((RwLock::new(0), RwLock::new(1)));\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\t#[allow(unused)]\n\t\t\t\tlet guard = collection.lock(key);\n\t\t\t\tstd::mem::forget(guard);\n\t\t\t});\n\t\t});\n\n\t\tassert!(collection.try_read(key).is_err());\n\t}\n\n\t#[test]\n\tfn can_read_twice_on_different_threads() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = OwnedLockCollection::new(mutexes);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.read(key);\n\t\t\t\tassert_eq!(*guard[0], 24);\n\t\t\t\tassert_eq!(*guard[1], 42);\n\t\t\t\tstd::mem::forget(guard);\n\t\t\t});\n\t\t});\n\n\t\tlet guard = collection.try_read(key).unwrap();\n\t\tassert_eq!(*guard[0], 24);\n\t\tassert_eq!(*guard[1], 42);\n\t}\n\n\t#[test]\n\tfn unlock_collection_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new((Mutex::new(\"foo\"), Mutex::new(\"bar\")));\n\t\tlet guard = collection.lock(key);\n\n\t\tlet key = OwnedLockCollection::\u003c(Mutex\u003c_\u003e, Mutex\u003c_\u003e)\u003e::unlock(guard);\n\t\tassert!(collection.try_lock(key).is_ok())\n\t}\n\n\t#[test]\n\tfn read_unlock_collection_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new((RwLock::new(\"foo\"), RwLock::new(\"bar\")));\n\t\tlet guard = collection.read(key);\n\n\t\tlet key = OwnedLockCollection::\u003c(\u0026RwLock\u003c_\u003e, \u0026RwLock\u003c_\u003e)\u003e::unlock_read(guard);\n\t\tassert!(collection.try_lock(key).is_ok())\n\t}\n\n\t#[test]\n\tfn default_works() {\n\t\ttype MyCollection = OwnedLockCollection\u003c(Mutex\u003ci32\u003e, Mutex\u003cOption\u003ci32\u003e\u003e, Mutex\u003cString\u003e)\u003e;\n\t\tlet collection = MyCollection::default();\n\t\tlet inner = collection.into_inner();\n\t\tassert_eq!(inner.0, 0);\n\t\tassert_eq!(inner.1, None);\n\t\tassert_eq!(inner.2, String::new());\n\t}\n\n\t#[test]\n\tfn can_be_extended() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(1);\n\t\tlet mut collection = OwnedLockCollection::new(vec![mutex1, mutex2]);\n\n\t\tcollection.extend([Mutex::new(2)]);\n\n\t\tassert_eq!(collection.data.len(), 3);\n\t}\n\n\t#[test]\n\tfn works_in_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tOwnedLockCollection::new(OwnedLockCollection::new([RwLock::new(0), RwLock::new(1)]));\n\n\t\tlet mut guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], 0);\n\t\tassert_eq!(*guard[1], 1);\n\t\t*guard[1] = 2;\n\n\t\tlet key = OwnedLockCollection::\u003cOwnedLockCollection\u003c[RwLock\u003c_\u003e; 2]\u003e\u003e::unlock(guard);\n\t\tlet guard = collection.read(key);\n\t\tassert_eq!(*guard[0], 0);\n\t\tassert_eq!(*guard[1], 2);\n\t}\n\n\t#[test]\n\tfn as_mut_works() {\n\t\tlet mut mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet mut collection = OwnedLockCollection::new(\u0026mut mutexes);\n\n\t\tcollection.as_mut()[0] = Mutex::new(42);\n\n\t\tassert_eq!(*collection.as_mut()[0].get_mut(), 42);\n\t}\n\n\t#[test]\n\tfn child_mut_works() {\n\t\tlet mut mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet mut collection = OwnedLockCollection::new(\u0026mut mutexes);\n\n\t\tcollection.child_mut()[0] = Mutex::new(42);\n\n\t\tassert_eq!(*collection.child_mut()[0].get_mut(), 42);\n\t}\n\n\t#[test]\n\tfn into_child_works() {\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet mut collection = OwnedLockCollection::new(mutexes);\n\n\t\tcollection.child_mut()[0] = Mutex::new(42);\n\n\t\tassert_eq!(\n\t\t\t*collection\n\t\t\t\t.into_child()\n\t\t\t\t.as_mut()\n\t\t\t\t.get_mut(0)\n\t\t\t\t.unwrap()\n\t\t\t\t.get_mut(),\n\t\t\t42\n\t\t);\n\t}\n}\n","traces":[{"line":19,"address":[1576096,1575584,1575328,1576203,1575819,1575968,1575435,1575456,1576331,1575712,1575691,1575840,1575563,1576224,1576075,1575947],"length":1,"stats":{"Line":8}},{"line":20,"address":[],"length":0,"stats":{"Line":16}},{"line":23,"address":[],"length":0,"stats":{"Line":4}},{"line":24,"address":[],"length":0,"stats":{"Line":4}},{"line":25,"address":[],"length":0,"stats":{"Line":8}},{"line":28,"address":[1576928,1577173,1577445,1577200],"length":1,"stats":{"Line":1}},{"line":29,"address":[],"length":0,"stats":{"Line":1}},{"line":30,"address":[],"length":0,"stats":{"Line":3}},{"line":31,"address":[],"length":0,"stats":{"Line":1}},{"line":35,"address":[],"length":0,"stats":{"Line":4}},{"line":36,"address":[],"length":0,"stats":{"Line":8}},{"line":39,"address":[],"length":0,"stats":{"Line":2}},{"line":40,"address":[1578006,1578150],"length":1,"stats":{"Line":2}},{"line":41,"address":[1578016,1578160,1578071,1578215],"length":1,"stats":{"Line":4}},{"line":44,"address":[],"length":0,"stats":{"Line":1}},{"line":45,"address":[],"length":0,"stats":{"Line":1}},{"line":46,"address":[1578424,1578300,1578478],"length":1,"stats":{"Line":3}},{"line":47,"address":[1578504],"length":1,"stats":{"Line":1}},{"line":69,"address":[],"length":0,"stats":{"Line":1}},{"line":70,"address":[],"length":0,"stats":{"Line":1}},{"line":73,"address":[],"length":0,"stats":{"Line":1}},{"line":74,"address":[],"length":0,"stats":{"Line":1}},{"line":84,"address":[1578656,1578640],"length":1,"stats":{"Line":2}},{"line":85,"address":[1578673,1578645],"length":1,"stats":{"Line":2}},{"line":92,"address":[],"length":0,"stats":{"Line":2}},{"line":93,"address":[],"length":0,"stats":{"Line":2}},{"line":108,"address":[],"length":0,"stats":{"Line":1}},{"line":109,"address":[1578833],"length":1,"stats":{"Line":1}},{"line":112,"address":[1578848],"length":1,"stats":{"Line":1}},{"line":113,"address":[1578865],"length":1,"stats":{"Line":1}},{"line":126,"address":[],"length":0,"stats":{"Line":1}},{"line":127,"address":[1578892],"length":1,"stats":{"Line":1}},{"line":134,"address":[1578944],"length":1,"stats":{"Line":1}},{"line":135,"address":[],"length":0,"stats":{"Line":1}},{"line":136,"address":[],"length":0,"stats":{"Line":1}},{"line":141,"address":[],"length":0,"stats":{"Line":1}},{"line":142,"address":[],"length":0,"stats":{"Line":1}},{"line":151,"address":[],"length":0,"stats":{"Line":2}},{"line":152,"address":[],"length":0,"stats":{"Line":2}},{"line":157,"address":[],"length":0,"stats":{"Line":1}},{"line":158,"address":[],"length":0,"stats":{"Line":1}},{"line":163,"address":[],"length":0,"stats":{"Line":1}},{"line":164,"address":[],"length":0,"stats":{"Line":1}},{"line":185,"address":[1579392,1579248,1579648,1579504,1579296,1579216,1579600,1579184,1579568,1579328,1579424,1579536,1579472,1579376],"length":1,"stats":{"Line":15}},{"line":189,"address":[],"length":0,"stats":{"Line":2}},{"line":190,"address":[],"length":0,"stats":{"Line":2}},{"line":193,"address":[1579744],"length":1,"stats":{"Line":1}},{"line":198,"address":[],"length":0,"stats":{"Line":1}},{"line":221,"address":[1579776,1580000,1580836,1580304,1579872,1580032,1580720,1580276,1580448,1580160,1580128,1580420,1580698,1580564,1580592,1579904],"length":1,"stats":{"Line":8}},{"line":225,"address":[1580336,1580046,1580752,1580606,1579790,1579918,1580480,1580192],"length":1,"stats":{"Line":8}},{"line":228,"address":[1580086,1579830,1580381,1580525,1579958,1580797,1580646,1580237],"length":1,"stats":{"Line":8}},{"line":264,"address":[1580864,1581040,1581359,1581183,1581216,1581007],"length":1,"stats":{"Line":3}},{"line":266,"address":[1581230,1581097,1580878,1580921,1581273,1581054],"length":1,"stats":{"Line":6}},{"line":267,"address":[],"length":0,"stats":{"Line":1}},{"line":271,"address":[1581337,1581161,1581119,1581295,1580985,1580943],"length":1,"stats":{"Line":6}},{"line":274,"address":[],"length":0,"stats":{"Line":3}},{"line":296,"address":[1581458,1581392,1581488,1581552],"length":1,"stats":{"Line":2}},{"line":297,"address":[],"length":0,"stats":{"Line":2}},{"line":298,"address":[],"length":0,"stats":{"Line":0}},{"line":303,"address":[],"length":0,"stats":{"Line":1}},{"line":304,"address":[1581597],"length":1,"stats":{"Line":1}},{"line":307,"address":[1581616],"length":1,"stats":{"Line":1}},{"line":312,"address":[],"length":0,"stats":{"Line":1}},{"line":335,"address":[],"length":0,"stats":{"Line":4}},{"line":338,"address":[],"length":0,"stats":{"Line":4}},{"line":342,"address":[],"length":0,"stats":{"Line":4}},{"line":379,"address":[],"length":0,"stats":{"Line":2}},{"line":382,"address":[],"length":0,"stats":{"Line":4}},{"line":383,"address":[],"length":0,"stats":{"Line":1}},{"line":387,"address":[],"length":0,"stats":{"Line":1}},{"line":390,"address":[],"length":0,"stats":{"Line":1}},{"line":410,"address":[],"length":0,"stats":{"Line":1}},{"line":411,"address":[],"length":0,"stats":{"Line":1}},{"line":412,"address":[],"length":0,"stats":{"Line":0}},{"line":434,"address":[],"length":0,"stats":{"Line":1}},{"line":435,"address":[],"length":0,"stats":{"Line":1}},{"line":455,"address":[],"length":0,"stats":{"Line":2}},{"line":456,"address":[],"length":0,"stats":{"Line":0}},{"line":476,"address":[],"length":0,"stats":{"Line":2}},{"line":477,"address":[],"length":0,"stats":{"Line":2}},{"line":494,"address":[],"length":0,"stats":{"Line":2}},{"line":495,"address":[],"length":0,"stats":{"Line":2}}],"covered":79,"coverable":82},{"path":["/","home","botahamec","Projects","happylock","src","collection","ref.rs"],"content":"use std::fmt::Debug;\n\nuse crate::lockable::{Lockable, OwnedLockable, RawLock, Sharable};\nuse crate::{Keyable, ThreadKey};\n\nuse super::utils::{\n\tget_locks, ordered_contains_duplicates, scoped_read, scoped_try_read, scoped_try_write,\n\tscoped_write,\n};\nuse super::{utils, LockGuard, RefLockCollection};\n\nimpl\u003c'a, L\u003e IntoIterator for \u0026'a RefLockCollection\u003c'a, L\u003e\nwhere\n\t\u0026'a L: IntoIterator,\n{\n\ttype Item = \u003c\u0026'a L as IntoIterator\u003e::Item;\n\ttype IntoIter = \u003c\u0026'a L as IntoIterator\u003e::IntoIter;\n\n\tfn into_iter(self) -\u003e Self::IntoIter {\n\t\tself.data.into_iter()\n\t}\n}\n\nunsafe impl\u003cL: Lockable\u003e RawLock for RefLockCollection\u003c'_, L\u003e {\n\t#[mutants::skip] // this should never run\n\t#[cfg(not(tarpaulin_include))]\n\tfn poison(\u0026self) {\n\t\tfor lock in \u0026self.locks {\n\t\t\tlock.poison();\n\t\t}\n\t}\n\n\tunsafe fn raw_write(\u0026self) {\n\t\tutils::ordered_write(\u0026self.locks)\n\t}\n\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool {\n\t\tutils::ordered_try_write(\u0026self.locks)\n\t}\n\n\tunsafe fn raw_unlock_write(\u0026self) {\n\t\tfor lock in \u0026self.locks {\n\t\t\tlock.raw_unlock_write();\n\t\t}\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tutils::ordered_read(\u0026self.locks)\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tutils::ordered_try_read(\u0026self.locks)\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tfor lock in \u0026self.locks {\n\t\t\tlock.raw_unlock_read();\n\t\t}\n\t}\n}\n\nunsafe impl\u003cL: Lockable\u003e Lockable for RefLockCollection\u003c'_, L\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= L::Guard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= L::DataMut\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tptrs.push(self)\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tself.data.guard()\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.data.data_mut()\n\t}\n}\n\nunsafe impl\u003cL: Sharable\u003e Sharable for RefLockCollection\u003c'_, L\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= L::ReadGuard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= L::DataRef\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tself.data.read_guard()\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tself.data.data_ref()\n\t}\n}\n\nimpl\u003cT: ?Sized, L: AsRef\u003cT\u003e\u003e AsRef\u003cT\u003e for RefLockCollection\u003c'_, L\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself.data.as_ref()\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cL: Debug\u003e Debug for RefLockCollection\u003c'_, L\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tf.debug_struct(stringify!(RefLockCollection))\n\t\t\t.field(\"data\", self.data)\n\t\t\t.finish_non_exhaustive()\n\t}\n}\n\n// safety: the RawLocks must be send because they come from the Send Lockable\n#[allow(clippy::non_send_fields_in_send_ty)]\nunsafe impl\u003cL: Send\u003e Send for RefLockCollection\u003c'_, L\u003e {}\nunsafe impl\u003cL: Sync\u003e Sync for RefLockCollection\u003c'_, L\u003e {}\n\nimpl\u003c'a, L: OwnedLockable + Default\u003e From\u003c\u0026'a L\u003e for RefLockCollection\u003c'a, L\u003e {\n\tfn from(value: \u0026'a L) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\nimpl\u003c'a, L: OwnedLockable\u003e RefLockCollection\u003c'a, L\u003e {\n\t/// Creates a new collection of owned locks.\n\t///\n\t/// Because the locks are owned, there's no need to do any checks for\n\t/// duplicate values.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t/// ```\n\t#[must_use]\n\tpub fn new(data: \u0026'a L) -\u003e Self {\n\t\tRefLockCollection {\n\t\t\tlocks: get_locks(data),\n\t\t\tdata,\n\t\t}\n\t}\n}\n\nimpl\u003cL\u003e RefLockCollection\u003c'_, L\u003e {\n\t/// Gets an immutable reference to the underlying data\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let data1 = Mutex::new(42);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // data1 and data2 refer to distinct mutexes, so this won't panic\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = RefLockCollection::try_new(\u0026data).unwrap();\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let guard = lock.child().0.lock(key);\n\t/// assert_eq!(*guard, 42);\n\t/// ```\n\t#[must_use]\n\tpub const fn child(\u0026self) -\u003e \u0026L {\n\t\tself.data\n\t}\n}\n\nimpl\u003c'a, L: Lockable\u003e RefLockCollection\u003c'a, L\u003e {\n\t/// Creates a new collections of locks.\n\t///\n\t/// # Safety\n\t///\n\t/// This results in undefined behavior if any locks are presented twice\n\t/// within this collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let data1 = Mutex::new(0);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // safety: data1 and data2 refer to distinct mutexes\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = unsafe { RefLockCollection::new_unchecked(\u0026data) };\n\t/// ```\n\t#[must_use]\n\tpub unsafe fn new_unchecked(data: \u0026'a L) -\u003e Self {\n\t\tSelf {\n\t\t\tdata,\n\t\t\tlocks: get_locks(data),\n\t\t}\n\t}\n\n\t/// Creates a new collection of locks.\n\t///\n\t/// This returns `None` if any locks are found twice in the given\n\t/// collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let data1 = Mutex::new(0);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // data1 and data2 refer to distinct mutexes, so this won't panic\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = RefLockCollection::try_new(\u0026data).unwrap();\n\t/// ```\n\t#[must_use]\n\tpub fn try_new(data: \u0026'a L) -\u003e Option\u003cSelf\u003e {\n\t\tlet locks = get_locks(data);\n\t\tif ordered_contains_duplicates(\u0026locks) {\n\t\t\treturn None;\n\t\t}\n\n\t\tSome(Self { data, locks })\n\t}\n\n\tpub fn scoped_lock\u003c's, R\u003e(\u0026's self, key: impl Keyable, f: impl Fn(L::DataMut\u003c's\u003e) -\u003e R) -\u003e R {\n\t\tscoped_write(self, key, f)\n\t}\n\n\tpub fn scoped_try_lock\u003c's, Key: Keyable, R\u003e(\n\t\t\u0026's self,\n\t\tkey: Key,\n\t\tf: impl Fn(L::DataMut\u003c's\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tscoped_try_write(self, key, f)\n\t}\n\n\t/// Locks the collection\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data. When the guard is dropped, the locks in the collection are also\n\t/// dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// ```\n\t#[must_use]\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::Guard\u003c'_\u003e\u003e {\n\t\tlet guard = unsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_write();\n\n\t\t\t// safety: we've locked all of this already\n\t\t\tself.data.guard()\n\t\t};\n\n\t\tLockGuard { guard, key }\n\t}\n\n\t/// Attempts to lock the without blocking.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// locks when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If any of the locks in the collection are already locked, then an error\n\t/// is returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// match lock.try_lock(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::Guard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tlet guard = unsafe {\n\t\t\tif !self.raw_try_write() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we've acquired the locks\n\t\t\tself.data.guard()\n\t\t};\n\n\t\tOk(LockGuard { guard, key })\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// let key = RefLockCollection::\u003c(Mutex\u003ci32\u003e, Mutex\u003c\u0026str\u003e)\u003e::unlock(guard);\n\t/// ```\n\t#[allow(clippy::missing_const_for_fn)]\n\tpub fn unlock(guard: LockGuard\u003cL::Guard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: Sharable\u003e RefLockCollection\u003c'_, L\u003e {\n\tpub fn scoped_read\u003c'a, R\u003e(\u0026'a self, key: impl Keyable, f: impl Fn(L::DataRef\u003c'a\u003e) -\u003e R) -\u003e R {\n\t\tscoped_read(self, key, f)\n\t}\n\n\tpub fn scoped_try_read\u003c'a, Key: Keyable, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(L::DataRef\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tscoped_try_read(self, key, f)\n\t}\n\n\t/// Locks the collection, so that other threads can still read from it\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data immutably. When the guard is dropped, the locks in the collection\n\t/// are also dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// assert_eq!(*guard.0, 0);\n\t/// assert_eq!(*guard.1, \"\");\n\t/// ```\n\t#[must_use]\n\tpub fn read(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_read();\n\n\t\t\tLockGuard {\n\t\t\t\t// safety: we've already acquired the lock\n\t\t\t\tguard: self.data.read_guard(),\n\t\t\t\tkey,\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Attempts to lock the without blocking, in such a way that other threads\n\t/// can still read from the collection.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// shared access when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If any of the locks in the collection are already locked, then an error\n\t/// is returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(5), RwLock::new(\"6\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// match lock.try_read(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// assert_eq!(*guard.0, 5);\n\t/// assert_eq!(*guard.1, \"6\");\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_read(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tlet guard = unsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_read() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we've acquired the locks\n\t\t\tself.data.read_guard()\n\t\t};\n\n\t\tOk(LockGuard { guard, key })\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// let key = RefLockCollection::\u003c(RwLock\u003ci32\u003e, RwLock\u003c\u0026str\u003e)\u003e::unlock_read(guard);\n\t/// ```\n\t#[allow(clippy::missing_const_for_fn)]\n\tpub fn unlock_read(guard: LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003c'a, L: 'a\u003e RefLockCollection\u003c'a, L\u003e\nwhere\n\t\u0026'a L: IntoIterator,\n{\n\t/// Returns an iterator over references to each value in the collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = [Mutex::new(26), Mutex::new(1)];\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// let mut iter = lock.iter();\n\t/// let mutex = iter.next().unwrap();\n\t/// let guard = mutex.lock(key);\n\t///\n\t/// assert_eq!(*guard, 26);\n\t/// ```\n\t#[must_use]\n\tpub fn iter(\u0026'a self) -\u003e \u003c\u0026'a L as IntoIterator\u003e::IntoIter {\n\t\tself.into_iter()\n\t}\n}\n\n#[cfg(test)]\nmod tests {\n\tuse super::*;\n\tuse crate::{Mutex, RwLock, ThreadKey};\n\n\t#[test]\n\tfn non_duplicates_allowed() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(1);\n\t\tassert!(RefLockCollection::try_new(\u0026[\u0026mutex1, \u0026mutex2]).is_some())\n\t}\n\n\t#[test]\n\tfn duplicates_not_allowed() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tassert!(RefLockCollection::try_new(\u0026[\u0026mutex1, \u0026mutex1]).is_none())\n\t}\n\n\t#[test]\n\tfn from() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [Mutex::new(\"foo\"), Mutex::new(\"bar\"), Mutex::new(\"baz\")];\n\t\tlet collection = RefLockCollection::from(\u0026mutexes);\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], \"foo\");\n\t\tassert_eq!(*guard[1], \"bar\");\n\t\tassert_eq!(*guard[2], \"baz\");\n\t}\n\n\t#[test]\n\tfn scoped_lock_changes_collection() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [Mutex::new(24), Mutex::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\t\tlet sum = collection.scoped_lock(\u0026mut key, |guard| {\n\t\t\t*guard[0] = 128;\n\t\t\t*guard[0] + *guard[1]\n\t\t});\n\n\t\tassert_eq!(sum, 128 + 42);\n\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], 128);\n\t\tassert_eq!(*guard[1], 42);\n\t}\n\n\t#[test]\n\tfn scoped_read_sees_changes() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\t\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\t\t*guard[0] = 128;\n\t\t});\n\n\t\tlet sum = collection.scoped_read(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 128);\n\t\t\tassert_eq!(*guard[1], 42);\n\t\t\t*guard[0] + *guard[1]\n\t\t});\n\n\t\tassert_eq!(sum, 128 + 42);\n\t}\n\n\t#[test]\n\tfn scoped_try_lock_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = [Mutex::new(1), Mutex::new(2)];\n\t\tlet collection = RefLockCollection::new(\u0026locks);\n\t\tlet guard = collection.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = collection.scoped_try_lock(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn scoped_try_read_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = [RwLock::new(1), RwLock::new(2)];\n\t\tlet collection = RefLockCollection::new(\u0026locks);\n\t\tlet guard = collection.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = collection.scoped_try_read(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn try_lock_succeeds_for_unlocked_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [Mutex::new(24), Mutex::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\t\tlet guard = collection.try_lock(key).unwrap();\n\t\tassert_eq!(*guard[0], 24);\n\t\tassert_eq!(*guard[1], 42);\n\t}\n\n\t#[test]\n\tfn try_lock_fails_for_locked_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [Mutex::new(24), Mutex::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = mutexes[1].lock(key);\n\t\t\t\tassert_eq!(*guard, 42);\n\t\t\t\tstd::mem::forget(guard);\n\t\t\t});\n\t\t});\n\n\t\tlet guard = collection.try_lock(key);\n\t\tassert!(guard.is_err());\n\t}\n\n\t#[test]\n\tfn try_read_succeeds_for_unlocked_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\t\tlet guard = collection.try_read(key).unwrap();\n\t\tassert_eq!(*guard[0], 24);\n\t\tassert_eq!(*guard[1], 42);\n\t}\n\n\t#[test]\n\tfn try_read_fails_for_locked_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = mutexes[1].write(key);\n\t\t\t\tassert_eq!(*guard, 42);\n\t\t\t\tstd::mem::forget(guard);\n\t\t\t});\n\t\t});\n\n\t\tlet guard = collection.try_read(key);\n\t\tassert!(guard.is_err());\n\t}\n\n\t#[test]\n\tfn can_read_twice_on_different_threads() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.read(key);\n\t\t\t\tassert_eq!(*guard[0], 24);\n\t\t\t\tassert_eq!(*guard[1], 42);\n\t\t\t\tstd::mem::forget(guard);\n\t\t\t});\n\t\t});\n\n\t\tlet guard = collection.try_read(key).unwrap();\n\t\tassert_eq!(*guard[0], 24);\n\t\tassert_eq!(*guard[1], 42);\n\t}\n\n\t#[test]\n\tfn into_ref_iterator() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1), Mutex::new(2)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\t\tfor (i, mutex) in (\u0026collection).into_iter().enumerate() {\n\t\t\tmutex.scoped_lock(\u0026mut key, |val| assert_eq!(*val, i))\n\t\t}\n\t}\n\n\t#[test]\n\tfn ref_iterator() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1), Mutex::new(2)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\t\tfor (i, mutex) in collection.iter().enumerate() {\n\t\t\tmutex.scoped_lock(\u0026mut key, |val| assert_eq!(*val, i))\n\t\t}\n\t}\n\n\t#[test]\n\tfn works_in_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex1 = RwLock::new(0);\n\t\tlet mutex2 = RwLock::new(1);\n\t\tlet collection0 = [\u0026mutex1, \u0026mutex2];\n\t\tlet collection1 = RefLockCollection::try_new(\u0026collection0).unwrap();\n\t\tlet collection = RefLockCollection::try_new(\u0026collection1).unwrap();\n\n\t\tlet mut guard = collection.lock(key);\n\t\tassert!(mutex1.is_locked());\n\t\tassert!(mutex2.is_locked());\n\t\tassert_eq!(*guard[0], 0);\n\t\tassert_eq!(*guard[1], 1);\n\t\t*guard[1] = 2;\n\t\tdrop(guard);\n\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet guard = collection.read(key);\n\t\tassert!(mutex1.is_locked());\n\t\tassert!(mutex2.is_locked());\n\t\tassert_eq!(*guard[0], 0);\n\t\tassert_eq!(*guard[1], 2);\n\t}\n\n\t#[test]\n\tfn unlock_collection_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = (Mutex::new(\"foo\"), Mutex::new(\"bar\"));\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\t\tlet guard = collection.lock(key);\n\n\t\tlet key = RefLockCollection::\u003c(Mutex\u003c_\u003e, Mutex\u003c_\u003e)\u003e::unlock(guard);\n\t\tassert!(collection.try_lock(key).is_ok())\n\t}\n\n\t#[test]\n\tfn read_unlock_collection_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = (RwLock::new(\"foo\"), RwLock::new(\"bar\"));\n\t\tlet collection = RefLockCollection::new(\u0026locks);\n\t\tlet guard = collection.read(key);\n\n\t\tlet key = RefLockCollection::\u003c(\u0026RwLock\u003c_\u003e, \u0026RwLock\u003c_\u003e)\u003e::unlock_read(guard);\n\t\tassert!(collection.try_lock(key).is_ok())\n\t}\n\n\t#[test]\n\tfn as_ref_works() {\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\n\t\tassert!(std::ptr::addr_eq(\u0026mutexes, collection.as_ref()))\n\t}\n\n\t#[test]\n\tfn child() {\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\n\t\tassert!(std::ptr::addr_eq(\u0026mutexes, collection.child()))\n\t}\n}\n","traces":[{"line":19,"address":[1582784],"length":1,"stats":{"Line":1}},{"line":20,"address":[],"length":0,"stats":{"Line":1}},{"line":33,"address":[],"length":0,"stats":{"Line":6}},{"line":34,"address":[],"length":0,"stats":{"Line":6}},{"line":37,"address":[],"length":0,"stats":{"Line":4}},{"line":38,"address":[],"length":0,"stats":{"Line":4}},{"line":41,"address":[1583056,1583152],"length":1,"stats":{"Line":2}},{"line":42,"address":[],"length":0,"stats":{"Line":4}},{"line":43,"address":[],"length":0,"stats":{"Line":2}},{"line":47,"address":[],"length":0,"stats":{"Line":3}},{"line":48,"address":[1583285,1583253,1583317],"length":1,"stats":{"Line":3}},{"line":51,"address":[],"length":0,"stats":{"Line":1}},{"line":52,"address":[],"length":0,"stats":{"Line":1}},{"line":55,"address":[],"length":0,"stats":{"Line":1}},{"line":56,"address":[],"length":0,"stats":{"Line":2}},{"line":57,"address":[],"length":0,"stats":{"Line":1}},{"line":73,"address":[],"length":0,"stats":{"Line":1}},{"line":74,"address":[],"length":0,"stats":{"Line":1}},{"line":77,"address":[],"length":0,"stats":{"Line":1}},{"line":78,"address":[],"length":0,"stats":{"Line":1}},{"line":81,"address":[],"length":0,"stats":{"Line":2}},{"line":82,"address":[],"length":0,"stats":{"Line":2}},{"line":97,"address":[],"length":0,"stats":{"Line":1}},{"line":98,"address":[],"length":0,"stats":{"Line":1}},{"line":101,"address":[],"length":0,"stats":{"Line":1}},{"line":102,"address":[],"length":0,"stats":{"Line":1}},{"line":107,"address":[],"length":0,"stats":{"Line":1}},{"line":108,"address":[],"length":0,"stats":{"Line":1}},{"line":128,"address":[],"length":0,"stats":{"Line":1}},{"line":129,"address":[],"length":0,"stats":{"Line":1}},{"line":149,"address":[],"length":0,"stats":{"Line":6}},{"line":151,"address":[],"length":0,"stats":{"Line":6}},{"line":178,"address":[],"length":0,"stats":{"Line":1}},{"line":179,"address":[],"length":0,"stats":{"Line":1}},{"line":205,"address":[],"length":0,"stats":{"Line":0}},{"line":208,"address":[],"length":0,"stats":{"Line":0}},{"line":231,"address":[],"length":0,"stats":{"Line":3}},{"line":232,"address":[],"length":0,"stats":{"Line":3}},{"line":233,"address":[],"length":0,"stats":{"Line":8}},{"line":234,"address":[],"length":0,"stats":{"Line":1}},{"line":237,"address":[],"length":0,"stats":{"Line":3}},{"line":240,"address":[],"length":0,"stats":{"Line":2}},{"line":241,"address":[],"length":0,"stats":{"Line":2}},{"line":244,"address":[1585472],"length":1,"stats":{"Line":1}},{"line":249,"address":[],"length":0,"stats":{"Line":1}},{"line":273,"address":[],"length":0,"stats":{"Line":5}},{"line":276,"address":[],"length":0,"stats":{"Line":5}},{"line":279,"address":[],"length":0,"stats":{"Line":5}},{"line":315,"address":[],"length":0,"stats":{"Line":3}},{"line":317,"address":[],"length":0,"stats":{"Line":7}},{"line":318,"address":[],"length":0,"stats":{"Line":1}},{"line":322,"address":[],"length":0,"stats":{"Line":5}},{"line":325,"address":[],"length":0,"stats":{"Line":3}},{"line":347,"address":[],"length":0,"stats":{"Line":1}},{"line":348,"address":[1586782],"length":1,"stats":{"Line":1}},{"line":349,"address":[],"length":0,"stats":{"Line":0}},{"line":354,"address":[],"length":0,"stats":{"Line":1}},{"line":355,"address":[],"length":0,"stats":{"Line":1}},{"line":358,"address":[1586896],"length":1,"stats":{"Line":1}},{"line":363,"address":[1586905],"length":1,"stats":{"Line":1}},{"line":387,"address":[],"length":0,"stats":{"Line":3}},{"line":390,"address":[],"length":0,"stats":{"Line":3}},{"line":394,"address":[],"length":0,"stats":{"Line":3}},{"line":431,"address":[1587328,1587498],"length":1,"stats":{"Line":1}},{"line":434,"address":[],"length":0,"stats":{"Line":2}},{"line":435,"address":[1587414],"length":1,"stats":{"Line":1}},{"line":439,"address":[],"length":0,"stats":{"Line":1}},{"line":442,"address":[1587459],"length":1,"stats":{"Line":1}},{"line":462,"address":[],"length":0,"stats":{"Line":1}},{"line":463,"address":[],"length":0,"stats":{"Line":1}},{"line":464,"address":[],"length":0,"stats":{"Line":0}},{"line":491,"address":[],"length":0,"stats":{"Line":1}},{"line":492,"address":[],"length":0,"stats":{"Line":1}}],"covered":69,"coverable":73},{"path":["/","home","botahamec","Projects","happylock","src","collection","retry.rs"],"content":"use std::cell::Cell;\nuse std::collections::HashSet;\n\nuse crate::collection::utils;\nuse crate::handle_unwind::handle_unwind;\nuse crate::lockable::{\n\tLockable, LockableGetMut, LockableIntoInner, OwnedLockable, RawLock, Sharable,\n};\nuse crate::{Keyable, ThreadKey};\n\nuse super::utils::{\n\tattempt_to_recover_reads_from_panic, attempt_to_recover_writes_from_panic, get_locks_unsorted,\n\tscoped_read, scoped_try_read, scoped_try_write, scoped_write,\n};\nuse super::{LockGuard, RetryingLockCollection};\n\n/// Checks that a collection contains no duplicate references to a lock.\nfn contains_duplicates\u003cL: Lockable\u003e(data: L) -\u003e bool {\n\tlet mut locks = Vec::new();\n\tdata.get_ptrs(\u0026mut locks);\n\t// cast to *const () so that the v-table pointers are not used for hashing\n\tlet locks = locks.into_iter().map(|l| (\u0026raw const *l).cast::\u003c()\u003e());\n\n\tlet mut locks_set = HashSet::with_capacity(locks.len());\n\tfor lock in locks {\n\t\tif !locks_set.insert(lock) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tfalse\n}\n\nunsafe impl\u003cL: Lockable\u003e RawLock for RetryingLockCollection\u003cL\u003e {\n\t#[mutants::skip] // this should never run\n\t#[cfg(not(tarpaulin_include))]\n\tfn poison(\u0026self) {\n\t\tlet locks = get_locks_unsorted(\u0026self.data);\n\t\tfor lock in locks {\n\t\t\tlock.poison();\n\t\t}\n\t}\n\n\tunsafe fn raw_write(\u0026self) {\n\t\tlet locks = get_locks_unsorted(\u0026self.data);\n\n\t\tif locks.is_empty() {\n\t\t\t// this probably prevents a panic later\n\t\t\treturn;\n\t\t}\n\n\t\t// these will be unlocked in case of a panic\n\t\tlet first_index = Cell::new(0);\n\t\tlet locked = Cell::new(0);\n\t\thandle_unwind(\n\t\t\t|| unsafe {\n\t\t\t\t'outer: loop {\n\t\t\t\t\t// This prevents us from entering a spin loop waiting for\n\t\t\t\t\t// the same lock to be unlocked\n\t\t\t\t\t// safety: we have the thread key\n\t\t\t\t\tlocks[first_index.get()].raw_write();\n\t\t\t\t\tfor (i, lock) in locks.iter().enumerate() {\n\t\t\t\t\t\tif i == first_index.get() {\n\t\t\t\t\t\t\t// we've already locked this one\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// If the lock has been killed, then this returns false\n\t\t\t\t\t\t// instead of panicking. This sounds like a problem, but if\n\t\t\t\t\t\t// it does return false, then the lock function is called\n\t\t\t\t\t\t// immediately after, causing a panic\n\t\t\t\t\t\t// safety: we have the thread key\n\t\t\t\t\t\tif lock.raw_try_write() {\n\t\t\t\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// safety: we already locked all of these\n\t\t\t\t\t\t\tattempt_to_recover_writes_from_panic(\u0026locks[0..i]);\n\t\t\t\t\t\t\tif first_index.get() \u003e= i {\n\t\t\t\t\t\t\t\t// safety: this is already locked and can't be\n\t\t\t\t\t\t\t\t// unlocked by the previous loop\n\t\t\t\t\t\t\t\tlocks[first_index.get()].raw_unlock_write();\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// nothing is locked anymore\n\t\t\t\t\t\t\tlocked.set(0);\n\n\t\t\t\t\t\t\t// call lock on this to prevent a spin loop\n\t\t\t\t\t\t\tfirst_index.set(i);\n\t\t\t\t\t\t\tcontinue 'outer;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// safety: we locked all the data\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t},\n\t\t\t|| {\n\t\t\t\tutils::attempt_to_recover_writes_from_panic(\u0026locks[0..locked.get()]);\n\t\t\t\tif first_index.get() \u003e= locked.get() {\n\t\t\t\t\tlocks[first_index.get()].raw_unlock_write();\n\t\t\t\t}\n\t\t\t},\n\t\t)\n\t}\n\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool {\n\t\tlet locks = get_locks_unsorted(\u0026self.data);\n\n\t\tif locks.is_empty() {\n\t\t\t// this is an interesting case, but it doesn't give us access to\n\t\t\t// any data, and can't possibly cause a deadlock\n\t\t\treturn true;\n\t\t}\n\n\t\t// these will be unlocked in case of a panic\n\t\tlet locked = Cell::new(0);\n\t\thandle_unwind(\n\t\t\t|| unsafe {\n\t\t\t\tfor (i, lock) in locks.iter().enumerate() {\n\t\t\t\t\t// safety: we have the thread key\n\t\t\t\t\tif lock.raw_try_write() {\n\t\t\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// safety: we already locked all of these\n\t\t\t\t\t\tattempt_to_recover_writes_from_panic(\u0026locks[0..i]);\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ttrue\n\t\t\t},\n\t\t\t|| utils::attempt_to_recover_writes_from_panic(\u0026locks[0..locked.get()]),\n\t\t)\n\t}\n\n\tunsafe fn raw_unlock_write(\u0026self) {\n\t\tlet locks = get_locks_unsorted(\u0026self.data);\n\n\t\tfor lock in locks {\n\t\t\tlock.raw_unlock_write();\n\t\t}\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tlet locks = get_locks_unsorted(\u0026self.data);\n\n\t\tif locks.is_empty() {\n\t\t\t// this probably prevents a panic later\n\t\t\treturn;\n\t\t}\n\n\t\tlet locked = Cell::new(0);\n\t\tlet first_index = Cell::new(0);\n\t\thandle_unwind(\n\t\t\t|| 'outer: loop {\n\t\t\t\t// safety: we have the thread key\n\t\t\t\tlocks[first_index.get()].raw_read();\n\t\t\t\tfor (i, lock) in locks.iter().enumerate() {\n\t\t\t\t\tif i == first_index.get() {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// safety: we have the thread key\n\t\t\t\t\tif lock.raw_try_read() {\n\t\t\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// safety: we already locked all of these\n\t\t\t\t\t\tattempt_to_recover_reads_from_panic(\u0026locks[0..i]);\n\n\t\t\t\t\t\tif first_index.get() \u003e= i {\n\t\t\t\t\t\t\t// safety: this is already locked and can't be unlocked\n\t\t\t\t\t\t\t// by the previous loop\n\t\t\t\t\t\t\tlocks[first_index.get()].raw_unlock_read();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// these are no longer locked\n\t\t\t\t\t\tlocked.set(0);\n\n\t\t\t\t\t\t// don't go into a spin loop, wait for this one to lock\n\t\t\t\t\t\tfirst_index.set(i);\n\t\t\t\t\t\tcontinue 'outer;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// safety: we locked all the data\n\t\t\t\tbreak;\n\t\t\t},\n\t\t\t|| {\n\t\t\t\tutils::attempt_to_recover_reads_from_panic(\u0026locks[0..locked.get()]);\n\t\t\t\tif first_index.get() \u003e= locked.get() {\n\t\t\t\t\tlocks[first_index.get()].raw_unlock_read();\n\t\t\t\t}\n\t\t\t},\n\t\t)\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tlet locks = get_locks_unsorted(\u0026self.data);\n\n\t\tif locks.is_empty() {\n\t\t\t// this is an interesting case, but it doesn't give us access to\n\t\t\t// any data, and can't possibly cause a deadlock\n\t\t\treturn true;\n\t\t}\n\n\t\tlet locked = Cell::new(0);\n\t\thandle_unwind(\n\t\t\t|| unsafe {\n\t\t\t\tfor (i, lock) in locks.iter().enumerate() {\n\t\t\t\t\t// safety: we have the thread key\n\t\t\t\t\tif lock.raw_try_read() {\n\t\t\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// safety: we already locked all of these\n\t\t\t\t\t\tattempt_to_recover_reads_from_panic(\u0026locks[0..i]);\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ttrue\n\t\t\t},\n\t\t\t|| utils::attempt_to_recover_reads_from_panic(\u0026locks[0..locked.get()]),\n\t\t)\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tlet locks = get_locks_unsorted(\u0026self.data);\n\n\t\tfor lock in locks {\n\t\t\tlock.raw_unlock_read();\n\t\t}\n\t}\n}\n\nunsafe impl\u003cL: Lockable\u003e Lockable for RetryingLockCollection\u003cL\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= L::Guard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= L::DataMut\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tptrs.push(self)\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tself.data.guard()\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.data.data_mut()\n\t}\n}\n\nunsafe impl\u003cL: Sharable\u003e Sharable for RetryingLockCollection\u003cL\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= L::ReadGuard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= L::DataRef\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tself.data.read_guard()\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tself.data.data_ref()\n\t}\n}\n\nunsafe impl\u003cL: OwnedLockable\u003e OwnedLockable for RetryingLockCollection\u003cL\u003e {}\n\nimpl\u003cL: LockableGetMut\u003e LockableGetMut for RetryingLockCollection\u003cL\u003e {\n\ttype Inner\u003c'a\u003e\n\t\t= L::Inner\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tself.data.get_mut()\n\t}\n}\n\nimpl\u003cL: LockableIntoInner\u003e LockableIntoInner for RetryingLockCollection\u003cL\u003e {\n\ttype Inner = L::Inner;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tself.data.into_inner()\n\t}\n}\n\nimpl\u003cL\u003e IntoIterator for RetryingLockCollection\u003cL\u003e\nwhere\n\tL: IntoIterator,\n{\n\ttype Item = \u003cL as IntoIterator\u003e::Item;\n\ttype IntoIter = \u003cL as IntoIterator\u003e::IntoIter;\n\n\tfn into_iter(self) -\u003e Self::IntoIter {\n\t\tself.data.into_iter()\n\t}\n}\n\nimpl\u003c'a, L\u003e IntoIterator for \u0026'a RetryingLockCollection\u003cL\u003e\nwhere\n\t\u0026'a L: IntoIterator,\n{\n\ttype Item = \u003c\u0026'a L as IntoIterator\u003e::Item;\n\ttype IntoIter = \u003c\u0026'a L as IntoIterator\u003e::IntoIter;\n\n\tfn into_iter(self) -\u003e Self::IntoIter {\n\t\tself.data.into_iter()\n\t}\n}\n\nimpl\u003c'a, L\u003e IntoIterator for \u0026'a mut RetryingLockCollection\u003cL\u003e\nwhere\n\t\u0026'a mut L: IntoIterator,\n{\n\ttype Item = \u003c\u0026'a mut L as IntoIterator\u003e::Item;\n\ttype IntoIter = \u003c\u0026'a mut L as IntoIterator\u003e::IntoIter;\n\n\tfn into_iter(self) -\u003e Self::IntoIter {\n\t\tself.data.into_iter()\n\t}\n}\n\nimpl\u003cL: OwnedLockable, I: FromIterator\u003cL\u003e + OwnedLockable\u003e FromIterator\u003cL\u003e\n\tfor RetryingLockCollection\u003cI\u003e\n{\n\tfn from_iter\u003cT: IntoIterator\u003cItem = L\u003e\u003e(iter: T) -\u003e Self {\n\t\tlet iter: I = iter.into_iter().collect();\n\t\tSelf::new(iter)\n\t}\n}\n\nimpl\u003cE: OwnedLockable + Extend\u003cL\u003e, L: OwnedLockable\u003e Extend\u003cL\u003e for RetryingLockCollection\u003cE\u003e {\n\tfn extend\u003cT: IntoIterator\u003cItem = L\u003e\u003e(\u0026mut self, iter: T) {\n\t\tself.data.extend(iter)\n\t}\n}\n\nimpl\u003cT: ?Sized, L: AsRef\u003cT\u003e\u003e AsRef\u003cT\u003e for RetryingLockCollection\u003cL\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself.data.as_ref()\n\t}\n}\n\nimpl\u003cT: ?Sized, L: AsMut\u003cT\u003e\u003e AsMut\u003cT\u003e for RetryingLockCollection\u003cL\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself.data.as_mut()\n\t}\n}\n\nimpl\u003cL: OwnedLockable + Default\u003e Default for RetryingLockCollection\u003cL\u003e {\n\tfn default() -\u003e Self {\n\t\tSelf::new(L::default())\n\t}\n}\n\nimpl\u003cL: OwnedLockable\u003e From\u003cL\u003e for RetryingLockCollection\u003cL\u003e {\n\tfn from(value: L) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\nimpl\u003cL: OwnedLockable\u003e RetryingLockCollection\u003cL\u003e {\n\t/// Creates a new collection of owned locks.\n\t///\n\t/// Because the locks are owned, there's no need to do any checks for\n\t/// duplicate values. The locks also don't need to be sorted by memory\n\t/// address because they aren't used anywhere else.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t/// ```\n\t#[must_use]\n\tpub const fn new(data: L) -\u003e Self {\n\t\t// safety: the data cannot cannot contain references\n\t\tunsafe { Self::new_unchecked(data) }\n\t}\n}\n\nimpl\u003c'a, L: OwnedLockable\u003e RetryingLockCollection\u003c\u0026'a L\u003e {\n\t/// Creates a new collection of owned locks.\n\t///\n\t/// Because the locks are owned, there's no need to do any checks for\n\t/// duplicate values.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new_ref(\u0026data);\n\t/// ```\n\t#[must_use]\n\tpub const fn new_ref(data: \u0026'a L) -\u003e Self {\n\t\t// safety: the data cannot cannot contain references\n\t\tunsafe { Self::new_unchecked(data) }\n\t}\n}\n\nimpl\u003cL\u003e RetryingLockCollection\u003cL\u003e {\n\t/// Creates a new collections of locks.\n\t///\n\t/// # Safety\n\t///\n\t/// This results in undefined behavior if any locks are presented twice\n\t/// within this collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let data1 = Mutex::new(0);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // safety: data1 and data2 refer to distinct mutexes\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = unsafe { RetryingLockCollection::new_unchecked(\u0026data) };\n\t/// ```\n\t#[must_use]\n\tpub const unsafe fn new_unchecked(data: L) -\u003e Self {\n\t\tSelf { data }\n\t}\n\n\t/// Gets an immutable reference to the underlying collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let data = (Mutex::new(42), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let inner = lock.child();\n\t/// let guard = inner.0.lock(key);\n\t/// assert_eq!(*guard, 42);\n\t/// ```\n\t#[must_use]\n\tpub const fn child(\u0026self) -\u003e \u0026L {\n\t\t\u0026self.data\n\t}\n\n\t/// Gets a mutable reference to the underlying collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let data = (Mutex::new(42), Mutex::new(\"\"));\n\t/// let mut lock = RetryingLockCollection::new(data);\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut inner = lock.child_mut();\n\t/// let guard = inner.0.get_mut();\n\t/// assert_eq!(*guard, 42);\n\t/// ```\n\t#[must_use]\n\tpub fn child_mut(\u0026mut self) -\u003e \u0026mut L {\n\t\t\u0026mut self.data\n\t}\n\n\t/// Gets the underlying collection, consuming this collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let data = (Mutex::new(42), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let inner = lock.into_child();\n\t/// let guard = inner.0.lock(key);\n\t/// assert_eq!(*guard, 42);\n\t/// ```\n\t#[must_use]\n\tpub fn into_child(self) -\u003e L {\n\t\tself.data\n\t}\n}\n\nimpl\u003cL: Lockable\u003e RetryingLockCollection\u003cL\u003e {\n\t/// Creates a new collection of locks.\n\t///\n\t/// This returns `None` if any locks are found twice in the given\n\t/// collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let data1 = Mutex::new(0);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // data1 and data2 refer to distinct mutexes, so this won't panic\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = RetryingLockCollection::try_new(\u0026data).unwrap();\n\t/// ```\n\t#[must_use]\n\tpub fn try_new(data: L) -\u003e Option\u003cSelf\u003e {\n\t\t// safety: the data is checked for duplicates before returning the collection\n\t\t(!contains_duplicates(\u0026data)).then_some(unsafe { Self::new_unchecked(data) })\n\t}\n\n\tpub fn scoped_lock\u003c'a, R\u003e(\u0026'a self, key: impl Keyable, f: impl Fn(L::DataMut\u003c'a\u003e) -\u003e R) -\u003e R {\n\t\tscoped_write(self, key, f)\n\t}\n\n\tpub fn scoped_try_lock\u003c'a, Key: Keyable, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(L::DataMut\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tscoped_try_write(self, key, f)\n\t}\n\n\t/// Locks the collection\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data. When the guard is dropped, the locks in the collection are also\n\t/// dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// ```\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::Guard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\t// safety: we're taking the thread key\n\t\t\tself.raw_write();\n\n\t\t\tLockGuard {\n\t\t\t\t// safety: we just locked the collection\n\t\t\t\tguard: self.guard(),\n\t\t\t\tkey,\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Attempts to lock the without blocking.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// locks when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If any of the locks in the collection are already locked, then an error\n\t/// is returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// match lock.try_lock(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::Guard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tunsafe {\n\t\t\t// safety: we're taking the thread key\n\t\t\tif self.raw_try_write() {\n\t\t\t\tOk(LockGuard {\n\t\t\t\t\t// safety: we just succeeded in locking everything\n\t\t\t\t\tguard: self.guard(),\n\t\t\t\t\tkey,\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\tErr(key)\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// let key = RetryingLockCollection::\u003c(Mutex\u003ci32\u003e, Mutex\u003c\u0026str\u003e)\u003e::unlock(guard);\n\t/// ```\n\tpub fn unlock(guard: LockGuard\u003cL::Guard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: Sharable\u003e RetryingLockCollection\u003cL\u003e {\n\tpub fn scoped_read\u003c'a, R\u003e(\u0026'a self, key: impl Keyable, f: impl Fn(L::DataRef\u003c'a\u003e) -\u003e R) -\u003e R {\n\t\tscoped_read(self, key, f)\n\t}\n\n\tpub fn scoped_try_read\u003c'a, Key: Keyable, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(L::DataRef\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tscoped_try_read(self, key, f)\n\t}\n\n\t/// Locks the collection, so that other threads can still read from it\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data immutably. When the guard is dropped, the locks in the collection\n\t/// are also dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// assert_eq!(*guard.0, 0);\n\t/// assert_eq!(*guard.1, \"\");\n\t/// ```\n\tpub fn read(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\t// safety: we're taking the thread key\n\t\t\tself.raw_read();\n\n\t\t\tLockGuard {\n\t\t\t\t// safety: we just locked the collection\n\t\t\t\tguard: self.read_guard(),\n\t\t\t\tkey,\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Attempts to lock the without blocking, in such a way that other threads\n\t/// can still read from the collection.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// shared access when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If shared access cannot be acquired at this time, then an error is\n\t/// returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(5), RwLock::new(\"6\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// match lock.try_read(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// assert_eq!(*guard.0, 5);\n\t/// assert_eq!(*guard.1, \"6\");\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_read(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tunsafe {\n\t\t\t// safety: we're taking the thread key\n\t\t\tif !self.raw_try_read() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\tOk(LockGuard {\n\t\t\t\t// safety: we just succeeded in locking everything\n\t\t\t\tguard: self.read_guard(),\n\t\t\t\tkey,\n\t\t\t})\n\t\t}\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// let key = RetryingLockCollection::\u003c(RwLock\u003ci32\u003e, RwLock\u003c\u0026str\u003e)\u003e::unlock_read(guard);\n\t/// ```\n\tpub fn unlock_read(guard: LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: LockableGetMut\u003e RetryingLockCollection\u003cL\u003e {\n\t/// Gets a mutable reference to the data behind this\n\t/// `RetryingLockCollection`.\n\t///\n\t/// Since this call borrows the `RetryingLockCollection` mutably, no actual\n\t/// locking needs to take place - the mutable borrow statically guarantees\n\t/// no locks exist.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let mut mutex = RetryingLockCollection::new([Mutex::new(0), Mutex::new(0)]);\n\t/// assert_eq!(mutex.get_mut(), [\u0026mut 0, \u0026mut 0]);\n\t/// ```\n\tpub fn get_mut(\u0026mut self) -\u003e L::Inner\u003c'_\u003e {\n\t\tLockableGetMut::get_mut(self)\n\t}\n}\n\nimpl\u003cL: LockableIntoInner\u003e RetryingLockCollection\u003cL\u003e {\n\t/// Consumes this `RetryingLockCollection`, returning the underlying data.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let mutex = RetryingLockCollection::new([Mutex::new(0), Mutex::new(0)]);\n\t/// assert_eq!(mutex.into_inner(), [0, 0]);\n\t/// ```\n\tpub fn into_inner(self) -\u003e L::Inner {\n\t\tLockableIntoInner::into_inner(self)\n\t}\n}\n\nimpl\u003c'a, L: 'a\u003e RetryingLockCollection\u003cL\u003e\nwhere\n\t\u0026'a L: IntoIterator,\n{\n\t/// Returns an iterator over references to each value in the collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = [Mutex::new(26), Mutex::new(1)];\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let mut iter = lock.iter();\n\t/// let mutex = iter.next().unwrap();\n\t/// let guard = mutex.lock(key);\n\t///\n\t/// assert_eq!(*guard, 26);\n\t/// ```\n\t#[must_use]\n\tpub fn iter(\u0026'a self) -\u003e \u003c\u0026'a L as IntoIterator\u003e::IntoIter {\n\t\tself.into_iter()\n\t}\n}\n\nimpl\u003c'a, L: 'a\u003e RetryingLockCollection\u003cL\u003e\nwhere\n\t\u0026'a mut L: IntoIterator,\n{\n\t/// Returns an iterator over mutable references to each value in the\n\t/// collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = [Mutex::new(26), Mutex::new(1)];\n\t/// let mut lock = RetryingLockCollection::new(data);\n\t///\n\t/// let mut iter = lock.iter_mut();\n\t/// let mutex = iter.next().unwrap();\n\t///\n\t/// assert_eq!(*mutex.as_mut(), 26);\n\t/// ```\n\t#[must_use]\n\tpub fn iter_mut(\u0026'a mut self) -\u003e \u003c\u0026'a mut L as IntoIterator\u003e::IntoIter {\n\t\tself.into_iter()\n\t}\n}\n\n#[cfg(test)]\nmod tests {\n\tuse super::*;\n\tuse crate::collection::BoxedLockCollection;\n\tuse crate::{Mutex, RwLock, ThreadKey};\n\n\t#[test]\n\tfn nonduplicate_lock_references_are_allowed() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(0);\n\t\tassert!(RetryingLockCollection::try_new([\u0026mutex1, \u0026mutex2]).is_some());\n\t}\n\n\t#[test]\n\tfn duplicate_lock_references_are_disallowed() {\n\t\tlet mutex = Mutex::new(0);\n\t\tassert!(RetryingLockCollection::try_new([\u0026mutex, \u0026mutex]).is_none());\n\t}\n\n\t#[test]\n\t#[allow(clippy::float_cmp)]\n\tfn uses_correct_default() {\n\t\tlet collection =\n\t\t\tRetryingLockCollection::\u003c(RwLock\u003cf64\u003e, Mutex\u003cOption\u003ci32\u003e\u003e, Mutex\u003cusize\u003e)\u003e::default();\n\t\tlet tuple = collection.into_inner();\n\t\tassert_eq!(tuple.0, 0.0);\n\t\tassert!(tuple.1.is_none());\n\t\tassert_eq!(tuple.2, 0)\n\t}\n\n\t#[test]\n\tfn from() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tRetryingLockCollection::from([Mutex::new(\"foo\"), Mutex::new(\"bar\"), Mutex::new(\"baz\")]);\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], \"foo\");\n\t\tassert_eq!(*guard[1], \"bar\");\n\t\tassert_eq!(*guard[2], \"baz\");\n\t}\n\n\t#[test]\n\tfn new_ref_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet collection = RetryingLockCollection::new_ref(\u0026mutexes);\n\t\tcollection.scoped_lock(key, |guard| {\n\t\t\tassert_eq!(*guard[0], 0);\n\t\t\tassert_eq!(*guard[1], 1);\n\t\t})\n\t}\n\n\t#[test]\n\tfn scoped_read_sees_changes() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = RetryingLockCollection::new(mutexes);\n\t\tcollection.scoped_lock(\u0026mut key, |guard| *guard[0] = 128);\n\n\t\tlet sum = collection.scoped_read(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 128);\n\t\t\tassert_eq!(*guard[1], 42);\n\t\t\t*guard[0] + *guard[1]\n\t\t});\n\n\t\tassert_eq!(sum, 128 + 42);\n\t}\n\n\t#[test]\n\tfn get_mut_affects_scoped_read() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet mut collection = RetryingLockCollection::new(mutexes);\n\t\tlet guard = collection.get_mut();\n\t\t*guard[0] = 128;\n\n\t\tlet sum = collection.scoped_read(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 128);\n\t\t\tassert_eq!(*guard[1], 42);\n\t\t\t*guard[0] + *guard[1]\n\t\t});\n\n\t\tassert_eq!(sum, 128 + 42);\n\t}\n\n\t#[test]\n\tfn scoped_try_lock_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = RetryingLockCollection::new([Mutex::new(1), Mutex::new(2)]);\n\t\tlet guard = collection.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = collection.scoped_try_lock(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn scoped_try_read_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = RetryingLockCollection::new([RwLock::new(1), RwLock::new(2)]);\n\t\tlet guard = collection.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = collection.scoped_try_read(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn try_lock_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = RetryingLockCollection::new([Mutex::new(1), Mutex::new(2)]);\n\t\tlet guard = collection.try_lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_lock(key);\n\t\t\t\tassert!(guard.is_err());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn try_read_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = RetryingLockCollection::new([RwLock::new(1), RwLock::new(2)]);\n\t\tlet guard = collection.try_read(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_read(key);\n\t\t\t\tassert!(guard.is_ok());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn try_read_fails_for_locked_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = RetryingLockCollection::new_ref(\u0026mutexes);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = mutexes[1].write(key);\n\t\t\t\tassert_eq!(*guard, 42);\n\t\t\t\tstd::mem::forget(guard);\n\t\t\t});\n\t\t});\n\n\t\tlet guard = collection.try_read(key);\n\t\tassert!(guard.is_err());\n\t}\n\n\t#[test]\n\tfn locks_all_inner_mutexes() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(0);\n\t\tlet collection = RetryingLockCollection::try_new([\u0026mutex1, \u0026mutex2]).unwrap();\n\n\t\tlet guard = collection.lock(key);\n\n\t\tassert!(mutex1.is_locked());\n\t\tassert!(mutex2.is_locked());\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn locks_all_inner_rwlocks() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet rwlock1 = RwLock::new(0);\n\t\tlet rwlock2 = RwLock::new(0);\n\t\tlet collection = RetryingLockCollection::try_new([\u0026rwlock1, \u0026rwlock2]).unwrap();\n\n\t\tlet guard = collection.read(key);\n\n\t\tassert!(rwlock1.is_locked());\n\t\tassert!(rwlock2.is_locked());\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn works_with_other_collections() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(0);\n\t\tlet collection = BoxedLockCollection::try_new(\n\t\t\tRetryingLockCollection::try_new([\u0026mutex1, \u0026mutex2]).unwrap(),\n\t\t)\n\t\t.unwrap();\n\n\t\tlet guard = collection.lock(key);\n\n\t\tassert!(mutex1.is_locked());\n\t\tassert!(mutex2.is_locked());\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn from_iterator() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection: RetryingLockCollection\u003cVec\u003cMutex\u003c\u0026str\u003e\u003e\u003e =\n\t\t\t[Mutex::new(\"foo\"), Mutex::new(\"bar\"), Mutex::new(\"baz\")]\n\t\t\t\t.into_iter()\n\t\t\t\t.collect();\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], \"foo\");\n\t\tassert_eq!(*guard[1], \"bar\");\n\t\tassert_eq!(*guard[2], \"baz\");\n\t}\n\n\t#[test]\n\tfn into_owned_iterator() {\n\t\tlet collection = RetryingLockCollection::new([Mutex::new(0), Mutex::new(1), Mutex::new(2)]);\n\t\tfor (i, mutex) in collection.into_iter().enumerate() {\n\t\t\tassert_eq!(mutex.into_inner(), i);\n\t\t}\n\t}\n\n\t#[test]\n\tfn into_ref_iterator() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet collection = RetryingLockCollection::new([Mutex::new(0), Mutex::new(1), Mutex::new(2)]);\n\t\tfor (i, mutex) in (\u0026collection).into_iter().enumerate() {\n\t\t\tmutex.scoped_lock(\u0026mut key, |val| assert_eq!(*val, i))\n\t\t}\n\t}\n\n\t#[test]\n\tfn ref_iterator() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet collection = RetryingLockCollection::new([Mutex::new(0), Mutex::new(1), Mutex::new(2)]);\n\t\tfor (i, mutex) in collection.iter().enumerate() {\n\t\t\tmutex.scoped_lock(\u0026mut key, |val| assert_eq!(*val, i))\n\t\t}\n\t}\n\n\t#[test]\n\tfn mut_iterator() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mut collection =\n\t\t\tRetryingLockCollection::new([Mutex::new(0), Mutex::new(1), Mutex::new(2)]);\n\t\tfor (i, mutex) in collection.iter_mut().enumerate() {\n\t\t\tmutex.scoped_lock(\u0026mut key, |val| assert_eq!(*val, i))\n\t\t}\n\t}\n\n\t#[test]\n\tfn extend_collection() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(0);\n\t\tlet mut collection = RetryingLockCollection::new(vec![mutex1]);\n\n\t\tcollection.extend([mutex2]);\n\n\t\tassert_eq!(collection.into_inner().len(), 2);\n\t}\n\n\t#[test]\n\tfn lock_empty_lock_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection: RetryingLockCollection\u003c[RwLock\u003ci32\u003e; 0]\u003e = RetryingLockCollection::new([]);\n\n\t\tlet guard = collection.lock(key);\n\t\tassert!(guard.len() == 0);\n\t\tlet key = RetryingLockCollection::\u003c[RwLock\u003c_\u003e; 0]\u003e::unlock(guard);\n\n\t\tlet guard = collection.read(key);\n\t\tassert!(guard.len() == 0);\n\t}\n\n\t#[test]\n\tfn read_empty_lock_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection: RetryingLockCollection\u003c[RwLock\u003ci32\u003e; 0]\u003e = RetryingLockCollection::new([]);\n\n\t\tlet guard = collection.read(key);\n\t\tassert!(guard.len() == 0);\n\t\tlet key = RetryingLockCollection::\u003c[RwLock\u003c_\u003e; 0]\u003e::unlock_read(guard);\n\n\t\tlet guard = collection.lock(key);\n\t\tassert!(guard.len() == 0);\n\t}\n\n\t#[test]\n\tfn as_ref_works() {\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet collection = RetryingLockCollection::new_ref(\u0026mutexes);\n\n\t\tassert!(std::ptr::addr_eq(\u0026mutexes, collection.as_ref()))\n\t}\n\n\t#[test]\n\tfn as_mut_works() {\n\t\tlet mut mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet mut collection = RetryingLockCollection::new(\u0026mut mutexes);\n\n\t\tcollection.as_mut()[0] = Mutex::new(42);\n\n\t\tassert_eq!(*collection.as_mut()[0].get_mut(), 42);\n\t}\n\n\t#[test]\n\tfn child() {\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet collection = RetryingLockCollection::new_ref(\u0026mutexes);\n\n\t\tassert!(std::ptr::addr_eq(\u0026mutexes, *collection.child()))\n\t}\n\n\t#[test]\n\tfn child_mut_works() {\n\t\tlet mut mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet mut collection = RetryingLockCollection::new(\u0026mut mutexes);\n\n\t\tcollection.child_mut()[0] = Mutex::new(42);\n\n\t\tassert_eq!(*collection.child_mut()[0].get_mut(), 42);\n\t}\n\n\t#[test]\n\tfn into_child_works() {\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet mut collection = RetryingLockCollection::new(mutexes);\n\n\t\tcollection.child_mut()[0] = Mutex::new(42);\n\n\t\tassert_eq!(\n\t\t\t*collection\n\t\t\t\t.into_child()\n\t\t\t\t.as_mut()\n\t\t\t\t.get_mut(0)\n\t\t\t\t.unwrap()\n\t\t\t\t.get_mut(),\n\t\t\t42\n\t\t);\n\t}\n}\n","traces":[{"line":18,"address":[213893,213208,213168],"length":1,"stats":{"Line":11}},{"line":19,"address":[1090812,1091580],"length":1,"stats":{"Line":11}},{"line":20,"address":[1090875,1091643],"length":1,"stats":{"Line":11}},{"line":22,"address":[],"length":0,"stats":{"Line":33}},{"line":24,"address":[186400,185566,186334,185632],"length":1,"stats":{"Line":22}},{"line":25,"address":[201469,201427,201288,201191],"length":1,"stats":{"Line":44}},{"line":26,"address":[1092230,1092165,1091397,1091462],"length":1,"stats":{"Line":23}},{"line":27,"address":[186784,186016],"length":1,"stats":{"Line":1}},{"line":31,"address":[],"length":0,"stats":{"Line":11}},{"line":44,"address":[],"length":0,"stats":{"Line":11}},{"line":45,"address":[172956],"length":1,"stats":{"Line":11}},{"line":47,"address":[227126,227178],"length":1,"stats":{"Line":22}},{"line":49,"address":[],"length":0,"stats":{"Line":0}},{"line":53,"address":[],"length":0,"stats":{"Line":20}},{"line":54,"address":[219626],"length":1,"stats":{"Line":10}},{"line":56,"address":[184784],"length":1,"stats":{"Line":20}},{"line":57,"address":[],"length":0,"stats":{"Line":0}},{"line":61,"address":[212449],"length":1,"stats":{"Line":10}},{"line":62,"address":[184854,185006],"length":1,"stats":{"Line":20}},{"line":63,"address":[210824],"length":1,"stats":{"Line":10}},{"line":65,"address":[],"length":0,"stats":{"Line":0}},{"line":73,"address":[185080],"length":1,"stats":{"Line":10}},{"line":74,"address":[1096138,1095722,1093898,1095578,1094602,1096282,1093338,1092778,1093482,1092922,1094042,1094458,1095018,1095162],"length":1,"stats":{"Line":18}},{"line":77,"address":[210880],"length":1,"stats":{"Line":1}},{"line":78,"address":[185140],"length":1,"stats":{"Line":1}},{"line":81,"address":[185251],"length":1,"stats":{"Line":1}},{"line":85,"address":[140775],"length":1,"stats":{"Line":1}},{"line":88,"address":[212879],"length":1,"stats":{"Line":1}},{"line":89,"address":[],"length":0,"stats":{"Line":0}},{"line":94,"address":[],"length":0,"stats":{"Line":0}},{"line":97,"address":[184608],"length":1,"stats":{"Line":11}},{"line":98,"address":[140921],"length":1,"stats":{"Line":1}},{"line":99,"address":[213060],"length":1,"stats":{"Line":1}},{"line":100,"address":[],"length":0,"stats":{"Line":1}},{"line":106,"address":[218925,218736],"length":1,"stats":{"Line":3}},{"line":107,"address":[203446],"length":1,"stats":{"Line":3}},{"line":109,"address":[],"length":0,"stats":{"Line":6}},{"line":112,"address":[218840],"length":1,"stats":{"Line":0}},{"line":116,"address":[203550,203508],"length":1,"stats":{"Line":6}},{"line":118,"address":[200464],"length":1,"stats":{"Line":3}},{"line":119,"address":[184415,184273],"length":1,"stats":{"Line":6}},{"line":121,"address":[200657],"length":1,"stats":{"Line":3}},{"line":122,"address":[200732],"length":1,"stats":{"Line":3}},{"line":125,"address":[1097776],"length":1,"stats":{"Line":2}},{"line":126,"address":[200725],"length":1,"stats":{"Line":3}},{"line":130,"address":[184408],"length":1,"stats":{"Line":1}},{"line":132,"address":[1097888,1097902],"length":1,"stats":{"Line":2}},{"line":136,"address":[219216,219461,219189,218944],"length":1,"stats":{"Line":3}},{"line":137,"address":[219234,218962],"length":1,"stats":{"Line":3}},{"line":139,"address":[219096,219150,219422,218972,219368,219244],"length":1,"stats":{"Line":9}},{"line":140,"address":[],"length":0,"stats":{"Line":3}},{"line":144,"address":[202768,203016],"length":1,"stats":{"Line":5}},{"line":145,"address":[181996],"length":1,"stats":{"Line":5}},{"line":147,"address":[1590870,1590922,1590598,1590650,1591142,1591194],"length":1,"stats":{"Line":10}},{"line":149,"address":[],"length":0,"stats":{"Line":0}},{"line":152,"address":[],"length":0,"stats":{"Line":8}},{"line":153,"address":[182106],"length":1,"stats":{"Line":4}},{"line":155,"address":[202941],"length":1,"stats":{"Line":8}},{"line":157,"address":[1099105,1098545,1097985],"length":1,"stats":{"Line":4}},{"line":158,"address":[],"length":0,"stats":{"Line":8}},{"line":159,"address":[],"length":0,"stats":{"Line":4}},{"line":160,"address":[],"length":0,"stats":{"Line":0}},{"line":164,"address":[206728],"length":1,"stats":{"Line":4}},{"line":165,"address":[1099050,1098906,1098490,1098346,1099610,1099466],"length":1,"stats":{"Line":6}},{"line":168,"address":[177984],"length":1,"stats":{"Line":1}},{"line":170,"address":[1099439,1098879,1098319],"length":1,"stats":{"Line":1}},{"line":173,"address":[206899],"length":1,"stats":{"Line":1}},{"line":177,"address":[178087],"length":1,"stats":{"Line":1}},{"line":180,"address":[178111],"length":1,"stats":{"Line":1}},{"line":181,"address":[],"length":0,"stats":{"Line":0}},{"line":186,"address":[],"length":0,"stats":{"Line":0}},{"line":188,"address":[182166],"length":1,"stats":{"Line":5}},{"line":189,"address":[],"length":0,"stats":{"Line":1}},{"line":190,"address":[207060],"length":1,"stats":{"Line":1}},{"line":191,"address":[],"length":0,"stats":{"Line":1}},{"line":197,"address":[],"length":0,"stats":{"Line":3}},{"line":198,"address":[1591606,1591398],"length":1,"stats":{"Line":3}},{"line":200,"address":[1591408,1591616,1591454,1591662],"length":1,"stats":{"Line":6}},{"line":203,"address":[],"length":0,"stats":{"Line":0}},{"line":206,"address":[],"length":0,"stats":{"Line":8}},{"line":208,"address":[],"length":0,"stats":{"Line":4}},{"line":209,"address":[],"length":0,"stats":{"Line":8}},{"line":211,"address":[],"length":0,"stats":{"Line":4}},{"line":212,"address":[],"length":0,"stats":{"Line":3}},{"line":215,"address":[1100400,1100736],"length":1,"stats":{"Line":2}},{"line":216,"address":[202725],"length":1,"stats":{"Line":2}},{"line":220,"address":[1100324,1100660],"length":1,"stats":{"Line":1}},{"line":222,"address":[1100942,1100848,1100862,1100928],"length":1,"stats":{"Line":2}},{"line":226,"address":[1591792,1592037],"length":1,"stats":{"Line":1}},{"line":227,"address":[1591810],"length":1,"stats":{"Line":1}},{"line":229,"address":[],"length":0,"stats":{"Line":3}},{"line":230,"address":[],"length":0,"stats":{"Line":1}},{"line":246,"address":[],"length":0,"stats":{"Line":1}},{"line":247,"address":[],"length":0,"stats":{"Line":1}},{"line":250,"address":[151168],"length":1,"stats":{"Line":8}},{"line":251,"address":[],"length":0,"stats":{"Line":8}},{"line":254,"address":[],"length":0,"stats":{"Line":3}},{"line":255,"address":[219777,219809],"length":1,"stats":{"Line":3}},{"line":270,"address":[190560],"length":1,"stats":{"Line":4}},{"line":271,"address":[182257],"length":1,"stats":{"Line":4}},{"line":274,"address":[1592464],"length":1,"stats":{"Line":1}},{"line":275,"address":[1592481],"length":1,"stats":{"Line":1}},{"line":287,"address":[1592496],"length":1,"stats":{"Line":1}},{"line":288,"address":[1592513],"length":1,"stats":{"Line":1}},{"line":295,"address":[],"length":0,"stats":{"Line":2}},{"line":296,"address":[1592532,1592589],"length":1,"stats":{"Line":2}},{"line":307,"address":[1592640],"length":1,"stats":{"Line":1}},{"line":308,"address":[1592654],"length":1,"stats":{"Line":1}},{"line":319,"address":[1592704],"length":1,"stats":{"Line":1}},{"line":320,"address":[1592709],"length":1,"stats":{"Line":1}},{"line":331,"address":[1592720],"length":1,"stats":{"Line":1}},{"line":332,"address":[],"length":0,"stats":{"Line":1}},{"line":339,"address":[1592736],"length":1,"stats":{"Line":1}},{"line":340,"address":[],"length":0,"stats":{"Line":1}},{"line":341,"address":[],"length":0,"stats":{"Line":1}},{"line":346,"address":[1592816],"length":1,"stats":{"Line":1}},{"line":347,"address":[],"length":0,"stats":{"Line":1}},{"line":352,"address":[],"length":0,"stats":{"Line":1}},{"line":353,"address":[1592885],"length":1,"stats":{"Line":1}},{"line":358,"address":[],"length":0,"stats":{"Line":1}},{"line":359,"address":[],"length":0,"stats":{"Line":1}},{"line":364,"address":[1592912],"length":1,"stats":{"Line":1}},{"line":365,"address":[],"length":0,"stats":{"Line":1}},{"line":370,"address":[],"length":0,"stats":{"Line":1}},{"line":371,"address":[],"length":0,"stats":{"Line":1}},{"line":392,"address":[],"length":0,"stats":{"Line":9}},{"line":394,"address":[1593000,1593224,1593192,1593096,1593141,1593121,1593064,1593032,1593160],"length":1,"stats":{"Line":10}},{"line":414,"address":[1593248,1593264],"length":1,"stats":{"Line":3}},{"line":416,"address":[1593253,1593269],"length":1,"stats":{"Line":3}},{"line":442,"address":[190592],"length":1,"stats":{"Line":25}},{"line":463,"address":[1593648],"length":1,"stats":{"Line":1}},{"line":464,"address":[],"length":0,"stats":{"Line":0}},{"line":484,"address":[],"length":0,"stats":{"Line":2}},{"line":485,"address":[],"length":0,"stats":{"Line":0}},{"line":505,"address":[],"length":0,"stats":{"Line":1}},{"line":506,"address":[],"length":0,"stats":{"Line":1}},{"line":530,"address":[151557,151392],"length":1,"stats":{"Line":11}},{"line":532,"address":[203346,203291],"length":1,"stats":{"Line":23}},{"line":535,"address":[1594112,1594080],"length":1,"stats":{"Line":3}},{"line":536,"address":[],"length":0,"stats":{"Line":3}},{"line":539,"address":[219920],"length":1,"stats":{"Line":2}},{"line":544,"address":[1594153],"length":1,"stats":{"Line":2}},{"line":567,"address":[173390,173264],"length":1,"stats":{"Line":9}},{"line":570,"address":[1594878,1594606,1594190,1594752,1594464,1594320],"length":1,"stats":{"Line":9}},{"line":574,"address":[173342],"length":1,"stats":{"Line":8}},{"line":610,"address":[204080,203888],"length":1,"stats":{"Line":2}},{"line":613,"address":[1595124,1595053,1594992,1595035],"length":1,"stats":{"Line":5}},{"line":614,"address":[204022],"length":1,"stats":{"Line":1}},{"line":616,"address":[204000],"length":1,"stats":{"Line":1}},{"line":617,"address":[],"length":0,"stats":{"Line":0}},{"line":620,"address":[],"length":0,"stats":{"Line":1}},{"line":643,"address":[],"length":0,"stats":{"Line":1}},{"line":644,"address":[],"length":0,"stats":{"Line":1}},{"line":645,"address":[],"length":0,"stats":{"Line":0}},{"line":650,"address":[],"length":0,"stats":{"Line":2}},{"line":651,"address":[],"length":0,"stats":{"Line":2}},{"line":654,"address":[1595280],"length":1,"stats":{"Line":1}},{"line":659,"address":[1595289],"length":1,"stats":{"Line":1}},{"line":682,"address":[182430,182304],"length":1,"stats":{"Line":4}},{"line":685,"address":[1595440,1595326],"length":1,"stats":{"Line":4}},{"line":689,"address":[182382],"length":1,"stats":{"Line":3}},{"line":726,"address":[],"length":0,"stats":{"Line":3}},{"line":729,"address":[],"length":0,"stats":{"Line":5}},{"line":730,"address":[190909],"length":1,"stats":{"Line":1}},{"line":733,"address":[190950],"length":1,"stats":{"Line":1}},{"line":735,"address":[],"length":0,"stats":{"Line":1}},{"line":736,"address":[],"length":0,"stats":{"Line":0}},{"line":757,"address":[],"length":0,"stats":{"Line":1}},{"line":758,"address":[1595940],"length":1,"stats":{"Line":1}},{"line":759,"address":[],"length":0,"stats":{"Line":0}},{"line":780,"address":[],"length":0,"stats":{"Line":1}},{"line":781,"address":[],"length":0,"stats":{"Line":1}},{"line":797,"address":[],"length":0,"stats":{"Line":2}},{"line":798,"address":[],"length":0,"stats":{"Line":2}},{"line":825,"address":[],"length":0,"stats":{"Line":1}},{"line":826,"address":[],"length":0,"stats":{"Line":1}},{"line":853,"address":[],"length":0,"stats":{"Line":1}},{"line":854,"address":[],"length":0,"stats":{"Line":1}}],"covered":161,"coverable":178},{"path":["/","home","botahamec","Projects","happylock","src","collection","utils.rs"],"content":"use std::cell::Cell;\n\nuse crate::handle_unwind::handle_unwind;\nuse crate::lockable::{Lockable, RawLock, Sharable};\nuse crate::Keyable;\n\n#[must_use]\npub fn get_locks\u003cL: Lockable\u003e(data: \u0026L) -\u003e Vec\u003c\u0026dyn RawLock\u003e {\n\tlet mut locks = Vec::new();\n\tdata.get_ptrs(\u0026mut locks);\n\tlocks.sort_by_key(|lock| \u0026raw const **lock);\n\tlocks\n}\n\n#[must_use]\npub fn get_locks_unsorted\u003cL: Lockable\u003e(data: \u0026L) -\u003e Vec\u003c\u0026dyn RawLock\u003e {\n\tlet mut locks = Vec::new();\n\tdata.get_ptrs(\u0026mut locks);\n\tlocks\n}\n\n/// returns `true` if the sorted list contains a duplicate\n#[must_use]\npub fn ordered_contains_duplicates(l: \u0026[\u0026dyn RawLock]) -\u003e bool {\n\tif l.is_empty() {\n\t\t// Return early to prevent panic in the below call to `windows`\n\t\treturn false;\n\t}\n\n\tl.windows(2)\n\t\t// NOTE: addr_eq is necessary because eq would also compare the v-table pointers\n\t\t.any(|window| std::ptr::addr_eq(window[0], window[1]))\n}\n\n/// Lock a set of locks in the given order. It's UB to call this without a `ThreadKey`\npub unsafe fn ordered_write(locks: \u0026[\u0026dyn RawLock]) {\n\t// these will be unlocked in case of a panic\n\tlet locked = Cell::new(0);\n\n\thandle_unwind(\n\t\t|| {\n\t\t\tfor lock in locks {\n\t\t\t\tlock.raw_write();\n\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t}\n\t\t},\n\t\t|| attempt_to_recover_writes_from_panic(\u0026locks[0..locked.get()]),\n\t)\n}\n\n/// Lock a set of locks in the given order. It's UB to call this without a `ThreadKey`\npub unsafe fn ordered_read(locks: \u0026[\u0026dyn RawLock]) {\n\tlet locked = Cell::new(0);\n\n\thandle_unwind(\n\t\t|| {\n\t\t\tfor lock in locks {\n\t\t\t\tlock.raw_read();\n\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t}\n\t\t},\n\t\t|| attempt_to_recover_reads_from_panic(\u0026locks[0..locked.get()]),\n\t)\n}\n\n/// Locks the locks in the order they are given. This causes deadlock if the\n/// locks contain duplicates, or if this is called by multiple threads with the\n/// locks in different orders.\npub unsafe fn ordered_try_write(locks: \u0026[\u0026dyn RawLock]) -\u003e bool {\n\tlet locked = Cell::new(0);\n\n\thandle_unwind(\n\t\t|| unsafe {\n\t\t\tfor (i, lock) in locks.iter().enumerate() {\n\t\t\t\t// safety: we have the thread key\n\t\t\t\tif lock.raw_try_write() {\n\t\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t\t} else {\n\t\t\t\t\tfor lock in \u0026locks[0..i] {\n\t\t\t\t\t\t// safety: this lock was already acquired\n\t\t\t\t\t\tlock.raw_unlock_write();\n\t\t\t\t\t}\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttrue\n\t\t},\n\t\t||\n\t\t// safety: everything in locked is locked\n\t\tattempt_to_recover_writes_from_panic(\u0026locks[0..locked.get()]),\n\t)\n}\n\n/// Locks the locks in the order they are given. This causes deadlock if this\n/// is called by multiple threads with the locks in different orders.\npub unsafe fn ordered_try_read(locks: \u0026[\u0026dyn RawLock]) -\u003e bool {\n\t// these will be unlocked in case of a panic\n\tlet locked = Cell::new(0);\n\n\thandle_unwind(\n\t\t|| unsafe {\n\t\t\tfor (i, lock) in locks.iter().enumerate() {\n\t\t\t\t// safety: we have the thread key\n\t\t\t\tif lock.raw_try_read() {\n\t\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t\t} else {\n\t\t\t\t\tfor lock in \u0026locks[0..i] {\n\t\t\t\t\t\t// safety: this lock was already acquired\n\t\t\t\t\t\tlock.raw_unlock_read();\n\t\t\t\t\t}\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttrue\n\t\t},\n\t\t||\n\t\t// safety: everything in locked is locked\n\t\tattempt_to_recover_reads_from_panic(\u0026locks[0..locked.get()]),\n\t)\n}\n\npub fn scoped_write\u003c'a, L: RawLock + Lockable, R\u003e(\n\tcollection: \u0026'a L,\n\tkey: impl Keyable,\n\tf: impl FnOnce(L::DataMut\u003c'a\u003e) -\u003e R,\n) -\u003e R {\n\tunsafe {\n\t\t// safety: we have the key\n\t\tcollection.raw_write();\n\n\t\t// safety: we just locked this\n\t\tlet r = f(collection.data_mut());\n\n\t\t// this ensures the key is held long enough\n\t\tdrop(key);\n\n\t\t// safety: we've locked already, and aren't using the data again\n\t\tcollection.raw_unlock_write();\n\n\t\tr\n\t}\n}\n\npub fn scoped_try_write\u003c'a, L: RawLock + Lockable, Key: Keyable, R\u003e(\n\tcollection: \u0026'a L,\n\tkey: Key,\n\tf: impl FnOnce(L::DataMut\u003c'a\u003e) -\u003e R,\n) -\u003e Result\u003cR, Key\u003e {\n\tunsafe {\n\t\t// safety: we have the key\n\t\tif !collection.raw_try_write() {\n\t\t\treturn Err(key);\n\t\t}\n\n\t\t// safety: we just locked this\n\t\tlet r = f(collection.data_mut());\n\n\t\t// this ensures the key is held long enough\n\t\tdrop(key);\n\n\t\t// safety: we've locked already, and aren't using the data again\n\t\tcollection.raw_unlock_write();\n\n\t\tOk(r)\n\t}\n}\n\npub fn scoped_read\u003c'a, L: RawLock + Sharable, R\u003e(\n\tcollection: \u0026'a L,\n\tkey: impl Keyable,\n\tf: impl FnOnce(L::DataRef\u003c'a\u003e) -\u003e R,\n) -\u003e R {\n\tunsafe {\n\t\t// safety: we have the key\n\t\tcollection.raw_read();\n\n\t\t// safety: we just locked this\n\t\tlet r = f(collection.data_ref());\n\n\t\t// this ensures the key is held long enough\n\t\tdrop(key);\n\n\t\t// safety: we've locked already, and aren't using the data again\n\t\tcollection.raw_unlock_read();\n\n\t\tr\n\t}\n}\n\npub fn scoped_try_read\u003c'a, L: RawLock + Sharable, Key: Keyable, R\u003e(\n\tcollection: \u0026'a L,\n\tkey: Key,\n\tf: impl FnOnce(L::DataRef\u003c'a\u003e) -\u003e R,\n) -\u003e Result\u003cR, Key\u003e {\n\tunsafe {\n\t\t// safety: we have the key\n\t\tif !collection.raw_try_read() {\n\t\t\treturn Err(key);\n\t\t}\n\n\t\t// safety: we just locked this\n\t\tlet r = f(collection.data_ref());\n\n\t\t// this ensures the key is held long enough\n\t\tdrop(key);\n\n\t\t// safety: we've locked already, and aren't using the data again\n\t\tcollection.raw_unlock_read();\n\n\t\tOk(r)\n\t}\n}\n\n/// Unlocks the already locked locks in order to recover from a panic\npub unsafe fn attempt_to_recover_writes_from_panic(locks: \u0026[\u0026dyn RawLock]) {\n\thandle_unwind(\n\t\t|| {\n\t\t\t// safety: the caller assumes that these are already locked\n\t\t\tlocks.iter().for_each(|lock| lock.raw_unlock_write());\n\t\t},\n\t\t// if we get another panic in here, we'll just have to poison what remains\n\t\t|| locks.iter().for_each(|l| l.poison()),\n\t)\n}\n\n/// Unlocks the already locked locks in order to recover from a panic\npub unsafe fn attempt_to_recover_reads_from_panic(locked: \u0026[\u0026dyn RawLock]) {\n\thandle_unwind(\n\t\t|| {\n\t\t\t// safety: the caller assumes these are already locked\n\t\t\tlocked.iter().for_each(|lock| lock.raw_unlock_read());\n\t\t},\n\t\t// if we get another panic in here, we'll just have to poison what remains\n\t\t|| locked.iter().for_each(|l| l.poison()),\n\t)\n}\n\n#[cfg(test)]\nmod tests {\n\tuse crate::collection::utils::ordered_contains_duplicates;\n\n\t#[test]\n\tfn empty_array_does_not_contain_duplicates() {\n\t\tassert!(!ordered_contains_duplicates(\u0026[]))\n\t}\n}\n","traces":[{"line":8,"address":[],"length":0,"stats":{"Line":9}},{"line":9,"address":[],"length":0,"stats":{"Line":9}},{"line":10,"address":[426081,425505,426273,425313,424929,425121,426465,425697,425889],"length":1,"stats":{"Line":9}},{"line":11,"address":[],"length":0,"stats":{"Line":27}},{"line":12,"address":[],"length":0,"stats":{"Line":9}},{"line":16,"address":[159760,159885],"length":1,"stats":{"Line":26}},{"line":17,"address":[],"length":0,"stats":{"Line":26}},{"line":18,"address":[],"length":0,"stats":{"Line":26}},{"line":19,"address":[],"length":0,"stats":{"Line":26}},{"line":24,"address":[538336],"length":1,"stats":{"Line":8}},{"line":25,"address":[453224],"length":1,"stats":{"Line":8}},{"line":27,"address":[532806],"length":1,"stats":{"Line":1}},{"line":30,"address":[535115],"length":1,"stats":{"Line":8}},{"line":32,"address":[526816,526845,526963],"length":1,"stats":{"Line":24}},{"line":36,"address":[532400],"length":1,"stats":{"Line":4}},{"line":38,"address":[517230],"length":1,"stats":{"Line":4}},{"line":41,"address":[518608],"length":1,"stats":{"Line":4}},{"line":42,"address":[429550,429623],"length":1,"stats":{"Line":8}},{"line":43,"address":[527107],"length":1,"stats":{"Line":4}},{"line":44,"address":[557021],"length":1,"stats":{"Line":4}},{"line":47,"address":[555277],"length":1,"stats":{"Line":13}},{"line":52,"address":[535280],"length":1,"stats":{"Line":2}},{"line":53,"address":[547230],"length":1,"stats":{"Line":2}},{"line":56,"address":[429920],"length":1,"stats":{"Line":2}},{"line":57,"address":[429934,430007],"length":1,"stats":{"Line":4}},{"line":58,"address":[430017],"length":1,"stats":{"Line":2}},{"line":59,"address":[543133],"length":1,"stats":{"Line":2}},{"line":62,"address":[543216,543397,543223],"length":1,"stats":{"Line":5}},{"line":69,"address":[547312],"length":1,"stats":{"Line":2}},{"line":70,"address":[509031],"length":1,"stats":{"Line":2}},{"line":73,"address":[532639],"length":1,"stats":{"Line":6}},{"line":74,"address":[543581,543439],"length":1,"stats":{"Line":6}},{"line":76,"address":[557906],"length":1,"stats":{"Line":3}},{"line":77,"address":[549632,549498],"length":1,"stats":{"Line":4}},{"line":79,"address":[543485,543230,543421,543546],"length":1,"stats":{"Line":4}},{"line":81,"address":[566410],"length":1,"stats":{"Line":1}},{"line":83,"address":[543968],"length":1,"stats":{"Line":1}},{"line":87,"address":[543142],"length":1,"stats":{"Line":1}},{"line":89,"address":[555507],"length":1,"stats":{"Line":4}},{"line":91,"address":[528439,528613],"length":1,"stats":{"Line":2}},{"line":97,"address":[517520],"length":1,"stats":{"Line":2}},{"line":99,"address":[532727],"length":1,"stats":{"Line":2}},{"line":102,"address":[536847],"length":1,"stats":{"Line":6}},{"line":103,"address":[550029,549887],"length":1,"stats":{"Line":8}},{"line":105,"address":[520434],"length":1,"stats":{"Line":4}},{"line":106,"address":[548378,548512],"length":1,"stats":{"Line":6}},{"line":108,"address":[559037,559098,558973,558782],"length":1,"stats":{"Line":8}},{"line":110,"address":[550442],"length":1,"stats":{"Line":1}},{"line":112,"address":[550416],"length":1,"stats":{"Line":2}},{"line":116,"address":[548070],"length":1,"stats":{"Line":1}},{"line":118,"address":[538819],"length":1,"stats":{"Line":3}},{"line":120,"address":[545061,544887],"length":1,"stats":{"Line":2}},{"line":124,"address":[140016,140244],"length":1,"stats":{"Line":27}},{"line":131,"address":[183047],"length":1,"stats":{"Line":27}},{"line":134,"address":[183258,183115],"length":1,"stats":{"Line":27}},{"line":137,"address":[],"length":0,"stats":{"Line":27}},{"line":140,"address":[140216],"length":1,"stats":{"Line":27}},{"line":142,"address":[],"length":0,"stats":{"Line":0}},{"line":146,"address":[438920,438630,438942,439816,439519,440134,439246,439224,440160,438960,439838,440424,440446,438352,439856,439552,438656,439264,439541],"length":1,"stats":{"Line":38}},{"line":153,"address":[],"length":0,"stats":{"Line":76}},{"line":154,"address":[215483,213963,214571,214875,214267,215179],"length":1,"stats":{"Line":20}},{"line":158,"address":[188795,187883,187686,187852,188294,188460,187548,188764,188902,189068,188187,187579,187990,188491,188156,189206,189099,188598],"length":1,"stats":{"Line":18}},{"line":161,"address":[224542,225150,224238,224846,225454,225758],"length":1,"stats":{"Line":18}},{"line":164,"address":[179700,180612,180004,179396,180308,180916],"length":1,"stats":{"Line":18}},{"line":166,"address":[224891,224283,225803,224587,225195,225499],"length":1,"stats":{"Line":18}},{"line":170,"address":[442544,440464,443059,442272,441232,441989,441491,441219,440723,442032,442800,442260,440964,442011,440976,441776,440736,442531,442787,441504,441763],"length":1,"stats":{"Line":10}},{"line":177,"address":[442824,441256,440488,442568,440760,441000,441790,442296,441528,442056],"length":1,"stats":{"Line":10}},{"line":180,"address":[442893,441859,442125,442365,440829,442244,441475,441203,441069,442637,440948,441747,441325,443043,440557,441973,442515,441597,442771,440707],"length":1,"stats":{"Line":10}},{"line":183,"address":[441926,442197,442461,442989,441153,442721,441421,441693,440653,440901],"length":1,"stats":{"Line":10}},{"line":186,"address":[],"length":0,"stats":{"Line":10}},{"line":188,"address":[],"length":0,"stats":{"Line":0}},{"line":192,"address":[228598,228016,229536,229232,229814,228294,229206,229510,228320,228624,228928,228902],"length":1,"stats":{"Line":12}},{"line":199,"address":[],"length":0,"stats":{"Line":24}},{"line":200,"address":[229035,229339,228123,228427,229643,228731],"length":1,"stats":{"Line":7}},{"line":204,"address":[228582,229798,228779,229387,229190,229083,228475,229494,229660,229356,229052,228278,228444,228171,228748,228140,228886,229691],"length":1,"stats":{"Line":5}},{"line":207,"address":[],"length":0,"stats":{"Line":5}},{"line":210,"address":[228868,229172,229780,229476,228564,228260],"length":1,"stats":{"Line":5}},{"line":212,"address":[228875,228571,228267,229787,229179,229483],"length":1,"stats":{"Line":5}},{"line":217,"address":[538864],"length":1,"stats":{"Line":6}},{"line":219,"address":[548752],"length":1,"stats":{"Line":9}},{"line":221,"address":[548766,548814,548800],"length":1,"stats":{"Line":20}},{"line":224,"address":[544798,544750,544736,544784],"length":1,"stats":{"Line":4}},{"line":229,"address":[517680],"length":1,"stats":{"Line":4}},{"line":231,"address":[548912],"length":1,"stats":{"Line":5}},{"line":233,"address":[545296,545310,545262],"length":1,"stats":{"Line":15}},{"line":236,"address":[549054,548992,549040,549006],"length":1,"stats":{"Line":4}}],"covered":84,"coverable":86},{"path":["/","home","botahamec","Projects","happylock","src","collection.rs"],"content":"use std::cell::UnsafeCell;\n\nuse crate::{lockable::RawLock, ThreadKey};\n\nmod boxed;\nmod guard;\nmod owned;\nmod r#ref;\nmod retry;\npub(crate) mod utils;\n\n/// Locks a collection of locks, which cannot be shared immutably.\n///\n/// This could be a tuple of [`Lockable`] types, an array, or a `Vec`. But it\n/// can be safely locked without causing a deadlock.\n///\n/// The data in this collection is guaranteed to not contain duplicates because\n/// `L` must always implement [`OwnedLockable`]. The underlying data may not be\n/// immutably referenced and locked. Because of this, there is no need for\n/// sorting the locks in the collection, or checking for duplicates, because it\n/// can be guaranteed that until the underlying collection is mutated (which\n/// requires releasing all acquired locks in the collection to do), then the\n/// locks will stay in the same order and be locked in that order, preventing\n/// cyclic wait.\n///\n/// [`Lockable`]: `crate::lockable::Lockable`\n/// [`OwnedLockable`]: `crate::lockable::OwnedLockable`\n\n// this type caches the idea that no immutable references to the underlying\n// collection exist\n#[derive(Debug)]\npub struct OwnedLockCollection\u003cL\u003e {\n\tdata: L,\n}\n\n/// Locks a reference to a collection of locks, by sorting them by memory\n/// address.\n///\n/// This could be a tuple of [`Lockable`] types, an array, or a `Vec`. But it\n/// can be safely locked without causing a deadlock.\n///\n/// Upon construction, it must be confirmed that the collection contains no\n/// duplicate locks. This can be done by either using [`OwnedLockable`] or by\n/// checking. Regardless of how this is done, the locks will be sorted by their\n/// memory address before locking them. The sorted order of the locks is stored\n/// within this collection.\n///\n/// Unlike [`BoxedLockCollection`], this type does not allocate memory for the\n/// data, although it does allocate memory for the sorted list of lock\n/// references. This makes it slightly faster, but lifetimes must be handled.\n///\n/// [`Lockable`]: `crate::lockable::Lockable`\n/// [`OwnedLockable`]: `crate::lockable::OwnedLockable`\n//\n// This type was born when I eventually realized that I needed a self\n// referential structure. That used boxing, so I elected to make a more\n// efficient implementation (polonius please save us)\n//\n// This type caches the sorting order of the locks and the fact that it doesn't\n// contain any duplicates.\npub struct RefLockCollection\u003c'a, L\u003e {\n\tdata: \u0026'a L,\n\tlocks: Vec\u003c\u0026'a dyn RawLock\u003e,\n}\n\n/// Locks a collection of locks, stored in the heap, by sorting them by memory\n/// address.\n///\n/// This could be a tuple of [`Lockable`] types, an array, or a `Vec`. But it\n/// can be safely locked without causing a deadlock.\n///\n/// Upon construction, it must be confirmed that the collection contains no\n/// duplicate locks. This can be done by either using [`OwnedLockable`] or by\n/// checking. Regardless of how this is done, the locks will be sorted by their\n/// memory address before locking them. The sorted order of the locks is stored\n/// within this collection.\n///\n/// Unlike [`RefLockCollection`], this is a self-referential type which boxes\n/// the data that is given to it. This means no lifetimes are necessary on the\n/// type itself, but it is slightly slower because of the memory allocation.\n///\n/// [`Lockable`]: `crate::lockable::Lockable`\n/// [`OwnedLockable`]: `crate::lockable::OwnedLockable`\n//\n// This type caches the sorting order of the locks and the fact that it doesn't\n// contain any duplicates.\npub struct BoxedLockCollection\u003cL\u003e {\n\tdata: *const UnsafeCell\u003cL\u003e,\n\tlocks: Vec\u003c\u0026'static dyn RawLock\u003e,\n}\n\n/// Locks a collection of locks using a retrying algorithm.\n///\n/// This could be a tuple of [`Lockable`] types, an array, or a `Vec`. But it\n/// can be safely locked without causing a deadlock.\n///\n/// The data in this collection is guaranteed to not contain duplicates, but it\n/// also not be sorted. In some cases the lack of sorting can increase\n/// performance. However, in most cases, this collection will be slower. Cyclic\n/// wait is not guaranteed here, so the locking algorithm must release all its\n/// locks if one of the lock attempts blocks. This results in wasted time and\n/// potential [livelocking].\n///\n/// However, one case where this might be faster than [`RefLockCollection`] is\n/// when the first lock in the collection is always the first in any\n/// collection, and the other locks in the collection are always locked after\n/// that first lock is acquired. This means that as soon as it is locked, there\n/// will be no need to unlock it later on subsequent lock attempts, because\n/// they will always succeed.\n///\n/// [`Lockable`]: `crate::lockable::Lockable`\n/// [`OwnedLockable`]: `crate::lockable::OwnedLockable`\n/// [livelocking]: https://en.wikipedia.org/wiki/Deadlock#Livelock\n//\n// This type caches the fact that there are no duplicates\n#[derive(Debug)]\npub struct RetryingLockCollection\u003cL\u003e {\n\tdata: L,\n}\n\n/// A RAII guard for a generic [`Lockable`] type.\n///\n/// [`Lockable`]: `crate::lockable::Lockable`\npub struct LockGuard\u003cGuard\u003e {\n\tguard: Guard,\n\tkey: ThreadKey,\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","src","handle_unwind.rs"],"content":"use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};\n\n/// Runs `try_fn`. If it unwinds, it will run `catch` and then continue\n/// unwinding. This is used instead of `scopeguard` to ensure the `catch`\n/// function doesn't run if the thread is already panicking. The unwind\n/// must specifically be caused by the `try_fn`\npub fn handle_unwind\u003cR, F: FnOnce() -\u003e R, G: FnOnce()\u003e(try_fn: F, catch: G) -\u003e R {\n\tlet try_fn = AssertUnwindSafe(try_fn);\n\tcatch_unwind(try_fn).unwrap_or_else(|e| {\n\t\tcatch();\n\t\tresume_unwind(e)\n\t})\n}\n","traces":[{"line":7,"address":[509474,510326,510176,510000,509664,509840,510152,509488,509638,509986,509328,509816],"length":1,"stats":{"Line":157}},{"line":8,"address":[207922,208594,208201,208338,207753,208466,208057],"length":1,"stats":{"Line":126}},{"line":9,"address":[208353,208077,208936,209449,208832,209091,209104,208481,209347,208819,207993,208258,208976,208221,209219,208665,208960,208409,208537,208793,209488,209577,209475,207842,208114,209360,209232,208609,207937,209193,209065,208704,207785,209603,209321],"length":1,"stats":{"Line":312}},{"line":10,"address":[540567,540285,540439,540685,539997,540141],"length":1,"stats":{"Line":29}},{"line":11,"address":[549421,549281,549021,548877,548733,549153],"length":1,"stats":{"Line":25}}],"covered":5,"coverable":5},{"path":["/","home","botahamec","Projects","happylock","src","key.rs"],"content":"use std::cell::{Cell, LazyCell};\nuse std::fmt::{self, Debug};\nuse std::marker::PhantomData;\n\nuse sealed::Sealed;\n\n// Sealed to prevent other key types from being implemented. Otherwise, this\n// would almost instant undefined behavior.\nmod sealed {\n\tuse super::ThreadKey;\n\n\tpub trait Sealed {}\n\timpl Sealed for ThreadKey {}\n\timpl Sealed for \u0026mut ThreadKey {}\n}\n\nthread_local! {\n\tstatic KEY: LazyCell\u003cKeyCell\u003e = LazyCell::new(KeyCell::default);\n}\n\n/// The key for the current thread.\n///\n/// Only one of these exist per thread. To get the current thread's key, call\n/// [`ThreadKey::get`]. If the `ThreadKey` is dropped, it can be re-obtained.\npub struct ThreadKey {\n\tphantom: PhantomData\u003c*const ()\u003e, // implement !Send and !Sync\n}\n\n/// Allows the type to be used as a key for a lock\n///\n/// # Safety\n///\n/// Only one value which implements this trait may be allowed to exist at a\n/// time. Creating a new `Keyable` value requires making any other `Keyable`\n/// values invalid.\npub unsafe trait Keyable: Sealed {}\nunsafe impl Keyable for ThreadKey {}\n// the ThreadKey can't be moved while a mutable reference to it exists\nunsafe impl Keyable for \u0026mut ThreadKey {}\n\n// Implementing this means we can allow `MutexGuard` to be Sync\n// Safety: a \u0026ThreadKey is useless by design.\nunsafe impl Sync for ThreadKey {}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl Debug for ThreadKey {\n\tfn fmt(\u0026self, f: \u0026mut fmt::Formatter\u003c'_\u003e) -\u003e fmt::Result {\n\t\twrite!(f, \"ThreadKey\")\n\t}\n}\n\n// If you lose the thread key, you can get it back by calling ThreadKey::get\nimpl Drop for ThreadKey {\n\tfn drop(\u0026mut self) {\n\t\t// safety: a thread key cannot be acquired without creating the lock\n\t\t// safety: the key is lost, so it's safe to unlock the cell\n\t\tunsafe { KEY.with(|key| key.force_unlock()) }\n\t}\n}\n\nimpl ThreadKey {\n\t/// Get the current thread's `ThreadKey`, if it's not already taken.\n\t///\n\t/// The first time this is called, it will successfully return a\n\t/// `ThreadKey`. However, future calls to this function on the same thread\n\t/// will return [`None`], unless the key is dropped or unlocked first.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::ThreadKey;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// ```\n\t#[must_use]\n\tpub fn get() -\u003e Option\u003cSelf\u003e {\n\t\t// safety: we just acquired the lock\n\t\t// safety: if this code changes, check to ensure the requirement for\n\t\t// the Drop implementation is still true\n\t\tKEY.with(|key| {\n\t\t\tkey.try_lock().then_some(Self {\n\t\t\t\tphantom: PhantomData,\n\t\t\t})\n\t\t})\n\t}\n}\n\n/// A dumb lock that's just a wrapper for an [`AtomicBool`].\n#[derive(Default)]\nstruct KeyCell {\n\tis_locked: Cell\u003cbool\u003e,\n}\n\nimpl KeyCell {\n\t/// Attempt to lock the `KeyCell`. This is not a fair lock.\n\t#[must_use]\n\tpub fn try_lock(\u0026self) -\u003e bool {\n\t\t!self.is_locked.replace(true)\n\t}\n\n\t/// Forcibly unlocks the `KeyCell`. This should only be called if the key\n\t/// from this `KeyCell` has been \"lost\".\n\tpub unsafe fn force_unlock(\u0026self) {\n\t\tself.is_locked.set(false);\n\t}\n}\n\n#[cfg(test)]\nmod tests {\n\tuse super::*;\n\n\t#[test]\n\tfn thread_key_returns_some_on_first_call() {\n\t\tassert!(ThreadKey::get().is_some());\n\t}\n\n\t#[test]\n\tfn thread_key_returns_none_on_second_call() {\n\t\tlet key = ThreadKey::get();\n\t\tassert!(ThreadKey::get().is_none());\n\t\tdrop(key);\n\t}\n\n\t#[test]\n\tfn dropping_thread_key_allows_reobtaining() {\n\t\tdrop(ThreadKey::get());\n\t\tassert!(ThreadKey::get().is_some())\n\t}\n}\n","traces":[{"line":18,"address":[536408],"length":1,"stats":{"Line":21}},{"line":55,"address":[536912],"length":1,"stats":{"Line":13}},{"line":58,"address":[515685],"length":1,"stats":{"Line":39}},{"line":77,"address":[536752],"length":1,"stats":{"Line":20}},{"line":81,"address":[542880],"length":1,"stats":{"Line":40}},{"line":82,"address":[537273],"length":1,"stats":{"Line":20}},{"line":98,"address":[536352],"length":1,"stats":{"Line":19}},{"line":99,"address":[551077],"length":1,"stats":{"Line":20}},{"line":104,"address":[536384],"length":1,"stats":{"Line":12}},{"line":105,"address":[439893],"length":1,"stats":{"Line":13}}],"covered":10,"coverable":10},{"path":["/","home","botahamec","Projects","happylock","src","lib.rs"],"content":"#![warn(clippy::pedantic)]\n#![warn(clippy::nursery)]\n#![allow(clippy::module_name_repetitions)]\n#![allow(clippy::declare_interior_mutable_const)]\n#![allow(clippy::semicolon_if_nothing_returned)]\n#![allow(clippy::module_inception)]\n#![allow(clippy::single_match_else)]\n\n//! As it turns out, the Rust borrow checker is powerful enough that, if the\n//! standard library supported it, we could've made deadlocks undefined\n//! behavior. This library currently serves as a proof of concept for how that\n//! would work.\n//!\n//! # Theory\n//!\n//! There are four conditions necessary for a deadlock to occur. In order to\n//! prevent deadlocks, we just need to prevent one of the following:\n//!\n//! 1. mutual exclusion\n//! 2. non-preemptive allocation\n//! 3. circular wait\n//! 4. **partial allocation**\n//!\n//! This library seeks to solve **partial allocation** by requiring total\n//! allocation. All the resources a thread needs must be allocated at the same\n//! time. In order to request new resources, the old resources must be dropped\n//! first. Requesting multiple resources at once is atomic. You either get all\n//! the requested resources or none at all.\n//!\n//! As an optimization, this library also often prevents **circular wait**.\n//! Many collections sort the locks in order of their memory address. As long\n//! as the locks are always acquired in that order, then time doesn't need to\n//! be wasted on releasing locks after a failure and re-acquiring them later.\n//!\n//! # Examples\n//!\n//! Simple example:\n//! ```\n//! use std::thread;\n//! use happylock::{Mutex, ThreadKey};\n//!\n//! const N: usize = 10;\n//!\n//! static DATA: Mutex\u003ci32\u003e = Mutex::new(0);\n//!\n//! for _ in 0..N {\n//! thread::spawn(move || {\n//! // each thread gets one thread key\n//! let key = ThreadKey::get().unwrap();\n//!\n//! // unlocking a mutex requires a ThreadKey\n//! let mut data = DATA.lock(key);\n//! *data += 1;\n//!\n//! // the key is unlocked at the end of the scope\n//! });\n//! }\n//!\n//! let key = ThreadKey::get().unwrap();\n//! let data = DATA.lock(key);\n//! println!(\"{}\", *data);\n//! ```\n//!\n//! To lock multiple mutexes at a time, create a [`LockCollection`]:\n//!\n//! ```\n//! use std::thread;\n//! use happylock::{LockCollection, Mutex, ThreadKey};\n//!\n//! const N: usize = 10;\n//!\n//! static DATA_1: Mutex\u003ci32\u003e = Mutex::new(0);\n//! static DATA_2: Mutex\u003cString\u003e = Mutex::new(String::new());\n//!\n//! for _ in 0..N {\n//! thread::spawn(move || {\n//! let key = ThreadKey::get().unwrap();\n//!\n//! // happylock ensures at runtime there are no duplicate locks\n//! let collection = LockCollection::try_new((\u0026DATA_1, \u0026DATA_2)).unwrap();\n//! let mut guard = collection.lock(key);\n//!\n//! *guard.1 = (100 - *guard.0).to_string();\n//! *guard.0 += 1;\n//! });\n//! }\n//!\n//! let key = ThreadKey::get().unwrap();\n//! let data = LockCollection::try_new((\u0026DATA_1, \u0026DATA_2)).unwrap();\n//! let data = data.lock(key);\n//! println!(\"{}\", *data.0);\n//! println!(\"{}\", *data.1);\n//! ```\n//!\n//! In many cases, the [`LockCollection::new`] or [`LockCollection::new_ref`]\n//! method can be used, improving performance.\n//!\n//! ```rust\n//! use std::thread;\n//! use happylock::{LockCollection, Mutex, ThreadKey};\n//!\n//! const N: usize = 32;\n//!\n//! static DATA: [Mutex\u003ci32\u003e; 2] = [Mutex::new(0), Mutex::new(1)];\n//!\n//! for _ in 0..N {\n//! thread::spawn(move || {\n//! let key = ThreadKey::get().unwrap();\n//!\n//! // a reference to a type that implements `OwnedLockable` will never\n//! // contain duplicates, so no duplicate checking is needed.\n//! let collection = LockCollection::new_ref(\u0026DATA);\n//! let mut guard = collection.lock(key);\n//!\n//! let x = *guard[1];\n//! *guard[1] += *guard[0];\n//! *guard[0] = x;\n//! });\n//! }\n//!\n//! let key = ThreadKey::get().unwrap();\n//! let data = LockCollection::new_ref(\u0026DATA);\n//! let data = data.lock(key);\n//! println!(\"{}\", data[0]);\n//! println!(\"{}\", data[1]);\n//! ```\n//!\n//! # Performance\n//!\n//! **The `ThreadKey` is a mostly-zero cost abstraction.** It doesn't use any\n//! memory, and it doesn't really exist at run-time. The only cost comes from\n//! calling `ThreadKey::get()`, because the function has to ensure at runtime\n//! that the key hasn't already been taken. Dropping the key will also have a\n//! small cost.\n//!\n//! **Consider [`OwnedLockCollection`].** This will almost always be the\n//! fastest lock collection. It doesn't expose the underlying collection\n//! immutably, which means that it will always be locked in the same order, and\n//! doesn't need any sorting.\n//!\n//! **Avoid [`LockCollection::try_new`].** This constructor will check to make\n//! sure that the collection contains no duplicate locks. In most cases, this\n//! is O(nlogn), where n is the number of locks in the collections but in the\n//! case of [`RetryingLockCollection`], it's close to O(n).\n//! [`LockCollection::new`] and [`LockCollection::new_ref`] don't need these\n//! checks because they use [`OwnedLockable`], which is guaranteed to be unique\n//! as long as it is accessible. As a last resort,\n//! [`LockCollection::new_unchecked`] doesn't do this check, but is unsafe to\n//! call.\n//!\n//! **Know how to use [`RetryingLockCollection`].** This collection doesn't do\n//! any sorting, but uses a wasteful lock algorithm. It can't rely on the order\n//! of the locks to be the same across threads, so if it finds a lock that it\n//! can't acquire without blocking, it'll first release all of the locks it\n//! already acquired to avoid blocking other threads. This is wasteful because\n//! this algorithm may end up re-acquiring the same lock multiple times. To\n//! avoid this, ensure that (1) the first lock in the collection is always the\n//! first lock in any collection it appears in, and (2) the other locks in the\n//! collection are always preceded by that first lock. This will prevent any\n//! wasted time from re-acquiring locks. If you're unsure, [`LockCollection`]\n//! is a sensible default.\n//!\n//! [`OwnedLockable`]: `lockable::OwnedLockable`\n//! [`OwnedLockCollection`]: `collection::OwnedLockCollection`\n//! [`RetryingLockCollection`]: `collection::RetryingLockCollection`\n\nmod handle_unwind;\nmod key;\n\npub mod collection;\npub mod lockable;\npub mod mutex;\npub mod poisonable;\npub mod rwlock;\n\npub use key::{Keyable, ThreadKey};\n\n#[cfg(feature = \"spin\")]\npub use mutex::SpinLock;\n\n// Personally, I think re-exports look ugly in the rust documentation, so I\n// went with type aliases instead.\n\n/// A collection of locks that can be acquired simultaneously.\n///\n/// This re-exports [`BoxedLockCollection`] as a sensible default.\n///\n/// [`BoxedLockCollection`]: collection::BoxedLockCollection\npub type LockCollection\u003cL\u003e = collection::BoxedLockCollection\u003cL\u003e;\n\n/// A re-export for [`poisonable::Poisonable`]\npub type Poisonable\u003cL\u003e = poisonable::Poisonable\u003cL\u003e;\n\n/// A mutual exclusion primitive useful for protecting shared data, which cannot deadlock.\n///\n/// By default, this uses `parking_lot` as a backend.\n#[cfg(feature = \"parking_lot\")]\npub type Mutex\u003cT\u003e = mutex::Mutex\u003cT, parking_lot::RawMutex\u003e;\n\n/// A reader-writer lock\n///\n/// By default, this uses `parking_lot` as a backend.\n#[cfg(feature = \"parking_lot\")]\npub type RwLock\u003cT\u003e = rwlock::RwLock\u003cT, parking_lot::RawRwLock\u003e;\n","traces":[{"line":197,"address":[423062],"length":1,"stats":{"Line":10}}],"covered":1,"coverable":1},{"path":["/","home","botahamec","Projects","happylock","src","lockable.rs"],"content":"use std::mem::MaybeUninit;\n\n/// A raw lock type that may be locked and unlocked\n///\n/// # Safety\n///\n/// A deadlock must never occur. The `unlock` method must correctly unlock the\n/// data. The `get_ptrs` method must be implemented correctly. The `Output`\n/// must be unlocked when it is dropped.\n//\n// Why not use a RawRwLock? Because that would be semantically incorrect, and I\n// don't want an INIT or GuardMarker associated item.\n// Originally, RawLock had a sister trait: RawSharableLock. I removed it\n// because it'd be difficult to implement a separate type that takes a\n// different kind of RawLock. But now the Sharable marker trait is needed to\n// indicate if reads can be used.\npub unsafe trait RawLock {\n\t/// Causes all subsequent calls to the `lock` function on this lock to\n\t/// panic. This does not affect anything currently holding the lock.\n\tfn poison(\u0026self);\n\n\t/// Blocks until the lock is acquired\n\t///\n\t/// # Safety\n\t///\n\t/// It is undefined behavior to use this without ownership or mutable\n\t/// access to the [`ThreadKey`], which should last as long as the return\n\t/// value is alive.\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\tunsafe fn raw_write(\u0026self);\n\n\t/// Attempt to lock without blocking.\n\t///\n\t/// Returns `true` if successful, `false` otherwise.\n\t///\n\t/// # Safety\n\t///\n\t/// It is undefined behavior to use this without ownership or mutable\n\t/// access to the [`ThreadKey`], which should last as long as the return\n\t/// value is alive.\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool;\n\n\t/// Releases the lock\n\t///\n\t/// # Safety\n\t///\n\t/// It is undefined behavior to use this if the lock is not acquired\n\tunsafe fn raw_unlock_write(\u0026self);\n\n\t/// Blocks until the data the lock protects can be safely read.\n\t///\n\t/// Some locks, but not all, will allow multiple readers at once. If\n\t/// multiple readers are allowed for a [`Lockable`] type, then the\n\t/// [`Sharable`] marker trait should be implemented.\n\t///\n\t/// # Safety\n\t///\n\t/// It is undefined behavior to use this without ownership or mutable\n\t/// access to the [`ThreadKey`], which should last as long as the return\n\t/// value is alive.\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\tunsafe fn raw_read(\u0026self);\n\n\t// Attempt to read without blocking.\n\t///\n\t/// Returns `true` if successful, `false` otherwise.\n\t///\n\t/// Some locks, but not all, will allow multiple readers at once. If\n\t/// multiple readers are allowed for a [`Lockable`] type, then the\n\t/// [`Sharable`] marker trait should be implemented.\n\t///\n\t/// # Safety\n\t///\n\t/// It is undefined behavior to use this without ownership or mutable\n\t/// access to the [`ThreadKey`], which should last as long as the return\n\t/// value is alive.\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool;\n\n\t/// Releases the lock after calling `read`.\n\t///\n\t/// # Safety\n\t///\n\t/// It is undefined behavior to use this if the read lock is not acquired\n\tunsafe fn raw_unlock_read(\u0026self);\n}\n\n/// A type that may be locked and unlocked.\n///\n/// This trait is usually implemented on collections of [`RawLock`]s. For\n/// example, a `Vec\u003cMutex\u003ci32\u003e\u003e`.\n///\n/// # Safety\n///\n/// Acquiring the locks returned by `get_ptrs` must allow access to the values\n/// returned by `guard`.\n///\n/// Dropping the `Guard` must unlock those same locks.\n///\n/// The order of the resulting list from `get_ptrs` must be deterministic. As\n/// long as the value is not mutated, the references must always be in the same\n/// order.\npub unsafe trait Lockable {\n\t/// The exclusive guard that does not hold a key\n\ttype Guard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\t/// Yields a list of references to the [`RawLock`]s contained within this\n\t/// value.\n\t///\n\t/// These reference locks which must be locked before acquiring a guard,\n\t/// and unlocked when the guard is dropped. The order of the resulting list\n\t/// is deterministic. As long as the value is not mutated, the references\n\t/// will always be in the same order.\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e);\n\n\t/// Returns a guard that can be used to access the underlying data mutably.\n\t///\n\t/// # Safety\n\t///\n\t/// All locks given by calling [`Lockable::get_ptrs`] must be locked\n\t/// exclusively before calling this function. The locks must not be\n\t/// unlocked until this guard is dropped.\n\t#[must_use]\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e;\n\n\t#[must_use]\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e;\n}\n\n/// Allows a lock to be accessed by multiple readers.\n///\n/// # Safety\n///\n/// Acquiring shared access to the locks returned by `get_ptrs` must allow\n/// shared access to the values returned by `read_guard`.\n///\n/// Dropping the `ReadGuard` must unlock those same locks.\npub unsafe trait Sharable: Lockable {\n\t/// The shared guard type that does not hold a key\n\ttype ReadGuard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\t/// Returns a guard that can be used to immutably access the underlying\n\t/// data.\n\t///\n\t/// # Safety\n\t///\n\t/// All locks given by calling [`Lockable::get_ptrs`] must be locked using\n\t/// [`RawLock::raw_read`] before calling this function. The locks must not be\n\t/// unlocked until this guard is dropped.\n\t#[must_use]\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e;\n\n\t#[must_use]\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e;\n}\n\n/// A type that may be locked and unlocked, and is known to be the only valid\n/// instance of the lock.\n///\n/// # Safety\n///\n/// There must not be any two values which can unlock the value at the same\n/// time, i.e., this must either be an owned value or a mutable reference.\npub unsafe trait OwnedLockable: Lockable {}\n\n/// A trait which indicates that `into_inner` is a valid operation for a\n/// [`Lockable`].\n///\n/// This is used for types like [`Poisonable`] to access the inner value of a\n/// lock. [`Poisonable::into_inner`] calls [`LockableIntoInner::into_inner`] to\n/// return a mutable reference of the inner value. This isn't implemented for\n/// some `Lockable`s, such as `\u0026[T]`.\n///\n/// [`Poisonable`]: `crate::Poisonable`\n/// [`Poisonable::into_inner`]: `crate::poisonable::Poisonable::into_inner`\npub trait LockableIntoInner: Lockable {\n\t/// The inner type that is behind the lock\n\ttype Inner;\n\n\t/// Consumes the lock, returning the underlying the lock.\n\tfn into_inner(self) -\u003e Self::Inner;\n}\n\n/// A trait which indicates that `as_mut` is a valid operation for a\n/// [`Lockable`].\n///\n/// This is used for types like [`Poisonable`] to access the inner value of a\n/// lock. [`Poisonable::get_mut`] calls [`LockableGetMut::get_mut`] to return a\n/// mutable reference of the inner value. This isn't implemented for some\n/// `Lockable`s, such as `\u0026[T]`.\n///\n/// [`Poisonable`]: `crate::Poisonable`\n/// [`Poisonable::get_mut`]: `crate::poisonable::Poisonable::get_mut`\npub trait LockableGetMut: Lockable {\n\t/// The inner type that is behind the lock\n\ttype Inner\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\t/// Returns a mutable reference to the underlying data.\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e;\n}\n\nunsafe impl\u003cT: Lockable\u003e Lockable for \u0026T {\n\ttype Guard\u003c'g\u003e\n\t\t= T::Guard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= T::DataMut\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\t(*self).get_ptrs(ptrs);\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\t(*self).guard()\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\t(*self).data_mut()\n\t}\n}\n\nunsafe impl\u003cT: Sharable\u003e Sharable for \u0026T {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= T::ReadGuard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= T::DataRef\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\t(*self).read_guard()\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\t(*self).data_ref()\n\t}\n}\n\nunsafe impl\u003cT: Lockable\u003e Lockable for \u0026mut T {\n\ttype Guard\u003c'g\u003e\n\t\t= T::Guard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= T::DataMut\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\t(**self).get_ptrs(ptrs)\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\t(**self).guard()\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\t(**self).data_mut()\n\t}\n}\n\nimpl\u003cT: LockableGetMut\u003e LockableGetMut for \u0026mut T {\n\ttype Inner\u003c'a\u003e\n\t\t= T::Inner\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\t(*self).get_mut()\n\t}\n}\n\nunsafe impl\u003cT: Sharable\u003e Sharable for \u0026mut T {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= T::ReadGuard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= T::DataRef\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\t(**self).read_guard()\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\t(**self).data_ref()\n\t}\n}\n\nunsafe impl\u003cT: OwnedLockable\u003e OwnedLockable for \u0026mut T {}\n\n/// Implements `Lockable`, `Sharable`, and `OwnedLockable` for tuples\n/// ex: `tuple_impls!(A B C, 0 1 2);`\nmacro_rules! tuple_impls {\n\t($($generic:ident)*, $($value:tt)*) =\u003e {\n\t\tunsafe impl\u003c$($generic: Lockable,)*\u003e Lockable for ($($generic,)*) {\n\t\t\ttype Guard\u003c'g\u003e = ($($generic::Guard\u003c'g\u003e,)*) where Self: 'g;\n\n\t\t\ttype DataMut\u003c'a\u003e = ($($generic::DataMut\u003c'a\u003e,)*) where Self: 'a;\n\n\t\t\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\t\t\t$(self.$value.get_ptrs(ptrs));*\n\t\t\t}\n\n\t\t\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\t\t\t// It's weird that this works\n\t\t\t\t// I don't think any other way of doing it compiles\n\t\t\t\t($(self.$value.guard(),)*)\n\t\t\t}\n\n\t\t\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\t\t\t($(self.$value.data_mut(),)*)\n\t\t\t}\n\t\t}\n\n\t\timpl\u003c$($generic: LockableGetMut,)*\u003e LockableGetMut for ($($generic,)*) {\n\t\t\ttype Inner\u003c'a\u003e = ($($generic::Inner\u003c'a\u003e,)*) where Self: 'a;\n\n\t\t\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\t\t\t($(self.$value.get_mut(),)*)\n\t\t\t}\n\t\t}\n\n\t\timpl\u003c$($generic: LockableIntoInner,)*\u003e LockableIntoInner for ($($generic,)*) {\n\t\t\ttype Inner = ($($generic::Inner,)*);\n\n\t\t\tfn into_inner(self) -\u003e Self::Inner {\n\t\t\t\t($(self.$value.into_inner(),)*)\n\t\t\t}\n\t\t}\n\n\t\tunsafe impl\u003c$($generic: Sharable,)*\u003e Sharable for ($($generic,)*) {\n\t\t\ttype ReadGuard\u003c'g\u003e = ($($generic::ReadGuard\u003c'g\u003e,)*) where Self: 'g;\n\n\t\t\ttype DataRef\u003c'a\u003e = ($($generic::DataRef\u003c'a\u003e,)*) where Self: 'a;\n\n\t\t\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\t\t\t($(self.$value.read_guard(),)*)\n\t\t\t}\n\n\t\t\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\t\t\t($(self.$value.data_ref(),)*)\n\t\t\t}\n\t\t}\n\n\t\tunsafe impl\u003c$($generic: OwnedLockable,)*\u003e OwnedLockable for ($($generic,)*) {}\n\t};\n}\n\ntuple_impls!(A, 0);\ntuple_impls!(A B, 0 1);\ntuple_impls!(A B C, 0 1 2);\ntuple_impls!(A B C D, 0 1 2 3);\ntuple_impls!(A B C D E, 0 1 2 3 4);\ntuple_impls!(A B C D E F, 0 1 2 3 4 5);\ntuple_impls!(A B C D E F G, 0 1 2 3 4 5 6);\n\nunsafe impl\u003cT: Lockable, const N: usize\u003e Lockable for [T; N] {\n\ttype Guard\u003c'g\u003e\n\t\t= [T::Guard\u003c'g\u003e; N]\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= [T::DataMut\u003c'a\u003e; N]\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tfor lock in self {\n\t\t\tlock.get_ptrs(ptrs);\n\t\t}\n\t}\n\n\tunsafe fn guard\u003c'g\u003e(\u0026'g self) -\u003e Self::Guard\u003c'g\u003e {\n\t\t// The MaybeInit helper functions for arrays aren't stable yet, so\n\t\t// we'll just have to implement it ourselves\n\t\tlet mut guards = MaybeUninit::\u003c[MaybeUninit\u003cT::Guard\u003c'g\u003e\u003e; N]\u003e::uninit().assume_init();\n\t\tfor i in 0..N {\n\t\t\tguards[i].write(self[i].guard());\n\t\t}\n\n\t\tguards.map(|g| g.assume_init())\n\t}\n\n\tunsafe fn data_mut\u003c'a\u003e(\u0026'a self) -\u003e Self::DataMut\u003c'a\u003e {\n\t\tlet mut guards = MaybeUninit::\u003c[MaybeUninit\u003cT::DataMut\u003c'a\u003e\u003e; N]\u003e::uninit().assume_init();\n\t\tfor i in 0..N {\n\t\t\tguards[i].write(self[i].data_mut());\n\t\t}\n\n\t\tguards.map(|g| g.assume_init())\n\t}\n}\n\nimpl\u003cT: LockableGetMut, const N: usize\u003e LockableGetMut for [T; N] {\n\ttype Inner\u003c'a\u003e\n\t\t= [T::Inner\u003c'a\u003e; N]\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tunsafe {\n\t\t\tlet mut guards = MaybeUninit::\u003c[MaybeUninit\u003cT::Inner\u003c'_\u003e\u003e; N]\u003e::uninit().assume_init();\n\t\t\tfor (i, lock) in self.iter_mut().enumerate() {\n\t\t\t\tguards[i].write(lock.get_mut());\n\t\t\t}\n\n\t\t\tguards.map(|g| g.assume_init())\n\t\t}\n\t}\n}\n\nimpl\u003cT: LockableIntoInner, const N: usize\u003e LockableIntoInner for [T; N] {\n\ttype Inner = [T::Inner; N];\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tunsafe {\n\t\t\tlet mut guards = MaybeUninit::\u003c[MaybeUninit\u003cT::Inner\u003e; N]\u003e::uninit().assume_init();\n\t\t\tfor (i, lock) in self.into_iter().enumerate() {\n\t\t\t\tguards[i].write(lock.into_inner());\n\t\t\t}\n\n\t\t\tguards.map(|g| g.assume_init())\n\t\t}\n\t}\n}\n\nunsafe impl\u003cT: Sharable, const N: usize\u003e Sharable for [T; N] {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= [T::ReadGuard\u003c'g\u003e; N]\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= [T::DataRef\u003c'a\u003e; N]\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard\u003c'g\u003e(\u0026'g self) -\u003e Self::ReadGuard\u003c'g\u003e {\n\t\tlet mut guards = MaybeUninit::\u003c[MaybeUninit\u003cT::ReadGuard\u003c'g\u003e\u003e; N]\u003e::uninit().assume_init();\n\t\tfor i in 0..N {\n\t\t\tguards[i].write(self[i].read_guard());\n\t\t}\n\n\t\tguards.map(|g| g.assume_init())\n\t}\n\n\tunsafe fn data_ref\u003c'a\u003e(\u0026'a self) -\u003e Self::DataRef\u003c'a\u003e {\n\t\tlet mut guards = MaybeUninit::\u003c[MaybeUninit\u003cT::DataRef\u003c'a\u003e\u003e; N]\u003e::uninit().assume_init();\n\t\tfor i in 0..N {\n\t\t\tguards[i].write(self[i].data_ref());\n\t\t}\n\n\t\tguards.map(|g| g.assume_init())\n\t}\n}\n\nunsafe impl\u003cT: OwnedLockable, const N: usize\u003e OwnedLockable for [T; N] {}\n\nunsafe impl\u003cT: Lockable\u003e Lockable for Box\u003c[T]\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= Box\u003c[T::Guard\u003c'g\u003e]\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= Box\u003c[T::DataMut\u003c'a\u003e]\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tfor lock in self {\n\t\t\tlock.get_ptrs(ptrs);\n\t\t}\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tself.iter().map(|lock| lock.guard()).collect()\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.iter().map(|lock| lock.data_mut()).collect()\n\t}\n}\n\nimpl\u003cT: LockableGetMut + 'static\u003e LockableGetMut for Box\u003c[T]\u003e {\n\ttype Inner\u003c'a\u003e\n\t\t= Box\u003c[T::Inner\u003c'a\u003e]\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tself.iter_mut().map(LockableGetMut::get_mut).collect()\n\t}\n}\n\nunsafe impl\u003cT: Sharable\u003e Sharable for Box\u003c[T]\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= Box\u003c[T::ReadGuard\u003c'g\u003e]\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= Box\u003c[T::DataRef\u003c'a\u003e]\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tself.iter().map(|lock| lock.read_guard()).collect()\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tself.iter().map(|lock| lock.data_ref()).collect()\n\t}\n}\n\nunsafe impl\u003cT: Sharable\u003e Sharable for Vec\u003cT\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= Box\u003c[T::ReadGuard\u003c'g\u003e]\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= Box\u003c[T::DataRef\u003c'a\u003e]\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tself.iter().map(|lock| lock.read_guard()).collect()\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tself.iter().map(|lock| lock.data_ref()).collect()\n\t}\n}\n\nunsafe impl\u003cT: OwnedLockable\u003e OwnedLockable for Box\u003c[T]\u003e {}\n\nunsafe impl\u003cT: Lockable\u003e Lockable for Vec\u003cT\u003e {\n\t// There's no reason why I'd ever want to extend a list of lock guards\n\ttype Guard\u003c'g\u003e\n\t\t= Box\u003c[T::Guard\u003c'g\u003e]\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= Box\u003c[T::DataMut\u003c'a\u003e]\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tfor lock in self {\n\t\t\tlock.get_ptrs(ptrs);\n\t\t}\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tself.iter().map(|lock| lock.guard()).collect()\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.iter().map(|lock| lock.data_mut()).collect()\n\t}\n}\n\n// I'd make a generic impl\u003cT: Lockable, I: IntoIterator\u003cItem=T\u003e\u003e Lockable for I\n// but I think that'd require sealing up this trait\n\n// TODO: using edition 2024, impl LockableIntoInner for Box\u003c[T]\u003e\n\nimpl\u003cT: LockableGetMut + 'static\u003e LockableGetMut for Vec\u003cT\u003e {\n\ttype Inner\u003c'a\u003e\n\t\t= Box\u003c[T::Inner\u003c'a\u003e]\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tself.iter_mut().map(LockableGetMut::get_mut).collect()\n\t}\n}\n\nimpl\u003cT: LockableIntoInner\u003e LockableIntoInner for Vec\u003cT\u003e {\n\ttype Inner = Box\u003c[T::Inner]\u003e;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tself.into_iter()\n\t\t\t.map(LockableIntoInner::into_inner)\n\t\t\t.collect()\n\t}\n}\n\nunsafe impl\u003cT: OwnedLockable\u003e OwnedLockable for Vec\u003cT\u003e {}\n\n#[cfg(test)]\nmod tests {\n\tuse super::*;\n\tuse crate::{LockCollection, Mutex, RwLock, ThreadKey};\n\n\t#[test]\n\tfn mut_ref_get_ptrs() {\n\t\tlet mut rwlock = RwLock::new(5);\n\t\tlet mutref = \u0026mut rwlock;\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tmutref.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tassert!(std::ptr::addr_eq(lock_ptrs[0], mutref));\n\t}\n\n\t#[test]\n\tfn array_get_ptrs_empty() {\n\t\tlet locks: [Mutex\u003c()\u003e; 0] = [];\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert!(lock_ptrs.is_empty());\n\t}\n\n\t#[test]\n\tfn array_get_ptrs_length_one() {\n\t\tlet locks: [Mutex\u003ci32\u003e; 1] = [Mutex::new(1)];\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[0], locks[0].raw())) }\n\t}\n\n\t#[test]\n\tfn array_get_ptrs_length_two() {\n\t\tlet locks: [Mutex\u003ci32\u003e; 2] = [Mutex::new(1), Mutex::new(2)];\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 2);\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[0], locks[0].raw())) }\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[1], locks[1].raw())) }\n\t}\n\n\t#[test]\n\tfn vec_get_ptrs_empty() {\n\t\tlet locks: Vec\u003cMutex\u003c()\u003e\u003e = Vec::new();\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert!(lock_ptrs.is_empty());\n\t}\n\n\t#[test]\n\tfn vec_get_ptrs_length_one() {\n\t\tlet locks: Vec\u003cMutex\u003ci32\u003e\u003e = vec![Mutex::new(1)];\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[0], locks[0].raw())) }\n\t}\n\n\t#[test]\n\tfn vec_get_ptrs_length_two() {\n\t\tlet locks: Vec\u003cMutex\u003ci32\u003e\u003e = vec![Mutex::new(1), Mutex::new(2)];\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 2);\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[0], locks[0].raw())) }\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[1], locks[1].raw())) }\n\t}\n\n\t#[test]\n\tfn vec_as_mut() {\n\t\tlet mut locks: Vec\u003cMutex\u003ci32\u003e\u003e = vec![Mutex::new(1), Mutex::new(2)];\n\t\tlet lock_ptrs = LockableGetMut::get_mut(\u0026mut locks);\n\n\t\tassert_eq!(lock_ptrs.len(), 2);\n\t\tassert_eq!(*lock_ptrs[0], 1);\n\t\tassert_eq!(*lock_ptrs[1], 2);\n\t}\n\n\t#[test]\n\tfn vec_into_inner() {\n\t\tlet locks: Vec\u003cMutex\u003ci32\u003e\u003e = vec![Mutex::new(1), Mutex::new(2)];\n\t\tlet lock_ptrs = LockableIntoInner::into_inner(locks);\n\n\t\tassert_eq!(lock_ptrs.len(), 2);\n\t\tassert_eq!(lock_ptrs[0], 1);\n\t\tassert_eq!(lock_ptrs[1], 2);\n\t}\n\n\t#[test]\n\tfn vec_guard_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = vec![RwLock::new(1), RwLock::new(2)];\n\t\tlet collection = LockCollection::new(locks);\n\n\t\tlet mut guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], 1);\n\t\tassert_eq!(*guard[1], 2);\n\t\t*guard[0] = 3;\n\n\t\tlet key = LockCollection::\u003cVec\u003cRwLock\u003c_\u003e\u003e\u003e::unlock(guard);\n\t\tlet guard = collection.read(key);\n\t\tassert_eq!(*guard[0], 3);\n\t\tassert_eq!(*guard[1], 2);\n\t}\n\n\t#[test]\n\tfn vec_data_mut() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = vec![Mutex::new(1), Mutex::new(2)];\n\t\tlet collection = LockCollection::new(mutexes);\n\t\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 1);\n\t\t\tassert_eq!(*guard[1], 2);\n\t\t\t*guard[0] = 3;\n\t\t});\n\n\t\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 3);\n\t\t\tassert_eq!(*guard[1], 2);\n\t\t})\n\t}\n\n\t#[test]\n\tfn vec_data_ref() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = vec![RwLock::new(1), RwLock::new(2)];\n\t\tlet collection = LockCollection::new(mutexes);\n\t\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 1);\n\t\t\tassert_eq!(*guard[1], 2);\n\t\t\t*guard[0] = 3;\n\t\t});\n\n\t\tcollection.scoped_read(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 3);\n\t\t\tassert_eq!(*guard[1], 2);\n\t\t})\n\t}\n\n\t#[test]\n\tfn box_get_ptrs_empty() {\n\t\tlet locks: Box\u003c[Mutex\u003c()\u003e]\u003e = Box::from([]);\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert!(lock_ptrs.is_empty());\n\t}\n\n\t#[test]\n\tfn box_get_ptrs_length_one() {\n\t\tlet locks: Box\u003c[Mutex\u003ci32\u003e]\u003e = vec![Mutex::new(1)].into_boxed_slice();\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[0], locks[0].raw())) }\n\t}\n\n\t#[test]\n\tfn box_get_ptrs_length_two() {\n\t\tlet locks: Box\u003c[Mutex\u003ci32\u003e]\u003e = vec![Mutex::new(1), Mutex::new(2)].into_boxed_slice();\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 2);\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[0], locks[0].raw())) }\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[1], locks[1].raw())) }\n\t}\n\n\t#[test]\n\tfn box_as_mut() {\n\t\tlet mut locks: Box\u003c[Mutex\u003ci32\u003e]\u003e = vec![Mutex::new(1), Mutex::new(2)].into_boxed_slice();\n\t\tlet lock_ptrs = LockableGetMut::get_mut(\u0026mut locks);\n\n\t\tassert_eq!(lock_ptrs.len(), 2);\n\t\tassert_eq!(*lock_ptrs[0], 1);\n\t\tassert_eq!(*lock_ptrs[1], 2);\n\t}\n\n\t#[test]\n\tfn box_guard_mut() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet x = [Mutex::new(1), Mutex::new(2)];\n\t\tlet collection: LockCollection\u003cBox\u003c[Mutex\u003c_\u003e]\u003e\u003e = LockCollection::new(Box::new(x));\n\n\t\tlet mut guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], 1);\n\t\tassert_eq!(*guard[1], 2);\n\t\t*guard[0] = 3;\n\n\t\tlet key = LockCollection::\u003cBox\u003c[Mutex\u003c_\u003e]\u003e\u003e::unlock(guard);\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], 3);\n\t\tassert_eq!(*guard[1], 2);\n\t}\n\n\t#[test]\n\tfn box_data_mut() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = vec![Mutex::new(1), Mutex::new(2)].into_boxed_slice();\n\t\tlet collection = LockCollection::new(mutexes);\n\t\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 1);\n\t\t\tassert_eq!(*guard[1], 2);\n\t\t\t*guard[0] = 3;\n\t\t});\n\n\t\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 3);\n\t\t\tassert_eq!(*guard[1], 2);\n\t\t});\n\t}\n\n\t#[test]\n\tfn box_guard_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = [RwLock::new(1), RwLock::new(2)];\n\t\tlet collection: LockCollection\u003cBox\u003c[RwLock\u003c_\u003e]\u003e\u003e = LockCollection::new(Box::new(locks));\n\n\t\tlet mut guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], 1);\n\t\tassert_eq!(*guard[1], 2);\n\t\t*guard[0] = 3;\n\n\t\tlet key = LockCollection::\u003cBox\u003c[RwLock\u003c_\u003e]\u003e\u003e::unlock(guard);\n\t\tlet guard = collection.read(key);\n\t\tassert_eq!(*guard[0], 3);\n\t\tassert_eq!(*guard[1], 2);\n\t}\n\n\t#[test]\n\tfn box_data_ref() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = vec![RwLock::new(1), RwLock::new(2)].into_boxed_slice();\n\t\tlet collection = LockCollection::new(mutexes);\n\t\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 1);\n\t\t\tassert_eq!(*guard[1], 2);\n\t\t\t*guard[0] = 3;\n\t\t});\n\n\t\tcollection.scoped_read(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 3);\n\t\t\tassert_eq!(*guard[1], 2);\n\t\t});\n\t}\n}\n","traces":[{"line":232,"address":[162784,162816,162848],"length":1,"stats":{"Line":47}},{"line":233,"address":[247374,247342,247406,247310],"length":1,"stats":{"Line":46}},{"line":236,"address":[961984],"length":1,"stats":{"Line":10}},{"line":237,"address":[174309,174325],"length":1,"stats":{"Line":10}},{"line":240,"address":[],"length":0,"stats":{"Line":2}},{"line":241,"address":[216885],"length":1,"stats":{"Line":2}},{"line":256,"address":[215536,215552],"length":1,"stats":{"Line":3}},{"line":257,"address":[962005,962069,962033],"length":1,"stats":{"Line":3}},{"line":260,"address":[],"length":0,"stats":{"Line":0}},{"line":261,"address":[],"length":0,"stats":{"Line":0}},{"line":276,"address":[],"length":0,"stats":{"Line":1}},{"line":277,"address":[],"length":0,"stats":{"Line":1}},{"line":280,"address":[],"length":0,"stats":{"Line":0}},{"line":281,"address":[],"length":0,"stats":{"Line":0}},{"line":284,"address":[],"length":0,"stats":{"Line":0}},{"line":285,"address":[],"length":0,"stats":{"Line":0}},{"line":295,"address":[],"length":0,"stats":{"Line":0}},{"line":296,"address":[],"length":0,"stats":{"Line":0}},{"line":311,"address":[],"length":0,"stats":{"Line":0}},{"line":312,"address":[],"length":0,"stats":{"Line":0}},{"line":315,"address":[],"length":0,"stats":{"Line":0}},{"line":316,"address":[],"length":0,"stats":{"Line":0}},{"line":331,"address":[228336],"length":1,"stats":{"Line":16}},{"line":332,"address":[247496],"length":1,"stats":{"Line":19}},{"line":335,"address":[162880,163071],"length":1,"stats":{"Line":9}},{"line":338,"address":[1289086,1288974,1289198],"length":1,"stats":{"Line":9}},{"line":357,"address":[972059,971712],"length":1,"stats":{"Line":4}},{"line":358,"address":[1289423,1289654,1290092,1289318,1289974,1289740],"length":1,"stats":{"Line":8}},{"line":367,"address":[971488,971376,971464,971688,971600,971576],"length":1,"stats":{"Line":2}},{"line":368,"address":[215680],"length":1,"stats":{"Line":2}},{"line":399,"address":[962112,962336,962224],"length":1,"stats":{"Line":17}},{"line":400,"address":[217794,217859,217906,217971],"length":1,"stats":{"Line":32}},{"line":401,"address":[962205,962429,962317],"length":1,"stats":{"Line":15}},{"line":405,"address":[963184,962448,962816],"length":1,"stats":{"Line":8}},{"line":408,"address":[],"length":0,"stats":{"Line":2}},{"line":409,"address":[962898,962530,963260,963000,962632,963198],"length":1,"stats":{"Line":15}},{"line":410,"address":[962650,963275,963018,962780,963148,963390],"length":1,"stats":{"Line":14}},{"line":413,"address":[],"length":0,"stats":{"Line":22}},{"line":416,"address":[963424],"length":1,"stats":{"Line":3}},{"line":417,"address":[],"length":0,"stats":{"Line":0}},{"line":418,"address":[1281944,1281842],"length":1,"stats":{"Line":6}},{"line":419,"address":[963756,963626],"length":1,"stats":{"Line":6}},{"line":422,"address":[217133,217527],"length":1,"stats":{"Line":9}},{"line":432,"address":[1282128],"length":1,"stats":{"Line":2}},{"line":434,"address":[],"length":0,"stats":{"Line":0}},{"line":435,"address":[1282218,1282410],"length":1,"stats":{"Line":4}},{"line":436,"address":[1282452,1282538],"length":1,"stats":{"Line":4}},{"line":439,"address":[605472,605495,605440,605463],"length":1,"stats":{"Line":6}},{"line":447,"address":[],"length":0,"stats":{"Line":1}},{"line":449,"address":[1282598],"length":1,"stats":{"Line":1}},{"line":450,"address":[],"length":0,"stats":{"Line":4}},{"line":451,"address":[1283070,1283008],"length":1,"stats":{"Line":2}},{"line":454,"address":[1283024],"length":1,"stats":{"Line":3}},{"line":470,"address":[],"length":0,"stats":{"Line":4}},{"line":471,"address":[],"length":0,"stats":{"Line":0}},{"line":472,"address":[964664,964562,964930,964254,965032,964316],"length":1,"stats":{"Line":7}},{"line":473,"address":[],"length":0,"stats":{"Line":6}},{"line":476,"address":[964989,964621,964306],"length":1,"stats":{"Line":10}},{"line":479,"address":[],"length":0,"stats":{"Line":1}},{"line":480,"address":[],"length":0,"stats":{"Line":0}},{"line":481,"address":[965298,965400],"length":1,"stats":{"Line":2}},{"line":482,"address":[965418,965548],"length":1,"stats":{"Line":2}},{"line":485,"address":[965357],"length":1,"stats":{"Line":3}},{"line":502,"address":[],"length":0,"stats":{"Line":3}},{"line":503,"address":[],"length":0,"stats":{"Line":5}},{"line":504,"address":[994013,994125,993901],"length":1,"stats":{"Line":2}},{"line":508,"address":[],"length":0,"stats":{"Line":2}},{"line":509,"address":[605728,605753,605680,605705],"length":1,"stats":{"Line":6}},{"line":512,"address":[],"length":0,"stats":{"Line":2}},{"line":513,"address":[],"length":0,"stats":{"Line":6}},{"line":523,"address":[994336],"length":1,"stats":{"Line":1}},{"line":524,"address":[],"length":0,"stats":{"Line":1}},{"line":539,"address":[],"length":0,"stats":{"Line":1}},{"line":540,"address":[],"length":0,"stats":{"Line":3}},{"line":543,"address":[994432],"length":1,"stats":{"Line":1}},{"line":544,"address":[],"length":0,"stats":{"Line":3}},{"line":559,"address":[],"length":0,"stats":{"Line":1}},{"line":560,"address":[532437],"length":1,"stats":{"Line":3}},{"line":563,"address":[],"length":0,"stats":{"Line":1}},{"line":564,"address":[606041,606016],"length":1,"stats":{"Line":3}},{"line":582,"address":[],"length":0,"stats":{"Line":5}},{"line":583,"address":[],"length":0,"stats":{"Line":9}},{"line":584,"address":[],"length":0,"stats":{"Line":4}},{"line":588,"address":[],"length":0,"stats":{"Line":2}},{"line":589,"address":[606089,606064,606112,606137],"length":1,"stats":{"Line":6}},{"line":592,"address":[],"length":0,"stats":{"Line":2}},{"line":593,"address":[],"length":0,"stats":{"Line":6}},{"line":608,"address":[],"length":0,"stats":{"Line":2}},{"line":609,"address":[],"length":0,"stats":{"Line":2}},{"line":616,"address":[],"length":0,"stats":{"Line":1}},{"line":617,"address":[],"length":0,"stats":{"Line":1}},{"line":618,"address":[],"length":0,"stats":{"Line":0}}],"covered":75,"coverable":92},{"path":["/","home","botahamec","Projects","happylock","src","mutex","guard.rs"],"content":"use std::fmt::{Debug, Display};\nuse std::hash::Hash;\nuse std::marker::PhantomData;\nuse std::ops::{Deref, DerefMut};\n\nuse lock_api::RawMutex;\n\nuse crate::lockable::RawLock;\nuse crate::ThreadKey;\n\nuse super::{Mutex, MutexGuard, MutexRef};\n\n// These impls make things slightly easier because now you can use\n// `println!(\"{guard}\")` instead of `println!(\"{}\", *guard)`\n\n#[mutants::skip] // hashing involves RNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Hash + ?Sized, R: RawMutex\u003e Hash for MutexRef\u003c'_, T, R\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.deref().hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug + ?Sized, R: RawMutex\u003e Debug for MutexRef\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: Display + ?Sized, R: RawMutex\u003e Display for MutexRef\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e Drop for MutexRef\u003c'_, T, R\u003e {\n\tfn drop(\u0026mut self) {\n\t\t// safety: this guard is being destroyed, so the data cannot be\n\t\t// accessed without locking again\n\t\tunsafe { self.0.raw_unlock_write() }\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e Deref for MutexRef\u003c'_, T, R\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t// safety: this is the only type that can use `value`, and there's\n\t\t// a reference to this type, so there cannot be any mutable\n\t\t// references to this value.\n\t\tunsafe { \u0026*self.0.data.get() }\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e DerefMut for MutexRef\u003c'_, T, R\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t// safety: this is the only type that can use `value`, and we have a\n\t\t// mutable reference to this type, so there cannot be any other\n\t\t// references to this value.\n\t\tunsafe { \u0026mut *self.0.data.get() }\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e AsRef\u003cT\u003e for MutexRef\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e AsMut\u003cT\u003e for MutexRef\u003c'_, T, R\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself\n\t}\n}\n\nimpl\u003c'a, T: ?Sized, R: RawMutex\u003e MutexRef\u003c'a, T, R\u003e {\n\t/// Creates a reference to the underlying data of a mutex without\n\t/// attempting to lock it or take ownership of the key. But it's also quite\n\t/// dangerous to drop.\n\tpub(crate) const unsafe fn new(mutex: \u0026'a Mutex\u003cT, R\u003e) -\u003e Self {\n\t\tSelf(mutex, PhantomData)\n\t}\n}\n\n// it's kinda annoying to re-implement some of this stuff on guards\n// there's nothing i can do about that\n\n#[mutants::skip] // hashing involves RNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Hash + ?Sized, R: RawMutex\u003e Hash for MutexGuard\u003c'_, T, R\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.deref().hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug + ?Sized, R: RawMutex\u003e Debug for MutexGuard\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: Display + ?Sized, R: RawMutex\u003e Display for MutexGuard\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e Deref for MutexGuard\u003c'_, T, R\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t\u0026self.mutex\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e DerefMut for MutexGuard\u003c'_, T, R\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t\u0026mut self.mutex\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e AsRef\u003cT\u003e for MutexGuard\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e AsMut\u003cT\u003e for MutexGuard\u003c'_, T, R\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself\n\t}\n}\n\nimpl\u003c'a, T: ?Sized, R: RawMutex\u003e MutexGuard\u003c'a, T, R\u003e {\n\t/// Create a guard to the given mutex. Undefined if multiple guards to the\n\t/// same mutex exist at once.\n\t#[must_use]\n\tpub(super) const unsafe fn new(mutex: \u0026'a Mutex\u003cT, R\u003e, thread_key: ThreadKey) -\u003e Self {\n\t\tSelf {\n\t\t\tmutex: MutexRef(mutex, PhantomData),\n\t\t\tthread_key,\n\t\t}\n\t}\n}\n\nunsafe impl\u003cT: ?Sized + Sync, R: RawMutex + Sync\u003e Sync for MutexRef\u003c'_, T, R\u003e {}\n","traces":[{"line":33,"address":[1283344],"length":1,"stats":{"Line":1}},{"line":34,"address":[],"length":0,"stats":{"Line":1}},{"line":39,"address":[153792],"length":1,"stats":{"Line":6}},{"line":42,"address":[],"length":0,"stats":{"Line":7}},{"line":49,"address":[1283392,1283424],"length":1,"stats":{"Line":4}},{"line":53,"address":[163269],"length":1,"stats":{"Line":4}},{"line":58,"address":[1283488,1283456],"length":1,"stats":{"Line":5}},{"line":62,"address":[1283461,1283493],"length":1,"stats":{"Line":5}},{"line":67,"address":[],"length":0,"stats":{"Line":2}},{"line":68,"address":[],"length":0,"stats":{"Line":2}},{"line":73,"address":[],"length":0,"stats":{"Line":1}},{"line":74,"address":[],"length":0,"stats":{"Line":1}},{"line":82,"address":[],"length":0,"stats":{"Line":4}},{"line":83,"address":[],"length":0,"stats":{"Line":0}},{"line":107,"address":[],"length":0,"stats":{"Line":1}},{"line":108,"address":[],"length":0,"stats":{"Line":1}},{"line":115,"address":[],"length":0,"stats":{"Line":2}},{"line":116,"address":[],"length":0,"stats":{"Line":3}},{"line":121,"address":[],"length":0,"stats":{"Line":2}},{"line":122,"address":[],"length":0,"stats":{"Line":2}},{"line":127,"address":[1283696],"length":1,"stats":{"Line":2}},{"line":128,"address":[],"length":0,"stats":{"Line":2}},{"line":133,"address":[],"length":0,"stats":{"Line":1}},{"line":134,"address":[],"length":0,"stats":{"Line":1}},{"line":142,"address":[140544],"length":1,"stats":{"Line":4}},{"line":144,"address":[],"length":0,"stats":{"Line":0}}],"covered":24,"coverable":26},{"path":["/","home","botahamec","Projects","happylock","src","mutex","mutex.rs"],"content":"use std::cell::UnsafeCell;\nuse std::fmt::Debug;\nuse std::marker::PhantomData;\nuse std::panic::AssertUnwindSafe;\n\nuse lock_api::RawMutex;\n\nuse crate::collection::utils;\nuse crate::handle_unwind::handle_unwind;\nuse crate::lockable::{Lockable, LockableGetMut, LockableIntoInner, OwnedLockable, RawLock};\nuse crate::poisonable::PoisonFlag;\nuse crate::{Keyable, ThreadKey};\n\nuse super::{Mutex, MutexGuard, MutexRef};\n\nunsafe impl\u003cT: ?Sized, R: RawMutex\u003e RawLock for Mutex\u003cT, R\u003e {\n\tfn poison(\u0026self) {\n\t\tself.poison.poison();\n\t}\n\n\tunsafe fn raw_write(\u0026self) {\n\t\tassert!(!self.poison.is_poisoned(), \"The mutex has been killed\");\n\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.lock(), || self.poison())\n\t}\n\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool {\n\t\tif self.poison.is_poisoned() {\n\t\t\treturn false;\n\t\t}\n\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.try_lock(), || self.poison())\n\t}\n\n\tunsafe fn raw_unlock_write(\u0026self) {\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.unlock(), || self.poison())\n\t}\n\n\t// this is the closest thing to a read we can get, but Sharable isn't\n\t// implemented for this\n\t#[mutants::skip]\n\t#[cfg(not(tarpaulin_include))]\n\tunsafe fn raw_read(\u0026self) {\n\t\tself.raw_write()\n\t}\n\n\t#[mutants::skip]\n\t#[cfg(not(tarpaulin_include))]\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tself.raw_try_write()\n\t}\n\n\t#[mutants::skip]\n\t#[cfg(not(tarpaulin_include))]\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tself.raw_unlock_write()\n\t}\n}\n\nunsafe impl\u003cT, R: RawMutex\u003e Lockable for Mutex\u003cT, R\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= MutexRef\u003c'g, T, R\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= \u0026'a mut T\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tptrs.push(self);\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tMutexRef::new(self)\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.data.get().as_mut().unwrap_unchecked()\n\t}\n}\n\nimpl\u003cT: Send, R: RawMutex\u003e LockableIntoInner for Mutex\u003cT, R\u003e {\n\ttype Inner = T;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tself.into_inner()\n\t}\n}\n\nimpl\u003cT: Send, R: RawMutex\u003e LockableGetMut for Mutex\u003cT, R\u003e {\n\ttype Inner\u003c'a\u003e\n\t\t= \u0026'a mut T\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tself.get_mut()\n\t}\n}\n\nunsafe impl\u003cT: Send, R: RawMutex\u003e OwnedLockable for Mutex\u003cT, R\u003e {}\n\nimpl\u003cT, R: RawMutex\u003e Mutex\u003cT, R\u003e {\n\t/// Create a new unlocked `Mutex`.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t///\n\t/// let mutex = Mutex::new(0);\n\t/// ```\n\t#[must_use]\n\tpub const fn new(data: T) -\u003e Self {\n\t\tSelf {\n\t\t\traw: R::INIT,\n\t\t\tpoison: PoisonFlag::new(),\n\t\t\tdata: UnsafeCell::new(data),\n\t\t}\n\t}\n\n\t/// Returns the raw underlying mutex.\n\t///\n\t/// Note that you will most likely need to import the [`RawMutex`] trait\n\t/// from `lock_api` to be able to call functions on the raw mutex.\n\t///\n\t/// # Safety\n\t///\n\t/// This method is unsafe because it allows unlocking a mutex while still\n\t/// holding a reference to a [`MutexGuard`], and locking a mutex without\n\t/// holding the [`ThreadKey`].\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\t#[must_use]\n\tpub const unsafe fn raw(\u0026self) -\u003e \u0026R {\n\t\t\u0026self.raw\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug, R: RawMutex\u003e Debug for Mutex\u003cT, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t// safety: this is just a try lock, and the value is dropped\n\t\t// immediately after, so there's no risk of blocking ourselves\n\t\t// or any other threads\n\t\t// when i implement try_clone this code will become less unsafe\n\t\tif let Some(value) = unsafe { self.try_lock_no_key() } {\n\t\t\tf.debug_struct(\"Mutex\").field(\"data\", \u0026\u0026*value).finish()\n\t\t} else {\n\t\t\tstruct LockedPlaceholder;\n\t\t\timpl Debug for LockedPlaceholder {\n\t\t\t\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t\t\t\tf.write_str(\"\u003clocked\u003e\")\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tf.debug_struct(\"Mutex\")\n\t\t\t\t.field(\"data\", \u0026LockedPlaceholder)\n\t\t\t\t.finish()\n\t\t}\n\t}\n}\n\nimpl\u003cT: Default, R: RawMutex\u003e Default for Mutex\u003cT, R\u003e {\n\tfn default() -\u003e Self {\n\t\tSelf::new(T::default())\n\t}\n}\n\nimpl\u003cT, R: RawMutex\u003e From\u003cT\u003e for Mutex\u003cT, R\u003e {\n\tfn from(value: T) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\n// We don't need a `get_mut` because we don't have mutex poisoning. Hurray!\n// We have it anyway for documentation\nimpl\u003cT: ?Sized, R\u003e AsMut\u003cT\u003e for Mutex\u003cT, R\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself.get_mut()\n\t}\n}\n\nimpl\u003cT, R\u003e Mutex\u003cT, R\u003e {\n\t/// Consumes this mutex, returning the underlying data.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t///\n\t/// let mutex = Mutex::new(0);\n\t/// assert_eq!(mutex.into_inner(), 0);\n\t/// ```\n\t#[must_use]\n\tpub fn into_inner(self) -\u003e T {\n\t\tself.data.into_inner()\n\t}\n}\n\nimpl\u003cT: ?Sized, R\u003e Mutex\u003cT, R\u003e {\n\t/// Returns a mutable reference to the underlying data.\n\t///\n\t/// Since this call borrows `Mutex` mutably, no actual locking is taking\n\t/// place. The mutable borrow statically guarantees that no locks exist.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, Mutex};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut mutex = Mutex::new(0);\n\t/// *mutex.get_mut() = 10;\n\t/// assert_eq!(*mutex.lock(key), 10);\n\t/// ```\n\t#[must_use]\n\tpub fn get_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself.data.get_mut()\n\t}\n}\n\nimpl\u003cT, R: RawMutex\u003e Mutex\u003cT, R\u003e {\n\tpub fn scoped_lock\u003c'a, Ret\u003e(\n\t\t\u0026'a self,\n\t\tkey: impl Keyable,\n\t\tf: impl FnOnce(\u0026'a mut T) -\u003e Ret,\n\t) -\u003e Ret {\n\t\tutils::scoped_write(self, key, f)\n\t}\n\n\tpub fn scoped_try_lock\u003c'a, Key: Keyable, Ret\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl FnOnce(\u0026'a mut T) -\u003e Ret,\n\t) -\u003e Result\u003cRet, Key\u003e {\n\t\tutils::scoped_try_write(self, key, f)\n\t}\n\n\t/// Block the thread until this mutex can be locked, and lock it.\n\t///\n\t/// Upon returning, the thread is the only thread with a lock on the\n\t/// `Mutex`. A [`MutexGuard`] is returned to allow a scoped unlock of this\n\t/// `Mutex`. When the guard is dropped, this `Mutex` will unlock.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::{thread, sync::Arc};\n\t/// use happylock::{Mutex, ThreadKey};\n\t///\n\t/// let mutex = Arc::new(Mutex::new(0));\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t///\n\t/// thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// *c_mutex.lock(key) = 10;\n\t/// }).join().expect(\"thread::spawn failed\");\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// assert_eq!(*mutex.lock(key), 10);\n\t/// ```\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e MutexGuard\u003c'_, T, R\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_write();\n\n\t\t\t// safety: we just locked the mutex\n\t\t\tMutexGuard::new(self, key)\n\t\t}\n\t}\n\n\t/// Attempts to lock the `Mutex` without blocking.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// lock when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If the mutex could not be acquired because it is already locked, then\n\t/// this call will return an error containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::{thread, sync::Arc};\n\t/// use happylock::{Mutex, ThreadKey};\n\t///\n\t/// let mutex = Arc::new(Mutex::new(0));\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t///\n\t/// thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut lock = c_mutex.try_lock(key);\n\t/// if let Ok(mut lock) = lock {\n\t/// *lock = 10;\n\t/// } else {\n\t/// println!(\"try_lock failed\");\n\t/// }\n\t/// }).join().expect(\"thread::spawn failed\");\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// assert_eq!(*mutex.lock(key), 10);\n\t/// ```\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e Result\u003cMutexGuard\u003c'_, T, R\u003e, ThreadKey\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the key to the mutex\n\t\t\tif self.raw_try_write() {\n\t\t\t\t// safety: we just locked the mutex\n\t\t\t\tOk(MutexGuard::new(self, key))\n\t\t\t} else {\n\t\t\t\tErr(key)\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Returns `true` if the mutex is currently locked\n\t#[cfg(test)]\n\tpub(crate) fn is_locked(\u0026self) -\u003e bool {\n\t\tself.raw.is_locked()\n\t}\n\n\t/// Lock without a [`ThreadKey`]. It is undefined behavior to do this without\n\t/// owning the [`ThreadKey`].\n\tpub(crate) unsafe fn try_lock_no_key(\u0026self) -\u003e Option\u003cMutexRef\u003c'_, T, R\u003e\u003e {\n\t\tself.raw_try_write().then_some(MutexRef(self, PhantomData))\n\t}\n\n\t/// Consumes the [`MutexGuard`], and consequently unlocks its `Mutex`.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, Mutex};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mutex = Mutex::new(0);\n\t///\n\t/// let mut guard = mutex.lock(key);\n\t/// *guard += 20;\n\t///\n\t/// let key = Mutex::unlock(guard);\n\t/// ```\n\t#[must_use]\n\tpub fn unlock(guard: MutexGuard\u003c'_, T, R\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.mutex);\n\t\tguard.thread_key\n\t}\n}\n\nunsafe impl\u003cR: RawMutex + Send, T: ?Sized + Send\u003e Send for Mutex\u003cT, R\u003e {}\nunsafe impl\u003cR: RawMutex + Sync, T: ?Sized + Send\u003e Sync for Mutex\u003cT, R\u003e {}\n","traces":[{"line":17,"address":[229520,229536,229568],"length":1,"stats":{"Line":5}},{"line":18,"address":[142933,142901],"length":1,"stats":{"Line":6}},{"line":21,"address":[164160,164272],"length":1,"stats":{"Line":10}},{"line":22,"address":[164325,164286,164174,164213],"length":1,"stats":{"Line":11}},{"line":25,"address":[141073],"length":1,"stats":{"Line":11}},{"line":26,"address":[],"length":0,"stats":{"Line":40}},{"line":29,"address":[229296,229136,229216],"length":1,"stats":{"Line":10}},{"line":30,"address":[229150,229310,229230],"length":1,"stats":{"Line":12}},{"line":31,"address":[142696,142776],"length":1,"stats":{"Line":5}},{"line":35,"address":[218241],"length":1,"stats":{"Line":11}},{"line":36,"address":[192917,192837,192885,192965,192832,192880,192912,192848,192960,192853,192944,192949],"length":1,"stats":{"Line":39}},{"line":39,"address":[164032,164000],"length":1,"stats":{"Line":12}},{"line":41,"address":[218316],"length":1,"stats":{"Line":14}},{"line":42,"address":[990965,990757,990821,990933,990752,990997,990720,990928,990880,990981,990992,990848,990816,990853,990885,990784,990917,990725,990912,990976,990789,990944,990960,990949],"length":1,"stats":{"Line":48}},{"line":77,"address":[218544],"length":1,"stats":{"Line":19}},{"line":78,"address":[1285225,1285545,1285289,1285417,1285481,1285353],"length":1,"stats":{"Line":19}},{"line":81,"address":[164384,164400],"length":1,"stats":{"Line":4}},{"line":82,"address":[164389,164405],"length":1,"stats":{"Line":4}},{"line":85,"address":[218496],"length":1,"stats":{"Line":9}},{"line":86,"address":[],"length":0,"stats":{"Line":9}},{"line":93,"address":[1285808,1285792,1285744,1285712,1285760,1285728],"length":1,"stats":{"Line":6}},{"line":94,"address":[],"length":0,"stats":{"Line":6}},{"line":104,"address":[1285888,1285856,1285872],"length":1,"stats":{"Line":3}},{"line":105,"address":[1285861,1285893,1285877],"length":1,"stats":{"Line":3}},{"line":122,"address":[1286678,1286720,1285904,1286846,1286240,1286700,1286096,1286464,1286076,1286446,1286864,1287010,1286256],"length":1,"stats":{"Line":19}},{"line":125,"address":[228875,229019,228726,228827,228678,228971],"length":1,"stats":{"Line":38}},{"line":126,"address":[1286562,1286961,1286371,1286009,1286803,1286190],"length":1,"stats":{"Line":20}},{"line":143,"address":[1287024],"length":1,"stats":{"Line":1}},{"line":144,"address":[],"length":0,"stats":{"Line":0}},{"line":174,"address":[],"length":0,"stats":{"Line":5}},{"line":175,"address":[1287102,1287140,1287053,1287224,1287192],"length":1,"stats":{"Line":5}},{"line":180,"address":[],"length":0,"stats":{"Line":2}},{"line":181,"address":[],"length":0,"stats":{"Line":2}},{"line":188,"address":[],"length":0,"stats":{"Line":1}},{"line":189,"address":[1287349],"length":1,"stats":{"Line":1}},{"line":205,"address":[],"length":0,"stats":{"Line":2}},{"line":206,"address":[],"length":0,"stats":{"Line":6}},{"line":227,"address":[1287616,1287648,1287680],"length":1,"stats":{"Line":3}},{"line":228,"address":[],"length":0,"stats":{"Line":3}},{"line":233,"address":[140560],"length":1,"stats":{"Line":9}},{"line":238,"address":[1287826,1287890,1287954,1287849,1287922,1287730,1287762,1287794],"length":1,"stats":{"Line":9}},{"line":241,"address":[228496,228464,228592,228560,228624,228528],"length":1,"stats":{"Line":18}},{"line":246,"address":[142173,142301,142269,142141,142205,142237],"length":1,"stats":{"Line":18}},{"line":272,"address":[218064,218176,218150],"length":1,"stats":{"Line":6}},{"line":275,"address":[1288110,1287982],"length":1,"stats":{"Line":6}},{"line":278,"address":[],"length":0,"stats":{"Line":6}},{"line":315,"address":[],"length":0,"stats":{"Line":2}},{"line":318,"address":[],"length":0,"stats":{"Line":6}},{"line":320,"address":[1288505,1288475,1288315,1288345],"length":1,"stats":{"Line":4}},{"line":322,"address":[],"length":0,"stats":{"Line":0}},{"line":329,"address":[1288560,1288544],"length":1,"stats":{"Line":2}},{"line":330,"address":[],"length":0,"stats":{"Line":2}},{"line":335,"address":[],"length":0,"stats":{"Line":1}},{"line":336,"address":[],"length":0,"stats":{"Line":1}},{"line":355,"address":[],"length":0,"stats":{"Line":3}},{"line":356,"address":[],"length":0,"stats":{"Line":3}},{"line":357,"address":[],"length":0,"stats":{"Line":0}}],"covered":54,"coverable":57},{"path":["/","home","botahamec","Projects","happylock","src","mutex.rs"],"content":"use std::cell::UnsafeCell;\nuse std::marker::PhantomData;\n\nuse lock_api::RawMutex;\n\nuse crate::poisonable::PoisonFlag;\nuse crate::ThreadKey;\n\nmod guard;\nmod mutex;\n\n/// A spinning mutex\n#[cfg(feature = \"spin\")]\npub type SpinLock\u003cT\u003e = Mutex\u003cT, spin::Mutex\u003c()\u003e\u003e;\n\n/// A parking lot mutex\n#[cfg(feature = \"parking_lot\")]\npub type ParkingMutex\u003cT\u003e = Mutex\u003cT, parking_lot::RawMutex\u003e;\n\n/// A mutual exclusion primitive useful for protecting shared data, which\n/// cannot deadlock.\n///\n/// This mutex will block threads waiting for the lock to become available.\n/// Each mutex has a type parameter which represents the data that it is\n/// protecting. The data can only be accessed through the [`MutexGuard`]s\n/// returned from [`lock`] and [`try_lock`], which guarantees that the data is\n/// only ever accessed when the mutex is locked.\n///\n/// Locking the mutex on a thread that already locked it is impossible, due to\n/// the requirement of the [`ThreadKey`]. Therefore, this will never deadlock.\n///\n/// # Examples\n///\n/// ```\n/// use std::sync::Arc;\n/// use std::thread;\n/// use std::sync::mpsc;\n///\n/// use happylock::{Mutex, ThreadKey};\n///\n/// // Spawn a few threads to increment a shared variable (non-atomically),\n/// // and let the main thread know once all increments are done.\n/// //\n/// // Here we're using an Arc to share memory among threads, and the data\n/// // inside the Arc is protected with a mutex.\n/// const N: usize = 10;\n///\n/// let data = Arc::new(Mutex::new(0));\n///\n/// let (tx, rx) = mpsc::channel();\n/// for _ in 0..N {\n/// let (data, tx) = (Arc::clone(\u0026data), tx.clone());\n/// thread::spawn(move || {\n/// let key = ThreadKey::get().unwrap();\n/// let mut data = data.lock(key);\n/// *data += 1;\n/// if *data == N {\n/// tx.send(()).unwrap();\n/// }\n/// // the lock is unlocked\n/// });\n/// }\n///\n/// rx.recv().unwrap();\n/// ```\n///\n/// To unlock a mutex guard sooner than the end of the enclosing scope, either\n/// create an inner scope, drop the guard manually, or call [`Mutex::unlock`].\n///\n/// ```\n/// use std::sync::Arc;\n/// use std::thread;\n///\n/// use happylock::{Mutex, ThreadKey};\n///\n/// const N: usize = 3;\n///\n/// let data_mutex = Arc::new(Mutex::new(vec![1, 2, 3, 4]));\n/// let res_mutex = Arc::new(Mutex::new(0));\n///\n/// let mut threads = Vec::with_capacity(N);\n/// (0..N).for_each(|_| {\n/// let data_mutex_clone = Arc::clone(\u0026data_mutex);\n/// let res_mutex_clone = Arc::clone(\u0026res_mutex);\n///\n/// threads.push(thread::spawn(move || {\n/// let mut key = ThreadKey::get().unwrap();\n///\n/// // Here we use a block to limit the lifetime of the lock guard.\n/// let result = data_mutex_clone.scoped_lock(\u0026mut key, |data| {\n/// let result = data.iter().fold(0, |acc, x| acc + x * 2);\n/// data.push(result);\n/// result\n/// // The mutex guard gets dropped here, so the lock is released\n/// });\n/// // The thread key is available again\n/// *res_mutex_clone.lock(key) += result;\n/// }));\n/// });\n///\n/// let key = ThreadKey::get().unwrap();\n/// let mut data = data_mutex.lock(key);\n/// let result = data.iter().fold(0, |acc, x| acc + x * 2);\n/// data.push(result);\n///\n/// // We drop the `data` explicitly because it's not necessary anymore. This\n/// // allows other threads to start working on the data immediately. Dropping\n/// // the data also gives us access to the thread key, so we can lock\n/// // another mutex.\n/// let key = Mutex::unlock(data);\n///\n/// // Here the mutex guard is not assigned to a variable and so, even if the\n/// // scope does not end after this line, the mutex is still released: there is\n/// // no deadlock.\n/// *res_mutex.lock(key) += result;\n///\n/// threads.into_iter().for_each(|thread| {\n/// thread\n/// .join()\n/// .expect(\"The thread creating or execution failed !\")\n/// });\n///\n/// let key = ThreadKey::get().unwrap();\n/// assert_eq!(*res_mutex.lock(key), 800);\n/// ```\n///\n/// [`lock`]: `Mutex::lock`\n/// [`try_lock`]: `Mutex::try_lock`\n/// [`ThreadKey`]: `crate::ThreadKey`\npub struct Mutex\u003cT: ?Sized, R\u003e {\n\traw: R,\n\tpoison: PoisonFlag,\n\tdata: UnsafeCell\u003cT\u003e,\n}\n\n/// A reference to a mutex that unlocks it when dropped.\n///\n/// This is similar to [`MutexGuard`], except it does not hold a [`Keyable`].\npub struct MutexRef\u003c'a, T: ?Sized + 'a, R: RawMutex\u003e(\u0026'a Mutex\u003cT, R\u003e, PhantomData\u003cR::GuardMarker\u003e);\n\n/// An RAII implementation of a “scoped lock” of a mutex.\n///\n/// When this structure is dropped (falls out of scope), the lock will be\n/// unlocked.\n///\n/// This is created by calling the [`lock`] and [`try_lock`] methods on [`Mutex`]\n///\n/// [`lock`]: `Mutex::lock`\n/// [`try_lock`]: `Mutex::try_lock`\n//\n// This is the most lifetime-intensive thing I've ever written. Can I graduate\n// from borrow checker university now?\npub struct MutexGuard\u003c'a, T: ?Sized + 'a, R: RawMutex\u003e {\n\tmutex: MutexRef\u003c'a, T, R\u003e, // this way we don't need to re-implement Drop\n\tthread_key: ThreadKey,\n}\n\n#[cfg(test)]\nmod tests {\n\tuse crate::{LockCollection, ThreadKey};\n\n\tuse super::*;\n\n\t#[test]\n\tfn unlocked_when_initialized() {\n\t\tlet lock: crate::Mutex\u003c_\u003e = Mutex::new(\"Hello, world!\");\n\n\t\tassert!(!lock.is_locked());\n\t}\n\n\t#[test]\n\tfn locked_after_read() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::Mutex\u003c_\u003e = Mutex::new(\"Hello, world!\");\n\n\t\tlet guard = lock.lock(key);\n\n\t\tassert!(lock.is_locked());\n\t\tdrop(guard)\n\t}\n\n\t#[test]\n\tfn from_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex: crate::Mutex\u003c_\u003e = Mutex::from(\"Hello, world!\");\n\n\t\tlet guard = mutex.lock(key);\n\t\tassert_eq!(*guard, \"Hello, world!\");\n\t}\n\n\t#[test]\n\tfn as_mut_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mut mutex = crate::Mutex::from(42);\n\n\t\tlet mut_ref = mutex.as_mut();\n\t\t*mut_ref = 24;\n\n\t\tmutex.scoped_lock(key, |guard| assert_eq!(*guard, 24))\n\t}\n\n\t#[test]\n\tfn display_works_for_guard() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex: crate::Mutex\u003c_\u003e = Mutex::new(\"Hello, world!\");\n\t\tlet guard = mutex.lock(key);\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn display_works_for_ref() {\n\t\tlet mutex: crate::Mutex\u003c_\u003e = Mutex::new(\"Hello, world!\");\n\t\tlet guard = unsafe { mutex.try_lock_no_key().unwrap() };\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn ref_as_mut() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = LockCollection::new(crate::Mutex::new(0));\n\t\tlet mut guard = collection.lock(key);\n\t\tlet guard_mut = guard.as_mut().as_mut();\n\n\t\t*guard_mut = 3;\n\t\tlet key = LockCollection::\u003ccrate::Mutex\u003c_\u003e\u003e::unlock(guard);\n\n\t\tlet guard = collection.lock(key);\n\n\t\tassert_eq!(guard.as_ref().as_ref(), \u00263);\n\t}\n\n\t#[test]\n\tfn guard_as_mut() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = crate::Mutex::new(0);\n\t\tlet mut guard = mutex.lock(key);\n\t\tlet guard_mut = guard.as_mut();\n\n\t\t*guard_mut = 3;\n\t\tlet key = Mutex::unlock(guard);\n\n\t\tlet guard = mutex.lock(key);\n\n\t\tassert_eq!(guard.as_ref(), \u00263);\n\t}\n\n\t#[test]\n\tfn dropping_guard_releases_mutex() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex: crate::Mutex\u003c_\u003e = Mutex::new(\"Hello, world!\");\n\n\t\tlet guard = mutex.lock(key);\n\t\tdrop(guard);\n\n\t\tassert!(!mutex.is_locked());\n\t}\n\n\t#[test]\n\tfn dropping_ref_releases_mutex() {\n\t\tlet mutex: crate::Mutex\u003c_\u003e = Mutex::new(\"Hello, world!\");\n\n\t\tlet guard = unsafe { mutex.try_lock_no_key().unwrap() };\n\t\tdrop(guard);\n\n\t\tassert!(!mutex.is_locked());\n\t}\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","src","poisonable","error.rs"],"content":"use core::fmt;\nuse std::error::Error;\n\nuse super::{PoisonError, PoisonGuard, TryLockPoisonableError};\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard\u003e fmt::Debug for PoisonError\u003cGuard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut fmt::Formatter\u003c'_\u003e) -\u003e fmt::Result {\n\t\tf.debug_struct(\"PoisonError\").finish_non_exhaustive()\n\t}\n}\n\nimpl\u003cGuard\u003e fmt::Display for PoisonError\u003cGuard\u003e {\n\t#[cfg_attr(test, mutants::skip)]\n\t#[cfg(not(tarpaulin_include))]\n\tfn fmt(\u0026self, f: \u0026mut fmt::Formatter\u003c'_\u003e) -\u003e fmt::Result {\n\t\t\"poisoned lock: another task failed inside\".fmt(f)\n\t}\n}\n\nimpl\u003cGuard\u003e AsRef\u003cGuard\u003e for PoisonError\u003cGuard\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026Guard {\n\t\tself.get_ref()\n\t}\n}\n\nimpl\u003cGuard\u003e AsMut\u003cGuard\u003e for PoisonError\u003cGuard\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut Guard {\n\t\tself.get_mut()\n\t}\n}\n\nimpl\u003cGuard\u003e Error for PoisonError\u003cGuard\u003e {}\n\nimpl\u003cGuard\u003e PoisonError\u003cGuard\u003e {\n\t/// Creates a `PoisonError`\n\t///\n\t/// This is generally created by methods like [`Poisonable::lock`].\n\t///\n\t/// ```\n\t/// use happylock::poisonable::PoisonError;\n\t///\n\t/// let error = PoisonError::new(\"oh no\");\n\t/// ```\n\t///\n\t/// [`Poisonable::lock`]: `crate::poisonable::Poisonable::lock`\n\t#[must_use]\n\tpub const fn new(guard: Guard) -\u003e Self {\n\t\tSelf { guard }\n\t}\n\n\t/// Consumes the error indicating that a lock is poisonmed, returning the\n\t/// underlying guard to allow access regardless.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::collections::HashSet;\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let mutex = Arc::new(Poisonable::new(Mutex::new(HashSet::new())));\n\t///\n\t/// // poison the mutex\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t/// let _ = thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut data = c_mutex.lock(key).unwrap();\n\t/// data.insert(10);\n\t/// panic!();\n\t/// }).join();\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let p_err = mutex.lock(key).unwrap_err();\n\t/// let data = p_err.into_inner();\n\t/// println!(\"recovered {} items\", data.len());\n\t/// ```\n\t#[must_use]\n\tpub fn into_inner(self) -\u003e Guard {\n\t\tself.guard\n\t}\n\n\t/// Reaches into this error indicating that a lock is poisoned, returning a\n\t/// reference to the underlying guard to allow access regardless.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::collections::HashSet;\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t/// use happylock::poisonable::PoisonGuard;\n\t///\n\t/// let mutex = Arc::new(Poisonable::new(Mutex::new(HashSet::new())));\n\t///\n\t/// // poison the mutex\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t/// let _ = thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut data = c_mutex.lock(key).unwrap();\n\t/// data.insert(10);\n\t/// panic!();\n\t/// }).join();\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let p_err = mutex.lock(key).unwrap_err();\n\t/// let data: \u0026PoisonGuard\u003c_\u003e = p_err.get_ref();\n\t/// println!(\"recovered {} items\", data.len());\n\t/// ```\n\t#[must_use]\n\tpub const fn get_ref(\u0026self) -\u003e \u0026Guard {\n\t\t\u0026self.guard\n\t}\n\n\t/// Reaches into this error indicating that a lock is poisoned, returning a\n\t/// mutable reference to the underlying guard to allow access regardless.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::collections::HashSet;\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let mutex = Arc::new(Poisonable::new(Mutex::new(HashSet::new())));\n\t///\n\t/// // poison the mutex\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t/// let _ = thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut data = c_mutex.lock(key).unwrap();\n\t/// data.insert(10);\n\t/// panic!();\n\t/// }).join();\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut p_err = mutex.lock(key).unwrap_err();\n\t/// let data = p_err.get_mut();\n\t/// data.insert(20);\n\t/// println!(\"recovered {} items\", data.len());\n\t/// ```\n\t#[must_use]\n\tpub fn get_mut(\u0026mut self) -\u003e \u0026mut Guard {\n\t\t\u0026mut self.guard\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cG\u003e fmt::Debug for TryLockPoisonableError\u003c'_, G\u003e {\n\tfn fmt(\u0026self, f: \u0026mut fmt::Formatter\u003c'_\u003e) -\u003e fmt::Result {\n\t\tmatch *self {\n\t\t\tSelf::Poisoned(..) =\u003e \"Poisoned(..)\".fmt(f),\n\t\t\tSelf::WouldBlock(_) =\u003e \"WouldBlock\".fmt(f),\n\t\t}\n\t}\n}\n\nimpl\u003cG\u003e fmt::Display for TryLockPoisonableError\u003c'_, G\u003e {\n\t#[cfg_attr(test, mutants::skip)]\n\t#[cfg(not(tarpaulin_include))]\n\tfn fmt(\u0026self, f: \u0026mut fmt::Formatter\u003c'_\u003e) -\u003e fmt::Result {\n\t\tmatch *self {\n\t\t\tSelf::Poisoned(..) =\u003e \"poisoned lock: another task failed inside\",\n\t\t\tSelf::WouldBlock(_) =\u003e \"try_lock failed because the operation would block\",\n\t\t}\n\t\t.fmt(f)\n\t}\n}\n\nimpl\u003cG\u003e Error for TryLockPoisonableError\u003c'_, G\u003e {}\n\nimpl\u003c'flag, G\u003e From\u003cPoisonError\u003cPoisonGuard\u003c'flag, G\u003e\u003e\u003e for TryLockPoisonableError\u003c'flag, G\u003e {\n\tfn from(value: PoisonError\u003cPoisonGuard\u003c'flag, G\u003e\u003e) -\u003e Self {\n\t\tSelf::Poisoned(value)\n\t}\n}\n","traces":[{"line":23,"address":[862832],"length":1,"stats":{"Line":1}},{"line":24,"address":[],"length":0,"stats":{"Line":1}},{"line":29,"address":[],"length":0,"stats":{"Line":1}},{"line":30,"address":[],"length":0,"stats":{"Line":1}},{"line":49,"address":[],"length":0,"stats":{"Line":9}},{"line":82,"address":[],"length":0,"stats":{"Line":7}},{"line":83,"address":[],"length":0,"stats":{"Line":1}},{"line":116,"address":[],"length":0,"stats":{"Line":4}},{"line":117,"address":[],"length":0,"stats":{"Line":0}},{"line":150,"address":[],"length":0,"stats":{"Line":3}},{"line":151,"address":[],"length":0,"stats":{"Line":0}},{"line":181,"address":[],"length":0,"stats":{"Line":1}},{"line":182,"address":[],"length":0,"stats":{"Line":1}}],"covered":11,"coverable":13},{"path":["/","home","botahamec","Projects","happylock","src","poisonable","flag.rs"],"content":"#[cfg(panic = \"unwind\")]\nuse std::sync::atomic::{AtomicBool, Ordering::Relaxed};\n\nuse super::PoisonFlag;\n\n#[cfg(panic = \"unwind\")]\nimpl PoisonFlag {\n\tpub const fn new() -\u003e Self {\n\t\tSelf(AtomicBool::new(false))\n\t}\n\n\tpub fn is_poisoned(\u0026self) -\u003e bool {\n\t\tself.0.load(Relaxed)\n\t}\n\n\tpub fn clear_poison(\u0026self) {\n\t\tself.0.store(false, Relaxed)\n\t}\n\n\tpub fn poison(\u0026self) {\n\t\tself.0.store(true, Relaxed);\n\t}\n}\n\n#[cfg(not(panic = \"unwind\"))]\nimpl PoisonFlag {\n\tpub const fn new() -\u003e Self {\n\t\tSelf()\n\t}\n\n\t#[mutants::skip] // None of the tests have panic = \"abort\", so this can't be tested\n\t#[cfg(not(tarpaulin_include))]\n\tpub fn is_poisoned(\u0026self) -\u003e bool {\n\t\tfalse\n\t}\n\n\tpub fn clear_poison(\u0026self) {\n\t\t()\n\t}\n\n\tpub fn poison(\u0026self) {\n\t\t()\n\t}\n}\n","traces":[{"line":8,"address":[531984],"length":1,"stats":{"Line":12}},{"line":9,"address":[532417],"length":1,"stats":{"Line":14}},{"line":12,"address":[508448],"length":1,"stats":{"Line":12}},{"line":13,"address":[532041],"length":1,"stats":{"Line":11}},{"line":16,"address":[546784],"length":1,"stats":{"Line":1}},{"line":17,"address":[508489],"length":1,"stats":{"Line":1}},{"line":20,"address":[554944],"length":1,"stats":{"Line":9}},{"line":21,"address":[508521],"length":1,"stats":{"Line":9}}],"covered":8,"coverable":8},{"path":["/","home","botahamec","Projects","happylock","src","poisonable","guard.rs"],"content":"use std::fmt::{Debug, Display};\nuse std::hash::Hash;\nuse std::marker::PhantomData;\nuse std::ops::{Deref, DerefMut};\n\nuse super::{PoisonFlag, PoisonGuard, PoisonRef};\n\nimpl\u003c'a, Guard\u003e PoisonRef\u003c'a, Guard\u003e {\n\t// This is used so that we don't keep accidentally adding the flag reference\n\tpub(super) const fn new(flag: \u0026'a PoisonFlag, guard: Guard) -\u003e Self {\n\t\tSelf {\n\t\t\tguard,\n\t\t\t#[cfg(panic = \"unwind\")]\n\t\t\tflag,\n\t\t\t_phantom: PhantomData,\n\t\t}\n\t}\n}\n\nimpl\u003cGuard\u003e Drop for PoisonRef\u003c'_, Guard\u003e {\n\tfn drop(\u0026mut self) {\n\t\t#[cfg(panic = \"unwind\")]\n\t\tif std::thread::panicking() {\n\t\t\tself.flag.poison();\n\t\t}\n\t}\n}\n\n#[mutants::skip] // hashing involves RNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard: Hash\u003e Hash for PoisonRef\u003c'_, Guard\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.guard.hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard: Debug\u003e Debug for PoisonRef\u003c'_, Guard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cGuard: Display\u003e Display for PoisonRef\u003c'_, Guard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cGuard\u003e Deref for PoisonRef\u003c'_, Guard\u003e {\n\ttype Target = Guard;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t\u0026self.guard\n\t}\n}\n\nimpl\u003cGuard\u003e DerefMut for PoisonRef\u003c'_, Guard\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t\u0026mut self.guard\n\t}\n}\n\nimpl\u003cGuard\u003e AsRef\u003cGuard\u003e for PoisonRef\u003c'_, Guard\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026Guard {\n\t\t\u0026self.guard\n\t}\n}\n\nimpl\u003cGuard\u003e AsMut\u003cGuard\u003e for PoisonRef\u003c'_, Guard\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut Guard {\n\t\t\u0026mut self.guard\n\t}\n}\n\n#[mutants::skip] // hashing involves RNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard: Hash\u003e Hash for PoisonGuard\u003c'_, Guard\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.guard.hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard: Debug\u003e Debug for PoisonGuard\u003c'_, Guard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026self.guard, f)\n\t}\n}\n\nimpl\u003cGuard: Display\u003e Display for PoisonGuard\u003c'_, Guard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026self.guard, f)\n\t}\n}\n\nimpl\u003cT, Guard: Deref\u003cTarget = T\u003e\u003e Deref for PoisonGuard\u003c'_, Guard\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t#[allow(clippy::explicit_auto_deref)] // fixing this results in a compiler error\n\t\t\u0026*self.guard.guard\n\t}\n}\n\nimpl\u003cT, Guard: DerefMut\u003cTarget = T\u003e\u003e DerefMut for PoisonGuard\u003c'_, Guard\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t#[allow(clippy::explicit_auto_deref)] // fixing this results in a compiler error\n\t\t\u0026mut *self.guard.guard\n\t}\n}\n\nimpl\u003cGuard\u003e AsRef\u003cGuard\u003e for PoisonGuard\u003c'_, Guard\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026Guard {\n\t\t\u0026self.guard.guard\n\t}\n}\n\nimpl\u003cGuard\u003e AsMut\u003cGuard\u003e for PoisonGuard\u003c'_, Guard\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut Guard {\n\t\t\u0026mut self.guard.guard\n\t}\n}\n","traces":[{"line":10,"address":[863520,863552,863616,863584],"length":1,"stats":{"Line":4}},{"line":21,"address":[],"length":0,"stats":{"Line":4}},{"line":22,"address":[],"length":0,"stats":{"Line":0}},{"line":23,"address":[],"length":0,"stats":{"Line":4}},{"line":24,"address":[],"length":0,"stats":{"Line":3}},{"line":46,"address":[],"length":0,"stats":{"Line":1}},{"line":47,"address":[],"length":0,"stats":{"Line":1}},{"line":54,"address":[],"length":0,"stats":{"Line":3}},{"line":55,"address":[],"length":0,"stats":{"Line":0}},{"line":60,"address":[],"length":0,"stats":{"Line":2}},{"line":61,"address":[],"length":0,"stats":{"Line":0}},{"line":66,"address":[],"length":0,"stats":{"Line":1}},{"line":67,"address":[],"length":0,"stats":{"Line":0}},{"line":72,"address":[],"length":0,"stats":{"Line":1}},{"line":73,"address":[],"length":0,"stats":{"Line":0}},{"line":94,"address":[],"length":0,"stats":{"Line":1}},{"line":95,"address":[],"length":0,"stats":{"Line":1}},{"line":102,"address":[],"length":0,"stats":{"Line":2}},{"line":104,"address":[],"length":0,"stats":{"Line":2}},{"line":109,"address":[],"length":0,"stats":{"Line":3}},{"line":111,"address":[],"length":0,"stats":{"Line":3}},{"line":116,"address":[],"length":0,"stats":{"Line":1}},{"line":117,"address":[],"length":0,"stats":{"Line":0}},{"line":122,"address":[],"length":0,"stats":{"Line":1}},{"line":123,"address":[],"length":0,"stats":{"Line":0}}],"covered":18,"coverable":25},{"path":["/","home","botahamec","Projects","happylock","src","poisonable","poisonable.rs"],"content":"use std::panic::{RefUnwindSafe, UnwindSafe};\n\nuse crate::handle_unwind::handle_unwind;\nuse crate::lockable::{\n\tLockable, LockableGetMut, LockableIntoInner, OwnedLockable, RawLock, Sharable,\n};\nuse crate::{Keyable, ThreadKey};\n\nuse super::{\n\tPoisonError, PoisonFlag, PoisonGuard, PoisonRef, PoisonResult, Poisonable,\n\tTryLockPoisonableError, TryLockPoisonableResult,\n};\n\nunsafe impl\u003cL: Lockable + RawLock\u003e RawLock for Poisonable\u003cL\u003e {\n\t#[mutants::skip] // this should never run\n\t#[cfg(not(tarpaulin_include))]\n\tfn poison(\u0026self) {\n\t\tself.inner.poison()\n\t}\n\n\tunsafe fn raw_write(\u0026self) {\n\t\tself.inner.raw_write()\n\t}\n\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool {\n\t\tself.inner.raw_try_write()\n\t}\n\n\tunsafe fn raw_unlock_write(\u0026self) {\n\t\tself.inner.raw_unlock_write()\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tself.inner.raw_read()\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tself.inner.raw_try_read()\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tself.inner.raw_unlock_read()\n\t}\n}\n\nunsafe impl\u003cL: Lockable\u003e Lockable for Poisonable\u003cL\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= PoisonResult\u003cPoisonRef\u003c'g, L::Guard\u003c'g\u003e\u003e\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= PoisonResult\u003cL::DataMut\u003c'a\u003e\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tself.inner.get_ptrs(ptrs)\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tlet ref_guard = PoisonRef::new(\u0026self.poisoned, self.inner.guard());\n\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(ref_guard))\n\t\t} else {\n\t\t\tOk(ref_guard)\n\t\t}\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(self.inner.data_mut()))\n\t\t} else {\n\t\t\tOk(self.inner.data_mut())\n\t\t}\n\t}\n}\n\nunsafe impl\u003cL: Sharable\u003e Sharable for Poisonable\u003cL\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= PoisonResult\u003cPoisonRef\u003c'g, L::ReadGuard\u003c'g\u003e\u003e\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= PoisonResult\u003cL::DataRef\u003c'a\u003e\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tlet ref_guard = PoisonRef::new(\u0026self.poisoned, self.inner.read_guard());\n\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(ref_guard))\n\t\t} else {\n\t\t\tOk(ref_guard)\n\t\t}\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(self.inner.data_ref()))\n\t\t} else {\n\t\t\tOk(self.inner.data_ref())\n\t\t}\n\t}\n}\n\nunsafe impl\u003cL: OwnedLockable\u003e OwnedLockable for Poisonable\u003cL\u003e {}\n\n// AsMut won't work here because we don't strictly return a \u0026mut T\n// LockableGetMut is the next best thing\nimpl\u003cL: LockableGetMut\u003e LockableGetMut for Poisonable\u003cL\u003e {\n\ttype Inner\u003c'a\u003e\n\t\t= PoisonResult\u003cL::Inner\u003c'a\u003e\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(self.inner.get_mut()))\n\t\t} else {\n\t\t\tOk(self.inner.get_mut())\n\t\t}\n\t}\n}\n\nimpl\u003cL: LockableIntoInner\u003e LockableIntoInner for Poisonable\u003cL\u003e {\n\ttype Inner = PoisonResult\u003cL::Inner\u003e;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(self.inner.into_inner()))\n\t\t} else {\n\t\t\tOk(self.inner.into_inner())\n\t\t}\n\t}\n}\n\nimpl\u003cL\u003e From\u003cL\u003e for Poisonable\u003cL\u003e {\n\tfn from(value: L) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\nimpl\u003cL\u003e Poisonable\u003cL\u003e {\n\t/// Creates a new `Poisonable`\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, Poisonable};\n\t///\n\t/// let mutex = Poisonable::new(Mutex::new(0));\n\t/// ```\n\tpub const fn new(value: L) -\u003e Self {\n\t\tSelf {\n\t\t\tinner: value,\n\t\t\tpoisoned: PoisonFlag::new(),\n\t\t}\n\t}\n\n\t/// Determines whether the mutex is poisoned.\n\t///\n\t/// If another thread is active, the mutex can still become poisoned at any\n\t/// time. You should not trust a `false` value for program correctness\n\t/// without additional synchronization.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let mutex = Arc::new(Poisonable::new(Mutex::new(0)));\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t///\n\t/// let _ = thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let _lock = c_mutex.lock(key).unwrap();\n\t/// panic!(); // the mutex gets poisoned\n\t/// }).join();\n\t///\n\t/// assert_eq!(mutex.is_poisoned(), true);\n\t/// ```\n\tpub fn is_poisoned(\u0026self) -\u003e bool {\n\t\tself.poisoned.is_poisoned()\n\t}\n\n\t/// Clear the poisoned state from a lock.\n\t///\n\t/// If the lock is poisoned, it will remain poisoned until this function\n\t/// is called. This allows recovering from a poisoned state and marking\n\t/// that it has recovered. For example, if the value is overwritten by a\n\t/// known-good value, then the lock can be marked as un-poisoned. Or\n\t/// possibly, the value could by inspected to determine if it is in a\n\t/// consistent state, and if so the poison is removed.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let mutex = Arc::new(Poisonable::new(Mutex::new(0)));\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t///\n\t/// let _ = thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let _lock = c_mutex.lock(key).unwrap();\n\t/// panic!(); // the mutex gets poisoned\n\t/// }).join();\n\t///\n\t/// assert_eq!(mutex.is_poisoned(), true);\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let x = mutex.lock(key).unwrap_or_else(|mut e| {\n\t/// **e.get_mut() = 1;\n\t/// mutex.clear_poison();\n\t/// e.into_inner()\n\t/// });\n\t///\n\t/// assert_eq!(mutex.is_poisoned(), false);\n\t/// assert_eq!(*x, 1);\n\t/// ```\n\tpub fn clear_poison(\u0026self) {\n\t\tself.poisoned.clear_poison()\n\t}\n\n\t/// Consumes this `Poisonable`, returning the underlying lock.\n\t///\n\t/// This consumes the `Poisonable` and returns ownership of the lock, which\n\t/// means that the `Poisonable` can still be `RefUnwindSafe`.\n\t///\n\t/// # Errors\n\t///\n\t/// If another user of this lock panicked while holding the lock, then this\n\t/// call will return an error instead.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, Poisonable};\n\t///\n\t/// let mutex = Poisonable::new(Mutex::new(0));\n\t/// assert_eq!(mutex.into_child().unwrap().into_inner(), 0);\n\t/// ```\n\tpub fn into_child(self) -\u003e PoisonResult\u003cL\u003e {\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(self.inner))\n\t\t} else {\n\t\t\tOk(self.inner)\n\t\t}\n\t}\n\n\t/// Returns a mutable reference to the underlying lock.\n\t///\n\t/// This can be implemented while still being `RefUnwindSafe` because\n\t/// it requires a mutable reference.\n\t///\n\t/// # Errors\n\t///\n\t/// If another user of this lock panicked while holding the lock, then\n\t/// this call will return an error instead.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut mutex = Poisonable::new(Mutex::new(0));\n\t/// *mutex.child_mut().unwrap().as_mut() = 10;\n\t/// assert_eq!(*mutex.lock(key).unwrap(), 10);\n\t/// ```\n\tpub fn child_mut(\u0026mut self) -\u003e PoisonResult\u003c\u0026mut L\u003e {\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(\u0026mut self.inner))\n\t\t} else {\n\t\t\tOk(\u0026mut self.inner)\n\t\t}\n\t}\n\n\t// NOTE: `child_ref` isn't implemented because it would make this not `RefUnwindSafe`\n\t//\n}\n\nimpl\u003cL: Lockable\u003e Poisonable\u003cL\u003e {\n\t/// Creates a guard for the poisonable, without locking it\n\tunsafe fn guard(\u0026self, key: ThreadKey) -\u003e PoisonResult\u003cPoisonGuard\u003c'_, L::Guard\u003c'_\u003e\u003e\u003e {\n\t\tlet guard = PoisonGuard {\n\t\t\tguard: PoisonRef::new(\u0026self.poisoned, self.inner.guard()),\n\t\t\tkey,\n\t\t};\n\n\t\tif self.is_poisoned() {\n\t\t\treturn Err(PoisonError::new(guard));\n\t\t}\n\n\t\tOk(guard)\n\t}\n}\n\nimpl\u003cL: Lockable + RawLock\u003e Poisonable\u003cL\u003e {\n\tpub fn scoped_lock\u003c'a, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: impl Keyable,\n\t\tf: impl Fn(\u003cSelf as Lockable\u003e::DataMut\u003c'a\u003e) -\u003e R,\n\t) -\u003e R {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_write();\n\n\t\t\t// safety: the data was just locked\n\t\t\tlet r = handle_unwind(\n\t\t\t\t|| f(self.data_mut()),\n\t\t\t\t|| {\n\t\t\t\t\tself.poisoned.poison();\n\t\t\t\t\tself.raw_unlock_write();\n\t\t\t\t},\n\t\t\t);\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock_write();\n\n\t\t\tdrop(key); // ensure the key stays alive long enough\n\n\t\t\tr\n\t\t}\n\t}\n\n\tpub fn scoped_try_lock\u003c'a, Key: Keyable, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(\u003cSelf as Lockable\u003e::DataMut\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_write() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we just locked the collection\n\t\t\tlet r = handle_unwind(\n\t\t\t\t|| f(self.data_mut()),\n\t\t\t\t|| {\n\t\t\t\t\tself.poisoned.poison();\n\t\t\t\t\tself.raw_unlock_write();\n\t\t\t\t},\n\t\t\t);\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock_write();\n\n\t\t\tdrop(key); // ensures the key stays valid long enough\n\n\t\t\tOk(r)\n\t\t}\n\t}\n\n\t/// Acquires the lock, blocking the current thread until it is ok to do so.\n\t///\n\t/// This function will block the current thread until it is available to\n\t/// acquire the mutex. Upon returning, the thread is the only thread with\n\t/// the lock held. An RAII guard is returned to allow scoped unlock of the\n\t/// lock. When the guard goes out of scope, the mutex will be unlocked.\n\t///\n\t/// # Errors\n\t///\n\t/// If another use of this mutex panicked while holding the mutex, then\n\t/// this call will return an error once the mutex is acquired.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let mutex = Arc::new(Poisonable::new(Mutex::new(0)));\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t///\n\t/// thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// *c_mutex.lock(key).unwrap() = 10;\n\t/// }).join().expect(\"thread::spawn failed\");\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// assert_eq!(*mutex.lock(key).unwrap(), 10);\n\t/// ```\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e PoisonResult\u003cPoisonGuard\u003c'_, L::Guard\u003c'_\u003e\u003e\u003e {\n\t\tunsafe {\n\t\t\tself.inner.raw_write();\n\t\t\tself.guard(key)\n\t\t}\n\t}\n\n\t/// Attempts to acquire this lock.\n\t///\n\t/// If the lock could not be acquired at this time, then [`Err`] is\n\t/// returned. Otherwise, an RAII guard is returned. The lock will be\n\t/// unlocked when the guard is dropped.\n\t///\n\t/// This function does not block.\n\t///\n\t/// # Errors\n\t///\n\t/// If another user of this mutex panicked while holding the mutex, then\n\t/// this call will return the [`Poisoned`] error if the mutex would\n\t/// otherwise be acquired.\n\t///\n\t/// If the mutex could not be acquired because it is already locked, then\n\t/// this call will return the [`WouldBlock`] error.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let mutex = Arc::new(Poisonable::new(Mutex::new(0)));\n\t/// let c_mutex = Arc::clone(\u0026mutex);\n\t///\n\t/// thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut lock = c_mutex.try_lock(key);\n\t/// if let Ok(mut mutex) = lock {\n\t/// *mutex = 10;\n\t/// } else {\n\t/// println!(\"try_lock failed\");\n\t/// }\n\t/// }).join().expect(\"thread::spawn failed\");\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// assert_eq!(*mutex.lock(key).unwrap(), 10);\n\t/// ```\n\t///\n\t/// [`Poisoned`]: `TryLockPoisonableError::Poisoned`\n\t/// [`WouldBlock`]: `TryLockPoisonableError::WouldBlock`\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e TryLockPoisonableResult\u003c'_, L::Guard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\tif self.inner.raw_try_write() {\n\t\t\t\tOk(self.guard(key)?)\n\t\t\t} else {\n\t\t\t\tErr(TryLockPoisonableError::WouldBlock(key))\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Consumes the [`PoisonGuard`], and consequently unlocks its `Poisonable`.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, Mutex, Poisonable};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mutex = Poisonable::new(Mutex::new(0));\n\t///\n\t/// let mut guard = mutex.lock(key).unwrap();\n\t/// *guard += 20;\n\t///\n\t/// let key = Poisonable::\u003cMutex\u003c_\u003e\u003e::unlock(guard);\n\t/// ```\n\tpub fn unlock\u003c'flag\u003e(guard: PoisonGuard\u003c'flag, L::Guard\u003c'flag\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: Sharable + RawLock\u003e Poisonable\u003cL\u003e {\n\tunsafe fn read_guard(\u0026self, key: ThreadKey) -\u003e PoisonResult\u003cPoisonGuard\u003c'_, L::ReadGuard\u003c'_\u003e\u003e\u003e {\n\t\tlet guard = PoisonGuard {\n\t\t\tguard: PoisonRef::new(\u0026self.poisoned, self.inner.read_guard()),\n\t\t\tkey,\n\t\t};\n\n\t\tif self.is_poisoned() {\n\t\t\treturn Err(PoisonError::new(guard));\n\t\t}\n\n\t\tOk(guard)\n\t}\n\n\tpub fn scoped_read\u003c'a, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: impl Keyable,\n\t\tf: impl Fn(\u003cSelf as Sharable\u003e::DataRef\u003c'a\u003e) -\u003e R,\n\t) -\u003e R {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_read();\n\n\t\t\t// safety: the data was just locked\n\t\t\tlet r = handle_unwind(\n\t\t\t\t|| f(self.data_ref()),\n\t\t\t\t|| {\n\t\t\t\t\tself.poisoned.poison();\n\t\t\t\t\tself.raw_unlock_read();\n\t\t\t\t},\n\t\t\t);\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock_read();\n\n\t\t\tdrop(key); // ensure the key stays alive long enough\n\n\t\t\tr\n\t\t}\n\t}\n\n\tpub fn scoped_try_read\u003c'a, Key: Keyable, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(\u003cSelf as Sharable\u003e::DataRef\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_read() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we just locked the collection\n\t\t\tlet r = handle_unwind(\n\t\t\t\t|| f(self.data_ref()),\n\t\t\t\t|| {\n\t\t\t\t\tself.poisoned.poison();\n\t\t\t\t\tself.raw_unlock_read();\n\t\t\t\t},\n\t\t\t);\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock_read();\n\n\t\t\tdrop(key); // ensures the key stays valid long enough\n\n\t\t\tOk(r)\n\t\t}\n\t}\n\n\t/// Locks with shared read access, blocking the current thread until it can\n\t/// be acquired.\n\t///\n\t/// This function will block the current thread until there are no writers\n\t/// which hold the lock. This method does not provide any guarantee with\n\t/// respect to the ordering of contentious readers or writers will acquire\n\t/// the lock.\n\t///\n\t/// # Errors\n\t///\n\t/// If another use of this lock panicked while holding the lock, then\n\t/// this call will return an error once the lock is acquired.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t///\n\t/// use happylock::{RwLock, Poisonable, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = Arc::new(Poisonable::new(RwLock::new(0)));\n\t/// let c_lock = Arc::clone(\u0026lock);\n\t///\n\t/// let n = lock.read(key).unwrap();\n\t/// assert_eq!(*n, 0);\n\t///\n\t/// thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// assert!(c_lock.read(key).is_ok());\n\t/// }).join().expect(\"thread::spawn failed\");\n\t/// ```\n\tpub fn read(\u0026self, key: ThreadKey) -\u003e PoisonResult\u003cPoisonGuard\u003c'_, L::ReadGuard\u003c'_\u003e\u003e\u003e {\n\t\tunsafe {\n\t\t\tself.inner.raw_read();\n\t\t\tself.read_guard(key)\n\t\t}\n\t}\n\n\t/// Attempts to acquire the lock with shared read access.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is returned.\n\t/// Otherwise, an RAII guard is returned which will release the shared access\n\t/// when it is dropped.\n\t///\n\t/// This function does not block.\n\t///\n\t/// This function does not provide any guarantees with respect to the ordering\n\t/// of whether contentious readers or writers will acquire the lock first.\n\t///\n\t/// # Errors\n\t///\n\t/// This function will return the [`Poisoned`] error if the lock is\n\t/// poisoned. A [`Poisonable`] is poisoned whenever a writer panics while\n\t/// holding an exclusive lock. `Poisoned` will only be returned if the lock\n\t/// would have otherwise been acquired.\n\t///\n\t/// This function will return the [`WouldBlock`] error if the lock could\n\t/// not be acquired because it was already locked exclusively.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Poisonable, RwLock, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = Poisonable::new(RwLock::new(1));\n\t///\n\t/// match lock.try_read(key) {\n\t/// Ok(n) =\u003e assert_eq!(*n, 1),\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t/// ```\n\t///\n\t/// [`Poisoned`]: `TryLockPoisonableError::Poisoned`\n\t/// [`WouldBlock`]: `TryLockPoisonableError::WouldBlock`\n\tpub fn try_read(\u0026self, key: ThreadKey) -\u003e TryLockPoisonableResult\u003c'_, L::ReadGuard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\tif self.inner.raw_try_read() {\n\t\t\t\tOk(self.read_guard(key)?)\n\t\t\t} else {\n\t\t\t\tErr(TryLockPoisonableError::WouldBlock(key))\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Consumes the [`PoisonGuard`], and consequently unlocks its underlying lock.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, RwLock, Poisonable};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = Poisonable::new(RwLock::new(0));\n\t///\n\t/// let mut guard = lock.read(key).unwrap();\n\t/// let key = Poisonable::\u003cRwLock\u003c_\u003e\u003e::unlock_read(guard);\n\t/// ```\n\tpub fn unlock_read\u003c'flag\u003e(guard: PoisonGuard\u003c'flag, L::ReadGuard\u003c'flag\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: LockableIntoInner\u003e Poisonable\u003cL\u003e {\n\t/// Consumes this `Poisonable`, returning the underlying data.\n\t///\n\t/// # Errors\n\t///\n\t/// If another user of this lock panicked while holding the lock, then this\n\t/// call will return an error instead.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, Poisonable};\n\t///\n\t/// let mutex = Poisonable::new(Mutex::new(0));\n\t/// assert_eq!(mutex.into_inner().unwrap(), 0);\n\t/// ```\n\tpub fn into_inner(self) -\u003e PoisonResult\u003cL::Inner\u003e {\n\t\tLockableIntoInner::into_inner(self)\n\t}\n}\n\nimpl\u003cL: LockableGetMut + RawLock\u003e Poisonable\u003cL\u003e {\n\t/// Returns a mutable reference to the underlying data.\n\t///\n\t/// Since this call borrows the `Poisonable` mutably, no actual locking\n\t/// needs to take place - the mutable borrow statically guarantees no locks\n\t/// exist.\n\t///\n\t/// # Errors\n\t///\n\t/// If another user of this lock panicked while holding the lock, then\n\t/// this call will return an error instead.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut mutex = Poisonable::new(Mutex::new(0));\n\t/// *mutex.get_mut().unwrap() = 10;\n\t/// assert_eq!(*mutex.lock(key).unwrap(), 10);\n\t/// ```\n\tpub fn get_mut(\u0026mut self) -\u003e PoisonResult\u003cL::Inner\u003c'_\u003e\u003e {\n\t\tLockableGetMut::get_mut(self)\n\t}\n}\n\nimpl\u003cL: UnwindSafe\u003e RefUnwindSafe for Poisonable\u003cL\u003e {}\nimpl\u003cL: UnwindSafe\u003e UnwindSafe for Poisonable\u003cL\u003e {}\n","traces":[{"line":21,"address":[],"length":0,"stats":{"Line":1}},{"line":22,"address":[863957],"length":1,"stats":{"Line":1}},{"line":25,"address":[],"length":0,"stats":{"Line":2}},{"line":26,"address":[],"length":0,"stats":{"Line":2}},{"line":29,"address":[],"length":0,"stats":{"Line":2}},{"line":30,"address":[864021,864005],"length":1,"stats":{"Line":2}},{"line":33,"address":[],"length":0,"stats":{"Line":1}},{"line":34,"address":[],"length":0,"stats":{"Line":1}},{"line":37,"address":[],"length":0,"stats":{"Line":1}},{"line":38,"address":[],"length":0,"stats":{"Line":1}},{"line":41,"address":[],"length":0,"stats":{"Line":1}},{"line":42,"address":[],"length":0,"stats":{"Line":1}},{"line":57,"address":[],"length":0,"stats":{"Line":4}},{"line":58,"address":[],"length":0,"stats":{"Line":4}},{"line":61,"address":[],"length":0,"stats":{"Line":2}},{"line":62,"address":[],"length":0,"stats":{"Line":2}},{"line":64,"address":[],"length":0,"stats":{"Line":7}},{"line":65,"address":[864413,864701,864641,864353],"length":1,"stats":{"Line":2}},{"line":67,"address":[],"length":0,"stats":{"Line":2}},{"line":71,"address":[],"length":0,"stats":{"Line":2}},{"line":72,"address":[],"length":0,"stats":{"Line":4}},{"line":73,"address":[864902,864806],"length":1,"stats":{"Line":1}},{"line":75,"address":[],"length":0,"stats":{"Line":2}},{"line":91,"address":[865220,864944,865198],"length":1,"stats":{"Line":1}},{"line":92,"address":[],"length":0,"stats":{"Line":1}},{"line":94,"address":[],"length":0,"stats":{"Line":4}},{"line":95,"address":[],"length":0,"stats":{"Line":2}},{"line":97,"address":[865089],"length":1,"stats":{"Line":1}},{"line":101,"address":[],"length":0,"stats":{"Line":1}},{"line":102,"address":[],"length":0,"stats":{"Line":2}},{"line":103,"address":[],"length":0,"stats":{"Line":1}},{"line":105,"address":[],"length":0,"stats":{"Line":1}},{"line":120,"address":[],"length":0,"stats":{"Line":1}},{"line":121,"address":[],"length":0,"stats":{"Line":2}},{"line":122,"address":[865382],"length":1,"stats":{"Line":1}},{"line":124,"address":[],"length":0,"stats":{"Line":1}},{"line":132,"address":[865424,865739],"length":1,"stats":{"Line":1}},{"line":133,"address":[],"length":0,"stats":{"Line":3}},{"line":134,"address":[],"length":0,"stats":{"Line":2}},{"line":136,"address":[],"length":0,"stats":{"Line":2}},{"line":142,"address":[],"length":0,"stats":{"Line":1}},{"line":143,"address":[],"length":0,"stats":{"Line":2}},{"line":157,"address":[],"length":0,"stats":{"Line":3}},{"line":160,"address":[],"length":0,"stats":{"Line":6}},{"line":189,"address":[],"length":0,"stats":{"Line":4}},{"line":190,"address":[866197,866229,866261],"length":1,"stats":{"Line":4}},{"line":231,"address":[],"length":0,"stats":{"Line":3}},{"line":232,"address":[866293,866309,866325],"length":1,"stats":{"Line":3}},{"line":253,"address":[],"length":0,"stats":{"Line":1}},{"line":254,"address":[],"length":0,"stats":{"Line":4}},{"line":255,"address":[],"length":0,"stats":{"Line":2}},{"line":257,"address":[866442],"length":1,"stats":{"Line":1}},{"line":281,"address":[],"length":0,"stats":{"Line":0}},{"line":282,"address":[],"length":0,"stats":{"Line":0}},{"line":283,"address":[],"length":0,"stats":{"Line":0}},{"line":285,"address":[],"length":0,"stats":{"Line":0}},{"line":295,"address":[],"length":0,"stats":{"Line":3}},{"line":297,"address":[867162,867090,867578,866746,867506,866674],"length":1,"stats":{"Line":6}},{"line":301,"address":[867630,866848,867264,866798,867214,867680],"length":1,"stats":{"Line":7}},{"line":302,"address":[866956,866896,867788,867372,867728,867312],"length":1,"stats":{"Line":6}},{"line":305,"address":[866859,867691,867275],"length":1,"stats":{"Line":4}},{"line":310,"address":[868002,867872,868173,868195,868048,868024],"length":1,"stats":{"Line":3}},{"line":317,"address":[],"length":0,"stats":{"Line":2}},{"line":321,"address":[],"length":0,"stats":{"Line":4}},{"line":322,"address":[824768,824800],"length":1,"stats":{"Line":1}},{"line":323,"address":[824805,824773],"length":1,"stats":{"Line":1}},{"line":324,"address":[824786,824818],"length":1,"stats":{"Line":1}},{"line":329,"address":[],"length":0,"stats":{"Line":1}},{"line":331,"address":[868147,867976],"length":1,"stats":{"Line":1}},{"line":333,"address":[],"length":0,"stats":{"Line":0}},{"line":337,"address":[868416,868392,868578,868208,868370,868600],"length":1,"stats":{"Line":2}},{"line":344,"address":[868265,868430,868222,868473],"length":1,"stats":{"Line":4}},{"line":345,"address":[],"length":0,"stats":{"Line":1}},{"line":350,"address":[],"length":0,"stats":{"Line":2}},{"line":351,"address":[824992,824960],"length":1,"stats":{"Line":0}},{"line":352,"address":[824997,824965],"length":1,"stats":{"Line":0}},{"line":353,"address":[],"length":0,"stats":{"Line":0}},{"line":358,"address":[],"length":0,"stats":{"Line":1}},{"line":360,"address":[868344,868552],"length":1,"stats":{"Line":1}},{"line":362,"address":[868564,868356],"length":1,"stats":{"Line":1}},{"line":397,"address":[868912,868768,868730,869018,869040,868624,868896,868752,868874],"length":1,"stats":{"Line":3}},{"line":399,"address":[],"length":0,"stats":{"Line":3}},{"line":400,"address":[868708,868852,868996],"length":1,"stats":{"Line":3}},{"line":448,"address":[],"length":0,"stats":{"Line":0}},{"line":450,"address":[],"length":0,"stats":{"Line":0}},{"line":451,"address":[],"length":0,"stats":{"Line":0}},{"line":453,"address":[],"length":0,"stats":{"Line":0}},{"line":473,"address":[],"length":0,"stats":{"Line":1}},{"line":474,"address":[869070],"length":1,"stats":{"Line":1}},{"line":475,"address":[],"length":0,"stats":{"Line":0}},{"line":480,"address":[869152,869521],"length":1,"stats":{"Line":1}},{"line":482,"address":[869274,869202],"length":1,"stats":{"Line":2}},{"line":486,"address":[869326,869376],"length":1,"stats":{"Line":2}},{"line":487,"address":[],"length":0,"stats":{"Line":0}},{"line":490,"address":[869387],"length":1,"stats":{"Line":1}},{"line":493,"address":[869880,869858,869728,869693,869568,869715],"length":1,"stats":{"Line":3}},{"line":500,"address":[869747,869582],"length":1,"stats":{"Line":2}},{"line":504,"address":[],"length":0,"stats":{"Line":4}},{"line":505,"address":[],"length":0,"stats":{"Line":1}},{"line":506,"address":[],"length":0,"stats":{"Line":1}},{"line":507,"address":[825170,825202],"length":1,"stats":{"Line":1}},{"line":512,"address":[],"length":0,"stats":{"Line":1}},{"line":514,"address":[869667,869832],"length":1,"stats":{"Line":1}},{"line":516,"address":[],"length":0,"stats":{"Line":0}},{"line":520,"address":[870296,870274,870112,869904,870066,870088],"length":1,"stats":{"Line":2}},{"line":527,"address":[870126,870169,869918,869961],"length":1,"stats":{"Line":4}},{"line":528,"address":[],"length":0,"stats":{"Line":1}},{"line":533,"address":[],"length":0,"stats":{"Line":2}},{"line":534,"address":[],"length":0,"stats":{"Line":0}},{"line":535,"address":[825381,825349],"length":1,"stats":{"Line":0}},{"line":536,"address":[],"length":0,"stats":{"Line":0}},{"line":541,"address":[],"length":0,"stats":{"Line":1}},{"line":543,"address":[870040,870248],"length":1,"stats":{"Line":1}},{"line":545,"address":[],"length":0,"stats":{"Line":1}},{"line":582,"address":[],"length":0,"stats":{"Line":1}},{"line":584,"address":[],"length":0,"stats":{"Line":1}},{"line":585,"address":[],"length":0,"stats":{"Line":1}},{"line":626,"address":[],"length":0,"stats":{"Line":0}},{"line":628,"address":[],"length":0,"stats":{"Line":0}},{"line":629,"address":[],"length":0,"stats":{"Line":0}},{"line":631,"address":[],"length":0,"stats":{"Line":0}},{"line":649,"address":[],"length":0,"stats":{"Line":0}},{"line":650,"address":[],"length":0,"stats":{"Line":0}},{"line":651,"address":[],"length":0,"stats":{"Line":0}},{"line":671,"address":[],"length":0,"stats":{"Line":1}},{"line":672,"address":[],"length":0,"stats":{"Line":1}},{"line":698,"address":[],"length":0,"stats":{"Line":1}},{"line":699,"address":[],"length":0,"stats":{"Line":1}}],"covered":103,"coverable":128},{"path":["/","home","botahamec","Projects","happylock","src","poisonable.rs"],"content":"use std::marker::PhantomData;\nuse std::sync::atomic::AtomicBool;\n\nuse crate::ThreadKey;\n\nmod error;\nmod flag;\nmod guard;\nmod poisonable;\n\n/// A flag indicating if a lock is poisoned or not. The implementation differs\n/// depending on whether panics are set to unwind or abort.\n#[derive(Debug, Default)]\npub(crate) struct PoisonFlag(#[cfg(panic = \"unwind\")] AtomicBool);\n\n/// A wrapper around [`Lockable`] types which will enable poisoning.\n///\n/// A lock is \"poisoned\" when the thread panics while holding the lock. Once a\n/// lock is poisoned, all other threads are unable to access the data by\n/// default, because the data may be tainted (some invariant of the data might\n/// not be upheld).\n///\n/// The [`lock`] and [`try_lock`] methods return a [`Result`] which indicates\n/// whether the lock has been poisoned or not. The [`PoisonError`] type has an\n/// [`into_inner`] method which will return the guard that normally would have\n/// been returned for a successful lock. This allows access to the data,\n/// despite the lock being poisoned.\n///\n/// Alternatively, there is also a [`clear_poison`] method, which should\n/// indicate that all invariants of the underlying data are upheld, so that\n/// subsequent calls may still return [`Ok`].\n///\n/// [`Lockable`]: `crate::lockable::Lockable`\n/// [`lock`]: `Poisonable::lock`\n/// [`try_lock`]: `Poisonable::try_lock`\n/// [`into_inner`]: `PoisonError::into_inner`\n/// [`clear_poison`]: `Poisonable::clear_poison`\n#[derive(Debug, Default)]\npub struct Poisonable\u003cL\u003e {\n\tinner: L,\n\tpoisoned: PoisonFlag,\n}\n\n/// An RAII guard for a [`Poisonable`].\n///\n/// This is similar to a [`PoisonGuard`], except that it does not hold a\n/// [`Keyable`]\n///\n/// [`Keyable`]: `crate::Keyable`\npub struct PoisonRef\u003c'a, G\u003e {\n\tguard: G,\n\t#[cfg(panic = \"unwind\")]\n\tflag: \u0026'a PoisonFlag,\n\t_phantom: PhantomData\u003c\u0026'a ()\u003e,\n}\n\n/// An RAII guard for a [`Poisonable`].\n///\n/// This is created by calling methods like [`Poisonable::lock`].\npub struct PoisonGuard\u003c'a, G\u003e {\n\tguard: PoisonRef\u003c'a, G\u003e,\n\tkey: ThreadKey,\n}\n\n/// A type of error which can be returned when acquiring a [`Poisonable`] lock.\npub struct PoisonError\u003cGuard\u003e {\n\tguard: Guard,\n}\n\n/// An enumeration of possible errors associated with\n/// [`TryLockPoisonableResult`] which can occur while trying to acquire a lock\n/// (i.e.: [`Poisonable::try_lock`]).\npub enum TryLockPoisonableError\u003c'flag, G\u003e {\n\tPoisoned(PoisonError\u003cPoisonGuard\u003c'flag, G\u003e\u003e),\n\tWouldBlock(ThreadKey),\n}\n\n/// A type alias for the result of a lock method which can poisoned.\n///\n/// The [`Ok`] variant of this result indicates that the primitive was not\n/// poisoned, and the primitive was poisoned. Note that the [`Err`] variant\n/// *also* carries the associated guard, and it can be acquired through the\n/// [`into_inner`] method.\n///\n/// [`into_inner`]: `PoisonError::into_inner`\npub type PoisonResult\u003cGuard\u003e = Result\u003cGuard, PoisonError\u003cGuard\u003e\u003e;\n\n/// A type alias for the result of a nonblocking locking method.\n///\n/// For more information, see [`PoisonResult`]. A `TryLockPoisonableResult`\n/// doesn't necessarily hold the associated guard in the [`Err`] type as the\n/// lock might not have been acquired for other reasons.\npub type TryLockPoisonableResult\u003c'flag, G\u003e =\n\tResult\u003cPoisonGuard\u003c'flag, G\u003e, TryLockPoisonableError\u003c'flag, G\u003e\u003e;\n\n#[cfg(test)]\nmod tests {\n\tuse std::sync::Arc;\n\n\tuse super::*;\n\tuse crate::lockable::Lockable;\n\tuse crate::{LockCollection, Mutex, RwLock, ThreadKey};\n\n\t#[test]\n\tfn locking_poisoned_mutex_returns_error_in_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = LockCollection::new(Poisonable::new(Mutex::new(42)));\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet mut guard1 = mutex.lock(key);\n\t\t\t\tlet guard = guard1.as_deref_mut().unwrap();\n\t\t\t\tassert_eq!(**guard, 42);\n\t\t\t\tpanic!();\n\n\t\t\t\t#[allow(unreachable_code)]\n\t\t\t\tdrop(guard1);\n\t\t\t})\n\t\t\t.join()\n\t\t\t.unwrap_err();\n\t\t});\n\n\t\tlet error = mutex.lock(key);\n\t\tlet error = error.as_deref().unwrap_err();\n\t\tassert_eq!(***error.get_ref(), 42);\n\t}\n\n\t#[test]\n\tfn locking_poisoned_rwlock_returns_error_in_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = LockCollection::new(Poisonable::new(RwLock::new(42)));\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet mut guard1 = mutex.read(key);\n\t\t\t\tlet guard = guard1.as_deref_mut().unwrap();\n\t\t\t\tassert_eq!(**guard, 42);\n\t\t\t\tpanic!();\n\n\t\t\t\t#[allow(unreachable_code)]\n\t\t\t\tdrop(guard1);\n\t\t\t})\n\t\t\t.join()\n\t\t\t.unwrap_err();\n\t\t});\n\n\t\tlet error = mutex.read(key);\n\t\tlet error = error.as_deref().unwrap_err();\n\t\tassert_eq!(***error.get_ref(), 42);\n\t}\n\n\t#[test]\n\tfn non_poisoned_get_mut_is_ok() {\n\t\tlet mut mutex = Poisonable::new(Mutex::new(42));\n\t\tlet guard = mutex.get_mut();\n\t\tassert!(guard.is_ok());\n\t\tassert_eq!(*guard.unwrap(), 42);\n\t}\n\n\t#[test]\n\tfn non_poisoned_get_mut_is_err() {\n\t\tlet mut mutex = Poisonable::new(Mutex::new(42));\n\n\t\tlet _ = std::panic::catch_unwind(|| {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t#[allow(unused_variables)]\n\t\t\tlet guard = mutex.lock(key);\n\t\t\tpanic!();\n\t\t\t#[allow(unreachable_code)]\n\t\t\tdrop(guard);\n\t\t});\n\n\t\tlet guard = mutex.get_mut();\n\t\tassert!(guard.is_err());\n\t\tassert_eq!(**guard.unwrap_err().get_ref(), 42);\n\t}\n\n\t#[test]\n\tfn unpoisoned_into_inner() {\n\t\tlet mutex = Poisonable::new(Mutex::new(\"foo\"));\n\t\tassert_eq!(mutex.into_inner().unwrap(), \"foo\");\n\t}\n\n\t#[test]\n\tfn poisoned_into_inner() {\n\t\tlet mutex = Poisonable::from(Mutex::new(\"foo\"));\n\n\t\tstd::panic::catch_unwind(|| {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t#[allow(unused_variables)]\n\t\t\tlet guard = mutex.lock(key);\n\t\t\tpanic!();\n\t\t\t#[allow(unreachable_code)]\n\t\t\tdrop(guard);\n\t\t})\n\t\t.unwrap_err();\n\n\t\tlet error = mutex.into_inner().unwrap_err();\n\t\tassert_eq!(error.into_inner(), \"foo\");\n\t}\n\n\t#[test]\n\tfn unpoisoned_into_child() {\n\t\tlet mutex = Poisonable::new(Mutex::new(\"foo\"));\n\t\tassert_eq!(mutex.into_child().unwrap().into_inner(), \"foo\");\n\t}\n\n\t#[test]\n\tfn poisoned_into_child() {\n\t\tlet mutex = Poisonable::from(Mutex::new(\"foo\"));\n\n\t\tstd::panic::catch_unwind(|| {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t#[allow(unused_variables)]\n\t\t\tlet guard = mutex.lock(key);\n\t\t\tpanic!();\n\t\t\t#[allow(unreachable_code)]\n\t\t\tdrop(guard);\n\t\t})\n\t\t.unwrap_err();\n\n\t\tlet error = mutex.into_child().unwrap_err();\n\t\tassert_eq!(error.into_inner().into_inner(), \"foo\");\n\t}\n\n\t#[test]\n\tfn scoped_lock_can_poison() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = Poisonable::new(Mutex::new(42));\n\n\t\tlet r = std::panic::catch_unwind(|| {\n\t\t\tmutex.scoped_lock(key, |num| {\n\t\t\t\t*num.unwrap() = 56;\n\t\t\t\tpanic!();\n\t\t\t})\n\t\t});\n\t\tassert!(r.is_err());\n\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tassert!(mutex.is_poisoned());\n\t\tmutex.scoped_lock(key, |num| {\n\t\t\tlet Err(error) = num else { panic!() };\n\t\t\tmutex.clear_poison();\n\t\t\tlet guard = error.into_inner();\n\t\t\tassert_eq!(*guard, 56);\n\t\t});\n\t\tassert!(!mutex.is_poisoned());\n\t}\n\n\t#[test]\n\tfn scoped_try_lock_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = Poisonable::new(Mutex::new(42));\n\t\tlet guard = mutex.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = mutex.scoped_try_lock(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn scoped_try_lock_can_succeed() {\n\t\tlet rwlock = Poisonable::new(RwLock::new(42));\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = rwlock.scoped_try_lock(key, |guard| {\n\t\t\t\t\tassert_eq!(*guard.unwrap(), 42);\n\t\t\t\t});\n\t\t\t\tassert!(r.is_ok());\n\t\t\t});\n\t\t});\n\t}\n\n\t#[test]\n\tfn scoped_read_can_poison() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = Poisonable::new(RwLock::new(42));\n\n\t\tlet r = std::panic::catch_unwind(|| {\n\t\t\tmutex.scoped_read(key, |num| {\n\t\t\t\tassert_eq!(*num.unwrap(), 42);\n\t\t\t\tpanic!();\n\t\t\t})\n\t\t});\n\t\tassert!(r.is_err());\n\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tassert!(mutex.is_poisoned());\n\t\tmutex.scoped_read(key, |num| {\n\t\t\tlet Err(error) = num else { panic!() };\n\t\t\tmutex.clear_poison();\n\t\t\tlet guard = error.into_inner();\n\t\t\tassert_eq!(*guard, 42);\n\t\t});\n\t\tassert!(!mutex.is_poisoned());\n\t}\n\n\t#[test]\n\tfn scoped_try_read_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet rwlock = Poisonable::new(RwLock::new(42));\n\t\tlet guard = rwlock.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = rwlock.scoped_try_read(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn scoped_try_read_can_succeed() {\n\t\tlet rwlock = Poisonable::new(RwLock::new(42));\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = rwlock.scoped_try_read(key, |guard| {\n\t\t\t\t\tassert_eq!(*guard.unwrap(), 42);\n\t\t\t\t});\n\t\t\t\tassert!(r.is_ok());\n\t\t\t});\n\t\t});\n\t}\n\n\t#[test]\n\tfn display_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = Poisonable::new(Mutex::new(\"Hello, world!\"));\n\n\t\tlet guard = mutex.lock(key).unwrap();\n\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\");\n\t}\n\n\t#[test]\n\tfn ref_as_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = LockCollection::new(Poisonable::new(Mutex::new(\"foo\")));\n\t\tlet guard = collection.lock(key);\n\t\tlet Ok(ref guard) = guard.as_ref() else {\n\t\t\tpanic!()\n\t\t};\n\t\tassert_eq!(**guard.as_ref(), \"foo\");\n\t}\n\n\t#[test]\n\tfn ref_as_mut() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = LockCollection::new(Poisonable::new(Mutex::new(\"foo\")));\n\t\tlet mut guard1 = collection.lock(key);\n\t\tlet Ok(ref mut guard) = guard1.as_mut() else {\n\t\t\tpanic!()\n\t\t};\n\t\tlet guard = guard.as_mut();\n\t\t**guard = \"bar\";\n\n\t\tlet key = LockCollection::\u003cPoisonable\u003cMutex\u003c_\u003e\u003e\u003e::unlock(guard1);\n\t\tlet guard = collection.lock(key);\n\t\tlet guard = guard.as_deref().unwrap();\n\t\tassert_eq!(*guard.as_ref(), \"bar\");\n\t}\n\n\t#[test]\n\tfn guard_as_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = Poisonable::new(Mutex::new(\"foo\"));\n\t\tlet guard = collection.lock(key);\n\t\tlet Ok(ref guard) = guard.as_ref() else {\n\t\t\tpanic!()\n\t\t};\n\t\tassert_eq!(**guard.as_ref(), \"foo\");\n\t}\n\n\t#[test]\n\tfn guard_as_mut() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = Poisonable::new(Mutex::new(\"foo\"));\n\t\tlet mut guard1 = mutex.lock(key);\n\t\tlet Ok(ref mut guard) = guard1.as_mut() else {\n\t\t\tpanic!()\n\t\t};\n\t\tlet guard = guard.as_mut();\n\t\t**guard = \"bar\";\n\n\t\tlet key = Poisonable::\u003cMutex\u003c_\u003e\u003e::unlock(guard1.unwrap());\n\t\tlet guard = mutex.lock(key);\n\t\tlet guard = guard.as_deref().unwrap();\n\t\tassert_eq!(*guard, \"bar\");\n\t}\n\n\t#[test]\n\tfn deref_mut_in_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = LockCollection::new(Poisonable::new(Mutex::new(42)));\n\t\tlet mut guard1 = collection.lock(key);\n\t\tlet Ok(ref mut guard) = guard1.as_mut() else {\n\t\t\tpanic!()\n\t\t};\n\t\t// TODO make this more convenient\n\t\tassert_eq!(***guard, 42);\n\t\t***guard = 24;\n\n\t\tlet key = LockCollection::\u003cPoisonable\u003cMutex\u003c_\u003e\u003e\u003e::unlock(guard1);\n\t\t_ = collection.lock(key);\n\t}\n\n\t#[test]\n\tfn get_ptrs() {\n\t\tlet mutex = Mutex::new(5);\n\t\tlet poisonable = Poisonable::new(mutex);\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tpoisonable.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tassert!(std::ptr::addr_eq(lock_ptrs[0], \u0026poisonable.inner));\n\t}\n\n\t#[test]\n\tfn clear_poison_for_poisoned_mutex() {\n\t\tlet mutex = Arc::new(Poisonable::new(Mutex::new(0)));\n\t\tlet c_mutex = Arc::clone(\u0026mutex);\n\n\t\tlet _ = std::thread::spawn(move || {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\tlet _lock = c_mutex.lock(key).unwrap();\n\t\t\tpanic!(); // the mutex gets poisoned\n\t\t})\n\t\t.join();\n\n\t\tassert!(mutex.is_poisoned());\n\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet _ = mutex.lock(key).unwrap_or_else(|mut e| {\n\t\t\t**e.get_mut() = 1;\n\t\t\tmutex.clear_poison();\n\t\t\te.into_inner()\n\t\t});\n\n\t\tassert!(!mutex.is_poisoned());\n\t}\n\n\t#[test]\n\tfn clear_poison_for_poisoned_rwlock() {\n\t\tlet lock = Arc::new(Poisonable::new(RwLock::new(0)));\n\t\tlet c_mutex = Arc::clone(\u0026lock);\n\n\t\tlet _ = std::thread::spawn(move || {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\tlet lock = c_mutex.read(key).unwrap();\n\t\t\tassert_eq!(*lock, 42);\n\t\t\tpanic!(); // the mutex gets poisoned\n\t\t})\n\t\t.join();\n\n\t\tassert!(lock.is_poisoned());\n\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet _ = lock.lock(key).unwrap_or_else(|mut e| {\n\t\t\t**e.get_mut() = 1;\n\t\t\tlock.clear_poison();\n\t\t\te.into_inner()\n\t\t});\n\n\t\tassert!(!lock.is_poisoned());\n\t}\n\n\t#[test]\n\tfn error_as_ref() {\n\t\tlet mutex = Poisonable::new(Mutex::new(\"foo\"));\n\n\t\tlet _ = std::panic::catch_unwind(|| {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t#[allow(unused_variables)]\n\t\t\tlet guard = mutex.lock(key);\n\t\t\tpanic!();\n\n\t\t\t#[allow(unknown_lints)]\n\t\t\t#[allow(unreachable_code)]\n\t\t\tdrop(guard);\n\t\t});\n\n\t\tassert!(mutex.is_poisoned());\n\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet error = mutex.lock(key).unwrap_err();\n\t\tassert_eq!(\u0026***error.as_ref(), \"foo\");\n\t}\n\n\t#[test]\n\tfn error_as_mut() {\n\t\tlet mutex = Poisonable::new(Mutex::new(\"foo\"));\n\n\t\tlet _ = std::panic::catch_unwind(|| {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t#[allow(unused_variables)]\n\t\t\tlet guard = mutex.lock(key);\n\t\t\tpanic!();\n\n\t\t\t#[allow(unknown_lints)]\n\t\t\t#[allow(unreachable_code)]\n\t\t\tdrop(guard);\n\t\t});\n\n\t\tassert!(mutex.is_poisoned());\n\n\t\tlet key: ThreadKey = ThreadKey::get().unwrap();\n\t\tlet mut error = mutex.lock(key).unwrap_err();\n\t\tlet error1 = error.as_mut();\n\t\t**error1 = \"bar\";\n\t\tlet key = Poisonable::\u003cMutex\u003c_\u003e\u003e::unlock(error.into_inner());\n\n\t\tmutex.clear_poison();\n\t\tlet guard = mutex.lock(key).unwrap();\n\t\tassert_eq!(\u0026**guard, \"bar\");\n\t}\n\n\t#[test]\n\tfn try_error_from_lock_error() {\n\t\tlet mutex = Poisonable::new(Mutex::new(\"foo\"));\n\n\t\tlet _ = std::panic::catch_unwind(|| {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t#[allow(unused_variables)]\n\t\t\tlet guard = mutex.lock(key);\n\t\t\tpanic!();\n\n\t\t\t#[allow(unknown_lints)]\n\t\t\t#[allow(unreachable_code)]\n\t\t\tdrop(guard);\n\t\t});\n\n\t\tassert!(mutex.is_poisoned());\n\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet error = mutex.lock(key).unwrap_err();\n\t\tlet error = TryLockPoisonableError::from(error);\n\n\t\tlet TryLockPoisonableError::Poisoned(error) = error else {\n\t\t\tpanic!()\n\t\t};\n\t\tassert_eq!(\u0026**error.into_inner(), \"foo\");\n\t}\n\n\t#[test]\n\tfn new_poisonable_is_not_poisoned() {\n\t\tlet mutex = Poisonable::new(Mutex::new(42));\n\t\tassert!(!mutex.is_poisoned());\n\t}\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","src","rwlock","read_guard.rs"],"content":"use std::fmt::{Debug, Display};\nuse std::hash::Hash;\nuse std::marker::PhantomData;\nuse std::ops::Deref;\n\nuse lock_api::RawRwLock;\n\nuse crate::lockable::RawLock;\nuse crate::ThreadKey;\n\nuse super::{RwLock, RwLockReadGuard, RwLockReadRef};\n\n// These impls make things slightly easier because now you can use\n// `println!(\"{guard}\")` instead of `println!(\"{}\", *guard)`\n\n#[mutants::skip] // hashing involves PRNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Hash + ?Sized, R: RawRwLock\u003e Hash for RwLockReadRef\u003c'_, T, R\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.deref().hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug + ?Sized, R: RawRwLock\u003e Debug for RwLockReadRef\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: Display + ?Sized, R: RawRwLock\u003e Display for RwLockReadRef\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e Deref for RwLockReadRef\u003c'_, T, R\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t// safety: this is the only type that can use `value`, and there's\n\t\t// a reference to this type, so there cannot be any mutable\n\t\t// references to this value.\n\t\tunsafe { \u0026*self.0.data.get() }\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e AsRef\u003cT\u003e for RwLockReadRef\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e Drop for RwLockReadRef\u003c'_, T, R\u003e {\n\tfn drop(\u0026mut self) {\n\t\t// safety: this guard is being destroyed, so the data cannot be\n\t\t// accessed without locking again\n\t\tunsafe { self.0.raw_unlock_read() }\n\t}\n}\n\nimpl\u003c'a, T: ?Sized, R: RawRwLock\u003e RwLockReadRef\u003c'a, T, R\u003e {\n\t/// Creates an immutable reference for the underlying data of an [`RwLock`]\n\t/// without locking it or taking ownership of the key.\n\t#[must_use]\n\tpub(crate) const unsafe fn new(mutex: \u0026'a RwLock\u003cT, R\u003e) -\u003e Self {\n\t\tSelf(mutex, PhantomData)\n\t}\n}\n\n#[mutants::skip] // hashing involves PRNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Hash + ?Sized, R: RawRwLock\u003e Hash for RwLockReadGuard\u003c'_, T, R\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.deref().hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug + ?Sized, R: RawRwLock\u003e Debug for RwLockReadGuard\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: Display + ?Sized, R: RawRwLock\u003e Display for RwLockReadGuard\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e Deref for RwLockReadGuard\u003c'_, T, R\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t\u0026self.rwlock\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e AsRef\u003cT\u003e for RwLockReadGuard\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself\n\t}\n}\n\nimpl\u003c'a, T: ?Sized, R: RawRwLock\u003e RwLockReadGuard\u003c'a, T, R\u003e {\n\t/// Create a guard to the given mutex. Undefined if multiple guards to the\n\t/// same mutex exist at once.\n\t#[must_use]\n\tpub(super) const unsafe fn new(rwlock: \u0026'a RwLock\u003cT, R\u003e, thread_key: ThreadKey) -\u003e Self {\n\t\tSelf {\n\t\t\trwlock: RwLockReadRef(rwlock, PhantomData),\n\t\t\tthread_key,\n\t\t}\n\t}\n}\n\nunsafe impl\u003cT: ?Sized + Sync, R: RawRwLock + Sync\u003e Sync for RwLockReadRef\u003c'_, T, R\u003e {}\n","traces":[{"line":33,"address":[970320],"length":1,"stats":{"Line":1}},{"line":34,"address":[],"length":0,"stats":{"Line":1}},{"line":41,"address":[],"length":0,"stats":{"Line":3}},{"line":45,"address":[970405,970373],"length":1,"stats":{"Line":3}},{"line":50,"address":[],"length":0,"stats":{"Line":1}},{"line":51,"address":[],"length":0,"stats":{"Line":1}},{"line":56,"address":[240400,240384],"length":1,"stats":{"Line":3}},{"line":59,"address":[237893,237909],"length":1,"stats":{"Line":3}},{"line":67,"address":[],"length":0,"stats":{"Line":3}},{"line":68,"address":[],"length":0,"stats":{"Line":0}},{"line":89,"address":[],"length":0,"stats":{"Line":1}},{"line":90,"address":[],"length":0,"stats":{"Line":1}},{"line":97,"address":[],"length":0,"stats":{"Line":2}},{"line":98,"address":[],"length":0,"stats":{"Line":2}},{"line":103,"address":[],"length":0,"stats":{"Line":1}},{"line":104,"address":[],"length":0,"stats":{"Line":1}},{"line":112,"address":[],"length":0,"stats":{"Line":1}},{"line":114,"address":[],"length":0,"stats":{"Line":0}}],"covered":16,"coverable":18},{"path":["/","home","botahamec","Projects","happylock","src","rwlock","read_lock.rs"],"content":"use std::fmt::Debug;\n\nuse lock_api::RawRwLock;\n\nuse crate::lockable::{Lockable, RawLock, Sharable};\nuse crate::{Keyable, ThreadKey};\n\nuse super::{ReadLock, RwLock, RwLockReadGuard, RwLockReadRef};\n\nunsafe impl\u003cT, R: RawRwLock\u003e RawLock for ReadLock\u003c'_, T, R\u003e {\n\tfn poison(\u0026self) {\n\t\tself.0.poison()\n\t}\n\n\tunsafe fn raw_write(\u0026self) {\n\t\tself.0.raw_read()\n\t}\n\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool {\n\t\tself.0.raw_try_read()\n\t}\n\n\tunsafe fn raw_unlock_write(\u0026self) {\n\t\tself.0.raw_unlock_read()\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tself.0.raw_read()\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tself.0.raw_try_read()\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tself.0.raw_unlock_read()\n\t}\n}\n\nunsafe impl\u003cT, R: RawRwLock\u003e Lockable for ReadLock\u003c'_, T, R\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= RwLockReadRef\u003c'g, T, R\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= \u0026'a T\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tptrs.push(self);\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tRwLockReadRef::new(self.as_ref())\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.0.data_ref()\n\t}\n}\n\nunsafe impl\u003cT, R: RawRwLock\u003e Sharable for ReadLock\u003c'_, T, R\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= RwLockReadRef\u003c'g, T, R\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= \u0026'a T\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tRwLockReadRef::new(self.as_ref())\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tself.0.data_ref()\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug, R: RawRwLock\u003e Debug for ReadLock\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t// safety: this is just a try lock, and the value is dropped\n\t\t// immediately after, so there's no risk of blocking ourselves\n\t\t// or any other threads\n\t\tif let Some(value) = unsafe { self.try_lock_no_key() } {\n\t\t\tf.debug_struct(\"ReadLock\").field(\"data\", \u0026\u0026*value).finish()\n\t\t} else {\n\t\t\tstruct LockedPlaceholder;\n\t\t\timpl Debug for LockedPlaceholder {\n\t\t\t\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t\t\t\tf.write_str(\"\u003clocked\u003e\")\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tf.debug_struct(\"ReadLock\")\n\t\t\t\t.field(\"data\", \u0026LockedPlaceholder)\n\t\t\t\t.finish()\n\t\t}\n\t}\n}\n\nimpl\u003c'l, T, R\u003e From\u003c\u0026'l RwLock\u003cT, R\u003e\u003e for ReadLock\u003c'l, T, R\u003e {\n\tfn from(value: \u0026'l RwLock\u003cT, R\u003e) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\nimpl\u003cT: ?Sized, R\u003e AsRef\u003cRwLock\u003cT, R\u003e\u003e for ReadLock\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026RwLock\u003cT, R\u003e {\n\t\tself.0\n\t}\n}\n\nimpl\u003c'l, T, R\u003e ReadLock\u003c'l, T, R\u003e {\n\t/// Creates a new `ReadLock` which accesses the given [`RwLock`]\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{rwlock::ReadLock, RwLock};\n\t///\n\t/// let lock = RwLock::new(5);\n\t/// let read_lock = ReadLock::new(\u0026lock);\n\t/// ```\n\t#[must_use]\n\tpub const fn new(rwlock: \u0026'l RwLock\u003cT, R\u003e) -\u003e Self {\n\t\tSelf(rwlock)\n\t}\n}\n\nimpl\u003cT, R: RawRwLock\u003e ReadLock\u003c'_, T, R\u003e {\n\tpub fn scoped_lock\u003c'a, Ret\u003e(\u0026'a self, key: impl Keyable, f: impl Fn(\u0026'a T) -\u003e Ret) -\u003e Ret {\n\t\tself.0.scoped_read(key, f)\n\t}\n\n\tpub fn scoped_try_lock\u003c'a, Key: Keyable, Ret\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(\u0026'a T) -\u003e Ret,\n\t) -\u003e Result\u003cRet, Key\u003e {\n\t\tself.0.scoped_try_read(key, f)\n\t}\n\n\t/// Locks the underlying [`RwLock`] with shared read access, blocking the\n\t/// current thread until it can be acquired.\n\t///\n\t/// The calling thread will be blocked until there are no more writers\n\t/// which hold the lock. There may be other readers currently inside the\n\t/// lock when this method returns.\n\t///\n\t/// Returns an RAII guard which will release this thread's shared access\n\t/// once it is dropped.\n\t///\n\t/// Because this method takes a [`ThreadKey`], it's not possible for this\n\t/// method to cause a deadlock.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::rwlock::ReadLock;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock: RwLock\u003c_\u003e = RwLock::new(1);\n\t/// let reader = ReadLock::new(\u0026lock);\n\t///\n\t/// let n = reader.lock(key);\n\t/// assert_eq!(*n, 1);\n\t/// ```\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\t#[must_use]\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e RwLockReadGuard\u003c'_, T, R\u003e {\n\t\tself.0.read(key)\n\t}\n\n\t/// Attempts to acquire the underlying [`RwLock`] with shared read access\n\t/// without blocking.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// shared access when it is dropped.\n\t///\n\t/// This function does not provide any guarantees with respect to the\n\t/// ordering of whether contentious readers or writers will acquire the\n\t/// lock first.\n\t///\n\t/// # Errors\n\t///\n\t/// If the `RwLock` could not be acquired because it was already locked\n\t/// exclusively, then an error will be returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::rwlock::ReadLock;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(1);\n\t/// let reader = ReadLock::new(\u0026lock);\n\t///\n\t/// match reader.try_lock(key) {\n\t/// Ok(n) =\u003e assert_eq!(*n, 1),\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t/// ```\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e Result\u003cRwLockReadGuard\u003c'_, T, R\u003e, ThreadKey\u003e {\n\t\tself.0.try_read(key)\n\t}\n\n\t/// Attempts to create an exclusive lock without a key. Locking this\n\t/// without exclusive access to the key is undefined behavior.\n\tpub(crate) unsafe fn try_lock_no_key(\u0026self) -\u003e Option\u003cRwLockReadRef\u003c'_, T, R\u003e\u003e {\n\t\tself.0.try_read_no_key()\n\t}\n\n\t/// Immediately drops the guard, and consequently releases the shared lock\n\t/// on the underlying [`RwLock`].\n\t///\n\t/// This function is equivalent to calling [`drop`] on the guard, except\n\t/// that it returns the key that was used to create it. Alternately, the\n\t/// guard will be automatically dropped when it goes out of scope.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::rwlock::ReadLock;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(0);\n\t/// let reader = ReadLock::new(\u0026lock);\n\t///\n\t/// let mut guard = reader.lock(key);\n\t/// assert_eq!(*guard, 0);\n\t/// let key = ReadLock::unlock(guard);\n\t/// ```\n\t#[must_use]\n\tpub fn unlock(guard: RwLockReadGuard\u003c'_, T, R\u003e) -\u003e ThreadKey {\n\t\tRwLock::unlock_read(guard)\n\t}\n}\n","traces":[{"line":11,"address":[968928,968944],"length":1,"stats":{"Line":1}},{"line":12,"address":[],"length":0,"stats":{"Line":1}},{"line":15,"address":[],"length":0,"stats":{"Line":1}},{"line":16,"address":[],"length":0,"stats":{"Line":1}},{"line":19,"address":[968992,969024],"length":1,"stats":{"Line":1}},{"line":20,"address":[],"length":0,"stats":{"Line":1}},{"line":23,"address":[969072,969056],"length":1,"stats":{"Line":1}},{"line":24,"address":[],"length":0,"stats":{"Line":1}},{"line":27,"address":[],"length":0,"stats":{"Line":1}},{"line":28,"address":[],"length":0,"stats":{"Line":1}},{"line":31,"address":[],"length":0,"stats":{"Line":1}},{"line":32,"address":[],"length":0,"stats":{"Line":1}},{"line":35,"address":[],"length":0,"stats":{"Line":1}},{"line":36,"address":[],"length":0,"stats":{"Line":1}},{"line":51,"address":[],"length":0,"stats":{"Line":2}},{"line":52,"address":[969305,969241],"length":1,"stats":{"Line":2}},{"line":55,"address":[],"length":0,"stats":{"Line":1}},{"line":56,"address":[],"length":0,"stats":{"Line":1}},{"line":59,"address":[],"length":0,"stats":{"Line":1}},{"line":60,"address":[],"length":0,"stats":{"Line":1}},{"line":75,"address":[],"length":0,"stats":{"Line":1}},{"line":76,"address":[],"length":0,"stats":{"Line":1}},{"line":79,"address":[],"length":0,"stats":{"Line":1}},{"line":80,"address":[],"length":0,"stats":{"Line":1}},{"line":109,"address":[],"length":0,"stats":{"Line":1}},{"line":110,"address":[],"length":0,"stats":{"Line":1}},{"line":115,"address":[],"length":0,"stats":{"Line":1}},{"line":116,"address":[],"length":0,"stats":{"Line":1}},{"line":132,"address":[969472,969488],"length":1,"stats":{"Line":2}},{"line":133,"address":[],"length":0,"stats":{"Line":0}},{"line":138,"address":[],"length":0,"stats":{"Line":1}},{"line":139,"address":[],"length":0,"stats":{"Line":1}},{"line":142,"address":[969536],"length":1,"stats":{"Line":1}},{"line":147,"address":[969545],"length":1,"stats":{"Line":1}},{"line":181,"address":[],"length":0,"stats":{"Line":1}},{"line":182,"address":[],"length":0,"stats":{"Line":1}},{"line":216,"address":[],"length":0,"stats":{"Line":1}},{"line":217,"address":[],"length":0,"stats":{"Line":1}},{"line":222,"address":[],"length":0,"stats":{"Line":0}},{"line":223,"address":[],"length":0,"stats":{"Line":0}},{"line":248,"address":[],"length":0,"stats":{"Line":1}},{"line":249,"address":[],"length":0,"stats":{"Line":1}}],"covered":39,"coverable":42},{"path":["/","home","botahamec","Projects","happylock","src","rwlock","rwlock.rs"],"content":"use std::cell::UnsafeCell;\nuse std::fmt::Debug;\nuse std::marker::PhantomData;\nuse std::panic::AssertUnwindSafe;\n\nuse lock_api::RawRwLock;\n\nuse crate::collection::utils;\nuse crate::handle_unwind::handle_unwind;\nuse crate::lockable::{\n\tLockable, LockableGetMut, LockableIntoInner, OwnedLockable, RawLock, Sharable,\n};\nuse crate::{Keyable, ThreadKey};\n\nuse super::{PoisonFlag, RwLock, RwLockReadGuard, RwLockReadRef, RwLockWriteGuard, RwLockWriteRef};\n\nunsafe impl\u003cT: ?Sized, R: RawRwLock\u003e RawLock for RwLock\u003cT, R\u003e {\n\tfn poison(\u0026self) {\n\t\tself.poison.poison();\n\t}\n\n\tunsafe fn raw_write(\u0026self) {\n\t\tassert!(\n\t\t\t!self.poison.is_poisoned(),\n\t\t\t\"The read-write lock has been killed\"\n\t\t);\n\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.lock_exclusive(), || self.poison())\n\t}\n\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool {\n\t\tif self.poison.is_poisoned() {\n\t\t\treturn false;\n\t\t}\n\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.try_lock_exclusive(), || self.poison())\n\t}\n\n\tunsafe fn raw_unlock_write(\u0026self) {\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.unlock_exclusive(), || self.poison())\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tassert!(\n\t\t\t!self.poison.is_poisoned(),\n\t\t\t\"The read-write lock has been killed\"\n\t\t);\n\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.lock_shared(), || self.poison())\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tif self.poison.is_poisoned() {\n\t\t\treturn false;\n\t\t}\n\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.try_lock_shared(), || self.poison())\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.unlock_shared(), || self.poison())\n\t}\n}\n\nunsafe impl\u003cT, R: RawRwLock\u003e Lockable for RwLock\u003cT, R\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= RwLockWriteRef\u003c'g, T, R\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= \u0026'a mut T\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tptrs.push(self);\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tRwLockWriteRef::new(self)\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.data.get().as_mut().unwrap_unchecked()\n\t}\n}\n\nunsafe impl\u003cT, R: RawRwLock\u003e Sharable for RwLock\u003cT, R\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= RwLockReadRef\u003c'g, T, R\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= \u0026'a T\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tRwLockReadRef::new(self)\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tself.data.get().as_ref().unwrap_unchecked()\n\t}\n}\n\nunsafe impl\u003cT: Send, R: RawRwLock\u003e OwnedLockable for RwLock\u003cT, R\u003e {}\n\nimpl\u003cT: Send, R: RawRwLock\u003e LockableIntoInner for RwLock\u003cT, R\u003e {\n\ttype Inner = T;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tself.into_inner()\n\t}\n}\n\nimpl\u003cT: Send, R: RawRwLock\u003e LockableGetMut for RwLock\u003cT, R\u003e {\n\ttype Inner\u003c'a\u003e\n\t\t= \u0026'a mut T\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tAsMut::as_mut(self)\n\t}\n}\n\nimpl\u003cT, R: RawRwLock\u003e RwLock\u003cT, R\u003e {\n\t/// Creates a new instance of an `RwLock\u003cT\u003e` which is unlocked.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::RwLock;\n\t///\n\t/// let lock = RwLock::new(5);\n\t/// ```\n\t#[must_use]\n\tpub const fn new(data: T) -\u003e Self {\n\t\tSelf {\n\t\t\tdata: UnsafeCell::new(data),\n\t\t\tpoison: PoisonFlag::new(),\n\t\t\traw: R::INIT,\n\t\t}\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug, R: RawRwLock\u003e Debug for RwLock\u003cT, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t// safety: this is just a try lock, and the value is dropped\n\t\t// immediately after, so there's no risk of blocking ourselves\n\t\t// or any other threads\n\t\tif let Some(value) = unsafe { self.try_read_no_key() } {\n\t\t\tf.debug_struct(\"RwLock\").field(\"data\", \u0026\u0026*value).finish()\n\t\t} else {\n\t\t\tstruct LockedPlaceholder;\n\t\t\timpl Debug for LockedPlaceholder {\n\t\t\t\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t\t\t\tf.write_str(\"\u003clocked\u003e\")\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tf.debug_struct(\"RwLock\")\n\t\t\t\t.field(\"data\", \u0026LockedPlaceholder)\n\t\t\t\t.finish()\n\t\t}\n\t}\n}\n\nimpl\u003cT: Default, R: RawRwLock\u003e Default for RwLock\u003cT, R\u003e {\n\tfn default() -\u003e Self {\n\t\tSelf::new(T::default())\n\t}\n}\n\nimpl\u003cT, R: RawRwLock\u003e From\u003cT\u003e for RwLock\u003cT, R\u003e {\n\tfn from(value: T) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\n// We don't need a `get_mut` because we don't have mutex poisoning. Hurray!\n// This is safe because you can't have a mutable reference to the lock if it's\n// locked. Being locked requires an immutable reference because of the guard.\nimpl\u003cT: ?Sized, R\u003e AsMut\u003cT\u003e for RwLock\u003cT, R\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself.data.get_mut()\n\t}\n}\n\nimpl\u003cT, R\u003e RwLock\u003cT, R\u003e {\n\t/// Consumes this `RwLock`, returning the underlying data.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t///\n\t/// let lock = RwLock::new(String::new());\n\t/// {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut s = lock.write(key);\n\t/// *s = \"modified\".to_owned();\n\t/// }\n\t/// assert_eq!(lock.into_inner(), \"modified\");\n\t/// ```\n\t#[must_use]\n\tpub fn into_inner(self) -\u003e T {\n\t\tself.data.into_inner()\n\t}\n}\n\nimpl\u003cT: ?Sized, R\u003e RwLock\u003cT, R\u003e {\n\t/// Returns a mutable reference to the underlying data.\n\t///\n\t/// Since this call borrows `RwLock` mutably, no actual locking is taking\n\t/// place. The mutable borrow statically guarantees that no locks exist.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, RwLock};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut mutex = RwLock::new(0);\n\t/// *mutex.get_mut() = 10;\n\t/// assert_eq!(*mutex.read(key), 10);\n\t/// ```\n\t#[must_use]\n\tpub fn get_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself.data.get_mut()\n\t}\n}\n\nimpl\u003cT, R: RawRwLock\u003e RwLock\u003cT, R\u003e {\n\tpub fn scoped_read\u003c'a, Ret\u003e(\u0026'a self, key: impl Keyable, f: impl Fn(\u0026'a T) -\u003e Ret) -\u003e Ret {\n\t\tutils::scoped_read(self, key, f)\n\t}\n\n\tpub fn scoped_try_read\u003c'a, Key: Keyable, Ret\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(\u0026'a T) -\u003e Ret,\n\t) -\u003e Result\u003cRet, Key\u003e {\n\t\tutils::scoped_try_read(self, key, f)\n\t}\n\n\tpub fn scoped_write\u003c'a, Ret\u003e(\u0026'a self, key: impl Keyable, f: impl Fn(\u0026'a mut T) -\u003e Ret) -\u003e Ret {\n\t\tutils::scoped_write(self, key, f)\n\t}\n\n\tpub fn scoped_try_write\u003c'a, Key: Keyable, Ret\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(\u0026'a mut T) -\u003e Ret,\n\t) -\u003e Result\u003cRet, Key\u003e {\n\t\tutils::scoped_try_write(self, key, f)\n\t}\n\n\t/// Locks this `RwLock` with shared read access, blocking the current\n\t/// thread until it can be acquired.\n\t///\n\t/// The calling thread will be blocked until there are no more writers\n\t/// which hold the lock. There may be other readers currently inside the\n\t/// lock when this method returns.\n\t///\n\t/// Returns an RAII guard which will release this thread's shared access\n\t/// once it is dropped.\n\t///\n\t/// Because this method takes a [`ThreadKey`], it's not possible for this\n\t/// method to cause a deadlock.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::sync::Arc;\n\t/// use std::thread;\n\t/// use happylock::{RwLock, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = Arc::new(RwLock::new(1));\n\t/// let c_lock = Arc::clone(\u0026lock);\n\t///\n\t/// let n = lock.read(key);\n\t/// assert_eq!(*n, 1);\n\t///\n\t/// thread::spawn(move || {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let r = c_lock.read(key);\n\t/// }).join().unwrap();\n\t/// ```\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\tpub fn read(\u0026self, key: ThreadKey) -\u003e RwLockReadGuard\u003c'_, T, R\u003e {\n\t\tunsafe {\n\t\t\tself.raw_read();\n\n\t\t\t// safety: the lock is locked first\n\t\t\tRwLockReadGuard::new(self, key)\n\t\t}\n\t}\n\n\t/// Attempts to acquire this `RwLock` with shared read access without\n\t/// blocking.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// shared access when it is dropped.\n\t///\n\t/// This function does not provide any guarantees with respect to the\n\t/// ordering of whether contentious readers or writers will acquire the\n\t/// lock first.\n\t///\n\t/// # Errors\n\t///\n\t/// If the `RwLock` could not be acquired because it was already locked\n\t/// exclusively, then an error will be returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(1);\n\t///\n\t/// match lock.try_read(key) {\n\t/// Ok(n) =\u003e assert_eq!(*n, 1),\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t/// ```\n\tpub fn try_read(\u0026self, key: ThreadKey) -\u003e Result\u003cRwLockReadGuard\u003c'_, T, R\u003e, ThreadKey\u003e {\n\t\tunsafe {\n\t\t\tif self.raw_try_read() {\n\t\t\t\t// safety: the lock is locked first\n\t\t\t\tOk(RwLockReadGuard::new(self, key))\n\t\t\t} else {\n\t\t\t\tErr(key)\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Attempts to create a shared lock without a key. Locking this without\n\t/// exclusive access to the key is undefined behavior.\n\tpub(crate) unsafe fn try_read_no_key(\u0026self) -\u003e Option\u003cRwLockReadRef\u003c'_, T, R\u003e\u003e {\n\t\tif self.raw_try_read() {\n\t\t\t// safety: the lock is locked first\n\t\t\tSome(RwLockReadRef(self, PhantomData))\n\t\t} else {\n\t\t\tNone\n\t\t}\n\t}\n\n\t/// Attempts to create an exclusive lock without a key. Locking this\n\t/// without exclusive access to the key is undefined behavior.\n\t#[cfg(test)]\n\tpub(crate) unsafe fn try_write_no_key(\u0026self) -\u003e Option\u003cRwLockWriteRef\u003c'_, T, R\u003e\u003e {\n\t\tif self.raw_try_write() {\n\t\t\t// safety: the lock is locked first\n\t\t\tSome(RwLockWriteRef(self, PhantomData))\n\t\t} else {\n\t\t\tNone\n\t\t}\n\t}\n\n\t/// Locks this `RwLock` with exclusive write access, blocking the current\n\t/// until it can be acquired.\n\t///\n\t/// This function will not return while other writers or readers currently\n\t/// have access to the lock.\n\t///\n\t/// Returns an RAII guard which will drop the write access of this `RwLock`\n\t/// when dropped.\n\t///\n\t/// Because this method takes a [`ThreadKey`], it's not possible for this\n\t/// method to cause a deadlock.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, RwLock};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(1);\n\t///\n\t/// match lock.try_write(key) {\n\t/// Ok(n) =\u003e assert_eq!(*n, 1),\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t/// ```\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\tpub fn write(\u0026self, key: ThreadKey) -\u003e RwLockWriteGuard\u003c'_, T, R\u003e {\n\t\tunsafe {\n\t\t\tself.raw_write();\n\n\t\t\t// safety: the lock is locked first\n\t\t\tRwLockWriteGuard::new(self, key)\n\t\t}\n\t}\n\n\t/// Attempts to lock this `RwLock` with exclusive write access.\n\t///\n\t/// This function does not block. If the lock could not be acquired at this\n\t/// time, then `None` is returned. Otherwise, an RAII guard is returned\n\t/// which will release the lock when it is dropped.\n\t///\n\t/// This function does not provide any guarantees with respect to the\n\t/// ordering of whether contentious readers or writers will acquire the\n\t/// lock first.\n\t///\n\t/// # Errors\n\t///\n\t/// If the `RwLock` could not be acquired because it was already locked,\n\t/// then an error will be returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(1);\n\t///\n\t/// let n = lock.read(key);\n\t/// assert_eq!(*n, 1);\n\t/// ```\n\tpub fn try_write(\u0026self, key: ThreadKey) -\u003e Result\u003cRwLockWriteGuard\u003c'_, T, R\u003e, ThreadKey\u003e {\n\t\tunsafe {\n\t\t\tif self.raw_try_write() {\n\t\t\t\t// safety: the lock is locked first\n\t\t\t\tOk(RwLockWriteGuard::new(self, key))\n\t\t\t} else {\n\t\t\t\tErr(key)\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Returns `true` if the rwlock is currently locked in any way\n\t#[cfg(test)]\n\tpub(crate) fn is_locked(\u0026self) -\u003e bool {\n\t\tself.raw.is_locked()\n\t}\n\n\t/// Immediately drops the guard, and consequently releases the shared lock.\n\t///\n\t/// This function is equivalent to calling [`drop`] on the guard, except\n\t/// that it returns the key that was used to create it. Alternately, the\n\t/// guard will be automatically dropped when it goes out of scope.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(0);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// assert_eq!(*guard, 0);\n\t/// let key = RwLock::unlock_read(guard);\n\t/// ```\n\t#[must_use]\n\tpub fn unlock_read(guard: RwLockReadGuard\u003c'_, T, R\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.rwlock);\n\t\tguard.thread_key\n\t}\n\n\t/// Immediately drops the guard, and consequently releases the exclusive\n\t/// lock.\n\t///\n\t/// This function is equivalent to calling [`drop`] on the guard, except\n\t/// that it returns the key that was used to create it. Alternately, the\n\t/// guard will be automatically dropped when it goes out of scope.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(0);\n\t///\n\t/// let mut guard = lock.write(key);\n\t/// *guard += 20;\n\t/// let key = RwLock::unlock_write(guard);\n\t/// ```\n\t#[must_use]\n\tpub fn unlock_write(guard: RwLockWriteGuard\u003c'_, T, R\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.rwlock);\n\t\tguard.thread_key\n\t}\n}\n\nunsafe impl\u003cR: RawRwLock + Send, T: ?Sized + Send\u003e Send for RwLock\u003cT, R\u003e {}\nunsafe impl\u003cR: RawRwLock + Sync, T: ?Sized + Send\u003e Sync for RwLock\u003cT, R\u003e {}\n","traces":[{"line":18,"address":[249088,249056,249120],"length":1,"stats":{"Line":8}},{"line":19,"address":[175989,175957],"length":1,"stats":{"Line":8}},{"line":22,"address":[176240,176352],"length":1,"stats":{"Line":5}},{"line":23,"address":[138805],"length":1,"stats":{"Line":2}},{"line":24,"address":[],"length":0,"stats":{"Line":0}},{"line":25,"address":[],"length":0,"stats":{"Line":0}},{"line":29,"address":[965760,965648],"length":1,"stats":{"Line":5}},{"line":30,"address":[249490,249590,249702],"length":1,"stats":{"Line":17}},{"line":33,"address":[138464],"length":1,"stats":{"Line":9}},{"line":34,"address":[965934,965854],"length":1,"stats":{"Line":9}},{"line":35,"address":[],"length":0,"stats":{"Line":5}},{"line":39,"address":[216545,216625],"length":1,"stats":{"Line":6}},{"line":40,"address":[217973,217941,217936,217968,217925,217920,217984,217989],"length":1,"stats":{"Line":16}},{"line":43,"address":[],"length":0,"stats":{"Line":6}},{"line":45,"address":[966044,966012],"length":1,"stats":{"Line":6}},{"line":46,"address":[1006885,1006912,1006848,1006880,1006917,1006933,1006853,1006928],"length":1,"stats":{"Line":21}},{"line":49,"address":[249344,249136,249248],"length":1,"stats":{"Line":5}},{"line":50,"address":[966228,966116],"length":1,"stats":{"Line":1}},{"line":51,"address":[],"length":0,"stats":{"Line":0}},{"line":52,"address":[],"length":0,"stats":{"Line":0}},{"line":56,"address":[217009,216897],"length":1,"stats":{"Line":5}},{"line":57,"address":[176166,176054],"length":1,"stats":{"Line":20}},{"line":60,"address":[248544,248384,248464],"length":1,"stats":{"Line":9}},{"line":61,"address":[138398],"length":1,"stats":{"Line":9}},{"line":62,"address":[138440],"length":1,"stats":{"Line":2}},{"line":66,"address":[216385,216465],"length":1,"stats":{"Line":9}},{"line":67,"address":[147557,147584,147525,147600,147605,147520,147552,147589],"length":1,"stats":{"Line":33}},{"line":70,"address":[248864,248928,248896],"length":1,"stats":{"Line":6}},{"line":72,"address":[966492,966460],"length":1,"stats":{"Line":7}},{"line":73,"address":[966465,966497],"length":1,"stats":{"Line":25}},{"line":88,"address":[217376,217312],"length":1,"stats":{"Line":16}},{"line":89,"address":[217337,217401],"length":1,"stats":{"Line":15}},{"line":92,"address":[966656,966640],"length":1,"stats":{"Line":4}},{"line":93,"address":[],"length":0,"stats":{"Line":4}},{"line":96,"address":[966672,966720],"length":1,"stats":{"Line":5}},{"line":97,"address":[249881,249785,249833],"length":1,"stats":{"Line":4}},{"line":112,"address":[217456,217440],"length":1,"stats":{"Line":3}},{"line":113,"address":[138933],"length":1,"stats":{"Line":3}},{"line":116,"address":[],"length":0,"stats":{"Line":4}},{"line":117,"address":[217529,217481],"length":1,"stats":{"Line":4}},{"line":126,"address":[],"length":0,"stats":{"Line":1}},{"line":127,"address":[],"length":0,"stats":{"Line":1}},{"line":137,"address":[],"length":0,"stats":{"Line":1}},{"line":138,"address":[966917],"length":1,"stats":{"Line":1}},{"line":153,"address":[966970,967253,967111,966928,967216,967056],"length":1,"stats":{"Line":15}},{"line":155,"address":[],"length":0,"stats":{"Line":0}},{"line":156,"address":[248042,248102,248209,248282,248160,248342],"length":1,"stats":{"Line":30}},{"line":187,"address":[],"length":0,"stats":{"Line":1}},{"line":188,"address":[],"length":0,"stats":{"Line":1}},{"line":193,"address":[967376,967424],"length":1,"stats":{"Line":2}},{"line":194,"address":[967397,967440],"length":1,"stats":{"Line":2}},{"line":202,"address":[],"length":0,"stats":{"Line":1}},{"line":203,"address":[967464],"length":1,"stats":{"Line":1}},{"line":224,"address":[],"length":0,"stats":{"Line":0}},{"line":225,"address":[967488],"length":1,"stats":{"Line":1}},{"line":246,"address":[],"length":0,"stats":{"Line":1}},{"line":247,"address":[967528],"length":1,"stats":{"Line":1}},{"line":252,"address":[967584,967552],"length":1,"stats":{"Line":2}},{"line":253,"address":[967561,967597],"length":1,"stats":{"Line":2}},{"line":256,"address":[967616],"length":1,"stats":{"Line":7}},{"line":261,"address":[215949,215981,216013,216077,215917,216045],"length":1,"stats":{"Line":7}},{"line":264,"address":[],"length":0,"stats":{"Line":2}},{"line":265,"address":[967666,967693],"length":1,"stats":{"Line":2}},{"line":268,"address":[247920,247856,247824,247984,247952,247888],"length":1,"stats":{"Line":13}},{"line":273,"address":[],"length":0,"stats":{"Line":13}},{"line":310,"address":[],"length":0,"stats":{"Line":1}},{"line":312,"address":[],"length":0,"stats":{"Line":1}},{"line":315,"address":[967805],"length":1,"stats":{"Line":1}},{"line":348,"address":[968000,968022,967872],"length":1,"stats":{"Line":1}},{"line":350,"address":[],"length":0,"stats":{"Line":4}},{"line":352,"address":[967993,967963],"length":1,"stats":{"Line":2}},{"line":354,"address":[967942],"length":1,"stats":{"Line":1}},{"line":361,"address":[],"length":0,"stats":{"Line":1}},{"line":362,"address":[],"length":0,"stats":{"Line":1}},{"line":364,"address":[968069],"length":1,"stats":{"Line":1}},{"line":366,"address":[],"length":0,"stats":{"Line":0}},{"line":373,"address":[],"length":0,"stats":{"Line":1}},{"line":374,"address":[],"length":0,"stats":{"Line":1}},{"line":376,"address":[],"length":0,"stats":{"Line":1}},{"line":378,"address":[],"length":0,"stats":{"Line":0}},{"line":409,"address":[175462,175334,175488,175248,175360,175376],"length":1,"stats":{"Line":6}},{"line":411,"address":[175262,175390],"length":1,"stats":{"Line":5}},{"line":414,"address":[],"length":0,"stats":{"Line":4}},{"line":444,"address":[],"length":0,"stats":{"Line":2}},{"line":446,"address":[],"length":0,"stats":{"Line":7}},{"line":448,"address":[],"length":0,"stats":{"Line":4}},{"line":450,"address":[],"length":0,"stats":{"Line":1}},{"line":457,"address":[968736,968752],"length":1,"stats":{"Line":2}},{"line":458,"address":[],"length":0,"stats":{"Line":2}},{"line":480,"address":[968768,968820],"length":1,"stats":{"Line":1}},{"line":481,"address":[],"length":0,"stats":{"Line":1}},{"line":482,"address":[],"length":0,"stats":{"Line":0}},{"line":505,"address":[],"length":0,"stats":{"Line":1}},{"line":506,"address":[],"length":0,"stats":{"Line":2}},{"line":507,"address":[],"length":0,"stats":{"Line":0}}],"covered":85,"coverable":95},{"path":["/","home","botahamec","Projects","happylock","src","rwlock","write_guard.rs"],"content":"use std::fmt::{Debug, Display};\nuse std::hash::Hash;\nuse std::marker::PhantomData;\nuse std::ops::{Deref, DerefMut};\n\nuse lock_api::RawRwLock;\n\nuse crate::lockable::RawLock;\nuse crate::ThreadKey;\n\nuse super::{RwLock, RwLockWriteGuard, RwLockWriteRef};\n\n// These impls make things slightly easier because now you can use\n// `println!(\"{guard}\")` instead of `println!(\"{}\", *guard)`\n\n#[mutants::skip] // hashing involves PRNG and is difficult to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Hash + ?Sized, R: RawRwLock\u003e Hash for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.deref().hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug + ?Sized, R: RawRwLock\u003e Debug for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: Display + ?Sized, R: RawRwLock\u003e Display for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e Deref for RwLockWriteRef\u003c'_, T, R\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t// safety: this is the only type that can use `value`, and there's\n\t\t// a reference to this type, so there cannot be any mutable\n\t\t// references to this value.\n\t\tunsafe { \u0026*self.0.data.get() }\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e DerefMut for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t// safety: this is the only type that can use `value`, and we have a\n\t\t// mutable reference to this type, so there cannot be any other\n\t\t// references to this value.\n\t\tunsafe { \u0026mut *self.0.data.get() }\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e AsRef\u003cT\u003e for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e AsMut\u003cT\u003e for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e Drop for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn drop(\u0026mut self) {\n\t\t// safety: this guard is being destroyed, so the data cannot be\n\t\t// accessed without locking again\n\t\tunsafe { self.0.raw_unlock_write() }\n\t}\n}\n\nimpl\u003c'a, T: ?Sized + 'a, R: RawRwLock\u003e RwLockWriteRef\u003c'a, T, R\u003e {\n\t/// Creates a reference to the underlying data of an [`RwLock`] without\n\t/// locking or taking ownership of the key.\n\t#[must_use]\n\tpub(crate) const unsafe fn new(mutex: \u0026'a RwLock\u003cT, R\u003e) -\u003e Self {\n\t\tSelf(mutex, PhantomData)\n\t}\n}\n\n#[mutants::skip] // hashing involves PRNG and is difficult to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Hash + ?Sized, R: RawRwLock\u003e Hash for RwLockWriteGuard\u003c'_, T, R\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.deref().hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug + ?Sized, R: RawRwLock\u003e Debug for RwLockWriteGuard\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: Display + ?Sized, R: RawRwLock\u003e Display for RwLockWriteGuard\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e Deref for RwLockWriteGuard\u003c'_, T, R\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t\u0026self.rwlock\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e DerefMut for RwLockWriteGuard\u003c'_, T, R\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t\u0026mut self.rwlock\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e AsRef\u003cT\u003e for RwLockWriteGuard\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e AsMut\u003cT\u003e for RwLockWriteGuard\u003c'_, T, R\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself\n\t}\n}\n\nimpl\u003c'a, T: ?Sized + 'a, R: RawRwLock\u003e RwLockWriteGuard\u003c'a, T, R\u003e {\n\t/// Create a guard to the given mutex. Undefined if multiple guards to the\n\t/// same mutex exist at once.\n\t#[must_use]\n\tpub(super) const unsafe fn new(rwlock: \u0026'a RwLock\u003cT, R\u003e, thread_key: ThreadKey) -\u003e Self {\n\t\tSelf {\n\t\t\trwlock: RwLockWriteRef(rwlock, PhantomData),\n\t\t\tthread_key,\n\t\t}\n\t}\n}\n\nunsafe impl\u003cT: ?Sized + Sync, R: RawRwLock + Sync\u003e Sync for RwLockWriteRef\u003c'_, T, R\u003e {}\n","traces":[{"line":33,"address":[970576],"length":1,"stats":{"Line":1}},{"line":34,"address":[],"length":0,"stats":{"Line":1}},{"line":41,"address":[],"length":0,"stats":{"Line":3}},{"line":45,"address":[138149],"length":1,"stats":{"Line":3}},{"line":50,"address":[138192],"length":1,"stats":{"Line":3}},{"line":54,"address":[],"length":0,"stats":{"Line":4}},{"line":59,"address":[],"length":0,"stats":{"Line":1}},{"line":60,"address":[],"length":0,"stats":{"Line":1}},{"line":65,"address":[],"length":0,"stats":{"Line":0}},{"line":66,"address":[],"length":0,"stats":{"Line":0}},{"line":71,"address":[],"length":0,"stats":{"Line":6}},{"line":74,"address":[244293,244309],"length":1,"stats":{"Line":6}},{"line":82,"address":[],"length":0,"stats":{"Line":4}},{"line":83,"address":[],"length":0,"stats":{"Line":0}},{"line":104,"address":[970800],"length":1,"stats":{"Line":1}},{"line":105,"address":[],"length":0,"stats":{"Line":1}},{"line":112,"address":[138176],"length":1,"stats":{"Line":3}},{"line":113,"address":[],"length":0,"stats":{"Line":3}},{"line":118,"address":[],"length":0,"stats":{"Line":2}},{"line":119,"address":[138229],"length":1,"stats":{"Line":2}},{"line":124,"address":[],"length":0,"stats":{"Line":1}},{"line":125,"address":[],"length":0,"stats":{"Line":1}},{"line":130,"address":[],"length":0,"stats":{"Line":1}},{"line":131,"address":[],"length":0,"stats":{"Line":1}},{"line":139,"address":[],"length":0,"stats":{"Line":4}},{"line":141,"address":[],"length":0,"stats":{"Line":0}}],"covered":22,"coverable":26},{"path":["/","home","botahamec","Projects","happylock","src","rwlock","write_lock.rs"],"content":"use std::fmt::Debug;\n\nuse lock_api::RawRwLock;\n\nuse crate::lockable::{Lockable, RawLock};\nuse crate::{Keyable, ThreadKey};\n\nuse super::{RwLock, RwLockWriteGuard, RwLockWriteRef, WriteLock};\n\nunsafe impl\u003cT, R: RawRwLock\u003e RawLock for WriteLock\u003c'_, T, R\u003e {\n\tfn poison(\u0026self) {\n\t\tself.0.poison()\n\t}\n\n\tunsafe fn raw_write(\u0026self) {\n\t\tself.0.raw_write()\n\t}\n\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool {\n\t\tself.0.raw_try_write()\n\t}\n\n\tunsafe fn raw_unlock_write(\u0026self) {\n\t\tself.0.raw_unlock_write()\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tself.0.raw_write()\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tself.0.raw_try_write()\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tself.0.raw_unlock_write()\n\t}\n}\n\nunsafe impl\u003cT, R: RawRwLock\u003e Lockable for WriteLock\u003c'_, T, R\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= RwLockWriteRef\u003c'g, T, R\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= \u0026'a mut T\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tptrs.push(self)\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tRwLockWriteRef::new(self.as_ref())\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.0.data_mut()\n\t}\n}\n\n// Technically, the exclusive locks can also be shared, but there's currently\n// no way to express that. I don't think I want to ever express that.\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug, R: RawRwLock\u003e Debug for WriteLock\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t// safety: this is just a try lock, and the value is dropped\n\t\t// immediately after, so there's no risk of blocking ourselves\n\t\t// or any other threads\n\t\t// It makes zero sense to try using an exclusive lock for this, so this\n\t\t// is the only time when WriteLock does a read.\n\t\tif let Some(value) = unsafe { self.0.try_read_no_key() } {\n\t\t\tf.debug_struct(\"WriteLock\").field(\"data\", \u0026\u0026*value).finish()\n\t\t} else {\n\t\t\tstruct LockedPlaceholder;\n\t\t\timpl Debug for LockedPlaceholder {\n\t\t\t\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t\t\t\tf.write_str(\"\u003clocked\u003e\")\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tf.debug_struct(\"WriteLock\")\n\t\t\t\t.field(\"data\", \u0026LockedPlaceholder)\n\t\t\t\t.finish()\n\t\t}\n\t}\n}\n\nimpl\u003c'l, T, R\u003e From\u003c\u0026'l RwLock\u003cT, R\u003e\u003e for WriteLock\u003c'l, T, R\u003e {\n\tfn from(value: \u0026'l RwLock\u003cT, R\u003e) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\nimpl\u003cT: ?Sized, R\u003e AsRef\u003cRwLock\u003cT, R\u003e\u003e for WriteLock\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026RwLock\u003cT, R\u003e {\n\t\tself.0\n\t}\n}\n\nimpl\u003c'l, T, R\u003e WriteLock\u003c'l, T, R\u003e {\n\t/// Creates a new `WriteLock` which accesses the given [`RwLock`]\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{rwlock::WriteLock, RwLock};\n\t///\n\t/// let lock = RwLock::new(5);\n\t/// let write_lock = WriteLock::new(\u0026lock);\n\t/// ```\n\t#[must_use]\n\tpub const fn new(rwlock: \u0026'l RwLock\u003cT, R\u003e) -\u003e Self {\n\t\tSelf(rwlock)\n\t}\n}\n\nimpl\u003cT, R: RawRwLock\u003e WriteLock\u003c'_, T, R\u003e {\n\tpub fn scoped_lock\u003c'a, Ret\u003e(\u0026'a self, key: impl Keyable, f: impl Fn(\u0026'a mut T) -\u003e Ret) -\u003e Ret {\n\t\tself.0.scoped_write(key, f)\n\t}\n\n\tpub fn scoped_try_lock\u003c'a, Key: Keyable, Ret\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl Fn(\u0026'a mut T) -\u003e Ret,\n\t) -\u003e Result\u003cRet, Key\u003e {\n\t\tself.0.scoped_try_write(key, f)\n\t}\n\n\t/// Locks the underlying [`RwLock`] with exclusive write access, blocking\n\t/// the current until it can be acquired.\n\t///\n\t/// This function will not return while other writers or readers currently\n\t/// have access to the lock.\n\t///\n\t/// Returns an RAII guard which will drop the write access of this `RwLock`\n\t/// when dropped.\n\t///\n\t/// Because this method takes a [`ThreadKey`], it's not possible for this\n\t/// method to cause a deadlock.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, RwLock};\n\t/// use happylock::rwlock::WriteLock;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(1);\n\t/// let writer = WriteLock::new(\u0026lock);\n\t///\n\t/// let mut n = writer.lock(key);\n\t/// *n += 2;\n\t/// ```\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\t#[must_use]\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e RwLockWriteGuard\u003c'_, T, R\u003e {\n\t\tself.0.write(key)\n\t}\n\n\t/// Attempts to lock the underlying [`RwLock`] with exclusive write access.\n\t///\n\t/// This function does not block. If the lock could not be acquired at this\n\t/// time, then `None` is returned. Otherwise, an RAII guard is returned\n\t/// which will release the lock when it is dropped.\n\t///\n\t/// This function does not provide any guarantees with respect to the\n\t/// ordering of whether contentious readers or writers will acquire the\n\t/// lock first.\n\t///\n\t/// # Errors\n\t///\n\t/// If the [`RwLock`] could not be acquired because it was already locked,\n\t/// then an error will be returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::rwlock::WriteLock;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(1);\n\t/// let writer = WriteLock::new(\u0026lock);\n\t///\n\t/// match writer.try_lock(key) {\n\t/// Ok(n) =\u003e assert_eq!(*n, 1),\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t/// ```\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e Result\u003cRwLockWriteGuard\u003c'_, T, R\u003e, ThreadKey\u003e {\n\t\tself.0.try_write(key)\n\t}\n\n\t// There's no `try_lock_no_key`. Instead, `try_read_no_key` is called on\n\t// the referenced `RwLock`.\n\n\t/// Immediately drops the guard, and consequently releases the exclusive\n\t/// lock on the underlying [`RwLock`].\n\t///\n\t/// This function is equivalent to calling [`drop`] on the guard, except\n\t/// that it returns the key that was used to create it. Alternately, the\n\t/// guard will be automatically dropped when it goes out of scope.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::rwlock::WriteLock;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(0);\n\t/// let writer = WriteLock::new(\u0026lock);\n\t///\n\t/// let mut guard = writer.lock(key);\n\t/// *guard += 20;\n\t/// let key = WriteLock::unlock(guard);\n\t/// ```\n\t#[must_use]\n\tpub fn unlock(guard: RwLockWriteGuard\u003c'_, T, R\u003e) -\u003e ThreadKey {\n\t\tRwLock::unlock_write(guard)\n\t}\n}\n","traces":[{"line":11,"address":[969664,969648],"length":1,"stats":{"Line":1}},{"line":12,"address":[],"length":0,"stats":{"Line":1}},{"line":15,"address":[],"length":0,"stats":{"Line":1}},{"line":16,"address":[],"length":0,"stats":{"Line":1}},{"line":19,"address":[969712,969744],"length":1,"stats":{"Line":1}},{"line":20,"address":[],"length":0,"stats":{"Line":1}},{"line":23,"address":[],"length":0,"stats":{"Line":1}},{"line":24,"address":[],"length":0,"stats":{"Line":1}},{"line":27,"address":[],"length":0,"stats":{"Line":0}},{"line":28,"address":[],"length":0,"stats":{"Line":0}},{"line":31,"address":[],"length":0,"stats":{"Line":0}},{"line":32,"address":[],"length":0,"stats":{"Line":0}},{"line":35,"address":[],"length":0,"stats":{"Line":0}},{"line":36,"address":[],"length":0,"stats":{"Line":0}},{"line":51,"address":[],"length":0,"stats":{"Line":2}},{"line":52,"address":[],"length":0,"stats":{"Line":2}},{"line":55,"address":[],"length":0,"stats":{"Line":1}},{"line":56,"address":[],"length":0,"stats":{"Line":1}},{"line":59,"address":[970096],"length":1,"stats":{"Line":1}},{"line":60,"address":[],"length":0,"stats":{"Line":1}},{"line":94,"address":[],"length":0,"stats":{"Line":1}},{"line":95,"address":[],"length":0,"stats":{"Line":1}},{"line":100,"address":[970128],"length":1,"stats":{"Line":1}},{"line":101,"address":[],"length":0,"stats":{"Line":1}},{"line":117,"address":[970160,970144],"length":1,"stats":{"Line":2}},{"line":118,"address":[],"length":0,"stats":{"Line":0}},{"line":123,"address":[],"length":0,"stats":{"Line":1}},{"line":124,"address":[],"length":0,"stats":{"Line":1}},{"line":127,"address":[970208],"length":1,"stats":{"Line":1}},{"line":132,"address":[970217],"length":1,"stats":{"Line":1}},{"line":163,"address":[],"length":0,"stats":{"Line":1}},{"line":164,"address":[],"length":0,"stats":{"Line":1}},{"line":197,"address":[],"length":0,"stats":{"Line":1}},{"line":198,"address":[],"length":0,"stats":{"Line":1}},{"line":226,"address":[],"length":0,"stats":{"Line":1}},{"line":227,"address":[],"length":0,"stats":{"Line":1}}],"covered":29,"coverable":36},{"path":["/","home","botahamec","Projects","happylock","src","rwlock.rs"],"content":"use std::cell::UnsafeCell;\nuse std::marker::PhantomData;\n\nuse lock_api::RawRwLock;\n\nuse crate::poisonable::PoisonFlag;\nuse crate::ThreadKey;\n\nmod rwlock;\n\nmod read_lock;\nmod write_lock;\n\nmod read_guard;\nmod write_guard;\n\n#[cfg(feature = \"spin\")]\npub type SpinRwLock\u003cT\u003e = RwLock\u003cT, spin::RwLock\u003c()\u003e\u003e;\n\n#[cfg(feature = \"parking_lot\")]\npub type ParkingRwLock\u003cT\u003e = RwLock\u003cT, parking_lot::RawRwLock\u003e;\n\n/// A reader-writer lock\n///\n/// This type of lock allows a number of readers or at most one writer at any\n/// point in time. The write portion of this lock typically allows modification\n/// of the underlying data (exclusive access) and the read portion of this lock\n/// typically allows for read-only access (shared access).\n///\n/// In comparison, a [`Mutex`] does not distinguish between readers or writers\n/// that acquire the lock, therefore blocking any threads waiting for the lock\n/// to become available. An `RwLock` will allow any number of readers to\n/// acquire the lock as long as a writer is not holding the lock.\n///\n/// The type parameter T represents the data that this lock protects. It is\n/// required that T satisfies [`Send`] to be shared across threads and [`Sync`]\n/// to allow concurrent access through readers. The RAII guard returned from\n/// the locking methods implement [`Deref`] (and [`DerefMut`] for the `write`\n/// methods) to allow access to the content of the lock.\n///\n/// Locking the mutex on a thread that already locked it is impossible, due to\n/// the requirement of the [`ThreadKey`]. Therefore, this will never deadlock.\n///\n/// [`ThreadKey`]: `crate::ThreadKey`\n/// [`Mutex`]: `crate::mutex::Mutex`\n/// [`Deref`]: `std::ops::Deref`\n/// [`DerefMut`]: `std::ops::DerefMut`\npub struct RwLock\u003cT: ?Sized, R\u003e {\n\traw: R,\n\tpoison: PoisonFlag,\n\tdata: UnsafeCell\u003cT\u003e,\n}\n\n/// Grants read access to an [`RwLock`]\n///\n/// This structure is designed to be used in a [`LockCollection`] to indicate\n/// that only read access is needed to the data.\n///\n/// [`LockCollection`]: `crate::LockCollection`\n#[repr(transparent)]\npub struct ReadLock\u003c'l, T: ?Sized, R\u003e(\u0026'l RwLock\u003cT, R\u003e);\n\n/// Grants write access to an [`RwLock`]\n///\n/// This structure is designed to be used in a [`LockCollection`] to indicate\n/// that write access is needed to the data.\n///\n/// [`LockCollection`]: `crate::LockCollection`\n#[repr(transparent)]\npub struct WriteLock\u003c'l, T: ?Sized, R\u003e(\u0026'l RwLock\u003cT, R\u003e);\n\n/// RAII structure that unlocks the shared read access to a [`RwLock`]\n///\n/// This is similar to [`RwLockReadRef`], except it does not hold a\n/// [`Keyable`].\npub struct RwLockReadRef\u003c'a, T: ?Sized, R: RawRwLock\u003e(\n\t\u0026'a RwLock\u003cT, R\u003e,\n\tPhantomData\u003cR::GuardMarker\u003e,\n);\n\n/// RAII structure that unlocks the exclusive write access to a [`RwLock`]\n///\n/// This is similar to [`RwLockWriteRef`], except it does not hold a\n/// [`Keyable`].\npub struct RwLockWriteRef\u003c'a, T: ?Sized, R: RawRwLock\u003e(\n\t\u0026'a RwLock\u003cT, R\u003e,\n\tPhantomData\u003cR::GuardMarker\u003e,\n);\n\n/// RAII structure used to release the shared read access of a lock when\n/// dropped.\n///\n/// This structure is created by the [`read`] and [`try_read`] methods on\n/// [`RwLock`].\n///\n/// [`read`]: `RwLock::read`\n/// [`try_read`]: `RwLock::try_read`\npub struct RwLockReadGuard\u003c'a, T: ?Sized, R: RawRwLock\u003e {\n\trwlock: RwLockReadRef\u003c'a, T, R\u003e,\n\tthread_key: ThreadKey,\n}\n\n/// RAII structure used to release the exclusive write access of a lock when\n/// dropped.\n///\n/// This structure is created by the [`write`] and [`try_write`] methods on\n/// [`RwLock`]\n///\n/// [`try_write`]: `RwLock::try_write`\npub struct RwLockWriteGuard\u003c'a, T: ?Sized, R: RawRwLock\u003e {\n\trwlock: RwLockWriteRef\u003c'a, T, R\u003e,\n\tthread_key: ThreadKey,\n}\n\n#[cfg(test)]\nmod tests {\n\tuse crate::lockable::Lockable;\n\tuse crate::lockable::RawLock;\n\tuse crate::LockCollection;\n\tuse crate::RwLock;\n\tuse crate::ThreadKey;\n\n\tuse super::*;\n\n\t#[test]\n\tfn unlocked_when_initialized() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\n\t\tassert!(!lock.is_locked());\n\t\tassert!(lock.try_write(key).is_ok());\n\t}\n\n\t#[test]\n\tfn read_lock_unlocked_when_initialized() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet reader = ReadLock::new(\u0026lock);\n\n\t\tassert!(reader.try_lock(key).is_ok());\n\t}\n\n\t#[test]\n\tfn read_lock_from_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::from(\"Hello, world!\");\n\t\tlet reader = ReadLock::from(\u0026lock);\n\n\t\tlet guard = reader.lock(key);\n\t\tassert_eq!(*guard, \"Hello, world!\");\n\t}\n\n\t#[test]\n\tfn read_lock_scoped_works() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(42);\n\t\tlet reader = ReadLock::new(\u0026lock);\n\n\t\treader.scoped_lock(\u0026mut key, |num| assert_eq!(*num, 42));\n\t}\n\n\t#[test]\n\tfn read_lock_scoped_try_fails_during_write() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(42);\n\t\tlet reader = ReadLock::new(\u0026lock);\n\t\tlet guard = lock.write(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = reader.scoped_try_lock(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn write_lock_unlocked_when_initialized() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet writer = WriteLock::new(\u0026lock);\n\n\t\tassert!(writer.try_lock(key).is_ok());\n\t}\n\n\t#[test]\n\tfn read_lock_get_ptrs() {\n\t\tlet rwlock = RwLock::new(5);\n\t\tlet readlock = ReadLock::new(\u0026rwlock);\n\t\tlet mut lock_ptrs = Vec::new();\n\t\treadlock.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tassert!(std::ptr::addr_eq(lock_ptrs[0], \u0026readlock));\n\t}\n\n\t#[test]\n\tfn write_lock_get_ptrs() {\n\t\tlet rwlock = RwLock::new(5);\n\t\tlet writelock = WriteLock::new(\u0026rwlock);\n\t\tlet mut lock_ptrs = Vec::new();\n\t\twritelock.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tassert!(std::ptr::addr_eq(lock_ptrs[0], \u0026writelock));\n\t}\n\n\t#[test]\n\tfn write_lock_scoped_works() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(42);\n\t\tlet writer = WriteLock::new(\u0026lock);\n\n\t\twriter.scoped_lock(\u0026mut key, |num| assert_eq!(*num, 42));\n\t}\n\n\t#[test]\n\tfn write_lock_scoped_try_fails_during_write() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(42);\n\t\tlet writer = WriteLock::new(\u0026lock);\n\t\tlet guard = lock.write(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = writer.scoped_try_lock(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn locked_after_read() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\n\t\tlet guard = lock.read(key);\n\n\t\tassert!(lock.is_locked());\n\t\tdrop(guard)\n\t}\n\n\t#[test]\n\tfn locked_after_using_read_lock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet reader = ReadLock::new(\u0026lock);\n\n\t\tlet guard = reader.lock(key);\n\n\t\tassert!(lock.is_locked());\n\t\tdrop(guard)\n\t}\n\n\t#[test]\n\tfn locked_after_write() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\n\t\tlet guard = lock.write(key);\n\n\t\tassert!(lock.is_locked());\n\t\tdrop(guard)\n\t}\n\n\t#[test]\n\tfn locked_after_using_write_lock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet writer = WriteLock::new(\u0026lock);\n\n\t\tlet guard = writer.lock(key);\n\n\t\tassert!(lock.is_locked());\n\t\tdrop(guard)\n\t}\n\n\t#[test]\n\tfn locked_after_scoped_write() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"Hello, world!\");\n\n\t\tlock.scoped_write(\u0026mut key, |guard| {\n\t\t\tassert!(lock.is_locked());\n\t\t\tassert_eq!(*guard, \"Hello, world!\");\n\n\t\t\tstd::thread::scope(|s| {\n\t\t\t\ts.spawn(|| {\n\t\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\t\tassert!(lock.try_read(key).is_err());\n\t\t\t\t});\n\t\t\t})\n\t\t})\n\t}\n\n\t#[test]\n\tfn get_mut_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mut lock = crate::RwLock::from(42);\n\n\t\tlet mut_ref = lock.get_mut();\n\t\t*mut_ref = 24;\n\n\t\tlock.scoped_read(key, |guard| assert_eq!(*guard, 24))\n\t}\n\n\t#[test]\n\tfn try_write_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"Hello\");\n\t\tlet guard = lock.write(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = lock.try_write(key);\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn try_read_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"Hello\");\n\t\tlet guard = lock.write(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = lock.try_read(key);\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn read_display_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet guard = lock.read(key);\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn write_display_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet guard = lock.write(key);\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn read_ref_display_works() {\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet guard = unsafe { lock.try_read_no_key().unwrap() };\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn write_ref_display_works() {\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet guard = unsafe { lock.try_write_no_key().unwrap() };\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn dropping_read_ref_releases_rwlock() {\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\n\t\tlet guard = unsafe { lock.try_read_no_key().unwrap() };\n\t\tdrop(guard);\n\n\t\tassert!(!lock.is_locked());\n\t}\n\n\t#[test]\n\tfn dropping_write_guard_releases_rwlock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\n\t\tlet guard = lock.write(key);\n\t\tdrop(guard);\n\n\t\tassert!(!lock.is_locked());\n\t}\n\n\t#[test]\n\tfn unlock_write() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"Hello, world\");\n\n\t\tlet mut guard = lock.write(key);\n\t\t*guard = \"Goodbye, world!\";\n\t\tlet key = RwLock::unlock_write(guard);\n\n\t\tlet guard = lock.read(key);\n\t\tassert_eq!(*guard, \"Goodbye, world!\");\n\t}\n\n\t#[test]\n\tfn unlock_read() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"Hello, world\");\n\n\t\tlet guard = lock.read(key);\n\t\tassert_eq!(*guard, \"Hello, world\");\n\t\tlet key = RwLock::unlock_read(guard);\n\n\t\tlet guard = lock.write(key);\n\t\tassert_eq!(*guard, \"Hello, world\");\n\t}\n\n\t#[test]\n\tfn unlock_read_lock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"Hello, world\");\n\t\tlet reader = ReadLock::new(\u0026lock);\n\n\t\tlet guard = reader.lock(key);\n\t\tlet key = ReadLock::unlock(guard);\n\n\t\tlock.write(key);\n\t}\n\n\t#[test]\n\tfn unlock_write_lock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"Hello, world\");\n\t\tlet writer = WriteLock::from(\u0026lock);\n\n\t\tlet guard = writer.lock(key);\n\t\tlet key = WriteLock::unlock(guard);\n\n\t\tlock.write(key);\n\t}\n\n\t#[test]\n\tfn read_lock_in_collection() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"hi\");\n\t\tlet collection = LockCollection::try_new(ReadLock::new(\u0026lock)).unwrap();\n\n\t\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard, \"hi\");\n\t\t});\n\t\tcollection.scoped_read(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard, \"hi\");\n\t\t});\n\t\tassert!(collection\n\t\t\t.scoped_try_lock(\u0026mut key, |guard| {\n\t\t\t\tassert_eq!(*guard, \"hi\");\n\t\t\t})\n\t\t\t.is_ok());\n\t\tassert!(collection\n\t\t\t.scoped_try_read(\u0026mut key, |guard| {\n\t\t\t\tassert_eq!(*guard, \"hi\");\n\t\t\t})\n\t\t\t.is_ok());\n\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(**guard, \"hi\");\n\n\t\tlet key = LockCollection::\u003cReadLock\u003c_, _\u003e\u003e::unlock(guard);\n\t\tlet guard = collection.read(key);\n\t\tassert_eq!(**guard, \"hi\");\n\n\t\tlet key = LockCollection::\u003cReadLock\u003c_, _\u003e\u003e::unlock(guard);\n\t\tlet guard = lock.write(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_lock(key);\n\t\t\t\tassert!(guard.is_err());\n\t\t\t});\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_read(key);\n\t\t\t\tassert!(guard.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn write_lock_in_collection() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"hi\");\n\t\tlet collection = LockCollection::try_new(WriteLock::new(\u0026lock)).unwrap();\n\n\t\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard, \"hi\");\n\t\t});\n\t\tassert!(collection\n\t\t\t.scoped_try_lock(\u0026mut key, |guard| {\n\t\t\t\tassert_eq!(*guard, \"hi\");\n\t\t\t})\n\t\t\t.is_ok());\n\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(**guard, \"hi\");\n\n\t\tlet key = LockCollection::\u003cWriteLock\u003c_, _\u003e\u003e::unlock(guard);\n\t\tlet guard = lock.write(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_lock(key);\n\t\t\t\tassert!(guard.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn read_ref_as_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = LockCollection::new(crate::RwLock::new(\"hi\"));\n\t\tlet guard = lock.read(key);\n\n\t\tassert_eq!(*(*guard).as_ref(), \"hi\");\n\t}\n\n\t#[test]\n\tfn read_guard_as_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"hi\");\n\t\tlet guard = lock.read(key);\n\n\t\tassert_eq!(*guard.as_ref(), \"hi\");\n\t}\n\n\t#[test]\n\tfn write_ref_as_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = LockCollection::new(crate::RwLock::new(\"hi\"));\n\t\tlet guard = lock.lock(key);\n\n\t\tassert_eq!(*(*guard).as_ref(), \"hi\");\n\t}\n\n\t#[test]\n\tfn write_guard_as_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"hi\");\n\t\tlet guard = lock.write(key);\n\n\t\tassert_eq!(*guard.as_ref(), \"hi\");\n\t}\n\n\t#[test]\n\tfn write_guard_as_mut() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"hi\");\n\t\tlet mut guard = lock.write(key);\n\n\t\tassert_eq!(*guard.as_mut(), \"hi\");\n\t\t*guard.as_mut() = \"foo\";\n\t\tassert_eq!(*guard.as_mut(), \"foo\");\n\t}\n\n\t#[test]\n\tfn poison_read_lock() {\n\t\tlet lock = crate::RwLock::new(\"hi\");\n\t\tlet reader = ReadLock::new(\u0026lock);\n\n\t\treader.poison();\n\t\tassert!(lock.poison.is_poisoned());\n\t}\n\n\t#[test]\n\tfn poison_write_lock() {\n\t\tlet lock = crate::RwLock::new(\"hi\");\n\t\tlet reader = WriteLock::new(\u0026lock);\n\n\t\treader.poison();\n\t\tassert!(lock.poison.is_poisoned());\n\t}\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","src","thread.rs"],"content":"use std::marker::PhantomData;\n\nmod scope;\n\n#[derive(Debug)]\npub struct Scope\u003c'scope, 'env: 'scope\u003e(PhantomData\u003c(\u0026'env (), \u0026'scope ())\u003e);\n\n#[derive(Debug)]\npub struct ScopedJoinHandle\u003c'scope, T\u003e {\n\thandle: std::thread::JoinHandle\u003cT\u003e,\n\t_phantom: PhantomData\u003c\u0026'scope ()\u003e,\n}\n\npub struct JoinHandle\u003cT\u003e {\n\thandle: std::thread::JoinHandle\u003cT\u003e,\n\tkey: crate::ThreadKey,\n}\n\npub struct ThreadBuilder(std::thread::Builder);\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","evil_mutex.rs"],"content":"use std::sync::Arc;\n\nuse happylock::collection::{BoxedLockCollection, RetryingLockCollection};\nuse happylock::mutex::Mutex;\nuse happylock::ThreadKey;\nuse lock_api::{GuardNoSend, RawMutex};\n\nstruct EvilMutex {\n\tinner: parking_lot::RawMutex,\n}\n\nunsafe impl RawMutex for EvilMutex {\n\t#[allow(clippy::declare_interior_mutable_const)]\n\tconst INIT: Self = Self {\n\t\tinner: parking_lot::RawMutex::INIT,\n\t};\n\n\ttype GuardMarker = GuardNoSend;\n\n\tfn lock(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn try_lock(\u0026self) -\u003e bool {\n\t\tself.inner.try_lock()\n\t}\n\n\tunsafe fn unlock(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n}\n\n#[test]\nfn boxed_mutexes() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet good_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(5));\n\tlet evil_mutex: Arc\u003cMutex\u003ci32, EvilMutex\u003e\u003e = Arc::new(Mutex::new(7));\n\tlet useless_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(10));\n\tlet c_good = Arc::clone(\u0026good_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\t_ = collection.lock(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(good_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n\tassert!(evil_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n}\n\n#[test]\nfn retrying_mutexes() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet good_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(5));\n\tlet evil_mutex: Arc\u003cMutex\u003ci32, EvilMutex\u003e\u003e = Arc::new(Mutex::new(7));\n\tlet useless_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(10));\n\tlet c_good = Arc::clone(\u0026good_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tRetryingLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\tcollection.lock(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(good_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n\tassert!(evil_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","evil_rwlock.rs"],"content":"use std::panic::AssertUnwindSafe;\nuse std::sync::Arc;\n\nuse happylock::collection::{BoxedLockCollection, RetryingLockCollection};\nuse happylock::rwlock::RwLock;\nuse happylock::ThreadKey;\nuse lock_api::{GuardNoSend, RawRwLock};\n\nstruct EvilRwLock {\n\tinner: parking_lot::RawRwLock,\n}\n\nunsafe impl RawRwLock for EvilRwLock {\n\t#[allow(clippy::declare_interior_mutable_const)]\n\tconst INIT: Self = Self {\n\t\tinner: parking_lot::RawRwLock::INIT,\n\t};\n\n\ttype GuardMarker = GuardNoSend;\n\n\tfn lock_shared(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn try_lock_shared(\u0026self) -\u003e bool {\n\t\tself.inner.try_lock_shared()\n\t}\n\n\tunsafe fn unlock_shared(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn lock_exclusive(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn try_lock_exclusive(\u0026self) -\u003e bool {\n\t\tself.inner.try_lock_exclusive()\n\t}\n\n\tunsafe fn unlock_exclusive(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n}\n\n#[test]\nfn boxed_rwlocks() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet good_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(5));\n\tlet evil_mutex: Arc\u003cRwLock\u003ci32, EvilRwLock\u003e\u003e = Arc::new(RwLock::new(7));\n\tlet useless_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(10));\n\tlet c_good = Arc::clone(\u0026good_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\t_ = collection.lock(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(good_mutex.scoped_try_write(\u0026mut key, |_| {}).is_ok());\n\tassert!(evil_mutex.scoped_try_write(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_write(\u0026mut key, |_| {}).is_ok());\n\n\tstd::thread::scope(|s| {\n\t\ts.spawn(|| {\n\t\t\tlet evil_mutex = AssertUnwindSafe(evil_mutex);\n\t\t\tlet r = std::panic::catch_unwind(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tevil_mutex.write(key);\n\t\t\t});\n\n\t\t\tassert!(r.is_err());\n\t\t});\n\n\t\ts.spawn(|| {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\tgood_mutex.write(key);\n\t\t});\n\t});\n}\n\n#[test]\nfn retrying_rwlocks() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet good_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(5));\n\tlet evil_mutex: Arc\u003cRwLock\u003ci32, EvilRwLock\u003e\u003e = Arc::new(RwLock::new(7));\n\tlet useless_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(10));\n\tlet c_good = Arc::clone(\u0026good_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tRetryingLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\tcollection.lock(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(good_mutex.scoped_try_write(\u0026mut key, |_| {}).is_ok());\n\tassert!(evil_mutex.scoped_try_write(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_write(\u0026mut key, |_| {}).is_ok());\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","evil_try_mutex.rs"],"content":"use std::sync::Arc;\n\nuse happylock::{\n\tcollection::{BoxedLockCollection, RetryingLockCollection},\n\tmutex::Mutex,\n\tThreadKey,\n};\nuse lock_api::{GuardNoSend, RawMutex};\n\nstruct EvilMutex {\n\tinner: parking_lot::RawMutex,\n}\n\nunsafe impl RawMutex for EvilMutex {\n\t#[allow(clippy::declare_interior_mutable_const)]\n\tconst INIT: Self = Self {\n\t\tinner: parking_lot::RawMutex::INIT,\n\t};\n\n\ttype GuardMarker = GuardNoSend;\n\n\tfn lock(\u0026self) {\n\t\tself.inner.lock()\n\t}\n\n\tfn try_lock(\u0026self) -\u003e bool {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tunsafe fn unlock(\u0026self) {\n\t\tself.inner.unlock()\n\t}\n}\n\n#[test]\nfn boxed_mutexes() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet good_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(5));\n\tlet evil_mutex: Arc\u003cMutex\u003ci32, EvilMutex\u003e\u003e = Arc::new(Mutex::new(7));\n\tlet useless_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(10));\n\tlet c_good = Arc::clone(\u0026good_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\tlet g = collection.try_lock(key);\n\t\tprintln!(\"{}\", g.unwrap().1);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(good_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n\tassert!(evil_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n}\n\n#[test]\nfn retrying_mutexes() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet good_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(5));\n\tlet evil_mutex: Arc\u003cMutex\u003ci32, EvilMutex\u003e\u003e = Arc::new(Mutex::new(7));\n\tlet useless_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(10));\n\tlet c_good = Arc::clone(\u0026good_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tRetryingLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\tlet _ = collection.try_lock(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(good_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n\tassert!(evil_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","evil_try_rwlock.rs"],"content":"use std::sync::Arc;\n\nuse happylock::collection::{BoxedLockCollection, RetryingLockCollection};\nuse happylock::rwlock::RwLock;\nuse happylock::ThreadKey;\nuse lock_api::{GuardNoSend, RawRwLock};\n\nstruct EvilRwLock {\n\tinner: parking_lot::RawRwLock,\n}\n\nunsafe impl RawRwLock for EvilRwLock {\n\t#[allow(clippy::declare_interior_mutable_const)]\n\tconst INIT: Self = Self {\n\t\tinner: parking_lot::RawRwLock::INIT,\n\t};\n\n\ttype GuardMarker = GuardNoSend;\n\n\tfn lock_shared(\u0026self) {\n\t\tself.inner.lock_shared()\n\t}\n\n\tfn try_lock_shared(\u0026self) -\u003e bool {\n\t\tpanic!(\"mwahahahaha\")\n\t}\n\n\tunsafe fn unlock_shared(\u0026self) {\n\t\tself.inner.unlock_shared()\n\t}\n\n\tfn lock_exclusive(\u0026self) {\n\t\tself.inner.lock_exclusive()\n\t}\n\n\tfn try_lock_exclusive(\u0026self) -\u003e bool {\n\t\tpanic!(\"mwahahahaha\")\n\t}\n\n\tunsafe fn unlock_exclusive(\u0026self) {\n\t\tself.inner.unlock_exclusive()\n\t}\n}\n\n#[test]\nfn boxed_rwlocks() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet good_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(5));\n\tlet evil_mutex: Arc\u003cRwLock\u003ci32, EvilRwLock\u003e\u003e = Arc::new(RwLock::new(7));\n\tlet useless_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(10));\n\tlet c_good = Arc::clone(\u0026good_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\tlet _ = collection.try_read(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(good_mutex.scoped_try_read(\u0026mut key, |_| {}).is_ok());\n\tassert!(evil_mutex.scoped_try_read(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_read(\u0026mut key, |_| {}).is_ok());\n}\n\n#[test]\nfn retrying_rwlocks() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet good_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(5));\n\tlet evil_mutex: Arc\u003cRwLock\u003ci32, EvilRwLock\u003e\u003e = Arc::new(RwLock::new(7));\n\tlet useless_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(10));\n\tlet c_good = Arc::clone(\u0026good_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tRetryingLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\t_ = collection.try_read(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(good_mutex.scoped_try_read(\u0026mut key, |_| {}).is_ok());\n\tassert!(evil_mutex.scoped_try_read(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_read(\u0026mut key, |_| {}).is_ok());\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","evil_unlock_mutex.rs"],"content":"use std::sync::Arc;\n\nuse happylock::collection::{BoxedLockCollection, RetryingLockCollection};\nuse happylock::mutex::Mutex;\nuse happylock::ThreadKey;\nuse lock_api::{GuardNoSend, RawMutex};\n\nstruct KindaEvilMutex {\n\tinner: parking_lot::RawMutex,\n}\n\nstruct EvilMutex {}\n\nunsafe impl RawMutex for KindaEvilMutex {\n\t#[allow(clippy::declare_interior_mutable_const)]\n\tconst INIT: Self = Self {\n\t\tinner: parking_lot::RawMutex::INIT,\n\t};\n\n\ttype GuardMarker = GuardNoSend;\n\n\tfn lock(\u0026self) {\n\t\tself.inner.lock()\n\t}\n\n\tfn try_lock(\u0026self) -\u003e bool {\n\t\tself.inner.try_lock()\n\t}\n\n\tunsafe fn unlock(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n}\n\nunsafe impl RawMutex for EvilMutex {\n\t#[allow(clippy::declare_interior_mutable_const)]\n\tconst INIT: Self = Self {};\n\n\ttype GuardMarker = GuardNoSend;\n\n\tfn lock(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn try_lock(\u0026self) -\u003e bool {\n\t\tpanic!(\"mwahahahaha\")\n\t}\n\n\tunsafe fn unlock(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n}\n\n#[test]\nfn boxed_mutexes() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet kinda_evil_mutex: Arc\u003cMutex\u003ci32, KindaEvilMutex\u003e\u003e = Arc::new(Mutex::new(5));\n\tlet evil_mutex: Arc\u003cMutex\u003ci32, EvilMutex\u003e\u003e = Arc::new(Mutex::new(7));\n\tlet useless_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(10));\n\tlet c_good = Arc::clone(\u0026kinda_evil_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\t_ = collection.lock(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(kinda_evil_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n\tassert!(evil_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n}\n\n#[test]\nfn retrying_mutexes() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet kinda_evil_mutex: Arc\u003cMutex\u003ci32, KindaEvilMutex\u003e\u003e = Arc::new(Mutex::new(5));\n\tlet evil_mutex: Arc\u003cMutex\u003ci32, EvilMutex\u003e\u003e = Arc::new(Mutex::new(7));\n\tlet useless_mutex: Arc\u003cMutex\u003ci32, parking_lot::RawMutex\u003e\u003e = Arc::new(Mutex::new(10));\n\tlet c_good = Arc::clone(\u0026kinda_evil_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tRetryingLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\tcollection.lock(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(kinda_evil_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n\tassert!(evil_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_lock(\u0026mut key, |_| {}).is_ok());\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","evil_unlock_rwlock.rs"],"content":"use std::sync::Arc;\n\nuse happylock::collection::{BoxedLockCollection, RetryingLockCollection};\nuse happylock::rwlock::RwLock;\nuse happylock::ThreadKey;\nuse lock_api::{GuardNoSend, RawRwLock};\n\nstruct KindaEvilRwLock {\n\tinner: parking_lot::RawRwLock,\n}\n\nstruct EvilRwLock {}\n\nunsafe impl RawRwLock for KindaEvilRwLock {\n\t#[allow(clippy::declare_interior_mutable_const)]\n\tconst INIT: Self = Self {\n\t\tinner: parking_lot::RawRwLock::INIT,\n\t};\n\n\ttype GuardMarker = GuardNoSend;\n\n\tfn lock_shared(\u0026self) {\n\t\tself.inner.lock_shared()\n\t}\n\n\tfn try_lock_shared(\u0026self) -\u003e bool {\n\t\tself.inner.try_lock_shared()\n\t}\n\n\tunsafe fn unlock_shared(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn lock_exclusive(\u0026self) {\n\t\tself.inner.lock_exclusive()\n\t}\n\n\tfn try_lock_exclusive(\u0026self) -\u003e bool {\n\t\tself.inner.try_lock_exclusive()\n\t}\n\n\tunsafe fn unlock_exclusive(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n}\n\nunsafe impl RawRwLock for EvilRwLock {\n\t#[allow(clippy::declare_interior_mutable_const)]\n\tconst INIT: Self = Self {};\n\n\ttype GuardMarker = GuardNoSend;\n\n\tfn lock_shared(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn try_lock_shared(\u0026self) -\u003e bool {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tunsafe fn unlock_shared(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn lock_exclusive(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n\n\tfn try_lock_exclusive(\u0026self) -\u003e bool {\n\t\tpanic!(\"mwahahahaha\")\n\t}\n\n\tunsafe fn unlock_exclusive(\u0026self) {\n\t\tpanic!(\"mwahahahaha\");\n\t}\n}\n\n#[test]\nfn boxed_rwlocks() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet kinda_evil_mutex: RwLock\u003ci32, KindaEvilRwLock\u003e = RwLock::new(5);\n\tlet evil_mutex: RwLock\u003ci32, EvilRwLock\u003e = RwLock::new(7);\n\tlet useless_mutex: RwLock\u003ci32, parking_lot::RawRwLock\u003e = RwLock::new(10);\n\n\tlet r = std::thread::scope(|s| {\n\t\tlet r = s\n\t\t\t.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet collection =\n\t\t\t\t\tBoxedLockCollection::try_new((\u0026kinda_evil_mutex, \u0026evil_mutex, \u0026useless_mutex))\n\t\t\t\t\t\t.unwrap();\n\t\t\t\t_ = collection.read(key);\n\t\t\t})\n\t\t\t.join();\n\n\t\tr\n\t});\n\n\tassert!(r.is_err());\n\tassert!(kinda_evil_mutex.scoped_try_write(\u0026mut key, |_| {}).is_err());\n\tassert!(evil_mutex.scoped_try_write(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_write(\u0026mut key, |_| {}).is_ok());\n}\n\n#[test]\nfn retrying_rwlocks() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet kinda_evil_mutex: Arc\u003cRwLock\u003ci32, KindaEvilRwLock\u003e\u003e = Arc::new(RwLock::new(5));\n\tlet evil_mutex: Arc\u003cRwLock\u003ci32, EvilRwLock\u003e\u003e = Arc::new(RwLock::new(7));\n\tlet useless_mutex: Arc\u003cRwLock\u003ci32, parking_lot::RawRwLock\u003e\u003e = Arc::new(RwLock::new(10));\n\tlet c_good = Arc::clone(\u0026kinda_evil_mutex);\n\tlet c_evil = Arc::clone(\u0026evil_mutex);\n\tlet c_useless = Arc::clone(\u0026useless_mutex);\n\n\tlet r = std::thread::spawn(move || {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tRetryingLockCollection::try_new((\u0026*c_good, \u0026*c_evil, \u0026*c_useless)).unwrap();\n\t\tcollection.read(key);\n\t})\n\t.join();\n\n\tassert!(r.is_err());\n\tassert!(kinda_evil_mutex.scoped_try_write(\u0026mut key, |_| {}).is_err());\n\tassert!(evil_mutex.scoped_try_write(\u0026mut key, |_| {}).is_err());\n\tassert!(useless_mutex.scoped_try_write(\u0026mut key, |_| {}).is_ok());\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","forget.rs"],"content":"use happylock::{Mutex, ThreadKey};\n\n#[test]\nfn no_new_threadkey_when_forgetting_lock() {\n\tlet key = ThreadKey::get().unwrap();\n\tlet mutex = Mutex::new(\"foo\".to_string());\n\n\tlet guard = mutex.lock(key);\n\tstd::mem::forget(guard);\n\n\tassert!(ThreadKey::get().is_none());\n}\n\n#[test]\nfn no_new_threadkey_in_scoped_lock() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tlet mutex = Mutex::new(\"foo\".to_string());\n\n\tmutex.scoped_lock(\u0026mut key, |_| {\n\t\tassert!(ThreadKey::get().is_none());\n\t});\n\n\tmutex.lock(key);\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","retry.rs"],"content":"use std::time::Duration;\n\nuse happylock::{collection::RetryingLockCollection, Mutex, ThreadKey};\n\nstatic MUTEX_1: Mutex\u003ci32\u003e = Mutex::new(1);\nstatic MUTEX_2: Mutex\u003ci32\u003e = Mutex::new(2);\nstatic MUTEX_3: Mutex\u003ci32\u003e = Mutex::new(3);\n\nfn thread_1() {\n\tlet key = ThreadKey::get().unwrap();\n\tlet mut guard = MUTEX_2.lock(key);\n\tstd::thread::sleep(Duration::from_millis(100));\n\t*guard = 5;\n}\n\nfn thread_2() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tstd::thread::sleep(Duration::from_millis(50));\n\tlet collection = RetryingLockCollection::try_new([\u0026MUTEX_1, \u0026MUTEX_2, \u0026MUTEX_3]).unwrap();\n\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\tassert_eq!(*guard[0], 4);\n\t\tassert_eq!(*guard[1], 5);\n\t\tassert_eq!(*guard[2], 3);\n\t});\n}\n\nfn thread_3() {\n\tlet key = ThreadKey::get().unwrap();\n\tstd::thread::sleep(Duration::from_millis(75));\n\tlet mut guard = MUTEX_1.lock(key);\n\tstd::thread::sleep(Duration::from_millis(100));\n\t*guard = 4;\n}\n\nfn thread_4() {\n\tlet mut key = ThreadKey::get().unwrap();\n\tstd::thread::sleep(Duration::from_millis(25));\n\tlet collection = RetryingLockCollection::try_new([\u0026MUTEX_1, \u0026MUTEX_2]).unwrap();\n\tassert!(collection.scoped_try_lock(\u0026mut key, |_| {}).is_err());\n}\n\n#[test]\nfn retries() {\n\tlet t1 = std::thread::spawn(thread_1);\n\tlet t2 = std::thread::spawn(thread_2);\n\tlet t3 = std::thread::spawn(thread_3);\n\tlet t4 = std::thread::spawn(thread_4);\n\n\tt1.join().unwrap();\n\tt2.join().unwrap();\n\tt3.join().unwrap();\n\tt4.join().unwrap();\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","tests","retry_rw.rs"],"content":"use std::time::Duration;\n\nuse happylock::{collection::RetryingLockCollection, RwLock, ThreadKey};\n\nstatic RWLOCK_1: RwLock\u003ci32\u003e = RwLock::new(1);\nstatic RWLOCK_2: RwLock\u003ci32\u003e = RwLock::new(2);\nstatic RWLOCK_3: RwLock\u003ci32\u003e = RwLock::new(3);\n\nfn thread_1() {\n\tlet key = ThreadKey::get().unwrap();\n\tlet mut guard = RWLOCK_2.write(key);\n\tstd::thread::sleep(Duration::from_millis(75));\n\tassert_eq!(*guard, 2);\n\t*guard = 5;\n}\n\nfn thread_2() {\n\tlet key = ThreadKey::get().unwrap();\n\tlet collection = RetryingLockCollection::try_new([\u0026RWLOCK_1, \u0026RWLOCK_2, \u0026RWLOCK_3]).unwrap();\n\tstd::thread::sleep(Duration::from_millis(25));\n\tlet guard = collection.read(key);\n\tassert_eq!(*guard[0], 1);\n\tassert_eq!(*guard[1], 5);\n\tassert_eq!(*guard[2], 3);\n}\n\nfn thread_3() {\n\tlet key = ThreadKey::get().unwrap();\n\tstd::thread::sleep(Duration::from_millis(50));\n\tlet guard = RWLOCK_1.write(key);\n\tstd::thread::sleep(Duration::from_millis(50));\n\tassert_eq!(*guard, 1);\n}\n\n#[test]\nfn retries() {\n\tlet t1 = std::thread::spawn(thread_1);\n\tlet t2 = std::thread::spawn(thread_2);\n\tlet t3 = std::thread::spawn(thread_3);\n\n\tt1.join().unwrap();\n\tt2.join().unwrap();\n\tt3.join().unwrap();\n}\n","traces":[],"covered":0,"coverable":0}]};
+ var data = {"files":[{"path":["/","home","botahamec","Projects","happylock","examples","basic.rs"],"content":"use std::thread;\n\nuse happylock::{Mutex, ThreadKey};\n\nconst N: usize = 10;\n\nstatic DATA: Mutex\u003ci32\u003e = Mutex::new(0);\n\nfn main() {\n\tlet mut threads = Vec::new();\n\tfor _ in 0..N {\n\t\tlet th = thread::spawn(move || {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\tlet mut data = DATA.lock(key);\n\t\t\t*data += 1;\n\t\t});\n\t\tthreads.push(th);\n\t}\n\n\tfor th in threads {\n\t\t_ = th.join();\n\t}\n\n\tlet key = ThreadKey::get().unwrap();\n\tlet data = DATA.lock(key);\n\tprintln!(\"{data}\");\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","examples","dining_philosophers.rs"],"content":"use std::{thread, time::Duration};\n\nuse happylock::{collection, Mutex, ThreadKey};\n\nstatic PHILOSOPHERS: [Philosopher; 5] = [\n\tPhilosopher {\n\t\tname: \"Socrates\",\n\t\tleft: 0,\n\t\tright: 1,\n\t},\n\tPhilosopher {\n\t\tname: \"John Rawls\",\n\t\tleft: 1,\n\t\tright: 2,\n\t},\n\tPhilosopher {\n\t\tname: \"Jeremy Bentham\",\n\t\tleft: 2,\n\t\tright: 3,\n\t},\n\tPhilosopher {\n\t\tname: \"John Stuart Mill\",\n\t\tleft: 3,\n\t\tright: 4,\n\t},\n\tPhilosopher {\n\t\tname: \"Judith Butler\",\n\t\tleft: 4,\n\t\tright: 0,\n\t},\n];\n\nstatic FORKS: [Mutex\u003c()\u003e; 5] = [\n\tMutex::new(()),\n\tMutex::new(()),\n\tMutex::new(()),\n\tMutex::new(()),\n\tMutex::new(()),\n];\n\nstruct Philosopher {\n\tname: \u0026'static str,\n\tleft: usize,\n\tright: usize,\n}\n\nimpl Philosopher {\n\tfn cycle(\u0026self) {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tthread::sleep(Duration::from_secs(1));\n\n\t\t// safety: no philosopher asks for the same fork twice\n\t\tlet forks = [\u0026FORKS[self.left], \u0026FORKS[self.right]];\n\t\tlet forks = unsafe { collection::RefLockCollection::new_unchecked(\u0026forks) };\n\t\tlet forks = forks.lock(key);\n\t\tprintln!(\"{} is eating...\", self.name);\n\t\tthread::sleep(Duration::from_secs(1));\n\t\tprintln!(\"{} is done eating\", self.name);\n\t\tdrop(forks);\n\t}\n}\n\nfn main() {\n\tlet handles: Vec\u003c_\u003e = PHILOSOPHERS\n\t\t.iter()\n\t\t.map(|philosopher| thread::spawn(move || philosopher.cycle()))\n\t\t// The `collect` is absolutely necessary, because we're using lazy\n\t\t// iterators. If `collect` isn't used, then the thread won't spawn\n\t\t// until we try to join on it.\n\t\t.collect();\n\n\tfor handle in handles {\n\t\t_ = handle.join();\n\t}\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","examples","dining_philosophers_retry.rs"],"content":"use std::{thread, time::Duration};\n\nuse happylock::{collection, Mutex, ThreadKey};\n\nstatic PHILOSOPHERS: [Philosopher; 5] = [\n\tPhilosopher {\n\t\tname: \"Socrates\",\n\t\tleft: 0,\n\t\tright: 1,\n\t},\n\tPhilosopher {\n\t\tname: \"John Rawls\",\n\t\tleft: 1,\n\t\tright: 2,\n\t},\n\tPhilosopher {\n\t\tname: \"Jeremy Bentham\",\n\t\tleft: 2,\n\t\tright: 3,\n\t},\n\tPhilosopher {\n\t\tname: \"John Stuart Mill\",\n\t\tleft: 3,\n\t\tright: 4,\n\t},\n\tPhilosopher {\n\t\tname: \"Judith Butler\",\n\t\tleft: 4,\n\t\tright: 0,\n\t},\n];\n\nstatic FORKS: [Mutex\u003c()\u003e; 5] = [\n\tMutex::new(()),\n\tMutex::new(()),\n\tMutex::new(()),\n\tMutex::new(()),\n\tMutex::new(()),\n];\n\nstruct Philosopher {\n\tname: \u0026'static str,\n\tleft: usize,\n\tright: usize,\n}\n\nimpl Philosopher {\n\tfn cycle(\u0026self) {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tthread::sleep(Duration::from_secs(1));\n\n\t\t// safety: no philosopher asks for the same fork twice\n\t\tlet forks = [\u0026FORKS[self.left], \u0026FORKS[self.right]];\n\t\tlet forks = unsafe { collection::RetryingLockCollection::new_unchecked(\u0026forks) };\n\t\tlet forks = forks.lock(key);\n\t\tprintln!(\"{} is eating...\", self.name);\n\t\tthread::sleep(Duration::from_secs(1));\n\t\tprintln!(\"{} is done eating\", self.name);\n\t\tdrop(forks);\n\t}\n}\n\nfn main() {\n\tlet handles: Vec\u003c_\u003e = PHILOSOPHERS\n\t\t.iter()\n\t\t.map(|philosopher| thread::spawn(move || philosopher.cycle()))\n\t\t// The `collect` is absolutely necessary, because we're using lazy\n\t\t// iterators. If `collect` isn't used, then the thread won't spawn\n\t\t// until we try to join on it.\n\t\t.collect();\n\n\tfor handle in handles {\n\t\t_ = handle.join();\n\t}\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","examples","double_mutex.rs"],"content":"use std::thread;\n\nuse happylock::{collection::RefLockCollection, Mutex, ThreadKey};\n\nconst N: usize = 10;\n\nstatic DATA: (Mutex\u003ci32\u003e, Mutex\u003cString\u003e) = (Mutex::new(0), Mutex::new(String::new()));\n\nfn main() {\n\tlet mut threads = Vec::new();\n\tfor _ in 0..N {\n\t\tlet th = thread::spawn(move || {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\tlet lock = RefLockCollection::new(\u0026DATA);\n\t\t\tlet mut guard = lock.lock(key);\n\t\t\t*guard.1 = (100 - *guard.0).to_string();\n\t\t\t*guard.0 += 1;\n\t\t});\n\t\tthreads.push(th);\n\t}\n\n\tfor th in threads {\n\t\t_ = th.join();\n\t}\n\n\tlet key = ThreadKey::get().unwrap();\n\tlet data = RefLockCollection::new(\u0026DATA);\n\tlet data = data.lock(key);\n\tprintln!(\"{}\", data.0);\n\tprintln!(\"{}\", data.1);\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","examples","fibonacci.rs"],"content":"use happylock::{collection, LockCollection, Mutex, ThreadKey};\nuse std::thread;\n\nconst N: usize = 36;\n\nstatic DATA: [Mutex\u003ci32\u003e; 2] = [Mutex::new(0), Mutex::new(1)];\n\nfn main() {\n\tlet mut threads = Vec::new();\n\tfor _ in 0..N {\n\t\tlet th = thread::spawn(move || {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\n\t\t\t// a reference to a type that implements `OwnedLockable` will never\n\t\t\t// contain duplicates, so no duplicate checking is needed.\n\t\t\tlet collection = collection::RetryingLockCollection::new_ref(\u0026DATA);\n\t\t\tlet mut guard = collection.lock(key);\n\n\t\t\tlet x = *guard[1];\n\t\t\t*guard[1] += *guard[0];\n\t\t\t*guard[0] = x;\n\t\t});\n\t\tthreads.push(th);\n\t}\n\n\tfor thread in threads {\n\t\t_ = thread.join();\n\t}\n\n\tlet key = ThreadKey::get().unwrap();\n\tlet data = LockCollection::new_ref(\u0026DATA);\n\tlet data = data.lock(key);\n\tprintln!(\"{}\", data[0]);\n\tprintln!(\"{}\", data[1]);\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","examples","list.rs"],"content":"use std::thread;\n\nuse happylock::{collection::RefLockCollection, Mutex, ThreadKey};\n\nconst N: usize = 10;\n\nstatic DATA: [Mutex\u003cusize\u003e; 6] = [\n\tMutex::new(0),\n\tMutex::new(1),\n\tMutex::new(2),\n\tMutex::new(3),\n\tMutex::new(4),\n\tMutex::new(5),\n];\n\nstatic SEED: Mutex\u003cu32\u003e = Mutex::new(42);\n\nfn random(key: \u0026mut ThreadKey) -\u003e usize {\n\tSEED.scoped_lock(key, |seed| {\n\t\tlet x = *seed;\n\t\tlet x = x ^ (x \u003c\u003c 13);\n\t\tlet x = x ^ (x \u003e\u003e 17);\n\t\tlet x = x ^ (x \u003c\u003c 5);\n\t\t*seed = x;\n\t\tx as usize\n\t})\n}\n\nfn main() {\n\tlet mut threads = Vec::new();\n\tfor _ in 0..N {\n\t\tlet th = thread::spawn(move || {\n\t\t\tlet mut key = ThreadKey::get().unwrap();\n\t\t\tloop {\n\t\t\t\tlet mut data = Vec::new();\n\t\t\t\tfor _ in 0..3 {\n\t\t\t\t\tlet rand = random(\u0026mut key);\n\t\t\t\t\tdata.push(\u0026DATA[rand % 6]);\n\t\t\t\t}\n\n\t\t\t\tlet Some(lock) = RefLockCollection::try_new(\u0026data) else {\n\t\t\t\t\tcontinue;\n\t\t\t\t};\n\t\t\t\tlet mut guard = lock.lock(key);\n\t\t\t\t*guard[0] += *guard[1];\n\t\t\t\t*guard[1] += *guard[2];\n\t\t\t\t*guard[2] += *guard[0];\n\n\t\t\t\treturn;\n\t\t\t}\n\t\t});\n\t\tthreads.push(th);\n\t}\n\n\tfor th in threads {\n\t\t_ = th.join();\n\t}\n\n\tlet key = ThreadKey::get().unwrap();\n\tlet data = RefLockCollection::new(\u0026DATA);\n\tlet data = data.lock(key);\n\tfor val in \u0026*data {\n\t\tprintln!(\"{val}\");\n\t}\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","src","collection","boxed.rs"],"content":"use std::cell::UnsafeCell;\nuse std::fmt::Debug;\n\nuse crate::lockable::{Lockable, LockableIntoInner, OwnedLockable, RawLock, Sharable};\nuse crate::{Keyable, ThreadKey};\n\nuse super::utils::{\n\tordered_contains_duplicates, scoped_read, scoped_try_read, scoped_try_write, scoped_write,\n};\nuse super::{utils, BoxedLockCollection, LockGuard};\n\nunsafe impl\u003cL: Lockable\u003e RawLock for BoxedLockCollection\u003cL\u003e {\n\t#[mutants::skip] // this should never be called\n\t#[cfg(not(tarpaulin_include))]\n\tfn poison(\u0026self) {\n\t\tfor lock in \u0026self.locks {\n\t\t\tlock.poison();\n\t\t}\n\t}\n\n\tunsafe fn raw_write(\u0026self) {\n\t\tutils::ordered_write(self.locks())\n\t}\n\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool {\n\t\tutils::ordered_try_write(self.locks())\n\t}\n\n\tunsafe fn raw_unlock_write(\u0026self) {\n\t\tfor lock in self.locks() {\n\t\t\tlock.raw_unlock_write();\n\t\t}\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tutils::ordered_read(self.locks());\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tutils::ordered_try_read(self.locks())\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tfor lock in self.locks() {\n\t\t\tlock.raw_unlock_read();\n\t\t}\n\t}\n}\n\nunsafe impl\u003cL: Lockable\u003e Lockable for BoxedLockCollection\u003cL\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= L::Guard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= L::DataMut\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\t// Doing it this way means that if a boxed collection is put inside a\n\t\t// different collection, it will use the other method of locking. However,\n\t\t// this prevents duplicate locks in a collection.\n\t\tptrs.extend_from_slice(\u0026self.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#[expect(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.child.cast_mut());\n\n\t\t\tdrop(boxed)\n\t\t}\n\t}\n}\n\nimpl\u003cT: ?Sized, L: AsRef\u003cT\u003e\u003e AsRef\u003cT\u003e for BoxedLockCollection\u003cL\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself.child().as_ref()\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cL: Debug\u003e Debug for BoxedLockCollection\u003cL\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tf.debug_struct(stringify!(BoxedLockCollection))\n\t\t\t.field(\"data\", \u0026self.child)\n\t\t\t// there's not much reason to show the sorted locks\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 collection = LockCollection::try_new([Mutex::new(42), Mutex::new(1)]).unwrap();\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mutex = \u0026collection.into_child()[0];\n\t/// mutex.scoped_lock(key, |guard| 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(\u0026raw mut 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.child.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 collection = LockCollection::try_new([Mutex::new(42), Mutex::new(1)]).unwrap();\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let mutex1 = \u0026collection.child()[0];\n\t/// let mutex2 = \u0026collection.child()[1];\n\t/// mutex1.scoped_lock(\u0026mut key, |guard| assert_eq!(*guard, 42));\n\t/// mutex2.scoped_lock(\u0026mut key, |guard| assert_eq!(*guard, 1));\n\t/// ```\n\t#[must_use]\n\tpub fn child(\u0026self) -\u003e \u0026L {\n\t\tunsafe {\n\t\t\tself.child\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 = unsafe { std::mem::transmute(locks) };\n\t\tlet data = \u0026raw const *data;\n\t\tSelf { child: 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\t/// Acquires an exclusive lock, blocking until it is safe to do so, and then\n\t/// unlocks after the provided function returns.\n\t///\n\t/// This function is useful to ensure that the data is never accidentally\n\t/// locked forever by leaking the guard. Even if the function panics, this\n\t/// function will gracefully notice the panic, and unlock. This function\n\t/// provides no guarantees with respect to the ordering of whether contentious\n\t/// readers or writers will acquire the lock first.\n\t///\n\t/// # Panics\n\t///\n\t/// This function will panic if the provided function also panics. However,\n\t/// the collection will be safely unlocked in this case, allowing the\n\t/// collection to be locked again later.\n\t///\n\t/// # Example\n\t///\n\t/// ```\n\t/// use happylock::{LockCollection, ThreadKey, Mutex};\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// lock.scoped_lock(\u0026mut key, |(number, string)| {\n\t/// *number += 1;\n\t/// *string = \"1\";\n\t/// });\n\t/// ```\n\tpub fn scoped_lock\u003c'a, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: impl Keyable,\n\t\tf: impl FnOnce(L::DataMut\u003c'a\u003e) -\u003e R,\n\t) -\u003e R {\n\t\tscoped_write(self, key, f)\n\t}\n\n\t/// Attempts to acquire an exclusive lock to the underlying data without\n\t/// blocking, and then unlocks once the provided function returns.\n\t///\n\t/// This function implements a non-blocking variant of [`scoped_lock`].\n\t/// Unlike `scoped_lock`, if the lock collection is not already unlocked, then\n\t/// the provided function will not run, and the given [`Keyable`] is returned.\n\t/// This method 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/// If any of the locks in the collection are already locked, then the\n\t/// provided function will not run. `Err` is returned with the given key.\n\t///\n\t/// # Panics\n\t///\n\t/// This function will panic if the provided function also panics. However,\n\t/// the collection will be safely unlocked in this case, allowing the\n\t/// collection to be locked again later.\n\t///\n\t/// # Example\n\t///\n\t/// ```\n\t/// use happylock::{LockCollection, ThreadKey, Mutex};\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// lock.scoped_try_lock(\u0026mut key, |(number, string)| {\n\t/// *number += 1;\n\t/// *string = \"1\";\n\t/// }).expect(\"This lock has not yet been locked\");\n\t/// ```\n\t///\n\t/// [`scoped_lock`]: BoxedLockCollection::scoped_lock\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 FnOnce(L::DataMut\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tscoped_try_write(self, key, f)\n\t}\n\n\t/// Locks the collection, blocking the current thread until it can be\n\t/// acquired.\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data. When the guard is dropped, the locks in the collection are also\n\t/// dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// ```\n\t#[must_use]\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::Guard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_write();\n\n\t\t\tLockGuard {\n\t\t\t\t// safety: we've already acquired the lock\n\t\t\t\tguard: self.child().guard(),\n\t\t\t\tkey,\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Attempts to lock the without blocking.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// locks when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If any locks in the collection are already locked, then an error\n\t/// containing the given key is returned.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// match lock.try_lock(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::Guard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tlet guard = unsafe {\n\t\t\tif !self.raw_try_write() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we've acquired the locks\n\t\t\tself.child().guard()\n\t\t};\n\n\t\tOk(LockGuard { guard, key })\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// let key = LockCollection::\u003c(Mutex\u003ci32\u003e, Mutex\u003c\u0026str\u003e)\u003e::unlock(guard);\n\t/// ```\n\tpub fn unlock(guard: LockGuard\u003cL::Guard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: Sharable\u003e BoxedLockCollection\u003cL\u003e {\n\t/// Acquires a shared lock, blocking until it is safe to do so, and then\n\t/// unlocks after the provided function returns.\n\t///\n\t/// This function is useful to ensure that the data is never accidentally\n\t/// locked forever by leaking the guard. Even if the function panics, this\n\t/// function will gracefully notice the panic, and unlock. This function\n\t/// provides no guarantees with respect to the ordering of whether contentious\n\t/// readers or writers will acquire the lock first.\n\t///\n\t/// # Panics\n\t///\n\t/// This function will panic if the provided function also panics. However,\n\t/// the collection will be safely unlocked in this case, allowing the\n\t/// collection to be locked again later.\n\t///\n\t/// # Example\n\t///\n\t/// ```\n\t/// use happylock::{LockCollection, ThreadKey, RwLock};\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// lock.scoped_read(\u0026mut key, |(number, string)| {\n\t/// assert_eq!(*number, 0);\n\t/// assert_eq!(*string, \"\");\n\t/// });\n\t/// ```\n\tpub fn scoped_read\u003c'a, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: impl Keyable,\n\t\tf: impl FnOnce(L::DataRef\u003c'a\u003e) -\u003e R,\n\t) -\u003e R {\n\t\tscoped_read(self, key, f)\n\t}\n\n\t/// Attempts to acquire an exclusive lock to the underlying data without\n\t/// blocking, and then unlocks once the provided function returns.\n\t///\n\t/// This function implements a non-blocking variant of [`scoped_read`].\n\t/// Unlike `scoped_read`, if the lock collection is exclusively locked, then\n\t/// the provided function will not run, and the given [`Keyable`] is returned.\n\t/// This method 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/// If any of the locks in the collection are already exclusively locked, then\n\t/// the provided function will not run. `Err` is returned with the given key.\n\t///\n\t/// # Panics\n\t///\n\t/// This function will panic if the provided function also panics. However,\n\t/// the collection will be safely unlocked in this case, allowing the\n\t/// collection to be locked again later.\n\t///\n\t/// # Example\n\t///\n\t/// ```\n\t/// use happylock::{LockCollection, ThreadKey, RwLock};\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// lock.scoped_try_read(\u0026mut key, |(number, string)| {\n\t/// assert_eq!(*number, 0);\n\t/// assert_eq!(*string, \"\");\n\t/// }).expect(\"This lock has not yet been locked\");\n\t/// ```\n\t///\n\t/// [`scoped_read`]: BoxedLockCollection::scoped_read\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 FnOnce(L::DataRef\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tscoped_try_read(self, key, f)\n\t}\n\n\t/// Locks the collection, so that other threads can still read from it\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data immutably. When the guard is dropped, the locks in the collection\n\t/// are also dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// assert_eq!(*guard.0, 0);\n\t/// assert_eq!(*guard.1, \"\");\n\t/// ```\n\t#[must_use]\n\tpub fn read(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_read();\n\n\t\t\tLockGuard {\n\t\t\t\t// safety: we've already acquired the lock\n\t\t\t\tguard: self.child().read_guard(),\n\t\t\t\tkey,\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Attempts to lock the without blocking, in such a way that other threads\n\t/// can still read from the collection.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// shared access when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If any of the locks in the collection are already locked, then an error\n\t/// is returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(5), RwLock::new(\"6\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// match lock.try_read(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// assert_eq!(*guard.0, 5);\n\t/// assert_eq!(*guard.1, \"6\");\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_read(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tlet guard = unsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_read() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we've acquired the locks\n\t\t\tself.child().read_guard()\n\t\t};\n\n\t\tOk(LockGuard { guard, key })\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// let key = LockCollection::\u003c(RwLock\u003ci32\u003e, RwLock\u003c\u0026str\u003e)\u003e::unlock_read(guard);\n\t/// ```\n\tpub fn unlock_read(guard: LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: OwnedLockable\u003e BoxedLockCollection\u003cL\u003e {}\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#[expect(clippy::float_cmp)]\n\tfn uses_correct_default() {\n\t\tlet collection =\n\t\t\tBoxedLockCollection::\u003c(Mutex\u003cf64\u003e, Mutex\u003cOption\u003ci32\u003e\u003e, Mutex\u003cusize\u003e)\u003e::default();\n\t\tlet tuple = collection.into_inner();\n\t\tassert_eq!(tuple.0, 0.0);\n\t\tassert!(tuple.1.is_none());\n\t\tassert_eq!(tuple.2, 0)\n\t}\n\n\t#[test]\n\tfn non_duplicates_allowed() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(1);\n\t\tassert!(BoxedLockCollection::try_new([\u0026mutex1, \u0026mutex2]).is_some())\n\t}\n\n\t#[test]\n\tfn duplicates_not_allowed() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tassert!(BoxedLockCollection::try_new([\u0026mutex1, \u0026mutex1]).is_none())\n\t}\n\n\t#[test]\n\tfn scoped_read_sees_changes() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = BoxedLockCollection::new(mutexes);\n\t\tcollection.scoped_lock(\u0026mut key, |guard| *guard[0] = 128);\n\n\t\tlet sum = collection.scoped_read(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 128);\n\t\t\tassert_eq!(*guard[1], 42);\n\t\t\t*guard[0] + *guard[1]\n\t\t});\n\n\t\tassert_eq!(sum, 128 + 42);\n\t}\n\n\t#[test]\n\tfn scoped_try_lock_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::new([Mutex::new(1), Mutex::new(2)]);\n\t\tlet guard = collection.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = collection.scoped_try_lock(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn scoped_try_read_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::new([RwLock::new(1), RwLock::new(2)]);\n\t\tlet guard = collection.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = collection.scoped_try_read(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn try_lock_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::new([Mutex::new(1), Mutex::new(2)]);\n\t\tlet guard = collection.try_lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_lock(key);\n\t\t\t\tassert!(guard.is_err());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn try_read_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::new([RwLock::new(1), RwLock::new(2)]);\n\t\tlet guard = collection.try_read(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_read(key);\n\t\t\t\tassert!(guard.is_ok());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn try_lock_fails_with_one_exclusive_lock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = [Mutex::new(1), Mutex::new(2)];\n\t\tlet collection = BoxedLockCollection::new_ref(\u0026locks);\n\t\tlet guard = locks[1].try_lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_lock(key);\n\t\t\t\tassert!(guard.is_err());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn try_read_fails_during_exclusive_lock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::new([RwLock::new(1), RwLock::new(2)]);\n\t\tlet guard = collection.try_lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_read(key);\n\t\t\t\tassert!(guard.is_err());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn try_read_fails_with_one_exclusive_lock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = [RwLock::new(1), RwLock::new(2)];\n\t\tlet collection = BoxedLockCollection::new_ref(\u0026locks);\n\t\tlet guard = locks[1].try_write(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_read(key);\n\t\t\t\tassert!(guard.is_err());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn unlock_collection_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex1 = Mutex::new(\"foo\");\n\t\tlet mutex2 = Mutex::new(\"bar\");\n\t\tlet collection = BoxedLockCollection::try_new((\u0026mutex1, \u0026mutex2)).unwrap();\n\t\tlet guard = collection.lock(key);\n\t\tlet key = BoxedLockCollection::\u003c(\u0026Mutex\u003c_\u003e, \u0026Mutex\u003c_\u003e)\u003e::unlock(guard);\n\n\t\tassert!(mutex1.try_lock(key).is_ok())\n\t}\n\n\t#[test]\n\tfn read_unlock_collection_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock1 = RwLock::new(\"foo\");\n\t\tlet lock2 = RwLock::new(\"bar\");\n\t\tlet collection = BoxedLockCollection::try_new((\u0026lock1, \u0026lock2)).unwrap();\n\t\tlet guard = collection.read(key);\n\t\tlet key = BoxedLockCollection::\u003c(\u0026RwLock\u003c_\u003e, \u0026RwLock\u003c_\u003e)\u003e::unlock_read(guard);\n\n\t\tassert!(lock1.try_write(key).is_ok())\n\t}\n\n\t#[test]\n\tfn into_inner_works() {\n\t\tlet collection = BoxedLockCollection::new((Mutex::new(\"Hello\"), Mutex::new(47)));\n\t\tassert_eq!(collection.into_inner(), (\"Hello\", 47))\n\t}\n\n\t#[test]\n\tfn works_in_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex1 = RwLock::new(0);\n\t\tlet mutex2 = RwLock::new(1);\n\t\tlet collection =\n\t\t\tBoxedLockCollection::try_new(BoxedLockCollection::try_new([\u0026mutex1, \u0026mutex2]).unwrap())\n\t\t\t\t.unwrap();\n\n\t\tlet mut guard = collection.lock(key);\n\t\tassert!(mutex1.is_locked());\n\t\tassert!(mutex2.is_locked());\n\t\tassert_eq!(*guard[0], 0);\n\t\tassert_eq!(*guard[1], 1);\n\t\t*guard[0] = 2;\n\t\tlet key = BoxedLockCollection::\u003cBoxedLockCollection\u003c[\u0026RwLock\u003c_\u003e; 2]\u003e\u003e::unlock(guard);\n\n\t\tlet guard = collection.read(key);\n\t\tassert!(mutex1.is_locked());\n\t\tassert!(mutex2.is_locked());\n\t\tassert_eq!(*guard[0], 2);\n\t\tassert_eq!(*guard[1], 1);\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn as_ref_works() {\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet collection = BoxedLockCollection::new_ref(\u0026mutexes);\n\n\t\tassert!(std::ptr::addr_eq(\u0026raw const mutexes, collection.as_ref()))\n\t}\n\n\t#[test]\n\tfn child() {\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet collection = BoxedLockCollection::new_ref(\u0026mutexes);\n\n\t\tassert!(std::ptr::addr_eq(\u0026raw const mutexes, *collection.child()))\n\t}\n}\n","traces":[{"line":21,"address":[],"length":0,"stats":{"Line":22}},{"line":22,"address":[595141],"length":1,"stats":{"Line":22}},{"line":25,"address":[1658448,1658656,1660160],"length":1,"stats":{"Line":4}},{"line":26,"address":[],"length":0,"stats":{"Line":4}},{"line":29,"address":[],"length":0,"stats":{"Line":5}},{"line":30,"address":[],"length":0,"stats":{"Line":10}},{"line":31,"address":[],"length":0,"stats":{"Line":5}},{"line":35,"address":[],"length":0,"stats":{"Line":8}},{"line":36,"address":[1660101,1659141,1658981,1659205,1659669,1660293,1658917],"length":1,"stats":{"Line":8}},{"line":39,"address":[675280],"length":1,"stats":{"Line":3}},{"line":40,"address":[675285],"length":1,"stats":{"Line":3}},{"line":43,"address":[],"length":0,"stats":{"Line":3}},{"line":44,"address":[],"length":0,"stats":{"Line":6}},{"line":45,"address":[],"length":0,"stats":{"Line":3}},{"line":61,"address":[1672704],"length":1,"stats":{"Line":1}},{"line":65,"address":[],"length":0,"stats":{"Line":1}},{"line":68,"address":[],"length":0,"stats":{"Line":1}},{"line":69,"address":[1672672],"length":1,"stats":{"Line":1}},{"line":72,"address":[],"length":0,"stats":{"Line":5}},{"line":73,"address":[1672608,1672544,1672757,1672789,1672821,1672853],"length":1,"stats":{"Line":5}},{"line":88,"address":[],"length":0,"stats":{"Line":1}},{"line":89,"address":[],"length":0,"stats":{"Line":1}},{"line":92,"address":[1669760,1669888,1669920],"length":1,"stats":{"Line":3}},{"line":93,"address":[1669893,1669925,1669792],"length":1,"stats":{"Line":3}},{"line":105,"address":[1670528,1670624],"length":1,"stats":{"Line":2}},{"line":106,"address":[1670542,1670638],"length":1,"stats":{"Line":2}},{"line":117,"address":[],"length":0,"stats":{"Line":1}},{"line":118,"address":[],"length":0,"stats":{"Line":1}},{"line":129,"address":[],"length":0,"stats":{"Line":1}},{"line":130,"address":[],"length":0,"stats":{"Line":1}},{"line":137,"address":[],"length":0,"stats":{"Line":1}},{"line":138,"address":[1624657],"length":1,"stats":{"Line":1}},{"line":139,"address":[1624713],"length":1,"stats":{"Line":1}},{"line":164,"address":[1671392],"length":1,"stats":{"Line":1}},{"line":165,"address":[],"length":0,"stats":{"Line":1}},{"line":181,"address":[],"length":0,"stats":{"Line":1}},{"line":182,"address":[1673646],"length":1,"stats":{"Line":1}},{"line":187,"address":[],"length":0,"stats":{"Line":1}},{"line":188,"address":[1673725],"length":1,"stats":{"Line":1}},{"line":207,"address":[],"length":0,"stats":{"Line":3}},{"line":210,"address":[],"length":0,"stats":{"Line":3}},{"line":212,"address":[],"length":0,"stats":{"Line":3}},{"line":214,"address":[1635283,1632835,1634819],"length":1,"stats":{"Line":3}},{"line":216,"address":[],"length":0,"stats":{"Line":3}},{"line":239,"address":[593792],"length":1,"stats":{"Line":26}},{"line":241,"address":[668149],"length":1,"stats":{"Line":26}},{"line":251,"address":[597408],"length":1,"stats":{"Line":36}},{"line":252,"address":[593861],"length":1,"stats":{"Line":35}},{"line":271,"address":[],"length":0,"stats":{"Line":16}},{"line":273,"address":[],"length":0,"stats":{"Line":18}},{"line":292,"address":[],"length":0,"stats":{"Line":6}},{"line":294,"address":[],"length":0,"stats":{"Line":6}},{"line":319,"address":[674692,674717,674304],"length":1,"stats":{"Line":39}},{"line":320,"address":[682865],"length":1,"stats":{"Line":39}},{"line":321,"address":[682956],"length":1,"stats":{"Line":39}},{"line":323,"address":[1641195,1652089,1640507,1652790,1637838,1643735,1641895,1646759,1650171,1647319,1638467,1647751,1650555,1638907,1639703,1645591,1646199,1648390,1649579,1642875,1651095,1651555,1648998,1645127,1644437,1653670],"length":1,"stats":{"Line":39}},{"line":324,"address":[594054],"length":1,"stats":{"Line":39}},{"line":327,"address":[674147],"length":1,"stats":{"Line":110}},{"line":330,"address":[674594],"length":1,"stats":{"Line":41}},{"line":331,"address":[597755],"length":1,"stats":{"Line":42}},{"line":353,"address":[683456,683724],"length":1,"stats":{"Line":14}},{"line":356,"address":[683473],"length":1,"stats":{"Line":14}},{"line":357,"address":[],"length":0,"stats":{"Line":28}},{"line":358,"address":[669070],"length":1,"stats":{"Line":2}},{"line":360,"address":[594636],"length":1,"stats":{"Line":12}},{"line":393,"address":[1624128,1624192,1624096,1624160,1624224,1624256,1624288],"length":1,"stats":{"Line":7}},{"line":398,"address":[1624269,1624141,1624109,1624301,1624237,1624205,1624173],"length":1,"stats":{"Line":7}},{"line":437,"address":[1624064],"length":1,"stats":{"Line":1}},{"line":442,"address":[],"length":0,"stats":{"Line":1}},{"line":466,"address":[668802,668808,668656],"length":1,"stats":{"Line":24}},{"line":469,"address":[1650814,1647038,1639200,1645854,1640016,1644736,1643168,1649246,1638160,1642208,1648030,1648638,1653038,1646494,1644048],"length":1,"stats":{"Line":21}},{"line":473,"address":[1645894,1640056,1653078,1650854,1647078,1644776,1638200,1648678,1639240,1649286,1646534,1644088,1642248,1643208,1648070],"length":1,"stats":{"Line":19}},{"line":508,"address":[],"length":0,"stats":{"Line":5}},{"line":510,"address":[],"length":0,"stats":{"Line":7}},{"line":511,"address":[1639417,1640233,1649913],"length":1,"stats":{"Line":2}},{"line":515,"address":[598281,598251],"length":1,"stats":{"Line":4}},{"line":518,"address":[],"length":0,"stats":{"Line":2}},{"line":538,"address":[1644939,1653152,1653216,1653222,1646004,1651840,1651904,1648214,1648816,1642402,1648208,1646010,1644864,1651910,1649360,1644176,1648144,1648822,1649424,1648752,1642336,1644251,1649430,1645952],"length":1,"stats":{"Line":11}},{"line":539,"address":[],"length":0,"stats":{"Line":11}},{"line":540,"address":[],"length":0,"stats":{"Line":0}},{"line":574,"address":[1624384,1624416,1624320],"length":1,"stats":{"Line":3}},{"line":579,"address":[1624429,1624333,1624397],"length":1,"stats":{"Line":3}},{"line":618,"address":[1624352],"length":1,"stats":{"Line":1}},{"line":623,"address":[],"length":0,"stats":{"Line":1}},{"line":646,"address":[675056,675208,675202],"length":1,"stats":{"Line":8}},{"line":649,"address":[],"length":0,"stats":{"Line":7}},{"line":653,"address":[1655446,1655318,1654904,1655064,1655862,1655206],"length":1,"stats":{"Line":6}},{"line":689,"address":[],"length":0,"stats":{"Line":4}},{"line":692,"address":[674688,674738],"length":1,"stats":{"Line":6}},{"line":693,"address":[1655609,1654713],"length":1,"stats":{"Line":2}},{"line":697,"address":[],"length":0,"stats":{"Line":2}},{"line":700,"address":[674805],"length":1,"stats":{"Line":1}},{"line":718,"address":[],"length":0,"stats":{"Line":1}},{"line":719,"address":[],"length":0,"stats":{"Line":1}},{"line":720,"address":[],"length":0,"stats":{"Line":0}},{"line":738,"address":[],"length":0,"stats":{"Line":2}},{"line":739,"address":[],"length":0,"stats":{"Line":2}},{"line":765,"address":[],"length":0,"stats":{"Line":1}},{"line":766,"address":[],"length":0,"stats":{"Line":1}}],"covered":97,"coverable":99},{"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// No implementations of Eq, PartialEq, PartialOrd, or Ord\n// You can't implement both PartialEq\u003cSelf\u003e and PartialEq\u003cT\u003e\n// It's easier to just implement neither and ask users to dereference\n// This is less of a problem when using the scoped lock API\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":29,"address":[],"length":0,"stats":{"Line":1}},{"line":30,"address":[],"length":0,"stats":{"Line":1}},{"line":37,"address":[],"length":0,"stats":{"Line":18}},{"line":38,"address":[],"length":0,"stats":{"Line":0}},{"line":43,"address":[],"length":0,"stats":{"Line":8}},{"line":44,"address":[],"length":0,"stats":{"Line":0}},{"line":49,"address":[],"length":0,"stats":{"Line":2}},{"line":50,"address":[],"length":0,"stats":{"Line":0}},{"line":55,"address":[],"length":0,"stats":{"Line":4}},{"line":56,"address":[],"length":0,"stats":{"Line":0}}],"covered":6,"coverable":10},{"path":["/","home","botahamec","Projects","happylock","src","collection","owned.rs"],"content":"use crate::context::LockContext;\nuse crate::lockable::{\n\tLockable, LockableGetMut, LockableIntoInner, OwnedLockable, RawLock, Sharable,\n};\nuse crate::{Keyable, ThreadKey};\n\nuse super::utils::{scoped_read, scoped_try_read, scoped_try_write, scoped_write};\nuse super::{utils, LockGuard, OwnedLockCollection};\n\nunsafe impl\u003cL: Lockable\u003e RawLock for OwnedLockCollection\u003cL\u003e {\n\t#[mutants::skip] // this should never run\n\t#[cfg(not(tarpaulin_include))]\n\tfn poison(\u0026self) {\n\t\tlet locks = utils::get_locks_unsorted(\u0026self.child);\n\t\tfor lock in locks {\n\t\t\tlock.poison();\n\t\t}\n\t}\n\n\tunsafe fn raw_write(\u0026self) {\n\t\tutils::ordered_write(\u0026utils::get_locks_unsorted(\u0026self.child))\n\t}\n\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool {\n\t\tlet locks = utils::get_locks_unsorted(\u0026self.child);\n\t\tutils::ordered_try_write(\u0026locks)\n\t}\n\n\tunsafe fn raw_unlock_write(\u0026self) {\n\t\tlet locks = utils::get_locks_unsorted(\u0026self.child);\n\t\tfor lock in locks {\n\t\t\tlock.raw_unlock_write();\n\t\t}\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tutils::ordered_read(\u0026utils::get_locks_unsorted(\u0026self.child))\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tlet locks = utils::get_locks_unsorted(\u0026self.child);\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.child);\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 locks 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\t// It's ok to use self here, because the values in the collection already\n\t\t// cannot be referenced anywhere else. It's necessary to use self as the lock\n\t\t// because otherwise we will be handing out shared references to the child\n\t\tptrs.push(self)\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tself.child.guard()\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.child.data_mut()\n\t}\n}\n\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.child.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.child.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.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 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.child.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.child.extend(iter)\n\t}\n}\n\n// AsRef can't be implemented because an impl of AsRef\u003cL\u003e for L could break the\n// invariant that there is only one way to lock the collection. AsMut is fine,\n// because the collection can't be locked as long as the reference is valid.\n\nimpl\u003cT: ?Sized, L: AsMut\u003cT\u003e\u003e AsMut\u003cT\u003e for OwnedLockCollection\u003cL\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself.child.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 { child: data }\n\t}\n\n\t/// Acquires an exclusive lock, blocking until it is safe to do so, and then\n\t/// unlocks after the provided function returns.\n\t///\n\t/// This function is useful to ensure that the data is never accidentally\n\t/// locked forever by leaking the guard. Even if the function panics, this\n\t/// function will gracefully notice the panic, and unlock. This function\n\t/// provides no guarantees with respect to the ordering of whether contentious\n\t/// readers or writers will acquire the lock first.\n\t///\n\t/// # Panics\n\t///\n\t/// This function will panic if the provided function also panics. However,\n\t/// the collection will be safely unlocked in this case, allowing the\n\t/// collection to be locked again later.\n\t///\n\t/// # Example\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, Mutex};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// lock.scoped_lock(\u0026mut key, |(number, string)| {\n\t/// *number += 1;\n\t/// *string = \"1\";\n\t/// });\n\t/// ```\n\tpub fn scoped_lock\u003c'a, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: impl Keyable,\n\t\tf: impl FnOnce(L::DataMut\u003c'a\u003e) -\u003e R,\n\t) -\u003e R {\n\t\tscoped_write(self, key, f)\n\t}\n\n\t/// Attempts to acquire an exclusive lock to the underlying data without\n\t/// blocking, and then unlocks once the provided function returns.\n\t///\n\t/// This function implements a non-blocking variant of [`scoped_lock`].\n\t/// Unlike `scoped_lock`, if the lock collection is not already unlocked, then\n\t/// the provided function will not run, and the given [`Keyable`] is returned.\n\t/// This method 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/// If any of the locks in the collection are already locked, then the\n\t/// provided function will not run. `Err` is returned with the given key.\n\t///\n\t/// # Panics\n\t///\n\t/// This function will panic if the provided function also panics. However,\n\t/// the collection will be safely unlocked in this case, allowing the\n\t/// collection to be locked again later.\n\t///\n\t/// # Example\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, Mutex};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// lock.scoped_try_lock(\u0026mut key, |(number, string)| {\n\t/// *number += 1;\n\t/// *string = \"1\";\n\t/// }).expect(\"This lock has not yet been locked\");\n\t/// ```\n\t///\n\t/// [`scoped_lock`]: OwnedLockCollection::scoped_lock\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 FnOnce(L::DataMut\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tscoped_try_write(self, key, f)\n\t}\n\n\t/// Locks the collection\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data. When the guard is dropped, the locks in the collection are also\n\t/// dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// ```\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::Guard\u003c'_\u003e\u003e {\n\t\tlet guard = unsafe {\n\t\t\t// safety: we have the thread key, and these locks happen in a\n\t\t\t// predetermined order\n\t\t\tself.raw_write();\n\n\t\t\t// safety: we've locked all of this already\n\t\t\tself.child.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\t// safety: we've acquired the key\n\t\t\tif !self.raw_try_write() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we'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/// Creates a context that can be used to iterate over the items in order.\n\t///\n\t/// Sometimes, partial allocation of locks is useful. For example, you may\n\t/// want to acquire a lock on the first element of a list before deciding if\n\t/// the second element should be locked. This function creates a\n\t/// [`LockContext`] which is capable of doing exactly that.\n\t///\n\t/// # Example\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(true), Mutex::new(42), Mutex::new(67));\n\t/// let locks = OwnedLockCollection::new(data);\n\t/// let mut ctx = locks.context();\n\t/// let tuple = ctx.tuple(key);\n\t///\n\t/// let (use_other, tuple) = tuple.lock_0();\n\t/// let number = if **use_other {\n\t/// tuple.lock_2().0\n\t/// } else {\n\t/// tuple.lock_1().0\n\t/// };\n\t/// assert_eq!(**number, 67);\n\t/// ```\n\t#[must_use]\n\tpub const fn context(\u0026self) -\u003e LockContext\u003c'_, L\u003e {\n\t\tLockContext::new(\u0026self.child)\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\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\t/// Acquires a shared lock, blocking until it is safe to do so, and then\n\t/// unlocks after the provided function returns.\n\t///\n\t/// This function is useful to ensure that the data is never accidentally\n\t/// locked forever by leaking the guard. Even if the function panics, this\n\t/// function will gracefully notice the panic, and unlock. This function\n\t/// provides no guarantees with respect to the ordering of whether contentious\n\t/// readers or writers will acquire the lock first.\n\t///\n\t/// # Panics\n\t///\n\t/// This function will panic if the provided function also panics. However,\n\t/// the collection will be safely unlocked in this case, allowing the\n\t/// collection to be locked again later.\n\t///\n\t/// # Example\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, RwLock};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// lock.scoped_read(\u0026mut key, |(number, string)| {\n\t/// assert_eq!(*number, 0);\n\t/// assert_eq!(*string, \"\");\n\t/// });\n\t/// ```\n\tpub fn scoped_read\u003c'a, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: impl Keyable,\n\t\tf: impl FnOnce(L::DataRef\u003c'a\u003e) -\u003e R,\n\t) -\u003e R {\n\t\tscoped_read(self, key, f)\n\t}\n\n\t/// Attempts to acquire an exclusive lock to the underlying data without\n\t/// blocking, and then unlocks once the provided function returns.\n\t///\n\t/// This function implements a non-blocking variant of [`scoped_read`].\n\t/// Unlike `scoped_read`, if the lock collection is exclusively locked, then\n\t/// the provided function will not run, and the given [`Keyable`] is returned.\n\t/// This method 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/// If any of the locks in the collection are already exclusively locked, then\n\t/// the provided function will not run. `Err` is returned with the given key.\n\t///\n\t/// # Panics\n\t///\n\t/// This function will panic if the provided function also panics. However,\n\t/// the collection will be safely unlocked in this case, allowing the\n\t/// collection to be locked again later.\n\t///\n\t/// # Example\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, RwLock};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// lock.scoped_try_read(\u0026mut key, |(number, string)| {\n\t/// assert_eq!(*number, 0);\n\t/// assert_eq!(*string, \"\");\n\t/// }).expect(\"This lock has not yet been locked\");\n\t/// ```\n\t///\n\t/// [`scoped_read`]: OwnedLockCollection::scoped_read\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 FnOnce(L::DataRef\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tscoped_try_read(self, key, f)\n\t}\n\n\t/// Locks the collection, so that other threads can still read from it\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data immutably. When the guard is dropped, the locks in the collection\n\t/// are also dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// assert_eq!(*guard.0, 0);\n\t/// assert_eq!(*guard.1, \"\");\n\t/// ```\n\tpub fn read(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_read();\n\n\t\t\tLockGuard {\n\t\t\t\t// safety: we've already acquired the lock\n\t\t\t\tguard: self.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 this collection can't be acquired, then an error\n\t/// is returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(5), RwLock::new(\"6\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// match lock.try_read(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// assert_eq!(*guard.0, 5);\n\t/// assert_eq!(*guard.1, \"6\");\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_read(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tlet guard = unsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_read() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we've acquired the locks\n\t\t\tself.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};\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\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.child\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.child\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::{LockCollection, Mutex, RwLock, ThreadKey};\n\n\t#[test]\n\tfn get_mut_applies_changes() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mut collection = OwnedLockCollection::new([Mutex::new(\"foo\"), Mutex::new(\"bar\")]);\n\t\tassert_eq!(*collection.get_mut()[0], \"foo\");\n\t\tassert_eq!(*collection.get_mut()[1], \"bar\");\n\t\t*collection.get_mut()[0] = \"baz\";\n\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], \"baz\");\n\t\tassert_eq!(*guard[1], \"bar\");\n\t}\n\n\t#[test]\n\tfn into_inner_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::from([Mutex::new(\"foo\")]);\n\t\tlet mut guard = collection.lock(key);\n\t\t*guard[0] = \"bar\";\n\t\tdrop(guard);\n\n\t\tlet array = collection.into_inner();\n\t\tassert_eq!(array.len(), 1);\n\t\tassert_eq!(array[0], \"bar\");\n\t}\n\n\t#[test]\n\tfn from_into_iter_is_correct() {\n\t\tlet array = [Mutex::new(0), Mutex::new(1), Mutex::new(2), Mutex::new(3)];\n\t\tlet mut collection: OwnedLockCollection\u003cVec\u003cMutex\u003cusize\u003e\u003e\u003e = array.into_iter().collect();\n\t\tassert_eq!(collection.get_mut().len(), 4);\n\t\tfor (i, lock) in collection.into_iter().enumerate() {\n\t\t\tassert_eq!(lock.into_inner(), i);\n\t\t}\n\t}\n\n\t#[test]\n\tfn from_iter_is_correct() {\n\t\tlet array = [Mutex::new(0), Mutex::new(1), Mutex::new(2), Mutex::new(3)];\n\t\tlet mut collection: OwnedLockCollection\u003cVec\u003cMutex\u003cusize\u003e\u003e\u003e = array.into_iter().collect();\n\t\tlet collection: \u0026mut Vec\u003c_\u003e = collection.as_mut();\n\t\tassert_eq!(collection.len(), 4);\n\t\tfor (i, lock) in collection.iter_mut().enumerate() {\n\t\t\tassert_eq!(*lock.get_mut(), i);\n\t\t}\n\t}\n\n\t#[test]\n\tfn scoped_read_works() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new([RwLock::new(24), RwLock::new(42)]);\n\t\tlet sum = collection.scoped_read(\u0026mut key, |guard| guard[0] + guard[1]);\n\t\tassert_eq!(sum, 24 + 42);\n\t}\n\n\t#[test]\n\tfn scoped_lock_works() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new([RwLock::new(24), RwLock::new(42)]);\n\t\tcollection.scoped_lock(\u0026mut key, |guard| *guard[0] += *guard[1]);\n\n\t\tlet sum = collection.scoped_lock(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 24 + 42);\n\t\t\tassert_eq!(*guard[1], 42);\n\t\t\t*guard[0] + *guard[1]\n\t\t});\n\n\t\tassert_eq!(sum, 24 + 42 + 42);\n\t}\n\n\t#[test]\n\tfn scoped_try_lock_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new([Mutex::new(1), Mutex::new(2)]);\n\t\tlet guard = collection.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = collection.scoped_try_lock(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn scoped_try_read_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new([RwLock::new(1), RwLock::new(2)]);\n\t\tlet guard = collection.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = collection.scoped_try_read(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn try_lock_works_on_unlocked() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new((Mutex::new(0), Mutex::new(1)));\n\t\tlet guard = collection.try_lock(key).unwrap();\n\t\tassert_eq!(*guard.0, 0);\n\t\tassert_eq!(*guard.1, 1);\n\t}\n\n\t#[test]\n\tfn try_lock_fails_on_locked() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new((Mutex::new(0), Mutex::new(1)));\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.lock(key);\n\t\t\t\tstd::mem::forget(guard);\n\t\t\t});\n\t\t});\n\n\t\tassert!(collection.try_lock(key).is_err());\n\t}\n\n\t#[test]\n\tfn try_read_succeeds_for_unlocked_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = OwnedLockCollection::new(mutexes);\n\t\tlet guard = collection.try_read(key).unwrap();\n\t\tassert_eq!(*guard[0], 24);\n\t\tassert_eq!(*guard[1], 42);\n\t}\n\n\t#[test]\n\tfn try_read_fails_on_locked() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new((RwLock::new(0), RwLock::new(1)));\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.lock(key);\n\t\t\t\tstd::mem::forget(guard);\n\t\t\t});\n\t\t});\n\n\t\tassert!(collection.try_read(key).is_err());\n\t}\n\n\t#[test]\n\tfn can_read_twice_on_different_threads() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = OwnedLockCollection::new(mutexes);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.read(key);\n\t\t\t\tassert_eq!(*guard[0], 24);\n\t\t\t\tassert_eq!(*guard[1], 42);\n\t\t\t\tstd::mem::forget(guard);\n\t\t\t});\n\t\t});\n\n\t\tlet guard = collection.try_read(key).unwrap();\n\t\tassert_eq!(*guard[0], 24);\n\t\tassert_eq!(*guard[1], 42);\n\t}\n\n\t#[test]\n\tfn unlock_collection_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new((Mutex::new(\"foo\"), Mutex::new(\"bar\")));\n\t\tlet guard = collection.lock(key);\n\n\t\tlet key = OwnedLockCollection::\u003c(Mutex\u003c_\u003e, Mutex\u003c_\u003e)\u003e::unlock(guard);\n\t\tassert!(collection.try_lock(key).is_ok())\n\t}\n\n\t#[test]\n\tfn read_unlock_collection_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new((RwLock::new(\"foo\"), RwLock::new(\"bar\")));\n\t\tlet guard = collection.read(key);\n\n\t\tlet key = OwnedLockCollection::\u003c(\u0026RwLock\u003c_\u003e, \u0026RwLock\u003c_\u003e)\u003e::unlock_read(guard);\n\t\tassert!(collection.try_lock(key).is_ok())\n\t}\n\n\t#[test]\n\tfn default_works() {\n\t\ttype MyCollection = OwnedLockCollection\u003c(Mutex\u003ci32\u003e, Mutex\u003cOption\u003ci32\u003e\u003e, Mutex\u003cString\u003e)\u003e;\n\t\tlet collection = MyCollection::default();\n\t\tlet inner = collection.into_inner();\n\t\tassert_eq!(inner.0, 0);\n\t\tassert_eq!(inner.1, None);\n\t\tassert_eq!(inner.2, String::new());\n\t}\n\n\t#[test]\n\tfn can_be_extended() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(1);\n\t\tlet mut collection = OwnedLockCollection::new(vec![mutex1, mutex2]);\n\n\t\tcollection.extend([Mutex::new(2)]);\n\n\t\tassert_eq!(collection.child.len(), 3);\n\t}\n\n\t#[test]\n\tfn works_in_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tOwnedLockCollection::new(OwnedLockCollection::new([RwLock::new(0), RwLock::new(1)]));\n\n\t\tlet mut guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], 0);\n\t\tassert_eq!(*guard[1], 1);\n\t\t*guard[1] = 2;\n\n\t\tlet key = OwnedLockCollection::\u003cOwnedLockCollection\u003c[RwLock\u003c_\u003e; 2]\u003e\u003e::unlock(guard);\n\t\tlet guard = collection.read(key);\n\t\tassert_eq!(*guard[0], 0);\n\t\tassert_eq!(*guard[1], 2);\n\t}\n\n\t#[test]\n\tfn as_mut_works() {\n\t\tlet mut mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet mut collection = OwnedLockCollection::new(\u0026mut mutexes);\n\n\t\tcollection.as_mut()[0] = Mutex::new(42);\n\n\t\tassert_eq!(*collection.as_mut()[0].get_mut(), 42);\n\t}\n\n\t#[test]\n\tfn child_mut_works() {\n\t\tlet mut mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet mut collection = OwnedLockCollection::new(\u0026mut mutexes);\n\n\t\tcollection.child_mut()[0] = Mutex::new(42);\n\n\t\tassert_eq!(*collection.child_mut()[0].get_mut(), 42);\n\t}\n\n\t#[test]\n\tfn into_child_works() {\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet mut collection = OwnedLockCollection::new(mutexes);\n\n\t\tcollection.child_mut()[0] = Mutex::new(42);\n\n\t\tassert_eq!(\n\t\t\t*collection\n\t\t\t\t.into_child()\n\t\t\t\t.as_mut()\n\t\t\t\t.get_mut(0)\n\t\t\t\t.unwrap()\n\t\t\t\t.get_mut(),\n\t\t\t42\n\t\t);\n\t}\n\n\t#[test]\n\tfn duplicates_detected() {\n\t\tlet collection1 = OwnedLockCollection::new([Mutex::new(5), Mutex::new(10)]);\n\t\tlet collection2 = LockCollection::try_new((\u0026collection1, \u0026collection1));\n\n\t\tassert!(collection2.is_none());\n\t}\n}\n","traces":[{"line":20,"address":[1660609,1660368,1664177,1660475,1660496,1663521,1660603,1662065,1664171,1664336,1664449,1660481,1663408,1664987,1663664,1664993,1663777,1663515,1662059,1664064,1663771,1664443,1664880,1661952],"length":1,"stats":{"Line":8}},{"line":21,"address":[],"length":0,"stats":{"Line":16}},{"line":24,"address":[],"length":0,"stats":{"Line":4}},{"line":25,"address":[],"length":0,"stats":{"Line":4}},{"line":26,"address":[],"length":0,"stats":{"Line":8}},{"line":29,"address":[],"length":0,"stats":{"Line":1}},{"line":30,"address":[],"length":0,"stats":{"Line":1}},{"line":31,"address":[1661405,1662861,1661241,1662697],"length":1,"stats":{"Line":2}},{"line":32,"address":[],"length":0,"stats":{"Line":2}},{"line":36,"address":[],"length":0,"stats":{"Line":4}},{"line":37,"address":[],"length":0,"stats":{"Line":8}},{"line":40,"address":[],"length":0,"stats":{"Line":2}},{"line":41,"address":[],"length":0,"stats":{"Line":2}},{"line":42,"address":[1660711,1662112,1660656,1664768,1664823,1662167],"length":1,"stats":{"Line":4}},{"line":45,"address":[1662658,1662368,1660912,1662652,1661196,1661202],"length":1,"stats":{"Line":1}},{"line":46,"address":[],"length":0,"stats":{"Line":1}},{"line":47,"address":[],"length":0,"stats":{"Line":2}},{"line":48,"address":[],"length":0,"stats":{"Line":2}},{"line":73,"address":[1673232],"length":1,"stats":{"Line":1}},{"line":74,"address":[1673249],"length":1,"stats":{"Line":1}},{"line":77,"address":[],"length":0,"stats":{"Line":1}},{"line":78,"address":[],"length":0,"stats":{"Line":1}},{"line":88,"address":[],"length":0,"stats":{"Line":2}},{"line":89,"address":[],"length":0,"stats":{"Line":2}},{"line":96,"address":[1670384,1670448],"length":1,"stats":{"Line":2}},{"line":97,"address":[1670396,1670461],"length":1,"stats":{"Line":2}},{"line":112,"address":[],"length":0,"stats":{"Line":1}},{"line":113,"address":[],"length":0,"stats":{"Line":1}},{"line":116,"address":[],"length":0,"stats":{"Line":1}},{"line":117,"address":[],"length":0,"stats":{"Line":1}},{"line":130,"address":[1671120],"length":1,"stats":{"Line":1}},{"line":131,"address":[1671132],"length":1,"stats":{"Line":1}},{"line":138,"address":[],"length":0,"stats":{"Line":1}},{"line":139,"address":[],"length":0,"stats":{"Line":1}},{"line":140,"address":[1624857],"length":1,"stats":{"Line":1}},{"line":145,"address":[],"length":0,"stats":{"Line":1}},{"line":146,"address":[],"length":0,"stats":{"Line":1}},{"line":155,"address":[],"length":0,"stats":{"Line":2}},{"line":156,"address":[],"length":0,"stats":{"Line":2}},{"line":161,"address":[],"length":0,"stats":{"Line":1}},{"line":162,"address":[],"length":0,"stats":{"Line":1}},{"line":167,"address":[],"length":0,"stats":{"Line":1}},{"line":168,"address":[],"length":0,"stats":{"Line":1}},{"line":189,"address":[1631168,1629472,1631200,1629136,1630432,1629088,1631616,1630304,1631440,1630816,1629920,1630864,1630336,1630256,1629648,1630384,1629296,1629696,1630368,1631392,1629968],"length":1,"stats":{"Line":22}},{"line":223,"address":[1623936,1623968],"length":1,"stats":{"Line":2}},{"line":228,"address":[],"length":0,"stats":{"Line":2}},{"line":268,"address":[1623904],"length":1,"stats":{"Line":1}},{"line":273,"address":[],"length":0,"stats":{"Line":1}},{"line":296,"address":[],"length":0,"stats":{"Line":8}},{"line":300,"address":[1630048,1629536,1629182,1629776,1629360,1630910,1630478,1631502],"length":1,"stats":{"Line":8}},{"line":303,"address":[1629581,1631542,1629222,1629821,1630093,1630518,1630950,1629405],"length":1,"stats":{"Line":8}},{"line":339,"address":[1631008,1630656,1631157,1631151,1630799,1630805,1631381,1631232,1631375],"length":1,"stats":{"Line":3}},{"line":342,"address":[],"length":0,"stats":{"Line":7}},{"line":343,"address":[],"length":0,"stats":{"Line":1}},{"line":347,"address":[],"length":0,"stats":{"Line":6}},{"line":350,"address":[1631363,1630787,1631139],"length":1,"stats":{"Line":3}},{"line":381,"address":[1631600,1630416,1630848,1629952,1631648,1631424,1629072,1629120,1629680,1629904],"length":1,"stats":{"Line":11}},{"line":382,"address":[1629077,1629125,1631605,1629957,1630421,1631653,1629909,1631429,1629685,1630853],"length":1,"stats":{"Line":12}},{"line":403,"address":[],"length":0,"stats":{"Line":2}},{"line":404,"address":[1630169,1630590],"length":1,"stats":{"Line":2}},{"line":405,"address":[],"length":0,"stats":{"Line":0}},{"line":440,"address":[1624000],"length":1,"stats":{"Line":1}},{"line":445,"address":[1624013],"length":1,"stats":{"Line":1}},{"line":485,"address":[1624032],"length":1,"stats":{"Line":1}},{"line":490,"address":[],"length":0,"stats":{"Line":1}},{"line":513,"address":[],"length":0,"stats":{"Line":4}},{"line":516,"address":[],"length":0,"stats":{"Line":4}},{"line":520,"address":[],"length":0,"stats":{"Line":4}},{"line":557,"address":[],"length":0,"stats":{"Line":2}},{"line":560,"address":[],"length":0,"stats":{"Line":4}},{"line":561,"address":[],"length":0,"stats":{"Line":1}},{"line":565,"address":[1632473,1631913,1632431],"length":1,"stats":{"Line":1}},{"line":568,"address":[],"length":0,"stats":{"Line":1}},{"line":587,"address":[1632582,1632576,1632512],"length":1,"stats":{"Line":1}},{"line":588,"address":[],"length":0,"stats":{"Line":1}},{"line":589,"address":[],"length":0,"stats":{"Line":0}},{"line":611,"address":[],"length":0,"stats":{"Line":1}},{"line":612,"address":[],"length":0,"stats":{"Line":1}},{"line":632,"address":[],"length":0,"stats":{"Line":2}},{"line":633,"address":[],"length":0,"stats":{"Line":0}},{"line":653,"address":[],"length":0,"stats":{"Line":2}},{"line":654,"address":[],"length":0,"stats":{"Line":2}},{"line":671,"address":[],"length":0,"stats":{"Line":2}},{"line":672,"address":[],"length":0,"stats":{"Line":2}}],"covered":81,"coverable":84},{"path":["/","home","botahamec","Projects","happylock","src","collection","ref.rs"],"content":"use std::fmt::Debug;\n\nuse crate::lockable::{Lockable, OwnedLockable, RawLock, Sharable};\nuse crate::{Keyable, ThreadKey};\n\nuse super::utils::{\n\tget_locks, ordered_contains_duplicates, scoped_read, scoped_try_read, scoped_try_write,\n\tscoped_write,\n};\nuse super::{utils, LockGuard, RefLockCollection};\n\nimpl\u003c'a, L\u003e IntoIterator for \u0026'a RefLockCollection\u003c'a, L\u003e\nwhere\n\t\u0026'a L: IntoIterator,\n{\n\ttype Item = \u003c\u0026'a L as IntoIterator\u003e::Item;\n\ttype IntoIter = \u003c\u0026'a L as IntoIterator\u003e::IntoIter;\n\n\tfn into_iter(self) -\u003e Self::IntoIter {\n\t\tself.child.into_iter()\n\t}\n}\n\nunsafe impl\u003cL: Lockable\u003e RawLock for RefLockCollection\u003c'_, L\u003e {\n\t#[mutants::skip] // this should never run\n\t#[cfg(not(tarpaulin_include))]\n\tfn poison(\u0026self) {\n\t\tfor lock in \u0026self.locks {\n\t\t\tlock.poison();\n\t\t}\n\t}\n\n\tunsafe fn raw_write(\u0026self) {\n\t\tutils::ordered_write(\u0026self.locks)\n\t}\n\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool {\n\t\tutils::ordered_try_write(\u0026self.locks)\n\t}\n\n\tunsafe fn raw_unlock_write(\u0026self) {\n\t\tfor lock in \u0026self.locks {\n\t\t\tlock.raw_unlock_write();\n\t\t}\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tutils::ordered_read(\u0026self.locks)\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tutils::ordered_try_read(\u0026self.locks)\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tfor lock in \u0026self.locks {\n\t\t\tlock.raw_unlock_read();\n\t\t}\n\t}\n}\n\nunsafe impl\u003cL: Lockable\u003e Lockable for RefLockCollection\u003c'_, L\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= L::Guard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= L::DataMut\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\t// Just like with BoxedLockCollection, we need to return all the individual\n\t\t// locks to avoid duplicates\n\t\tptrs.extend_from_slice(\u0026self.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 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.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\nimpl\u003cT: ?Sized, L: AsRef\u003cT\u003e\u003e AsRef\u003cT\u003e for RefLockCollection\u003c'_, L\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself.child.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.child)\n\t\t\t// there's not much reason to show the sorting order\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#[expect(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\tchild: data,\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.child\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\tchild: data,\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 { child: data, locks })\n\t}\n\n\t/// Acquires an exclusive lock, blocking until it is safe to do so, and then\n\t/// unlocks after the provided function returns.\n\t///\n\t/// This function is useful to ensure that the data is never accidentally\n\t/// locked forever by leaking the guard. Even if the function panics, this\n\t/// function will gracefully notice the panic, and unlock. This function\n\t/// provides no guarantees with respect to the ordering of whether contentious\n\t/// readers or writers will acquire the lock first.\n\t///\n\t/// # Panics\n\t///\n\t/// This function will panic if the provided function also panics. However,\n\t/// the collection will be safely unlocked in this case, allowing the\n\t/// collection to be locked again later.\n\t///\n\t/// # Example\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, Mutex};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// lock.scoped_lock(\u0026mut key, |(number, string)| {\n\t/// *number += 1;\n\t/// *string = \"1\";\n\t/// });\n\t/// ```\n\tpub fn scoped_lock\u003c's, R\u003e(\n\t\t\u0026's self,\n\t\tkey: impl Keyable,\n\t\tf: impl FnOnce(L::DataMut\u003c's\u003e) -\u003e R,\n\t) -\u003e R {\n\t\tscoped_write(self, key, f)\n\t}\n\n\t/// Attempts to acquire an exclusive lock to the underlying data without\n\t/// blocking, and then unlocks once the provided function returns.\n\t///\n\t/// This function implements a non-blocking variant of [`scoped_lock`].\n\t/// Unlike `scoped_lock`, if the lock collection is not already unlocked, then\n\t/// the provided function will not run, and the given [`Keyable`] is returned.\n\t/// This method 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/// If any of the locks in the collection are already locked, then the\n\t/// provided function will not run. `Err` is returned with the given key.\n\t///\n\t/// # Panics\n\t///\n\t/// This function will panic if the provided function also panics. However,\n\t/// the collection will be safely unlocked in this case, allowing the\n\t/// collection to be locked again later.\n\t///\n\t/// # Example\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, Mutex};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// lock.scoped_try_lock(\u0026mut key, |(number, string)| {\n\t/// *number += 1;\n\t/// *string = \"1\";\n\t/// }).expect(\"This lock has not yet been locked\");\n\t/// ```\n\t///\n\t/// [`scoped_lock`]: RefLockCollection::scoped_lock\n\tpub fn scoped_try_lock\u003c's, Key: Keyable, R\u003e(\n\t\t\u0026's self,\n\t\tkey: Key,\n\t\tf: impl FnOnce(L::DataMut\u003c's\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tscoped_try_write(self, key, f)\n\t}\n\n\t/// Locks the collection\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data. When the guard is dropped, the locks in the collection are also\n\t/// dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// ```\n\t#[must_use]\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::Guard\u003c'_\u003e\u003e {\n\t\tlet guard = unsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_write();\n\n\t\t\t// safety: we've locked all of this already\n\t\t\tself.child.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\t// safety: we have the thread key\n\t\t\tif !self.raw_try_write() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we'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};\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\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\t/// Acquires a shared lock, blocking until it is safe to do so, and then\n\t/// unlocks after the provided function returns.\n\t///\n\t/// This function is useful to ensure that the data is never accidentally\n\t/// locked forever by leaking the guard. Even if the function panics, this\n\t/// function will gracefully notice the panic, and unlock. This function\n\t/// provides no guarantees with respect to the ordering of whether contentious\n\t/// readers or writers will acquire the lock first.\n\t///\n\t/// # Panics\n\t///\n\t/// This function will panic if the provided function also panics. However,\n\t/// the collection will be safely unlocked in this case, allowing the\n\t/// collection to be locked again later.\n\t///\n\t/// # Example\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, RwLock};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// lock.scoped_read(\u0026mut key, |(number, string)| {\n\t/// assert_eq!(*number, 0);\n\t/// assert_eq!(*string, \"\");\n\t/// });\n\t/// ```\n\tpub fn scoped_read\u003c'a, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: impl Keyable,\n\t\tf: impl FnOnce(L::DataRef\u003c'a\u003e) -\u003e R,\n\t) -\u003e R {\n\t\tscoped_read(self, key, f)\n\t}\n\n\t/// Attempts to acquire an exclusive lock to the underlying data without\n\t/// blocking, and then unlocks once the provided function returns.\n\t///\n\t/// This function implements a non-blocking variant of [`scoped_read`].\n\t/// Unlike `scoped_read`, if the lock collection is exclusively locked, then\n\t/// the provided function will not run, and the given [`Keyable`] is returned.\n\t/// This method 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/// If any of the locks in the collection are already exclusively locked, then\n\t/// the provided function will not run. `Err` is returned with the given key.\n\t///\n\t/// # Panics\n\t///\n\t/// This function will panic if the provided function also panics. However,\n\t/// the collection will be safely unlocked in this case, allowing the\n\t/// collection to be locked again later.\n\t///\n\t/// # Example\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, RwLock};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// lock.scoped_try_read(\u0026mut key, |(number, string)| {\n\t/// assert_eq!(*number, 0);\n\t/// assert_eq!(*string, \"\");\n\t/// }).expect(\"This lock has not yet been locked\");\n\t/// ```\n\t///\n\t/// [`scoped_read`]: RefLockCollection::scoped_read\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 FnOnce(L::DataRef\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tscoped_try_read(self, key, f)\n\t}\n\n\t/// Locks the collection, so that other threads can still read from it\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data immutably. When the guard is dropped, the locks in the collection\n\t/// are also dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// assert_eq!(*guard.0, 0);\n\t/// assert_eq!(*guard.1, \"\");\n\t/// ```\n\t#[must_use]\n\tpub fn read(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_read();\n\n\t\t\tLockGuard {\n\t\t\t\t// safety: we've already acquired the lock\n\t\t\t\tguard: self.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};\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.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};\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\tpub fn unlock_read(guard: LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003c'a, L: 'a\u003e RefLockCollection\u003c'a, L\u003e\nwhere\n\t\u0026'a L: IntoIterator,\n{\n\t/// Returns an iterator over references to each value in the collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = [Mutex::new(26), Mutex::new(1)];\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// let mut iter = lock.iter();\n\t/// let mutex = iter.next().unwrap();\n\t/// let guard = mutex.lock(key);\n\t///\n\t/// assert_eq!(*guard, 26);\n\t/// ```\n\t#[must_use]\n\tpub fn iter(\u0026'a self) -\u003e \u003c\u0026'a L as IntoIterator\u003e::IntoIter {\n\t\tself.into_iter()\n\t}\n}\n\n#[cfg(test)]\nmod tests {\n\tuse super::*;\n\tuse crate::{Mutex, RwLock, ThreadKey};\n\n\t#[test]\n\tfn non_duplicates_allowed() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(1);\n\t\tassert!(RefLockCollection::try_new(\u0026[\u0026mutex1, \u0026mutex2]).is_some())\n\t}\n\n\t#[test]\n\tfn duplicates_not_allowed() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tassert!(RefLockCollection::try_new(\u0026[\u0026mutex1, \u0026mutex1]).is_none())\n\t}\n\n\t#[test]\n\tfn from() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [Mutex::new(\"foo\"), Mutex::new(\"bar\"), Mutex::new(\"baz\")];\n\t\tlet collection = RefLockCollection::from(\u0026mutexes);\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], \"foo\");\n\t\tassert_eq!(*guard[1], \"bar\");\n\t\tassert_eq!(*guard[2], \"baz\");\n\t}\n\n\t#[test]\n\tfn scoped_lock_changes_collection() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [Mutex::new(24), Mutex::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\t\tlet sum = collection.scoped_lock(\u0026mut key, |guard| {\n\t\t\t*guard[0] = 128;\n\t\t\t*guard[0] + *guard[1]\n\t\t});\n\n\t\tassert_eq!(sum, 128 + 42);\n\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], 128);\n\t\tassert_eq!(*guard[1], 42);\n\t}\n\n\t#[test]\n\tfn scoped_read_sees_changes() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\t\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\t\t*guard[0] = 128;\n\t\t});\n\n\t\tlet sum = collection.scoped_read(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 128);\n\t\t\tassert_eq!(*guard[1], 42);\n\t\t\t*guard[0] + *guard[1]\n\t\t});\n\n\t\tassert_eq!(sum, 128 + 42);\n\t}\n\n\t#[test]\n\tfn scoped_try_lock_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = [Mutex::new(1), Mutex::new(2)];\n\t\tlet collection = RefLockCollection::new(\u0026locks);\n\t\tlet guard = collection.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = collection.scoped_try_lock(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn scoped_try_read_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = [RwLock::new(1), RwLock::new(2)];\n\t\tlet collection = RefLockCollection::new(\u0026locks);\n\t\tlet guard = collection.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = collection.scoped_try_read(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn try_lock_succeeds_for_unlocked_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [Mutex::new(24), Mutex::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\t\tlet guard = collection.try_lock(key).unwrap();\n\t\tassert_eq!(*guard[0], 24);\n\t\tassert_eq!(*guard[1], 42);\n\t}\n\n\t#[test]\n\tfn try_lock_fails_for_locked_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [Mutex::new(24), Mutex::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = mutexes[1].lock(key);\n\t\t\t\tassert_eq!(*guard, 42);\n\t\t\t\tstd::mem::forget(guard);\n\t\t\t});\n\t\t});\n\n\t\tlet guard = collection.try_lock(key);\n\t\tassert!(guard.is_err());\n\t}\n\n\t#[test]\n\tfn try_read_succeeds_for_unlocked_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\t\tlet guard = collection.try_read(key).unwrap();\n\t\tassert_eq!(*guard[0], 24);\n\t\tassert_eq!(*guard[1], 42);\n\t}\n\n\t#[test]\n\tfn try_read_fails_for_locked_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = mutexes[1].write(key);\n\t\t\t\tassert_eq!(*guard, 42);\n\t\t\t\tstd::mem::forget(guard);\n\t\t\t});\n\t\t});\n\n\t\tlet guard = collection.try_read(key);\n\t\tassert!(guard.is_err());\n\t}\n\n\t#[test]\n\tfn can_read_twice_on_different_threads() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.read(key);\n\t\t\t\tassert_eq!(*guard[0], 24);\n\t\t\t\tassert_eq!(*guard[1], 42);\n\t\t\t\tstd::mem::forget(guard);\n\t\t\t});\n\t\t});\n\n\t\tlet guard = collection.try_read(key).unwrap();\n\t\tassert_eq!(*guard[0], 24);\n\t\tassert_eq!(*guard[1], 42);\n\t}\n\n\t#[test]\n\tfn into_ref_iterator() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1), Mutex::new(2)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\t\tfor (i, mutex) in (\u0026collection).into_iter().enumerate() {\n\t\t\tmutex.scoped_lock(\u0026mut key, |val| assert_eq!(*val, i))\n\t\t}\n\t}\n\n\t#[test]\n\tfn ref_iterator() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1), Mutex::new(2)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\t\tfor (i, mutex) in collection.iter().enumerate() {\n\t\t\tmutex.scoped_lock(\u0026mut key, |val| assert_eq!(*val, i))\n\t\t}\n\t}\n\n\t#[test]\n\tfn works_in_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex1 = RwLock::new(0);\n\t\tlet mutex2 = RwLock::new(1);\n\t\tlet collection0 = [\u0026mutex1, \u0026mutex2];\n\t\tlet collection1 = RefLockCollection::try_new(\u0026collection0).unwrap();\n\t\tlet collection = RefLockCollection::try_new(\u0026collection1).unwrap();\n\n\t\tlet mut guard = collection.lock(key);\n\t\tassert!(mutex1.is_locked());\n\t\tassert!(mutex2.is_locked());\n\t\tassert_eq!(*guard[0], 0);\n\t\tassert_eq!(*guard[1], 1);\n\t\t*guard[1] = 2;\n\t\tdrop(guard);\n\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet guard = collection.read(key);\n\t\tassert!(mutex1.is_locked());\n\t\tassert!(mutex2.is_locked());\n\t\tassert_eq!(*guard[0], 0);\n\t\tassert_eq!(*guard[1], 2);\n\t}\n\n\t#[test]\n\tfn unlock_collection_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = (Mutex::new(\"foo\"), Mutex::new(\"bar\"));\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\t\tlet guard = collection.lock(key);\n\n\t\tlet key = RefLockCollection::\u003c(Mutex\u003c_\u003e, Mutex\u003c_\u003e)\u003e::unlock(guard);\n\t\tassert!(collection.try_lock(key).is_ok())\n\t}\n\n\t#[test]\n\tfn read_unlock_collection_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = (RwLock::new(\"foo\"), RwLock::new(\"bar\"));\n\t\tlet collection = RefLockCollection::new(\u0026locks);\n\t\tlet guard = collection.read(key);\n\n\t\tlet key = RefLockCollection::\u003c(\u0026RwLock\u003c_\u003e, \u0026RwLock\u003c_\u003e)\u003e::unlock_read(guard);\n\t\tassert!(collection.try_lock(key).is_ok())\n\t}\n\n\t#[test]\n\tfn as_ref_works() {\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\n\t\tassert!(std::ptr::addr_eq(\u0026raw const mutexes, collection.as_ref()))\n\t}\n\n\t#[test]\n\tfn child() {\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\n\t\tassert!(std::ptr::addr_eq(\u0026raw const mutexes, collection.child()))\n\t}\n}\n","traces":[{"line":19,"address":[1658400],"length":1,"stats":{"Line":1}},{"line":20,"address":[],"length":0,"stats":{"Line":1}},{"line":33,"address":[],"length":0,"stats":{"Line":5}},{"line":34,"address":[],"length":0,"stats":{"Line":6}},{"line":37,"address":[],"length":0,"stats":{"Line":4}},{"line":38,"address":[],"length":0,"stats":{"Line":5}},{"line":41,"address":[1671904,1672160],"length":1,"stats":{"Line":2}},{"line":42,"address":[],"length":0,"stats":{"Line":4}},{"line":43,"address":[],"length":0,"stats":{"Line":2}},{"line":47,"address":[],"length":0,"stats":{"Line":3}},{"line":48,"address":[1672325,1672261,1672485],"length":1,"stats":{"Line":3}},{"line":51,"address":[],"length":0,"stats":{"Line":1}},{"line":52,"address":[],"length":0,"stats":{"Line":1}},{"line":55,"address":[],"length":0,"stats":{"Line":1}},{"line":56,"address":[],"length":0,"stats":{"Line":2}},{"line":57,"address":[],"length":0,"stats":{"Line":1}},{"line":73,"address":[],"length":0,"stats":{"Line":1}},{"line":76,"address":[],"length":0,"stats":{"Line":1}},{"line":79,"address":[],"length":0,"stats":{"Line":1}},{"line":80,"address":[],"length":0,"stats":{"Line":1}},{"line":83,"address":[],"length":0,"stats":{"Line":2}},{"line":84,"address":[],"length":0,"stats":{"Line":2}},{"line":99,"address":[],"length":0,"stats":{"Line":1}},{"line":100,"address":[],"length":0,"stats":{"Line":1}},{"line":103,"address":[],"length":0,"stats":{"Line":1}},{"line":104,"address":[],"length":0,"stats":{"Line":1}},{"line":109,"address":[],"length":0,"stats":{"Line":1}},{"line":110,"address":[],"length":0,"stats":{"Line":1}},{"line":131,"address":[],"length":0,"stats":{"Line":1}},{"line":132,"address":[],"length":0,"stats":{"Line":1}},{"line":152,"address":[],"length":0,"stats":{"Line":6}},{"line":154,"address":[],"length":0,"stats":{"Line":6}},{"line":181,"address":[],"length":0,"stats":{"Line":1}},{"line":182,"address":[],"length":0,"stats":{"Line":1}},{"line":208,"address":[],"length":0,"stats":{"Line":0}},{"line":211,"address":[],"length":0,"stats":{"Line":0}},{"line":234,"address":[],"length":0,"stats":{"Line":3}},{"line":235,"address":[],"length":0,"stats":{"Line":3}},{"line":236,"address":[],"length":0,"stats":{"Line":8}},{"line":237,"address":[],"length":0,"stats":{"Line":1}},{"line":240,"address":[],"length":0,"stats":{"Line":3}},{"line":273,"address":[1623744,1623808],"length":1,"stats":{"Line":2}},{"line":278,"address":[1623821,1623757],"length":1,"stats":{"Line":2}},{"line":318,"address":[1623776],"length":1,"stats":{"Line":1}},{"line":323,"address":[],"length":0,"stats":{"Line":1}},{"line":347,"address":[],"length":0,"stats":{"Line":5}},{"line":350,"address":[],"length":0,"stats":{"Line":5}},{"line":353,"address":[],"length":0,"stats":{"Line":5}},{"line":389,"address":[1628163,1626368,1626538,1628016,1628192,1628339,1628345,1628169,1626544],"length":1,"stats":{"Line":4}},{"line":392,"address":[],"length":0,"stats":{"Line":8}},{"line":393,"address":[1626454,1628079,1628255],"length":1,"stats":{"Line":1}},{"line":397,"address":[],"length":0,"stats":{"Line":5}},{"line":400,"address":[1626499,1628151,1628327],"length":1,"stats":{"Line":3}},{"line":421,"address":[],"length":0,"stats":{"Line":1}},{"line":422,"address":[],"length":0,"stats":{"Line":1}},{"line":423,"address":[],"length":0,"stats":{"Line":0}},{"line":458,"address":[1623840],"length":1,"stats":{"Line":1}},{"line":463,"address":[],"length":0,"stats":{"Line":1}},{"line":503,"address":[1623872],"length":1,"stats":{"Line":1}},{"line":508,"address":[],"length":0,"stats":{"Line":1}},{"line":532,"address":[],"length":0,"stats":{"Line":3}},{"line":535,"address":[],"length":0,"stats":{"Line":3}},{"line":539,"address":[],"length":0,"stats":{"Line":3}},{"line":576,"address":[],"length":0,"stats":{"Line":1}},{"line":579,"address":[1628544,1628587],"length":1,"stats":{"Line":2}},{"line":580,"address":[],"length":0,"stats":{"Line":1}},{"line":584,"address":[],"length":0,"stats":{"Line":1}},{"line":587,"address":[],"length":0,"stats":{"Line":1}},{"line":606,"address":[],"length":0,"stats":{"Line":1}},{"line":607,"address":[],"length":0,"stats":{"Line":1}},{"line":608,"address":[],"length":0,"stats":{"Line":0}},{"line":635,"address":[],"length":0,"stats":{"Line":1}},{"line":636,"address":[],"length":0,"stats":{"Line":1}}],"covered":69,"coverable":73},{"path":["/","home","botahamec","Projects","happylock","src","collection","retry.rs"],"content":"use std::cell::Cell;\nuse std::collections::HashSet;\n\nuse crate::collection::utils;\nuse crate::handle_unwind::handle_unwind;\nuse crate::lockable::{\n\tLockable, LockableGetMut, LockableIntoInner, OwnedLockable, RawLock, Sharable,\n};\nuse crate::{Keyable, ThreadKey};\n\nuse super::utils::{\n\tattempt_to_recover_reads_from_panic, attempt_to_recover_writes_from_panic, get_locks_unsorted,\n\tscoped_read, scoped_try_read, scoped_try_write, scoped_write,\n};\nuse super::{LockGuard, RetryingLockCollection};\n\n/// Checks that a collection contains no duplicate references to a lock.\nfn contains_duplicates\u003cL: Lockable\u003e(data: L) -\u003e bool {\n\tlet mut locks = Vec::new();\n\tdata.get_ptrs(\u0026mut locks);\n\t// cast to *const () so that the v-table pointers are not used for hashing\n\tlet locks = locks.into_iter().map(|l| (\u0026raw const *l).cast::\u003c()\u003e());\n\n\tlet mut locks_set = HashSet::with_capacity(locks.len());\n\tfor lock in locks {\n\t\tif !locks_set.insert(lock) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tfalse\n}\n\nunsafe impl\u003cL: Lockable\u003e RawLock for RetryingLockCollection\u003cL\u003e {\n\t#[mutants::skip] // this should never run\n\t#[cfg(not(tarpaulin_include))]\n\tfn poison(\u0026self) {\n\t\tlet locks = get_locks_unsorted(\u0026self.child);\n\t\tfor lock in locks {\n\t\t\tlock.poison();\n\t\t}\n\t}\n\n\tunsafe fn raw_write(\u0026self) {\n\t\tlet locks = get_locks_unsorted(\u0026self.child);\n\n\t\tif locks.is_empty() {\n\t\t\t// this probably prevents a panic later\n\t\t\treturn;\n\t\t}\n\n\t\t// these will be unlocked in case of a panic\n\t\tlet first_index = Cell::new(0);\n\t\tlet locked = Cell::new(0);\n\t\thandle_unwind(\n\t\t\t|| unsafe {\n\t\t\t\t'outer: loop {\n\t\t\t\t\t// This prevents us from entering a spin loop waiting for\n\t\t\t\t\t// the same lock to be unlocked\n\t\t\t\t\t// safety: we have the thread key\n\t\t\t\t\tlocks[first_index.get()].raw_write();\n\t\t\t\t\tfor (i, lock) in locks.iter().enumerate() {\n\t\t\t\t\t\tif i == first_index.get() {\n\t\t\t\t\t\t\t// we've already locked this one\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// If the lock has been killed, then this returns false\n\t\t\t\t\t\t// instead of panicking. This sounds like a problem, but if\n\t\t\t\t\t\t// it does return false, then the lock function is called\n\t\t\t\t\t\t// immediately after, causing a panic\n\t\t\t\t\t\t// safety: we have the thread key\n\t\t\t\t\t\tif lock.raw_try_write() {\n\t\t\t\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// safety: we already locked all of these\n\t\t\t\t\t\t\tattempt_to_recover_writes_from_panic(\u0026locks[0..i]);\n\t\t\t\t\t\t\tif first_index.get() \u003e= i {\n\t\t\t\t\t\t\t\t// safety: this is already locked and can't be\n\t\t\t\t\t\t\t\t// unlocked by the previous loop\n\t\t\t\t\t\t\t\tlocks[first_index.get()].raw_unlock_write();\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// nothing is locked anymore\n\t\t\t\t\t\t\tlocked.set(0);\n\n\t\t\t\t\t\t\t// call lock on this to prevent a spin loop\n\t\t\t\t\t\t\tfirst_index.set(i);\n\t\t\t\t\t\t\tcontinue 'outer;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// safety: we locked all the data\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t},\n\t\t\t|| {\n\t\t\t\tutils::attempt_to_recover_writes_from_panic(\u0026locks[0..locked.get()]);\n\t\t\t\tif first_index.get() \u003e= locked.get() {\n\t\t\t\t\tlocks[first_index.get()].raw_unlock_write();\n\t\t\t\t}\n\t\t\t},\n\t\t)\n\t}\n\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool {\n\t\tlet locks = get_locks_unsorted(\u0026self.child);\n\n\t\tif locks.is_empty() {\n\t\t\t// this is an interesting case, but it doesn't give us access to\n\t\t\t// any data, and can't possibly cause a deadlock\n\t\t\treturn true;\n\t\t}\n\n\t\t// these will be unlocked in case of a panic\n\t\tlet locked = Cell::new(0);\n\t\thandle_unwind(\n\t\t\t|| unsafe {\n\t\t\t\tfor (i, lock) in locks.iter().enumerate() {\n\t\t\t\t\t// safety: we have the thread key\n\t\t\t\t\tif lock.raw_try_write() {\n\t\t\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// safety: we already locked all of these\n\t\t\t\t\t\tattempt_to_recover_writes_from_panic(\u0026locks[0..i]);\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ttrue\n\t\t\t},\n\t\t\t|| utils::attempt_to_recover_writes_from_panic(\u0026locks[0..locked.get()]),\n\t\t)\n\t}\n\n\tunsafe fn raw_unlock_write(\u0026self) {\n\t\tlet locks = get_locks_unsorted(\u0026self.child);\n\n\t\tfor lock in locks {\n\t\t\tlock.raw_unlock_write();\n\t\t}\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tlet locks = get_locks_unsorted(\u0026self.child);\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\tunsafe {\n\t\t\t\t\tlocks[first_index.get()].raw_read();\n\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\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// safety: we have the thread key\n\t\t\t\t\t\tif lock.raw_try_read() {\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_reads_from_panic(\u0026locks[0..i]);\n\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 unlocked\n\t\t\t\t\t\t\t\t// by the previous loop\n\t\t\t\t\t\t\t\tlocks[first_index.get()].raw_unlock_read();\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// these are no longer locked\n\t\t\t\t\t\t\tlocked.set(0);\n\n\t\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\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_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.child);\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.child);\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\t// this collection, just like the sorting collection, must return all of its\n\t\t// locks in order to check for duplication\n\t\tself.child.get_ptrs(ptrs)\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 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.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 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.child.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.child.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.child.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.child.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.child.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.child.extend(iter)\n\t}\n}\n\nimpl\u003cT: ?Sized, L: AsRef\u003cT\u003e\u003e AsRef\u003cT\u003e for RetryingLockCollection\u003cL\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself.child.as_ref()\n\t}\n}\n\nimpl\u003cT: ?Sized, L: AsMut\u003cT\u003e\u003e AsMut\u003cT\u003e for RetryingLockCollection\u003cL\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself.child.as_mut()\n\t}\n}\n\nimpl\u003cL: OwnedLockable + Default\u003e Default for RetryingLockCollection\u003cL\u003e {\n\tfn default() -\u003e Self {\n\t\tSelf::new(L::default())\n\t}\n}\n\nimpl\u003cL: OwnedLockable\u003e From\u003cL\u003e for RetryingLockCollection\u003cL\u003e {\n\tfn from(value: L) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\nimpl\u003cL: OwnedLockable\u003e RetryingLockCollection\u003cL\u003e {\n\t/// Creates a new collection of owned locks.\n\t///\n\t/// Because the locks are owned, there's no need to do any checks for\n\t/// duplicate values. The locks also don't need to be sorted by memory\n\t/// address because they aren't used anywhere else.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t/// ```\n\t#[must_use]\n\tpub const fn new(data: L) -\u003e Self {\n\t\t// safety: the data cannot cannot contain references\n\t\tunsafe { Self::new_unchecked(data) }\n\t}\n}\n\nimpl\u003c'a, L: OwnedLockable\u003e RetryingLockCollection\u003c\u0026'a L\u003e {\n\t/// Creates a new collection of owned locks.\n\t///\n\t/// Because the locks are owned, there's no need to do any checks for\n\t/// duplicate values.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new_ref(\u0026data);\n\t/// ```\n\t#[must_use]\n\tpub const fn new_ref(data: \u0026'a L) -\u003e Self {\n\t\t// safety: the data cannot cannot contain references\n\t\tunsafe { Self::new_unchecked(data) }\n\t}\n}\n\nimpl\u003cL\u003e RetryingLockCollection\u003cL\u003e {\n\t/// Creates a new collections of locks.\n\t///\n\t/// # Safety\n\t///\n\t/// This results in undefined behavior if any locks are presented twice\n\t/// within this collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let data1 = Mutex::new(0);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // safety: data1 and data2 refer to distinct mutexes\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = unsafe { RetryingLockCollection::new_unchecked(\u0026data) };\n\t/// ```\n\t#[must_use]\n\tpub const unsafe fn new_unchecked(data: L) -\u003e Self {\n\t\tSelf { child: 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.child\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.child\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.child\n\t}\n}\n\nimpl\u003cL: Lockable\u003e RetryingLockCollection\u003cL\u003e {\n\t/// Creates a new collection of locks.\n\t///\n\t/// This returns `None` if any locks are found twice in the given\n\t/// collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let data1 = Mutex::new(0);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // data1 and data2 refer to distinct mutexes, so this won't panic\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = RetryingLockCollection::try_new(\u0026data).unwrap();\n\t/// ```\n\t#[must_use]\n\tpub fn try_new(data: L) -\u003e Option\u003cSelf\u003e {\n\t\t// safety: the data is checked for duplicates before returning the collection\n\t\t(!contains_duplicates(\u0026data)).then_some(unsafe { Self::new_unchecked(data) })\n\t}\n\n\t/// Acquires an exclusive lock, blocking until it is safe to do so, and then\n\t/// unlocks after the provided function returns.\n\t///\n\t/// This function is useful to ensure that the data is never accidentally\n\t/// locked forever by leaking the guard. Even if the function panics, this\n\t/// function will gracefully notice the panic, and unlock. This function\n\t/// provides no guarantees with respect to the ordering of whether contentious\n\t/// readers or writers will acquire the lock first.\n\t///\n\t/// # Panics\n\t///\n\t/// This function will panic if the provided function also panics. However,\n\t/// the collection will be safely unlocked in this case, allowing the\n\t/// collection to be locked again later.\n\t///\n\t/// # Example\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, Mutex};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// lock.scoped_lock(\u0026mut key, |(number, string)| {\n\t/// *number += 1;\n\t/// *string = \"1\";\n\t/// });\n\t/// ```\n\tpub fn scoped_lock\u003c'a, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: impl Keyable,\n\t\tf: impl FnOnce(L::DataMut\u003c'a\u003e) -\u003e R,\n\t) -\u003e R {\n\t\tscoped_write(self, key, f)\n\t}\n\n\t/// Attempts to acquire an exclusive lock to the underlying data without\n\t/// blocking, and then unlocks once the provided function returns.\n\t///\n\t/// This function implements a non-blocking variant of [`scoped_lock`].\n\t/// Unlike `scoped_lock`, if the lock collection is not already unlocked, then\n\t/// the provided function will not run, and the given [`Keyable`] is returned.\n\t/// This method 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/// If any of the locks in the collection are already locked, then the\n\t/// provided function will not run. `Err` is returned with the given key.\n\t///\n\t/// # Panics\n\t///\n\t/// This function will panic if the provided function also panics. However,\n\t/// the collection will be safely unlocked in this case, allowing the\n\t/// collection to be locked again later.\n\t///\n\t/// # Example\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, Mutex};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// lock.scoped_try_lock(\u0026mut key, |(number, string)| {\n\t/// *number += 1;\n\t/// *string = \"1\";\n\t/// }).expect(\"This lock has not yet been locked\");\n\t/// ```\n\t///\n\t/// [`scoped_lock`]: RetryingLockCollection::scoped_lock\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 FnOnce(L::DataMut\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tscoped_try_write(self, key, f)\n\t}\n\n\t/// Locks the collection\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data. When the guard is dropped, the locks in the collection are also\n\t/// dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// ```\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::Guard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\t// safety: we're taking the thread key\n\t\t\tself.raw_write();\n\n\t\t\tLockGuard {\n\t\t\t\t// safety: we just locked the collection\n\t\t\t\tguard: self.guard(),\n\t\t\t\tkey,\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Attempts to lock the without blocking.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// locks when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If any of the locks in the collection are already locked, then an error\n\t/// is returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// match lock.try_lock(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::Guard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tunsafe {\n\t\t\t// safety: we're taking the thread key\n\t\t\tif self.raw_try_write() {\n\t\t\t\tOk(LockGuard {\n\t\t\t\t\t// safety: we just succeeded in locking everything\n\t\t\t\t\tguard: self.guard(),\n\t\t\t\t\tkey,\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\tErr(key)\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// let key = RetryingLockCollection::\u003c(Mutex\u003ci32\u003e, Mutex\u003c\u0026str\u003e)\u003e::unlock(guard);\n\t/// ```\n\tpub fn unlock(guard: LockGuard\u003cL::Guard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: Sharable\u003e RetryingLockCollection\u003cL\u003e {\n\t/// Acquires a shared lock, blocking until it is safe to do so, and then\n\t/// unlocks after the provided function returns.\n\t///\n\t/// This function is useful to ensure that the data is never accidentally\n\t/// locked forever by leaking the guard. Even if the function panics, this\n\t/// function will gracefully notice the panic, and unlock. This function\n\t/// provides no guarantees with respect to the ordering of whether contentious\n\t/// readers or writers will acquire the lock first.\n\t///\n\t/// # Panics\n\t///\n\t/// This function will panic if the provided function also panics. However,\n\t/// the collection will be safely unlocked in this case, allowing the\n\t/// collection to be locked again later.\n\t///\n\t/// # Example\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, RwLock};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// lock.scoped_read(\u0026mut key, |(number, string)| {\n\t/// assert_eq!(*number, 0);\n\t/// assert_eq!(*string, \"\");\n\t/// });\n\t/// ```\n\tpub fn scoped_read\u003c'a, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: impl Keyable,\n\t\tf: impl FnOnce(L::DataRef\u003c'a\u003e) -\u003e R,\n\t) -\u003e R {\n\t\tscoped_read(self, key, f)\n\t}\n\n\t/// Attempts to acquire an exclusive lock to the underlying data without\n\t/// blocking, and then unlocks once the provided function returns.\n\t///\n\t/// This function implements a non-blocking variant of [`scoped_read`].\n\t/// Unlike `scoped_read`, if the lock collection is exclusively locked, then\n\t/// the provided function will not run, and the given [`Keyable`] is returned.\n\t/// This method 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/// If any of the locks in the collection are already exclusively locked, then\n\t/// the provided function will not run. `Err` is returned with the given key.\n\t///\n\t/// # Panics\n\t///\n\t/// This function will panic if the provided function also panics. However,\n\t/// the collection will be safely unlocked in this case, allowing the\n\t/// collection to be locked again later.\n\t///\n\t/// # Example\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, RwLock};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// lock.scoped_try_read(\u0026mut key, |(number, string)| {\n\t/// assert_eq!(*number, 0);\n\t/// assert_eq!(*string, \"\");\n\t/// }).expect(\"This lock has not yet been locked\");\n\t/// ```\n\t///\n\t/// [`scoped_read`]: RetryingLockCollection::scoped_read\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 FnOnce(L::DataRef\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tscoped_try_read(self, key, f)\n\t}\n\n\t/// Locks the collection, so that other threads can still read from it\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data immutably. When the guard is dropped, the locks in the collection\n\t/// are also dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// assert_eq!(*guard.0, 0);\n\t/// assert_eq!(*guard.1, \"\");\n\t/// ```\n\tpub fn read(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\t// safety: we're taking the thread key\n\t\t\tself.raw_read();\n\n\t\t\tLockGuard {\n\t\t\t\t// safety: we just locked the collection\n\t\t\t\tguard: self.read_guard(),\n\t\t\t\tkey,\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Attempts to lock the without blocking, in such a way that other threads\n\t/// can still read from the collection.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// shared access when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If shared access cannot be acquired at this time, then an error is\n\t/// returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(5), RwLock::new(\"6\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// match lock.try_read(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// assert_eq!(*guard.0, 5);\n\t/// assert_eq!(*guard.1, \"6\");\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_read(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tunsafe {\n\t\t\t// safety: we're taking the thread key\n\t\t\tif !self.raw_try_read() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\tOk(LockGuard {\n\t\t\t\t// safety: we just succeeded in locking everything\n\t\t\t\tguard: self.read_guard(),\n\t\t\t\tkey,\n\t\t\t})\n\t\t}\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// let key = RetryingLockCollection::\u003c(RwLock\u003ci32\u003e, RwLock\u003c\u0026str\u003e)\u003e::unlock_read(guard);\n\t/// ```\n\tpub fn unlock_read(guard: LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: LockableGetMut\u003e RetryingLockCollection\u003cL\u003e {\n\t/// Gets a mutable reference to the data behind this\n\t/// `RetryingLockCollection`.\n\t///\n\t/// Since this call borrows the `RetryingLockCollection` mutably, no actual\n\t/// locking needs to take place - the mutable borrow statically guarantees\n\t/// no locks exist.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let mut mutex = RetryingLockCollection::new([Mutex::new(0), Mutex::new(0)]);\n\t/// assert_eq!(mutex.get_mut(), [\u0026mut 0, \u0026mut 0]);\n\t/// ```\n\tpub fn get_mut(\u0026mut self) -\u003e L::Inner\u003c'_\u003e {\n\t\tLockableGetMut::get_mut(self)\n\t}\n}\n\nimpl\u003cL: LockableIntoInner\u003e RetryingLockCollection\u003cL\u003e {\n\t/// Consumes this `RetryingLockCollection`, returning the underlying data.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let mutex = RetryingLockCollection::new([Mutex::new(0), Mutex::new(0)]);\n\t/// assert_eq!(mutex.into_inner(), [0, 0]);\n\t/// ```\n\tpub fn into_inner(self) -\u003e L::Inner {\n\t\tLockableIntoInner::into_inner(self)\n\t}\n}\n\nimpl\u003c'a, L: 'a\u003e RetryingLockCollection\u003cL\u003e\nwhere\n\t\u0026'a L: IntoIterator,\n{\n\t/// Returns an iterator over references to each value in the collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = [Mutex::new(26), Mutex::new(1)];\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let mut iter = lock.iter();\n\t/// let mutex = iter.next().unwrap();\n\t/// let guard = mutex.lock(key);\n\t///\n\t/// assert_eq!(*guard, 26);\n\t/// ```\n\t#[must_use]\n\tpub fn iter(\u0026'a self) -\u003e \u003c\u0026'a L as IntoIterator\u003e::IntoIter {\n\t\tself.into_iter()\n\t}\n}\n\nimpl\u003c'a, L: 'a\u003e RetryingLockCollection\u003cL\u003e\nwhere\n\t\u0026'a mut L: IntoIterator,\n{\n\t/// Returns an iterator over mutable references to each value in the\n\t/// collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = [Mutex::new(26), Mutex::new(1)];\n\t/// let mut lock = RetryingLockCollection::new(data);\n\t///\n\t/// let mut iter = lock.iter_mut();\n\t/// let mutex = iter.next().unwrap();\n\t///\n\t/// assert_eq!(*mutex.as_mut(), 26);\n\t/// ```\n\t#[must_use]\n\tpub fn iter_mut(\u0026'a mut self) -\u003e \u003c\u0026'a mut L as IntoIterator\u003e::IntoIter {\n\t\tself.into_iter()\n\t}\n}\n\n#[cfg(test)]\nmod tests {\n\tuse super::*;\n\tuse crate::collection::BoxedLockCollection;\n\tuse crate::{Mutex, RwLock, ThreadKey};\n\n\t#[test]\n\tfn nonduplicate_lock_references_are_allowed() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(0);\n\t\tassert!(RetryingLockCollection::try_new([\u0026mutex1, \u0026mutex2]).is_some());\n\t}\n\n\t#[test]\n\tfn duplicate_lock_references_are_disallowed() {\n\t\tlet mutex = Mutex::new(0);\n\t\tassert!(RetryingLockCollection::try_new([\u0026mutex, \u0026mutex]).is_none());\n\t}\n\n\t#[test]\n\t#[expect(clippy::float_cmp)]\n\tfn uses_correct_default() {\n\t\tlet collection =\n\t\t\tRetryingLockCollection::\u003c(RwLock\u003cf64\u003e, Mutex\u003cOption\u003ci32\u003e\u003e, Mutex\u003cusize\u003e)\u003e::default();\n\t\tlet tuple = collection.into_inner();\n\t\tassert_eq!(tuple.0, 0.0);\n\t\tassert!(tuple.1.is_none());\n\t\tassert_eq!(tuple.2, 0)\n\t}\n\n\t#[test]\n\tfn from() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tRetryingLockCollection::from([Mutex::new(\"foo\"), Mutex::new(\"bar\"), Mutex::new(\"baz\")]);\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], \"foo\");\n\t\tassert_eq!(*guard[1], \"bar\");\n\t\tassert_eq!(*guard[2], \"baz\");\n\t}\n\n\t#[test]\n\tfn new_ref_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet collection = RetryingLockCollection::new_ref(\u0026mutexes);\n\t\tcollection.scoped_lock(key, |guard| {\n\t\t\tassert_eq!(*guard[0], 0);\n\t\t\tassert_eq!(*guard[1], 1);\n\t\t})\n\t}\n\n\t#[test]\n\tfn scoped_read_sees_changes() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = RetryingLockCollection::new(mutexes);\n\t\tcollection.scoped_lock(\u0026mut key, |guard| *guard[0] = 128);\n\n\t\tlet sum = collection.scoped_read(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 128);\n\t\t\tassert_eq!(*guard[1], 42);\n\t\t\t*guard[0] + *guard[1]\n\t\t});\n\n\t\tassert_eq!(sum, 128 + 42);\n\t}\n\n\t#[test]\n\tfn get_mut_affects_scoped_read() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet mut collection = RetryingLockCollection::new(mutexes);\n\t\tlet guard = collection.get_mut();\n\t\t*guard[0] = 128;\n\n\t\tlet sum = collection.scoped_read(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 128);\n\t\t\tassert_eq!(*guard[1], 42);\n\t\t\t*guard[0] + *guard[1]\n\t\t});\n\n\t\tassert_eq!(sum, 128 + 42);\n\t}\n\n\t#[test]\n\tfn scoped_try_lock_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = RetryingLockCollection::new([Mutex::new(1), Mutex::new(2)]);\n\t\tlet guard = collection.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = collection.scoped_try_lock(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn scoped_try_read_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = RetryingLockCollection::new([RwLock::new(1), RwLock::new(2)]);\n\t\tlet guard = collection.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = collection.scoped_try_read(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn try_lock_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = RetryingLockCollection::new([Mutex::new(1), Mutex::new(2)]);\n\t\tlet guard = collection.try_lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_lock(key);\n\t\t\t\tassert!(guard.is_err());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn try_read_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = RetryingLockCollection::new([RwLock::new(1), RwLock::new(2)]);\n\t\tlet guard = collection.try_read(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_read(key);\n\t\t\t\tassert!(guard.is_ok());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn try_read_fails_for_locked_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = RetryingLockCollection::new_ref(\u0026mutexes);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = mutexes[1].write(key);\n\t\t\t\tassert_eq!(*guard, 42);\n\t\t\t\tstd::mem::forget(guard);\n\t\t\t});\n\t\t});\n\n\t\tlet guard = collection.try_read(key);\n\t\tassert!(guard.is_err());\n\t}\n\n\t#[test]\n\tfn locks_all_inner_mutexes() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(0);\n\t\tlet collection = RetryingLockCollection::try_new([\u0026mutex1, \u0026mutex2]).unwrap();\n\n\t\tlet guard = collection.lock(key);\n\n\t\tassert!(mutex1.is_locked());\n\t\tassert!(mutex2.is_locked());\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn locks_all_inner_rwlocks() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet rwlock1 = RwLock::new(0);\n\t\tlet rwlock2 = RwLock::new(0);\n\t\tlet collection = RetryingLockCollection::try_new([\u0026rwlock1, \u0026rwlock2]).unwrap();\n\n\t\tlet guard = collection.read(key);\n\n\t\tassert!(rwlock1.is_locked());\n\t\tassert!(rwlock2.is_locked());\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn works_with_other_collections() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(0);\n\t\tlet collection = BoxedLockCollection::try_new(\n\t\t\tRetryingLockCollection::try_new([\u0026mutex1, \u0026mutex2]).unwrap(),\n\t\t)\n\t\t.unwrap();\n\n\t\tlet guard = collection.lock(key);\n\n\t\tassert!(mutex1.is_locked());\n\t\tassert!(mutex2.is_locked());\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn from_iterator() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection: RetryingLockCollection\u003cVec\u003cMutex\u003c\u0026str\u003e\u003e\u003e =\n\t\t\t[Mutex::new(\"foo\"), Mutex::new(\"bar\"), Mutex::new(\"baz\")]\n\t\t\t\t.into_iter()\n\t\t\t\t.collect();\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], \"foo\");\n\t\tassert_eq!(*guard[1], \"bar\");\n\t\tassert_eq!(*guard[2], \"baz\");\n\t}\n\n\t#[test]\n\tfn into_owned_iterator() {\n\t\tlet collection = RetryingLockCollection::new([Mutex::new(0), Mutex::new(1), Mutex::new(2)]);\n\t\tfor (i, mutex) in collection.into_iter().enumerate() {\n\t\t\tassert_eq!(mutex.into_inner(), i);\n\t\t}\n\t}\n\n\t#[test]\n\tfn into_ref_iterator() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet collection = RetryingLockCollection::new([Mutex::new(0), Mutex::new(1), Mutex::new(2)]);\n\t\tfor (i, mutex) in (\u0026collection).into_iter().enumerate() {\n\t\t\tmutex.scoped_lock(\u0026mut key, |val| assert_eq!(*val, i))\n\t\t}\n\t}\n\n\t#[test]\n\tfn ref_iterator() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet collection = RetryingLockCollection::new([Mutex::new(0), Mutex::new(1), Mutex::new(2)]);\n\t\tfor (i, mutex) in collection.iter().enumerate() {\n\t\t\tmutex.scoped_lock(\u0026mut key, |val| assert_eq!(*val, i))\n\t\t}\n\t}\n\n\t#[test]\n\tfn mut_iterator() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mut collection =\n\t\t\tRetryingLockCollection::new([Mutex::new(0), Mutex::new(1), Mutex::new(2)]);\n\t\tfor (i, mutex) in collection.iter_mut().enumerate() {\n\t\t\tmutex.scoped_lock(\u0026mut key, |val| assert_eq!(*val, i))\n\t\t}\n\t}\n\n\t#[test]\n\tfn extend_collection() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(0);\n\t\tlet mut collection = RetryingLockCollection::new(vec![mutex1]);\n\n\t\tcollection.extend([mutex2]);\n\n\t\tassert_eq!(collection.into_inner().len(), 2);\n\t}\n\n\t#[test]\n\tfn lock_empty_lock_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection: RetryingLockCollection\u003c[RwLock\u003ci32\u003e; 0]\u003e = RetryingLockCollection::new([]);\n\n\t\tlet guard = collection.lock(key);\n\t\tassert!(guard.is_empty());\n\t\tlet key = RetryingLockCollection::\u003c[RwLock\u003c_\u003e; 0]\u003e::unlock(guard);\n\n\t\tlet guard = collection.read(key);\n\t\tassert!(guard.is_empty());\n\t}\n\n\t#[test]\n\tfn read_empty_lock_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection: RetryingLockCollection\u003c[RwLock\u003ci32\u003e; 0]\u003e = RetryingLockCollection::new([]);\n\n\t\tlet guard = collection.read(key);\n\t\tassert!(guard.is_empty());\n\t\tlet key = RetryingLockCollection::\u003c[RwLock\u003c_\u003e; 0]\u003e::unlock_read(guard);\n\n\t\tlet guard = collection.lock(key);\n\t\tassert!(guard.is_empty());\n\t}\n\n\t#[test]\n\tfn as_ref_works() {\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet collection = RetryingLockCollection::new_ref(\u0026mutexes);\n\n\t\tassert!(std::ptr::addr_eq(\u0026raw const mutexes, collection.as_ref()))\n\t}\n\n\t#[test]\n\tfn as_mut_works() {\n\t\tlet mut mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet mut collection = RetryingLockCollection::new(\u0026mut mutexes);\n\n\t\tcollection.as_mut()[0] = Mutex::new(42);\n\n\t\tassert_eq!(*collection.as_mut()[0].get_mut(), 42);\n\t}\n\n\t#[test]\n\tfn child() {\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet collection = RetryingLockCollection::new_ref(\u0026mutexes);\n\n\t\tassert!(std::ptr::addr_eq(\u0026raw const mutexes, *collection.child()))\n\t}\n\n\t#[test]\n\tfn child_mut_works() {\n\t\tlet mut mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet mut collection = RetryingLockCollection::new(\u0026mut mutexes);\n\n\t\tcollection.child_mut()[0] = Mutex::new(42);\n\n\t\tassert_eq!(*collection.child_mut()[0].get_mut(), 42);\n\t}\n\n\t#[test]\n\tfn into_child_works() {\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet mut collection = RetryingLockCollection::new(mutexes);\n\n\t\tcollection.child_mut()[0] = Mutex::new(42);\n\n\t\tassert_eq!(\n\t\t\t*collection\n\t\t\t\t.into_child()\n\t\t\t\t.as_mut()\n\t\t\t\t.get_mut(0)\n\t\t\t\t.unwrap()\n\t\t\t\t.get_mut(),\n\t\t\t42\n\t\t);\n\t}\n}\n","traces":[{"line":18,"address":[669840,670568,669880],"length":1,"stats":{"Line":11}},{"line":19,"address":[1214444,1213676],"length":1,"stats":{"Line":11}},{"line":20,"address":[662971],"length":1,"stats":{"Line":11}},{"line":22,"address":[656256,654794,655562,656235,656192,656299],"length":1,"stats":{"Line":33}},{"line":24,"address":[663184,663118],"length":1,"stats":{"Line":22}},{"line":25,"address":[657859,657720,657623],"length":1,"stats":{"Line":33}},{"line":26,"address":[],"length":0,"stats":{"Line":22}},{"line":27,"address":[658003],"length":1,"stats":{"Line":1}},{"line":31,"address":[618515],"length":1,"stats":{"Line":11}},{"line":44,"address":[684392,684144,684398],"length":1,"stats":{"Line":13}},{"line":45,"address":[669548],"length":1,"stats":{"Line":12}},{"line":47,"address":[585722,585670],"length":1,"stats":{"Line":24}},{"line":49,"address":[],"length":0,"stats":{"Line":0}},{"line":53,"address":[1665141,1667792,1665925,1667829,1668064,1668101,1668608,1666469,1669184,1669221,1665104,1668645,1666432,1665888],"length":1,"stats":{"Line":22}},{"line":54,"address":[],"length":0,"stats":{"Line":11}},{"line":56,"address":[595341],"length":1,"stats":{"Line":22}},{"line":57,"address":[],"length":0,"stats":{"Line":0}},{"line":61,"address":[],"length":0,"stats":{"Line":11}},{"line":62,"address":[658230],"length":1,"stats":{"Line":11}},{"line":63,"address":[616066],"length":1,"stats":{"Line":11}},{"line":65,"address":[],"length":0,"stats":{"Line":0}},{"line":73,"address":[],"length":0,"stats":{"Line":11}},{"line":74,"address":[616341,616191],"length":1,"stats":{"Line":20}},{"line":77,"address":[616128],"length":1,"stats":{"Line":1}},{"line":78,"address":[],"length":0,"stats":{"Line":1}},{"line":81,"address":[657225],"length":1,"stats":{"Line":1}},{"line":85,"address":[619165],"length":1,"stats":{"Line":1}},{"line":88,"address":[619189],"length":1,"stats":{"Line":1}},{"line":89,"address":[],"length":0,"stats":{"Line":0}},{"line":94,"address":[],"length":0,"stats":{"Line":0}},{"line":97,"address":[619296],"length":1,"stats":{"Line":12}},{"line":98,"address":[657321],"length":1,"stats":{"Line":1}},{"line":99,"address":[658788],"length":1,"stats":{"Line":1}},{"line":100,"address":[657433],"length":1,"stats":{"Line":1}},{"line":106,"address":[585005,584816,585011],"length":1,"stats":{"Line":4}},{"line":107,"address":[],"length":0,"stats":{"Line":3}},{"line":109,"address":[598864,598910],"length":1,"stats":{"Line":6}},{"line":112,"address":[1665384],"length":1,"stats":{"Line":0}},{"line":116,"address":[584900,584942],"length":1,"stats":{"Line":6}},{"line":118,"address":[1216064],"length":1,"stats":{"Line":3}},{"line":119,"address":[663838,663761],"length":1,"stats":{"Line":6}},{"line":121,"address":[656519],"length":1,"stats":{"Line":3}},{"line":122,"address":[664012],"length":1,"stats":{"Line":3}},{"line":125,"address":[],"length":0,"stats":{"Line":2}},{"line":126,"address":[1216331],"length":1,"stats":{"Line":3}},{"line":130,"address":[1216275],"length":1,"stats":{"Line":1}},{"line":132,"address":[],"length":0,"stats":{"Line":2}},{"line":136,"address":[],"length":0,"stats":{"Line":3}},{"line":137,"address":[585343,585039],"length":1,"stats":{"Line":3}},{"line":139,"address":[585213,585049,585353,585517],"length":1,"stats":{"Line":6}},{"line":140,"address":[1665767,1667399,1669037,1669063,1665741,1667373],"length":1,"stats":{"Line":6}},{"line":144,"address":[675848,675854,675600],"length":1,"stats":{"Line":6}},{"line":145,"address":[639980],"length":1,"stats":{"Line":5}},{"line":147,"address":[],"length":0,"stats":{"Line":10}},{"line":149,"address":[],"length":0,"stats":{"Line":0}},{"line":152,"address":[],"length":0,"stats":{"Line":8}},{"line":153,"address":[640090],"length":1,"stats":{"Line":4}},{"line":155,"address":[],"length":0,"stats":{"Line":8}},{"line":157,"address":[],"length":0,"stats":{"Line":0}},{"line":158,"address":[1221361,1219153,1217249],"length":1,"stats":{"Line":4}},{"line":160,"address":[670742],"length":1,"stats":{"Line":4}},{"line":161,"address":[670930],"length":1,"stats":{"Line":4}},{"line":162,"address":[],"length":0,"stats":{"Line":0}},{"line":166,"address":[1217524,1219428,1221636],"length":1,"stats":{"Line":4}},{"line":167,"address":[],"length":0,"stats":{"Line":6}},{"line":170,"address":[1221660,1219452,1217548],"length":1,"stats":{"Line":1}},{"line":172,"address":[1217583,1219487,1221695],"length":1,"stats":{"Line":1}},{"line":175,"address":[584345],"length":1,"stats":{"Line":1}},{"line":179,"address":[584301],"length":1,"stats":{"Line":1}},{"line":182,"address":[1221792,1219584,1217680],"length":1,"stats":{"Line":1}},{"line":183,"address":[],"length":0,"stats":{"Line":0}},{"line":188,"address":[],"length":0,"stats":{"Line":0}},{"line":191,"address":[],"length":0,"stats":{"Line":5}},{"line":192,"address":[1217801,1221913,1219705],"length":1,"stats":{"Line":1}},{"line":193,"address":[584500],"length":1,"stats":{"Line":1}},{"line":194,"address":[584553],"length":1,"stats":{"Line":1}},{"line":200,"address":[1669555,1669360,1666803,1666797,1666608,1669549],"length":1,"stats":{"Line":4}},{"line":201,"address":[],"length":0,"stats":{"Line":3}},{"line":203,"address":[],"length":0,"stats":{"Line":6}},{"line":206,"address":[],"length":0,"stats":{"Line":0}},{"line":209,"address":[],"length":0,"stats":{"Line":6}},{"line":211,"address":[1218704,1223552],"length":1,"stats":{"Line":3}},{"line":212,"address":[],"length":0,"stats":{"Line":6}},{"line":214,"address":[679831],"length":1,"stats":{"Line":3}},{"line":215,"address":[679916],"length":1,"stats":{"Line":3}},{"line":218,"address":[],"length":0,"stats":{"Line":2}},{"line":219,"address":[1223819,1218971],"length":1,"stats":{"Line":2}},{"line":223,"address":[1223763,1218915],"length":1,"stats":{"Line":1}},{"line":225,"address":[679984,680016],"length":1,"stats":{"Line":2}},{"line":229,"address":[],"length":0,"stats":{"Line":1}},{"line":230,"address":[],"length":0,"stats":{"Line":1}},{"line":232,"address":[],"length":0,"stats":{"Line":2}},{"line":233,"address":[1667069,1667095],"length":1,"stats":{"Line":2}},{"line":249,"address":[1673552],"length":1,"stats":{"Line":1}},{"line":252,"address":[],"length":0,"stats":{"Line":1}},{"line":255,"address":[],"length":0,"stats":{"Line":8}},{"line":256,"address":[],"length":0,"stats":{"Line":8}},{"line":259,"address":[],"length":0,"stats":{"Line":3}},{"line":260,"address":[1673617,1673425,1673505],"length":1,"stats":{"Line":3}},{"line":275,"address":[675872],"length":1,"stats":{"Line":4}},{"line":276,"address":[1670117,1670241,1670145,1670209],"length":1,"stats":{"Line":4}},{"line":279,"address":[],"length":0,"stats":{"Line":1}},{"line":280,"address":[],"length":0,"stats":{"Line":1}},{"line":292,"address":[],"length":0,"stats":{"Line":1}},{"line":293,"address":[],"length":0,"stats":{"Line":1}},{"line":300,"address":[],"length":0,"stats":{"Line":2}},{"line":301,"address":[],"length":0,"stats":{"Line":2}},{"line":312,"address":[],"length":0,"stats":{"Line":1}},{"line":313,"address":[],"length":0,"stats":{"Line":1}},{"line":324,"address":[],"length":0,"stats":{"Line":1}},{"line":325,"address":[],"length":0,"stats":{"Line":1}},{"line":336,"address":[],"length":0,"stats":{"Line":1}},{"line":337,"address":[1671301],"length":1,"stats":{"Line":1}},{"line":344,"address":[],"length":0,"stats":{"Line":1}},{"line":345,"address":[1625009],"length":1,"stats":{"Line":1}},{"line":346,"address":[],"length":0,"stats":{"Line":1}},{"line":351,"address":[],"length":0,"stats":{"Line":1}},{"line":352,"address":[],"length":0,"stats":{"Line":1}},{"line":357,"address":[],"length":0,"stats":{"Line":1}},{"line":358,"address":[1671493],"length":1,"stats":{"Line":1}},{"line":363,"address":[],"length":0,"stats":{"Line":1}},{"line":364,"address":[1673701],"length":1,"stats":{"Line":1}},{"line":369,"address":[1673776],"length":1,"stats":{"Line":1}},{"line":370,"address":[],"length":0,"stats":{"Line":1}},{"line":375,"address":[],"length":0,"stats":{"Line":1}},{"line":376,"address":[],"length":0,"stats":{"Line":1}},{"line":397,"address":[1636960,1637024,1637088,1636896,1637312,1637072,1637168,1637232,1637296],"length":1,"stats":{"Line":9}},{"line":399,"address":[1636974,1637073,1637099,1637180,1637325,1636909,1637244,1637301,1637035],"length":1,"stats":{"Line":9}},{"line":419,"address":[],"length":0,"stats":{"Line":3}},{"line":421,"address":[],"length":0,"stats":{"Line":3}},{"line":447,"address":[598368],"length":1,"stats":{"Line":22}},{"line":468,"address":[1654560],"length":1,"stats":{"Line":1}},{"line":469,"address":[],"length":0,"stats":{"Line":0}},{"line":489,"address":[1654528,1654304],"length":1,"stats":{"Line":2}},{"line":490,"address":[],"length":0,"stats":{"Line":0}},{"line":510,"address":[],"length":0,"stats":{"Line":1}},{"line":511,"address":[],"length":0,"stats":{"Line":1}},{"line":535,"address":[595109,594944],"length":1,"stats":{"Line":11}},{"line":537,"address":[675259,675314],"length":1,"stats":{"Line":22}},{"line":570,"address":[584352],"length":1,"stats":{"Line":3}},{"line":575,"address":[584365],"length":1,"stats":{"Line":3}},{"line":615,"address":[584320],"length":1,"stats":{"Line":2}},{"line":620,"address":[],"length":0,"stats":{"Line":2}},{"line":643,"address":[1656488,1656416,1656058,1656064,1656196,1656698,1656080,1656576,1657308,1656720,1656202,1656692,1656836,1656842,1657216,1657302,1656482,1655936],"length":1,"stats":{"Line":10}},{"line":646,"address":[1656430,1655966,1656112,1656608,1656752,1657230],"length":1,"stats":{"Line":9}},{"line":650,"address":[669230],"length":1,"stats":{"Line":8}},{"line":686,"address":[598592,598784,598790],"length":1,"stats":{"Line":3}},{"line":689,"address":[1656299,1656317,1656388,1656256],"length":1,"stats":{"Line":5}},{"line":690,"address":[],"length":0,"stats":{"Line":1}},{"line":692,"address":[598704],"length":1,"stats":{"Line":1}},{"line":693,"address":[],"length":0,"stats":{"Line":0}},{"line":696,"address":[598685],"length":1,"stats":{"Line":1}},{"line":719,"address":[1656554,1656560,1656512],"length":1,"stats":{"Line":1}},{"line":720,"address":[1656516],"length":1,"stats":{"Line":1}},{"line":721,"address":[],"length":0,"stats":{"Line":0}},{"line":756,"address":[1624544,1624576],"length":1,"stats":{"Line":2}},{"line":761,"address":[],"length":0,"stats":{"Line":2}},{"line":801,"address":[1624608],"length":1,"stats":{"Line":1}},{"line":806,"address":[],"length":0,"stats":{"Line":1}},{"line":829,"address":[639940,639934,639808],"length":1,"stats":{"Line":5}},{"line":832,"address":[1657406,1657712],"length":1,"stats":{"Line":4}},{"line":836,"address":[639886],"length":1,"stats":{"Line":3}},{"line":873,"address":[1657660,1657488,1657824,1657990,1657996,1657654],"length":1,"stats":{"Line":4}},{"line":876,"address":[675104,675154],"length":1,"stats":{"Line":5}},{"line":877,"address":[675165],"length":1,"stats":{"Line":1}},{"line":880,"address":[675206],"length":1,"stats":{"Line":1}},{"line":882,"address":[1657593,1657929],"length":1,"stats":{"Line":1}},{"line":883,"address":[],"length":0,"stats":{"Line":0}},{"line":904,"address":[],"length":0,"stats":{"Line":1}},{"line":905,"address":[],"length":0,"stats":{"Line":1}},{"line":906,"address":[],"length":0,"stats":{"Line":0}},{"line":927,"address":[],"length":0,"stats":{"Line":1}},{"line":928,"address":[],"length":0,"stats":{"Line":1}},{"line":944,"address":[],"length":0,"stats":{"Line":2}},{"line":945,"address":[],"length":0,"stats":{"Line":2}},{"line":972,"address":[],"length":0,"stats":{"Line":1}},{"line":973,"address":[],"length":0,"stats":{"Line":1}},{"line":1000,"address":[],"length":0,"stats":{"Line":1}},{"line":1001,"address":[],"length":0,"stats":{"Line":1}}],"covered":161,"coverable":179},{"path":["/","home","botahamec","Projects","happylock","src","collection","utils.rs"],"content":"use std::cell::Cell;\n\nuse crate::handle_unwind::handle_unwind;\nuse crate::lockable::{Lockable, RawLock, Sharable};\nuse crate::Keyable;\n\n/// Returns a list of locks in the given collection and sorts them by their\n/// memory address\n#[must_use]\npub fn get_locks\u003cL: Lockable\u003e(data: \u0026L) -\u003e Vec\u003c\u0026dyn RawLock\u003e {\n\tlet mut locks = get_locks_unsorted(data);\n\tlocks.sort_by_key(|lock| \u0026raw const **lock);\n\tlocks\n}\n\n/// Returns a list of locks from the data. Unlike the above function, this does\n/// not do any sorting of the locks.\n#[must_use]\npub fn get_locks_unsorted\u003cL: Lockable\u003e(data: \u0026L) -\u003e Vec\u003c\u0026dyn RawLock\u003e {\n\tlet mut locks = Vec::new();\n\tdata.get_ptrs(\u0026mut locks);\n\tlocks\n}\n\n/// returns `true` if the sorted list contains a duplicate\n#[must_use]\npub fn ordered_contains_duplicates(l: \u0026[\u0026dyn RawLock]) -\u003e bool {\n\tif l.is_empty() {\n\t\t// Return early to prevent panic in the below call to `windows`\n\t\treturn false;\n\t}\n\n\tl.windows(2)\n\t\t// NOTE: addr_eq is necessary because eq would also compare the v-table pointers\n\t\t.any(|window| std::ptr::addr_eq(window[0], window[1]))\n}\n\n/// Lock a set of locks in the given order. It's UB to call this without a `ThreadKey`\npub unsafe fn ordered_write(locks: \u0026[\u0026dyn RawLock]) {\n\t// these will be unlocked in case of a panic\n\tlet locked = Cell::new(0);\n\n\thandle_unwind(\n\t\t|| {\n\t\t\tfor lock in locks {\n\t\t\t\tlock.raw_write();\n\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t}\n\t\t},\n\t\t|| attempt_to_recover_writes_from_panic(\u0026locks[0..locked.get()]),\n\t)\n}\n\n/// Lock a set of locks in the given order. It's UB to call this without a `ThreadKey`\npub unsafe fn ordered_read(locks: \u0026[\u0026dyn RawLock]) {\n\tlet locked = Cell::new(0);\n\n\thandle_unwind(\n\t\t|| {\n\t\t\tfor lock in locks {\n\t\t\t\tlock.raw_read();\n\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t}\n\t\t},\n\t\t|| attempt_to_recover_reads_from_panic(\u0026locks[0..locked.get()]),\n\t)\n}\n\n/// Locks the locks in the order they are given. This causes deadlock if the\n/// locks contain duplicates, or if this is called by multiple threads with the\n/// locks in different orders.\npub unsafe fn ordered_try_write(locks: \u0026[\u0026dyn RawLock]) -\u003e bool {\n\tlet locked = Cell::new(0);\n\n\thandle_unwind(\n\t\t|| unsafe {\n\t\t\tfor (i, lock) in locks.iter().enumerate() {\n\t\t\t\t// safety: we have the thread key\n\t\t\t\tif lock.raw_try_write() {\n\t\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t\t} else {\n\t\t\t\t\tfor lock in \u0026locks[0..i] {\n\t\t\t\t\t\t// safety: this lock was already acquired\n\t\t\t\t\t\tlock.raw_unlock_write();\n\t\t\t\t\t}\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttrue\n\t\t},\n\t\t||\n\t\t// safety: everything in locked is locked\n\t\tattempt_to_recover_writes_from_panic(\u0026locks[0..locked.get()]),\n\t)\n}\n\n/// Locks the locks in the order they are given. This causes deadlock if this\n/// is called by multiple threads with the locks in different orders.\npub unsafe fn ordered_try_read(locks: \u0026[\u0026dyn RawLock]) -\u003e bool {\n\t// these will be unlocked in case of a panic\n\tlet locked = Cell::new(0);\n\n\thandle_unwind(\n\t\t|| unsafe {\n\t\t\tfor (i, lock) in locks.iter().enumerate() {\n\t\t\t\t// safety: we have the thread key\n\t\t\t\tif lock.raw_try_read() {\n\t\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t\t} else {\n\t\t\t\t\tfor lock in \u0026locks[0..i] {\n\t\t\t\t\t\t// safety: this lock was already acquired\n\t\t\t\t\t\tlock.raw_unlock_read();\n\t\t\t\t\t}\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttrue\n\t\t},\n\t\t||\n\t\t// safety: everything in locked is locked\n\t\tattempt_to_recover_reads_from_panic(\u0026locks[0..locked.get()]),\n\t)\n}\n\npub fn scoped_write\u003c'a, L: RawLock + Lockable + ?Sized, R\u003e(\n\tcollection: \u0026'a L,\n\tkey: impl Keyable,\n\tf: impl FnOnce(L::DataMut\u003c'a\u003e) -\u003e R,\n) -\u003e R {\n\tunsafe {\n\t\t// safety: we have the key\n\t\tcollection.raw_write();\n\n\t\t// safety: we just locked this\n\t\tlet r = handle_unwind(\n\t\t\t|| f(collection.data_mut()),\n\t\t\t|| collection.raw_unlock_write(),\n\t\t);\n\n\t\t// this ensures the key is held long enough\n\t\tdrop(key);\n\n\t\t// safety: we've locked already, and aren't using the data again\n\t\tcollection.raw_unlock_write();\n\n\t\tr\n\t}\n}\n\npub fn scoped_try_write\u003c'a, L: RawLock + Lockable + ?Sized, Key: Keyable, R\u003e(\n\tcollection: \u0026'a L,\n\tkey: Key,\n\tf: impl FnOnce(L::DataMut\u003c'a\u003e) -\u003e R,\n) -\u003e Result\u003cR, Key\u003e {\n\tunsafe {\n\t\t// safety: we have the key\n\t\tif !collection.raw_try_write() {\n\t\t\treturn Err(key);\n\t\t}\n\n\t\t// safety: we just locked this\n\t\tlet r = handle_unwind(\n\t\t\t|| f(collection.data_mut()),\n\t\t\t|| collection.raw_unlock_write(),\n\t\t);\n\n\t\t// this ensures the key is held long enough\n\t\tdrop(key);\n\n\t\t// safety: we've locked already, and aren't using the data again\n\t\tcollection.raw_unlock_write();\n\n\t\tOk(r)\n\t}\n}\n\npub fn scoped_read\u003c'a, L: RawLock + Sharable + ?Sized, R\u003e(\n\tcollection: \u0026'a L,\n\tkey: impl Keyable,\n\tf: impl FnOnce(L::DataRef\u003c'a\u003e) -\u003e R,\n) -\u003e R {\n\tunsafe {\n\t\t// safety: we have the key\n\t\tcollection.raw_read();\n\n\t\t// safety: we just locked this\n\t\tlet r = handle_unwind(|| f(collection.data_ref()), || collection.raw_unlock_read());\n\n\t\t// this ensures the key is held long enough\n\t\tdrop(key);\n\n\t\t// safety: we've locked already, and aren't using the data again\n\t\tcollection.raw_unlock_read();\n\n\t\tr\n\t}\n}\n\npub fn scoped_try_read\u003c'a, L: RawLock + Sharable + ?Sized, Key: Keyable, R\u003e(\n\tcollection: \u0026'a L,\n\tkey: Key,\n\tf: impl FnOnce(L::DataRef\u003c'a\u003e) -\u003e R,\n) -\u003e Result\u003cR, Key\u003e {\n\tunsafe {\n\t\t// safety: we have the key\n\t\tif !collection.raw_try_read() {\n\t\t\treturn Err(key);\n\t\t}\n\n\t\t// safety: we just locked this\n\t\tlet r = handle_unwind(|| f(collection.data_ref()), || collection.raw_unlock_read());\n\n\t\t// this ensures the key is held long enough\n\t\tdrop(key);\n\n\t\t// safety: we've locked already, and aren't using the data again\n\t\tcollection.raw_unlock_read();\n\n\t\tOk(r)\n\t}\n}\n\n/// Unlocks the already locked locks in order to recover from a panic\npub unsafe fn attempt_to_recover_writes_from_panic(locks: \u0026[\u0026dyn RawLock]) {\n\thandle_unwind(\n\t\t|| {\n\t\t\t// safety: the caller assumes that these are already locked\n\t\t\tfor lock in locks {\n\t\t\t\tlock.raw_unlock_write();\n\t\t\t}\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\tfor lock in locked {\n\t\t\t\tlock.raw_unlock_read();\n\t\t\t}\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":10,"address":[1682738,1683392,1683372,1682432,1683218,1682412,1682898,1682752,1682252,1682272,1683212,1682912,1682258,1682892,1682578,1682592,1682732,1683052,1682112,1683058,1683072,1683232,1682418,1683378,1683532,1683538,1682572],"length":1,"stats":{"Line":9}},{"line":11,"address":[1682300,1682620,1682460,1682940,1682140,1683100,1683260,1683420,1682780],"length":1,"stats":{"Line":9}},{"line":12,"address":[],"length":0,"stats":{"Line":36}},{"line":13,"address":[],"length":0,"stats":{"Line":9}},{"line":19,"address":[642320,642451,642445],"length":1,"stats":{"Line":28}},{"line":20,"address":[646994],"length":1,"stats":{"Line":28}},{"line":21,"address":[629025,629169],"length":1,"stats":{"Line":28}},{"line":22,"address":[],"length":0,"stats":{"Line":28}},{"line":27,"address":[997872],"length":1,"stats":{"Line":8}},{"line":28,"address":[948120],"length":1,"stats":{"Line":8}},{"line":30,"address":[973158],"length":1,"stats":{"Line":1}},{"line":33,"address":[972700],"length":1,"stats":{"Line":8}},{"line":35,"address":[978723],"length":1,"stats":{"Line":25}},{"line":39,"address":[1213072],"length":1,"stats":{"Line":4}},{"line":41,"address":[992814],"length":1,"stats":{"Line":5}},{"line":44,"address":[1000144],"length":1,"stats":{"Line":4}},{"line":45,"address":[1004918,1004941],"length":1,"stats":{"Line":10}},{"line":46,"address":[944963],"length":1,"stats":{"Line":4}},{"line":47,"address":[979837,979794],"length":1,"stats":{"Line":8}},{"line":50,"address":[1213101],"length":1,"stats":{"Line":11}},{"line":55,"address":[1212976],"length":1,"stats":{"Line":2}},{"line":56,"address":[992718],"length":1,"stats":{"Line":2}},{"line":59,"address":[979824],"length":1,"stats":{"Line":2}},{"line":60,"address":[999917,999894],"length":1,"stats":{"Line":4}},{"line":61,"address":[1004723],"length":1,"stats":{"Line":2}},{"line":62,"address":[979938,979981],"length":1,"stats":{"Line":4}},{"line":65,"address":[1213005],"length":1,"stats":{"Line":4}},{"line":72,"address":[947984],"length":1,"stats":{"Line":2}},{"line":73,"address":[972983],"length":1,"stats":{"Line":2}},{"line":76,"address":[972591],"length":1,"stats":{"Line":4}},{"line":77,"address":[945679],"length":1,"stats":{"Line":2}},{"line":79,"address":[1005873],"length":1,"stats":{"Line":2}},{"line":80,"address":[1001367,1001225],"length":1,"stats":{"Line":4}},{"line":82,"address":[1006018,1005918],"length":1,"stats":{"Line":2}},{"line":84,"address":[986871],"length":1,"stats":{"Line":1}},{"line":86,"address":[1006099],"length":1,"stats":{"Line":1}},{"line":90,"address":[981089],"length":1,"stats":{"Line":1}},{"line":92,"address":[980928],"length":1,"stats":{"Line":3}},{"line":94,"address":[1689092],"length":1,"stats":{"Line":1}},{"line":100,"address":[972432],"length":1,"stats":{"Line":2}},{"line":102,"address":[972455],"length":1,"stats":{"Line":2}},{"line":105,"address":[978479],"length":1,"stats":{"Line":5}},{"line":106,"address":[988207],"length":1,"stats":{"Line":2}},{"line":108,"address":[980129],"length":1,"stats":{"Line":2}},{"line":109,"address":[986233,986375],"length":1,"stats":{"Line":4}},{"line":111,"address":[1005390,1005490],"length":1,"stats":{"Line":2}},{"line":113,"address":[986343],"length":1,"stats":{"Line":1}},{"line":115,"address":[1688508],"length":1,"stats":{"Line":1}},{"line":119,"address":[980561],"length":1,"stats":{"Line":1}},{"line":121,"address":[947939],"length":1,"stats":{"Line":3}},{"line":123,"address":[955844],"length":1,"stats":{"Line":1}},{"line":127,"address":[1675615,1675632,1675264,1675967,1675984,1676143,1676336,1676495,1675456,1677552,1677232,1677039,1675808,1677391,1675791,1675439,1677056,1676160,1676688,1677215,1676319,1677574,1676671,1676512,1676847,1677408,1676864],"length":1,"stats":{"Line":14}},{"line":134,"address":[],"length":0,"stats":{"Line":14}},{"line":138,"address":[1677313,1685476,1675537,1685321,1676945,1686108,1675713,1684528,1685730,1685874,1675345,1675889,1684540,1684684,1685696,1685184,1685056,1676241,1684937,1685193,1677137,1685577,1685852,1685440,1677479,1684928,1685980,1685568,1686002,1684562,1684800,1676417,1684812,1685312,1685092,1676769,1685449,1684706,1676593,1685220,1685708,1685968,1686096,1686130,1676065,1684834,1684964,1685840,1685348,1684672,1685604,1685065],"length":1,"stats":{"Line":42}},{"line":139,"address":[],"length":0,"stats":{"Line":0}},{"line":143,"address":[],"length":0,"stats":{"Line":14}},{"line":146,"address":[628696],"length":1,"stats":{"Line":14}},{"line":148,"address":[],"length":0,"stats":{"Line":0}},{"line":152,"address":[628736,628949],"length":1,"stats":{"Line":5}},{"line":159,"address":[],"length":0,"stats":{"Line":10}},{"line":160,"address":[1678564,1679012,1678788,1679236],"length":1,"stats":{"Line":5}},{"line":165,"address":[629408,628855,629442,629420],"length":1,"stats":{"Line":0}},{"line":166,"address":[1686848,1686981,1687104,1687109,1686853,1687232,1686976,1687237],"length":1,"stats":{"Line":0}},{"line":170,"address":[1678607,1678831,1679055,1679279],"length":1,"stats":{"Line":0}},{"line":173,"address":[],"length":0,"stats":{"Line":0}},{"line":175,"address":[1679321,1678649,1678873,1679097],"length":1,"stats":{"Line":0}},{"line":179,"address":[1674495,1675072,1674863,1674512,1673952,1674144,1674127,1674336,1674319,1674671,1674688,1675055,1675247,1674880],"length":1,"stats":{"Line":7}},{"line":186,"address":[],"length":0,"stats":{"Line":7}},{"line":189,"address":[1683708,1674225,1683829,1683957,1683730,1674593,1684384,1684229,1683685,1683968,1683564,1674961,1683824,1684252,1684274,1684512,1684517,1683840,1683977,1683849,1674769,1683586,1683680,1683952,1684085,1684080,1684096,1684396,1684373,1684368,1683552,1674033,1683696,1684004,1684108,1683876,1684130,1675153,1674417,1684224,1684240,1684418],"length":1,"stats":{"Line":21}},{"line":192,"address":[],"length":0,"stats":{"Line":7}},{"line":195,"address":[],"length":0,"stats":{"Line":7}},{"line":197,"address":[],"length":0,"stats":{"Line":0}},{"line":201,"address":[1677771,1677584,1677793,1678443,1678017,1678256,1678219,1678032,1677808,1677995,1678241,1678465],"length":1,"stats":{"Line":4}},{"line":208,"address":[1678046,1677822,1678270,1678334,1677598,1677662,1677886,1678110],"length":1,"stats":{"Line":8}},{"line":209,"address":[],"length":0,"stats":{"Line":4}},{"line":213,"address":[1678133,1686592,1686720,1677909,1677685,1686464,1686492,1686341,1686224,1678357,1686480,1686336,1686352,1686469,1686597,1686620,1686364,1686386,1686642,1686514,1686608,1686236,1686725,1686258],"length":1,"stats":{"Line":0}},{"line":216,"address":[],"length":0,"stats":{"Line":0}},{"line":219,"address":[],"length":0,"stats":{"Line":0}},{"line":221,"address":[],"length":0,"stats":{"Line":0}},{"line":226,"address":[998032],"length":1,"stats":{"Line":7}},{"line":228,"address":[1006544],"length":1,"stats":{"Line":8}},{"line":230,"address":[946546,946524],"length":1,"stats":{"Line":16}},{"line":231,"address":[989654],"length":1,"stats":{"Line":6}},{"line":235,"address":[978830],"length":1,"stats":{"Line":12}},{"line":240,"address":[937952],"length":1,"stats":{"Line":6}},{"line":242,"address":[1689328],"length":1,"stats":{"Line":6}},{"line":244,"address":[981180,981202],"length":1,"stats":{"Line":12}},{"line":245,"address":[1006470],"length":1,"stats":{"Line":5}},{"line":249,"address":[948222],"length":1,"stats":{"Line":10}}],"covered":77,"coverable":89},{"path":["/","home","botahamec","Projects","happylock","src","collection.rs"],"content":"use std::cell::UnsafeCell;\n\nuse crate::{lockable::RawLock, ThreadKey};\n\nmod boxed;\nmod guard;\nmod owned;\nmod r#ref;\nmod retry;\npub(crate) mod utils;\n\n/// Locks a collection of locks, which cannot be shared immutably.\n///\n/// This could be a tuple of [`Lockable`] types, an array, or a `Vec`. But it\n/// can be safely locked without causing a deadlock.\n///\n/// The data in this collection is guaranteed to not contain duplicates because\n/// `L` must always implement [`OwnedLockable`]. The underlying data may not be\n/// immutably referenced. Because of this, there is no need for sorting the\n/// locks in the collection, or checking for duplicates, because it can be\n/// guaranteed that until the underlying collection is mutated (which requires\n/// releasing all acquired locks in the collection to do), then the locks will\n/// stay in the same order and be locked in that order, preventing 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\tchild: 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 cached\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\tchild: \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 cached\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\tchild: *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 is not 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 cyclic wait is ensured manually. This will prevent the need for\n/// subsequent unlocking and re-locking.\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\tchild: L,\n}\n\n/// A RAII guard for a generic [`Lockable`] type. When this structure is\n/// dropped (falls out of scope), the locks will be unlocked.\n///\n/// The data protected by the mutex can be accessed through this guard via its\n/// [`Deref`] and [`DerefMut`] implementations.\n///\n/// Several lock collections can be used to create this type. Specifically,\n/// [`BoxedLockCollection`], [`RefLockCollection`], [`OwnedLockCollection`], and\n/// [`RetryingLockCollection`]. It is created using the methods, `lock`,\n/// `try_lock`, `read`, and `try_read`.\n///\n/// [`Deref`]: `std::ops::Deref`\n/// [`DerefMut`]: `std::ops::DerefMut`\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","context","context.rs"],"content":"use std::marker::PhantomData;\n\nuse crate::{\n\tcontext::{LockContext, LockingIterator, LockingTuple},\n\tlockable::{Lockable, OwnedLockable},\n\tThreadKey,\n};\n\nimpl\u003c'l, L\u003e LockContext\u003c'l, L\u003e {\n\tpub(crate) const fn new(lockable: \u0026'l L) -\u003e Self\n\twhere\n\t\tL: OwnedLockable,\n\t{\n\t\tSelf {\n\t\t\tkey: None,\n\t\t\tlockable,\n\t\t}\n\t}\n\n\t/// Unlocks all locks in the collection, returning the [`ThreadKey`].\n\t///\n\t/// This requires a mutable reference to the context, so it cannot be called\n\t/// without first dropping any [`ContextGuard`]s that reference this context.\n\t/// This method will also return `None` if the context has not been locked\n\t/// with a `ThreadKey`.\n\t///\n\t/// # Example\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(42), Mutex::new(true));\n\t/// let locks = OwnedLockCollection::new(data);\n\t/// let mut ctx = locks.context();\n\t/// let tuple = ctx.tuple(key);\n\t///\n\t/// let (use_other, tuple) = tuple.lock_1();\n\t/// if **use_other {\n\t/// drop(use_other);\n\t/// drop(tuple);\n\t/// let key = ctx.unlock().unwrap();\n\t/// let tuple = ctx.tuple(key);\n\t/// let (mut item, _) = tuple.lock_0();\n\t/// **item = 67;\n\t/// } else {\n\t/// drop(use_other);\n\t/// drop(tuple);\n\t/// };\n\t///\n\t/// let key = ctx.unlock().unwrap();\n\t/// let tuple = ctx.tuple(key);\n\t/// let (number, _) = tuple.lock_0();\n\t/// assert_eq!(**number, 67);\n\t/// ```\n\t///\n\t/// [`ContextGuard`]: `crate::context::ContextGuard`\n\tpub fn unlock(\u0026mut self) -\u003e Option\u003cThreadKey\u003e {\n\t\tself.key.take()\n\t}\n}\n\nimpl\u003cL: Lockable\u003e LockContext\u003c'_, L\u003e {\n\t/// Creates a [`LockingTuple`], which can lock a subset of a tuple of locks,\n\t/// in a specific order.\n\t///\n\t/// Sometimes, partial allocation of locks is useful. For example, you may want\n\t/// to acquire a lock on one item before deciding if the second item should be\n\t/// locked. If the locks can be organized into a tuple, [`LockingTuple`] is\n\t/// capable of doing exactly that.\n\t///\n\t/// # Example\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(true), Mutex::new(42), Mutex::new(67));\n\t/// let locks = OwnedLockCollection::new(data);\n\t/// let mut ctx = locks.context();\n\t/// let tuple = ctx.tuple(key);\n\t///\n\t/// let (use_other, tuple) = tuple.lock_0();\n\t/// let number = if **use_other {\n\t/// tuple.lock_2().0\n\t/// } else {\n\t/// tuple.lock_1().0\n\t/// };\n\t/// assert_eq!(**number, 67);\n\t/// ```\n\tpub fn tuple(\u0026mut self, key: ThreadKey) -\u003e LockingTuple\u003c'_, L, L\u003e {\n\t\tunsafe {\n\t\t\tself.key = Some(key);\n\n\t\t\tLockingTuple {\n\t\t\t\t_lockable: PhantomData,\n\t\t\t\t// safety: we just inserted a key\n\t\t\t\tkey: self.key.as_ref().unwrap_unchecked(),\n\t\t\t\ttuple: self.lockable,\n\t\t\t\touter: (),\n\t\t\t}\n\t\t}\n\t}\n}\n\nimpl\u003c'l, L\u003e LockContext\u003c'l, L\u003e\nwhere\n\t\u0026'l L: IntoIterator,\n{\n\t/// Creates a [`LockingIterator`] to iterate through a collection of locks\n\t/// without locking everything at once.\n\t///\n\t/// Sometimes, partial allocation of locks is useful. For example, you may\n\t/// want to acquire a lock on the first element of a list before deciding if\n\t/// the second element should be locked. If the list is iterable, then a\n\t/// [`LockingIterator`] is capable of doing exactly that.\n\t///\n\t/// # Example\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(1), Mutex::new(3), Mutex::new(8)];\n\t/// let locks = OwnedLockCollection::new(data);\n\t/// let mut ctx = locks.context();\n\t/// let mut iter = ctx.iter(key);\n\t///\n\t/// let mut sum = 0;\n\t/// while let Some(item) = iter.lock_next() {\n\t/// sum += **item;\n\t/// }\n\t///\n\t/// assert_eq!(sum, 12);\n\t/// ```\n\t// TODO: support scoped locks\n\t// TODO: implement get_disjoint\n\t// TODO: support some sort of index tower thing\n\t#[expect(clippy::iter_not_returning_iterator)]\n\tpub fn iter(\n\t\t\u0026mut self,\n\t\tkey: ThreadKey,\n\t) -\u003e LockingIterator\u003c'_, \u003c\u0026'l L as IntoIterator\u003e::IntoIter\u003e {\n\t\tunsafe {\n\t\t\tself.key = Some(key);\n\n\t\t\tLockingIterator {\n\t\t\t\t// safety: we just inserted a key\n\t\t\t\tkey: self.key.as_ref().unwrap_unchecked(),\n\t\t\t\titerator: self.lockable.into_iter(),\n\t\t\t\touter: (),\n\t\t\t}\n\t\t}\n\t}\n}\n","traces":[{"line":10,"address":[918960,919040,918992,919024,918944,918928,918976,919056,919008,919072],"length":1,"stats":{"Line":12}},{"line":59,"address":[],"length":0,"stats":{"Line":0}},{"line":60,"address":[],"length":0,"stats":{"Line":0}},{"line":93,"address":[],"length":0,"stats":{"Line":5}},{"line":95,"address":[],"length":0,"stats":{"Line":10}},{"line":100,"address":[],"length":0,"stats":{"Line":5}},{"line":101,"address":[],"length":0,"stats":{"Line":5}},{"line":102,"address":[],"length":0,"stats":{"Line":0}},{"line":143,"address":[920192,919892,919840,919664,919540,919488,919716,920016,920068,920244],"length":1,"stats":{"Line":7}},{"line":148,"address":[],"length":0,"stats":{"Line":14}},{"line":152,"address":[],"length":0,"stats":{"Line":7}},{"line":153,"address":[],"length":0,"stats":{"Line":7}},{"line":154,"address":[],"length":0,"stats":{"Line":0}}],"covered":9,"coverable":13},{"path":["/","home","botahamec","Projects","happylock","src","context","guard.rs"],"content":"use std::fmt::{Debug, Display};\nuse std::hash::Hash;\nuse std::ops::{Deref, DerefMut};\n\nuse super::ContextGuard;\n\n#[mutants::skip] // hashing involves RNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard: Hash, Key\u003e Hash for ContextGuard\u003c'_, Guard, Key\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.guard.hash(state)\n\t}\n}\n\n// No implementations of Eq, PartialEq, PartialOrd, or Ord\n// You can't implement both PartialEq\u003cSelf\u003e and PartialEq\u003cT\u003e\n// It's easier to just implement neither and ask users to dereference\n// This is less of a problem when using the scoped lock API\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard: Debug, Key\u003e Debug for ContextGuard\u003c'_, Guard, Key\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, Key\u003e Display for ContextGuard\u003c'_, Guard, Key\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, Key\u003e Deref for ContextGuard\u003c'_, Guard, Key\u003e {\n\ttype Target = Guard;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t\u0026self.guard\n\t}\n}\n\nimpl\u003cGuard, Key\u003e DerefMut for ContextGuard\u003c'_, Guard, Key\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t\u0026mut self.guard\n\t}\n}\n\nimpl\u003cGuard, Key\u003e AsRef\u003cGuard\u003e for ContextGuard\u003c'_, Guard, Key\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026Guard {\n\t\t\u0026self.guard\n\t}\n}\n\nimpl\u003cGuard, Key\u003e AsMut\u003cGuard\u003e for ContextGuard\u003c'_, Guard, Key\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut Guard {\n\t\t\u0026mut self.guard\n\t}\n}\n","traces":[{"line":29,"address":[],"length":0,"stats":{"Line":1}},{"line":30,"address":[],"length":0,"stats":{"Line":1}},{"line":37,"address":[],"length":0,"stats":{"Line":5}},{"line":38,"address":[],"length":0,"stats":{"Line":5}},{"line":43,"address":[],"length":0,"stats":{"Line":1}},{"line":44,"address":[],"length":0,"stats":{"Line":1}},{"line":49,"address":[],"length":0,"stats":{"Line":0}},{"line":50,"address":[],"length":0,"stats":{"Line":0}},{"line":55,"address":[],"length":0,"stats":{"Line":0}},{"line":56,"address":[],"length":0,"stats":{"Line":0}}],"covered":6,"coverable":10},{"path":["/","home","botahamec","Projects","happylock","src","context","iterator.rs"],"content":"use std::{\n\titer::{Fuse, Peekable, Skip, Take},\n\tmarker::PhantomData,\n};\n\nuse super::{ContextGuard, LockingIterator};\n\nuse crate::{\n\tcontext::LockingTuple,\n\tlockable::{Lockable, RawLock, Sharable},\n\tThreadKey,\n};\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum TryLockNextError {\n\tFinishedIteration,\n\tWouldBlock,\n}\n\nimpl\u003c'l, I, O\u003e LockingIterator\u003c'l, I, O\u003e {\n\tfn with_iterator\u003cM\u003e(self, f: impl FnOnce(I) -\u003e M) -\u003e LockingIterator\u003c'l, M, O\u003e {\n\t\tLockingIterator {\n\t\t\tkey: self.key,\n\t\t\titerator: f(self.iterator),\n\t\t\touter: self.outer,\n\t\t}\n\t}\n\n\t/// Exit out of the current scope of the locking iterator into the parent.\n\t///\n\t/// After using one the recurse methods, it is possible to regain access to\n\t/// the parent by exiting out of the scope of the child. Doing this will make\n\t/// it impossible to re-enter this scope again.\n\t///\n\t/// # Example\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(1), Mutex::new(2), Mutex::new(3)], Mutex::new(true));\n\t/// let locks = OwnedLockCollection::new(data);\n\t/// let mut ctx = locks.context();\n\t/// let tuple = ctx.tuple(key);\n\t/// let mut iter = tuple.recurse_0_iter();\n\t///\n\t/// let mut sum = 0;\n\t/// while let Some(item) = iter.lock_next() {\n\t/// sum += **item;\n\t/// }\n\t///\n\t/// let tuple = iter.exit();\n\t/// let (should_assert, _) = tuple.lock_1();\n\t/// if **should_assert {\n\t/// assert_eq!(sum, 6);\n\t/// }\n\t/// ```\n\tpub fn exit(self) -\u003e O {\n\t\tself.outer\n\t}\n}\n\nimpl\u003c'c, L: Iterator\u003cItem = I\u003e, I: IntoIterator, O\u003e LockingIterator\u003c'c, L, O\u003e {\n\t/// Create a new `LockingIterator` based on the next element in the iterator.\n\t///\n\t/// If a list contains a list of locks, then this method can be used to\n\t/// recurse into the next element of the list. To go back to the parent scope,\n\t/// use [`LockingIterator::exit`] on the new list.\n\t///\n\t/// # Example\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 = [\n\t/// [Mutex::new(1), Mutex::new(2), Mutex::new(3)],\n\t/// [Mutex::new(4), Mutex::new(5), Mutex::new(6)],\n\t/// ];\n\t/// let locks = OwnedLockCollection::new(data);\n\t/// let mut ctx = locks.context();\n\t/// let mut iter = ctx.iter(key);\n\t///\n\t/// let mut sums = Vec::new();\n\t/// while let Some(mut list) = iter.recurse_next() {\n\t/// let mut sum = 0;\n\t/// while let Some(item) = list.lock_next() {\n\t/// sum += **item;\n\t/// }\n\t/// sums.push(sum);\n\t/// iter = list.exit();\n\t/// }\n\t///\n\t/// assert_eq!(sums, vec![6, 15]);\n\t/// ```\n\tpub fn recurse_next(\n\t\tmut self,\n\t) -\u003e Option\u003cLockingIterator\u003c'c, \u003cI as IntoIterator\u003e::IntoIter, Self\u003e\u003e {\n\t\tif let Some(iterator) = self.iterator.next() {\n\t\t\tSome(LockingIterator {\n\t\t\t\tkey: self.key,\n\t\t\t\titerator: iterator.into_iter(),\n\t\t\t\touter: self,\n\t\t\t})\n\t\t} else {\n\t\t\tNone\n\t\t}\n\t}\n\n\t/// Create a new `LockingIterator` based on the next element in the iterator.\n\t///\n\t/// If a list contains a list of locks, then this method can be used to\n\t/// recurse into the next element of the list. To go back to the parent scope,\n\t/// use [`LockingIterator::exit`] on the new list.\n\t///\n\t/// # Example\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 = [\n\t/// [Mutex::new(1), Mutex::new(2), Mutex::new(3)],\n\t/// [Mutex::new(4), Mutex::new(5), Mutex::new(6)],\n\t/// ];\n\t/// let locks = OwnedLockCollection::new(data);\n\t/// let mut ctx = locks.context();\n\t/// let iter = ctx.iter(key);\n\t///\n\t/// let mut list = iter.recurse_last().unwrap();\n\t/// let mut sum = 0;\n\t/// while let Some(item) = list.lock_next() {\n\t/// sum += **item;\n\t/// }\n\t///\n\t/// assert_eq!(sum, 15);\n\t/// ```\n\tpub fn recurse_last(self) -\u003e Option\u003cLockingIterator\u003c'c, \u003cI as IntoIterator\u003e::IntoIter, O\u003e\u003e {\n\t\tif let Some(iterator) = self.iterator.last() {\n\t\t\tSome(LockingIterator {\n\t\t\t\tkey: self.key,\n\t\t\t\titerator: iterator.into_iter(),\n\t\t\t\touter: self.outer,\n\t\t\t})\n\t\t} else {\n\t\t\tNone\n\t\t}\n\t}\n}\n\nimpl\u003c'c, L: Iterator\u003cItem = \u0026'c T\u003e, T: 'c, O\u003e LockingIterator\u003c'c, L, O\u003e {\n\t/// Create a new `LockingIterator` based on the next element in the iterator.\n\t///\n\t/// If a list contains a list of locks, then this method can be used to\n\t/// recurse into the next element of the list. To go back to the parent scope,\n\t/// use [`LockingIterator::exit`] on the new list.\n\t///\n\t/// # Example\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 = [\n\t/// (Mutex::new(true), Mutex::new(1)),\n\t/// (Mutex::new(false), Mutex::new(2)),\n\t/// (Mutex::new(true), Mutex::new(3)),\n\t/// ];\n\t/// let locks = OwnedLockCollection::new(data);\n\t/// let mut ctx = locks.context();\n\t/// let mut iter = ctx.iter(key);\n\t///\n\t/// let mut sum = 0;\n\t/// while let Some(tuple) = iter.recurse_next_tuple() {\n\t/// let (should_count, mut tuple) = tuple.lock_0();\n\t/// if **should_count {\n\t/// let num = tuple.lock_mut_1();\n\t/// sum += **num;\n\t/// }\n\t/// iter = tuple.exit();\n\t/// }\n\t///\n\t/// assert_eq!(sum, 4);\n\t/// ```\n\tpub fn recurse_next_tuple(mut self) -\u003e Option\u003cLockingTuple\u003c'c, T, T, Self\u003e\u003e {\n\t\tif let Some(tuple) = self.iterator.next() {\n\t\t\tSome(LockingTuple {\n\t\t\t\tkey: self.key,\n\t\t\t\t_lockable: PhantomData,\n\t\t\t\ttuple,\n\t\t\t\touter: self,\n\t\t\t})\n\t\t} else {\n\t\t\tNone\n\t\t}\n\t}\n\n\t/// Create a new `LockingIterator` based on the next element in the iterator.\n\t///\n\t/// If a list contains a list of locks, then this method can be used to\n\t/// recurse into the next element of the list. To go back to the parent scope,\n\t/// use [`LockingIterator::exit`] on the new list.\n\t///\n\t/// # Example\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 = [\n\t/// (Mutex::new(true), Mutex::new(1)),\n\t/// (Mutex::new(false), Mutex::new(2)),\n\t/// (Mutex::new(true), Mutex::new(3)),\n\t/// ];\n\t/// let locks = OwnedLockCollection::new(data);\n\t/// let mut ctx = locks.context();\n\t/// let iter = ctx.iter(key);\n\t///\n\t/// let tuple = iter.recurse_last_tuple().unwrap();\n\t/// let (should_count, mut tuple) = tuple.lock_0();\n\t/// if **should_count {\n\t/// let num = tuple.lock_mut_1();\n\t/// assert_eq!(**num, 3);\n\t/// } else {\n\t/// panic!();\n\t/// }\n\t/// ```\n\tpub fn recurse_last_tuple(self) -\u003e Option\u003cLockingTuple\u003c'c, T, T, O\u003e\u003e {\n\t\tif let Some(tuple) = self.iterator.last() {\n\t\t\tSome(LockingTuple {\n\t\t\t\tkey: self.key,\n\t\t\t\t_lockable: PhantomData,\n\t\t\t\ttuple,\n\t\t\t\touter: self.outer,\n\t\t\t})\n\t\t} else {\n\t\t\tNone\n\t\t}\n\t}\n}\n\nimpl\u003c'c, L: 'c + Iterator\u003cItem = \u0026'c I\u003e, I: 'c + RawLock + Lockable, O\u003e LockingIterator\u003c'c, L, O\u003e {\n\t/// Advances the iterator, locking the next element and returning a guard to\n\t/// the inner data.\n\t///\n\t/// Returns `None` when iteration is finished. Individual iterator\n\t/// implementations may choose to resume iteration, and so calling `next()`\n\t/// again may or may not eventually start returning `Some(Item)` again at some\n\t/// point.\n\t///\n\t/// # Example\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(1), Mutex::new(3), Mutex::new(8)];\n\t/// let locks = OwnedLockCollection::new(data);\n\t/// let mut ctx = locks.context();\n\t/// let mut iter = ctx.iter(key);\n\t///\n\t/// let mut sum = 0;\n\t/// while let Some(item) = iter.lock_next() {\n\t/// sum += **item;\n\t/// }\n\t///\n\t/// assert_eq!(sum, 12);\n\t/// ```\n\tpub fn lock_next(\u0026mut self) -\u003e Option\u003cContextGuard\u003c'c, \u003cI as Lockable\u003e::Guard\u003c'c\u003e, ThreadKey\u003e\u003e {\n\t\tif let Some(lock) = self.iterator.next() {\n\t\t\tunsafe {\n\t\t\t\tlock.raw_write();\n\t\t\t\tlet guard = lock.guard();\n\n\t\t\t\tSome(ContextGuard {\n\t\t\t\t\t_key: self.key,\n\t\t\t\t\tguard,\n\t\t\t\t})\n\t\t\t}\n\t\t} else {\n\t\t\tNone\n\t\t}\n\t}\n\n\t/// Consumes the iterator, returning the last element, without locking any\n\t/// other elements.\n\t///\n\t/// This method will evaluate the iterator until it returns `None`. While\n\t/// doing so, it keeps track of the current element. After `None` is returned,\n\t/// `lock_last()` will then lock the last element it saw and return the\n\t/// lock's data.\n\t///\n\t/// # Panics\n\t///\n\t/// This function might panic if the iterator is infinite.\n\t///\n\t/// # Example\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(1), Mutex::new(3), Mutex::new(8)];\n\t/// let locks = OwnedLockCollection::new(data);\n\t/// let mut ctx = locks.context();\n\t/// let iter = ctx.iter(key);\n\t///\n\t/// let last = iter.lock_last().unwrap();\n\t/// assert_eq!(**last, 8);\n\t/// ```\n\tpub fn lock_last(self) -\u003e Option\u003cContextGuard\u003c'c, \u003cI as Lockable\u003e::Guard\u003c'c\u003e, ThreadKey\u003e\u003e {\n\t\tself.iterator.last().map(|lock| unsafe {\n\t\t\tlock.raw_write();\n\t\t\tlet guard = lock.guard();\n\n\t\t\tContextGuard {\n\t\t\t\t_key: self.key,\n\t\t\t\tguard,\n\t\t\t}\n\t\t})\n\t}\n}\n\nimpl\u003c'c, L: 'c + Iterator\u003cItem = \u0026'c I\u003e, I: 'c + RawLock + Lockable, O\u003e\n\tLockingIterator\u003c'c, Peekable\u003cL\u003e, O\u003e\n{\n\t/// Attempts to lock the next element and returning a guard to\n\t/// the inner data.\n\t///\n\t/// # Errors\n\t///\n\t/// Returns `Err(TryLockNextError::FinishedIteration)` when iteration is\n\t/// finished. Individual iterator implementations may choose to resume\n\t/// iteration, and so calling `next()` again may or may not eventually start\n\t/// returning `Some(Item)` again at some point.\n\t///\n\t/// Returns `Err(TryLockNextError::WouldBlock)` if the next lock in the\n\t/// iterator is already locked. This will not advance the iterator.\n\t///\n\t/// # Example\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t/// use happylock::context::iterator::TryLockNextError;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = [Mutex::new(1), Mutex::new(3), Mutex::new(8)];\n\t/// let locks = OwnedLockCollection::new(data);\n\t/// let mut ctx = locks.context();\n\t/// let mut iter = ctx.iter(key).peekable();\n\t///\n\t/// let mut sum = 0;\n\t/// loop {\n\t/// match iter.try_lock_next() {\n\t/// Ok(item) =\u003e sum += **item,\n\t/// Err(TryLockNextError::WouldBlock) =\u003e continue,\n\t/// Err(TryLockNextError::FinishedIteration) =\u003e break,\n\t/// }\n\t/// }\n\t///\n\t/// assert_eq!(sum, 12);\n\t/// ```\n\tpub fn try_lock_next(\n\t\t\u0026mut self,\n\t) -\u003e Result\u003cContextGuard\u003c'c, \u003cI as Lockable\u003e::Guard\u003c'c\u003e, ThreadKey\u003e, TryLockNextError\u003e {\n\t\tif let Some(lock) = self.iterator.peek().copied() {\n\t\t\tunsafe {\n\t\t\t\tif lock.raw_try_write() {\n\t\t\t\t\t// safety: we just saw that there is a valid value\n\t\t\t\t\tlet lock = self.iterator.next().unwrap_unchecked();\n\t\t\t\t\tlet guard = lock.guard();\n\n\t\t\t\t\tOk(ContextGuard {\n\t\t\t\t\t\t_key: self.key,\n\t\t\t\t\t\tguard,\n\t\t\t\t\t})\n\t\t\t\t} else {\n\t\t\t\t\tErr(TryLockNextError::WouldBlock)\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tErr(TryLockNextError::FinishedIteration)\n\t\t}\n\t}\n}\n\nimpl\u003c'c, L: 'c + Iterator\u003cItem = \u0026'c I\u003e, I: 'c + RawLock + Sharable, O\u003e LockingIterator\u003c'c, L, O\u003e {\n\t/// Advances the iterator, acquiring a shared lock to the next element and\n\t/// returning a guard to the inner data.\n\t///\n\t/// Returns `None` when iteration is finished. Individual iterator\n\t/// implementations may choose to resume iteration, and so calling `next()`\n\t/// again may or may not eventually start returning `Some(Item)` again at some\n\t/// point.\n\t///\n\t/// # Example\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(1), RwLock::new(3), RwLock::new(8)];\n\t/// let locks = OwnedLockCollection::new(data);\n\t/// let mut ctx = locks.context();\n\t/// let mut iter = ctx.iter(key);\n\t///\n\t/// let mut sum = 0;\n\t/// while let Some(item) = iter.read_next() {\n\t/// sum += **item;\n\t/// }\n\t///\n\t/// assert_eq!(sum, 12);\n\t/// ```\n\tpub fn read_next(\n\t\t\u0026mut self,\n\t) -\u003e Option\u003cContextGuard\u003c'c, \u003cI as Sharable\u003e::ReadGuard\u003c'c\u003e, ThreadKey\u003e\u003e {\n\t\tif let Some(lock) = self.iterator.next() {\n\t\t\tunsafe {\n\t\t\t\tlock.raw_read();\n\t\t\t\tlet guard = lock.read_guard();\n\n\t\t\t\tSome(ContextGuard {\n\t\t\t\t\t_key: self.key,\n\t\t\t\t\tguard,\n\t\t\t\t})\n\t\t\t}\n\t\t} else {\n\t\t\tNone\n\t\t}\n\t}\n\n\t/// Consumes the iterator, returning the last element with readonly access,\n\t/// without locking any other elements.\n\t///\n\t/// This method will evaluate the iterator until it returns `None`. While\n\t/// doing so, it keeps track of the current element. After `None` is returned,\n\t/// `lock_last()` will then lock the last element it saw and return the\n\t/// lock's data.\n\t///\n\t/// # Panics\n\t///\n\t/// This function might panic if the iterator is infinite.\n\t///\n\t/// # Example\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(1), RwLock::new(3), RwLock::new(8)];\n\t/// let locks = OwnedLockCollection::new(data);\n\t/// let mut ctx = locks.context();\n\t/// let iter = ctx.iter(key);\n\t///\n\t/// let last = iter.read_last().unwrap();\n\t/// assert_eq!(**last, 8);\n\t/// ```\n\tpub fn read_last(self) -\u003e Option\u003cContextGuard\u003c'c, \u003cI as Sharable\u003e::ReadGuard\u003c'c\u003e, ThreadKey\u003e\u003e {\n\t\tself.iterator.last().map(|lock| unsafe {\n\t\t\tlock.raw_read();\n\t\t\tlet guard = lock.read_guard();\n\n\t\t\tContextGuard {\n\t\t\t\t_key: self.key,\n\t\t\t\tguard,\n\t\t\t}\n\t\t})\n\t}\n}\n\nimpl\u003c'c, L: 'c + Iterator\u003cItem = \u0026'c I\u003e, I: 'c + RawLock + Sharable, O\u003e\n\tLockingIterator\u003c'c, Peekable\u003cL\u003e, O\u003e\n{\n\t/// Attempts to acquire a shared lock the next element and returning a guard\n\t/// to the inner data.\n\t///\n\t/// # Errors\n\t///\n\t/// Returns `Err(TryLockNextError::FinishedIteration)` when iteration is\n\t/// finished. Individual iterator implementations may choose to resume\n\t/// iteration, and so calling `next()` again may or may not eventually start\n\t/// returning `Some(Item)` again at some point.\n\t///\n\t/// Returns `Err(TryLockNextError::WouldBlock)` if the next lock in the\n\t/// iterator is already locked. This will not advance the iterator.\n\t///\n\t/// # Example\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t/// use happylock::context::iterator::TryLockNextError;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = [RwLock::new(1), RwLock::new(3), RwLock::new(8)];\n\t/// let locks = OwnedLockCollection::new(data);\n\t/// let mut ctx = locks.context();\n\t/// let mut iter = ctx.iter(key).peekable();\n\t///\n\t/// let mut sum = 0;\n\t/// loop {\n\t/// match iter.try_read_next() {\n\t/// Ok(item) =\u003e sum += **item,\n\t/// Err(TryLockNextError::WouldBlock) =\u003e continue,\n\t/// Err(TryLockNextError::FinishedIteration) =\u003e break,\n\t/// }\n\t/// }\n\t///\n\t/// assert_eq!(sum, 12);\n\t/// ```\n\tpub fn try_read_next(\n\t\t\u0026mut self,\n\t) -\u003e Result\u003cContextGuard\u003c'c, \u003cI as Sharable\u003e::ReadGuard\u003c'c\u003e, ThreadKey\u003e, TryLockNextError\u003e {\n\t\tif let Some(lock) = self.iterator.peek().copied() {\n\t\t\tunsafe {\n\t\t\t\tif lock.raw_try_read() {\n\t\t\t\t\t// safety: we just saw that there is a valid value\n\t\t\t\t\tlet lock = self.iterator.next().unwrap_unchecked();\n\t\t\t\t\tlet guard = lock.read_guard();\n\n\t\t\t\t\tOk(ContextGuard {\n\t\t\t\t\t\t_key: self.key,\n\t\t\t\t\t\tguard,\n\t\t\t\t\t})\n\t\t\t\t} else {\n\t\t\t\t\tErr(TryLockNextError::WouldBlock)\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tErr(TryLockNextError::FinishedIteration)\n\t\t}\n\t}\n}\n\nimpl\u003c'l, L: Iterator, O\u003e LockingIterator\u003c'l, L, O\u003e {\n\t/// Advances the iterator, without locking the next element in the iterator.\n\t///\n\t/// Returns `false` when iteration is finished. Individual iterator\n\t/// implementations may choose to resume iteration, and so calling\n\t/// `skip_next()` again may or may not eventually start returning `true` again\n\t/// at some point.\n\t///\n\t/// # Example\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(1), Mutex::new(3), Mutex::new(8)];\n\t/// let locks = OwnedLockCollection::new(data);\n\t/// let mut ctx = locks.context();\n\t/// let mut iter = ctx.iter(key);\n\t///\n\t/// iter.skip_next();\n\t/// assert!(iter.lock_next().is_some_and(|v| **v == 3));\n\t/// ```\n\tpub fn skip_next(\u0026mut self) -\u003e bool {\n\t\tself.iterator.next().is_some()\n\t}\n\n\t/// Advances the iterator, skipping `n` elements without locking.\n\t///\n\t/// See [`Iterator::skip`] for more information.\n\t///\n\t/// # Example\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(1), Mutex::new(3), Mutex::new(8)];\n\t/// let locks = OwnedLockCollection::new(data);\n\t/// let mut ctx = locks.context();\n\t/// let mut iter = ctx.iter(key);\n\t///\n\t/// iter.skip_mut(2);\n\t/// assert!(iter.lock_next().is_some_and(|v| **v == 8));\n\t/// ```\n\tpub fn skip_mut(\u0026mut self, n: usize) {\n\t\tfor _ in 0..n {\n\t\t\tself.iterator.next();\n\t\t}\n\t}\n\n\t/// Returns the bounds on the remaining length of the iterator.\n\t///\n\t/// See [`Iterator::size_hint`] for more information.\n\t///\n\t/// # Example\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(1), Mutex::new(2), Mutex::new(3)];\n\t/// let locks = OwnedLockCollection::new(data);\n\t/// let mut ctx = locks.context();\n\t/// let mut iter = ctx.iter(key);\n\t///\n\t/// assert_eq!((3, Some(3)), iter.size_hint());\n\t/// let _ = iter.skip_next();\n\t/// assert_eq!((2, Some(2)), iter.size_hint());\n\t/// ```\n\t#[must_use]\n\tpub fn size_hint(\u0026self) -\u003e (usize, Option\u003cusize\u003e) {\n\t\tself.iterator.size_hint()\n\t}\n\n\t/// Creates a new [`LockingIterator`] that skips the first `n` elements.\n\t///\n\t/// Unlike `skip_next` or `skip_mut`, this method does not modify the iterator\n\t/// in place. Instead, it returns a new iterator which skips the first `n`\n\t/// elements.\n\t///\n\t/// See [`Iterator::skip`] for more information.\n\t///\n\t/// # Example\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(1), Mutex::new(2), Mutex::new(3)];\n\t/// let locks = OwnedLockCollection::new(data);\n\t/// let mut ctx = locks.context();\n\t/// let mut iter = ctx.iter(key).skip(2);\n\t///\n\t/// assert!(iter.lock_next().is_some_and(|v| **v == 3));\n\t/// assert!(iter.lock_next().is_none());\n\t/// ```\n\t#[must_use]\n\tpub fn skip(self, n: usize) -\u003e LockingIterator\u003c'l, Skip\u003cL\u003e, O\u003e {\n\t\tself.with_iterator(|i| i.skip(n))\n\t}\n\n\t/// Creates a new [`LockingIterator`] that yields only the first `n` elements,\n\t/// or fewer if the iterator ends sooner.\n\t///\n\t/// See [`Iterator::take`] for more information.\n\t///\n\t/// # Example\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(1), Mutex::new(2), Mutex::new(3)];\n\t/// let locks = OwnedLockCollection::new(data);\n\t/// let mut ctx = locks.context();\n\t/// let mut iter = ctx.iter(key).take(2);\n\t///\n\t/// assert!(iter.lock_next().is_some_and(|v| **v == 1));\n\t/// assert!(iter.lock_next().is_some_and(|v| **v == 2));\n\t/// assert!(iter.lock_next().is_none());\n\t/// ```\n\t#[must_use]\n\tpub fn take(self, n: usize) -\u003e LockingIterator\u003c'l, Take\u003cL\u003e, O\u003e {\n\t\tself.with_iterator(|i| i.take(n))\n\t}\n\n\t/// Creates a new [`LockingIterator`] that ends after the first `None`\n\t///\n\t/// See [`Iterator::fuse`] for more information\n\t#[must_use]\n\tpub fn fuse(self) -\u003e LockingIterator\u003c'l, Fuse\u003cL\u003e, O\u003e {\n\t\tself.with_iterator(Iterator::fuse)\n\t}\n\n\t/// Creates a new [`LockingIterator`] which has access to the\n\t/// [`try_lock_next`] and/or [`try_read_next`] methods.\n\t///\n\t/// See [`Iterator::peekable`] for more information\n\t///\n\t/// [`try_lock_next`]: `LockingIterator::try_lock_next`\n\t/// [`try_read_next`]: `LockingIterator::try_read_next`\n\t#[must_use]\n\tpub fn peekable(self) -\u003e LockingIterator\u003c'l, Peekable\u003cL\u003e, O\u003e {\n\t\tself.with_iterator(Iterator::peekable)\n\t}\n}\n","traces":[{"line":21,"address":[918784,918829],"length":1,"stats":{"Line":1}},{"line":23,"address":[],"length":0,"stats":{"Line":1}},{"line":24,"address":[],"length":0,"stats":{"Line":1}},{"line":25,"address":[],"length":0,"stats":{"Line":0}},{"line":59,"address":[],"length":0,"stats":{"Line":1}},{"line":60,"address":[],"length":0,"stats":{"Line":2}},{"line":98,"address":[924640,924986],"length":1,"stats":{"Line":1}},{"line":101,"address":[],"length":0,"stats":{"Line":2}},{"line":102,"address":[],"length":0,"stats":{"Line":1}},{"line":103,"address":[924799],"length":1,"stats":{"Line":1}},{"line":104,"address":[],"length":0,"stats":{"Line":1}},{"line":105,"address":[],"length":0,"stats":{"Line":1}},{"line":108,"address":[],"length":0,"stats":{"Line":0}},{"line":141,"address":[],"length":0,"stats":{"Line":2}},{"line":142,"address":[],"length":0,"stats":{"Line":5}},{"line":143,"address":[924292,924564],"length":1,"stats":{"Line":1}},{"line":144,"address":[],"length":0,"stats":{"Line":1}},{"line":145,"address":[],"length":0,"stats":{"Line":1}},{"line":146,"address":[],"length":0,"stats":{"Line":1}},{"line":149,"address":[],"length":0,"stats":{"Line":1}},{"line":189,"address":[],"length":0,"stats":{"Line":1}},{"line":190,"address":[920572,920629,920773],"length":1,"stats":{"Line":3}},{"line":191,"address":[],"length":0,"stats":{"Line":1}},{"line":192,"address":[],"length":0,"stats":{"Line":1}},{"line":193,"address":[],"length":0,"stats":{"Line":0}},{"line":194,"address":[],"length":0,"stats":{"Line":0}},{"line":195,"address":[],"length":0,"stats":{"Line":1}},{"line":198,"address":[],"length":0,"stats":{"Line":1}},{"line":233,"address":[],"length":0,"stats":{"Line":1}},{"line":234,"address":[],"length":0,"stats":{"Line":3}},{"line":235,"address":[],"length":0,"stats":{"Line":1}},{"line":236,"address":[],"length":0,"stats":{"Line":1}},{"line":237,"address":[],"length":0,"stats":{"Line":0}},{"line":238,"address":[],"length":0,"stats":{"Line":0}},{"line":239,"address":[],"length":0,"stats":{"Line":1}},{"line":242,"address":[],"length":0,"stats":{"Line":1}},{"line":275,"address":[],"length":0,"stats":{"Line":3}},{"line":276,"address":[],"length":0,"stats":{"Line":6}},{"line":278,"address":[],"length":0,"stats":{"Line":3}},{"line":279,"address":[],"length":0,"stats":{"Line":3}},{"line":281,"address":[],"length":0,"stats":{"Line":3}},{"line":282,"address":[],"length":0,"stats":{"Line":3}},{"line":283,"address":[],"length":0,"stats":{"Line":0}},{"line":287,"address":[921279,921135,921423],"length":1,"stats":{"Line":3}},{"line":318,"address":[],"length":0,"stats":{"Line":1}},{"line":319,"address":[920980,920924],"length":1,"stats":{"Line":3}},{"line":320,"address":[1536691],"length":1,"stats":{"Line":1}},{"line":321,"address":[1536701],"length":1,"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":371,"address":[921456],"length":1,"stats":{"Line":1}},{"line":374,"address":[],"length":0,"stats":{"Line":2}},{"line":376,"address":[],"length":0,"stats":{"Line":2}},{"line":378,"address":[921596],"length":1,"stats":{"Line":1}},{"line":379,"address":[],"length":0,"stats":{"Line":1}},{"line":381,"address":[],"length":0,"stats":{"Line":1}},{"line":382,"address":[],"length":0,"stats":{"Line":1}},{"line":383,"address":[],"length":0,"stats":{"Line":0}},{"line":386,"address":[921578],"length":1,"stats":{"Line":1}},{"line":390,"address":[],"length":0,"stats":{"Line":1}},{"line":426,"address":[],"length":0,"stats":{"Line":0}},{"line":428,"address":[],"length":0,"stats":{"Line":0}},{"line":429,"address":[],"length":0,"stats":{"Line":0}},{"line":431,"address":[],"length":0,"stats":{"Line":0}},{"line":432,"address":[],"length":0,"stats":{"Line":0}},{"line":433,"address":[],"length":0,"stats":{"Line":0}},{"line":437,"address":[],"length":0,"stats":{"Line":0}},{"line":468,"address":[],"length":0,"stats":{"Line":0}},{"line":469,"address":[],"length":0,"stats":{"Line":0}},{"line":470,"address":[],"length":0,"stats":{"Line":0}},{"line":471,"address":[],"length":0,"stats":{"Line":0}},{"line":473,"address":[],"length":0,"stats":{"Line":0}},{"line":474,"address":[],"length":0,"stats":{"Line":0}},{"line":475,"address":[],"length":0,"stats":{"Line":0}},{"line":524,"address":[],"length":0,"stats":{"Line":0}},{"line":526,"address":[],"length":0,"stats":{"Line":0}},{"line":528,"address":[],"length":0,"stats":{"Line":0}},{"line":529,"address":[],"length":0,"stats":{"Line":0}},{"line":531,"address":[],"length":0,"stats":{"Line":0}},{"line":532,"address":[],"length":0,"stats":{"Line":0}},{"line":533,"address":[],"length":0,"stats":{"Line":0}},{"line":536,"address":[],"length":0,"stats":{"Line":0}},{"line":540,"address":[],"length":0,"stats":{"Line":0}},{"line":568,"address":[],"length":0,"stats":{"Line":1}},{"line":569,"address":[922841],"length":1,"stats":{"Line":1}},{"line":591,"address":[],"length":0,"stats":{"Line":1}},{"line":592,"address":[],"length":0,"stats":{"Line":2}},{"line":593,"address":[],"length":0,"stats":{"Line":1}},{"line":618,"address":[],"length":0,"stats":{"Line":0}},{"line":619,"address":[],"length":0,"stats":{"Line":0}},{"line":646,"address":[],"length":0,"stats":{"Line":0}},{"line":647,"address":[],"length":0,"stats":{"Line":0}},{"line":672,"address":[],"length":0,"stats":{"Line":0}},{"line":673,"address":[],"length":0,"stats":{"Line":0}},{"line":680,"address":[],"length":0,"stats":{"Line":0}},{"line":681,"address":[],"length":0,"stats":{"Line":0}},{"line":692,"address":[],"length":0,"stats":{"Line":1}},{"line":693,"address":[],"length":0,"stats":{"Line":1}}],"covered":57,"coverable":99},{"path":["/","home","botahamec","Projects","happylock","src","context","tuple.rs"],"content":"use std::marker::PhantomData;\n\nuse crate::{\n\tcontext::{ContextGuard, LockingIterator, LockingTuple},\n\tlockable::{Lockable, RawLock, Sharable},\n\tThreadKey,\n};\n\nimpl\u003c'c, A, B, O\u003e LockingTuple\u003c'c, A, B, O\u003e {\n\tfn transmute\u003cC\u003e(self) -\u003e LockingTuple\u003c'c, C, B, O\u003e {\n\t\tLockingTuple {\n\t\t\t_lockable: PhantomData,\n\t\t\tkey: self.key,\n\t\t\ttuple: self.tuple,\n\t\t\touter: self.outer,\n\t\t}\n\t}\n}\n\nmacro_rules! lock_impl {\n\t($self: expr, $field: tt) =\u003e {\n\t\tunsafe {\n\t\t\t$self.tuple.$field.raw_write();\n\t\t\t(\n\t\t\t\tContextGuard {\n\t\t\t\t\t_key: \u0026$self.key,\n\t\t\t\t\tguard: $self.tuple.$field.guard(),\n\t\t\t\t},\n\t\t\t\t$self.transmute(),\n\t\t\t)\n\t\t}\n\t};\n}\n\nmacro_rules! try_lock_impl {\n\t($self: expr, $field: tt) =\u003e {\n\t\tunsafe {\n\t\t\tif $self.tuple.$field.raw_try_write() {\n\t\t\t\tOk((\n\t\t\t\t\tContextGuard {\n\t\t\t\t\t\t_key: $self.key,\n\t\t\t\t\t\tguard: $self.tuple.$field.guard(),\n\t\t\t\t\t},\n\t\t\t\t\t$self.transmute(),\n\t\t\t\t))\n\t\t\t} else {\n\t\t\t\tErr($self)\n\t\t\t}\n\t\t}\n\t};\n}\n\nmacro_rules! lock_mut_impl {\n\t($self: expr, $field: tt) =\u003e {\n\t\tunsafe {\n\t\t\t$self.tuple.$field.raw_write();\n\t\t\tContextGuard {\n\t\t\t\t_key: $self.key,\n\t\t\t\tguard: $self.tuple.$field.guard(),\n\t\t\t}\n\t\t}\n\t};\n}\n\nmacro_rules! try_lock_mut_impl {\n\t($self: expr, $field: tt) =\u003e {\n\t\tunsafe {\n\t\t\tif $self.tuple.$field.raw_try_write() {\n\t\t\t\tSome(ContextGuard {\n\t\t\t\t\t_key: $self.key,\n\t\t\t\t\tguard: $self.tuple.$field.guard(),\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\tNone\n\t\t\t}\n\t\t}\n\t};\n}\n\nmacro_rules! read_impl {\n\t($self: expr, $field: tt) =\u003e {\n\t\tunsafe {\n\t\t\t$self.tuple.$field.raw_read();\n\t\t\t(\n\t\t\t\tContextGuard {\n\t\t\t\t\t_key: \u0026$self.key,\n\t\t\t\t\tguard: $self.tuple.$field.read_guard(),\n\t\t\t\t},\n\t\t\t\t$self.transmute(),\n\t\t\t)\n\t\t}\n\t};\n}\n\nmacro_rules! try_read_impl {\n\t($self: expr, $field: tt) =\u003e {\n\t\tunsafe {\n\t\t\tif $self.tuple.$field.raw_try_read() {\n\t\t\t\tOk((\n\t\t\t\t\tContextGuard {\n\t\t\t\t\t\t_key: $self.key,\n\t\t\t\t\t\tguard: $self.tuple.$field.read_guard(),\n\t\t\t\t\t},\n\t\t\t\t\t$self.transmute(),\n\t\t\t\t))\n\t\t\t} else {\n\t\t\t\tErr($self)\n\t\t\t}\n\t\t}\n\t};\n}\n\nmacro_rules! read_mut_impl {\n\t($self: expr, $field: tt) =\u003e {\n\t\tunsafe {\n\t\t\t$self.tuple.$field.raw_read();\n\t\t\tContextGuard {\n\t\t\t\t_key: $self.key,\n\t\t\t\tguard: $self.tuple.$field.read_guard(),\n\t\t\t}\n\t\t}\n\t};\n}\n\nmacro_rules! try_read_mut_impl {\n\t($self: expr, $field: tt) =\u003e {\n\t\tunsafe {\n\t\t\tif $self.tuple.$field.raw_try_read() {\n\t\t\t\tSome(ContextGuard {\n\t\t\t\t\t_key: $self.key,\n\t\t\t\t\tguard: $self.tuple.$field.read_guard(),\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\tNone\n\t\t\t}\n\t\t}\n\t};\n}\n\nmacro_rules! recurse_impl {\n\t($self: expr, $field: tt) =\u003e {\n\t\tLockingTuple {\n\t\t\t_lockable: PhantomData,\n\t\t\tkey: $self.key,\n\t\t\ttuple: \u0026$self.tuple.$field,\n\t\t\touter: $self.transmute(),\n\t\t}\n\t};\n}\n\nmacro_rules! recurse_iter_impl {\n\t($self: expr, $field: tt) =\u003e {\n\t\tLockingIterator {\n\t\t\tkey: $self.key,\n\t\t\titerator: $self.tuple.$field.into_iter(),\n\t\t\touter: $self.transmute(),\n\t\t}\n\t};\n}\n\ntype LockReturn\u003c'a, 'context, Guarded, L, C, O\u003e = (\n\tContextGuard\u003c'a, \u003cGuarded as Lockable\u003e::Guard\u003c'a\u003e, ThreadKey\u003e,\n\tLockingTuple\u003c'context, L, C, O\u003e,\n);\n\ntype TryLockReturn\u003c'a, 'context, Guarded, L, C, O, This\u003e =\n\tResult\u003cLockReturn\u003c'a, 'context, Guarded, L, C, O\u003e, This\u003e;\n\ntype ReadReturn\u003c'a, 'context, Guarded, L, C, O\u003e = (\n\tContextGuard\u003c'a, \u003cGuarded as Sharable\u003e::ReadGuard\u003c'a\u003e, ThreadKey\u003e,\n\tLockingTuple\u003c'context, L, C, O\u003e,\n);\n\ntype TryReadReturn\u003c'a, 'context, Guarded, L, C, O, This\u003e =\n\tResult\u003cReadReturn\u003c'a, 'context, Guarded, L, C, O\u003e, This\u003e;\n\ntype RecurseReturn\u003c'context, Inner, L, C, O\u003e =\n\tLockingTuple\u003c'context, Inner, Inner, LockingTuple\u003c'context, L, C, O\u003e\u003e;\n\ntype RecurseIterReturn\u003c'context, Inner, L, C, O\u003e = LockingIterator\u003c\n\t'context,\n\t\u003c\u0026'context Inner as IntoIterator\u003e::IntoIter,\n\tLockingTuple\u003c'context, L, C, O\u003e,\n\u003e;\n\nimpl\u003cT, C, Outer\u003e LockingTuple\u003c'_, T, C, Outer\u003e {\n\t/// Exit out of the current scope of the locking tuple into the parent.\n\tpub fn exit(self) -\u003e Outer {\n\t\tself.outer\n\t}\n}\n\nimpl\u003c'context, A: RawLock + Lockable, Outer\u003e LockingTuple\u003c'context, (A,), (A,), Outer\u003e {\n\t/// Lock the first element, and return a new tuple where the first element\n\t/// is inaccessible.\n\t#[must_use]\n\tpub fn lock_0\u003c'a\u003e(self) -\u003e LockReturn\u003c'a, 'context, A, ((),), (A,), Outer\u003e\n\twhere\n\t\t'context: 'a,\n\t{\n\t\tlock_impl!(self, 0)\n\t}\n\n\t/// Attempts to lock the first element without blocking, and return a new tuple\n\t/// where the first element is inaccessible.\n\t///\n\t/// # Errors\n\t///\n\t/// If the element is already locked, `Err` is returned with the original\n\t/// tuple.\n\tpub fn try_lock_0\u003c'a\u003e(self) -\u003e TryLockReturn\u003c'a, 'context, A, ((),), (A,), Outer, Self\u003e\n\twhere\n\t\t'context: 'a,\n\t{\n\t\ttry_lock_impl!(self, 0)\n\t}\n\n\t/// Lock the first element. The tuple becomes unusable until the returned\n\t/// guard is dropped.\n\t#[must_use]\n\tpub fn lock_mut_0(\u0026mut self) -\u003e ContextGuard\u003c'_, A::Guard\u003c'_\u003e, ThreadKey\u003e {\n\t\tlock_mut_impl!(self, 0)\n\t}\n\n\t/// Attempts to lock the first element without blocking. If successful, the\n\t/// tuple becomes unusable until the returned guard is dropped. If the element\n\t/// is already locked, `None` is returned.\n\t#[must_use]\n\tpub fn try_lock_mut_0(\u0026mut self) -\u003e Option\u003cContextGuard\u003c'_, A::Guard\u003c'_\u003e, ThreadKey\u003e\u003e {\n\t\ttry_lock_mut_impl!(self, 0)\n\t}\n}\n\nimpl\u003c'context, A: RawLock + Sharable, Outer\u003e LockingTuple\u003c'context, (A,), (A,), Outer\u003e {\n\t/// Acquire a shared lock to the first element, and return a new tuple where\n\t/// the first element is inaccessible.\n\t#[must_use]\n\tpub fn read_0\u003c'a\u003e(self) -\u003e ReadReturn\u003c'a, 'context, A, ((),), (A,), Outer\u003e\n\twhere\n\t\t'context: 'a,\n\t{\n\t\tread_impl!(self, 0)\n\t}\n\n\t/// Attempts to acquire a shared lock the first element without blocking, and\n\t/// return a new tuple where the first element is inaccessible.\n\t///\n\t/// # Errors\n\t///\n\t/// If the element is already exclusively locked, `Err` is returned with the original\n\t/// tuple.\n\tpub fn try_read_0\u003c'a\u003e(self) -\u003e TryReadReturn\u003c'a, 'context, A, ((),), (A,), Outer, Self\u003e\n\twhere\n\t\t'context: 'a,\n\t{\n\t\ttry_read_impl!(self, 0)\n\t}\n\n\t/// Acquire a shared lock to the first element. The tuple becomes unusable\n\t/// until the returned guard is dropped.\n\t#[must_use]\n\tpub fn read_mut_0(\u0026mut self) -\u003e ContextGuard\u003c'_, A::ReadGuard\u003c'_\u003e, ThreadKey\u003e {\n\t\tread_mut_impl!(self, 0)\n\t}\n\n\t/// Attempts to acquire a shared lock the first element without blocking. If\n\t/// successful, the tuple becomes unusable until the returned guard is\n\t/// dropped. If the element is already exclusively locked, `None` is returned.\n\t#[must_use]\n\tpub fn try_read_mut_0(\u0026mut self) -\u003e Option\u003cContextGuard\u003c'_, A::ReadGuard\u003c'_\u003e, ThreadKey\u003e\u003e {\n\t\ttry_read_mut_impl!(self, 0)\n\t}\n}\n\nimpl\u003c'context, A, O\u003e LockingTuple\u003c'context, (A,), (A,), O\u003e {\n\t/// Consume the tuple, and return the first element as a new tuple.\n\t#[must_use]\n\tpub fn recurse_0(self) -\u003e RecurseReturn\u003c'context, A, ((),), (A,), O\u003e {\n\t\trecurse_impl!(self, 0)\n\t}\n\n\t/// Consume the tuple, and return the first element as a locking iterator.\n\tpub fn recurse_0_iter(self) -\u003e RecurseIterReturn\u003c'context, A, ((),), (A,), O\u003e\n\twhere\n\t\t\u0026'context A: IntoIterator,\n\t{\n\t\trecurse_iter_impl!(self, 0)\n\t}\n}\n\nimpl\u003c'context, A: RawLock + Lockable, B, B0, O\u003e LockingTuple\u003c'context, (A, B), (A, B0), O\u003e {\n\t/// Lock the first element, and return a new tuple where the first element\n\t/// is inaccessible.\n\t#[must_use]\n\tpub fn lock_0\u003c'a\u003e(self) -\u003e LockReturn\u003c'a, 'context, A, ((), B), (A, B0), O\u003e\n\twhere\n\t\t'context: 'a,\n\t{\n\t\tlock_impl!(self, 0)\n\t}\n\n\t/// Lock the first element. The tuple becomes unusable until the returned\n\t/// guard is dropped.\n\t#[must_use]\n\tpub fn lock_mut_0(\u0026mut self) -\u003e ContextGuard\u003c'_, A::Guard\u003c'_\u003e, ThreadKey\u003e {\n\t\tlock_mut_impl!(self, 0)\n\t}\n\n\t/// Attempts to lock the first element without blocking, and return a new tuple\n\t/// where the first element is inaccessible.\n\t///\n\t/// # Errors\n\t///\n\t/// If the element is already locked, `Err` is returned with the original\n\t/// tuple.\n\tpub fn try_lock_0\u003c'a\u003e(self) -\u003e TryLockReturn\u003c'a, 'context, A, ((), B), (A, B0), O, Self\u003e\n\twhere\n\t\t'context: 'a,\n\t{\n\t\ttry_lock_impl!(self, 0)\n\t}\n\n\t/// Attempts to lock the first element without blocking. If successful, the\n\t/// tuple becomes unusable until the returned guard is dropped. If the element\n\t/// is already locked, `None` is returned.\n\t#[must_use]\n\tpub fn try_lock_mut_0(\u0026mut self) -\u003e Option\u003cContextGuard\u003c'_, A::Guard\u003c'_\u003e, ThreadKey\u003e\u003e {\n\t\ttry_lock_mut_impl!(self, 0)\n\t}\n}\n\nimpl\u003c'context, A: RawLock + Sharable, B, B0, O\u003e LockingTuple\u003c'context, (A, B), (A, B0), O\u003e {\n\t/// Acquire a shared lock to the first element, and return a new tuple where\n\t/// the first element is inaccessible.\n\t#[must_use]\n\tpub fn read_0\u003c'a\u003e(self) -\u003e ReadReturn\u003c'a, 'context, A, ((), B), (A, B0), O\u003e\n\twhere\n\t\t'context: 'a,\n\t{\n\t\tread_impl!(self, 0)\n\t}\n\n\t/// Attempts to acquire a shared lock the first element without blocking, and\n\t/// return a new tuple where the first element is inaccessible.\n\t///\n\t/// # Errors\n\t///\n\t/// If the element is already exclusively locked, `Err` is returned with the original\n\t/// tuple.\n\tpub fn try_read_0\u003c'a\u003e(self) -\u003e TryReadReturn\u003c'a, 'context, A, ((), B), (A, B0), O, Self\u003e\n\twhere\n\t\t'context: 'a,\n\t{\n\t\ttry_read_impl!(self, 0)\n\t}\n\n\t/// Acquire a shared lock to the first element. The tuple becomes unusable\n\t/// until the returned guard is dropped.\n\t#[must_use]\n\tpub fn read_mut_0(\u0026mut self) -\u003e ContextGuard\u003c'_, A::ReadGuard\u003c'_\u003e, ThreadKey\u003e {\n\t\tread_mut_impl!(self, 0)\n\t}\n\n\t/// Attempts to acquire a shared lock the first element without blocking. If\n\t/// successful, the tuple becomes unusable until the returned guard is\n\t/// dropped. If the element is already exclusively locked, `None` is returned.\n\t#[must_use]\n\tpub fn try_read_mut_0(\u0026mut self) -\u003e Option\u003cContextGuard\u003c'_, A::ReadGuard\u003c'_\u003e, ThreadKey\u003e\u003e {\n\t\ttry_read_mut_impl!(self, 0)\n\t}\n}\n\nimpl\u003c'context, A, B, B0, O\u003e LockingTuple\u003c'context, (A, B), (A, B0), O\u003e {\n\t/// Consume the tuple, and return the first element as a new tuple.\n\t#[must_use]\n\tpub fn recurse_0(self) -\u003e RecurseReturn\u003c'context, A, ((), B), (A, B0), O\u003e {\n\t\trecurse_impl!(self, 0)\n\t}\n\n\t/// Consume the tuple, and return the first element as a locking iterator.\n\tpub fn recurse_0_iter(self) -\u003e RecurseIterReturn\u003c'context, A, ((), B), (A, B0), O\u003e\n\twhere\n\t\t\u0026'context A: IntoIterator,\n\t{\n\t\trecurse_iter_impl!(self, 0)\n\t}\n}\n\nimpl\u003c'context, A: Lockable + RawLock, B, O\u003e LockingTuple\u003c'context, (A, B), (A, B), O\u003e {\n\t/// Lock the first element, and return the second element as a new tuple.\n\t#[must_use]\n\tpub fn lock_and_recurse\u003c'a\u003e(\n\t\tself,\n\t) -\u003e (\n\t\tContextGuard\u003c'a, \u003cA as Lockable\u003e::Guard\u003c'a\u003e, ThreadKey\u003e,\n\t\tLockingTuple\u003c'context, B, B, O\u003e,\n\t)\n\twhere\n\t\t'context: 'a,\n\t{\n\t\tunsafe {\n\t\t\tself.tuple.0.raw_write();\n\t\t\t(\n\t\t\t\tContextGuard {\n\t\t\t\t\t_key: self.key,\n\t\t\t\t\tguard: self.tuple.0.guard(),\n\t\t\t\t},\n\t\t\t\tLockingTuple {\n\t\t\t\t\t_lockable: PhantomData,\n\t\t\t\t\tkey: self.key,\n\t\t\t\t\ttuple: \u0026self.tuple.1,\n\t\t\t\t\touter: self.outer,\n\t\t\t\t},\n\t\t\t)\n\t\t}\n\t}\n}\n\nimpl\u003c'context, A, A0, B: RawLock + Lockable, O\u003e LockingTuple\u003c'context, (A, B), (A0, B), O\u003e {\n\t/// Lock the second element, and return a new tuple where the first and second\n\t/// elements are inaccessible.\n\t#[must_use]\n\tpub fn lock_1\u003c'a\u003e(self) -\u003e LockReturn\u003c'a, 'context, B, ((), ()), (A0, B), O\u003e\n\twhere\n\t\t'context: 'a,\n\t{\n\t\tlock_impl!(self, 1)\n\t}\n\n\t/// Lock the second element. The tuple becomes unusable until the returned\n\t/// guard is dropped.\n\t#[must_use]\n\tpub fn lock_mut_1(\u0026mut self) -\u003e ContextGuard\u003c'_, B::Guard\u003c'_\u003e, ThreadKey\u003e {\n\t\tlock_mut_impl!(self, 1)\n\t}\n\n\t/// Attempts to lock the second element without blocking, and return a new tuple\n\t/// where the first element is inaccessible.\n\t///\n\t/// # Errors\n\t///\n\t/// If the element is already locked, `Err` is returned with the original\n\t/// tuple.\n\tpub fn try_lock_1\u003c'a\u003e(self) -\u003e TryLockReturn\u003c'a, 'context, B, ((), ()), (A0, B), O, Self\u003e\n\twhere\n\t\t'context: 'a,\n\t{\n\t\ttry_lock_impl!(self, 1)\n\t}\n\n\t/// Attempts to lock the second element without blocking. If successful, the\n\t/// tuple becomes unusable until the returned guard is dropped. If the element\n\t/// is already locked, `None` is returned.\n\t#[must_use]\n\tpub fn try_lock_mut_1(\u0026mut self) -\u003e Option\u003cContextGuard\u003c'_, B::Guard\u003c'_\u003e, ThreadKey\u003e\u003e {\n\t\ttry_lock_mut_impl!(self, 1)\n\t}\n}\n\nimpl\u003c'context, A, A0, B: RawLock + Sharable, O\u003e LockingTuple\u003c'context, (A, B), (A0, B), O\u003e {\n\t/// Acquire a shared lock to the second element, and return a new tuple where\n\t/// the second element is inaccessible.\n\t#[must_use]\n\tpub fn read_1\u003c'a\u003e(self) -\u003e ReadReturn\u003c'a, 'context, B, ((), ()), (A0, B), O\u003e\n\twhere\n\t\t'context: 'a,\n\t{\n\t\tread_impl!(self, 1)\n\t}\n\n\t/// Attempts to acquire a shared lock the second element without blocking, and\n\t/// return a new tuple where the second element is inaccessible.\n\t///\n\t/// # Errors\n\t///\n\t/// If the element is already exclusively locked, `Err` is returned with the original\n\t/// tuple.\n\tpub fn try_read_1\u003c'a\u003e(self) -\u003e TryReadReturn\u003c'a, 'context, B, ((), ()), (A0, B), O, Self\u003e\n\twhere\n\t\t'context: 'a,\n\t{\n\t\ttry_read_impl!(self, 1)\n\t}\n\n\t/// Acquire a shared lock to the second element. The tuple becomes unusable\n\t/// until the returned guard is dropped.\n\t#[must_use]\n\tpub fn read_mut_1(\u0026mut self) -\u003e ContextGuard\u003c'_, B::ReadGuard\u003c'_\u003e, ThreadKey\u003e {\n\t\tread_mut_impl!(self, 1)\n\t}\n\n\t/// Attempts to acquire a shared lock the second element without blocking. If\n\t/// successful, the tuple becomes unusable until the returned guard is\n\t/// dropped. If the element is already exclusively locked, `None` is returned.\n\t#[must_use]\n\tpub fn try_read_mut_1(\u0026mut self) -\u003e Option\u003cContextGuard\u003c'_, B::ReadGuard\u003c'_\u003e, ThreadKey\u003e\u003e {\n\t\ttry_read_mut_impl!(self, 1)\n\t}\n}\n\nimpl\u003c'context, A, A0, B, O\u003e LockingTuple\u003c'context, (A, B), (A0, B), O\u003e {\n\t/// Consume the tuple, and return the second element as a new tuple.\n\t#[must_use]\n\tpub fn recurse_1(self) -\u003e RecurseReturn\u003c'context, B, ((), ()), (A0, B), O\u003e {\n\t\trecurse_impl!(self, 1)\n\t}\n\n\t/// Consume the tuple, and return the second element as a locking iterator.\n\tpub fn recurse_1_iter(self) -\u003e RecurseIterReturn\u003c'context, B, (A, ()), (A0, B), O\u003e\n\twhere\n\t\t\u0026'context B: IntoIterator,\n\t{\n\t\trecurse_iter_impl!(self, 1)\n\t}\n}\n\nimpl\u003c'context, A: RawLock + Lockable, B, B0, C, C0, O\u003e\n\tLockingTuple\u003c'context, (A, B, C), (A, B0, C0), O\u003e\n{\n\t/// Lock the first element, and return a new tuple where the first element\n\t/// is inaccessible.\n\t#[must_use]\n\t// The type is impossible to refactor, and I already wrote this function, so no point in removing it\n\t#[expect(clippy::type_complexity)]\n\tpub fn lock_0\u003c'a\u003e(self) -\u003e LockReturn\u003c'a, 'context, A, ((), B, C), (A, B0, C0), O\u003e\n\twhere\n\t\t'context: 'a,\n\t{\n\t\tlock_impl!(self, 0)\n\t}\n\n\t/// Lock the first element. The tuple becomes unusable until the returned\n\t/// guard is dropped.\n\t#[must_use]\n\tpub fn lock_mut_0(\u0026mut self) -\u003e ContextGuard\u003c'_, A::Guard\u003c'_\u003e, ThreadKey\u003e {\n\t\tlock_mut_impl!(self, 0)\n\t}\n\n\t/// Attempts to lock the first element without blocking. If successful, the\n\t/// tuple becomes unusable until the returned guard is dropped. If the element\n\t/// is already locked, `None` is returned.\n\t#[must_use]\n\tpub fn try_lock_mut_0(\u0026mut self) -\u003e Option\u003cContextGuard\u003c'_, A::Guard\u003c'_\u003e, ThreadKey\u003e\u003e {\n\t\ttry_lock_mut_impl!(self, 0)\n\t}\n}\n\nimpl\u003c'context, A: RawLock + Sharable, B, B0, C, C0, O\u003e\n\tLockingTuple\u003c'context, (A, B, C), (A, B0, C0), O\u003e\n{\n\t/// Acquire a shared lock to the first element, and return a new tuple where\n\t/// the first element is inaccessible.\n\t#[must_use]\n\t// The type is impossible to refactor, and I already wrote this function, so no point in removing it\n\t#[expect(clippy::type_complexity)]\n\tpub fn read_0\u003c'a\u003e(self) -\u003e ReadReturn\u003c'a, 'context, A, ((), B, C), (A, B0, C0), O\u003e\n\twhere\n\t\t'context: 'a,\n\t{\n\t\tread_impl!(self, 0)\n\t}\n\n\t/// Acquire a shared lock to the first element. The tuple becomes unusable\n\t/// until the returned guard is dropped.\n\t#[must_use]\n\tpub fn read_mut_0(\u0026mut self) -\u003e ContextGuard\u003c'_, A::ReadGuard\u003c'_\u003e, ThreadKey\u003e {\n\t\tread_mut_impl!(self, 0)\n\t}\n\n\t/// Attempts to acquire a shared lock the first element without blocking. If\n\t/// successful, the tuple becomes unusable until the returned guard is\n\t/// dropped. If the element is already exclusively locked, `None` is returned.\n\t#[must_use]\n\tpub fn try_read_mut_0(\u0026mut self) -\u003e Option\u003cContextGuard\u003c'_, A::ReadGuard\u003c'_\u003e, ThreadKey\u003e\u003e {\n\t\ttry_read_mut_impl!(self, 0)\n\t}\n}\n\nimpl\u003c'context, A, B, B0, C, C0, O\u003e LockingTuple\u003c'context, (A, B, C), (A, B0, C0), O\u003e {\n\t/// Consume the tuple, and return the first element as a new tuple.\n\t#[must_use]\n\t// The type is impossible to refactor, and I already wrote this function, so no point in removing it\n\t#[expect(clippy::type_complexity)]\n\tpub fn recurse_0(self) -\u003e RecurseReturn\u003c'context, A, ((), B, C), (A, B0, C0), O\u003e {\n\t\trecurse_impl!(self, 0)\n\t}\n\n\t/// Consume the tuple, and return the first element as a locking iterator.\n\t// The type is impossible to refactor, and I already wrote this function, so no point in removing it\n\t#[expect(clippy::type_complexity)]\n\tpub fn recurse_0_iter(self) -\u003e RecurseIterReturn\u003c'context, A, ((), B, C), (A, B0, C0), O\u003e\n\twhere\n\t\t\u0026'context A: IntoIterator,\n\t{\n\t\trecurse_iter_impl!(self, 0)\n\t}\n}\n\nimpl\u003c'context, A, A0, B: RawLock + Lockable, C, C0, O\u003e\n\tLockingTuple\u003c'context, (A, B, C), (A0, B, C0), O\u003e\n{\n\t/// Lock the second element, and return a new tuple where the first and second\n\t/// elements are inaccessible.\n\t#[must_use]\n\t// The type is impossible to refactor, and I already wrote this function, so no point in removing it\n\t#[expect(clippy::type_complexity)]\n\tpub fn lock_1\u003c'a\u003e(self) -\u003e LockReturn\u003c'a, 'context, B, ((), (), C), (A0, B, C0), O\u003e\n\twhere\n\t\t'context: 'a,\n\t{\n\t\tlock_impl!(self, 1)\n\t}\n\n\t/// Lock the second element. The tuple becomes unusable until the returned\n\t/// guard is dropped.\n\t#[must_use]\n\tpub fn lock_mut_1(\u0026mut self) -\u003e ContextGuard\u003c'_, B::Guard\u003c'_\u003e, ThreadKey\u003e {\n\t\tlock_mut_impl!(self, 1)\n\t}\n\n\t/// Attempts to lock the second element without blocking. If successful, the\n\t/// tuple becomes unusable until the returned guard is dropped. If the element\n\t/// is already locked, `None` is returned.\n\t#[must_use]\n\tpub fn try_lock_mut_1(\u0026mut self) -\u003e Option\u003cContextGuard\u003c'_, B::Guard\u003c'_\u003e, ThreadKey\u003e\u003e {\n\t\ttry_lock_mut_impl!(self, 1)\n\t}\n}\n\nimpl\u003c'context, A, A0, B: RawLock + Sharable, C, C0, O\u003e\n\tLockingTuple\u003c'context, (A, B, C), (A0, B, C0), O\u003e\n{\n\t/// Acquire a shared lock to the second element, and return a new tuple where\n\t/// the second element is inaccessible.\n\t#[must_use]\n\t// The type is impossible to refactor, and I already wrote this function, so no point in removing it\n\t#[expect(clippy::type_complexity)]\n\tpub fn read_1\u003c'a\u003e(self) -\u003e ReadReturn\u003c'a, 'context, B, ((), (), C), (A0, B, C0), O\u003e\n\twhere\n\t\t'context: 'a,\n\t{\n\t\tread_impl!(self, 1)\n\t}\n\n\t/// Acquire a shared lock to the second element. The tuple becomes unusable\n\t/// until the returned guard is dropped.\n\t#[must_use]\n\tpub fn read_mut_1(\u0026mut self) -\u003e ContextGuard\u003c'_, B::ReadGuard\u003c'_\u003e, ThreadKey\u003e {\n\t\tread_mut_impl!(self, 1)\n\t}\n\n\t/// Attempts to acquire a shared lock the second element without blocking. If\n\t/// successful, the tuple becomes unusable until the returned guard is\n\t/// dropped. If the element is already exclusively locked, `None` is returned.\n\t#[must_use]\n\tpub fn try_read_mut_1(\u0026mut self) -\u003e Option\u003cContextGuard\u003c'_, B::ReadGuard\u003c'_\u003e, ThreadKey\u003e\u003e {\n\t\ttry_read_mut_impl!(self, 1)\n\t}\n}\n\nimpl\u003c'context, A, A0, B, C, C0, O\u003e LockingTuple\u003c'context, (A, B, C), (A0, B, C0), O\u003e {\n\t/// Consume the tuple, and return the second element as a new tuple.\n\t#[must_use]\n\t// The type is impossible to refactor, and I already wrote this function, so no point in removing it\n\t#[expect(clippy::type_complexity)]\n\tpub fn recurse_1(self) -\u003e RecurseReturn\u003c'context, B, ((), (), C), (A0, B, C0), O\u003e {\n\t\trecurse_impl!(self, 1)\n\t}\n\n\t/// Consume the tuple, and return the second element as a locking iterator.\n\t// The type is impossible to refactor, and I already wrote this function, so no point in removing it\n\t#[expect(clippy::type_complexity)]\n\tpub fn recurse_1_iter(self) -\u003e RecurseIterReturn\u003c'context, B, ((), (), C), (A0, B, C0), O\u003e\n\twhere\n\t\t\u0026'context B: IntoIterator,\n\t{\n\t\trecurse_iter_impl!(self, 1)\n\t}\n}\n\nimpl\u003c'context, A, A0, B, B0, C: RawLock + Lockable, O\u003e\n\tLockingTuple\u003c'context, (A, B, C), (A0, B0, C), O\u003e\n{\n\t/// Lock the third element, and return a new tuple where all elements are\n\t/// inaccessible.\n\t#[must_use]\n\t// The type is impossible to refactor, and I already wrote this function, so no point in removing it\n\t#[expect(clippy::type_complexity)]\n\tpub fn lock_2\u003c'a\u003e(self) -\u003e LockReturn\u003c'a, 'context, C, ((), (), ()), (A0, B0, C), O\u003e\n\twhere\n\t\t'context: 'a,\n\t{\n\t\tlock_impl!(self, 2)\n\t}\n\n\t/// Lock the third element. The tuple becomes unusable until the returned\n\t/// guard is dropped.\n\t#[must_use]\n\tpub fn lock_mut_2(\u0026mut self) -\u003e ContextGuard\u003c'_, C::Guard\u003c'_\u003e, ThreadKey\u003e {\n\t\tlock_mut_impl!(self, 2)\n\t}\n\n\t/// Attempts to lock the third element without blocking. If successful, the\n\t/// tuple becomes unusable until the returned guard is dropped. If the element\n\t/// is already locked, `None` is returned.\n\t#[must_use]\n\tpub fn try_lock_mut_2(\u0026mut self) -\u003e Option\u003cContextGuard\u003c'_, C::Guard\u003c'_\u003e, ThreadKey\u003e\u003e {\n\t\ttry_lock_mut_impl!(self, 2)\n\t}\n}\n\nimpl\u003c'context, A, A0, B, B0, C: RawLock + Sharable, O\u003e\n\tLockingTuple\u003c'context, (A, B, C), (A0, B0, C), O\u003e\n{\n\t/// Acquire a shared lock to the third element, and return a new tuple where\n\t/// the third element is inaccessible.\n\t#[must_use]\n\t// The type is impossible to refactor, and I already wrote this function, so no point in removing it\n\t#[expect(clippy::type_complexity)]\n\tpub fn read_2\u003c'a\u003e(self) -\u003e ReadReturn\u003c'a, 'context, C, ((), (), ()), (A0, B0, C), O\u003e\n\twhere\n\t\t'context: 'a,\n\t{\n\t\tread_impl!(self, 2)\n\t}\n\n\t/// Acquire a shared lock to the third element. The tuple becomes unusable\n\t/// until the returned guard is dropped.\n\t#[must_use]\n\tpub fn read_mut_2(\u0026mut self) -\u003e ContextGuard\u003c'_, C::ReadGuard\u003c'_\u003e, ThreadKey\u003e {\n\t\tread_mut_impl!(self, 2)\n\t}\n\n\t/// Attempts to acquire a shared lock the third element without blocking. If\n\t/// successful, the tuple becomes unusable until the returned guard is\n\t/// dropped. If the element is already exclusively locked, `None` is returned.\n\t#[must_use]\n\tpub fn try_read_mut_2(\u0026mut self) -\u003e Option\u003cContextGuard\u003c'_, C::ReadGuard\u003c'_\u003e, ThreadKey\u003e\u003e {\n\t\ttry_read_mut_impl!(self, 2)\n\t}\n}\n\nimpl\u003c'context, A, A0, B, B0, C, O\u003e LockingTuple\u003c'context, (A, B, C), (A0, B0, C), O\u003e {\n\t/// Consume the tuple, and return the third element as a new tuple.\n\t#[must_use]\n\t// The type is impossible to refactor, and I already wrote this function, so no point in removing it\n\t#[expect(clippy::type_complexity)]\n\tpub fn recurse_2(self) -\u003e RecurseReturn\u003c'context, C, ((), (), ()), (A0, B0, C), O\u003e {\n\t\trecurse_impl!(self, 2)\n\t}\n\n\t/// Consume the tuple, and return the third element as a locking iterator.\n\t// The type is impossible to refactor, and I already wrote this function, so no point in removing it\n\t#[expect(clippy::type_complexity)]\n\tpub fn recurse_2_iter(self) -\u003e RecurseIterReturn\u003c'context, C, ((), (), ()), (A0, B0, C), O\u003e\n\twhere\n\t\t\u0026'context C: IntoIterator,\n\t{\n\t\trecurse_iter_impl!(self, 2)\n\t}\n}\n\nimpl\u003c'context, A, B, B0, C, C0, D, D0, O\u003e LockingTuple\u003c'context, (A, B, C, D), (A, B0, C0, D0), O\u003e {\n\t/// Consume the tuple, and return the first element as a new tuple.\n\t#[must_use]\n\t// The type is impossible to refactor, and I already wrote this function, so no point in removing it\n\t#[expect(clippy::type_complexity)]\n\tpub fn recurse_0(self) -\u003e RecurseReturn\u003c'context, A, ((), B, C, D), (A, B0, C0, D0), O\u003e {\n\t\trecurse_impl!(self, 0)\n\t}\n\n\t/// Consume the tuple, and return the first element as a locking iterator.\n\t// The type is impossible to refactor, and I already wrote this function, so no point in removing it\n\t#[expect(clippy::type_complexity)]\n\tpub fn recurse_0_iter(self) -\u003e RecurseIterReturn\u003c'context, A, ((), B, C, D), (A, B0, C0, D0), O\u003e\n\twhere\n\t\t\u0026'context A: IntoIterator,\n\t{\n\t\trecurse_iter_impl!(self, 0)\n\t}\n}\n\nimpl\u003c'context, A, A0, B, C, C0, D, D0, O\u003e LockingTuple\u003c'context, (A, B, C, D), (A0, B, C0, D0), O\u003e {\n\t/// Consume the tuple, and return the second element as a new tuple.\n\t#[must_use]\n\t// The type is impossible to refactor, and I already wrote this function, so no point in removing it\n\t#[expect(clippy::type_complexity)]\n\tpub fn recurse_1(self) -\u003e RecurseReturn\u003c'context, B, ((), (), C, D), (A0, B, C0, D0), O\u003e {\n\t\trecurse_impl!(self, 1)\n\t}\n\n\t/// Consume the tuple, and return the second element as a locking iterator.\n\t// The type is impossible to refactor, and I already wrote this function, so no point in removing it\n\t#[expect(clippy::type_complexity)]\n\tpub fn recurse_1_iter(\n\t\tself,\n\t) -\u003e RecurseIterReturn\u003c'context, B, ((), (), C, D), (A0, B, C0, D0), O\u003e\n\twhere\n\t\t\u0026'context B: IntoIterator,\n\t{\n\t\trecurse_iter_impl!(self, 1)\n\t}\n}\n\nimpl\u003c'context, A, A0, B, B0, C, D, D0, O\u003e LockingTuple\u003c'context, (A, B, C, D), (A0, B0, C, D0), O\u003e {\n\t/// Consume the tuple, and return the third element as a new tuple.\n\t#[must_use]\n\t// The type is impossible to refactor, and I already wrote this function, so no point in removing it\n\t#[expect(clippy::type_complexity)]\n\tpub fn recurse_2(self) -\u003e RecurseReturn\u003c'context, C, ((), (), (), D), (A0, B0, C, D0), O\u003e {\n\t\trecurse_impl!(self, 2)\n\t}\n\n\t/// Consume the tuple, and return the third element as a locking iterator.\n\t// The type is impossible to refactor, and I already wrote this function, so no point in removing it\n\t#[expect(clippy::type_complexity)]\n\tpub fn recurse_2_iter(\n\t\tself,\n\t) -\u003e RecurseIterReturn\u003c'context, C, ((), (), (), D), (A0, B0, C, D0), O\u003e\n\twhere\n\t\t\u0026'context C: IntoIterator,\n\t{\n\t\trecurse_iter_impl!(self, 2)\n\t}\n}\n\nimpl\u003c'context, A, A0, B, B0, C, C0, D, O\u003e LockingTuple\u003c'context, (A, B, C, D), (A0, B0, C0, D), O\u003e {\n\t/// Consume the tuple, and return the fourth element as a new tuple.\n\t#[must_use]\n\t// The type is impossible to refactor, and I already wrote this function, so no point in removing it\n\t#[expect(clippy::type_complexity)]\n\tpub fn recurse_3(self) -\u003e RecurseReturn\u003c'context, D, ((), (), (), ()), (A0, B0, C0, D), O\u003e {\n\t\trecurse_impl!(self, 3)\n\t}\n\n\t/// Consume the tuple, and return the first element as a locking iterator.\n\t// The type is impossible to refactor, and I already wrote this function, so no point in removing it\n\t#[expect(clippy::type_complexity)]\n\tpub fn recurse_3_iter(\n\t\tself,\n\t) -\u003e RecurseIterReturn\u003c'context, D, ((), (), (), ()), (A0, B0, C0, D), O\u003e\n\twhere\n\t\t\u0026'context D: IntoIterator,\n\t{\n\t\trecurse_iter_impl!(self, 3)\n\t}\n}\n","traces":[{"line":10,"address":[],"length":0,"stats":{"Line":5}},{"line":13,"address":[918675],"length":1,"stats":{"Line":1}},{"line":14,"address":[],"length":0,"stats":{"Line":1}},{"line":15,"address":[],"length":0,"stats":{"Line":1}},{"line":23,"address":[919160,921704,923032,921958],"length":1,"stats":{"Line":4}},{"line":25,"address":[922068,919251,923131,921795],"length":1,"stats":{"Line":4}},{"line":26,"address":[919214,923090,921758,922031],"length":1,"stats":{"Line":4}},{"line":27,"address":[923100,919224,921768,922038],"length":1,"stats":{"Line":4}},{"line":29,"address":[922125,923156,919276,921820],"length":1,"stats":{"Line":4}},{"line":56,"address":[923293,923357],"length":1,"stats":{"Line":2}},{"line":58,"address":[923374,923310],"length":1,"stats":{"Line":2}},{"line":59,"address":[923318,923382],"length":1,"stats":{"Line":2}},{"line":68,"address":[925406,925432,922270,922952,919428,922292,922926,919406,925044,925214,925022,925240],"length":1,"stats":{"Line":12}},{"line":69,"address":[925463,925271,925071,919455,922319,922983],"length":1,"stats":{"Line":6}},{"line":70,"address":[925439,925051,919435,922299,925247,922959],"length":1,"stats":{"Line":6}},{"line":71,"address":[919442,922966,925254,922306,925446,925058],"length":1,"stats":{"Line":6}},{"line":74,"address":[925035,922943,925423,925231,919419,922283],"length":1,"stats":{"Line":6}},{"line":128,"address":[923448,922388,925118,925140,920852,922366,925336,920830,925502,925528,923422,925310],"length":1,"stats":{"Line":12}},{"line":129,"address":[925367,923479,925167,922415,925559,920879],"length":1,"stats":{"Line":6}},{"line":130,"address":[922395,925147,925535,925343,923455,920859],"length":1,"stats":{"Line":6}},{"line":131,"address":[923462,922402,925154,920866,925350,925542],"length":1,"stats":{"Line":6}},{"line":134,"address":[920843,922379,923439,925131,925519,925327],"length":1,"stats":{"Line":6}},{"line":153,"address":[922652],"length":1,"stats":{"Line":1}},{"line":154,"address":[922490],"length":1,"stats":{"Line":1}},{"line":155,"address":[922500],"length":1,"stats":{"Line":1}},{"line":156,"address":[922583],"length":1,"stats":{"Line":1}},{"line":188,"address":[],"length":0,"stats":{"Line":1}},{"line":189,"address":[],"length":0,"stats":{"Line":1}},{"line":197,"address":[919375,919136,919369],"length":1,"stats":{"Line":1}},{"line":201,"address":[],"length":0,"stats":{"Line":1}},{"line":215,"address":[],"length":0,"stats":{"Line":0}},{"line":221,"address":[],"length":0,"stats":{"Line":0}},{"line":222,"address":[],"length":0,"stats":{"Line":0}},{"line":229,"address":[919392],"length":1,"stats":{"Line":1}},{"line":230,"address":[],"length":0,"stats":{"Line":0}},{"line":242,"address":[],"length":0,"stats":{"Line":0}},{"line":256,"address":[],"length":0,"stats":{"Line":0}},{"line":262,"address":[],"length":0,"stats":{"Line":0}},{"line":263,"address":[],"length":0,"stats":{"Line":0}},{"line":270,"address":[],"length":0,"stats":{"Line":1}},{"line":271,"address":[],"length":0,"stats":{"Line":0}},{"line":278,"address":[],"length":0,"stats":{"Line":0}},{"line":279,"address":[],"length":0,"stats":{"Line":0}},{"line":287,"address":[],"length":0,"stats":{"Line":0}},{"line":295,"address":[921936,921919,922223,921913,921680],"length":1,"stats":{"Line":2}},{"line":299,"address":[],"length":0,"stats":{"Line":2}},{"line":305,"address":[],"length":0,"stats":{"Line":0}},{"line":306,"address":[],"length":0,"stats":{"Line":0}},{"line":320,"address":[],"length":0,"stats":{"Line":0}},{"line":327,"address":[],"length":0,"stats":{"Line":1}},{"line":328,"address":[],"length":0,"stats":{"Line":0}},{"line":340,"address":[],"length":0,"stats":{"Line":0}},{"line":354,"address":[],"length":0,"stats":{"Line":0}},{"line":360,"address":[],"length":0,"stats":{"Line":0}},{"line":361,"address":[],"length":0,"stats":{"Line":0}},{"line":368,"address":[922352],"length":1,"stats":{"Line":1}},{"line":369,"address":[],"length":0,"stats":{"Line":0}},{"line":376,"address":[],"length":0,"stats":{"Line":0}},{"line":377,"address":[],"length":0,"stats":{"Line":0}},{"line":381,"address":[922676,922448],"length":1,"stats":{"Line":1}},{"line":385,"address":[],"length":0,"stats":{"Line":1}},{"line":402,"address":[],"length":0,"stats":{"Line":0}},{"line":404,"address":[],"length":0,"stats":{"Line":0}},{"line":405,"address":[],"length":0,"stats":{"Line":0}},{"line":406,"address":[],"length":0,"stats":{"Line":0}},{"line":408,"address":[],"length":0,"stats":{"Line":0}},{"line":409,"address":[],"length":0,"stats":{"Line":0}},{"line":410,"address":[],"length":0,"stats":{"Line":0}},{"line":411,"address":[],"length":0,"stats":{"Line":0}},{"line":412,"address":[],"length":0,"stats":{"Line":0}},{"line":423,"address":[923255,923008,923249],"length":1,"stats":{"Line":1}},{"line":427,"address":[],"length":0,"stats":{"Line":1}},{"line":433,"address":[923280,923344],"length":1,"stats":{"Line":2}},{"line":434,"address":[],"length":0,"stats":{"Line":0}},{"line":448,"address":[],"length":0,"stats":{"Line":0}},{"line":455,"address":[922912],"length":1,"stats":{"Line":1}},{"line":456,"address":[],"length":0,"stats":{"Line":0}},{"line":468,"address":[],"length":0,"stats":{"Line":0}},{"line":482,"address":[],"length":0,"stats":{"Line":0}},{"line":488,"address":[],"length":0,"stats":{"Line":0}},{"line":489,"address":[],"length":0,"stats":{"Line":0}},{"line":496,"address":[923408],"length":1,"stats":{"Line":1}},{"line":497,"address":[],"length":0,"stats":{"Line":0}},{"line":504,"address":[],"length":0,"stats":{"Line":0}},{"line":505,"address":[],"length":0,"stats":{"Line":0}},{"line":513,"address":[],"length":0,"stats":{"Line":0}},{"line":529,"address":[],"length":0,"stats":{"Line":0}},{"line":535,"address":[],"length":0,"stats":{"Line":0}},{"line":536,"address":[],"length":0,"stats":{"Line":0}},{"line":543,"address":[],"length":0,"stats":{"Line":1}},{"line":544,"address":[],"length":0,"stats":{"Line":0}},{"line":560,"address":[],"length":0,"stats":{"Line":0}},{"line":566,"address":[],"length":0,"stats":{"Line":0}},{"line":567,"address":[],"length":0,"stats":{"Line":0}},{"line":574,"address":[],"length":0,"stats":{"Line":1}},{"line":575,"address":[],"length":0,"stats":{"Line":0}},{"line":584,"address":[],"length":0,"stats":{"Line":0}},{"line":585,"address":[],"length":0,"stats":{"Line":0}},{"line":595,"address":[],"length":0,"stats":{"Line":0}},{"line":611,"address":[],"length":0,"stats":{"Line":0}},{"line":617,"address":[],"length":0,"stats":{"Line":0}},{"line":618,"address":[],"length":0,"stats":{"Line":0}},{"line":625,"address":[],"length":0,"stats":{"Line":1}},{"line":626,"address":[],"length":0,"stats":{"Line":0}},{"line":642,"address":[],"length":0,"stats":{"Line":0}},{"line":648,"address":[],"length":0,"stats":{"Line":0}},{"line":649,"address":[],"length":0,"stats":{"Line":0}},{"line":656,"address":[925296],"length":1,"stats":{"Line":1}},{"line":657,"address":[],"length":0,"stats":{"Line":0}},{"line":666,"address":[],"length":0,"stats":{"Line":0}},{"line":667,"address":[],"length":0,"stats":{"Line":0}},{"line":677,"address":[],"length":0,"stats":{"Line":0}},{"line":693,"address":[],"length":0,"stats":{"Line":0}},{"line":699,"address":[],"length":0,"stats":{"Line":0}},{"line":700,"address":[],"length":0,"stats":{"Line":0}},{"line":707,"address":[],"length":0,"stats":{"Line":1}},{"line":708,"address":[],"length":0,"stats":{"Line":0}},{"line":724,"address":[],"length":0,"stats":{"Line":0}},{"line":730,"address":[],"length":0,"stats":{"Line":0}},{"line":731,"address":[],"length":0,"stats":{"Line":0}},{"line":738,"address":[],"length":0,"stats":{"Line":1}},{"line":739,"address":[],"length":0,"stats":{"Line":0}},{"line":748,"address":[],"length":0,"stats":{"Line":0}},{"line":749,"address":[],"length":0,"stats":{"Line":0}},{"line":759,"address":[],"length":0,"stats":{"Line":0}},{"line":768,"address":[],"length":0,"stats":{"Line":0}},{"line":769,"address":[],"length":0,"stats":{"Line":0}},{"line":779,"address":[],"length":0,"stats":{"Line":0}},{"line":788,"address":[],"length":0,"stats":{"Line":0}},{"line":789,"address":[],"length":0,"stats":{"Line":0}},{"line":801,"address":[],"length":0,"stats":{"Line":0}},{"line":810,"address":[],"length":0,"stats":{"Line":0}},{"line":811,"address":[],"length":0,"stats":{"Line":0}},{"line":823,"address":[],"length":0,"stats":{"Line":0}},{"line":832,"address":[],"length":0,"stats":{"Line":0}},{"line":833,"address":[],"length":0,"stats":{"Line":0}},{"line":845,"address":[],"length":0,"stats":{"Line":0}}],"covered":49,"coverable":137},{"path":["/","home","botahamec","Projects","happylock","src","context.rs"],"content":"use std::marker::PhantomData;\n\nuse crate::ThreadKey;\n\nmod context;\nmod guard;\npub mod iterator;\npub mod tuple;\n\n/// Allows iterating over a lock collection, without locking every element at\n/// once.\n///\n/// Sometimes, partial allocation of locks is useful. For example, you may\n/// want to acquire a lock on the first element of a list before deciding if\n/// the second element should be locked. This function creates a\n/// [`LockContext`] which is capable of doing exactly that.\n///\n/// Upon using this context, the [`ThreadKey`] is stored inside this context.\n/// This ensures that nothing else, besides the types exposed by the context,\n/// can be locked until this context is dropped. To re-acquire the `ThreadKey`,\n/// call [`LockContext::unlock`].\n///\n/// A [`LockContext`] can be created by calling [`OwnedLockCollection::context`].\n///\n/// # Examples\n///\n/// Iterating through a tuple.\n///\n/// ```\n/// use happylock::{Mutex, ThreadKey};\n/// use happylock::collection::OwnedLockCollection;\n///\n/// let key = ThreadKey::get().unwrap();\n/// let data = (Mutex::new(true), Mutex::new(42), Mutex::new(67));\n/// let locks = OwnedLockCollection::new(data);\n/// let mut ctx = locks.context();\n/// let tuple = ctx.tuple(key);\n///\n/// let (use_other, tuple) = tuple.lock_0();\n/// let number = if **use_other {\n/// tuple.lock_2().0\n/// } else {\n/// tuple.lock_1().0\n/// };\n/// assert_eq!(**number, 67);\n/// ```\n///\n/// Iterating through a list\n///\n/// ```\n/// use happylock::{Mutex, ThreadKey};\n/// use happylock::collection::OwnedLockCollection;\n///\n/// let key = ThreadKey::get().unwrap();\n/// let data = [Mutex::new(1), Mutex::new(3), Mutex::new(8)];\n/// let locks = OwnedLockCollection::new(data);\n/// let mut ctx = locks.context();\n/// let mut iter = ctx.iter(key);\n///\n/// let mut sum = 0;\n/// while let Some(item) = iter.lock_next() {\n/// sum += **item;\n/// }\n///\n/// assert_eq!(sum, 12);\n/// ```\n///\n/// [`OwnedLockCollection::context`]: crate::collection::OwnedLockCollection::context\npub struct LockContext\u003c'l, L\u003e {\n\tkey: Option\u003cThreadKey\u003e,\n\tlockable: \u0026'l L,\n}\n\n/// Iterates through a collection of locks, allowing for partial allocation of\n/// locks, or for some locks to be skipped.\n///\n/// Sometimes, partial allocation of locks is useful. For example, you may\n/// want to acquire a lock on the first element of a list before deciding if\n/// the second element should be locked. If the list is iterable, then a\n/// [`LockingIterator`] is capable of doing exactly that.\n///\n/// A [`LockingIterator`] can be created by calling the [`LockContext::iter`]\n/// method.\n///\n/// # Example\n///\n/// ```\n/// use happylock::{Mutex, ThreadKey};\n/// use happylock::collection::OwnedLockCollection;\n///\n/// let key = ThreadKey::get().unwrap();\n/// let data = [Mutex::new(1), Mutex::new(3), Mutex::new(8)];\n/// let locks = OwnedLockCollection::new(data);\n/// let mut ctx = locks.context();\n/// let mut iter = ctx.iter(key);\n///\n/// let mut sum = 0;\n/// while let Some(item) = iter.lock_next() {\n/// sum += **item;\n/// }\n///\n/// assert_eq!(sum, 12);\n/// ```\npub struct LockingIterator\u003c'context, I, Outer = ()\u003e {\n\tkey: \u0026'context ThreadKey,\n\titerator: I,\n\touter: Outer,\n}\n\n/// Iterates through a tuple of locks, requiring that elements are only locked\n/// before any successive elements are locked.\n///\n/// Sometimes, partial allocation of locks is useful. For example, you may want\n/// to acquire a lock on one item before deciding if the second item should be\n/// locked. If the locks can be organized into a tuple, [`LockingTuple`] is\n/// capable of doing exactly that.\n///\n/// A [`LockingTuple`] can be created by calling the [`LockContext::iter`]\n/// method.\n///\n/// # Example\n///\n/// ```\n/// use happylock::{Mutex, ThreadKey};\n/// use happylock::collection::OwnedLockCollection;\n///\n/// let key = ThreadKey::get().unwrap();\n/// let data = (Mutex::new(true), Mutex::new(42), Mutex::new(67));\n/// let locks = OwnedLockCollection::new(data);\n/// let mut ctx = locks.context();\n/// let tuple = ctx.tuple(key);\n///\n/// let (use_other, tuple) = tuple.lock_0();\n/// let number = if **use_other {\n/// tuple.lock_2().0\n/// } else {\n/// tuple.lock_1().0\n/// };\n/// assert_eq!(**number, 67);\n/// ```\npub struct LockingTuple\u003c'context, L, C, Outer = ()\u003e {\n\t_lockable: PhantomData\u003cL\u003e,\n\tkey: \u0026'context ThreadKey,\n\ttuple: \u0026'context C,\n\touter: Outer,\n}\n\n/// An RAII implementation of a “scoped lock”. When this structure\n/// is dropped (falls out of scope), the lock will be unlocked.\n///\n/// The data protected by the mutex can be accessed through this guard via its\n/// [`Deref`] and [`DerefMut`] implementations.\n///\n/// This is created by calling the [`lock`] and [`try_lock`] methods on [`Mutex`]\n///\n/// Unlike other guards in this crate, this guard holds a reference to a\n/// [`ThreadKey`], which is stored in the [`LockContext`]. This ensures that\n/// context cannot be dropped until all guards created with the context are\n/// dropped. The `ThreadKey` can be re-acquired by calling\n/// [`LockContext::unlock`].\n///\n/// [`Mutex`]: `crate::mutex::Mutex`\n/// [`Deref`]: `std::ops::Deref`\n/// [`DerefMut`]: `std::ops::DerefMut`\n/// [`lock`]: `crate::mutex::Mutex::lock`\n/// [`try_lock`]: `crate::Mutex::try_lock`\npub struct ContextGuard\u003c'a, Guard, Key\u003e {\n\t_key: \u0026'a Key,\n\tguard: Guard,\n}\n\n#[cfg(test)]\nmod tests {\n\tuse crate::{\n\t\tcollection::OwnedLockCollection, context::iterator::TryLockNextError, Mutex, RwLock,\n\t\tThreadKey,\n\t};\n\n\t#[test]\n\tfn display_works_for_guard() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new((Mutex::new(\"Hello, world!\"),));\n\t\tlet mut context = collection.context();\n\t\tlet tuple = context.tuple(key);\n\t\tlet (guard, _) = tuple.lock_0();\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn try_lock_mut_works_single_element_tuple() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new((RwLock::new(42),));\n\t\tlet mut context = collection.context();\n\t\tlet mut tuple = context.tuple(key);\n\t\tlet mut guard = tuple.try_lock_mut_0().unwrap();\n\t\t**guard = 67;\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 context = collection.context();\n\t\t\t\tlet mut tuple = context.tuple(key);\n\t\t\t\tlet guard = tuple.try_read_mut_0();\n\t\t\t\tassert!(guard.is_none());\n\t\t\t});\n\t\t});\n\t\tdrop(guard);\n\n\t\tlet result = tuple.try_read_mut_0().unwrap();\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 context = collection.context();\n\t\t\t\tlet mut tuple = context.tuple(key);\n\t\t\t\tlet guard = tuple.try_lock_mut_0();\n\t\t\t\tassert!(guard.is_none());\n\t\t\t\tdrop(guard);\n\t\t\t\tlet guard = tuple.try_read_mut_0();\n\t\t\t\tassert!(guard.is_some());\n\t\t\t\tdrop(guard);\n\t\t\t});\n\t\t});\n\t\tassert_eq!(**result, 67);\n\t}\n\n\t#[test]\n\tfn try_lock_mut_works_double_element_tuple() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new((RwLock::new(42), RwLock::new(67)));\n\t\tlet mut context = collection.context();\n\t\tlet mut tuple = context.tuple(key);\n\n\t\tlet mut guard = tuple.try_lock_mut_0().unwrap();\n\t\t**guard *= 2;\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 context = collection.context();\n\t\t\t\tlet mut tuple = context.tuple(key);\n\t\t\t\tlet guard = tuple.try_read_mut_0();\n\t\t\t\tassert!(guard.is_none());\n\t\t\t});\n\t\t});\n\t\tdrop(guard);\n\t\tlet mut guard = tuple.try_lock_mut_1().unwrap();\n\t\t**guard *= 2;\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 context = collection.context();\n\t\t\t\tlet mut tuple = context.tuple(key);\n\t\t\t\tlet guard = tuple.try_read_mut_1();\n\t\t\t\tassert!(guard.is_none());\n\t\t\t});\n\t\t});\n\t\tdrop(guard);\n\n\t\tlet result = tuple.try_read_mut_0().unwrap();\n\t\tassert_eq!(**result, 84);\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 context = collection.context();\n\t\t\t\tlet mut tuple = context.tuple(key);\n\t\t\t\tlet guard = tuple.try_lock_mut_0();\n\t\t\t\tassert!(guard.is_none());\n\t\t\t\tdrop(guard);\n\t\t\t\tlet guard = tuple.try_read_mut_0();\n\t\t\t\tassert!(guard.is_some());\n\t\t\t\tdrop(guard);\n\t\t\t});\n\t\t});\n\t\tdrop(result);\n\t\tlet result = tuple.try_read_mut_1().unwrap();\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 context = collection.context();\n\t\t\t\tlet mut tuple = context.tuple(key);\n\t\t\t\tlet guard = tuple.try_lock_mut_1();\n\t\t\t\tassert!(guard.is_none());\n\t\t\t\tdrop(guard);\n\t\t\t\tlet guard = tuple.try_read_mut_1();\n\t\t\t\tassert!(guard.is_some());\n\t\t\t\tdrop(guard);\n\t\t\t});\n\t\t});\n\t\tassert_eq!(**result, 134);\n\t}\n\n\t#[test]\n\tfn try_lock_mut_works_triple_element_tuple() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new((RwLock::new(1), RwLock::new(2), RwLock::new(3)));\n\t\tlet mut context = collection.context();\n\t\tlet mut tuple = context.tuple(key);\n\n\t\tlet mut guard = tuple.try_lock_mut_0().unwrap();\n\t\t**guard *= 2;\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 context = collection.context();\n\t\t\t\tlet mut tuple = context.tuple(key);\n\t\t\t\tlet guard = tuple.try_read_mut_0();\n\t\t\t\tassert!(guard.is_none());\n\t\t\t});\n\t\t});\n\t\tdrop(guard);\n\t\tlet mut guard = tuple.try_lock_mut_1().unwrap();\n\t\t**guard *= 2;\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 context = collection.context();\n\t\t\t\tlet mut tuple = context.tuple(key);\n\t\t\t\tlet guard = tuple.try_read_mut_1();\n\t\t\t\tassert!(guard.is_none());\n\t\t\t});\n\t\t});\n\t\tdrop(guard);\n\t\tlet mut guard = tuple.try_lock_mut_2().unwrap();\n\t\t**guard *= 2;\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 context = collection.context();\n\t\t\t\tlet mut tuple = context.tuple(key);\n\t\t\t\tlet guard = tuple.try_read_mut_2();\n\t\t\t\tassert!(guard.is_none());\n\t\t\t});\n\t\t});\n\t\tdrop(guard);\n\n\t\tlet result = tuple.try_read_mut_0().unwrap();\n\t\tassert_eq!(**result, 2);\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 context = collection.context();\n\t\t\t\tlet mut tuple = context.tuple(key);\n\t\t\t\tlet guard = tuple.try_lock_mut_0();\n\t\t\t\tassert!(guard.is_none());\n\t\t\t\tdrop(guard);\n\t\t\t\tlet guard = tuple.try_read_mut_0();\n\t\t\t\tassert!(guard.is_some());\n\t\t\t\tdrop(guard);\n\t\t\t});\n\t\t});\n\t\tdrop(result);\n\t\tlet result = tuple.try_read_mut_1().unwrap();\n\t\tassert_eq!(**result, 4);\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 context = collection.context();\n\t\t\t\tlet mut tuple = context.tuple(key);\n\t\t\t\tlet guard = tuple.try_lock_mut_1();\n\t\t\t\tassert!(guard.is_none());\n\t\t\t\tdrop(guard);\n\t\t\t\tlet guard = tuple.try_read_mut_1();\n\t\t\t\tassert!(guard.is_some());\n\t\t\t\tdrop(guard);\n\t\t\t});\n\t\t});\n\t\tdrop(result);\n\t\tlet result = tuple.try_read_mut_2().unwrap();\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 context = collection.context();\n\t\t\t\tlet mut tuple = context.tuple(key);\n\t\t\t\tlet guard = tuple.try_lock_mut_2();\n\t\t\t\tassert!(guard.is_none());\n\t\t\t\tdrop(guard);\n\t\t\t\tlet guard = tuple.try_read_mut_2();\n\t\t\t\tassert!(guard.is_some());\n\t\t\t\tdrop(guard);\n\t\t\t});\n\t\t});\n\t\tassert_eq!(**result, 6);\n\t}\n\n\t#[test]\n\tfn basic_iteration() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet data = [Mutex::new(1), Mutex::new(3), Mutex::new(8)];\n\t\tlet locks = OwnedLockCollection::new(data);\n\t\tlet mut ctx = locks.context();\n\t\tlet mut iter = ctx.iter(key);\n\n\t\tlet item = iter.lock_next().unwrap();\n\t\tassert_eq!(**item, 1);\n\t\tlet item = iter.lock_next().unwrap();\n\t\tassert_eq!(**item, 3);\n\t\tlet item = iter.lock_next().unwrap();\n\t\tassert_eq!(**item, 8);\n\t\tassert!(iter.lock_next().is_none());\n\t}\n\n\t#[test]\n\tfn recurse_tuple_of_lists() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet data = (\n\t\t\t[Mutex::new(1), Mutex::new(2), Mutex::new(3)],\n\t\t\tMutex::new(true),\n\t\t);\n\t\tlet locks = OwnedLockCollection::new(data);\n\t\tlet mut ctx = locks.context();\n\t\tlet tuple = ctx.tuple(key);\n\t\tlet mut iter = tuple.recurse_0_iter();\n\n\t\tlet item = iter.lock_next().unwrap();\n\t\tassert_eq!(**item, 1);\n\t\tlet item = iter.lock_next().unwrap();\n\t\tassert_eq!(**item, 2);\n\t\tlet item = iter.lock_next().unwrap();\n\t\tassert_eq!(**item, 3);\n\t\tassert!(iter.lock_next().is_none());\n\n\t\tlet tuple = iter.exit();\n\t\tlet (should_assert, _) = tuple.lock_1();\n\t\tassert!(**should_assert);\n\t}\n\n\t#[test]\n\tfn recurse_list_of_lists() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet data = [\n\t\t\t[Mutex::new(1), Mutex::new(2), Mutex::new(3)],\n\t\t\t[Mutex::new(4), Mutex::new(5), Mutex::new(6)],\n\t\t];\n\t\tlet locks = OwnedLockCollection::new(data);\n\t\tlet mut ctx = locks.context();\n\t\tlet mut iter = ctx.iter(key);\n\n\t\tlet mut list = iter.recurse_next().unwrap();\n\t\tlet item = list.lock_next().unwrap();\n\t\tassert_eq!(**item, 1);\n\t\tlet item = list.lock_next().unwrap();\n\t\tassert_eq!(**item, 2);\n\t\tlet item = list.lock_next().unwrap();\n\t\tassert_eq!(**item, 3);\n\t\tassert!(list.lock_next().is_none());\n\t\titer = list.exit();\n\t\tlet mut list = iter.recurse_next().unwrap();\n\t\tlet item = list.lock_next().unwrap();\n\t\tassert_eq!(**item, 4);\n\t\tlet item = list.lock_next().unwrap();\n\t\tassert_eq!(**item, 5);\n\t\tlet item = list.lock_next().unwrap();\n\t\tassert_eq!(**item, 6);\n\t\tassert!(list.lock_next().is_none());\n\t}\n\n\t#[test]\n\tfn recurse_last_list_of_lists() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet data = [\n\t\t\t[Mutex::new(1), Mutex::new(2), Mutex::new(3)],\n\t\t\t[Mutex::new(4), Mutex::new(5), Mutex::new(6)],\n\t\t];\n\t\tlet locks = OwnedLockCollection::new(data);\n\t\tlet mut ctx = locks.context();\n\t\tlet iter = ctx.iter(key);\n\n\t\tlet mut list = iter.recurse_last().unwrap();\n\t\tlet item = list.lock_next().unwrap();\n\t\tassert_eq!(**item, 4);\n\t\tlet item = list.lock_next().unwrap();\n\t\tassert_eq!(**item, 5);\n\t\tlet item = list.lock_next().unwrap();\n\t\tassert_eq!(**item, 6);\n\t\tassert!(list.lock_next().is_none());\n\t}\n\n\t#[test]\n\tfn recurse_last_list_of_empty_list() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet data: [[Mutex\u003ci32\u003e; 0]; 0] = [];\n\t\tlet locks = OwnedLockCollection::new(data);\n\t\tlet mut ctx = locks.context();\n\t\tlet iter = ctx.iter(key);\n\n\t\tlet list = iter.recurse_last();\n\t\tassert!(list.is_none())\n\t}\n\n\t#[test]\n\tfn recurse_list_of_tuples() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet data = [\n\t\t\t(Mutex::new(true), Mutex::new(1)),\n\t\t\t(Mutex::new(false), Mutex::new(2)),\n\t\t\t(Mutex::new(true), Mutex::new(3)),\n\t\t];\n\t\tlet locks = OwnedLockCollection::new(data);\n\t\tlet mut ctx = locks.context();\n\t\tlet mut iter = ctx.iter(key);\n\n\t\tlet tuple = iter.recurse_next_tuple().unwrap();\n\t\tlet (should_count, mut tuple) = tuple.lock_0();\n\t\tassert!(**should_count);\n\t\tlet num = tuple.lock_mut_1();\n\t\tassert_eq!(**num, 1);\n\t\tdrop(num);\n\t\titer = tuple.exit();\n\t\tlet tuple = iter.recurse_next_tuple().unwrap();\n\t\tlet (should_count, mut tuple) = tuple.lock_0();\n\t\tassert!(!**should_count);\n\t\tlet num = tuple.lock_mut_1();\n\t\tassert_eq!(**num, 2);\n\t\tdrop(num);\n\t\titer = tuple.exit();\n\t\tlet tuple = iter.recurse_next_tuple().unwrap();\n\t\tlet (should_count, mut tuple) = tuple.lock_0();\n\t\tassert!(**should_count);\n\t\tlet num = tuple.lock_mut_1();\n\t\tassert_eq!(**num, 3);\n\t\tdrop(num);\n\t\titer = tuple.exit();\n\t\tassert!(iter.recurse_next_tuple().is_none());\n\t}\n\n\t#[test]\n\tfn recurse_last_list_of_tuples() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet data = [\n\t\t\t(Mutex::new(true), Mutex::new(1)),\n\t\t\t(Mutex::new(false), Mutex::new(2)),\n\t\t\t(Mutex::new(true), Mutex::new(3)),\n\t\t];\n\t\tlet locks = OwnedLockCollection::new(data);\n\t\tlet mut ctx = locks.context();\n\t\tlet iter = ctx.iter(key);\n\n\t\tlet tuple = iter.recurse_last_tuple().unwrap();\n\t\tlet (should_count, mut tuple) = tuple.lock_0();\n\t\tif **should_count {\n\t\t\tlet num = tuple.lock_mut_1();\n\t\t\tassert_eq!(**num, 3);\n\t\t} else {\n\t\t\tpanic!();\n\t\t}\n\t}\n\n\t#[test]\n\tfn recurse_last_of_empty_list_of_tuples() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet data: [(Mutex\u003cbool\u003e, Mutex\u003ci32\u003e); 0] = [];\n\t\tlet locks = OwnedLockCollection::new(data);\n\t\tlet mut ctx = locks.context();\n\t\tlet iter = ctx.iter(key);\n\n\t\tlet tuple = iter.recurse_last_tuple();\n\t\tassert!(tuple.is_none());\n\t}\n\n\t#[test]\n\tfn lock_last_of_list() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet data = [Mutex::new(1), Mutex::new(3), Mutex::new(8)];\n\t\tlet locks = OwnedLockCollection::new(data);\n\t\tlet mut ctx = locks.context();\n\t\tlet iter = ctx.iter(key);\n\t\tlet last = iter.lock_last().unwrap();\n\t\tassert_eq!(**last, 8);\n\t}\n\n\t#[test]\n\tfn try_lock_next_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet data = [Mutex::new(1), Mutex::new(3), Mutex::new(8)];\n\t\tlet locks = OwnedLockCollection::new(data);\n\t\tlet mut ctx = locks.context();\n\t\tlet mut iter = ctx.iter(key).peekable();\n\n\t\tlet item = iter.try_lock_next().unwrap();\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 context = locks.context();\n\t\t\t\tlet mut iter = context.iter(key).peekable();\n\t\t\t\tlet guard = iter.try_lock_next();\n\t\t\t\tassert_eq!(guard.unwrap_err(), TryLockNextError::WouldBlock);\n\t\t\t});\n\t\t});\n\t\tassert_eq!(**item, 1);\n\t\tlet item = iter.try_lock_next().unwrap();\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 context = locks.context();\n\t\t\t\tlet mut iter = context.iter(key).peekable();\n\t\t\t\titer.skip_next();\n\t\t\t\tlet guard = iter.try_lock_next();\n\t\t\t\tassert_eq!(guard.unwrap_err(), TryLockNextError::WouldBlock);\n\t\t\t});\n\t\t});\n\t\tassert_eq!(**item, 3);\n\t\tlet item = iter.try_lock_next().unwrap();\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 context = locks.context();\n\t\t\t\tlet mut iter = context.iter(key).peekable();\n\t\t\t\titer.skip_mut(2);\n\t\t\t\tlet guard = iter.try_lock_next();\n\t\t\t\tassert_eq!(guard.unwrap_err(), TryLockNextError::WouldBlock);\n\t\t\t});\n\t\t});\n\t\tassert_eq!(**item, 8);\n\t\tassert_eq!(\n\t\t\titer.try_lock_next().unwrap_err(),\n\t\t\tTryLockNextError::FinishedIteration\n\t\t);\n\t}\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":[648224,648672,648785,648053,648653,648800,648913,648465,647904,648352,648480,648080,648337,648201],"length":1,"stats":{"Line":207}},{"line":8,"address":[630162,630674,629467,629906,630546,630930,629625,629769,630034,630290,630418,631058,630802],"length":1,"stats":{"Line":185}},{"line":9,"address":[993649,994416,994020,993783,994952,994560,994664,994544,994992,994704,994364,995108,995120,993844,995236,994520,994688,994808,993367,993575,993441,994832,993959,994848,994976,995212,994204,994299,995084,994139],"length":1,"stats":{"Line":423}},{"line":10,"address":[665443,667507,667379,666083,666339,665955,667123,667251,665827,666717,665571,665699,666211,666467,666995,667635,666595,667763,666867],"length":1,"stats":{"Line":28}},{"line":11,"address":[647421,647165,648701,648189,649085,648061,648829,646909,647677,647549,648317,647805,648573,647293,648957,646785,648445,647933,647037],"length":1,"stats":{"Line":25}}],"covered":5,"coverable":5},{"path":["/","home","botahamec","Projects","happylock","src","key.rs"],"content":"use std::cell::{Cell, LazyCell};\nuse std::fmt::{self, Debug};\nuse std::marker::PhantomData;\n\nuse sealed::Sealed;\n\n// Sealed to prevent other key types from being implemented. Otherwise, this\n// would almost instant undefined behavior.\nmod sealed {\n\tuse super::ThreadKey;\n\n\tpub trait Sealed {}\n\timpl Sealed for ThreadKey {}\n\timpl Sealed for \u0026mut ThreadKey {}\n}\n\nthread_local! {\n\tstatic KEY: LazyCell\u003cKeyCell\u003e = LazyCell::new(KeyCell::default);\n}\n\n/// The key for the current thread.\n///\n/// Only one of these exist per thread. To get the current thread's key, call\n/// [`ThreadKey::get`]. If the `ThreadKey` is dropped, it can be re-obtained.\npub struct ThreadKey {\n\tphantom: PhantomData\u003c*const ()\u003e, // implement !Send and !Sync\n}\n\n/// Allows the type to be used as a key for a scoped 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// 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":[976776],"length":1,"stats":{"Line":24}},{"line":55,"address":[990464],"length":1,"stats":{"Line":11}},{"line":58,"address":[945445],"length":1,"stats":{"Line":39}},{"line":77,"address":[941456],"length":1,"stats":{"Line":23}},{"line":80,"address":[1002112],"length":1,"stats":{"Line":42}},{"line":81,"address":[946841],"length":1,"stats":{"Line":23}},{"line":97,"address":[976736],"length":1,"stats":{"Line":20}},{"line":98,"address":[982325],"length":1,"stats":{"Line":22}},{"line":103,"address":[976720],"length":1,"stats":{"Line":12}},{"line":104,"address":[850757],"length":1,"stats":{"Line":14}}],"covered":10,"coverable":10},{"path":["/","home","botahamec","Projects","happylock","src","lib.rs"],"content":"#![warn(clippy::pedantic)]\n#![warn(clippy::nursery)]\n#![warn(clippy::cargo)]\n#![warn(clippy::allow_attributes)]\n#![warn(clippy::as_pointer_underscore)]\n#![warn(clippy::cognitive_complexity)]\n#![warn(clippy::dbg_macro)]\n#![warn(clippy::error_impl_error)]\n#![warn(clippy::exit)]\n#![warn(clippy::fn_to_numeric_cast_any)]\n#![warn(clippy::infinite_loop)]\n#![warn(clippy::lossy_float_literal)]\n#![warn(clippy::mixed_read_write_in_expression)]\n#![warn(clippy::mod_module_files)]\n#![warn(clippy::needless_raw_strings)]\n#![warn(clippy::non_zero_suggestions)]\n#![warn(clippy::print_stdout)]\n#![warn(clippy::print_stderr)]\n#![warn(clippy::redundant_test_prefix)]\n#![warn(clippy::redundant_type_annotations)]\n#![warn(clippy::string_add)]\n#![warn(clippy::string_lit_chars_any)]\n#![warn(clippy::tests_outside_test_module)]\n#![warn(clippy::todo)]\n#![warn(clippy::try_err)]\n#![warn(clippy::unimplemented)]\n#![warn(clippy::unnecessary_safety_comment)]\n#![warn(clippy::unnecessary_safety_doc)]\n#![warn(clippy::unseparated_literal_suffix)]\n#![warn(clippy::unused_result_ok)]\n#![warn(clippy::unused_trait_names)]\n#![warn(clippy::unwrap_in_result)]\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 context;\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 [`context::LockContext`]\npub type LockContext\u003c'l, L\u003e = context::LockContext\u003c'l, L\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":220,"address":[841568],"length":1,"stats":{"Line":10}},{"line":231,"address":[842054],"length":1,"stats":{"Line":10}}],"covered":2,"coverable":2},{"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 when using these methods correctly.\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 lock is\n\t/// held.\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\tunsafe fn raw_write(\u0026self);\n\n\t/// Attempt to lock without blocking.\n\t///\n\t/// Returns `true` if successful, `false` otherwise.\n\t///\n\t/// # Safety\n\t///\n\t/// It is undefined behavior to use this without ownership or mutable\n\t/// access to the [`ThreadKey`], which should last as long as the lock is\n\t/// held.\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool;\n\n\t/// Releases the lock\n\t///\n\t/// # Safety\n\t///\n\t/// It is undefined behavior to use this if the lock is not acquired by the\n\t/// calling thread.\n\tunsafe fn raw_unlock_write(\u0026self);\n\n\t/// Blocks until the data the lock protects can be safely read.\n\t///\n\t/// Some locks, but not all, will allow multiple readers at once. If\n\t/// multiple readers are allowed for a [`Lockable`] type, then the\n\t/// [`Sharable`] marker trait should be implemented.\n\t///\n\t/// # Safety\n\t///\n\t/// It is undefined behavior to use this without ownership or mutable\n\t/// access to the [`ThreadKey`], which should last as long as the lock is\n\t/// held.\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 lock is\n\t/// held.\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 held by the\n\t/// calling thread.\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.\n///\n/// The list returned by `get_ptrs` must contain any lock which could possibly\n/// be referenced in another collection.\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\t/// A reference to the protected data\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/// Returns a mutable reference to the data protected by this lock.\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 unlocked\n\t/// until the lifetime of this reference ends.\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\t/// An immutable reference to the protected data\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/// Creates an immutable reference to the data that is protected by this lock.\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 the lifetime of this reference ends.\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.\n///\n/// The implementation of [`Lockable::get_ptrs`] must return the locks in the\n/// same order that they would be locked in if this lockable were passed into a\n/// [`LockContext`].\n///\n/// [`LockContext`]: `crate::context::LockContext`\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($(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);\ntuple_impls!(A B C D E F G H, 0 1 2 3 4 5 6 7);\ntuple_impls!(A B C D E F G H I, 0 1 2 3 4 5 6 7 8);\ntuple_impls!(A B C D E F G H I J, 0 1 2 3 4 5 6 7 8 9);\ntuple_impls!(A B C D E F G H I J K, 0 1 2 3 4 5 6 7 8 9 10);\ntuple_impls!(A B C D E F G H I J K L, 0 1 2 3 4 5 6 7 8 9 10 11);\ntuple_impls!(A B C D E F G H I J K L M, 0 1 2 3 4 5 6 7 8 9 10 11 12);\ntuple_impls!(A B C D E F G H I J K L M N, 0 1 2 3 4 5 6 7 8 9 10 11 12 13);\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\nimpl\u003cT: LockableIntoInner + 'static\u003e LockableIntoInner for Box\u003c[T]\u003e {\n\ttype Inner = Box\u003c[T::Inner]\u003e;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tSelf::into_iter(self)\n\t\t\t.map(LockableIntoInner::into_inner)\n\t\t\t.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: 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\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\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\nimpl\u003cT: LockableGetMut + 'static\u003e LockableGetMut for Vec\u003cT\u003e {\n\ttype Inner\u003c'a\u003e\n\t\t= Box\u003c[T::Inner\u003c'a\u003e]\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tself.iter_mut().map(LockableGetMut::get_mut).collect()\n\t}\n}\n\nimpl\u003cT: LockableIntoInner\u003e LockableIntoInner for Vec\u003cT\u003e {\n\ttype Inner = Box\u003c[T::Inner]\u003e;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tself.into_iter()\n\t\t\t.map(LockableIntoInner::into_inner)\n\t\t\t.collect()\n\t}\n}\n\nunsafe impl\u003cT: OwnedLockable\u003e OwnedLockable for Vec\u003cT\u003e {}\n\n#[cfg(test)]\nmod tests {\n\tuse super::*;\n\tuse crate::{LockCollection, Mutex, RwLock, ThreadKey};\n\n\t#[test]\n\tfn mut_ref_get_ptrs() {\n\t\tlet mut rwlock = RwLock::new(5);\n\t\tlet mutref = \u0026mut rwlock;\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tmutref.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tassert!(std::ptr::addr_eq(lock_ptrs[0], mutref));\n\t}\n\n\t#[test]\n\tfn array_get_ptrs_empty() {\n\t\tlet locks: [Mutex\u003c()\u003e; 0] = [];\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert!(lock_ptrs.is_empty());\n\t}\n\n\t#[test]\n\tfn array_get_ptrs_length_one() {\n\t\tlet locks: [Mutex\u003ci32\u003e; 1] = [Mutex::new(1)];\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[0], locks[0].raw())) }\n\t}\n\n\t#[test]\n\tfn array_get_ptrs_length_two() {\n\t\tlet locks: [Mutex\u003ci32\u003e; 2] = [Mutex::new(1), Mutex::new(2)];\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 2);\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[0], locks[0].raw())) }\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[1], locks[1].raw())) }\n\t}\n\n\t#[test]\n\tfn vec_get_ptrs_empty() {\n\t\tlet locks: Vec\u003cMutex\u003c()\u003e\u003e = Vec::new();\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert!(lock_ptrs.is_empty());\n\t}\n\n\t#[test]\n\tfn vec_get_ptrs_length_one() {\n\t\tlet locks: Vec\u003cMutex\u003ci32\u003e\u003e = vec![Mutex::new(1)];\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[0], locks[0].raw())) }\n\t}\n\n\t#[test]\n\tfn vec_get_ptrs_length_two() {\n\t\tlet locks: Vec\u003cMutex\u003ci32\u003e\u003e = vec![Mutex::new(1), Mutex::new(2)];\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 2);\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[0], locks[0].raw())) }\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[1], locks[1].raw())) }\n\t}\n\n\t#[test]\n\tfn vec_as_mut() {\n\t\tlet mut locks: Vec\u003cMutex\u003ci32\u003e\u003e = vec![Mutex::new(1), Mutex::new(2)];\n\t\tlet lock_ptrs = LockableGetMut::get_mut(\u0026mut locks);\n\n\t\tassert_eq!(lock_ptrs.len(), 2);\n\t\tassert_eq!(*lock_ptrs[0], 1);\n\t\tassert_eq!(*lock_ptrs[1], 2);\n\t}\n\n\t#[test]\n\tfn vec_into_inner() {\n\t\tlet locks: Vec\u003cMutex\u003ci32\u003e\u003e = vec![Mutex::new(1), Mutex::new(2)];\n\t\tlet lock_ptrs = LockableIntoInner::into_inner(locks);\n\n\t\tassert_eq!(lock_ptrs.len(), 2);\n\t\tassert_eq!(lock_ptrs[0], 1);\n\t\tassert_eq!(lock_ptrs[1], 2);\n\t}\n\n\t#[test]\n\tfn vec_guard_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = vec![RwLock::new(1), RwLock::new(2)];\n\t\tlet collection = LockCollection::new(locks);\n\n\t\tlet mut guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], 1);\n\t\tassert_eq!(*guard[1], 2);\n\t\t*guard[0] = 3;\n\n\t\tlet key = LockCollection::\u003cVec\u003cRwLock\u003c_\u003e\u003e\u003e::unlock(guard);\n\t\tlet guard = collection.read(key);\n\t\tassert_eq!(*guard[0], 3);\n\t\tassert_eq!(*guard[1], 2);\n\t}\n\n\t#[test]\n\tfn vec_data_mut() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = vec![Mutex::new(1), Mutex::new(2)];\n\t\tlet collection = LockCollection::new(mutexes);\n\t\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 1);\n\t\t\tassert_eq!(*guard[1], 2);\n\t\t\t*guard[0] = 3;\n\t\t});\n\n\t\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 3);\n\t\t\tassert_eq!(*guard[1], 2);\n\t\t})\n\t}\n\n\t#[test]\n\tfn vec_data_ref() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = vec![RwLock::new(1), RwLock::new(2)];\n\t\tlet collection = LockCollection::new(mutexes);\n\t\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 1);\n\t\t\tassert_eq!(*guard[1], 2);\n\t\t\t*guard[0] = 3;\n\t\t});\n\n\t\tcollection.scoped_read(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 3);\n\t\t\tassert_eq!(*guard[1], 2);\n\t\t})\n\t}\n\n\t#[test]\n\tfn box_get_ptrs_empty() {\n\t\tlet locks: Box\u003c[Mutex\u003c()\u003e]\u003e = Box::from([]);\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert!(lock_ptrs.is_empty());\n\t}\n\n\t#[test]\n\tfn box_get_ptrs_length_one() {\n\t\tlet locks: Box\u003c[Mutex\u003ci32\u003e]\u003e = vec![Mutex::new(1)].into_boxed_slice();\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[0], locks[0].raw())) }\n\t}\n\n\t#[test]\n\tfn box_get_ptrs_length_two() {\n\t\tlet locks: Box\u003c[Mutex\u003ci32\u003e]\u003e = vec![Mutex::new(1), Mutex::new(2)].into_boxed_slice();\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 2);\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[0], locks[0].raw())) }\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[1], locks[1].raw())) }\n\t}\n\n\t#[test]\n\tfn box_as_mut() {\n\t\tlet mut locks: Box\u003c[Mutex\u003ci32\u003e]\u003e = vec![Mutex::new(1), Mutex::new(2)].into_boxed_slice();\n\t\tlet lock_ptrs = LockableGetMut::get_mut(\u0026mut locks);\n\n\t\tassert_eq!(lock_ptrs.len(), 2);\n\t\tassert_eq!(*lock_ptrs[0], 1);\n\t\tassert_eq!(*lock_ptrs[1], 2);\n\t}\n\n\t#[test]\n\tfn box_guard_mut() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet x = [Mutex::new(1), Mutex::new(2)];\n\t\tlet collection: LockCollection\u003cBox\u003c[Mutex\u003c_\u003e]\u003e\u003e = LockCollection::new(Box::new(x));\n\n\t\tlet mut guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], 1);\n\t\tassert_eq!(*guard[1], 2);\n\t\t*guard[0] = 3;\n\n\t\tlet key = LockCollection::\u003cBox\u003c[Mutex\u003c_\u003e]\u003e\u003e::unlock(guard);\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], 3);\n\t\tassert_eq!(*guard[1], 2);\n\t}\n\n\t#[test]\n\tfn box_data_mut() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = vec![Mutex::new(1), Mutex::new(2)].into_boxed_slice();\n\t\tlet collection = LockCollection::new(mutexes);\n\t\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 1);\n\t\t\tassert_eq!(*guard[1], 2);\n\t\t\t*guard[0] = 3;\n\t\t});\n\n\t\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 3);\n\t\t\tassert_eq!(*guard[1], 2);\n\t\t});\n\t}\n\n\t#[test]\n\tfn box_guard_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = [RwLock::new(1), RwLock::new(2)];\n\t\tlet collection: LockCollection\u003cBox\u003c[RwLock\u003c_\u003e]\u003e\u003e = LockCollection::new(Box::new(locks));\n\n\t\tlet mut guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], 1);\n\t\tassert_eq!(*guard[1], 2);\n\t\t*guard[0] = 3;\n\n\t\tlet key = LockCollection::\u003cBox\u003c[RwLock\u003c_\u003e]\u003e\u003e::unlock(guard);\n\t\tlet guard = collection.read(key);\n\t\tassert_eq!(*guard[0], 3);\n\t\tassert_eq!(*guard[1], 2);\n\t}\n\n\t#[test]\n\tfn box_data_ref() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = vec![RwLock::new(1), RwLock::new(2)].into_boxed_slice();\n\t\tlet collection = LockCollection::new(mutexes);\n\t\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 1);\n\t\t\tassert_eq!(*guard[1], 2);\n\t\t\t*guard[0] = 3;\n\t\t});\n\n\t\tcollection.scoped_read(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 3);\n\t\t\tassert_eq!(*guard[1], 2);\n\t\t});\n\t}\n\n\t#[test]\n\tfn box_into_inner() {\n\t\tlet locks = vec![Mutex::new(1), Mutex::new(2)].into_boxed_slice();\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","traces":[{"line":257,"address":[1658368],"length":1,"stats":{"Line":51}},{"line":258,"address":[625694,625742,625774],"length":1,"stats":{"Line":51}},{"line":261,"address":[672256,672304],"length":1,"stats":{"Line":10}},{"line":262,"address":[650373,650277,650325],"length":1,"stats":{"Line":10}},{"line":265,"address":[646320],"length":1,"stats":{"Line":2}},{"line":266,"address":[],"length":0,"stats":{"Line":2}},{"line":281,"address":[702160,702176,702144],"length":1,"stats":{"Line":3}},{"line":282,"address":[],"length":0,"stats":{"Line":3}},{"line":285,"address":[],"length":0,"stats":{"Line":0}},{"line":286,"address":[],"length":0,"stats":{"Line":0}},{"line":301,"address":[],"length":0,"stats":{"Line":1}},{"line":302,"address":[],"length":0,"stats":{"Line":1}},{"line":305,"address":[],"length":0,"stats":{"Line":0}},{"line":306,"address":[],"length":0,"stats":{"Line":0}},{"line":309,"address":[],"length":0,"stats":{"Line":0}},{"line":310,"address":[],"length":0,"stats":{"Line":0}},{"line":320,"address":[],"length":0,"stats":{"Line":0}},{"line":321,"address":[],"length":0,"stats":{"Line":0}},{"line":336,"address":[],"length":0,"stats":{"Line":0}},{"line":337,"address":[],"length":0,"stats":{"Line":0}},{"line":340,"address":[],"length":0,"stats":{"Line":0}},{"line":341,"address":[],"length":0,"stats":{"Line":0}},{"line":356,"address":[2266336,2266576,2266512],"length":1,"stats":{"Line":20}},{"line":357,"address":[2054984,2054568,2055704,2054632,2054808],"length":1,"stats":{"Line":21}},{"line":360,"address":[673408,673599,673605],"length":1,"stats":{"Line":9}},{"line":361,"address":[651520],"length":1,"stats":{"Line":9}},{"line":380,"address":[2267184,2267531],"length":1,"stats":{"Line":4}},{"line":381,"address":[2055868,2055782,2056102,2056220,2055446,2055551],"length":1,"stats":{"Line":8}},{"line":390,"address":[683359,683365,683168],"length":1,"stats":{"Line":2}},{"line":391,"address":[2266974,2267086,2266862],"length":1,"stats":{"Line":2}},{"line":429,"address":[],"length":0,"stats":{"Line":18}},{"line":430,"address":[2261025,2261777,2262194,2262209,2261762,2261010],"length":1,"stats":{"Line":36}},{"line":431,"address":[2048378,2047514,2048490,2047626,2046522,2048922,2046954,2047402],"length":1,"stats":{"Line":16}},{"line":435,"address":[],"length":0,"stats":{"Line":8}},{"line":438,"address":[2260766,2261131,2261883],"length":1,"stats":{"Line":8}},{"line":439,"address":[2260806,2261163,2261188,2261915,2260783,2261940],"length":1,"stats":{"Line":16}},{"line":440,"address":[],"length":0,"stats":{"Line":14}},{"line":443,"address":[1621337,1621241,1621312,1621168,1621216,1621289,1621600,1621552,1621456,1621408,1621577,1621481,1621625,1621193,1621433,1621264],"length":1,"stats":{"Line":25}},{"line":446,"address":[2261424],"length":1,"stats":{"Line":3}},{"line":447,"address":[647150,646715],"length":1,"stats":{"Line":3}},{"line":448,"address":[647182,646747,646772,647207],"length":1,"stats":{"Line":6}},{"line":449,"address":[2048163,2048102],"length":1,"stats":{"Line":6}},{"line":452,"address":[2048115],"length":1,"stats":{"Line":9}},{"line":462,"address":[2049184],"length":1,"stats":{"Line":2}},{"line":464,"address":[],"length":0,"stats":{"Line":2}},{"line":465,"address":[2049328,2049251],"length":1,"stats":{"Line":4}},{"line":466,"address":[2262625,2262550],"length":1,"stats":{"Line":4}},{"line":469,"address":[2262563],"length":1,"stats":{"Line":6}},{"line":477,"address":[],"length":0,"stats":{"Line":1}},{"line":479,"address":[],"length":0,"stats":{"Line":2}},{"line":480,"address":[],"length":0,"stats":{"Line":3}},{"line":481,"address":[2050191,2050350,2050114],"length":1,"stats":{"Line":3}},{"line":484,"address":[2050145],"length":1,"stats":{"Line":3}},{"line":500,"address":[],"length":0,"stats":{"Line":4}},{"line":501,"address":[],"length":0,"stats":{"Line":4}},{"line":502,"address":[2263163,2262918,2263803,2262895,2263188,2263828],"length":1,"stats":{"Line":8}},{"line":503,"address":[2262988,2263238,2263299,2263878,2262965,2263939],"length":1,"stats":{"Line":6}},{"line":506,"address":[613846],"length":1,"stats":{"Line":10}},{"line":509,"address":[],"length":0,"stats":{"Line":1}},{"line":510,"address":[],"length":0,"stats":{"Line":1}},{"line":511,"address":[2263483,2263508],"length":1,"stats":{"Line":2}},{"line":512,"address":[],"length":0,"stats":{"Line":2}},{"line":515,"address":[2263571],"length":1,"stats":{"Line":3}},{"line":532,"address":[],"length":0,"stats":{"Line":3}},{"line":533,"address":[],"length":0,"stats":{"Line":7}},{"line":534,"address":[],"length":0,"stats":{"Line":2}},{"line":538,"address":[],"length":0,"stats":{"Line":2}},{"line":539,"address":[1622121,1622096,1622000,1622025],"length":1,"stats":{"Line":6}},{"line":542,"address":[],"length":0,"stats":{"Line":2}},{"line":543,"address":[1622073,1622169,1622144,1622048],"length":1,"stats":{"Line":6}},{"line":553,"address":[],"length":0,"stats":{"Line":1}},{"line":554,"address":[],"length":0,"stats":{"Line":1}},{"line":561,"address":[],"length":0,"stats":{"Line":1}},{"line":562,"address":[2353134],"length":1,"stats":{"Line":1}},{"line":563,"address":[],"length":0,"stats":{"Line":1}},{"line":579,"address":[],"length":0,"stats":{"Line":1}},{"line":580,"address":[2353192],"length":1,"stats":{"Line":3}},{"line":583,"address":[],"length":0,"stats":{"Line":1}},{"line":584,"address":[],"length":0,"stats":{"Line":3}},{"line":600,"address":[],"length":0,"stats":{"Line":4}},{"line":601,"address":[],"length":0,"stats":{"Line":10}},{"line":602,"address":[],"length":0,"stats":{"Line":4}},{"line":606,"address":[],"length":0,"stats":{"Line":2}},{"line":607,"address":[1210949,1211381],"length":1,"stats":{"Line":6}},{"line":610,"address":[],"length":0,"stats":{"Line":2}},{"line":611,"address":[],"length":0,"stats":{"Line":6}},{"line":626,"address":[1211584],"length":1,"stats":{"Line":1}},{"line":627,"address":[],"length":0,"stats":{"Line":3}},{"line":630,"address":[],"length":0,"stats":{"Line":1}},{"line":631,"address":[],"length":0,"stats":{"Line":3}},{"line":646,"address":[],"length":0,"stats":{"Line":2}},{"line":647,"address":[],"length":0,"stats":{"Line":2}},{"line":654,"address":[],"length":0,"stats":{"Line":1}},{"line":655,"address":[],"length":0,"stats":{"Line":1}},{"line":656,"address":[],"length":0,"stats":{"Line":1}}],"covered":83,"coverable":95},{"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 as _;\nuse crate::ThreadKey;\n\nuse super::{Mutex, MutexGuard, MutexRef};\n\n// These impls make things slightly easier because now you can use\n// `println!(\"{guard}\")` instead of `println!(\"{}\", *guard)`\n\n#[mutants::skip] // hashing involves RNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Hash + ?Sized, R: RawMutex\u003e Hash for MutexRef\u003c'_, T, R\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.deref().hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug + ?Sized, R: RawMutex\u003e Debug for MutexRef\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: Display + ?Sized, R: RawMutex\u003e Display for MutexRef\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e Drop for MutexRef\u003c'_, T, R\u003e {\n\tfn drop(\u0026mut self) {\n\t\t// safety: this guard is being destroyed, so the data cannot be\n\t\t// accessed without locking again\n\t\tunsafe { self.0.raw_unlock_write() }\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e Deref for MutexRef\u003c'_, T, R\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t// safety: this is the only type that can use `value`, and there's\n\t\t// a reference to this type, so there cannot be any mutable\n\t\t// references to this value.\n\t\tunsafe { \u0026*self.0.data.get() }\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e DerefMut for MutexRef\u003c'_, T, R\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t// safety: this is the only type that can use `value`, and we have a\n\t\t// mutable reference to this type, so there cannot be any other\n\t\t// references to this value.\n\t\tunsafe { \u0026mut *self.0.data.get() }\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e AsRef\u003cT\u003e for MutexRef\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e AsMut\u003cT\u003e for MutexRef\u003c'_, T, R\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself\n\t}\n}\n\nimpl\u003c'a, T: ?Sized, R: RawMutex\u003e MutexRef\u003c'a, T, R\u003e {\n\t/// Creates a reference to the underlying data of a mutex without\n\t/// attempting to lock it or take ownership of the key. But it's also quite\n\t/// dangerous to drop.\n\tpub(crate) const unsafe fn new(mutex: \u0026'a Mutex\u003cT, R\u003e) -\u003e Self {\n\t\tSelf(mutex, PhantomData)\n\t}\n}\n\n// it's kinda annoying to re-implement some of this stuff on guards\n// there's nothing i can do about that\n\n#[mutants::skip] // hashing involves RNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Hash + ?Sized, R: RawMutex\u003e Hash for MutexGuard\u003c'_, T, R\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.deref().hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug + ?Sized, R: RawMutex\u003e Debug for MutexGuard\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: Display + ?Sized, R: RawMutex\u003e Display for MutexGuard\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e Deref for MutexGuard\u003c'_, T, R\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t\u0026self.mutex\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e DerefMut for MutexGuard\u003c'_, T, R\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t\u0026mut self.mutex\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e AsRef\u003cT\u003e for MutexGuard\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e AsMut\u003cT\u003e for MutexGuard\u003c'_, T, R\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself\n\t}\n}\n\nimpl\u003c'a, T: ?Sized, R: RawMutex\u003e MutexGuard\u003c'a, T, R\u003e {\n\t/// Create a guard to the given mutex. Undefined if multiple guards to the\n\t/// same mutex exist at once.\n\t#[must_use]\n\tpub(super) const unsafe fn new(mutex: \u0026'a Mutex\u003cT, R\u003e, thread_key: ThreadKey) -\u003e Self {\n\t\tSelf {\n\t\t\tmutex: MutexRef(mutex, PhantomData),\n\t\t\tthread_key,\n\t\t}\n\t}\n}\n\nunsafe impl\u003cT: ?Sized + Sync, R: RawMutex + Sync\u003e Sync for MutexRef\u003c'_, T, R\u003e {}\n","traces":[{"line":33,"address":[2043600],"length":1,"stats":{"Line":1}},{"line":34,"address":[],"length":0,"stats":{"Line":1}},{"line":39,"address":[658464,658448],"length":1,"stats":{"Line":7}},{"line":42,"address":[],"length":0,"stats":{"Line":7}},{"line":49,"address":[2045504,2045440,2045472],"length":1,"stats":{"Line":6}},{"line":53,"address":[626421],"length":1,"stats":{"Line":7}},{"line":58,"address":[2045872,2045904],"length":1,"stats":{"Line":5}},{"line":62,"address":[2045909,2045877],"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":[650256,650224,650240],"length":1,"stats":{"Line":8}},{"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":[647568],"length":1,"stats":{"Line":2}},{"line":122,"address":[647573],"length":1,"stats":{"Line":2}},{"line":127,"address":[2050464],"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":[558192],"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_write(\u0026self) {\n\t\tassert!(!self.poison.is_poisoned(), \"The mutex has been killed\");\n\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.lock(), || self.poison())\n\t}\n\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool {\n\t\tif self.poison.is_poisoned() {\n\t\t\treturn false;\n\t\t}\n\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.try_lock(), || self.poison())\n\t}\n\n\tunsafe fn raw_unlock_write(\u0026self) {\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.unlock(), || self.poison())\n\t}\n\n\t// this is the closest thing to a read we can get, but Sharable isn't\n\t// implemented for this\n\t#[mutants::skip]\n\t#[cfg(not(tarpaulin_include))]\n\tunsafe fn raw_read(\u0026self) {\n\t\tself.raw_write()\n\t}\n\n\t#[mutants::skip]\n\t#[cfg(not(tarpaulin_include))]\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tself.raw_try_write()\n\t}\n\n\t#[mutants::skip]\n\t#[cfg(not(tarpaulin_include))]\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tself.raw_unlock_write()\n\t}\n}\n\nunsafe impl\u003cT, R: RawMutex\u003e Lockable for Mutex\u003cT, R\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= MutexRef\u003c'g, T, R\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= \u0026'a mut T\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tptrs.push(self);\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tMutexRef::new(self)\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.data.get().as_mut().unwrap_unchecked()\n\t}\n}\n\nimpl\u003cT, R: RawMutex\u003e LockableIntoInner for Mutex\u003cT, R\u003e {\n\ttype Inner = T;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tself.into_inner()\n\t}\n}\n\nimpl\u003cT, R: RawMutex\u003e LockableGetMut for Mutex\u003cT, R\u003e {\n\ttype Inner\u003c'a\u003e\n\t\t= \u0026'a mut T\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tself.get_mut()\n\t}\n}\n\nunsafe impl\u003cT, R: RawMutex\u003e OwnedLockable for Mutex\u003cT, R\u003e {}\n\nimpl\u003cT, R: RawMutex\u003e Mutex\u003cT, R\u003e {\n\t/// Creates a `Mutex` in an unlocked state ready for use.\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\t/// Acquires a lock on the mutex, blocking until it is safe to do so, and then\n\t/// unlocks the mutex after the provided function returns.\n\t///\n\t/// This function is useful to ensure that a Mutex is never accidentally\n\t/// locked forever by leaking the `MutexGuard`. Even if the function panics,\n\t/// this function will gracefully notice the panic, and unlock the mutex.\n\t///\n\t/// # Panics\n\t///\n\t/// This function will panic if the provided function also panics. However,\n\t/// mutex will be safely unlocked in this case, allowing the mutex to be\n\t/// locked again later.\n\t///\n\t/// # Example\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, Mutex};\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let mutex = Mutex::new(42);\n\t///\n\t/// let x = mutex.scoped_lock(\u0026mut key, |number| {\n\t/// *number += 5;\n\t/// *number\n\t/// });\n\t/// assert_eq!(x, 47);\n\t/// ```\n\tpub fn scoped_lock\u003c'a, Ret\u003e(\n\t\t\u0026'a self,\n\t\tkey: impl Keyable,\n\t\tf: impl FnOnce(\u0026'a mut T) -\u003e Ret,\n\t) -\u003e Ret {\n\t\tunsafe {\n\t\t\t// safety: we have the key\n\t\t\tself.raw_write();\n\n\t\t\t// safety: the data has been locked\n\t\t\tlet r = handle_unwind(\n\t\t\t\t|| f(self.data.get().as_mut().unwrap_unchecked()),\n\t\t\t\t|| self.raw_unlock_write(),\n\t\t\t);\n\n\t\t\t// ensures the key is held long enough\n\t\t\tdrop(key);\n\n\t\t\t// safety: the mutex is still locked\n\t\t\tself.raw_unlock_write();\n\n\t\t\tr\n\t\t}\n\t}\n\n\t/// Attempts to acquire the `Mutex` without blocking, and then unlocks it once\n\t/// the provided function returns.\n\t///\n\t/// This function implements a non-blocking variant of [`scoped_lock`]. Unlike\n\t/// `scoped_lock`, if the mutex is not already unlocked, then the provided\n\t/// function will not run, and the given [`Keyable`] is returned.\n\t///\n\t/// # Errors\n\t///\n\t/// If the mutex is already locked, then the provided function will not run.\n\t/// `Err` is returned with the given key.\n\t///\n\t/// # Panics\n\t///\n\t/// If the provided function panics, then the panic will be bubbled up and\n\t/// rethrown. The mutex will also be gracefully unlocked, allowing the mutex\n\t/// to be locked again.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let mutex = Mutex::new(42);\n\t///\n\t/// let result = mutex.scoped_try_lock(\u0026mut key, |num| {\n\t/// *num\n\t/// });\n\t///\n\t/// match result {\n\t/// Ok(val) =\u003e println!(\"The number is {val}\"),\n\t/// Err(_) =\u003e unreachable!(),\n\t/// }\n\t/// ```\n\t///\n\t/// [`scoped_lock`]: Mutex::scoped_lock\n\tpub fn scoped_try_lock\u003c'a, Key: Keyable, Ret\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl FnOnce(\u0026'a mut T) -\u003e Ret,\n\t) -\u003e Result\u003cRet, Key\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the key\n\t\t\tif !self.raw_try_write() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: the data has been locked\n\t\t\tlet r = handle_unwind(\n\t\t\t\t|| f(self.data.get().as_mut().unwrap_unchecked()),\n\t\t\t\t|| self.raw_unlock_write(),\n\t\t\t);\n\n\t\t\t// ensures the key is held long enough\n\t\t\tdrop(key);\n\n\t\t\t// safety: the mutex is still locked\n\t\t\tself.raw_unlock_write();\n\n\t\t\tOk(r)\n\t\t}\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e Mutex\u003cT, R\u003e {\n\t/// Acquires a mutex, blocking the current thread until it is able to do so.\n\t///\n\t/// This function will block the local thread until it is available to acquire\n\t/// the mutex. Upon returning, the thread is the only thread with the lock\n\t/// held. A [`MutexGuard`] is returned to allow a scoped unlock of this\n\t/// `Mutex`. When the guard goes out of scope, this `Mutex` will unlock.\n\t///\n\t/// Due to the requirement of a [`ThreadKey`] to call this function, it is not\n\t/// possible for this function to deadlock.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::thread;\n\t/// use happylock::{Mutex, ThreadKey};\n\t///\n\t/// let mutex = Mutex::new(0);\n\t///\n\t/// thread::scope(|s| {\n\t/// s.spawn(|| {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// *mutex.lock(key) = 10;\n\t/// });\n\t/// });\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// assert_eq!(*mutex.lock(key), 10);\n\t/// ```\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e MutexGuard\u003c'_, T, R\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_write();\n\n\t\t\t// safety: we just locked the mutex\n\t\t\tMutexGuard::new(self, key)\n\t\t}\n\t}\n\n\t/// Attempts to lock this `Mutex` without blocking.\n\t///\n\t/// If the lock could not be acquired at this time, then `Err` is returned.\n\t/// Otherwise, an RAII guard is returned. The lock will be unlocked when the\n\t/// guard 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 [`ThreadKey`].\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::thread;\n\t/// use happylock::{Mutex, ThreadKey};\n\t///\n\t/// let mutex = Mutex::new(0);\n\t///\n\t/// thread::scope(|s| {\n\t/// s.spawn(|| {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut lock = 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/// });\n\t/// });\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// assert_eq!(*mutex.lock(key), 10);\n\t/// ```\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e Result\u003cMutexGuard\u003c'_, T, R\u003e, ThreadKey\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the key to the mutex\n\t\t\tif self.raw_try_write() {\n\t\t\t\t// safety: we just locked the mutex\n\t\t\t\tOk(MutexGuard::new(self, key))\n\t\t\t} else {\n\t\t\t\tErr(key)\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Returns `true` if the mutex is currently locked\n\t#[cfg(test)]\n\tpub(crate) fn is_locked(\u0026self) -\u003e bool {\n\t\tself.raw.is_locked()\n\t}\n\n\t/// Lock without a [`ThreadKey`]. It is undefined behavior to do this without\n\t/// owning the [`ThreadKey`].\n\tpub(crate) unsafe fn try_lock_no_key(\u0026self) -\u003e Option\u003cMutexRef\u003c'_, T, R\u003e\u003e {\n\t\tself.raw_try_write().then_some(MutexRef(self, PhantomData))\n\t}\n\n\t/// Consumes the [`MutexGuard`], and consequently unlocks its `Mutex`.\n\t///\n\t/// This function is equivalent to calling [`drop`] on the guard, except that\n\t/// it returns the key that was used to create it. Alernatively, the guard\n\t/// will be automatically dropped when it goes out of scope.\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/// let guard = mutex.lock(key);\n\t/// assert_eq!(*guard, 20);\n\t/// ```\n\t#[must_use]\n\tpub fn unlock(guard: MutexGuard\u003c'_, T, R\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.mutex);\n\t\tguard.thread_key\n\t}\n}\n\nunsafe impl\u003cR: RawMutex + Send, T: ?Sized + Send\u003e Send for Mutex\u003cT, R\u003e {}\nunsafe impl\u003cR: RawMutex + Sync, T: ?Sized + Send\u003e Sync for Mutex\u003cT, R\u003e {}\n","traces":[{"line":16,"address":[],"length":0,"stats":{"Line":5}},{"line":17,"address":[626277,625989],"length":1,"stats":{"Line":4}},{"line":20,"address":[626032,626320],"length":1,"stats":{"Line":13}},{"line":21,"address":[],"length":0,"stats":{"Line":13}},{"line":24,"address":[558431],"length":1,"stats":{"Line":14}},{"line":25,"address":[612784,612928,612757,612752,612901,612933,613072,613040,612789,612896,613045,613077],"length":1,"stats":{"Line":44}},{"line":28,"address":[626144,625856],"length":1,"stats":{"Line":10}},{"line":29,"address":[650478,651038,650766],"length":1,"stats":{"Line":11}},{"line":30,"address":[2044679,2043719,2043991,2044951,2045223,2044407],"length":1,"stats":{"Line":6}},{"line":34,"address":[651057,650497,650781],"length":1,"stats":{"Line":11}},{"line":35,"address":[647286,647574],"length":1,"stats":{"Line":38}},{"line":38,"address":[2044032,2044448,2044720,2044992,2045264,2043760,2044192],"length":1,"stats":{"Line":13}},{"line":40,"address":[646492],"length":1,"stats":{"Line":13}},{"line":41,"address":[558353],"length":1,"stats":{"Line":48}},{"line":76,"address":[651376,651440,651312],"length":1,"stats":{"Line":22}},{"line":77,"address":[],"length":0,"stats":{"Line":22}},{"line":80,"address":[2051216,2051280,2051392],"length":1,"stats":{"Line":6}},{"line":81,"address":[2051397,2051285,2051221],"length":1,"stats":{"Line":7}},{"line":84,"address":[],"length":0,"stats":{"Line":2}},{"line":85,"address":[2051413],"length":1,"stats":{"Line":2}},{"line":92,"address":[],"length":0,"stats":{"Line":6}},{"line":93,"address":[],"length":0,"stats":{"Line":6}},{"line":103,"address":[],"length":0,"stats":{"Line":3}},{"line":104,"address":[],"length":0,"stats":{"Line":3}},{"line":121,"address":[2041440,2041792,2041024,2041768,2041594,2041925,2040640,2041616,2041239,2041264,2041056,2041418,2040832,2041046,2040809],"length":1,"stats":{"Line":23}},{"line":124,"address":[649990,650139,650091,649787,649835,649942],"length":1,"stats":{"Line":46}},{"line":125,"address":[],"length":0,"stats":{"Line":24}},{"line":142,"address":[],"length":0,"stats":{"Line":1}},{"line":143,"address":[],"length":0,"stats":{"Line":0}},{"line":173,"address":[],"length":0,"stats":{"Line":5}},{"line":174,"address":[2049054,2048958,2049005,2049140,2049102],"length":1,"stats":{"Line":5}},{"line":179,"address":[],"length":0,"stats":{"Line":2}},{"line":180,"address":[],"length":0,"stats":{"Line":2}},{"line":187,"address":[],"length":0,"stats":{"Line":1}},{"line":188,"address":[],"length":0,"stats":{"Line":1}},{"line":204,"address":[],"length":0,"stats":{"Line":6}},{"line":205,"address":[],"length":0,"stats":{"Line":6}},{"line":226,"address":[2042464,2042480,2042496],"length":1,"stats":{"Line":3}},{"line":227,"address":[],"length":0,"stats":{"Line":3}},{"line":259,"address":[557951,557792],"length":1,"stats":{"Line":9}},{"line":266,"address":[],"length":0,"stats":{"Line":9}},{"line":270,"address":[2038272,2039399,2038464,2039232,2038080,2038848,2039040,2038656],"length":1,"stats":{"Line":27}},{"line":271,"address":[],"length":0,"stats":{"Line":0}},{"line":275,"address":[557893],"length":1,"stats":{"Line":9}},{"line":278,"address":[],"length":0,"stats":{"Line":9}},{"line":280,"address":[],"length":0,"stats":{"Line":0}},{"line":321,"address":[624000,625173,624480,624453,624693,623760,624240,624213,624720,624933,623973,624960],"length":1,"stats":{"Line":18}},{"line":328,"address":[648232,648779,648952,649259,648299,648539,648712,649019,648472,649432,649192,649499],"length":1,"stats":{"Line":36}},{"line":329,"address":[624102,624342,624822,623862,624582,625062],"length":1,"stats":{"Line":8}},{"line":334,"address":[645319,645559,645799,646039,646279,646519],"length":1,"stats":{"Line":30}},{"line":335,"address":[678661,678821,679141,679456,678981,678976,679461,678656,678816,679296,679301,679136],"length":1,"stats":{"Line":0}},{"line":339,"address":[648837,648597,648357,649077,649557,649317],"length":1,"stats":{"Line":10}},{"line":342,"address":[624424,624664,625144,623944,624184,624904],"length":1,"stats":{"Line":10}},{"line":344,"address":[646111,645871,645631,645391,646591,646351],"length":1,"stats":{"Line":10}},{"line":378,"address":[2042896,2042700,2043004,2042678,2042982,2042592],"length":1,"stats":{"Line":6}},{"line":381,"address":[646142],"length":1,"stats":{"Line":6}},{"line":384,"address":[2042653,2042957],"length":1,"stats":{"Line":6}},{"line":422,"address":[],"length":0,"stats":{"Line":2}},{"line":425,"address":[],"length":0,"stats":{"Line":6}},{"line":427,"address":[2042841,2043195,2043225,2042811],"length":1,"stats":{"Line":4}},{"line":429,"address":[],"length":0,"stats":{"Line":0}},{"line":436,"address":[2043264,2042880],"length":1,"stats":{"Line":2}},{"line":437,"address":[],"length":0,"stats":{"Line":2}},{"line":442,"address":[],"length":0,"stats":{"Line":1}},{"line":443,"address":[],"length":0,"stats":{"Line":1}},{"line":469,"address":[],"length":0,"stats":{"Line":3}},{"line":470,"address":[],"length":0,"stats":{"Line":3}},{"line":471,"address":[],"length":0,"stats":{"Line":0}}],"covered":62,"coverable":68},{"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. The\n/// mutex can be created via a `new` constructor. Each mutex has a type\n/// parameter which represents the data that it is protecting. The data can\n/// only be accessed through the [`MutexGuard`]s returned from [`lock`] and\n/// [`try_lock`], which guarantees that the data is only ever accessed when\n/// 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/// An RAII implementation of a “scoped lock” of a mutex. When this structure\n/// is dropped (falls out of scope), the lock will be unlocked.\n///\n/// The data protected by the mutex can be accessed through this guard via its\n/// [`Deref`] and [`DerefMut`] implementations.\n///\n/// This is created by calling the [`lock`] and [`try_lock`] methods on [`Mutex`]\n///\n/// This is similar to the [`MutexGuard`] type, except it does not hold a\n/// [`ThreadKey`].\n///\n/// [`lock`]: `Mutex::lock`\n/// [`try_lock`]: `Mutex::try_lock`\n/// [`Deref`]: `std::ops::Deref`\n/// [`DerefMut`]: `std::ops::DerefMut`\npub struct MutexRef\u003c'a, T: ?Sized + 'a, R: RawMutex\u003e(\u0026'a Mutex\u003cT, R\u003e, PhantomData\u003cR::GuardMarker\u003e);\n\n/// An RAII implementation of a “scoped lock” of a mutex. When this structure\n/// is dropped (falls out of scope), the lock will be unlocked.\n///\n/// The data protected by the mutex can be accessed through this guard via its\n/// [`Deref`] and [`DerefMut`] implementations.\n///\n/// This is created by calling the [`lock`] and [`try_lock`] methods on [`Mutex`]\n///\n/// This guard holds on to a [`ThreadKey`], which ensures that nothing else is\n/// locked until this guard is dropped. The [`ThreadKey`] can be reacquired\n/// using [`Mutex::unlock`].\n///\n/// [`Deref`]: `std::ops::Deref`\n/// [`DerefMut`]: `std::ops::DerefMut`\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?\n//\n// As an update, I've now written `LockContext`. That was even more challenging\npub struct MutexGuard\u003c'a, T: ?Sized + 'a, R: RawMutex\u003e {\n\tmutex: MutexRef\u003c'a, T, R\u003e, // this way we don't need to re-implement Drop\n\tthread_key: ThreadKey,\n}\n\n#[cfg(test)]\nmod tests {\n\tuse crate::{LockCollection, ThreadKey};\n\n\tuse super::*;\n\n\t#[test]\n\tfn unlocked_when_initialized() {\n\t\tlet lock: crate::Mutex\u003c_\u003e = Mutex::new(\"Hello, world!\");\n\n\t\tassert!(!lock.is_locked());\n\t}\n\n\t#[test]\n\tfn locked_after_read() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::Mutex\u003c_\u003e = Mutex::new(\"Hello, world!\");\n\n\t\tlet guard = lock.lock(key);\n\n\t\tassert!(lock.is_locked());\n\t\tdrop(guard)\n\t}\n\n\t#[test]\n\tfn from_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex: crate::Mutex\u003c_\u003e = Mutex::from(\"Hello, world!\");\n\n\t\tlet guard = mutex.lock(key);\n\t\tassert_eq!(*guard, \"Hello, world!\");\n\t}\n\n\t#[test]\n\tfn as_mut_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mut mutex = crate::Mutex::from(42);\n\n\t\tlet mut_ref = mutex.as_mut();\n\t\t*mut_ref = 24;\n\n\t\tmutex.scoped_lock(key, |guard| assert_eq!(*guard, 24))\n\t}\n\n\t#[test]\n\tfn display_works_for_guard() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex: crate::Mutex\u003c_\u003e = Mutex::new(\"Hello, world!\");\n\t\tlet guard = mutex.lock(key);\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn display_works_for_ref() {\n\t\tlet mutex: crate::Mutex\u003c_\u003e = Mutex::new(\"Hello, world!\");\n\t\tlet guard = unsafe { mutex.try_lock_no_key().unwrap() };\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn ref_as_mut() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = LockCollection::new(crate::Mutex::new(0));\n\t\tlet mut guard = collection.lock(key);\n\t\tlet guard_mut = guard.as_mut().as_mut();\n\n\t\t*guard_mut = 3;\n\t\tlet key = LockCollection::\u003ccrate::Mutex\u003c_\u003e\u003e::unlock(guard);\n\n\t\tlet guard = collection.lock(key);\n\n\t\tassert_eq!(guard.as_ref().as_ref(), \u00263);\n\t}\n\n\t#[test]\n\tfn guard_as_mut() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = crate::Mutex::new(0);\n\t\tlet mut guard = mutex.lock(key);\n\t\tlet guard_mut = guard.as_mut();\n\n\t\t*guard_mut = 3;\n\t\tlet key = Mutex::unlock(guard);\n\n\t\tlet guard = mutex.lock(key);\n\n\t\tassert_eq!(guard.as_ref(), \u00263);\n\t}\n\n\t#[test]\n\tfn dropping_guard_releases_mutex() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex: crate::Mutex\u003c_\u003e = Mutex::new(\"Hello, world!\");\n\n\t\tlet guard = mutex.lock(key);\n\t\tdrop(guard);\n\n\t\tassert!(!mutex.is_locked());\n\t}\n\n\t#[test]\n\tfn dropping_ref_releases_mutex() {\n\t\tlet mutex: crate::Mutex\u003c_\u003e = Mutex::new(\"Hello, world!\");\n\n\t\tlet guard = unsafe { mutex.try_lock_no_key().unwrap() };\n\t\tdrop(guard);\n\n\t\tassert!(!mutex.is_locked());\n\t}\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","src","poisonable","error.rs"],"content":"use core::fmt;\nuse std::error::Error;\n\nuse super::{PoisonError, PoisonGuard, TryLockPoisonableError};\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard\u003e fmt::Debug for PoisonError\u003cGuard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut fmt::Formatter\u003c'_\u003e) -\u003e fmt::Result {\n\t\tf.debug_struct(\"PoisonError\").finish_non_exhaustive()\n\t}\n}\n\nimpl\u003cGuard\u003e fmt::Display for PoisonError\u003cGuard\u003e {\n\t#[cfg_attr(test, mutants::skip)]\n\t#[cfg(not(tarpaulin_include))]\n\tfn fmt(\u0026self, f: \u0026mut fmt::Formatter\u003c'_\u003e) -\u003e fmt::Result {\n\t\t\"poisoned lock: another task failed inside\".fmt(f)\n\t}\n}\n\nimpl\u003cGuard\u003e AsRef\u003cGuard\u003e for PoisonError\u003cGuard\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026Guard {\n\t\tself.get_ref()\n\t}\n}\n\nimpl\u003cGuard\u003e AsMut\u003cGuard\u003e for PoisonError\u003cGuard\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut Guard {\n\t\tself.get_mut()\n\t}\n}\n\nimpl\u003cGuard\u003e Error for PoisonError\u003cGuard\u003e {}\n\nimpl\u003cGuard\u003e PoisonError\u003cGuard\u003e {\n\t/// Creates a `PoisonError`\n\t///\n\t/// This is generally created by methods like [`Poisonable::lock`].\n\t///\n\t/// [`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::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let mutex = Poisonable::new(Mutex::new(HashSet::new()));\n\t///\n\t/// // poison the mutex\n\t/// thread::scope(|s| {\n\t/// let r = s.spawn(|| {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut data = mutex.lock(key).unwrap();\n\t/// data.insert(10);\n\t/// panic!();\n\t/// }).join();\n\t/// });\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::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t/// use happylock::poisonable::PoisonGuard;\n\t///\n\t/// let mutex = Poisonable::new(Mutex::new(HashSet::new()));\n\t///\n\t/// // poison the mutex\n\t/// thread::scope(|s| {\n\t/// let r = s.spawn(|| {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut data = mutex.lock(key).unwrap();\n\t/// data.insert(10);\n\t/// panic!();\n\t/// }).join();\n\t/// });\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::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let mutex =Poisonable::new(Mutex::new(HashSet::new()));\n\t///\n\t/// // poison the mutex\n\t/// thread::scope(|s| {\n\t/// let r = s.spawn(|| {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut data = mutex.lock(key).unwrap();\n\t/// data.insert(10);\n\t/// panic!();\n\t/// }).join();\n\t/// });\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":[2017760],"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":43,"address":[],"length":0,"stats":{"Line":10}},{"line":76,"address":[],"length":0,"stats":{"Line":7}},{"line":77,"address":[],"length":0,"stats":{"Line":1}},{"line":110,"address":[],"length":0,"stats":{"Line":4}},{"line":111,"address":[],"length":0,"stats":{"Line":0}},{"line":144,"address":[],"length":0,"stats":{"Line":3}},{"line":145,"address":[],"length":0,"stats":{"Line":0}},{"line":175,"address":[],"length":0,"stats":{"Line":1}},{"line":176,"address":[],"length":0,"stats":{"Line":0}}],"covered":10,"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":[976384],"length":1,"stats":{"Line":18}},{"line":9,"address":[941153],"length":1,"stats":{"Line":17}},{"line":12,"address":[975936],"length":1,"stats":{"Line":15}},{"line":13,"address":[976357],"length":1,"stats":{"Line":15}},{"line":16,"address":[981952],"length":1,"stats":{"Line":1}},{"line":17,"address":[975957],"length":1,"stats":{"Line":1}},{"line":20,"address":[1001232],"length":1,"stats":{"Line":9}},{"line":21,"address":[976021],"length":1,"stats":{"Line":9}}],"covered":8,"coverable":8},{"path":["/","home","botahamec","Projects","happylock","src","poisonable","guard.rs"],"content":"use std::fmt::{Debug, Display};\nuse std::hash::Hash;\nuse std::marker::PhantomData;\nuse std::ops::{Deref, DerefMut};\n\nuse super::{PoisonFlag, PoisonGuard, PoisonRef};\n\nimpl\u003c'a, Guard\u003e PoisonRef\u003c'a, Guard\u003e {\n\t// This is used so that we don't keep accidentally adding the flag reference\n\tpub(super) const fn new(flag: \u0026'a PoisonFlag, guard: Guard) -\u003e Self {\n\t\tSelf {\n\t\t\tguard,\n\t\t\t#[cfg(panic = \"unwind\")]\n\t\t\tflag,\n\t\t\t_phantom: PhantomData,\n\t\t}\n\t}\n}\n\nimpl\u003cGuard\u003e Drop for PoisonRef\u003c'_, Guard\u003e {\n\tfn drop(\u0026mut self) {\n\t\t#[cfg(panic = \"unwind\")]\n\t\tif std::thread::panicking() {\n\t\t\tself.flag.poison();\n\t\t}\n\t}\n}\n\n#[mutants::skip] // hashing involves RNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard: Hash\u003e Hash for PoisonRef\u003c'_, Guard\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.guard.hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard: Debug\u003e Debug for PoisonRef\u003c'_, Guard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cGuard: Display\u003e Display for PoisonRef\u003c'_, Guard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cGuard\u003e Deref for PoisonRef\u003c'_, Guard\u003e {\n\ttype Target = Guard;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t\u0026self.guard\n\t}\n}\n\nimpl\u003cGuard\u003e DerefMut for PoisonRef\u003c'_, Guard\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t\u0026mut self.guard\n\t}\n}\n\nimpl\u003cGuard\u003e AsRef\u003cGuard\u003e for PoisonRef\u003c'_, Guard\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026Guard {\n\t\t\u0026self.guard\n\t}\n}\n\nimpl\u003cGuard\u003e AsMut\u003cGuard\u003e for PoisonRef\u003c'_, Guard\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut Guard {\n\t\t\u0026mut self.guard\n\t}\n}\n\n#[mutants::skip] // hashing involves RNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard: Hash\u003e Hash for PoisonGuard\u003c'_, Guard\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.guard.hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard: Debug\u003e Debug for PoisonGuard\u003c'_, Guard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026self.guard, f)\n\t}\n}\n\nimpl\u003cGuard: Display\u003e Display for PoisonGuard\u003c'_, Guard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026self.guard, f)\n\t}\n}\n\nimpl\u003cT, Guard: Deref\u003cTarget = T\u003e\u003e Deref for PoisonGuard\u003c'_, Guard\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t#[expect(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#[expect(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":[2010128,2010160,2010224,2010192],"length":1,"stats":{"Line":7}},{"line":21,"address":[],"length":0,"stats":{"Line":5}},{"line":22,"address":[],"length":0,"stats":{"Line":0}},{"line":23,"address":[],"length":0,"stats":{"Line":5}},{"line":24,"address":[],"length":0,"stats":{"Line":4}},{"line":46,"address":[],"length":0,"stats":{"Line":1}},{"line":47,"address":[],"length":0,"stats":{"Line":1}},{"line":54,"address":[],"length":0,"stats":{"Line":3}},{"line":55,"address":[],"length":0,"stats":{"Line":0}},{"line":60,"address":[],"length":0,"stats":{"Line":2}},{"line":61,"address":[],"length":0,"stats":{"Line":0}},{"line":66,"address":[],"length":0,"stats":{"Line":1}},{"line":67,"address":[],"length":0,"stats":{"Line":0}},{"line":72,"address":[],"length":0,"stats":{"Line":1}},{"line":73,"address":[],"length":0,"stats":{"Line":0}},{"line":94,"address":[],"length":0,"stats":{"Line":1}},{"line":95,"address":[],"length":0,"stats":{"Line":1}},{"line":102,"address":[],"length":0,"stats":{"Line":2}},{"line":104,"address":[],"length":0,"stats":{"Line":2}},{"line":109,"address":[],"length":0,"stats":{"Line":3}},{"line":111,"address":[],"length":0,"stats":{"Line":3}},{"line":116,"address":[],"length":0,"stats":{"Line":1}},{"line":117,"address":[],"length":0,"stats":{"Line":0}},{"line":122,"address":[],"length":0,"stats":{"Line":1}},{"line":123,"address":[],"length":0,"stats":{"Line":0}}],"covered":18,"coverable":25},{"path":["/","home","botahamec","Projects","happylock","src","poisonable","poisonable.rs"],"content":"use std::panic::{RefUnwindSafe, UnwindSafe};\n\nuse crate::collection::OwnedLockCollection;\nuse crate::handle_unwind::handle_unwind;\nuse crate::lockable::{\n\tLockable, LockableGetMut, LockableIntoInner, OwnedLockable, RawLock, Sharable,\n};\nuse crate::{Keyable, LockContext, ThreadKey};\n\nuse super::{\n\tPoisonError, PoisonFlag, PoisonGuard, PoisonRef, PoisonResult, Poisonable,\n\tTryLockPoisonableError, TryLockPoisonableResult,\n};\n\nunsafe impl\u003cL: Lockable + RawLock\u003e RawLock for Poisonable\u003cL\u003e {\n\t#[mutants::skip] // this should never run\n\t#[cfg(not(tarpaulin_include))]\n\tfn poison(\u0026self) {\n\t\tself.inner.poison()\n\t}\n\n\tunsafe fn raw_write(\u0026self) {\n\t\tself.inner.raw_write()\n\t}\n\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool {\n\t\tself.inner.raw_try_write()\n\t}\n\n\tunsafe fn raw_unlock_write(\u0026self) {\n\t\tself.inner.raw_unlock_write()\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tself.inner.raw_read()\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tself.inner.raw_try_read()\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tself.inner.raw_unlock_read()\n\t}\n}\n\nunsafe impl\u003cL: Lockable\u003e Lockable for Poisonable\u003cL\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= PoisonResult\u003cPoisonRef\u003c'g, L::Guard\u003c'g\u003e\u003e\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= PoisonResult\u003cL::DataMut\u003c'a\u003e\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tself.inner.get_ptrs(ptrs)\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tlet ref_guard = PoisonRef::new(\u0026self.poisoned, self.inner.guard());\n\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(ref_guard))\n\t\t} else {\n\t\t\tOk(ref_guard)\n\t\t}\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(self.inner.data_mut()))\n\t\t} else {\n\t\t\tOk(self.inner.data_mut())\n\t\t}\n\t}\n}\n\nunsafe impl\u003cL: Sharable\u003e Sharable for Poisonable\u003cL\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= PoisonResult\u003cPoisonRef\u003c'g, L::ReadGuard\u003c'g\u003e\u003e\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= PoisonResult\u003cL::DataRef\u003c'a\u003e\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tlet ref_guard = PoisonRef::new(\u0026self.poisoned, self.inner.read_guard());\n\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(ref_guard))\n\t\t} else {\n\t\t\tOk(ref_guard)\n\t\t}\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(self.inner.data_ref()))\n\t\t} else {\n\t\t\tOk(self.inner.data_ref())\n\t\t}\n\t}\n}\n\nunsafe impl\u003cL: OwnedLockable\u003e OwnedLockable for Poisonable\u003cL\u003e {}\n\n// AsMut won't work here because we don't strictly return a \u0026mut T\n// LockableGetMut is the next best thing\nimpl\u003cL: LockableGetMut\u003e LockableGetMut for Poisonable\u003cL\u003e {\n\ttype Inner\u003c'a\u003e\n\t\t= PoisonResult\u003cL::Inner\u003c'a\u003e\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(self.inner.get_mut()))\n\t\t} else {\n\t\t\tOk(self.inner.get_mut())\n\t\t}\n\t}\n}\n\nimpl\u003cL: LockableIntoInner\u003e LockableIntoInner for Poisonable\u003cL\u003e {\n\ttype Inner = PoisonResult\u003cL::Inner\u003e;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(self.inner.into_inner()))\n\t\t} else {\n\t\t\tOk(self.inner.into_inner())\n\t\t}\n\t}\n}\n\nimpl\u003cL\u003e From\u003cL\u003e for Poisonable\u003cL\u003e {\n\tfn from(value: L) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\nimpl\u003cL\u003e Poisonable\u003cL\u003e {\n\t/// Creates a new `Poisonable`\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, Poisonable};\n\t///\n\t/// let mutex = Poisonable::new(Mutex::new(0));\n\t/// ```\n\tpub const fn new(value: L) -\u003e Self {\n\t\tSelf {\n\t\t\tinner: value,\n\t\t\tpoisoned: PoisonFlag::new(),\n\t\t}\n\t}\n\n\t/// Determines whether the `Poisonable` is poisoned.\n\t///\n\t/// If another thread is active, the `Poisonable` can still become poisoned at\n\t/// any 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::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let mutex = Poisonable::new(Mutex::new(0));\n\t///\n\t/// thread::scope(|s| {\n\t/// let r = s.spawn(|| {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let _lock = mutex.lock(key).unwrap();\n\t/// panic!(); // the mutex gets poisoned\n\t/// }).join();\n\t/// });\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::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let mutex = Poisonable::new(Mutex::new(0));\n\t///\n\t/// thread::scope(|s| {\n\t/// let r = s.spawn(|| {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let _lock = mutex.lock(key).unwrap();\n\t/// panic!(); // the mutex gets poisoned\n\t/// }).join();\n\t/// });\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/// # 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/// # 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}\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\t/// Acquires an exclusive lock, blocking until it is safe to do so, and then\n\t/// unlocks after the provided function returns.\n\t///\n\t/// This function is useful to ensure that a `Poisonable` is never\n\t/// accidentally locked forever by leaking the guard. Even if the function\n\t/// panics, this function will gracefully notice the panic, poison the lock,\n\t/// and unlock.\n\t///\n\t/// If the lock is poisoned, then an error will be passed into the provided\n\t/// function.\n\t///\n\t/// # Panics\n\t///\n\t/// This function will panic if the provided function also panics. However,\n\t/// `Poisonable` will be safely poisoned and any subsequent calls will pass\n\t/// `Err` into the given function.\n\t///\n\t/// # Example\n\t///\n\t/// ```\n\t/// use happylock::{Poisonable, ThreadKey, RwLock};\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let lock = Poisonable::new(RwLock::new(42));\n\t///\n\t/// let x = lock.scoped_lock(\u0026mut key, |number| {\n\t/// *number.unwrap()\n\t/// });\n\t/// assert_eq!(x, 42);\n\t/// ```\n\tpub fn scoped_lock\u003c'a, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: impl Keyable,\n\t\tf: impl FnOnce(\u003cSelf as Lockable\u003e::DataMut\u003c'a\u003e) -\u003e R,\n\t) -\u003e R {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_write();\n\n\t\t\t// safety: the data was just locked\n\t\t\tlet r = handle_unwind(\n\t\t\t\t|| f(self.data_mut()),\n\t\t\t\t|| {\n\t\t\t\t\tself.poisoned.poison();\n\t\t\t\t\tself.raw_unlock_write();\n\t\t\t\t},\n\t\t\t);\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock_write();\n\n\t\t\tdrop(key); // ensure the key stays alive long enough\n\n\t\t\tr\n\t\t}\n\t}\n\n\t/// Attempts to acquire an exclusive lock to the `Poisonable` without\n\t/// blocking, and then unlocks it once the provided function returns.\n\t///\n\t/// This function implements a non-blocking variant of [`scoped_lock`].\n\t/// Unlike `scoped_lock`, if the `Poisonable` is not already unlocked, then\n\t/// the provided function will not run, and the given [`Keyable`] is returned.\n\t/// This method 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/// If the lock is poisoned, then an error will be passed into the provided\n\t/// function.\n\t///\n\t/// # Errors\n\t///\n\t/// If the `Poisonable` is already locked, then the provided function will not\n\t/// run. `Err` is returned with the given key.\n\t///\n\t/// # Panics\n\t///\n\t/// If the provided function panics, then the panic will be bubbled up and\n\t/// rethrown. The `Poisonable` will also be gracefully unlocked, allowing the\n\t/// `Poisonable` to be locked again.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Poisonable, RwLock, ThreadKey};\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let lock = Poisonable::new(RwLock::new(42));\n\t///\n\t/// let result = lock.scoped_try_lock(\u0026mut key, |num| {\n\t/// *num.unwrap()\n\t/// });\n\t///\n\t/// match result {\n\t/// Ok(val) =\u003e println!(\"The number is {val}\"),\n\t/// Err(_) =\u003e unreachable!(),\n\t/// }\n\t/// ```\n\t///\n\t/// [`scoped_lock`]: [`crate::Poisonable::scoped_lock`]\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 FnOnce(\u003cSelf as Lockable\u003e::DataMut\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_write() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we just locked the collection\n\t\t\tlet r = handle_unwind(\n\t\t\t\t|| f(self.data_mut()),\n\t\t\t\t|| {\n\t\t\t\t\tself.poisoned.poison();\n\t\t\t\t\tself.raw_unlock_write();\n\t\t\t\t},\n\t\t\t);\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock_write();\n\n\t\t\tdrop(key); // ensures the key stays valid long enough\n\n\t\t\tOk(r)\n\t\t}\n\t}\n\n\t/// Acquires the lock, blocking the current thread until it is ok to do so.\n\t///\n\t/// This function will block the current thread until it is available to\n\t/// acquire the lock. 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 lock 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::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let mutex = Poisonable::new(Mutex::new(0));\n\t///\n\t/// thread::scope(|s| {\n\t/// let r = s.spawn(|| {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// *mutex.lock(key).unwrap() = 10;\n\t/// }).join();\n\t/// });\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// assert_eq!(*mutex.lock(key).unwrap(), 10);\n\t/// ```\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e PoisonResult\u003cPoisonGuard\u003c'_, L::Guard\u003c'_\u003e\u003e\u003e {\n\t\tunsafe {\n\t\t\tself.inner.raw_write();\n\t\t\tself.guard(key)\n\t\t}\n\t}\n\n\t/// Attempts to acquire this lock.\n\t///\n\t/// If the lock could not be acquired at this time, then [`Err`] is\n\t/// returned. Otherwise, an RAII guard is returned. The lock will be\n\t/// unlocked when the guard is dropped.\n\t///\n\t/// This function does not block.\n\t///\n\t/// # Errors\n\t///\n\t/// If another user of this lock panicked while holding the lock, then this\n\t/// call will return the [`Poisoned`] error if the lock would otherwise be\n\t/// acquired.\n\t///\n\t/// If the lock 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::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let mutex = Poisonable::new(Mutex::new(0));\n\t///\n\t/// thread::scope(|s| {\n\t/// s.spawn(|| {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut lock = 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/// });\n\t/// });\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// assert_eq!(*mutex.lock(key).unwrap(), 10);\n\t/// ```\n\t///\n\t/// [`Poisoned`]: `TryLockPoisonableError::Poisoned`\n\t/// [`WouldBlock`]: `TryLockPoisonableError::WouldBlock`\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e TryLockPoisonableResult\u003c'_, L::Guard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\tif self.inner.raw_try_write() {\n\t\t\t\tOk(self.guard(key)?)\n\t\t\t} else {\n\t\t\t\tErr(TryLockPoisonableError::WouldBlock(key))\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Consumes the [`PoisonGuard`], and consequently unlocks its `Poisonable`.\n\t///\n\t/// This function is equivalent to calling [`drop`] on the guard, except that\n\t/// it returns the key that was used to create it. Alternatively, the guard\n\t/// will be automatically dropped when it goes out of scope.\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\t/// Acquires a shared lock, blocking until it is safe to do so, and then\n\t/// unlocks after the provided function returns.\n\t///\n\t/// This function is useful to ensure that a `Poisonable` is never\n\t/// accidentally locked forever by leaking the `ReadGuard`. Even if the\n\t/// function panics, this function will gracefully notice the panic, and\n\t/// unlock. This function provides no guarantees with respect to the ordering\n\t/// of whether contentious readers or writers will acquire the lock first.\n\t///\n\t/// If the lock is poisoned, then an error will be passed into the provided\n\t/// into the provided function.\n\t///\n\t/// # Panics\n\t///\n\t/// This function will panic if the provided function also panics. However,\n\t/// `Poisonable` will be safely unlocked in this case, allowing the\n\t/// `Poisonable` to be locked again later.\n\t///\n\t/// # Example\n\t///\n\t/// ```\n\t/// use happylock::{Poisonable, ThreadKey, RwLock};\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let lock = Poisonable::new(RwLock::new(42));\n\t///\n\t/// let x = lock.scoped_read(\u0026mut key, |number| {\n\t/// *number.unwrap()\n\t/// });\n\t/// assert_eq!(x, 42);\n\t/// ```\n\tpub fn scoped_read\u003c'a, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: impl Keyable,\n\t\tf: impl FnOnce(\u003cSelf as Sharable\u003e::DataRef\u003c'a\u003e) -\u003e R,\n\t) -\u003e R {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_read();\n\n\t\t\t// safety: the data was just locked\n\t\t\tlet r = handle_unwind(\n\t\t\t\t|| f(self.data_ref()),\n\t\t\t\t|| {\n\t\t\t\t\tself.poisoned.poison();\n\t\t\t\t\tself.raw_unlock_read();\n\t\t\t\t},\n\t\t\t);\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock_read();\n\n\t\t\tdrop(key); // ensure the key stays alive long enough\n\n\t\t\tr\n\t\t}\n\t}\n\n\t/// Attempts to acquire a shared lock to the `Poisonable` without blocking,\n\t/// and then unlocks it once the provided function returns.\n\t///\n\t/// This function implements a non-blocking variant of [`scoped_read`].\n\t/// Unlike `scoped_read`, if the `Poisonable` is exclusively locked, then the\n\t/// provided function will not run, and the given [`Keyable`] is returned.\n\t/// This method provides no guarantees with respect to the ordering of whether\n\t/// contentious readers of writers will acquire the lock first.\n\t///\n\t/// If the lock is poisoned, then an error will be passed into the provided\n\t/// function.\n\t///\n\t/// # Errors\n\t///\n\t/// If the `Poisonable` is already exclusively locked, then the provided\n\t/// function will not run. `Err` is returned with the given key.\n\t///\n\t/// # Panics\n\t///\n\t/// If the provided function panics, then the panic will be bubbled up and\n\t/// rethrown. The `Poisonable` will also be gracefully unlocked, allowing the\n\t/// `Poisonable` to be locked again.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Poisonable, RwLock, ThreadKey};\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let lock = Poisonable::new(RwLock::new(42));\n\t///\n\t/// let result = lock.scoped_try_read(\u0026mut key, |num| {\n\t/// *num.unwrap()\n\t/// });\n\t///\n\t/// match result {\n\t/// Ok(val) =\u003e println!(\"The number is {val}\"),\n\t/// Err(_) =\u003e unreachable!(),\n\t/// }\n\t/// ```\n\t///\n\t/// [`scoped_read`]: crate::Poisonable::scoped_read\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 FnOnce(\u003cSelf as Sharable\u003e::DataRef\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_read() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we just locked the collection\n\t\t\tlet r = handle_unwind(\n\t\t\t\t|| f(self.data_ref()),\n\t\t\t\t|| {\n\t\t\t\t\tself.poisoned.poison();\n\t\t\t\t\tself.raw_unlock_read();\n\t\t\t\t},\n\t\t\t);\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock_read();\n\n\t\t\tdrop(key); // ensures the key stays valid long enough\n\n\t\t\tOk(r)\n\t\t}\n\t}\n\n\t/// Locks with shared read access, blocking the current thread until it can\n\t/// be acquired.\n\t///\n\t/// This function will block the current thread until there are no writers\n\t/// which hold the lock. This method does not provide any guarantee with\n\t/// respect to the ordering of contentious readers or writers will acquire\n\t/// the lock.\n\t///\n\t/// # Errors\n\t///\n\t/// This function will return an error if the `Poisonable` is poisoned. A\n\t/// `Poisonable` is poisoned whenever a thread panics while holding a lock.\n\t/// The failure will occur immediately after the lock has been acquired. The\n\t/// acquired lock guard will be contained in the returned error.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\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 = Poisonable::new(RwLock::new(0));\n\t///\n\t/// let n = lock.read(key).unwrap();\n\t/// assert_eq!(*n, 0);\n\t///\n\t/// thread::scope(|s| {\n\t/// s.spawn(|| {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let r = lock.read(key);\n\t/// assert!(r.is_ok());\n\t/// });\n\t/// });\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, without blocking the\n\t/// thread.\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 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 thread panics while\n\t/// holding a lock. `Poisoned` will only be returned if the lock would have\n\t/// 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\t// TODO don't poison when holding shared lock\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 `Poisonable`.\n\t///\n\t/// This function is equivalent to calling [`drop`] on the guard, except that\n\t/// it returns the key that was used to create it. Alternatively, the guard\n\t/// will be automatically dropped when it goes out of scope.\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(20));\n\t///\n\t/// let mut guard = lock.read(key).unwrap();\n\t/// assert_eq!(*guard, 20);\n\t///\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. A `Poisonable` is poisoned whenever a\n\t/// thread panics while holding a lock.\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. A `Poisonable` is poisoned\n\t/// whenever a thread panics while holding a lock.\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: OwnedLockable\u003e Poisonable\u003cOwnedLockCollection\u003cL\u003e\u003e {\n\t/// Creates a context that can be used to iterate over the items in order.\n\t///\n\t/// For more information, see [`OwnedLockCollection::context`].\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. A `Poisonable` is poisoned\n\t/// whenever a thread panics while holding a lock.\n\tpub fn context(\u0026self) -\u003e PoisonResult\u003cLockContext\u003c'_, L\u003e\u003e {\n\t\tif self.is_poisoned() {\n\t\t\tOk(self.inner.context())\n\t\t} else {\n\t\t\tErr(PoisonError::new(self.inner.context()))\n\t\t}\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":22,"address":[2014448],"length":1,"stats":{"Line":1}},{"line":23,"address":[],"length":0,"stats":{"Line":1}},{"line":26,"address":[2014416,2014480],"length":1,"stats":{"Line":2}},{"line":27,"address":[],"length":0,"stats":{"Line":2}},{"line":30,"address":[],"length":0,"stats":{"Line":2}},{"line":31,"address":[2014437,2014517],"length":1,"stats":{"Line":2}},{"line":34,"address":[],"length":0,"stats":{"Line":1}},{"line":35,"address":[],"length":0,"stats":{"Line":1}},{"line":38,"address":[2014464],"length":1,"stats":{"Line":2}},{"line":39,"address":[],"length":0,"stats":{"Line":2}},{"line":42,"address":[],"length":0,"stats":{"Line":1}},{"line":43,"address":[2014501],"length":1,"stats":{"Line":1}},{"line":58,"address":[],"length":0,"stats":{"Line":4}},{"line":59,"address":[2017198,2017614,2017742],"length":1,"stats":{"Line":4}},{"line":62,"address":[],"length":0,"stats":{"Line":3}},{"line":63,"address":[],"length":0,"stats":{"Line":3}},{"line":65,"address":[],"length":0,"stats":{"Line":11}},{"line":66,"address":[2017453,2017393,2017133,2017073],"length":1,"stats":{"Line":2}},{"line":68,"address":[],"length":0,"stats":{"Line":3}},{"line":72,"address":[],"length":0,"stats":{"Line":2}},{"line":73,"address":[],"length":0,"stats":{"Line":4}},{"line":74,"address":[],"length":0,"stats":{"Line":1}},{"line":76,"address":[2017532,2017660],"length":1,"stats":{"Line":2}},{"line":92,"address":[2014836,2014560,2014814],"length":1,"stats":{"Line":1}},{"line":93,"address":[],"length":0,"stats":{"Line":1}},{"line":95,"address":[],"length":0,"stats":{"Line":4}},{"line":96,"address":[],"length":0,"stats":{"Line":2}},{"line":98,"address":[2014705],"length":1,"stats":{"Line":1}},{"line":102,"address":[2014848],"length":1,"stats":{"Line":1}},{"line":103,"address":[2014862,2014895],"length":1,"stats":{"Line":3}},{"line":104,"address":[],"length":0,"stats":{"Line":1}},{"line":106,"address":[],"length":0,"stats":{"Line":2}},{"line":121,"address":[],"length":0,"stats":{"Line":2}},{"line":122,"address":[],"length":0,"stats":{"Line":3}},{"line":123,"address":[],"length":0,"stats":{"Line":1}},{"line":125,"address":[],"length":0,"stats":{"Line":1}},{"line":133,"address":[2015312,2015627],"length":1,"stats":{"Line":1}},{"line":134,"address":[2015625,2015342,2015414],"length":1,"stats":{"Line":3}},{"line":135,"address":[],"length":0,"stats":{"Line":2}},{"line":137,"address":[],"length":0,"stats":{"Line":2}},{"line":143,"address":[],"length":0,"stats":{"Line":1}},{"line":144,"address":[],"length":0,"stats":{"Line":1}},{"line":158,"address":[],"length":0,"stats":{"Line":3}},{"line":161,"address":[2011834,2011583,2011634,2011993,2011942,2011783],"length":1,"stats":{"Line":6}},{"line":190,"address":[2011504,2011696,2011872],"length":1,"stats":{"Line":6}},{"line":191,"address":[],"length":0,"stats":{"Line":3}},{"line":232,"address":[],"length":0,"stats":{"Line":3}},{"line":233,"address":[2011909,2011541,2011733],"length":1,"stats":{"Line":3}},{"line":251,"address":[],"length":0,"stats":{"Line":1}},{"line":252,"address":[],"length":0,"stats":{"Line":4}},{"line":253,"address":[2011451,2011394],"length":1,"stats":{"Line":2}},{"line":255,"address":[],"length":0,"stats":{"Line":1}},{"line":276,"address":[],"length":0,"stats":{"Line":0}},{"line":277,"address":[],"length":0,"stats":{"Line":0}},{"line":278,"address":[],"length":0,"stats":{"Line":0}},{"line":280,"address":[],"length":0,"stats":{"Line":0}},{"line":289,"address":[],"length":0,"stats":{"Line":3}},{"line":291,"address":[2012970,2012898,2012098,2012498,2012570,2012170],"length":1,"stats":{"Line":11}},{"line":295,"address":[2012272,2012622,2013022,2012672,2012222,2013072],"length":1,"stats":{"Line":9}},{"line":296,"address":[2012720,2013180,2012320,2012380,2012780,2013120],"length":1,"stats":{"Line":8}},{"line":299,"address":[2012283,2012683,2013083],"length":1,"stats":{"Line":3}},{"line":334,"address":[2008496,2008640,2008672,2008831,2008853,2008662],"length":1,"stats":{"Line":3}},{"line":341,"address":[],"length":0,"stats":{"Line":2}},{"line":345,"address":[2068676,2068640,2068857,2068800,2068830,2068649],"length":1,"stats":{"Line":7}},{"line":346,"address":[],"length":0,"stats":{"Line":1}},{"line":347,"address":[2068958,2068766],"length":1,"stats":{"Line":1}},{"line":348,"address":[],"length":0,"stats":{"Line":1}},{"line":353,"address":[2008778,2008587],"length":1,"stats":{"Line":1}},{"line":355,"address":[2008803,2008612],"length":1,"stats":{"Line":1}},{"line":357,"address":[],"length":0,"stats":{"Line":0}},{"line":403,"address":[2009088,2009275,2009051,2009073,2009297,2008864],"length":1,"stats":{"Line":2}},{"line":410,"address":[],"length":0,"stats":{"Line":4}},{"line":411,"address":[2008948,2009172],"length":1,"stats":{"Line":1}},{"line":416,"address":[2009189,2008965],"length":1,"stats":{"Line":3}},{"line":417,"address":[2069104,2069264],"length":1,"stats":{"Line":0}},{"line":418,"address":[2069118,2069278],"length":1,"stats":{"Line":0}},{"line":419,"address":[2069292,2069132],"length":1,"stats":{"Line":0}},{"line":424,"address":[],"length":0,"stats":{"Line":1}},{"line":426,"address":[2009245,2009021],"length":1,"stats":{"Line":1}},{"line":428,"address":[],"length":0,"stats":{"Line":1}},{"line":463,"address":[],"length":0,"stats":{"Line":4}},{"line":465,"address":[2013280,2013648,2013504],"length":1,"stats":{"Line":5}},{"line":466,"address":[],"length":0,"stats":{"Line":5}},{"line":514,"address":[],"length":0,"stats":{"Line":0}},{"line":516,"address":[],"length":0,"stats":{"Line":0}},{"line":517,"address":[],"length":0,"stats":{"Line":0}},{"line":519,"address":[],"length":0,"stats":{"Line":0}},{"line":543,"address":[2013456,2013462,2013392],"length":1,"stats":{"Line":1}},{"line":544,"address":[],"length":0,"stats":{"Line":1}},{"line":545,"address":[],"length":0,"stats":{"Line":0}},{"line":550,"address":[2013760,2014129,2014135],"length":1,"stats":{"Line":1}},{"line":552,"address":[2013810,2013882],"length":1,"stats":{"Line":2}},{"line":556,"address":[],"length":0,"stats":{"Line":2}},{"line":557,"address":[],"length":0,"stats":{"Line":0}},{"line":560,"address":[2013995],"length":1,"stats":{"Line":1}},{"line":594,"address":[2009312,2009669,2009488,2009647,2009456,2009478],"length":1,"stats":{"Line":3}},{"line":601,"address":[],"length":0,"stats":{"Line":2}},{"line":605,"address":[2009383,2009574],"length":1,"stats":{"Line":7}},{"line":606,"address":[],"length":0,"stats":{"Line":1}},{"line":607,"address":[],"length":0,"stats":{"Line":1}},{"line":608,"address":[],"length":0,"stats":{"Line":1}},{"line":613,"address":[2009403,2009594],"length":1,"stats":{"Line":1}},{"line":615,"address":[2009428,2009619],"length":1,"stats":{"Line":1}},{"line":617,"address":[],"length":0,"stats":{"Line":0}},{"line":663,"address":[2009680,2009867,2010091,2009889,2009904,2010113],"length":1,"stats":{"Line":2}},{"line":670,"address":[],"length":0,"stats":{"Line":4}},{"line":671,"address":[],"length":0,"stats":{"Line":1}},{"line":676,"address":[],"length":0,"stats":{"Line":3}},{"line":677,"address":[],"length":0,"stats":{"Line":0}},{"line":678,"address":[],"length":0,"stats":{"Line":0}},{"line":679,"address":[],"length":0,"stats":{"Line":0}},{"line":684,"address":[],"length":0,"stats":{"Line":1}},{"line":686,"address":[2009837,2010061],"length":1,"stats":{"Line":1}},{"line":688,"address":[],"length":0,"stats":{"Line":1}},{"line":728,"address":[],"length":0,"stats":{"Line":1}},{"line":730,"address":[],"length":0,"stats":{"Line":1}},{"line":731,"address":[],"length":0,"stats":{"Line":1}},{"line":772,"address":[],"length":0,"stats":{"Line":0}},{"line":774,"address":[],"length":0,"stats":{"Line":0}},{"line":775,"address":[],"length":0,"stats":{"Line":0}},{"line":777,"address":[],"length":0,"stats":{"Line":0}},{"line":801,"address":[],"length":0,"stats":{"Line":0}},{"line":802,"address":[],"length":0,"stats":{"Line":0}},{"line":803,"address":[],"length":0,"stats":{"Line":0}},{"line":824,"address":[],"length":0,"stats":{"Line":1}},{"line":825,"address":[],"length":0,"stats":{"Line":1}},{"line":852,"address":[],"length":0,"stats":{"Line":2}},{"line":853,"address":[],"length":0,"stats":{"Line":2}},{"line":867,"address":[],"length":0,"stats":{"Line":0}},{"line":868,"address":[],"length":0,"stats":{"Line":0}},{"line":869,"address":[],"length":0,"stats":{"Line":0}},{"line":871,"address":[],"length":0,"stats":{"Line":0}}],"covered":103,"coverable":132},{"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// TODO add helper types for poisonable mutex and so on\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`], [`try_lock`], [`read`], and [`try_read`] methods return a\n/// [`Result`] which indicates whether the lock has been poisoned or not. The\n/// [`PoisonError`] type has an [`into_inner`] method which will return the\n/// guard that normally would have been returned for a successful lock. This\n/// allows access to the data, despite the lock being poisoned. The scoped\n/// locking methods (such as [`scoped_lock`]) will pass the [`Result`] into the\n/// given closure. Poisoning will occur if the closure panics.\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///\n/// [`Lockable`]: `crate::lockable::Lockable`\n/// [`lock`]: `Poisonable::lock`\n/// [`try_lock`]: `Poisonable::try_lock`\n/// [`read`]: `Poisonable::read`\n/// [`try_read`]: `Poisonable::try_read`\n/// [`scoped_lock`]: `Poisonable::scoped_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`]. When this structure is dropped (falls\n/// out of scope), the lock will be unlocked.\n///\n/// This is similar to a [`PoisonGuard`], except that it does not hold a\n/// [`ThreadKey`].\n///\n/// The data protected by the underlying lock can be accessed through this\n/// guard via its [`Deref`] and [`DerefMut`] implementations.\n///\n/// This structure is created when passing a `Poisonable` into another lock\n/// wrapper, such as [`LockCollection`], and obtaining a guard through the\n/// wrapper type.\n///\n/// [`Deref`]: `std::ops::Deref`\n/// [`DerefMut`]: `std::ops::DerefMut`\n/// [`ThreadKey`]: `crate::ThreadKey`\n/// [`LockCollection`]: `crate::LockCollection`\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`]. When this structure is dropped (falls\n/// out of scope), the lock will be unlocked.\n///\n/// The data protected by the underlying lock can be accessed through this\n/// guard via its [`Deref`] and [`DerefMut`] implementations.\n///\n/// This method is created by calling the [`lock`], [`try_lock`], [`read`], and\n/// [`try_read`] methods on [`Poisonable`]\n///\n/// This guard holds a [`ThreadKey`], so it is not possible to lock anything\n/// else until this guard is dropped. The [`ThreadKey`] can be reacquired by\n/// calling [`Poisonable::unlock`], or [`Poisonable::unlock_read`].\n///\n/// [`lock`]: `Poisonable::lock`\n/// [`try_lock`]: `Poisonable::try_lock`\n/// [`read`]: `Poisonable::read`\n/// [`try_read`]: `Poisonable::try_read`\n/// [`Deref`]: `std::ops::Deref`\n/// [`DerefMut`]: `std::ops::DerefMut`\n/// [`ThreadKey`]: `crate::ThreadKey`\n/// [`LockCollection`]: `crate::LockCollection`\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.\n///\n/// A [`Poisonable`] is poisoned whenever a thread fails while the lock is\n/// held. For a lock in the poisoned state, unless the state is cleared\n/// manually, all future acquisitions will return this error.\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 operation result is contained within. The [`Err`] variant\n/// indicates that 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 as _;\n\tuse crate::{LockCollection, Mutex, RwLock, ThreadKey};\n\n\t#[test]\n\tfn locking_poisoned_mutex_returns_error_in_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = LockCollection::new(Poisonable::new(Mutex::new(42)));\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet mut guard1 = mutex.lock(key);\n\t\t\t\tlet guard = guard1.as_deref_mut().unwrap();\n\t\t\t\tassert_eq!(**guard, 42);\n\t\t\t\tpanic!();\n\n\t\t\t\t#[expect(unreachable_code)]\n\t\t\t\tdrop(guard1);\n\t\t\t})\n\t\t\t.join()\n\t\t\t.unwrap_err();\n\t\t});\n\n\t\tlet error = mutex.lock(key);\n\t\tlet error = error.as_deref().unwrap_err();\n\t\tassert_eq!(***error.get_ref(), 42);\n\t}\n\n\t#[test]\n\tfn locking_poisoned_rwlock_returns_error_in_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = LockCollection::new(Poisonable::new(RwLock::new(42)));\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet mut guard1 = mutex.read(key);\n\t\t\t\tlet guard = guard1.as_deref_mut().unwrap();\n\t\t\t\tassert_eq!(**guard, 42);\n\t\t\t\tpanic!();\n\n\t\t\t\t#[expect(unreachable_code)]\n\t\t\t\tdrop(guard1);\n\t\t\t})\n\t\t\t.join()\n\t\t\t.unwrap_err();\n\t\t});\n\n\t\tlet error = mutex.read(key);\n\t\tlet error = error.as_deref().unwrap_err();\n\t\tassert_eq!(***error.get_ref(), 42);\n\t}\n\n\t#[test]\n\tfn non_poisoned_get_mut_is_ok() {\n\t\tlet mut mutex = Poisonable::new(Mutex::new(42));\n\t\tlet guard = mutex.get_mut();\n\t\tassert!(guard.is_ok());\n\t\tassert_eq!(*guard.unwrap(), 42);\n\t}\n\n\t#[test]\n\tfn non_poisoned_get_mut_is_err() {\n\t\tlet mut mutex = Poisonable::new(Mutex::new(42));\n\n\t\tlet _ = std::panic::catch_unwind(|| {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t#[expect(unused_variables)]\n\t\t\tlet guard = mutex.lock(key);\n\t\t\tpanic!();\n\t\t\t#[expect(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#[expect(unused_variables)]\n\t\t\tlet guard = mutex.lock(key);\n\t\t\tpanic!();\n\t\t\t#[expect(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#[expect(unused_variables)]\n\t\t\tlet guard = mutex.lock(key);\n\t\t\tpanic!();\n\t\t\t#[expect(unreachable_code)]\n\t\t\tdrop(guard);\n\t\t})\n\t\t.unwrap_err();\n\n\t\tlet error = mutex.into_child().unwrap_err();\n\t\tassert_eq!(error.into_inner().into_inner(), \"foo\");\n\t}\n\n\t#[test]\n\tfn scoped_lock_can_poison() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = Poisonable::new(Mutex::new(42));\n\n\t\tlet r = std::panic::catch_unwind(|| {\n\t\t\tmutex.scoped_lock(key, |num| {\n\t\t\t\t*num.unwrap() = 56;\n\t\t\t\tpanic!();\n\t\t\t})\n\t\t});\n\t\tassert!(r.is_err());\n\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tassert!(mutex.is_poisoned());\n\t\tmutex.scoped_lock(key, |num| {\n\t\t\tlet Err(error) = num else { panic!() };\n\t\t\tmutex.clear_poison();\n\t\t\tlet guard = error.into_inner();\n\t\t\tassert_eq!(*guard, 56);\n\t\t});\n\t\tassert!(!mutex.is_poisoned());\n\t}\n\n\t#[test]\n\tfn scoped_try_lock_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = Poisonable::new(Mutex::new(42));\n\t\tlet guard = mutex.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = mutex.scoped_try_lock(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn scoped_try_lock_can_succeed() {\n\t\tlet rwlock = Poisonable::new(RwLock::new(42));\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = rwlock.scoped_try_lock(key, |guard| {\n\t\t\t\t\tassert_eq!(*guard.unwrap(), 42);\n\t\t\t\t});\n\t\t\t\tassert!(r.is_ok());\n\t\t\t});\n\t\t});\n\t}\n\n\t#[test]\n\tfn scoped_read_can_poison() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = Poisonable::new(RwLock::new(42));\n\n\t\tlet r = std::panic::catch_unwind(|| {\n\t\t\tmutex.scoped_read(key, |num| {\n\t\t\t\tassert_eq!(*num.unwrap(), 42);\n\t\t\t\tpanic!();\n\t\t\t})\n\t\t});\n\t\tassert!(r.is_err());\n\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tassert!(mutex.is_poisoned());\n\t\tmutex.scoped_read(key, |num| {\n\t\t\tlet Err(error) = num else { panic!() };\n\t\t\tmutex.clear_poison();\n\t\t\tlet guard = error.into_inner();\n\t\t\tassert_eq!(*guard, 42);\n\t\t});\n\t\tassert!(!mutex.is_poisoned());\n\t}\n\n\t#[test]\n\tfn scoped_try_read_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet rwlock = Poisonable::new(RwLock::new(42));\n\t\tlet guard = rwlock.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = rwlock.scoped_try_read(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn scoped_try_read_can_succeed() {\n\t\tlet rwlock = Poisonable::new(RwLock::new(42));\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = rwlock.scoped_try_read(key, |guard| {\n\t\t\t\t\tassert_eq!(*guard.unwrap(), 42);\n\t\t\t\t});\n\t\t\t\tassert!(r.is_ok());\n\t\t\t});\n\t\t});\n\t}\n\n\t#[test]\n\tfn display_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = Poisonable::new(Mutex::new(\"Hello, world!\"));\n\n\t\tlet guard = mutex.lock(key).unwrap();\n\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\");\n\t}\n\n\t#[test]\n\tfn ref_as_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = LockCollection::new(Poisonable::new(Mutex::new(\"foo\")));\n\t\tlet guard = collection.lock(key);\n\t\tlet Ok(ref guard) = guard.as_ref() else {\n\t\t\tpanic!()\n\t\t};\n\t\tassert_eq!(**guard.as_ref(), \"foo\");\n\t}\n\n\t#[test]\n\tfn ref_as_mut() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = LockCollection::new(Poisonable::new(Mutex::new(\"foo\")));\n\t\tlet mut guard1 = collection.lock(key);\n\t\tlet Ok(ref mut guard) = guard1.as_mut() else {\n\t\t\tpanic!()\n\t\t};\n\t\tlet guard = guard.as_mut();\n\t\t**guard = \"bar\";\n\n\t\tlet key = LockCollection::\u003cPoisonable\u003cMutex\u003c_\u003e\u003e\u003e::unlock(guard1);\n\t\tlet guard = collection.lock(key);\n\t\tlet guard = guard.as_deref().unwrap();\n\t\tassert_eq!(*guard.as_ref(), \"bar\");\n\t}\n\n\t#[test]\n\tfn guard_as_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = Poisonable::new(Mutex::new(\"foo\"));\n\t\tlet guard = collection.lock(key);\n\t\tlet Ok(ref guard) = guard.as_ref() else {\n\t\t\tpanic!()\n\t\t};\n\t\tassert_eq!(**guard.as_ref(), \"foo\");\n\t}\n\n\t#[test]\n\tfn guard_as_mut() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = Poisonable::new(Mutex::new(\"foo\"));\n\t\tlet mut guard1 = mutex.lock(key);\n\t\tlet Ok(ref mut guard) = guard1.as_mut() else {\n\t\t\tpanic!()\n\t\t};\n\t\tlet guard = guard.as_mut();\n\t\t**guard = \"bar\";\n\n\t\tlet key = Poisonable::\u003cMutex\u003c_\u003e\u003e::unlock(guard1.unwrap());\n\t\tlet guard = mutex.lock(key);\n\t\tlet guard = guard.as_deref().unwrap();\n\t\tassert_eq!(*guard, \"bar\");\n\t}\n\n\t#[test]\n\tfn deref_mut_in_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = LockCollection::new(Poisonable::new(Mutex::new(42)));\n\t\tlet mut guard1 = collection.lock(key);\n\t\tlet Ok(ref mut guard) = guard1.as_mut() else {\n\t\t\tpanic!()\n\t\t};\n\t\t// TODO make this more convenient\n\t\tassert_eq!(***guard, 42);\n\t\t***guard = 24;\n\n\t\tlet key = LockCollection::\u003cPoisonable\u003cMutex\u003c_\u003e\u003e\u003e::unlock(guard1);\n\t\t_ = collection.lock(key);\n\t}\n\n\t#[test]\n\tfn get_ptrs() {\n\t\tlet mutex = Mutex::new(5);\n\t\tlet poisonable = Poisonable::new(mutex);\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tpoisonable.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tassert!(std::ptr::addr_eq(lock_ptrs[0], \u0026raw const poisonable.inner));\n\t}\n\n\t#[test]\n\tfn clear_poison_for_poisoned_mutex() {\n\t\tlet mutex = Arc::new(Poisonable::new(Mutex::new(0)));\n\t\tlet c_mutex = Arc::clone(\u0026mutex);\n\n\t\tlet _ = std::thread::spawn(move || {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\tlet _lock = c_mutex.lock(key).unwrap();\n\t\t\tpanic!(); // the mutex gets poisoned\n\t\t})\n\t\t.join();\n\n\t\tassert!(mutex.is_poisoned());\n\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet _ = mutex.lock(key).unwrap_or_else(|mut e| {\n\t\t\t**e.get_mut() = 1;\n\t\t\tmutex.clear_poison();\n\t\t\te.into_inner()\n\t\t});\n\n\t\tassert!(!mutex.is_poisoned());\n\t}\n\n\t#[test]\n\tfn clear_poison_for_poisoned_rwlock() {\n\t\tlet lock = Arc::new(Poisonable::new(RwLock::new(0)));\n\t\tlet c_mutex = Arc::clone(\u0026lock);\n\n\t\tlet _ = std::thread::spawn(move || {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\tlet lock = c_mutex.read(key).unwrap();\n\t\t\tassert_eq!(*lock, 42);\n\t\t\tpanic!(); // the mutex gets poisoned\n\t\t})\n\t\t.join();\n\n\t\tassert!(lock.is_poisoned());\n\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet _ = lock.lock(key).unwrap_or_else(|mut e| {\n\t\t\t**e.get_mut() = 1;\n\t\t\tlock.clear_poison();\n\t\t\te.into_inner()\n\t\t});\n\n\t\tassert!(!lock.is_poisoned());\n\t}\n\n\t#[test]\n\tfn error_as_ref() {\n\t\tlet mutex = Poisonable::new(Mutex::new(\"foo\"));\n\n\t\tlet _ = std::panic::catch_unwind(|| {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t#[expect(unused_variables)]\n\t\t\tlet guard = mutex.lock(key);\n\t\t\tpanic!();\n\n\t\t\t#[expect(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#[expect(unused_variables)]\n\t\t\tlet guard = mutex.lock(key);\n\t\t\tpanic!();\n\n\t\t\t#[expect(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#[expect(unused_variables)]\n\t\t\tlet guard = mutex.lock(key);\n\t\t\tpanic!();\n\n\t\t\t#[expect(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 as _;\nuse crate::ThreadKey;\n\nuse super::{RwLock, RwLockReadGuard, RwLockReadRef};\n\n// These impls make things slightly easier because now you can use\n// `println!(\"{guard}\")` instead of `println!(\"{}\", *guard)`\n\n#[mutants::skip] // hashing involves PRNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Hash + ?Sized, R: RawRwLock\u003e Hash for RwLockReadRef\u003c'_, T, R\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.deref().hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug + ?Sized, R: RawRwLock\u003e Debug for RwLockReadRef\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: Display + ?Sized, R: RawRwLock\u003e Display for RwLockReadRef\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e Deref for RwLockReadRef\u003c'_, T, R\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t// safety: this is the only type that can use `value`, and there's\n\t\t// a reference to this type, so there cannot be any mutable\n\t\t// references to this value.\n\t\tunsafe { \u0026*self.0.data.get() }\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e AsRef\u003cT\u003e for RwLockReadRef\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e Drop for RwLockReadRef\u003c'_, T, R\u003e {\n\tfn drop(\u0026mut self) {\n\t\t// safety: this guard is being destroyed, so the data cannot be\n\t\t// accessed without locking again\n\t\tunsafe { self.0.raw_unlock_read() }\n\t}\n}\n\nimpl\u003c'a, T: ?Sized, R: RawRwLock\u003e RwLockReadRef\u003c'a, T, R\u003e {\n\t/// Creates an immutable reference for the underlying data of an [`RwLock`]\n\t/// without locking it or taking ownership of the key.\n\t#[must_use]\n\tpub(crate) const unsafe fn new(mutex: \u0026'a RwLock\u003cT, R\u003e) -\u003e Self {\n\t\tSelf(mutex, PhantomData)\n\t}\n}\n\n#[mutants::skip] // hashing involves PRNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Hash + ?Sized, R: RawRwLock\u003e Hash for RwLockReadGuard\u003c'_, T, R\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.deref().hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug + ?Sized, R: RawRwLock\u003e Debug for RwLockReadGuard\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: Display + ?Sized, R: RawRwLock\u003e Display for RwLockReadGuard\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e Deref for RwLockReadGuard\u003c'_, T, R\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t\u0026self.rwlock\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e AsRef\u003cT\u003e for RwLockReadGuard\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself\n\t}\n}\n\nimpl\u003c'a, T: ?Sized, R: RawRwLock\u003e RwLockReadGuard\u003c'a, T, R\u003e {\n\t/// Create a guard to the given mutex. Undefined if multiple guards to the\n\t/// same mutex exist at once.\n\t#[must_use]\n\tpub(super) const unsafe fn new(rwlock: \u0026'a RwLock\u003cT, R\u003e, thread_key: ThreadKey) -\u003e Self {\n\t\tSelf {\n\t\t\trwlock: RwLockReadRef(rwlock, PhantomData),\n\t\t\tthread_key,\n\t\t}\n\t}\n}\n\nunsafe impl\u003cT: ?Sized + Sync, R: RawRwLock + Sync\u003e Sync for RwLockReadRef\u003c'_, T, R\u003e {}\n","traces":[{"line":33,"address":[2259440],"length":1,"stats":{"Line":1}},{"line":34,"address":[],"length":0,"stats":{"Line":1}},{"line":41,"address":[],"length":0,"stats":{"Line":3}},{"line":45,"address":[2265605,2265637],"length":1,"stats":{"Line":3}},{"line":50,"address":[],"length":0,"stats":{"Line":1}},{"line":51,"address":[],"length":0,"stats":{"Line":1}},{"line":56,"address":[1831296,1831312],"length":1,"stats":{"Line":3}},{"line":59,"address":[620421,620437,620453],"length":1,"stats":{"Line":3}},{"line":67,"address":[],"length":0,"stats":{"Line":3}},{"line":68,"address":[],"length":0,"stats":{"Line":0}},{"line":89,"address":[],"length":0,"stats":{"Line":1}},{"line":90,"address":[],"length":0,"stats":{"Line":1}},{"line":97,"address":[],"length":0,"stats":{"Line":2}},{"line":98,"address":[],"length":0,"stats":{"Line":2}},{"line":103,"address":[],"length":0,"stats":{"Line":1}},{"line":104,"address":[],"length":0,"stats":{"Line":1}},{"line":112,"address":[],"length":0,"stats":{"Line":1}},{"line":114,"address":[],"length":0,"stats":{"Line":0}}],"covered":16,"coverable":18},{"path":["/","home","botahamec","Projects","happylock","src","rwlock","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_write(\u0026self) {\n\t\tassert!(\n\t\t\t!self.poison.is_poisoned(),\n\t\t\t\"The read-write lock has been killed\"\n\t\t);\n\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.lock_exclusive(), || self.poison())\n\t}\n\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool {\n\t\tif self.poison.is_poisoned() {\n\t\t\treturn false;\n\t\t}\n\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.try_lock_exclusive(), || self.poison())\n\t}\n\n\tunsafe fn raw_unlock_write(\u0026self) {\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.unlock_exclusive(), || self.poison())\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tassert!(\n\t\t\t!self.poison.is_poisoned(),\n\t\t\t\"The read-write lock has been killed\"\n\t\t);\n\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.lock_shared(), || self.poison())\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tif self.poison.is_poisoned() {\n\t\t\treturn false;\n\t\t}\n\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.try_lock_shared(), || self.poison())\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.unlock_shared(), || self.poison())\n\t}\n}\n\nunsafe impl\u003cT, R: RawRwLock\u003e Lockable for RwLock\u003cT, R\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= RwLockWriteRef\u003c'g, T, R\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= \u0026'a mut T\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tptrs.push(self);\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tRwLockWriteRef::new(self)\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.data.get().as_mut().unwrap_unchecked()\n\t}\n}\n\nunsafe impl\u003cT, R: RawRwLock\u003e Sharable for RwLock\u003cT, R\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= RwLockReadRef\u003c'g, T, R\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= \u0026'a T\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tRwLockReadRef::new(self)\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tself.data.get().as_ref().unwrap_unchecked()\n\t}\n}\n\nunsafe impl\u003cT, R: RawRwLock\u003e OwnedLockable for RwLock\u003cT, R\u003e {}\n\nimpl\u003cT, R: RawRwLock\u003e LockableIntoInner for RwLock\u003cT, R\u003e {\n\ttype Inner = T;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tself.into_inner()\n\t}\n}\n\nimpl\u003cT, R: RawRwLock\u003e LockableGetMut for RwLock\u003cT, R\u003e {\n\ttype Inner\u003c'a\u003e\n\t\t= \u0026'a mut T\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tAsMut::as_mut(self)\n\t}\n}\n\nimpl\u003cT, R: RawRwLock\u003e RwLock\u003cT, R\u003e {\n\t/// Creates a new instance of an `RwLock\u003cT\u003e` which is unlocked.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::RwLock;\n\t///\n\t/// let lock = RwLock::new(5);\n\t///\n\t///\n\t/// ```\n\t#[must_use]\n\tpub const fn new(data: T) -\u003e Self {\n\t\tSelf {\n\t\t\tdata: UnsafeCell::new(data),\n\t\t\tpoison: PoisonFlag::new(),\n\t\t\traw: R::INIT,\n\t\t}\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug, R: RawRwLock\u003e Debug for RwLock\u003cT, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t// safety: this is just a try lock, and the value is dropped\n\t\t// immediately after, so there's no risk of blocking ourselves\n\t\t// or any other threads\n\t\tif let Some(value) = unsafe { self.try_read_no_key() } {\n\t\t\tf.debug_struct(\"RwLock\").field(\"data\", \u0026\u0026*value).finish()\n\t\t} else {\n\t\t\tstruct LockedPlaceholder;\n\t\t\timpl Debug for LockedPlaceholder {\n\t\t\t\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t\t\t\tf.write_str(\"\u003clocked\u003e\")\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tf.debug_struct(\"RwLock\")\n\t\t\t\t.field(\"data\", \u0026LockedPlaceholder)\n\t\t\t\t.finish()\n\t\t}\n\t}\n}\n\nimpl\u003cT: Default, R: RawRwLock\u003e Default for RwLock\u003cT, R\u003e {\n\tfn default() -\u003e Self {\n\t\tSelf::new(T::default())\n\t}\n}\n\nimpl\u003cT, R: RawRwLock\u003e From\u003cT\u003e for RwLock\u003cT, R\u003e {\n\tfn from(value: T) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\n// We don't need a `get_mut` because we don't have mutex poisoning. Hurray!\n// This is safe because you can't have a mutable reference to the lock if it's\n// locked. Being locked requires an immutable reference because of the guard.\nimpl\u003cT: ?Sized, R\u003e AsMut\u003cT\u003e for RwLock\u003cT, R\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself.data.get_mut()\n\t}\n}\n\nimpl\u003cT, R\u003e RwLock\u003cT, R\u003e {\n\t/// Consumes this `RwLock`, returning the underlying data.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(String::new());\n\t/// {\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 needs to take\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 lock = RwLock::new(0);\n\t/// *lock.get_mut() = 10;\n\t/// assert_eq!(*lock.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\t/// Acquires a shared lock, blocking until it is safe to do so, and then\n\t/// unlocks after the provided function returns.\n\t///\n\t/// This function is useful to ensure that a `RwLock` is never accidentally\n\t/// locked forever by leaking the `ReadGuard`. Even if the function panics,\n\t/// this function will gracefully notice the panic, and unlock. This function\n\t/// provides no guarantees with respect to the ordering of whether contentious\n\t/// readers or writers will acquire the lock first.\n\t///\n\t/// # Panics\n\t///\n\t/// This function will panic if the provided function also panics. However,\n\t/// `RwLock` will be safely unlocked in this case, allowing the `RwLock` to be\n\t/// locked again later.\n\t///\n\t/// # Example\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, RwLock};\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(42);\n\t///\n\t/// let x = lock.scoped_read(\u0026mut key, |number| {\n\t/// *number\n\t/// });\n\t/// assert_eq!(x, 42);\n\t/// ```\n\tpub fn scoped_read\u003c'a, Ret\u003e(\u0026'a self, key: impl Keyable, f: impl FnOnce(\u0026'a T) -\u003e Ret) -\u003e Ret {\n\t\tunsafe {\n\t\t\t// safety: we have the key\n\t\t\tself.raw_read();\n\n\t\t\t// safety: the data has been locked\n\t\t\tlet r = handle_unwind(\n\t\t\t\t|| f(self.data.get().as_ref().unwrap_unchecked()),\n\t\t\t\t|| self.raw_unlock_read(),\n\t\t\t);\n\n\t\t\t// ensures the key is held long enough\n\t\t\tdrop(key);\n\n\t\t\t// safety: the mutex is still locked\n\t\t\tself.raw_unlock_read();\n\n\t\t\tr\n\t\t}\n\t}\n\n\t/// Attempts to acquire a shared lock to the `RwLock` without blocking,\n\t/// and then unlocks it once the provided function returns.\n\t///\n\t/// This function implements a non-blocking variant of [`scoped_read`].\n\t/// Unlike `scoped_read`, if the `RwLock` is exclusively locked, then the\n\t/// provided function will not run, and the given [`Keyable`] is returned.\n\t/// This method provides no guarantees with respect to the ordering of whether\n\t/// contentious readers of writers will acquire the lock first.\n\t///\n\t/// # Errors\n\t///\n\t/// If the `RwLock` is already exclusively locked, then the provided function\n\t/// will not run. `Err` is returned with the given key.\n\t///\n\t/// # Panics\n\t///\n\t/// If the provided function panics, then the panic will be bubbled up and\n\t/// rethrown. The `RwLock` will also be gracefully unlocked, allowing the\n\t/// `RwLock` to be locked again.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(42);\n\t///\n\t/// let result = lock.scoped_try_read(\u0026mut key, |num| {\n\t/// *num\n\t/// });\n\t///\n\t/// match result {\n\t/// Ok(val) =\u003e println!(\"The number is {val}\"),\n\t/// Err(_) =\u003e unreachable!(),\n\t/// }\n\t/// ```\n\t///\n\t/// [`scoped_read`]: RwLock::scoped_read\n\tpub fn scoped_try_read\u003c'a, Key: Keyable, Ret\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl FnOnce(\u0026'a T) -\u003e Ret,\n\t) -\u003e Result\u003cRet, Key\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the 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 data has been locked\n\t\t\tlet r = handle_unwind(\n\t\t\t\t|| f(self.data.get().as_ref().unwrap_unchecked()),\n\t\t\t\t|| self.raw_unlock_read(),\n\t\t\t);\n\n\t\t\t// ensures the key is held long enough\n\t\t\tdrop(key);\n\n\t\t\t// safety: the mutex is still locked\n\t\t\tself.raw_unlock_read();\n\n\t\t\tOk(r)\n\t\t}\n\t}\n\n\t/// Acquires an exclusive lock, blocking until it is safe to do so, and then\n\t/// unlocks after the provided function returns.\n\t///\n\t/// This function is useful to ensure that a `RwLock` is never accidentally\n\t/// locked forever by leaking the `WriteGuard`. Even if the function panics,\n\t/// this function will gracefully notice the panic, and unlock. This method\n\t/// does not provide any guarantees with respect to the ordering of whether\n\t/// contentious readers or writers will acquire the lock first.\n\t///\n\t/// # Panics\n\t///\n\t/// This function will panic if the provided function also panics. However,\n\t/// `RwLock` will be safely unlocked in this case, allowing the `RwLock` to be\n\t/// locked again later.\n\t///\n\t/// # Example\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, RwLock};\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(42);\n\t///\n\t/// let x = lock.scoped_write(\u0026mut key, |number| {\n\t/// *number += 5;\n\t/// *number\n\t/// });\n\t/// assert_eq!(x, 47);\n\t/// ```\n\tpub fn scoped_write\u003c'a, Ret\u003e(\n\t\t\u0026'a self,\n\t\tkey: impl Keyable,\n\t\tf: impl FnOnce(\u0026'a mut T) -\u003e Ret,\n\t) -\u003e Ret {\n\t\tunsafe {\n\t\t\t// safety: we have the key\n\t\t\tself.raw_write();\n\n\t\t\t// safety: the data has been locked\n\t\t\tlet r = handle_unwind(\n\t\t\t\t|| f(self.data.get().as_mut().unwrap_unchecked()),\n\t\t\t\t|| self.raw_unlock_write(),\n\t\t\t);\n\n\t\t\t// ensures the key is held long enough\n\t\t\tdrop(key);\n\n\t\t\t// safety: the mutex is still locked\n\t\t\tself.raw_unlock_write();\n\n\t\t\tr\n\t\t}\n\t}\n\n\t/// Attempts to acquire an exclusive lock to the `RwLock` without blocking,\n\t/// and then unlocks it once the provided function returns.\n\t///\n\t/// This function implements a non-blocking variant of [`scoped_write`].\n\t/// Unlike `scoped_write`, if the `RwLock` is not already unlocked, then the\n\t/// provided function will not run, and the given [`Keyable`] is returned.\n\t/// This method 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/// If the `RwLock` is already locked, then the provided function will not\n\t/// run. `Err` is returned with the given key.\n\t///\n\t/// # Panics\n\t///\n\t/// If the provided function panics, then the panic will be bubbled up and\n\t/// rethrown. The `RwLock` will also be gracefully unlocked, allowing the\n\t/// `RwLock` to be locked again.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(42);\n\t///\n\t/// let result = lock.scoped_try_write(\u0026mut key, |num| {\n\t/// *num\n\t/// });\n\t///\n\t/// match result {\n\t/// Ok(val) =\u003e println!(\"The number is {val}\"),\n\t/// Err(_) =\u003e unreachable!(),\n\t/// }\n\t/// ```\n\t///\n\t/// [`scoped_write`]: RwLock::scoped_write\n\tpub fn scoped_try_write\u003c'a, Key: Keyable, Ret\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl FnOnce(\u0026'a mut T) -\u003e Ret,\n\t) -\u003e Result\u003cRet, Key\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the key\n\t\t\tif !self.raw_try_write() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: the data has been locked\n\t\t\tlet r = handle_unwind(\n\t\t\t\t|| f(self.data.get().as_mut().unwrap_unchecked()),\n\t\t\t\t|| self.raw_unlock_write(),\n\t\t\t);\n\n\t\t\t// ensures the key is held long enough\n\t\t\tdrop(key);\n\n\t\t\t// safety: the mutex is still locked\n\t\t\tself.raw_unlock_write();\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. This method does not provide any guarantees\n\t/// with respect to the ordering of whether contentious readers or writers\n\t/// will acquire the lock first.\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::thread;\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\t/// thread::scope(|s| {\n\t/// s.spawn(|| {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let r = lock.read(key);\n\t/// });\n\t/// });\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/// This function will return an error containing the [`ThreadKey`] if the\n\t/// `RwLock` could not be acquired because it was already locked exclusively.\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\tunsafe {\n\t\t\tif self.raw_try_read() {\n\t\t\t\t// safety: the lock is locked first\n\t\t\t\tSome(RwLockReadRef(self, PhantomData))\n\t\t\t} else {\n\t\t\t\tNone\n\t\t\t}\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\tunsafe {\n\t\t\tif self.raw_try_write() {\n\t\t\t\t// safety: the lock is locked first\n\t\t\t\tSome(RwLockWriteRef(self, PhantomData))\n\t\t\t} else {\n\t\t\t\tNone\n\t\t\t}\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/// let mut n = lock.write(key);\n\t/// *n = 2;\n\t///\n\t/// let key = RwLock::unlock_write(n);\n\t/// assert_eq!(*lock.read(key), 2);\n\t/// ```\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\tpub fn write(\u0026self, key: ThreadKey) -\u003e RwLockWriteGuard\u003c'_, T, R\u003e {\n\t\tunsafe {\n\t\t\tself.raw_write();\n\n\t\t\t// safety: the lock is locked first\n\t\t\tRwLockWriteGuard::new(self, key)\n\t\t}\n\t}\n\n\t/// Attempts to lock this `RwLock` with exclusive write access, without\n\t/// blocking.\n\t///\n\t/// This function does not block. If the lock could not be acquired at this\n\t/// time, then `Err` 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/// This function will return an error containing the [`ThreadKey`] if the\n\t/// `RwLock` could not be acquired because it was already locked exclusively.\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 key = match lock.try_write(key) {\n\t/// Ok(mut n) =\u003e {\n\t/// assert_eq!(*n, 1);\n\t/// *n = 2;\n\t/// RwLock::unlock_write(n)\n\t/// }\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// let n = lock.read(key);\n\t/// assert_eq!(*n, 2);\n\t/// ```\n\tpub fn try_write(\u0026self, key: ThreadKey) -\u003e Result\u003cRwLockWriteGuard\u003c'_, T, R\u003e, ThreadKey\u003e {\n\t\tunsafe {\n\t\t\tif self.raw_try_write() {\n\t\t\t\t// safety: the lock is locked first\n\t\t\t\tOk(RwLockWriteGuard::new(self, key))\n\t\t\t} else {\n\t\t\t\tErr(key)\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Returns `true` if the rwlock is currently locked in any way\n\t#[cfg(test)]\n\tpub(crate) fn is_locked(\u0026self) -\u003e bool {\n\t\tself.raw.is_locked()\n\t}\n\n\t/// Immediately drops the guard, and consequently releases the shared lock.\n\t///\n\t/// This function is equivalent to calling [`drop`] on the guard, except\n\t/// that it returns the key that was used to create it. Alternatively, the\n\t/// guard will be automatically dropped when it goes out of scope.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(0);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// assert_eq!(*guard, 0);\n\t/// let key = RwLock::unlock_read(guard);\n\t/// ```\n\t#[must_use]\n\tpub fn unlock_read(guard: RwLockReadGuard\u003c'_, T, R\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.rwlock);\n\t\tguard.thread_key\n\t}\n\n\t/// Immediately drops the guard, and consequently releases the exclusive\n\t/// lock.\n\t///\n\t/// This function is equivalent to calling [`drop`] on the guard, except that\n\t/// it returns the key that was used to create it. Alternatively, the guard\n\t/// 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/// let guard = lock.read(key);\n\t/// assert_eq!(*guard, 20);\n\t/// ```\n\t#[must_use]\n\tpub fn unlock_write(guard: RwLockWriteGuard\u003c'_, T, R\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.rwlock);\n\t\tguard.thread_key\n\t}\n}\n\nunsafe impl\u003cR: RawRwLock + Send, T: ?Sized + Send\u003e Send for RwLock\u003cT, R\u003e {}\nunsafe impl\u003cR: RawRwLock + Sync, T: ?Sized + Send\u003e Sync for RwLock\u003cT, R\u003e {}\n","traces":[{"line":17,"address":[673056,672608],"length":1,"stats":{"Line":6}},{"line":18,"address":[2259765,2260197],"length":1,"stats":{"Line":6}},{"line":21,"address":[],"length":0,"stats":{"Line":5}},{"line":22,"address":[672749,673197],"length":1,"stats":{"Line":4}},{"line":23,"address":[],"length":0,"stats":{"Line":0}},{"line":24,"address":[],"length":0,"stats":{"Line":0}},{"line":28,"address":[682863,682415],"length":1,"stats":{"Line":6}},{"line":29,"address":[613460],"length":1,"stats":{"Line":20}},{"line":32,"address":[],"length":0,"stats":{"Line":9}},{"line":33,"address":[613166],"length":1,"stats":{"Line":10}},{"line":34,"address":[613208],"length":1,"stats":{"Line":5}},{"line":38,"address":[701313,701761,700877],"length":1,"stats":{"Line":6}},{"line":39,"address":[601872,601840,602160,602133,602165,601877,602128,601845],"length":1,"stats":{"Line":18}},{"line":42,"address":[672576,673024],"length":1,"stats":{"Line":7}},{"line":44,"address":[613276],"length":1,"stats":{"Line":7}},{"line":45,"address":[2269349,2269637,2269312,2269605,2269344,2269317,2269600,2269632],"length":1,"stats":{"Line":26}},{"line":48,"address":[],"length":0,"stats":{"Line":5}},{"line":49,"address":[2260258,2259826],"length":1,"stats":{"Line":2}},{"line":50,"address":[],"length":0,"stats":{"Line":0}},{"line":51,"address":[],"length":0,"stats":{"Line":0}},{"line":55,"address":[682767,682319],"length":1,"stats":{"Line":5}},{"line":56,"address":[2259811,2260243],"length":1,"stats":{"Line":19}},{"line":59,"address":[672832,672384],"length":1,"stats":{"Line":7}},{"line":60,"address":[2259982,2259550],"length":1,"stats":{"Line":7}},{"line":61,"address":[],"length":0,"stats":{"Line":2}},{"line":65,"address":[2260000,2259568],"length":1,"stats":{"Line":8}},{"line":66,"address":[612325,612288,612037,611717,612000,612293,611744,611712,612005,612320,612032,611749],"length":1,"stats":{"Line":30}},{"line":69,"address":[701808,700928,701360],"length":1,"stats":{"Line":6}},{"line":71,"address":[2259708,2260140],"length":1,"stats":{"Line":6}},{"line":72,"address":[682209,682657],"length":1,"stats":{"Line":25}},{"line":87,"address":[614128],"length":1,"stats":{"Line":16}},{"line":88,"address":[],"length":0,"stats":{"Line":15}},{"line":91,"address":[2265792,2265728],"length":1,"stats":{"Line":4}},{"line":92,"address":[673285,673349],"length":1,"stats":{"Line":4}},{"line":95,"address":[2265808],"length":1,"stats":{"Line":1}},{"line":96,"address":[],"length":0,"stats":{"Line":1}},{"line":111,"address":[],"length":0,"stats":{"Line":3}},{"line":112,"address":[],"length":0,"stats":{"Line":3}},{"line":115,"address":[],"length":0,"stats":{"Line":1}},{"line":116,"address":[],"length":0,"stats":{"Line":1}},{"line":125,"address":[2260640],"length":1,"stats":{"Line":1}},{"line":126,"address":[2260644],"length":1,"stats":{"Line":1}},{"line":136,"address":[2260736],"length":1,"stats":{"Line":1}},{"line":137,"address":[],"length":0,"stats":{"Line":1}},{"line":154,"address":[681648,681792,681845,681701],"length":1,"stats":{"Line":15}},{"line":156,"address":[671853,671709],"length":1,"stats":{"Line":15}},{"line":157,"address":[700306,700390,700594,700450,700257,700534],"length":1,"stats":{"Line":31}},{"line":188,"address":[],"length":0,"stats":{"Line":1}},{"line":189,"address":[2262766],"length":1,"stats":{"Line":1}},{"line":194,"address":[2262832],"length":1,"stats":{"Line":1}},{"line":195,"address":[2262848],"length":1,"stats":{"Line":1}},{"line":203,"address":[],"length":0,"stats":{"Line":1}},{"line":204,"address":[],"length":0,"stats":{"Line":1}},{"line":225,"address":[2258000,2258021],"length":1,"stats":{"Line":1}},{"line":226,"address":[],"length":0,"stats":{"Line":1}},{"line":247,"address":[2258064],"length":1,"stats":{"Line":1}},{"line":248,"address":[2258069],"length":1,"stats":{"Line":1}},{"line":281,"address":[2256608,2256752,2256774],"length":1,"stats":{"Line":1}},{"line":284,"address":[],"length":0,"stats":{"Line":1}},{"line":288,"address":[],"length":0,"stats":{"Line":3}},{"line":289,"address":[],"length":0,"stats":{"Line":0}},{"line":293,"address":[2256694],"length":1,"stats":{"Line":1}},{"line":296,"address":[2256729],"length":1,"stats":{"Line":1}},{"line":298,"address":[],"length":0,"stats":{"Line":0}},{"line":341,"address":[680757,680277,680304,680544,680784,680997,681237,680517,681024,681264,681477,680064],"length":1,"stats":{"Line":6}},{"line":348,"address":[681288,680635,680328,680568,681048,680395,680808,680875,680155,681115,680088,681355],"length":1,"stats":{"Line":12}},{"line":349,"address":[680166,681126,680646,681366,680406,680886],"length":1,"stats":{"Line":2}},{"line":354,"address":[],"length":0,"stats":{"Line":12}},{"line":355,"address":[623269,623429,623749,623589,623909,624064,623424,623904,624069,623744,623264,623584],"length":1,"stats":{"Line":0}},{"line":359,"address":[],"length":0,"stats":{"Line":4}},{"line":362,"address":[681448,680488,680248,680728,680968,681208],"length":1,"stats":{"Line":4}},{"line":364,"address":[680495,680975,681215,681455,680255,680735],"length":1,"stats":{"Line":4}},{"line":397,"address":[2256590,2256416],"length":1,"stats":{"Line":1}},{"line":404,"address":[2256450],"length":1,"stats":{"Line":1}},{"line":408,"address":[2256512],"length":1,"stats":{"Line":3}},{"line":409,"address":[],"length":0,"stats":{"Line":0}},{"line":413,"address":[],"length":0,"stats":{"Line":1}},{"line":416,"address":[],"length":0,"stats":{"Line":1}},{"line":418,"address":[],"length":0,"stats":{"Line":0}},{"line":461,"address":[699813,698880,699333,698640,699360,698853,700053,699120,699093,699573,699600,699840],"length":1,"stats":{"Line":12}},{"line":468,"address":[],"length":0,"stats":{"Line":24}},{"line":469,"address":[698742,699462,699942,698982,699702,699222],"length":1,"stats":{"Line":6}},{"line":474,"address":[601507,600832,601001,601027,600841,601161,601481,601641,601667,601312,601152,600867,601347,601632,600992,601321,601472,601187],"length":1,"stats":{"Line":18}},{"line":475,"address":[],"length":0,"stats":{"Line":0}},{"line":479,"address":[699509,699269,699989,698789,699029,699749],"length":1,"stats":{"Line":6}},{"line":482,"address":[699784,698824,700024,699544,699064,699304],"length":1,"stats":{"Line":6}},{"line":484,"address":[],"length":0,"stats":{"Line":6}},{"line":524,"address":[2258486,2258508,2258400],"length":1,"stats":{"Line":1}},{"line":526,"address":[2258414],"length":1,"stats":{"Line":1}},{"line":529,"address":[],"length":0,"stats":{"Line":1}},{"line":562,"address":[2258784,2258806,2258656],"length":1,"stats":{"Line":1}},{"line":564,"address":[],"length":0,"stats":{"Line":5}},{"line":566,"address":[],"length":0,"stats":{"Line":0}},{"line":568,"address":[],"length":0,"stats":{"Line":2}},{"line":575,"address":[2258272],"length":1,"stats":{"Line":1}},{"line":577,"address":[],"length":0,"stats":{"Line":1}},{"line":579,"address":[],"length":0,"stats":{"Line":1}},{"line":581,"address":[],"length":0,"stats":{"Line":0}},{"line":589,"address":[],"length":0,"stats":{"Line":1}},{"line":591,"address":[2258367,2258349],"length":1,"stats":{"Line":1}},{"line":593,"address":[],"length":0,"stats":{"Line":1}},{"line":595,"address":[2258358],"length":1,"stats":{"Line":0}},{"line":628,"address":[672112,672000,672128,672086,672214,672240],"length":1,"stats":{"Line":6}},{"line":630,"address":[],"length":0,"stats":{"Line":5}},{"line":633,"address":[672061,672189],"length":1,"stats":{"Line":4}},{"line":673,"address":[],"length":0,"stats":{"Line":2}},{"line":675,"address":[2259262,2259150,2258958,2259200,2258846,2258896,2258916,2259220],"length":1,"stats":{"Line":7}},{"line":677,"address":[2259257,2258923,2259227,2258953],"length":1,"stats":{"Line":4}},{"line":679,"address":[],"length":0,"stats":{"Line":1}},{"line":686,"address":[2258816,2259120],"length":1,"stats":{"Line":2}},{"line":687,"address":[2258821,2259125],"length":1,"stats":{"Line":2}},{"line":709,"address":[],"length":0,"stats":{"Line":1}},{"line":710,"address":[],"length":0,"stats":{"Line":1}},{"line":711,"address":[],"length":0,"stats":{"Line":0}},{"line":737,"address":[],"length":0,"stats":{"Line":1}},{"line":738,"address":[],"length":0,"stats":{"Line":1}},{"line":739,"address":[],"length":0,"stats":{"Line":0}}],"covered":102,"coverable":117},{"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 as _;\nuse crate::ThreadKey;\n\nuse super::{RwLock, RwLockWriteGuard, RwLockWriteRef};\n\n// These impls make things slightly easier because now you can use\n// `println!(\"{guard}\")` instead of `println!(\"{}\", *guard)`\n\n#[mutants::skip] // hashing involves PRNG and is difficult to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Hash + ?Sized, R: RawRwLock\u003e Hash for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.deref().hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug + ?Sized, R: RawRwLock\u003e Debug for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: Display + ?Sized, R: RawRwLock\u003e Display for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e Deref for RwLockWriteRef\u003c'_, T, R\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t// safety: this is the only type that can use `value`, and there's\n\t\t// a reference to this type, so there cannot be any mutable\n\t\t// references to this value.\n\t\tunsafe { \u0026*self.0.data.get() }\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e DerefMut for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t// safety: this is the only type that can use `value`, and we have a\n\t\t// mutable reference to this type, so there cannot be any other\n\t\t// references to this value.\n\t\tunsafe { \u0026mut *self.0.data.get() }\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e AsRef\u003cT\u003e for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e AsMut\u003cT\u003e for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e Drop for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn drop(\u0026mut self) {\n\t\t// safety: this guard is being destroyed, so the data cannot be\n\t\t// accessed without locking again\n\t\tunsafe { self.0.raw_unlock_write() }\n\t}\n}\n\nimpl\u003c'a, T: ?Sized + 'a, R: RawRwLock\u003e RwLockWriteRef\u003c'a, T, R\u003e {\n\t/// Creates a reference to the underlying data of an [`RwLock`] without\n\t/// locking or taking ownership of the key.\n\t#[must_use]\n\tpub(crate) const unsafe fn new(mutex: \u0026'a RwLock\u003cT, R\u003e) -\u003e Self {\n\t\tSelf(mutex, PhantomData)\n\t}\n}\n\n#[mutants::skip] // hashing involves PRNG and is difficult to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Hash + ?Sized, R: RawRwLock\u003e Hash for RwLockWriteGuard\u003c'_, T, R\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.deref().hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug + ?Sized, R: RawRwLock\u003e Debug for RwLockWriteGuard\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: Display + ?Sized, R: RawRwLock\u003e Display for RwLockWriteGuard\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e Deref for RwLockWriteGuard\u003c'_, T, R\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t\u0026self.rwlock\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e DerefMut for RwLockWriteGuard\u003c'_, T, R\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t\u0026mut self.rwlock\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e AsRef\u003cT\u003e for RwLockWriteGuard\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e AsMut\u003cT\u003e for RwLockWriteGuard\u003c'_, T, R\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself\n\t}\n}\n\nimpl\u003c'a, T: ?Sized + 'a, R: RawRwLock\u003e RwLockWriteGuard\u003c'a, T, R\u003e {\n\t/// Create a guard to the given mutex. Undefined if multiple guards to the\n\t/// same mutex exist at once.\n\t#[must_use]\n\tpub(super) const unsafe fn new(rwlock: \u0026'a RwLock\u003cT, R\u003e, thread_key: ThreadKey) -\u003e Self {\n\t\tSelf {\n\t\t\trwlock: RwLockWriteRef(rwlock, PhantomData),\n\t\t\tthread_key,\n\t\t}\n\t}\n}\n\nunsafe impl\u003cT: ?Sized + Sync, R: RawRwLock + Sync\u003e Sync for RwLockWriteRef\u003c'_, T, R\u003e {}\n","traces":[{"line":33,"address":[2259488],"length":1,"stats":{"Line":1}},{"line":34,"address":[],"length":0,"stats":{"Line":1}},{"line":41,"address":[],"length":0,"stats":{"Line":3}},{"line":45,"address":[614101],"length":1,"stats":{"Line":3}},{"line":50,"address":[613520],"length":1,"stats":{"Line":3}},{"line":54,"address":[],"length":0,"stats":{"Line":3}},{"line":59,"address":[],"length":0,"stats":{"Line":1}},{"line":60,"address":[],"length":0,"stats":{"Line":1}},{"line":65,"address":[],"length":0,"stats":{"Line":0}},{"line":66,"address":[],"length":0,"stats":{"Line":0}},{"line":71,"address":[],"length":0,"stats":{"Line":6}},{"line":74,"address":[624997,624981],"length":1,"stats":{"Line":6}},{"line":82,"address":[],"length":0,"stats":{"Line":4}},{"line":83,"address":[],"length":0,"stats":{"Line":0}},{"line":104,"address":[2262704],"length":1,"stats":{"Line":1}},{"line":105,"address":[],"length":0,"stats":{"Line":1}},{"line":112,"address":[613680],"length":1,"stats":{"Line":3}},{"line":113,"address":[],"length":0,"stats":{"Line":3}},{"line":118,"address":[],"length":0,"stats":{"Line":2}},{"line":119,"address":[614037],"length":1,"stats":{"Line":2}},{"line":124,"address":[],"length":0,"stats":{"Line":1}},{"line":125,"address":[],"length":0,"stats":{"Line":1}},{"line":130,"address":[],"length":0,"stats":{"Line":1}},{"line":131,"address":[],"length":0,"stats":{"Line":1}},{"line":139,"address":[],"length":0,"stats":{"Line":4}},{"line":141,"address":[],"length":0,"stats":{"Line":0}}],"covered":22,"coverable":26},{"path":["/","home","botahamec","Projects","happylock","src","rwlock.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_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`]. 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/// RAII structure that unlocks the shared read access to a [`RwLock`] when\n/// dropped.\n///\n/// This structure is created when the [`RwLock`] is put in a wrapper type,\n/// such as [`LockCollection`], and a read-only guard is obtained through the\n/// wrapper.\n///\n/// This is similar to [`RwLockReadGuard`], except it does not hold a\n/// [`ThreadKey`].\n///\n/// [`Deref`]: `std::ops::Deref`\n/// [`DerefMut`]: `std::ops::DerefMut`\n/// [`LockCollection`]: `crate::LockCollection`\npub struct RwLockReadRef\u003c'a, T: ?Sized, R: RawRwLock\u003e(\n\t\u0026'a RwLock\u003cT, R\u003e,\n\tPhantomData\u003cR::GuardMarker\u003e,\n);\n\n/// RAII structure that unlocks the exclusive write access to a [`RwLock`] when\n/// dropped.\n///\n/// This structure is created when the [`RwLock`] is put in a wrapper type,\n/// such as [`LockCollection`], and a mutable guard is obtained through the\n/// wrapper.\n///\n/// This is similar to [`RwLockWriteGuard`], except it does not hold a\n/// [`ThreadKey`].\n///\n/// [`Deref`]: `std::ops::Deref`\n/// [`DerefMut`]: `std::ops::DerefMut`\n/// [`LockCollection`]: `crate::LockCollection`\npub struct RwLockWriteRef\u003c'a, T: ?Sized, R: RawRwLock\u003e(\n\t\u0026'a RwLock\u003cT, R\u003e,\n\tPhantomData\u003cR::GuardMarker\u003e,\n);\n\n/// RAII structure used to release the shared read access of a lock when\n/// dropped.\n///\n/// This structure is created by the [`read`] and [`try_read`] methods on\n/// [`RwLock`].\n///\n/// This guard holds a [`ThreadKey`] for its entire lifetime. Therefore, a new\n/// lock cannot be acquired until this one is dropped. The [`ThreadKey`] can be\n/// reacquired using [`RwLock::unlock_read`].\n///\n/// [`Deref`]: `std::ops::Deref`\n/// [`DerefMut`]: `std::ops::DerefMut`\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/// This guard holds a [`ThreadKey`] for its entire lifetime. Therefor, a new\n/// lock cannot be acquired until this one is dropped. The [`ThreadKey`] can be\n/// reacquired using [`RwLock::unlock_write`].\n///\n/// [`Deref`]: `std::ops::Deref`\n/// [`DerefMut`]: `std::ops::DerefMut`\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::LockCollection;\n\tuse crate::RwLock;\n\tuse crate::ThreadKey;\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 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_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_scoped_write() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"Hello, world!\");\n\n\t\tlock.scoped_write(\u0026mut key, |guard| {\n\t\t\tassert!(lock.is_locked());\n\t\t\tassert_eq!(*guard, \"Hello, world!\");\n\n\t\t\tstd::thread::scope(|s| {\n\t\t\t\ts.spawn(|| {\n\t\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\t\tassert!(lock.try_read(key).is_err());\n\t\t\t\t});\n\t\t\t})\n\t\t})\n\t}\n\n\t#[test]\n\tfn get_mut_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mut lock = crate::RwLock::from(42);\n\n\t\tlet mut_ref = lock.get_mut();\n\t\t*mut_ref = 24;\n\n\t\tlock.scoped_read(key, |guard| assert_eq!(*guard, 24))\n\t}\n\n\t#[test]\n\tfn try_write_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"Hello\");\n\t\tlet guard = lock.write(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = lock.try_write(key);\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn try_read_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"Hello\");\n\t\tlet guard = lock.write(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = lock.try_read(key);\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn read_display_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet guard = lock.read(key);\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn write_display_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet guard = lock.write(key);\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn read_ref_display_works() {\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet guard = unsafe { lock.try_read_no_key().unwrap() };\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn write_ref_display_works() {\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet guard = unsafe { lock.try_write_no_key().unwrap() };\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn dropping_read_ref_releases_rwlock() {\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\n\t\tlet guard = unsafe { lock.try_read_no_key().unwrap() };\n\t\tdrop(guard);\n\n\t\tassert!(!lock.is_locked());\n\t}\n\n\t#[test]\n\tfn dropping_write_guard_releases_rwlock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\n\t\tlet guard = lock.write(key);\n\t\tdrop(guard);\n\n\t\tassert!(!lock.is_locked());\n\t}\n\n\t#[test]\n\tfn unlock_write() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"Hello, world\");\n\n\t\tlet mut guard = lock.write(key);\n\t\t*guard = \"Goodbye, world!\";\n\t\tlet key = RwLock::unlock_write(guard);\n\n\t\tlet guard = lock.read(key);\n\t\tassert_eq!(*guard, \"Goodbye, world!\");\n\t}\n\n\t#[test]\n\tfn unlock_read() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"Hello, world\");\n\n\t\tlet guard = lock.read(key);\n\t\tassert_eq!(*guard, \"Hello, world\");\n\t\tlet key = RwLock::unlock_read(guard);\n\n\t\tlet guard = lock.write(key);\n\t\tassert_eq!(*guard, \"Hello, world\");\n\t}\n\n\t#[test]\n\tfn read_ref_as_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = LockCollection::new(crate::RwLock::new(\"hi\"));\n\t\tlet guard = lock.read(key);\n\n\t\tassert_eq!(*(*guard).as_ref(), \"hi\");\n\t}\n\n\t#[test]\n\tfn read_guard_as_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"hi\");\n\t\tlet guard = lock.read(key);\n\n\t\tassert_eq!(*guard.as_ref(), \"hi\");\n\t}\n\n\t#[test]\n\tfn write_ref_as_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = LockCollection::new(crate::RwLock::new(\"hi\"));\n\t\tlet guard = lock.lock(key);\n\n\t\tassert_eq!(*(*guard).as_ref(), \"hi\");\n\t}\n\n\t#[test]\n\tfn write_guard_as_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"hi\");\n\t\tlet guard = lock.write(key);\n\n\t\tassert_eq!(*guard.as_ref(), \"hi\");\n\t}\n\n\t#[test]\n\tfn write_guard_as_mut() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"hi\");\n\t\tlet mut guard = lock.write(key);\n\n\t\tassert_eq!(*guard.as_mut(), \"hi\");\n\t\t*guard.as_mut() = \"foo\";\n\t\tassert_eq!(*guard.as_mut(), \"foo\");\n\t}\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","src","thread.rs"],"content":"use std::marker::PhantomData;\n\nmod scope;\n\n#[derive(Debug)]\npub struct Scope\u003c'scope, 'env: 'scope\u003e(PhantomData\u003c(\u0026'env (), \u0026'scope ())\u003e);\n\n#[derive(Debug)]\npub struct ScopedJoinHandle\u003c'scope, T\u003e {\n\thandle: std::thread::JoinHandle\u003cT\u003e,\n\t_phantom: PhantomData\u003c\u0026'scope ()\u003e,\n}\n\npub struct JoinHandle\u003cT\u003e {\n\thandle: std::thread::JoinHandle\u003cT\u003e,\n\tkey: crate::ThreadKey,\n}\n\npub struct ThreadBuilder(std::thread::Builder);\n","traces":[],"covered":0,"coverable":0}]};
+ var previousData = {"files":[{"path":["/","home","botahamec","Projects","happylock","examples","basic.rs"],"content":"use std::thread;\n\nuse happylock::{Mutex, ThreadKey};\n\nconst N: usize = 10;\n\nstatic DATA: Mutex\u003ci32\u003e = Mutex::new(0);\n\nfn main() {\n\tlet mut threads = Vec::new();\n\tfor _ in 0..N {\n\t\tlet th = thread::spawn(move || {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\tlet mut data = DATA.lock(key);\n\t\t\t*data += 1;\n\t\t});\n\t\tthreads.push(th);\n\t}\n\n\tfor th in threads {\n\t\t_ = th.join();\n\t}\n\n\tlet key = ThreadKey::get().unwrap();\n\tlet data = DATA.lock(key);\n\tprintln!(\"{data}\");\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","examples","dining_philosophers.rs"],"content":"use std::{thread, time::Duration};\n\nuse happylock::{collection, Mutex, ThreadKey};\n\nstatic PHILOSOPHERS: [Philosopher; 5] = [\n\tPhilosopher {\n\t\tname: \"Socrates\",\n\t\tleft: 0,\n\t\tright: 1,\n\t},\n\tPhilosopher {\n\t\tname: \"John Rawls\",\n\t\tleft: 1,\n\t\tright: 2,\n\t},\n\tPhilosopher {\n\t\tname: \"Jeremy Bentham\",\n\t\tleft: 2,\n\t\tright: 3,\n\t},\n\tPhilosopher {\n\t\tname: \"John Stuart Mill\",\n\t\tleft: 3,\n\t\tright: 4,\n\t},\n\tPhilosopher {\n\t\tname: \"Judith Butler\",\n\t\tleft: 4,\n\t\tright: 0,\n\t},\n];\n\nstatic FORKS: [Mutex\u003c()\u003e; 5] = [\n\tMutex::new(()),\n\tMutex::new(()),\n\tMutex::new(()),\n\tMutex::new(()),\n\tMutex::new(()),\n];\n\nstruct Philosopher {\n\tname: \u0026'static str,\n\tleft: usize,\n\tright: usize,\n}\n\nimpl Philosopher {\n\tfn cycle(\u0026self) {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tthread::sleep(Duration::from_secs(1));\n\n\t\t// safety: no philosopher asks for the same fork twice\n\t\tlet forks = [\u0026FORKS[self.left], \u0026FORKS[self.right]];\n\t\tlet forks = unsafe { collection::RefLockCollection::new_unchecked(\u0026forks) };\n\t\tlet forks = forks.lock(key);\n\t\tprintln!(\"{} is eating...\", self.name);\n\t\tthread::sleep(Duration::from_secs(1));\n\t\tprintln!(\"{} is done eating\", self.name);\n\t\tdrop(forks);\n\t}\n}\n\nfn main() {\n\tlet handles: Vec\u003c_\u003e = PHILOSOPHERS\n\t\t.iter()\n\t\t.map(|philosopher| thread::spawn(move || philosopher.cycle()))\n\t\t// The `collect` is absolutely necessary, because we're using lazy\n\t\t// iterators. If `collect` isn't used, then the thread won't spawn\n\t\t// until we try to join on it.\n\t\t.collect();\n\n\tfor handle in handles {\n\t\t_ = handle.join();\n\t}\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","examples","dining_philosophers_retry.rs"],"content":"use std::{thread, time::Duration};\n\nuse happylock::{collection, Mutex, ThreadKey};\n\nstatic PHILOSOPHERS: [Philosopher; 5] = [\n\tPhilosopher {\n\t\tname: \"Socrates\",\n\t\tleft: 0,\n\t\tright: 1,\n\t},\n\tPhilosopher {\n\t\tname: \"John Rawls\",\n\t\tleft: 1,\n\t\tright: 2,\n\t},\n\tPhilosopher {\n\t\tname: \"Jeremy Bentham\",\n\t\tleft: 2,\n\t\tright: 3,\n\t},\n\tPhilosopher {\n\t\tname: \"John Stuart Mill\",\n\t\tleft: 3,\n\t\tright: 4,\n\t},\n\tPhilosopher {\n\t\tname: \"Judith Butler\",\n\t\tleft: 4,\n\t\tright: 0,\n\t},\n];\n\nstatic FORKS: [Mutex\u003c()\u003e; 5] = [\n\tMutex::new(()),\n\tMutex::new(()),\n\tMutex::new(()),\n\tMutex::new(()),\n\tMutex::new(()),\n];\n\nstruct Philosopher {\n\tname: \u0026'static str,\n\tleft: usize,\n\tright: usize,\n}\n\nimpl Philosopher {\n\tfn cycle(\u0026self) {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tthread::sleep(Duration::from_secs(1));\n\n\t\t// safety: no philosopher asks for the same fork twice\n\t\tlet forks = [\u0026FORKS[self.left], \u0026FORKS[self.right]];\n\t\tlet forks = unsafe { collection::RetryingLockCollection::new_unchecked(\u0026forks) };\n\t\tlet forks = forks.lock(key);\n\t\tprintln!(\"{} is eating...\", self.name);\n\t\tthread::sleep(Duration::from_secs(1));\n\t\tprintln!(\"{} is done eating\", self.name);\n\t\tdrop(forks);\n\t}\n}\n\nfn main() {\n\tlet handles: Vec\u003c_\u003e = PHILOSOPHERS\n\t\t.iter()\n\t\t.map(|philosopher| thread::spawn(move || philosopher.cycle()))\n\t\t// The `collect` is absolutely necessary, because we're using lazy\n\t\t// iterators. If `collect` isn't used, then the thread won't spawn\n\t\t// until we try to join on it.\n\t\t.collect();\n\n\tfor handle in handles {\n\t\t_ = handle.join();\n\t}\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","examples","double_mutex.rs"],"content":"use std::thread;\n\nuse happylock::{collection::RefLockCollection, Mutex, ThreadKey};\n\nconst N: usize = 10;\n\nstatic DATA: (Mutex\u003ci32\u003e, Mutex\u003cString\u003e) = (Mutex::new(0), Mutex::new(String::new()));\n\nfn main() {\n\tlet mut threads = Vec::new();\n\tfor _ in 0..N {\n\t\tlet th = thread::spawn(move || {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\tlet lock = RefLockCollection::new(\u0026DATA);\n\t\t\tlet mut guard = lock.lock(key);\n\t\t\t*guard.1 = (100 - *guard.0).to_string();\n\t\t\t*guard.0 += 1;\n\t\t});\n\t\tthreads.push(th);\n\t}\n\n\tfor th in threads {\n\t\t_ = th.join();\n\t}\n\n\tlet key = ThreadKey::get().unwrap();\n\tlet data = RefLockCollection::new(\u0026DATA);\n\tlet data = data.lock(key);\n\tprintln!(\"{}\", data.0);\n\tprintln!(\"{}\", data.1);\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","examples","fibonacci.rs"],"content":"use happylock::{collection, LockCollection, Mutex, ThreadKey};\nuse std::thread;\n\nconst N: usize = 36;\n\nstatic DATA: [Mutex\u003ci32\u003e; 2] = [Mutex::new(0), Mutex::new(1)];\n\nfn main() {\n\tlet mut threads = Vec::new();\n\tfor _ in 0..N {\n\t\tlet th = thread::spawn(move || {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\n\t\t\t// a reference to a type that implements `OwnedLockable` will never\n\t\t\t// contain duplicates, so no duplicate checking is needed.\n\t\t\tlet collection = collection::RetryingLockCollection::new_ref(\u0026DATA);\n\t\t\tlet mut guard = collection.lock(key);\n\n\t\t\tlet x = *guard[1];\n\t\t\t*guard[1] += *guard[0];\n\t\t\t*guard[0] = x;\n\t\t});\n\t\tthreads.push(th);\n\t}\n\n\tfor thread in threads {\n\t\t_ = thread.join();\n\t}\n\n\tlet key = ThreadKey::get().unwrap();\n\tlet data = LockCollection::new_ref(\u0026DATA);\n\tlet data = data.lock(key);\n\tprintln!(\"{}\", data[0]);\n\tprintln!(\"{}\", data[1]);\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","examples","list.rs"],"content":"use std::thread;\n\nuse happylock::{collection::RefLockCollection, Mutex, ThreadKey};\n\nconst N: usize = 10;\n\nstatic DATA: [Mutex\u003cusize\u003e; 6] = [\n\tMutex::new(0),\n\tMutex::new(1),\n\tMutex::new(2),\n\tMutex::new(3),\n\tMutex::new(4),\n\tMutex::new(5),\n];\n\nstatic SEED: Mutex\u003cu32\u003e = Mutex::new(42);\n\nfn random(key: \u0026mut ThreadKey) -\u003e usize {\n\tSEED.scoped_lock(key, |seed| {\n\t\tlet x = *seed;\n\t\tlet x = x ^ (x \u003c\u003c 13);\n\t\tlet x = x ^ (x \u003e\u003e 17);\n\t\tlet x = x ^ (x \u003c\u003c 5);\n\t\t*seed = x;\n\t\tx as usize\n\t})\n}\n\nfn main() {\n\tlet mut threads = Vec::new();\n\tfor _ in 0..N {\n\t\tlet th = thread::spawn(move || {\n\t\t\tlet mut key = ThreadKey::get().unwrap();\n\t\t\tloop {\n\t\t\t\tlet mut data = Vec::new();\n\t\t\t\tfor _ in 0..3 {\n\t\t\t\t\tlet rand = random(\u0026mut key);\n\t\t\t\t\tdata.push(\u0026DATA[rand % 6]);\n\t\t\t\t}\n\n\t\t\t\tlet Some(lock) = RefLockCollection::try_new(\u0026data) else {\n\t\t\t\t\tcontinue;\n\t\t\t\t};\n\t\t\t\tlet mut guard = lock.lock(key);\n\t\t\t\t*guard[0] += *guard[1];\n\t\t\t\t*guard[1] += *guard[2];\n\t\t\t\t*guard[2] += *guard[0];\n\n\t\t\t\treturn;\n\t\t\t}\n\t\t});\n\t\tthreads.push(th);\n\t}\n\n\tfor th in threads {\n\t\t_ = th.join();\n\t}\n\n\tlet key = ThreadKey::get().unwrap();\n\tlet data = RefLockCollection::new(\u0026DATA);\n\tlet data = data.lock(key);\n\tfor val in \u0026*data {\n\t\tprintln!(\"{val}\");\n\t}\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","src","collection","boxed.rs"],"content":"use std::cell::UnsafeCell;\nuse std::fmt::Debug;\n\nuse crate::lockable::{Lockable, LockableIntoInner, OwnedLockable, RawLock, Sharable};\nuse crate::{Keyable, ThreadKey};\n\nuse super::utils::{\n\tordered_contains_duplicates, scoped_read, scoped_try_read, scoped_try_write, scoped_write,\n};\nuse super::{utils, BoxedLockCollection, LockGuard};\n\nunsafe impl\u003cL: Lockable\u003e RawLock for BoxedLockCollection\u003cL\u003e {\n\t#[mutants::skip] // this should never be called\n\t#[cfg(not(tarpaulin_include))]\n\tfn poison(\u0026self) {\n\t\tfor lock in \u0026self.locks {\n\t\t\tlock.poison();\n\t\t}\n\t}\n\n\tunsafe fn raw_write(\u0026self) {\n\t\tutils::ordered_write(self.locks())\n\t}\n\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool {\n\t\tutils::ordered_try_write(self.locks())\n\t}\n\n\tunsafe fn raw_unlock_write(\u0026self) {\n\t\tfor lock in self.locks() {\n\t\t\tlock.raw_unlock_write();\n\t\t}\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tutils::ordered_read(self.locks());\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tutils::ordered_try_read(self.locks())\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tfor lock in self.locks() {\n\t\t\tlock.raw_unlock_read();\n\t\t}\n\t}\n}\n\nunsafe impl\u003cL: Lockable\u003e Lockable for BoxedLockCollection\u003cL\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= L::Guard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= L::DataMut\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\t// Doing it this way means that if a boxed collection is put inside a\n\t\t// different collection, it will use the other method of locking. However,\n\t\t// this prevents duplicate locks in a collection.\n\t\tptrs.extend_from_slice(\u0026self.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#[expect(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.child.cast_mut());\n\n\t\t\tdrop(boxed)\n\t\t}\n\t}\n}\n\nimpl\u003cT: ?Sized, L: AsRef\u003cT\u003e\u003e AsRef\u003cT\u003e for BoxedLockCollection\u003cL\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself.child().as_ref()\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cL: Debug\u003e Debug for BoxedLockCollection\u003cL\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tf.debug_struct(stringify!(BoxedLockCollection))\n\t\t\t.field(\"data\", \u0026self.child)\n\t\t\t// there's not much reason to show the sorted locks\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 collection = LockCollection::try_new([Mutex::new(42), Mutex::new(1)]).unwrap();\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mutex = \u0026collection.into_child()[0];\n\t/// mutex.scoped_lock(key, |guard| 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(\u0026raw mut 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.child.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 collection = LockCollection::try_new([Mutex::new(42), Mutex::new(1)]).unwrap();\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let mutex1 = \u0026collection.child()[0];\n\t/// let mutex2 = \u0026collection.child()[1];\n\t/// mutex1.scoped_lock(\u0026mut key, |guard| assert_eq!(*guard, 42));\n\t/// mutex2.scoped_lock(\u0026mut key, |guard| assert_eq!(*guard, 1));\n\t/// ```\n\t#[must_use]\n\tpub fn child(\u0026self) -\u003e \u0026L {\n\t\tunsafe {\n\t\t\tself.child\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 = unsafe { std::mem::transmute(locks) };\n\t\tlet data = \u0026raw const *data;\n\t\tSelf { child: 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\t/// Acquires an exclusive lock, blocking until it is safe to do so, and then\n\t/// unlocks after the provided function returns.\n\t///\n\t/// This function is useful to ensure that the data is never accidentally\n\t/// locked forever by leaking the guard. Even if the function panics, this\n\t/// function will gracefully notice the panic, and unlock. This function\n\t/// provides no guarantees with respect to the ordering of whether contentious\n\t/// readers or writers will acquire the lock first.\n\t///\n\t/// # Panics\n\t///\n\t/// This function will panic if the provided function also panics. However,\n\t/// the collection will be safely unlocked in this case, allowing the\n\t/// collection to be locked again later.\n\t///\n\t/// # Example\n\t///\n\t/// ```\n\t/// use happylock::{LockCollection, ThreadKey, Mutex};\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// lock.scoped_lock(\u0026mut key, |(number, string)| {\n\t/// *number += 1;\n\t/// *string = \"1\";\n\t/// });\n\t/// ```\n\tpub fn scoped_lock\u003c'a, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: impl Keyable,\n\t\tf: impl FnOnce(L::DataMut\u003c'a\u003e) -\u003e R,\n\t) -\u003e R {\n\t\tscoped_write(self, key, f)\n\t}\n\n\t/// Attempts to acquire an exclusive lock to the underlying data without\n\t/// blocking, and then unlocks once the provided function returns.\n\t///\n\t/// This function implements a non-blocking variant of [`scoped_lock`].\n\t/// Unlike `scoped_lock`, if the lock collection is not already unlocked, then\n\t/// the provided function will not run, and the given [`Keyable`] is returned.\n\t/// This method 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/// If any of the locks in the collection are already locked, then the\n\t/// provided function will not run. `Err` is returned with the given key.\n\t///\n\t/// # Panics\n\t///\n\t/// This function will panic if the provided function also panics. However,\n\t/// the collection will be safely unlocked in this case, allowing the\n\t/// collection to be locked again later.\n\t///\n\t/// # Example\n\t///\n\t/// ```\n\t/// use happylock::{LockCollection, ThreadKey, Mutex};\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// lock.scoped_try_lock(\u0026mut key, |(number, string)| {\n\t/// *number += 1;\n\t/// *string = \"1\";\n\t/// }).expect(\"This lock has not yet been locked\");\n\t/// ```\n\t///\n\t/// [`scoped_lock`]: BoxedLockCollection::scoped_lock\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 FnOnce(L::DataMut\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tscoped_try_write(self, key, f)\n\t}\n\n\t/// Locks the collection, blocking the current thread until it can be\n\t/// acquired.\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data. When the guard is dropped, the locks in the collection are also\n\t/// dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// ```\n\t#[must_use]\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::Guard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_write();\n\n\t\t\tLockGuard {\n\t\t\t\t// safety: we've already acquired the lock\n\t\t\t\tguard: self.child().guard(),\n\t\t\t\tkey,\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Attempts to lock the without blocking.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// locks when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If any locks in the collection are already locked, then an error\n\t/// containing the given key is returned.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// match lock.try_lock(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::Guard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tlet guard = unsafe {\n\t\t\tif !self.raw_try_write() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we've acquired the locks\n\t\t\tself.child().guard()\n\t\t};\n\n\t\tOk(LockGuard { guard, key })\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// let key = LockCollection::\u003c(Mutex\u003ci32\u003e, Mutex\u003c\u0026str\u003e)\u003e::unlock(guard);\n\t/// ```\n\tpub fn unlock(guard: LockGuard\u003cL::Guard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: Sharable\u003e BoxedLockCollection\u003cL\u003e {\n\t/// Acquires a shared lock, blocking until it is safe to do so, and then\n\t/// unlocks after the provided function returns.\n\t///\n\t/// This function is useful to ensure that the data is never accidentally\n\t/// locked forever by leaking the guard. Even if the function panics, this\n\t/// function will gracefully notice the panic, and unlock. This function\n\t/// provides no guarantees with respect to the ordering of whether contentious\n\t/// readers or writers will acquire the lock first.\n\t///\n\t/// # Panics\n\t///\n\t/// This function will panic if the provided function also panics. However,\n\t/// the collection will be safely unlocked in this case, allowing the\n\t/// collection to be locked again later.\n\t///\n\t/// # Example\n\t///\n\t/// ```\n\t/// use happylock::{LockCollection, ThreadKey, RwLock};\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// lock.scoped_read(\u0026mut key, |(number, string)| {\n\t/// assert_eq!(*number, 0);\n\t/// assert_eq!(*string, \"\");\n\t/// });\n\t/// ```\n\tpub fn scoped_read\u003c'a, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: impl Keyable,\n\t\tf: impl FnOnce(L::DataRef\u003c'a\u003e) -\u003e R,\n\t) -\u003e R {\n\t\tscoped_read(self, key, f)\n\t}\n\n\t/// Attempts to acquire an exclusive lock to the underlying data without\n\t/// blocking, and then unlocks once the provided function returns.\n\t///\n\t/// This function implements a non-blocking variant of [`scoped_read`].\n\t/// Unlike `scoped_read`, if the lock collection is exclusively locked, then\n\t/// the provided function will not run, and the given [`Keyable`] is returned.\n\t/// This method 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/// If any of the locks in the collection are already exclusively locked, then\n\t/// the provided function will not run. `Err` is returned with the given key.\n\t///\n\t/// # Panics\n\t///\n\t/// This function will panic if the provided function also panics. However,\n\t/// the collection will be safely unlocked in this case, allowing the\n\t/// collection to be locked again later.\n\t///\n\t/// # Example\n\t///\n\t/// ```\n\t/// use happylock::{LockCollection, ThreadKey, RwLock};\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// lock.scoped_try_read(\u0026mut key, |(number, string)| {\n\t/// assert_eq!(*number, 0);\n\t/// assert_eq!(*string, \"\");\n\t/// }).expect(\"This lock has not yet been locked\");\n\t/// ```\n\t///\n\t/// [`scoped_read`]: BoxedLockCollection::scoped_read\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 FnOnce(L::DataRef\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tscoped_try_read(self, key, f)\n\t}\n\n\t/// Locks the collection, so that other threads can still read from it\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data immutably. When the guard is dropped, the locks in the collection\n\t/// are also dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// assert_eq!(*guard.0, 0);\n\t/// assert_eq!(*guard.1, \"\");\n\t/// ```\n\t#[must_use]\n\tpub fn read(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_read();\n\n\t\t\tLockGuard {\n\t\t\t\t// safety: we've already acquired the lock\n\t\t\t\tguard: self.child().read_guard(),\n\t\t\t\tkey,\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Attempts to lock the without blocking, in such a way that other threads\n\t/// can still read from the collection.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// shared access when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If any of the locks in the collection are already locked, then an error\n\t/// is returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(5), RwLock::new(\"6\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// match lock.try_read(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// assert_eq!(*guard.0, 5);\n\t/// assert_eq!(*guard.1, \"6\");\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_read(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tlet guard = unsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_read() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we've acquired the locks\n\t\t\tself.child().read_guard()\n\t\t};\n\n\t\tOk(LockGuard { guard, key })\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey, LockCollection};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = LockCollection::new(data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// let key = LockCollection::\u003c(RwLock\u003ci32\u003e, RwLock\u003c\u0026str\u003e)\u003e::unlock_read(guard);\n\t/// ```\n\tpub fn unlock_read(guard: LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: OwnedLockable\u003e BoxedLockCollection\u003cL\u003e {}\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#[expect(clippy::float_cmp)]\n\tfn uses_correct_default() {\n\t\tlet collection =\n\t\t\tBoxedLockCollection::\u003c(Mutex\u003cf64\u003e, Mutex\u003cOption\u003ci32\u003e\u003e, Mutex\u003cusize\u003e)\u003e::default();\n\t\tlet tuple = collection.into_inner();\n\t\tassert_eq!(tuple.0, 0.0);\n\t\tassert!(tuple.1.is_none());\n\t\tassert_eq!(tuple.2, 0)\n\t}\n\n\t#[test]\n\tfn non_duplicates_allowed() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(1);\n\t\tassert!(BoxedLockCollection::try_new([\u0026mutex1, \u0026mutex2]).is_some())\n\t}\n\n\t#[test]\n\tfn duplicates_not_allowed() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tassert!(BoxedLockCollection::try_new([\u0026mutex1, \u0026mutex1]).is_none())\n\t}\n\n\t#[test]\n\tfn scoped_read_sees_changes() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = BoxedLockCollection::new(mutexes);\n\t\tcollection.scoped_lock(\u0026mut key, |guard| *guard[0] = 128);\n\n\t\tlet sum = collection.scoped_read(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 128);\n\t\t\tassert_eq!(*guard[1], 42);\n\t\t\t*guard[0] + *guard[1]\n\t\t});\n\n\t\tassert_eq!(sum, 128 + 42);\n\t}\n\n\t#[test]\n\tfn scoped_try_lock_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::new([Mutex::new(1), Mutex::new(2)]);\n\t\tlet guard = collection.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = collection.scoped_try_lock(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn scoped_try_read_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::new([RwLock::new(1), RwLock::new(2)]);\n\t\tlet guard = collection.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = collection.scoped_try_read(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn try_lock_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::new([Mutex::new(1), Mutex::new(2)]);\n\t\tlet guard = collection.try_lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_lock(key);\n\t\t\t\tassert!(guard.is_err());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn try_read_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::new([RwLock::new(1), RwLock::new(2)]);\n\t\tlet guard = collection.try_read(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_read(key);\n\t\t\t\tassert!(guard.is_ok());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn try_lock_fails_with_one_exclusive_lock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = [Mutex::new(1), Mutex::new(2)];\n\t\tlet collection = BoxedLockCollection::new_ref(\u0026locks);\n\t\tlet guard = locks[1].try_lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_lock(key);\n\t\t\t\tassert!(guard.is_err());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn try_read_fails_during_exclusive_lock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = BoxedLockCollection::new([RwLock::new(1), RwLock::new(2)]);\n\t\tlet guard = collection.try_lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_read(key);\n\t\t\t\tassert!(guard.is_err());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn try_read_fails_with_one_exclusive_lock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = [RwLock::new(1), RwLock::new(2)];\n\t\tlet collection = BoxedLockCollection::new_ref(\u0026locks);\n\t\tlet guard = locks[1].try_write(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_read(key);\n\t\t\t\tassert!(guard.is_err());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn unlock_collection_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex1 = Mutex::new(\"foo\");\n\t\tlet mutex2 = Mutex::new(\"bar\");\n\t\tlet collection = BoxedLockCollection::try_new((\u0026mutex1, \u0026mutex2)).unwrap();\n\t\tlet guard = collection.lock(key);\n\t\tlet key = BoxedLockCollection::\u003c(\u0026Mutex\u003c_\u003e, \u0026Mutex\u003c_\u003e)\u003e::unlock(guard);\n\n\t\tassert!(mutex1.try_lock(key).is_ok())\n\t}\n\n\t#[test]\n\tfn read_unlock_collection_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock1 = RwLock::new(\"foo\");\n\t\tlet lock2 = RwLock::new(\"bar\");\n\t\tlet collection = BoxedLockCollection::try_new((\u0026lock1, \u0026lock2)).unwrap();\n\t\tlet guard = collection.read(key);\n\t\tlet key = BoxedLockCollection::\u003c(\u0026RwLock\u003c_\u003e, \u0026RwLock\u003c_\u003e)\u003e::unlock_read(guard);\n\n\t\tassert!(lock1.try_write(key).is_ok())\n\t}\n\n\t#[test]\n\tfn into_inner_works() {\n\t\tlet collection = BoxedLockCollection::new((Mutex::new(\"Hello\"), Mutex::new(47)));\n\t\tassert_eq!(collection.into_inner(), (\"Hello\", 47))\n\t}\n\n\t#[test]\n\tfn works_in_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex1 = RwLock::new(0);\n\t\tlet mutex2 = RwLock::new(1);\n\t\tlet collection =\n\t\t\tBoxedLockCollection::try_new(BoxedLockCollection::try_new([\u0026mutex1, \u0026mutex2]).unwrap())\n\t\t\t\t.unwrap();\n\n\t\tlet mut guard = collection.lock(key);\n\t\tassert!(mutex1.is_locked());\n\t\tassert!(mutex2.is_locked());\n\t\tassert_eq!(*guard[0], 0);\n\t\tassert_eq!(*guard[1], 1);\n\t\t*guard[0] = 2;\n\t\tlet key = BoxedLockCollection::\u003cBoxedLockCollection\u003c[\u0026RwLock\u003c_\u003e; 2]\u003e\u003e::unlock(guard);\n\n\t\tlet guard = collection.read(key);\n\t\tassert!(mutex1.is_locked());\n\t\tassert!(mutex2.is_locked());\n\t\tassert_eq!(*guard[0], 2);\n\t\tassert_eq!(*guard[1], 1);\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn as_ref_works() {\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet collection = BoxedLockCollection::new_ref(\u0026mutexes);\n\n\t\tassert!(std::ptr::addr_eq(\u0026raw const mutexes, collection.as_ref()))\n\t}\n\n\t#[test]\n\tfn child() {\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet collection = BoxedLockCollection::new_ref(\u0026mutexes);\n\n\t\tassert!(std::ptr::addr_eq(\u0026raw const mutexes, *collection.child()))\n\t}\n}\n","traces":[{"line":21,"address":[595136],"length":1,"stats":{"Line":19}},{"line":22,"address":[],"length":0,"stats":{"Line":20}},{"line":25,"address":[1608096,1606384,1606592],"length":1,"stats":{"Line":4}},{"line":26,"address":[],"length":0,"stats":{"Line":4}},{"line":29,"address":[],"length":0,"stats":{"Line":5}},{"line":30,"address":[],"length":0,"stats":{"Line":10}},{"line":31,"address":[],"length":0,"stats":{"Line":5}},{"line":35,"address":[675568],"length":1,"stats":{"Line":8}},{"line":36,"address":[1608037,1606917,1607605,1607077,1607141,1606853,1608229],"length":1,"stats":{"Line":8}},{"line":39,"address":[],"length":0,"stats":{"Line":3}},{"line":40,"address":[675285],"length":1,"stats":{"Line":3}},{"line":43,"address":[],"length":0,"stats":{"Line":3}},{"line":44,"address":[],"length":0,"stats":{"Line":6}},{"line":45,"address":[],"length":0,"stats":{"Line":3}},{"line":61,"address":[1620640],"length":1,"stats":{"Line":1}},{"line":65,"address":[],"length":0,"stats":{"Line":1}},{"line":68,"address":[],"length":0,"stats":{"Line":1}},{"line":69,"address":[1620608],"length":1,"stats":{"Line":1}},{"line":72,"address":[],"length":0,"stats":{"Line":5}},{"line":73,"address":[1620757,1620789,1620725,1620544,1620693,1620480],"length":1,"stats":{"Line":5}},{"line":88,"address":[],"length":0,"stats":{"Line":1}},{"line":89,"address":[],"length":0,"stats":{"Line":1}},{"line":92,"address":[1617824,1617696,1617856],"length":1,"stats":{"Line":3}},{"line":93,"address":[1617728,1617829,1617861],"length":1,"stats":{"Line":3}},{"line":105,"address":[1618464,1618560],"length":1,"stats":{"Line":2}},{"line":106,"address":[1618478,1618574],"length":1,"stats":{"Line":2}},{"line":117,"address":[],"length":0,"stats":{"Line":1}},{"line":118,"address":[],"length":0,"stats":{"Line":1}},{"line":129,"address":[],"length":0,"stats":{"Line":1}},{"line":130,"address":[],"length":0,"stats":{"Line":1}},{"line":137,"address":[],"length":0,"stats":{"Line":1}},{"line":138,"address":[1572593],"length":1,"stats":{"Line":1}},{"line":139,"address":[1572649],"length":1,"stats":{"Line":1}},{"line":164,"address":[1619328],"length":1,"stats":{"Line":1}},{"line":165,"address":[],"length":0,"stats":{"Line":1}},{"line":181,"address":[],"length":0,"stats":{"Line":1}},{"line":182,"address":[1621582],"length":1,"stats":{"Line":1}},{"line":187,"address":[],"length":0,"stats":{"Line":1}},{"line":188,"address":[1621661],"length":1,"stats":{"Line":1}},{"line":207,"address":[],"length":0,"stats":{"Line":3}},{"line":210,"address":[],"length":0,"stats":{"Line":3}},{"line":212,"address":[],"length":0,"stats":{"Line":3}},{"line":214,"address":[1583219,1580771,1582755],"length":1,"stats":{"Line":3}},{"line":216,"address":[],"length":0,"stats":{"Line":3}},{"line":239,"address":[682768],"length":1,"stats":{"Line":24}},{"line":241,"address":[668149],"length":1,"stats":{"Line":24}},{"line":251,"address":[593856],"length":1,"stats":{"Line":34}},{"line":252,"address":[682837],"length":1,"stats":{"Line":36}},{"line":271,"address":[],"length":0,"stats":{"Line":16}},{"line":273,"address":[],"length":0,"stats":{"Line":18}},{"line":292,"address":[],"length":0,"stats":{"Line":4}},{"line":294,"address":[],"length":0,"stats":{"Line":4}},{"line":319,"address":[683261,682848,683236],"length":1,"stats":{"Line":37}},{"line":320,"address":[597441],"length":1,"stats":{"Line":36}},{"line":321,"address":[682956],"length":1,"stats":{"Line":36}},{"line":323,"address":[1595255,1585774,1594695,1598107,1586843,1596934,1589831,1597515,1600025,1588443,1600726,1594135,1595687,1601606,1592373,1586403,1593527,1599491,1596326,1599031,1598491,1593063,1587639,1589131,1591671,1590811],"length":1,"stats":{"Line":36}},{"line":324,"address":[597606],"length":1,"stats":{"Line":36}},{"line":327,"address":[676302,676288],"length":1,"stats":{"Line":107}},{"line":330,"address":[674194],"length":1,"stats":{"Line":40}},{"line":331,"address":[674635],"length":1,"stats":{"Line":42}},{"line":353,"address":[594480,594748],"length":1,"stats":{"Line":14}},{"line":356,"address":[674353],"length":1,"stats":{"Line":14}},{"line":357,"address":[674862,674798],"length":1,"stats":{"Line":28}},{"line":358,"address":[669070],"length":1,"stats":{"Line":2}},{"line":360,"address":[598012],"length":1,"stats":{"Line":12}},{"line":393,"address":[1572032,1572160,1572224,1572128,1572096,1572064,1572192],"length":1,"stats":{"Line":7}},{"line":398,"address":[1572237,1572173,1572109,1572205,1572141,1572045,1572077],"length":1,"stats":{"Line":7}},{"line":437,"address":[1572000],"length":1,"stats":{"Line":1}},{"line":442,"address":[],"length":0,"stats":{"Line":1}},{"line":466,"address":[668802,668808,668656],"length":1,"stats":{"Line":21}},{"line":469,"address":[1591104,1598750,1587952,1594974,1590144,1597182,1587136,1594430,1592672,1600974,1586096,1591984,1593790,1595966,1596574],"length":1,"stats":{"Line":19}},{"line":473,"address":[1593830,1592712,1594470,1587992,1590184,1598790,1597222,1596614,1595014,1596006,1592024,1591144,1587176,1601014,1586136],"length":1,"stats":{"Line":15}},{"line":508,"address":[],"length":0,"stats":{"Line":5}},{"line":510,"address":[],"length":0,"stats":{"Line":7}},{"line":511,"address":[1597849,1587353,1588169],"length":1,"stats":{"Line":2}},{"line":515,"address":[],"length":0,"stats":{"Line":4}},{"line":518,"address":[],"length":0,"stats":{"Line":2}},{"line":538,"address":[1601158,1596688,1593888,1593946,1601152,1592875,1601088,1599846,1596144,1592187,1596758,1597366,1596752,1592800,1599776,1596150,1593940,1590272,1597360,1590338,1596080,1599840,1592112,1597296],"length":1,"stats":{"Line":10}},{"line":539,"address":[],"length":0,"stats":{"Line":9}},{"line":540,"address":[],"length":0,"stats":{"Line":0}},{"line":574,"address":[1572320,1572352,1572256],"length":1,"stats":{"Line":3}},{"line":579,"address":[1572269,1572333,1572365],"length":1,"stats":{"Line":3}},{"line":618,"address":[1572288],"length":1,"stats":{"Line":1}},{"line":623,"address":[],"length":0,"stats":{"Line":1}},{"line":646,"address":[675202,675208,675056],"length":1,"stats":{"Line":8}},{"line":649,"address":[675088],"length":1,"stats":{"Line":7}},{"line":653,"address":[1603382,1603142,1602840,1603798,1603000,1603254],"length":1,"stats":{"Line":6}},{"line":689,"address":[],"length":0,"stats":{"Line":4}},{"line":692,"address":[],"length":0,"stats":{"Line":5}},{"line":693,"address":[1602649,1603545],"length":1,"stats":{"Line":2}},{"line":697,"address":[],"length":0,"stats":{"Line":2}},{"line":700,"address":[674805],"length":1,"stats":{"Line":1}},{"line":718,"address":[],"length":0,"stats":{"Line":1}},{"line":719,"address":[],"length":0,"stats":{"Line":1}},{"line":720,"address":[],"length":0,"stats":{"Line":0}},{"line":738,"address":[],"length":0,"stats":{"Line":2}},{"line":739,"address":[],"length":0,"stats":{"Line":2}},{"line":765,"address":[],"length":0,"stats":{"Line":1}},{"line":766,"address":[],"length":0,"stats":{"Line":1}}],"covered":97,"coverable":99},{"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// No implementations of Eq, PartialEq, PartialOrd, or Ord\n// You can't implement both PartialEq\u003cSelf\u003e and PartialEq\u003cT\u003e\n// It's easier to just implement neither and ask users to dereference\n// This is less of a problem when using the scoped lock API\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":29,"address":[],"length":0,"stats":{"Line":1}},{"line":30,"address":[],"length":0,"stats":{"Line":1}},{"line":37,"address":[],"length":0,"stats":{"Line":19}},{"line":38,"address":[],"length":0,"stats":{"Line":0}},{"line":43,"address":[],"length":0,"stats":{"Line":8}},{"line":44,"address":[],"length":0,"stats":{"Line":0}},{"line":49,"address":[],"length":0,"stats":{"Line":2}},{"line":50,"address":[],"length":0,"stats":{"Line":0}},{"line":55,"address":[],"length":0,"stats":{"Line":4}},{"line":56,"address":[],"length":0,"stats":{"Line":0}}],"covered":6,"coverable":10},{"path":["/","home","botahamec","Projects","happylock","src","collection","owned.rs"],"content":"use crate::context::LockContext;\nuse crate::lockable::{\n\tLockable, LockableGetMut, LockableIntoInner, OwnedLockable, RawLock, Sharable,\n};\nuse crate::{Keyable, ThreadKey};\n\nuse super::utils::{scoped_read, scoped_try_read, scoped_try_write, scoped_write};\nuse super::{utils, LockGuard, OwnedLockCollection};\n\nunsafe impl\u003cL: Lockable\u003e RawLock for OwnedLockCollection\u003cL\u003e {\n\t#[mutants::skip] // this should never run\n\t#[cfg(not(tarpaulin_include))]\n\tfn poison(\u0026self) {\n\t\tlet locks = utils::get_locks_unsorted(\u0026self.child);\n\t\tfor lock in locks {\n\t\t\tlock.poison();\n\t\t}\n\t}\n\n\tunsafe fn raw_write(\u0026self) {\n\t\tutils::ordered_write(\u0026utils::get_locks_unsorted(\u0026self.child))\n\t}\n\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool {\n\t\tlet locks = utils::get_locks_unsorted(\u0026self.child);\n\t\tutils::ordered_try_write(\u0026locks)\n\t}\n\n\tunsafe fn raw_unlock_write(\u0026self) {\n\t\tlet locks = utils::get_locks_unsorted(\u0026self.child);\n\t\tfor lock in locks {\n\t\t\tlock.raw_unlock_write();\n\t\t}\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tutils::ordered_read(\u0026utils::get_locks_unsorted(\u0026self.child))\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tlet locks = utils::get_locks_unsorted(\u0026self.child);\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.child);\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 locks 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\t// It's ok to use self here, because the values in the collection already\n\t\t// cannot be referenced anywhere else. It's necessary to use self as the lock\n\t\t// because otherwise we will be handing out shared references to the child\n\t\tptrs.push(self)\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tself.child.guard()\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.child.data_mut()\n\t}\n}\n\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.child.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.child.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.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 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.child.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.child.extend(iter)\n\t}\n}\n\n// AsRef can't be implemented because an impl of AsRef\u003cL\u003e for L could break the\n// invariant that there is only one way to lock the collection. AsMut is fine,\n// because the collection can't be locked as long as the reference is valid.\n\nimpl\u003cT: ?Sized, L: AsMut\u003cT\u003e\u003e AsMut\u003cT\u003e for OwnedLockCollection\u003cL\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself.child.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 { child: data }\n\t}\n\n\t/// Acquires an exclusive lock, blocking until it is safe to do so, and then\n\t/// unlocks after the provided function returns.\n\t///\n\t/// This function is useful to ensure that the data is never accidentally\n\t/// locked forever by leaking the guard. Even if the function panics, this\n\t/// function will gracefully notice the panic, and unlock. This function\n\t/// provides no guarantees with respect to the ordering of whether contentious\n\t/// readers or writers will acquire the lock first.\n\t///\n\t/// # Panics\n\t///\n\t/// This function will panic if the provided function also panics. However,\n\t/// the collection will be safely unlocked in this case, allowing the\n\t/// collection to be locked again later.\n\t///\n\t/// # Example\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, Mutex};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// lock.scoped_lock(\u0026mut key, |(number, string)| {\n\t/// *number += 1;\n\t/// *string = \"1\";\n\t/// });\n\t/// ```\n\tpub fn scoped_lock\u003c'a, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: impl Keyable,\n\t\tf: impl FnOnce(L::DataMut\u003c'a\u003e) -\u003e R,\n\t) -\u003e R {\n\t\tscoped_write(self, key, f)\n\t}\n\n\t/// Attempts to acquire an exclusive lock to the underlying data without\n\t/// blocking, and then unlocks once the provided function returns.\n\t///\n\t/// This function implements a non-blocking variant of [`scoped_lock`].\n\t/// Unlike `scoped_lock`, if the lock collection is not already unlocked, then\n\t/// the provided function will not run, and the given [`Keyable`] is returned.\n\t/// This method 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/// If any of the locks in the collection are already locked, then the\n\t/// provided function will not run. `Err` is returned with the given key.\n\t///\n\t/// # Panics\n\t///\n\t/// This function will panic if the provided function also panics. However,\n\t/// the collection will be safely unlocked in this case, allowing the\n\t/// collection to be locked again later.\n\t///\n\t/// # Example\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, Mutex};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// lock.scoped_try_lock(\u0026mut key, |(number, string)| {\n\t/// *number += 1;\n\t/// *string = \"1\";\n\t/// }).expect(\"This lock has not yet been locked\");\n\t/// ```\n\t///\n\t/// [`scoped_lock`]: OwnedLockCollection::scoped_lock\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 FnOnce(L::DataMut\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tscoped_try_write(self, key, f)\n\t}\n\n\t/// Locks the collection\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data. When the guard is dropped, the locks in the collection are also\n\t/// dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// ```\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::Guard\u003c'_\u003e\u003e {\n\t\tlet guard = unsafe {\n\t\t\t// safety: we have the thread key, and these locks happen in a\n\t\t\t// predetermined order\n\t\t\tself.raw_write();\n\n\t\t\t// safety: we've locked all of this already\n\t\t\tself.child.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\t// safety: we've acquired the key\n\t\t\tif !self.raw_try_write() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we'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/// Creates a context that can be used to iterate over the items in order.\n\t///\n\t/// Sometimes, partial allocation of locks is useful. For example, you may\n\t/// want to acquire a lock on the first element of a list before deciding if\n\t/// the second element should be locked. This function creates a\n\t/// [`LockContext`] which is capable of doing exactly that.\n\t///\n\t/// # Example\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(true), Mutex::new(42), Mutex::new(67));\n\t/// let locks = OwnedLockCollection::new(data);\n\t/// let mut ctx = locks.context();\n\t/// let tuple = ctx.tuple(key);\n\t///\n\t/// let (use_other, tuple) = tuple.lock_0();\n\t/// let number = if **use_other {\n\t/// tuple.lock_2().0\n\t/// } else {\n\t/// tuple.lock_1().0\n\t/// };\n\t/// assert_eq!(**number, 67);\n\t/// ```\n\t#[must_use]\n\tpub const fn context(\u0026self) -\u003e LockContext\u003c'_, L\u003e {\n\t\tLockContext::new(\u0026self.child)\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\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\t/// Acquires a shared lock, blocking until it is safe to do so, and then\n\t/// unlocks after the provided function returns.\n\t///\n\t/// This function is useful to ensure that the data is never accidentally\n\t/// locked forever by leaking the guard. Even if the function panics, this\n\t/// function will gracefully notice the panic, and unlock. This function\n\t/// provides no guarantees with respect to the ordering of whether contentious\n\t/// readers or writers will acquire the lock first.\n\t///\n\t/// # Panics\n\t///\n\t/// This function will panic if the provided function also panics. However,\n\t/// the collection will be safely unlocked in this case, allowing the\n\t/// collection to be locked again later.\n\t///\n\t/// # Example\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, RwLock};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// lock.scoped_read(\u0026mut key, |(number, string)| {\n\t/// assert_eq!(*number, 0);\n\t/// assert_eq!(*string, \"\");\n\t/// });\n\t/// ```\n\tpub fn scoped_read\u003c'a, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: impl Keyable,\n\t\tf: impl FnOnce(L::DataRef\u003c'a\u003e) -\u003e R,\n\t) -\u003e R {\n\t\tscoped_read(self, key, f)\n\t}\n\n\t/// Attempts to acquire an exclusive lock to the underlying data without\n\t/// blocking, and then unlocks once the provided function returns.\n\t///\n\t/// This function implements a non-blocking variant of [`scoped_read`].\n\t/// Unlike `scoped_read`, if the lock collection is exclusively locked, then\n\t/// the provided function will not run, and the given [`Keyable`] is returned.\n\t/// This method 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/// If any of the locks in the collection are already exclusively locked, then\n\t/// the provided function will not run. `Err` is returned with the given key.\n\t///\n\t/// # Panics\n\t///\n\t/// This function will panic if the provided function also panics. However,\n\t/// the collection will be safely unlocked in this case, allowing the\n\t/// collection to be locked again later.\n\t///\n\t/// # Example\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, RwLock};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// lock.scoped_try_read(\u0026mut key, |(number, string)| {\n\t/// assert_eq!(*number, 0);\n\t/// assert_eq!(*string, \"\");\n\t/// }).expect(\"This lock has not yet been locked\");\n\t/// ```\n\t///\n\t/// [`scoped_read`]: OwnedLockCollection::scoped_read\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 FnOnce(L::DataRef\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tscoped_try_read(self, key, f)\n\t}\n\n\t/// Locks the collection, so that other threads can still read from it\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data immutably. When the guard is dropped, the locks in the collection\n\t/// are also dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// assert_eq!(*guard.0, 0);\n\t/// assert_eq!(*guard.1, \"\");\n\t/// ```\n\tpub fn read(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_read();\n\n\t\t\tLockGuard {\n\t\t\t\t// safety: we've already acquired the lock\n\t\t\t\tguard: self.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 this collection can't be acquired, then an error\n\t/// is returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(5), RwLock::new(\"6\"));\n\t/// let lock = OwnedLockCollection::new(data);\n\t///\n\t/// match lock.try_read(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// assert_eq!(*guard.0, 5);\n\t/// assert_eq!(*guard.1, \"6\");\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_read(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tlet guard = unsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_read() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we've acquired the locks\n\t\t\tself.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};\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\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.child\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.child\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::{LockCollection, Mutex, RwLock, ThreadKey};\n\n\t#[test]\n\tfn get_mut_applies_changes() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mut collection = OwnedLockCollection::new([Mutex::new(\"foo\"), Mutex::new(\"bar\")]);\n\t\tassert_eq!(*collection.get_mut()[0], \"foo\");\n\t\tassert_eq!(*collection.get_mut()[1], \"bar\");\n\t\t*collection.get_mut()[0] = \"baz\";\n\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], \"baz\");\n\t\tassert_eq!(*guard[1], \"bar\");\n\t}\n\n\t#[test]\n\tfn into_inner_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::from([Mutex::new(\"foo\")]);\n\t\tlet mut guard = collection.lock(key);\n\t\t*guard[0] = \"bar\";\n\t\tdrop(guard);\n\n\t\tlet array = collection.into_inner();\n\t\tassert_eq!(array.len(), 1);\n\t\tassert_eq!(array[0], \"bar\");\n\t}\n\n\t#[test]\n\tfn from_into_iter_is_correct() {\n\t\tlet array = [Mutex::new(0), Mutex::new(1), Mutex::new(2), Mutex::new(3)];\n\t\tlet mut collection: OwnedLockCollection\u003cVec\u003cMutex\u003cusize\u003e\u003e\u003e = array.into_iter().collect();\n\t\tassert_eq!(collection.get_mut().len(), 4);\n\t\tfor (i, lock) in collection.into_iter().enumerate() {\n\t\t\tassert_eq!(lock.into_inner(), i);\n\t\t}\n\t}\n\n\t#[test]\n\tfn from_iter_is_correct() {\n\t\tlet array = [Mutex::new(0), Mutex::new(1), Mutex::new(2), Mutex::new(3)];\n\t\tlet mut collection: OwnedLockCollection\u003cVec\u003cMutex\u003cusize\u003e\u003e\u003e = array.into_iter().collect();\n\t\tlet collection: \u0026mut Vec\u003c_\u003e = collection.as_mut();\n\t\tassert_eq!(collection.len(), 4);\n\t\tfor (i, lock) in collection.iter_mut().enumerate() {\n\t\t\tassert_eq!(*lock.get_mut(), i);\n\t\t}\n\t}\n\n\t#[test]\n\tfn scoped_read_works() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new([RwLock::new(24), RwLock::new(42)]);\n\t\tlet sum = collection.scoped_read(\u0026mut key, |guard| guard[0] + guard[1]);\n\t\tassert_eq!(sum, 24 + 42);\n\t}\n\n\t#[test]\n\tfn scoped_lock_works() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new([RwLock::new(24), RwLock::new(42)]);\n\t\tcollection.scoped_lock(\u0026mut key, |guard| *guard[0] += *guard[1]);\n\n\t\tlet sum = collection.scoped_lock(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 24 + 42);\n\t\t\tassert_eq!(*guard[1], 42);\n\t\t\t*guard[0] + *guard[1]\n\t\t});\n\n\t\tassert_eq!(sum, 24 + 42 + 42);\n\t}\n\n\t#[test]\n\tfn scoped_try_lock_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new([Mutex::new(1), Mutex::new(2)]);\n\t\tlet guard = collection.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = collection.scoped_try_lock(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn scoped_try_read_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new([RwLock::new(1), RwLock::new(2)]);\n\t\tlet guard = collection.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = collection.scoped_try_read(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn try_lock_works_on_unlocked() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new((Mutex::new(0), Mutex::new(1)));\n\t\tlet guard = collection.try_lock(key).unwrap();\n\t\tassert_eq!(*guard.0, 0);\n\t\tassert_eq!(*guard.1, 1);\n\t}\n\n\t#[test]\n\tfn try_lock_fails_on_locked() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new((Mutex::new(0), Mutex::new(1)));\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.lock(key);\n\t\t\t\tstd::mem::forget(guard);\n\t\t\t});\n\t\t});\n\n\t\tassert!(collection.try_lock(key).is_err());\n\t}\n\n\t#[test]\n\tfn try_read_succeeds_for_unlocked_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = OwnedLockCollection::new(mutexes);\n\t\tlet guard = collection.try_read(key).unwrap();\n\t\tassert_eq!(*guard[0], 24);\n\t\tassert_eq!(*guard[1], 42);\n\t}\n\n\t#[test]\n\tfn try_read_fails_on_locked() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new((RwLock::new(0), RwLock::new(1)));\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.lock(key);\n\t\t\t\tstd::mem::forget(guard);\n\t\t\t});\n\t\t});\n\n\t\tassert!(collection.try_read(key).is_err());\n\t}\n\n\t#[test]\n\tfn can_read_twice_on_different_threads() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = OwnedLockCollection::new(mutexes);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.read(key);\n\t\t\t\tassert_eq!(*guard[0], 24);\n\t\t\t\tassert_eq!(*guard[1], 42);\n\t\t\t\tstd::mem::forget(guard);\n\t\t\t});\n\t\t});\n\n\t\tlet guard = collection.try_read(key).unwrap();\n\t\tassert_eq!(*guard[0], 24);\n\t\tassert_eq!(*guard[1], 42);\n\t}\n\n\t#[test]\n\tfn unlock_collection_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new((Mutex::new(\"foo\"), Mutex::new(\"bar\")));\n\t\tlet guard = collection.lock(key);\n\n\t\tlet key = OwnedLockCollection::\u003c(Mutex\u003c_\u003e, Mutex\u003c_\u003e)\u003e::unlock(guard);\n\t\tassert!(collection.try_lock(key).is_ok())\n\t}\n\n\t#[test]\n\tfn read_unlock_collection_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new((RwLock::new(\"foo\"), RwLock::new(\"bar\")));\n\t\tlet guard = collection.read(key);\n\n\t\tlet key = OwnedLockCollection::\u003c(\u0026RwLock\u003c_\u003e, \u0026RwLock\u003c_\u003e)\u003e::unlock_read(guard);\n\t\tassert!(collection.try_lock(key).is_ok())\n\t}\n\n\t#[test]\n\tfn default_works() {\n\t\ttype MyCollection = OwnedLockCollection\u003c(Mutex\u003ci32\u003e, Mutex\u003cOption\u003ci32\u003e\u003e, Mutex\u003cString\u003e)\u003e;\n\t\tlet collection = MyCollection::default();\n\t\tlet inner = collection.into_inner();\n\t\tassert_eq!(inner.0, 0);\n\t\tassert_eq!(inner.1, None);\n\t\tassert_eq!(inner.2, String::new());\n\t}\n\n\t#[test]\n\tfn can_be_extended() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(1);\n\t\tlet mut collection = OwnedLockCollection::new(vec![mutex1, mutex2]);\n\n\t\tcollection.extend([Mutex::new(2)]);\n\n\t\tassert_eq!(collection.child.len(), 3);\n\t}\n\n\t#[test]\n\tfn works_in_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tOwnedLockCollection::new(OwnedLockCollection::new([RwLock::new(0), RwLock::new(1)]));\n\n\t\tlet mut guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], 0);\n\t\tassert_eq!(*guard[1], 1);\n\t\t*guard[1] = 2;\n\n\t\tlet key = OwnedLockCollection::\u003cOwnedLockCollection\u003c[RwLock\u003c_\u003e; 2]\u003e\u003e::unlock(guard);\n\t\tlet guard = collection.read(key);\n\t\tassert_eq!(*guard[0], 0);\n\t\tassert_eq!(*guard[1], 2);\n\t}\n\n\t#[test]\n\tfn as_mut_works() {\n\t\tlet mut mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet mut collection = OwnedLockCollection::new(\u0026mut mutexes);\n\n\t\tcollection.as_mut()[0] = Mutex::new(42);\n\n\t\tassert_eq!(*collection.as_mut()[0].get_mut(), 42);\n\t}\n\n\t#[test]\n\tfn child_mut_works() {\n\t\tlet mut mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet mut collection = OwnedLockCollection::new(\u0026mut mutexes);\n\n\t\tcollection.child_mut()[0] = Mutex::new(42);\n\n\t\tassert_eq!(*collection.child_mut()[0].get_mut(), 42);\n\t}\n\n\t#[test]\n\tfn into_child_works() {\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet mut collection = OwnedLockCollection::new(mutexes);\n\n\t\tcollection.child_mut()[0] = Mutex::new(42);\n\n\t\tassert_eq!(\n\t\t\t*collection\n\t\t\t\t.into_child()\n\t\t\t\t.as_mut()\n\t\t\t\t.get_mut(0)\n\t\t\t\t.unwrap()\n\t\t\t\t.get_mut(),\n\t\t\t42\n\t\t);\n\t}\n\n\t#[test]\n\tfn duplicates_detected() {\n\t\tlet collection1 = OwnedLockCollection::new([Mutex::new(5), Mutex::new(10)]);\n\t\tlet collection2 = LockCollection::try_new((\u0026collection1, \u0026collection1));\n\n\t\tassert!(collection2.is_none());\n\t}\n}\n","traces":[{"line":20,"address":[1612385,1611451,1608432,1608411,1612113,1611600,1608417,1608545,1611344,1612107,1608304,1611713,1612379,1609888,1611707,1612816,1612929,1612000,1612272,1609995,1611457,1612923,1610001,1608539],"length":1,"stats":{"Line":8}},{"line":21,"address":[],"length":0,"stats":{"Line":16}},{"line":24,"address":[],"length":0,"stats":{"Line":4}},{"line":25,"address":[],"length":0,"stats":{"Line":5}},{"line":26,"address":[],"length":0,"stats":{"Line":9}},{"line":29,"address":[],"length":0,"stats":{"Line":1}},{"line":30,"address":[],"length":0,"stats":{"Line":1}},{"line":31,"address":[1610633,1609177,1610797,1609341],"length":1,"stats":{"Line":2}},{"line":32,"address":[],"length":0,"stats":{"Line":2}},{"line":36,"address":[],"length":0,"stats":{"Line":4}},{"line":37,"address":[],"length":0,"stats":{"Line":8}},{"line":40,"address":[],"length":0,"stats":{"Line":2}},{"line":41,"address":[],"length":0,"stats":{"Line":2}},{"line":42,"address":[1610103,1608592,1608647,1610048,1612704,1612759],"length":1,"stats":{"Line":4}},{"line":45,"address":[1609138,1610304,1610588,1609132,1608848,1610594],"length":1,"stats":{"Line":1}},{"line":46,"address":[],"length":0,"stats":{"Line":1}},{"line":47,"address":[],"length":0,"stats":{"Line":2}},{"line":48,"address":[],"length":0,"stats":{"Line":2}},{"line":73,"address":[1621168],"length":1,"stats":{"Line":1}},{"line":74,"address":[1621185],"length":1,"stats":{"Line":1}},{"line":77,"address":[],"length":0,"stats":{"Line":1}},{"line":78,"address":[],"length":0,"stats":{"Line":1}},{"line":88,"address":[],"length":0,"stats":{"Line":2}},{"line":89,"address":[],"length":0,"stats":{"Line":2}},{"line":96,"address":[1618320,1618384],"length":1,"stats":{"Line":2}},{"line":97,"address":[1618332,1618397],"length":1,"stats":{"Line":2}},{"line":112,"address":[],"length":0,"stats":{"Line":1}},{"line":113,"address":[],"length":0,"stats":{"Line":1}},{"line":116,"address":[],"length":0,"stats":{"Line":1}},{"line":117,"address":[],"length":0,"stats":{"Line":1}},{"line":130,"address":[1619056],"length":1,"stats":{"Line":1}},{"line":131,"address":[1619068],"length":1,"stats":{"Line":1}},{"line":138,"address":[],"length":0,"stats":{"Line":2}},{"line":139,"address":[],"length":0,"stats":{"Line":2}},{"line":140,"address":[1572793],"length":1,"stats":{"Line":2}},{"line":145,"address":[],"length":0,"stats":{"Line":1}},{"line":146,"address":[],"length":0,"stats":{"Line":1}},{"line":155,"address":[],"length":0,"stats":{"Line":2}},{"line":156,"address":[],"length":0,"stats":{"Line":2}},{"line":161,"address":[],"length":0,"stats":{"Line":1}},{"line":162,"address":[],"length":0,"stats":{"Line":1}},{"line":167,"address":[],"length":0,"stats":{"Line":1}},{"line":168,"address":[],"length":0,"stats":{"Line":1}},{"line":189,"address":[1579328,1579376,1577408,1578272,1578320,1579552,1578752,1578304,1577232,1578240,1578192,1577632,1577904,1579104,1579136,1577856,1578368,1577584,1578800,1577072,1577024],"length":1,"stats":{"Line":22}},{"line":223,"address":[1571872,1571904],"length":1,"stats":{"Line":2}},{"line":228,"address":[],"length":0,"stats":{"Line":2}},{"line":268,"address":[1571840],"length":1,"stats":{"Line":1}},{"line":273,"address":[],"length":0,"stats":{"Line":1}},{"line":296,"address":[],"length":0,"stats":{"Line":8}},{"line":300,"address":[1578846,1577472,1577296,1577712,1577118,1579438,1577984,1578414],"length":1,"stats":{"Line":8}},{"line":303,"address":[1578454,1579478,1577517,1577341,1578886,1578029,1577158,1577757],"length":1,"stats":{"Line":8}},{"line":339,"address":[1579093,1579317,1579087,1578944,1578735,1578592,1579311,1579168,1578741],"length":1,"stats":{"Line":3}},{"line":342,"address":[],"length":0,"stats":{"Line":7}},{"line":343,"address":[],"length":0,"stats":{"Line":1}},{"line":347,"address":[],"length":0,"stats":{"Line":6}},{"line":350,"address":[1578723,1579299,1579075],"length":1,"stats":{"Line":3}},{"line":381,"address":[1578352,1579536,1579584,1577888,1577056,1577840,1577616,1579360,1578784,1577008],"length":1,"stats":{"Line":11}},{"line":382,"address":[1579589,1579365,1577013,1577893,1579541,1578357,1578789,1577061,1577845,1577621],"length":1,"stats":{"Line":11}},{"line":403,"address":[],"length":0,"stats":{"Line":2}},{"line":404,"address":[1578526,1578105],"length":1,"stats":{"Line":2}},{"line":405,"address":[],"length":0,"stats":{"Line":0}},{"line":440,"address":[1571936],"length":1,"stats":{"Line":1}},{"line":445,"address":[1571949],"length":1,"stats":{"Line":1}},{"line":485,"address":[1571968],"length":1,"stats":{"Line":1}},{"line":490,"address":[],"length":0,"stats":{"Line":1}},{"line":513,"address":[],"length":0,"stats":{"Line":4}},{"line":516,"address":[],"length":0,"stats":{"Line":4}},{"line":520,"address":[],"length":0,"stats":{"Line":4}},{"line":557,"address":[],"length":0,"stats":{"Line":2}},{"line":560,"address":[],"length":0,"stats":{"Line":4}},{"line":561,"address":[],"length":0,"stats":{"Line":1}},{"line":565,"address":[1580409,1580367,1579849],"length":1,"stats":{"Line":1}},{"line":568,"address":[],"length":0,"stats":{"Line":1}},{"line":587,"address":[1580448,1580512,1580518],"length":1,"stats":{"Line":1}},{"line":588,"address":[],"length":0,"stats":{"Line":1}},{"line":589,"address":[],"length":0,"stats":{"Line":0}},{"line":611,"address":[],"length":0,"stats":{"Line":1}},{"line":612,"address":[],"length":0,"stats":{"Line":1}},{"line":632,"address":[],"length":0,"stats":{"Line":2}},{"line":633,"address":[],"length":0,"stats":{"Line":0}},{"line":653,"address":[],"length":0,"stats":{"Line":2}},{"line":654,"address":[],"length":0,"stats":{"Line":2}},{"line":671,"address":[],"length":0,"stats":{"Line":2}},{"line":672,"address":[],"length":0,"stats":{"Line":2}}],"covered":81,"coverable":84},{"path":["/","home","botahamec","Projects","happylock","src","collection","ref.rs"],"content":"use std::fmt::Debug;\n\nuse crate::lockable::{Lockable, OwnedLockable, RawLock, Sharable};\nuse crate::{Keyable, ThreadKey};\n\nuse super::utils::{\n\tget_locks, ordered_contains_duplicates, scoped_read, scoped_try_read, scoped_try_write,\n\tscoped_write,\n};\nuse super::{utils, LockGuard, RefLockCollection};\n\nimpl\u003c'a, L\u003e IntoIterator for \u0026'a RefLockCollection\u003c'a, L\u003e\nwhere\n\t\u0026'a L: IntoIterator,\n{\n\ttype Item = \u003c\u0026'a L as IntoIterator\u003e::Item;\n\ttype IntoIter = \u003c\u0026'a L as IntoIterator\u003e::IntoIter;\n\n\tfn into_iter(self) -\u003e Self::IntoIter {\n\t\tself.child.into_iter()\n\t}\n}\n\nunsafe impl\u003cL: Lockable\u003e RawLock for RefLockCollection\u003c'_, L\u003e {\n\t#[mutants::skip] // this should never run\n\t#[cfg(not(tarpaulin_include))]\n\tfn poison(\u0026self) {\n\t\tfor lock in \u0026self.locks {\n\t\t\tlock.poison();\n\t\t}\n\t}\n\n\tunsafe fn raw_write(\u0026self) {\n\t\tutils::ordered_write(\u0026self.locks)\n\t}\n\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool {\n\t\tutils::ordered_try_write(\u0026self.locks)\n\t}\n\n\tunsafe fn raw_unlock_write(\u0026self) {\n\t\tfor lock in \u0026self.locks {\n\t\t\tlock.raw_unlock_write();\n\t\t}\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tutils::ordered_read(\u0026self.locks)\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tutils::ordered_try_read(\u0026self.locks)\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tfor lock in \u0026self.locks {\n\t\t\tlock.raw_unlock_read();\n\t\t}\n\t}\n}\n\nunsafe impl\u003cL: Lockable\u003e Lockable for RefLockCollection\u003c'_, L\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= L::Guard\u003c'g\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= L::DataMut\u003c'a\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\t// Just like with BoxedLockCollection, we need to return all the individual\n\t\t// locks to avoid duplicates\n\t\tptrs.extend_from_slice(\u0026self.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 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.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\nimpl\u003cT: ?Sized, L: AsRef\u003cT\u003e\u003e AsRef\u003cT\u003e for RefLockCollection\u003c'_, L\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself.child.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.child)\n\t\t\t// there's not much reason to show the sorting order\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#[expect(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\tchild: data,\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.child\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\tchild: data,\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 { child: data, locks })\n\t}\n\n\t/// Acquires an exclusive lock, blocking until it is safe to do so, and then\n\t/// unlocks after the provided function returns.\n\t///\n\t/// This function is useful to ensure that the data is never accidentally\n\t/// locked forever by leaking the guard. Even if the function panics, this\n\t/// function will gracefully notice the panic, and unlock. This function\n\t/// provides no guarantees with respect to the ordering of whether contentious\n\t/// readers or writers will acquire the lock first.\n\t///\n\t/// # Panics\n\t///\n\t/// This function will panic if the provided function also panics. However,\n\t/// the collection will be safely unlocked in this case, allowing the\n\t/// collection to be locked again later.\n\t///\n\t/// # Example\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, Mutex};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// lock.scoped_lock(\u0026mut key, |(number, string)| {\n\t/// *number += 1;\n\t/// *string = \"1\";\n\t/// });\n\t/// ```\n\tpub fn scoped_lock\u003c's, R\u003e(\n\t\t\u0026's self,\n\t\tkey: impl Keyable,\n\t\tf: impl FnOnce(L::DataMut\u003c's\u003e) -\u003e R,\n\t) -\u003e R {\n\t\tscoped_write(self, key, f)\n\t}\n\n\t/// Attempts to acquire an exclusive lock to the underlying data without\n\t/// blocking, and then unlocks once the provided function returns.\n\t///\n\t/// This function implements a non-blocking variant of [`scoped_lock`].\n\t/// Unlike `scoped_lock`, if the lock collection is not already unlocked, then\n\t/// the provided function will not run, and the given [`Keyable`] is returned.\n\t/// This method 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/// If any of the locks in the collection are already locked, then the\n\t/// provided function will not run. `Err` is returned with the given key.\n\t///\n\t/// # Panics\n\t///\n\t/// This function will panic if the provided function also panics. However,\n\t/// the collection will be safely unlocked in this case, allowing the\n\t/// collection to be locked again later.\n\t///\n\t/// # Example\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, Mutex};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// lock.scoped_try_lock(\u0026mut key, |(number, string)| {\n\t/// *number += 1;\n\t/// *string = \"1\";\n\t/// }).expect(\"This lock has not yet been locked\");\n\t/// ```\n\t///\n\t/// [`scoped_lock`]: RefLockCollection::scoped_lock\n\tpub fn scoped_try_lock\u003c's, Key: Keyable, R\u003e(\n\t\t\u0026's self,\n\t\tkey: Key,\n\t\tf: impl FnOnce(L::DataMut\u003c's\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tscoped_try_write(self, key, f)\n\t}\n\n\t/// Locks the collection\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data. When the guard is dropped, the locks in the collection are also\n\t/// dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// ```\n\t#[must_use]\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::Guard\u003c'_\u003e\u003e {\n\t\tlet guard = unsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_write();\n\n\t\t\t// safety: we've locked all of this already\n\t\t\tself.child.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\t// safety: we have the thread key\n\t\t\tif !self.raw_try_write() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we'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};\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\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\t/// Acquires a shared lock, blocking until it is safe to do so, and then\n\t/// unlocks after the provided function returns.\n\t///\n\t/// This function is useful to ensure that the data is never accidentally\n\t/// locked forever by leaking the guard. Even if the function panics, this\n\t/// function will gracefully notice the panic, and unlock. This function\n\t/// provides no guarantees with respect to the ordering of whether contentious\n\t/// readers or writers will acquire the lock first.\n\t///\n\t/// # Panics\n\t///\n\t/// This function will panic if the provided function also panics. However,\n\t/// the collection will be safely unlocked in this case, allowing the\n\t/// collection to be locked again later.\n\t///\n\t/// # Example\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, RwLock};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// lock.scoped_read(\u0026mut key, |(number, string)| {\n\t/// assert_eq!(*number, 0);\n\t/// assert_eq!(*string, \"\");\n\t/// });\n\t/// ```\n\tpub fn scoped_read\u003c'a, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: impl Keyable,\n\t\tf: impl FnOnce(L::DataRef\u003c'a\u003e) -\u003e R,\n\t) -\u003e R {\n\t\tscoped_read(self, key, f)\n\t}\n\n\t/// Attempts to acquire an exclusive lock to the underlying data without\n\t/// blocking, and then unlocks once the provided function returns.\n\t///\n\t/// This function implements a non-blocking variant of [`scoped_read`].\n\t/// Unlike `scoped_read`, if the lock collection is exclusively locked, then\n\t/// the provided function will not run, and the given [`Keyable`] is returned.\n\t/// This method 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/// If any of the locks in the collection are already exclusively locked, then\n\t/// the provided function will not run. `Err` is returned with the given key.\n\t///\n\t/// # Panics\n\t///\n\t/// This function will panic if the provided function also panics. However,\n\t/// the collection will be safely unlocked in this case, allowing the\n\t/// collection to be locked again later.\n\t///\n\t/// # Example\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, RwLock};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// lock.scoped_try_read(\u0026mut key, |(number, string)| {\n\t/// assert_eq!(*number, 0);\n\t/// assert_eq!(*string, \"\");\n\t/// }).expect(\"This lock has not yet been locked\");\n\t/// ```\n\t///\n\t/// [`scoped_read`]: RefLockCollection::scoped_read\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 FnOnce(L::DataRef\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tscoped_try_read(self, key, f)\n\t}\n\n\t/// Locks the collection, so that other threads can still read from it\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data immutably. When the guard is dropped, the locks in the collection\n\t/// are also dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// assert_eq!(*guard.0, 0);\n\t/// assert_eq!(*guard.1, \"\");\n\t/// ```\n\t#[must_use]\n\tpub fn read(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_read();\n\n\t\t\tLockGuard {\n\t\t\t\t// safety: we've already acquired the lock\n\t\t\t\tguard: self.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};\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.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};\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\tpub fn unlock_read(guard: LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003c'a, L: 'a\u003e RefLockCollection\u003c'a, L\u003e\nwhere\n\t\u0026'a L: IntoIterator,\n{\n\t/// Returns an iterator over references to each value in the collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RefLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = [Mutex::new(26), Mutex::new(1)];\n\t/// let lock = RefLockCollection::new(\u0026data);\n\t///\n\t/// let mut iter = lock.iter();\n\t/// let mutex = iter.next().unwrap();\n\t/// let guard = mutex.lock(key);\n\t///\n\t/// assert_eq!(*guard, 26);\n\t/// ```\n\t#[must_use]\n\tpub fn iter(\u0026'a self) -\u003e \u003c\u0026'a L as IntoIterator\u003e::IntoIter {\n\t\tself.into_iter()\n\t}\n}\n\n#[cfg(test)]\nmod tests {\n\tuse super::*;\n\tuse crate::{Mutex, RwLock, ThreadKey};\n\n\t#[test]\n\tfn non_duplicates_allowed() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(1);\n\t\tassert!(RefLockCollection::try_new(\u0026[\u0026mutex1, \u0026mutex2]).is_some())\n\t}\n\n\t#[test]\n\tfn duplicates_not_allowed() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tassert!(RefLockCollection::try_new(\u0026[\u0026mutex1, \u0026mutex1]).is_none())\n\t}\n\n\t#[test]\n\tfn from() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [Mutex::new(\"foo\"), Mutex::new(\"bar\"), Mutex::new(\"baz\")];\n\t\tlet collection = RefLockCollection::from(\u0026mutexes);\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], \"foo\");\n\t\tassert_eq!(*guard[1], \"bar\");\n\t\tassert_eq!(*guard[2], \"baz\");\n\t}\n\n\t#[test]\n\tfn scoped_lock_changes_collection() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [Mutex::new(24), Mutex::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\t\tlet sum = collection.scoped_lock(\u0026mut key, |guard| {\n\t\t\t*guard[0] = 128;\n\t\t\t*guard[0] + *guard[1]\n\t\t});\n\n\t\tassert_eq!(sum, 128 + 42);\n\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], 128);\n\t\tassert_eq!(*guard[1], 42);\n\t}\n\n\t#[test]\n\tfn scoped_read_sees_changes() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\t\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\t\t*guard[0] = 128;\n\t\t});\n\n\t\tlet sum = collection.scoped_read(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 128);\n\t\t\tassert_eq!(*guard[1], 42);\n\t\t\t*guard[0] + *guard[1]\n\t\t});\n\n\t\tassert_eq!(sum, 128 + 42);\n\t}\n\n\t#[test]\n\tfn scoped_try_lock_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = [Mutex::new(1), Mutex::new(2)];\n\t\tlet collection = RefLockCollection::new(\u0026locks);\n\t\tlet guard = collection.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = collection.scoped_try_lock(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn scoped_try_read_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = [RwLock::new(1), RwLock::new(2)];\n\t\tlet collection = RefLockCollection::new(\u0026locks);\n\t\tlet guard = collection.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = collection.scoped_try_read(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn try_lock_succeeds_for_unlocked_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [Mutex::new(24), Mutex::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\t\tlet guard = collection.try_lock(key).unwrap();\n\t\tassert_eq!(*guard[0], 24);\n\t\tassert_eq!(*guard[1], 42);\n\t}\n\n\t#[test]\n\tfn try_lock_fails_for_locked_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [Mutex::new(24), Mutex::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = mutexes[1].lock(key);\n\t\t\t\tassert_eq!(*guard, 42);\n\t\t\t\tstd::mem::forget(guard);\n\t\t\t});\n\t\t});\n\n\t\tlet guard = collection.try_lock(key);\n\t\tassert!(guard.is_err());\n\t}\n\n\t#[test]\n\tfn try_read_succeeds_for_unlocked_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\t\tlet guard = collection.try_read(key).unwrap();\n\t\tassert_eq!(*guard[0], 24);\n\t\tassert_eq!(*guard[1], 42);\n\t}\n\n\t#[test]\n\tfn try_read_fails_for_locked_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = mutexes[1].write(key);\n\t\t\t\tassert_eq!(*guard, 42);\n\t\t\t\tstd::mem::forget(guard);\n\t\t\t});\n\t\t});\n\n\t\tlet guard = collection.try_read(key);\n\t\tassert!(guard.is_err());\n\t}\n\n\t#[test]\n\tfn can_read_twice_on_different_threads() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.read(key);\n\t\t\t\tassert_eq!(*guard[0], 24);\n\t\t\t\tassert_eq!(*guard[1], 42);\n\t\t\t\tstd::mem::forget(guard);\n\t\t\t});\n\t\t});\n\n\t\tlet guard = collection.try_read(key).unwrap();\n\t\tassert_eq!(*guard[0], 24);\n\t\tassert_eq!(*guard[1], 42);\n\t}\n\n\t#[test]\n\tfn into_ref_iterator() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1), Mutex::new(2)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\t\tfor (i, mutex) in (\u0026collection).into_iter().enumerate() {\n\t\t\tmutex.scoped_lock(\u0026mut key, |val| assert_eq!(*val, i))\n\t\t}\n\t}\n\n\t#[test]\n\tfn ref_iterator() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1), Mutex::new(2)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\t\tfor (i, mutex) in collection.iter().enumerate() {\n\t\t\tmutex.scoped_lock(\u0026mut key, |val| assert_eq!(*val, i))\n\t\t}\n\t}\n\n\t#[test]\n\tfn works_in_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex1 = RwLock::new(0);\n\t\tlet mutex2 = RwLock::new(1);\n\t\tlet collection0 = [\u0026mutex1, \u0026mutex2];\n\t\tlet collection1 = RefLockCollection::try_new(\u0026collection0).unwrap();\n\t\tlet collection = RefLockCollection::try_new(\u0026collection1).unwrap();\n\n\t\tlet mut guard = collection.lock(key);\n\t\tassert!(mutex1.is_locked());\n\t\tassert!(mutex2.is_locked());\n\t\tassert_eq!(*guard[0], 0);\n\t\tassert_eq!(*guard[1], 1);\n\t\t*guard[1] = 2;\n\t\tdrop(guard);\n\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet guard = collection.read(key);\n\t\tassert!(mutex1.is_locked());\n\t\tassert!(mutex2.is_locked());\n\t\tassert_eq!(*guard[0], 0);\n\t\tassert_eq!(*guard[1], 2);\n\t}\n\n\t#[test]\n\tfn unlock_collection_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = (Mutex::new(\"foo\"), Mutex::new(\"bar\"));\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\t\tlet guard = collection.lock(key);\n\n\t\tlet key = RefLockCollection::\u003c(Mutex\u003c_\u003e, Mutex\u003c_\u003e)\u003e::unlock(guard);\n\t\tassert!(collection.try_lock(key).is_ok())\n\t}\n\n\t#[test]\n\tfn read_unlock_collection_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = (RwLock::new(\"foo\"), RwLock::new(\"bar\"));\n\t\tlet collection = RefLockCollection::new(\u0026locks);\n\t\tlet guard = collection.read(key);\n\n\t\tlet key = RefLockCollection::\u003c(\u0026RwLock\u003c_\u003e, \u0026RwLock\u003c_\u003e)\u003e::unlock_read(guard);\n\t\tassert!(collection.try_lock(key).is_ok())\n\t}\n\n\t#[test]\n\tfn as_ref_works() {\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\n\t\tassert!(std::ptr::addr_eq(\u0026raw const mutexes, collection.as_ref()))\n\t}\n\n\t#[test]\n\tfn child() {\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet collection = RefLockCollection::new(\u0026mutexes);\n\n\t\tassert!(std::ptr::addr_eq(\u0026raw const mutexes, collection.child()))\n\t}\n}\n","traces":[{"line":19,"address":[1606336],"length":1,"stats":{"Line":1}},{"line":20,"address":[],"length":0,"stats":{"Line":1}},{"line":33,"address":[],"length":0,"stats":{"Line":5}},{"line":34,"address":[],"length":0,"stats":{"Line":5}},{"line":37,"address":[],"length":0,"stats":{"Line":3}},{"line":38,"address":[],"length":0,"stats":{"Line":3}},{"line":41,"address":[1619840,1620096],"length":1,"stats":{"Line":2}},{"line":42,"address":[],"length":0,"stats":{"Line":4}},{"line":43,"address":[],"length":0,"stats":{"Line":2}},{"line":47,"address":[],"length":0,"stats":{"Line":3}},{"line":48,"address":[1620421,1620261,1620197],"length":1,"stats":{"Line":3}},{"line":51,"address":[],"length":0,"stats":{"Line":1}},{"line":52,"address":[],"length":0,"stats":{"Line":1}},{"line":55,"address":[],"length":0,"stats":{"Line":1}},{"line":56,"address":[],"length":0,"stats":{"Line":2}},{"line":57,"address":[],"length":0,"stats":{"Line":1}},{"line":73,"address":[],"length":0,"stats":{"Line":1}},{"line":76,"address":[],"length":0,"stats":{"Line":1}},{"line":79,"address":[],"length":0,"stats":{"Line":1}},{"line":80,"address":[],"length":0,"stats":{"Line":1}},{"line":83,"address":[],"length":0,"stats":{"Line":2}},{"line":84,"address":[],"length":0,"stats":{"Line":2}},{"line":99,"address":[],"length":0,"stats":{"Line":1}},{"line":100,"address":[],"length":0,"stats":{"Line":1}},{"line":103,"address":[],"length":0,"stats":{"Line":1}},{"line":104,"address":[],"length":0,"stats":{"Line":1}},{"line":109,"address":[],"length":0,"stats":{"Line":1}},{"line":110,"address":[],"length":0,"stats":{"Line":1}},{"line":131,"address":[],"length":0,"stats":{"Line":1}},{"line":132,"address":[],"length":0,"stats":{"Line":1}},{"line":152,"address":[],"length":0,"stats":{"Line":6}},{"line":154,"address":[],"length":0,"stats":{"Line":6}},{"line":181,"address":[],"length":0,"stats":{"Line":1}},{"line":182,"address":[],"length":0,"stats":{"Line":1}},{"line":208,"address":[],"length":0,"stats":{"Line":0}},{"line":211,"address":[],"length":0,"stats":{"Line":0}},{"line":234,"address":[],"length":0,"stats":{"Line":3}},{"line":235,"address":[],"length":0,"stats":{"Line":3}},{"line":236,"address":[],"length":0,"stats":{"Line":6}},{"line":237,"address":[],"length":0,"stats":{"Line":1}},{"line":240,"address":[],"length":0,"stats":{"Line":3}},{"line":273,"address":[1571744,1571680],"length":1,"stats":{"Line":2}},{"line":278,"address":[1571757,1571693],"length":1,"stats":{"Line":2}},{"line":318,"address":[1571712],"length":1,"stats":{"Line":1}},{"line":323,"address":[],"length":0,"stats":{"Line":1}},{"line":347,"address":[],"length":0,"stats":{"Line":5}},{"line":350,"address":[],"length":0,"stats":{"Line":5}},{"line":353,"address":[],"length":0,"stats":{"Line":5}},{"line":389,"address":[1574480,1576275,1574474,1576281,1574304,1576099,1575952,1576105,1576128],"length":1,"stats":{"Line":3}},{"line":392,"address":[],"length":0,"stats":{"Line":6}},{"line":393,"address":[1574390,1576015,1576191],"length":1,"stats":{"Line":1}},{"line":397,"address":[],"length":0,"stats":{"Line":5}},{"line":400,"address":[1574435,1576087,1576263],"length":1,"stats":{"Line":3}},{"line":421,"address":[],"length":0,"stats":{"Line":1}},{"line":422,"address":[],"length":0,"stats":{"Line":1}},{"line":423,"address":[],"length":0,"stats":{"Line":0}},{"line":458,"address":[1571776],"length":1,"stats":{"Line":1}},{"line":463,"address":[],"length":0,"stats":{"Line":1}},{"line":503,"address":[1571808],"length":1,"stats":{"Line":1}},{"line":508,"address":[],"length":0,"stats":{"Line":1}},{"line":532,"address":[],"length":0,"stats":{"Line":3}},{"line":535,"address":[],"length":0,"stats":{"Line":3}},{"line":539,"address":[],"length":0,"stats":{"Line":3}},{"line":576,"address":[],"length":0,"stats":{"Line":1}},{"line":579,"address":[1576480,1576523],"length":1,"stats":{"Line":2}},{"line":580,"address":[],"length":0,"stats":{"Line":1}},{"line":584,"address":[],"length":0,"stats":{"Line":1}},{"line":587,"address":[],"length":0,"stats":{"Line":1}},{"line":606,"address":[],"length":0,"stats":{"Line":1}},{"line":607,"address":[],"length":0,"stats":{"Line":1}},{"line":608,"address":[],"length":0,"stats":{"Line":0}},{"line":635,"address":[],"length":0,"stats":{"Line":1}},{"line":636,"address":[],"length":0,"stats":{"Line":1}}],"covered":69,"coverable":73},{"path":["/","home","botahamec","Projects","happylock","src","collection","retry.rs"],"content":"use std::cell::Cell;\nuse std::collections::HashSet;\n\nuse crate::collection::utils;\nuse crate::handle_unwind::handle_unwind;\nuse crate::lockable::{\n\tLockable, LockableGetMut, LockableIntoInner, OwnedLockable, RawLock, Sharable,\n};\nuse crate::{Keyable, ThreadKey};\n\nuse super::utils::{\n\tattempt_to_recover_reads_from_panic, attempt_to_recover_writes_from_panic, get_locks_unsorted,\n\tscoped_read, scoped_try_read, scoped_try_write, scoped_write,\n};\nuse super::{LockGuard, RetryingLockCollection};\n\n/// Checks that a collection contains no duplicate references to a lock.\nfn contains_duplicates\u003cL: Lockable\u003e(data: L) -\u003e bool {\n\tlet mut locks = Vec::new();\n\tdata.get_ptrs(\u0026mut locks);\n\t// cast to *const () so that the v-table pointers are not used for hashing\n\tlet locks = locks.into_iter().map(|l| (\u0026raw const *l).cast::\u003c()\u003e());\n\n\tlet mut locks_set = HashSet::with_capacity(locks.len());\n\tfor lock in locks {\n\t\tif !locks_set.insert(lock) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tfalse\n}\n\nunsafe impl\u003cL: Lockable\u003e RawLock for RetryingLockCollection\u003cL\u003e {\n\t#[mutants::skip] // this should never run\n\t#[cfg(not(tarpaulin_include))]\n\tfn poison(\u0026self) {\n\t\tlet locks = get_locks_unsorted(\u0026self.child);\n\t\tfor lock in locks {\n\t\t\tlock.poison();\n\t\t}\n\t}\n\n\tunsafe fn raw_write(\u0026self) {\n\t\tlet locks = get_locks_unsorted(\u0026self.child);\n\n\t\tif locks.is_empty() {\n\t\t\t// this probably prevents a panic later\n\t\t\treturn;\n\t\t}\n\n\t\t// these will be unlocked in case of a panic\n\t\tlet first_index = Cell::new(0);\n\t\tlet locked = Cell::new(0);\n\t\thandle_unwind(\n\t\t\t|| unsafe {\n\t\t\t\t'outer: loop {\n\t\t\t\t\t// This prevents us from entering a spin loop waiting for\n\t\t\t\t\t// the same lock to be unlocked\n\t\t\t\t\t// safety: we have the thread key\n\t\t\t\t\tlocks[first_index.get()].raw_write();\n\t\t\t\t\tfor (i, lock) in locks.iter().enumerate() {\n\t\t\t\t\t\tif i == first_index.get() {\n\t\t\t\t\t\t\t// we've already locked this one\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// If the lock has been killed, then this returns false\n\t\t\t\t\t\t// instead of panicking. This sounds like a problem, but if\n\t\t\t\t\t\t// it does return false, then the lock function is called\n\t\t\t\t\t\t// immediately after, causing a panic\n\t\t\t\t\t\t// safety: we have the thread key\n\t\t\t\t\t\tif lock.raw_try_write() {\n\t\t\t\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// safety: we already locked all of these\n\t\t\t\t\t\t\tattempt_to_recover_writes_from_panic(\u0026locks[0..i]);\n\t\t\t\t\t\t\tif first_index.get() \u003e= i {\n\t\t\t\t\t\t\t\t// safety: this is already locked and can't be\n\t\t\t\t\t\t\t\t// unlocked by the previous loop\n\t\t\t\t\t\t\t\tlocks[first_index.get()].raw_unlock_write();\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// nothing is locked anymore\n\t\t\t\t\t\t\tlocked.set(0);\n\n\t\t\t\t\t\t\t// call lock on this to prevent a spin loop\n\t\t\t\t\t\t\tfirst_index.set(i);\n\t\t\t\t\t\t\tcontinue 'outer;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// safety: we locked all the data\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t},\n\t\t\t|| {\n\t\t\t\tutils::attempt_to_recover_writes_from_panic(\u0026locks[0..locked.get()]);\n\t\t\t\tif first_index.get() \u003e= locked.get() {\n\t\t\t\t\tlocks[first_index.get()].raw_unlock_write();\n\t\t\t\t}\n\t\t\t},\n\t\t)\n\t}\n\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool {\n\t\tlet locks = get_locks_unsorted(\u0026self.child);\n\n\t\tif locks.is_empty() {\n\t\t\t// this is an interesting case, but it doesn't give us access to\n\t\t\t// any data, and can't possibly cause a deadlock\n\t\t\treturn true;\n\t\t}\n\n\t\t// these will be unlocked in case of a panic\n\t\tlet locked = Cell::new(0);\n\t\thandle_unwind(\n\t\t\t|| unsafe {\n\t\t\t\tfor (i, lock) in locks.iter().enumerate() {\n\t\t\t\t\t// safety: we have the thread key\n\t\t\t\t\tif lock.raw_try_write() {\n\t\t\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// safety: we already locked all of these\n\t\t\t\t\t\tattempt_to_recover_writes_from_panic(\u0026locks[0..i]);\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ttrue\n\t\t\t},\n\t\t\t|| utils::attempt_to_recover_writes_from_panic(\u0026locks[0..locked.get()]),\n\t\t)\n\t}\n\n\tunsafe fn raw_unlock_write(\u0026self) {\n\t\tlet locks = get_locks_unsorted(\u0026self.child);\n\n\t\tfor lock in locks {\n\t\t\tlock.raw_unlock_write();\n\t\t}\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tlet locks = get_locks_unsorted(\u0026self.child);\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\tunsafe {\n\t\t\t\t\tlocks[first_index.get()].raw_read();\n\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\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// safety: we have the thread key\n\t\t\t\t\t\tif lock.raw_try_read() {\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_reads_from_panic(\u0026locks[0..i]);\n\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 unlocked\n\t\t\t\t\t\t\t\t// by the previous loop\n\t\t\t\t\t\t\t\tlocks[first_index.get()].raw_unlock_read();\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// these are no longer locked\n\t\t\t\t\t\t\tlocked.set(0);\n\n\t\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\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_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.child);\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.child);\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\t// this collection, just like the sorting collection, must return all of its\n\t\t// locks in order to check for duplication\n\t\tself.child.get_ptrs(ptrs)\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 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.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 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.child.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.child.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.child.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.child.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.child.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.child.extend(iter)\n\t}\n}\n\nimpl\u003cT: ?Sized, L: AsRef\u003cT\u003e\u003e AsRef\u003cT\u003e for RetryingLockCollection\u003cL\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself.child.as_ref()\n\t}\n}\n\nimpl\u003cT: ?Sized, L: AsMut\u003cT\u003e\u003e AsMut\u003cT\u003e for RetryingLockCollection\u003cL\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself.child.as_mut()\n\t}\n}\n\nimpl\u003cL: OwnedLockable + Default\u003e Default for RetryingLockCollection\u003cL\u003e {\n\tfn default() -\u003e Self {\n\t\tSelf::new(L::default())\n\t}\n}\n\nimpl\u003cL: OwnedLockable\u003e From\u003cL\u003e for RetryingLockCollection\u003cL\u003e {\n\tfn from(value: L) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\nimpl\u003cL: OwnedLockable\u003e RetryingLockCollection\u003cL\u003e {\n\t/// Creates a new collection of owned locks.\n\t///\n\t/// Because the locks are owned, there's no need to do any checks for\n\t/// duplicate values. The locks also don't need to be sorted by memory\n\t/// address because they aren't used anywhere else.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t/// ```\n\t#[must_use]\n\tpub const fn new(data: L) -\u003e Self {\n\t\t// safety: the data cannot cannot contain references\n\t\tunsafe { Self::new_unchecked(data) }\n\t}\n}\n\nimpl\u003c'a, L: OwnedLockable\u003e RetryingLockCollection\u003c\u0026'a L\u003e {\n\t/// Creates a new collection of owned locks.\n\t///\n\t/// Because the locks are owned, there's no need to do any checks for\n\t/// duplicate values.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new_ref(\u0026data);\n\t/// ```\n\t#[must_use]\n\tpub const fn new_ref(data: \u0026'a L) -\u003e Self {\n\t\t// safety: the data cannot cannot contain references\n\t\tunsafe { Self::new_unchecked(data) }\n\t}\n}\n\nimpl\u003cL\u003e RetryingLockCollection\u003cL\u003e {\n\t/// Creates a new collections of locks.\n\t///\n\t/// # Safety\n\t///\n\t/// This results in undefined behavior if any locks are presented twice\n\t/// within this collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let data1 = Mutex::new(0);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // safety: data1 and data2 refer to distinct mutexes\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = unsafe { RetryingLockCollection::new_unchecked(\u0026data) };\n\t/// ```\n\t#[must_use]\n\tpub const unsafe fn new_unchecked(data: L) -\u003e Self {\n\t\tSelf { child: 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.child\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.child\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.child\n\t}\n}\n\nimpl\u003cL: Lockable\u003e RetryingLockCollection\u003cL\u003e {\n\t/// Creates a new collection of locks.\n\t///\n\t/// This returns `None` if any locks are found twice in the given\n\t/// collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::Mutex;\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let data1 = Mutex::new(0);\n\t/// let data2 = Mutex::new(\"\");\n\t///\n\t/// // data1 and data2 refer to distinct mutexes, so this won't panic\n\t/// let data = (\u0026data1, \u0026data2);\n\t/// let lock = RetryingLockCollection::try_new(\u0026data).unwrap();\n\t/// ```\n\t#[must_use]\n\tpub fn try_new(data: L) -\u003e Option\u003cSelf\u003e {\n\t\t// safety: the data is checked for duplicates before returning the collection\n\t\t(!contains_duplicates(\u0026data)).then_some(unsafe { Self::new_unchecked(data) })\n\t}\n\n\t/// Acquires an exclusive lock, blocking until it is safe to do so, and then\n\t/// unlocks after the provided function returns.\n\t///\n\t/// This function is useful to ensure that the data is never accidentally\n\t/// locked forever by leaking the guard. Even if the function panics, this\n\t/// function will gracefully notice the panic, and unlock. This function\n\t/// provides no guarantees with respect to the ordering of whether contentious\n\t/// readers or writers will acquire the lock first.\n\t///\n\t/// # Panics\n\t///\n\t/// This function will panic if the provided function also panics. However,\n\t/// the collection will be safely unlocked in this case, allowing the\n\t/// collection to be locked again later.\n\t///\n\t/// # Example\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, Mutex};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// lock.scoped_lock(\u0026mut key, |(number, string)| {\n\t/// *number += 1;\n\t/// *string = \"1\";\n\t/// });\n\t/// ```\n\tpub fn scoped_lock\u003c'a, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: impl Keyable,\n\t\tf: impl FnOnce(L::DataMut\u003c'a\u003e) -\u003e R,\n\t) -\u003e R {\n\t\tscoped_write(self, key, f)\n\t}\n\n\t/// Attempts to acquire an exclusive lock to the underlying data without\n\t/// blocking, and then unlocks once the provided function returns.\n\t///\n\t/// This function implements a non-blocking variant of [`scoped_lock`].\n\t/// Unlike `scoped_lock`, if the lock collection is not already unlocked, then\n\t/// the provided function will not run, and the given [`Keyable`] is returned.\n\t/// This method 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/// If any of the locks in the collection are already locked, then the\n\t/// provided function will not run. `Err` is returned with the given key.\n\t///\n\t/// # Panics\n\t///\n\t/// This function will panic if the provided function also panics. However,\n\t/// the collection will be safely unlocked in this case, allowing the\n\t/// collection to be locked again later.\n\t///\n\t/// # Example\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, Mutex};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// lock.scoped_try_lock(\u0026mut key, |(number, string)| {\n\t/// *number += 1;\n\t/// *string = \"1\";\n\t/// }).expect(\"This lock has not yet been locked\");\n\t/// ```\n\t///\n\t/// [`scoped_lock`]: RetryingLockCollection::scoped_lock\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 FnOnce(L::DataMut\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tscoped_try_write(self, key, f)\n\t}\n\n\t/// Locks the collection\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data. When the guard is dropped, the locks in the collection are also\n\t/// dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// ```\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::Guard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\t// safety: we're taking the thread key\n\t\t\tself.raw_write();\n\n\t\t\tLockGuard {\n\t\t\t\t// safety: we just locked the collection\n\t\t\t\tguard: self.guard(),\n\t\t\t\tkey,\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Attempts to lock the without blocking.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// locks when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If any of the locks in the collection are already locked, then an error\n\t/// is returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// match lock.try_lock(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::Guard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tunsafe {\n\t\t\t// safety: we're taking the thread key\n\t\t\tif self.raw_try_write() {\n\t\t\t\tOk(LockGuard {\n\t\t\t\t\t// safety: we just succeeded in locking everything\n\t\t\t\t\tguard: self.guard(),\n\t\t\t\t\tkey,\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\tErr(key)\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (Mutex::new(0), Mutex::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.lock(key);\n\t/// *guard.0 += 1;\n\t/// *guard.1 = \"1\";\n\t/// let key = RetryingLockCollection::\u003c(Mutex\u003ci32\u003e, Mutex\u003c\u0026str\u003e)\u003e::unlock(guard);\n\t/// ```\n\tpub fn unlock(guard: LockGuard\u003cL::Guard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: Sharable\u003e RetryingLockCollection\u003cL\u003e {\n\t/// Acquires a shared lock, blocking until it is safe to do so, and then\n\t/// unlocks after the provided function returns.\n\t///\n\t/// This function is useful to ensure that the data is never accidentally\n\t/// locked forever by leaking the guard. Even if the function panics, this\n\t/// function will gracefully notice the panic, and unlock. This function\n\t/// provides no guarantees with respect to the ordering of whether contentious\n\t/// readers or writers will acquire the lock first.\n\t///\n\t/// # Panics\n\t///\n\t/// This function will panic if the provided function also panics. However,\n\t/// the collection will be safely unlocked in this case, allowing the\n\t/// collection to be locked again later.\n\t///\n\t/// # Example\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, RwLock};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// lock.scoped_read(\u0026mut key, |(number, string)| {\n\t/// assert_eq!(*number, 0);\n\t/// assert_eq!(*string, \"\");\n\t/// });\n\t/// ```\n\tpub fn scoped_read\u003c'a, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: impl Keyable,\n\t\tf: impl FnOnce(L::DataRef\u003c'a\u003e) -\u003e R,\n\t) -\u003e R {\n\t\tscoped_read(self, key, f)\n\t}\n\n\t/// Attempts to acquire an exclusive lock to the underlying data without\n\t/// blocking, and then unlocks once the provided function returns.\n\t///\n\t/// This function implements a non-blocking variant of [`scoped_read`].\n\t/// Unlike `scoped_read`, if the lock collection is exclusively locked, then\n\t/// the provided function will not run, and the given [`Keyable`] is returned.\n\t/// This method 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/// If any of the locks in the collection are already exclusively locked, then\n\t/// the provided function will not run. `Err` is returned with the given key.\n\t///\n\t/// # Panics\n\t///\n\t/// This function will panic if the provided function also panics. However,\n\t/// the collection will be safely unlocked in this case, allowing the\n\t/// collection to be locked again later.\n\t///\n\t/// # Example\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, RwLock};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// lock.scoped_try_read(\u0026mut key, |(number, string)| {\n\t/// assert_eq!(*number, 0);\n\t/// assert_eq!(*string, \"\");\n\t/// }).expect(\"This lock has not yet been locked\");\n\t/// ```\n\t///\n\t/// [`scoped_read`]: RetryingLockCollection::scoped_read\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 FnOnce(L::DataRef\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tscoped_try_read(self, key, f)\n\t}\n\n\t/// Locks the collection, so that other threads can still read from it\n\t///\n\t/// This function returns a guard that can be used to access the underlying\n\t/// data immutably. When the guard is dropped, the locks in the collection\n\t/// are also dropped.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// assert_eq!(*guard.0, 0);\n\t/// assert_eq!(*guard.1, \"\");\n\t/// ```\n\tpub fn read(\u0026self, key: ThreadKey) -\u003e LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\t// safety: we're taking the thread key\n\t\t\tself.raw_read();\n\n\t\t\tLockGuard {\n\t\t\t\t// safety: we just locked the collection\n\t\t\t\tguard: self.read_guard(),\n\t\t\t\tkey,\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Attempts to lock the without blocking, in such a way that other threads\n\t/// can still read from the collection.\n\t///\n\t/// If the access could not be granted at this time, then `Err` is\n\t/// returned. Otherwise, an RAII guard is returned which will release the\n\t/// shared access when it is dropped.\n\t///\n\t/// # Errors\n\t///\n\t/// If shared access cannot be acquired at this time, then an error is\n\t/// returned containing the given key.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(5), RwLock::new(\"6\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// match lock.try_read(key) {\n\t/// Ok(mut guard) =\u003e {\n\t/// assert_eq!(*guard.0, 5);\n\t/// assert_eq!(*guard.1, \"6\");\n\t/// },\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// ```\n\tpub fn try_read(\u0026self, key: ThreadKey) -\u003e Result\u003cLockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e, ThreadKey\u003e {\n\t\tunsafe {\n\t\t\t// safety: we're taking the thread key\n\t\t\tif !self.raw_try_read() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\tOk(LockGuard {\n\t\t\t\t// safety: we just succeeded in locking everything\n\t\t\t\tguard: self.read_guard(),\n\t\t\t\tkey,\n\t\t\t})\n\t\t}\n\t}\n\n\t/// Unlocks the underlying lockable data type, returning the key that's\n\t/// associated with it.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = (RwLock::new(0), RwLock::new(\"\"));\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// let key = RetryingLockCollection::\u003c(RwLock\u003ci32\u003e, RwLock\u003c\u0026str\u003e)\u003e::unlock_read(guard);\n\t/// ```\n\tpub fn unlock_read(guard: LockGuard\u003cL::ReadGuard\u003c'_\u003e\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.guard);\n\t\tguard.key\n\t}\n}\n\nimpl\u003cL: LockableGetMut\u003e RetryingLockCollection\u003cL\u003e {\n\t/// Gets a mutable reference to the data behind this\n\t/// `RetryingLockCollection`.\n\t///\n\t/// Since this call borrows the `RetryingLockCollection` mutably, no actual\n\t/// locking needs to take place - the mutable borrow statically guarantees\n\t/// no locks exist.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let mut mutex = RetryingLockCollection::new([Mutex::new(0), Mutex::new(0)]);\n\t/// assert_eq!(mutex.get_mut(), [\u0026mut 0, \u0026mut 0]);\n\t/// ```\n\tpub fn get_mut(\u0026mut self) -\u003e L::Inner\u003c'_\u003e {\n\t\tLockableGetMut::get_mut(self)\n\t}\n}\n\nimpl\u003cL: LockableIntoInner\u003e RetryingLockCollection\u003cL\u003e {\n\t/// Consumes this `RetryingLockCollection`, returning the underlying data.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, LockCollection};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let mutex = RetryingLockCollection::new([Mutex::new(0), Mutex::new(0)]);\n\t/// assert_eq!(mutex.into_inner(), [0, 0]);\n\t/// ```\n\tpub fn into_inner(self) -\u003e L::Inner {\n\t\tLockableIntoInner::into_inner(self)\n\t}\n}\n\nimpl\u003c'a, L: 'a\u003e RetryingLockCollection\u003cL\u003e\nwhere\n\t\u0026'a L: IntoIterator,\n{\n\t/// Returns an iterator over references to each value in the collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = [Mutex::new(26), Mutex::new(1)];\n\t/// let lock = RetryingLockCollection::new(data);\n\t///\n\t/// let mut iter = lock.iter();\n\t/// let mutex = iter.next().unwrap();\n\t/// let guard = mutex.lock(key);\n\t///\n\t/// assert_eq!(*guard, 26);\n\t/// ```\n\t#[must_use]\n\tpub fn iter(\u0026'a self) -\u003e \u003c\u0026'a L as IntoIterator\u003e::IntoIter {\n\t\tself.into_iter()\n\t}\n}\n\nimpl\u003c'a, L: 'a\u003e RetryingLockCollection\u003cL\u003e\nwhere\n\t\u0026'a mut L: IntoIterator,\n{\n\t/// Returns an iterator over mutable references to each value in the\n\t/// collection.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::RetryingLockCollection;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = [Mutex::new(26), Mutex::new(1)];\n\t/// let mut lock = RetryingLockCollection::new(data);\n\t///\n\t/// let mut iter = lock.iter_mut();\n\t/// let mutex = iter.next().unwrap();\n\t///\n\t/// assert_eq!(*mutex.as_mut(), 26);\n\t/// ```\n\t#[must_use]\n\tpub fn iter_mut(\u0026'a mut self) -\u003e \u003c\u0026'a mut L as IntoIterator\u003e::IntoIter {\n\t\tself.into_iter()\n\t}\n}\n\n#[cfg(test)]\nmod tests {\n\tuse super::*;\n\tuse crate::collection::BoxedLockCollection;\n\tuse crate::{Mutex, RwLock, ThreadKey};\n\n\t#[test]\n\tfn nonduplicate_lock_references_are_allowed() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(0);\n\t\tassert!(RetryingLockCollection::try_new([\u0026mutex1, \u0026mutex2]).is_some());\n\t}\n\n\t#[test]\n\tfn duplicate_lock_references_are_disallowed() {\n\t\tlet mutex = Mutex::new(0);\n\t\tassert!(RetryingLockCollection::try_new([\u0026mutex, \u0026mutex]).is_none());\n\t}\n\n\t#[test]\n\t#[expect(clippy::float_cmp)]\n\tfn uses_correct_default() {\n\t\tlet collection =\n\t\t\tRetryingLockCollection::\u003c(RwLock\u003cf64\u003e, Mutex\u003cOption\u003ci32\u003e\u003e, Mutex\u003cusize\u003e)\u003e::default();\n\t\tlet tuple = collection.into_inner();\n\t\tassert_eq!(tuple.0, 0.0);\n\t\tassert!(tuple.1.is_none());\n\t\tassert_eq!(tuple.2, 0)\n\t}\n\n\t#[test]\n\tfn from() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection =\n\t\t\tRetryingLockCollection::from([Mutex::new(\"foo\"), Mutex::new(\"bar\"), Mutex::new(\"baz\")]);\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], \"foo\");\n\t\tassert_eq!(*guard[1], \"bar\");\n\t\tassert_eq!(*guard[2], \"baz\");\n\t}\n\n\t#[test]\n\tfn new_ref_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet collection = RetryingLockCollection::new_ref(\u0026mutexes);\n\t\tcollection.scoped_lock(key, |guard| {\n\t\t\tassert_eq!(*guard[0], 0);\n\t\t\tassert_eq!(*guard[1], 1);\n\t\t})\n\t}\n\n\t#[test]\n\tfn scoped_read_sees_changes() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = RetryingLockCollection::new(mutexes);\n\t\tcollection.scoped_lock(\u0026mut key, |guard| *guard[0] = 128);\n\n\t\tlet sum = collection.scoped_read(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 128);\n\t\t\tassert_eq!(*guard[1], 42);\n\t\t\t*guard[0] + *guard[1]\n\t\t});\n\n\t\tassert_eq!(sum, 128 + 42);\n\t}\n\n\t#[test]\n\tfn get_mut_affects_scoped_read() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet mut collection = RetryingLockCollection::new(mutexes);\n\t\tlet guard = collection.get_mut();\n\t\t*guard[0] = 128;\n\n\t\tlet sum = collection.scoped_read(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 128);\n\t\t\tassert_eq!(*guard[1], 42);\n\t\t\t*guard[0] + *guard[1]\n\t\t});\n\n\t\tassert_eq!(sum, 128 + 42);\n\t}\n\n\t#[test]\n\tfn scoped_try_lock_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = RetryingLockCollection::new([Mutex::new(1), Mutex::new(2)]);\n\t\tlet guard = collection.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = collection.scoped_try_lock(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn scoped_try_read_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = RetryingLockCollection::new([RwLock::new(1), RwLock::new(2)]);\n\t\tlet guard = collection.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = collection.scoped_try_read(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn try_lock_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = RetryingLockCollection::new([Mutex::new(1), Mutex::new(2)]);\n\t\tlet guard = collection.try_lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_lock(key);\n\t\t\t\tassert!(guard.is_err());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn try_read_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = RetryingLockCollection::new([RwLock::new(1), RwLock::new(2)]);\n\t\tlet guard = collection.try_read(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = collection.try_read(key);\n\t\t\t\tassert!(guard.is_ok());\n\t\t\t});\n\t\t});\n\n\t\tassert!(guard.is_ok());\n\t}\n\n\t#[test]\n\tfn try_read_fails_for_locked_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutexes = [RwLock::new(24), RwLock::new(42)];\n\t\tlet collection = RetryingLockCollection::new_ref(\u0026mutexes);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet guard = mutexes[1].write(key);\n\t\t\t\tassert_eq!(*guard, 42);\n\t\t\t\tstd::mem::forget(guard);\n\t\t\t});\n\t\t});\n\n\t\tlet guard = collection.try_read(key);\n\t\tassert!(guard.is_err());\n\t}\n\n\t#[test]\n\tfn locks_all_inner_mutexes() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(0);\n\t\tlet collection = RetryingLockCollection::try_new([\u0026mutex1, \u0026mutex2]).unwrap();\n\n\t\tlet guard = collection.lock(key);\n\n\t\tassert!(mutex1.is_locked());\n\t\tassert!(mutex2.is_locked());\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn locks_all_inner_rwlocks() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet rwlock1 = RwLock::new(0);\n\t\tlet rwlock2 = RwLock::new(0);\n\t\tlet collection = RetryingLockCollection::try_new([\u0026rwlock1, \u0026rwlock2]).unwrap();\n\n\t\tlet guard = collection.read(key);\n\n\t\tassert!(rwlock1.is_locked());\n\t\tassert!(rwlock2.is_locked());\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn works_with_other_collections() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(0);\n\t\tlet collection = BoxedLockCollection::try_new(\n\t\t\tRetryingLockCollection::try_new([\u0026mutex1, \u0026mutex2]).unwrap(),\n\t\t)\n\t\t.unwrap();\n\n\t\tlet guard = collection.lock(key);\n\n\t\tassert!(mutex1.is_locked());\n\t\tassert!(mutex2.is_locked());\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn from_iterator() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection: RetryingLockCollection\u003cVec\u003cMutex\u003c\u0026str\u003e\u003e\u003e =\n\t\t\t[Mutex::new(\"foo\"), Mutex::new(\"bar\"), Mutex::new(\"baz\")]\n\t\t\t\t.into_iter()\n\t\t\t\t.collect();\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], \"foo\");\n\t\tassert_eq!(*guard[1], \"bar\");\n\t\tassert_eq!(*guard[2], \"baz\");\n\t}\n\n\t#[test]\n\tfn into_owned_iterator() {\n\t\tlet collection = RetryingLockCollection::new([Mutex::new(0), Mutex::new(1), Mutex::new(2)]);\n\t\tfor (i, mutex) in collection.into_iter().enumerate() {\n\t\t\tassert_eq!(mutex.into_inner(), i);\n\t\t}\n\t}\n\n\t#[test]\n\tfn into_ref_iterator() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet collection = RetryingLockCollection::new([Mutex::new(0), Mutex::new(1), Mutex::new(2)]);\n\t\tfor (i, mutex) in (\u0026collection).into_iter().enumerate() {\n\t\t\tmutex.scoped_lock(\u0026mut key, |val| assert_eq!(*val, i))\n\t\t}\n\t}\n\n\t#[test]\n\tfn ref_iterator() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet collection = RetryingLockCollection::new([Mutex::new(0), Mutex::new(1), Mutex::new(2)]);\n\t\tfor (i, mutex) in collection.iter().enumerate() {\n\t\t\tmutex.scoped_lock(\u0026mut key, |val| assert_eq!(*val, i))\n\t\t}\n\t}\n\n\t#[test]\n\tfn mut_iterator() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mut collection =\n\t\t\tRetryingLockCollection::new([Mutex::new(0), Mutex::new(1), Mutex::new(2)]);\n\t\tfor (i, mutex) in collection.iter_mut().enumerate() {\n\t\t\tmutex.scoped_lock(\u0026mut key, |val| assert_eq!(*val, i))\n\t\t}\n\t}\n\n\t#[test]\n\tfn extend_collection() {\n\t\tlet mutex1 = Mutex::new(0);\n\t\tlet mutex2 = Mutex::new(0);\n\t\tlet mut collection = RetryingLockCollection::new(vec![mutex1]);\n\n\t\tcollection.extend([mutex2]);\n\n\t\tassert_eq!(collection.into_inner().len(), 2);\n\t}\n\n\t#[test]\n\tfn lock_empty_lock_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection: RetryingLockCollection\u003c[RwLock\u003ci32\u003e; 0]\u003e = RetryingLockCollection::new([]);\n\n\t\tlet guard = collection.lock(key);\n\t\tassert!(guard.is_empty());\n\t\tlet key = RetryingLockCollection::\u003c[RwLock\u003c_\u003e; 0]\u003e::unlock(guard);\n\n\t\tlet guard = collection.read(key);\n\t\tassert!(guard.is_empty());\n\t}\n\n\t#[test]\n\tfn read_empty_lock_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection: RetryingLockCollection\u003c[RwLock\u003ci32\u003e; 0]\u003e = RetryingLockCollection::new([]);\n\n\t\tlet guard = collection.read(key);\n\t\tassert!(guard.is_empty());\n\t\tlet key = RetryingLockCollection::\u003c[RwLock\u003c_\u003e; 0]\u003e::unlock_read(guard);\n\n\t\tlet guard = collection.lock(key);\n\t\tassert!(guard.is_empty());\n\t}\n\n\t#[test]\n\tfn as_ref_works() {\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet collection = RetryingLockCollection::new_ref(\u0026mutexes);\n\n\t\tassert!(std::ptr::addr_eq(\u0026raw const mutexes, collection.as_ref()))\n\t}\n\n\t#[test]\n\tfn as_mut_works() {\n\t\tlet mut mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet mut collection = RetryingLockCollection::new(\u0026mut mutexes);\n\n\t\tcollection.as_mut()[0] = Mutex::new(42);\n\n\t\tassert_eq!(*collection.as_mut()[0].get_mut(), 42);\n\t}\n\n\t#[test]\n\tfn child() {\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet collection = RetryingLockCollection::new_ref(\u0026mutexes);\n\n\t\tassert!(std::ptr::addr_eq(\u0026raw const mutexes, *collection.child()))\n\t}\n\n\t#[test]\n\tfn child_mut_works() {\n\t\tlet mut mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet mut collection = RetryingLockCollection::new(\u0026mut mutexes);\n\n\t\tcollection.child_mut()[0] = Mutex::new(42);\n\n\t\tassert_eq!(*collection.child_mut()[0].get_mut(), 42);\n\t}\n\n\t#[test]\n\tfn into_child_works() {\n\t\tlet mutexes = [Mutex::new(0), Mutex::new(1)];\n\t\tlet mut collection = RetryingLockCollection::new(mutexes);\n\n\t\tcollection.child_mut()[0] = Mutex::new(42);\n\n\t\tassert_eq!(\n\t\t\t*collection\n\t\t\t\t.into_child()\n\t\t\t\t.as_mut()\n\t\t\t\t.get_mut(0)\n\t\t\t\t.unwrap()\n\t\t\t\t.get_mut(),\n\t\t\t42\n\t\t);\n\t}\n}\n","traces":[{"line":18,"address":[669880,670568,669840],"length":1,"stats":{"Line":11}},{"line":19,"address":[1194764,1195532],"length":1,"stats":{"Line":11}},{"line":20,"address":[657403],"length":1,"stats":{"Line":11}},{"line":22,"address":[658139,657466,658096],"length":1,"stats":{"Line":33}},{"line":24,"address":[657550,657616],"length":1,"stats":{"Line":22}},{"line":25,"address":[583335,583432,583571],"length":1,"stats":{"Line":33}},{"line":26,"address":[618489,618569],"length":1,"stats":{"Line":22}},{"line":27,"address":[618579],"length":1,"stats":{"Line":1}},{"line":31,"address":[],"length":0,"stats":{"Line":11}},{"line":44,"address":[585632,585886,585880],"length":1,"stats":{"Line":12}},{"line":45,"address":[669548],"length":1,"stats":{"Line":11}},{"line":47,"address":[],"length":0,"stats":{"Line":22}},{"line":49,"address":[],"length":0,"stats":{"Line":0}},{"line":53,"address":[1616000,1615728,1616037,1613861,1616544,1613077,1614405,1617157,1615765,1616581,1614368,1613824,1617120,1613040],"length":1,"stats":{"Line":20}},{"line":54,"address":[],"length":0,"stats":{"Line":10}},{"line":56,"address":[585805],"length":1,"stats":{"Line":20}},{"line":57,"address":[],"length":0,"stats":{"Line":0}},{"line":61,"address":[658177],"length":1,"stats":{"Line":10}},{"line":62,"address":[],"length":0,"stats":{"Line":10}},{"line":63,"address":[616066],"length":1,"stats":{"Line":10}},{"line":65,"address":[],"length":0,"stats":{"Line":0}},{"line":73,"address":[619032],"length":1,"stats":{"Line":10}},{"line":74,"address":[616341,616191],"length":1,"stats":{"Line":18}},{"line":77,"address":[616128],"length":1,"stats":{"Line":1}},{"line":78,"address":[],"length":0,"stats":{"Line":1}},{"line":81,"address":[657225],"length":1,"stats":{"Line":1}},{"line":85,"address":[619165],"length":1,"stats":{"Line":1}},{"line":88,"address":[],"length":0,"stats":{"Line":1}},{"line":89,"address":[],"length":0,"stats":{"Line":0}},{"line":94,"address":[],"length":0,"stats":{"Line":0}},{"line":97,"address":[619296],"length":1,"stats":{"Line":11}},{"line":98,"address":[657321],"length":1,"stats":{"Line":1}},{"line":99,"address":[619364],"length":1,"stats":{"Line":1}},{"line":100,"address":[619417],"length":1,"stats":{"Line":1}},{"line":106,"address":[599021,599027,598832],"length":1,"stats":{"Line":4}},{"line":107,"address":[598854],"length":1,"stats":{"Line":3}},{"line":109,"address":[598910,598864],"length":1,"stats":{"Line":6}},{"line":112,"address":[1613320],"length":1,"stats":{"Line":0}},{"line":116,"address":[584942,584900],"length":1,"stats":{"Line":6}},{"line":118,"address":[1197152],"length":1,"stats":{"Line":3}},{"line":119,"address":[663838,663761],"length":1,"stats":{"Line":6}},{"line":121,"address":[],"length":0,"stats":{"Line":3}},{"line":122,"address":[664012],"length":1,"stats":{"Line":3}},{"line":125,"address":[],"length":0,"stats":{"Line":2}},{"line":126,"address":[1197419],"length":1,"stats":{"Line":2}},{"line":130,"address":[1197363],"length":1,"stats":{"Line":1}},{"line":132,"address":[],"length":0,"stats":{"Line":2}},{"line":136,"address":[],"length":0,"stats":{"Line":3}},{"line":137,"address":[585039,585343],"length":1,"stats":{"Line":3}},{"line":139,"address":[585517,585213,585353,585049],"length":1,"stats":{"Line":6}},{"line":140,"address":[1615335,1613703,1616973,1615309,1616999,1613677],"length":1,"stats":{"Line":6}},{"line":144,"address":[639952,640200,640206],"length":1,"stats":{"Line":6}},{"line":145,"address":[],"length":0,"stats":{"Line":5}},{"line":147,"address":[675690,675638],"length":1,"stats":{"Line":10}},{"line":149,"address":[],"length":0,"stats":{"Line":0}},{"line":152,"address":[],"length":0,"stats":{"Line":8}},{"line":153,"address":[675738],"length":1,"stats":{"Line":4}},{"line":155,"address":[],"length":0,"stats":{"Line":8}},{"line":157,"address":[],"length":0,"stats":{"Line":0}},{"line":158,"address":[1198337,1202449,1200241],"length":1,"stats":{"Line":4}},{"line":160,"address":[583942],"length":1,"stats":{"Line":4}},{"line":161,"address":[584130],"length":1,"stats":{"Line":4}},{"line":162,"address":[],"length":0,"stats":{"Line":0}},{"line":166,"address":[1198612,1200516,1202724],"length":1,"stats":{"Line":4}},{"line":167,"address":[671055,671205],"length":1,"stats":{"Line":6}},{"line":170,"address":[1198636,1200540,1202748],"length":1,"stats":{"Line":1}},{"line":172,"address":[1202783,1200575,1198671],"length":1,"stats":{"Line":1}},{"line":175,"address":[],"length":0,"stats":{"Line":1}},{"line":179,"address":[],"length":0,"stats":{"Line":1}},{"line":182,"address":[1202880,1200672,1198768],"length":1,"stats":{"Line":1}},{"line":183,"address":[],"length":0,"stats":{"Line":0}},{"line":188,"address":[],"length":0,"stats":{"Line":0}},{"line":191,"address":[640150],"length":1,"stats":{"Line":5}},{"line":192,"address":[1200793,1198889,1203001],"length":1,"stats":{"Line":1}},{"line":193,"address":[584500],"length":1,"stats":{"Line":1}},{"line":194,"address":[],"length":0,"stats":{"Line":1}},{"line":200,"address":[1617296,1614733,1617491,1614739,1614544,1617485],"length":1,"stats":{"Line":4}},{"line":201,"address":[],"length":0,"stats":{"Line":3}},{"line":203,"address":[],"length":0,"stats":{"Line":6}},{"line":206,"address":[],"length":0,"stats":{"Line":0}},{"line":209,"address":[],"length":0,"stats":{"Line":6}},{"line":211,"address":[1204640,1199792],"length":1,"stats":{"Line":3}},{"line":212,"address":[679742,679665],"length":1,"stats":{"Line":6}},{"line":214,"address":[679831],"length":1,"stats":{"Line":3}},{"line":215,"address":[],"length":0,"stats":{"Line":3}},{"line":218,"address":[],"length":0,"stats":{"Line":2}},{"line":219,"address":[1204907,1200059],"length":1,"stats":{"Line":2}},{"line":223,"address":[1200003,1204851],"length":1,"stats":{"Line":1}},{"line":225,"address":[680016,679984],"length":1,"stats":{"Line":2}},{"line":229,"address":[],"length":0,"stats":{"Line":1}},{"line":230,"address":[],"length":0,"stats":{"Line":1}},{"line":232,"address":[],"length":0,"stats":{"Line":2}},{"line":233,"address":[1615031,1615005],"length":1,"stats":{"Line":2}},{"line":249,"address":[1621488],"length":1,"stats":{"Line":1}},{"line":252,"address":[],"length":0,"stats":{"Line":1}},{"line":255,"address":[684416],"length":1,"stats":{"Line":8}},{"line":256,"address":[595457],"length":1,"stats":{"Line":8}},{"line":259,"address":[],"length":0,"stats":{"Line":3}},{"line":260,"address":[1621553,1621361,1621441],"length":1,"stats":{"Line":3}},{"line":275,"address":[675520],"length":1,"stats":{"Line":4}},{"line":276,"address":[1618177,1618145,1618081,1618053],"length":1,"stats":{"Line":4}},{"line":279,"address":[],"length":0,"stats":{"Line":1}},{"line":280,"address":[],"length":0,"stats":{"Line":1}},{"line":292,"address":[],"length":0,"stats":{"Line":1}},{"line":293,"address":[],"length":0,"stats":{"Line":1}},{"line":300,"address":[],"length":0,"stats":{"Line":2}},{"line":301,"address":[],"length":0,"stats":{"Line":2}},{"line":312,"address":[],"length":0,"stats":{"Line":1}},{"line":313,"address":[],"length":0,"stats":{"Line":1}},{"line":324,"address":[],"length":0,"stats":{"Line":1}},{"line":325,"address":[],"length":0,"stats":{"Line":1}},{"line":336,"address":[],"length":0,"stats":{"Line":1}},{"line":337,"address":[1619237],"length":1,"stats":{"Line":1}},{"line":344,"address":[],"length":0,"stats":{"Line":1}},{"line":345,"address":[1572945],"length":1,"stats":{"Line":1}},{"line":346,"address":[],"length":0,"stats":{"Line":1}},{"line":351,"address":[],"length":0,"stats":{"Line":1}},{"line":352,"address":[],"length":0,"stats":{"Line":1}},{"line":357,"address":[],"length":0,"stats":{"Line":1}},{"line":358,"address":[1619429],"length":1,"stats":{"Line":1}},{"line":363,"address":[],"length":0,"stats":{"Line":1}},{"line":364,"address":[1621637],"length":1,"stats":{"Line":1}},{"line":369,"address":[1621712],"length":1,"stats":{"Line":1}},{"line":370,"address":[],"length":0,"stats":{"Line":1}},{"line":375,"address":[],"length":0,"stats":{"Line":1}},{"line":376,"address":[],"length":0,"stats":{"Line":1}},{"line":397,"address":[1584832,1585232,1585024,1584896,1585168,1584960,1585008,1585248,1585104],"length":1,"stats":{"Line":9}},{"line":399,"address":[1585237,1585035,1584971,1584845,1585180,1584910,1585009,1585116,1585261],"length":1,"stats":{"Line":9}},{"line":419,"address":[],"length":0,"stats":{"Line":2}},{"line":421,"address":[],"length":0,"stats":{"Line":2}},{"line":447,"address":[598368],"length":1,"stats":{"Line":21}},{"line":468,"address":[1602496],"length":1,"stats":{"Line":1}},{"line":469,"address":[],"length":0,"stats":{"Line":0}},{"line":489,"address":[1602464,1602240],"length":1,"stats":{"Line":2}},{"line":490,"address":[],"length":0,"stats":{"Line":0}},{"line":510,"address":[],"length":0,"stats":{"Line":1}},{"line":511,"address":[],"length":0,"stats":{"Line":1}},{"line":535,"address":[594944,595109],"length":1,"stats":{"Line":11}},{"line":537,"address":[584475,584651,584530,584706],"length":1,"stats":{"Line":22}},{"line":570,"address":[584352],"length":1,"stats":{"Line":3}},{"line":575,"address":[],"length":0,"stats":{"Line":3}},{"line":615,"address":[584320],"length":1,"stats":{"Line":2}},{"line":620,"address":[],"length":0,"stats":{"Line":2}},{"line":643,"address":[1603872,1605238,1604424,1604418,1604634,1604778,1604656,1604016,1605244,1603994,1604138,1604000,1604772,1605152,1604132,1604512,1604628,1604352],"length":1,"stats":{"Line":10}},{"line":646,"address":[1604544,1604048,1604366,1603902,1605166,1604688],"length":1,"stats":{"Line":9}},{"line":650,"address":[669230],"length":1,"stats":{"Line":8}},{"line":686,"address":[598784,598592,598790],"length":1,"stats":{"Line":3}},{"line":689,"address":[1604235,1604324,1604253,1604192],"length":1,"stats":{"Line":5}},{"line":690,"address":[598726],"length":1,"stats":{"Line":1}},{"line":692,"address":[598704],"length":1,"stats":{"Line":1}},{"line":693,"address":[],"length":0,"stats":{"Line":0}},{"line":696,"address":[],"length":0,"stats":{"Line":1}},{"line":719,"address":[1604448,1604496,1604490],"length":1,"stats":{"Line":1}},{"line":720,"address":[1604452],"length":1,"stats":{"Line":1}},{"line":721,"address":[],"length":0,"stats":{"Line":0}},{"line":756,"address":[1572480,1572512],"length":1,"stats":{"Line":2}},{"line":761,"address":[],"length":0,"stats":{"Line":2}},{"line":801,"address":[1572544],"length":1,"stats":{"Line":1}},{"line":806,"address":[],"length":0,"stats":{"Line":1}},{"line":829,"address":[639808,639934,639940],"length":1,"stats":{"Line":5}},{"line":832,"address":[1605648,1605342],"length":1,"stats":{"Line":4}},{"line":836,"address":[],"length":0,"stats":{"Line":3}},{"line":873,"address":[1605424,1605596,1605932,1605590,1605760,1605926],"length":1,"stats":{"Line":4}},{"line":876,"address":[],"length":0,"stats":{"Line":5}},{"line":877,"address":[675165],"length":1,"stats":{"Line":1}},{"line":880,"address":[],"length":0,"stats":{"Line":1}},{"line":882,"address":[1605529,1605865],"length":1,"stats":{"Line":1}},{"line":883,"address":[],"length":0,"stats":{"Line":0}},{"line":904,"address":[],"length":0,"stats":{"Line":1}},{"line":905,"address":[],"length":0,"stats":{"Line":1}},{"line":906,"address":[],"length":0,"stats":{"Line":0}},{"line":927,"address":[],"length":0,"stats":{"Line":1}},{"line":928,"address":[],"length":0,"stats":{"Line":1}},{"line":944,"address":[],"length":0,"stats":{"Line":2}},{"line":945,"address":[],"length":0,"stats":{"Line":2}},{"line":972,"address":[],"length":0,"stats":{"Line":1}},{"line":973,"address":[],"length":0,"stats":{"Line":1}},{"line":1000,"address":[],"length":0,"stats":{"Line":1}},{"line":1001,"address":[],"length":0,"stats":{"Line":1}}],"covered":161,"coverable":179},{"path":["/","home","botahamec","Projects","happylock","src","collection","utils.rs"],"content":"use std::cell::Cell;\n\nuse crate::handle_unwind::handle_unwind;\nuse crate::lockable::{Lockable, RawLock, Sharable};\nuse crate::Keyable;\n\n/// Returns a list of locks in the given collection and sorts them by their\n/// memory address\n#[must_use]\npub fn get_locks\u003cL: Lockable\u003e(data: \u0026L) -\u003e Vec\u003c\u0026dyn RawLock\u003e {\n\tlet mut locks = get_locks_unsorted(data);\n\tlocks.sort_by_key(|lock| \u0026raw const **lock);\n\tlocks\n}\n\n/// Returns a list of locks from the data. Unlike the above function, this does\n/// not do any sorting of the locks.\n#[must_use]\npub fn get_locks_unsorted\u003cL: Lockable\u003e(data: \u0026L) -\u003e Vec\u003c\u0026dyn RawLock\u003e {\n\tlet mut locks = Vec::new();\n\tdata.get_ptrs(\u0026mut locks);\n\tlocks\n}\n\n/// returns `true` if the sorted list contains a duplicate\n#[must_use]\npub fn ordered_contains_duplicates(l: \u0026[\u0026dyn RawLock]) -\u003e bool {\n\tif l.is_empty() {\n\t\t// Return early to prevent panic in the below call to `windows`\n\t\treturn false;\n\t}\n\n\tl.windows(2)\n\t\t// NOTE: addr_eq is necessary because eq would also compare the v-table pointers\n\t\t.any(|window| std::ptr::addr_eq(window[0], window[1]))\n}\n\n/// Lock a set of locks in the given order. It's UB to call this without a `ThreadKey`\npub unsafe fn ordered_write(locks: \u0026[\u0026dyn RawLock]) {\n\t// these will be unlocked in case of a panic\n\tlet locked = Cell::new(0);\n\n\thandle_unwind(\n\t\t|| {\n\t\t\tfor lock in locks {\n\t\t\t\tlock.raw_write();\n\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t}\n\t\t},\n\t\t|| attempt_to_recover_writes_from_panic(\u0026locks[0..locked.get()]),\n\t)\n}\n\n/// Lock a set of locks in the given order. It's UB to call this without a `ThreadKey`\npub unsafe fn ordered_read(locks: \u0026[\u0026dyn RawLock]) {\n\tlet locked = Cell::new(0);\n\n\thandle_unwind(\n\t\t|| {\n\t\t\tfor lock in locks {\n\t\t\t\tlock.raw_read();\n\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t}\n\t\t},\n\t\t|| attempt_to_recover_reads_from_panic(\u0026locks[0..locked.get()]),\n\t)\n}\n\n/// Locks the locks in the order they are given. This causes deadlock if the\n/// locks contain duplicates, or if this is called by multiple threads with the\n/// locks in different orders.\npub unsafe fn ordered_try_write(locks: \u0026[\u0026dyn RawLock]) -\u003e bool {\n\tlet locked = Cell::new(0);\n\n\thandle_unwind(\n\t\t|| unsafe {\n\t\t\tfor (i, lock) in locks.iter().enumerate() {\n\t\t\t\t// safety: we have the thread key\n\t\t\t\tif lock.raw_try_write() {\n\t\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t\t} else {\n\t\t\t\t\tfor lock in \u0026locks[0..i] {\n\t\t\t\t\t\t// safety: this lock was already acquired\n\t\t\t\t\t\tlock.raw_unlock_write();\n\t\t\t\t\t}\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttrue\n\t\t},\n\t\t||\n\t\t// safety: everything in locked is locked\n\t\tattempt_to_recover_writes_from_panic(\u0026locks[0..locked.get()]),\n\t)\n}\n\n/// Locks the locks in the order they are given. This causes deadlock if this\n/// is called by multiple threads with the locks in different orders.\npub unsafe fn ordered_try_read(locks: \u0026[\u0026dyn RawLock]) -\u003e bool {\n\t// these will be unlocked in case of a panic\n\tlet locked = Cell::new(0);\n\n\thandle_unwind(\n\t\t|| unsafe {\n\t\t\tfor (i, lock) in locks.iter().enumerate() {\n\t\t\t\t// safety: we have the thread key\n\t\t\t\tif lock.raw_try_read() {\n\t\t\t\t\tlocked.set(locked.get() + 1);\n\t\t\t\t} else {\n\t\t\t\t\tfor lock in \u0026locks[0..i] {\n\t\t\t\t\t\t// safety: this lock was already acquired\n\t\t\t\t\t\tlock.raw_unlock_read();\n\t\t\t\t\t}\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttrue\n\t\t},\n\t\t||\n\t\t// safety: everything in locked is locked\n\t\tattempt_to_recover_reads_from_panic(\u0026locks[0..locked.get()]),\n\t)\n}\n\npub fn scoped_write\u003c'a, L: RawLock + Lockable + ?Sized, R\u003e(\n\tcollection: \u0026'a L,\n\tkey: impl Keyable,\n\tf: impl FnOnce(L::DataMut\u003c'a\u003e) -\u003e R,\n) -\u003e R {\n\tunsafe {\n\t\t// safety: we have the key\n\t\tcollection.raw_write();\n\n\t\t// safety: we just locked this\n\t\tlet r = handle_unwind(\n\t\t\t|| f(collection.data_mut()),\n\t\t\t|| collection.raw_unlock_write(),\n\t\t);\n\n\t\t// this ensures the key is held long enough\n\t\tdrop(key);\n\n\t\t// safety: we've locked already, and aren't using the data again\n\t\tcollection.raw_unlock_write();\n\n\t\tr\n\t}\n}\n\npub fn scoped_try_write\u003c'a, L: RawLock + Lockable + ?Sized, Key: Keyable, R\u003e(\n\tcollection: \u0026'a L,\n\tkey: Key,\n\tf: impl FnOnce(L::DataMut\u003c'a\u003e) -\u003e R,\n) -\u003e Result\u003cR, Key\u003e {\n\tunsafe {\n\t\t// safety: we have the key\n\t\tif !collection.raw_try_write() {\n\t\t\treturn Err(key);\n\t\t}\n\n\t\t// safety: we just locked this\n\t\tlet r = handle_unwind(\n\t\t\t|| f(collection.data_mut()),\n\t\t\t|| collection.raw_unlock_write(),\n\t\t);\n\n\t\t// this ensures the key is held long enough\n\t\tdrop(key);\n\n\t\t// safety: we've locked already, and aren't using the data again\n\t\tcollection.raw_unlock_write();\n\n\t\tOk(r)\n\t}\n}\n\npub fn scoped_read\u003c'a, L: RawLock + Sharable + ?Sized, R\u003e(\n\tcollection: \u0026'a L,\n\tkey: impl Keyable,\n\tf: impl FnOnce(L::DataRef\u003c'a\u003e) -\u003e R,\n) -\u003e R {\n\tunsafe {\n\t\t// safety: we have the key\n\t\tcollection.raw_read();\n\n\t\t// safety: we just locked this\n\t\tlet r = handle_unwind(|| f(collection.data_ref()), || collection.raw_unlock_read());\n\n\t\t// this ensures the key is held long enough\n\t\tdrop(key);\n\n\t\t// safety: we've locked already, and aren't using the data again\n\t\tcollection.raw_unlock_read();\n\n\t\tr\n\t}\n}\n\npub fn scoped_try_read\u003c'a, L: RawLock + Sharable + ?Sized, Key: Keyable, R\u003e(\n\tcollection: \u0026'a L,\n\tkey: Key,\n\tf: impl FnOnce(L::DataRef\u003c'a\u003e) -\u003e R,\n) -\u003e Result\u003cR, Key\u003e {\n\tunsafe {\n\t\t// safety: we have the key\n\t\tif !collection.raw_try_read() {\n\t\t\treturn Err(key);\n\t\t}\n\n\t\t// safety: we just locked this\n\t\tlet r = handle_unwind(|| f(collection.data_ref()), || collection.raw_unlock_read());\n\n\t\t// this ensures the key is held long enough\n\t\tdrop(key);\n\n\t\t// safety: we've locked already, and aren't using the data again\n\t\tcollection.raw_unlock_read();\n\n\t\tOk(r)\n\t}\n}\n\n/// Unlocks the already locked locks in order to recover from a panic\npub unsafe fn attempt_to_recover_writes_from_panic(locks: \u0026[\u0026dyn RawLock]) {\n\thandle_unwind(\n\t\t|| {\n\t\t\t// safety: the caller assumes that these are already locked\n\t\t\tfor lock in locks {\n\t\t\t\tlock.raw_unlock_write();\n\t\t\t}\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\tfor lock in locked {\n\t\t\t\tlock.raw_unlock_read();\n\t\t\t}\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":10,"address":[1631314,1630834,1630514,1630368,1631154,1631328,1630188,1631148,1631008,1630508,1631168,1630348,1630048,1630208,1630668,1630848,1631468,1631308,1630828,1630988,1630194,1631474,1630688,1630528,1630994,1630354,1630674],"length":1,"stats":{"Line":9}},{"line":11,"address":[1630236,1631036,1630396,1630876,1630076,1631196,1631356,1630556,1630716],"length":1,"stats":{"Line":9}},{"line":12,"address":[],"length":0,"stats":{"Line":36}},{"line":13,"address":[],"length":0,"stats":{"Line":9}},{"line":19,"address":[647773,647648,647779],"length":1,"stats":{"Line":28}},{"line":20,"address":[635458],"length":1,"stats":{"Line":28}},{"line":21,"address":[629025,629169],"length":1,"stats":{"Line":28}},{"line":22,"address":[],"length":0,"stats":{"Line":28}},{"line":27,"address":[997872],"length":1,"stats":{"Line":8}},{"line":28,"address":[948120],"length":1,"stats":{"Line":8}},{"line":30,"address":[973158],"length":1,"stats":{"Line":1}},{"line":33,"address":[937884],"length":1,"stats":{"Line":8}},{"line":35,"address":[981008,981037],"length":1,"stats":{"Line":25}},{"line":39,"address":[1194160],"length":1,"stats":{"Line":4}},{"line":41,"address":[980590],"length":1,"stats":{"Line":5}},{"line":44,"address":[987920],"length":1,"stats":{"Line":7}},{"line":45,"address":[1004941,1004918],"length":1,"stats":{"Line":14}},{"line":46,"address":[1000243],"length":1,"stats":{"Line":7}},{"line":47,"address":[945021,944978],"length":1,"stats":{"Line":10}},{"line":50,"address":[1194189],"length":1,"stats":{"Line":13}},{"line":55,"address":[1194064],"length":1,"stats":{"Line":2}},{"line":56,"address":[980494],"length":1,"stats":{"Line":2}},{"line":59,"address":[979824],"length":1,"stats":{"Line":2}},{"line":60,"address":[987670,987693],"length":1,"stats":{"Line":4}},{"line":61,"address":[1004723],"length":1,"stats":{"Line":2}},{"line":62,"address":[979981,979938],"length":1,"stats":{"Line":4}},{"line":65,"address":[1194093],"length":1,"stats":{"Line":4}},{"line":72,"address":[947984],"length":1,"stats":{"Line":2}},{"line":73,"address":[972983],"length":1,"stats":{"Line":2}},{"line":76,"address":[937775],"length":1,"stats":{"Line":5}},{"line":77,"address":[1000959],"length":1,"stats":{"Line":3}},{"line":79,"address":[1005873],"length":1,"stats":{"Line":3}},{"line":80,"address":[989001,989143],"length":1,"stats":{"Line":4}},{"line":82,"address":[1005918,1006018],"length":1,"stats":{"Line":2}},{"line":84,"address":[980871],"length":1,"stats":{"Line":1}},{"line":86,"address":[1006099],"length":1,"stats":{"Line":1}},{"line":90,"address":[981089],"length":1,"stats":{"Line":1}},{"line":92,"address":[946112],"length":1,"stats":{"Line":4}},{"line":94,"address":[1637028],"length":1,"stats":{"Line":1}},{"line":100,"address":[937616],"length":1,"stats":{"Line":2}},{"line":102,"address":[937639],"length":1,"stats":{"Line":2}},{"line":105,"address":[979952],"length":1,"stats":{"Line":4}},{"line":106,"address":[985967],"length":1,"stats":{"Line":2}},{"line":108,"address":[945313],"length":1,"stats":{"Line":2}},{"line":109,"address":[980375,980233],"length":1,"stats":{"Line":6}},{"line":111,"address":[1005490,1005390],"length":1,"stats":{"Line":2}},{"line":113,"address":[980343],"length":1,"stats":{"Line":1}},{"line":115,"address":[1636444],"length":1,"stats":{"Line":1}},{"line":119,"address":[980561],"length":1,"stats":{"Line":1}},{"line":121,"address":[947939],"length":1,"stats":{"Line":3}},{"line":123,"address":[955844],"length":1,"stats":{"Line":1}},{"line":127,"address":[1623727,1624431,1624992,1623920,1623200,1623375,1625488,1624079,1624607,1625510,1624800,1625151,1623551,1623392,1625168,1624272,1624448,1624624,1624096,1624255,1624783,1623744,1624975,1625344,1623903,1625327,1623568],"length":1,"stats":{"Line":14}},{"line":134,"address":[],"length":0,"stats":{"Line":14}},{"line":138,"address":[1624353,1633376,1623649,1633810,1632770,1632873,1625073,1624177,1624001,1633028,1632608,1634044,1623473,1632736,1633120,1632864,1633248,1632992,1625249,1633257,1632748,1624881,1623825,1634066,1633385,1633904,1632476,1633776,1633129,1633666,1633644,1632642,1633916,1633001,1633788,1633938,1624529,1633504,1633540,1633513,1624705,1632464,1633632,1632620,1633156,1625415,1632900,1623281,1634032,1633412,1632498,1633284],"length":1,"stats":{"Line":42}},{"line":139,"address":[],"length":0,"stats":{"Line":0}},{"line":143,"address":[],"length":0,"stats":{"Line":14}},{"line":146,"address":[628696],"length":1,"stats":{"Line":14}},{"line":148,"address":[],"length":0,"stats":{"Line":0}},{"line":152,"address":[628736,628949],"length":1,"stats":{"Line":5}},{"line":159,"address":[],"length":0,"stats":{"Line":10}},{"line":160,"address":[1627172,1626948,1626724,1626500],"length":1,"stats":{"Line":5}},{"line":165,"address":[629420,629408,629442,628855],"length":1,"stats":{"Line":0}},{"line":166,"address":[1635040,1635173,1634912,1634784,1634789,1635045,1634917,1635168],"length":1,"stats":{"Line":0}},{"line":170,"address":[1626543,1626991,1626767,1627215],"length":1,"stats":{"Line":0}},{"line":173,"address":[],"length":0,"stats":{"Line":0}},{"line":175,"address":[1626585,1627257,1626809,1627033],"length":1,"stats":{"Line":0}},{"line":179,"address":[1622431,1622799,1621888,1623008,1622272,1622607,1622448,1622255,1623183,1622624,1622080,1622063,1622991,1622816],"length":1,"stats":{"Line":7}},{"line":186,"address":[],"length":0,"stats":{"Line":7}},{"line":189,"address":[1631760,1631904,1622529,1632354,1631616,1632309,1631666,1631893,1632016,1622705,1631812,1622161,1622897,1621969,1632304,1631765,1631913,1632044,1632210,1631785,1632176,1631776,1632320,1632021,1623089,1631621,1631644,1632160,1622353,1631522,1632165,1631888,1631940,1631500,1632188,1632453,1632448,1632032,1631632,1632332,1631488,1632066],"length":1,"stats":{"Line":21}},{"line":192,"address":[],"length":0,"stats":{"Line":7}},{"line":195,"address":[],"length":0,"stats":{"Line":7}},{"line":197,"address":[],"length":0,"stats":{"Line":0}},{"line":201,"address":[1625520,1626401,1625931,1625707,1625729,1625953,1626155,1626379,1625968,1626192,1625744,1626177],"length":1,"stats":{"Line":4}},{"line":208,"address":[1625982,1626046,1625822,1625534,1626206,1625598,1625758,1626270],"length":1,"stats":{"Line":8}},{"line":209,"address":[],"length":0,"stats":{"Line":4}},{"line":213,"address":[1626069,1634405,1634528,1634400,1625845,1634578,1634288,1634300,1634544,1625621,1634556,1634661,1634450,1634428,1634322,1634533,1634172,1634272,1634277,1634656,1626293,1634160,1634416,1634194],"length":1,"stats":{"Line":0}},{"line":216,"address":[],"length":0,"stats":{"Line":0}},{"line":219,"address":[],"length":0,"stats":{"Line":0}},{"line":221,"address":[],"length":0,"stats":{"Line":0}},{"line":226,"address":[998032],"length":1,"stats":{"Line":6}},{"line":228,"address":[1006544],"length":1,"stats":{"Line":6}},{"line":230,"address":[1001826,1001804],"length":1,"stats":{"Line":12}},{"line":231,"address":[987414],"length":1,"stats":{"Line":5}},{"line":235,"address":[981440,979376,979390,981454],"length":1,"stats":{"Line":10}},{"line":240,"address":[993232],"length":1,"stats":{"Line":5}},{"line":242,"address":[1637264],"length":1,"stats":{"Line":5}},{"line":244,"address":[946364,946386],"length":1,"stats":{"Line":10}},{"line":245,"address":[1006470],"length":1,"stats":{"Line":4}},{"line":249,"address":[948222],"length":1,"stats":{"Line":9}}],"covered":77,"coverable":89},{"path":["/","home","botahamec","Projects","happylock","src","collection.rs"],"content":"use std::cell::UnsafeCell;\n\nuse crate::{lockable::RawLock, ThreadKey};\n\nmod boxed;\nmod guard;\nmod owned;\nmod r#ref;\nmod retry;\npub(crate) mod utils;\n\n/// Locks a collection of locks, which cannot be shared immutably.\n///\n/// This could be a tuple of [`Lockable`] types, an array, or a `Vec`. But it\n/// can be safely locked without causing a deadlock.\n///\n/// The data in this collection is guaranteed to not contain duplicates because\n/// `L` must always implement [`OwnedLockable`]. The underlying data may not be\n/// immutably referenced. Because of this, there is no need for sorting the\n/// locks in the collection, or checking for duplicates, because it can be\n/// guaranteed that until the underlying collection is mutated (which requires\n/// releasing all acquired locks in the collection to do), then the locks will\n/// stay in the same order and be locked in that order, preventing 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\tchild: 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 cached\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\tchild: \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 cached\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\tchild: *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 is not 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 cyclic wait is ensured manually. This will prevent the need for\n/// subsequent unlocking and re-locking.\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\tchild: L,\n}\n\n/// A RAII guard for a generic [`Lockable`] type. When this structure is\n/// dropped (falls out of scope), the locks will be unlocked.\n///\n/// The data protected by the mutex can be accessed through this guard via its\n/// [`Deref`] and [`DerefMut`] implementations.\n///\n/// Several lock collections can be used to create this type. Specifically,\n/// [`BoxedLockCollection`], [`RefLockCollection`], [`OwnedLockCollection`], and\n/// [`RetryingLockCollection`]. It is created using the methods, `lock`,\n/// `try_lock`, `read`, and `try_read`.\n///\n/// [`Deref`]: `std::ops::Deref`\n/// [`DerefMut`]: `std::ops::DerefMut`\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","context","context.rs"],"content":"use std::marker::PhantomData;\n\nuse crate::{\n\tcontext::{LockContext, LockingIterator, LockingTuple},\n\tlockable::{Lockable, OwnedLockable},\n\tThreadKey,\n};\n\nimpl\u003c'l, L\u003e LockContext\u003c'l, L\u003e {\n\tpub(crate) const fn new(lockable: \u0026'l L) -\u003e Self\n\twhere\n\t\tL: OwnedLockable,\n\t{\n\t\tSelf {\n\t\t\tkey: None,\n\t\t\tlockable,\n\t\t}\n\t}\n\n\t/// Unlocks all locks in the collection, returning the [`ThreadKey`].\n\t///\n\t/// This requires a mutable reference to the context, so it cannot be called\n\t/// without first dropping any [`ContextGuard`]s that reference this context.\n\t/// This method will also return `None` if the context has not been locked\n\t/// with a `ThreadKey`.\n\t///\n\t/// # Example\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(42), Mutex::new(true));\n\t/// let locks = OwnedLockCollection::new(data);\n\t/// let mut ctx = locks.context();\n\t/// let tuple = ctx.tuple(key);\n\t///\n\t/// let (use_other, tuple) = tuple.lock_1();\n\t/// if **use_other {\n\t/// drop(use_other);\n\t/// drop(tuple);\n\t/// let key = ctx.unlock().unwrap();\n\t/// let tuple = ctx.tuple(key);\n\t/// let (mut item, _) = tuple.lock_0();\n\t/// **item = 67;\n\t/// } else {\n\t/// drop(use_other);\n\t/// drop(tuple);\n\t/// };\n\t///\n\t/// let key = ctx.unlock().unwrap();\n\t/// let tuple = ctx.tuple(key);\n\t/// let (number, _) = tuple.lock_0();\n\t/// assert_eq!(**number, 67);\n\t/// ```\n\t///\n\t/// [`ContextGuard`]: `crate::context::ContextGuard`\n\tpub fn unlock(\u0026mut self) -\u003e Option\u003cThreadKey\u003e {\n\t\tself.key.take()\n\t}\n}\n\nimpl\u003cL: Lockable\u003e LockContext\u003c'_, L\u003e {\n\t/// Creates a [`LockingTuple`], which can lock a subset of a tuple of locks,\n\t/// in a specific order.\n\t///\n\t/// Sometimes, partial allocation of locks is useful. For example, you may want\n\t/// to acquire a lock on one item before deciding if the second item should be\n\t/// locked. If the locks can be organized into a tuple, [`LockingTuple`] is\n\t/// capable of doing exactly that.\n\t///\n\t/// # Example\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(true), Mutex::new(42), Mutex::new(67));\n\t/// let locks = OwnedLockCollection::new(data);\n\t/// let mut ctx = locks.context();\n\t/// let tuple = ctx.tuple(key);\n\t///\n\t/// let (use_other, tuple) = tuple.lock_0();\n\t/// let number = if **use_other {\n\t/// tuple.lock_2().0\n\t/// } else {\n\t/// tuple.lock_1().0\n\t/// };\n\t/// assert_eq!(**number, 67);\n\t/// ```\n\tpub fn tuple(\u0026mut self, key: ThreadKey) -\u003e LockingTuple\u003c'_, L, L\u003e {\n\t\tunsafe {\n\t\t\tself.key = Some(key);\n\n\t\t\tLockingTuple {\n\t\t\t\t_lockable: PhantomData,\n\t\t\t\t// safety: we just inserted a key\n\t\t\t\tkey: self.key.as_ref().unwrap_unchecked(),\n\t\t\t\ttuple: self.lockable,\n\t\t\t\touter: (),\n\t\t\t}\n\t\t}\n\t}\n}\n\nimpl\u003c'l, L\u003e LockContext\u003c'l, L\u003e\nwhere\n\t\u0026'l L: IntoIterator,\n{\n\t/// Creates a [`LockingIterator`] to iterate through a collection of locks\n\t/// without locking everything at once.\n\t///\n\t/// Sometimes, partial allocation of locks is useful. For example, you may\n\t/// want to acquire a lock on the first element of a list before deciding if\n\t/// the second element should be locked. If the list is iterable, then a\n\t/// [`LockingIterator`] is capable of doing exactly that.\n\t///\n\t/// # Example\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(1), Mutex::new(3), Mutex::new(8)];\n\t/// let locks = OwnedLockCollection::new(data);\n\t/// let mut ctx = locks.context();\n\t/// let mut iter = ctx.iter(key);\n\t///\n\t/// let mut sum = 0;\n\t/// while let Some(item) = iter.lock_next() {\n\t/// sum += **item;\n\t/// }\n\t///\n\t/// assert_eq!(sum, 12);\n\t/// ```\n\t// TODO: support scoped locks\n\t// TODO: implement get_disjoint\n\t// TODO: support some sort of index tower thing\n\t#[expect(clippy::iter_not_returning_iterator)]\n\tpub fn iter(\n\t\t\u0026mut self,\n\t\tkey: ThreadKey,\n\t) -\u003e LockingIterator\u003c'_, \u003c\u0026'l L as IntoIterator\u003e::IntoIter\u003e {\n\t\tunsafe {\n\t\t\tself.key = Some(key);\n\n\t\t\tLockingIterator {\n\t\t\t\t// safety: we just inserted a key\n\t\t\t\tkey: self.key.as_ref().unwrap_unchecked(),\n\t\t\t\titerator: self.lockable.into_iter(),\n\t\t\t\touter: (),\n\t\t\t}\n\t\t}\n\t}\n}\n","traces":[{"line":10,"address":[899888,899904,899760,899808,899856,899776,899840,899872,899792,899824],"length":1,"stats":{"Line":11}},{"line":59,"address":[],"length":0,"stats":{"Line":0}},{"line":60,"address":[],"length":0,"stats":{"Line":0}},{"line":93,"address":[],"length":0,"stats":{"Line":5}},{"line":95,"address":[],"length":0,"stats":{"Line":10}},{"line":100,"address":[],"length":0,"stats":{"Line":5}},{"line":101,"address":[],"length":0,"stats":{"Line":5}},{"line":102,"address":[],"length":0,"stats":{"Line":0}},{"line":143,"address":[900548,900848,901024,900724,900900,900372,900320,900672,900496,901076],"length":1,"stats":{"Line":7}},{"line":148,"address":[],"length":0,"stats":{"Line":16}},{"line":152,"address":[],"length":0,"stats":{"Line":8}},{"line":153,"address":[],"length":0,"stats":{"Line":8}},{"line":154,"address":[],"length":0,"stats":{"Line":0}}],"covered":9,"coverable":13},{"path":["/","home","botahamec","Projects","happylock","src","context","guard.rs"],"content":"use std::fmt::{Debug, Display};\nuse std::hash::Hash;\nuse std::ops::{Deref, DerefMut};\n\nuse super::ContextGuard;\n\n#[mutants::skip] // hashing involves RNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard: Hash, Key\u003e Hash for ContextGuard\u003c'_, Guard, Key\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.guard.hash(state)\n\t}\n}\n\n// No implementations of Eq, PartialEq, PartialOrd, or Ord\n// You can't implement both PartialEq\u003cSelf\u003e and PartialEq\u003cT\u003e\n// It's easier to just implement neither and ask users to dereference\n// This is less of a problem when using the scoped lock API\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard: Debug, Key\u003e Debug for ContextGuard\u003c'_, Guard, Key\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, Key\u003e Display for ContextGuard\u003c'_, Guard, Key\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, Key\u003e Deref for ContextGuard\u003c'_, Guard, Key\u003e {\n\ttype Target = Guard;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t\u0026self.guard\n\t}\n}\n\nimpl\u003cGuard, Key\u003e DerefMut for ContextGuard\u003c'_, Guard, Key\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t\u0026mut self.guard\n\t}\n}\n\nimpl\u003cGuard, Key\u003e AsRef\u003cGuard\u003e for ContextGuard\u003c'_, Guard, Key\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026Guard {\n\t\t\u0026self.guard\n\t}\n}\n\nimpl\u003cGuard, Key\u003e AsMut\u003cGuard\u003e for ContextGuard\u003c'_, Guard, Key\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut Guard {\n\t\t\u0026mut self.guard\n\t}\n}\n","traces":[{"line":29,"address":[],"length":0,"stats":{"Line":1}},{"line":30,"address":[],"length":0,"stats":{"Line":1}},{"line":37,"address":[],"length":0,"stats":{"Line":4}},{"line":38,"address":[],"length":0,"stats":{"Line":6}},{"line":43,"address":[],"length":0,"stats":{"Line":1}},{"line":44,"address":[],"length":0,"stats":{"Line":1}},{"line":49,"address":[],"length":0,"stats":{"Line":0}},{"line":50,"address":[],"length":0,"stats":{"Line":0}},{"line":55,"address":[],"length":0,"stats":{"Line":0}},{"line":56,"address":[],"length":0,"stats":{"Line":0}}],"covered":6,"coverable":10},{"path":["/","home","botahamec","Projects","happylock","src","context","iterator.rs"],"content":"use std::{\n\titer::{Fuse, Peekable, Skip, Take},\n\tmarker::PhantomData,\n};\n\nuse super::{ContextGuard, LockingIterator};\n\nuse crate::{\n\tcontext::LockingTuple,\n\tlockable::{Lockable, RawLock, Sharable},\n\tThreadKey,\n};\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum TryLockNextError {\n\tFinishedIteration,\n\tWouldBlock,\n}\n\nimpl\u003c'l, I, O\u003e LockingIterator\u003c'l, I, O\u003e {\n\tfn with_iterator\u003cM\u003e(self, f: impl FnOnce(I) -\u003e M) -\u003e LockingIterator\u003c'l, M, O\u003e {\n\t\tLockingIterator {\n\t\t\tkey: self.key,\n\t\t\titerator: f(self.iterator),\n\t\t\touter: self.outer,\n\t\t}\n\t}\n\n\t/// Exit out of the current scope of the locking iterator into the parent.\n\t///\n\t/// After using one the recurse methods, it is possible to regain access to\n\t/// the parent by exiting out of the scope of the child. Doing this will make\n\t/// it impossible to re-enter this scope again.\n\t///\n\t/// # Example\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(1), Mutex::new(2), Mutex::new(3)], Mutex::new(true));\n\t/// let locks = OwnedLockCollection::new(data);\n\t/// let mut ctx = locks.context();\n\t/// let tuple = ctx.tuple(key);\n\t/// let mut iter = tuple.recurse_0_iter();\n\t///\n\t/// let mut sum = 0;\n\t/// while let Some(item) = iter.lock_next() {\n\t/// sum += **item;\n\t/// }\n\t///\n\t/// let tuple = iter.exit();\n\t/// let (should_assert, _) = tuple.lock_1();\n\t/// if **should_assert {\n\t/// assert_eq!(sum, 6);\n\t/// }\n\t/// ```\n\tpub fn exit(self) -\u003e O {\n\t\tself.outer\n\t}\n}\n\nimpl\u003c'c, L: Iterator\u003cItem = I\u003e, I: IntoIterator, O\u003e LockingIterator\u003c'c, L, O\u003e {\n\t/// Create a new `LockingIterator` based on the next element in the iterator.\n\t///\n\t/// If a list contains a list of locks, then this method can be used to\n\t/// recurse into the next element of the list. To go back to the parent scope,\n\t/// use [`LockingIterator::exit`] on the new list.\n\t///\n\t/// # Example\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 = [\n\t/// [Mutex::new(1), Mutex::new(2), Mutex::new(3)],\n\t/// [Mutex::new(4), Mutex::new(5), Mutex::new(6)],\n\t/// ];\n\t/// let locks = OwnedLockCollection::new(data);\n\t/// let mut ctx = locks.context();\n\t/// let mut iter = ctx.iter(key);\n\t///\n\t/// let mut sums = Vec::new();\n\t/// while let Some(mut list) = iter.recurse_next() {\n\t/// let mut sum = 0;\n\t/// while let Some(item) = list.lock_next() {\n\t/// sum += **item;\n\t/// }\n\t/// sums.push(sum);\n\t/// iter = list.exit();\n\t/// }\n\t///\n\t/// assert_eq!(sums, vec![6, 15]);\n\t/// ```\n\tpub fn recurse_next(\n\t\tmut self,\n\t) -\u003e Option\u003cLockingIterator\u003c'c, \u003cI as IntoIterator\u003e::IntoIter, Self\u003e\u003e {\n\t\tif let Some(iterator) = self.iterator.next() {\n\t\t\tSome(LockingIterator {\n\t\t\t\tkey: self.key,\n\t\t\t\titerator: iterator.into_iter(),\n\t\t\t\touter: self,\n\t\t\t})\n\t\t} else {\n\t\t\tNone\n\t\t}\n\t}\n\n\t/// Create a new `LockingIterator` based on the next element in the iterator.\n\t///\n\t/// If a list contains a list of locks, then this method can be used to\n\t/// recurse into the next element of the list. To go back to the parent scope,\n\t/// use [`LockingIterator::exit`] on the new list.\n\t///\n\t/// # Example\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 = [\n\t/// [Mutex::new(1), Mutex::new(2), Mutex::new(3)],\n\t/// [Mutex::new(4), Mutex::new(5), Mutex::new(6)],\n\t/// ];\n\t/// let locks = OwnedLockCollection::new(data);\n\t/// let mut ctx = locks.context();\n\t/// let iter = ctx.iter(key);\n\t///\n\t/// let mut list = iter.recurse_last().unwrap();\n\t/// let mut sum = 0;\n\t/// while let Some(item) = list.lock_next() {\n\t/// sum += **item;\n\t/// }\n\t///\n\t/// assert_eq!(sum, 15);\n\t/// ```\n\tpub fn recurse_last(self) -\u003e Option\u003cLockingIterator\u003c'c, \u003cI as IntoIterator\u003e::IntoIter, O\u003e\u003e {\n\t\tif let Some(iterator) = self.iterator.last() {\n\t\t\tSome(LockingIterator {\n\t\t\t\tkey: self.key,\n\t\t\t\titerator: iterator.into_iter(),\n\t\t\t\touter: self.outer,\n\t\t\t})\n\t\t} else {\n\t\t\tNone\n\t\t}\n\t}\n}\n\nimpl\u003c'c, L: Iterator\u003cItem = \u0026'c T\u003e, T: 'c, O\u003e LockingIterator\u003c'c, L, O\u003e {\n\t/// Create a new `LockingIterator` based on the next element in the iterator.\n\t///\n\t/// If a list contains a list of locks, then this method can be used to\n\t/// recurse into the next element of the list. To go back to the parent scope,\n\t/// use [`LockingIterator::exit`] on the new list.\n\t///\n\t/// # Example\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 = [\n\t/// (Mutex::new(true), Mutex::new(1)),\n\t/// (Mutex::new(false), Mutex::new(2)),\n\t/// (Mutex::new(true), Mutex::new(3)),\n\t/// ];\n\t/// let locks = OwnedLockCollection::new(data);\n\t/// let mut ctx = locks.context();\n\t/// let mut iter = ctx.iter(key);\n\t///\n\t/// let mut sum = 0;\n\t/// while let Some(tuple) = iter.recurse_next_tuple() {\n\t/// let (should_count, mut tuple) = tuple.lock_0();\n\t/// if **should_count {\n\t/// let num = tuple.lock_mut_1();\n\t/// sum += **num;\n\t/// }\n\t/// iter = tuple.exit();\n\t/// }\n\t///\n\t/// assert_eq!(sum, 4);\n\t/// ```\n\tpub fn recurse_next_tuple(mut self) -\u003e Option\u003cLockingTuple\u003c'c, T, T, Self\u003e\u003e {\n\t\tif let Some(tuple) = self.iterator.next() {\n\t\t\tSome(LockingTuple {\n\t\t\t\tkey: self.key,\n\t\t\t\t_lockable: PhantomData,\n\t\t\t\ttuple,\n\t\t\t\touter: self,\n\t\t\t})\n\t\t} else {\n\t\t\tNone\n\t\t}\n\t}\n\n\t/// Create a new `LockingIterator` based on the next element in the iterator.\n\t///\n\t/// If a list contains a list of locks, then this method can be used to\n\t/// recurse into the next element of the list. To go back to the parent scope,\n\t/// use [`LockingIterator::exit`] on the new list.\n\t///\n\t/// # Example\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 = [\n\t/// (Mutex::new(true), Mutex::new(1)),\n\t/// (Mutex::new(false), Mutex::new(2)),\n\t/// (Mutex::new(true), Mutex::new(3)),\n\t/// ];\n\t/// let locks = OwnedLockCollection::new(data);\n\t/// let mut ctx = locks.context();\n\t/// let iter = ctx.iter(key);\n\t///\n\t/// let tuple = iter.recurse_last_tuple().unwrap();\n\t/// let (should_count, mut tuple) = tuple.lock_0();\n\t/// if **should_count {\n\t/// let num = tuple.lock_mut_1();\n\t/// assert_eq!(**num, 3);\n\t/// } else {\n\t/// panic!();\n\t/// }\n\t/// ```\n\tpub fn recurse_last_tuple(self) -\u003e Option\u003cLockingTuple\u003c'c, T, T, O\u003e\u003e {\n\t\tif let Some(tuple) = self.iterator.last() {\n\t\t\tSome(LockingTuple {\n\t\t\t\tkey: self.key,\n\t\t\t\t_lockable: PhantomData,\n\t\t\t\ttuple,\n\t\t\t\touter: self.outer,\n\t\t\t})\n\t\t} else {\n\t\t\tNone\n\t\t}\n\t}\n}\n\nimpl\u003c'c, L: 'c + Iterator\u003cItem = \u0026'c I\u003e, I: 'c + RawLock + Lockable, O\u003e LockingIterator\u003c'c, L, O\u003e {\n\t/// Advances the iterator, locking the next element and returning a guard to\n\t/// the inner data.\n\t///\n\t/// Returns `None` when iteration is finished. Individual iterator\n\t/// implementations may choose to resume iteration, and so calling `next()`\n\t/// again may or may not eventually start returning `Some(Item)` again at some\n\t/// point.\n\t///\n\t/// # Example\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(1), Mutex::new(3), Mutex::new(8)];\n\t/// let locks = OwnedLockCollection::new(data);\n\t/// let mut ctx = locks.context();\n\t/// let mut iter = ctx.iter(key);\n\t///\n\t/// let mut sum = 0;\n\t/// while let Some(item) = iter.lock_next() {\n\t/// sum += **item;\n\t/// }\n\t///\n\t/// assert_eq!(sum, 12);\n\t/// ```\n\tpub fn lock_next(\u0026mut self) -\u003e Option\u003cContextGuard\u003c'c, \u003cI as Lockable\u003e::Guard\u003c'c\u003e, ThreadKey\u003e\u003e {\n\t\tif let Some(lock) = self.iterator.next() {\n\t\t\tunsafe {\n\t\t\t\tlock.raw_write();\n\t\t\t\tlet guard = lock.guard();\n\n\t\t\t\tSome(ContextGuard {\n\t\t\t\t\t_key: self.key,\n\t\t\t\t\tguard,\n\t\t\t\t})\n\t\t\t}\n\t\t} else {\n\t\t\tNone\n\t\t}\n\t}\n\n\t/// Consumes the iterator, returning the last element, without locking any\n\t/// other elements.\n\t///\n\t/// This method will evaluate the iterator until it returns `None`. While\n\t/// doing so, it keeps track of the current element. After `None` is returned,\n\t/// `lock_last()` will then lock the last element it saw and return the\n\t/// lock's data.\n\t///\n\t/// # Panics\n\t///\n\t/// This function might panic if the iterator is infinite.\n\t///\n\t/// # Example\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(1), Mutex::new(3), Mutex::new(8)];\n\t/// let locks = OwnedLockCollection::new(data);\n\t/// let mut ctx = locks.context();\n\t/// let iter = ctx.iter(key);\n\t///\n\t/// let last = iter.lock_last().unwrap();\n\t/// assert_eq!(**last, 8);\n\t/// ```\n\tpub fn lock_last(self) -\u003e Option\u003cContextGuard\u003c'c, \u003cI as Lockable\u003e::Guard\u003c'c\u003e, ThreadKey\u003e\u003e {\n\t\tself.iterator.last().map(|lock| unsafe {\n\t\t\tlock.raw_write();\n\t\t\tlet guard = lock.guard();\n\n\t\t\tContextGuard {\n\t\t\t\t_key: self.key,\n\t\t\t\tguard,\n\t\t\t}\n\t\t})\n\t}\n}\n\nimpl\u003c'c, L: 'c + Iterator\u003cItem = \u0026'c I\u003e, I: 'c + RawLock + Lockable, O\u003e\n\tLockingIterator\u003c'c, Peekable\u003cL\u003e, O\u003e\n{\n\t/// Attempts to lock the next element and returning a guard to\n\t/// the inner data.\n\t///\n\t/// # Errors\n\t///\n\t/// Returns `Err(TryLockNextError::FinishedIteration)` when iteration is\n\t/// finished. Individual iterator implementations may choose to resume\n\t/// iteration, and so calling `next()` again may or may not eventually start\n\t/// returning `Some(Item)` again at some point.\n\t///\n\t/// Returns `Err(TryLockNextError::WouldBlock)` if the next lock in the\n\t/// iterator is already locked. This will not advance the iterator.\n\t///\n\t/// # Example\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t/// use happylock::context::iterator::TryLockNextError;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = [Mutex::new(1), Mutex::new(3), Mutex::new(8)];\n\t/// let locks = OwnedLockCollection::new(data);\n\t/// let mut ctx = locks.context();\n\t/// let mut iter = ctx.iter(key).peekable();\n\t///\n\t/// let mut sum = 0;\n\t/// loop {\n\t/// match iter.try_lock_next() {\n\t/// Ok(item) =\u003e sum += **item,\n\t/// Err(TryLockNextError::WouldBlock) =\u003e continue,\n\t/// Err(TryLockNextError::FinishedIteration) =\u003e break,\n\t/// }\n\t/// }\n\t///\n\t/// assert_eq!(sum, 12);\n\t/// ```\n\tpub fn try_lock_next(\n\t\t\u0026mut self,\n\t) -\u003e Result\u003cContextGuard\u003c'c, \u003cI as Lockable\u003e::Guard\u003c'c\u003e, ThreadKey\u003e, TryLockNextError\u003e {\n\t\tif let Some(lock) = self.iterator.peek().copied() {\n\t\t\tunsafe {\n\t\t\t\tif lock.raw_try_write() {\n\t\t\t\t\t// safety: we just saw that there is a valid value\n\t\t\t\t\tlet lock = self.iterator.next().unwrap_unchecked();\n\t\t\t\t\tlet guard = lock.guard();\n\n\t\t\t\t\tOk(ContextGuard {\n\t\t\t\t\t\t_key: self.key,\n\t\t\t\t\t\tguard,\n\t\t\t\t\t})\n\t\t\t\t} else {\n\t\t\t\t\tErr(TryLockNextError::WouldBlock)\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tErr(TryLockNextError::FinishedIteration)\n\t\t}\n\t}\n}\n\nimpl\u003c'c, L: 'c + Iterator\u003cItem = \u0026'c I\u003e, I: 'c + RawLock + Sharable, O\u003e LockingIterator\u003c'c, L, O\u003e {\n\t/// Advances the iterator, acquiring a shared lock to the next element and\n\t/// returning a guard to the inner data.\n\t///\n\t/// Returns `None` when iteration is finished. Individual iterator\n\t/// implementations may choose to resume iteration, and so calling `next()`\n\t/// again may or may not eventually start returning `Some(Item)` again at some\n\t/// point.\n\t///\n\t/// # Example\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(1), RwLock::new(3), RwLock::new(8)];\n\t/// let locks = OwnedLockCollection::new(data);\n\t/// let mut ctx = locks.context();\n\t/// let mut iter = ctx.iter(key);\n\t///\n\t/// let mut sum = 0;\n\t/// while let Some(item) = iter.read_next() {\n\t/// sum += **item;\n\t/// }\n\t///\n\t/// assert_eq!(sum, 12);\n\t/// ```\n\tpub fn read_next(\n\t\t\u0026mut self,\n\t) -\u003e Option\u003cContextGuard\u003c'c, \u003cI as Sharable\u003e::ReadGuard\u003c'c\u003e, ThreadKey\u003e\u003e {\n\t\tif let Some(lock) = self.iterator.next() {\n\t\t\tunsafe {\n\t\t\t\tlock.raw_read();\n\t\t\t\tlet guard = lock.read_guard();\n\n\t\t\t\tSome(ContextGuard {\n\t\t\t\t\t_key: self.key,\n\t\t\t\t\tguard,\n\t\t\t\t})\n\t\t\t}\n\t\t} else {\n\t\t\tNone\n\t\t}\n\t}\n\n\t/// Consumes the iterator, returning the last element with readonly access,\n\t/// without locking any other elements.\n\t///\n\t/// This method will evaluate the iterator until it returns `None`. While\n\t/// doing so, it keeps track of the current element. After `None` is returned,\n\t/// `lock_last()` will then lock the last element it saw and return the\n\t/// lock's data.\n\t///\n\t/// # Panics\n\t///\n\t/// This function might panic if the iterator is infinite.\n\t///\n\t/// # Example\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(1), RwLock::new(3), RwLock::new(8)];\n\t/// let locks = OwnedLockCollection::new(data);\n\t/// let mut ctx = locks.context();\n\t/// let iter = ctx.iter(key);\n\t///\n\t/// let last = iter.read_last().unwrap();\n\t/// assert_eq!(**last, 8);\n\t/// ```\n\tpub fn read_last(self) -\u003e Option\u003cContextGuard\u003c'c, \u003cI as Sharable\u003e::ReadGuard\u003c'c\u003e, ThreadKey\u003e\u003e {\n\t\tself.iterator.last().map(|lock| unsafe {\n\t\t\tlock.raw_read();\n\t\t\tlet guard = lock.read_guard();\n\n\t\t\tContextGuard {\n\t\t\t\t_key: self.key,\n\t\t\t\tguard,\n\t\t\t}\n\t\t})\n\t}\n}\n\nimpl\u003c'c, L: 'c + Iterator\u003cItem = \u0026'c I\u003e, I: 'c + RawLock + Sharable, O\u003e\n\tLockingIterator\u003c'c, Peekable\u003cL\u003e, O\u003e\n{\n\t/// Attempts to acquire a shared lock the next element and returning a guard\n\t/// to the inner data.\n\t///\n\t/// # Errors\n\t///\n\t/// Returns `Err(TryLockNextError::FinishedIteration)` when iteration is\n\t/// finished. Individual iterator implementations may choose to resume\n\t/// iteration, and so calling `next()` again may or may not eventually start\n\t/// returning `Some(Item)` again at some point.\n\t///\n\t/// Returns `Err(TryLockNextError::WouldBlock)` if the next lock in the\n\t/// iterator is already locked. This will not advance the iterator.\n\t///\n\t/// # Example\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t/// use happylock::collection::OwnedLockCollection;\n\t/// use happylock::context::iterator::TryLockNextError;\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let data = [RwLock::new(1), RwLock::new(3), RwLock::new(8)];\n\t/// let locks = OwnedLockCollection::new(data);\n\t/// let mut ctx = locks.context();\n\t/// let mut iter = ctx.iter(key).peekable();\n\t///\n\t/// let mut sum = 0;\n\t/// loop {\n\t/// match iter.try_read_next() {\n\t/// Ok(item) =\u003e sum += **item,\n\t/// Err(TryLockNextError::WouldBlock) =\u003e continue,\n\t/// Err(TryLockNextError::FinishedIteration) =\u003e break,\n\t/// }\n\t/// }\n\t///\n\t/// assert_eq!(sum, 12);\n\t/// ```\n\tpub fn try_read_next(\n\t\t\u0026mut self,\n\t) -\u003e Result\u003cContextGuard\u003c'c, \u003cI as Sharable\u003e::ReadGuard\u003c'c\u003e, ThreadKey\u003e, TryLockNextError\u003e {\n\t\tif let Some(lock) = self.iterator.peek().copied() {\n\t\t\tunsafe {\n\t\t\t\tif lock.raw_try_read() {\n\t\t\t\t\t// safety: we just saw that there is a valid value\n\t\t\t\t\tlet lock = self.iterator.next().unwrap_unchecked();\n\t\t\t\t\tlet guard = lock.read_guard();\n\n\t\t\t\t\tOk(ContextGuard {\n\t\t\t\t\t\t_key: self.key,\n\t\t\t\t\t\tguard,\n\t\t\t\t\t})\n\t\t\t\t} else {\n\t\t\t\t\tErr(TryLockNextError::WouldBlock)\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tErr(TryLockNextError::FinishedIteration)\n\t\t}\n\t}\n}\n\nimpl\u003c'l, L: Iterator, O\u003e LockingIterator\u003c'l, L, O\u003e {\n\t/// Advances the iterator, without locking the next element in the iterator.\n\t///\n\t/// Returns `false` when iteration is finished. Individual iterator\n\t/// implementations may choose to resume iteration, and so calling\n\t/// `skip_next()` again may or may not eventually start returning `true` again\n\t/// at some point.\n\t///\n\t/// # Example\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(1), Mutex::new(3), Mutex::new(8)];\n\t/// let locks = OwnedLockCollection::new(data);\n\t/// let mut ctx = locks.context();\n\t/// let mut iter = ctx.iter(key);\n\t///\n\t/// iter.skip_next();\n\t/// assert!(iter.lock_next().is_some_and(|v| **v == 3));\n\t/// ```\n\tpub fn skip_next(\u0026mut self) -\u003e bool {\n\t\tself.iterator.next().is_some()\n\t}\n\n\t/// Advances the iterator, skipping `n` elements without locking.\n\t///\n\t/// See [`Iterator::skip`] for more information.\n\t///\n\t/// # Example\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(1), Mutex::new(3), Mutex::new(8)];\n\t/// let locks = OwnedLockCollection::new(data);\n\t/// let mut ctx = locks.context();\n\t/// let mut iter = ctx.iter(key);\n\t///\n\t/// iter.skip_mut(2);\n\t/// assert!(iter.lock_next().is_some_and(|v| **v == 8));\n\t/// ```\n\tpub fn skip_mut(\u0026mut self, n: usize) {\n\t\tfor _ in 0..n {\n\t\t\tself.iterator.next();\n\t\t}\n\t}\n\n\t/// Returns the bounds on the remaining length of the iterator.\n\t///\n\t/// See [`Iterator::size_hint`] for more information.\n\t///\n\t/// # Example\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(1), Mutex::new(2), Mutex::new(3)];\n\t/// let locks = OwnedLockCollection::new(data);\n\t/// let mut ctx = locks.context();\n\t/// let mut iter = ctx.iter(key);\n\t///\n\t/// assert_eq!((3, Some(3)), iter.size_hint());\n\t/// let _ = iter.skip_next();\n\t/// assert_eq!((2, Some(2)), iter.size_hint());\n\t/// ```\n\t#[must_use]\n\tpub fn size_hint(\u0026self) -\u003e (usize, Option\u003cusize\u003e) {\n\t\tself.iterator.size_hint()\n\t}\n\n\t/// Creates a new [`LockingIterator`] that skips the first `n` elements.\n\t///\n\t/// Unlike `skip_next` or `skip_mut`, this method does not modify the iterator\n\t/// in place. Instead, it returns a new iterator which skips the first `n`\n\t/// elements.\n\t///\n\t/// See [`Iterator::skip`] for more information.\n\t///\n\t/// # Example\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(1), Mutex::new(2), Mutex::new(3)];\n\t/// let locks = OwnedLockCollection::new(data);\n\t/// let mut ctx = locks.context();\n\t/// let mut iter = ctx.iter(key).skip(2);\n\t///\n\t/// assert!(iter.lock_next().is_some_and(|v| **v == 3));\n\t/// assert!(iter.lock_next().is_none());\n\t/// ```\n\t#[must_use]\n\tpub fn skip(self, n: usize) -\u003e LockingIterator\u003c'l, Skip\u003cL\u003e, O\u003e {\n\t\tself.with_iterator(|i| i.skip(n))\n\t}\n\n\t/// Creates a new [`LockingIterator`] that yields only the first `n` elements,\n\t/// or fewer if the iterator ends sooner.\n\t///\n\t/// See [`Iterator::take`] for more information.\n\t///\n\t/// # Example\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(1), Mutex::new(2), Mutex::new(3)];\n\t/// let locks = OwnedLockCollection::new(data);\n\t/// let mut ctx = locks.context();\n\t/// let mut iter = ctx.iter(key).take(2);\n\t///\n\t/// assert!(iter.lock_next().is_some_and(|v| **v == 1));\n\t/// assert!(iter.lock_next().is_some_and(|v| **v == 2));\n\t/// assert!(iter.lock_next().is_none());\n\t/// ```\n\t#[must_use]\n\tpub fn take(self, n: usize) -\u003e LockingIterator\u003c'l, Take\u003cL\u003e, O\u003e {\n\t\tself.with_iterator(|i| i.take(n))\n\t}\n\n\t/// Creates a new [`LockingIterator`] that ends after the first `None`\n\t///\n\t/// See [`Iterator::fuse`] for more information\n\t#[must_use]\n\tpub fn fuse(self) -\u003e LockingIterator\u003c'l, Fuse\u003cL\u003e, O\u003e {\n\t\tself.with_iterator(Iterator::fuse)\n\t}\n\n\t/// Creates a new [`LockingIterator`] which has access to the\n\t/// [`try_lock_next`] and/or [`try_read_next`] methods.\n\t///\n\t/// See [`Iterator::peekable`] for more information\n\t///\n\t/// [`try_lock_next`]: `LockingIterator::try_lock_next`\n\t/// [`try_read_next`]: `LockingIterator::try_read_next`\n\t#[must_use]\n\tpub fn peekable(self) -\u003e LockingIterator\u003c'l, Peekable\u003cL\u003e, O\u003e {\n\t\tself.with_iterator(Iterator::peekable)\n\t}\n}\n","traces":[{"line":21,"address":[],"length":0,"stats":{"Line":1}},{"line":23,"address":[899633],"length":1,"stats":{"Line":1}},{"line":24,"address":[],"length":0,"stats":{"Line":1}},{"line":25,"address":[],"length":0,"stats":{"Line":0}},{"line":59,"address":[],"length":0,"stats":{"Line":1}},{"line":60,"address":[],"length":0,"stats":{"Line":2}},{"line":98,"address":[905312,905658],"length":1,"stats":{"Line":1}},{"line":101,"address":[],"length":0,"stats":{"Line":3}},{"line":102,"address":[905570],"length":1,"stats":{"Line":1}},{"line":103,"address":[905471],"length":1,"stats":{"Line":1}},{"line":104,"address":[],"length":0,"stats":{"Line":1}},{"line":105,"address":[],"length":0,"stats":{"Line":1}},{"line":108,"address":[],"length":0,"stats":{"Line":1}},{"line":141,"address":[905024,905040,904768,905296],"length":1,"stats":{"Line":2}},{"line":142,"address":[],"length":0,"stats":{"Line":5}},{"line":143,"address":[904964,905236],"length":1,"stats":{"Line":1}},{"line":144,"address":[],"length":0,"stats":{"Line":1}},{"line":145,"address":[904909,905181],"length":1,"stats":{"Line":1}},{"line":146,"address":[905231,904959],"length":1,"stats":{"Line":1}},{"line":149,"address":[],"length":0,"stats":{"Line":1}},{"line":189,"address":[],"length":0,"stats":{"Line":1}},{"line":190,"address":[],"length":0,"stats":{"Line":3}},{"line":191,"address":[901550],"length":1,"stats":{"Line":1}},{"line":192,"address":[],"length":0,"stats":{"Line":1}},{"line":193,"address":[],"length":0,"stats":{"Line":0}},{"line":194,"address":[],"length":0,"stats":{"Line":0}},{"line":195,"address":[],"length":0,"stats":{"Line":1}},{"line":198,"address":[901612],"length":1,"stats":{"Line":1}},{"line":233,"address":[901200,901241],"length":1,"stats":{"Line":1}},{"line":234,"address":[],"length":0,"stats":{"Line":3}},{"line":235,"address":[],"length":0,"stats":{"Line":1}},{"line":236,"address":[],"length":0,"stats":{"Line":1}},{"line":237,"address":[],"length":0,"stats":{"Line":0}},{"line":238,"address":[],"length":0,"stats":{"Line":0}},{"line":239,"address":[],"length":0,"stats":{"Line":1}},{"line":242,"address":[],"length":0,"stats":{"Line":1}},{"line":275,"address":[],"length":0,"stats":{"Line":3}},{"line":276,"address":[],"length":0,"stats":{"Line":6}},{"line":278,"address":[],"length":0,"stats":{"Line":3}},{"line":279,"address":[],"length":0,"stats":{"Line":3}},{"line":281,"address":[],"length":0,"stats":{"Line":3}},{"line":282,"address":[],"length":0,"stats":{"Line":3}},{"line":283,"address":[],"length":0,"stats":{"Line":0}},{"line":287,"address":[],"length":0,"stats":{"Line":3}},{"line":318,"address":[901776,901744],"length":1,"stats":{"Line":1}},{"line":319,"address":[],"length":0,"stats":{"Line":3}},{"line":320,"address":[1497619],"length":1,"stats":{"Line":1}},{"line":321,"address":[1497629],"length":1,"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":371,"address":[902288],"length":1,"stats":{"Line":1}},{"line":374,"address":[],"length":0,"stats":{"Line":2}},{"line":376,"address":[902376,902421],"length":1,"stats":{"Line":1}},{"line":378,"address":[],"length":0,"stats":{"Line":1}},{"line":379,"address":[],"length":0,"stats":{"Line":1}},{"line":381,"address":[],"length":0,"stats":{"Line":1}},{"line":382,"address":[],"length":0,"stats":{"Line":1}},{"line":383,"address":[],"length":0,"stats":{"Line":0}},{"line":386,"address":[],"length":0,"stats":{"Line":0}},{"line":390,"address":[],"length":0,"stats":{"Line":1}},{"line":426,"address":[],"length":0,"stats":{"Line":0}},{"line":428,"address":[],"length":0,"stats":{"Line":0}},{"line":429,"address":[],"length":0,"stats":{"Line":0}},{"line":431,"address":[],"length":0,"stats":{"Line":0}},{"line":432,"address":[],"length":0,"stats":{"Line":0}},{"line":433,"address":[],"length":0,"stats":{"Line":0}},{"line":437,"address":[],"length":0,"stats":{"Line":0}},{"line":468,"address":[],"length":0,"stats":{"Line":0}},{"line":469,"address":[],"length":0,"stats":{"Line":0}},{"line":470,"address":[],"length":0,"stats":{"Line":0}},{"line":471,"address":[],"length":0,"stats":{"Line":0}},{"line":473,"address":[],"length":0,"stats":{"Line":0}},{"line":474,"address":[],"length":0,"stats":{"Line":0}},{"line":475,"address":[],"length":0,"stats":{"Line":0}},{"line":524,"address":[],"length":0,"stats":{"Line":0}},{"line":526,"address":[],"length":0,"stats":{"Line":0}},{"line":528,"address":[],"length":0,"stats":{"Line":0}},{"line":529,"address":[],"length":0,"stats":{"Line":0}},{"line":531,"address":[],"length":0,"stats":{"Line":0}},{"line":532,"address":[],"length":0,"stats":{"Line":0}},{"line":533,"address":[],"length":0,"stats":{"Line":0}},{"line":536,"address":[],"length":0,"stats":{"Line":0}},{"line":540,"address":[],"length":0,"stats":{"Line":0}},{"line":568,"address":[],"length":0,"stats":{"Line":0}},{"line":569,"address":[],"length":0,"stats":{"Line":0}},{"line":591,"address":[],"length":0,"stats":{"Line":0}},{"line":592,"address":[],"length":0,"stats":{"Line":0}},{"line":593,"address":[],"length":0,"stats":{"Line":0}},{"line":618,"address":[],"length":0,"stats":{"Line":0}},{"line":619,"address":[],"length":0,"stats":{"Line":0}},{"line":646,"address":[],"length":0,"stats":{"Line":0}},{"line":647,"address":[],"length":0,"stats":{"Line":0}},{"line":672,"address":[],"length":0,"stats":{"Line":0}},{"line":673,"address":[],"length":0,"stats":{"Line":0}},{"line":680,"address":[],"length":0,"stats":{"Line":0}},{"line":681,"address":[],"length":0,"stats":{"Line":0}},{"line":692,"address":[],"length":0,"stats":{"Line":1}},{"line":693,"address":[],"length":0,"stats":{"Line":1}}],"covered":52,"coverable":99},{"path":["/","home","botahamec","Projects","happylock","src","context","tuple.rs"],"content":"use std::marker::PhantomData;\n\nuse crate::{\n\tcontext::{ContextGuard, LockingIterator, LockingTuple},\n\tlockable::{Lockable, RawLock, Sharable},\n\tThreadKey,\n};\n\nimpl\u003c'c, A, B, O\u003e LockingTuple\u003c'c, A, B, O\u003e {\n\tfn transmute\u003cC\u003e(self) -\u003e LockingTuple\u003c'c, C, B, O\u003e {\n\t\tLockingTuple {\n\t\t\t_lockable: PhantomData,\n\t\t\tkey: self.key,\n\t\t\ttuple: self.tuple,\n\t\t\touter: self.outer,\n\t\t}\n\t}\n}\n\nmacro_rules! lock_impl {\n\t($self: expr, $field: tt) =\u003e {\n\t\tunsafe {\n\t\t\t$self.tuple.$field.raw_write();\n\t\t\t(\n\t\t\t\tContextGuard {\n\t\t\t\t\t_key: \u0026$self.key,\n\t\t\t\t\tguard: $self.tuple.$field.guard(),\n\t\t\t\t},\n\t\t\t\t$self.transmute(),\n\t\t\t)\n\t\t}\n\t};\n}\n\nmacro_rules! try_lock_impl {\n\t($self: expr, $field: tt) =\u003e {\n\t\tunsafe {\n\t\t\tif $self.tuple.$field.raw_try_write() {\n\t\t\t\tOk((\n\t\t\t\t\tContextGuard {\n\t\t\t\t\t\t_key: $self.key,\n\t\t\t\t\t\tguard: $self.tuple.$field.guard(),\n\t\t\t\t\t},\n\t\t\t\t\t$self.transmute(),\n\t\t\t\t))\n\t\t\t} else {\n\t\t\t\tErr($self)\n\t\t\t}\n\t\t}\n\t};\n}\n\nmacro_rules! lock_mut_impl {\n\t($self: expr, $field: tt) =\u003e {\n\t\tunsafe {\n\t\t\t$self.tuple.$field.raw_write();\n\t\t\tContextGuard {\n\t\t\t\t_key: $self.key,\n\t\t\t\tguard: $self.tuple.$field.guard(),\n\t\t\t}\n\t\t}\n\t};\n}\n\nmacro_rules! try_lock_mut_impl {\n\t($self: expr, $field: tt) =\u003e {\n\t\tunsafe {\n\t\t\tif $self.tuple.$field.raw_try_write() {\n\t\t\t\tSome(ContextGuard {\n\t\t\t\t\t_key: $self.key,\n\t\t\t\t\tguard: $self.tuple.$field.guard(),\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\tNone\n\t\t\t}\n\t\t}\n\t};\n}\n\nmacro_rules! read_impl {\n\t($self: expr, $field: tt) =\u003e {\n\t\tunsafe {\n\t\t\t$self.tuple.$field.raw_read();\n\t\t\t(\n\t\t\t\tContextGuard {\n\t\t\t\t\t_key: \u0026$self.key,\n\t\t\t\t\tguard: $self.tuple.$field.read_guard(),\n\t\t\t\t},\n\t\t\t\t$self.transmute(),\n\t\t\t)\n\t\t}\n\t};\n}\n\nmacro_rules! try_read_impl {\n\t($self: expr, $field: tt) =\u003e {\n\t\tunsafe {\n\t\t\tif $self.tuple.$field.raw_try_read() {\n\t\t\t\tOk((\n\t\t\t\t\tContextGuard {\n\t\t\t\t\t\t_key: $self.key,\n\t\t\t\t\t\tguard: $self.tuple.$field.read_guard(),\n\t\t\t\t\t},\n\t\t\t\t\t$self.transmute(),\n\t\t\t\t))\n\t\t\t} else {\n\t\t\t\tErr($self)\n\t\t\t}\n\t\t}\n\t};\n}\n\nmacro_rules! read_mut_impl {\n\t($self: expr, $field: tt) =\u003e {\n\t\tunsafe {\n\t\t\t$self.tuple.$field.raw_read();\n\t\t\tContextGuard {\n\t\t\t\t_key: $self.key,\n\t\t\t\tguard: $self.tuple.$field.read_guard(),\n\t\t\t}\n\t\t}\n\t};\n}\n\nmacro_rules! try_read_mut_impl {\n\t($self: expr, $field: tt) =\u003e {\n\t\tunsafe {\n\t\t\tif $self.tuple.$field.raw_try_read() {\n\t\t\t\tSome(ContextGuard {\n\t\t\t\t\t_key: $self.key,\n\t\t\t\t\tguard: $self.tuple.$field.read_guard(),\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\tNone\n\t\t\t}\n\t\t}\n\t};\n}\n\nmacro_rules! recurse_impl {\n\t($self: expr, $field: tt) =\u003e {\n\t\tLockingTuple {\n\t\t\t_lockable: PhantomData,\n\t\t\tkey: $self.key,\n\t\t\ttuple: \u0026$self.tuple.$field,\n\t\t\touter: $self.transmute(),\n\t\t}\n\t};\n}\n\nmacro_rules! recurse_iter_impl {\n\t($self: expr, $field: tt) =\u003e {\n\t\tLockingIterator {\n\t\t\tkey: $self.key,\n\t\t\titerator: $self.tuple.$field.into_iter(),\n\t\t\touter: $self.transmute(),\n\t\t}\n\t};\n}\n\ntype LockReturn\u003c'a, 'context, Guarded, L, C, O\u003e = (\n\tContextGuard\u003c'a, \u003cGuarded as Lockable\u003e::Guard\u003c'a\u003e, ThreadKey\u003e,\n\tLockingTuple\u003c'context, L, C, O\u003e,\n);\n\ntype TryLockReturn\u003c'a, 'context, Guarded, L, C, O, This\u003e =\n\tResult\u003cLockReturn\u003c'a, 'context, Guarded, L, C, O\u003e, This\u003e;\n\ntype ReadReturn\u003c'a, 'context, Guarded, L, C, O\u003e = (\n\tContextGuard\u003c'a, \u003cGuarded as Sharable\u003e::ReadGuard\u003c'a\u003e, ThreadKey\u003e,\n\tLockingTuple\u003c'context, L, C, O\u003e,\n);\n\ntype TryReadReturn\u003c'a, 'context, Guarded, L, C, O, This\u003e =\n\tResult\u003cReadReturn\u003c'a, 'context, Guarded, L, C, O\u003e, This\u003e;\n\ntype RecurseReturn\u003c'context, Inner, L, C, O\u003e =\n\tLockingTuple\u003c'context, Inner, Inner, LockingTuple\u003c'context, L, C, O\u003e\u003e;\n\ntype RecurseIterReturn\u003c'context, Inner, L, C, O\u003e = LockingIterator\u003c\n\t'context,\n\t\u003c\u0026'context Inner as IntoIterator\u003e::IntoIter,\n\tLockingTuple\u003c'context, L, C, O\u003e,\n\u003e;\n\nimpl\u003cT, C, Outer\u003e LockingTuple\u003c'_, T, C, Outer\u003e {\n\t/// Exit out of the current scope of the locking tuple into the parent.\n\tpub fn exit(self) -\u003e Outer {\n\t\tself.outer\n\t}\n}\n\nimpl\u003c'context, A: RawLock + Lockable, Outer\u003e LockingTuple\u003c'context, (A,), (A,), Outer\u003e {\n\t/// Lock the first element, and return a new tuple where the first element\n\t/// is inaccessible.\n\t#[must_use]\n\tpub fn lock_0\u003c'a\u003e(self) -\u003e LockReturn\u003c'a, 'context, A, ((),), (A,), Outer\u003e\n\twhere\n\t\t'context: 'a,\n\t{\n\t\tlock_impl!(self, 0)\n\t}\n\n\t/// Attempts to lock the first element without blocking, and return a new tuple\n\t/// where the first element is inaccessible.\n\t///\n\t/// # Errors\n\t///\n\t/// If the element is already locked, `Err` is returned with the original\n\t/// tuple.\n\tpub fn try_lock_0\u003c'a\u003e(self) -\u003e TryLockReturn\u003c'a, 'context, A, ((),), (A,), Outer, Self\u003e\n\twhere\n\t\t'context: 'a,\n\t{\n\t\ttry_lock_impl!(self, 0)\n\t}\n\n\t/// Lock the first element. The tuple becomes unusable until the returned\n\t/// guard is dropped.\n\t#[must_use]\n\tpub fn lock_mut_0(\u0026mut self) -\u003e ContextGuard\u003c'_, A::Guard\u003c'_\u003e, ThreadKey\u003e {\n\t\tlock_mut_impl!(self, 0)\n\t}\n\n\t/// Attempts to lock the first element without blocking. If successful, the\n\t/// tuple becomes unusable until the returned guard is dropped. If the element\n\t/// is already locked, `None` is returned.\n\t#[must_use]\n\tpub fn try_lock_mut_0(\u0026mut self) -\u003e Option\u003cContextGuard\u003c'_, A::Guard\u003c'_\u003e, ThreadKey\u003e\u003e {\n\t\ttry_lock_mut_impl!(self, 0)\n\t}\n}\n\nimpl\u003c'context, A: RawLock + Sharable, Outer\u003e LockingTuple\u003c'context, (A,), (A,), Outer\u003e {\n\t/// Acquire a shared lock to the first element, and return a new tuple where\n\t/// the first element is inaccessible.\n\t#[must_use]\n\tpub fn read_0\u003c'a\u003e(self) -\u003e ReadReturn\u003c'a, 'context, A, ((),), (A,), Outer\u003e\n\twhere\n\t\t'context: 'a,\n\t{\n\t\tread_impl!(self, 0)\n\t}\n\n\t/// Attempts to acquire a shared lock the first element without blocking, and\n\t/// return a new tuple where the first element is inaccessible.\n\t///\n\t/// # Errors\n\t///\n\t/// If the element is already exclusively locked, `Err` is returned with the original\n\t/// tuple.\n\tpub fn try_read_0\u003c'a\u003e(self) -\u003e TryReadReturn\u003c'a, 'context, A, ((),), (A,), Outer, Self\u003e\n\twhere\n\t\t'context: 'a,\n\t{\n\t\ttry_read_impl!(self, 0)\n\t}\n\n\t/// Acquire a shared lock to the first element. The tuple becomes unusable\n\t/// until the returned guard is dropped.\n\t#[must_use]\n\tpub fn read_mut_0(\u0026mut self) -\u003e ContextGuard\u003c'_, A::ReadGuard\u003c'_\u003e, ThreadKey\u003e {\n\t\tread_mut_impl!(self, 0)\n\t}\n\n\t/// Attempts to acquire a shared lock the first element without blocking. If\n\t/// successful, the tuple becomes unusable until the returned guard is\n\t/// dropped. If the element is already exclusively locked, `None` is returned.\n\t#[must_use]\n\tpub fn try_read_mut_0(\u0026mut self) -\u003e Option\u003cContextGuard\u003c'_, A::ReadGuard\u003c'_\u003e, ThreadKey\u003e\u003e {\n\t\ttry_read_mut_impl!(self, 0)\n\t}\n}\n\nimpl\u003c'context, A, O\u003e LockingTuple\u003c'context, (A,), (A,), O\u003e {\n\t/// Consume the tuple, and return the first element as a new tuple.\n\t#[must_use]\n\tpub fn recurse_0(self) -\u003e RecurseReturn\u003c'context, A, ((),), (A,), O\u003e {\n\t\trecurse_impl!(self, 0)\n\t}\n\n\t/// Consume the tuple, and return the first element as a locking iterator.\n\tpub fn recurse_0_iter(self) -\u003e RecurseIterReturn\u003c'context, A, ((),), (A,), O\u003e\n\twhere\n\t\t\u0026'context A: IntoIterator,\n\t{\n\t\trecurse_iter_impl!(self, 0)\n\t}\n}\n\nimpl\u003c'context, A: RawLock + Lockable, B, B0, O\u003e LockingTuple\u003c'context, (A, B), (A, B0), O\u003e {\n\t/// Lock the first element, and return a new tuple where the first element\n\t/// is inaccessible.\n\t#[must_use]\n\tpub fn lock_0\u003c'a\u003e(self) -\u003e LockReturn\u003c'a, 'context, A, ((), B), (A, B0), O\u003e\n\twhere\n\t\t'context: 'a,\n\t{\n\t\tlock_impl!(self, 0)\n\t}\n\n\t/// Lock the first element. The tuple becomes unusable until the returned\n\t/// guard is dropped.\n\t#[must_use]\n\tpub fn lock_mut_0(\u0026mut self) -\u003e ContextGuard\u003c'_, A::Guard\u003c'_\u003e, ThreadKey\u003e {\n\t\tlock_mut_impl!(self, 0)\n\t}\n\n\t/// Attempts to lock the first element without blocking, and return a new tuple\n\t/// where the first element is inaccessible.\n\t///\n\t/// # Errors\n\t///\n\t/// If the element is already locked, `Err` is returned with the original\n\t/// tuple.\n\tpub fn try_lock_0\u003c'a\u003e(self) -\u003e TryLockReturn\u003c'a, 'context, A, ((), B), (A, B0), O, Self\u003e\n\twhere\n\t\t'context: 'a,\n\t{\n\t\ttry_lock_impl!(self, 0)\n\t}\n\n\t/// Attempts to lock the first element without blocking. If successful, the\n\t/// tuple becomes unusable until the returned guard is dropped. If the element\n\t/// is already locked, `None` is returned.\n\t#[must_use]\n\tpub fn try_lock_mut_0(\u0026mut self) -\u003e Option\u003cContextGuard\u003c'_, A::Guard\u003c'_\u003e, ThreadKey\u003e\u003e {\n\t\ttry_lock_mut_impl!(self, 0)\n\t}\n}\n\nimpl\u003c'context, A: RawLock + Sharable, B, B0, O\u003e LockingTuple\u003c'context, (A, B), (A, B0), O\u003e {\n\t/// Acquire a shared lock to the first element, and return a new tuple where\n\t/// the first element is inaccessible.\n\t#[must_use]\n\tpub fn read_0\u003c'a\u003e(self) -\u003e ReadReturn\u003c'a, 'context, A, ((), B), (A, B0), O\u003e\n\twhere\n\t\t'context: 'a,\n\t{\n\t\tread_impl!(self, 0)\n\t}\n\n\t/// Attempts to acquire a shared lock the first element without blocking, and\n\t/// return a new tuple where the first element is inaccessible.\n\t///\n\t/// # Errors\n\t///\n\t/// If the element is already exclusively locked, `Err` is returned with the original\n\t/// tuple.\n\tpub fn try_read_0\u003c'a\u003e(self) -\u003e TryReadReturn\u003c'a, 'context, A, ((), B), (A, B0), O, Self\u003e\n\twhere\n\t\t'context: 'a,\n\t{\n\t\ttry_read_impl!(self, 0)\n\t}\n\n\t/// Acquire a shared lock to the first element. The tuple becomes unusable\n\t/// until the returned guard is dropped.\n\t#[must_use]\n\tpub fn read_mut_0(\u0026mut self) -\u003e ContextGuard\u003c'_, A::ReadGuard\u003c'_\u003e, ThreadKey\u003e {\n\t\tread_mut_impl!(self, 0)\n\t}\n\n\t/// Attempts to acquire a shared lock the first element without blocking. If\n\t/// successful, the tuple becomes unusable until the returned guard is\n\t/// dropped. If the element is already exclusively locked, `None` is returned.\n\t#[must_use]\n\tpub fn try_read_mut_0(\u0026mut self) -\u003e Option\u003cContextGuard\u003c'_, A::ReadGuard\u003c'_\u003e, ThreadKey\u003e\u003e {\n\t\ttry_read_mut_impl!(self, 0)\n\t}\n}\n\nimpl\u003c'context, A, B, B0, O\u003e LockingTuple\u003c'context, (A, B), (A, B0), O\u003e {\n\t/// Consume the tuple, and return the first element as a new tuple.\n\t#[must_use]\n\tpub fn recurse_0(self) -\u003e RecurseReturn\u003c'context, A, ((), B), (A, B0), O\u003e {\n\t\trecurse_impl!(self, 0)\n\t}\n\n\t/// Consume the tuple, and return the first element as a locking iterator.\n\tpub fn recurse_0_iter(self) -\u003e RecurseIterReturn\u003c'context, A, ((), B), (A, B0), O\u003e\n\twhere\n\t\t\u0026'context A: IntoIterator,\n\t{\n\t\trecurse_iter_impl!(self, 0)\n\t}\n}\n\nimpl\u003c'context, A: Lockable + RawLock, B, O\u003e LockingTuple\u003c'context, (A, B), (A, B), O\u003e {\n\t/// Lock the first element, and return the second element as a new tuple.\n\t#[must_use]\n\tpub fn lock_and_recurse\u003c'a\u003e(\n\t\tself,\n\t) -\u003e (\n\t\tContextGuard\u003c'a, \u003cA as Lockable\u003e::Guard\u003c'a\u003e, ThreadKey\u003e,\n\t\tLockingTuple\u003c'context, B, B, O\u003e,\n\t)\n\twhere\n\t\t'context: 'a,\n\t{\n\t\tunsafe {\n\t\t\tself.tuple.0.raw_write();\n\t\t\t(\n\t\t\t\tContextGuard {\n\t\t\t\t\t_key: self.key,\n\t\t\t\t\tguard: self.tuple.0.guard(),\n\t\t\t\t},\n\t\t\t\tLockingTuple {\n\t\t\t\t\t_lockable: PhantomData,\n\t\t\t\t\tkey: self.key,\n\t\t\t\t\ttuple: \u0026self.tuple.1,\n\t\t\t\t\touter: self.outer,\n\t\t\t\t},\n\t\t\t)\n\t\t}\n\t}\n}\n\nimpl\u003c'context, A, A0, B: RawLock + Lockable, O\u003e LockingTuple\u003c'context, (A, B), (A0, B), O\u003e {\n\t/// Lock the second element, and return a new tuple where the first and second\n\t/// elements are inaccessible.\n\t#[must_use]\n\tpub fn lock_1\u003c'a\u003e(self) -\u003e LockReturn\u003c'a, 'context, B, ((), ()), (A0, B), O\u003e\n\twhere\n\t\t'context: 'a,\n\t{\n\t\tlock_impl!(self, 1)\n\t}\n\n\t/// Lock the second element. The tuple becomes unusable until the returned\n\t/// guard is dropped.\n\t#[must_use]\n\tpub fn lock_mut_1(\u0026mut self) -\u003e ContextGuard\u003c'_, B::Guard\u003c'_\u003e, ThreadKey\u003e {\n\t\tlock_mut_impl!(self, 1)\n\t}\n\n\t/// Attempts to lock the second element without blocking, and return a new tuple\n\t/// where the first element is inaccessible.\n\t///\n\t/// # Errors\n\t///\n\t/// If the element is already locked, `Err` is returned with the original\n\t/// tuple.\n\tpub fn try_lock_1\u003c'a\u003e(self) -\u003e TryLockReturn\u003c'a, 'context, B, ((), ()), (A0, B), O, Self\u003e\n\twhere\n\t\t'context: 'a,\n\t{\n\t\ttry_lock_impl!(self, 1)\n\t}\n\n\t/// Attempts to lock the second element without blocking. If successful, the\n\t/// tuple becomes unusable until the returned guard is dropped. If the element\n\t/// is already locked, `None` is returned.\n\t#[must_use]\n\tpub fn try_lock_mut_1(\u0026mut self) -\u003e Option\u003cContextGuard\u003c'_, B::Guard\u003c'_\u003e, ThreadKey\u003e\u003e {\n\t\ttry_lock_mut_impl!(self, 1)\n\t}\n}\n\nimpl\u003c'context, A, A0, B: RawLock + Sharable, O\u003e LockingTuple\u003c'context, (A, B), (A0, B), O\u003e {\n\t/// Acquire a shared lock to the second element, and return a new tuple where\n\t/// the second element is inaccessible.\n\t#[must_use]\n\tpub fn read_1\u003c'a\u003e(self) -\u003e ReadReturn\u003c'a, 'context, B, ((), ()), (A0, B), O\u003e\n\twhere\n\t\t'context: 'a,\n\t{\n\t\tread_impl!(self, 1)\n\t}\n\n\t/// Attempts to acquire a shared lock the second element without blocking, and\n\t/// return a new tuple where the second element is inaccessible.\n\t///\n\t/// # Errors\n\t///\n\t/// If the element is already exclusively locked, `Err` is returned with the original\n\t/// tuple.\n\tpub fn try_read_1\u003c'a\u003e(self) -\u003e TryReadReturn\u003c'a, 'context, B, ((), ()), (A0, B), O, Self\u003e\n\twhere\n\t\t'context: 'a,\n\t{\n\t\ttry_read_impl!(self, 1)\n\t}\n\n\t/// Acquire a shared lock to the second element. The tuple becomes unusable\n\t/// until the returned guard is dropped.\n\t#[must_use]\n\tpub fn read_mut_1(\u0026mut self) -\u003e ContextGuard\u003c'_, B::ReadGuard\u003c'_\u003e, ThreadKey\u003e {\n\t\tread_mut_impl!(self, 1)\n\t}\n\n\t/// Attempts to acquire a shared lock the second element without blocking. If\n\t/// successful, the tuple becomes unusable until the returned guard is\n\t/// dropped. If the element is already exclusively locked, `None` is returned.\n\t#[must_use]\n\tpub fn try_read_mut_1(\u0026mut self) -\u003e Option\u003cContextGuard\u003c'_, B::ReadGuard\u003c'_\u003e, ThreadKey\u003e\u003e {\n\t\ttry_read_mut_impl!(self, 1)\n\t}\n}\n\nimpl\u003c'context, A, A0, B, O\u003e LockingTuple\u003c'context, (A, B), (A0, B), O\u003e {\n\t/// Consume the tuple, and return the second element as a new tuple.\n\t#[must_use]\n\tpub fn recurse_1(self) -\u003e RecurseReturn\u003c'context, B, ((), ()), (A0, B), O\u003e {\n\t\trecurse_impl!(self, 1)\n\t}\n\n\t/// Consume the tuple, and return the second element as a locking iterator.\n\tpub fn recurse_1_iter(self) -\u003e RecurseIterReturn\u003c'context, B, (A, ()), (A0, B), O\u003e\n\twhere\n\t\t\u0026'context B: IntoIterator,\n\t{\n\t\trecurse_iter_impl!(self, 1)\n\t}\n}\n\nimpl\u003c'context, A: RawLock + Lockable, B, B0, C, C0, O\u003e\n\tLockingTuple\u003c'context, (A, B, C), (A, B0, C0), O\u003e\n{\n\t/// Lock the first element, and return a new tuple where the first element\n\t/// is inaccessible.\n\t#[must_use]\n\t// The type is impossible to refactor, and I already wrote this function, so no point in removing it\n\t#[expect(clippy::type_complexity)]\n\tpub fn lock_0\u003c'a\u003e(self) -\u003e LockReturn\u003c'a, 'context, A, ((), B, C), (A, B0, C0), O\u003e\n\twhere\n\t\t'context: 'a,\n\t{\n\t\tlock_impl!(self, 0)\n\t}\n\n\t/// Lock the first element. The tuple becomes unusable until the returned\n\t/// guard is dropped.\n\t#[must_use]\n\tpub fn lock_mut_0(\u0026mut self) -\u003e ContextGuard\u003c'_, A::Guard\u003c'_\u003e, ThreadKey\u003e {\n\t\tlock_mut_impl!(self, 0)\n\t}\n\n\t/// Attempts to lock the first element without blocking. If successful, the\n\t/// tuple becomes unusable until the returned guard is dropped. If the element\n\t/// is already locked, `None` is returned.\n\t#[must_use]\n\tpub fn try_lock_mut_0(\u0026mut self) -\u003e Option\u003cContextGuard\u003c'_, A::Guard\u003c'_\u003e, ThreadKey\u003e\u003e {\n\t\ttry_lock_mut_impl!(self, 0)\n\t}\n}\n\nimpl\u003c'context, A: RawLock + Sharable, B, B0, C, C0, O\u003e\n\tLockingTuple\u003c'context, (A, B, C), (A, B0, C0), O\u003e\n{\n\t/// Acquire a shared lock to the first element, and return a new tuple where\n\t/// the first element is inaccessible.\n\t#[must_use]\n\t// The type is impossible to refactor, and I already wrote this function, so no point in removing it\n\t#[expect(clippy::type_complexity)]\n\tpub fn read_0\u003c'a\u003e(self) -\u003e ReadReturn\u003c'a, 'context, A, ((), B, C), (A, B0, C0), O\u003e\n\twhere\n\t\t'context: 'a,\n\t{\n\t\tread_impl!(self, 0)\n\t}\n\n\t/// Acquire a shared lock to the first element. The tuple becomes unusable\n\t/// until the returned guard is dropped.\n\t#[must_use]\n\tpub fn read_mut_0(\u0026mut self) -\u003e ContextGuard\u003c'_, A::ReadGuard\u003c'_\u003e, ThreadKey\u003e {\n\t\tread_mut_impl!(self, 0)\n\t}\n\n\t/// Attempts to acquire a shared lock the first element without blocking. If\n\t/// successful, the tuple becomes unusable until the returned guard is\n\t/// dropped. If the element is already exclusively locked, `None` is returned.\n\t#[must_use]\n\tpub fn try_read_mut_0(\u0026mut self) -\u003e Option\u003cContextGuard\u003c'_, A::ReadGuard\u003c'_\u003e, ThreadKey\u003e\u003e {\n\t\ttry_read_mut_impl!(self, 0)\n\t}\n}\n\nimpl\u003c'context, A, B, B0, C, C0, O\u003e LockingTuple\u003c'context, (A, B, C), (A, B0, C0), O\u003e {\n\t/// Consume the tuple, and return the first element as a new tuple.\n\t#[must_use]\n\t// The type is impossible to refactor, and I already wrote this function, so no point in removing it\n\t#[expect(clippy::type_complexity)]\n\tpub fn recurse_0(self) -\u003e RecurseReturn\u003c'context, A, ((), B, C), (A, B0, C0), O\u003e {\n\t\trecurse_impl!(self, 0)\n\t}\n\n\t/// Consume the tuple, and return the first element as a locking iterator.\n\t// The type is impossible to refactor, and I already wrote this function, so no point in removing it\n\t#[expect(clippy::type_complexity)]\n\tpub fn recurse_0_iter(self) -\u003e RecurseIterReturn\u003c'context, A, ((), B, C), (A, B0, C0), O\u003e\n\twhere\n\t\t\u0026'context A: IntoIterator,\n\t{\n\t\trecurse_iter_impl!(self, 0)\n\t}\n}\n\nimpl\u003c'context, A, A0, B: RawLock + Lockable, C, C0, O\u003e\n\tLockingTuple\u003c'context, (A, B, C), (A0, B, C0), O\u003e\n{\n\t/// Lock the second element, and return a new tuple where the first and second\n\t/// elements are inaccessible.\n\t#[must_use]\n\t// The type is impossible to refactor, and I already wrote this function, so no point in removing it\n\t#[expect(clippy::type_complexity)]\n\tpub fn lock_1\u003c'a\u003e(self) -\u003e LockReturn\u003c'a, 'context, B, ((), (), C), (A0, B, C0), O\u003e\n\twhere\n\t\t'context: 'a,\n\t{\n\t\tlock_impl!(self, 1)\n\t}\n\n\t/// Lock the second element. The tuple becomes unusable until the returned\n\t/// guard is dropped.\n\t#[must_use]\n\tpub fn lock_mut_1(\u0026mut self) -\u003e ContextGuard\u003c'_, B::Guard\u003c'_\u003e, ThreadKey\u003e {\n\t\tlock_mut_impl!(self, 1)\n\t}\n\n\t/// Attempts to lock the second element without blocking. If successful, the\n\t/// tuple becomes unusable until the returned guard is dropped. If the element\n\t/// is already locked, `None` is returned.\n\t#[must_use]\n\tpub fn try_lock_mut_1(\u0026mut self) -\u003e Option\u003cContextGuard\u003c'_, B::Guard\u003c'_\u003e, ThreadKey\u003e\u003e {\n\t\ttry_lock_mut_impl!(self, 1)\n\t}\n}\n\nimpl\u003c'context, A, A0, B: RawLock + Sharable, C, C0, O\u003e\n\tLockingTuple\u003c'context, (A, B, C), (A0, B, C0), O\u003e\n{\n\t/// Acquire a shared lock to the second element, and return a new tuple where\n\t/// the second element is inaccessible.\n\t#[must_use]\n\t// The type is impossible to refactor, and I already wrote this function, so no point in removing it\n\t#[expect(clippy::type_complexity)]\n\tpub fn read_1\u003c'a\u003e(self) -\u003e ReadReturn\u003c'a, 'context, B, ((), (), C), (A0, B, C0), O\u003e\n\twhere\n\t\t'context: 'a,\n\t{\n\t\tread_impl!(self, 1)\n\t}\n\n\t/// Acquire a shared lock to the second element. The tuple becomes unusable\n\t/// until the returned guard is dropped.\n\t#[must_use]\n\tpub fn read_mut_1(\u0026mut self) -\u003e ContextGuard\u003c'_, B::ReadGuard\u003c'_\u003e, ThreadKey\u003e {\n\t\tread_mut_impl!(self, 1)\n\t}\n\n\t/// Attempts to acquire a shared lock the second element without blocking. If\n\t/// successful, the tuple becomes unusable until the returned guard is\n\t/// dropped. If the element is already exclusively locked, `None` is returned.\n\t#[must_use]\n\tpub fn try_read_mut_1(\u0026mut self) -\u003e Option\u003cContextGuard\u003c'_, B::ReadGuard\u003c'_\u003e, ThreadKey\u003e\u003e {\n\t\ttry_read_mut_impl!(self, 1)\n\t}\n}\n\nimpl\u003c'context, A, A0, B, C, C0, O\u003e LockingTuple\u003c'context, (A, B, C), (A0, B, C0), O\u003e {\n\t/// Consume the tuple, and return the second element as a new tuple.\n\t#[must_use]\n\t// The type is impossible to refactor, and I already wrote this function, so no point in removing it\n\t#[expect(clippy::type_complexity)]\n\tpub fn recurse_1(self) -\u003e RecurseReturn\u003c'context, B, ((), (), C), (A0, B, C0), O\u003e {\n\t\trecurse_impl!(self, 1)\n\t}\n\n\t/// Consume the tuple, and return the second element as a locking iterator.\n\t// The type is impossible to refactor, and I already wrote this function, so no point in removing it\n\t#[expect(clippy::type_complexity)]\n\tpub fn recurse_1_iter(self) -\u003e RecurseIterReturn\u003c'context, B, ((), (), C), (A0, B, C0), O\u003e\n\twhere\n\t\t\u0026'context B: IntoIterator,\n\t{\n\t\trecurse_iter_impl!(self, 1)\n\t}\n}\n\nimpl\u003c'context, A, A0, B, B0, C: RawLock + Lockable, O\u003e\n\tLockingTuple\u003c'context, (A, B, C), (A0, B0, C), O\u003e\n{\n\t/// Lock the third element, and return a new tuple where all elements are\n\t/// inaccessible.\n\t#[must_use]\n\t// The type is impossible to refactor, and I already wrote this function, so no point in removing it\n\t#[expect(clippy::type_complexity)]\n\tpub fn lock_2\u003c'a\u003e(self) -\u003e LockReturn\u003c'a, 'context, C, ((), (), ()), (A0, B0, C), O\u003e\n\twhere\n\t\t'context: 'a,\n\t{\n\t\tlock_impl!(self, 2)\n\t}\n\n\t/// Lock the third element. The tuple becomes unusable until the returned\n\t/// guard is dropped.\n\t#[must_use]\n\tpub fn lock_mut_2(\u0026mut self) -\u003e ContextGuard\u003c'_, C::Guard\u003c'_\u003e, ThreadKey\u003e {\n\t\tlock_mut_impl!(self, 2)\n\t}\n\n\t/// Attempts to lock the third element without blocking. If successful, the\n\t/// tuple becomes unusable until the returned guard is dropped. If the element\n\t/// is already locked, `None` is returned.\n\t#[must_use]\n\tpub fn try_lock_mut_2(\u0026mut self) -\u003e Option\u003cContextGuard\u003c'_, C::Guard\u003c'_\u003e, ThreadKey\u003e\u003e {\n\t\ttry_lock_mut_impl!(self, 2)\n\t}\n}\n\nimpl\u003c'context, A, A0, B, B0, C: RawLock + Sharable, O\u003e\n\tLockingTuple\u003c'context, (A, B, C), (A0, B0, C), O\u003e\n{\n\t/// Acquire a shared lock to the third element, and return a new tuple where\n\t/// the third element is inaccessible.\n\t#[must_use]\n\t// The type is impossible to refactor, and I already wrote this function, so no point in removing it\n\t#[expect(clippy::type_complexity)]\n\tpub fn read_2\u003c'a\u003e(self) -\u003e ReadReturn\u003c'a, 'context, C, ((), (), ()), (A0, B0, C), O\u003e\n\twhere\n\t\t'context: 'a,\n\t{\n\t\tread_impl!(self, 2)\n\t}\n\n\t/// Acquire a shared lock to the third element. The tuple becomes unusable\n\t/// until the returned guard is dropped.\n\t#[must_use]\n\tpub fn read_mut_2(\u0026mut self) -\u003e ContextGuard\u003c'_, C::ReadGuard\u003c'_\u003e, ThreadKey\u003e {\n\t\tread_mut_impl!(self, 2)\n\t}\n\n\t/// Attempts to acquire a shared lock the third element without blocking. If\n\t/// successful, the tuple becomes unusable until the returned guard is\n\t/// dropped. If the element is already exclusively locked, `None` is returned.\n\t#[must_use]\n\tpub fn try_read_mut_2(\u0026mut self) -\u003e Option\u003cContextGuard\u003c'_, C::ReadGuard\u003c'_\u003e, ThreadKey\u003e\u003e {\n\t\ttry_read_mut_impl!(self, 2)\n\t}\n}\n\nimpl\u003c'context, A, A0, B, B0, C, O\u003e LockingTuple\u003c'context, (A, B, C), (A0, B0, C), O\u003e {\n\t/// Consume the tuple, and return the third element as a new tuple.\n\t#[must_use]\n\t// The type is impossible to refactor, and I already wrote this function, so no point in removing it\n\t#[expect(clippy::type_complexity)]\n\tpub fn recurse_2(self) -\u003e RecurseReturn\u003c'context, C, ((), (), ()), (A0, B0, C), O\u003e {\n\t\trecurse_impl!(self, 2)\n\t}\n\n\t/// Consume the tuple, and return the third element as a locking iterator.\n\t// The type is impossible to refactor, and I already wrote this function, so no point in removing it\n\t#[expect(clippy::type_complexity)]\n\tpub fn recurse_2_iter(self) -\u003e RecurseIterReturn\u003c'context, C, ((), (), ()), (A0, B0, C), O\u003e\n\twhere\n\t\t\u0026'context C: IntoIterator,\n\t{\n\t\trecurse_iter_impl!(self, 2)\n\t}\n}\n\nimpl\u003c'context, A, B, B0, C, C0, D, D0, O\u003e LockingTuple\u003c'context, (A, B, C, D), (A, B0, C0, D0), O\u003e {\n\t/// Consume the tuple, and return the first element as a new tuple.\n\t#[must_use]\n\t// The type is impossible to refactor, and I already wrote this function, so no point in removing it\n\t#[expect(clippy::type_complexity)]\n\tpub fn recurse_0(self) -\u003e RecurseReturn\u003c'context, A, ((), B, C, D), (A, B0, C0, D0), O\u003e {\n\t\trecurse_impl!(self, 0)\n\t}\n\n\t/// Consume the tuple, and return the first element as a locking iterator.\n\t// The type is impossible to refactor, and I already wrote this function, so no point in removing it\n\t#[expect(clippy::type_complexity)]\n\tpub fn recurse_0_iter(self) -\u003e RecurseIterReturn\u003c'context, A, ((), B, C, D), (A, B0, C0, D0), O\u003e\n\twhere\n\t\t\u0026'context A: IntoIterator,\n\t{\n\t\trecurse_iter_impl!(self, 0)\n\t}\n}\n\nimpl\u003c'context, A, A0, B, C, C0, D, D0, O\u003e LockingTuple\u003c'context, (A, B, C, D), (A0, B, C0, D0), O\u003e {\n\t/// Consume the tuple, and return the second element as a new tuple.\n\t#[must_use]\n\t// The type is impossible to refactor, and I already wrote this function, so no point in removing it\n\t#[expect(clippy::type_complexity)]\n\tpub fn recurse_1(self) -\u003e RecurseReturn\u003c'context, B, ((), (), C, D), (A0, B, C0, D0), O\u003e {\n\t\trecurse_impl!(self, 1)\n\t}\n\n\t/// Consume the tuple, and return the second element as a locking iterator.\n\t// The type is impossible to refactor, and I already wrote this function, so no point in removing it\n\t#[expect(clippy::type_complexity)]\n\tpub fn recurse_1_iter(\n\t\tself,\n\t) -\u003e RecurseIterReturn\u003c'context, B, ((), (), C, D), (A0, B, C0, D0), O\u003e\n\twhere\n\t\t\u0026'context B: IntoIterator,\n\t{\n\t\trecurse_iter_impl!(self, 1)\n\t}\n}\n\nimpl\u003c'context, A, A0, B, B0, C, D, D0, O\u003e LockingTuple\u003c'context, (A, B, C, D), (A0, B0, C, D0), O\u003e {\n\t/// Consume the tuple, and return the third element as a new tuple.\n\t#[must_use]\n\t// The type is impossible to refactor, and I already wrote this function, so no point in removing it\n\t#[expect(clippy::type_complexity)]\n\tpub fn recurse_2(self) -\u003e RecurseReturn\u003c'context, C, ((), (), (), D), (A0, B0, C, D0), O\u003e {\n\t\trecurse_impl!(self, 2)\n\t}\n\n\t/// Consume the tuple, and return the third element as a locking iterator.\n\t// The type is impossible to refactor, and I already wrote this function, so no point in removing it\n\t#[expect(clippy::type_complexity)]\n\tpub fn recurse_2_iter(\n\t\tself,\n\t) -\u003e RecurseIterReturn\u003c'context, C, ((), (), (), D), (A0, B0, C, D0), O\u003e\n\twhere\n\t\t\u0026'context C: IntoIterator,\n\t{\n\t\trecurse_iter_impl!(self, 2)\n\t}\n}\n\nimpl\u003c'context, A, A0, B, B0, C, C0, D, O\u003e LockingTuple\u003c'context, (A, B, C, D), (A0, B0, C0, D), O\u003e {\n\t/// Consume the tuple, and return the fourth element as a new tuple.\n\t#[must_use]\n\t// The type is impossible to refactor, and I already wrote this function, so no point in removing it\n\t#[expect(clippy::type_complexity)]\n\tpub fn recurse_3(self) -\u003e RecurseReturn\u003c'context, D, ((), (), (), ()), (A0, B0, C0, D), O\u003e {\n\t\trecurse_impl!(self, 3)\n\t}\n\n\t/// Consume the tuple, and return the first element as a locking iterator.\n\t// The type is impossible to refactor, and I already wrote this function, so no point in removing it\n\t#[expect(clippy::type_complexity)]\n\tpub fn recurse_3_iter(\n\t\tself,\n\t) -\u003e RecurseIterReturn\u003c'context, D, ((), (), (), ()), (A0, B0, C0, D), O\u003e\n\twhere\n\t\t\u0026'context D: IntoIterator,\n\t{\n\t\trecurse_iter_impl!(self, 3)\n\t}\n}\n","traces":[{"line":10,"address":[],"length":0,"stats":{"Line":5}},{"line":13,"address":[899507],"length":1,"stats":{"Line":1}},{"line":14,"address":[],"length":0,"stats":{"Line":1}},{"line":15,"address":[],"length":0,"stats":{"Line":1}},{"line":23,"address":[902536,899992,902790,903704],"length":1,"stats":{"Line":4}},{"line":25,"address":[902627,900083,902900,903803],"length":1,"stats":{"Line":4}},{"line":26,"address":[902590,902863,903762,900046],"length":1,"stats":{"Line":4}},{"line":27,"address":[903772,902870,900056,902600],"length":1,"stats":{"Line":4}},{"line":29,"address":[900108,902652,903828,902957],"length":1,"stats":{"Line":4}},{"line":56,"address":[903965,904029],"length":1,"stats":{"Line":2}},{"line":58,"address":[904046,903982],"length":1,"stats":{"Line":2}},{"line":59,"address":[904054,903990],"length":1,"stats":{"Line":2}},{"line":68,"address":[906078,905886,905694,903124,903598,906104,905912,900238,903102,905716,903624,900260],"length":1,"stats":{"Line":7}},{"line":69,"address":[903151,906135,905743,900287,903655,905943],"length":1,"stats":{"Line":6}},{"line":70,"address":[903131,905723,905919,900267,903631,906111],"length":1,"stats":{"Line":6}},{"line":71,"address":[900274,903638,905730,903138,906118,905926],"length":1,"stats":{"Line":6}},{"line":74,"address":[903615,905903,906095,900251,905707,903115],"length":1,"stats":{"Line":1}},{"line":128,"address":[903220,905812,905982,906008,903198,906174,904094,904120,905790,901662,906200,901684],"length":1,"stats":{"Line":7}},{"line":129,"address":[905839,904151,906231,906039,903247,901711],"length":1,"stats":{"Line":6}},{"line":130,"address":[905819,906015,903227,906207,904127,901691],"length":1,"stats":{"Line":6}},{"line":131,"address":[904134,906022,903234,905826,901698,906214],"length":1,"stats":{"Line":6}},{"line":134,"address":[906191,903211,905803,905999,904111,901675],"length":1,"stats":{"Line":1}},{"line":153,"address":[903484],"length":1,"stats":{"Line":1}},{"line":154,"address":[903322],"length":1,"stats":{"Line":1}},{"line":155,"address":[903332],"length":1,"stats":{"Line":1}},{"line":156,"address":[903415],"length":1,"stats":{"Line":1}},{"line":188,"address":[],"length":0,"stats":{"Line":1}},{"line":189,"address":[],"length":0,"stats":{"Line":1}},{"line":197,"address":[900207,899968,900201],"length":1,"stats":{"Line":1}},{"line":201,"address":[],"length":0,"stats":{"Line":1}},{"line":215,"address":[],"length":0,"stats":{"Line":0}},{"line":221,"address":[],"length":0,"stats":{"Line":0}},{"line":222,"address":[],"length":0,"stats":{"Line":0}},{"line":229,"address":[900224],"length":1,"stats":{"Line":1}},{"line":230,"address":[],"length":0,"stats":{"Line":0}},{"line":242,"address":[],"length":0,"stats":{"Line":0}},{"line":256,"address":[],"length":0,"stats":{"Line":0}},{"line":262,"address":[],"length":0,"stats":{"Line":0}},{"line":263,"address":[],"length":0,"stats":{"Line":0}},{"line":270,"address":[],"length":0,"stats":{"Line":1}},{"line":271,"address":[],"length":0,"stats":{"Line":0}},{"line":278,"address":[],"length":0,"stats":{"Line":0}},{"line":279,"address":[],"length":0,"stats":{"Line":0}},{"line":287,"address":[],"length":0,"stats":{"Line":0}},{"line":295,"address":[902768,903055,902751,902512,902745],"length":1,"stats":{"Line":2}},{"line":299,"address":[],"length":0,"stats":{"Line":2}},{"line":305,"address":[],"length":0,"stats":{"Line":0}},{"line":306,"address":[],"length":0,"stats":{"Line":0}},{"line":320,"address":[],"length":0,"stats":{"Line":0}},{"line":327,"address":[],"length":0,"stats":{"Line":1}},{"line":328,"address":[],"length":0,"stats":{"Line":0}},{"line":340,"address":[],"length":0,"stats":{"Line":0}},{"line":354,"address":[],"length":0,"stats":{"Line":0}},{"line":360,"address":[],"length":0,"stats":{"Line":0}},{"line":361,"address":[],"length":0,"stats":{"Line":0}},{"line":368,"address":[903184],"length":1,"stats":{"Line":1}},{"line":369,"address":[],"length":0,"stats":{"Line":0}},{"line":376,"address":[],"length":0,"stats":{"Line":0}},{"line":377,"address":[],"length":0,"stats":{"Line":0}},{"line":381,"address":[903508,903280],"length":1,"stats":{"Line":1}},{"line":385,"address":[],"length":0,"stats":{"Line":1}},{"line":402,"address":[],"length":0,"stats":{"Line":0}},{"line":404,"address":[],"length":0,"stats":{"Line":0}},{"line":405,"address":[],"length":0,"stats":{"Line":0}},{"line":406,"address":[],"length":0,"stats":{"Line":0}},{"line":408,"address":[],"length":0,"stats":{"Line":0}},{"line":409,"address":[],"length":0,"stats":{"Line":0}},{"line":410,"address":[],"length":0,"stats":{"Line":0}},{"line":411,"address":[],"length":0,"stats":{"Line":0}},{"line":412,"address":[],"length":0,"stats":{"Line":0}},{"line":423,"address":[903927,903680,903921],"length":1,"stats":{"Line":1}},{"line":427,"address":[],"length":0,"stats":{"Line":1}},{"line":433,"address":[904016,903952],"length":1,"stats":{"Line":2}},{"line":434,"address":[],"length":0,"stats":{"Line":0}},{"line":448,"address":[],"length":0,"stats":{"Line":0}},{"line":455,"address":[903584],"length":1,"stats":{"Line":1}},{"line":456,"address":[],"length":0,"stats":{"Line":0}},{"line":468,"address":[],"length":0,"stats":{"Line":0}},{"line":482,"address":[],"length":0,"stats":{"Line":0}},{"line":488,"address":[],"length":0,"stats":{"Line":0}},{"line":489,"address":[],"length":0,"stats":{"Line":0}},{"line":496,"address":[904080],"length":1,"stats":{"Line":1}},{"line":497,"address":[],"length":0,"stats":{"Line":0}},{"line":504,"address":[],"length":0,"stats":{"Line":0}},{"line":505,"address":[],"length":0,"stats":{"Line":0}},{"line":513,"address":[],"length":0,"stats":{"Line":0}},{"line":529,"address":[],"length":0,"stats":{"Line":0}},{"line":535,"address":[],"length":0,"stats":{"Line":0}},{"line":536,"address":[],"length":0,"stats":{"Line":0}},{"line":543,"address":[],"length":0,"stats":{"Line":1}},{"line":544,"address":[],"length":0,"stats":{"Line":0}},{"line":560,"address":[],"length":0,"stats":{"Line":0}},{"line":566,"address":[],"length":0,"stats":{"Line":0}},{"line":567,"address":[],"length":0,"stats":{"Line":0}},{"line":574,"address":[],"length":0,"stats":{"Line":1}},{"line":575,"address":[],"length":0,"stats":{"Line":0}},{"line":584,"address":[],"length":0,"stats":{"Line":0}},{"line":585,"address":[],"length":0,"stats":{"Line":0}},{"line":595,"address":[],"length":0,"stats":{"Line":0}},{"line":611,"address":[],"length":0,"stats":{"Line":0}},{"line":617,"address":[],"length":0,"stats":{"Line":0}},{"line":618,"address":[],"length":0,"stats":{"Line":0}},{"line":625,"address":[],"length":0,"stats":{"Line":1}},{"line":626,"address":[],"length":0,"stats":{"Line":0}},{"line":642,"address":[],"length":0,"stats":{"Line":0}},{"line":648,"address":[],"length":0,"stats":{"Line":0}},{"line":649,"address":[],"length":0,"stats":{"Line":0}},{"line":656,"address":[905968],"length":1,"stats":{"Line":1}},{"line":657,"address":[],"length":0,"stats":{"Line":0}},{"line":666,"address":[],"length":0,"stats":{"Line":0}},{"line":667,"address":[],"length":0,"stats":{"Line":0}},{"line":677,"address":[],"length":0,"stats":{"Line":0}},{"line":693,"address":[],"length":0,"stats":{"Line":0}},{"line":699,"address":[],"length":0,"stats":{"Line":0}},{"line":700,"address":[],"length":0,"stats":{"Line":0}},{"line":707,"address":[],"length":0,"stats":{"Line":1}},{"line":708,"address":[],"length":0,"stats":{"Line":0}},{"line":724,"address":[],"length":0,"stats":{"Line":0}},{"line":730,"address":[],"length":0,"stats":{"Line":0}},{"line":731,"address":[],"length":0,"stats":{"Line":0}},{"line":738,"address":[],"length":0,"stats":{"Line":1}},{"line":739,"address":[],"length":0,"stats":{"Line":0}},{"line":748,"address":[],"length":0,"stats":{"Line":0}},{"line":749,"address":[],"length":0,"stats":{"Line":0}},{"line":759,"address":[],"length":0,"stats":{"Line":0}},{"line":768,"address":[],"length":0,"stats":{"Line":0}},{"line":769,"address":[],"length":0,"stats":{"Line":0}},{"line":779,"address":[],"length":0,"stats":{"Line":0}},{"line":788,"address":[],"length":0,"stats":{"Line":0}},{"line":789,"address":[],"length":0,"stats":{"Line":0}},{"line":801,"address":[],"length":0,"stats":{"Line":0}},{"line":810,"address":[],"length":0,"stats":{"Line":0}},{"line":811,"address":[],"length":0,"stats":{"Line":0}},{"line":823,"address":[],"length":0,"stats":{"Line":0}},{"line":832,"address":[],"length":0,"stats":{"Line":0}},{"line":833,"address":[],"length":0,"stats":{"Line":0}},{"line":845,"address":[],"length":0,"stats":{"Line":0}}],"covered":49,"coverable":137},{"path":["/","home","botahamec","Projects","happylock","src","context.rs"],"content":"use std::marker::PhantomData;\n\nuse crate::ThreadKey;\n\nmod context;\nmod guard;\npub mod iterator;\npub mod tuple;\n\n/// Allows iterating over a lock collection, without locking every element at\n/// once.\n///\n/// Sometimes, partial allocation of locks is useful. For example, you may\n/// want to acquire a lock on the first element of a list before deciding if\n/// the second element should be locked. This function creates a\n/// [`LockContext`] which is capable of doing exactly that.\n///\n/// Upon using this context, the [`ThreadKey`] is stored inside this context.\n/// This ensures that nothing else, besides the types exposed by the context,\n/// can be locked until this context is dropped. To re-acquire the `ThreadKey`,\n/// call [`LockContext::unlock`].\n///\n/// A [`LockContext`] can be created by calling [`OwnedLockCollection::context`].\n///\n/// # Examples\n///\n/// Iterating through a tuple.\n///\n/// ```\n/// use happylock::{Mutex, ThreadKey};\n/// use happylock::collection::OwnedLockCollection;\n///\n/// let key = ThreadKey::get().unwrap();\n/// let data = (Mutex::new(true), Mutex::new(42), Mutex::new(67));\n/// let locks = OwnedLockCollection::new(data);\n/// let mut ctx = locks.context();\n/// let tuple = ctx.tuple(key);\n///\n/// let (use_other, tuple) = tuple.lock_0();\n/// let number = if **use_other {\n/// tuple.lock_2().0\n/// } else {\n/// tuple.lock_1().0\n/// };\n/// assert_eq!(**number, 67);\n/// ```\n///\n/// Iterating through a list\n///\n/// ```\n/// use happylock::{Mutex, ThreadKey};\n/// use happylock::collection::OwnedLockCollection;\n///\n/// let key = ThreadKey::get().unwrap();\n/// let data = [Mutex::new(1), Mutex::new(3), Mutex::new(8)];\n/// let locks = OwnedLockCollection::new(data);\n/// let mut ctx = locks.context();\n/// let mut iter = ctx.iter(key);\n///\n/// let mut sum = 0;\n/// while let Some(item) = iter.lock_next() {\n/// sum += **item;\n/// }\n///\n/// assert_eq!(sum, 12);\n/// ```\n///\n/// [`OwnedLockCollection::context`]: crate::collection::OwnedLockCollection::context\npub struct LockContext\u003c'l, L\u003e {\n\tkey: Option\u003cThreadKey\u003e,\n\tlockable: \u0026'l L,\n}\n\n/// Iterates through a collection of locks, allowing for partial allocation of\n/// locks, or for some locks to be skipped.\n///\n/// Sometimes, partial allocation of locks is useful. For example, you may\n/// want to acquire a lock on the first element of a list before deciding if\n/// the second element should be locked. If the list is iterable, then a\n/// [`LockingIterator`] is capable of doing exactly that.\n///\n/// A [`LockingIterator`] can be created by calling the [`LockContext::iter`]\n/// method.\n///\n/// # Example\n///\n/// ```\n/// use happylock::{Mutex, ThreadKey};\n/// use happylock::collection::OwnedLockCollection;\n///\n/// let key = ThreadKey::get().unwrap();\n/// let data = [Mutex::new(1), Mutex::new(3), Mutex::new(8)];\n/// let locks = OwnedLockCollection::new(data);\n/// let mut ctx = locks.context();\n/// let mut iter = ctx.iter(key);\n///\n/// let mut sum = 0;\n/// while let Some(item) = iter.lock_next() {\n/// sum += **item;\n/// }\n///\n/// assert_eq!(sum, 12);\n/// ```\npub struct LockingIterator\u003c'context, I, Outer = ()\u003e {\n\tkey: \u0026'context ThreadKey,\n\titerator: I,\n\touter: Outer,\n}\n\n/// Iterates through a tuple of locks, requiring that elements are only locked\n/// before any successive elements are locked.\n///\n/// Sometimes, partial allocation of locks is useful. For example, you may want\n/// to acquire a lock on one item before deciding if the second item should be\n/// locked. If the locks can be organized into a tuple, [`LockingTuple`] is\n/// capable of doing exactly that.\n///\n/// A [`LockingTuple`] can be created by calling the [`LockContext::iter`]\n/// method.\n///\n/// # Example\n///\n/// ```\n/// use happylock::{Mutex, ThreadKey};\n/// use happylock::collection::OwnedLockCollection;\n///\n/// let key = ThreadKey::get().unwrap();\n/// let data = (Mutex::new(true), Mutex::new(42), Mutex::new(67));\n/// let locks = OwnedLockCollection::new(data);\n/// let mut ctx = locks.context();\n/// let tuple = ctx.tuple(key);\n///\n/// let (use_other, tuple) = tuple.lock_0();\n/// let number = if **use_other {\n/// tuple.lock_2().0\n/// } else {\n/// tuple.lock_1().0\n/// };\n/// assert_eq!(**number, 67);\n/// ```\npub struct LockingTuple\u003c'context, L, C, Outer = ()\u003e {\n\t_lockable: PhantomData\u003cL\u003e,\n\tkey: \u0026'context ThreadKey,\n\ttuple: \u0026'context C,\n\touter: Outer,\n}\n\n/// An RAII implementation of a “scoped lock”. When this structure\n/// is dropped (falls out of scope), the lock will be unlocked.\n///\n/// The data protected by the mutex can be accessed through this guard via its\n/// [`Deref`] and [`DerefMut`] implementations.\n///\n/// This is created by calling the [`lock`] and [`try_lock`] methods on [`Mutex`]\n///\n/// Unlike other guards in this crate, this guard holds a reference to a\n/// [`ThreadKey`], which is stored in the [`LockContext`]. This ensures that\n/// context cannot be dropped until all guards created with the context are\n/// dropped. The `ThreadKey` can be re-acquired by calling\n/// [`LockContext::unlock`].\n///\n/// [`Mutex`]: `crate::mutex::Mutex`\n/// [`Deref`]: `std::ops::Deref`\n/// [`DerefMut`]: `std::ops::DerefMut`\n/// [`lock`]: `crate::mutex::Mutex::lock`\n/// [`try_lock`]: `crate::Mutex::try_lock`\npub struct ContextGuard\u003c'a, Guard, Key\u003e {\n\t_key: \u0026'a Key,\n\tguard: Guard,\n}\n\n#[cfg(test)]\nmod tests {\n\tuse crate::{\n\t\tcollection::OwnedLockCollection, context::iterator::TryLockNextError, Mutex, RwLock,\n\t\tThreadKey,\n\t};\n\n\t#[test]\n\tfn display_works_for_guard() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new((Mutex::new(\"Hello, world!\"),));\n\t\tlet mut context = collection.context();\n\t\tlet tuple = context.tuple(key);\n\t\tlet (guard, _) = tuple.lock_0();\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn try_lock_mut_works_single_element_tuple() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new((RwLock::new(42),));\n\t\tlet mut context = collection.context();\n\t\tlet mut tuple = context.tuple(key);\n\t\tlet mut guard = tuple.try_lock_mut_0().unwrap();\n\t\t**guard = 67;\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 context = collection.context();\n\t\t\t\tlet mut tuple = context.tuple(key);\n\t\t\t\tlet guard = tuple.try_read_mut_0();\n\t\t\t\tassert!(guard.is_none());\n\t\t\t});\n\t\t});\n\t\tdrop(guard);\n\n\t\tlet result = tuple.try_read_mut_0().unwrap();\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 context = collection.context();\n\t\t\t\tlet mut tuple = context.tuple(key);\n\t\t\t\tlet guard = tuple.try_lock_mut_0();\n\t\t\t\tassert!(guard.is_none());\n\t\t\t\tdrop(guard);\n\t\t\t\tlet guard = tuple.try_read_mut_0();\n\t\t\t\tassert!(guard.is_some());\n\t\t\t\tdrop(guard);\n\t\t\t});\n\t\t});\n\t\tassert_eq!(**result, 67);\n\t}\n\n\t#[test]\n\tfn try_lock_mut_works_double_element_tuple() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new((RwLock::new(42), RwLock::new(67)));\n\t\tlet mut context = collection.context();\n\t\tlet mut tuple = context.tuple(key);\n\n\t\tlet mut guard = tuple.try_lock_mut_0().unwrap();\n\t\t**guard *= 2;\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 context = collection.context();\n\t\t\t\tlet mut tuple = context.tuple(key);\n\t\t\t\tlet guard = tuple.try_read_mut_0();\n\t\t\t\tassert!(guard.is_none());\n\t\t\t});\n\t\t});\n\t\tdrop(guard);\n\t\tlet mut guard = tuple.try_lock_mut_1().unwrap();\n\t\t**guard *= 2;\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 context = collection.context();\n\t\t\t\tlet mut tuple = context.tuple(key);\n\t\t\t\tlet guard = tuple.try_read_mut_1();\n\t\t\t\tassert!(guard.is_none());\n\t\t\t});\n\t\t});\n\t\tdrop(guard);\n\n\t\tlet result = tuple.try_read_mut_0().unwrap();\n\t\tassert_eq!(**result, 84);\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 context = collection.context();\n\t\t\t\tlet mut tuple = context.tuple(key);\n\t\t\t\tlet guard = tuple.try_lock_mut_0();\n\t\t\t\tassert!(guard.is_none());\n\t\t\t\tdrop(guard);\n\t\t\t\tlet guard = tuple.try_read_mut_0();\n\t\t\t\tassert!(guard.is_some());\n\t\t\t\tdrop(guard);\n\t\t\t});\n\t\t});\n\t\tdrop(result);\n\t\tlet result = tuple.try_read_mut_1().unwrap();\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 context = collection.context();\n\t\t\t\tlet mut tuple = context.tuple(key);\n\t\t\t\tlet guard = tuple.try_lock_mut_1();\n\t\t\t\tassert!(guard.is_none());\n\t\t\t\tdrop(guard);\n\t\t\t\tlet guard = tuple.try_read_mut_1();\n\t\t\t\tassert!(guard.is_some());\n\t\t\t\tdrop(guard);\n\t\t\t});\n\t\t});\n\t\tassert_eq!(**result, 134);\n\t}\n\n\t#[test]\n\tfn try_lock_mut_works_triple_element_tuple() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = OwnedLockCollection::new((RwLock::new(1), RwLock::new(2), RwLock::new(3)));\n\t\tlet mut context = collection.context();\n\t\tlet mut tuple = context.tuple(key);\n\n\t\tlet mut guard = tuple.try_lock_mut_0().unwrap();\n\t\t**guard *= 2;\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 context = collection.context();\n\t\t\t\tlet mut tuple = context.tuple(key);\n\t\t\t\tlet guard = tuple.try_read_mut_0();\n\t\t\t\tassert!(guard.is_none());\n\t\t\t});\n\t\t});\n\t\tdrop(guard);\n\t\tlet mut guard = tuple.try_lock_mut_1().unwrap();\n\t\t**guard *= 2;\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 context = collection.context();\n\t\t\t\tlet mut tuple = context.tuple(key);\n\t\t\t\tlet guard = tuple.try_read_mut_1();\n\t\t\t\tassert!(guard.is_none());\n\t\t\t});\n\t\t});\n\t\tdrop(guard);\n\t\tlet mut guard = tuple.try_lock_mut_2().unwrap();\n\t\t**guard *= 2;\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 context = collection.context();\n\t\t\t\tlet mut tuple = context.tuple(key);\n\t\t\t\tlet guard = tuple.try_read_mut_2();\n\t\t\t\tassert!(guard.is_none());\n\t\t\t});\n\t\t});\n\t\tdrop(guard);\n\n\t\tlet result = tuple.try_read_mut_0().unwrap();\n\t\tassert_eq!(**result, 2);\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 context = collection.context();\n\t\t\t\tlet mut tuple = context.tuple(key);\n\t\t\t\tlet guard = tuple.try_lock_mut_0();\n\t\t\t\tassert!(guard.is_none());\n\t\t\t\tdrop(guard);\n\t\t\t\tlet guard = tuple.try_read_mut_0();\n\t\t\t\tassert!(guard.is_some());\n\t\t\t\tdrop(guard);\n\t\t\t});\n\t\t});\n\t\tdrop(result);\n\t\tlet result = tuple.try_read_mut_1().unwrap();\n\t\tassert_eq!(**result, 4);\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 context = collection.context();\n\t\t\t\tlet mut tuple = context.tuple(key);\n\t\t\t\tlet guard = tuple.try_lock_mut_1();\n\t\t\t\tassert!(guard.is_none());\n\t\t\t\tdrop(guard);\n\t\t\t\tlet guard = tuple.try_read_mut_1();\n\t\t\t\tassert!(guard.is_some());\n\t\t\t\tdrop(guard);\n\t\t\t});\n\t\t});\n\t\tdrop(result);\n\t\tlet result = tuple.try_read_mut_2().unwrap();\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 context = collection.context();\n\t\t\t\tlet mut tuple = context.tuple(key);\n\t\t\t\tlet guard = tuple.try_lock_mut_2();\n\t\t\t\tassert!(guard.is_none());\n\t\t\t\tdrop(guard);\n\t\t\t\tlet guard = tuple.try_read_mut_2();\n\t\t\t\tassert!(guard.is_some());\n\t\t\t\tdrop(guard);\n\t\t\t});\n\t\t});\n\t\tassert_eq!(**result, 6);\n\t}\n\n\t#[test]\n\tfn basic_iteration() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet data = [Mutex::new(1), Mutex::new(3), Mutex::new(8)];\n\t\tlet locks = OwnedLockCollection::new(data);\n\t\tlet mut ctx = locks.context();\n\t\tlet mut iter = ctx.iter(key);\n\n\t\tlet item = iter.lock_next().unwrap();\n\t\tassert_eq!(**item, 1);\n\t\tlet item = iter.lock_next().unwrap();\n\t\tassert_eq!(**item, 3);\n\t\tlet item = iter.lock_next().unwrap();\n\t\tassert_eq!(**item, 8);\n\t\tassert!(iter.lock_next().is_none());\n\t}\n\n\t#[test]\n\tfn recurse_tuple_of_lists() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet data = (\n\t\t\t[Mutex::new(1), Mutex::new(2), Mutex::new(3)],\n\t\t\tMutex::new(true),\n\t\t);\n\t\tlet locks = OwnedLockCollection::new(data);\n\t\tlet mut ctx = locks.context();\n\t\tlet tuple = ctx.tuple(key);\n\t\tlet mut iter = tuple.recurse_0_iter();\n\n\t\tlet item = iter.lock_next().unwrap();\n\t\tassert_eq!(**item, 1);\n\t\tlet item = iter.lock_next().unwrap();\n\t\tassert_eq!(**item, 2);\n\t\tlet item = iter.lock_next().unwrap();\n\t\tassert_eq!(**item, 3);\n\t\tassert!(iter.lock_next().is_none());\n\n\t\tlet tuple = iter.exit();\n\t\tlet (should_assert, _) = tuple.lock_1();\n\t\tassert!(**should_assert);\n\t}\n\n\t#[test]\n\tfn recurse_list_of_lists() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet data = [\n\t\t\t[Mutex::new(1), Mutex::new(2), Mutex::new(3)],\n\t\t\t[Mutex::new(4), Mutex::new(5), Mutex::new(6)],\n\t\t];\n\t\tlet locks = OwnedLockCollection::new(data);\n\t\tlet mut ctx = locks.context();\n\t\tlet mut iter = ctx.iter(key);\n\n\t\tlet mut list = iter.recurse_next().unwrap();\n\t\tlet item = list.lock_next().unwrap();\n\t\tassert_eq!(**item, 1);\n\t\tlet item = list.lock_next().unwrap();\n\t\tassert_eq!(**item, 2);\n\t\tlet item = list.lock_next().unwrap();\n\t\tassert_eq!(**item, 3);\n\t\tassert!(list.lock_next().is_none());\n\t\titer = list.exit();\n\t\tlet mut list = iter.recurse_next().unwrap();\n\t\tlet item = list.lock_next().unwrap();\n\t\tassert_eq!(**item, 4);\n\t\tlet item = list.lock_next().unwrap();\n\t\tassert_eq!(**item, 5);\n\t\tlet item = list.lock_next().unwrap();\n\t\tassert_eq!(**item, 6);\n\t\tassert!(list.lock_next().is_none());\n\t}\n\n\t#[test]\n\tfn recurse_last_list_of_lists() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet data = [\n\t\t\t[Mutex::new(1), Mutex::new(2), Mutex::new(3)],\n\t\t\t[Mutex::new(4), Mutex::new(5), Mutex::new(6)],\n\t\t];\n\t\tlet locks = OwnedLockCollection::new(data);\n\t\tlet mut ctx = locks.context();\n\t\tlet iter = ctx.iter(key);\n\n\t\tlet mut list = iter.recurse_last().unwrap();\n\t\tlet item = list.lock_next().unwrap();\n\t\tassert_eq!(**item, 4);\n\t\tlet item = list.lock_next().unwrap();\n\t\tassert_eq!(**item, 5);\n\t\tlet item = list.lock_next().unwrap();\n\t\tassert_eq!(**item, 6);\n\t\tassert!(list.lock_next().is_none());\n\t}\n\n\t#[test]\n\tfn recurse_last_list_of_empty_list() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet data: [[Mutex\u003ci32\u003e; 0]; 0] = [];\n\t\tlet locks = OwnedLockCollection::new(data);\n\t\tlet mut ctx = locks.context();\n\t\tlet iter = ctx.iter(key);\n\n\t\tlet list = iter.recurse_last();\n\t\tassert!(list.is_none())\n\t}\n\n\t#[test]\n\tfn recurse_list_of_tuples() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet data = [\n\t\t\t(Mutex::new(true), Mutex::new(1)),\n\t\t\t(Mutex::new(false), Mutex::new(2)),\n\t\t\t(Mutex::new(true), Mutex::new(3)),\n\t\t];\n\t\tlet locks = OwnedLockCollection::new(data);\n\t\tlet mut ctx = locks.context();\n\t\tlet mut iter = ctx.iter(key);\n\n\t\tlet tuple = iter.recurse_next_tuple().unwrap();\n\t\tlet (should_count, mut tuple) = tuple.lock_0();\n\t\tassert!(**should_count);\n\t\tlet num = tuple.lock_mut_1();\n\t\tassert_eq!(**num, 1);\n\t\tdrop(num);\n\t\titer = tuple.exit();\n\t\tlet tuple = iter.recurse_next_tuple().unwrap();\n\t\tlet (should_count, mut tuple) = tuple.lock_0();\n\t\tassert!(!**should_count);\n\t\tlet num = tuple.lock_mut_1();\n\t\tassert_eq!(**num, 2);\n\t\tdrop(num);\n\t\titer = tuple.exit();\n\t\tlet tuple = iter.recurse_next_tuple().unwrap();\n\t\tlet (should_count, mut tuple) = tuple.lock_0();\n\t\tassert!(**should_count);\n\t\tlet num = tuple.lock_mut_1();\n\t\tassert_eq!(**num, 3);\n\t\tdrop(num);\n\t\titer = tuple.exit();\n\t\tassert!(iter.recurse_next_tuple().is_none());\n\t}\n\n\t#[test]\n\tfn recurse_last_list_of_tuples() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet data = [\n\t\t\t(Mutex::new(true), Mutex::new(1)),\n\t\t\t(Mutex::new(false), Mutex::new(2)),\n\t\t\t(Mutex::new(true), Mutex::new(3)),\n\t\t];\n\t\tlet locks = OwnedLockCollection::new(data);\n\t\tlet mut ctx = locks.context();\n\t\tlet iter = ctx.iter(key);\n\n\t\tlet tuple = iter.recurse_last_tuple().unwrap();\n\t\tlet (should_count, mut tuple) = tuple.lock_0();\n\t\tif **should_count {\n\t\t\tlet num = tuple.lock_mut_1();\n\t\t\tassert_eq!(**num, 3);\n\t\t} else {\n\t\t\tpanic!();\n\t\t}\n\t}\n\n\t#[test]\n\tfn recurse_last_of_empty_list_of_tuples() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet data: [(Mutex\u003cbool\u003e, Mutex\u003ci32\u003e); 0] = [];\n\t\tlet locks = OwnedLockCollection::new(data);\n\t\tlet mut ctx = locks.context();\n\t\tlet iter = ctx.iter(key);\n\n\t\tlet tuple = iter.recurse_last_tuple();\n\t\tassert!(tuple.is_none());\n\t}\n\n\t#[test]\n\tfn lock_last_of_list() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet data = [Mutex::new(1), Mutex::new(3), Mutex::new(8)];\n\t\tlet locks = OwnedLockCollection::new(data);\n\t\tlet mut ctx = locks.context();\n\t\tlet iter = ctx.iter(key);\n\t\tlet last = iter.lock_last().unwrap();\n\t\tassert_eq!(**last, 8);\n\t}\n\n\t#[test]\n\tfn try_lock_next_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet data = [Mutex::new(1), Mutex::new(3), Mutex::new(8)];\n\t\tlet locks = OwnedLockCollection::new(data);\n\t\tlet mut ctx = locks.context();\n\t\tlet mut iter = ctx.iter(key).peekable();\n\n\t\tlet item = iter.try_lock_next().unwrap();\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 context = locks.context();\n\t\t\t\tlet mut iter = context.iter(key).peekable();\n\t\t\t\tlet guard = iter.try_lock_next();\n\t\t\t\tassert_eq!(guard.unwrap_err(), TryLockNextError::WouldBlock);\n\t\t\t});\n\t\t});\n\t\tassert_eq!(**item, 1);\n\t\tlet item = iter.try_lock_next().unwrap();\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 context = locks.context();\n\t\t\t\tlet mut iter = context.iter(key).peekable();\n\t\t\t\titer.skip_next();\n\t\t\t\tlet guard = iter.try_lock_next();\n\t\t\t\tassert_eq!(guard.unwrap_err(), TryLockNextError::WouldBlock);\n\t\t\t});\n\t\t});\n\t\tassert_eq!(**item, 3);\n\t\tlet item = iter.try_lock_next().unwrap();\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 context = locks.context();\n\t\t\t\tlet mut iter = context.iter(key).peekable();\n\t\t\t\titer.skip_mut(2);\n\t\t\t\tlet guard = iter.try_lock_next();\n\t\t\t\tassert_eq!(guard.unwrap_err(), TryLockNextError::WouldBlock);\n\t\t\t});\n\t\t});\n\t\tassert_eq!(**item, 8);\n\t\tassert_eq!(\n\t\t\titer.try_lock_next().unwrap_err(),\n\t\t\tTryLockNextError::FinishedIteration\n\t\t);\n\t}\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":[648352,648465,648785,648800,647904,648653,648337,648053,648913,648672,648201,648224,648080,648480],"length":1,"stats":{"Line":202}},{"line":8,"address":[630034,629769,629625,630802,629467,630674,630418,629906,630162,630930,630546,630290,631058],"length":1,"stats":{"Line":176}},{"line":9,"address":[981980,981559,982296,982584,981735,982624,982075,982140,981217,982752,982608,981620,982480,982768,981143,981915,982860,981351,982988,982896,982464,982440,982884,981425,981796,982728,982336,982192,983012,982320],"length":1,"stats":{"Line":407}},{"line":10,"address":[665827,665571,666595,666995,665955,666339,665443,667379,667635,666467,667507,667763,666083,666867,666717,666211,667123,665699,667251],"length":1,"stats":{"Line":27}},{"line":11,"address":[620573,621485,621869,621997,620445,621613,620189,620061,621357,620829,621229,620317,620701,621085,620957,621741],"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 scoped 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// 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":[976776],"length":1,"stats":{"Line":21}},{"line":55,"address":[978240],"length":1,"stats":{"Line":10}},{"line":58,"address":[945445],"length":1,"stats":{"Line":33}},{"line":77,"address":[996736],"length":1,"stats":{"Line":21}},{"line":80,"address":[989888],"length":1,"stats":{"Line":40}},{"line":81,"address":[1002121],"length":1,"stats":{"Line":21}},{"line":97,"address":[976736],"length":1,"stats":{"Line":20}},{"line":98,"address":[976325],"length":1,"stats":{"Line":21}},{"line":103,"address":[976720],"length":1,"stats":{"Line":12}},{"line":104,"address":[850757],"length":1,"stats":{"Line":12}}],"covered":10,"coverable":10},{"path":["/","home","botahamec","Projects","happylock","src","lib.rs"],"content":"#![warn(clippy::pedantic)]\n#![warn(clippy::nursery)]\n#![warn(clippy::cargo)]\n#![warn(clippy::allow_attributes)]\n#![warn(clippy::as_pointer_underscore)]\n#![warn(clippy::cognitive_complexity)]\n#![warn(clippy::dbg_macro)]\n#![warn(clippy::error_impl_error)]\n#![warn(clippy::exit)]\n#![warn(clippy::fn_to_numeric_cast_any)]\n#![warn(clippy::infinite_loop)]\n#![warn(clippy::lossy_float_literal)]\n#![warn(clippy::mixed_read_write_in_expression)]\n#![warn(clippy::mod_module_files)]\n#![warn(clippy::needless_raw_strings)]\n#![warn(clippy::non_zero_suggestions)]\n#![warn(clippy::print_stdout)]\n#![warn(clippy::print_stderr)]\n#![warn(clippy::redundant_test_prefix)]\n#![warn(clippy::redundant_type_annotations)]\n#![warn(clippy::string_add)]\n#![warn(clippy::string_lit_chars_any)]\n#![warn(clippy::tests_outside_test_module)]\n#![warn(clippy::todo)]\n#![warn(clippy::try_err)]\n#![warn(clippy::unimplemented)]\n#![warn(clippy::unnecessary_safety_comment)]\n#![warn(clippy::unnecessary_safety_doc)]\n#![warn(clippy::unseparated_literal_suffix)]\n#![warn(clippy::unused_result_ok)]\n#![warn(clippy::unused_trait_names)]\n#![warn(clippy::unwrap_in_result)]\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 context;\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 [`context::LockContext`]\npub type LockContext\u003c'l, L\u003e = context::LockContext\u003c'l, L\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":220,"address":[841568],"length":1,"stats":{"Line":10}},{"line":231,"address":[842054],"length":1,"stats":{"Line":10}}],"covered":2,"coverable":2},{"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 when using these methods correctly.\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 lock is\n\t/// held.\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\tunsafe fn raw_write(\u0026self);\n\n\t/// Attempt to lock without blocking.\n\t///\n\t/// Returns `true` if successful, `false` otherwise.\n\t///\n\t/// # Safety\n\t///\n\t/// It is undefined behavior to use this without ownership or mutable\n\t/// access to the [`ThreadKey`], which should last as long as the lock is\n\t/// held.\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool;\n\n\t/// Releases the lock\n\t///\n\t/// # Safety\n\t///\n\t/// It is undefined behavior to use this if the lock is not acquired by the\n\t/// calling thread.\n\tunsafe fn raw_unlock_write(\u0026self);\n\n\t/// Blocks until the data the lock protects can be safely read.\n\t///\n\t/// Some locks, but not all, will allow multiple readers at once. If\n\t/// multiple readers are allowed for a [`Lockable`] type, then the\n\t/// [`Sharable`] marker trait should be implemented.\n\t///\n\t/// # Safety\n\t///\n\t/// It is undefined behavior to use this without ownership or mutable\n\t/// access to the [`ThreadKey`], which should last as long as the lock is\n\t/// held.\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 lock is\n\t/// held.\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 held by the\n\t/// calling thread.\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.\n///\n/// The list returned by `get_ptrs` must contain any lock which could possibly\n/// be referenced in another collection.\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\t/// A reference to the protected data\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/// Returns a mutable reference to the data protected by this lock.\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 unlocked\n\t/// until the lifetime of this reference ends.\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\t/// An immutable reference to the protected data\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/// Creates an immutable reference to the data that is protected by this lock.\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 the lifetime of this reference ends.\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.\n///\n/// The implementation of [`Lockable::get_ptrs`] must return the locks in the\n/// same order that they would be locked in if this lockable were passed into a\n/// [`LockContext`].\n///\n/// [`LockContext`]: `crate::context::LockContext`\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($(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);\ntuple_impls!(A B C D E F G H, 0 1 2 3 4 5 6 7);\ntuple_impls!(A B C D E F G H I, 0 1 2 3 4 5 6 7 8);\ntuple_impls!(A B C D E F G H I J, 0 1 2 3 4 5 6 7 8 9);\ntuple_impls!(A B C D E F G H I J K, 0 1 2 3 4 5 6 7 8 9 10);\ntuple_impls!(A B C D E F G H I J K L, 0 1 2 3 4 5 6 7 8 9 10 11);\ntuple_impls!(A B C D E F G H I J K L M, 0 1 2 3 4 5 6 7 8 9 10 11 12);\ntuple_impls!(A B C D E F G H I J K L M N, 0 1 2 3 4 5 6 7 8 9 10 11 12 13);\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\nimpl\u003cT: LockableIntoInner + 'static\u003e LockableIntoInner for Box\u003c[T]\u003e {\n\ttype Inner = Box\u003c[T::Inner]\u003e;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tSelf::into_iter(self)\n\t\t\t.map(LockableIntoInner::into_inner)\n\t\t\t.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: 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\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\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\nimpl\u003cT: LockableGetMut + 'static\u003e LockableGetMut for Vec\u003cT\u003e {\n\ttype Inner\u003c'a\u003e\n\t\t= Box\u003c[T::Inner\u003c'a\u003e]\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tself.iter_mut().map(LockableGetMut::get_mut).collect()\n\t}\n}\n\nimpl\u003cT: LockableIntoInner\u003e LockableIntoInner for Vec\u003cT\u003e {\n\ttype Inner = Box\u003c[T::Inner]\u003e;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tself.into_iter()\n\t\t\t.map(LockableIntoInner::into_inner)\n\t\t\t.collect()\n\t}\n}\n\nunsafe impl\u003cT: OwnedLockable\u003e OwnedLockable for Vec\u003cT\u003e {}\n\n#[cfg(test)]\nmod tests {\n\tuse super::*;\n\tuse crate::{LockCollection, Mutex, RwLock, ThreadKey};\n\n\t#[test]\n\tfn mut_ref_get_ptrs() {\n\t\tlet mut rwlock = RwLock::new(5);\n\t\tlet mutref = \u0026mut rwlock;\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tmutref.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tassert!(std::ptr::addr_eq(lock_ptrs[0], mutref));\n\t}\n\n\t#[test]\n\tfn array_get_ptrs_empty() {\n\t\tlet locks: [Mutex\u003c()\u003e; 0] = [];\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert!(lock_ptrs.is_empty());\n\t}\n\n\t#[test]\n\tfn array_get_ptrs_length_one() {\n\t\tlet locks: [Mutex\u003ci32\u003e; 1] = [Mutex::new(1)];\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[0], locks[0].raw())) }\n\t}\n\n\t#[test]\n\tfn array_get_ptrs_length_two() {\n\t\tlet locks: [Mutex\u003ci32\u003e; 2] = [Mutex::new(1), Mutex::new(2)];\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 2);\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[0], locks[0].raw())) }\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[1], locks[1].raw())) }\n\t}\n\n\t#[test]\n\tfn vec_get_ptrs_empty() {\n\t\tlet locks: Vec\u003cMutex\u003c()\u003e\u003e = Vec::new();\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert!(lock_ptrs.is_empty());\n\t}\n\n\t#[test]\n\tfn vec_get_ptrs_length_one() {\n\t\tlet locks: Vec\u003cMutex\u003ci32\u003e\u003e = vec![Mutex::new(1)];\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[0], locks[0].raw())) }\n\t}\n\n\t#[test]\n\tfn vec_get_ptrs_length_two() {\n\t\tlet locks: Vec\u003cMutex\u003ci32\u003e\u003e = vec![Mutex::new(1), Mutex::new(2)];\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 2);\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[0], locks[0].raw())) }\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[1], locks[1].raw())) }\n\t}\n\n\t#[test]\n\tfn vec_as_mut() {\n\t\tlet mut locks: Vec\u003cMutex\u003ci32\u003e\u003e = vec![Mutex::new(1), Mutex::new(2)];\n\t\tlet lock_ptrs = LockableGetMut::get_mut(\u0026mut locks);\n\n\t\tassert_eq!(lock_ptrs.len(), 2);\n\t\tassert_eq!(*lock_ptrs[0], 1);\n\t\tassert_eq!(*lock_ptrs[1], 2);\n\t}\n\n\t#[test]\n\tfn vec_into_inner() {\n\t\tlet locks: Vec\u003cMutex\u003ci32\u003e\u003e = vec![Mutex::new(1), Mutex::new(2)];\n\t\tlet lock_ptrs = LockableIntoInner::into_inner(locks);\n\n\t\tassert_eq!(lock_ptrs.len(), 2);\n\t\tassert_eq!(lock_ptrs[0], 1);\n\t\tassert_eq!(lock_ptrs[1], 2);\n\t}\n\n\t#[test]\n\tfn vec_guard_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = vec![RwLock::new(1), RwLock::new(2)];\n\t\tlet collection = LockCollection::new(locks);\n\n\t\tlet mut guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], 1);\n\t\tassert_eq!(*guard[1], 2);\n\t\t*guard[0] = 3;\n\n\t\tlet key = LockCollection::\u003cVec\u003cRwLock\u003c_\u003e\u003e\u003e::unlock(guard);\n\t\tlet guard = collection.read(key);\n\t\tassert_eq!(*guard[0], 3);\n\t\tassert_eq!(*guard[1], 2);\n\t}\n\n\t#[test]\n\tfn vec_data_mut() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = vec![Mutex::new(1), Mutex::new(2)];\n\t\tlet collection = LockCollection::new(mutexes);\n\t\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 1);\n\t\t\tassert_eq!(*guard[1], 2);\n\t\t\t*guard[0] = 3;\n\t\t});\n\n\t\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 3);\n\t\t\tassert_eq!(*guard[1], 2);\n\t\t})\n\t}\n\n\t#[test]\n\tfn vec_data_ref() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = vec![RwLock::new(1), RwLock::new(2)];\n\t\tlet collection = LockCollection::new(mutexes);\n\t\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 1);\n\t\t\tassert_eq!(*guard[1], 2);\n\t\t\t*guard[0] = 3;\n\t\t});\n\n\t\tcollection.scoped_read(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 3);\n\t\t\tassert_eq!(*guard[1], 2);\n\t\t})\n\t}\n\n\t#[test]\n\tfn box_get_ptrs_empty() {\n\t\tlet locks: Box\u003c[Mutex\u003c()\u003e]\u003e = Box::from([]);\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert!(lock_ptrs.is_empty());\n\t}\n\n\t#[test]\n\tfn box_get_ptrs_length_one() {\n\t\tlet locks: Box\u003c[Mutex\u003ci32\u003e]\u003e = vec![Mutex::new(1)].into_boxed_slice();\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[0], locks[0].raw())) }\n\t}\n\n\t#[test]\n\tfn box_get_ptrs_length_two() {\n\t\tlet locks: Box\u003c[Mutex\u003ci32\u003e]\u003e = vec![Mutex::new(1), Mutex::new(2)].into_boxed_slice();\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tlocks.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 2);\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[0], locks[0].raw())) }\n\t\tunsafe { assert!(std::ptr::addr_eq(lock_ptrs[1], locks[1].raw())) }\n\t}\n\n\t#[test]\n\tfn box_as_mut() {\n\t\tlet mut locks: Box\u003c[Mutex\u003ci32\u003e]\u003e = vec![Mutex::new(1), Mutex::new(2)].into_boxed_slice();\n\t\tlet lock_ptrs = LockableGetMut::get_mut(\u0026mut locks);\n\n\t\tassert_eq!(lock_ptrs.len(), 2);\n\t\tassert_eq!(*lock_ptrs[0], 1);\n\t\tassert_eq!(*lock_ptrs[1], 2);\n\t}\n\n\t#[test]\n\tfn box_guard_mut() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet x = [Mutex::new(1), Mutex::new(2)];\n\t\tlet collection: LockCollection\u003cBox\u003c[Mutex\u003c_\u003e]\u003e\u003e = LockCollection::new(Box::new(x));\n\n\t\tlet mut guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], 1);\n\t\tassert_eq!(*guard[1], 2);\n\t\t*guard[0] = 3;\n\n\t\tlet key = LockCollection::\u003cBox\u003c[Mutex\u003c_\u003e]\u003e\u003e::unlock(guard);\n\t\tlet guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], 3);\n\t\tassert_eq!(*guard[1], 2);\n\t}\n\n\t#[test]\n\tfn box_data_mut() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = vec![Mutex::new(1), Mutex::new(2)].into_boxed_slice();\n\t\tlet collection = LockCollection::new(mutexes);\n\t\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 1);\n\t\t\tassert_eq!(*guard[1], 2);\n\t\t\t*guard[0] = 3;\n\t\t});\n\n\t\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 3);\n\t\t\tassert_eq!(*guard[1], 2);\n\t\t});\n\t}\n\n\t#[test]\n\tfn box_guard_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet locks = [RwLock::new(1), RwLock::new(2)];\n\t\tlet collection: LockCollection\u003cBox\u003c[RwLock\u003c_\u003e]\u003e\u003e = LockCollection::new(Box::new(locks));\n\n\t\tlet mut guard = collection.lock(key);\n\t\tassert_eq!(*guard[0], 1);\n\t\tassert_eq!(*guard[1], 2);\n\t\t*guard[0] = 3;\n\n\t\tlet key = LockCollection::\u003cBox\u003c[RwLock\u003c_\u003e]\u003e\u003e::unlock(guard);\n\t\tlet guard = collection.read(key);\n\t\tassert_eq!(*guard[0], 3);\n\t\tassert_eq!(*guard[1], 2);\n\t}\n\n\t#[test]\n\tfn box_data_ref() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet mutexes = vec![RwLock::new(1), RwLock::new(2)].into_boxed_slice();\n\t\tlet collection = LockCollection::new(mutexes);\n\t\tcollection.scoped_lock(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 1);\n\t\t\tassert_eq!(*guard[1], 2);\n\t\t\t*guard[0] = 3;\n\t\t});\n\n\t\tcollection.scoped_read(\u0026mut key, |guard| {\n\t\t\tassert_eq!(*guard[0], 3);\n\t\t\tassert_eq!(*guard[1], 2);\n\t\t});\n\t}\n\n\t#[test]\n\tfn box_into_inner() {\n\t\tlet locks = vec![Mutex::new(1), Mutex::new(2)].into_boxed_slice();\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","traces":[{"line":257,"address":[1606304],"length":1,"stats":{"Line":42}},{"line":258,"address":[625742,625774,625694],"length":1,"stats":{"Line":42}},{"line":261,"address":[672256,672304],"length":1,"stats":{"Line":9}},{"line":262,"address":[647109,647157],"length":1,"stats":{"Line":8}},{"line":265,"address":[646320],"length":1,"stats":{"Line":2}},{"line":266,"address":[646325],"length":1,"stats":{"Line":2}},{"line":281,"address":[682960,682976],"length":1,"stats":{"Line":3}},{"line":282,"address":[702165,702181,702149],"length":1,"stats":{"Line":3}},{"line":285,"address":[],"length":0,"stats":{"Line":0}},{"line":286,"address":[],"length":0,"stats":{"Line":0}},{"line":301,"address":[],"length":0,"stats":{"Line":1}},{"line":302,"address":[],"length":0,"stats":{"Line":1}},{"line":305,"address":[],"length":0,"stats":{"Line":0}},{"line":306,"address":[],"length":0,"stats":{"Line":0}},{"line":309,"address":[],"length":0,"stats":{"Line":0}},{"line":310,"address":[],"length":0,"stats":{"Line":0}},{"line":320,"address":[],"length":0,"stats":{"Line":0}},{"line":321,"address":[],"length":0,"stats":{"Line":0}},{"line":336,"address":[],"length":0,"stats":{"Line":0}},{"line":337,"address":[],"length":0,"stats":{"Line":0}},{"line":340,"address":[],"length":0,"stats":{"Line":0}},{"line":341,"address":[],"length":0,"stats":{"Line":0}},{"line":356,"address":[1621824],"length":1,"stats":{"Line":18}},{"line":357,"address":[1993640,1992568,1992504,1992744,1992920],"length":1,"stats":{"Line":17}},{"line":360,"address":[673599,673408,673605],"length":1,"stats":{"Line":8}},{"line":361,"address":[647968],"length":1,"stats":{"Line":7}},{"line":380,"address":[2161707,2161360],"length":1,"stats":{"Line":4}},{"line":381,"address":[1993487,1993718,1993382,1993804,1994038,1994156],"length":1,"stats":{"Line":8}},{"line":390,"address":[702416,702607,702613],"length":1,"stats":{"Line":2}},{"line":391,"address":[2161150,2161262,2161038],"length":1,"stats":{"Line":2}},{"line":429,"address":[613568],"length":1,"stats":{"Line":18}},{"line":430,"address":[2156370,2155201,2155186,2155938,2156385,2155953],"length":1,"stats":{"Line":36}},{"line":431,"address":[1985562,1986426,1985450,1986314,1984458,1985338,1984890,1986858],"length":1,"stats":{"Line":16}},{"line":435,"address":[],"length":0,"stats":{"Line":8}},{"line":438,"address":[2156059,2154942,2155307],"length":1,"stats":{"Line":9}},{"line":439,"address":[2154959,2156116,2155364,2155339,2154982,2156091],"length":1,"stats":{"Line":18}},{"line":440,"address":[],"length":0,"stats":{"Line":16}},{"line":443,"address":[1569177,1569417,1569369,1569225,1569129,1569104,1569200,1569536,1569392,1569513,1569248,1569344,1569152,1569488,1569273,1569561],"length":1,"stats":{"Line":28}},{"line":446,"address":[2155600],"length":1,"stats":{"Line":3}},{"line":447,"address":[],"length":0,"stats":{"Line":3}},{"line":448,"address":[],"length":0,"stats":{"Line":6}},{"line":449,"address":[1986038,1986099],"length":1,"stats":{"Line":6}},{"line":452,"address":[1986051],"length":1,"stats":{"Line":9}},{"line":462,"address":[1987120],"length":1,"stats":{"Line":2}},{"line":464,"address":[],"length":0,"stats":{"Line":2}},{"line":465,"address":[1987187,1987264],"length":1,"stats":{"Line":4}},{"line":466,"address":[2156801,2156726],"length":1,"stats":{"Line":4}},{"line":469,"address":[2156739],"length":1,"stats":{"Line":6}},{"line":477,"address":[],"length":0,"stats":{"Line":1}},{"line":479,"address":[],"length":0,"stats":{"Line":2}},{"line":480,"address":[],"length":0,"stats":{"Line":3}},{"line":481,"address":[1988127,1988050,1988286],"length":1,"stats":{"Line":3}},{"line":484,"address":[1988081],"length":1,"stats":{"Line":3}},{"line":500,"address":[613696],"length":1,"stats":{"Line":4}},{"line":501,"address":[],"length":0,"stats":{"Line":4}},{"line":502,"address":[2157364,2157979,2157094,2157339,2158004,2157071],"length":1,"stats":{"Line":8}},{"line":503,"address":[2157414,2157475,2157164,2158054,2157141,2158115],"length":1,"stats":{"Line":6}},{"line":506,"address":[606448,606473],"length":1,"stats":{"Line":10}},{"line":509,"address":[],"length":0,"stats":{"Line":1}},{"line":510,"address":[],"length":0,"stats":{"Line":1}},{"line":511,"address":[2157684,2157659],"length":1,"stats":{"Line":2}},{"line":512,"address":[],"length":0,"stats":{"Line":2}},{"line":515,"address":[2157747],"length":1,"stats":{"Line":5}},{"line":532,"address":[],"length":0,"stats":{"Line":4}},{"line":533,"address":[],"length":0,"stats":{"Line":8}},{"line":534,"address":[],"length":0,"stats":{"Line":4}},{"line":538,"address":[],"length":0,"stats":{"Line":2}},{"line":539,"address":[1569961,1570032,1569936,1570057],"length":1,"stats":{"Line":6}},{"line":542,"address":[],"length":0,"stats":{"Line":2}},{"line":543,"address":[1570080,1569984,1570009,1570105],"length":1,"stats":{"Line":6}},{"line":553,"address":[],"length":0,"stats":{"Line":1}},{"line":554,"address":[],"length":0,"stats":{"Line":1}},{"line":561,"address":[],"length":0,"stats":{"Line":1}},{"line":562,"address":[2246030],"length":1,"stats":{"Line":1}},{"line":563,"address":[],"length":0,"stats":{"Line":1}},{"line":579,"address":[],"length":0,"stats":{"Line":1}},{"line":580,"address":[2246088],"length":1,"stats":{"Line":3}},{"line":583,"address":[],"length":0,"stats":{"Line":1}},{"line":584,"address":[],"length":0,"stats":{"Line":3}},{"line":600,"address":[],"length":0,"stats":{"Line":4}},{"line":601,"address":[],"length":0,"stats":{"Line":8}},{"line":602,"address":[],"length":0,"stats":{"Line":4}},{"line":606,"address":[],"length":0,"stats":{"Line":2}},{"line":607,"address":[1191989,1192421],"length":1,"stats":{"Line":6}},{"line":610,"address":[],"length":0,"stats":{"Line":2}},{"line":611,"address":[],"length":0,"stats":{"Line":6}},{"line":626,"address":[1192624],"length":1,"stats":{"Line":1}},{"line":627,"address":[],"length":0,"stats":{"Line":3}},{"line":630,"address":[],"length":0,"stats":{"Line":1}},{"line":631,"address":[],"length":0,"stats":{"Line":3}},{"line":646,"address":[],"length":0,"stats":{"Line":2}},{"line":647,"address":[],"length":0,"stats":{"Line":2}},{"line":654,"address":[],"length":0,"stats":{"Line":1}},{"line":655,"address":[],"length":0,"stats":{"Line":1}},{"line":656,"address":[],"length":0,"stats":{"Line":1}}],"covered":83,"coverable":95},{"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 as _;\nuse crate::ThreadKey;\n\nuse super::{Mutex, MutexGuard, MutexRef};\n\n// These impls make things slightly easier because now you can use\n// `println!(\"{guard}\")` instead of `println!(\"{}\", *guard)`\n\n#[mutants::skip] // hashing involves RNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Hash + ?Sized, R: RawMutex\u003e Hash for MutexRef\u003c'_, T, R\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.deref().hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug + ?Sized, R: RawMutex\u003e Debug for MutexRef\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: Display + ?Sized, R: RawMutex\u003e Display for MutexRef\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e Drop for MutexRef\u003c'_, T, R\u003e {\n\tfn drop(\u0026mut self) {\n\t\t// safety: this guard is being destroyed, so the data cannot be\n\t\t// accessed without locking again\n\t\tunsafe { self.0.raw_unlock_write() }\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e Deref for MutexRef\u003c'_, T, R\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t// safety: this is the only type that can use `value`, and there's\n\t\t// a reference to this type, so there cannot be any mutable\n\t\t// references to this value.\n\t\tunsafe { \u0026*self.0.data.get() }\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e DerefMut for MutexRef\u003c'_, T, R\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t// safety: this is the only type that can use `value`, and we have a\n\t\t// mutable reference to this type, so there cannot be any other\n\t\t// references to this value.\n\t\tunsafe { \u0026mut *self.0.data.get() }\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e AsRef\u003cT\u003e for MutexRef\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e AsMut\u003cT\u003e for MutexRef\u003c'_, T, R\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself\n\t}\n}\n\nimpl\u003c'a, T: ?Sized, R: RawMutex\u003e MutexRef\u003c'a, T, R\u003e {\n\t/// Creates a reference to the underlying data of a mutex without\n\t/// attempting to lock it or take ownership of the key. But it's also quite\n\t/// dangerous to drop.\n\tpub(crate) const unsafe fn new(mutex: \u0026'a Mutex\u003cT, R\u003e) -\u003e Self {\n\t\tSelf(mutex, PhantomData)\n\t}\n}\n\n// it's kinda annoying to re-implement some of this stuff on guards\n// there's nothing i can do about that\n\n#[mutants::skip] // hashing involves RNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Hash + ?Sized, R: RawMutex\u003e Hash for MutexGuard\u003c'_, T, R\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.deref().hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug + ?Sized, R: RawMutex\u003e Debug for MutexGuard\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: Display + ?Sized, R: RawMutex\u003e Display for MutexGuard\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e Deref for MutexGuard\u003c'_, T, R\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t\u0026self.mutex\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e DerefMut for MutexGuard\u003c'_, T, R\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t\u0026mut self.mutex\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e AsRef\u003cT\u003e for MutexGuard\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e AsMut\u003cT\u003e for MutexGuard\u003c'_, T, R\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself\n\t}\n}\n\nimpl\u003c'a, T: ?Sized, R: RawMutex\u003e MutexGuard\u003c'a, T, R\u003e {\n\t/// Create a guard to the given mutex. Undefined if multiple guards to the\n\t/// same mutex exist at once.\n\t#[must_use]\n\tpub(super) const unsafe fn new(mutex: \u0026'a Mutex\u003cT, R\u003e, thread_key: ThreadKey) -\u003e Self {\n\t\tSelf {\n\t\t\tmutex: MutexRef(mutex, PhantomData),\n\t\t\tthread_key,\n\t\t}\n\t}\n}\n\nunsafe impl\u003cT: ?Sized + Sync, R: RawMutex + Sync\u003e Sync for MutexRef\u003c'_, T, R\u003e {}\n","traces":[{"line":33,"address":[1981536],"length":1,"stats":{"Line":1}},{"line":34,"address":[],"length":0,"stats":{"Line":1}},{"line":39,"address":[606288,606272,606304],"length":1,"stats":{"Line":8}},{"line":42,"address":[],"length":0,"stats":{"Line":9}},{"line":49,"address":[1983440,1983376,1983408],"length":1,"stats":{"Line":4}},{"line":53,"address":[626421],"length":1,"stats":{"Line":4}},{"line":58,"address":[1983840,1983808],"length":1,"stats":{"Line":4}},{"line":62,"address":[1983845,1983813],"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":[647072,647088],"length":1,"stats":{"Line":7}},{"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":[647568],"length":1,"stats":{"Line":2}},{"line":122,"address":[647573],"length":1,"stats":{"Line":2}},{"line":127,"address":[1988400],"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":[558192],"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_write(\u0026self) {\n\t\tassert!(!self.poison.is_poisoned(), \"The mutex has been killed\");\n\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.lock(), || self.poison())\n\t}\n\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool {\n\t\tif self.poison.is_poisoned() {\n\t\t\treturn false;\n\t\t}\n\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.try_lock(), || self.poison())\n\t}\n\n\tunsafe fn raw_unlock_write(\u0026self) {\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.unlock(), || self.poison())\n\t}\n\n\t// this is the closest thing to a read we can get, but Sharable isn't\n\t// implemented for this\n\t#[mutants::skip]\n\t#[cfg(not(tarpaulin_include))]\n\tunsafe fn raw_read(\u0026self) {\n\t\tself.raw_write()\n\t}\n\n\t#[mutants::skip]\n\t#[cfg(not(tarpaulin_include))]\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tself.raw_try_write()\n\t}\n\n\t#[mutants::skip]\n\t#[cfg(not(tarpaulin_include))]\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tself.raw_unlock_write()\n\t}\n}\n\nunsafe impl\u003cT, R: RawMutex\u003e Lockable for Mutex\u003cT, R\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= MutexRef\u003c'g, T, R\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= \u0026'a mut T\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tptrs.push(self);\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tMutexRef::new(self)\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.data.get().as_mut().unwrap_unchecked()\n\t}\n}\n\nimpl\u003cT, R: RawMutex\u003e LockableIntoInner for Mutex\u003cT, R\u003e {\n\ttype Inner = T;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tself.into_inner()\n\t}\n}\n\nimpl\u003cT, R: RawMutex\u003e LockableGetMut for Mutex\u003cT, R\u003e {\n\ttype Inner\u003c'a\u003e\n\t\t= \u0026'a mut T\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tself.get_mut()\n\t}\n}\n\nunsafe impl\u003cT, R: RawMutex\u003e OwnedLockable for Mutex\u003cT, R\u003e {}\n\nimpl\u003cT, R: RawMutex\u003e Mutex\u003cT, R\u003e {\n\t/// Creates a `Mutex` in an unlocked state ready for use.\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\t/// Acquires a lock on the mutex, blocking until it is safe to do so, and then\n\t/// unlocks the mutex after the provided function returns.\n\t///\n\t/// This function is useful to ensure that a Mutex is never accidentally\n\t/// locked forever by leaking the `MutexGuard`. Even if the function panics,\n\t/// this function will gracefully notice the panic, and unlock the mutex.\n\t///\n\t/// # Panics\n\t///\n\t/// This function will panic if the provided function also panics. However,\n\t/// mutex will be safely unlocked in this case, allowing the mutex to be\n\t/// locked again later.\n\t///\n\t/// # Example\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, Mutex};\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let mutex = Mutex::new(42);\n\t///\n\t/// let x = mutex.scoped_lock(\u0026mut key, |number| {\n\t/// *number += 5;\n\t/// *number\n\t/// });\n\t/// assert_eq!(x, 47);\n\t/// ```\n\tpub fn scoped_lock\u003c'a, Ret\u003e(\n\t\t\u0026'a self,\n\t\tkey: impl Keyable,\n\t\tf: impl FnOnce(\u0026'a mut T) -\u003e Ret,\n\t) -\u003e Ret {\n\t\tunsafe {\n\t\t\t// safety: we have the key\n\t\t\tself.raw_write();\n\n\t\t\t// safety: the data has been locked\n\t\t\tlet r = handle_unwind(\n\t\t\t\t|| f(self.data.get().as_mut().unwrap_unchecked()),\n\t\t\t\t|| self.raw_unlock_write(),\n\t\t\t);\n\n\t\t\t// ensures the key is held long enough\n\t\t\tdrop(key);\n\n\t\t\t// safety: the mutex is still locked\n\t\t\tself.raw_unlock_write();\n\n\t\t\tr\n\t\t}\n\t}\n\n\t/// Attempts to acquire the `Mutex` without blocking, and then unlocks it once\n\t/// the provided function returns.\n\t///\n\t/// This function implements a non-blocking variant of [`scoped_lock`]. Unlike\n\t/// `scoped_lock`, if the mutex is not already unlocked, then the provided\n\t/// function will not run, and the given [`Keyable`] is returned.\n\t///\n\t/// # Errors\n\t///\n\t/// If the mutex is already locked, then the provided function will not run.\n\t/// `Err` is returned with the given key.\n\t///\n\t/// # Panics\n\t///\n\t/// If the provided function panics, then the panic will be bubbled up and\n\t/// rethrown. The mutex will also be gracefully unlocked, allowing the mutex\n\t/// to be locked again.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, ThreadKey};\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let mutex = Mutex::new(42);\n\t///\n\t/// let result = mutex.scoped_try_lock(\u0026mut key, |num| {\n\t/// *num\n\t/// });\n\t///\n\t/// match result {\n\t/// Ok(val) =\u003e println!(\"The number is {val}\"),\n\t/// Err(_) =\u003e unreachable!(),\n\t/// }\n\t/// ```\n\t///\n\t/// [`scoped_lock`]: Mutex::scoped_lock\n\tpub fn scoped_try_lock\u003c'a, Key: Keyable, Ret\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl FnOnce(\u0026'a mut T) -\u003e Ret,\n\t) -\u003e Result\u003cRet, Key\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the key\n\t\t\tif !self.raw_try_write() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: the data has been locked\n\t\t\tlet r = handle_unwind(\n\t\t\t\t|| f(self.data.get().as_mut().unwrap_unchecked()),\n\t\t\t\t|| self.raw_unlock_write(),\n\t\t\t);\n\n\t\t\t// ensures the key is held long enough\n\t\t\tdrop(key);\n\n\t\t\t// safety: the mutex is still locked\n\t\t\tself.raw_unlock_write();\n\n\t\t\tOk(r)\n\t\t}\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawMutex\u003e Mutex\u003cT, R\u003e {\n\t/// Acquires a mutex, blocking the current thread until it is able to do so.\n\t///\n\t/// This function will block the local thread until it is available to acquire\n\t/// the mutex. Upon returning, the thread is the only thread with the lock\n\t/// held. A [`MutexGuard`] is returned to allow a scoped unlock of this\n\t/// `Mutex`. When the guard goes out of scope, this `Mutex` will unlock.\n\t///\n\t/// Due to the requirement of a [`ThreadKey`] to call this function, it is not\n\t/// possible for this function to deadlock.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::thread;\n\t/// use happylock::{Mutex, ThreadKey};\n\t///\n\t/// let mutex = Mutex::new(0);\n\t///\n\t/// thread::scope(|s| {\n\t/// s.spawn(|| {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// *mutex.lock(key) = 10;\n\t/// });\n\t/// });\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// assert_eq!(*mutex.lock(key), 10);\n\t/// ```\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e MutexGuard\u003c'_, T, R\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_write();\n\n\t\t\t// safety: we just locked the mutex\n\t\t\tMutexGuard::new(self, key)\n\t\t}\n\t}\n\n\t/// Attempts to lock this `Mutex` without blocking.\n\t///\n\t/// If the lock could not be acquired at this time, then `Err` is returned.\n\t/// Otherwise, an RAII guard is returned. The lock will be unlocked when the\n\t/// guard 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 [`ThreadKey`].\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use std::thread;\n\t/// use happylock::{Mutex, ThreadKey};\n\t///\n\t/// let mutex = Mutex::new(0);\n\t///\n\t/// thread::scope(|s| {\n\t/// s.spawn(|| {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut lock = 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/// });\n\t/// });\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// assert_eq!(*mutex.lock(key), 10);\n\t/// ```\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e Result\u003cMutexGuard\u003c'_, T, R\u003e, ThreadKey\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the key to the mutex\n\t\t\tif self.raw_try_write() {\n\t\t\t\t// safety: we just locked the mutex\n\t\t\t\tOk(MutexGuard::new(self, key))\n\t\t\t} else {\n\t\t\t\tErr(key)\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Returns `true` if the mutex is currently locked\n\t#[cfg(test)]\n\tpub(crate) fn is_locked(\u0026self) -\u003e bool {\n\t\tself.raw.is_locked()\n\t}\n\n\t/// Lock without a [`ThreadKey`]. It is undefined behavior to do this without\n\t/// owning the [`ThreadKey`].\n\tpub(crate) unsafe fn try_lock_no_key(\u0026self) -\u003e Option\u003cMutexRef\u003c'_, T, R\u003e\u003e {\n\t\tself.raw_try_write().then_some(MutexRef(self, PhantomData))\n\t}\n\n\t/// Consumes the [`MutexGuard`], and consequently unlocks its `Mutex`.\n\t///\n\t/// This function is equivalent to calling [`drop`] on the guard, except that\n\t/// it returns the key that was used to create it. Alernatively, the guard\n\t/// will be automatically dropped when it goes out of scope.\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/// let guard = mutex.lock(key);\n\t/// assert_eq!(*guard, 20);\n\t/// ```\n\t#[must_use]\n\tpub fn unlock(guard: MutexGuard\u003c'_, T, R\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.mutex);\n\t\tguard.thread_key\n\t}\n}\n\nunsafe impl\u003cR: RawMutex + Send, T: ?Sized + Send\u003e Send for Mutex\u003cT, R\u003e {}\nunsafe impl\u003cR: RawMutex + Sync, T: ?Sized + Send\u003e Sync for Mutex\u003cT, R\u003e {}\n","traces":[{"line":16,"address":[],"length":0,"stats":{"Line":4}},{"line":17,"address":[625989,626277],"length":1,"stats":{"Line":4}},{"line":20,"address":[626320,626032],"length":1,"stats":{"Line":13}},{"line":21,"address":[],"length":0,"stats":{"Line":13}},{"line":24,"address":[558431],"length":1,"stats":{"Line":15}},{"line":25,"address":[647460,647748],"length":1,"stats":{"Line":43}},{"line":28,"address":[626144,625856],"length":1,"stats":{"Line":10}},{"line":29,"address":[647550,647262],"length":1,"stats":{"Line":10}},{"line":30,"address":[1981655,1983159,1982343,1981927,1982887,1982615],"length":1,"stats":{"Line":5}},{"line":34,"address":[647569,647281],"length":1,"stats":{"Line":9}},{"line":35,"address":[612661,612949,612805,612800,612832,612693,612688,612656,612981,612837,612944,612976],"length":1,"stats":{"Line":31}},{"line":38,"address":[1981696,1982656,1983200,1981968,1982128,1982928,1982384],"length":1,"stats":{"Line":12}},{"line":40,"address":[646492],"length":1,"stats":{"Line":13}},{"line":41,"address":[558353],"length":1,"stats":{"Line":42}},{"line":76,"address":[647824,647888],"length":1,"stats":{"Line":17}},{"line":77,"address":[],"length":0,"stats":{"Line":18}},{"line":80,"address":[1989328,1989152,1989216],"length":1,"stats":{"Line":6}},{"line":81,"address":[1989333,1989157,1989221],"length":1,"stats":{"Line":6}},{"line":84,"address":[],"length":0,"stats":{"Line":2}},{"line":85,"address":[1989349],"length":1,"stats":{"Line":2}},{"line":92,"address":[],"length":0,"stats":{"Line":6}},{"line":93,"address":[],"length":0,"stats":{"Line":6}},{"line":103,"address":[],"length":0,"stats":{"Line":3}},{"line":104,"address":[],"length":0,"stats":{"Line":3}},{"line":121,"address":[1979728,1978576,1979175,1978745,1979861,1979376,1979200,1978768,1979530,1978960,1979552,1978982,1979354,1979704,1978992],"length":1,"stats":{"Line":24}},{"line":124,"address":[646987,646827,646779,646939],"length":1,"stats":{"Line":48}},{"line":125,"address":[],"length":0,"stats":{"Line":24}},{"line":142,"address":[],"length":0,"stats":{"Line":1}},{"line":143,"address":[],"length":0,"stats":{"Line":0}},{"line":173,"address":[],"length":0,"stats":{"Line":5}},{"line":174,"address":[1987076,1986941,1987038,1986894,1986990],"length":1,"stats":{"Line":5}},{"line":179,"address":[],"length":0,"stats":{"Line":2}},{"line":180,"address":[],"length":0,"stats":{"Line":2}},{"line":187,"address":[],"length":0,"stats":{"Line":1}},{"line":188,"address":[],"length":0,"stats":{"Line":1}},{"line":204,"address":[],"length":0,"stats":{"Line":6}},{"line":205,"address":[],"length":0,"stats":{"Line":6}},{"line":226,"address":[1980416,1980400,1980432],"length":1,"stats":{"Line":3}},{"line":227,"address":[],"length":0,"stats":{"Line":3}},{"line":259,"address":[557792,557951],"length":1,"stats":{"Line":9}},{"line":266,"address":[],"length":0,"stats":{"Line":9}},{"line":270,"address":[1976976,1976400,1976784,1977168,1976016,1976208,1976592,1977335],"length":1,"stats":{"Line":27}},{"line":271,"address":[],"length":0,"stats":{"Line":0}},{"line":275,"address":[557893],"length":1,"stats":{"Line":9}},{"line":278,"address":[],"length":0,"stats":{"Line":9}},{"line":280,"address":[],"length":0,"stats":{"Line":0}},{"line":321,"address":[623760,624693,625173,624240,624933,624960,624453,624000,624213,624720,623973,624480],"length":1,"stats":{"Line":18}},{"line":328,"address":[645704,646011,646491,646424,646251,645531,645771,645464,645291,645944,646184,645224],"length":1,"stats":{"Line":36}},{"line":329,"address":[624102,624582,625062,624342,624822,623862],"length":1,"stats":{"Line":8}},{"line":334,"address":[648807,649047,649287,649527,648567,648327],"length":1,"stats":{"Line":30}},{"line":335,"address":[679296,679301,678661,678821,678816,679461,678976,679456,678656,679136,678981,679141],"length":1,"stats":{"Line":0}},{"line":339,"address":[645829,646069,645349,645589,646309,646549],"length":1,"stats":{"Line":10}},{"line":342,"address":[624184,623944,624904,624424,624664,625144],"length":1,"stats":{"Line":10}},{"line":344,"address":[649599,649359,648399,649119,648879,648639],"length":1,"stats":{"Line":10}},{"line":378,"address":[1980940,1980614,1980636,1980832,1980918,1980528],"length":1,"stats":{"Line":5}},{"line":381,"address":[646142],"length":1,"stats":{"Line":4}},{"line":384,"address":[1980893,1980589],"length":1,"stats":{"Line":5}},{"line":422,"address":[],"length":0,"stats":{"Line":2}},{"line":425,"address":[],"length":0,"stats":{"Line":6}},{"line":427,"address":[1980747,1981131,1980777,1981161],"length":1,"stats":{"Line":4}},{"line":429,"address":[],"length":0,"stats":{"Line":0}},{"line":436,"address":[1980816,1981200],"length":1,"stats":{"Line":2}},{"line":437,"address":[],"length":0,"stats":{"Line":2}},{"line":442,"address":[],"length":0,"stats":{"Line":1}},{"line":443,"address":[],"length":0,"stats":{"Line":1}},{"line":469,"address":[],"length":0,"stats":{"Line":2}},{"line":470,"address":[],"length":0,"stats":{"Line":1}},{"line":471,"address":[],"length":0,"stats":{"Line":0}}],"covered":62,"coverable":68},{"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. The\n/// mutex can be created via a `new` constructor. Each mutex has a type\n/// parameter which represents the data that it is protecting. The data can\n/// only be accessed through the [`MutexGuard`]s returned from [`lock`] and\n/// [`try_lock`], which guarantees that the data is only ever accessed when\n/// 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/// An RAII implementation of a “scoped lock” of a mutex. When this structure\n/// is dropped (falls out of scope), the lock will be unlocked.\n///\n/// The data protected by the mutex can be accessed through this guard via its\n/// [`Deref`] and [`DerefMut`] implementations.\n///\n/// This is created by calling the [`lock`] and [`try_lock`] methods on [`Mutex`]\n///\n/// This is similar to the [`MutexGuard`] type, except it does not hold a\n/// [`ThreadKey`].\n///\n/// [`lock`]: `Mutex::lock`\n/// [`try_lock`]: `Mutex::try_lock`\n/// [`Deref`]: `std::ops::Deref`\n/// [`DerefMut`]: `std::ops::DerefMut`\npub struct MutexRef\u003c'a, T: ?Sized + 'a, R: RawMutex\u003e(\u0026'a Mutex\u003cT, R\u003e, PhantomData\u003cR::GuardMarker\u003e);\n\n/// An RAII implementation of a “scoped lock” of a mutex. When this structure\n/// is dropped (falls out of scope), the lock will be unlocked.\n///\n/// The data protected by the mutex can be accessed through this guard via its\n/// [`Deref`] and [`DerefMut`] implementations.\n///\n/// This is created by calling the [`lock`] and [`try_lock`] methods on [`Mutex`]\n///\n/// This guard holds on to a [`ThreadKey`], which ensures that nothing else is\n/// locked until this guard is dropped. The [`ThreadKey`] can be reacquired\n/// using [`Mutex::unlock`].\n///\n/// [`Deref`]: `std::ops::Deref`\n/// [`DerefMut`]: `std::ops::DerefMut`\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?\n//\n// As an update, I've now written `LockContext`. That was even more challenging\npub struct MutexGuard\u003c'a, T: ?Sized + 'a, R: RawMutex\u003e {\n\tmutex: MutexRef\u003c'a, T, R\u003e, // this way we don't need to re-implement Drop\n\tthread_key: ThreadKey,\n}\n\n#[cfg(test)]\nmod tests {\n\tuse crate::{LockCollection, ThreadKey};\n\n\tuse super::*;\n\n\t#[test]\n\tfn unlocked_when_initialized() {\n\t\tlet lock: crate::Mutex\u003c_\u003e = Mutex::new(\"Hello, world!\");\n\n\t\tassert!(!lock.is_locked());\n\t}\n\n\t#[test]\n\tfn locked_after_read() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::Mutex\u003c_\u003e = Mutex::new(\"Hello, world!\");\n\n\t\tlet guard = lock.lock(key);\n\n\t\tassert!(lock.is_locked());\n\t\tdrop(guard)\n\t}\n\n\t#[test]\n\tfn from_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex: crate::Mutex\u003c_\u003e = Mutex::from(\"Hello, world!\");\n\n\t\tlet guard = mutex.lock(key);\n\t\tassert_eq!(*guard, \"Hello, world!\");\n\t}\n\n\t#[test]\n\tfn as_mut_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mut mutex = crate::Mutex::from(42);\n\n\t\tlet mut_ref = mutex.as_mut();\n\t\t*mut_ref = 24;\n\n\t\tmutex.scoped_lock(key, |guard| assert_eq!(*guard, 24))\n\t}\n\n\t#[test]\n\tfn display_works_for_guard() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex: crate::Mutex\u003c_\u003e = Mutex::new(\"Hello, world!\");\n\t\tlet guard = mutex.lock(key);\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn display_works_for_ref() {\n\t\tlet mutex: crate::Mutex\u003c_\u003e = Mutex::new(\"Hello, world!\");\n\t\tlet guard = unsafe { mutex.try_lock_no_key().unwrap() };\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn ref_as_mut() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = LockCollection::new(crate::Mutex::new(0));\n\t\tlet mut guard = collection.lock(key);\n\t\tlet guard_mut = guard.as_mut().as_mut();\n\n\t\t*guard_mut = 3;\n\t\tlet key = LockCollection::\u003ccrate::Mutex\u003c_\u003e\u003e::unlock(guard);\n\n\t\tlet guard = collection.lock(key);\n\n\t\tassert_eq!(guard.as_ref().as_ref(), \u00263);\n\t}\n\n\t#[test]\n\tfn guard_as_mut() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = crate::Mutex::new(0);\n\t\tlet mut guard = mutex.lock(key);\n\t\tlet guard_mut = guard.as_mut();\n\n\t\t*guard_mut = 3;\n\t\tlet key = Mutex::unlock(guard);\n\n\t\tlet guard = mutex.lock(key);\n\n\t\tassert_eq!(guard.as_ref(), \u00263);\n\t}\n\n\t#[test]\n\tfn dropping_guard_releases_mutex() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex: crate::Mutex\u003c_\u003e = Mutex::new(\"Hello, world!\");\n\n\t\tlet guard = mutex.lock(key);\n\t\tdrop(guard);\n\n\t\tassert!(!mutex.is_locked());\n\t}\n\n\t#[test]\n\tfn dropping_ref_releases_mutex() {\n\t\tlet mutex: crate::Mutex\u003c_\u003e = Mutex::new(\"Hello, world!\");\n\n\t\tlet guard = unsafe { mutex.try_lock_no_key().unwrap() };\n\t\tdrop(guard);\n\n\t\tassert!(!mutex.is_locked());\n\t}\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","src","poisonable","error.rs"],"content":"use core::fmt;\nuse std::error::Error;\n\nuse super::{PoisonError, PoisonGuard, TryLockPoisonableError};\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard\u003e fmt::Debug for PoisonError\u003cGuard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut fmt::Formatter\u003c'_\u003e) -\u003e fmt::Result {\n\t\tf.debug_struct(\"PoisonError\").finish_non_exhaustive()\n\t}\n}\n\nimpl\u003cGuard\u003e fmt::Display for PoisonError\u003cGuard\u003e {\n\t#[cfg_attr(test, mutants::skip)]\n\t#[cfg(not(tarpaulin_include))]\n\tfn fmt(\u0026self, f: \u0026mut fmt::Formatter\u003c'_\u003e) -\u003e fmt::Result {\n\t\t\"poisoned lock: another task failed inside\".fmt(f)\n\t}\n}\n\nimpl\u003cGuard\u003e AsRef\u003cGuard\u003e for PoisonError\u003cGuard\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026Guard {\n\t\tself.get_ref()\n\t}\n}\n\nimpl\u003cGuard\u003e AsMut\u003cGuard\u003e for PoisonError\u003cGuard\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut Guard {\n\t\tself.get_mut()\n\t}\n}\n\nimpl\u003cGuard\u003e Error for PoisonError\u003cGuard\u003e {}\n\nimpl\u003cGuard\u003e PoisonError\u003cGuard\u003e {\n\t/// Creates a `PoisonError`\n\t///\n\t/// This is generally created by methods like [`Poisonable::lock`].\n\t///\n\t/// [`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::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let mutex = Poisonable::new(Mutex::new(HashSet::new()));\n\t///\n\t/// // poison the mutex\n\t/// thread::scope(|s| {\n\t/// let r = s.spawn(|| {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut data = mutex.lock(key).unwrap();\n\t/// data.insert(10);\n\t/// panic!();\n\t/// }).join();\n\t/// });\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::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t/// use happylock::poisonable::PoisonGuard;\n\t///\n\t/// let mutex = Poisonable::new(Mutex::new(HashSet::new()));\n\t///\n\t/// // poison the mutex\n\t/// thread::scope(|s| {\n\t/// let r = s.spawn(|| {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut data = mutex.lock(key).unwrap();\n\t/// data.insert(10);\n\t/// panic!();\n\t/// }).join();\n\t/// });\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::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let mutex =Poisonable::new(Mutex::new(HashSet::new()));\n\t///\n\t/// // poison the mutex\n\t/// thread::scope(|s| {\n\t/// let r = s.spawn(|| {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut data = mutex.lock(key).unwrap();\n\t/// data.insert(10);\n\t/// panic!();\n\t/// }).join();\n\t/// });\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":[1956320],"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":43,"address":[],"length":0,"stats":{"Line":9}},{"line":76,"address":[],"length":0,"stats":{"Line":7}},{"line":77,"address":[],"length":0,"stats":{"Line":1}},{"line":110,"address":[],"length":0,"stats":{"Line":4}},{"line":111,"address":[],"length":0,"stats":{"Line":0}},{"line":144,"address":[],"length":0,"stats":{"Line":3}},{"line":145,"address":[],"length":0,"stats":{"Line":0}},{"line":175,"address":[],"length":0,"stats":{"Line":1}},{"line":176,"address":[],"length":0,"stats":{"Line":0}}],"covered":10,"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":[976384],"length":1,"stats":{"Line":17}},{"line":9,"address":[996433],"length":1,"stats":{"Line":17}},{"line":12,"address":[941120],"length":1,"stats":{"Line":12}},{"line":13,"address":[976357],"length":1,"stats":{"Line":12}},{"line":16,"address":[975952],"length":1,"stats":{"Line":1}},{"line":17,"address":[941141],"length":1,"stats":{"Line":1}},{"line":20,"address":[1001232],"length":1,"stats":{"Line":8}},{"line":21,"address":[941205],"length":1,"stats":{"Line":9}}],"covered":8,"coverable":8},{"path":["/","home","botahamec","Projects","happylock","src","poisonable","guard.rs"],"content":"use std::fmt::{Debug, Display};\nuse std::hash::Hash;\nuse std::marker::PhantomData;\nuse std::ops::{Deref, DerefMut};\n\nuse super::{PoisonFlag, PoisonGuard, PoisonRef};\n\nimpl\u003c'a, Guard\u003e PoisonRef\u003c'a, Guard\u003e {\n\t// This is used so that we don't keep accidentally adding the flag reference\n\tpub(super) const fn new(flag: \u0026'a PoisonFlag, guard: Guard) -\u003e Self {\n\t\tSelf {\n\t\t\tguard,\n\t\t\t#[cfg(panic = \"unwind\")]\n\t\t\tflag,\n\t\t\t_phantom: PhantomData,\n\t\t}\n\t}\n}\n\nimpl\u003cGuard\u003e Drop for PoisonRef\u003c'_, Guard\u003e {\n\tfn drop(\u0026mut self) {\n\t\t#[cfg(panic = \"unwind\")]\n\t\tif std::thread::panicking() {\n\t\t\tself.flag.poison();\n\t\t}\n\t}\n}\n\n#[mutants::skip] // hashing involves RNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard: Hash\u003e Hash for PoisonRef\u003c'_, Guard\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.guard.hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard: Debug\u003e Debug for PoisonRef\u003c'_, Guard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cGuard: Display\u003e Display for PoisonRef\u003c'_, Guard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cGuard\u003e Deref for PoisonRef\u003c'_, Guard\u003e {\n\ttype Target = Guard;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t\u0026self.guard\n\t}\n}\n\nimpl\u003cGuard\u003e DerefMut for PoisonRef\u003c'_, Guard\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t\u0026mut self.guard\n\t}\n}\n\nimpl\u003cGuard\u003e AsRef\u003cGuard\u003e for PoisonRef\u003c'_, Guard\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026Guard {\n\t\t\u0026self.guard\n\t}\n}\n\nimpl\u003cGuard\u003e AsMut\u003cGuard\u003e for PoisonRef\u003c'_, Guard\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut Guard {\n\t\t\u0026mut self.guard\n\t}\n}\n\n#[mutants::skip] // hashing involves RNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard: Hash\u003e Hash for PoisonGuard\u003c'_, Guard\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.guard.hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cGuard: Debug\u003e Debug for PoisonGuard\u003c'_, Guard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026self.guard, f)\n\t}\n}\n\nimpl\u003cGuard: Display\u003e Display for PoisonGuard\u003c'_, Guard\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026self.guard, f)\n\t}\n}\n\nimpl\u003cT, Guard: Deref\u003cTarget = T\u003e\u003e Deref for PoisonGuard\u003c'_, Guard\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t#[expect(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#[expect(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":[1948784,1948688,1948752,1948720],"length":1,"stats":{"Line":4}},{"line":21,"address":[],"length":0,"stats":{"Line":4}},{"line":22,"address":[],"length":0,"stats":{"Line":0}},{"line":23,"address":[],"length":0,"stats":{"Line":4}},{"line":24,"address":[],"length":0,"stats":{"Line":3}},{"line":46,"address":[],"length":0,"stats":{"Line":1}},{"line":47,"address":[],"length":0,"stats":{"Line":1}},{"line":54,"address":[],"length":0,"stats":{"Line":3}},{"line":55,"address":[],"length":0,"stats":{"Line":0}},{"line":60,"address":[],"length":0,"stats":{"Line":2}},{"line":61,"address":[],"length":0,"stats":{"Line":0}},{"line":66,"address":[],"length":0,"stats":{"Line":1}},{"line":67,"address":[],"length":0,"stats":{"Line":0}},{"line":72,"address":[],"length":0,"stats":{"Line":1}},{"line":73,"address":[],"length":0,"stats":{"Line":0}},{"line":94,"address":[],"length":0,"stats":{"Line":1}},{"line":95,"address":[],"length":0,"stats":{"Line":1}},{"line":102,"address":[],"length":0,"stats":{"Line":2}},{"line":104,"address":[],"length":0,"stats":{"Line":2}},{"line":109,"address":[],"length":0,"stats":{"Line":3}},{"line":111,"address":[],"length":0,"stats":{"Line":3}},{"line":116,"address":[],"length":0,"stats":{"Line":1}},{"line":117,"address":[],"length":0,"stats":{"Line":0}},{"line":122,"address":[],"length":0,"stats":{"Line":1}},{"line":123,"address":[],"length":0,"stats":{"Line":0}}],"covered":18,"coverable":25},{"path":["/","home","botahamec","Projects","happylock","src","poisonable","poisonable.rs"],"content":"use std::panic::{RefUnwindSafe, UnwindSafe};\n\nuse crate::collection::OwnedLockCollection;\nuse crate::handle_unwind::handle_unwind;\nuse crate::lockable::{\n\tLockable, LockableGetMut, LockableIntoInner, OwnedLockable, RawLock, Sharable,\n};\nuse crate::{Keyable, LockContext, ThreadKey};\n\nuse super::{\n\tPoisonError, PoisonFlag, PoisonGuard, PoisonRef, PoisonResult, Poisonable,\n\tTryLockPoisonableError, TryLockPoisonableResult,\n};\n\nunsafe impl\u003cL: Lockable + RawLock\u003e RawLock for Poisonable\u003cL\u003e {\n\t#[mutants::skip] // this should never run\n\t#[cfg(not(tarpaulin_include))]\n\tfn poison(\u0026self) {\n\t\tself.inner.poison()\n\t}\n\n\tunsafe fn raw_write(\u0026self) {\n\t\tself.inner.raw_write()\n\t}\n\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool {\n\t\tself.inner.raw_try_write()\n\t}\n\n\tunsafe fn raw_unlock_write(\u0026self) {\n\t\tself.inner.raw_unlock_write()\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tself.inner.raw_read()\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tself.inner.raw_try_read()\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\tself.inner.raw_unlock_read()\n\t}\n}\n\nunsafe impl\u003cL: Lockable\u003e Lockable for Poisonable\u003cL\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= PoisonResult\u003cPoisonRef\u003c'g, L::Guard\u003c'g\u003e\u003e\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= PoisonResult\u003cL::DataMut\u003c'a\u003e\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tself.inner.get_ptrs(ptrs)\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tlet ref_guard = PoisonRef::new(\u0026self.poisoned, self.inner.guard());\n\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(ref_guard))\n\t\t} else {\n\t\t\tOk(ref_guard)\n\t\t}\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(self.inner.data_mut()))\n\t\t} else {\n\t\t\tOk(self.inner.data_mut())\n\t\t}\n\t}\n}\n\nunsafe impl\u003cL: Sharable\u003e Sharable for Poisonable\u003cL\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= PoisonResult\u003cPoisonRef\u003c'g, L::ReadGuard\u003c'g\u003e\u003e\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= PoisonResult\u003cL::DataRef\u003c'a\u003e\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tlet ref_guard = PoisonRef::new(\u0026self.poisoned, self.inner.read_guard());\n\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(ref_guard))\n\t\t} else {\n\t\t\tOk(ref_guard)\n\t\t}\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(self.inner.data_ref()))\n\t\t} else {\n\t\t\tOk(self.inner.data_ref())\n\t\t}\n\t}\n}\n\nunsafe impl\u003cL: OwnedLockable\u003e OwnedLockable for Poisonable\u003cL\u003e {}\n\n// AsMut won't work here because we don't strictly return a \u0026mut T\n// LockableGetMut is the next best thing\nimpl\u003cL: LockableGetMut\u003e LockableGetMut for Poisonable\u003cL\u003e {\n\ttype Inner\u003c'a\u003e\n\t\t= PoisonResult\u003cL::Inner\u003c'a\u003e\u003e\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(self.inner.get_mut()))\n\t\t} else {\n\t\t\tOk(self.inner.get_mut())\n\t\t}\n\t}\n}\n\nimpl\u003cL: LockableIntoInner\u003e LockableIntoInner for Poisonable\u003cL\u003e {\n\ttype Inner = PoisonResult\u003cL::Inner\u003e;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tif self.is_poisoned() {\n\t\t\tErr(PoisonError::new(self.inner.into_inner()))\n\t\t} else {\n\t\t\tOk(self.inner.into_inner())\n\t\t}\n\t}\n}\n\nimpl\u003cL\u003e From\u003cL\u003e for Poisonable\u003cL\u003e {\n\tfn from(value: L) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\nimpl\u003cL\u003e Poisonable\u003cL\u003e {\n\t/// Creates a new `Poisonable`\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Mutex, Poisonable};\n\t///\n\t/// let mutex = Poisonable::new(Mutex::new(0));\n\t/// ```\n\tpub const fn new(value: L) -\u003e Self {\n\t\tSelf {\n\t\t\tinner: value,\n\t\t\tpoisoned: PoisonFlag::new(),\n\t\t}\n\t}\n\n\t/// Determines whether the `Poisonable` is poisoned.\n\t///\n\t/// If another thread is active, the `Poisonable` can still become poisoned at\n\t/// any 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::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let mutex = Poisonable::new(Mutex::new(0));\n\t///\n\t/// thread::scope(|s| {\n\t/// let r = s.spawn(|| {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let _lock = mutex.lock(key).unwrap();\n\t/// panic!(); // the mutex gets poisoned\n\t/// }).join();\n\t/// });\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::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let mutex = Poisonable::new(Mutex::new(0));\n\t///\n\t/// thread::scope(|s| {\n\t/// let r = s.spawn(|| {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let _lock = mutex.lock(key).unwrap();\n\t/// panic!(); // the mutex gets poisoned\n\t/// }).join();\n\t/// });\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/// # 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/// # 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}\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\t/// Acquires an exclusive lock, blocking until it is safe to do so, and then\n\t/// unlocks after the provided function returns.\n\t///\n\t/// This function is useful to ensure that a `Poisonable` is never\n\t/// accidentally locked forever by leaking the guard. Even if the function\n\t/// panics, this function will gracefully notice the panic, poison the lock,\n\t/// and unlock.\n\t///\n\t/// If the lock is poisoned, then an error will be passed into the provided\n\t/// function.\n\t///\n\t/// # Panics\n\t///\n\t/// This function will panic if the provided function also panics. However,\n\t/// `Poisonable` will be safely poisoned and any subsequent calls will pass\n\t/// `Err` into the given function.\n\t///\n\t/// # Example\n\t///\n\t/// ```\n\t/// use happylock::{Poisonable, ThreadKey, RwLock};\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let lock = Poisonable::new(RwLock::new(42));\n\t///\n\t/// let x = lock.scoped_lock(\u0026mut key, |number| {\n\t/// *number.unwrap()\n\t/// });\n\t/// assert_eq!(x, 42);\n\t/// ```\n\tpub fn scoped_lock\u003c'a, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: impl Keyable,\n\t\tf: impl FnOnce(\u003cSelf as Lockable\u003e::DataMut\u003c'a\u003e) -\u003e R,\n\t) -\u003e R {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_write();\n\n\t\t\t// safety: the data was just locked\n\t\t\tlet r = handle_unwind(\n\t\t\t\t|| f(self.data_mut()),\n\t\t\t\t|| {\n\t\t\t\t\tself.poisoned.poison();\n\t\t\t\t\tself.raw_unlock_write();\n\t\t\t\t},\n\t\t\t);\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock_write();\n\n\t\t\tdrop(key); // ensure the key stays alive long enough\n\n\t\t\tr\n\t\t}\n\t}\n\n\t/// Attempts to acquire an exclusive lock to the `Poisonable` without\n\t/// blocking, and then unlocks it once the provided function returns.\n\t///\n\t/// This function implements a non-blocking variant of [`scoped_lock`].\n\t/// Unlike `scoped_lock`, if the `Poisonable` is not already unlocked, then\n\t/// the provided function will not run, and the given [`Keyable`] is returned.\n\t/// This method 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/// If the lock is poisoned, then an error will be passed into the provided\n\t/// function.\n\t///\n\t/// # Errors\n\t///\n\t/// If the `Poisonable` is already locked, then the provided function will not\n\t/// run. `Err` is returned with the given key.\n\t///\n\t/// # Panics\n\t///\n\t/// If the provided function panics, then the panic will be bubbled up and\n\t/// rethrown. The `Poisonable` will also be gracefully unlocked, allowing the\n\t/// `Poisonable` to be locked again.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Poisonable, RwLock, ThreadKey};\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let lock = Poisonable::new(RwLock::new(42));\n\t///\n\t/// let result = lock.scoped_try_lock(\u0026mut key, |num| {\n\t/// *num.unwrap()\n\t/// });\n\t///\n\t/// match result {\n\t/// Ok(val) =\u003e println!(\"The number is {val}\"),\n\t/// Err(_) =\u003e unreachable!(),\n\t/// }\n\t/// ```\n\t///\n\t/// [`scoped_lock`]: [`crate::Poisonable::scoped_lock`]\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 FnOnce(\u003cSelf as Lockable\u003e::DataMut\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_write() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we just locked the collection\n\t\t\tlet r = handle_unwind(\n\t\t\t\t|| f(self.data_mut()),\n\t\t\t\t|| {\n\t\t\t\t\tself.poisoned.poison();\n\t\t\t\t\tself.raw_unlock_write();\n\t\t\t\t},\n\t\t\t);\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock_write();\n\n\t\t\tdrop(key); // ensures the key stays valid long enough\n\n\t\t\tOk(r)\n\t\t}\n\t}\n\n\t/// Acquires the lock, blocking the current thread until it is ok to do so.\n\t///\n\t/// This function will block the current thread until it is available to\n\t/// acquire the lock. 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 lock 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::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let mutex = Poisonable::new(Mutex::new(0));\n\t///\n\t/// thread::scope(|s| {\n\t/// let r = s.spawn(|| {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// *mutex.lock(key).unwrap() = 10;\n\t/// }).join();\n\t/// });\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// assert_eq!(*mutex.lock(key).unwrap(), 10);\n\t/// ```\n\tpub fn lock(\u0026self, key: ThreadKey) -\u003e PoisonResult\u003cPoisonGuard\u003c'_, L::Guard\u003c'_\u003e\u003e\u003e {\n\t\tunsafe {\n\t\t\tself.inner.raw_write();\n\t\t\tself.guard(key)\n\t\t}\n\t}\n\n\t/// Attempts to acquire this lock.\n\t///\n\t/// If the lock could not be acquired at this time, then [`Err`] is\n\t/// returned. Otherwise, an RAII guard is returned. The lock will be\n\t/// unlocked when the guard is dropped.\n\t///\n\t/// This function does not block.\n\t///\n\t/// # Errors\n\t///\n\t/// If another user of this lock panicked while holding the lock, then this\n\t/// call will return the [`Poisoned`] error if the lock would otherwise be\n\t/// acquired.\n\t///\n\t/// If the lock 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::thread;\n\t///\n\t/// use happylock::{Mutex, Poisonable, ThreadKey};\n\t///\n\t/// let mutex = Poisonable::new(Mutex::new(0));\n\t///\n\t/// thread::scope(|s| {\n\t/// s.spawn(|| {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let mut lock = 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/// });\n\t/// });\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// assert_eq!(*mutex.lock(key).unwrap(), 10);\n\t/// ```\n\t///\n\t/// [`Poisoned`]: `TryLockPoisonableError::Poisoned`\n\t/// [`WouldBlock`]: `TryLockPoisonableError::WouldBlock`\n\tpub fn try_lock(\u0026self, key: ThreadKey) -\u003e TryLockPoisonableResult\u003c'_, L::Guard\u003c'_\u003e\u003e {\n\t\tunsafe {\n\t\t\tif self.inner.raw_try_write() {\n\t\t\t\tOk(self.guard(key)?)\n\t\t\t} else {\n\t\t\t\tErr(TryLockPoisonableError::WouldBlock(key))\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Consumes the [`PoisonGuard`], and consequently unlocks its `Poisonable`.\n\t///\n\t/// This function is equivalent to calling [`drop`] on the guard, except that\n\t/// it returns the key that was used to create it. Alternatively, the guard\n\t/// will be automatically dropped when it goes out of scope.\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\t/// Acquires a shared lock, blocking until it is safe to do so, and then\n\t/// unlocks after the provided function returns.\n\t///\n\t/// This function is useful to ensure that a `Poisonable` is never\n\t/// accidentally locked forever by leaking the `ReadGuard`. Even if the\n\t/// function panics, this function will gracefully notice the panic, and\n\t/// unlock. This function provides no guarantees with respect to the ordering\n\t/// of whether contentious readers or writers will acquire the lock first.\n\t///\n\t/// If the lock is poisoned, then an error will be passed into the provided\n\t/// into the provided function.\n\t///\n\t/// # Panics\n\t///\n\t/// This function will panic if the provided function also panics. However,\n\t/// `Poisonable` will be safely unlocked in this case, allowing the\n\t/// `Poisonable` to be locked again later.\n\t///\n\t/// # Example\n\t///\n\t/// ```\n\t/// use happylock::{Poisonable, ThreadKey, RwLock};\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let lock = Poisonable::new(RwLock::new(42));\n\t///\n\t/// let x = lock.scoped_read(\u0026mut key, |number| {\n\t/// *number.unwrap()\n\t/// });\n\t/// assert_eq!(x, 42);\n\t/// ```\n\tpub fn scoped_read\u003c'a, R\u003e(\n\t\t\u0026'a self,\n\t\tkey: impl Keyable,\n\t\tf: impl FnOnce(\u003cSelf as Sharable\u003e::DataRef\u003c'a\u003e) -\u003e R,\n\t) -\u003e R {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tself.raw_read();\n\n\t\t\t// safety: the data was just locked\n\t\t\tlet r = handle_unwind(\n\t\t\t\t|| f(self.data_ref()),\n\t\t\t\t|| {\n\t\t\t\t\tself.poisoned.poison();\n\t\t\t\t\tself.raw_unlock_read();\n\t\t\t\t},\n\t\t\t);\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock_read();\n\n\t\t\tdrop(key); // ensure the key stays alive long enough\n\n\t\t\tr\n\t\t}\n\t}\n\n\t/// Attempts to acquire a shared lock to the `Poisonable` without blocking,\n\t/// and then unlocks it once the provided function returns.\n\t///\n\t/// This function implements a non-blocking variant of [`scoped_read`].\n\t/// Unlike `scoped_read`, if the `Poisonable` is exclusively locked, then the\n\t/// provided function will not run, and the given [`Keyable`] is returned.\n\t/// This method provides no guarantees with respect to the ordering of whether\n\t/// contentious readers of writers will acquire the lock first.\n\t///\n\t/// If the lock is poisoned, then an error will be passed into the provided\n\t/// function.\n\t///\n\t/// # Errors\n\t///\n\t/// If the `Poisonable` is already exclusively locked, then the provided\n\t/// function will not run. `Err` is returned with the given key.\n\t///\n\t/// # Panics\n\t///\n\t/// If the provided function panics, then the panic will be bubbled up and\n\t/// rethrown. The `Poisonable` will also be gracefully unlocked, allowing the\n\t/// `Poisonable` to be locked again.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{Poisonable, RwLock, ThreadKey};\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let lock = Poisonable::new(RwLock::new(42));\n\t///\n\t/// let result = lock.scoped_try_read(\u0026mut key, |num| {\n\t/// *num.unwrap()\n\t/// });\n\t///\n\t/// match result {\n\t/// Ok(val) =\u003e println!(\"The number is {val}\"),\n\t/// Err(_) =\u003e unreachable!(),\n\t/// }\n\t/// ```\n\t///\n\t/// [`scoped_read`]: crate::Poisonable::scoped_read\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 FnOnce(\u003cSelf as Sharable\u003e::DataRef\u003c'a\u003e) -\u003e R,\n\t) -\u003e Result\u003cR, Key\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the thread key\n\t\t\tif !self.raw_try_read() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: we just locked the collection\n\t\t\tlet r = handle_unwind(\n\t\t\t\t|| f(self.data_ref()),\n\t\t\t\t|| {\n\t\t\t\t\tself.poisoned.poison();\n\t\t\t\t\tself.raw_unlock_read();\n\t\t\t\t},\n\t\t\t);\n\n\t\t\t// safety: the collection is still locked\n\t\t\tself.raw_unlock_read();\n\n\t\t\tdrop(key); // ensures the key stays valid long enough\n\n\t\t\tOk(r)\n\t\t}\n\t}\n\n\t/// Locks with shared read access, blocking the current thread until it can\n\t/// be acquired.\n\t///\n\t/// This function will block the current thread until there are no writers\n\t/// which hold the lock. This method does not provide any guarantee with\n\t/// respect to the ordering of contentious readers or writers will acquire\n\t/// the lock.\n\t///\n\t/// # Errors\n\t///\n\t/// This function will return an error if the `Poisonable` is poisoned. A\n\t/// `Poisonable` is poisoned whenever a thread panics while holding a lock.\n\t/// The failure will occur immediately after the lock has been acquired. The\n\t/// acquired lock guard will be contained in the returned error.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\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 = Poisonable::new(RwLock::new(0));\n\t///\n\t/// let n = lock.read(key).unwrap();\n\t/// assert_eq!(*n, 0);\n\t///\n\t/// thread::scope(|s| {\n\t/// s.spawn(|| {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let r = lock.read(key);\n\t/// assert!(r.is_ok());\n\t/// });\n\t/// });\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, without blocking the\n\t/// thread.\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 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 thread panics while\n\t/// holding a lock. `Poisoned` will only be returned if the lock would have\n\t/// 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\t// TODO don't poison when holding shared lock\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 `Poisonable`.\n\t///\n\t/// This function is equivalent to calling [`drop`] on the guard, except that\n\t/// it returns the key that was used to create it. Alternatively, the guard\n\t/// will be automatically dropped when it goes out of scope.\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(20));\n\t///\n\t/// let mut guard = lock.read(key).unwrap();\n\t/// assert_eq!(*guard, 20);\n\t///\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. A `Poisonable` is poisoned whenever a\n\t/// thread panics while holding a lock.\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. A `Poisonable` is poisoned\n\t/// whenever a thread panics while holding a lock.\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: OwnedLockable\u003e Poisonable\u003cOwnedLockCollection\u003cL\u003e\u003e {\n\t/// Creates a context that can be used to iterate over the items in order.\n\t///\n\t/// For more information, see [`OwnedLockCollection::context`].\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. A `Poisonable` is poisoned\n\t/// whenever a thread panics while holding a lock.\n\tpub fn context(\u0026self) -\u003e PoisonResult\u003cLockContext\u003c'_, L\u003e\u003e {\n\t\tif self.is_poisoned() {\n\t\t\tOk(self.inner.context())\n\t\t} else {\n\t\t\tErr(PoisonError::new(self.inner.context()))\n\t\t}\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":22,"address":[1953008],"length":1,"stats":{"Line":1}},{"line":23,"address":[],"length":0,"stats":{"Line":1}},{"line":26,"address":[1952976,1953040],"length":1,"stats":{"Line":2}},{"line":27,"address":[],"length":0,"stats":{"Line":2}},{"line":30,"address":[],"length":0,"stats":{"Line":2}},{"line":31,"address":[1953077,1952997],"length":1,"stats":{"Line":2}},{"line":34,"address":[],"length":0,"stats":{"Line":1}},{"line":35,"address":[],"length":0,"stats":{"Line":1}},{"line":38,"address":[1953024],"length":1,"stats":{"Line":1}},{"line":39,"address":[],"length":0,"stats":{"Line":1}},{"line":42,"address":[],"length":0,"stats":{"Line":1}},{"line":43,"address":[1953061],"length":1,"stats":{"Line":1}},{"line":58,"address":[],"length":0,"stats":{"Line":3}},{"line":59,"address":[1956174,1956302,1955758],"length":1,"stats":{"Line":3}},{"line":62,"address":[],"length":0,"stats":{"Line":2}},{"line":63,"address":[],"length":0,"stats":{"Line":3}},{"line":65,"address":[],"length":0,"stats":{"Line":9}},{"line":66,"address":[1955633,1955693,1955953,1956013],"length":1,"stats":{"Line":2}},{"line":68,"address":[],"length":0,"stats":{"Line":3}},{"line":72,"address":[],"length":0,"stats":{"Line":2}},{"line":73,"address":[],"length":0,"stats":{"Line":4}},{"line":74,"address":[],"length":0,"stats":{"Line":1}},{"line":76,"address":[1956092,1956220],"length":1,"stats":{"Line":2}},{"line":92,"address":[1953396,1953120,1953374],"length":1,"stats":{"Line":1}},{"line":93,"address":[],"length":0,"stats":{"Line":1}},{"line":95,"address":[],"length":0,"stats":{"Line":4}},{"line":96,"address":[],"length":0,"stats":{"Line":2}},{"line":98,"address":[1953265],"length":1,"stats":{"Line":1}},{"line":102,"address":[1953408],"length":1,"stats":{"Line":1}},{"line":103,"address":[1953455,1953422],"length":1,"stats":{"Line":2}},{"line":104,"address":[],"length":0,"stats":{"Line":1}},{"line":106,"address":[],"length":0,"stats":{"Line":1}},{"line":121,"address":[],"length":0,"stats":{"Line":1}},{"line":122,"address":[],"length":0,"stats":{"Line":2}},{"line":123,"address":[],"length":0,"stats":{"Line":1}},{"line":125,"address":[],"length":0,"stats":{"Line":1}},{"line":133,"address":[1953872,1954187],"length":1,"stats":{"Line":1}},{"line":134,"address":[1953902,1954185,1953974],"length":1,"stats":{"Line":3}},{"line":135,"address":[],"length":0,"stats":{"Line":2}},{"line":137,"address":[],"length":0,"stats":{"Line":2}},{"line":143,"address":[],"length":0,"stats":{"Line":1}},{"line":144,"address":[],"length":0,"stats":{"Line":1}},{"line":158,"address":[],"length":0,"stats":{"Line":3}},{"line":161,"address":[1950394,1950143,1950502,1950553,1950343,1950194],"length":1,"stats":{"Line":6}},{"line":190,"address":[1950432,1950064,1950256],"length":1,"stats":{"Line":3}},{"line":191,"address":[],"length":0,"stats":{"Line":4}},{"line":232,"address":[],"length":0,"stats":{"Line":3}},{"line":233,"address":[1950469,1950101,1950293],"length":1,"stats":{"Line":3}},{"line":251,"address":[],"length":0,"stats":{"Line":1}},{"line":252,"address":[],"length":0,"stats":{"Line":4}},{"line":253,"address":[1950011,1949954],"length":1,"stats":{"Line":2}},{"line":255,"address":[],"length":0,"stats":{"Line":1}},{"line":276,"address":[],"length":0,"stats":{"Line":0}},{"line":277,"address":[],"length":0,"stats":{"Line":0}},{"line":278,"address":[],"length":0,"stats":{"Line":0}},{"line":280,"address":[],"length":0,"stats":{"Line":0}},{"line":289,"address":[],"length":0,"stats":{"Line":3}},{"line":291,"address":[1950658,1951458,1951530,1951130,1950730,1951058],"length":1,"stats":{"Line":7}},{"line":295,"address":[1950782,1951582,1950832,1951632,1951232,1951182],"length":1,"stats":{"Line":7}},{"line":296,"address":[1951740,1951280,1951680,1950880,1950940,1951340],"length":1,"stats":{"Line":6}},{"line":299,"address":[1951243,1950843,1951643],"length":1,"stats":{"Line":4}},{"line":334,"address":[1947391,1947413,1947232,1947222,1947200,1947056],"length":1,"stats":{"Line":3}},{"line":341,"address":[],"length":0,"stats":{"Line":2}},{"line":345,"address":[2006793,2006766,2006736,2006585,2006576,2006612],"length":1,"stats":{"Line":7}},{"line":346,"address":[],"length":0,"stats":{"Line":1}},{"line":347,"address":[2006702,2006894],"length":1,"stats":{"Line":1}},{"line":348,"address":[],"length":0,"stats":{"Line":1}},{"line":353,"address":[1947338,1947147],"length":1,"stats":{"Line":1}},{"line":355,"address":[1947363,1947172],"length":1,"stats":{"Line":1}},{"line":357,"address":[],"length":0,"stats":{"Line":0}},{"line":403,"address":[1947611,1947857,1947648,1947835,1947633,1947424],"length":1,"stats":{"Line":2}},{"line":410,"address":[],"length":0,"stats":{"Line":4}},{"line":411,"address":[1947732,1947508],"length":1,"stats":{"Line":1}},{"line":416,"address":[2006964,2006928,2007097,2007088,2007124,2006937],"length":1,"stats":{"Line":3}},{"line":417,"address":[2007200,2007040],"length":1,"stats":{"Line":0}},{"line":418,"address":[2007054,2007214],"length":1,"stats":{"Line":0}},{"line":419,"address":[2007228,2007068],"length":1,"stats":{"Line":0}},{"line":424,"address":[],"length":0,"stats":{"Line":1}},{"line":426,"address":[1947805,1947581],"length":1,"stats":{"Line":1}},{"line":428,"address":[],"length":0,"stats":{"Line":1}},{"line":463,"address":[],"length":0,"stats":{"Line":3}},{"line":465,"address":[1952064,1951840,1952208],"length":1,"stats":{"Line":3}},{"line":466,"address":[],"length":0,"stats":{"Line":3}},{"line":514,"address":[],"length":0,"stats":{"Line":0}},{"line":516,"address":[],"length":0,"stats":{"Line":0}},{"line":517,"address":[],"length":0,"stats":{"Line":0}},{"line":519,"address":[],"length":0,"stats":{"Line":0}},{"line":543,"address":[1951952,1952022,1952016],"length":1,"stats":{"Line":1}},{"line":544,"address":[],"length":0,"stats":{"Line":1}},{"line":545,"address":[],"length":0,"stats":{"Line":0}},{"line":550,"address":[1952689,1952695,1952320],"length":1,"stats":{"Line":1}},{"line":552,"address":[1952442,1952370],"length":1,"stats":{"Line":2}},{"line":556,"address":[],"length":0,"stats":{"Line":2}},{"line":557,"address":[],"length":0,"stats":{"Line":0}},{"line":560,"address":[1952555],"length":1,"stats":{"Line":1}},{"line":594,"address":[1948229,1948048,1948038,1947872,1948016,1948207],"length":1,"stats":{"Line":3}},{"line":601,"address":[],"length":0,"stats":{"Line":2}},{"line":605,"address":[1947943,1948134],"length":1,"stats":{"Line":7}},{"line":606,"address":[],"length":0,"stats":{"Line":1}},{"line":607,"address":[],"length":0,"stats":{"Line":1}},{"line":608,"address":[],"length":0,"stats":{"Line":1}},{"line":613,"address":[1948154,1947963],"length":1,"stats":{"Line":1}},{"line":615,"address":[1948179,1947988],"length":1,"stats":{"Line":1}},{"line":617,"address":[],"length":0,"stats":{"Line":0}},{"line":663,"address":[1948464,1948673,1948240,1948427,1948651,1948449],"length":1,"stats":{"Line":2}},{"line":670,"address":[],"length":0,"stats":{"Line":4}},{"line":671,"address":[],"length":0,"stats":{"Line":1}},{"line":676,"address":[],"length":0,"stats":{"Line":3}},{"line":677,"address":[],"length":0,"stats":{"Line":0}},{"line":678,"address":[],"length":0,"stats":{"Line":0}},{"line":679,"address":[],"length":0,"stats":{"Line":0}},{"line":684,"address":[],"length":0,"stats":{"Line":1}},{"line":686,"address":[1948621,1948397],"length":1,"stats":{"Line":1}},{"line":688,"address":[],"length":0,"stats":{"Line":1}},{"line":728,"address":[],"length":0,"stats":{"Line":1}},{"line":730,"address":[],"length":0,"stats":{"Line":1}},{"line":731,"address":[],"length":0,"stats":{"Line":1}},{"line":772,"address":[],"length":0,"stats":{"Line":0}},{"line":774,"address":[],"length":0,"stats":{"Line":0}},{"line":775,"address":[],"length":0,"stats":{"Line":0}},{"line":777,"address":[],"length":0,"stats":{"Line":0}},{"line":801,"address":[],"length":0,"stats":{"Line":0}},{"line":802,"address":[],"length":0,"stats":{"Line":0}},{"line":803,"address":[],"length":0,"stats":{"Line":0}},{"line":824,"address":[],"length":0,"stats":{"Line":1}},{"line":825,"address":[],"length":0,"stats":{"Line":1}},{"line":852,"address":[],"length":0,"stats":{"Line":1}},{"line":853,"address":[],"length":0,"stats":{"Line":1}},{"line":867,"address":[],"length":0,"stats":{"Line":0}},{"line":868,"address":[],"length":0,"stats":{"Line":0}},{"line":869,"address":[],"length":0,"stats":{"Line":0}},{"line":871,"address":[],"length":0,"stats":{"Line":0}}],"covered":103,"coverable":132},{"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// TODO add helper types for poisonable mutex and so on\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`], [`try_lock`], [`read`], and [`try_read`] methods return a\n/// [`Result`] which indicates whether the lock has been poisoned or not. The\n/// [`PoisonError`] type has an [`into_inner`] method which will return the\n/// guard that normally would have been returned for a successful lock. This\n/// allows access to the data, despite the lock being poisoned. The scoped\n/// locking methods (such as [`scoped_lock`]) will pass the [`Result`] into the\n/// given closure. Poisoning will occur if the closure panics.\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///\n/// [`Lockable`]: `crate::lockable::Lockable`\n/// [`lock`]: `Poisonable::lock`\n/// [`try_lock`]: `Poisonable::try_lock`\n/// [`read`]: `Poisonable::read`\n/// [`try_read`]: `Poisonable::try_read`\n/// [`scoped_lock`]: `Poisonable::scoped_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`]. When this structure is dropped (falls\n/// out of scope), the lock will be unlocked.\n///\n/// This is similar to a [`PoisonGuard`], except that it does not hold a\n/// [`ThreadKey`].\n///\n/// The data protected by the underlying lock can be accessed through this\n/// guard via its [`Deref`] and [`DerefMut`] implementations.\n///\n/// This structure is created when passing a `Poisonable` into another lock\n/// wrapper, such as [`LockCollection`], and obtaining a guard through the\n/// wrapper type.\n///\n/// [`Deref`]: `std::ops::Deref`\n/// [`DerefMut`]: `std::ops::DerefMut`\n/// [`ThreadKey`]: `crate::ThreadKey`\n/// [`LockCollection`]: `crate::LockCollection`\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`]. When this structure is dropped (falls\n/// out of scope), the lock will be unlocked.\n///\n/// The data protected by the underlying lock can be accessed through this\n/// guard via its [`Deref`] and [`DerefMut`] implementations.\n///\n/// This method is created by calling the [`lock`], [`try_lock`], [`read`], and\n/// [`try_read`] methods on [`Poisonable`]\n///\n/// This guard holds a [`ThreadKey`], so it is not possible to lock anything\n/// else until this guard is dropped. The [`ThreadKey`] can be reacquired by\n/// calling [`Poisonable::unlock`], or [`Poisonable::unlock_read`].\n///\n/// [`lock`]: `Poisonable::lock`\n/// [`try_lock`]: `Poisonable::try_lock`\n/// [`read`]: `Poisonable::read`\n/// [`try_read`]: `Poisonable::try_read`\n/// [`Deref`]: `std::ops::Deref`\n/// [`DerefMut`]: `std::ops::DerefMut`\n/// [`ThreadKey`]: `crate::ThreadKey`\n/// [`LockCollection`]: `crate::LockCollection`\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.\n///\n/// A [`Poisonable`] is poisoned whenever a thread fails while the lock is\n/// held. For a lock in the poisoned state, unless the state is cleared\n/// manually, all future acquisitions will return this error.\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 operation result is contained within. The [`Err`] variant\n/// indicates that 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 as _;\n\tuse crate::{LockCollection, Mutex, RwLock, ThreadKey};\n\n\t#[test]\n\tfn locking_poisoned_mutex_returns_error_in_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = LockCollection::new(Poisonable::new(Mutex::new(42)));\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet mut guard1 = mutex.lock(key);\n\t\t\t\tlet guard = guard1.as_deref_mut().unwrap();\n\t\t\t\tassert_eq!(**guard, 42);\n\t\t\t\tpanic!();\n\n\t\t\t\t#[expect(unreachable_code)]\n\t\t\t\tdrop(guard1);\n\t\t\t})\n\t\t\t.join()\n\t\t\t.unwrap_err();\n\t\t});\n\n\t\tlet error = mutex.lock(key);\n\t\tlet error = error.as_deref().unwrap_err();\n\t\tassert_eq!(***error.get_ref(), 42);\n\t}\n\n\t#[test]\n\tfn locking_poisoned_rwlock_returns_error_in_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = LockCollection::new(Poisonable::new(RwLock::new(42)));\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet mut guard1 = mutex.read(key);\n\t\t\t\tlet guard = guard1.as_deref_mut().unwrap();\n\t\t\t\tassert_eq!(**guard, 42);\n\t\t\t\tpanic!();\n\n\t\t\t\t#[expect(unreachable_code)]\n\t\t\t\tdrop(guard1);\n\t\t\t})\n\t\t\t.join()\n\t\t\t.unwrap_err();\n\t\t});\n\n\t\tlet error = mutex.read(key);\n\t\tlet error = error.as_deref().unwrap_err();\n\t\tassert_eq!(***error.get_ref(), 42);\n\t}\n\n\t#[test]\n\tfn non_poisoned_get_mut_is_ok() {\n\t\tlet mut mutex = Poisonable::new(Mutex::new(42));\n\t\tlet guard = mutex.get_mut();\n\t\tassert!(guard.is_ok());\n\t\tassert_eq!(*guard.unwrap(), 42);\n\t}\n\n\t#[test]\n\tfn non_poisoned_get_mut_is_err() {\n\t\tlet mut mutex = Poisonable::new(Mutex::new(42));\n\n\t\tlet _ = std::panic::catch_unwind(|| {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t#[expect(unused_variables)]\n\t\t\tlet guard = mutex.lock(key);\n\t\t\tpanic!();\n\t\t\t#[expect(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#[expect(unused_variables)]\n\t\t\tlet guard = mutex.lock(key);\n\t\t\tpanic!();\n\t\t\t#[expect(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#[expect(unused_variables)]\n\t\t\tlet guard = mutex.lock(key);\n\t\t\tpanic!();\n\t\t\t#[expect(unreachable_code)]\n\t\t\tdrop(guard);\n\t\t})\n\t\t.unwrap_err();\n\n\t\tlet error = mutex.into_child().unwrap_err();\n\t\tassert_eq!(error.into_inner().into_inner(), \"foo\");\n\t}\n\n\t#[test]\n\tfn scoped_lock_can_poison() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = Poisonable::new(Mutex::new(42));\n\n\t\tlet r = std::panic::catch_unwind(|| {\n\t\t\tmutex.scoped_lock(key, |num| {\n\t\t\t\t*num.unwrap() = 56;\n\t\t\t\tpanic!();\n\t\t\t})\n\t\t});\n\t\tassert!(r.is_err());\n\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tassert!(mutex.is_poisoned());\n\t\tmutex.scoped_lock(key, |num| {\n\t\t\tlet Err(error) = num else { panic!() };\n\t\t\tmutex.clear_poison();\n\t\t\tlet guard = error.into_inner();\n\t\t\tassert_eq!(*guard, 56);\n\t\t});\n\t\tassert!(!mutex.is_poisoned());\n\t}\n\n\t#[test]\n\tfn scoped_try_lock_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = Poisonable::new(Mutex::new(42));\n\t\tlet guard = mutex.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = mutex.scoped_try_lock(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn scoped_try_lock_can_succeed() {\n\t\tlet rwlock = Poisonable::new(RwLock::new(42));\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = rwlock.scoped_try_lock(key, |guard| {\n\t\t\t\t\tassert_eq!(*guard.unwrap(), 42);\n\t\t\t\t});\n\t\t\t\tassert!(r.is_ok());\n\t\t\t});\n\t\t});\n\t}\n\n\t#[test]\n\tfn scoped_read_can_poison() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = Poisonable::new(RwLock::new(42));\n\n\t\tlet r = std::panic::catch_unwind(|| {\n\t\t\tmutex.scoped_read(key, |num| {\n\t\t\t\tassert_eq!(*num.unwrap(), 42);\n\t\t\t\tpanic!();\n\t\t\t})\n\t\t});\n\t\tassert!(r.is_err());\n\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tassert!(mutex.is_poisoned());\n\t\tmutex.scoped_read(key, |num| {\n\t\t\tlet Err(error) = num else { panic!() };\n\t\t\tmutex.clear_poison();\n\t\t\tlet guard = error.into_inner();\n\t\t\tassert_eq!(*guard, 42);\n\t\t});\n\t\tassert!(!mutex.is_poisoned());\n\t}\n\n\t#[test]\n\tfn scoped_try_read_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet rwlock = Poisonable::new(RwLock::new(42));\n\t\tlet guard = rwlock.lock(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = rwlock.scoped_try_read(key, |_| {});\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn scoped_try_read_can_succeed() {\n\t\tlet rwlock = Poisonable::new(RwLock::new(42));\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = rwlock.scoped_try_read(key, |guard| {\n\t\t\t\t\tassert_eq!(*guard.unwrap(), 42);\n\t\t\t\t});\n\t\t\t\tassert!(r.is_ok());\n\t\t\t});\n\t\t});\n\t}\n\n\t#[test]\n\tfn display_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = Poisonable::new(Mutex::new(\"Hello, world!\"));\n\n\t\tlet guard = mutex.lock(key).unwrap();\n\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\");\n\t}\n\n\t#[test]\n\tfn ref_as_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = LockCollection::new(Poisonable::new(Mutex::new(\"foo\")));\n\t\tlet guard = collection.lock(key);\n\t\tlet Ok(ref guard) = guard.as_ref() else {\n\t\t\tpanic!()\n\t\t};\n\t\tassert_eq!(**guard.as_ref(), \"foo\");\n\t}\n\n\t#[test]\n\tfn ref_as_mut() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = LockCollection::new(Poisonable::new(Mutex::new(\"foo\")));\n\t\tlet mut guard1 = collection.lock(key);\n\t\tlet Ok(ref mut guard) = guard1.as_mut() else {\n\t\t\tpanic!()\n\t\t};\n\t\tlet guard = guard.as_mut();\n\t\t**guard = \"bar\";\n\n\t\tlet key = LockCollection::\u003cPoisonable\u003cMutex\u003c_\u003e\u003e\u003e::unlock(guard1);\n\t\tlet guard = collection.lock(key);\n\t\tlet guard = guard.as_deref().unwrap();\n\t\tassert_eq!(*guard.as_ref(), \"bar\");\n\t}\n\n\t#[test]\n\tfn guard_as_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = Poisonable::new(Mutex::new(\"foo\"));\n\t\tlet guard = collection.lock(key);\n\t\tlet Ok(ref guard) = guard.as_ref() else {\n\t\t\tpanic!()\n\t\t};\n\t\tassert_eq!(**guard.as_ref(), \"foo\");\n\t}\n\n\t#[test]\n\tfn guard_as_mut() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mutex = Poisonable::new(Mutex::new(\"foo\"));\n\t\tlet mut guard1 = mutex.lock(key);\n\t\tlet Ok(ref mut guard) = guard1.as_mut() else {\n\t\t\tpanic!()\n\t\t};\n\t\tlet guard = guard.as_mut();\n\t\t**guard = \"bar\";\n\n\t\tlet key = Poisonable::\u003cMutex\u003c_\u003e\u003e::unlock(guard1.unwrap());\n\t\tlet guard = mutex.lock(key);\n\t\tlet guard = guard.as_deref().unwrap();\n\t\tassert_eq!(*guard, \"bar\");\n\t}\n\n\t#[test]\n\tfn deref_mut_in_collection() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet collection = LockCollection::new(Poisonable::new(Mutex::new(42)));\n\t\tlet mut guard1 = collection.lock(key);\n\t\tlet Ok(ref mut guard) = guard1.as_mut() else {\n\t\t\tpanic!()\n\t\t};\n\t\t// TODO make this more convenient\n\t\tassert_eq!(***guard, 42);\n\t\t***guard = 24;\n\n\t\tlet key = LockCollection::\u003cPoisonable\u003cMutex\u003c_\u003e\u003e\u003e::unlock(guard1);\n\t\t_ = collection.lock(key);\n\t}\n\n\t#[test]\n\tfn get_ptrs() {\n\t\tlet mutex = Mutex::new(5);\n\t\tlet poisonable = Poisonable::new(mutex);\n\t\tlet mut lock_ptrs = Vec::new();\n\t\tpoisonable.get_ptrs(\u0026mut lock_ptrs);\n\n\t\tassert_eq!(lock_ptrs.len(), 1);\n\t\tassert!(std::ptr::addr_eq(lock_ptrs[0], \u0026raw const poisonable.inner));\n\t}\n\n\t#[test]\n\tfn clear_poison_for_poisoned_mutex() {\n\t\tlet mutex = Arc::new(Poisonable::new(Mutex::new(0)));\n\t\tlet c_mutex = Arc::clone(\u0026mutex);\n\n\t\tlet _ = std::thread::spawn(move || {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\tlet _lock = c_mutex.lock(key).unwrap();\n\t\t\tpanic!(); // the mutex gets poisoned\n\t\t})\n\t\t.join();\n\n\t\tassert!(mutex.is_poisoned());\n\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet _ = mutex.lock(key).unwrap_or_else(|mut e| {\n\t\t\t**e.get_mut() = 1;\n\t\t\tmutex.clear_poison();\n\t\t\te.into_inner()\n\t\t});\n\n\t\tassert!(!mutex.is_poisoned());\n\t}\n\n\t#[test]\n\tfn clear_poison_for_poisoned_rwlock() {\n\t\tlet lock = Arc::new(Poisonable::new(RwLock::new(0)));\n\t\tlet c_mutex = Arc::clone(\u0026lock);\n\n\t\tlet _ = std::thread::spawn(move || {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\tlet lock = c_mutex.read(key).unwrap();\n\t\t\tassert_eq!(*lock, 42);\n\t\t\tpanic!(); // the mutex gets poisoned\n\t\t})\n\t\t.join();\n\n\t\tassert!(lock.is_poisoned());\n\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet _ = lock.lock(key).unwrap_or_else(|mut e| {\n\t\t\t**e.get_mut() = 1;\n\t\t\tlock.clear_poison();\n\t\t\te.into_inner()\n\t\t});\n\n\t\tassert!(!lock.is_poisoned());\n\t}\n\n\t#[test]\n\tfn error_as_ref() {\n\t\tlet mutex = Poisonable::new(Mutex::new(\"foo\"));\n\n\t\tlet _ = std::panic::catch_unwind(|| {\n\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t#[expect(unused_variables)]\n\t\t\tlet guard = mutex.lock(key);\n\t\t\tpanic!();\n\n\t\t\t#[expect(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#[expect(unused_variables)]\n\t\t\tlet guard = mutex.lock(key);\n\t\t\tpanic!();\n\n\t\t\t#[expect(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#[expect(unused_variables)]\n\t\t\tlet guard = mutex.lock(key);\n\t\t\tpanic!();\n\n\t\t\t#[expect(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 as _;\nuse crate::ThreadKey;\n\nuse super::{RwLock, RwLockReadGuard, RwLockReadRef};\n\n// These impls make things slightly easier because now you can use\n// `println!(\"{guard}\")` instead of `println!(\"{}\", *guard)`\n\n#[mutants::skip] // hashing involves PRNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Hash + ?Sized, R: RawRwLock\u003e Hash for RwLockReadRef\u003c'_, T, R\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.deref().hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug + ?Sized, R: RawRwLock\u003e Debug for RwLockReadRef\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: Display + ?Sized, R: RawRwLock\u003e Display for RwLockReadRef\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e Deref for RwLockReadRef\u003c'_, T, R\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t// safety: this is the only type that can use `value`, and there's\n\t\t// a reference to this type, so there cannot be any mutable\n\t\t// references to this value.\n\t\tunsafe { \u0026*self.0.data.get() }\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e AsRef\u003cT\u003e for RwLockReadRef\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e Drop for RwLockReadRef\u003c'_, T, R\u003e {\n\tfn drop(\u0026mut self) {\n\t\t// safety: this guard is being destroyed, so the data cannot be\n\t\t// accessed without locking again\n\t\tunsafe { self.0.raw_unlock_read() }\n\t}\n}\n\nimpl\u003c'a, T: ?Sized, R: RawRwLock\u003e RwLockReadRef\u003c'a, T, R\u003e {\n\t/// Creates an immutable reference for the underlying data of an [`RwLock`]\n\t/// without locking it or taking ownership of the key.\n\t#[must_use]\n\tpub(crate) const unsafe fn new(mutex: \u0026'a RwLock\u003cT, R\u003e) -\u003e Self {\n\t\tSelf(mutex, PhantomData)\n\t}\n}\n\n#[mutants::skip] // hashing involves PRNG and is hard to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Hash + ?Sized, R: RawRwLock\u003e Hash for RwLockReadGuard\u003c'_, T, R\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.deref().hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug + ?Sized, R: RawRwLock\u003e Debug for RwLockReadGuard\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: Display + ?Sized, R: RawRwLock\u003e Display for RwLockReadGuard\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e Deref for RwLockReadGuard\u003c'_, T, R\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t\u0026self.rwlock\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e AsRef\u003cT\u003e for RwLockReadGuard\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself\n\t}\n}\n\nimpl\u003c'a, T: ?Sized, R: RawRwLock\u003e RwLockReadGuard\u003c'a, T, R\u003e {\n\t/// Create a guard to the given mutex. Undefined if multiple guards to the\n\t/// same mutex exist at once.\n\t#[must_use]\n\tpub(super) const unsafe fn new(rwlock: \u0026'a RwLock\u003cT, R\u003e, thread_key: ThreadKey) -\u003e Self {\n\t\tSelf {\n\t\t\trwlock: RwLockReadRef(rwlock, PhantomData),\n\t\t\tthread_key,\n\t\t}\n\t}\n}\n\nunsafe impl\u003cT: ?Sized + Sync, R: RawRwLock + Sync\u003e Sync for RwLockReadRef\u003c'_, T, R\u003e {}\n","traces":[{"line":33,"address":[2153616],"length":1,"stats":{"Line":1}},{"line":34,"address":[],"length":0,"stats":{"Line":1}},{"line":41,"address":[],"length":0,"stats":{"Line":3}},{"line":45,"address":[2159781,2159813],"length":1,"stats":{"Line":3}},{"line":50,"address":[],"length":0,"stats":{"Line":1}},{"line":51,"address":[],"length":0,"stats":{"Line":1}},{"line":56,"address":[1769872,1769856],"length":1,"stats":{"Line":3}},{"line":59,"address":[630117,630101],"length":1,"stats":{"Line":3}},{"line":67,"address":[],"length":0,"stats":{"Line":3}},{"line":68,"address":[],"length":0,"stats":{"Line":0}},{"line":89,"address":[],"length":0,"stats":{"Line":1}},{"line":90,"address":[],"length":0,"stats":{"Line":1}},{"line":97,"address":[],"length":0,"stats":{"Line":1}},{"line":98,"address":[],"length":0,"stats":{"Line":1}},{"line":103,"address":[],"length":0,"stats":{"Line":1}},{"line":104,"address":[],"length":0,"stats":{"Line":1}},{"line":112,"address":[],"length":0,"stats":{"Line":1}},{"line":114,"address":[],"length":0,"stats":{"Line":0}}],"covered":16,"coverable":18},{"path":["/","home","botahamec","Projects","happylock","src","rwlock","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_write(\u0026self) {\n\t\tassert!(\n\t\t\t!self.poison.is_poisoned(),\n\t\t\t\"The read-write lock has been killed\"\n\t\t);\n\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.lock_exclusive(), || self.poison())\n\t}\n\n\tunsafe fn raw_try_write(\u0026self) -\u003e bool {\n\t\tif self.poison.is_poisoned() {\n\t\t\treturn false;\n\t\t}\n\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.try_lock_exclusive(), || self.poison())\n\t}\n\n\tunsafe fn raw_unlock_write(\u0026self) {\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.unlock_exclusive(), || self.poison())\n\t}\n\n\tunsafe fn raw_read(\u0026self) {\n\t\tassert!(\n\t\t\t!self.poison.is_poisoned(),\n\t\t\t\"The read-write lock has been killed\"\n\t\t);\n\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.lock_shared(), || self.poison())\n\t}\n\n\tunsafe fn raw_try_read(\u0026self) -\u003e bool {\n\t\tif self.poison.is_poisoned() {\n\t\t\treturn false;\n\t\t}\n\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.try_lock_shared(), || self.poison())\n\t}\n\n\tunsafe fn raw_unlock_read(\u0026self) {\n\t\t// if the closure unwraps, then the mutex will be killed\n\t\tlet this = AssertUnwindSafe(self);\n\t\thandle_unwind(|| this.raw.unlock_shared(), || self.poison())\n\t}\n}\n\nunsafe impl\u003cT, R: RawRwLock\u003e Lockable for RwLock\u003cT, R\u003e {\n\ttype Guard\u003c'g\u003e\n\t\t= RwLockWriteRef\u003c'g, T, R\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataMut\u003c'a\u003e\n\t\t= \u0026'a mut T\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_ptrs\u003c'a\u003e(\u0026'a self, ptrs: \u0026mut Vec\u003c\u0026'a dyn RawLock\u003e) {\n\t\tptrs.push(self);\n\t}\n\n\tunsafe fn guard(\u0026self) -\u003e Self::Guard\u003c'_\u003e {\n\t\tRwLockWriteRef::new(self)\n\t}\n\n\tunsafe fn data_mut(\u0026self) -\u003e Self::DataMut\u003c'_\u003e {\n\t\tself.data.get().as_mut().unwrap_unchecked()\n\t}\n}\n\nunsafe impl\u003cT, R: RawRwLock\u003e Sharable for RwLock\u003cT, R\u003e {\n\ttype ReadGuard\u003c'g\u003e\n\t\t= RwLockReadRef\u003c'g, T, R\u003e\n\twhere\n\t\tSelf: 'g;\n\n\ttype DataRef\u003c'a\u003e\n\t\t= \u0026'a T\n\twhere\n\t\tSelf: 'a;\n\n\tunsafe fn read_guard(\u0026self) -\u003e Self::ReadGuard\u003c'_\u003e {\n\t\tRwLockReadRef::new(self)\n\t}\n\n\tunsafe fn data_ref(\u0026self) -\u003e Self::DataRef\u003c'_\u003e {\n\t\tself.data.get().as_ref().unwrap_unchecked()\n\t}\n}\n\nunsafe impl\u003cT, R: RawRwLock\u003e OwnedLockable for RwLock\u003cT, R\u003e {}\n\nimpl\u003cT, R: RawRwLock\u003e LockableIntoInner for RwLock\u003cT, R\u003e {\n\ttype Inner = T;\n\n\tfn into_inner(self) -\u003e Self::Inner {\n\t\tself.into_inner()\n\t}\n}\n\nimpl\u003cT, R: RawRwLock\u003e LockableGetMut for RwLock\u003cT, R\u003e {\n\ttype Inner\u003c'a\u003e\n\t\t= \u0026'a mut T\n\twhere\n\t\tSelf: 'a;\n\n\tfn get_mut(\u0026mut self) -\u003e Self::Inner\u003c'_\u003e {\n\t\tAsMut::as_mut(self)\n\t}\n}\n\nimpl\u003cT, R: RawRwLock\u003e RwLock\u003cT, R\u003e {\n\t/// Creates a new instance of an `RwLock\u003cT\u003e` which is unlocked.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::RwLock;\n\t///\n\t/// let lock = RwLock::new(5);\n\t///\n\t///\n\t/// ```\n\t#[must_use]\n\tpub const fn new(data: T) -\u003e Self {\n\t\tSelf {\n\t\t\tdata: UnsafeCell::new(data),\n\t\t\tpoison: PoisonFlag::new(),\n\t\t\traw: R::INIT,\n\t\t}\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug, R: RawRwLock\u003e Debug for RwLock\u003cT, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t// safety: this is just a try lock, and the value is dropped\n\t\t// immediately after, so there's no risk of blocking ourselves\n\t\t// or any other threads\n\t\tif let Some(value) = unsafe { self.try_read_no_key() } {\n\t\t\tf.debug_struct(\"RwLock\").field(\"data\", \u0026\u0026*value).finish()\n\t\t} else {\n\t\t\tstruct LockedPlaceholder;\n\t\t\timpl Debug for LockedPlaceholder {\n\t\t\t\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\t\t\t\tf.write_str(\"\u003clocked\u003e\")\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tf.debug_struct(\"RwLock\")\n\t\t\t\t.field(\"data\", \u0026LockedPlaceholder)\n\t\t\t\t.finish()\n\t\t}\n\t}\n}\n\nimpl\u003cT: Default, R: RawRwLock\u003e Default for RwLock\u003cT, R\u003e {\n\tfn default() -\u003e Self {\n\t\tSelf::new(T::default())\n\t}\n}\n\nimpl\u003cT, R: RawRwLock\u003e From\u003cT\u003e for RwLock\u003cT, R\u003e {\n\tfn from(value: T) -\u003e Self {\n\t\tSelf::new(value)\n\t}\n}\n\n// We don't need a `get_mut` because we don't have mutex poisoning. Hurray!\n// This is safe because you can't have a mutable reference to the lock if it's\n// locked. Being locked requires an immutable reference because of the guard.\nimpl\u003cT: ?Sized, R\u003e AsMut\u003cT\u003e for RwLock\u003cT, R\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself.data.get_mut()\n\t}\n}\n\nimpl\u003cT, R\u003e RwLock\u003cT, R\u003e {\n\t/// Consumes this `RwLock`, returning the underlying data.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(String::new());\n\t/// {\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 needs to take\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 lock = RwLock::new(0);\n\t/// *lock.get_mut() = 10;\n\t/// assert_eq!(*lock.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\t/// Acquires a shared lock, blocking until it is safe to do so, and then\n\t/// unlocks after the provided function returns.\n\t///\n\t/// This function is useful to ensure that a `RwLock` is never accidentally\n\t/// locked forever by leaking the `ReadGuard`. Even if the function panics,\n\t/// this function will gracefully notice the panic, and unlock. This function\n\t/// provides no guarantees with respect to the ordering of whether contentious\n\t/// readers or writers will acquire the lock first.\n\t///\n\t/// # Panics\n\t///\n\t/// This function will panic if the provided function also panics. However,\n\t/// `RwLock` will be safely unlocked in this case, allowing the `RwLock` to be\n\t/// locked again later.\n\t///\n\t/// # Example\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, RwLock};\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(42);\n\t///\n\t/// let x = lock.scoped_read(\u0026mut key, |number| {\n\t/// *number\n\t/// });\n\t/// assert_eq!(x, 42);\n\t/// ```\n\tpub fn scoped_read\u003c'a, Ret\u003e(\u0026'a self, key: impl Keyable, f: impl FnOnce(\u0026'a T) -\u003e Ret) -\u003e Ret {\n\t\tunsafe {\n\t\t\t// safety: we have the key\n\t\t\tself.raw_read();\n\n\t\t\t// safety: the data has been locked\n\t\t\tlet r = handle_unwind(\n\t\t\t\t|| f(self.data.get().as_ref().unwrap_unchecked()),\n\t\t\t\t|| self.raw_unlock_read(),\n\t\t\t);\n\n\t\t\t// ensures the key is held long enough\n\t\t\tdrop(key);\n\n\t\t\t// safety: the mutex is still locked\n\t\t\tself.raw_unlock_read();\n\n\t\t\tr\n\t\t}\n\t}\n\n\t/// Attempts to acquire a shared lock to the `RwLock` without blocking,\n\t/// and then unlocks it once the provided function returns.\n\t///\n\t/// This function implements a non-blocking variant of [`scoped_read`].\n\t/// Unlike `scoped_read`, if the `RwLock` is exclusively locked, then the\n\t/// provided function will not run, and the given [`Keyable`] is returned.\n\t/// This method provides no guarantees with respect to the ordering of whether\n\t/// contentious readers of writers will acquire the lock first.\n\t///\n\t/// # Errors\n\t///\n\t/// If the `RwLock` is already exclusively locked, then the provided function\n\t/// will not run. `Err` is returned with the given key.\n\t///\n\t/// # Panics\n\t///\n\t/// If the provided function panics, then the panic will be bubbled up and\n\t/// rethrown. The `RwLock` will also be gracefully unlocked, allowing the\n\t/// `RwLock` to be locked again.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(42);\n\t///\n\t/// let result = lock.scoped_try_read(\u0026mut key, |num| {\n\t/// *num\n\t/// });\n\t///\n\t/// match result {\n\t/// Ok(val) =\u003e println!(\"The number is {val}\"),\n\t/// Err(_) =\u003e unreachable!(),\n\t/// }\n\t/// ```\n\t///\n\t/// [`scoped_read`]: RwLock::scoped_read\n\tpub fn scoped_try_read\u003c'a, Key: Keyable, Ret\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl FnOnce(\u0026'a T) -\u003e Ret,\n\t) -\u003e Result\u003cRet, Key\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the 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 data has been locked\n\t\t\tlet r = handle_unwind(\n\t\t\t\t|| f(self.data.get().as_ref().unwrap_unchecked()),\n\t\t\t\t|| self.raw_unlock_read(),\n\t\t\t);\n\n\t\t\t// ensures the key is held long enough\n\t\t\tdrop(key);\n\n\t\t\t// safety: the mutex is still locked\n\t\t\tself.raw_unlock_read();\n\n\t\t\tOk(r)\n\t\t}\n\t}\n\n\t/// Acquires an exclusive lock, blocking until it is safe to do so, and then\n\t/// unlocks after the provided function returns.\n\t///\n\t/// This function is useful to ensure that a `RwLock` is never accidentally\n\t/// locked forever by leaking the `WriteGuard`. Even if the function panics,\n\t/// this function will gracefully notice the panic, and unlock. This method\n\t/// does not provide any guarantees with respect to the ordering of whether\n\t/// contentious readers or writers will acquire the lock first.\n\t///\n\t/// # Panics\n\t///\n\t/// This function will panic if the provided function also panics. However,\n\t/// `RwLock` will be safely unlocked in this case, allowing the `RwLock` to be\n\t/// locked again later.\n\t///\n\t/// # Example\n\t///\n\t/// ```\n\t/// use happylock::{ThreadKey, RwLock};\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(42);\n\t///\n\t/// let x = lock.scoped_write(\u0026mut key, |number| {\n\t/// *number += 5;\n\t/// *number\n\t/// });\n\t/// assert_eq!(x, 47);\n\t/// ```\n\tpub fn scoped_write\u003c'a, Ret\u003e(\n\t\t\u0026'a self,\n\t\tkey: impl Keyable,\n\t\tf: impl FnOnce(\u0026'a mut T) -\u003e Ret,\n\t) -\u003e Ret {\n\t\tunsafe {\n\t\t\t// safety: we have the key\n\t\t\tself.raw_write();\n\n\t\t\t// safety: the data has been locked\n\t\t\tlet r = handle_unwind(\n\t\t\t\t|| f(self.data.get().as_mut().unwrap_unchecked()),\n\t\t\t\t|| self.raw_unlock_write(),\n\t\t\t);\n\n\t\t\t// ensures the key is held long enough\n\t\t\tdrop(key);\n\n\t\t\t// safety: the mutex is still locked\n\t\t\tself.raw_unlock_write();\n\n\t\t\tr\n\t\t}\n\t}\n\n\t/// Attempts to acquire an exclusive lock to the `RwLock` without blocking,\n\t/// and then unlocks it once the provided function returns.\n\t///\n\t/// This function implements a non-blocking variant of [`scoped_write`].\n\t/// Unlike `scoped_write`, if the `RwLock` is not already unlocked, then the\n\t/// provided function will not run, and the given [`Keyable`] is returned.\n\t/// This method 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/// If the `RwLock` is already locked, then the provided function will not\n\t/// run. `Err` is returned with the given key.\n\t///\n\t/// # Panics\n\t///\n\t/// If the provided function panics, then the panic will be bubbled up and\n\t/// rethrown. The `RwLock` will also be gracefully unlocked, allowing the\n\t/// `RwLock` to be locked again.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t///\n\t/// let mut key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(42);\n\t///\n\t/// let result = lock.scoped_try_write(\u0026mut key, |num| {\n\t/// *num\n\t/// });\n\t///\n\t/// match result {\n\t/// Ok(val) =\u003e println!(\"The number is {val}\"),\n\t/// Err(_) =\u003e unreachable!(),\n\t/// }\n\t/// ```\n\t///\n\t/// [`scoped_write`]: RwLock::scoped_write\n\tpub fn scoped_try_write\u003c'a, Key: Keyable, Ret\u003e(\n\t\t\u0026'a self,\n\t\tkey: Key,\n\t\tf: impl FnOnce(\u0026'a mut T) -\u003e Ret,\n\t) -\u003e Result\u003cRet, Key\u003e {\n\t\tunsafe {\n\t\t\t// safety: we have the key\n\t\t\tif !self.raw_try_write() {\n\t\t\t\treturn Err(key);\n\t\t\t}\n\n\t\t\t// safety: the data has been locked\n\t\t\tlet r = handle_unwind(\n\t\t\t\t|| f(self.data.get().as_mut().unwrap_unchecked()),\n\t\t\t\t|| self.raw_unlock_write(),\n\t\t\t);\n\n\t\t\t// ensures the key is held long enough\n\t\t\tdrop(key);\n\n\t\t\t// safety: the mutex is still locked\n\t\t\tself.raw_unlock_write();\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. This method does not provide any guarantees\n\t/// with respect to the ordering of whether contentious readers or writers\n\t/// will acquire the lock first.\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::thread;\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\t/// thread::scope(|s| {\n\t/// s.spawn(|| {\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let r = lock.read(key);\n\t/// });\n\t/// });\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/// This function will return an error containing the [`ThreadKey`] if the\n\t/// `RwLock` could not be acquired because it was already locked exclusively.\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\tunsafe {\n\t\t\tif self.raw_try_read() {\n\t\t\t\t// safety: the lock is locked first\n\t\t\t\tSome(RwLockReadRef(self, PhantomData))\n\t\t\t} else {\n\t\t\t\tNone\n\t\t\t}\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\tunsafe {\n\t\t\tif self.raw_try_write() {\n\t\t\t\t// safety: the lock is locked first\n\t\t\t\tSome(RwLockWriteRef(self, PhantomData))\n\t\t\t} else {\n\t\t\t\tNone\n\t\t\t}\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/// let mut n = lock.write(key);\n\t/// *n = 2;\n\t///\n\t/// let key = RwLock::unlock_write(n);\n\t/// assert_eq!(*lock.read(key), 2);\n\t/// ```\n\t///\n\t/// [`ThreadKey`]: `crate::ThreadKey`\n\tpub fn write(\u0026self, key: ThreadKey) -\u003e RwLockWriteGuard\u003c'_, T, R\u003e {\n\t\tunsafe {\n\t\t\tself.raw_write();\n\n\t\t\t// safety: the lock is locked first\n\t\t\tRwLockWriteGuard::new(self, key)\n\t\t}\n\t}\n\n\t/// Attempts to lock this `RwLock` with exclusive write access, without\n\t/// blocking.\n\t///\n\t/// This function does not block. If the lock could not be acquired at this\n\t/// time, then `Err` 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/// This function will return an error containing the [`ThreadKey`] if the\n\t/// `RwLock` could not be acquired because it was already locked exclusively.\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 key = match lock.try_write(key) {\n\t/// Ok(mut n) =\u003e {\n\t/// assert_eq!(*n, 1);\n\t/// *n = 2;\n\t/// RwLock::unlock_write(n)\n\t/// }\n\t/// Err(_) =\u003e unreachable!(),\n\t/// };\n\t///\n\t/// let n = lock.read(key);\n\t/// assert_eq!(*n, 2);\n\t/// ```\n\tpub fn try_write(\u0026self, key: ThreadKey) -\u003e Result\u003cRwLockWriteGuard\u003c'_, T, R\u003e, ThreadKey\u003e {\n\t\tunsafe {\n\t\t\tif self.raw_try_write() {\n\t\t\t\t// safety: the lock is locked first\n\t\t\t\tOk(RwLockWriteGuard::new(self, key))\n\t\t\t} else {\n\t\t\t\tErr(key)\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Returns `true` if the rwlock is currently locked in any way\n\t#[cfg(test)]\n\tpub(crate) fn is_locked(\u0026self) -\u003e bool {\n\t\tself.raw.is_locked()\n\t}\n\n\t/// Immediately drops the guard, and consequently releases the shared lock.\n\t///\n\t/// This function is equivalent to calling [`drop`] on the guard, except\n\t/// that it returns the key that was used to create it. Alternatively, the\n\t/// guard will be automatically dropped when it goes out of scope.\n\t///\n\t/// # Examples\n\t///\n\t/// ```\n\t/// use happylock::{RwLock, ThreadKey};\n\t///\n\t/// let key = ThreadKey::get().unwrap();\n\t/// let lock = RwLock::new(0);\n\t///\n\t/// let mut guard = lock.read(key);\n\t/// assert_eq!(*guard, 0);\n\t/// let key = RwLock::unlock_read(guard);\n\t/// ```\n\t#[must_use]\n\tpub fn unlock_read(guard: RwLockReadGuard\u003c'_, T, R\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.rwlock);\n\t\tguard.thread_key\n\t}\n\n\t/// Immediately drops the guard, and consequently releases the exclusive\n\t/// lock.\n\t///\n\t/// This function is equivalent to calling [`drop`] on the guard, except that\n\t/// it returns the key that was used to create it. Alternatively, the guard\n\t/// 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/// let guard = lock.read(key);\n\t/// assert_eq!(*guard, 20);\n\t/// ```\n\t#[must_use]\n\tpub fn unlock_write(guard: RwLockWriteGuard\u003c'_, T, R\u003e) -\u003e ThreadKey {\n\t\tdrop(guard.rwlock);\n\t\tguard.thread_key\n\t}\n}\n\nunsafe impl\u003cR: RawRwLock + Send, T: ?Sized + Send\u003e Send for RwLock\u003cT, R\u003e {}\nunsafe impl\u003cR: RawRwLock + Sync, T: ?Sized + Send\u003e Sync for RwLock\u003cT, R\u003e {}\n","traces":[{"line":17,"address":[672608,673056],"length":1,"stats":{"Line":5}},{"line":18,"address":[2153941,2154373],"length":1,"stats":{"Line":6}},{"line":21,"address":[],"length":0,"stats":{"Line":6}},{"line":22,"address":[673235,672787],"length":1,"stats":{"Line":2}},{"line":23,"address":[],"length":0,"stats":{"Line":0}},{"line":24,"address":[],"length":0,"stats":{"Line":0}},{"line":28,"address":[613455],"length":1,"stats":{"Line":6}},{"line":29,"address":[584880,584885,584848,584853],"length":1,"stats":{"Line":24}},{"line":32,"address":[],"length":0,"stats":{"Line":8}},{"line":33,"address":[613166],"length":1,"stats":{"Line":9}},{"line":34,"address":[],"length":0,"stats":{"Line":5}},{"line":38,"address":[682593,682145],"length":1,"stats":{"Line":5}},{"line":39,"address":[601845,602165,602128,602160,601872,601840,602133,601877],"length":1,"stats":{"Line":15}},{"line":42,"address":[672576,673024],"length":1,"stats":{"Line":7}},{"line":44,"address":[700972,701852,701404],"length":1,"stats":{"Line":7}},{"line":45,"address":[2163781,2163525,2163808,2163488,2163493,2163813,2163520,2163776],"length":1,"stats":{"Line":22}},{"line":48,"address":[701456,701904,701008],"length":1,"stats":{"Line":5}},{"line":49,"address":[2154002,2154434],"length":1,"stats":{"Line":3}},{"line":50,"address":[],"length":0,"stats":{"Line":0}},{"line":51,"address":[],"length":0,"stats":{"Line":0}},{"line":55,"address":[],"length":0,"stats":{"Line":5}},{"line":56,"address":[2154419,2153987],"length":1,"stats":{"Line":20}},{"line":59,"address":[672384,672832],"length":1,"stats":{"Line":6}},{"line":60,"address":[2153726,2154158],"length":1,"stats":{"Line":6}},{"line":61,"address":[],"length":0,"stats":{"Line":2}},{"line":65,"address":[2153744,2154176],"length":1,"stats":{"Line":6}},{"line":66,"address":[624085,624117,624400,624405,624112,624373,624368,624080],"length":1,"stats":{"Line":25}},{"line":69,"address":[682640,682192],"length":1,"stats":{"Line":5}},{"line":71,"address":[2153884,2154316],"length":1,"stats":{"Line":6}},{"line":72,"address":[584741,584736,584709,584704],"length":1,"stats":{"Line":25}},{"line":87,"address":[702240,702288,702192],"length":1,"stats":{"Line":15}},{"line":88,"address":[702313,702217,702265],"length":1,"stats":{"Line":12}},{"line":91,"address":[2159968,2159904],"length":1,"stats":{"Line":4}},{"line":92,"address":[673349,673285],"length":1,"stats":{"Line":4}},{"line":95,"address":[2159984],"length":1,"stats":{"Line":1}},{"line":96,"address":[],"length":0,"stats":{"Line":1}},{"line":111,"address":[702128,702096,702112],"length":1,"stats":{"Line":3}},{"line":112,"address":[613557],"length":1,"stats":{"Line":3}},{"line":115,"address":[],"length":0,"stats":{"Line":1}},{"line":116,"address":[],"length":0,"stats":{"Line":1}},{"line":125,"address":[2154816],"length":1,"stats":{"Line":1}},{"line":126,"address":[2154820],"length":1,"stats":{"Line":1}},{"line":136,"address":[2154912],"length":1,"stats":{"Line":1}},{"line":137,"address":[],"length":0,"stats":{"Line":1}},{"line":154,"address":[],"length":0,"stats":{"Line":13}},{"line":156,"address":[671853,671709],"length":1,"stats":{"Line":13}},{"line":157,"address":[700594,700390,700450,700257,700534,700306],"length":1,"stats":{"Line":24}},{"line":188,"address":[],"length":0,"stats":{"Line":1}},{"line":189,"address":[2156942],"length":1,"stats":{"Line":1}},{"line":194,"address":[2157008],"length":1,"stats":{"Line":1}},{"line":195,"address":[2157024],"length":1,"stats":{"Line":1}},{"line":203,"address":[],"length":0,"stats":{"Line":1}},{"line":204,"address":[],"length":0,"stats":{"Line":1}},{"line":225,"address":[2152197,2152176],"length":1,"stats":{"Line":1}},{"line":226,"address":[],"length":0,"stats":{"Line":1}},{"line":247,"address":[2152240],"length":1,"stats":{"Line":1}},{"line":248,"address":[2152245],"length":1,"stats":{"Line":1}},{"line":281,"address":[2150784,2150950,2150928],"length":1,"stats":{"Line":1}},{"line":284,"address":[],"length":0,"stats":{"Line":1}},{"line":288,"address":[],"length":0,"stats":{"Line":3}},{"line":289,"address":[],"length":0,"stats":{"Line":0}},{"line":293,"address":[2150870],"length":1,"stats":{"Line":1}},{"line":296,"address":[2150905],"length":1,"stats":{"Line":1}},{"line":298,"address":[],"length":0,"stats":{"Line":0}},{"line":341,"address":[681477,680517,681237,680277,680064,680997,680784,680304,681024,680757,681264,680544],"length":1,"stats":{"Line":6}},{"line":348,"address":[],"length":0,"stats":{"Line":12}},{"line":349,"address":[681126,681366,680886,680166,680646,680406],"length":1,"stats":{"Line":2}},{"line":354,"address":[],"length":0,"stats":{"Line":12}},{"line":355,"address":[623429,624069,623749,623589,623584,623424,623909,623904,623269,623744,623264,624064],"length":1,"stats":{"Line":0}},{"line":359,"address":[680213,680933,681173,680693,681413,680453],"length":1,"stats":{"Line":4}},{"line":362,"address":[],"length":0,"stats":{"Line":4}},{"line":364,"address":[],"length":0,"stats":{"Line":4}},{"line":397,"address":[2150592,2150766],"length":1,"stats":{"Line":1}},{"line":404,"address":[2150626],"length":1,"stats":{"Line":1}},{"line":408,"address":[2150688],"length":1,"stats":{"Line":3}},{"line":409,"address":[],"length":0,"stats":{"Line":0}},{"line":413,"address":[],"length":0,"stats":{"Line":1}},{"line":416,"address":[],"length":0,"stats":{"Line":1}},{"line":418,"address":[],"length":0,"stats":{"Line":0}},{"line":461,"address":[698880,699840,699600,699333,699813,699573,699120,700053,698853,698640,699360,699093],"length":1,"stats":{"Line":12}},{"line":468,"address":[],"length":0,"stats":{"Line":24}},{"line":469,"address":[698742,699942,698982,699462,699702,699222],"length":1,"stats":{"Line":6}},{"line":474,"address":[670695,671175,670215,670935,670455,671415],"length":1,"stats":{"Line":18}},{"line":475,"address":[],"length":0,"stats":{"Line":0}},{"line":479,"address":[],"length":0,"stats":{"Line":6}},{"line":482,"address":[700024,699784,699544,698824,699304,699064],"length":1,"stats":{"Line":6}},{"line":484,"address":[],"length":0,"stats":{"Line":6}},{"line":524,"address":[2152662,2152576,2152684],"length":1,"stats":{"Line":1}},{"line":526,"address":[2152590],"length":1,"stats":{"Line":1}},{"line":529,"address":[],"length":0,"stats":{"Line":1}},{"line":562,"address":[2152960,2152832,2152982],"length":1,"stats":{"Line":1}},{"line":564,"address":[],"length":0,"stats":{"Line":4}},{"line":566,"address":[],"length":0,"stats":{"Line":0}},{"line":568,"address":[],"length":0,"stats":{"Line":2}},{"line":575,"address":[2152448],"length":1,"stats":{"Line":1}},{"line":577,"address":[],"length":0,"stats":{"Line":1}},{"line":579,"address":[],"length":0,"stats":{"Line":1}},{"line":581,"address":[],"length":0,"stats":{"Line":0}},{"line":589,"address":[],"length":0,"stats":{"Line":1}},{"line":591,"address":[2152543,2152525],"length":1,"stats":{"Line":1}},{"line":593,"address":[],"length":0,"stats":{"Line":1}},{"line":595,"address":[2152534],"length":1,"stats":{"Line":0}},{"line":628,"address":[672128,672214,672112,672086,672000,672240],"length":1,"stats":{"Line":6}},{"line":630,"address":[612894],"length":1,"stats":{"Line":5}},{"line":633,"address":[672061,672189],"length":1,"stats":{"Line":4}},{"line":673,"address":[],"length":0,"stats":{"Line":2}},{"line":675,"address":[2153072,2153092,2153438,2153326,2153396,2153022,2153376,2153134],"length":1,"stats":{"Line":7}},{"line":677,"address":[2153129,2153403,2153433,2153099],"length":1,"stats":{"Line":4}},{"line":679,"address":[],"length":0,"stats":{"Line":1}},{"line":686,"address":[2152992,2153296],"length":1,"stats":{"Line":2}},{"line":687,"address":[2152997,2153301],"length":1,"stats":{"Line":2}},{"line":709,"address":[],"length":0,"stats":{"Line":1}},{"line":710,"address":[],"length":0,"stats":{"Line":1}},{"line":711,"address":[],"length":0,"stats":{"Line":0}},{"line":737,"address":[],"length":0,"stats":{"Line":1}},{"line":738,"address":[],"length":0,"stats":{"Line":1}},{"line":739,"address":[],"length":0,"stats":{"Line":0}}],"covered":102,"coverable":117},{"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 as _;\nuse crate::ThreadKey;\n\nuse super::{RwLock, RwLockWriteGuard, RwLockWriteRef};\n\n// These impls make things slightly easier because now you can use\n// `println!(\"{guard}\")` instead of `println!(\"{}\", *guard)`\n\n#[mutants::skip] // hashing involves PRNG and is difficult to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Hash + ?Sized, R: RawRwLock\u003e Hash for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.deref().hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug + ?Sized, R: RawRwLock\u003e Debug for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: Display + ?Sized, R: RawRwLock\u003e Display for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e Deref for RwLockWriteRef\u003c'_, T, R\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t// safety: this is the only type that can use `value`, and there's\n\t\t// a reference to this type, so there cannot be any mutable\n\t\t// references to this value.\n\t\tunsafe { \u0026*self.0.data.get() }\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e DerefMut for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t// safety: this is the only type that can use `value`, and we have a\n\t\t// mutable reference to this type, so there cannot be any other\n\t\t// references to this value.\n\t\tunsafe { \u0026mut *self.0.data.get() }\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e AsRef\u003cT\u003e for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e AsMut\u003cT\u003e for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e Drop for RwLockWriteRef\u003c'_, T, R\u003e {\n\tfn drop(\u0026mut self) {\n\t\t// safety: this guard is being destroyed, so the data cannot be\n\t\t// accessed without locking again\n\t\tunsafe { self.0.raw_unlock_write() }\n\t}\n}\n\nimpl\u003c'a, T: ?Sized + 'a, R: RawRwLock\u003e RwLockWriteRef\u003c'a, T, R\u003e {\n\t/// Creates a reference to the underlying data of an [`RwLock`] without\n\t/// locking or taking ownership of the key.\n\t#[must_use]\n\tpub(crate) const unsafe fn new(mutex: \u0026'a RwLock\u003cT, R\u003e) -\u003e Self {\n\t\tSelf(mutex, PhantomData)\n\t}\n}\n\n#[mutants::skip] // hashing involves PRNG and is difficult to test\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Hash + ?Sized, R: RawRwLock\u003e Hash for RwLockWriteGuard\u003c'_, T, R\u003e {\n\tfn hash\u003cH: std::hash::Hasher\u003e(\u0026self, state: \u0026mut H) {\n\t\tself.deref().hash(state)\n\t}\n}\n\n#[mutants::skip]\n#[cfg(not(tarpaulin_include))]\nimpl\u003cT: Debug + ?Sized, R: RawRwLock\u003e Debug for RwLockWriteGuard\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDebug::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: Display + ?Sized, R: RawRwLock\u003e Display for RwLockWriteGuard\u003c'_, T, R\u003e {\n\tfn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n\t\tDisplay::fmt(\u0026**self, f)\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e Deref for RwLockWriteGuard\u003c'_, T, R\u003e {\n\ttype Target = T;\n\n\tfn deref(\u0026self) -\u003e \u0026Self::Target {\n\t\t\u0026self.rwlock\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e DerefMut for RwLockWriteGuard\u003c'_, T, R\u003e {\n\tfn deref_mut(\u0026mut self) -\u003e \u0026mut Self::Target {\n\t\t\u0026mut self.rwlock\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e AsRef\u003cT\u003e for RwLockWriteGuard\u003c'_, T, R\u003e {\n\tfn as_ref(\u0026self) -\u003e \u0026T {\n\t\tself\n\t}\n}\n\nimpl\u003cT: ?Sized, R: RawRwLock\u003e AsMut\u003cT\u003e for RwLockWriteGuard\u003c'_, T, R\u003e {\n\tfn as_mut(\u0026mut self) -\u003e \u0026mut T {\n\t\tself\n\t}\n}\n\nimpl\u003c'a, T: ?Sized + 'a, R: RawRwLock\u003e RwLockWriteGuard\u003c'a, T, R\u003e {\n\t/// Create a guard to the given mutex. Undefined if multiple guards to the\n\t/// same mutex exist at once.\n\t#[must_use]\n\tpub(super) const unsafe fn new(rwlock: \u0026'a RwLock\u003cT, R\u003e, thread_key: ThreadKey) -\u003e Self {\n\t\tSelf {\n\t\t\trwlock: RwLockWriteRef(rwlock, PhantomData),\n\t\t\tthread_key,\n\t\t}\n\t}\n}\n\nunsafe impl\u003cT: ?Sized + Sync, R: RawRwLock + Sync\u003e Sync for RwLockWriteRef\u003c'_, T, R\u003e {}\n","traces":[{"line":33,"address":[2153664],"length":1,"stats":{"Line":1}},{"line":34,"address":[],"length":0,"stats":{"Line":1}},{"line":41,"address":[],"length":0,"stats":{"Line":4}},{"line":45,"address":[614101],"length":1,"stats":{"Line":4}},{"line":50,"address":[613520],"length":1,"stats":{"Line":3}},{"line":54,"address":[],"length":0,"stats":{"Line":4}},{"line":59,"address":[],"length":0,"stats":{"Line":1}},{"line":60,"address":[],"length":0,"stats":{"Line":1}},{"line":65,"address":[],"length":0,"stats":{"Line":0}},{"line":66,"address":[],"length":0,"stats":{"Line":0}},{"line":71,"address":[],"length":0,"stats":{"Line":6}},{"line":74,"address":[624981,624997],"length":1,"stats":{"Line":6}},{"line":82,"address":[],"length":0,"stats":{"Line":4}},{"line":83,"address":[],"length":0,"stats":{"Line":0}},{"line":104,"address":[2156880],"length":1,"stats":{"Line":1}},{"line":105,"address":[],"length":0,"stats":{"Line":1}},{"line":112,"address":[613680],"length":1,"stats":{"Line":3}},{"line":113,"address":[],"length":0,"stats":{"Line":3}},{"line":118,"address":[],"length":0,"stats":{"Line":2}},{"line":119,"address":[614037],"length":1,"stats":{"Line":2}},{"line":124,"address":[],"length":0,"stats":{"Line":1}},{"line":125,"address":[],"length":0,"stats":{"Line":1}},{"line":130,"address":[],"length":0,"stats":{"Line":1}},{"line":131,"address":[],"length":0,"stats":{"Line":1}},{"line":139,"address":[],"length":0,"stats":{"Line":4}},{"line":141,"address":[],"length":0,"stats":{"Line":0}}],"covered":22,"coverable":26},{"path":["/","home","botahamec","Projects","happylock","src","rwlock.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_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`]. 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/// RAII structure that unlocks the shared read access to a [`RwLock`] when\n/// dropped.\n///\n/// This structure is created when the [`RwLock`] is put in a wrapper type,\n/// such as [`LockCollection`], and a read-only guard is obtained through the\n/// wrapper.\n///\n/// This is similar to [`RwLockReadGuard`], except it does not hold a\n/// [`ThreadKey`].\n///\n/// [`Deref`]: `std::ops::Deref`\n/// [`DerefMut`]: `std::ops::DerefMut`\n/// [`LockCollection`]: `crate::LockCollection`\npub struct RwLockReadRef\u003c'a, T: ?Sized, R: RawRwLock\u003e(\n\t\u0026'a RwLock\u003cT, R\u003e,\n\tPhantomData\u003cR::GuardMarker\u003e,\n);\n\n/// RAII structure that unlocks the exclusive write access to a [`RwLock`] when\n/// dropped.\n///\n/// This structure is created when the [`RwLock`] is put in a wrapper type,\n/// such as [`LockCollection`], and a mutable guard is obtained through the\n/// wrapper.\n///\n/// This is similar to [`RwLockWriteGuard`], except it does not hold a\n/// [`ThreadKey`].\n///\n/// [`Deref`]: `std::ops::Deref`\n/// [`DerefMut`]: `std::ops::DerefMut`\n/// [`LockCollection`]: `crate::LockCollection`\npub struct RwLockWriteRef\u003c'a, T: ?Sized, R: RawRwLock\u003e(\n\t\u0026'a RwLock\u003cT, R\u003e,\n\tPhantomData\u003cR::GuardMarker\u003e,\n);\n\n/// RAII structure used to release the shared read access of a lock when\n/// dropped.\n///\n/// This structure is created by the [`read`] and [`try_read`] methods on\n/// [`RwLock`].\n///\n/// This guard holds a [`ThreadKey`] for its entire lifetime. Therefore, a new\n/// lock cannot be acquired until this one is dropped. The [`ThreadKey`] can be\n/// reacquired using [`RwLock::unlock_read`].\n///\n/// [`Deref`]: `std::ops::Deref`\n/// [`DerefMut`]: `std::ops::DerefMut`\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/// This guard holds a [`ThreadKey`] for its entire lifetime. Therefor, a new\n/// lock cannot be acquired until this one is dropped. The [`ThreadKey`] can be\n/// reacquired using [`RwLock::unlock_write`].\n///\n/// [`Deref`]: `std::ops::Deref`\n/// [`DerefMut`]: `std::ops::DerefMut`\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::LockCollection;\n\tuse crate::RwLock;\n\tuse crate::ThreadKey;\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 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_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_scoped_write() {\n\t\tlet mut key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"Hello, world!\");\n\n\t\tlock.scoped_write(\u0026mut key, |guard| {\n\t\t\tassert!(lock.is_locked());\n\t\t\tassert_eq!(*guard, \"Hello, world!\");\n\n\t\t\tstd::thread::scope(|s| {\n\t\t\t\ts.spawn(|| {\n\t\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\t\tassert!(lock.try_read(key).is_err());\n\t\t\t\t});\n\t\t\t})\n\t\t})\n\t}\n\n\t#[test]\n\tfn get_mut_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet mut lock = crate::RwLock::from(42);\n\n\t\tlet mut_ref = lock.get_mut();\n\t\t*mut_ref = 24;\n\n\t\tlock.scoped_read(key, |guard| assert_eq!(*guard, 24))\n\t}\n\n\t#[test]\n\tfn try_write_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"Hello\");\n\t\tlet guard = lock.write(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = lock.try_write(key);\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn try_read_can_fail() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"Hello\");\n\t\tlet guard = lock.write(key);\n\n\t\tstd::thread::scope(|s| {\n\t\t\ts.spawn(|| {\n\t\t\t\tlet key = ThreadKey::get().unwrap();\n\t\t\t\tlet r = lock.try_read(key);\n\t\t\t\tassert!(r.is_err());\n\t\t\t});\n\t\t});\n\n\t\tdrop(guard);\n\t}\n\n\t#[test]\n\tfn read_display_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet guard = lock.read(key);\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn write_display_works() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet guard = lock.write(key);\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn read_ref_display_works() {\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet guard = unsafe { lock.try_read_no_key().unwrap() };\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn write_ref_display_works() {\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\t\tlet guard = unsafe { lock.try_write_no_key().unwrap() };\n\t\tassert_eq!(guard.to_string(), \"Hello, world!\".to_string());\n\t}\n\n\t#[test]\n\tfn dropping_read_ref_releases_rwlock() {\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\n\t\tlet guard = unsafe { lock.try_read_no_key().unwrap() };\n\t\tdrop(guard);\n\n\t\tassert!(!lock.is_locked());\n\t}\n\n\t#[test]\n\tfn dropping_write_guard_releases_rwlock() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock: crate::RwLock\u003c_\u003e = RwLock::new(\"Hello, world!\");\n\n\t\tlet guard = lock.write(key);\n\t\tdrop(guard);\n\n\t\tassert!(!lock.is_locked());\n\t}\n\n\t#[test]\n\tfn unlock_write() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"Hello, world\");\n\n\t\tlet mut guard = lock.write(key);\n\t\t*guard = \"Goodbye, world!\";\n\t\tlet key = RwLock::unlock_write(guard);\n\n\t\tlet guard = lock.read(key);\n\t\tassert_eq!(*guard, \"Goodbye, world!\");\n\t}\n\n\t#[test]\n\tfn unlock_read() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"Hello, world\");\n\n\t\tlet guard = lock.read(key);\n\t\tassert_eq!(*guard, \"Hello, world\");\n\t\tlet key = RwLock::unlock_read(guard);\n\n\t\tlet guard = lock.write(key);\n\t\tassert_eq!(*guard, \"Hello, world\");\n\t}\n\n\t#[test]\n\tfn read_ref_as_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = LockCollection::new(crate::RwLock::new(\"hi\"));\n\t\tlet guard = lock.read(key);\n\n\t\tassert_eq!(*(*guard).as_ref(), \"hi\");\n\t}\n\n\t#[test]\n\tfn read_guard_as_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"hi\");\n\t\tlet guard = lock.read(key);\n\n\t\tassert_eq!(*guard.as_ref(), \"hi\");\n\t}\n\n\t#[test]\n\tfn write_ref_as_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = LockCollection::new(crate::RwLock::new(\"hi\"));\n\t\tlet guard = lock.lock(key);\n\n\t\tassert_eq!(*(*guard).as_ref(), \"hi\");\n\t}\n\n\t#[test]\n\tfn write_guard_as_ref() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"hi\");\n\t\tlet guard = lock.write(key);\n\n\t\tassert_eq!(*guard.as_ref(), \"hi\");\n\t}\n\n\t#[test]\n\tfn write_guard_as_mut() {\n\t\tlet key = ThreadKey::get().unwrap();\n\t\tlet lock = crate::RwLock::new(\"hi\");\n\t\tlet mut guard = lock.write(key);\n\n\t\tassert_eq!(*guard.as_mut(), \"hi\");\n\t\t*guard.as_mut() = \"foo\";\n\t\tassert_eq!(*guard.as_mut(), \"foo\");\n\t}\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","botahamec","Projects","happylock","src","thread.rs"],"content":"use std::marker::PhantomData;\n\nmod scope;\n\n#[derive(Debug)]\npub struct Scope\u003c'scope, 'env: 'scope\u003e(PhantomData\u003c(\u0026'env (), \u0026'scope ())\u003e);\n\n#[derive(Debug)]\npub struct ScopedJoinHandle\u003c'scope, T\u003e {\n\thandle: std::thread::JoinHandle\u003cT\u003e,\n\t_phantom: PhantomData\u003c\u0026'scope ()\u003e,\n}\n\npub struct JoinHandle\u003cT\u003e {\n\thandle: std::thread::JoinHandle\u003cT\u003e,\n\tkey: crate::ThreadKey,\n}\n\npub struct ThreadBuilder(std::thread::Builder);\n","traces":[],"covered":0,"coverable":0}]};
</script>
<script crossorigin>/** @license React v16.13.1
* react.production.min.js
@@ -461,14 +536,11 @@ function findFolders(files) {
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),
- ];
+ return [...folders, ...files.filter(file => file.path.length === 1)];
}
class App extends React.Component {
@@ -482,12 +554,12 @@ class App extends React.Component {
componentDidMount() {
this.updateStateFromLocation();
- window.addEventListener("hashchange", () => this.updateStateFromLocation(), false);
+ window.addEventListener('hashchange', () => this.updateStateFromLocation(), false);
}
updateStateFromLocation() {
if (window.location.hash.length > 1) {
- const current = window.location.hash.substr(1).split('/');
+ const current = window.location.hash.slice(1).split('/').map(decodeURIComponent);
this.setState({current});
} else {
this.setState({current: []});
@@ -529,15 +601,21 @@ class App extends React.Component {
}
selectFile(file) {
- this.setState(({current}) => {
- return {current: [...current, file.path[0]]};
- }, () => this.updateHash());
+ 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());
+ this.setState(
+ ({current}) => {
+ return {current: current.slice(0, current.length - 1)};
+ },
+ () => this.updateHash(),
+ );
}
updateHash() {
@@ -551,101 +629,132 @@ class App extends React.Component {
function FilesList({folder, onSelectFile, onBack}) {
let files = folder.children;
- return e('div', {className: 'display-folder'},
+ 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(
+ '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})),
),
- 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);
+ 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': ''),
+ 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)}%)` : ''))
- )
+ 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})
- );
+ 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);
+ 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'},
+ 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)}%)` : ''))
- )
+ 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)}%)` : '',
+ ),
+ e('input', {id: 'theme-toggle', type: 'checkbox', hidden: true}),
+ e('label', {for: 'theme-toggle', id: 'theme-toggle-label'}, '🌙'),
+ ),
);
}
function FileContent({file}) {
- return e('pre', {className: 'file-content'},
+ 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);
- })
+ const nbHit = covered? trace.stats.Line: 0;
+ return e(
+ 'div',
+ { className: 'code-text-container' },
+ e(
+ 'code',
+ {
+ className: 'code-line' + (covered ? ' code-line_covered' : '') + (uncovered ? ' code-line_uncovered' : ''),
+ },
+ line
+ ),
+ e(
+ 'div',
+ { className: 'cover-indicator' + (covered? ' check-cover': '') + (uncovered? ' no-cover': '')},
+ e(
+ 'div',
+ { className: (covered? 'stat-line-hit': '')},
+ covered? nbHit: ""
+ )
+ )
+ );
+ }),
);
}
-(function(){
+(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);
- });
+ previousData &&
+ previousData.files.forEach(file => {
+ const path = file.path.slice(commonPath.length).join('/');
+ prevFilesMap.set(path, file);
+ });
- const files = data.files.map((file) => {
+ const files = data.files.map(file => {
const path = file.path.slice(commonPath.length);
- const { covered = 0, coverable = 0 } = prevFilesMap.get(path.join('/')) || {};
+ const {covered = 0, coverable = 0} = prevFilesMap.get(path.join('/')) || {};
return {
...file,
path,
parent: commonPath,
- prevRun: { covered, coverable },
+ prevRun: {covered, coverable},
};
});
@@ -661,11 +770,29 @@ function FileContent({file}) {
prevRun: {
covered: children.reduce((sum, file) => sum + file.prevRun.covered, 0),
coverable: children.reduce((sum, file) => sum + file.prevRun.coverable, 0),
- }
+ },
};
+ if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
+ document.documentElement.setAttribute('data-theme', 'dark');
+ }
+
ReactDOM.render(e(App, {root, prevFilesMap}), document.getElementById('root'));
-}());
+
+ const toggle = document.getElementById('theme-toggle');
+ const label = document.getElementById('theme-toggle-label');
+ label.textContent = '🌙';
+
+ toggle.addEventListener('change', () => {
+ if (toggle.checked) {
+ document.documentElement.setAttribute('data-theme', 'dark');
+ label.textContent = '☀️';
+ } else {
+ document.documentElement.removeAttribute('data-theme');
+ label.textContent = '🌙';
+ }
+ });
+})();
</script>
</body>
</html> \ No newline at end of file