Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support "sleep" forms of poll_oneoff. #2753

Merged
merged 3 commits into from
Mar 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 41 additions & 1 deletion crates/test-programs/wasi-tests/src/bin/poll_oneoff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@ unsafe fn test_empty_poll() {
}

unsafe fn test_timeout() {
let timeout = 5_000_000u64; // 5 milliseconds
let clock = wasi::SubscriptionClock {
id: wasi::CLOCKID_MONOTONIC,
timeout: 5_000_000u64, // 5 milliseconds
timeout,
precision: 0,
flags: 0,
};
Expand All @@ -39,7 +40,9 @@ unsafe fn test_timeout() {
u: wasi::SubscriptionUU { clock },
},
}];
let before = wasi::clock_time_get(wasi::CLOCKID_MONOTONIC, 0).unwrap();
let out = poll_oneoff_impl(&r#in).unwrap();
let after = wasi::clock_time_get(wasi::CLOCKID_MONOTONIC, 0).unwrap();
assert_eq!(out.len(), 1, "should return 1 event");
let event = &out[0];
assert_errno!(event.error, wasi::ERRNO_SUCCESS);
Expand All @@ -52,6 +55,42 @@ unsafe fn test_timeout() {
event.userdata, CLOCK_ID,
"the event.userdata should contain clock_id specified by the user"
);
assert!(after - before >= timeout, "poll_oneoff should sleep for the specified interval");
}

// Like test_timeout, but uses `CLOCKID_REALTIME`, as WASI libc's sleep
// functions do.
unsafe fn test_sleep() {
let timeout = 5_000_000u64; // 5 milliseconds
let clock = wasi::SubscriptionClock {
id: wasi::CLOCKID_REALTIME,
timeout,
precision: 0,
flags: 0,
};
let r#in = [wasi::Subscription {
userdata: CLOCK_ID,
u: wasi::SubscriptionU {
tag: wasi::EVENTTYPE_CLOCK,
u: wasi::SubscriptionUU { clock },
},
}];
let before = wasi::clock_time_get(wasi::CLOCKID_MONOTONIC, 0).unwrap();
let out = poll_oneoff_impl(&r#in).unwrap();
let after = wasi::clock_time_get(wasi::CLOCKID_MONOTONIC, 0).unwrap();
assert_eq!(out.len(), 1, "should return 1 event");
let event = &out[0];
assert_errno!(event.error, wasi::ERRNO_SUCCESS);
assert_eq!(
event.r#type,
wasi::EVENTTYPE_CLOCK,
"the event.type should equal clock"
);
assert_eq!(
event.userdata, CLOCK_ID,
"the event.userdata should contain clock_id specified by the user"
);
assert!(after - before >= timeout, "poll_oneoff should sleep for the specified interval");
}

unsafe fn test_fd_readwrite(fd: wasi::Fd, error_code: wasi::Errno) {
Expand Down Expand Up @@ -156,6 +195,7 @@ unsafe fn test_fd_readwrite_invalid_fd() {

unsafe fn test_poll_oneoff(dir_fd: wasi::Fd) {
test_timeout();
test_sleep();
test_empty_poll();
test_fd_readwrite_valid_fd(dir_fd);
test_fd_readwrite_invalid_fd();
Expand Down
3 changes: 3 additions & 0 deletions crates/wasi-common/cap-std-sync/src/sched/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ impl WasiSched for SyncSched {
std::thread::yield_now();
Ok(())
}
fn sleep(&self, duration: Duration) {
std::thread::sleep(duration)
}
}

fn wasi_file_raw_fd(f: &dyn WasiFile) -> Option<RawFd> {
Expand Down
3 changes: 3 additions & 0 deletions crates/wasi-common/cap-std-sync/src/sched/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,9 @@ impl WasiSched for SyncSched {
thread::yield_now();
Ok(())
}
fn sleep(&self, duration: Duration) {
std::thread::sleep(duration)
}
}

fn wasi_file_raw_handle(f: &dyn WasiFile) -> Option<RawHandle> {
Expand Down
1 change: 1 addition & 0 deletions crates/wasi-common/src/sched.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use subscription::{MonotonicClockSubscription, RwSubscription, Subscription, Sub
pub trait WasiSched {
fn poll_oneoff(&self, poll: &Poll) -> Result<(), Error>;
fn sched_yield(&self) -> Result<(), Error>;
fn sleep(&self, duration: Duration);
}

pub struct Userdata(u64);
Expand Down
24 changes: 24 additions & 0 deletions crates/wasi-common/src/snapshots/preview_0.rs
Original file line number Diff line number Diff line change
Expand Up @@ -738,6 +738,30 @@ impl<'a> wasi_unstable::WasiUnstable for WasiCtx {
return Err(Error::invalid_argument().context("nsubscriptions must be nonzero"));
}

// Special-case a `poll_oneoff` which is just sleeping on a single
// relative timer event, such as what WASI libc uses to implement sleep
// functions. This supports all clock IDs, because POSIX says that
// `clock_settime` doesn't effect relative sleeps.
if nsubscriptions == 1 {
let sub = subs.read()?;
if let types::SubscriptionU::Clock(clocksub) = sub.u {
if !clocksub
.flags
.contains(types::Subclockflags::SUBSCRIPTION_CLOCK_ABSTIME)
{
self.sched.sleep(Duration::from_nanos(clocksub.timeout));

events.write(types::Event {
userdata: sub.userdata,
error: types::Errno::Success,
type_: types::Eventtype::Clock,
fd_readwrite: fd_readwrite_empty(),
})?;
return Ok(1);
}
}
}

let table = self.table();
let mut poll = Poll::new();

Expand Down
24 changes: 24 additions & 0 deletions crates/wasi-common/src/snapshots/preview_1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -909,6 +909,30 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
return Err(Error::invalid_argument().context("nsubscriptions must be nonzero"));
}

// Special-case a `poll_oneoff` which is just sleeping on a single
// relative timer event, such as what WASI libc uses to implement sleep
// functions. This supports all clock IDs, because POSIX says that
// `clock_settime` doesn't effect relative sleeps.
if nsubscriptions == 1 {
let sub = subs.read()?;
if let types::SubscriptionU::Clock(clocksub) = sub.u {
if !clocksub
.flags
.contains(types::Subclockflags::SUBSCRIPTION_CLOCK_ABSTIME)
{
self.sched.sleep(Duration::from_nanos(clocksub.timeout));

events.write(types::Event {
userdata: sub.userdata,
error: types::Errno::Success,
type_: types::Eventtype::Clock,
fd_readwrite: fd_readwrite_empty(),
})?;
return Ok(1);
}
}
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This special case is a good idea, but i'd like for the thread::sleep call to instead be dispatched into self.sched.sleep so that wasi-common consumers can control that implementation by either using the default wasi-cap-std-sync impls or creating their own.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good; I've now added a sleep function to WasiSched and added impls for it.

let table = self.table();
let mut poll = Poll::new();

Expand Down