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 trapped: bool,
44
45 num_component_instances: usize,
48
49 handle_counter: HandleCounter,
53
54 component_host_table: HandleTable,
58 host_resource_data: HostResourceData,
59
60 task_state: ComponentTaskState,
63
64 hostcall_fuel: usize,
70}
71
72pub enum ComponentTaskState {
74 NotConcurrent(ComponentTasksNotConcurrent),
77
78 #[cfg(feature = "component-model-async")]
81 Concurrent(ConcurrentState),
82}
83
84#[derive(Copy, Clone, Debug, PartialEq, Eq)]
85pub struct ComponentInstanceId(u32);
86wasmtime_environ::entity_impl!(ComponentInstanceId);
87
88#[derive(Debug, Copy, Clone, PartialEq, Eq)]
89pub struct RuntimeInstance {
90 pub instance: ComponentInstanceId,
91 pub index: RuntimeComponentInstanceIndex,
92}
93
94impl ComponentStoreData {
95 pub fn new(engine: &Engine) -> ComponentStoreData {
96 ComponentStoreData {
97 instances: Default::default(),
98 trapped: false,
99 num_component_instances: 0,
100 component_host_table: Default::default(),
101 handle_counter: HandleCounter::new(DEFAULT_MAX_COMPONENT_HANDLES),
102 host_resource_data: Default::default(),
103 task_state: if engine.tunables().concurrency_support {
104 #[cfg(feature = "component-model-async")]
105 {
106 ComponentTaskState::Concurrent(Default::default())
107 }
108 #[cfg(not(feature = "component-model-async"))]
109 {
110 unreachable!()
114 }
115 } else {
116 ComponentTaskState::NotConcurrent(Default::default())
117 },
118 hostcall_fuel: DEFAULT_HOSTCALL_FUEL,
119 }
120 }
121
122 pub fn run_manual_drop_routines<T>(store: StoreContextMut<T>) {
127 #[cfg(feature = "component-model-async")]
136 if store.0.component_data().task_state.is_concurrent() {
137 ComponentStoreData::drop_fibers_and_futures(store.0);
138 store.0.clean_up_task_groups();
139 }
140 #[cfg(not(feature = "component-model-async"))]
141 let _ = store;
142 }
143
144 pub fn next_component_instance_id(&self) -> ComponentInstanceId {
145 self.instances.next_key()
146 }
147
148 #[cfg(feature = "component-model-async")]
149 pub(crate) fn drop_fibers_and_futures(store: &mut dyn VMStore) {
150 let mut fibers = Vec::new();
151 let mut futures = Vec::new();
152 store
153 .concurrent_state_mut_without_forcing_current_thread()
154 .take_fibers_and_futures(&mut fibers, &mut futures);
155
156 for mut fiber in fibers {
157 fiber.dispose(store);
158 }
159
160 crate::component::concurrent::tls::set(store, move || drop(futures));
161 }
162
163 #[cfg(feature = "component-model-async")]
164 pub(crate) fn assert_instance_states_empty(&mut self) {
165 for (_, instance) in self.instances.iter_mut() {
166 let Some(instance) = instance.as_mut() else {
167 continue;
168 };
169
170 assert!(instance.get_mut().instance_states().0.iter_mut().all(
171 |(_, state): (_, &mut InstanceState)| state.handle_table().is_empty()
172 && state.concurrent_state().pending_is_empty()
173 ));
174 }
175 }
176
177 pub fn decrement_allocator_resources(&mut self, allocator: &dyn vm::InstanceAllocator) {
178 for _ in 0..self.num_component_instances {
179 allocator.decrement_component_instance_count();
180 }
181 }
182
183 #[cfg(all(feature = "component-model-async", feature = "gc"))]
184 pub fn task_state_mut(&mut self) -> &mut ComponentTaskState {
185 &mut self.task_state
186 }
187}
188
189#[repr(C)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
200pub struct StoreComponentInstanceId {
201 store_id: StoreId,
202 instance: ComponentInstanceId,
203}
204
205impl StoreComponentInstanceId {
206 pub(crate) fn new(
207 store_id: StoreId,
208 instance: ComponentInstanceId,
209 ) -> StoreComponentInstanceId {
210 StoreComponentInstanceId { store_id, instance }
211 }
212
213 #[inline]
214 pub fn assert_belongs_to(&self, store: StoreId) {
215 self.store_id.assert_belongs_to(store)
216 }
217
218 #[inline]
219 pub(crate) fn store_id(&self) -> StoreId {
220 self.store_id
221 }
222
223 #[inline]
224 pub(crate) fn instance(&self) -> ComponentInstanceId {
225 self.instance
226 }
227
228 pub(crate) fn get<'a>(&self, store: &'a StoreOpaque) -> &'a ComponentInstance {
235 self.assert_belongs_to(store.id());
236 store.component_instance(self.instance)
237 }
238
239 pub(crate) fn get_mut<'a>(&self, store: &'a mut StoreOpaque) -> Pin<&'a mut ComponentInstance> {
245 self.from_data_get_mut(store.store_data_mut())
246 }
247
248 #[cfg(feature = "component-model-async")]
254 pub(crate) fn get_mut_and_handle_counter<'a>(
255 &self,
256 store: &'a mut StoreOpaque,
257 ) -> (Pin<&'a mut ComponentInstance>, &'a mut HandleCounter) {
258 let store = store.store_data_mut();
259 self.assert_belongs_to(store.id());
260 store.component_instance_and_handle_counter_mut(self.instance)
261 }
262
263 #[cfg(feature = "component-model-async")]
270 pub(crate) fn get_mut_and_registry<'a>(
271 &self,
272 store: &'a mut StoreOpaque,
273 ) -> (
274 Pin<&'a mut ComponentInstance>,
275 &'a crate::module::ModuleRegistry,
276 ) {
277 let (store_data, registry) = store.store_data_mut_and_registry();
278 let instance = self.from_data_get_mut(store_data);
279 (instance, registry)
280 }
281
282 fn from_data_get_mut<'a>(&self, store: &'a mut StoreData) -> Pin<&'a mut ComponentInstance> {
284 self.assert_belongs_to(store.id());
285 store.component_instance_mut(self.instance)
286 }
287}
288
289impl StoreData {
290 pub(crate) fn push_component_instance(
291 &mut self,
292 data: OwnedComponentInstance,
293 ) -> Result<ComponentInstanceId, OutOfMemory> {
294 let expected = data.get().id();
295 let ret = self.components.instances.push(Some(data))?;
296 assert_eq!(expected, ret);
297 Ok(ret)
298 }
299
300 pub(crate) fn component_instance(&self, id: ComponentInstanceId) -> &ComponentInstance {
301 self.components.instances[id].as_ref().unwrap().get()
302 }
303
304 pub(crate) fn component_instance_mut(
305 &mut self,
306 id: ComponentInstanceId,
307 ) -> Pin<&mut ComponentInstance> {
308 self.components.instances[id].as_mut().unwrap().get_mut()
309 }
310
311 #[cfg(feature = "component-model-async")]
312 pub(crate) fn component_instance_and_handle_counter_mut(
313 &mut self,
314 id: ComponentInstanceId,
315 ) -> (Pin<&mut ComponentInstance>, &mut HandleCounter) {
316 let data = &mut self.components;
317 (
318 data.instances[id].as_mut().unwrap().get_mut(),
319 &mut data.handle_counter,
320 )
321 }
322}
323
324impl StoreOpaque {
325 pub(crate) fn trapped(&self) -> bool {
326 self.store_data().components.trapped
327 }
328
329 pub(crate) fn set_trapped(&mut self) {
330 self.store_data_mut().components.trapped = true;
331 }
332
333 pub(crate) fn may_enter(&mut self) -> bool {
337 !self.trapped()
338 }
339
340 pub(crate) fn component_data(&self) -> &ComponentStoreData {
341 &self.store_data().components
342 }
343
344 pub(crate) fn component_data_mut(&mut self) -> &mut ComponentStoreData {
345 &mut self.store_data_mut().components
346 }
347
348 pub(crate) fn push_component_instance(&mut self, instance: Instance) {
349 let _ = instance;
353
354 self.component_data_mut().num_component_instances += 1;
355 }
356
357 pub(crate) fn component_instance(&self, id: ComponentInstanceId) -> &ComponentInstance {
358 self.store_data().component_instance(id)
359 }
360
361 #[cfg(feature = "component-model-async")]
362 pub(crate) fn component_instance_mut(
363 &mut self,
364 id: ComponentInstanceId,
365 ) -> Pin<&mut ComponentInstance> {
366 self.store_data_mut().component_instance_mut(id)
367 }
368
369 #[cfg(feature = "component-model-async")]
370 pub(crate) fn concurrent_state_mut_without_forcing_current_thread(
371 &mut self,
372 ) -> &mut ConcurrentState {
373 debug_assert!(self.concurrency_support());
374 self.component_data_mut().task_state.concurrent_state_mut()
375 }
376
377 #[cfg(feature = "component-model-async")]
378 pub(crate) fn concurrent_state_mut_already_forced_current_thread(
379 &mut self,
380 ) -> &mut ConcurrentState {
381 debug_assert!(self.concurrency_support());
382 debug_assert!(
383 !self
384 .vm_store_context_mut()
385 .current_thread_mut()
386 .is_deferred()
387 );
388 self.concurrent_state_mut_without_forcing_current_thread()
389 }
390
391 #[cfg(feature = "component-model-async")]
392 pub(crate) fn concurrent_state_mut(&mut self) -> Result<&mut ConcurrentState> {
393 debug_assert!(self.concurrency_support());
394 self.current_thread()?;
395 Ok(self.component_data_mut().task_state.concurrent_state_mut())
396 }
397
398 #[inline]
399 #[cfg(feature = "component-model-async")]
400 pub(crate) fn concurrency_support(&self) -> bool {
401 let support = self.component_data().task_state.is_concurrent();
402 debug_assert_eq!(support, self.engine().tunables().concurrency_support);
403 support
404 }
405
406 pub(crate) fn lift_context_parts(
407 &mut self,
408 instance: Instance,
409 ) -> (
410 &mut ComponentTaskState,
411 &mut HandleTable,
412 &mut HandleCounter,
413 &mut HostResourceData,
414 Pin<&mut ComponentInstance>,
415 ) {
416 let instance = instance.id();
417 instance.assert_belongs_to(self.id());
418 let data = self.component_data_mut();
419 (
420 &mut data.task_state,
421 &mut data.component_host_table,
422 &mut data.handle_counter,
423 &mut data.host_resource_data,
424 data.instances[instance.instance]
425 .as_mut()
426 .unwrap()
427 .get_mut(),
428 )
429 }
430
431 pub(crate) fn component_resource_tables(
432 &mut self,
433 instance: Option<Instance>,
434 ) -> Result<vm::component::ResourceTables<'_>> {
435 Ok(self
436 .component_resource_tables_and_host_resource_data(instance)?
437 .0)
438 }
439
440 pub(crate) fn component_resource_tables_and_host_resource_data(
441 &mut self,
442 instance: Option<Instance>,
443 ) -> Result<(
444 vm::component::ResourceTables<'_>,
445 &mut crate::component::HostResourceData,
446 )> {
447 let current_scope = self.current_scope()?;
448
449 let store_id = self.id();
450 let data = self.component_data_mut();
451 let guest = instance.map(|i| {
452 let i = i.id();
453 i.assert_belongs_to(store_id);
454 data.instances[i.instance]
455 .as_mut()
456 .unwrap()
457 .get_mut()
458 .instance_states()
459 });
460
461 Ok((
462 vm::component::ResourceTables {
463 host_table: &mut data.component_host_table,
464 handle_counter: &mut data.handle_counter,
465 task_state: &mut data.task_state,
466 guest,
467 current_scope,
468 },
469 &mut data.host_resource_data,
470 ))
471 }
472
473 pub(crate) fn enter_call_not_concurrent(&mut self) -> Result<()> {
474 let state = match &mut self.component_data_mut().task_state {
475 ComponentTaskState::NotConcurrent(state) => state,
476 #[cfg(feature = "component-model-async")]
477 ComponentTaskState::Concurrent(_) => unreachable!(),
478 };
479 state.scopes.push(CallContext::default())?;
480 Ok(())
481 }
482
483 pub(crate) fn exit_call_not_concurrent(&mut self) {
484 let state = match &mut self.component_data_mut().task_state {
485 ComponentTaskState::NotConcurrent(state) => state,
486 #[cfg(feature = "component-model-async")]
487 ComponentTaskState::Concurrent(_) => unreachable!(),
488 };
489 state.scopes.pop();
490 }
491
492 pub(crate) fn hostcall_fuel(&self) -> usize {
493 self.component_data().hostcall_fuel
494 }
495
496 pub(crate) fn set_hostcall_fuel(&mut self, fuel: usize) {
497 self.component_data_mut().hostcall_fuel = fuel;
498 }
499
500 pub(crate) fn max_component_handles(&self) -> usize {
501 self.component_data().handle_counter.limit()
502 }
503
504 pub(crate) fn set_max_component_handles(&mut self, limit: usize) {
505 self.component_data_mut().handle_counter.set_limit(limit);
506 }
507
508 #[cfg(feature = "component-model-async")]
509 fn concurrent_resource_table(&mut self) -> Option<&mut ResourceTable> {
510 if self.concurrency_support() {
511 Some(
512 self.concurrent_state_mut_without_forcing_current_thread()
513 .table(),
514 )
515 } else {
516 None
517 }
518 }
519
520 pub(crate) fn current_scope_id_not_concurrent(&mut self) -> Result<Option<u32>> {
521 match &mut self.component_data_mut().task_state {
522 ComponentTaskState::NotConcurrent(state) => match state.scopes.len().checked_sub(1) {
523 Some(i) => Ok(Some(u32::try_from(i)?)),
524 None => Ok(None),
525 },
526 #[cfg(feature = "component-model-async")]
527 ComponentTaskState::Concurrent(_) => crate::bail_bug!("should not be reachable"),
528 }
529 }
530}
531
532impl<T> Store<T> {
533 pub fn hostcall_fuel(&self) -> usize {
541 self.as_context().0.hostcall_fuel()
542 }
543
544 pub fn set_hostcall_fuel(&mut self, fuel: usize) {
568 self.as_context_mut().set_hostcall_fuel(fuel)
569 }
570
571 pub fn max_component_handles(&self) -> usize {
579 self.as_context().0.max_component_handles()
580 }
581
582 pub fn set_max_component_handles(&mut self, limit: usize) {
597 self.as_context_mut().set_max_component_handles(limit)
598 }
599
600 #[cfg(feature = "component-model-async")]
618 pub fn concurrent_resource_table(&mut self) -> Option<&mut ResourceTable> {
619 self.as_context_mut().0.concurrent_resource_table()
620 }
621}
622
623impl<T> StoreContextMut<'_, T> {
624 pub fn hostcall_fuel(&self) -> usize {
626 self.0.hostcall_fuel()
627 }
628
629 pub fn set_hostcall_fuel(&mut self, fuel: usize) {
631 self.0.set_hostcall_fuel(fuel)
632 }
633
634 pub fn max_component_handles(&self) -> usize {
636 self.0.max_component_handles()
637 }
638
639 pub fn set_max_component_handles(&mut self, limit: usize) {
641 self.0.set_max_component_handles(limit)
642 }
643
644 #[cfg(feature = "component-model-async")]
646 pub fn concurrent_resource_table(&mut self) -> Option<&mut ResourceTable> {
647 self.0.concurrent_resource_table()
648 }
649}
650
651#[derive(Default)]
652pub struct ComponentTasksNotConcurrent {
653 scopes: TryVec<CallContext>,
654}
655
656impl ComponentTaskState {
657 pub fn call_context(&mut self, id: Scope) -> Result<&mut CallContext> {
658 match self {
659 ComponentTaskState::NotConcurrent(state) => match id {
660 Scope::Id(id) => Ok(&mut state.scopes[id as usize]),
661 Scope::HostId(_) => bail_bug!("non-concurrent scope cannot be a host ID"),
662 },
663 #[cfg(feature = "component-model-async")]
664 ComponentTaskState::Concurrent(state) => state.call_context(id),
665 }
666 }
667
668 pub(crate) fn materialize_current_scope(&mut self) -> Result<Scope> {
669 match self {
670 ComponentTaskState::NotConcurrent(_) => {
671 bail_bug!("a non-concurrent scope cannot be deferred")
672 }
673 #[cfg(feature = "component-model-async")]
674 ComponentTaskState::Concurrent(state) => state.materialize_current_scope(),
675 }
676 }
677
678 pub(crate) fn deferred_host_call_context(&mut self) -> Option<&mut CallContext> {
679 match self {
680 ComponentTaskState::NotConcurrent(_) => None,
681 #[cfg(feature = "component-model-async")]
682 ComponentTaskState::Concurrent(state) => state.deferred_host_call_context(),
683 }
684 }
685
686 #[cfg(feature = "component-model-async")]
687 pub fn concurrent_state_mut(&mut self) -> &mut ConcurrentState {
688 match self {
689 ComponentTaskState::Concurrent(state) => state,
690 ComponentTaskState::NotConcurrent(_) => {
691 panic!("expected concurrent state to be present")
692 }
693 }
694 }
695
696 #[cfg(feature = "component-model-async")]
697 fn is_concurrent(&self) -> bool {
698 match self {
699 ComponentTaskState::Concurrent(_) => true,
700 ComponentTaskState::NotConcurrent(_) => false,
701 }
702 }
703}