message: Spawns a task to run `execute_finalized_block`.
diff: @@ -5,6 +5,7 @@ mod error;
  mod operations;
  mod types;
  +use once_cell::sync::Lazy;
  use std::{
  collections::BTreeMap,
  fmt::{self, Debug, Formatter},
  @@ -50,6 +51,30 @@ pub use operations::execute_finalized_block;
  pub use types::BlockAndExecutionEffects;
  pub(crate) use types::EraValidatorsRequest;
  +/// Maximum number of resource intensive tasks that can be run in parallel.
  +const MAX_PARALLEL_INTENSIVE_TASKS: usize = 4;
  +
  +/// Semaphore enforcing maximum number of parallel resource intensive tasks.
  +static INTENSIVE_TASKS_SEMAPHORE: Lazy<tokio::sync::Semaphore> =
  + Lazy::new(|| tokio::sync::Semaphore::new(MAX_PARALLEL_INTENSIVE_TASKS));
  +
  +/// Asynchronously runs a resource intensive task.
  +/// At most `MAX_PARALLEL_INTENSIVE_TASKS` are being run in parallel at any time.
  +///
  +/// The task is a closure that takes no arguments and returns a value.
  +/// This function returns a future for that value.
  +async fn run_intensive_task<T, V>(task: T) -> V
  +where
  + T: 'static + Send + FnOnce() -> V,
  + V: 'static + Send,
  +{
  + // This will never panic since the semaphore is never closed.
  + let _permit = INTENSIVE_TASKS_SEMAPHORE.acquire().await.unwrap();
  + tokio::task::spawn_blocking(task)
  + .await
  + .expect("task panicked")
  +}
  +
  /// State to use to construct the next block in the blockchain. Includes the state root hash for the
  /// execution engine as well as certain values the next header will be based on.
  #[derive(DataSize, Debug, Clone, Serialize)]
  @@ -412,8 +437,9 @@ where
  );
  let engine_state = Arc::clone(&self.engine_state);
  let metrics = Arc::clone(&self.metrics);
  - tokio::task::unconstrained(async move {
  - let result = execute_finalized_block(
  + tokio::task::spawn(async move {
  + let result = run_intensive_task(move || {
  + execute_finalized_block(
  engine_state.as_ref(),
  Some(metrics),
  protocol_version,
  @@ -421,7 +447,9 @@ where
  finalized_block,
  deploys,
  transfers,
  - );
  + )
  + })
  + .await;
  trace!(?result, "execute block response");
  responder.respond(result).await
  })
  @@ -607,7 +635,7 @@ impl ContractRuntime {
  block,
  execution_results,
  maybe_step_effect_and_upcoming_era_validators,
  - } = match tokio::task::unconstrained(async move {
  + } = match run_intensive_task(move || {
  execute_finalized_block(
  engine_state.as_ref(),
  Some(metrics),
  