async_std/io/
stdout.rs

1use std::pin::Pin;
2use std::sync::Mutex;
3use std::future::Future;
4
5use crate::io::{self, Write};
6use crate::task::{spawn_blocking, Context, JoinHandle, Poll};
7
8cfg_unstable! {
9    use once_cell::sync::Lazy;
10    use std::io::Write as _;
11}
12
13/// Constructs a new handle to the standard output of the current process.
14///
15/// This function is an async version of [`std::io::stdout`].
16///
17/// [`std::io::stdout`]: https://doc.rust-lang.org/std/io/fn.stdout.html
18///
19/// ### Note: Windows Portability Consideration
20///
21/// When operating in a console, the Windows implementation of this stream does not support
22/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return
23/// an error.
24///
25/// # Examples
26///
27/// ```no_run
28/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
29/// #
30/// use async_std::io;
31/// use async_std::prelude::*;
32///
33/// let mut stdout = io::stdout();
34/// stdout.write_all(b"Hello, world!").await?;
35/// #
36/// # Ok(()) }) }
37/// ```
38pub fn stdout() -> Stdout {
39    Stdout(Mutex::new(State::Idle(Some(Inner {
40        stdout: std::io::stdout(),
41        buf: Vec::new(),
42        last_op: None,
43    }))))
44}
45
46/// A handle to the standard output of the current process.
47///
48/// This writer is created by the [`stdout`] function. See its documentation
49/// for more.
50///
51/// ### Note: Windows Portability Consideration
52///
53/// When operating in a console, the Windows implementation of this stream does not support
54/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return
55/// an error.
56///
57/// [`stdout`]: fn.stdout.html
58#[derive(Debug)]
59pub struct Stdout(Mutex<State>);
60
61/// A locked reference to the Stderr handle.
62///
63/// This handle implements the [`Write`] traits, and is constructed via the [`Stdout::lock`]
64/// method.
65///
66/// [`Write`]: trait.Read.html
67/// [`Stdout::lock`]: struct.Stdout.html#method.lock
68#[cfg(feature = "unstable")]
69#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
70#[derive(Debug)]
71pub struct StdoutLock<'a>(std::io::StdoutLock<'a>);
72
73#[cfg(feature = "unstable")]
74#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
75unsafe impl Send for StdoutLock<'_> {}
76
77/// The state of the asynchronous stdout.
78///
79/// The stdout can be either idle or busy performing an asynchronous operation.
80#[derive(Debug)]
81enum State {
82    /// The stdout is idle.
83    Idle(Option<Inner>),
84
85    /// The stdout is blocked on an asynchronous operation.
86    ///
87    /// Awaiting this operation will result in the new state of the stdout.
88    Busy(JoinHandle<State>),
89}
90
91/// Inner representation of the asynchronous stdout.
92#[derive(Debug)]
93struct Inner {
94    /// The blocking stdout handle.
95    stdout: std::io::Stdout,
96
97    /// The write buffer.
98    buf: Vec<u8>,
99
100    /// The result of the last asynchronous operation on the stdout.
101    last_op: Option<Operation>,
102}
103
104/// Possible results of an asynchronous operation on the stdout.
105#[derive(Debug)]
106enum Operation {
107    Write(io::Result<usize>),
108    Flush(io::Result<()>),
109}
110
111impl Stdout {
112    /// Locks this handle to the standard error stream, returning a writable guard.
113    ///
114    /// The lock is released when the returned lock goes out of scope. The returned guard also implements the Write trait for writing data.
115    ///
116    /// # Examples
117    ///
118    /// ```no_run
119    /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
120    /// #
121    /// use async_std::io;
122    /// use async_std::prelude::*;
123    ///
124    /// let stdout = io::stdout();
125    /// let mut handle = stdout.lock().await;
126    ///
127    /// handle.write_all(b"hello world").await?;
128    /// #
129    /// # Ok(()) }) }
130    /// ```
131    #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
132    #[cfg(any(feature = "unstable", feature = "docs"))]
133    pub async fn lock(&self) -> StdoutLock<'static> {
134        static STDOUT: Lazy<std::io::Stdout> = Lazy::new(std::io::stdout);
135
136        spawn_blocking(move || StdoutLock(STDOUT.lock())).await.unwrap()
137    }
138}
139
140impl Write for Stdout {
141    fn poll_write(
142        mut self: Pin<&mut Self>,
143        cx: &mut Context<'_>,
144        buf: &[u8],
145    ) -> Poll<io::Result<usize>> {
146        let state = &mut *self.0.lock().unwrap();
147
148        loop {
149            match state {
150                State::Idle(opt) => {
151                    let inner = opt.as_mut().unwrap();
152
153                    // Check if the operation has completed.
154                    if let Some(Operation::Write(res)) = inner.last_op.take() {
155                        let n = res?;
156
157                        // If more data was written than is available in the buffer, let's retry
158                        // the write operation.
159                        if n <= buf.len() {
160                            return Poll::Ready(Ok(n));
161                        }
162                    } else {
163                        let mut inner = opt.take().unwrap();
164
165                        // Set the length of the inner buffer to the length of the provided buffer.
166                        if inner.buf.len() < buf.len() {
167                            inner.buf.reserve(buf.len() - inner.buf.len());
168                        }
169                        unsafe {
170                            inner.buf.set_len(buf.len());
171                        }
172
173                        // Copy the data to write into the inner buffer.
174                        inner.buf[..buf.len()].copy_from_slice(buf);
175
176                        // Start the operation asynchronously.
177                        *state = State::Busy(spawn_blocking(move || {
178                            let res = std::io::Write::write(&mut inner.stdout, &inner.buf);
179                            inner.last_op = Some(Operation::Write(res));
180                            State::Idle(Some(inner))
181                        }));
182                    }
183                }
184                // Poll the asynchronous operation the stdout is currently blocked on.
185                State::Busy(task) => *state = futures_core::ready!(Pin::new(task).poll(cx))?,
186            }
187        }
188    }
189
190    fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
191        let state = &mut *self.0.lock().unwrap();
192
193        loop {
194            match state {
195                State::Idle(opt) => {
196                    let inner = opt.as_mut().unwrap();
197
198                    // Check if the operation has completed.
199                    if let Some(Operation::Flush(res)) = inner.last_op.take() {
200                        return Poll::Ready(res);
201                    } else {
202                        let mut inner = opt.take().unwrap();
203
204                        // Start the operation asynchronously.
205                        *state = State::Busy(spawn_blocking(move || {
206                            let res = std::io::Write::flush(&mut inner.stdout);
207                            inner.last_op = Some(Operation::Flush(res));
208                            State::Idle(Some(inner))
209                        }));
210                    }
211                }
212                // Poll the asynchronous operation the stdout is currently blocked on.
213                State::Busy(task) => *state = futures_core::ready!(Pin::new(task).poll(cx))?,
214            }
215        }
216    }
217
218    fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
219        self.poll_flush(cx)
220    }
221}
222
223cfg_unix! {
224    use crate::os::unix::io::{AsRawFd, RawFd};
225
226    impl AsRawFd for Stdout {
227        fn as_raw_fd(&self) -> RawFd {
228            std::io::stdout().as_raw_fd()
229        }
230    }
231}
232
233cfg_windows! {
234    use crate::os::windows::io::{AsRawHandle, RawHandle};
235
236    impl AsRawHandle for Stdout {
237        fn as_raw_handle(&self) -> RawHandle {
238            std::io::stdout().as_raw_handle()
239        }
240    }
241}
242
243#[cfg(feature = "unstable")]
244#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
245impl Write for StdoutLock<'_> {
246    fn poll_write(
247        mut self: Pin<&mut Self>,
248        _cx: &mut Context<'_>,
249        buf: &[u8],
250    ) -> Poll<io::Result<usize>> {
251        Poll::Ready(self.0.write(buf))
252    }
253
254    fn poll_flush(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
255        Poll::Ready(self.0.flush())
256    }
257
258    fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
259        self.poll_flush(cx)
260    }
261}