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