1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
// Copyright 2021-2023 Protocol Labs
// SPDX-License-Identifier: Apache-2.0, MIT
use anyhow::anyhow;
use cid::Cid;
use fvm_shared::message::Message;
use lazy_static::lazy_static;
use super::{ApplyKind, ApplyRet, Executor};
lazy_static! {
static ref EXEC_POOL: yastl::Pool = yastl::Pool::with_config(
std::thread::available_parallelism().map(|n|n.get()).unwrap_or(8),
yastl::ThreadConfig::new()
.prefix("fvm-executor")
// fvm needs more than the default available stack (2MiB):
// - Max 2048 wasm stack elements, which is 16KiB of 64bit entries
// - Roughly 20KiB overhead per actor call
// - max 1024 nested calls, which means that in the worst case we need ~36MiB of stack
// We also want some more space just to be conservative, so 64MiB seems like a reasonable choice
.stack_size(64 << 20),
);
}
/// An executor that executes messages on a separate thread with a 64MiB stack. If you can guarantee
/// at least 64MiB of stack space, you don't need this executor.
pub struct ThreadedExecutor<E>(pub E);
impl<E> Executor for ThreadedExecutor<E>
where
E: Executor + Send,
{
type Kernel = E::Kernel;
/// This is the entrypoint to execute a message.
fn execute_message(
&mut self,
msg: Message,
apply_kind: ApplyKind,
raw_length: usize,
) -> anyhow::Result<ApplyRet> {
let mut ret = Err(anyhow!("failed to execute"));
EXEC_POOL.scoped(|scope| {
scope.execute(|| ret = self.0.execute_message(msg, apply_kind, raw_length));
});
ret
}
fn flush(&mut self) -> anyhow::Result<Cid> {
self.0.flush()
}
}