Support "sleep" forms of poll_oneoff.

Add support for `poll_oneoff` calls which just sleep on a relative
timeout. This fixes a bug handling code compiled with WASI libc's `sleep`
family of functions, which call `poll_oneoff` with a `CLOCK_REALTIME`
timer, which wasn't previously implemented.
This commit is contained in:
Dan Gohman
2021-03-22 09:11:55 -07:00
parent cba0144612
commit 6b40724d18
2 changed files with 65 additions and 1 deletions

View File

@@ -26,9 +26,10 @@ unsafe fn test_empty_poll() {
} }
unsafe fn test_timeout() { unsafe fn test_timeout() {
let timeout = 5_000_000u64; // 5 milliseconds
let clock = wasi::SubscriptionClock { let clock = wasi::SubscriptionClock {
id: wasi::CLOCKID_MONOTONIC, id: wasi::CLOCKID_MONOTONIC,
timeout: 5_000_000u64, // 5 milliseconds timeout,
precision: 0, precision: 0,
flags: 0, flags: 0,
}; };
@@ -39,7 +40,9 @@ unsafe fn test_timeout() {
u: wasi::SubscriptionUU { 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 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"); assert_eq!(out.len(), 1, "should return 1 event");
let event = &out[0]; let event = &out[0];
assert_errno!(event.error, wasi::ERRNO_SUCCESS); assert_errno!(event.error, wasi::ERRNO_SUCCESS);
@@ -52,6 +55,42 @@ unsafe fn test_timeout() {
event.userdata, CLOCK_ID, event.userdata, CLOCK_ID,
"the event.userdata should contain clock_id specified by the user" "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) { unsafe fn test_fd_readwrite(fd: wasi::Fd, error_code: wasi::Errno) {

View File

@@ -16,6 +16,7 @@ use std::cell::{Ref, RefMut};
use std::convert::{TryFrom, TryInto}; use std::convert::{TryFrom, TryInto};
use std::io::{IoSlice, IoSliceMut}; use std::io::{IoSlice, IoSliceMut};
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use std::thread;
use tracing::debug; use tracing::debug;
use wiggle::GuestPtr; use wiggle::GuestPtr;
@@ -909,6 +910,30 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
return Err(Error::invalid_argument().context("nsubscriptions must be nonzero")); 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)
{
thread::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 table = self.table();
let mut poll = Poll::new(); let mut poll = Poll::new();