mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 05:21:08 +00:00
Better wasm instance cache (#5109)
* Wasm instance cache * Reduce slot locking * Fixed test * Dispose of instance in case of error * Fixed benches * Style, comments, some renames * Replaced upgradable lock with mutex * Bump dependencies * Re-export CallInWasm * Update client/executor/src/wasm_runtime.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Update client/executor/src/native_executor.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Update client/executor/src/native_executor.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Update client/executor/src/wasm_runtime.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Update client/executor/wasmtime/src/runtime.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Update client/executor/src/wasm_runtime.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Update client/executor/src/wasm_runtime.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Update client/executor/src/wasm_runtime.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Indents * Whitespace * Formatting * Added issue link Co-authored-by: Benjamin Kampmann <ben.kampmann@googlemail.com> Co-authored-by: Gavin Wood <github@gavwood.com> Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
40b243f1c8
commit
d3208aa7bc
@@ -14,10 +14,10 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! This crate provides an implementation of `WasmRuntime` that is baked by wasmi.
|
||||
//! This crate provides an implementation of `WasmModule` that is baked by wasmi.
|
||||
|
||||
use sc_executor_common::{error::{Error, WasmError}, sandbox};
|
||||
use std::{str, mem, cell::RefCell};
|
||||
use std::{str, mem, cell::RefCell, sync::Arc};
|
||||
use wasmi::{
|
||||
Module, ModuleInstance, MemoryInstance, MemoryRef, TableRef, ImportsBuilder, ModuleRef,
|
||||
memory_units::Pages, RuntimeValue::{I32, I64, self},
|
||||
@@ -30,7 +30,7 @@ use sp_wasm_interface::{
|
||||
FunctionContext, Pointer, WordSize, Sandbox, MemoryId, Result as WResult, Function,
|
||||
};
|
||||
use sp_runtime_interface::unpack_ptr_and_len;
|
||||
use sc_executor_common::wasm_runtime::WasmRuntime;
|
||||
use sc_executor_common::wasm_runtime::{WasmModule, WasmInstance};
|
||||
|
||||
struct FunctionExecutor<'a> {
|
||||
sandbox_store: sandbox::Store<wasmi::FuncRef>,
|
||||
@@ -623,9 +623,77 @@ impl StateSnapshot {
|
||||
}
|
||||
}
|
||||
|
||||
/// A runtime along with its initial state snapshot.
|
||||
#[derive(Clone)]
|
||||
/// A runtime along with initial copy of data segments.
|
||||
pub struct WasmiRuntime {
|
||||
/// A wasm module.
|
||||
module: Module,
|
||||
/// The host functions registered for this instance.
|
||||
host_functions: Arc<Vec<&'static dyn Function>>,
|
||||
/// Enable stub generation for functions that are not available in `host_functions`.
|
||||
/// These stubs will error when the wasm blob tries to call them.
|
||||
allow_missing_func_imports: bool,
|
||||
/// Numer of heap pages this runtime uses.
|
||||
heap_pages: u64,
|
||||
/// Data segments created for each new instance.
|
||||
data_segments: Vec<DataSegment>,
|
||||
}
|
||||
|
||||
impl WasmModule for WasmiRuntime {
|
||||
fn new_instance(&self) -> Result<Box<dyn WasmInstance>, Error> {
|
||||
// Instantiate this module.
|
||||
let (instance, missing_functions, memory) = instantiate_module(
|
||||
self.heap_pages as usize,
|
||||
&self.module,
|
||||
&self.host_functions,
|
||||
self.allow_missing_func_imports,
|
||||
).map_err(|e| WasmError::Instantiation(e.to_string()))?;
|
||||
|
||||
// Take state snapshot before executing anything.
|
||||
let state_snapshot = StateSnapshot::take(&instance, self.data_segments.clone())
|
||||
.expect(
|
||||
"`take` returns `Err` if the module is not valid;
|
||||
we already loaded module above, thus the `Module` is proven to be valid at this point;
|
||||
qed
|
||||
",
|
||||
);
|
||||
|
||||
Ok(Box::new(WasmiInstance {
|
||||
instance,
|
||||
memory,
|
||||
state_snapshot,
|
||||
host_functions: self.host_functions.clone(),
|
||||
allow_missing_func_imports: self.allow_missing_func_imports,
|
||||
missing_functions,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new `WasmiRuntime` given the code. This function loads the module and
|
||||
/// stores it in the instance.
|
||||
pub fn create_runtime(
|
||||
code: &[u8],
|
||||
heap_pages: u64,
|
||||
host_functions: Vec<&'static dyn Function>,
|
||||
allow_missing_func_imports: bool,
|
||||
) -> Result<WasmiRuntime, WasmError> {
|
||||
let module = Module::from_buffer(&code).map_err(|_| WasmError::InvalidModule)?;
|
||||
|
||||
// Extract the data segments from the wasm code.
|
||||
//
|
||||
// A return of this error actually indicates that there is a problem in logic, since
|
||||
// we just loaded and validated the `module` above.
|
||||
let data_segments = extract_data_segments(&code)?;
|
||||
Ok(WasmiRuntime {
|
||||
module,
|
||||
data_segments,
|
||||
host_functions: Arc::new(host_functions),
|
||||
allow_missing_func_imports,
|
||||
heap_pages,
|
||||
})
|
||||
}
|
||||
|
||||
/// Wasmi instance wrapper along with the state snapshot.
|
||||
pub struct WasmiInstance {
|
||||
/// A wasm module instance.
|
||||
instance: ModuleRef,
|
||||
/// The memory instance of used by the wasm module.
|
||||
@@ -633,7 +701,7 @@ pub struct WasmiRuntime {
|
||||
/// The snapshot of the instance's state taken just after the instantiation.
|
||||
state_snapshot: StateSnapshot,
|
||||
/// The host functions registered for this instance.
|
||||
host_functions: Vec<&'static dyn Function>,
|
||||
host_functions: Arc<Vec<&'static dyn Function>>,
|
||||
/// Enable stub generation for functions that are not available in `host_functions`.
|
||||
/// These stubs will error when the wasm blob tries to call them.
|
||||
allow_missing_func_imports: bool,
|
||||
@@ -641,13 +709,12 @@ pub struct WasmiRuntime {
|
||||
missing_functions: Vec<String>,
|
||||
}
|
||||
|
||||
impl WasmRuntime for WasmiRuntime {
|
||||
fn host_functions(&self) -> &[&'static dyn Function] {
|
||||
&self.host_functions
|
||||
}
|
||||
// This is safe because `WasmiInstance` does not leak any references to `self.memory` and `self.instance`
|
||||
unsafe impl Send for WasmiInstance {}
|
||||
|
||||
impl WasmInstance for WasmiInstance {
|
||||
fn call(
|
||||
&mut self,
|
||||
&self,
|
||||
method: &str,
|
||||
data: &[u8],
|
||||
) -> Result<Vec<u8>, Error> {
|
||||
@@ -664,67 +731,26 @@ impl WasmRuntime for WasmiRuntime {
|
||||
&self.memory,
|
||||
method,
|
||||
data,
|
||||
&self.host_functions,
|
||||
self.host_functions.as_ref(),
|
||||
self.allow_missing_func_imports,
|
||||
&self.missing_functions,
|
||||
self.missing_functions.as_ref(),
|
||||
)
|
||||
}
|
||||
|
||||
fn get_global_val(&self, name: &str) -> Result<Option<sp_wasm_interface::Value>, Error> {
|
||||
fn get_global_const(&self, name: &str) -> Result<Option<sp_wasm_interface::Value>, Error> {
|
||||
match self.instance.export_by_name(name) {
|
||||
Some(global) => Ok(Some(
|
||||
global
|
||||
.as_global()
|
||||
.ok_or_else(|| format!("`{}` is not a global", name))?
|
||||
.get()
|
||||
.into()
|
||||
.as_global()
|
||||
.ok_or_else(|| format!("`{}` is not a global", name))?
|
||||
.get()
|
||||
.into()
|
||||
)),
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_instance(
|
||||
code: &[u8],
|
||||
heap_pages: u64,
|
||||
host_functions: Vec<&'static dyn Function>,
|
||||
allow_missing_func_imports: bool,
|
||||
) -> Result<WasmiRuntime, WasmError> {
|
||||
let module = Module::from_buffer(&code).map_err(|_| WasmError::InvalidModule)?;
|
||||
|
||||
// Extract the data segments from the wasm code.
|
||||
//
|
||||
// A return of this error actually indicates that there is a problem in logic, since
|
||||
// we just loaded and validated the `module` above.
|
||||
let data_segments = extract_data_segments(&code)?;
|
||||
|
||||
// Instantiate this module.
|
||||
let (instance, missing_functions, memory) = instantiate_module(
|
||||
heap_pages as usize,
|
||||
&module,
|
||||
&host_functions,
|
||||
allow_missing_func_imports,
|
||||
).map_err(|e| WasmError::Instantiation(e.to_string()))?;
|
||||
|
||||
// Take state snapshot before executing anything.
|
||||
let state_snapshot = StateSnapshot::take(&instance, data_segments)
|
||||
.expect(
|
||||
"`take` returns `Err` if the module is not valid;
|
||||
we already loaded module above, thus the `Module` is proven to be valid at this point;
|
||||
qed
|
||||
",
|
||||
);
|
||||
|
||||
Ok(WasmiRuntime {
|
||||
instance,
|
||||
memory,
|
||||
state_snapshot,
|
||||
host_functions,
|
||||
allow_missing_func_imports,
|
||||
missing_functions,
|
||||
})
|
||||
}
|
||||
|
||||
/// Extract the data segments from the given wasm code.
|
||||
///
|
||||
/// Returns `Err` if the given wasm code cannot be deserialized.
|
||||
|
||||
Reference in New Issue
Block a user