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

Commit 450881e

Browse files
committed
feat: all async/resume functionality is now feature-gated
1 parent 27c7d66 commit 450881e

File tree

12 files changed

+311
-227
lines changed

12 files changed

+311
-227
lines changed

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ rust-version.workspace=true
3030
name="wasm-rust"
3131
test=false
3232

33+
[[example]]
34+
name="host_coro"
35+
required-features=["async"]
36+
3337
[dev-dependencies]
3438
wat={workspace=true}
3539
eyre={workspace=true}

crates/tinywasm/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ logging=["log", "tinywasm-parser?/logging", "tinywasm-types/logging"]
3737
std=["tinywasm-parser?/std", "tinywasm-types/std"]
3838
parser=["dep:tinywasm-parser"]
3939
archive=["tinywasm-types/archive"]
40-
test_async=[] #feels weird putting it here
40+
async=[]
41+
test_async=["async"] #feels weird putting it here
4142

4243
[[test]]
4344
name="test-wasm-1"

crates/tinywasm/src/coro.rs

Lines changed: 220 additions & 200 deletions
Large diffs are not rendered by default.

crates/tinywasm/src/error.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ use tinywasm_types::FuncType;
66
#[cfg(feature = "parser")]
77
pub use tinywasm_parser::ParseError;
88

9-
use crate::{coro::UnexpectedSuspendError, interpreter};
9+
#[cfg(feature = "async")]
10+
use crate::coro::UnexpectedSuspendError;
11+
12+
use crate::interpreter;
1013

1114
/// Errors that can occur for `TinyWasm` operations
1215
#[derive(Debug)]
@@ -45,6 +48,7 @@ pub enum Error {
4548

4649
/// Function unexpectedly yielded instead of returning
4750
/// (for backwards compatibility with old api)
51+
#[cfg(feature = "async")]
4852
UnexpectedSuspend(UnexpectedSuspendError),
4953

5054
#[cfg(feature = "std")]
@@ -196,6 +200,9 @@ impl Display for Error {
196200
#[cfg(feature = "std")]
197201
Self::Io(err) => write!(f, "I/O error: {err}"),
198202

203+
#[cfg(feature = "async")]
204+
Self::UnexpectedSuspend(_) => write!(f, "funtion yielded instead of returning"),
205+
199206
Self::Trap(trap) => write!(f, "trap: {trap}"),
200207
Self::Linker(err) => write!(f, "linking error: {err}"),
201208
Self::InvalidLabelType => write!(f, "invalid label type"),
@@ -205,8 +212,6 @@ impl Display for Error {
205212
write!(f, "invalid host function return: expected={expected:?}, actual={actual:?}")
206213
}
207214
Self::InvalidStore => write!(f, "invalid store"),
208-
209-
Self::UnexpectedSuspend(_) => write!(f, "funtion yielded instead of returning"),
210215
Self::InvalidResumeArgument => write!(f, "invalid resume argument supplied to suspended function"),
211216
Self::InvalidResume => write!(f, "attempt to resume coroutine that has already finished"),
212217
}

crates/tinywasm/src/func.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
use crate::coro::CoroState;
1+
#[cfg(feature = "async")]
2+
use {crate::coro::CoroState, tinywasm_types::ResumeArgument};
3+
24
use crate::interpreter;
35
use crate::interpreter::executor::SuspendedHostCoroState;
46
use crate::interpreter::stack::{CallFrame, Stack};
57
use crate::{log, unlikely, Function};
68
use crate::{Error, FuncContext, Result, Store};
79
use alloc::{boxed::Box, format, string::String, string::ToString, vec, vec::Vec};
8-
use tinywasm_types::{ExternRef, FuncRef, FuncType, ModuleInstanceAddr, ResumeArgument, ValType, WasmValue};
10+
use tinywasm_types::{ExternRef, FuncRef, FuncType, ModuleInstanceAddr, ValType, WasmValue};
911

1012
#[derive(Debug)]
1113
/// A function handle
@@ -152,7 +154,7 @@ impl<P: IntoWasmValueTuple, R: FromWasmValueTuple> FuncHandleTyped<P, R> {
152154
// Convert the Vec<WasmValue> back to R
153155
result
154156
.map_result(|vals| R::from_wasm_value_tuple(&vals))
155-
.map_state(|state| SuspendedFuncTyped::<R> { func: state, _marker: core::marker::PhantomData {} })
157+
.map_state(|state| SuspendedFuncTyped::<R> { func: state, _marker: Default::default() })
156158
.propagate_err_result()
157159
}
158160
}
@@ -161,11 +163,13 @@ pub(crate) type FuncHandleCallOutcome = crate::coro::PotentialCoroCallResult<Vec
161163
pub(crate) type TypedFuncHandleCallOutcome<R> = crate::coro::PotentialCoroCallResult<R, SuspendedFuncTyped<R>>;
162164

163165
#[derive(Debug)]
166+
#[cfg_attr(not(feature = "async"), allow(unused))]
164167
struct SuspendedWasmFunc {
165168
runtime: interpreter::SuspendedRuntime,
166169
result_types: Box<[ValType]>,
167170
}
168171
impl SuspendedWasmFunc {
172+
#[cfg(feature = "async")]
169173
fn resume(
170174
&mut self,
171175
ctx: FuncContext<'_>,
@@ -176,6 +180,7 @@ impl SuspendedWasmFunc {
176180
}
177181

178182
#[derive(Debug)]
183+
#[cfg_attr(not(feature = "async"), allow(unused))]
179184
#[allow(clippy::large_enum_variant)] // Wasm is bigger, but also much more common variant
180185
enum SuspendedFuncInner {
181186
Wasm(SuspendedWasmFunc),
@@ -184,13 +189,15 @@ enum SuspendedFuncInner {
184189

185190
/// handle to function that was suspended and can be resumed
186191
#[derive(Debug)]
192+
#[cfg_attr(not(feature = "async"), allow(unused))]
187193
pub struct SuspendedFunc {
188194
func: SuspendedFuncInner,
189195
module_addr: ModuleInstanceAddr,
190196
store_id: usize,
191197
}
192198

193199
impl crate::coro::CoroState<Vec<WasmValue>, &mut Store> for SuspendedFunc {
200+
#[cfg(feature = "async")]
194201
fn resume(
195202
&mut self,
196203
store: &mut Store,
@@ -208,6 +215,7 @@ impl crate::coro::CoroState<Vec<WasmValue>, &mut Store> for SuspendedFunc {
208215
}
209216
}
210217

218+
#[cfg_attr(not(feature = "async"), allow(unused))]
211219
pub struct SuspendedFuncTyped<R> {
212220
pub func: SuspendedFunc,
213221
pub(crate) _marker: core::marker::PhantomData<R>,
@@ -223,6 +231,7 @@ impl<R> crate::coro::CoroState<R, &mut Store> for SuspendedFuncTyped<R>
223231
where
224232
R: FromWasmValueTuple,
225233
{
234+
#[cfg(feature = "async")]
226235
fn resume(&mut self, ctx: &mut Store, arg: ResumeArgument) -> Result<crate::CoroStateResumeResult<R>> {
227236
self.func.resume(ctx, arg)?.map_result(|vals| R::from_wasm_value_tuple(&vals)).propagate_err()
228237
}

crates/tinywasm/src/instance.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@ use alloc::{boxed::Box, format, rc::Rc, string::ToString};
22
use tinywasm_types::*;
33

44
use crate::func::{FromWasmValueTuple, IntoWasmValueTuple};
5-
use crate::{
6-
Error, FuncHandle, FuncHandleTyped, Imports, MemoryRef, MemoryRefMut, Module, PotentialCoroCallResult, Result,
7-
Store, SuspendedFunc,
8-
};
5+
use crate::{Error, FuncHandle, FuncHandleTyped, Imports, MemoryRef, MemoryRefMut, Module, Result, Store};
6+
#[cfg(feature = "async")]
7+
use crate::{PotentialCoroCallResult, SuspendedFunc};
98

109
/// An instanciated WebAssembly module
1110
///
@@ -270,8 +269,9 @@ impl ModuleInstance {
270269
/// Invoke the start function of the module
271270
///
272271
/// Returns None if the module has no start function
273-
/// If start function suspends, returns SuspededFunc.
272+
/// If start function suspends, returns SuspendedFunc.
274273
/// Only when it finishes can this module instance be considered instantiated
274+
#[cfg(feature = "async")]
275275
pub fn start_coro(&self, store: &mut Store) -> Result<Option<PotentialCoroCallResult<(), SuspendedFunc>>> {
276276
let Some(func) = self.start_func(store)? else {
277277
return Ok(None);

crates/tinywasm/src/interpreter/executor.rs

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,21 @@ use super::no_std_floats::NoStdFloatExt;
55
use alloc::boxed::Box;
66
use alloc::{format, rc::Rc, string::ToString};
77
use core::ops::ControlFlow;
8-
use coro::SuspendReason;
98
use interpreter::simd::exec_next_simd;
109
use interpreter::stack::CallFrame;
1110
use tinywasm_types::*;
1211

12+
#[cfg(feature = "async")]
13+
use coro::SuspendReason;
14+
1315
use super::num_helpers::*;
1416
use super::stack::{BlockFrame, BlockType, Stack};
1517
use super::values::*;
1618
use crate::*;
1719

1820
pub(crate) enum ReasonToBreak {
1921
Errored(Error),
22+
#[cfg_attr(not(feature = "async"), allow(unused))]
2023
Suspended(SuspendReason),
2124
Finished,
2225
}
@@ -28,18 +31,20 @@ impl From<ReasonToBreak> for ControlFlow<ReasonToBreak> {
2831
}
2932

3033
#[derive(Debug)]
34+
#[cfg_attr(not(feature = "async"), allow(unused))]
3135
pub(crate) struct SuspendedHostCoroState {
3236
pub(crate) coro_state: Box<dyn HostCoroState>,
3337
// plug into used in store.get_func to get original function
3438
// can be used for checking returned types
35-
#[allow(dead_code)] // not implemented yet, but knowing context is useful
39+
#[allow(dead_code)] // knowing context is useful for debug and other possible future uses
3640
pub(crate) coro_orig_function: u32,
3741
}
3842

3943
#[derive(Debug)]
4044
pub(crate) struct Executor<'store, 'stack> {
4145
pub(crate) cf: CallFrame,
4246
pub(crate) module: ModuleInstance,
47+
#[cfg(feature = "async")]
4348
pub(crate) suspended_host_coro: Option<SuspendedHostCoroState>,
4449
pub(crate) store: &'store mut Store,
4550
pub(crate) stack: &'stack mut Stack,
@@ -51,7 +56,14 @@ impl<'store, 'stack> Executor<'store, 'stack> {
5156
pub(crate) fn new(store: &'store mut Store, stack: &'stack mut Stack) -> Result<Self> {
5257
let current_frame = stack.call_stack.pop().expect("no call frame, this is a bug");
5358
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 })
59+
Ok(Self {
60+
cf: current_frame,
61+
module: current_module,
62+
#[cfg(feature = "async")]
63+
suspended_host_coro: None,
64+
stack,
65+
store,
66+
})
5567
}
5668

5769
#[inline(always)]
@@ -67,6 +79,7 @@ impl<'store, 'stack> Executor<'store, 'stack> {
6779
}
6880
}
6981

82+
#[cfg(feature = "async")]
7083
#[inline(always)]
7184
pub(crate) fn resume(&mut self, res_arg: ResumeArgument) -> Result<ExecOutcome> {
7285
if let Some(coro_state) = self.suspended_host_coro.as_mut() {
@@ -105,6 +118,7 @@ impl<'store, 'stack> Executor<'store, 'stack> {
105118
/// execution may not be suspended in the middle of execution the funcion:
106119
/// so only do it as the last thing or first thing in the intsruction execution
107120
#[must_use = "If this returns ControlFlow::Break, the caller should propagate it"]
121+
#[cfg(feature = "async")]
108122
fn check_should_suspend(&mut self) -> ControlFlow<ReasonToBreak> {
109123
if let Some(flag) = &self.store.suspend_cond.suspend_flag {
110124
if flag.load(core::sync::atomic::Ordering::Acquire) {
@@ -130,6 +144,11 @@ impl<'store, 'stack> Executor<'store, 'stack> {
130144
ControlFlow::Continue(())
131145
}
132146

147+
#[cfg(not(feature = "async"))]
148+
fn check_should_suspend(&mut self) -> ControlFlow<ReasonToBreak> {
149+
ControlFlow::Continue(())
150+
}
151+
133152
#[inline(always)]
134153
fn exec_next(&mut self) -> ControlFlow<ReasonToBreak> {
135154
use tinywasm_types::Instruction::*;
@@ -414,7 +433,7 @@ impl<'store, 'stack> Executor<'store, 'stack> {
414433
self.module.swap_with(self.cf.module_addr(), self.store);
415434
ControlFlow::Continue(())
416435
}
417-
fn exec_call_host(&mut self, host_func: Rc<HostFunction>, func_ref: u32) -> ControlFlow<ReasonToBreak> {
436+
fn exec_call_host(&mut self, host_func: Rc<HostFunction>, _func_ref: u32) -> ControlFlow<ReasonToBreak> {
418437
let params = self.stack.values.pop_params(&host_func.ty.params);
419438
let res = host_func.call(FuncContext { store: self.store, module_addr: self.module.id() }, &params).to_cf()?;
420439
match res {
@@ -424,9 +443,10 @@ impl<'store, 'stack> Executor<'store, 'stack> {
424443
self.check_should_suspend()?; // who knows how long we've spent in host function
425444
ControlFlow::Continue(())
426445
}
446+
#[cfg(feature = "async")]
427447
PotentialCoroCallResult::Suspended(suspend_reason, state) => {
428448
self.suspended_host_coro =
429-
Some(SuspendedHostCoroState { coro_state: state, coro_orig_function: func_ref });
449+
Some(SuspendedHostCoroState { coro_state: state, coro_orig_function: _func_ref });
430450
self.cf.incr_instr_ptr();
431451
ReasonToBreak::Suspended(suspend_reason).into()
432452
}

crates/tinywasm/src/interpreter/mod.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@ mod values;
66

77
#[cfg(not(feature = "std"))]
88
mod no_std_floats;
9+
#[cfg(feature = "async")]
10+
use {executor::Executor, tinywasm_types::ResumeArgument};
911

1012
use crate::coro;
1113
use crate::{FuncContext, ModuleInstance, Result, Store};
12-
use executor::{Executor, SuspendedHostCoroState};
14+
use executor::SuspendedHostCoroState;
1315
use stack::{CallFrame, Stack};
14-
use tinywasm_types::ResumeArgument;
1516
pub use values::*;
1617

1718
/// The main `TinyWasm` runtime.
@@ -21,6 +22,7 @@ pub use values::*;
2122
pub struct InterpreterRuntime {}
2223

2324
#[derive(Debug)]
25+
#[cfg_attr(not(feature = "async"), allow(unused))]
2426
pub(crate) struct SuspendedRuntimeBody {
2527
pub(crate) suspended_host_coro: Option<SuspendedHostCoroState>,
2628
pub(crate) module: ModuleInstance,
@@ -29,8 +31,10 @@ pub(crate) struct SuspendedRuntimeBody {
2931

3032
#[derive(Debug)]
3133
pub(crate) struct SuspendedRuntime {
34+
#[cfg_attr(not(feature = "async"), allow(unused))]
3235
pub(crate) body: Option<(SuspendedRuntimeBody, Stack)>,
3336
}
37+
#[cfg(feature = "async")]
3438
impl SuspendedRuntime {
3539
fn make_exec<'store, 'stack>(
3640
body: SuspendedRuntimeBody,
@@ -44,10 +48,11 @@ impl SuspendedRuntime {
4448
}
4549
}
4650

47-
impl<'a> coro::CoroState<stack::Stack, FuncContext<'a>> for SuspendedRuntime {
51+
impl coro::CoroState<stack::Stack, FuncContext<'_>> for SuspendedRuntime {
52+
#[cfg(feature = "async")]
4853
fn resume(
4954
&mut self,
50-
ctx: FuncContext<'a>,
55+
ctx: FuncContext<'_>,
5156
arg: ResumeArgument,
5257
) -> Result<coro::CoroStateResumeResult<stack::Stack>> {
5358
// should be put back into self.body unless we're finished
@@ -83,6 +88,7 @@ impl InterpreterRuntime {
8388
let mut executor = executor::Executor::new(store, &mut stack)?;
8489
match executor.run_to_suspension()? {
8590
coro::CoroStateResumeResult::Return(()) => Ok(RuntimeExecOutcome::Return(stack)),
91+
#[cfg(feature = "async")]
8692
coro::CoroStateResumeResult::Suspended(suspend) => Ok(RuntimeExecOutcome::Suspended(
8793
suspend,
8894
SuspendedRuntime { body: Some((SuspendedRuntime::unmake_exec(executor), stack)) },

crates/tinywasm/src/lib.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,12 +91,20 @@ pub(crate) mod log {
9191
}
9292

9393
mod error;
94-
pub use coro::{CoroState, CoroStateResumeResult, PotentialCoroCallResult, SuspendReason};
94+
#[cfg(not(feature = "async"))]
95+
#[allow(unused)]
96+
use coro::{CoroState, CoroStateResumeResult, PotentialCoroCallResult, SuspendReason};
97+
#[cfg(feature = "async")]
98+
pub use {
99+
coro::{CoroState, CoroStateResumeResult, PotentialCoroCallResult, SuspendReason},
100+
module::IncompleteModule,
101+
};
102+
95103
pub use error::*;
96104
pub use func::{FuncHandle, FuncHandleTyped, SuspendedFunc};
97105
pub use imports::*;
98106
pub use instance::ModuleInstance;
99-
pub use module::{IncompleteModule, Module};
107+
pub use module::Module;
100108
pub use reference::*;
101109
pub use store::*;
102110

crates/tinywasm/src/module.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1-
use crate::{CoroState, Imports, ModuleInstance, PotentialCoroCallResult, Result, Store, SuspendedFunc};
2-
use tinywasm_types::{ResumeArgument, TinyWasmModule};
1+
#[cfg(feature = "async")]
2+
use crate::{CoroState, PotentialCoroCallResult, SuspendedFunc};
3+
#[cfg(feature = "async")]
4+
use tinywasm_types::ResumeArgument;
5+
6+
use crate::{Imports, ModuleInstance, Result, Store};
7+
use tinywasm_types::TinyWasmModule;
38

49
/// A WebAssembly Module
510
///
@@ -59,6 +64,7 @@ impl Module {
5964

6065
/// same as [Self::instantiate] but accounts for possibility of start function suspending, in which case it returns
6166
/// [PotentialCoroCallResult::Suspended]. You can call [CoroState::resume] on it at any time to resume instantiation
67+
#[cfg(feature = "async")]
6268
pub fn instantiate_coro(
6369
self,
6470
store: &mut Store,
@@ -80,11 +86,14 @@ impl Module {
8086

8187
/// a corostate that results in [ModuleInstance] when finished
8288
#[derive(Debug)]
89+
#[cfg(feature = "async")]
8390
pub struct IncompleteModule(Option<HitTheFloor>);
8491

8592
#[derive(Debug)]
93+
#[cfg(feature = "async")]
8694
struct HitTheFloor(ModuleInstance, SuspendedFunc);
8795

96+
#[cfg(feature = "async")]
8897
impl CoroState<ModuleInstance, &mut Store> for IncompleteModule {
8998
fn resume(&mut self, ctx: &mut Store, arg: ResumeArgument) -> Result<crate::CoroStateResumeResult<ModuleInstance>> {
9099
let mut body: HitTheFloor = match self.0.take() {

0 commit comments

Comments
 (0)