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