async_std/io/
stderr.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 error of the current process.
9///
10/// This function is an async version of [`std::io::stderr`].
11///
12/// [`std::io::stderr`]: https://doc.rust-lang.org/std/io/fn.stderr.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 stderr = io::stderr();
29/// stderr.write_all(b"Hello, world!").await?;
30/// #
31/// # Ok(()) }) }
32/// ```
33pub fn stderr() -> Stderr {
34    Stderr(Mutex::new(State::Idle(Some(Inner {
35        stderr: std::io::stderr(),
36        buf: Vec::new(),
37        last_op: None,
38    }))))
39}
40
41/// A handle to the standard error of the current process.
42///
43/// This writer is created by the [`stderr`] function. See its documentation for
44/// 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/// [`stderr`]: fn.stderr.html
53#[derive(Debug)]
54pub struct Stderr(Mutex<State>);
55
56/// The state of the asynchronous stderr.
57///
58/// The stderr can be either idle or busy performing an asynchronous operation.
59#[derive(Debug)]
60enum State {
61    /// The stderr is idle.
62    Idle(Option<Inner>),
63
64    /// The stderr is blocked on an asynchronous operation.
65    ///
66    /// Awaiting this operation will result in the new state of the stderr.
67    Busy(JoinHandle<State>),
68}
69
70/// Inner representation of the asynchronous stderr.
71#[derive(Debug)]
72struct Inner {
73    /// The blocking stderr handle.
74    stderr: std::io::Stderr,
75
76    /// The write buffer.
77    buf: Vec<u8>,
78
79    /// The result of the last asynchronous operation on the stderr.
80    last_op: Option<Operation>,
81}
82
83/// Possible results of an asynchronous operation on the stderr.
84#[derive(Debug)]
85enum Operation {
86    Write(io::Result<usize>),
87    Flush(io::Result<()>),
88}
89
90impl Write for Stderr {
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.stderr, &inner.buf);
130                            inner.last_op = Some(Operation::Write(res));
131                            State::Idle(Some(inner))
132                        }));
133                    }
134                }
135                // Poll the asynchronous operation the stderr 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.stderr);
159                            inner.last_op = Some(Operation::Flush(res));
160                            State::Idle(Some(inner))
161                        }));
162                    }
163                }
164                // Poll the asynchronous operation the stderr 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 Stderr {
179        fn as_raw_fd(&self) -> RawFd {
180            std::io::stderr().as_raw_fd()
181        }
182    }
183
184    cfg_io_safety! {
185        use crate::os::unix::io::{AsFd, BorrowedFd};
186
187        impl AsFd for Stderr {
188            fn as_fd(&self) -> BorrowedFd<'_> {
189                unsafe {
190                    BorrowedFd::borrow_raw(std::io::stderr().as_raw_fd())
191                }
192            }
193        }
194    }
195}
196
197cfg_windows! {
198    use crate::os::windows::io::{AsRawHandle, RawHandle};
199
200    impl AsRawHandle for Stderr {
201        fn as_raw_handle(&self) -> RawHandle {
202            std::io::stderr().as_raw_handle()
203        }
204    }
205
206    cfg_io_safety! {
207        use crate::os::windows::io::{AsHandle, BorrowedHandle};
208
209        impl AsHandle for Stderr {
210            fn as_handle(&self) -> BorrowedHandle<'_> {
211                unsafe {
212                    BorrowedHandle::borrow_raw(std::io::stderr().as_raw_handle())
213                }
214            }
215        }
216    }
217}