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