Skip to content
This repository was archived by the owner on Oct 3, 2025. It is now read-only.

Commit 0c83703

Browse files
committed
move host_coro_state out of the executor
1 parent 9934060 commit 0c83703

File tree

4 files changed

+104
-81
lines changed

4 files changed

+104
-81
lines changed

crates/tinywasm/src/coro.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -95,13 +95,13 @@ impl<R, State> PotentialCoroCallResult<R, State> {
9595
Self::Suspended(suspend, state) => PotentialCoroCallResult::Suspended(suspend, mapper(state)),
9696
}
9797
}
98-
/// transform result with mapper if there is none - calls "otherwise".
98+
/// transform result with res_mapper or state with state_mapper
9999
/// user_val passed to whichever is called and is guaranteed to be used
100-
pub fn map<OutR, Usr, OutS>(
100+
pub fn map<OutR, UserData, OutS>(
101101
self,
102-
user_val: Usr,
103-
res_mapper: impl FnOnce(R, Usr) -> OutR,
104-
state_mapper: impl FnOnce(State, Usr) -> OutS,
102+
user_val: UserData,
103+
res_mapper: impl FnOnce(R, UserData) -> OutR,
104+
state_mapper: impl FnOnce(State, UserData) -> OutS,
105105
) -> PotentialCoroCallResult<OutR, OutS> {
106106
match self {
107107
Self::Return(res) => PotentialCoroCallResult::Return(res_mapper(res, user_val)),

crates/tinywasm/src/func.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ pub(crate) type TypedFuncHandleCallOutcome<R> = crate::coro::PotentialCoroCallRe
162162

163163
#[derive(Debug)]
164164
struct SuspendedWasmFunc {
165-
runtime: interpreter::SuspendedRuntime,
165+
runtime: interpreter::SuspendedInterpreterRuntime,
166166
result_types: Box<[ValType]>,
167167
}
168168
impl SuspendedWasmFunc {
@@ -178,8 +178,8 @@ impl SuspendedWasmFunc {
178178
#[derive(Debug)]
179179
#[allow(clippy::large_enum_variant)] // Wasm is bigger, but also much more common variant
180180
enum SuspendedFuncInner {
181-
Wasm(SuspendedWasmFunc),
182-
Host(SuspendedHostCoroState),
181+
Wasm(SuspendedWasmFunc), // could also have host coro-function in the stack, but with some wasm below
182+
Host(SuspendedHostCoroState), // in case we directly called host coro-function bypassing wasm stack
183183
}
184184

185185
/// handle to function that was suspended and can be resumed

crates/tinywasm/src/interpreter/executor.rs

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use crate::*;
1717

1818
pub(crate) enum ReasonToBreak {
1919
Errored(Error),
20-
Suspended(SuspendReason),
20+
Suspended(SuspendReason, Option<SuspendedHostCoroState>),
2121
Finished,
2222
}
2323

@@ -40,18 +40,17 @@ pub(crate) struct SuspendedHostCoroState {
4040
pub(crate) struct Executor<'store, 'stack> {
4141
pub(crate) cf: CallFrame,
4242
pub(crate) module: ModuleInstance,
43-
pub(crate) suspended_host_coro: Option<SuspendedHostCoroState>,
4443
pub(crate) store: &'store mut Store,
4544
pub(crate) stack: &'stack mut Stack,
4645
}
4746

48-
pub(crate) type ExecOutcome = coro::CoroStateResumeResult<()>;
47+
pub(crate) type ExecOutcome = coro::PotentialCoroCallResult<(), Option<SuspendedHostCoroState>>;
4948

5049
impl<'store, 'stack> Executor<'store, 'stack> {
5150
pub(crate) fn new(store: &'store mut Store, stack: &'stack mut Stack) -> Result<Self> {
5251
let current_frame = stack.call_stack.pop().expect("no call frame, this is a bug");
5352
let current_module = store.get_module_instance_raw(current_frame.module_addr());
54-
Ok(Self { cf: current_frame, module: current_module, suspended_host_coro: None, stack, store })
53+
Ok(Self { cf: current_frame, module: current_module, stack, store })
5554
}
5655

5756
#[inline(always)]
@@ -60,38 +59,51 @@ impl<'store, 'stack> Executor<'store, 'stack> {
6059
if let ControlFlow::Break(res) = self.exec_next() {
6160
return match res {
6261
ReasonToBreak::Errored(e) => Err(e),
63-
ReasonToBreak::Suspended(suspend_reason) => Ok(ExecOutcome::Suspended(suspend_reason)),
62+
ReasonToBreak::Suspended(suspend_reason, coro_state) => {
63+
Ok(ExecOutcome::Suspended(suspend_reason, coro_state))
64+
}
6465
ReasonToBreak::Finished => Ok(ExecOutcome::Return(())),
6566
};
6667
}
6768
}
6869
}
6970

71+
// on error running suspended_coro, returns it
72+
// so that it could be retried later
7073
#[inline(always)]
71-
pub(crate) fn resume(&mut self, res_arg: ResumeArgument) -> Result<ExecOutcome> {
72-
if let Some(coro_state) = self.suspended_host_coro.as_mut() {
74+
pub(crate) fn resume(
75+
&mut self,
76+
res_arg: ResumeArgument,
77+
mut suspended_coro: Option<SuspendedHostCoroState>,
78+
) -> Result<ExecOutcome, (Error, Option<SuspendedHostCoroState>)> {
79+
if let Some(coro_state) = suspended_coro.as_mut() {
7380
let ctx = FuncContext { store: self.store, module_addr: self.module.id() };
74-
let host_res = coro_state.coro_state.resume(ctx, res_arg)?;
81+
let host_res = match coro_state.coro_state.resume(ctx, res_arg) {
82+
Ok(val) => val,
83+
Err(err) => return Err((err, suspended_coro)),
84+
};
85+
7586
let res = match host_res {
7687
CoroStateResumeResult::Return(res) => res,
7788
CoroStateResumeResult::Suspended(suspend_reason) => {
78-
return Ok(ExecOutcome::Suspended(suspend_reason));
89+
return Ok(ExecOutcome::Suspended(suspend_reason, suspended_coro));
7990
}
8091
};
8192
self.stack.values.extend_from_wasmvalues(&res);
82-
self.suspended_host_coro = None;
8393

8494
// 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));
95+
if let ControlFlow::Break(ReasonToBreak::Suspended(reason, coro_state)) = self.check_should_suspend() {
96+
return Ok(ExecOutcome::Suspended(reason, coro_state));
8797
}
8898
}
8999

90100
loop {
91101
if let ControlFlow::Break(res) = self.exec_next() {
92102
return match res {
93-
ReasonToBreak::Errored(e) => Err(e),
94-
ReasonToBreak::Suspended(suspend_reason) => Ok(ExecOutcome::Suspended(suspend_reason)),
103+
ReasonToBreak::Errored(e) => Err((e, None)),
104+
ReasonToBreak::Suspended(suspend_reason, coro_state) => {
105+
Ok(ExecOutcome::Suspended(suspend_reason, coro_state))
106+
}
95107
ReasonToBreak::Finished => Ok(ExecOutcome::Return(())),
96108
};
97109
}
@@ -102,28 +114,28 @@ impl<'store, 'stack> Executor<'store, 'stack> {
102114
/// called when execution loops back, because that might happen indefinite amount of times
103115
/// and before and after function calls, because even without loops or infinite recursion, wasm function calls
104116
/// can mutliply time spent in execution
105-
/// execution may not be suspended in the middle of execution the funcion:
117+
/// execution may not be suspended in the middle of execution the innsruction (without rolling back its effects):
106118
/// so only do it as the last thing or first thing in the intsruction execution
107119
#[must_use = "If this returns ControlFlow::Break, the caller should propagate it"]
108120
fn check_should_suspend(&mut self) -> ControlFlow<ReasonToBreak> {
109121
if let Some(flag) = &self.store.suspend_cond.suspend_flag {
110122
if flag.load(core::sync::atomic::Ordering::Acquire) {
111-
return ReasonToBreak::Suspended(SuspendReason::SuspendedFlag).into();
123+
return ReasonToBreak::Suspended(SuspendReason::SuspendedFlag, None).into();
112124
}
113125
}
114126

115127
#[cfg(feature = "std")]
116128
if let Some(when) = &self.store.suspend_cond.timeout_instant {
117129
if crate::std::time::Instant::now() >= *when {
118-
return ReasonToBreak::Suspended(SuspendReason::SuspendedEpoch).into();
130+
return ReasonToBreak::Suspended(SuspendReason::SuspendedEpoch, None).into();
119131
}
120132
}
121133

122134
if let Some(mut cb) = self.store.suspend_cond.suspend_cb.take() {
123135
let should_suspend = matches!(cb(self.store), ControlFlow::Break(()));
124136
self.store.suspend_cond.suspend_cb = Some(cb); // put it back
125137
if should_suspend {
126-
return ReasonToBreak::Suspended(SuspendReason::SuspendedCallback).into();
138+
return ReasonToBreak::Suspended(SuspendReason::SuspendedCallback, None).into();
127139
}
128140
}
129141

@@ -425,10 +437,9 @@ impl<'store, 'stack> Executor<'store, 'stack> {
425437
ControlFlow::Continue(())
426438
}
427439
PotentialCoroCallResult::Suspended(suspend_reason, state) => {
428-
self.suspended_host_coro =
429-
Some(SuspendedHostCoroState { coro_state: state, coro_orig_function: func_ref });
440+
let coro_state = Some(SuspendedHostCoroState { coro_state: state, coro_orig_function: func_ref });
430441
self.cf.incr_instr_ptr();
431-
ReasonToBreak::Suspended(suspend_reason).into()
442+
ReasonToBreak::Suspended(suspend_reason, coro_state).into()
432443
}
433444
}
434445
}

crates/tinywasm/src/interpreter/mod.rs

Lines changed: 64 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ mod no_std_floats;
99

1010
use crate::coro;
1111
use crate::{FuncContext, ModuleInstance, Result, Store};
12-
use executor::{Executor, SuspendedHostCoroState};
12+
use executor::{ExecOutcome, Executor, SuspendedHostCoroState};
1313
use stack::{CallFrame, Stack};
1414
use tinywasm_types::ResumeArgument;
1515
pub use values::*;
@@ -21,72 +21,84 @@ pub use values::*;
2121
pub struct InterpreterRuntime {}
2222

2323
#[derive(Debug)]
24-
pub(crate) struct SuspendedRuntimeBody {
25-
pub(crate) suspended_host_coro: Option<SuspendedHostCoroState>,
26-
pub(crate) module: ModuleInstance,
27-
pub(crate) frame: CallFrame,
24+
struct SuspendedInterpreterRuntimeBody {
25+
host_coro: Option<SuspendedHostCoroState>,
26+
executor_stack: Stack,
27+
executor_module: ModuleInstance,
28+
executor_frame: CallFrame,
2829
}
2930

30-
#[derive(Debug)]
31-
pub(crate) struct SuspendedRuntime {
32-
pub(crate) body: Option<(SuspendedRuntimeBody, Stack)>,
33-
}
34-
impl SuspendedRuntime {
35-
fn make_exec<'store, 'stack>(
36-
body: SuspendedRuntimeBody,
37-
stack: &'stack mut Stack,
38-
store: &'store mut Store,
39-
) -> Executor<'store, 'stack> {
40-
Executor { cf: body.frame, suspended_host_coro: body.suspended_host_coro, module: body.module, store, stack }
41-
}
42-
fn unmake_exec(exec: Executor<'_, '_>) -> SuspendedRuntimeBody {
43-
SuspendedRuntimeBody { suspended_host_coro: exec.suspended_host_coro, module: exec.module, frame: exec.cf }
31+
impl SuspendedInterpreterRuntimeBody {
32+
fn new(
33+
host_coro: Option<SuspendedHostCoroState>,
34+
executor_stack: Stack,
35+
executor_module: ModuleInstance,
36+
executor_frame: CallFrame,
37+
) -> Self {
38+
Self { host_coro, executor_stack, executor_module, executor_frame }
4439
}
4540
}
4641

47-
impl<'a> coro::CoroState<stack::Stack, FuncContext<'a>> for SuspendedRuntime {
48-
fn resume(
49-
&mut self,
50-
ctx: FuncContext<'a>,
51-
arg: ResumeArgument,
52-
) -> Result<coro::CoroStateResumeResult<stack::Stack>> {
53-
// should be put back into self.body unless we're finished
54-
let (body, mut stack) = if let Some(body_) = self.body.take() {
55-
body_
42+
#[derive(Debug, Default)]
43+
pub(crate) struct SuspendedInterpreterRuntime(Option<SuspendedInterpreterRuntimeBody>);
44+
45+
pub(crate) type InterpreterRuntimeExecOutcome = coro::PotentialCoroCallResult<Stack, SuspendedInterpreterRuntime>;
46+
pub(crate) type InterpreterRuntimeResumeOutcome = coro::CoroStateResumeResult<Stack>;
47+
48+
impl<'_a> coro::CoroState<Stack, FuncContext<'_a>> for SuspendedInterpreterRuntime {
49+
fn resume(&mut self, ctx: FuncContext<'_a>, arg: ResumeArgument) -> Result<InterpreterRuntimeResumeOutcome> {
50+
let body = if let Some(body) = self.0.take() {
51+
body
5652
} else {
57-
return Err(crate::error::Error::InvalidResume);
53+
// no suspended state to continue
54+
return Result::Err(crate::Error::InvalidResume);
5855
};
5956

60-
let mut exec = Self::make_exec(body, &mut stack, ctx.store);
61-
let resumed = match exec.resume(arg) {
62-
Ok(resumed) => resumed,
63-
Err(err) => {
64-
self.body = Some((Self::unmake_exec(exec), stack));
65-
return Err(err);
57+
let SuspendedInterpreterRuntimeBody { host_coro, executor_stack, executor_module, executor_frame } = body;
58+
59+
let mut stack = executor_stack;
60+
let mut exec =
61+
executor::Executor { cf: executor_frame, module: executor_module, store: ctx.store, stack: &mut stack };
62+
let res = match exec.resume(arg, host_coro) {
63+
Ok(val) => val,
64+
Err(e) => {
65+
let Executor { cf, module, .. } = exec;
66+
// pack back in case host_coro isn't corrupted and can be continued
67+
self.0 = Some(SuspendedInterpreterRuntimeBody::new(e.1, stack, module, cf));
68+
return Err(e.0);
6669
}
6770
};
68-
match resumed {
69-
executor::ExecOutcome::Return(()) => Ok(coro::CoroStateResumeResult::Return(stack)),
70-
executor::ExecOutcome::Suspended(suspend) => {
71-
self.body = Some((Self::unmake_exec(exec), stack));
72-
Ok(coro::CoroStateResumeResult::Suspended(suspend))
71+
72+
return Ok(match res {
73+
ExecOutcome::Return(()) => {
74+
// we are finished
75+
InterpreterRuntimeResumeOutcome::Return(stack)
76+
}
77+
ExecOutcome::Suspended(suspend_reason, host_coro) => {
78+
// host_coro could be different host_coro than one we provided
79+
let Executor { cf, module, .. } = exec;
80+
self.0 = Some(SuspendedInterpreterRuntimeBody::new(host_coro, stack, module, cf));
81+
InterpreterRuntimeResumeOutcome::Suspended(suspend_reason)
7382
}
74-
}
83+
});
7584
}
7685
}
7786

78-
pub(crate) type RuntimeExecOutcome = coro::PotentialCoroCallResult<stack::Stack, SuspendedRuntime>;
79-
8087
impl InterpreterRuntime {
81-
pub(crate) fn exec(&self, store: &mut Store, stack: stack::Stack) -> Result<RuntimeExecOutcome> {
82-
let mut stack = stack;
88+
pub(crate) fn exec(&self, store: &mut Store, mut stack: Stack) -> Result<InterpreterRuntimeExecOutcome> {
8389
let mut executor = executor::Executor::new(store, &mut stack)?;
84-
match executor.run_to_suspension()? {
85-
coro::CoroStateResumeResult::Return(()) => Ok(RuntimeExecOutcome::Return(stack)),
86-
coro::CoroStateResumeResult::Suspended(suspend) => Ok(RuntimeExecOutcome::Suspended(
87-
suspend,
88-
SuspendedRuntime { body: Some((SuspendedRuntime::unmake_exec(executor), stack)) },
89-
)),
90-
}
90+
let result = executor.run_to_suspension()?;
91+
Ok(match result {
92+
ExecOutcome::Return(()) => InterpreterRuntimeExecOutcome::Return(stack),
93+
ExecOutcome::Suspended(suspend_reason, host_coro) => {
94+
let Executor { cf, module, .. } = executor;
95+
InterpreterRuntimeExecOutcome::Suspended(
96+
suspend_reason,
97+
SuspendedInterpreterRuntime(Some(SuspendedInterpreterRuntimeBody::new(
98+
host_coro, stack, module, cf,
99+
))),
100+
)
101+
}
102+
})
91103
}
92104
}

0 commit comments

Comments
 (0)