Skip to content

Commit

Permalink
feat(wasi): add support for wasi:[email protected]
Browse files Browse the repository at this point in the history
Signed-off-by: Roman Volosatovs <[email protected]>
  • Loading branch information
rvolosatovs committed Jan 22, 2025
1 parent 3ba676b commit e7d9c96
Show file tree
Hide file tree
Showing 21 changed files with 364 additions and 9 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions ci/vendor-wit.sh
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ make_vendor "wasi/src/p2" "
"

make_vendor "wasi/src/p3" "
clocks@[email protected]
random@[email protected]
"

Expand Down
3 changes: 3 additions & 0 deletions crates/test-programs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,13 @@ anyhow = { workspace = true, features = ['std'] }
wasi = "0.11.0"
wasi-nn = "0.6.0"
wit-bindgen = { workspace = true, features = ['default'] }
# TODO: Remove once https://github.com/bytecodealliance/wit-bindgen/pull/1136 lands
wit-bindgen-rt = "0.37"
libc = { workspace = true }
getrandom = "0.2.9"
futures = { workspace = true, default-features = false, features = ['alloc'] }
url = { workspace = true }
sha2 = "0.10.2"
base64 = "0.21.0"
wasip2 = { version = "0.14.0", package = 'wasi' }
tokio = { workspace = true, features = ["macros"] }
2 changes: 1 addition & 1 deletion crates/test-programs/src/bin/preview2_sleep.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use test_programs::wasi::clocks::monotonic_clock;
use test_programs::wasi::clocks0_2_3::monotonic_clock;

fn main() {
sleep_10ms();
Expand Down
62 changes: 62 additions & 0 deletions crates/test-programs/src/bin/preview3_sleep.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use core::future::Future as _;
use core::pin::pin;
use core::ptr;
use core::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};

use test_programs::wasi::clocks0_3_0::monotonic_clock;

// Adapted from https://github.com/rust-lang/rust/blob/cd805f09ffbfa3896c8f50a619de9b67e1d9f3c3/library/core/src/task/wake.rs#L63-L77
// TODO: Replace by `Waker::noop` once MSRV is raised to 1.85
const NOOP_RAW_WAKER: RawWaker = {
const VTABLE: RawWakerVTable = RawWakerVTable::new(
// Cloning just returns a new no-op raw waker
|_| NOOP_RAW_WAKER,
// `wake` does nothing
|_| {},
// `wake_by_ref` does nothing
|_| {},
// Dropping does nothing as we don't allocate anything
|_| {},
);
RawWaker::new(ptr::null(), &VTABLE)
};

const NOOP_WAKER: &'static Waker = &unsafe { Waker::from_raw(NOOP_RAW_WAKER) };

#[tokio::main(flavor = "current_thread")]
async fn main() {
sleep_10ms().await;
sleep_0ms();
sleep_backwards_in_time();
}

async fn sleep_10ms() {
let dur = 10_000_000;
monotonic_clock::wait_until(monotonic_clock::now() + dur).await;
monotonic_clock::wait_for(dur).await;
}

fn sleep_0ms() {
let mut cx = Context::from_waker(NOOP_WAKER);

assert_eq!(
pin!(monotonic_clock::wait_until(monotonic_clock::now())).poll(&mut cx),
Poll::Ready(()),
"waiting until now() is ready immediately",
);
assert_eq!(
pin!(monotonic_clock::wait_for(0)).poll(&mut cx),
Poll::Ready(()),
"waiting for 0 is ready immediately",
);
}

fn sleep_backwards_in_time() {
let mut cx = Context::from_waker(NOOP_WAKER);

assert_eq!(
pin!(monotonic_clock::wait_until(monotonic_clock::now() - 1)).poll(&mut cx),
Poll::Ready(()),
"waiting until instant which has passed is ready immediately",
);
}
11 changes: 9 additions & 2 deletions crates/test-programs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ wit_bindgen::generate!({
include wasi:config/[email protected];
include wasi:keyvalue/[email protected];
include wasi:clocks/[email protected];
include wasi:random/[email protected];
}
",
Expand All @@ -24,6 +25,12 @@ wit_bindgen::generate!({
],
world: "wasmtime:test/test",
features: ["cli-exit-with-code"],
async: {
imports: [
"wasi:clocks/[email protected]#wait-for",
"wasi:clocks/[email protected]#wait-until",
],
},
generate_all,
});

Expand All @@ -43,8 +50,8 @@ pub mod proxy {
"wasi:cli/[email protected]": crate::wasi::cli::stdout,
"wasi:cli/[email protected]": crate::wasi::cli::stderr,
"wasi:cli/[email protected]": crate::wasi::cli::stdin,
"wasi:clocks/[email protected]": crate::wasi::clocks::monotonic_clock,
"wasi:clocks/[email protected]": crate::wasi::clocks::wall_clock,
"wasi:clocks/[email protected]": crate::wasi::clocks0_2_3::monotonic_clock,
"wasi:clocks/[email protected]": crate::wasi::clocks0_2_3::wall_clock,
},
});
}
2 changes: 1 addition & 1 deletion crates/test-programs/src/sockets.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::wasi::clocks::monotonic_clock;
use crate::wasi::clocks0_2_3::monotonic_clock;
use crate::wasi::io::poll::{self, Pollable};
use crate::wasi::io::streams::{InputStream, OutputStream, StreamError};
use crate::wasi::random0_2_3 as random;
Expand Down
2 changes: 1 addition & 1 deletion crates/wasi/src/p2/host/clocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ impl TryFrom<SystemTime> for Datetime {
let duration =
time.duration_since(SystemTime::from_std(std::time::SystemTime::UNIX_EPOCH))?;

Ok(Datetime {
Ok(Self {
seconds: duration.as_secs(),
nanoseconds: duration.subsec_nanos(),
})
Expand Down
11 changes: 9 additions & 2 deletions crates/wasi/src/p3/bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@
//! inline: "
//! package example:wasi;
//!
//! // An example of extending the `wasi:random/imports` world with a
//! // An example of extending the `wasi:cli/command` world with a
//! // custom host interface.
//! world my-world {
//! include wasi:clocks/[email protected];
//! include wasi:random/[email protected];
//!
//! import custom-host;
Expand Down Expand Up @@ -96,9 +97,10 @@
/// inline: "
/// package example:wasi;
///
/// // An example of extending the `wasi:random/imports` world with a
/// // An example of extending the `wasi:cli/command` world with a
/// // custom host interface.
/// world my-world {
/// include wasi:clocks/[email protected];
/// include wasi:random/[email protected];
///
/// import custom-host;
Expand Down Expand Up @@ -157,6 +159,7 @@ pub mod sync {
package inline:wasi;
world command {
include wasi:clocks/[email protected];
include wasi:random/[email protected];
}
",
Expand All @@ -166,6 +169,7 @@ pub mod sync {
// These interfaces come from the outer module, as it's
// sync/async agnostic.
"wasi:random": crate::p3::bindings::random,
"wasi:clocks/wall-clock": crate::p3::bindings::clocks::wall_clock,
},
require_store_data_send: true,
});
Expand Down Expand Up @@ -324,6 +328,7 @@ mod async_io {
package inline:wasi;
world command {
include wasi:clocks/[email protected];
include wasi:random/[email protected];
}
",
Expand All @@ -338,6 +343,8 @@ mod async_io {
// which in theory can be shared across interfaces, so this may
// need fancier syntax in the future.
only_imports: [
"wait-for",
"wait-until",
],
},
});
Expand Down
73 changes: 73 additions & 0 deletions crates/wasi/src/p3/host/clocks.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
use crate::p3::bindings::{
clocks::monotonic_clock::{self, Duration as WasiDuration, Instant},
clocks::wall_clock::{self, Datetime},
};
use crate::{WasiImpl, WasiView};
use cap_std::time::SystemTime;
use std::time::Duration;
use tokio::time::sleep;

mod sync;

impl TryFrom<SystemTime> for Datetime {
type Error = anyhow::Error;

fn try_from(time: SystemTime) -> Result<Self, Self::Error> {
let duration =
time.duration_since(SystemTime::from_std(std::time::SystemTime::UNIX_EPOCH))?;

Ok(Self {
seconds: duration.as_secs(),
nanoseconds: duration.subsec_nanos(),
})
}
}

impl<T> wall_clock::Host for WasiImpl<T>
where
T: WasiView,
{
fn now(&mut self) -> anyhow::Result<Datetime> {
let now = self.ctx().wall_clock.now();
Ok(Datetime {
seconds: now.as_secs(),
nanoseconds: now.subsec_nanos(),
})
}

fn resolution(&mut self) -> anyhow::Result<Datetime> {
let res = self.ctx().wall_clock.resolution();
Ok(Datetime {
seconds: res.as_secs(),
nanoseconds: res.subsec_nanos(),
})
}
}

impl<T> monotonic_clock::Host for WasiImpl<T>
where
T: WasiView,
{
fn now(&mut self) -> anyhow::Result<Instant> {
Ok(self.ctx().monotonic_clock.now())
}

fn resolution(&mut self) -> anyhow::Result<Instant> {
Ok(self.ctx().monotonic_clock.resolution())
}

async fn wait_until(&mut self, when: Instant) -> anyhow::Result<()> {
let clock_now = self.ctx().monotonic_clock.now();
if when > clock_now {
sleep(Duration::from_nanos(when - clock_now)).await;
};
Ok(())
}

async fn wait_for(&mut self, duration: WasiDuration) -> anyhow::Result<()> {
if duration > 0 {
sleep(Duration::from_nanos(duration)).await;
}
Ok(())
}
}
28 changes: 28 additions & 0 deletions crates/wasi/src/p3/host/clocks/sync.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use crate::p3::bindings::clocks as async_clocks;
use crate::p3::bindings::sync::clocks as sync_clocks;
use crate::p3::bindings::sync::clocks::monotonic_clock::{Duration, Instant};
use crate::runtime::in_tokio;
use crate::{WasiImpl, WasiView};

impl<T> sync_clocks::monotonic_clock::Host for WasiImpl<T>
where
T: WasiView,
{
fn now(&mut self) -> anyhow::Result<Instant> {
async_clocks::monotonic_clock::Host::now(self)
}

fn resolution(&mut self) -> anyhow::Result<Instant> {
async_clocks::monotonic_clock::Host::resolution(self)
}

fn wait_until(&mut self, when: Instant) -> anyhow::Result<()> {
in_tokio(async_clocks::monotonic_clock::Host::wait_until(self, when))
}

fn wait_for(&mut self, duration: Duration) -> anyhow::Result<()> {
in_tokio(async_clocks::monotonic_clock::Host::wait_for(
self, duration,
))
}
}
1 change: 1 addition & 0 deletions crates/wasi/src/p3/host/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
mod clocks;
mod random;
4 changes: 4 additions & 0 deletions crates/wasi/src/p3/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ pub fn add_to_linker_async<T: WasiView>(linker: &mut Linker<T>) -> anyhow::Resul
let l = linker;
let closure = type_annotate::<T, _>(|t| WasiImpl(IoImpl(t)));

crate::p3::bindings::clocks::wall_clock::add_to_linker_get_host(l, closure)?;
crate::p3::bindings::clocks::monotonic_clock::add_to_linker_get_host(l, closure)?;
crate::p3::bindings::random::random::add_to_linker_get_host(l, closure)?;
crate::p3::bindings::random::insecure::add_to_linker_get_host(l, closure)?;
crate::p3::bindings::random::insecure_seed::add_to_linker_get_host(l, closure)?;
Expand Down Expand Up @@ -145,6 +147,8 @@ pub fn add_to_linker_sync<T: WasiView>(
let l = linker;
let closure = type_annotate::<T, _>(|t| WasiImpl(IoImpl(t)));

crate::p3::bindings::clocks::wall_clock::add_to_linker_get_host(l, closure)?;
crate::p3::bindings::sync::clocks::monotonic_clock::add_to_linker_get_host(l, closure)?;
crate::p3::bindings::random::random::add_to_linker_get_host(l, closure)?;
crate::p3::bindings::random::insecure::add_to_linker_get_host(l, closure)?;
crate::p3::bindings::random::insecure_seed::add_to_linker_get_host(l, closure)?;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package wasi:clocks@0.3.0;
/// WASI Monotonic Clock is a clock API intended to let users measure elapsed
/// time.
///
/// It is intended to be portable at least between Unix-family platforms and
/// Windows.
///
/// A monotonic clock is a clock which has an unspecified initial value, and
/// successive reads of the clock will produce non-decreasing values.
@since(version = 0.3.0)
interface monotonic-clock {
/// An instant in time, in nanoseconds. An instant is relative to an
/// unspecified initial value, and can only be compared to instances from
/// the same monotonic-clock.
@since(version = 0.3.0)
type instant = u64;

/// A duration of time, in nanoseconds.
@since(version = 0.3.0)
type duration = u64;

/// Read the current value of the clock.
///
/// The clock is monotonic, therefore calling this function repeatedly will
/// produce a sequence of non-decreasing values.
@since(version = 0.3.0)
now: func() -> instant;

/// Query the resolution of the clock. Returns the duration of time
/// corresponding to a clock tick.
@since(version = 0.3.0)
resolution: func() -> duration;

/// Wait until the specified instant has occurred.
@since(version = 0.3.0)
wait-until: func(
when: instant,
);

/// Wait for the specified duration has elapsed.
@since(version = 0.3.0)
wait-for: func(
how-long: duration,
);
}
Loading

0 comments on commit e7d9c96

Please sign in to comment.