1fa70f025SJoel Dice //! Runtime support for the Component Model Async ABI.
2fa70f025SJoel Dice //!
3fa70f025SJoel Dice //! This module and its submodules provide host runtime support for Component
4fa70f025SJoel Dice //! Model Async features such as async-lifted exports, async-lowered imports,
5fa70f025SJoel Dice //! streams, futures, and related intrinsics.  See [the Async
6696ef2cbSMozirDmitriy //! Explainer](https://github.com/WebAssembly/component-model/blob/main/design/mvp/Concurrency.md)
7fa70f025SJoel Dice //! for a high-level overview.
8fa70f025SJoel Dice //!
9fa70f025SJoel Dice //! At the core of this support is an event loop which schedules and switches
10fa70f025SJoel Dice //! between guest tasks and any host tasks they create.  Each
117e39c25eSJoel Dice //! `Store` will have at most one event loop running at any given
12fa70f025SJoel Dice //! time, and that loop may be suspended and resumed by the host embedder using
137e39c25eSJoel Dice //! e.g. `StoreContextMut::run_concurrent`.  The `StoreContextMut::poll_until`
14da265515SAlex Crichton //! function contains the loop itself, while the
157e39c25eSJoel Dice //! `StoreOpaque::concurrent_state` field holds its state.
16fa70f025SJoel Dice //!
17fa70f025SJoel Dice //! # Public API Overview
18fa70f025SJoel Dice //!
19fa70f025SJoel Dice //! ## Top-level API (e.g. kicking off host->guest calls and driving the event loop)
20fa70f025SJoel Dice //!
21fa70f025SJoel Dice //! - `[Typed]Func::call_concurrent`: Start a host->guest call to an
22fa70f025SJoel Dice //! async-lifted or sync-lifted import, creating a guest task.
23fa70f025SJoel Dice //!
247e39c25eSJoel Dice //! - `StoreContextMut::run_concurrent`: Run the event loop for the specified
257e39c25eSJoel Dice //! instance, allowing any and all tasks belonging to that instance to make
267e39c25eSJoel Dice //! progress.
27fa70f025SJoel Dice //!
287e39c25eSJoel Dice //! - `StoreContextMut::spawn`: Run a background task as part of the event loop
297e39c25eSJoel Dice //! for the specified instance.
30fa70f025SJoel Dice //!
317e39c25eSJoel Dice //! - `{Future,Stream}Reader::new`: Create a new Component Model `future` or
327e39c25eSJoel Dice //! `stream` which may be passed to the guest.  This takes a
337e39c25eSJoel Dice //! `{Future,Stream}Producer` implementation which will be polled for items when
347e39c25eSJoel Dice //! the consumer requests them.
35fa70f025SJoel Dice //!
367e39c25eSJoel Dice //! - `{Future,Stream}Reader::pipe`: Consume a `future` or `stream` by
377e39c25eSJoel Dice //! connecting it to a `{Future,Stream}Consumer` which will consume any items
387e39c25eSJoel Dice //! produced by the write end.
39fa70f025SJoel Dice //!
40fa70f025SJoel Dice //! ## Host Task API (e.g. implementing concurrent host functions and background tasks)
41fa70f025SJoel Dice //!
42fa70f025SJoel Dice //! - `LinkerInstance::func_wrap_concurrent`: Register a concurrent host
43fa70f025SJoel Dice //! function with the linker.  That function will take an `Accessor` as its
447e39c25eSJoel Dice //! first parameter, which provides access to the store between (but not across)
457e39c25eSJoel Dice //! await points.
46fa70f025SJoel Dice //!
477e39c25eSJoel Dice //! - `Accessor::with`: Access the store and its associated data.
48fa70f025SJoel Dice //!
49fa70f025SJoel Dice //! - `Accessor::spawn`: Run a background task as part of the event loop for the
507e39c25eSJoel Dice //! store.  This is equivalent to `StoreContextMut::spawn` but more convenient to use
517e39c25eSJoel Dice //! in host functions.
52fa70f025SJoel Dice 
53da093747SAlex Crichton use crate::bail_bug;
54c09aa380SJoel Dice use crate::component::func::{self, Func, call_post_return};
55cb97ae85SJoel Dice use crate::component::{
56b856261dSJoel Dice     HasData, HasSelf, Instance, Resource, ResourceTable, ResourceTableError, RuntimeInstance,
57cb97ae85SJoel Dice };
58fa70f025SJoel Dice use crate::fiber::{self, StoreFiber, StoreFiberYield};
5963679896SNick Fitzgerald use crate::prelude::*;
607e39c25eSJoel Dice use crate::store::{Store, StoreId, StoreInner, StoreOpaque, StoreToken};
6198499653SAlex Crichton use crate::vm::component::{CallContext, ComponentInstance, InstanceState};
62624c8235SJoel Dice use crate::vm::{AlwaysMut, SendSyncPtr, VMFuncRef, VMMemoryDefinition, VMStore};
6396e19700SNick Fitzgerald use crate::{
64da093747SAlex Crichton     AsContext, AsContextMut, FuncType, Result, StoreContext, StoreContextMut, ValRaw, ValType, bail,
6596e19700SNick Fitzgerald };
66e8189549SJoel Dice use error_contexts::GlobalErrorContextRefCount;
67fa70f025SJoel Dice use futures::channel::oneshot;
68bde99243SSy Brand use futures::future::{self, FutureExt};
69fa70f025SJoel Dice use futures::stream::{FuturesUnordered, StreamExt};
70e8189549SJoel Dice use futures_and_streams::{FlatAbi, ReturnCode, TransmitHandle, TransmitIndex};
71b221fca7SJoel Dice use std::any::Any;
72fa70f025SJoel Dice use std::borrow::ToOwned;
73b221fca7SJoel Dice use std::boxed::Box;
74fa70f025SJoel Dice use std::cell::UnsafeCell;
75bde99243SSy Brand use std::collections::{BTreeMap, BTreeSet, HashSet, VecDeque};
76fa70f025SJoel Dice use std::fmt;
77b221fca7SJoel Dice use std::future::Future;
78b221fca7SJoel Dice use std::marker::PhantomData;
79b4475438SJoel Dice use std::mem::{self, ManuallyDrop, MaybeUninit};
80b4475438SJoel Dice use std::ops::DerefMut;
81b221fca7SJoel Dice use std::pin::{Pin, pin};
82fa70f025SJoel Dice use std::ptr::{self, NonNull};
83b221fca7SJoel Dice use std::task::{Context, Poll, Waker};
84fa70f025SJoel Dice use std::vec::Vec;
85624c8235SJoel Dice use table::{TableDebug, TableId};
86e06fbf70SSy Brand use wasmtime_environ::Trap;
87b221fca7SJoel Dice use wasmtime_environ::component::{
881d8827f3SAlex Crichton     CanonicalAbiInfo, CanonicalOptions, CanonicalOptionsDataModel, MAX_FLAT_PARAMS,
8905a711f6SAlex Crichton     MAX_FLAT_RESULTS, OptionsIndex, PREPARE_ASYNC_NO_RESULT, PREPARE_ASYNC_WITH_RESULT,
90e06fbf70SSy Brand     RuntimeComponentInstanceIndex, RuntimeTableIndex, StringEncoding,
91e06fbf70SSy Brand     TypeComponentGlobalErrorContextTableIndex, TypeComponentLocalErrorContextTableIndex,
92e06fbf70SSy Brand     TypeFuncIndex, TypeFutureTableIndex, TypeStreamTableIndex, TypeTupleIndex,
93636435f1SJoel Dice };
94b271e452SJoel Dice use wasmtime_environ::packed_option::ReservedValue;
95636435f1SJoel Dice 
96c42ed27aSAlex Crichton pub use abort::JoinHandle;
97f586be11SAlex Crichton pub use future_stream_any::{FutureAny, StreamAny};
98b221fca7SJoel Dice pub use futures_and_streams::{
995764da5fSJoel Dice     Destination, DirectDestination, DirectSource, ErrorContext, FutureConsumer, FutureProducer,
1005764da5fSJoel Dice     FutureReader, GuardedFutureReader, GuardedStreamReader, ReadBuffer, Source, StreamConsumer,
1015764da5fSJoel Dice     StreamProducer, StreamReader, StreamResult, VecBuffer, WriteBuffer,
102b221fca7SJoel Dice };
103f586be11SAlex Crichton pub(crate) use futures_and_streams::{ResourcePair, lower_error_context_to_index};
104636435f1SJoel Dice 
105fa70f025SJoel Dice mod abort;
106fa70f025SJoel Dice mod error_contexts;
107f586be11SAlex Crichton mod future_stream_any;
108636435f1SJoel Dice mod futures_and_streams;
109e06fbf70SSy Brand pub(crate) mod table;
110b4475438SJoel Dice pub(crate) mod tls;
111636435f1SJoel Dice 
112fa70f025SJoel Dice /// Constant defined in the Component Model spec to indicate that the async
113fa70f025SJoel Dice /// intrinsic (e.g. `future.write`) has not yet completed.
114fa70f025SJoel Dice const BLOCKED: u32 = 0xffff_ffff;
115fa70f025SJoel Dice 
116fa70f025SJoel Dice /// Corresponds to `CallState` in the upstream spec.
117fa70f025SJoel Dice #[derive(Clone, Copy, Eq, PartialEq, Debug)]
118b221fca7SJoel Dice pub enum Status {
119b221fca7SJoel Dice     Starting = 0,
120b221fca7SJoel Dice     Started = 1,
121b221fca7SJoel Dice     Returned = 2,
122b221fca7SJoel Dice     StartCancelled = 3,
123b221fca7SJoel Dice     ReturnCancelled = 4,
124636435f1SJoel Dice }
125636435f1SJoel Dice 
126b221fca7SJoel Dice impl Status {
127b221fca7SJoel Dice     /// Packs this status and the optional `waitable` provided into a 32-bit
128b221fca7SJoel Dice     /// result that the canonical ABI requires.
129636435f1SJoel Dice     ///
130b221fca7SJoel Dice     /// The low 4 bits are reserved for the status while the upper 28 bits are
131b221fca7SJoel Dice     /// the waitable, if present.
pack(self, waitable: Option<u32>) -> u32132b221fca7SJoel Dice     pub fn pack(self, waitable: Option<u32>) -> u32 {
133fa70f025SJoel Dice         assert!(matches!(self, Status::Returned) == waitable.is_none());
134fa70f025SJoel Dice         let waitable = waitable.unwrap_or(0);
135fa70f025SJoel Dice         assert!(waitable < (1 << 28));
136fa70f025SJoel Dice         (waitable << 4) | (self as u32)
137b221fca7SJoel Dice     }
138b221fca7SJoel Dice }
139b221fca7SJoel Dice 
140fa70f025SJoel Dice /// Corresponds to `EventCode` in the Component Model spec, plus related payload
141fa70f025SJoel Dice /// data.
142fa70f025SJoel Dice #[derive(Clone, Copy, Debug)]
143fa70f025SJoel Dice enum Event {
144fa70f025SJoel Dice     None,
145fa70f025SJoel Dice     Subtask {
146fa70f025SJoel Dice         status: Status,
147fa70f025SJoel Dice     },
148fa70f025SJoel Dice     StreamRead {
149fa70f025SJoel Dice         code: ReturnCode,
150fa70f025SJoel Dice         pending: Option<(TypeStreamTableIndex, u32)>,
151fa70f025SJoel Dice     },
152fa70f025SJoel Dice     StreamWrite {
153fa70f025SJoel Dice         code: ReturnCode,
154fa70f025SJoel Dice         pending: Option<(TypeStreamTableIndex, u32)>,
155fa70f025SJoel Dice     },
156fa70f025SJoel Dice     FutureRead {
157fa70f025SJoel Dice         code: ReturnCode,
158fa70f025SJoel Dice         pending: Option<(TypeFutureTableIndex, u32)>,
159fa70f025SJoel Dice     },
160fa70f025SJoel Dice     FutureWrite {
161fa70f025SJoel Dice         code: ReturnCode,
162fa70f025SJoel Dice         pending: Option<(TypeFutureTableIndex, u32)>,
163fa70f025SJoel Dice     },
164*f820750bSAlex Crichton     Cancelled,
165fa70f025SJoel Dice }
166fa70f025SJoel Dice 
167fa70f025SJoel Dice impl Event {
168fa70f025SJoel Dice     /// Lower this event to core Wasm integers for delivery to the guest.
169fa70f025SJoel Dice     ///
170fa70f025SJoel Dice     /// Note that the waitable handle, if any, is assumed to be lowered
171fa70f025SJoel Dice     /// separately.
parts(self) -> (u32, u32)172fa70f025SJoel Dice     fn parts(self) -> (u32, u32) {
173fa70f025SJoel Dice         const EVENT_NONE: u32 = 0;
174fa70f025SJoel Dice         const EVENT_SUBTASK: u32 = 1;
175fa70f025SJoel Dice         const EVENT_STREAM_READ: u32 = 2;
176fa70f025SJoel Dice         const EVENT_STREAM_WRITE: u32 = 3;
177fa70f025SJoel Dice         const EVENT_FUTURE_READ: u32 = 4;
178fa70f025SJoel Dice         const EVENT_FUTURE_WRITE: u32 = 5;
179fa70f025SJoel Dice         const EVENT_CANCELLED: u32 = 6;
180fa70f025SJoel Dice         match self {
181fa70f025SJoel Dice             Event::None => (EVENT_NONE, 0),
182fa70f025SJoel Dice             Event::Cancelled => (EVENT_CANCELLED, 0),
183fa70f025SJoel Dice             Event::Subtask { status } => (EVENT_SUBTASK, status as u32),
184fa70f025SJoel Dice             Event::StreamRead { code, .. } => (EVENT_STREAM_READ, code.encode()),
185fa70f025SJoel Dice             Event::StreamWrite { code, .. } => (EVENT_STREAM_WRITE, code.encode()),
186fa70f025SJoel Dice             Event::FutureRead { code, .. } => (EVENT_FUTURE_READ, code.encode()),
187fa70f025SJoel Dice             Event::FutureWrite { code, .. } => (EVENT_FUTURE_WRITE, code.encode()),
188fa70f025SJoel Dice         }
189fa70f025SJoel Dice     }
190fa70f025SJoel Dice }
191fa70f025SJoel Dice 
192fa70f025SJoel Dice /// Corresponds to `CallbackCode` in the spec.
193fa70f025SJoel Dice mod callback_code {
194fa70f025SJoel Dice     pub const EXIT: u32 = 0;
195fa70f025SJoel Dice     pub const YIELD: u32 = 1;
196fa70f025SJoel Dice     pub const WAIT: u32 = 2;
197fa70f025SJoel Dice }
198fa70f025SJoel Dice 
199fa70f025SJoel Dice /// A flag indicating that the callee is an async-lowered export.
200fa70f025SJoel Dice ///
201fa70f025SJoel Dice /// This may be passed to the `async-start` intrinsic from a fused adapter.
20227b18602SRoman Volosatovs const START_FLAG_ASYNC_CALLEE: u32 = wasmtime_environ::component::START_FLAG_ASYNC_CALLEE as u32;
203fa70f025SJoel Dice 
204fa70f025SJoel Dice /// Provides access to either store data (via the `get` method) or the store
205fa70f025SJoel Dice /// itself (via [`AsContext`]/[`AsContextMut`]), as well as the component
206fa70f025SJoel Dice /// instance to which the current host task belongs.
207fa70f025SJoel Dice ///
208fa70f025SJoel Dice /// See [`Accessor::with`] for details.
2091155d6dfSAlex Crichton pub struct Access<'a, T: 'static, D: HasData + ?Sized = HasSelf<T>> {
210fa70f025SJoel Dice     store: StoreContextMut<'a, T>,
2119f47be2eSAlex Crichton     get_data: fn(&mut T) -> D::Data<'_>,
212fa70f025SJoel Dice }
213fa70f025SJoel Dice 
214fa70f025SJoel Dice impl<'a, T, D> Access<'a, T, D>
215fa70f025SJoel Dice where
2161155d6dfSAlex Crichton     D: HasData + ?Sized,
217fa70f025SJoel Dice     T: 'static,
218fa70f025SJoel Dice {
2199f47be2eSAlex Crichton     /// Creates a new [`Access`] from its component parts.
new(store: StoreContextMut<'a, T>, get_data: fn(&mut T) -> D::Data<'_>) -> Self2209f47be2eSAlex Crichton     pub fn new(store: StoreContextMut<'a, T>, get_data: fn(&mut T) -> D::Data<'_>) -> Self {
2217e39c25eSJoel Dice         Self { store, get_data }
2229f47be2eSAlex Crichton     }
2239f47be2eSAlex Crichton 
224fa70f025SJoel Dice     /// Get mutable access to the store data.
data_mut(&mut self) -> &mut T225fa70f025SJoel Dice     pub fn data_mut(&mut self) -> &mut T {
226fa70f025SJoel Dice         self.store.data_mut()
227fa70f025SJoel Dice     }
228fa70f025SJoel Dice 
229fa70f025SJoel Dice     /// Get mutable access to the store data.
get(&mut self) -> D::Data<'_>230fa70f025SJoel Dice     pub fn get(&mut self) -> D::Data<'_> {
2319f47be2eSAlex Crichton         (self.get_data)(self.data_mut())
232fa70f025SJoel Dice     }
233fa70f025SJoel Dice 
234fa70f025SJoel Dice     /// Spawn a background task.
235fa70f025SJoel Dice     ///
236fa70f025SJoel Dice     /// See [`Accessor::spawn`] for details.
spawn(&mut self, task: impl AccessorTask<T, D>) -> JoinHandle where T: 'static,237fee9be21SAlex Crichton     pub fn spawn(&mut self, task: impl AccessorTask<T, D>) -> JoinHandle
238fa70f025SJoel Dice     where
239fa70f025SJoel Dice         T: 'static,
240fa70f025SJoel Dice     {
2419f47be2eSAlex Crichton         let accessor = Accessor {
2429f47be2eSAlex Crichton             get_data: self.get_data,
2439f47be2eSAlex Crichton             token: StoreToken::new(self.store.as_context_mut()),
2449f47be2eSAlex Crichton         };
2457e39c25eSJoel Dice         self.store
2467e39c25eSJoel Dice             .as_context_mut()
2477e39c25eSJoel Dice             .spawn_with_accessor(accessor, task)
248fa70f025SJoel Dice     }
249b570b4fcSAlex Crichton 
250b570b4fcSAlex Crichton     /// Returns the getter this accessor is using to project from `T` into
251b570b4fcSAlex Crichton     /// `D::Data`.
getter(&self) -> fn(&mut T) -> D::Data<'_>252b570b4fcSAlex Crichton     pub fn getter(&self) -> fn(&mut T) -> D::Data<'_> {
253b570b4fcSAlex Crichton         self.get_data
254b570b4fcSAlex Crichton     }
255fa70f025SJoel Dice }
256fa70f025SJoel Dice 
257fa70f025SJoel Dice impl<'a, T, D> AsContext for Access<'a, T, D>
258fa70f025SJoel Dice where
2591155d6dfSAlex Crichton     D: HasData + ?Sized,
260fa70f025SJoel Dice     T: 'static,
261fa70f025SJoel Dice {
262fa70f025SJoel Dice     type Data = T;
263fa70f025SJoel Dice 
as_context(&self) -> StoreContext<'_, T>264fa70f025SJoel Dice     fn as_context(&self) -> StoreContext<'_, T> {
265fa70f025SJoel Dice         self.store.as_context()
266fa70f025SJoel Dice     }
267fa70f025SJoel Dice }
268fa70f025SJoel Dice 
269fa70f025SJoel Dice impl<'a, T, D> AsContextMut for Access<'a, T, D>
270fa70f025SJoel Dice where
2711155d6dfSAlex Crichton     D: HasData + ?Sized,
272fa70f025SJoel Dice     T: 'static,
273fa70f025SJoel Dice {
as_context_mut(&mut self) -> StoreContextMut<'_, T>274fa70f025SJoel Dice     fn as_context_mut(&mut self) -> StoreContextMut<'_, T> {
275fa70f025SJoel Dice         self.store.as_context_mut()
276fa70f025SJoel Dice     }
277fa70f025SJoel Dice }
278fa70f025SJoel Dice 
279fa70f025SJoel Dice /// Provides scoped mutable access to store data in the context of a concurrent
280fa70f025SJoel Dice /// host task future.
281fa70f025SJoel Dice ///
282fa70f025SJoel Dice /// This allows multiple host task futures to execute concurrently and access
283fa70f025SJoel Dice /// the store between (but not across) `await` points.
284fa70f025SJoel Dice ///
285fa70f025SJoel Dice /// # Rationale
286fa70f025SJoel Dice ///
287fa70f025SJoel Dice /// This structure is sort of like `&mut T` plus a projection from `&mut T` to
288fa70f025SJoel Dice /// `D::Data<'_>`. The problem this is solving, however, is that it does not
289fa70f025SJoel Dice /// literally store these values. The basic problem is that when a concurrent
290fa70f025SJoel Dice /// host future is being polled it has access to `&mut T` (and the whole
291fa70f025SJoel Dice /// `Store`) but when it's not being polled it does not have access to these
292fa70f025SJoel Dice /// values. This reflects how the store is only ever polling one future at a
293fa70f025SJoel Dice /// time so the store is effectively being passed between futures.
294fa70f025SJoel Dice ///
295fa70f025SJoel Dice /// Rust's `Future` trait, however, has no means of passing a `Store`
296fa70f025SJoel Dice /// temporarily between futures. The [`Context`](std::task::Context) type does
297fa70f025SJoel Dice /// not have the ability to attach arbitrary information to it at this time.
298fa70f025SJoel Dice /// This type, [`Accessor`], is used to bridge this expressivity gap.
299fa70f025SJoel Dice ///
300fa70f025SJoel Dice /// The [`Accessor`] type here represents the ability to acquire, temporarily in
301fa70f025SJoel Dice /// a synchronous manner, the current store. The [`Accessor::with`] function
302fa70f025SJoel Dice /// yields an [`Access`] which can be used to access [`StoreContextMut`], `&mut
303fa70f025SJoel Dice /// T`, or `D::Data<'_>`. Note though that [`Accessor::with`] intentionally does
304fa70f025SJoel Dice /// not take an `async` closure as its argument, instead it's a synchronous
305fa70f025SJoel Dice /// closure which must complete during on run of `Future::poll`. This reflects
306fa70f025SJoel Dice /// how the store is temporarily made available while a host future is being
307fa70f025SJoel Dice /// polled.
308fa70f025SJoel Dice ///
309fa70f025SJoel Dice /// # Implementation
310fa70f025SJoel Dice ///
311fa70f025SJoel Dice /// This type does not actually store `&mut T` nor `StoreContextMut<T>`, and
312fa70f025SJoel Dice /// this type additionally doesn't even have a lifetime parameter. This is
313fa70f025SJoel Dice /// instead a representation of proof of the ability to acquire these while a
314fa70f025SJoel Dice /// future is being polled. Wasmtime will, when it polls a host future,
315fa70f025SJoel Dice /// configure ambient state such that the `Accessor` that a future closes over
316fa70f025SJoel Dice /// will work and be able to access the store.
317fa70f025SJoel Dice ///
318fa70f025SJoel Dice /// This has a number of implications for users such as:
319fa70f025SJoel Dice ///
320fa70f025SJoel Dice /// * It's intentional that `Accessor` cannot be cloned, it needs to stay within
321fa70f025SJoel Dice ///   the lifetime of a single future.
322624c8235SJoel Dice /// * A future is expected to, however, close over an `Accessor` and keep it
323fa70f025SJoel Dice ///   alive probably for the duration of the entire future.
324fa70f025SJoel Dice /// * Different host futures will be given different `Accessor`s, and that's
325fa70f025SJoel Dice ///   intentional.
326fa70f025SJoel Dice /// * The `Accessor` type is `Send` and `Sync` irrespective of `T` which
327fa70f025SJoel Dice ///   alleviates some otherwise required bounds to be written down.
328fa70f025SJoel Dice ///
329fa70f025SJoel Dice /// # Using `Accessor` in `Drop`
330fa70f025SJoel Dice ///
331fa70f025SJoel Dice /// The methods on `Accessor` are only expected to work in the context of
332fa70f025SJoel Dice /// `Future::poll` and are not guaranteed to work in `Drop`. This is because a
333fa70f025SJoel Dice /// host future can be dropped at any time throughout the system and Wasmtime
334fa70f025SJoel Dice /// store context is not necessarily available at that time. It's recommended to
335fa70f025SJoel Dice /// not use `Accessor` methods in anything connected to a `Drop` implementation
336fa70f025SJoel Dice /// as they will panic and have unintended results. If you run into this though
337fa70f025SJoel Dice /// feel free to file an issue on the Wasmtime repository.
338fa70f025SJoel Dice pub struct Accessor<T: 'static, D = HasSelf<T>>
339fa70f025SJoel Dice where
3401155d6dfSAlex Crichton     D: HasData + ?Sized,
341fa70f025SJoel Dice {
342fa70f025SJoel Dice     token: StoreToken<T>,
343fa70f025SJoel Dice     get_data: fn(&mut T) -> D::Data<'_>,
344fa70f025SJoel Dice }
345fa70f025SJoel Dice 
346c34eb3f7SAlex Crichton /// A helper trait to take any type of accessor-with-data in functions.
347c34eb3f7SAlex Crichton ///
348c34eb3f7SAlex Crichton /// This trait is similar to [`AsContextMut`] except that it's used when
349c34eb3f7SAlex Crichton /// working with an [`Accessor`] instead of a [`StoreContextMut`]. The
350c34eb3f7SAlex Crichton /// [`Accessor`] is the main type used in concurrent settings and is passed to
351bc4582c3SAlex Crichton /// functions such as [`Func::call_concurrent`].
352c34eb3f7SAlex Crichton ///
353c34eb3f7SAlex Crichton /// This trait is implemented for [`Accessor`] and `&T` where `T` implements
354c34eb3f7SAlex Crichton /// this trait. This effectively means that regardless of the `D` in
355c34eb3f7SAlex Crichton /// `Accessor<T, D>` it can still be passed to a function which just needs a
356c34eb3f7SAlex Crichton /// store accessor.
357c34eb3f7SAlex Crichton ///
3587e39c25eSJoel Dice /// Acquiring an [`Accessor`] can be done through
3597e39c25eSJoel Dice /// [`StoreContextMut::run_concurrent`] for example or in a host function
3607e39c25eSJoel Dice /// through
361bc4582c3SAlex Crichton /// [`Linker::func_wrap_concurrent`](crate::component::LinkerInstance::func_wrap_concurrent).
362c34eb3f7SAlex Crichton pub trait AsAccessor {
363c34eb3f7SAlex Crichton     /// The `T` in `Store<T>` that this accessor refers to.
364c34eb3f7SAlex Crichton     type Data: 'static;
365c34eb3f7SAlex Crichton 
366c34eb3f7SAlex Crichton     /// The `D` in `Accessor<T, D>`, or the projection out of
367c34eb3f7SAlex Crichton     /// `Self::Data`.
3681155d6dfSAlex Crichton     type AccessorData: HasData + ?Sized;
369c34eb3f7SAlex Crichton 
370c34eb3f7SAlex Crichton     /// Returns the accessor that this is referring to.
as_accessor(&self) -> &Accessor<Self::Data, Self::AccessorData>371c34eb3f7SAlex Crichton     fn as_accessor(&self) -> &Accessor<Self::Data, Self::AccessorData>;
372c34eb3f7SAlex Crichton }
373c34eb3f7SAlex Crichton 
374c34eb3f7SAlex Crichton impl<T: AsAccessor + ?Sized> AsAccessor for &T {
375c34eb3f7SAlex Crichton     type Data = T::Data;
376c34eb3f7SAlex Crichton     type AccessorData = T::AccessorData;
377c34eb3f7SAlex Crichton 
as_accessor(&self) -> &Accessor<Self::Data, Self::AccessorData>378c34eb3f7SAlex Crichton     fn as_accessor(&self) -> &Accessor<Self::Data, Self::AccessorData> {
379c34eb3f7SAlex Crichton         T::as_accessor(self)
380c34eb3f7SAlex Crichton     }
381c34eb3f7SAlex Crichton }
382c34eb3f7SAlex Crichton 
3831155d6dfSAlex Crichton impl<T, D: HasData + ?Sized> AsAccessor for Accessor<T, D> {
384c34eb3f7SAlex Crichton     type Data = T;
385c34eb3f7SAlex Crichton     type AccessorData = D;
386c34eb3f7SAlex Crichton 
as_accessor(&self) -> &Accessor<T, D>387c34eb3f7SAlex Crichton     fn as_accessor(&self) -> &Accessor<T, D> {
388c34eb3f7SAlex Crichton         self
389c34eb3f7SAlex Crichton     }
390c34eb3f7SAlex Crichton }
391c34eb3f7SAlex Crichton 
392fa70f025SJoel Dice // Note that it is intentional at this time that `Accessor` does not actually
393fa70f025SJoel Dice // store `&mut T` or anything similar. This distinctly enables the `Accessor`
394fa70f025SJoel Dice // structure to be both `Send` and `Sync` regardless of what `T` is (or `D` for
395fa70f025SJoel Dice // that matter). This is used to ergonomically simplify bindings where the
396fa70f025SJoel Dice // majority of the time `Accessor` is closed over in a future which then needs
397fa70f025SJoel Dice // to be `Send` and `Sync`. To avoid needing to write `T: Send` everywhere (as
398fa70f025SJoel Dice // you already have to write `T: 'static`...) it helps to avoid this.
399fa70f025SJoel Dice //
400fa70f025SJoel Dice // Note as well that `Accessor` doesn't actually store its data at all. Instead
401fa70f025SJoel Dice // it's more of a "proof" of what can be accessed from TLS. API design around
402fa70f025SJoel Dice // `Accessor` and functions like `Linker::func_wrap_concurrent` are
403fa70f025SJoel Dice // intentionally made to ensure that `Accessor` is ideally only used in the
404fa70f025SJoel Dice // context that TLS variables are actually set. For example host functions are
40564bc3bd9SAlex Crichton // given `&Accessor`, not `Accessor`, and this prevents them from persisting
406fa70f025SJoel Dice // the value outside of a future. Within the future the TLS variables are all
407fa70f025SJoel Dice // guaranteed to be set while the future is being polled.
408fa70f025SJoel Dice //
409fa70f025SJoel Dice // Finally though this is not an ironclad guarantee, but nor does it need to be.
410fa70f025SJoel Dice // The TLS APIs are designed to panic or otherwise model usage where they're
411fa70f025SJoel Dice // called recursively or similar. It's hoped that code cannot be constructed to
412fa70f025SJoel Dice // actually hit this at runtime but this is not a safety requirement at this
413fa70f025SJoel Dice // time.
414fa70f025SJoel Dice const _: () = {
assert<T: Send + Sync>()415fa70f025SJoel Dice     const fn assert<T: Send + Sync>() {}
416fa70f025SJoel Dice     assert::<Accessor<UnsafeCell<u32>>>();
417fa70f025SJoel Dice };
418fa70f025SJoel Dice 
419fa70f025SJoel Dice impl<T> Accessor<T> {
420fa70f025SJoel Dice     /// Creates a new `Accessor` backed by the specified functions.
421fa70f025SJoel Dice     ///
422fa70f025SJoel Dice     /// - `get`: used to retrieve the store
423fa70f025SJoel Dice     ///
424fa70f025SJoel Dice     /// - `get_data`: used to "project" from the store's associated data to
425fa70f025SJoel Dice     /// another type (e.g. a field of that data or a wrapper around it).
426fa70f025SJoel Dice     ///
427fa70f025SJoel Dice     /// - `spawn`: used to queue spawned background tasks to be run later
new(token: StoreToken<T>) -> Self4287e39c25eSJoel Dice     pub(crate) fn new(token: StoreToken<T>) -> Self {
429fa70f025SJoel Dice         Self {
430fa70f025SJoel Dice             token,
431fa70f025SJoel Dice             get_data: |x| x,
432fa70f025SJoel Dice         }
433fa70f025SJoel Dice     }
434fa70f025SJoel Dice }
435fa70f025SJoel Dice 
436fa70f025SJoel Dice impl<T, D> Accessor<T, D>
437fa70f025SJoel Dice where
4381155d6dfSAlex Crichton     D: HasData + ?Sized,
439fa70f025SJoel Dice {
44064bc3bd9SAlex Crichton     /// Run the specified closure, passing it mutable access to the store.
441fa70f025SJoel Dice     ///
44264bc3bd9SAlex Crichton     /// This function is one of the main building blocks of the [`Accessor`]
443b856261dSJoel Dice     /// type. This yields synchronous, blocking, access to the store via an
44464bc3bd9SAlex Crichton     /// [`Access`]. The [`Access`] implements [`AsContextMut`] in addition to
44564bc3bd9SAlex Crichton     /// providing the ability to access `D` via [`Access::get`]. Note that the
44664bc3bd9SAlex Crichton     /// `fun` here is given only temporary access to the store and `T`/`D`
44764bc3bd9SAlex Crichton     /// meaning that the return value `R` here is not allowed to capture borrows
44864bc3bd9SAlex Crichton     /// into the two. If access is needed to data within `T` or `D` outside of
44964bc3bd9SAlex Crichton     /// this closure then it must be `clone`d out, for example.
45064bc3bd9SAlex Crichton     ///
45164bc3bd9SAlex Crichton     /// # Panics
45264bc3bd9SAlex Crichton     ///
45364bc3bd9SAlex Crichton     /// This function will panic if it is call recursively with any other
45464bc3bd9SAlex Crichton     /// accessor already in scope. For example if `with` is called within `fun`,
45564bc3bd9SAlex Crichton     /// then this function will panic. It is up to the embedder to ensure that
45664bc3bd9SAlex Crichton     /// this does not happen.
with<R>(&self, fun: impl FnOnce(Access<'_, T, D>) -> R) -> R45764bc3bd9SAlex Crichton     pub fn with<R>(&self, fun: impl FnOnce(Access<'_, T, D>) -> R) -> R {
458fa70f025SJoel Dice         tls::get(|vmstore| {
459fa70f025SJoel Dice             fun(Access {
460fa70f025SJoel Dice                 store: self.token.as_context_mut(vmstore),
4619f47be2eSAlex Crichton                 get_data: self.get_data,
462fa70f025SJoel Dice             })
463fa70f025SJoel Dice         })
464fa70f025SJoel Dice     }
465fa70f025SJoel Dice 
4665764da5fSJoel Dice     /// Returns the getter this accessor is using to project from `T` into
4675764da5fSJoel Dice     /// `D::Data`.
getter(&self) -> fn(&mut T) -> D::Data<'_>4685764da5fSJoel Dice     pub fn getter(&self) -> fn(&mut T) -> D::Data<'_> {
4695764da5fSJoel Dice         self.get_data
4705764da5fSJoel Dice     }
4715764da5fSJoel Dice 
472fa70f025SJoel Dice     /// Changes this accessor to access `D2` instead of the current type
473fa70f025SJoel Dice     /// parameter `D`.
474fa70f025SJoel Dice     ///
475fa70f025SJoel Dice     /// This changes the underlying data access from `T` to `D2::Data<'_>`.
476fa70f025SJoel Dice     ///
4775764da5fSJoel Dice     /// # Panics
478fa70f025SJoel Dice     ///
4795764da5fSJoel Dice     /// When using this API the returned value is disconnected from `&self` and
4805764da5fSJoel Dice     /// the lifetime binding the `self` argument. An `Accessor` only works
4815764da5fSJoel Dice     /// within the context of the closure or async closure that it was
4825764da5fSJoel Dice     /// originally given to, however. This means that due to the fact that the
4835764da5fSJoel Dice     /// returned value has no lifetime connection it's possible to use the
4845764da5fSJoel Dice     /// accessor outside of `&self`, the original accessor, and panic.
485fa70f025SJoel Dice     ///
4865764da5fSJoel Dice     /// The returned value should only be used within the scope of the original
4875764da5fSJoel Dice     /// `Accessor` that `self` refers to.
with_getter<D2: HasData>( &self, get_data: fn(&mut T) -> D2::Data<'_>, ) -> Accessor<T, D2>4885764da5fSJoel Dice     pub fn with_getter<D2: HasData>(
4895764da5fSJoel Dice         &self,
4905764da5fSJoel Dice         get_data: fn(&mut T) -> D2::Data<'_>,
4915764da5fSJoel Dice     ) -> Accessor<T, D2> {
492fa70f025SJoel Dice         Accessor {
493fa70f025SJoel Dice             token: self.token,
494fa70f025SJoel Dice             get_data,
495fa70f025SJoel Dice         }
496fa70f025SJoel Dice     }
497fa70f025SJoel Dice 
49864bc3bd9SAlex Crichton     /// Spawn a background task which will receive an `&Accessor<T, D>` and
499fa70f025SJoel Dice     /// run concurrently with any other tasks in progress for the current
5007e39c25eSJoel Dice     /// store.
501fa70f025SJoel Dice     ///
502fa70f025SJoel Dice     /// This is particularly useful for host functions which return a `stream`
503fa70f025SJoel Dice     /// or `future` such that the code to write to the write end of that
504fa70f025SJoel Dice     /// `stream` or `future` must run after the function returns.
505fa70f025SJoel Dice     ///
506c42ed27aSAlex Crichton     /// The returned [`JoinHandle`] may be used to cancel the task.
50764bc3bd9SAlex Crichton     ///
50864bc3bd9SAlex Crichton     /// # Panics
50964bc3bd9SAlex Crichton     ///
51064bc3bd9SAlex Crichton     /// Panics if called within a closure provided to the [`Accessor::with`]
51164bc3bd9SAlex Crichton     /// function. This can only be called outside an active invocation of
51264bc3bd9SAlex Crichton     /// [`Accessor::with`].
spawn(&self, task: impl AccessorTask<T, D>) -> JoinHandle where T: 'static,513fee9be21SAlex Crichton     pub fn spawn(&self, task: impl AccessorTask<T, D>) -> JoinHandle
514fa70f025SJoel Dice     where
515fa70f025SJoel Dice         T: 'static,
516fa70f025SJoel Dice     {
517fa70f025SJoel Dice         let accessor = self.clone_for_spawn();
5187e39c25eSJoel Dice         self.with(|mut access| access.as_context_mut().spawn_with_accessor(accessor, task))
519fa70f025SJoel Dice     }
520fa70f025SJoel Dice 
clone_for_spawn(&self) -> Self521fa70f025SJoel Dice     fn clone_for_spawn(&self) -> Self {
522fa70f025SJoel Dice         Self {
523fa70f025SJoel Dice             token: self.token,
524fa70f025SJoel Dice             get_data: self.get_data,
525fa70f025SJoel Dice         }
526fa70f025SJoel Dice     }
527fa70f025SJoel Dice }
528fa70f025SJoel Dice 
529fa70f025SJoel Dice /// Represents a task which may be provided to `Accessor::spawn`,
5307e39c25eSJoel Dice /// `Accessor::forward`, or `StorecContextMut::spawn`.
531fa70f025SJoel Dice // TODO: Replace this with `std::ops::AsyncFnOnce` when that becomes a viable
532fa70f025SJoel Dice // option.
533fa70f025SJoel Dice //
5347e39c25eSJoel Dice // As of this writing, it's not possible to specify e.g. `Send` and `Sync`
5357e39c25eSJoel Dice // bounds on the `Future` type returned by an `AsyncFnOnce`.  Also, using `F:
5367e39c25eSJoel Dice // Future<Output = Result<()>> + Send + Sync, FN: FnOnce(&Accessor<T>) -> F +
5377e39c25eSJoel Dice // Send + Sync + 'static` fails with a type mismatch error when we try to pass
5387e39c25eSJoel Dice // it an async closure (e.g. `async move |_| { ... }`).  So this seems to be the
5397e39c25eSJoel Dice // best we can do for the time being.
540fee9be21SAlex Crichton pub trait AccessorTask<T, D = HasSelf<T>>: Send + 'static
541fa70f025SJoel Dice where
5421155d6dfSAlex Crichton     D: HasData + ?Sized,
543fa70f025SJoel Dice {
544fa70f025SJoel Dice     /// Run the task.
run(self, accessor: &Accessor<T, D>) -> impl Future<Output = Result<()>> + Send545fee9be21SAlex Crichton     fn run(self, accessor: &Accessor<T, D>) -> impl Future<Output = Result<()>> + Send;
546fa70f025SJoel Dice }
547fa70f025SJoel Dice 
548fa70f025SJoel Dice /// Represents parameter and result metadata for the caller side of a
549fa70f025SJoel Dice /// guest->guest call orchestrated by a fused adapter.
550fa70f025SJoel Dice enum CallerInfo {
551fa70f025SJoel Dice     /// Metadata for a call to an async-lowered import
552fa70f025SJoel Dice     Async {
553fa70f025SJoel Dice         params: Vec<ValRaw>,
554fa70f025SJoel Dice         has_result: bool,
555fa70f025SJoel Dice     },
556fa70f025SJoel Dice     /// Metadata for a call to an sync-lowered import
557fa70f025SJoel Dice     Sync {
558fa70f025SJoel Dice         params: Vec<ValRaw>,
559fa70f025SJoel Dice         result_count: u32,
560fa70f025SJoel Dice     },
561fa70f025SJoel Dice }
562fa70f025SJoel Dice 
563fa70f025SJoel Dice /// Indicates how a guest task is waiting on a waitable set.
564fa70f025SJoel Dice enum WaitMode {
565fa70f025SJoel Dice     /// The guest task is waiting using `task.wait`
566fa70f025SJoel Dice     Fiber(StoreFiber<'static>),
567fa70f025SJoel Dice     /// The guest task is waiting via a callback declared as part of an
568fa70f025SJoel Dice     /// async-lifted export.
5697e39c25eSJoel Dice     Callback(Instance),
570fa70f025SJoel Dice }
571fa70f025SJoel Dice 
572fa70f025SJoel Dice /// Represents the reason a fiber is suspending itself.
573fa70f025SJoel Dice #[derive(Debug)]
574fa70f025SJoel Dice enum SuspendReason {
575fa70f025SJoel Dice     /// The fiber is waiting for an event to be delivered to the specified
576fa70f025SJoel Dice     /// waitable set or task.
577fa70f025SJoel Dice     Waiting {
578fa70f025SJoel Dice         set: TableId<WaitableSet>,
579e06fbf70SSy Brand         thread: QualifiedThreadId,
5808992b99bSJoel Dice         skip_may_block_check: bool,
581fa70f025SJoel Dice     },
582fa70f025SJoel Dice     /// The fiber has finished handling its most recent work item and is waiting
583fa70f025SJoel Dice     /// for another (or to be dropped if it is no longer needed).
584fa70f025SJoel Dice     NeedWork,
585fa70f025SJoel Dice     /// The fiber is yielding and should be resumed once other tasks have had a
586fa70f025SJoel Dice     /// chance to run.
587fc4020baSSy Brand     Yielding {
588fc4020baSSy Brand         thread: QualifiedThreadId,
589fc4020baSSy Brand         skip_may_block_check: bool,
590fc4020baSSy Brand     },
591e06fbf70SSy Brand     /// The fiber was explicitly suspended with a call to `thread.suspend` or `thread.switch-to`.
5928992b99bSJoel Dice     ExplicitlySuspending {
5938992b99bSJoel Dice         thread: QualifiedThreadId,
5948992b99bSJoel Dice         skip_may_block_check: bool,
5958992b99bSJoel Dice     },
596fa70f025SJoel Dice }
597fa70f025SJoel Dice 
598fa70f025SJoel Dice /// Represents a pending call into guest code for a given guest task.
599fa70f025SJoel Dice enum GuestCallKind {
600fa70f025SJoel Dice     /// Indicates there's an event to deliver to the task, possibly related to a
601fa70f025SJoel Dice     /// waitable set the task has been waiting on or polling.
602fa70f025SJoel Dice     DeliverEvent {
6037e39c25eSJoel Dice         /// The instance to which the task belongs.
6047e39c25eSJoel Dice         instance: Instance,
605fa70f025SJoel Dice         /// The waitable set the event belongs to, if any.
606fa70f025SJoel Dice         ///
607fa70f025SJoel Dice         /// If this is `None` the event will be waiting in the
608fa70f025SJoel Dice         /// `GuestTask::event` field for the task.
609fa70f025SJoel Dice         set: Option<TableId<WaitableSet>>,
610fa70f025SJoel Dice     },
611fa70f025SJoel Dice     /// Indicates that a new guest task call is pending and may be executed
612fa70f025SJoel Dice     /// using the specified closure.
6138992b99bSJoel Dice     ///
6148992b99bSJoel Dice     /// If the closure returns `Ok(Some(call))`, the `call` should be run
6158992b99bSJoel Dice     /// immediately using `handle_guest_call`.
6168992b99bSJoel Dice     StartImplicit(Box<dyn FnOnce(&mut dyn VMStore) -> Result<Option<GuestCall>> + Send + Sync>),
617e06fbf70SSy Brand     StartExplicit(Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send + Sync>),
618fa70f025SJoel Dice }
619fa70f025SJoel Dice 
620fa70f025SJoel Dice impl fmt::Debug for GuestCallKind {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result621fa70f025SJoel Dice     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
622fa70f025SJoel Dice         match self {
6237e39c25eSJoel Dice             Self::DeliverEvent { instance, set } => f
6247e39c25eSJoel Dice                 .debug_struct("DeliverEvent")
6257e39c25eSJoel Dice                 .field("instance", instance)
6267e39c25eSJoel Dice                 .field("set", set)
6277e39c25eSJoel Dice                 .finish(),
628e06fbf70SSy Brand             Self::StartImplicit(_) => f.debug_tuple("StartImplicit").finish(),
629e06fbf70SSy Brand             Self::StartExplicit(_) => f.debug_tuple("StartExplicit").finish(),
630fa70f025SJoel Dice         }
631fa70f025SJoel Dice     }
632fa70f025SJoel Dice }
633fa70f025SJoel Dice 
634d2fbd2deSAlex Crichton /// The target of a suspension intrinsic.
635d2fbd2deSAlex Crichton #[derive(Copy, Clone, Debug)]
636d2fbd2deSAlex Crichton pub enum SuspensionTarget {
637d2fbd2deSAlex Crichton     SomeSuspended(u32),
638d2fbd2deSAlex Crichton     Some(u32),
639d2fbd2deSAlex Crichton     None,
640d2fbd2deSAlex Crichton }
641d2fbd2deSAlex Crichton 
642d2fbd2deSAlex Crichton impl SuspensionTarget {
is_none(&self) -> bool643d2fbd2deSAlex Crichton     fn is_none(&self) -> bool {
644d2fbd2deSAlex Crichton         matches!(self, SuspensionTarget::None)
645d2fbd2deSAlex Crichton     }
is_some(&self) -> bool646d2fbd2deSAlex Crichton     fn is_some(&self) -> bool {
647d2fbd2deSAlex Crichton         !self.is_none()
648d2fbd2deSAlex Crichton     }
649d2fbd2deSAlex Crichton }
650d2fbd2deSAlex Crichton 
651e06fbf70SSy Brand /// Represents a pending call into guest code for a given guest thread.
652fa70f025SJoel Dice #[derive(Debug)]
653fa70f025SJoel Dice struct GuestCall {
654e06fbf70SSy Brand     thread: QualifiedThreadId,
655fa70f025SJoel Dice     kind: GuestCallKind,
656fa70f025SJoel Dice }
657fa70f025SJoel Dice 
658fa70f025SJoel Dice impl GuestCall {
659fa70f025SJoel Dice     /// Returns whether or not the call is ready to run.
660fa70f025SJoel Dice     ///
661fa70f025SJoel Dice     /// A call will not be ready to run if either:
662fa70f025SJoel Dice     ///
663fa70f025SJoel Dice     /// - the (sub-)component instance to be called has already been entered and
664fa70f025SJoel Dice     /// cannot be reentered until an in-progress call completes
665fa70f025SJoel Dice     ///
666fa70f025SJoel Dice     /// - the call is for a not-yet started task and the (sub-)component
667fa70f025SJoel Dice     /// instance to be called has backpressure enabled
is_ready(&self, store: &mut StoreOpaque) -> Result<bool>668cb97ae85SJoel Dice     fn is_ready(&self, store: &mut StoreOpaque) -> Result<bool> {
669cb97ae85SJoel Dice         let instance = store
670cb97ae85SJoel Dice             .concurrent_state_mut()
671cb97ae85SJoel Dice             .get_mut(self.thread.task)?
672cb97ae85SJoel Dice             .instance;
67357f899c4SAlex Crichton         let state = store.instance_state(instance).concurrent_state();
674e06fbf70SSy Brand 
675fa70f025SJoel Dice         let ready = match &self.kind {
676fa70f025SJoel Dice             GuestCallKind::DeliverEvent { .. } => !state.do_not_enter,
677e06fbf70SSy Brand             GuestCallKind::StartImplicit(_) => !(state.do_not_enter || state.backpressure > 0),
678e06fbf70SSy Brand             GuestCallKind::StartExplicit(_) => true,
679fa70f025SJoel Dice         };
680fa70f025SJoel Dice         log::trace!(
681fa70f025SJoel Dice             "call {self:?} ready? {ready} (do_not_enter: {}; backpressure: {})",
682fa70f025SJoel Dice             state.do_not_enter,
683fa70f025SJoel Dice             state.backpressure
684fa70f025SJoel Dice         );
685fa70f025SJoel Dice         Ok(ready)
686fa70f025SJoel Dice     }
687fa70f025SJoel Dice }
688fa70f025SJoel Dice 
689587ca6f2SJoel Dice /// Job to be run on a worker fiber.
690587ca6f2SJoel Dice enum WorkerItem {
691587ca6f2SJoel Dice     GuestCall(GuestCall),
6927e39c25eSJoel Dice     Function(AlwaysMut<Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send>>),
693587ca6f2SJoel Dice }
694587ca6f2SJoel Dice 
695fa70f025SJoel Dice /// Represents a pending work item to be handled by the event loop for a given
696fa70f025SJoel Dice /// component instance.
697fa70f025SJoel Dice enum WorkItem {
698fa70f025SJoel Dice     /// A host task to be pushed to `ConcurrentState::futures`.
699624c8235SJoel Dice     PushFuture(AlwaysMut<HostTaskFuture>),
700fa70f025SJoel Dice     /// A fiber to resume.
701fa70f025SJoel Dice     ResumeFiber(StoreFiber<'static>),
702d2fbd2deSAlex Crichton     /// A thread to resume.
703d2fbd2deSAlex Crichton     ResumeThread(RuntimeComponentInstanceIndex, QualifiedThreadId),
704fa70f025SJoel Dice     /// A pending call into guest code for a given guest task.
705d2fbd2deSAlex Crichton     GuestCall(RuntimeComponentInstanceIndex, GuestCall),
706587ca6f2SJoel Dice     /// A job to run on a worker fiber.
7077e39c25eSJoel Dice     WorkerFunction(AlwaysMut<Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send>>),
708fa70f025SJoel Dice }
709fa70f025SJoel Dice 
710fa70f025SJoel Dice impl fmt::Debug for WorkItem {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result711fa70f025SJoel Dice     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
712fa70f025SJoel Dice         match self {
713fa70f025SJoel Dice             Self::PushFuture(_) => f.debug_tuple("PushFuture").finish(),
714fa70f025SJoel Dice             Self::ResumeFiber(_) => f.debug_tuple("ResumeFiber").finish(),
715d2fbd2deSAlex Crichton             Self::ResumeThread(instance, thread) => f
716d2fbd2deSAlex Crichton                 .debug_tuple("ResumeThread")
717d2fbd2deSAlex Crichton                 .field(instance)
718d2fbd2deSAlex Crichton                 .field(thread)
719d2fbd2deSAlex Crichton                 .finish(),
720d2fbd2deSAlex Crichton             Self::GuestCall(instance, call) => f
721d2fbd2deSAlex Crichton                 .debug_tuple("GuestCall")
722d2fbd2deSAlex Crichton                 .field(instance)
723d2fbd2deSAlex Crichton                 .field(call)
724d2fbd2deSAlex Crichton                 .finish(),
725587ca6f2SJoel Dice             Self::WorkerFunction(_) => f.debug_tuple("WorkerFunction").finish(),
726fa70f025SJoel Dice         }
727fa70f025SJoel Dice     }
728fa70f025SJoel Dice }
729b221fca7SJoel Dice 
730e06fbf70SSy Brand /// Whether a suspension intrinsic was cancelled or completed
731e06fbf70SSy Brand #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
732e06fbf70SSy Brand pub(crate) enum WaitResult {
733e06fbf70SSy Brand     Cancelled,
734e06fbf70SSy Brand     Completed,
735e06fbf70SSy Brand }
736e06fbf70SSy Brand 
7377e39c25eSJoel Dice /// Poll the specified future until it completes on behalf of a guest->host call
7387e39c25eSJoel Dice /// using a sync-lowered import.
739fa70f025SJoel Dice ///
7407e39c25eSJoel Dice /// This is similar to `Instance::first_poll` except it's for sync-lowered
7417e39c25eSJoel Dice /// imports, meaning we don't need to handle cancellation and we can block the
7427e39c25eSJoel Dice /// caller until the task completes, at which point the caller can handle
7437e39c25eSJoel Dice /// lowering the result to the guest's stack and linear memory.
poll_and_block<R: Send + Sync + 'static>( store: &mut dyn VMStore, future: impl Future<Output = Result<R>> + Send + 'static, ) -> Result<R>7447e39c25eSJoel Dice pub(crate) fn poll_and_block<R: Send + Sync + 'static>(
7457e39c25eSJoel Dice     store: &mut dyn VMStore,
7467e39c25eSJoel Dice     future: impl Future<Output = Result<R>> + Send + 'static,
7477e39c25eSJoel Dice ) -> Result<R> {
7487e39c25eSJoel Dice     let state = store.concurrent_state_mut();
749da093747SAlex Crichton     let task = state.current_host_thread()?;
7507e39c25eSJoel Dice 
7517e39c25eSJoel Dice     // Wrap the future in a closure which will take care of stashing the result
7527e39c25eSJoel Dice     // in `GuestTask::result` and resuming this fiber when the host task
7537e39c25eSJoel Dice     // completes.
7547e39c25eSJoel Dice     let mut future = Box::pin(async move {
7557e39c25eSJoel Dice         let result = future.await?;
7567e39c25eSJoel Dice         tls::get(move |store| {
7577e39c25eSJoel Dice             let state = store.concurrent_state_mut();
758065baac4SAlex Crichton             let host_state = &mut state.get_mut(task)?.state;
759065baac4SAlex Crichton             assert!(matches!(host_state, HostTaskState::CalleeStarted));
760065baac4SAlex Crichton             *host_state = HostTaskState::CalleeFinished(Box::new(result));
7617e39c25eSJoel Dice 
7627e39c25eSJoel Dice             Waitable::Host(task).set_event(
7637e39c25eSJoel Dice                 state,
7647e39c25eSJoel Dice                 Some(Event::Subtask {
7657e39c25eSJoel Dice                     status: Status::Returned,
7667e39c25eSJoel Dice                 }),
7677e39c25eSJoel Dice             )?;
7687e39c25eSJoel Dice 
7697e39c25eSJoel Dice             Ok(())
7707e39c25eSJoel Dice         })
7717e39c25eSJoel Dice     }) as HostTaskFuture;
7727e39c25eSJoel Dice 
7737e39c25eSJoel Dice     // Finally, poll the future.  We can use a dummy `Waker` here because we'll
7747e39c25eSJoel Dice     // add the future to `ConcurrentState::futures` and poll it automatically
7757e39c25eSJoel Dice     // from the event loop if it doesn't complete immediately here.
7767e39c25eSJoel Dice     let poll = tls::set(store, || {
7777e39c25eSJoel Dice         future
7787e39c25eSJoel Dice             .as_mut()
7797e39c25eSJoel Dice             .poll(&mut Context::from_waker(&Waker::noop()))
7807e39c25eSJoel Dice     });
7817e39c25eSJoel Dice 
7827e39c25eSJoel Dice     match poll {
7837e39c25eSJoel Dice         // It completed immediately; check the result and delete the task.
7843764e757SAlex Crichton         Poll::Ready(result) => result?,
7853764e757SAlex Crichton 
7867e39c25eSJoel Dice         // It did not complete immediately; add it to
7873764e757SAlex Crichton         // `ConcurrentState::futures` so it will be polled via the event loop;
78835887491SSy Brand         // then use `GuestThread::sync_call_set` to wait for the task to
7897e39c25eSJoel Dice         // complete, suspending the current fiber until it does so.
7903764e757SAlex Crichton         Poll::Pending => {
7917e39c25eSJoel Dice             let state = store.concurrent_state_mut();
7927e39c25eSJoel Dice             state.push_future(future);
7937e39c25eSJoel Dice 
7943764e757SAlex Crichton             let caller = state.get_mut(task)?.caller;
79535887491SSy Brand             let set = state.get_mut(caller.thread)?.sync_call_set;
7967e39c25eSJoel Dice             Waitable::Host(task).join(state, Some(set))?;
7977e39c25eSJoel Dice 
798e06fbf70SSy Brand             store.suspend(SuspendReason::Waiting {
799e06fbf70SSy Brand                 set,
800e06fbf70SSy Brand                 thread: caller,
8018992b99bSJoel Dice                 skip_may_block_check: false,
802e06fbf70SSy Brand             })?;
8033764e757SAlex Crichton 
8043764e757SAlex Crichton             // Remove the `task` from the `sync_call_set` to ensure that when
8053764e757SAlex Crichton             // this function returns and the task is deleted that there are no
8063764e757SAlex Crichton             // more lingering references to this host task.
8073764e757SAlex Crichton             Waitable::Host(task).join(store.concurrent_state_mut(), None)?;
8087e39c25eSJoel Dice         }
809b221fca7SJoel Dice     }
810b221fca7SJoel Dice 
8117e39c25eSJoel Dice     // Retrieve and return the result.
812065baac4SAlex Crichton     let host_state = &mut store.concurrent_state_mut().get_mut(task)?.state;
813e8cb8751SAlex Crichton     match mem::replace(host_state, HostTaskState::CalleeDone { cancelled: false }) {
814da093747SAlex Crichton         HostTaskState::CalleeFinished(result) => Ok(match result.downcast() {
815da093747SAlex Crichton             Ok(result) => *result,
816da093747SAlex Crichton             Err(_) => bail_bug!("host task finished with wrong type of result"),
817da093747SAlex Crichton         }),
818da093747SAlex Crichton         _ => bail_bug!("unexpected host task state after completion"),
819065baac4SAlex Crichton     }
820636435f1SJoel Dice }
821812dd1e8SJoel Dice 
8227e39c25eSJoel Dice /// Execute the specified guest call.
handle_guest_call(store: &mut dyn VMStore, call: GuestCall) -> Result<()>8237e39c25eSJoel Dice fn handle_guest_call(store: &mut dyn VMStore, call: GuestCall) -> Result<()> {
8248992b99bSJoel Dice     let mut next = Some(call);
8258992b99bSJoel Dice     while let Some(call) = next.take() {
8267e39c25eSJoel Dice         match call.kind {
8277e39c25eSJoel Dice             GuestCallKind::DeliverEvent { instance, set } => {
828da093747SAlex Crichton                 let (event, waitable) =
829da093747SAlex Crichton                     match instance.get_event(store, call.thread.task, set, true)? {
830da093747SAlex Crichton                         Some(pair) => pair,
831da093747SAlex Crichton                         None => bail_bug!("delivering non-present event"),
832da093747SAlex Crichton                     };
8337e39c25eSJoel Dice                 let state = store.concurrent_state_mut();
834e06fbf70SSy Brand                 let task = state.get_mut(call.thread.task)?;
8357e39c25eSJoel Dice                 let runtime_instance = task.instance;
8367e39c25eSJoel Dice                 let handle = waitable.map(|(_, v)| v).unwrap_or(0);
8377e39c25eSJoel Dice 
8387e39c25eSJoel Dice                 log::trace!(
8397e39c25eSJoel Dice                     "use callback to deliver event {event:?} to {:?} for {waitable:?}",
840e06fbf70SSy Brand                     call.thread,
8417e39c25eSJoel Dice                 );
8427e39c25eSJoel Dice 
843da093747SAlex Crichton                 let old_thread = store.set_thread(call.thread)?;
8447e39c25eSJoel Dice                 log::trace!(
845e06fbf70SSy Brand                     "GuestCallKind::DeliverEvent: replaced {old_thread:?} with {:?} as current thread",
846e06fbf70SSy Brand                     call.thread
8477e39c25eSJoel Dice                 );
8487e39c25eSJoel Dice 
849cb97ae85SJoel Dice                 store.enter_instance(runtime_instance);
8507e39c25eSJoel Dice 
851da093747SAlex Crichton                 let Some(callback) = store
852cb97ae85SJoel Dice                     .concurrent_state_mut()
853cb97ae85SJoel Dice                     .get_mut(call.thread.task)?
854cb97ae85SJoel Dice                     .callback
855cb97ae85SJoel Dice                     .take()
856da093747SAlex Crichton                 else {
857da093747SAlex Crichton                     bail_bug!("guest task callback field not present")
858da093747SAlex Crichton                 };
8597e39c25eSJoel Dice 
860b856261dSJoel Dice                 let code = callback(store, event, handle)?;
8617e39c25eSJoel Dice 
862cb97ae85SJoel Dice                 store
863cb97ae85SJoel Dice                     .concurrent_state_mut()
864cb97ae85SJoel Dice                     .get_mut(call.thread.task)?
865cb97ae85SJoel Dice                     .callback = Some(callback);
8667e39c25eSJoel Dice 
867cb97ae85SJoel Dice                 store.exit_instance(runtime_instance)?;
8687e39c25eSJoel Dice 
869da093747SAlex Crichton                 store.set_thread(old_thread)?;
870fae9e6afSJoel Dice 
871cb97ae85SJoel Dice                 next = instance.handle_callback_code(
872cb97ae85SJoel Dice                     store,
873cb97ae85SJoel Dice                     call.thread,
874cb97ae85SJoel Dice                     runtime_instance.index,
875cb97ae85SJoel Dice                     code,
876cb97ae85SJoel Dice                 )?;
8777e39c25eSJoel Dice 
8788992b99bSJoel Dice                 log::trace!(
8798992b99bSJoel Dice                     "GuestCallKind::DeliverEvent: restored {old_thread:?} as current thread"
8808992b99bSJoel Dice                 );
8817e39c25eSJoel Dice             }
882e06fbf70SSy Brand             GuestCallKind::StartImplicit(fun) => {
8838992b99bSJoel Dice                 next = fun(store)?;
884e06fbf70SSy Brand             }
885e06fbf70SSy Brand             GuestCallKind::StartExplicit(fun) => {
8867e39c25eSJoel Dice                 fun(store)?;
8877e39c25eSJoel Dice             }
8887e39c25eSJoel Dice         }
8898992b99bSJoel Dice     }
8907e39c25eSJoel Dice 
891fa70f025SJoel Dice     Ok(())
892beca86b0SAlex Crichton }
8937e39c25eSJoel Dice 
8947e39c25eSJoel Dice impl<T> Store<T> {
8957e39c25eSJoel Dice     /// Convenience wrapper for [`StoreContextMut::run_concurrent`].
run_concurrent<R>(&mut self, fun: impl AsyncFnOnce(&Accessor<T>) -> R) -> Result<R> where T: Send + 'static,8967e39c25eSJoel Dice     pub async fn run_concurrent<R>(&mut self, fun: impl AsyncFnOnce(&Accessor<T>) -> R) -> Result<R>
8977e39c25eSJoel Dice     where
8987e39c25eSJoel Dice         T: Send + 'static,
8997e39c25eSJoel Dice     {
90063679896SNick Fitzgerald         ensure!(
90121797bb5SAlex Crichton             self.as_context().0.concurrency_support(),
90221797bb5SAlex Crichton             "cannot use `run_concurrent` when Config::concurrency_support disabled",
90363679896SNick Fitzgerald         );
9047e39c25eSJoel Dice         self.as_context_mut().run_concurrent(fun).await
905beca86b0SAlex Crichton     }
906beca86b0SAlex Crichton 
9077e39c25eSJoel Dice     #[doc(hidden)]
assert_concurrent_state_empty(&mut self)9087e39c25eSJoel Dice     pub fn assert_concurrent_state_empty(&mut self) {
9097e39c25eSJoel Dice         self.as_context_mut().assert_concurrent_state_empty();
9107e39c25eSJoel Dice     }
9117e39c25eSJoel Dice 
9123764e757SAlex Crichton     #[doc(hidden)]
concurrent_state_table_size(&mut self) -> usize9133764e757SAlex Crichton     pub fn concurrent_state_table_size(&mut self) -> usize {
9143764e757SAlex Crichton         self.as_context_mut().concurrent_state_table_size()
9153764e757SAlex Crichton     }
9163764e757SAlex Crichton 
9177e39c25eSJoel Dice     /// Convenience wrapper for [`StoreContextMut::spawn`].
spawn(&mut self, task: impl AccessorTask<T, HasSelf<T>>) -> JoinHandle where T: 'static,918fee9be21SAlex Crichton     pub fn spawn(&mut self, task: impl AccessorTask<T, HasSelf<T>>) -> JoinHandle
9197e39c25eSJoel Dice     where
9207e39c25eSJoel Dice         T: 'static,
9217e39c25eSJoel Dice     {
9227e39c25eSJoel Dice         self.as_context_mut().spawn(task)
9237e39c25eSJoel Dice     }
9247e39c25eSJoel Dice }
9257e39c25eSJoel Dice 
9267e39c25eSJoel Dice impl<T> StoreContextMut<'_, T> {
927fa70f025SJoel Dice     /// Assert that all the relevant tables and queues in the concurrent state
9287e39c25eSJoel Dice     /// for this store are empty.
929fa70f025SJoel Dice     ///
930fa70f025SJoel Dice     /// This is for sanity checking in integration tests
931fa70f025SJoel Dice     /// (e.g. `component-async-tests`) that the relevant state has been cleared
932fa70f025SJoel Dice     /// after each test concludes.  This should help us catch leaks, e.g. guest
933fa70f025SJoel Dice     /// tasks which haven't been deleted despite having completed and having
934fa70f025SJoel Dice     /// been dropped by their supertasks.
9353764e757SAlex Crichton     ///
9363764e757SAlex Crichton     /// Only intended for use in Wasmtime's own testing.
937fa70f025SJoel Dice     #[doc(hidden)]
assert_concurrent_state_empty(self)9387e39c25eSJoel Dice     pub fn assert_concurrent_state_empty(self) {
9397e39c25eSJoel Dice         let store = self.0;
9407e39c25eSJoel Dice         store
9417e39c25eSJoel Dice             .store_data_mut()
9427e39c25eSJoel Dice             .components
943cb97ae85SJoel Dice             .assert_instance_states_empty();
9447e39c25eSJoel Dice         let state = store.concurrent_state_mut();
945624c8235SJoel Dice         assert!(
946624c8235SJoel Dice             state.table.get_mut().is_empty(),
947624c8235SJoel Dice             "non-empty table: {:?}",
948078364f6SJoel Dice             state.table.get_mut()
949624c8235SJoel Dice         );
950fa70f025SJoel Dice         assert!(state.high_priority.is_empty());
951fa70f025SJoel Dice         assert!(state.low_priority.is_empty());
9523764e757SAlex Crichton         assert!(state.current_thread.is_none());
953da093747SAlex Crichton         assert!(state.futures_mut().unwrap().is_empty());
954fa70f025SJoel Dice         assert!(state.global_error_context_ref_counts.is_empty());
955fa70f025SJoel Dice     }
956fa70f025SJoel Dice 
9573764e757SAlex Crichton     /// Helper function to perform tests over the size of the concurrent state
9583764e757SAlex Crichton     /// table which can be useful for detecting leaks.
9593764e757SAlex Crichton     ///
9603764e757SAlex Crichton     /// Only intended for use in Wasmtime's own testing.
9613764e757SAlex Crichton     #[doc(hidden)]
concurrent_state_table_size(&mut self) -> usize9623764e757SAlex Crichton     pub fn concurrent_state_table_size(&mut self) -> usize {
9633764e757SAlex Crichton         self.0
9643764e757SAlex Crichton             .concurrent_state_mut()
9653764e757SAlex Crichton             .table
9663764e757SAlex Crichton             .get_mut()
9673764e757SAlex Crichton             .iter_mut()
9683764e757SAlex Crichton             .count()
9693764e757SAlex Crichton     }
9703764e757SAlex Crichton 
9717e39c25eSJoel Dice     /// Spawn a background task to run as part of this instance's event loop.
9727e39c25eSJoel Dice     ///
9737e39c25eSJoel Dice     /// The task will receive an `&Accessor<U>` and run concurrently with
9747e39c25eSJoel Dice     /// any other tasks in progress for the instance.
9757e39c25eSJoel Dice     ///
9767e39c25eSJoel Dice     /// Note that the task will only make progress if and when the event loop
9777e39c25eSJoel Dice     /// for this instance is run.
9787e39c25eSJoel Dice     ///
979bc4582c3SAlex Crichton     /// The returned [`JoinHandle`] may be used to cancel the task.
spawn(mut self, task: impl AccessorTask<T>) -> JoinHandle where T: 'static,980fee9be21SAlex Crichton     pub fn spawn(mut self, task: impl AccessorTask<T>) -> JoinHandle
9817e39c25eSJoel Dice     where
9827e39c25eSJoel Dice         T: 'static,
9837e39c25eSJoel Dice     {
9847e39c25eSJoel Dice         let accessor = Accessor::new(StoreToken::new(self.as_context_mut()));
9857e39c25eSJoel Dice         self.spawn_with_accessor(accessor, task)
9867e39c25eSJoel Dice     }
9877e39c25eSJoel Dice 
9887e39c25eSJoel Dice     /// Internal implementation of `spawn` functions where a `store` is
9897e39c25eSJoel Dice     /// available along with an `Accessor`.
spawn_with_accessor<D>( self, accessor: Accessor<T, D>, task: impl AccessorTask<T, D>, ) -> JoinHandle where T: 'static, D: HasData + ?Sized,9907e39c25eSJoel Dice     fn spawn_with_accessor<D>(
9917e39c25eSJoel Dice         self,
9927e39c25eSJoel Dice         accessor: Accessor<T, D>,
993fee9be21SAlex Crichton         task: impl AccessorTask<T, D>,
9947e39c25eSJoel Dice     ) -> JoinHandle
9957e39c25eSJoel Dice     where
9967e39c25eSJoel Dice         T: 'static,
9977e39c25eSJoel Dice         D: HasData + ?Sized,
9987e39c25eSJoel Dice     {
9997e39c25eSJoel Dice         // Create an "abortable future" here where internally the future will
10007e39c25eSJoel Dice         // hook calls to poll and possibly spawn more background tasks on each
10017e39c25eSJoel Dice         // iteration.
10027e39c25eSJoel Dice         let (handle, future) = JoinHandle::run(async move { task.run(&accessor).await });
10037e39c25eSJoel Dice         self.0
10047e39c25eSJoel Dice             .concurrent_state_mut()
10057e39c25eSJoel Dice             .push_future(Box::pin(async move { future.await.unwrap_or(Ok(())) }));
10067e39c25eSJoel Dice         handle
10077e39c25eSJoel Dice     }
10087e39c25eSJoel Dice 
10097e39c25eSJoel Dice     /// Run the specified closure `fun` to completion as part of this store's
101064bc3bd9SAlex Crichton     /// event loop.
1011fa70f025SJoel Dice     ///
10127e39c25eSJoel Dice     /// This will run `fun` as part of this store's event loop until it
1013ec68a031SJoel Dice     /// yields a result.  `fun` is provided an [`Accessor`], which provides
10147e39c25eSJoel Dice     /// controlled access to the store and its data.
1015fa70f025SJoel Dice     ///
101664bc3bd9SAlex Crichton     /// This function can be used to invoke [`Func::call_concurrent`] for
101764bc3bd9SAlex Crichton     /// example within the async closure provided here.
1018fa70f025SJoel Dice     ///
101921797bb5SAlex Crichton     /// This function will unconditionally return an error if
102021797bb5SAlex Crichton     /// [`Config::concurrency_support`] is disabled.
102121797bb5SAlex Crichton     ///
102221797bb5SAlex Crichton     /// [`Config::concurrency_support`]: crate::Config::concurrency_support
102321797bb5SAlex Crichton     ///
1024d6e7841fSAlex Crichton     /// # Store-blocking behavior
1025d6e7841fSAlex Crichton     ///
1026d6e7841fSAlex Crichton     /// At this time there are certain situations in which the `Future` returned
1027d6e7841fSAlex Crichton     /// by the `AsyncFnOnce` passed to this function will not be polled for an
1028d6e7841fSAlex Crichton     /// extended period of time, despite one or more `Waker::wake` events having
1029d6e7841fSAlex Crichton     /// occurred for the task to which it belongs.  This can manifest as the
1030d6e7841fSAlex Crichton     /// `Future` seeming to be "blocked" or "locked up", but is actually due to
1031d6e7841fSAlex Crichton     /// the `Store` being held by e.g. a blocking host function, preventing the
1032d6e7841fSAlex Crichton     /// `Future` from being polled. A canonical example of this is when the
1033d6e7841fSAlex Crichton     /// `fun` provided to this function attempts to set a timeout for an
1034d6e7841fSAlex Crichton     /// invocation of a wasm function. In this situation the async closure is
1035d6e7841fSAlex Crichton     /// waiting both on (a) the wasm computation to finish, and (b) the timeout
1036d6e7841fSAlex Crichton     /// to elapse. At this time this setup will not always work and the timeout
1037d6e7841fSAlex Crichton     /// may not reliably fire.
1038d6e7841fSAlex Crichton     ///
1039d6e7841fSAlex Crichton     /// This function will not block the current thread and as such is always
1040d6e7841fSAlex Crichton     /// suitable to run in an `async` context, but the current implementation of
1041d6e7841fSAlex Crichton     /// Wasmtime can lead to situations where a certain wasm computation is
1042d6e7841fSAlex Crichton     /// required to make progress the closure to make progress. This is an
1043d6e7841fSAlex Crichton     /// artifact of Wasmtime's historical implementation of `async` functions
1044d6e7841fSAlex Crichton     /// and is the topic of [#11869] and [#11870]. In the timeout example from
1045d6e7841fSAlex Crichton     /// above it means that Wasmtime can get "wedged" for a bit where (a) must
1046d6e7841fSAlex Crichton     /// progress for a readiness notification of (b) to get delivered.
1047d6e7841fSAlex Crichton     ///
1048d6e7841fSAlex Crichton     /// This effectively means that it's not possible to reliably perform a
1049d6e7841fSAlex Crichton     /// "select" operation within the `fun` closure, which timeouts for example
1050d6e7841fSAlex Crichton     /// are based on. Fixing this requires some relatively major refactoring
1051d6e7841fSAlex Crichton     /// work within Wasmtime itself. This is a known pitfall otherwise and one
1052d6e7841fSAlex Crichton     /// that is intended to be fixed one day. In the meantime it's recommended
1053d6e7841fSAlex Crichton     /// to apply timeouts or such to the entire `run_concurrent` call itself
1054d6e7841fSAlex Crichton     /// rather than internally.
1055d6e7841fSAlex Crichton     ///
1056d6e7841fSAlex Crichton     /// [#11869]: https://github.com/bytecodealliance/wasmtime/issues/11869
1057d6e7841fSAlex Crichton     /// [#11870]: https://github.com/bytecodealliance/wasmtime/issues/11870
1058d6e7841fSAlex Crichton     ///
105964bc3bd9SAlex Crichton     /// # Example
1060fa70f025SJoel Dice     ///
1061fa70f025SJoel Dice     /// ```
1062fa70f025SJoel Dice     /// # use {
1063fa70f025SJoel Dice     /// #   wasmtime::{
106496e19700SNick Fitzgerald     /// #     error::{Result},
1065fa70f025SJoel Dice     /// #     component::{ Component, Linker, Resource, ResourceTable},
1066fa70f025SJoel Dice     /// #     Config, Engine, Store
1067fa70f025SJoel Dice     /// #   },
1068fa70f025SJoel Dice     /// # };
1069fa70f025SJoel Dice     /// #
1070fa70f025SJoel Dice     /// # struct MyResource(u32);
1071fa70f025SJoel Dice     /// # struct Ctx { table: ResourceTable }
1072fa70f025SJoel Dice     /// #
1073fa70f025SJoel Dice     /// # async fn foo() -> Result<()> {
1074fa70f025SJoel Dice     /// # let mut config = Config::new();
1075fa70f025SJoel Dice     /// # let engine = Engine::new(&config)?;
1076fa70f025SJoel Dice     /// # let mut store = Store::new(&engine, Ctx { table: ResourceTable::new() });
1077fa70f025SJoel Dice     /// # let mut linker = Linker::new(&engine);
1078fa70f025SJoel Dice     /// # let component = Component::new(&engine, "")?;
1079fa70f025SJoel Dice     /// # let instance = linker.instantiate_async(&mut store, &component).await?;
1080fa70f025SJoel Dice     /// # let foo = instance.get_typed_func::<(Resource<MyResource>,), (Resource<MyResource>,)>(&mut store, "foo")?;
1081fa70f025SJoel Dice     /// # let bar = instance.get_typed_func::<(u32,), ()>(&mut store, "bar")?;
10827e39c25eSJoel Dice     /// store.run_concurrent(async |accessor| -> wasmtime::Result<_> {
108364bc3bd9SAlex Crichton     ///    let resource = accessor.with(|mut access| access.get().table.push(MyResource(42)))?;
10841e0b0b46SAlex Crichton     ///    let (another_resource,) = foo.call_concurrent(accessor, (resource,)).await?;
108564bc3bd9SAlex Crichton     ///    let value = accessor.with(|mut access| access.get().table.delete(another_resource))?;
108664bc3bd9SAlex Crichton     ///    bar.call_concurrent(accessor, (value.0,)).await?;
108764bc3bd9SAlex Crichton     ///    Ok(())
108864bc3bd9SAlex Crichton     /// }).await??;
1089fa70f025SJoel Dice     /// # Ok(())
1090fa70f025SJoel Dice     /// # }
1091fa70f025SJoel Dice     /// ```
run_concurrent<R>(self, fun: impl AsyncFnOnce(&Accessor<T>) -> R) -> Result<R> where T: Send + 'static,10927e39c25eSJoel Dice     pub async fn run_concurrent<R>(self, fun: impl AsyncFnOnce(&Accessor<T>) -> R) -> Result<R>
10937e39c25eSJoel Dice     where
10947e39c25eSJoel Dice         T: Send + 'static,
10957e39c25eSJoel Dice     {
109663679896SNick Fitzgerald         ensure!(
109721797bb5SAlex Crichton             self.0.concurrency_support(),
109821797bb5SAlex Crichton             "cannot use `run_concurrent` when Config::concurrency_support disabled",
109963679896SNick Fitzgerald         );
11007e39c25eSJoel Dice         self.do_run_concurrent(fun, false).await
11017e39c25eSJoel Dice     }
11027e39c25eSJoel Dice 
run_concurrent_trap_on_idle<R>( self, fun: impl AsyncFnOnce(&Accessor<T>) -> R, ) -> Result<R> where T: Send + 'static,11037e39c25eSJoel Dice     pub(super) async fn run_concurrent_trap_on_idle<R>(
110464bc3bd9SAlex Crichton         self,
1105ec68a031SJoel Dice         fun: impl AsyncFnOnce(&Accessor<T>) -> R,
1106ec68a031SJoel Dice     ) -> Result<R>
1107ec68a031SJoel Dice     where
1108ec68a031SJoel Dice         T: Send + 'static,
1109ec68a031SJoel Dice     {
11107e39c25eSJoel Dice         self.do_run_concurrent(fun, true).await
1111ec68a031SJoel Dice     }
1112ec68a031SJoel Dice 
do_run_concurrent<R>( mut self, fun: impl AsyncFnOnce(&Accessor<T>) -> R, trap_on_idle: bool, ) -> Result<R> where T: Send + 'static,11137e39c25eSJoel Dice     async fn do_run_concurrent<R>(
11147e39c25eSJoel Dice         mut self,
111564bc3bd9SAlex Crichton         fun: impl AsyncFnOnce(&Accessor<T>) -> R,
1116ec68a031SJoel Dice         trap_on_idle: bool,
111764bc3bd9SAlex Crichton     ) -> Result<R>
1118fa70f025SJoel Dice     where
1119aa91737eSAlex Crichton         T: Send + 'static,
1120fa70f025SJoel Dice     {
112121797bb5SAlex Crichton         debug_assert!(self.0.concurrency_support());
1122fa70f025SJoel Dice         check_recursive_run();
11237e39c25eSJoel Dice         let token = StoreToken::new(self.as_context_mut());
1124da265515SAlex Crichton 
1125b4475438SJoel Dice         struct Dropper<'a, T: 'static, V> {
1126b4475438SJoel Dice             store: StoreContextMut<'a, T>,
1127b4475438SJoel Dice             value: ManuallyDrop<V>,
1128b4475438SJoel Dice         }
1129b4475438SJoel Dice 
1130b4475438SJoel Dice         impl<'a, T, V> Drop for Dropper<'a, T, V> {
1131b4475438SJoel Dice             fn drop(&mut self) {
11328aefdcc0SAlex Crichton                 tls::set(self.store.0, || {
1133b4475438SJoel Dice                     // SAFETY: Here we drop the value without moving it for the
1134b4475438SJoel Dice                     // first and only time -- per the contract for `Drop::drop`,
1135b4475438SJoel Dice                     // this code won't run again, and the `value` field will no
1136b4475438SJoel Dice                     // longer be accessible.
1137b4475438SJoel Dice                     unsafe { ManuallyDrop::drop(&mut self.value) }
1138b4475438SJoel Dice                 });
1139b4475438SJoel Dice             }
1140b4475438SJoel Dice         }
1141b4475438SJoel Dice 
11427e39c25eSJoel Dice         let accessor = &Accessor::new(token);
1143b4475438SJoel Dice         let dropper = &mut Dropper {
11447e39c25eSJoel Dice             store: self,
1145b4475438SJoel Dice             value: ManuallyDrop::new(fun(accessor)),
1146b4475438SJoel Dice         };
1147b4475438SJoel Dice         // SAFETY: We never move `dropper` nor its `value` field.
1148b4475438SJoel Dice         let future = unsafe { Pin::new_unchecked(dropper.value.deref_mut()) };
1149b4475438SJoel Dice 
11507e39c25eSJoel Dice         dropper
11517e39c25eSJoel Dice             .store
11527e39c25eSJoel Dice             .as_context_mut()
11537e39c25eSJoel Dice             .poll_until(future, trap_on_idle)
1154da265515SAlex Crichton             .await
1155fa70f025SJoel Dice     }
1156fa70f025SJoel Dice 
11577e39c25eSJoel Dice     /// Run this store's event loop.
1158fa70f025SJoel Dice     ///
11597e39c25eSJoel Dice     /// The returned future will resolve when the specified future completes or,
11607e39c25eSJoel Dice     /// if `trap_on_idle` is true, when the event loop can't make further
11617e39c25eSJoel Dice     /// progress.
poll_until<R>( mut self, mut future: Pin<&mut impl Future<Output = R>>, trap_on_idle: bool, ) -> Result<R> where T: Send + 'static,11627e39c25eSJoel Dice     async fn poll_until<R>(
11637e39c25eSJoel Dice         mut self,
1164b4475438SJoel Dice         mut future: Pin<&mut impl Future<Output = R>>,
1165ec68a031SJoel Dice         trap_on_idle: bool,
1166aa91737eSAlex Crichton     ) -> Result<R>
1167aa91737eSAlex Crichton     where
1168aca2a573SJoel Dice         T: Send + 'static,
1169aa91737eSAlex Crichton     {
1170aca2a573SJoel Dice         struct Reset<'a, T: 'static> {
1171aca2a573SJoel Dice             store: StoreContextMut<'a, T>,
1172aca2a573SJoel Dice             futures: Option<FuturesUnordered<HostTaskFuture>>,
1173aca2a573SJoel Dice         }
1174aca2a573SJoel Dice 
1175aca2a573SJoel Dice         impl<'a, T> Drop for Reset<'a, T> {
1176aca2a573SJoel Dice             fn drop(&mut self) {
1177aca2a573SJoel Dice                 if let Some(futures) = self.futures.take() {
11787e39c25eSJoel Dice                     *self.store.0.concurrent_state_mut().futures.get_mut() = Some(futures);
1179aca2a573SJoel Dice                 }
1180aca2a573SJoel Dice             }
1181aca2a573SJoel Dice         }
1182aca2a573SJoel Dice 
1183fa70f025SJoel Dice         loop {
1184dcd65446SJoel Dice             // Take `ConcurrentState::futures` out of the store so we can poll
1185dcd65446SJoel Dice             // it while also safely giving any of the futures inside access to
1186dcd65446SJoel Dice             // `self`.
11877e39c25eSJoel Dice             let futures = self.0.concurrent_state_mut().futures.get_mut().take();
1188aca2a573SJoel Dice             let mut reset = Reset {
11897e39c25eSJoel Dice                 store: self.as_context_mut(),
1190aca2a573SJoel Dice                 futures,
1191aca2a573SJoel Dice             };
1192da093747SAlex Crichton             let mut next = match reset.futures.as_mut() {
1193da093747SAlex Crichton                 Some(f) => pin!(f.next()),
1194da093747SAlex Crichton                 None => bail_bug!("concurrent state missing futures field"),
1195da093747SAlex Crichton             };
1196fa70f025SJoel Dice 
1197bde99243SSy Brand             enum PollResult<R> {
1198bde99243SSy Brand                 Complete(R),
1199bde99243SSy Brand                 ProcessWork(Vec<WorkItem>),
1200bde99243SSy Brand             }
1201fa70f025SJoel Dice             let result = future::poll_fn(|cx| {
1202fa70f025SJoel Dice                 // First, poll the future we were passed as an argument and
1203fa70f025SJoel Dice                 // return immediately if it's ready.
12047e39c25eSJoel Dice                 if let Poll::Ready(value) = tls::set(reset.store.0, || future.as_mut().poll(cx)) {
1205bde99243SSy Brand                     return Poll::Ready(Ok(PollResult::Complete(value)));
1206fa70f025SJoel Dice                 }
1207fa70f025SJoel Dice 
1208fa70f025SJoel Dice                 // Next, poll `ConcurrentState::futures` (which includes any
1209fa70f025SJoel Dice                 // pending host tasks and/or background tasks), returning
1210fa70f025SJoel Dice                 // immediately if one of them fails.
12117e39c25eSJoel Dice                 let next = match tls::set(reset.store.0, || next.as_mut().poll(cx)) {
1212fa70f025SJoel Dice                     Poll::Ready(Some(output)) => {
1213587ca6f2SJoel Dice                         match output {
12145764da5fSJoel Dice                             Err(e) => return Poll::Ready(Err(e)),
12155764da5fSJoel Dice                             Ok(()) => {}
1216fa70f025SJoel Dice                         }
1217fa70f025SJoel Dice                         Poll::Ready(true)
1218fa70f025SJoel Dice                     }
1219fa70f025SJoel Dice                     Poll::Ready(None) => Poll::Ready(false),
1220fa70f025SJoel Dice                     Poll::Pending => Poll::Pending,
1221fa70f025SJoel Dice                 };
1222fa70f025SJoel Dice 
1223bde99243SSy Brand                 // Next, collect the next batch of work items to process, if any.
1224bde99243SSy Brand                 // This will be either all of the high-priority work items, or if
1225bde99243SSy Brand                 // there are none, a single low-priority work item.
12267e39c25eSJoel Dice                 let state = reset.store.0.concurrent_state_mut();
1227bde99243SSy Brand                 let ready = state.collect_work_items_to_run();
1228bde99243SSy Brand                 if !ready.is_empty() {
1229bde99243SSy Brand                     return Poll::Ready(Ok(PollResult::ProcessWork(ready)));
1230bde99243SSy Brand                 }
1231bde99243SSy Brand 
1232bde99243SSy Brand                 // Finally, if we have nothing else to do right now, determine what to do
1233bde99243SSy Brand                 // based on whether there are any pending futures in
1234bde99243SSy Brand                 // `ConcurrentState::futures`.
1235fa70f025SJoel Dice                 return match next {
1236587ca6f2SJoel Dice                     Poll::Ready(true) => {
1237fa70f025SJoel Dice                         // In this case, one of the futures in
1238fa70f025SJoel Dice                         // `ConcurrentState::futures` completed
1239587ca6f2SJoel Dice                         // successfully, so we return now and continue
1240587ca6f2SJoel Dice                         // the outer loop in case there is another one
1241587ca6f2SJoel Dice                         // ready to complete.
1242bde99243SSy Brand                         Poll::Ready(Ok(PollResult::ProcessWork(Vec::new())))
1243587ca6f2SJoel Dice                     }
1244fa70f025SJoel Dice                     Poll::Ready(false) => {
1245b4475438SJoel Dice                         // Poll the future we were passed one last time
1246b4475438SJoel Dice                         // in case one of `ConcurrentState::futures` had
1247b4475438SJoel Dice                         // the side effect of unblocking it.
1248b4475438SJoel Dice                         if let Poll::Ready(value) =
12497e39c25eSJoel Dice                             tls::set(reset.store.0, || future.as_mut().poll(cx))
1250b4475438SJoel Dice                         {
1251bde99243SSy Brand                             Poll::Ready(Ok(PollResult::Complete(value)))
1252b4475438SJoel Dice                         } else {
1253b4475438SJoel Dice                             // In this case, there are no more pending
1254b4475438SJoel Dice                             // futures in `ConcurrentState::futures`,
1255b4475438SJoel Dice                             // there are no remaining work items, _and_
1256b4475438SJoel Dice                             // the future we were passed as an argument
1257ec68a031SJoel Dice                             // still hasn't completed.
1258ec68a031SJoel Dice                             if trap_on_idle {
1259ec68a031SJoel Dice                                 // `trap_on_idle` is true, so we exit
1260ec68a031SJoel Dice                                 // immediately.
1261da093747SAlex Crichton                                 Poll::Ready(Err(Trap::AsyncDeadlock.into()))
1262ec68a031SJoel Dice                             } else {
1263ec68a031SJoel Dice                                 // `trap_on_idle` is false, so we assume
1264ec68a031SJoel Dice                                 // that future will wake up and give us
1265ec68a031SJoel Dice                                 // more work to do when it's ready to.
1266ec68a031SJoel Dice                                 Poll::Pending
1267ec68a031SJoel Dice                             }
1268fa70f025SJoel Dice                         }
1269b4475438SJoel Dice                     }
1270fa70f025SJoel Dice                     // There is at least one pending future in
1271fa70f025SJoel Dice                     // `ConcurrentState::futures` and we have nothing
1272fa70f025SJoel Dice                     // else to do but wait for now, so we return
1273fa70f025SJoel Dice                     // `Pending`.
1274fa70f025SJoel Dice                     Poll::Pending => Poll::Pending,
1275fa70f025SJoel Dice                 };
1276fa70f025SJoel Dice             })
1277fa70f025SJoel Dice             .await;
1278fa70f025SJoel Dice 
1279dcd65446SJoel Dice             // Put the `ConcurrentState::futures` back into the store before we
1280dcd65446SJoel Dice             // return or handle any work items since one or more of those items
1281dcd65446SJoel Dice             // might append more futures.
1282aca2a573SJoel Dice             drop(reset);
1283fa70f025SJoel Dice 
1284fa70f025SJoel Dice             match result? {
1285fa70f025SJoel Dice                 // The future we were passed as an argument completed, so we
1286fa70f025SJoel Dice                 // return the result.
1287bde99243SSy Brand                 PollResult::Complete(value) => break Ok(value),
1288fa70f025SJoel Dice                 // The future we were passed has not yet completed, so handle
1289fa70f025SJoel Dice                 // any work items and then loop again.
1290bde99243SSy Brand                 PollResult::ProcessWork(ready) => {
1291cefa3bf7SJoel Dice                     struct Dispose<'a, T: 'static, I: Iterator<Item = WorkItem>> {
1292cefa3bf7SJoel Dice                         store: StoreContextMut<'a, T>,
1293cefa3bf7SJoel Dice                         ready: I,
1294cefa3bf7SJoel Dice                     }
1295cefa3bf7SJoel Dice 
1296cefa3bf7SJoel Dice                     impl<'a, T, I: Iterator<Item = WorkItem>> Drop for Dispose<'a, T, I> {
1297cefa3bf7SJoel Dice                         fn drop(&mut self) {
1298cefa3bf7SJoel Dice                             while let Some(item) = self.ready.next() {
1299cefa3bf7SJoel Dice                                 match item {
1300cefa3bf7SJoel Dice                                     WorkItem::ResumeFiber(mut fiber) => fiber.dispose(self.store.0),
1301cefa3bf7SJoel Dice                                     WorkItem::PushFuture(future) => {
1302cefa3bf7SJoel Dice                                         tls::set(self.store.0, move || drop(future))
1303cefa3bf7SJoel Dice                                     }
1304cefa3bf7SJoel Dice                                     _ => {}
1305cefa3bf7SJoel Dice                                 }
1306cefa3bf7SJoel Dice                             }
1307cefa3bf7SJoel Dice                         }
1308cefa3bf7SJoel Dice                     }
1309cefa3bf7SJoel Dice 
1310cefa3bf7SJoel Dice                     let mut dispose = Dispose {
13117e39c25eSJoel Dice                         store: self.as_context_mut(),
1312cefa3bf7SJoel Dice                         ready: ready.into_iter(),
1313cefa3bf7SJoel Dice                     };
1314cefa3bf7SJoel Dice 
1315cefa3bf7SJoel Dice                     while let Some(item) = dispose.ready.next() {
13167e39c25eSJoel Dice                         dispose
13177e39c25eSJoel Dice                             .store
13187e39c25eSJoel Dice                             .as_context_mut()
13197e39c25eSJoel Dice                             .handle_work_item(item)
1320cefa3bf7SJoel Dice                             .await?;
1321fa70f025SJoel Dice                     }
1322fa70f025SJoel Dice                 }
1323fa70f025SJoel Dice             }
1324fa70f025SJoel Dice         }
1325fa70f025SJoel Dice     }
1326fa70f025SJoel Dice 
1327fa70f025SJoel Dice     /// Handle the specified work item, possibly resuming a fiber if applicable.
handle_work_item(self, item: WorkItem) -> Result<()> where T: Send,13287e39c25eSJoel Dice     async fn handle_work_item(self, item: WorkItem) -> Result<()>
13297e39c25eSJoel Dice     where
13307e39c25eSJoel Dice         T: Send,
13317e39c25eSJoel Dice     {
1332fa70f025SJoel Dice         log::trace!("handle work item {item:?}");
1333fa70f025SJoel Dice         match item {
1334fa70f025SJoel Dice             WorkItem::PushFuture(future) => {
13357e39c25eSJoel Dice                 self.0
13367e39c25eSJoel Dice                     .concurrent_state_mut()
1337da093747SAlex Crichton                     .futures_mut()?
1338624c8235SJoel Dice                     .push(future.into_inner());
1339fa70f025SJoel Dice             }
1340fa70f025SJoel Dice             WorkItem::ResumeFiber(fiber) => {
13417e39c25eSJoel Dice                 self.0.resume_fiber(fiber).await?;
1342fa70f025SJoel Dice             }
1343d2fbd2deSAlex Crichton             WorkItem::ResumeThread(_, thread) => {
1344d2fbd2deSAlex Crichton                 if let GuestThreadState::Ready(fiber) = mem::replace(
1345d2fbd2deSAlex Crichton                     &mut self.0.concurrent_state_mut().get_mut(thread.thread)?.state,
1346d2fbd2deSAlex Crichton                     GuestThreadState::Running,
1347d2fbd2deSAlex Crichton                 ) {
1348d2fbd2deSAlex Crichton                     self.0.resume_fiber(fiber).await?;
1349d2fbd2deSAlex Crichton                 } else {
1350da093747SAlex Crichton                     bail_bug!("cannot resume non-pending thread {thread:?}");
1351d2fbd2deSAlex Crichton                 }
1352d2fbd2deSAlex Crichton             }
1353d2fbd2deSAlex Crichton             WorkItem::GuestCall(_, call) => {
1354cb97ae85SJoel Dice                 if call.is_ready(self.0)? {
13557e39c25eSJoel Dice                     self.run_on_worker(WorkerItem::GuestCall(call)).await?;
1356fa70f025SJoel Dice                 } else {
1357cb97ae85SJoel Dice                     let state = self.0.concurrent_state_mut();
1358e06fbf70SSy Brand                     let task = state.get_mut(call.thread.task)?;
1359fa70f025SJoel Dice                     if !task.starting_sent {
1360fa70f025SJoel Dice                         task.starting_sent = true;
1361e06fbf70SSy Brand                         if let GuestCallKind::StartImplicit(_) = &call.kind {
1362e06fbf70SSy Brand                             Waitable::Guest(call.thread.task).set_event(
1363fa70f025SJoel Dice                                 state,
1364fa70f025SJoel Dice                                 Some(Event::Subtask {
1365fa70f025SJoel Dice                                     status: Status::Starting,
1366fa70f025SJoel Dice                                 }),
1367fa70f025SJoel Dice                             )?;
1368fa70f025SJoel Dice                         }
1369fa70f025SJoel Dice                     }
1370fa70f025SJoel Dice 
1371cb97ae85SJoel Dice                     let instance = state.get_mut(call.thread.task)?.instance;
1372cb97ae85SJoel Dice                     self.0
1373cb97ae85SJoel Dice                         .instance_state(instance)
137457f899c4SAlex Crichton                         .concurrent_state()
1375fa70f025SJoel Dice                         .pending
1376e06fbf70SSy Brand                         .insert(call.thread, call.kind);
1377fa70f025SJoel Dice                 }
1378fa70f025SJoel Dice             }
1379587ca6f2SJoel Dice             WorkItem::WorkerFunction(fun) => {
13807e39c25eSJoel Dice                 self.run_on_worker(WorkerItem::Function(fun)).await?;
1381587ca6f2SJoel Dice             }
1382fa70f025SJoel Dice         }
1383fa70f025SJoel Dice 
1384fa70f025SJoel Dice         Ok(())
1385fa70f025SJoel Dice     }
1386fa70f025SJoel Dice 
13877e39c25eSJoel Dice     /// Execute the specified guest call on a worker fiber.
run_on_worker(self, item: WorkerItem) -> Result<()> where T: Send,13887e39c25eSJoel Dice     async fn run_on_worker(self, item: WorkerItem) -> Result<()>
13897e39c25eSJoel Dice     where
13907e39c25eSJoel Dice         T: Send,
13917e39c25eSJoel Dice     {
13927e39c25eSJoel Dice         let worker = if let Some(fiber) = self.0.concurrent_state_mut().worker.take() {
13937e39c25eSJoel Dice             fiber
13947e39c25eSJoel Dice         } else {
13957e39c25eSJoel Dice             fiber::make_fiber(self.0, move |store| {
13967e39c25eSJoel Dice                 loop {
1397da093747SAlex Crichton                     let Some(item) = store.concurrent_state_mut().worker_item.take() else {
1398da093747SAlex Crichton                         bail_bug!("worker_item not present when resuming fiber")
1399da093747SAlex Crichton                     };
1400da093747SAlex Crichton                     match item {
14017e39c25eSJoel Dice                         WorkerItem::GuestCall(call) => handle_guest_call(store, call)?,
14027e39c25eSJoel Dice                         WorkerItem::Function(fun) => fun.into_inner()(store)?,
14037e39c25eSJoel Dice                     }
14047e39c25eSJoel Dice 
14057e39c25eSJoel Dice                     store.suspend(SuspendReason::NeedWork)?;
14067e39c25eSJoel Dice                 }
14077e39c25eSJoel Dice             })?
14087e39c25eSJoel Dice         };
14097e39c25eSJoel Dice 
14107e39c25eSJoel Dice         let worker_item = &mut self.0.concurrent_state_mut().worker_item;
14117e39c25eSJoel Dice         assert!(worker_item.is_none());
14127e39c25eSJoel Dice         *worker_item = Some(item);
14137e39c25eSJoel Dice 
14147e39c25eSJoel Dice         self.0.resume_fiber(worker).await
14157e39c25eSJoel Dice     }
14167e39c25eSJoel Dice 
14177e39c25eSJoel Dice     /// Wrap the specified host function in a future which will call it, passing
14187e39c25eSJoel Dice     /// it an `&Accessor<T>`.
14197e39c25eSJoel Dice     ///
14207e39c25eSJoel Dice     /// See the `Accessor` documentation for details.
wrap_call<F, R>(self, closure: F) -> impl Future<Output = Result<R>> + 'static where T: 'static, F: FnOnce(&Accessor<T>) -> Pin<Box<dyn Future<Output = Result<R>> + Send + '_>> + Send + Sync + 'static, R: Send + Sync + 'static,14217e39c25eSJoel Dice     pub(crate) fn wrap_call<F, R>(self, closure: F) -> impl Future<Output = Result<R>> + 'static
14227e39c25eSJoel Dice     where
14237e39c25eSJoel Dice         T: 'static,
14247e39c25eSJoel Dice         F: FnOnce(&Accessor<T>) -> Pin<Box<dyn Future<Output = Result<R>> + Send + '_>>
14257e39c25eSJoel Dice             + Send
14267e39c25eSJoel Dice             + Sync
14277e39c25eSJoel Dice             + 'static,
14287e39c25eSJoel Dice         R: Send + Sync + 'static,
14297e39c25eSJoel Dice     {
14307e39c25eSJoel Dice         let token = StoreToken::new(self);
14317e39c25eSJoel Dice         async move {
14327e39c25eSJoel Dice             let mut accessor = Accessor::new(token);
14337e39c25eSJoel Dice             closure(&mut accessor).await
14347e39c25eSJoel Dice         }
14357e39c25eSJoel Dice     }
14367e39c25eSJoel Dice }
14377e39c25eSJoel Dice 
1438b856261dSJoel Dice impl StoreOpaque {
1439b271e452SJoel Dice     /// Push a `GuestTask` onto the task stack for either a sync-to-sync,
1440b271e452SJoel Dice     /// guest-to-guest call or a sync host-to-guest call.
1441b271e452SJoel Dice     ///
1442b271e452SJoel Dice     /// This task will only be used for the purpose of handling calls to
1443b271e452SJoel Dice     /// intrinsic functions; both parameter lowering and result lifting are
1444b271e452SJoel Dice     /// assumed to be taken care of elsewhere.
enter_guest_sync_call( &mut self, guest_caller: Option<RuntimeInstance>, callee_async: bool, callee: RuntimeInstance, ) -> Result<()>14453764e757SAlex Crichton     pub(crate) fn enter_guest_sync_call(
1446b271e452SJoel Dice         &mut self,
1447b271e452SJoel Dice         guest_caller: Option<RuntimeInstance>,
1448b271e452SJoel Dice         callee_async: bool,
1449b271e452SJoel Dice         callee: RuntimeInstance,
1450b271e452SJoel Dice     ) -> Result<()> {
1451b271e452SJoel Dice         log::trace!("enter sync call {callee:?}");
14523764e757SAlex Crichton         if !self.concurrency_support() {
14533764e757SAlex Crichton             return Ok(self.enter_call_not_concurrent());
14543764e757SAlex Crichton         }
1455b271e452SJoel Dice 
1456b271e452SJoel Dice         let state = self.concurrent_state_mut();
14573764e757SAlex Crichton         let thread = state.current_thread;
14583764e757SAlex Crichton         let instance = if let Some(thread) = thread.guest() {
1459b271e452SJoel Dice             Some(state.get_mut(thread.task)?.instance)
1460b271e452SJoel Dice         } else {
1461b271e452SJoel Dice             None
1462b271e452SJoel Dice         };
1463da093747SAlex Crichton         if guest_caller.is_some() {
1464da093747SAlex Crichton             debug_assert_eq!(instance, guest_caller);
1465da093747SAlex Crichton         }
1466b271e452SJoel Dice         let task = GuestTask::new(
1467da093747SAlex Crichton             Box::new(move |_, _| bail_bug!("cannot lower params in sync call")),
1468b271e452SJoel Dice             LiftResult {
1469da093747SAlex Crichton                 lift: Box::new(move |_, _| bail_bug!("cannot lift result in sync call")),
1470b271e452SJoel Dice                 ty: TypeTupleIndex::reserved_value(),
1471b271e452SJoel Dice                 memory: None,
1472b271e452SJoel Dice                 string_encoding: StringEncoding::Utf8,
1473b271e452SJoel Dice             },
1474da093747SAlex Crichton             if let Some(thread) = thread.guest() {
1475da093747SAlex Crichton                 Caller::Guest { thread: *thread }
1476b271e452SJoel Dice             } else {
1477b271e452SJoel Dice                 Caller::Host {
1478b271e452SJoel Dice                     tx: None,
1479b271e452SJoel Dice                     host_future_present: false,
14803764e757SAlex Crichton                     caller: thread,
1481b271e452SJoel Dice                 }
1482b271e452SJoel Dice             },
1483b271e452SJoel Dice             None,
1484b271e452SJoel Dice             callee,
1485b271e452SJoel Dice             callee_async,
1486b271e452SJoel Dice         )?;
1487b271e452SJoel Dice 
1488b271e452SJoel Dice         let guest_task = state.push(task)?;
148935887491SSy Brand         let new_thread = GuestThread::new_implicit(state, guest_task)?;
1490b271e452SJoel Dice         let guest_thread = state.push(new_thread)?;
1491b271e452SJoel Dice         Instance::from_wasmtime(self, callee.instance).add_guest_thread_to_instance_table(
1492b271e452SJoel Dice             guest_thread,
1493b271e452SJoel Dice             self,
1494b271e452SJoel Dice             callee.index,
1495b271e452SJoel Dice         )?;
1496b271e452SJoel Dice 
1497b271e452SJoel Dice         let state = self.concurrent_state_mut();
1498b271e452SJoel Dice         state.get_mut(guest_task)?.threads.insert(guest_thread);
1499b271e452SJoel Dice 
15003764e757SAlex Crichton         self.set_thread(QualifiedThreadId {
1501b271e452SJoel Dice             task: guest_task,
1502b271e452SJoel Dice             thread: guest_thread,
1503da093747SAlex Crichton         })?;
1504b271e452SJoel Dice 
1505b271e452SJoel Dice         Ok(())
1506b271e452SJoel Dice     }
1507b271e452SJoel Dice 
1508b271e452SJoel Dice     /// Pop a `GuestTask` previously pushed using `enter_sync_call`.
exit_guest_sync_call(&mut self) -> Result<()>1509da093747SAlex Crichton     pub(crate) fn exit_guest_sync_call(&mut self) -> Result<()> {
15103764e757SAlex Crichton         if !self.concurrency_support() {
15113764e757SAlex Crichton             return Ok(self.exit_call_not_concurrent());
15123764e757SAlex Crichton         }
1513da093747SAlex Crichton         let thread = match self.set_thread(CurrentThread::None)?.guest() {
1514da093747SAlex Crichton             Some(t) => *t,
1515da093747SAlex Crichton             None => bail_bug!("expected task when exiting"),
1516da093747SAlex Crichton         };
1517b271e452SJoel Dice         let instance = self.concurrent_state_mut().get_mut(thread.task)?.instance;
1518b271e452SJoel Dice         log::trace!("exit sync call {instance:?}");
1519b271e452SJoel Dice         Instance::from_wasmtime(self, instance.instance).cleanup_thread(
1520b271e452SJoel Dice             self,
1521b271e452SJoel Dice             thread,
1522b271e452SJoel Dice             instance.index,
1523b271e452SJoel Dice         )?;
1524b271e452SJoel Dice 
1525b271e452SJoel Dice         let state = self.concurrent_state_mut();
1526b271e452SJoel Dice         let task = state.get_mut(thread.task)?;
1527b271e452SJoel Dice         let caller = match &task.caller {
1528da093747SAlex Crichton             &Caller::Guest { thread } => thread.into(),
1529da093747SAlex Crichton             &Caller::Host { caller, .. } => caller,
1530b271e452SJoel Dice         };
1531da093747SAlex Crichton         self.set_thread(caller)?;
1532b271e452SJoel Dice 
1533b271e452SJoel Dice         let state = self.concurrent_state_mut();
1534b271e452SJoel Dice         let task = state.get_mut(thread.task)?;
1535b271e452SJoel Dice         if task.ready_to_delete() {
15361e0b0b46SAlex Crichton             state.delete(thread.task)?.dispose(state)?;
1537b271e452SJoel Dice         }
1538b271e452SJoel Dice 
1539b271e452SJoel Dice         Ok(())
1540b271e452SJoel Dice     }
1541b271e452SJoel Dice 
15423764e757SAlex Crichton     /// Similar to `enter_guest_sync_call` except for when the guest makes a
15433764e757SAlex Crichton     /// transition to the host.
15443764e757SAlex Crichton     ///
15453764e757SAlex Crichton     /// FIXME: this is called for all guest->host transitions and performs some
15463764e757SAlex Crichton     /// relatively expensive table manipulations. This would ideally be
15473764e757SAlex Crichton     /// optimized to avoid the full allocation of a `HostTask` in at least some
15483764e757SAlex Crichton     /// situations.
host_task_create(&mut self) -> Result<Option<TableId<HostTask>>>154958877f2fSAlex Crichton     pub(crate) fn host_task_create(&mut self) -> Result<Option<TableId<HostTask>>> {
15502e3d0ecbSNick Fitzgerald         if !self.concurrency_support() {
15512e3d0ecbSNick Fitzgerald             self.enter_call_not_concurrent();
155258877f2fSAlex Crichton             return Ok(None);
15532e3d0ecbSNick Fitzgerald         }
15543764e757SAlex Crichton         let state = self.concurrent_state_mut();
1555da093747SAlex Crichton         let caller = state.current_guest_thread()?;
1556065baac4SAlex Crichton         let task = state.push(HostTask::new(caller, HostTaskState::CalleeStarted))?;
15573764e757SAlex Crichton         log::trace!("new host task {task:?}");
1558da093747SAlex Crichton         self.set_thread(task)?;
155958877f2fSAlex Crichton         Ok(Some(task))
156058877f2fSAlex Crichton     }
156158877f2fSAlex Crichton 
156258877f2fSAlex Crichton     /// Invoked before lowering the results of a host task to the guest.
156358877f2fSAlex Crichton     ///
156458877f2fSAlex Crichton     /// This is used to update the current thread annotations within the store
156558877f2fSAlex Crichton     /// to ensure that it reflects the guest task, not the host task, since
156658877f2fSAlex Crichton     /// lowering may execute guest code.
host_task_reenter_caller(&mut self) -> Result<()>156758877f2fSAlex Crichton     pub fn host_task_reenter_caller(&mut self) -> Result<()> {
156858877f2fSAlex Crichton         if !self.concurrency_support() {
156958877f2fSAlex Crichton             return Ok(());
157058877f2fSAlex Crichton         }
157158877f2fSAlex Crichton         let task = self.concurrent_state_mut().current_host_thread()?;
157258877f2fSAlex Crichton         let caller = self.concurrent_state_mut().get_mut(task)?.caller;
157358877f2fSAlex Crichton         self.set_thread(caller)?;
15743764e757SAlex Crichton         Ok(())
15753764e757SAlex Crichton     }
15763764e757SAlex Crichton 
157758877f2fSAlex Crichton     /// Dual of `host_task_create` and signifies that the host has finished and
15783764e757SAlex Crichton     /// will be cleaned up.
15793764e757SAlex Crichton     ///
15803764e757SAlex Crichton     /// Note that this isn't invoked when the host is invoked asynchronously and
15813764e757SAlex Crichton     /// the host isn't complete yet. In that situation the host task persists
158258877f2fSAlex Crichton     /// and will be cleaned up separately in `subtask_drop`
host_task_delete(&mut self, task: Option<TableId<HostTask>>) -> Result<()>158358877f2fSAlex Crichton     pub(crate) fn host_task_delete(&mut self, task: Option<TableId<HostTask>>) -> Result<()> {
158458877f2fSAlex Crichton         match task {
158558877f2fSAlex Crichton             Some(task) => {
15863764e757SAlex Crichton                 log::trace!("delete host task {task:?}");
158758877f2fSAlex Crichton                 self.concurrent_state_mut().delete(task)?;
158858877f2fSAlex Crichton             }
158958877f2fSAlex Crichton             None => {
159058877f2fSAlex Crichton                 self.exit_call_not_concurrent();
159158877f2fSAlex Crichton             }
159258877f2fSAlex Crichton         }
15933764e757SAlex Crichton         Ok(())
15943764e757SAlex Crichton     }
15953764e757SAlex Crichton 
1596b856261dSJoel Dice     /// Determine whether the specified instance may be entered from the host.
1597b856261dSJoel Dice     ///
1598b856261dSJoel Dice     /// We return `true` here only if all of the following hold:
1599b856261dSJoel Dice     ///
1600b856261dSJoel Dice     /// - The top-level instance is not already on the current task's call stack.
1601b856261dSJoel Dice     /// - The instance is not in need of a post-return function call.
1602b856261dSJoel Dice     /// - `self` has not been poisoned due to a trap.
may_enter(&mut self, instance: RuntimeInstance) -> Result<bool>1603da093747SAlex Crichton     pub(crate) fn may_enter(&mut self, instance: RuntimeInstance) -> Result<bool> {
16041a154f61SAlex Crichton         if self.trapped() {
1605da093747SAlex Crichton             return Ok(false);
16061a154f61SAlex Crichton         }
160721797bb5SAlex Crichton         if !self.concurrency_support() {
1608da093747SAlex Crichton             return Ok(true);
160921797bb5SAlex Crichton         }
1610b856261dSJoel Dice         let state = self.concurrent_state_mut();
16113764e757SAlex Crichton         let mut cur = state.current_thread;
1612b856261dSJoel Dice         loop {
16131a154f61SAlex Crichton             match cur {
1614da093747SAlex Crichton                 CurrentThread::None => break Ok(true),
16153764e757SAlex Crichton                 CurrentThread::Guest(thread) => {
1616da093747SAlex Crichton                     let task = state.get_mut(thread.task)?;
16171a154f61SAlex Crichton 
16181a154f61SAlex Crichton                     // Note that we only compare top-level instance IDs here.
16191a154f61SAlex Crichton                     // The idea is that the host is not allowed to recursively
16201a154f61SAlex Crichton                     // enter a top-level instance even if the specific leaf
16211a154f61SAlex Crichton                     // instance is not on the stack. This the behavior defined
16221a154f61SAlex Crichton                     // in the spec, and it allows us to elide runtime checks in
16231a154f61SAlex Crichton                     // guest-to-guest adapters.
16241a154f61SAlex Crichton                     if task.instance.instance == instance.instance {
1625da093747SAlex Crichton                         break Ok(false);
1626b856261dSJoel Dice                     }
16271a154f61SAlex Crichton                     cur = match task.caller {
16281a154f61SAlex Crichton                         Caller::Host { caller, .. } => caller,
16291a154f61SAlex Crichton                         Caller::Guest { thread } => thread.into(),
1630b856261dSJoel Dice                     };
16311a154f61SAlex Crichton                 }
16323764e757SAlex Crichton                 CurrentThread::Host(id) => {
1633da093747SAlex Crichton                     cur = state.get_mut(id)?.caller.into();
16343764e757SAlex Crichton                 }
1635b856261dSJoel Dice             }
1636b856261dSJoel Dice         }
1637b856261dSJoel Dice     }
1638b856261dSJoel Dice 
163957f899c4SAlex Crichton     /// Helper function to retrieve the `InstanceState` for the
1640cb97ae85SJoel Dice     /// specified instance.
instance_state(&mut self, instance: RuntimeInstance) -> &mut InstanceState164157f899c4SAlex Crichton     fn instance_state(&mut self, instance: RuntimeInstance) -> &mut InstanceState {
1642b856261dSJoel Dice         self.component_instance_mut(instance.instance)
1643cb97ae85SJoel Dice             .instance_state(instance.index)
1644cb97ae85SJoel Dice     }
1645cb97ae85SJoel Dice 
set_thread(&mut self, thread: impl Into<CurrentThread>) -> Result<CurrentThread>1646da093747SAlex Crichton     fn set_thread(&mut self, thread: impl Into<CurrentThread>) -> Result<CurrentThread> {
1647fae9e6afSJoel Dice         // Each time we switch threads, we conservatively set `task_may_block`
1648fae9e6afSJoel Dice         // to `false` for the component instance we're switching away from (if
1649fae9e6afSJoel Dice         // any), meaning it will be `false` for any new thread created for that
1650fae9e6afSJoel Dice         // instance unless explicitly set otherwise.
1651fae9e6afSJoel Dice         let state = self.concurrent_state_mut();
16523764e757SAlex Crichton         let old_thread = mem::replace(&mut state.current_thread, thread.into());
16533764e757SAlex Crichton         if let Some(old_thread) = old_thread.guest() {
1654da093747SAlex Crichton             let instance = state.get_mut(old_thread.task)?.instance.instance;
1655b856261dSJoel Dice             self.component_instance_mut(instance)
1656fae9e6afSJoel Dice                 .set_task_may_block(false)
1657fae9e6afSJoel Dice         }
1658fae9e6afSJoel Dice 
1659fae9e6afSJoel Dice         // If we're switching to a new thread, set its component instance's
1660fae9e6afSJoel Dice         // `task_may_block` according to where it left off.
16613764e757SAlex Crichton         if self.concurrent_state_mut().current_thread.guest().is_some() {
1662da093747SAlex Crichton             self.set_task_may_block()?;
1663fae9e6afSJoel Dice         }
1664fae9e6afSJoel Dice 
1665da093747SAlex Crichton         Ok(old_thread)
1666fae9e6afSJoel Dice     }
1667fae9e6afSJoel Dice 
1668fae9e6afSJoel Dice     /// Set the global variable representing whether the current task may block
1669fae9e6afSJoel Dice     /// prior to entering Wasm code.
set_task_may_block(&mut self) -> Result<()>1670da093747SAlex Crichton     fn set_task_may_block(&mut self) -> Result<()> {
1671fae9e6afSJoel Dice         let state = self.concurrent_state_mut();
1672da093747SAlex Crichton         let guest_thread = state.current_guest_thread()?;
1673da093747SAlex Crichton         let instance = state.get_mut(guest_thread.task)?.instance.instance;
1674da093747SAlex Crichton         let may_block = self.concurrent_state_mut().may_block(guest_thread.task)?;
1675b856261dSJoel Dice         self.component_instance_mut(instance)
1676da093747SAlex Crichton             .set_task_may_block(may_block);
1677da093747SAlex Crichton         Ok(())
1678fae9e6afSJoel Dice     }
1679fae9e6afSJoel Dice 
check_blocking(&mut self) -> Result<()>168021797bb5SAlex Crichton     pub(crate) fn check_blocking(&mut self) -> Result<()> {
168121797bb5SAlex Crichton         if !self.concurrency_support() {
168221797bb5SAlex Crichton             return Ok(());
168321797bb5SAlex Crichton         }
1684fae9e6afSJoel Dice         let state = self.concurrent_state_mut();
1685da093747SAlex Crichton         let task = state.current_guest_thread()?.task;
1686da093747SAlex Crichton         let instance = state.get_mut(task)?.instance.instance;
1687b856261dSJoel Dice         let task_may_block = self.component_instance(instance).get_task_may_block();
1688fae9e6afSJoel Dice 
1689fae9e6afSJoel Dice         if task_may_block {
1690fae9e6afSJoel Dice             Ok(())
1691fae9e6afSJoel Dice         } else {
1692fae9e6afSJoel Dice             Err(Trap::CannotBlockSyncTask.into())
1693fae9e6afSJoel Dice         }
1694fae9e6afSJoel Dice     }
1695fae9e6afSJoel Dice 
1696cb97ae85SJoel Dice     /// Record that we're about to enter a (sub-)component instance which does
1697cb97ae85SJoel Dice     /// not support more than one concurrent, stackful activation, meaning it
1698cb97ae85SJoel Dice     /// cannot be entered again until the next call returns.
enter_instance(&mut self, instance: RuntimeInstance)1699cb97ae85SJoel Dice     fn enter_instance(&mut self, instance: RuntimeInstance) {
1700cb97ae85SJoel Dice         log::trace!("enter {instance:?}");
170157f899c4SAlex Crichton         self.instance_state(instance)
170257f899c4SAlex Crichton             .concurrent_state()
170357f899c4SAlex Crichton             .do_not_enter = true;
1704cb97ae85SJoel Dice     }
1705cb97ae85SJoel Dice 
1706cb97ae85SJoel Dice     /// Record that we've exited a (sub-)component instance previously entered
1707cb97ae85SJoel Dice     /// with `Self::enter_instance` and then calls `Self::partition_pending`.
1708cb97ae85SJoel Dice     /// See the documentation for the latter for details.
exit_instance(&mut self, instance: RuntimeInstance) -> Result<()>1709cb97ae85SJoel Dice     fn exit_instance(&mut self, instance: RuntimeInstance) -> Result<()> {
1710cb97ae85SJoel Dice         log::trace!("exit {instance:?}");
171157f899c4SAlex Crichton         self.instance_state(instance)
171257f899c4SAlex Crichton             .concurrent_state()
171357f899c4SAlex Crichton             .do_not_enter = false;
1714cb97ae85SJoel Dice         self.partition_pending(instance)
1715cb97ae85SJoel Dice     }
1716cb97ae85SJoel Dice 
1717cb97ae85SJoel Dice     /// Iterate over `InstanceState::pending`, moving any ready items into the
1718cb97ae85SJoel Dice     /// "high priority" work item queue.
1719cb97ae85SJoel Dice     ///
1720cb97ae85SJoel Dice     /// See `GuestCall::is_ready` for details.
partition_pending(&mut self, instance: RuntimeInstance) -> Result<()>1721cb97ae85SJoel Dice     fn partition_pending(&mut self, instance: RuntimeInstance) -> Result<()> {
172257f899c4SAlex Crichton         for (thread, kind) in
172357f899c4SAlex Crichton             mem::take(&mut self.instance_state(instance).concurrent_state().pending).into_iter()
172457f899c4SAlex Crichton         {
1725cb97ae85SJoel Dice             let call = GuestCall { thread, kind };
1726cb97ae85SJoel Dice             if call.is_ready(self)? {
1727cb97ae85SJoel Dice                 self.concurrent_state_mut()
1728d2fbd2deSAlex Crichton                     .push_high_priority(WorkItem::GuestCall(instance.index, call));
1729cb97ae85SJoel Dice             } else {
1730cb97ae85SJoel Dice                 self.instance_state(instance)
173157f899c4SAlex Crichton                     .concurrent_state()
1732cb97ae85SJoel Dice                     .pending
1733cb97ae85SJoel Dice                     .insert(call.thread, call.kind);
1734cb97ae85SJoel Dice             }
1735cb97ae85SJoel Dice         }
1736cb97ae85SJoel Dice 
1737cb97ae85SJoel Dice         Ok(())
1738cb97ae85SJoel Dice     }
1739cb97ae85SJoel Dice 
1740cb97ae85SJoel Dice     /// Implements the `backpressure.{inc,dec}` intrinsics.
backpressure_modify( &mut self, caller_instance: RuntimeInstance, modify: impl FnOnce(u16) -> Option<u16>, ) -> Result<()>1741cb97ae85SJoel Dice     pub(crate) fn backpressure_modify(
1742cb97ae85SJoel Dice         &mut self,
1743cb97ae85SJoel Dice         caller_instance: RuntimeInstance,
1744cb97ae85SJoel Dice         modify: impl FnOnce(u16) -> Option<u16>,
1745cb97ae85SJoel Dice     ) -> Result<()> {
174657f899c4SAlex Crichton         let state = self.instance_state(caller_instance).concurrent_state();
1747cb97ae85SJoel Dice         let old = state.backpressure;
1748da093747SAlex Crichton         let new = modify(old).ok_or_else(|| Trap::BackpressureOverflow)?;
1749cb97ae85SJoel Dice         state.backpressure = new;
1750cb97ae85SJoel Dice 
1751cb97ae85SJoel Dice         if old > 0 && new == 0 {
1752cb97ae85SJoel Dice             // Backpressure was previously enabled and is now disabled; move any
1753cb97ae85SJoel Dice             // newly-eligible guest calls to the "high priority" queue.
1754cb97ae85SJoel Dice             self.partition_pending(caller_instance)?;
1755cb97ae85SJoel Dice         }
1756cb97ae85SJoel Dice 
1757cb97ae85SJoel Dice         Ok(())
1758cb97ae85SJoel Dice     }
1759cb97ae85SJoel Dice 
1760fa70f025SJoel Dice     /// Resume the specified fiber, giving it exclusive access to the specified
1761fa70f025SJoel Dice     /// store.
resume_fiber(&mut self, fiber: StoreFiber<'static>) -> Result<()>17627e39c25eSJoel Dice     async fn resume_fiber(&mut self, fiber: StoreFiber<'static>) -> Result<()> {
17633764e757SAlex Crichton         let old_thread = self.concurrent_state_mut().current_thread;
1764e06fbf70SSy Brand         log::trace!("resume_fiber: save current thread {old_thread:?}");
1765fa70f025SJoel Dice 
17667e39c25eSJoel Dice         let fiber = fiber::resolve_or_release(self, fiber).await?;
1767fa70f025SJoel Dice 
1768da093747SAlex Crichton         self.set_thread(old_thread)?;
1769fae9e6afSJoel Dice 
17707e39c25eSJoel Dice         let state = self.concurrent_state_mut();
1771fa70f025SJoel Dice 
17723764e757SAlex Crichton         if let Some(ot) = old_thread.guest() {
1773e06fbf70SSy Brand             state.get_mut(ot.thread)?.state = GuestThreadState::Running;
1774e06fbf70SSy Brand         }
1775e06fbf70SSy Brand         log::trace!("resume_fiber: restore current thread {old_thread:?}");
1776fa70f025SJoel Dice 
1777fa70f025SJoel Dice         if let Some(mut fiber) = fiber {
1778e06fbf70SSy Brand             log::trace!("resume_fiber: suspend reason {:?}", &state.suspend_reason);
1779fa70f025SJoel Dice             // See the `SuspendReason` documentation for what each case means.
1780da093747SAlex Crichton             let reason = match state.suspend_reason.take() {
1781da093747SAlex Crichton                 Some(r) => r,
1782da093747SAlex Crichton                 None => bail_bug!("suspend reason missing when resuming fiber"),
1783da093747SAlex Crichton             };
1784da093747SAlex Crichton             match reason {
1785fa70f025SJoel Dice                 SuspendReason::NeedWork => {
1786fa70f025SJoel Dice                     if state.worker.is_none() {
1787fa70f025SJoel Dice                         state.worker = Some(fiber);
1788fa70f025SJoel Dice                     } else {
17897e39c25eSJoel Dice                         fiber.dispose(self);
1790fa70f025SJoel Dice                     }
1791fa70f025SJoel Dice                 }
1792e06fbf70SSy Brand                 SuspendReason::Yielding { thread, .. } => {
1793d2fbd2deSAlex Crichton                     state.get_mut(thread.thread)?.state = GuestThreadState::Ready(fiber);
1794d2fbd2deSAlex Crichton                     let instance = state.get_mut(thread.task)?.instance.index;
1795d2fbd2deSAlex Crichton                     state.push_low_priority(WorkItem::ResumeThread(instance, thread));
1796fa70f025SJoel Dice                 }
1797e06fbf70SSy Brand                 SuspendReason::ExplicitlySuspending { thread, .. } => {
1798e06fbf70SSy Brand                     state.get_mut(thread.thread)?.state = GuestThreadState::Suspended(fiber);
1799e06fbf70SSy Brand                 }
18008992b99bSJoel Dice                 SuspendReason::Waiting { set, thread, .. } => {
1801fa70f025SJoel Dice                     let old = state
1802fa70f025SJoel Dice                         .get_mut(set)?
1803fa70f025SJoel Dice                         .waiting
1804e06fbf70SSy Brand                         .insert(thread, WaitMode::Fiber(fiber));
1805fa70f025SJoel Dice                     assert!(old.is_none());
1806fa70f025SJoel Dice                 }
1807e06fbf70SSy Brand             };
1808e06fbf70SSy Brand         } else {
1809e06fbf70SSy Brand             log::trace!("resume_fiber: fiber has exited");
1810fa70f025SJoel Dice         }
1811fa70f025SJoel Dice 
1812fa70f025SJoel Dice         Ok(())
1813fa70f025SJoel Dice     }
1814fa70f025SJoel Dice 
1815fa70f025SJoel Dice     /// Suspend the current fiber, storing the reason in
1816fa70f025SJoel Dice     /// `ConcurrentState::suspend_reason` to indicate the conditions under which
1817fa70f025SJoel Dice     /// it should be resumed.
1818fa70f025SJoel Dice     ///
1819fa70f025SJoel Dice     /// See the `SuspendReason` documentation for details.
suspend(&mut self, reason: SuspendReason) -> Result<()>18207e39c25eSJoel Dice     fn suspend(&mut self, reason: SuspendReason) -> Result<()> {
1821fa70f025SJoel Dice         log::trace!("suspend fiber: {reason:?}");
1822fa70f025SJoel Dice 
1823e06fbf70SSy Brand         // If we're yielding or waiting on behalf of a guest thread, we'll need to
1824fa70f025SJoel Dice         // pop the call context which manages resource borrows before suspending
1825fa70f025SJoel Dice         // and then push it again once we've resumed.
1826fa70f025SJoel Dice         let task = match &reason {
1827e06fbf70SSy Brand             SuspendReason::Yielding { thread, .. }
1828e06fbf70SSy Brand             | SuspendReason::Waiting { thread, .. }
1829e06fbf70SSy Brand             | SuspendReason::ExplicitlySuspending { thread, .. } => Some(thread.task),
1830fa70f025SJoel Dice             SuspendReason::NeedWork => None,
1831fa70f025SJoel Dice         };
1832fa70f025SJoel Dice 
18333764e757SAlex Crichton         let old_guest_thread = if task.is_some() {
18343764e757SAlex Crichton             self.concurrent_state_mut().current_thread
1835fa70f025SJoel Dice         } else {
18363764e757SAlex Crichton             CurrentThread::None
1837fa70f025SJoel Dice         };
1838fa70f025SJoel Dice 
18398992b99bSJoel Dice         // We should not have reached here unless either there's no current
18408992b99bSJoel Dice         // task, or the current task is permitted to block.  In addition, we
18418992b99bSJoel Dice         // special-case `thread.switch-to` and waiting for a subtask to go from
18428992b99bSJoel Dice         // `starting` to `started`, both of which we consider non-blocking
18438992b99bSJoel Dice         // operations despite requiring a suspend.
1844da093747SAlex Crichton         debug_assert!(
18458992b99bSJoel Dice             matches!(
18468992b99bSJoel Dice                 reason,
18478992b99bSJoel Dice                 SuspendReason::ExplicitlySuspending {
18488992b99bSJoel Dice                     skip_may_block_check: true,
18498992b99bSJoel Dice                     ..
18508992b99bSJoel Dice                 } | SuspendReason::Waiting {
18518992b99bSJoel Dice                     skip_may_block_check: true,
18528992b99bSJoel Dice                     ..
1853fc4020baSSy Brand                 } | SuspendReason::Yielding {
1854fc4020baSSy Brand                     skip_may_block_check: true,
1855fc4020baSSy Brand                     ..
18568992b99bSJoel Dice                 }
18578992b99bSJoel Dice             ) || old_guest_thread
18583764e757SAlex Crichton                 .guest()
18598992b99bSJoel Dice                 .map(|thread| self.concurrent_state_mut().may_block(thread.task))
1860da093747SAlex Crichton                 .transpose()?
18618992b99bSJoel Dice                 .unwrap_or(true)
18628992b99bSJoel Dice         );
18638992b99bSJoel Dice 
18647e39c25eSJoel Dice         let suspend_reason = &mut self.concurrent_state_mut().suspend_reason;
1865fa70f025SJoel Dice         assert!(suspend_reason.is_none());
1866fa70f025SJoel Dice         *suspend_reason = Some(reason);
1867fa70f025SJoel Dice 
18687e39c25eSJoel Dice         self.with_blocking(|_, cx| cx.suspend(StoreFiberYield::ReleaseStore))?;
1869fa70f025SJoel Dice 
18703764e757SAlex Crichton         if task.is_some() {
1871da093747SAlex Crichton             self.set_thread(old_guest_thread)?;
1872fa70f025SJoel Dice         }
1873fa70f025SJoel Dice 
1874fa70f025SJoel Dice         Ok(())
1875fa70f025SJoel Dice     }
1876fa70f025SJoel Dice 
wait_for_event(&mut self, waitable: Waitable) -> Result<()>18777e39c25eSJoel Dice     fn wait_for_event(&mut self, waitable: Waitable) -> Result<()> {
18787e39c25eSJoel Dice         let state = self.concurrent_state_mut();
1879da093747SAlex Crichton         let caller = state.current_guest_thread()?;
18807e39c25eSJoel Dice         let old_set = waitable.common(state)?.set;
188135887491SSy Brand         let set = state.get_mut(caller.thread)?.sync_call_set;
18827e39c25eSJoel Dice         waitable.join(state, Some(set))?;
1883e06fbf70SSy Brand         self.suspend(SuspendReason::Waiting {
1884e06fbf70SSy Brand             set,
1885e06fbf70SSy Brand             thread: caller,
18868992b99bSJoel Dice             skip_may_block_check: false,
1887e06fbf70SSy Brand         })?;
18887e39c25eSJoel Dice         let state = self.concurrent_state_mut();
18897e39c25eSJoel Dice         waitable.join(state, old_set)
18907e39c25eSJoel Dice     }
18917e39c25eSJoel Dice }
18927e39c25eSJoel Dice 
18937e39c25eSJoel Dice impl Instance {
18947e39c25eSJoel Dice     /// Get the next pending event for the specified task and (optional)
18957e39c25eSJoel Dice     /// waitable set, along with the waitable handle if applicable.
get_event( self, store: &mut StoreOpaque, guest_task: TableId<GuestTask>, set: Option<TableId<WaitableSet>>, cancellable: bool, ) -> Result<Option<(Event, Option<(Waitable, u32)>)>>18967e39c25eSJoel Dice     fn get_event(
18977e39c25eSJoel Dice         self,
18987e39c25eSJoel Dice         store: &mut StoreOpaque,
18997e39c25eSJoel Dice         guest_task: TableId<GuestTask>,
19007e39c25eSJoel Dice         set: Option<TableId<WaitableSet>>,
19017e39c25eSJoel Dice         cancellable: bool,
19027e39c25eSJoel Dice     ) -> Result<Option<(Event, Option<(Waitable, u32)>)>> {
19037e39c25eSJoel Dice         let state = store.concurrent_state_mut();
19047e39c25eSJoel Dice 
1905da093747SAlex Crichton         let event = &mut state.get_mut(guest_task)?.event;
1906da093747SAlex Crichton         if let Some(ev) = event
1907da093747SAlex Crichton             && (cancellable || !matches!(ev, Event::Cancelled))
19087e39c25eSJoel Dice         {
1909da093747SAlex Crichton             log::trace!("deliver event {ev:?} to {guest_task:?}");
1910da093747SAlex Crichton             let ev = *ev;
1911da093747SAlex Crichton             *event = None;
1912da093747SAlex Crichton             return Ok(Some((ev, None)));
1913da093747SAlex Crichton         }
1914da093747SAlex Crichton 
1915da093747SAlex Crichton         let set = match set {
1916da093747SAlex Crichton             Some(set) => set,
1917da093747SAlex Crichton             None => return Ok(None),
1918da093747SAlex Crichton         };
1919da093747SAlex Crichton         let waitable = match state.get_mut(set)?.ready.pop_first() {
1920da093747SAlex Crichton             Some(v) => v,
1921da093747SAlex Crichton             None => return Ok(None),
1922da093747SAlex Crichton         };
1923da093747SAlex Crichton 
19247e39c25eSJoel Dice         let common = waitable.common(state)?;
1925da093747SAlex Crichton         let handle = match common.handle {
1926da093747SAlex Crichton             Some(h) => h,
1927da093747SAlex Crichton             None => bail_bug!("handle not set when delivering event"),
1928da093747SAlex Crichton         };
1929da093747SAlex Crichton         let event = match common.event.take() {
1930da093747SAlex Crichton             Some(e) => e,
1931da093747SAlex Crichton             None => bail_bug!("event not set when delivering event"),
1932da093747SAlex Crichton         };
19337e39c25eSJoel Dice 
19347e39c25eSJoel Dice         log::trace!(
19357e39c25eSJoel Dice             "deliver event {event:?} to {guest_task:?} for {waitable:?} (handle {handle}); set {set:?}"
19367e39c25eSJoel Dice         );
19377e39c25eSJoel Dice 
1938da093747SAlex Crichton         waitable.on_delivery(store, self, event)?;
19397e39c25eSJoel Dice 
1940da093747SAlex Crichton         Ok(Some((event, Some((waitable, handle)))))
19417e39c25eSJoel Dice     }
19427e39c25eSJoel Dice 
19437e39c25eSJoel Dice     /// Handle the `CallbackCode` returned from an async-lifted export or its
19447e39c25eSJoel Dice     /// callback.
19458992b99bSJoel Dice     ///
19468992b99bSJoel Dice     /// If this returns `Ok(Some(call))`, then `call` should be run immediately
19478992b99bSJoel Dice     /// using `handle_guest_call`.
handle_callback_code( self, store: &mut StoreOpaque, guest_thread: QualifiedThreadId, runtime_instance: RuntimeComponentInstanceIndex, code: u32, ) -> Result<Option<GuestCall>>19487e39c25eSJoel Dice     fn handle_callback_code(
19497e39c25eSJoel Dice         self,
19507e39c25eSJoel Dice         store: &mut StoreOpaque,
1951e06fbf70SSy Brand         guest_thread: QualifiedThreadId,
19527e39c25eSJoel Dice         runtime_instance: RuntimeComponentInstanceIndex,
19537e39c25eSJoel Dice         code: u32,
19548992b99bSJoel Dice     ) -> Result<Option<GuestCall>> {
19557e39c25eSJoel Dice         let (code, set) = unpack_callback_code(code);
19567e39c25eSJoel Dice 
1957e06fbf70SSy Brand         log::trace!("received callback code from {guest_thread:?}: {code} (set: {set})");
19587e39c25eSJoel Dice 
19597e39c25eSJoel Dice         let state = store.concurrent_state_mut();
19607e39c25eSJoel Dice 
1961da093747SAlex Crichton         let get_set = |store: &mut StoreOpaque, handle| -> Result<_> {
1962cb97ae85SJoel Dice             let set = store
196357f899c4SAlex Crichton                 .instance_state(RuntimeInstance {
1964cb97ae85SJoel Dice                     instance: self.id().instance(),
1965cb97ae85SJoel Dice                     index: runtime_instance,
1966cb97ae85SJoel Dice                 })
196757f899c4SAlex Crichton                 .handle_table()
19687e39c25eSJoel Dice                 .waitable_set_rep(handle)?;
19697e39c25eSJoel Dice 
19707e39c25eSJoel Dice             Ok(TableId::<WaitableSet>::new(set))
19717e39c25eSJoel Dice         };
19727e39c25eSJoel Dice 
19738992b99bSJoel Dice         Ok(match code {
19747e39c25eSJoel Dice             callback_code::EXIT => {
1975e06fbf70SSy Brand                 log::trace!("implicit thread {guest_thread:?} completed");
1976e06fbf70SSy Brand                 self.cleanup_thread(store, guest_thread, runtime_instance)?;
1977e06fbf70SSy Brand                 let task = store.concurrent_state_mut().get_mut(guest_thread.task)?;
1978e06fbf70SSy Brand                 if task.threads.is_empty() && !task.returned_or_cancelled() {
1979e06fbf70SSy Brand                     bail!(Trap::NoAsyncResult);
1980e06fbf70SSy Brand                 }
19811e0b0b46SAlex Crichton                 if let Caller::Guest { .. } = task.caller {
19827e39c25eSJoel Dice                     task.exited = true;
19837e39c25eSJoel Dice                     task.callback = None;
19847e39c25eSJoel Dice                 }
19851e0b0b46SAlex Crichton                 if task.ready_to_delete() {
19861e0b0b46SAlex Crichton                     Waitable::Guest(guest_thread.task).delete_from(store.concurrent_state_mut())?;
19877e39c25eSJoel Dice                 }
19888992b99bSJoel Dice                 None
19897e39c25eSJoel Dice             }
19907e39c25eSJoel Dice             callback_code::YIELD => {
1991e06fbf70SSy Brand                 let task = state.get_mut(guest_thread.task)?;
1992df618ea7SJoel Dice                 // If an `Event::Cancelled` is pending, we'll deliver that;
1993df618ea7SJoel Dice                 // otherwise, we'll deliver `Event::None`.  Note that
1994df618ea7SJoel Dice                 // `GuestTask::event` is only ever set to one of those two
1995df618ea7SJoel Dice                 // `Event` variants.
1996df618ea7SJoel Dice                 if let Some(event) = task.event {
1997df618ea7SJoel Dice                     assert!(matches!(event, Event::None | Event::Cancelled));
1998df618ea7SJoel Dice                 } else {
19997e39c25eSJoel Dice                     task.event = Some(Event::None);
2000df618ea7SJoel Dice                 }
20018992b99bSJoel Dice                 let call = GuestCall {
2002e06fbf70SSy Brand                     thread: guest_thread,
20037e39c25eSJoel Dice                     kind: GuestCallKind::DeliverEvent {
20047e39c25eSJoel Dice                         instance: self,
20057e39c25eSJoel Dice                         set: None,
20067e39c25eSJoel Dice                     },
20078992b99bSJoel Dice                 };
2008da093747SAlex Crichton                 if state.may_block(guest_thread.task)? {
20098992b99bSJoel Dice                     // Push this thread onto the "low priority" queue so it runs
20108992b99bSJoel Dice                     // after any other threads have had a chance to run.
2011d2fbd2deSAlex Crichton                     state.push_low_priority(WorkItem::GuestCall(runtime_instance, call));
20128992b99bSJoel Dice                     None
20138992b99bSJoel Dice                 } else {
20148992b99bSJoel Dice                     // Yielding in a non-blocking context is defined as a no-op
20158992b99bSJoel Dice                     // according to the spec, so we must run this thread
20168992b99bSJoel Dice                     // immediately without allowing any others to run.
20178992b99bSJoel Dice                     Some(call)
20188992b99bSJoel Dice                 }
20197e39c25eSJoel Dice             }
202034ba273bSJoel Dice             callback_code::WAIT => {
202134ba273bSJoel Dice                 // The task may only return `WAIT` if it was created for a call
202234ba273bSJoel Dice                 // to an async export).  Otherwise, we'll trap.
20238992b99bSJoel Dice                 state.check_blocking_for(guest_thread.task)?;
20248992b99bSJoel Dice 
20257e39c25eSJoel Dice                 let set = get_set(store, set)?;
20267e39c25eSJoel Dice                 let state = store.concurrent_state_mut();
20277e39c25eSJoel Dice 
2028e06fbf70SSy Brand                 if state.get_mut(guest_thread.task)?.event.is_some()
20297e39c25eSJoel Dice                     || !state.get_mut(set)?.ready.is_empty()
20307e39c25eSJoel Dice                 {
20317e39c25eSJoel Dice                     // An event is immediately available; deliver it ASAP.
2032d2fbd2deSAlex Crichton                     state.push_high_priority(WorkItem::GuestCall(
2033d2fbd2deSAlex Crichton                         runtime_instance,
2034d2fbd2deSAlex Crichton                         GuestCall {
2035e06fbf70SSy Brand                             thread: guest_thread,
20367e39c25eSJoel Dice                             kind: GuestCallKind::DeliverEvent {
20377e39c25eSJoel Dice                                 instance: self,
20387e39c25eSJoel Dice                                 set: Some(set),
20397e39c25eSJoel Dice                             },
2040d2fbd2deSAlex Crichton                         },
2041d2fbd2deSAlex Crichton                     ));
20427e39c25eSJoel Dice                 } else {
20437e39c25eSJoel Dice                     // No event is immediately available.
20447e39c25eSJoel Dice                     //
204534ba273bSJoel Dice                     // We're waiting, so register to be woken up when an event
204634ba273bSJoel Dice                     // is published for this waitable set.
204734ba273bSJoel Dice                     //
204834ba273bSJoel Dice                     // Here we also set `GuestTask::wake_on_cancel` which allows
204934ba273bSJoel Dice                     // `subtask.cancel` to interrupt the wait.
2050e06fbf70SSy Brand                     let old = state
2051e06fbf70SSy Brand                         .get_mut(guest_thread.thread)?
2052e06fbf70SSy Brand                         .wake_on_cancel
2053e06fbf70SSy Brand                         .replace(set);
2054da093747SAlex Crichton                     if !old.is_none() {
2055da093747SAlex Crichton                         bail_bug!("thread unexpectedly had wake_on_cancel set");
2056da093747SAlex Crichton                     }
20577e39c25eSJoel Dice                     let old = state
20587e39c25eSJoel Dice                         .get_mut(set)?
20597e39c25eSJoel Dice                         .waiting
2060e06fbf70SSy Brand                         .insert(guest_thread, WaitMode::Callback(self));
2061da093747SAlex Crichton                     if !old.is_none() {
2062da093747SAlex Crichton                         bail_bug!("set's waiting set already had this thread registered");
2063da093747SAlex Crichton                     }
20647e39c25eSJoel Dice                 }
20658992b99bSJoel Dice                 None
20667e39c25eSJoel Dice             }
2067da093747SAlex Crichton             _ => bail!(Trap::UnsupportedCallbackCode),
20688992b99bSJoel Dice         })
20697e39c25eSJoel Dice     }
20707e39c25eSJoel Dice 
cleanup_thread( self, store: &mut StoreOpaque, guest_thread: QualifiedThreadId, runtime_instance: RuntimeComponentInstanceIndex, ) -> Result<()>2071e06fbf70SSy Brand     fn cleanup_thread(
2072e06fbf70SSy Brand         self,
2073e06fbf70SSy Brand         store: &mut StoreOpaque,
2074e06fbf70SSy Brand         guest_thread: QualifiedThreadId,
2075e06fbf70SSy Brand         runtime_instance: RuntimeComponentInstanceIndex,
2076e06fbf70SSy Brand     ) -> Result<()> {
207735887491SSy Brand         let state = store.concurrent_state_mut();
207835887491SSy Brand         let thread_data = state.get_mut(guest_thread.thread)?;
207935887491SSy Brand         let guest_id = match thread_data.instance_rep {
2080da093747SAlex Crichton             Some(id) => id,
2081da093747SAlex Crichton             None => bail_bug!("thread must have instance_rep set by now"),
2082da093747SAlex Crichton         };
208335887491SSy Brand         let sync_call_set = thread_data.sync_call_set;
208435887491SSy Brand 
208535887491SSy Brand         // Clean up any pending subtasks in the sync_call_set
208635887491SSy Brand         for waitable in mem::take(&mut state.get_mut(sync_call_set)?.ready) {
208735887491SSy Brand             if let Some(Event::Subtask {
208835887491SSy Brand                 status: Status::Returned | Status::ReturnCancelled,
208935887491SSy Brand             }) = waitable.common(state)?.event
209035887491SSy Brand             {
209135887491SSy Brand                 waitable.delete_from(state)?;
209235887491SSy Brand             }
209335887491SSy Brand         }
209435887491SSy Brand 
2095cb97ae85SJoel Dice         store
209657f899c4SAlex Crichton             .instance_state(RuntimeInstance {
2097cb97ae85SJoel Dice                 instance: self.id().instance(),
2098cb97ae85SJoel Dice                 index: runtime_instance,
2099cb97ae85SJoel Dice             })
210057f899c4SAlex Crichton             .thread_handle_table()
2101da093747SAlex Crichton             .guest_thread_remove(guest_id)?;
2102e06fbf70SSy Brand 
2103e06fbf70SSy Brand         store.concurrent_state_mut().delete(guest_thread.thread)?;
210435887491SSy Brand         store.concurrent_state_mut().delete(sync_call_set)?;
2105e06fbf70SSy Brand         let task = store.concurrent_state_mut().get_mut(guest_thread.task)?;
2106e06fbf70SSy Brand         task.threads.remove(&guest_thread.thread);
2107e06fbf70SSy Brand         Ok(())
2108e06fbf70SSy Brand     }
2109e06fbf70SSy Brand 
2110fa70f025SJoel Dice     /// Add the specified guest call to the "high priority" work item queue, to
2111fa70f025SJoel Dice     /// be started as soon as backpressure and/or reentrance rules allow.
2112fa70f025SJoel Dice     ///
2113fa70f025SJoel Dice     /// SAFETY: The raw pointer arguments must be valid references to guest
2114fa70f025SJoel Dice     /// functions (with the appropriate signatures) when the closures queued by
2115fa70f025SJoel Dice     /// this function are called.
queue_call<T: 'static>( self, mut store: StoreContextMut<T>, guest_thread: QualifiedThreadId, callee: SendSyncPtr<VMFuncRef>, param_count: usize, result_count: usize, async_: bool, callback: Option<SendSyncPtr<VMFuncRef>>, post_return: Option<SendSyncPtr<VMFuncRef>>, ) -> Result<()>2116fa70f025SJoel Dice     unsafe fn queue_call<T: 'static>(
2117fa70f025SJoel Dice         self,
2118fa70f025SJoel Dice         mut store: StoreContextMut<T>,
2119e06fbf70SSy Brand         guest_thread: QualifiedThreadId,
2120fa70f025SJoel Dice         callee: SendSyncPtr<VMFuncRef>,
2121fa70f025SJoel Dice         param_count: usize,
2122fa70f025SJoel Dice         result_count: usize,
2123fa70f025SJoel Dice         async_: bool,
2124fa70f025SJoel Dice         callback: Option<SendSyncPtr<VMFuncRef>>,
2125fa70f025SJoel Dice         post_return: Option<SendSyncPtr<VMFuncRef>>,
2126fa70f025SJoel Dice     ) -> Result<()> {
2127fa70f025SJoel Dice         /// Return a closure which will call the specified function in the scope
2128fa70f025SJoel Dice         /// of the specified task.
2129fa70f025SJoel Dice         ///
2130fa70f025SJoel Dice         /// This will use `GuestTask::lower_params` to lower the parameters, but
2131fa70f025SJoel Dice         /// will not lift the result; instead, it returns a
2132fa70f025SJoel Dice         /// `[MaybeUninit<ValRaw>; MAX_FLAT_PARAMS]` from which the result, if
2133fa70f025SJoel Dice         /// any, may be lifted.  Note that an async-lifted export will have
2134fa70f025SJoel Dice         /// returned its result using the `task.return` intrinsic (or not
2135fa70f025SJoel Dice         /// returned a result at all, in the case of `task.cancel`), in which
2136fa70f025SJoel Dice         /// case the "result" of this call will either be a callback code or
2137fa70f025SJoel Dice         /// nothing.
2138fa70f025SJoel Dice         ///
2139fa70f025SJoel Dice         /// SAFETY: `callee` must be a valid `*mut VMFuncRef` at the time when
2140fa70f025SJoel Dice         /// the returned closure is called.
2141fa70f025SJoel Dice         unsafe fn make_call<T: 'static>(
2142fa70f025SJoel Dice             store: StoreContextMut<T>,
2143e06fbf70SSy Brand             guest_thread: QualifiedThreadId,
2144fa70f025SJoel Dice             callee: SendSyncPtr<VMFuncRef>,
2145fa70f025SJoel Dice             param_count: usize,
2146fa70f025SJoel Dice             result_count: usize,
21477e39c25eSJoel Dice         ) -> impl FnOnce(&mut dyn VMStore) -> Result<[MaybeUninit<ValRaw>; MAX_FLAT_PARAMS]>
2148fa70f025SJoel Dice         + Send
2149fa70f025SJoel Dice         + Sync
2150fa70f025SJoel Dice         + 'static
2151fa70f025SJoel Dice         + use<T> {
2152fa70f025SJoel Dice             let token = StoreToken::new(store);
21537e39c25eSJoel Dice             move |store: &mut dyn VMStore| {
2154fa70f025SJoel Dice                 let mut storage = [MaybeUninit::uninit(); MAX_FLAT_PARAMS];
2155e06fbf70SSy Brand 
2156e06fbf70SSy Brand                 store
2157e06fbf70SSy Brand                     .concurrent_state_mut()
2158e06fbf70SSy Brand                     .get_mut(guest_thread.thread)?
2159e06fbf70SSy Brand                     .state = GuestThreadState::Running;
2160e06fbf70SSy Brand                 let task = store.concurrent_state_mut().get_mut(guest_thread.task)?;
2161da093747SAlex Crichton                 let lower = match task.lower_params.take() {
2162da093747SAlex Crichton                     Some(l) => l,
2163da093747SAlex Crichton                     None => bail_bug!("lower_params missing"),
2164da093747SAlex Crichton                 };
2165fa70f025SJoel Dice 
21667e39c25eSJoel Dice                 lower(store, &mut storage[..param_count])?;
2167fa70f025SJoel Dice 
2168fa70f025SJoel Dice                 let mut store = token.as_context_mut(store);
2169fa70f025SJoel Dice 
2170fa70f025SJoel Dice                 // SAFETY: Per the contract documented in `make_call's`
2171fa70f025SJoel Dice                 // documentation, `callee` must be a valid pointer.
2172fa70f025SJoel Dice                 unsafe {
2173fa70f025SJoel Dice                     crate::Func::call_unchecked_raw(
2174fa70f025SJoel Dice                         &mut store,
2175fa70f025SJoel Dice                         callee.as_non_null(),
2176fa70f025SJoel Dice                         NonNull::new(
2177fa70f025SJoel Dice                             &mut storage[..param_count.max(result_count)]
2178fa70f025SJoel Dice                                 as *mut [MaybeUninit<ValRaw>] as _,
2179fa70f025SJoel Dice                         )
2180fa70f025SJoel Dice                         .unwrap(),
2181fa70f025SJoel Dice                     )?;
2182fa70f025SJoel Dice                 }
2183fa70f025SJoel Dice 
2184fa70f025SJoel Dice                 Ok(storage)
2185fa70f025SJoel Dice             }
2186fa70f025SJoel Dice         }
2187fa70f025SJoel Dice 
2188fa70f025SJoel Dice         // SAFETY: Per the contract described in this function documentation,
2189fa70f025SJoel Dice         // the `callee` pointer which `call` closes over must be valid when
2190fa70f025SJoel Dice         // called by the closure we queue below.
2191fa70f025SJoel Dice         let call = unsafe {
2192fa70f025SJoel Dice             make_call(
2193fa70f025SJoel Dice                 store.as_context_mut(),
2194e06fbf70SSy Brand                 guest_thread,
2195fa70f025SJoel Dice                 callee,
2196fa70f025SJoel Dice                 param_count,
2197fa70f025SJoel Dice                 result_count,
2198fa70f025SJoel Dice             )
2199fa70f025SJoel Dice         };
2200fa70f025SJoel Dice 
2201e06fbf70SSy Brand         let callee_instance = store
2202e06fbf70SSy Brand             .0
2203e06fbf70SSy Brand             .concurrent_state_mut()
2204e06fbf70SSy Brand             .get_mut(guest_thread.task)?
2205e06fbf70SSy Brand             .instance;
2206cb97ae85SJoel Dice 
2207fa70f025SJoel Dice         let fun = if callback.is_some() {
2208fa70f025SJoel Dice             assert!(async_);
2209fa70f025SJoel Dice 
22107e39c25eSJoel Dice             Box::new(move |store: &mut dyn VMStore| {
2211e06fbf70SSy Brand                 self.add_guest_thread_to_instance_table(
2212e06fbf70SSy Brand                     guest_thread.thread,
2213e06fbf70SSy Brand                     store,
2214cb97ae85SJoel Dice                     callee_instance.index,
2215e06fbf70SSy Brand                 )?;
2216da093747SAlex Crichton                 let old_thread = store.set_thread(guest_thread)?;
2217fa70f025SJoel Dice                 log::trace!(
2218e06fbf70SSy Brand                     "stackless call: replaced {old_thread:?} with {guest_thread:?} as current thread"
2219fa70f025SJoel Dice                 );
2220fa70f025SJoel Dice 
2221cb97ae85SJoel Dice                 store.enter_instance(callee_instance);
2222fa70f025SJoel Dice 
2223fa70f025SJoel Dice                 // SAFETY: See the documentation for `make_call` to review the
2224fa70f025SJoel Dice                 // contract we must uphold for `call` here.
2225fa70f025SJoel Dice                 //
2226fa70f025SJoel Dice                 // Per the contract described in the `queue_call`
2227fa70f025SJoel Dice                 // documentation, the `callee` pointer which `call` closes
2228fa70f025SJoel Dice                 // over must be valid.
22297e39c25eSJoel Dice                 let storage = call(store)?;
2230fa70f025SJoel Dice 
2231cb97ae85SJoel Dice                 store.exit_instance(callee_instance)?;
2232fa70f025SJoel Dice 
2233da093747SAlex Crichton                 store.set_thread(old_thread)?;
22347e39c25eSJoel Dice                 let state = store.concurrent_state_mut();
2235da093747SAlex Crichton                 if let Some(t) = old_thread.guest() {
2236da093747SAlex Crichton                     state.get_mut(t.thread)?.state = GuestThreadState::Running;
2237da093747SAlex Crichton                 }
2238e06fbf70SSy Brand                 log::trace!("stackless call: restored {old_thread:?} as current thread");
2239fa70f025SJoel Dice 
2240fa70f025SJoel Dice                 // SAFETY: `wasmparser` will have validated that the callback
2241fa70f025SJoel Dice                 // function returns a `i32` result.
2242fa70f025SJoel Dice                 let code = unsafe { storage[0].assume_init() }.get_i32() as u32;
2243fa70f025SJoel Dice 
2244cb97ae85SJoel Dice                 self.handle_callback_code(store, guest_thread, callee_instance.index, code)
22458992b99bSJoel Dice             })
22468992b99bSJoel Dice                 as Box<dyn FnOnce(&mut dyn VMStore) -> Result<Option<GuestCall>> + Send + Sync>
2247fa70f025SJoel Dice         } else {
2248fa70f025SJoel Dice             let token = StoreToken::new(store.as_context_mut());
22497e39c25eSJoel Dice             Box::new(move |store: &mut dyn VMStore| {
2250e06fbf70SSy Brand                 self.add_guest_thread_to_instance_table(
2251e06fbf70SSy Brand                     guest_thread.thread,
2252e06fbf70SSy Brand                     store,
2253cb97ae85SJoel Dice                     callee_instance.index,
2254e06fbf70SSy Brand                 )?;
2255da093747SAlex Crichton                 let old_thread = store.set_thread(guest_thread)?;
2256fa70f025SJoel Dice                 log::trace!(
2257e06fbf70SSy Brand                     "sync/async-stackful call: replaced {old_thread:?} with {guest_thread:?} as current thread",
2258fa70f025SJoel Dice                 );
2259c09aa380SJoel Dice                 let flags = self.id().get(store).instance_flags(callee_instance.index);
2260fa70f025SJoel Dice 
2261fa70f025SJoel Dice                 // Unless this is a callback-less (i.e. stackful)
2262fa70f025SJoel Dice                 // async-lifted export, we need to record that the instance
2263fa70f025SJoel Dice                 // cannot be entered until the call returns.
2264fa70f025SJoel Dice                 if !async_ {
2265cb97ae85SJoel Dice                     store.enter_instance(callee_instance);
2266fa70f025SJoel Dice                 }
2267fa70f025SJoel Dice 
2268fa70f025SJoel Dice                 // SAFETY: See the documentation for `make_call` to review the
2269fa70f025SJoel Dice                 // contract we must uphold for `call` here.
2270fa70f025SJoel Dice                 //
2271fa70f025SJoel Dice                 // Per the contract described in the `queue_call`
2272fa70f025SJoel Dice                 // documentation, the `callee` pointer which `call` closes
2273fa70f025SJoel Dice                 // over must be valid.
22747e39c25eSJoel Dice                 let storage = call(store)?;
2275fa70f025SJoel Dice 
2276fa70f025SJoel Dice                 if async_ {
2277e06fbf70SSy Brand                     let task = store.concurrent_state_mut().get_mut(guest_thread.task)?;
2278c09aa380SJoel Dice                     if task.threads.len() == 1 && !task.returned_or_cancelled() {
2279e06fbf70SSy Brand                         bail!(Trap::NoAsyncResult);
2280fa70f025SJoel Dice                     }
2281fa70f025SJoel Dice                 } else {
2282fa70f025SJoel Dice                     // This is a sync-lifted export, so now is when we lift the
2283fa70f025SJoel Dice                     // result, optionally call the post-return function, if any,
2284fa70f025SJoel Dice                     // and finally notify any current or future waiters that the
2285fa70f025SJoel Dice                     // subtask has returned.
2286fa70f025SJoel Dice 
2287fa70f025SJoel Dice                     let lift = {
2288cb97ae85SJoel Dice                         store.exit_instance(callee_instance)?;
2289fa70f025SJoel Dice 
2290cb97ae85SJoel Dice                         let state = store.concurrent_state_mut();
2291da093747SAlex Crichton                         if !state.get_mut(guest_thread.task)?.result.is_none() {
2292da093747SAlex Crichton                             bail_bug!("task has not yet produced a result");
2293da093747SAlex Crichton                         }
2294fa70f025SJoel Dice 
2295da093747SAlex Crichton                         match state.get_mut(guest_thread.task)?.lift_result.take() {
2296da093747SAlex Crichton                             Some(lift) => lift,
2297da093747SAlex Crichton                             None => bail_bug!("lift_result field is missing"),
2298da093747SAlex Crichton                         }
2299fa70f025SJoel Dice                     };
2300fa70f025SJoel Dice 
2301fa70f025SJoel Dice                     // SAFETY: `result_count` represents the number of core Wasm
2302fa70f025SJoel Dice                     // results returned, per `wasmparser`.
23037e39c25eSJoel Dice                     let result = (lift.lift)(store, unsafe {
2304fa70f025SJoel Dice                         mem::transmute::<&[MaybeUninit<ValRaw>], &[ValRaw]>(
2305fa70f025SJoel Dice                             &storage[..result_count],
2306fa70f025SJoel Dice                         )
2307fa70f025SJoel Dice                     })?;
2308fa70f025SJoel Dice 
2309fa70f025SJoel Dice                     let post_return_arg = match result_count {
2310fa70f025SJoel Dice                         0 => ValRaw::i32(0),
2311fa70f025SJoel Dice                         // SAFETY: `result_count` represents the number of
2312fa70f025SJoel Dice                         // core Wasm results returned, per `wasmparser`.
2313fa70f025SJoel Dice                         1 => unsafe { storage[0].assume_init() },
2314fa70f025SJoel Dice                         _ => unreachable!(),
2315fa70f025SJoel Dice                     };
2316fa70f025SJoel Dice 
23176751ea79SJoel Dice                     unsafe {
2318c09aa380SJoel Dice                         call_post_return(
2319c09aa380SJoel Dice                             token.as_context_mut(store),
2320c09aa380SJoel Dice                             post_return.map(|v| v.as_non_null()),
2321fa70f025SJoel Dice                             post_return_arg,
2322c09aa380SJoel Dice                             flags,
2323fa70f025SJoel Dice                         )?;
2324fa70f025SJoel Dice                     }
2325fa70f025SJoel Dice 
2326c09aa380SJoel Dice                     self.task_complete(store, guest_thread.task, result, Status::Returned)?;
2327c09aa380SJoel Dice                 }
2328c09aa380SJoel Dice 
2329c09aa380SJoel Dice                 // This is a callback-less call, so the implicit thread has now completed
2330c09aa380SJoel Dice                 self.cleanup_thread(store, guest_thread, callee_instance.index)?;
2331c09aa380SJoel Dice 
2332da093747SAlex Crichton                 store.set_thread(old_thread)?;
2333fae9e6afSJoel Dice 
2334e06fbf70SSy Brand                 let state = store.concurrent_state_mut();
2335e06fbf70SSy Brand                 let task = state.get_mut(guest_thread.task)?;
2336fa70f025SJoel Dice 
2337fa70f025SJoel Dice                 match &task.caller {
2338e06fbf70SSy Brand                     Caller::Host { .. } => {
2339e06fbf70SSy Brand                         if task.ready_to_delete() {
2340e06fbf70SSy Brand                             Waitable::Guest(guest_thread.task).delete_from(state)?;
2341fa70f025SJoel Dice                         }
2342fa70f025SJoel Dice                     }
2343fa70f025SJoel Dice                     Caller::Guest { .. } => {
2344fa70f025SJoel Dice                         task.exited = true;
2345fa70f025SJoel Dice                     }
2346fa70f025SJoel Dice                 }
2347fa70f025SJoel Dice 
23488992b99bSJoel Dice                 Ok(None)
2349fa70f025SJoel Dice             })
2350fa70f025SJoel Dice         };
2351fa70f025SJoel Dice 
23527e39c25eSJoel Dice         store
23537e39c25eSJoel Dice             .0
23547e39c25eSJoel Dice             .concurrent_state_mut()
2355d2fbd2deSAlex Crichton             .push_high_priority(WorkItem::GuestCall(
2356d2fbd2deSAlex Crichton                 callee_instance.index,
2357d2fbd2deSAlex Crichton                 GuestCall {
2358e06fbf70SSy Brand                     thread: guest_thread,
2359e06fbf70SSy Brand                     kind: GuestCallKind::StartImplicit(fun),
2360d2fbd2deSAlex Crichton                 },
2361d2fbd2deSAlex Crichton             ));
2362fa70f025SJoel Dice 
2363fa70f025SJoel Dice         Ok(())
2364fa70f025SJoel Dice     }
2365fa70f025SJoel Dice 
2366fa70f025SJoel Dice     /// Prepare (but do not start) a guest->guest call.
2367fa70f025SJoel Dice     ///
2368fa70f025SJoel Dice     /// This is called from fused adapter code generated in
2369fa70f025SJoel Dice     /// `wasmtime_environ::fact::trampoline::Compiler`.  `start` and `return_`
2370fa70f025SJoel Dice     /// are synthesized Wasm functions which move the parameters from the caller
2371fa70f025SJoel Dice     /// to the callee and the result from the callee to the caller,
2372fa70f025SJoel Dice     /// respectively.  The adapter will call `Self::start_call` immediately
2373fa70f025SJoel Dice     /// after calling this function.
2374fa70f025SJoel Dice     ///
2375fa70f025SJoel Dice     /// SAFETY: All the pointer arguments must be valid pointers to guest
2376fa70f025SJoel Dice     /// entities (and with the expected signatures for the function references
2377fa70f025SJoel Dice     /// -- see `wasmtime_environ::fact::trampoline::Compiler` for details).
prepare_call<T: 'static>( self, mut store: StoreContextMut<T>, start: NonNull<VMFuncRef>, return_: NonNull<VMFuncRef>, caller_instance: RuntimeComponentInstanceIndex, callee_instance: RuntimeComponentInstanceIndex, task_return_type: TypeTupleIndex, callee_async: bool, memory: *mut VMMemoryDefinition, string_encoding: StringEncoding, caller_info: CallerInfo, ) -> Result<()>2378fa70f025SJoel Dice     unsafe fn prepare_call<T: 'static>(
2379fa70f025SJoel Dice         self,
2380fa70f025SJoel Dice         mut store: StoreContextMut<T>,
2381da093747SAlex Crichton         start: NonNull<VMFuncRef>,
2382da093747SAlex Crichton         return_: NonNull<VMFuncRef>,
2383fa70f025SJoel Dice         caller_instance: RuntimeComponentInstanceIndex,
2384fa70f025SJoel Dice         callee_instance: RuntimeComponentInstanceIndex,
2385fa70f025SJoel Dice         task_return_type: TypeTupleIndex,
23868992b99bSJoel Dice         callee_async: bool,
2387fa70f025SJoel Dice         memory: *mut VMMemoryDefinition,
2388da093747SAlex Crichton         string_encoding: StringEncoding,
2389fa70f025SJoel Dice         caller_info: CallerInfo,
2390fa70f025SJoel Dice     ) -> Result<()> {
23918992b99bSJoel Dice         if let (CallerInfo::Sync { .. }, true) = (&caller_info, callee_async) {
23928992b99bSJoel Dice             // A task may only call an async-typed function via a sync lower if
23938992b99bSJoel Dice             // it was created by a call to an async export.  Otherwise, we'll
23948992b99bSJoel Dice             // trap.
2395fae9e6afSJoel Dice             store.0.check_blocking()?;
23968992b99bSJoel Dice         }
23978992b99bSJoel Dice 
2398fa70f025SJoel Dice         enum ResultInfo {
2399fa70f025SJoel Dice             Heap { results: u32 },
2400fa70f025SJoel Dice             Stack { result_count: u32 },
2401fa70f025SJoel Dice         }
2402fa70f025SJoel Dice 
2403fa70f025SJoel Dice         let result_info = match &caller_info {
2404fa70f025SJoel Dice             CallerInfo::Async {
2405fa70f025SJoel Dice                 has_result: true,
2406fa70f025SJoel Dice                 params,
2407fa70f025SJoel Dice             } => ResultInfo::Heap {
2408da093747SAlex Crichton                 results: match params.last() {
2409da093747SAlex Crichton                     Some(r) => r.get_u32(),
2410da093747SAlex Crichton                     None => bail_bug!("retptr missing"),
2411da093747SAlex Crichton                 },
2412fa70f025SJoel Dice             },
2413fa70f025SJoel Dice             CallerInfo::Async {
2414fa70f025SJoel Dice                 has_result: false, ..
2415fa70f025SJoel Dice             } => ResultInfo::Stack { result_count: 0 },
2416fa70f025SJoel Dice             CallerInfo::Sync {
2417fa70f025SJoel Dice                 result_count,
2418fa70f025SJoel Dice                 params,
2419da093747SAlex Crichton             } if *result_count > u32::try_from(MAX_FLAT_RESULTS)? => ResultInfo::Heap {
2420da093747SAlex Crichton                 results: match params.last() {
2421da093747SAlex Crichton                     Some(r) => r.get_u32(),
2422da093747SAlex Crichton                     None => bail_bug!("arg ptr missing"),
2423da093747SAlex Crichton                 },
2424fa70f025SJoel Dice             },
2425fa70f025SJoel Dice             CallerInfo::Sync { result_count, .. } => ResultInfo::Stack {
2426fa70f025SJoel Dice                 result_count: *result_count,
2427fa70f025SJoel Dice             },
2428fa70f025SJoel Dice         };
2429fa70f025SJoel Dice 
2430fa70f025SJoel Dice         let sync_caller = matches!(caller_info, CallerInfo::Sync { .. });
2431fa70f025SJoel Dice 
2432fa70f025SJoel Dice         // Create a new guest task for the call, closing over the `start` and
2433fa70f025SJoel Dice         // `return_` functions to lift the parameters and lower the result,
2434fa70f025SJoel Dice         // respectively.
2435da093747SAlex Crichton         let start = SendSyncPtr::new(start);
2436da093747SAlex Crichton         let return_ = SendSyncPtr::new(return_);
2437fa70f025SJoel Dice         let token = StoreToken::new(store.as_context_mut());
24387e39c25eSJoel Dice         let state = store.0.concurrent_state_mut();
2439da093747SAlex Crichton         let old_thread = state.current_guest_thread()?;
2440b271e452SJoel Dice 
2441da093747SAlex Crichton         debug_assert_eq!(
2442b271e452SJoel Dice             state.get_mut(old_thread.task)?.instance,
2443b271e452SJoel Dice             RuntimeInstance {
2444b271e452SJoel Dice                 instance: self.id().instance(),
2445b271e452SJoel Dice                 index: caller_instance,
2446b271e452SJoel Dice             }
2447b271e452SJoel Dice         );
2448b271e452SJoel Dice 
2449fa70f025SJoel Dice         let new_task = GuestTask::new(
24507e39c25eSJoel Dice             Box::new(move |store, dst| {
2451fa70f025SJoel Dice                 let mut store = token.as_context_mut(store);
2452fa70f025SJoel Dice                 assert!(dst.len() <= MAX_FLAT_PARAMS);
2453449e5962SJoel Dice                 // The `+ 1` here accounts for the return pointer, if any:
2454449e5962SJoel Dice                 let mut src = [MaybeUninit::uninit(); MAX_FLAT_PARAMS + 1];
2455fa70f025SJoel Dice                 let count = match caller_info {
2456fa70f025SJoel Dice                     // Async callers, if they have a result, use the last
2457fa70f025SJoel Dice                     // parameter as a return pointer so chop that off if
2458fa70f025SJoel Dice                     // relevant here.
2459fa70f025SJoel Dice                     CallerInfo::Async { params, has_result } => {
2460fa70f025SJoel Dice                         let params = &params[..params.len() - usize::from(has_result)];
2461fa70f025SJoel Dice                         for (param, src) in params.iter().zip(&mut src) {
2462fa70f025SJoel Dice                             src.write(*param);
2463fa70f025SJoel Dice                         }
2464fa70f025SJoel Dice                         params.len()
2465fa70f025SJoel Dice                     }
2466fa70f025SJoel Dice 
2467fa70f025SJoel Dice                     // Sync callers forward everything directly.
2468fa70f025SJoel Dice                     CallerInfo::Sync { params, .. } => {
2469fa70f025SJoel Dice                         for (param, src) in params.iter().zip(&mut src) {
2470fa70f025SJoel Dice                             src.write(*param);
2471fa70f025SJoel Dice                         }
2472fa70f025SJoel Dice                         params.len()
2473fa70f025SJoel Dice                     }
2474fa70f025SJoel Dice                 };
2475fa70f025SJoel Dice                 // SAFETY: `start` is a valid `*mut VMFuncRef` from
2476fa70f025SJoel Dice                 // `wasmtime-cranelift`-generated fused adapter code.  Based on
2477fa70f025SJoel Dice                 // how it was constructed (see
2478fa70f025SJoel Dice                 // `wasmtime_environ::fact::trampoline::Compiler::compile_async_start_adapter`
2479fa70f025SJoel Dice                 // for details) we know it takes count parameters and returns
2480fa70f025SJoel Dice                 // `dst.len()` results.
2481fa70f025SJoel Dice                 unsafe {
2482fa70f025SJoel Dice                     crate::Func::call_unchecked_raw(
2483fa70f025SJoel Dice                         &mut store,
2484fa70f025SJoel Dice                         start.as_non_null(),
2485fa70f025SJoel Dice                         NonNull::new(
2486fa70f025SJoel Dice                             &mut src[..count.max(dst.len())] as *mut [MaybeUninit<ValRaw>] as _,
2487fa70f025SJoel Dice                         )
2488fa70f025SJoel Dice                         .unwrap(),
2489fa70f025SJoel Dice                     )?;
2490fa70f025SJoel Dice                 }
2491fa70f025SJoel Dice                 dst.copy_from_slice(&src[..dst.len()]);
24927e39c25eSJoel Dice                 let state = store.0.concurrent_state_mut();
2493da093747SAlex Crichton                 Waitable::Guest(state.current_guest_thread()?.task).set_event(
2494fa70f025SJoel Dice                     state,
2495fa70f025SJoel Dice                     Some(Event::Subtask {
2496fa70f025SJoel Dice                         status: Status::Started,
2497fa70f025SJoel Dice                     }),
2498fa70f025SJoel Dice                 )?;
2499fa70f025SJoel Dice                 Ok(())
2500fa70f025SJoel Dice             }),
2501fa70f025SJoel Dice             LiftResult {
25027e39c25eSJoel Dice                 lift: Box::new(move |store, src| {
2503fa70f025SJoel Dice                     // SAFETY: See comment in closure passed as `lower_params`
2504fa70f025SJoel Dice                     // parameter above.
2505fa70f025SJoel Dice                     let mut store = token.as_context_mut(store);
2506fa70f025SJoel Dice                     let mut my_src = src.to_owned(); // TODO: use stack to avoid allocation?
2507fa70f025SJoel Dice                     if let ResultInfo::Heap { results } = &result_info {
2508fa70f025SJoel Dice                         my_src.push(ValRaw::u32(*results));
2509fa70f025SJoel Dice                     }
2510039ae2afSAlex Crichton 
2511039ae2afSAlex Crichton                     // Execute the `return_` hook, generated by Wasmtime's FACT
2512039ae2afSAlex Crichton                     // compiler, in the context of the old thread. The old
2513039ae2afSAlex Crichton                     // thread, this thread's caller, may have `realloc`
2514039ae2afSAlex Crichton                     // callbacks invoked for example and those need the correct
2515039ae2afSAlex Crichton                     // context set for the current thread.
2516039ae2afSAlex Crichton                     let prev = store.0.set_thread(old_thread)?;
2517039ae2afSAlex Crichton 
2518fa70f025SJoel Dice                     // SAFETY: `return_` is a valid `*mut VMFuncRef` from
2519fa70f025SJoel Dice                     // `wasmtime-cranelift`-generated fused adapter code.  Based
2520fa70f025SJoel Dice                     // on how it was constructed (see
2521fa70f025SJoel Dice                     // `wasmtime_environ::fact::trampoline::Compiler::compile_async_return_adapter`
2522fa70f025SJoel Dice                     // for details) we know it takes `src.len()` parameters and
2523fa70f025SJoel Dice                     // returns up to 1 result.
2524fa70f025SJoel Dice                     unsafe {
2525fa70f025SJoel Dice                         crate::Func::call_unchecked_raw(
2526fa70f025SJoel Dice                             &mut store,
2527fa70f025SJoel Dice                             return_.as_non_null(),
2528fa70f025SJoel Dice                             my_src.as_mut_slice().into(),
2529fa70f025SJoel Dice                         )?;
2530fa70f025SJoel Dice                     }
2531039ae2afSAlex Crichton 
2532039ae2afSAlex Crichton                     // Restore the previous current thread after the
2533039ae2afSAlex Crichton                     // lifting/lowering has returned.
2534039ae2afSAlex Crichton                     store.0.set_thread(prev)?;
2535039ae2afSAlex Crichton 
25367e39c25eSJoel Dice                     let state = store.0.concurrent_state_mut();
2537da093747SAlex Crichton                     let thread = state.current_guest_thread()?;
2538fa70f025SJoel Dice                     if sync_caller {
2539e06fbf70SSy Brand                         state.get_mut(thread.task)?.sync_result = SyncResult::Produced(
2540e06fbf70SSy Brand                             if let ResultInfo::Stack { result_count } = &result_info {
2541fa70f025SJoel Dice                                 match result_count {
2542fa70f025SJoel Dice                                     0 => None,
2543fa70f025SJoel Dice                                     1 => Some(my_src[0]),
2544fa70f025SJoel Dice                                     _ => unreachable!(),
2545fa70f025SJoel Dice                                 }
2546fa70f025SJoel Dice                             } else {
2547fa70f025SJoel Dice                                 None
2548e06fbf70SSy Brand                             },
2549e06fbf70SSy Brand                         );
2550fa70f025SJoel Dice                     }
2551fa70f025SJoel Dice                     Ok(Box::new(DummyResult) as Box<dyn Any + Send + Sync>)
2552fa70f025SJoel Dice                 }),
2553fa70f025SJoel Dice                 ty: task_return_type,
2554fa70f025SJoel Dice                 memory: NonNull::new(memory).map(SendSyncPtr::new),
2555da093747SAlex Crichton                 string_encoding,
2556fa70f025SJoel Dice             },
2557b271e452SJoel Dice             Caller::Guest { thread: old_thread },
2558fa70f025SJoel Dice             None,
2559b856261dSJoel Dice             RuntimeInstance {
2560b856261dSJoel Dice                 instance: self.id().instance(),
2561b856261dSJoel Dice                 index: callee_instance,
2562b856261dSJoel Dice             },
25638992b99bSJoel Dice             callee_async,
2564fa70f025SJoel Dice         )?;
2565fa70f025SJoel Dice 
2566fa70f025SJoel Dice         let guest_task = state.push(new_task)?;
256735887491SSy Brand         let new_thread = GuestThread::new_implicit(state, guest_task)?;
2568e06fbf70SSy Brand         let guest_thread = state.push(new_thread)?;
2569e06fbf70SSy Brand         state.get_mut(guest_task)?.threads.insert(guest_thread);
2570fa70f025SJoel Dice 
2571e06fbf70SSy Brand         // Make the new thread the current one so that `Self::start_call` knows
2572fa70f025SJoel Dice         // which one to start.
25733764e757SAlex Crichton         store.0.set_thread(QualifiedThreadId {
2574e06fbf70SSy Brand             task: guest_task,
2575e06fbf70SSy Brand             thread: guest_thread,
2576da093747SAlex Crichton         })?;
2577e06fbf70SSy Brand         log::trace!(
2578e06fbf70SSy Brand             "pushed {guest_task:?}:{guest_thread:?} as current thread; old thread was {old_thread:?}"
2579e06fbf70SSy Brand         );
2580fa70f025SJoel Dice 
2581fa70f025SJoel Dice         Ok(())
2582fa70f025SJoel Dice     }
2583fa70f025SJoel Dice 
2584fa70f025SJoel Dice     /// Call the specified callback function for an async-lifted export.
2585fa70f025SJoel Dice     ///
2586fa70f025SJoel Dice     /// SAFETY: `function` must be a valid reference to a guest function of the
2587fa70f025SJoel Dice     /// correct signature for a callback.
call_callback<T>( self, mut store: StoreContextMut<T>, function: SendSyncPtr<VMFuncRef>, event: Event, handle: u32, ) -> Result<u32>2588fa70f025SJoel Dice     unsafe fn call_callback<T>(
2589fa70f025SJoel Dice         self,
2590fa70f025SJoel Dice         mut store: StoreContextMut<T>,
2591fa70f025SJoel Dice         function: SendSyncPtr<VMFuncRef>,
2592fa70f025SJoel Dice         event: Event,
2593fa70f025SJoel Dice         handle: u32,
2594fa70f025SJoel Dice     ) -> Result<u32> {
2595fa70f025SJoel Dice         let (ordinal, result) = event.parts();
2596fa70f025SJoel Dice         let params = &mut [
2597fa70f025SJoel Dice             ValRaw::u32(ordinal),
2598fa70f025SJoel Dice             ValRaw::u32(handle),
2599fa70f025SJoel Dice             ValRaw::u32(result),
2600fa70f025SJoel Dice         ];
2601fa70f025SJoel Dice         // SAFETY: `func` is a valid `*mut VMFuncRef` from either
2602fa70f025SJoel Dice         // `wasmtime-cranelift`-generated fused adapter code or
2603fa70f025SJoel Dice         // `component::Options`.  Per `wasmparser` callback signature
2604fa70f025SJoel Dice         // validation, we know it takes three parameters and returns one.
2605fa70f025SJoel Dice         unsafe {
2606fa70f025SJoel Dice             crate::Func::call_unchecked_raw(
2607fa70f025SJoel Dice                 &mut store,
2608fa70f025SJoel Dice                 function.as_non_null(),
2609fa70f025SJoel Dice                 params.as_mut_slice().into(),
2610fa70f025SJoel Dice             )?;
2611fa70f025SJoel Dice         }
2612fa70f025SJoel Dice         Ok(params[0].get_u32())
2613fa70f025SJoel Dice     }
2614fa70f025SJoel Dice 
2615fa70f025SJoel Dice     /// Start a guest->guest call previously prepared using
2616fa70f025SJoel Dice     /// `Self::prepare_call`.
2617fa70f025SJoel Dice     ///
2618fa70f025SJoel Dice     /// This is called from fused adapter code generated in
2619fa70f025SJoel Dice     /// `wasmtime_environ::fact::trampoline::Compiler`.  The adapter will call
2620fa70f025SJoel Dice     /// this function immediately after calling `Self::prepare_call`.
2621fa70f025SJoel Dice     ///
2622fa70f025SJoel Dice     /// SAFETY: The `*mut VMFuncRef` arguments must be valid pointers to guest
2623fa70f025SJoel Dice     /// functions with the appropriate signatures for the current guest task.
2624fa70f025SJoel Dice     /// If this is a call to an async-lowered import, the actual call may be
2625fa70f025SJoel Dice     /// deferred and run after this function returns, in which case the pointer
2626fa70f025SJoel Dice     /// arguments must also be valid when the call happens.
start_call<T: 'static>( self, mut store: StoreContextMut<T>, callback: *mut VMFuncRef, post_return: *mut VMFuncRef, callee: NonNull<VMFuncRef>, param_count: u32, result_count: u32, flags: u32, storage: Option<&mut [MaybeUninit<ValRaw>]>, ) -> Result<u32>2627fa70f025SJoel Dice     unsafe fn start_call<T: 'static>(
2628fa70f025SJoel Dice         self,
2629fa70f025SJoel Dice         mut store: StoreContextMut<T>,
2630fa70f025SJoel Dice         callback: *mut VMFuncRef,
2631fa70f025SJoel Dice         post_return: *mut VMFuncRef,
2632da093747SAlex Crichton         callee: NonNull<VMFuncRef>,
2633fa70f025SJoel Dice         param_count: u32,
2634fa70f025SJoel Dice         result_count: u32,
2635fa70f025SJoel Dice         flags: u32,
2636fa70f025SJoel Dice         storage: Option<&mut [MaybeUninit<ValRaw>]>,
2637fa70f025SJoel Dice     ) -> Result<u32> {
2638fa70f025SJoel Dice         let token = StoreToken::new(store.as_context_mut());
2639fa70f025SJoel Dice         let async_caller = storage.is_none();
26407e39c25eSJoel Dice         let state = store.0.concurrent_state_mut();
2641da093747SAlex Crichton         let guest_thread = state.current_guest_thread()?;
26428992b99bSJoel Dice         let callee_async = state.get_mut(guest_thread.task)?.async_function;
2643da093747SAlex Crichton         let callee = SendSyncPtr::new(callee);
2644da093747SAlex Crichton         let param_count = usize::try_from(param_count)?;
2645fa70f025SJoel Dice         assert!(param_count <= MAX_FLAT_PARAMS);
2646da093747SAlex Crichton         let result_count = usize::try_from(result_count)?;
2647fa70f025SJoel Dice         assert!(result_count <= MAX_FLAT_RESULTS);
2648fa70f025SJoel Dice 
2649e06fbf70SSy Brand         let task = state.get_mut(guest_thread.task)?;
2650da093747SAlex Crichton         if let Some(callback) = NonNull::new(callback) {
2651fa70f025SJoel Dice             // We're calling an async-lifted export with a callback, so store
2652fa70f025SJoel Dice             // the callback and related context as part of the task so we can
2653fa70f025SJoel Dice             // call it later when needed.
2654da093747SAlex Crichton             let callback = SendSyncPtr::new(callback);
2655b856261dSJoel Dice             task.callback = Some(Box::new(move |store, event, handle| {
2656fa70f025SJoel Dice                 let store = token.as_context_mut(store);
2657b856261dSJoel Dice                 unsafe { self.call_callback::<T>(store, callback, event, handle) }
26587e39c25eSJoel Dice             }));
2659fa70f025SJoel Dice         }
2660fa70f025SJoel Dice 
2661b271e452SJoel Dice         let Caller::Guest { thread: caller } = &task.caller else {
2662fa70f025SJoel Dice             // As of this writing, `start_call` is only used for guest->guest
2663fa70f025SJoel Dice             // calls.
2664da093747SAlex Crichton             bail_bug!("start_call unexpectedly invoked for host->guest call");
2665fa70f025SJoel Dice         };
2666fa70f025SJoel Dice         let caller = *caller;
2667b271e452SJoel Dice         let caller_instance = state.get_mut(caller.task)?.instance;
2668fa70f025SJoel Dice 
2669fa70f025SJoel Dice         // Queue the call as a "high priority" work item.
2670fa70f025SJoel Dice         unsafe {
2671fa70f025SJoel Dice             self.queue_call(
2672fa70f025SJoel Dice                 store.as_context_mut(),
2673e06fbf70SSy Brand                 guest_thread,
2674fa70f025SJoel Dice                 callee,
2675fa70f025SJoel Dice                 param_count,
2676fa70f025SJoel Dice                 result_count,
2677fa70f025SJoel Dice                 (flags & START_FLAG_ASYNC_CALLEE) != 0,
2678fa70f025SJoel Dice                 NonNull::new(callback).map(SendSyncPtr::new),
2679fa70f025SJoel Dice                 NonNull::new(post_return).map(SendSyncPtr::new),
2680fa70f025SJoel Dice             )?;
2681fa70f025SJoel Dice         }
2682fa70f025SJoel Dice 
26837e39c25eSJoel Dice         let state = store.0.concurrent_state_mut();
2684fa70f025SJoel Dice 
268535887491SSy Brand         // Use the caller's `GuestThread::sync_call_set` to register interest in
2686fa70f025SJoel Dice         // the subtask...
2687e06fbf70SSy Brand         let guest_waitable = Waitable::Guest(guest_thread.task);
26881a0f9538SJoel Dice         let old_set = guest_waitable.common(state)?.set;
268935887491SSy Brand         let set = state.get_mut(caller.thread)?.sync_call_set;
26901a0f9538SJoel Dice         guest_waitable.join(state, Some(set))?;
2691fa70f025SJoel Dice 
2692fa70f025SJoel Dice         // ... and suspend this fiber temporarily while we wait for it to start.
2693fa70f025SJoel Dice         //
2694fa70f025SJoel Dice         // Note that we _could_ call the callee directly using the current fiber
2695fa70f025SJoel Dice         // rather than suspend this one, but that would make reasoning about the
2696fa70f025SJoel Dice         // event loop more complicated and is probably only worth doing if
2697fa70f025SJoel Dice         // there's a measurable performance benefit.  In addition, it would mean
2698fa70f025SJoel Dice         // blocking the caller if the callee calls a blocking sync-lowered
2699fa70f025SJoel Dice         // import, and as of this writing the spec says we must not do that.
2700fa70f025SJoel Dice         //
2701fa70f025SJoel Dice         // Alternatively, the fused adapter code could be modified to call the
2702fa70f025SJoel Dice         // callee directly without calling a host-provided intrinsic at all (in
2703fa70f025SJoel Dice         // which case it would need to do its own, inline backpressure checks,
2704fa70f025SJoel Dice         // etc.).  Again, we'd want to see a measurable performance benefit
2705fa70f025SJoel Dice         // before committing to such an optimization.  And again, we'd need to
2706fa70f025SJoel Dice         // update the spec to allow that.
2707fa70f025SJoel Dice         let (status, waitable) = loop {
2708e06fbf70SSy Brand             store.0.suspend(SuspendReason::Waiting {
2709e06fbf70SSy Brand                 set,
2710e06fbf70SSy Brand                 thread: caller,
27118992b99bSJoel Dice                 // Normally, `StoreOpaque::suspend` would assert it's being
27128992b99bSJoel Dice                 // called from a context where blocking is allowed.  However, if
27138992b99bSJoel Dice                 // `async_caller` is `true`, we'll only "block" long enough for
27148992b99bSJoel Dice                 // the callee to start, i.e. we won't repeat this loop, so we
27158992b99bSJoel Dice                 // tell `suspend` it's okay even if we're not allowed to block.
27168992b99bSJoel Dice                 // Alternatively, if the callee is not an async function, then
27178992b99bSJoel Dice                 // we know it won't block anyway.
27188992b99bSJoel Dice                 skip_may_block_check: async_caller || !callee_async,
2719e06fbf70SSy Brand             })?;
2720fa70f025SJoel Dice 
27217e39c25eSJoel Dice             let state = store.0.concurrent_state_mut();
2722fa70f025SJoel Dice 
2723e06fbf70SSy Brand             log::trace!("taking event for {:?}", guest_thread.task);
2724e8189549SJoel Dice             let event = guest_waitable.take_event(state)?;
2725fa70f025SJoel Dice             let Some(Event::Subtask { status }) = event else {
2726da093747SAlex Crichton                 bail_bug!("subtasks should only get subtask events, got {event:?}")
2727fa70f025SJoel Dice             };
2728fa70f025SJoel Dice 
2729e06fbf70SSy Brand             log::trace!("status {status:?} for {:?}", guest_thread.task);
2730fa70f025SJoel Dice 
2731fa70f025SJoel Dice             if status == Status::Returned {
2732fa70f025SJoel Dice                 // It returned, so we can stop waiting.
2733fa70f025SJoel Dice                 break (status, None);
2734fa70f025SJoel Dice             } else if async_caller {
2735fa70f025SJoel Dice                 // It hasn't returned yet, but the caller is calling via an
2736fa70f025SJoel Dice                 // async-lowered import, so we generate a handle for the task
2737fa70f025SJoel Dice                 // waitable and return the status.
2738cb97ae85SJoel Dice                 let handle = store
2739cb97ae85SJoel Dice                     .0
274057f899c4SAlex Crichton                     .instance_state(caller_instance)
274157f899c4SAlex Crichton                     .handle_table()
2742e06fbf70SSy Brand                     .subtask_insert_guest(guest_thread.task.rep())?;
27437e39c25eSJoel Dice                 store
27447e39c25eSJoel Dice                     .0
27457e39c25eSJoel Dice                     .concurrent_state_mut()
2746e06fbf70SSy Brand                     .get_mut(guest_thread.task)?
2747e8189549SJoel Dice                     .common
2748e8189549SJoel Dice                     .handle = Some(handle);
2749e8189549SJoel Dice                 break (status, Some(handle));
2750fa70f025SJoel Dice             } else {
2751fa70f025SJoel Dice                 // The callee hasn't returned yet, and the caller is calling via
2752fa70f025SJoel Dice                 // a sync-lowered import, so we loop and keep waiting until the
2753fa70f025SJoel Dice                 // callee returns.
2754fa70f025SJoel Dice             }
2755fa70f025SJoel Dice         };
2756fa70f025SJoel Dice 
2757fae9e6afSJoel Dice         guest_waitable.join(store.0.concurrent_state_mut(), old_set)?;
2758fa70f025SJoel Dice 
2759fae9e6afSJoel Dice         // Reset the current thread to point to the caller as it resumes control.
2760da093747SAlex Crichton         store.0.set_thread(caller)?;
2761fae9e6afSJoel Dice         store.0.concurrent_state_mut().get_mut(caller.thread)?.state = GuestThreadState::Running;
2762fae9e6afSJoel Dice         log::trace!("popped current thread {guest_thread:?}; new thread is {caller:?}");
2763fa70f025SJoel Dice 
2764fa70f025SJoel Dice         if let Some(storage) = storage {
2765fa70f025SJoel Dice             // The caller used a sync-lowered import to call an async-lifted
2766fa70f025SJoel Dice             // export, in which case the result, if any, has been stashed in
2767fa70f025SJoel Dice             // `GuestTask::sync_result`.
2768fae9e6afSJoel Dice             let state = store.0.concurrent_state_mut();
2769e06fbf70SSy Brand             let task = state.get_mut(guest_thread.task)?;
2770da093747SAlex Crichton             if let Some(result) = task.sync_result.take()? {
2771fa70f025SJoel Dice                 if let Some(result) = result {
2772fa70f025SJoel Dice                     storage[0] = MaybeUninit::new(result);
2773fa70f025SJoel Dice                 }
2774fa70f025SJoel Dice 
2775fae9e6afSJoel Dice                 if task.exited && task.ready_to_delete() {
2776e06fbf70SSy Brand                     Waitable::Guest(guest_thread.task).delete_from(state)?;
2777078364f6SJoel Dice                 }
2778e06fbf70SSy Brand             }
2779fa70f025SJoel Dice         }
2780fa70f025SJoel Dice 
2781fa70f025SJoel Dice         Ok(status.pack(waitable))
2782fa70f025SJoel Dice     }
2783fa70f025SJoel Dice 
2784b221fca7SJoel Dice     /// Poll the specified future once on behalf of a guest->host call using an
2785b221fca7SJoel Dice     /// async-lowered import.
2786b221fca7SJoel Dice     ///
2787b221fca7SJoel Dice     /// If it returns `Ready`, return `Ok(None)`.  Otherwise, if it returns
2788b221fca7SJoel Dice     /// `Pending`, add it to the set of futures to be polled as part of this
2789b221fca7SJoel Dice     /// instance's event loop until it completes, and then return
2790b221fca7SJoel Dice     /// `Ok(Some(handle))` where `handle` is the waitable handle to return.
2791b221fca7SJoel Dice     ///
2792b221fca7SJoel Dice     /// Whether the future returns `Ready` immediately or later, the `lower`
2793b221fca7SJoel Dice     /// function will be used to lower the result, if any, into the guest caller's
2794065baac4SAlex Crichton     /// stack and linear memory. The `lower` function is invoked with `None` if
2795065baac4SAlex Crichton     /// the future is cancelled.
first_poll<T: 'static, R: Send + 'static>( self, mut store: StoreContextMut<'_, T>, future: impl Future<Output = Result<R>> + Send + 'static, lower: impl FnOnce(StoreContextMut<T>, Option<R>) -> Result<()> + Send + 'static, ) -> Result<Option<u32>>2796b221fca7SJoel Dice     pub(crate) fn first_poll<T: 'static, R: Send + 'static>(
2797b221fca7SJoel Dice         self,
279805a711f6SAlex Crichton         mut store: StoreContextMut<'_, T>,
2799b221fca7SJoel Dice         future: impl Future<Output = Result<R>> + Send + 'static,
2800065baac4SAlex Crichton         lower: impl FnOnce(StoreContextMut<T>, Option<R>) -> Result<()> + Send + 'static,
2801b221fca7SJoel Dice     ) -> Result<Option<u32>> {
2802fa70f025SJoel Dice         let token = StoreToken::new(store.as_context_mut());
28037e39c25eSJoel Dice         let state = store.0.concurrent_state_mut();
2804da093747SAlex Crichton         let task = state.current_host_thread()?;
2805fa70f025SJoel Dice 
2806fa70f025SJoel Dice         // Create an abortable future which hooks calls to poll and manages call
2807fa70f025SJoel Dice         // context state for the future.
28083764e757SAlex Crichton         let (join_handle, future) = JoinHandle::run(future);
28093764e757SAlex Crichton         {
2810065baac4SAlex Crichton             let state = &mut state.get_mut(task)?.state;
2811065baac4SAlex Crichton             assert!(matches!(state, HostTaskState::CalleeStarted));
2812065baac4SAlex Crichton             *state = HostTaskState::CalleeRunning(join_handle);
2813fa70f025SJoel Dice         }
2814fa70f025SJoel Dice 
28155764da5fSJoel Dice         let mut future = Box::pin(future);
2816fa70f025SJoel Dice 
2817fa70f025SJoel Dice         // Finally, poll the future.  We can use a dummy `Waker` here because
2818fa70f025SJoel Dice         // we'll add the future to `ConcurrentState::futures` and poll it
2819fa70f025SJoel Dice         // automatically from the event loop if it doesn't complete immediately
2820fa70f025SJoel Dice         // here.
28217e39c25eSJoel Dice         let poll = tls::set(store.0, || {
2822fa70f025SJoel Dice             future
2823fa70f025SJoel Dice                 .as_mut()
2824fa70f025SJoel Dice                 .poll(&mut Context::from_waker(&Waker::noop()))
2825fa70f025SJoel Dice         });
2826fa70f025SJoel Dice 
282788a7c608SAlex Crichton         match poll {
28283764e757SAlex Crichton             // It finished immediately; lower the result and delete the task.
2829da093747SAlex Crichton             Poll::Ready(result) => {
2830da093747SAlex Crichton                 let result = result.transpose()?;
2831da093747SAlex Crichton                 lower(store.as_context_mut(), result)?;
283288a7c608SAlex Crichton                 return Ok(None);
2833fa70f025SJoel Dice             }
28343764e757SAlex Crichton 
28353764e757SAlex Crichton             // Future isn't ready yet, so fall through.
283688a7c608SAlex Crichton             Poll::Pending => {}
283788a7c608SAlex Crichton         }
28383764e757SAlex Crichton 
2839fa70f025SJoel Dice         // It hasn't finished yet; add the future to
2840fa70f025SJoel Dice         // `ConcurrentState::futures` so it will be polled by the event
2841fa70f025SJoel Dice         // loop and allocate a waitable handle to return to the guest.
28425764da5fSJoel Dice 
28435764da5fSJoel Dice         // Wrap the future in a closure responsible for lowering the result into
28445764da5fSJoel Dice         // the guest's stack and memory, as well as notifying any waiters that
28455764da5fSJoel Dice         // the task returned.
284688a7c608SAlex Crichton         let future = Box::pin(async move {
28475764da5fSJoel Dice             let result = match future.await {
2848065baac4SAlex Crichton                 Some(result) => Some(result?),
2849065baac4SAlex Crichton                 None => None,
28505764da5fSJoel Dice             };
285188a7c608SAlex Crichton             let on_complete = move |store: &mut dyn VMStore| {
28523764e757SAlex Crichton                 // Restore the `current_thread` to be the host so `lower` knows
28533764e757SAlex Crichton                 // how to manipulate borrows and knows which scope of borrows
28543764e757SAlex Crichton                 // to check.
28553764e757SAlex Crichton                 let mut store = token.as_context_mut(store);
2856da093747SAlex Crichton                 let old = store.0.set_thread(task)?;
28573764e757SAlex Crichton 
2858065baac4SAlex Crichton                 let status = if result.is_some() {
2859065baac4SAlex Crichton                     Status::Returned
2860065baac4SAlex Crichton                 } else {
2861065baac4SAlex Crichton                     Status::ReturnCancelled
2862065baac4SAlex Crichton                 };
2863065baac4SAlex Crichton 
28643764e757SAlex Crichton                 lower(store.as_context_mut(), result)?;
28653764e757SAlex Crichton                 let state = store.0.concurrent_state_mut();
2866e8cb8751SAlex Crichton                 match &mut state.get_mut(task)?.state {
2867e8cb8751SAlex Crichton                     // The task is already flagged as finished because it was
2868e8cb8751SAlex Crichton                     // cancelled. No need to transition further.
2869e8cb8751SAlex Crichton                     HostTaskState::CalleeDone { .. } => {}
2870e8cb8751SAlex Crichton 
2871e8cb8751SAlex Crichton                     // Otherwise transition this task to the done state.
2872e8cb8751SAlex Crichton                     other => *other = HostTaskState::CalleeDone { cancelled: false },
2873e8cb8751SAlex Crichton                 }
2874065baac4SAlex Crichton                 Waitable::Host(task).set_event(state, Some(Event::Subtask { status }))?;
28753764e757SAlex Crichton 
2876da093747SAlex Crichton                 store.0.set_thread(old)?;
28773764e757SAlex Crichton                 Ok(())
287888a7c608SAlex Crichton             };
28793764e757SAlex Crichton 
28803764e757SAlex Crichton             // Here we schedule a task to run on a worker fiber to do the
28813764e757SAlex Crichton             // lowering since it may involve a call to the guest's realloc
28823764e757SAlex Crichton             // function. This is necessary because calling the guest while
28833764e757SAlex Crichton             // there are host embedder frames on the stack is unsound.
288488a7c608SAlex Crichton             tls::get(move |store| {
288588a7c608SAlex Crichton                 store
288688a7c608SAlex Crichton                     .concurrent_state_mut()
288788a7c608SAlex Crichton                     .push_high_priority(WorkItem::WorkerFunction(AlwaysMut::new(Box::new(
288888a7c608SAlex Crichton                         on_complete,
288988a7c608SAlex Crichton                     ))));
28905764da5fSJoel Dice                 Ok(())
28915764da5fSJoel Dice             })
28925764da5fSJoel Dice         });
28935764da5fSJoel Dice 
28943764e757SAlex Crichton         // Make this task visible to the guest and then record what it
28953764e757SAlex Crichton         // was made visible as.
28963764e757SAlex Crichton         let state = store.0.concurrent_state_mut();
28973764e757SAlex Crichton         state.push_future(future);
28983764e757SAlex Crichton         let caller = state.get_mut(task)?.caller;
28993764e757SAlex Crichton         let instance = state.get_mut(caller.task)?.instance;
2900cb97ae85SJoel Dice         let handle = store
2901cb97ae85SJoel Dice             .0
29023764e757SAlex Crichton             .instance_state(instance)
290357f899c4SAlex Crichton             .handle_table()
2904e8189549SJoel Dice             .subtask_insert_host(task.rep())?;
29057e39c25eSJoel Dice         store.0.concurrent_state_mut().get_mut(task)?.common.handle = Some(handle);
29063764e757SAlex Crichton         log::trace!("assign {task:?} handle {handle} for {caller:?} instance {instance:?}");
29073764e757SAlex Crichton 
29083764e757SAlex Crichton         // Restore the currently running thread to this host task's
29093764e757SAlex Crichton         // caller. Note that the host task isn't deallocated as it's
29103764e757SAlex Crichton         // within the store and will get deallocated later.
2911da093747SAlex Crichton         store.0.set_thread(caller)?;
291288a7c608SAlex Crichton         Ok(Some(handle))
2913b221fca7SJoel Dice     }
2914b221fca7SJoel Dice 
2915b221fca7SJoel Dice     /// Implements the `task.return` intrinsic, lifting the result for the
2916b221fca7SJoel Dice     /// current guest task.
task_return( self, store: &mut dyn VMStore, ty: TypeTupleIndex, options: OptionsIndex, storage: &[ValRaw], ) -> Result<()>2917815c10deSAlex Crichton     pub(crate) fn task_return(
2918b221fca7SJoel Dice         self,
2919b221fca7SJoel Dice         store: &mut dyn VMStore,
2920b221fca7SJoel Dice         ty: TypeTupleIndex,
2921815c10deSAlex Crichton         options: OptionsIndex,
2922815c10deSAlex Crichton         storage: &[ValRaw],
2923b221fca7SJoel Dice     ) -> Result<()> {
29247e39c25eSJoel Dice         let state = store.concurrent_state_mut();
2925da093747SAlex Crichton         let guest_thread = state.current_guest_thread()?;
2926fa70f025SJoel Dice         let lift = state
2927e06fbf70SSy Brand             .get_mut(guest_thread.task)?
2928fa70f025SJoel Dice             .lift_result
2929fa70f025SJoel Dice             .take()
2930da093747SAlex Crichton             .ok_or_else(|| Trap::TaskCancelOrReturnTwice)?;
2931da093747SAlex Crichton         if !state.get_mut(guest_thread.task)?.result.is_none() {
2932da093747SAlex Crichton             bail_bug!("task result unexpectedly already set");
2933da093747SAlex Crichton         }
2934fa70f025SJoel Dice 
29357e39c25eSJoel Dice         let CanonicalOptions {
29367e39c25eSJoel Dice             string_encoding,
29377e39c25eSJoel Dice             data_model,
29387e39c25eSJoel Dice             ..
29397e39c25eSJoel Dice         } = &self.id().get(store).component().env_component().options[options];
29407e39c25eSJoel Dice 
2941815c10deSAlex Crichton         let invalid = ty != lift.ty
29427e39c25eSJoel Dice             || string_encoding != &lift.string_encoding
2943815c10deSAlex Crichton             || match data_model {
2944815c10deSAlex Crichton                 CanonicalOptionsDataModel::LinearMemory(opts) => match opts.memory {
2945815c10deSAlex Crichton                     Some(memory) => {
2946815c10deSAlex Crichton                         let expected = lift.memory.map(|v| v.as_ptr()).unwrap_or(ptr::null_mut());
2947815c10deSAlex Crichton                         let actual = self.id().get(store).runtime_memory(memory);
2948ec9b62abSAlex Crichton                         expected != actual.as_ptr()
2949815c10deSAlex Crichton                     }
2950815c10deSAlex Crichton                     // Memory not specified, meaning it didn't need to be
2951815c10deSAlex Crichton                     // specified per validation, so not invalid.
2952815c10deSAlex Crichton                     None => false,
2953815c10deSAlex Crichton                 },
2954815c10deSAlex Crichton                 // Always invalid as this isn't supported.
2955815c10deSAlex Crichton                 CanonicalOptionsDataModel::Gc { .. } => true,
2956815c10deSAlex Crichton             };
2957815c10deSAlex Crichton 
2958815c10deSAlex Crichton         if invalid {
2959da093747SAlex Crichton             bail!(Trap::TaskReturnInvalid);
2960fa70f025SJoel Dice         }
2961fa70f025SJoel Dice 
2962e06fbf70SSy Brand         log::trace!("task.return for {guest_thread:?}");
2963fa70f025SJoel Dice 
29647e39c25eSJoel Dice         let result = (lift.lift)(store, storage)?;
2965c09aa380SJoel Dice         self.task_complete(store, guest_thread.task, result, Status::Returned)
2966b221fca7SJoel Dice     }
2967b221fca7SJoel Dice 
2968b221fca7SJoel Dice     /// Implements the `task.cancel` intrinsic.
task_cancel(self, store: &mut StoreOpaque) -> Result<()>29694d129904SAlex Crichton     pub(crate) fn task_cancel(self, store: &mut StoreOpaque) -> Result<()> {
29707e39c25eSJoel Dice         let state = store.concurrent_state_mut();
2971da093747SAlex Crichton         let guest_thread = state.current_guest_thread()?;
2972e06fbf70SSy Brand         let task = state.get_mut(guest_thread.task)?;
2973fa70f025SJoel Dice         if !task.cancel_sent {
2974da093747SAlex Crichton             bail!(Trap::TaskCancelNotCancelled);
2975fa70f025SJoel Dice         }
2976da093747SAlex Crichton         _ = task
2977da093747SAlex Crichton             .lift_result
2978da093747SAlex Crichton             .take()
2979da093747SAlex Crichton             .ok_or_else(|| Trap::TaskCancelOrReturnTwice)?;
2980fa70f025SJoel Dice 
2981da093747SAlex Crichton         if !task.result.is_none() {
2982da093747SAlex Crichton             bail_bug!("task result should not bet set yet");
2983da093747SAlex Crichton         }
2984fa70f025SJoel Dice 
2985e06fbf70SSy Brand         log::trace!("task.cancel for {guest_thread:?}");
2986fa70f025SJoel Dice 
2987fa70f025SJoel Dice         self.task_complete(
2988fa70f025SJoel Dice             store,
2989e06fbf70SSy Brand             guest_thread.task,
2990fa70f025SJoel Dice             Box::new(DummyResult),
2991fa70f025SJoel Dice             Status::ReturnCancelled,
2992fa70f025SJoel Dice         )
2993fa70f025SJoel Dice     }
2994fa70f025SJoel Dice 
2995fa70f025SJoel Dice     /// Complete the specified guest task (i.e. indicate that it has either
2996fa70f025SJoel Dice     /// returned a (possibly empty) result or cancelled itself).
2997fa70f025SJoel Dice     ///
2998fa70f025SJoel Dice     /// This will return any resource borrows and notify any current or future
2999fa70f025SJoel Dice     /// waiters that the task has completed.
task_complete( self, store: &mut StoreOpaque, guest_task: TableId<GuestTask>, result: Box<dyn Any + Send + Sync>, status: Status, ) -> Result<()>3000fa70f025SJoel Dice     fn task_complete(
3001fa70f025SJoel Dice         self,
30027e39c25eSJoel Dice         store: &mut StoreOpaque,
3003fa70f025SJoel Dice         guest_task: TableId<GuestTask>,
3004fa70f025SJoel Dice         result: Box<dyn Any + Send + Sync>,
3005fa70f025SJoel Dice         status: Status,
3006fa70f025SJoel Dice     ) -> Result<()> {
30073764e757SAlex Crichton         store
30083764e757SAlex Crichton             .component_resource_tables(Some(self))
30093764e757SAlex Crichton             .validate_scope_exit()?;
3010fa70f025SJoel Dice 
30117e39c25eSJoel Dice         let state = store.concurrent_state_mut();
3012fa70f025SJoel Dice         let task = state.get_mut(guest_task)?;
3013fa70f025SJoel Dice 
3014fa70f025SJoel Dice         if let Caller::Host { tx, .. } = &mut task.caller {
3015fa70f025SJoel Dice             if let Some(tx) = tx.take() {
3016fa70f025SJoel Dice                 _ = tx.send(result);
3017fa70f025SJoel Dice             }
3018fa70f025SJoel Dice         } else {
3019fa70f025SJoel Dice             task.result = Some(result);
3020fa70f025SJoel Dice             Waitable::Guest(guest_task).set_event(state, Some(Event::Subtask { status }))?;
3021fa70f025SJoel Dice         }
3022fa70f025SJoel Dice 
3023fa70f025SJoel Dice         Ok(())
3024b221fca7SJoel Dice     }
3025b221fca7SJoel Dice 
30267e39c25eSJoel Dice     /// Implements the `waitable-set.new` intrinsic.
waitable_set_new( self, store: &mut StoreOpaque, caller_instance: RuntimeComponentInstanceIndex, ) -> Result<u32>30277e39c25eSJoel Dice     pub(crate) fn waitable_set_new(
30287e39c25eSJoel Dice         self,
30297e39c25eSJoel Dice         store: &mut StoreOpaque,
30307e39c25eSJoel Dice         caller_instance: RuntimeComponentInstanceIndex,
30317e39c25eSJoel Dice     ) -> Result<u32> {
30327e39c25eSJoel Dice         let set = store.concurrent_state_mut().push(WaitableSet::default())?;
3033cb97ae85SJoel Dice         let handle = store
303457f899c4SAlex Crichton             .instance_state(RuntimeInstance {
3035cb97ae85SJoel Dice                 instance: self.id().instance(),
3036cb97ae85SJoel Dice                 index: caller_instance,
3037cb97ae85SJoel Dice             })
303857f899c4SAlex Crichton             .handle_table()
30397e39c25eSJoel Dice             .waitable_set_insert(set.rep())?;
30407e39c25eSJoel Dice         log::trace!("new waitable set {set:?} (handle {handle})");
30417e39c25eSJoel Dice         Ok(handle)
30427e39c25eSJoel Dice     }
30437e39c25eSJoel Dice 
30447e39c25eSJoel Dice     /// Implements the `waitable-set.drop` intrinsic.
waitable_set_drop( self, store: &mut StoreOpaque, caller_instance: RuntimeComponentInstanceIndex, set: u32, ) -> Result<()>30457e39c25eSJoel Dice     pub(crate) fn waitable_set_drop(
30467e39c25eSJoel Dice         self,
30477e39c25eSJoel Dice         store: &mut StoreOpaque,
30487e39c25eSJoel Dice         caller_instance: RuntimeComponentInstanceIndex,
30497e39c25eSJoel Dice         set: u32,
30507e39c25eSJoel Dice     ) -> Result<()> {
3051cb97ae85SJoel Dice         let rep = store
305257f899c4SAlex Crichton             .instance_state(RuntimeInstance {
3053cb97ae85SJoel Dice                 instance: self.id().instance(),
3054cb97ae85SJoel Dice                 index: caller_instance,
3055cb97ae85SJoel Dice             })
305657f899c4SAlex Crichton             .handle_table()
3057cb97ae85SJoel Dice             .waitable_set_remove(set)?;
30587e39c25eSJoel Dice 
30597e39c25eSJoel Dice         log::trace!("drop waitable set {rep} (handle {set})");
30607e39c25eSJoel Dice 
30617e39c25eSJoel Dice         let set = store
30627e39c25eSJoel Dice             .concurrent_state_mut()
30637e39c25eSJoel Dice             .delete(TableId::<WaitableSet>::new(rep))?;
30647e39c25eSJoel Dice 
30657e39c25eSJoel Dice         if !set.waiting.is_empty() {
3066da093747SAlex Crichton             bail!(Trap::WaitableSetDropHasWaiters);
30677e39c25eSJoel Dice         }
30687e39c25eSJoel Dice 
30697e39c25eSJoel Dice         Ok(())
30707e39c25eSJoel Dice     }
30717e39c25eSJoel Dice 
30727e39c25eSJoel Dice     /// Implements the `waitable.join` intrinsic.
waitable_join( self, store: &mut StoreOpaque, caller_instance: RuntimeComponentInstanceIndex, waitable_handle: u32, set_handle: u32, ) -> Result<()>30737e39c25eSJoel Dice     pub(crate) fn waitable_join(
30747e39c25eSJoel Dice         self,
30757e39c25eSJoel Dice         store: &mut StoreOpaque,
30767e39c25eSJoel Dice         caller_instance: RuntimeComponentInstanceIndex,
30777e39c25eSJoel Dice         waitable_handle: u32,
30787e39c25eSJoel Dice         set_handle: u32,
30797e39c25eSJoel Dice     ) -> Result<()> {
30807e39c25eSJoel Dice         let mut instance = self.id().get_mut(store);
30817e39c25eSJoel Dice         let waitable =
30827e39c25eSJoel Dice             Waitable::from_instance(instance.as_mut(), caller_instance, waitable_handle)?;
30837e39c25eSJoel Dice 
30847e39c25eSJoel Dice         let set = if set_handle == 0 {
30857e39c25eSJoel Dice             None
30867e39c25eSJoel Dice         } else {
3087cb97ae85SJoel Dice             let set = instance.instance_states().0[caller_instance]
3088cb97ae85SJoel Dice                 .handle_table()
3089cb97ae85SJoel Dice                 .waitable_set_rep(set_handle)?;
30907e39c25eSJoel Dice 
30917e39c25eSJoel Dice             Some(TableId::<WaitableSet>::new(set))
30927e39c25eSJoel Dice         };
30937e39c25eSJoel Dice 
30947e39c25eSJoel Dice         log::trace!(
30957e39c25eSJoel Dice             "waitable {waitable:?} (handle {waitable_handle}) join set {set:?} (handle {set_handle})",
30967e39c25eSJoel Dice         );
30977e39c25eSJoel Dice 
30987e39c25eSJoel Dice         waitable.join(store.concurrent_state_mut(), set)
30997e39c25eSJoel Dice     }
31007e39c25eSJoel Dice 
31017e39c25eSJoel Dice     /// Implements the `subtask.drop` intrinsic.
subtask_drop( self, store: &mut StoreOpaque, caller_instance: RuntimeComponentInstanceIndex, task_id: u32, ) -> Result<()>31027e39c25eSJoel Dice     pub(crate) fn subtask_drop(
31037e39c25eSJoel Dice         self,
31047e39c25eSJoel Dice         store: &mut StoreOpaque,
31057e39c25eSJoel Dice         caller_instance: RuntimeComponentInstanceIndex,
31067e39c25eSJoel Dice         task_id: u32,
31077e39c25eSJoel Dice     ) -> Result<()> {
31087e39c25eSJoel Dice         self.waitable_join(store, caller_instance, task_id, 0)?;
31097e39c25eSJoel Dice 
3110cb97ae85SJoel Dice         let (rep, is_host) = store
311157f899c4SAlex Crichton             .instance_state(RuntimeInstance {
3112cb97ae85SJoel Dice                 instance: self.id().instance(),
3113cb97ae85SJoel Dice                 index: caller_instance,
3114cb97ae85SJoel Dice             })
311557f899c4SAlex Crichton             .handle_table()
3116cb97ae85SJoel Dice             .subtask_remove(task_id)?;
31177e39c25eSJoel Dice 
31187e39c25eSJoel Dice         let concurrent_state = store.concurrent_state_mut();
31193764e757SAlex Crichton         let (waitable, expected_caller, delete) = if is_host {
31207e39c25eSJoel Dice             let id = TableId::<HostTask>::new(rep);
31217e39c25eSJoel Dice             let task = concurrent_state.get_mut(id)?;
3122065baac4SAlex Crichton             match &task.state {
3123da093747SAlex Crichton                 HostTaskState::CalleeRunning(_) => bail!(Trap::SubtaskDropNotResolved),
3124e8cb8751SAlex Crichton                 HostTaskState::CalleeDone { .. } => {}
3125da093747SAlex Crichton                 HostTaskState::CalleeStarted | HostTaskState::CalleeFinished(_) => {
3126da093747SAlex Crichton                     bail_bug!("invalid state for callee in `subtask.drop`")
3127da093747SAlex Crichton                 }
3128065baac4SAlex Crichton             }
31293764e757SAlex Crichton             (Waitable::Host(id), task.caller, true)
31307e39c25eSJoel Dice         } else {
31317e39c25eSJoel Dice             let id = TableId::<GuestTask>::new(rep);
31327e39c25eSJoel Dice             let task = concurrent_state.get_mut(id)?;
31337e39c25eSJoel Dice             if task.lift_result.is_some() {
3134da093747SAlex Crichton                 bail!(Trap::SubtaskDropNotResolved);
31357e39c25eSJoel Dice             }
31363764e757SAlex Crichton             if let Caller::Guest { thread } = task.caller {
3137b271e452SJoel Dice                 (
3138b271e452SJoel Dice                     Waitable::Guest(id),
31393764e757SAlex Crichton                     thread,
3140b271e452SJoel Dice                     concurrent_state.get_mut(id)?.exited,
3141b271e452SJoel Dice                 )
31427e39c25eSJoel Dice             } else {
3143da093747SAlex Crichton                 bail_bug!("expected guest caller for `subtask.drop`")
31447e39c25eSJoel Dice             }
31457e39c25eSJoel Dice         };
31467e39c25eSJoel Dice 
31477e39c25eSJoel Dice         waitable.common(concurrent_state)?.handle = None;
31487e39c25eSJoel Dice 
3149da093747SAlex Crichton         // If this subtask has an event that means that the terminal status of
3150da093747SAlex Crichton         // this subtask wasn't yet received so it can't be dropped yet.
31517e39c25eSJoel Dice         if waitable.take_event(concurrent_state)?.is_some() {
3152da093747SAlex Crichton             bail!(Trap::SubtaskDropNotResolved);
31537e39c25eSJoel Dice         }
31547e39c25eSJoel Dice 
31557e39c25eSJoel Dice         if delete {
31567e39c25eSJoel Dice             waitable.delete_from(concurrent_state)?;
31577e39c25eSJoel Dice         }
31587e39c25eSJoel Dice 
31597e39c25eSJoel Dice         // Since waitables can neither be passed between instances nor forged,
31607e39c25eSJoel Dice         // this should never fail unless there's a bug in Wasmtime, but we check
31617e39c25eSJoel Dice         // here to be sure:
3162da093747SAlex Crichton         debug_assert_eq!(expected_caller, concurrent_state.current_guest_thread()?);
31637e39c25eSJoel Dice         log::trace!("subtask_drop {waitable:?} (handle {task_id})");
31647e39c25eSJoel Dice         Ok(())
31657e39c25eSJoel Dice     }
31667e39c25eSJoel Dice 
3167b221fca7SJoel Dice     /// Implements the `waitable-set.wait` intrinsic.
waitable_set_wait( self, store: &mut StoreOpaque, options: OptionsIndex, set: u32, payload: u32, ) -> Result<u32>3168b221fca7SJoel Dice     pub(crate) fn waitable_set_wait(
3169b221fca7SJoel Dice         self,
31707e39c25eSJoel Dice         store: &mut StoreOpaque,
3171815c10deSAlex Crichton         options: OptionsIndex,
3172b221fca7SJoel Dice         set: u32,
3173b221fca7SJoel Dice         payload: u32,
3174b221fca7SJoel Dice     ) -> Result<u32> {
31758992b99bSJoel Dice         if !self.options(store, options).async_ {
31768992b99bSJoel Dice             // The caller may only call `waitable-set.wait` from an async task
31778992b99bSJoel Dice             // (i.e. a task created via a call to an async export).
31788992b99bSJoel Dice             // Otherwise, we'll trap.
3179fae9e6afSJoel Dice             store.check_blocking()?;
31808992b99bSJoel Dice         }
31818992b99bSJoel Dice 
31827e39c25eSJoel Dice         let &CanonicalOptions {
31837e39c25eSJoel Dice             cancellable,
31847e39c25eSJoel Dice             instance: caller_instance,
31857e39c25eSJoel Dice             ..
31867e39c25eSJoel Dice         } = &self.id().get(store).component().env_component().options[options];
3187cb97ae85SJoel Dice         let rep = store
318857f899c4SAlex Crichton             .instance_state(RuntimeInstance {
3189cb97ae85SJoel Dice                 instance: self.id().instance(),
3190cb97ae85SJoel Dice                 index: caller_instance,
3191cb97ae85SJoel Dice             })
319257f899c4SAlex Crichton             .handle_table()
3193cb97ae85SJoel Dice             .waitable_set_rep(set)?;
3194fa70f025SJoel Dice 
3195fa70f025SJoel Dice         self.waitable_check(
3196fa70f025SJoel Dice             store,
319788b630dbSAlex Crichton             cancellable,
319834ba273bSJoel Dice             WaitableCheck::Wait,
319934ba273bSJoel Dice             WaitableCheckParams {
3200fa70f025SJoel Dice                 set: TableId::new(rep),
3201815c10deSAlex Crichton                 options,
3202815c10deSAlex Crichton                 payload,
320334ba273bSJoel Dice             },
3204fa70f025SJoel Dice         )
3205b221fca7SJoel Dice     }
3206b221fca7SJoel Dice 
3207b221fca7SJoel Dice     /// Implements the `waitable-set.poll` intrinsic.
waitable_set_poll( self, store: &mut StoreOpaque, options: OptionsIndex, set: u32, payload: u32, ) -> Result<u32>3208b221fca7SJoel Dice     pub(crate) fn waitable_set_poll(
3209b221fca7SJoel Dice         self,
32107e39c25eSJoel Dice         store: &mut StoreOpaque,
3211815c10deSAlex Crichton         options: OptionsIndex,
3212b221fca7SJoel Dice         set: u32,
3213b221fca7SJoel Dice         payload: u32,
3214b221fca7SJoel Dice     ) -> Result<u32> {
32157e39c25eSJoel Dice         let &CanonicalOptions {
32167e39c25eSJoel Dice             cancellable,
32177e39c25eSJoel Dice             instance: caller_instance,
32187e39c25eSJoel Dice             ..
32197e39c25eSJoel Dice         } = &self.id().get(store).component().env_component().options[options];
3220cb97ae85SJoel Dice         let rep = store
322157f899c4SAlex Crichton             .instance_state(RuntimeInstance {
3222cb97ae85SJoel Dice                 instance: self.id().instance(),
3223cb97ae85SJoel Dice                 index: caller_instance,
3224cb97ae85SJoel Dice             })
322557f899c4SAlex Crichton             .handle_table()
3226cb97ae85SJoel Dice             .waitable_set_rep(set)?;
3227fa70f025SJoel Dice 
3228fa70f025SJoel Dice         self.waitable_check(
3229fa70f025SJoel Dice             store,
323088b630dbSAlex Crichton             cancellable,
323134ba273bSJoel Dice             WaitableCheck::Poll,
323234ba273bSJoel Dice             WaitableCheckParams {
3233fa70f025SJoel Dice                 set: TableId::new(rep),
3234815c10deSAlex Crichton                 options,
3235815c10deSAlex Crichton                 payload,
323634ba273bSJoel Dice             },
3237fa70f025SJoel Dice         )
3238b221fca7SJoel Dice     }
3239b221fca7SJoel Dice 
3240e06fbf70SSy Brand     /// Implements the `thread.index` intrinsic.
thread_index(&self, store: &mut dyn VMStore) -> Result<u32>3241e06fbf70SSy Brand     pub(crate) fn thread_index(&self, store: &mut dyn VMStore) -> Result<u32> {
3242da093747SAlex Crichton         let thread_id = store.concurrent_state_mut().current_guest_thread()?.thread;
3243da093747SAlex Crichton         match store
3244e06fbf70SSy Brand             .concurrent_state_mut()
3245e06fbf70SSy Brand             .get_mut(thread_id)?
3246e06fbf70SSy Brand             .instance_rep
3247da093747SAlex Crichton         {
3248da093747SAlex Crichton             Some(r) => Ok(r),
3249da093747SAlex Crichton             None => bail_bug!("thread should have instance_rep by now"),
3250da093747SAlex Crichton         }
3251e06fbf70SSy Brand     }
3252e06fbf70SSy Brand 
3253020727d0SAlex Crichton     /// Implements the `thread.new-indirect` intrinsic.
thread_new_indirect<T: 'static>( self, mut store: StoreContextMut<T>, runtime_instance: RuntimeComponentInstanceIndex, _func_ty_idx: TypeFuncIndex, start_func_table_idx: RuntimeTableIndex, start_func_idx: u32, context: i32, ) -> Result<u32>3254e06fbf70SSy Brand     pub(crate) fn thread_new_indirect<T: 'static>(
3255e06fbf70SSy Brand         self,
3256e06fbf70SSy Brand         mut store: StoreContextMut<T>,
3257e06fbf70SSy Brand         runtime_instance: RuntimeComponentInstanceIndex,
3258e06fbf70SSy Brand         _func_ty_idx: TypeFuncIndex, // currently unused
3259e06fbf70SSy Brand         start_func_table_idx: RuntimeTableIndex,
3260e06fbf70SSy Brand         start_func_idx: u32,
3261e06fbf70SSy Brand         context: i32,
3262e06fbf70SSy Brand     ) -> Result<u32> {
3263e06fbf70SSy Brand         log::trace!("creating new thread");
3264e06fbf70SSy Brand 
3265e06fbf70SSy Brand         let start_func_ty = FuncType::new(store.engine(), [ValType::I32], []);
326699ecf728SChris Fallin         let (instance, registry) = self.id().get_mut_and_registry(store.0);
3267e06fbf70SSy Brand         let callee = instance
326899ecf728SChris Fallin             .index_runtime_func_table(registry, start_func_table_idx, start_func_idx as u64)?
3269da093747SAlex Crichton             .ok_or_else(|| Trap::ThreadNewIndirectUninitialized)?;
3270e06fbf70SSy Brand         if callee.type_index(store.0) != start_func_ty.type_index() {
3271da093747SAlex Crichton             bail!(Trap::ThreadNewIndirectInvalidType);
3272e06fbf70SSy Brand         }
3273e06fbf70SSy Brand 
3274e06fbf70SSy Brand         let token = StoreToken::new(store.as_context_mut());
3275e06fbf70SSy Brand         let start_func = Box::new(
3276e06fbf70SSy Brand             move |store: &mut dyn VMStore, guest_thread: QualifiedThreadId| -> Result<()> {
3277da093747SAlex Crichton                 let old_thread = store.set_thread(guest_thread)?;
3278e06fbf70SSy Brand                 log::trace!(
3279e06fbf70SSy Brand                     "thread start: replaced {old_thread:?} with {guest_thread:?} as current thread"
3280e06fbf70SSy Brand                 );
3281e06fbf70SSy Brand 
3282e06fbf70SSy Brand                 let mut store = token.as_context_mut(store);
3283e06fbf70SSy Brand                 let mut params = [ValRaw::i32(context)];
3284e06fbf70SSy Brand                 // Use call_unchecked rather than call or call_async, as we don't want to run the function
3285e06fbf70SSy Brand                 // on a separate fiber if we're running in an async store.
3286e06fbf70SSy Brand                 unsafe { callee.call_unchecked(store.as_context_mut(), &mut params)? };
3287e06fbf70SSy Brand 
3288e06fbf70SSy Brand                 self.cleanup_thread(store.0, guest_thread, runtime_instance)?;
3289e06fbf70SSy Brand                 log::trace!("explicit thread {guest_thread:?} completed");
3290e06fbf70SSy Brand                 let state = store.0.concurrent_state_mut();
3291e06fbf70SSy Brand                 let task = state.get_mut(guest_thread.task)?;
3292e06fbf70SSy Brand                 if task.threads.is_empty() && !task.returned_or_cancelled() {
3293e06fbf70SSy Brand                     bail!(Trap::NoAsyncResult);
3294e06fbf70SSy Brand                 }
3295da093747SAlex Crichton                 store.0.set_thread(old_thread)?;
3296fae9e6afSJoel Dice                 let state = store.0.concurrent_state_mut();
3297da093747SAlex Crichton                 if let Some(t) = old_thread.guest() {
3298da093747SAlex Crichton                     state.get_mut(t.thread)?.state = GuestThreadState::Running;
3299da093747SAlex Crichton                 }
3300e06fbf70SSy Brand                 if state.get_mut(guest_thread.task)?.ready_to_delete() {
3301e06fbf70SSy Brand                     Waitable::Guest(guest_thread.task).delete_from(state)?;
3302e06fbf70SSy Brand                 }
3303e06fbf70SSy Brand                 log::trace!("thread start: restored {old_thread:?} as current thread");
3304e06fbf70SSy Brand 
3305e06fbf70SSy Brand                 Ok(())
3306e06fbf70SSy Brand             },
3307e06fbf70SSy Brand         );
3308e06fbf70SSy Brand 
3309e06fbf70SSy Brand         let state = store.0.concurrent_state_mut();
3310da093747SAlex Crichton         let current_thread = state.current_guest_thread()?;
3311e06fbf70SSy Brand         let parent_task = current_thread.task;
3312e06fbf70SSy Brand 
331335887491SSy Brand         let new_thread = GuestThread::new_explicit(state, parent_task, start_func)?;
3314e06fbf70SSy Brand         let thread_id = state.push(new_thread)?;
3315e06fbf70SSy Brand         state.get_mut(parent_task)?.threads.insert(thread_id);
3316e06fbf70SSy Brand 
3317e06fbf70SSy Brand         log::trace!("new thread with id {thread_id:?} created");
3318e06fbf70SSy Brand 
3319e06fbf70SSy Brand         self.add_guest_thread_to_instance_table(thread_id, store.0, runtime_instance)
3320e06fbf70SSy Brand     }
3321e06fbf70SSy Brand 
resume_thread( self, store: &mut StoreOpaque, runtime_instance: RuntimeComponentInstanceIndex, thread_idx: u32, high_priority: bool, allow_ready: bool, ) -> Result<()>3322d2fbd2deSAlex Crichton     pub(crate) fn resume_thread(
3323e06fbf70SSy Brand         self,
3324e06fbf70SSy Brand         store: &mut StoreOpaque,
3325e06fbf70SSy Brand         runtime_instance: RuntimeComponentInstanceIndex,
3326e06fbf70SSy Brand         thread_idx: u32,
3327e06fbf70SSy Brand         high_priority: bool,
3328d2fbd2deSAlex Crichton         allow_ready: bool,
3329e06fbf70SSy Brand     ) -> Result<()> {
3330e06fbf70SSy Brand         let thread_id =
3331e06fbf70SSy Brand             GuestThread::from_instance(self.id().get_mut(store), runtime_instance, thread_idx)?;
3332e06fbf70SSy Brand         let state = store.concurrent_state_mut();
3333e06fbf70SSy Brand         let guest_thread = QualifiedThreadId::qualify(state, thread_id)?;
3334e06fbf70SSy Brand         let thread = state.get_mut(guest_thread.thread)?;
3335e06fbf70SSy Brand 
3336e06fbf70SSy Brand         match mem::replace(&mut thread.state, GuestThreadState::Running) {
3337e06fbf70SSy Brand             GuestThreadState::NotStartedExplicit(start_func) => {
3338e06fbf70SSy Brand                 log::trace!("starting thread {guest_thread:?}");
3339d2fbd2deSAlex Crichton                 let guest_call = WorkItem::GuestCall(
3340d2fbd2deSAlex Crichton                     runtime_instance,
3341d2fbd2deSAlex Crichton                     GuestCall {
3342e06fbf70SSy Brand                         thread: guest_thread,
3343e06fbf70SSy Brand                         kind: GuestCallKind::StartExplicit(Box::new(move |store| {
3344e06fbf70SSy Brand                             start_func(store, guest_thread)
3345e06fbf70SSy Brand                         })),
3346d2fbd2deSAlex Crichton                     },
3347d2fbd2deSAlex Crichton                 );
3348e06fbf70SSy Brand                 store
3349e06fbf70SSy Brand                     .concurrent_state_mut()
3350e06fbf70SSy Brand                     .push_work_item(guest_call, high_priority);
3351e06fbf70SSy Brand             }
3352e06fbf70SSy Brand             GuestThreadState::Suspended(fiber) => {
3353e06fbf70SSy Brand                 log::trace!("resuming thread {thread_id:?} that was suspended");
3354e06fbf70SSy Brand                 store
3355e06fbf70SSy Brand                     .concurrent_state_mut()
3356e06fbf70SSy Brand                     .push_work_item(WorkItem::ResumeFiber(fiber), high_priority);
3357e06fbf70SSy Brand             }
3358d2fbd2deSAlex Crichton             GuestThreadState::Ready(fiber) if allow_ready => {
3359d2fbd2deSAlex Crichton                 log::trace!("resuming thread {thread_id:?} that was ready");
3360d2fbd2deSAlex Crichton                 thread.state = GuestThreadState::Ready(fiber);
3361d2fbd2deSAlex Crichton                 store
3362d2fbd2deSAlex Crichton                     .concurrent_state_mut()
3363d2fbd2deSAlex Crichton                     .promote_thread_work_item(guest_thread);
3364d2fbd2deSAlex Crichton             }
3365d2fbd2deSAlex Crichton             other => {
3366d2fbd2deSAlex Crichton                 thread.state = other;
3367da093747SAlex Crichton                 bail!(Trap::CannotResumeThread);
3368e06fbf70SSy Brand             }
3369e06fbf70SSy Brand         }
3370e06fbf70SSy Brand         Ok(())
3371e06fbf70SSy Brand     }
3372e06fbf70SSy Brand 
add_guest_thread_to_instance_table( self, thread_id: TableId<GuestThread>, store: &mut StoreOpaque, runtime_instance: RuntimeComponentInstanceIndex, ) -> Result<u32>3373e06fbf70SSy Brand     fn add_guest_thread_to_instance_table(
3374e06fbf70SSy Brand         self,
3375e06fbf70SSy Brand         thread_id: TableId<GuestThread>,
3376e06fbf70SSy Brand         store: &mut StoreOpaque,
3377e06fbf70SSy Brand         runtime_instance: RuntimeComponentInstanceIndex,
3378e06fbf70SSy Brand     ) -> Result<u32> {
3379cb97ae85SJoel Dice         let guest_id = store
338057f899c4SAlex Crichton             .instance_state(RuntimeInstance {
3381cb97ae85SJoel Dice                 instance: self.id().instance(),
3382cb97ae85SJoel Dice                 index: runtime_instance,
3383cb97ae85SJoel Dice             })
338457f899c4SAlex Crichton             .thread_handle_table()
3385e06fbf70SSy Brand             .guest_thread_insert(thread_id.rep())?;
3386e06fbf70SSy Brand         store
3387e06fbf70SSy Brand             .concurrent_state_mut()
3388e06fbf70SSy Brand             .get_mut(thread_id)?
3389e06fbf70SSy Brand             .instance_rep = Some(guest_id);
3390e06fbf70SSy Brand         Ok(guest_id)
3391e06fbf70SSy Brand     }
3392e06fbf70SSy Brand 
3393d2fbd2deSAlex Crichton     /// Helper function for the `thread.yield`, `thread.yield-to-suspended`, `thread.suspend`,
3394d2fbd2deSAlex Crichton     /// `thread.suspend-to`, and `thread.suspend-to-suspended` intrinsics.
suspension_intrinsic( self, store: &mut StoreOpaque, caller: RuntimeComponentInstanceIndex, cancellable: bool, yielding: bool, to_thread: SuspensionTarget, ) -> Result<WaitResult>3395e06fbf70SSy Brand     pub(crate) fn suspension_intrinsic(
33966751ea79SJoel Dice         self,
33977e39c25eSJoel Dice         store: &mut StoreOpaque,
33986751ea79SJoel Dice         caller: RuntimeComponentInstanceIndex,
33996751ea79SJoel Dice         cancellable: bool,
3400e06fbf70SSy Brand         yielding: bool,
3401d2fbd2deSAlex Crichton         to_thread: SuspensionTarget,
3402e06fbf70SSy Brand     ) -> Result<WaitResult> {
3403da093747SAlex Crichton         let guest_thread = store.concurrent_state_mut().current_guest_thread()?;
34048992b99bSJoel Dice         if to_thread.is_none() {
34058992b99bSJoel Dice             let state = store.concurrent_state_mut();
34068992b99bSJoel Dice             if yielding {
34078992b99bSJoel Dice                 // This is a `thread.yield` call
3408da093747SAlex Crichton                 if !state.may_block(guest_thread.task)? {
3409d2fbd2deSAlex Crichton                     // In a non-blocking context, a `thread.yield` may trigger
3410d2fbd2deSAlex Crichton                     // other threads in the same component instance to run.
3411d2fbd2deSAlex Crichton                     if !state.promote_instance_local_thread_work_item(caller) {
3412d2fbd2deSAlex Crichton                         // No other threads are runnable, so just return
34138992b99bSJoel Dice                         return Ok(WaitResult::Completed);
34148992b99bSJoel Dice                     }
3415d2fbd2deSAlex Crichton                 }
34168992b99bSJoel Dice             } else {
34178992b99bSJoel Dice                 // The caller may only call `thread.suspend` from an async task
34188992b99bSJoel Dice                 // (i.e. a task created via a call to an async export).
34198992b99bSJoel Dice                 // Otherwise, we'll trap.
3420fae9e6afSJoel Dice                 store.check_blocking()?;
34218992b99bSJoel Dice             }
34228992b99bSJoel Dice         }
34238992b99bSJoel Dice 
3424e06fbf70SSy Brand         // There could be a pending cancellation from a previous uncancellable wait
3425da093747SAlex Crichton         if cancellable && store.concurrent_state_mut().take_pending_cancellation()? {
3426e06fbf70SSy Brand             return Ok(WaitResult::Cancelled);
3427fa70f025SJoel Dice         }
3428fa70f025SJoel Dice 
3429d2fbd2deSAlex Crichton         match to_thread {
3430d2fbd2deSAlex Crichton             SuspensionTarget::SomeSuspended(thread) => {
3431d2fbd2deSAlex Crichton                 self.resume_thread(store, caller, thread, true, false)?
3432d2fbd2deSAlex Crichton             }
3433d2fbd2deSAlex Crichton             SuspensionTarget::Some(thread) => {
3434d2fbd2deSAlex Crichton                 self.resume_thread(store, caller, thread, true, true)?
3435d2fbd2deSAlex Crichton             }
3436d2fbd2deSAlex Crichton             SuspensionTarget::None => { /* nothing to do */ }
3437e06fbf70SSy Brand         }
3438e06fbf70SSy Brand 
3439e06fbf70SSy Brand         let reason = if yielding {
3440e06fbf70SSy Brand             SuspendReason::Yielding {
3441e06fbf70SSy Brand                 thread: guest_thread,
3442fc4020baSSy Brand                 // Tell `StoreOpaque::suspend` it's okay to suspend here since
3443d2fbd2deSAlex Crichton                 // we're handling a `thread.yield-to-suspended` call; otherwise it would
3444fc4020baSSy Brand                 // panic if we called it in a non-blocking context.
3445fc4020baSSy Brand                 skip_may_block_check: to_thread.is_some(),
3446e06fbf70SSy Brand             }
3447e06fbf70SSy Brand         } else {
3448e06fbf70SSy Brand             SuspendReason::ExplicitlySuspending {
3449e06fbf70SSy Brand                 thread: guest_thread,
34508992b99bSJoel Dice                 // Tell `StoreOpaque::suspend` it's okay to suspend here since
3451d2fbd2deSAlex Crichton                 // we're handling a `thread.suspend-to(-suspended)` call; otherwise it would
34528992b99bSJoel Dice                 // panic if we called it in a non-blocking context.
34538992b99bSJoel Dice                 skip_may_block_check: to_thread.is_some(),
3454e06fbf70SSy Brand             }
3455e06fbf70SSy Brand         };
3456e06fbf70SSy Brand 
3457e06fbf70SSy Brand         store.suspend(reason)?;
3458e06fbf70SSy Brand 
3459da093747SAlex Crichton         if cancellable && store.concurrent_state_mut().take_pending_cancellation()? {
3460e06fbf70SSy Brand             Ok(WaitResult::Cancelled)
3461e06fbf70SSy Brand         } else {
3462e06fbf70SSy Brand             Ok(WaitResult::Completed)
3463e06fbf70SSy Brand         }
3464e06fbf70SSy Brand     }
3465e06fbf70SSy Brand 
3466e06fbf70SSy Brand     /// Helper function for the `waitable-set.wait` and `waitable-set.poll` intrinsics.
waitable_check( self, store: &mut StoreOpaque, cancellable: bool, check: WaitableCheck, params: WaitableCheckParams, ) -> Result<u32>3467fa70f025SJoel Dice     fn waitable_check(
3468fa70f025SJoel Dice         self,
34697e39c25eSJoel Dice         store: &mut StoreOpaque,
347088b630dbSAlex Crichton         cancellable: bool,
3471fa70f025SJoel Dice         check: WaitableCheck,
347234ba273bSJoel Dice         params: WaitableCheckParams,
3473fa70f025SJoel Dice     ) -> Result<u32> {
3474da093747SAlex Crichton         let guest_thread = store.concurrent_state_mut().current_guest_thread()?;
3475fa70f025SJoel Dice 
347634ba273bSJoel Dice         log::trace!("waitable check for {guest_thread:?}; set {:?}", params.set);
3477fa70f025SJoel Dice 
34787e39c25eSJoel Dice         let state = store.concurrent_state_mut();
3479e06fbf70SSy Brand         let task = state.get_mut(guest_thread.task)?;
3480fa70f025SJoel Dice 
3481fa70f025SJoel Dice         // If we're waiting, and there are no events immediately available,
3482fa70f025SJoel Dice         // suspend the fiber until that changes.
348334ba273bSJoel Dice         match &check {
348434ba273bSJoel Dice             WaitableCheck::Wait => {
348534ba273bSJoel Dice                 let set = params.set;
3486fa70f025SJoel Dice 
3487db1aee0fSJoel Dice                 if (task.event.is_none()
3488db1aee0fSJoel Dice                     || (matches!(task.event, Some(Event::Cancelled)) && !cancellable))
3489db1aee0fSJoel Dice                     && state.get_mut(set)?.ready.is_empty()
3490db1aee0fSJoel Dice                 {
3491db1aee0fSJoel Dice                     if cancellable {
3492e06fbf70SSy Brand                         let old = state
3493e06fbf70SSy Brand                             .get_mut(guest_thread.thread)?
3494e06fbf70SSy Brand                             .wake_on_cancel
3495e06fbf70SSy Brand                             .replace(set);
3496da093747SAlex Crichton                         if !old.is_none() {
3497da093747SAlex Crichton                             bail_bug!("thread unexpectedly in a prior wake_on_cancel set");
3498da093747SAlex Crichton                         }
3499db1aee0fSJoel Dice                     }
3500fa70f025SJoel Dice 
35017e39c25eSJoel Dice                     store.suspend(SuspendReason::Waiting {
3502fa70f025SJoel Dice                         set,
3503e06fbf70SSy Brand                         thread: guest_thread,
35048992b99bSJoel Dice                         skip_may_block_check: false,
35057e39c25eSJoel Dice                     })?;
3506fa70f025SJoel Dice                 }
3507fa70f025SJoel Dice             }
350834ba273bSJoel Dice             WaitableCheck::Poll => {}
350934ba273bSJoel Dice         }
3510fa70f025SJoel Dice 
351134ba273bSJoel Dice         log::trace!(
351234ba273bSJoel Dice             "waitable check for {guest_thread:?}; set {:?}, part two",
351334ba273bSJoel Dice             params.set
351434ba273bSJoel Dice         );
3515fa70f025SJoel Dice 
3516fa70f025SJoel Dice         // Deliver any pending events to the guest and return.
351734ba273bSJoel Dice         let event = self.get_event(store, guest_thread.task, Some(params.set), cancellable)?;
3518fa70f025SJoel Dice 
351934ba273bSJoel Dice         let (ordinal, handle, result) = match &check {
352034ba273bSJoel Dice             WaitableCheck::Wait => {
3521da093747SAlex Crichton                 let (event, waitable) = match event {
3522da093747SAlex Crichton                     Some(p) => p,
3523da093747SAlex Crichton                     None => bail_bug!("event expected to be present"),
3524da093747SAlex Crichton                 };
3525fa70f025SJoel Dice                 let handle = waitable.map(|(_, v)| v).unwrap_or(0);
3526fa70f025SJoel Dice                 let (ordinal, result) = event.parts();
3527fa70f025SJoel Dice                 (ordinal, handle, result)
352834ba273bSJoel Dice             }
352934ba273bSJoel Dice             WaitableCheck::Poll => {
3530fa70f025SJoel Dice                 if let Some((event, waitable)) = event {
3531fa70f025SJoel Dice                     let handle = waitable.map(|(_, v)| v).unwrap_or(0);
3532fa70f025SJoel Dice                     let (ordinal, result) = event.parts();
3533fa70f025SJoel Dice                     (ordinal, handle, result)
3534fa70f025SJoel Dice                 } else {
3535fa70f025SJoel Dice                     log::trace!(
3536e06fbf70SSy Brand                         "no events ready to deliver via waitable-set.poll to {:?}; set {:?}",
3537e06fbf70SSy Brand                         guest_thread.task,
3538fa70f025SJoel Dice                         params.set
3539fa70f025SJoel Dice                     );
3540fa70f025SJoel Dice                     let (ordinal, result) = Event::None.parts();
3541fa70f025SJoel Dice                     (ordinal, 0, result)
3542fa70f025SJoel Dice                 }
354334ba273bSJoel Dice             }
3544fa70f025SJoel Dice         };
3545ec9b62abSAlex Crichton         let memory = self.options_memory_mut(store, params.options);
354605a711f6SAlex Crichton         let ptr = func::validate_inbounds_dynamic(
354705a711f6SAlex Crichton             &CanonicalAbiInfo::POINTER_PAIR,
354805a711f6SAlex Crichton             memory,
354905a711f6SAlex Crichton             &ValRaw::u32(params.payload),
355005a711f6SAlex Crichton         )?;
3551ec9b62abSAlex Crichton         memory[ptr + 0..][..4].copy_from_slice(&handle.to_le_bytes());
3552ec9b62abSAlex Crichton         memory[ptr + 4..][..4].copy_from_slice(&result.to_le_bytes());
3553fa70f025SJoel Dice         Ok(ordinal)
3554fa70f025SJoel Dice     }
3555b221fca7SJoel Dice 
3556b221fca7SJoel Dice     /// Implements the `subtask.cancel` intrinsic.
subtask_cancel( self, store: &mut StoreOpaque, caller_instance: RuntimeComponentInstanceIndex, async_: bool, task_id: u32, ) -> Result<u32>3557b221fca7SJoel Dice     pub(crate) fn subtask_cancel(
3558b221fca7SJoel Dice         self,
35597e39c25eSJoel Dice         store: &mut StoreOpaque,
3560b221fca7SJoel Dice         caller_instance: RuntimeComponentInstanceIndex,
3561b221fca7SJoel Dice         async_: bool,
3562b221fca7SJoel Dice         task_id: u32,
3563b221fca7SJoel Dice     ) -> Result<u32> {
35648992b99bSJoel Dice         if !async_ {
35658992b99bSJoel Dice             // The caller may only sync call `subtask.cancel` from an async task
35668992b99bSJoel Dice             // (i.e. a task created via a call to an async export).  Otherwise,
35678992b99bSJoel Dice             // we'll trap.
3568fae9e6afSJoel Dice             store.check_blocking()?;
35698992b99bSJoel Dice         }
35708992b99bSJoel Dice 
3571cb97ae85SJoel Dice         let (rep, is_host) = store
357257f899c4SAlex Crichton             .instance_state(RuntimeInstance {
3573cb97ae85SJoel Dice                 instance: self.id().instance(),
3574cb97ae85SJoel Dice                 index: caller_instance,
3575cb97ae85SJoel Dice             })
357657f899c4SAlex Crichton             .handle_table()
3577cb97ae85SJoel Dice             .subtask_rep(task_id)?;
35783764e757SAlex Crichton         let (waitable, expected_caller) = if is_host {
3579fa70f025SJoel Dice             let id = TableId::<HostTask>::new(rep);
3580fa70f025SJoel Dice             (
3581fa70f025SJoel Dice                 Waitable::Host(id),
35823764e757SAlex Crichton                 store.concurrent_state_mut().get_mut(id)?.caller,
3583fa70f025SJoel Dice             )
3584e8189549SJoel Dice         } else {
3585fa70f025SJoel Dice             let id = TableId::<GuestTask>::new(rep);
35863764e757SAlex Crichton             if let Caller::Guest { thread } = store.concurrent_state_mut().get_mut(id)?.caller {
35873764e757SAlex Crichton                 (Waitable::Guest(id), thread)
3588fa70f025SJoel Dice             } else {
3589da093747SAlex Crichton                 bail_bug!("expected guest caller for `subtask.cancel`")
3590fa70f025SJoel Dice             }
3591fa70f025SJoel Dice         };
3592fa70f025SJoel Dice         // Since waitables can neither be passed between instances nor forged,
3593fa70f025SJoel Dice         // this should never fail unless there's a bug in Wasmtime, but we check
3594fa70f025SJoel Dice         // here to be sure:
35953764e757SAlex Crichton         let concurrent_state = store.concurrent_state_mut();
3596da093747SAlex Crichton         debug_assert_eq!(expected_caller, concurrent_state.current_guest_thread()?);
3597fa70f025SJoel Dice 
3598fa70f025SJoel Dice         log::trace!("subtask_cancel {waitable:?} (handle {task_id})");
3599fa70f025SJoel Dice 
3600065baac4SAlex Crichton         let needs_block;
3601fa70f025SJoel Dice         if let Waitable::Host(host_task) = waitable {
3602065baac4SAlex Crichton             let state = &mut concurrent_state.get_mut(host_task)?.state;
3603e8cb8751SAlex Crichton             match mem::replace(state, HostTaskState::CalleeDone { cancelled: true }) {
3604065baac4SAlex Crichton                 // If the callee is still running, signal an abort is requested.
3605e8cb8751SAlex Crichton                 //
3606e8cb8751SAlex Crichton                 // After cancelling this falls through to block waiting for the
3607e8cb8751SAlex Crichton                 // host task to actually finish assuming that `async_` is false.
3608e8cb8751SAlex Crichton                 // This blocking behavior resolves the race of `handle.abort()`
3609e8cb8751SAlex Crichton                 // with the task actually getting cancelled or finishing.
3610e8cb8751SAlex Crichton                 HostTaskState::CalleeRunning(handle) => {
3611e8cb8751SAlex Crichton                     handle.abort();
3612e8cb8751SAlex Crichton                     needs_block = true;
3613e8cb8751SAlex Crichton                 }
3614065baac4SAlex Crichton 
3615065baac4SAlex Crichton                 // Cancellation was already requested, so fail as the task can't
3616065baac4SAlex Crichton                 // be cancelled twice.
3617e8cb8751SAlex Crichton                 HostTaskState::CalleeDone { cancelled } => {
3618e8cb8751SAlex Crichton                     if cancelled {
3619da093747SAlex Crichton                         bail!(Trap::SubtaskCancelAfterTerminal);
3620e8cb8751SAlex Crichton                     } else {
3621e8cb8751SAlex Crichton                         // The callee is already done so there's no need to
3622e8cb8751SAlex Crichton                         // block further for an event.
3623e8cb8751SAlex Crichton                         needs_block = false;
3624e8cb8751SAlex Crichton                     }
3625fa70f025SJoel Dice                 }
3626065baac4SAlex Crichton 
3627065baac4SAlex Crichton                 // These states should not be possible for a subtask that's
3628da093747SAlex Crichton                 // visible from the guest, so trap here.
3629da093747SAlex Crichton                 HostTaskState::CalleeStarted | HostTaskState::CalleeFinished(_) => {
3630da093747SAlex Crichton                     bail_bug!("invalid states for host callee")
3631da093747SAlex Crichton                 }
3632065baac4SAlex Crichton             }
3633fa70f025SJoel Dice         } else {
3634da093747SAlex Crichton             let caller = concurrent_state.current_guest_thread()?;
3635fa70f025SJoel Dice             let guest_task = TableId::<GuestTask>::new(rep);
3636fa70f025SJoel Dice             let task = concurrent_state.get_mut(guest_task)?;
3637e06fbf70SSy Brand             if !task.already_lowered_parameters() {
3638fd03e30bSJoel Dice                 // The task is in a `starting` state, meaning it hasn't run at
3639fd03e30bSJoel Dice                 // all yet.  Here we update its fields to indicate that it is
3640fd03e30bSJoel Dice                 // ready to delete immediately once `subtask.drop` is called.
3641fa70f025SJoel Dice                 task.lower_params = None;
3642fa70f025SJoel Dice                 task.lift_result = None;
3643fd03e30bSJoel Dice                 task.exited = true;
3644fd03e30bSJoel Dice 
3645fd03e30bSJoel Dice                 let instance = task.instance;
3646fd03e30bSJoel Dice 
3647fd03e30bSJoel Dice                 assert_eq!(1, task.threads.len());
3648fd03e30bSJoel Dice                 let thread = mem::take(&mut task.threads).into_iter().next().unwrap();
3649fd03e30bSJoel Dice                 let concurrent_state = store.concurrent_state_mut();
3650fd03e30bSJoel Dice                 concurrent_state.delete(thread)?;
3651fd03e30bSJoel Dice                 assert!(concurrent_state.get_mut(guest_task)?.ready_to_delete());
3652fa70f025SJoel Dice 
3653fa70f025SJoel Dice                 // Not yet started; cancel and remove from pending
365457f899c4SAlex Crichton                 let pending = &mut store.instance_state(instance).concurrent_state().pending;
3655e06fbf70SSy Brand                 let pending_count = pending.len();
3656e06fbf70SSy Brand                 pending.retain(|thread, _| thread.task != guest_task);
3657e06fbf70SSy Brand                 // If there were no pending threads for this task, we're in an error state
3658e06fbf70SSy Brand                 if pending.len() == pending_count {
3659da093747SAlex Crichton                     bail!(Trap::SubtaskCancelAfterTerminal);
3660fa70f025SJoel Dice                 }
3661fa70f025SJoel Dice                 return Ok(Status::StartCancelled as u32);
3662e06fbf70SSy Brand             } else if !task.returned_or_cancelled() {
3663fa70f025SJoel Dice                 // Started, but not yet returned or cancelled; send the
3664fa70f025SJoel Dice                 // `CANCELLED` event
3665fa70f025SJoel Dice                 task.cancel_sent = true;
3666fa70f025SJoel Dice                 // Note that this might overwrite an event that was set earlier
3667fa70f025SJoel Dice                 // (e.g. `Event::None` if the task is yielding, or
3668fa70f025SJoel Dice                 // `Event::Cancelled` if it was already cancelled), but that's
3669fa70f025SJoel Dice                 // okay -- this should supersede the previous state.
3670fa70f025SJoel Dice                 task.event = Some(Event::Cancelled);
3671d2fbd2deSAlex Crichton                 let runtime_instance = task.instance.index;
3672e06fbf70SSy Brand                 for thread in task.threads.clone() {
3673e06fbf70SSy Brand                     let thread = QualifiedThreadId {
3674e06fbf70SSy Brand                         task: guest_task,
3675e06fbf70SSy Brand                         thread,
3676e06fbf70SSy Brand                     };
3677e06fbf70SSy Brand                     if let Some(set) = concurrent_state
3678da093747SAlex Crichton                         .get_mut(thread.thread)?
3679e06fbf70SSy Brand                         .wake_on_cancel
3680e06fbf70SSy Brand                         .take()
3681e06fbf70SSy Brand                     {
3682da093747SAlex Crichton                         let item = match concurrent_state.get_mut(set)?.waiting.remove(&thread) {
3683da093747SAlex Crichton                             Some(WaitMode::Fiber(fiber)) => WorkItem::ResumeFiber(fiber),
3684da093747SAlex Crichton                             Some(WaitMode::Callback(instance)) => WorkItem::GuestCall(
3685d2fbd2deSAlex Crichton                                 runtime_instance,
3686d2fbd2deSAlex Crichton                                 GuestCall {
3687e06fbf70SSy Brand                                     thread,
36887e39c25eSJoel Dice                                     kind: GuestCallKind::DeliverEvent {
36897e39c25eSJoel Dice                                         instance,
36907e39c25eSJoel Dice                                         set: None,
36917e39c25eSJoel Dice                                     },
3692d2fbd2deSAlex Crichton                                 },
3693d2fbd2deSAlex Crichton                             ),
3694da093747SAlex Crichton                             None => bail_bug!("thread not present in wake_on_cancel set"),
3695fa70f025SJoel Dice                         };
3696fa70f025SJoel Dice                         concurrent_state.push_high_priority(item);
3697fa70f025SJoel Dice 
3698fc4020baSSy Brand                         store.suspend(SuspendReason::Yielding {
3699fc4020baSSy Brand                             thread: caller,
3700fc4020baSSy Brand                             // `subtask.cancel` is not allowed to be called in a
3701fc4020baSSy Brand                             // sync context, so we cannot skip the may-block check.
3702fc4020baSSy Brand                             skip_may_block_check: false,
3703fc4020baSSy Brand                         })?;
3704e06fbf70SSy Brand                         break;
3705e06fbf70SSy Brand                     }
3706fa70f025SJoel Dice                 }
3707fa70f025SJoel Dice 
3708065baac4SAlex Crichton                 // Guest tasks need to block if they have not yet returned or
3709065baac4SAlex Crichton                 // cancelled, even as a result of the event delivery above.
3710065baac4SAlex Crichton                 needs_block = !store
3711065baac4SAlex Crichton                     .concurrent_state_mut()
3712065baac4SAlex Crichton                     .get_mut(guest_task)?
3713065baac4SAlex Crichton                     .returned_or_cancelled()
3714065baac4SAlex Crichton             } else {
3715065baac4SAlex Crichton                 needs_block = false;
3716065baac4SAlex Crichton             }
3717065baac4SAlex Crichton         };
3718065baac4SAlex Crichton 
3719065baac4SAlex Crichton         // If we need to block waiting on the terminal status of this subtask
3720065baac4SAlex Crichton         // then return immediately in `async` mode, or otherwise wait for the
3721065baac4SAlex Crichton         // event to get signaled through the store.
3722065baac4SAlex Crichton         if needs_block {
3723fa70f025SJoel Dice             if async_ {
3724fa70f025SJoel Dice                 return Ok(BLOCKED);
3725fa70f025SJoel Dice             }
3726065baac4SAlex Crichton 
3727065baac4SAlex Crichton             // Wait for this waitable to get signaled with its terminal status
3728065baac4SAlex Crichton             // from the completion callback enqueued by `first_poll`. Once
3729065baac4SAlex Crichton             // that's done fall through to the sahred
3730065baac4SAlex Crichton             store.wait_for_event(waitable)?;
3731065baac4SAlex Crichton 
3732065baac4SAlex Crichton             // .. fall through to determine what event's in store for us.
3733fa70f025SJoel Dice         }
3734fa70f025SJoel Dice 
37357e39c25eSJoel Dice         let event = waitable.take_event(store.concurrent_state_mut())?;
3736fa70f025SJoel Dice         if let Some(Event::Subtask {
3737fa70f025SJoel Dice             status: status @ (Status::Returned | Status::ReturnCancelled),
3738fa70f025SJoel Dice         }) = event
3739fa70f025SJoel Dice         {
3740fa70f025SJoel Dice             Ok(status as u32)
3741fa70f025SJoel Dice         } else {
3742da093747SAlex Crichton             bail!(Trap::SubtaskCancelAfterTerminal);
3743fa70f025SJoel Dice         }
3744fa70f025SJoel Dice     }
3745fa70f025SJoel Dice 
context_get(self, store: &mut StoreOpaque, slot: u32) -> Result<u32>37464d129904SAlex Crichton     pub(crate) fn context_get(self, store: &mut StoreOpaque, slot: u32) -> Result<u32> {
37477e39c25eSJoel Dice         store.concurrent_state_mut().context_get(slot)
37486751ea79SJoel Dice     }
37496751ea79SJoel Dice 
context_set(self, store: &mut StoreOpaque, slot: u32, value: u32) -> Result<()>37504d129904SAlex Crichton     pub(crate) fn context_set(self, store: &mut StoreOpaque, slot: u32, value: u32) -> Result<()> {
37517e39c25eSJoel Dice         store.concurrent_state_mut().context_set(slot, value)
37526751ea79SJoel Dice     }
3753b221fca7SJoel Dice }
3754b221fca7SJoel Dice 
3755812dd1e8SJoel Dice /// Trait representing component model ABI async intrinsics and fused adapter
3756812dd1e8SJoel Dice /// helper functions.
3757fa70f025SJoel Dice ///
3758fa70f025SJoel Dice /// SAFETY (callers): Most of the methods in this trait accept raw pointers,
3759fa70f025SJoel Dice /// which must be valid for at least the duration of the call (and possibly for
3760fa70f025SJoel Dice /// as long as the relevant guest task exists, in the case of `*mut VMFuncRef`
3761fa70f025SJoel Dice /// pointers used for async calls).
3762fa70f025SJoel Dice pub trait VMComponentAsyncStore {
3763b221fca7SJoel Dice     /// A helper function for fused adapter modules involving calls where the
3764b221fca7SJoel Dice     /// one of the caller or callee is async.
3765b221fca7SJoel Dice     ///
3766b221fca7SJoel Dice     /// This helper is not used when the caller and callee both use the sync
3767b221fca7SJoel Dice     /// ABI, only when at least one is async is this used.
prepare_call( &mut self, instance: Instance, memory: *mut VMMemoryDefinition, start: NonNull<VMFuncRef>, return_: NonNull<VMFuncRef>, caller_instance: RuntimeComponentInstanceIndex, callee_instance: RuntimeComponentInstanceIndex, task_return_type: TypeTupleIndex, callee_async: bool, string_encoding: StringEncoding, result_count: u32, storage: *mut ValRaw, storage_len: usize, ) -> Result<()>3768b221fca7SJoel Dice     unsafe fn prepare_call(
3769b221fca7SJoel Dice         &mut self,
3770b221fca7SJoel Dice         instance: Instance,
3771b221fca7SJoel Dice         memory: *mut VMMemoryDefinition,
3772da093747SAlex Crichton         start: NonNull<VMFuncRef>,
3773da093747SAlex Crichton         return_: NonNull<VMFuncRef>,
3774b221fca7SJoel Dice         caller_instance: RuntimeComponentInstanceIndex,
3775b221fca7SJoel Dice         callee_instance: RuntimeComponentInstanceIndex,
3776b221fca7SJoel Dice         task_return_type: TypeTupleIndex,
37778992b99bSJoel Dice         callee_async: bool,
3778da093747SAlex Crichton         string_encoding: StringEncoding,
3779b221fca7SJoel Dice         result_count: u32,
3780b221fca7SJoel Dice         storage: *mut ValRaw,
3781b221fca7SJoel Dice         storage_len: usize,
3782b221fca7SJoel Dice     ) -> Result<()>;
3783b221fca7SJoel Dice 
3784b221fca7SJoel Dice     /// A helper function for fused adapter modules involving calls where the
3785b221fca7SJoel Dice     /// caller is sync-lowered but the callee is async-lifted.
sync_start( &mut self, instance: Instance, callback: *mut VMFuncRef, callee: NonNull<VMFuncRef>, param_count: u32, storage: *mut MaybeUninit<ValRaw>, storage_len: usize, ) -> Result<()>3786b221fca7SJoel Dice     unsafe fn sync_start(
3787b221fca7SJoel Dice         &mut self,
3788b221fca7SJoel Dice         instance: Instance,
3789b221fca7SJoel Dice         callback: *mut VMFuncRef,
3790da093747SAlex Crichton         callee: NonNull<VMFuncRef>,
3791b221fca7SJoel Dice         param_count: u32,
3792b221fca7SJoel Dice         storage: *mut MaybeUninit<ValRaw>,
3793b221fca7SJoel Dice         storage_len: usize,
3794b221fca7SJoel Dice     ) -> Result<()>;
3795b221fca7SJoel Dice 
3796b221fca7SJoel Dice     /// A helper function for fused adapter modules involving calls where the
3797b221fca7SJoel Dice     /// caller is async-lowered.
async_start( &mut self, instance: Instance, callback: *mut VMFuncRef, post_return: *mut VMFuncRef, callee: NonNull<VMFuncRef>, param_count: u32, result_count: u32, flags: u32, ) -> Result<u32>3798b221fca7SJoel Dice     unsafe fn async_start(
3799b221fca7SJoel Dice         &mut self,
3800b221fca7SJoel Dice         instance: Instance,
3801b221fca7SJoel Dice         callback: *mut VMFuncRef,
3802b221fca7SJoel Dice         post_return: *mut VMFuncRef,
3803da093747SAlex Crichton         callee: NonNull<VMFuncRef>,
3804b221fca7SJoel Dice         param_count: u32,
3805b221fca7SJoel Dice         result_count: u32,
3806b221fca7SJoel Dice         flags: u32,
3807b221fca7SJoel Dice     ) -> Result<u32>;
3808b221fca7SJoel Dice 
3809812dd1e8SJoel Dice     /// The `future.write` intrinsic.
future_write( &mut self, instance: Instance, caller: RuntimeComponentInstanceIndex, ty: TypeFutureTableIndex, options: OptionsIndex, future: u32, address: u32, ) -> Result<u32>3810815c10deSAlex Crichton     fn future_write(
3811812dd1e8SJoel Dice         &mut self,
3812b221fca7SJoel Dice         instance: Instance,
38136751ea79SJoel Dice         caller: RuntimeComponentInstanceIndex,
3814812dd1e8SJoel Dice         ty: TypeFutureTableIndex,
3815815c10deSAlex Crichton         options: OptionsIndex,
3816812dd1e8SJoel Dice         future: u32,
3817812dd1e8SJoel Dice         address: u32,
3818812dd1e8SJoel Dice     ) -> Result<u32>;
3819812dd1e8SJoel Dice 
3820812dd1e8SJoel Dice     /// The `future.read` intrinsic.
future_read( &mut self, instance: Instance, caller: RuntimeComponentInstanceIndex, ty: TypeFutureTableIndex, options: OptionsIndex, future: u32, address: u32, ) -> Result<u32>3821815c10deSAlex Crichton     fn future_read(
3822812dd1e8SJoel Dice         &mut self,
3823b221fca7SJoel Dice         instance: Instance,
38246751ea79SJoel Dice         caller: RuntimeComponentInstanceIndex,
3825812dd1e8SJoel Dice         ty: TypeFutureTableIndex,
3826815c10deSAlex Crichton         options: OptionsIndex,
3827812dd1e8SJoel Dice         future: u32,
3828812dd1e8SJoel Dice         address: u32,
3829812dd1e8SJoel Dice     ) -> Result<u32>;
3830812dd1e8SJoel Dice 
3831b4475438SJoel Dice     /// The `future.drop-writable` intrinsic.
future_drop_writable( &mut self, instance: Instance, ty: TypeFutureTableIndex, writer: u32, ) -> Result<()>3832b4475438SJoel Dice     fn future_drop_writable(
3833b4475438SJoel Dice         &mut self,
3834b4475438SJoel Dice         instance: Instance,
3835b4475438SJoel Dice         ty: TypeFutureTableIndex,
3836b4475438SJoel Dice         writer: u32,
3837b4475438SJoel Dice     ) -> Result<()>;
3838b4475438SJoel Dice 
3839812dd1e8SJoel Dice     /// The `stream.write` intrinsic.
stream_write( &mut self, instance: Instance, caller: RuntimeComponentInstanceIndex, ty: TypeStreamTableIndex, options: OptionsIndex, stream: u32, address: u32, count: u32, ) -> Result<u32>3840815c10deSAlex Crichton     fn stream_write(
3841812dd1e8SJoel Dice         &mut self,
3842b221fca7SJoel Dice         instance: Instance,
38436751ea79SJoel Dice         caller: RuntimeComponentInstanceIndex,
3844812dd1e8SJoel Dice         ty: TypeStreamTableIndex,
3845815c10deSAlex Crichton         options: OptionsIndex,
3846812dd1e8SJoel Dice         stream: u32,
3847812dd1e8SJoel Dice         address: u32,
3848812dd1e8SJoel Dice         count: u32,
3849812dd1e8SJoel Dice     ) -> Result<u32>;
3850812dd1e8SJoel Dice 
3851812dd1e8SJoel Dice     /// The `stream.read` intrinsic.
stream_read( &mut self, instance: Instance, caller: RuntimeComponentInstanceIndex, ty: TypeStreamTableIndex, options: OptionsIndex, stream: u32, address: u32, count: u32, ) -> Result<u32>3852815c10deSAlex Crichton     fn stream_read(
3853812dd1e8SJoel Dice         &mut self,
3854b221fca7SJoel Dice         instance: Instance,
38556751ea79SJoel Dice         caller: RuntimeComponentInstanceIndex,
3856812dd1e8SJoel Dice         ty: TypeStreamTableIndex,
3857815c10deSAlex Crichton         options: OptionsIndex,
3858812dd1e8SJoel Dice         stream: u32,
3859812dd1e8SJoel Dice         address: u32,
3860812dd1e8SJoel Dice         count: u32,
3861812dd1e8SJoel Dice     ) -> Result<u32>;
3862812dd1e8SJoel Dice 
3863812dd1e8SJoel Dice     /// The "fast-path" implementation of the `stream.write` intrinsic for
3864812dd1e8SJoel Dice     /// "flat" (i.e. memcpy-able) payloads.
flat_stream_write( &mut self, instance: Instance, caller: RuntimeComponentInstanceIndex, ty: TypeStreamTableIndex, options: OptionsIndex, payload_size: u32, payload_align: u32, stream: u32, address: u32, count: u32, ) -> Result<u32>3865815c10deSAlex Crichton     fn flat_stream_write(
3866812dd1e8SJoel Dice         &mut self,
3867b221fca7SJoel Dice         instance: Instance,
38686751ea79SJoel Dice         caller: RuntimeComponentInstanceIndex,
3869812dd1e8SJoel Dice         ty: TypeStreamTableIndex,
3870815c10deSAlex Crichton         options: OptionsIndex,
3871812dd1e8SJoel Dice         payload_size: u32,
3872812dd1e8SJoel Dice         payload_align: u32,
3873812dd1e8SJoel Dice         stream: u32,
3874812dd1e8SJoel Dice         address: u32,
3875812dd1e8SJoel Dice         count: u32,
3876812dd1e8SJoel Dice     ) -> Result<u32>;
3877812dd1e8SJoel Dice 
3878812dd1e8SJoel Dice     /// The "fast-path" implementation of the `stream.read` intrinsic for "flat"
3879812dd1e8SJoel Dice     /// (i.e. memcpy-able) payloads.
flat_stream_read( &mut self, instance: Instance, caller: RuntimeComponentInstanceIndex, ty: TypeStreamTableIndex, options: OptionsIndex, payload_size: u32, payload_align: u32, stream: u32, address: u32, count: u32, ) -> Result<u32>3880815c10deSAlex Crichton     fn flat_stream_read(
3881812dd1e8SJoel Dice         &mut self,
3882b221fca7SJoel Dice         instance: Instance,
38836751ea79SJoel Dice         caller: RuntimeComponentInstanceIndex,
3884812dd1e8SJoel Dice         ty: TypeStreamTableIndex,
3885815c10deSAlex Crichton         options: OptionsIndex,
3886812dd1e8SJoel Dice         payload_size: u32,
3887812dd1e8SJoel Dice         payload_align: u32,
3888812dd1e8SJoel Dice         stream: u32,
3889812dd1e8SJoel Dice         address: u32,
3890812dd1e8SJoel Dice         count: u32,
3891812dd1e8SJoel Dice     ) -> Result<u32>;
3892812dd1e8SJoel Dice 
3893b4475438SJoel Dice     /// The `stream.drop-writable` intrinsic.
stream_drop_writable( &mut self, instance: Instance, ty: TypeStreamTableIndex, writer: u32, ) -> Result<()>3894b4475438SJoel Dice     fn stream_drop_writable(
3895b4475438SJoel Dice         &mut self,
3896b4475438SJoel Dice         instance: Instance,
3897b4475438SJoel Dice         ty: TypeStreamTableIndex,
3898b4475438SJoel Dice         writer: u32,
3899b4475438SJoel Dice     ) -> Result<()>;
3900b4475438SJoel Dice 
3901812dd1e8SJoel Dice     /// The `error-context.debug-message` intrinsic.
error_context_debug_message( &mut self, instance: Instance, ty: TypeComponentLocalErrorContextTableIndex, options: OptionsIndex, err_ctx_handle: u32, debug_msg_address: u32, ) -> Result<()>3902815c10deSAlex Crichton     fn error_context_debug_message(
3903812dd1e8SJoel Dice         &mut self,
3904b221fca7SJoel Dice         instance: Instance,
3905812dd1e8SJoel Dice         ty: TypeComponentLocalErrorContextTableIndex,
3906815c10deSAlex Crichton         options: OptionsIndex,
3907812dd1e8SJoel Dice         err_ctx_handle: u32,
3908812dd1e8SJoel Dice         debug_msg_address: u32,
3909812dd1e8SJoel Dice     ) -> Result<()>;
3910e06fbf70SSy Brand 
3911020727d0SAlex Crichton     /// The `thread.new-indirect` intrinsic
thread_new_indirect( &mut self, instance: Instance, caller: RuntimeComponentInstanceIndex, func_ty_idx: TypeFuncIndex, start_func_table_idx: RuntimeTableIndex, start_func_idx: u32, context: i32, ) -> Result<u32>3912e06fbf70SSy Brand     fn thread_new_indirect(
3913e06fbf70SSy Brand         &mut self,
3914e06fbf70SSy Brand         instance: Instance,
3915e06fbf70SSy Brand         caller: RuntimeComponentInstanceIndex,
3916e06fbf70SSy Brand         func_ty_idx: TypeFuncIndex,
3917e06fbf70SSy Brand         start_func_table_idx: RuntimeTableIndex,
3918e06fbf70SSy Brand         start_func_idx: u32,
3919e06fbf70SSy Brand         context: i32,
3920e06fbf70SSy Brand     ) -> Result<u32>;
3921812dd1e8SJoel Dice }
3922812dd1e8SJoel Dice 
3923fa70f025SJoel Dice /// SAFETY: See trait docs.
3924fa70f025SJoel Dice impl<T: 'static> VMComponentAsyncStore for StoreInner<T> {
prepare_call( &mut self, instance: Instance, memory: *mut VMMemoryDefinition, start: NonNull<VMFuncRef>, return_: NonNull<VMFuncRef>, caller_instance: RuntimeComponentInstanceIndex, callee_instance: RuntimeComponentInstanceIndex, task_return_type: TypeTupleIndex, callee_async: bool, string_encoding: StringEncoding, result_count_or_max_if_async: u32, storage: *mut ValRaw, storage_len: usize, ) -> Result<()>3925b221fca7SJoel Dice     unsafe fn prepare_call(
3926b221fca7SJoel Dice         &mut self,
3927b221fca7SJoel Dice         instance: Instance,
3928b221fca7SJoel Dice         memory: *mut VMMemoryDefinition,
3929da093747SAlex Crichton         start: NonNull<VMFuncRef>,
3930da093747SAlex Crichton         return_: NonNull<VMFuncRef>,
3931b221fca7SJoel Dice         caller_instance: RuntimeComponentInstanceIndex,
3932b221fca7SJoel Dice         callee_instance: RuntimeComponentInstanceIndex,
3933b221fca7SJoel Dice         task_return_type: TypeTupleIndex,
39348992b99bSJoel Dice         callee_async: bool,
3935da093747SAlex Crichton         string_encoding: StringEncoding,
3936fa70f025SJoel Dice         result_count_or_max_if_async: u32,
3937b221fca7SJoel Dice         storage: *mut ValRaw,
3938b221fca7SJoel Dice         storage_len: usize,
3939b221fca7SJoel Dice     ) -> Result<()> {
3940fa70f025SJoel Dice         // SAFETY: The `wasmtime_cranelift`-generated code that calls
3941fa70f025SJoel Dice         // this method will have ensured that `storage` is a valid
3942fa70f025SJoel Dice         // pointer containing at least `storage_len` items.
3943fa70f025SJoel Dice         let params = unsafe { std::slice::from_raw_parts(storage, storage_len) }.to_vec();
3944fa70f025SJoel Dice 
3945fa70f025SJoel Dice         unsafe {
3946fa70f025SJoel Dice             instance.prepare_call(
3947fa70f025SJoel Dice                 StoreContextMut(self),
3948b221fca7SJoel Dice                 start,
3949b221fca7SJoel Dice                 return_,
3950b221fca7SJoel Dice                 caller_instance,
3951b221fca7SJoel Dice                 callee_instance,
3952b221fca7SJoel Dice                 task_return_type,
39538992b99bSJoel Dice                 callee_async,
3954fa70f025SJoel Dice                 memory,
3955b221fca7SJoel Dice                 string_encoding,
3956fa70f025SJoel Dice                 match result_count_or_max_if_async {
3957fa70f025SJoel Dice                     PREPARE_ASYNC_NO_RESULT => CallerInfo::Async {
3958fa70f025SJoel Dice                         params,
3959fa70f025SJoel Dice                         has_result: false,
3960fa70f025SJoel Dice                     },
3961fa70f025SJoel Dice                     PREPARE_ASYNC_WITH_RESULT => CallerInfo::Async {
3962fa70f025SJoel Dice                         params,
3963fa70f025SJoel Dice                         has_result: true,
3964fa70f025SJoel Dice                     },
3965fa70f025SJoel Dice                     result_count => CallerInfo::Sync {
3966fa70f025SJoel Dice                         params,
3967b221fca7SJoel Dice                         result_count,
3968fa70f025SJoel Dice                     },
3969fa70f025SJoel Dice                 },
3970fa70f025SJoel Dice             )
3971fa70f025SJoel Dice         }
3972b221fca7SJoel Dice     }
3973b221fca7SJoel Dice 
sync_start( &mut self, instance: Instance, callback: *mut VMFuncRef, callee: NonNull<VMFuncRef>, param_count: u32, storage: *mut MaybeUninit<ValRaw>, storage_len: usize, ) -> Result<()>3974b221fca7SJoel Dice     unsafe fn sync_start(
3975b221fca7SJoel Dice         &mut self,
3976b221fca7SJoel Dice         instance: Instance,
3977b221fca7SJoel Dice         callback: *mut VMFuncRef,
3978da093747SAlex Crichton         callee: NonNull<VMFuncRef>,
3979b221fca7SJoel Dice         param_count: u32,
3980b221fca7SJoel Dice         storage: *mut MaybeUninit<ValRaw>,
3981b221fca7SJoel Dice         storage_len: usize,
3982b221fca7SJoel Dice     ) -> Result<()> {
3983fa70f025SJoel Dice         unsafe {
3984fa70f025SJoel Dice             instance
3985fa70f025SJoel Dice                 .start_call(
3986fa70f025SJoel Dice                     StoreContextMut(self),
3987b221fca7SJoel Dice                     callback,
3988fa70f025SJoel Dice                     ptr::null_mut(),
3989b221fca7SJoel Dice                     callee,
3990b221fca7SJoel Dice                     param_count,
3991fa70f025SJoel Dice                     1,
3992fa70f025SJoel Dice                     START_FLAG_ASYNC_CALLEE,
3993fa70f025SJoel Dice                     // SAFETY: The `wasmtime_cranelift`-generated code that calls
3994fa70f025SJoel Dice                     // this method will have ensured that `storage` is a valid
3995fa70f025SJoel Dice                     // pointer containing at least `storage_len` items.
3996fa70f025SJoel Dice                     Some(std::slice::from_raw_parts_mut(storage, storage_len)),
3997fa70f025SJoel Dice                 )
3998fa70f025SJoel Dice                 .map(drop)
3999fa70f025SJoel Dice         }
4000b221fca7SJoel Dice     }
4001b221fca7SJoel Dice 
async_start( &mut self, instance: Instance, callback: *mut VMFuncRef, post_return: *mut VMFuncRef, callee: NonNull<VMFuncRef>, param_count: u32, result_count: u32, flags: u32, ) -> Result<u32>4002b221fca7SJoel Dice     unsafe fn async_start(
4003b221fca7SJoel Dice         &mut self,
4004b221fca7SJoel Dice         instance: Instance,
4005b221fca7SJoel Dice         callback: *mut VMFuncRef,
4006b221fca7SJoel Dice         post_return: *mut VMFuncRef,
4007da093747SAlex Crichton         callee: NonNull<VMFuncRef>,
4008b221fca7SJoel Dice         param_count: u32,
4009b221fca7SJoel Dice         result_count: u32,
4010b221fca7SJoel Dice         flags: u32,
4011b221fca7SJoel Dice     ) -> Result<u32> {
4012fa70f025SJoel Dice         unsafe {
4013fa70f025SJoel Dice             instance.start_call(
4014fa70f025SJoel Dice                 StoreContextMut(self),
4015b221fca7SJoel Dice                 callback,
4016b221fca7SJoel Dice                 post_return,
4017b221fca7SJoel Dice                 callee,
4018b221fca7SJoel Dice                 param_count,
4019b221fca7SJoel Dice                 result_count,
4020b221fca7SJoel Dice                 flags,
4021fa70f025SJoel Dice                 None,
4022fa70f025SJoel Dice             )
4023b221fca7SJoel Dice         }
4024812dd1e8SJoel Dice     }
4025812dd1e8SJoel Dice 
future_write( &mut self, instance: Instance, caller: RuntimeComponentInstanceIndex, ty: TypeFutureTableIndex, options: OptionsIndex, future: u32, address: u32, ) -> Result<u32>4026815c10deSAlex Crichton     fn future_write(
4027812dd1e8SJoel Dice         &mut self,
4028b221fca7SJoel Dice         instance: Instance,
40296751ea79SJoel Dice         caller: RuntimeComponentInstanceIndex,
4030812dd1e8SJoel Dice         ty: TypeFutureTableIndex,
4031815c10deSAlex Crichton         options: OptionsIndex,
4032812dd1e8SJoel Dice         future: u32,
4033812dd1e8SJoel Dice         address: u32,
4034812dd1e8SJoel Dice     ) -> Result<u32> {
4035fa70f025SJoel Dice         instance
4036fa70f025SJoel Dice             .guest_write(
4037fa70f025SJoel Dice                 StoreContextMut(self),
403891abf8caSJoel Dice                 caller,
4039e8189549SJoel Dice                 TransmitIndex::Future(ty),
4040815c10deSAlex Crichton                 options,
4041fa70f025SJoel Dice                 None,
4042812dd1e8SJoel Dice                 future,
4043812dd1e8SJoel Dice                 address,
4044fa70f025SJoel Dice                 1,
4045fa70f025SJoel Dice             )
4046fa70f025SJoel Dice             .map(|result| result.encode())
4047fa70f025SJoel Dice     }
4048812dd1e8SJoel Dice 
future_read( &mut self, instance: Instance, caller: RuntimeComponentInstanceIndex, ty: TypeFutureTableIndex, options: OptionsIndex, future: u32, address: u32, ) -> Result<u32>4049815c10deSAlex Crichton     fn future_read(
4050812dd1e8SJoel Dice         &mut self,
4051b221fca7SJoel Dice         instance: Instance,
40526751ea79SJoel Dice         caller: RuntimeComponentInstanceIndex,
4053812dd1e8SJoel Dice         ty: TypeFutureTableIndex,
4054815c10deSAlex Crichton         options: OptionsIndex,
4055812dd1e8SJoel Dice         future: u32,
4056812dd1e8SJoel Dice         address: u32,
4057812dd1e8SJoel Dice     ) -> Result<u32> {
4058fa70f025SJoel Dice         instance
4059fa70f025SJoel Dice             .guest_read(
4060fa70f025SJoel Dice                 StoreContextMut(self),
406191abf8caSJoel Dice                 caller,
4062e8189549SJoel Dice                 TransmitIndex::Future(ty),
4063815c10deSAlex Crichton                 options,
4064fa70f025SJoel Dice                 None,
4065812dd1e8SJoel Dice                 future,
4066812dd1e8SJoel Dice                 address,
4067fa70f025SJoel Dice                 1,
4068fa70f025SJoel Dice             )
4069fa70f025SJoel Dice             .map(|result| result.encode())
4070fa70f025SJoel Dice     }
4071812dd1e8SJoel Dice 
stream_write( &mut self, instance: Instance, caller: RuntimeComponentInstanceIndex, ty: TypeStreamTableIndex, options: OptionsIndex, stream: u32, address: u32, count: u32, ) -> Result<u32>4072815c10deSAlex Crichton     fn stream_write(
4073812dd1e8SJoel Dice         &mut self,
4074b221fca7SJoel Dice         instance: Instance,
40756751ea79SJoel Dice         caller: RuntimeComponentInstanceIndex,
4076812dd1e8SJoel Dice         ty: TypeStreamTableIndex,
4077815c10deSAlex Crichton         options: OptionsIndex,
4078812dd1e8SJoel Dice         stream: u32,
4079812dd1e8SJoel Dice         address: u32,
4080812dd1e8SJoel Dice         count: u32,
4081812dd1e8SJoel Dice     ) -> Result<u32> {
4082fa70f025SJoel Dice         instance
4083fa70f025SJoel Dice             .guest_write(
4084fa70f025SJoel Dice                 StoreContextMut(self),
408591abf8caSJoel Dice                 caller,
4086e8189549SJoel Dice                 TransmitIndex::Stream(ty),
4087815c10deSAlex Crichton                 options,
4088fa70f025SJoel Dice                 None,
4089812dd1e8SJoel Dice                 stream,
4090812dd1e8SJoel Dice                 address,
4091812dd1e8SJoel Dice                 count,
4092fa70f025SJoel Dice             )
4093fa70f025SJoel Dice             .map(|result| result.encode())
4094fa70f025SJoel Dice     }
4095812dd1e8SJoel Dice 
stream_read( &mut self, instance: Instance, caller: RuntimeComponentInstanceIndex, ty: TypeStreamTableIndex, options: OptionsIndex, stream: u32, address: u32, count: u32, ) -> Result<u32>4096815c10deSAlex Crichton     fn stream_read(
4097812dd1e8SJoel Dice         &mut self,
4098b221fca7SJoel Dice         instance: Instance,
40996751ea79SJoel Dice         caller: RuntimeComponentInstanceIndex,
4100812dd1e8SJoel Dice         ty: TypeStreamTableIndex,
4101815c10deSAlex Crichton         options: OptionsIndex,
4102812dd1e8SJoel Dice         stream: u32,
4103812dd1e8SJoel Dice         address: u32,
4104812dd1e8SJoel Dice         count: u32,
4105812dd1e8SJoel Dice     ) -> Result<u32> {
4106fa70f025SJoel Dice         instance
4107fa70f025SJoel Dice             .guest_read(
4108fa70f025SJoel Dice                 StoreContextMut(self),
410991abf8caSJoel Dice                 caller,
4110e8189549SJoel Dice                 TransmitIndex::Stream(ty),
4111815c10deSAlex Crichton                 options,
4112fa70f025SJoel Dice                 None,
4113812dd1e8SJoel Dice                 stream,
4114812dd1e8SJoel Dice                 address,
4115812dd1e8SJoel Dice                 count,
4116fa70f025SJoel Dice             )
4117fa70f025SJoel Dice             .map(|result| result.encode())
4118fa70f025SJoel Dice     }
4119812dd1e8SJoel Dice 
future_drop_writable( &mut self, instance: Instance, ty: TypeFutureTableIndex, writer: u32, ) -> Result<()>4120b4475438SJoel Dice     fn future_drop_writable(
4121b4475438SJoel Dice         &mut self,
4122b4475438SJoel Dice         instance: Instance,
4123b4475438SJoel Dice         ty: TypeFutureTableIndex,
4124b4475438SJoel Dice         writer: u32,
4125b4475438SJoel Dice     ) -> Result<()> {
41267e39c25eSJoel Dice         instance.guest_drop_writable(self, TransmitIndex::Future(ty), writer)
4127b4475438SJoel Dice     }
4128b4475438SJoel Dice 
flat_stream_write( &mut self, instance: Instance, caller: RuntimeComponentInstanceIndex, ty: TypeStreamTableIndex, options: OptionsIndex, payload_size: u32, payload_align: u32, stream: u32, address: u32, count: u32, ) -> Result<u32>4129815c10deSAlex Crichton     fn flat_stream_write(
4130812dd1e8SJoel Dice         &mut self,
4131b221fca7SJoel Dice         instance: Instance,
41326751ea79SJoel Dice         caller: RuntimeComponentInstanceIndex,
4133812dd1e8SJoel Dice         ty: TypeStreamTableIndex,
4134815c10deSAlex Crichton         options: OptionsIndex,
4135812dd1e8SJoel Dice         payload_size: u32,
4136812dd1e8SJoel Dice         payload_align: u32,
4137812dd1e8SJoel Dice         stream: u32,
4138812dd1e8SJoel Dice         address: u32,
4139812dd1e8SJoel Dice         count: u32,
4140812dd1e8SJoel Dice     ) -> Result<u32> {
4141fa70f025SJoel Dice         instance
4142fa70f025SJoel Dice             .guest_write(
4143fa70f025SJoel Dice                 StoreContextMut(self),
414491abf8caSJoel Dice                 caller,
4145e8189549SJoel Dice                 TransmitIndex::Stream(ty),
4146815c10deSAlex Crichton                 options,
4147fa70f025SJoel Dice                 Some(FlatAbi {
4148fa70f025SJoel Dice                     size: payload_size,
4149fa70f025SJoel Dice                     align: payload_align,
4150fa70f025SJoel Dice                 }),
4151812dd1e8SJoel Dice                 stream,
4152812dd1e8SJoel Dice                 address,
4153812dd1e8SJoel Dice                 count,
4154fa70f025SJoel Dice             )
4155fa70f025SJoel Dice             .map(|result| result.encode())
4156fa70f025SJoel Dice     }
4157812dd1e8SJoel Dice 
flat_stream_read( &mut self, instance: Instance, caller: RuntimeComponentInstanceIndex, ty: TypeStreamTableIndex, options: OptionsIndex, payload_size: u32, payload_align: u32, stream: u32, address: u32, count: u32, ) -> Result<u32>4158815c10deSAlex Crichton     fn flat_stream_read(
4159812dd1e8SJoel Dice         &mut self,
4160b221fca7SJoel Dice         instance: Instance,
41616751ea79SJoel Dice         caller: RuntimeComponentInstanceIndex,
4162812dd1e8SJoel Dice         ty: TypeStreamTableIndex,
4163815c10deSAlex Crichton         options: OptionsIndex,
4164812dd1e8SJoel Dice         payload_size: u32,
4165812dd1e8SJoel Dice         payload_align: u32,
4166812dd1e8SJoel Dice         stream: u32,
4167812dd1e8SJoel Dice         address: u32,
4168812dd1e8SJoel Dice         count: u32,
4169812dd1e8SJoel Dice     ) -> Result<u32> {
4170fa70f025SJoel Dice         instance
4171fa70f025SJoel Dice             .guest_read(
4172fa70f025SJoel Dice                 StoreContextMut(self),
417391abf8caSJoel Dice                 caller,
4174e8189549SJoel Dice                 TransmitIndex::Stream(ty),
4175815c10deSAlex Crichton                 options,
4176fa70f025SJoel Dice                 Some(FlatAbi {
4177fa70f025SJoel Dice                     size: payload_size,
4178fa70f025SJoel Dice                     align: payload_align,
4179fa70f025SJoel Dice                 }),
4180812dd1e8SJoel Dice                 stream,
4181812dd1e8SJoel Dice                 address,
4182812dd1e8SJoel Dice                 count,
4183fa70f025SJoel Dice             )
4184fa70f025SJoel Dice             .map(|result| result.encode())
4185fa70f025SJoel Dice     }
4186812dd1e8SJoel Dice 
stream_drop_writable( &mut self, instance: Instance, ty: TypeStreamTableIndex, writer: u32, ) -> Result<()>4187b4475438SJoel Dice     fn stream_drop_writable(
4188b4475438SJoel Dice         &mut self,
4189b4475438SJoel Dice         instance: Instance,
4190b4475438SJoel Dice         ty: TypeStreamTableIndex,
4191b4475438SJoel Dice         writer: u32,
4192b4475438SJoel Dice     ) -> Result<()> {
41937e39c25eSJoel Dice         instance.guest_drop_writable(self, TransmitIndex::Stream(ty), writer)
4194b4475438SJoel Dice     }
4195b4475438SJoel Dice 
error_context_debug_message( &mut self, instance: Instance, ty: TypeComponentLocalErrorContextTableIndex, options: OptionsIndex, err_ctx_handle: u32, debug_msg_address: u32, ) -> Result<()>4196815c10deSAlex Crichton     fn error_context_debug_message(
4197812dd1e8SJoel Dice         &mut self,
4198b221fca7SJoel Dice         instance: Instance,
4199812dd1e8SJoel Dice         ty: TypeComponentLocalErrorContextTableIndex,
4200815c10deSAlex Crichton         options: OptionsIndex,
4201812dd1e8SJoel Dice         err_ctx_handle: u32,
4202812dd1e8SJoel Dice         debug_msg_address: u32,
4203812dd1e8SJoel Dice     ) -> Result<()> {
4204fa70f025SJoel Dice         instance.error_context_debug_message(
4205fa70f025SJoel Dice             StoreContextMut(self),
4206812dd1e8SJoel Dice             ty,
4207815c10deSAlex Crichton             options,
4208812dd1e8SJoel Dice             err_ctx_handle,
4209812dd1e8SJoel Dice             debug_msg_address,
4210fa70f025SJoel Dice         )
4211fa70f025SJoel Dice     }
4212e06fbf70SSy Brand 
thread_new_indirect( &mut self, instance: Instance, caller: RuntimeComponentInstanceIndex, func_ty_idx: TypeFuncIndex, start_func_table_idx: RuntimeTableIndex, start_func_idx: u32, context: i32, ) -> Result<u32>4213e06fbf70SSy Brand     fn thread_new_indirect(
4214e06fbf70SSy Brand         &mut self,
4215e06fbf70SSy Brand         instance: Instance,
4216e06fbf70SSy Brand         caller: RuntimeComponentInstanceIndex,
4217e06fbf70SSy Brand         func_ty_idx: TypeFuncIndex,
4218e06fbf70SSy Brand         start_func_table_idx: RuntimeTableIndex,
4219e06fbf70SSy Brand         start_func_idx: u32,
4220e06fbf70SSy Brand         context: i32,
4221e06fbf70SSy Brand     ) -> Result<u32> {
4222e06fbf70SSy Brand         instance.thread_new_indirect(
4223e06fbf70SSy Brand             StoreContextMut(self),
4224e06fbf70SSy Brand             caller,
4225e06fbf70SSy Brand             func_ty_idx,
4226e06fbf70SSy Brand             start_func_table_idx,
4227e06fbf70SSy Brand             start_func_idx,
4228e06fbf70SSy Brand             context,
4229e06fbf70SSy Brand         )
4230e06fbf70SSy Brand     }
4231812dd1e8SJoel Dice }
4232812dd1e8SJoel Dice 
42335764da5fSJoel Dice type HostTaskFuture = Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>;
4234fa70f025SJoel Dice 
4235fa70f025SJoel Dice /// Represents the state of a pending host task.
42363764e757SAlex Crichton ///
42373764e757SAlex Crichton /// This is used to represent tasks when the guest calls into the host.
423858877f2fSAlex Crichton pub(crate) struct HostTask {
4239fa70f025SJoel Dice     common: WaitableCommon,
42403764e757SAlex Crichton 
42413764e757SAlex Crichton     /// Guest thread which called the host.
42423764e757SAlex Crichton     caller: QualifiedThreadId,
42433764e757SAlex Crichton 
42443764e757SAlex Crichton     /// State of borrows/etc the host needs to track. Used when the guest passes
42453764e757SAlex Crichton     /// borrows to the host, for example.
42463764e757SAlex Crichton     call_context: CallContext,
42473764e757SAlex Crichton 
4248065baac4SAlex Crichton     state: HostTaskState,
4249065baac4SAlex Crichton }
42503764e757SAlex Crichton 
4251065baac4SAlex Crichton enum HostTaskState {
4252065baac4SAlex Crichton     /// A host task has been created and it's considered "started".
4253065baac4SAlex Crichton     ///
4254065baac4SAlex Crichton     /// The host task has yet to enter `first_poll` or `poll_and_block` which
4255065baac4SAlex Crichton     /// is where this will get updated further.
4256065baac4SAlex Crichton     CalleeStarted,
4257065baac4SAlex Crichton 
4258065baac4SAlex Crichton     /// State used for tasks in `first_poll` meaning that the guest did an async
4259065baac4SAlex Crichton     /// lower of a host async function which is blocked. The specified handle is
4260065baac4SAlex Crichton     /// linked to the future in the main `FuturesUnordered` of a store which is
4261065baac4SAlex Crichton     /// used to cancel it if the guest requests cancellation.
4262065baac4SAlex Crichton     CalleeRunning(JoinHandle),
4263065baac4SAlex Crichton 
4264065baac4SAlex Crichton     /// Terminal state used for tasks in `poll_and_block` to store the result of
4265065baac4SAlex Crichton     /// their computation. Note that this state is not used for tasks in
4266065baac4SAlex Crichton     /// `first_poll`.
4267065baac4SAlex Crichton     CalleeFinished(LiftedResult),
4268065baac4SAlex Crichton 
4269065baac4SAlex Crichton     /// Terminal state for host tasks meaning that the task was cancelled or the
4270065baac4SAlex Crichton     /// result was taken.
4271e8cb8751SAlex Crichton     CalleeDone { cancelled: bool },
4272fa70f025SJoel Dice }
4273fa70f025SJoel Dice 
4274fa70f025SJoel Dice impl HostTask {
new(caller: QualifiedThreadId, state: HostTaskState) -> Self4275065baac4SAlex Crichton     fn new(caller: QualifiedThreadId, state: HostTaskState) -> Self {
4276fa70f025SJoel Dice         Self {
4277fa70f025SJoel Dice             common: WaitableCommon::default(),
42783764e757SAlex Crichton             call_context: CallContext::default(),
42793764e757SAlex Crichton             caller,
4280065baac4SAlex Crichton             state,
4281fa70f025SJoel Dice         }
4282fa70f025SJoel Dice     }
4283fa70f025SJoel Dice }
4284fa70f025SJoel Dice 
4285fa70f025SJoel Dice impl TableDebug for HostTask {
type_name() -> &'static str4286fa70f025SJoel Dice     fn type_name() -> &'static str {
4287fa70f025SJoel Dice         "HostTask"
4288fa70f025SJoel Dice     }
4289fa70f025SJoel Dice }
4290fa70f025SJoel Dice 
4291b856261dSJoel Dice type CallbackFn = Box<dyn Fn(&mut dyn VMStore, Event, u32) -> Result<u32> + Send + Sync + 'static>;
4292fa70f025SJoel Dice 
4293fa70f025SJoel Dice /// Represents the caller of a given guest task.
4294fa70f025SJoel Dice enum Caller {
4295fa70f025SJoel Dice     /// The host called the guest task.
4296fa70f025SJoel Dice     Host {
4297fa70f025SJoel Dice         /// If present, may be used to deliver the result.
4298fa70f025SJoel Dice         tx: Option<oneshot::Sender<LiftedResult>>,
4299e06fbf70SSy Brand         /// If true, there's a host future that must be dropped before the task
4300e06fbf70SSy Brand         /// can be deleted.
4301e06fbf70SSy Brand         host_future_present: bool,
43023764e757SAlex Crichton         /// Represents the caller of the host function which called back into a
43033764e757SAlex Crichton         /// guest. Note that this thread could belong to an entirely unrelated
43043764e757SAlex Crichton         /// top-level component instance than the one the host called into.
43053764e757SAlex Crichton         caller: CurrentThread,
4306fa70f025SJoel Dice     },
4307e06fbf70SSy Brand     /// Another guest thread called the guest task
4308fa70f025SJoel Dice     Guest {
4309fa70f025SJoel Dice         /// The id of the caller
4310e06fbf70SSy Brand         thread: QualifiedThreadId,
4311fa70f025SJoel Dice     },
4312fa70f025SJoel Dice }
4313fa70f025SJoel Dice 
4314fa70f025SJoel Dice /// Represents a closure and related canonical ABI parameters required to
4315fa70f025SJoel Dice /// validate a `task.return` call at runtime and lift the result.
4316fa70f025SJoel Dice struct LiftResult {
4317fa70f025SJoel Dice     lift: RawLift,
4318fa70f025SJoel Dice     ty: TypeTupleIndex,
4319fa70f025SJoel Dice     memory: Option<SendSyncPtr<VMMemoryDefinition>>,
4320fa70f025SJoel Dice     string_encoding: StringEncoding,
4321fa70f025SJoel Dice }
4322fa70f025SJoel Dice 
4323e06fbf70SSy Brand /// The table ID for a guest thread, qualified by the task to which it belongs.
4324e06fbf70SSy Brand ///
4325e06fbf70SSy Brand /// This exists to minimize table lookups and the necessity to pass stores around mutably
4326e06fbf70SSy Brand /// for the common case of identifying the task to which a thread belongs.
4327e06fbf70SSy Brand #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
4328e4894952SSy Brand pub(crate) struct QualifiedThreadId {
4329e06fbf70SSy Brand     task: TableId<GuestTask>,
4330e06fbf70SSy Brand     thread: TableId<GuestThread>,
4331e06fbf70SSy Brand }
4332e06fbf70SSy Brand 
4333e06fbf70SSy Brand impl QualifiedThreadId {
qualify( state: &mut ConcurrentState, thread: TableId<GuestThread>, ) -> Result<QualifiedThreadId>4334e06fbf70SSy Brand     fn qualify(
4335e06fbf70SSy Brand         state: &mut ConcurrentState,
4336e06fbf70SSy Brand         thread: TableId<GuestThread>,
4337e06fbf70SSy Brand     ) -> Result<QualifiedThreadId> {
4338e06fbf70SSy Brand         Ok(QualifiedThreadId {
4339e06fbf70SSy Brand             task: state.get_mut(thread)?.parent_task,
4340e06fbf70SSy Brand             thread,
4341e06fbf70SSy Brand         })
4342e06fbf70SSy Brand     }
4343e06fbf70SSy Brand }
4344e06fbf70SSy Brand 
4345e06fbf70SSy Brand impl fmt::Debug for QualifiedThreadId {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result4346e06fbf70SSy Brand     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4347e06fbf70SSy Brand         f.debug_tuple("QualifiedThreadId")
4348e06fbf70SSy Brand             .field(&self.task.rep())
4349e06fbf70SSy Brand             .field(&self.thread.rep())
4350e06fbf70SSy Brand             .finish()
4351e06fbf70SSy Brand     }
4352e06fbf70SSy Brand }
4353e06fbf70SSy Brand 
4354e06fbf70SSy Brand enum GuestThreadState {
4355e06fbf70SSy Brand     NotStartedImplicit,
4356e06fbf70SSy Brand     NotStartedExplicit(
4357e06fbf70SSy Brand         Box<dyn FnOnce(&mut dyn VMStore, QualifiedThreadId) -> Result<()> + Send + Sync>,
4358e06fbf70SSy Brand     ),
4359e06fbf70SSy Brand     Running,
4360e06fbf70SSy Brand     Suspended(StoreFiber<'static>),
4361d2fbd2deSAlex Crichton     Ready(StoreFiber<'static>),
4362e06fbf70SSy Brand     Completed,
4363e06fbf70SSy Brand }
4364e06fbf70SSy Brand pub struct GuestThread {
4365e06fbf70SSy Brand     /// Context-local state used to implement the `context.{get,set}`
4366e06fbf70SSy Brand     /// intrinsics.
4367e06fbf70SSy Brand     context: [u32; 2],
4368e06fbf70SSy Brand     /// The owning guest task.
4369e06fbf70SSy Brand     parent_task: TableId<GuestTask>,
4370e06fbf70SSy Brand     /// If present, indicates that the thread is currently waiting on the
4371e06fbf70SSy Brand     /// specified set but may be cancelled and woken immediately.
4372e06fbf70SSy Brand     wake_on_cancel: Option<TableId<WaitableSet>>,
4373e06fbf70SSy Brand     /// The execution state of this guest thread
4374e06fbf70SSy Brand     state: GuestThreadState,
4375e06fbf70SSy Brand     /// The index of this thread in the component instance's handle table.
4376e06fbf70SSy Brand     /// This must always be `Some` after initialization.
4377e06fbf70SSy Brand     instance_rep: Option<u32>,
437835887491SSy Brand     /// Scratch waitable set used to watch subtasks during synchronous calls.
437935887491SSy Brand     sync_call_set: TableId<WaitableSet>,
4380e06fbf70SSy Brand }
4381e06fbf70SSy Brand 
4382e06fbf70SSy Brand impl GuestThread {
4383e06fbf70SSy Brand     /// Retrieve the `GuestThread` corresponding to the specified guest-visible
4384e06fbf70SSy Brand     /// handle.
from_instance( state: Pin<&mut ComponentInstance>, caller_instance: RuntimeComponentInstanceIndex, guest_thread: u32, ) -> Result<TableId<Self>>4385e06fbf70SSy Brand     fn from_instance(
4386e06fbf70SSy Brand         state: Pin<&mut ComponentInstance>,
4387e06fbf70SSy Brand         caller_instance: RuntimeComponentInstanceIndex,
4388e06fbf70SSy Brand         guest_thread: u32,
4389e06fbf70SSy Brand     ) -> Result<TableId<Self>> {
4390cb97ae85SJoel Dice         let rep = state.instance_states().0[caller_instance]
439157f899c4SAlex Crichton             .thread_handle_table()
4392cb97ae85SJoel Dice             .guest_thread_rep(guest_thread)?;
4393e06fbf70SSy Brand         Ok(TableId::new(rep))
4394e06fbf70SSy Brand     }
4395e06fbf70SSy Brand 
new_implicit(state: &mut ConcurrentState, parent_task: TableId<GuestTask>) -> Result<Self>439635887491SSy Brand     fn new_implicit(state: &mut ConcurrentState, parent_task: TableId<GuestTask>) -> Result<Self> {
439735887491SSy Brand         let sync_call_set = state.push(WaitableSet::default())?;
439835887491SSy Brand         Ok(Self {
4399e06fbf70SSy Brand             context: [0; 2],
4400e06fbf70SSy Brand             parent_task,
4401e06fbf70SSy Brand             wake_on_cancel: None,
4402e06fbf70SSy Brand             state: GuestThreadState::NotStartedImplicit,
4403e06fbf70SSy Brand             instance_rep: None,
440435887491SSy Brand             sync_call_set,
440535887491SSy Brand         })
4406e06fbf70SSy Brand     }
4407e06fbf70SSy Brand 
new_explicit( state: &mut ConcurrentState, parent_task: TableId<GuestTask>, start_func: Box< dyn FnOnce(&mut dyn VMStore, QualifiedThreadId) -> Result<()> + Send + Sync, >, ) -> Result<Self>4408e06fbf70SSy Brand     fn new_explicit(
440935887491SSy Brand         state: &mut ConcurrentState,
4410e06fbf70SSy Brand         parent_task: TableId<GuestTask>,
4411e06fbf70SSy Brand         start_func: Box<
4412e06fbf70SSy Brand             dyn FnOnce(&mut dyn VMStore, QualifiedThreadId) -> Result<()> + Send + Sync,
4413e06fbf70SSy Brand         >,
441435887491SSy Brand     ) -> Result<Self> {
441535887491SSy Brand         let sync_call_set = state.push(WaitableSet::default())?;
441635887491SSy Brand         Ok(Self {
4417e06fbf70SSy Brand             context: [0; 2],
4418e06fbf70SSy Brand             parent_task,
4419e06fbf70SSy Brand             wake_on_cancel: None,
4420e06fbf70SSy Brand             state: GuestThreadState::NotStartedExplicit(start_func),
4421e06fbf70SSy Brand             instance_rep: None,
442235887491SSy Brand             sync_call_set,
442335887491SSy Brand         })
4424e06fbf70SSy Brand     }
4425e06fbf70SSy Brand }
4426e06fbf70SSy Brand 
4427e06fbf70SSy Brand impl TableDebug for GuestThread {
type_name() -> &'static str4428e06fbf70SSy Brand     fn type_name() -> &'static str {
4429e06fbf70SSy Brand         "GuestThread"
4430e06fbf70SSy Brand     }
4431e06fbf70SSy Brand }
4432e06fbf70SSy Brand 
4433e06fbf70SSy Brand enum SyncResult {
4434e06fbf70SSy Brand     NotProduced,
4435e06fbf70SSy Brand     Produced(Option<ValRaw>),
4436e06fbf70SSy Brand     Taken,
4437e06fbf70SSy Brand }
4438e06fbf70SSy Brand 
4439e06fbf70SSy Brand impl SyncResult {
take(&mut self) -> Result<Option<Option<ValRaw>>>4440da093747SAlex Crichton     fn take(&mut self) -> Result<Option<Option<ValRaw>>> {
4441da093747SAlex Crichton         Ok(match mem::replace(self, SyncResult::Taken) {
4442e06fbf70SSy Brand             SyncResult::NotProduced => None,
4443e06fbf70SSy Brand             SyncResult::Produced(val) => Some(val),
4444e06fbf70SSy Brand             SyncResult::Taken => {
4445da093747SAlex Crichton                 bail_bug!("attempted to take a synchronous result that was already taken")
4446e06fbf70SSy Brand             }
4447da093747SAlex Crichton         })
4448e06fbf70SSy Brand     }
4449e06fbf70SSy Brand }
4450e06fbf70SSy Brand 
4451e06fbf70SSy Brand #[derive(Debug)]
4452e06fbf70SSy Brand enum HostFutureState {
4453e06fbf70SSy Brand     NotApplicable,
4454e06fbf70SSy Brand     Live,
4455e06fbf70SSy Brand     Dropped,
4456e06fbf70SSy Brand }
4457e06fbf70SSy Brand 
4458fa70f025SJoel Dice /// Represents a pending guest task.
4459e06fbf70SSy Brand pub(crate) struct GuestTask {
4460fa70f025SJoel Dice     /// See `WaitableCommon`
4461fa70f025SJoel Dice     common: WaitableCommon,
4462fa70f025SJoel Dice     /// Closure to lower the parameters passed to this task.
4463fa70f025SJoel Dice     lower_params: Option<RawLower>,
4464fa70f025SJoel Dice     /// See `LiftResult`
4465fa70f025SJoel Dice     lift_result: Option<LiftResult>,
4466fa70f025SJoel Dice     /// A place to stash the type-erased lifted result if it can't be delivered
4467fa70f025SJoel Dice     /// immediately.
4468fa70f025SJoel Dice     result: Option<LiftedResult>,
4469fa70f025SJoel Dice     /// Closure to call the callback function for an async-lifted export, if
4470fa70f025SJoel Dice     /// provided.
4471fa70f025SJoel Dice     callback: Option<CallbackFn>,
4472fa70f025SJoel Dice     /// See `Caller`
4473fa70f025SJoel Dice     caller: Caller,
44743764e757SAlex Crichton     /// Borrow state for this task.
44753764e757SAlex Crichton     ///
44763764e757SAlex Crichton     /// Keeps track of `borrow<T>` received to this task to ensure that
44773764e757SAlex Crichton     /// everything is dropped by the time it exits.
44783764e757SAlex Crichton     call_context: CallContext,
4479fa70f025SJoel Dice     /// A place to stash the lowered result for a sync-to-async call until it
4480fa70f025SJoel Dice     /// can be returned to the caller.
4481e06fbf70SSy Brand     sync_result: SyncResult,
4482fa70f025SJoel Dice     /// Whether or not the task has been cancelled (i.e. whether the task is
4483fa70f025SJoel Dice     /// permitted to call `task.cancel`).
4484fa70f025SJoel Dice     cancel_sent: bool,
4485fa70f025SJoel Dice     /// Whether or not we've sent a `Status::Starting` event to any current or
4486fa70f025SJoel Dice     /// future waiters for this waitable.
4487fa70f025SJoel Dice     starting_sent: bool,
4488cb97ae85SJoel Dice     /// The runtime instance to which the exported function for this guest task
4489cb97ae85SJoel Dice     /// belongs.
4490fa70f025SJoel Dice     ///
4491fa70f025SJoel Dice     /// Note that the task may do a sync->sync call via a fused adapter which
4492fa70f025SJoel Dice     /// results in that task executing code in a different instance, and it may
4493fa70f025SJoel Dice     /// call host functions and intrinsics from that other instance.
4494cb97ae85SJoel Dice     instance: RuntimeInstance,
4495fa70f025SJoel Dice     /// If present, a pending `Event::None` or `Event::Cancelled` to be
4496fa70f025SJoel Dice     /// delivered to this task.
4497fa70f025SJoel Dice     event: Option<Event>,
4498fa70f025SJoel Dice     /// Whether or not the task has exited.
4499fa70f025SJoel Dice     exited: bool,
4500e06fbf70SSy Brand     /// Threads belonging to this task
4501e06fbf70SSy Brand     threads: HashSet<TableId<GuestThread>>,
4502e06fbf70SSy Brand     /// The state of the host future that represents an async task, which must
4503e06fbf70SSy Brand     /// be dropped before we can delete the task.
4504e06fbf70SSy Brand     host_future_state: HostFutureState,
45058992b99bSJoel Dice     /// Indicates whether this task was created for a call to an async-lifted
45068992b99bSJoel Dice     /// export.
45078992b99bSJoel Dice     async_function: bool,
4508fa70f025SJoel Dice }
4509fa70f025SJoel Dice 
4510fa70f025SJoel Dice impl GuestTask {
already_lowered_parameters(&self) -> bool4511e06fbf70SSy Brand     fn already_lowered_parameters(&self) -> bool {
4512e06fbf70SSy Brand         // We reset `lower_params` after we lower the parameters
4513e06fbf70SSy Brand         self.lower_params.is_none()
4514e06fbf70SSy Brand     }
4515cb97ae85SJoel Dice 
returned_or_cancelled(&self) -> bool4516e06fbf70SSy Brand     fn returned_or_cancelled(&self) -> bool {
4517e06fbf70SSy Brand         // We reset `lift_result` after we return or exit
4518e06fbf70SSy Brand         self.lift_result.is_none()
4519e06fbf70SSy Brand     }
4520cb97ae85SJoel Dice 
ready_to_delete(&self) -> bool4521e06fbf70SSy Brand     fn ready_to_delete(&self) -> bool {
4522e06fbf70SSy Brand         let threads_completed = self.threads.is_empty();
4523e06fbf70SSy Brand         let has_sync_result = matches!(self.sync_result, SyncResult::Produced(_));
4524e06fbf70SSy Brand         let pending_completion_event = matches!(
4525e06fbf70SSy Brand             self.common.event,
4526e06fbf70SSy Brand             Some(Event::Subtask {
4527e06fbf70SSy Brand                 status: Status::Returned | Status::ReturnCancelled
4528e06fbf70SSy Brand             })
4529e06fbf70SSy Brand         );
4530e06fbf70SSy Brand         let ready = threads_completed
4531e06fbf70SSy Brand             && !has_sync_result
4532e06fbf70SSy Brand             && !pending_completion_event
4533e06fbf70SSy Brand             && !matches!(self.host_future_state, HostFutureState::Live);
4534e06fbf70SSy Brand         log::trace!(
4535e06fbf70SSy Brand             "ready to delete? {ready} (threads_completed: {}, has_sync_result: {}, pending_completion_event: {}, host_future_state: {:?})",
4536e06fbf70SSy Brand             threads_completed,
4537e06fbf70SSy Brand             has_sync_result,
4538e06fbf70SSy Brand             pending_completion_event,
4539e06fbf70SSy Brand             self.host_future_state
4540e06fbf70SSy Brand         );
4541e06fbf70SSy Brand         ready
4542e06fbf70SSy Brand     }
4543cb97ae85SJoel Dice 
new( lower_params: RawLower, lift_result: LiftResult, caller: Caller, callback: Option<CallbackFn>, instance: RuntimeInstance, async_function: bool, ) -> Result<Self>4544fa70f025SJoel Dice     fn new(
4545fa70f025SJoel Dice         lower_params: RawLower,
4546fa70f025SJoel Dice         lift_result: LiftResult,
4547fa70f025SJoel Dice         caller: Caller,
4548fa70f025SJoel Dice         callback: Option<CallbackFn>,
4549b856261dSJoel Dice         instance: RuntimeInstance,
45508992b99bSJoel Dice         async_function: bool,
4551fa70f025SJoel Dice     ) -> Result<Self> {
4552e06fbf70SSy Brand         let host_future_state = match &caller {
4553e06fbf70SSy Brand             Caller::Guest { .. } => HostFutureState::NotApplicable,
4554e06fbf70SSy Brand             Caller::Host {
4555e06fbf70SSy Brand                 host_future_present,
4556e06fbf70SSy Brand                 ..
4557e06fbf70SSy Brand             } => {
4558e06fbf70SSy Brand                 if *host_future_present {
4559e06fbf70SSy Brand                     HostFutureState::Live
4560e06fbf70SSy Brand                 } else {
4561e06fbf70SSy Brand                     HostFutureState::NotApplicable
4562e06fbf70SSy Brand                 }
4563e06fbf70SSy Brand             }
4564e06fbf70SSy Brand         };
4565fa70f025SJoel Dice         Ok(Self {
4566fa70f025SJoel Dice             common: WaitableCommon::default(),
4567fa70f025SJoel Dice             lower_params: Some(lower_params),
4568fa70f025SJoel Dice             lift_result: Some(lift_result),
4569fa70f025SJoel Dice             result: None,
4570fa70f025SJoel Dice             callback,
4571fa70f025SJoel Dice             caller,
45723764e757SAlex Crichton             call_context: CallContext::default(),
4573e06fbf70SSy Brand             sync_result: SyncResult::NotProduced,
4574fa70f025SJoel Dice             cancel_sent: false,
4575fa70f025SJoel Dice             starting_sent: false,
4576b856261dSJoel Dice             instance,
4577fa70f025SJoel Dice             event: None,
4578fa70f025SJoel Dice             exited: false,
4579e06fbf70SSy Brand             threads: HashSet::new(),
4580e06fbf70SSy Brand             host_future_state,
45818992b99bSJoel Dice             async_function,
4582fa70f025SJoel Dice         })
4583fa70f025SJoel Dice     }
4584fa70f025SJoel Dice 
458535887491SSy Brand     /// Dispose of this guest task.
dispose(self, _state: &mut ConcurrentState) -> Result<()>458635887491SSy Brand     fn dispose(self, _state: &mut ConcurrentState) -> Result<()> {
4587e06fbf70SSy Brand         assert!(self.threads.is_empty());
4588fa70f025SJoel Dice         Ok(())
4589fa70f025SJoel Dice     }
4590fa70f025SJoel Dice }
4591fa70f025SJoel Dice 
4592fa70f025SJoel Dice impl TableDebug for GuestTask {
type_name() -> &'static str4593fa70f025SJoel Dice     fn type_name() -> &'static str {
4594fa70f025SJoel Dice         "GuestTask"
4595fa70f025SJoel Dice     }
4596fa70f025SJoel Dice }
4597fa70f025SJoel Dice 
4598fa70f025SJoel Dice /// Represents state common to all kinds of waitables.
4599fa70f025SJoel Dice #[derive(Default)]
4600fa70f025SJoel Dice struct WaitableCommon {
4601fa70f025SJoel Dice     /// The currently pending event for this waitable, if any.
4602fa70f025SJoel Dice     event: Option<Event>,
4603fa70f025SJoel Dice     /// The set to which this waitable belongs, if any.
4604fa70f025SJoel Dice     set: Option<TableId<WaitableSet>>,
4605e8189549SJoel Dice     /// The handle with which the guest refers to this waitable, if any.
4606e8189549SJoel Dice     handle: Option<u32>,
4607fa70f025SJoel Dice }
4608fa70f025SJoel Dice 
4609fa70f025SJoel Dice /// Represents a Component Model Async `waitable`.
4610fa70f025SJoel Dice #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
4611fa70f025SJoel Dice enum Waitable {
4612fa70f025SJoel Dice     /// A host task
4613fa70f025SJoel Dice     Host(TableId<HostTask>),
4614fa70f025SJoel Dice     /// A guest task
4615fa70f025SJoel Dice     Guest(TableId<GuestTask>),
4616fa70f025SJoel Dice     /// The read or write end of a stream or future
4617fa70f025SJoel Dice     Transmit(TableId<TransmitHandle>),
4618fa70f025SJoel Dice }
4619fa70f025SJoel Dice 
4620fa70f025SJoel Dice impl Waitable {
4621fa70f025SJoel Dice     /// Retrieve the `Waitable` corresponding to the specified guest-visible
4622fa70f025SJoel Dice     /// handle.
from_instance( state: Pin<&mut ComponentInstance>, caller_instance: RuntimeComponentInstanceIndex, waitable: u32, ) -> Result<Self>4623fa70f025SJoel Dice     fn from_instance(
4624e8189549SJoel Dice         state: Pin<&mut ComponentInstance>,
4625fa70f025SJoel Dice         caller_instance: RuntimeComponentInstanceIndex,
4626fa70f025SJoel Dice         waitable: u32,
4627fa70f025SJoel Dice     ) -> Result<Self> {
4628e8189549SJoel Dice         use crate::runtime::vm::component::Waitable;
4629fa70f025SJoel Dice 
4630cb97ae85SJoel Dice         let (waitable, kind) = state.instance_states().0[caller_instance]
4631cb97ae85SJoel Dice             .handle_table()
4632cb97ae85SJoel Dice             .waitable_rep(waitable)?;
4633e8189549SJoel Dice 
4634e8189549SJoel Dice         Ok(match kind {
4635e8189549SJoel Dice             Waitable::Subtask { is_host: true } => Self::Host(TableId::new(waitable)),
4636e8189549SJoel Dice             Waitable::Subtask { is_host: false } => Self::Guest(TableId::new(waitable)),
4637e8189549SJoel Dice             Waitable::Stream | Waitable::Future => Self::Transmit(TableId::new(waitable)),
4638fa70f025SJoel Dice         })
4639fa70f025SJoel Dice     }
4640fa70f025SJoel Dice 
4641fa70f025SJoel Dice     /// Retrieve the host-visible identifier for this `Waitable`.
rep(&self) -> u324642fa70f025SJoel Dice     fn rep(&self) -> u32 {
4643fa70f025SJoel Dice         match self {
4644fa70f025SJoel Dice             Self::Host(id) => id.rep(),
4645fa70f025SJoel Dice             Self::Guest(id) => id.rep(),
4646fa70f025SJoel Dice             Self::Transmit(id) => id.rep(),
4647fa70f025SJoel Dice         }
4648fa70f025SJoel Dice     }
4649fa70f025SJoel Dice 
4650fa70f025SJoel Dice     /// Move this `Waitable` to the specified set (when `set` is `Some(_)`) or
4651fa70f025SJoel Dice     /// remove it from any set it may currently belong to (when `set` is
4652fa70f025SJoel Dice     /// `None`).
join(&self, state: &mut ConcurrentState, set: Option<TableId<WaitableSet>>) -> Result<()>4653fa70f025SJoel Dice     fn join(&self, state: &mut ConcurrentState, set: Option<TableId<WaitableSet>>) -> Result<()> {
46541a0f9538SJoel Dice         log::trace!("waitable {self:?} join set {set:?}",);
46551a0f9538SJoel Dice 
4656fa70f025SJoel Dice         let old = mem::replace(&mut self.common(state)?.set, set);
4657fa70f025SJoel Dice 
4658fa70f025SJoel Dice         if let Some(old) = old {
4659fa70f025SJoel Dice             match *self {
4660fa70f025SJoel Dice                 Waitable::Host(id) => state.remove_child(id, old),
4661fa70f025SJoel Dice                 Waitable::Guest(id) => state.remove_child(id, old),
4662fa70f025SJoel Dice                 Waitable::Transmit(id) => state.remove_child(id, old),
4663fa70f025SJoel Dice             }?;
4664fa70f025SJoel Dice 
4665fa70f025SJoel Dice             state.get_mut(old)?.ready.remove(self);
4666fa70f025SJoel Dice         }
4667fa70f025SJoel Dice 
4668fa70f025SJoel Dice         if let Some(set) = set {
4669fa70f025SJoel Dice             match *self {
4670fa70f025SJoel Dice                 Waitable::Host(id) => state.add_child(id, set),
4671fa70f025SJoel Dice                 Waitable::Guest(id) => state.add_child(id, set),
4672fa70f025SJoel Dice                 Waitable::Transmit(id) => state.add_child(id, set),
4673fa70f025SJoel Dice             }?;
4674fa70f025SJoel Dice 
4675fa70f025SJoel Dice             if self.common(state)?.event.is_some() {
4676fa70f025SJoel Dice                 self.mark_ready(state)?;
4677fa70f025SJoel Dice             }
4678fa70f025SJoel Dice         }
4679fa70f025SJoel Dice 
4680fa70f025SJoel Dice         Ok(())
4681fa70f025SJoel Dice     }
4682fa70f025SJoel Dice 
4683fa70f025SJoel Dice     /// Retrieve mutable access to the `WaitableCommon` for this `Waitable`.
common<'a>(&self, state: &'a mut ConcurrentState) -> Result<&'a mut WaitableCommon>4684fa70f025SJoel Dice     fn common<'a>(&self, state: &'a mut ConcurrentState) -> Result<&'a mut WaitableCommon> {
4685fa70f025SJoel Dice         Ok(match self {
4686fa70f025SJoel Dice             Self::Host(id) => &mut state.get_mut(*id)?.common,
4687fa70f025SJoel Dice             Self::Guest(id) => &mut state.get_mut(*id)?.common,
4688fa70f025SJoel Dice             Self::Transmit(id) => &mut state.get_mut(*id)?.common,
4689fa70f025SJoel Dice         })
4690fa70f025SJoel Dice     }
4691fa70f025SJoel Dice 
4692fa70f025SJoel Dice     /// Set or clear the pending event for this waitable and either deliver it
4693fa70f025SJoel Dice     /// to the first waiter, if any, or mark it as ready to be delivered to the
4694fa70f025SJoel Dice     /// next waiter that arrives.
set_event(&self, state: &mut ConcurrentState, event: Option<Event>) -> Result<()>4695fa70f025SJoel Dice     fn set_event(&self, state: &mut ConcurrentState, event: Option<Event>) -> Result<()> {
4696fa70f025SJoel Dice         log::trace!("set event for {self:?}: {event:?}");
4697fa70f025SJoel Dice         self.common(state)?.event = event;
4698fa70f025SJoel Dice         self.mark_ready(state)
4699fa70f025SJoel Dice     }
4700fa70f025SJoel Dice 
4701fa70f025SJoel Dice     /// Take the pending event from this waitable, leaving `None` in its place.
take_event(&self, state: &mut ConcurrentState) -> Result<Option<Event>>4702fa70f025SJoel Dice     fn take_event(&self, state: &mut ConcurrentState) -> Result<Option<Event>> {
4703fa70f025SJoel Dice         let common = self.common(state)?;
4704fa70f025SJoel Dice         let event = common.event.take();
4705fa70f025SJoel Dice         if let Some(set) = self.common(state)?.set {
4706fa70f025SJoel Dice             state.get_mut(set)?.ready.remove(self);
4707fa70f025SJoel Dice         }
4708e06fbf70SSy Brand 
4709fa70f025SJoel Dice         Ok(event)
4710fa70f025SJoel Dice     }
4711fa70f025SJoel Dice 
4712fa70f025SJoel Dice     /// Deliver the current event for this waitable to the first waiter, if any,
4713fa70f025SJoel Dice     /// or else mark it as ready to be delivered to the next waiter that
4714fa70f025SJoel Dice     /// arrives.
mark_ready(&self, state: &mut ConcurrentState) -> Result<()>4715fa70f025SJoel Dice     fn mark_ready(&self, state: &mut ConcurrentState) -> Result<()> {
4716fa70f025SJoel Dice         if let Some(set) = self.common(state)?.set {
4717fa70f025SJoel Dice             state.get_mut(set)?.ready.insert(*self);
4718e06fbf70SSy Brand             if let Some((thread, mode)) = state.get_mut(set)?.waiting.pop_first() {
4719e06fbf70SSy Brand                 let wake_on_cancel = state.get_mut(thread.thread)?.wake_on_cancel.take();
4720fa70f025SJoel Dice                 assert!(wake_on_cancel.is_none() || wake_on_cancel == Some(set));
4721fa70f025SJoel Dice 
4722fa70f025SJoel Dice                 let item = match mode {
4723fa70f025SJoel Dice                     WaitMode::Fiber(fiber) => WorkItem::ResumeFiber(fiber),
4724d2fbd2deSAlex Crichton                     WaitMode::Callback(instance) => WorkItem::GuestCall(
4725d2fbd2deSAlex Crichton                         state.get_mut(thread.task)?.instance.index,
4726d2fbd2deSAlex Crichton                         GuestCall {
4727e06fbf70SSy Brand                             thread,
47287e39c25eSJoel Dice                             kind: GuestCallKind::DeliverEvent {
47297e39c25eSJoel Dice                                 instance,
47307e39c25eSJoel Dice                                 set: Some(set),
47317e39c25eSJoel Dice                             },
4732d2fbd2deSAlex Crichton                         },
4733d2fbd2deSAlex Crichton                     ),
4734fa70f025SJoel Dice                 };
4735fa70f025SJoel Dice                 state.push_high_priority(item);
4736fa70f025SJoel Dice             }
4737fa70f025SJoel Dice         }
4738fa70f025SJoel Dice         Ok(())
4739fa70f025SJoel Dice     }
4740fa70f025SJoel Dice 
4741fa70f025SJoel Dice     /// Remove this waitable from the instance's rep table.
delete_from(&self, state: &mut ConcurrentState) -> Result<()>4742fa70f025SJoel Dice     fn delete_from(&self, state: &mut ConcurrentState) -> Result<()> {
4743fa70f025SJoel Dice         match self {
4744fa70f025SJoel Dice             Self::Host(task) => {
4745fa70f025SJoel Dice                 log::trace!("delete host task {task:?}");
4746fa70f025SJoel Dice                 state.delete(*task)?;
4747fa70f025SJoel Dice             }
4748fa70f025SJoel Dice             Self::Guest(task) => {
4749fa70f025SJoel Dice                 log::trace!("delete guest task {task:?}");
47501e0b0b46SAlex Crichton                 state.delete(*task)?.dispose(state)?;
4751fa70f025SJoel Dice             }
4752fa70f025SJoel Dice             Self::Transmit(task) => {
4753fa70f025SJoel Dice                 state.delete(*task)?;
4754fa70f025SJoel Dice             }
4755fa70f025SJoel Dice         }
4756fa70f025SJoel Dice 
4757fa70f025SJoel Dice         Ok(())
4758fa70f025SJoel Dice     }
4759fa70f025SJoel Dice }
4760fa70f025SJoel Dice 
4761fa70f025SJoel Dice impl fmt::Debug for Waitable {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result4762fa70f025SJoel Dice     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
4763fa70f025SJoel Dice         match self {
4764fa70f025SJoel Dice             Self::Host(id) => write!(f, "{id:?}"),
4765fa70f025SJoel Dice             Self::Guest(id) => write!(f, "{id:?}"),
4766fa70f025SJoel Dice             Self::Transmit(id) => write!(f, "{id:?}"),
4767fa70f025SJoel Dice         }
4768fa70f025SJoel Dice     }
4769fa70f025SJoel Dice }
4770fa70f025SJoel Dice 
4771fa70f025SJoel Dice /// Represents a Component Model Async `waitable-set`.
4772fa70f025SJoel Dice #[derive(Default)]
4773fa70f025SJoel Dice struct WaitableSet {
4774fa70f025SJoel Dice     /// Which waitables in this set have pending events, if any.
4775fa70f025SJoel Dice     ready: BTreeSet<Waitable>,
4776e06fbf70SSy Brand     /// Which guest threads are currently waiting on this set, if any.
4777e06fbf70SSy Brand     waiting: BTreeMap<QualifiedThreadId, WaitMode>,
4778fa70f025SJoel Dice }
4779fa70f025SJoel Dice 
4780fa70f025SJoel Dice impl TableDebug for WaitableSet {
type_name() -> &'static str4781fa70f025SJoel Dice     fn type_name() -> &'static str {
4782fa70f025SJoel Dice         "WaitableSet"
4783fa70f025SJoel Dice     }
4784fa70f025SJoel Dice }
4785fa70f025SJoel Dice 
4786fa70f025SJoel Dice /// Type-erased closure to lower the parameters for a guest task.
47877e39c25eSJoel Dice type RawLower =
47887e39c25eSJoel Dice     Box<dyn FnOnce(&mut dyn VMStore, &mut [MaybeUninit<ValRaw>]) -> Result<()> + Send + Sync>;
4789fa70f025SJoel Dice 
4790fa70f025SJoel Dice /// Type-erased closure to lift the result for a guest task.
4791fa70f025SJoel Dice type RawLift = Box<
47927e39c25eSJoel Dice     dyn FnOnce(&mut dyn VMStore, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>> + Send + Sync,
4793fa70f025SJoel Dice >;
4794fa70f025SJoel Dice 
4795fa70f025SJoel Dice /// Type erased result of a guest task which may be downcast to the expected
4796fa70f025SJoel Dice /// type by a host caller (or simply ignored in the case of a guest caller; see
4797fa70f025SJoel Dice /// `DummyResult`).
4798fa70f025SJoel Dice type LiftedResult = Box<dyn Any + Send + Sync>;
4799fa70f025SJoel Dice 
4800fa70f025SJoel Dice /// Used to return a result from a `LiftFn` when the actual result has already
4801fa70f025SJoel Dice /// been lowered to a guest task's stack and linear memory.
4802fa70f025SJoel Dice struct DummyResult;
4803fa70f025SJoel Dice 
4804fa70f025SJoel Dice /// Represents the Component Model Async state of a (sub-)component instance.
4805fa70f025SJoel Dice #[derive(Default)]
4806cb97ae85SJoel Dice pub struct ConcurrentInstanceState {
480758c5085aSAlex Crichton     /// Whether backpressure is set for this instance (enabled if >0)
480858c5085aSAlex Crichton     backpressure: u16,
4809fa70f025SJoel Dice     /// Whether this instance can be entered
4810fa70f025SJoel Dice     do_not_enter: bool,
4811fa70f025SJoel Dice     /// Pending calls for this instance which require `Self::backpressure` to be
4812fa70f025SJoel Dice     /// `true` and/or `Self::do_not_enter` to be false before they can proceed.
4813e06fbf70SSy Brand     pending: BTreeMap<QualifiedThreadId, GuestCallKind>,
4814fa70f025SJoel Dice }
4815fa70f025SJoel Dice 
4816cb97ae85SJoel Dice impl ConcurrentInstanceState {
pending_is_empty(&self) -> bool4817cb97ae85SJoel Dice     pub fn pending_is_empty(&self) -> bool {
4818cb97ae85SJoel Dice         self.pending.is_empty()
4819cb97ae85SJoel Dice     }
4820cb97ae85SJoel Dice }
4821cb97ae85SJoel Dice 
48223764e757SAlex Crichton #[derive(Debug, Copy, Clone)]
4823e4894952SSy Brand pub(crate) enum CurrentThread {
48243764e757SAlex Crichton     Guest(QualifiedThreadId),
48253764e757SAlex Crichton     Host(TableId<HostTask>),
48263764e757SAlex Crichton     None,
48273764e757SAlex Crichton }
48283764e757SAlex Crichton 
48293764e757SAlex Crichton impl CurrentThread {
guest(&self) -> Option<&QualifiedThreadId>48303764e757SAlex Crichton     fn guest(&self) -> Option<&QualifiedThreadId> {
48313764e757SAlex Crichton         match self {
48323764e757SAlex Crichton             Self::Guest(id) => Some(id),
48333764e757SAlex Crichton             _ => None,
48343764e757SAlex Crichton         }
48353764e757SAlex Crichton     }
48363764e757SAlex Crichton 
host(&self) -> Option<TableId<HostTask>>48373764e757SAlex Crichton     fn host(&self) -> Option<TableId<HostTask>> {
48383764e757SAlex Crichton         match self {
48393764e757SAlex Crichton             Self::Host(id) => Some(*id),
48403764e757SAlex Crichton             _ => None,
48413764e757SAlex Crichton         }
48423764e757SAlex Crichton     }
48433764e757SAlex Crichton 
is_none(&self) -> bool48443764e757SAlex Crichton     fn is_none(&self) -> bool {
48453764e757SAlex Crichton         matches!(self, Self::None)
48463764e757SAlex Crichton     }
48473764e757SAlex Crichton }
48483764e757SAlex Crichton 
48493764e757SAlex Crichton impl From<QualifiedThreadId> for CurrentThread {
from(id: QualifiedThreadId) -> Self48503764e757SAlex Crichton     fn from(id: QualifiedThreadId) -> Self {
48513764e757SAlex Crichton         Self::Guest(id)
48523764e757SAlex Crichton     }
48533764e757SAlex Crichton }
48543764e757SAlex Crichton 
48553764e757SAlex Crichton impl From<TableId<HostTask>> for CurrentThread {
from(id: TableId<HostTask>) -> Self48563764e757SAlex Crichton     fn from(id: TableId<HostTask>) -> Self {
48573764e757SAlex Crichton         Self::Host(id)
48583764e757SAlex Crichton     }
48593764e757SAlex Crichton }
48603764e757SAlex Crichton 
48617e39c25eSJoel Dice /// Represents the Component Model Async state of a store.
4862fa70f025SJoel Dice pub struct ConcurrentState {
48633764e757SAlex Crichton     /// The currently running thread, if any.
48643764e757SAlex Crichton     current_thread: CurrentThread,
4865e06fbf70SSy Brand 
4866fa70f025SJoel Dice     /// The set of pending host and background tasks, if any.
4867fa70f025SJoel Dice     ///
4868fa70f025SJoel Dice     /// See `ComponentInstance::poll_until` for where we temporarily take this
4869fa70f025SJoel Dice     /// out, poll it, then put it back to avoid any mutable aliasing hazards.
4870624c8235SJoel Dice     futures: AlwaysMut<Option<FuturesUnordered<HostTaskFuture>>>,
4871fa70f025SJoel Dice     /// The table of waitables, waitable sets, etc.
4872624c8235SJoel Dice     table: AlwaysMut<ResourceTable>,
4873e06fbf70SSy Brand     /// The "high priority" work queue for this store's event loop.
4874fa70f025SJoel Dice     high_priority: Vec<WorkItem>,
4875e06fbf70SSy Brand     /// The "low priority" work queue for this store's event loop.
4876bde99243SSy Brand     low_priority: VecDeque<WorkItem>,
4877fa70f025SJoel Dice     /// A place to stash the reason a fiber is suspending so that the code which
4878fa70f025SJoel Dice     /// resumed it will know under what conditions the fiber should be resumed
4879fa70f025SJoel Dice     /// again.
4880fa70f025SJoel Dice     suspend_reason: Option<SuspendReason>,
4881fa70f025SJoel Dice     /// A cached fiber which is waiting for work to do.
4882fa70f025SJoel Dice     ///
4883fa70f025SJoel Dice     /// This helps us avoid creating a new fiber for each `GuestCall` work item.
4884fa70f025SJoel Dice     worker: Option<StoreFiber<'static>>,
4885fa70f025SJoel Dice     /// A place to stash the work item for which we're resuming a worker fiber.
4886587ca6f2SJoel Dice     worker_item: Option<WorkerItem>,
4887fa70f025SJoel Dice 
4888fa70f025SJoel Dice     /// Reference counts for all component error contexts
4889fa70f025SJoel Dice     ///
4890fa70f025SJoel Dice     /// NOTE: it is possible the global ref count to be *greater* than the sum of
4891fa70f025SJoel Dice     /// (sub)component ref counts as tracked by `error_context_tables`, for
4892fa70f025SJoel Dice     /// example when the host holds one or more references to error contexts.
4893fa70f025SJoel Dice     ///
4894fa70f025SJoel Dice     /// The key of this primary map is often referred to as the "rep" (i.e. host-side
4895fa70f025SJoel Dice     /// component-wide representation) of the index into concurrent state for a given
4896fa70f025SJoel Dice     /// stored `ErrorContext`.
4897fa70f025SJoel Dice     ///
4898fa70f025SJoel Dice     /// Stated another way, `TypeComponentGlobalErrorContextTableIndex` is essentially the same
4899fa70f025SJoel Dice     /// as a `TableId<ErrorContextState>`.
4900fa70f025SJoel Dice     global_error_context_ref_counts:
4901fa70f025SJoel Dice         BTreeMap<TypeComponentGlobalErrorContextTableIndex, GlobalErrorContextRefCount>,
4902fa70f025SJoel Dice }
4903fa70f025SJoel Dice 
49047e39c25eSJoel Dice impl Default for ConcurrentState {
default() -> Self49057e39c25eSJoel Dice     fn default() -> Self {
4906fa70f025SJoel Dice         Self {
49073764e757SAlex Crichton             current_thread: CurrentThread::None,
4908624c8235SJoel Dice             table: AlwaysMut::new(ResourceTable::new()),
4909624c8235SJoel Dice             futures: AlwaysMut::new(Some(FuturesUnordered::new())),
4910fa70f025SJoel Dice             high_priority: Vec::new(),
4911bde99243SSy Brand             low_priority: VecDeque::new(),
4912fa70f025SJoel Dice             suspend_reason: None,
4913fa70f025SJoel Dice             worker: None,
4914587ca6f2SJoel Dice             worker_item: None,
4915fa70f025SJoel Dice             global_error_context_ref_counts: BTreeMap::new(),
49167e39c25eSJoel Dice         }
4917fa70f025SJoel Dice     }
4918fa70f025SJoel Dice }
4919fa70f025SJoel Dice 
49207e39c25eSJoel Dice impl ConcurrentState {
4921b4475438SJoel Dice     /// Take ownership of any fibers and futures owned by this object.
4922fa70f025SJoel Dice     ///
4923fa70f025SJoel Dice     /// This should be used when disposing of the `Store` containing this object
4924fa70f025SJoel Dice     /// in order to gracefully resolve any and all fibers using
4925fa70f025SJoel Dice     /// `StoreFiber::dispose`.  This is necessary to avoid possible
4926fa70f025SJoel Dice     /// use-after-free bugs due to fibers which may still have access to the
4927fa70f025SJoel Dice     /// `Store`.
4928fa70f025SJoel Dice     ///
4929b4475438SJoel Dice     /// Additionally, the futures collected with this function should be dropped
4930b4475438SJoel Dice     /// within a `tls::set` call, which will ensure than any futures closing
4931b4475438SJoel Dice     /// over an `&Accessor` will have access to the store when dropped, allowing
4932b4475438SJoel Dice     /// e.g. `WithAccessor[AndValue]` instances to be disposed of without
4933b4475438SJoel Dice     /// panicking.
4934b4475438SJoel Dice     ///
4935fa70f025SJoel Dice     /// Note that this will leave the object in an inconsistent and unusable
4936fa70f025SJoel Dice     /// state, so it should only be used just prior to dropping it.
take_fibers_and_futures( &mut self, fibers: &mut Vec<StoreFiber<'static>>, futures: &mut Vec<FuturesUnordered<HostTaskFuture>>, )4937b4475438SJoel Dice     pub(crate) fn take_fibers_and_futures(
4938b4475438SJoel Dice         &mut self,
4939b4475438SJoel Dice         fibers: &mut Vec<StoreFiber<'static>>,
4940b4475438SJoel Dice         futures: &mut Vec<FuturesUnordered<HostTaskFuture>>,
4941b4475438SJoel Dice     ) {
4942624c8235SJoel Dice         for entry in self.table.get_mut().iter_mut() {
4943228515c8SAlex Crichton             if let Some(set) = entry.downcast_mut::<WaitableSet>() {
4944228515c8SAlex Crichton                 for mode in mem::take(&mut set.waiting).into_values() {
4945fa70f025SJoel Dice                     if let WaitMode::Fiber(fiber) = mode {
4946b4475438SJoel Dice                         fibers.push(fiber);
4947fa70f025SJoel Dice                     }
4948fa70f025SJoel Dice                 }
4949e06fbf70SSy Brand             } else if let Some(thread) = entry.downcast_mut::<GuestThread>() {
4950d2fbd2deSAlex Crichton                 if let GuestThreadState::Suspended(fiber) | GuestThreadState::Ready(fiber) =
4951e06fbf70SSy Brand                     mem::replace(&mut thread.state, GuestThreadState::Completed)
4952e06fbf70SSy Brand                 {
4953e06fbf70SSy Brand                     fibers.push(fiber);
4954e06fbf70SSy Brand                 }
4955fa70f025SJoel Dice             }
4956fa70f025SJoel Dice         }
4957fa70f025SJoel Dice 
4958fa70f025SJoel Dice         if let Some(fiber) = self.worker.take() {
4959b4475438SJoel Dice             fibers.push(fiber);
4960fa70f025SJoel Dice         }
4961fa70f025SJoel Dice 
4962bde99243SSy Brand         let mut handle_item = |item| match item {
4963b4475438SJoel Dice             WorkItem::ResumeFiber(fiber) => {
4964b4475438SJoel Dice                 fibers.push(fiber);
4965b4475438SJoel Dice             }
4966b4475438SJoel Dice             WorkItem::PushFuture(future) => {
4967b4475438SJoel Dice                 self.futures
4968b4475438SJoel Dice                     .get_mut()
4969b4475438SJoel Dice                     .as_mut()
4970b4475438SJoel Dice                     .unwrap()
4971624c8235SJoel Dice                     .push(future.into_inner());
4972b4475438SJoel Dice             }
4973da093747SAlex Crichton             WorkItem::ResumeThread(..) | WorkItem::GuestCall(..) | WorkItem::WorkerFunction(..) => {
4974da093747SAlex Crichton             }
4975fa70f025SJoel Dice         };
4976fa70f025SJoel Dice 
4977bde99243SSy Brand         for item in mem::take(&mut self.high_priority) {
4978bde99243SSy Brand             handle_item(item);
4979bde99243SSy Brand         }
4980bde99243SSy Brand         for item in mem::take(&mut self.low_priority) {
4981bde99243SSy Brand             handle_item(item);
4982bde99243SSy Brand         }
4983b4475438SJoel Dice 
4984624c8235SJoel Dice         if let Some(them) = self.futures.get_mut().take() {
4985b4475438SJoel Dice             futures.push(them);
4986b4475438SJoel Dice         }
4987fa70f025SJoel Dice     }
4988e8189549SJoel Dice 
4989bde99243SSy Brand     /// Collect the next set of work items to run. This will be either all
4990bde99243SSy Brand     /// high-priority items, or a single low-priority item if there are no
4991bde99243SSy Brand     /// high-priority items.
collect_work_items_to_run(&mut self) -> Vec<WorkItem>4992bde99243SSy Brand     fn collect_work_items_to_run(&mut self) -> Vec<WorkItem> {
4993bde99243SSy Brand         let mut ready = mem::take(&mut self.high_priority);
4994bde99243SSy Brand         if ready.is_empty() {
4995bde99243SSy Brand             if let Some(item) = self.low_priority.pop_back() {
4996bde99243SSy Brand                 ready.push(item);
4997bde99243SSy Brand             }
4998bde99243SSy Brand         }
4999bde99243SSy Brand         ready
5000bde99243SSy Brand     }
5001bde99243SSy Brand 
push<V: Send + Sync + 'static>( &mut self, value: V, ) -> Result<TableId<V>, ResourceTableError>5002624c8235SJoel Dice     fn push<V: Send + Sync + 'static>(
5003624c8235SJoel Dice         &mut self,
5004624c8235SJoel Dice         value: V,
5005624c8235SJoel Dice     ) -> Result<TableId<V>, ResourceTableError> {
5006624c8235SJoel Dice         self.table.get_mut().push(value).map(TableId::from)
5007e8189549SJoel Dice     }
5008e8189549SJoel Dice 
get_mut<V: 'static>(&mut self, id: TableId<V>) -> Result<&mut V, ResourceTableError>5009624c8235SJoel Dice     fn get_mut<V: 'static>(&mut self, id: TableId<V>) -> Result<&mut V, ResourceTableError> {
5010624c8235SJoel Dice         self.table.get_mut().get_mut(&Resource::from(id))
5011e8189549SJoel Dice     }
5012e8189549SJoel Dice 
add_child<T: 'static, U: 'static>( &mut self, child: TableId<T>, parent: TableId<U>, ) -> Result<(), ResourceTableError>5013624c8235SJoel Dice     pub fn add_child<T: 'static, U: 'static>(
5014e8189549SJoel Dice         &mut self,
5015e8189549SJoel Dice         child: TableId<T>,
5016e8189549SJoel Dice         parent: TableId<U>,
5017624c8235SJoel Dice     ) -> Result<(), ResourceTableError> {
5018624c8235SJoel Dice         self.table
5019624c8235SJoel Dice             .get_mut()
5020624c8235SJoel Dice             .add_child(Resource::from(child), Resource::from(parent))
5021e8189549SJoel Dice     }
5022e8189549SJoel Dice 
remove_child<T: 'static, U: 'static>( &mut self, child: TableId<T>, parent: TableId<U>, ) -> Result<(), ResourceTableError>5023624c8235SJoel Dice     pub fn remove_child<T: 'static, U: 'static>(
5024e8189549SJoel Dice         &mut self,
5025e8189549SJoel Dice         child: TableId<T>,
5026e8189549SJoel Dice         parent: TableId<U>,
5027624c8235SJoel Dice     ) -> Result<(), ResourceTableError> {
5028624c8235SJoel Dice         self.table
5029624c8235SJoel Dice             .get_mut()
5030624c8235SJoel Dice             .remove_child(Resource::from(child), Resource::from(parent))
5031e8189549SJoel Dice     }
5032e8189549SJoel Dice 
delete<V: 'static>(&mut self, id: TableId<V>) -> Result<V, ResourceTableError>5033624c8235SJoel Dice     fn delete<V: 'static>(&mut self, id: TableId<V>) -> Result<V, ResourceTableError> {
5034624c8235SJoel Dice         self.table.get_mut().delete(Resource::from(id))
5035e8189549SJoel Dice     }
5036e8189549SJoel Dice 
push_future(&mut self, future: HostTaskFuture)5037e8189549SJoel Dice     fn push_future(&mut self, future: HostTaskFuture) {
5038e8189549SJoel Dice         // Note that we can't directly push to `ConcurrentState::futures` here
5039e8189549SJoel Dice         // since this may be called from a future that's being polled inside
5040e8189549SJoel Dice         // `Self::poll_until`, which temporarily removes the `FuturesUnordered`
5041e8189549SJoel Dice         // so it has exclusive access while polling it.  Therefore, we push a
5042e8189549SJoel Dice         // work item to the "high priority" queue, which will actually push to
5043e8189549SJoel Dice         // `ConcurrentState::futures` later.
5044624c8235SJoel Dice         self.push_high_priority(WorkItem::PushFuture(AlwaysMut::new(future)));
5045e8189549SJoel Dice     }
5046e8189549SJoel Dice 
push_high_priority(&mut self, item: WorkItem)5047e8189549SJoel Dice     fn push_high_priority(&mut self, item: WorkItem) {
5048e8189549SJoel Dice         log::trace!("push high priority: {item:?}");
5049e8189549SJoel Dice         self.high_priority.push(item);
5050e8189549SJoel Dice     }
5051e8189549SJoel Dice 
push_low_priority(&mut self, item: WorkItem)5052e8189549SJoel Dice     fn push_low_priority(&mut self, item: WorkItem) {
5053e8189549SJoel Dice         log::trace!("push low priority: {item:?}");
5054bde99243SSy Brand         self.low_priority.push_front(item);
5055e8189549SJoel Dice     }
5056e8189549SJoel Dice 
push_work_item(&mut self, item: WorkItem, high_priority: bool)5057e06fbf70SSy Brand     fn push_work_item(&mut self, item: WorkItem, high_priority: bool) {
5058e06fbf70SSy Brand         if high_priority {
5059e06fbf70SSy Brand             self.push_high_priority(item);
5060e06fbf70SSy Brand         } else {
5061e06fbf70SSy Brand             self.push_low_priority(item);
5062e06fbf70SSy Brand         }
5063e06fbf70SSy Brand     }
5064e06fbf70SSy Brand 
promote_instance_local_thread_work_item( &mut self, current_instance: RuntimeComponentInstanceIndex, ) -> bool5065d2fbd2deSAlex Crichton     fn promote_instance_local_thread_work_item(
5066d2fbd2deSAlex Crichton         &mut self,
5067d2fbd2deSAlex Crichton         current_instance: RuntimeComponentInstanceIndex,
5068d2fbd2deSAlex Crichton     ) -> bool {
5069d2fbd2deSAlex Crichton         self.promote_work_items_matching(|item: &WorkItem| match item {
5070d2fbd2deSAlex Crichton             WorkItem::ResumeThread(instance, _) | WorkItem::GuestCall(instance, _) => {
5071d2fbd2deSAlex Crichton                 *instance == current_instance
5072d2fbd2deSAlex Crichton             }
5073d2fbd2deSAlex Crichton             _ => false,
5074d2fbd2deSAlex Crichton         })
5075d2fbd2deSAlex Crichton     }
5076d2fbd2deSAlex Crichton 
promote_thread_work_item(&mut self, thread: QualifiedThreadId) -> bool5077d2fbd2deSAlex Crichton     fn promote_thread_work_item(&mut self, thread: QualifiedThreadId) -> bool {
5078d2fbd2deSAlex Crichton         self.promote_work_items_matching(|item: &WorkItem| match item {
5079d2fbd2deSAlex Crichton             WorkItem::ResumeThread(_, t) | WorkItem::GuestCall(_, GuestCall { thread: t, .. }) => {
5080d2fbd2deSAlex Crichton                 *t == thread
5081d2fbd2deSAlex Crichton             }
5082d2fbd2deSAlex Crichton             _ => false,
5083d2fbd2deSAlex Crichton         })
5084d2fbd2deSAlex Crichton     }
5085d2fbd2deSAlex Crichton 
promote_work_items_matching<F>(&mut self, mut predicate: F) -> bool where F: FnMut(&WorkItem) -> bool,5086d2fbd2deSAlex Crichton     fn promote_work_items_matching<F>(&mut self, mut predicate: F) -> bool
5087d2fbd2deSAlex Crichton     where
5088d2fbd2deSAlex Crichton         F: FnMut(&WorkItem) -> bool,
5089d2fbd2deSAlex Crichton     {
5090d2fbd2deSAlex Crichton         // If there's a high-priority work item to resume the current guest thread,
5091d2fbd2deSAlex Crichton         // we don't need to promote anything, but we return true to indicate that
5092d2fbd2deSAlex Crichton         // work is pending for the current instance.
5093d2fbd2deSAlex Crichton         if self.high_priority.iter().any(&mut predicate) {
5094d2fbd2deSAlex Crichton             true
5095d2fbd2deSAlex Crichton         }
5096d2fbd2deSAlex Crichton         // Otherwise, look for a low-priority work item that matches the current
5097d2fbd2deSAlex Crichton         // instance and promote it to high-priority.
5098d2fbd2deSAlex Crichton         else if let Some(idx) = self.low_priority.iter().position(&mut predicate) {
5099d2fbd2deSAlex Crichton             let item = self.low_priority.remove(idx).unwrap();
5100d2fbd2deSAlex Crichton             self.push_high_priority(item);
5101d2fbd2deSAlex Crichton             true
5102d2fbd2deSAlex Crichton         } else {
5103d2fbd2deSAlex Crichton             false
5104d2fbd2deSAlex Crichton         }
5105d2fbd2deSAlex Crichton     }
5106d2fbd2deSAlex Crichton 
5107e8189549SJoel Dice     /// Implements the `context.get` intrinsic.
context_get(&mut self, slot: u32) -> Result<u32>5108e8189549SJoel Dice     pub(crate) fn context_get(&mut self, slot: u32) -> Result<u32> {
5109da093747SAlex Crichton         let thread = self.current_guest_thread()?;
5110da093747SAlex Crichton         let val = self.get_mut(thread.thread)?.context[usize::try_from(slot)?];
5111e06fbf70SSy Brand         log::trace!("context_get {thread:?} slot {slot} val {val:#x}");
5112e8189549SJoel Dice         Ok(val)
5113e8189549SJoel Dice     }
5114e8189549SJoel Dice 
5115e8189549SJoel Dice     /// Implements the `context.set` intrinsic.
context_set(&mut self, slot: u32, val: u32) -> Result<()>5116e8189549SJoel Dice     pub(crate) fn context_set(&mut self, slot: u32, val: u32) -> Result<()> {
5117da093747SAlex Crichton         let thread = self.current_guest_thread()?;
5118e06fbf70SSy Brand         log::trace!("context_set {thread:?} slot {slot} val {val:#x}");
5119da093747SAlex Crichton         self.get_mut(thread.thread)?.context[usize::try_from(slot)?] = val;
5120e8189549SJoel Dice         Ok(())
5121e8189549SJoel Dice     }
5122e06fbf70SSy Brand 
5123e06fbf70SSy Brand     /// Returns whether there's a pending cancellation on the current guest thread,
5124e06fbf70SSy Brand     /// consuming the event if so.
take_pending_cancellation(&mut self) -> Result<bool>5125da093747SAlex Crichton     fn take_pending_cancellation(&mut self) -> Result<bool> {
5126da093747SAlex Crichton         let thread = self.current_guest_thread()?;
5127da093747SAlex Crichton         if let Some(event) = self.get_mut(thread.task)?.event.take() {
5128e06fbf70SSy Brand             assert!(matches!(event, Event::Cancelled));
5129da093747SAlex Crichton             Ok(true)
5130e06fbf70SSy Brand         } else {
5131da093747SAlex Crichton             Ok(false)
5132e06fbf70SSy Brand         }
5133e06fbf70SSy Brand     }
51348992b99bSJoel Dice 
check_blocking_for(&mut self, task: TableId<GuestTask>) -> Result<()>51358992b99bSJoel Dice     fn check_blocking_for(&mut self, task: TableId<GuestTask>) -> Result<()> {
5136da093747SAlex Crichton         if self.may_block(task)? {
51378992b99bSJoel Dice             Ok(())
51388992b99bSJoel Dice         } else {
51398992b99bSJoel Dice             Err(Trap::CannotBlockSyncTask.into())
51408992b99bSJoel Dice         }
51418992b99bSJoel Dice     }
51428992b99bSJoel Dice 
may_block(&mut self, task: TableId<GuestTask>) -> Result<bool>5143da093747SAlex Crichton     fn may_block(&mut self, task: TableId<GuestTask>) -> Result<bool> {
5144da093747SAlex Crichton         let task = self.get_mut(task)?;
5145da093747SAlex Crichton         Ok(task.async_function || task.returned_or_cancelled())
51468992b99bSJoel Dice     }
51473764e757SAlex Crichton 
51483764e757SAlex Crichton     /// Used by `ResourceTables` to acquire the current `CallContext` for the
51493764e757SAlex Crichton     /// specified task.
51503764e757SAlex Crichton     ///
51513764e757SAlex Crichton     /// The `task` is bit-packed as returned by `current_call_context_scope_id`
51523764e757SAlex Crichton     /// below.
call_context(&mut self, task: u32) -> Result<&mut CallContext>51539661ca85SAlex Crichton     pub fn call_context(&mut self, task: u32) -> Result<&mut CallContext> {
51543764e757SAlex Crichton         let (task, is_host) = (task >> 1, task & 1 == 1);
51553764e757SAlex Crichton         if is_host {
51563764e757SAlex Crichton             let task: TableId<HostTask> = TableId::new(task);
51579661ca85SAlex Crichton             Ok(&mut self.get_mut(task)?.call_context)
51583764e757SAlex Crichton         } else {
51593764e757SAlex Crichton             let task: TableId<GuestTask> = TableId::new(task);
51609661ca85SAlex Crichton             Ok(&mut self.get_mut(task)?.call_context)
51613764e757SAlex Crichton         }
51623764e757SAlex Crichton     }
51633764e757SAlex Crichton 
51643764e757SAlex Crichton     /// Used by `ResourceTables` to record the scope of a borrow to get undone
51653764e757SAlex Crichton     /// in the future.
current_call_context_scope_id(&self) -> Result<u32>51669661ca85SAlex Crichton     pub fn current_call_context_scope_id(&self) -> Result<u32> {
51673764e757SAlex Crichton         let (bits, is_host) = match self.current_thread {
51683764e757SAlex Crichton             CurrentThread::Guest(id) => (id.task.rep(), false),
51693764e757SAlex Crichton             CurrentThread::Host(id) => (id.rep(), true),
51709661ca85SAlex Crichton             CurrentThread::None => bail_bug!("current thread is not set"),
51713764e757SAlex Crichton         };
51723764e757SAlex Crichton         assert_eq!((bits << 1) >> 1, bits);
51739661ca85SAlex Crichton         Ok((bits << 1) | u32::from(is_host))
51743764e757SAlex Crichton     }
51753764e757SAlex Crichton 
current_guest_thread(&self) -> Result<QualifiedThreadId>5176da093747SAlex Crichton     fn current_guest_thread(&self) -> Result<QualifiedThreadId> {
5177da093747SAlex Crichton         match self.current_thread.guest() {
5178da093747SAlex Crichton             Some(id) => Ok(*id),
5179da093747SAlex Crichton             None => bail_bug!("current thread is not a guest thread"),
5180da093747SAlex Crichton         }
51813764e757SAlex Crichton     }
51823764e757SAlex Crichton 
current_host_thread(&self) -> Result<TableId<HostTask>>5183da093747SAlex Crichton     fn current_host_thread(&self) -> Result<TableId<HostTask>> {
5184da093747SAlex Crichton         match self.current_thread.host() {
5185da093747SAlex Crichton             Some(id) => Ok(id),
5186da093747SAlex Crichton             None => bail_bug!("current thread is not a host thread"),
5187da093747SAlex Crichton         }
5188da093747SAlex Crichton     }
5189da093747SAlex Crichton 
futures_mut(&mut self) -> Result<&mut FuturesUnordered<HostTaskFuture>>5190da093747SAlex Crichton     fn futures_mut(&mut self) -> Result<&mut FuturesUnordered<HostTaskFuture>> {
5191da093747SAlex Crichton         match self.futures.get_mut().as_mut() {
5192da093747SAlex Crichton             Some(f) => Ok(f),
5193da093747SAlex Crichton             None => bail_bug!("futures field of concurrent state is currently taken"),
5194da093747SAlex Crichton         }
51953764e757SAlex Crichton     }
51962264f72aSAlex Crichton 
table(&mut self) -> &mut ResourceTable51972264f72aSAlex Crichton     pub(crate) fn table(&mut self) -> &mut ResourceTable {
51982264f72aSAlex Crichton         self.table.get_mut()
51992264f72aSAlex Crichton     }
5200fa70f025SJoel Dice }
5201fa70f025SJoel Dice 
5202fa70f025SJoel Dice /// Provide a type hint to compiler about the shape of a parameter lower
5203fa70f025SJoel Dice /// closure.
for_any_lower< F: FnOnce(&mut dyn VMStore, &mut [MaybeUninit<ValRaw>]) -> Result<()> + Send + Sync, >( fun: F, ) -> F5204fa70f025SJoel Dice fn for_any_lower<
52057e39c25eSJoel Dice     F: FnOnce(&mut dyn VMStore, &mut [MaybeUninit<ValRaw>]) -> Result<()> + Send + Sync,
5206fa70f025SJoel Dice >(
5207fa70f025SJoel Dice     fun: F,
5208fa70f025SJoel Dice ) -> F {
5209fa70f025SJoel Dice     fun
5210fa70f025SJoel Dice }
5211fa70f025SJoel Dice 
5212fa70f025SJoel Dice /// Provide a type hint to compiler about the shape of a result lift closure.
for_any_lift< F: FnOnce(&mut dyn VMStore, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>> + Send + Sync, >( fun: F, ) -> F5213fa70f025SJoel Dice fn for_any_lift<
52147e39c25eSJoel Dice     F: FnOnce(&mut dyn VMStore, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>> + Send + Sync,
5215fa70f025SJoel Dice >(
5216fa70f025SJoel Dice     fun: F,
5217fa70f025SJoel Dice ) -> F {
5218fa70f025SJoel Dice     fun
5219fa70f025SJoel Dice }
5220fa70f025SJoel Dice 
5221fa70f025SJoel Dice /// Wrap the specified future in a `poll_fn` which asserts that the future is
52227e39c25eSJoel Dice /// only polled from the event loop of the specified `Store`.
5223fa70f025SJoel Dice ///
52247e39c25eSJoel Dice /// See `StoreContextMut::run_concurrent` for details.
checked<F: Future + Send + 'static>( id: StoreId, fut: F, ) -> impl Future<Output = F::Output> + Send + 'static5225fa70f025SJoel Dice fn checked<F: Future + Send + 'static>(
52267e39c25eSJoel Dice     id: StoreId,
5227fa70f025SJoel Dice     fut: F,
5228fa70f025SJoel Dice ) -> impl Future<Output = F::Output> + Send + 'static {
5229fa70f025SJoel Dice     async move {
5230fa70f025SJoel Dice         let mut fut = pin!(fut);
5231fa70f025SJoel Dice         future::poll_fn(move |cx| {
5232fa70f025SJoel Dice             let message = "\
5233fa70f025SJoel Dice                 `Future`s which depend on asynchronous component tasks, streams, or \
5234fa70f025SJoel Dice                 futures to complete may only be polled from the event loop of the \
52357e39c25eSJoel Dice                 store to which they belong.  Please use \
52367e39c25eSJoel Dice                 `StoreContextMut::{run_concurrent,spawn}` to poll or await them.\
5237fa70f025SJoel Dice             ";
5238fa70f025SJoel Dice             tls::try_get(|store| {
5239fa70f025SJoel Dice                 let matched = match store {
52407e39c25eSJoel Dice                     tls::TryGet::Some(store) => store.id() == id,
5241fa70f025SJoel Dice                     tls::TryGet::Taken | tls::TryGet::None => false,
5242fa70f025SJoel Dice                 };
5243fa70f025SJoel Dice 
5244fa70f025SJoel Dice                 if !matched {
5245fa70f025SJoel Dice                     panic!("{message}")
5246fa70f025SJoel Dice                 }
5247fa70f025SJoel Dice             });
5248fa70f025SJoel Dice             fut.as_mut().poll(cx)
5249fa70f025SJoel Dice         })
5250fa70f025SJoel Dice         .await
5251fa70f025SJoel Dice     }
5252fa70f025SJoel Dice }
5253fa70f025SJoel Dice 
52547e39c25eSJoel Dice /// Assert that `StoreContextMut::run_concurrent` has not been called from
52557e39c25eSJoel Dice /// within an store's event loop.
check_recursive_run()5256fa70f025SJoel Dice fn check_recursive_run() {
5257fa70f025SJoel Dice     tls::try_get(|store| {
5258fa70f025SJoel Dice         if !matches!(store, tls::TryGet::None) {
52597e39c25eSJoel Dice             panic!("Recursive `StoreContextMut::run_concurrent` calls not supported")
5260fa70f025SJoel Dice         }
5261fa70f025SJoel Dice     });
5262fa70f025SJoel Dice }
5263fa70f025SJoel Dice 
unpack_callback_code(code: u32) -> (u32, u32)5264fa70f025SJoel Dice fn unpack_callback_code(code: u32) -> (u32, u32) {
5265fa70f025SJoel Dice     (code & 0xF, code >> 4)
5266fa70f025SJoel Dice }
5267fa70f025SJoel Dice 
5268fa70f025SJoel Dice /// Helper struct for packaging parameters to be passed to
5269fa70f025SJoel Dice /// `ComponentInstance::waitable_check` for calls to `waitable-set.wait` or
5270fa70f025SJoel Dice /// `waitable-set.poll`.
5271fa70f025SJoel Dice struct WaitableCheckParams {
5272fa70f025SJoel Dice     set: TableId<WaitableSet>,
5273815c10deSAlex Crichton     options: OptionsIndex,
5274815c10deSAlex Crichton     payload: u32,
5275fa70f025SJoel Dice }
5276fa70f025SJoel Dice 
527734ba273bSJoel Dice /// Indicates whether `ComponentInstance::waitable_check` is being called for
527834ba273bSJoel Dice /// `waitable-set.wait` or `waitable-set.poll`.
5279fa70f025SJoel Dice enum WaitableCheck {
528034ba273bSJoel Dice     Wait,
528134ba273bSJoel Dice     Poll,
5282fa70f025SJoel Dice }
5283fa70f025SJoel Dice 
5284fa70f025SJoel Dice /// Represents a guest task called from the host, prepared using `prepare_call`.
5285b221fca7SJoel Dice pub(crate) struct PreparedCall<R> {
5286fa70f025SJoel Dice     /// The guest export to be called
5287fa70f025SJoel Dice     handle: Func,
5288e06fbf70SSy Brand     /// The guest thread created by `prepare_call`
5289e06fbf70SSy Brand     thread: QualifiedThreadId,
5290fa70f025SJoel Dice     /// The number of lowered core Wasm parameters to pass to the call.
5291fa70f025SJoel Dice     param_count: usize,
5292fa70f025SJoel Dice     /// The `oneshot::Receiver` to which the result of the call will be
5293fa70f025SJoel Dice     /// delivered when it is available.
5294fa70f025SJoel Dice     rx: oneshot::Receiver<LiftedResult>,
5295b221fca7SJoel Dice     _phantom: PhantomData<R>,
5296b221fca7SJoel Dice }
5297b221fca7SJoel Dice 
5298fa70f025SJoel Dice impl<R> PreparedCall<R> {
5299fa70f025SJoel Dice     /// Get a copy of the `TaskId` for this `PreparedCall`.
task_id(&self) -> TaskId5300fa70f025SJoel Dice     pub(crate) fn task_id(&self) -> TaskId {
5301e06fbf70SSy Brand         TaskId {
5302e06fbf70SSy Brand             task: self.thread.task,
5303e06fbf70SSy Brand         }
5304fa70f025SJoel Dice     }
5305fa70f025SJoel Dice }
5306fa70f025SJoel Dice 
5307fa70f025SJoel Dice /// Represents a task created by `prepare_call`.
5308fa70f025SJoel Dice pub(crate) struct TaskId {
5309fa70f025SJoel Dice     task: TableId<GuestTask>,
5310fa70f025SJoel Dice }
5311fa70f025SJoel Dice 
5312fa70f025SJoel Dice impl TaskId {
5313e06fbf70SSy Brand     /// The host future for an async task was dropped. If the parameters have not been lowered yet,
5314e06fbf70SSy Brand     /// it is no longer valid to do so, as the lowering closure would see a dangling pointer. In this case,
5315e06fbf70SSy Brand     /// we delete the task eagerly. Otherwise, there may be running threads, or ones that are suspended
5316e06fbf70SSy Brand     /// and can be resumed by other tasks for this component, so we mark the future as dropped
5317e06fbf70SSy Brand     /// and delete the task when all threads are done.
host_future_dropped<T>(&self, store: StoreContextMut<T>) -> Result<()>5318e06fbf70SSy Brand     pub(crate) fn host_future_dropped<T>(&self, store: StoreContextMut<T>) -> Result<()> {
5319e06fbf70SSy Brand         let task = store.0.concurrent_state_mut().get_mut(self.task)?;
5320e06fbf70SSy Brand         if !task.already_lowered_parameters() {
5321e06fbf70SSy Brand             Waitable::Guest(self.task).delete_from(store.0.concurrent_state_mut())?
5322e06fbf70SSy Brand         } else {
5323e06fbf70SSy Brand             task.host_future_state = HostFutureState::Dropped;
5324e06fbf70SSy Brand             if task.ready_to_delete() {
5325e06fbf70SSy Brand                 Waitable::Guest(self.task).delete_from(store.0.concurrent_state_mut())?
5326e06fbf70SSy Brand             }
5327e06fbf70SSy Brand         }
5328e06fbf70SSy Brand         Ok(())
5329fa70f025SJoel Dice     }
5330fa70f025SJoel Dice }
5331fa70f025SJoel Dice 
5332b221fca7SJoel Dice /// Prepare a call to the specified exported Wasm function, providing functions
5333b221fca7SJoel Dice /// for lowering the parameters and lifting the result.
5334b221fca7SJoel Dice ///
5335b221fca7SJoel Dice /// To enqueue the returned `PreparedCall` in the `ComponentInstance`'s event
5336b221fca7SJoel Dice /// loop, use `queue_call`.
prepare_call<T, R>( mut store: StoreContextMut<T>, handle: Func, param_count: usize, host_future_present: bool, lower_params: impl FnOnce(Func, StoreContextMut<T>, &mut [MaybeUninit<ValRaw>]) -> Result<()> + Send + Sync + 'static, lift_result: impl FnOnce(Func, &mut StoreOpaque, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>> + Send + Sync + 'static, ) -> Result<PreparedCall<R>>5337b221fca7SJoel Dice pub(crate) fn prepare_call<T, R>(
5338b221fca7SJoel Dice     mut store: StoreContextMut<T>,
5339fa70f025SJoel Dice     handle: Func,
5340fa70f025SJoel Dice     param_count: usize,
5341e06fbf70SSy Brand     host_future_present: bool,
5342b221fca7SJoel Dice     lower_params: impl FnOnce(Func, StoreContextMut<T>, &mut [MaybeUninit<ValRaw>]) -> Result<()>
5343b221fca7SJoel Dice     + Send
5344b221fca7SJoel Dice     + Sync
5345b221fca7SJoel Dice     + 'static,
5346b221fca7SJoel Dice     lift_result: impl FnOnce(Func, &mut StoreOpaque, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>>
5347b221fca7SJoel Dice     + Send
5348b221fca7SJoel Dice     + Sync
5349b221fca7SJoel Dice     + 'static,
5350b221fca7SJoel Dice ) -> Result<PreparedCall<R>> {
5351fa70f025SJoel Dice     let (options, _flags, ty, raw_options) = handle.abi_info(store.0);
5352fa70f025SJoel Dice 
5353fa70f025SJoel Dice     let instance = handle.instance().id().get(store.0);
5354ec9b62abSAlex Crichton     let options = &instance.component().env_component().options[options];
53558992b99bSJoel Dice     let ty = &instance.component().types()[ty];
53568992b99bSJoel Dice     let async_function = ty.async_;
53578992b99bSJoel Dice     let task_return_type = ty.results;
5358fa70f025SJoel Dice     let component_instance = raw_options.instance;
5359ec9b62abSAlex Crichton     let callback = options.callback.map(|i| instance.runtime_callback(i));
5360ec9b62abSAlex Crichton     let memory = options
5361ec9b62abSAlex Crichton         .memory()
5362ec9b62abSAlex Crichton         .map(|i| instance.runtime_memory(i))
5363ec9b62abSAlex Crichton         .map(SendSyncPtr::new);
5364ec9b62abSAlex Crichton     let string_encoding = options.string_encoding;
5365fa70f025SJoel Dice     let token = StoreToken::new(store.as_context_mut());
53667e39c25eSJoel Dice     let state = store.0.concurrent_state_mut();
5367fa70f025SJoel Dice 
5368fa70f025SJoel Dice     let (tx, rx) = oneshot::channel();
5369fa70f025SJoel Dice 
53701a154f61SAlex Crichton     let instance = RuntimeInstance {
53711a154f61SAlex Crichton         instance: handle.instance().id().instance(),
53721a154f61SAlex Crichton         index: component_instance,
53731a154f61SAlex Crichton     };
53743764e757SAlex Crichton     let caller = state.current_thread;
53751d8827f3SAlex Crichton     let task = GuestTask::new(
53767e39c25eSJoel Dice         Box::new(for_any_lower(move |store, params| {
5377fa70f025SJoel Dice             lower_params(handle, token.as_context_mut(store), params)
5378fa70f025SJoel Dice         })),
5379fa70f025SJoel Dice         LiftResult {
53807e39c25eSJoel Dice             lift: Box::new(for_any_lift(move |store, result| {
5381fa70f025SJoel Dice                 lift_result(handle, store, result)
5382fa70f025SJoel Dice             })),
5383fa70f025SJoel Dice             ty: task_return_type,
5384fa70f025SJoel Dice             memory,
5385fa70f025SJoel Dice             string_encoding,
5386fa70f025SJoel Dice         },
5387fa70f025SJoel Dice         Caller::Host {
5388fa70f025SJoel Dice             tx: Some(tx),
5389e06fbf70SSy Brand             host_future_present,
5390b856261dSJoel Dice             caller,
5391fa70f025SJoel Dice         },
5392fa70f025SJoel Dice         callback.map(|callback| {
5393fa70f025SJoel Dice             let callback = SendSyncPtr::new(callback);
53947e39c25eSJoel Dice             let instance = handle.instance();
5395b856261dSJoel Dice             Box::new(move |store: &mut dyn VMStore, event, handle| {
5396fa70f025SJoel Dice                 let store = token.as_context_mut(store);
5397fa70f025SJoel Dice                 // SAFETY: Per the contract of `prepare_call`, the callback
5398fa70f025SJoel Dice                 // will remain valid at least as long is this task exists.
5399b856261dSJoel Dice                 unsafe { instance.call_callback(store, callback, event, handle) }
5400b856261dSJoel Dice             }) as CallbackFn
5401fa70f025SJoel Dice         }),
54021a154f61SAlex Crichton         instance,
54038992b99bSJoel Dice         async_function,
5404fa70f025SJoel Dice     )?;
5405fa70f025SJoel Dice 
5406fa70f025SJoel Dice     let task = state.push(task)?;
540735887491SSy Brand     let new_thread = GuestThread::new_implicit(state, task)?;
540835887491SSy Brand     let thread = state.push(new_thread)?;
5409e06fbf70SSy Brand     state.get_mut(task)?.threads.insert(thread);
5410fa70f025SJoel Dice 
5411da093747SAlex Crichton     if !store.0.may_enter(instance)? {
5412da093747SAlex Crichton         bail!(Trap::CannotEnterComponent);
5413b856261dSJoel Dice     }
5414b856261dSJoel Dice 
5415fa70f025SJoel Dice     Ok(PreparedCall {
5416fa70f025SJoel Dice         handle,
5417e06fbf70SSy Brand         thread: QualifiedThreadId { task, thread },
5418fa70f025SJoel Dice         param_count,
5419fa70f025SJoel Dice         rx,
5420fa70f025SJoel Dice         _phantom: PhantomData,
5421fa70f025SJoel Dice     })
5422812dd1e8SJoel Dice }
5423b221fca7SJoel Dice 
5424b221fca7SJoel Dice /// Queue a call previously prepared using `prepare_call` to be run as part of
5425b221fca7SJoel Dice /// the associated `ComponentInstance`'s event loop.
5426b221fca7SJoel Dice ///
5427b221fca7SJoel Dice /// The returned future will resolve to the result once it is available, but
5428da265515SAlex Crichton /// must only be polled via the instance's event loop. See
54297e39c25eSJoel Dice /// `StoreContextMut::run_concurrent` for details.
queue_call<T: 'static, R: Send + 'static>( mut store: StoreContextMut<T>, prepared: PreparedCall<R>, ) -> Result<impl Future<Output = Result<R>> + Send + 'static + use<T, R>>5430b221fca7SJoel Dice pub(crate) fn queue_call<T: 'static, R: Send + 'static>(
5431b221fca7SJoel Dice     mut store: StoreContextMut<T>,
5432b221fca7SJoel Dice     prepared: PreparedCall<R>,
54331e0b0b46SAlex Crichton ) -> Result<impl Future<Output = Result<R>> + Send + 'static + use<T, R>> {
5434fa70f025SJoel Dice     let PreparedCall {
5435fa70f025SJoel Dice         handle,
5436e06fbf70SSy Brand         thread,
5437fa70f025SJoel Dice         param_count,
5438fa70f025SJoel Dice         rx,
5439fa70f025SJoel Dice         ..
5440fa70f025SJoel Dice     } = prepared;
5441fa70f025SJoel Dice 
5442e06fbf70SSy Brand     queue_call0(store.as_context_mut(), handle, thread, param_count)?;
5443fa70f025SJoel Dice 
5444fa70f025SJoel Dice     Ok(checked(
54457e39c25eSJoel Dice         store.0.id(),
5446da093747SAlex Crichton         rx.map(move |result| match result {
5447da093747SAlex Crichton             Ok(r) => match r.downcast() {
5448da093747SAlex Crichton                 Ok(r) => Ok(*r),
5449da093747SAlex Crichton                 Err(_) => bail_bug!("wrong type of value produced"),
5450da093747SAlex Crichton             },
5451da093747SAlex Crichton             Err(e) => Err(e.into()),
5452fa70f025SJoel Dice         }),
5453fa70f025SJoel Dice     ))
5454fa70f025SJoel Dice }
5455fa70f025SJoel Dice 
5456fa70f025SJoel Dice /// Queue a call previously prepared using `prepare_call` to be run as part of
5457fa70f025SJoel Dice /// the associated `ComponentInstance`'s event loop.
queue_call0<T: 'static>( store: StoreContextMut<T>, handle: Func, guest_thread: QualifiedThreadId, param_count: usize, ) -> Result<()>5458fa70f025SJoel Dice fn queue_call0<T: 'static>(
5459fa70f025SJoel Dice     store: StoreContextMut<T>,
5460fa70f025SJoel Dice     handle: Func,
5461e06fbf70SSy Brand     guest_thread: QualifiedThreadId,
5462fa70f025SJoel Dice     param_count: usize,
5463fa70f025SJoel Dice ) -> Result<()> {
5464b856261dSJoel Dice     let (_options, _, _ty, raw_options) = handle.abi_info(store.0);
5465fa70f025SJoel Dice     let is_concurrent = raw_options.async_;
5466ec9b62abSAlex Crichton     let callback = raw_options.callback;
5467fa70f025SJoel Dice     let instance = handle.instance();
5468fa70f025SJoel Dice     let callee = handle.lifted_core_func(store.0);
5469fa70f025SJoel Dice     let post_return = handle.post_return_core_func(store.0);
5470ec9b62abSAlex Crichton     let callback = callback.map(|i| {
5471ec9b62abSAlex Crichton         let instance = instance.id().get(store.0);
5472ec9b62abSAlex Crichton         SendSyncPtr::new(instance.runtime_callback(i))
5473ec9b62abSAlex Crichton     });
5474fa70f025SJoel Dice 
5475e06fbf70SSy Brand     log::trace!("queueing call {guest_thread:?}");
5476fa70f025SJoel Dice 
5477fa70f025SJoel Dice     // SAFETY: `callee`, `callback`, and `post_return` are valid pointers
5478fa70f025SJoel Dice     // (with signatures appropriate for this call) and will remain valid as
5479fa70f025SJoel Dice     // long as this instance is valid.
5480fa70f025SJoel Dice     unsafe {
5481fa70f025SJoel Dice         instance.queue_call(
5482fa70f025SJoel Dice             store,
5483e06fbf70SSy Brand             guest_thread,
5484fa70f025SJoel Dice             SendSyncPtr::new(callee),
5485fa70f025SJoel Dice             param_count,
5486fa70f025SJoel Dice             1,
5487fa70f025SJoel Dice             is_concurrent,
5488ec9b62abSAlex Crichton             callback,
5489fa70f025SJoel Dice             post_return.map(SendSyncPtr::new),
5490fa70f025SJoel Dice         )
5491fa70f025SJoel Dice     }
5492812dd1e8SJoel Dice }
5493