Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
3b3b80b
feat: expose externally serviced host-call continuations
matthargett Nov 2, 2026
0f3904c
feat: isolate optional host continuations and define shared C API req…
matthargett Nov 2, 2026
91b6462
feat: add shared numeric C continuations and conformance fixture
matthargett Nov 2, 2026
e0642c9
docs: record continuation size and performance measurements
matthargett Nov 2, 2026
485de14
test: separate fixture diagnostics from backend LTO warnings
matthargett Nov 2, 2026
d71f9d7
test: audit async readiness and expand native sanitizer coverage
matthargett Nov 3, 2026
6059423
test: match Windows client and runtime CRT selection
matthargett Nov 3, 2026
f9205d2
docs: refresh pinned Windows sanitizer receipts
matthargett Nov 3, 2026
dbf1d6e
feat: demonstrate bounded stackless HTTP components
matthargett Nov 3, 2026
ac22bbc
test: make stackless receipts portable across hosts
matthargett Nov 3, 2026
2826c6d
Add a bounded WASI 0.3 HTTP service adapter
matthargett Nov 3, 2026
86caa8e
fix: wake only pending transfers on peer closure
matthargett Nov 3, 2026
04957b0
feat: add a bounded HTTP/2 service fixture
matthargett Nov 3, 2026
142eaf1
test: record HTTP/2 hosted and Cortex-M33 validation
matthargett Nov 3, 2026
2ffa27f
test: exercise GC and SIMD continuations with the shared C fixture
matthargett Nov 4, 2026
6230791
test: audit native unwinding and document missing guest traces
matthargett Nov 4, 2026
60072ee
feat: add optional guest source snapshots and async failure completion
matthargett Nov 4, 2026
1be986b
test: join native crash traces with guest sources and async ancestry
matthargett Nov 4, 2026
cf17b59
test: make the crash recorder portable to strict C11 builds
matthargett Nov 4, 2026
5194cfb
test: retain diagnostic qualification and disabled-build receipts
matthargett Nov 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 102 additions & 0 deletions .github/workflows/shared_continuations.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
name: Shared C continuations
on:
workflow_dispatch:
pull_request:
paths: ["crates/parser/**", "crates/types/**", "crates/tinywasm/**", "crates/c-api/**", ".github/workflows/shared_continuations.yml"]
permissions:
contents: read
jobs:
c-fixture:
name: C fixture (${{ matrix.os }}, ${{ matrix.rust }})
strategy:
matrix:
include:
- os: ubuntu-24.04
rust: stable
extra: ""
core_args: "--features external-host-calls,diagnostics"
capi_args: "--features external-host-calls,diagnostics"
- os: windows-2025
rust: stable
extra: ""
core_args: "--features external-host-calls,diagnostics"
capi_args: "--features external-host-calls,diagnostics"
- os: windows-2025
rust: nightly-2026-08-28
extra: "--tail-calls"
core_args: "--no-default-features --features full,std,nightly-tail-calls,external-host-calls,diagnostics"
capi_args: "--features external-host-calls,tinywasm/nightly-tail-calls,diagnostics"
runs-on: ${{ matrix.os }}
env:
WASM_EXT_TEST_ONLY: "1"
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
- uses: actions-rust-lang/setup-rust-toolchain@ecabd13d1c56bd1345c230e542e9144811ad706f
with:
toolchain: ${{ matrix.rust }}
- if: runner.os != 'Windows'
run: python3 crates/c-api/tests/continuations/run.py --runtime tinywasm --root . --build target/shared-c-fixture --feature on --gc --simd ${{ matrix.extra }}
- name: Windows x64 release and C-client ASan (Rust uninstrumented)
if: runner.os == 'Windows'
shell: cmd
run: |
for /f "usebackq tokens=*" %%i in (`"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -latest -property installationPath`) do call "%%i\Common7\Tools\VsDevCmd.bat" -arch=amd64
python crates/c-api/tests/continuations/run.py --runtime tinywasm --root . --build target/shared-c-fixture --feature on --gc --simd ${{ matrix.extra }}
if errorlevel 1 exit /b 1
python crates/c-api/tests/continuations/run.py --runtime tinywasm --root . --build target/shared-c-client-asan --client-asan --gc --simd ${{ matrix.extra }}
- run: cargo test --locked -p tinywasm ${{ matrix.core_args }} --test host_execution
- run: cargo test --locked -p tinywasm-c-api ${{ matrix.capi_args }} --lib
rust-and-c-asan:
name: Linux x64 Rust, std and C ASan
runs-on: ubuntu-24.04
env:
WASM_EXT_TEST_ONLY: "1"
CC: clang
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
- uses: actions-rust-lang/setup-rust-toolchain@ecabd13d1c56bd1345c230e542e9144811ad706f
with:
toolchain: nightly
components: rust-src
- run: python3 crates/c-api/tests/continuations/run.py --runtime tinywasm --root . --build target/shared-c-full-asan --gc --simd --sanitize --diagnostics
miri:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
- uses: actions-rust-lang/setup-rust-toolchain@ecabd13d1c56bd1345c230e542e9144811ad706f
with:
toolchain: nightly
components: miri
- run: cargo miri test --locked -p tinywasm-c-api --features diagnostics --lib

native-unwind:
name: Guest/native/async diagnostics (${{ matrix.os }})
strategy:
matrix:
include:
- os: ubuntu-24.04
debugger: gdb
extra: ""
- os: macos-15
debugger: lldb
extra: "--swift"
runs-on: ${{ matrix.os }}
env:
WASM_EXT_TEST_ONLY: "1"
CARGO_PROFILE_RELEASE_DEBUG: "1"
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
- if: runner.os == 'Linux'
run: sudo apt-get update && sudo apt-get install -y gdb
- run: cargo install wasm-tools --locked --version 1.247.0
- run: python3 crates/c-api/tests/continuations/run.py --runtime tinywasm --root . --build build/native-unwind --diagnostics
- run: python3 crates/c-api/tests/continuations/debug/diagnostic_run.py --runtime tinywasm --include crates/c-api/include --library build/native-unwind/runtime/release/libtinywasm.a --build build/native-unwind/client --debugger ${{ matrix.debugger }} ${{ matrix.extra }}
- run: python3 crates/c-api/tests/continuations/debug/test_symbolize.py
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
if: always()
with:
name: diagnostic-traces-${{ matrix.os }}
path: |
build/native-unwind/client/debugger.txt
build/native-unwind/client/receipt.json
build/native-unwind/client/tests.txt
47 changes: 47 additions & 0 deletions .github/workflows/stackless_http.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: Stackless HTTP component
on:
workflow_dispatch:
pull_request:
paths: ["crates/tinywasm/**", "crates/c-api/**", "crates/c-api/examples/stackless-http/**", ".github/workflows/stackless_http.yml"]
permissions:
contents: read
jobs:
hosted:
name: Component assertions and TCP (${{ matrix.os }})
strategy:
fail-fast: false
matrix:
os: [ubuntu-24.04, windows-2025]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
repository: WebAssembly/component-model
ref: fed10b0b823b82553dd8363f16a640816f5eac65
path: .stackless-component-tests
- uses: actions-rust-lang/setup-rust-toolchain@ecabd13d1c56bd1345c230e542e9144811ad706f
with:
toolchain: stable
rustflags: ""
- name: Linux release and canonical assertions
if: runner.os != 'Windows'
run: python3 crates/c-api/examples/stackless-http/run.py --runtime tinywasm --root . --build build/stackless --component-tests .stackless-component-tests
- name: Windows x64 release and C-client ASan (Rust uninstrumented)
if: runner.os == 'Windows'
shell: cmd
run: |
for /f "usebackq tokens=*" %%i in (`"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -latest -property installationPath`) do call "%%i\Common7\Tools\VsDevCmd.bat" -arch=amd64
if errorlevel 1 exit /b 1
python crates/c-api/examples/stackless-http/run.py --runtime tinywasm --root . --build build/stackless --component-tests .stackless-component-tests
if errorlevel 1 exit /b 1
python crates/c-api/examples/stackless-http/run.py --runtime tinywasm --root . --build build/stackless-asan --component-tests .stackless-component-tests --client-asan
- name: Install Rust std sources for full instrumentation
if: runner.os != 'Windows' && true
run: rustup toolchain install nightly --profile minimal --component rust-src
- name: Linux runtime/client ASan
if: runner.os != 'Windows'
env:
CC: clang
RUSTUP_TOOLCHAIN: nightly
run: python3 crates/c-api/examples/stackless-http/run.py --runtime tinywasm --root . --build build/stackless-asan --component-tests .stackless-component-tests --sanitize
4 changes: 4 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,7 @@ jobs:
- name: Run clippy
run: cargo clippy --workspace --target ${{ matrix.target }} ${{ matrix.args }}
if: matrix.target != 'armv7-unknown-linux-gnueabihf'

- name: Run optional host-call continuation tests
if: matrix.target == 'x86_64-unknown-linux-gnu'
run: cargo test -p tinywasm --target ${{ matrix.target }} --features external-host-calls --test host_execution
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- `Function::call_resumable_with_host_calls` and `HostExecution` for externally serviced host calls,
with checked results, rooted arguments, and access to the calling module's context, behind the
opt-in `external-host-calls` feature.
- Experimental numeric C continuations in `wasm_ext.h`, with a shared WAMR fixture and
disabled-feature linked-artifact measurements. The shared fixtures also exercise guest GC
and SIMD across suspension, cancellation and indirect host calls.

## [0.12.0-beta.0] - 2026-11-01

Preliminary x86-64 benchmarks show around a 20% performance improvement over 0.11.0 on nightly, depending on the workload.
Expand Down
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,27 @@ assert_eq!(result, 3);

See the [examples](./examples) directory and [documentation](https://docs.rs/tinywasm) for more information.

## External host-call scheduling

Enable the `external-host-calls` Cargo feature to include this API. It is excluded from both
`default` and `full`; disabled builds do not compile the continuation driver or its public types.

`Function::call_resumable_with_host_calls` returns a `HostExecution` that stops before each
host callback. On `HostExecProgress::HostCall`, obtain `host_call()` and either `invoke()` the
registered synchronous callback or `complete(&results)` with externally produced values.
Dropping the pending-call view leaves the call waiting; resuming does not replay it.

An external adapter can own futures, register wakeups, and resume Wasm only when work is ready.
It must cancel that work when its invocation is dropped. `PendingHostCall::with_context` gives
the adapter access to host state and the calling module's exported memory and functions,
including blocking calls to canonical ABI realloc and post-return functions. Arguments remain
rooted across collection, and completion checks result types and store ownership.

Component parsing, canonical ABI lowering, resource tables, and task scheduling remain outside
the runtime. Each execution handle exclusively borrows its store; nested calls are synchronous.
This seam does not implement the WASI 0.3 component concurrency model. The existing synchronous
and fuel-only execution APIs use their existing drivers and dispatch paths.

## Cargo Features

- **`full`:** Enables `archive`, `debug`, `parallel-parser`, `parser`, `state`, and `validate`. Enabled by default.
Expand Down
3 changes: 3 additions & 0 deletions crates/c-api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ name = "tinywasm"
tinywasm = { workspace = true, features = ["archive", "parser", "std", "validate"] }

[features]
# Optional bounded diagnostic snapshots and original Wasm source offsets.
diagnostics = ["external-host-calls", "tinywasm/diagnostics"]
external-host-calls = ["tinywasm/external-host-calls"]
custom-prefix = []

[lints]
Expand Down
5 changes: 4 additions & 1 deletion crates/c-api/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ TARGET_DIR ?= $(if $(CARGO_TARGET_DIR),$(CARGO_TARGET_DIR),../../target)
BUILD_LIBDIR := $(abspath $(TARGET_DIR)/$(if $(filter dev,$(PROFILE)),debug,$(PROFILE)))
BUILDDIR := $(abspath $(TARGET_DIR)/c-api)
CARGO_FLAGS := --profile $(PROFILE)
ifeq ($(CONTINUATIONS),1)
CARGO_FLAGS += --features external-host-calls
endif
ifneq ($(TINYWASM_C_API_PREFIX),)
export TINYWASM_C_API_PREFIX
CARGO_FLAGS += --features custom-prefix
Expand Down Expand Up @@ -44,7 +47,7 @@ test: example

install: build
install -d "$(DESTDIR)$(PREFIX)/include" "$(DESTDIR)$(LIBDIR)/pkgconfig" "$(DESTDIR)$(PREFIX)/share/licenses/tinywasm"
install -m644 include/wasm.h include/tinywasm.h include/tinywasm-prefix.h "$(DESTDIR)$(PREFIX)/include/"
install -m644 include/wasm.h include/wasm_ext.h include/tinywasm.h include/tinywasm-prefix.h "$(DESTDIR)$(PREFIX)/include/"
install -m644 "$(BUILD_LIBDIR)/libtinywasm.a" "$(BUILD_LIBDIR)/libtinywasm.$(SHARED_EXT)" "$(DESTDIR)$(LIBDIR)/"
install -m644 ../../LICENSE-APACHE ../../LICENSE-MIT "$(DESTDIR)$(PREFIX)/share/licenses/tinywasm/"
sed -e 's|@PREFIX@|$(PREFIX)|g' -e 's|@LIBDIR@|$(LIBDIR)|g' -e 's|@VERSION@|$(VERSION)|g' -e 's|@NATIVE_LIBS@|$(NATIVE_LIBS)|g' -e 's|@PREFIX_FLAG@|$(if $(TINYWASM_C_API_PREFIX),-DTINYWASM_C_API_PREFIX=$(TINYWASM_C_API_PREFIX))|g' tinywasm.pc.in > "$(DESTDIR)$(LIBDIR)/pkgconfig/tinywasm.pc"
40 changes: 39 additions & 1 deletion crates/c-api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,45 @@ When building with Cargo directly, enable `custom-prefix` and set the
`TINYWASM_C_API_PREFIX` environment variable. Include `tinywasm.h` before
`wasm.h` so the symbol aliases take effect.

## Notes
## Runtime-neutral extension direction

The integration target is the standard `wasm.h` plus a small, separately versioned common
extension header implemented by TinyWasm and WAMR. `wasm_ext.h` is an experimental numeric continuation implementation, not an approved standard.
Enable the C wrapper's `external-host-calls` feature (`CONTINUATIONS=1` with Make).
The Rust API is its backend machinery; see [the shared fixture](tests/continuations/README.md).
See the [WAMR roadmap discussion](https://github.com/wasm-micro-runtime/wasm-micro-runtime/discussions/5164#discussioncomment-19111943).

Keep ordinary module linking, calls, memories, tables, and ownership on the standard API.
Test the same external component/WASI layer against both implementations. A common header
alone does not establish shared semantics, object compatibility, or binary plugin compatibility.
Objects must remain with their originating runtime and store. Symbol prefixes permit static
coexistence; any runtime dispatch table belongs in an optional adapter outside the core.

The prototype provides opaque invocation handles, completed/pending/error outcomes, host-call
suspension, checked completion, and cancellation. Qualify reference rooting, caller memory/context
access, nested canonical ABI operations, error/trap ownership, and finalizer ordering before
freezing declarations. Callback-style
async can use ordinary calls; stackful suspension support must be qualified per execution mode.
Do not expose interpreter frames, instruction pointers, or Rust futures across the C ABI.
Wakers, event loops, resource tables, canonical ABI, and WASI implementations stay external.

Optional functionality must not add fields to ordinary stores/instances/call frames, branches
to synchronous dispatch, global registration roots, or allocation/initialization work in disabled
builds. Put implementations in separately selectable crates or translation units. Avoid forcing
whole-archive retention; shared-library export roots and callback tables need explicit auditing.
LTO cannot remove observable fields or reachable hooks merely because an application never
uses their feature.

Acceptance requires final linked-artifact comparisons with identical compiler, linker, LTO,
whole-program, panic, allocator, and stripping settings: baseline versus extension-disabled;
enabled-but-unused; and enabled-and-used. Check text/rodata/data/bss, retained symbols and link
maps, store/frame layouts, heap/stack high-water marks, and execution cost. Use the same portable
C conformance fixtures for both runtimes, including shared memory/table imports and cancel/error
paths. On a Pico, measure static RAM and heap/stack peaks; RSS applies to hosted deployments.
The current TinyWasm C wrapper requires `std`; Pico C-wrapper support is not qualified by hosted C fixture tests. Final linked-artifact
comparisons and process-memory measurements are recorded separately from Rust seam tests.

## Current API limitations

- Follow the ownership annotations in `wasm.h` and use the matching delete
functions. Keep a store and its objects on the same thread.
Expand Down
69 changes: 69 additions & 0 deletions crates/c-api/examples/stackless-http/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Copyright (C) 2026 Matt Hargett. All rights reserved.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
cmake_minimum_required(VERSION 3.20)
project(stackless_http C)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)

if(NOT P3_RUNTIME_LIBRARY OR NOT P3_RUNTIME_INCLUDE OR NOT P3_HANDLER_INCLUDE)
message(FATAL_ERROR "Set P3_RUNTIME_LIBRARY, P3_RUNTIME_INCLUDE and P3_HANDLER_INCLUDE")
endif()

add_executable(stackless_http p3.c vm.c task.c http.c server.c)
add_executable(component_check p3.c vm.c check.c)
add_executable(wasi_http_check p3.c vm.c task.c wasi_http.c wasi_http_check.c)
target_compile_definitions(wasi_http_check PRIVATE P3_WASI_HTTP P3_WASI_HTTP_HOSTED P3_IMPORTS=128 HTTP_FIELD_BYTES=4096)
target_compile_definitions(component_check PRIVATE P3_HANDLES=128)
set(targets stackless_http component_check wasi_http_check)
if(P3_WASI_HANDLER_INCLUDE)
add_executable(wasi_http_server p3.c vm.c task.c wasi_http.c wasi_http_io.c wasi_http_server.c server.c)
target_include_directories(wasi_http_server PRIVATE ${P3_WASI_HANDLER_INCLUDE})
target_compile_definitions(wasi_http_server PRIVATE P3_WASI_HTTP P3_WASI_HTTP_HOSTED P3_IMPORTS=128)
list(APPEND targets wasi_http_server)
endif()
if(P3_H2_HANDLER_INCLUDE)
if(NOT P3_NGHTTP2_INCLUDE OR NOT P3_NGHTTP2_LIBRARY)
message(FATAL_ERROR "Set P3_NGHTTP2_INCLUDE and P3_NGHTTP2_LIBRARY")
endif()
add_executable(wasi_h2_server p3.c vm.c task.c wasi_http.c wasi_http_io.c h2.c h2_server.c)
target_include_directories(wasi_h2_server PRIVATE ${P3_H2_HANDLER_INCLUDE} ${P3_NGHTTP2_INCLUDE})
target_link_libraries(wasi_h2_server PRIVATE ${P3_NGHTTP2_LIBRARY})
target_compile_definitions(wasi_h2_server PRIVATE P3_WASI_HTTP NGHTTP2_STATICLIB NGHTTP2_NO_SSIZE_T
P3_HANDLES=240 HTTP_RESOURCE_SLOTS=64 HTTP_PENDING_DROPS=48
H2_STREAMS=16 H2_PER_CONNECTION=8 H2_CONNECTIONS=4 H2_CONNECTION_HEAP=65536)
list(APPEND targets wasi_h2_server)
endif()
foreach(target IN LISTS targets)
target_include_directories(${target} PRIVATE ${P3_RUNTIME_INCLUDE} ${P3_HANDLER_INCLUDE})
target_link_libraries(${target} PRIVATE ${P3_RUNTIME_LIBRARY})
if(P3_WAMR)
target_compile_definitions(${target} PRIVATE P3_WAMR)
endif()
if(MSVC)
target_compile_definitions(${target} PRIVATE LIBWASM_STATIC WASM_API_EXTERN=)
target_compile_options(${target} PRIVATE /W3)
target_link_libraries(${target} PRIVATE ws2_32 psapi userenv bcrypt ntdll advapi32)
target_link_options(${target} PRIVATE /INCREMENTAL:NO /OPT:REF /OPT:ICF)
if(P3_ASAN)
target_compile_options(${target} PRIVATE /fsanitize=address /Zi)
endif()
else()
target_compile_options(${target} PRIVATE -Wall -Wextra -Werror -fvisibility=hidden)
if(P3_ASAN)
target_compile_options(${target} PRIVATE -fsanitize=address -fno-omit-frame-pointer -g)
target_link_options(${target} PRIVATE -fsanitize=address)
endif()
if(APPLE)
if(NOT P3_WAMR)
target_link_libraries(${target} PRIVATE "-framework Security" "-framework CoreFoundation" iconv)
endif()
target_link_options(${target} PRIVATE -Wl,-dead_strip)
else()
target_link_libraries(${target} PRIVATE m pthread dl)
target_link_options(${target} PRIVATE -Wl,--gc-sections)
endif()
endif()
if(NOT P3_ASAN)
set_property(TARGET ${target} PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE)
endif()
endforeach()
Loading
Loading