1 //! Runtime support for the Component Model Async ABI. 2 //! 3 //! This module and its submodules provide host runtime support for Component 4 //! Model Async features such as async-lifted exports, async-lowered imports, 5 //! streams, futures, and related intrinsics. See [the Async 6 //! Explainer](https://github.com/WebAssembly/component-model/blob/main/design/mvp/Concurrency.md) 7 //! for a high-level overview. 8 //! 9 //! At the core of this support is an event loop which schedules and switches 10 //! between guest tasks and any host tasks they create. Each 11 //! `Store` will have at most one event loop running at any given 12 //! time, and that loop may be suspended and resumed by the host embedder using 13 //! e.g. `StoreContextMut::run_concurrent`. The `StoreContextMut::poll_until` 14 //! function contains the loop itself, while the 15 //! `StoreOpaque::concurrent_state` field holds its state. 16 //! 17 //! # Public API Overview 18 //! 19 //! ## Top-level API (e.g. kicking off host->guest calls and driving the event loop) 20 //! 21 //! - `[Typed]Func::call_concurrent`: Start a host->guest call to an 22 //! async-lifted or sync-lifted import, creating a guest task. 23 //! 24 //! - `StoreContextMut::run_concurrent`: Run the event loop for the specified 25 //! instance, allowing any and all tasks belonging to that instance to make 26 //! progress. 27 //! 28 //! - `StoreContextMut::spawn`: Run a background task as part of the event loop 29 //! for the specified instance. 30 //! 31 //! - `{Future,Stream}Reader::new`: Create a new Component Model `future` or 32 //! `stream` which may be passed to the guest. This takes a 33 //! `{Future,Stream}Producer` implementation which will be polled for items when 34 //! the consumer requests them. 35 //! 36 //! - `{Future,Stream}Reader::pipe`: Consume a `future` or `stream` by 37 //! connecting it to a `{Future,Stream}Consumer` which will consume any items 38 //! produced by the write end. 39 //! 40 //! ## Host Task API (e.g. implementing concurrent host functions and background tasks) 41 //! 42 //! - `LinkerInstance::func_wrap_concurrent`: Register a concurrent host 43 //! function with the linker. That function will take an `Accessor` as its 44 //! first parameter, which provides access to the store between (but not across) 45 //! await points. 46 //! 47 //! - `Accessor::with`: Access the store and its associated data. 48 //! 49 //! - `Accessor::spawn`: Run a background task as part of the event loop for the 50 //! store. This is equivalent to `StoreContextMut::spawn` but more convenient to use 51 //! in host functions. 52 53 use crate::component::func::{self, Func}; 54 use crate::component::{ 55 HasData, HasSelf, Instance, Resource, ResourceTable, ResourceTableError, RuntimeInstance, 56 }; 57 use crate::fiber::{self, StoreFiber, StoreFiberYield}; 58 use crate::store::{Store, StoreId, StoreInner, StoreOpaque, StoreToken}; 59 use crate::vm::component::{CallContext, ComponentInstance, HandleTable, ResourceTables}; 60 use crate::vm::{AlwaysMut, SendSyncPtr, VMFuncRef, VMMemoryDefinition, VMStore}; 61 use crate::{ 62 AsContext, AsContextMut, FuncType, Result, StoreContext, StoreContextMut, ValRaw, ValType, 63 bail, 64 error::{Context as _, format_err}, 65 }; 66 use error_contexts::GlobalErrorContextRefCount; 67 use futures::channel::oneshot; 68 use futures::future::{self, FutureExt}; 69 use futures::stream::{FuturesUnordered, StreamExt}; 70 use futures_and_streams::{FlatAbi, ReturnCode, TransmitHandle, TransmitIndex}; 71 use std::any::Any; 72 use std::borrow::ToOwned; 73 use std::boxed::Box; 74 use std::cell::UnsafeCell; 75 use std::collections::{BTreeMap, BTreeSet, HashSet, VecDeque}; 76 use std::fmt; 77 use std::future::Future; 78 use std::marker::PhantomData; 79 use std::mem::{self, ManuallyDrop, MaybeUninit}; 80 use std::ops::DerefMut; 81 use std::pin::{Pin, pin}; 82 use std::ptr::{self, NonNull}; 83 use std::slice; 84 use std::sync::Arc; 85 use std::task::{Context, Poll, Waker}; 86 use std::vec::Vec; 87 use table::{TableDebug, TableId}; 88 use wasmtime_environ::Trap; 89 use wasmtime_environ::component::{ 90 CanonicalAbiInfo, CanonicalOptions, CanonicalOptionsDataModel, ExportIndex, MAX_FLAT_PARAMS, 91 MAX_FLAT_RESULTS, OptionsIndex, PREPARE_ASYNC_NO_RESULT, PREPARE_ASYNC_WITH_RESULT, 92 RuntimeComponentInstanceIndex, RuntimeTableIndex, StringEncoding, 93 TypeComponentGlobalErrorContextTableIndex, TypeComponentLocalErrorContextTableIndex, 94 TypeFuncIndex, TypeFutureTableIndex, TypeStreamTableIndex, TypeTupleIndex, 95 }; 96 97 pub use abort::JoinHandle; 98 pub use future_stream_any::{FutureAny, StreamAny}; 99 pub use futures_and_streams::{ 100 Destination, DirectDestination, DirectSource, ErrorContext, FutureConsumer, FutureProducer, 101 FutureReader, GuardedFutureReader, GuardedStreamReader, ReadBuffer, Source, StreamConsumer, 102 StreamProducer, StreamReader, StreamResult, VecBuffer, WriteBuffer, 103 }; 104 pub(crate) use futures_and_streams::{ResourcePair, lower_error_context_to_index}; 105 106 mod abort; 107 mod error_contexts; 108 mod future_stream_any; 109 mod futures_and_streams; 110 pub(crate) mod table; 111 pub(crate) mod tls; 112 113 /// Constant defined in the Component Model spec to indicate that the async 114 /// intrinsic (e.g. `future.write`) has not yet completed. 115 const BLOCKED: u32 = 0xffff_ffff; 116 117 /// Corresponds to `CallState` in the upstream spec. 118 #[derive(Clone, Copy, Eq, PartialEq, Debug)] 119 pub enum Status { 120 Starting = 0, 121 Started = 1, 122 Returned = 2, 123 StartCancelled = 3, 124 ReturnCancelled = 4, 125 } 126 127 impl Status { 128 /// Packs this status and the optional `waitable` provided into a 32-bit 129 /// result that the canonical ABI requires. 130 /// 131 /// The low 4 bits are reserved for the status while the upper 28 bits are 132 /// the waitable, if present. 133 pub fn pack(self, waitable: Option<u32>) -> u32 { 134 assert!(matches!(self, Status::Returned) == waitable.is_none()); 135 let waitable = waitable.unwrap_or(0); 136 assert!(waitable < (1 << 28)); 137 (waitable << 4) | (self as u32) 138 } 139 } 140 141 /// Corresponds to `EventCode` in the Component Model spec, plus related payload 142 /// data. 143 #[derive(Clone, Copy, Debug)] 144 enum Event { 145 None, 146 Cancelled, 147 Subtask { 148 status: Status, 149 }, 150 StreamRead { 151 code: ReturnCode, 152 pending: Option<(TypeStreamTableIndex, u32)>, 153 }, 154 StreamWrite { 155 code: ReturnCode, 156 pending: Option<(TypeStreamTableIndex, u32)>, 157 }, 158 FutureRead { 159 code: ReturnCode, 160 pending: Option<(TypeFutureTableIndex, u32)>, 161 }, 162 FutureWrite { 163 code: ReturnCode, 164 pending: Option<(TypeFutureTableIndex, u32)>, 165 }, 166 } 167 168 impl Event { 169 /// Lower this event to core Wasm integers for delivery to the guest. 170 /// 171 /// Note that the waitable handle, if any, is assumed to be lowered 172 /// separately. 173 fn parts(self) -> (u32, u32) { 174 const EVENT_NONE: u32 = 0; 175 const EVENT_SUBTASK: u32 = 1; 176 const EVENT_STREAM_READ: u32 = 2; 177 const EVENT_STREAM_WRITE: u32 = 3; 178 const EVENT_FUTURE_READ: u32 = 4; 179 const EVENT_FUTURE_WRITE: u32 = 5; 180 const EVENT_CANCELLED: u32 = 6; 181 match self { 182 Event::None => (EVENT_NONE, 0), 183 Event::Cancelled => (EVENT_CANCELLED, 0), 184 Event::Subtask { status } => (EVENT_SUBTASK, status as u32), 185 Event::StreamRead { code, .. } => (EVENT_STREAM_READ, code.encode()), 186 Event::StreamWrite { code, .. } => (EVENT_STREAM_WRITE, code.encode()), 187 Event::FutureRead { code, .. } => (EVENT_FUTURE_READ, code.encode()), 188 Event::FutureWrite { code, .. } => (EVENT_FUTURE_WRITE, code.encode()), 189 } 190 } 191 } 192 193 /// Corresponds to `CallbackCode` in the spec. 194 mod callback_code { 195 pub const EXIT: u32 = 0; 196 pub const YIELD: u32 = 1; 197 pub const WAIT: u32 = 2; 198 } 199 200 /// A flag indicating that the callee is an async-lowered export. 201 /// 202 /// This may be passed to the `async-start` intrinsic from a fused adapter. 203 const START_FLAG_ASYNC_CALLEE: u32 = wasmtime_environ::component::START_FLAG_ASYNC_CALLEE as u32; 204 205 /// Provides access to either store data (via the `get` method) or the store 206 /// itself (via [`AsContext`]/[`AsContextMut`]), as well as the component 207 /// instance to which the current host task belongs. 208 /// 209 /// See [`Accessor::with`] for details. 210 pub struct Access<'a, T: 'static, D: HasData + ?Sized = HasSelf<T>> { 211 store: StoreContextMut<'a, T>, 212 get_data: fn(&mut T) -> D::Data<'_>, 213 } 214 215 impl<'a, T, D> Access<'a, T, D> 216 where 217 D: HasData + ?Sized, 218 T: 'static, 219 { 220 /// Creates a new [`Access`] from its component parts. 221 pub fn new(store: StoreContextMut<'a, T>, get_data: fn(&mut T) -> D::Data<'_>) -> Self { 222 Self { store, get_data } 223 } 224 225 /// Get mutable access to the store data. 226 pub fn data_mut(&mut self) -> &mut T { 227 self.store.data_mut() 228 } 229 230 /// Get mutable access to the store data. 231 pub fn get(&mut self) -> D::Data<'_> { 232 (self.get_data)(self.data_mut()) 233 } 234 235 /// Spawn a background task. 236 /// 237 /// See [`Accessor::spawn`] for details. 238 pub fn spawn(&mut self, task: impl AccessorTask<T, D>) -> JoinHandle 239 where 240 T: 'static, 241 { 242 let accessor = Accessor { 243 get_data: self.get_data, 244 token: StoreToken::new(self.store.as_context_mut()), 245 }; 246 self.store 247 .as_context_mut() 248 .spawn_with_accessor(accessor, task) 249 } 250 251 /// Returns the getter this accessor is using to project from `T` into 252 /// `D::Data`. 253 pub fn getter(&self) -> fn(&mut T) -> D::Data<'_> { 254 self.get_data 255 } 256 } 257 258 impl<'a, T, D> AsContext for Access<'a, T, D> 259 where 260 D: HasData + ?Sized, 261 T: 'static, 262 { 263 type Data = T; 264 265 fn as_context(&self) -> StoreContext<'_, T> { 266 self.store.as_context() 267 } 268 } 269 270 impl<'a, T, D> AsContextMut for Access<'a, T, D> 271 where 272 D: HasData + ?Sized, 273 T: 'static, 274 { 275 fn as_context_mut(&mut self) -> StoreContextMut<'_, T> { 276 self.store.as_context_mut() 277 } 278 } 279 280 /// Provides scoped mutable access to store data in the context of a concurrent 281 /// host task future. 282 /// 283 /// This allows multiple host task futures to execute concurrently and access 284 /// the store between (but not across) `await` points. 285 /// 286 /// # Rationale 287 /// 288 /// This structure is sort of like `&mut T` plus a projection from `&mut T` to 289 /// `D::Data<'_>`. The problem this is solving, however, is that it does not 290 /// literally store these values. The basic problem is that when a concurrent 291 /// host future is being polled it has access to `&mut T` (and the whole 292 /// `Store`) but when it's not being polled it does not have access to these 293 /// values. This reflects how the store is only ever polling one future at a 294 /// time so the store is effectively being passed between futures. 295 /// 296 /// Rust's `Future` trait, however, has no means of passing a `Store` 297 /// temporarily between futures. The [`Context`](std::task::Context) type does 298 /// not have the ability to attach arbitrary information to it at this time. 299 /// This type, [`Accessor`], is used to bridge this expressivity gap. 300 /// 301 /// The [`Accessor`] type here represents the ability to acquire, temporarily in 302 /// a synchronous manner, the current store. The [`Accessor::with`] function 303 /// yields an [`Access`] which can be used to access [`StoreContextMut`], `&mut 304 /// T`, or `D::Data<'_>`. Note though that [`Accessor::with`] intentionally does 305 /// not take an `async` closure as its argument, instead it's a synchronous 306 /// closure which must complete during on run of `Future::poll`. This reflects 307 /// how the store is temporarily made available while a host future is being 308 /// polled. 309 /// 310 /// # Implementation 311 /// 312 /// This type does not actually store `&mut T` nor `StoreContextMut<T>`, and 313 /// this type additionally doesn't even have a lifetime parameter. This is 314 /// instead a representation of proof of the ability to acquire these while a 315 /// future is being polled. Wasmtime will, when it polls a host future, 316 /// configure ambient state such that the `Accessor` that a future closes over 317 /// will work and be able to access the store. 318 /// 319 /// This has a number of implications for users such as: 320 /// 321 /// * It's intentional that `Accessor` cannot be cloned, it needs to stay within 322 /// the lifetime of a single future. 323 /// * A future is expected to, however, close over an `Accessor` and keep it 324 /// alive probably for the duration of the entire future. 325 /// * Different host futures will be given different `Accessor`s, and that's 326 /// intentional. 327 /// * The `Accessor` type is `Send` and `Sync` irrespective of `T` which 328 /// alleviates some otherwise required bounds to be written down. 329 /// 330 /// # Using `Accessor` in `Drop` 331 /// 332 /// The methods on `Accessor` are only expected to work in the context of 333 /// `Future::poll` and are not guaranteed to work in `Drop`. This is because a 334 /// host future can be dropped at any time throughout the system and Wasmtime 335 /// store context is not necessarily available at that time. It's recommended to 336 /// not use `Accessor` methods in anything connected to a `Drop` implementation 337 /// as they will panic and have unintended results. If you run into this though 338 /// feel free to file an issue on the Wasmtime repository. 339 pub struct Accessor<T: 'static, D = HasSelf<T>> 340 where 341 D: HasData + ?Sized, 342 { 343 token: StoreToken<T>, 344 get_data: fn(&mut T) -> D::Data<'_>, 345 } 346 347 /// A helper trait to take any type of accessor-with-data in functions. 348 /// 349 /// This trait is similar to [`AsContextMut`] except that it's used when 350 /// working with an [`Accessor`] instead of a [`StoreContextMut`]. The 351 /// [`Accessor`] is the main type used in concurrent settings and is passed to 352 /// functions such as [`Func::call_concurrent`] or [`FutureWriter::write`]. 353 /// 354 /// This trait is implemented for [`Accessor`] and `&T` where `T` implements 355 /// this trait. This effectively means that regardless of the `D` in 356 /// `Accessor<T, D>` it can still be passed to a function which just needs a 357 /// store accessor. 358 /// 359 /// Acquiring an [`Accessor`] can be done through 360 /// [`StoreContextMut::run_concurrent`] for example or in a host function 361 /// through 362 /// [`Linker::func_wrap_concurrent`](crate::component::Linker::func_wrap_concurrent). 363 pub trait AsAccessor { 364 /// The `T` in `Store<T>` that this accessor refers to. 365 type Data: 'static; 366 367 /// The `D` in `Accessor<T, D>`, or the projection out of 368 /// `Self::Data`. 369 type AccessorData: HasData + ?Sized; 370 371 /// Returns the accessor that this is referring to. 372 fn as_accessor(&self) -> &Accessor<Self::Data, Self::AccessorData>; 373 } 374 375 impl<T: AsAccessor + ?Sized> AsAccessor for &T { 376 type Data = T::Data; 377 type AccessorData = T::AccessorData; 378 379 fn as_accessor(&self) -> &Accessor<Self::Data, Self::AccessorData> { 380 T::as_accessor(self) 381 } 382 } 383 384 impl<T, D: HasData + ?Sized> AsAccessor for Accessor<T, D> { 385 type Data = T; 386 type AccessorData = D; 387 388 fn as_accessor(&self) -> &Accessor<T, D> { 389 self 390 } 391 } 392 393 // Note that it is intentional at this time that `Accessor` does not actually 394 // store `&mut T` or anything similar. This distinctly enables the `Accessor` 395 // structure to be both `Send` and `Sync` regardless of what `T` is (or `D` for 396 // that matter). This is used to ergonomically simplify bindings where the 397 // majority of the time `Accessor` is closed over in a future which then needs 398 // to be `Send` and `Sync`. To avoid needing to write `T: Send` everywhere (as 399 // you already have to write `T: 'static`...) it helps to avoid this. 400 // 401 // Note as well that `Accessor` doesn't actually store its data at all. Instead 402 // it's more of a "proof" of what can be accessed from TLS. API design around 403 // `Accessor` and functions like `Linker::func_wrap_concurrent` are 404 // intentionally made to ensure that `Accessor` is ideally only used in the 405 // context that TLS variables are actually set. For example host functions are 406 // given `&Accessor`, not `Accessor`, and this prevents them from persisting 407 // the value outside of a future. Within the future the TLS variables are all 408 // guaranteed to be set while the future is being polled. 409 // 410 // Finally though this is not an ironclad guarantee, but nor does it need to be. 411 // The TLS APIs are designed to panic or otherwise model usage where they're 412 // called recursively or similar. It's hoped that code cannot be constructed to 413 // actually hit this at runtime but this is not a safety requirement at this 414 // time. 415 const _: () = { 416 const fn assert<T: Send + Sync>() {} 417 assert::<Accessor<UnsafeCell<u32>>>(); 418 }; 419 420 impl<T> Accessor<T> { 421 /// Creates a new `Accessor` backed by the specified functions. 422 /// 423 /// - `get`: used to retrieve the store 424 /// 425 /// - `get_data`: used to "project" from the store's associated data to 426 /// another type (e.g. a field of that data or a wrapper around it). 427 /// 428 /// - `spawn`: used to queue spawned background tasks to be run later 429 pub(crate) fn new(token: StoreToken<T>) -> Self { 430 Self { 431 token, 432 get_data: |x| x, 433 } 434 } 435 } 436 437 impl<T, D> Accessor<T, D> 438 where 439 D: HasData + ?Sized, 440 { 441 /// Run the specified closure, passing it mutable access to the store. 442 /// 443 /// This function is one of the main building blocks of the [`Accessor`] 444 /// type. This yields synchronous, blocking, access to the store via an 445 /// [`Access`]. The [`Access`] implements [`AsContextMut`] in addition to 446 /// providing the ability to access `D` via [`Access::get`]. Note that the 447 /// `fun` here is given only temporary access to the store and `T`/`D` 448 /// meaning that the return value `R` here is not allowed to capture borrows 449 /// into the two. If access is needed to data within `T` or `D` outside of 450 /// this closure then it must be `clone`d out, for example. 451 /// 452 /// # Panics 453 /// 454 /// This function will panic if it is call recursively with any other 455 /// accessor already in scope. For example if `with` is called within `fun`, 456 /// then this function will panic. It is up to the embedder to ensure that 457 /// this does not happen. 458 pub fn with<R>(&self, fun: impl FnOnce(Access<'_, T, D>) -> R) -> R { 459 tls::get(|vmstore| { 460 fun(Access { 461 store: self.token.as_context_mut(vmstore), 462 get_data: self.get_data, 463 }) 464 }) 465 } 466 467 /// Returns the getter this accessor is using to project from `T` into 468 /// `D::Data`. 469 pub fn getter(&self) -> fn(&mut T) -> D::Data<'_> { 470 self.get_data 471 } 472 473 /// Changes this accessor to access `D2` instead of the current type 474 /// parameter `D`. 475 /// 476 /// This changes the underlying data access from `T` to `D2::Data<'_>`. 477 /// 478 /// # Panics 479 /// 480 /// When using this API the returned value is disconnected from `&self` and 481 /// the lifetime binding the `self` argument. An `Accessor` only works 482 /// within the context of the closure or async closure that it was 483 /// originally given to, however. This means that due to the fact that the 484 /// returned value has no lifetime connection it's possible to use the 485 /// accessor outside of `&self`, the original accessor, and panic. 486 /// 487 /// The returned value should only be used within the scope of the original 488 /// `Accessor` that `self` refers to. 489 pub fn with_getter<D2: HasData>( 490 &self, 491 get_data: fn(&mut T) -> D2::Data<'_>, 492 ) -> Accessor<T, D2> { 493 Accessor { 494 token: self.token, 495 get_data, 496 } 497 } 498 499 /// Spawn a background task which will receive an `&Accessor<T, D>` and 500 /// run concurrently with any other tasks in progress for the current 501 /// store. 502 /// 503 /// This is particularly useful for host functions which return a `stream` 504 /// or `future` such that the code to write to the write end of that 505 /// `stream` or `future` must run after the function returns. 506 /// 507 /// The returned [`JoinHandle`] may be used to cancel the task. 508 /// 509 /// # Panics 510 /// 511 /// Panics if called within a closure provided to the [`Accessor::with`] 512 /// function. This can only be called outside an active invocation of 513 /// [`Accessor::with`]. 514 pub fn spawn(&self, task: impl AccessorTask<T, D>) -> JoinHandle 515 where 516 T: 'static, 517 { 518 let accessor = self.clone_for_spawn(); 519 self.with(|mut access| access.as_context_mut().spawn_with_accessor(accessor, task)) 520 } 521 522 fn clone_for_spawn(&self) -> Self { 523 Self { 524 token: self.token, 525 get_data: self.get_data, 526 } 527 } 528 } 529 530 /// Represents a task which may be provided to `Accessor::spawn`, 531 /// `Accessor::forward`, or `StorecContextMut::spawn`. 532 // TODO: Replace this with `std::ops::AsyncFnOnce` when that becomes a viable 533 // option. 534 // 535 // As of this writing, it's not possible to specify e.g. `Send` and `Sync` 536 // bounds on the `Future` type returned by an `AsyncFnOnce`. Also, using `F: 537 // Future<Output = Result<()>> + Send + Sync, FN: FnOnce(&Accessor<T>) -> F + 538 // Send + Sync + 'static` fails with a type mismatch error when we try to pass 539 // it an async closure (e.g. `async move |_| { ... }`). So this seems to be the 540 // best we can do for the time being. 541 pub trait AccessorTask<T, D = HasSelf<T>>: Send + 'static 542 where 543 D: HasData + ?Sized, 544 { 545 /// Run the task. 546 fn run(self, accessor: &Accessor<T, D>) -> impl Future<Output = Result<()>> + Send; 547 } 548 549 /// Represents parameter and result metadata for the caller side of a 550 /// guest->guest call orchestrated by a fused adapter. 551 enum CallerInfo { 552 /// Metadata for a call to an async-lowered import 553 Async { 554 params: Vec<ValRaw>, 555 has_result: bool, 556 }, 557 /// Metadata for a call to an sync-lowered import 558 Sync { 559 params: Vec<ValRaw>, 560 result_count: u32, 561 }, 562 } 563 564 /// Indicates how a guest task is waiting on a waitable set. 565 enum WaitMode { 566 /// The guest task is waiting using `task.wait` 567 Fiber(StoreFiber<'static>), 568 /// The guest task is waiting via a callback declared as part of an 569 /// async-lifted export. 570 Callback(Instance), 571 } 572 573 /// Represents the reason a fiber is suspending itself. 574 #[derive(Debug)] 575 enum SuspendReason { 576 /// The fiber is waiting for an event to be delivered to the specified 577 /// waitable set or task. 578 Waiting { 579 set: TableId<WaitableSet>, 580 thread: QualifiedThreadId, 581 skip_may_block_check: bool, 582 }, 583 /// The fiber has finished handling its most recent work item and is waiting 584 /// for another (or to be dropped if it is no longer needed). 585 NeedWork, 586 /// The fiber is yielding and should be resumed once other tasks have had a 587 /// chance to run. 588 Yielding { 589 thread: QualifiedThreadId, 590 skip_may_block_check: bool, 591 }, 592 /// The fiber was explicitly suspended with a call to `thread.suspend` or `thread.switch-to`. 593 ExplicitlySuspending { 594 thread: QualifiedThreadId, 595 skip_may_block_check: bool, 596 }, 597 } 598 599 /// Represents a pending call into guest code for a given guest task. 600 enum GuestCallKind { 601 /// Indicates there's an event to deliver to the task, possibly related to a 602 /// waitable set the task has been waiting on or polling. 603 DeliverEvent { 604 /// The instance to which the task belongs. 605 instance: Instance, 606 /// The waitable set the event belongs to, if any. 607 /// 608 /// If this is `None` the event will be waiting in the 609 /// `GuestTask::event` field for the task. 610 set: Option<TableId<WaitableSet>>, 611 }, 612 /// Indicates that a new guest task call is pending and may be executed 613 /// using the specified closure. 614 /// 615 /// If the closure returns `Ok(Some(call))`, the `call` should be run 616 /// immediately using `handle_guest_call`. 617 StartImplicit(Box<dyn FnOnce(&mut dyn VMStore) -> Result<Option<GuestCall>> + Send + Sync>), 618 StartExplicit(Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send + Sync>), 619 } 620 621 impl fmt::Debug for GuestCallKind { 622 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 623 match self { 624 Self::DeliverEvent { instance, set } => f 625 .debug_struct("DeliverEvent") 626 .field("instance", instance) 627 .field("set", set) 628 .finish(), 629 Self::StartImplicit(_) => f.debug_tuple("StartImplicit").finish(), 630 Self::StartExplicit(_) => f.debug_tuple("StartExplicit").finish(), 631 } 632 } 633 } 634 635 /// Represents a pending call into guest code for a given guest thread. 636 #[derive(Debug)] 637 struct GuestCall { 638 thread: QualifiedThreadId, 639 kind: GuestCallKind, 640 } 641 642 impl GuestCall { 643 /// Returns whether or not the call is ready to run. 644 /// 645 /// A call will not be ready to run if either: 646 /// 647 /// - the (sub-)component instance to be called has already been entered and 648 /// cannot be reentered until an in-progress call completes 649 /// 650 /// - the call is for a not-yet started task and the (sub-)component 651 /// instance to be called has backpressure enabled 652 fn is_ready(&self, store: &mut StoreOpaque) -> Result<bool> { 653 let instance = store 654 .concurrent_state_mut() 655 .get_mut(self.thread.task)? 656 .instance; 657 let state = store.instance_state(instance); 658 659 let ready = match &self.kind { 660 GuestCallKind::DeliverEvent { .. } => !state.do_not_enter, 661 GuestCallKind::StartImplicit(_) => !(state.do_not_enter || state.backpressure > 0), 662 GuestCallKind::StartExplicit(_) => true, 663 }; 664 log::trace!( 665 "call {self:?} ready? {ready} (do_not_enter: {}; backpressure: {})", 666 state.do_not_enter, 667 state.backpressure 668 ); 669 Ok(ready) 670 } 671 } 672 673 /// Job to be run on a worker fiber. 674 enum WorkerItem { 675 GuestCall(GuestCall), 676 Function(AlwaysMut<Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send>>), 677 } 678 679 /// Represents a pending work item to be handled by the event loop for a given 680 /// component instance. 681 enum WorkItem { 682 /// A host task to be pushed to `ConcurrentState::futures`. 683 PushFuture(AlwaysMut<HostTaskFuture>), 684 /// A fiber to resume. 685 ResumeFiber(StoreFiber<'static>), 686 /// A pending call into guest code for a given guest task. 687 GuestCall(GuestCall), 688 /// A job to run on a worker fiber. 689 WorkerFunction(AlwaysMut<Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send>>), 690 } 691 692 impl fmt::Debug for WorkItem { 693 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 694 match self { 695 Self::PushFuture(_) => f.debug_tuple("PushFuture").finish(), 696 Self::ResumeFiber(_) => f.debug_tuple("ResumeFiber").finish(), 697 Self::GuestCall(call) => f.debug_tuple("GuestCall").field(call).finish(), 698 Self::WorkerFunction(_) => f.debug_tuple("WorkerFunction").finish(), 699 } 700 } 701 } 702 703 /// Whether a suspension intrinsic was cancelled or completed 704 #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] 705 pub(crate) enum WaitResult { 706 Cancelled, 707 Completed, 708 } 709 710 /// Poll the specified future until it completes on behalf of a guest->host call 711 /// using a sync-lowered import. 712 /// 713 /// This is similar to `Instance::first_poll` except it's for sync-lowered 714 /// imports, meaning we don't need to handle cancellation and we can block the 715 /// caller until the task completes, at which point the caller can handle 716 /// lowering the result to the guest's stack and linear memory. 717 pub(crate) fn poll_and_block<R: Send + Sync + 'static>( 718 store: &mut dyn VMStore, 719 future: impl Future<Output = Result<R>> + Send + 'static, 720 caller_instance: RuntimeInstance, 721 ) -> Result<R> { 722 let state = store.concurrent_state_mut(); 723 724 // If there is no current guest thread set, that means the host function was 725 // registered using e.g. `LinkerInstance::func_wrap`, in which case it 726 // should complete immediately. 727 let Some(caller) = state.guest_thread else { 728 return match pin!(future).poll(&mut Context::from_waker(&Waker::noop())) { 729 Poll::Ready(result) => result, 730 Poll::Pending => { 731 unreachable!() 732 } 733 }; 734 }; 735 736 // Save any existing result stashed in `GuestTask::result` so we can replace 737 // it with the new result. 738 let old_result = state 739 .get_mut(caller.task) 740 .with_context(|| format!("bad handle: {caller:?}"))? 741 .result 742 .take(); 743 744 // Add a temporary host task into the table so we can track its progress. 745 // Note that we'll never allocate a waitable handle for the guest since 746 // we're being called synchronously. 747 let task = state.push(HostTask::new(caller_instance, None))?; 748 749 log::trace!("new host task child of {caller:?}: {task:?}"); 750 751 // Wrap the future in a closure which will take care of stashing the result 752 // in `GuestTask::result` and resuming this fiber when the host task 753 // completes. 754 let mut future = Box::pin(async move { 755 let result = future.await?; 756 tls::get(move |store| { 757 let state = store.concurrent_state_mut(); 758 state.get_mut(caller.task)?.result = Some(Box::new(result) as _); 759 760 Waitable::Host(task).set_event( 761 state, 762 Some(Event::Subtask { 763 status: Status::Returned, 764 }), 765 )?; 766 767 Ok(()) 768 }) 769 }) as HostTaskFuture; 770 771 // Finally, poll the future. We can use a dummy `Waker` here because we'll 772 // add the future to `ConcurrentState::futures` and poll it automatically 773 // from the event loop if it doesn't complete immediately here. 774 let poll = tls::set(store, || { 775 future 776 .as_mut() 777 .poll(&mut Context::from_waker(&Waker::noop())) 778 }); 779 780 match poll { 781 Poll::Ready(result) => { 782 // It completed immediately; check the result and delete the task. 783 result?; 784 log::trace!("delete host task {task:?} (already ready)"); 785 store.concurrent_state_mut().delete(task)?; 786 } 787 Poll::Pending => { 788 // It did not complete immediately; add it to 789 // `ConcurrentState::futures` so it will be polled via the event 790 // loop; then use `GuestTask::sync_call_set` to wait for the task to 791 // complete, suspending the current fiber until it does so. 792 let state = store.concurrent_state_mut(); 793 state.push_future(future); 794 795 let set = state.get_mut(caller.task)?.sync_call_set; 796 Waitable::Host(task).join(state, Some(set))?; 797 798 store.suspend(SuspendReason::Waiting { 799 set, 800 thread: caller, 801 skip_may_block_check: false, 802 })?; 803 } 804 } 805 806 // Retrieve and return the result. 807 Ok(*mem::replace( 808 &mut store.concurrent_state_mut().get_mut(caller.task)?.result, 809 old_result, 810 ) 811 .unwrap() 812 .downcast() 813 .unwrap()) 814 } 815 816 /// Execute the specified guest call. 817 fn handle_guest_call(store: &mut dyn VMStore, call: GuestCall) -> Result<()> { 818 let mut next = Some(call); 819 while let Some(call) = next.take() { 820 match call.kind { 821 GuestCallKind::DeliverEvent { instance, set } => { 822 let (event, waitable) = instance 823 .get_event(store, call.thread.task, set, true)? 824 .unwrap(); 825 let state = store.concurrent_state_mut(); 826 let task = state.get_mut(call.thread.task)?; 827 let runtime_instance = task.instance; 828 let handle = waitable.map(|(_, v)| v).unwrap_or(0); 829 830 log::trace!( 831 "use callback to deliver event {event:?} to {:?} for {waitable:?}", 832 call.thread, 833 ); 834 835 let old_thread = store.set_thread(Some(call.thread)); 836 log::trace!( 837 "GuestCallKind::DeliverEvent: replaced {old_thread:?} with {:?} as current thread", 838 call.thread 839 ); 840 841 store.maybe_push_call_context(call.thread.task)?; 842 843 store.enter_instance(runtime_instance); 844 845 let callback = store 846 .concurrent_state_mut() 847 .get_mut(call.thread.task)? 848 .callback 849 .take() 850 .unwrap(); 851 852 let code = callback(store, event, handle)?; 853 854 store 855 .concurrent_state_mut() 856 .get_mut(call.thread.task)? 857 .callback = Some(callback); 858 859 store.exit_instance(runtime_instance)?; 860 861 store.maybe_pop_call_context(call.thread.task)?; 862 863 store.set_thread(old_thread); 864 865 next = instance.handle_callback_code( 866 store, 867 call.thread, 868 runtime_instance.index, 869 code, 870 )?; 871 872 log::trace!( 873 "GuestCallKind::DeliverEvent: restored {old_thread:?} as current thread" 874 ); 875 } 876 GuestCallKind::StartImplicit(fun) => { 877 next = fun(store)?; 878 } 879 GuestCallKind::StartExplicit(fun) => { 880 fun(store)?; 881 } 882 } 883 } 884 885 Ok(()) 886 } 887 888 impl<T> Store<T> { 889 /// Convenience wrapper for [`StoreContextMut::run_concurrent`]. 890 pub async fn run_concurrent<R>(&mut self, fun: impl AsyncFnOnce(&Accessor<T>) -> R) -> Result<R> 891 where 892 T: Send + 'static, 893 { 894 self.as_context_mut().run_concurrent(fun).await 895 } 896 897 #[doc(hidden)] 898 pub fn assert_concurrent_state_empty(&mut self) { 899 self.as_context_mut().assert_concurrent_state_empty(); 900 } 901 902 /// Convenience wrapper for [`StoreContextMut::spawn`]. 903 pub fn spawn(&mut self, task: impl AccessorTask<T, HasSelf<T>>) -> JoinHandle 904 where 905 T: 'static, 906 { 907 self.as_context_mut().spawn(task) 908 } 909 } 910 911 impl<T> StoreContextMut<'_, T> { 912 /// Assert that all the relevant tables and queues in the concurrent state 913 /// for this store are empty. 914 /// 915 /// This is for sanity checking in integration tests 916 /// (e.g. `component-async-tests`) that the relevant state has been cleared 917 /// after each test concludes. This should help us catch leaks, e.g. guest 918 /// tasks which haven't been deleted despite having completed and having 919 /// been dropped by their supertasks. 920 #[doc(hidden)] 921 pub fn assert_concurrent_state_empty(self) { 922 let store = self.0; 923 store 924 .store_data_mut() 925 .components 926 .assert_instance_states_empty(); 927 let state = store.concurrent_state_mut(); 928 assert!( 929 state.table.get_mut().is_empty(), 930 "non-empty table: {:?}", 931 state.table.get_mut() 932 ); 933 assert!(state.high_priority.is_empty()); 934 assert!(state.low_priority.is_empty()); 935 assert!(state.guest_thread.is_none()); 936 assert!(state.futures.get_mut().as_ref().unwrap().is_empty()); 937 assert!(state.global_error_context_ref_counts.is_empty()); 938 } 939 940 /// Spawn a background task to run as part of this instance's event loop. 941 /// 942 /// The task will receive an `&Accessor<U>` and run concurrently with 943 /// any other tasks in progress for the instance. 944 /// 945 /// Note that the task will only make progress if and when the event loop 946 /// for this instance is run. 947 /// 948 /// The returned [`SpawnHandle`] may be used to cancel the task. 949 pub fn spawn(mut self, task: impl AccessorTask<T>) -> JoinHandle 950 where 951 T: 'static, 952 { 953 let accessor = Accessor::new(StoreToken::new(self.as_context_mut())); 954 self.spawn_with_accessor(accessor, task) 955 } 956 957 /// Internal implementation of `spawn` functions where a `store` is 958 /// available along with an `Accessor`. 959 fn spawn_with_accessor<D>( 960 self, 961 accessor: Accessor<T, D>, 962 task: impl AccessorTask<T, D>, 963 ) -> JoinHandle 964 where 965 T: 'static, 966 D: HasData + ?Sized, 967 { 968 // Create an "abortable future" here where internally the future will 969 // hook calls to poll and possibly spawn more background tasks on each 970 // iteration. 971 let (handle, future) = JoinHandle::run(async move { task.run(&accessor).await }); 972 self.0 973 .concurrent_state_mut() 974 .push_future(Box::pin(async move { future.await.unwrap_or(Ok(())) })); 975 handle 976 } 977 978 /// Run the specified closure `fun` to completion as part of this store's 979 /// event loop. 980 /// 981 /// This will run `fun` as part of this store's event loop until it 982 /// yields a result. `fun` is provided an [`Accessor`], which provides 983 /// controlled access to the store and its data. 984 /// 985 /// This function can be used to invoke [`Func::call_concurrent`] for 986 /// example within the async closure provided here. 987 /// 988 /// # Store-blocking behavior 989 /// 990 /// 991 /// 992 /// At this time there are certain situations in which the `Future` returned 993 /// by the `AsyncFnOnce` passed to this function will not be polled for an 994 /// extended period of time, despite one or more `Waker::wake` events having 995 /// occurred for the task to which it belongs. This can manifest as the 996 /// `Future` seeming to be "blocked" or "locked up", but is actually due to 997 /// the `Store` being held by e.g. a blocking host function, preventing the 998 /// `Future` from being polled. A canonical example of this is when the 999 /// `fun` provided to this function attempts to set a timeout for an 1000 /// invocation of a wasm function. In this situation the async closure is 1001 /// waiting both on (a) the wasm computation to finish, and (b) the timeout 1002 /// to elapse. At this time this setup will not always work and the timeout 1003 /// may not reliably fire. 1004 /// 1005 /// This function will not block the current thread and as such is always 1006 /// suitable to run in an `async` context, but the current implementation of 1007 /// Wasmtime can lead to situations where a certain wasm computation is 1008 /// required to make progress the closure to make progress. This is an 1009 /// artifact of Wasmtime's historical implementation of `async` functions 1010 /// and is the topic of [#11869] and [#11870]. In the timeout example from 1011 /// above it means that Wasmtime can get "wedged" for a bit where (a) must 1012 /// progress for a readiness notification of (b) to get delivered. 1013 /// 1014 /// This effectively means that it's not possible to reliably perform a 1015 /// "select" operation within the `fun` closure, which timeouts for example 1016 /// are based on. Fixing this requires some relatively major refactoring 1017 /// work within Wasmtime itself. This is a known pitfall otherwise and one 1018 /// that is intended to be fixed one day. In the meantime it's recommended 1019 /// to apply timeouts or such to the entire `run_concurrent` call itself 1020 /// rather than internally. 1021 /// 1022 /// [#11869]: https://github.com/bytecodealliance/wasmtime/issues/11869 1023 /// [#11870]: https://github.com/bytecodealliance/wasmtime/issues/11870 1024 /// 1025 /// # Example 1026 /// 1027 /// ``` 1028 /// # use { 1029 /// # wasmtime::{ 1030 /// # error::{Result}, 1031 /// # component::{ Component, Linker, Resource, ResourceTable}, 1032 /// # Config, Engine, Store 1033 /// # }, 1034 /// # }; 1035 /// # 1036 /// # struct MyResource(u32); 1037 /// # struct Ctx { table: ResourceTable } 1038 /// # 1039 /// # async fn foo() -> Result<()> { 1040 /// # let mut config = Config::new(); 1041 /// # let engine = Engine::new(&config)?; 1042 /// # let mut store = Store::new(&engine, Ctx { table: ResourceTable::new() }); 1043 /// # let mut linker = Linker::new(&engine); 1044 /// # let component = Component::new(&engine, "")?; 1045 /// # let instance = linker.instantiate_async(&mut store, &component).await?; 1046 /// # let foo = instance.get_typed_func::<(Resource<MyResource>,), (Resource<MyResource>,)>(&mut store, "foo")?; 1047 /// # let bar = instance.get_typed_func::<(u32,), ()>(&mut store, "bar")?; 1048 /// store.run_concurrent(async |accessor| -> wasmtime::Result<_> { 1049 /// let resource = accessor.with(|mut access| access.get().table.push(MyResource(42)))?; 1050 /// let (another_resource,) = foo.call_concurrent(accessor, (resource,)).await?.0; 1051 /// let value = accessor.with(|mut access| access.get().table.delete(another_resource))?; 1052 /// bar.call_concurrent(accessor, (value.0,)).await?; 1053 /// Ok(()) 1054 /// }).await??; 1055 /// # Ok(()) 1056 /// # } 1057 /// ``` 1058 pub async fn run_concurrent<R>(self, fun: impl AsyncFnOnce(&Accessor<T>) -> R) -> Result<R> 1059 where 1060 T: Send + 'static, 1061 { 1062 self.do_run_concurrent(fun, false).await 1063 } 1064 1065 pub(super) async fn run_concurrent_trap_on_idle<R>( 1066 self, 1067 fun: impl AsyncFnOnce(&Accessor<T>) -> R, 1068 ) -> Result<R> 1069 where 1070 T: Send + 'static, 1071 { 1072 self.do_run_concurrent(fun, true).await 1073 } 1074 1075 async fn do_run_concurrent<R>( 1076 mut self, 1077 fun: impl AsyncFnOnce(&Accessor<T>) -> R, 1078 trap_on_idle: bool, 1079 ) -> Result<R> 1080 where 1081 T: Send + 'static, 1082 { 1083 check_recursive_run(); 1084 let token = StoreToken::new(self.as_context_mut()); 1085 1086 struct Dropper<'a, T: 'static, V> { 1087 store: StoreContextMut<'a, T>, 1088 value: ManuallyDrop<V>, 1089 } 1090 1091 impl<'a, T, V> Drop for Dropper<'a, T, V> { 1092 fn drop(&mut self) { 1093 tls::set(self.store.0, || { 1094 // SAFETY: Here we drop the value without moving it for the 1095 // first and only time -- per the contract for `Drop::drop`, 1096 // this code won't run again, and the `value` field will no 1097 // longer be accessible. 1098 unsafe { ManuallyDrop::drop(&mut self.value) } 1099 }); 1100 } 1101 } 1102 1103 let accessor = &Accessor::new(token); 1104 let dropper = &mut Dropper { 1105 store: self, 1106 value: ManuallyDrop::new(fun(accessor)), 1107 }; 1108 // SAFETY: We never move `dropper` nor its `value` field. 1109 let future = unsafe { Pin::new_unchecked(dropper.value.deref_mut()) }; 1110 1111 dropper 1112 .store 1113 .as_context_mut() 1114 .poll_until(future, trap_on_idle) 1115 .await 1116 } 1117 1118 /// Run this store's event loop. 1119 /// 1120 /// The returned future will resolve when the specified future completes or, 1121 /// if `trap_on_idle` is true, when the event loop can't make further 1122 /// progress. 1123 async fn poll_until<R>( 1124 mut self, 1125 mut future: Pin<&mut impl Future<Output = R>>, 1126 trap_on_idle: bool, 1127 ) -> Result<R> 1128 where 1129 T: Send + 'static, 1130 { 1131 struct Reset<'a, T: 'static> { 1132 store: StoreContextMut<'a, T>, 1133 futures: Option<FuturesUnordered<HostTaskFuture>>, 1134 } 1135 1136 impl<'a, T> Drop for Reset<'a, T> { 1137 fn drop(&mut self) { 1138 if let Some(futures) = self.futures.take() { 1139 *self.store.0.concurrent_state_mut().futures.get_mut() = Some(futures); 1140 } 1141 } 1142 } 1143 1144 loop { 1145 // Take `ConcurrentState::futures` out of the store so we can poll 1146 // it while also safely giving any of the futures inside access to 1147 // `self`. 1148 let futures = self.0.concurrent_state_mut().futures.get_mut().take(); 1149 let mut reset = Reset { 1150 store: self.as_context_mut(), 1151 futures, 1152 }; 1153 let mut next = pin!(reset.futures.as_mut().unwrap().next()); 1154 1155 enum PollResult<R> { 1156 Complete(R), 1157 ProcessWork(Vec<WorkItem>), 1158 } 1159 let result = future::poll_fn(|cx| { 1160 // First, poll the future we were passed as an argument and 1161 // return immediately if it's ready. 1162 if let Poll::Ready(value) = tls::set(reset.store.0, || future.as_mut().poll(cx)) { 1163 return Poll::Ready(Ok(PollResult::Complete(value))); 1164 } 1165 1166 // Next, poll `ConcurrentState::futures` (which includes any 1167 // pending host tasks and/or background tasks), returning 1168 // immediately if one of them fails. 1169 let next = match tls::set(reset.store.0, || next.as_mut().poll(cx)) { 1170 Poll::Ready(Some(output)) => { 1171 match output { 1172 Err(e) => return Poll::Ready(Err(e)), 1173 Ok(()) => {} 1174 } 1175 Poll::Ready(true) 1176 } 1177 Poll::Ready(None) => Poll::Ready(false), 1178 Poll::Pending => Poll::Pending, 1179 }; 1180 1181 // Next, collect the next batch of work items to process, if any. 1182 // This will be either all of the high-priority work items, or if 1183 // there are none, a single low-priority work item. 1184 let state = reset.store.0.concurrent_state_mut(); 1185 let ready = state.collect_work_items_to_run(); 1186 if !ready.is_empty() { 1187 return Poll::Ready(Ok(PollResult::ProcessWork(ready))); 1188 } 1189 1190 // Finally, if we have nothing else to do right now, determine what to do 1191 // based on whether there are any pending futures in 1192 // `ConcurrentState::futures`. 1193 return match next { 1194 Poll::Ready(true) => { 1195 // In this case, one of the futures in 1196 // `ConcurrentState::futures` completed 1197 // successfully, so we return now and continue 1198 // the outer loop in case there is another one 1199 // ready to complete. 1200 Poll::Ready(Ok(PollResult::ProcessWork(Vec::new()))) 1201 } 1202 Poll::Ready(false) => { 1203 // Poll the future we were passed one last time 1204 // in case one of `ConcurrentState::futures` had 1205 // the side effect of unblocking it. 1206 if let Poll::Ready(value) = 1207 tls::set(reset.store.0, || future.as_mut().poll(cx)) 1208 { 1209 Poll::Ready(Ok(PollResult::Complete(value))) 1210 } else { 1211 // In this case, there are no more pending 1212 // futures in `ConcurrentState::futures`, 1213 // there are no remaining work items, _and_ 1214 // the future we were passed as an argument 1215 // still hasn't completed. 1216 if trap_on_idle { 1217 // `trap_on_idle` is true, so we exit 1218 // immediately. 1219 Poll::Ready(Err(format_err!(crate::Trap::AsyncDeadlock))) 1220 } else { 1221 // `trap_on_idle` is false, so we assume 1222 // that future will wake up and give us 1223 // more work to do when it's ready to. 1224 Poll::Pending 1225 } 1226 } 1227 } 1228 // There is at least one pending future in 1229 // `ConcurrentState::futures` and we have nothing 1230 // else to do but wait for now, so we return 1231 // `Pending`. 1232 Poll::Pending => Poll::Pending, 1233 }; 1234 }) 1235 .await; 1236 1237 // Put the `ConcurrentState::futures` back into the store before we 1238 // return or handle any work items since one or more of those items 1239 // might append more futures. 1240 drop(reset); 1241 1242 match result? { 1243 // The future we were passed as an argument completed, so we 1244 // return the result. 1245 PollResult::Complete(value) => break Ok(value), 1246 // The future we were passed has not yet completed, so handle 1247 // any work items and then loop again. 1248 PollResult::ProcessWork(ready) => { 1249 struct Dispose<'a, T: 'static, I: Iterator<Item = WorkItem>> { 1250 store: StoreContextMut<'a, T>, 1251 ready: I, 1252 } 1253 1254 impl<'a, T, I: Iterator<Item = WorkItem>> Drop for Dispose<'a, T, I> { 1255 fn drop(&mut self) { 1256 while let Some(item) = self.ready.next() { 1257 match item { 1258 WorkItem::ResumeFiber(mut fiber) => fiber.dispose(self.store.0), 1259 WorkItem::PushFuture(future) => { 1260 tls::set(self.store.0, move || drop(future)) 1261 } 1262 _ => {} 1263 } 1264 } 1265 } 1266 } 1267 1268 let mut dispose = Dispose { 1269 store: self.as_context_mut(), 1270 ready: ready.into_iter(), 1271 }; 1272 1273 while let Some(item) = dispose.ready.next() { 1274 dispose 1275 .store 1276 .as_context_mut() 1277 .handle_work_item(item) 1278 .await?; 1279 } 1280 } 1281 } 1282 } 1283 } 1284 1285 /// Handle the specified work item, possibly resuming a fiber if applicable. 1286 async fn handle_work_item(self, item: WorkItem) -> Result<()> 1287 where 1288 T: Send, 1289 { 1290 log::trace!("handle work item {item:?}"); 1291 match item { 1292 WorkItem::PushFuture(future) => { 1293 self.0 1294 .concurrent_state_mut() 1295 .futures 1296 .get_mut() 1297 .as_mut() 1298 .unwrap() 1299 .push(future.into_inner()); 1300 } 1301 WorkItem::ResumeFiber(fiber) => { 1302 self.0.resume_fiber(fiber).await?; 1303 } 1304 WorkItem::GuestCall(call) => { 1305 if call.is_ready(self.0)? { 1306 self.run_on_worker(WorkerItem::GuestCall(call)).await?; 1307 } else { 1308 let state = self.0.concurrent_state_mut(); 1309 let task = state.get_mut(call.thread.task)?; 1310 if !task.starting_sent { 1311 task.starting_sent = true; 1312 if let GuestCallKind::StartImplicit(_) = &call.kind { 1313 Waitable::Guest(call.thread.task).set_event( 1314 state, 1315 Some(Event::Subtask { 1316 status: Status::Starting, 1317 }), 1318 )?; 1319 } 1320 } 1321 1322 let instance = state.get_mut(call.thread.task)?.instance; 1323 self.0 1324 .instance_state(instance) 1325 .pending 1326 .insert(call.thread, call.kind); 1327 } 1328 } 1329 WorkItem::WorkerFunction(fun) => { 1330 self.run_on_worker(WorkerItem::Function(fun)).await?; 1331 } 1332 } 1333 1334 Ok(()) 1335 } 1336 1337 /// Execute the specified guest call on a worker fiber. 1338 async fn run_on_worker(self, item: WorkerItem) -> Result<()> 1339 where 1340 T: Send, 1341 { 1342 let worker = if let Some(fiber) = self.0.concurrent_state_mut().worker.take() { 1343 fiber 1344 } else { 1345 fiber::make_fiber(self.0, move |store| { 1346 loop { 1347 match store.concurrent_state_mut().worker_item.take().unwrap() { 1348 WorkerItem::GuestCall(call) => handle_guest_call(store, call)?, 1349 WorkerItem::Function(fun) => fun.into_inner()(store)?, 1350 } 1351 1352 store.suspend(SuspendReason::NeedWork)?; 1353 } 1354 })? 1355 }; 1356 1357 let worker_item = &mut self.0.concurrent_state_mut().worker_item; 1358 assert!(worker_item.is_none()); 1359 *worker_item = Some(item); 1360 1361 self.0.resume_fiber(worker).await 1362 } 1363 1364 /// Wrap the specified host function in a future which will call it, passing 1365 /// it an `&Accessor<T>`. 1366 /// 1367 /// See the `Accessor` documentation for details. 1368 pub(crate) fn wrap_call<F, R>(self, closure: F) -> impl Future<Output = Result<R>> + 'static 1369 where 1370 T: 'static, 1371 F: FnOnce(&Accessor<T>) -> Pin<Box<dyn Future<Output = Result<R>> + Send + '_>> 1372 + Send 1373 + Sync 1374 + 'static, 1375 R: Send + Sync + 'static, 1376 { 1377 let token = StoreToken::new(self); 1378 async move { 1379 let mut accessor = Accessor::new(token); 1380 closure(&mut accessor).await 1381 } 1382 } 1383 } 1384 1385 impl StoreOpaque { 1386 /// Determine whether the specified instance may be entered from the host. 1387 /// 1388 /// We return `true` here only if all of the following hold: 1389 /// 1390 /// - The top-level instance is not already on the current task's call stack. 1391 /// - The instance is not in need of a post-return function call. 1392 /// - `self` has not been poisoned due to a trap. 1393 pub(crate) fn may_enter(&mut self, instance: RuntimeInstance) -> bool { 1394 let state = self.concurrent_state_mut(); 1395 if let Some(caller) = state.guest_thread { 1396 instance != state.get_mut(caller.task).unwrap().instance 1397 && self.may_enter_from_caller(caller.task, instance) 1398 } else { 1399 self.may_enter_at_all(instance) 1400 } 1401 } 1402 1403 fn may_enter_at_all(&self, instance: RuntimeInstance) -> bool { 1404 if self.trapped() { 1405 return false; 1406 } 1407 1408 let flags = self 1409 .component_instance(instance.instance) 1410 .instance_flags(instance.index); 1411 1412 unsafe { !flags.needs_post_return() } 1413 } 1414 1415 /// Variation of `may_enter` which takes a `TableId<GuestTask>` representing 1416 /// the callee. 1417 fn may_enter_task(&mut self, task: TableId<GuestTask>) -> bool { 1418 let instance = self.concurrent_state_mut().get_mut(task).unwrap().instance; 1419 self.may_enter_from_caller(task, instance) 1420 } 1421 1422 /// Variation of `may_enter` which takes a `TableId<GuestTask>` representing 1423 /// the caller, plus a `RuntimeInstance` representing the callee. 1424 fn may_enter_from_caller( 1425 &mut self, 1426 mut guest_task: TableId<GuestTask>, 1427 instance: RuntimeInstance, 1428 ) -> bool { 1429 self.may_enter_at_all(instance) && { 1430 let state = self.concurrent_state_mut(); 1431 let guest_instance = instance.instance; 1432 loop { 1433 // Note that we only compare top-level instance IDs here. The 1434 // idea is that the host is not allowed to recursively enter a 1435 // top-level instance even if the specific leaf instance is not 1436 // on the stack. This the behavior defined in the spec, and it 1437 // allows us to elide runtime checks in guest-to-guest adapters. 1438 let next_thread = match &state.get_mut(guest_task).unwrap().caller { 1439 Caller::Host { caller: None, .. } => break true, 1440 &Caller::Host { 1441 caller: Some(caller), 1442 .. 1443 } => { 1444 let instance = state.get_mut(caller.task).unwrap().instance; 1445 if instance.instance == guest_instance { 1446 break false; 1447 } else { 1448 caller 1449 } 1450 } 1451 &Caller::Guest { thread, instance } => { 1452 if instance.instance == guest_instance { 1453 break false; 1454 } else { 1455 thread 1456 } 1457 } 1458 }; 1459 guest_task = next_thread.task; 1460 } 1461 } 1462 } 1463 1464 /// Helper function to retrieve the `ConcurrentInstanceState` for the 1465 /// specified instance. 1466 fn instance_state(&mut self, instance: RuntimeInstance) -> &mut ConcurrentInstanceState { 1467 self.component_instance_mut(instance.instance) 1468 .instance_state(instance.index) 1469 .concurrent_state() 1470 } 1471 1472 /// Helper function to retrieve the `HandleTable` for the specified 1473 /// instance. 1474 fn handle_table(&mut self, instance: RuntimeInstance) -> &mut HandleTable { 1475 self.component_instance_mut(instance.instance) 1476 .instance_state(instance.index) 1477 .handle_table() 1478 } 1479 1480 fn set_thread(&mut self, thread: Option<QualifiedThreadId>) -> Option<QualifiedThreadId> { 1481 // Each time we switch threads, we conservatively set `task_may_block` 1482 // to `false` for the component instance we're switching away from (if 1483 // any), meaning it will be `false` for any new thread created for that 1484 // instance unless explicitly set otherwise. 1485 let state = self.concurrent_state_mut(); 1486 let old_thread = state.guest_thread.take(); 1487 if let Some(old_thread) = old_thread { 1488 let instance = state.get_mut(old_thread.task).unwrap().instance.instance; 1489 self.component_instance_mut(instance) 1490 .set_task_may_block(false) 1491 } 1492 1493 self.concurrent_state_mut().guest_thread = thread; 1494 1495 // If we're switching to a new thread, set its component instance's 1496 // `task_may_block` according to where it left off. 1497 if thread.is_some() { 1498 self.set_task_may_block(); 1499 } 1500 1501 old_thread 1502 } 1503 1504 /// Set the global variable representing whether the current task may block 1505 /// prior to entering Wasm code. 1506 fn set_task_may_block(&mut self) { 1507 let state = self.concurrent_state_mut(); 1508 let guest_thread = state.guest_thread.unwrap(); 1509 let instance = state.get_mut(guest_thread.task).unwrap().instance.instance; 1510 let may_block = self.concurrent_state_mut().may_block(guest_thread.task); 1511 self.component_instance_mut(instance) 1512 .set_task_may_block(may_block) 1513 } 1514 1515 pub(crate) fn check_blocking(&mut self) -> Result<()> { 1516 let state = self.concurrent_state_mut(); 1517 let task = state.guest_thread.unwrap().task; 1518 let instance = state.get_mut(task).unwrap().instance.instance; 1519 let task_may_block = self.component_instance(instance).get_task_may_block(); 1520 1521 if task_may_block { 1522 Ok(()) 1523 } else { 1524 Err(Trap::CannotBlockSyncTask.into()) 1525 } 1526 } 1527 1528 /// Record that we're about to enter a (sub-)component instance which does 1529 /// not support more than one concurrent, stackful activation, meaning it 1530 /// cannot be entered again until the next call returns. 1531 fn enter_instance(&mut self, instance: RuntimeInstance) { 1532 log::trace!("enter {instance:?}"); 1533 self.instance_state(instance).do_not_enter = true; 1534 } 1535 1536 /// Record that we've exited a (sub-)component instance previously entered 1537 /// with `Self::enter_instance` and then calls `Self::partition_pending`. 1538 /// See the documentation for the latter for details. 1539 fn exit_instance(&mut self, instance: RuntimeInstance) -> Result<()> { 1540 log::trace!("exit {instance:?}"); 1541 self.instance_state(instance).do_not_enter = false; 1542 self.partition_pending(instance) 1543 } 1544 1545 /// Iterate over `InstanceState::pending`, moving any ready items into the 1546 /// "high priority" work item queue. 1547 /// 1548 /// See `GuestCall::is_ready` for details. 1549 fn partition_pending(&mut self, instance: RuntimeInstance) -> Result<()> { 1550 for (thread, kind) in mem::take(&mut self.instance_state(instance).pending).into_iter() { 1551 let call = GuestCall { thread, kind }; 1552 if call.is_ready(self)? { 1553 self.concurrent_state_mut() 1554 .push_high_priority(WorkItem::GuestCall(call)); 1555 } else { 1556 self.instance_state(instance) 1557 .pending 1558 .insert(call.thread, call.kind); 1559 } 1560 } 1561 1562 Ok(()) 1563 } 1564 1565 /// Implements the `backpressure.{inc,dec}` intrinsics. 1566 pub(crate) fn backpressure_modify( 1567 &mut self, 1568 caller_instance: RuntimeInstance, 1569 modify: impl FnOnce(u16) -> Option<u16>, 1570 ) -> Result<()> { 1571 let state = self.instance_state(caller_instance); 1572 let old = state.backpressure; 1573 let new = modify(old).ok_or_else(|| format_err!("backpressure counter overflow"))?; 1574 state.backpressure = new; 1575 1576 if old > 0 && new == 0 { 1577 // Backpressure was previously enabled and is now disabled; move any 1578 // newly-eligible guest calls to the "high priority" queue. 1579 self.partition_pending(caller_instance)?; 1580 } 1581 1582 Ok(()) 1583 } 1584 1585 /// Resume the specified fiber, giving it exclusive access to the specified 1586 /// store. 1587 async fn resume_fiber(&mut self, fiber: StoreFiber<'static>) -> Result<()> { 1588 let old_thread = self.concurrent_state_mut().guest_thread; 1589 log::trace!("resume_fiber: save current thread {old_thread:?}"); 1590 1591 let fiber = fiber::resolve_or_release(self, fiber).await?; 1592 1593 self.set_thread(old_thread); 1594 1595 let state = self.concurrent_state_mut(); 1596 1597 if let Some(ref ot) = old_thread { 1598 state.get_mut(ot.thread)?.state = GuestThreadState::Running; 1599 } 1600 log::trace!("resume_fiber: restore current thread {old_thread:?}"); 1601 1602 if let Some(mut fiber) = fiber { 1603 log::trace!("resume_fiber: suspend reason {:?}", &state.suspend_reason); 1604 // See the `SuspendReason` documentation for what each case means. 1605 match state.suspend_reason.take().unwrap() { 1606 SuspendReason::NeedWork => { 1607 if state.worker.is_none() { 1608 state.worker = Some(fiber); 1609 } else { 1610 fiber.dispose(self); 1611 } 1612 } 1613 SuspendReason::Yielding { thread, .. } => { 1614 state.get_mut(thread.thread)?.state = GuestThreadState::Pending; 1615 state.push_low_priority(WorkItem::ResumeFiber(fiber)); 1616 } 1617 SuspendReason::ExplicitlySuspending { thread, .. } => { 1618 state.get_mut(thread.thread)?.state = GuestThreadState::Suspended(fiber); 1619 } 1620 SuspendReason::Waiting { set, thread, .. } => { 1621 let old = state 1622 .get_mut(set)? 1623 .waiting 1624 .insert(thread, WaitMode::Fiber(fiber)); 1625 assert!(old.is_none()); 1626 } 1627 }; 1628 } else { 1629 log::trace!("resume_fiber: fiber has exited"); 1630 } 1631 1632 Ok(()) 1633 } 1634 1635 /// Suspend the current fiber, storing the reason in 1636 /// `ConcurrentState::suspend_reason` to indicate the conditions under which 1637 /// it should be resumed. 1638 /// 1639 /// See the `SuspendReason` documentation for details. 1640 fn suspend(&mut self, reason: SuspendReason) -> Result<()> { 1641 log::trace!("suspend fiber: {reason:?}"); 1642 1643 // If we're yielding or waiting on behalf of a guest thread, we'll need to 1644 // pop the call context which manages resource borrows before suspending 1645 // and then push it again once we've resumed. 1646 let task = match &reason { 1647 SuspendReason::Yielding { thread, .. } 1648 | SuspendReason::Waiting { thread, .. } 1649 | SuspendReason::ExplicitlySuspending { thread, .. } => Some(thread.task), 1650 SuspendReason::NeedWork => None, 1651 }; 1652 1653 let old_guest_thread = if let Some(task) = task { 1654 self.maybe_pop_call_context(task)?; 1655 self.concurrent_state_mut().guest_thread 1656 } else { 1657 None 1658 }; 1659 1660 // We should not have reached here unless either there's no current 1661 // task, or the current task is permitted to block. In addition, we 1662 // special-case `thread.switch-to` and waiting for a subtask to go from 1663 // `starting` to `started`, both of which we consider non-blocking 1664 // operations despite requiring a suspend. 1665 assert!( 1666 matches!( 1667 reason, 1668 SuspendReason::ExplicitlySuspending { 1669 skip_may_block_check: true, 1670 .. 1671 } | SuspendReason::Waiting { 1672 skip_may_block_check: true, 1673 .. 1674 } | SuspendReason::Yielding { 1675 skip_may_block_check: true, 1676 .. 1677 } 1678 ) || old_guest_thread 1679 .map(|thread| self.concurrent_state_mut().may_block(thread.task)) 1680 .unwrap_or(true) 1681 ); 1682 1683 let suspend_reason = &mut self.concurrent_state_mut().suspend_reason; 1684 assert!(suspend_reason.is_none()); 1685 *suspend_reason = Some(reason); 1686 1687 self.with_blocking(|_, cx| cx.suspend(StoreFiberYield::ReleaseStore))?; 1688 1689 if let Some(task) = task { 1690 self.set_thread(old_guest_thread); 1691 self.maybe_push_call_context(task)?; 1692 } 1693 1694 Ok(()) 1695 } 1696 1697 /// Push the call context for managing resource borrows for the specified 1698 /// guest task if it has not yet either returned a result or cancelled 1699 /// itself. 1700 fn maybe_push_call_context(&mut self, guest_task: TableId<GuestTask>) -> Result<()> { 1701 let task = self.concurrent_state_mut().get_mut(guest_task)?; 1702 1703 if !task.returned_or_cancelled() { 1704 log::trace!("push call context for {guest_task:?}"); 1705 let call_context = task.call_context.take().unwrap(); 1706 self.component_resource_state().0.push(call_context); 1707 } 1708 Ok(()) 1709 } 1710 1711 /// Pop the call context for managing resource borrows for the specified 1712 /// guest task if it has not yet either returned a result or cancelled 1713 /// itself. 1714 fn maybe_pop_call_context(&mut self, guest_task: TableId<GuestTask>) -> Result<()> { 1715 if !self 1716 .concurrent_state_mut() 1717 .get_mut(guest_task)? 1718 .returned_or_cancelled() 1719 { 1720 log::trace!("pop call context for {guest_task:?}"); 1721 let call_context = Some(self.component_resource_state().0.pop().unwrap()); 1722 self.concurrent_state_mut() 1723 .get_mut(guest_task)? 1724 .call_context = call_context; 1725 } 1726 Ok(()) 1727 } 1728 1729 fn wait_for_event(&mut self, waitable: Waitable) -> Result<()> { 1730 let state = self.concurrent_state_mut(); 1731 let caller = state.guest_thread.unwrap(); 1732 let old_set = waitable.common(state)?.set; 1733 let set = state.get_mut(caller.task)?.sync_call_set; 1734 waitable.join(state, Some(set))?; 1735 self.suspend(SuspendReason::Waiting { 1736 set, 1737 thread: caller, 1738 skip_may_block_check: false, 1739 })?; 1740 let state = self.concurrent_state_mut(); 1741 waitable.join(state, old_set) 1742 } 1743 } 1744 1745 impl Instance { 1746 /// Get the next pending event for the specified task and (optional) 1747 /// waitable set, along with the waitable handle if applicable. 1748 fn get_event( 1749 self, 1750 store: &mut StoreOpaque, 1751 guest_task: TableId<GuestTask>, 1752 set: Option<TableId<WaitableSet>>, 1753 cancellable: bool, 1754 ) -> Result<Option<(Event, Option<(Waitable, u32)>)>> { 1755 let state = store.concurrent_state_mut(); 1756 1757 if let Some(event) = state.get_mut(guest_task)?.event.take() { 1758 log::trace!("deliver event {event:?} to {guest_task:?}"); 1759 1760 if cancellable || !matches!(event, Event::Cancelled) { 1761 return Ok(Some((event, None))); 1762 } else { 1763 state.get_mut(guest_task)?.event = Some(event); 1764 } 1765 } 1766 1767 Ok( 1768 if let Some((set, waitable)) = set 1769 .and_then(|set| { 1770 state 1771 .get_mut(set) 1772 .map(|v| v.ready.pop_first().map(|v| (set, v))) 1773 .transpose() 1774 }) 1775 .transpose()? 1776 { 1777 let common = waitable.common(state)?; 1778 let handle = common.handle.unwrap(); 1779 let event = common.event.take().unwrap(); 1780 1781 log::trace!( 1782 "deliver event {event:?} to {guest_task:?} for {waitable:?} (handle {handle}); set {set:?}" 1783 ); 1784 1785 waitable.on_delivery(store, self, event); 1786 1787 Some((event, Some((waitable, handle)))) 1788 } else { 1789 None 1790 }, 1791 ) 1792 } 1793 1794 /// Handle the `CallbackCode` returned from an async-lifted export or its 1795 /// callback. 1796 /// 1797 /// If this returns `Ok(Some(call))`, then `call` should be run immediately 1798 /// using `handle_guest_call`. 1799 fn handle_callback_code( 1800 self, 1801 store: &mut StoreOpaque, 1802 guest_thread: QualifiedThreadId, 1803 runtime_instance: RuntimeComponentInstanceIndex, 1804 code: u32, 1805 ) -> Result<Option<GuestCall>> { 1806 let (code, set) = unpack_callback_code(code); 1807 1808 log::trace!("received callback code from {guest_thread:?}: {code} (set: {set})"); 1809 1810 let state = store.concurrent_state_mut(); 1811 1812 let get_set = |store: &mut StoreOpaque, handle| { 1813 if handle == 0 { 1814 bail!("invalid waitable-set handle"); 1815 } 1816 1817 let set = store 1818 .handle_table(RuntimeInstance { 1819 instance: self.id().instance(), 1820 index: runtime_instance, 1821 }) 1822 .waitable_set_rep(handle)?; 1823 1824 Ok(TableId::<WaitableSet>::new(set)) 1825 }; 1826 1827 Ok(match code { 1828 callback_code::EXIT => { 1829 log::trace!("implicit thread {guest_thread:?} completed"); 1830 self.cleanup_thread(store, guest_thread, runtime_instance)?; 1831 let task = store.concurrent_state_mut().get_mut(guest_thread.task)?; 1832 if task.threads.is_empty() && !task.returned_or_cancelled() { 1833 bail!(Trap::NoAsyncResult); 1834 } 1835 match &task.caller { 1836 Caller::Host { .. } => { 1837 if task.ready_to_delete() { 1838 Waitable::Guest(guest_thread.task) 1839 .delete_from(store.concurrent_state_mut())?; 1840 } 1841 } 1842 Caller::Guest { .. } => { 1843 task.exited = true; 1844 task.callback = None; 1845 } 1846 } 1847 None 1848 } 1849 callback_code::YIELD => { 1850 let task = state.get_mut(guest_thread.task)?; 1851 // If an `Event::Cancelled` is pending, we'll deliver that; 1852 // otherwise, we'll deliver `Event::None`. Note that 1853 // `GuestTask::event` is only ever set to one of those two 1854 // `Event` variants. 1855 if let Some(event) = task.event { 1856 assert!(matches!(event, Event::None | Event::Cancelled)); 1857 } else { 1858 task.event = Some(Event::None); 1859 } 1860 let call = GuestCall { 1861 thread: guest_thread, 1862 kind: GuestCallKind::DeliverEvent { 1863 instance: self, 1864 set: None, 1865 }, 1866 }; 1867 if state.may_block(guest_thread.task) { 1868 // Push this thread onto the "low priority" queue so it runs 1869 // after any other threads have had a chance to run. 1870 state.push_low_priority(WorkItem::GuestCall(call)); 1871 None 1872 } else { 1873 // Yielding in a non-blocking context is defined as a no-op 1874 // according to the spec, so we must run this thread 1875 // immediately without allowing any others to run. 1876 Some(call) 1877 } 1878 } 1879 callback_code::WAIT => { 1880 // The task may only return `WAIT` if it was created for a call 1881 // to an async export). Otherwise, we'll trap. 1882 state.check_blocking_for(guest_thread.task)?; 1883 1884 let set = get_set(store, set)?; 1885 let state = store.concurrent_state_mut(); 1886 1887 if state.get_mut(guest_thread.task)?.event.is_some() 1888 || !state.get_mut(set)?.ready.is_empty() 1889 { 1890 // An event is immediately available; deliver it ASAP. 1891 state.push_high_priority(WorkItem::GuestCall(GuestCall { 1892 thread: guest_thread, 1893 kind: GuestCallKind::DeliverEvent { 1894 instance: self, 1895 set: Some(set), 1896 }, 1897 })); 1898 } else { 1899 // No event is immediately available. 1900 // 1901 // We're waiting, so register to be woken up when an event 1902 // is published for this waitable set. 1903 // 1904 // Here we also set `GuestTask::wake_on_cancel` which allows 1905 // `subtask.cancel` to interrupt the wait. 1906 let old = state 1907 .get_mut(guest_thread.thread)? 1908 .wake_on_cancel 1909 .replace(set); 1910 assert!(old.is_none()); 1911 let old = state 1912 .get_mut(set)? 1913 .waiting 1914 .insert(guest_thread, WaitMode::Callback(self)); 1915 assert!(old.is_none()); 1916 } 1917 None 1918 } 1919 _ => bail!("unsupported callback code: {code}"), 1920 }) 1921 } 1922 1923 fn cleanup_thread( 1924 self, 1925 store: &mut StoreOpaque, 1926 guest_thread: QualifiedThreadId, 1927 runtime_instance: RuntimeComponentInstanceIndex, 1928 ) -> Result<()> { 1929 let guest_id = store 1930 .concurrent_state_mut() 1931 .get_mut(guest_thread.thread)? 1932 .instance_rep; 1933 store 1934 .handle_table(RuntimeInstance { 1935 instance: self.id().instance(), 1936 index: runtime_instance, 1937 }) 1938 .guest_thread_remove(guest_id.unwrap())?; 1939 1940 store.concurrent_state_mut().delete(guest_thread.thread)?; 1941 let task = store.concurrent_state_mut().get_mut(guest_thread.task)?; 1942 task.threads.remove(&guest_thread.thread); 1943 Ok(()) 1944 } 1945 1946 /// Add the specified guest call to the "high priority" work item queue, to 1947 /// be started as soon as backpressure and/or reentrance rules allow. 1948 /// 1949 /// SAFETY: The raw pointer arguments must be valid references to guest 1950 /// functions (with the appropriate signatures) when the closures queued by 1951 /// this function are called. 1952 unsafe fn queue_call<T: 'static>( 1953 self, 1954 mut store: StoreContextMut<T>, 1955 guest_thread: QualifiedThreadId, 1956 callee: SendSyncPtr<VMFuncRef>, 1957 param_count: usize, 1958 result_count: usize, 1959 async_: bool, 1960 callback: Option<SendSyncPtr<VMFuncRef>>, 1961 post_return: Option<SendSyncPtr<VMFuncRef>>, 1962 ) -> Result<()> { 1963 /// Return a closure which will call the specified function in the scope 1964 /// of the specified task. 1965 /// 1966 /// This will use `GuestTask::lower_params` to lower the parameters, but 1967 /// will not lift the result; instead, it returns a 1968 /// `[MaybeUninit<ValRaw>; MAX_FLAT_PARAMS]` from which the result, if 1969 /// any, may be lifted. Note that an async-lifted export will have 1970 /// returned its result using the `task.return` intrinsic (or not 1971 /// returned a result at all, in the case of `task.cancel`), in which 1972 /// case the "result" of this call will either be a callback code or 1973 /// nothing. 1974 /// 1975 /// SAFETY: `callee` must be a valid `*mut VMFuncRef` at the time when 1976 /// the returned closure is called. 1977 unsafe fn make_call<T: 'static>( 1978 store: StoreContextMut<T>, 1979 guest_thread: QualifiedThreadId, 1980 callee: SendSyncPtr<VMFuncRef>, 1981 param_count: usize, 1982 result_count: usize, 1983 ) -> impl FnOnce(&mut dyn VMStore) -> Result<[MaybeUninit<ValRaw>; MAX_FLAT_PARAMS]> 1984 + Send 1985 + Sync 1986 + 'static 1987 + use<T> { 1988 let token = StoreToken::new(store); 1989 move |store: &mut dyn VMStore| { 1990 let mut storage = [MaybeUninit::uninit(); MAX_FLAT_PARAMS]; 1991 1992 store 1993 .concurrent_state_mut() 1994 .get_mut(guest_thread.thread)? 1995 .state = GuestThreadState::Running; 1996 let task = store.concurrent_state_mut().get_mut(guest_thread.task)?; 1997 let lower = task.lower_params.take().unwrap(); 1998 1999 lower(store, &mut storage[..param_count])?; 2000 2001 let mut store = token.as_context_mut(store); 2002 2003 // SAFETY: Per the contract documented in `make_call's` 2004 // documentation, `callee` must be a valid pointer. 2005 unsafe { 2006 crate::Func::call_unchecked_raw( 2007 &mut store, 2008 callee.as_non_null(), 2009 NonNull::new( 2010 &mut storage[..param_count.max(result_count)] 2011 as *mut [MaybeUninit<ValRaw>] as _, 2012 ) 2013 .unwrap(), 2014 )?; 2015 } 2016 2017 Ok(storage) 2018 } 2019 } 2020 2021 // SAFETY: Per the contract described in this function documentation, 2022 // the `callee` pointer which `call` closes over must be valid when 2023 // called by the closure we queue below. 2024 let call = unsafe { 2025 make_call( 2026 store.as_context_mut(), 2027 guest_thread, 2028 callee, 2029 param_count, 2030 result_count, 2031 ) 2032 }; 2033 2034 let callee_instance = store 2035 .0 2036 .concurrent_state_mut() 2037 .get_mut(guest_thread.task)? 2038 .instance; 2039 2040 let fun = if callback.is_some() { 2041 assert!(async_); 2042 2043 Box::new(move |store: &mut dyn VMStore| { 2044 self.add_guest_thread_to_instance_table( 2045 guest_thread.thread, 2046 store, 2047 callee_instance.index, 2048 )?; 2049 let old_thread = store.set_thread(Some(guest_thread)); 2050 log::trace!( 2051 "stackless call: replaced {old_thread:?} with {guest_thread:?} as current thread" 2052 ); 2053 2054 store.maybe_push_call_context(guest_thread.task)?; 2055 2056 store.enter_instance(callee_instance); 2057 2058 // SAFETY: See the documentation for `make_call` to review the 2059 // contract we must uphold for `call` here. 2060 // 2061 // Per the contract described in the `queue_call` 2062 // documentation, the `callee` pointer which `call` closes 2063 // over must be valid. 2064 let storage = call(store)?; 2065 2066 store.exit_instance(callee_instance)?; 2067 2068 store.maybe_pop_call_context(guest_thread.task)?; 2069 2070 store.set_thread(old_thread); 2071 let state = store.concurrent_state_mut(); 2072 old_thread 2073 .map(|t| state.get_mut(t.thread).unwrap().state = GuestThreadState::Running); 2074 log::trace!("stackless call: restored {old_thread:?} as current thread"); 2075 2076 // SAFETY: `wasmparser` will have validated that the callback 2077 // function returns a `i32` result. 2078 let code = unsafe { storage[0].assume_init() }.get_i32() as u32; 2079 2080 self.handle_callback_code(store, guest_thread, callee_instance.index, code) 2081 }) 2082 as Box<dyn FnOnce(&mut dyn VMStore) -> Result<Option<GuestCall>> + Send + Sync> 2083 } else { 2084 let token = StoreToken::new(store.as_context_mut()); 2085 Box::new(move |store: &mut dyn VMStore| { 2086 self.add_guest_thread_to_instance_table( 2087 guest_thread.thread, 2088 store, 2089 callee_instance.index, 2090 )?; 2091 let old_thread = store.set_thread(Some(guest_thread)); 2092 log::trace!( 2093 "sync/async-stackful call: replaced {old_thread:?} with {guest_thread:?} as current thread", 2094 ); 2095 let mut flags = self.id().get(store).instance_flags(callee_instance.index); 2096 2097 store.maybe_push_call_context(guest_thread.task)?; 2098 2099 // Unless this is a callback-less (i.e. stackful) 2100 // async-lifted export, we need to record that the instance 2101 // cannot be entered until the call returns. 2102 if !async_ { 2103 store.enter_instance(callee_instance); 2104 } 2105 2106 // SAFETY: See the documentation for `make_call` to review the 2107 // contract we must uphold for `call` here. 2108 // 2109 // Per the contract described in the `queue_call` 2110 // documentation, the `callee` pointer which `call` closes 2111 // over must be valid. 2112 let storage = call(store)?; 2113 2114 // This is a callback-less call, so the implicit thread has now completed 2115 self.cleanup_thread(store, guest_thread, callee_instance.index)?; 2116 2117 if async_ { 2118 let task = store.concurrent_state_mut().get_mut(guest_thread.task)?; 2119 if task.threads.is_empty() && !task.returned_or_cancelled() { 2120 bail!(Trap::NoAsyncResult); 2121 } 2122 } else { 2123 // This is a sync-lifted export, so now is when we lift the 2124 // result, optionally call the post-return function, if any, 2125 // and finally notify any current or future waiters that the 2126 // subtask has returned. 2127 2128 let lift = { 2129 store.exit_instance(callee_instance)?; 2130 2131 let state = store.concurrent_state_mut(); 2132 assert!(state.get_mut(guest_thread.task)?.result.is_none()); 2133 2134 state 2135 .get_mut(guest_thread.task)? 2136 .lift_result 2137 .take() 2138 .unwrap() 2139 }; 2140 2141 // SAFETY: `result_count` represents the number of core Wasm 2142 // results returned, per `wasmparser`. 2143 let result = (lift.lift)(store, unsafe { 2144 mem::transmute::<&[MaybeUninit<ValRaw>], &[ValRaw]>( 2145 &storage[..result_count], 2146 ) 2147 })?; 2148 2149 let post_return_arg = match result_count { 2150 0 => ValRaw::i32(0), 2151 // SAFETY: `result_count` represents the number of 2152 // core Wasm results returned, per `wasmparser`. 2153 1 => unsafe { storage[0].assume_init() }, 2154 _ => unreachable!(), 2155 }; 2156 2157 if store 2158 .concurrent_state_mut() 2159 .get_mut(guest_thread.task)? 2160 .call_post_return_automatically() 2161 { 2162 unsafe { 2163 flags.set_may_leave(false); 2164 flags.set_needs_post_return(false); 2165 } 2166 2167 if let Some(func) = post_return { 2168 let mut store = token.as_context_mut(store); 2169 2170 // SAFETY: `func` is a valid `*mut VMFuncRef` from 2171 // either `wasmtime-cranelift`-generated fused adapter 2172 // code or `component::Options`. Per `wasmparser` 2173 // post-return signature validation, we know it takes a 2174 // single parameter. 2175 unsafe { 2176 crate::Func::call_unchecked_raw( 2177 &mut store, 2178 func.as_non_null(), 2179 slice::from_ref(&post_return_arg).into(), 2180 )?; 2181 } 2182 } 2183 2184 unsafe { 2185 flags.set_may_leave(true); 2186 } 2187 } 2188 2189 self.task_complete( 2190 store, 2191 guest_thread.task, 2192 result, 2193 Status::Returned, 2194 post_return_arg, 2195 )?; 2196 } 2197 2198 store.set_thread(old_thread); 2199 2200 store.maybe_pop_call_context(guest_thread.task)?; 2201 2202 let state = store.concurrent_state_mut(); 2203 let task = state.get_mut(guest_thread.task)?; 2204 2205 match &task.caller { 2206 Caller::Host { .. } => { 2207 if task.ready_to_delete() { 2208 Waitable::Guest(guest_thread.task).delete_from(state)?; 2209 } 2210 } 2211 Caller::Guest { .. } => { 2212 task.exited = true; 2213 } 2214 } 2215 2216 Ok(None) 2217 }) 2218 }; 2219 2220 store 2221 .0 2222 .concurrent_state_mut() 2223 .push_high_priority(WorkItem::GuestCall(GuestCall { 2224 thread: guest_thread, 2225 kind: GuestCallKind::StartImplicit(fun), 2226 })); 2227 2228 Ok(()) 2229 } 2230 2231 /// Prepare (but do not start) a guest->guest call. 2232 /// 2233 /// This is called from fused adapter code generated in 2234 /// `wasmtime_environ::fact::trampoline::Compiler`. `start` and `return_` 2235 /// are synthesized Wasm functions which move the parameters from the caller 2236 /// to the callee and the result from the callee to the caller, 2237 /// respectively. The adapter will call `Self::start_call` immediately 2238 /// after calling this function. 2239 /// 2240 /// SAFETY: All the pointer arguments must be valid pointers to guest 2241 /// entities (and with the expected signatures for the function references 2242 /// -- see `wasmtime_environ::fact::trampoline::Compiler` for details). 2243 unsafe fn prepare_call<T: 'static>( 2244 self, 2245 mut store: StoreContextMut<T>, 2246 start: *mut VMFuncRef, 2247 return_: *mut VMFuncRef, 2248 caller_instance: RuntimeComponentInstanceIndex, 2249 callee_instance: RuntimeComponentInstanceIndex, 2250 task_return_type: TypeTupleIndex, 2251 callee_async: bool, 2252 memory: *mut VMMemoryDefinition, 2253 string_encoding: u8, 2254 caller_info: CallerInfo, 2255 ) -> Result<()> { 2256 self.id().get(store.0).check_may_leave(caller_instance)?; 2257 2258 if let (CallerInfo::Sync { .. }, true) = (&caller_info, callee_async) { 2259 // A task may only call an async-typed function via a sync lower if 2260 // it was created by a call to an async export. Otherwise, we'll 2261 // trap. 2262 store.0.check_blocking()?; 2263 } 2264 2265 enum ResultInfo { 2266 Heap { results: u32 }, 2267 Stack { result_count: u32 }, 2268 } 2269 2270 let result_info = match &caller_info { 2271 CallerInfo::Async { 2272 has_result: true, 2273 params, 2274 } => ResultInfo::Heap { 2275 results: params.last().unwrap().get_u32(), 2276 }, 2277 CallerInfo::Async { 2278 has_result: false, .. 2279 } => ResultInfo::Stack { result_count: 0 }, 2280 CallerInfo::Sync { 2281 result_count, 2282 params, 2283 } if *result_count > u32::try_from(MAX_FLAT_RESULTS).unwrap() => ResultInfo::Heap { 2284 results: params.last().unwrap().get_u32(), 2285 }, 2286 CallerInfo::Sync { result_count, .. } => ResultInfo::Stack { 2287 result_count: *result_count, 2288 }, 2289 }; 2290 2291 let sync_caller = matches!(caller_info, CallerInfo::Sync { .. }); 2292 2293 // Create a new guest task for the call, closing over the `start` and 2294 // `return_` functions to lift the parameters and lower the result, 2295 // respectively. 2296 let start = SendSyncPtr::new(NonNull::new(start).unwrap()); 2297 let return_ = SendSyncPtr::new(NonNull::new(return_).unwrap()); 2298 let token = StoreToken::new(store.as_context_mut()); 2299 let state = store.0.concurrent_state_mut(); 2300 let old_thread = state.guest_thread.take(); 2301 let new_task = GuestTask::new( 2302 state, 2303 Box::new(move |store, dst| { 2304 let mut store = token.as_context_mut(store); 2305 assert!(dst.len() <= MAX_FLAT_PARAMS); 2306 // The `+ 1` here accounts for the return pointer, if any: 2307 let mut src = [MaybeUninit::uninit(); MAX_FLAT_PARAMS + 1]; 2308 let count = match caller_info { 2309 // Async callers, if they have a result, use the last 2310 // parameter as a return pointer so chop that off if 2311 // relevant here. 2312 CallerInfo::Async { params, has_result } => { 2313 let params = ¶ms[..params.len() - usize::from(has_result)]; 2314 for (param, src) in params.iter().zip(&mut src) { 2315 src.write(*param); 2316 } 2317 params.len() 2318 } 2319 2320 // Sync callers forward everything directly. 2321 CallerInfo::Sync { params, .. } => { 2322 for (param, src) in params.iter().zip(&mut src) { 2323 src.write(*param); 2324 } 2325 params.len() 2326 } 2327 }; 2328 // SAFETY: `start` is a valid `*mut VMFuncRef` from 2329 // `wasmtime-cranelift`-generated fused adapter code. Based on 2330 // how it was constructed (see 2331 // `wasmtime_environ::fact::trampoline::Compiler::compile_async_start_adapter` 2332 // for details) we know it takes count parameters and returns 2333 // `dst.len()` results. 2334 unsafe { 2335 crate::Func::call_unchecked_raw( 2336 &mut store, 2337 start.as_non_null(), 2338 NonNull::new( 2339 &mut src[..count.max(dst.len())] as *mut [MaybeUninit<ValRaw>] as _, 2340 ) 2341 .unwrap(), 2342 )?; 2343 } 2344 dst.copy_from_slice(&src[..dst.len()]); 2345 let state = store.0.concurrent_state_mut(); 2346 Waitable::Guest(state.guest_thread.unwrap().task).set_event( 2347 state, 2348 Some(Event::Subtask { 2349 status: Status::Started, 2350 }), 2351 )?; 2352 Ok(()) 2353 }), 2354 LiftResult { 2355 lift: Box::new(move |store, src| { 2356 // SAFETY: See comment in closure passed as `lower_params` 2357 // parameter above. 2358 let mut store = token.as_context_mut(store); 2359 let mut my_src = src.to_owned(); // TODO: use stack to avoid allocation? 2360 if let ResultInfo::Heap { results } = &result_info { 2361 my_src.push(ValRaw::u32(*results)); 2362 } 2363 // SAFETY: `return_` is a valid `*mut VMFuncRef` from 2364 // `wasmtime-cranelift`-generated fused adapter code. Based 2365 // on how it was constructed (see 2366 // `wasmtime_environ::fact::trampoline::Compiler::compile_async_return_adapter` 2367 // for details) we know it takes `src.len()` parameters and 2368 // returns up to 1 result. 2369 unsafe { 2370 crate::Func::call_unchecked_raw( 2371 &mut store, 2372 return_.as_non_null(), 2373 my_src.as_mut_slice().into(), 2374 )?; 2375 } 2376 let state = store.0.concurrent_state_mut(); 2377 let thread = state.guest_thread.unwrap(); 2378 if sync_caller { 2379 state.get_mut(thread.task)?.sync_result = SyncResult::Produced( 2380 if let ResultInfo::Stack { result_count } = &result_info { 2381 match result_count { 2382 0 => None, 2383 1 => Some(my_src[0]), 2384 _ => unreachable!(), 2385 } 2386 } else { 2387 None 2388 }, 2389 ); 2390 } 2391 Ok(Box::new(DummyResult) as Box<dyn Any + Send + Sync>) 2392 }), 2393 ty: task_return_type, 2394 memory: NonNull::new(memory).map(SendSyncPtr::new), 2395 string_encoding: StringEncoding::from_u8(string_encoding).unwrap(), 2396 }, 2397 Caller::Guest { 2398 thread: old_thread.unwrap(), 2399 instance: RuntimeInstance { 2400 instance: self.id().instance(), 2401 index: caller_instance, 2402 }, 2403 }, 2404 None, 2405 RuntimeInstance { 2406 instance: self.id().instance(), 2407 index: callee_instance, 2408 }, 2409 callee_async, 2410 )?; 2411 2412 let guest_task = state.push(new_task)?; 2413 let new_thread = GuestThread::new_implicit(guest_task); 2414 let guest_thread = state.push(new_thread)?; 2415 state.get_mut(guest_task)?.threads.insert(guest_thread); 2416 2417 let state = store.0.concurrent_state_mut(); 2418 if let Some(old_thread) = old_thread { 2419 state.get_mut(old_thread.task)?.subtasks.insert(guest_task); 2420 }; 2421 2422 // Make the new thread the current one so that `Self::start_call` knows 2423 // which one to start. 2424 store.0.set_thread(Some(QualifiedThreadId { 2425 task: guest_task, 2426 thread: guest_thread, 2427 })); 2428 log::trace!( 2429 "pushed {guest_task:?}:{guest_thread:?} as current thread; old thread was {old_thread:?}" 2430 ); 2431 2432 Ok(()) 2433 } 2434 2435 /// Call the specified callback function for an async-lifted export. 2436 /// 2437 /// SAFETY: `function` must be a valid reference to a guest function of the 2438 /// correct signature for a callback. 2439 unsafe fn call_callback<T>( 2440 self, 2441 mut store: StoreContextMut<T>, 2442 function: SendSyncPtr<VMFuncRef>, 2443 event: Event, 2444 handle: u32, 2445 ) -> Result<u32> { 2446 let (ordinal, result) = event.parts(); 2447 let params = &mut [ 2448 ValRaw::u32(ordinal), 2449 ValRaw::u32(handle), 2450 ValRaw::u32(result), 2451 ]; 2452 // SAFETY: `func` is a valid `*mut VMFuncRef` from either 2453 // `wasmtime-cranelift`-generated fused adapter code or 2454 // `component::Options`. Per `wasmparser` callback signature 2455 // validation, we know it takes three parameters and returns one. 2456 unsafe { 2457 crate::Func::call_unchecked_raw( 2458 &mut store, 2459 function.as_non_null(), 2460 params.as_mut_slice().into(), 2461 )?; 2462 } 2463 Ok(params[0].get_u32()) 2464 } 2465 2466 /// Start a guest->guest call previously prepared using 2467 /// `Self::prepare_call`. 2468 /// 2469 /// This is called from fused adapter code generated in 2470 /// `wasmtime_environ::fact::trampoline::Compiler`. The adapter will call 2471 /// this function immediately after calling `Self::prepare_call`. 2472 /// 2473 /// SAFETY: The `*mut VMFuncRef` arguments must be valid pointers to guest 2474 /// functions with the appropriate signatures for the current guest task. 2475 /// If this is a call to an async-lowered import, the actual call may be 2476 /// deferred and run after this function returns, in which case the pointer 2477 /// arguments must also be valid when the call happens. 2478 unsafe fn start_call<T: 'static>( 2479 self, 2480 mut store: StoreContextMut<T>, 2481 callback: *mut VMFuncRef, 2482 post_return: *mut VMFuncRef, 2483 callee: *mut VMFuncRef, 2484 param_count: u32, 2485 result_count: u32, 2486 flags: u32, 2487 storage: Option<&mut [MaybeUninit<ValRaw>]>, 2488 ) -> Result<u32> { 2489 let token = StoreToken::new(store.as_context_mut()); 2490 let async_caller = storage.is_none(); 2491 let state = store.0.concurrent_state_mut(); 2492 let guest_thread = state.guest_thread.unwrap(); 2493 let callee_async = state.get_mut(guest_thread.task)?.async_function; 2494 let callee = SendSyncPtr::new(NonNull::new(callee).unwrap()); 2495 let param_count = usize::try_from(param_count).unwrap(); 2496 assert!(param_count <= MAX_FLAT_PARAMS); 2497 let result_count = usize::try_from(result_count).unwrap(); 2498 assert!(result_count <= MAX_FLAT_RESULTS); 2499 2500 let task = state.get_mut(guest_thread.task)?; 2501 if !callback.is_null() { 2502 // We're calling an async-lifted export with a callback, so store 2503 // the callback and related context as part of the task so we can 2504 // call it later when needed. 2505 let callback = SendSyncPtr::new(NonNull::new(callback).unwrap()); 2506 task.callback = Some(Box::new(move |store, event, handle| { 2507 let store = token.as_context_mut(store); 2508 unsafe { self.call_callback::<T>(store, callback, event, handle) } 2509 })); 2510 } 2511 2512 let Caller::Guest { 2513 thread: caller, 2514 instance: runtime_instance, 2515 } = &task.caller 2516 else { 2517 // As of this writing, `start_call` is only used for guest->guest 2518 // calls. 2519 unreachable!() 2520 }; 2521 let caller = *caller; 2522 let caller_instance = *runtime_instance; 2523 2524 // Queue the call as a "high priority" work item. 2525 unsafe { 2526 self.queue_call( 2527 store.as_context_mut(), 2528 guest_thread, 2529 callee, 2530 param_count, 2531 result_count, 2532 (flags & START_FLAG_ASYNC_CALLEE) != 0, 2533 NonNull::new(callback).map(SendSyncPtr::new), 2534 NonNull::new(post_return).map(SendSyncPtr::new), 2535 )?; 2536 } 2537 2538 let state = store.0.concurrent_state_mut(); 2539 2540 // Use the caller's `GuestTask::sync_call_set` to register interest in 2541 // the subtask... 2542 let guest_waitable = Waitable::Guest(guest_thread.task); 2543 let old_set = guest_waitable.common(state)?.set; 2544 let set = state.get_mut(caller.task)?.sync_call_set; 2545 guest_waitable.join(state, Some(set))?; 2546 2547 // ... and suspend this fiber temporarily while we wait for it to start. 2548 // 2549 // Note that we _could_ call the callee directly using the current fiber 2550 // rather than suspend this one, but that would make reasoning about the 2551 // event loop more complicated and is probably only worth doing if 2552 // there's a measurable performance benefit. In addition, it would mean 2553 // blocking the caller if the callee calls a blocking sync-lowered 2554 // import, and as of this writing the spec says we must not do that. 2555 // 2556 // Alternatively, the fused adapter code could be modified to call the 2557 // callee directly without calling a host-provided intrinsic at all (in 2558 // which case it would need to do its own, inline backpressure checks, 2559 // etc.). Again, we'd want to see a measurable performance benefit 2560 // before committing to such an optimization. And again, we'd need to 2561 // update the spec to allow that. 2562 let (status, waitable) = loop { 2563 store.0.suspend(SuspendReason::Waiting { 2564 set, 2565 thread: caller, 2566 // Normally, `StoreOpaque::suspend` would assert it's being 2567 // called from a context where blocking is allowed. However, if 2568 // `async_caller` is `true`, we'll only "block" long enough for 2569 // the callee to start, i.e. we won't repeat this loop, so we 2570 // tell `suspend` it's okay even if we're not allowed to block. 2571 // Alternatively, if the callee is not an async function, then 2572 // we know it won't block anyway. 2573 skip_may_block_check: async_caller || !callee_async, 2574 })?; 2575 2576 let state = store.0.concurrent_state_mut(); 2577 2578 log::trace!("taking event for {:?}", guest_thread.task); 2579 let event = guest_waitable.take_event(state)?; 2580 let Some(Event::Subtask { status }) = event else { 2581 unreachable!(); 2582 }; 2583 2584 log::trace!("status {status:?} for {:?}", guest_thread.task); 2585 2586 if status == Status::Returned { 2587 // It returned, so we can stop waiting. 2588 break (status, None); 2589 } else if async_caller { 2590 // It hasn't returned yet, but the caller is calling via an 2591 // async-lowered import, so we generate a handle for the task 2592 // waitable and return the status. 2593 let handle = store 2594 .0 2595 .handle_table(caller_instance) 2596 .subtask_insert_guest(guest_thread.task.rep())?; 2597 store 2598 .0 2599 .concurrent_state_mut() 2600 .get_mut(guest_thread.task)? 2601 .common 2602 .handle = Some(handle); 2603 break (status, Some(handle)); 2604 } else { 2605 // The callee hasn't returned yet, and the caller is calling via 2606 // a sync-lowered import, so we loop and keep waiting until the 2607 // callee returns. 2608 } 2609 }; 2610 2611 guest_waitable.join(store.0.concurrent_state_mut(), old_set)?; 2612 2613 // Reset the current thread to point to the caller as it resumes control. 2614 store.0.set_thread(Some(caller)); 2615 store.0.concurrent_state_mut().get_mut(caller.thread)?.state = GuestThreadState::Running; 2616 log::trace!("popped current thread {guest_thread:?}; new thread is {caller:?}"); 2617 2618 if let Some(storage) = storage { 2619 // The caller used a sync-lowered import to call an async-lifted 2620 // export, in which case the result, if any, has been stashed in 2621 // `GuestTask::sync_result`. 2622 let state = store.0.concurrent_state_mut(); 2623 let task = state.get_mut(guest_thread.task)?; 2624 if let Some(result) = task.sync_result.take() { 2625 if let Some(result) = result { 2626 storage[0] = MaybeUninit::new(result); 2627 } 2628 2629 if task.exited && task.ready_to_delete() { 2630 Waitable::Guest(guest_thread.task).delete_from(state)?; 2631 } 2632 } 2633 } 2634 2635 Ok(status.pack(waitable)) 2636 } 2637 2638 /// Poll the specified future once on behalf of a guest->host call using an 2639 /// async-lowered import. 2640 /// 2641 /// If it returns `Ready`, return `Ok(None)`. Otherwise, if it returns 2642 /// `Pending`, add it to the set of futures to be polled as part of this 2643 /// instance's event loop until it completes, and then return 2644 /// `Ok(Some(handle))` where `handle` is the waitable handle to return. 2645 /// 2646 /// Whether the future returns `Ready` immediately or later, the `lower` 2647 /// function will be used to lower the result, if any, into the guest caller's 2648 /// stack and linear memory unless the task has been cancelled. 2649 pub(crate) fn first_poll<T: 'static, R: Send + 'static>( 2650 self, 2651 mut store: StoreContextMut<'_, T>, 2652 future: impl Future<Output = Result<R>> + Send + 'static, 2653 caller_instance: RuntimeComponentInstanceIndex, 2654 lower: impl FnOnce(StoreContextMut<T>, R) -> Result<()> + Send + 'static, 2655 ) -> Result<Option<u32>> { 2656 let token = StoreToken::new(store.as_context_mut()); 2657 let state = store.0.concurrent_state_mut(); 2658 let caller = state.guest_thread.unwrap(); 2659 2660 // Create an abortable future which hooks calls to poll and manages call 2661 // context state for the future. 2662 let (join_handle, future) = JoinHandle::run(async move { 2663 let mut future = pin!(future); 2664 let mut call_context = None; 2665 future::poll_fn(move |cx| { 2666 // Push the call context for managing any resource borrows 2667 // for the task. 2668 tls::get(|store| { 2669 if let Some(call_context) = call_context.take() { 2670 token 2671 .as_context_mut(store) 2672 .0 2673 .component_resource_state() 2674 .0 2675 .push(call_context); 2676 } 2677 }); 2678 2679 let result = future.as_mut().poll(cx); 2680 2681 if result.is_pending() { 2682 // Pop the call context for managing any resource 2683 // borrows for the task. 2684 tls::get(|store| { 2685 call_context = Some( 2686 token 2687 .as_context_mut(store) 2688 .0 2689 .component_resource_state() 2690 .0 2691 .pop() 2692 .unwrap(), 2693 ); 2694 }); 2695 } 2696 result 2697 }) 2698 .await 2699 }); 2700 2701 // We create a new host task even though it might complete immediately 2702 // (in which case we won't need to pass a waitable back to the guest). 2703 // If it does complete immediately, we'll remove it before we return. 2704 let task = state.push(HostTask::new( 2705 RuntimeInstance { 2706 instance: self.id().instance(), 2707 index: caller_instance, 2708 }, 2709 Some(join_handle), 2710 ))?; 2711 2712 log::trace!("new host task child of {caller:?}: {task:?}"); 2713 2714 let mut future = Box::pin(future); 2715 2716 // Finally, poll the future. We can use a dummy `Waker` here because 2717 // we'll add the future to `ConcurrentState::futures` and poll it 2718 // automatically from the event loop if it doesn't complete immediately 2719 // here. 2720 let poll = tls::set(store.0, || { 2721 future 2722 .as_mut() 2723 .poll(&mut Context::from_waker(&Waker::noop())) 2724 }); 2725 2726 Ok(match poll { 2727 Poll::Ready(None) => unreachable!(), 2728 Poll::Ready(Some(result)) => { 2729 // It finished immediately; lower the result and delete the 2730 // task. 2731 lower(store.as_context_mut(), result?)?; 2732 log::trace!("delete host task {task:?} (already ready)"); 2733 store.0.concurrent_state_mut().delete(task)?; 2734 None 2735 } 2736 Poll::Pending => { 2737 // It hasn't finished yet; add the future to 2738 // `ConcurrentState::futures` so it will be polled by the event 2739 // loop and allocate a waitable handle to return to the guest. 2740 2741 // Wrap the future in a closure responsible for lowering the result into 2742 // the guest's stack and memory, as well as notifying any waiters that 2743 // the task returned. 2744 let future = 2745 Box::pin(async move { 2746 let result = match future.await { 2747 Some(result) => result?, 2748 // Task was cancelled; nothing left to do. 2749 None => return Ok(()), 2750 }; 2751 tls::get(move |store| { 2752 // Here we schedule a task to run on a worker fiber to do 2753 // the lowering since it may involve a call to the guest's 2754 // realloc function. This is necessary because calling the 2755 // guest while there are host embedder frames on the stack 2756 // is unsound. 2757 store.concurrent_state_mut().push_high_priority( 2758 WorkItem::WorkerFunction(AlwaysMut::new(Box::new(move |store| { 2759 lower(token.as_context_mut(store), result)?; 2760 let state = store.concurrent_state_mut(); 2761 state.get_mut(task)?.join_handle.take(); 2762 Waitable::Host(task).set_event( 2763 state, 2764 Some(Event::Subtask { 2765 status: Status::Returned, 2766 }), 2767 ) 2768 }))), 2769 ); 2770 Ok(()) 2771 }) 2772 }); 2773 2774 store.0.concurrent_state_mut().push_future(future); 2775 let handle = store 2776 .0 2777 .handle_table(RuntimeInstance { 2778 instance: self.id().instance(), 2779 index: caller_instance, 2780 }) 2781 .subtask_insert_host(task.rep())?; 2782 store.0.concurrent_state_mut().get_mut(task)?.common.handle = Some(handle); 2783 log::trace!( 2784 "assign {task:?} handle {handle} for {caller:?} instance {caller_instance:?}" 2785 ); 2786 Some(handle) 2787 } 2788 }) 2789 } 2790 2791 /// Implements the `task.return` intrinsic, lifting the result for the 2792 /// current guest task. 2793 pub(crate) fn task_return( 2794 self, 2795 store: &mut dyn VMStore, 2796 caller: RuntimeComponentInstanceIndex, 2797 ty: TypeTupleIndex, 2798 options: OptionsIndex, 2799 storage: &[ValRaw], 2800 ) -> Result<()> { 2801 self.id().get(store).check_may_leave(caller)?; 2802 let state = store.concurrent_state_mut(); 2803 let guest_thread = state.guest_thread.unwrap(); 2804 let lift = state 2805 .get_mut(guest_thread.task)? 2806 .lift_result 2807 .take() 2808 .ok_or_else(|| { 2809 format_err!("`task.return` or `task.cancel` called more than once for current task") 2810 })?; 2811 assert!(state.get_mut(guest_thread.task)?.result.is_none()); 2812 2813 let CanonicalOptions { 2814 string_encoding, 2815 data_model, 2816 .. 2817 } = &self.id().get(store).component().env_component().options[options]; 2818 2819 let invalid = ty != lift.ty 2820 || string_encoding != &lift.string_encoding 2821 || match data_model { 2822 CanonicalOptionsDataModel::LinearMemory(opts) => match opts.memory { 2823 Some(memory) => { 2824 let expected = lift.memory.map(|v| v.as_ptr()).unwrap_or(ptr::null_mut()); 2825 let actual = self.id().get(store).runtime_memory(memory); 2826 expected != actual.as_ptr() 2827 } 2828 // Memory not specified, meaning it didn't need to be 2829 // specified per validation, so not invalid. 2830 None => false, 2831 }, 2832 // Always invalid as this isn't supported. 2833 CanonicalOptionsDataModel::Gc { .. } => true, 2834 }; 2835 2836 if invalid { 2837 bail!("invalid `task.return` signature and/or options for current task"); 2838 } 2839 2840 log::trace!("task.return for {guest_thread:?}"); 2841 2842 let result = (lift.lift)(store, storage)?; 2843 self.task_complete( 2844 store, 2845 guest_thread.task, 2846 result, 2847 Status::Returned, 2848 ValRaw::i32(0), 2849 ) 2850 } 2851 2852 /// Implements the `task.cancel` intrinsic. 2853 pub(crate) fn task_cancel( 2854 self, 2855 store: &mut StoreOpaque, 2856 caller: RuntimeComponentInstanceIndex, 2857 ) -> Result<()> { 2858 self.id().get(store).check_may_leave(caller)?; 2859 let state = store.concurrent_state_mut(); 2860 let guest_thread = state.guest_thread.unwrap(); 2861 let task = state.get_mut(guest_thread.task)?; 2862 if !task.cancel_sent { 2863 bail!("`task.cancel` called by task which has not been cancelled") 2864 } 2865 _ = task.lift_result.take().ok_or_else(|| { 2866 format_err!("`task.return` or `task.cancel` called more than once for current task") 2867 })?; 2868 2869 assert!(task.result.is_none()); 2870 2871 log::trace!("task.cancel for {guest_thread:?}"); 2872 2873 self.task_complete( 2874 store, 2875 guest_thread.task, 2876 Box::new(DummyResult), 2877 Status::ReturnCancelled, 2878 ValRaw::i32(0), 2879 ) 2880 } 2881 2882 /// Complete the specified guest task (i.e. indicate that it has either 2883 /// returned a (possibly empty) result or cancelled itself). 2884 /// 2885 /// This will return any resource borrows and notify any current or future 2886 /// waiters that the task has completed. 2887 fn task_complete( 2888 self, 2889 store: &mut StoreOpaque, 2890 guest_task: TableId<GuestTask>, 2891 result: Box<dyn Any + Send + Sync>, 2892 status: Status, 2893 post_return_arg: ValRaw, 2894 ) -> Result<()> { 2895 if store 2896 .concurrent_state_mut() 2897 .get_mut(guest_task)? 2898 .call_post_return_automatically() 2899 { 2900 let (calls, host_table, _, instance) = 2901 store.component_resource_state_with_instance(self); 2902 ResourceTables { 2903 calls, 2904 host_table: Some(host_table), 2905 guest: Some(instance.instance_states()), 2906 } 2907 .exit_call()?; 2908 } else { 2909 // As of this writing, the only scenario where `call_post_return_automatically` 2910 // would be false for a `GuestTask` is for host-to-guest calls using 2911 // `[Typed]Func::call_async`, in which case the `function_index` 2912 // should be a non-`None` value. 2913 let function_index = store 2914 .concurrent_state_mut() 2915 .get_mut(guest_task)? 2916 .function_index 2917 .unwrap(); 2918 self.id() 2919 .get_mut(store) 2920 .post_return_arg_set(function_index, post_return_arg); 2921 } 2922 2923 let state = store.concurrent_state_mut(); 2924 let task = state.get_mut(guest_task)?; 2925 2926 if let Caller::Host { tx, .. } = &mut task.caller { 2927 if let Some(tx) = tx.take() { 2928 _ = tx.send(result); 2929 } 2930 } else { 2931 task.result = Some(result); 2932 Waitable::Guest(guest_task).set_event(state, Some(Event::Subtask { status }))?; 2933 } 2934 2935 Ok(()) 2936 } 2937 2938 /// Implements the `waitable-set.new` intrinsic. 2939 pub(crate) fn waitable_set_new( 2940 self, 2941 store: &mut StoreOpaque, 2942 caller_instance: RuntimeComponentInstanceIndex, 2943 ) -> Result<u32> { 2944 self.id().get_mut(store).check_may_leave(caller_instance)?; 2945 let set = store.concurrent_state_mut().push(WaitableSet::default())?; 2946 let handle = store 2947 .handle_table(RuntimeInstance { 2948 instance: self.id().instance(), 2949 index: caller_instance, 2950 }) 2951 .waitable_set_insert(set.rep())?; 2952 log::trace!("new waitable set {set:?} (handle {handle})"); 2953 Ok(handle) 2954 } 2955 2956 /// Implements the `waitable-set.drop` intrinsic. 2957 pub(crate) fn waitable_set_drop( 2958 self, 2959 store: &mut StoreOpaque, 2960 caller_instance: RuntimeComponentInstanceIndex, 2961 set: u32, 2962 ) -> Result<()> { 2963 self.id().get_mut(store).check_may_leave(caller_instance)?; 2964 let rep = store 2965 .handle_table(RuntimeInstance { 2966 instance: self.id().instance(), 2967 index: caller_instance, 2968 }) 2969 .waitable_set_remove(set)?; 2970 2971 log::trace!("drop waitable set {rep} (handle {set})"); 2972 2973 let set = store 2974 .concurrent_state_mut() 2975 .delete(TableId::<WaitableSet>::new(rep))?; 2976 2977 if !set.waiting.is_empty() { 2978 bail!("cannot drop waitable set with waiters"); 2979 } 2980 2981 Ok(()) 2982 } 2983 2984 /// Implements the `waitable.join` intrinsic. 2985 pub(crate) fn waitable_join( 2986 self, 2987 store: &mut StoreOpaque, 2988 caller_instance: RuntimeComponentInstanceIndex, 2989 waitable_handle: u32, 2990 set_handle: u32, 2991 ) -> Result<()> { 2992 let mut instance = self.id().get_mut(store); 2993 instance.check_may_leave(caller_instance)?; 2994 let waitable = 2995 Waitable::from_instance(instance.as_mut(), caller_instance, waitable_handle)?; 2996 2997 let set = if set_handle == 0 { 2998 None 2999 } else { 3000 let set = instance.instance_states().0[caller_instance] 3001 .handle_table() 3002 .waitable_set_rep(set_handle)?; 3003 3004 Some(TableId::<WaitableSet>::new(set)) 3005 }; 3006 3007 log::trace!( 3008 "waitable {waitable:?} (handle {waitable_handle}) join set {set:?} (handle {set_handle})", 3009 ); 3010 3011 waitable.join(store.concurrent_state_mut(), set) 3012 } 3013 3014 /// Implements the `subtask.drop` intrinsic. 3015 pub(crate) fn subtask_drop( 3016 self, 3017 store: &mut StoreOpaque, 3018 caller_instance: RuntimeComponentInstanceIndex, 3019 task_id: u32, 3020 ) -> Result<()> { 3021 self.id().get_mut(store).check_may_leave(caller_instance)?; 3022 self.waitable_join(store, caller_instance, task_id, 0)?; 3023 3024 let (rep, is_host) = store 3025 .handle_table(RuntimeInstance { 3026 instance: self.id().instance(), 3027 index: caller_instance, 3028 }) 3029 .subtask_remove(task_id)?; 3030 3031 let concurrent_state = store.concurrent_state_mut(); 3032 let (waitable, expected_caller_instance, delete) = if is_host { 3033 let id = TableId::<HostTask>::new(rep); 3034 let task = concurrent_state.get_mut(id)?; 3035 if task.join_handle.is_some() { 3036 bail!("cannot drop a subtask which has not yet resolved"); 3037 } 3038 (Waitable::Host(id), task.caller_instance, true) 3039 } else { 3040 let id = TableId::<GuestTask>::new(rep); 3041 let task = concurrent_state.get_mut(id)?; 3042 if task.lift_result.is_some() { 3043 bail!("cannot drop a subtask which has not yet resolved"); 3044 } 3045 if let Caller::Guest { instance, .. } = &task.caller { 3046 (Waitable::Guest(id), *instance, task.exited) 3047 } else { 3048 unreachable!() 3049 } 3050 }; 3051 3052 waitable.common(concurrent_state)?.handle = None; 3053 3054 if waitable.take_event(concurrent_state)?.is_some() { 3055 bail!("cannot drop a subtask with an undelivered event"); 3056 } 3057 3058 if delete { 3059 waitable.delete_from(concurrent_state)?; 3060 } 3061 3062 // Since waitables can neither be passed between instances nor forged, 3063 // this should never fail unless there's a bug in Wasmtime, but we check 3064 // here to be sure: 3065 assert_eq!( 3066 expected_caller_instance, 3067 RuntimeInstance { 3068 instance: self.id().instance(), 3069 index: caller_instance 3070 } 3071 ); 3072 log::trace!("subtask_drop {waitable:?} (handle {task_id})"); 3073 Ok(()) 3074 } 3075 3076 /// Implements the `waitable-set.wait` intrinsic. 3077 pub(crate) fn waitable_set_wait( 3078 self, 3079 store: &mut StoreOpaque, 3080 caller: RuntimeComponentInstanceIndex, 3081 options: OptionsIndex, 3082 set: u32, 3083 payload: u32, 3084 ) -> Result<u32> { 3085 self.id().get(store).check_may_leave(caller)?; 3086 3087 if !self.options(store, options).async_ { 3088 // The caller may only call `waitable-set.wait` from an async task 3089 // (i.e. a task created via a call to an async export). 3090 // Otherwise, we'll trap. 3091 store.check_blocking()?; 3092 } 3093 3094 let &CanonicalOptions { 3095 cancellable, 3096 instance: caller_instance, 3097 .. 3098 } = &self.id().get(store).component().env_component().options[options]; 3099 let rep = store 3100 .handle_table(RuntimeInstance { 3101 instance: self.id().instance(), 3102 index: caller_instance, 3103 }) 3104 .waitable_set_rep(set)?; 3105 3106 self.waitable_check( 3107 store, 3108 cancellable, 3109 WaitableCheck::Wait, 3110 WaitableCheckParams { 3111 set: TableId::new(rep), 3112 options, 3113 payload, 3114 }, 3115 ) 3116 } 3117 3118 /// Implements the `waitable-set.poll` intrinsic. 3119 pub(crate) fn waitable_set_poll( 3120 self, 3121 store: &mut StoreOpaque, 3122 caller: RuntimeComponentInstanceIndex, 3123 options: OptionsIndex, 3124 set: u32, 3125 payload: u32, 3126 ) -> Result<u32> { 3127 self.id().get(store).check_may_leave(caller)?; 3128 3129 let &CanonicalOptions { 3130 cancellable, 3131 instance: caller_instance, 3132 .. 3133 } = &self.id().get(store).component().env_component().options[options]; 3134 let rep = store 3135 .handle_table(RuntimeInstance { 3136 instance: self.id().instance(), 3137 index: caller_instance, 3138 }) 3139 .waitable_set_rep(set)?; 3140 3141 self.waitable_check( 3142 store, 3143 cancellable, 3144 WaitableCheck::Poll, 3145 WaitableCheckParams { 3146 set: TableId::new(rep), 3147 options, 3148 payload, 3149 }, 3150 ) 3151 } 3152 3153 /// Implements the `thread.index` intrinsic. 3154 pub(crate) fn thread_index(&self, store: &mut dyn VMStore) -> Result<u32> { 3155 let thread_id = store 3156 .concurrent_state_mut() 3157 .guest_thread 3158 .ok_or_else(|| format_err!("no current thread"))? 3159 .thread; 3160 // The unwrap is safe because `instance_rep` must be `Some` by this point 3161 Ok(store 3162 .concurrent_state_mut() 3163 .get_mut(thread_id)? 3164 .instance_rep 3165 .unwrap()) 3166 } 3167 3168 /// Implements the `thread.new-indirect` intrinsic. 3169 pub(crate) fn thread_new_indirect<T: 'static>( 3170 self, 3171 mut store: StoreContextMut<T>, 3172 runtime_instance: RuntimeComponentInstanceIndex, 3173 _func_ty_idx: TypeFuncIndex, // currently unused 3174 start_func_table_idx: RuntimeTableIndex, 3175 start_func_idx: u32, 3176 context: i32, 3177 ) -> Result<u32> { 3178 self.id().get(store.0).check_may_leave(runtime_instance)?; 3179 3180 log::trace!("creating new thread"); 3181 3182 let start_func_ty = FuncType::new(store.engine(), [ValType::I32], []); 3183 let (instance, registry) = self.id().get_mut_and_registry(store.0); 3184 let callee = instance 3185 .index_runtime_func_table(registry, start_func_table_idx, start_func_idx as u64)? 3186 .ok_or_else(|| { 3187 format_err!("the start function index points to an uninitialized function") 3188 })?; 3189 if callee.type_index(store.0) != start_func_ty.type_index() { 3190 bail!( 3191 "start function does not match expected type (currently only `(i32) -> ()` is supported)" 3192 ); 3193 } 3194 3195 let token = StoreToken::new(store.as_context_mut()); 3196 let start_func = Box::new( 3197 move |store: &mut dyn VMStore, guest_thread: QualifiedThreadId| -> Result<()> { 3198 let old_thread = store.set_thread(Some(guest_thread)); 3199 log::trace!( 3200 "thread start: replaced {old_thread:?} with {guest_thread:?} as current thread" 3201 ); 3202 3203 store.maybe_push_call_context(guest_thread.task)?; 3204 3205 let mut store = token.as_context_mut(store); 3206 let mut params = [ValRaw::i32(context)]; 3207 // Use call_unchecked rather than call or call_async, as we don't want to run the function 3208 // on a separate fiber if we're running in an async store. 3209 unsafe { callee.call_unchecked(store.as_context_mut(), &mut params)? }; 3210 3211 store.0.maybe_pop_call_context(guest_thread.task)?; 3212 3213 self.cleanup_thread(store.0, guest_thread, runtime_instance)?; 3214 log::trace!("explicit thread {guest_thread:?} completed"); 3215 let state = store.0.concurrent_state_mut(); 3216 let task = state.get_mut(guest_thread.task)?; 3217 if task.threads.is_empty() && !task.returned_or_cancelled() { 3218 bail!(Trap::NoAsyncResult); 3219 } 3220 store.0.set_thread(old_thread); 3221 let state = store.0.concurrent_state_mut(); 3222 old_thread 3223 .map(|t| state.get_mut(t.thread).unwrap().state = GuestThreadState::Running); 3224 if state.get_mut(guest_thread.task)?.ready_to_delete() { 3225 Waitable::Guest(guest_thread.task).delete_from(state)?; 3226 } 3227 log::trace!("thread start: restored {old_thread:?} as current thread"); 3228 3229 Ok(()) 3230 }, 3231 ); 3232 3233 let state = store.0.concurrent_state_mut(); 3234 let current_thread = state.guest_thread.unwrap(); 3235 let parent_task = current_thread.task; 3236 3237 let new_thread = GuestThread::new_explicit(parent_task, start_func); 3238 let thread_id = state.push(new_thread)?; 3239 state.get_mut(parent_task)?.threads.insert(thread_id); 3240 3241 log::trace!("new thread with id {thread_id:?} created"); 3242 3243 self.add_guest_thread_to_instance_table(thread_id, store.0, runtime_instance) 3244 } 3245 3246 pub(crate) fn resume_suspended_thread( 3247 self, 3248 store: &mut StoreOpaque, 3249 runtime_instance: RuntimeComponentInstanceIndex, 3250 thread_idx: u32, 3251 high_priority: bool, 3252 ) -> Result<()> { 3253 let thread_id = 3254 GuestThread::from_instance(self.id().get_mut(store), runtime_instance, thread_idx)?; 3255 let state = store.concurrent_state_mut(); 3256 let guest_thread = QualifiedThreadId::qualify(state, thread_id)?; 3257 let thread = state.get_mut(guest_thread.thread)?; 3258 3259 match mem::replace(&mut thread.state, GuestThreadState::Running) { 3260 GuestThreadState::NotStartedExplicit(start_func) => { 3261 log::trace!("starting thread {guest_thread:?}"); 3262 let guest_call = WorkItem::GuestCall(GuestCall { 3263 thread: guest_thread, 3264 kind: GuestCallKind::StartExplicit(Box::new(move |store| { 3265 start_func(store, guest_thread) 3266 })), 3267 }); 3268 store 3269 .concurrent_state_mut() 3270 .push_work_item(guest_call, high_priority); 3271 } 3272 GuestThreadState::Suspended(fiber) => { 3273 log::trace!("resuming thread {thread_id:?} that was suspended"); 3274 store 3275 .concurrent_state_mut() 3276 .push_work_item(WorkItem::ResumeFiber(fiber), high_priority); 3277 } 3278 _ => { 3279 bail!("cannot resume thread which is not suspended"); 3280 } 3281 } 3282 Ok(()) 3283 } 3284 3285 fn add_guest_thread_to_instance_table( 3286 self, 3287 thread_id: TableId<GuestThread>, 3288 store: &mut StoreOpaque, 3289 runtime_instance: RuntimeComponentInstanceIndex, 3290 ) -> Result<u32> { 3291 let guest_id = store 3292 .handle_table(RuntimeInstance { 3293 instance: self.id().instance(), 3294 index: runtime_instance, 3295 }) 3296 .guest_thread_insert(thread_id.rep())?; 3297 store 3298 .concurrent_state_mut() 3299 .get_mut(thread_id)? 3300 .instance_rep = Some(guest_id); 3301 Ok(guest_id) 3302 } 3303 3304 /// Helper function for the `thread.yield`, `thread.yield-to`, `thread.suspend`, 3305 /// and `thread.switch-to` intrinsics. 3306 pub(crate) fn suspension_intrinsic( 3307 self, 3308 store: &mut StoreOpaque, 3309 caller: RuntimeComponentInstanceIndex, 3310 cancellable: bool, 3311 yielding: bool, 3312 to_thread: Option<u32>, 3313 ) -> Result<WaitResult> { 3314 self.id().get(store).check_may_leave(caller)?; 3315 3316 if to_thread.is_none() { 3317 let state = store.concurrent_state_mut(); 3318 if yielding { 3319 // This is a `thread.yield` call 3320 if !state.may_block(state.guest_thread.unwrap().task) { 3321 // The spec defines `thread.yield` to be a no-op in a 3322 // non-blocking context, so we return immediately without giving 3323 // any other thread a chance to run. 3324 return Ok(WaitResult::Completed); 3325 } 3326 } else { 3327 // The caller may only call `thread.suspend` from an async task 3328 // (i.e. a task created via a call to an async export). 3329 // Otherwise, we'll trap. 3330 store.check_blocking()?; 3331 } 3332 } 3333 3334 // There could be a pending cancellation from a previous uncancellable wait 3335 if cancellable && store.concurrent_state_mut().take_pending_cancellation() { 3336 return Ok(WaitResult::Cancelled); 3337 } 3338 3339 if let Some(thread) = to_thread { 3340 self.resume_suspended_thread(store, caller, thread, true)?; 3341 } 3342 3343 let state = store.concurrent_state_mut(); 3344 let guest_thread = state.guest_thread.unwrap(); 3345 let reason = if yielding { 3346 SuspendReason::Yielding { 3347 thread: guest_thread, 3348 // Tell `StoreOpaque::suspend` it's okay to suspend here since 3349 // we're handling a `thread.yield-to` call; otherwise it would 3350 // panic if we called it in a non-blocking context. 3351 skip_may_block_check: to_thread.is_some(), 3352 } 3353 } else { 3354 SuspendReason::ExplicitlySuspending { 3355 thread: guest_thread, 3356 // Tell `StoreOpaque::suspend` it's okay to suspend here since 3357 // we're handling a `thread.switch-to` call; otherwise it would 3358 // panic if we called it in a non-blocking context. 3359 skip_may_block_check: to_thread.is_some(), 3360 } 3361 }; 3362 3363 store.suspend(reason)?; 3364 3365 if cancellable && store.concurrent_state_mut().take_pending_cancellation() { 3366 Ok(WaitResult::Cancelled) 3367 } else { 3368 Ok(WaitResult::Completed) 3369 } 3370 } 3371 3372 /// Helper function for the `waitable-set.wait` and `waitable-set.poll` intrinsics. 3373 fn waitable_check( 3374 self, 3375 store: &mut StoreOpaque, 3376 cancellable: bool, 3377 check: WaitableCheck, 3378 params: WaitableCheckParams, 3379 ) -> Result<u32> { 3380 let guest_thread = store.concurrent_state_mut().guest_thread.unwrap(); 3381 3382 log::trace!("waitable check for {guest_thread:?}; set {:?}", params.set); 3383 3384 let state = store.concurrent_state_mut(); 3385 let task = state.get_mut(guest_thread.task)?; 3386 3387 // If we're waiting, and there are no events immediately available, 3388 // suspend the fiber until that changes. 3389 match &check { 3390 WaitableCheck::Wait => { 3391 let set = params.set; 3392 3393 if (task.event.is_none() 3394 || (matches!(task.event, Some(Event::Cancelled)) && !cancellable)) 3395 && state.get_mut(set)?.ready.is_empty() 3396 { 3397 if cancellable { 3398 let old = state 3399 .get_mut(guest_thread.thread)? 3400 .wake_on_cancel 3401 .replace(set); 3402 assert!(old.is_none()); 3403 } 3404 3405 store.suspend(SuspendReason::Waiting { 3406 set, 3407 thread: guest_thread, 3408 skip_may_block_check: false, 3409 })?; 3410 } 3411 } 3412 WaitableCheck::Poll => {} 3413 } 3414 3415 log::trace!( 3416 "waitable check for {guest_thread:?}; set {:?}, part two", 3417 params.set 3418 ); 3419 3420 // Deliver any pending events to the guest and return. 3421 let event = self.get_event(store, guest_thread.task, Some(params.set), cancellable)?; 3422 3423 let (ordinal, handle, result) = match &check { 3424 WaitableCheck::Wait => { 3425 let (event, waitable) = event.unwrap(); 3426 let handle = waitable.map(|(_, v)| v).unwrap_or(0); 3427 let (ordinal, result) = event.parts(); 3428 (ordinal, handle, result) 3429 } 3430 WaitableCheck::Poll => { 3431 if let Some((event, waitable)) = event { 3432 let handle = waitable.map(|(_, v)| v).unwrap_or(0); 3433 let (ordinal, result) = event.parts(); 3434 (ordinal, handle, result) 3435 } else { 3436 log::trace!( 3437 "no events ready to deliver via waitable-set.poll to {:?}; set {:?}", 3438 guest_thread.task, 3439 params.set 3440 ); 3441 let (ordinal, result) = Event::None.parts(); 3442 (ordinal, 0, result) 3443 } 3444 } 3445 }; 3446 let memory = self.options_memory_mut(store, params.options); 3447 let ptr = func::validate_inbounds_dynamic( 3448 &CanonicalAbiInfo::POINTER_PAIR, 3449 memory, 3450 &ValRaw::u32(params.payload), 3451 )?; 3452 memory[ptr + 0..][..4].copy_from_slice(&handle.to_le_bytes()); 3453 memory[ptr + 4..][..4].copy_from_slice(&result.to_le_bytes()); 3454 Ok(ordinal) 3455 } 3456 3457 /// Implements the `subtask.cancel` intrinsic. 3458 pub(crate) fn subtask_cancel( 3459 self, 3460 store: &mut StoreOpaque, 3461 caller_instance: RuntimeComponentInstanceIndex, 3462 async_: bool, 3463 task_id: u32, 3464 ) -> Result<u32> { 3465 self.id().get(store).check_may_leave(caller_instance)?; 3466 3467 if !async_ { 3468 // The caller may only sync call `subtask.cancel` from an async task 3469 // (i.e. a task created via a call to an async export). Otherwise, 3470 // we'll trap. 3471 store.check_blocking()?; 3472 } 3473 3474 let (rep, is_host) = store 3475 .handle_table(RuntimeInstance { 3476 instance: self.id().instance(), 3477 index: caller_instance, 3478 }) 3479 .subtask_rep(task_id)?; 3480 let (waitable, expected_caller_instance) = if is_host { 3481 let id = TableId::<HostTask>::new(rep); 3482 ( 3483 Waitable::Host(id), 3484 store.concurrent_state_mut().get_mut(id)?.caller_instance, 3485 ) 3486 } else { 3487 let id = TableId::<GuestTask>::new(rep); 3488 if let Caller::Guest { instance, .. } = 3489 &store.concurrent_state_mut().get_mut(id)?.caller 3490 { 3491 (Waitable::Guest(id), *instance) 3492 } else { 3493 unreachable!() 3494 } 3495 }; 3496 // Since waitables can neither be passed between instances nor forged, 3497 // this should never fail unless there's a bug in Wasmtime, but we check 3498 // here to be sure: 3499 assert_eq!( 3500 expected_caller_instance, 3501 RuntimeInstance { 3502 instance: self.id().instance(), 3503 index: caller_instance 3504 } 3505 ); 3506 3507 log::trace!("subtask_cancel {waitable:?} (handle {task_id})"); 3508 3509 let concurrent_state = store.concurrent_state_mut(); 3510 if let Waitable::Host(host_task) = waitable { 3511 if let Some(handle) = concurrent_state.get_mut(host_task)?.join_handle.take() { 3512 handle.abort(); 3513 return Ok(Status::ReturnCancelled as u32); 3514 } 3515 } else { 3516 let caller = concurrent_state.guest_thread.unwrap(); 3517 let guest_task = TableId::<GuestTask>::new(rep); 3518 let task = concurrent_state.get_mut(guest_task)?; 3519 if !task.already_lowered_parameters() { 3520 // The task is in a `starting` state, meaning it hasn't run at 3521 // all yet. Here we update its fields to indicate that it is 3522 // ready to delete immediately once `subtask.drop` is called. 3523 task.lower_params = None; 3524 task.lift_result = None; 3525 task.exited = true; 3526 3527 let instance = task.instance; 3528 3529 assert_eq!(1, task.threads.len()); 3530 let thread = mem::take(&mut task.threads).into_iter().next().unwrap(); 3531 let concurrent_state = store.concurrent_state_mut(); 3532 concurrent_state.delete(thread)?; 3533 assert!(concurrent_state.get_mut(guest_task)?.ready_to_delete()); 3534 3535 // Not yet started; cancel and remove from pending 3536 let pending = &mut store.instance_state(instance).pending; 3537 let pending_count = pending.len(); 3538 pending.retain(|thread, _| thread.task != guest_task); 3539 // If there were no pending threads for this task, we're in an error state 3540 if pending.len() == pending_count { 3541 bail!("`subtask.cancel` called after terminal status delivered"); 3542 } 3543 return Ok(Status::StartCancelled as u32); 3544 } else if !task.returned_or_cancelled() { 3545 // Started, but not yet returned or cancelled; send the 3546 // `CANCELLED` event 3547 task.cancel_sent = true; 3548 // Note that this might overwrite an event that was set earlier 3549 // (e.g. `Event::None` if the task is yielding, or 3550 // `Event::Cancelled` if it was already cancelled), but that's 3551 // okay -- this should supersede the previous state. 3552 task.event = Some(Event::Cancelled); 3553 for thread in task.threads.clone() { 3554 let thread = QualifiedThreadId { 3555 task: guest_task, 3556 thread, 3557 }; 3558 if let Some(set) = concurrent_state 3559 .get_mut(thread.thread) 3560 .unwrap() 3561 .wake_on_cancel 3562 .take() 3563 { 3564 let item = match concurrent_state 3565 .get_mut(set)? 3566 .waiting 3567 .remove(&thread) 3568 .unwrap() 3569 { 3570 WaitMode::Fiber(fiber) => WorkItem::ResumeFiber(fiber), 3571 WaitMode::Callback(instance) => WorkItem::GuestCall(GuestCall { 3572 thread, 3573 kind: GuestCallKind::DeliverEvent { 3574 instance, 3575 set: None, 3576 }, 3577 }), 3578 }; 3579 concurrent_state.push_high_priority(item); 3580 3581 store.suspend(SuspendReason::Yielding { 3582 thread: caller, 3583 // `subtask.cancel` is not allowed to be called in a 3584 // sync context, so we cannot skip the may-block check. 3585 skip_may_block_check: false, 3586 })?; 3587 break; 3588 } 3589 } 3590 3591 let concurrent_state = store.concurrent_state_mut(); 3592 let task = concurrent_state.get_mut(guest_task)?; 3593 if !task.returned_or_cancelled() { 3594 if async_ { 3595 return Ok(BLOCKED); 3596 } else { 3597 store.wait_for_event(Waitable::Guest(guest_task))?; 3598 } 3599 } 3600 } 3601 } 3602 3603 let event = waitable.take_event(store.concurrent_state_mut())?; 3604 if let Some(Event::Subtask { 3605 status: status @ (Status::Returned | Status::ReturnCancelled), 3606 }) = event 3607 { 3608 Ok(status as u32) 3609 } else { 3610 bail!("`subtask.cancel` called after terminal status delivered"); 3611 } 3612 } 3613 3614 pub(crate) fn context_get( 3615 self, 3616 store: &mut StoreOpaque, 3617 caller: RuntimeComponentInstanceIndex, 3618 slot: u32, 3619 ) -> Result<u32> { 3620 self.id().get(store).check_may_leave(caller)?; 3621 store.concurrent_state_mut().context_get(slot) 3622 } 3623 3624 pub(crate) fn context_set( 3625 self, 3626 store: &mut StoreOpaque, 3627 caller: RuntimeComponentInstanceIndex, 3628 slot: u32, 3629 value: u32, 3630 ) -> Result<()> { 3631 self.id().get(store).check_may_leave(caller)?; 3632 store.concurrent_state_mut().context_set(slot, value) 3633 } 3634 } 3635 3636 /// Trait representing component model ABI async intrinsics and fused adapter 3637 /// helper functions. 3638 /// 3639 /// SAFETY (callers): Most of the methods in this trait accept raw pointers, 3640 /// which must be valid for at least the duration of the call (and possibly for 3641 /// as long as the relevant guest task exists, in the case of `*mut VMFuncRef` 3642 /// pointers used for async calls). 3643 pub trait VMComponentAsyncStore { 3644 /// A helper function for fused adapter modules involving calls where the 3645 /// one of the caller or callee is async. 3646 /// 3647 /// This helper is not used when the caller and callee both use the sync 3648 /// ABI, only when at least one is async is this used. 3649 unsafe fn prepare_call( 3650 &mut self, 3651 instance: Instance, 3652 memory: *mut VMMemoryDefinition, 3653 start: *mut VMFuncRef, 3654 return_: *mut VMFuncRef, 3655 caller_instance: RuntimeComponentInstanceIndex, 3656 callee_instance: RuntimeComponentInstanceIndex, 3657 task_return_type: TypeTupleIndex, 3658 callee_async: bool, 3659 string_encoding: u8, 3660 result_count: u32, 3661 storage: *mut ValRaw, 3662 storage_len: usize, 3663 ) -> Result<()>; 3664 3665 /// A helper function for fused adapter modules involving calls where the 3666 /// caller is sync-lowered but the callee is async-lifted. 3667 unsafe fn sync_start( 3668 &mut self, 3669 instance: Instance, 3670 callback: *mut VMFuncRef, 3671 callee: *mut VMFuncRef, 3672 param_count: u32, 3673 storage: *mut MaybeUninit<ValRaw>, 3674 storage_len: usize, 3675 ) -> Result<()>; 3676 3677 /// A helper function for fused adapter modules involving calls where the 3678 /// caller is async-lowered. 3679 unsafe fn async_start( 3680 &mut self, 3681 instance: Instance, 3682 callback: *mut VMFuncRef, 3683 post_return: *mut VMFuncRef, 3684 callee: *mut VMFuncRef, 3685 param_count: u32, 3686 result_count: u32, 3687 flags: u32, 3688 ) -> Result<u32>; 3689 3690 /// The `future.write` intrinsic. 3691 fn future_write( 3692 &mut self, 3693 instance: Instance, 3694 caller: RuntimeComponentInstanceIndex, 3695 ty: TypeFutureTableIndex, 3696 options: OptionsIndex, 3697 future: u32, 3698 address: u32, 3699 ) -> Result<u32>; 3700 3701 /// The `future.read` intrinsic. 3702 fn future_read( 3703 &mut self, 3704 instance: Instance, 3705 caller: RuntimeComponentInstanceIndex, 3706 ty: TypeFutureTableIndex, 3707 options: OptionsIndex, 3708 future: u32, 3709 address: u32, 3710 ) -> Result<u32>; 3711 3712 /// The `future.drop-writable` intrinsic. 3713 fn future_drop_writable( 3714 &mut self, 3715 instance: Instance, 3716 caller: RuntimeComponentInstanceIndex, 3717 ty: TypeFutureTableIndex, 3718 writer: u32, 3719 ) -> Result<()>; 3720 3721 /// The `stream.write` intrinsic. 3722 fn stream_write( 3723 &mut self, 3724 instance: Instance, 3725 caller: RuntimeComponentInstanceIndex, 3726 ty: TypeStreamTableIndex, 3727 options: OptionsIndex, 3728 stream: u32, 3729 address: u32, 3730 count: u32, 3731 ) -> Result<u32>; 3732 3733 /// The `stream.read` intrinsic. 3734 fn stream_read( 3735 &mut self, 3736 instance: Instance, 3737 caller: RuntimeComponentInstanceIndex, 3738 ty: TypeStreamTableIndex, 3739 options: OptionsIndex, 3740 stream: u32, 3741 address: u32, 3742 count: u32, 3743 ) -> Result<u32>; 3744 3745 /// The "fast-path" implementation of the `stream.write` intrinsic for 3746 /// "flat" (i.e. memcpy-able) payloads. 3747 fn flat_stream_write( 3748 &mut self, 3749 instance: Instance, 3750 caller: RuntimeComponentInstanceIndex, 3751 ty: TypeStreamTableIndex, 3752 options: OptionsIndex, 3753 payload_size: u32, 3754 payload_align: u32, 3755 stream: u32, 3756 address: u32, 3757 count: u32, 3758 ) -> Result<u32>; 3759 3760 /// The "fast-path" implementation of the `stream.read` intrinsic for "flat" 3761 /// (i.e. memcpy-able) payloads. 3762 fn flat_stream_read( 3763 &mut self, 3764 instance: Instance, 3765 caller: RuntimeComponentInstanceIndex, 3766 ty: TypeStreamTableIndex, 3767 options: OptionsIndex, 3768 payload_size: u32, 3769 payload_align: u32, 3770 stream: u32, 3771 address: u32, 3772 count: u32, 3773 ) -> Result<u32>; 3774 3775 /// The `stream.drop-writable` intrinsic. 3776 fn stream_drop_writable( 3777 &mut self, 3778 instance: Instance, 3779 caller: RuntimeComponentInstanceIndex, 3780 ty: TypeStreamTableIndex, 3781 writer: u32, 3782 ) -> Result<()>; 3783 3784 /// The `error-context.debug-message` intrinsic. 3785 fn error_context_debug_message( 3786 &mut self, 3787 instance: Instance, 3788 caller: RuntimeComponentInstanceIndex, 3789 ty: TypeComponentLocalErrorContextTableIndex, 3790 options: OptionsIndex, 3791 err_ctx_handle: u32, 3792 debug_msg_address: u32, 3793 ) -> Result<()>; 3794 3795 /// The `thread.new-indirect` intrinsic 3796 fn thread_new_indirect( 3797 &mut self, 3798 instance: Instance, 3799 caller: RuntimeComponentInstanceIndex, 3800 func_ty_idx: TypeFuncIndex, 3801 start_func_table_idx: RuntimeTableIndex, 3802 start_func_idx: u32, 3803 context: i32, 3804 ) -> Result<u32>; 3805 } 3806 3807 /// SAFETY: See trait docs. 3808 impl<T: 'static> VMComponentAsyncStore for StoreInner<T> { 3809 unsafe fn prepare_call( 3810 &mut self, 3811 instance: Instance, 3812 memory: *mut VMMemoryDefinition, 3813 start: *mut VMFuncRef, 3814 return_: *mut VMFuncRef, 3815 caller_instance: RuntimeComponentInstanceIndex, 3816 callee_instance: RuntimeComponentInstanceIndex, 3817 task_return_type: TypeTupleIndex, 3818 callee_async: bool, 3819 string_encoding: u8, 3820 result_count_or_max_if_async: u32, 3821 storage: *mut ValRaw, 3822 storage_len: usize, 3823 ) -> Result<()> { 3824 // SAFETY: The `wasmtime_cranelift`-generated code that calls 3825 // this method will have ensured that `storage` is a valid 3826 // pointer containing at least `storage_len` items. 3827 let params = unsafe { std::slice::from_raw_parts(storage, storage_len) }.to_vec(); 3828 3829 unsafe { 3830 instance.prepare_call( 3831 StoreContextMut(self), 3832 start, 3833 return_, 3834 caller_instance, 3835 callee_instance, 3836 task_return_type, 3837 callee_async, 3838 memory, 3839 string_encoding, 3840 match result_count_or_max_if_async { 3841 PREPARE_ASYNC_NO_RESULT => CallerInfo::Async { 3842 params, 3843 has_result: false, 3844 }, 3845 PREPARE_ASYNC_WITH_RESULT => CallerInfo::Async { 3846 params, 3847 has_result: true, 3848 }, 3849 result_count => CallerInfo::Sync { 3850 params, 3851 result_count, 3852 }, 3853 }, 3854 ) 3855 } 3856 } 3857 3858 unsafe fn sync_start( 3859 &mut self, 3860 instance: Instance, 3861 callback: *mut VMFuncRef, 3862 callee: *mut VMFuncRef, 3863 param_count: u32, 3864 storage: *mut MaybeUninit<ValRaw>, 3865 storage_len: usize, 3866 ) -> Result<()> { 3867 unsafe { 3868 instance 3869 .start_call( 3870 StoreContextMut(self), 3871 callback, 3872 ptr::null_mut(), 3873 callee, 3874 param_count, 3875 1, 3876 START_FLAG_ASYNC_CALLEE, 3877 // SAFETY: The `wasmtime_cranelift`-generated code that calls 3878 // this method will have ensured that `storage` is a valid 3879 // pointer containing at least `storage_len` items. 3880 Some(std::slice::from_raw_parts_mut(storage, storage_len)), 3881 ) 3882 .map(drop) 3883 } 3884 } 3885 3886 unsafe fn async_start( 3887 &mut self, 3888 instance: Instance, 3889 callback: *mut VMFuncRef, 3890 post_return: *mut VMFuncRef, 3891 callee: *mut VMFuncRef, 3892 param_count: u32, 3893 result_count: u32, 3894 flags: u32, 3895 ) -> Result<u32> { 3896 unsafe { 3897 instance.start_call( 3898 StoreContextMut(self), 3899 callback, 3900 post_return, 3901 callee, 3902 param_count, 3903 result_count, 3904 flags, 3905 None, 3906 ) 3907 } 3908 } 3909 3910 fn future_write( 3911 &mut self, 3912 instance: Instance, 3913 caller: RuntimeComponentInstanceIndex, 3914 ty: TypeFutureTableIndex, 3915 options: OptionsIndex, 3916 future: u32, 3917 address: u32, 3918 ) -> Result<u32> { 3919 instance.id().get(self).check_may_leave(caller)?; 3920 instance 3921 .guest_write( 3922 StoreContextMut(self), 3923 caller, 3924 TransmitIndex::Future(ty), 3925 options, 3926 None, 3927 future, 3928 address, 3929 1, 3930 ) 3931 .map(|result| result.encode()) 3932 } 3933 3934 fn future_read( 3935 &mut self, 3936 instance: Instance, 3937 caller: RuntimeComponentInstanceIndex, 3938 ty: TypeFutureTableIndex, 3939 options: OptionsIndex, 3940 future: u32, 3941 address: u32, 3942 ) -> Result<u32> { 3943 instance.id().get(self).check_may_leave(caller)?; 3944 instance 3945 .guest_read( 3946 StoreContextMut(self), 3947 caller, 3948 TransmitIndex::Future(ty), 3949 options, 3950 None, 3951 future, 3952 address, 3953 1, 3954 ) 3955 .map(|result| result.encode()) 3956 } 3957 3958 fn stream_write( 3959 &mut self, 3960 instance: Instance, 3961 caller: RuntimeComponentInstanceIndex, 3962 ty: TypeStreamTableIndex, 3963 options: OptionsIndex, 3964 stream: u32, 3965 address: u32, 3966 count: u32, 3967 ) -> Result<u32> { 3968 instance.id().get(self).check_may_leave(caller)?; 3969 instance 3970 .guest_write( 3971 StoreContextMut(self), 3972 caller, 3973 TransmitIndex::Stream(ty), 3974 options, 3975 None, 3976 stream, 3977 address, 3978 count, 3979 ) 3980 .map(|result| result.encode()) 3981 } 3982 3983 fn stream_read( 3984 &mut self, 3985 instance: Instance, 3986 caller: RuntimeComponentInstanceIndex, 3987 ty: TypeStreamTableIndex, 3988 options: OptionsIndex, 3989 stream: u32, 3990 address: u32, 3991 count: u32, 3992 ) -> Result<u32> { 3993 instance.id().get(self).check_may_leave(caller)?; 3994 instance 3995 .guest_read( 3996 StoreContextMut(self), 3997 caller, 3998 TransmitIndex::Stream(ty), 3999 options, 4000 None, 4001 stream, 4002 address, 4003 count, 4004 ) 4005 .map(|result| result.encode()) 4006 } 4007 4008 fn future_drop_writable( 4009 &mut self, 4010 instance: Instance, 4011 caller: RuntimeComponentInstanceIndex, 4012 ty: TypeFutureTableIndex, 4013 writer: u32, 4014 ) -> Result<()> { 4015 instance.id().get(self).check_may_leave(caller)?; 4016 instance.guest_drop_writable(self, TransmitIndex::Future(ty), writer) 4017 } 4018 4019 fn flat_stream_write( 4020 &mut self, 4021 instance: Instance, 4022 caller: RuntimeComponentInstanceIndex, 4023 ty: TypeStreamTableIndex, 4024 options: OptionsIndex, 4025 payload_size: u32, 4026 payload_align: u32, 4027 stream: u32, 4028 address: u32, 4029 count: u32, 4030 ) -> Result<u32> { 4031 instance.id().get(self).check_may_leave(caller)?; 4032 instance 4033 .guest_write( 4034 StoreContextMut(self), 4035 caller, 4036 TransmitIndex::Stream(ty), 4037 options, 4038 Some(FlatAbi { 4039 size: payload_size, 4040 align: payload_align, 4041 }), 4042 stream, 4043 address, 4044 count, 4045 ) 4046 .map(|result| result.encode()) 4047 } 4048 4049 fn flat_stream_read( 4050 &mut self, 4051 instance: Instance, 4052 caller: RuntimeComponentInstanceIndex, 4053 ty: TypeStreamTableIndex, 4054 options: OptionsIndex, 4055 payload_size: u32, 4056 payload_align: u32, 4057 stream: u32, 4058 address: u32, 4059 count: u32, 4060 ) -> Result<u32> { 4061 instance.id().get(self).check_may_leave(caller)?; 4062 instance 4063 .guest_read( 4064 StoreContextMut(self), 4065 caller, 4066 TransmitIndex::Stream(ty), 4067 options, 4068 Some(FlatAbi { 4069 size: payload_size, 4070 align: payload_align, 4071 }), 4072 stream, 4073 address, 4074 count, 4075 ) 4076 .map(|result| result.encode()) 4077 } 4078 4079 fn stream_drop_writable( 4080 &mut self, 4081 instance: Instance, 4082 caller: RuntimeComponentInstanceIndex, 4083 ty: TypeStreamTableIndex, 4084 writer: u32, 4085 ) -> Result<()> { 4086 instance.id().get(self).check_may_leave(caller)?; 4087 instance.guest_drop_writable(self, TransmitIndex::Stream(ty), writer) 4088 } 4089 4090 fn error_context_debug_message( 4091 &mut self, 4092 instance: Instance, 4093 caller: RuntimeComponentInstanceIndex, 4094 ty: TypeComponentLocalErrorContextTableIndex, 4095 options: OptionsIndex, 4096 err_ctx_handle: u32, 4097 debug_msg_address: u32, 4098 ) -> Result<()> { 4099 instance.id().get(self).check_may_leave(caller)?; 4100 instance.error_context_debug_message( 4101 StoreContextMut(self), 4102 ty, 4103 options, 4104 err_ctx_handle, 4105 debug_msg_address, 4106 ) 4107 } 4108 4109 fn thread_new_indirect( 4110 &mut self, 4111 instance: Instance, 4112 caller: RuntimeComponentInstanceIndex, 4113 func_ty_idx: TypeFuncIndex, 4114 start_func_table_idx: RuntimeTableIndex, 4115 start_func_idx: u32, 4116 context: i32, 4117 ) -> Result<u32> { 4118 instance.thread_new_indirect( 4119 StoreContextMut(self), 4120 caller, 4121 func_ty_idx, 4122 start_func_table_idx, 4123 start_func_idx, 4124 context, 4125 ) 4126 } 4127 } 4128 4129 type HostTaskFuture = Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>; 4130 4131 /// Represents the state of a pending host task. 4132 struct HostTask { 4133 common: WaitableCommon, 4134 caller_instance: RuntimeInstance, 4135 join_handle: Option<JoinHandle>, 4136 } 4137 4138 impl HostTask { 4139 fn new(caller_instance: RuntimeInstance, join_handle: Option<JoinHandle>) -> Self { 4140 Self { 4141 common: WaitableCommon::default(), 4142 caller_instance, 4143 join_handle, 4144 } 4145 } 4146 } 4147 4148 impl TableDebug for HostTask { 4149 fn type_name() -> &'static str { 4150 "HostTask" 4151 } 4152 } 4153 4154 type CallbackFn = Box<dyn Fn(&mut dyn VMStore, Event, u32) -> Result<u32> + Send + Sync + 'static>; 4155 4156 /// Represents the caller of a given guest task. 4157 enum Caller { 4158 /// The host called the guest task. 4159 Host { 4160 /// If present, may be used to deliver the result. 4161 tx: Option<oneshot::Sender<LiftedResult>>, 4162 /// Channel to notify once all subtasks spawned by this caller have 4163 /// completed. 4164 /// 4165 /// Note that we'll never actually send anything to this channel; 4166 /// dropping it when the refcount goes to zero is sufficient to notify 4167 /// the receiver. 4168 exit_tx: Arc<oneshot::Sender<()>>, 4169 /// If true, there's a host future that must be dropped before the task 4170 /// can be deleted. 4171 host_future_present: bool, 4172 /// If true, call `post-return` function (if any) automatically. 4173 call_post_return_automatically: bool, 4174 /// If `Some`, represents the `QualifiedThreadId` caller of the host 4175 /// function which called back into a guest. Note that this thread 4176 /// could belong to an entirely unrelated top-level component instance 4177 /// than the one the host called into. 4178 caller: Option<QualifiedThreadId>, 4179 }, 4180 /// Another guest thread called the guest task 4181 Guest { 4182 /// The id of the caller 4183 thread: QualifiedThreadId, 4184 /// The instance to use to enforce reentrance rules. 4185 /// 4186 /// Note that this might not be the same as the instance the caller task 4187 /// started executing in given that one or more synchronous guest->guest 4188 /// calls may have occurred involving multiple instances. 4189 instance: RuntimeInstance, 4190 }, 4191 } 4192 4193 /// Represents a closure and related canonical ABI parameters required to 4194 /// validate a `task.return` call at runtime and lift the result. 4195 struct LiftResult { 4196 lift: RawLift, 4197 ty: TypeTupleIndex, 4198 memory: Option<SendSyncPtr<VMMemoryDefinition>>, 4199 string_encoding: StringEncoding, 4200 } 4201 4202 /// The table ID for a guest thread, qualified by the task to which it belongs. 4203 /// 4204 /// This exists to minimize table lookups and the necessity to pass stores around mutably 4205 /// for the common case of identifying the task to which a thread belongs. 4206 #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] 4207 struct QualifiedThreadId { 4208 task: TableId<GuestTask>, 4209 thread: TableId<GuestThread>, 4210 } 4211 4212 impl QualifiedThreadId { 4213 fn qualify( 4214 state: &mut ConcurrentState, 4215 thread: TableId<GuestThread>, 4216 ) -> Result<QualifiedThreadId> { 4217 Ok(QualifiedThreadId { 4218 task: state.get_mut(thread)?.parent_task, 4219 thread, 4220 }) 4221 } 4222 } 4223 4224 impl fmt::Debug for QualifiedThreadId { 4225 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 4226 f.debug_tuple("QualifiedThreadId") 4227 .field(&self.task.rep()) 4228 .field(&self.thread.rep()) 4229 .finish() 4230 } 4231 } 4232 4233 enum GuestThreadState { 4234 NotStartedImplicit, 4235 NotStartedExplicit( 4236 Box<dyn FnOnce(&mut dyn VMStore, QualifiedThreadId) -> Result<()> + Send + Sync>, 4237 ), 4238 Running, 4239 Suspended(StoreFiber<'static>), 4240 Pending, 4241 Completed, 4242 } 4243 pub struct GuestThread { 4244 /// Context-local state used to implement the `context.{get,set}` 4245 /// intrinsics. 4246 context: [u32; 2], 4247 /// The owning guest task. 4248 parent_task: TableId<GuestTask>, 4249 /// If present, indicates that the thread is currently waiting on the 4250 /// specified set but may be cancelled and woken immediately. 4251 wake_on_cancel: Option<TableId<WaitableSet>>, 4252 /// The execution state of this guest thread 4253 state: GuestThreadState, 4254 /// The index of this thread in the component instance's handle table. 4255 /// This must always be `Some` after initialization. 4256 instance_rep: Option<u32>, 4257 } 4258 4259 impl GuestThread { 4260 /// Retrieve the `GuestThread` corresponding to the specified guest-visible 4261 /// handle. 4262 fn from_instance( 4263 state: Pin<&mut ComponentInstance>, 4264 caller_instance: RuntimeComponentInstanceIndex, 4265 guest_thread: u32, 4266 ) -> Result<TableId<Self>> { 4267 let rep = state.instance_states().0[caller_instance] 4268 .handle_table() 4269 .guest_thread_rep(guest_thread)?; 4270 Ok(TableId::new(rep)) 4271 } 4272 4273 fn new_implicit(parent_task: TableId<GuestTask>) -> Self { 4274 Self { 4275 context: [0; 2], 4276 parent_task, 4277 wake_on_cancel: None, 4278 state: GuestThreadState::NotStartedImplicit, 4279 instance_rep: None, 4280 } 4281 } 4282 4283 fn new_explicit( 4284 parent_task: TableId<GuestTask>, 4285 start_func: Box< 4286 dyn FnOnce(&mut dyn VMStore, QualifiedThreadId) -> Result<()> + Send + Sync, 4287 >, 4288 ) -> Self { 4289 Self { 4290 context: [0; 2], 4291 parent_task, 4292 wake_on_cancel: None, 4293 state: GuestThreadState::NotStartedExplicit(start_func), 4294 instance_rep: None, 4295 } 4296 } 4297 } 4298 4299 impl TableDebug for GuestThread { 4300 fn type_name() -> &'static str { 4301 "GuestThread" 4302 } 4303 } 4304 4305 enum SyncResult { 4306 NotProduced, 4307 Produced(Option<ValRaw>), 4308 Taken, 4309 } 4310 4311 impl SyncResult { 4312 fn take(&mut self) -> Option<Option<ValRaw>> { 4313 match mem::replace(self, SyncResult::Taken) { 4314 SyncResult::NotProduced => None, 4315 SyncResult::Produced(val) => Some(val), 4316 SyncResult::Taken => { 4317 panic!("attempted to take a synchronous result that was already taken") 4318 } 4319 } 4320 } 4321 } 4322 4323 #[derive(Debug)] 4324 enum HostFutureState { 4325 NotApplicable, 4326 Live, 4327 Dropped, 4328 } 4329 4330 /// Represents a pending guest task. 4331 pub(crate) struct GuestTask { 4332 /// See `WaitableCommon` 4333 common: WaitableCommon, 4334 /// Closure to lower the parameters passed to this task. 4335 lower_params: Option<RawLower>, 4336 /// See `LiftResult` 4337 lift_result: Option<LiftResult>, 4338 /// A place to stash the type-erased lifted result if it can't be delivered 4339 /// immediately. 4340 result: Option<LiftedResult>, 4341 /// Closure to call the callback function for an async-lifted export, if 4342 /// provided. 4343 callback: Option<CallbackFn>, 4344 /// See `Caller` 4345 caller: Caller, 4346 /// A place to stash the call context for managing resource borrows while 4347 /// switching between guest tasks. 4348 call_context: Option<CallContext>, 4349 /// A place to stash the lowered result for a sync-to-async call until it 4350 /// can be returned to the caller. 4351 sync_result: SyncResult, 4352 /// Whether or not the task has been cancelled (i.e. whether the task is 4353 /// permitted to call `task.cancel`). 4354 cancel_sent: bool, 4355 /// Whether or not we've sent a `Status::Starting` event to any current or 4356 /// future waiters for this waitable. 4357 starting_sent: bool, 4358 /// Pending guest subtasks created by this task (directly or indirectly). 4359 /// 4360 /// This is used to re-parent subtasks which are still running when their 4361 /// parent task is disposed. 4362 subtasks: HashSet<TableId<GuestTask>>, 4363 /// Scratch waitable set used to watch subtasks during synchronous calls. 4364 sync_call_set: TableId<WaitableSet>, 4365 /// The runtime instance to which the exported function for this guest task 4366 /// belongs. 4367 /// 4368 /// Note that the task may do a sync->sync call via a fused adapter which 4369 /// results in that task executing code in a different instance, and it may 4370 /// call host functions and intrinsics from that other instance. 4371 instance: RuntimeInstance, 4372 /// If present, a pending `Event::None` or `Event::Cancelled` to be 4373 /// delivered to this task. 4374 event: Option<Event>, 4375 /// The `ExportIndex` of the guest function being called, if known. 4376 function_index: Option<ExportIndex>, 4377 /// Whether or not the task has exited. 4378 exited: bool, 4379 /// Threads belonging to this task 4380 threads: HashSet<TableId<GuestThread>>, 4381 /// The state of the host future that represents an async task, which must 4382 /// be dropped before we can delete the task. 4383 host_future_state: HostFutureState, 4384 /// Indicates whether this task was created for a call to an async-lifted 4385 /// export. 4386 async_function: bool, 4387 } 4388 4389 impl GuestTask { 4390 fn already_lowered_parameters(&self) -> bool { 4391 // We reset `lower_params` after we lower the parameters 4392 self.lower_params.is_none() 4393 } 4394 4395 fn returned_or_cancelled(&self) -> bool { 4396 // We reset `lift_result` after we return or exit 4397 self.lift_result.is_none() 4398 } 4399 4400 fn ready_to_delete(&self) -> bool { 4401 let threads_completed = self.threads.is_empty(); 4402 let has_sync_result = matches!(self.sync_result, SyncResult::Produced(_)); 4403 let pending_completion_event = matches!( 4404 self.common.event, 4405 Some(Event::Subtask { 4406 status: Status::Returned | Status::ReturnCancelled 4407 }) 4408 ); 4409 let ready = threads_completed 4410 && !has_sync_result 4411 && !pending_completion_event 4412 && !matches!(self.host_future_state, HostFutureState::Live); 4413 log::trace!( 4414 "ready to delete? {ready} (threads_completed: {}, has_sync_result: {}, pending_completion_event: {}, host_future_state: {:?})", 4415 threads_completed, 4416 has_sync_result, 4417 pending_completion_event, 4418 self.host_future_state 4419 ); 4420 ready 4421 } 4422 4423 fn new( 4424 state: &mut ConcurrentState, 4425 lower_params: RawLower, 4426 lift_result: LiftResult, 4427 caller: Caller, 4428 callback: Option<CallbackFn>, 4429 instance: RuntimeInstance, 4430 async_function: bool, 4431 ) -> Result<Self> { 4432 let sync_call_set = state.push(WaitableSet::default())?; 4433 let host_future_state = match &caller { 4434 Caller::Guest { .. } => HostFutureState::NotApplicable, 4435 Caller::Host { 4436 host_future_present, 4437 .. 4438 } => { 4439 if *host_future_present { 4440 HostFutureState::Live 4441 } else { 4442 HostFutureState::NotApplicable 4443 } 4444 } 4445 }; 4446 Ok(Self { 4447 common: WaitableCommon::default(), 4448 lower_params: Some(lower_params), 4449 lift_result: Some(lift_result), 4450 result: None, 4451 callback, 4452 caller, 4453 call_context: Some(CallContext::default()), 4454 sync_result: SyncResult::NotProduced, 4455 cancel_sent: false, 4456 starting_sent: false, 4457 subtasks: HashSet::new(), 4458 sync_call_set, 4459 instance, 4460 event: None, 4461 function_index: None, 4462 exited: false, 4463 threads: HashSet::new(), 4464 host_future_state, 4465 async_function, 4466 }) 4467 } 4468 4469 /// Dispose of this guest task, reparenting any pending subtasks to the 4470 /// caller. 4471 fn dispose(self, state: &mut ConcurrentState, me: TableId<GuestTask>) -> Result<()> { 4472 // If there are not-yet-delivered completion events for subtasks in 4473 // `self.sync_call_set`, recursively dispose of those subtasks as well. 4474 for waitable in mem::take(&mut state.get_mut(self.sync_call_set)?.ready) { 4475 if let Some(Event::Subtask { 4476 status: Status::Returned | Status::ReturnCancelled, 4477 }) = waitable.common(state)?.event 4478 { 4479 waitable.delete_from(state)?; 4480 } 4481 } 4482 4483 assert!(self.threads.is_empty()); 4484 4485 state.delete(self.sync_call_set)?; 4486 4487 // Reparent any pending subtasks to the caller. 4488 match &self.caller { 4489 Caller::Guest { 4490 thread, 4491 instance: runtime_instance, 4492 } => { 4493 let task_mut = state.get_mut(thread.task)?; 4494 let present = task_mut.subtasks.remove(&me); 4495 assert!(present); 4496 4497 for subtask in &self.subtasks { 4498 task_mut.subtasks.insert(*subtask); 4499 } 4500 4501 for subtask in &self.subtasks { 4502 state.get_mut(*subtask)?.caller = Caller::Guest { 4503 thread: *thread, 4504 instance: *runtime_instance, 4505 }; 4506 } 4507 } 4508 Caller::Host { 4509 exit_tx, caller, .. 4510 } => { 4511 for subtask in &self.subtasks { 4512 state.get_mut(*subtask)?.caller = Caller::Host { 4513 tx: None, 4514 // Clone `exit_tx` to ensure that it is only dropped 4515 // once all transitive subtasks of the host call have 4516 // exited: 4517 exit_tx: exit_tx.clone(), 4518 host_future_present: false, 4519 call_post_return_automatically: true, 4520 caller: *caller, 4521 }; 4522 } 4523 } 4524 } 4525 4526 for subtask in self.subtasks { 4527 if state.get_mut(subtask)?.exited { 4528 Waitable::Guest(subtask).delete_from(state)?; 4529 } 4530 } 4531 4532 Ok(()) 4533 } 4534 4535 fn call_post_return_automatically(&self) -> bool { 4536 matches!( 4537 self.caller, 4538 Caller::Guest { .. } 4539 | Caller::Host { 4540 call_post_return_automatically: true, 4541 .. 4542 } 4543 ) 4544 } 4545 } 4546 4547 impl TableDebug for GuestTask { 4548 fn type_name() -> &'static str { 4549 "GuestTask" 4550 } 4551 } 4552 4553 /// Represents state common to all kinds of waitables. 4554 #[derive(Default)] 4555 struct WaitableCommon { 4556 /// The currently pending event for this waitable, if any. 4557 event: Option<Event>, 4558 /// The set to which this waitable belongs, if any. 4559 set: Option<TableId<WaitableSet>>, 4560 /// The handle with which the guest refers to this waitable, if any. 4561 handle: Option<u32>, 4562 } 4563 4564 /// Represents a Component Model Async `waitable`. 4565 #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] 4566 enum Waitable { 4567 /// A host task 4568 Host(TableId<HostTask>), 4569 /// A guest task 4570 Guest(TableId<GuestTask>), 4571 /// The read or write end of a stream or future 4572 Transmit(TableId<TransmitHandle>), 4573 } 4574 4575 impl Waitable { 4576 /// Retrieve the `Waitable` corresponding to the specified guest-visible 4577 /// handle. 4578 fn from_instance( 4579 state: Pin<&mut ComponentInstance>, 4580 caller_instance: RuntimeComponentInstanceIndex, 4581 waitable: u32, 4582 ) -> Result<Self> { 4583 use crate::runtime::vm::component::Waitable; 4584 4585 let (waitable, kind) = state.instance_states().0[caller_instance] 4586 .handle_table() 4587 .waitable_rep(waitable)?; 4588 4589 Ok(match kind { 4590 Waitable::Subtask { is_host: true } => Self::Host(TableId::new(waitable)), 4591 Waitable::Subtask { is_host: false } => Self::Guest(TableId::new(waitable)), 4592 Waitable::Stream | Waitable::Future => Self::Transmit(TableId::new(waitable)), 4593 }) 4594 } 4595 4596 /// Retrieve the host-visible identifier for this `Waitable`. 4597 fn rep(&self) -> u32 { 4598 match self { 4599 Self::Host(id) => id.rep(), 4600 Self::Guest(id) => id.rep(), 4601 Self::Transmit(id) => id.rep(), 4602 } 4603 } 4604 4605 /// Move this `Waitable` to the specified set (when `set` is `Some(_)`) or 4606 /// remove it from any set it may currently belong to (when `set` is 4607 /// `None`). 4608 fn join(&self, state: &mut ConcurrentState, set: Option<TableId<WaitableSet>>) -> Result<()> { 4609 log::trace!("waitable {self:?} join set {set:?}",); 4610 4611 let old = mem::replace(&mut self.common(state)?.set, set); 4612 4613 if let Some(old) = old { 4614 match *self { 4615 Waitable::Host(id) => state.remove_child(id, old), 4616 Waitable::Guest(id) => state.remove_child(id, old), 4617 Waitable::Transmit(id) => state.remove_child(id, old), 4618 }?; 4619 4620 state.get_mut(old)?.ready.remove(self); 4621 } 4622 4623 if let Some(set) = set { 4624 match *self { 4625 Waitable::Host(id) => state.add_child(id, set), 4626 Waitable::Guest(id) => state.add_child(id, set), 4627 Waitable::Transmit(id) => state.add_child(id, set), 4628 }?; 4629 4630 if self.common(state)?.event.is_some() { 4631 self.mark_ready(state)?; 4632 } 4633 } 4634 4635 Ok(()) 4636 } 4637 4638 /// Retrieve mutable access to the `WaitableCommon` for this `Waitable`. 4639 fn common<'a>(&self, state: &'a mut ConcurrentState) -> Result<&'a mut WaitableCommon> { 4640 Ok(match self { 4641 Self::Host(id) => &mut state.get_mut(*id)?.common, 4642 Self::Guest(id) => &mut state.get_mut(*id)?.common, 4643 Self::Transmit(id) => &mut state.get_mut(*id)?.common, 4644 }) 4645 } 4646 4647 /// Set or clear the pending event for this waitable and either deliver it 4648 /// to the first waiter, if any, or mark it as ready to be delivered to the 4649 /// next waiter that arrives. 4650 fn set_event(&self, state: &mut ConcurrentState, event: Option<Event>) -> Result<()> { 4651 log::trace!("set event for {self:?}: {event:?}"); 4652 self.common(state)?.event = event; 4653 self.mark_ready(state) 4654 } 4655 4656 /// Take the pending event from this waitable, leaving `None` in its place. 4657 fn take_event(&self, state: &mut ConcurrentState) -> Result<Option<Event>> { 4658 let common = self.common(state)?; 4659 let event = common.event.take(); 4660 if let Some(set) = self.common(state)?.set { 4661 state.get_mut(set)?.ready.remove(self); 4662 } 4663 4664 Ok(event) 4665 } 4666 4667 /// Deliver the current event for this waitable to the first waiter, if any, 4668 /// or else mark it as ready to be delivered to the next waiter that 4669 /// arrives. 4670 fn mark_ready(&self, state: &mut ConcurrentState) -> Result<()> { 4671 if let Some(set) = self.common(state)?.set { 4672 state.get_mut(set)?.ready.insert(*self); 4673 if let Some((thread, mode)) = state.get_mut(set)?.waiting.pop_first() { 4674 let wake_on_cancel = state.get_mut(thread.thread)?.wake_on_cancel.take(); 4675 assert!(wake_on_cancel.is_none() || wake_on_cancel == Some(set)); 4676 4677 let item = match mode { 4678 WaitMode::Fiber(fiber) => WorkItem::ResumeFiber(fiber), 4679 WaitMode::Callback(instance) => WorkItem::GuestCall(GuestCall { 4680 thread, 4681 kind: GuestCallKind::DeliverEvent { 4682 instance, 4683 set: Some(set), 4684 }, 4685 }), 4686 }; 4687 state.push_high_priority(item); 4688 } 4689 } 4690 Ok(()) 4691 } 4692 4693 /// Remove this waitable from the instance's rep table. 4694 fn delete_from(&self, state: &mut ConcurrentState) -> Result<()> { 4695 match self { 4696 Self::Host(task) => { 4697 log::trace!("delete host task {task:?}"); 4698 state.delete(*task)?; 4699 } 4700 Self::Guest(task) => { 4701 log::trace!("delete guest task {task:?}"); 4702 state.delete(*task)?.dispose(state, *task)?; 4703 } 4704 Self::Transmit(task) => { 4705 state.delete(*task)?; 4706 } 4707 } 4708 4709 Ok(()) 4710 } 4711 } 4712 4713 impl fmt::Debug for Waitable { 4714 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 4715 match self { 4716 Self::Host(id) => write!(f, "{id:?}"), 4717 Self::Guest(id) => write!(f, "{id:?}"), 4718 Self::Transmit(id) => write!(f, "{id:?}"), 4719 } 4720 } 4721 } 4722 4723 /// Represents a Component Model Async `waitable-set`. 4724 #[derive(Default)] 4725 struct WaitableSet { 4726 /// Which waitables in this set have pending events, if any. 4727 ready: BTreeSet<Waitable>, 4728 /// Which guest threads are currently waiting on this set, if any. 4729 waiting: BTreeMap<QualifiedThreadId, WaitMode>, 4730 } 4731 4732 impl TableDebug for WaitableSet { 4733 fn type_name() -> &'static str { 4734 "WaitableSet" 4735 } 4736 } 4737 4738 /// Type-erased closure to lower the parameters for a guest task. 4739 type RawLower = 4740 Box<dyn FnOnce(&mut dyn VMStore, &mut [MaybeUninit<ValRaw>]) -> Result<()> + Send + Sync>; 4741 4742 /// Type-erased closure to lift the result for a guest task. 4743 type RawLift = Box< 4744 dyn FnOnce(&mut dyn VMStore, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>> + Send + Sync, 4745 >; 4746 4747 /// Type erased result of a guest task which may be downcast to the expected 4748 /// type by a host caller (or simply ignored in the case of a guest caller; see 4749 /// `DummyResult`). 4750 type LiftedResult = Box<dyn Any + Send + Sync>; 4751 4752 /// Used to return a result from a `LiftFn` when the actual result has already 4753 /// been lowered to a guest task's stack and linear memory. 4754 struct DummyResult; 4755 4756 /// Represents the Component Model Async state of a (sub-)component instance. 4757 #[derive(Default)] 4758 pub struct ConcurrentInstanceState { 4759 /// Whether backpressure is set for this instance (enabled if >0) 4760 backpressure: u16, 4761 /// Whether this instance can be entered 4762 do_not_enter: bool, 4763 /// Pending calls for this instance which require `Self::backpressure` to be 4764 /// `true` and/or `Self::do_not_enter` to be false before they can proceed. 4765 pending: BTreeMap<QualifiedThreadId, GuestCallKind>, 4766 } 4767 4768 impl ConcurrentInstanceState { 4769 pub fn pending_is_empty(&self) -> bool { 4770 self.pending.is_empty() 4771 } 4772 } 4773 4774 /// Represents the Component Model Async state of a store. 4775 pub struct ConcurrentState { 4776 /// The currently running guest thread, if any. 4777 guest_thread: Option<QualifiedThreadId>, 4778 4779 /// The set of pending host and background tasks, if any. 4780 /// 4781 /// See `ComponentInstance::poll_until` for where we temporarily take this 4782 /// out, poll it, then put it back to avoid any mutable aliasing hazards. 4783 futures: AlwaysMut<Option<FuturesUnordered<HostTaskFuture>>>, 4784 /// The table of waitables, waitable sets, etc. 4785 table: AlwaysMut<ResourceTable>, 4786 /// The "high priority" work queue for this store's event loop. 4787 high_priority: Vec<WorkItem>, 4788 /// The "low priority" work queue for this store's event loop. 4789 low_priority: VecDeque<WorkItem>, 4790 /// A place to stash the reason a fiber is suspending so that the code which 4791 /// resumed it will know under what conditions the fiber should be resumed 4792 /// again. 4793 suspend_reason: Option<SuspendReason>, 4794 /// A cached fiber which is waiting for work to do. 4795 /// 4796 /// This helps us avoid creating a new fiber for each `GuestCall` work item. 4797 worker: Option<StoreFiber<'static>>, 4798 /// A place to stash the work item for which we're resuming a worker fiber. 4799 worker_item: Option<WorkerItem>, 4800 4801 /// Reference counts for all component error contexts 4802 /// 4803 /// NOTE: it is possible the global ref count to be *greater* than the sum of 4804 /// (sub)component ref counts as tracked by `error_context_tables`, for 4805 /// example when the host holds one or more references to error contexts. 4806 /// 4807 /// The key of this primary map is often referred to as the "rep" (i.e. host-side 4808 /// component-wide representation) of the index into concurrent state for a given 4809 /// stored `ErrorContext`. 4810 /// 4811 /// Stated another way, `TypeComponentGlobalErrorContextTableIndex` is essentially the same 4812 /// as a `TableId<ErrorContextState>`. 4813 global_error_context_ref_counts: 4814 BTreeMap<TypeComponentGlobalErrorContextTableIndex, GlobalErrorContextRefCount>, 4815 } 4816 4817 impl Default for ConcurrentState { 4818 fn default() -> Self { 4819 Self { 4820 guest_thread: None, 4821 table: AlwaysMut::new(ResourceTable::new()), 4822 futures: AlwaysMut::new(Some(FuturesUnordered::new())), 4823 high_priority: Vec::new(), 4824 low_priority: VecDeque::new(), 4825 suspend_reason: None, 4826 worker: None, 4827 worker_item: None, 4828 global_error_context_ref_counts: BTreeMap::new(), 4829 } 4830 } 4831 } 4832 4833 impl ConcurrentState { 4834 /// Take ownership of any fibers and futures owned by this object. 4835 /// 4836 /// This should be used when disposing of the `Store` containing this object 4837 /// in order to gracefully resolve any and all fibers using 4838 /// `StoreFiber::dispose`. This is necessary to avoid possible 4839 /// use-after-free bugs due to fibers which may still have access to the 4840 /// `Store`. 4841 /// 4842 /// Additionally, the futures collected with this function should be dropped 4843 /// within a `tls::set` call, which will ensure than any futures closing 4844 /// over an `&Accessor` will have access to the store when dropped, allowing 4845 /// e.g. `WithAccessor[AndValue]` instances to be disposed of without 4846 /// panicking. 4847 /// 4848 /// Note that this will leave the object in an inconsistent and unusable 4849 /// state, so it should only be used just prior to dropping it. 4850 pub(crate) fn take_fibers_and_futures( 4851 &mut self, 4852 fibers: &mut Vec<StoreFiber<'static>>, 4853 futures: &mut Vec<FuturesUnordered<HostTaskFuture>>, 4854 ) { 4855 for entry in self.table.get_mut().iter_mut() { 4856 if let Some(set) = entry.downcast_mut::<WaitableSet>() { 4857 for mode in mem::take(&mut set.waiting).into_values() { 4858 if let WaitMode::Fiber(fiber) = mode { 4859 fibers.push(fiber); 4860 } 4861 } 4862 } else if let Some(thread) = entry.downcast_mut::<GuestThread>() { 4863 if let GuestThreadState::Suspended(fiber) = 4864 mem::replace(&mut thread.state, GuestThreadState::Completed) 4865 { 4866 fibers.push(fiber); 4867 } 4868 } 4869 } 4870 4871 if let Some(fiber) = self.worker.take() { 4872 fibers.push(fiber); 4873 } 4874 4875 let mut handle_item = |item| match item { 4876 WorkItem::ResumeFiber(fiber) => { 4877 fibers.push(fiber); 4878 } 4879 WorkItem::PushFuture(future) => { 4880 self.futures 4881 .get_mut() 4882 .as_mut() 4883 .unwrap() 4884 .push(future.into_inner()); 4885 } 4886 _ => {} 4887 }; 4888 4889 for item in mem::take(&mut self.high_priority) { 4890 handle_item(item); 4891 } 4892 for item in mem::take(&mut self.low_priority) { 4893 handle_item(item); 4894 } 4895 4896 if let Some(them) = self.futures.get_mut().take() { 4897 futures.push(them); 4898 } 4899 } 4900 4901 /// Collect the next set of work items to run. This will be either all 4902 /// high-priority items, or a single low-priority item if there are no 4903 /// high-priority items. 4904 fn collect_work_items_to_run(&mut self) -> Vec<WorkItem> { 4905 let mut ready = mem::take(&mut self.high_priority); 4906 if ready.is_empty() { 4907 if let Some(item) = self.low_priority.pop_back() { 4908 ready.push(item); 4909 } 4910 } 4911 ready 4912 } 4913 4914 fn push<V: Send + Sync + 'static>( 4915 &mut self, 4916 value: V, 4917 ) -> Result<TableId<V>, ResourceTableError> { 4918 self.table.get_mut().push(value).map(TableId::from) 4919 } 4920 4921 fn get_mut<V: 'static>(&mut self, id: TableId<V>) -> Result<&mut V, ResourceTableError> { 4922 self.table.get_mut().get_mut(&Resource::from(id)) 4923 } 4924 4925 pub fn add_child<T: 'static, U: 'static>( 4926 &mut self, 4927 child: TableId<T>, 4928 parent: TableId<U>, 4929 ) -> Result<(), ResourceTableError> { 4930 self.table 4931 .get_mut() 4932 .add_child(Resource::from(child), Resource::from(parent)) 4933 } 4934 4935 pub fn remove_child<T: 'static, U: 'static>( 4936 &mut self, 4937 child: TableId<T>, 4938 parent: TableId<U>, 4939 ) -> Result<(), ResourceTableError> { 4940 self.table 4941 .get_mut() 4942 .remove_child(Resource::from(child), Resource::from(parent)) 4943 } 4944 4945 fn delete<V: 'static>(&mut self, id: TableId<V>) -> Result<V, ResourceTableError> { 4946 self.table.get_mut().delete(Resource::from(id)) 4947 } 4948 4949 fn push_future(&mut self, future: HostTaskFuture) { 4950 // Note that we can't directly push to `ConcurrentState::futures` here 4951 // since this may be called from a future that's being polled inside 4952 // `Self::poll_until`, which temporarily removes the `FuturesUnordered` 4953 // so it has exclusive access while polling it. Therefore, we push a 4954 // work item to the "high priority" queue, which will actually push to 4955 // `ConcurrentState::futures` later. 4956 self.push_high_priority(WorkItem::PushFuture(AlwaysMut::new(future))); 4957 } 4958 4959 fn push_high_priority(&mut self, item: WorkItem) { 4960 log::trace!("push high priority: {item:?}"); 4961 self.high_priority.push(item); 4962 } 4963 4964 fn push_low_priority(&mut self, item: WorkItem) { 4965 log::trace!("push low priority: {item:?}"); 4966 self.low_priority.push_front(item); 4967 } 4968 4969 fn push_work_item(&mut self, item: WorkItem, high_priority: bool) { 4970 if high_priority { 4971 self.push_high_priority(item); 4972 } else { 4973 self.push_low_priority(item); 4974 } 4975 } 4976 4977 /// Implements the `context.get` intrinsic. 4978 pub(crate) fn context_get(&mut self, slot: u32) -> Result<u32> { 4979 let thread = self.guest_thread.unwrap(); 4980 let val = self.get_mut(thread.thread)?.context[usize::try_from(slot).unwrap()]; 4981 log::trace!("context_get {thread:?} slot {slot} val {val:#x}"); 4982 Ok(val) 4983 } 4984 4985 /// Implements the `context.set` intrinsic. 4986 pub(crate) fn context_set(&mut self, slot: u32, val: u32) -> Result<()> { 4987 let thread = self.guest_thread.unwrap(); 4988 log::trace!("context_set {thread:?} slot {slot} val {val:#x}"); 4989 self.get_mut(thread.thread)?.context[usize::try_from(slot).unwrap()] = val; 4990 Ok(()) 4991 } 4992 4993 /// Returns whether there's a pending cancellation on the current guest thread, 4994 /// consuming the event if so. 4995 fn take_pending_cancellation(&mut self) -> bool { 4996 let thread = self.guest_thread.unwrap(); 4997 if let Some(event) = self.get_mut(thread.task).unwrap().event.take() { 4998 assert!(matches!(event, Event::Cancelled)); 4999 true 5000 } else { 5001 false 5002 } 5003 } 5004 5005 fn check_blocking_for(&mut self, task: TableId<GuestTask>) -> Result<()> { 5006 if self.may_block(task) { 5007 Ok(()) 5008 } else { 5009 Err(Trap::CannotBlockSyncTask.into()) 5010 } 5011 } 5012 5013 fn may_block(&mut self, task: TableId<GuestTask>) -> bool { 5014 let task = self.get_mut(task).unwrap(); 5015 task.async_function || task.returned_or_cancelled() 5016 } 5017 } 5018 5019 /// Provide a type hint to compiler about the shape of a parameter lower 5020 /// closure. 5021 fn for_any_lower< 5022 F: FnOnce(&mut dyn VMStore, &mut [MaybeUninit<ValRaw>]) -> Result<()> + Send + Sync, 5023 >( 5024 fun: F, 5025 ) -> F { 5026 fun 5027 } 5028 5029 /// Provide a type hint to compiler about the shape of a result lift closure. 5030 fn for_any_lift< 5031 F: FnOnce(&mut dyn VMStore, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>> + Send + Sync, 5032 >( 5033 fun: F, 5034 ) -> F { 5035 fun 5036 } 5037 5038 /// Wrap the specified future in a `poll_fn` which asserts that the future is 5039 /// only polled from the event loop of the specified `Store`. 5040 /// 5041 /// See `StoreContextMut::run_concurrent` for details. 5042 fn checked<F: Future + Send + 'static>( 5043 id: StoreId, 5044 fut: F, 5045 ) -> impl Future<Output = F::Output> + Send + 'static { 5046 async move { 5047 let mut fut = pin!(fut); 5048 future::poll_fn(move |cx| { 5049 let message = "\ 5050 `Future`s which depend on asynchronous component tasks, streams, or \ 5051 futures to complete may only be polled from the event loop of the \ 5052 store to which they belong. Please use \ 5053 `StoreContextMut::{run_concurrent,spawn}` to poll or await them.\ 5054 "; 5055 tls::try_get(|store| { 5056 let matched = match store { 5057 tls::TryGet::Some(store) => store.id() == id, 5058 tls::TryGet::Taken | tls::TryGet::None => false, 5059 }; 5060 5061 if !matched { 5062 panic!("{message}") 5063 } 5064 }); 5065 fut.as_mut().poll(cx) 5066 }) 5067 .await 5068 } 5069 } 5070 5071 /// Assert that `StoreContextMut::run_concurrent` has not been called from 5072 /// within an store's event loop. 5073 fn check_recursive_run() { 5074 tls::try_get(|store| { 5075 if !matches!(store, tls::TryGet::None) { 5076 panic!("Recursive `StoreContextMut::run_concurrent` calls not supported") 5077 } 5078 }); 5079 } 5080 5081 fn unpack_callback_code(code: u32) -> (u32, u32) { 5082 (code & 0xF, code >> 4) 5083 } 5084 5085 /// Helper struct for packaging parameters to be passed to 5086 /// `ComponentInstance::waitable_check` for calls to `waitable-set.wait` or 5087 /// `waitable-set.poll`. 5088 struct WaitableCheckParams { 5089 set: TableId<WaitableSet>, 5090 options: OptionsIndex, 5091 payload: u32, 5092 } 5093 5094 /// Indicates whether `ComponentInstance::waitable_check` is being called for 5095 /// `waitable-set.wait` or `waitable-set.poll`. 5096 enum WaitableCheck { 5097 Wait, 5098 Poll, 5099 } 5100 5101 /// Represents a guest task called from the host, prepared using `prepare_call`. 5102 pub(crate) struct PreparedCall<R> { 5103 /// The guest export to be called 5104 handle: Func, 5105 /// The guest thread created by `prepare_call` 5106 thread: QualifiedThreadId, 5107 /// The number of lowered core Wasm parameters to pass to the call. 5108 param_count: usize, 5109 /// The `oneshot::Receiver` to which the result of the call will be 5110 /// delivered when it is available. 5111 rx: oneshot::Receiver<LiftedResult>, 5112 /// The `oneshot::Receiver` which will resolve when the task -- and any 5113 /// transitive subtasks -- have all exited. 5114 exit_rx: oneshot::Receiver<()>, 5115 _phantom: PhantomData<R>, 5116 } 5117 5118 impl<R> PreparedCall<R> { 5119 /// Get a copy of the `TaskId` for this `PreparedCall`. 5120 pub(crate) fn task_id(&self) -> TaskId { 5121 TaskId { 5122 task: self.thread.task, 5123 } 5124 } 5125 } 5126 5127 /// Represents a task created by `prepare_call`. 5128 pub(crate) struct TaskId { 5129 task: TableId<GuestTask>, 5130 } 5131 5132 impl TaskId { 5133 /// The host future for an async task was dropped. If the parameters have not been lowered yet, 5134 /// it is no longer valid to do so, as the lowering closure would see a dangling pointer. In this case, 5135 /// we delete the task eagerly. Otherwise, there may be running threads, or ones that are suspended 5136 /// and can be resumed by other tasks for this component, so we mark the future as dropped 5137 /// and delete the task when all threads are done. 5138 pub(crate) fn host_future_dropped<T>(&self, store: StoreContextMut<T>) -> Result<()> { 5139 let task = store.0.concurrent_state_mut().get_mut(self.task)?; 5140 if !task.already_lowered_parameters() { 5141 Waitable::Guest(self.task).delete_from(store.0.concurrent_state_mut())? 5142 } else { 5143 task.host_future_state = HostFutureState::Dropped; 5144 if task.ready_to_delete() { 5145 Waitable::Guest(self.task).delete_from(store.0.concurrent_state_mut())? 5146 } 5147 } 5148 Ok(()) 5149 } 5150 } 5151 5152 /// Prepare a call to the specified exported Wasm function, providing functions 5153 /// for lowering the parameters and lifting the result. 5154 /// 5155 /// To enqueue the returned `PreparedCall` in the `ComponentInstance`'s event 5156 /// loop, use `queue_call`. 5157 pub(crate) fn prepare_call<T, R>( 5158 mut store: StoreContextMut<T>, 5159 handle: Func, 5160 param_count: usize, 5161 host_future_present: bool, 5162 call_post_return_automatically: bool, 5163 lower_params: impl FnOnce(Func, StoreContextMut<T>, &mut [MaybeUninit<ValRaw>]) -> Result<()> 5164 + Send 5165 + Sync 5166 + 'static, 5167 lift_result: impl FnOnce(Func, &mut StoreOpaque, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>> 5168 + Send 5169 + Sync 5170 + 'static, 5171 ) -> Result<PreparedCall<R>> { 5172 let (options, _flags, ty, raw_options) = handle.abi_info(store.0); 5173 5174 let instance = handle.instance().id().get(store.0); 5175 let options = &instance.component().env_component().options[options]; 5176 let ty = &instance.component().types()[ty]; 5177 let async_function = ty.async_; 5178 let task_return_type = ty.results; 5179 let component_instance = raw_options.instance; 5180 let callback = options.callback.map(|i| instance.runtime_callback(i)); 5181 let memory = options 5182 .memory() 5183 .map(|i| instance.runtime_memory(i)) 5184 .map(SendSyncPtr::new); 5185 let string_encoding = options.string_encoding; 5186 let token = StoreToken::new(store.as_context_mut()); 5187 let state = store.0.concurrent_state_mut(); 5188 5189 let (tx, rx) = oneshot::channel(); 5190 let (exit_tx, exit_rx) = oneshot::channel(); 5191 5192 let caller = state.guest_thread; 5193 let mut task = GuestTask::new( 5194 state, 5195 Box::new(for_any_lower(move |store, params| { 5196 lower_params(handle, token.as_context_mut(store), params) 5197 })), 5198 LiftResult { 5199 lift: Box::new(for_any_lift(move |store, result| { 5200 lift_result(handle, store, result) 5201 })), 5202 ty: task_return_type, 5203 memory, 5204 string_encoding, 5205 }, 5206 Caller::Host { 5207 tx: Some(tx), 5208 exit_tx: Arc::new(exit_tx), 5209 host_future_present, 5210 call_post_return_automatically, 5211 caller, 5212 }, 5213 callback.map(|callback| { 5214 let callback = SendSyncPtr::new(callback); 5215 let instance = handle.instance(); 5216 Box::new(move |store: &mut dyn VMStore, event, handle| { 5217 let store = token.as_context_mut(store); 5218 // SAFETY: Per the contract of `prepare_call`, the callback 5219 // will remain valid at least as long is this task exists. 5220 unsafe { instance.call_callback(store, callback, event, handle) } 5221 }) as CallbackFn 5222 }), 5223 RuntimeInstance { 5224 instance: handle.instance().id().instance(), 5225 index: component_instance, 5226 }, 5227 async_function, 5228 )?; 5229 task.function_index = Some(handle.index()); 5230 5231 let task = state.push(task)?; 5232 let thread = state.push(GuestThread::new_implicit(task))?; 5233 state.get_mut(task)?.threads.insert(thread); 5234 5235 if !store.0.may_enter_task(task) { 5236 bail!(crate::Trap::CannotEnterComponent); 5237 } 5238 5239 Ok(PreparedCall { 5240 handle, 5241 thread: QualifiedThreadId { task, thread }, 5242 param_count, 5243 rx, 5244 exit_rx, 5245 _phantom: PhantomData, 5246 }) 5247 } 5248 5249 /// Queue a call previously prepared using `prepare_call` to be run as part of 5250 /// the associated `ComponentInstance`'s event loop. 5251 /// 5252 /// The returned future will resolve to the result once it is available, but 5253 /// must only be polled via the instance's event loop. See 5254 /// `StoreContextMut::run_concurrent` for details. 5255 pub(crate) fn queue_call<T: 'static, R: Send + 'static>( 5256 mut store: StoreContextMut<T>, 5257 prepared: PreparedCall<R>, 5258 ) -> Result<impl Future<Output = Result<(R, oneshot::Receiver<()>)>> + Send + 'static + use<T, R>> { 5259 let PreparedCall { 5260 handle, 5261 thread, 5262 param_count, 5263 rx, 5264 exit_rx, 5265 .. 5266 } = prepared; 5267 5268 queue_call0(store.as_context_mut(), handle, thread, param_count)?; 5269 5270 Ok(checked( 5271 store.0.id(), 5272 rx.map(move |result| { 5273 result 5274 .map(|v| (*v.downcast().unwrap(), exit_rx)) 5275 .map_err(crate::Error::from) 5276 }), 5277 )) 5278 } 5279 5280 /// Queue a call previously prepared using `prepare_call` to be run as part of 5281 /// the associated `ComponentInstance`'s event loop. 5282 fn queue_call0<T: 'static>( 5283 store: StoreContextMut<T>, 5284 handle: Func, 5285 guest_thread: QualifiedThreadId, 5286 param_count: usize, 5287 ) -> Result<()> { 5288 let (_options, _, _ty, raw_options) = handle.abi_info(store.0); 5289 let is_concurrent = raw_options.async_; 5290 let callback = raw_options.callback; 5291 let instance = handle.instance(); 5292 let callee = handle.lifted_core_func(store.0); 5293 let post_return = handle.post_return_core_func(store.0); 5294 let callback = callback.map(|i| { 5295 let instance = instance.id().get(store.0); 5296 SendSyncPtr::new(instance.runtime_callback(i)) 5297 }); 5298 5299 log::trace!("queueing call {guest_thread:?}"); 5300 5301 // SAFETY: `callee`, `callback`, and `post_return` are valid pointers 5302 // (with signatures appropriate for this call) and will remain valid as 5303 // long as this instance is valid. 5304 unsafe { 5305 instance.queue_call( 5306 store, 5307 guest_thread, 5308 SendSyncPtr::new(callee), 5309 param_count, 5310 1, 5311 is_concurrent, 5312 callback, 5313 post_return.map(SendSyncPtr::new), 5314 ) 5315 } 5316 } 5317