Skip to content
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
11 changes: 1 addition & 10 deletions Cargo.lock

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

16 changes: 12 additions & 4 deletions ci/vendor-wit.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,18 @@ make_vendor() {
mkdir -p $path

for package in $packages; do
IFS='@' read -r repo tag <<< "$package"
mkdir -p $path/$repo
IFS='@' read -r repo tag subdir <<< "$package"
mkdir -p "$path/$repo"
cached_extracted_dir="$cache_dir/$repo-$tag"

if [[ ! -d $cached_extracted_dir ]]; then
mkdir -p $cached_extracted_dir
curl -sL https://github.com/WebAssembly/wasi-$repo/archive/$tag.tar.gz | \
tar xzf - --strip-components=1 -C $cached_extracted_dir
rm -rf $cached_extracted_dir/wit/deps*
rm -rf $cached_extracted_dir/${subdir:-"wit"}/deps*
fi

cp -r $cached_extracted_dir/wit/* $path/$repo
cp -r $cached_extracted_dir/${subdir:-"wit"}/* $path/$repo
done
}

Expand Down Expand Up @@ -68,6 +68,14 @@ make_vendor "wasi-config" "config@f4d699b"

make_vendor "wasi-keyvalue" "keyvalue@219ea36"

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

rm -rf $cache_dir

# Separately (for now), vendor the `wasi-nn` WIT files since their retrieval is
Expand Down
1 change: 1 addition & 0 deletions crates/test-programs/artifacts/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ impl Artifacts {
}
let adapter = match test.name.as_str() {
"reactor" => &reactor_adapter,
s if s.starts_with("p3_") => &reactor_adapter,
s if s.starts_with("api_proxy") => &proxy_adapter,
_ => &command_adapter,
};
Expand Down
51 changes: 51 additions & 0 deletions crates/test-programs/src/bin/p3_clocks_sleep.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use core::future::Future as _;
use core::pin::pin;
use core::task::{Context, Poll, Waker};

use test_programs::p3::wasi::clocks::monotonic_clock;

struct Component;

test_programs::p3::export!(Component);

impl test_programs::p3::exports::wasi::cli::run::Guest for Component {
async fn run() -> Result<(), ()> {
sleep_10ms().await;
sleep_0ms();
sleep_backwards_in_time();
Ok(())
}
}

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(Waker::noop());

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(Waker::noop());

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",
);
}

fn main() {}
44 changes: 44 additions & 0 deletions crates/test-programs/src/bin/p3_random_imports.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
use test_programs::p3::wasi::random;

struct Component;

test_programs::p3::export!(Component);

impl test_programs::p3::exports::wasi::cli::run::Guest for Component {
async fn run() -> Result<(), ()> {
// Acquired random bytes should be of the expected length.
let array = random::random::get_random_bytes(100);
assert_eq!(array.len(), 100);

// It shouldn't take 100+ tries to get a nonzero random integer.
for i in 0.. {
if random::random::get_random_u64() == 0 {
continue;
}
assert!(i < 100);
break;
}

// The `insecure_seed` API should return the same result each time.
let (a1, b1) = random::insecure_seed::insecure_seed();
let (a2, b2) = random::insecure_seed::insecure_seed();
assert_eq!(a1, a2);
assert_eq!(b1, b2);

// Acquired random bytes should be of the expected length.
let array = random::insecure::get_insecure_random_bytes(100);
assert_eq!(array.len(), 100);

// It shouldn't take 100+ tries to get a nonzero random integer.
for i in 0.. {
if random::insecure::get_insecure_random_u64() == 0 {
continue;
}
assert!(i < 100);
break;
}
Ok(())
}
}

fn main() {}
1 change: 1 addition & 0 deletions crates/test-programs/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
pub mod async_;
pub mod http;
pub mod nn;
pub mod p3;
pub mod preview1;
pub mod sockets;
pub mod tls;
Expand Down
19 changes: 19 additions & 0 deletions crates/test-programs/src/p3/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
wit_bindgen::generate!({
inline: "
package wasmtime:test;

world testp3 {
include wasi:cli/[email protected];

export wasi:cli/[email protected];
}
",
path: "../wasi/src/p3/wit",
world: "wasmtime:test/testp3",
default_bindings_module: "test_programs::p3",
pub_export_macro: true,
async: [
"wasi:cli/[email protected]#run",
],
generate_all
});
5 changes: 4 additions & 1 deletion crates/wasi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,13 @@ windows-sys = { workspace = true }
rustix = { workspace = true, features = ["event", "net"] }

[features]
default = [ "preview1"]
default = ["preview1"]
preview1 = [
"dep:wiggle",
]
p3 = [
"wasmtime/component-model-async",
]

[[test]]
name = "process_stdin"
Expand Down
125 changes: 123 additions & 2 deletions crates/wasi/src/clocks.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,45 @@
pub mod host;
use cap_std::time::Duration;
use cap_std::time::{Duration, Instant, SystemClock};
use cap_std::{AmbientAuthority, ambient_authority};
use cap_time_ext::{MonotonicClockExt as _, SystemClockExt as _};

#[repr(transparent)]
pub struct WasiClocksImpl<T>(pub T);

impl<T: WasiClocksView> WasiClocksView for &mut T {
fn clocks(&mut self) -> &WasiClocksCtx {
(**self).clocks()
}
}

impl<T: WasiClocksView> WasiClocksView for WasiClocksImpl<T> {
fn clocks(&mut self) -> &WasiClocksCtx {
self.0.clocks()
}
}

impl WasiClocksView for WasiClocksCtx {
fn clocks(&mut self) -> &WasiClocksCtx {
self
}
}

pub trait WasiClocksView: Send {
fn clocks(&mut self) -> &WasiClocksCtx;
}

pub struct WasiClocksCtx {
pub wall_clock: Box<dyn HostWallClock + Send>,
pub monotonic_clock: Box<dyn HostMonotonicClock + Send>,
}

impl Default for WasiClocksCtx {
fn default() -> Self {
Self {
wall_clock: wall_clock(),
monotonic_clock: monotonic_clock(),
}
}
}

pub trait HostWallClock: Send {
fn resolution(&self) -> Duration;
Expand All @@ -10,3 +50,84 @@ pub trait HostMonotonicClock: Send {
fn resolution(&self) -> u64;
fn now(&self) -> u64;
}

pub struct WallClock {
/// The underlying system clock.
clock: cap_std::time::SystemClock,
}

impl Default for WallClock {
fn default() -> Self {
Self::new(ambient_authority())
}
}

impl WallClock {
pub fn new(ambient_authority: AmbientAuthority) -> Self {
Self {
clock: cap_std::time::SystemClock::new(ambient_authority),
}
}
}

impl HostWallClock for WallClock {
fn resolution(&self) -> Duration {
self.clock.resolution()
}

fn now(&self) -> Duration {
// WASI defines wall clocks to return "Unix time".
self.clock
.now()
.duration_since(SystemClock::UNIX_EPOCH)
.unwrap()
}
}

pub struct MonotonicClock {
/// The underlying system clock.
clock: cap_std::time::MonotonicClock,

/// The `Instant` this clock was created. All returned times are
/// durations since that time.
initial: Instant,
}

impl Default for MonotonicClock {
fn default() -> Self {
Self::new(ambient_authority())
}
}

impl MonotonicClock {
pub fn new(ambient_authority: AmbientAuthority) -> Self {
let clock = cap_std::time::MonotonicClock::new(ambient_authority);
let initial = clock.now();
Self { clock, initial }
}
}

impl HostMonotonicClock for MonotonicClock {
fn resolution(&self) -> u64 {
self.clock.resolution().as_nanos().try_into().unwrap()
}

fn now(&self) -> u64 {
// Unwrap here and in `resolution` above; a `u64` is wide enough to
// hold over 584 years of nanoseconds.
self.clock
.now()
.duration_since(self.initial)
.as_nanos()
.try_into()
.unwrap()
}
}

pub fn monotonic_clock() -> Box<dyn HostMonotonicClock + Send> {
Box::new(MonotonicClock::default())
}

pub fn wall_clock() -> Box<dyn HostWallClock + Send> {
Box::new(WallClock::default())
}
Loading