cardinal_wasm_plugins/
instance.rs1use 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}