1use crate::prelude::*;
2use crate::runtime::component::{HostResourceData, Instance};
3use crate::runtime::vm;
4use crate::runtime::vm::VMResourceIndex;
5use crate::runtime::vm::component::{
6 CallContext, ComponentInstance, HandleCounter, HandleTable, OwnedComponentInstance,
7};
8use crate::store::{StoreData, StoreId, StoreOpaque};
9use crate::vm::VMScope;
10use crate::vm::component::ScopeKind;
11use crate::{AsContext, AsContextMut, Engine, Store, StoreContextMut, bail_bug};
12use core::pin::Pin;
13use wasmtime_environ::LAZY_LENDERS_CAPACITY;
14use wasmtime_environ::component::RuntimeComponentInstanceIndex;
15use wasmtime_environ::prelude::TryPrimaryMap;
16
17#[cfg(feature = "component-model-async")]
18use crate::{
19 component::ResourceTable,
20 component::concurrent::ConcurrentState,
21 runtime::vm::{VMStore, component::InstanceState},
22};
23
24const DEFAULT_HOSTCALL_FUEL: usize = 128 << 20;
32
33const DEFAULT_MAX_COMPONENT_HANDLES: usize = 100_000;
38
39pub struct ComponentStoreData {
42 instances: TryPrimaryMap<ComponentInstanceId, Option<OwnedComponentInstance>>,
45
46 component_trapped: bool,
53
54 has_components: bool,
56
57 num_component_instances: usize,
60
61 handle_counter: HandleCounter,
65
66 component_host_table: HandleTable,
70 host_resource_data: HostResourceData,
71
72 lazy_lenders: Box<[VMResourceIndex; LAZY_LENDERS_CAPACITY]>,
77
78 task_state: ComponentTaskState,
81
82 hostcall_fuel: usize,
88}
89
90pub enum ComponentTaskState {
92 NotConcurrent(ComponentTasksNotConcurrent),
95
96 #[cfg(feature = "component-model-async")]
99 Concurrent(ConcurrentState),
100}
101
102#[derive(Copy, Clone, Debug, PartialEq, Eq)]
103pub struct ComponentInstanceId(u32);
104wasmtime_environ::entity_impl!(ComponentInstanceId);
105
106#[derive(Debug, Copy, Clone, PartialEq, Eq)]
107pub struct RuntimeInstance {
108 pub instance: ComponentInstanceId,
109 pub index: RuntimeComponentInstanceIndex,
110}
111
112impl ComponentStoreData {
113 pub fn new(engine: &Engine) -> Result<ComponentStoreData> {
114 Ok(ComponentStoreData {
115 instances: Default::default(),
116 component_trapped: false,
117 has_components: false,
118 num_component_instances: 0,
119 component_host_table: Default::default(),
120 handle_counter: HandleCounter::new(DEFAULT_MAX_COMPONENT_HANDLES),
121 host_resource_data: Default::default(),
122 task_state: if engine.tunables().concurrency_support {
123 #[cfg(feature = "component-model-async")]
124 {
125 ComponentTaskState::Concurrent(Default::default())
126 }
127 #[cfg(not(feature = "component-model-async"))]
128 {
129 unreachable!()
133 }
134 } else {
135 ComponentTaskState::NotConcurrent(Default::default())
136 },
137 hostcall_fuel: DEFAULT_HOSTCALL_FUEL,
138 lazy_lenders: try_new::<Box<_>>([VMResourceIndex::EMPTY; LAZY_LENDERS_CAPACITY])?,
139 })
140 }
141
142 fn resource_tables(
149 &mut self,
150 store_id: StoreId,
151 instance: Option<Instance>,
152 current_scope: Option<vm::component::CurrentScope>,
153 ) -> (
154 vm::component::ResourceTables<'_>,
155 &mut HostResourceData,
156 &mut [VMResourceIndex],
157 ) {
158 let guest = instance.map(|i| {
159 let i = i.id();
160 i.assert_belongs_to(store_id);
161 self.instances[i.instance]
162 .as_mut()
163 .unwrap()
164 .get_mut()
165 .instance_states_and_published()
166 });
167 (
168 vm::component::ResourceTables {
169 host_table: &mut self.component_host_table,
170 handle_counter: &mut self.handle_counter,
171 task_state: &mut self.task_state,
172 guest,
173 current_scope,
174 },
175 &mut self.host_resource_data,
176 &mut self.lazy_lenders[..],
177 )
178 }
179
180 pub(crate) unsafe fn publish_lazy_lenders(
187 &mut self,
188 vm_store_context: &mut vm::VMStoreContext,
189 ) {
190 let lazy_lenders = core::ptr::NonNull::from(&mut *self.lazy_lenders);
191 unsafe { vm_store_context.set_lazy_lenders(lazy_lenders) }
194 }
195
196 pub fn run_manual_drop_routines<T>(store: StoreContextMut<T>) {
201 #[cfg(feature = "component-model-async")]
210 if store.0.component_data().task_state.is_concurrent() {
211 ComponentStoreData::drop_fibers_and_futures(store.0);
212 store.0.clean_up_task_groups();
213 }
214 #[cfg(not(feature = "component-model-async"))]
215 let _ = store;
216 }
217
218 pub fn next_component_instance_id(&self) -> ComponentInstanceId {
219 self.instances.next_key()
220 }
221
222 #[cfg(feature = "component-model-async")]
223 pub(crate) fn drop_fibers_and_futures(store: &mut dyn VMStore) {
224 let mut fibers = Vec::new();
225 let mut futures = Vec::new();
226 store
227 .concurrent_state_mut_without_forcing_current_thread()
228 .take_fibers_and_futures(&mut fibers, &mut futures);
229
230 for mut fiber in fibers {
231 fiber.dispose(store);
232 }
233
234 crate::component::concurrent::tls::set(store, move || drop(futures));
235 }
236
237 #[cfg(feature = "component-model-async")]
238 pub(crate) fn assert_instance_states_empty(&mut self) {
239 for (_, instance) in self.instances.iter_mut() {
240 let Some(instance) = instance.as_mut() else {
241 continue;
242 };
243
244 assert!(instance.get_mut().instance_states().0.iter_mut().all(
245 |(_, state): (_, &mut InstanceState)| state.handle_table().is_empty()
246 && state.concurrent_state().pending_is_empty()
247 ));
248 }
249 }
250
251 pub fn decrement_allocator_resources(&mut self, allocator: &dyn vm::InstanceAllocator) {
252 for _ in 0..self.num_component_instances {
253 allocator.decrement_component_instance_count();
254 }
255 }
256
257 #[cfg(all(feature = "component-model-async", feature = "gc"))]
258 pub fn task_state_mut(&mut self) -> &mut ComponentTaskState {
259 &mut self.task_state
260 }
261}
262
263#[repr(C)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
274pub struct StoreComponentInstanceId {
275 store_id: StoreId,
276 instance: ComponentInstanceId,
277}
278
279impl StoreComponentInstanceId {
280 pub(crate) fn new(
281 store_id: StoreId,
282 instance: ComponentInstanceId,
283 ) -> StoreComponentInstanceId {
284 StoreComponentInstanceId { store_id, instance }
285 }
286
287 #[inline]
288 pub fn assert_belongs_to(&self, store: StoreId) {
289 self.store_id.assert_belongs_to(store)
290 }
291
292 #[inline]
293 pub(crate) fn store_id(&self) -> StoreId {
294 self.store_id
295 }
296
297 #[inline]
298 pub(crate) fn instance(&self) -> ComponentInstanceId {
299 self.instance
300 }
301
302 pub(crate) fn get<'a>(&self, store: &'a StoreOpaque) -> &'a ComponentInstance {
309 self.assert_belongs_to(store.id());
310 store.component_instance(self.instance)
311 }
312
313 pub(crate) fn get_mut<'a>(&self, store: &'a mut StoreOpaque) -> Pin<&'a mut ComponentInstance> {
319 self.from_data_get_mut(store.store_data_mut())
320 }
321
322 #[cfg(feature = "component-model-async")]
328 pub(crate) fn get_mut_and_handle_counter<'a>(
329 &self,
330 store: &'a mut StoreOpaque,
331 ) -> (Pin<&'a mut ComponentInstance>, &'a mut HandleCounter) {
332 let store = store.store_data_mut();
333 self.assert_belongs_to(store.id());
334 store.component_instance_and_handle_counter_mut(self.instance)
335 }
336
337 #[cfg(feature = "component-model-async")]
344 pub(crate) fn get_mut_and_registry<'a>(
345 &self,
346 store: &'a mut StoreOpaque,
347 ) -> (
348 Pin<&'a mut ComponentInstance>,
349 &'a crate::module::ModuleRegistry,
350 ) {
351 let (store_data, registry) = store.store_data_mut_and_registry();
352 let instance = self.from_data_get_mut(store_data);
353 (instance, registry)
354 }
355
356 fn from_data_get_mut<'a>(&self, store: &'a mut StoreData) -> Pin<&'a mut ComponentInstance> {
358 self.assert_belongs_to(store.id());
359 store.component_instance_mut(self.instance)
360 }
361}
362
363impl StoreData {
364 pub(crate) fn push_component_instance(
365 &mut self,
366 data: OwnedComponentInstance,
367 ) -> Result<ComponentInstanceId, OutOfMemory> {
368 let expected = data.get().id();
369 let ret = self.components.instances.push(Some(data))?;
370 assert_eq!(expected, ret);
371 self.components.has_components = true;
372 Ok(ret)
373 }
374
375 pub(crate) fn component_instance(&self, id: ComponentInstanceId) -> &ComponentInstance {
376 self.components.instances[id].as_ref().unwrap().get()
377 }
378
379 pub(crate) fn component_instance_mut(
380 &mut self,
381 id: ComponentInstanceId,
382 ) -> Pin<&mut ComponentInstance> {
383 self.components.instances[id].as_mut().unwrap().get_mut()
384 }
385
386 #[cfg(feature = "component-model-async")]
387 pub(crate) fn component_instance_and_handle_counter_mut(
388 &mut self,
389 id: ComponentInstanceId,
390 ) -> (Pin<&mut ComponentInstance>, &mut HandleCounter) {
391 let data = &mut self.components;
392 (
393 data.instances[id].as_mut().unwrap().get_mut(),
394 &mut data.handle_counter,
395 )
396 }
397}
398
399impl StoreOpaque {
400 #[inline]
401 pub(crate) fn component_trapped(&self) -> bool {
402 self.store_data().components.component_trapped
403 }
404
405 pub(crate) fn set_trapped(&mut self) {
406 let component_data = &mut self.store_data_mut().components;
407 if component_data.has_components {
408 component_data.component_trapped = true;
409 }
410 }
411
412 #[inline]
416 pub(crate) fn may_enter(&mut self) -> bool {
417 !self.component_trapped()
418 }
419
420 pub(crate) fn component_data(&self) -> &ComponentStoreData {
421 &self.store_data().components
422 }
423
424 pub(crate) fn component_data_mut(&mut self) -> &mut ComponentStoreData {
425 &mut self.store_data_mut().components
426 }
427
428 pub(crate) fn push_component_instance(&mut self, instance: Instance) {
429 let _ = instance;
433
434 self.component_data_mut().num_component_instances += 1;
435 }
436
437 pub(crate) fn component_instance(&self, id: ComponentInstanceId) -> &ComponentInstance {
438 self.store_data().component_instance(id)
439 }
440
441 #[cfg(feature = "component-model-async")]
442 pub(crate) fn component_instance_mut(
443 &mut self,
444 id: ComponentInstanceId,
445 ) -> Pin<&mut ComponentInstance> {
446 self.store_data_mut().component_instance_mut(id)
447 }
448
449 #[cfg(feature = "component-model-async")]
450 pub(crate) fn concurrent_state_mut_without_forcing_current_thread(
451 &mut self,
452 ) -> &mut ConcurrentState {
453 debug_assert!(self.concurrency_support());
454 self.component_data_mut().task_state.concurrent_state_mut()
455 }
456
457 #[cfg(feature = "component-model-async")]
458 pub(crate) fn concurrent_state_mut_already_forced_current_thread(
459 &mut self,
460 ) -> &mut ConcurrentState {
461 debug_assert!(self.concurrency_support());
462 debug_assert!(
463 !self
464 .vm_store_context_mut()
465 .current_thread_mut()
466 .is_deferred()
467 );
468 self.concurrent_state_mut_without_forcing_current_thread()
469 }
470
471 #[cfg(feature = "component-model-async")]
472 pub(crate) fn concurrent_state_mut(&mut self) -> Result<&mut ConcurrentState> {
473 debug_assert!(self.concurrency_support());
474 self.current_thread()?;
475 Ok(self.component_data_mut().task_state.concurrent_state_mut())
476 }
477
478 #[inline]
479 #[cfg(feature = "component-model-async")]
480 pub(crate) fn concurrency_support(&self) -> bool {
481 let support = self.component_data().task_state.is_concurrent();
482 debug_assert_eq!(support, self.engine().tunables().concurrency_support);
483 support
484 }
485
486 pub(crate) fn lift_context_parts(
487 &mut self,
488 instance: Instance,
489 ) -> (
490 &mut ComponentTaskState,
491 &mut HandleTable,
492 &mut HandleCounter,
493 &mut HostResourceData,
494 Pin<&mut ComponentInstance>,
495 ) {
496 let instance = instance.id();
497 instance.assert_belongs_to(self.id());
498 let data = self.component_data_mut();
499 (
500 &mut data.task_state,
501 &mut data.component_host_table,
502 &mut data.handle_counter,
503 &mut data.host_resource_data,
504 data.instances[instance.instance]
505 .as_mut()
506 .unwrap()
507 .get_mut(),
508 )
509 }
510
511 pub(crate) fn component_resource_tables(
512 &mut self,
513 instance: Option<Instance>,
514 ) -> Result<vm::component::ResourceTables<'_>> {
515 Ok(self
516 .component_resource_tables_and_host_resource_data(instance)?
517 .0)
518 }
519
520 pub(crate) fn component_resource_tables_and_host_resource_data(
521 &mut self,
522 instance: Option<Instance>,
523 ) -> Result<(
524 vm::component::ResourceTables<'_>,
525 &mut crate::component::HostResourceData,
526 )> {
527 let current_scope = self.current_scope()?;
528 let store_id = self.id();
529 let (tables, host_resource_data, _) =
530 self.component_data_mut()
531 .resource_tables(store_id, instance, current_scope);
532 Ok((tables, host_resource_data))
533 }
534
535 pub(crate) fn resource_lift_borrow_maybe_lazy(
541 &mut self,
542 instance: Instance,
543 index: vm::component::TypedResourceIndex,
544 ) -> Result<u32> {
545 if self
548 .vm_store_context_mut()
549 .current_thread_mut()
550 .is_deferred()
551 {
552 let store_id = self.id();
553 let (vm_store_context, store_data) = self.vm_store_context_and_store_data_mut();
554 let (mut tables, _, lazy_lenders) =
555 store_data
556 .components
557 .resource_tables(store_id, Some(instance), None);
558 match tables.resource_lift_borrow_lazy(index, lazy_lenders, vm_store_context)? {
559 Some(rep) => return Ok(rep),
560 None => {
561 }
563 }
564 }
565
566 self.component_resource_tables(Some(instance))?
569 .resource_lift_borrow(index)
570 }
571
572 #[cfg_attr(
575 not(feature = "component-model-async"),
576 expect(
577 dead_code,
578 reason = "deferred threads are only created with `component-model-async`, \
579 so nothing promotes lazy lends without it"
580 )
581 )]
582 pub(crate) fn promote_lazy_lends(
583 &mut self,
584 instance: Instance,
585 scope: vm::VMScope,
586 start: usize,
587 end: usize,
588 ) -> Result<()> {
589 debug_assert!(start <= end && end <= *self.vm_store_context_mut().lazy_lenders_len_mut());
590 let store_id = self.id();
591 let (mut tables, _, lazy_lenders) =
592 self.component_data_mut()
593 .resource_tables(store_id, Some(instance), None);
594 tables.promote_lazy_lends(scope, &lazy_lenders[start..end])
595 }
596
597 pub(crate) fn enter_call_not_concurrent(&mut self) -> Result<()> {
598 let state = match &mut self.component_data_mut().task_state {
599 ComponentTaskState::NotConcurrent(state) => state,
600 #[cfg(feature = "component-model-async")]
601 ComponentTaskState::Concurrent(_) => unreachable!(),
602 };
603 state.scopes.push(CallContext::default())?;
604 Ok(())
605 }
606
607 pub(crate) fn exit_call_not_concurrent(&mut self) {
608 let state = match &mut self.component_data_mut().task_state {
609 ComponentTaskState::NotConcurrent(state) => state,
610 #[cfg(feature = "component-model-async")]
611 ComponentTaskState::Concurrent(_) => unreachable!(),
612 };
613 state.scopes.pop();
614 }
615
616 pub(crate) fn hostcall_fuel(&self) -> usize {
617 self.component_data().hostcall_fuel
618 }
619
620 pub(crate) fn set_hostcall_fuel(&mut self, fuel: usize) {
621 self.component_data_mut().hostcall_fuel = fuel;
622 }
623
624 pub(crate) fn max_component_handles(&self) -> usize {
625 self.component_data().handle_counter.limit()
626 }
627
628 pub(crate) fn set_max_component_handles(&mut self, limit: usize) {
629 self.component_data_mut().handle_counter.set_limit(limit);
630 }
631
632 #[cfg(feature = "component-model-async")]
633 fn concurrent_resource_table(&mut self) -> Option<&mut ResourceTable> {
634 if self.concurrency_support() {
635 Some(
636 self.concurrent_state_mut_without_forcing_current_thread()
637 .table(),
638 )
639 } else {
640 None
641 }
642 }
643
644 pub(crate) fn current_scope_id_not_concurrent(&mut self) -> Result<Option<u32>> {
645 match &mut self.component_data_mut().task_state {
646 ComponentTaskState::NotConcurrent(state) => match state.scopes.len().checked_sub(1) {
647 Some(i) => Ok(Some(u32::try_from(i)?)),
648 None => Ok(None),
649 },
650 #[cfg(feature = "component-model-async")]
651 ComponentTaskState::Concurrent(_) => crate::bail_bug!("should not be reachable"),
652 }
653 }
654}
655
656impl<T> Store<T> {
657 pub fn hostcall_fuel(&self) -> usize {
665 self.as_context().0.hostcall_fuel()
666 }
667
668 pub fn set_hostcall_fuel(&mut self, fuel: usize) {
692 self.as_context_mut().set_hostcall_fuel(fuel)
693 }
694
695 pub fn max_component_handles(&self) -> usize {
703 self.as_context().0.max_component_handles()
704 }
705
706 pub fn set_max_component_handles(&mut self, limit: usize) {
724 self.as_context_mut().set_max_component_handles(limit)
725 }
726
727 #[cfg(feature = "component-model-async")]
745 pub fn concurrent_resource_table(&mut self) -> Option<&mut ResourceTable> {
746 self.as_context_mut().0.concurrent_resource_table()
747 }
748}
749
750impl<T> StoreContextMut<'_, T> {
751 pub fn hostcall_fuel(&self) -> usize {
753 self.0.hostcall_fuel()
754 }
755
756 pub fn set_hostcall_fuel(&mut self, fuel: usize) {
758 self.0.set_hostcall_fuel(fuel)
759 }
760
761 pub fn max_component_handles(&self) -> usize {
763 self.0.max_component_handles()
764 }
765
766 pub fn set_max_component_handles(&mut self, limit: usize) {
768 self.0.set_max_component_handles(limit)
769 }
770
771 #[cfg(feature = "component-model-async")]
773 pub fn concurrent_resource_table(&mut self) -> Option<&mut ResourceTable> {
774 self.0.concurrent_resource_table()
775 }
776}
777
778#[derive(Default)]
779pub struct ComponentTasksNotConcurrent {
780 scopes: TryVec<CallContext>,
781}
782
783impl ComponentTaskState {
784 pub fn call_context(&mut self, id: VMScope) -> Result<&mut CallContext> {
785 match self {
786 ComponentTaskState::NotConcurrent(state) => match id.kind() {
787 ScopeKind::Id(id) => Ok(&mut state.scopes[id as usize]),
788 ScopeKind::HostId(_) => bail_bug!("non-concurrent scope cannot be a host ID"),
789 },
790 #[cfg(feature = "component-model-async")]
791 ComponentTaskState::Concurrent(state) => state.call_context(id),
792 }
793 }
794
795 pub(crate) fn materialize_current_scope(&mut self) -> Result<VMScope> {
796 match self {
797 ComponentTaskState::NotConcurrent(_) => {
798 bail_bug!("a non-concurrent scope cannot be deferred")
799 }
800 #[cfg(feature = "component-model-async")]
801 ComponentTaskState::Concurrent(state) => state.materialize_current_scope(),
802 }
803 }
804
805 pub(crate) fn deferred_host_call_context(&mut self) -> Option<&mut CallContext> {
806 match self {
807 ComponentTaskState::NotConcurrent(_) => None,
808 #[cfg(feature = "component-model-async")]
809 ComponentTaskState::Concurrent(state) => state.deferred_host_call_context(),
810 }
811 }
812
813 #[cfg(feature = "component-model-async")]
814 pub fn concurrent_state_mut(&mut self) -> &mut ConcurrentState {
815 match self {
816 ComponentTaskState::Concurrent(state) => state,
817 ComponentTaskState::NotConcurrent(_) => {
818 panic!("expected concurrent state to be present")
819 }
820 }
821 }
822
823 #[cfg(feature = "component-model-async")]
824 fn is_concurrent(&self) -> bool {
825 match self {
826 ComponentTaskState::Concurrent(_) => true,
827 ComponentTaskState::NotConcurrent(_) => false,
828 }
829 }
830}