diff --git a/core/Cargo.toml b/core/Cargo.toml
index be573e9e30..aff77b5cd4 100644
--- a/core/Cargo.toml
+++ b/core/Cargo.toml
@@ -13,6 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"]
 
 [dependencies]
 # Crates.io
+tokio = { workspace = true }
 async-trait = { workspace = true }
 array-bytes = { workspace = true }
 polkadot-primitives = { workspace = true }
@@ -64,7 +65,6 @@ similar-asserts = "1.5.0"
 assert_cmd = { workspace = true }
 regex = { workspace = true }
 tempfile = { workspace = true }
-tokio = { workspace = true }
 
 sc-service = { workspace = true }
 substrate-cli-test-utils = { workspace = true, features = ["try-runtime"] }
diff --git a/core/src/commands/fast_forward.rs b/core/src/commands/fast_forward.rs
index a418bda7db..27132ff65a 100644
--- a/core/src/commands/fast_forward.rs
+++ b/core/src/commands/fast_forward.rs
@@ -15,11 +15,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use std::{
-    fmt::Debug,
-    str::FromStr,
-    sync::{Arc, Mutex},
-};
+use std::{fmt::Debug, str::FromStr, sync::Arc, time::Duration};
 
 use parity_scale_codec::Encode;
 use sc_cli::Result;
@@ -27,6 +23,7 @@ use sc_executor::sp_wasm_interface::HostFunctions;
 use serde::de::DeserializeOwned;
 use sp_core::H256;
 use sp_runtime::traits::NumberFor;
+use tokio::sync::Mutex;
 
 use crate::{
     common::{
@@ -43,11 +40,9 @@ pub struct Command {
     #[arg(long)]
     pub n_blocks: u64,
 
-    /// Which inherent provider variant to use. In most cases "smart" should be used, which
-    /// attempts to support all chains.
-    #[arg(long, default_value = "smart")]
-    #[clap(value_enum)]
-    pub provider_variant: ProviderVariant,
+    /// The chain blocktime in milliseconds.
+    #[arg(long, default_value = "6000")]
+    pub blocktime: u64,
 
     /// Which try-state targets to execute when running this command.
     ///
@@ -108,6 +103,7 @@ where
     let inner_ext = Arc::new(Mutex::new(ext.inner_ext));
     let mut parent_header = ext.header.clone();
     let mut parent_block_building_info = None;
+    let provider_variant = ProviderVariant::Smart(Duration::from_millis(command.blocktime));
 
     for _ in 1..=command.n_blocks {
         let (next_block_building_info, next_header) = mine_block::<Block, HostFns>(
@@ -115,7 +111,7 @@ where
             &executor,
             parent_block_building_info,
             parent_header.clone(),
-            command.provider_variant,
+            provider_variant,
             command.try_state.clone(),
         )
         .await?;
diff --git a/core/src/common/empty_block/inherents/custom_idps/para_parachain.rs b/core/src/common/empty_block/inherents/custom_idps/para_parachain.rs
index 9e442a380c..8f4c7d1ff2 100644
--- a/core/src/common/empty_block/inherents/custom_idps/para_parachain.rs
+++ b/core/src/common/empty_block/inherents/custom_idps/para_parachain.rs
@@ -18,7 +18,7 @@
 //! Inherent data provider for the [cumulus parachin inherents](https://github.com/paritytech/polkadot-sdk/blob/master/cumulus/primitives/parachain-inherent/src/lib.rs)
 //! for empty block production on top of an existing externalities.
 
-use std::sync::{Arc, Mutex};
+use std::{ops::DerefMut, sync::Arc};
 
 use parity_scale_codec::{Decode, Encode};
 use polkadot_primitives::{BlockNumber, HeadData};
@@ -27,6 +27,7 @@ use sp_core::twox_128;
 use sp_inherents::InherentIdentifier;
 use sp_runtime::traits::{Block as BlockT, HashingFor, NumberFor};
 use sp_state_machine::TestExternalities;
+use tokio::sync::Mutex;
 
 /// Get the para id if it exists
 pub fn get_para_id<B: BlockT>(ext: &mut TestExternalities<HashingFor<B>>) -> Option<u32> {
@@ -61,7 +62,7 @@ pub struct InherentDataProvider<B: BlockT> {
     pub timestamp: sp_timestamp::Timestamp,
     pub blocktime_millis: u64,
     pub parent_header: B::Header,
-    pub ext: Arc<Mutex<TestExternalities<HashingFor<B>>>>,
+    pub ext_mutex: Arc<Mutex<TestExternalities<HashingFor<B>>>>,
 }
 
 #[async_trait::async_trait]
@@ -70,9 +71,10 @@ impl<B: BlockT> sp_inherents::InherentDataProvider for InherentDataProvider<B> {
         &self,
         inherent_data: &mut sp_inherents::InherentData,
     ) -> Result<(), sp_inherents::Error> {
-        let maybe_last_relay_chain_block_number =
-            get_last_relay_chain_block_number::<B>(&mut self.ext.lock().unwrap());
-        let maybe_para_id = get_para_id::<B>(&mut self.ext.lock().unwrap());
+        let mut ext_guard = self.ext_mutex.lock().await;
+        let ext = ext_guard.deref_mut();
+        let maybe_last_relay_chain_block_number = get_last_relay_chain_block_number::<B>(ext);
+        let maybe_para_id = get_para_id::<B>(ext);
         let (last_relay_chain_block_number, para_id) =
             match (maybe_last_relay_chain_block_number, maybe_para_id) {
                 (Some(last_relay_chain_block_number), Some(para_id)) => {
diff --git a/core/src/common/empty_block/inherents/providers.rs b/core/src/common/empty_block/inherents/providers.rs
index 7d99b9e284..b51a319018 100644
--- a/core/src/common/empty_block/inherents/providers.rs
+++ b/core/src/common/empty_block/inherents/providers.rs
@@ -17,12 +17,8 @@
 
 //! Contains providers for inherents required for empty block production.
 
-use std::{
-    sync::{Arc, Mutex},
-    time::Duration,
-};
+use std::{sync::Arc, time::Duration};
 
-use clap::ValueEnum;
 use parity_scale_codec::Encode;
 use sp_consensus_aura::{Slot, SlotDuration, AURA_ENGINE_ID};
 use sp_consensus_babe::{
@@ -37,6 +33,7 @@ use sp_runtime::{
 use sp_state_machine::TestExternalities;
 use sp_std::prelude::*;
 use strum_macros::{Display, EnumIter};
+use tokio::sync::Mutex;
 
 use crate::common::empty_block::inherents::custom_idps;
 
@@ -60,12 +57,13 @@ type InherentProviderResult<Err> =
 ///
 /// Currently only Smart is implemented. New implementations may be added if Smart is not suitable
 /// for some edge cases.
-#[derive(Debug, Clone, clap::Parser, EnumIter, Display, Copy, ValueEnum)]
-#[clap(rename_all = "snake_case")]
+#[derive(Debug, Clone, EnumIter, Display, Copy)]
 pub enum ProviderVariant {
     /// Smart chain varient will automatically adjust provided inherents based on the given
     /// externalities.
-    Smart,
+    ///
+    /// The blocktime is provided in milliseconds.
+    Smart(core::time::Duration),
 }
 
 impl<B: BlockT> InherentProvider<B> for ProviderVariant {
@@ -78,9 +76,9 @@ impl<B: BlockT> InherentProvider<B> for ProviderVariant {
         ext: Arc<Mutex<TestExternalities<HashingFor<B>>>>,
     ) -> InherentProviderResult<Self::Err> {
         match *self {
-            ProviderVariant::Smart => {
-                 <SmartInherentProvider as InherentProvider<B>>::get_inherent_providers_and_pre_digest(&SmartInherentProvider {
-                     blocktime: Duration::from_secs(6),
+            ProviderVariant::Smart(blocktime) => {
+                <SmartInherentProvider as InherentProvider<B>>::get_inherent_providers_and_pre_digest(&SmartInherentProvider {
+                     blocktime,
                  }, maybe_parent_info, parent_header, ext)
             }
         }
@@ -108,24 +106,22 @@ impl<B: BlockT> InherentProvider<B> for SmartInherentProvider {
         parent_header: B::Header,
         ext: Arc<Mutex<TestExternalities<HashingFor<B>>>>,
     ) -> InherentProviderResult<Self::Err> {
-        let blocktime_millis = self.blocktime.as_millis() as u64;
-
         let timestamp_idp = custom_idps::timestamp::InherentDataProvider {
-            blocktime_millis,
+            blocktime_millis: self.blocktime.as_millis() as u64,
             maybe_parent_info,
         };
         let para_parachain_idp = custom_idps::para_parachain::InherentDataProvider::<B> {
-            blocktime_millis,
+            blocktime_millis: self.blocktime.as_millis() as u64,
             parent_header: parent_header.clone(),
             timestamp: timestamp_idp.timestamp(),
-            ext,
+            ext_mutex: ext,
         };
         let relay_parachain_data_idp =
             custom_idps::relay_parachains::InherentDataProvider::<B>::new(parent_header);
 
         let slot = Slot::from_timestamp(
             timestamp_idp.timestamp(),
-            SlotDuration::from_millis(blocktime_millis),
+            SlotDuration::from_millis(self.blocktime.as_millis() as u64),
         );
         let digest = vec![
             DigestItem::PreRuntime(
diff --git a/core/src/common/empty_block/production.rs b/core/src/common/empty_block/production.rs
index 9fb7346737..e3ea66389b 100644
--- a/core/src/common/empty_block/production.rs
+++ b/core/src/common/empty_block/production.rs
@@ -1,8 +1,4 @@
-use std::{
-    ops::DerefMut,
-    str::FromStr,
-    sync::{Arc, Mutex},
-};
+use std::{ops::DerefMut, str::FromStr, sync::Arc};
 
 use parity_scale_codec::{Decode, Encode};
 use sc_cli::Result;
@@ -15,6 +11,7 @@ use sp_runtime::{
 };
 use sp_state_machine::TestExternalities;
 use sp_std::fmt::Debug;
+use tokio::sync::Mutex;
 
 use super::inherents::{pre_apply::pre_apply_inherents, providers::InherentProvider};
 use crate::{
@@ -23,7 +20,7 @@ use crate::{
 };
 
 pub async fn mine_block<Block: BlockT, HostFns: HostFunctions>(
-    ext: Arc<Mutex<TestExternalities<HashingFor<Block>>>>,
+    ext_mutex: Arc<Mutex<TestExternalities<HashingFor<Block>>>>,
     executor: &WasmExecutor<HostFns>,
     previous_block_building_info: Option<(InherentData, Digest)>,
     parent_header: Block::Header,
@@ -38,7 +35,10 @@ where
     <NumberFor<Block> as FromStr>::Err: Debug,
 {
     // We are saving state before we overwrite it while producing new block.
-    let backend = ext.lock().unwrap().deref_mut().as_backend();
+    let mut ext_guard = ext_mutex.lock().await;
+    let ext = ext_guard.deref_mut();
+    let backend = ext.as_backend();
+    drop(ext_guard);
 
     log::info!(
         "Producing new empty block at height {:?}",
@@ -46,7 +46,7 @@ where
     );
 
     let (next_block, new_block_building_info) = produce_next_block::<Block, HostFns>(
-        ext.clone(),
+        ext_mutex.clone(),
         executor,
         parent_header.clone(),
         provider_variant,
@@ -59,10 +59,13 @@ where
         array_bytes::bytes2hex("0x", next_block.header().hash())
     );
 
+    let mut ext_guard = ext_mutex.lock().await;
+    let ext = ext_guard.deref_mut();
+
     // And now we restore previous state.
-    ext.lock().unwrap().deref_mut().backend = backend;
+    ext.backend = backend;
 
-    pre_apply_inherents::<Block>(ext.lock().unwrap().deref_mut());
+    pre_apply_inherents::<Block>(ext);
     let state_root_check = true;
     let signature_check = true;
     let payload = (
@@ -72,13 +75,7 @@ where
         try_state,
     )
         .encode();
-    call::<Block, _>(
-        ext.lock().unwrap().deref_mut(),
-        executor,
-        "TryRuntime_execute_block",
-        &payload,
-    )
-    .await?;
+    call::<Block, _>(ext, executor, "TryRuntime_execute_block", &payload).await?;
 
     log::info!("Executed the new block");
 
@@ -87,7 +84,7 @@ where
 
 /// Produces next block containing only inherents.
 pub async fn produce_next_block<Block: BlockT, HostFns: HostFunctions>(
-    externalities: Arc<Mutex<TestExternalities<HashingFor<Block>>>>,
+    ext_mutex: Arc<Mutex<TestExternalities<HashingFor<Block>>>>,
     executor: &WasmExecutor<HostFns>,
     parent_header: Block::Header,
     chain: ProviderVariant,
@@ -105,10 +102,14 @@ where
             &chain,
             previous_block_building_info,
             parent_header.clone(),
-            externalities.clone(),
+            ext_mutex.clone(),
         )?;
 
-    pre_apply_inherents::<Block>(externalities.clone().lock().unwrap().deref_mut());
+    let mut ext_guard = ext_mutex.lock().await;
+    let ext = ext_guard.deref_mut();
+
+    pre_apply_inherents::<Block>(ext);
+    drop(ext_guard);
     let inherent_data = inherent_data_provider
         .create_inherent_data()
         .await
@@ -123,45 +124,31 @@ where
         digest.clone(),
     );
 
-    call::<Block, _>(
-        externalities.lock().unwrap().deref_mut(),
-        executor,
-        "Core_initialize_block",
-        &header.encode(),
-    )
-    .await?;
+    let mut ext_guard = ext_mutex.lock().await;
+    let ext = ext_guard.deref_mut();
+    call::<Block, _>(ext, executor, "Core_initialize_block", &header.encode()).await?;
 
     let extrinsics = dry_call::<Vec<Block::Extrinsic>, Block, _>(
-        externalities.lock().unwrap().deref_mut(),
+        ext,
         executor,
         "BlockBuilder_inherent_extrinsics",
         &inherent_data.encode(),
     )?;
 
     for xt in &extrinsics {
-        call::<Block, _>(
-            externalities.lock().unwrap().deref_mut(),
-            executor,
-            "BlockBuilder_apply_extrinsic",
-            &xt.encode(),
-        )
-        .await?;
+        call::<Block, _>(ext, executor, "BlockBuilder_apply_extrinsic", &xt.encode()).await?;
     }
 
     let header = dry_call::<Block::Header, Block, _>(
-        externalities.lock().unwrap().deref_mut(),
+        ext,
         executor,
         "BlockBuilder_finalize_block",
         &[0u8; 0],
     )?;
 
-    call::<Block, _>(
-        externalities.lock().unwrap().deref_mut(),
-        executor,
-        "BlockBuilder_finalize_block",
-        &[0u8; 0],
-    )
-    .await?;
+    call::<Block, _>(ext, executor, "BlockBuilder_finalize_block", &[0u8; 0]).await?;
+
+    drop(ext_guard);
 
     Ok((Block::new(header, extrinsics), (inherent_data, digest)))
 }