Improve comments

This commit is contained in:
Marcin Mielniczuk
2020-01-16 17:54:12 +01:00
parent 5b9272f2a6
commit 3c132d6909

View File

@@ -137,11 +137,6 @@ pub(crate) fn clock_time_get(clock_id: wasi::__wasi_clockid_t) -> Result<wasi::_
duration.as_nanos().try_into().map_err(Into::into) duration.as_nanos().try_into().map_err(Into::into)
} }
fn stdin_nonempty() -> bool {
use std::io::Read;
std::io::stdin().bytes().peekable().peek().is_some()
}
fn make_rw_event(event: &FdEventData, nbytes: Result<u64>) -> wasi::__wasi_event_t { fn make_rw_event(event: &FdEventData, nbytes: Result<u64>) -> wasi::__wasi_event_t {
use crate::error::WasiErrno; use crate::error::WasiErrno;
let error = nbytes.as_wasi_errno(); let error = nbytes.as_wasi_errno();
@@ -253,13 +248,6 @@ pub(crate) fn poll_oneoff(
} }
} }
// Currently WASI file support is only (a) regular files (b) directories (c) symlinks on Windows,
// which are always ready to write on Unix.
//
// We need to consider stdin/stdout/stderr separately.
// We treat stdout/stderr as always ready to write. I'm not sure if it's correct
// on Windows but I have not find any way of checking if a write to stdout would block.
// Therefore, we only poll the stdin.
let mut stdin_events = vec![]; let mut stdin_events = vec![];
let mut immediate_events = vec![]; let mut immediate_events = vec![];
let mut pipe_events = vec![]; let mut pipe_events = vec![];
@@ -269,13 +257,18 @@ pub(crate) fn poll_oneoff(
Descriptor::Stdin if event.r#type == wasi::__WASI_EVENTTYPE_FD_READ => { Descriptor::Stdin if event.r#type == wasi::__WASI_EVENTTYPE_FD_READ => {
stdin_events.push(event) stdin_events.push(event)
} }
// stdout/stderr are always considered ready to write because there seems to
// be no way of checking if a write to stdout would block.
//
// If stdin is polled for anything else then reading, then it is also
// considered immediately ready, following the behavior on Linux.
Descriptor::Stdin | Descriptor::Stderr | Descriptor::Stdout => { Descriptor::Stdin | Descriptor::Stderr | Descriptor::Stdout => {
immediate_events.push(event) immediate_events.push(event)
} }
Descriptor::OsHandle(os_handle) => { Descriptor::OsHandle(os_handle) => {
let ftype = unsafe { winx::file::get_file_type(os_handle.as_raw_handle()) }?; let ftype = unsafe { winx::file::get_file_type(os_handle.as_raw_handle()) }?;
if ftype.is_unknown() || ftype.is_char() { if ftype.is_unknown() || ftype.is_char() {
error!("poll_oneoff: unsupported file type: {:?}", ftype); debug!("poll_oneoff: unsupported file type: {:?}", ftype);
handle_error_event(event, Error::ENOTSUP, events); handle_error_event(event, Error::ENOTSUP, events);
} else if ftype.is_disk() { } else if ftype.is_disk() {
immediate_events.push(event); immediate_events.push(event);
@@ -295,13 +288,28 @@ pub(crate) fn poll_oneoff(
handle_rw_event(event, events); handle_rw_event(event, events);
} }
} else if !stdin_events.is_empty() { } else if !stdin_events.is_empty() {
// REVIEW: is there a better place to document this? Perhaps in // During the firt request to poll stdin, we spin up a separate thread to
// `struct PollStdin`?
//
// If there's a request to poll stdin, we spin up a separate thread to
// waiting for data to arrive on stdin. This thread will not terminate. // waiting for data to arrive on stdin. This thread will not terminate.
// //
// TODO more explain why this way // FIXME this is not thread safe!
//
// We'd like to do the following:
// (1) wait in a non-blocking way for data to be available in stdin, with timeout
// (2) find out, how many bytes are there available to be read.
//
// One issue is that we are currently relying on the Rust libstd for interaction
// with stdin. More precisely, `io::stdin` is used via the `BufRead` trait,
// in the `fd_read` function, which always does buffering on the libstd side. [1]
// This means that even if there's still some unread data in stdin,
// the lower-level Windows system calls may return false negatives,
// claiming that stdin is empty.
//
// Theoretically, one could use `WaitForSingleObject` on the stdin handle
// to achieve (1). Unfortunately, this function doesn't seem to honor the
// requested timeout and to misbehaves after the stdin is closed.
//
// There appears to be no way of achieving (2) on Windows.
// [1]: https://github.com/rust-lang/rust/pull/12422
trace!(" | passively waiting on stdin"); trace!(" | passively waiting on stdin");
let dur = timeout.map(|t| t.1); let dur = timeout.map(|t| t.1);
let state = STDIN_POLL.poll(dur); let state = STDIN_POLL.poll(dur);
@@ -316,10 +324,14 @@ pub(crate) fn poll_oneoff(
} }
} }
} }
} else if !pipe_events.is_empty() { }
trace!(" | actively polling stdin or pipes");
if !pipe_events.is_empty() {
trace!(" | actively polling pipes");
match timeout { match timeout {
Some((event, dur)) => { Some((event, dur)) => {
// In the tests stdin is replaced with a dummy pipe, so for now
// we just time out. Support for pipes will be decided later on.
warn!("Polling pipes not supported on Windows, will just time out."); warn!("Polling pipes not supported on Windows, will just time out.");
return Ok(handle_timeout(event, dur, events)); return Ok(handle_timeout(event, dur, events));
} }
@@ -328,32 +340,6 @@ pub(crate) fn poll_oneoff(
return Err(Error::ENOTSUP); return Err(Error::ENOTSUP);
} }
} }
// TODO remove these old comments!!!
// There are some stdin or pipe poll requests and there's no data available immediately
// We are busy-polling the stdin with delay, unfortunately.
//
// We'd like to do the following:
// (1) wait in a non-blocking way for data to be available in stdin, with timeout
// (2) find out, how many bytes are there available to be read.
// For one, using `WaitForSingleObject` on the stdin handle could possibly be one way of
// achieving (1).
// I don't know of any way to achieve (2).
//
// While both of these are not as trivial on Windows as they are on Linux, there's a much
// more fundamental issue preventing us from achieving such behavior with the current
// implementation of wasi-common.
//
// Precisely, in `fd_read` we are using `io::stdin` via the `BufRead` trait, which does
// buffering on the libstd side. This means that even if there's still some unread data
// in stdin, the Windows system calls may return false negatives, indicating that stdin is empty.
// Therefore, avoiding the busy-poll here would require us to ditch libstd for the interaction
// with stdin altogether.
//
// However, polling stdin is a relatively infrequent use case, so this hopefully won't be
// a major issue.
// avoid issuing more syscalls if we're requested to return immediately
} }
Ok(()) Ok(())