use std::{
cell::UnsafeCell,
fmt,
ops::{Deref, DerefMut},
};
use crate::fiber::{Latch, LatchGuard};
#[cfg(debug_assertions)]
use crate::unwrap_or;
#[cfg(debug_assertions)]
use std::{cell::Cell, panic::Location};
pub struct Mutex<T: ?Sized> {
latch: Latch,
#[cfg(debug_assertions)]
lock_location: Cell<Option<&'static Location<'static>>>,
data: UnsafeCell<T>,
}
impl<T: ?Sized> Mutex<T> {
pub fn new(t: T) -> Mutex<T>
where
T: Sized,
{
Mutex {
latch: Latch::new(),
#[cfg(debug_assertions)]
lock_location: Cell::default(),
data: UnsafeCell::new(t),
}
}
#[track_caller]
pub fn lock(&self) -> MutexGuard<'_, T> {
#[cfg(debug_assertions)]
let guard = unwrap_or!(self.latch.try_lock(), {
self.log_lock_location();
self.latch.lock()
});
#[cfg(not(debug_assertions))]
let guard = self.latch.lock();
unsafe { MutexGuard::new(self, guard) }
}
#[track_caller]
pub fn try_lock(&self) -> Option<MutexGuard<'_, T>> {
match self.latch.try_lock() {
Some(guard) => unsafe { Some(MutexGuard::new(self, guard)) },
None => {
#[cfg(debug_assertions)]
self.log_lock_location();
None
}
}
}
#[track_caller]
pub fn lock_timeout(&self, timeout: core::time::Duration) -> Option<MutexGuard<'_, T>> {
match self.latch.lock_timeout(timeout) {
Some(guard) => unsafe { Some(MutexGuard::new(self, guard)) },
None => {
#[cfg(debug_assertions)]
self.log_lock_location();
None
}
}
}
pub fn unlock(guard: MutexGuard<'_, T>) {
drop(guard);
}
pub fn into_inner(self) -> T
where
T: Sized,
{
self.data.into_inner()
}
pub fn get_mut(&mut self) -> &mut T {
self.data.get_mut()
}
#[cfg(debug_assertions)]
#[inline]
#[track_caller]
fn log_lock_location(&self) {
use std::borrow::Cow;
let caller = Location::caller();
let msg: Cow<str> = if let Some(loc) = self.lock_location.get() {
format!("can't lock mutex at {caller}, already locked at {loc}").into()
} else {
format!("can't lock mutex at {caller}, already locked at unknown location").into()
};
crate::say_verbose!("{msg}");
}
}
impl<T> From<T> for Mutex<T> {
fn from(t: T) -> Self {
Mutex::new(t)
}
}
impl<T: Default> Default for Mutex<T> {
fn default() -> Mutex<T> {
Mutex::new(Default::default())
}
}
impl<T: ?Sized + fmt::Debug> fmt::Debug for Mutex<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut d = f.debug_struct("Mutex");
match self.try_lock() {
Some(guard) => {
d.field("data", &&*guard);
}
#[cfg(debug_assertions)]
None => {
struct LockedPlaceholder(Option<&'static Location<'static>>);
impl fmt::Debug for LockedPlaceholder {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(loc) = self.0 {
write!(f, "<locked at {}:{}>", loc.file(), loc.line())
} else {
f.write_str("<locked>")
}
}
}
d.field("data", &LockedPlaceholder(self.lock_location.get()));
}
#[cfg(not(debug_assertions))]
None => {
struct LockedPlaceholder;
impl fmt::Debug for LockedPlaceholder {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("<locked>")
}
}
d.field("data", &LockedPlaceholder);
}
}
d.finish_non_exhaustive()
}
}
pub struct MutexGuard<'a, T: ?Sized + 'a> {
lock: &'a Mutex<T>,
_latch_guard: LatchGuard,
}
impl<'mutex, T: ?Sized> MutexGuard<'mutex, T> {
#[track_caller]
unsafe fn new(lock: &'mutex Mutex<T>, _latch_guard: LatchGuard) -> Self {
#[cfg(debug_assertions)]
lock.lock_location.set(Some(Location::caller()));
Self { lock, _latch_guard }
}
}
impl<'a, T: ?Sized + 'a> Drop for MutexGuard<'a, T> {
fn drop(&mut self) {
#[cfg(debug_assertions)]
self.lock.lock_location.set(None);
}
}
impl<T: ?Sized> Deref for MutexGuard<'_, T> {
type Target = T;
fn deref(&self) -> &T {
unsafe { &*self.lock.data.get() }
}
}
impl<T: ?Sized> DerefMut for MutexGuard<'_, T> {
fn deref_mut(&mut self) -> &mut T {
unsafe { &mut *self.lock.data.get() }
}
}
impl<T: ?Sized + fmt::Debug> fmt::Debug for MutexGuard<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&**self, f)
}
}
impl<T: ?Sized + fmt::Display> fmt::Display for MutexGuard<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&**self, f)
}
}