use core::marker::PhantomData;
use crate::{
acquirable::Acquirable,
level::{IsLevel, LockAfter},
mutex::{Mutex, guard::MutexGuard},
raw_mutex::RawMutex,
set::{LockSet, build_sorted},
};
#[cfg(feature = "std")]
use crate::level::Bottom;
#[diagnostic::on_unimplemented(
message = "`{Self}` cannot be used as a lock target",
note = "use `&Mutex<T>` for a single lock or `&LockSet` for multiple locks"
)]
pub trait Lockable<'a> {
type Guard;
type MinLvl: IsLevel;
type MaxLvl: IsLevel;
fn lock_impl(&'a self) -> Self::Guard;
}
impl<'a, T: 'a, Lvl: IsLevel, R: RawMutex + 'a> Lockable<'a> for Mutex<T, Lvl, R> {
type Guard = MutexGuard<'a, R, T>;
type MinLvl = Lvl;
type MaxLvl = Lvl;
fn lock_impl(&'a self) -> Self::Guard {
let raw_guard = self.raw.lock();
MutexGuard {
data: &self.data,
_raw_guard: raw_guard,
}
}
}
#[cfg(target_has_atomic = "ptr")]
impl<'a, T: Lockable<'a>> Lockable<'a> for alloc::sync::Arc<T> {
type Guard = T::Guard;
type MinLvl = T::MinLvl;
type MaxLvl = T::MaxLvl;
fn lock_impl(&'a self) -> Self::Guard {
T::lock_impl(self)
}
}
impl<'a, T: Lockable<'a>> Lockable<'a> for alloc::rc::Rc<T> {
type Guard = T::Guard;
type MinLvl = T::MinLvl;
type MaxLvl = T::MaxLvl;
fn lock_impl(&'a self) -> Self::Guard {
T::lock_impl(self)
}
}
impl<'a, T: Lockable<'a>> Lockable<'a> for alloc::boxed::Box<T> {
type Guard = T::Guard;
type MinLvl = T::MinLvl;
type MaxLvl = T::MaxLvl;
fn lock_impl(&'a self) -> Self::Guard {
T::lock_impl(self)
}
}
impl<'a, L: Acquirable<'a>> Lockable<'a> for LockSet<L> {
type Guard = <L as Acquirable<'a>>::Guard;
type MinLvl = <L as Acquirable<'a>>::MinLvl;
type MaxLvl = <L as Acquirable<'a>>::MaxLvl;
fn lock_impl(&'a self) -> Self::Guard {
self.lock_sorted()
}
}
pub struct MutexKey<'scope, Lvl: IsLevel> {
_brand: PhantomData<fn(&'scope ()) -> &'scope ()>,
_not_send: PhantomData<*const ()>,
_level: PhantomData<Lvl>,
}
impl<Lvl: IsLevel> core::fmt::Debug for MutexKey<'_, Lvl> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("MutexKey").finish_non_exhaustive()
}
}
impl<'scope, Lvl: IsLevel> MutexKey<'scope, Lvl> {
pub(crate) fn new_internal() -> Self {
Self {
_brand: PhantomData,
_not_send: PhantomData,
_level: PhantomData,
}
}
pub fn lock<'a, L: Lockable<'a>>(self, target: &'a L) -> (L::Guard, MutexKey<'scope, L::MaxLvl>)
where
L::MinLvl: LockAfter<Lvl>,
{
let guards = target.lock_impl();
(guards, MutexKey::new_internal())
}
pub fn lock_with<'a, L, F, Ret>(
self,
lockable: &'a L,
f: F,
) -> (Ret, MutexKey<'scope, <L as Acquirable<'a>>::MaxLvl>)
where
L: Acquirable<'a>,
<L as Acquirable<'a>>::MinLvl: LockAfter<Lvl>,
F: FnOnce(<L as Acquirable<'a>>::Guard) -> Ret,
{
let (indices, has_duplicates) = build_sorted(lockable);
assert!(!has_duplicates, "lock_with called with duplicate locks");
let guards = lockable.lock_sorted(&indices);
let result = f(guards);
(result, MutexKey::new_internal())
}
pub fn subscope<F, Ret>(self, f: F) -> (Ret, MutexKey<'scope, Lvl>)
where
F: for<'inner> FnOnce(MutexKey<'inner, Lvl>) -> Ret,
{
let result = f(MutexKey {
_brand: PhantomData,
_not_send: PhantomData,
_level: PhantomData,
});
(
result,
MutexKey {
_brand: PhantomData,
_not_send: PhantomData,
_level: PhantomData,
},
)
}
}
#[cfg(feature = "std")]
pub fn try_lock_scope<F, Ret>(f: F) -> Option<Ret>
where
F: for<'scope> FnOnce(MutexKey<'scope, Bottom>) -> Ret,
{
let mut handle = crate::key_handle::KeyHandle::try_claim()?;
Some(handle.scope(f))
}
#[cfg(feature = "std")]
#[allow(clippy::expect_used)] pub fn lock_scope<F, Ret>(f: F) -> Ret
where
F: for<'scope> FnOnce(MutexKey<'scope, Bottom>) -> Ret,
{
try_lock_scope(f).expect("nested lock_scope -- use key.subscope() or try_lock_scope()")
}