use futures_util::task::{self, ArcWake};
use std::io::{self, Read, Write};
use std::pin::Pin;
use std::sync::Arc;
use std::task::{Context, Poll};
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
#[derive(Debug, Default)]
struct WakerProxy {
read_waker: task::AtomicWaker,
write_waker: task::AtomicWaker,
}
impl ArcWake for WakerProxy {
fn wake_by_ref(arc_self: &Arc<Self>) {
arc_self.read_waker.wake();
arc_self.write_waker.wake();
}
}
pub(super) enum Direction {
Read,
Write,
}
pub(super) struct AllowStd<S> {
inner: S,
read_waker_proxy: Arc<WakerProxy>,
write_waker_proxy: Arc<WakerProxy>,
}
impl<S> AllowStd<S> {
pub(super) fn new(inner: S) -> Self {
Self {
inner,
read_waker_proxy: Arc::new(WakerProxy {
read_waker: task::AtomicWaker::new(),
write_waker: task::AtomicWaker::new(),
}),
write_waker_proxy: Arc::new(WakerProxy {
read_waker: task::AtomicWaker::new(),
write_waker: task::AtomicWaker::new(),
}),
}
}
pub(super) fn register(&self, direction: &Direction, cx: &Context<'_>) {
match direction {
Direction::Read => {
self.write_waker_proxy.read_waker.register(cx.waker());
self.read_waker_proxy.read_waker.register(cx.waker());
}
Direction::Write => {
self.write_waker_proxy.write_waker.register(cx.waker());
self.read_waker_proxy.write_waker.register(cx.waker());
}
}
}
}
impl<S> AllowStd<S>
where
S: Unpin,
{
fn with_context<F, R>(&mut self, direction: &Direction, f: F) -> Poll<io::Result<R>>
where
F: FnOnce(&mut Context<'_>, Pin<&mut S>) -> Poll<io::Result<R>>,
{
let waker = match direction {
Direction::Read => task::waker_ref(&self.read_waker_proxy),
Direction::Write => task::waker_ref(&self.write_waker_proxy),
};
let mut cx = Context::from_waker(&waker);
f(&mut cx, Pin::new(&mut self.inner))
}
}
impl<S> Read for AllowStd<S>
where
S: AsyncRead + Unpin,
{
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let mut read_buf = ReadBuf::new(buf);
match self.with_context(&Direction::Read, |cx, stream| stream.poll_read(cx, &mut read_buf))
{
Poll::Ready(Ok(())) => Ok(read_buf.filled().len()),
Poll::Ready(Err(err)) => Err(err),
Poll::Pending => Err(io::ErrorKind::WouldBlock.into()),
}
}
}
impl<S> Write for AllowStd<S>
where
S: AsyncWrite + Unpin,
{
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
match self.with_context(&Direction::Write, |cx, stream| stream.poll_write(cx, buf)) {
Poll::Ready(result) => result,
Poll::Pending => Err(io::ErrorKind::WouldBlock.into()),
}
}
fn flush(&mut self) -> io::Result<()> {
match self.with_context(&Direction::Write, |cx, stream| stream.poll_flush(cx)) {
Poll::Ready(result) => result,
Poll::Pending => Err(io::ErrorKind::WouldBlock.into()),
}
}
}