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