Implement RFC 11: Redesigning Wasmtime's APIs (#2897)

Implement Wasmtime's new API as designed by RFC 11. This is quite a large commit which has had lots of discussion externally, so for more information it's best to read the RFC thread and the PR thread.
This commit is contained in:
Alex Crichton
2021-06-03 09:10:53 -05:00
committed by GitHub
parent a5a28b1c5b
commit 7a1b7cdf92
233 changed files with 13349 additions and 11997 deletions

View File

@@ -1,6 +1,10 @@
//! This program is an example of how Wasmtime can be used with multithreaded
//! runtimes and how various types and structures can be shared across threads.
// You can execute this example with `cargo run --example threads`
use anyhow::Result;
use std::sync::Arc;
use std::thread;
use std::time;
use wasmtime::*;
@@ -8,57 +12,59 @@ use wasmtime::*;
const N_THREADS: i32 = 10;
const N_REPS: i32 = 3;
fn run(engine: &Engine, module: Module, id: i32) -> Result<()> {
let store = Store::new(&engine);
// Create external print functions.
println!("Creating callback...");
let callback_func = Func::wrap(&store, |arg: i32| {
println!("> Thread {} is running", arg);
});
let id_type = GlobalType::new(ValType::I32, Mutability::Const);
let id_global = Global::new(&store, id_type, Val::I32(id))?;
// Instantiate.
println!("Instantiating module...");
let instance = Instance::new(&store, &module, &[callback_func.into(), id_global.into()])?;
// Extract exports.
println!("Extracting export...");
let g = instance.get_typed_func::<(), ()>("run")?;
for _ in 0..N_REPS {
thread::sleep(time::Duration::from_millis(100));
// Call `$run`.
drop(g.call(())?);
}
Ok(())
}
fn main() -> Result<()> {
println!("Initializing...");
// Initialize global per-process state. This state will be shared amonst all
// threads. Notably this includes the compiled module as well as a `Linker`,
// which contains all our host functions we want to define.
let engine = Engine::default();
// Compile.
println!("Compiling module...");
let module = Module::from_file(&engine, "examples/threads.wat")?;
let mut linker = Linker::new(&engine);
linker.func_wrap("global", "hello", || {
println!("> Hello from {:?}", thread::current().id());
})?;
let linker = Arc::new(linker); // "finalize" the linker
let mut children = Vec::new();
for id in 0..N_THREADS {
let engine = engine.clone();
let module = module.clone();
children.push(thread::spawn(move || {
run(&engine, module, id).expect("Success");
}));
}
// Share this global state amongst a set of threads, each of which will
// create stores and execute instances.
let children = (0..N_THREADS)
.map(|_| {
let engine = engine.clone();
let module = module.clone();
let linker = linker.clone();
thread::spawn(move || {
run(&engine, &module, &linker).expect("Success");
})
})
.collect::<Vec<_>>();
for (i, child) in children.into_iter().enumerate() {
if let Err(_) = child.join() {
println!("Thread #{} errors", i);
}
for child in children {
child.join().unwrap();
}
Ok(())
}
fn run(engine: &Engine, module: &Module, linker: &Linker<()>) -> Result<()> {
// Each sub-thread we have starting out by instantiating the `module`
// provided into a fresh `Store`.
println!("Instantiating module...");
let mut store = Store::new(&engine, ());
let instance = linker.instantiate(&mut store, module)?;
let run = instance.get_typed_func::<(), (), _>(&mut store, "run")?;
println!("Executing...");
for _ in 0..N_REPS {
run.call(&mut store, ())?;
thread::sleep(time::Duration::from_millis(100));
}
// Also note that that a `Store` can also move between threads:
println!("> Moving {:?} to a new thread", thread::current().id());
let child = thread::spawn(move || run.call(&mut store, ()));
child.join().unwrap()?;
Ok(())
}