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 = ¶ms[..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