use std::{
cell::UnsafeCell,
fmt,
ops::{Deref, DerefMut},
sync::{
Arc,
atomic::{AtomicUsize, Ordering},
},
};
use super::WaitStart;
use crate::Event;
pub struct Mutex<T>
where
T: ?Sized,
{
state: AtomicUsize,
unlocked: Event,
value: UnsafeCell<T>,
}
unsafe impl<T> Sync for Mutex<T> where T: ?Sized + Send {}
impl<T> Mutex<T> {
pub const fn new(value: T) -> Self {
Self {
state: AtomicUsize::new(0),
unlocked: Event::new(),
value: UnsafeCell::new(value),
}
}
pub fn into_inner(self) -> T {
self.value.into_inner()
}
}
impl<T> Mutex<T>
where
T: ?Sized,
{
pub async fn lock(&self) -> MutexGuard<'_, T> {
self.acquire(|| MutexGuard(self)).await
}
pub fn try_lock(&self) -> Option<MutexGuard<'_, T>> {
self.try_acquire().then(|| MutexGuard(self))
}
pub async fn lock_arc(self: &Arc<Self>) -> MutexGuardArc<T> {
self.acquire(|| MutexGuardArc(self.clone())).await
}
pub fn try_lock_arc(self: &Arc<Self>) -> Option<MutexGuardArc<T>> {
self.try_acquire().then(|| MutexGuardArc(self.clone()))
}
pub fn get_mut(&mut self) -> &mut T {
self.value.get_mut()
}
async fn acquire<F, G>(&self, guard: F) -> G
where
F: FnOnce() -> G,
{
let mut since = None;
let mut starved = None;
let listener = loop {
if self.try_acquire_as(since.is_some()) {
break None;
}
if starved.is_none() && since.is_some_and(WaitStart::waited_long) {
starved = Some(Starved::new(self));
}
let listener = self.unlocked.listen_unfenced();
if self.try_acquire_as(since.is_some()) {
break Some(listener);
}
since.get_or_insert_with(WaitStart::now);
listener.await;
};
let guard = guard();
drop(listener);
drop(starved);
guard
}
fn try_acquire(&self) -> bool {
self.state
.compare_exchange(0, LOCKED, Ordering::Acquire, Ordering::SeqCst)
.is_ok()
}
fn try_acquire_as(&self, waited: bool) -> bool {
if !waited {
return self.try_acquire();
}
self.state.fetch_or(LOCKED, Ordering::SeqCst) & LOCKED == 0
}
fn unlock(&self) {
self.state.fetch_sub(LOCKED, Ordering::SeqCst);
self.unlocked.notify_unfenced(1);
}
}
impl<T> Default for Mutex<T>
where
T: Default,
{
fn default() -> Self {
Self::new(T::default())
}
}
impl<T> From<T> for Mutex<T> {
fn from(value: T) -> Self {
Self::new(value)
}
}
impl<T> fmt::Debug for Mutex<T>
where
T: ?Sized + fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut s = f.debug_struct("Mutex");
match self.try_lock() {
Some(guard) => s.field("value", &&*guard),
None => s.field("value", &format_args!("<locked>")),
};
s.finish()
}
}
#[must_use = "if unused the Mutex will immediately unlock"]
pub struct MutexGuard<'a, T>(&'a Mutex<T>)
where
T: ?Sized;
unsafe impl<T> Sync for MutexGuard<'_, T> where T: ?Sized + Sync {}
impl<T> Deref for MutexGuard<'_, T>
where
T: ?Sized,
{
type Target = T;
fn deref(&self) -> &T {
unsafe { &*self.0.value.get() }
}
}
impl<T> DerefMut for MutexGuard<'_, T>
where
T: ?Sized,
{
fn deref_mut(&mut self) -> &mut T {
unsafe { &mut *self.0.value.get() }
}
}
impl<T> fmt::Debug for MutexGuard<'_, T>
where
T: ?Sized + fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&**self, f)
}
}
impl<T> Drop for MutexGuard<'_, T>
where
T: ?Sized,
{
fn drop(&mut self) {
self.0.unlock();
}
}
#[must_use = "if unused the Mutex will immediately unlock"]
pub struct MutexGuardArc<T>(Arc<Mutex<T>>)
where
T: ?Sized;
unsafe impl<T> Sync for MutexGuardArc<T> where T: ?Sized + Sync {}
impl<T> Deref for MutexGuardArc<T>
where
T: ?Sized,
{
type Target = T;
fn deref(&self) -> &T {
unsafe { &*self.0.value.get() }
}
}
impl<T> DerefMut for MutexGuardArc<T>
where
T: ?Sized,
{
fn deref_mut(&mut self) -> &mut T {
unsafe { &mut *self.0.value.get() }
}
}
impl<T> fmt::Debug for MutexGuardArc<T>
where
T: ?Sized + fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&**self, f)
}
}
impl<T> Drop for MutexGuardArc<T>
where
T: ?Sized,
{
fn drop(&mut self) {
self.0.unlock();
}
}
struct Starved<'a, T>(&'a Mutex<T>)
where
T: ?Sized;
impl<'a, T> Starved<'a, T>
where
T: ?Sized,
{
fn new(mutex: &'a Mutex<T>) -> Self {
mutex.state.fetch_add(STARVED, Ordering::Relaxed);
Self(mutex)
}
}
impl<T> Drop for Starved<'_, T>
where
T: ?Sized,
{
fn drop(&mut self) {
if self.0.state.fetch_sub(STARVED, Ordering::SeqCst) & LOCKED != 0 {
return;
}
self.0.unlocked.notify_unfenced(1);
}
}
const LOCKED: usize = 1;
const STARVED: usize = 2;