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