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 component_data(&self) -> &ComponentStoreData {
294 &self.store_data().components
295 }
296
297 pub(crate) fn component_data_mut(&mut self) -> &mut ComponentStoreData {
298 &mut self.store_data_mut().components
299 }
300
301 pub(crate) fn push_component_instance(&mut self, instance: Instance) {
302 let _ = instance;
306
307 self.component_data_mut().num_component_instances += 1;
308 }
309
310 pub(crate) fn component_instance(&self, id: ComponentInstanceId) -> &ComponentInstance {
311 self.store_data().component_instance(id)
312 }
313
314 #[cfg(feature = "component-model-async")]
315 pub(crate) fn component_instance_mut(
316 &mut self,
317 id: ComponentInstanceId,
318 ) -> Pin<&mut ComponentInstance> {
319 self.store_data_mut().component_instance_mut(id)
320 }
321
322 #[cfg(feature = "component-model-async")]
323 pub(crate) fn concurrent_state_mut_without_forcing_current_thread(
324 &mut self,
325 ) -> &mut ConcurrentState {
326 debug_assert!(self.concurrency_support());
327 self.component_data_mut().task_state.concurrent_state_mut()
328 }
329
330 #[cfg(feature = "component-model-async")]
331 pub(crate) fn concurrent_state_mut_already_forced_current_thread(
332 &mut self,
333 ) -> &mut ConcurrentState {
334 debug_assert!(self.concurrency_support());
335 debug_assert!(
336 !self
337 .vm_store_context_mut()
338 .current_thread_mut()
339 .is_deferred()
340 );
341 self.concurrent_state_mut_without_forcing_current_thread()
342 }
343
344 #[cfg(feature = "component-model-async")]
345 pub(crate) fn concurrent_state_mut(&mut self) -> Result<&mut ConcurrentState> {
346 debug_assert!(self.concurrency_support());
347 self.current_thread()?;
348 Ok(self.component_data_mut().task_state.concurrent_state_mut())
349 }
350
351 #[inline]
352 #[cfg(feature = "component-model-async")]
353 pub(crate) fn concurrency_support(&self) -> bool {
354 let support = self.component_data().task_state.is_concurrent();
355 debug_assert_eq!(support, self.engine().tunables().concurrency_support);
356 support
357 }
358
359 pub(crate) fn lift_context_parts(
360 &mut self,
361 instance: Instance,
362 ) -> (
363 &mut ComponentTaskState,
364 &mut HandleTable,
365 &mut HostResourceData,
366 Pin<&mut ComponentInstance>,
367 ) {
368 let instance = instance.id();
369 instance.assert_belongs_to(self.id());
370 let data = self.component_data_mut();
371 (
372 &mut data.task_state,
373 &mut data.component_host_table,
374 &mut data.host_resource_data,
375 data.instances[instance.instance]
376 .as_mut()
377 .unwrap()
378 .get_mut(),
379 )
380 }
381
382 pub(crate) fn component_resource_tables(
383 &mut self,
384 instance: Option<Instance>,
385 ) -> Result<vm::component::ResourceTables<'_>> {
386 Ok(self
387 .component_resource_tables_and_host_resource_data(instance)?
388 .0)
389 }
390
391 pub(crate) fn component_resource_tables_and_host_resource_data(
392 &mut self,
393 instance: Option<Instance>,
394 ) -> Result<(
395 vm::component::ResourceTables<'_>,
396 &mut crate::component::HostResourceData,
397 )> {
398 let current_scope_id = self.current_scope_id()?;
399
400 let store_id = self.id();
401 let data = self.component_data_mut();
402 let guest = instance.map(|i| {
403 let i = i.id();
404 i.assert_belongs_to(store_id);
405 data.instances[i.instance]
406 .as_mut()
407 .unwrap()
408 .get_mut()
409 .instance_states()
410 });
411
412 Ok((
413 vm::component::ResourceTables {
414 host_table: &mut data.component_host_table,
415 task_state: &mut data.task_state,
416 guest,
417 current_scope_id,
418 },
419 &mut data.host_resource_data,
420 ))
421 }
422
423 pub(crate) fn enter_call_not_concurrent(&mut self) -> Result<()> {
424 let state = match &mut self.component_data_mut().task_state {
425 ComponentTaskState::NotConcurrent(state) => state,
426 #[cfg(feature = "component-model-async")]
427 ComponentTaskState::Concurrent(_) => unreachable!(),
428 };
429 state.scopes.push(CallContext::default())?;
430 Ok(())
431 }
432
433 pub(crate) fn exit_call_not_concurrent(&mut self) {
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.pop();
440 }
441
442 pub(crate) fn hostcall_fuel(&self) -> usize {
443 self.component_data().hostcall_fuel
444 }
445
446 pub(crate) fn set_hostcall_fuel(&mut self, fuel: usize) {
447 self.component_data_mut().hostcall_fuel = fuel;
448 }
449
450 #[cfg(feature = "component-model-async")]
451 fn concurrent_resource_table(&mut self) -> Option<&mut ResourceTable> {
452 if self.concurrency_support() {
453 Some(
454 self.concurrent_state_mut_without_forcing_current_thread()
455 .table(),
456 )
457 } else {
458 None
459 }
460 }
461
462 pub(crate) fn current_scope_id_not_concurrent(&mut self) -> Result<Option<u32>> {
463 match &mut self.component_data_mut().task_state {
464 ComponentTaskState::NotConcurrent(state) => match state.scopes.len().checked_sub(1) {
465 Some(i) => Ok(Some(u32::try_from(i)?)),
466 None => Ok(None),
467 },
468 #[cfg(feature = "component-model-async")]
469 ComponentTaskState::Concurrent(_) => crate::bail_bug!("should not be reachable"),
470 }
471 }
472}
473
474impl<T> Store<T> {
475 pub fn hostcall_fuel(&self) -> usize {
483 self.as_context().0.hostcall_fuel()
484 }
485
486 pub fn set_hostcall_fuel(&mut self, fuel: usize) {
510 self.as_context_mut().set_hostcall_fuel(fuel)
511 }
512
513 #[cfg(feature = "component-model-async")]
520 pub fn concurrent_resource_table(&mut self) -> Option<&mut ResourceTable> {
521 self.as_context_mut().0.concurrent_resource_table()
522 }
523}
524
525impl<T> StoreContextMut<'_, T> {
526 pub fn hostcall_fuel(&self) -> usize {
528 self.0.hostcall_fuel()
529 }
530
531 pub fn set_hostcall_fuel(&mut self, fuel: usize) {
533 self.0.set_hostcall_fuel(fuel)
534 }
535
536 #[cfg(feature = "component-model-async")]
538 pub fn concurrent_resource_table(&mut self) -> Option<&mut ResourceTable> {
539 self.0.concurrent_resource_table()
540 }
541}
542
543#[derive(Default)]
544pub struct ComponentTasksNotConcurrent {
545 scopes: TryVec<CallContext>,
546}
547
548impl ComponentTaskState {
549 pub fn call_context(&mut self, id: u32) -> Result<&mut CallContext> {
550 match self {
551 ComponentTaskState::NotConcurrent(state) => Ok(&mut state.scopes[id as usize]),
552 #[cfg(feature = "component-model-async")]
553 ComponentTaskState::Concurrent(state) => state.call_context(id),
554 }
555 }
556
557 #[cfg(feature = "component-model-async")]
558 pub fn concurrent_state_mut(&mut self) -> &mut ConcurrentState {
559 match self {
560 ComponentTaskState::Concurrent(state) => state,
561 ComponentTaskState::NotConcurrent(_) => {
562 panic!("expected concurrent state to be present")
563 }
564 }
565 }
566
567 #[cfg(feature = "component-model-async")]
568 fn is_concurrent(&self) -> bool {
569 match self {
570 ComponentTaskState::Concurrent(_) => true,
571 ComponentTaskState::NotConcurrent(_) => false,
572 }
573 }
574}