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, 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
29pub struct ComponentStoreData {
32 instances: TryPrimaryMap<ComponentInstanceId, Option<OwnedComponentInstance>>,
35
36 trapped: bool,
38
39 num_component_instances: usize,
42
43 component_host_table: HandleTable,
47 host_resource_data: HostResourceData,
48
49 task_state: ComponentTaskState,
52
53 hostcall_fuel: usize,
59}
60
61pub enum ComponentTaskState {
63 NotConcurrent(ComponentTasksNotConcurrent),
66
67 #[cfg(feature = "component-model-async")]
70 Concurrent(ConcurrentState),
71}
72
73#[derive(Copy, Clone, Debug, PartialEq, Eq)]
74pub struct ComponentInstanceId(u32);
75wasmtime_environ::entity_impl!(ComponentInstanceId);
76
77#[derive(Debug, Copy, Clone, PartialEq, Eq)]
78pub struct RuntimeInstance {
79 pub instance: ComponentInstanceId,
80 pub index: RuntimeComponentInstanceIndex,
81}
82
83impl ComponentStoreData {
84 pub fn new(engine: &Engine) -> ComponentStoreData {
85 ComponentStoreData {
86 instances: Default::default(),
87 trapped: false,
88 num_component_instances: 0,
89 component_host_table: Default::default(),
90 host_resource_data: Default::default(),
91 task_state: if engine.tunables().concurrency_support {
92 #[cfg(feature = "component-model-async")]
93 {
94 ComponentTaskState::Concurrent(Default::default())
95 }
96 #[cfg(not(feature = "component-model-async"))]
97 {
98 unreachable!()
102 }
103 } else {
104 ComponentTaskState::NotConcurrent(Default::default())
105 },
106 hostcall_fuel: DEFAULT_HOSTCALL_FUEL,
107 }
108 }
109
110 pub fn run_manual_drop_routines<T>(store: StoreContextMut<T>) {
115 #[cfg(feature = "component-model-async")]
124 if store.0.component_data().task_state.is_concurrent() {
125 store.0.clean_up_task_groups();
126
127 ComponentStoreData::drop_fibers_and_futures(store.0);
128 }
129 #[cfg(not(feature = "component-model-async"))]
130 let _ = store;
131 }
132
133 pub fn next_component_instance_id(&self) -> ComponentInstanceId {
134 self.instances.next_key()
135 }
136
137 #[cfg(feature = "component-model-async")]
138 pub(crate) fn drop_fibers_and_futures(store: &mut dyn VMStore) {
139 let mut fibers = Vec::new();
140 let mut futures = Vec::new();
141 store
142 .concurrent_state_mut_without_forcing_current_thread()
143 .take_fibers_and_futures(&mut fibers, &mut futures);
144
145 for mut fiber in fibers {
146 fiber.dispose(store);
147 }
148
149 crate::component::concurrent::tls::set(store, move || drop(futures));
150 }
151
152 #[cfg(feature = "component-model-async")]
153 pub(crate) fn assert_instance_states_empty(&mut self) {
154 for (_, instance) in self.instances.iter_mut() {
155 let Some(instance) = instance.as_mut() else {
156 continue;
157 };
158
159 assert!(instance.get_mut().instance_states().0.iter_mut().all(
160 |(_, state): (_, &mut InstanceState)| state.handle_table().is_empty()
161 && state.concurrent_state().pending_is_empty()
162 ));
163 }
164 }
165
166 pub fn decrement_allocator_resources(&mut self, allocator: &dyn vm::InstanceAllocator) {
167 for _ in 0..self.num_component_instances {
168 allocator.decrement_component_instance_count();
169 }
170 }
171
172 #[cfg(all(feature = "component-model-async", feature = "gc"))]
173 pub fn task_state_mut(&mut self) -> &mut ComponentTaskState {
174 &mut self.task_state
175 }
176}
177
178#[repr(C)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
189pub struct StoreComponentInstanceId {
190 store_id: StoreId,
191 instance: ComponentInstanceId,
192}
193
194impl StoreComponentInstanceId {
195 pub(crate) fn new(
196 store_id: StoreId,
197 instance: ComponentInstanceId,
198 ) -> StoreComponentInstanceId {
199 StoreComponentInstanceId { store_id, instance }
200 }
201
202 #[inline]
203 pub fn assert_belongs_to(&self, store: StoreId) {
204 self.store_id.assert_belongs_to(store)
205 }
206
207 #[inline]
208 pub(crate) fn store_id(&self) -> StoreId {
209 self.store_id
210 }
211
212 #[inline]
213 pub(crate) fn instance(&self) -> ComponentInstanceId {
214 self.instance
215 }
216
217 pub(crate) fn get<'a>(&self, store: &'a StoreOpaque) -> &'a ComponentInstance {
224 self.assert_belongs_to(store.id());
225 store.component_instance(self.instance)
226 }
227
228 pub(crate) fn get_mut<'a>(&self, store: &'a mut StoreOpaque) -> Pin<&'a mut ComponentInstance> {
234 self.from_data_get_mut(store.store_data_mut())
235 }
236
237 #[cfg(feature = "component-model-async")]
244 pub(crate) fn get_mut_and_registry<'a>(
245 &self,
246 store: &'a mut StoreOpaque,
247 ) -> (
248 Pin<&'a mut ComponentInstance>,
249 &'a crate::module::ModuleRegistry,
250 ) {
251 let (store_data, registry) = store.store_data_mut_and_registry();
252 let instance = self.from_data_get_mut(store_data);
253 (instance, registry)
254 }
255
256 fn from_data_get_mut<'a>(&self, store: &'a mut StoreData) -> Pin<&'a mut ComponentInstance> {
258 self.assert_belongs_to(store.id());
259 store.component_instance_mut(self.instance)
260 }
261}
262
263impl StoreData {
264 pub(crate) fn push_component_instance(
265 &mut self,
266 data: OwnedComponentInstance,
267 ) -> Result<ComponentInstanceId, OutOfMemory> {
268 let expected = data.get().id();
269 let ret = self.components.instances.push(Some(data))?;
270 assert_eq!(expected, ret);
271 Ok(ret)
272 }
273
274 pub(crate) fn component_instance(&self, id: ComponentInstanceId) -> &ComponentInstance {
275 self.components.instances[id].as_ref().unwrap().get()
276 }
277
278 pub(crate) fn component_instance_mut(
279 &mut self,
280 id: ComponentInstanceId,
281 ) -> Pin<&mut ComponentInstance> {
282 self.components.instances[id].as_mut().unwrap().get_mut()
283 }
284}
285
286impl StoreOpaque {
287 pub(crate) fn trapped(&self) -> bool {
288 self.store_data().components.trapped
289 }
290
291 pub(crate) fn set_trapped(&mut self) {
292 self.store_data_mut().components.trapped = true;
293 self.clean_up_task_groups();
294 }
295
296 pub(crate) fn may_enter(&mut self) -> bool {
300 !self.trapped()
301 }
302
303 pub(crate) fn component_data(&self) -> &ComponentStoreData {
304 &self.store_data().components
305 }
306
307 pub(crate) fn component_data_mut(&mut self) -> &mut ComponentStoreData {
308 &mut self.store_data_mut().components
309 }
310
311 pub(crate) fn push_component_instance(&mut self, instance: Instance) {
312 let _ = instance;
316
317 self.component_data_mut().num_component_instances += 1;
318 }
319
320 pub(crate) fn component_instance(&self, id: ComponentInstanceId) -> &ComponentInstance {
321 self.store_data().component_instance(id)
322 }
323
324 #[cfg(feature = "component-model-async")]
325 pub(crate) fn component_instance_mut(
326 &mut self,
327 id: ComponentInstanceId,
328 ) -> Pin<&mut ComponentInstance> {
329 self.store_data_mut().component_instance_mut(id)
330 }
331
332 #[cfg(feature = "component-model-async")]
333 pub(crate) fn concurrent_state_mut_without_forcing_current_thread(
334 &mut self,
335 ) -> &mut ConcurrentState {
336 debug_assert!(self.concurrency_support());
337 self.component_data_mut().task_state.concurrent_state_mut()
338 }
339
340 #[cfg(feature = "component-model-async")]
341 pub(crate) fn concurrent_state_mut_already_forced_current_thread(
342 &mut self,
343 ) -> &mut ConcurrentState {
344 debug_assert!(self.concurrency_support());
345 debug_assert!(
346 !self
347 .vm_store_context_mut()
348 .current_thread_mut()
349 .is_deferred()
350 );
351 self.concurrent_state_mut_without_forcing_current_thread()
352 }
353
354 #[cfg(feature = "component-model-async")]
355 pub(crate) fn concurrent_state_mut(&mut self) -> Result<&mut ConcurrentState> {
356 debug_assert!(self.concurrency_support());
357 self.current_thread()?;
358 Ok(self.component_data_mut().task_state.concurrent_state_mut())
359 }
360
361 #[inline]
362 #[cfg(feature = "component-model-async")]
363 pub(crate) fn concurrency_support(&self) -> bool {
364 let support = self.component_data().task_state.is_concurrent();
365 debug_assert_eq!(support, self.engine().tunables().concurrency_support);
366 support
367 }
368
369 pub(crate) fn lift_context_parts(
370 &mut self,
371 instance: Instance,
372 ) -> (
373 &mut ComponentTaskState,
374 &mut HandleTable,
375 &mut HostResourceData,
376 Pin<&mut ComponentInstance>,
377 ) {
378 let instance = instance.id();
379 instance.assert_belongs_to(self.id());
380 let data = self.component_data_mut();
381 (
382 &mut data.task_state,
383 &mut data.component_host_table,
384 &mut data.host_resource_data,
385 data.instances[instance.instance]
386 .as_mut()
387 .unwrap()
388 .get_mut(),
389 )
390 }
391
392 pub(crate) fn component_resource_tables(
393 &mut self,
394 instance: Option<Instance>,
395 ) -> Result<vm::component::ResourceTables<'_>> {
396 Ok(self
397 .component_resource_tables_and_host_resource_data(instance)?
398 .0)
399 }
400
401 pub(crate) fn component_resource_tables_and_host_resource_data(
402 &mut self,
403 instance: Option<Instance>,
404 ) -> Result<(
405 vm::component::ResourceTables<'_>,
406 &mut crate::component::HostResourceData,
407 )> {
408 let current_scope = self.current_scope()?;
409
410 let store_id = self.id();
411 let data = self.component_data_mut();
412 let guest = instance.map(|i| {
413 let i = i.id();
414 i.assert_belongs_to(store_id);
415 data.instances[i.instance]
416 .as_mut()
417 .unwrap()
418 .get_mut()
419 .instance_states()
420 });
421
422 Ok((
423 vm::component::ResourceTables {
424 host_table: &mut data.component_host_table,
425 task_state: &mut data.task_state,
426 guest,
427 current_scope,
428 },
429 &mut data.host_resource_data,
430 ))
431 }
432
433 pub(crate) fn enter_call_not_concurrent(&mut self) -> Result<()> {
434 let state = match &mut self.component_data_mut().task_state {
435 ComponentTaskState::NotConcurrent(state) => state,
436 #[cfg(feature = "component-model-async")]
437 ComponentTaskState::Concurrent(_) => unreachable!(),
438 };
439 state.scopes.push(CallContext::default())?;
440 Ok(())
441 }
442
443 pub(crate) fn exit_call_not_concurrent(&mut self) {
444 let state = match &mut self.component_data_mut().task_state {
445 ComponentTaskState::NotConcurrent(state) => state,
446 #[cfg(feature = "component-model-async")]
447 ComponentTaskState::Concurrent(_) => unreachable!(),
448 };
449 state.scopes.pop();
450 }
451
452 pub(crate) fn hostcall_fuel(&self) -> usize {
453 self.component_data().hostcall_fuel
454 }
455
456 pub(crate) fn set_hostcall_fuel(&mut self, fuel: usize) {
457 self.component_data_mut().hostcall_fuel = fuel;
458 }
459
460 #[cfg(feature = "component-model-async")]
461 fn concurrent_resource_table(&mut self) -> Option<&mut ResourceTable> {
462 if self.concurrency_support() {
463 Some(
464 self.concurrent_state_mut_without_forcing_current_thread()
465 .table(),
466 )
467 } else {
468 None
469 }
470 }
471
472 pub(crate) fn current_scope_id_not_concurrent(&mut self) -> Result<Option<u32>> {
473 match &mut self.component_data_mut().task_state {
474 ComponentTaskState::NotConcurrent(state) => match state.scopes.len().checked_sub(1) {
475 Some(i) => Ok(Some(u32::try_from(i)?)),
476 None => Ok(None),
477 },
478 #[cfg(feature = "component-model-async")]
479 ComponentTaskState::Concurrent(_) => crate::bail_bug!("should not be reachable"),
480 }
481 }
482}
483
484impl<T> Store<T> {
485 pub fn hostcall_fuel(&self) -> usize {
493 self.as_context().0.hostcall_fuel()
494 }
495
496 pub fn set_hostcall_fuel(&mut self, fuel: usize) {
520 self.as_context_mut().set_hostcall_fuel(fuel)
521 }
522
523 #[cfg(feature = "component-model-async")]
530 pub fn concurrent_resource_table(&mut self) -> Option<&mut ResourceTable> {
531 self.as_context_mut().0.concurrent_resource_table()
532 }
533}
534
535impl<T> StoreContextMut<'_, T> {
536 pub fn hostcall_fuel(&self) -> usize {
538 self.0.hostcall_fuel()
539 }
540
541 pub fn set_hostcall_fuel(&mut self, fuel: usize) {
543 self.0.set_hostcall_fuel(fuel)
544 }
545
546 #[cfg(feature = "component-model-async")]
548 pub fn concurrent_resource_table(&mut self) -> Option<&mut ResourceTable> {
549 self.0.concurrent_resource_table()
550 }
551}
552
553#[derive(Default)]
554pub struct ComponentTasksNotConcurrent {
555 scopes: TryVec<CallContext>,
556}
557
558impl ComponentTaskState {
559 pub fn call_context(&mut self, id: Scope) -> Result<&mut CallContext> {
560 match self {
561 ComponentTaskState::NotConcurrent(state) => match id {
562 Scope::Id(id) => Ok(&mut state.scopes[id as usize]),
563 Scope::HostId(_) => bail_bug!("non-concurrent scope cannot be a host ID"),
564 },
565 #[cfg(feature = "component-model-async")]
566 ComponentTaskState::Concurrent(state) => state.call_context(id),
567 }
568 }
569
570 pub(crate) fn materialize_current_scope(&mut self) -> Result<Scope> {
571 match self {
572 ComponentTaskState::NotConcurrent(_) => {
573 bail_bug!("a non-concurrent scope cannot be deferred")
574 }
575 #[cfg(feature = "component-model-async")]
576 ComponentTaskState::Concurrent(state) => state.materialize_current_scope(),
577 }
578 }
579
580 pub(crate) fn deferred_host_call_context(&mut self) -> Option<&mut CallContext> {
581 match self {
582 ComponentTaskState::NotConcurrent(_) => None,
583 #[cfg(feature = "component-model-async")]
584 ComponentTaskState::Concurrent(state) => state.deferred_host_call_context(),
585 }
586 }
587
588 #[cfg(feature = "component-model-async")]
589 pub fn concurrent_state_mut(&mut self) -> &mut ConcurrentState {
590 match self {
591 ComponentTaskState::Concurrent(state) => state,
592 ComponentTaskState::NotConcurrent(_) => {
593 panic!("expected concurrent state to be present")
594 }
595 }
596 }
597
598 #[cfg(feature = "component-model-async")]
599 fn is_concurrent(&self) -> bool {
600 match self {
601 ComponentTaskState::Concurrent(_) => true,
602 ComponentTaskState::NotConcurrent(_) => false,
603 }
604 }
605}