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

Commit ff933a2

Browse files
committed
chore: reorganized some code
1 parent 89d9dcc commit ff933a2

File tree

4 files changed

+123
-74
lines changed

4 files changed

+123
-74
lines changed

crates/tinywasm/src/coro.rs

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ pub trait CoroState<Ret, ResumeContext>: Debug {
1212

1313
/// explains why did execution suspend, and carries payload if needed
1414
#[derive(Debug)]
15+
#[non_exhaustive] // some variants are feature-gated
1516
pub enum SuspendReason {
1617
/// host function yielded
1718
/// potentially some host functions might expect resume argument when calling resume
@@ -59,7 +60,6 @@ pub enum CoroStateResumeResult<R> {
5960
}
6061

6162
impl<R, State> PotentialCoroCallResult<R, State> {
62-
6363
/// in case you expect function only to return
6464
/// you can make Suspend into [crate::Error::UnexpectedSuspend] error
6565
pub fn suspend_to_err(self) -> Result<R> {
@@ -95,24 +95,24 @@ 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" arg. user_val
99-
pub fn map_result_or_else<OutR, Usr>(
98+
/// transform result with mapper if there is none - calls "otherwise".
99+
/// user_val passed to whichever is called and is guaranteed to be used
100+
pub fn map<OutR, Usr, OutS>(
100101
self,
101102
user_val: Usr,
102-
mapper: impl FnOnce(R, Usr) -> OutR,
103-
otherwise: impl FnOnce(Usr),
104-
) -> PotentialCoroCallResult<OutR, State> {
103+
res_mapper: impl FnOnce(R, Usr) -> OutR,
104+
state_mapper: impl FnOnce(State, Usr) -> OutS,
105+
) -> PotentialCoroCallResult<OutR, OutS> {
105106
match self {
106-
Self::Return(res) => PotentialCoroCallResult::Return(mapper(res, user_val)),
107+
Self::Return(res) => PotentialCoroCallResult::Return(res_mapper(res, user_val)),
107108
Self::Suspended(suspend, state) => {
108-
otherwise(user_val);
109-
PotentialCoroCallResult::Suspended(suspend, state)
109+
PotentialCoroCallResult::Suspended(suspend, state_mapper(state, user_val))
110110
}
111111
}
112112
}
113113
/// transforms result
114114
pub fn map_result<OutR>(self, mapper: impl FnOnce(R) -> OutR) -> PotentialCoroCallResult<OutR, State> {
115-
self.map_result_or_else((), |val, _| mapper(val), |_| {})
115+
self.map((), |val, _| mapper(val), |s,_| {s})
116116
}
117117
}
118118

@@ -130,14 +130,15 @@ impl<R> CoroStateResumeResult<R> {
130130
pub fn map_result<OutR>(self, mapper: impl FnOnce(R) -> OutR) -> CoroStateResumeResult<OutR> {
131131
PotentialCoroCallResult::<R, ()>::from(self).map_result(mapper).into()
132132
}
133-
/// transform result with mapper if there is none - calls "otherwise" arg. user_val called
134-
pub fn map_result_or_else<OutR, Usr>(
133+
/// transform result with mapper. If there is none - calls "otherwise"
134+
/// user_val passed to whichever is called and is guaranteed to be used
135+
pub fn map<OutR, Usr>(
135136
self,
136137
user_val: Usr,
137138
mapper: impl FnOnce(R, Usr) -> OutR,
138139
otherwise: impl FnOnce(Usr),
139140
) -> CoroStateResumeResult<OutR> {
140-
PotentialCoroCallResult::<R, ()>::from(self).map_result_or_else(user_val, mapper, otherwise).into()
141+
PotentialCoroCallResult::<R, ()>::from(self).map(user_val, mapper, |(), usr|{otherwise(usr)}).into()
141142
}
142143
}
143144

crates/tinywasm/src/store/mod.rs

Lines changed: 10 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use alloc::{boxed::Box, format, string::ToString, vec::Vec};
22
use core::fmt::Debug;
3-
use core::ops::ControlFlow;
43
use core::sync::atomic::{AtomicUsize, Ordering};
54
use tinywasm_types::*;
65

@@ -12,8 +11,10 @@ mod element;
1211
mod function;
1312
mod global;
1413
mod memory;
14+
mod suspend_conditions;
1515
mod table;
1616

17+
pub use suspend_conditions::*;
1718
pub(crate) use {data::*, element::*, function::*, global::*, memory::*, table::*};
1819

1920
// global store id counter
@@ -34,7 +35,11 @@ pub struct Store {
3435

3536
pub(crate) data: StoreData,
3637
pub(crate) runtime: Runtime,
37-
// idk where to put it, but here it's accessible to host functions without modifying their signature
38+
39+
// idk where really to put it, but it should be accessible to host environment (obviously)
40+
// and (less obviously) to host functions called from store - for calling wasm callbacks and propagating this config to them
41+
// (or just complying with suspend conditions themselves)
42+
// alternatively it could be passed to function handles and passend into function context
3843
pub(crate) suspend_cond: SuspendConditions,
3944
}
4045

@@ -486,53 +491,7 @@ fn get_pair_mut<T>(slice: &mut [T], i: usize, j: usize) -> Option<(&mut T, &mut
486491
Some(pair)
487492
}
488493

489-
/// user callback for use in [SuspendConditions::suspend_cb]
490-
pub type ShouldSuspendCb = Box<dyn FnMut(&Store) -> ControlFlow<(), ()>>;
491-
492-
// idk where really to put it, but it should be accessible to host environment (obviously)
493-
// and (less obviously) to host functions called from it - for calling wasm callbacks and propagating this config to them
494-
// (or just complying with suspend conditions themselves)
495-
/// used to limit execution time wasm code takes
496-
#[derive(Default)]
497-
pub struct SuspendConditions {
498-
/// atomic flag. when set to true it means execution should suspend
499-
/// can be used to tell executor to stop from another thread
500-
pub suspend_flag: Option<alloc::sync::Arc<core::sync::atomic::AtomicBool>>,
501-
502-
/// instant at which execution should suspend
503-
/// can be used to control how much time will be spent in wasm without requiring other threads
504-
/// such as for time-slice multitasking
505-
/// uses rust standard library for checking time - so not available in no-std
506-
#[cfg(feature = "std")]
507-
pub timeout_instant: Option<crate::std::time::Instant>,
508-
509-
/// callback that returns [`ControlFlow::Break`]` when execution should suspend
510-
/// can be used when above methods are insufficient or
511-
/// instead of [`timeout_instant`] in no-std builds, if you have your own clock function
512-
pub suspend_cb: Option<ShouldSuspendCb>,
513-
}
514-
515-
impl Debug for SuspendConditions {
516-
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
517-
let stop_cb_text = if self.suspend_cb.is_some() { "<present>" } else { "<not present>" };
518-
let mut f = f.debug_struct("SuspendConditions");
519-
f.field("stop_flag", &self.suspend_flag);
520-
#[cfg(feature = "std")]
521-
{
522-
f.field("timeout_instant", &self.timeout_instant);
523-
}
524-
f.field("stop_cb", &stop_cb_text).finish()
525-
}
526-
}
527-
528-
impl SuspendConditions {
529-
/// sets timeout_instant to `how_long` from now
530-
#[cfg(feature = "std")]
531-
pub fn set_timeout_in(&mut self, how_long: crate::std::time::Duration) {
532-
self.timeout_instant = Some(crate::std::time::Instant::now() + how_long);
533-
}
534-
}
535-
494+
// suspend_conditions-related functions
536495
impl Store {
537496
/// sets suspend conditions for store
538497
pub fn set_suspend_conditions(&mut self, val: SuspendConditions) {
@@ -543,8 +502,8 @@ impl Store {
543502
&self.suspend_cond
544503
}
545504
/// transforms suspend conditions for store using user-provided function
546-
pub fn update_suspend_conditions(&mut self, replacer: impl FnOnce(SuspendConditions) -> SuspendConditions) {
505+
pub fn update_suspend_conditions(&mut self, mapper: impl FnOnce(SuspendConditions) -> SuspendConditions) {
547506
let temp = core::mem::take(&mut self.suspend_cond);
548-
self.suspend_cond = replacer(temp);
507+
self.suspend_cond = mapper(temp);
549508
}
550509
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
use crate::store::Store;
2+
use alloc::boxed::Box;
3+
use core::fmt::Debug;
4+
use core::ops::ControlFlow;
5+
6+
/// user callback for use in [SuspendConditions::suspend_cb]
7+
pub type ShouldSuspendCb = Box<dyn FnMut(&Store) -> ControlFlow<(), ()>>;
8+
9+
/// used to limit execution time wasm code takes
10+
#[derive(Default)]
11+
#[non_exhaustive] // some fields are feature-gated, use with*-methods to construct
12+
pub struct SuspendConditions {
13+
/// atomic flag. when set to true it means execution should suspend
14+
/// can be used to tell executor to stop from another thread
15+
pub suspend_flag: Option<alloc::sync::Arc<core::sync::atomic::AtomicBool>>,
16+
17+
/// instant at which execution should suspend
18+
/// can be used to control how much time will be spent in wasm without requiring other threads
19+
/// such as for time-slice multitasking
20+
/// uses rust standard library for checking time - so not available in no-std
21+
#[cfg(feature = "std")]
22+
pub timeout_instant: Option<crate::std::time::Instant>,
23+
24+
/// callback that returns [`ControlFlow::Break`]` when execution should suspend
25+
/// can be used when above ways are insufficient or
26+
/// instead of [`timeout_instant`] in no-std builds, with your own clock function
27+
pub suspend_cb: Option<ShouldSuspendCb>,
28+
}
29+
30+
impl Debug for SuspendConditions {
31+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
32+
let stop_cb_text = if self.suspend_cb.is_some() { "<present>" } else { "<not present>" };
33+
let mut f = f.debug_struct("SuspendConditions");
34+
f.field("stop_flag", &self.suspend_flag);
35+
#[cfg(feature = "std")]
36+
{
37+
f.field("timeout_instant", &self.timeout_instant);
38+
}
39+
f.field("stop_cb", &stop_cb_text).finish()
40+
}
41+
}
42+
43+
impl SuspendConditions {
44+
/// creates suspend_conditions with every condition unset
45+
pub fn new() -> Self {
46+
Default::default()
47+
}
48+
49+
/// sets timeout_instant to `how_long` from now
50+
#[cfg(feature = "std")]
51+
pub fn set_timeout_in(&mut self, how_long: crate::std::time::Duration) -> &mut Self {
52+
self.timeout_instant = Some(crate::std::time::Instant::now() + how_long);
53+
self
54+
}
55+
/// adds timeout at specified instant
56+
#[cfg(feature = "std")]
57+
pub fn with_timeout_at(self, when: crate::std::time::Instant) -> Self {
58+
Self { timeout_instant: Some(when), ..self }
59+
}
60+
/// adds timeout in specified duration
61+
#[cfg(feature = "std")]
62+
pub fn with_timeout_in(self, how_long: crate::std::time::Duration) -> Self {
63+
Self { timeout_instant: Some(crate::std::time::Instant::now() + how_long), ..self }
64+
}
65+
/// removes timeout
66+
pub fn without_timeout(self) -> Self {
67+
#[cfg(feature = "std")]
68+
{
69+
Self { timeout_instant: None, ..self }
70+
}
71+
#[cfg(not(feature = "std"))]
72+
{
73+
self
74+
}
75+
}
76+
77+
/// adds susped flag
78+
pub fn with_suspend_flag(self, should_suspend: alloc::sync::Arc<core::sync::atomic::AtomicBool>) -> Self {
79+
Self { suspend_flag: Some(should_suspend), ..self }
80+
}
81+
/// removes susped flag
82+
pub fn without_suspend_flag(self) -> Self {
83+
Self { suspend_flag: None, ..self }
84+
}
85+
86+
/// adds suspend callback
87+
pub fn with_suspend_callback(self, cb: ShouldSuspendCb) -> Self {
88+
Self { suspend_cb: Some(cb), ..self }
89+
}
90+
/// removes suspend callback
91+
pub fn without_suspend_callback(self) -> Self {
92+
Self { suspend_cb: None, ..self }
93+
}
94+
}

tests/wasm_resume.rs

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
use core::panic;
22
use eyre;
33
use std::sync;
4-
use std::{
5-
ops::ControlFlow,
6-
time::{Duration, Instant},
7-
};
4+
use std::{ops::ControlFlow, time::Duration};
85
use tinywasm::{
96
CoroState, CoroStateResumeResult, Module, ModuleInstance, PotentialCoroCallResult, Store, SuspendConditions,
107
SuspendReason,
@@ -17,18 +14,15 @@ fn main() -> std::result::Result<(), eyre::Report> {
1714
println!("\n# testing with callback");
1815
let mut cb_cond = |store: &mut Store| {
1916
let callback = make_suspend_in_time_cb(30);
20-
store.set_suspend_conditions(SuspendConditions { suspend_cb: Some(Box::new(callback)), ..Default::default() });
17+
store.set_suspend_conditions(SuspendConditions::new().with_suspend_callback(Box::new(callback)));
2118
};
2219
suspend_with_pure_loop(&mut cb_cond, SuspendReason::SuspendedCallback)?;
2320
suspend_with_wasm_fn(&mut cb_cond, SuspendReason::SuspendedCallback)?;
2421
suspend_with_host_fn(&mut cb_cond, SuspendReason::SuspendedCallback)?;
2522

2623
println!("\n# testing with epoch");
2724
let mut time_cond = |store: &mut Store| {
28-
store.set_suspend_conditions(SuspendConditions {
29-
timeout_instant: Some(Instant::now() + Duration::from_millis(10)),
30-
..Default::default()
31-
})
25+
store.set_suspend_conditions(SuspendConditions::new().with_timeout_in(Duration::from_millis(10)))
3226
};
3327
suspend_with_pure_loop(&mut time_cond, SuspendReason::SuspendedEpoch)?;
3428
suspend_with_wasm_fn(&mut time_cond, SuspendReason::SuspendedEpoch)?;
@@ -37,7 +31,7 @@ fn main() -> std::result::Result<(), eyre::Report> {
3731
println!("\n# testing atomic bool");
3832
let mut cb_thead = |store: &mut Store| {
3933
let arc = sync::Arc::<sync::atomic::AtomicBool>::new(sync::atomic::AtomicBool::new(false));
40-
store.set_suspend_conditions(SuspendConditions { suspend_flag: Some(arc.clone()), ..Default::default() });
34+
store.set_suspend_conditions(SuspendConditions::new().with_suspend_flag(arc.clone()));
4135
let handle = std::thread::spawn(move || {
4236
std::thread::sleep(Duration::from_millis(10));
4337
arc.store(true, sync::atomic::Ordering::Release);
@@ -70,6 +64,7 @@ fn try_compare(lhs: &SuspendReason, rhs: &SuspendReason) -> eyre::Result<bool> {
7064
SuspendReason::SuspendedEpoch => matches!(rhs, SuspendReason::SuspendedEpoch),
7165
SuspendReason::SuspendedCallback => matches!(rhs, SuspendReason::SuspendedCallback),
7266
SuspendReason::SuspendedFlag => matches!(rhs, SuspendReason::SuspendedFlag),
67+
_ =>eyre::bail!("unimplemented new variant"),
7368
})
7469
}
7570

0 commit comments

Comments
 (0)