use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; use crate::{ key::Keyable, lockable::{Lockable, OwnedLockable}, }; /// returns `true` if the list contains a duplicate #[must_use] fn contains_duplicates(l: &[usize]) -> bool { for i in 0..l.len() { for j in (i + 1)..l.len() { if l[i] == l[j] { return true; } } } false } /// A type which can be locked. /// /// This could be a tuple of [`Lockable`] types, an array, or a `Vec`. But it /// can be safely locked without causing a deadlock. To do this, it is very /// important that no duplicate locks are included within. #[derive(Debug, Clone, Copy)] pub struct LockCollection { collection: L, } /// A guard for a generic [`Lockable`] type. pub struct LockGuard<'a, 'key: 'a, L: Lockable<'a>, Key: Keyable + 'key> { guard: L::Output, key: Key, _phantom: PhantomData<&'key ()>, } impl<'a, L: OwnedLockable<'a>> From for LockCollection { fn from(value: L) -> Self { Self::new(value) } } impl<'a, L: OwnedLockable<'a>> AsRef for LockCollection { fn as_ref(&self) -> &L { &self.collection } } impl<'a, L: OwnedLockable<'a>> AsMut for LockCollection { fn as_mut(&mut self) -> &mut L { &mut self.collection } } impl<'a, L: OwnedLockable<'a>> AsRef for LockCollection { fn as_ref(&self) -> &Self { self } } impl<'a, L: OwnedLockable<'a>> AsMut for LockCollection { fn as_mut(&mut self) -> &mut Self { self } } impl IntoIterator for LockCollection { type Item = L::Item; type IntoIter = L::IntoIter; fn into_iter(self) -> Self::IntoIter { self.collection.into_iter() } } impl<'a, L> IntoIterator for &'a LockCollection where &'a L: IntoIterator, { type Item = <&'a L as IntoIterator>::Item; type IntoIter = <&'a L as IntoIterator>::IntoIter; fn into_iter(self) -> Self::IntoIter { self.collection.into_iter() } } impl<'a, L> IntoIterator for &'a mut LockCollection where &'a mut L: IntoIterator, { type Item = <&'a mut L as IntoIterator>::Item; type IntoIter = <&'a mut L as IntoIterator>::IntoIter; fn into_iter(self) -> Self::IntoIter { self.collection.into_iter() } } impl<'a, L: OwnedLockable<'a>, I: FromIterator + OwnedLockable<'a>> FromIterator for LockCollection { fn from_iter>(iter: T) -> Self { let iter: I = iter.into_iter().collect(); Self::new(iter) } } impl<'a, E: OwnedLockable<'a> + Extend, L: OwnedLockable<'a>> Extend for LockCollection { fn extend>(&mut self, iter: T) { self.collection.extend(iter) } } impl<'a, L: OwnedLockable<'a>> LockCollection { /// Creates a new collection of owned locks. /// /// Because the locks are owned, there's no need to do any checks for /// duplicate values. #[must_use] pub const fn new(collection: L) -> Self { Self { collection } } /// Creates a new collection of owned locks. /// /// Because the locks are owned, there's no need to do any checks for /// duplicate values. #[must_use] pub const fn new_ref(collection: &L) -> LockCollection<&L> { LockCollection { collection } } } impl LockCollection { /// Creates a new collections of locks. /// /// # Safety /// /// This results in undefined behavior if any locks are presented twice /// within this collection. #[must_use] pub const unsafe fn new_unchecked(collection: L) -> Self { Self { collection } } } impl<'a, L: Lockable<'a>> LockCollection { /// Creates a new collection of locks. /// /// This returns `None` if any locks are found twice in the given /// collection. /// /// # Performance /// /// This does a check at runtime to make sure that the collection contains /// no two copies of the same lock. This is an `O(n^2)` operation. Prefer /// [`LockCollection::new`] or [`LockCollection::new_ref`] instead. #[must_use] pub fn try_new(collection: L) -> Option { let ptrs = collection.get_ptrs(); if contains_duplicates(&ptrs) { return None; } Some(Self { collection }) } /// Locks the lockable type and returns a guard that can be used to access /// the underlying data. pub fn lock<'key: 'a, Key: Keyable + 'key>(&'a self, key: Key) -> LockGuard<'a, 'key, L, Key> { LockGuard { // safety: we have the thread's key guard: unsafe { self.collection.lock() }, key, _phantom: PhantomData, } } /// Attempts to lock the guard without blocking. /// /// If successful, this method returns a guard that can be used to access /// the data. Otherwise, `None` is returned. pub fn try_lock<'key: 'a, Key: Keyable + 'key>( &'a self, key: Key, ) -> Option> { // safety: we have the thread's key unsafe { self.collection.try_lock() }.map(|guard| LockGuard { guard, key, _phantom: PhantomData, }) } /// Unlocks the underlying lockable data type, returning the key that's /// associated with it. #[allow(clippy::missing_const_for_fn)] pub fn unlock<'key: 'a, Key: Keyable + 'key>(guard: LockGuard<'a, 'key, L, Key>) -> Key { drop(guard.guard); guard.key } } impl<'a, L: 'a> LockCollection where &'a L: IntoIterator, { /// Returns an iterator over references to each value in the collection. pub fn iter(&'a self) -> <&'a L as IntoIterator>::IntoIter { self.into_iter() } } impl<'a, L: 'a> LockCollection where &'a mut L: IntoIterator, { /// Returns an iterator over mutable references to each value in the /// collection. pub fn iter_mut(&'a mut self) -> <&'a mut L as IntoIterator>::IntoIter { self.into_iter() } } impl<'a, 'key: 'a, L: Lockable<'a>, Key: Keyable> Deref for LockGuard<'a, 'key, L, Key> { type Target = L::Output; fn deref(&self) -> &Self::Target { &self.guard } } impl<'a, 'key: 'a, L: Lockable<'a>, Key: Keyable> DerefMut for LockGuard<'a, 'key, L, Key> { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.guard } }