@@ -252,6 +252,7 @@ enum CallHookInner<T> {
252252
253253/// What to do after returning from a callback when the engine epoch reaches
254254/// the deadline for a Store during execution of a function using that store.
255+ #[ non_exhaustive]
255256pub enum UpdateDeadline {
256257 /// Extend the deadline by the specified number of ticks.
257258 Continue ( u64 ) ,
@@ -260,6 +261,18 @@ pub enum UpdateDeadline {
260261 /// configured via [`Config::async_support`](crate::Config::async_support).
261262 #[ cfg( feature = "async" ) ]
262263 Yield ( u64 ) ,
264+ /// Extend the deadline by the specified number of ticks after yielding to
265+ /// the async executor loop. This can only be used with an async [`Store`]
266+ /// configured via [`Config::async_support`](crate::Config::async_support).
267+ ///
268+ /// The yield will be performed by the future provided; when using `tokio`
269+ /// it is recommended to provide [`tokio::task::yield_now`](https://docs.rs/tokio/latest/tokio/task/fn.yield_now.html)
270+ /// here.
271+ #[ cfg( feature = "async" ) ]
272+ YieldCustom (
273+ u64 ,
274+ :: core:: pin:: Pin < Box < dyn :: core:: future:: Future < Output = ( ) > + Send > > ,
275+ ) ,
263276}
264277
265278// Forward methods on `StoreOpaque` to also being on `StoreInner<T>`
@@ -942,10 +955,10 @@ impl<T> Store<T> {
942955 /// add to the epoch deadline, as well as indicating what
943956 /// to do after the callback returns. If the [`Store`] is
944957 /// configured with async support, then the callback may return
945- /// [`UpdateDeadline::Yield`] to yield to the async executor before
946- /// updating the epoch deadline. Alternatively, the callback may
947- /// return [`UpdateDeadline::Continue`] to update the epoch deadline
948- /// immediately.
958+ /// [`UpdateDeadline::Yield`] or [`UpdateDeadline::YieldCustom`]
959+ /// to yield to the async executor before updating the epoch deadline.
960+ /// Alternatively, the callback may return [`UpdateDeadline::Continue`] to
961+ /// update the epoch deadline immediately.
949962 ///
950963 /// This setting is intended to allow for coarse-grained
951964 /// interruption, but not a deterministic deadline of a fixed,
@@ -2104,7 +2117,6 @@ unsafe impl<T> crate::runtime::vm::VMStore for StoreInner<T> {
21042117 Some ( callback) => callback ( ( & mut * self ) . as_context_mut ( ) ) . and_then ( |update| {
21052118 let delta = match update {
21062119 UpdateDeadline :: Continue ( delta) => delta,
2107-
21082120 #[ cfg( feature = "async" ) ]
21092121 UpdateDeadline :: Yield ( delta) => {
21102122 assert ! (
@@ -2116,6 +2128,27 @@ unsafe impl<T> crate::runtime::vm::VMStore for StoreInner<T> {
21162128 self . async_yield_impl ( ) ?;
21172129 delta
21182130 }
2131+ #[ cfg( feature = "async" ) ]
2132+ UpdateDeadline :: YieldCustom ( delta, future) => {
2133+ assert ! (
2134+ self . async_support( ) ,
2135+ "cannot use `UpdateDeadline::YieldCustom` without enabling async support in the config"
2136+ ) ;
2137+
2138+ // When control returns, we have a `Result<()>` passed
2139+ // in from the host fiber. If this finished successfully then
2140+ // we were resumed normally via a `poll`, so keep going. If
2141+ // the future was dropped while we were yielded, then we need
2142+ // to clean up this fiber. Do so by raising a trap which will
2143+ // abort all wasm and get caught on the other side to clean
2144+ // things up.
2145+ unsafe {
2146+ self . async_cx ( )
2147+ . expect ( "attempted to pull async context during shutdown" )
2148+ . block_on ( future) ?
2149+ }
2150+ delta
2151+ }
21192152 } ;
21202153
21212154 // Set a new deadline and return the new epoch deadline so
0 commit comments