lunatic_process/runtimes/
mod.rs

1//! WebAssembly runtimes powering lunatic.
2//!
3//! Currently only Wasmtime is supported, but it should be "easy" to add any runtime that has a
4//! `Linker` abstraction and supports `async` host functions.
5//!
6//! NOTE: This traits are not used at all. Until rust supports async-traits all functions working
7//!       with a runtime will directly take `wasmtime::WasmtimeRuntime` instead of a generic.
8
9use std::sync::Arc;
10
11use anyhow::Result;
12use dashmap::DashMap;
13use tokio::task::JoinHandle;
14
15use crate::state::ProcessState;
16
17use self::wasmtime::{WasmtimeCompiledModule, WasmtimeRuntime};
18
19pub mod wasmtime;
20
21pub struct RawWasm {
22    // Id returned by control and used when spawning modules on other nodes
23    pub id: Option<u64>,
24    pub bytes: Vec<u8>,
25}
26
27impl RawWasm {
28    pub fn new(id: Option<u64>, bytes: Vec<u8>) -> Self {
29        Self { id, bytes }
30    }
31
32    pub fn as_slice(&self) -> &[u8] {
33        self.bytes.as_slice()
34    }
35}
36
37impl From<Vec<u8>> for RawWasm {
38    fn from(bytes: Vec<u8>) -> Self {
39        Self::new(None, bytes)
40    }
41}
42
43/// A `WasmRuntime` is a compiler that can generate runnable code from raw .wasm files.
44///
45/// It also provides a mechanism to register host functions that are accessible to the wasm guest
46/// code through the generic type `T`. The type `T` must implement the [`ProcessState`] trait and
47/// expose a `register` function for host functions.
48pub trait WasmRuntime<T>: Clone
49where
50    T: crate::state::ProcessState + Default + Send,
51{
52    type WasmInstance: WasmInstance;
53
54    /// Takes a raw binary WebAssembly module and returns the index of a compiled module.
55    fn compile_module(&mut self, data: RawWasm) -> anyhow::Result<usize>;
56
57    /// Returns a reference to the raw binary WebAssembly module if the index exists.
58    fn wasm_module(&self, index: usize) -> Option<&RawWasm>;
59
60    // Creates a wasm instance from compiled module if the index exists.
61    /* async fn instantiate(
62        &self,
63        index: usize,
64        state: T,
65        config: ProcessConfig,
66    ) -> Result<WasmtimeInstance<T>>; */
67}
68
69pub trait WasmInstance {
70    type Param;
71
72    // Calls a wasm function by name with the specified arguments. Ignores the returned values.
73    /* async fn call(&mut self, function: &str, params: Vec<Self::Param>) -> Result<()>; */
74}
75
76pub struct Modules<T> {
77    modules: Arc<DashMap<u64, Arc<WasmtimeCompiledModule<T>>>>,
78}
79
80impl<T> Clone for Modules<T> {
81    fn clone(&self) -> Self {
82        Self {
83            modules: self.modules.clone(),
84        }
85    }
86}
87
88impl<T> Default for Modules<T> {
89    fn default() -> Self {
90        Self {
91            modules: Arc::new(DashMap::new()),
92        }
93    }
94}
95
96impl<T: ProcessState + 'static> Modules<T> {
97    pub fn get(&self, module_id: u64) -> Option<Arc<WasmtimeCompiledModule<T>>> {
98        self.modules.get(&module_id).map(|m| m.clone())
99    }
100
101    pub fn compile(
102        &self,
103        runtime: WasmtimeRuntime,
104        wasm: RawWasm,
105    ) -> JoinHandle<Result<Arc<WasmtimeCompiledModule<T>>>> {
106        let modules = self.modules.clone();
107        tokio::task::spawn_blocking(move || {
108            let id = wasm.id;
109            match runtime.compile_module(wasm) {
110                Ok(m) => {
111                    let module = Arc::new(m);
112                    if let Some(id) = id {
113                        modules.insert(id, Arc::clone(&module));
114                    }
115                    Ok(module)
116                }
117                Err(e) => Err(e),
118            }
119        })
120    }
121}