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()
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
170#[repr(C)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
181pub struct StoreComponentInstanceId {
182 store_id: StoreId,
183 instance: ComponentInstanceId,
184}
185
186impl StoreComponentInstanceId {
187 pub(crate) fn new(
188 store_id: StoreId,
189 instance: ComponentInstanceId,
190 ) -> StoreComponentInstanceId {
191 StoreComponentInstanceId { store_id, instance }
192 }
193
194 #[inline]
195 pub fn assert_belongs_to(&self, store: StoreId) {
196 self.store_id.assert_belongs_to(store)
197 }
198
199 #[inline]
200 pub(crate) fn store_id(&self) -> StoreId {
201 self.store_id
202 }
203
204 #[inline]
205 pub(crate) fn instance(&self) -> ComponentInstanceId {
206 self.instance
207 }
208
209 pub(crate) fn get<'a>(&self, store: &'a StoreOpaque) -> &'a ComponentInstance {
216 self.assert_belongs_to(store.id());
217 store.component_instance(self.instance)
218 }
219
220 pub(crate) fn get_mut<'a>(&self, store: &'a mut StoreOpaque) -> Pin<&'a mut ComponentInstance> {
226 self.from_data_get_mut(store.store_data_mut())
227 }
228
229 #[cfg(feature = "component-model-async")]
236 pub(crate) fn get_mut_and_registry<'a>(
237 &self,
238 store: &'a mut StoreOpaque,
239 ) -> (
240 Pin<&'a mut ComponentInstance>,
241 &'a crate::module::ModuleRegistry,
242 ) {
243 let (store_data, registry) = store.store_data_mut_and_registry();
244 let instance = self.from_data_get_mut(store_data);
245 (instance, registry)
246 }
247
248 fn from_data_get_mut<'a>(&self, store: &'a mut StoreData) -> Pin<&'a mut ComponentInstance> {
250 self.assert_belongs_to(store.id());
251 store.component_instance_mut(self.instance)
252 }
253}
254
255impl StoreData {
256 pub(crate) fn push_component_instance(
257 &mut self,
258 data: OwnedComponentInstance,
259 ) -> Result<ComponentInstanceId, OutOfMemory> {
260 let expected = data.get().id();
261 let ret = self.components.instances.push(Some(data))?;
262 assert_eq!(expected, ret);
263 Ok(ret)
264 }
265
266 pub(crate) fn component_instance(&self, id: ComponentInstanceId) -> &ComponentInstance {
267 self.components.instances[id].as_ref().unwrap().get()
268 }
269
270 pub(crate) fn component_instance_mut(
271 &mut self,
272 id: ComponentInstanceId,
273 ) -> Pin<&mut ComponentInstance> {
274 self.components.instances[id].as_mut().unwrap().get_mut()
275 }
276}
277
278impl StoreOpaque {
279 pub(crate) fn trapped(&self) -> bool {
280 self.store_data().components.trapped
281 }
282
283 pub(crate) fn set_trapped(&mut self) {
284 self.store_data_mut().components.trapped = true;
285 }
286
287 pub(crate) fn component_data(&self) -> &ComponentStoreData {
288 &self.store_data().components
289 }
290
291 pub(crate) fn component_data_mut(&mut self) -> &mut ComponentStoreData {
292 &mut self.store_data_mut().components
293 }
294
295 pub(crate) fn component_task_state_mut(&mut self) -> &mut ComponentTaskState {
296 &mut self.component_data_mut().task_state
297 }
298
299 pub(crate) fn push_component_instance(&mut self, instance: Instance) {
300 let _ = instance;
304
305 self.component_data_mut().num_component_instances += 1;
306 }
307
308 pub(crate) fn component_instance(&self, id: ComponentInstanceId) -> &ComponentInstance {
309 self.store_data().component_instance(id)
310 }
311
312 #[cfg(feature = "component-model-async")]
313 pub(crate) fn component_instance_mut(
314 &mut self,
315 id: ComponentInstanceId,
316 ) -> Pin<&mut ComponentInstance> {
317 self.store_data_mut().component_instance_mut(id)
318 }
319
320 #[cfg(feature = "component-model-async")]
321 pub(crate) fn concurrent_state_mut(&mut self) -> &mut ConcurrentState {
322 debug_assert!(self.concurrency_support());
323 self.component_data_mut().task_state.concurrent_state_mut()
324 }
325
326 #[inline]
327 #[cfg(feature = "component-model-async")]
328 pub(crate) fn concurrency_support(&self) -> bool {
329 let support = self.component_data().task_state.is_concurrent();
330 debug_assert_eq!(support, self.engine().tunables().concurrency_support);
331 support
332 }
333
334 pub(crate) fn lift_context_parts(
335 &mut self,
336 instance: Instance,
337 ) -> (
338 &mut ComponentTaskState,
339 &mut HandleTable,
340 &mut HostResourceData,
341 Pin<&mut ComponentInstance>,
342 ) {
343 let instance = instance.id();
344 instance.assert_belongs_to(self.id());
345 let data = self.component_data_mut();
346 (
347 &mut data.task_state,
348 &mut data.component_host_table,
349 &mut data.host_resource_data,
350 data.instances[instance.instance]
351 .as_mut()
352 .unwrap()
353 .get_mut(),
354 )
355 }
356
357 pub(crate) fn component_resource_tables(
358 &mut self,
359 instance: Option<Instance>,
360 ) -> vm::component::ResourceTables<'_> {
361 self.component_resource_tables_and_host_resource_data(instance)
362 .0
363 }
364
365 pub(crate) fn component_resource_tables_and_host_resource_data(
366 &mut self,
367 instance: Option<Instance>,
368 ) -> (
369 vm::component::ResourceTables<'_>,
370 &mut crate::component::HostResourceData,
371 ) {
372 let store_id = self.id();
373 let data = self.component_data_mut();
374 let guest = instance.map(|i| {
375 let i = i.id();
376 i.assert_belongs_to(store_id);
377 data.instances[i.instance]
378 .as_mut()
379 .unwrap()
380 .get_mut()
381 .instance_states()
382 });
383
384 (
385 vm::component::ResourceTables {
386 host_table: &mut data.component_host_table,
387 task_state: &mut data.task_state,
388 guest,
389 },
390 &mut data.host_resource_data,
391 )
392 }
393
394 pub(crate) fn enter_call_not_concurrent(&mut self) -> Result<()> {
395 let state = match &mut self.component_data_mut().task_state {
396 ComponentTaskState::NotConcurrent(state) => state,
397 ComponentTaskState::Concurrent(_) => unreachable!(),
398 };
399 state.scopes.push(CallContext::default())?;
400 Ok(())
401 }
402
403 pub(crate) fn exit_call_not_concurrent(&mut self) {
404 let state = match &mut self.component_data_mut().task_state {
405 ComponentTaskState::NotConcurrent(state) => state,
406 ComponentTaskState::Concurrent(_) => unreachable!(),
407 };
408 state.scopes.pop();
409 }
410
411 pub(crate) fn hostcall_fuel(&self) -> usize {
412 self.component_data().hostcall_fuel
413 }
414
415 pub(crate) fn set_hostcall_fuel(&mut self, fuel: usize) {
416 self.component_data_mut().hostcall_fuel = fuel;
417 }
418
419 #[cfg(feature = "component-model-async")]
420 fn concurrent_resource_table(&mut self) -> Option<&mut ResourceTable> {
421 if self.concurrency_support() {
422 Some(self.concurrent_state_mut().table())
423 } else {
424 None
425 }
426 }
427}
428
429impl<T> Store<T> {
430 pub fn hostcall_fuel(&self) -> usize {
438 self.as_context().0.hostcall_fuel()
439 }
440
441 pub fn set_hostcall_fuel(&mut self, fuel: usize) {
465 self.as_context_mut().set_hostcall_fuel(fuel)
466 }
467
468 #[cfg(feature = "component-model-async")]
475 pub fn concurrent_resource_table(&mut self) -> Option<&mut ResourceTable> {
476 self.as_context_mut().0.concurrent_resource_table()
477 }
478}
479
480impl<T> StoreContextMut<'_, T> {
481 pub fn hostcall_fuel(&self) -> usize {
483 self.0.hostcall_fuel()
484 }
485
486 pub fn set_hostcall_fuel(&mut self, fuel: usize) {
488 self.0.set_hostcall_fuel(fuel)
489 }
490
491 #[cfg(feature = "component-model-async")]
493 pub fn concurrent_resource_table(&mut self) -> Option<&mut ResourceTable> {
494 self.0.concurrent_resource_table()
495 }
496}
497
498#[derive(Default)]
499pub struct ComponentTasksNotConcurrent {
500 scopes: TryVec<CallContext>,
501}
502
503impl ComponentTaskState {
504 pub fn call_context(&mut self, id: u32) -> Result<&mut CallContext> {
505 match self {
506 ComponentTaskState::NotConcurrent(state) => Ok(&mut state.scopes[id as usize]),
507 ComponentTaskState::Concurrent(state) => state.call_context(id),
508 }
509 }
510
511 pub fn current_call_context_scope_id(&self) -> Result<u32> {
512 match self {
513 ComponentTaskState::NotConcurrent(state) => Ok(u32::try_from(state.scopes.len() - 1)?),
514 ComponentTaskState::Concurrent(state) => state.current_call_context_scope_id(),
515 }
516 }
517
518 pub fn concurrent_state_mut(&mut self) -> &mut ConcurrentState {
519 match self {
520 ComponentTaskState::Concurrent(state) => state,
521 ComponentTaskState::NotConcurrent(_) => {
522 panic!("expected concurrent state to be present")
523 }
524 }
525 }
526
527 #[cfg(feature = "component-model-async")]
528 fn is_concurrent(&self) -> bool {
529 match self {
530 ComponentTaskState::Concurrent(_) => true,
531 ComponentTaskState::NotConcurrent(_) => false,
532 }
533 }
534}