use crate::lockfree::{AtomicWakerSlot, SpscQueue};
use std::io;
#[cfg(feature = "compat")]
use std::pin::Pin;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::task::{Context, Poll};
#[repr(align(64))]
struct HalfPipe {
queue: SpscQueue<u8>,
read_waker: AtomicWakerSlot,
write_waker: AtomicWakerSlot,
writer_dropped: AtomicBool,
reader_dropped: AtomicBool,
}
impl HalfPipe {
fn new(capacity: usize) -> Self {
Self {
queue: SpscQueue::new(capacity),
read_waker: AtomicWakerSlot::new(),
write_waker: AtomicWakerSlot::new(),
writer_dropped: AtomicBool::new(false),
reader_dropped: AtomicBool::new(false),
}
}
}
pub struct DtactStream {
tx: Arc<HalfPipe>,
rx: Arc<HalfPipe>,
}
unsafe impl Send for DtactStream {}
unsafe impl Sync for DtactStream {}
#[must_use]
pub fn pair(capacity: usize) -> (DtactStream, DtactStream) {
let capacity = capacity.next_power_of_two().max(1);
let a = Arc::new(HalfPipe::new(capacity));
let b = Arc::new(HalfPipe::new(capacity));
(
DtactStream {
tx: Arc::clone(&a),
rx: Arc::clone(&b),
},
DtactStream { tx: b, rx: a },
)
}
impl DtactStream {
#[inline]
pub fn poll_read(&self, cx: &Context<'_>, buf: &mut [u8]) -> Poll<io::Result<usize>> {
if buf.is_empty() {
return Poll::Ready(Ok(0));
}
let n = self.rx.queue.pop_slice(buf);
if n > 0 {
self.rx.write_waker.take_and_wake();
return Poll::Ready(Ok(n));
}
if self.rx.writer_dropped.load(Ordering::Acquire) && self.rx.queue.is_empty() {
return Poll::Ready(Ok(0)); }
self.rx.read_waker.register(cx.waker());
let n = self.rx.queue.pop_slice(buf);
if n > 0 {
self.rx.write_waker.take_and_wake();
return Poll::Ready(Ok(n));
}
if self.rx.writer_dropped.load(Ordering::Acquire) && self.rx.queue.is_empty() {
return Poll::Ready(Ok(0));
}
Poll::Pending
}
#[inline]
pub fn poll_write(&self, cx: &Context<'_>, buf: &[u8]) -> Poll<io::Result<usize>> {
if buf.is_empty() {
return Poll::Ready(Ok(0));
}
if self.tx.reader_dropped.load(Ordering::Acquire) {
return Poll::Ready(Err(io::Error::new(
io::ErrorKind::BrokenPipe,
"dtact-stream: peer dropped its read half",
)));
}
let n = self.tx.queue.push_slice(buf);
if n > 0 {
self.tx.read_waker.take_and_wake();
return Poll::Ready(Ok(n));
}
self.tx.write_waker.register(cx.waker());
if self.tx.reader_dropped.load(Ordering::Acquire) {
return Poll::Ready(Err(io::Error::new(
io::ErrorKind::BrokenPipe,
"dtact-stream: peer dropped its read half",
)));
}
let n = self.tx.queue.push_slice(buf);
if n > 0 {
self.tx.read_waker.take_and_wake();
return Poll::Ready(Ok(n));
}
Poll::Pending
}
pub async fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
std::future::poll_fn(|cx| self.poll_read(cx, buf)).await
}
pub async fn write(&self, buf: &[u8]) -> io::Result<usize> {
std::future::poll_fn(|cx| self.poll_write(cx, buf)).await
}
pub async fn write_all(&self, mut buf: &[u8]) -> io::Result<()> {
while !buf.is_empty() {
let n = self.write(buf).await?;
buf = &buf[n..];
}
Ok(())
}
}
impl Drop for DtactStream {
fn drop(&mut self) {
self.tx.writer_dropped.store(true, Ordering::Release);
self.tx.read_waker.take_and_wake();
self.rx.reader_dropped.store(true, Ordering::Release);
self.rx.write_waker.take_and_wake();
}
}
#[cfg(feature = "compat")]
impl futures_io::AsyncRead for DtactStream {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<io::Result<usize>> {
Self::poll_read(&self, cx, buf)
}
}
#[cfg(feature = "compat")]
impl futures_io::AsyncWrite for DtactStream {
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
Self::poll_write(&self, cx, buf)
}
fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Poll::Ready(Ok(()))
}
fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Poll::Ready(Ok(()))
}
}
#[cfg(feature = "compat")]
impl tokio::io::AsyncRead for DtactStream {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut tokio::io::ReadBuf<'_>,
) -> Poll<io::Result<()>> {
let unfilled = buf.initialize_unfilled();
match Self::poll_read(&self, cx, unfilled) {
Poll::Ready(Ok(n)) => {
buf.advance(n);
Poll::Ready(Ok(()))
}
Poll::Ready(Err(e)) => Poll::Ready(Err(e)),
Poll::Pending => Poll::Pending,
}
}
}
#[cfg(feature = "compat")]
impl tokio::io::AsyncWrite for DtactStream {
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
Self::poll_write(&self, cx, buf)
}
fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Poll::Ready(Ok(()))
}
fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Poll::Ready(Ok(()))
}
}