diff --git a/crates/cli-flags/src/lib.rs b/crates/cli-flags/src/lib.rs index 6cc61e6cfb07..aad803fe729c 100644 --- a/crates/cli-flags/src/lib.rs +++ b/crates/cli-flags/src/lib.rs @@ -412,6 +412,9 @@ wasmtime_option_group! { /// Component model support for fixed-length lists: this corresponds /// to the 🔧 emoji in the component model specification pub component_model_fixed_length_lists: Option, + /// Whether or not any concurrency infrastructure in Wasmtime is + /// enabled or not. + pub concurrency_support: Option, } enum Wasm { @@ -1006,6 +1009,10 @@ impl CommonOptions { config.gc_support(enable); } + if let Some(enable) = self.wasm.concurrency_support { + config.concurrency_support(enable); + } + if let Some(enable) = self.wasm.shared_memory { config.shared_memory(enable); } diff --git a/crates/cranelift/src/compiler/component.rs b/crates/cranelift/src/compiler/component.rs index 6a6c5b8224a7..93795cd6da89 100644 --- a/crates/cranelift/src/compiler/component.rs +++ b/crates/cranelift/src/compiler/component.rs @@ -1220,7 +1220,7 @@ impl<'a> TrampolineCompiler<'a> { .ins() .trapz(masked, TRAP_CANNOT_LEAVE_COMPONENT); - if self.compiler.tunables.component_model_concurrency { + if self.compiler.tunables.concurrency_support { // Stash the old value of `may_block` and then set it to false. let old_may_block = self.builder.ins().load( ir::types::I32, diff --git a/crates/environ/src/fact/trampoline.rs b/crates/environ/src/fact/trampoline.rs index 160949ae06b8..79cf0a49eb10 100644 --- a/crates/environ/src/fact/trampoline.rs +++ b/crates/environ/src/fact/trampoline.rs @@ -180,7 +180,7 @@ pub(super) fn compile(module: &mut Module<'_>, adapter: &AdapterData) { compiler.compile_sync_to_sync_adapter(adapter, &lower_sig, &lift_sig) } (true, true) => { - assert!(module.tunables.component_model_concurrency); + assert!(module.tunables.concurrency_support); // In the async->async case, we must compile a couple of helper functions: // @@ -209,7 +209,7 @@ pub(super) fn compile(module: &mut Module<'_>, adapter: &AdapterData) { ); } (false, true) => { - assert!(module.tunables.component_model_concurrency); + assert!(module.tunables.concurrency_support); // Like the async->async case above, for the sync->async case we // also need `async-start` and `async-return` helper functions to @@ -235,7 +235,7 @@ pub(super) fn compile(module: &mut Module<'_>, adapter: &AdapterData) { ); } (true, false) => { - assert!(module.tunables.component_model_concurrency); + assert!(module.tunables.concurrency_support); // As with the async->async and sync->async cases above, for the // async->sync case we use `async-start` and `async-return` helper @@ -759,7 +759,7 @@ impl<'a, 'b> Compiler<'a, 'b> { Trap::CannotLeaveComponent, ); - let old_task_may_block = if self.module.tunables.component_model_concurrency { + let old_task_may_block = if self.module.tunables.concurrency_support { // Save, clear, and later restore the `may_block` field. let task_may_block = self.module.import_task_may_block(); let old_task_may_block = if self.types[adapter.lift.ty].async_ { @@ -871,7 +871,7 @@ impl<'a, 'b> Compiler<'a, 'b> { self.instruction(Call(exit.as_u32())); } - if self.module.tunables.component_model_concurrency { + if self.module.tunables.concurrency_support { // Pop the task we pushed earlier off of the current task stack. // // FIXME: Apply the optimizations described in #12311. diff --git a/crates/environ/src/tunables.rs b/crates/environ/src/tunables.rs index 5a958a313a8b..fc9b4c3ce18a 100644 --- a/crates/environ/src/tunables.rs +++ b/crates/environ/src/tunables.rs @@ -143,7 +143,7 @@ define_tunables! { /// Whether any component model feature related to concurrency is /// enabled. - pub component_model_concurrency: bool, + pub concurrency_support: bool, } pub struct ConfigTunables { @@ -219,7 +219,7 @@ impl Tunables { inlining_small_callee_size: 50, inlining_sum_size_threshold: 2000, debug_guest: false, - component_model_concurrency: true, + concurrency_support: true, } } diff --git a/crates/wasmtime/src/config.rs b/crates/wasmtime/src/config.rs index d500ed9c372f..45d01e007e51 100644 --- a/crates/wasmtime/src/config.rs +++ b/crates/wasmtime/src/config.rs @@ -2358,6 +2358,10 @@ impl Config { let mut tunables = Tunables::default_for_target(&self.compiler_target())?; + // By default this is enabled with the Cargo feature, and if the feature + // is missing this is disabled. + tunables.concurrency_support = cfg!(feature = "component-model-async"); + // If no target is explicitly specified then further refine `tunables` // for the configuration of this host depending on what platform // features were found available at compile time. This means that anyone @@ -2430,9 +2434,23 @@ impl Config { ); } - #[cfg(feature = "component-model")] - { - tunables.component_model_concurrency = self.cm_concurrency_enabled(); + // Concurrency support is required for some component model features. + let requires_concurrency = WasmFeatures::CM_ASYNC + | WasmFeatures::CM_ASYNC_BUILTINS + | WasmFeatures::CM_ASYNC_STACKFUL + | WasmFeatures::CM_THREADING + | WasmFeatures::CM_ERROR_CONTEXT; + if tunables.concurrency_support && !cfg!(feature = "component-model-async") { + bail!( + "concurrency support was requested but was not \ + compiled into this build of Wasmtime" + ) + } + if !tunables.concurrency_support && features.intersects(requires_concurrency) { + bail!( + "concurrency support must be enabled to use the component \ + model async or threading features" + ) } Ok((tunables, features)) @@ -2923,17 +2941,41 @@ impl Config { self } - #[cfg(feature = "component-model")] - #[inline] - pub(crate) fn cm_concurrency_enabled(&self) -> bool { - cfg!(feature = "component-model-async") - && self.enabled_features.intersects( - WasmFeatures::CM_ASYNC - | WasmFeatures::CM_ASYNC_BUILTINS - | WasmFeatures::CM_ASYNC_STACKFUL - | WasmFeatures::CM_THREADING - | WasmFeatures::CM_ERROR_CONTEXT, - ) + /// Specifies whether support for concurrent execution of WebAssembly is + /// supported within this store. + /// + /// This configuration option affects whether runtime data structures are + /// initialized within a `Store` on creation to support concurrent execution + /// of WebAssembly guests. This is primarily applicable to the + /// [`Config::wasm_component_model_async`] configuration which is the first + /// time Wasmtime has supported concurrent execution of guests. This + /// configuration option, for example, enables usage of + /// [`Store::run_concurrent`], [`Func::call_concurrent`], [`StreamReader`], + /// etc. + /// + /// This configuration option can be manually disabled to avoid initializing + /// data structures in the [`Store`] related to concurrent execution. When + /// this option is disabled then APIs related to concurrency will all fail + /// with a panic. For example [`Store::run_concurrent`] will panic, creating + /// a [`StreamReader`] will panic, etc. + /// + /// The value of this option additionally affects whether a [`Config`] is + /// valid and the default set of enabled WebAssembly features. If this + /// option is disabled then component-model features related to concurrency + /// will all be disabled. If this option is enabled, then the options will + /// retain their normal defaults. It is not valid to create a [`Config`] + /// with component-model-async explicitly enabled and this option explicitly + /// disabled, however. + /// + /// This option defaults to `true`. + /// + /// [`Store`]: crate::Store + /// [`Store::run_concurrent`]: crate::Store::run_concurrent + /// [`Func::call_concurrent`]: crate::component::Func::call_concurrent + /// [`StreamReader`]: crate::component::StreamReader + pub fn concurrency_support(&mut self, enable: bool) -> &mut Self { + self.tunables.concurrency_support = Some(enable); + self } } diff --git a/crates/wasmtime/src/engine/serialization.rs b/crates/wasmtime/src/engine/serialization.rs index 190183971248..c014cb86116b 100644 --- a/crates/wasmtime/src/engine/serialization.rs +++ b/crates/wasmtime/src/engine/serialization.rs @@ -285,7 +285,7 @@ impl Metadata<'_> { inlining_intra_module, inlining_small_callee_size, inlining_sum_size_threshold, - component_model_concurrency, + concurrency_support, // This doesn't affect compilation, it's just a runtime setting. memory_reservation_for_growth: _, @@ -367,9 +367,9 @@ impl Metadata<'_> { "function inlining sum-size threshold", )?; Self::check_bool( - component_model_concurrency, - other.component_model_concurrency, - "component model concurrency", + concurrency_support, + other.concurrency_support, + "concurrency support", )?; Self::check_intra_module_inlining(inlining_intra_module, other.inlining_intra_module)?; diff --git a/crates/wasmtime/src/runtime/component/concurrent.rs b/crates/wasmtime/src/runtime/component/concurrent.rs index fcff9d596511..395b967d3736 100644 --- a/crates/wasmtime/src/runtime/component/concurrent.rs +++ b/crates/wasmtime/src/runtime/component/concurrent.rs @@ -886,8 +886,8 @@ impl Store { T: Send + 'static, { ensure!( - self.as_context().0.cm_concurrency_enabled(), - "cannot use `run_concurrent` without enabling component-model async" + self.as_context().0.concurrency_support(), + "cannot use `run_concurrent` when Config::concurrency_support disabled", ); self.as_context_mut().run_concurrent(fun).await } @@ -983,6 +983,11 @@ impl StoreContextMut<'_, T> { /// This function can be used to invoke [`Func::call_concurrent`] for /// example within the async closure provided here. /// + /// This function will unconditionally return an error if + /// [`Config::concurrency_support`] is disabled. + /// + /// [`Config::concurrency_support`]: crate::Config::concurrency_support + /// /// # Store-blocking behavior /// /// At this time there are certain situations in which the `Future` returned @@ -1056,8 +1061,8 @@ impl StoreContextMut<'_, T> { T: Send + 'static, { ensure!( - self.0.cm_concurrency_enabled(), - "cannot use `run_concurrent` without enabling component-model async" + self.0.concurrency_support(), + "cannot use `run_concurrent` when Config::concurrency_support disabled", ); self.do_run_concurrent(fun, false).await } @@ -1080,7 +1085,7 @@ impl StoreContextMut<'_, T> { where T: Send + 'static, { - debug_assert!(self.0.cm_concurrency_enabled()); + debug_assert!(self.0.concurrency_support()); check_recursive_run(); let token = StoreToken::new(self.as_context_mut()); @@ -1513,8 +1518,10 @@ impl StoreOpaque { /// - The top-level instance is not already on the current task's call stack. /// - The instance is not in need of a post-return function call. /// - `self` has not been poisoned due to a trap. - pub(crate) fn may_enter_concurrent(&mut self, instance: RuntimeInstance) -> bool { - debug_assert!(self.cm_concurrency_enabled()); + pub(crate) fn may_enter(&mut self, instance: RuntimeInstance) -> bool { + if !self.concurrency_support() { + return self.may_enter_at_all(instance); + } let state = self.concurrent_state_mut(); if let Some(caller) = state.guest_thread { instance != state.get_mut(caller.task).unwrap().instance @@ -1524,23 +1531,6 @@ impl StoreOpaque { } } - /// Returns `false` if the specified instance may not be entered, regardless - /// of what's on a task's call stack. - /// - /// If this returns `true`, the instance may be entered as long as it isn't - /// on the task's call stack, if applicable. - fn may_enter_at_all(&self, instance: RuntimeInstance) -> bool { - if self.trapped() { - return false; - } - - let flags = self - .component_instance(instance.instance) - .instance_flags(instance.index); - - unsafe { !flags.needs_post_return() } - } - /// Variation of `may_enter` which takes a `TableId` representing /// the callee. fn may_enter_task(&mut self, task: TableId) -> bool { @@ -1641,8 +1631,10 @@ impl StoreOpaque { .set_task_may_block(may_block) } - pub(crate) fn check_blocking_concurrent(&mut self) -> Result<()> { - debug_assert!(self.cm_concurrency_enabled()); + pub(crate) fn check_blocking(&mut self) -> Result<()> { + if !self.concurrency_support() { + return Ok(()); + } let state = self.concurrent_state_mut(); let task = state.guest_thread.unwrap().task; let instance = state.get_mut(task).unwrap().instance.instance; diff --git a/crates/wasmtime/src/runtime/component/concurrent/futures_and_streams.rs b/crates/wasmtime/src/runtime/component/concurrent/futures_and_streams.rs index a352e89f805a..82c7ab33703b 100644 --- a/crates/wasmtime/src/runtime/component/concurrent/futures_and_streams.rs +++ b/crates/wasmtime/src/runtime/component/concurrent/futures_and_streams.rs @@ -1116,6 +1116,12 @@ pub struct FutureReader { impl FutureReader { /// Create a new future with the specified producer. + /// + /// # Panics + /// + /// Panics if [`Config::concurrency_support`] is not enabled. + /// + /// [`Config::concurrency_support`]: crate::Config::concurrency_support pub fn new( mut store: S, producer: impl FutureProducer, @@ -1123,7 +1129,7 @@ impl FutureReader { where T: func::Lower + func::Lift + Send + Sync + 'static, { - assert!(store.as_context().0.cm_concurrency_enabled()); + assert!(store.as_context().0.concurrency_support()); struct Producer

(P); @@ -1451,11 +1457,17 @@ where A: AsAccessor, { /// Create a new `GuardedFutureReader` with the specified `accessor` and `reader`. + /// + /// # Panics + /// + /// Panics if [`Config::concurrency_support`] is not enabled. + /// + /// [`Config::concurrency_support`]: crate::Config::concurrency_support pub fn new(accessor: A, reader: FutureReader) -> Self { assert!( accessor .as_accessor() - .with(|a| a.as_context().0.cm_concurrency_enabled()) + .with(|a| a.as_context().0.concurrency_support()) ); Self { reader: Some(reader), @@ -1503,6 +1515,12 @@ pub struct StreamReader { impl StreamReader { /// Create a new stream with the specified producer. + /// + /// # Panics + /// + /// Panics if [`Config::concurrency_support`] is not enabled. + /// + /// [`Config::concurrency_support`]: crate::Config::concurrency_support pub fn new( mut store: S, producer: impl StreamProducer, @@ -1510,7 +1528,7 @@ impl StreamReader { where T: func::Lower + func::Lift + Send + Sync + 'static, { - assert!(store.as_context().0.cm_concurrency_enabled()); + assert!(store.as_context().0.concurrency_support()); Self::new_( store .as_context_mut() @@ -1785,11 +1803,17 @@ where { /// Create a new `GuardedStreamReader` with the specified `accessor` and /// `reader`. + /// + /// # Panics + /// + /// Panics if [`Config::concurrency_support`] is not enabled. + /// + /// [`Config::concurrency_support`]: crate::Config::concurrency_support pub fn new(accessor: A, reader: StreamReader) -> Self { assert!( accessor .as_accessor() - .with(|a| a.as_context().0.cm_concurrency_enabled()) + .with(|a| a.as_context().0.concurrency_support()) ); Self { reader: Some(reader), diff --git a/crates/wasmtime/src/runtime/component/concurrent_disabled.rs b/crates/wasmtime/src/runtime/component/concurrent_disabled.rs index fa8ea16708f1..5bfd79f762f6 100644 --- a/crates/wasmtime/src/runtime/component/concurrent_disabled.rs +++ b/crates/wasmtime/src/runtime/component/concurrent_disabled.rs @@ -7,8 +7,7 @@ use core::convert::Infallible; use core::mem::MaybeUninit; use wasmtime_environ::component::{CanonicalAbiInfo, InterfaceType}; -#[derive(Default)] -pub struct ConcurrentState; +pub enum ConcurrentState {} fn should_have_failed_validation(what: &str) -> Result { // This should be unreachable; if we trap here, it indicates a @@ -168,4 +167,12 @@ impl StoreOpaque { // See comment in `enter_sync_call` unreachable!() } + + pub(crate) fn check_blocking(&mut self) -> crate::Result<()> { + Ok(()) + } + + pub(crate) fn may_enter(&mut self, instance: RuntimeInstance) -> bool { + self.may_enter_at_all(instance) + } } diff --git a/crates/wasmtime/src/runtime/component/func.rs b/crates/wasmtime/src/runtime/component/func.rs index e56d933ab000..4bfad04bbc25 100644 --- a/crates/wasmtime/src/runtime/component/func.rs +++ b/crates/wasmtime/src/runtime/component/func.rs @@ -258,7 +258,7 @@ impl Func { let store = store.as_context_mut(); #[cfg(feature = "component-model-async")] - if store.0.cm_concurrency_enabled() { + if store.0.concurrency_support() { return store .run_concurrent_trap_on_idle(async |store| { self.call_concurrent_dynamic(store, params, results, false) @@ -353,6 +353,10 @@ impl Func { /// but the task will still progress and invoke callbacks and such until /// completion. /// + /// This function will return an error if [`Config::concurrency_support`] is + /// disabled. + /// + /// [`Config::concurrency_support`]: crate::Config::concurrency_support /// [`run_concurrent`]: crate::Store::run_concurrent /// [#11833]: https://github.com/bytecodealliance/wasmtime/issues/11833 /// [`Accessor`]: crate::component::Accessor @@ -610,7 +614,7 @@ impl Func { bail!(crate::Trap::CannotEnterComponent); } - if store.engine().config().cm_concurrency_enabled() { + if store.0.concurrency_support() { let async_type = self.abi_async(store.0); store.0.enter_sync_call(None, async_type, instance)?; } @@ -812,7 +816,7 @@ impl Func { } .exit_call()?; - if !async_ && store.engine().config().cm_concurrency_enabled() { + if !async_ && store.0.concurrency_support() { store.0.exit_sync_call(false)?; } } diff --git a/crates/wasmtime/src/runtime/component/func/typed.rs b/crates/wasmtime/src/runtime/component/func/typed.rs index ed9b8a703a76..bc81692c38db 100644 --- a/crates/wasmtime/src/runtime/component/func/typed.rs +++ b/crates/wasmtime/src/runtime/component/func/typed.rs @@ -181,7 +181,7 @@ where let mut store = store.as_context_mut(); #[cfg(feature = "component-model-async")] - if store.0.cm_concurrency_enabled() { + if store.0.concurrency_support() { use crate::component::concurrent::TaskId; use crate::runtime::vm::SendSyncPtr; use core::ptr::NonNull; @@ -258,6 +258,11 @@ where /// representing the completion of the guest task and any transitive /// subtasks it might create. /// + /// This function will return an error if [`Config::concurrency_support`] is + /// disabled. + /// + /// [`Config::concurrency_support`]: crate::Config::concurrency_support + /// /// # Progress and Cancellation /// /// For more information about how to make progress on the wasm task or how @@ -313,7 +318,10 @@ where { let result = accessor.as_accessor().with(|mut store| { let mut store = store.as_context_mut(); - assert!(store.0.cm_concurrency_enabled()); + ensure!( + store.0.concurrency_support(), + "cannot use `call_concurrent` Config::concurrency_support disabled", + ); let prepared = self.prepare_call(store.as_context_mut(), false, true, move |cx, ty, dst| { @@ -368,7 +376,7 @@ where Return: 'static, { use crate::component::storage::slice_to_storage; - debug_assert!(store.0.cm_concurrency_enabled()); + debug_assert!(store.0.concurrency_support()); let param_count = if Params::flatten_count() <= MAX_FLAT_PARAMS { Params::flatten_count() diff --git a/crates/wasmtime/src/runtime/component/instance.rs b/crates/wasmtime/src/runtime/component/instance.rs index 887313e78802..271a4d2f5632 100644 --- a/crates/wasmtime/src/runtime/component/instance.rs +++ b/crates/wasmtime/src/runtime/component/instance.rs @@ -869,10 +869,9 @@ impl<'a> Instantiator<'a> { } }; - let exit = if let (&Some(component_instance), true) = ( - component_instance, - store.engine().config().cm_concurrency_enabled(), - ) { + let exit = if let Some(component_instance) = *component_instance + && store.0.concurrency_support() + { store.0.enter_sync_call( None, false, diff --git a/crates/wasmtime/src/runtime/component/linker.rs b/crates/wasmtime/src/runtime/component/linker.rs index 5ef3820d7195..9e3cf42c2f06 100644 --- a/crates/wasmtime/src/runtime/component/linker.rs +++ b/crates/wasmtime/src/runtime/component/linker.rs @@ -531,6 +531,9 @@ impl LinkerInstance<'_, T> { Params: ComponentNamedList + Lift + 'static, Return: ComponentNamedList + Lower + 'static, { + if !self.engine.tunables().concurrency_support { + bail!("concurrent host functions require `Config::concurrency_support`"); + } self.insert(name, Definition::Func(HostFunc::func_wrap_concurrent(f)))?; Ok(()) } @@ -690,6 +693,9 @@ impl LinkerInstance<'_, T> { + Sync + 'static, { + if !self.engine.tunables().concurrency_support { + bail!("concurrent host functions require `Config::concurrency_support`"); + } self.insert(name, Definition::Func(HostFunc::func_new_concurrent(f)))?; Ok(()) } @@ -768,6 +774,9 @@ impl LinkerInstance<'_, T> { + Sync + 'static, { + if !self.engine.tunables().concurrency_support { + bail!("concurrent host functions require `Config::concurrency_support`"); + } // TODO: This isn't really concurrent -- it requires exclusive access to // the store for the duration of the call, preventing guest code from // running until it completes. We should make it concurrent and clean diff --git a/crates/wasmtime/src/runtime/component/mod.rs b/crates/wasmtime/src/runtime/component/mod.rs index b06907125b2c..16d93b12ee28 100644 --- a/crates/wasmtime/src/runtime/component/mod.rs +++ b/crates/wasmtime/src/runtime/component/mod.rs @@ -764,38 +764,3 @@ pub(crate) mod concurrent_disabled; #[cfg(not(feature = "component-model-async"))] pub(crate) use concurrent_disabled as concurrent; - -impl crate::runtime::store::StoreOpaque { - #[cfg(feature = "component-model-async")] - pub(crate) fn cm_concurrency_enabled(&self) -> bool { - let enabled = self.concurrent_state().is_some(); - debug_assert_eq!(enabled, self.engine().config().cm_concurrency_enabled()); - enabled - } - - pub(crate) fn check_blocking(&mut self) -> crate::Result<()> { - #[cfg(feature = "component-model-async")] - if self.cm_concurrency_enabled() { - return self.check_blocking_concurrent(); - } - - Ok(()) - } - - pub(crate) fn may_enter(&mut self, instance: RuntimeInstance) -> bool { - #[cfg(feature = "component-model-async")] - if self.cm_concurrency_enabled() { - return self.may_enter_concurrent(instance); - } - - if self.trapped() { - return false; - } - - let flags = self - .component_instance(instance.instance) - .instance_flags(instance.index); - - unsafe { !flags.needs_post_return() } - } -} diff --git a/crates/wasmtime/src/runtime/component/resources/any.rs b/crates/wasmtime/src/runtime/component/resources/any.rs index 78c417b6728f..27cb760c9e40 100644 --- a/crates/wasmtime/src/runtime/component/resources/any.rs +++ b/crates/wasmtime/src/runtime/component/resources/any.rs @@ -197,10 +197,9 @@ impl ResourceAny { }; let mut args = [ValRaw::u32(rep)]; - let exit = if let (Some(instance), true) = ( - slot.instance, - store.engine().config().cm_concurrency_enabled(), - ) { + let exit = if let Some(instance) = slot.instance + && store.0.concurrency_support() + { store.0.enter_sync_call(None, false, instance)?; true } else { diff --git a/crates/wasmtime/src/runtime/component/store.rs b/crates/wasmtime/src/runtime/component/store.rs index 0b2d571aa52d..9402c209e144 100644 --- a/crates/wasmtime/src/runtime/component/store.rs +++ b/crates/wasmtime/src/runtime/component/store.rs @@ -34,6 +34,23 @@ impl StoreOpaque { pub(crate) fn set_trapped(&mut self) { self.store_data_mut().components.trapped = true; } + + /// Returns `false` if the specified instance may not be entered, regardless + /// of what's on a task's call stack. + /// + /// If this returns `true`, the instance may be entered as long as it isn't + /// on the task's call stack, if applicable. + pub(crate) fn may_enter_at_all(&mut self, instance: RuntimeInstance) -> bool { + if self.trapped() { + return false; + } + + let flags = self + .component_instance(instance.instance) + .instance_flags(instance.index); + + unsafe { !flags.needs_post_return() } + } } impl StoreData { diff --git a/crates/wasmtime/src/runtime/store.rs b/crates/wasmtime/src/runtime/store.rs index 4671bc8f7bee..307459b68130 100644 --- a/crates/wasmtime/src/runtime/store.rs +++ b/crates/wasmtime/src/runtime/store.rs @@ -777,8 +777,15 @@ impl Store { host_resource_data: Default::default(), executor: Executor::new(engine), #[cfg(feature = "component-model")] - concurrent_state: if engine.config().cm_concurrency_enabled() { - Some(Default::default()) + concurrent_state: if engine.tunables().concurrency_support { + #[cfg(feature = "component-model-async")] + { + Some(Default::default()) + } + #[cfg(not(feature = "component-model-async"))] + { + unreachable!() + } } else { None }, @@ -2603,17 +2610,20 @@ at https://bytecodealliance.org/security. &mut self.async_state } - #[cfg(feature = "component-model-async")] - pub(crate) fn concurrent_state(&self) -> Option<&concurrent::ConcurrentState> { - self.concurrent_state.as_ref() - } - #[cfg(feature = "component-model-async")] pub(crate) fn concurrent_state_mut(&mut self) -> &mut concurrent::ConcurrentState { - debug_assert!(self.engine().config().cm_concurrency_enabled()); + debug_assert!(self.concurrency_support()); self.concurrent_state.as_mut().unwrap() } + #[inline] + #[cfg(feature = "component-model")] + pub(crate) fn concurrency_support(&self) -> bool { + let support = self.concurrent_state.is_some(); + debug_assert_eq!(support, self.engine().tunables().concurrency_support); + support + } + #[cfg(feature = "async")] pub(crate) fn has_pkey(&self) -> bool { self.pkey.is_some() diff --git a/src/commands/run.rs b/src/commands/run.rs index edcca310fffd..e3ce0e9a0abe 100644 --- a/src/commands/run.rs +++ b/src/commands/run.rs @@ -633,7 +633,7 @@ impl RunCommand { results: &mut Vec, ) -> Result<(), Error> { #[cfg(feature = "component-model-async")] - if self.run.common.wasm.component_model_async.unwrap_or(false) { + if self.run.common.wasm.concurrency_support.unwrap_or(true) { store .run_concurrent(async |store| { let task = func.call_concurrent(store, params, results).await?; diff --git a/src/commands/serve.rs b/src/commands/serve.rs index 4d7ca7ff5985..4b87cb893081 100644 --- a/src/commands/serve.rs +++ b/src/commands/serve.rs @@ -403,7 +403,6 @@ impl ServeCommand { .common .config(use_pooling_allocator_by_default().unwrap_or(None))?; config.wasm_component_model(true); - config.wasm_component_model_async(true); if self.run.common.wasm.timeout.is_some() { config.epoch_interruption(true); diff --git a/tests/all/component_model/async.rs b/tests/all/component_model/async.rs index da6cde8b4c7d..7e71ccd38eab 100644 --- a/tests/all/component_model/async.rs +++ b/tests/all/component_model/async.rs @@ -832,3 +832,48 @@ async fn run_wasm_in_call_async() -> Result<()> { run.call_async(&mut store, ()).await?; Ok(()) } + +#[tokio::test] +#[cfg_attr(miri, ignore)] +async fn require_concurrency_support() -> Result<()> { + let mut config = Config::new(); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + + let mut store = Store::new(&engine, ()); + + assert!( + store + .run_concurrent(async |_| wasmtime::error::Ok(())) + .await + .is_err() + ); + + let ok = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + StreamReader::::new(&mut store, Vec::new()); + })); + assert!(ok.is_err()); + + let ok = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + FutureReader::new(&mut store, async { wasmtime::error::Ok(0) }) + })); + assert!(ok.is_err()); + + let mut linker = Linker::<()>::new(&engine); + let mut root = linker.root(); + + assert!( + root.func_wrap_concurrent::<(), (), _>("f1", |_, _| { todo!() }) + .is_err() + ); + assert!( + root.func_new_concurrent("f2", |_, _, _, _| { todo!() }) + .is_err() + ); + assert!( + root.resource_concurrent("f3", ResourceType::host::(), |_, _| { todo!() }) + .is_err() + ); + + Ok(()) +} diff --git a/tests/all/component_model/resources.rs b/tests/all/component_model/resources.rs index 821553fca49b..f40ab07f2526 100644 --- a/tests/all/component_model/resources.rs +++ b/tests/all/component_model/resources.rs @@ -233,7 +233,7 @@ fn mismatch_intrinsics() -> Result<()> { let ctor = i.get_typed_func::<(u32,), (ResourceAny,)>(&mut store, "ctor")?; assert_eq!( ctor.call(&mut store, (100,)).unwrap_err().to_string(), - "handle index 1 used with the wrong type, expected guest-defined \ + "handle index 2 used with the wrong type, expected guest-defined \ resource but found a different guest-defined resource", ); @@ -1411,8 +1411,8 @@ fn guest_different_host_same() -> Result<()> { (func (export "f") (param i32 i32) ;; different types, but everything goes into the same ;; handle index namespace - (if (i32.ne (local.get 0) (i32.const 1)) (then (unreachable))) - (if (i32.ne (local.get 1) (i32.const 2)) (then (unreachable))) + (if (i32.ne (local.get 0) (i32.const 2)) (then (unreachable))) + (if (i32.ne (local.get 1) (i32.const 3)) (then (unreachable))) ;; host should end up getting the same resource (call $f (local.get 0) (local.get 1)) diff --git a/tests/disas/component-model/direct-adapter-calls-inlining.wat b/tests/disas/component-model/direct-adapter-calls-inlining.wat index 82c1aae8b16f..41bfa66c1d02 100644 --- a/tests/disas/component-model/direct-adapter-calls-inlining.wat +++ b/tests/disas/component-model/direct-adapter-calls-inlining.wat @@ -1,7 +1,7 @@ ;;! target = "x86_64" ;;! test = "optimize" ;;! filter = "wasm[1]--function" -;;! flags = "-C inlining=y" +;;! flags = "-C inlining=y -Wconcurrency-support=n" ;; Same as `direct-adapter-calls.wat`, except we have enabled function inlining ;; so all the direct calls should get inlined. diff --git a/tests/disas/component-model/direct-adapter-calls-x64.wat b/tests/disas/component-model/direct-adapter-calls-x64.wat index 4d5bece74070..984198c73bc2 100644 --- a/tests/disas/component-model/direct-adapter-calls-x64.wat +++ b/tests/disas/component-model/direct-adapter-calls-x64.wat @@ -1,7 +1,7 @@ ;;! target = "x86_64" ;;! test = 'compile' ;;! filter = "function" -;;! flags = "-C inlining=n" +;;! flags = "-C inlining=n -Wconcurrency-support=n" ;; Same as `direct-adapter-calls.wat` but shows full compilation down to x86_64 ;; so that we can exercise our linker's ability to resolve relocations for diff --git a/tests/disas/component-model/direct-adapter-calls.wat b/tests/disas/component-model/direct-adapter-calls.wat index f8e71a7d98aa..8f935101d4d4 100644 --- a/tests/disas/component-model/direct-adapter-calls.wat +++ b/tests/disas/component-model/direct-adapter-calls.wat @@ -1,7 +1,7 @@ ;;! target = "x86_64" ;;! test = "optimize" ;;! filter = "function" -;;! flags = "-C inlining=n" +;;! flags = "-C inlining=n -Wconcurrency-support=n" ;; The following component links two sub-components together and each are only ;; instantiated the once, so we statically know what their core modules'