use std::cell::{Cell, RefCell, UnsafeCell};
use std::collections::VecDeque;
use std::future::Future;
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
use std::pin::Pin;
use std::rc::Rc;
use std::task::{Context, Poll, Waker};
struct Waiter {
id: usize,
selected: Rc<Cell<bool>>,
waker: Waker,
}
pub struct Mutex<T: ?Sized> {
locked: Cell<bool>,
next_waiter_id: Cell<usize>,
waiters: RefCell<VecDeque<Waiter>>,
_not_send_sync: PhantomData<Rc<()>>,
value: UnsafeCell<T>,
}
pub struct MutexGuard<'a, T: ?Sized> {
mutex: &'a Mutex<T>,
_not_send_sync: PhantomData<Rc<()>>,
}
impl<T> Mutex<T> {
pub fn new(value: T) -> Self {
Self {
locked: Cell::new(false),
next_waiter_id: Cell::new(0),
waiters: RefCell::new(VecDeque::new()),
_not_send_sync: PhantomData,
value: UnsafeCell::new(value),
}
}
}
impl<T: ?Sized> Mutex<T> {
pub async fn lock(&self) -> MutexGuard<'_, T> {
LockFuture::new(self).await
}
pub fn try_lock(&self) -> Option<MutexGuard<'_, T>> {
if self.locked.get() || !self.waiters.borrow().is_empty() {
return None;
}
self.locked.set(true);
Some(MutexGuard {
mutex: self,
_not_send_sync: PhantomData,
})
}
fn allocate_waiter_id(&self) -> usize {
let id = self.next_waiter_id.get();
self.next_waiter_id.set(id.wrapping_add(1));
id
}
fn remove_waiter(&self, id: usize) {
let mut waiters = self.waiters.borrow_mut();
if let Some(index) = waiters.iter().position(|waiter| waiter.id == id) {
waiters.remove(index);
}
}
fn release_to_next_waiter(&self) {
if let Some(waiter) = self.waiters.borrow_mut().pop_front() {
waiter.selected.set(true);
self.locked.set(true);
waiter.waker.wake();
} else {
self.locked.set(false);
}
}
}
impl<T: ?Sized> Deref for MutexGuard<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { &*self.mutex.value.get() }
}
}
impl<T: ?Sized> DerefMut for MutexGuard<'_, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *self.mutex.value.get() }
}
}
impl<T: ?Sized> Drop for MutexGuard<'_, T> {
fn drop(&mut self) {
self.mutex.release_to_next_waiter();
}
}
struct LockFuture<'a, T: ?Sized> {
mutex: &'a Mutex<T>,
waiter: Option<(usize, Rc<Cell<bool>>)>,
acquired: bool,
}
impl<'a, T: ?Sized> LockFuture<'a, T> {
fn new(mutex: &'a Mutex<T>) -> Self {
Self {
mutex,
waiter: None,
acquired: false,
}
}
}
impl<'a, T: ?Sized> Future for LockFuture<'a, T> {
type Output = MutexGuard<'a, T>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
if self
.waiter
.as_ref()
.is_some_and(|(_, selected)| selected.get())
{
self.acquired = true;
return Poll::Ready(MutexGuard {
mutex: self.mutex,
_not_send_sync: PhantomData,
});
}
if self.waiter.is_none()
&& !self.mutex.locked.get()
&& self.mutex.waiters.borrow().is_empty()
{
self.mutex.locked.set(true);
self.acquired = true;
return Poll::Ready(MutexGuard {
mutex: self.mutex,
_not_send_sync: PhantomData,
});
}
if let Some((id, _)) = &self.waiter {
if let Some(waiter) = self
.mutex
.waiters
.borrow_mut()
.iter_mut()
.find(|waiter| waiter.id == *id)
{
waiter.waker = cx.waker().clone();
}
} else {
let id = self.mutex.allocate_waiter_id();
let selected = Rc::new(Cell::new(false));
self.mutex.waiters.borrow_mut().push_back(Waiter {
id,
selected: Rc::clone(&selected),
waker: cx.waker().clone(),
});
self.waiter = Some((id, selected));
}
Poll::Pending
}
}
impl<T: ?Sized> Drop for LockFuture<'_, T> {
fn drop(&mut self) {
let Some((id, selected)) = &self.waiter else {
return;
};
if self.acquired {
return;
}
if selected.get() {
self.mutex.release_to_next_waiter();
} else {
self.mutex.remove_waiter(*id);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::cell::RefCell;
use crate::{run, spawn, yield_now};
#[test]
fn fast_path_acquires_and_releases() {
let mutex = Rc::new(Mutex::new(1));
let observed = Rc::new(Cell::new(0));
spawn({
let mutex = Rc::clone(&mutex);
let observed = Rc::clone(&observed);
async move {
let mut guard = mutex.lock().await;
*guard += 1;
observed.set(*guard);
}
});
run();
assert_eq!(observed.get(), 2);
assert_eq!(*mutex.try_lock().unwrap(), 2);
}
#[test]
fn contention_is_fifo_and_exclusive() {
let mutex = Rc::new(Mutex::new(()));
let order = Rc::new(RefCell::new(Vec::new()));
spawn({
let mutex = Rc::clone(&mutex);
let order = Rc::clone(&order);
async move {
let _guard = mutex.lock().await;
order.borrow_mut().push(1);
yield_now().await;
order.borrow_mut().push(10);
}
});
spawn({
let mutex = Rc::clone(&mutex);
let order = Rc::clone(&order);
async move {
let _guard = mutex.lock().await;
order.borrow_mut().push(2);
}
});
run();
assert_eq!(&*order.borrow(), &[1, 10, 2]);
}
}