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