Skip to main content

nuclei/io/util/
duplex.rs

1//! In-memory pipes: [`duplex`] (a pair of connected streams) and [`simplex`]
2//! (one pipe, a reading end and a writing end).
3//!
4//! Each pipe is a ring of bytes with one writer and one reader: the writer
5//! copies in and publishes its count with a release store, the reader copies
6//! out and publishes its own the same way; each looks at the other's count
7//! with an acquire load. A side that finds the ring empty (or full)
8//! registers its waker and looks once more before it waits, and each side
9//! wakes the other after it moves its count, so no wake is lost. No lock
10//! and no allocation after the ring is made.
11
12use core::pin::Pin;
13use core::task::{Context, Poll};
14use std::io;
15use std::sync::Arc;
16
17use futures_io::{AsyncRead, AsyncWrite};
18
19use crate::sync::atomic_waker::AtomicWaker;
20use crate::sync::internal::{AtomicBool, AtomicUsize, Ordering};
21
22/// One pipe: a ring of `capacity` bytes, the counts of bytes written and
23/// read since it was made, and the two sides' wakers and ends.
24struct Pipe {
25    ring: Box<[core::cell::UnsafeCell<u8>]>,
26    /// Bytes written since the pipe was made (the writer's alone to move).
27    written: AtomicUsize,
28    /// Bytes read since the pipe was made (the reader's alone to move).
29    read: AtomicUsize,
30    reader: AtomicWaker,
31    writer: AtomicWaker,
32    /// The writing end closed: the reader sees the end once the ring is empty.
33    write_closed: AtomicBool,
34    /// The reading end is gone: writes fail.
35    read_closed: AtomicBool,
36}
37
38// SAFETY: the ring's bytes are only touched by the one writer (the free part)
39// and the one reader (the filled part), which the counts keep apart; every
40// other field is atomic.
41unsafe impl Send for Pipe {}
42// SAFETY: as for `Send`.
43unsafe impl Sync for Pipe {}
44
45impl Pipe {
46    fn new(capacity: usize) -> Arc<Self> {
47        let capacity = capacity.max(1);
48        Arc::new(Self {
49            ring: (0..capacity)
50                .map(|_| core::cell::UnsafeCell::new(0))
51                .collect(),
52            written: AtomicUsize::new(0),
53            read: AtomicUsize::new(0),
54            reader: AtomicWaker::new(),
55            writer: AtomicWaker::new(),
56            write_closed: AtomicBool::new(false),
57            read_closed: AtomicBool::new(false),
58        })
59    }
60
61    fn base(&self) -> *mut u8 {
62        core::cell::UnsafeCell::raw_get(self.ring.as_ptr())
63    }
64
65    /// The reader's poll: copies out what is there, the end once the writer
66    /// closed and the ring is empty, or waits.
67    fn poll_read(&self, cx: &mut Context<'_>, out: &mut [u8]) -> Poll<io::Result<usize>> {
68        if out.is_empty() {
69            return Poll::Ready(Ok(0));
70        }
71        let read = self.read.load(Ordering::Relaxed);
72        let mut written = self.written.load(Ordering::Acquire);
73        if written == read {
74            if self.write_closed.load(Ordering::Acquire) {
75                return Poll::Ready(Ok(0));
76            }
77            self.reader.register(cx.waker());
78            written = self.written.load(Ordering::SeqCst);
79            if written == read {
80                // The writer may have closed after the first look.
81                if self.write_closed.load(Ordering::SeqCst) {
82                    return Poll::Ready(Ok(0));
83                }
84                return Poll::Pending;
85            }
86        }
87        let n = (written - read).min(out.len());
88        let cap = self.ring.len();
89        let at = read % cap;
90        let first = n.min(cap - at);
91        // SAFETY: bytes `read..read + n` were written and published by the
92        // writer's release store, which the acquire load above saw; the
93        // writer does not touch them until `read` moves past them.
94        unsafe {
95            core::ptr::copy_nonoverlapping(self.base().add(at), out.as_mut_ptr(), first);
96            core::ptr::copy_nonoverlapping(self.base(), out.as_mut_ptr().add(first), n - first);
97        }
98        self.read.store(read + n, Ordering::Release);
99        self.writer.wake();
100        Poll::Ready(Ok(n))
101    }
102
103    /// The writer's poll: copies in what fits, fails once the reader is
104    /// gone, or waits for room.
105    fn poll_write(&self, cx: &mut Context<'_>, input: &[u8]) -> Poll<io::Result<usize>> {
106        if self.read_closed.load(Ordering::Acquire) || self.write_closed.load(Ordering::Relaxed) {
107            return Poll::Ready(Err(io::ErrorKind::BrokenPipe.into()));
108        }
109        if input.is_empty() {
110            return Poll::Ready(Ok(0));
111        }
112        let cap = self.ring.len();
113        let written = self.written.load(Ordering::Relaxed);
114        let mut read = self.read.load(Ordering::Acquire);
115        if written - read == cap {
116            self.writer.register(cx.waker());
117            read = self.read.load(Ordering::SeqCst);
118            if written - read == cap {
119                if self.read_closed.load(Ordering::SeqCst) {
120                    return Poll::Ready(Err(io::ErrorKind::BrokenPipe.into()));
121                }
122                return Poll::Pending;
123            }
124        }
125        let n = (cap - (written - read)).min(input.len());
126        let at = written % cap;
127        let first = n.min(cap - at);
128        // SAFETY: bytes `written..written + n` are free: the reader moved
129        // `read` past them (seen by the acquire load) and does not touch
130        // them until `written` moves past them.
131        unsafe {
132            core::ptr::copy_nonoverlapping(input.as_ptr(), self.base().add(at), first);
133            core::ptr::copy_nonoverlapping(input.as_ptr().add(first), self.base(), n - first);
134        }
135        self.written.store(written + n, Ordering::Release);
136        self.reader.wake();
137        Poll::Ready(Ok(n))
138    }
139
140    /// The writing end closes: the reader sees the end after what is left.
141    fn close_write(&self) {
142        self.write_closed.store(true, Ordering::SeqCst);
143        self.reader.wake();
144    }
145
146    /// The reading end goes: the writer's next write fails.
147    fn close_read(&self) {
148        self.read_closed.store(true, Ordering::SeqCst);
149        self.writer.wake();
150    }
151}
152
153/// One end of a [`duplex`] pair: what it writes the other reads, and what
154/// the other writes it reads. Dropping it ends the other's reads (after what
155/// is left) and fails the other's writes.
156pub struct DuplexStream {
157    read: Arc<Pipe>,
158    write: Arc<Pipe>,
159}
160
161/// Two connected in-memory streams, each direction a pipe of
162/// `max_buf_size` bytes: a write waits while its pipe is full.
163///
164/// ```
165/// use nuclei::io::{AsyncReadExt, AsyncWriteExt};
166///
167/// # nuclei::block_on(async {
168/// let (mut client, mut server) = nuclei::io::duplex(64);
169/// client.write_all(b"ping").await.unwrap();
170/// let mut buf = [0u8; 4];
171/// server.read_exact(&mut buf).await.unwrap();
172/// assert_eq!(&buf, b"ping");
173/// # });
174/// ```
175pub fn duplex(max_buf_size: usize) -> (DuplexStream, DuplexStream) {
176    let (ab, ba) = (Pipe::new(max_buf_size), Pipe::new(max_buf_size));
177    (
178        DuplexStream {
179            read: Arc::clone(&ba),
180            write: Arc::clone(&ab),
181        },
182        DuplexStream {
183            read: ab,
184            write: ba,
185        },
186    )
187}
188
189impl AsyncRead for DuplexStream {
190    fn poll_read(
191        self: Pin<&mut Self>,
192        cx: &mut Context<'_>,
193        buf: &mut [u8],
194    ) -> Poll<io::Result<usize>> {
195        crate::coop::poll_budget(cx, |cx| self.read.poll_read(cx, buf))
196    }
197}
198
199impl AsyncWrite for DuplexStream {
200    fn poll_write(
201        self: Pin<&mut Self>,
202        cx: &mut Context<'_>,
203        buf: &[u8],
204    ) -> Poll<io::Result<usize>> {
205        crate::coop::poll_budget(cx, |cx| self.write.poll_write(cx, buf))
206    }
207
208    fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<()>> {
209        Poll::Ready(Ok(()))
210    }
211
212    fn poll_close(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<()>> {
213        self.write.close_write();
214        Poll::Ready(Ok(()))
215    }
216}
217
218impl Drop for DuplexStream {
219    fn drop(&mut self) {
220        self.write.close_write();
221        self.read.close_read();
222    }
223}
224
225impl core::fmt::Debug for DuplexStream {
226    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
227        f.debug_struct("DuplexStream").finish_non_exhaustive()
228    }
229}
230
231/// The reading end of a [`simplex`] pipe. Dropping it fails the writer's
232/// next write.
233pub struct SimplexReader {
234    pipe: Arc<Pipe>,
235}
236
237/// The writing end of a [`simplex`] pipe. Dropping it (or closing it) ends
238/// the reader's reads after what is left.
239pub struct SimplexWriter {
240    pipe: Arc<Pipe>,
241}
242
243/// One in-memory pipe of `max_buf_size` bytes: what the writer writes, the
244/// reader reads.
245pub fn simplex(max_buf_size: usize) -> (SimplexReader, SimplexWriter) {
246    let pipe = Pipe::new(max_buf_size);
247    (
248        SimplexReader {
249            pipe: Arc::clone(&pipe),
250        },
251        SimplexWriter { pipe },
252    )
253}
254
255impl AsyncRead for SimplexReader {
256    fn poll_read(
257        self: Pin<&mut Self>,
258        cx: &mut Context<'_>,
259        buf: &mut [u8],
260    ) -> Poll<io::Result<usize>> {
261        crate::coop::poll_budget(cx, |cx| self.pipe.poll_read(cx, buf))
262    }
263}
264
265impl AsyncWrite for SimplexWriter {
266    fn poll_write(
267        self: Pin<&mut Self>,
268        cx: &mut Context<'_>,
269        buf: &[u8],
270    ) -> Poll<io::Result<usize>> {
271        crate::coop::poll_budget(cx, |cx| self.pipe.poll_write(cx, buf))
272    }
273
274    fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<()>> {
275        Poll::Ready(Ok(()))
276    }
277
278    fn poll_close(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<()>> {
279        self.pipe.close_write();
280        Poll::Ready(Ok(()))
281    }
282}
283
284impl Drop for SimplexReader {
285    fn drop(&mut self) {
286        self.pipe.close_read();
287    }
288}
289
290impl Drop for SimplexWriter {
291    fn drop(&mut self) {
292        self.pipe.close_write();
293    }
294}
295
296impl core::fmt::Debug for SimplexReader {
297    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
298        f.debug_struct("SimplexReader").finish_non_exhaustive()
299    }
300}
301
302impl core::fmt::Debug for SimplexWriter {
303    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
304        f.debug_struct("SimplexWriter").finish_non_exhaustive()
305    }
306}