use core::cell::{RefCell, RefMut};
use core::ops::DerefMut;
use core::panic::Location;
use super::{Mutex, MutexNode};
use crate::cfg::thread::LocalKey;
use crate::lock::{Lock, Wait};
pub type Key<N> = &'static LocalMutexNode<N>;
#[derive(Debug)]
#[repr(transparent)]
pub struct LocalMutexNode<N: 'static> {
#[cfg(not(all(loom, test)))]
key: LocalKey<RefCell<N>>,
#[cfg(all(loom, test))]
key: &'static LocalKey<RefCell<N>>,
}
#[cfg(not(tarpaulin_include))]
impl<N> LocalMutexNode<N> {
#[cfg(not(all(loom, test)))]
pub const fn new(key: LocalKey<RefCell<N>>) -> Self {
Self { key }
}
#[cfg(all(loom, test))]
pub const fn new(key: &'static LocalKey<RefCell<N>>) -> Self {
Self { key }
}
}
#[inline(never)]
#[cold]
fn panic_already_borrowed(caller: &Location<'static>) -> ! {
panic!("{}, conflict at: {}", already_borrowed_error!(), caller)
}
impl<T: ?Sized, L: Lock, W: Wait> Mutex<T, L, W> {
#[track_caller]
pub fn try_lock_with_local_then<N, F, Ret>(&self, node: Key<N>, f: F) -> Ret
where
N: DerefMut<Target = MutexNode<L>>,
F: FnOnce(Option<&mut T>) -> Ret,
{
self.with_local_node_then(node, |m, n| m.try_lock_with_then(n, f))
}
pub unsafe fn try_lock_with_local_then_unchecked<F, Ret, N>(&self, node: Key<N>, f: F) -> Ret
where
N: DerefMut<Target = MutexNode<L>>,
F: FnOnce(Option<&mut T>) -> Ret,
{
unsafe { self.with_local_node_then_unchecked(node, |m, n| m.try_lock_with_then(n, f)) }
}
#[track_caller]
pub fn lock_with_local_then<N, F, Ret>(&self, node: Key<N>, f: F) -> Ret
where
N: DerefMut<Target = MutexNode<L>>,
F: FnOnce(&mut T) -> Ret,
{
self.with_local_node_then(node, |m, n| m.lock_with_then(n, f))
}
pub unsafe fn lock_with_local_then_unchecked<N, F, Ret>(&self, node: Key<N>, f: F) -> Ret
where
N: DerefMut<Target = MutexNode<L>>,
F: FnOnce(&mut T) -> Ret,
{
unsafe { self.with_local_node_then_unchecked(node, |m, n| m.lock_with_then(n, f)) }
}
#[track_caller]
fn with_local_node_then<N, F, Ret>(&self, node: Key<N>, f: F) -> Ret
where
N: DerefMut<Target = MutexNode<L>>,
F: FnOnce(&Self, &mut MutexNode<L>) -> Ret,
{
let caller = Location::caller();
let panic = |_| panic_already_borrowed(caller);
let f = |mut node: RefMut<N>| f(self, &mut node);
node.key.with(|node| node.try_borrow_mut().map_or_else(panic, f))
}
unsafe fn with_local_node_then_unchecked<N, F, Ret>(&self, node: Key<N>, f: F) -> Ret
where
N: DerefMut<Target = MutexNode<L>>,
F: FnOnce(&Self, &mut MutexNode<L>) -> Ret,
{
node.key.with(|node| f(self, unsafe { &mut *node.as_ptr() }))
}
}