@@ -78,8 +78,13 @@ impl<'store, 'stack> Executor<'store, 'stack> {
7878 return Ok ( ExecOutcome :: Suspended ( suspend_reason) ) ;
7979 }
8080 } ;
81- self . after_resume_host_coro ( & res) ;
81+ self . stack . values . extend_from_wasmvalues ( & res) ;
8282 self . suspended_host_coro = None ;
83+
84+ // we don't know how much time we spent in host function
85+ if let ControlFlow :: Break ( ReasonToBreak :: Suspended ( reason) ) = self . check_should_suspend ( ) {
86+ return Ok ( ExecOutcome :: Suspended ( reason) ) ;
87+ }
8388 }
8489
8590 loop {
@@ -93,9 +98,35 @@ impl<'store, 'stack> Executor<'store, 'stack> {
9398 }
9499 }
95100
96- fn after_resume_host_coro ( & mut self , vals : & [ WasmValue ] ) {
97- self . stack . values . extend_from_wasmvalues ( & vals) ;
98- self . cf . incr_instr_ptr ( ) ;
101+ /// for controlling how long execution spends in wasm
102+ /// called when execution loops back, because that might happen indefinite amount of times
103+ /// and before and after function calls, because even without loops or infinite recursion, wasm function calls
104+ /// can mutliply time spent in execution
105+ /// execution may not be suspended in the middle of execution the funcion:
106+ /// so only do it as the last thing or first thing in the intsruction execution
107+ fn check_should_suspend ( & mut self ) -> ControlFlow < ReasonToBreak > {
108+ if let Some ( flag) = & self . store . suspend_cond . suspend_flag {
109+ if flag. load ( core:: sync:: atomic:: Ordering :: Acquire ) {
110+ return ReasonToBreak :: Suspended ( SuspendReason :: SuspendedFlag ) . into ( ) ;
111+ }
112+ }
113+
114+ #[ cfg( feature = "std" ) ]
115+ if let Some ( when) = & self . store . suspend_cond . timeout_instant {
116+ if crate :: std:: time:: Instant :: now ( ) >= * when {
117+ return ReasonToBreak :: Suspended ( SuspendReason :: SuspendedEpoch ) . into ( ) ;
118+ }
119+ }
120+
121+ if let Some ( mut cb) = self . store . suspend_cond . suspend_cb . take ( ) {
122+ let should_suspend = matches ! ( cb( & self . store) , ControlFlow :: Break ( ( ) ) ) ;
123+ self . store . suspend_cond . suspend_cb = Some ( cb) ; // put it back
124+ if should_suspend {
125+ return ReasonToBreak :: Suspended ( SuspendReason :: SuspendedCallback ) . into ( ) ;
126+ }
127+ }
128+
129+ ControlFlow :: Continue ( ( ) )
99130 }
100131
101132 #[ inline( always) ]
@@ -382,35 +413,41 @@ impl<'store, 'stack> Executor<'store, 'stack> {
382413 self . module . swap_with ( self . cf . module_addr ( ) , self . store ) ;
383414 ControlFlow :: Continue ( ( ) )
384415 }
416+ fn exec_call_host ( & mut self , host_func : Rc < HostFunction > , func_ref : u32 ) -> ControlFlow < ReasonToBreak > {
417+ let params = self . stack . values . pop_params ( & host_func. ty . params ) ;
418+ let res =
419+ ( host_func. func ) ( FuncContext { store : self . store , module_addr : self . module . id ( ) } , & params) . to_cf ( ) ?;
420+ match res {
421+ PotentialCoroCallResult :: Return ( res) => {
422+ self . stack . values . extend_from_wasmvalues ( & res) ;
423+ self . cf . incr_instr_ptr ( ) ;
424+ self . check_should_suspend ( ) ?; // who knows how long we've spent in host function
425+ return ControlFlow :: Continue ( ( ) ) ;
426+ }
427+ PotentialCoroCallResult :: Suspended ( suspend_reason, state) => {
428+ self . suspended_host_coro =
429+ Some ( SuspendedHostCoroState { coro_state : state, coro_orig_function : func_ref } ) ;
430+ self . cf . incr_instr_ptr ( ) ;
431+ return ReasonToBreak :: Suspended ( suspend_reason) . into ( ) ;
432+ }
433+ }
434+ }
385435 fn exec_call_direct ( & mut self , v : u32 ) -> ControlFlow < ReasonToBreak > {
436+ self . check_should_suspend ( ) ?; // don't commit to function if we should be stopping now
386437 let func_ref = self . module . resolve_func_addr ( v) ;
387438 let func_inst = self . store . get_func ( func_ref) ;
388439 let wasm_func = match & func_inst. func {
389440 crate :: Function :: Wasm ( wasm_func) => wasm_func,
390441 crate :: Function :: Host ( host_func) => {
391- let func = & host_func. clone ( ) ;
392- let params = self . stack . values . pop_params ( & host_func. ty . params ) ;
393- let res =
394- ( func. func ) ( FuncContext { store : self . store , module_addr : self . module . id ( ) } , & params) . to_cf ( ) ?;
395- match res {
396- PotentialCoroCallResult :: Return ( res) => {
397- self . stack . values . extend_from_wasmvalues ( & res) ;
398- self . cf . incr_instr_ptr ( ) ;
399- return ControlFlow :: Continue ( ( ) ) ;
400- }
401- PotentialCoroCallResult :: Suspended ( suspend_reason, state) => {
402- self . suspended_host_coro =
403- Some ( SuspendedHostCoroState { coro_state : state, coro_orig_function : func_ref } ) ;
404- return ReasonToBreak :: Suspended ( suspend_reason) . into ( ) ;
405- }
406- }
442+ return self . exec_call_host ( host_func. clone ( ) , func_ref) ;
407443 }
408444 } ;
409445
410446 self . exec_call ( wasm_func. clone ( ) , func_inst. owner )
411447 }
412448 fn exec_call_indirect ( & mut self , type_addr : u32 , table_addr : u32 ) -> ControlFlow < ReasonToBreak > {
413- // verify that the table is of the right type, this should be validated by the parser already
449+ self . check_should_suspend ( ) ?; // check if we should suspend now before commiting to function
450+ // verify that the table is of the right type, this should be validated by the parser already
414451 let func_ref = {
415452 let table = self . store . get_table ( self . module . resolve_table_addr ( table_addr) ) ;
416453 let table_idx: u32 = self . stack . values . pop :: < i32 > ( ) as u32 ;
@@ -436,27 +473,7 @@ impl<'store, 'stack> Executor<'store, 'stack> {
436473 )
437474 . into ( ) ;
438475 }
439-
440- let host_func = host_func. clone ( ) ;
441- let params = self . stack . values . pop_params ( & host_func. ty . params ) ;
442- let res =
443- match ( host_func. func ) ( FuncContext { store : self . store , module_addr : self . module . id ( ) } , & params) {
444- Ok ( res) => res,
445- Err ( e) => return ReasonToBreak :: Errored ( e) . into ( ) ,
446- } ;
447- match res {
448- PotentialCoroCallResult :: Return ( res) => {
449- self . stack . values . extend_from_wasmvalues ( & res) ;
450- self . cf . incr_instr_ptr ( ) ;
451- }
452- PotentialCoroCallResult :: Suspended ( suspend_reason, state) => {
453- self . suspended_host_coro =
454- Some ( SuspendedHostCoroState { coro_state : state, coro_orig_function : func_ref } ) ;
455- return ReasonToBreak :: Suspended ( suspend_reason) . into ( ) ;
456- }
457- }
458-
459- return ControlFlow :: Continue ( ( ) ) ;
476+ return self . exec_call_host ( host_func. clone ( ) , func_ref) ;
460477 }
461478 } ;
462479
@@ -505,20 +522,30 @@ impl<'store, 'stack> Executor<'store, 'stack> {
505522 } ) ;
506523 }
507524 fn exec_br ( & mut self , to : u32 ) -> ControlFlow < ReasonToBreak > {
508- if self . cf . break_to ( to, & mut self . stack . values , & mut self . stack . blocks ) . is_none ( ) {
525+ let break_type = if let Some ( bl_ty) = self . cf . break_to ( to, & mut self . stack . values , & mut self . stack . blocks ) {
526+ bl_ty
527+ } else {
509528 return self . exec_return ( ) ;
510- }
529+ } ;
511530
512531 self . cf . incr_instr_ptr ( ) ;
532+
533+ if matches ! ( break_type, BlockType :: Loop ) {
534+ self . check_should_suspend ( ) ?;
535+ }
536+
513537 ControlFlow :: Continue ( ( ) )
514538 }
515539 fn exec_br_if ( & mut self , to : u32 ) -> ControlFlow < ReasonToBreak > {
516- if self . stack . values . pop :: < i32 > ( ) != 0
517- && self . cf . break_to ( to, & mut self . stack . values , & mut self . stack . blocks ) . is_none ( )
518- {
540+ let break_type = self . cf . break_to ( to, & mut self . stack . values , & mut self . stack . blocks ) ;
541+ if self . stack . values . pop :: < i32 > ( ) != 0 && break_type. is_none ( ) {
519542 return self . exec_return ( ) ;
520543 }
521544 self . cf . incr_instr_ptr ( ) ;
545+
546+ if matches ! ( break_type, Some ( BlockType :: Loop ) ) {
547+ self . check_should_suspend ( ) ?;
548+ }
522549 ControlFlow :: Continue ( ( ) )
523550 }
524551 fn exec_brtable ( & mut self , default : u32 , len : u32 ) -> ControlFlow < ReasonToBreak > {
@@ -540,11 +567,19 @@ impl<'store, 'stack> Executor<'store, 'stack> {
540567 _ => return ReasonToBreak :: Errored ( Error :: Other ( "br_table out of bounds" . to_string ( ) ) ) . into ( ) ,
541568 } ;
542569
543- if self . cf . break_to ( to, & mut self . stack . values , & mut self . stack . blocks ) . is_none ( ) {
570+ let break_type = self . cf . break_to ( to, & mut self . stack . values , & mut self . stack . blocks ) ;
571+ let break_type = if let Some ( br_ty) = break_type {
572+ br_ty
573+ } else {
544574 return self . exec_return ( ) ;
545- }
575+ } ;
546576
547577 self . cf . incr_instr_ptr ( ) ;
578+
579+ if matches ! ( break_type, BlockType :: Loop ) {
580+ self . check_should_suspend ( ) ?;
581+ }
582+
548583 ControlFlow :: Continue ( ( ) )
549584 }
550585 fn exec_return ( & mut self ) -> ControlFlow < ReasonToBreak > {
@@ -559,6 +594,8 @@ impl<'store, 'stack> Executor<'store, 'stack> {
559594 }
560595
561596 self . module . swap_with ( self . cf . module_addr ( ) , self . store ) ;
597+
598+ self . check_should_suspend ( ) ?;
562599 ControlFlow :: Continue ( ( ) )
563600 }
564601 fn exec_end_block ( & mut self ) {
0 commit comments