1
- use anyhow:: { anyhow, Result } ;
1
+ use anyhow:: { anyhow, ensure , Result } ;
2
2
use std:: {
3
3
io:: { Read , Write } ,
4
4
path:: { Path , PathBuf } ,
5
5
sync:: Mutex ,
6
- time:: { Duration , Instant } ,
6
+ time:: Duration ,
7
7
} ;
8
8
use system_interface:: io:: ReadReady ;
9
9
use tokio:: io:: { AsyncRead , AsyncWrite } ;
@@ -67,35 +67,13 @@ pub enum WasiVersion {
67
67
/// A `Store` can be built with a [`StoreBuilder`].
68
68
pub struct Store < T > {
69
69
inner : wasmtime:: Store < Data < T > > ,
70
- epoch_tick_interval : Duration ,
71
70
}
72
71
73
72
impl < T > Store < T > {
74
73
/// Returns a mutable reference to the [`HostComponentsData`] of this [`Store`].
75
74
pub fn host_components_data ( & mut self ) -> & mut HostComponentsData {
76
75
& mut self . inner . data_mut ( ) . host_components_data
77
76
}
78
-
79
- /// Sets the execution deadline.
80
- ///
81
- /// This is a rough deadline; an instance will trap some time after this
82
- /// deadline, determined by [`EngineBuilder::epoch_tick_interval`] and
83
- /// details of the system's thread scheduler.
84
- ///
85
- /// See [`wasmtime::Store::set_epoch_deadline`](https://docs.rs/wasmtime/latest/wasmtime/struct.Store.html#method.set_epoch_deadline).
86
- pub fn set_deadline ( & mut self , deadline : Instant ) {
87
- let now = Instant :: now ( ) ;
88
- let duration = deadline - now;
89
- let ticks = if duration. is_zero ( ) {
90
- tracing:: warn!( "Execution deadline set in past: {deadline:?} < {now:?}" ) ;
91
- 0
92
- } else {
93
- let ticks = duration. as_micros ( ) / self . epoch_tick_interval . as_micros ( ) ;
94
- let ticks = ticks. min ( u64:: MAX as u128 ) as u64 ;
95
- ticks + 1 // Add one to allow for current partially-completed tick
96
- } ;
97
- self . inner . set_epoch_deadline ( ticks) ;
98
- }
99
77
}
100
78
101
79
impl < T > AsRef < wasmtime:: Store < Data < T > > > for Store < T > {
@@ -130,6 +108,7 @@ impl<T> wasmtime::AsContextMut for Store<T> {
130
108
pub struct StoreBuilder {
131
109
engine : wasmtime:: Engine ,
132
110
epoch_tick_interval : Duration ,
111
+ yield_interval : Duration ,
133
112
wasi : std:: result:: Result < WasiCtxBuilder , String > ,
134
113
host_components_data : HostComponentsData ,
135
114
store_limits : StoreLimitsAsync ,
@@ -146,6 +125,7 @@ impl StoreBuilder {
146
125
Self {
147
126
engine,
148
127
epoch_tick_interval,
128
+ yield_interval : epoch_tick_interval,
149
129
wasi : Ok ( wasi. into ( ) ) ,
150
130
host_components_data : host_components. new_data ( ) ,
151
131
store_limits : StoreLimitsAsync :: default ( ) ,
@@ -160,6 +140,20 @@ impl StoreBuilder {
160
140
self . store_limits = StoreLimitsAsync :: new ( Some ( max_memory_size) , None ) ;
161
141
}
162
142
143
+ /// Sets the execution yield interval.
144
+ ///
145
+ /// A CPU-bound running instance will be forced to yield approximately
146
+ /// every interval, which gives the host thread an opportunity to cancel
147
+ /// the instance or schedule other work on the thread.
148
+ ///
149
+ /// The exact interval of yielding is determined by [`EngineBuilder::epoch_tick_interval`]
150
+ /// and details of the task scheduler.
151
+ ///
152
+ /// The interval defaults to the epoch tick interval.
153
+ pub fn yield_interval ( & mut self , interval : Duration ) {
154
+ self . yield_interval = interval;
155
+ }
156
+
163
157
/// Inherit stdin from the host process.
164
158
pub fn inherit_stdin ( & mut self ) {
165
159
self . with_wasi ( |wasi| match wasi {
@@ -386,16 +380,26 @@ impl StoreBuilder {
386
380
387
381
inner. limiter_async ( move |data| & mut data. store_limits ) ;
388
382
389
- // With epoch interruption enabled, there must be _some_ deadline set
390
- // or execution will trap immediately. Since this is a delta, we need
391
- // to avoid overflow so we'll use 2^63 which is still "practically
392
- // forever" for any plausible tick interval.
393
- inner. set_epoch_deadline ( u64:: MAX / 2 ) ;
383
+ ensure ! (
384
+ !self . epoch_tick_interval. is_zero( ) ,
385
+ "epoch_tick_interval may not be zero"
386
+ ) ;
387
+ let delta = self . yield_interval . as_nanos ( ) / self . epoch_tick_interval . as_nanos ( ) ;
388
+ let delta = if delta == 0 {
389
+ tracing:: warn!(
390
+ "Yield interval {interval:?} too small to resolve; clamping to tick interval {tick:?}" ,
391
+ interval = self . yield_interval,
392
+ tick = self . epoch_tick_interval) ;
393
+ 1
394
+ } else if delta > u64:: MAX as u128 {
395
+ tracing:: warn!( "Yield interval too large; yielding effectively disabled" ) ;
396
+ u64:: MAX
397
+ } else {
398
+ delta as u64
399
+ } ;
400
+ inner. epoch_deadline_async_yield_and_update ( delta) ;
394
401
395
- Ok ( Store {
396
- inner,
397
- epoch_tick_interval : self . epoch_tick_interval ,
398
- } )
402
+ Ok ( Store { inner } )
399
403
}
400
404
401
405
/// Builds a [`Store`] from this builder with `Default` host state data.
0 commit comments