use std::fmt;
use std::mem::ManuallyDrop;
use std::pin::Pin;
use std::ptr::NonNull;
use std::sync::atomic::Ordering;
use std::sync::atomic::fence;
use std::task::Context;
use std::task::Poll;
use crate::oneshot::AWAKING;
use crate::oneshot::Channel;
use crate::oneshot::DISCONNECTED;
use crate::oneshot::EMPTY;
use crate::oneshot::MESSAGE;
use crate::oneshot::RECEIVING;
#[cfg(doc)]
use crate::oneshot::Sender;
use crate::oneshot::deallocate_empty_channel;
use crate::oneshot::drop_message_and_deallocate_channel;
pub struct Receiver<T> {
channel_ptr: NonNull<Channel<T>>,
}
impl<T> fmt::Debug for Receiver<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Receiver").finish_non_exhaustive()
}
}
unsafe impl<T: Send> Send for Receiver<T> {}
impl<T> Unpin for Receiver<T> {}
impl<T> IntoFuture for Receiver<T> {
type Output = Result<T, RecvError>;
type IntoFuture = Recv<T>;
fn into_future(self) -> Self::IntoFuture {
let receiver = ManuallyDrop::new(self);
let channel_ptr = receiver.channel_ptr;
Recv { channel_ptr }
}
}
impl<T> Receiver<T> {
pub fn is_closed(&self) -> bool {
let channel = unsafe { self.channel_ptr.as_ref() };
matches!(channel.state.load(Ordering::Relaxed), DISCONNECTED)
}
pub fn has_message(&self) -> bool {
let channel = unsafe { self.channel_ptr.as_ref() };
matches!(channel.state.load(Ordering::Relaxed), MESSAGE)
}
pub fn try_recv(&self) -> Result<T, TryRecvError> {
let channel = unsafe { self.channel_ptr.as_ref() };
match channel.state.load(Ordering::Relaxed) {
MESSAGE => {
channel.state.store(DISCONNECTED, Ordering::Relaxed);
fence(Ordering::Acquire);
Ok(unsafe { channel.take_message() })
}
EMPTY => Err(TryRecvError::Empty),
DISCONNECTED => Err(TryRecvError::Disconnected),
state => unreachable!("unexpected channel state: {}", state),
}
}
pub(super) fn new(channel_ptr: NonNull<Channel<T>>) -> Self {
Self { channel_ptr }
}
}
impl<T> Drop for Receiver<T> {
fn drop(&mut self) {
let channel = unsafe { self.channel_ptr.as_ref() };
match channel.state.swap(DISCONNECTED, Ordering::AcqRel) {
EMPTY => {}
MESSAGE => {
unsafe { drop_message_and_deallocate_channel(self.channel_ptr) };
}
DISCONNECTED => {
unsafe { deallocate_empty_channel(self.channel_ptr) };
}
state => unreachable!("unexpected channel state: {}", state),
}
}
}
pub struct Recv<T> {
channel_ptr: NonNull<Channel<T>>,
}
unsafe impl<T: Send> Send for Recv<T> {}
impl<T> fmt::Debug for Recv<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Recv").finish_non_exhaustive()
}
}
impl<T> Future for Recv<T> {
type Output = Result<T, RecvError>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let channel = unsafe { self.channel_ptr.as_ref() };
match channel.state.load(Ordering::Relaxed) {
EMPTY => {
let waker = cx.waker().clone();
unsafe { channel.register_waker(waker) }
}
MESSAGE => {
channel.state.store(DISCONNECTED, Ordering::Relaxed);
fence(Ordering::Acquire);
Poll::Ready(Ok(unsafe { channel.take_message() }))
}
RECEIVING => {
match channel.state.compare_exchange(
RECEIVING,
EMPTY,
Ordering::Acquire,
Ordering::Relaxed,
) {
Ok(_) => {
let waker = cx.waker().clone();
unsafe { channel.drop_waker() };
unsafe { channel.register_waker(waker) }
}
Err(MESSAGE) => {
channel.state.store(DISCONNECTED, Ordering::Relaxed);
fence(Ordering::Acquire);
Poll::Ready(Ok(unsafe { channel.take_message() }))
}
Err(AWAKING) => {
cx.waker().wake_by_ref();
Poll::Pending
}
Err(DISCONNECTED) => Poll::Ready(Err(RecvError::Disconnected)),
Err(state) => unreachable!("unexpected channel state: {}", state),
}
}
AWAKING => {
cx.waker().wake_by_ref();
Poll::Pending
}
DISCONNECTED => Poll::Ready(Err(RecvError::Disconnected)),
state => unreachable!("unexpected channel state: {}", state),
}
}
}
impl<T> Drop for Recv<T> {
fn drop(&mut self) {
let channel = unsafe { self.channel_ptr.as_ref() };
loop {
match channel.state.load(Ordering::Acquire) {
EMPTY => {
if channel
.state
.compare_exchange(EMPTY, DISCONNECTED, Ordering::Release, Ordering::Relaxed)
.is_ok()
{
break;
}
}
MESSAGE => {
unsafe { drop_message_and_deallocate_channel(self.channel_ptr) };
break;
}
RECEIVING => {
if channel
.state
.compare_exchange(RECEIVING, EMPTY, Ordering::Relaxed, Ordering::Relaxed)
.is_ok()
{
unsafe { channel.drop_waker() };
}
}
AWAKING => {
if channel
.state
.compare_exchange(
AWAKING,
DISCONNECTED,
Ordering::Release,
Ordering::Relaxed,
)
.is_ok()
{
break;
}
}
DISCONNECTED => {
unsafe { deallocate_empty_channel(self.channel_ptr) };
break;
}
state => unreachable!("unexpected channel state: {}", state),
}
}
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum TryRecvError {
Empty,
Disconnected,
}
impl fmt::Display for TryRecvError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
TryRecvError::Empty => "receiving on an empty channel",
TryRecvError::Disconnected => "receiving on a closed channel",
})
}
}
impl std::error::Error for TryRecvError {}
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum RecvError {
Disconnected,
}
impl fmt::Display for RecvError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("receiving on a closed channel")
}
}
impl std::error::Error for RecvError {}