1 use crate::prelude::*;
2 use crate::runtime::component::concurrent::ConcurrentState;
3 use crate::runtime::component::{HostResourceData, Instance};
4 use crate::runtime::vm;
5 #[cfg(feature = "component-model-async")]
6 use crate::runtime::vm::VMStore;
7 use crate::runtime::vm::component::{
8     CallContext, ComponentInstance, HandleTable, OwnedComponentInstance,
9 };
10 use crate::store::{StoreData, StoreId, StoreOpaque};
11 use crate::{Engine, StoreContextMut};
12 use core::pin::Pin;
13 use wasmtime_environ::PrimaryMap;
14 use wasmtime_environ::component::RuntimeComponentInstanceIndex;
15 
16 /// Extensions to `Store` which are only relevant for component-related
17 /// information.
18 pub struct ComponentStoreData {
19     /// All component instances, in a similar manner to how core wasm instances
20     /// are managed.
21     instances: PrimaryMap<ComponentInstanceId, Option<OwnedComponentInstance>>,
22 
23     /// Whether an instance belonging to this store has trapped.
24     trapped: bool,
25 
26     /// Total number of component instances in this store, used to track
27     /// resources in the instance allocator.
28     num_component_instances: usize,
29 
30     /// Runtime state for components used in the handling of resources, borrow,
31     /// and calls. These also interact with the `ResourceAny` type and its
32     /// internal representation.
33     component_host_table: HandleTable,
34     host_resource_data: HostResourceData,
35 
36     /// Metadata/tasks/etc related to component-model-async and concurrency
37     /// support.
38     task_state: ComponentTaskState,
39 }
40 
41 /// State tracking for tasks within components.
42 pub enum ComponentTaskState {
43     /// Used when `Config::concurrency_support` is disabled. Here there are no
44     /// async tasks but there's still state for borrows that needs managing.
45     NotConcurrent(ComponentTasksNotConcurrent),
46 
47     /// Used when `Config::concurrency_support` is enabled and has
48     /// full state for all async tasks.
49     Concurrent(ConcurrentState),
50 }
51 
52 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
53 pub struct ComponentInstanceId(u32);
54 wasmtime_environ::entity_impl!(ComponentInstanceId);
55 
56 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
57 pub struct RuntimeInstance {
58     pub instance: ComponentInstanceId,
59     pub index: RuntimeComponentInstanceIndex,
60 }
61 
62 impl ComponentStoreData {
63     pub fn new(engine: &Engine) -> ComponentStoreData {
64         ComponentStoreData {
65             instances: Default::default(),
66             trapped: false,
67             num_component_instances: 0,
68             component_host_table: Default::default(),
69             host_resource_data: Default::default(),
70             task_state: if engine.tunables().concurrency_support {
71                 #[cfg(feature = "component-model-async")]
72                 {
73                     ComponentTaskState::Concurrent(Default::default())
74                 }
75                 #[cfg(not(feature = "component-model-async"))]
76                 {
77                     // This should be validated in `Config` where if
78                     // `concurrency_support` is enabled but compile time support
79                     // isn't available then an `Engine` isn't creatable.
80                     unreachable!()
81                 }
82             } else {
83                 ComponentTaskState::NotConcurrent(Default::default())
84             },
85         }
86     }
87 
88     /// Hook used just before a `Store` is dropped to dispose of anything
89     /// necessary.
90     ///
91     /// Used at this time to deallocate fibers related to concurrency support.
92     pub fn run_manual_drop_routines<T>(store: StoreContextMut<T>) {
93         // We need to drop the fibers of each component instance before
94         // attempting to drop the instances themselves since the fibers may need
95         // to be resumed and allowed to exit cleanly before we yank the state
96         // out from under them.
97         //
98         // This will also drop any futures which might use a `&Accessor` fields
99         // in their `Drop::drop` implementations, in which case they'll need to
100         // be called from with in the context of a `tls::set` closure.
101         #[cfg(feature = "component-model-async")]
102         if store.0.component_data().task_state.is_concurrent() {
103             ComponentStoreData::drop_fibers_and_futures(store.0);
104         }
105         #[cfg(not(feature = "component-model-async"))]
106         let _ = store;
107     }
108 
109     pub fn next_component_instance_id(&self) -> ComponentInstanceId {
110         self.instances.next_key()
111     }
112 
113     #[cfg(feature = "component-model-async")]
114     pub(crate) fn drop_fibers_and_futures(store: &mut dyn VMStore) {
115         let mut fibers = Vec::new();
116         let mut futures = Vec::new();
117         store
118             .concurrent_state_mut()
119             .take_fibers_and_futures(&mut fibers, &mut futures);
120 
121         for mut fiber in fibers {
122             fiber.dispose(store);
123         }
124 
125         crate::component::concurrent::tls::set(store, move || drop(futures));
126     }
127 
128     #[cfg(feature = "component-model-async")]
129     pub(crate) fn assert_instance_states_empty(&mut self) {
130         for (_, instance) in self.instances.iter_mut() {
131             let Some(instance) = instance.as_mut() else {
132                 continue;
133             };
134 
135             assert!(
136                 instance
137                     .get_mut()
138                     .instance_states()
139                     .0
140                     .iter_mut()
141                     .all(|(_, state)| state.handle_table().is_empty()
142                         && state.concurrent_state().pending_is_empty())
143             );
144         }
145     }
146 
147     pub fn decrement_allocator_resources(&mut self, allocator: &dyn vm::InstanceAllocator) {
148         for _ in 0..self.num_component_instances {
149             allocator.decrement_component_instance_count();
150         }
151     }
152 }
153 
154 /// A type used to represent an allocated `ComponentInstance` located within a
155 /// store.
156 ///
157 /// This type is held in various locations as a "safe index" into a store. This
158 /// encapsulates a `StoreId` which owns the instance as well as the index within
159 /// the store's list of which instance it's pointing to.
160 ///
161 /// This type can notably be used to index into a `StoreOpaque` to project out
162 /// the `ComponentInstance` that is associated with this id.
163 #[repr(C)] // used by reference in the C API
164 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
165 pub struct StoreComponentInstanceId {
166     store_id: StoreId,
167     instance: ComponentInstanceId,
168 }
169 
170 impl StoreComponentInstanceId {
171     pub(crate) fn new(
172         store_id: StoreId,
173         instance: ComponentInstanceId,
174     ) -> StoreComponentInstanceId {
175         StoreComponentInstanceId { store_id, instance }
176     }
177 
178     #[inline]
179     pub fn assert_belongs_to(&self, store: StoreId) {
180         self.store_id.assert_belongs_to(store)
181     }
182 
183     #[inline]
184     pub(crate) fn store_id(&self) -> StoreId {
185         self.store_id
186     }
187 
188     #[inline]
189     pub(crate) fn instance(&self) -> ComponentInstanceId {
190         self.instance
191     }
192 
193     /// Looks up the `vm::ComponentInstance` within `store` that this id points
194     /// to.
195     ///
196     /// # Panics
197     ///
198     /// Panics if `self` does not belong to `store`.
199     pub(crate) fn get<'a>(&self, store: &'a StoreOpaque) -> &'a ComponentInstance {
200         self.assert_belongs_to(store.id());
201         store.component_instance(self.instance)
202     }
203 
204     /// Mutable version of `get` above.
205     ///
206     /// # Panics
207     ///
208     /// Panics if `self` does not belong to `store`.
209     pub(crate) fn get_mut<'a>(&self, store: &'a mut StoreOpaque) -> Pin<&'a mut ComponentInstance> {
210         self.from_data_get_mut(store.store_data_mut())
211     }
212 
213     /// Return a mutable `ComponentInstance` and a `ModuleRegistry`
214     /// from the store.
215     ///
216     /// # Panics
217     ///
218     /// Panics if `self` does not belong to `store`.
219     #[cfg(feature = "component-model-async")]
220     pub(crate) fn get_mut_and_registry<'a>(
221         &self,
222         store: &'a mut StoreOpaque,
223     ) -> (
224         Pin<&'a mut ComponentInstance>,
225         &'a crate::module::ModuleRegistry,
226     ) {
227         let (store_data, registry) = store.store_data_mut_and_registry();
228         let instance = self.from_data_get_mut(store_data);
229         (instance, registry)
230     }
231 
232     /// Same as `get_mut`, but borrows less of a store.
233     fn from_data_get_mut<'a>(&self, store: &'a mut StoreData) -> Pin<&'a mut ComponentInstance> {
234         self.assert_belongs_to(store.id());
235         store.component_instance_mut(self.instance)
236     }
237 }
238 
239 impl StoreData {
240     pub(crate) fn push_component_instance(
241         &mut self,
242         data: OwnedComponentInstance,
243     ) -> ComponentInstanceId {
244         let expected = data.get().id();
245         let ret = self.components.instances.push(Some(data));
246         assert_eq!(expected, ret);
247         ret
248     }
249 
250     pub(crate) fn component_instance(&self, id: ComponentInstanceId) -> &ComponentInstance {
251         self.components.instances[id].as_ref().unwrap().get()
252     }
253 
254     pub(crate) fn component_instance_mut(
255         &mut self,
256         id: ComponentInstanceId,
257     ) -> Pin<&mut ComponentInstance> {
258         self.components.instances[id].as_mut().unwrap().get_mut()
259     }
260 }
261 
262 impl StoreOpaque {
263     pub(crate) fn trapped(&self) -> bool {
264         self.store_data().components.trapped
265     }
266 
267     pub(crate) fn set_trapped(&mut self) {
268         self.store_data_mut().components.trapped = true;
269     }
270 
271     #[cfg(feature = "component-model-async")]
272     pub(crate) fn component_data(&self) -> &ComponentStoreData {
273         &self.store_data().components
274     }
275 
276     pub(crate) fn component_data_mut(&mut self) -> &mut ComponentStoreData {
277         &mut self.store_data_mut().components
278     }
279 
280     pub(crate) fn component_task_state_mut(&mut self) -> &mut ComponentTaskState {
281         &mut self.component_data_mut().task_state
282     }
283 
284     pub(crate) fn push_component_instance(&mut self, instance: Instance) {
285         // We don't actually need the instance itself right now, but it seems
286         // like something we will almost certainly eventually want to keep
287         // around, so force callers to provide it.
288         let _ = instance;
289 
290         self.component_data_mut().num_component_instances += 1;
291     }
292 
293     pub(crate) fn component_instance(&self, id: ComponentInstanceId) -> &ComponentInstance {
294         self.store_data().component_instance(id)
295     }
296 
297     #[cfg(feature = "component-model-async")]
298     pub(crate) fn component_instance_mut(
299         &mut self,
300         id: ComponentInstanceId,
301     ) -> Pin<&mut ComponentInstance> {
302         self.store_data_mut().component_instance_mut(id)
303     }
304 
305     #[cfg(feature = "component-model-async")]
306     pub(crate) fn concurrent_state_mut(&mut self) -> &mut ConcurrentState {
307         debug_assert!(self.concurrency_support());
308         self.component_data_mut().task_state.concurrent_state_mut()
309     }
310 
311     #[inline]
312     #[cfg(feature = "component-model-async")]
313     pub(crate) fn concurrency_support(&self) -> bool {
314         let support = self.component_data().task_state.is_concurrent();
315         debug_assert_eq!(support, self.engine().tunables().concurrency_support);
316         support
317     }
318 
319     pub(crate) fn lift_context_parts(
320         &mut self,
321         instance: Instance,
322     ) -> (
323         &mut ComponentTaskState,
324         &mut HandleTable,
325         &mut HostResourceData,
326         Pin<&mut ComponentInstance>,
327     ) {
328         let instance = instance.id();
329         instance.assert_belongs_to(self.id());
330         let data = self.component_data_mut();
331         (
332             &mut data.task_state,
333             &mut data.component_host_table,
334             &mut data.host_resource_data,
335             data.instances[instance.instance]
336                 .as_mut()
337                 .unwrap()
338                 .get_mut(),
339         )
340     }
341 
342     pub(crate) fn component_resource_tables(
343         &mut self,
344         instance: Option<Instance>,
345     ) -> vm::component::ResourceTables<'_> {
346         self.component_resource_tables_and_host_resource_data(instance)
347             .0
348     }
349 
350     pub(crate) fn component_resource_tables_and_host_resource_data(
351         &mut self,
352         instance: Option<Instance>,
353     ) -> (
354         vm::component::ResourceTables<'_>,
355         &mut crate::component::HostResourceData,
356     ) {
357         let store_id = self.id();
358         let data = self.component_data_mut();
359         let guest = instance.map(|i| {
360             let i = i.id();
361             i.assert_belongs_to(store_id);
362             data.instances[i.instance]
363                 .as_mut()
364                 .unwrap()
365                 .get_mut()
366                 .instance_states()
367         });
368 
369         (
370             vm::component::ResourceTables {
371                 host_table: &mut data.component_host_table,
372                 task_state: &mut data.task_state,
373                 guest,
374             },
375             &mut data.host_resource_data,
376         )
377     }
378 
379     pub(crate) fn enter_call_not_concurrent(&mut self) {
380         let state = match &mut self.component_data_mut().task_state {
381             ComponentTaskState::NotConcurrent(state) => state,
382             ComponentTaskState::Concurrent(_) => unreachable!(),
383         };
384         state.scopes.push(CallContext::default());
385     }
386 
387     pub(crate) fn exit_call_not_concurrent(&mut self) {
388         let state = match &mut self.component_data_mut().task_state {
389             ComponentTaskState::NotConcurrent(state) => state,
390             ComponentTaskState::Concurrent(_) => unreachable!(),
391         };
392         state.scopes.pop();
393     }
394 }
395 
396 #[derive(Default)]
397 pub struct ComponentTasksNotConcurrent {
398     scopes: Vec<CallContext>,
399 }
400 
401 impl ComponentTaskState {
402     pub fn call_context(&mut self, id: u32) -> &mut CallContext {
403         match self {
404             ComponentTaskState::NotConcurrent(state) => &mut state.scopes[id as usize],
405             ComponentTaskState::Concurrent(state) => state.call_context(id),
406         }
407     }
408 
409     pub fn current_call_context_scope_id(&self) -> u32 {
410         match self {
411             ComponentTaskState::NotConcurrent(state) => {
412                 u32::try_from(state.scopes.len() - 1).unwrap()
413             }
414             ComponentTaskState::Concurrent(state) => state.current_call_context_scope_id(),
415         }
416     }
417 
418     pub fn concurrent_state_mut(&mut self) -> &mut ConcurrentState {
419         match self {
420             ComponentTaskState::Concurrent(state) => state,
421             ComponentTaskState::NotConcurrent(_) => {
422                 panic!("expected concurrent state to be present")
423             }
424         }
425     }
426 
427     #[cfg(feature = "component-model-async")]
428     fn is_concurrent(&self) -> bool {
429         match self {
430             ComponentTaskState::Concurrent(_) => true,
431             ComponentTaskState::NotConcurrent(_) => false,
432         }
433     }
434 }
435