xref: /wasmtime-44.0.1/examples/tokio/main.rs (revision cc8d04f4)
1 use std::sync::Arc;
2 use tokio::time::Duration;
3 use wasmtime::Error;
4 use wasmtime::{Config, Engine, Linker, Module, Store};
5 use wasmtime_wasi::{WasiCtx, p1::WasiP1Ctx};
6 
7 #[tokio::main]
main() -> Result<(), Error>8 async fn main() -> Result<(), Error> {
9     // Create an environment shared by all wasm execution. This contains
10     // the `Engine` and the `Module` we are executing.
11     let env = Environment::new()?;
12 
13     // The inputs to run_wasm are `Send`: we can create them here and send
14     // them to a new task that we spawn.
15     let inputs1 = Inputs::new(env.clone(), "Gussie");
16     let inputs2 = Inputs::new(env.clone(), "Willa");
17     let inputs3 = Inputs::new(env, "Sparky");
18 
19     // Spawn some tasks. Insert sleeps before run_wasm so that the
20     // interleaving is easy to observe.
21     let join1 = tokio::task::spawn(async move { run_wasm(inputs1).await });
22     let join2 = tokio::task::spawn(async move {
23         tokio::time::sleep(Duration::from_millis(750)).await;
24         run_wasm(inputs2).await
25     });
26     let join3 = tokio::task::spawn(async move {
27         tokio::time::sleep(Duration::from_millis(1250)).await;
28         run_wasm(inputs3).await
29     });
30 
31     // All tasks should join successfully.
32     join1.await??;
33     join2.await??;
34     join3.await??;
35     Ok(())
36 }
37 
38 #[derive(Clone)]
39 struct Environment {
40     engine: Engine,
41     module: Module,
42     linker: Arc<Linker<WasiP1Ctx>>,
43 }
44 
45 impl Environment {
new() -> Result<Self, Error>46     pub fn new() -> Result<Self, Error> {
47         let mut config = Config::new();
48         // Consume fuel for guests so that they can co-operatively yield during
49         // execution.
50         config.consume_fuel(true);
51 
52         let engine = Engine::new(&config)?;
53         let module = Module::from_file(&engine, "target/wasm32-wasip1/debug/tokio-wasi.wasm")?;
54 
55         // A `Linker` is shared in the environment amongst all stores, and this
56         // linker is used to instantiate the `module` above. This example only
57         // adds WASI functions to the linker, notably the async versions built
58         // on tokio.
59         let mut linker = Linker::new(&engine);
60         wasmtime_wasi::p1::add_to_linker_async(&mut linker, |cx| cx)?;
61 
62         Ok(Self {
63             engine,
64             module,
65             linker: Arc::new(linker),
66         })
67     }
68 }
69 
70 struct Inputs {
71     env: Environment,
72     name: String,
73 }
74 
75 impl Inputs {
new(env: Environment, name: &str) -> Self76     fn new(env: Environment, name: &str) -> Self {
77         Self {
78             env,
79             name: name.to_owned(),
80         }
81     }
82 }
83 
run_wasm(inputs: Inputs) -> Result<(), Error>84 async fn run_wasm(inputs: Inputs) -> Result<(), Error> {
85     let wasi = WasiCtx::builder()
86         // Let wasi print to this process's stdout.
87         .inherit_stdout()
88         // Set an environment variable so the wasm knows its name.
89         .env("NAME", &inputs.name)
90         .build_p1();
91     let mut store = Store::new(&inputs.env.engine, wasi);
92 
93     // Put effectively unlimited fuel so it can run forever.
94     store.set_fuel(u64::MAX)?;
95     // WebAssembly execution will be paused for an async yield every time it
96     // consumes 10000 fuel.
97     store.fuel_async_yield_interval(Some(10000))?;
98 
99     // Instantiate into our own unique store using the shared linker, afterwards
100     // acquiring the `_start` function for the module and executing it.
101     let instance = inputs
102         .env
103         .linker
104         .instantiate_async(&mut store, &inputs.env.module)
105         .await?;
106     instance
107         .get_typed_func::<(), ()>(&mut store, "_start")?
108         .call_async(&mut store, ())
109         .await?;
110 
111     Ok(())
112 }
113