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