Merge pull request #2832 from bytecodealliance/pch/wiggle_sync_shimming
wasi-common support for tokio, & wiggle support for async methods containing sync code
This commit is contained in:
2
.github/workflows/main.yml
vendored
2
.github/workflows/main.yml
vendored
@@ -208,6 +208,7 @@ jobs:
|
|||||||
name: Test
|
name: Test
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
strategy:
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
build: [stable, beta, nightly, windows, macos]
|
build: [stable, beta, nightly, windows, macos]
|
||||||
include:
|
include:
|
||||||
@@ -382,6 +383,7 @@ jobs:
|
|||||||
name: Build wasmtime
|
name: Build wasmtime
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
strategy:
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- build: x86_64-linux
|
- build: x86_64-linux
|
||||||
|
|||||||
121
Cargo.lock
generated
121
Cargo.lock
generated
@@ -261,6 +261,12 @@ version = "1.4.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b"
|
checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bytes"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cap-fs-ext"
|
name = "cap-fs-ext"
|
||||||
version = "0.13.9"
|
version = "0.13.9"
|
||||||
@@ -315,6 +321,17 @@ dependencies = [
|
|||||||
"unsafe-io",
|
"unsafe-io",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cap-tempfile"
|
||||||
|
version = "0.13.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8d2f6f45ddb06ff26f4cf2ba9838d5826d52e1a5f6b321d71f114bb38cf34a57"
|
||||||
|
dependencies = [
|
||||||
|
"cap-std",
|
||||||
|
"rand 0.8.3",
|
||||||
|
"uuid",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cap-time-ext"
|
name = "cap-time-ext"
|
||||||
version = "0.13.9"
|
version = "0.13.9"
|
||||||
@@ -1073,6 +1090,10 @@ dependencies = [
|
|||||||
name = "example-fib-debug-wasm"
|
name = "example-fib-debug-wasm"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "example-tokio-wasm"
|
||||||
|
version = "0.0.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "example-wasi-wasm"
|
name = "example-wasi-wasm"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
@@ -1551,6 +1572,28 @@ dependencies = [
|
|||||||
"autocfg 1.0.1",
|
"autocfg 1.0.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mio"
|
||||||
|
version = "0.7.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cf80d3e903b34e0bd7282b218398aec54e082c840d9baf8339e0080a0c542956"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"miow",
|
||||||
|
"ntapi",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "miow"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "more-asserts"
|
name = "more-asserts"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
@@ -1580,6 +1623,15 @@ dependencies = [
|
|||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ntapi"
|
||||||
|
version = "0.3.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-bigint"
|
name = "num-bigint"
|
||||||
version = "0.2.6"
|
version = "0.2.6"
|
||||||
@@ -2732,6 +2784,7 @@ dependencies = [
|
|||||||
"pretty_env_logger",
|
"pretty_env_logger",
|
||||||
"target-lexicon",
|
"target-lexicon",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
|
"tokio",
|
||||||
"wasi-cap-std-sync",
|
"wasi-cap-std-sync",
|
||||||
"wasi-common",
|
"wasi-common",
|
||||||
"wasmtime",
|
"wasmtime",
|
||||||
@@ -2787,6 +2840,33 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "83f0c8e7c0addab50b663055baf787d0af7f413a46e6e7fb9559a4e4db7137a5"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg 1.0.1",
|
||||||
|
"bytes",
|
||||||
|
"libc",
|
||||||
|
"memchr",
|
||||||
|
"mio",
|
||||||
|
"num_cpus",
|
||||||
|
"pin-project-lite",
|
||||||
|
"tokio-macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-macros"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "caf7b11a536f46a809a8a9f0bb4237020f70ecbf115b842360afb127ea2fda57"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml"
|
name = "toml"
|
||||||
version = "0.5.8"
|
version = "0.5.8"
|
||||||
@@ -2938,9 +3018,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unsafe-io"
|
name = "unsafe-io"
|
||||||
version = "0.6.2"
|
version = "0.6.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0301dd0f2c21baed606faa2717fbfbb1a68b7e289ea29b40bc21a16f5ae9f5aa"
|
checksum = "fe39acfe60d3754452ea6881613c3240100b23ffd94a627c138863f8cd314b1b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rustc_version",
|
"rustc_version",
|
||||||
"winapi",
|
"winapi",
|
||||||
@@ -2970,6 +3050,15 @@ dependencies = [
|
|||||||
"cfg-if 0.1.10",
|
"cfg-if 0.1.10",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "uuid"
|
||||||
|
version = "0.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom 0.2.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vec_map"
|
name = "vec_map"
|
||||||
version = "0.8.2"
|
version = "0.8.2"
|
||||||
@@ -3025,6 +3114,7 @@ name = "wasi-cap-std-sync"
|
|||||||
version = "0.26.0"
|
version = "0.26.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"async-trait",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"cap-fs-ext",
|
"cap-fs-ext",
|
||||||
"cap-rand",
|
"cap-rand",
|
||||||
@@ -3085,6 +3175,31 @@ dependencies = [
|
|||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi-tokio"
|
||||||
|
version = "0.26.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"bitflags",
|
||||||
|
"cap-fs-ext",
|
||||||
|
"cap-std",
|
||||||
|
"cap-tempfile",
|
||||||
|
"cap-time-ext",
|
||||||
|
"fs-set-times",
|
||||||
|
"lazy_static",
|
||||||
|
"libc",
|
||||||
|
"posish",
|
||||||
|
"system-interface",
|
||||||
|
"tempfile",
|
||||||
|
"tokio",
|
||||||
|
"tracing",
|
||||||
|
"unsafe-io",
|
||||||
|
"wasi-cap-std-sync",
|
||||||
|
"wasi-common",
|
||||||
|
"wiggle",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-encoder"
|
name = "wasm-encoder"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
@@ -3263,6 +3378,7 @@ dependencies = [
|
|||||||
"target-lexicon",
|
"target-lexicon",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"test-programs",
|
"test-programs",
|
||||||
|
"tokio",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
"wasmparser",
|
"wasmparser",
|
||||||
"wasmtime",
|
"wasmtime",
|
||||||
@@ -3491,6 +3607,7 @@ dependencies = [
|
|||||||
"anyhow",
|
"anyhow",
|
||||||
"wasi-cap-std-sync",
|
"wasi-cap-std-sync",
|
||||||
"wasi-common",
|
"wasi-common",
|
||||||
|
"wasi-tokio",
|
||||||
"wasmtime",
|
"wasmtime",
|
||||||
"wasmtime-wiggle",
|
"wasmtime-wiggle",
|
||||||
"wiggle",
|
"wiggle",
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ tempfile = "3.1.0"
|
|||||||
test-programs = { path = "crates/test-programs" }
|
test-programs = { path = "crates/test-programs" }
|
||||||
wasmtime-fuzzing = { path = "crates/fuzzing" }
|
wasmtime-fuzzing = { path = "crates/fuzzing" }
|
||||||
wasmtime-runtime = { path = "crates/runtime" }
|
wasmtime-runtime = { path = "crates/runtime" }
|
||||||
|
tokio = { version = "1.5.0", features = ["rt", "time", "macros", "rt-multi-thread"] }
|
||||||
tracing-subscriber = "0.2.16"
|
tracing-subscriber = "0.2.16"
|
||||||
wast = "35.0.0"
|
wast = "35.0.0"
|
||||||
|
|
||||||
@@ -78,8 +79,10 @@ members = [
|
|||||||
"crates/wiggle/wasmtime",
|
"crates/wiggle/wasmtime",
|
||||||
"crates/wasi-common",
|
"crates/wasi-common",
|
||||||
"crates/wasi-common/cap-std-sync",
|
"crates/wasi-common/cap-std-sync",
|
||||||
|
"crates/wasi-common/tokio",
|
||||||
"examples/fib-debug/wasm",
|
"examples/fib-debug/wasm",
|
||||||
"examples/wasi/wasm",
|
"examples/wasi/wasm",
|
||||||
|
"examples/tokio/wasm",
|
||||||
"fuzz",
|
"fuzz",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -107,5 +110,9 @@ maintenance = { status = "actively-developed" }
|
|||||||
name = "host_segfault"
|
name = "host_segfault"
|
||||||
harness = false
|
harness = false
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "tokio"
|
||||||
|
required-features = ["wasmtime-wasi/tokio"]
|
||||||
|
|
||||||
[profile.dev.package.backtrace]
|
[profile.dev.package.backtrace]
|
||||||
debug = false # FIXME(#1813)
|
debug = false # FIXME(#1813)
|
||||||
|
|||||||
@@ -83,9 +83,8 @@ use std::env;
|
|||||||
use std::os::raw::{c_int, c_void};
|
use std::os::raw::{c_int, c_void};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::slice;
|
use std::slice;
|
||||||
use wasi_cap_std_sync::WasiCtxBuilder;
|
|
||||||
use wasmtime::{Config, Engine, Instance, Linker, Module, Store};
|
use wasmtime::{Config, Engine, Instance, Linker, Module, Store};
|
||||||
use wasmtime_wasi::Wasi;
|
use wasmtime_wasi::sync::{Wasi, WasiCtxBuilder};
|
||||||
|
|
||||||
pub type ExitCode = c_int;
|
pub type ExitCode = c_int;
|
||||||
pub const OK: ExitCode = 0;
|
pub const OK: ExitCode = 0;
|
||||||
|
|||||||
@@ -12,9 +12,10 @@ use std::slice;
|
|||||||
use std::str;
|
use std::str;
|
||||||
use wasmtime::{Extern, Linker, Trap};
|
use wasmtime::{Extern, Linker, Trap};
|
||||||
use wasmtime_wasi::{
|
use wasmtime_wasi::{
|
||||||
snapshots::preview_0::Wasi as WasiSnapshot0,
|
sync::{
|
||||||
snapshots::preview_1::Wasi as WasiPreview1,
|
snapshots::preview_0::Wasi as WasiSnapshot0, snapshots::preview_1::Wasi as WasiPreview1,
|
||||||
sync::{Dir, WasiCtxBuilder},
|
Dir, WasiCtxBuilder,
|
||||||
|
},
|
||||||
WasiCtx,
|
WasiCtx,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -45,10 +45,13 @@ fn main() -> anyhow::Result<()> {
|
|||||||
.arg(target))?;
|
.arg(target))?;
|
||||||
}
|
}
|
||||||
println!("======== Rust example `{}` ============", example);
|
println!("======== Rust example `{}` ============", example);
|
||||||
run(Command::new("cargo")
|
let mut cargo_cmd = Command::new("cargo");
|
||||||
.arg("run")
|
cargo_cmd.arg("run").arg("--example").arg(&example);
|
||||||
.arg("--example")
|
|
||||||
.arg(&example))?;
|
if example.contains("tokio") {
|
||||||
|
cargo_cmd.arg("--features").arg("wasmtime-wasi/tokio");
|
||||||
|
}
|
||||||
|
run(&mut cargo_cmd)?;
|
||||||
|
|
||||||
println!("======== C/C++ example `{}` ============", example);
|
println!("======== C/C++ example `{}` ============", example);
|
||||||
for extension in ["c", "cc"].iter() {
|
for extension in ["c", "cc"].iter() {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ cfg-if = "1.0"
|
|||||||
wasi-common = { path = "../wasi-common", version = "0.26.0" }
|
wasi-common = { path = "../wasi-common", version = "0.26.0" }
|
||||||
wasi-cap-std-sync = { path = "../wasi-common/cap-std-sync", version = "0.26.0" }
|
wasi-cap-std-sync = { path = "../wasi-common/cap-std-sync", version = "0.26.0" }
|
||||||
wasmtime = { path = "../wasmtime", version = "0.26.0" }
|
wasmtime = { path = "../wasmtime", version = "0.26.0" }
|
||||||
wasmtime-wasi = { path = "../wasi", version = "0.26.0" }
|
wasmtime-wasi = { path = "../wasi", version = "0.26.0", features = ["tokio"] }
|
||||||
target-lexicon = "0.12.0"
|
target-lexicon = "0.12.0"
|
||||||
pretty_env_logger = "0.4.0"
|
pretty_env_logger = "0.4.0"
|
||||||
tempfile = "3.1.0"
|
tempfile = "3.1.0"
|
||||||
@@ -22,6 +22,7 @@ os_pipe = "0.9"
|
|||||||
anyhow = "1.0.19"
|
anyhow = "1.0.19"
|
||||||
wat = "1.0.37"
|
wat = "1.0.37"
|
||||||
cap-std = "0.13"
|
cap-std = "0.13"
|
||||||
|
tokio = { version = "1.5.0", features = ["rt-multi-thread"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
test_programs = []
|
test_programs = []
|
||||||
|
|||||||
@@ -40,7 +40,9 @@ mod wasi_tests {
|
|||||||
File::create(out_dir.join("wasi_tests.rs")).expect("error generating test source file");
|
File::create(out_dir.join("wasi_tests.rs")).expect("error generating test source file");
|
||||||
build_tests("wasi-tests", &out_dir).expect("building tests");
|
build_tests("wasi-tests", &out_dir).expect("building tests");
|
||||||
test_directory(&mut out, "wasi-cap-std-sync", "cap_std_sync", &out_dir)
|
test_directory(&mut out, "wasi-cap-std-sync", "cap_std_sync", &out_dir)
|
||||||
.expect("generating tests");
|
.expect("generating wasi-cap-std-sync tests");
|
||||||
|
test_directory(&mut out, "wasi-tokio", "tokio", &out_dir)
|
||||||
|
.expect("generating wasi-tokio tests");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_tests(testsuite: &str, out_dir: &Path) -> io::Result<()> {
|
fn build_tests(testsuite: &str, out_dir: &Path) -> io::Result<()> {
|
||||||
@@ -173,6 +175,7 @@ mod wasi_tests {
|
|||||||
match testsuite {
|
match testsuite {
|
||||||
"wasi-cap-std-sync" => cap_std_sync_ignore(name),
|
"wasi-cap-std-sync" => cap_std_sync_ignore(name),
|
||||||
"wasi-virtfs" => virtfs_ignore(name),
|
"wasi-virtfs" => virtfs_ignore(name),
|
||||||
|
"wasi-tokio" => tokio_ignore(name),
|
||||||
_ => panic!("unknown test suite: {}", testsuite),
|
_ => panic!("unknown test suite: {}", testsuite),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -200,6 +203,10 @@ mod wasi_tests {
|
|||||||
.contains(&name)
|
.contains(&name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tokio should support the same things as cap_std_sync
|
||||||
|
fn tokio_ignore(name: &str) -> bool {
|
||||||
|
cap_std_sync_ignore(name)
|
||||||
|
}
|
||||||
/// Virtfs barely works at all and is not suitable for any purpose
|
/// Virtfs barely works at all and is not suitable for any purpose
|
||||||
fn virtfs_ignore(name: &str) -> bool {
|
fn virtfs_ignore(name: &str) -> bool {
|
||||||
[
|
[
|
||||||
@@ -260,7 +267,7 @@ mod wasi_tests {
|
|||||||
/// Mark tests which require inheriting parent process stdio
|
/// Mark tests which require inheriting parent process stdio
|
||||||
fn inherit_stdio(testsuite: &str, name: &str) -> bool {
|
fn inherit_stdio(testsuite: &str, name: &str) -> bool {
|
||||||
match testsuite {
|
match testsuite {
|
||||||
"wasi-cap-std-sync" => match name {
|
"wasi-cap-std-sync" | "wasi-tokio" => match name {
|
||||||
"poll_oneoff_stdio" => true,
|
"poll_oneoff_stdio" => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,10 +1,26 @@
|
|||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use wasi_cap_std_sync::WasiCtxBuilder;
|
|
||||||
use wasi_common::pipe::WritePipe;
|
use wasi_common::pipe::WritePipe;
|
||||||
use wasmtime::{Linker, Module, Store};
|
use wasmtime::{Linker, Module, Store};
|
||||||
|
use wasmtime_wasi::sync::{Wasi, WasiCtxBuilder};
|
||||||
|
|
||||||
pub fn instantiate(data: &[u8], bin_name: &str, workspace: Option<&Path>) -> anyhow::Result<()> {
|
pub fn instantiate(data: &[u8], bin_name: &str, workspace: Option<&Path>) -> anyhow::Result<()> {
|
||||||
|
run(data, bin_name, workspace, false)
|
||||||
|
}
|
||||||
|
pub fn instantiate_inherit_stdio(
|
||||||
|
data: &[u8],
|
||||||
|
bin_name: &str,
|
||||||
|
workspace: Option<&Path>,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
run(data, bin_name, workspace, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
data: &[u8],
|
||||||
|
bin_name: &str,
|
||||||
|
workspace: Option<&Path>,
|
||||||
|
inherit_stdio: bool,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
let stdout = WritePipe::new_in_memory();
|
let stdout = WritePipe::new_in_memory();
|
||||||
let stderr = WritePipe::new_in_memory();
|
let stderr = WritePipe::new_in_memory();
|
||||||
|
|
||||||
@@ -15,41 +31,29 @@ pub fn instantiate(data: &[u8], bin_name: &str, workspace: Option<&Path>) -> any
|
|||||||
// Additionally register any preopened directories if we have them.
|
// Additionally register any preopened directories if we have them.
|
||||||
let mut builder = WasiCtxBuilder::new();
|
let mut builder = WasiCtxBuilder::new();
|
||||||
|
|
||||||
|
if inherit_stdio {
|
||||||
|
builder = builder.inherit_stdio();
|
||||||
|
} else {
|
||||||
builder = builder
|
builder = builder
|
||||||
.arg(bin_name)?
|
|
||||||
.arg(".")?
|
|
||||||
.stdout(Box::new(stdout.clone()))
|
.stdout(Box::new(stdout.clone()))
|
||||||
.stderr(Box::new(stderr.clone()));
|
.stderr(Box::new(stderr.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
builder = builder.arg(bin_name)?.arg(".")?;
|
||||||
|
|
||||||
if let Some(workspace) = workspace {
|
if let Some(workspace) = workspace {
|
||||||
println!("preopen: {:?}", workspace);
|
println!("preopen: {:?}", workspace);
|
||||||
let preopen_dir = unsafe { cap_std::fs::Dir::open_ambient_dir(workspace) }?;
|
let preopen_dir = unsafe { cap_std::fs::Dir::open_ambient_dir(workspace) }?;
|
||||||
builder = builder.preopened_dir(preopen_dir, ".")?;
|
builder = builder.preopened_dir(preopen_dir, ".")?;
|
||||||
}
|
}
|
||||||
|
for (var, val) in super::test_suite_environment() {
|
||||||
#[cfg(windows)]
|
builder = builder.env(var, val)?;
|
||||||
{
|
|
||||||
builder = builder
|
|
||||||
.env("ERRNO_MODE_WINDOWS", "1")?
|
|
||||||
.env("NO_DANGLING_FILESYSTEM", "1")?
|
|
||||||
.env("NO_FD_ALLOCATE", "1")?
|
|
||||||
.env("NO_RENAME_DIR_TO_EMPTY_DIR", "1")?
|
|
||||||
}
|
|
||||||
#[cfg(all(unix, not(target_os = "macos")))]
|
|
||||||
{
|
|
||||||
builder = builder.env("ERRNO_MODE_UNIX", "1")?;
|
|
||||||
}
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
{
|
|
||||||
builder = builder
|
|
||||||
.env("ERRNO_MODE_MACOS", "1")?
|
|
||||||
.env("NO_FD_ALLOCATE", "1")?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// cap-std-sync does not yet support the sync family of fdflags
|
// cap-std-sync does not yet support the sync family of fdflags
|
||||||
builder = builder.env("NO_FDFLAGS_SYNC_SUPPORT", "1")?;
|
builder = builder.env("NO_FDFLAGS_SYNC_SUPPORT", "1")?;
|
||||||
|
|
||||||
let wasi = wasmtime_wasi::Wasi::new(&store, builder.build()?);
|
let wasi = Wasi::new(&store, builder.build()?);
|
||||||
|
|
||||||
let mut linker = Linker::new(&store);
|
let mut linker = Linker::new(&store);
|
||||||
|
|
||||||
@@ -82,41 +86,3 @@ pub fn instantiate(data: &[u8], bin_name: &str, workspace: Option<&Path>) -> any
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn instantiate_inherit_stdio(
|
|
||||||
data: &[u8],
|
|
||||||
bin_name: &str,
|
|
||||||
workspace: Option<&Path>,
|
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
let r = {
|
|
||||||
let store = Store::default();
|
|
||||||
|
|
||||||
// Create our wasi context.
|
|
||||||
// Additionally register any preopened directories if we have them.
|
|
||||||
let mut builder = WasiCtxBuilder::new();
|
|
||||||
|
|
||||||
builder = builder.arg(bin_name)?.arg(".")?.inherit_stdio();
|
|
||||||
|
|
||||||
if let Some(workspace) = workspace {
|
|
||||||
println!("preopen: {:?}", workspace);
|
|
||||||
let preopen_dir = unsafe { cap_std::fs::Dir::open_ambient_dir(workspace) }?;
|
|
||||||
builder = builder.preopened_dir(preopen_dir, ".")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let snapshot1 = wasmtime_wasi::Wasi::new(&store, builder.build()?);
|
|
||||||
|
|
||||||
let mut linker = Linker::new(&store);
|
|
||||||
|
|
||||||
snapshot1.add_to_linker(&mut linker)?;
|
|
||||||
|
|
||||||
let module = Module::new(store.engine(), &data).context("failed to create wasm module")?;
|
|
||||||
let instance = linker.instantiate(&module)?;
|
|
||||||
let start = instance.get_typed_func::<(), ()>("_start")?;
|
|
||||||
start.call(()).map_err(anyhow::Error::from)
|
|
||||||
};
|
|
||||||
|
|
||||||
match r {
|
|
||||||
Ok(()) => Ok(()),
|
|
||||||
Err(trap) => Err(trap.context(format!("error while testing Wasm module '{}'", bin_name,))),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1 +1,34 @@
|
|||||||
pub mod cap_std_sync;
|
pub mod cap_std_sync;
|
||||||
|
pub mod tokio;
|
||||||
|
|
||||||
|
// Configure the test suite environment.
|
||||||
|
// Test programs use these environment variables to determine what behavior
|
||||||
|
// is expected: different errnos are expected on windows, mac, and other unixes,
|
||||||
|
// and other filesystem operations are supported or not.
|
||||||
|
pub fn test_suite_environment() -> &'static [(&'static str, &'static str)] {
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
&[
|
||||||
|
("ERRNO_MODE_WINDOWS", "1"),
|
||||||
|
// Windows does not support dangling links or symlinks in the filesystem.
|
||||||
|
("NO_DANGLING_FILESYSTEM", "1"),
|
||||||
|
// Windows does not support fd_allocate.
|
||||||
|
("NO_FD_ALLOCATE", "1"),
|
||||||
|
// Windows does not support renaming a directory to an empty directory -
|
||||||
|
// empty directory must be deleted.
|
||||||
|
("NO_RENAME_DIR_TO_EMPTY_DIR", "1"),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
#[cfg(all(unix, not(target_os = "macos")))]
|
||||||
|
{
|
||||||
|
&[("ERRNO_MODE_UNIX", "1")]
|
||||||
|
}
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
{
|
||||||
|
&[
|
||||||
|
("ERRNO_MODE_MACOS", "1"),
|
||||||
|
// MacOS does not support fd_allocate
|
||||||
|
("NO_FD_ALLOCATE", "1"),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
96
crates/test-programs/tests/wasm_tests/runtime/tokio.rs
Normal file
96
crates/test-programs/tests/wasm_tests/runtime/tokio.rs
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
use anyhow::Context;
|
||||||
|
use std::path::Path;
|
||||||
|
use wasi_common::pipe::WritePipe;
|
||||||
|
use wasmtime::{Config, Engine, Linker, Module, Store};
|
||||||
|
use wasmtime_wasi::tokio::{Wasi, WasiCtxBuilder};
|
||||||
|
|
||||||
|
pub fn instantiate(data: &[u8], bin_name: &str, workspace: Option<&Path>) -> anyhow::Result<()> {
|
||||||
|
run(data, bin_name, workspace, false)
|
||||||
|
}
|
||||||
|
pub fn instantiate_inherit_stdio(
|
||||||
|
data: &[u8],
|
||||||
|
bin_name: &str,
|
||||||
|
workspace: Option<&Path>,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
run(data, bin_name, workspace, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
data: &[u8],
|
||||||
|
bin_name: &str,
|
||||||
|
workspace: Option<&Path>,
|
||||||
|
inherit_stdio: bool,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
let stdout = WritePipe::new_in_memory();
|
||||||
|
let stdout_ = stdout.clone();
|
||||||
|
let stderr = WritePipe::new_in_memory();
|
||||||
|
let stderr_ = stderr.clone();
|
||||||
|
|
||||||
|
let r = tokio::runtime::Runtime::new()
|
||||||
|
.expect("create runtime")
|
||||||
|
.block_on(async move {
|
||||||
|
let mut config = Config::new();
|
||||||
|
config.async_support(true);
|
||||||
|
Wasi::add_to_config(&mut config);
|
||||||
|
let engine = Engine::new(&config)?;
|
||||||
|
let store = Store::new(&engine);
|
||||||
|
|
||||||
|
// Create our wasi context.
|
||||||
|
let mut builder = WasiCtxBuilder::new();
|
||||||
|
|
||||||
|
if inherit_stdio {
|
||||||
|
builder = builder.inherit_stdio();
|
||||||
|
} else {
|
||||||
|
builder = builder
|
||||||
|
.stdout(Box::new(stdout_.clone()))
|
||||||
|
.stderr(Box::new(stderr_.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
builder = builder.arg(bin_name)?.arg(".")?;
|
||||||
|
|
||||||
|
if let Some(workspace) = workspace {
|
||||||
|
println!("preopen: {:?}", workspace);
|
||||||
|
let preopen_dir = unsafe { cap_std::fs::Dir::open_ambient_dir(workspace) }?;
|
||||||
|
builder = builder.preopened_dir(preopen_dir, ".")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var, val) in super::test_suite_environment() {
|
||||||
|
builder = builder.env(var, val)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// tokio does not yet support the sync family of fdflags, because cap-std-sync
|
||||||
|
// does not.
|
||||||
|
builder = builder.env("NO_FDFLAGS_SYNC_SUPPORT", "1")?;
|
||||||
|
|
||||||
|
Wasi::set_context(&store, builder.build()?)
|
||||||
|
.map_err(|_| anyhow::anyhow!("wasi set_context failed"))?;
|
||||||
|
|
||||||
|
let module =
|
||||||
|
Module::new(store.engine(), &data).context("failed to create wasm module")?;
|
||||||
|
let linker = Linker::new(&store);
|
||||||
|
let instance = linker.instantiate_async(&module).await?;
|
||||||
|
let start = instance.get_typed_func::<(), ()>("_start")?;
|
||||||
|
start.call_async(()).await.map_err(anyhow::Error::from)
|
||||||
|
});
|
||||||
|
|
||||||
|
match r {
|
||||||
|
Ok(()) => Ok(()),
|
||||||
|
Err(trap) => {
|
||||||
|
let stdout = stdout
|
||||||
|
.try_into_inner()
|
||||||
|
.expect("sole ref to stdout")
|
||||||
|
.into_inner();
|
||||||
|
if !stdout.is_empty() {
|
||||||
|
println!("guest stdout:\n{}\n===", String::from_utf8_lossy(&stdout));
|
||||||
|
}
|
||||||
|
let stderr = stderr
|
||||||
|
.try_into_inner()
|
||||||
|
.expect("sole ref to stderr")
|
||||||
|
.into_inner();
|
||||||
|
if !stderr.is_empty() {
|
||||||
|
println!("guest stderr:\n{}\n===", String::from_utf8_lossy(&stderr));
|
||||||
|
}
|
||||||
|
Err(trap.context(format!("error while testing Wasm module '{}'", bin_name,)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -55,7 +55,10 @@ 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");
|
assert!(
|
||||||
|
after - before >= timeout,
|
||||||
|
"poll_oneoff should sleep for the specified interval"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Like test_timeout, but uses `CLOCKID_REALTIME`, as WASI libc's sleep
|
// Like test_timeout, but uses `CLOCKID_REALTIME`, as WASI libc's sleep
|
||||||
@@ -90,20 +93,22 @@ unsafe fn test_sleep() {
|
|||||||
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");
|
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(readable_fd: wasi::Fd, writable_fd: wasi::Fd, error_code: wasi::Errno) {
|
||||||
let fd_readwrite = wasi::SubscriptionFdReadwrite {
|
|
||||||
file_descriptor: fd,
|
|
||||||
};
|
|
||||||
let r#in = [
|
let r#in = [
|
||||||
wasi::Subscription {
|
wasi::Subscription {
|
||||||
userdata: 1,
|
userdata: 1,
|
||||||
u: wasi::SubscriptionU {
|
u: wasi::SubscriptionU {
|
||||||
tag: wasi::EVENTTYPE_FD_READ,
|
tag: wasi::EVENTTYPE_FD_READ,
|
||||||
u: wasi::SubscriptionUU {
|
u: wasi::SubscriptionUU {
|
||||||
fd_read: fd_readwrite,
|
fd_read: wasi::SubscriptionFdReadwrite {
|
||||||
|
file_descriptor: readable_fd,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -112,13 +117,15 @@ unsafe fn test_fd_readwrite(fd: wasi::Fd, error_code: wasi::Errno) {
|
|||||||
u: wasi::SubscriptionU {
|
u: wasi::SubscriptionU {
|
||||||
tag: wasi::EVENTTYPE_FD_WRITE,
|
tag: wasi::EVENTTYPE_FD_WRITE,
|
||||||
u: wasi::SubscriptionUU {
|
u: wasi::SubscriptionUU {
|
||||||
fd_write: fd_readwrite,
|
fd_write: wasi::SubscriptionFdReadwrite {
|
||||||
|
file_descriptor: writable_fd,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
let out = poll_oneoff_impl(&r#in).unwrap();
|
let out = poll_oneoff_impl(&r#in).unwrap();
|
||||||
assert_eq!(out.len(), 2, "should return 2 events");
|
assert_eq!(out.len(), 2, "should return 2 events, got: {:?}", out);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
out[0].userdata, 1,
|
out[0].userdata, 1,
|
||||||
"the event.userdata should contain fd userdata specified by the user"
|
"the event.userdata should contain fd userdata specified by the user"
|
||||||
@@ -143,26 +150,64 @@ unsafe fn test_fd_readwrite(fd: wasi::Fd, error_code: wasi::Errno) {
|
|||||||
|
|
||||||
unsafe fn test_fd_readwrite_valid_fd(dir_fd: wasi::Fd) {
|
unsafe fn test_fd_readwrite_valid_fd(dir_fd: wasi::Fd) {
|
||||||
// Create a file in the scratch directory.
|
// Create a file in the scratch directory.
|
||||||
let file_fd = wasi::path_open(
|
let nonempty_file = wasi::path_open(
|
||||||
dir_fd,
|
dir_fd,
|
||||||
0,
|
0,
|
||||||
"file",
|
"readable_file",
|
||||||
wasi::OFLAGS_CREAT,
|
wasi::OFLAGS_CREAT,
|
||||||
wasi::RIGHTS_FD_READ | wasi::RIGHTS_FD_WRITE | wasi::RIGHTS_POLL_FD_READWRITE,
|
wasi::RIGHTS_FD_WRITE,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
)
|
)
|
||||||
.expect("opening a file");
|
.expect("create writable file");
|
||||||
|
// Write to file
|
||||||
|
let contents = &[1u8];
|
||||||
|
let ciovec = wasi::Ciovec {
|
||||||
|
buf: contents.as_ptr() as *const _,
|
||||||
|
buf_len: contents.len(),
|
||||||
|
};
|
||||||
|
wasi::fd_write(nonempty_file, &[ciovec]).expect("write");
|
||||||
|
wasi::fd_close(nonempty_file).expect("close");
|
||||||
|
|
||||||
|
// Now open the file for reading
|
||||||
|
let readable_fd = wasi::path_open(
|
||||||
|
dir_fd,
|
||||||
|
0,
|
||||||
|
"readable_file",
|
||||||
|
0,
|
||||||
|
wasi::RIGHTS_FD_READ | wasi::RIGHTS_POLL_FD_READWRITE,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
.expect("opening a readable file");
|
||||||
|
|
||||||
assert_gt!(
|
assert_gt!(
|
||||||
file_fd,
|
readable_fd,
|
||||||
|
libc::STDERR_FILENO as wasi::Fd,
|
||||||
|
"file descriptor range check",
|
||||||
|
);
|
||||||
|
// Create a file in the scratch directory.
|
||||||
|
let writable_fd = wasi::path_open(
|
||||||
|
dir_fd,
|
||||||
|
0,
|
||||||
|
"writable_file",
|
||||||
|
wasi::OFLAGS_CREAT,
|
||||||
|
wasi::RIGHTS_FD_WRITE | wasi::RIGHTS_POLL_FD_READWRITE,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
.expect("opening a writable file");
|
||||||
|
assert_gt!(
|
||||||
|
writable_fd,
|
||||||
libc::STDERR_FILENO as wasi::Fd,
|
libc::STDERR_FILENO as wasi::Fd,
|
||||||
"file descriptor range check",
|
"file descriptor range check",
|
||||||
);
|
);
|
||||||
|
|
||||||
test_fd_readwrite(file_fd, wasi::ERRNO_SUCCESS);
|
test_fd_readwrite(readable_fd, writable_fd, wasi::ERRNO_SUCCESS);
|
||||||
|
|
||||||
wasi::fd_close(file_fd).expect("closing a file");
|
wasi::fd_close(readable_fd).expect("closing readable_file");
|
||||||
wasi::path_unlink_file(dir_fd, "file").expect("removing a file");
|
wasi::path_unlink_file(dir_fd, "readable_file").expect("removing readable_file");
|
||||||
|
wasi::path_unlink_file(dir_fd, "writable_file").expect("removing writable_file");
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn test_fd_readwrite_invalid_fd() {
|
unsafe fn test_fd_readwrite_invalid_fd() {
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
use std::mem::MaybeUninit;
|
use std::mem::MaybeUninit;
|
||||||
use wasi_tests::{assert_errno, STDERR_FD, STDIN_FD, STDOUT_FD};
|
use wasi_tests::{assert_errno, STDERR_FD, STDIN_FD, STDOUT_FD};
|
||||||
|
|
||||||
@@ -46,7 +47,7 @@ unsafe fn test_stdin_read() {
|
|||||||
let out = poll_oneoff_impl(&r#in).unwrap();
|
let out = poll_oneoff_impl(&r#in).unwrap();
|
||||||
// The result should be either a timeout, or that stdin is ready for reading.
|
// The result should be either a timeout, or that stdin is ready for reading.
|
||||||
// Both are valid behaviors that depend on the test environment.
|
// Both are valid behaviors that depend on the test environment.
|
||||||
assert!(out.len() >= 1, "should return at least 1 event");
|
assert!(out.len() >= 1, "stdin read should return at least 1 event");
|
||||||
for event in out {
|
for event in out {
|
||||||
if event.r#type == wasi::EVENTTYPE_CLOCK {
|
if event.r#type == wasi::EVENTTYPE_CLOCK {
|
||||||
assert_errno!(event.error, wasi::ERRNO_SUCCESS);
|
assert_errno!(event.error, wasi::ERRNO_SUCCESS);
|
||||||
@@ -66,55 +67,61 @@ unsafe fn test_stdin_read() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn writable_subs(h: &HashMap<u64, wasi::Fd>) -> Vec<wasi::Subscription> {
|
||||||
|
println!("writable subs: {:?}", h);
|
||||||
|
h.iter()
|
||||||
|
.map(|(ud, fd)| wasi::Subscription {
|
||||||
|
userdata: *ud,
|
||||||
|
u: wasi::SubscriptionU {
|
||||||
|
tag: wasi::EVENTTYPE_FD_WRITE,
|
||||||
|
u: wasi::SubscriptionUU {
|
||||||
|
fd_write: wasi::SubscriptionFdReadwrite {
|
||||||
|
file_descriptor: *fd,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
unsafe fn test_stdout_stderr_write() {
|
unsafe fn test_stdout_stderr_write() {
|
||||||
let stdout_readwrite = wasi::SubscriptionFdReadwrite {
|
let mut writable: HashMap<u64, wasi::Fd> =
|
||||||
file_descriptor: STDOUT_FD,
|
vec![(1, STDOUT_FD), (2, STDERR_FD)].into_iter().collect();
|
||||||
};
|
|
||||||
let stderr_readwrite = wasi::SubscriptionFdReadwrite {
|
let clock = wasi::Subscription {
|
||||||
file_descriptor: STDERR_FD,
|
userdata: CLOCK_ID,
|
||||||
};
|
|
||||||
let r#in = [
|
|
||||||
wasi::Subscription {
|
|
||||||
userdata: 1,
|
|
||||||
u: wasi::SubscriptionU {
|
u: wasi::SubscriptionU {
|
||||||
tag: wasi::EVENTTYPE_FD_WRITE,
|
tag: wasi::EVENTTYPE_CLOCK,
|
||||||
u: wasi::SubscriptionUU {
|
u: wasi::SubscriptionUU {
|
||||||
fd_write: stdout_readwrite,
|
clock: wasi::SubscriptionClock {
|
||||||
|
id: wasi::CLOCKID_MONOTONIC,
|
||||||
|
timeout: 10_000_000u64, // 10 milliseconds
|
||||||
|
precision: 0,
|
||||||
|
flags: 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
wasi::Subscription {
|
};
|
||||||
userdata: 2,
|
while !writable.is_empty() {
|
||||||
u: wasi::SubscriptionU {
|
let mut subs = writable_subs(&writable);
|
||||||
tag: wasi::EVENTTYPE_FD_WRITE,
|
subs.push(clock.clone());
|
||||||
u: wasi::SubscriptionUU {
|
let out = poll_oneoff_impl(&subs).unwrap();
|
||||||
fd_write: stderr_readwrite,
|
for event in out {
|
||||||
},
|
match event.userdata {
|
||||||
},
|
CLOCK_ID => {
|
||||||
},
|
panic!("timed out with the following pending subs: {:?}", writable)
|
||||||
];
|
}
|
||||||
let out = poll_oneoff_impl(&r#in).unwrap();
|
ud => {
|
||||||
assert_eq!(out.len(), 2, "should return 2 events");
|
if let Some(_) = writable.remove(&ud) {
|
||||||
assert_eq!(
|
assert_eq!(event.r#type, wasi::EVENTTYPE_FD_WRITE);
|
||||||
out[0].userdata, 1,
|
assert_errno!(event.error, wasi::ERRNO_SUCCESS);
|
||||||
"the event.userdata should contain fd userdata specified by the user"
|
} else {
|
||||||
);
|
panic!("Unknown userdata {}, pending sub: {:?}", ud, writable)
|
||||||
assert_errno!(out[0].error, wasi::ERRNO_SUCCESS);
|
}
|
||||||
assert_eq!(
|
}
|
||||||
out[0].r#type,
|
}
|
||||||
wasi::EVENTTYPE_FD_WRITE,
|
}
|
||||||
"the event.type should equal FD_WRITE"
|
}
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
out[1].userdata, 2,
|
|
||||||
"the event.userdata should contain fd userdata specified by the user"
|
|
||||||
);
|
|
||||||
assert_errno!(out[1].error, wasi::ERRNO_SUCCESS);
|
|
||||||
assert_eq!(
|
|
||||||
out[1].r#type,
|
|
||||||
wasi::EVENTTYPE_FD_WRITE,
|
|
||||||
"the event.type should equal FD_WRITE"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn test_poll_oneoff() {
|
unsafe fn test_poll_oneoff() {
|
||||||
|
|||||||
@@ -13,13 +13,14 @@ include = ["src/**/*", "LICENSE" ]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
wasi-common = { path = "../", version = "0.26.0" }
|
wasi-common = { path = "../", version = "0.26.0" }
|
||||||
|
async-trait = "0.1"
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
cap-std = "0.13.9"
|
cap-std = "0.13.9"
|
||||||
cap-fs-ext = "0.13.9"
|
cap-fs-ext = "0.13.9"
|
||||||
cap-time-ext = "0.13.9"
|
cap-time-ext = "0.13.9"
|
||||||
cap-rand = "0.13.9"
|
cap-rand = "0.13.9"
|
||||||
fs-set-times = "0.3.1"
|
fs-set-times = "0.3.1"
|
||||||
unsafe-io = "0.6.2"
|
unsafe-io = "0.6.5"
|
||||||
system-interface = { version = "0.6.3", features = ["cap_std_impls"] }
|
system-interface = { version = "0.6.3", features = ["cap_std_impls"] }
|
||||||
tracing = "0.1.19"
|
tracing = "0.1.19"
|
||||||
bitflags = "1.2"
|
bitflags = "1.2"
|
||||||
|
|||||||
@@ -15,13 +15,8 @@ impl Dir {
|
|||||||
pub fn from_cap_std(dir: cap_std::fs::Dir) -> Self {
|
pub fn from_cap_std(dir: cap_std::fs::Dir) -> Self {
|
||||||
Dir(dir)
|
Dir(dir)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl WasiDir for Dir {
|
pub fn open_file_(
|
||||||
fn as_any(&self) -> &dyn Any {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
fn open_file(
|
|
||||||
&self,
|
&self,
|
||||||
symlink_follow: bool,
|
symlink_follow: bool,
|
||||||
path: &str,
|
path: &str,
|
||||||
@@ -29,7 +24,7 @@ impl WasiDir for Dir {
|
|||||||
read: bool,
|
read: bool,
|
||||||
write: bool,
|
write: bool,
|
||||||
fdflags: FdFlags,
|
fdflags: FdFlags,
|
||||||
) -> Result<Box<dyn WasiFile>, Error> {
|
) -> Result<File, Error> {
|
||||||
use cap_fs_ext::{FollowSymlinks, OpenOptionsFollowExt};
|
use cap_fs_ext::{FollowSymlinks, OpenOptionsFollowExt};
|
||||||
|
|
||||||
let mut opts = cap_std::fs::OpenOptions::new();
|
let mut opts = cap_std::fs::OpenOptions::new();
|
||||||
@@ -81,26 +76,67 @@ impl WasiDir for Dir {
|
|||||||
if fdflags.contains(wasi_common::file::FdFlags::NONBLOCK) {
|
if fdflags.contains(wasi_common::file::FdFlags::NONBLOCK) {
|
||||||
f.set_fd_flags(system_interface::fs::FdFlags::NONBLOCK)?;
|
f.set_fd_flags(system_interface::fs::FdFlags::NONBLOCK)?;
|
||||||
}
|
}
|
||||||
Ok(Box::new(File::from_cap_std(f)))
|
Ok(File::from_cap_std(f))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn open_dir(&self, symlink_follow: bool, path: &str) -> Result<Box<dyn WasiDir>, Error> {
|
pub fn open_dir_(&self, symlink_follow: bool, path: &str) -> Result<Self, Error> {
|
||||||
let d = if symlink_follow {
|
let d = if symlink_follow {
|
||||||
self.0.open_dir(Path::new(path))?
|
self.0.open_dir(Path::new(path))?
|
||||||
} else {
|
} else {
|
||||||
self.0.open_dir_nofollow(Path::new(path))?
|
self.0.open_dir_nofollow(Path::new(path))?
|
||||||
};
|
};
|
||||||
Ok(Box::new(Dir::from_cap_std(d)))
|
Ok(Dir::from_cap_std(d))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_dir(&self, path: &str) -> Result<(), Error> {
|
pub fn rename_(&self, src_path: &str, dest_dir: &Self, dest_path: &str) -> Result<(), Error> {
|
||||||
|
self.0
|
||||||
|
.rename(Path::new(src_path), &dest_dir.0, Path::new(dest_path))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
pub fn hard_link_(
|
||||||
|
&self,
|
||||||
|
src_path: &str,
|
||||||
|
target_dir: &Self,
|
||||||
|
target_path: &str,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let src_path = Path::new(src_path);
|
||||||
|
let target_path = Path::new(target_path);
|
||||||
|
self.0.hard_link(src_path, &target_dir.0, target_path)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait(?Send)]
|
||||||
|
impl WasiDir for Dir {
|
||||||
|
fn as_any(&self) -> &dyn Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
async fn open_file(
|
||||||
|
&self,
|
||||||
|
symlink_follow: bool,
|
||||||
|
path: &str,
|
||||||
|
oflags: OFlags,
|
||||||
|
read: bool,
|
||||||
|
write: bool,
|
||||||
|
fdflags: FdFlags,
|
||||||
|
) -> Result<Box<dyn WasiFile>, Error> {
|
||||||
|
let f = self.open_file_(symlink_follow, path, oflags, read, write, fdflags)?;
|
||||||
|
Ok(Box::new(f))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn open_dir(&self, symlink_follow: bool, path: &str) -> Result<Box<dyn WasiDir>, Error> {
|
||||||
|
let d = self.open_dir_(symlink_follow, path)?;
|
||||||
|
Ok(Box::new(d))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create_dir(&self, path: &str) -> Result<(), Error> {
|
||||||
self.0.create_dir(Path::new(path))?;
|
self.0.create_dir(Path::new(path))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn readdir(
|
async fn readdir(
|
||||||
&self,
|
&self,
|
||||||
cursor: ReaddirCursor,
|
cursor: ReaddirCursor,
|
||||||
) -> Result<Box<dyn Iterator<Item = Result<ReaddirEntity, Error>>>, Error> {
|
) -> Result<Box<dyn Iterator<Item = Result<ReaddirEntity, Error>> + Send>, Error> {
|
||||||
// cap_std's read_dir does not include . and .., we should prepend these.
|
// cap_std's read_dir does not include . and .., we should prepend these.
|
||||||
// Why does the Ok contain a tuple? We can't construct a cap_std::fs::DirEntry, and we don't
|
// Why does the Ok contain a tuple? We can't construct a cap_std::fs::DirEntry, and we don't
|
||||||
// have enough info to make a ReaddirEntity yet.
|
// have enough info to make a ReaddirEntity yet.
|
||||||
@@ -165,24 +201,24 @@ impl WasiDir for Dir {
|
|||||||
Ok(Box::new(rd))
|
Ok(Box::new(rd))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn symlink(&self, src_path: &str, dest_path: &str) -> Result<(), Error> {
|
async fn symlink(&self, src_path: &str, dest_path: &str) -> Result<(), Error> {
|
||||||
self.0.symlink(src_path, dest_path)?;
|
self.0.symlink(src_path, dest_path)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn remove_dir(&self, path: &str) -> Result<(), Error> {
|
async fn remove_dir(&self, path: &str) -> Result<(), Error> {
|
||||||
self.0.remove_dir(Path::new(path))?;
|
self.0.remove_dir(Path::new(path))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unlink_file(&self, path: &str) -> Result<(), Error> {
|
async fn unlink_file(&self, path: &str) -> Result<(), Error> {
|
||||||
self.0.remove_file_or_symlink(Path::new(path))?;
|
self.0.remove_file_or_symlink(Path::new(path))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn read_link(&self, path: &str) -> Result<PathBuf, Error> {
|
async fn read_link(&self, path: &str) -> Result<PathBuf, Error> {
|
||||||
let link = self.0.read_link(Path::new(path))?;
|
let link = self.0.read_link(Path::new(path))?;
|
||||||
Ok(link)
|
Ok(link)
|
||||||
}
|
}
|
||||||
fn get_filestat(&self) -> Result<Filestat, Error> {
|
async fn get_filestat(&self) -> Result<Filestat, Error> {
|
||||||
let meta = self.0.dir_metadata()?;
|
let meta = self.0.dir_metadata()?;
|
||||||
Ok(Filestat {
|
Ok(Filestat {
|
||||||
device_id: meta.dev(),
|
device_id: meta.dev(),
|
||||||
@@ -195,7 +231,11 @@ impl WasiDir for Dir {
|
|||||||
ctim: meta.created().map(|t| Some(t.into_std())).unwrap_or(None),
|
ctim: meta.created().map(|t| Some(t.into_std())).unwrap_or(None),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
fn get_path_filestat(&self, path: &str, follow_symlinks: bool) -> Result<Filestat, Error> {
|
async fn get_path_filestat(
|
||||||
|
&self,
|
||||||
|
path: &str,
|
||||||
|
follow_symlinks: bool,
|
||||||
|
) -> Result<Filestat, Error> {
|
||||||
let meta = if follow_symlinks {
|
let meta = if follow_symlinks {
|
||||||
self.0.metadata(Path::new(path))?
|
self.0.metadata(Path::new(path))?
|
||||||
} else {
|
} else {
|
||||||
@@ -212,16 +252,19 @@ impl WasiDir for Dir {
|
|||||||
ctim: meta.created().map(|t| Some(t.into_std())).unwrap_or(None),
|
ctim: meta.created().map(|t| Some(t.into_std())).unwrap_or(None),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
fn rename(&self, src_path: &str, dest_dir: &dyn WasiDir, dest_path: &str) -> Result<(), Error> {
|
async fn rename(
|
||||||
|
&self,
|
||||||
|
src_path: &str,
|
||||||
|
dest_dir: &dyn WasiDir,
|
||||||
|
dest_path: &str,
|
||||||
|
) -> Result<(), Error> {
|
||||||
let dest_dir = dest_dir
|
let dest_dir = dest_dir
|
||||||
.as_any()
|
.as_any()
|
||||||
.downcast_ref::<Self>()
|
.downcast_ref::<Self>()
|
||||||
.ok_or(Error::badf().context("failed downcast to cap-std Dir"))?;
|
.ok_or(Error::badf().context("failed downcast to cap-std Dir"))?;
|
||||||
self.0
|
self.rename_(src_path, dest_dir, dest_path)
|
||||||
.rename(Path::new(src_path), &dest_dir.0, Path::new(dest_path))?;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
fn hard_link(
|
async fn hard_link(
|
||||||
&self,
|
&self,
|
||||||
src_path: &str,
|
src_path: &str,
|
||||||
target_dir: &dyn WasiDir,
|
target_dir: &dyn WasiDir,
|
||||||
@@ -231,12 +274,9 @@ impl WasiDir for Dir {
|
|||||||
.as_any()
|
.as_any()
|
||||||
.downcast_ref::<Self>()
|
.downcast_ref::<Self>()
|
||||||
.ok_or(Error::badf().context("failed downcast to cap-std Dir"))?;
|
.ok_or(Error::badf().context("failed downcast to cap-std Dir"))?;
|
||||||
let src_path = Path::new(src_path);
|
self.hard_link_(src_path, target_dir, target_path)
|
||||||
let target_path = Path::new(target_path);
|
|
||||||
self.0.hard_link(src_path, &target_dir.0, target_path)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
fn set_times(
|
async fn set_times(
|
||||||
&self,
|
&self,
|
||||||
path: &str,
|
path: &str,
|
||||||
atime: Option<wasi_common::SystemTimeSpec>,
|
atime: Option<wasi_common::SystemTimeSpec>,
|
||||||
@@ -280,7 +320,7 @@ mod test {
|
|||||||
let preopen_dir = unsafe { cap_std::fs::Dir::open_ambient_dir(tempdir.path()) }
|
let preopen_dir = unsafe { cap_std::fs::Dir::open_ambient_dir(tempdir.path()) }
|
||||||
.expect("open ambient temporary dir");
|
.expect("open ambient temporary dir");
|
||||||
let preopen_dir = Dir::from_cap_std(preopen_dir);
|
let preopen_dir = Dir::from_cap_std(preopen_dir);
|
||||||
wasi_common::WasiDir::open_dir(&preopen_dir, false, ".")
|
run(wasi_common::WasiDir::open_dir(&preopen_dir, false, "."))
|
||||||
.expect("open the same directory via WasiDir abstraction");
|
.expect("open the same directory via WasiDir abstraction");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -294,9 +334,8 @@ mod test {
|
|||||||
|
|
||||||
fn readdir_into_map(dir: &dyn WasiDir) -> HashMap<String, ReaddirEntity> {
|
fn readdir_into_map(dir: &dyn WasiDir) -> HashMap<String, ReaddirEntity> {
|
||||||
let mut out = HashMap::new();
|
let mut out = HashMap::new();
|
||||||
for readdir_result in dir
|
for readdir_result in
|
||||||
.readdir(ReaddirCursor::from(0))
|
run(dir.readdir(ReaddirCursor::from(0))).expect("readdir succeeds")
|
||||||
.expect("readdir succeeds")
|
|
||||||
{
|
{
|
||||||
let entity = readdir_result.expect("readdir entry is valid");
|
let entity = readdir_result.expect("readdir entry is valid");
|
||||||
out.insert(entity.name.clone(), entity);
|
out.insert(entity.name.clone(), entity);
|
||||||
@@ -322,15 +361,14 @@ mod test {
|
|||||||
assert!(entities.get(".").is_some());
|
assert!(entities.get(".").is_some());
|
||||||
assert!(entities.get("..").is_some());
|
assert!(entities.get("..").is_some());
|
||||||
|
|
||||||
preopen_dir
|
run(preopen_dir.open_file(
|
||||||
.open_file(
|
|
||||||
false,
|
false,
|
||||||
"file1",
|
"file1",
|
||||||
OFlags::CREATE,
|
OFlags::CREATE,
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
FdFlags::empty(),
|
FdFlags::empty(),
|
||||||
)
|
))
|
||||||
.expect("create file1");
|
.expect("create file1");
|
||||||
|
|
||||||
let entities = readdir_into_map(&preopen_dir);
|
let entities = readdir_into_map(&preopen_dir);
|
||||||
@@ -348,4 +386,41 @@ mod test {
|
|||||||
FileType::RegularFile
|
FileType::RegularFile
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn run<F: std::future::Future>(future: F) -> F::Output {
|
||||||
|
use std::pin::Pin;
|
||||||
|
use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
|
||||||
|
|
||||||
|
let mut f = Pin::from(Box::new(future));
|
||||||
|
let waker = dummy_waker();
|
||||||
|
let mut cx = Context::from_waker(&waker);
|
||||||
|
match f.as_mut().poll(&mut cx) {
|
||||||
|
Poll::Ready(val) => return val,
|
||||||
|
Poll::Pending => {
|
||||||
|
panic!("Cannot wait on pending future: must enable wiggle \"async\" future and execute on an async Store")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dummy_waker() -> Waker {
|
||||||
|
return unsafe { Waker::from_raw(clone(5 as *const _)) };
|
||||||
|
|
||||||
|
unsafe fn clone(ptr: *const ()) -> RawWaker {
|
||||||
|
assert_eq!(ptr as usize, 5);
|
||||||
|
const VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake_by_ref, drop);
|
||||||
|
RawWaker::new(ptr, &VTABLE)
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn wake(ptr: *const ()) {
|
||||||
|
assert_eq!(ptr as usize, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn wake_by_ref(ptr: *const ()) {
|
||||||
|
assert_eq!(ptr as usize, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn drop(ptr: *const ()) {
|
||||||
|
assert_eq!(ptr as usize, 5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,27 +20,28 @@ impl File {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait(?Send)]
|
||||||
impl WasiFile for File {
|
impl WasiFile for File {
|
||||||
fn as_any(&self) -> &dyn Any {
|
fn as_any(&self) -> &dyn Any {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
fn datasync(&self) -> Result<(), Error> {
|
async fn datasync(&self) -> Result<(), Error> {
|
||||||
self.0.sync_data()?;
|
self.0.sync_data()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn sync(&self) -> Result<(), Error> {
|
async fn sync(&self) -> Result<(), Error> {
|
||||||
self.0.sync_all()?;
|
self.0.sync_all()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn get_filetype(&self) -> Result<FileType, Error> {
|
async fn get_filetype(&self) -> Result<FileType, Error> {
|
||||||
let meta = self.0.metadata()?;
|
let meta = self.0.metadata()?;
|
||||||
Ok(filetype_from(&meta.file_type()))
|
Ok(filetype_from(&meta.file_type()))
|
||||||
}
|
}
|
||||||
fn get_fdflags(&self) -> Result<FdFlags, Error> {
|
async fn get_fdflags(&self) -> Result<FdFlags, Error> {
|
||||||
let fdflags = self.0.get_fd_flags()?;
|
let fdflags = self.0.get_fd_flags()?;
|
||||||
Ok(from_sysif_fdflags(fdflags))
|
Ok(from_sysif_fdflags(fdflags))
|
||||||
}
|
}
|
||||||
fn set_fdflags(&mut self, fdflags: FdFlags) -> Result<(), Error> {
|
async fn set_fdflags(&mut self, fdflags: FdFlags) -> Result<(), Error> {
|
||||||
if fdflags.intersects(
|
if fdflags.intersects(
|
||||||
wasi_common::file::FdFlags::DSYNC
|
wasi_common::file::FdFlags::DSYNC
|
||||||
| wasi_common::file::FdFlags::SYNC
|
| wasi_common::file::FdFlags::SYNC
|
||||||
@@ -50,7 +51,7 @@ impl WasiFile for File {
|
|||||||
}
|
}
|
||||||
Ok(self.0.set_fd_flags(to_sysif_fdflags(fdflags))?)
|
Ok(self.0.set_fd_flags(to_sysif_fdflags(fdflags))?)
|
||||||
}
|
}
|
||||||
fn get_filestat(&self) -> Result<Filestat, Error> {
|
async fn get_filestat(&self) -> Result<Filestat, Error> {
|
||||||
let meta = self.0.metadata()?;
|
let meta = self.0.metadata()?;
|
||||||
Ok(Filestat {
|
Ok(Filestat {
|
||||||
device_id: meta.dev(),
|
device_id: meta.dev(),
|
||||||
@@ -63,19 +64,19 @@ impl WasiFile for File {
|
|||||||
ctim: meta.created().map(|t| Some(t.into_std())).unwrap_or(None),
|
ctim: meta.created().map(|t| Some(t.into_std())).unwrap_or(None),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
fn set_filestat_size(&self, size: u64) -> Result<(), Error> {
|
async fn set_filestat_size(&self, size: u64) -> Result<(), Error> {
|
||||||
self.0.set_len(size)?;
|
self.0.set_len(size)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn advise(&self, offset: u64, len: u64, advice: Advice) -> Result<(), Error> {
|
async fn advise(&self, offset: u64, len: u64, advice: Advice) -> Result<(), Error> {
|
||||||
self.0.advise(offset, len, convert_advice(advice))?;
|
self.0.advise(offset, len, convert_advice(advice))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn allocate(&self, offset: u64, len: u64) -> Result<(), Error> {
|
async fn allocate(&self, offset: u64, len: u64) -> Result<(), Error> {
|
||||||
self.0.allocate(offset, len)?;
|
self.0.allocate(offset, len)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn set_times(
|
async fn set_times(
|
||||||
&self,
|
&self,
|
||||||
atime: Option<wasi_common::SystemTimeSpec>,
|
atime: Option<wasi_common::SystemTimeSpec>,
|
||||||
mtime: Option<wasi_common::SystemTimeSpec>,
|
mtime: Option<wasi_common::SystemTimeSpec>,
|
||||||
@@ -84,32 +85,46 @@ impl WasiFile for File {
|
|||||||
.set_times(convert_systimespec(atime), convert_systimespec(mtime))?;
|
.set_times(convert_systimespec(atime), convert_systimespec(mtime))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn read_vectored(&self, bufs: &mut [io::IoSliceMut]) -> Result<u64, Error> {
|
async fn read_vectored<'a>(&self, bufs: &mut [io::IoSliceMut<'a>]) -> Result<u64, Error> {
|
||||||
let n = self.0.read_vectored(bufs)?;
|
let n = self.0.read_vectored(bufs)?;
|
||||||
Ok(n.try_into()?)
|
Ok(n.try_into()?)
|
||||||
}
|
}
|
||||||
fn read_vectored_at(&self, bufs: &mut [io::IoSliceMut], offset: u64) -> Result<u64, Error> {
|
async fn read_vectored_at<'a>(
|
||||||
|
&self,
|
||||||
|
bufs: &mut [io::IoSliceMut<'a>],
|
||||||
|
offset: u64,
|
||||||
|
) -> Result<u64, Error> {
|
||||||
let n = self.0.read_vectored_at(bufs, offset)?;
|
let n = self.0.read_vectored_at(bufs, offset)?;
|
||||||
Ok(n.try_into()?)
|
Ok(n.try_into()?)
|
||||||
}
|
}
|
||||||
fn write_vectored(&self, bufs: &[io::IoSlice]) -> Result<u64, Error> {
|
async fn write_vectored<'a>(&self, bufs: &[io::IoSlice<'a>]) -> Result<u64, Error> {
|
||||||
let n = self.0.write_vectored(bufs)?;
|
let n = self.0.write_vectored(bufs)?;
|
||||||
Ok(n.try_into()?)
|
Ok(n.try_into()?)
|
||||||
}
|
}
|
||||||
fn write_vectored_at(&self, bufs: &[io::IoSlice], offset: u64) -> Result<u64, Error> {
|
async fn write_vectored_at<'a>(
|
||||||
|
&self,
|
||||||
|
bufs: &[io::IoSlice<'a>],
|
||||||
|
offset: u64,
|
||||||
|
) -> Result<u64, Error> {
|
||||||
let n = self.0.write_vectored_at(bufs, offset)?;
|
let n = self.0.write_vectored_at(bufs, offset)?;
|
||||||
Ok(n.try_into()?)
|
Ok(n.try_into()?)
|
||||||
}
|
}
|
||||||
fn seek(&self, pos: std::io::SeekFrom) -> Result<u64, Error> {
|
async fn seek(&self, pos: std::io::SeekFrom) -> Result<u64, Error> {
|
||||||
Ok(self.0.seek(pos)?)
|
Ok(self.0.seek(pos)?)
|
||||||
}
|
}
|
||||||
fn peek(&self, buf: &mut [u8]) -> Result<u64, Error> {
|
async fn peek(&self, buf: &mut [u8]) -> Result<u64, Error> {
|
||||||
let n = self.0.peek(buf)?;
|
let n = self.0.peek(buf)?;
|
||||||
Ok(n.try_into()?)
|
Ok(n.try_into()?)
|
||||||
}
|
}
|
||||||
fn num_ready_bytes(&self) -> Result<u64, Error> {
|
async fn num_ready_bytes(&self) -> Result<u64, Error> {
|
||||||
Ok(self.0.num_ready_bytes()?)
|
Ok(self.0.num_ready_bytes()?)
|
||||||
}
|
}
|
||||||
|
async fn readable(&mut self) -> Result<(), Error> {
|
||||||
|
Err(Error::badf())
|
||||||
|
}
|
||||||
|
async fn writable(&mut self) -> Result<(), Error> {
|
||||||
|
Err(Error::badf())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn filetype_from(ft: &cap_std::fs::FileType) -> FileType {
|
pub fn filetype_from(ft: &cap_std::fs::FileType) -> FileType {
|
||||||
|
|||||||
@@ -1,15 +1,40 @@
|
|||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
mod unix;
|
pub mod unix;
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
pub use unix::*;
|
pub use unix::poll_oneoff;
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
mod windows;
|
pub mod windows;
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
pub use windows::*;
|
pub use windows::poll_oneoff;
|
||||||
|
|
||||||
use wasi_common::sched::WasiSched;
|
use std::thread;
|
||||||
|
use std::time::Duration;
|
||||||
|
use wasi_common::{
|
||||||
|
sched::{Poll, WasiSched},
|
||||||
|
Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct SyncSched {}
|
||||||
|
impl SyncSched {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[async_trait::async_trait(?Send)]
|
||||||
|
impl WasiSched for SyncSched {
|
||||||
|
async fn poll_oneoff<'a>(&self, poll: &mut Poll<'a>) -> Result<(), Error> {
|
||||||
|
poll_oneoff(poll).await
|
||||||
|
}
|
||||||
|
async fn sched_yield(&self) -> Result<(), Error> {
|
||||||
|
thread::yield_now();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
async fn sleep(&self, duration: Duration) -> Result<(), Error> {
|
||||||
|
std::thread::sleep(duration);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
pub fn sched_ctx() -> Box<dyn WasiSched> {
|
pub fn sched_ctx() -> Box<dyn WasiSched> {
|
||||||
Box::new(SyncSched::new())
|
Box::new(SyncSched::new())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,44 +1,33 @@
|
|||||||
use cap_std::time::Duration;
|
use cap_std::time::Duration;
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use std::ops::Deref;
|
|
||||||
use std::os::unix::io::{AsRawFd, RawFd};
|
use std::os::unix::io::{AsRawFd, RawFd};
|
||||||
use wasi_common::{
|
use wasi_common::{
|
||||||
file::WasiFile,
|
file::WasiFile,
|
||||||
sched::{
|
sched::{
|
||||||
subscription::{RwEventFlags, Subscription},
|
subscription::{RwEventFlags, Subscription},
|
||||||
Poll, WasiSched,
|
Poll,
|
||||||
},
|
},
|
||||||
Error, ErrorExt,
|
Error, ErrorExt,
|
||||||
};
|
};
|
||||||
|
|
||||||
use poll::{PollFd, PollFlags};
|
use poll::{PollFd, PollFlags};
|
||||||
|
|
||||||
pub struct SyncSched;
|
pub async fn poll_oneoff<'a>(poll: &mut Poll<'a>) -> Result<(), Error> {
|
||||||
|
|
||||||
impl SyncSched {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
SyncSched
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WasiSched for SyncSched {
|
|
||||||
fn poll_oneoff<'a>(&self, poll: &'a Poll<'a>) -> Result<(), Error> {
|
|
||||||
if poll.is_empty() {
|
if poll.is_empty() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let mut pollfds = Vec::new();
|
let mut pollfds = Vec::new();
|
||||||
let timeout = poll.earliest_clock_deadline();
|
|
||||||
for s in poll.rw_subscriptions() {
|
for s in poll.rw_subscriptions() {
|
||||||
match s {
|
match s {
|
||||||
Subscription::Read(f) => {
|
Subscription::Read(f) => {
|
||||||
let raw_fd = wasi_file_raw_fd(f.file.deref()).ok_or(
|
let raw_fd = wasi_file_raw_fd(f.file).ok_or(
|
||||||
Error::invalid_argument().context("read subscription fd downcast failed"),
|
Error::invalid_argument().context("read subscription fd downcast failed"),
|
||||||
)?;
|
)?;
|
||||||
pollfds.push(unsafe { PollFd::new(raw_fd, PollFlags::POLLIN) });
|
pollfds.push(unsafe { PollFd::new(raw_fd, PollFlags::POLLIN) });
|
||||||
}
|
}
|
||||||
|
|
||||||
Subscription::Write(f) => {
|
Subscription::Write(f) => {
|
||||||
let raw_fd = wasi_file_raw_fd(f.file.deref()).ok_or(
|
let raw_fd = wasi_file_raw_fd(f.file).ok_or(
|
||||||
Error::invalid_argument().context("write subscription fd downcast failed"),
|
Error::invalid_argument().context("write subscription fd downcast failed"),
|
||||||
)?;
|
)?;
|
||||||
pollfds.push(unsafe { PollFd::new(raw_fd, PollFlags::POLLOUT) });
|
pollfds.push(unsafe { PollFd::new(raw_fd, PollFlags::POLLOUT) });
|
||||||
@@ -48,7 +37,7 @@ impl WasiSched for SyncSched {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let ready = loop {
|
let ready = loop {
|
||||||
let poll_timeout = if let Some(t) = timeout {
|
let poll_timeout = if let Some(t) = poll.earliest_clock_deadline() {
|
||||||
let duration = t.duration_until().unwrap_or(Duration::from_secs(0));
|
let duration = t.duration_until().unwrap_or(Duration::from_secs(0));
|
||||||
(duration.as_millis() + 1) // XXX try always rounding up?
|
(duration.as_millis() + 1) // XXX try always rounding up?
|
||||||
.try_into()
|
.try_into()
|
||||||
@@ -78,7 +67,7 @@ impl WasiSched for SyncSched {
|
|||||||
if let Some(revents) = pollfd.revents() {
|
if let Some(revents) = pollfd.revents() {
|
||||||
let (nbytes, rwsub) = match rwsub {
|
let (nbytes, rwsub) = match rwsub {
|
||||||
Subscription::Read(sub) => {
|
Subscription::Read(sub) => {
|
||||||
let ready = sub.file.num_ready_bytes()?;
|
let ready = sub.file.num_ready_bytes().await?;
|
||||||
(std::cmp::max(ready, 1), sub)
|
(std::cmp::max(ready, 1), sub)
|
||||||
}
|
}
|
||||||
Subscription::Write(sub) => (0, sub),
|
Subscription::Write(sub) => (0, sub),
|
||||||
@@ -96,22 +85,13 @@ impl WasiSched for SyncSched {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
timeout
|
poll.earliest_clock_deadline()
|
||||||
.expect("timed out")
|
.expect("timed out")
|
||||||
.result()
|
.result()
|
||||||
.expect("timer deadline is past")
|
.expect("timer deadline is past")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
|
||||||
fn sched_yield(&self) -> Result<(), Error> {
|
|
||||||
std::thread::yield_now();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn sleep(&self, duration: Duration) -> Result<(), Error> {
|
|
||||||
std::thread::sleep(duration);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wasi_file_raw_fd(f: &dyn WasiFile) -> Option<RawFd> {
|
fn wasi_file_raw_fd(f: &dyn WasiFile) -> Option<RawFd> {
|
||||||
|
|||||||
@@ -1,3 +1,13 @@
|
|||||||
|
// The windows scheduler is unmaintained and due for a rewrite.
|
||||||
|
//
|
||||||
|
// Rather than use a polling mechanism for file read/write readiness,
|
||||||
|
// it checks readiness just once, before sleeping for any timer subscriptions.
|
||||||
|
// Checking stdin readiness uses a worker thread which, once started, lives for the
|
||||||
|
// lifetime of the process.
|
||||||
|
//
|
||||||
|
// We suspect there are bugs in this scheduler, however, we have not
|
||||||
|
// taken the time to improve it. See bug #2880.
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::os::windows::io::{AsRawHandle, RawHandle};
|
use std::os::windows::io::{AsRawHandle, RawHandle};
|
||||||
@@ -9,48 +19,27 @@ use wasi_common::{
|
|||||||
file::WasiFile,
|
file::WasiFile,
|
||||||
sched::{
|
sched::{
|
||||||
subscription::{RwEventFlags, Subscription},
|
subscription::{RwEventFlags, Subscription},
|
||||||
Poll, WasiSched,
|
Poll,
|
||||||
},
|
},
|
||||||
Error, ErrorExt,
|
Error, ErrorExt,
|
||||||
};
|
};
|
||||||
pub struct SyncSched {}
|
|
||||||
|
|
||||||
impl SyncSched {
|
pub async fn poll_oneoff<'a>(poll: &mut Poll<'a>) -> Result<(), Error> {
|
||||||
pub fn new() -> Self {
|
poll_oneoff_(poll, wasi_file_is_stdin, wasi_file_raw_handle).await
|
||||||
Self {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WasiSched for SyncSched {
|
// For reuse by wasi-tokio, which has a different WasiFile -> RawHandle translator.
|
||||||
fn poll_oneoff<'a>(&self, poll: &'a Poll<'a>) -> Result<(), Error> {
|
pub async fn poll_oneoff_<'a>(
|
||||||
|
poll: &mut Poll<'a>,
|
||||||
|
file_is_stdin: impl Fn(&dyn WasiFile) -> bool,
|
||||||
|
file_to_handle: impl Fn(&dyn WasiFile) -> Option<RawHandle>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
if poll.is_empty() {
|
if poll.is_empty() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut ready = false;
|
let mut ready = false;
|
||||||
let timeout = poll.earliest_clock_deadline();
|
let waitmode = if let Some(t) = poll.earliest_clock_deadline() {
|
||||||
|
|
||||||
let mut stdin_read_subs = Vec::new();
|
|
||||||
let mut immediate_subs = Vec::new();
|
|
||||||
for s in poll.rw_subscriptions() {
|
|
||||||
match s {
|
|
||||||
Subscription::Read(r) if r.file.as_any().is::<crate::stdio::Stdin>() => {
|
|
||||||
stdin_read_subs.push(r);
|
|
||||||
}
|
|
||||||
Subscription::Read(rw) | Subscription::Write(rw) => {
|
|
||||||
if wasi_file_raw_handle(rw.file.deref()).is_some() {
|
|
||||||
immediate_subs.push(s);
|
|
||||||
} else {
|
|
||||||
return Err(Error::invalid_argument()
|
|
||||||
.context("read/write subscription fd downcast failed"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Subscription::MonotonicClock { .. } => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !stdin_read_subs.is_empty() {
|
|
||||||
let waitmode = if let Some(t) = timeout {
|
|
||||||
if let Some(duration) = t.duration_until() {
|
if let Some(duration) = t.duration_until() {
|
||||||
WaitMode::Timeout(duration)
|
WaitMode::Timeout(duration)
|
||||||
} else {
|
} else {
|
||||||
@@ -63,6 +52,37 @@ impl WasiSched for SyncSched {
|
|||||||
WaitMode::Infinite
|
WaitMode::Infinite
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut stdin_read_subs = Vec::new();
|
||||||
|
let mut immediate_reads = Vec::new();
|
||||||
|
let mut immediate_writes = Vec::new();
|
||||||
|
for s in poll.rw_subscriptions() {
|
||||||
|
match s {
|
||||||
|
Subscription::Read(r) => {
|
||||||
|
if file_is_stdin(r.file.deref()) {
|
||||||
|
stdin_read_subs.push(r);
|
||||||
|
} else if file_to_handle(r.file.deref()).is_some() {
|
||||||
|
immediate_reads.push(r);
|
||||||
|
} else {
|
||||||
|
return Err(
|
||||||
|
Error::invalid_argument().context("read subscription fd downcast failed")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Subscription::Write(w) => {
|
||||||
|
if file_to_handle(w.file.deref()).is_some() {
|
||||||
|
immediate_writes.push(w);
|
||||||
|
} else {
|
||||||
|
return Err(
|
||||||
|
Error::invalid_argument().context("write subscription fd downcast failed")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Subscription::MonotonicClock { .. } => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !stdin_read_subs.is_empty() {
|
||||||
let state = STDIN_POLL
|
let state = STDIN_POLL
|
||||||
.lock()
|
.lock()
|
||||||
.map_err(|_| Error::trap("failed to take lock of STDIN_POLL"))?
|
.map_err(|_| Error::trap("failed to take lock of STDIN_POLL"))?
|
||||||
@@ -88,13 +108,8 @@ impl WasiSched for SyncSched {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for sub in immediate_subs {
|
for r in immediate_reads {
|
||||||
match sub {
|
match r.file.num_ready_bytes().await {
|
||||||
Subscription::Read(r) => {
|
|
||||||
// XXX This doesnt strictly preserve the behavior in the earlier
|
|
||||||
// implementation, which would always do complete(0) for reads from
|
|
||||||
// stdout/err.
|
|
||||||
match r.file.num_ready_bytes() {
|
|
||||||
Ok(ready_bytes) => {
|
Ok(ready_bytes) => {
|
||||||
r.complete(ready_bytes, RwEventFlags::empty());
|
r.complete(ready_bytes, RwEventFlags::empty());
|
||||||
ready = true;
|
ready = true;
|
||||||
@@ -105,36 +120,26 @@ impl WasiSched for SyncSched {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Subscription::Write(w) => {
|
for w in immediate_writes {
|
||||||
// Everything is always ready for writing, apparently?
|
// Everything is always ready for writing, apparently?
|
||||||
w.complete(0, RwEventFlags::empty());
|
w.complete(0, RwEventFlags::empty());
|
||||||
ready = true;
|
ready = true;
|
||||||
}
|
}
|
||||||
Subscription::MonotonicClock { .. } => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !ready {
|
if !ready {
|
||||||
if let Some(t) = timeout {
|
if let WaitMode::Timeout(duration) = waitmode {
|
||||||
if let Some(duration) = t.duration_until() {
|
|
||||||
thread::sleep(duration);
|
thread::sleep(duration);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
|
||||||
fn sched_yield(&self) -> Result<(), Error> {
|
|
||||||
thread::yield_now();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn sleep(&self, duration: Duration) -> Result<(), Error> {
|
|
||||||
std::thread::sleep(duration);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wasi_file_raw_handle(f: &dyn WasiFile) -> Option<RawHandle> {
|
pub fn wasi_file_is_stdin(f: &dyn WasiFile) -> bool {
|
||||||
|
f.as_any().is::<crate::stdio::Stdin>()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn wasi_file_raw_handle(f: &dyn WasiFile) -> Option<RawHandle> {
|
||||||
let a = f.as_any();
|
let a = f.as_any();
|
||||||
if a.is::<crate::file::File>() {
|
if a.is::<crate::file::File>() {
|
||||||
Some(
|
Some(
|
||||||
@@ -172,6 +177,7 @@ enum PollState {
|
|||||||
Error(std::io::Error),
|
Error(std::io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
enum WaitMode {
|
enum WaitMode {
|
||||||
Timeout(Duration),
|
Timeout(Duration),
|
||||||
Infinite,
|
Infinite,
|
||||||
|
|||||||
@@ -22,31 +22,32 @@ pub fn stdin() -> Stdin {
|
|||||||
Stdin(std::io::stdin())
|
Stdin(std::io::stdin())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait(?Send)]
|
||||||
impl WasiFile for Stdin {
|
impl WasiFile for Stdin {
|
||||||
fn as_any(&self) -> &dyn Any {
|
fn as_any(&self) -> &dyn Any {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
fn datasync(&self) -> Result<(), Error> {
|
async fn datasync(&self) -> Result<(), Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn sync(&self) -> Result<(), Error> {
|
async fn sync(&self) -> Result<(), Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn get_filetype(&self) -> Result<FileType, Error> {
|
async fn get_filetype(&self) -> Result<FileType, Error> {
|
||||||
Ok(FileType::Unknown)
|
Ok(FileType::Unknown)
|
||||||
}
|
}
|
||||||
fn get_fdflags(&self) -> Result<FdFlags, Error> {
|
async fn get_fdflags(&self) -> Result<FdFlags, Error> {
|
||||||
Ok(FdFlags::empty())
|
Ok(FdFlags::empty())
|
||||||
}
|
}
|
||||||
fn set_fdflags(&mut self, _fdflags: FdFlags) -> Result<(), Error> {
|
async fn set_fdflags(&mut self, _fdflags: FdFlags) -> Result<(), Error> {
|
||||||
Err(Error::badf())
|
Err(Error::badf())
|
||||||
}
|
}
|
||||||
fn get_filestat(&self) -> Result<Filestat, Error> {
|
async fn get_filestat(&self) -> Result<Filestat, Error> {
|
||||||
let meta = self.0.as_file_view().metadata()?;
|
let meta = self.0.as_file_view().metadata()?;
|
||||||
Ok(Filestat {
|
Ok(Filestat {
|
||||||
device_id: 0,
|
device_id: 0,
|
||||||
inode: 0,
|
inode: 0,
|
||||||
filetype: self.get_filetype()?,
|
filetype: self.get_filetype().await?,
|
||||||
nlink: 0,
|
nlink: 0,
|
||||||
size: meta.len(),
|
size: meta.len(),
|
||||||
atim: meta.accessed().ok(),
|
atim: meta.accessed().ok(),
|
||||||
@@ -54,35 +55,43 @@ impl WasiFile for Stdin {
|
|||||||
ctim: meta.created().ok(),
|
ctim: meta.created().ok(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
fn set_filestat_size(&self, _size: u64) -> Result<(), Error> {
|
async fn set_filestat_size(&self, _size: u64) -> Result<(), Error> {
|
||||||
Err(Error::badf())
|
Err(Error::badf())
|
||||||
}
|
}
|
||||||
fn advise(&self, _offset: u64, _len: u64, _advice: Advice) -> Result<(), Error> {
|
async fn advise(&self, _offset: u64, _len: u64, _advice: Advice) -> Result<(), Error> {
|
||||||
Err(Error::badf())
|
Err(Error::badf())
|
||||||
}
|
}
|
||||||
fn allocate(&self, _offset: u64, _len: u64) -> Result<(), Error> {
|
async fn allocate(&self, _offset: u64, _len: u64) -> Result<(), Error> {
|
||||||
Err(Error::badf())
|
Err(Error::badf())
|
||||||
}
|
}
|
||||||
fn read_vectored(&self, bufs: &mut [io::IoSliceMut]) -> Result<u64, Error> {
|
async fn read_vectored<'a>(&self, bufs: &mut [io::IoSliceMut<'a>]) -> Result<u64, Error> {
|
||||||
let n = self.0.as_file_view().read_vectored(bufs)?;
|
let n = self.0.as_file_view().read_vectored(bufs)?;
|
||||||
Ok(n.try_into().map_err(|_| Error::range())?)
|
Ok(n.try_into().map_err(|_| Error::range())?)
|
||||||
}
|
}
|
||||||
fn read_vectored_at(&self, _bufs: &mut [io::IoSliceMut], _offset: u64) -> Result<u64, Error> {
|
async fn read_vectored_at<'a>(
|
||||||
|
&self,
|
||||||
|
_bufs: &mut [io::IoSliceMut<'a>],
|
||||||
|
_offset: u64,
|
||||||
|
) -> Result<u64, Error> {
|
||||||
Err(Error::seek_pipe())
|
Err(Error::seek_pipe())
|
||||||
}
|
}
|
||||||
fn write_vectored(&self, _bufs: &[io::IoSlice]) -> Result<u64, Error> {
|
async fn write_vectored<'a>(&self, _bufs: &[io::IoSlice<'a>]) -> Result<u64, Error> {
|
||||||
Err(Error::badf())
|
Err(Error::badf())
|
||||||
}
|
}
|
||||||
fn write_vectored_at(&self, _bufs: &[io::IoSlice], _offset: u64) -> Result<u64, Error> {
|
async fn write_vectored_at<'a>(
|
||||||
|
&self,
|
||||||
|
_bufs: &[io::IoSlice<'a>],
|
||||||
|
_offset: u64,
|
||||||
|
) -> Result<u64, Error> {
|
||||||
Err(Error::badf())
|
Err(Error::badf())
|
||||||
}
|
}
|
||||||
fn seek(&self, _pos: std::io::SeekFrom) -> Result<u64, Error> {
|
async fn seek(&self, _pos: std::io::SeekFrom) -> Result<u64, Error> {
|
||||||
Err(Error::seek_pipe())
|
Err(Error::seek_pipe())
|
||||||
}
|
}
|
||||||
fn peek(&self, _buf: &mut [u8]) -> Result<u64, Error> {
|
async fn peek(&self, _buf: &mut [u8]) -> Result<u64, Error> {
|
||||||
Err(Error::seek_pipe())
|
Err(Error::seek_pipe())
|
||||||
}
|
}
|
||||||
fn set_times(
|
async fn set_times(
|
||||||
&self,
|
&self,
|
||||||
atime: Option<wasi_common::SystemTimeSpec>,
|
atime: Option<wasi_common::SystemTimeSpec>,
|
||||||
mtime: Option<wasi_common::SystemTimeSpec>,
|
mtime: Option<wasi_common::SystemTimeSpec>,
|
||||||
@@ -91,9 +100,15 @@ impl WasiFile for Stdin {
|
|||||||
.set_times(convert_systimespec(atime), convert_systimespec(mtime))?;
|
.set_times(convert_systimespec(atime), convert_systimespec(mtime))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn num_ready_bytes(&self) -> Result<u64, Error> {
|
async fn num_ready_bytes(&self) -> Result<u64, Error> {
|
||||||
Ok(self.0.num_ready_bytes()?)
|
Ok(self.0.num_ready_bytes()?)
|
||||||
}
|
}
|
||||||
|
async fn readable(&mut self) -> Result<(), Error> {
|
||||||
|
Err(Error::badf())
|
||||||
|
}
|
||||||
|
async fn writable(&mut self) -> Result<(), Error> {
|
||||||
|
Err(Error::badf())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
impl AsRawHandle for Stdin {
|
impl AsRawHandle for Stdin {
|
||||||
@@ -110,31 +125,32 @@ impl AsRawFd for Stdin {
|
|||||||
|
|
||||||
macro_rules! wasi_file_write_impl {
|
macro_rules! wasi_file_write_impl {
|
||||||
($ty:ty) => {
|
($ty:ty) => {
|
||||||
|
#[async_trait::async_trait(?Send)]
|
||||||
impl WasiFile for $ty {
|
impl WasiFile for $ty {
|
||||||
fn as_any(&self) -> &dyn Any {
|
fn as_any(&self) -> &dyn Any {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
fn datasync(&self) -> Result<(), Error> {
|
async fn datasync(&self) -> Result<(), Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn sync(&self) -> Result<(), Error> {
|
async fn sync(&self) -> Result<(), Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn get_filetype(&self) -> Result<FileType, Error> {
|
async fn get_filetype(&self) -> Result<FileType, Error> {
|
||||||
Ok(FileType::Unknown)
|
Ok(FileType::Unknown)
|
||||||
}
|
}
|
||||||
fn get_fdflags(&self) -> Result<FdFlags, Error> {
|
async fn get_fdflags(&self) -> Result<FdFlags, Error> {
|
||||||
Ok(FdFlags::APPEND)
|
Ok(FdFlags::APPEND)
|
||||||
}
|
}
|
||||||
fn set_fdflags(&mut self, _fdflags: FdFlags) -> Result<(), Error> {
|
async fn set_fdflags(&mut self, _fdflags: FdFlags) -> Result<(), Error> {
|
||||||
Err(Error::badf())
|
Err(Error::badf())
|
||||||
}
|
}
|
||||||
fn get_filestat(&self) -> Result<Filestat, Error> {
|
async fn get_filestat(&self) -> Result<Filestat, Error> {
|
||||||
let meta = self.0.as_file_view().metadata()?;
|
let meta = self.0.as_file_view().metadata()?;
|
||||||
Ok(Filestat {
|
Ok(Filestat {
|
||||||
device_id: 0,
|
device_id: 0,
|
||||||
inode: 0,
|
inode: 0,
|
||||||
filetype: self.get_filetype()?,
|
filetype: self.get_filetype().await?,
|
||||||
nlink: 0,
|
nlink: 0,
|
||||||
size: meta.len(),
|
size: meta.len(),
|
||||||
atim: meta.accessed().ok(),
|
atim: meta.accessed().ok(),
|
||||||
@@ -142,39 +158,46 @@ macro_rules! wasi_file_write_impl {
|
|||||||
ctim: meta.created().ok(),
|
ctim: meta.created().ok(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
fn set_filestat_size(&self, _size: u64) -> Result<(), Error> {
|
async fn set_filestat_size(&self, _size: u64) -> Result<(), Error> {
|
||||||
Err(Error::badf())
|
Err(Error::badf())
|
||||||
}
|
}
|
||||||
fn advise(&self, _offset: u64, _len: u64, _advice: Advice) -> Result<(), Error> {
|
async fn advise(&self, _offset: u64, _len: u64, _advice: Advice) -> Result<(), Error> {
|
||||||
Err(Error::badf())
|
Err(Error::badf())
|
||||||
}
|
}
|
||||||
fn allocate(&self, _offset: u64, _len: u64) -> Result<(), Error> {
|
async fn allocate(&self, _offset: u64, _len: u64) -> Result<(), Error> {
|
||||||
Err(Error::badf())
|
Err(Error::badf())
|
||||||
}
|
}
|
||||||
fn read_vectored(&self, _bufs: &mut [io::IoSliceMut]) -> Result<u64, Error> {
|
async fn read_vectored<'a>(
|
||||||
Err(Error::badf())
|
|
||||||
}
|
|
||||||
fn read_vectored_at(
|
|
||||||
&self,
|
&self,
|
||||||
_bufs: &mut [io::IoSliceMut],
|
_bufs: &mut [io::IoSliceMut<'a>],
|
||||||
|
) -> Result<u64, Error> {
|
||||||
|
Err(Error::badf())
|
||||||
|
}
|
||||||
|
async fn read_vectored_at<'a>(
|
||||||
|
&self,
|
||||||
|
_bufs: &mut [io::IoSliceMut<'a>],
|
||||||
_offset: u64,
|
_offset: u64,
|
||||||
) -> Result<u64, Error> {
|
) -> Result<u64, Error> {
|
||||||
Err(Error::badf())
|
Err(Error::badf())
|
||||||
}
|
}
|
||||||
fn write_vectored(&self, bufs: &[io::IoSlice]) -> Result<u64, Error> {
|
async fn write_vectored<'a>(&self, bufs: &[io::IoSlice<'a>]) -> Result<u64, Error> {
|
||||||
let n = self.0.as_file_view().write_vectored(bufs)?;
|
let n = self.0.as_file_view().write_vectored(bufs)?;
|
||||||
Ok(n.try_into().map_err(|c| Error::range().context(c))?)
|
Ok(n.try_into().map_err(|c| Error::range().context(c))?)
|
||||||
}
|
}
|
||||||
fn write_vectored_at(&self, _bufs: &[io::IoSlice], _offset: u64) -> Result<u64, Error> {
|
async fn write_vectored_at<'a>(
|
||||||
|
&self,
|
||||||
|
_bufs: &[io::IoSlice<'a>],
|
||||||
|
_offset: u64,
|
||||||
|
) -> Result<u64, Error> {
|
||||||
Err(Error::seek_pipe())
|
Err(Error::seek_pipe())
|
||||||
}
|
}
|
||||||
fn seek(&self, _pos: std::io::SeekFrom) -> Result<u64, Error> {
|
async fn seek(&self, _pos: std::io::SeekFrom) -> Result<u64, Error> {
|
||||||
Err(Error::seek_pipe())
|
Err(Error::seek_pipe())
|
||||||
}
|
}
|
||||||
fn peek(&self, _buf: &mut [u8]) -> Result<u64, Error> {
|
async fn peek(&self, _buf: &mut [u8]) -> Result<u64, Error> {
|
||||||
Err(Error::badf())
|
Err(Error::badf())
|
||||||
}
|
}
|
||||||
fn set_times(
|
async fn set_times(
|
||||||
&self,
|
&self,
|
||||||
atime: Option<wasi_common::SystemTimeSpec>,
|
atime: Option<wasi_common::SystemTimeSpec>,
|
||||||
mtime: Option<wasi_common::SystemTimeSpec>,
|
mtime: Option<wasi_common::SystemTimeSpec>,
|
||||||
@@ -183,9 +206,15 @@ macro_rules! wasi_file_write_impl {
|
|||||||
.set_times(convert_systimespec(atime), convert_systimespec(mtime))?;
|
.set_times(convert_systimespec(atime), convert_systimespec(mtime))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn num_ready_bytes(&self) -> Result<u64, Error> {
|
async fn num_ready_bytes(&self) -> Result<u64, Error> {
|
||||||
Ok(0)
|
Ok(0)
|
||||||
}
|
}
|
||||||
|
async fn readable(&mut self) -> Result<(), Error> {
|
||||||
|
Err(Error::badf())
|
||||||
|
}
|
||||||
|
async fn writable(&mut self) -> Result<(), Error> {
|
||||||
|
Err(Error::badf())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
impl AsRawHandle for $ty {
|
impl AsRawHandle for $ty {
|
||||||
|
|||||||
@@ -5,12 +5,12 @@ pub enum SystemTimeSpec {
|
|||||||
Absolute(SystemTime),
|
Absolute(SystemTime),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait WasiSystemClock {
|
pub trait WasiSystemClock: Send + Sync {
|
||||||
fn resolution(&self) -> Duration;
|
fn resolution(&self) -> Duration;
|
||||||
fn now(&self, precision: Duration) -> SystemTime;
|
fn now(&self, precision: Duration) -> SystemTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait WasiMonotonicClock {
|
pub trait WasiMonotonicClock: Send + Sync {
|
||||||
fn resolution(&self) -> Duration;
|
fn resolution(&self) -> Duration;
|
||||||
fn now(&self, precision: Duration) -> Instant;
|
fn now(&self, precision: Duration) -> Instant;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,9 +6,10 @@ use std::cell::Ref;
|
|||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
pub trait WasiDir {
|
#[wiggle::async_trait]
|
||||||
|
pub trait WasiDir: Send + Sync {
|
||||||
fn as_any(&self) -> &dyn Any;
|
fn as_any(&self) -> &dyn Any;
|
||||||
fn open_file(
|
async fn open_file(
|
||||||
&self,
|
&self,
|
||||||
symlink_follow: bool,
|
symlink_follow: bool,
|
||||||
path: &str,
|
path: &str,
|
||||||
@@ -17,26 +18,33 @@ pub trait WasiDir {
|
|||||||
write: bool,
|
write: bool,
|
||||||
fdflags: FdFlags,
|
fdflags: FdFlags,
|
||||||
) -> Result<Box<dyn WasiFile>, Error>;
|
) -> Result<Box<dyn WasiFile>, Error>;
|
||||||
fn open_dir(&self, symlink_follow: bool, path: &str) -> Result<Box<dyn WasiDir>, Error>;
|
async fn open_dir(&self, symlink_follow: bool, path: &str) -> Result<Box<dyn WasiDir>, Error>;
|
||||||
fn create_dir(&self, path: &str) -> Result<(), Error>;
|
async fn create_dir(&self, path: &str) -> Result<(), Error>;
|
||||||
fn readdir(
|
// XXX the iterator here needs to be asyncified as well!
|
||||||
|
async fn readdir(
|
||||||
&self,
|
&self,
|
||||||
cursor: ReaddirCursor,
|
cursor: ReaddirCursor,
|
||||||
) -> Result<Box<dyn Iterator<Item = Result<ReaddirEntity, Error>>>, Error>;
|
) -> Result<Box<dyn Iterator<Item = Result<ReaddirEntity, Error>> + Send>, Error>;
|
||||||
fn symlink(&self, old_path: &str, new_path: &str) -> Result<(), Error>;
|
async fn symlink(&self, old_path: &str, new_path: &str) -> Result<(), Error>;
|
||||||
fn remove_dir(&self, path: &str) -> Result<(), Error>;
|
async fn remove_dir(&self, path: &str) -> Result<(), Error>;
|
||||||
fn unlink_file(&self, path: &str) -> Result<(), Error>;
|
async fn unlink_file(&self, path: &str) -> Result<(), Error>;
|
||||||
fn read_link(&self, path: &str) -> Result<PathBuf, Error>;
|
async fn read_link(&self, path: &str) -> Result<PathBuf, Error>;
|
||||||
fn get_filestat(&self) -> Result<Filestat, Error>;
|
async fn get_filestat(&self) -> Result<Filestat, Error>;
|
||||||
fn get_path_filestat(&self, path: &str, follow_symlinks: bool) -> Result<Filestat, Error>;
|
async fn get_path_filestat(&self, path: &str, follow_symlinks: bool)
|
||||||
fn rename(&self, path: &str, dest_dir: &dyn WasiDir, dest_path: &str) -> Result<(), Error>;
|
-> Result<Filestat, Error>;
|
||||||
fn hard_link(
|
async fn rename(
|
||||||
|
&self,
|
||||||
|
path: &str,
|
||||||
|
dest_dir: &dyn WasiDir,
|
||||||
|
dest_path: &str,
|
||||||
|
) -> Result<(), Error>;
|
||||||
|
async fn hard_link(
|
||||||
&self,
|
&self,
|
||||||
path: &str,
|
path: &str,
|
||||||
target_dir: &dyn WasiDir,
|
target_dir: &dyn WasiDir,
|
||||||
target_path: &str,
|
target_path: &str,
|
||||||
) -> Result<(), Error>;
|
) -> Result<(), Error>;
|
||||||
fn set_times(
|
async fn set_times(
|
||||||
&self,
|
&self,
|
||||||
path: &str,
|
path: &str,
|
||||||
atime: Option<SystemTimeSpec>,
|
atime: Option<SystemTimeSpec>,
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
//! The real value of using `anyhow::Error` here is being able to use
|
//! The real value of using `anyhow::Error` here is being able to use
|
||||||
//! `anyhow::Result::context` to aid in debugging of errors.
|
//! `anyhow::Result::context` to aid in debugging of errors.
|
||||||
|
|
||||||
pub use anyhow::Error;
|
pub use anyhow::{Context, Error};
|
||||||
|
|
||||||
/// Internal error type for the `wasi-common` crate.
|
/// Internal error type for the `wasi-common` crate.
|
||||||
/// Contains variants of the WASI `$errno` type are added according to what is actually used internally by
|
/// Contains variants of the WASI `$errno` type are added according to what is actually used internally by
|
||||||
|
|||||||
@@ -4,30 +4,41 @@ use std::any::Any;
|
|||||||
use std::cell::{Ref, RefMut};
|
use std::cell::{Ref, RefMut};
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
pub trait WasiFile {
|
#[wiggle::async_trait]
|
||||||
|
pub trait WasiFile: Send {
|
||||||
fn as_any(&self) -> &dyn Any;
|
fn as_any(&self) -> &dyn Any;
|
||||||
fn datasync(&self) -> Result<(), Error>; // write op
|
async fn datasync(&self) -> Result<(), Error>; // write op
|
||||||
fn sync(&self) -> Result<(), Error>; // file op
|
async fn sync(&self) -> Result<(), Error>; // file op
|
||||||
fn get_filetype(&self) -> Result<FileType, Error>; // file op
|
async fn get_filetype(&self) -> Result<FileType, Error>; // file op
|
||||||
fn get_fdflags(&self) -> Result<FdFlags, Error>; // file op
|
async fn get_fdflags(&self) -> Result<FdFlags, Error>; // file op
|
||||||
fn set_fdflags(&mut self, flags: FdFlags) -> Result<(), Error>; // file op
|
async fn set_fdflags(&mut self, flags: FdFlags) -> Result<(), Error>; // file op
|
||||||
fn get_filestat(&self) -> Result<Filestat, Error>; // split out get_length as a read & write op, rest is a file op
|
async fn get_filestat(&self) -> Result<Filestat, Error>; // split out get_length as a read & write op, rest is a file op
|
||||||
fn set_filestat_size(&self, _size: u64) -> Result<(), Error>; // write op
|
async fn set_filestat_size(&self, _size: u64) -> Result<(), Error>; // write op
|
||||||
fn advise(&self, offset: u64, len: u64, advice: Advice) -> Result<(), Error>; // file op
|
async fn advise(&self, offset: u64, len: u64, advice: Advice) -> Result<(), Error>; // file op
|
||||||
fn allocate(&self, offset: u64, len: u64) -> Result<(), Error>; // write op
|
async fn allocate(&self, offset: u64, len: u64) -> Result<(), Error>; // write op
|
||||||
fn set_times(
|
async fn set_times(
|
||||||
&self,
|
&self,
|
||||||
atime: Option<SystemTimeSpec>,
|
atime: Option<SystemTimeSpec>,
|
||||||
mtime: Option<SystemTimeSpec>,
|
mtime: Option<SystemTimeSpec>,
|
||||||
) -> Result<(), Error>;
|
) -> Result<(), Error>;
|
||||||
fn read_vectored(&self, bufs: &mut [std::io::IoSliceMut]) -> Result<u64, Error>; // read op
|
async fn read_vectored<'a>(&self, bufs: &mut [std::io::IoSliceMut<'a>]) -> Result<u64, Error>; // read op
|
||||||
fn read_vectored_at(&self, bufs: &mut [std::io::IoSliceMut], offset: u64)
|
async fn read_vectored_at<'a>(
|
||||||
-> Result<u64, Error>; // file op
|
&self,
|
||||||
fn write_vectored(&self, bufs: &[std::io::IoSlice]) -> Result<u64, Error>; // write op
|
bufs: &mut [std::io::IoSliceMut<'a>],
|
||||||
fn write_vectored_at(&self, bufs: &[std::io::IoSlice], offset: u64) -> Result<u64, Error>; // file op
|
offset: u64,
|
||||||
fn seek(&self, pos: std::io::SeekFrom) -> Result<u64, Error>; // file op that generates a new stream from a file will supercede this
|
) -> Result<u64, Error>; // file op
|
||||||
fn peek(&self, buf: &mut [u8]) -> Result<u64, Error>; // read op
|
async fn write_vectored<'a>(&self, bufs: &[std::io::IoSlice<'a>]) -> Result<u64, Error>; // write op
|
||||||
fn num_ready_bytes(&self) -> Result<u64, Error>; // read op
|
async fn write_vectored_at<'a>(
|
||||||
|
&self,
|
||||||
|
bufs: &[std::io::IoSlice<'a>],
|
||||||
|
offset: u64,
|
||||||
|
) -> Result<u64, Error>; // file op
|
||||||
|
async fn seek(&self, pos: std::io::SeekFrom) -> Result<u64, Error>; // file op that generates a new stream from a file will supercede this
|
||||||
|
async fn peek(&self, buf: &mut [u8]) -> Result<u64, Error>; // read op
|
||||||
|
async fn num_ready_bytes(&self) -> Result<u64, Error>; // read op
|
||||||
|
|
||||||
|
async fn readable(&mut self) -> Result<(), Error>;
|
||||||
|
async fn writable(&mut self) -> Result<(), Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
@@ -111,11 +122,11 @@ impl FileEntry {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_fdstat(&self) -> Result<FdStat, Error> {
|
pub async fn get_fdstat(&self) -> Result<FdStat, Error> {
|
||||||
Ok(FdStat {
|
Ok(FdStat {
|
||||||
filetype: self.file.get_filetype()?,
|
filetype: self.file.get_filetype().await?,
|
||||||
caps: self.caps,
|
caps: self.caps,
|
||||||
flags: self.file.get_fdflags()?,
|
flags: self.file.get_fdflags().await?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ pub use cap_rand::RngCore;
|
|||||||
pub use clocks::{SystemTimeSpec, WasiClocks, WasiMonotonicClock, WasiSystemClock};
|
pub use clocks::{SystemTimeSpec, WasiClocks, WasiMonotonicClock, WasiSystemClock};
|
||||||
pub use ctx::{WasiCtx, WasiCtxBuilder};
|
pub use ctx::{WasiCtx, WasiCtxBuilder};
|
||||||
pub use dir::WasiDir;
|
pub use dir::WasiDir;
|
||||||
pub use error::{Error, ErrorExt, ErrorKind};
|
pub use error::{Context, Error, ErrorExt, ErrorKind};
|
||||||
pub use file::WasiFile;
|
pub use file::WasiFile;
|
||||||
pub use sched::{Poll, WasiSched};
|
pub use sched::{Poll, WasiSched};
|
||||||
pub use string_array::StringArrayError;
|
pub use string_array::StringArrayError;
|
||||||
|
|||||||
@@ -105,30 +105,31 @@ impl From<&str> for ReadPipe<io::Cursor<String>> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Read + Any> WasiFile for ReadPipe<R> {
|
#[wiggle::async_trait]
|
||||||
|
impl<R: Read + Any + Send + Sync> WasiFile for ReadPipe<R> {
|
||||||
fn as_any(&self) -> &dyn Any {
|
fn as_any(&self) -> &dyn Any {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
fn datasync(&self) -> Result<(), Error> {
|
async fn datasync(&self) -> Result<(), Error> {
|
||||||
Ok(()) // trivial: no implementation needed
|
Ok(()) // trivial: no implementation needed
|
||||||
}
|
}
|
||||||
fn sync(&self) -> Result<(), Error> {
|
async fn sync(&self) -> Result<(), Error> {
|
||||||
Ok(()) // trivial
|
Ok(()) // trivial
|
||||||
}
|
}
|
||||||
fn get_filetype(&self) -> Result<FileType, Error> {
|
async fn get_filetype(&self) -> Result<FileType, Error> {
|
||||||
Ok(FileType::Pipe)
|
Ok(FileType::Pipe)
|
||||||
}
|
}
|
||||||
fn get_fdflags(&self) -> Result<FdFlags, Error> {
|
async fn get_fdflags(&self) -> Result<FdFlags, Error> {
|
||||||
Ok(FdFlags::empty())
|
Ok(FdFlags::empty())
|
||||||
}
|
}
|
||||||
fn set_fdflags(&mut self, _fdflags: FdFlags) -> Result<(), Error> {
|
async fn set_fdflags(&mut self, _fdflags: FdFlags) -> Result<(), Error> {
|
||||||
Err(Error::badf())
|
Err(Error::badf())
|
||||||
}
|
}
|
||||||
fn get_filestat(&self) -> Result<Filestat, Error> {
|
async fn get_filestat(&self) -> Result<Filestat, Error> {
|
||||||
Ok(Filestat {
|
Ok(Filestat {
|
||||||
device_id: 0,
|
device_id: 0,
|
||||||
inode: 0,
|
inode: 0,
|
||||||
filetype: self.get_filetype()?,
|
filetype: self.get_filetype().await?,
|
||||||
nlink: 0,
|
nlink: 0,
|
||||||
size: 0, // XXX no way to get a size out of a Read :(
|
size: 0, // XXX no way to get a size out of a Read :(
|
||||||
atim: None,
|
atim: None,
|
||||||
@@ -136,44 +137,58 @@ impl<R: Read + Any> WasiFile for ReadPipe<R> {
|
|||||||
ctim: None,
|
ctim: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
fn set_filestat_size(&self, _size: u64) -> Result<(), Error> {
|
async fn set_filestat_size(&self, _size: u64) -> Result<(), Error> {
|
||||||
Err(Error::badf())
|
Err(Error::badf())
|
||||||
}
|
}
|
||||||
fn advise(&self, offset: u64, len: u64, advice: Advice) -> Result<(), Error> {
|
async fn advise(&self, offset: u64, len: u64, advice: Advice) -> Result<(), Error> {
|
||||||
Err(Error::badf())
|
Err(Error::badf())
|
||||||
}
|
}
|
||||||
fn allocate(&self, offset: u64, len: u64) -> Result<(), Error> {
|
async fn allocate(&self, offset: u64, len: u64) -> Result<(), Error> {
|
||||||
Err(Error::badf())
|
Err(Error::badf())
|
||||||
}
|
}
|
||||||
fn read_vectored(&self, bufs: &mut [io::IoSliceMut]) -> Result<u64, Error> {
|
async fn read_vectored<'a>(&self, bufs: &mut [io::IoSliceMut<'a>]) -> Result<u64, Error> {
|
||||||
let n = self.borrow().read_vectored(bufs)?;
|
let n = self.borrow().read_vectored(bufs)?;
|
||||||
Ok(n.try_into()?)
|
Ok(n.try_into()?)
|
||||||
}
|
}
|
||||||
fn read_vectored_at(&self, bufs: &mut [io::IoSliceMut], offset: u64) -> Result<u64, Error> {
|
async fn read_vectored_at<'a>(
|
||||||
|
&self,
|
||||||
|
bufs: &mut [io::IoSliceMut<'a>],
|
||||||
|
offset: u64,
|
||||||
|
) -> Result<u64, Error> {
|
||||||
Err(Error::badf())
|
Err(Error::badf())
|
||||||
}
|
}
|
||||||
fn write_vectored(&self, bufs: &[io::IoSlice]) -> Result<u64, Error> {
|
async fn write_vectored<'a>(&self, bufs: &[io::IoSlice<'a>]) -> Result<u64, Error> {
|
||||||
Err(Error::badf())
|
Err(Error::badf())
|
||||||
}
|
}
|
||||||
fn write_vectored_at(&self, bufs: &[io::IoSlice], offset: u64) -> Result<u64, Error> {
|
async fn write_vectored_at<'a>(
|
||||||
|
&self,
|
||||||
|
bufs: &[io::IoSlice<'a>],
|
||||||
|
offset: u64,
|
||||||
|
) -> Result<u64, Error> {
|
||||||
Err(Error::badf())
|
Err(Error::badf())
|
||||||
}
|
}
|
||||||
fn seek(&self, pos: std::io::SeekFrom) -> Result<u64, Error> {
|
async fn seek(&self, pos: std::io::SeekFrom) -> Result<u64, Error> {
|
||||||
Err(Error::badf())
|
Err(Error::badf())
|
||||||
}
|
}
|
||||||
fn peek(&self, buf: &mut [u8]) -> Result<u64, Error> {
|
async fn peek(&self, buf: &mut [u8]) -> Result<u64, Error> {
|
||||||
Err(Error::badf())
|
Err(Error::badf())
|
||||||
}
|
}
|
||||||
fn set_times(
|
async fn set_times(
|
||||||
&self,
|
&self,
|
||||||
atime: Option<SystemTimeSpec>,
|
atime: Option<SystemTimeSpec>,
|
||||||
mtime: Option<SystemTimeSpec>,
|
mtime: Option<SystemTimeSpec>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
Err(Error::badf())
|
Err(Error::badf())
|
||||||
}
|
}
|
||||||
fn num_ready_bytes(&self) -> Result<u64, Error> {
|
async fn num_ready_bytes(&self) -> Result<u64, Error> {
|
||||||
Ok(0)
|
Ok(0)
|
||||||
}
|
}
|
||||||
|
async fn readable(&mut self) -> Result<(), Error> {
|
||||||
|
Err(Error::badf())
|
||||||
|
}
|
||||||
|
async fn writable(&mut self) -> Result<(), Error> {
|
||||||
|
Err(Error::badf())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A virtual pipe write end.
|
/// A virtual pipe write end.
|
||||||
@@ -249,30 +264,31 @@ impl WritePipe<io::Cursor<Vec<u8>>> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W: Write + Any> WasiFile for WritePipe<W> {
|
#[wiggle::async_trait]
|
||||||
|
impl<W: Write + Any + Send + Sync> WasiFile for WritePipe<W> {
|
||||||
fn as_any(&self) -> &dyn Any {
|
fn as_any(&self) -> &dyn Any {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
fn datasync(&self) -> Result<(), Error> {
|
async fn datasync(&self) -> Result<(), Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn sync(&self) -> Result<(), Error> {
|
async fn sync(&self) -> Result<(), Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn get_filetype(&self) -> Result<FileType, Error> {
|
async fn get_filetype(&self) -> Result<FileType, Error> {
|
||||||
Ok(FileType::Pipe)
|
Ok(FileType::Pipe)
|
||||||
}
|
}
|
||||||
fn get_fdflags(&self) -> Result<FdFlags, Error> {
|
async fn get_fdflags(&self) -> Result<FdFlags, Error> {
|
||||||
Ok(FdFlags::APPEND)
|
Ok(FdFlags::APPEND)
|
||||||
}
|
}
|
||||||
fn set_fdflags(&mut self, _fdflags: FdFlags) -> Result<(), Error> {
|
async fn set_fdflags(&mut self, _fdflags: FdFlags) -> Result<(), Error> {
|
||||||
Err(Error::badf())
|
Err(Error::badf())
|
||||||
}
|
}
|
||||||
fn get_filestat(&self) -> Result<Filestat, Error> {
|
async fn get_filestat(&self) -> Result<Filestat, Error> {
|
||||||
Ok(Filestat {
|
Ok(Filestat {
|
||||||
device_id: 0,
|
device_id: 0,
|
||||||
inode: 0,
|
inode: 0,
|
||||||
filetype: self.get_filetype()?,
|
filetype: self.get_filetype().await?,
|
||||||
nlink: 0,
|
nlink: 0,
|
||||||
size: 0, // XXX no way to get a size out of a Write :(
|
size: 0, // XXX no way to get a size out of a Write :(
|
||||||
atim: None,
|
atim: None,
|
||||||
@@ -280,42 +296,56 @@ impl<W: Write + Any> WasiFile for WritePipe<W> {
|
|||||||
ctim: None,
|
ctim: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
fn set_filestat_size(&self, _size: u64) -> Result<(), Error> {
|
async fn set_filestat_size(&self, _size: u64) -> Result<(), Error> {
|
||||||
Err(Error::badf())
|
Err(Error::badf())
|
||||||
}
|
}
|
||||||
fn advise(&self, offset: u64, len: u64, advice: Advice) -> Result<(), Error> {
|
async fn advise(&self, offset: u64, len: u64, advice: Advice) -> Result<(), Error> {
|
||||||
Err(Error::badf())
|
Err(Error::badf())
|
||||||
}
|
}
|
||||||
fn allocate(&self, offset: u64, len: u64) -> Result<(), Error> {
|
async fn allocate(&self, offset: u64, len: u64) -> Result<(), Error> {
|
||||||
Err(Error::badf())
|
Err(Error::badf())
|
||||||
}
|
}
|
||||||
fn read_vectored(&self, bufs: &mut [io::IoSliceMut]) -> Result<u64, Error> {
|
async fn read_vectored<'a>(&self, bufs: &mut [io::IoSliceMut<'a>]) -> Result<u64, Error> {
|
||||||
Err(Error::badf())
|
Err(Error::badf())
|
||||||
}
|
}
|
||||||
fn read_vectored_at(&self, bufs: &mut [io::IoSliceMut], offset: u64) -> Result<u64, Error> {
|
async fn read_vectored_at<'a>(
|
||||||
|
&self,
|
||||||
|
bufs: &mut [io::IoSliceMut<'a>],
|
||||||
|
offset: u64,
|
||||||
|
) -> Result<u64, Error> {
|
||||||
Err(Error::badf())
|
Err(Error::badf())
|
||||||
}
|
}
|
||||||
fn write_vectored(&self, bufs: &[io::IoSlice]) -> Result<u64, Error> {
|
async fn write_vectored<'a>(&self, bufs: &[io::IoSlice<'a>]) -> Result<u64, Error> {
|
||||||
let n = self.borrow().write_vectored(bufs)?;
|
let n = self.borrow().write_vectored(bufs)?;
|
||||||
Ok(n.try_into()?)
|
Ok(n.try_into()?)
|
||||||
}
|
}
|
||||||
fn write_vectored_at(&self, bufs: &[io::IoSlice], offset: u64) -> Result<u64, Error> {
|
async fn write_vectored_at<'a>(
|
||||||
|
&self,
|
||||||
|
bufs: &[io::IoSlice<'a>],
|
||||||
|
offset: u64,
|
||||||
|
) -> Result<u64, Error> {
|
||||||
Err(Error::badf())
|
Err(Error::badf())
|
||||||
}
|
}
|
||||||
fn seek(&self, pos: std::io::SeekFrom) -> Result<u64, Error> {
|
async fn seek(&self, pos: std::io::SeekFrom) -> Result<u64, Error> {
|
||||||
Err(Error::badf())
|
Err(Error::badf())
|
||||||
}
|
}
|
||||||
fn peek(&self, buf: &mut [u8]) -> Result<u64, Error> {
|
async fn peek(&self, buf: &mut [u8]) -> Result<u64, Error> {
|
||||||
Err(Error::badf())
|
Err(Error::badf())
|
||||||
}
|
}
|
||||||
fn set_times(
|
async fn set_times(
|
||||||
&self,
|
&self,
|
||||||
atime: Option<SystemTimeSpec>,
|
atime: Option<SystemTimeSpec>,
|
||||||
mtime: Option<SystemTimeSpec>,
|
mtime: Option<SystemTimeSpec>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
Err(Error::badf())
|
Err(Error::badf())
|
||||||
}
|
}
|
||||||
fn num_ready_bytes(&self) -> Result<u64, Error> {
|
async fn num_ready_bytes(&self) -> Result<u64, Error> {
|
||||||
Ok(0)
|
Ok(0)
|
||||||
}
|
}
|
||||||
|
async fn readable(&mut self) -> Result<(), Error> {
|
||||||
|
Err(Error::badf())
|
||||||
|
}
|
||||||
|
async fn writable(&mut self) -> Result<(), Error> {
|
||||||
|
Err(Error::badf())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,22 @@
|
|||||||
use crate::clocks::WasiMonotonicClock;
|
use crate::clocks::WasiMonotonicClock;
|
||||||
use crate::file::WasiFile;
|
use crate::file::WasiFile;
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
use cap_std::time::{Duration, Instant};
|
use cap_std::time::Instant;
|
||||||
use std::cell::Ref;
|
|
||||||
pub mod subscription;
|
pub mod subscription;
|
||||||
|
pub use cap_std::time::Duration;
|
||||||
|
|
||||||
use subscription::{MonotonicClockSubscription, RwSubscription, Subscription, SubscriptionResult};
|
pub use subscription::{
|
||||||
|
MonotonicClockSubscription, RwEventFlags, RwSubscription, Subscription, SubscriptionResult,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[wiggle::async_trait]
|
||||||
pub trait WasiSched {
|
pub trait WasiSched {
|
||||||
fn poll_oneoff(&self, poll: &Poll) -> Result<(), Error>;
|
async fn poll_oneoff<'a>(&self, poll: &mut Poll<'a>) -> Result<(), Error>;
|
||||||
fn sched_yield(&self) -> Result<(), Error>;
|
async fn sched_yield(&self) -> Result<(), Error>;
|
||||||
fn sleep(&self, duration: Duration) -> Result<(), Error>;
|
async fn sleep(&self, duration: Duration) -> Result<(), Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
pub struct Userdata(u64);
|
pub struct Userdata(u64);
|
||||||
impl From<u64> for Userdata {
|
impl From<u64> for Userdata {
|
||||||
fn from(u: u64) -> Userdata {
|
fn from(u: u64) -> Userdata {
|
||||||
@@ -26,6 +30,8 @@ impl From<Userdata> for u64 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type PollResults = Vec<(SubscriptionResult, Userdata)>;
|
||||||
|
|
||||||
pub struct Poll<'a> {
|
pub struct Poll<'a> {
|
||||||
subs: Vec<(Subscription<'a>, Userdata)>,
|
subs: Vec<(Subscription<'a>, Userdata)>,
|
||||||
}
|
}
|
||||||
@@ -50,11 +56,11 @@ impl<'a> Poll<'a> {
|
|||||||
ud,
|
ud,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
pub fn subscribe_read(&mut self, file: Ref<'a, dyn WasiFile>, ud: Userdata) {
|
pub fn subscribe_read(&mut self, file: &'a mut dyn WasiFile, ud: Userdata) {
|
||||||
self.subs
|
self.subs
|
||||||
.push((Subscription::Read(RwSubscription::new(file)), ud));
|
.push((Subscription::Read(RwSubscription::new(file)), ud));
|
||||||
}
|
}
|
||||||
pub fn subscribe_write(&mut self, file: Ref<'a, dyn WasiFile>, ud: Userdata) {
|
pub fn subscribe_write(&mut self, file: &'a mut dyn WasiFile, ud: Userdata) {
|
||||||
self.subs
|
self.subs
|
||||||
.push((Subscription::Write(RwSubscription::new(file)), ud));
|
.push((Subscription::Write(RwSubscription::new(file)), ud));
|
||||||
}
|
}
|
||||||
@@ -67,7 +73,7 @@ impl<'a> Poll<'a> {
|
|||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.subs.is_empty()
|
self.subs.is_empty()
|
||||||
}
|
}
|
||||||
pub fn earliest_clock_deadline(&'a self) -> Option<&MonotonicClockSubscription<'a>> {
|
pub fn earliest_clock_deadline(&self) -> Option<&MonotonicClockSubscription<'a>> {
|
||||||
self.subs
|
self.subs
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|(s, _ud)| match s {
|
.filter_map(|(s, _ud)| match s {
|
||||||
@@ -76,8 +82,8 @@ impl<'a> Poll<'a> {
|
|||||||
})
|
})
|
||||||
.min_by(|a, b| a.deadline.cmp(&b.deadline))
|
.min_by(|a, b| a.deadline.cmp(&b.deadline))
|
||||||
}
|
}
|
||||||
pub fn rw_subscriptions(&'a self) -> impl Iterator<Item = &Subscription<'a>> {
|
pub fn rw_subscriptions<'b>(&'b mut self) -> impl Iterator<Item = &'b mut Subscription<'a>> {
|
||||||
self.subs.iter().filter_map(|(s, _ud)| match s {
|
self.subs.iter_mut().filter_map(|(s, _ud)| match s {
|
||||||
Subscription::Read { .. } | Subscription::Write { .. } => Some(s),
|
Subscription::Read { .. } | Subscription::Write { .. } => Some(s),
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use crate::file::WasiFile;
|
|||||||
use crate::Error;
|
use crate::Error;
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
use cap_std::time::{Duration, Instant};
|
use cap_std::time::{Duration, Instant};
|
||||||
use std::cell::{Cell, Ref};
|
use std::cell::Cell;
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
pub struct RwEventFlags: u32 {
|
pub struct RwEventFlags: u32 {
|
||||||
@@ -12,12 +12,12 @@ bitflags! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct RwSubscription<'a> {
|
pub struct RwSubscription<'a> {
|
||||||
pub file: Ref<'a, dyn WasiFile>,
|
pub file: &'a mut dyn WasiFile,
|
||||||
status: Cell<Option<Result<(u64, RwEventFlags), Error>>>,
|
status: Cell<Option<Result<(u64, RwEventFlags), Error>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> RwSubscription<'a> {
|
impl<'a> RwSubscription<'a> {
|
||||||
pub fn new(file: Ref<'a, dyn WasiFile>) -> Self {
|
pub fn new(file: &'a mut dyn WasiFile) -> Self {
|
||||||
Self {
|
Self {
|
||||||
file,
|
file,
|
||||||
status: Cell::new(None),
|
status: Cell::new(None),
|
||||||
@@ -29,8 +29,8 @@ impl<'a> RwSubscription<'a> {
|
|||||||
pub fn error(&self, error: Error) {
|
pub fn error(&self, error: Error) {
|
||||||
self.status.set(Some(Err(error)))
|
self.status.set(Some(Err(error)))
|
||||||
}
|
}
|
||||||
pub fn result(self) -> Option<Result<(u64, RwEventFlags), Error>> {
|
pub fn result(&self) -> Option<Result<(u64, RwEventFlags), Error>> {
|
||||||
self.status.into_inner()
|
self.status.take()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,6 +62,7 @@ pub enum Subscription<'a> {
|
|||||||
MonotonicClock(MonotonicClockSubscription<'a>),
|
MonotonicClock(MonotonicClockSubscription<'a>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum SubscriptionResult {
|
pub enum SubscriptionResult {
|
||||||
Read(Result<(u64, RwEventFlags), Error>),
|
Read(Result<(u64, RwEventFlags), Error>),
|
||||||
Write(Result<(u64, RwEventFlags), Error>),
|
Write(Result<(u64, RwEventFlags), Error>),
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
use crate::file::{FileCaps, FileEntryExt, TableFileExt};
|
use crate::file::{FileCaps, FileEntryExt, FileEntryMutExt, TableFileExt, WasiFile};
|
||||||
use crate::sched::{
|
use crate::sched::{
|
||||||
subscription::{RwEventFlags, SubscriptionResult},
|
subscription::{RwEventFlags, SubscriptionResult},
|
||||||
Poll,
|
Poll, Userdata,
|
||||||
};
|
};
|
||||||
use crate::snapshots::preview_1::types as snapshot1_types;
|
use crate::snapshots::preview_1::types as snapshot1_types;
|
||||||
use crate::snapshots::preview_1::wasi_snapshot_preview1::WasiSnapshotPreview1 as Snapshot1;
|
use crate::snapshots::preview_1::wasi_snapshot_preview1::WasiSnapshotPreview1 as Snapshot1;
|
||||||
use crate::{Error, ErrorExt, WasiCtx};
|
use crate::{Error, ErrorExt, WasiCtx};
|
||||||
use cap_std::time::Duration;
|
use cap_std::time::Duration;
|
||||||
|
use std::cell::RefMut;
|
||||||
|
use std::collections::HashSet;
|
||||||
use std::convert::{TryFrom, TryInto};
|
use std::convert::{TryFrom, TryInto};
|
||||||
use std::io::{IoSlice, IoSliceMut};
|
use std::io::{IoSlice, IoSliceMut};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
@@ -16,6 +18,7 @@ use wiggle::GuestPtr;
|
|||||||
wiggle::from_witx!({
|
wiggle::from_witx!({
|
||||||
witx: ["$WASI_ROOT/phases/old/snapshot_0/witx/wasi_unstable.witx"],
|
witx: ["$WASI_ROOT/phases/old/snapshot_0/witx/wasi_unstable.witx"],
|
||||||
errors: { errno => Error },
|
errors: { errno => Error },
|
||||||
|
async: *,
|
||||||
});
|
});
|
||||||
|
|
||||||
impl wiggle::GuestErrorType for types::Errno {
|
impl wiggle::GuestErrorType for types::Errno {
|
||||||
@@ -332,79 +335,80 @@ convert_flags_bidirectional!(
|
|||||||
|
|
||||||
// This implementation, wherever possible, delegates directly to the Snapshot1 implementation,
|
// This implementation, wherever possible, delegates directly to the Snapshot1 implementation,
|
||||||
// performing the no-op type conversions along the way.
|
// performing the no-op type conversions along the way.
|
||||||
impl<'a> wasi_unstable::WasiUnstable for WasiCtx {
|
#[wiggle::async_trait]
|
||||||
fn args_get<'b>(
|
impl wasi_unstable::WasiUnstable for WasiCtx {
|
||||||
|
async fn args_get<'a>(
|
||||||
&self,
|
&self,
|
||||||
argv: &GuestPtr<'b, GuestPtr<'b, u8>>,
|
argv: &GuestPtr<'a, GuestPtr<'a, u8>>,
|
||||||
argv_buf: &GuestPtr<'b, u8>,
|
argv_buf: &GuestPtr<'a, u8>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
Snapshot1::args_get(self, argv, argv_buf)
|
Snapshot1::args_get(self, argv, argv_buf).await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn args_sizes_get(&self) -> Result<(types::Size, types::Size), Error> {
|
async fn args_sizes_get(&self) -> Result<(types::Size, types::Size), Error> {
|
||||||
Snapshot1::args_sizes_get(self)
|
Snapshot1::args_sizes_get(self).await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn environ_get<'b>(
|
async fn environ_get<'a>(
|
||||||
&self,
|
&self,
|
||||||
environ: &GuestPtr<'b, GuestPtr<'b, u8>>,
|
environ: &GuestPtr<'a, GuestPtr<'a, u8>>,
|
||||||
environ_buf: &GuestPtr<'b, u8>,
|
environ_buf: &GuestPtr<'a, u8>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
Snapshot1::environ_get(self, environ, environ_buf)
|
Snapshot1::environ_get(self, environ, environ_buf).await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn environ_sizes_get(&self) -> Result<(types::Size, types::Size), Error> {
|
async fn environ_sizes_get(&self) -> Result<(types::Size, types::Size), Error> {
|
||||||
Snapshot1::environ_sizes_get(self)
|
Snapshot1::environ_sizes_get(self).await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clock_res_get(&self, id: types::Clockid) -> Result<types::Timestamp, Error> {
|
async fn clock_res_get(&self, id: types::Clockid) -> Result<types::Timestamp, Error> {
|
||||||
Snapshot1::clock_res_get(self, id.into())
|
Snapshot1::clock_res_get(self, id.into()).await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clock_time_get(
|
async fn clock_time_get(
|
||||||
&self,
|
&self,
|
||||||
id: types::Clockid,
|
id: types::Clockid,
|
||||||
precision: types::Timestamp,
|
precision: types::Timestamp,
|
||||||
) -> Result<types::Timestamp, Error> {
|
) -> Result<types::Timestamp, Error> {
|
||||||
Snapshot1::clock_time_get(self, id.into(), precision)
|
Snapshot1::clock_time_get(self, id.into(), precision).await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fd_advise(
|
async fn fd_advise(
|
||||||
&self,
|
&self,
|
||||||
fd: types::Fd,
|
fd: types::Fd,
|
||||||
offset: types::Filesize,
|
offset: types::Filesize,
|
||||||
len: types::Filesize,
|
len: types::Filesize,
|
||||||
advice: types::Advice,
|
advice: types::Advice,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
Snapshot1::fd_advise(self, fd.into(), offset, len, advice.into())
|
Snapshot1::fd_advise(self, fd.into(), offset, len, advice.into()).await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fd_allocate(
|
async fn fd_allocate(
|
||||||
&self,
|
&self,
|
||||||
fd: types::Fd,
|
fd: types::Fd,
|
||||||
offset: types::Filesize,
|
offset: types::Filesize,
|
||||||
len: types::Filesize,
|
len: types::Filesize,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
Snapshot1::fd_allocate(self, fd.into(), offset, len)
|
Snapshot1::fd_allocate(self, fd.into(), offset, len).await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fd_close(&self, fd: types::Fd) -> Result<(), Error> {
|
async fn fd_close(&self, fd: types::Fd) -> Result<(), Error> {
|
||||||
Snapshot1::fd_close(self, fd.into())
|
Snapshot1::fd_close(self, fd.into()).await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fd_datasync(&self, fd: types::Fd) -> Result<(), Error> {
|
async fn fd_datasync(&self, fd: types::Fd) -> Result<(), Error> {
|
||||||
Snapshot1::fd_datasync(self, fd.into())
|
Snapshot1::fd_datasync(self, fd.into()).await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fd_fdstat_get(&self, fd: types::Fd) -> Result<types::Fdstat, Error> {
|
async fn fd_fdstat_get(&self, fd: types::Fd) -> Result<types::Fdstat, Error> {
|
||||||
Ok(Snapshot1::fd_fdstat_get(self, fd.into())?.into())
|
Ok(Snapshot1::fd_fdstat_get(self, fd.into()).await?.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fd_fdstat_set_flags(&self, fd: types::Fd, flags: types::Fdflags) -> Result<(), Error> {
|
async fn fd_fdstat_set_flags(&self, fd: types::Fd, flags: types::Fdflags) -> Result<(), Error> {
|
||||||
Snapshot1::fd_fdstat_set_flags(self, fd.into(), flags.into())
|
Snapshot1::fd_fdstat_set_flags(self, fd.into(), flags.into()).await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fd_fdstat_set_rights(
|
async fn fd_fdstat_set_rights(
|
||||||
&self,
|
&self,
|
||||||
fd: types::Fd,
|
fd: types::Fd,
|
||||||
fs_rights_base: types::Rights,
|
fs_rights_base: types::Rights,
|
||||||
@@ -416,24 +420,29 @@ impl<'a> wasi_unstable::WasiUnstable for WasiCtx {
|
|||||||
fs_rights_base.into(),
|
fs_rights_base.into(),
|
||||||
fs_rights_inheriting.into(),
|
fs_rights_inheriting.into(),
|
||||||
)
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fd_filestat_get(&self, fd: types::Fd) -> Result<types::Filestat, Error> {
|
async fn fd_filestat_get(&self, fd: types::Fd) -> Result<types::Filestat, Error> {
|
||||||
Ok(Snapshot1::fd_filestat_get(self, fd.into())?.into())
|
Ok(Snapshot1::fd_filestat_get(self, fd.into()).await?.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fd_filestat_set_size(&self, fd: types::Fd, size: types::Filesize) -> Result<(), Error> {
|
async fn fd_filestat_set_size(
|
||||||
Snapshot1::fd_filestat_set_size(self, fd.into(), size)
|
&self,
|
||||||
|
fd: types::Fd,
|
||||||
|
size: types::Filesize,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
Snapshot1::fd_filestat_set_size(self, fd.into(), size).await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fd_filestat_set_times(
|
async fn fd_filestat_set_times(
|
||||||
&self,
|
&self,
|
||||||
fd: types::Fd,
|
fd: types::Fd,
|
||||||
atim: types::Timestamp,
|
atim: types::Timestamp,
|
||||||
mtim: types::Timestamp,
|
mtim: types::Timestamp,
|
||||||
fst_flags: types::Fstflags,
|
fst_flags: types::Fstflags,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
Snapshot1::fd_filestat_set_times(self, fd.into(), atim, mtim, fst_flags.into())
|
Snapshot1::fd_filestat_set_times(self, fd.into(), atim, mtim, fst_flags.into()).await
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE on fd_read, fd_pread, fd_write, fd_pwrite implementations:
|
// NOTE on fd_read, fd_pread, fd_write, fd_pwrite implementations:
|
||||||
@@ -444,7 +453,11 @@ impl<'a> wasi_unstable::WasiUnstable for WasiCtx {
|
|||||||
// The bodies of these functions is mostly about converting the GuestPtr and types::-based
|
// The bodies of these functions is mostly about converting the GuestPtr and types::-based
|
||||||
// representation to a std::io::IoSlice(Mut) representation.
|
// representation to a std::io::IoSlice(Mut) representation.
|
||||||
|
|
||||||
fn fd_read(&self, fd: types::Fd, iovs: &types::IovecArray<'_>) -> Result<types::Size, Error> {
|
async fn fd_read<'a>(
|
||||||
|
&self,
|
||||||
|
fd: types::Fd,
|
||||||
|
iovs: &types::IovecArray<'a>,
|
||||||
|
) -> Result<types::Size, Error> {
|
||||||
let table = self.table();
|
let table = self.table();
|
||||||
let f = table.get_file(u32::from(fd))?.get_cap(FileCaps::READ)?;
|
let f = table.get_file(u32::from(fd))?.get_cap(FileCaps::READ)?;
|
||||||
|
|
||||||
@@ -462,14 +475,14 @@ impl<'a> wasi_unstable::WasiUnstable for WasiCtx {
|
|||||||
.map(|s| IoSliceMut::new(&mut *s))
|
.map(|s| IoSliceMut::new(&mut *s))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let bytes_read = f.read_vectored(&mut ioslices)?;
|
let bytes_read = f.read_vectored(&mut ioslices).await?;
|
||||||
Ok(types::Size::try_from(bytes_read)?)
|
Ok(types::Size::try_from(bytes_read)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fd_pread(
|
async fn fd_pread<'a>(
|
||||||
&self,
|
&self,
|
||||||
fd: types::Fd,
|
fd: types::Fd,
|
||||||
iovs: &types::IovecArray<'_>,
|
iovs: &types::IovecArray<'a>,
|
||||||
offset: types::Filesize,
|
offset: types::Filesize,
|
||||||
) -> Result<types::Size, Error> {
|
) -> Result<types::Size, Error> {
|
||||||
let table = self.table();
|
let table = self.table();
|
||||||
@@ -491,14 +504,14 @@ impl<'a> wasi_unstable::WasiUnstable for WasiCtx {
|
|||||||
.map(|s| IoSliceMut::new(&mut *s))
|
.map(|s| IoSliceMut::new(&mut *s))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let bytes_read = f.read_vectored_at(&mut ioslices, offset)?;
|
let bytes_read = f.read_vectored_at(&mut ioslices, offset).await?;
|
||||||
Ok(types::Size::try_from(bytes_read)?)
|
Ok(types::Size::try_from(bytes_read)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fd_write(
|
async fn fd_write<'a>(
|
||||||
&self,
|
&self,
|
||||||
fd: types::Fd,
|
fd: types::Fd,
|
||||||
ciovs: &types::CiovecArray<'_>,
|
ciovs: &types::CiovecArray<'a>,
|
||||||
) -> Result<types::Size, Error> {
|
) -> Result<types::Size, Error> {
|
||||||
let table = self.table();
|
let table = self.table();
|
||||||
let f = table.get_file(u32::from(fd))?.get_cap(FileCaps::WRITE)?;
|
let f = table.get_file(u32::from(fd))?.get_cap(FileCaps::WRITE)?;
|
||||||
@@ -516,15 +529,15 @@ impl<'a> wasi_unstable::WasiUnstable for WasiCtx {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|s| IoSlice::new(s.deref()))
|
.map(|s| IoSlice::new(s.deref()))
|
||||||
.collect();
|
.collect();
|
||||||
let bytes_written = f.write_vectored(&ioslices)?;
|
let bytes_written = f.write_vectored(&ioslices).await?;
|
||||||
|
|
||||||
Ok(types::Size::try_from(bytes_written)?)
|
Ok(types::Size::try_from(bytes_written)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fd_pwrite(
|
async fn fd_pwrite<'a>(
|
||||||
&self,
|
&self,
|
||||||
fd: types::Fd,
|
fd: types::Fd,
|
||||||
ciovs: &types::CiovecArray<'_>,
|
ciovs: &types::CiovecArray<'a>,
|
||||||
offset: types::Filesize,
|
offset: types::Filesize,
|
||||||
) -> Result<types::Size, Error> {
|
) -> Result<types::Size, Error> {
|
||||||
let table = self.table();
|
let table = self.table();
|
||||||
@@ -545,77 +558,81 @@ impl<'a> wasi_unstable::WasiUnstable for WasiCtx {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|s| IoSlice::new(s.deref()))
|
.map(|s| IoSlice::new(s.deref()))
|
||||||
.collect();
|
.collect();
|
||||||
let bytes_written = f.write_vectored_at(&ioslices, offset)?;
|
let bytes_written = f.write_vectored_at(&ioslices, offset).await?;
|
||||||
|
|
||||||
Ok(types::Size::try_from(bytes_written)?)
|
Ok(types::Size::try_from(bytes_written)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fd_prestat_get(&self, fd: types::Fd) -> Result<types::Prestat, Error> {
|
async fn fd_prestat_get(&self, fd: types::Fd) -> Result<types::Prestat, Error> {
|
||||||
Ok(Snapshot1::fd_prestat_get(self, fd.into())?.into())
|
Ok(Snapshot1::fd_prestat_get(self, fd.into()).await?.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fd_prestat_dir_name(
|
async fn fd_prestat_dir_name<'a>(
|
||||||
&self,
|
&self,
|
||||||
fd: types::Fd,
|
fd: types::Fd,
|
||||||
path: &GuestPtr<u8>,
|
path: &GuestPtr<'a, u8>,
|
||||||
path_max_len: types::Size,
|
path_max_len: types::Size,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
Snapshot1::fd_prestat_dir_name(self, fd.into(), path, path_max_len)
|
Snapshot1::fd_prestat_dir_name(self, fd.into(), path, path_max_len).await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fd_renumber(&self, from: types::Fd, to: types::Fd) -> Result<(), Error> {
|
async fn fd_renumber(&self, from: types::Fd, to: types::Fd) -> Result<(), Error> {
|
||||||
Snapshot1::fd_renumber(self, from.into(), to.into())
|
Snapshot1::fd_renumber(self, from.into(), to.into()).await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fd_seek(
|
async fn fd_seek(
|
||||||
&self,
|
&self,
|
||||||
fd: types::Fd,
|
fd: types::Fd,
|
||||||
offset: types::Filedelta,
|
offset: types::Filedelta,
|
||||||
whence: types::Whence,
|
whence: types::Whence,
|
||||||
) -> Result<types::Filesize, Error> {
|
) -> Result<types::Filesize, Error> {
|
||||||
Snapshot1::fd_seek(self, fd.into(), offset, whence.into())
|
Snapshot1::fd_seek(self, fd.into(), offset, whence.into()).await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fd_sync(&self, fd: types::Fd) -> Result<(), Error> {
|
async fn fd_sync(&self, fd: types::Fd) -> Result<(), Error> {
|
||||||
Snapshot1::fd_sync(self, fd.into())
|
Snapshot1::fd_sync(self, fd.into()).await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fd_tell(&self, fd: types::Fd) -> Result<types::Filesize, Error> {
|
async fn fd_tell(&self, fd: types::Fd) -> Result<types::Filesize, Error> {
|
||||||
Snapshot1::fd_tell(self, fd.into())
|
Snapshot1::fd_tell(self, fd.into()).await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fd_readdir(
|
async fn fd_readdir<'a>(
|
||||||
&self,
|
&self,
|
||||||
fd: types::Fd,
|
fd: types::Fd,
|
||||||
buf: &GuestPtr<u8>,
|
buf: &GuestPtr<'a, u8>,
|
||||||
buf_len: types::Size,
|
buf_len: types::Size,
|
||||||
cookie: types::Dircookie,
|
cookie: types::Dircookie,
|
||||||
) -> Result<types::Size, Error> {
|
) -> Result<types::Size, Error> {
|
||||||
Snapshot1::fd_readdir(self, fd.into(), buf, buf_len, cookie)
|
Snapshot1::fd_readdir(self, fd.into(), buf, buf_len, cookie).await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path_create_directory(
|
async fn path_create_directory<'a>(
|
||||||
&self,
|
&self,
|
||||||
dirfd: types::Fd,
|
dirfd: types::Fd,
|
||||||
path: &GuestPtr<'_, str>,
|
path: &GuestPtr<'a, str>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
Snapshot1::path_create_directory(self, dirfd.into(), path)
|
Snapshot1::path_create_directory(self, dirfd.into(), path).await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path_filestat_get(
|
async fn path_filestat_get<'a>(
|
||||||
&self,
|
&self,
|
||||||
dirfd: types::Fd,
|
dirfd: types::Fd,
|
||||||
flags: types::Lookupflags,
|
flags: types::Lookupflags,
|
||||||
path: &GuestPtr<'_, str>,
|
path: &GuestPtr<'a, str>,
|
||||||
) -> Result<types::Filestat, Error> {
|
) -> Result<types::Filestat, Error> {
|
||||||
Ok(Snapshot1::path_filestat_get(self, dirfd.into(), flags.into(), path)?.into())
|
Ok(
|
||||||
|
Snapshot1::path_filestat_get(self, dirfd.into(), flags.into(), path)
|
||||||
|
.await?
|
||||||
|
.into(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path_filestat_set_times(
|
async fn path_filestat_set_times<'a>(
|
||||||
&self,
|
&self,
|
||||||
dirfd: types::Fd,
|
dirfd: types::Fd,
|
||||||
flags: types::Lookupflags,
|
flags: types::Lookupflags,
|
||||||
path: &GuestPtr<'_, str>,
|
path: &GuestPtr<'a, str>,
|
||||||
atim: types::Timestamp,
|
atim: types::Timestamp,
|
||||||
mtim: types::Timestamp,
|
mtim: types::Timestamp,
|
||||||
fst_flags: types::Fstflags,
|
fst_flags: types::Fstflags,
|
||||||
@@ -629,15 +646,16 @@ impl<'a> wasi_unstable::WasiUnstable for WasiCtx {
|
|||||||
mtim,
|
mtim,
|
||||||
fst_flags.into(),
|
fst_flags.into(),
|
||||||
)
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path_link(
|
async fn path_link<'a>(
|
||||||
&self,
|
&self,
|
||||||
src_fd: types::Fd,
|
src_fd: types::Fd,
|
||||||
src_flags: types::Lookupflags,
|
src_flags: types::Lookupflags,
|
||||||
src_path: &GuestPtr<'_, str>,
|
src_path: &GuestPtr<'a, str>,
|
||||||
target_fd: types::Fd,
|
target_fd: types::Fd,
|
||||||
target_path: &GuestPtr<'_, str>,
|
target_path: &GuestPtr<'a, str>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
Snapshot1::path_link(
|
Snapshot1::path_link(
|
||||||
self,
|
self,
|
||||||
@@ -647,13 +665,14 @@ impl<'a> wasi_unstable::WasiUnstable for WasiCtx {
|
|||||||
target_fd.into(),
|
target_fd.into(),
|
||||||
target_path,
|
target_path,
|
||||||
)
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path_open(
|
async fn path_open<'a>(
|
||||||
&self,
|
&self,
|
||||||
dirfd: types::Fd,
|
dirfd: types::Fd,
|
||||||
dirflags: types::Lookupflags,
|
dirflags: types::Lookupflags,
|
||||||
path: &GuestPtr<'_, str>,
|
path: &GuestPtr<'a, str>,
|
||||||
oflags: types::Oflags,
|
oflags: types::Oflags,
|
||||||
fs_rights_base: types::Rights,
|
fs_rights_base: types::Rights,
|
||||||
fs_rights_inheriting: types::Rights,
|
fs_rights_inheriting: types::Rights,
|
||||||
@@ -668,49 +687,54 @@ impl<'a> wasi_unstable::WasiUnstable for WasiCtx {
|
|||||||
fs_rights_base.into(),
|
fs_rights_base.into(),
|
||||||
fs_rights_inheriting.into(),
|
fs_rights_inheriting.into(),
|
||||||
fdflags.into(),
|
fdflags.into(),
|
||||||
)?
|
)
|
||||||
|
.await?
|
||||||
.into())
|
.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path_readlink(
|
async fn path_readlink<'a>(
|
||||||
&self,
|
&self,
|
||||||
dirfd: types::Fd,
|
dirfd: types::Fd,
|
||||||
path: &GuestPtr<'_, str>,
|
path: &GuestPtr<'a, str>,
|
||||||
buf: &GuestPtr<u8>,
|
buf: &GuestPtr<'a, u8>,
|
||||||
buf_len: types::Size,
|
buf_len: types::Size,
|
||||||
) -> Result<types::Size, Error> {
|
) -> Result<types::Size, Error> {
|
||||||
Snapshot1::path_readlink(self, dirfd.into(), path, buf, buf_len)
|
Snapshot1::path_readlink(self, dirfd.into(), path, buf, buf_len).await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path_remove_directory(
|
async fn path_remove_directory<'a>(
|
||||||
&self,
|
&self,
|
||||||
dirfd: types::Fd,
|
dirfd: types::Fd,
|
||||||
path: &GuestPtr<'_, str>,
|
path: &GuestPtr<'a, str>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
Snapshot1::path_remove_directory(self, dirfd.into(), path)
|
Snapshot1::path_remove_directory(self, dirfd.into(), path).await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path_rename(
|
async fn path_rename<'a>(
|
||||||
&self,
|
&self,
|
||||||
src_fd: types::Fd,
|
src_fd: types::Fd,
|
||||||
src_path: &GuestPtr<'_, str>,
|
src_path: &GuestPtr<'a, str>,
|
||||||
dest_fd: types::Fd,
|
dest_fd: types::Fd,
|
||||||
dest_path: &GuestPtr<'_, str>,
|
dest_path: &GuestPtr<'a, str>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
Snapshot1::path_rename(self, src_fd.into(), src_path, dest_fd.into(), dest_path)
|
Snapshot1::path_rename(self, src_fd.into(), src_path, dest_fd.into(), dest_path).await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path_symlink(
|
async fn path_symlink<'a>(
|
||||||
&self,
|
&self,
|
||||||
src_path: &GuestPtr<'_, str>,
|
src_path: &GuestPtr<'a, str>,
|
||||||
dirfd: types::Fd,
|
dirfd: types::Fd,
|
||||||
dest_path: &GuestPtr<'_, str>,
|
dest_path: &GuestPtr<'a, str>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
Snapshot1::path_symlink(self, src_path, dirfd.into(), dest_path)
|
Snapshot1::path_symlink(self, src_path, dirfd.into(), dest_path).await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path_unlink_file(&self, dirfd: types::Fd, path: &GuestPtr<'_, str>) -> Result<(), Error> {
|
async fn path_unlink_file<'a>(
|
||||||
Snapshot1::path_unlink_file(self, dirfd.into(), path)
|
&self,
|
||||||
|
dirfd: types::Fd,
|
||||||
|
path: &GuestPtr<'a, str>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
Snapshot1::path_unlink_file(self, dirfd.into(), path).await
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE on poll_oneoff implementation:
|
// NOTE on poll_oneoff implementation:
|
||||||
@@ -720,10 +744,10 @@ impl<'a> wasi_unstable::WasiUnstable for WasiCtx {
|
|||||||
// The implementations are identical, but the `types::` in scope locally is different.
|
// The implementations are identical, but the `types::` in scope locally is different.
|
||||||
// The bodies of these functions is mostly about converting the GuestPtr and types::-based
|
// The bodies of these functions is mostly about converting the GuestPtr and types::-based
|
||||||
// representation to use the Poll abstraction.
|
// representation to use the Poll abstraction.
|
||||||
fn poll_oneoff(
|
async fn poll_oneoff<'a>(
|
||||||
&self,
|
&self,
|
||||||
subs: &GuestPtr<types::Subscription>,
|
subs: &GuestPtr<'a, types::Subscription>,
|
||||||
events: &GuestPtr<types::Event>,
|
events: &GuestPtr<'a, types::Event>,
|
||||||
nsubscriptions: types::Size,
|
nsubscriptions: types::Size,
|
||||||
) -> Result<types::Size, Error> {
|
) -> Result<types::Size, Error> {
|
||||||
if nsubscriptions == 0 {
|
if nsubscriptions == 0 {
|
||||||
@@ -741,7 +765,9 @@ impl<'a> wasi_unstable::WasiUnstable for WasiCtx {
|
|||||||
.flags
|
.flags
|
||||||
.contains(types::Subclockflags::SUBSCRIPTION_CLOCK_ABSTIME)
|
.contains(types::Subclockflags::SUBSCRIPTION_CLOCK_ABSTIME)
|
||||||
{
|
{
|
||||||
self.sched.sleep(Duration::from_nanos(clocksub.timeout))?;
|
self.sched
|
||||||
|
.sleep(Duration::from_nanos(clocksub.timeout))
|
||||||
|
.await?;
|
||||||
events.write(types::Event {
|
events.write(types::Event {
|
||||||
userdata: sub.userdata,
|
userdata: sub.userdata,
|
||||||
error: types::Errno::Success,
|
error: types::Errno::Success,
|
||||||
@@ -754,6 +780,10 @@ impl<'a> wasi_unstable::WasiUnstable for WasiCtx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let table = self.table();
|
let table = self.table();
|
||||||
|
let mut sub_fds: HashSet<types::Fd> = HashSet::new();
|
||||||
|
// We need these refmuts to outlive Poll, which will hold the &mut dyn WasiFile inside
|
||||||
|
let mut read_refs: Vec<(RefMut<'_, dyn WasiFile>, Userdata)> = Vec::new();
|
||||||
|
let mut write_refs: Vec<(RefMut<'_, dyn WasiFile>, Userdata)> = Vec::new();
|
||||||
let mut poll = Poll::new();
|
let mut poll = Poll::new();
|
||||||
|
|
||||||
let subs = subs.as_array(nsubscriptions);
|
let subs = subs.as_array(nsubscriptions);
|
||||||
@@ -792,22 +822,34 @@ impl<'a> wasi_unstable::WasiUnstable for WasiCtx {
|
|||||||
},
|
},
|
||||||
types::SubscriptionU::FdRead(readsub) => {
|
types::SubscriptionU::FdRead(readsub) => {
|
||||||
let fd = readsub.file_descriptor;
|
let fd = readsub.file_descriptor;
|
||||||
let file = table
|
if sub_fds.contains(&fd) {
|
||||||
.get_file(u32::from(fd))?
|
return Err(Error::invalid_argument()
|
||||||
|
.context("Fd can be subscribed to at most once per poll"));
|
||||||
|
} else {
|
||||||
|
sub_fds.insert(fd);
|
||||||
|
}
|
||||||
|
let file_ref = table
|
||||||
|
.get_file_mut(u32::from(fd))?
|
||||||
.get_cap(FileCaps::POLL_READWRITE)?;
|
.get_cap(FileCaps::POLL_READWRITE)?;
|
||||||
poll.subscribe_read(file, sub.userdata.into());
|
read_refs.push((file_ref, sub.userdata.into()));
|
||||||
}
|
}
|
||||||
types::SubscriptionU::FdWrite(writesub) => {
|
types::SubscriptionU::FdWrite(writesub) => {
|
||||||
let fd = writesub.file_descriptor;
|
let fd = writesub.file_descriptor;
|
||||||
let file = table
|
if sub_fds.contains(&fd) {
|
||||||
.get_file(u32::from(fd))?
|
return Err(Error::invalid_argument()
|
||||||
|
.context("Fd can be subscribed to at most once per poll"));
|
||||||
|
} else {
|
||||||
|
sub_fds.insert(fd);
|
||||||
|
}
|
||||||
|
let file_ref = table
|
||||||
|
.get_file_mut(u32::from(fd))?
|
||||||
.get_cap(FileCaps::POLL_READWRITE)?;
|
.get_cap(FileCaps::POLL_READWRITE)?;
|
||||||
poll.subscribe_write(file, sub.userdata.into());
|
write_refs.push((file_ref, sub.userdata.into()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.sched.poll_oneoff(&poll)?;
|
self.sched.poll_oneoff(&mut poll).await?;
|
||||||
|
|
||||||
let results = poll.results();
|
let results = poll.results();
|
||||||
let num_results = results.len();
|
let num_results = results.len();
|
||||||
@@ -882,41 +924,45 @@ impl<'a> wasi_unstable::WasiUnstable for WasiCtx {
|
|||||||
Ok(num_results.try_into().expect("results fit into memory"))
|
Ok(num_results.try_into().expect("results fit into memory"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn proc_exit(&self, status: types::Exitcode) -> wiggle::Trap {
|
async fn proc_exit(&self, status: types::Exitcode) -> wiggle::Trap {
|
||||||
Snapshot1::proc_exit(self, status)
|
Snapshot1::proc_exit(self, status).await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn proc_raise(&self, _sig: types::Signal) -> Result<(), Error> {
|
async fn proc_raise(&self, _sig: types::Signal) -> Result<(), Error> {
|
||||||
Err(Error::trap("proc_raise unsupported"))
|
Err(Error::trap("proc_raise unsupported"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sched_yield(&self) -> Result<(), Error> {
|
async fn sched_yield(&self) -> Result<(), Error> {
|
||||||
Snapshot1::sched_yield(self)
|
Snapshot1::sched_yield(self).await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn random_get(&self, buf: &GuestPtr<u8>, buf_len: types::Size) -> Result<(), Error> {
|
async fn random_get<'a>(
|
||||||
Snapshot1::random_get(self, buf, buf_len)
|
&self,
|
||||||
|
buf: &GuestPtr<'a, u8>,
|
||||||
|
buf_len: types::Size,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
Snapshot1::random_get(self, buf, buf_len).await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sock_recv(
|
async fn sock_recv<'a>(
|
||||||
&self,
|
&self,
|
||||||
_fd: types::Fd,
|
_fd: types::Fd,
|
||||||
_ri_data: &types::IovecArray<'_>,
|
_ri_data: &types::IovecArray<'a>,
|
||||||
_ri_flags: types::Riflags,
|
_ri_flags: types::Riflags,
|
||||||
) -> Result<(types::Size, types::Roflags), Error> {
|
) -> Result<(types::Size, types::Roflags), Error> {
|
||||||
Err(Error::trap("sock_recv unsupported"))
|
Err(Error::trap("sock_recv unsupported"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sock_send(
|
async fn sock_send<'a>(
|
||||||
&self,
|
&self,
|
||||||
_fd: types::Fd,
|
_fd: types::Fd,
|
||||||
_si_data: &types::CiovecArray<'_>,
|
_si_data: &types::CiovecArray<'a>,
|
||||||
_si_flags: types::Siflags,
|
_si_flags: types::Siflags,
|
||||||
) -> Result<types::Size, Error> {
|
) -> Result<types::Size, Error> {
|
||||||
Err(Error::trap("sock_send unsupported"))
|
Err(Error::trap("sock_send unsupported"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sock_shutdown(&self, _fd: types::Fd, _how: types::Sdflags) -> Result<(), Error> {
|
async fn sock_shutdown(&self, _fd: types::Fd, _how: types::Sdflags) -> Result<(), Error> {
|
||||||
Err(Error::trap("sock_shutdown unsupported"))
|
Err(Error::trap("sock_shutdown unsupported"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,17 +2,18 @@ use crate::{
|
|||||||
dir::{DirCaps, DirEntry, DirEntryExt, DirFdStat, ReaddirCursor, ReaddirEntity, TableDirExt},
|
dir::{DirCaps, DirEntry, DirEntryExt, DirFdStat, ReaddirCursor, ReaddirEntity, TableDirExt},
|
||||||
file::{
|
file::{
|
||||||
Advice, FdFlags, FdStat, FileCaps, FileEntry, FileEntryExt, FileEntryMutExt, FileType,
|
Advice, FdFlags, FdStat, FileCaps, FileEntry, FileEntryExt, FileEntryMutExt, FileType,
|
||||||
Filestat, OFlags, TableFileExt,
|
Filestat, OFlags, TableFileExt, WasiFile,
|
||||||
},
|
},
|
||||||
sched::{
|
sched::{
|
||||||
subscription::{RwEventFlags, SubscriptionResult},
|
subscription::{RwEventFlags, SubscriptionResult},
|
||||||
Poll,
|
Poll, Userdata,
|
||||||
},
|
},
|
||||||
Error, ErrorExt, ErrorKind, SystemTimeSpec, WasiCtx,
|
Error, ErrorExt, ErrorKind, SystemTimeSpec, WasiCtx,
|
||||||
};
|
};
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use cap_std::time::{Duration, SystemClock};
|
use cap_std::time::{Duration, SystemClock};
|
||||||
use std::cell::{Ref, RefMut};
|
use std::cell::{Ref, RefMut};
|
||||||
|
use std::collections::HashSet;
|
||||||
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};
|
||||||
@@ -22,6 +23,10 @@ use wiggle::GuestPtr;
|
|||||||
wiggle::from_witx!({
|
wiggle::from_witx!({
|
||||||
witx: ["$WASI_ROOT/phases/snapshot/witx/wasi_snapshot_preview1.witx"],
|
witx: ["$WASI_ROOT/phases/snapshot/witx/wasi_snapshot_preview1.witx"],
|
||||||
errors: { errno => Error },
|
errors: { errno => Error },
|
||||||
|
// Note: not every function actually needs to be async, however, nearly all of them do, and
|
||||||
|
// keeping that set the same in this macro and the wasmtime_wiggle / lucet_wiggle macros is
|
||||||
|
// tedious, and there is no cost to having a sync function be async in this case.
|
||||||
|
async: *
|
||||||
});
|
});
|
||||||
|
|
||||||
impl wiggle::GuestErrorType for types::Errno {
|
impl wiggle::GuestErrorType for types::Errno {
|
||||||
@@ -188,8 +193,9 @@ impl TryFrom<std::io::Error> for types::Errno {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
#[wiggle::async_trait]
|
||||||
fn args_get<'b>(
|
impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
||||||
|
async fn args_get<'b>(
|
||||||
&self,
|
&self,
|
||||||
argv: &GuestPtr<'b, GuestPtr<'b, u8>>,
|
argv: &GuestPtr<'b, GuestPtr<'b, u8>>,
|
||||||
argv_buf: &GuestPtr<'b, u8>,
|
argv_buf: &GuestPtr<'b, u8>,
|
||||||
@@ -197,11 +203,11 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
|||||||
self.args.write_to_guest(argv_buf, argv)
|
self.args.write_to_guest(argv_buf, argv)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn args_sizes_get(&self) -> Result<(types::Size, types::Size), Error> {
|
async fn args_sizes_get(&self) -> Result<(types::Size, types::Size), Error> {
|
||||||
Ok((self.args.number_elements(), self.args.cumulative_size()))
|
Ok((self.args.number_elements(), self.args.cumulative_size()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn environ_get<'b>(
|
async fn environ_get<'b>(
|
||||||
&self,
|
&self,
|
||||||
environ: &GuestPtr<'b, GuestPtr<'b, u8>>,
|
environ: &GuestPtr<'b, GuestPtr<'b, u8>>,
|
||||||
environ_buf: &GuestPtr<'b, u8>,
|
environ_buf: &GuestPtr<'b, u8>,
|
||||||
@@ -209,11 +215,11 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
|||||||
self.env.write_to_guest(environ_buf, environ)
|
self.env.write_to_guest(environ_buf, environ)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn environ_sizes_get(&self) -> Result<(types::Size, types::Size), Error> {
|
async fn environ_sizes_get(&self) -> Result<(types::Size, types::Size), Error> {
|
||||||
Ok((self.env.number_elements(), self.env.cumulative_size()))
|
Ok((self.env.number_elements(), self.env.cumulative_size()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clock_res_get(&self, id: types::Clockid) -> Result<types::Timestamp, Error> {
|
async fn clock_res_get(&self, id: types::Clockid) -> Result<types::Timestamp, Error> {
|
||||||
let resolution = match id {
|
let resolution = match id {
|
||||||
types::Clockid::Realtime => Ok(self.clocks.system.resolution()),
|
types::Clockid::Realtime => Ok(self.clocks.system.resolution()),
|
||||||
types::Clockid::Monotonic => Ok(self.clocks.monotonic.resolution()),
|
types::Clockid::Monotonic => Ok(self.clocks.monotonic.resolution()),
|
||||||
@@ -224,7 +230,7 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
|||||||
Ok(resolution.as_nanos().try_into()?)
|
Ok(resolution.as_nanos().try_into()?)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clock_time_get(
|
async fn clock_time_get(
|
||||||
&self,
|
&self,
|
||||||
id: types::Clockid,
|
id: types::Clockid,
|
||||||
precision: types::Timestamp,
|
precision: types::Timestamp,
|
||||||
@@ -249,7 +255,7 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fd_advise(
|
async fn fd_advise(
|
||||||
&self,
|
&self,
|
||||||
fd: types::Fd,
|
fd: types::Fd,
|
||||||
offset: types::Filesize,
|
offset: types::Filesize,
|
||||||
@@ -259,11 +265,12 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
|||||||
self.table()
|
self.table()
|
||||||
.get_file(u32::from(fd))?
|
.get_file(u32::from(fd))?
|
||||||
.get_cap(FileCaps::ADVISE)?
|
.get_cap(FileCaps::ADVISE)?
|
||||||
.advise(offset, len, advice.into())?;
|
.advise(offset, len, advice.into())
|
||||||
|
.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fd_allocate(
|
async fn fd_allocate(
|
||||||
&self,
|
&self,
|
||||||
fd: types::Fd,
|
fd: types::Fd,
|
||||||
offset: types::Filesize,
|
offset: types::Filesize,
|
||||||
@@ -272,11 +279,12 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
|||||||
self.table()
|
self.table()
|
||||||
.get_file(u32::from(fd))?
|
.get_file(u32::from(fd))?
|
||||||
.get_cap(FileCaps::ALLOCATE)?
|
.get_cap(FileCaps::ALLOCATE)?
|
||||||
.allocate(offset, len)?;
|
.allocate(offset, len)
|
||||||
|
.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fd_close(&self, fd: types::Fd) -> Result<(), Error> {
|
async fn fd_close(&self, fd: types::Fd) -> Result<(), Error> {
|
||||||
let mut table = self.table();
|
let mut table = self.table();
|
||||||
let fd = u32::from(fd);
|
let fd = u32::from(fd);
|
||||||
|
|
||||||
@@ -302,20 +310,21 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fd_datasync(&self, fd: types::Fd) -> Result<(), Error> {
|
async fn fd_datasync(&self, fd: types::Fd) -> Result<(), Error> {
|
||||||
self.table()
|
self.table()
|
||||||
.get_file(u32::from(fd))?
|
.get_file(u32::from(fd))?
|
||||||
.get_cap(FileCaps::DATASYNC)?
|
.get_cap(FileCaps::DATASYNC)?
|
||||||
.datasync()?;
|
.datasync()
|
||||||
|
.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fd_fdstat_get(&self, fd: types::Fd) -> Result<types::Fdstat, Error> {
|
async fn fd_fdstat_get(&self, fd: types::Fd) -> Result<types::Fdstat, Error> {
|
||||||
let table = self.table();
|
let table = self.table();
|
||||||
let fd = u32::from(fd);
|
let fd = u32::from(fd);
|
||||||
if table.is::<FileEntry>(fd) {
|
if table.is::<FileEntry>(fd) {
|
||||||
let file_entry: Ref<FileEntry> = table.get(fd)?;
|
let file_entry: Ref<FileEntry> = table.get(fd)?;
|
||||||
let fdstat = file_entry.get_fdstat()?;
|
let fdstat = file_entry.get_fdstat().await?;
|
||||||
Ok(types::Fdstat::from(&fdstat))
|
Ok(types::Fdstat::from(&fdstat))
|
||||||
} else if table.is::<DirEntry>(fd) {
|
} else if table.is::<DirEntry>(fd) {
|
||||||
let dir_entry: Ref<DirEntry> = table.get(fd)?;
|
let dir_entry: Ref<DirEntry> = table.get(fd)?;
|
||||||
@@ -326,14 +335,15 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fd_fdstat_set_flags(&self, fd: types::Fd, flags: types::Fdflags) -> Result<(), Error> {
|
async fn fd_fdstat_set_flags(&self, fd: types::Fd, flags: types::Fdflags) -> Result<(), Error> {
|
||||||
self.table()
|
self.table()
|
||||||
.get_file_mut(u32::from(fd))?
|
.get_file_mut(u32::from(fd))?
|
||||||
.get_cap(FileCaps::FDSTAT_SET_FLAGS)?
|
.get_cap(FileCaps::FDSTAT_SET_FLAGS)?
|
||||||
.set_fdflags(FdFlags::from(flags))
|
.set_fdflags(FdFlags::from(flags))
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fd_fdstat_set_rights(
|
async fn fd_fdstat_set_rights(
|
||||||
&self,
|
&self,
|
||||||
fd: types::Fd,
|
fd: types::Fd,
|
||||||
fs_rights_base: types::Rights,
|
fs_rights_base: types::Rights,
|
||||||
@@ -355,35 +365,42 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fd_filestat_get(&self, fd: types::Fd) -> Result<types::Filestat, Error> {
|
async fn fd_filestat_get(&self, fd: types::Fd) -> Result<types::Filestat, Error> {
|
||||||
let table = self.table();
|
let table = self.table();
|
||||||
let fd = u32::from(fd);
|
let fd = u32::from(fd);
|
||||||
if table.is::<FileEntry>(fd) {
|
if table.is::<FileEntry>(fd) {
|
||||||
let filestat = table
|
let filestat = table
|
||||||
.get_file(fd)?
|
.get_file(fd)?
|
||||||
.get_cap(FileCaps::FILESTAT_GET)?
|
.get_cap(FileCaps::FILESTAT_GET)?
|
||||||
.get_filestat()?;
|
.get_filestat()
|
||||||
|
.await?;
|
||||||
Ok(filestat.into())
|
Ok(filestat.into())
|
||||||
} else if table.is::<DirEntry>(fd) {
|
} else if table.is::<DirEntry>(fd) {
|
||||||
let filestat = table
|
let filestat = table
|
||||||
.get_dir(fd)?
|
.get_dir(fd)?
|
||||||
.get_cap(DirCaps::FILESTAT_GET)?
|
.get_cap(DirCaps::FILESTAT_GET)?
|
||||||
.get_filestat()?;
|
.get_filestat()
|
||||||
|
.await?;
|
||||||
Ok(filestat.into())
|
Ok(filestat.into())
|
||||||
} else {
|
} else {
|
||||||
Err(Error::badf())
|
Err(Error::badf())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fd_filestat_set_size(&self, fd: types::Fd, size: types::Filesize) -> Result<(), Error> {
|
async fn fd_filestat_set_size(
|
||||||
|
&self,
|
||||||
|
fd: types::Fd,
|
||||||
|
size: types::Filesize,
|
||||||
|
) -> Result<(), Error> {
|
||||||
self.table()
|
self.table()
|
||||||
.get_file(u32::from(fd))?
|
.get_file(u32::from(fd))?
|
||||||
.get_cap(FileCaps::FILESTAT_SET_SIZE)?
|
.get_cap(FileCaps::FILESTAT_SET_SIZE)?
|
||||||
.set_filestat_size(size)?;
|
.set_filestat_size(size)
|
||||||
|
.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fd_filestat_set_times(
|
async fn fd_filestat_set_times(
|
||||||
&self,
|
&self,
|
||||||
fd: types::Fd,
|
fd: types::Fd,
|
||||||
atim: types::Timestamp,
|
atim: types::Timestamp,
|
||||||
@@ -407,18 +424,24 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
|||||||
.expect("checked that entry is file")
|
.expect("checked that entry is file")
|
||||||
.get_cap(FileCaps::FILESTAT_SET_TIMES)?
|
.get_cap(FileCaps::FILESTAT_SET_TIMES)?
|
||||||
.set_times(atim, mtim)
|
.set_times(atim, mtim)
|
||||||
|
.await
|
||||||
} else if table.is::<DirEntry>(fd) {
|
} else if table.is::<DirEntry>(fd) {
|
||||||
table
|
table
|
||||||
.get_dir(fd)
|
.get_dir(fd)
|
||||||
.expect("checked that entry is dir")
|
.expect("checked that entry is dir")
|
||||||
.get_cap(DirCaps::FILESTAT_SET_TIMES)?
|
.get_cap(DirCaps::FILESTAT_SET_TIMES)?
|
||||||
.set_times(".", atim, mtim, false)
|
.set_times(".", atim, mtim, false)
|
||||||
|
.await
|
||||||
} else {
|
} else {
|
||||||
Err(Error::badf())
|
Err(Error::badf())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fd_read(&self, fd: types::Fd, iovs: &types::IovecArray<'_>) -> Result<types::Size, Error> {
|
async fn fd_read<'a>(
|
||||||
|
&self,
|
||||||
|
fd: types::Fd,
|
||||||
|
iovs: &types::IovecArray<'a>,
|
||||||
|
) -> Result<types::Size, Error> {
|
||||||
let table = self.table();
|
let table = self.table();
|
||||||
let f = table.get_file(u32::from(fd))?.get_cap(FileCaps::READ)?;
|
let f = table.get_file(u32::from(fd))?.get_cap(FileCaps::READ)?;
|
||||||
|
|
||||||
@@ -436,14 +459,14 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
|||||||
.map(|s| IoSliceMut::new(&mut *s))
|
.map(|s| IoSliceMut::new(&mut *s))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let bytes_read = f.read_vectored(&mut ioslices)?;
|
let bytes_read = f.read_vectored(&mut ioslices).await?;
|
||||||
Ok(types::Size::try_from(bytes_read)?)
|
Ok(types::Size::try_from(bytes_read)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fd_pread(
|
async fn fd_pread<'a>(
|
||||||
&self,
|
&self,
|
||||||
fd: types::Fd,
|
fd: types::Fd,
|
||||||
iovs: &types::IovecArray<'_>,
|
iovs: &types::IovecArray<'a>,
|
||||||
offset: types::Filesize,
|
offset: types::Filesize,
|
||||||
) -> Result<types::Size, Error> {
|
) -> Result<types::Size, Error> {
|
||||||
let table = self.table();
|
let table = self.table();
|
||||||
@@ -465,14 +488,14 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
|||||||
.map(|s| IoSliceMut::new(&mut *s))
|
.map(|s| IoSliceMut::new(&mut *s))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let bytes_read = f.read_vectored_at(&mut ioslices, offset)?;
|
let bytes_read = f.read_vectored_at(&mut ioslices, offset).await?;
|
||||||
Ok(types::Size::try_from(bytes_read)?)
|
Ok(types::Size::try_from(bytes_read)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fd_write(
|
async fn fd_write<'a>(
|
||||||
&self,
|
&self,
|
||||||
fd: types::Fd,
|
fd: types::Fd,
|
||||||
ciovs: &types::CiovecArray<'_>,
|
ciovs: &types::CiovecArray<'a>,
|
||||||
) -> Result<types::Size, Error> {
|
) -> Result<types::Size, Error> {
|
||||||
let table = self.table();
|
let table = self.table();
|
||||||
let f = table.get_file(u32::from(fd))?.get_cap(FileCaps::WRITE)?;
|
let f = table.get_file(u32::from(fd))?.get_cap(FileCaps::WRITE)?;
|
||||||
@@ -490,15 +513,15 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|s| IoSlice::new(s.deref()))
|
.map(|s| IoSlice::new(s.deref()))
|
||||||
.collect();
|
.collect();
|
||||||
let bytes_written = f.write_vectored(&ioslices)?;
|
let bytes_written = f.write_vectored(&ioslices).await?;
|
||||||
|
|
||||||
Ok(types::Size::try_from(bytes_written)?)
|
Ok(types::Size::try_from(bytes_written)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fd_pwrite(
|
async fn fd_pwrite<'a>(
|
||||||
&self,
|
&self,
|
||||||
fd: types::Fd,
|
fd: types::Fd,
|
||||||
ciovs: &types::CiovecArray<'_>,
|
ciovs: &types::CiovecArray<'a>,
|
||||||
offset: types::Filesize,
|
offset: types::Filesize,
|
||||||
) -> Result<types::Size, Error> {
|
) -> Result<types::Size, Error> {
|
||||||
let table = self.table();
|
let table = self.table();
|
||||||
@@ -519,12 +542,12 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|s| IoSlice::new(s.deref()))
|
.map(|s| IoSlice::new(s.deref()))
|
||||||
.collect();
|
.collect();
|
||||||
let bytes_written = f.write_vectored_at(&ioslices, offset)?;
|
let bytes_written = f.write_vectored_at(&ioslices, offset).await?;
|
||||||
|
|
||||||
Ok(types::Size::try_from(bytes_written)?)
|
Ok(types::Size::try_from(bytes_written)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fd_prestat_get(&self, fd: types::Fd) -> Result<types::Prestat, Error> {
|
async fn fd_prestat_get(&self, fd: types::Fd) -> Result<types::Prestat, Error> {
|
||||||
let table = self.table();
|
let table = self.table();
|
||||||
let dir_entry: Ref<DirEntry> = table.get(u32::from(fd)).map_err(|_| Error::badf())?;
|
let dir_entry: Ref<DirEntry> = table.get(u32::from(fd)).map_err(|_| Error::badf())?;
|
||||||
if let Some(ref preopen) = dir_entry.preopen_path() {
|
if let Some(ref preopen) = dir_entry.preopen_path() {
|
||||||
@@ -536,10 +559,10 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fd_prestat_dir_name(
|
async fn fd_prestat_dir_name<'a>(
|
||||||
&self,
|
&self,
|
||||||
fd: types::Fd,
|
fd: types::Fd,
|
||||||
path: &GuestPtr<u8>,
|
path: &GuestPtr<'a, u8>,
|
||||||
path_max_len: types::Size,
|
path_max_len: types::Size,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let table = self.table();
|
let table = self.table();
|
||||||
@@ -560,7 +583,7 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
|||||||
Err(Error::not_supported())
|
Err(Error::not_supported())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn fd_renumber(&self, from: types::Fd, to: types::Fd) -> Result<(), Error> {
|
async fn fd_renumber(&self, from: types::Fd, to: types::Fd) -> Result<(), Error> {
|
||||||
let mut table = self.table();
|
let mut table = self.table();
|
||||||
let from = u32::from(from);
|
let from = u32::from(from);
|
||||||
let to = u32::from(to);
|
let to = u32::from(to);
|
||||||
@@ -577,7 +600,7 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fd_seek(
|
async fn fd_seek(
|
||||||
&self,
|
&self,
|
||||||
fd: types::Fd,
|
fd: types::Fd,
|
||||||
offset: types::Filedelta,
|
offset: types::Filedelta,
|
||||||
@@ -600,32 +623,35 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
|||||||
.table()
|
.table()
|
||||||
.get_file(u32::from(fd))?
|
.get_file(u32::from(fd))?
|
||||||
.get_cap(required_caps)?
|
.get_cap(required_caps)?
|
||||||
.seek(whence)?;
|
.seek(whence)
|
||||||
|
.await?;
|
||||||
Ok(newoffset)
|
Ok(newoffset)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fd_sync(&self, fd: types::Fd) -> Result<(), Error> {
|
async fn fd_sync(&self, fd: types::Fd) -> Result<(), Error> {
|
||||||
self.table()
|
self.table()
|
||||||
.get_file(u32::from(fd))?
|
.get_file(u32::from(fd))?
|
||||||
.get_cap(FileCaps::SYNC)?
|
.get_cap(FileCaps::SYNC)?
|
||||||
.sync()?;
|
.sync()
|
||||||
|
.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fd_tell(&self, fd: types::Fd) -> Result<types::Filesize, Error> {
|
async fn fd_tell(&self, fd: types::Fd) -> Result<types::Filesize, Error> {
|
||||||
// XXX should this be stream_position?
|
// XXX should this be stream_position?
|
||||||
let offset = self
|
let offset = self
|
||||||
.table()
|
.table()
|
||||||
.get_file(u32::from(fd))?
|
.get_file(u32::from(fd))?
|
||||||
.get_cap(FileCaps::TELL)?
|
.get_cap(FileCaps::TELL)?
|
||||||
.seek(std::io::SeekFrom::Current(0))?;
|
.seek(std::io::SeekFrom::Current(0))
|
||||||
|
.await?;
|
||||||
Ok(offset)
|
Ok(offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fd_readdir(
|
async fn fd_readdir<'a>(
|
||||||
&self,
|
&self,
|
||||||
fd: types::Fd,
|
fd: types::Fd,
|
||||||
buf: &GuestPtr<u8>,
|
buf: &GuestPtr<'a, u8>,
|
||||||
buf_len: types::Size,
|
buf_len: types::Size,
|
||||||
cookie: types::Dircookie,
|
cookie: types::Dircookie,
|
||||||
) -> Result<types::Size, Error> {
|
) -> Result<types::Size, Error> {
|
||||||
@@ -635,7 +661,8 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
|||||||
.table()
|
.table()
|
||||||
.get_dir(u32::from(fd))?
|
.get_dir(u32::from(fd))?
|
||||||
.get_cap(DirCaps::READDIR)?
|
.get_cap(DirCaps::READDIR)?
|
||||||
.readdir(ReaddirCursor::from(cookie))?
|
.readdir(ReaddirCursor::from(cookie))
|
||||||
|
.await?
|
||||||
{
|
{
|
||||||
let entity = entity?;
|
let entity = entity?;
|
||||||
let dirent_raw = dirent_bytes(types::Dirent::try_from(&entity)?);
|
let dirent_raw = dirent_bytes(types::Dirent::try_from(&entity)?);
|
||||||
@@ -675,22 +702,23 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
|||||||
Ok(bufused)
|
Ok(bufused)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path_create_directory(
|
async fn path_create_directory<'a>(
|
||||||
&self,
|
&self,
|
||||||
dirfd: types::Fd,
|
dirfd: types::Fd,
|
||||||
path: &GuestPtr<'_, str>,
|
path: &GuestPtr<'a, str>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
self.table()
|
self.table()
|
||||||
.get_dir(u32::from(dirfd))?
|
.get_dir(u32::from(dirfd))?
|
||||||
.get_cap(DirCaps::CREATE_DIRECTORY)?
|
.get_cap(DirCaps::CREATE_DIRECTORY)?
|
||||||
.create_dir(path.as_str()?.deref())
|
.create_dir(path.as_str()?.deref())
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path_filestat_get(
|
async fn path_filestat_get<'a>(
|
||||||
&self,
|
&self,
|
||||||
dirfd: types::Fd,
|
dirfd: types::Fd,
|
||||||
flags: types::Lookupflags,
|
flags: types::Lookupflags,
|
||||||
path: &GuestPtr<'_, str>,
|
path: &GuestPtr<'a, str>,
|
||||||
) -> Result<types::Filestat, Error> {
|
) -> Result<types::Filestat, Error> {
|
||||||
let filestat = self
|
let filestat = self
|
||||||
.table()
|
.table()
|
||||||
@@ -699,15 +727,16 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
|||||||
.get_path_filestat(
|
.get_path_filestat(
|
||||||
path.as_str()?.deref(),
|
path.as_str()?.deref(),
|
||||||
flags.contains(types::Lookupflags::SYMLINK_FOLLOW),
|
flags.contains(types::Lookupflags::SYMLINK_FOLLOW),
|
||||||
)?;
|
)
|
||||||
|
.await?;
|
||||||
Ok(types::Filestat::from(filestat))
|
Ok(types::Filestat::from(filestat))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path_filestat_set_times(
|
async fn path_filestat_set_times<'a>(
|
||||||
&self,
|
&self,
|
||||||
dirfd: types::Fd,
|
dirfd: types::Fd,
|
||||||
flags: types::Lookupflags,
|
flags: types::Lookupflags,
|
||||||
path: &GuestPtr<'_, str>,
|
path: &GuestPtr<'a, str>,
|
||||||
atim: types::Timestamp,
|
atim: types::Timestamp,
|
||||||
mtim: types::Timestamp,
|
mtim: types::Timestamp,
|
||||||
fst_flags: types::Fstflags,
|
fst_flags: types::Fstflags,
|
||||||
@@ -728,15 +757,16 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
|||||||
mtim,
|
mtim,
|
||||||
flags.contains(types::Lookupflags::SYMLINK_FOLLOW),
|
flags.contains(types::Lookupflags::SYMLINK_FOLLOW),
|
||||||
)
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path_link(
|
async fn path_link<'a>(
|
||||||
&self,
|
&self,
|
||||||
src_fd: types::Fd,
|
src_fd: types::Fd,
|
||||||
src_flags: types::Lookupflags,
|
src_flags: types::Lookupflags,
|
||||||
src_path: &GuestPtr<'_, str>,
|
src_path: &GuestPtr<'a, str>,
|
||||||
target_fd: types::Fd,
|
target_fd: types::Fd,
|
||||||
target_path: &GuestPtr<'_, str>,
|
target_path: &GuestPtr<'a, str>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let table = self.table();
|
let table = self.table();
|
||||||
let src_dir = table
|
let src_dir = table
|
||||||
@@ -751,18 +781,20 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
|||||||
.context("symlink following on path_link is not supported"));
|
.context("symlink following on path_link is not supported"));
|
||||||
}
|
}
|
||||||
|
|
||||||
src_dir.hard_link(
|
src_dir
|
||||||
|
.hard_link(
|
||||||
src_path.as_str()?.deref(),
|
src_path.as_str()?.deref(),
|
||||||
target_dir.deref(),
|
target_dir.deref(),
|
||||||
target_path.as_str()?.deref(),
|
target_path.as_str()?.deref(),
|
||||||
)
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path_open(
|
async fn path_open<'a>(
|
||||||
&self,
|
&self,
|
||||||
dirfd: types::Fd,
|
dirfd: types::Fd,
|
||||||
dirflags: types::Lookupflags,
|
dirflags: types::Lookupflags,
|
||||||
path: &GuestPtr<'_, str>,
|
path: &GuestPtr<'a, str>,
|
||||||
oflags: types::Oflags,
|
oflags: types::Oflags,
|
||||||
fs_rights_base: types::Rights,
|
fs_rights_base: types::Rights,
|
||||||
fs_rights_inheriting: types::Rights,
|
fs_rights_inheriting: types::Rights,
|
||||||
@@ -790,7 +822,7 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
|||||||
let dir_caps = dir_entry.child_dir_caps(DirCaps::from(&fs_rights_base));
|
let dir_caps = dir_entry.child_dir_caps(DirCaps::from(&fs_rights_base));
|
||||||
let file_caps = dir_entry.child_file_caps(FileCaps::from(&fs_rights_inheriting));
|
let file_caps = dir_entry.child_file_caps(FileCaps::from(&fs_rights_inheriting));
|
||||||
let dir = dir_entry.get_cap(DirCaps::OPEN)?;
|
let dir = dir_entry.get_cap(DirCaps::OPEN)?;
|
||||||
let child_dir = dir.open_dir(symlink_follow, path.deref())?;
|
let child_dir = dir.open_dir(symlink_follow, path.deref()).await?;
|
||||||
drop(dir);
|
drop(dir);
|
||||||
let fd = table.push(Box::new(DirEntry::new(
|
let fd = table.push(Box::new(DirEntry::new(
|
||||||
dir_caps, file_caps, None, child_dir,
|
dir_caps, file_caps, None, child_dir,
|
||||||
@@ -808,25 +840,28 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
|||||||
let write = file_caps.contains(FileCaps::WRITE)
|
let write = file_caps.contains(FileCaps::WRITE)
|
||||||
|| file_caps.contains(FileCaps::ALLOCATE)
|
|| file_caps.contains(FileCaps::ALLOCATE)
|
||||||
|| file_caps.contains(FileCaps::FILESTAT_SET_SIZE);
|
|| file_caps.contains(FileCaps::FILESTAT_SET_SIZE);
|
||||||
let file = dir.open_file(symlink_follow, path.deref(), oflags, read, write, fdflags)?;
|
let file = dir
|
||||||
|
.open_file(symlink_follow, path.deref(), oflags, read, write, fdflags)
|
||||||
|
.await?;
|
||||||
drop(dir);
|
drop(dir);
|
||||||
let fd = table.push(Box::new(FileEntry::new(file_caps, file)))?;
|
let fd = table.push(Box::new(FileEntry::new(file_caps, file)))?;
|
||||||
Ok(types::Fd::from(fd))
|
Ok(types::Fd::from(fd))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path_readlink(
|
async fn path_readlink<'a>(
|
||||||
&self,
|
&self,
|
||||||
dirfd: types::Fd,
|
dirfd: types::Fd,
|
||||||
path: &GuestPtr<'_, str>,
|
path: &GuestPtr<'a, str>,
|
||||||
buf: &GuestPtr<u8>,
|
buf: &GuestPtr<'a, u8>,
|
||||||
buf_len: types::Size,
|
buf_len: types::Size,
|
||||||
) -> Result<types::Size, Error> {
|
) -> Result<types::Size, Error> {
|
||||||
let link = self
|
let link = self
|
||||||
.table()
|
.table()
|
||||||
.get_dir(u32::from(dirfd))?
|
.get_dir(u32::from(dirfd))?
|
||||||
.get_cap(DirCaps::READLINK)?
|
.get_cap(DirCaps::READLINK)?
|
||||||
.read_link(path.as_str()?.deref())?
|
.read_link(path.as_str()?.deref())
|
||||||
|
.await?
|
||||||
.into_os_string()
|
.into_os_string()
|
||||||
.into_string()
|
.into_string()
|
||||||
.map_err(|_| Error::illegal_byte_sequence().context("link contents"))?;
|
.map_err(|_| Error::illegal_byte_sequence().context("link contents"))?;
|
||||||
@@ -840,23 +875,24 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
|||||||
Ok(link_len as types::Size)
|
Ok(link_len as types::Size)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path_remove_directory(
|
async fn path_remove_directory<'a>(
|
||||||
&self,
|
&self,
|
||||||
dirfd: types::Fd,
|
dirfd: types::Fd,
|
||||||
path: &GuestPtr<'_, str>,
|
path: &GuestPtr<'a, str>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
self.table()
|
self.table()
|
||||||
.get_dir(u32::from(dirfd))?
|
.get_dir(u32::from(dirfd))?
|
||||||
.get_cap(DirCaps::REMOVE_DIRECTORY)?
|
.get_cap(DirCaps::REMOVE_DIRECTORY)?
|
||||||
.remove_dir(path.as_str()?.deref())
|
.remove_dir(path.as_str()?.deref())
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path_rename(
|
async fn path_rename<'a>(
|
||||||
&self,
|
&self,
|
||||||
src_fd: types::Fd,
|
src_fd: types::Fd,
|
||||||
src_path: &GuestPtr<'_, str>,
|
src_path: &GuestPtr<'a, str>,
|
||||||
dest_fd: types::Fd,
|
dest_fd: types::Fd,
|
||||||
dest_path: &GuestPtr<'_, str>,
|
dest_path: &GuestPtr<'a, str>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let table = self.table();
|
let table = self.table();
|
||||||
let src_dir = table
|
let src_dir = table
|
||||||
@@ -865,36 +901,44 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
|||||||
let dest_dir = table
|
let dest_dir = table
|
||||||
.get_dir(u32::from(dest_fd))?
|
.get_dir(u32::from(dest_fd))?
|
||||||
.get_cap(DirCaps::RENAME_TARGET)?;
|
.get_cap(DirCaps::RENAME_TARGET)?;
|
||||||
src_dir.rename(
|
src_dir
|
||||||
|
.rename(
|
||||||
src_path.as_str()?.deref(),
|
src_path.as_str()?.deref(),
|
||||||
dest_dir.deref(),
|
dest_dir.deref(),
|
||||||
dest_path.as_str()?.deref(),
|
dest_path.as_str()?.deref(),
|
||||||
)
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path_symlink(
|
async fn path_symlink<'a>(
|
||||||
&self,
|
&self,
|
||||||
src_path: &GuestPtr<'_, str>,
|
src_path: &GuestPtr<'a, str>,
|
||||||
dirfd: types::Fd,
|
dirfd: types::Fd,
|
||||||
dest_path: &GuestPtr<'_, str>,
|
dest_path: &GuestPtr<'a, str>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
self.table()
|
self.table()
|
||||||
.get_dir(u32::from(dirfd))?
|
.get_dir(u32::from(dirfd))?
|
||||||
.get_cap(DirCaps::SYMLINK)?
|
.get_cap(DirCaps::SYMLINK)?
|
||||||
.symlink(src_path.as_str()?.deref(), dest_path.as_str()?.deref())
|
.symlink(src_path.as_str()?.deref(), dest_path.as_str()?.deref())
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path_unlink_file(&self, dirfd: types::Fd, path: &GuestPtr<'_, str>) -> Result<(), Error> {
|
async fn path_unlink_file<'a>(
|
||||||
|
&self,
|
||||||
|
dirfd: types::Fd,
|
||||||
|
path: &GuestPtr<'a, str>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
self.table()
|
self.table()
|
||||||
.get_dir(u32::from(dirfd))?
|
.get_dir(u32::from(dirfd))?
|
||||||
.get_cap(DirCaps::UNLINK_FILE)?
|
.get_cap(DirCaps::UNLINK_FILE)?
|
||||||
.unlink_file(path.as_str()?.deref())
|
.unlink_file(path.as_str()?.deref())
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll_oneoff(
|
async fn poll_oneoff<'a>(
|
||||||
&self,
|
&self,
|
||||||
subs: &GuestPtr<types::Subscription>,
|
subs: &GuestPtr<'a, types::Subscription>,
|
||||||
events: &GuestPtr<types::Event>,
|
events: &GuestPtr<'a, types::Event>,
|
||||||
nsubscriptions: types::Size,
|
nsubscriptions: types::Size,
|
||||||
) -> Result<types::Size, Error> {
|
) -> Result<types::Size, Error> {
|
||||||
if nsubscriptions == 0 {
|
if nsubscriptions == 0 {
|
||||||
@@ -912,7 +956,9 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
|||||||
.flags
|
.flags
|
||||||
.contains(types::Subclockflags::SUBSCRIPTION_CLOCK_ABSTIME)
|
.contains(types::Subclockflags::SUBSCRIPTION_CLOCK_ABSTIME)
|
||||||
{
|
{
|
||||||
self.sched.sleep(Duration::from_nanos(clocksub.timeout))?;
|
self.sched
|
||||||
|
.sleep(Duration::from_nanos(clocksub.timeout))
|
||||||
|
.await?;
|
||||||
events.write(types::Event {
|
events.write(types::Event {
|
||||||
userdata: sub.userdata,
|
userdata: sub.userdata,
|
||||||
error: types::Errno::Success,
|
error: types::Errno::Success,
|
||||||
@@ -925,6 +971,10 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let table = self.table();
|
let table = self.table();
|
||||||
|
let mut sub_fds: HashSet<types::Fd> = HashSet::new();
|
||||||
|
// We need these refmuts to outlive Poll, which will hold the &mut dyn WasiFile inside
|
||||||
|
let mut read_refs: Vec<(RefMut<'_, dyn WasiFile>, Userdata)> = Vec::new();
|
||||||
|
let mut write_refs: Vec<(RefMut<'_, dyn WasiFile>, Userdata)> = Vec::new();
|
||||||
let mut poll = Poll::new();
|
let mut poll = Poll::new();
|
||||||
|
|
||||||
let subs = subs.as_array(nsubscriptions);
|
let subs = subs.as_array(nsubscriptions);
|
||||||
@@ -963,22 +1013,41 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
|||||||
},
|
},
|
||||||
types::SubscriptionU::FdRead(readsub) => {
|
types::SubscriptionU::FdRead(readsub) => {
|
||||||
let fd = readsub.file_descriptor;
|
let fd = readsub.file_descriptor;
|
||||||
let file = table
|
if sub_fds.contains(&fd) {
|
||||||
.get_file(u32::from(fd))?
|
return Err(Error::invalid_argument()
|
||||||
|
.context("Fd can be subscribed to at most once per poll"));
|
||||||
|
} else {
|
||||||
|
sub_fds.insert(fd);
|
||||||
|
}
|
||||||
|
let file_ref = table
|
||||||
|
.get_file_mut(u32::from(fd))?
|
||||||
.get_cap(FileCaps::POLL_READWRITE)?;
|
.get_cap(FileCaps::POLL_READWRITE)?;
|
||||||
poll.subscribe_read(file, sub.userdata.into());
|
read_refs.push((file_ref, sub.userdata.into()));
|
||||||
}
|
}
|
||||||
types::SubscriptionU::FdWrite(writesub) => {
|
types::SubscriptionU::FdWrite(writesub) => {
|
||||||
let fd = writesub.file_descriptor;
|
let fd = writesub.file_descriptor;
|
||||||
let file = table
|
if sub_fds.contains(&fd) {
|
||||||
.get_file(u32::from(fd))?
|
return Err(Error::invalid_argument()
|
||||||
|
.context("Fd can be subscribed to at most once per poll"));
|
||||||
|
} else {
|
||||||
|
sub_fds.insert(fd);
|
||||||
|
}
|
||||||
|
let file_ref = table
|
||||||
|
.get_file_mut(u32::from(fd))?
|
||||||
.get_cap(FileCaps::POLL_READWRITE)?;
|
.get_cap(FileCaps::POLL_READWRITE)?;
|
||||||
poll.subscribe_write(file, sub.userdata.into());
|
write_refs.push((file_ref, sub.userdata.into()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.sched.poll_oneoff(&poll)?;
|
for (f, ud) in read_refs.iter_mut() {
|
||||||
|
poll.subscribe_read(f.deref_mut(), *ud);
|
||||||
|
}
|
||||||
|
for (f, ud) in write_refs.iter_mut() {
|
||||||
|
poll.subscribe_write(f.deref_mut(), *ud);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.sched.poll_oneoff(&mut poll).await?;
|
||||||
|
|
||||||
let results = poll.results();
|
let results = poll.results();
|
||||||
let num_results = results.len();
|
let num_results = results.len();
|
||||||
@@ -1053,7 +1122,7 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
|||||||
Ok(num_results.try_into().expect("results fit into memory"))
|
Ok(num_results.try_into().expect("results fit into memory"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn proc_exit(&self, status: types::Exitcode) -> wiggle::Trap {
|
async fn proc_exit(&self, status: types::Exitcode) -> wiggle::Trap {
|
||||||
// Check that the status is within WASI's range.
|
// Check that the status is within WASI's range.
|
||||||
if status < 126 {
|
if status < 126 {
|
||||||
wiggle::Trap::I32Exit(status as i32)
|
wiggle::Trap::I32Exit(status as i32)
|
||||||
@@ -1062,39 +1131,43 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn proc_raise(&self, _sig: types::Signal) -> Result<(), Error> {
|
async fn proc_raise(&self, _sig: types::Signal) -> Result<(), Error> {
|
||||||
Err(Error::trap("proc_raise unsupported"))
|
Err(Error::trap("proc_raise unsupported"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sched_yield(&self) -> Result<(), Error> {
|
async fn sched_yield(&self) -> Result<(), Error> {
|
||||||
self.sched.sched_yield()
|
self.sched.sched_yield().await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn random_get(&self, buf: &GuestPtr<u8>, buf_len: types::Size) -> Result<(), Error> {
|
async fn random_get<'a>(
|
||||||
|
&self,
|
||||||
|
buf: &GuestPtr<'a, u8>,
|
||||||
|
buf_len: types::Size,
|
||||||
|
) -> Result<(), Error> {
|
||||||
let mut buf = buf.as_array(buf_len).as_slice_mut()?;
|
let mut buf = buf.as_array(buf_len).as_slice_mut()?;
|
||||||
self.random.borrow_mut().try_fill_bytes(buf.deref_mut())?;
|
self.random.borrow_mut().try_fill_bytes(buf.deref_mut())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sock_recv(
|
async fn sock_recv<'a>(
|
||||||
&self,
|
&self,
|
||||||
_fd: types::Fd,
|
_fd: types::Fd,
|
||||||
_ri_data: &types::IovecArray<'_>,
|
_ri_data: &types::IovecArray<'a>,
|
||||||
_ri_flags: types::Riflags,
|
_ri_flags: types::Riflags,
|
||||||
) -> Result<(types::Size, types::Roflags), Error> {
|
) -> Result<(types::Size, types::Roflags), Error> {
|
||||||
Err(Error::trap("sock_recv unsupported"))
|
Err(Error::trap("sock_recv unsupported"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sock_send(
|
async fn sock_send<'a>(
|
||||||
&self,
|
&self,
|
||||||
_fd: types::Fd,
|
_fd: types::Fd,
|
||||||
_si_data: &types::CiovecArray<'_>,
|
_si_data: &types::CiovecArray<'a>,
|
||||||
_si_flags: types::Siflags,
|
_si_flags: types::Siflags,
|
||||||
) -> Result<types::Size, Error> {
|
) -> Result<types::Size, Error> {
|
||||||
Err(Error::trap("sock_send unsupported"))
|
Err(Error::trap("sock_send unsupported"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sock_shutdown(&self, _fd: types::Fd, _how: types::Sdflags) -> Result<(), Error> {
|
async fn sock_shutdown(&self, _fd: types::Fd, _how: types::Sdflags) -> Result<(), Error> {
|
||||||
Err(Error::trap("sock_shutdown unsupported"))
|
Err(Error::trap("sock_shutdown unsupported"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
42
crates/wasi-common/tokio/Cargo.toml
Normal file
42
crates/wasi-common/tokio/Cargo.toml
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
[package]
|
||||||
|
name = "wasi-tokio"
|
||||||
|
version = "0.26.0"
|
||||||
|
authors = ["The Wasmtime Project Developers"]
|
||||||
|
description = "WASI implementation in Rust"
|
||||||
|
license = "Apache-2.0 WITH LLVM-exception"
|
||||||
|
categories = ["wasm"]
|
||||||
|
keywords = ["webassembly", "wasm"]
|
||||||
|
repository = "https://github.com/bytecodealliance/wasmtime"
|
||||||
|
readme = "README.md"
|
||||||
|
edition = "2018"
|
||||||
|
include = ["src/**/*", "LICENSE" ]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
wasi-common = { path = "../", version = "0.26.0" }
|
||||||
|
wasi-cap-std-sync = { path = "../cap-std-sync", version = "0.26.0" }
|
||||||
|
wiggle = { path = "../../wiggle", version = "0.26.0" }
|
||||||
|
tokio = { version = "1.5.0", features = [ "rt", "fs", "time", "io-util", "net", "io-std", "rt-multi-thread"] }
|
||||||
|
cap-std = "0.13.7"
|
||||||
|
cap-fs-ext = "0.13.7"
|
||||||
|
cap-time-ext = "0.13.7"
|
||||||
|
fs-set-times = "0.3.1"
|
||||||
|
unsafe-io = "0.6.5"
|
||||||
|
system-interface = { version = "0.6.3", features = ["cap_std_impls"] }
|
||||||
|
tracing = "0.1.19"
|
||||||
|
bitflags = "1.2"
|
||||||
|
anyhow = "1"
|
||||||
|
|
||||||
|
[target.'cfg(unix)'.dependencies]
|
||||||
|
libc = "0.2"
|
||||||
|
posish = "0.6.1"
|
||||||
|
|
||||||
|
|
||||||
|
[target.'cfg(windows)'.dependencies]
|
||||||
|
winapi = "0.3"
|
||||||
|
lazy_static = "1.4"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
tempfile = "3.1.0"
|
||||||
|
tokio = { version = "1.5.0", features = [ "macros" ] }
|
||||||
|
anyhow = "1"
|
||||||
|
cap-tempfile = "0.13.7"
|
||||||
209
crates/wasi-common/tokio/src/dir.rs
Normal file
209
crates/wasi-common/tokio/src/dir.rs
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
use crate::{block_on_dummy_executor, file::File};
|
||||||
|
use std::any::Any;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use wasi_common::{
|
||||||
|
dir::{ReaddirCursor, ReaddirEntity, WasiDir},
|
||||||
|
file::{FdFlags, Filestat, OFlags, WasiFile},
|
||||||
|
Error, ErrorExt,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct Dir(wasi_cap_std_sync::dir::Dir);
|
||||||
|
|
||||||
|
impl Dir {
|
||||||
|
pub fn from_cap_std(dir: cap_std::fs::Dir) -> Self {
|
||||||
|
Dir(wasi_cap_std_sync::dir::Dir::from_cap_std(dir))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wiggle::async_trait]
|
||||||
|
impl WasiDir for Dir {
|
||||||
|
fn as_any(&self) -> &dyn Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
async fn open_file(
|
||||||
|
&self,
|
||||||
|
symlink_follow: bool,
|
||||||
|
path: &str,
|
||||||
|
oflags: OFlags,
|
||||||
|
read: bool,
|
||||||
|
write: bool,
|
||||||
|
fdflags: FdFlags,
|
||||||
|
) -> Result<Box<dyn WasiFile>, Error> {
|
||||||
|
let f = block_on_dummy_executor(move || async move {
|
||||||
|
self.0
|
||||||
|
.open_file_(symlink_follow, path, oflags, read, write, fdflags)
|
||||||
|
})?;
|
||||||
|
Ok(Box::new(File::from_inner(f)))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn open_dir(&self, symlink_follow: bool, path: &str) -> Result<Box<dyn WasiDir>, Error> {
|
||||||
|
let d =
|
||||||
|
block_on_dummy_executor(move || async move { self.0.open_dir_(symlink_follow, path) })?;
|
||||||
|
Ok(Box::new(Dir(d)))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create_dir(&self, path: &str) -> Result<(), Error> {
|
||||||
|
block_on_dummy_executor(|| self.0.create_dir(path))
|
||||||
|
}
|
||||||
|
async fn readdir(
|
||||||
|
&self,
|
||||||
|
cursor: ReaddirCursor,
|
||||||
|
) -> Result<Box<dyn Iterator<Item = Result<ReaddirEntity, Error>> + Send>, Error> {
|
||||||
|
struct I(Box<dyn Iterator<Item = Result<ReaddirEntity, Error>> + Send>);
|
||||||
|
impl Iterator for I {
|
||||||
|
type Item = Result<ReaddirEntity, Error>;
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
tokio::task::block_in_place(move || self.0.next())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let inner = block_on_dummy_executor(move || self.0.readdir(cursor))?;
|
||||||
|
Ok(Box::new(I(inner)))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn symlink(&self, src_path: &str, dest_path: &str) -> Result<(), Error> {
|
||||||
|
block_on_dummy_executor(move || self.0.symlink(src_path, dest_path))
|
||||||
|
}
|
||||||
|
async fn remove_dir(&self, path: &str) -> Result<(), Error> {
|
||||||
|
block_on_dummy_executor(move || self.0.remove_dir(path))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn unlink_file(&self, path: &str) -> Result<(), Error> {
|
||||||
|
block_on_dummy_executor(move || self.0.unlink_file(path))
|
||||||
|
}
|
||||||
|
async fn read_link(&self, path: &str) -> Result<PathBuf, Error> {
|
||||||
|
block_on_dummy_executor(move || self.0.read_link(path))
|
||||||
|
}
|
||||||
|
async fn get_filestat(&self) -> Result<Filestat, Error> {
|
||||||
|
block_on_dummy_executor(|| self.0.get_filestat())
|
||||||
|
}
|
||||||
|
async fn get_path_filestat(
|
||||||
|
&self,
|
||||||
|
path: &str,
|
||||||
|
follow_symlinks: bool,
|
||||||
|
) -> Result<Filestat, Error> {
|
||||||
|
block_on_dummy_executor(move || self.0.get_path_filestat(path, follow_symlinks))
|
||||||
|
}
|
||||||
|
async fn rename(
|
||||||
|
&self,
|
||||||
|
src_path: &str,
|
||||||
|
dest_dir: &dyn WasiDir,
|
||||||
|
dest_path: &str,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let dest_dir = dest_dir
|
||||||
|
.as_any()
|
||||||
|
.downcast_ref::<Self>()
|
||||||
|
.ok_or(Error::badf().context("failed downcast to tokio Dir"))?;
|
||||||
|
block_on_dummy_executor(
|
||||||
|
move || async move { self.0.rename_(src_path, &dest_dir.0, dest_path) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
async fn hard_link(
|
||||||
|
&self,
|
||||||
|
src_path: &str,
|
||||||
|
target_dir: &dyn WasiDir,
|
||||||
|
target_path: &str,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let target_dir = target_dir
|
||||||
|
.as_any()
|
||||||
|
.downcast_ref::<Self>()
|
||||||
|
.ok_or(Error::badf().context("failed downcast to tokio Dir"))?;
|
||||||
|
block_on_dummy_executor(move || async move {
|
||||||
|
self.0.hard_link_(src_path, &target_dir.0, target_path)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
async fn set_times(
|
||||||
|
&self,
|
||||||
|
path: &str,
|
||||||
|
atime: Option<wasi_common::SystemTimeSpec>,
|
||||||
|
mtime: Option<wasi_common::SystemTimeSpec>,
|
||||||
|
follow_symlinks: bool,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
block_on_dummy_executor(move || self.0.set_times(path, atime, mtime, follow_symlinks))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::Dir;
|
||||||
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
|
async fn scratch_dir() {
|
||||||
|
let tempdir = tempfile::Builder::new()
|
||||||
|
.prefix("cap-std-sync")
|
||||||
|
.tempdir()
|
||||||
|
.expect("create temporary dir");
|
||||||
|
let preopen_dir = unsafe { cap_std::fs::Dir::open_ambient_dir(tempdir.path()) }
|
||||||
|
.expect("open ambient temporary dir");
|
||||||
|
let preopen_dir = Dir::from_cap_std(preopen_dir);
|
||||||
|
wasi_common::WasiDir::open_dir(&preopen_dir, false, ".")
|
||||||
|
.await
|
||||||
|
.expect("open the same directory via WasiDir abstraction");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Readdir does not work on windows, so we won't test it there.
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
|
async fn readdir() {
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use wasi_common::dir::{ReaddirCursor, ReaddirEntity, WasiDir};
|
||||||
|
use wasi_common::file::{FdFlags, FileType, OFlags};
|
||||||
|
|
||||||
|
async fn readdir_into_map(dir: &dyn WasiDir) -> HashMap<String, ReaddirEntity> {
|
||||||
|
let mut out = HashMap::new();
|
||||||
|
for readdir_result in dir
|
||||||
|
.readdir(ReaddirCursor::from(0))
|
||||||
|
.await
|
||||||
|
.expect("readdir succeeds")
|
||||||
|
{
|
||||||
|
let entity = readdir_result.expect("readdir entry is valid");
|
||||||
|
out.insert(entity.name.clone(), entity);
|
||||||
|
}
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
let tempdir = tempfile::Builder::new()
|
||||||
|
.prefix("cap-std-sync")
|
||||||
|
.tempdir()
|
||||||
|
.expect("create temporary dir");
|
||||||
|
let preopen_dir = unsafe { cap_std::fs::Dir::open_ambient_dir(tempdir.path()) }
|
||||||
|
.expect("open ambient temporary dir");
|
||||||
|
let preopen_dir = Dir::from_cap_std(preopen_dir);
|
||||||
|
|
||||||
|
let entities = readdir_into_map(&preopen_dir).await;
|
||||||
|
assert_eq!(
|
||||||
|
entities.len(),
|
||||||
|
2,
|
||||||
|
"should just be . and .. in empty dir: {:?}",
|
||||||
|
entities
|
||||||
|
);
|
||||||
|
assert!(entities.get(".").is_some());
|
||||||
|
assert!(entities.get("..").is_some());
|
||||||
|
|
||||||
|
preopen_dir
|
||||||
|
.open_file(
|
||||||
|
false,
|
||||||
|
"file1",
|
||||||
|
OFlags::CREATE,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
FdFlags::empty(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.expect("create file1");
|
||||||
|
|
||||||
|
let entities = readdir_into_map(&preopen_dir).await;
|
||||||
|
assert_eq!(entities.len(), 3, "should be ., .., file1 {:?}", entities);
|
||||||
|
assert_eq!(
|
||||||
|
entities.get(".").expect(". entry").filetype,
|
||||||
|
FileType::Directory
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
entities.get("..").expect(".. entry").filetype,
|
||||||
|
FileType::Directory
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
entities.get("file1").expect("file1 entry").filetype,
|
||||||
|
FileType::RegularFile
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
186
crates/wasi-common/tokio/src/file.rs
Normal file
186
crates/wasi-common/tokio/src/file.rs
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
use crate::block_on_dummy_executor;
|
||||||
|
use std::any::Any;
|
||||||
|
use std::io;
|
||||||
|
#[cfg(windows)]
|
||||||
|
use std::os::windows::io::{AsRawHandle, RawHandle};
|
||||||
|
use wasi_common::{
|
||||||
|
file::{Advice, FdFlags, FileType, Filestat, WasiFile},
|
||||||
|
Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct File(wasi_cap_std_sync::file::File);
|
||||||
|
|
||||||
|
impl File {
|
||||||
|
pub(crate) fn from_inner(file: wasi_cap_std_sync::file::File) -> Self {
|
||||||
|
File(file)
|
||||||
|
}
|
||||||
|
pub fn from_cap_std(file: cap_std::fs::File) -> Self {
|
||||||
|
Self::from_inner(wasi_cap_std_sync::file::File::from_cap_std(file))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Stdin(wasi_cap_std_sync::stdio::Stdin);
|
||||||
|
|
||||||
|
pub fn stdin() -> Stdin {
|
||||||
|
Stdin(wasi_cap_std_sync::stdio::stdin())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Stdout(wasi_cap_std_sync::stdio::Stdout);
|
||||||
|
|
||||||
|
pub fn stdout() -> Stdout {
|
||||||
|
Stdout(wasi_cap_std_sync::stdio::stdout())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Stderr(wasi_cap_std_sync::stdio::Stderr);
|
||||||
|
|
||||||
|
pub fn stderr() -> Stderr {
|
||||||
|
Stderr(wasi_cap_std_sync::stdio::stderr())
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! wasi_file_impl {
|
||||||
|
($ty:ty) => {
|
||||||
|
#[wiggle::async_trait]
|
||||||
|
impl WasiFile for $ty {
|
||||||
|
fn as_any(&self) -> &dyn Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
async fn datasync(&self) -> Result<(), Error> {
|
||||||
|
block_on_dummy_executor(|| self.0.datasync())
|
||||||
|
}
|
||||||
|
async fn sync(&self) -> Result<(), Error> {
|
||||||
|
block_on_dummy_executor(|| self.0.sync())
|
||||||
|
}
|
||||||
|
async fn get_filetype(&self) -> Result<FileType, Error> {
|
||||||
|
block_on_dummy_executor(|| self.0.get_filetype())
|
||||||
|
}
|
||||||
|
async fn get_fdflags(&self) -> Result<FdFlags, Error> {
|
||||||
|
block_on_dummy_executor(|| self.0.get_fdflags())
|
||||||
|
}
|
||||||
|
async fn set_fdflags(&mut self, fdflags: FdFlags) -> Result<(), Error> {
|
||||||
|
block_on_dummy_executor(|| self.0.set_fdflags(fdflags))
|
||||||
|
}
|
||||||
|
async fn get_filestat(&self) -> Result<Filestat, Error> {
|
||||||
|
block_on_dummy_executor(|| self.0.get_filestat())
|
||||||
|
}
|
||||||
|
async fn set_filestat_size(&self, size: u64) -> Result<(), Error> {
|
||||||
|
block_on_dummy_executor(move || self.0.set_filestat_size(size))
|
||||||
|
}
|
||||||
|
async fn advise(&self, offset: u64, len: u64, advice: Advice) -> Result<(), Error> {
|
||||||
|
block_on_dummy_executor(move || self.0.advise(offset, len, advice))
|
||||||
|
}
|
||||||
|
async fn allocate(&self, offset: u64, len: u64) -> Result<(), Error> {
|
||||||
|
block_on_dummy_executor(move || self.0.allocate(offset, len))
|
||||||
|
}
|
||||||
|
async fn read_vectored<'a>(
|
||||||
|
&self,
|
||||||
|
bufs: &mut [io::IoSliceMut<'a>],
|
||||||
|
) -> Result<u64, Error> {
|
||||||
|
block_on_dummy_executor(move || self.0.read_vectored(bufs))
|
||||||
|
}
|
||||||
|
async fn read_vectored_at<'a>(
|
||||||
|
&self,
|
||||||
|
bufs: &mut [io::IoSliceMut<'a>],
|
||||||
|
offset: u64,
|
||||||
|
) -> Result<u64, Error> {
|
||||||
|
block_on_dummy_executor(move || self.0.read_vectored_at(bufs, offset))
|
||||||
|
}
|
||||||
|
async fn write_vectored<'a>(&self, bufs: &[io::IoSlice<'a>]) -> Result<u64, Error> {
|
||||||
|
block_on_dummy_executor(move || self.0.write_vectored(bufs))
|
||||||
|
}
|
||||||
|
async fn write_vectored_at<'a>(
|
||||||
|
&self,
|
||||||
|
bufs: &[io::IoSlice<'a>],
|
||||||
|
offset: u64,
|
||||||
|
) -> Result<u64, Error> {
|
||||||
|
block_on_dummy_executor(move || self.0.write_vectored_at(bufs, offset))
|
||||||
|
}
|
||||||
|
async fn seek(&self, pos: std::io::SeekFrom) -> Result<u64, Error> {
|
||||||
|
block_on_dummy_executor(move || self.0.seek(pos))
|
||||||
|
}
|
||||||
|
async fn peek(&self, buf: &mut [u8]) -> Result<u64, Error> {
|
||||||
|
block_on_dummy_executor(move || self.0.peek(buf))
|
||||||
|
}
|
||||||
|
async fn set_times(
|
||||||
|
&self,
|
||||||
|
atime: Option<wasi_common::SystemTimeSpec>,
|
||||||
|
mtime: Option<wasi_common::SystemTimeSpec>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
block_on_dummy_executor(move || self.0.set_times(atime, mtime))
|
||||||
|
}
|
||||||
|
async fn num_ready_bytes(&self) -> Result<u64, Error> {
|
||||||
|
block_on_dummy_executor(|| self.0.num_ready_bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
async fn readable(&mut self) -> Result<(), Error> {
|
||||||
|
// The Inner impls OwnsRaw, which asserts exclusive use of the handle by the owned object.
|
||||||
|
// AsyncFd needs to wrap an owned `impl std::os::unix::io::AsRawFd`. Rather than introduce
|
||||||
|
// mutability to let it own the `Inner`, we are depending on the `&mut self` bound on this
|
||||||
|
// async method to ensure this is the only Future which can access the RawFd during the
|
||||||
|
// lifetime of the AsyncFd.
|
||||||
|
use tokio::io::{unix::AsyncFd, Interest};
|
||||||
|
use unsafe_io::os::posish::AsRawFd;
|
||||||
|
let rawfd = self.0.as_raw_fd();
|
||||||
|
match AsyncFd::with_interest(rawfd, Interest::READABLE) {
|
||||||
|
Ok(asyncfd) => {
|
||||||
|
let _ = asyncfd.readable().await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Err(e) if e.kind() == std::io::ErrorKind::PermissionDenied => {
|
||||||
|
// if e is EPERM, this file isnt supported by epoll because it is immediately
|
||||||
|
// available for reading:
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Err(e) => Err(e.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(windows)]
|
||||||
|
async fn readable(&mut self) -> Result<(), Error> {
|
||||||
|
// Windows uses a rawfd based scheduler :(
|
||||||
|
use wasi_common::ErrorExt;
|
||||||
|
Err(Error::badf())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
async fn writable(&mut self) -> Result<(), Error> {
|
||||||
|
// The Inner impls OwnsRaw, which asserts exclusive use of the handle by the owned object.
|
||||||
|
// AsyncFd needs to wrap an owned `impl std::os::unix::io::AsRawFd`. Rather than introduce
|
||||||
|
// mutability to let it own the `Inner`, we are depending on the `&mut self` bound on this
|
||||||
|
// async method to ensure this is the only Future which can access the RawFd during the
|
||||||
|
// lifetime of the AsyncFd.
|
||||||
|
use tokio::io::{unix::AsyncFd, Interest};
|
||||||
|
use unsafe_io::os::posish::AsRawFd;
|
||||||
|
let rawfd = self.0.as_raw_fd();
|
||||||
|
match AsyncFd::with_interest(rawfd, Interest::WRITABLE) {
|
||||||
|
Ok(asyncfd) => {
|
||||||
|
let _ = asyncfd.writable().await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Err(e) if e.kind() == std::io::ErrorKind::PermissionDenied => {
|
||||||
|
// if e is EPERM, this file isnt supported by epoll because it is immediately
|
||||||
|
// available for writing:
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Err(e) => Err(e.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(windows)]
|
||||||
|
async fn writable(&mut self) -> Result<(), Error> {
|
||||||
|
// Windows uses a rawfd based scheduler :(
|
||||||
|
use wasi_common::ErrorExt;
|
||||||
|
Err(Error::badf())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(windows)]
|
||||||
|
impl AsRawHandle for $ty {
|
||||||
|
fn as_raw_handle(&self) -> RawHandle {
|
||||||
|
self.0.as_raw_handle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
wasi_file_impl!(File);
|
||||||
|
wasi_file_impl!(Stdin);
|
||||||
|
wasi_file_impl!(Stdout);
|
||||||
|
wasi_file_impl!(Stderr);
|
||||||
114
crates/wasi-common/tokio/src/lib.rs
Normal file
114
crates/wasi-common/tokio/src/lib.rs
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
mod dir;
|
||||||
|
mod file;
|
||||||
|
pub mod sched;
|
||||||
|
pub mod stdio;
|
||||||
|
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::future::Future;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::rc::Rc;
|
||||||
|
pub use wasi_cap_std_sync::{clocks_ctx, random_ctx};
|
||||||
|
use wasi_common::{Error, Table, WasiCtx};
|
||||||
|
|
||||||
|
pub use dir::Dir;
|
||||||
|
pub use file::File;
|
||||||
|
|
||||||
|
use crate::sched::sched_ctx;
|
||||||
|
|
||||||
|
pub struct WasiCtxBuilder(wasi_common::WasiCtxBuilder);
|
||||||
|
|
||||||
|
impl WasiCtxBuilder {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
WasiCtxBuilder(WasiCtx::builder(
|
||||||
|
random_ctx(),
|
||||||
|
clocks_ctx(),
|
||||||
|
sched_ctx(),
|
||||||
|
Rc::new(RefCell::new(Table::new())),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
pub fn env(self, var: &str, value: &str) -> Result<Self, wasi_common::StringArrayError> {
|
||||||
|
let s = self.0.env(var, value)?;
|
||||||
|
Ok(WasiCtxBuilder(s))
|
||||||
|
}
|
||||||
|
pub fn envs(self, env: &[(String, String)]) -> Result<Self, wasi_common::StringArrayError> {
|
||||||
|
let mut s = self;
|
||||||
|
for (k, v) in env {
|
||||||
|
s = s.env(k, v)?;
|
||||||
|
}
|
||||||
|
Ok(s)
|
||||||
|
}
|
||||||
|
pub fn inherit_env(self) -> Result<Self, wasi_common::StringArrayError> {
|
||||||
|
let mut s = self.0;
|
||||||
|
for (key, value) in std::env::vars() {
|
||||||
|
s = s.env(&key, &value)?;
|
||||||
|
}
|
||||||
|
Ok(WasiCtxBuilder(s))
|
||||||
|
}
|
||||||
|
pub fn arg(self, arg: &str) -> Result<Self, wasi_common::StringArrayError> {
|
||||||
|
let s = self.0.arg(arg)?;
|
||||||
|
Ok(WasiCtxBuilder(s))
|
||||||
|
}
|
||||||
|
pub fn args(self, arg: &[String]) -> Result<Self, wasi_common::StringArrayError> {
|
||||||
|
let mut s = self;
|
||||||
|
for a in arg {
|
||||||
|
s = s.arg(&a)?;
|
||||||
|
}
|
||||||
|
Ok(s)
|
||||||
|
}
|
||||||
|
pub fn inherit_args(self) -> Result<Self, wasi_common::StringArrayError> {
|
||||||
|
let mut s = self.0;
|
||||||
|
for arg in std::env::args() {
|
||||||
|
s = s.arg(&arg)?;
|
||||||
|
}
|
||||||
|
Ok(WasiCtxBuilder(s))
|
||||||
|
}
|
||||||
|
pub fn stdin(self, f: Box<dyn wasi_common::WasiFile>) -> Self {
|
||||||
|
WasiCtxBuilder(self.0.stdin(f))
|
||||||
|
}
|
||||||
|
pub fn stdout(self, f: Box<dyn wasi_common::WasiFile>) -> Self {
|
||||||
|
WasiCtxBuilder(self.0.stdout(f))
|
||||||
|
}
|
||||||
|
pub fn stderr(self, f: Box<dyn wasi_common::WasiFile>) -> Self {
|
||||||
|
WasiCtxBuilder(self.0.stderr(f))
|
||||||
|
}
|
||||||
|
pub fn inherit_stdin(self) -> Self {
|
||||||
|
self.stdin(Box::new(crate::stdio::stdin()))
|
||||||
|
}
|
||||||
|
pub fn inherit_stdout(self) -> Self {
|
||||||
|
self.stdout(Box::new(crate::stdio::stdout()))
|
||||||
|
}
|
||||||
|
pub fn inherit_stderr(self) -> Self {
|
||||||
|
self.stderr(Box::new(crate::stdio::stderr()))
|
||||||
|
}
|
||||||
|
pub fn inherit_stdio(self) -> Self {
|
||||||
|
self.inherit_stdin().inherit_stdout().inherit_stderr()
|
||||||
|
}
|
||||||
|
pub fn preopened_dir(
|
||||||
|
self,
|
||||||
|
dir: cap_std::fs::Dir,
|
||||||
|
guest_path: impl AsRef<Path>,
|
||||||
|
) -> Result<Self, wasi_common::Error> {
|
||||||
|
let dir = Box::new(Dir::from_cap_std(dir));
|
||||||
|
Ok(WasiCtxBuilder(self.0.preopened_dir(dir, guest_path)?))
|
||||||
|
}
|
||||||
|
pub fn build(self) -> Result<WasiCtx, wasi_common::Error> {
|
||||||
|
self.0.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Much of this crate is implemented in terms of `async` methods from the
|
||||||
|
// wasi-cap-std-sync crate. These methods may be async in signature, however,
|
||||||
|
// they are synchronous in implementation (always Poll::Ready on first poll)
|
||||||
|
// and perform blocking syscalls.
|
||||||
|
//
|
||||||
|
// This function takes this blocking code and executes it using a dummy executor
|
||||||
|
// to assert its immediate readiness. We tell tokio this is a blocking operation
|
||||||
|
// with the block_in_place function.
|
||||||
|
pub(crate) fn block_on_dummy_executor<'a, F, Fut, T>(f: F) -> Result<T, Error>
|
||||||
|
where
|
||||||
|
F: FnOnce() -> Fut + Send + 'a,
|
||||||
|
Fut: Future<Output = Result<T, Error>>,
|
||||||
|
T: Send + 'static,
|
||||||
|
{
|
||||||
|
tokio::task::block_in_place(move || wiggle::run_in_dummy_executor(f()))
|
||||||
|
}
|
||||||
35
crates/wasi-common/tokio/src/sched.rs
Normal file
35
crates/wasi-common/tokio/src/sched.rs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
#[cfg(unix)]
|
||||||
|
mod unix;
|
||||||
|
#[cfg(unix)]
|
||||||
|
pub use unix::poll_oneoff;
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
mod windows;
|
||||||
|
#[cfg(windows)]
|
||||||
|
pub use windows::poll_oneoff;
|
||||||
|
|
||||||
|
use wasi_common::{
|
||||||
|
sched::{Duration, Poll, WasiSched},
|
||||||
|
Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn sched_ctx() -> Box<dyn wasi_common::WasiSched> {
|
||||||
|
struct AsyncSched;
|
||||||
|
|
||||||
|
#[wiggle::async_trait]
|
||||||
|
impl WasiSched for AsyncSched {
|
||||||
|
async fn poll_oneoff<'a>(&self, poll: &mut Poll<'a>) -> Result<(), Error> {
|
||||||
|
poll_oneoff(poll).await
|
||||||
|
}
|
||||||
|
async fn sched_yield(&self) -> Result<(), Error> {
|
||||||
|
tokio::task::yield_now().await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
async fn sleep(&self, duration: Duration) -> Result<(), Error> {
|
||||||
|
tokio::time::sleep(duration).await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Box::new(AsyncSched)
|
||||||
|
}
|
||||||
91
crates/wasi-common/tokio/src/sched/unix.rs
Normal file
91
crates/wasi-common/tokio/src/sched/unix.rs
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
use std::future::Future;
|
||||||
|
use std::pin::Pin;
|
||||||
|
use std::task::{Context, Poll as FPoll};
|
||||||
|
use wasi_common::{
|
||||||
|
sched::{
|
||||||
|
subscription::{RwEventFlags, Subscription},
|
||||||
|
Poll,
|
||||||
|
},
|
||||||
|
Context as _, Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FirstReady<'a, T>(Vec<Pin<Box<dyn Future<Output = T> + 'a>>>);
|
||||||
|
|
||||||
|
impl<'a, T> FirstReady<'a, T> {
|
||||||
|
fn new() -> Self {
|
||||||
|
FirstReady(Vec::new())
|
||||||
|
}
|
||||||
|
fn push(&mut self, f: impl Future<Output = T> + 'a) {
|
||||||
|
self.0.push(Box::pin(f));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> Future for FirstReady<'a, T> {
|
||||||
|
type Output = T;
|
||||||
|
fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> FPoll<T> {
|
||||||
|
let mut result = FPoll::Pending;
|
||||||
|
for f in self.as_mut().0.iter_mut() {
|
||||||
|
match f.as_mut().poll(cx) {
|
||||||
|
FPoll::Ready(r) => match result {
|
||||||
|
// First ready gets to set the result. But, continue the loop so all futures
|
||||||
|
// which are ready simultaneously (often on first poll) get to report their
|
||||||
|
// readiness.
|
||||||
|
FPoll::Pending => {
|
||||||
|
result = FPoll::Ready(r);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
_ => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn poll_oneoff<'a>(poll: &mut Poll<'a>) -> Result<(), Error> {
|
||||||
|
if poll.is_empty() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let duration = poll
|
||||||
|
.earliest_clock_deadline()
|
||||||
|
.map(|sub| sub.duration_until());
|
||||||
|
|
||||||
|
let mut futures = FirstReady::new();
|
||||||
|
for s in poll.rw_subscriptions() {
|
||||||
|
match s {
|
||||||
|
Subscription::Read(f) => {
|
||||||
|
futures.push(async move {
|
||||||
|
f.file.readable().await.context("readable future")?;
|
||||||
|
f.complete(
|
||||||
|
f.file
|
||||||
|
.num_ready_bytes()
|
||||||
|
.await
|
||||||
|
.context("read num_ready_bytes")?,
|
||||||
|
RwEventFlags::empty(),
|
||||||
|
);
|
||||||
|
Ok::<(), Error>(())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Subscription::Write(f) => {
|
||||||
|
futures.push(async move {
|
||||||
|
f.file.writable().await.context("writable future")?;
|
||||||
|
f.complete(0, RwEventFlags::empty());
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Subscription::MonotonicClock { .. } => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(Some(remaining_duration)) = duration {
|
||||||
|
match tokio::time::timeout(remaining_duration, futures).await {
|
||||||
|
Ok(r) => r?,
|
||||||
|
Err(_deadline_elapsed) => {}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
futures.await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
47
crates/wasi-common/tokio/src/sched/windows.rs
Normal file
47
crates/wasi-common/tokio/src/sched/windows.rs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
use crate::block_on_dummy_executor;
|
||||||
|
use std::os::windows::io::{AsRawHandle, RawHandle};
|
||||||
|
use wasi_cap_std_sync::sched::windows::poll_oneoff_;
|
||||||
|
use wasi_common::{file::WasiFile, sched::Poll, Error};
|
||||||
|
|
||||||
|
pub async fn poll_oneoff<'a>(poll: &mut Poll<'a>) -> Result<(), Error> {
|
||||||
|
// Tokio doesn't provide us the AsyncFd primitive on Windows, so instead
|
||||||
|
// we use the blocking poll_oneoff implementation from the wasi-cap-std-crate.
|
||||||
|
// We provide a function specific to this crate's WasiFile types for downcasting
|
||||||
|
// to a RawHandle.
|
||||||
|
block_on_dummy_executor(move || poll_oneoff_(poll, wasi_file_is_stdin, wasi_file_raw_handle))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn wasi_file_is_stdin(f: &dyn WasiFile) -> bool {
|
||||||
|
f.as_any().is::<crate::stdio::Stdin>()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wasi_file_raw_handle(f: &dyn WasiFile) -> Option<RawHandle> {
|
||||||
|
let a = f.as_any();
|
||||||
|
if a.is::<crate::file::File>() {
|
||||||
|
Some(
|
||||||
|
a.downcast_ref::<crate::file::File>()
|
||||||
|
.unwrap()
|
||||||
|
.as_raw_handle(),
|
||||||
|
)
|
||||||
|
} else if a.is::<crate::stdio::Stdin>() {
|
||||||
|
Some(
|
||||||
|
a.downcast_ref::<crate::stdio::Stdin>()
|
||||||
|
.unwrap()
|
||||||
|
.as_raw_handle(),
|
||||||
|
)
|
||||||
|
} else if a.is::<crate::stdio::Stdout>() {
|
||||||
|
Some(
|
||||||
|
a.downcast_ref::<crate::stdio::Stdout>()
|
||||||
|
.unwrap()
|
||||||
|
.as_raw_handle(),
|
||||||
|
)
|
||||||
|
} else if a.is::<crate::stdio::Stderr>() {
|
||||||
|
Some(
|
||||||
|
a.downcast_ref::<crate::stdio::Stderr>()
|
||||||
|
.unwrap()
|
||||||
|
.as_raw_handle(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
1
crates/wasi-common/tokio/src/stdio.rs
Normal file
1
crates/wasi-common/tokio/src/stdio.rs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
pub use crate::file::{stderr, stdin, stdout, Stderr, Stdin, Stdout};
|
||||||
158
crates/wasi-common/tokio/tests/poll_oneoff.rs
Normal file
158
crates/wasi-common/tokio/tests/poll_oneoff.rs
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
use anyhow::{Context, Error};
|
||||||
|
use cap_std::time::Duration;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use wasi_common::{
|
||||||
|
file::{FdFlags, OFlags},
|
||||||
|
sched::{Poll, RwEventFlags, SubscriptionResult, Userdata},
|
||||||
|
WasiDir, WasiFile,
|
||||||
|
};
|
||||||
|
use wasi_tokio::{clocks_ctx, sched::poll_oneoff, Dir};
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
|
async fn empty_file_readable() -> Result<(), Error> {
|
||||||
|
let clocks = clocks_ctx();
|
||||||
|
|
||||||
|
let workspace = unsafe { cap_tempfile::tempdir().expect("create tempdir") };
|
||||||
|
workspace.create_dir("d").context("create dir")?;
|
||||||
|
let d = workspace.open_dir("d").context("open dir")?;
|
||||||
|
let d = Dir::from_cap_std(d);
|
||||||
|
|
||||||
|
let f = d
|
||||||
|
.open_file(false, "f", OFlags::CREATE, false, true, FdFlags::empty())
|
||||||
|
.await
|
||||||
|
.context("create writable file f")?;
|
||||||
|
let to_write: Vec<u8> = vec![0];
|
||||||
|
f.write_vectored(&vec![std::io::IoSlice::new(&to_write)])
|
||||||
|
.await
|
||||||
|
.context("write to f")?;
|
||||||
|
drop(f);
|
||||||
|
|
||||||
|
let mut f = d
|
||||||
|
.open_file(false, "f", OFlags::empty(), true, false, FdFlags::empty())
|
||||||
|
.await
|
||||||
|
.context("open f as readable")?;
|
||||||
|
|
||||||
|
let mut poll = Poll::new();
|
||||||
|
poll.subscribe_read(&mut *f, Userdata::from(123));
|
||||||
|
// Timeout bounds time in poll_oneoff
|
||||||
|
poll.subscribe_monotonic_clock(
|
||||||
|
&*clocks.monotonic,
|
||||||
|
clocks
|
||||||
|
.monotonic
|
||||||
|
.now(clocks.monotonic.resolution())
|
||||||
|
.checked_add(Duration::from_millis(10))
|
||||||
|
.unwrap(),
|
||||||
|
clocks.monotonic.resolution(),
|
||||||
|
Userdata::from(0),
|
||||||
|
);
|
||||||
|
poll_oneoff(&mut poll).await?;
|
||||||
|
|
||||||
|
let events = poll.results();
|
||||||
|
|
||||||
|
match events.get(0).expect("at least one event") {
|
||||||
|
(SubscriptionResult::Read(Ok((1, flags))), ud) => {
|
||||||
|
assert_eq!(*flags, RwEventFlags::empty());
|
||||||
|
assert_eq!(*ud, Userdata::from(123));
|
||||||
|
}
|
||||||
|
_ => panic!("expected (Read(Ok(1, empty), 123), got: {:?}", events[0]),
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
|
async fn empty_file_writable() -> Result<(), Error> {
|
||||||
|
let clocks = clocks_ctx();
|
||||||
|
|
||||||
|
let workspace = unsafe { cap_tempfile::tempdir().expect("create tempdir") };
|
||||||
|
workspace.create_dir("d").context("create dir")?;
|
||||||
|
let d = workspace.open_dir("d").context("open dir")?;
|
||||||
|
let d = Dir::from_cap_std(d);
|
||||||
|
|
||||||
|
let mut writable_f = d
|
||||||
|
.open_file(false, "f", OFlags::CREATE, true, true, FdFlags::empty())
|
||||||
|
.await
|
||||||
|
.context("create writable file")?;
|
||||||
|
|
||||||
|
let mut poll = Poll::new();
|
||||||
|
poll.subscribe_write(&mut *writable_f, Userdata::from(123));
|
||||||
|
// Timeout bounds time in poll_oneoff
|
||||||
|
poll.subscribe_monotonic_clock(
|
||||||
|
&*clocks.monotonic,
|
||||||
|
clocks
|
||||||
|
.monotonic
|
||||||
|
.now(clocks.monotonic.resolution())
|
||||||
|
.checked_add(Duration::from_millis(10))
|
||||||
|
.unwrap(),
|
||||||
|
clocks.monotonic.resolution(),
|
||||||
|
Userdata::from(0),
|
||||||
|
);
|
||||||
|
poll_oneoff(&mut poll).await?;
|
||||||
|
|
||||||
|
let events = poll.results();
|
||||||
|
|
||||||
|
match events.get(0).expect("at least one event") {
|
||||||
|
(SubscriptionResult::Write(Ok((0, flags))), ud) => {
|
||||||
|
assert_eq!(*flags, RwEventFlags::empty());
|
||||||
|
assert_eq!(*ud, Userdata::from(123));
|
||||||
|
}
|
||||||
|
_ => panic!(""),
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
|
async fn stdio_readable() -> Result<(), Error> {
|
||||||
|
let clocks = clocks_ctx();
|
||||||
|
|
||||||
|
let deadline = clocks
|
||||||
|
.monotonic
|
||||||
|
.now(clocks.monotonic.resolution())
|
||||||
|
.checked_add(Duration::from_millis(10))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut waiting_on: HashMap<u64, Box<dyn WasiFile>> = vec![
|
||||||
|
(
|
||||||
|
1,
|
||||||
|
Box::new(wasi_tokio::stdio::stdout()) as Box<dyn WasiFile>,
|
||||||
|
),
|
||||||
|
(2, Box::new(wasi_tokio::stdio::stderr())),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
while !waiting_on.is_empty() {
|
||||||
|
let mut poll = Poll::new();
|
||||||
|
|
||||||
|
for (ix, file) in waiting_on.iter_mut() {
|
||||||
|
poll.subscribe_write(&mut **file, Userdata::from(*ix));
|
||||||
|
}
|
||||||
|
// Timeout bounds time in poll_oneoff
|
||||||
|
poll.subscribe_monotonic_clock(
|
||||||
|
&*clocks.monotonic,
|
||||||
|
deadline,
|
||||||
|
clocks.monotonic.resolution(),
|
||||||
|
Userdata::from(999),
|
||||||
|
);
|
||||||
|
poll_oneoff(&mut poll).await?;
|
||||||
|
let events = poll.results();
|
||||||
|
|
||||||
|
for e in events {
|
||||||
|
match e {
|
||||||
|
(SubscriptionResult::Write(Ok(_)), ud) => {
|
||||||
|
let _ = waiting_on.remove(&u64::from(ud));
|
||||||
|
}
|
||||||
|
(SubscriptionResult::Write(Err(_)), ud) => {
|
||||||
|
panic!("error on ix {}", u64::from(ud))
|
||||||
|
}
|
||||||
|
(SubscriptionResult::Read { .. }, _) => unreachable!(),
|
||||||
|
(SubscriptionResult::MonotonicClock { .. }, _) => {
|
||||||
|
panic!("timed out before stdin and stdout ready for reading")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -15,6 +15,7 @@ build = "build.rs"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
wasi-common = { path = "../wasi-common", version = "0.26.0" }
|
wasi-common = { path = "../wasi-common", version = "0.26.0" }
|
||||||
wasi-cap-std-sync = { path = "../wasi-common/cap-std-sync", version = "0.26.0", optional = true }
|
wasi-cap-std-sync = { path = "../wasi-common/cap-std-sync", version = "0.26.0", optional = true }
|
||||||
|
wasi-tokio = { path = "../wasi-common/tokio", version = "0.26.0", optional = true }
|
||||||
wiggle = { path = "../wiggle", default-features = false, version = "0.26.0" }
|
wiggle = { path = "../wiggle", default-features = false, version = "0.26.0" }
|
||||||
wasmtime-wiggle = { path = "../wiggle/wasmtime", default-features = false, version = "0.26.0" }
|
wasmtime-wiggle = { path = "../wiggle/wasmtime", default-features = false, version = "0.26.0" }
|
||||||
wasmtime = { path = "../wasmtime", default-features = false, version = "0.26.0" }
|
wasmtime = { path = "../wasmtime", default-features = false, version = "0.26.0" }
|
||||||
@@ -23,3 +24,4 @@ anyhow = "1.0"
|
|||||||
[features]
|
[features]
|
||||||
default = ["sync"]
|
default = ["sync"]
|
||||||
sync = ["wasi-cap-std-sync"]
|
sync = ["wasi-cap-std-sync"]
|
||||||
|
tokio = ["wasi-tokio", "wasmtime/async", "wasmtime-wiggle/async"]
|
||||||
|
|||||||
@@ -7,10 +7,7 @@
|
|||||||
//! Individual snapshots are available through
|
//! Individual snapshots are available through
|
||||||
//! `wasmtime_wasi::snapshots::preview_{0, 1}::Wasi::new(&Store, Rc<RefCell<WasiCtx>>)`.
|
//! `wasmtime_wasi::snapshots::preview_{0, 1}::Wasi::new(&Store, Rc<RefCell<WasiCtx>>)`.
|
||||||
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::rc::Rc;
|
|
||||||
pub use wasi_common::{Error, WasiCtx, WasiCtxBuilder, WasiDir, WasiFile};
|
pub use wasi_common::{Error, WasiCtx, WasiCtxBuilder, WasiDir, WasiFile};
|
||||||
use wasmtime::{Config, Linker, Store};
|
|
||||||
|
|
||||||
/// Re-export the commonly used wasi-cap-std-sync crate here. This saves
|
/// Re-export the commonly used wasi-cap-std-sync crate here. This saves
|
||||||
/// consumers of this library from having to keep additional dependencies
|
/// consumers of this library from having to keep additional dependencies
|
||||||
@@ -18,8 +15,36 @@ use wasmtime::{Config, Linker, Store};
|
|||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
pub mod sync {
|
pub mod sync {
|
||||||
pub use wasi_cap_std_sync::*;
|
pub use wasi_cap_std_sync::*;
|
||||||
|
super::define_wasi!(block_on);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sync mode is the "default" of this crate, so we also export it at the top
|
||||||
|
/// level.
|
||||||
|
#[cfg(feature = "sync")]
|
||||||
|
pub use sync::*;
|
||||||
|
|
||||||
|
/// Re-export the wasi-tokio crate here. This saves consumers of this library from having
|
||||||
|
/// to keep additional dependencies in sync.
|
||||||
|
#[cfg(feature = "tokio")]
|
||||||
|
pub mod tokio {
|
||||||
|
pub use wasi_tokio::*;
|
||||||
|
super::define_wasi!(async);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The only difference between these definitions for sync vs async is whether
|
||||||
|
// the wasmtime::Funcs generated are async (& therefore need an async Store and an executor to run)
|
||||||
|
// or whether they have an internal "dummy executor" that expects the implementation of all
|
||||||
|
// the async funcs to poll to Ready immediately.
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! define_wasi {
|
||||||
|
($async_mode: tt) => {
|
||||||
|
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use wasmtime::{Config, Linker, Store};
|
||||||
|
use wasi_common::WasiCtx;
|
||||||
|
|
||||||
/// An instantiated instance of all available wasi exports. Presently includes
|
/// An instantiated instance of all available wasi exports. Presently includes
|
||||||
/// both the "preview1" snapshot and the "unstable" (preview0) snapshot.
|
/// both the "preview1" snapshot and the "unstable" (preview0) snapshot.
|
||||||
pub struct Wasi {
|
pub struct Wasi {
|
||||||
@@ -79,6 +104,7 @@ necessary. Additionally [`Wasi::get_export`] can be used to do name-based
|
|||||||
resolution.",
|
resolution.",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
$async_mode: *
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
pub mod preview_0 {
|
pub mod preview_0 {
|
||||||
@@ -106,6 +132,9 @@ necessary. Additionally [`Wasi::get_export`] can be used to do name-based
|
|||||||
resolution.",
|
resolution.",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
$async_mode: *
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -47,7 +47,9 @@ impl Parse for ConfigField {
|
|||||||
} else if lookahead.peek(Token![async]) {
|
} else if lookahead.peek(Token![async]) {
|
||||||
input.parse::<Token![async]>()?;
|
input.parse::<Token![async]>()?;
|
||||||
input.parse::<Token![:]>()?;
|
input.parse::<Token![:]>()?;
|
||||||
Ok(ConfigField::Async(input.parse()?))
|
Ok(ConfigField::Async(AsyncConf {
|
||||||
|
functions: input.parse()?,
|
||||||
|
}))
|
||||||
} else {
|
} else {
|
||||||
Err(lookahead.error())
|
Err(lookahead.error())
|
||||||
}
|
}
|
||||||
@@ -280,25 +282,43 @@ impl Parse for ErrorConfField {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Default, Debug)]
|
#[derive(Clone, Default, Debug)]
|
||||||
/// Modules and funcs that should be async
|
/// Modules and funcs that have async signatures
|
||||||
pub struct AsyncConf(HashMap<String, Vec<String>>);
|
pub struct AsyncConf {
|
||||||
|
functions: AsyncFunctions,
|
||||||
|
}
|
||||||
|
|
||||||
impl AsyncConf {
|
#[derive(Clone, Debug)]
|
||||||
pub fn is_async(&self, module: &str, function: &str) -> bool {
|
pub enum AsyncFunctions {
|
||||||
self.0
|
Some(HashMap<String, Vec<String>>),
|
||||||
.get(module)
|
All,
|
||||||
.and_then(|fs| fs.iter().find(|f| *f == function))
|
}
|
||||||
.is_some()
|
impl Default for AsyncFunctions {
|
||||||
|
fn default() -> Self {
|
||||||
|
AsyncFunctions::Some(HashMap::default())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parse for AsyncConf {
|
impl AsyncConf {
|
||||||
|
pub fn is_async(&self, module: &str, function: &str) -> bool {
|
||||||
|
match &self.functions {
|
||||||
|
AsyncFunctions::Some(fs) => fs
|
||||||
|
.get(module)
|
||||||
|
.and_then(|fs| fs.iter().find(|f| *f == function))
|
||||||
|
.is_some(),
|
||||||
|
AsyncFunctions::All => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for AsyncFunctions {
|
||||||
fn parse(input: ParseStream) -> Result<Self> {
|
fn parse(input: ParseStream) -> Result<Self> {
|
||||||
let content;
|
let content;
|
||||||
|
let lookahead = input.lookahead1();
|
||||||
|
if lookahead.peek(syn::token::Brace) {
|
||||||
let _ = braced!(content in input);
|
let _ = braced!(content in input);
|
||||||
let items: Punctuated<AsyncConfField, Token![,]> =
|
let items: Punctuated<AsyncConfField, Token![,]> =
|
||||||
content.parse_terminated(Parse::parse)?;
|
content.parse_terminated(Parse::parse)?;
|
||||||
let mut m: HashMap<String, Vec<String>> = HashMap::new();
|
let mut functions: HashMap<String, Vec<String>> = HashMap::new();
|
||||||
use std::collections::hash_map::Entry;
|
use std::collections::hash_map::Entry;
|
||||||
for i in items {
|
for i in items {
|
||||||
let function_names = i
|
let function_names = i
|
||||||
@@ -306,14 +326,20 @@ impl Parse for AsyncConf {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|i| i.to_string())
|
.map(|i| i.to_string())
|
||||||
.collect::<Vec<String>>();
|
.collect::<Vec<String>>();
|
||||||
match m.entry(i.module_name.to_string()) {
|
match functions.entry(i.module_name.to_string()) {
|
||||||
Entry::Occupied(o) => o.into_mut().extend(function_names),
|
Entry::Occupied(o) => o.into_mut().extend(function_names),
|
||||||
Entry::Vacant(v) => {
|
Entry::Vacant(v) => {
|
||||||
v.insert(function_names);
|
v.insert(function_names);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(AsyncConf(m))
|
Ok(AsyncFunctions::Some(functions))
|
||||||
|
} else if lookahead.peek(Token![*]) {
|
||||||
|
let _: Token![*] = input.parse().unwrap();
|
||||||
|
Ok(AsyncFunctions::All)
|
||||||
|
} else {
|
||||||
|
Err(lookahead.error())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -953,3 +953,40 @@ impl From<GuestError> for Trap {
|
|||||||
Trap::String(err.to_string())
|
Trap::String(err.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn run_in_dummy_executor<F: std::future::Future>(future: F) -> F::Output {
|
||||||
|
use std::pin::Pin;
|
||||||
|
use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
|
||||||
|
|
||||||
|
let mut f = Pin::from(Box::new(future));
|
||||||
|
let waker = dummy_waker();
|
||||||
|
let mut cx = Context::from_waker(&waker);
|
||||||
|
match f.as_mut().poll(&mut cx) {
|
||||||
|
Poll::Ready(val) => return val,
|
||||||
|
Poll::Pending => {
|
||||||
|
panic!("Cannot wait on pending future: must enable wiggle \"async\" future and execute on an async Store")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dummy_waker() -> Waker {
|
||||||
|
return unsafe { Waker::from_raw(clone(5 as *const _)) };
|
||||||
|
|
||||||
|
unsafe fn clone(ptr: *const ()) -> RawWaker {
|
||||||
|
assert_eq!(ptr as usize, 5);
|
||||||
|
const VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake_by_ref, drop);
|
||||||
|
RawWaker::new(ptr, &VTABLE)
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn wake(ptr: *const ()) {
|
||||||
|
assert_eq!(ptr as usize, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn wake_by_ref(ptr: *const ()) {
|
||||||
|
assert_eq!(ptr as usize, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn drop(ptr: *const ()) {
|
||||||
|
assert_eq!(ptr as usize, 5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,9 +7,7 @@ use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx};
|
|||||||
|
|
||||||
wiggle::from_witx!({
|
wiggle::from_witx!({
|
||||||
witx: ["$CARGO_MANIFEST_DIR/tests/atoms.witx"],
|
witx: ["$CARGO_MANIFEST_DIR/tests/atoms.witx"],
|
||||||
async: {
|
async: *,
|
||||||
atoms::{int_float_args, double_int_return_float}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
impl_errno!(types::Errno);
|
impl_errno!(types::Errno);
|
||||||
|
|||||||
@@ -26,6 +26,11 @@ name = "atoms_async"
|
|||||||
path = "tests/atoms_async.rs"
|
path = "tests/atoms_async.rs"
|
||||||
required-features = ["async", "wasmtime/wat"]
|
required-features = ["async", "wasmtime/wat"]
|
||||||
|
|
||||||
|
[[test]]
|
||||||
|
name = "atoms_sync"
|
||||||
|
path = "tests/atoms_sync.rs"
|
||||||
|
required-features = ["wasmtime/wat"]
|
||||||
|
|
||||||
[badges]
|
[badges]
|
||||||
maintenance = { status = "actively-developed" }
|
maintenance = { status = "actively-developed" }
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
pub use wiggle_generate::config::AsyncConf;
|
use wiggle_generate::config::AsyncFunctions;
|
||||||
use {
|
use {
|
||||||
proc_macro2::Span,
|
proc_macro2::Span,
|
||||||
std::collections::HashMap,
|
std::collections::HashMap,
|
||||||
@@ -16,7 +16,6 @@ pub struct Config {
|
|||||||
pub witx: WitxConf,
|
pub witx: WitxConf,
|
||||||
pub ctx: CtxConf,
|
pub ctx: CtxConf,
|
||||||
pub modules: ModulesConf,
|
pub modules: ModulesConf,
|
||||||
#[cfg(feature = "async")]
|
|
||||||
pub async_: AsyncConf,
|
pub async_: AsyncConf,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,7 +25,6 @@ pub enum ConfigField {
|
|||||||
Witx(WitxConf),
|
Witx(WitxConf),
|
||||||
Ctx(CtxConf),
|
Ctx(CtxConf),
|
||||||
Modules(ModulesConf),
|
Modules(ModulesConf),
|
||||||
#[cfg(feature = "async")]
|
|
||||||
Async(AsyncConf),
|
Async(AsyncConf),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,6 +37,7 @@ mod kw {
|
|||||||
syn::custom_keyword!(name);
|
syn::custom_keyword!(name);
|
||||||
syn::custom_keyword!(docs);
|
syn::custom_keyword!(docs);
|
||||||
syn::custom_keyword!(function_override);
|
syn::custom_keyword!(function_override);
|
||||||
|
syn::custom_keyword!(block_on);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parse for ConfigField {
|
impl Parse for ConfigField {
|
||||||
@@ -67,17 +66,17 @@ impl Parse for ConfigField {
|
|||||||
} else if lookahead.peek(Token![async]) {
|
} else if lookahead.peek(Token![async]) {
|
||||||
input.parse::<Token![async]>()?;
|
input.parse::<Token![async]>()?;
|
||||||
input.parse::<Token![:]>()?;
|
input.parse::<Token![:]>()?;
|
||||||
#[cfg(feature = "async")]
|
Ok(ConfigField::Async(AsyncConf {
|
||||||
{
|
blocking: false,
|
||||||
Ok(ConfigField::Async(input.parse()?))
|
functions: input.parse()?,
|
||||||
}
|
}))
|
||||||
#[cfg(not(feature = "async"))]
|
} else if lookahead.peek(kw::block_on) {
|
||||||
{
|
input.parse::<kw::block_on>()?;
|
||||||
Err(syn::Error::new(
|
input.parse::<Token![:]>()?;
|
||||||
input.span(),
|
Ok(ConfigField::Async(AsyncConf {
|
||||||
"async not supported, enable cargo feature \"async\"",
|
blocking: true,
|
||||||
))
|
functions: input.parse()?,
|
||||||
}
|
}))
|
||||||
} else {
|
} else {
|
||||||
Err(lookahead.error())
|
Err(lookahead.error())
|
||||||
}
|
}
|
||||||
@@ -90,7 +89,6 @@ impl Config {
|
|||||||
let mut witx = None;
|
let mut witx = None;
|
||||||
let mut ctx = None;
|
let mut ctx = None;
|
||||||
let mut modules = None;
|
let mut modules = None;
|
||||||
#[cfg(feature = "async")]
|
|
||||||
let mut async_ = None;
|
let mut async_ = None;
|
||||||
for f in fields {
|
for f in fields {
|
||||||
match f {
|
match f {
|
||||||
@@ -118,7 +116,6 @@ impl Config {
|
|||||||
}
|
}
|
||||||
modules = Some(c);
|
modules = Some(c);
|
||||||
}
|
}
|
||||||
#[cfg(feature = "async")]
|
|
||||||
ConfigField::Async(c) => {
|
ConfigField::Async(c) => {
|
||||||
if async_.is_some() {
|
if async_.is_some() {
|
||||||
return Err(Error::new(err_loc, "duplicate `async` field"));
|
return Err(Error::new(err_loc, "duplicate `async` field"));
|
||||||
@@ -132,7 +129,6 @@ impl Config {
|
|||||||
witx: witx.ok_or_else(|| Error::new(err_loc, "`witx` field required"))?,
|
witx: witx.ok_or_else(|| Error::new(err_loc, "`witx` field required"))?,
|
||||||
ctx: ctx.ok_or_else(|| Error::new(err_loc, "`ctx` field required"))?,
|
ctx: ctx.ok_or_else(|| Error::new(err_loc, "`ctx` field required"))?,
|
||||||
modules: modules.ok_or_else(|| Error::new(err_loc, "`modules` field required"))?,
|
modules: modules.ok_or_else(|| Error::new(err_loc, "`modules` field required"))?,
|
||||||
#[cfg(feature = "async")]
|
|
||||||
async_: async_.unwrap_or_default(),
|
async_: async_.unwrap_or_default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -276,3 +272,53 @@ impl Parse for ModulesConf {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug)]
|
||||||
|
/// Modules and funcs that have async signatures
|
||||||
|
pub struct AsyncConf {
|
||||||
|
blocking: bool,
|
||||||
|
functions: AsyncFunctions,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub enum Asyncness {
|
||||||
|
/// Wiggle function is synchronous, wasmtime Func is synchronous
|
||||||
|
Sync,
|
||||||
|
/// Wiggle function is asynchronous, but wasmtime Func is synchronous
|
||||||
|
Blocking,
|
||||||
|
/// Wiggle function and wasmtime Func are asynchronous.
|
||||||
|
Async,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Asyncness {
|
||||||
|
pub fn is_sync(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Asyncness::Sync => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsyncConf {
|
||||||
|
pub fn is_async(&self, module: &str, function: &str) -> Asyncness {
|
||||||
|
let a = if self.blocking {
|
||||||
|
Asyncness::Blocking
|
||||||
|
} else {
|
||||||
|
Asyncness::Async
|
||||||
|
};
|
||||||
|
match &self.functions {
|
||||||
|
AsyncFunctions::Some(fs) => {
|
||||||
|
if fs
|
||||||
|
.get(module)
|
||||||
|
.and_then(|fs| fs.iter().find(|f| *f == function))
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
a
|
||||||
|
} else {
|
||||||
|
Asyncness::Sync
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AsyncFunctions::All => a,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use wiggle_generate::Names;
|
|||||||
|
|
||||||
mod config;
|
mod config;
|
||||||
|
|
||||||
use config::{AsyncConf, ModuleConf, TargetConf};
|
use config::{AsyncConf, Asyncness, ModuleConf, TargetConf};
|
||||||
|
|
||||||
/// Define the structs required to integrate a Wiggle implementation with Wasmtime.
|
/// Define the structs required to integrate a Wiggle implementation with Wasmtime.
|
||||||
///
|
///
|
||||||
@@ -48,11 +48,6 @@ pub fn wasmtime_integration(args: TokenStream) -> TokenStream {
|
|||||||
let doc = config.load_document();
|
let doc = config.load_document();
|
||||||
let names = Names::new(quote!(wasmtime_wiggle));
|
let names = Names::new(quote!(wasmtime_wiggle));
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
|
||||||
let async_config = config.async_.clone();
|
|
||||||
#[cfg(not(feature = "async"))]
|
|
||||||
let async_config = AsyncConf::default();
|
|
||||||
|
|
||||||
let modules = config.modules.iter().map(|(name, module_conf)| {
|
let modules = config.modules.iter().map(|(name, module_conf)| {
|
||||||
let module = doc
|
let module = doc
|
||||||
.module(&witx::Id::new(name))
|
.module(&witx::Id::new(name))
|
||||||
@@ -63,7 +58,7 @@ pub fn wasmtime_integration(args: TokenStream) -> TokenStream {
|
|||||||
&names,
|
&names,
|
||||||
&config.target,
|
&config.target,
|
||||||
&config.ctx.name,
|
&config.ctx.name,
|
||||||
&async_config,
|
&config.async_,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
quote!( #(#modules)* ).into()
|
quote!( #(#modules)* ).into()
|
||||||
@@ -107,13 +102,24 @@ fn generate_module(
|
|||||||
let mut host_funcs = Vec::new();
|
let mut host_funcs = Vec::new();
|
||||||
|
|
||||||
for f in module.funcs() {
|
for f in module.funcs() {
|
||||||
|
let asyncness = async_conf.is_async(module.name.as_str(), f.name.as_str());
|
||||||
|
match asyncness {
|
||||||
|
Asyncness::Blocking => {}
|
||||||
|
Asyncness::Async => {
|
||||||
|
assert!(
|
||||||
|
cfg!(feature = "async"),
|
||||||
|
"generating async wasmtime Funcs requires cargo feature \"async\""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
generate_func(
|
generate_func(
|
||||||
&module_id,
|
&module_id,
|
||||||
&f,
|
&f,
|
||||||
names,
|
names,
|
||||||
&target_module,
|
&target_module,
|
||||||
ctx_type,
|
ctx_type,
|
||||||
async_conf.is_async(module.name.as_str(), f.name.as_str()),
|
asyncness,
|
||||||
&mut fns,
|
&mut fns,
|
||||||
&mut ctor_externs,
|
&mut ctor_externs,
|
||||||
&mut host_funcs,
|
&mut host_funcs,
|
||||||
@@ -229,11 +235,12 @@ fn generate_func(
|
|||||||
names: &Names,
|
names: &Names,
|
||||||
target_module: &TokenStream2,
|
target_module: &TokenStream2,
|
||||||
ctx_type: &syn::Type,
|
ctx_type: &syn::Type,
|
||||||
is_async: bool,
|
asyncness: Asyncness,
|
||||||
fns: &mut Vec<TokenStream2>,
|
fns: &mut Vec<TokenStream2>,
|
||||||
ctors: &mut Vec<TokenStream2>,
|
ctors: &mut Vec<TokenStream2>,
|
||||||
host_funcs: &mut Vec<(witx::Id, TokenStream2)>,
|
host_funcs: &mut Vec<(witx::Id, TokenStream2)>,
|
||||||
) {
|
) {
|
||||||
|
let rt = names.runtime_mod();
|
||||||
let name_ident = names.func(&func.name);
|
let name_ident = names.func(&func.name);
|
||||||
|
|
||||||
let (params, results) = func.wasm_signature();
|
let (params, results) = func.wasm_signature();
|
||||||
@@ -257,8 +264,16 @@ fn generate_func(
|
|||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let async_ = if is_async { quote!(async) } else { quote!() };
|
let async_ = if asyncness.is_sync() {
|
||||||
let await_ = if is_async { quote!(.await) } else { quote!() };
|
quote!()
|
||||||
|
} else {
|
||||||
|
quote!(async)
|
||||||
|
};
|
||||||
|
let await_ = if asyncness.is_sync() {
|
||||||
|
quote!()
|
||||||
|
} else {
|
||||||
|
quote!(.await)
|
||||||
|
};
|
||||||
|
|
||||||
let runtime = names.runtime_mod();
|
let runtime = names.runtime_mod();
|
||||||
let fn_ident = format_ident!("{}_{}", module_ident, name_ident);
|
let fn_ident = format_ident!("{}_{}", module_ident, name_ident);
|
||||||
@@ -282,7 +297,8 @@ fn generate_func(
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if is_async {
|
match asyncness {
|
||||||
|
Asyncness::Async => {
|
||||||
let wrapper = format_ident!("wrap{}_async", params.len());
|
let wrapper = format_ident!("wrap{}_async", params.len());
|
||||||
ctors.push(quote! {
|
ctors.push(quote! {
|
||||||
let #name_ident = wasmtime::Func::#wrapper(
|
let #name_ident = wasmtime::Func::#wrapper(
|
||||||
@@ -294,7 +310,22 @@ fn generate_func(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
} else {
|
}
|
||||||
|
Asyncness::Blocking => {
|
||||||
|
// Emit a synchronous function. Self::#fn_ident returns a Future, so we need to
|
||||||
|
// use a dummy executor to let any synchronous code inside there execute correctly. If
|
||||||
|
// the future ends up Pending, this func will Trap.
|
||||||
|
ctors.push(quote! {
|
||||||
|
let my_ctx = ctx.clone();
|
||||||
|
let #name_ident = wasmtime::Func::wrap(
|
||||||
|
store,
|
||||||
|
move |caller: wasmtime::Caller #(, #arg_decls)*| -> Result<#ret_ty, wasmtime::Trap> {
|
||||||
|
#rt::run_in_dummy_executor(Self::#fn_ident(&caller, &mut my_ctx.borrow_mut() #(, #arg_names)*))
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Asyncness::Sync => {
|
||||||
ctors.push(quote! {
|
ctors.push(quote! {
|
||||||
let my_ctx = ctx.clone();
|
let my_ctx = ctx.clone();
|
||||||
let #name_ident = wasmtime::Func::wrap(
|
let #name_ident = wasmtime::Func::wrap(
|
||||||
@@ -305,8 +336,10 @@ fn generate_func(
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let host_wrapper = if is_async {
|
let host_wrapper = match asyncness {
|
||||||
|
Asyncness::Async => {
|
||||||
let wrapper = format_ident!("wrap{}_host_func_async", params.len());
|
let wrapper = format_ident!("wrap{}_host_func_async", params.len());
|
||||||
quote! {
|
quote! {
|
||||||
config.#wrapper(
|
config.#wrapper(
|
||||||
@@ -324,7 +357,12 @@ fn generate_func(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
|
||||||
|
Asyncness::Blocking => {
|
||||||
|
// Emit a synchronous host function. Self::#fn_ident returns a Future, so we need to
|
||||||
|
// use a dummy executor to let any synchronous code inside there execute correctly. If
|
||||||
|
// the future ends up Pending, this func will Trap.
|
||||||
quote! {
|
quote! {
|
||||||
config.wrap_host_func(
|
config.wrap_host_func(
|
||||||
module,
|
module,
|
||||||
@@ -334,11 +372,26 @@ fn generate_func(
|
|||||||
.store()
|
.store()
|
||||||
.get::<std::rc::Rc<std::cell::RefCell<#ctx_type>>>()
|
.get::<std::rc::Rc<std::cell::RefCell<#ctx_type>>>()
|
||||||
.ok_or_else(|| wasmtime::Trap::new("context is missing in the store"))?;
|
.ok_or_else(|| wasmtime::Trap::new("context is missing in the store"))?;
|
||||||
let result = Self::#fn_ident(&caller, &mut ctx.borrow_mut() #(, #arg_names)*);
|
#rt::run_in_dummy_executor(Self::#fn_ident(&caller, &mut ctx.borrow_mut() #(, #arg_names)*))
|
||||||
result
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
Asyncness::Sync => {
|
||||||
|
quote! {
|
||||||
|
config.wrap_host_func(
|
||||||
|
module,
|
||||||
|
field,
|
||||||
|
move |caller: wasmtime::Caller #(, #arg_decls)*| -> Result<#ret_ty, wasmtime::Trap> {
|
||||||
|
let ctx = caller
|
||||||
|
.store()
|
||||||
|
.get::<std::rc::Rc<std::cell::RefCell<#ctx_type>>>()
|
||||||
|
.ok_or_else(|| wasmtime::Trap::new("context is missing in the store"))?;
|
||||||
|
Self::#fn_ident(&caller, &mut ctx.borrow_mut() #(, #arg_names)*)
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
host_funcs.push((func.name.clone(), host_wrapper));
|
host_funcs.push((func.name.clone(), host_wrapper));
|
||||||
}
|
}
|
||||||
|
|||||||
176
crates/wiggle/wasmtime/tests/atoms_sync.rs
Normal file
176
crates/wiggle/wasmtime/tests/atoms_sync.rs
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
wasmtime_wiggle::from_witx!({
|
||||||
|
witx: ["$CARGO_MANIFEST_DIR/tests/atoms.witx"],
|
||||||
|
async: {
|
||||||
|
atoms::{double_int_return_float}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
wasmtime_wiggle::wasmtime_integration!({
|
||||||
|
target: crate,
|
||||||
|
witx: ["$CARGO_MANIFEST_DIR/tests/atoms.witx"],
|
||||||
|
ctx: Ctx,
|
||||||
|
modules: { atoms => { name: Atoms } },
|
||||||
|
block_on: {
|
||||||
|
atoms::double_int_return_float
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
pub struct Ctx;
|
||||||
|
impl wiggle::GuestErrorType for types::Errno {
|
||||||
|
fn success() -> Self {
|
||||||
|
types::Errno::Ok
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasmtime_wiggle::async_trait]
|
||||||
|
impl atoms::Atoms for Ctx {
|
||||||
|
fn int_float_args(&self, an_int: u32, an_float: f32) -> Result<(), types::Errno> {
|
||||||
|
println!("INT FLOAT ARGS: {} {}", an_int, an_float);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
async fn double_int_return_float(
|
||||||
|
&self,
|
||||||
|
an_int: u32,
|
||||||
|
) -> Result<types::AliasToFloat, types::Errno> {
|
||||||
|
Ok((an_int as f32) * 2.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_int_float_args(linker: &wasmtime::Linker) {
|
||||||
|
let shim_mod = shim_module(linker.store());
|
||||||
|
let shim_inst = linker.instantiate(&shim_mod).unwrap();
|
||||||
|
|
||||||
|
let results = shim_inst
|
||||||
|
.get_func("int_float_args_shim")
|
||||||
|
.unwrap()
|
||||||
|
.call(&[0i32.into(), 123.45f32.into()])
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(results.len(), 1, "one return value");
|
||||||
|
assert_eq!(
|
||||||
|
results[0].unwrap_i32(),
|
||||||
|
types::Errno::Ok as i32,
|
||||||
|
"int_float_args errno"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_double_int_return_float(linker: &wasmtime::Linker) {
|
||||||
|
let shim_mod = shim_module(linker.store());
|
||||||
|
let shim_inst = linker.instantiate(&shim_mod).unwrap();
|
||||||
|
|
||||||
|
let input: i32 = 123;
|
||||||
|
let result_location: i32 = 0;
|
||||||
|
|
||||||
|
let results = shim_inst
|
||||||
|
.get_func("double_int_return_float_shim")
|
||||||
|
.unwrap()
|
||||||
|
.call(&[input.into(), result_location.into()])
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(results.len(), 1, "one return value");
|
||||||
|
assert_eq!(
|
||||||
|
results[0].unwrap_i32(),
|
||||||
|
types::Errno::Ok as i32,
|
||||||
|
"double_int_return_float errno"
|
||||||
|
);
|
||||||
|
|
||||||
|
// The actual result is in memory:
|
||||||
|
let mem = shim_inst.get_memory("memory").unwrap();
|
||||||
|
let mut result_bytes: [u8; 4] = [0, 0, 0, 0];
|
||||||
|
mem.read(result_location as usize, &mut result_bytes)
|
||||||
|
.unwrap();
|
||||||
|
let result = f32::from_le_bytes(result_bytes);
|
||||||
|
assert_eq!((input * 2) as f32, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sync_host_func() {
|
||||||
|
let store = store();
|
||||||
|
|
||||||
|
let ctx = Rc::new(RefCell::new(Ctx));
|
||||||
|
let atoms = Atoms::new(&store, ctx.clone());
|
||||||
|
|
||||||
|
let mut linker = wasmtime::Linker::new(&store);
|
||||||
|
atoms.add_to_linker(&mut linker).unwrap();
|
||||||
|
|
||||||
|
run_int_float_args(&linker);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_async_host_func() {
|
||||||
|
let store = store();
|
||||||
|
|
||||||
|
let ctx = Rc::new(RefCell::new(Ctx));
|
||||||
|
let atoms = Atoms::new(&store, ctx.clone());
|
||||||
|
|
||||||
|
let mut linker = wasmtime::Linker::new(&store);
|
||||||
|
atoms.add_to_linker(&mut linker).unwrap();
|
||||||
|
|
||||||
|
run_double_int_return_float(&linker);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sync_config_host_func() {
|
||||||
|
let mut config = wasmtime::Config::new();
|
||||||
|
Atoms::add_to_config(&mut config);
|
||||||
|
|
||||||
|
let engine = wasmtime::Engine::new(&config).unwrap();
|
||||||
|
let store = wasmtime::Store::new(&engine);
|
||||||
|
|
||||||
|
assert!(Atoms::set_context(&store, Ctx).is_ok());
|
||||||
|
|
||||||
|
let linker = wasmtime::Linker::new(&store);
|
||||||
|
run_int_float_args(&linker);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_async_config_host_func() {
|
||||||
|
let mut config = wasmtime::Config::new();
|
||||||
|
Atoms::add_to_config(&mut config);
|
||||||
|
|
||||||
|
let engine = wasmtime::Engine::new(&config).unwrap();
|
||||||
|
let store = wasmtime::Store::new(&engine);
|
||||||
|
|
||||||
|
assert!(Atoms::set_context(&store, Ctx).is_ok());
|
||||||
|
|
||||||
|
let linker = wasmtime::Linker::new(&store);
|
||||||
|
run_double_int_return_float(&linker);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn store() -> wasmtime::Store {
|
||||||
|
wasmtime::Store::new(&wasmtime::Engine::new(&wasmtime::Config::new()).unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wiggle expects the caller to have an exported memory. Wasmtime can only
|
||||||
|
// provide this if the caller is a WebAssembly module, so we need to write
|
||||||
|
// a shim module:
|
||||||
|
fn shim_module(store: &wasmtime::Store) -> wasmtime::Module {
|
||||||
|
wasmtime::Module::new(
|
||||||
|
store.engine(),
|
||||||
|
r#"
|
||||||
|
(module
|
||||||
|
(memory 1)
|
||||||
|
(export "memory" (memory 0))
|
||||||
|
(import "atoms" "int_float_args" (func $int_float_args (param i32 f32) (result i32)))
|
||||||
|
(import "atoms" "double_int_return_float" (func $double_int_return_float (param i32 i32) (result i32)))
|
||||||
|
|
||||||
|
(func $int_float_args_shim (param i32 f32) (result i32)
|
||||||
|
local.get 0
|
||||||
|
local.get 1
|
||||||
|
call $int_float_args
|
||||||
|
)
|
||||||
|
(func $double_int_return_float_shim (param i32 i32) (result i32)
|
||||||
|
local.get 0
|
||||||
|
local.get 1
|
||||||
|
call $double_int_return_float
|
||||||
|
)
|
||||||
|
(export "int_float_args_shim" (func $int_float_args_shim))
|
||||||
|
(export "double_int_return_float_shim" (func $double_int_return_float_shim))
|
||||||
|
)
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use wasmtime::*;
|
use wasmtime::*;
|
||||||
use wasmtime_wasi::{sync::WasiCtxBuilder, Wasi};
|
use wasmtime_wasi::sync::{Wasi, WasiCtxBuilder};
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
let engine = Engine::default();
|
let engine = Engine::default();
|
||||||
|
|||||||
5
examples/tokio/main.c
Normal file
5
examples/tokio/main.c
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
// This example is specific to integrating with Rust's tokio ecosystem, so
|
||||||
|
// it isnt applicable to C/C++.
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
163
examples/tokio/main.rs
Normal file
163
examples/tokio/main.rs
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
use anyhow::{anyhow, Error};
|
||||||
|
use std::future::Future;
|
||||||
|
use tokio::time::Duration;
|
||||||
|
use wasmtime::{Config, Engine, Linker, Module, Store};
|
||||||
|
// For this example we want to use the async version of wasmtime_wasi.
|
||||||
|
// Notably, this version of wasi uses a scheduler that will async yield
|
||||||
|
// when sleeping in `poll_oneoff`.
|
||||||
|
use wasmtime_wasi::tokio::{Wasi, WasiCtxBuilder};
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<(), Error> {
|
||||||
|
// Create an environment shared by all wasm execution. This contains
|
||||||
|
// the `Engine` and the `Module` we are executing.
|
||||||
|
let env = Environment::new()?;
|
||||||
|
|
||||||
|
// The inputs to run_wasm are `Send`: we can create them here and send
|
||||||
|
// them to a new task that we spawn.
|
||||||
|
let inputs1 = Inputs::new(env.clone(), "Gussie");
|
||||||
|
let inputs2 = Inputs::new(env.clone(), "Willa");
|
||||||
|
let inputs3 = Inputs::new(env, "Sparky");
|
||||||
|
|
||||||
|
// Spawn some tasks. Insert sleeps before run_wasm so that the
|
||||||
|
// interleaving is easy to observe.
|
||||||
|
let join1 = tokio::task::spawn(async move { run_wasm(inputs1).await });
|
||||||
|
let join2 = tokio::task::spawn(async move {
|
||||||
|
tokio::time::sleep(Duration::from_millis(750)).await;
|
||||||
|
run_wasm(inputs2).await
|
||||||
|
});
|
||||||
|
let join3 = tokio::task::spawn(async move {
|
||||||
|
tokio::time::sleep(Duration::from_millis(1250)).await;
|
||||||
|
run_wasm(inputs3).await
|
||||||
|
});
|
||||||
|
|
||||||
|
// All tasks should join successfully.
|
||||||
|
join1.await??;
|
||||||
|
join2.await??;
|
||||||
|
join3.await??;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct Environment {
|
||||||
|
engine: Engine,
|
||||||
|
module: Module,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Environment {
|
||||||
|
pub fn new() -> Result<Self, Error> {
|
||||||
|
let mut config = Config::new();
|
||||||
|
// We need this engine's `Store`s to be async, and consume fuel, so
|
||||||
|
// that they can co-operatively yield during execution.
|
||||||
|
config.async_support(true);
|
||||||
|
config.consume_fuel(true);
|
||||||
|
|
||||||
|
// Install the host functions for `Wasi`.
|
||||||
|
Wasi::add_to_config(&mut config);
|
||||||
|
|
||||||
|
let engine = Engine::new(&config)?;
|
||||||
|
let module = Module::from_file(&engine, "target/wasm32-wasi/debug/tokio-wasi.wasm")?;
|
||||||
|
|
||||||
|
Ok(Self { engine, module })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Inputs {
|
||||||
|
env: Environment,
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Inputs {
|
||||||
|
fn new(env: Environment, name: &str) -> Self {
|
||||||
|
Self {
|
||||||
|
env,
|
||||||
|
name: name.to_owned(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_wasm(inputs: Inputs) -> impl Future<Output = Result<(), Error>> {
|
||||||
|
use std::pin::Pin;
|
||||||
|
use std::task::{Context, Poll};
|
||||||
|
// IMPORTANT: The current wasmtime API is very challenging to use safely
|
||||||
|
// on an async runtime. This RFC describes a redesign of the API that will
|
||||||
|
// resolve these safety issues:
|
||||||
|
// https://github.com/alexcrichton/rfcs-2/blob/new-api/accepted/new-api.md
|
||||||
|
|
||||||
|
// This is a "marker type future" which simply wraps some other future and
|
||||||
|
// the only purpose it serves is to forward the implementation of `Future`
|
||||||
|
// as well as have `unsafe impl Send` for itself, regardless of the
|
||||||
|
// underlying type.
|
||||||
|
//
|
||||||
|
// Note that the qctual safety of this relies on the fact that the inputs
|
||||||
|
// here are `Send`, the outputs (just () in this case) are `Send`, and the
|
||||||
|
// future itself is safe tu resume on other threads.
|
||||||
|
//
|
||||||
|
// For an in-depth discussion of the safety of moving Wasmtime's `Store`
|
||||||
|
// between threads, see
|
||||||
|
// https://docs.wasmtime.dev/examples-rust-multithreading.html.
|
||||||
|
struct UnsafeSend<T>(T);
|
||||||
|
|
||||||
|
// Note the `where` cause specifically ensures the output of the future to
|
||||||
|
// be `Send` is required. We specifically dont require `T` to be `Send`
|
||||||
|
// since that's the whole point of this function, but we require that
|
||||||
|
// everything used to construct `T` is `Send` below.
|
||||||
|
unsafe impl<T> Send for UnsafeSend<T>
|
||||||
|
where
|
||||||
|
T: Future,
|
||||||
|
T::Output: Send,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
impl<T: Future> Future for UnsafeSend<T> {
|
||||||
|
type Output = T::Output;
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T::Output> {
|
||||||
|
// Note that this `unsafe` is unrelated to `Send`, it only has to do with "pin
|
||||||
|
// projection" and should be safe since it's all we do with the `Pin`.
|
||||||
|
unsafe { self.map_unchecked_mut(|p| &mut p.0).poll(cx) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a crucial assertion that needs to be here. The compiler
|
||||||
|
// typically checks this for us, but do to our `UnsafeSend` type the
|
||||||
|
// compiler isn't automatically checking this. The assertion here must
|
||||||
|
// assert that all arguments to this function are indeed `Send` because
|
||||||
|
// we're closing over them and sending them to other threads. It's only
|
||||||
|
// everything *internal* to the computation of this function which doesn't
|
||||||
|
// have to be `Send`.
|
||||||
|
fn assert_send<T: Send>(_t: &T) {}
|
||||||
|
assert_send(&inputs);
|
||||||
|
|
||||||
|
// Wrap up the `_run_wasm` function, which is *not* `Send`, but is safe to
|
||||||
|
// resume on other threads.
|
||||||
|
UnsafeSend(_run_wasm(inputs))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn _run_wasm(inputs: Inputs) -> Result<(), Error> {
|
||||||
|
let store = Store::new(&inputs.env.engine);
|
||||||
|
|
||||||
|
// WebAssembly execution will be paused for an async yield every time it
|
||||||
|
// consumes 10000 fuel. Fuel will be refilled u32::MAX times.
|
||||||
|
store.out_of_fuel_async_yield(u32::MAX, 10000);
|
||||||
|
|
||||||
|
Wasi::set_context(
|
||||||
|
&store,
|
||||||
|
WasiCtxBuilder::new()
|
||||||
|
// Let wasi print to this process's stdout.
|
||||||
|
.inherit_stdout()
|
||||||
|
// Set an environment variable so the wasm knows its name.
|
||||||
|
.env("NAME", &inputs.name)?
|
||||||
|
.build()?,
|
||||||
|
)
|
||||||
|
.map_err(|_| anyhow!("setting wasi context"))?;
|
||||||
|
|
||||||
|
let linker = Linker::new(&store);
|
||||||
|
|
||||||
|
// Instantiate
|
||||||
|
let instance = linker.instantiate_async(&inputs.env.module).await?;
|
||||||
|
instance
|
||||||
|
.get_typed_func::<(), ()>("_start")?
|
||||||
|
.call_async(())
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
10
examples/tokio/wasm/Cargo.toml
Normal file
10
examples/tokio/wasm/Cargo.toml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
[package]
|
||||||
|
name = "example-tokio-wasm"
|
||||||
|
version = "0.0.0"
|
||||||
|
authors = ["The Wasmtime Project Developers"]
|
||||||
|
edition = "2018"
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
path = "tokio-wasi.rs"
|
||||||
|
name = "tokio-wasi"
|
||||||
6
examples/tokio/wasm/tokio-wasi.rs
Normal file
6
examples/tokio/wasm/tokio-wasi.rs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
fn main() {
|
||||||
|
let name = std::env::var("NAME").unwrap();
|
||||||
|
println!("Hello, world! My name is {}", name);
|
||||||
|
std::thread::sleep(std::time::Duration::from_secs(1));
|
||||||
|
println!("Goodbye from {}", name);
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use wasmtime::*;
|
use wasmtime::*;
|
||||||
use wasmtime_wasi::{sync::WasiCtxBuilder, Wasi};
|
use wasmtime_wasi::sync::{Wasi, WasiCtxBuilder};
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
tracing_subscriber::FmtSubscriber::builder()
|
tracing_subscriber::FmtSubscriber::builder()
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ const CRATES_TO_PUBLISH: &[&str] = &[
|
|||||||
// wasi-common
|
// wasi-common
|
||||||
"wasi-common",
|
"wasi-common",
|
||||||
"wasi-cap-std-sync",
|
"wasi-cap-std-sync",
|
||||||
|
"wasi-tokio",
|
||||||
// wasmtime
|
// wasmtime
|
||||||
"lightbeam",
|
"lightbeam",
|
||||||
"wasmtime-fiber",
|
"wasmtime-fiber",
|
||||||
|
|||||||
@@ -11,10 +11,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
use structopt::{clap::AppSettings, StructOpt};
|
use structopt::{clap::AppSettings, StructOpt};
|
||||||
use wasmtime::{Engine, Func, Linker, Module, Store, Trap, Val, ValType};
|
use wasmtime::{Engine, Func, Linker, Module, Store, Trap, Val, ValType};
|
||||||
use wasmtime_wasi::{
|
use wasmtime_wasi::sync::{Dir, Wasi, WasiCtxBuilder};
|
||||||
sync::{Dir, WasiCtxBuilder},
|
|
||||||
Wasi,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(feature = "wasi-nn")]
|
#[cfg(feature = "wasi-nn")]
|
||||||
use wasmtime_wasi_nn::{WasiNn, WasiNnCtx};
|
use wasmtime_wasi_nn::{WasiNn, WasiNnCtx};
|
||||||
|
|||||||
Reference in New Issue
Block a user