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,
6};
7use crate::store::{StoreData, StoreId, StoreOpaque};
8use crate::{AsContext, AsContextMut, Engine, Store, StoreContextMut};
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 ComponentStoreData::drop_fibers_and_futures(store.0);
126 }
127 #[cfg(not(feature = "component-model-async"))]
128 let _ = store;
129 }
130
131 pub fn next_component_instance_id(&self) -> ComponentInstanceId {
132 self.instances.next_key()
133 }
134
135 #[cfg(feature = "component-model-async")]
136 pub(crate) fn drop_fibers_and_futures(store: &mut dyn VMStore) {
137 let mut fibers = Vec::new();
138 let mut futures = Vec::new();
139 store
140 .concurrent_state_mut_without_forcing_current_thread()
141 .take_fibers_and_futures(&mut fibers, &mut futures);
142
143 for mut fiber in fibers {
144 fiber.dispose(store);
145 }
146
147 crate::component::concurrent::tls::set(store, move || drop(futures));
148 }
149
150 #[cfg(feature = "component-model-async")]
151 pub(crate) fn assert_instance_states_empty(&mut self) {
152 for (_, instance) in self.instances.iter_mut() {
153 let Some(instance) = instance.as_mut() else {
154 continue;
155 };
156
157 assert!(instance.get_mut().instance_states().0.iter_mut().all(
158 |(_, state): (_, &mut InstanceState)| state.handle_table().is_empty()
159 && state.concurrent_state().pending_is_empty()
160 ));
161 }
162 }
163
164 pub fn decrement_allocator_resources(&mut self, allocator: &dyn vm::InstanceAllocator) {
165 for _ in 0..self.num_component_instances {
166 allocator.decrement_component_instance_count();
167 }
168 }
169
170 #[cfg(all(feature = "component-model-async", feature = "gc"))]
171 pub fn task_state_mut(&mut self) -> &mut ComponentTaskState {
172 &mut self.task_state
173 }
174}
175
176#[repr(C)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
187pub struct StoreComponentInstanceId {
188 store_id: StoreId,
189 instance: ComponentInstanceId,
190}
191
192impl StoreComponentInstanceId {
193 pub(crate) fn new(
194 store_id: StoreId,
195 instance: ComponentInstanceId,
196 ) -> StoreComponentInstanceId {
197 StoreComponentInstanceId { store_id, instance }
198 }
199
200 #[inline]
201 pub fn assert_belongs_to(&self, store: StoreId) {
202 self.store_id.assert_belongs_to(store)
203 }
204
205 #[inline]
206 pub(crate) fn store_id(&self) -> StoreId {
207 self.store_id
208 }
209
210 #[inline]
211 pub(crate) fn instance(&self) -> ComponentInstanceId {
212 self.instance
213 }
214
215 pub(crate) fn get<'a>(&self, store: &'a StoreOpaque) -> &'a ComponentInstance {
222 self.assert_belongs_to(store.id());
223 store.component_instance(self.instance)
224 }
225
226 pub(crate) fn get_mut<'a>(&self, store: &'a mut StoreOpaque) -> Pin<&'a mut ComponentInstance> {
232 self.from_data_get_mut(store.store_data_mut())
233 }
234
235 #[cfg(feature = "component-model-async")]
242 pub(crate) fn get_mut_and_registry<'a>(
243 &self,
244 store: &'a mut StoreOpaque,
245 ) -> (
246 Pin<&'a mut ComponentInstance>,
247 &'a crate::module::ModuleRegistry,
248 ) {
249 let (store_data, registry) = store.store_data_mut_and_registry();
250 let instance = self.from_data_get_mut(store_data);
251 (instance, registry)
252 }
253
254 fn from_data_get_mut<'a>(&self, store: &'a mut StoreData) -> Pin<&'a mut ComponentInstance> {
256 self.assert_belongs_to(store.id());
257 store.component_instance_mut(self.instance)
258 }
259}
260
261impl StoreData {
262 pub(crate) fn push_component_instance(
263 &mut self,
264 data: OwnedComponentInstance,
265 ) -> Result<ComponentInstanceId, OutOfMemory> {
266 let expected = data.get().id();
267 let ret = self.components.instances.push(Some(data))?;
268 assert_eq!(expected, ret);
269 Ok(ret)
270 }
271
272 pub(crate) fn component_instance(&self, id: ComponentInstanceId) -> &ComponentInstance {
273 self.components.instances[id].as_ref().unwrap().get()
274 }
275
276 pub(crate) fn component_instance_mut(
277 &mut self,
278 id: ComponentInstanceId,
279 ) -> Pin<&mut ComponentInstance> {
280 self.components.instances[id].as_mut().unwrap().get_mut()
281 }
282}
283
284impl StoreOpaque {
285 pub(crate) fn trapped(&self) -> bool {
286 self.store_data().components.trapped
287 }
288
289 pub(crate) fn set_trapped(&mut self) {
290 self.store_data_mut().components.trapped = true;
291 }
292
293 pub(crate) fn may_enter(&mut self) -> bool {
297 !self.trapped()
298 }
299
300 pub(crate) fn component_data(&self) -> &ComponentStoreData {
301 &self.store_data().components
302 }
303
304 pub(crate) fn component_data_mut(&mut self) -> &mut ComponentStoreData {
305 &mut self.store_data_mut().components
306 }
307
308 pub(crate) fn push_component_instance(&mut self, instance: Instance) {
309 let _ = instance;
313
314 self.component_data_mut().num_component_instances += 1;
315 }
316
317 pub(crate) fn component_instance(&self, id: ComponentInstanceId) -> &ComponentInstance {
318 self.store_data().component_instance(id)
319 }
320
321 #[cfg(feature = "component-model-async")]
322 pub(crate) fn component_instance_mut(
323 &mut self,
324 id: ComponentInstanceId,
325 ) -> Pin<&mut ComponentInstance> {
326 self.store_data_mut().component_instance_mut(id)
327 }
328
329 #[cfg(feature = "component-model-async")]
330 pub(crate) fn concurrent_state_mut_without_forcing_current_thread(
331 &mut self,
332 ) -> &mut ConcurrentState {
333 debug_assert!(self.concurrency_support());
334 self.component_data_mut().task_state.concurrent_state_mut()
335 }
336
337 #[cfg(feature = "component-model-async")]
338 pub(crate) fn concurrent_state_mut_already_forced_current_thread(
339 &mut self,
340 ) -> &mut ConcurrentState {
341 debug_assert!(self.concurrency_support());
342 debug_assert!(
343 !self
344 .vm_store_context_mut()
345 .current_thread_mut()
346 .is_deferred()
347 );
348 self.concurrent_state_mut_without_forcing_current_thread()
349 }
350
351 #[cfg(feature = "component-model-async")]
352 pub(crate) fn concurrent_state_mut(&mut self) -> Result<&mut ConcurrentState> {
353 debug_assert!(self.concurrency_support());
354 self.current_thread()?;
355 Ok(self.component_data_mut().task_state.concurrent_state_mut())
356 }
357
358 #[inline]
359 #[cfg(feature = "component-model-async")]
360 pub(crate) fn concurrency_support(&self) -> bool {
361 let support = self.component_data().task_state.is_concurrent();
362 debug_assert_eq!(support, self.engine().tunables().concurrency_support);
363 support
364 }
365
366 pub(crate) fn lift_context_parts(
367 &mut self,
368 instance: Instance,
369 ) -> (
370 &mut ComponentTaskState,
371 &mut HandleTable,
372 &mut HostResourceData,
373 Pin<&mut ComponentInstance>,
374 ) {
375 let instance = instance.id();
376 instance.assert_belongs_to(self.id());
377 let data = self.component_data_mut();
378 (
379 &mut data.task_state,
380 &mut data.component_host_table,
381 &mut data.host_resource_data,
382 data.instances[instance.instance]
383 .as_mut()
384 .unwrap()
385 .get_mut(),
386 )
387 }
388
389 pub(crate) fn component_resource_tables(
390 &mut self,
391 instance: Option<Instance>,
392 ) -> Result<vm::component::ResourceTables<'_>> {
393 Ok(self
394 .component_resource_tables_and_host_resource_data(instance)?
395 .0)
396 }
397
398 pub(crate) fn component_resource_tables_and_host_resource_data(
399 &mut self,
400 instance: Option<Instance>,
401 ) -> Result<(
402 vm::component::ResourceTables<'_>,
403 &mut crate::component::HostResourceData,
404 )> {
405 let current_scope_id = self.current_scope_id()?;
406
407 let store_id = self.id();
408 let data = self.component_data_mut();
409 let guest = instance.map(|i| {
410 let i = i.id();
411 i.assert_belongs_to(store_id);
412 data.instances[i.instance]
413 .as_mut()
414 .unwrap()
415 .get_mut()
416 .instance_states()
417 });
418
419 Ok((
420 vm::component::ResourceTables {
421 host_table: &mut data.component_host_table,
422 task_state: &mut data.task_state,
423 guest,
424 current_scope_id,
425 },
426 &mut data.host_resource_data,
427 ))
428 }
429
430 pub(crate) fn enter_call_not_concurrent(&mut self) -> Result<()> {
431 let state = match &mut self.component_data_mut().task_state {
432 ComponentTaskState::NotConcurrent(state) => state,
433 #[cfg(feature = "component-model-async")]
434 ComponentTaskState::Concurrent(_) => unreachable!(),
435 };
436 state.scopes.push(CallContext::default())?;
437 Ok(())
438 }
439
440 pub(crate) fn exit_call_not_concurrent(&mut self) {
441 let state = match &mut self.component_data_mut().task_state {
442 ComponentTaskState::NotConcurrent(state) => state,
443 #[cfg(feature = "component-model-async")]
444 ComponentTaskState::Concurrent(_) => unreachable!(),
445 };
446 state.scopes.pop();
447 }
448
449 pub(crate) fn hostcall_fuel(&self) -> usize {
450 self.component_data().hostcall_fuel
451 }
452
453 pub(crate) fn set_hostcall_fuel(&mut self, fuel: usize) {
454 self.component_data_mut().hostcall_fuel = fuel;
455 }
456
457 #[cfg(feature = "component-model-async")]
458 fn concurrent_resource_table(&mut self) -> Option<&mut ResourceTable> {
459 if self.concurrency_support() {
460 Some(
461 self.concurrent_state_mut_without_forcing_current_thread()
462 .table(),
463 )
464 } else {
465 None
466 }
467 }
468
469 pub(crate) fn current_scope_id_not_concurrent(&mut self) -> Result<Option<u32>> {
470 match &mut self.component_data_mut().task_state {
471 ComponentTaskState::NotConcurrent(state) => match state.scopes.len().checked_sub(1) {
472 Some(i) => Ok(Some(u32::try_from(i)?)),
473 None => Ok(None),
474 },
475 #[cfg(feature = "component-model-async")]
476 ComponentTaskState::Concurrent(_) => crate::bail_bug!("should not be reachable"),
477 }
478 }
479}
480
481impl<T> Store<T> {
482 pub fn hostcall_fuel(&self) -> usize {
490 self.as_context().0.hostcall_fuel()
491 }
492
493 pub fn set_hostcall_fuel(&mut self, fuel: usize) {
517 self.as_context_mut().set_hostcall_fuel(fuel)
518 }
519
520 #[cfg(feature = "component-model-async")]
527 pub fn concurrent_resource_table(&mut self) -> Option<&mut ResourceTable> {
528 self.as_context_mut().0.concurrent_resource_table()
529 }
530}
531
532impl<T> StoreContextMut<'_, T> {
533 pub fn hostcall_fuel(&self) -> usize {
535 self.0.hostcall_fuel()
536 }
537
538 pub fn set_hostcall_fuel(&mut self, fuel: usize) {
540 self.0.set_hostcall_fuel(fuel)
541 }
542
543 #[cfg(feature = "component-model-async")]
545 pub fn concurrent_resource_table(&mut self) -> Option<&mut ResourceTable> {
546 self.0.concurrent_resource_table()
547 }
548}
549
550#[derive(Default)]
551pub struct ComponentTasksNotConcurrent {
552 scopes: TryVec<CallContext>,
553}
554
555impl ComponentTaskState {
556 pub fn call_context(&mut self, id: u32) -> Result<&mut CallContext> {
557 match self {
558 ComponentTaskState::NotConcurrent(state) => Ok(&mut state.scopes[id as usize]),
559 #[cfg(feature = "component-model-async")]
560 ComponentTaskState::Concurrent(state) => state.call_context(id),
561 }
562 }
563
564 #[cfg(feature = "component-model-async")]
565 pub fn concurrent_state_mut(&mut self) -> &mut ConcurrentState {
566 match self {
567 ComponentTaskState::Concurrent(state) => state,
568 ComponentTaskState::NotConcurrent(_) => {
569 panic!("expected concurrent state to be present")
570 }
571 }
572 }
573
574 #[cfg(feature = "component-model-async")]
575 fn is_concurrent(&self) -> bool {
576 match self {
577 ComponentTaskState::Concurrent(_) => true,
578 ComponentTaskState::NotConcurrent(_) => false,
579 }
580 }
581}