Skip to content

Commit ff3f2a8

Browse files
authored
io: add SimplexStream (#6589)
1 parent 5b9a290 commit ff3f2a8

File tree

3 files changed

+72
-17
lines changed

3 files changed

+72
-17
lines changed

tokio/src/io/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -271,8 +271,8 @@ cfg_io_util! {
271271
pub(crate) mod seek;
272272
pub(crate) mod util;
273273
pub use util::{
274-
copy, copy_bidirectional, copy_bidirectional_with_sizes, copy_buf, duplex, empty, repeat, sink, AsyncBufReadExt, AsyncReadExt, AsyncSeekExt, AsyncWriteExt,
275-
BufReader, BufStream, BufWriter, DuplexStream, Empty, Lines, Repeat, Sink, Split, Take,
274+
copy, copy_bidirectional, copy_bidirectional_with_sizes, copy_buf, duplex, empty, repeat, sink, simplex, AsyncBufReadExt, AsyncReadExt, AsyncSeekExt, AsyncWriteExt,
275+
BufReader, BufStream, BufWriter, DuplexStream, Empty, Lines, Repeat, Sink, Split, Take, SimplexStream,
276276
};
277277
}
278278

tokio/src/io/util/mem.rs

+69-14
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! In-process memory IO types.
22
3-
use crate::io::{AsyncRead, AsyncWrite, ReadBuf};
3+
use crate::io::{split, AsyncRead, AsyncWrite, ReadBuf, ReadHalf, WriteHalf};
44
use crate::loom::sync::Mutex;
55

66
use bytes::{Buf, BytesMut};
@@ -47,15 +47,34 @@ use std::{
4747
#[derive(Debug)]
4848
#[cfg_attr(docsrs, doc(cfg(feature = "io-util")))]
4949
pub struct DuplexStream {
50-
read: Arc<Mutex<Pipe>>,
51-
write: Arc<Mutex<Pipe>>,
50+
read: Arc<Mutex<SimplexStream>>,
51+
write: Arc<Mutex<SimplexStream>>,
5252
}
5353

54-
/// A unidirectional IO over a piece of memory.
54+
/// A unidirectional pipe to read and write bytes in memory.
5555
///
56-
/// Data can be written to the pipe, and reading will return that data.
56+
/// It can be constructed by [`simplex`] function which will create a pair of
57+
/// reader and writer or by calling [`SimplexStream::new_unsplit`] that will
58+
/// create a handle for both reading and writing.
59+
///
60+
/// # Example
61+
///
62+
/// ```
63+
/// # async fn ex() -> std::io::Result<()> {
64+
/// # use tokio::io::{AsyncReadExt, AsyncWriteExt};
65+
/// let (mut receiver, mut sender) = tokio::io::simplex(64);
66+
///
67+
/// sender.write_all(b"ping").await?;
68+
///
69+
/// let mut buf = [0u8; 4];
70+
/// receiver.read_exact(&mut buf).await?;
71+
/// assert_eq!(&buf, b"ping");
72+
/// # Ok(())
73+
/// # }
74+
/// ```
5775
#[derive(Debug)]
58-
struct Pipe {
76+
#[cfg_attr(docsrs, doc(cfg(feature = "io-util")))]
77+
pub struct SimplexStream {
5978
/// The buffer storing the bytes written, also read from.
6079
///
6180
/// Using a `BytesMut` because it has efficient `Buf` and `BufMut`
@@ -83,8 +102,8 @@ struct Pipe {
83102
/// written to a side before the write returns `Poll::Pending`.
84103
#[cfg_attr(docsrs, doc(cfg(feature = "io-util")))]
85104
pub fn duplex(max_buf_size: usize) -> (DuplexStream, DuplexStream) {
86-
let one = Arc::new(Mutex::new(Pipe::new(max_buf_size)));
87-
let two = Arc::new(Mutex::new(Pipe::new(max_buf_size)));
105+
let one = Arc::new(Mutex::new(SimplexStream::new_unsplit(max_buf_size)));
106+
let two = Arc::new(Mutex::new(SimplexStream::new_unsplit(max_buf_size)));
88107

89108
(
90109
DuplexStream {
@@ -161,11 +180,47 @@ impl Drop for DuplexStream {
161180
}
162181
}
163182

164-
// ===== impl Pipe =====
183+
// ===== impl SimplexStream =====
184+
185+
/// Creates unidirectional buffer that acts like in memory pipe.
186+
///
187+
/// The `max_buf_size` argument is the maximum amount of bytes that can be
188+
/// written to a buffer before the it returns `Poll::Pending`.
189+
///
190+
/// # Unify reader and writer
191+
///
192+
/// The reader and writer half can be unified into a single structure
193+
/// of `SimplexStream` that supports both reading and writing or
194+
/// the `SimplexStream` can be already created as unified structure
195+
/// using [`SimplexStream::new_unsplit()`].
196+
///
197+
/// ```
198+
/// # async fn ex() -> std::io::Result<()> {
199+
/// # use tokio::io::{AsyncReadExt, AsyncWriteExt};
200+
/// let (writer, reader) = tokio::io::simplex(64);
201+
/// let mut simplex_stream = writer.unsplit(reader);
202+
/// simplex_stream.write_all(b"hello").await?;
203+
///
204+
/// let mut buf = [0u8; 5];
205+
/// simplex_stream.read_exact(&mut buf).await?;
206+
/// assert_eq!(&buf, b"hello");
207+
/// # Ok(())
208+
/// # }
209+
/// ```
210+
#[cfg_attr(docsrs, doc(cfg(feature = "io-util")))]
211+
pub fn simplex(max_buf_size: usize) -> (ReadHalf<SimplexStream>, WriteHalf<SimplexStream>) {
212+
split(SimplexStream::new_unsplit(max_buf_size))
213+
}
165214

166-
impl Pipe {
167-
fn new(max_buf_size: usize) -> Self {
168-
Pipe {
215+
impl SimplexStream {
216+
/// Creates unidirectional buffer that acts like in memory pipe. To create split
217+
/// version with separate reader and writer you can use [`simplex`] function.
218+
///
219+
/// The `max_buf_size` argument is the maximum amount of bytes that can be
220+
/// written to a buffer before the it returns `Poll::Pending`.
221+
#[cfg_attr(docsrs, doc(cfg(feature = "io-util")))]
222+
pub fn new_unsplit(max_buf_size: usize) -> SimplexStream {
223+
SimplexStream {
169224
buffer: BytesMut::new(),
170225
is_closed: false,
171226
max_buf_size,
@@ -269,7 +324,7 @@ impl Pipe {
269324
}
270325
}
271326

272-
impl AsyncRead for Pipe {
327+
impl AsyncRead for SimplexStream {
273328
cfg_coop! {
274329
fn poll_read(
275330
self: Pin<&mut Self>,
@@ -299,7 +354,7 @@ impl AsyncRead for Pipe {
299354
}
300355
}
301356

302-
impl AsyncWrite for Pipe {
357+
impl AsyncWrite for SimplexStream {
303358
cfg_coop! {
304359
fn poll_write(
305360
self: Pin<&mut Self>,

tokio/src/io/util/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ cfg_io_util! {
4242
pub use lines::Lines;
4343

4444
mod mem;
45-
pub use mem::{duplex, DuplexStream};
45+
pub use mem::{duplex, simplex, DuplexStream, SimplexStream};
4646

4747
mod read;
4848
mod read_buf;

0 commit comments

Comments
 (0)