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