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