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