virtual_machine/manager.rs
1//! Virtual Machine Manager - Global singleton for WASM runtime management.
2
3//!
4//! The Manager provides a centralized interface for initializing the WASM runtime,
5//! registering host functions, and executing WASM modules. It maintains a global
6//! singleton instance that can be accessed throughout the system.
7
8use core::{ffi::CStr, mem::forget};
9
10use abi_context::FileIdentifier;
11use alloc::{string::ToString, vec, vec::Vec};
12use synchronization::once_lock::OnceLock;
13use task::TaskIdentifier;
14use virtual_file_system::File;
15use wamr_rust_sdk::{
16 sys::{wasm_runtime_is_xip_file, wasm_runtime_load, wasm_runtime_register_module},
17 value::WasmValue,
18};
19
20use crate::{Error, Instance, Module, Registrable, Result, Runtime};
21
22/// Global singleton instance of the Virtual Machine Manager
23static MANAGER_INSTANCE: OnceLock<Manager> = OnceLock::new();
24
25/// Initialize the Virtual Machine Manager with a set of registrable host functions.
26///
27/// This function must be called once before any WASM operations can be performed.
28/// It creates a global singleton Manager instance that will persist for the
29/// lifetime of the application.
30///
31/// # Arguments
32///
33/// * `Registrables` - Array of traits implementing host functions that can be called from WASM
34///
35/// # Returns
36///
37/// A static reference to the initialized Manager instance
38///
39/// # Example
40///
41/// ```rust,ignore
42/// let manager = Initialize(&[&MyHostFunctions]);
43/// ```
44pub fn initialize(registrables: &[&dyn Registrable]) -> &'static Manager {
45 MANAGER_INSTANCE
46 .get_or_init(|| Manager::new(registrables).expect("Cannot create virtual machine manager"));
47
48 get_instance()
49}
50
51/// Get a reference to the initialized Virtual Machine Manager instance.
52///
53/// # Panics
54///
55/// Panics if called before `Initialize()` has been called.
56///
57/// # Returns
58///
59/// A static reference to the Manager instance
60pub fn get_instance() -> &'static Manager {
61 MANAGER_INSTANCE
62 .try_get()
63 .expect("Cannot get virtual machine manager instance before initialization")
64}
65
66/// The Virtual Machine Manager handles WASM runtime lifecycle and module execution.
67///
68/// This struct encapsulates the WASM runtime and provides high-level operations
69/// for executing WASM modules with proper I/O redirection and resource management.
70pub struct Manager {
71 runtime: Runtime,
72}
73
74unsafe impl Send for Manager {}
75
76unsafe impl Sync for Manager {}
77
78impl Manager {
79 /// Create a new Virtual Machine Manager with the given registrable host functions.
80 ///
81 /// This function initializes the WASM runtime, registers all provided host functions,
82 /// and pre-loads any modules that the registrables provide.
83 ///
84 /// # Arguments
85 ///
86 /// * `Registrables` - Array of objects implementing host functions and optionally providing WASM modules
87 ///
88 /// # Returns
89 ///
90 /// A new Manager instance or an error if initialization fails
91 ///
92 /// # Errors
93 ///
94 /// Returns an error if:
95 /// - Runtime initialization fails
96 /// - Host function registration fails
97 /// - Module loading fails
98 pub fn new(registrables: &[&dyn Registrable]) -> Result<Self> {
99 let mut runtime_builder = Runtime::builder();
100
101 for registrable in registrables {
102 runtime_builder = runtime_builder.register(*registrable);
103 }
104
105 let runtime = runtime_builder.build()?;
106
107 let manager = Self { runtime };
108
109 for registrable in registrables {
110 if let Some(module_binary) = registrable.get_binary() {
111 manager.load_module(module_binary, registrable.is_xip(), registrable.get_name())?;
112 }
113 }
114
115 Ok(manager)
116 }
117
118 /// Load a WASM module from a buffer for execution.
119 ///
120 /// This method loads a WASM module into the runtime, either as a regular module
121 /// or as an XIP (execute-in-place) module for AOT compiled binaries.
122 ///
123 /// # Arguments
124 ///
125 /// * `Buffer` - The WASM module bytecode
126 /// * `XIP` - Whether this is an XIP AOT compiled module
127 /// * `Name` - Name to register the module under
128 ///
129 /// # Returns
130 ///
131 /// Success or an error if loading fails
132 ///
133 /// # Errors
134 ///
135 /// Returns an error if the module is not an XIP AOT compiled module or if the module cannot be loaded from the buffer.
136 fn load_module(&self, buffer: &[u8], xip: bool, name: &str) -> Result<()> {
137 if unsafe { xip && !wasm_runtime_is_xip_file(buffer.as_ptr(), buffer.len() as u32) } {
138 return Err(Error::InvalidModule);
139 }
140
141 unsafe {
142 let mut buffer = if xip {
143 Vec::from_raw_parts(buffer.as_ptr() as *mut u8, buffer.len(), buffer.len())
144 } else {
145 buffer.to_vec()
146 };
147
148 let mut error_buffer = [0_i8; 128];
149
150 let module = wasm_runtime_load(
151 buffer.as_mut_ptr(),
152 buffer.len() as u32,
153 error_buffer.as_mut_ptr(),
154 error_buffer.len() as u32,
155 );
156
157 if module.is_null() {
158 return Err(Error::CompilationError(
159 CStr::from_ptr(error_buffer.as_ptr())
160 .to_string_lossy()
161 .to_string(),
162 ));
163 }
164
165 if !wasm_runtime_register_module(
166 name.as_ptr() as *const i8,
167 module,
168 error_buffer.as_mut_ptr(),
169 error_buffer.len() as u32,
170 ) {
171 return Err(Error::InternalError);
172 }
173
174 forget(buffer);
175 }
176
177 Ok(())
178 }
179
180 /// Execute a WASM module with the specified I/O configuration.
181 ///
182 /// This is the main entry point for executing WASM modules. It creates a new
183 /// module instance, sets up the execution environment with proper I/O redirection,
184 /// and calls the module's main function.
185 ///
186 /// # Arguments
187 ///
188 /// * `Buffer` - The WASM module bytecode to execute
189 /// * `Stack_size` - Stack size in bytes for the WASM instance
190 /// * `Standard_in` - File identifier for standard input
191 /// * `Standard_out` - File identifier for standard output
192 /// * `Standard_error` - File identifier for standard error
193 ///
194 /// # Returns
195 ///
196 /// The return values from the WASM module's main function
197 ///
198 /// # Errors
199 ///
200 /// Returns an error if module loading, instantiation, or execution fails
201 pub async fn execute(
202 &'static self,
203 buffer: Vec<u8>,
204 stack_size: usize,
205 (standard_in, standard_out, standard_error): (File, File, File),
206 function_name: Option<&str>,
207 function_arguments: Vec<WasmValue>,
208 task: TaskIdentifier,
209 ) -> Result<Vec<WasmValue>> {
210 let abi_context = abi_context::get_instance();
211
212 abi_context
213 .call_abi(async || {
214 let standard_in = abi_context
215 .insert_file(
216 task,
217 standard_in.into_synchronous_file(),
218 Some(FileIdentifier::STANDARD_IN),
219 )
220 .ok_or(Error::FailedToRegisterFileContext)?;
221 let standard_out = abi_context
222 .insert_file(
223 task,
224 standard_out.into_synchronous_file(),
225 Some(FileIdentifier::STANDARD_OUT),
226 )
227 .ok_or(Error::FailedToRegisterFileContext)?;
228 let standard_error = abi_context
229 .insert_file(
230 task,
231 standard_error.into_synchronous_file(),
232 Some(FileIdentifier::STANDARD_ERROR),
233 )
234 .ok_or(Error::FailedToRegisterFileContext)?;
235
236 let module = Module::from_buffer(
237 &self.runtime,
238 buffer,
239 "module",
240 standard_in,
241 standard_out,
242 standard_error,
243 )
244 .await?;
245
246 let instance = Instance::new(&self.runtime, &module, stack_size)?;
247
248 let result = if let Some(function_name) = function_name {
249 instance.call_export_function(function_name, &function_arguments)?
250 } else {
251 instance.call_main(function_arguments.as_ref())?
252 };
253
254 Ok(result)
255 })
256 .await
257 }
258}