use core::cell::RefCell;
use super::{Mutex, MutexNode};
use crate::cfg::thread::LocalKey;
use crate::inner::raw as inner;
use crate::relax::Relax;
#[cfg(test)]
use crate::test::{LockNew, LockThen, LockWithThen, TryLockThen, TryLockWithThen};
type Key = &'static LocalMutexNode;
#[macro_export]
macro_rules! thread_local_node {
() => {};
($vis:vis static $node:ident; $($rest:tt)*) => {
$crate::__thread_local_node_inner!($vis $node, raw);
$crate::thread_local_node!($($rest)*);
};
($vis:vis static $node:ident) => {
$crate::__thread_local_node_inner!($vis $node, raw);
};
}
#[derive(Debug)]
#[repr(transparent)]
pub struct LocalMutexNode {
pub(crate) inner: inner::LocalMutexNode<MutexNode>,
}
#[cfg(not(tarpaulin_include))]
impl LocalMutexNode {
#[cfg(not(all(loom, test)))]
#[doc(hidden)]
#[must_use]
#[inline(always)]
pub const fn __new(key: LocalKey<RefCell<MutexNode>>) -> Self {
Self { inner: inner::LocalMutexNode::new(key) }
}
#[cfg(all(loom, test))]
#[must_use]
pub(crate) const fn new(key: &'static LocalKey<RefCell<MutexNode>>) -> Self {
Self { inner: inner::LocalMutexNode::new(key) }
}
}
impl<T: ?Sized, R: Relax> Mutex<T, R> {
#[doc = concat!("```should_panic(expected = ", already_borrowed_error!(), ")")]
#[inline]
#[track_caller]
pub fn try_lock_with_local_then<F, Ret>(&self, node: Key, f: F) -> Ret
where
F: FnOnce(Option<&mut T>) -> Ret,
{
self.inner.try_lock_with_local_then(&node.inner, f)
}
#[inline]
pub unsafe fn try_lock_with_local_then_unchecked<F, Ret>(&self, node: Key, f: F) -> Ret
where
F: FnOnce(Option<&mut T>) -> Ret,
{
unsafe { self.inner.try_lock_with_local_then_unchecked(&node.inner, f) }
}
#[doc = concat!("```should_panic(expected = ", already_borrowed_error!(), ")")]
#[inline]
#[track_caller]
pub fn lock_with_local_then<F, Ret>(&self, node: Key, f: F) -> Ret
where
F: FnOnce(&mut T) -> Ret,
{
self.inner.lock_with_local_then(&node.inner, f)
}
#[inline]
pub unsafe fn lock_with_local_then_unchecked<F, Ret>(&self, node: Key, f: F) -> Ret
where
F: FnOnce(&mut T) -> Ret,
{
unsafe { self.inner.lock_with_local_then_unchecked(&node.inner, f) }
}
#[cfg(doctest)]
#[cfg(not(tarpaulin_include))]
const fn __borrows_must_not_escape_closure() {}
}
#[cfg(test)]
#[cfg(not(tarpaulin_include))]
thread_local_node!(static TEST_NODE);
#[cfg(test)]
struct MutexPanic<T: ?Sized, R>(Mutex<T, R>);
#[cfg(test)]
impl<T: ?Sized, R> LockNew for MutexPanic<T, R> {
type Target = T;
fn new(value: Self::Target) -> Self
where
Self::Target: Sized,
{
Self(Mutex::new(value))
}
}
#[cfg(test)]
impl<T: ?Sized, R: Relax> LockWithThen for MutexPanic<T, R> {
type Node = ();
type Guard<'a>
= &'a mut Self::Target
where
Self: 'a,
Self::Target: 'a;
fn lock_with_then<F, Ret>(&self, (): &mut Self::Node, f: F) -> Ret
where
F: FnOnce(&mut Self::Target) -> Ret,
{
self.0.lock_with_local_then(&TEST_NODE, f)
}
}
#[cfg(test)]
impl<T: ?Sized, R: Relax> TryLockWithThen for MutexPanic<T, R> {
fn try_lock_with_then<F, Ret>(&self, (): &mut Self::Node, f: F) -> Ret
where
F: FnOnce(Option<&mut Self::Target>) -> Ret,
{
self.0.try_lock_with_local_then(&TEST_NODE, f)
}
fn is_locked(&self) -> bool {
self.0.is_locked()
}
}
#[cfg(test)]
impl<T: ?Sized, R: Relax> LockThen for MutexPanic<T, R> {}
#[cfg(test)]
impl<T: ?Sized, R: Relax> TryLockThen for MutexPanic<T, R> {}
#[cfg(test)]
struct MutexUnchecked<T: ?Sized, R>(Mutex<T, R>);
#[cfg(test)]
impl<T: ?Sized, R> LockNew for MutexUnchecked<T, R> {
type Target = T;
fn new(value: Self::Target) -> Self
where
Self::Target: Sized,
{
Self(Mutex::new(value))
}
}
#[cfg(test)]
impl<T: ?Sized, R: Relax> LockWithThen for MutexUnchecked<T, R> {
type Node = ();
type Guard<'a>
= &'a mut Self::Target
where
Self: 'a,
Self::Target: 'a;
fn lock_with_then<F, Ret>(&self, (): &mut Self::Node, f: F) -> Ret
where
F: FnOnce(&mut Self::Target) -> Ret,
{
unsafe { self.0.lock_with_local_then_unchecked(&TEST_NODE, f) }
}
}
#[cfg(test)]
impl<T: ?Sized, R: Relax> TryLockWithThen for MutexUnchecked<T, R> {
fn try_lock_with_then<F, Ret>(&self, (): &mut Self::Node, f: F) -> Ret
where
F: FnOnce(Option<&mut Self::Target>) -> Ret,
{
unsafe { self.0.try_lock_with_local_then_unchecked(&TEST_NODE, f) }
}
fn is_locked(&self) -> bool {
self.0.is_locked()
}
}
#[cfg(test)]
impl<T: ?Sized, R: Relax> LockThen for MutexUnchecked<T, R> {}
#[cfg(test)]
impl<T: ?Sized, R: Relax> TryLockThen for MutexUnchecked<T, R> {}
#[cfg(all(not(loom), test))]
mod test {
use crate::raw::MutexNode;
use crate::relax::Yield;
use crate::test::tests;
type MutexPanic<T> = super::MutexPanic<T, Yield>;
type MutexUnchecked<T> = super::MutexUnchecked<T, Yield>;
#[test]
fn ref_cell_node_drop_does_not_matter() {
use core::{cell::RefCell, mem};
assert!(!mem::needs_drop::<RefCell<MutexNode>>());
}
#[test]
fn lots_and_lots_lock() {
tests::lots_and_lots_lock::<MutexPanic<_>>();
}
#[test]
fn lots_and_lots_lock_unchecked() {
tests::lots_and_lots_lock::<MutexUnchecked<_>>();
}
#[test]
fn lots_and_lots_try_lock() {
tests::lots_and_lots_try_lock::<MutexPanic<_>>();
}
#[test]
fn lots_and_lots_try_lock_unchecked() {
tests::lots_and_lots_try_lock::<MutexUnchecked<_>>();
}
#[test]
fn lots_and_lots_mixed_lock() {
tests::lots_and_lots_mixed_lock::<MutexPanic<_>>();
}
#[test]
fn lots_and_lots_mixed_lock_unchecked() {
tests::lots_and_lots_mixed_lock::<MutexUnchecked<_>>();
}
#[test]
fn smoke() {
tests::smoke::<MutexPanic<_>>();
}
#[test]
fn smoke_unchecked() {
tests::smoke::<MutexUnchecked<_>>();
}
#[test]
fn test_try_lock() {
tests::test_try_lock::<MutexPanic<_>>();
}
#[test]
fn test_try_lock_unchecked() {
tests::test_try_lock::<MutexUnchecked<_>>();
}
#[test]
#[should_panic = already_borrowed_error!()]
fn test_lock_arc_nested() {
tests::test_lock_arc_nested::<MutexPanic<_>, MutexPanic<_>>();
}
#[test]
#[should_panic = already_borrowed_error!()]
fn test_acquire_more_than_one_lock() {
tests::test_acquire_more_than_one_lock::<MutexPanic<_>>();
}
#[test]
fn test_lock_arc_access_in_unwind() {
tests::test_lock_arc_access_in_unwind::<MutexPanic<_>>();
}
#[test]
fn test_lock_arc_access_in_unwind_unchecked() {
tests::test_lock_arc_access_in_unwind::<MutexUnchecked<_>>();
}
#[test]
fn test_lock_unsized() {
tests::test_lock_unsized::<MutexPanic<_>>();
}
#[test]
fn test_lock_unsized_unchecked() {
tests::test_lock_unsized::<MutexUnchecked<_>>();
}
}
#[cfg(all(loom, test))]
mod model {
use crate::loom::models;
use crate::relax::Yield;
type MutexPanic<T> = super::MutexPanic<T, Yield>;
type MutexUnchecked<T> = super::MutexUnchecked<T, Yield>;
#[test]
fn try_lock_join() {
models::try_lock_join::<MutexPanic<_>>();
}
#[test]
fn try_lock_join_unchecked() {
models::try_lock_join::<MutexUnchecked<_>>();
}
#[test]
fn lock_join() {
models::lock_join::<MutexPanic<_>>();
}
#[test]
fn lock_join_unchecked() {
models::lock_join::<MutexUnchecked<_>>();
}
#[test]
fn mixed_lock_join() {
models::mixed_lock_join::<MutexPanic<_>>();
}
#[test]
fn mixed_lock_join_unchecked() {
models::mixed_lock_join::<MutexUnchecked<_>>();
}
}