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
- time:: { Duration , Instant } ,
5
+ time:: Duration ,
6
6
} ;
7
7
use system_interface:: io:: ReadReady ;
8
8
use wasi_common_preview1 as wasi_preview1;
@@ -56,35 +56,13 @@ pub enum WasiVersion {
56
56
/// A `Store` can be built with a [`StoreBuilder`].
57
57
pub struct Store < T > {
58
58
inner : wasmtime:: Store < Data < T > > ,
59
- epoch_tick_interval : Duration ,
60
59
}
61
60
62
61
impl < T > Store < T > {
63
62
/// Returns a mutable reference to the [`HostComponentsData`] of this [`Store`].
64
63
pub fn host_components_data ( & mut self ) -> & mut HostComponentsData {
65
64
& mut self . inner . data_mut ( ) . host_components_data
66
65
}
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
- }
88
66
}
89
67
90
68
impl < T > AsRef < wasmtime:: Store < Data < T > > > for Store < T > {
@@ -119,6 +97,7 @@ impl<T> wasmtime::AsContextMut for Store<T> {
119
97
pub struct StoreBuilder {
120
98
engine : wasmtime:: Engine ,
121
99
epoch_tick_interval : Duration ,
100
+ yield_interval : Duration ,
122
101
wasi : std:: result:: Result < WasiCtxBuilder , String > ,
123
102
host_components_data : HostComponentsData ,
124
103
store_limits : StoreLimitsAsync ,
@@ -135,6 +114,7 @@ impl StoreBuilder {
135
114
Self {
136
115
engine,
137
116
epoch_tick_interval,
117
+ yield_interval : epoch_tick_interval,
138
118
wasi : Ok ( wasi. into ( ) ) ,
139
119
host_components_data : host_components. new_data ( ) ,
140
120
store_limits : StoreLimitsAsync :: default ( ) ,
@@ -149,6 +129,20 @@ impl StoreBuilder {
149
129
self . store_limits = StoreLimitsAsync :: new ( Some ( max_memory_size) , None ) ;
150
130
}
151
131
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
+
152
146
/// Inherit stdin from the host process.
153
147
pub fn inherit_stdin ( & mut self ) {
154
148
self . with_wasi ( |wasi| match wasi {
@@ -370,16 +364,26 @@ impl StoreBuilder {
370
364
371
365
inner. limiter_async ( move |data| & mut data. store_limits ) ;
372
366
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) ;
378
385
379
- Ok ( Store {
380
- inner,
381
- epoch_tick_interval : self . epoch_tick_interval ,
382
- } )
386
+ Ok ( Store { inner } )
383
387
}
384
388
385
389
/// Builds a [`Store`] from this builder with `Default` host state data.
0 commit comments