use std::marker::PhantomData; use crate::{ context::{LockContext, LockingIterator, LockingTuple}, lockable::{Lockable, OwnedLockable}, ThreadKey, }; impl<'l, L> LockContext<'l, L> { /// Safety: Don't lock the locks in a different order than what the /// `LockContext` uses pub(crate) const unsafe 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` // The mutable reference ensures that all of the guards, which have a shared // borrow to the context, must be dropped first pub fn unlock(&mut self) -> Option { self.key.take() } } impl 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: (), } } } }