use futures_io::{AsyncRead, AsyncWrite};
use std::{
fmt, io,
pin::Pin,
sync::{Arc, Mutex},
task::{Context, Poll},
};
pub fn split<S: AsyncRead + AsyncWrite + Unpin>(stream: S) -> (ReadHalf<S>, WriteHalf<S>) {
let shared = Arc::new(Mutex::new(stream));
(
ReadHalf {
shared: shared.clone(),
},
WriteHalf { shared },
)
}
pub struct ReadHalf<S> {
shared: Arc<Mutex<S>>,
}
pub struct WriteHalf<S> {
shared: Arc<Mutex<S>>,
}
impl<S> fmt::Debug for ReadHalf<S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ReadHalf").finish()
}
}
impl<S> fmt::Debug for WriteHalf<S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("WriteHalf").finish()
}
}
impl<S: AsyncRead + AsyncWrite + Unpin> ReadHalf<S> {
#[must_use]
pub fn unsplit(self, write: WriteHalf<S>) -> Option<S> {
if Arc::ptr_eq(&self.shared, &write.shared) {
drop(write);
Arc::try_unwrap(self.shared)
.ok()
.map(|m| m.into_inner().unwrap_or_else(|e| e.into_inner()))
} else {
None
}
}
}
fn lock<S>(shared: &Arc<Mutex<S>>) -> std::sync::MutexGuard<'_, S> {
shared.lock().unwrap_or_else(|e| e.into_inner())
}
impl<S: AsyncRead + Unpin> AsyncRead for ReadHalf<S> {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<io::Result<usize>> {
Pin::new(&mut *lock(&self.shared)).poll_read(cx, buf)
}
fn poll_read_vectored(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
bufs: &mut [io::IoSliceMut<'_>],
) -> Poll<io::Result<usize>> {
Pin::new(&mut *lock(&self.shared)).poll_read_vectored(cx, bufs)
}
}
impl<S: AsyncWrite + Unpin> AsyncWrite for WriteHalf<S> {
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
Pin::new(&mut *lock(&self.shared)).poll_write(cx, buf)
}
fn poll_write_vectored(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
bufs: &[io::IoSlice<'_>],
) -> Poll<io::Result<usize>> {
Pin::new(&mut *lock(&self.shared)).poll_write_vectored(cx, bufs)
}
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Pin::new(&mut *lock(&self.shared)).poll_flush(cx)
}
fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Pin::new(&mut *lock(&self.shared)).poll_close(cx)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::util::simple_block_on;
use std::{
collections::VecDeque,
future::poll_fn,
pin::Pin,
task::{Context, Poll},
};
struct Duplex {
incoming: VecDeque<u8>,
outgoing: Vec<u8>,
}
impl AsyncRead for Duplex {
fn poll_read(
mut self: Pin<&mut Self>,
_cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<io::Result<usize>> {
let n = self.incoming.len().min(buf.len());
for slot in buf.iter_mut().take(n) {
*slot = self.incoming.pop_front().unwrap();
}
Poll::Ready(Ok(n))
}
}
impl AsyncWrite for Duplex {
fn poll_write(
mut self: Pin<&mut Self>,
_cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
self.outgoing.extend_from_slice(buf);
Poll::Ready(Ok(buf.len()))
}
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(()))
}
}
#[test]
fn split_read_and_write_independently() {
let duplex = Duplex {
incoming: b"hello".iter().copied().collect(),
outgoing: Vec::new(),
};
let (mut r, mut w) = split(duplex);
simple_block_on(async {
let n = poll_fn(|cx| Pin::new(&mut w).poll_write(cx, b"world"))
.await
.unwrap();
assert_eq!(n, 5);
let mut buf = [0u8; 5];
let n = poll_fn(|cx| Pin::new(&mut r).poll_read(cx, &mut buf))
.await
.unwrap();
assert_eq!(n, 5);
assert_eq!(&buf, b"hello");
});
let stream = r.unsplit(w).expect("same split");
assert_eq!(stream.outgoing, b"world");
}
#[test]
fn unsplit_rejects_foreign_half() {
let (r1, _w1) = split(Duplex {
incoming: VecDeque::new(),
outgoing: Vec::new(),
});
let (_r2, w2) = split(Duplex {
incoming: VecDeque::new(),
outgoing: Vec::new(),
});
assert!(r1.unsplit(w2).is_none());
}
}