cardinal_wasm_plugins/
instance.rs

1use crate::context::ExecutionContext;
2use crate::host::{make_imports, HostImportHandle};
3use crate::plugin::WasmPlugin;
4use crate::runner::ExecutionPhase;
5use crate::SharedExecutionContext;
6use cardinal_errors::internal::CardinalInternalError;
7use cardinal_errors::CardinalError;
8use parking_lot::Mutex;
9use std::sync::Arc;
10use wasmer::{FunctionEnv, Instance, Memory, Store, TypedFunction};
11
12const ALLOC_FUNC: &str = "__new";
13
14pub struct InstancePool {
15    plugin: Arc<WasmPlugin>,
16    phase: ExecutionPhase,
17    dynamic_imports: Arc<Vec<HostImportHandle>>,
18    instances: Mutex<Vec<PreparedInstance>>,
19}
20
21impl InstancePool {
22    pub fn new(
23        plugin: Arc<WasmPlugin>,
24        phase: ExecutionPhase,
25        dynamic_imports: Vec<HostImportHandle>,
26    ) -> Self {
27        Self {
28            plugin,
29            phase,
30            dynamic_imports: Arc::new(dynamic_imports),
31            instances: Mutex::new(Vec::new()),
32        }
33    }
34
35    pub fn acquire(&self, ctx: SharedExecutionContext) -> Result<InstanceGuard<'_>, CardinalError> {
36        let mut pooled = self.instances.lock();
37        let mut instance = pooled.pop();
38        drop(pooled);
39
40        if instance.is_none() {
41            instance = Some(self.instantiate()?);
42        }
43
44        let mut instance = instance.expect("instance must be present");
45        instance.activate(ctx);
46
47        Ok(InstanceGuard {
48            pool: self,
49            instance: Some(instance),
50        })
51    }
52
53    fn instantiate(&self) -> Result<PreparedInstance, CardinalError> {
54        let mut store = Store::new(self.plugin.engine.clone());
55        let placeholder_ctx = Arc::new(parking_lot::RwLock::new(ExecutionContext::default()));
56        let env = FunctionEnv::new(&mut store, placeholder_ctx.clone());
57
58        let imports = make_imports(&mut store, &env, self.phase, self.dynamic_imports.as_ref());
59
60        let instance = Instance::new(&mut store, &self.plugin.module, &imports).map_err(|e| {
61            CardinalError::InternalError(CardinalInternalError::InvalidWasmModule(format!(
62                "Error creating WASM Instance {e}"
63            )))
64        })?;
65
66        let memory_name = self.plugin.memory_name.as_str();
67        let memory = instance
68            .exports
69            .get_memory(memory_name)
70            .map_err(|e| {
71                CardinalError::InternalError(CardinalInternalError::InvalidWasmModule(format!(
72                    "missing memory export `{memory_name}`: {e}"
73                )))
74            })?
75            .clone();
76
77        initialize_placeholder_memory(&env, &mut store, memory.clone());
78
79        let handle = instance
80            .exports
81            .get_typed_function::<(i32, i32), i32>(&store, self.plugin.handle_name.as_str())
82            .map_err(|e| {
83                CardinalError::InternalError(CardinalInternalError::InvalidWasmModule(format!(
84                    "missing `{}` export {e}",
85                    self.plugin.handle_name
86                )))
87            })?;
88
89        let allocator = instance
90            .exports
91            .get_typed_function::<(i32, i32), i32>(&store, ALLOC_FUNC)
92            .map_err(|e| {
93                CardinalError::InternalError(CardinalInternalError::InvalidWasmModule(format!(
94                    "missing `{ALLOC_FUNC}` export {e}"
95                )))
96            })?;
97
98        Ok(PreparedInstance {
99            store,
100            _instance: instance,
101            memory,
102            env,
103            handle,
104            allocator,
105        })
106    }
107}
108
109pub struct InstanceGuard<'a> {
110    pool: &'a InstancePool,
111    instance: Option<PreparedInstance>,
112}
113
114impl<'a> InstanceGuard<'a> {
115    pub fn instance(&mut self) -> &mut PreparedInstance {
116        self.instance.as_mut().expect("instance should be present")
117    }
118}
119
120impl Drop for InstanceGuard<'_> {
121    fn drop(&mut self) {
122        if let Some(instance) = self.instance.take() {
123            let mut pooled = self.pool.instances.lock();
124            pooled.push(instance);
125        }
126    }
127}
128
129pub struct PreparedInstance {
130    store: Store,
131    _instance: Instance,
132    memory: Memory,
133    env: FunctionEnv<SharedExecutionContext>,
134    handle: TypedFunction<(i32, i32), i32>,
135    allocator: TypedFunction<(i32, i32), i32>,
136}
137
138impl PreparedInstance {
139    pub fn activate(&mut self, ctx: SharedExecutionContext) {
140        {
141            let stored = self.env.as_mut(&mut self.store);
142            *stored = ctx.clone();
143        }
144
145        {
146            let mut guard = ctx.write();
147            guard.replace_memory(self.memory.clone());
148        }
149    }
150
151    pub fn memory(&self) -> &Memory {
152        &self.memory
153    }
154
155    pub fn write_body(&mut self, body: Option<&[u8]>) -> Result<(i32, i32), CardinalError> {
156        let Some(body) = body else {
157            return Ok((0, 0));
158        };
159
160        if body.is_empty() {
161            return Ok((0, 0));
162        }
163
164        let len = i32::try_from(body.len()).map_err(|_| {
165            CardinalError::InternalError(CardinalInternalError::InvalidWasmModule(
166                "body too large".into(),
167            ))
168        })?;
169
170        let ptr = self.allocator.call(&mut self.store, len, 0).map_err(|e| {
171            CardinalError::InternalError(CardinalInternalError::InvalidWasmModule(format!(
172                "Alloc failed {e}"
173            )))
174        })?;
175
176        let view = self.memory.view(&self.store);
177        view.write(ptr as u64, body).map_err(|e| {
178            CardinalError::InternalError(CardinalInternalError::InvalidWasmModule(format!(
179                "Writing body failed {e}"
180            )))
181        })?;
182
183        Ok((ptr, len))
184    }
185
186    pub fn call_handle(&mut self, ptr: i32, len: i32) -> Result<i32, CardinalError> {
187        self.handle.call(&mut self.store, ptr, len).map_err(|e| {
188            CardinalError::InternalError(CardinalInternalError::InvalidWasmModule(format!(
189                "WASM handle call failed {e}"
190            )))
191        })
192    }
193}
194
195fn initialize_placeholder_memory(
196    env: &FunctionEnv<SharedExecutionContext>,
197    store: &mut Store,
198    memory: Memory,
199) {
200    let env_mut = env.as_mut(store);
201    let mut ctx = env_mut.write();
202    ctx.replace_memory(memory);
203}