#![allow(unsafe_code)]
use core::cell::{Cell, UnsafeCell};
use core::fmt;
use core::future::Future;
use core::pin::Pin;
use core::task::{Context, Poll, Waker};
use crate::mem::BlockRc as Rc;
pub use crate::sync::oneshot::error;
use crate::sync::oneshot::error::{RecvError, TryRecvError};
const VALUE_SENT: u8 = 0b001;
const RX_CLOSED: u8 = 0b010;
const TX_DROPPED: u8 = 0b100;
struct Inner<T> {
state: Cell<u8>,
value: UnsafeCell<Option<T>>,
rx_waker: Cell<Option<Waker>>,
tx_waker: Cell<Option<Waker>>,
}
impl<T> Inner<T> {
fn has(&self, bit: u8) -> bool {
self.state.get() & bit != 0
}
fn set(&self, bit: u8) {
self.state.set(self.state.get() | bit);
}
fn put(&self, value: T) {
unsafe { *self.value.get() = Some(value) };
}
fn take(&self) -> Option<T> {
unsafe { (*self.value.get()).take() }
}
}
#[inline]
fn register(slot: &Cell<Option<Waker>>, waker: &Waker) -> bool {
if unsafe { &*slot.as_ptr() }
.as_ref()
.is_some_and(|old| old.will_wake(waker))
{
return false;
}
let fresh = waker.clone();
drop(slot.replace(Some(fresh)));
true
}
#[inline]
fn wake(slot: &Cell<Option<Waker>>) {
if let Some(waker) = slot.take() {
waker.wake();
}
}
pub fn channel<T>() -> (Sender<T>, Receiver<T>) {
let inner = Rc::new(Inner {
state: Cell::new(0),
value: UnsafeCell::new(None),
rx_waker: Cell::new(None),
tx_waker: Cell::new(None),
});
(
Sender {
inner: Some(Rc::clone(&inner)),
},
Receiver { inner: Some(inner) },
)
}
pub struct Sender<T> {
inner: Option<Rc<Inner<T>>>,
}
impl<T> Sender<T> {
pub fn send(mut self, value: T) -> Result<(), T> {
let Some(inner) = self.inner.take() else {
return Err(value);
};
if inner.has(RX_CLOSED) {
inner.set(TX_DROPPED);
return Err(value);
}
inner.put(value);
inner.set(VALUE_SENT | TX_DROPPED);
wake(&inner.rx_waker);
Ok(())
}
pub async fn closed(&mut self) {
core::future::poll_fn(|cx| self.poll_closed(cx)).await;
}
pub fn is_closed(&self) -> bool {
self.inner.as_ref().is_none_or(|inner| inner.has(RX_CLOSED))
}
pub fn poll_closed(&mut self, cx: &mut Context<'_>) -> Poll<()> {
let Some(inner) = self.inner.as_ref() else {
return Poll::Ready(());
};
if inner.has(RX_CLOSED) {
return Poll::Ready(());
}
if register(&inner.tx_waker, cx.waker()) && inner.has(RX_CLOSED) {
return Poll::Ready(());
}
Poll::Pending
}
}
impl<T> Drop for Sender<T> {
fn drop(&mut self) {
let Some(inner) = self.inner.as_ref() else {
return;
};
inner.set(TX_DROPPED);
wake(&inner.rx_waker);
drop(inner.tx_waker.take());
}
}
impl<T> fmt::Debug for Sender<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Sender")
.field("is_closed", &self.is_closed())
.finish()
}
}
pub struct Receiver<T> {
inner: Option<Rc<Inner<T>>>,
}
impl<T> Receiver<T> {
pub fn close(&mut self) {
let Some(inner) = self.inner.as_ref() else {
return;
};
inner.set(RX_CLOSED);
wake(&inner.tx_waker);
}
pub fn is_terminated(&self) -> bool {
self.inner.is_none()
}
pub fn is_empty(&self) -> bool {
self.inner
.as_ref()
.is_none_or(|inner| !inner.has(VALUE_SENT))
}
pub fn try_recv(&mut self) -> Result<T, TryRecvError> {
let Some(inner) = self.inner.as_ref() else {
return Err(TryRecvError::Closed);
};
if let Some(value) = inner.take() {
self.inner = None;
return Ok(value);
}
if inner.has(RX_CLOSED | TX_DROPPED) {
self.inner = None;
return Err(TryRecvError::Closed);
}
Err(TryRecvError::Empty)
}
}
impl<T> Drop for Receiver<T> {
fn drop(&mut self) {
let Some(inner) = self.inner.as_ref() else {
return;
};
inner.set(RX_CLOSED);
wake(&inner.tx_waker);
drop(inner.rx_waker.take());
drop(inner.take());
}
}
impl<T> Future for Receiver<T> {
type Output = Result<T, RecvError>;
#[expect(
clippy::panic,
reason = "polled after it completed, which the `Future` contract rules out"
)]
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.get_mut();
let Some(inner) = this.inner.as_ref() else {
panic!("called after complete");
};
const DONE: u8 = VALUE_SENT | RX_CLOSED | TX_DROPPED;
if !inner.has(DONE) && !(register(&inner.rx_waker, cx.waker()) && inner.has(DONE)) {
return Poll::Pending;
}
if crate::coop::exhausted(cx.waker()) {
return Poll::Pending;
}
let done = inner.take().ok_or(RecvError(()));
this.inner = None;
crate::coop::spent_one();
Poll::Ready(done)
}
}
impl<T> fmt::Debug for Receiver<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Receiver")
.field("is_terminated", &self.is_terminated())
.field("is_empty", &self.is_empty())
.finish()
}
}
#[cfg(all(test, not(loom)))]
mod tests {
use core::cell::RefCell;
use core::task::{Context, Poll};
use std::rc::Rc;
use super::*;
use crate::sync::test_support::hooked;
#[test]
fn a_receivers_clone_that_sends_completes_on_that_poll() {
let (tx, mut rx) = channel::<u32>();
let waker = hooked(
Some(Box::new(move || {
tx.send(5).unwrap();
})),
None,
);
let polled = Pin::new(&mut rx).poll(&mut Context::from_waker(&waker));
assert_eq!(polled, Poll::Ready(Ok(5)));
}
#[test]
fn a_receivers_clone_that_drops_the_sender_completes_on_that_poll() {
let (tx, mut rx) = channel::<u32>();
let waker = hooked(Some(Box::new(move || drop(tx))), None);
let polled = Pin::new(&mut rx).poll(&mut Context::from_waker(&waker));
assert_eq!(polled, Poll::Ready(Err(RecvError(()))));
}
#[test]
fn a_closed_wait_whose_clone_closes_the_receiver_completes() {
let (mut tx, rx) = channel::<u32>();
let rx = Rc::new(RefCell::new(rx));
let closer = Rc::clone(&rx);
let waker = hooked(Some(Box::new(move || closer.borrow_mut().close())), None);
let polled = tx.poll_closed(&mut Context::from_waker(&waker));
assert_eq!(polled, Poll::Ready(()));
}
}