wasmtime/runtime/component/
store.rs1use crate::prelude::*;
2#[cfg(feature = "component-model-async")]
3use crate::runtime::component::ResourceTable;
4use crate::runtime::component::concurrent::ConcurrentState;
5use crate::runtime::component::{HostResourceData, Instance};
6use crate::runtime::vm;
7#[cfg(feature = "component-model-async")]
8use crate::runtime::vm::VMStore;
9use crate::runtime::vm::component::{
10 CallContext, ComponentInstance, HandleTable, OwnedComponentInstance,
11};
12use crate::store::{StoreData, StoreId, StoreOpaque};
13use crate::{AsContext, AsContextMut, Engine, Store, StoreContextMut};
14use core::pin::Pin;
15use wasmtime_environ::PrimaryMap;
16use wasmtime_environ::component::RuntimeComponentInstanceIndex;
17
18const DEFAULT_HOSTCALL_FUEL: usize = 128 << 20;
26
27pub struct ComponentStoreData {
30 instances: PrimaryMap<ComponentInstanceId, Option<OwnedComponentInstance>>,
33
34 trapped: bool,
36
37 num_component_instances: usize,
40
41 component_host_table: HandleTable,
45 host_resource_data: HostResourceData,
46
47 task_state: ComponentTaskState,
50
51 hostcall_fuel: usize,
57}
58
59pub enum ComponentTaskState {
61 NotConcurrent(ComponentTasksNotConcurrent),
64
65 Concurrent(ConcurrentState),
68}
69
70#[derive(Copy, Clone, Debug, PartialEq, Eq)]
71pub struct ComponentInstanceId(u32);
72wasmtime_environ::entity_impl!(ComponentInstanceId);
73
74#[derive(Debug, Copy, Clone, PartialEq, Eq)]
75pub struct RuntimeInstance {
76 pub instance: ComponentInstanceId,
77 pub index: RuntimeComponentInstanceIndex,
78}
79
80impl ComponentStoreData {
81 pub fn new(engine: &Engine) -> ComponentStoreData {
82 ComponentStoreData {
83 instances: Default::default(),
84 trapped: false,
85 num_component_instances: 0,
86 component_host_table: Default::default(),
87 host_resource_data: Default::default(),
88 task_state: if engine.tunables().concurrency_support {
89 #[cfg(feature = "component-model-async")]
90 {
91 ComponentTaskState::Concurrent(Default::default())
92 }
93 #[cfg(not(feature = "component-model-async"))]
94 {
95 unreachable!()
99 }
100 } else {
101 ComponentTaskState::NotConcurrent(Default::default())
102 },
103 hostcall_fuel: DEFAULT_HOSTCALL_FUEL,
104 }
105 }
106
107 pub fn run_manual_drop_routines<T>(store: StoreContextMut<T>) {
112 #[cfg(feature = "component-model-async")]
121 if store.0.component_data().task_state.is_concurrent() {
122 ComponentStoreData::drop_fibers_and_futures(store.0);
123 }
124 #[cfg(not(feature = "component-model-async"))]
125 let _ = store;
126 }
127
128 pub fn next_component_instance_id(&self) -> ComponentInstanceId {
129 self.instances.next_key()
130 }
131
132 #[cfg(feature = "component-model-async")]
133 pub(crate) fn drop_fibers_and_futures(store: &mut dyn VMStore) {
134 let mut fibers = Vec::new();
135 let mut futures = Vec::new();
136 store
137 .concurrent_state_mut()
138 .take_fibers_and_futures(&mut fibers, &mut futures);
139
140 for mut fiber in fibers {
141 fiber.dispose(store);
142 }
143
144 crate::component::concurrent::tls::set(store, move || drop(futures));
145 }
146
147 #[cfg(feature = "component-model-async")]
148 pub(crate) fn assert_instance_states_empty(&mut self) {
149 for (_, instance) in self.instances.iter_mut() {
150 let Some(instance) = instance.as_mut() else {
151 continue;
152 };
153
154 assert!(
155 instance
156 .get_mut()
157 .instance_states()
158 .0
159 .iter_mut()
160 .all(|(_, state)| 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
173#[repr(C)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
184pub struct StoreComponentInstanceId {
185 store_id: StoreId,
186 instance: ComponentInstanceId,
187}
188
189impl StoreComponentInstanceId {
190 pub(crate) fn new(
191 store_id: StoreId,
192 instance: ComponentInstanceId,
193 ) -> StoreComponentInstanceId {
194 StoreComponentInstanceId { store_id, instance }
195 }
196
197 #[inline]
198 pub fn assert_belongs_to(&self, store: StoreId) {
199 self.store_id.assert_belongs_to(store)
200 }
201
202 #[inline]
203 pub(crate) fn store_id(&self) -> StoreId {
204 self.store_id
205 }
206
207 #[inline]
208 pub(crate) fn instance(&self) -> ComponentInstanceId {
209 self.instance
210 }
211
212 pub(crate) fn get<'a>(&self, store: &'a StoreOpaque) -> &'a ComponentInstance {
219 self.assert_belongs_to(store.id());
220 store.component_instance(self.instance)
221 }
222
223 pub(crate) fn get_mut<'a>(&self, store: &'a mut StoreOpaque) -> Pin<&'a mut ComponentInstance> {
229 self.from_data_get_mut(store.store_data_mut())
230 }
231
232 #[cfg(feature = "component-model-async")]
239 pub(crate) fn get_mut_and_registry<'a>(
240 &self,
241 store: &'a mut StoreOpaque,
242 ) -> (
243 Pin<&'a mut ComponentInstance>,
244 &'a crate::module::ModuleRegistry,
245 ) {
246 let (store_data, registry) = store.store_data_mut_and_registry();
247 let instance = self.from_data_get_mut(store_data);
248 (instance, registry)
249 }
250
251 fn from_data_get_mut<'a>(&self, store: &'a mut StoreData) -> Pin<&'a mut ComponentInstance> {
253 self.assert_belongs_to(store.id());
254 store.component_instance_mut(self.instance)
255 }
256}
257
258impl StoreData {
259 pub(crate) fn push_component_instance(
260 &mut self,
261 data: OwnedComponentInstance,
262 ) -> ComponentInstanceId {
263 let expected = data.get().id();
264 let ret = self.components.instances.push(Some(data));
265 assert_eq!(expected, ret);
266 ret
267 }
268
269 pub(crate) fn component_instance(&self, id: ComponentInstanceId) -> &ComponentInstance {
270 self.components.instances[id].as_ref().unwrap().get()
271 }
272
273 pub(crate) fn component_instance_mut(
274 &mut self,
275 id: ComponentInstanceId,
276 ) -> Pin<&mut ComponentInstance> {
277 self.components.instances[id].as_mut().unwrap().get_mut()
278 }
279}
280
281impl StoreOpaque {
282 pub(crate) fn trapped(&self) -> bool {
283 self.store_data().components.trapped
284 }
285
286 pub(crate) fn set_trapped(&mut self) {
287 self.store_data_mut().components.trapped = true;
288 }
289
290 pub(crate) fn component_data(&self) -> &ComponentStoreData {
291 &self.store_data().components
292 }
293
294 pub(crate) fn component_data_mut(&mut self) -> &mut ComponentStoreData {
295 &mut self.store_data_mut().components
296 }
297
298 pub(crate) fn component_task_state_mut(&mut self) -> &mut ComponentTaskState {
299 &mut self.component_data_mut().task_state
300 }
301
302 pub(crate) fn push_component_instance(&mut self, instance: Instance) {
303 let _ = instance;
307
308 self.component_data_mut().num_component_instances += 1;
309 }
310
311 pub(crate) fn component_instance(&self, id: ComponentInstanceId) -> &ComponentInstance {
312 self.store_data().component_instance(id)
313 }
314
315 #[cfg(feature = "component-model-async")]
316 pub(crate) fn component_instance_mut(
317 &mut self,
318 id: ComponentInstanceId,
319 ) -> Pin<&mut ComponentInstance> {
320 self.store_data_mut().component_instance_mut(id)
321 }
322
323 #[cfg(feature = "component-model-async")]
324 pub(crate) fn concurrent_state_mut(&mut self) -> &mut ConcurrentState {
325 debug_assert!(self.concurrency_support());
326 self.component_data_mut().task_state.concurrent_state_mut()
327 }
328
329 #[inline]
330 #[cfg(feature = "component-model-async")]
331 pub(crate) fn concurrency_support(&self) -> bool {
332 let support = self.component_data().task_state.is_concurrent();
333 debug_assert_eq!(support, self.engine().tunables().concurrency_support);
334 support
335 }
336
337 pub(crate) fn lift_context_parts(
338 &mut self,
339 instance: Instance,
340 ) -> (
341 &mut ComponentTaskState,
342 &mut HandleTable,
343 &mut HostResourceData,
344 Pin<&mut ComponentInstance>,
345 ) {
346 let instance = instance.id();
347 instance.assert_belongs_to(self.id());
348 let data = self.component_data_mut();
349 (
350 &mut data.task_state,
351 &mut data.component_host_table,
352 &mut data.host_resource_data,
353 data.instances[instance.instance]
354 .as_mut()
355 .unwrap()
356 .get_mut(),
357 )
358 }
359
360 pub(crate) fn component_resource_tables(
361 &mut self,
362 instance: Option<Instance>,
363 ) -> vm::component::ResourceTables<'_> {
364 self.component_resource_tables_and_host_resource_data(instance)
365 .0
366 }
367
368 pub(crate) fn component_resource_tables_and_host_resource_data(
369 &mut self,
370 instance: Option<Instance>,
371 ) -> (
372 vm::component::ResourceTables<'_>,
373 &mut crate::component::HostResourceData,
374 ) {
375 let store_id = self.id();
376 let data = self.component_data_mut();
377 let guest = instance.map(|i| {
378 let i = i.id();
379 i.assert_belongs_to(store_id);
380 data.instances[i.instance]
381 .as_mut()
382 .unwrap()
383 .get_mut()
384 .instance_states()
385 });
386
387 (
388 vm::component::ResourceTables {
389 host_table: &mut data.component_host_table,
390 task_state: &mut data.task_state,
391 guest,
392 },
393 &mut data.host_resource_data,
394 )
395 }
396
397 pub(crate) fn enter_call_not_concurrent(&mut self) {
398 let state = match &mut self.component_data_mut().task_state {
399 ComponentTaskState::NotConcurrent(state) => state,
400 ComponentTaskState::Concurrent(_) => unreachable!(),
401 };
402 state.scopes.push(CallContext::default());
403 }
404
405 pub(crate) fn exit_call_not_concurrent(&mut self) {
406 let state = match &mut self.component_data_mut().task_state {
407 ComponentTaskState::NotConcurrent(state) => state,
408 ComponentTaskState::Concurrent(_) => unreachable!(),
409 };
410 state.scopes.pop();
411 }
412
413 pub(crate) fn hostcall_fuel(&self) -> usize {
414 self.component_data().hostcall_fuel
415 }
416
417 pub(crate) fn set_hostcall_fuel(&mut self, fuel: usize) {
418 self.component_data_mut().hostcall_fuel = fuel;
419 }
420
421 #[cfg(feature = "component-model-async")]
422 fn concurrent_resource_table(&mut self) -> Option<&mut ResourceTable> {
423 if self.concurrency_support() {
424 Some(self.concurrent_state_mut().table())
425 } else {
426 None
427 }
428 }
429}
430
431impl<T> Store<T> {
432 pub fn hostcall_fuel(&self) -> usize {
440 self.as_context().0.hostcall_fuel()
441 }
442
443 pub fn set_hostcall_fuel(&mut self, fuel: usize) {
467 self.as_context_mut().set_hostcall_fuel(fuel)
468 }
469
470 #[cfg(feature = "component-model-async")]
477 pub fn concurrent_resource_table(&mut self) -> Option<&mut ResourceTable> {
478 self.as_context_mut().0.concurrent_resource_table()
479 }
480}
481
482impl<T> StoreContextMut<'_, T> {
483 pub fn hostcall_fuel(&self) -> usize {
485 self.0.hostcall_fuel()
486 }
487
488 pub fn set_hostcall_fuel(&mut self, fuel: usize) {
490 self.0.set_hostcall_fuel(fuel)
491 }
492
493 #[cfg(feature = "component-model-async")]
495 pub fn concurrent_resource_table(&mut self) -> Option<&mut ResourceTable> {
496 self.0.concurrent_resource_table()
497 }
498}
499
500#[derive(Default)]
501pub struct ComponentTasksNotConcurrent {
502 scopes: Vec<CallContext>,
503}
504
505impl ComponentTaskState {
506 pub fn call_context(&mut self, id: u32) -> &mut CallContext {
507 match self {
508 ComponentTaskState::NotConcurrent(state) => &mut state.scopes[id as usize],
509 ComponentTaskState::Concurrent(state) => state.call_context(id),
510 }
511 }
512
513 pub fn current_call_context_scope_id(&self) -> u32 {
514 match self {
515 ComponentTaskState::NotConcurrent(state) => {
516 u32::try_from(state.scopes.len() - 1).unwrap()
517 }
518 ComponentTaskState::Concurrent(state) => state.current_call_context_scope_id(),
519 }
520 }
521
522 pub fn concurrent_state_mut(&mut self) -> &mut ConcurrentState {
523 match self {
524 ComponentTaskState::Concurrent(state) => state,
525 ComponentTaskState::NotConcurrent(_) => {
526 panic!("expected concurrent state to be present")
527 }
528 }
529 }
530
531 #[cfg(feature = "component-model-async")]
532 fn is_concurrent(&self) -> bool {
533 match self {
534 ComponentTaskState::Concurrent(_) => true,
535 ComponentTaskState::NotConcurrent(_) => false,
536 }
537 }
538}