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