This PR introduces a new way of performing cooperative timeslicing that is intended to replace the "fuel" mechanism. The tradeoff is that this mechanism interrupts with less precision: not at deterministic points where fuel runs out, but rather when the Engine enters a new epoch. The generated code instrumentation is substantially faster, however, because it does not need to do as much work as when tracking fuel; it only loads the global "epoch counter" and does a compare-and-branch at backedges and function prologues. This change has been measured as ~twice as fast as fuel-based timeslicing for some workloads, especially control-flow-intensive workloads such as the SpiderMonkey JS interpreter on Wasm/WASI. The intended interface is that the embedder of the `Engine` performs an `engine.increment_epoch()` call periodically, e.g. once per millisecond. An async invocation of a Wasm guest on a `Store` can specify a number of epoch-ticks that are allowed before an async yield back to the executor's event loop. (The initial amount and automatic "refills" are configured on the `Store`, just as for fuel.) This call does only signal-safe work (it increments an `AtomicU64`) so could be invoked from a periodic signal, or from a thread that wakes up once per period.
122 lines
3.9 KiB
Rust
122 lines
3.9 KiB
Rust
use anyhow::Context;
|
|
use std::collections::BTreeSet;
|
|
use std::process::Command;
|
|
|
|
fn main() -> anyhow::Result<()> {
|
|
let example_to_run = std::env::args().nth(1);
|
|
let mut examples = BTreeSet::new();
|
|
for e in std::fs::read_dir("examples")? {
|
|
let e = e?;
|
|
let path = e.path();
|
|
let dir = e.metadata()?.is_dir();
|
|
if let Some("wat") = path.extension().and_then(|s| s.to_str()) {
|
|
continue;
|
|
}
|
|
|
|
examples.insert((path.file_stem().unwrap().to_str().unwrap().to_owned(), dir));
|
|
}
|
|
|
|
println!("======== Building libwasmtime.a ===========");
|
|
run(Command::new("cargo")
|
|
.args(&["build"])
|
|
.current_dir("crates/c-api"))?;
|
|
|
|
for (example, is_dir) in examples {
|
|
if example == "README" {
|
|
continue;
|
|
}
|
|
if let Some(example_to_run) = &example_to_run {
|
|
if !example.contains(&example_to_run[..]) {
|
|
continue;
|
|
}
|
|
}
|
|
if is_dir {
|
|
println!("======== Rust wasm file `{}` ============", example);
|
|
let target = if example == "fib-debug" {
|
|
"wasm32-unknown-unknown"
|
|
} else {
|
|
"wasm32-wasi"
|
|
};
|
|
run(Command::new("cargo")
|
|
.arg("build")
|
|
.arg("-p")
|
|
.arg(format!("example-{}-wasm", example))
|
|
.arg("--target")
|
|
.arg(target))?;
|
|
}
|
|
println!("======== Rust example `{}` ============", example);
|
|
let mut cargo_cmd = Command::new("cargo");
|
|
cargo_cmd.arg("run").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);
|
|
for extension in ["c", "cc"].iter() {
|
|
let mut cmd = cc::Build::new()
|
|
.opt_level(0)
|
|
.cargo_metadata(false)
|
|
.target(env!("TARGET"))
|
|
.host(env!("TARGET"))
|
|
.include("crates/c-api/include")
|
|
.include("crates/c-api/wasm-c-api/include")
|
|
.define("WASM_API_EXTERN", Some("")) // static linkage, not dynamic
|
|
.warnings(false)
|
|
.get_compiler()
|
|
.to_command();
|
|
|
|
let file = if is_dir {
|
|
format!("examples/{}/main.{}", example, extension)
|
|
} else {
|
|
format!("examples/{}.{}", example, extension)
|
|
};
|
|
|
|
if !std::path::Path::new(&file).exists() {
|
|
// C and C++ files are optional so we can skip them.
|
|
continue;
|
|
}
|
|
|
|
cmd.arg(file);
|
|
let exe = if cfg!(windows) {
|
|
cmd.arg("target/debug/wasmtime.lib")
|
|
.arg("ws2_32.lib")
|
|
.arg("advapi32.lib")
|
|
.arg("userenv.lib")
|
|
.arg("ntdll.lib")
|
|
.arg("shell32.lib")
|
|
.arg("ole32.lib")
|
|
.arg("bcrypt.lib");
|
|
if is_dir {
|
|
"./main.exe".to_string()
|
|
} else {
|
|
format!("./{}.exe", example)
|
|
}
|
|
} else {
|
|
cmd.arg("target/debug/libwasmtime.a").arg("-o").arg("foo");
|
|
"./foo".to_string()
|
|
};
|
|
if cfg!(target_os = "linux") {
|
|
cmd.arg("-lpthread").arg("-ldl").arg("-lm");
|
|
}
|
|
run(&mut cmd)?;
|
|
|
|
run(&mut Command::new(exe))?;
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn run(cmd: &mut Command) -> anyhow::Result<()> {
|
|
(|| -> anyhow::Result<()> {
|
|
let s = cmd.status()?;
|
|
if !s.success() {
|
|
anyhow::bail!("Exited with failure status: {}", s);
|
|
}
|
|
Ok(())
|
|
})()
|
|
.with_context(|| format!("failed to run `{:?}`", cmd))
|
|
}
|