wasmtime/runtime/component/
store.rs1use crate::prelude::*;
2use crate::runtime::component::{HostResourceData, Instance};
3use crate::runtime::vm;
4use crate::runtime::vm::component::{
5 CallContext, ComponentInstance, HandleCounter, HandleTable, OwnedComponentInstance, Scope,
6};
7use crate::store::{StoreData, StoreId, StoreOpaque};
8use crate::{AsContext, AsContextMut, Engine, Store, StoreContextMut, bail_bug};
9use core::pin::Pin;
10use wasmtime_environ::component::RuntimeComponentInstanceIndex;
11use wasmtime_environ::prelude::TryPrimaryMap;
12
13#[cfg(feature = "component-model-async")]
14use crate::{
15 component::ResourceTable,
16 component::concurrent::ConcurrentState,
17 runtime::vm::{VMStore, component::InstanceState},
18};
19
20const DEFAULT_HOSTCALL_FUEL: usize = 128 << 20;
28
29const DEFAULT_MAX_COMPONENT_HANDLES: usize = 100_000;
34
35pub struct ComponentStoreData {
38 instances: TryPrimaryMap<ComponentInstanceId, Option<OwnedComponentInstance>>,
41
42 component_trapped: bool,
49
50 has_components: bool,
52
53 num_component_instances: usize,
56
57 handle_counter: HandleCounter,
61
62 component_host_table: HandleTable,
66 host_resource_data: HostResourceData,
67
68 task_state: ComponentTaskState,
71
72 hostcall_fuel: usize,
78}
79
80pub enum ComponentTaskState {
82 NotConcurrent(ComponentTasksNotConcurrent),
85
86 #[cfg(feature = "component-model-async")]
89 Concurrent(ConcurrentState),
90}
91
92#[derive(Copy, Clone, Debug, PartialEq, Eq)]
93pub struct ComponentInstanceId(u32);
94wasmtime_environ::entity_impl!(ComponentInstanceId);
95
96#[derive(Debug, Copy, Clone, PartialEq, Eq)]
97pub struct RuntimeInstance {
98 pub instance: ComponentInstanceId,
99 pub index: RuntimeComponentInstanceIndex,
100}
101
102impl ComponentStoreData {
103 pub fn new(engine: &Engine) -> ComponentStoreData {
104 ComponentStoreData {
105 instances: Default::default(),
106 component_trapped: false,
107 has_components: false,
108 num_component_instances: 0,
109 component_host_table: Default::default(),
110 handle_counter: HandleCounter::new(DEFAULT_MAX_COMPONENT_HANDLES),
111 host_resource_data: Default::default(),
112 task_state: if engine.tunables().concurrency_support {
113 #[cfg(feature = "component-model-async")]
114 {
115 ComponentTaskState::Concurrent(Default::default())
116 }
117 #[cfg(not(feature = "component-model-async"))]
118 {
119 unreachable!()
123 }
124 } else {
125 ComponentTaskState::NotConcurrent(Default::default())
126 },
127 hostcall_fuel: DEFAULT_HOSTCALL_FUEL,
128 }
129 }
130
131 pub fn run_manual_drop_routines<T>(store: StoreContextMut<T>) {
136 #[cfg(feature = "component-model-async")]
145 if store.0.component_data().task_state.is_concurrent() {
146 ComponentStoreData::drop_fibers_and_futures(store.0);
147 store.0.clean_up_task_groups();
148 }
149 #[cfg(not(feature = "component-model-async"))]
150 let _ = store;
151 }
152
153 pub fn next_component_instance_id(&self) -> ComponentInstanceId {
154 self.instances.next_key()
155 }
156
157 #[cfg(feature = "component-model-async")]
158 pub(crate) fn drop_fibers_and_futures(store: &mut dyn VMStore) {
159 let mut fibers = Vec::new();
160 let mut futures = Vec::new();
161 store
162 .concurrent_state_mut_without_forcing_current_thread()
163 .take_fibers_and_futures(&mut fibers, &mut futures);
164
165 for mut fiber in fibers {
166 fiber.dispose(store);
167 }
168
169 crate::component::concurrent::tls::set(store, move || drop(futures));
170 }
171
172 #[cfg(feature = "component-model-async")]
173 pub(crate) fn assert_instance_states_empty(&mut self) {
174 for (_, instance) in self.instances.iter_mut() {
175 let Some(instance) = instance.as_mut() else {
176 continue;
177 };
178
179 assert!(instance.get_mut().instance_states().0.iter_mut().all(
180 |(_, state): (_, &mut InstanceState)| state.handle_table().is_empty()
181 && state.concurrent_state().pending_is_empty()
182 ));
183 }
184 }
185
186 pub fn decrement_allocator_resources(&mut self, allocator: &dyn vm::InstanceAllocator) {
187 for _ in 0..self.num_component_instances {
188 allocator.decrement_component_instance_count();
189 }
190 }
191
192 #[cfg(all(feature = "component-model-async", feature = "gc"))]
193 pub fn task_state_mut(&mut self) -> &mut ComponentTaskState {
194 &mut self.task_state
195 }
196}
197
198#[repr(C)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
209pub struct StoreComponentInstanceId {
210 store_id: StoreId,
211 instance: ComponentInstanceId,
212}
213
214impl StoreComponentInstanceId {
215 pub(crate) fn new(
216 store_id: StoreId,
217 instance: ComponentInstanceId,
218 ) -> StoreComponentInstanceId {
219 StoreComponentInstanceId { store_id, instance }
220 }
221
222 #[inline]
223 pub fn assert_belongs_to(&self, store: StoreId) {
224 self.store_id.assert_belongs_to(store)
225 }
226
227 #[inline]
228 pub(crate) fn store_id(&self) -> StoreId {
229 self.store_id
230 }
231
232 #[inline]
233 pub(crate) fn instance(&self) -> ComponentInstanceId {
234 self.instance
235 }
236
237 pub(crate) fn get<'a>(&self, store: &'a StoreOpaque) -> &'a ComponentInstance {
244 self.assert_belongs_to(store.id());
245 store.component_instance(self.instance)
246 }
247
248 pub(crate) fn get_mut<'a>(&self, store: &'a mut StoreOpaque) -> Pin<&'a mut ComponentInstance> {
254 self.from_data_get_mut(store.store_data_mut())
255 }
256
257 #[cfg(feature = "component-model-async")]
263 pub(crate) fn get_mut_and_handle_counter<'a>(
264 &self,
265 store: &'a mut StoreOpaque,
266 ) -> (Pin<&'a mut ComponentInstance>, &'a mut HandleCounter) {
267 let store = store.store_data_mut();
268 self.assert_belongs_to(store.id());
269 store.component_instance_and_handle_counter_mut(self.instance)
270 }
271
272 #[cfg(feature = "component-model-async")]
279 pub(crate) fn get_mut_and_registry<'a>(
280 &self,
281 store: &'a mut StoreOpaque,
282 ) -> (
283 Pin<&'a mut ComponentInstance>,
284 &'a crate::module::ModuleRegistry,
285 ) {
286 let (store_data, registry) = store.store_data_mut_and_registry();
287 let instance = self.from_data_get_mut(store_data);
288 (instance, registry)
289 }
290
291 fn from_data_get_mut<'a>(&self, store: &'a mut StoreData) -> Pin<&'a mut ComponentInstance> {
293 self.assert_belongs_to(store.id());
294 store.component_instance_mut(self.instance)
295 }
296}
297
298impl StoreData {
299 pub(crate) fn push_component_instance(
300 &mut self,
301 data: OwnedComponentInstance,
302 ) -> Result<ComponentInstanceId, OutOfMemory> {
303 let expected = data.get().id();
304 let ret = self.components.instances.push(Some(data))?;
305 assert_eq!(expected, ret);
306 self.components.has_components = true;
307 Ok(ret)
308 }
309
310 pub(crate) fn component_instance(&self, id: ComponentInstanceId) -> &ComponentInstance {
311 self.components.instances[id].as_ref().unwrap().get()
312 }
313
314 pub(crate) fn component_instance_mut(
315 &mut self,
316 id: ComponentInstanceId,
317 ) -> Pin<&mut ComponentInstance> {
318 self.components.instances[id].as_mut().unwrap().get_mut()
319 }
320
321 #[cfg(feature = "component-model-async")]
322 pub(crate) fn component_instance_and_handle_counter_mut(
323 &mut self,
324 id: ComponentInstanceId,
325 ) -> (Pin<&mut ComponentInstance>, &mut HandleCounter) {
326 let data = &mut self.components;
327 (
328 data.instances[id].as_mut().unwrap().get_mut(),
329 &mut data.handle_counter,
330 )
331 }
332}
333
334impl StoreOpaque {
335 #[inline]
336 pub(crate) fn component_trapped(&self) -> bool {
337 self.store_data().components.component_trapped
338 }
339
340 pub(crate) fn set_trapped(&mut self) {
341 let component_data = &mut self.store_data_mut().components;
342 if component_data.has_components {
343 component_data.component_trapped = true;
344 }
345 }
346
347 #[inline]
351 pub(crate) fn may_enter(&mut self) -> bool {
352 !self.component_trapped()
353 }
354
355 pub(crate) fn component_data(&self) -> &ComponentStoreData {
356 &self.store_data().components
357 }
358
359 pub(crate) fn component_data_mut(&mut self) -> &mut ComponentStoreData {
360 &mut self.store_data_mut().components
361 }
362
363 pub(crate) fn push_component_instance(&mut self, instance: Instance) {
364 let _ = instance;
368
369 self.component_data_mut().num_component_instances += 1;
370 }
371
372 pub(crate) fn component_instance(&self, id: ComponentInstanceId) -> &ComponentInstance {
373 self.store_data().component_instance(id)
374 }
375
376 #[cfg(feature = "component-model-async")]
377 pub(crate) fn component_instance_mut(
378 &mut self,
379 id: ComponentInstanceId,
380 ) -> Pin<&mut ComponentInstance> {
381 self.store_data_mut().component_instance_mut(id)
382 }
383
384 #[cfg(feature = "component-model-async")]
385 pub(crate) fn concurrent_state_mut_without_forcing_current_thread(
386 &mut self,
387 ) -> &mut ConcurrentState {
388 debug_assert!(self.concurrency_support());
389 self.component_data_mut().task_state.concurrent_state_mut()
390 }
391
392 #[cfg(feature = "component-model-async")]
393 pub(crate) fn concurrent_state_mut_already_forced_current_thread(
394 &mut self,
395 ) -> &mut ConcurrentState {
396 debug_assert!(self.concurrency_support());
397 debug_assert!(
398 !self
399 .vm_store_context_mut()
400 .current_thread_mut()
401 .is_deferred()
402 );
403 self.concurrent_state_mut_without_forcing_current_thread()
404 }
405
406 #[cfg(feature = "component-model-async")]
407 pub(crate) fn concurrent_state_mut(&mut self) -> Result<&mut ConcurrentState> {
408 debug_assert!(self.concurrency_support());
409 self.current_thread()?;
410 Ok(self.component_data_mut().task_state.concurrent_state_mut())
411 }
412
413 #[inline]
414 #[cfg(feature = "component-model-async")]
415 pub(crate) fn concurrency_support(&self) -> bool {
416 let support = self.component_data().task_state.is_concurrent();
417 debug_assert_eq!(support, self.engine().tunables().concurrency_support);
418 support
419 }
420
421 pub(crate) fn lift_context_parts(
422 &mut self,
423 instance: Instance,
424 ) -> (
425 &mut ComponentTaskState,
426 &mut HandleTable,
427 &mut HandleCounter,
428 &mut HostResourceData,
429 Pin<&mut ComponentInstance>,
430 ) {
431 let instance = instance.id();
432 instance.assert_belongs_to(self.id());
433 let data = self.component_data_mut();
434 (
435 &mut data.task_state,
436 &mut data.component_host_table,
437 &mut data.handle_counter,
438 &mut data.host_resource_data,
439 data.instances[instance.instance]
440 .as_mut()
441 .unwrap()
442 .get_mut(),
443 )
444 }
445
446 pub(crate) fn component_resource_tables(
447 &mut self,
448 instance: Option<Instance>,
449 ) -> Result<vm::component::ResourceTables<'_>> {
450 Ok(self
451 .component_resource_tables_and_host_resource_data(instance)?
452 .0)
453 }
454
455 pub(crate) fn component_resource_tables_and_host_resource_data(
456 &mut self,
457 instance: Option<Instance>,
458 ) -> Result<(
459 vm::component::ResourceTables<'_>,
460 &mut crate::component::HostResourceData,
461 )> {
462 let current_scope = self.current_scope()?;
463
464 let store_id = self.id();
465 let data = self.component_data_mut();
466 let guest = instance.map(|i| {
467 let i = i.id();
468 i.assert_belongs_to(store_id);
469 data.instances[i.instance]
470 .as_mut()
471 .unwrap()
472 .get_mut()
473 .instance_states()
474 });
475
476 Ok((
477 vm::component::ResourceTables {
478 host_table: &mut data.component_host_table,
479 handle_counter: &mut data.handle_counter,
480 task_state: &mut data.task_state,
481 guest,
482 current_scope,
483 },
484 &mut data.host_resource_data,
485 ))
486 }
487
488 pub(crate) fn enter_call_not_concurrent(&mut self) -> Result<()> {
489 let state = match &mut self.component_data_mut().task_state {
490 ComponentTaskState::NotConcurrent(state) => state,
491 #[cfg(feature = "component-model-async")]
492 ComponentTaskState::Concurrent(_) => unreachable!(),
493 };
494 state.scopes.push(CallContext::default())?;
495 Ok(())
496 }
497
498 pub(crate) fn exit_call_not_concurrent(&mut self) {
499 let state = match &mut self.component_data_mut().task_state {
500 ComponentTaskState::NotConcurrent(state) => state,
501 #[cfg(feature = "component-model-async")]
502 ComponentTaskState::Concurrent(_) => unreachable!(),
503 };
504 state.scopes.pop();
505 }
506
507 pub(crate) fn hostcall_fuel(&self) -> usize {
508 self.component_data().hostcall_fuel
509 }
510
511 pub(crate) fn set_hostcall_fuel(&mut self, fuel: usize) {
512 self.component_data_mut().hostcall_fuel = fuel;
513 }
514
515 pub(crate) fn max_component_handles(&self) -> usize {
516 self.component_data().handle_counter.limit()
517 }
518
519 pub(crate) fn set_max_component_handles(&mut self, limit: usize) {
520 self.component_data_mut().handle_counter.set_limit(limit);
521 }
522
523 #[cfg(feature = "component-model-async")]
524 fn concurrent_resource_table(&mut self) -> Option<&mut ResourceTable> {
525 if self.concurrency_support() {
526 Some(
527 self.concurrent_state_mut_without_forcing_current_thread()
528 .table(),
529 )
530 } else {
531 None
532 }
533 }
534
535 pub(crate) fn current_scope_id_not_concurrent(&mut self) -> Result<Option<u32>> {
536 match &mut self.component_data_mut().task_state {
537 ComponentTaskState::NotConcurrent(state) => match state.scopes.len().checked_sub(1) {
538 Some(i) => Ok(Some(u32::try_from(i)?)),
539 None => Ok(None),
540 },
541 #[cfg(feature = "component-model-async")]
542 ComponentTaskState::Concurrent(_) => crate::bail_bug!("should not be reachable"),
543 }
544 }
545}
546
547impl<T> Store<T> {
548 pub fn hostcall_fuel(&self) -> usize {
556 self.as_context().0.hostcall_fuel()
557 }
558
559 pub fn set_hostcall_fuel(&mut self, fuel: usize) {
583 self.as_context_mut().set_hostcall_fuel(fuel)
584 }
585
586 pub fn max_component_handles(&self) -> usize {
594 self.as_context().0.max_component_handles()
595 }
596
597 pub fn set_max_component_handles(&mut self, limit: usize) {
615 self.as_context_mut().set_max_component_handles(limit)
616 }
617
618 #[cfg(feature = "component-model-async")]
636 pub fn concurrent_resource_table(&mut self) -> Option<&mut ResourceTable> {
637 self.as_context_mut().0.concurrent_resource_table()
638 }
639}
640
641impl<T> StoreContextMut<'_, T> {
642 pub fn hostcall_fuel(&self) -> usize {
644 self.0.hostcall_fuel()
645 }
646
647 pub fn set_hostcall_fuel(&mut self, fuel: usize) {
649 self.0.set_hostcall_fuel(fuel)
650 }
651
652 pub fn max_component_handles(&self) -> usize {
654 self.0.max_component_handles()
655 }
656
657 pub fn set_max_component_handles(&mut self, limit: usize) {
659 self.0.set_max_component_handles(limit)
660 }
661
662 #[cfg(feature = "component-model-async")]
664 pub fn concurrent_resource_table(&mut self) -> Option<&mut ResourceTable> {
665 self.0.concurrent_resource_table()
666 }
667}
668
669#[derive(Default)]
670pub struct ComponentTasksNotConcurrent {
671 scopes: TryVec<CallContext>,
672}
673
674impl ComponentTaskState {
675 pub fn call_context(&mut self, id: Scope) -> Result<&mut CallContext> {
676 match self {
677 ComponentTaskState::NotConcurrent(state) => match id {
678 Scope::Id(id) => Ok(&mut state.scopes[id as usize]),
679 Scope::HostId(_) => bail_bug!("non-concurrent scope cannot be a host ID"),
680 },
681 #[cfg(feature = "component-model-async")]
682 ComponentTaskState::Concurrent(state) => state.call_context(id),
683 }
684 }
685
686 pub(crate) fn materialize_current_scope(&mut self) -> Result<Scope> {
687 match self {
688 ComponentTaskState::NotConcurrent(_) => {
689 bail_bug!("a non-concurrent scope cannot be deferred")
690 }
691 #[cfg(feature = "component-model-async")]
692 ComponentTaskState::Concurrent(state) => state.materialize_current_scope(),
693 }
694 }
695
696 pub(crate) fn deferred_host_call_context(&mut self) -> Option<&mut CallContext> {
697 match self {
698 ComponentTaskState::NotConcurrent(_) => None,
699 #[cfg(feature = "component-model-async")]
700 ComponentTaskState::Concurrent(state) => state.deferred_host_call_context(),
701 }
702 }
703
704 #[cfg(feature = "component-model-async")]
705 pub fn concurrent_state_mut(&mut self) -> &mut ConcurrentState {
706 match self {
707 ComponentTaskState::Concurrent(state) => state,
708 ComponentTaskState::NotConcurrent(_) => {
709 panic!("expected concurrent state to be present")
710 }
711 }
712 }
713
714 #[cfg(feature = "component-model-async")]
715 fn is_concurrent(&self) -> bool {
716 match self {
717 ComponentTaskState::Concurrent(_) => true,
718 ComponentTaskState::NotConcurrent(_) => false,
719 }
720 }
721}