Skip to main content

nuclei/io/util/
stdio.rs

1//! The process's standard streams as async streams: [`stdin`], [`stdout`] and
2//! [`stderr`].
3//!
4//! A standard stream may be a terminal, a pipe or a file the shell redirected,
5//! and no one poller waits on all three; so their reads and writes run on the
6//! blocking pool, one at a time per handle. A read takes what one blocking
7//! read gave and hands the rest out on later reads. A write is copied (up to
8//! [`MAX_BUF`] bytes at once) and answered at once while the pool writes it;
9//! the next write, a flush or a close waits for it, and reports its error if
10//! it failed.
11
12use core::future::Future;
13use core::pin::Pin;
14use core::task::{Context, Poll, ready};
15use std::io;
16
17use futures_io::{AsyncRead, AsyncWrite};
18
19use crate::task::Join;
20
21/// The most bytes one blocking read or write of a standard stream moves.
22const MAX_BUF: usize = 16 * 1024;
23
24/// What a blocking operation hands back: its result, the buffer, the stream.
25type Done<T> = (io::Result<usize>, Vec<u8>, T);
26
27/// A blocking stream driven through the blocking pool.
28#[derive(Debug)]
29enum Blocking<T> {
30    /// Ready for the next operation; `buf[pos..]` holds bytes read and not
31    /// yet handed out.
32    Idle { inner: T, buf: Vec<u8>, pos: usize },
33    /// An operation on the pool.
34    Busy(Join<Done<T>>),
35    /// The stream was lost: its operation's task did not come back.
36    Gone,
37}
38
39impl<T: Send + 'static> Blocking<T> {
40    fn new(inner: T) -> Self {
41        Self::Idle {
42            inner,
43            buf: Vec::new(),
44            pos: 0,
45        }
46    }
47
48    /// Waits for the operation on the pool, if one runs; answers its result
49    /// (`Ok(0)` when none ran).
50    fn poll_idle(&mut self, cx: &mut Context<'_>) -> Poll<io::Result<usize>> {
51        let Self::Busy(job) = self else {
52            return Poll::Ready(Ok(0));
53        };
54        let done = ready!(Pin::new(job).poll(cx));
55        match done {
56            Ok((result, buf, inner)) => {
57                *self = Self::Idle { inner, buf, pos: 0 };
58                Poll::Ready(result)
59            }
60            Err(err) => {
61                *self = Self::Gone;
62                Poll::Ready(Err(err.into()))
63            }
64        }
65    }
66
67    fn start(
68        &mut self,
69        op: impl FnOnce(&mut T, &mut Vec<u8>) -> io::Result<usize> + Send + 'static,
70    ) {
71        if let Self::Idle { inner, buf, .. } = core::mem::replace(self, Self::Gone) {
72            let (mut inner, mut buf) = (inner, buf);
73            *self = Self::Busy(
74                crate::runtime::spawn_blocking(move || {
75                    let result = op(&mut inner, &mut buf);
76                    (result, buf, inner)
77                })
78                .join(),
79            );
80        }
81    }
82
83    fn gone() -> io::Error {
84        io::Error::other("the standard stream was lost with its blocking task")
85    }
86}
87
88impl<T: io::Read + Send + 'static> Blocking<T> {
89    fn poll_read(&mut self, cx: &mut Context<'_>, out: &mut [u8]) -> Poll<io::Result<usize>> {
90        loop {
91            match self {
92                Self::Idle { buf, pos, .. } if *pos < buf.len() => {
93                    let n = (buf.len() - *pos).min(out.len());
94                    out[..n].copy_from_slice(&buf[*pos..*pos + n]);
95                    *pos += n;
96                    return Poll::Ready(Ok(n));
97                }
98                Self::Idle { .. } => {
99                    if out.is_empty() {
100                        return Poll::Ready(Ok(0));
101                    }
102                    let want = out.len().min(MAX_BUF);
103                    self.start(move |inner, buf| {
104                        buf.clear();
105                        buf.resize(want, 0);
106                        let n = inner.read(buf)?;
107                        buf.truncate(n);
108                        Ok(n)
109                    });
110                }
111                Self::Busy(_) => {
112                    let result = ready!(self.poll_idle(cx));
113                    if let Err(err) = result {
114                        return Poll::Ready(Err(err));
115                    }
116                    if let Self::Idle { buf, .. } = self
117                        && buf.is_empty()
118                    {
119                        return Poll::Ready(Ok(0));
120                    }
121                }
122                Self::Gone => return Poll::Ready(Err(Self::gone())),
123            }
124        }
125    }
126}
127
128impl<T: io::Write + Send + 'static> Blocking<T> {
129    fn poll_write(&mut self, cx: &mut Context<'_>, input: &[u8]) -> Poll<io::Result<usize>> {
130        ready!(self.poll_idle(cx))?;
131        // A write op always carries bytes: an empty buffer after an op is how
132        // `poll_flush` knows its own flush ran.
133        if input.is_empty() {
134            return Poll::Ready(Ok(0));
135        }
136        match self {
137            Self::Idle { buf, .. } => {
138                let n = input.len().min(MAX_BUF);
139                buf.clear();
140                buf.extend_from_slice(&input[..n]);
141                self.start(|inner, buf| inner.write_all(buf).map(|()| buf.len()));
142                Poll::Ready(Ok(n))
143            }
144            Self::Busy(_) => Poll::Pending,
145            Self::Gone => Poll::Ready(Err(Self::gone())),
146        }
147    }
148
149    fn poll_flush(&mut self, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
150        loop {
151            match self {
152                Self::Busy(_) => {
153                    ready!(self.poll_idle(cx))?;
154                    if let Self::Idle { buf, .. } = self
155                        && buf.is_empty()
156                    {
157                        // The flush below ran: done.
158                        return Poll::Ready(Ok(()));
159                    }
160                    // A write ran: flush after it.
161                    self.start(|inner, buf| {
162                        buf.clear();
163                        inner.flush().map(|()| 0)
164                    });
165                }
166                Self::Idle { .. } => {
167                    self.start(|inner, buf| {
168                        buf.clear();
169                        inner.flush().map(|()| 0)
170                    });
171                }
172                Self::Gone => return Poll::Ready(Err(Self::gone())),
173            }
174        }
175    }
176}
177
178macro_rules! std_stream {
179    ($(#[$doc:meta])* $name:ident, $make:ident, $std:ident, $what:literal) => {
180        $(#[$doc])*
181        #[derive(Debug)]
182        pub struct $name {
183            inner: Blocking<std::io::$std>,
184        }
185
186        #[doc = concat!("The process's ", $what, ", as an async stream (see [`", stringify!($name), "`]).")]
187        pub fn $make() -> $name {
188            $name {
189                inner: Blocking::new(std::io::$make()),
190            }
191        }
192    };
193}
194
195std_stream!(
196    /// The process's standard input: reads run on the blocking pool.
197    Stdin, stdin, Stdin, "standard input"
198);
199std_stream!(
200    /// The process's standard output: writes run on the blocking pool, one
201    /// at a time; flush before the process exits to see the last one done.
202    Stdout, stdout, Stdout, "standard output"
203);
204std_stream!(
205    /// The process's standard error: writes run on the blocking pool, one at
206    /// a time.
207    Stderr, stderr, Stderr, "standard error"
208);
209
210impl AsyncRead for Stdin {
211    fn poll_read(
212        self: Pin<&mut Self>,
213        cx: &mut Context<'_>,
214        buf: &mut [u8],
215    ) -> Poll<io::Result<usize>> {
216        self.get_mut().inner.poll_read(cx, buf)
217    }
218}
219
220macro_rules! write_stream {
221    ($($name:ident),*) => {$(
222        impl AsyncWrite for $name {
223            fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<io::Result<usize>> {
224                self.get_mut().inner.poll_write(cx, buf)
225            }
226
227            fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
228                self.get_mut().inner.poll_flush(cx)
229            }
230
231            fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
232                self.get_mut().inner.poll_flush(cx)
233            }
234        }
235    )*};
236}
237write_stream!(Stdout, Stderr);
238
239#[cfg(test)]
240mod tests {
241    use super::*;
242    use crate::io::{AsyncReadExt, AsyncWriteExt};
243    use std::sync::{Arc, Mutex};
244
245    /// A blocking writer that records what reaches it and its flushes.
246    #[derive(Clone, Default)]
247    struct Recorder(Arc<Mutex<(Vec<u8>, usize)>>);
248
249    impl io::Write for Recorder {
250        fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
251            self.0.lock().unwrap().0.extend_from_slice(buf);
252            Ok(buf.len())
253        }
254
255        fn flush(&mut self) -> io::Result<()> {
256            self.0.lock().unwrap().1 += 1;
257            Ok(())
258        }
259    }
260
261    struct Wrap<T>(Blocking<T>);
262
263    impl<T: io::Write + Send + Unpin + 'static> AsyncWrite for Wrap<T> {
264        fn poll_write(
265            self: Pin<&mut Self>,
266            cx: &mut Context<'_>,
267            buf: &[u8],
268        ) -> Poll<io::Result<usize>> {
269            self.get_mut().0.poll_write(cx, buf)
270        }
271        fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
272            self.get_mut().0.poll_flush(cx)
273        }
274        fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
275            self.get_mut().0.poll_flush(cx)
276        }
277    }
278
279    impl<T: io::Read + Send + Unpin + 'static> AsyncRead for Wrap<T> {
280        fn poll_read(
281            self: Pin<&mut Self>,
282            cx: &mut Context<'_>,
283            buf: &mut [u8],
284        ) -> Poll<io::Result<usize>> {
285            self.get_mut().0.poll_read(cx, buf)
286        }
287    }
288
289    #[test]
290    fn writes_reach_the_stream_in_order_and_a_flush_waits_for_them() {
291        crate::block_on(async {
292            let record = Recorder::default();
293            let mut w = Wrap(Blocking::new(record.clone()));
294            let big = vec![b'b'; MAX_BUF * 2 + 5];
295            w.write_all(b"").await.unwrap();
296            w.flush().await.unwrap();
297            assert_eq!(
298                record.0.lock().unwrap().1,
299                1,
300                "a flush after an empty write flushes"
301            );
302            w.write_all(b"head ").await.unwrap();
303            w.write_all(&big).await.unwrap();
304            w.write_all(b" tail").await.unwrap();
305            w.flush().await.unwrap();
306            let (bytes, flushes) = record.0.lock().unwrap().clone();
307            assert_eq!(bytes.len(), 5 + big.len() + 5);
308            assert!(bytes.starts_with(b"head ") && bytes.ends_with(b" tail"));
309            assert_eq!(flushes, 2);
310        });
311    }
312
313    #[test]
314    fn reads_hand_out_what_one_blocking_read_gave() {
315        crate::block_on(async {
316            let mut r = Wrap(Blocking::new(&b"abcdefghij"[..]));
317            let mut three = [0u8; 3];
318            r.read_exact(&mut three).await.unwrap();
319            assert_eq!(&three, b"abc");
320            let mut rest = Vec::new();
321            r.read_to_end(&mut rest).await.unwrap();
322            assert_eq!(rest, b"defghij");
323        });
324    }
325
326    /// A blocking writer that fails.
327    struct Broken;
328
329    impl io::Write for Broken {
330        fn write(&mut self, _: &[u8]) -> io::Result<usize> {
331            Err(io::ErrorKind::BrokenPipe.into())
332        }
333        fn flush(&mut self) -> io::Result<()> {
334            Ok(())
335        }
336    }
337
338    #[test]
339    fn a_failed_write_is_reported_by_the_next_operation() {
340        crate::block_on(async {
341            let mut w = Wrap(Blocking::new(Broken));
342            assert_eq!(
343                w.write(b"x").await.unwrap(),
344                1,
345                "answered while the pool writes it"
346            );
347            let err = w.flush().await.unwrap_err();
348            assert_eq!(err.kind(), io::ErrorKind::BrokenPipe);
349        });
350    }
351
352    #[test]
353    fn the_process_streams_take_writes() {
354        crate::block_on(async {
355            let mut out = stdout();
356            out.write_all(b"").await.unwrap();
357            out.flush().await.unwrap();
358            let mut err = stderr();
359            err.flush().await.unwrap();
360        });
361    }
362}