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