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 /// This will run `fun` as part of this instance's event loop until it 1038 /// yields a result. `fun` is provided an [`Accessor`], which provides 1039 /// controlled access to the `Store` and its data. 1040 /// 1041 /// This function can be used to invoke [`Func::call_concurrent`] for 1042 /// example within the async closure provided here. 1043 /// 1044 /// # Example 1045 /// 1046 /// ``` 1047 /// # use { 1048 /// # anyhow::{Result}, 1049 /// # wasmtime::{ 1050 /// # component::{ Component, Linker, Resource, ResourceTable}, 1051 /// # Config, Engine, Store 1052 /// # }, 1053 /// # }; 1054 /// # 1055 /// # struct MyResource(u32); 1056 /// # struct Ctx { table: ResourceTable } 1057 /// # 1058 /// # async fn foo() -> Result<()> { 1059 /// # let mut config = Config::new(); 1060 /// # let engine = Engine::new(&config)?; 1061 /// # let mut store = Store::new(&engine, Ctx { table: ResourceTable::new() }); 1062 /// # let mut linker = Linker::new(&engine); 1063 /// # let component = Component::new(&engine, "")?; 1064 /// # let instance = linker.instantiate_async(&mut store, &component).await?; 1065 /// # let foo = instance.get_typed_func::<(Resource<MyResource>,), (Resource<MyResource>,)>(&mut store, "foo")?; 1066 /// # let bar = instance.get_typed_func::<(u32,), ()>(&mut store, "bar")?; 1067 /// instance.run_concurrent(&mut store, async |accessor| -> wasmtime::Result<_> { 1068 /// let resource = accessor.with(|mut access| access.get().table.push(MyResource(42)))?; 1069 /// let (another_resource,) = foo.call_concurrent(accessor, (resource,)).await?.0; 1070 /// let value = accessor.with(|mut access| access.get().table.delete(another_resource))?; 1071 /// bar.call_concurrent(accessor, (value.0,)).await?; 1072 /// Ok(()) 1073 /// }).await??; 1074 /// # Ok(()) 1075 /// # } 1076 /// ``` 1077 pub async fn run_concurrent<T, R>( 1078 self, 1079 store: impl AsContextMut<Data = T>, 1080 fun: impl AsyncFnOnce(&Accessor<T>) -> R, 1081 ) -> Result<R> 1082 where 1083 T: Send + 'static, 1084 { 1085 self.do_run_concurrent(store, fun, false).await 1086 } 1087 1088 pub(super) async fn run_concurrent_trap_on_idle<T, R>( 1089 self, 1090 store: impl AsContextMut<Data = T>, 1091 fun: impl AsyncFnOnce(&Accessor<T>) -> R, 1092 ) -> Result<R> 1093 where 1094 T: Send + 'static, 1095 { 1096 self.do_run_concurrent(store, fun, true).await 1097 } 1098 1099 async fn do_run_concurrent<T, R>( 1100 self, 1101 mut store: impl AsContextMut<Data = T>, 1102 fun: impl AsyncFnOnce(&Accessor<T>) -> R, 1103 trap_on_idle: bool, 1104 ) -> Result<R> 1105 where 1106 T: Send + 'static, 1107 { 1108 check_recursive_run(); 1109 let mut store = store.as_context_mut(); 1110 let token = StoreToken::new(store.as_context_mut()); 1111 1112 struct Dropper<'a, T: 'static, V> { 1113 store: StoreContextMut<'a, T>, 1114 value: ManuallyDrop<V>, 1115 } 1116 1117 impl<'a, T, V> Drop for Dropper<'a, T, V> { 1118 fn drop(&mut self) { 1119 tls::set(self.store.0, || { 1120 // SAFETY: Here we drop the value without moving it for the 1121 // first and only time -- per the contract for `Drop::drop`, 1122 // this code won't run again, and the `value` field will no 1123 // longer be accessible. 1124 unsafe { ManuallyDrop::drop(&mut self.value) } 1125 }); 1126 } 1127 } 1128 1129 let accessor = &Accessor::new(token, Some(self)); 1130 let dropper = &mut Dropper { 1131 store, 1132 value: ManuallyDrop::new(fun(accessor)), 1133 }; 1134 // SAFETY: We never move `dropper` nor its `value` field. 1135 let future = unsafe { Pin::new_unchecked(dropper.value.deref_mut()) }; 1136 1137 self.poll_until(dropper.store.as_context_mut(), future, trap_on_idle) 1138 .await 1139 } 1140 1141 /// Spawn a background task to run as part of this instance's event loop. 1142 /// 1143 /// The task will receive an `&Accessor<U>` and run concurrently with 1144 /// any other tasks in progress for the instance. 1145 /// 1146 /// Note that the task will only make progress if and when the event loop 1147 /// for this instance is run. 1148 /// 1149 /// The returned [`SpawnHandle`] may be used to cancel the task. 1150 pub fn spawn<U: 'static>( 1151 self, 1152 mut store: impl AsContextMut<Data = U>, 1153 task: impl AccessorTask<U, HasSelf<U>, Result<()>>, 1154 ) -> JoinHandle { 1155 let mut store = store.as_context_mut(); 1156 let accessor = Accessor::new(StoreToken::new(store.as_context_mut()), Some(self)); 1157 self.spawn_with_accessor(store, accessor, task) 1158 } 1159 1160 /// Internal implementation of `spawn` functions where a `store` is 1161 /// available along with an `Accessor`. 1162 fn spawn_with_accessor<T, D>( 1163 self, 1164 mut store: StoreContextMut<T>, 1165 accessor: Accessor<T, D>, 1166 task: impl AccessorTask<T, D, Result<()>>, 1167 ) -> JoinHandle 1168 where 1169 T: 'static, 1170 D: HasData + ?Sized, 1171 { 1172 let store = store.as_context_mut(); 1173 1174 // Create an "abortable future" here where internally the future will 1175 // hook calls to poll and possibly spawn more background tasks on each 1176 // iteration. 1177 let (handle, future) = JoinHandle::run(async move { task.run(&accessor).await }); 1178 self.concurrent_state_mut(store.0) 1179 .push_future(Box::pin(async move { future.await.unwrap_or(Ok(())) })); 1180 1181 handle 1182 } 1183 1184 /// Run this instance's event loop. 1185 /// 1186 /// The returned future will resolve when the specified future completes. 1187 async fn poll_until<T, R>( 1188 self, 1189 mut store: StoreContextMut<'_, T>, 1190 mut future: Pin<&mut impl Future<Output = R>>, 1191 trap_on_idle: bool, 1192 ) -> Result<R> 1193 where 1194 T: Send + 'static, 1195 { 1196 struct Reset<'a, T: 'static> { 1197 store: StoreContextMut<'a, T>, 1198 instance: Instance, 1199 futures: Option<FuturesUnordered<HostTaskFuture>>, 1200 } 1201 1202 impl<'a, T> Drop for Reset<'a, T> { 1203 fn drop(&mut self) { 1204 if let Some(futures) = self.futures.take() { 1205 *self 1206 .instance 1207 .concurrent_state_mut(self.store.0) 1208 .futures 1209 .get_mut() = Some(futures); 1210 } 1211 } 1212 } 1213 1214 loop { 1215 // Take `ConcurrentState::futures` out of the instance so we can 1216 // poll it while also safely giving any of the futures inside access 1217 // to `self`. 1218 let futures = self.concurrent_state_mut(store.0).futures.get_mut().take(); 1219 let mut reset = Reset { 1220 store: store.as_context_mut(), 1221 instance: self, 1222 futures, 1223 }; 1224 let mut next = pin!(reset.futures.as_mut().unwrap().next()); 1225 1226 let result = future::poll_fn(|cx| { 1227 // First, poll the future we were passed as an argument and 1228 // return immediately if it's ready. 1229 if let Poll::Ready(value) = self.set_tls(reset.store.0, || future.as_mut().poll(cx)) 1230 { 1231 return Poll::Ready(Ok(Either::Left(value))); 1232 } 1233 1234 // Next, poll `ConcurrentState::futures` (which includes any 1235 // pending host tasks and/or background tasks), returning 1236 // immediately if one of them fails. 1237 let next = match self.set_tls(reset.store.0, || next.as_mut().poll(cx)) { 1238 Poll::Ready(Some(output)) => { 1239 match output { 1240 Err(e) => return Poll::Ready(Err(e)), 1241 Ok(()) => {} 1242 } 1243 Poll::Ready(true) 1244 } 1245 Poll::Ready(None) => Poll::Ready(false), 1246 Poll::Pending => Poll::Pending, 1247 }; 1248 1249 let mut instance = self.id().get_mut(reset.store.0); 1250 1251 // Next, check the "high priority" work queue and return 1252 // immediately if it has at least one item. 1253 let state = instance.as_mut().concurrent_state_mut(); 1254 let ready = mem::take(&mut state.high_priority); 1255 let ready = if ready.is_empty() { 1256 // Next, check the "low priority" work queue and return 1257 // immediately if it has at least one item. 1258 let ready = mem::take(&mut state.low_priority); 1259 if ready.is_empty() { 1260 return match next { 1261 Poll::Ready(true) => { 1262 // In this case, one of the futures in 1263 // `ConcurrentState::futures` completed 1264 // successfully, so we return now and continue 1265 // the outer loop in case there is another one 1266 // ready to complete. 1267 Poll::Ready(Ok(Either::Right(Vec::new()))) 1268 } 1269 Poll::Ready(false) => { 1270 // Poll the future we were passed one last time 1271 // in case one of `ConcurrentState::futures` had 1272 // the side effect of unblocking it. 1273 if let Poll::Ready(value) = 1274 self.set_tls(reset.store.0, || future.as_mut().poll(cx)) 1275 { 1276 Poll::Ready(Ok(Either::Left(value))) 1277 } else { 1278 // In this case, there are no more pending 1279 // futures in `ConcurrentState::futures`, 1280 // there are no remaining work items, _and_ 1281 // the future we were passed as an argument 1282 // still hasn't completed. 1283 if trap_on_idle { 1284 // `trap_on_idle` is true, so we exit 1285 // immediately. 1286 Poll::Ready(Err(anyhow!(crate::Trap::AsyncDeadlock))) 1287 } else { 1288 // `trap_on_idle` is false, so we assume 1289 // that future will wake up and give us 1290 // more work to do when it's ready to. 1291 Poll::Pending 1292 } 1293 } 1294 } 1295 // There is at least one pending future in 1296 // `ConcurrentState::futures` and we have nothing 1297 // else to do but wait for now, so we return 1298 // `Pending`. 1299 Poll::Pending => Poll::Pending, 1300 }; 1301 } else { 1302 ready 1303 } 1304 } else { 1305 ready 1306 }; 1307 1308 Poll::Ready(Ok(Either::Right(ready))) 1309 }) 1310 .await; 1311 1312 // Put the `ConcurrentState::futures` back into the instance before 1313 // we return or handle any work items since one or more of those 1314 // items might append more futures. 1315 drop(reset); 1316 1317 match result? { 1318 // The future we were passed as an argument completed, so we 1319 // return the result. 1320 Either::Left(value) => break Ok(value), 1321 // The future we were passed has not yet completed, so handle 1322 // any work items and then loop again. 1323 Either::Right(ready) => { 1324 struct Dispose<'a, T: 'static, I: Iterator<Item = WorkItem>> { 1325 store: StoreContextMut<'a, T>, 1326 ready: I, 1327 } 1328 1329 impl<'a, T, I: Iterator<Item = WorkItem>> Drop for Dispose<'a, T, I> { 1330 fn drop(&mut self) { 1331 while let Some(item) = self.ready.next() { 1332 match item { 1333 WorkItem::ResumeFiber(mut fiber) => fiber.dispose(self.store.0), 1334 WorkItem::PushFuture(future) => { 1335 tls::set(self.store.0, move || drop(future)) 1336 } 1337 _ => {} 1338 } 1339 } 1340 } 1341 } 1342 1343 let mut dispose = Dispose { 1344 store: store.as_context_mut(), 1345 ready: ready.into_iter(), 1346 }; 1347 1348 while let Some(item) = dispose.ready.next() { 1349 self.handle_work_item(dispose.store.as_context_mut(), item) 1350 .await?; 1351 } 1352 } 1353 } 1354 } 1355 } 1356 1357 /// Handle the specified work item, possibly resuming a fiber if applicable. 1358 async fn handle_work_item<T: Send>( 1359 self, 1360 store: StoreContextMut<'_, T>, 1361 item: WorkItem, 1362 ) -> Result<()> { 1363 log::trace!("handle work item {item:?}"); 1364 match item { 1365 WorkItem::PushFuture(future) => { 1366 self.concurrent_state_mut(store.0) 1367 .futures 1368 .get_mut() 1369 .as_mut() 1370 .unwrap() 1371 .push(future.into_inner()); 1372 } 1373 WorkItem::ResumeFiber(fiber) => { 1374 self.resume_fiber(store.0, fiber).await?; 1375 } 1376 WorkItem::GuestCall(call) => { 1377 let state = self.concurrent_state_mut(store.0); 1378 if call.is_ready(state)? { 1379 self.run_on_worker(store, WorkerItem::GuestCall(call)) 1380 .await?; 1381 } else { 1382 let task = state.get_mut(call.task)?; 1383 if !task.starting_sent { 1384 task.starting_sent = true; 1385 if let GuestCallKind::Start(_) = &call.kind { 1386 Waitable::Guest(call.task).set_event( 1387 state, 1388 Some(Event::Subtask { 1389 status: Status::Starting, 1390 }), 1391 )?; 1392 } 1393 } 1394 1395 let runtime_instance = state.get_mut(call.task)?.instance; 1396 state 1397 .instance_state(runtime_instance) 1398 .pending 1399 .insert(call.task, call.kind); 1400 } 1401 } 1402 WorkItem::Poll(params) => { 1403 let state = self.concurrent_state_mut(store.0); 1404 if state.get_mut(params.task)?.event.is_some() 1405 || !state.get_mut(params.set)?.ready.is_empty() 1406 { 1407 // There's at least one event immediately available; deliver 1408 // it to the guest ASAP. 1409 state.push_high_priority(WorkItem::GuestCall(GuestCall { 1410 task: params.task, 1411 kind: GuestCallKind::DeliverEvent { 1412 set: Some(params.set), 1413 }, 1414 })); 1415 } else { 1416 // There are no events immediately available; deliver 1417 // `Event::None` to the guest. 1418 state.get_mut(params.task)?.event = Some(Event::None); 1419 state.push_high_priority(WorkItem::GuestCall(GuestCall { 1420 task: params.task, 1421 kind: GuestCallKind::DeliverEvent { 1422 set: Some(params.set), 1423 }, 1424 })); 1425 } 1426 } 1427 WorkItem::WorkerFunction(fun) => { 1428 self.run_on_worker(store, WorkerItem::Function(fun)).await?; 1429 } 1430 } 1431 1432 Ok(()) 1433 } 1434 1435 /// Resume the specified fiber, giving it exclusive access to the specified 1436 /// store. 1437 async fn resume_fiber(self, store: &mut StoreOpaque, fiber: StoreFiber<'static>) -> Result<()> { 1438 let old_task = self.concurrent_state_mut(store).guest_task; 1439 log::trace!("resume_fiber: save current task {old_task:?}"); 1440 1441 let fiber = fiber::resolve_or_release(store, fiber).await?; 1442 1443 let state = self.concurrent_state_mut(store); 1444 1445 state.guest_task = old_task; 1446 log::trace!("resume_fiber: restore current task {old_task:?}"); 1447 1448 if let Some(mut fiber) = fiber { 1449 // See the `SuspendReason` documentation for what each case means. 1450 match state.suspend_reason.take().unwrap() { 1451 SuspendReason::NeedWork => { 1452 if state.worker.is_none() { 1453 state.worker = Some(fiber); 1454 } else { 1455 fiber.dispose(store); 1456 } 1457 } 1458 SuspendReason::Yielding { .. } => { 1459 state.push_low_priority(WorkItem::ResumeFiber(fiber)); 1460 } 1461 SuspendReason::Waiting { set, task } => { 1462 let old = state 1463 .get_mut(set)? 1464 .waiting 1465 .insert(task, WaitMode::Fiber(fiber)); 1466 assert!(old.is_none()); 1467 } 1468 } 1469 } 1470 1471 Ok(()) 1472 } 1473 1474 /// Execute the specified guest call on a worker fiber. 1475 async fn run_on_worker<T: Send>( 1476 self, 1477 store: StoreContextMut<'_, T>, 1478 item: WorkerItem, 1479 ) -> Result<()> { 1480 let worker = if let Some(fiber) = self.concurrent_state_mut(store.0).worker.take() { 1481 fiber 1482 } else { 1483 fiber::make_fiber(store.0, move |store| { 1484 loop { 1485 match self.concurrent_state_mut(store).worker_item.take().unwrap() { 1486 WorkerItem::GuestCall(call) => self.handle_guest_call(store, call)?, 1487 WorkerItem::Function(fun) => fun.into_inner()(store, self)?, 1488 } 1489 1490 self.suspend(store, SuspendReason::NeedWork)?; 1491 } 1492 })? 1493 }; 1494 1495 let worker_item = &mut self.concurrent_state_mut(store.0).worker_item; 1496 assert!(worker_item.is_none()); 1497 *worker_item = Some(item); 1498 1499 self.resume_fiber(store.0, worker).await 1500 } 1501 1502 /// Execute the specified guest call. 1503 fn handle_guest_call(self, store: &mut dyn VMStore, call: GuestCall) -> Result<()> { 1504 match call.kind { 1505 GuestCallKind::DeliverEvent { set } => { 1506 let (event, waitable) = self 1507 .id() 1508 .get_mut(store) 1509 .get_event(call.task, set, true)? 1510 .unwrap(); 1511 let state = self.concurrent_state_mut(store); 1512 let task = state.get_mut(call.task)?; 1513 let runtime_instance = task.instance; 1514 let handle = waitable.map(|(_, v)| v).unwrap_or(0); 1515 1516 log::trace!( 1517 "use callback to deliver event {event:?} to {:?} for {waitable:?}", 1518 call.task, 1519 ); 1520 1521 let old_task = state.guest_task.replace(call.task); 1522 log::trace!( 1523 "GuestCallKind::DeliverEvent: replaced {old_task:?} with {:?} as current task", 1524 call.task 1525 ); 1526 1527 self.maybe_push_call_context(store.store_opaque_mut(), call.task)?; 1528 1529 let state = self.concurrent_state_mut(store); 1530 state.enter_instance(runtime_instance); 1531 1532 let callback = state.get_mut(call.task)?.callback.take().unwrap(); 1533 1534 let code = callback(store, self, runtime_instance, event, handle)?; 1535 1536 let state = self.concurrent_state_mut(store); 1537 1538 state.get_mut(call.task)?.callback = Some(callback); 1539 1540 state.exit_instance(runtime_instance)?; 1541 1542 self.maybe_pop_call_context(store.store_opaque_mut(), call.task)?; 1543 1544 self.id().get_mut(store).handle_callback_code( 1545 call.task, 1546 runtime_instance, 1547 code, 1548 false, 1549 )?; 1550 1551 self.concurrent_state_mut(store).guest_task = old_task; 1552 log::trace!("GuestCallKind::DeliverEvent: restored {old_task:?} as current task"); 1553 } 1554 GuestCallKind::Start(fun) => { 1555 fun(store, self)?; 1556 } 1557 } 1558 1559 Ok(()) 1560 } 1561 1562 /// Suspend the current fiber, storing the reason in 1563 /// `ConcurrentState::suspend_reason` to indicate the conditions under which 1564 /// it should be resumed. 1565 /// 1566 /// See the `SuspendReason` documentation for details. 1567 fn suspend(self, store: &mut dyn VMStore, reason: SuspendReason) -> Result<()> { 1568 log::trace!("suspend fiber: {reason:?}"); 1569 1570 // If we're yielding or waiting on behalf of a guest task, we'll need to 1571 // pop the call context which manages resource borrows before suspending 1572 // and then push it again once we've resumed. 1573 let task = match &reason { 1574 SuspendReason::Yielding { task } | SuspendReason::Waiting { task, .. } => Some(*task), 1575 SuspendReason::NeedWork => None, 1576 }; 1577 1578 let old_guest_task = if let Some(task) = task { 1579 self.maybe_pop_call_context(store, task)?; 1580 self.concurrent_state_mut(store).guest_task 1581 } else { 1582 None 1583 }; 1584 1585 let suspend_reason = &mut self.concurrent_state_mut(store).suspend_reason; 1586 assert!(suspend_reason.is_none()); 1587 *suspend_reason = Some(reason); 1588 1589 store.with_blocking(|_, cx| cx.suspend(StoreFiberYield::ReleaseStore))?; 1590 1591 if let Some(task) = task { 1592 self.concurrent_state_mut(store).guest_task = old_guest_task; 1593 self.maybe_push_call_context(store, task)?; 1594 } 1595 1596 Ok(()) 1597 } 1598 1599 /// Push the call context for managing resource borrows for the specified 1600 /// guest task if it has not yet either returned a result or cancelled 1601 /// itself. 1602 fn maybe_push_call_context( 1603 self, 1604 store: &mut StoreOpaque, 1605 guest_task: TableId<GuestTask>, 1606 ) -> Result<()> { 1607 let task = self.concurrent_state_mut(store).get_mut(guest_task)?; 1608 if task.lift_result.is_some() { 1609 log::trace!("push call context for {guest_task:?}"); 1610 let call_context = task.call_context.take().unwrap(); 1611 store.component_resource_state().0.push(call_context); 1612 } 1613 Ok(()) 1614 } 1615 1616 /// Pop the call context for managing resource borrows for the specified 1617 /// guest task if it has not yet either returned a result or cancelled 1618 /// itself. 1619 fn maybe_pop_call_context( 1620 self, 1621 store: &mut StoreOpaque, 1622 guest_task: TableId<GuestTask>, 1623 ) -> Result<()> { 1624 if self 1625 .concurrent_state_mut(store) 1626 .get_mut(guest_task)? 1627 .lift_result 1628 .is_some() 1629 { 1630 log::trace!("pop call context for {guest_task:?}"); 1631 let call_context = Some(store.component_resource_state().0.pop().unwrap()); 1632 self.concurrent_state_mut(store) 1633 .get_mut(guest_task)? 1634 .call_context = call_context; 1635 } 1636 Ok(()) 1637 } 1638 1639 /// Add the specified guest call to the "high priority" work item queue, to 1640 /// be started as soon as backpressure and/or reentrance rules allow. 1641 /// 1642 /// SAFETY: The raw pointer arguments must be valid references to guest 1643 /// functions (with the appropriate signatures) when the closures queued by 1644 /// this function are called. 1645 unsafe fn queue_call<T: 'static>( 1646 self, 1647 mut store: StoreContextMut<T>, 1648 guest_task: TableId<GuestTask>, 1649 callee: SendSyncPtr<VMFuncRef>, 1650 param_count: usize, 1651 result_count: usize, 1652 flags: Option<InstanceFlags>, 1653 async_: bool, 1654 callback: Option<SendSyncPtr<VMFuncRef>>, 1655 post_return: Option<SendSyncPtr<VMFuncRef>>, 1656 ) -> Result<()> { 1657 /// Return a closure which will call the specified function in the scope 1658 /// of the specified task. 1659 /// 1660 /// This will use `GuestTask::lower_params` to lower the parameters, but 1661 /// will not lift the result; instead, it returns a 1662 /// `[MaybeUninit<ValRaw>; MAX_FLAT_PARAMS]` from which the result, if 1663 /// any, may be lifted. Note that an async-lifted export will have 1664 /// returned its result using the `task.return` intrinsic (or not 1665 /// returned a result at all, in the case of `task.cancel`), in which 1666 /// case the "result" of this call will either be a callback code or 1667 /// nothing. 1668 /// 1669 /// SAFETY: `callee` must be a valid `*mut VMFuncRef` at the time when 1670 /// the returned closure is called. 1671 unsafe fn make_call<T: 'static>( 1672 store: StoreContextMut<T>, 1673 guest_task: TableId<GuestTask>, 1674 callee: SendSyncPtr<VMFuncRef>, 1675 param_count: usize, 1676 result_count: usize, 1677 flags: Option<InstanceFlags>, 1678 ) -> impl FnOnce( 1679 &mut dyn VMStore, 1680 Instance, 1681 ) -> Result<[MaybeUninit<ValRaw>; MAX_FLAT_PARAMS]> 1682 + Send 1683 + Sync 1684 + 'static 1685 + use<T> { 1686 let token = StoreToken::new(store); 1687 move |store: &mut dyn VMStore, instance: Instance| { 1688 let mut storage = [MaybeUninit::uninit(); MAX_FLAT_PARAMS]; 1689 let task = instance.concurrent_state_mut(store).get_mut(guest_task)?; 1690 let may_enter_after_call = task.call_post_return_automatically(); 1691 let lower = task.lower_params.take().unwrap(); 1692 1693 lower(store, instance, &mut storage[..param_count])?; 1694 1695 let mut store = token.as_context_mut(store); 1696 1697 // SAFETY: Per the contract documented in `make_call's` 1698 // documentation, `callee` must be a valid pointer. 1699 unsafe { 1700 if let Some(mut flags) = flags { 1701 flags.set_may_enter(false); 1702 } 1703 crate::Func::call_unchecked_raw( 1704 &mut store, 1705 callee.as_non_null(), 1706 NonNull::new( 1707 &mut storage[..param_count.max(result_count)] 1708 as *mut [MaybeUninit<ValRaw>] as _, 1709 ) 1710 .unwrap(), 1711 )?; 1712 if let Some(mut flags) = flags { 1713 flags.set_may_enter(may_enter_after_call); 1714 } 1715 } 1716 1717 Ok(storage) 1718 } 1719 } 1720 1721 // SAFETY: Per the contract described in this function documentation, 1722 // the `callee` pointer which `call` closes over must be valid when 1723 // called by the closure we queue below. 1724 let call = unsafe { 1725 make_call( 1726 store.as_context_mut(), 1727 guest_task, 1728 callee, 1729 param_count, 1730 result_count, 1731 flags, 1732 ) 1733 }; 1734 1735 let callee_instance = self 1736 .concurrent_state_mut(store.0) 1737 .get_mut(guest_task)? 1738 .instance; 1739 let fun = if callback.is_some() { 1740 assert!(async_); 1741 1742 Box::new(move |store: &mut dyn VMStore, instance: Instance| { 1743 let old_task = instance 1744 .concurrent_state_mut(store) 1745 .guest_task 1746 .replace(guest_task); 1747 log::trace!( 1748 "stackless call: replaced {old_task:?} with {guest_task:?} as current task" 1749 ); 1750 1751 instance.maybe_push_call_context(store.store_opaque_mut(), guest_task)?; 1752 1753 instance 1754 .concurrent_state_mut(store) 1755 .enter_instance(callee_instance); 1756 1757 // SAFETY: See the documentation for `make_call` to review the 1758 // contract we must uphold for `call` here. 1759 // 1760 // Per the contract described in the `queue_call` 1761 // documentation, the `callee` pointer which `call` closes 1762 // over must be valid. 1763 let storage = call(store, instance)?; 1764 1765 instance 1766 .concurrent_state_mut(store) 1767 .exit_instance(callee_instance)?; 1768 1769 instance.maybe_pop_call_context(store.store_opaque_mut(), guest_task)?; 1770 1771 let state = instance.concurrent_state_mut(store); 1772 state.guest_task = old_task; 1773 log::trace!("stackless call: restored {old_task:?} as current task"); 1774 1775 // SAFETY: `wasmparser` will have validated that the callback 1776 // function returns a `i32` result. 1777 let code = unsafe { storage[0].assume_init() }.get_i32() as u32; 1778 1779 instance.id().get_mut(store).handle_callback_code( 1780 guest_task, 1781 callee_instance, 1782 code, 1783 true, 1784 )?; 1785 1786 Ok(()) 1787 }) 1788 as Box<dyn FnOnce(&mut dyn VMStore, Instance) -> Result<()> + Send + Sync> 1789 } else { 1790 let token = StoreToken::new(store.as_context_mut()); 1791 Box::new(move |store: &mut dyn VMStore, instance: Instance| { 1792 let old_task = instance 1793 .concurrent_state_mut(store) 1794 .guest_task 1795 .replace(guest_task); 1796 log::trace!( 1797 "stackful call: replaced {old_task:?} with {guest_task:?} as current task", 1798 ); 1799 1800 let mut flags = instance.id().get(store).instance_flags(callee_instance); 1801 1802 instance.maybe_push_call_context(store.store_opaque_mut(), guest_task)?; 1803 1804 // Unless this is a callback-less (i.e. stackful) 1805 // async-lifted export, we need to record that the instance 1806 // cannot be entered until the call returns. 1807 if !async_ { 1808 instance 1809 .concurrent_state_mut(store) 1810 .enter_instance(callee_instance); 1811 } 1812 1813 // SAFETY: See the documentation for `make_call` to review the 1814 // contract we must uphold for `call` here. 1815 // 1816 // Per the contract described in the `queue_call` 1817 // documentation, the `callee` pointer which `call` closes 1818 // over must be valid. 1819 let storage = call(store, instance)?; 1820 1821 if async_ { 1822 // This is a callback-less (i.e. stackful) async-lifted 1823 // export, so there is no post-return function, and 1824 // either `task.return` or `task.cancel` should have 1825 // been called. 1826 if instance 1827 .concurrent_state_mut(store) 1828 .get_mut(guest_task)? 1829 .lift_result 1830 .is_some() 1831 { 1832 return Err(anyhow!(crate::Trap::NoAsyncResult)); 1833 } 1834 } else { 1835 // This is a sync-lifted export, so now is when we lift the 1836 // result, optionally call the post-return function, if any, 1837 // and finally notify any current or future waiters that the 1838 // subtask has returned. 1839 1840 let lift = { 1841 let state = instance.concurrent_state_mut(store); 1842 state.exit_instance(callee_instance)?; 1843 1844 assert!(state.get_mut(guest_task)?.result.is_none()); 1845 1846 state.get_mut(guest_task)?.lift_result.take().unwrap() 1847 }; 1848 1849 // SAFETY: `result_count` represents the number of core Wasm 1850 // results returned, per `wasmparser`. 1851 let result = (lift.lift)(store, instance, unsafe { 1852 mem::transmute::<&[MaybeUninit<ValRaw>], &[ValRaw]>( 1853 &storage[..result_count], 1854 ) 1855 })?; 1856 1857 let post_return_arg = match result_count { 1858 0 => ValRaw::i32(0), 1859 // SAFETY: `result_count` represents the number of 1860 // core Wasm results returned, per `wasmparser`. 1861 1 => unsafe { storage[0].assume_init() }, 1862 _ => unreachable!(), 1863 }; 1864 1865 if instance 1866 .concurrent_state_mut(store) 1867 .get_mut(guest_task)? 1868 .call_post_return_automatically() 1869 { 1870 unsafe { 1871 flags.set_may_leave(false); 1872 flags.set_needs_post_return(false); 1873 } 1874 1875 if let Some(func) = post_return { 1876 let mut store = token.as_context_mut(store); 1877 1878 // SAFETY: `func` is a valid `*mut VMFuncRef` from 1879 // either `wasmtime-cranelift`-generated fused adapter 1880 // code or `component::Options`. Per `wasmparser` 1881 // post-return signature validation, we know it takes a 1882 // single parameter. 1883 unsafe { 1884 crate::Func::call_unchecked_raw( 1885 &mut store, 1886 func.as_non_null(), 1887 slice::from_ref(&post_return_arg).into(), 1888 )?; 1889 } 1890 } 1891 1892 unsafe { 1893 flags.set_may_leave(true); 1894 flags.set_may_enter(true); 1895 } 1896 } 1897 1898 instance.task_complete( 1899 store, 1900 guest_task, 1901 result, 1902 Status::Returned, 1903 post_return_arg, 1904 )?; 1905 } 1906 1907 instance.maybe_pop_call_context(store.store_opaque_mut(), guest_task)?; 1908 1909 let task = instance.concurrent_state_mut(store).get_mut(guest_task)?; 1910 1911 match &task.caller { 1912 Caller::Host { 1913 remove_task_automatically, 1914 .. 1915 } => { 1916 if *remove_task_automatically { 1917 Waitable::Guest(guest_task) 1918 .delete_from(instance.concurrent_state_mut(store))?; 1919 } 1920 } 1921 Caller::Guest { .. } => { 1922 task.exited = true; 1923 } 1924 } 1925 1926 Ok(()) 1927 }) 1928 }; 1929 1930 self.concurrent_state_mut(store.0) 1931 .push_high_priority(WorkItem::GuestCall(GuestCall { 1932 task: guest_task, 1933 kind: GuestCallKind::Start(fun), 1934 })); 1935 1936 Ok(()) 1937 } 1938 1939 /// Prepare (but do not start) a guest->guest call. 1940 /// 1941 /// This is called from fused adapter code generated in 1942 /// `wasmtime_environ::fact::trampoline::Compiler`. `start` and `return_` 1943 /// are synthesized Wasm functions which move the parameters from the caller 1944 /// to the callee and the result from the callee to the caller, 1945 /// respectively. The adapter will call `Self::start_call` immediately 1946 /// after calling this function. 1947 /// 1948 /// SAFETY: All the pointer arguments must be valid pointers to guest 1949 /// entities (and with the expected signatures for the function references 1950 /// -- see `wasmtime_environ::fact::trampoline::Compiler` for details). 1951 unsafe fn prepare_call<T: 'static>( 1952 self, 1953 mut store: StoreContextMut<T>, 1954 start: *mut VMFuncRef, 1955 return_: *mut VMFuncRef, 1956 caller_instance: RuntimeComponentInstanceIndex, 1957 callee_instance: RuntimeComponentInstanceIndex, 1958 task_return_type: TypeTupleIndex, 1959 memory: *mut VMMemoryDefinition, 1960 string_encoding: u8, 1961 caller_info: CallerInfo, 1962 ) -> Result<()> { 1963 self.id().get(store.0).check_may_leave(caller_instance)?; 1964 1965 enum ResultInfo { 1966 Heap { results: u32 }, 1967 Stack { result_count: u32 }, 1968 } 1969 1970 let result_info = match &caller_info { 1971 CallerInfo::Async { 1972 has_result: true, 1973 params, 1974 } => ResultInfo::Heap { 1975 results: params.last().unwrap().get_u32(), 1976 }, 1977 CallerInfo::Async { 1978 has_result: false, .. 1979 } => ResultInfo::Stack { result_count: 0 }, 1980 CallerInfo::Sync { 1981 result_count, 1982 params, 1983 } if *result_count > u32::try_from(MAX_FLAT_RESULTS).unwrap() => ResultInfo::Heap { 1984 results: params.last().unwrap().get_u32(), 1985 }, 1986 CallerInfo::Sync { result_count, .. } => ResultInfo::Stack { 1987 result_count: *result_count, 1988 }, 1989 }; 1990 1991 let sync_caller = matches!(caller_info, CallerInfo::Sync { .. }); 1992 1993 // Create a new guest task for the call, closing over the `start` and 1994 // `return_` functions to lift the parameters and lower the result, 1995 // respectively. 1996 let start = SendSyncPtr::new(NonNull::new(start).unwrap()); 1997 let return_ = SendSyncPtr::new(NonNull::new(return_).unwrap()); 1998 let token = StoreToken::new(store.as_context_mut()); 1999 let state = self.concurrent_state_mut(store.0); 2000 let old_task = state.guest_task.take(); 2001 let new_task = GuestTask::new( 2002 state, 2003 Box::new(move |store, instance, dst| { 2004 let mut store = token.as_context_mut(store); 2005 assert!(dst.len() <= MAX_FLAT_PARAMS); 2006 let mut src = [MaybeUninit::uninit(); MAX_FLAT_PARAMS]; 2007 let count = match caller_info { 2008 // Async callers, if they have a result, use the last 2009 // parameter as a return pointer so chop that off if 2010 // relevant here. 2011 CallerInfo::Async { params, has_result } => { 2012 let params = ¶ms[..params.len() - usize::from(has_result)]; 2013 for (param, src) in params.iter().zip(&mut src) { 2014 src.write(*param); 2015 } 2016 params.len() 2017 } 2018 2019 // Sync callers forward everything directly. 2020 CallerInfo::Sync { params, .. } => { 2021 for (param, src) in params.iter().zip(&mut src) { 2022 src.write(*param); 2023 } 2024 params.len() 2025 } 2026 }; 2027 // SAFETY: `start` is a valid `*mut VMFuncRef` from 2028 // `wasmtime-cranelift`-generated fused adapter code. Based on 2029 // how it was constructed (see 2030 // `wasmtime_environ::fact::trampoline::Compiler::compile_async_start_adapter` 2031 // for details) we know it takes count parameters and returns 2032 // `dst.len()` results. 2033 unsafe { 2034 crate::Func::call_unchecked_raw( 2035 &mut store, 2036 start.as_non_null(), 2037 NonNull::new( 2038 &mut src[..count.max(dst.len())] as *mut [MaybeUninit<ValRaw>] as _, 2039 ) 2040 .unwrap(), 2041 )?; 2042 } 2043 dst.copy_from_slice(&src[..dst.len()]); 2044 let state = instance.concurrent_state_mut(store.0); 2045 let task = state.guest_task.unwrap(); 2046 Waitable::Guest(task).set_event( 2047 state, 2048 Some(Event::Subtask { 2049 status: Status::Started, 2050 }), 2051 )?; 2052 Ok(()) 2053 }), 2054 LiftResult { 2055 lift: Box::new(move |store, instance, src| { 2056 // SAFETY: See comment in closure passed as `lower_params` 2057 // parameter above. 2058 let mut store = token.as_context_mut(store); 2059 let mut my_src = src.to_owned(); // TODO: use stack to avoid allocation? 2060 if let ResultInfo::Heap { results } = &result_info { 2061 my_src.push(ValRaw::u32(*results)); 2062 } 2063 // SAFETY: `return_` is a valid `*mut VMFuncRef` from 2064 // `wasmtime-cranelift`-generated fused adapter code. Based 2065 // on how it was constructed (see 2066 // `wasmtime_environ::fact::trampoline::Compiler::compile_async_return_adapter` 2067 // for details) we know it takes `src.len()` parameters and 2068 // returns up to 1 result. 2069 unsafe { 2070 crate::Func::call_unchecked_raw( 2071 &mut store, 2072 return_.as_non_null(), 2073 my_src.as_mut_slice().into(), 2074 )?; 2075 } 2076 let state = instance.concurrent_state_mut(store.0); 2077 let task = state.guest_task.unwrap(); 2078 if sync_caller { 2079 state.get_mut(task)?.sync_result = 2080 Some(if let ResultInfo::Stack { result_count } = &result_info { 2081 match result_count { 2082 0 => None, 2083 1 => Some(my_src[0]), 2084 _ => unreachable!(), 2085 } 2086 } else { 2087 None 2088 }); 2089 } 2090 Ok(Box::new(DummyResult) as Box<dyn Any + Send + Sync>) 2091 }), 2092 ty: task_return_type, 2093 memory: NonNull::new(memory).map(SendSyncPtr::new), 2094 string_encoding: StringEncoding::from_u8(string_encoding).unwrap(), 2095 }, 2096 Caller::Guest { 2097 task: old_task.unwrap(), 2098 instance: caller_instance, 2099 }, 2100 None, 2101 callee_instance, 2102 )?; 2103 2104 let guest_task = state.push(new_task)?; 2105 2106 if let Some(old_task) = old_task { 2107 if !state.may_enter(guest_task) { 2108 bail!(crate::Trap::CannotEnterComponent); 2109 } 2110 2111 state.get_mut(old_task)?.subtasks.insert(guest_task); 2112 }; 2113 2114 // Make the new task the current one so that `Self::start_call` knows 2115 // which one to start. 2116 state.guest_task = Some(guest_task); 2117 log::trace!("pushed {guest_task:?} as current task; old task was {old_task:?}"); 2118 2119 Ok(()) 2120 } 2121 2122 /// Call the specified callback function for an async-lifted export. 2123 /// 2124 /// SAFETY: `function` must be a valid reference to a guest function of the 2125 /// correct signature for a callback. 2126 unsafe fn call_callback<T>( 2127 self, 2128 mut store: StoreContextMut<T>, 2129 callee_instance: RuntimeComponentInstanceIndex, 2130 function: SendSyncPtr<VMFuncRef>, 2131 event: Event, 2132 handle: u32, 2133 may_enter_after_call: bool, 2134 ) -> Result<u32> { 2135 let mut flags = self.id().get(store.0).instance_flags(callee_instance); 2136 2137 let (ordinal, result) = event.parts(); 2138 let params = &mut [ 2139 ValRaw::u32(ordinal), 2140 ValRaw::u32(handle), 2141 ValRaw::u32(result), 2142 ]; 2143 // SAFETY: `func` is a valid `*mut VMFuncRef` from either 2144 // `wasmtime-cranelift`-generated fused adapter code or 2145 // `component::Options`. Per `wasmparser` callback signature 2146 // validation, we know it takes three parameters and returns one. 2147 unsafe { 2148 flags.set_may_enter(false); 2149 crate::Func::call_unchecked_raw( 2150 &mut store, 2151 function.as_non_null(), 2152 params.as_mut_slice().into(), 2153 )?; 2154 flags.set_may_enter(may_enter_after_call); 2155 } 2156 Ok(params[0].get_u32()) 2157 } 2158 2159 /// Start a guest->guest call previously prepared using 2160 /// `Self::prepare_call`. 2161 /// 2162 /// This is called from fused adapter code generated in 2163 /// `wasmtime_environ::fact::trampoline::Compiler`. The adapter will call 2164 /// this function immediately after calling `Self::prepare_call`. 2165 /// 2166 /// SAFETY: The `*mut VMFuncRef` arguments must be valid pointers to guest 2167 /// functions with the appropriate signatures for the current guest task. 2168 /// If this is a call to an async-lowered import, the actual call may be 2169 /// deferred and run after this function returns, in which case the pointer 2170 /// arguments must also be valid when the call happens. 2171 unsafe fn start_call<T: 'static>( 2172 self, 2173 mut store: StoreContextMut<T>, 2174 callback: *mut VMFuncRef, 2175 post_return: *mut VMFuncRef, 2176 callee: *mut VMFuncRef, 2177 param_count: u32, 2178 result_count: u32, 2179 flags: u32, 2180 storage: Option<&mut [MaybeUninit<ValRaw>]>, 2181 ) -> Result<u32> { 2182 let token = StoreToken::new(store.as_context_mut()); 2183 let async_caller = storage.is_none(); 2184 let state = self.concurrent_state_mut(store.0); 2185 let guest_task = state.guest_task.unwrap(); 2186 let may_enter_after_call = state.get_mut(guest_task)?.call_post_return_automatically(); 2187 let callee = SendSyncPtr::new(NonNull::new(callee).unwrap()); 2188 let param_count = usize::try_from(param_count).unwrap(); 2189 assert!(param_count <= MAX_FLAT_PARAMS); 2190 let result_count = usize::try_from(result_count).unwrap(); 2191 assert!(result_count <= MAX_FLAT_RESULTS); 2192 2193 let task = state.get_mut(guest_task)?; 2194 if !callback.is_null() { 2195 // We're calling an async-lifted export with a callback, so store 2196 // the callback and related context as part of the task so we can 2197 // call it later when needed. 2198 let callback = SendSyncPtr::new(NonNull::new(callback).unwrap()); 2199 task.callback = Some(Box::new( 2200 move |store, instance, runtime_instance, event, handle| { 2201 let store = token.as_context_mut(store); 2202 unsafe { 2203 instance.call_callback::<T>( 2204 store, 2205 runtime_instance, 2206 callback, 2207 event, 2208 handle, 2209 may_enter_after_call, 2210 ) 2211 } 2212 }, 2213 )); 2214 } 2215 2216 let Caller::Guest { 2217 task: caller, 2218 instance: runtime_instance, 2219 } = &task.caller 2220 else { 2221 // As of this writing, `start_call` is only used for guest->guest 2222 // calls. 2223 unreachable!() 2224 }; 2225 let caller = *caller; 2226 let caller_instance = *runtime_instance; 2227 2228 let callee_instance = task.instance; 2229 2230 let instance_flags = if callback.is_null() { 2231 None 2232 } else { 2233 Some(self.id().get(store.0).instance_flags(callee_instance)) 2234 }; 2235 2236 // Queue the call as a "high priority" work item. 2237 unsafe { 2238 self.queue_call( 2239 store.as_context_mut(), 2240 guest_task, 2241 callee, 2242 param_count, 2243 result_count, 2244 instance_flags, 2245 (flags & START_FLAG_ASYNC_CALLEE) != 0, 2246 NonNull::new(callback).map(SendSyncPtr::new), 2247 NonNull::new(post_return).map(SendSyncPtr::new), 2248 )?; 2249 } 2250 2251 let state = self.concurrent_state_mut(store.0); 2252 2253 // Use the caller's `GuestTask::sync_call_set` to register interest in 2254 // the subtask... 2255 let guest_waitable = Waitable::Guest(guest_task); 2256 let old_set = guest_waitable.common(state)?.set; 2257 let set = state.get_mut(caller)?.sync_call_set; 2258 guest_waitable.join(state, Some(set))?; 2259 2260 // ... and suspend this fiber temporarily while we wait for it to start. 2261 // 2262 // Note that we _could_ call the callee directly using the current fiber 2263 // rather than suspend this one, but that would make reasoning about the 2264 // event loop more complicated and is probably only worth doing if 2265 // there's a measurable performance benefit. In addition, it would mean 2266 // blocking the caller if the callee calls a blocking sync-lowered 2267 // import, and as of this writing the spec says we must not do that. 2268 // 2269 // Alternatively, the fused adapter code could be modified to call the 2270 // callee directly without calling a host-provided intrinsic at all (in 2271 // which case it would need to do its own, inline backpressure checks, 2272 // etc.). Again, we'd want to see a measurable performance benefit 2273 // before committing to such an optimization. And again, we'd need to 2274 // update the spec to allow that. 2275 let (status, waitable) = loop { 2276 self.suspend(store.0, SuspendReason::Waiting { set, task: caller })?; 2277 2278 let state = self.concurrent_state_mut(store.0); 2279 2280 let event = guest_waitable.take_event(state)?; 2281 let Some(Event::Subtask { status }) = event else { 2282 unreachable!(); 2283 }; 2284 2285 log::trace!("status {status:?} for {guest_task:?}"); 2286 2287 if status == Status::Returned { 2288 // It returned, so we can stop waiting. 2289 break (status, None); 2290 } else if async_caller { 2291 // It hasn't returned yet, but the caller is calling via an 2292 // async-lowered import, so we generate a handle for the task 2293 // waitable and return the status. 2294 let handle = self.id().get_mut(store.0).guest_tables().0[caller_instance] 2295 .subtask_insert_guest(guest_task.rep())?; 2296 self.concurrent_state_mut(store.0) 2297 .get_mut(guest_task)? 2298 .common 2299 .handle = Some(handle); 2300 break (status, Some(handle)); 2301 } else { 2302 // The callee hasn't returned yet, and the caller is calling via 2303 // a sync-lowered import, so we loop and keep waiting until the 2304 // callee returns. 2305 } 2306 }; 2307 2308 let state = self.concurrent_state_mut(store.0); 2309 2310 guest_waitable.join(state, old_set)?; 2311 2312 if let Some(storage) = storage { 2313 // The caller used a sync-lowered import to call an async-lifted 2314 // export, in which case the result, if any, has been stashed in 2315 // `GuestTask::sync_result`. 2316 let task = state.get_mut(guest_task)?; 2317 if let Some(result) = task.sync_result.take() { 2318 if let Some(result) = result { 2319 storage[0] = MaybeUninit::new(result); 2320 } 2321 2322 if task.exited { 2323 Waitable::Guest(guest_task).delete_from(state)?; 2324 } 2325 } else { 2326 // This means the callee failed to call either `task.return` or 2327 // `task.cancel` before exiting. 2328 return Err(anyhow!(crate::Trap::NoAsyncResult)); 2329 } 2330 } 2331 2332 // Reset the current task to point to the caller as it resumes control. 2333 state.guest_task = Some(caller); 2334 log::trace!("popped current task {guest_task:?}; new task is {caller:?}"); 2335 2336 Ok(status.pack(waitable)) 2337 } 2338 2339 /// Wrap the specified host function in a future which will call it, passing 2340 /// it an `&Accessor<T>`. 2341 /// 2342 /// See the `Accessor` documentation for details. 2343 pub(crate) fn wrap_call<T, F, R>( 2344 self, 2345 store: StoreContextMut<T>, 2346 closure: F, 2347 ) -> impl Future<Output = Result<R>> + 'static 2348 where 2349 T: 'static, 2350 F: FnOnce(&Accessor<T>) -> Pin<Box<dyn Future<Output = Result<R>> + Send + '_>> 2351 + Send 2352 + Sync 2353 + 'static, 2354 R: Send + Sync + 'static, 2355 { 2356 let token = StoreToken::new(store); 2357 async move { 2358 let mut accessor = Accessor::new(token, Some(self)); 2359 closure(&mut accessor).await 2360 } 2361 } 2362 2363 /// Poll the specified future once on behalf of a guest->host call using an 2364 /// async-lowered import. 2365 /// 2366 /// If it returns `Ready`, return `Ok(None)`. Otherwise, if it returns 2367 /// `Pending`, add it to the set of futures to be polled as part of this 2368 /// instance's event loop until it completes, and then return 2369 /// `Ok(Some(handle))` where `handle` is the waitable handle to return. 2370 /// 2371 /// Whether the future returns `Ready` immediately or later, the `lower` 2372 /// function will be used to lower the result, if any, into the guest caller's 2373 /// stack and linear memory unless the task has been cancelled. 2374 pub(crate) fn first_poll<T: 'static, R: Send + 'static>( 2375 self, 2376 mut store: StoreContextMut<T>, 2377 future: impl Future<Output = Result<R>> + Send + 'static, 2378 caller_instance: RuntimeComponentInstanceIndex, 2379 lower: impl FnOnce(StoreContextMut<T>, Instance, R) -> Result<()> + Send + 'static, 2380 ) -> Result<Option<u32>> { 2381 let token = StoreToken::new(store.as_context_mut()); 2382 let state = self.concurrent_state_mut(store.0); 2383 let caller = state.guest_task.unwrap(); 2384 2385 // Create an abortable future which hooks calls to poll and manages call 2386 // context state for the future. 2387 let (join_handle, future) = JoinHandle::run(async move { 2388 let mut future = pin!(future); 2389 let mut call_context = None; 2390 future::poll_fn(move |cx| { 2391 // Push the call context for managing any resource borrows 2392 // for the task. 2393 tls::get(|store| { 2394 if let Some(call_context) = call_context.take() { 2395 token 2396 .as_context_mut(store) 2397 .0 2398 .component_resource_state() 2399 .0 2400 .push(call_context); 2401 } 2402 }); 2403 2404 let result = future.as_mut().poll(cx); 2405 2406 if result.is_pending() { 2407 // Pop the call context for managing any resource 2408 // borrows for the task. 2409 tls::get(|store| { 2410 call_context = Some( 2411 token 2412 .as_context_mut(store) 2413 .0 2414 .component_resource_state() 2415 .0 2416 .pop() 2417 .unwrap(), 2418 ); 2419 }); 2420 } 2421 result 2422 }) 2423 .await 2424 }); 2425 2426 // We create a new host task even though it might complete immediately 2427 // (in which case we won't need to pass a waitable back to the guest). 2428 // If it does complete immediately, we'll remove it before we return. 2429 let task = state.push(HostTask::new(caller_instance, Some(join_handle)))?; 2430 2431 log::trace!("new host task child of {caller:?}: {task:?}"); 2432 2433 let mut future = Box::pin(future); 2434 2435 // Finally, poll the future. We can use a dummy `Waker` here because 2436 // we'll add the future to `ConcurrentState::futures` and poll it 2437 // automatically from the event loop if it doesn't complete immediately 2438 // here. 2439 let poll = self.set_tls(store.0, || { 2440 future 2441 .as_mut() 2442 .poll(&mut Context::from_waker(&Waker::noop())) 2443 }); 2444 2445 Ok(match poll { 2446 Poll::Ready(None) => unreachable!(), 2447 Poll::Ready(Some(result)) => { 2448 // It finished immediately; lower the result and delete the 2449 // task. 2450 lower(store.as_context_mut(), self, result?)?; 2451 log::trace!("delete host task {task:?} (already ready)"); 2452 self.concurrent_state_mut(store.0).delete(task)?; 2453 None 2454 } 2455 Poll::Pending => { 2456 // It hasn't finished yet; add the future to 2457 // `ConcurrentState::futures` so it will be polled by the event 2458 // loop and allocate a waitable handle to return to the guest. 2459 2460 // Wrap the future in a closure responsible for lowering the result into 2461 // the guest's stack and memory, as well as notifying any waiters that 2462 // the task returned. 2463 let future = Box::pin(async move { 2464 let result = match future.await { 2465 Some(result) => result?, 2466 // Task was cancelled; nothing left to do. 2467 None => return Ok(()), 2468 }; 2469 tls::get(move |store| { 2470 // Here we schedule a task to run on a worker fiber to do 2471 // the lowering since it may involve a call to the guest's 2472 // realloc function. This is necessary because calling the 2473 // guest while there are host embedder frames on the stack 2474 // is unsound. 2475 self.concurrent_state_mut(store).push_high_priority( 2476 WorkItem::WorkerFunction(AlwaysMut::new(Box::new(move |store, _| { 2477 lower(token.as_context_mut(store), self, result)?; 2478 let state = self.concurrent_state_mut(store); 2479 state.get_mut(task)?.join_handle.take(); 2480 Waitable::Host(task).set_event( 2481 state, 2482 Some(Event::Subtask { 2483 status: Status::Returned, 2484 }), 2485 ) 2486 }))), 2487 ); 2488 Ok(()) 2489 }) 2490 }); 2491 2492 self.concurrent_state_mut(store.0).push_future(future); 2493 let handle = self.id().get_mut(store.0).guest_tables().0[caller_instance] 2494 .subtask_insert_host(task.rep())?; 2495 self.concurrent_state_mut(store.0) 2496 .get_mut(task)? 2497 .common 2498 .handle = Some(handle); 2499 log::trace!( 2500 "assign {task:?} handle {handle} for {caller:?} instance {caller_instance:?}" 2501 ); 2502 Some(handle) 2503 } 2504 }) 2505 } 2506 2507 /// Poll the specified future until it completes on behalf of a guest->host 2508 /// call using a sync-lowered import. 2509 /// 2510 /// This is similar to `Self::first_poll` except it's for sync-lowered 2511 /// imports, meaning we don't need to handle cancellation and we can block 2512 /// the caller until the task completes, at which point the caller can 2513 /// handle lowering the result to the guest's stack and linear memory. 2514 pub(crate) fn poll_and_block<R: Send + Sync + 'static>( 2515 self, 2516 store: &mut dyn VMStore, 2517 future: impl Future<Output = Result<R>> + Send + 'static, 2518 caller_instance: RuntimeComponentInstanceIndex, 2519 ) -> Result<R> { 2520 let state = self.concurrent_state_mut(store); 2521 2522 // If there is no current guest task set, that means the host function 2523 // was registered using e.g. `LinkerInstance::func_wrap`, in which case 2524 // it should complete immediately. 2525 let Some(caller) = state.guest_task else { 2526 return match pin!(future).poll(&mut Context::from_waker(&Waker::noop())) { 2527 Poll::Ready(result) => result, 2528 Poll::Pending => { 2529 unreachable!() 2530 } 2531 }; 2532 }; 2533 2534 // Save any existing result stashed in `GuestTask::result` so we can 2535 // replace it with the new result. 2536 let old_result = state 2537 .get_mut(caller) 2538 .with_context(|| format!("bad handle: {caller:?}"))? 2539 .result 2540 .take(); 2541 2542 // Add a temporary host task into the table so we can track its 2543 // progress. Note that we'll never allocate a waitable handle for the 2544 // guest since we're being called synchronously. 2545 let task = state.push(HostTask::new(caller_instance, None))?; 2546 2547 log::trace!("new host task child of {caller:?}: {task:?}"); 2548 2549 // Wrap the future in a closure which will take care of stashing the 2550 // result in `GuestTask::result` and resuming this fiber when the host 2551 // task completes. 2552 let mut future = Box::pin(async move { 2553 let result = future.await?; 2554 tls::get(move |store| { 2555 let state = self.concurrent_state_mut(store); 2556 state.get_mut(caller)?.result = Some(Box::new(result) as _); 2557 2558 Waitable::Host(task).set_event( 2559 state, 2560 Some(Event::Subtask { 2561 status: Status::Returned, 2562 }), 2563 )?; 2564 2565 Ok(()) 2566 }) 2567 }) as HostTaskFuture; 2568 2569 // Finally, poll the future. We can use a dummy `Waker` here because 2570 // we'll add the future to `ConcurrentState::futures` and poll it 2571 // automatically from the event loop if it doesn't complete immediately 2572 // here. 2573 let poll = self.set_tls(store, || { 2574 future 2575 .as_mut() 2576 .poll(&mut Context::from_waker(&Waker::noop())) 2577 }); 2578 2579 match poll { 2580 Poll::Ready(result) => { 2581 // It completed immediately; check the result and delete the task. 2582 result?; 2583 log::trace!("delete host task {task:?} (already ready)"); 2584 self.concurrent_state_mut(store).delete(task)?; 2585 } 2586 Poll::Pending => { 2587 // It did not complete immediately; add it to 2588 // `ConcurrentState::futures` so it will be polled via the event 2589 // loop; then use `GuestTask::sync_call_set` to wait for the 2590 // task to complete, suspending the current fiber until it does 2591 // so. 2592 let state = self.concurrent_state_mut(store); 2593 state.push_future(future); 2594 2595 let set = state.get_mut(caller)?.sync_call_set; 2596 Waitable::Host(task).join(state, Some(set))?; 2597 2598 self.suspend(store, SuspendReason::Waiting { set, task: caller })?; 2599 } 2600 } 2601 2602 // Retrieve and return the result. 2603 Ok(*mem::replace( 2604 &mut self.concurrent_state_mut(store).get_mut(caller)?.result, 2605 old_result, 2606 ) 2607 .unwrap() 2608 .downcast() 2609 .unwrap()) 2610 } 2611 2612 /// Implements the `task.return` intrinsic, lifting the result for the 2613 /// current guest task. 2614 pub(crate) fn task_return( 2615 self, 2616 store: &mut dyn VMStore, 2617 caller: RuntimeComponentInstanceIndex, 2618 ty: TypeTupleIndex, 2619 options: OptionsIndex, 2620 storage: &[ValRaw], 2621 ) -> Result<()> { 2622 self.id().get(store).check_may_leave(caller)?; 2623 let state = self.concurrent_state_mut(store); 2624 let CanonicalOptions { 2625 string_encoding, 2626 data_model, 2627 .. 2628 } = *state.options(options); 2629 let guest_task = state.guest_task.unwrap(); 2630 let lift = state 2631 .get_mut(guest_task)? 2632 .lift_result 2633 .take() 2634 .ok_or_else(|| { 2635 anyhow!("`task.return` or `task.cancel` called more than once for current task") 2636 })?; 2637 assert!(state.get_mut(guest_task)?.result.is_none()); 2638 2639 let invalid = ty != lift.ty 2640 || string_encoding != lift.string_encoding 2641 || match data_model { 2642 CanonicalOptionsDataModel::LinearMemory(opts) => match opts.memory { 2643 Some(memory) => { 2644 let expected = lift.memory.map(|v| v.as_ptr()).unwrap_or(ptr::null_mut()); 2645 let actual = self.id().get(store).runtime_memory(memory); 2646 expected != actual 2647 } 2648 // Memory not specified, meaning it didn't need to be 2649 // specified per validation, so not invalid. 2650 None => false, 2651 }, 2652 // Always invalid as this isn't supported. 2653 CanonicalOptionsDataModel::Gc { .. } => true, 2654 }; 2655 2656 if invalid { 2657 bail!("invalid `task.return` signature and/or options for current task"); 2658 } 2659 2660 log::trace!("task.return for {guest_task:?}"); 2661 2662 let result = (lift.lift)(store, self, storage)?; 2663 2664 self.task_complete(store, guest_task, result, Status::Returned, ValRaw::i32(0)) 2665 } 2666 2667 /// Implements the `task.cancel` intrinsic. 2668 pub(crate) fn task_cancel( 2669 self, 2670 store: &mut dyn VMStore, 2671 caller: RuntimeComponentInstanceIndex, 2672 ) -> Result<()> { 2673 self.id().get(store).check_may_leave(caller)?; 2674 let state = self.concurrent_state_mut(store); 2675 let guest_task = state.guest_task.unwrap(); 2676 let task = state.get_mut(guest_task)?; 2677 if !task.cancel_sent { 2678 bail!("`task.cancel` called by task which has not been cancelled") 2679 } 2680 _ = task.lift_result.take().ok_or_else(|| { 2681 anyhow!("`task.return` or `task.cancel` called more than once for current task") 2682 })?; 2683 2684 assert!(task.result.is_none()); 2685 2686 log::trace!("task.cancel for {guest_task:?}"); 2687 2688 self.task_complete( 2689 store, 2690 guest_task, 2691 Box::new(DummyResult), 2692 Status::ReturnCancelled, 2693 ValRaw::i32(0), 2694 ) 2695 } 2696 2697 /// Complete the specified guest task (i.e. indicate that it has either 2698 /// returned a (possibly empty) result or cancelled itself). 2699 /// 2700 /// This will return any resource borrows and notify any current or future 2701 /// waiters that the task has completed. 2702 fn task_complete( 2703 self, 2704 store: &mut dyn VMStore, 2705 guest_task: TableId<GuestTask>, 2706 result: Box<dyn Any + Send + Sync>, 2707 status: Status, 2708 post_return_arg: ValRaw, 2709 ) -> Result<()> { 2710 if self 2711 .concurrent_state_mut(store) 2712 .get_mut(guest_task)? 2713 .call_post_return_automatically() 2714 { 2715 let (calls, host_table, _, instance) = store 2716 .store_opaque_mut() 2717 .component_resource_state_with_instance(self); 2718 ResourceTables { 2719 calls, 2720 host_table: Some(host_table), 2721 guest: Some(instance.guest_tables()), 2722 } 2723 .exit_call()?; 2724 } else { 2725 // As of this writing, the only scenario where `call_post_return_automatically` 2726 // would be false for a `GuestTask` is for host-to-guest calls using 2727 // `[Typed]Func::call_async`, in which case the `function_index` 2728 // should be a non-`None` value. 2729 let function_index = self 2730 .concurrent_state_mut(store) 2731 .get_mut(guest_task)? 2732 .function_index 2733 .unwrap(); 2734 2735 self.id() 2736 .get_mut(store) 2737 .post_return_arg_set(function_index, post_return_arg); 2738 } 2739 2740 let state = self.concurrent_state_mut(store); 2741 let task = state.get_mut(guest_task)?; 2742 2743 if let Caller::Host { tx, .. } = &mut task.caller { 2744 if let Some(tx) = tx.take() { 2745 _ = tx.send(result); 2746 } 2747 } else { 2748 task.result = Some(result); 2749 Waitable::Guest(guest_task).set_event(state, Some(Event::Subtask { status }))?; 2750 } 2751 2752 Ok(()) 2753 } 2754 2755 /// Implements the `waitable-set.wait` intrinsic. 2756 pub(crate) fn waitable_set_wait( 2757 self, 2758 store: &mut dyn VMStore, 2759 caller: RuntimeComponentInstanceIndex, 2760 options: OptionsIndex, 2761 set: u32, 2762 payload: u32, 2763 ) -> Result<u32> { 2764 self.id().get(store).check_may_leave(caller)?; 2765 let opts = self.concurrent_state_mut(store).options(options); 2766 let cancellable = opts.cancellable; 2767 let caller_instance = opts.instance; 2768 let rep = 2769 self.id().get_mut(store).guest_tables().0[caller_instance].waitable_set_rep(set)?; 2770 2771 self.waitable_check( 2772 store, 2773 cancellable, 2774 WaitableCheck::Wait(WaitableCheckParams { 2775 set: TableId::new(rep), 2776 options, 2777 payload, 2778 }), 2779 ) 2780 } 2781 2782 /// Implements the `waitable-set.poll` intrinsic. 2783 pub(crate) fn waitable_set_poll( 2784 self, 2785 store: &mut dyn VMStore, 2786 caller: RuntimeComponentInstanceIndex, 2787 options: OptionsIndex, 2788 set: u32, 2789 payload: u32, 2790 ) -> Result<u32> { 2791 self.id().get(store).check_may_leave(caller)?; 2792 let opts = self.concurrent_state_mut(store).options(options); 2793 let cancellable = opts.cancellable; 2794 let caller_instance = opts.instance; 2795 let rep = 2796 self.id().get_mut(store).guest_tables().0[caller_instance].waitable_set_rep(set)?; 2797 2798 self.waitable_check( 2799 store, 2800 cancellable, 2801 WaitableCheck::Poll(WaitableCheckParams { 2802 set: TableId::new(rep), 2803 options, 2804 payload, 2805 }), 2806 ) 2807 } 2808 2809 /// Implements the `thread.yield` intrinsic. 2810 pub(crate) fn thread_yield( 2811 self, 2812 store: &mut dyn VMStore, 2813 caller: RuntimeComponentInstanceIndex, 2814 cancellable: bool, 2815 ) -> Result<bool> { 2816 self.id().get(store).check_may_leave(caller)?; 2817 self.waitable_check(store, cancellable, WaitableCheck::Yield) 2818 .map(|_| { 2819 if cancellable { 2820 let state = self.concurrent_state_mut(store); 2821 let task = state.guest_task.unwrap(); 2822 if let Some(event) = state.get_mut(task).unwrap().event.take() { 2823 assert!(matches!(event, Event::Cancelled)); 2824 true 2825 } else { 2826 false 2827 } 2828 } else { 2829 false 2830 } 2831 }) 2832 } 2833 2834 /// Helper function for the `waitable-set.wait`, `waitable-set.poll`, and 2835 /// `yield` intrinsics. 2836 fn waitable_check( 2837 self, 2838 store: &mut dyn VMStore, 2839 cancellable: bool, 2840 check: WaitableCheck, 2841 ) -> Result<u32> { 2842 let guest_task = self.concurrent_state_mut(store).guest_task.unwrap(); 2843 2844 let (wait, set) = match &check { 2845 WaitableCheck::Wait(params) => (true, Some(params.set)), 2846 WaitableCheck::Poll(params) => (false, Some(params.set)), 2847 WaitableCheck::Yield => (false, None), 2848 }; 2849 2850 // First, suspend this fiber, allowing any other tasks to run. 2851 self.suspend(store, SuspendReason::Yielding { task: guest_task })?; 2852 2853 log::trace!("waitable check for {guest_task:?}; set {set:?}"); 2854 2855 let state = self.concurrent_state_mut(store); 2856 let task = state.get_mut(guest_task)?; 2857 2858 if wait && task.callback.is_some() { 2859 bail!("cannot call `task.wait` from async-lifted export with callback"); 2860 } 2861 2862 // If we're waiting, and there are no events immediately available, 2863 // suspend the fiber until that changes. 2864 if wait { 2865 let set = set.unwrap(); 2866 2867 if (task.event.is_none() 2868 || (matches!(task.event, Some(Event::Cancelled)) && !cancellable)) 2869 && state.get_mut(set)?.ready.is_empty() 2870 { 2871 if cancellable { 2872 let old = state.get_mut(guest_task)?.wake_on_cancel.replace(set); 2873 assert!(old.is_none()); 2874 } 2875 2876 self.suspend( 2877 store, 2878 SuspendReason::Waiting { 2879 set, 2880 task: guest_task, 2881 }, 2882 )?; 2883 } 2884 } 2885 2886 log::trace!("waitable check for {guest_task:?}; set {set:?}, part two"); 2887 2888 let result = match check { 2889 // Deliver any pending events to the guest and return. 2890 WaitableCheck::Wait(params) | WaitableCheck::Poll(params) => { 2891 let event = self.id().get_mut(store).get_event( 2892 guest_task, 2893 Some(params.set), 2894 cancellable, 2895 )?; 2896 2897 let (ordinal, handle, result) = if wait { 2898 let (event, waitable) = event.unwrap(); 2899 let handle = waitable.map(|(_, v)| v).unwrap_or(0); 2900 let (ordinal, result) = event.parts(); 2901 (ordinal, handle, result) 2902 } else { 2903 if let Some((event, waitable)) = event { 2904 let handle = waitable.map(|(_, v)| v).unwrap_or(0); 2905 let (ordinal, result) = event.parts(); 2906 (ordinal, handle, result) 2907 } else { 2908 log::trace!( 2909 "no events ready to deliver via waitable-set.poll to {guest_task:?}; set {:?}", 2910 params.set 2911 ); 2912 let (ordinal, result) = Event::None.parts(); 2913 (ordinal, 0, result) 2914 } 2915 }; 2916 let store = store.store_opaque_mut(); 2917 let options = Options::new_index(store, self, params.options); 2918 let ptr = func::validate_inbounds::<(u32, u32)>( 2919 options.memory_mut(store), 2920 &ValRaw::u32(params.payload), 2921 )?; 2922 options.memory_mut(store)[ptr + 0..][..4].copy_from_slice(&handle.to_le_bytes()); 2923 options.memory_mut(store)[ptr + 4..][..4].copy_from_slice(&result.to_le_bytes()); 2924 Ok(ordinal) 2925 } 2926 WaitableCheck::Yield => Ok(0), 2927 }; 2928 2929 result 2930 } 2931 2932 /// Implements the `subtask.cancel` intrinsic. 2933 pub(crate) fn subtask_cancel( 2934 self, 2935 store: &mut dyn VMStore, 2936 caller_instance: RuntimeComponentInstanceIndex, 2937 async_: bool, 2938 task_id: u32, 2939 ) -> Result<u32> { 2940 self.id().get(store).check_may_leave(caller_instance)?; 2941 let (rep, is_host) = 2942 self.id().get_mut(store).guest_tables().0[caller_instance].subtask_rep(task_id)?; 2943 let (waitable, expected_caller_instance) = if is_host { 2944 let id = TableId::<HostTask>::new(rep); 2945 ( 2946 Waitable::Host(id), 2947 self.concurrent_state_mut(store) 2948 .get_mut(id)? 2949 .caller_instance, 2950 ) 2951 } else { 2952 let id = TableId::<GuestTask>::new(rep); 2953 if let Caller::Guest { instance, .. } = 2954 &self.concurrent_state_mut(store).get_mut(id)?.caller 2955 { 2956 (Waitable::Guest(id), *instance) 2957 } else { 2958 unreachable!() 2959 } 2960 }; 2961 // Since waitables can neither be passed between instances nor forged, 2962 // this should never fail unless there's a bug in Wasmtime, but we check 2963 // here to be sure: 2964 assert_eq!(expected_caller_instance, caller_instance); 2965 2966 log::trace!("subtask_cancel {waitable:?} (handle {task_id})"); 2967 2968 let concurrent_state = self.concurrent_state_mut(store); 2969 if let Waitable::Host(host_task) = waitable { 2970 if let Some(handle) = concurrent_state.get_mut(host_task)?.join_handle.take() { 2971 handle.abort(); 2972 return Ok(Status::ReturnCancelled as u32); 2973 } 2974 } else { 2975 let caller = concurrent_state.guest_task.unwrap(); 2976 let guest_task = TableId::<GuestTask>::new(rep); 2977 let task = concurrent_state.get_mut(guest_task)?; 2978 if task.lower_params.is_some() { 2979 task.lower_params = None; 2980 task.lift_result = None; 2981 2982 // Not yet started; cancel and remove from pending 2983 let callee_instance = task.instance; 2984 2985 let kind = concurrent_state 2986 .instance_state(callee_instance) 2987 .pending 2988 .remove(&guest_task); 2989 2990 if kind.is_none() { 2991 bail!("`subtask.cancel` called after terminal status delivered"); 2992 } 2993 2994 return Ok(Status::StartCancelled as u32); 2995 } else if task.lift_result.is_some() { 2996 // Started, but not yet returned or cancelled; send the 2997 // `CANCELLED` event 2998 task.cancel_sent = true; 2999 // Note that this might overwrite an event that was set earlier 3000 // (e.g. `Event::None` if the task is yielding, or 3001 // `Event::Cancelled` if it was already cancelled), but that's 3002 // okay -- this should supersede the previous state. 3003 task.event = Some(Event::Cancelled); 3004 if let Some(set) = task.wake_on_cancel.take() { 3005 let item = match concurrent_state 3006 .get_mut(set)? 3007 .waiting 3008 .remove(&guest_task) 3009 .unwrap() 3010 { 3011 WaitMode::Fiber(fiber) => WorkItem::ResumeFiber(fiber), 3012 WaitMode::Callback => WorkItem::GuestCall(GuestCall { 3013 task: guest_task, 3014 kind: GuestCallKind::DeliverEvent { set: None }, 3015 }), 3016 }; 3017 concurrent_state.push_high_priority(item); 3018 3019 self.suspend(store, SuspendReason::Yielding { task: caller })?; 3020 } 3021 3022 let concurrent_state = self.concurrent_state_mut(store); 3023 let task = concurrent_state.get_mut(guest_task)?; 3024 if task.lift_result.is_some() { 3025 // Still not yet returned or cancelled; if `async_`, return 3026 // `BLOCKED`; otherwise wait 3027 if async_ { 3028 return Ok(BLOCKED); 3029 } else { 3030 self.wait_for_event(store, Waitable::Guest(guest_task))?; 3031 } 3032 } 3033 } 3034 } 3035 3036 let event = waitable.take_event(self.concurrent_state_mut(store))?; 3037 if let Some(Event::Subtask { 3038 status: status @ (Status::Returned | Status::ReturnCancelled), 3039 }) = event 3040 { 3041 Ok(status as u32) 3042 } else { 3043 bail!("`subtask.cancel` called after terminal status delivered"); 3044 } 3045 } 3046 3047 fn wait_for_event(self, store: &mut dyn VMStore, waitable: Waitable) -> Result<()> { 3048 let state = self.concurrent_state_mut(store); 3049 let caller = state.guest_task.unwrap(); 3050 let old_set = waitable.common(state)?.set; 3051 let set = state.get_mut(caller)?.sync_call_set; 3052 waitable.join(state, Some(set))?; 3053 self.suspend(store, SuspendReason::Waiting { set, task: caller })?; 3054 let state = self.concurrent_state_mut(store); 3055 waitable.join(state, old_set) 3056 } 3057 3058 /// Configures TLS state so `store` will be available via `tls::get` within 3059 /// the closure `f` provided. 3060 /// 3061 /// This is used to ensure that `Future::poll`, which doesn't take a `store` 3062 /// parameter, is able to get access to the `store` during future poll 3063 /// methods. 3064 fn set_tls<R>(self, store: &mut dyn VMStore, f: impl FnOnce() -> R) -> R { 3065 struct Reset<'a>(&'a mut dyn VMStore, Option<ComponentInstanceId>); 3066 3067 impl Drop for Reset<'_> { 3068 fn drop(&mut self) { 3069 self.0.concurrent_async_state_mut().current_instance = self.1; 3070 } 3071 } 3072 let prev = mem::replace( 3073 &mut store.concurrent_async_state_mut().current_instance, 3074 Some(self.id().instance()), 3075 ); 3076 let reset = Reset(store, prev); 3077 3078 tls::set(reset.0, f) 3079 } 3080 3081 /// Convenience function to reduce boilerplate. 3082 pub(crate) fn concurrent_state_mut<'a>( 3083 &self, 3084 store: &'a mut StoreOpaque, 3085 ) -> &'a mut ConcurrentState { 3086 self.id().get_mut(store).concurrent_state_mut() 3087 } 3088 3089 pub(crate) fn context_get( 3090 self, 3091 store: &mut dyn VMStore, 3092 caller: RuntimeComponentInstanceIndex, 3093 slot: u32, 3094 ) -> Result<u32> { 3095 self.id().get(store).check_may_leave(caller)?; 3096 self.concurrent_state_mut(store).context_get(slot) 3097 } 3098 3099 pub(crate) fn context_set( 3100 self, 3101 store: &mut dyn VMStore, 3102 caller: RuntimeComponentInstanceIndex, 3103 slot: u32, 3104 value: u32, 3105 ) -> Result<()> { 3106 self.id().get(store).check_may_leave(caller)?; 3107 self.concurrent_state_mut(store).context_set(slot, value) 3108 } 3109 } 3110 3111 /// Trait representing component model ABI async intrinsics and fused adapter 3112 /// helper functions. 3113 /// 3114 /// SAFETY (callers): Most of the methods in this trait accept raw pointers, 3115 /// which must be valid for at least the duration of the call (and possibly for 3116 /// as long as the relevant guest task exists, in the case of `*mut VMFuncRef` 3117 /// pointers used for async calls). 3118 pub trait VMComponentAsyncStore { 3119 /// A helper function for fused adapter modules involving calls where the 3120 /// one of the caller or callee is async. 3121 /// 3122 /// This helper is not used when the caller and callee both use the sync 3123 /// ABI, only when at least one is async is this used. 3124 unsafe fn prepare_call( 3125 &mut self, 3126 instance: Instance, 3127 memory: *mut VMMemoryDefinition, 3128 start: *mut VMFuncRef, 3129 return_: *mut VMFuncRef, 3130 caller_instance: RuntimeComponentInstanceIndex, 3131 callee_instance: RuntimeComponentInstanceIndex, 3132 task_return_type: TypeTupleIndex, 3133 string_encoding: u8, 3134 result_count: u32, 3135 storage: *mut ValRaw, 3136 storage_len: usize, 3137 ) -> Result<()>; 3138 3139 /// A helper function for fused adapter modules involving calls where the 3140 /// caller is sync-lowered but the callee is async-lifted. 3141 unsafe fn sync_start( 3142 &mut self, 3143 instance: Instance, 3144 callback: *mut VMFuncRef, 3145 callee: *mut VMFuncRef, 3146 param_count: u32, 3147 storage: *mut MaybeUninit<ValRaw>, 3148 storage_len: usize, 3149 ) -> Result<()>; 3150 3151 /// A helper function for fused adapter modules involving calls where the 3152 /// caller is async-lowered. 3153 unsafe fn async_start( 3154 &mut self, 3155 instance: Instance, 3156 callback: *mut VMFuncRef, 3157 post_return: *mut VMFuncRef, 3158 callee: *mut VMFuncRef, 3159 param_count: u32, 3160 result_count: u32, 3161 flags: u32, 3162 ) -> Result<u32>; 3163 3164 /// The `future.write` intrinsic. 3165 fn future_write( 3166 &mut self, 3167 instance: Instance, 3168 caller: RuntimeComponentInstanceIndex, 3169 ty: TypeFutureTableIndex, 3170 options: OptionsIndex, 3171 future: u32, 3172 address: u32, 3173 ) -> Result<u32>; 3174 3175 /// The `future.read` intrinsic. 3176 fn future_read( 3177 &mut self, 3178 instance: Instance, 3179 caller: RuntimeComponentInstanceIndex, 3180 ty: TypeFutureTableIndex, 3181 options: OptionsIndex, 3182 future: u32, 3183 address: u32, 3184 ) -> Result<u32>; 3185 3186 /// The `future.drop-writable` intrinsic. 3187 fn future_drop_writable( 3188 &mut self, 3189 instance: Instance, 3190 caller: RuntimeComponentInstanceIndex, 3191 ty: TypeFutureTableIndex, 3192 writer: u32, 3193 ) -> Result<()>; 3194 3195 /// The `stream.write` intrinsic. 3196 fn stream_write( 3197 &mut self, 3198 instance: Instance, 3199 caller: RuntimeComponentInstanceIndex, 3200 ty: TypeStreamTableIndex, 3201 options: OptionsIndex, 3202 stream: u32, 3203 address: u32, 3204 count: u32, 3205 ) -> Result<u32>; 3206 3207 /// The `stream.read` intrinsic. 3208 fn stream_read( 3209 &mut self, 3210 instance: Instance, 3211 caller: RuntimeComponentInstanceIndex, 3212 ty: TypeStreamTableIndex, 3213 options: OptionsIndex, 3214 stream: u32, 3215 address: u32, 3216 count: u32, 3217 ) -> Result<u32>; 3218 3219 /// The "fast-path" implementation of the `stream.write` intrinsic for 3220 /// "flat" (i.e. memcpy-able) payloads. 3221 fn flat_stream_write( 3222 &mut self, 3223 instance: Instance, 3224 caller: RuntimeComponentInstanceIndex, 3225 ty: TypeStreamTableIndex, 3226 options: OptionsIndex, 3227 payload_size: u32, 3228 payload_align: u32, 3229 stream: u32, 3230 address: u32, 3231 count: u32, 3232 ) -> Result<u32>; 3233 3234 /// The "fast-path" implementation of the `stream.read` intrinsic for "flat" 3235 /// (i.e. memcpy-able) payloads. 3236 fn flat_stream_read( 3237 &mut self, 3238 instance: Instance, 3239 caller: RuntimeComponentInstanceIndex, 3240 ty: TypeStreamTableIndex, 3241 options: OptionsIndex, 3242 payload_size: u32, 3243 payload_align: u32, 3244 stream: u32, 3245 address: u32, 3246 count: u32, 3247 ) -> Result<u32>; 3248 3249 /// The `stream.drop-writable` intrinsic. 3250 fn stream_drop_writable( 3251 &mut self, 3252 instance: Instance, 3253 caller: RuntimeComponentInstanceIndex, 3254 ty: TypeStreamTableIndex, 3255 writer: u32, 3256 ) -> Result<()>; 3257 3258 /// The `error-context.debug-message` intrinsic. 3259 fn error_context_debug_message( 3260 &mut self, 3261 instance: Instance, 3262 caller: RuntimeComponentInstanceIndex, 3263 ty: TypeComponentLocalErrorContextTableIndex, 3264 options: OptionsIndex, 3265 err_ctx_handle: u32, 3266 debug_msg_address: u32, 3267 ) -> Result<()>; 3268 } 3269 3270 /// SAFETY: See trait docs. 3271 impl<T: 'static> VMComponentAsyncStore for StoreInner<T> { 3272 unsafe fn prepare_call( 3273 &mut self, 3274 instance: Instance, 3275 memory: *mut VMMemoryDefinition, 3276 start: *mut VMFuncRef, 3277 return_: *mut VMFuncRef, 3278 caller_instance: RuntimeComponentInstanceIndex, 3279 callee_instance: RuntimeComponentInstanceIndex, 3280 task_return_type: TypeTupleIndex, 3281 string_encoding: u8, 3282 result_count_or_max_if_async: u32, 3283 storage: *mut ValRaw, 3284 storage_len: usize, 3285 ) -> Result<()> { 3286 // SAFETY: The `wasmtime_cranelift`-generated code that calls 3287 // this method will have ensured that `storage` is a valid 3288 // pointer containing at least `storage_len` items. 3289 let params = unsafe { std::slice::from_raw_parts(storage, storage_len) }.to_vec(); 3290 3291 unsafe { 3292 instance.prepare_call( 3293 StoreContextMut(self), 3294 start, 3295 return_, 3296 caller_instance, 3297 callee_instance, 3298 task_return_type, 3299 memory, 3300 string_encoding, 3301 match result_count_or_max_if_async { 3302 PREPARE_ASYNC_NO_RESULT => CallerInfo::Async { 3303 params, 3304 has_result: false, 3305 }, 3306 PREPARE_ASYNC_WITH_RESULT => CallerInfo::Async { 3307 params, 3308 has_result: true, 3309 }, 3310 result_count => CallerInfo::Sync { 3311 params, 3312 result_count, 3313 }, 3314 }, 3315 ) 3316 } 3317 } 3318 3319 unsafe fn sync_start( 3320 &mut self, 3321 instance: Instance, 3322 callback: *mut VMFuncRef, 3323 callee: *mut VMFuncRef, 3324 param_count: u32, 3325 storage: *mut MaybeUninit<ValRaw>, 3326 storage_len: usize, 3327 ) -> Result<()> { 3328 unsafe { 3329 instance 3330 .start_call( 3331 StoreContextMut(self), 3332 callback, 3333 ptr::null_mut(), 3334 callee, 3335 param_count, 3336 1, 3337 START_FLAG_ASYNC_CALLEE, 3338 // SAFETY: The `wasmtime_cranelift`-generated code that calls 3339 // this method will have ensured that `storage` is a valid 3340 // pointer containing at least `storage_len` items. 3341 Some(std::slice::from_raw_parts_mut(storage, storage_len)), 3342 ) 3343 .map(drop) 3344 } 3345 } 3346 3347 unsafe fn async_start( 3348 &mut self, 3349 instance: Instance, 3350 callback: *mut VMFuncRef, 3351 post_return: *mut VMFuncRef, 3352 callee: *mut VMFuncRef, 3353 param_count: u32, 3354 result_count: u32, 3355 flags: u32, 3356 ) -> Result<u32> { 3357 unsafe { 3358 instance.start_call( 3359 StoreContextMut(self), 3360 callback, 3361 post_return, 3362 callee, 3363 param_count, 3364 result_count, 3365 flags, 3366 None, 3367 ) 3368 } 3369 } 3370 3371 fn future_write( 3372 &mut self, 3373 instance: Instance, 3374 caller: RuntimeComponentInstanceIndex, 3375 ty: TypeFutureTableIndex, 3376 options: OptionsIndex, 3377 future: u32, 3378 address: u32, 3379 ) -> Result<u32> { 3380 instance.id().get(self).check_may_leave(caller)?; 3381 instance 3382 .guest_write( 3383 StoreContextMut(self), 3384 TransmitIndex::Future(ty), 3385 options, 3386 None, 3387 future, 3388 address, 3389 1, 3390 ) 3391 .map(|result| result.encode()) 3392 } 3393 3394 fn future_read( 3395 &mut self, 3396 instance: Instance, 3397 caller: RuntimeComponentInstanceIndex, 3398 ty: TypeFutureTableIndex, 3399 options: OptionsIndex, 3400 future: u32, 3401 address: u32, 3402 ) -> Result<u32> { 3403 instance.id().get(self).check_may_leave(caller)?; 3404 instance 3405 .guest_read( 3406 StoreContextMut(self), 3407 TransmitIndex::Future(ty), 3408 options, 3409 None, 3410 future, 3411 address, 3412 1, 3413 ) 3414 .map(|result| result.encode()) 3415 } 3416 3417 fn stream_write( 3418 &mut self, 3419 instance: Instance, 3420 caller: RuntimeComponentInstanceIndex, 3421 ty: TypeStreamTableIndex, 3422 options: OptionsIndex, 3423 stream: u32, 3424 address: u32, 3425 count: u32, 3426 ) -> Result<u32> { 3427 instance.id().get(self).check_may_leave(caller)?; 3428 instance 3429 .guest_write( 3430 StoreContextMut(self), 3431 TransmitIndex::Stream(ty), 3432 options, 3433 None, 3434 stream, 3435 address, 3436 count, 3437 ) 3438 .map(|result| result.encode()) 3439 } 3440 3441 fn stream_read( 3442 &mut self, 3443 instance: Instance, 3444 caller: RuntimeComponentInstanceIndex, 3445 ty: TypeStreamTableIndex, 3446 options: OptionsIndex, 3447 stream: u32, 3448 address: u32, 3449 count: u32, 3450 ) -> Result<u32> { 3451 instance.id().get(self).check_may_leave(caller)?; 3452 instance 3453 .guest_read( 3454 StoreContextMut(self), 3455 TransmitIndex::Stream(ty), 3456 options, 3457 None, 3458 stream, 3459 address, 3460 count, 3461 ) 3462 .map(|result| result.encode()) 3463 } 3464 3465 fn future_drop_writable( 3466 &mut self, 3467 instance: Instance, 3468 caller: RuntimeComponentInstanceIndex, 3469 ty: TypeFutureTableIndex, 3470 writer: u32, 3471 ) -> Result<()> { 3472 instance.id().get(self).check_may_leave(caller)?; 3473 instance.guest_drop_writable(StoreContextMut(self), TransmitIndex::Future(ty), writer) 3474 } 3475 3476 fn flat_stream_write( 3477 &mut self, 3478 instance: Instance, 3479 caller: RuntimeComponentInstanceIndex, 3480 ty: TypeStreamTableIndex, 3481 options: OptionsIndex, 3482 payload_size: u32, 3483 payload_align: u32, 3484 stream: u32, 3485 address: u32, 3486 count: u32, 3487 ) -> Result<u32> { 3488 instance.id().get(self).check_may_leave(caller)?; 3489 instance 3490 .guest_write( 3491 StoreContextMut(self), 3492 TransmitIndex::Stream(ty), 3493 options, 3494 Some(FlatAbi { 3495 size: payload_size, 3496 align: payload_align, 3497 }), 3498 stream, 3499 address, 3500 count, 3501 ) 3502 .map(|result| result.encode()) 3503 } 3504 3505 fn flat_stream_read( 3506 &mut self, 3507 instance: Instance, 3508 caller: RuntimeComponentInstanceIndex, 3509 ty: TypeStreamTableIndex, 3510 options: OptionsIndex, 3511 payload_size: u32, 3512 payload_align: u32, 3513 stream: u32, 3514 address: u32, 3515 count: u32, 3516 ) -> Result<u32> { 3517 instance.id().get(self).check_may_leave(caller)?; 3518 instance 3519 .guest_read( 3520 StoreContextMut(self), 3521 TransmitIndex::Stream(ty), 3522 options, 3523 Some(FlatAbi { 3524 size: payload_size, 3525 align: payload_align, 3526 }), 3527 stream, 3528 address, 3529 count, 3530 ) 3531 .map(|result| result.encode()) 3532 } 3533 3534 fn stream_drop_writable( 3535 &mut self, 3536 instance: Instance, 3537 caller: RuntimeComponentInstanceIndex, 3538 ty: TypeStreamTableIndex, 3539 writer: u32, 3540 ) -> Result<()> { 3541 instance.id().get(self).check_may_leave(caller)?; 3542 instance.guest_drop_writable(StoreContextMut(self), TransmitIndex::Stream(ty), writer) 3543 } 3544 3545 fn error_context_debug_message( 3546 &mut self, 3547 instance: Instance, 3548 caller: RuntimeComponentInstanceIndex, 3549 ty: TypeComponentLocalErrorContextTableIndex, 3550 options: OptionsIndex, 3551 err_ctx_handle: u32, 3552 debug_msg_address: u32, 3553 ) -> Result<()> { 3554 instance.id().get(self).check_may_leave(caller)?; 3555 instance.error_context_debug_message( 3556 StoreContextMut(self), 3557 ty, 3558 options, 3559 err_ctx_handle, 3560 debug_msg_address, 3561 ) 3562 } 3563 } 3564 3565 type HostTaskFuture = Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>; 3566 3567 /// Represents the state of a pending host task. 3568 struct HostTask { 3569 common: WaitableCommon, 3570 caller_instance: RuntimeComponentInstanceIndex, 3571 join_handle: Option<JoinHandle>, 3572 } 3573 3574 impl HostTask { 3575 fn new( 3576 caller_instance: RuntimeComponentInstanceIndex, 3577 join_handle: Option<JoinHandle>, 3578 ) -> Self { 3579 Self { 3580 common: WaitableCommon::default(), 3581 caller_instance, 3582 join_handle, 3583 } 3584 } 3585 } 3586 3587 impl TableDebug for HostTask { 3588 fn type_name() -> &'static str { 3589 "HostTask" 3590 } 3591 } 3592 3593 type CallbackFn = Box< 3594 dyn Fn(&mut dyn VMStore, Instance, RuntimeComponentInstanceIndex, Event, u32) -> Result<u32> 3595 + Send 3596 + Sync 3597 + 'static, 3598 >; 3599 3600 /// Represents the caller of a given guest task. 3601 enum Caller { 3602 /// The host called the guest task. 3603 Host { 3604 /// If present, may be used to deliver the result. 3605 tx: Option<oneshot::Sender<LiftedResult>>, 3606 /// Channel to notify once all subtasks spawned by this caller have 3607 /// completed. 3608 /// 3609 /// Note that we'll never actually send anything to this channel; 3610 /// dropping it when the refcount goes to zero is sufficient to notify 3611 /// the receiver. 3612 exit_tx: Arc<oneshot::Sender<()>>, 3613 /// If true, remove the task from the concurrent state that owns it 3614 /// automatically after it completes. 3615 remove_task_automatically: bool, 3616 /// If true, call `post-return` function (if any) automatically. 3617 call_post_return_automatically: bool, 3618 }, 3619 /// Another guest task called the guest task 3620 Guest { 3621 /// The id of the caller 3622 task: TableId<GuestTask>, 3623 /// The instance to use to enforce reentrance rules. 3624 /// 3625 /// Note that this might not be the same as the instance the caller task 3626 /// started executing in given that one or more synchronous guest->guest 3627 /// calls may have occurred involving multiple instances. 3628 instance: RuntimeComponentInstanceIndex, 3629 }, 3630 } 3631 3632 /// Represents a closure and related canonical ABI parameters required to 3633 /// validate a `task.return` call at runtime and lift the result. 3634 struct LiftResult { 3635 lift: RawLift, 3636 ty: TypeTupleIndex, 3637 memory: Option<SendSyncPtr<VMMemoryDefinition>>, 3638 string_encoding: StringEncoding, 3639 } 3640 3641 /// Represents a pending guest task. 3642 struct GuestTask { 3643 /// See `WaitableCommon` 3644 common: WaitableCommon, 3645 /// Closure to lower the parameters passed to this task. 3646 lower_params: Option<RawLower>, 3647 /// See `LiftResult` 3648 lift_result: Option<LiftResult>, 3649 /// A place to stash the type-erased lifted result if it can't be delivered 3650 /// immediately. 3651 result: Option<LiftedResult>, 3652 /// Closure to call the callback function for an async-lifted export, if 3653 /// provided. 3654 callback: Option<CallbackFn>, 3655 /// See `Caller` 3656 caller: Caller, 3657 /// A place to stash the call context for managing resource borrows while 3658 /// switching between guest tasks. 3659 call_context: Option<CallContext>, 3660 /// A place to stash the lowered result for a sync-to-async call until it 3661 /// can be returned to the caller. 3662 sync_result: Option<Option<ValRaw>>, 3663 /// Whether or not the task has been cancelled (i.e. whether the task is 3664 /// permitted to call `task.cancel`). 3665 cancel_sent: bool, 3666 /// Whether or not we've sent a `Status::Starting` event to any current or 3667 /// future waiters for this waitable. 3668 starting_sent: bool, 3669 /// Context-local state used to implement the `context.{get,set}` 3670 /// intrinsics. 3671 context: [u32; 2], 3672 /// Pending guest subtasks created by this task (directly or indirectly). 3673 /// 3674 /// This is used to re-parent subtasks which are still running when their 3675 /// parent task is disposed. 3676 subtasks: HashSet<TableId<GuestTask>>, 3677 /// Scratch waitable set used to watch subtasks during synchronous calls. 3678 sync_call_set: TableId<WaitableSet>, 3679 /// The instance to which the exported function for this guest task belongs. 3680 /// 3681 /// Note that the task may do a sync->sync call via a fused adapter which 3682 /// results in that task executing code in a different instance, and it may 3683 /// call host functions and intrinsics from that other instance. 3684 instance: RuntimeComponentInstanceIndex, 3685 /// If present, a pending `Event::None` or `Event::Cancelled` to be 3686 /// delivered to this task. 3687 event: Option<Event>, 3688 /// If present, indicates that the task is currently waiting on the 3689 /// specified set but may be cancelled and woken immediately. 3690 wake_on_cancel: Option<TableId<WaitableSet>>, 3691 /// The `ExportIndex` of the guest function being called, if known. 3692 function_index: Option<ExportIndex>, 3693 /// Whether or not the task has exited. 3694 exited: bool, 3695 } 3696 3697 impl GuestTask { 3698 fn new( 3699 state: &mut ConcurrentState, 3700 lower_params: RawLower, 3701 lift_result: LiftResult, 3702 caller: Caller, 3703 callback: Option<CallbackFn>, 3704 component_instance: RuntimeComponentInstanceIndex, 3705 ) -> Result<Self> { 3706 let sync_call_set = state.push(WaitableSet::default())?; 3707 3708 Ok(Self { 3709 common: WaitableCommon::default(), 3710 lower_params: Some(lower_params), 3711 lift_result: Some(lift_result), 3712 result: None, 3713 callback, 3714 caller, 3715 call_context: Some(CallContext::default()), 3716 sync_result: None, 3717 cancel_sent: false, 3718 starting_sent: false, 3719 context: [0u32; 2], 3720 subtasks: HashSet::new(), 3721 sync_call_set, 3722 instance: component_instance, 3723 event: None, 3724 wake_on_cancel: None, 3725 function_index: None, 3726 exited: false, 3727 }) 3728 } 3729 3730 /// Dispose of this guest task, reparenting any pending subtasks to the 3731 /// caller. 3732 fn dispose(self, state: &mut ConcurrentState, me: TableId<GuestTask>) -> Result<()> { 3733 // If there are not-yet-delivered completion events for subtasks in 3734 // `self.sync_call_set`, recursively dispose of those subtasks as well. 3735 for waitable in mem::take(&mut state.get_mut(self.sync_call_set)?.ready) { 3736 if let Some(Event::Subtask { 3737 status: Status::Returned | Status::ReturnCancelled, 3738 }) = waitable.common(state)?.event 3739 { 3740 waitable.delete_from(state)?; 3741 } 3742 } 3743 3744 state.delete(self.sync_call_set)?; 3745 3746 // Reparent any pending subtasks to the caller. 3747 match &self.caller { 3748 Caller::Guest { 3749 task, 3750 instance: runtime_instance, 3751 } => { 3752 let task_mut = state.get_mut(*task)?; 3753 let present = task_mut.subtasks.remove(&me); 3754 assert!(present); 3755 3756 for subtask in &self.subtasks { 3757 task_mut.subtasks.insert(*subtask); 3758 } 3759 3760 for subtask in &self.subtasks { 3761 state.get_mut(*subtask)?.caller = Caller::Guest { 3762 task: *task, 3763 instance: *runtime_instance, 3764 }; 3765 } 3766 } 3767 Caller::Host { exit_tx, .. } => { 3768 for subtask in &self.subtasks { 3769 state.get_mut(*subtask)?.caller = Caller::Host { 3770 tx: None, 3771 // Clone `exit_tx` to ensure that it is only dropped 3772 // once all transitive subtasks of the host call have 3773 // exited: 3774 exit_tx: exit_tx.clone(), 3775 remove_task_automatically: true, 3776 call_post_return_automatically: true, 3777 }; 3778 } 3779 } 3780 } 3781 3782 for subtask in self.subtasks { 3783 if state.get_mut(subtask)?.exited { 3784 Waitable::Guest(subtask).delete_from(state)?; 3785 } 3786 } 3787 3788 Ok(()) 3789 } 3790 3791 fn call_post_return_automatically(&self) -> bool { 3792 matches!( 3793 self.caller, 3794 Caller::Guest { .. } 3795 | Caller::Host { 3796 call_post_return_automatically: true, 3797 .. 3798 } 3799 ) 3800 } 3801 } 3802 3803 impl TableDebug for GuestTask { 3804 fn type_name() -> &'static str { 3805 "GuestTask" 3806 } 3807 } 3808 3809 /// Represents state common to all kinds of waitables. 3810 #[derive(Default)] 3811 struct WaitableCommon { 3812 /// The currently pending event for this waitable, if any. 3813 event: Option<Event>, 3814 /// The set to which this waitable belongs, if any. 3815 set: Option<TableId<WaitableSet>>, 3816 /// The handle with which the guest refers to this waitable, if any. 3817 handle: Option<u32>, 3818 } 3819 3820 /// Represents a Component Model Async `waitable`. 3821 #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] 3822 enum Waitable { 3823 /// A host task 3824 Host(TableId<HostTask>), 3825 /// A guest task 3826 Guest(TableId<GuestTask>), 3827 /// The read or write end of a stream or future 3828 Transmit(TableId<TransmitHandle>), 3829 } 3830 3831 impl Waitable { 3832 /// Retrieve the `Waitable` corresponding to the specified guest-visible 3833 /// handle. 3834 fn from_instance( 3835 state: Pin<&mut ComponentInstance>, 3836 caller_instance: RuntimeComponentInstanceIndex, 3837 waitable: u32, 3838 ) -> Result<Self> { 3839 use crate::runtime::vm::component::Waitable; 3840 3841 let (waitable, kind) = state.guest_tables().0[caller_instance].waitable_rep(waitable)?; 3842 3843 Ok(match kind { 3844 Waitable::Subtask { is_host: true } => Self::Host(TableId::new(waitable)), 3845 Waitable::Subtask { is_host: false } => Self::Guest(TableId::new(waitable)), 3846 Waitable::Stream | Waitable::Future => Self::Transmit(TableId::new(waitable)), 3847 }) 3848 } 3849 3850 /// Retrieve the host-visible identifier for this `Waitable`. 3851 fn rep(&self) -> u32 { 3852 match self { 3853 Self::Host(id) => id.rep(), 3854 Self::Guest(id) => id.rep(), 3855 Self::Transmit(id) => id.rep(), 3856 } 3857 } 3858 3859 /// Move this `Waitable` to the specified set (when `set` is `Some(_)`) or 3860 /// remove it from any set it may currently belong to (when `set` is 3861 /// `None`). 3862 fn join(&self, state: &mut ConcurrentState, set: Option<TableId<WaitableSet>>) -> Result<()> { 3863 log::trace!("waitable {self:?} join set {set:?}",); 3864 3865 let old = mem::replace(&mut self.common(state)?.set, set); 3866 3867 if let Some(old) = old { 3868 match *self { 3869 Waitable::Host(id) => state.remove_child(id, old), 3870 Waitable::Guest(id) => state.remove_child(id, old), 3871 Waitable::Transmit(id) => state.remove_child(id, old), 3872 }?; 3873 3874 state.get_mut(old)?.ready.remove(self); 3875 } 3876 3877 if let Some(set) = set { 3878 match *self { 3879 Waitable::Host(id) => state.add_child(id, set), 3880 Waitable::Guest(id) => state.add_child(id, set), 3881 Waitable::Transmit(id) => state.add_child(id, set), 3882 }?; 3883 3884 if self.common(state)?.event.is_some() { 3885 self.mark_ready(state)?; 3886 } 3887 } 3888 3889 Ok(()) 3890 } 3891 3892 /// Retrieve mutable access to the `WaitableCommon` for this `Waitable`. 3893 fn common<'a>(&self, state: &'a mut ConcurrentState) -> Result<&'a mut WaitableCommon> { 3894 Ok(match self { 3895 Self::Host(id) => &mut state.get_mut(*id)?.common, 3896 Self::Guest(id) => &mut state.get_mut(*id)?.common, 3897 Self::Transmit(id) => &mut state.get_mut(*id)?.common, 3898 }) 3899 } 3900 3901 /// Set or clear the pending event for this waitable and either deliver it 3902 /// to the first waiter, if any, or mark it as ready to be delivered to the 3903 /// next waiter that arrives. 3904 fn set_event(&self, state: &mut ConcurrentState, event: Option<Event>) -> Result<()> { 3905 log::trace!("set event for {self:?}: {event:?}"); 3906 self.common(state)?.event = event; 3907 self.mark_ready(state) 3908 } 3909 3910 /// Take the pending event from this waitable, leaving `None` in its place. 3911 fn take_event(&self, state: &mut ConcurrentState) -> Result<Option<Event>> { 3912 let common = self.common(state)?; 3913 let event = common.event.take(); 3914 if let Some(set) = self.common(state)?.set { 3915 state.get_mut(set)?.ready.remove(self); 3916 } 3917 Ok(event) 3918 } 3919 3920 /// Deliver the current event for this waitable to the first waiter, if any, 3921 /// or else mark it as ready to be delivered to the next waiter that 3922 /// arrives. 3923 fn mark_ready(&self, state: &mut ConcurrentState) -> Result<()> { 3924 if let Some(set) = self.common(state)?.set { 3925 state.get_mut(set)?.ready.insert(*self); 3926 if let Some((task, mode)) = state.get_mut(set)?.waiting.pop_first() { 3927 let wake_on_cancel = state.get_mut(task)?.wake_on_cancel.take(); 3928 assert!(wake_on_cancel.is_none() || wake_on_cancel == Some(set)); 3929 3930 let item = match mode { 3931 WaitMode::Fiber(fiber) => WorkItem::ResumeFiber(fiber), 3932 WaitMode::Callback => WorkItem::GuestCall(GuestCall { 3933 task, 3934 kind: GuestCallKind::DeliverEvent { set: Some(set) }, 3935 }), 3936 }; 3937 state.push_high_priority(item); 3938 } 3939 } 3940 Ok(()) 3941 } 3942 3943 /// Handle the imminent delivery of the specified event, e.g. by updating 3944 /// the state of the stream or future. 3945 fn on_delivery(&self, instance: Pin<&mut ComponentInstance>, event: Event) { 3946 match event { 3947 Event::FutureRead { 3948 pending: Some((ty, handle)), 3949 .. 3950 } 3951 | Event::FutureWrite { 3952 pending: Some((ty, handle)), 3953 .. 3954 } => { 3955 let runtime_instance = instance.component().types()[ty].instance; 3956 let (rep, state) = instance.guest_tables().0[runtime_instance] 3957 .future_rep(ty, handle) 3958 .unwrap(); 3959 assert_eq!(rep, self.rep()); 3960 assert_eq!(*state, TransmitLocalState::Busy); 3961 *state = match event { 3962 Event::FutureRead { .. } => TransmitLocalState::Read { done: false }, 3963 Event::FutureWrite { .. } => TransmitLocalState::Write { done: false }, 3964 _ => unreachable!(), 3965 }; 3966 } 3967 Event::StreamRead { 3968 pending: Some((ty, handle)), 3969 code, 3970 } 3971 | Event::StreamWrite { 3972 pending: Some((ty, handle)), 3973 code, 3974 } => { 3975 let runtime_instance = instance.component().types()[ty].instance; 3976 let (rep, state) = instance.guest_tables().0[runtime_instance] 3977 .stream_rep(ty, handle) 3978 .unwrap(); 3979 assert_eq!(rep, self.rep()); 3980 assert_eq!(*state, TransmitLocalState::Busy); 3981 let done = matches!(code, ReturnCode::Dropped(_)); 3982 *state = match event { 3983 Event::StreamRead { .. } => TransmitLocalState::Read { done }, 3984 Event::StreamWrite { .. } => TransmitLocalState::Write { done }, 3985 _ => unreachable!(), 3986 }; 3987 } 3988 _ => {} 3989 } 3990 } 3991 3992 /// Remove this waitable from the instance's rep table. 3993 fn delete_from(&self, state: &mut ConcurrentState) -> Result<()> { 3994 match self { 3995 Self::Host(task) => { 3996 log::trace!("delete host task {task:?}"); 3997 state.delete(*task)?; 3998 } 3999 Self::Guest(task) => { 4000 log::trace!("delete guest task {task:?}"); 4001 state.delete(*task)?.dispose(state, *task)?; 4002 } 4003 Self::Transmit(task) => { 4004 state.delete(*task)?; 4005 } 4006 } 4007 4008 Ok(()) 4009 } 4010 } 4011 4012 impl fmt::Debug for Waitable { 4013 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 4014 match self { 4015 Self::Host(id) => write!(f, "{id:?}"), 4016 Self::Guest(id) => write!(f, "{id:?}"), 4017 Self::Transmit(id) => write!(f, "{id:?}"), 4018 } 4019 } 4020 } 4021 4022 /// Represents a Component Model Async `waitable-set`. 4023 #[derive(Default)] 4024 struct WaitableSet { 4025 /// Which waitables in this set have pending events, if any. 4026 ready: BTreeSet<Waitable>, 4027 /// Which guest tasks are currently waiting on this set, if any. 4028 waiting: BTreeMap<TableId<GuestTask>, WaitMode>, 4029 } 4030 4031 impl TableDebug for WaitableSet { 4032 fn type_name() -> &'static str { 4033 "WaitableSet" 4034 } 4035 } 4036 4037 /// Type-erased closure to lower the parameters for a guest task. 4038 type RawLower = Box< 4039 dyn FnOnce(&mut dyn VMStore, Instance, &mut [MaybeUninit<ValRaw>]) -> Result<()> + Send + Sync, 4040 >; 4041 4042 /// Type-erased closure to lift the result for a guest task. 4043 type RawLift = Box< 4044 dyn FnOnce(&mut dyn VMStore, Instance, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>> 4045 + Send 4046 + Sync, 4047 >; 4048 4049 /// Type erased result of a guest task which may be downcast to the expected 4050 /// type by a host caller (or simply ignored in the case of a guest caller; see 4051 /// `DummyResult`). 4052 type LiftedResult = Box<dyn Any + Send + Sync>; 4053 4054 /// Used to return a result from a `LiftFn` when the actual result has already 4055 /// been lowered to a guest task's stack and linear memory. 4056 struct DummyResult; 4057 4058 /// Represents the state of a currently executing fiber which has been resumed 4059 /// via `self::poll_fn`. 4060 pub(crate) struct AsyncState { 4061 /// The current instance being polled, if any, which is used to perform 4062 /// checks to ensure that futures are always polled within the correct 4063 /// instance. 4064 current_instance: Option<ComponentInstanceId>, 4065 } 4066 4067 impl Default for AsyncState { 4068 fn default() -> Self { 4069 Self { 4070 current_instance: None, 4071 } 4072 } 4073 } 4074 4075 /// Represents the Component Model Async state of a (sub-)component instance. 4076 #[derive(Default)] 4077 struct InstanceState { 4078 /// Whether backpressure is set for this instance (enabled if >0) 4079 backpressure: u16, 4080 /// Whether this instance can be entered 4081 do_not_enter: bool, 4082 /// Pending calls for this instance which require `Self::backpressure` to be 4083 /// `true` and/or `Self::do_not_enter` to be false before they can proceed. 4084 pending: BTreeMap<TableId<GuestTask>, GuestCallKind>, 4085 } 4086 4087 /// Represents the Component Model Async state of a top-level component instance 4088 /// (i.e. a `super::ComponentInstance`). 4089 pub struct ConcurrentState { 4090 /// The currently running guest task, if any. 4091 guest_task: Option<TableId<GuestTask>>, 4092 /// The set of pending host and background tasks, if any. 4093 /// 4094 /// See `ComponentInstance::poll_until` for where we temporarily take this 4095 /// out, poll it, then put it back to avoid any mutable aliasing hazards. 4096 futures: AlwaysMut<Option<FuturesUnordered<HostTaskFuture>>>, 4097 /// The table of waitables, waitable sets, etc. 4098 table: AlwaysMut<ResourceTable>, 4099 /// Per (sub-)component instance states. 4100 /// 4101 /// See `InstanceState` for details and note that this map is lazily 4102 /// populated as needed. 4103 // TODO: this can and should be a `PrimaryMap` 4104 instance_states: HashMap<RuntimeComponentInstanceIndex, InstanceState>, 4105 /// The "high priority" work queue for this instance's event loop. 4106 high_priority: Vec<WorkItem>, 4107 /// The "high priority" work queue for this instance's event loop. 4108 low_priority: Vec<WorkItem>, 4109 /// A place to stash the reason a fiber is suspending so that the code which 4110 /// resumed it will know under what conditions the fiber should be resumed 4111 /// again. 4112 suspend_reason: Option<SuspendReason>, 4113 /// A cached fiber which is waiting for work to do. 4114 /// 4115 /// This helps us avoid creating a new fiber for each `GuestCall` work item. 4116 worker: Option<StoreFiber<'static>>, 4117 /// A place to stash the work item for which we're resuming a worker fiber. 4118 worker_item: Option<WorkerItem>, 4119 4120 /// Reference counts for all component error contexts 4121 /// 4122 /// NOTE: it is possible the global ref count to be *greater* than the sum of 4123 /// (sub)component ref counts as tracked by `error_context_tables`, for 4124 /// example when the host holds one or more references to error contexts. 4125 /// 4126 /// The key of this primary map is often referred to as the "rep" (i.e. host-side 4127 /// component-wide representation) of the index into concurrent state for a given 4128 /// stored `ErrorContext`. 4129 /// 4130 /// Stated another way, `TypeComponentGlobalErrorContextTableIndex` is essentially the same 4131 /// as a `TableId<ErrorContextState>`. 4132 global_error_context_ref_counts: 4133 BTreeMap<TypeComponentGlobalErrorContextTableIndex, GlobalErrorContextRefCount>, 4134 4135 /// Mirror of type information in `ComponentInstance`, placed here for 4136 /// convenience at the cost of an extra `Arc` clone. 4137 component: Component, 4138 } 4139 4140 impl ConcurrentState { 4141 pub(crate) fn new(component: &Component) -> Self { 4142 Self { 4143 guest_task: None, 4144 table: AlwaysMut::new(ResourceTable::new()), 4145 futures: AlwaysMut::new(Some(FuturesUnordered::new())), 4146 instance_states: HashMap::new(), 4147 high_priority: Vec::new(), 4148 low_priority: Vec::new(), 4149 suspend_reason: None, 4150 worker: None, 4151 worker_item: None, 4152 global_error_context_ref_counts: BTreeMap::new(), 4153 component: component.clone(), 4154 } 4155 } 4156 4157 /// Take ownership of any fibers and futures owned by this object. 4158 /// 4159 /// This should be used when disposing of the `Store` containing this object 4160 /// in order to gracefully resolve any and all fibers using 4161 /// `StoreFiber::dispose`. This is necessary to avoid possible 4162 /// use-after-free bugs due to fibers which may still have access to the 4163 /// `Store`. 4164 /// 4165 /// Additionally, the futures collected with this function should be dropped 4166 /// within a `tls::set` call, which will ensure than any futures closing 4167 /// over an `&Accessor` will have access to the store when dropped, allowing 4168 /// e.g. `WithAccessor[AndValue]` instances to be disposed of without 4169 /// panicking. 4170 /// 4171 /// Note that this will leave the object in an inconsistent and unusable 4172 /// state, so it should only be used just prior to dropping it. 4173 pub(crate) fn take_fibers_and_futures( 4174 &mut self, 4175 fibers: &mut Vec<StoreFiber<'static>>, 4176 futures: &mut Vec<FuturesUnordered<HostTaskFuture>>, 4177 ) { 4178 for entry in self.table.get_mut().iter_mut() { 4179 if let Some(set) = entry.downcast_mut::<WaitableSet>() { 4180 for mode in mem::take(&mut set.waiting).into_values() { 4181 if let WaitMode::Fiber(fiber) = mode { 4182 fibers.push(fiber); 4183 } 4184 } 4185 } 4186 } 4187 4188 if let Some(fiber) = self.worker.take() { 4189 fibers.push(fiber); 4190 } 4191 4192 let mut take_items = |list| { 4193 for item in mem::take(list) { 4194 match item { 4195 WorkItem::ResumeFiber(fiber) => { 4196 fibers.push(fiber); 4197 } 4198 WorkItem::PushFuture(future) => { 4199 self.futures 4200 .get_mut() 4201 .as_mut() 4202 .unwrap() 4203 .push(future.into_inner()); 4204 } 4205 _ => {} 4206 } 4207 } 4208 }; 4209 4210 take_items(&mut self.high_priority); 4211 take_items(&mut self.low_priority); 4212 4213 if let Some(them) = self.futures.get_mut().take() { 4214 futures.push(them); 4215 } 4216 } 4217 4218 fn instance_state(&mut self, instance: RuntimeComponentInstanceIndex) -> &mut InstanceState { 4219 self.instance_states.entry(instance).or_default() 4220 } 4221 4222 fn push<V: Send + Sync + 'static>( 4223 &mut self, 4224 value: V, 4225 ) -> Result<TableId<V>, ResourceTableError> { 4226 self.table.get_mut().push(value).map(TableId::from) 4227 } 4228 4229 fn get_mut<V: 'static>(&mut self, id: TableId<V>) -> Result<&mut V, ResourceTableError> { 4230 self.table.get_mut().get_mut(&Resource::from(id)) 4231 } 4232 4233 pub fn add_child<T: 'static, U: 'static>( 4234 &mut self, 4235 child: TableId<T>, 4236 parent: TableId<U>, 4237 ) -> Result<(), ResourceTableError> { 4238 self.table 4239 .get_mut() 4240 .add_child(Resource::from(child), Resource::from(parent)) 4241 } 4242 4243 pub fn remove_child<T: 'static, U: 'static>( 4244 &mut self, 4245 child: TableId<T>, 4246 parent: TableId<U>, 4247 ) -> Result<(), ResourceTableError> { 4248 self.table 4249 .get_mut() 4250 .remove_child(Resource::from(child), Resource::from(parent)) 4251 } 4252 4253 fn delete<V: 'static>(&mut self, id: TableId<V>) -> Result<V, ResourceTableError> { 4254 self.table.get_mut().delete(Resource::from(id)) 4255 } 4256 4257 fn push_future(&mut self, future: HostTaskFuture) { 4258 // Note that we can't directly push to `ConcurrentState::futures` here 4259 // since this may be called from a future that's being polled inside 4260 // `Self::poll_until`, which temporarily removes the `FuturesUnordered` 4261 // so it has exclusive access while polling it. Therefore, we push a 4262 // work item to the "high priority" queue, which will actually push to 4263 // `ConcurrentState::futures` later. 4264 self.push_high_priority(WorkItem::PushFuture(AlwaysMut::new(future))); 4265 } 4266 4267 fn push_high_priority(&mut self, item: WorkItem) { 4268 log::trace!("push high priority: {item:?}"); 4269 self.high_priority.push(item); 4270 } 4271 4272 fn push_low_priority(&mut self, item: WorkItem) { 4273 log::trace!("push low priority: {item:?}"); 4274 self.low_priority.push(item); 4275 } 4276 4277 /// Determine whether the instance associated with the specified guest task 4278 /// may be entered (i.e. is not already on the async call stack). 4279 /// 4280 /// This is an additional check on top of the "may_enter" instance flag; 4281 /// it's needed because async-lifted exports with callback functions must 4282 /// not call their own instances directly or indirectly, and due to the 4283 /// "stackless" nature of callback-enabled guest tasks this may happen even 4284 /// if there are no activation records on the stack (i.e. the "may_enter" 4285 /// field is `true`) for that instance. 4286 fn may_enter(&mut self, mut guest_task: TableId<GuestTask>) -> bool { 4287 let guest_instance = self.get_mut(guest_task).unwrap().instance; 4288 4289 // Walk the task tree back to the root, looking for potential 4290 // reentrance. 4291 // 4292 // TODO: This could be optimized by maintaining a per-`GuestTask` bitset 4293 // such that each bit represents and instance which has been entered by 4294 // that task or an ancestor of that task, in which case this would be a 4295 // constant time check. 4296 loop { 4297 match &self.get_mut(guest_task).unwrap().caller { 4298 Caller::Host { .. } => break true, 4299 Caller::Guest { task, instance } => { 4300 if *instance == guest_instance { 4301 break false; 4302 } else { 4303 guest_task = *task; 4304 } 4305 } 4306 } 4307 } 4308 } 4309 4310 /// Record that we're about to enter a (sub-)component instance which does 4311 /// not support more than one concurrent, stackful activation, meaning it 4312 /// cannot be entered again until the next call returns. 4313 fn enter_instance(&mut self, instance: RuntimeComponentInstanceIndex) { 4314 self.instance_state(instance).do_not_enter = true; 4315 } 4316 4317 /// Record that we've exited a (sub-)component instance previously entered 4318 /// with `Self::enter_instance` and then calls `Self::partition_pending`. 4319 /// See the documentation for the latter for details. 4320 fn exit_instance(&mut self, instance: RuntimeComponentInstanceIndex) -> Result<()> { 4321 self.instance_state(instance).do_not_enter = false; 4322 self.partition_pending(instance) 4323 } 4324 4325 /// Iterate over `InstanceState::pending`, moving any ready items into the 4326 /// "high priority" work item queue. 4327 /// 4328 /// See `GuestCall::is_ready` for details. 4329 fn partition_pending(&mut self, instance: RuntimeComponentInstanceIndex) -> Result<()> { 4330 for (task, kind) in mem::take(&mut self.instance_state(instance).pending).into_iter() { 4331 let call = GuestCall { task, kind }; 4332 if call.is_ready(self)? { 4333 self.push_high_priority(WorkItem::GuestCall(call)); 4334 } else { 4335 self.instance_state(instance) 4336 .pending 4337 .insert(call.task, call.kind); 4338 } 4339 } 4340 4341 Ok(()) 4342 } 4343 4344 /// Implements the `backpressure.{set,inc,dec}` intrinsics. 4345 pub(crate) fn backpressure_modify( 4346 &mut self, 4347 caller_instance: RuntimeComponentInstanceIndex, 4348 modify: impl FnOnce(u16) -> Option<u16>, 4349 ) -> Result<()> { 4350 let state = self.instance_state(caller_instance); 4351 let old = state.backpressure; 4352 let new = modify(old).ok_or_else(|| anyhow!("backpressure counter overflow"))?; 4353 state.backpressure = new; 4354 4355 if old > 0 && new == 0 { 4356 // Backpressure was previously enabled and is now disabled; move any 4357 // newly-eligible guest calls to the "high priority" queue. 4358 self.partition_pending(caller_instance)?; 4359 } 4360 4361 Ok(()) 4362 } 4363 4364 /// Implements the `context.get` intrinsic. 4365 pub(crate) fn context_get(&mut self, slot: u32) -> Result<u32> { 4366 let task = self.guest_task.unwrap(); 4367 let val = self.get_mut(task)?.context[usize::try_from(slot).unwrap()]; 4368 log::trace!("context_get {task:?} slot {slot} val {val:#x}"); 4369 Ok(val) 4370 } 4371 4372 /// Implements the `context.set` intrinsic. 4373 pub(crate) fn context_set(&mut self, slot: u32, val: u32) -> Result<()> { 4374 let task = self.guest_task.unwrap(); 4375 log::trace!("context_set {task:?} slot {slot} val {val:#x}"); 4376 self.get_mut(task)?.context[usize::try_from(slot).unwrap()] = val; 4377 Ok(()) 4378 } 4379 4380 fn options(&self, options: OptionsIndex) -> &CanonicalOptions { 4381 &self.component.env_component().options[options] 4382 } 4383 } 4384 4385 /// Provide a type hint to compiler about the shape of a parameter lower 4386 /// closure. 4387 fn for_any_lower< 4388 F: FnOnce(&mut dyn VMStore, Instance, &mut [MaybeUninit<ValRaw>]) -> Result<()> + Send + Sync, 4389 >( 4390 fun: F, 4391 ) -> F { 4392 fun 4393 } 4394 4395 /// Provide a type hint to compiler about the shape of a result lift closure. 4396 fn for_any_lift< 4397 F: FnOnce(&mut dyn VMStore, Instance, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>> 4398 + Send 4399 + Sync, 4400 >( 4401 fun: F, 4402 ) -> F { 4403 fun 4404 } 4405 4406 /// Wrap the specified future in a `poll_fn` which asserts that the future is 4407 /// only polled from the event loop of the specified `Instance`. 4408 /// 4409 /// See `Instance::run_concurrent` for details. 4410 fn checked<F: Future + Send + 'static>( 4411 instance: Instance, 4412 fut: F, 4413 ) -> impl Future<Output = F::Output> + Send + 'static { 4414 async move { 4415 let mut fut = pin!(fut); 4416 future::poll_fn(move |cx| { 4417 let message = "\ 4418 `Future`s which depend on asynchronous component tasks, streams, or \ 4419 futures to complete may only be polled from the event loop of the \ 4420 instance from which they originated. Please use \ 4421 `Instance::{run_concurrent,spawn}` to poll or await them.\ 4422 "; 4423 tls::try_get(|store| { 4424 let matched = match store { 4425 tls::TryGet::Some(store) => { 4426 let a = store.concurrent_async_state_mut().current_instance; 4427 a == Some(instance.id().instance()) 4428 } 4429 tls::TryGet::Taken | tls::TryGet::None => false, 4430 }; 4431 4432 if !matched { 4433 panic!("{message}") 4434 } 4435 }); 4436 fut.as_mut().poll(cx) 4437 }) 4438 .await 4439 } 4440 } 4441 4442 /// Assert that `Instance::run_concurrent` has not been called from within an 4443 /// instance's event loop. 4444 fn check_recursive_run() { 4445 tls::try_get(|store| { 4446 if !matches!(store, tls::TryGet::None) { 4447 panic!("Recursive `Instance::run_concurrent` calls not supported") 4448 } 4449 }); 4450 } 4451 4452 fn unpack_callback_code(code: u32) -> (u32, u32) { 4453 (code & 0xF, code >> 4) 4454 } 4455 4456 /// Helper struct for packaging parameters to be passed to 4457 /// `ComponentInstance::waitable_check` for calls to `waitable-set.wait` or 4458 /// `waitable-set.poll`. 4459 struct WaitableCheckParams { 4460 set: TableId<WaitableSet>, 4461 options: OptionsIndex, 4462 payload: u32, 4463 } 4464 4465 /// Helper enum for passing parameters to `ComponentInstance::waitable_check`. 4466 enum WaitableCheck { 4467 Wait(WaitableCheckParams), 4468 Poll(WaitableCheckParams), 4469 Yield, 4470 } 4471 4472 /// Represents a guest task called from the host, prepared using `prepare_call`. 4473 pub(crate) struct PreparedCall<R> { 4474 /// The guest export to be called 4475 handle: Func, 4476 /// The guest task created by `prepare_call` 4477 task: TableId<GuestTask>, 4478 /// The number of lowered core Wasm parameters to pass to the call. 4479 param_count: usize, 4480 /// The `oneshot::Receiver` to which the result of the call will be 4481 /// delivered when it is available. 4482 rx: oneshot::Receiver<LiftedResult>, 4483 /// The `oneshot::Receiver` which will resolve when the task -- and any 4484 /// transitive subtasks -- have all exited. 4485 exit_rx: oneshot::Receiver<()>, 4486 _phantom: PhantomData<R>, 4487 } 4488 4489 impl<R> PreparedCall<R> { 4490 /// Get a copy of the `TaskId` for this `PreparedCall`. 4491 pub(crate) fn task_id(&self) -> TaskId { 4492 TaskId { 4493 handle: self.handle, 4494 task: self.task, 4495 } 4496 } 4497 } 4498 4499 /// Represents a task created by `prepare_call`. 4500 pub(crate) struct TaskId { 4501 handle: Func, 4502 task: TableId<GuestTask>, 4503 } 4504 4505 impl TaskId { 4506 /// Remove the specified task from the concurrent state to which it belongs. 4507 /// 4508 /// This must be used with care to avoid use-after-delete or double-delete 4509 /// bugs. Specifically, it should only be called on tasks created with the 4510 /// `remove_task_automatically` parameter to `prepare_call` set to `false`, 4511 /// which tells the runtime that the caller is responsible for removing the 4512 /// task from the state; otherwise, it will be removed automatically. Also, 4513 /// it should only be called once for a given task, and only after either 4514 /// the task has completed or the instance has trapped. 4515 pub(crate) fn remove<T>(&self, store: StoreContextMut<T>) -> Result<()> { 4516 Waitable::Guest(self.task).delete_from(self.handle.instance().concurrent_state_mut(store.0)) 4517 } 4518 } 4519 4520 /// Prepare a call to the specified exported Wasm function, providing functions 4521 /// for lowering the parameters and lifting the result. 4522 /// 4523 /// To enqueue the returned `PreparedCall` in the `ComponentInstance`'s event 4524 /// loop, use `queue_call`. 4525 pub(crate) fn prepare_call<T, R>( 4526 mut store: StoreContextMut<T>, 4527 handle: Func, 4528 param_count: usize, 4529 remove_task_automatically: bool, 4530 call_post_return_automatically: bool, 4531 lower_params: impl FnOnce(Func, StoreContextMut<T>, &mut [MaybeUninit<ValRaw>]) -> Result<()> 4532 + Send 4533 + Sync 4534 + 'static, 4535 lift_result: impl FnOnce(Func, &mut StoreOpaque, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>> 4536 + Send 4537 + Sync 4538 + 'static, 4539 ) -> Result<PreparedCall<R>> { 4540 let (options, _flags, ty, raw_options) = handle.abi_info(store.0); 4541 4542 let instance = handle.instance().id().get(store.0); 4543 let task_return_type = instance.component().types()[ty].results; 4544 let component_instance = raw_options.instance; 4545 let callback = options.callback(); 4546 let memory = options.memory_raw().map(SendSyncPtr::new); 4547 let string_encoding = options.string_encoding(); 4548 let token = StoreToken::new(store.as_context_mut()); 4549 let state = handle.instance().concurrent_state_mut(store.0); 4550 4551 assert!(state.guest_task.is_none()); 4552 4553 let (tx, rx) = oneshot::channel(); 4554 let (exit_tx, exit_rx) = oneshot::channel(); 4555 4556 let mut task = GuestTask::new( 4557 state, 4558 Box::new(for_any_lower(move |store, instance, params| { 4559 debug_assert!(instance.id() == handle.instance().id()); 4560 lower_params(handle, token.as_context_mut(store), params) 4561 })), 4562 LiftResult { 4563 lift: Box::new(for_any_lift(move |store, instance, result| { 4564 debug_assert!(instance.id() == handle.instance().id()); 4565 lift_result(handle, store, result) 4566 })), 4567 ty: task_return_type, 4568 memory, 4569 string_encoding, 4570 }, 4571 Caller::Host { 4572 tx: Some(tx), 4573 exit_tx: Arc::new(exit_tx), 4574 remove_task_automatically, 4575 call_post_return_automatically, 4576 }, 4577 callback.map(|callback| { 4578 let callback = SendSyncPtr::new(callback); 4579 Box::new( 4580 move |store: &mut dyn VMStore, 4581 instance: Instance, 4582 runtime_instance, 4583 event, 4584 handle| { 4585 let store = token.as_context_mut(store); 4586 // SAFETY: Per the contract of `prepare_call`, the callback 4587 // will remain valid at least as long is this task exists. 4588 unsafe { 4589 instance.call_callback( 4590 store, 4591 runtime_instance, 4592 callback, 4593 event, 4594 handle, 4595 call_post_return_automatically, 4596 ) 4597 } 4598 }, 4599 ) as CallbackFn 4600 }), 4601 component_instance, 4602 )?; 4603 task.function_index = Some(handle.index()); 4604 4605 let task = state.push(task)?; 4606 4607 Ok(PreparedCall { 4608 handle, 4609 task, 4610 param_count, 4611 rx, 4612 exit_rx, 4613 _phantom: PhantomData, 4614 }) 4615 } 4616 4617 /// Queue a call previously prepared using `prepare_call` to be run as part of 4618 /// the associated `ComponentInstance`'s event loop. 4619 /// 4620 /// The returned future will resolve to the result once it is available, but 4621 /// must only be polled via the instance's event loop. See 4622 /// `Instance::run_concurrent` for details. 4623 pub(crate) fn queue_call<T: 'static, R: Send + 'static>( 4624 mut store: StoreContextMut<T>, 4625 prepared: PreparedCall<R>, 4626 ) -> Result<impl Future<Output = Result<(R, oneshot::Receiver<()>)>> + Send + 'static + use<T, R>> { 4627 let PreparedCall { 4628 handle, 4629 task, 4630 param_count, 4631 rx, 4632 exit_rx, 4633 .. 4634 } = prepared; 4635 4636 queue_call0(store.as_context_mut(), handle, task, param_count)?; 4637 4638 Ok(checked( 4639 handle.instance(), 4640 rx.map(move |result| { 4641 result 4642 .map(|v| (*v.downcast().unwrap(), exit_rx)) 4643 .map_err(anyhow::Error::from) 4644 }), 4645 )) 4646 } 4647 4648 /// Queue a call previously prepared using `prepare_call` to be run as part of 4649 /// the associated `ComponentInstance`'s event loop. 4650 fn queue_call0<T: 'static>( 4651 store: StoreContextMut<T>, 4652 handle: Func, 4653 guest_task: TableId<GuestTask>, 4654 param_count: usize, 4655 ) -> Result<()> { 4656 let (options, flags, _ty, raw_options) = handle.abi_info(store.0); 4657 let is_concurrent = raw_options.async_; 4658 let instance = handle.instance(); 4659 let callee = handle.lifted_core_func(store.0); 4660 let callback = options.callback(); 4661 let post_return = handle.post_return_core_func(store.0); 4662 4663 log::trace!("queueing call {guest_task:?}"); 4664 4665 let instance_flags = if callback.is_none() { 4666 None 4667 } else { 4668 Some(flags) 4669 }; 4670 4671 // SAFETY: `callee`, `callback`, and `post_return` are valid pointers 4672 // (with signatures appropriate for this call) and will remain valid as 4673 // long as this instance is valid. 4674 unsafe { 4675 instance.queue_call( 4676 store, 4677 guest_task, 4678 SendSyncPtr::new(callee), 4679 param_count, 4680 1, 4681 instance_flags, 4682 is_concurrent, 4683 callback.map(SendSyncPtr::new), 4684 post_return.map(SendSyncPtr::new), 4685 ) 4686 } 4687 } 4688