Skip to content

Commit e5f234b

Browse files
feat: add support for portable-atomic, add send feature
Signed-off-by: Henry <mail@henrygressmann.de>
1 parent 11052c6 commit e5f234b

29 files changed

Lines changed: 244 additions & 148 deletions

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1313
- `ResourceLimiter` callbacks for memory and table allocation or growth
1414
- A default `validate` Cargo feature & parser option to skip wasm validation
1515
- Optional parse-time operand deduplication to reduce `.twasm` archive size
16+
- Optional `send` support for moving stores and store-local handles across threads
17+
- Optional portable atomic shared pointers and counters for targets without native compare-and-swap
1618

1719
### Changed
1820

Cargo.lock

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,10 @@ See the [examples](./examples) directory and [documentation](https://docs.rs/tin
5757
- **`debug`:** Derives `Debug` for runtime types. Enabled by default.
5858
- **`parallel-parser`:** Parallelizes function parsing when `std` is enabled. Enabled by default.
5959
- **`guest-debug`:** Exposes module-internal by-index inspection APIs (`*_by_index`).
60+
- **`send`:** Makes stores and store-local handles movable across threads
6061
- **`simd-x86`:** Enables x86-specific SIMD intrinsics and uses `unsafe` internally.
6162

62-
With default features disabled, `tinywasm` depends only on `core`, `alloc`, and `libm`, making it usable in `no_std + alloc` environments.
63+
With default features disabled, `tinywasm` supports `no_std + alloc` and depends only on `libm`.
6364

6465
Use [`Engine`](https://docs.rs/tinywasm/latest/tinywasm/engine/struct.Engine.html) and [`engine::Config`](https://docs.rs/tinywasm/latest/tinywasm/engine/struct.Config.html) for non-default fuel accounting, stack sizing, or GC collection thresholds. A configured `ResourceLimiter` can allow, reject, or trap memory and table allocation or growth requests, and GC object allocations.
6566

crates/parser/Cargo.toml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,18 @@ wasmparser = { workspace = true, features = ["simd"] }
1717

1818
[features]
1919
default = ["log", "parallel", "std", "validate"]
20+
21+
# enable integration with the log crate
2022
log = ["dep:log"]
23+
24+
# parse and validate function bodies in parallel (requires std)
2125
parallel = ["std"]
26+
27+
# support targets without native atomic CAS
28+
portable-atomic = ["tinywasm-types/portable-atomic"]
29+
30+
# enable standard library support
2231
std = ["tinywasm-types/std", "wasmparser/std"]
32+
33+
# validate WebAssembly while parsing
2334
validate = ["wasmparser/features", "wasmparser/validate"]

crates/parser/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,8 @@ impl Parser {
292292
}
293293

294294
let section_end = buffer_offset + section_size;
295-
let section_bytes = alloc::sync::Arc::<[u8]>::from(buffer[buffer_offset..section_end].to_vec());
295+
let section_bytes =
296+
tinywasm_types::Shared::<[u8]>::from(buffer[buffer_offset..section_end].to_vec());
296297
reader.queue_owned_code_section(count, parser.offset(), section_bytes, validator.as_mut())?;
297298
parser.skip_section();
298299
buffer_offset = section_end;

crates/parser/src/module.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::log::debug;
33
use crate::validation::{FuncToValidate, ValidatorResources};
44
use crate::validation::{FuncValidatorAllocations, Validator};
55
use crate::{ParseError, ParserOptions, Result, conversion::*, optimize};
6-
use alloc::{boxed::Box, format, string::ToString, sync::Arc, vec::Vec};
6+
use alloc::{boxed::Box, format, string::ToString, vec::Vec};
77
use core::marker::PhantomData;
88
use core::ops::Range;
99
use tinywasm_types::*;
@@ -37,7 +37,7 @@ pub(crate) fn optimize_function_code(
3737
pub(crate) struct ModuleReader<'a> {
3838
func_validator_allocations: Option<FuncValidatorAllocations>,
3939
operators_reader_allocations: Option<OperatorsReaderAllocations>,
40-
translation_metadata: Option<Arc<crate::visit::ModuleMetadata>>,
40+
translation_metadata: Option<crate::visit::ModuleMetadata>,
4141

4242
has_code_section: bool,
4343
has_type_section: bool,
@@ -48,7 +48,7 @@ pub(crate) struct ModuleReader<'a> {
4848
pub(crate) types: TypeSection,
4949
pub(crate) code_type_addrs: Box<[u32]>,
5050
code_results: Box<[ValueCounts]>,
51-
pub(crate) exports: Arc<[Export]>,
51+
pub(crate) exports: Shared<[Export]>,
5252
pub(crate) code: Vec<OptimizedFunctionCode>,
5353
pub(crate) globals: Box<[Global]>,
5454
pub(crate) tables: Box<[TableDefinition]>,
@@ -68,17 +68,17 @@ pub(crate) struct ModuleReader<'a> {
6868
impl<'a> ModuleReader<'a> {
6969
fn translation_metadata(&mut self) -> &crate::visit::ModuleMetadata {
7070
if self.translation_metadata.is_none() {
71-
self.translation_metadata = Some(Arc::new(crate::visit::ModuleMetadata::new(
71+
self.translation_metadata = Some(crate::visit::ModuleMetadata::new(
7272
&self.types,
7373
&self.code_type_addrs,
7474
&self.imports,
7575
&self.globals,
7676
&self.memory_types,
7777
&self.tables,
7878
&self.tags,
79-
)));
79+
));
8080
}
81-
self.translation_metadata.as_deref().unwrap()
81+
self.translation_metadata.as_ref().unwrap()
8282
}
8383

8484
pub(crate) fn process_payload(&mut self, payload: Payload<'_>, validator: Option<&mut Validator>) -> Result<()> {
@@ -435,7 +435,7 @@ impl<'a> ModuleReader<'a> {
435435
&mut self,
436436
count: u32,
437437
body_offset: u64,
438-
section_bytes: Arc<[u8]>,
438+
section_bytes: Shared<[u8]>,
439439
validator: Option<&mut Validator>,
440440
) -> Result<()> {
441441
#[cfg(feature = "validate")]
@@ -545,7 +545,7 @@ impl<'a> ModuleReader<'a> {
545545
self.types.get(ty_idx).and_then(SubType::as_func).expect("function type was checked while parsing");
546546
let params = ValueCounts::from_iter(ty.params());
547547

548-
Ok(Arc::new(WasmFunction {
548+
Ok(Shared::new(WasmFunction {
549549
instructions: code.instructions.into_boxed_slice(),
550550
data: code.data,
551551
locals: code.locals,

crates/parser/src/parallel.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
use crate::module::{OptimizedFunctionCode, optimize_function_code};
22
use crate::validation::{FuncToValidate, FuncValidatorAllocations, ValidatorResources};
33
use crate::{ParseError, ParserOptions, Result, conversion};
4-
use alloc::sync::Arc;
54
use alloc::vec::Vec;
65
use core::ops::Range;
7-
use tinywasm_types::ValueCounts;
6+
use tinywasm_types::{Shared, ValueCounts};
87
use wasmparser::OperatorsReaderAllocations;
98

109
pub(crate) enum FunctionBodyInput<'a> {
@@ -15,7 +14,7 @@ pub(crate) enum FunctionBodyInput<'a> {
1514
pub(crate) struct OwnedFunctionBody {
1615
// A deferred stream code section is copied once, then shared by all queued
1716
// function jobs from that section.
18-
pub section_bytes: Arc<[u8]>,
17+
pub section_bytes: Shared<[u8]>,
1918
pub body_range: Range<usize>,
2019
pub body_offset: u64,
2120
}

crates/tinywasm/Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ name = "test-wasm-wide-arithmetic"
8888
[dependencies]
8989
libm = { version = "0.2", default-features = false }
9090
log = { workspace = true, optional = true }
91+
portable-atomic = { version = "1.15.0", default-features = false, optional = true }
9192
tinywasm-parser = { workspace = true, optional = true }
9293
tinywasm-types = { workspace = true }
9394

@@ -111,6 +112,9 @@ default = [
111112
log = ["dep:log", "tinywasm-parser?/log", "tinywasm-types/log"]
112113
std = ["tinywasm-parser?/std", "tinywasm-types/std"]
113114

115+
# make stores, instances, references, and host callbacks movable across threads
116+
send = []
117+
114118
# support for parsing WebAssembly
115119
parser = ["dep:tinywasm-parser"]
116120

@@ -120,6 +124,9 @@ validate = ["parser", "tinywasm-parser/validate"]
120124
# parallelize function parsing/validation across threads (requires std)
121125
parallel-parser = ["parser", "tinywasm-parser?/parallel"]
122126

127+
# support targets without native atomic CAS
128+
portable-atomic = ["dep:portable-atomic", "tinywasm-parser?/portable-atomic", "tinywasm-types/portable-atomic"]
129+
123130
# support for "archiving" tinywasm bytecode
124131
archive = ["tinywasm-types/archive"]
125132

crates/tinywasm/src/engine.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
use alloc::sync::Arc;
1+
use alloc::boxed::Box;
22

33
use crate::ResourceLimiter;
4+
use crate::shared::StoreShared;
45

56
/// Global configuration for the WebAssembly interpreter
67
///
7-
/// Can be cheaply cloned and shared across multiple executions and threads.
8+
/// Can be cheaply cloned across stores. With the `send` feature, it can also be
9+
/// moved and shared across threads.
810
///
911
/// ## Example
1012
/// ```rust
@@ -117,7 +119,7 @@ pub struct Config {
117119
/// Fuel accounting policy used by budgeted execution. Defaults to [`FuelPolicy::PerInstruction`].
118120
pub fuel_policy: FuelPolicy,
119121
/// Resource limiter shared across all stores created from this engine. Defaults to `None`.
120-
pub resource_limiter: Option<Arc<dyn ResourceLimiter>>,
122+
pub resource_limiter: Option<StoreShared<dyn ResourceLimiter>>,
121123
/// Initial number of GC heap bytes that triggers collection.
122124
/// Defaults to 1 MiB.
123125
pub gc_collection_threshold: usize,
@@ -168,8 +170,11 @@ impl Config {
168170
}
169171

170172
/// Set the resource limiter shared across all stores created from this engine.
171-
pub fn with_resource_limiter(mut self, limiter: Arc<dyn ResourceLimiter>) -> Self {
172-
self.resource_limiter = Some(limiter);
173+
///
174+
/// The limiter is converted to TinyWasm's internal shared pointer. Pass the
175+
/// limiter value directly rather than wrapping it in `Rc` or `Arc`.
176+
pub fn with_resource_limiter(mut self, limiter: impl ResourceLimiter + 'static) -> Self {
177+
self.resource_limiter = Some(StoreShared::from(Box::new(limiter) as Box<dyn ResourceLimiter>));
173178
self
174179
}
175180

crates/tinywasm/src/func/context.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,8 @@ impl FuncContext<'_> {
119119
}
120120
func.func.item.validate_store(self.store)?;
121121
let func_instance = self.store.state.get_func(func.func.addr()).clone();
122-
if matches!(&func_instance.kind, crate::store::FunctionKind::Host(host) if host.typed_callback().is_none()) {
122+
if matches!(&func_instance.inner, crate::store::FunctionInstanceInner::Host(host) if host.typed_callback().is_none())
123+
{
123124
let ty = self.store.state.get_canonical_func_type(func_instance.type_addr);
124125
let (param_count, result_count) = (ty.params().len(), ty.results().len());
125126
self.store.with_scratch_values(param_count + result_count, |store, values| {

0 commit comments

Comments
 (0)