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