1- use anyhow:: { anyhow, Result } ;
1+ use anyhow:: { anyhow, ensure , Result } ;
22use std:: {
33 io:: { Read , Write } ,
44 path:: { Path , PathBuf } ,
5- time:: { Duration , Instant } ,
5+ time:: Duration ,
66} ;
77use system_interface:: io:: ReadReady ;
88use wasi_common_preview1 as wasi_preview1;
@@ -56,35 +56,13 @@ pub enum WasiVersion {
5656/// A `Store` can be built with a [`StoreBuilder`].
5757pub struct Store < T > {
5858 inner : wasmtime:: Store < Data < T > > ,
59- epoch_tick_interval : Duration ,
6059}
6160
6261impl < T > Store < T > {
6362 /// Returns a mutable reference to the [`HostComponentsData`] of this [`Store`].
6463 pub fn host_components_data ( & mut self ) -> & mut HostComponentsData {
6564 & mut self . inner . data_mut ( ) . host_components_data
6665 }
67-
68- /// Sets the execution deadline.
69- ///
70- /// This is a rough deadline; an instance will trap some time after this
71- /// deadline, determined by [`EngineBuilder::epoch_tick_interval`] and
72- /// details of the system's thread scheduler.
73- ///
74- /// See [`wasmtime::Store::set_epoch_deadline`](https://docs.rs/wasmtime/latest/wasmtime/struct.Store.html#method.set_epoch_deadline).
75- pub fn set_deadline ( & mut self , deadline : Instant ) {
76- let now = Instant :: now ( ) ;
77- let duration = deadline - now;
78- let ticks = if duration. is_zero ( ) {
79- tracing:: warn!( "Execution deadline set in past: {deadline:?} < {now:?}" ) ;
80- 0
81- } else {
82- let ticks = duration. as_micros ( ) / self . epoch_tick_interval . as_micros ( ) ;
83- let ticks = ticks. min ( u64:: MAX as u128 ) as u64 ;
84- ticks + 1 // Add one to allow for current partially-completed tick
85- } ;
86- self . inner . set_epoch_deadline ( ticks) ;
87- }
8866}
8967
9068impl < T > AsRef < wasmtime:: Store < Data < T > > > for Store < T > {
@@ -119,6 +97,7 @@ impl<T> wasmtime::AsContextMut for Store<T> {
11997pub struct StoreBuilder {
12098 engine : wasmtime:: Engine ,
12199 epoch_tick_interval : Duration ,
100+ yield_interval : Duration ,
122101 wasi : std:: result:: Result < WasiCtxBuilder , String > ,
123102 host_components_data : HostComponentsData ,
124103 store_limits : StoreLimitsAsync ,
@@ -135,6 +114,7 @@ impl StoreBuilder {
135114 Self {
136115 engine,
137116 epoch_tick_interval,
117+ yield_interval : epoch_tick_interval,
138118 wasi : Ok ( wasi. into ( ) ) ,
139119 host_components_data : host_components. new_data ( ) ,
140120 store_limits : StoreLimitsAsync :: default ( ) ,
@@ -149,6 +129,20 @@ impl StoreBuilder {
149129 self . store_limits = StoreLimitsAsync :: new ( Some ( max_memory_size) , None ) ;
150130 }
151131
132+ /// Sets the execution yield interval.
133+ ///
134+ /// A CPU-bound running instance will be forced to yield approximately
135+ /// every interval, which gives the host thread an opportunity to cancel
136+ /// the instance or schedule other work on the thread.
137+ ///
138+ /// The exact interval of yielding is determined by [`EngineBuilder::epoch_tick_interval`]
139+ /// and details of the task scheduler.
140+ ///
141+ /// The interval defaults to the epoch tick interval.
142+ pub fn yield_interval ( & mut self , interval : Duration ) {
143+ self . yield_interval = interval;
144+ }
145+
152146 /// Inherit stdin from the host process.
153147 pub fn inherit_stdin ( & mut self ) {
154148 self . with_wasi ( |wasi| match wasi {
@@ -370,16 +364,26 @@ impl StoreBuilder {
370364
371365 inner. limiter_async ( move |data| & mut data. store_limits ) ;
372366
373- // With epoch interruption enabled, there must be _some_ deadline set
374- // or execution will trap immediately. Since this is a delta, we need
375- // to avoid overflow so we'll use 2^63 which is still "practically
376- // forever" for any plausible tick interval.
377- inner. set_epoch_deadline ( u64:: MAX / 2 ) ;
367+ ensure ! (
368+ !self . epoch_tick_interval. is_zero( ) ,
369+ "epoch_tick_interval may not be zero"
370+ ) ;
371+ let delta = self . yield_interval . as_nanos ( ) / self . epoch_tick_interval . as_nanos ( ) ;
372+ let delta = if delta == 0 {
373+ tracing:: warn!(
374+ "Yield interval {interval:?} too small to resolve; clamping to tick interval {tick:?}" ,
375+ interval = self . yield_interval,
376+ tick = self . epoch_tick_interval) ;
377+ 1
378+ } else if delta > u64:: MAX as u128 {
379+ tracing:: warn!( "Yield interval too large; yielding effectively disabled" ) ;
380+ u64:: MAX
381+ } else {
382+ delta as u64
383+ } ;
384+ inner. epoch_deadline_async_yield_and_update ( delta) ;
378385
379- Ok ( Store {
380- inner,
381- epoch_tick_interval : self . epoch_tick_interval ,
382- } )
386+ Ok ( Store { inner } )
383387 }
384388
385389 /// Builds a [`Store`] from this builder with `Default` host state data.
0 commit comments