use core::fmt::{self, Debug, Display, Formatter};
use crate::cfg::atomic::AtomicBool;
use crate::inner::raw as inner;
use crate::relax::{Relax, RelaxWait};
#[cfg(test)]
use crate::test::{LockNew, LockThen};
#[cfg(all(loom, test))]
use crate::loom::{Guard, GuardDeref, GuardDerefMut};
#[cfg(all(loom, test))]
use crate::test::{AsDeref, AsDerefMut};
#[derive(Debug)]
#[repr(transparent)]
pub struct MutexNode {
inner: inner::MutexNode<AtomicBool>,
}
unsafe impl Send for MutexNode {}
unsafe impl Sync for MutexNode {}
impl MutexNode {
#[must_use]
#[inline(always)]
pub fn new() -> Self {
Self { inner: inner::MutexNode::new() }
}
}
#[cfg(not(tarpaulin_include))]
impl Default for MutexNode {
#[inline(always)]
fn default() -> Self {
Self::new()
}
}
type MutexInner<T, R> = inner::Mutex<T, AtomicBool, RelaxWait<R>>;
pub struct Mutex<T: ?Sized, R> {
pub(super) inner: MutexInner<T, R>,
}
unsafe impl<T: ?Sized + Send, R> Send for Mutex<T, R> {}
unsafe impl<T: ?Sized + Send, R> Sync for Mutex<T, R> {}
impl<T, R> Mutex<T, R> {
#[inline]
pub fn new(value: T) -> Self {
Self { inner: inner::Mutex::new(value) }
}
}
impl<T: ?Sized, R: Relax> Mutex<T, R> {
#[inline]
pub fn lock(&self) -> MutexGuard<'_, T, R> {
self.lock_with(MutexNode::new())
}
#[inline]
pub fn lock_with(&self, node: MutexNode) -> MutexGuard<'_, T, R> {
self.inner.lock_with(node.inner).into()
}
#[inline]
pub fn lock_then<F, Ret>(&self, f: F) -> Ret
where
F: FnOnce(MutexGuard<'_, T, R>) -> Ret,
{
self.lock_with_then(MutexNode::new(), f)
}
#[inline]
pub fn lock_with_then<F, Ret>(&self, node: MutexNode, f: F) -> Ret
where
F: FnOnce(MutexGuard<'_, T, R>) -> Ret,
{
f(self.lock_with(node))
}
}
impl<T: ?Sized, R> Mutex<T, R> {
#[cfg(not(all(loom, test)))]
#[inline(always)]
pub fn get_mut(&mut self) -> &mut T {
self.inner.get_mut()
}
}
impl<T: Default, R> Default for Mutex<T, R> {
#[inline]
fn default() -> Self {
Self::new(Default::default())
}
}
impl<T, R> From<T> for Mutex<T, R> {
#[inline]
fn from(data: T) -> Self {
Self::new(data)
}
}
impl<T: ?Sized + Debug, R: Relax> Debug for Mutex<T, R> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
self.inner.fmt(f)
}
}
#[cfg(test)]
impl<T: ?Sized, R> LockNew for Mutex<T, R> {
type Target = T;
fn new(value: Self::Target) -> Self
where
Self::Target: Sized,
{
Self::new(value)
}
}
#[cfg(test)]
impl<T: ?Sized, R: Relax> LockThen for Mutex<T, R> {
type Guard<'a> = MutexGuard<'a, Self::Target, R>
where
Self: 'a,
Self::Target: 'a;
fn lock_then<F, Ret>(&self, f: F) -> Ret
where
F: FnOnce(MutexGuard<'_, T, R>) -> Ret,
{
self.lock_then(f)
}
}
#[cfg(all(not(loom), test))]
impl<T: ?Sized, R> crate::test::LockData for Mutex<T, R> {
fn get_mut(&mut self) -> &mut Self::Target {
self.get_mut()
}
}
type GuardInner<'a, T, R> = inner::MutexGuard<'a, T, AtomicBool, RelaxWait<R>>;
#[must_use = "if unused the Mutex will immediately unlock"]
pub struct MutexGuard<'a, T: ?Sized, R> {
inner: GuardInner<'a, T, R>,
}
unsafe impl<T: ?Sized + Send, R> Send for MutexGuard<'_, T, R> {}
unsafe impl<T: ?Sized + Sync, R> Sync for MutexGuard<'_, T, R> {}
impl<'a, T: ?Sized, R> MutexGuard<'a, T, R> {
#[must_use]
#[inline]
pub fn unlock(self) -> MutexNode {
let inner = self.inner.into_node();
MutexNode { inner }
}
}
#[doc(hidden)]
impl<'a, T: ?Sized, R> From<GuardInner<'a, T, R>> for MutexGuard<'a, T, R> {
#[inline(always)]
fn from(inner: GuardInner<'a, T, R>) -> Self {
Self { inner }
}
}
impl<'a, T: ?Sized + Debug, R> Debug for MutexGuard<'a, T, R> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
self.inner.fmt(f)
}
}
impl<'a, T: ?Sized + Display, R> Display for MutexGuard<'a, T, R> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
self.inner.fmt(f)
}
}
#[cfg(not(all(loom, test)))]
impl<'a, T: ?Sized, R> core::ops::Deref for MutexGuard<'a, T, R> {
type Target = T;
#[inline(always)]
fn deref(&self) -> &T {
&self.inner
}
}
#[cfg(not(all(loom, test)))]
impl<'a, T: ?Sized, R> core::ops::DerefMut for MutexGuard<'a, T, R> {
#[inline(always)]
fn deref_mut(&mut self) -> &mut T {
&mut self.inner
}
}
#[cfg(all(loom, test))]
#[cfg(not(tarpaulin_include))]
unsafe impl<T: ?Sized, R> crate::loom::Guard for MutexGuard<'_, T, R> {
type Target = T;
fn get(&self) -> &loom::cell::UnsafeCell<Self::Target> {
self.inner.get()
}
}
#[cfg(all(loom, test))]
#[cfg(not(tarpaulin_include))]
impl<T: ?Sized, R> AsDeref for MutexGuard<'_, T, R> {
type Target = T;
type Deref<'a> = GuardDeref<'a, Self>
where
Self: 'a,
Self::Target: 'a;
fn as_deref(&self) -> Self::Deref<'_> {
self.get_ref()
}
}
#[cfg(all(loom, test))]
#[cfg(not(tarpaulin_include))]
impl<T: ?Sized, R> AsDerefMut for MutexGuard<'_, T, R> {
type DerefMut<'a> = GuardDerefMut<'a, Self>
where
Self: 'a,
Self::Target: 'a;
fn as_deref_mut(&mut self) -> Self::DerefMut<'_> {
self.get_mut()
}
}
#[cfg(all(not(loom), test))]
mod test {
use crate::raw::yields::Mutex;
use crate::test::tests;
#[test]
fn lots_and_lots_lock() {
tests::lots_and_lots_lock::<Mutex<_>>();
}
#[test]
fn smoke() {
tests::smoke::<Mutex<_>>();
}
#[test]
fn test_guard_debug_display() {
tests::test_guard_debug_display::<Mutex<_>>();
}
#[test]
fn test_mutex_debug() {
tests::test_mutex_debug::<Mutex<_>>();
}
#[test]
fn test_mutex_from() {
tests::test_mutex_from::<Mutex<_>>();
}
#[test]
fn test_mutex_default() {
tests::test_mutex_default::<Mutex<_>>();
}
#[test]
fn test_get_mut() {
tests::test_get_mut::<Mutex<_>>();
}
#[test]
fn test_lock_arc_nested() {
tests::test_lock_arc_nested::<Mutex<_>, Mutex<_>>();
}
#[test]
fn test_acquire_more_than_one_lock() {
tests::test_acquire_more_than_one_lock::<Mutex<_>>();
}
#[test]
fn test_lock_arc_access_in_unwind() {
tests::test_lock_arc_access_in_unwind::<Mutex<_>>();
}
#[test]
fn test_lock_unsized() {
tests::test_lock_unsized::<Mutex<_>>();
}
#[test]
fn test_guard_into_node() {
let mutex = Mutex::new(0);
let mut guard = mutex.lock();
*guard += 1;
let node = guard.unlock();
assert_eq!(*mutex.lock_with(node), 1);
}
}
#[cfg(any(all(test, not(miri), not(loom)), all(miri, ignore_leaks)))]
mod test_leaks_expected {
use std::sync::mpsc::channel;
use std::sync::Arc;
use std::thread;
use crate::raw::yields::{Mutex, MutexGuard};
use crate::raw::MutexNode;
fn assert_forget_guard<T, F>(f: F)
where
F: FnOnce(MutexGuard<i32>) -> T,
{
let (tx1, rx1) = channel();
let (tx2, rx2) = channel();
let data = Arc::new(Mutex::new(0));
let handle = Arc::clone(&data);
let node = MutexNode::new();
let guard = data.lock_with(node);
thread::spawn(move || {
rx2.recv().unwrap();
let node = MutexNode::new();
let guard = handle.lock_with(node);
core::mem::forget(guard);
tx1.send(()).unwrap();
});
let _t = f(guard);
tx2.send(()).unwrap();
rx1.recv().unwrap();
}
#[test]
fn test_forget_guard_drop_predecessor() {
assert_forget_guard(|guard| drop(guard));
}
#[test]
#[allow(clippy::redundant_closure_for_method_calls)]
fn test_foget_guard_unlock_predecessor() {
assert_forget_guard(|guard| guard.unlock());
}
}
#[cfg(all(loom, test))]
mod model {
use crate::loom::models;
use crate::raw::yields::Mutex;
#[test]
fn lock_join() {
models::lock_join::<Mutex<_>>();
}
}