1e6a662b3SAlex Crichton //! Handling for standard in using a worker task.
2e6a662b3SAlex Crichton //!
3e6a662b3SAlex Crichton //! Standard input is a global singleton resource for the entire program which
4e6a662b3SAlex Crichton //! needs special care. Currently this implementation adheres to a few
5e6a662b3SAlex Crichton //! constraints which make this nontrivial to implement.
6e6a662b3SAlex Crichton //!
7e6a662b3SAlex Crichton //! * Any number of guest wasm programs can read stdin. While this doesn't make
8e6a662b3SAlex Crichton //!   a ton of sense semantically they shouldn't block forever. Instead it's a
9e6a662b3SAlex Crichton //!   race to see who actually reads which parts of stdin.
10e6a662b3SAlex Crichton //!
11e6a662b3SAlex Crichton //! * Data from stdin isn't actually read unless requested. This is done to try
12e6a662b3SAlex Crichton //!   to be a good neighbor to others running in the process. Under the
13e6a662b3SAlex Crichton //!   assumption that most programs have one "thing" which reads stdin the
14e6a662b3SAlex Crichton //!   actual consumption of bytes is delayed until the wasm guest is dynamically
15e6a662b3SAlex Crichton //!   chosen to be that "thing". Before that data from stdin is not consumed to
16e6a662b3SAlex Crichton //!   avoid taking it from other components in the process.
17e6a662b3SAlex Crichton //!
18e6a662b3SAlex Crichton //! * Tokio's documentation indicates that "interactive stdin" is best done with
19e6a662b3SAlex Crichton //!   a helper thread to avoid blocking shutdown of the event loop. That's
20e6a662b3SAlex Crichton //!   respected here where all stdin reading happens on a blocking helper thread
21e6a662b3SAlex Crichton //!   that, at this time, is never shut down.
22e6a662b3SAlex Crichton //!
23e6a662b3SAlex Crichton //! This module is one that's likely to change over time though as new systems
24e6a662b3SAlex Crichton //! are encountered along with preexisting bugs.
25e6a662b3SAlex Crichton 
26e6a662b3SAlex Crichton use crate::cli::{IsTerminal, StdinStream};
27e6a662b3SAlex Crichton use bytes::{Bytes, BytesMut};
28e6a662b3SAlex Crichton use std::io::Read;
29e6a662b3SAlex Crichton use std::mem;
30e6a662b3SAlex Crichton use std::pin::Pin;
31e6a662b3SAlex Crichton use std::sync::{Condvar, Mutex, OnceLock};
32e6a662b3SAlex Crichton use std::task::{Context, Poll};
33e6a662b3SAlex Crichton use tokio::io::{self, AsyncRead, ReadBuf};
34e6a662b3SAlex Crichton use tokio::sync::Notify;
35e6a662b3SAlex Crichton use tokio::sync::futures::Notified;
36e6a662b3SAlex Crichton use wasmtime_wasi_io::{
37e6a662b3SAlex Crichton     poll::Pollable,
38e6a662b3SAlex Crichton     streams::{InputStream, StreamError},
39e6a662b3SAlex Crichton };
40e6a662b3SAlex Crichton 
41e6a662b3SAlex Crichton // Implementation for tokio::io::Stdin
42e6a662b3SAlex Crichton impl IsTerminal for tokio::io::Stdin {
is_terminal(&self) -> bool43e6a662b3SAlex Crichton     fn is_terminal(&self) -> bool {
44e6a662b3SAlex Crichton         std::io::stdin().is_terminal()
45e6a662b3SAlex Crichton     }
46e6a662b3SAlex Crichton }
47e6a662b3SAlex Crichton impl StdinStream for tokio::io::Stdin {
p2_stream(&self) -> Box<dyn InputStream>48e6a662b3SAlex Crichton     fn p2_stream(&self) -> Box<dyn InputStream> {
49e6a662b3SAlex Crichton         Box::new(WasiStdin)
50e6a662b3SAlex Crichton     }
async_stream(&self) -> Box<dyn AsyncRead + Send + Sync>51e6a662b3SAlex Crichton     fn async_stream(&self) -> Box<dyn AsyncRead + Send + Sync> {
52e6a662b3SAlex Crichton         Box::new(WasiStdinAsyncRead::Ready)
53e6a662b3SAlex Crichton     }
54e6a662b3SAlex Crichton }
55e6a662b3SAlex Crichton 
56e6a662b3SAlex Crichton // Implementation for std::io::Stdin
57e6a662b3SAlex Crichton impl IsTerminal for std::io::Stdin {
is_terminal(&self) -> bool58e6a662b3SAlex Crichton     fn is_terminal(&self) -> bool {
59e6a662b3SAlex Crichton         std::io::IsTerminal::is_terminal(self)
60e6a662b3SAlex Crichton     }
61e6a662b3SAlex Crichton }
62e6a662b3SAlex Crichton impl StdinStream for std::io::Stdin {
p2_stream(&self) -> Box<dyn InputStream>63e6a662b3SAlex Crichton     fn p2_stream(&self) -> Box<dyn InputStream> {
64e6a662b3SAlex Crichton         Box::new(WasiStdin)
65e6a662b3SAlex Crichton     }
async_stream(&self) -> Box<dyn AsyncRead + Send + Sync>66e6a662b3SAlex Crichton     fn async_stream(&self) -> Box<dyn AsyncRead + Send + Sync> {
67e6a662b3SAlex Crichton         Box::new(WasiStdinAsyncRead::Ready)
68e6a662b3SAlex Crichton     }
69e6a662b3SAlex Crichton }
70e6a662b3SAlex Crichton 
71e6a662b3SAlex Crichton #[derive(Default)]
72e6a662b3SAlex Crichton struct GlobalStdin {
73e6a662b3SAlex Crichton     state: Mutex<StdinState>,
74e6a662b3SAlex Crichton     read_requested: Condvar,
75e6a662b3SAlex Crichton     read_completed: Notify,
76e6a662b3SAlex Crichton }
77e6a662b3SAlex Crichton 
78e6a662b3SAlex Crichton #[derive(Default, Debug)]
79e6a662b3SAlex Crichton enum StdinState {
80e6a662b3SAlex Crichton     #[default]
81e6a662b3SAlex Crichton     ReadNotRequested,
82e6a662b3SAlex Crichton     ReadRequested,
83e6a662b3SAlex Crichton     Data(BytesMut),
84e6a662b3SAlex Crichton     Error(std::io::Error),
85e6a662b3SAlex Crichton     Closed,
86e6a662b3SAlex Crichton }
87e6a662b3SAlex Crichton 
88e6a662b3SAlex Crichton impl GlobalStdin {
get() -> &'static GlobalStdin89e6a662b3SAlex Crichton     fn get() -> &'static GlobalStdin {
90e6a662b3SAlex Crichton         static STDIN: OnceLock<GlobalStdin> = OnceLock::new();
91e6a662b3SAlex Crichton         STDIN.get_or_init(|| create())
92e6a662b3SAlex Crichton     }
93e6a662b3SAlex Crichton }
94e6a662b3SAlex Crichton 
create() -> GlobalStdin95e6a662b3SAlex Crichton fn create() -> GlobalStdin {
96e6a662b3SAlex Crichton     std::thread::spawn(|| {
97e6a662b3SAlex Crichton         let state = GlobalStdin::get();
98e6a662b3SAlex Crichton         loop {
99e6a662b3SAlex Crichton             // Wait for a read to be requested, but don't hold the lock across
100e6a662b3SAlex Crichton             // the blocking read.
101e6a662b3SAlex Crichton             let mut lock = state.state.lock().unwrap();
102e6a662b3SAlex Crichton             lock = state
103e6a662b3SAlex Crichton                 .read_requested
104e6a662b3SAlex Crichton                 .wait_while(lock, |state| !matches!(state, StdinState::ReadRequested))
105e6a662b3SAlex Crichton                 .unwrap();
106e6a662b3SAlex Crichton             drop(lock);
107e6a662b3SAlex Crichton 
108e6a662b3SAlex Crichton             let mut bytes = BytesMut::zeroed(1024);
109e6a662b3SAlex Crichton             let (new_state, done) = match std::io::stdin().read(&mut bytes) {
110e6a662b3SAlex Crichton                 Ok(0) => (StdinState::Closed, true),
111e6a662b3SAlex Crichton                 Ok(nbytes) => {
112e6a662b3SAlex Crichton                     bytes.truncate(nbytes);
113e6a662b3SAlex Crichton                     (StdinState::Data(bytes), false)
114e6a662b3SAlex Crichton                 }
115e6a662b3SAlex Crichton                 Err(e) => (StdinState::Error(e), true),
116e6a662b3SAlex Crichton             };
117e6a662b3SAlex Crichton 
118e6a662b3SAlex Crichton             // After the blocking read completes the state should not have been
119e6a662b3SAlex Crichton             // tampered with.
120e6a662b3SAlex Crichton             debug_assert!(matches!(
121e6a662b3SAlex Crichton                 *state.state.lock().unwrap(),
122e6a662b3SAlex Crichton                 StdinState::ReadRequested
123e6a662b3SAlex Crichton             ));
124*b6eef223SAlex Crichton             let mut lock = state.state.lock().unwrap();
125*b6eef223SAlex Crichton             *lock = new_state;
126e6a662b3SAlex Crichton             state.read_completed.notify_waiters();
127e6a662b3SAlex Crichton             if done {
128e6a662b3SAlex Crichton                 break;
129e6a662b3SAlex Crichton             }
130e6a662b3SAlex Crichton         }
131e6a662b3SAlex Crichton     });
132e6a662b3SAlex Crichton 
133e6a662b3SAlex Crichton     GlobalStdin::default()
134e6a662b3SAlex Crichton }
135e6a662b3SAlex Crichton 
136e6a662b3SAlex Crichton struct WasiStdin;
137e6a662b3SAlex Crichton 
138e6a662b3SAlex Crichton #[async_trait::async_trait]
139e6a662b3SAlex Crichton impl InputStream for WasiStdin {
read(&mut self, size: usize) -> Result<Bytes, StreamError>140e6a662b3SAlex Crichton     fn read(&mut self, size: usize) -> Result<Bytes, StreamError> {
141e6a662b3SAlex Crichton         let g = GlobalStdin::get();
142e6a662b3SAlex Crichton         let mut locked = g.state.lock().unwrap();
143e6a662b3SAlex Crichton         match mem::replace(&mut *locked, StdinState::ReadRequested) {
144e6a662b3SAlex Crichton             StdinState::ReadNotRequested => {
145e6a662b3SAlex Crichton                 g.read_requested.notify_one();
146e6a662b3SAlex Crichton                 Ok(Bytes::new())
147e6a662b3SAlex Crichton             }
148e6a662b3SAlex Crichton             StdinState::ReadRequested => Ok(Bytes::new()),
149e6a662b3SAlex Crichton             StdinState::Data(mut data) => {
150e6a662b3SAlex Crichton                 let size = data.len().min(size);
151e6a662b3SAlex Crichton                 let bytes = data.split_to(size);
152e6a662b3SAlex Crichton                 *locked = if data.is_empty() {
153e6a662b3SAlex Crichton                     StdinState::ReadNotRequested
154e6a662b3SAlex Crichton                 } else {
155e6a662b3SAlex Crichton                     StdinState::Data(data)
156e6a662b3SAlex Crichton                 };
157e6a662b3SAlex Crichton                 Ok(bytes.freeze())
158e6a662b3SAlex Crichton             }
159e6a662b3SAlex Crichton             StdinState::Error(e) => {
160e6a662b3SAlex Crichton                 *locked = StdinState::Closed;
161e6a662b3SAlex Crichton                 Err(StreamError::LastOperationFailed(e.into()))
162e6a662b3SAlex Crichton             }
163e6a662b3SAlex Crichton             StdinState::Closed => {
164e6a662b3SAlex Crichton                 *locked = StdinState::Closed;
165e6a662b3SAlex Crichton                 Err(StreamError::Closed)
166e6a662b3SAlex Crichton             }
167e6a662b3SAlex Crichton         }
168e6a662b3SAlex Crichton     }
169e6a662b3SAlex Crichton }
170e6a662b3SAlex Crichton 
171e6a662b3SAlex Crichton #[async_trait::async_trait]
172e6a662b3SAlex Crichton impl Pollable for WasiStdin {
ready(&mut self)173e6a662b3SAlex Crichton     async fn ready(&mut self) {
174e6a662b3SAlex Crichton         let g = GlobalStdin::get();
175e6a662b3SAlex Crichton 
176e6a662b3SAlex Crichton         // Scope the synchronous `state.lock()` to this block which does not
177e6a662b3SAlex Crichton         // `.await` inside of it.
178e6a662b3SAlex Crichton         let notified = {
179e6a662b3SAlex Crichton             let mut locked = g.state.lock().unwrap();
180e6a662b3SAlex Crichton             match *locked {
181e6a662b3SAlex Crichton                 // If a read isn't requested yet
182e6a662b3SAlex Crichton                 StdinState::ReadNotRequested => {
183e6a662b3SAlex Crichton                     g.read_requested.notify_one();
184e6a662b3SAlex Crichton                     *locked = StdinState::ReadRequested;
185e6a662b3SAlex Crichton                     g.read_completed.notified()
186e6a662b3SAlex Crichton                 }
187e6a662b3SAlex Crichton                 StdinState::ReadRequested => g.read_completed.notified(),
188e6a662b3SAlex Crichton                 StdinState::Data(_) | StdinState::Closed | StdinState::Error(_) => return,
189e6a662b3SAlex Crichton             }
190e6a662b3SAlex Crichton         };
191e6a662b3SAlex Crichton 
192e6a662b3SAlex Crichton         notified.await;
193e6a662b3SAlex Crichton     }
194e6a662b3SAlex Crichton }
195e6a662b3SAlex Crichton 
196e6a662b3SAlex Crichton enum WasiStdinAsyncRead {
197e6a662b3SAlex Crichton     Ready,
198e6a662b3SAlex Crichton     Waiting(Notified<'static>),
199e6a662b3SAlex Crichton }
200e6a662b3SAlex Crichton 
201e6a662b3SAlex Crichton impl AsyncRead for WasiStdinAsyncRead {
poll_read( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut ReadBuf<'_>, ) -> Poll<io::Result<()>>202e6a662b3SAlex Crichton     fn poll_read(
203e6a662b3SAlex Crichton         mut self: Pin<&mut Self>,
204e6a662b3SAlex Crichton         cx: &mut Context<'_>,
205e6a662b3SAlex Crichton         buf: &mut ReadBuf<'_>,
206e6a662b3SAlex Crichton     ) -> Poll<io::Result<()>> {
207e6a662b3SAlex Crichton         let g = GlobalStdin::get();
208e6a662b3SAlex Crichton 
209*b6eef223SAlex Crichton         // Everything below is executed under the global stdin lock. It's not
210*b6eef223SAlex Crichton         // going to block below so that's semantically fine. Optimization-wise
211*b6eef223SAlex Crichton         // it's probably possible to move this within the loop around just a
212*b6eef223SAlex Crichton         // small part of reading/writing the state, but that was done
213*b6eef223SAlex Crichton         // historically and it resulted in lost wakeups with `Notify`, so this
214*b6eef223SAlex Crichton         // is conservatively hoisted up here.
215*b6eef223SAlex Crichton         let mut locked = g.state.lock().unwrap();
216*b6eef223SAlex Crichton 
217e6a662b3SAlex Crichton         // Perform everything below in a `loop` to handle the case that a read
218e6a662b3SAlex Crichton         // was stolen by another thread, for example, or perhaps a spurious
219e6a662b3SAlex Crichton         // notification to `Notified`.
220e6a662b3SAlex Crichton         loop {
221e6a662b3SAlex Crichton             // If we were previously blocked on reading a "ready" notification,
222e6a662b3SAlex Crichton             // wait for that notification to complete.
223e6a662b3SAlex Crichton             if let Some(notified) = self.as_mut().notified_future() {
224e6a662b3SAlex Crichton                 match notified.poll(cx) {
225e6a662b3SAlex Crichton                     Poll::Ready(()) => self.set(WasiStdinAsyncRead::Ready),
226e6a662b3SAlex Crichton                     Poll::Pending => break Poll::Pending,
227e6a662b3SAlex Crichton                 }
228e6a662b3SAlex Crichton             }
229e6a662b3SAlex Crichton 
230e6a662b3SAlex Crichton             assert!(matches!(*self, WasiStdinAsyncRead::Ready));
231e6a662b3SAlex Crichton 
232e6a662b3SAlex Crichton             // Once we're in the "ready" state then take a look at the global
233e6a662b3SAlex Crichton             // state of stdin.
234e6a662b3SAlex Crichton             match mem::replace(&mut *locked, StdinState::ReadRequested) {
235e6a662b3SAlex Crichton                 // If data is available then drain what we can into `buf`.
236e6a662b3SAlex Crichton                 StdinState::Data(mut data) => {
237e6a662b3SAlex Crichton                     let size = data.len().min(buf.remaining());
238e6a662b3SAlex Crichton                     let bytes = data.split_to(size);
239e6a662b3SAlex Crichton                     *locked = if data.is_empty() {
240e6a662b3SAlex Crichton                         StdinState::ReadNotRequested
241e6a662b3SAlex Crichton                     } else {
242e6a662b3SAlex Crichton                         StdinState::Data(data)
243e6a662b3SAlex Crichton                     };
244e6a662b3SAlex Crichton                     buf.put_slice(&bytes);
245e6a662b3SAlex Crichton                     break Poll::Ready(Ok(()));
246e6a662b3SAlex Crichton                 }
247e6a662b3SAlex Crichton 
248e6a662b3SAlex Crichton                 // If stdin failed to be read then we fail with that error and
249e6a662b3SAlex Crichton                 // transition to "closed"
250e6a662b3SAlex Crichton                 StdinState::Error(e) => {
251e6a662b3SAlex Crichton                     *locked = StdinState::Closed;
252e6a662b3SAlex Crichton                     break Poll::Ready(Err(e));
253e6a662b3SAlex Crichton                 }
254e6a662b3SAlex Crichton 
255e6a662b3SAlex Crichton                 // If stdin is closed, keep it closed.
256e6a662b3SAlex Crichton                 StdinState::Closed => {
257e6a662b3SAlex Crichton                     *locked = StdinState::Closed;
258e6a662b3SAlex Crichton                     break Poll::Ready(Ok(()));
259e6a662b3SAlex Crichton                 }
260e6a662b3SAlex Crichton 
261e6a662b3SAlex Crichton                 // For these states we indicate that a read is requested, if it
262e6a662b3SAlex Crichton                 // wasn't previously requested, and then we transition to
263e6a662b3SAlex Crichton                 // `Waiting` below by falling through outside this `match`.
264e6a662b3SAlex Crichton                 StdinState::ReadNotRequested => {
265e6a662b3SAlex Crichton                     g.read_requested.notify_one();
266e6a662b3SAlex Crichton                 }
267e6a662b3SAlex Crichton                 StdinState::ReadRequested => {}
268e6a662b3SAlex Crichton             }
269e6a662b3SAlex Crichton 
270e6a662b3SAlex Crichton             self.set(WasiStdinAsyncRead::Waiting(g.read_completed.notified()));
271e6a662b3SAlex Crichton         }
272e6a662b3SAlex Crichton     }
273e6a662b3SAlex Crichton }
274e6a662b3SAlex Crichton 
275e6a662b3SAlex Crichton impl WasiStdinAsyncRead {
notified_future(self: Pin<&mut Self>) -> Option<Pin<&mut Notified<'static>>>276e6a662b3SAlex Crichton     fn notified_future(self: Pin<&mut Self>) -> Option<Pin<&mut Notified<'static>>> {
277e6a662b3SAlex Crichton         // SAFETY: this is a pin-projection from `self` to the field `Notified`
278e6a662b3SAlex Crichton         // internally. Given that `self` is pinned it should be safe to acquire
279e6a662b3SAlex Crichton         // a pinned version of the internal field.
280e6a662b3SAlex Crichton         unsafe {
281e6a662b3SAlex Crichton             match self.get_unchecked_mut() {
282e6a662b3SAlex Crichton                 WasiStdinAsyncRead::Ready => None,
283e6a662b3SAlex Crichton                 WasiStdinAsyncRead::Waiting(notified) => Some(Pin::new_unchecked(notified)),
284e6a662b3SAlex Crichton             }
285e6a662b3SAlex Crichton         }
286e6a662b3SAlex Crichton     }
287e6a662b3SAlex Crichton }
288