marine_core/
marine_core.rs

1/*
2 * Copyright 2022 Fluence Labs Limited
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17use super::generic::*;
18use crate::config::MarineCoreConfig;
19use crate::module::MModule;
20use crate::module::MRecordTypes;
21use crate::{IRecordType, IValue, MemoryStats, MError, MFunctionSignature, ModuleMemoryStat, MResult};
22
23use marine_wasm_backend_traits::AsContextMut;
24use marine_wasm_backend_traits::Store;
25use marine_wasm_backend_traits::WasiState;
26use marine_wasm_backend_traits::WasmBackend;
27
28use serde::Serialize;
29
30use std::collections::hash_map::Entry;
31use std::collections::HashMap;
32use std::sync::Arc;
33use std::cell::RefCell;
34
35/// Represent Marine module interface.
36#[derive(PartialEq, Eq, Debug, Clone, Serialize)]
37pub struct MModuleInterface<'a> {
38    pub record_types: &'a MRecordTypes,
39    pub function_signatures: Vec<MFunctionSignature>,
40}
41
42/// # Description
43///
44/// The base struct of Marine, the Fluence compute runtime.
45/// Allows dynamic loading and unloading modules, but never frees resources used for instantiation.
46/// A new module can import functions from previously loaded modules.
47///
48/// # Recommendations
49///
50/// Its not recommended to use this struct to load/unload unlimited number of modules.
51/// Better alternative is to use multiple instances of this struct for independent groups of modules
52/// and drop them when the group is no longer needed.
53pub struct MarineCore<WB: WasmBackend> {
54    // set of modules registered inside Marine
55    modules: HashMap<String, MModule<WB>>,
56    // Wasm backend may have state in the future
57    #[allow(unused)]
58    wasm_backend: WB,
59    /// Container for all objects created by a Wasm backend.
60    store: RefCell<<WB as WasmBackend>::Store>,
61}
62
63impl<WB: WasmBackend> MarineCore<WB> {
64    pub fn new(config: MarineCoreConfig<WB>) -> MResult<Self> {
65        let mut store = <WB as WasmBackend>::Store::new(&config.wasm_backend);
66        store.set_total_memory_limit(config.total_memory_limit);
67        Ok(Self {
68            modules: HashMap::new(),
69            wasm_backend: config.wasm_backend,
70            store: RefCell::new(store),
71        })
72    }
73
74    /// Invoke a function of a module inside Marine by given function name with given arguments.
75    pub async fn call_async(
76        &mut self,
77        module_name: impl AsRef<str>,
78        func_name: impl AsRef<str>,
79        arguments: &[IValue],
80    ) -> MResult<Vec<IValue>> {
81        let module_name = module_name.as_ref();
82        let store = &mut self.store;
83        let module = self
84            .modules
85            .get_mut(module_name)
86            .ok_or_else(|| MError::NoSuchModule(module_name.to_string()))?;
87
88        module
89            .call_async(
90                &mut store.get_mut().as_context_mut(),
91                module_name,
92                func_name.as_ref(),
93                arguments,
94            )
95            .await
96    }
97
98    /// Load a new module inside Marine.
99    pub async fn load_module(
100        &mut self,
101        name: impl Into<String>,
102        wasm_bytes: &[u8],
103        config: MModuleConfig<WB>,
104    ) -> MResult<()> {
105        self.load_module_(name.into(), wasm_bytes, config).await
106    }
107
108    async fn load_module_(
109        &mut self,
110        name: String,
111        wasm_bytes: &[u8],
112        config: MModuleConfig<WB>,
113    ) -> MResult<()> {
114        let module = MModule::new(
115            &name,
116            self.store.get_mut(),
117            wasm_bytes,
118            config,
119            &self.modules,
120        )
121        .await?;
122
123        match self.modules.entry(name) {
124            Entry::Vacant(entry) => {
125                entry.insert(module);
126                Ok(())
127            }
128            Entry::Occupied(entry) => Err(MError::NonUniqueModuleName(entry.key().clone())),
129        }
130    }
131
132    /// Unload previously loaded module.
133    pub fn unload_module(&mut self, name: impl AsRef<str>) -> MResult<()> {
134        // TODO: clean up all reference from adaptors after adding support of lazy linking
135        self.modules
136            .remove(name.as_ref())
137            .map(|_| ())
138            .ok_or_else(|| MError::NoSuchModule(name.as_ref().to_string()))
139    }
140
141    pub fn module_wasi_state<'s>(
142        &'s mut self,
143        module_name: impl AsRef<str>,
144    ) -> Option<Box<dyn WasiState + 's>> {
145        self.modules
146            .get_mut(module_name.as_ref())
147            .map(|module| module.get_wasi_state())
148    }
149
150    /// Return function signatures of all loaded info Marine modules with their names.
151    pub fn interface(&self) -> impl Iterator<Item = (&str, MModuleInterface<'_>)> {
152        self.modules
153            .iter()
154            .map(|(module_name, module)| (module_name.as_str(), Self::get_module_interface(module)))
155    }
156
157    /// Return function signatures exported by module with given name.
158    pub fn module_interface(&self, module_name: impl AsRef<str>) -> Option<MModuleInterface<'_>> {
159        self.modules
160            .get(module_name.as_ref())
161            .map(Self::get_module_interface)
162    }
163
164    /// Return record types exported by module with given name.
165    pub fn module_record_types(&self, module_name: impl AsRef<str>) -> Option<&MRecordTypes> {
166        self.modules
167            .get(module_name.as_ref())
168            .map(|module| module.export_record_types())
169    }
170
171    /// Return record type for supplied record id exported by module with given name.
172    pub fn module_record_type_by_id(
173        &self,
174        module_name: impl AsRef<str>,
175        record_id: u64,
176    ) -> Option<&Arc<IRecordType>> {
177        self.modules
178            .get(module_name.as_ref())
179            .and_then(|module| module.export_record_type_by_id(record_id))
180    }
181
182    /// Returns a heap size that all modules consume in bytes.
183    pub fn module_memory_stats(&self) -> MemoryStats<'_> {
184        let records = self
185            .modules
186            .iter()
187            .map(|(module_name, module)| {
188                ModuleMemoryStat::new(
189                    module_name,
190                    module.memory_size(&mut self.store.borrow_mut().as_context_mut()),
191                )
192            })
193            .collect::<Vec<_>>();
194        let allocation_stats = self.store.borrow_mut().report_memory_allocation_stats();
195        MemoryStats::new(records, allocation_stats)
196    }
197
198    pub fn clear_allocation_stats(&mut self) {
199        self.store.borrow_mut().clear_allocation_stats()
200    }
201
202    fn get_module_interface(module: &MModule<WB>) -> MModuleInterface<'_> {
203        let record_types = module.export_record_types();
204
205        let function_signatures = module.get_exports_signatures().collect::<Vec<_>>();
206
207        MModuleInterface {
208            record_types,
209            function_signatures,
210        }
211    }
212}