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