fvm/executor/
threaded.rs

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