wasmtime/runtime/component/
store.rs1use crate::prelude::*;
2use crate::runtime::component::concurrent::ConcurrentState;
3use crate::runtime::component::{HostResourceData, Instance};
4use crate::runtime::vm;
5#[cfg(feature = "component-model-async")]
6use crate::runtime::vm::VMStore;
7use crate::runtime::vm::component::{
8 CallContext, ComponentInstance, HandleTable, OwnedComponentInstance,
9};
10use crate::store::{StoreData, StoreId, StoreOpaque};
11use crate::{AsContext, AsContextMut, Engine, Store, StoreContextMut};
12use core::pin::Pin;
13use wasmtime_environ::PrimaryMap;
14use wasmtime_environ::component::RuntimeComponentInstanceIndex;
15
16const DEFAULT_HOSTCALL_FUEL: usize = 128 << 20;
24
25pub struct ComponentStoreData {
28 instances: PrimaryMap<ComponentInstanceId, Option<OwnedComponentInstance>>,
31
32 trapped: bool,
34
35 num_component_instances: usize,
38
39 component_host_table: HandleTable,
43 host_resource_data: HostResourceData,
44
45 task_state: ComponentTaskState,
48
49 hostcall_fuel: usize,
55}
56
57pub enum ComponentTaskState {
59 NotConcurrent(ComponentTasksNotConcurrent),
62
63 Concurrent(ConcurrentState),
66}
67
68#[derive(Copy, Clone, Debug, PartialEq, Eq)]
69pub struct ComponentInstanceId(u32);
70wasmtime_environ::entity_impl!(ComponentInstanceId);
71
72#[derive(Debug, Copy, Clone, PartialEq, Eq)]
73pub struct RuntimeInstance {
74 pub instance: ComponentInstanceId,
75 pub index: RuntimeComponentInstanceIndex,
76}
77
78impl ComponentStoreData {
79 pub fn new(engine: &Engine) -> ComponentStoreData {
80 ComponentStoreData {
81 instances: Default::default(),
82 trapped: false,
83 num_component_instances: 0,
84 component_host_table: Default::default(),
85 host_resource_data: Default::default(),
86 task_state: if engine.tunables().concurrency_support {
87 #[cfg(feature = "component-model-async")]
88 {
89 ComponentTaskState::Concurrent(Default::default())
90 }
91 #[cfg(not(feature = "component-model-async"))]
92 {
93 unreachable!()
97 }
98 } else {
99 ComponentTaskState::NotConcurrent(Default::default())
100 },
101 hostcall_fuel: DEFAULT_HOSTCALL_FUEL,
102 }
103 }
104
105 pub fn run_manual_drop_routines<T>(store: StoreContextMut<T>) {
110 #[cfg(feature = "component-model-async")]
119 if store.0.component_data().task_state.is_concurrent() {
120 ComponentStoreData::drop_fibers_and_futures(store.0);
121 }
122 #[cfg(not(feature = "component-model-async"))]
123 let _ = store;
124 }
125
126 pub fn next_component_instance_id(&self) -> ComponentInstanceId {
127 self.instances.next_key()
128 }
129
130 #[cfg(feature = "component-model-async")]
131 pub(crate) fn drop_fibers_and_futures(store: &mut dyn VMStore) {
132 let mut fibers = Vec::new();
133 let mut futures = Vec::new();
134 store
135 .concurrent_state_mut()
136 .take_fibers_and_futures(&mut fibers, &mut futures);
137
138 for mut fiber in fibers {
139 fiber.dispose(store);
140 }
141
142 crate::component::concurrent::tls::set(store, move || drop(futures));
143 }
144
145 #[cfg(feature = "component-model-async")]
146 pub(crate) fn assert_instance_states_empty(&mut self) {
147 for (_, instance) in self.instances.iter_mut() {
148 let Some(instance) = instance.as_mut() else {
149 continue;
150 };
151
152 assert!(
153 instance
154 .get_mut()
155 .instance_states()
156 .0
157 .iter_mut()
158 .all(|(_, state)| 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
171#[repr(C)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
182pub struct StoreComponentInstanceId {
183 store_id: StoreId,
184 instance: ComponentInstanceId,
185}
186
187impl StoreComponentInstanceId {
188 pub(crate) fn new(
189 store_id: StoreId,
190 instance: ComponentInstanceId,
191 ) -> StoreComponentInstanceId {
192 StoreComponentInstanceId { store_id, instance }
193 }
194
195 #[inline]
196 pub fn assert_belongs_to(&self, store: StoreId) {
197 self.store_id.assert_belongs_to(store)
198 }
199
200 #[inline]
201 pub(crate) fn store_id(&self) -> StoreId {
202 self.store_id
203 }
204
205 #[inline]
206 pub(crate) fn instance(&self) -> ComponentInstanceId {
207 self.instance
208 }
209
210 pub(crate) fn get<'a>(&self, store: &'a StoreOpaque) -> &'a ComponentInstance {
217 self.assert_belongs_to(store.id());
218 store.component_instance(self.instance)
219 }
220
221 pub(crate) fn get_mut<'a>(&self, store: &'a mut StoreOpaque) -> Pin<&'a mut ComponentInstance> {
227 self.from_data_get_mut(store.store_data_mut())
228 }
229
230 #[cfg(feature = "component-model-async")]
237 pub(crate) fn get_mut_and_registry<'a>(
238 &self,
239 store: &'a mut StoreOpaque,
240 ) -> (
241 Pin<&'a mut ComponentInstance>,
242 &'a crate::module::ModuleRegistry,
243 ) {
244 let (store_data, registry) = store.store_data_mut_and_registry();
245 let instance = self.from_data_get_mut(store_data);
246 (instance, registry)
247 }
248
249 fn from_data_get_mut<'a>(&self, store: &'a mut StoreData) -> Pin<&'a mut ComponentInstance> {
251 self.assert_belongs_to(store.id());
252 store.component_instance_mut(self.instance)
253 }
254}
255
256impl StoreData {
257 pub(crate) fn push_component_instance(
258 &mut self,
259 data: OwnedComponentInstance,
260 ) -> ComponentInstanceId {
261 let expected = data.get().id();
262 let ret = self.components.instances.push(Some(data));
263 assert_eq!(expected, ret);
264 ret
265 }
266
267 pub(crate) fn component_instance(&self, id: ComponentInstanceId) -> &ComponentInstance {
268 self.components.instances[id].as_ref().unwrap().get()
269 }
270
271 pub(crate) fn component_instance_mut(
272 &mut self,
273 id: ComponentInstanceId,
274 ) -> Pin<&mut ComponentInstance> {
275 self.components.instances[id].as_mut().unwrap().get_mut()
276 }
277}
278
279impl StoreOpaque {
280 pub(crate) fn trapped(&self) -> bool {
281 self.store_data().components.trapped
282 }
283
284 pub(crate) fn set_trapped(&mut self) {
285 self.store_data_mut().components.trapped = true;
286 }
287
288 pub(crate) fn component_data(&self) -> &ComponentStoreData {
289 &self.store_data().components
290 }
291
292 pub(crate) fn component_data_mut(&mut self) -> &mut ComponentStoreData {
293 &mut self.store_data_mut().components
294 }
295
296 pub(crate) fn component_task_state_mut(&mut self) -> &mut ComponentTaskState {
297 &mut self.component_data_mut().task_state
298 }
299
300 pub(crate) fn push_component_instance(&mut self, instance: Instance) {
301 let _ = instance;
305
306 self.component_data_mut().num_component_instances += 1;
307 }
308
309 pub(crate) fn component_instance(&self, id: ComponentInstanceId) -> &ComponentInstance {
310 self.store_data().component_instance(id)
311 }
312
313 #[cfg(feature = "component-model-async")]
314 pub(crate) fn component_instance_mut(
315 &mut self,
316 id: ComponentInstanceId,
317 ) -> Pin<&mut ComponentInstance> {
318 self.store_data_mut().component_instance_mut(id)
319 }
320
321 #[cfg(feature = "component-model-async")]
322 pub(crate) fn concurrent_state_mut(&mut self) -> &mut ConcurrentState {
323 debug_assert!(self.concurrency_support());
324 self.component_data_mut().task_state.concurrent_state_mut()
325 }
326
327 #[inline]
328 #[cfg(feature = "component-model-async")]
329 pub(crate) fn concurrency_support(&self) -> bool {
330 let support = self.component_data().task_state.is_concurrent();
331 debug_assert_eq!(support, self.engine().tunables().concurrency_support);
332 support
333 }
334
335 pub(crate) fn lift_context_parts(
336 &mut self,
337 instance: Instance,
338 ) -> (
339 &mut ComponentTaskState,
340 &mut HandleTable,
341 &mut HostResourceData,
342 Pin<&mut ComponentInstance>,
343 ) {
344 let instance = instance.id();
345 instance.assert_belongs_to(self.id());
346 let data = self.component_data_mut();
347 (
348 &mut data.task_state,
349 &mut data.component_host_table,
350 &mut data.host_resource_data,
351 data.instances[instance.instance]
352 .as_mut()
353 .unwrap()
354 .get_mut(),
355 )
356 }
357
358 pub(crate) fn component_resource_tables(
359 &mut self,
360 instance: Option<Instance>,
361 ) -> vm::component::ResourceTables<'_> {
362 self.component_resource_tables_and_host_resource_data(instance)
363 .0
364 }
365
366 pub(crate) fn component_resource_tables_and_host_resource_data(
367 &mut self,
368 instance: Option<Instance>,
369 ) -> (
370 vm::component::ResourceTables<'_>,
371 &mut crate::component::HostResourceData,
372 ) {
373 let store_id = self.id();
374 let data = self.component_data_mut();
375 let guest = instance.map(|i| {
376 let i = i.id();
377 i.assert_belongs_to(store_id);
378 data.instances[i.instance]
379 .as_mut()
380 .unwrap()
381 .get_mut()
382 .instance_states()
383 });
384
385 (
386 vm::component::ResourceTables {
387 host_table: &mut data.component_host_table,
388 task_state: &mut data.task_state,
389 guest,
390 },
391 &mut data.host_resource_data,
392 )
393 }
394
395 pub(crate) fn enter_call_not_concurrent(&mut self) {
396 let state = match &mut self.component_data_mut().task_state {
397 ComponentTaskState::NotConcurrent(state) => state,
398 ComponentTaskState::Concurrent(_) => unreachable!(),
399 };
400 state.scopes.push(CallContext::default());
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
420impl<T> Store<T> {
421 pub fn hostcall_fuel(&self) -> usize {
429 self.as_context().0.hostcall_fuel()
430 }
431
432 pub fn set_hostcall_fuel(&mut self, fuel: usize) {
456 self.as_context_mut().set_hostcall_fuel(fuel)
457 }
458}
459
460impl<T> StoreContextMut<'_, T> {
461 pub fn hostcall_fuel(&self) -> usize {
463 self.0.hostcall_fuel()
464 }
465
466 pub fn set_hostcall_fuel(&mut self, fuel: usize) {
468 self.0.set_hostcall_fuel(fuel)
469 }
470}
471
472#[derive(Default)]
473pub struct ComponentTasksNotConcurrent {
474 scopes: Vec<CallContext>,
475}
476
477impl ComponentTaskState {
478 pub fn call_context(&mut self, id: u32) -> &mut CallContext {
479 match self {
480 ComponentTaskState::NotConcurrent(state) => &mut state.scopes[id as usize],
481 ComponentTaskState::Concurrent(state) => state.call_context(id),
482 }
483 }
484
485 pub fn current_call_context_scope_id(&self) -> u32 {
486 match self {
487 ComponentTaskState::NotConcurrent(state) => {
488 u32::try_from(state.scopes.len() - 1).unwrap()
489 }
490 ComponentTaskState::Concurrent(state) => state.current_call_context_scope_id(),
491 }
492 }
493
494 pub fn concurrent_state_mut(&mut self) -> &mut ConcurrentState {
495 match self {
496 ComponentTaskState::Concurrent(state) => state,
497 ComponentTaskState::NotConcurrent(_) => {
498 panic!("expected concurrent state to be present")
499 }
500 }
501 }
502
503 #[cfg(feature = "component-model-async")]
504 fn is_concurrent(&self) -> bool {
505 match self {
506 ComponentTaskState::Concurrent(_) => true,
507 ComponentTaskState::NotConcurrent(_) => false,
508 }
509 }
510}