mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-26 15:47:58 +00:00
Runtime worker threads (#7089)
* std variant * principal work * format and naming * format and naming continued * working nested fork * add comment * naming and tabs * line width * fix wording * address review * refactor dynamic dispatch * update wasmtime * some care * move ext * more refactor * doc effort * simplify * doc effort * tests and docs * address review * naming * explain some args * add example * unwinding for native and tests * rename stray * fix refs * fix tests * fix warnings * stray naming * fixes and comments * Update primitives/io/src/tasks.rs Co-authored-by: cheme <emericchevalier.pro@gmail.com> * make examples "compile" * dyn_dispatch -> spawn_call * fix impl * address review * Update primitives/io/src/lib.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * Update primitives/io/src/tasks.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * Update primitives/io/src/async_externalities.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * Update primitives/io/src/tasks.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * Update frame/example-parallel/src/lib.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * fix compilation * Update client/executor/common/src/wasm_runtime.rs Co-authored-by: Sergei Shulepov <sergei@parity.io> * address review * Update client/executor/wasmtime/src/instance_wrapper.rs Co-authored-by: Sergei Shulepov <sergei@parity.io> * Update client/executor/src/native_executor.rs Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> * Update primitives/io/src/tasks.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 primitives/io/src/tasks.rs Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> * Update client/executor/wasmtime/src/instance_wrapper.rs Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> * address some issues * address more issues * wasm_only interface * define sp_tasks * avoid anyhow * fix example Co-authored-by: cheme <emericchevalier.pro@gmail.com> Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Co-authored-by: Sergei Shulepov <sergei@parity.io> Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>
This commit is contained in:
@@ -26,6 +26,7 @@ use std::{slice, marker};
|
||||
use sc_executor_common::{
|
||||
error::{Error, Result},
|
||||
util::{WasmModuleInfo, DataSegmentsSnapshot},
|
||||
wasm_runtime::InvokeMethod,
|
||||
};
|
||||
use sp_wasm_interface::{Pointer, WordSize, Value};
|
||||
use wasmtime::{Engine, Instance, Module, Memory, Table, Val, Func, Extern, Global, Store};
|
||||
@@ -72,6 +73,82 @@ impl ModuleWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
/// Invoked entrypoint format.
|
||||
pub enum EntryPointType {
|
||||
/// Direct call.
|
||||
///
|
||||
/// Call is made by providing only payload reference and length.
|
||||
Direct,
|
||||
/// Indirect call.
|
||||
///
|
||||
/// Call is made by providing payload reference and length, and extra argument
|
||||
/// for advanced routing (typically extra WASM function pointer).
|
||||
Wrapped(u32),
|
||||
}
|
||||
|
||||
/// Wasm blob entry point.
|
||||
pub struct EntryPoint {
|
||||
call_type: EntryPointType,
|
||||
func: wasmtime::Func,
|
||||
}
|
||||
|
||||
impl EntryPoint {
|
||||
/// Call this entry point.
|
||||
pub fn call(&self, data_ptr: Pointer<u8>, data_len: WordSize) -> Result<u64> {
|
||||
let data_ptr = u32::from(data_ptr) as i32;
|
||||
let data_len = u32::from(data_len) as i32;
|
||||
|
||||
(match self.call_type {
|
||||
EntryPointType::Direct => {
|
||||
self.func.call(&[
|
||||
wasmtime::Val::I32(data_ptr),
|
||||
wasmtime::Val::I32(data_len),
|
||||
])
|
||||
},
|
||||
EntryPointType::Wrapped(func) => {
|
||||
self.func.call(&[
|
||||
wasmtime::Val::I32(func as _),
|
||||
wasmtime::Val::I32(data_ptr),
|
||||
wasmtime::Val::I32(data_len),
|
||||
])
|
||||
},
|
||||
})
|
||||
.map(|results|
|
||||
// the signature is checked to have i64 return type
|
||||
results[0].unwrap_i64() as u64
|
||||
)
|
||||
.map_err(|err| Error::from(format!(
|
||||
"Wasm execution trapped: {}",
|
||||
err
|
||||
)))
|
||||
}
|
||||
|
||||
pub fn direct(func: wasmtime::Func) -> std::result::Result<Self, &'static str> {
|
||||
match (func.ty().params(), func.ty().results()) {
|
||||
(&[wasmtime::ValType::I32, wasmtime::ValType::I32], &[wasmtime::ValType::I64]) => {
|
||||
Ok(Self { func, call_type: EntryPointType::Direct })
|
||||
}
|
||||
_ => {
|
||||
Err("Invalid signature for direct entry point")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wrapped(dispatcher: wasmtime::Func, func: u32) -> std::result::Result<Self, &'static str> {
|
||||
match (dispatcher.ty().params(), dispatcher.ty().results()) {
|
||||
(
|
||||
&[wasmtime::ValType::I32, wasmtime::ValType::I32, wasmtime::ValType::I32],
|
||||
&[wasmtime::ValType::I64],
|
||||
) => {
|
||||
Ok(Self { func: dispatcher, call_type: EntryPointType::Wrapped(func) })
|
||||
},
|
||||
_ => {
|
||||
Err("Invalid signature for wrapped entry point")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrap the given WebAssembly Instance of a wasm module with Substrate-runtime.
|
||||
///
|
||||
/// This struct is a handy wrapper around a wasmtime `Instance` that provides substrate specific
|
||||
@@ -150,24 +227,62 @@ impl InstanceWrapper {
|
||||
///
|
||||
/// An entrypoint must have a signature `(i32, i32) -> i64`, otherwise this function will return
|
||||
/// an error.
|
||||
pub fn resolve_entrypoint(&self, name: &str) -> Result<wasmtime::Func> {
|
||||
// Resolve the requested method and verify that it has a proper signature.
|
||||
let export = self
|
||||
.instance
|
||||
.get_export(name)
|
||||
.ok_or_else(|| Error::from(format!("Exported method {} is not found", name)))?;
|
||||
let entrypoint = extern_func(&export)
|
||||
.ok_or_else(|| Error::from(format!("Export {} is not a function", name)))?;
|
||||
match (entrypoint.ty().params(), entrypoint.ty().results()) {
|
||||
(&[wasmtime::ValType::I32, wasmtime::ValType::I32], &[wasmtime::ValType::I64]) => {}
|
||||
_ => {
|
||||
return Err(Error::from(format!(
|
||||
"method {} have an unsupported signature",
|
||||
name
|
||||
)))
|
||||
}
|
||||
}
|
||||
Ok(entrypoint.clone())
|
||||
pub fn resolve_entrypoint(&self, method: InvokeMethod) -> Result<EntryPoint> {
|
||||
Ok(match method {
|
||||
InvokeMethod::Export(method) => {
|
||||
// Resolve the requested method and verify that it has a proper signature.
|
||||
let export = self
|
||||
.instance
|
||||
.get_export(method)
|
||||
.ok_or_else(|| Error::from(format!("Exported method {} is not found", method)))?;
|
||||
let func = extern_func(&export)
|
||||
.ok_or_else(|| Error::from(format!("Export {} is not a function", method)))?
|
||||
.clone();
|
||||
EntryPoint::direct(func)
|
||||
.map_err(|_|
|
||||
Error::from(format!(
|
||||
"Exported function '{}' has invalid signature.",
|
||||
method,
|
||||
))
|
||||
)?
|
||||
},
|
||||
InvokeMethod::Table(func_ref) => {
|
||||
let table = self.instance.get_table("__indirect_function_table").ok_or(Error::NoTable)?;
|
||||
let val = table.get(func_ref)
|
||||
.ok_or(Error::NoTableEntryWithIndex(func_ref))?;
|
||||
let func = val
|
||||
.funcref()
|
||||
.ok_or(Error::TableElementIsNotAFunction(func_ref))?
|
||||
.ok_or(Error::FunctionRefIsNull(func_ref))?
|
||||
.clone();
|
||||
|
||||
EntryPoint::direct(func)
|
||||
.map_err(|_|
|
||||
Error::from(format!(
|
||||
"Function @{} in exported table has invalid signature for direct call.",
|
||||
func_ref,
|
||||
))
|
||||
)?
|
||||
},
|
||||
InvokeMethod::TableWithWrapper { dispatcher_ref, func } => {
|
||||
let table = self.instance.get_table("__indirect_function_table").ok_or(Error::NoTable)?;
|
||||
let val = table.get(dispatcher_ref)
|
||||
.ok_or(Error::NoTableEntryWithIndex(dispatcher_ref))?;
|
||||
let dispatcher = val
|
||||
.funcref()
|
||||
.ok_or(Error::TableElementIsNotAFunction(dispatcher_ref))?
|
||||
.ok_or(Error::FunctionRefIsNull(dispatcher_ref))?
|
||||
.clone();
|
||||
|
||||
EntryPoint::wrapped(dispatcher, func)
|
||||
.map_err(|_|
|
||||
Error::from(format!(
|
||||
"Function @{} in exported table has invalid signature for wrapped call.",
|
||||
dispatcher_ref,
|
||||
))
|
||||
)?
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns an indirect function table of this instance.
|
||||
|
||||
@@ -18,14 +18,14 @@
|
||||
|
||||
use crate::host::HostState;
|
||||
use crate::imports::{Imports, resolve_imports};
|
||||
use crate::instance_wrapper::{ModuleWrapper, InstanceWrapper, GlobalsSnapshot};
|
||||
use crate::instance_wrapper::{ModuleWrapper, InstanceWrapper, GlobalsSnapshot, EntryPoint};
|
||||
use crate::state_holder;
|
||||
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
use sc_executor_common::{
|
||||
error::{Error, Result, WasmError},
|
||||
wasm_runtime::{WasmModule, WasmInstance},
|
||||
error::{Result, WasmError},
|
||||
wasm_runtime::{WasmModule, WasmInstance, InvokeMethod},
|
||||
};
|
||||
use sp_allocator::FreeingBumpHeapAllocator;
|
||||
use sp_runtime_interface::unpack_ptr_and_len;
|
||||
@@ -90,7 +90,7 @@ pub struct WasmtimeInstance {
|
||||
unsafe impl Send for WasmtimeInstance {}
|
||||
|
||||
impl WasmInstance for WasmtimeInstance {
|
||||
fn call(&self, method: &str, data: &[u8]) -> Result<Vec<u8>> {
|
||||
fn call(&self, method: InvokeMethod, data: &[u8]) -> Result<Vec<u8>> {
|
||||
let entrypoint = self.instance_wrapper.resolve_entrypoint(method)?;
|
||||
let allocator = FreeingBumpHeapAllocator::new(self.heap_base);
|
||||
|
||||
@@ -146,28 +146,14 @@ pub fn create_runtime(
|
||||
fn perform_call(
|
||||
data: &[u8],
|
||||
instance_wrapper: Rc<InstanceWrapper>,
|
||||
entrypoint: wasmtime::Func,
|
||||
entrypoint: EntryPoint,
|
||||
mut allocator: FreeingBumpHeapAllocator,
|
||||
) -> Result<Vec<u8>> {
|
||||
let (data_ptr, data_len) = inject_input_data(&instance_wrapper, &mut allocator, data)?;
|
||||
|
||||
let host_state = HostState::new(allocator, instance_wrapper.clone());
|
||||
let ret = state_holder::with_initialized_state(&host_state, || {
|
||||
match entrypoint.call(&[
|
||||
wasmtime::Val::I32(u32::from(data_ptr) as i32),
|
||||
wasmtime::Val::I32(u32::from(data_len) as i32),
|
||||
]) {
|
||||
Ok(results) => {
|
||||
let retval = results[0].unwrap_i64() as u64;
|
||||
Ok(unpack_ptr_and_len(retval))
|
||||
}
|
||||
Err(trap) => {
|
||||
return Err(Error::from(format!(
|
||||
"Wasm execution trapped: {}",
|
||||
trap
|
||||
)));
|
||||
}
|
||||
}
|
||||
let ret = state_holder::with_initialized_state(&host_state, || -> Result<_> {
|
||||
Ok(unpack_ptr_and_len(entrypoint.call(data_ptr, data_len)?))
|
||||
});
|
||||
let (output_ptr, output_len) = ret?;
|
||||
let output = extract_output_data(&instance_wrapper, output_ptr, output_len)?;
|
||||
|
||||
Reference in New Issue
Block a user