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}