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