use std::cell::UnsafeCell;
use std::ops::{Deref, DerefMut};
use std::sync::Arc;
use std::task::{Context, Poll};
use tokio::sync::{OwnedSemaphorePermit, Semaphore};
use tokio_util::sync::PollSemaphore;
pub struct PollMutex<T> {
mutex: PollSemaphore,
data: Arc<UnsafeCell<T>>,
}
pub struct PollMutexGuard<T> {
#[allow(unused)]
permit: OwnedSemaphorePermit,
data: Arc<UnsafeCell<T>>,
}
impl<T> PollMutex<T> {
pub fn new(data: T) -> Self {
Self {
mutex: PollSemaphore::new(Arc::new(Semaphore::new(1))),
data: Arc::new(UnsafeCell::new(data)),
}
}
pub fn lock(&mut self) -> impl Future<Output = PollMutexGuard<T>> {
std::future::poll_fn(move |cx| self.poll_lock(cx))
}
pub fn try_lock(&self) -> Option<PollMutexGuard<T>> {
if let Ok(permit) = self.mutex.clone_inner().try_acquire_owned() {
Some(PollMutexGuard {
permit,
data: self.data.clone(),
})
} else {
None
}
}
pub fn poll_lock(&mut self, cx: &mut Context) -> Poll<PollMutexGuard<T>> {
match self.mutex.poll_acquire(cx) {
Poll::Ready(Some(permit)) => Poll::Ready(PollMutexGuard {
permit,
data: self.data.clone(),
}),
Poll::Ready(None) => unreachable!(),
Poll::Pending => Poll::Pending,
}
}
}
unsafe impl<T: Send> Send for PollMutex<T> {}
unsafe impl<T: Send> Sync for PollMutex<T> {}
unsafe impl<T: Send + Sync> Sync for PollMutexGuard<T> {}
impl<T> From<T> for PollMutex<T> {
fn from(data: T) -> Self {
Self::new(data)
}
}
impl<T> Clone for PollMutex<T> {
fn clone(&self) -> Self {
Self {
mutex: self.mutex.clone(),
data: self.data.clone(),
}
}
}
impl<T: Default> Default for PollMutex<T> {
fn default() -> Self {
Self::new(T::default())
}
}
impl<T> Deref for PollMutexGuard<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { &*self.data.get() }
}
}
impl<T> DerefMut for PollMutexGuard<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *self.data.get() }
}
}
#[cfg(test)]
mod tests {
use crate::sync::PollMutex;
async fn push_one(mut mutex: PollMutex<Vec<usize>>) {
mutex.lock().await.push(1);
}
#[tokio::test(flavor = "multi_thread", worker_threads = 1000)]
async fn poll_mutex_parallel_lock() {
let n = 1000;
let mut mutex = PollMutex::new(vec![]);
let mut handles = vec![];
for _ in 0..n {
handles.push(tokio::spawn(push_one(mutex.clone())));
}
assert_eq!(handles.len(), n);
for handle in handles {
let _ = handle.await;
}
let inner = mutex.lock().await;
assert_eq!(inner.len(), n);
let sum: usize = inner.iter().sum();
assert_eq!(sum, n);
}
}