diff --git a/Cargo.lock b/Cargo.lock index ea5ff03ec1..258a848fc9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2540,6 +2540,7 @@ dependencies = [ name = "wasi-c2-cap-std-sync" version = "0.22.0" dependencies = [ + "bitflags", "cap-fs-ext", "cap-rand", "cap-std", @@ -2550,7 +2551,6 @@ dependencies = [ "tracing", "unsafe-io", "wasi-c2", - "yanix 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2580,7 +2580,7 @@ dependencies = [ "wiggle", "winapi", "winx 0.22.0", - "yanix 0.22.0", + "yanix", ] [[package]] @@ -3190,19 +3190,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "yanix" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0504d76a87b9e77f1057d419a51acb4344b9e14eaf37dde22cf1fd0ec28901db" -dependencies = [ - "bitflags", - "cfg-if 1.0.0", - "filetime", - "libc", - "tracing", -] - [[package]] name = "z3" version = "0.7.1" diff --git a/crates/wasi-c2/cap-std-sync/Cargo.toml b/crates/wasi-c2/cap-std-sync/Cargo.toml index 53b43889d1..f61afb51a0 100644 --- a/crates/wasi-c2/cap-std-sync/Cargo.toml +++ b/crates/wasi-c2/cap-std-sync/Cargo.toml @@ -20,7 +20,7 @@ cap-time-ext = "0.11" cap-rand = "0.11" fs-set-times = "0.2.2" unsafe-io = "0.2" -yanix = "0.22" system-interface = { version = "0.5.2", features = ["cap_std_impls"] } tracing = "0.1.19" libc = "0.2" +bitflags = "1.2" diff --git a/crates/wasi-c2/cap-std-sync/src/sched.rs b/crates/wasi-c2/cap-std-sync/src/sched.rs index fe87e9b7d5..a6828c2935 100644 --- a/crates/wasi-c2/cap-std-sync/src/sched.rs +++ b/crates/wasi-c2/cap-std-sync/src/sched.rs @@ -1,189 +1,9 @@ #[cfg(unix)] +mod unix; +#[cfg(unix)] pub use unix::*; + +#[cfg(windows)] +mod windows; #[cfg(windows)] pub use windows::*; - -#[cfg(unix)] -mod unix { - use cap_std::time::Duration; - use std::convert::TryInto; - use std::ops::Deref; - use std::os::unix::io::{AsRawFd, RawFd}; - use wasi_c2::{ - file::WasiFile, - sched::{ - subscription::{RwEventFlags, Subscription}, - Poll, WasiSched, - }, - Error, - }; - use yanix::poll::{PollFd, PollFlags}; - - pub struct SyncSched; - - impl WasiSched for SyncSched { - fn poll_oneoff<'a>(&self, poll: &'a Poll<'a>) -> Result<(), Error> { - if poll.is_empty() { - return Ok(()); - } - let mut pollfds = Vec::new(); - let timeout = poll.earliest_clock_deadline(); - for s in poll.rw_subscriptions() { - match s { - Subscription::Read(f) => { - let raw_fd = wasi_file_raw_fd(f.file.deref()).ok_or(Error::Inval)?; - pollfds.push(unsafe { PollFd::new(raw_fd, PollFlags::POLLIN) }); - } - - Subscription::Write(f) => { - let raw_fd = wasi_file_raw_fd(f.file.deref()).ok_or(Error::Inval)?; - pollfds.push(unsafe { PollFd::new(raw_fd, PollFlags::POLLOUT) }); - } - Subscription::MonotonicClock { .. } => unreachable!(), - } - } - - let ready = loop { - let poll_timeout = if let Some(t) = timeout { - let duration = t - .deadline - .checked_duration_since(t.clock.now(t.precision)) - .unwrap_or(Duration::from_secs(0)); - (duration.as_millis() + 1) // XXX try always rounding up? - .try_into() - .map_err(|_| Error::Overflow)? - } else { - libc::c_int::max_value() - }; - tracing::debug!( - poll_timeout = tracing::field::debug(poll_timeout), - poll_fds = tracing::field::debug(&pollfds), - "poll" - ); - match yanix::poll::poll(&mut pollfds, poll_timeout) { - Ok(ready) => break ready, - Err(_) => { - let last_err = std::io::Error::last_os_error(); - if last_err.raw_os_error().unwrap() == libc::EINTR { - continue; - } else { - return Err(last_err.into()); - } - } - } - }; - if ready > 0 { - for (rwsub, pollfd) in poll.rw_subscriptions().zip(pollfds.into_iter()) { - if let Some(revents) = pollfd.revents() { - let (nbytes, rwsub) = match rwsub { - Subscription::Read(sub) => { - let ready = sub.file.num_ready_bytes()?; - (std::cmp::max(ready, 1), sub) - } - Subscription::Write(sub) => (0, sub), - _ => unreachable!(), - }; - if revents.contains(PollFlags::POLLNVAL) { - rwsub.error(Error::Badf); - } else if revents.contains(PollFlags::POLLERR) { - rwsub.error(Error::Io); - } else if revents.contains(PollFlags::POLLHUP) { - rwsub.complete(nbytes, RwEventFlags::HANGUP); - } else { - rwsub.complete(nbytes, RwEventFlags::empty()); - }; - } - } - } else { - timeout - .expect("timed out") - .result() - .expect("timer deadline is past") - .unwrap() - } - Ok(()) - } - fn sched_yield(&self) -> Result<(), Error> { - std::thread::yield_now(); - Ok(()) - } - } - - fn wasi_file_raw_fd(f: &dyn WasiFile) -> Option { - let a = f.as_any(); - if a.is::() { - Some(a.downcast_ref::().unwrap().as_raw_fd()) - } else if a.is::() { - Some(a.downcast_ref::().unwrap().as_raw_fd()) - } else if a.is::() { - Some( - a.downcast_ref::() - .unwrap() - .as_raw_fd(), - ) - } else if a.is::() { - Some( - a.downcast_ref::() - .unwrap() - .as_raw_fd(), - ) - } else { - None - } - } -} - -#[cfg(windows)] -mod windows { - use std::os::windows::io::{AsRawHandle, RawHandle}; - use wasi_c2::{ - file::WasiFile, - sched::{Poll, WasiSched}, - Error, - }; - pub struct SyncSched; - - impl WasiSched for SyncSched { - fn poll_oneoff<'a>(&self, poll: &'a Poll<'a>) -> Result<(), Error> { - if poll.is_empty() { - return Ok(()); - } - todo!() - } - fn sched_yield(&self) -> Result<(), Error> { - std::thread::yield_now(); - Ok(()) - } - } - - fn wasi_file_raw_handle(f: &dyn WasiFile) -> Option { - let a = f.as_any(); - if a.is::() { - Some( - a.downcast_ref::() - .unwrap() - .as_raw_handle(), - ) - } else if a.is::() { - Some( - a.downcast_ref::() - .unwrap() - .as_raw_handle(), - ) - } else if a.is::() { - Some( - a.downcast_ref::() - .unwrap() - .as_raw_handle(), - ) - } else if a.is::() { - Some( - a.downcast_ref::() - .unwrap() - .as_raw_handle(), - ) - } else { - None - } - } -} diff --git a/crates/wasi-c2/cap-std-sync/src/sched/unix.rs b/crates/wasi-c2/cap-std-sync/src/sched/unix.rs new file mode 100644 index 0000000000..20c4c61ce7 --- /dev/null +++ b/crates/wasi-c2/cap-std-sync/src/sched/unix.rs @@ -0,0 +1,183 @@ +use cap_std::time::Duration; +use std::convert::TryInto; +use std::ops::Deref; +use std::os::unix::io::{AsRawFd, RawFd}; +use wasi_c2::{ + file::WasiFile, + sched::{ + subscription::{RwEventFlags, Subscription}, + Poll, WasiSched, + }, + Error, +}; + +use poll::{PollFd, PollFlags}; + +pub struct SyncSched; + +impl WasiSched for SyncSched { + fn poll_oneoff<'a>(&self, poll: &'a Poll<'a>) -> Result<(), Error> { + if poll.is_empty() { + return Ok(()); + } + let mut pollfds = Vec::new(); + let timeout = poll.earliest_clock_deadline(); + for s in poll.rw_subscriptions() { + match s { + Subscription::Read(f) => { + let raw_fd = wasi_file_raw_fd(f.file.deref()).ok_or(Error::Inval)?; + pollfds.push(unsafe { PollFd::new(raw_fd, PollFlags::POLLIN) }); + } + + Subscription::Write(f) => { + let raw_fd = wasi_file_raw_fd(f.file.deref()).ok_or(Error::Inval)?; + pollfds.push(unsafe { PollFd::new(raw_fd, PollFlags::POLLOUT) }); + } + Subscription::MonotonicClock { .. } => unreachable!(), + } + } + + let ready = loop { + let poll_timeout = if let Some(t) = timeout { + let duration = t + .deadline + .checked_duration_since(t.clock.now(t.precision)) + .unwrap_or(Duration::from_secs(0)); + (duration.as_millis() + 1) // XXX try always rounding up? + .try_into() + .map_err(|_| Error::Overflow)? + } else { + libc::c_int::max_value() + }; + tracing::debug!( + poll_timeout = tracing::field::debug(poll_timeout), + poll_fds = tracing::field::debug(&pollfds), + "poll" + ); + match poll::poll(&mut pollfds, poll_timeout) { + Ok(ready) => break ready, + Err(_) => { + let last_err = std::io::Error::last_os_error(); + if last_err.raw_os_error().unwrap() == libc::EINTR { + continue; + } else { + return Err(last_err.into()); + } + } + } + }; + if ready > 0 { + for (rwsub, pollfd) in poll.rw_subscriptions().zip(pollfds.into_iter()) { + if let Some(revents) = pollfd.revents() { + let (nbytes, rwsub) = match rwsub { + Subscription::Read(sub) => { + let ready = sub.file.num_ready_bytes()?; + (std::cmp::max(ready, 1), sub) + } + Subscription::Write(sub) => (0, sub), + _ => unreachable!(), + }; + if revents.contains(PollFlags::POLLNVAL) { + rwsub.error(Error::Badf); + } else if revents.contains(PollFlags::POLLERR) { + rwsub.error(Error::Io); + } else if revents.contains(PollFlags::POLLHUP) { + rwsub.complete(nbytes, RwEventFlags::HANGUP); + } else { + rwsub.complete(nbytes, RwEventFlags::empty()); + }; + } + } + } else { + timeout + .expect("timed out") + .result() + .expect("timer deadline is past") + .unwrap() + } + Ok(()) + } + fn sched_yield(&self) -> Result<(), Error> { + std::thread::yield_now(); + Ok(()) + } +} + +fn wasi_file_raw_fd(f: &dyn WasiFile) -> Option { + let a = f.as_any(); + if a.is::() { + Some(a.downcast_ref::().unwrap().as_raw_fd()) + } else if a.is::() { + Some(a.downcast_ref::().unwrap().as_raw_fd()) + } else if a.is::() { + Some( + a.downcast_ref::() + .unwrap() + .as_raw_fd(), + ) + } else if a.is::() { + Some( + a.downcast_ref::() + .unwrap() + .as_raw_fd(), + ) + } else { + None + } +} + +mod poll { + use bitflags::bitflags; + use std::convert::TryInto; + use std::os::unix::io::RawFd; + + bitflags! { + pub struct PollFlags: libc::c_short { + const POLLIN = libc::POLLIN; + const POLLPRI = libc::POLLPRI; + const POLLOUT = libc::POLLOUT; + const POLLRDNORM = libc::POLLRDNORM; + const POLLWRNORM = libc::POLLWRNORM; + const POLLRDBAND = libc::POLLRDBAND; + const POLLWRBAND = libc::POLLWRBAND; + const POLLERR = libc::POLLERR; + const POLLHUP = libc::POLLHUP; + const POLLNVAL = libc::POLLNVAL; + } + } + + #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] + #[repr(C)] + pub struct PollFd(libc::pollfd); + + impl PollFd { + pub unsafe fn new(fd: RawFd, events: PollFlags) -> Self { + Self(libc::pollfd { + fd, + events: events.bits(), + revents: PollFlags::empty().bits(), + }) + } + + pub fn revents(self) -> Option { + PollFlags::from_bits(self.0.revents) + } + } + + pub fn poll(fds: &mut [PollFd], timeout: libc::c_int) -> Result { + let nready = unsafe { + libc::poll( + fds.as_mut_ptr() as *mut libc::pollfd, + fds.len() as libc::nfds_t, + timeout, + ) + }; + if nready == -1 { + Err(std::io::Error::last_os_error()) + } else { + // When poll doesn't fail, its return value is a non-negative int, which will + // always be convertable to usize, so we can unwrap() here. + Ok(nready.try_into().unwrap()) + } + } +} diff --git a/crates/wasi-c2/cap-std-sync/src/sched/windows.rs b/crates/wasi-c2/cap-std-sync/src/sched/windows.rs new file mode 100644 index 0000000000..7bb6d9a824 --- /dev/null +++ b/crates/wasi-c2/cap-std-sync/src/sched/windows.rs @@ -0,0 +1,52 @@ + +use std::os::windows::io::{AsRawHandle, RawHandle}; +use wasi_c2::{ + file::WasiFile, + sched::{Poll, WasiSched}, + Error, +}; +pub struct SyncSched; + +impl WasiSched for SyncSched { + fn poll_oneoff<'a>(&self, poll: &'a Poll<'a>) -> Result<(), Error> { + if poll.is_empty() { + return Ok(()); + } + todo!() + } + fn sched_yield(&self) -> Result<(), Error> { + std::thread::yield_now(); + Ok(()) + } +} + +fn wasi_file_raw_handle(f: &dyn WasiFile) -> Option { + let a = f.as_any(); + if a.is::() { + Some( + a.downcast_ref::() + .unwrap() + .as_raw_handle(), + ) + } else if a.is::() { + Some( + a.downcast_ref::() + .unwrap() + .as_raw_handle(), + ) + } else if a.is::() { + Some( + a.downcast_ref::() + .unwrap() + .as_raw_handle(), + ) + } else if a.is::() { + Some( + a.downcast_ref::() + .unwrap() + .as_raw_handle(), + ) + } else { + None + } +}