use std::future::poll_fn;
use std::sync::Arc;
use std::task::{Context, Poll};
use tokio::sync::Semaphore;
pub fn channel<T>() -> (Sender<T>, Receiver<T>) {
let (tx, rx) = tokio::sync::mpsc::channel(1);
let semaphore = Arc::new(Semaphore::new(0));
(
Sender {
semaphore: semaphore.clone(),
chan: tx,
},
Receiver {
semaphore,
chan: rx,
needs_permit: false,
},
)
}
pub mod error {
use std::fmt;
use tokio::sync::mpsc::error::SendError as TokioSendError;
#[derive(Debug)]
pub struct SendError<T> {
source: TokioSendError<T>,
}
impl<T> SendError<T> {
pub(crate) fn tokio_send_error(source: TokioSendError<T>) -> Self {
Self { source }
}
}
impl<T> fmt::Display for SendError<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "failed to send value to the receiver")
}
}
impl<T: fmt::Debug + 'static> std::error::Error for SendError<T> {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(&self.source)
}
}
}
#[derive(Debug)]
pub struct Sender<T> {
semaphore: Arc<Semaphore>,
chan: tokio::sync::mpsc::Sender<T>,
}
impl<T> Sender<T> {
pub async fn send(&self, item: T) -> Result<(), error::SendError<T>> {
let result = self.chan.send(item).await;
if result.is_ok() {
self.semaphore
.acquire()
.await
.expect("semaphore is never closed")
.forget();
}
result.map_err(error::SendError::tokio_send_error)
}
}
#[derive(Debug)]
pub struct Receiver<T> {
semaphore: Arc<Semaphore>,
chan: tokio::sync::mpsc::Receiver<T>,
needs_permit: bool,
}
impl<T> Receiver<T> {
pub async fn recv(&mut self) -> Option<T> {
poll_fn(|cx| self.poll_recv(cx)).await
}
pub(crate) fn poll_recv(&mut self, cx: &mut Context<'_>) -> Poll<Option<T>> {
let resp = self.chan.poll_recv(cx);
if self.needs_permit && matches!(resp, Poll::Pending) {
self.needs_permit = false;
self.semaphore.add_permits(1);
}
if matches!(resp, Poll::Ready(_)) {
self.needs_permit = true;
}
resp
}
}
#[cfg(test)]
mod test {
use crate::future::rendezvous::channel;
use std::sync::{Arc, Mutex};
#[tokio::test]
async fn send_blocks_caller() {
let (tx, mut rx) = channel::<u8>();
let done = Arc::new(Mutex::new(0));
let idone = done.clone();
let send = tokio::spawn(async move {
*idone.lock().unwrap() = 1;
tx.send(0).await.unwrap();
*idone.lock().unwrap() = 2;
tx.send(1).await.unwrap();
*idone.lock().unwrap() = 3;
});
assert_eq!(*done.lock().unwrap(), 0);
assert_eq!(rx.recv().await, Some(0));
assert_eq!(*done.lock().unwrap(), 1);
assert_eq!(rx.recv().await, Some(1));
assert_eq!(*done.lock().unwrap(), 2);
assert_eq!(rx.recv().await, None);
assert_eq!(*done.lock().unwrap(), 3);
let _ = send.await;
}
#[tokio::test]
async fn send_errors_when_rx_dropped() {
let (tx, rx) = channel::<u8>();
drop(rx);
tx.send(0).await.expect_err("rx half dropped");
}
}