wasi-c2: implement a synchronous poll_oneoff in terms of yanix
doesnt work on unix yet! also breaks all the rules about using the cap-std family instead of rawfds! but this is cool and im happy with it
This commit is contained in:
16
Cargo.lock
generated
16
Cargo.lock
generated
@@ -2472,6 +2472,7 @@ dependencies = [
|
|||||||
"thiserror",
|
"thiserror",
|
||||||
"tracing",
|
"tracing",
|
||||||
"wiggle",
|
"wiggle",
|
||||||
|
"yanix 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2501,7 +2502,7 @@ dependencies = [
|
|||||||
"wiggle",
|
"wiggle",
|
||||||
"winapi",
|
"winapi",
|
||||||
"winx 0.22.0",
|
"winx 0.22.0",
|
||||||
"yanix",
|
"yanix 0.22.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3111,6 +3112,19 @@ dependencies = [
|
|||||||
"tracing",
|
"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]]
|
[[package]]
|
||||||
name = "z3"
|
name = "z3"
|
||||||
version = "0.7.1"
|
version = "0.7.1"
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ cap-rand = "0.9"
|
|||||||
fs-set-times = "0.2.2"
|
fs-set-times = "0.2.2"
|
||||||
cfg-if = "1"
|
cfg-if = "1"
|
||||||
bitflags = "1.2"
|
bitflags = "1.2"
|
||||||
|
yanix = "0.22"
|
||||||
|
|
||||||
[badges]
|
[badges]
|
||||||
maintenance = { status = "actively-developed" }
|
maintenance = { status = "actively-developed" }
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ impl WasiCtxBuilder {
|
|||||||
self.0.insert_file(
|
self.0.insert_file(
|
||||||
0,
|
0,
|
||||||
f,
|
f,
|
||||||
FileCaps::READ, // XXX fixme: more rights are ok, but this is read-only
|
FileCaps::READ | FileCaps::POLL_READWRITE, // XXX fixme: more rights are ok, but this is read-only
|
||||||
);
|
);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@@ -84,7 +84,7 @@ impl WasiCtxBuilder {
|
|||||||
self.0.insert_file(
|
self.0.insert_file(
|
||||||
1,
|
1,
|
||||||
f,
|
f,
|
||||||
FileCaps::WRITE, // XXX fixme: more rights are ok, but this is append only
|
FileCaps::WRITE | FileCaps::POLL_READWRITE, // XXX fixme: more rights are ok, but this is append only
|
||||||
);
|
);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@@ -93,7 +93,7 @@ impl WasiCtxBuilder {
|
|||||||
self.0.insert_file(
|
self.0.insert_file(
|
||||||
2,
|
2,
|
||||||
f,
|
f,
|
||||||
FileCaps::WRITE, // XXX fixme: more rights are ok, but this is append only
|
FileCaps::WRITE | FileCaps::POLL_READWRITE, // XXX fixme: more rights are ok, but this is append only
|
||||||
);
|
);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ impl<'a> Poll<'a> {
|
|||||||
}
|
}
|
||||||
pub fn subscribe_write(&mut self, file: Ref<'a, dyn WasiFile>, ud: Userdata) {
|
pub fn subscribe_write(&mut self, file: Ref<'a, dyn WasiFile>, ud: Userdata) {
|
||||||
self.subs
|
self.subs
|
||||||
.push((Subscription::Read(RwSubscription::new(file)), ud));
|
.push((Subscription::Write(RwSubscription::new(file)), ud));
|
||||||
}
|
}
|
||||||
pub fn results(self) -> Vec<(SubscriptionResult, Userdata)> {
|
pub fn results(self) -> Vec<(SubscriptionResult, Userdata)> {
|
||||||
self.subs
|
self.subs
|
||||||
@@ -66,7 +66,25 @@ impl<'a> Poll<'a> {
|
|||||||
.filter_map(|(s, ud)| SubscriptionResult::from_subscription(s).map(|r| (r, ud)))
|
.filter_map(|(s, ud)| SubscriptionResult::from_subscription(s).map(|r| (r, ud)))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
pub fn subscriptions(&'a self) -> impl Iterator<Item = &Subscription<'a>> {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.subs.iter().map(|(s, _ud)| s)
|
self.subs.is_empty()
|
||||||
|
}
|
||||||
|
pub fn earliest_system_timer(&'a self) -> Option<&SystemTimerSubscription<'a>> {
|
||||||
|
let mut subs = self
|
||||||
|
.subs
|
||||||
|
.iter()
|
||||||
|
.filter_map(|(s, _ud)| match s {
|
||||||
|
Subscription::SystemTimer(t) => Some(t),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.collect::<Vec<&SystemTimerSubscription<'a>>>();
|
||||||
|
subs.sort_by(|a, b| a.deadline.cmp(&b.deadline));
|
||||||
|
subs.into_iter().next() // First element is earliest
|
||||||
|
}
|
||||||
|
pub fn rw_subscriptions(&'a self) -> impl Iterator<Item = &Subscription<'a>> {
|
||||||
|
self.subs.iter().filter_map(|(s, _ud)| match s {
|
||||||
|
Subscription::Read { .. } | Subscription::Write { .. } => Some(s),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,12 +41,11 @@ pub struct SystemTimerSubscription<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> SystemTimerSubscription<'a> {
|
impl<'a> SystemTimerSubscription<'a> {
|
||||||
|
pub fn now(&self) -> SystemTime {
|
||||||
|
self.clock.now(self.precision)
|
||||||
|
}
|
||||||
pub fn result(&self) -> Option<Result<(), Error>> {
|
pub fn result(&self) -> Option<Result<(), Error>> {
|
||||||
if self
|
if self.now().duration_since(self.deadline).is_ok() {
|
||||||
.deadline
|
|
||||||
.duration_since(self.clock.now(self.precision))
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
Some(Ok(()))
|
Some(Ok(()))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
|||||||
@@ -1,27 +1,102 @@
|
|||||||
use crate::file::WasiFile;
|
|
||||||
use crate::sched::subscription::{RwSubscription, Subscription, SystemTimerSubscription};
|
|
||||||
use crate::sched::{Poll, WasiSched};
|
|
||||||
use crate::Error;
|
|
||||||
use std::any::Any;
|
|
||||||
use std::ops::Deref;
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use std::os::unix::io::{AsRawFd, RawFd};
|
pub use unix::*;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[cfg(unix)]
|
||||||
pub struct SyncSched {}
|
mod unix {
|
||||||
|
use crate::file::WasiFile;
|
||||||
|
use crate::sched::subscription::{RwEventFlags, Subscription};
|
||||||
|
use crate::sched::{Poll, WasiSched};
|
||||||
|
use crate::Error;
|
||||||
|
use cap_std::time::Duration;
|
||||||
|
use std::convert::TryInto;
|
||||||
|
use std::ops::Deref;
|
||||||
|
use std::os::unix::io::{AsRawFd, RawFd};
|
||||||
|
use yanix::poll::{PollFd, PollFlags};
|
||||||
|
|
||||||
impl WasiSched for SyncSched {
|
#[derive(Default)]
|
||||||
fn poll_oneoff(&self, poll: &Poll) -> Result<(), Error> {
|
pub struct SyncSched {}
|
||||||
for s in poll.subscriptions() {
|
|
||||||
|
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_system_timer();
|
||||||
|
for s in poll.rw_subscriptions() {
|
||||||
match s {
|
match s {
|
||||||
Subscription::Read(f) | Subscription::Write(f) => {
|
Subscription::Read(f) => {
|
||||||
let raw_fd = wasi_file_raw_fd(f.file.deref()).ok_or(Error::Inval)?;
|
let raw_fd = wasi_file_raw_fd(f.file.deref()).ok_or(Error::Inval)?;
|
||||||
todo!()
|
pollfds.push(unsafe { PollFd::new(raw_fd, PollFlags::POLLIN) });
|
||||||
}
|
}
|
||||||
Subscription::SystemTimer(t) => {
|
|
||||||
todo!()
|
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::SystemTimer { .. } => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let ready = loop {
|
||||||
|
let poll_timeout = if let Some(t) = timeout {
|
||||||
|
let duration = t
|
||||||
|
.deadline
|
||||||
|
.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) => {
|
||||||
|
(1, sub)
|
||||||
|
// XXX FIXME: query_nbytes in wasi-common/src/sys/poll.rs
|
||||||
|
// uses metadata.len - tell to calculate for regular files,
|
||||||
|
// ioctl(fd, FIONREAD) for large files
|
||||||
|
}
|
||||||
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -29,9 +104,9 @@ impl WasiSched for SyncSched {
|
|||||||
std::thread::yield_now();
|
std::thread::yield_now();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wasi_file_raw_fd(f: &dyn WasiFile) -> Option<RawFd> {
|
fn wasi_file_raw_fd(f: &dyn WasiFile) -> Option<RawFd> {
|
||||||
let a = f.as_any();
|
let a = f.as_any();
|
||||||
if a.is::<cap_std::fs::File>() {
|
if a.is::<cap_std::fs::File>() {
|
||||||
Some(a.downcast_ref::<cap_std::fs::File>().unwrap().as_raw_fd())
|
Some(a.downcast_ref::<cap_std::fs::File>().unwrap().as_raw_fd())
|
||||||
@@ -52,4 +127,5 @@ fn wasi_file_raw_fd(f: &dyn WasiFile) -> Option<RawFd> {
|
|||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -891,6 +891,10 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
|||||||
events: &GuestPtr<types::Event>,
|
events: &GuestPtr<types::Event>,
|
||||||
nsubscriptions: types::Size,
|
nsubscriptions: types::Size,
|
||||||
) -> Result<types::Size, Error> {
|
) -> Result<types::Size, Error> {
|
||||||
|
if nsubscriptions == 0 {
|
||||||
|
return Err(Error::Inval);
|
||||||
|
}
|
||||||
|
|
||||||
use cap_std::time::{Duration, SystemClock};
|
use cap_std::time::{Duration, SystemClock};
|
||||||
let table = self.table();
|
let table = self.table();
|
||||||
let mut poll = Poll::new();
|
let mut poll = Poll::new();
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ impl Table {
|
|||||||
Err(Error::Exist) // Does exist, but borrowed
|
Err(Error::Exist) // Does exist, but borrowed
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(Error::Exist) // Does not exist
|
Err(Error::Badf) // Does not exist
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user