use core::fmt::{self, Debug, Display, Formatter};
use core::marker::PhantomData;
use crate::cfg::cell::{UnsafeCell, UnsafeCellWith};
use crate::inner::raw;
use crate::lock::{Lock, Wait};
#[cfg(feature = "thread_local")]
mod thread_local;
pub struct Mutex<T: ?Sized, L, Ws, Wq> {
lock: L,
queue: raw::Mutex<(), L, Wq>,
marker: PhantomData<Ws>,
data: UnsafeCell<T>,
}
unsafe impl<T: ?Sized + Send, L: Send, Ws, Wq> Send for Mutex<T, L, Ws, Wq> {}
unsafe impl<T: ?Sized + Send, L: Send, Ws, Wq> Sync for Mutex<T, L, Ws, Wq> {}
impl<T, L: Lock, Ws, Wq> Mutex<T, L, Ws, Wq> {
#[cfg(not(all(loom, test)))]
pub const fn new(value: T) -> Self {
let lock = Lock::UNLOCKED;
let queue = raw::Mutex::new(());
let data = UnsafeCell::new(value);
Self { lock, queue, data, marker: PhantomData }
}
#[cfg(all(loom, test))]
#[cfg(not(tarpaulin_include))]
pub fn new(value: T) -> Self {
let lock = Lock::unlocked();
let queue = raw::Mutex::new(());
let data = UnsafeCell::new(value);
Self { lock, queue, data, marker: PhantomData }
}
}
impl<T: ?Sized, L: Lock, Ws: Wait, Wq: Wait> Mutex<T, L, Ws, Wq> {
#[cfg(any(test, not(feature = "thread_local")))]
pub fn lock(&self) -> MutexGuard<'_, T, L, Ws, Wq> {
if self.lock.try_lock_acquire_weak() {
return MutexGuard::new(self);
}
let mut node = raw::MutexNode::new();
self.queue.lock_with_then(&mut node, |()| {
while !self.lock.try_lock_acquire_weak() {
self.lock.wait_lock_relaxed::<Ws>();
}
});
MutexGuard::new(self)
}
}
impl<T: ?Sized, L: Lock, Ws, Wq> Mutex<T, L, Ws, Wq> {
pub fn try_lock(&self) -> Option<MutexGuard<'_, T, L, Ws, Wq>> {
self.lock.try_lock_acquire().then(|| MutexGuard::new(self))
}
pub fn is_locked(&self) -> bool {
self.lock.is_locked_relaxed()
}
pub fn unlock(&self) {
self.lock.notify_release();
}
}
impl<T, L, Ws, Wq> Mutex<T, L, Ws, Wq> {
pub fn into_inner(self) -> T {
self.data.into_inner()
}
}
impl<T: ?Sized, L, Ws, Wq> Mutex<T, L, Ws, Wq> {
#[cfg(not(all(loom, test)))]
pub fn get_mut(&mut self) -> &mut T {
unsafe { &mut *self.data.get() }
}
}
impl<T: ?Sized + Debug, L: Lock, Ws, Wq> Debug for Mutex<T, L, Ws, Wq> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let mut d = f.debug_struct("Mutex");
match self.try_lock() {
Some(guard) => guard.with(|data| d.field("data", &data)),
None => d.field("data", &format_args!("<locked>")),
};
d.finish()
}
}
#[must_use = "if unused the Mutex will immediately unlock"]
pub struct MutexGuard<'a, T: ?Sized, L: Lock, Ws, Wq> {
lock: &'a Mutex<T, L, Ws, Wq>,
}
unsafe impl<T: ?Sized + Send, L: Lock, Ws, Wq> Send for MutexGuard<'_, T, L, Ws, Wq> {}
unsafe impl<T: ?Sized + Sync, L: Lock, Ws, Wq> Sync for MutexGuard<'_, T, L, Ws, Wq> {}
impl<'a, T: ?Sized, L: Lock, Ws, Wq> MutexGuard<'a, T, L, Ws, Wq> {
const fn new(lock: &'a Mutex<T, L, Ws, Wq>) -> Self {
Self { lock }
}
fn with<F, Ret>(&self, f: F) -> Ret
where
F: FnOnce(&T) -> Ret,
{
unsafe { self.lock.data.with_unchecked(f) }
}
}
impl<T: ?Sized + Debug, L: Lock, Ws, Wq> Debug for MutexGuard<'_, T, L, Ws, Wq> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
self.with(|data| data.fmt(f))
}
}
impl<T: ?Sized + Display, L: Lock, Ws, Wq> Display for MutexGuard<'_, T, L, Ws, Wq> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
self.with(|data| data.fmt(f))
}
}
#[cfg(not(all(loom, test)))]
impl<T: ?Sized, L: Lock, Ws, Wq> core::ops::Deref for MutexGuard<'_, T, L, Ws, Wq> {
type Target = T;
fn deref(&self) -> &T {
unsafe { &*self.lock.data.get() }
}
}
#[cfg(not(all(loom, test)))]
impl<T: ?Sized, L: Lock, Ws, Wq> core::ops::DerefMut for MutexGuard<'_, T, L, Ws, Wq> {
fn deref_mut(&mut self) -> &mut T {
unsafe { &mut *self.lock.data.get() }
}
}
impl<T: ?Sized, L: Lock, Ws, Wq> Drop for MutexGuard<'_, T, L, Ws, Wq> {
fn drop(&mut self) {
self.lock.unlock();
}
}
#[cfg(all(loom, test))]
#[cfg(not(tarpaulin_include))]
unsafe impl<T: ?Sized, L: Lock, Ws, Wq> crate::loom::Guard for MutexGuard<'_, T, L, Ws, Wq> {
type Target = T;
fn get(&self) -> &loom::cell::UnsafeCell<Self::Target> {
&self.lock.data
}
}