webcpp::cobalt makes Boost.Cobalt-style C++20 applications usable in browsers
through Emscripten and in WASIp2/P3 runtimes such as Wasmtime. JSPI and Asyncify
are Emscripten suspension variants; they are not WASI versions. Cobalt supplies
coroutines and asynchronous composition, while Asio supplies the executor and I/O.
Start with Tutorial: await a timer to await a timer. Use How-to: select the compiled integration to select the runner and optional TLS, Configuration and deployment profiles to choose requirements, and Differences to understand the boundaries of compatibility. The Feature matrix distinguishes target facilities from runtime permission.
Tutorial: await a timer
Use the library at libs/cobalt in the WebCPP superproject, matching Boost 1.92
headers and a configured C++20 compiler. The application writes co_main;
the separately linked runner owns main and drives its executor.
#include <webcpp/test/exit_status.hpp>
#include <webcpp/asio/steady_timer.hpp>
#include <webcpp/cobalt/main.hpp>
#include <webcpp/cobalt/op.hpp>
#include <webcpp/cobalt/result.hpp>
#include <webcpp/cobalt/this_coro.hpp>
#include <chrono>
#include <cstdio>
// Boost 1.92 intentionally omits this promise hook without exceptions. Clang
// 21 and 23 still warn at the coroutine definition even with -fno-exceptions.
// Keep the workaround on this native example, without changing Boost's types.
#if defined(__clang__) && defined(WEBCPP_TARGET_NATIVE) && !defined(__EXCEPTIONS)
#if __has_warning("-Wcoroutine-missing-unhandled-exception")
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wcoroutine-missing-unhandled-exception"
#endif
#endif
webcpp::cobalt::main co_main([[maybe_unused]] int argc, [[maybe_unused]] char** argv) {
// Cobalt's awaiter has static members called by the compiler's co_await expansion.
// NOLINTNEXTLINE(readability-static-accessed-through-instance)
const auto exec = co_await webcpp::cobalt::this_coro::executor;
webcpp::asio::steady_timer timer(exec, std::chrono::milliseconds(50));
auto [error] = co_await webcpp::cobalt::as_tuple(timer.async_wait(webcpp::cobalt::use_op));
if (error) {
std::printf("timer error: %d\n", error.value());
co_return webcpp::test::exit_status(1);
}
std::puts("cobalt slept");
co_return webcpp::test::exit_status(0);
}
#if defined(__clang__) && defined(WEBCPP_TARGET_NATIVE) && !defined(__EXCEPTIONS)
#if __has_warning("-Wcoroutine-missing-unhandled-exception")
#pragma clang diagnostic pop
#endif
#endif
The example’s webcpp::test::exit_status reports its result to the project’s
browser test harness. It is test support, not a Cobalt API requirement.
The coroutine gets its executor, awaits an Asio timer with Cobalt’s completion
token, checks the error, then returns its exit status.
From the superproject root:
unset CPATH CPLUS_INCLUDE_PATH C_INCLUDE_PATH
b2 -a variant=release libs/cobalt/example//asio_cobalt.output
The matched output is:
cobalt slept
For a real browser, build the page driver with Emscripten:
b2 -a toolset=emscripten variant=release libs/cobalt/test/browser//browser-pages
b2 -a toolset=emscripten variant=release webcpp-suspend=asyncify libs/cobalt/test/browser//browser-pages
The driver serves the example page and checks its interaction in Chrome. A successful Node run alone does not establish browser support.
How-to: select the compiled integration
Include webcpp/cobalt/main.hpp and the operation headers and use webcpp::cobalt. Select one entry-point model:
| B2 dependency | Application contract |
|---|---|
|
The application supplies |
|
The application supplies |
|
Add the corrected SSL wrapper to a custom-main application. |
|
Combine the runner and corrected SSL wrapper. |
The SSL targets select the same source-built OpenSSL and embedded CA provider as Asio and Beast. The plain core does not pull in TLS. Browser HTTPS/WSS uses the browser’s TLS implementation and trust policy, not these OpenSSL targets.
Build with another build system
sources.json is the source inventory, split into core, runner, ssl and
multi_add for each target. B2’s generated src/sources.jam comes from the same
inventory. Consumers compile these already shipped files; the maintainer-only
upstream/sync.py is not a build step.
Use matching Boost 1.92 headers. Compile sources and application with the same
target, exception mode, threading ABI and Cobalt configuration. For native,
define BOOST_COBALT_NO_LIB=1 when linking the explicit archives. The native core
and runner use the unchanged upstream sources. The native SSL inventory selects
WebCPP’s separate corrected wrapper and composed-transfer adapter.
Add Asio’s explicit runtime source for Wasm: src/wasip2/pollable.cpp,
src/wasip3/waitable.cpp or src/browser_src.cpp from the Asio library. Verify
the pinned WASI SDK ABI before compiling its bridge. Emscripten also requires
embind, stack exports and the chosen suspension flags. Its browser bridge must
reach the final link as an object, so JavaScript import metadata is retained.
For guest TLS, provide matching OpenSSL headers and libssl/libcrypto, the
generated embedded-CA header include directory, WEBCPP_ASIO_HAS_OPENSSL=1 and
WEBCPP_COBALT_LINK_SSL=1. A macro alone does not link or configure OpenSSL.
The WASI OpenSSL provider also links wasi-libc’s wasi-emulated-getpid,
wasi-emulated-signal, wasi-emulated-process-clocks and wasi-emulated-mman
support libraries, after its crypto archive, as the consumer recipe records.
Replacing the CA bytes is an Asio TLS-context operation; it does not change the
browser’s certificate store.
The superproject’s tools/test/fixtures/portable-network/CMakeLists.txt is a
consumer recipe. tools/test/portable_network_distribution.py creates a source
archive with a SHA-256 inventory, deletes its staging inputs and extracts it into
a path with spaces. The archive omits Git/B2/cache files. Consumers use a direct
compiler or CMake. Compiler dependency files verify the actual source and header
paths, the complete Cobalt source selection and the configuration of each source.
Checkout headers and undeclared external dependencies fail that audit.
The optional --openssl-prefix and --ca-header inputs copy explicitly supplied
dependencies into the extracted tree before compiling the TLS tests. They must
match the selected target and ABI. --evidence-dir <directory> preserves the
tested source archive and compiler-receipt.json, including commands and resolved
dependency paths. These are local consumer proofs, not a second release pipeline.
For example, run the native CMake consumer from the superproject:
python3 tools/test/portable_network_distribution.py \
--compiler /path/to/clang++ --boost-include /path/to/boost/include \
--target native --build-system cmake \
--cobalt-executor custom --cobalt-pmr custom \
--evidence-dir /path/to/evidence
The custom configuration is the compiled fixture
test/distribution/consumer_config.hpp. It declares an Asio context executor and
a counting PMR resource before any Cobalt header. Every compiled source receives
that same preincluded header. The test crosses translation-unit boundaries and
checks allocations through the compiled core’s allocator. It proves that concrete
customization, not every user-defined executor or memory implementation.
Await a browser Promise
Use Asio’s browser::async_await with the executor obtained from
this_coro::executor and Cobalt’s use_op completion token. The operation
resumes the coroutine when JavaScript settles the Promise; rejection follows
the bridge’s error contract. DOM-event waits use the corresponding Asio bridge.
Neither operation exists on a native or WASI target without JavaScript hosting.
The Promise tests cover both exception modes and both Emscripten suspension modes.
Preserve the corrected native SSL composition
Include webcpp/cobalt/io/ssl.hpp, keep the concrete io::ssl::stream type and
call webcpp::cobalt::io::read or write for complete-buffer transfers.
These overloads preserve cancellation through Asio’s composed operations.
read_some and write_some remain primitive operations on the corrected stream.
Do not erase the stream to Boost’s generic I/O interface and expect this
composition correction to follow it. That selects Boost’s original generic
read/write implementation, whose cancellation association failed the native
regression. The wrapper’s handshake state protections still apply, but this
does not repair an upstream composition chosen through a different static type.
Stream and buffer storage must outlive the pending operation. A transferred-byte count can be nonzero on cancellation. Terminal cancellation does not make a connection reusable; end that connection according to the underlying Asio contract.
SSL handshake and stream state
The wrapper fixes the Boost.Cobalt SSL state defects on
native and WASIp2/P3. The same state guards apply with exceptions enabled or
disabled in a supported compiler/threading profile. WASI SDK 34 provides both
exception modes for single-thread P2/P3 and only EH off for cooperative P3;
native co_main limitations remain as described in Exceptions and errors.
Browser HTTPS/WSS uses host TLS and does not use this wrapper.
The fixed wrapper supports STARTTLS. Before its first handshake, reads and
writes use plain TCP. Complete a successful, verified handshake before sending
confidential application data. secure() reports TLS establishment, not whether
the application configured trust and hostname verification.
| State or operation | Behavior |
|---|---|
Before the first handshake |
|
Successful handshake |
|
Failed or cancelled handshake |
|
Handshake after an attempt has started |
Returns |
Shutdown before TLS establishment |
Returns |
Completed shutdown, including failure or cancellation |
Clears |
Both handshake overloads update the same state. The buffered overload reports consumption of already-read handshake bytes. Serialize state-changing operations; the wrapper does not synchronize overlapping handshakes, shutdown or direct SSL object manipulation.
native_ssl_handle() returns a borrowed SSL*, owned by the stream. Do not free
it or retain it after stream destruction. Use it to configure SNI before the
handshake and check the result. native_handle() still denotes the TCP socket.
Peer trust, hostname verification and SNI remain distinct application duties.
Cancelling an application transfer cannot retract bytes already sent and does
not undo the handshake, so secure() may remain true after transfer cancellation.
It is not a connection-health or retry-safety indicator. End the connection after
terminal cancellation; do not infer safe reuse from that flag.
Configuration and deployment profiles
This source distribution requires Boost 1.92.0 headers. The public configuration
rejects other Boost versions before an application can combine them with the
pinned compiled sources, including when compiling without B2. The native core
inventory includes src/native_version.cpp for the same check when only the
archive is built. Do not omit source-inventory entries.
Define requirements before any Cobalt header, consistently in every translation
unit. WEBCPP_COBALT_HAS_* macros are read-only observations: defining one does
not supply the missing environment feature. REQUIRE_* controls default to
unset; setting one to 1 requests a compile-time diagnostic when unavailable.
| Control or observation | Meaning |
|---|---|
|
Derived from the collection’s exception policy, or explicitly selected for this integration. It does not change the compiler’s exception ABI or repair upstream issue 287. |
|
Unset by default; set to |
|
Require guest TCP/UDP, unavailable in a normal browser. |
|
Require both a guest TLS target and explicit SSL linkage. |
|
Require a thread-enabled Asio/compiler profile. |
|
Require native local sockets. |
|
Require native operating-system signals, not coroutine cancellation signals. |
|
Require native serial-port support. |
|
Require reverse name lookup, unavailable in the WASI/browser adapters. |
|
Require guest-created asynchronous pipes with transfer, cancellation and EOF support. WASIp2 has no guest pipe creation; the Emscripten lifecycle is not supported. |
|
Require native POSIX descriptors for asynchronous stdio. SDK 34’s WASI stdio descriptors do not provide nonblocking mode. |
|
Require Asio’s explicit WASIp2 pollable stdio adapter, usable with Cobalt’s completion tokens. |
|
Asio’s shared browser scheduling default, |
The corresponding HAS_SOCKET_BACKEND, HAS_OPENSSL_BACKEND, HAS_THREADS,
HAS_LOCAL_SOCKETS, HAS_SIGNALS, HAS_SERIAL_PORT and HAS_REVERSE_DNS
observations use the WEBCPP_COBALT_ prefix. HAS_BROWSER_BRIDGE,
HAS_BROWSER_WORKERS and HAS_HOST_DNS distinguish JavaScript integration,
pthread hosting and forward lookup. Internal BACKEND_* macros are not public
configuration controls.
In a browser build, including io.hpp, io/acceptor.hpp, io/socket.hpp,
io/stream_socket.hpp or io/datagram_socket.hpp requests a raw socket facility
and produces a targeted diagnostic. Include only the coroutine headers needed
by a browser HTTP/WebSocket client and use Beast’s host transport adapter.
The same socket headers select the guest transport on native and WASIp2/P3.
Keep Boost’s build-wide Cobalt controls when selecting executor and memory
policies: BOOST_COBALT_USE_IO_CONTEXT, BOOST_COBALT_CUSTOM_EXECUTOR,
BOOST_COBALT_USE_STD_PMR, BOOST_COBALT_USE_BOOST_CONTAINER_PMR,
BOOST_COBALT_USE_CUSTOM_PMR and BOOST_COBALT_NO_PMR. Native sources read them
directly; Wasm configuration forwards them to the shipped implementation.
The defaults are the type-erased Asio executor and standard-library PMR.
Choose one executor policy and one PMR policy. Rebuild the complete core, runner,
optional SSL and application with matching definitions; changing only one
translation unit violates the library’s configuration contract.
Custom executor and PMR declarations follow Boost’s customization convention:
supply boost::cobalt::executor or boost::cobalt::pmr in a configuration header
included before Cobalt in every source. For Wasm, the executor must belong to the
selected WebCPP Asio backend. A custom PMR implementation or Boost.Container PMR
also brings its own explicit dependencies. Source-archive consumers cover the
default policy, io_context plus no-PMR, and an application-declared context
executor with counting PMR, using direct compilation and CMake. These witnesses
do not certify arbitrary custom executor/resource implementations.
Conflicting executor or PMR selections in one translation unit produce a configuration error. There is no general linker guard for arbitrary configuration differences between separately compiled translation units or prebuilt archives. The consumer recipe applies one common configuration target to the application, core, runner and SSL. Its dependency audit rejects a compiled source missing the requested policy. Rebuild all of them when changing policy; an existing archive cannot acquire a different ABI just by defining a macro in the application.
HAS_PIPE_CREATION, HAS_POSIX_STDIO and HAS_POLLABLE_STDIO are separate
observations. Receiving a host-provided stream does not imply that the guest can
create a pipe. Use WEBCPP_COBALT_FOR from async_for.hpp for asynchronous range
iteration; it forwards to the selected coroutine backend.
Emscripten 6.0.11 does not provide the complete pipe lifecycle. A Node test
confirms byte transfer and pending-read cancellation through Cobalt. A separate
pipe_eof_sdk_limitation probe calls only libc: after closing the last writer,
poll reports POLLIN|POLLHUP, but an empty nonblocking read returns EAGAIN
instead of EOF. This SDK limitation leaves HAS_PIPE_CREATION at zero; the
passing negative probe is not lifecycle support or browser evidence. The probe
fails if the SDK behavior changes, requiring the capability to be re-evaluated.
Native and WASIp3 continue to test closure of the final writer through Cobalt.
Native Windows retains Boost.Asio’s broken_pipe error for that read; native
POSIX and WASIp3 report eof. In each case no further bytes are returned.
Feature matrix
This table describes facilities, not a claim that every upstream example has passed in every combination. See Testing for the current evidence gates.
| Facility | Native | WASIp2 | WASIp3 | Emscripten |
|---|---|---|---|---|
Core coroutines and Asio timers |
Boost core |
Shipped backend |
Shipped backend |
Shipped backend |
TCP/UDP and forward DNS |
Operating system |
Pollables; host grants |
Waitables; host grants |
No raw guest transport |
Corrected Cobalt SSL wrapper |
OpenSSL |
OpenSSL |
OpenSSL |
Browser-managed TLS instead |
Promises and DOM events |
Not applicable |
Not applicable |
Not applicable |
Asio browser bridge |
Single-thread profile |
Available |
Available |
Available |
JSPI or Asyncify |
Thread profile |
Native threads |
Unavailable in SDK 34 |
Experimental cooperative threads, EH off |
Pthreads; isolation and worker hosting required |
C++ exceptions |
On/off |
On/off, single-thread |
On/off single-thread; off for cooperative profile |
On/off; flags depend on JSPI/Asyncify |
OS signals, local sockets, serial ports |
Subject to native Boost/platform |
Unavailable |
Unavailable |
Unavailable |
Reverse DNS |
Native resolver |
Unavailable |
Unavailable |
Unavailable |
Guest asynchronous pipe pair |
Subject to native Boost/platform |
Unavailable |
Available |
Unsupported lifecycle |
Async stdio through POSIX descriptors |
Native POSIX only |
No nonblocking mode |
No nonblocking mode |
Use an explicit browser channel |
Explicit pollable stdio adapter |
Not applicable |
Cancellation and reuse supported |
Not implemented |
Not applicable |
WASIp3 cooperative threads do not run simultaneously on CPU cores. Browser pthreads require the hosting policy and shared-memory facilities established by the worker examples. Neither a macro nor a compiled binary grants network access. The runtime can deny sockets or DNS even when the target provides them.
Exceptions and errors
Both exception modes remain selectable. The build warns about
Boost.Cobalt issue 287: moving some
pending coroutine objects with libc++ and exceptions disabled can abort. This
is an upstream limitation, not a claim that every use of std::move fails.
The warning is informational; it does not enable exceptions or make the operation
safe. Use the versioned reproducer when evaluating another compiler/runtime.
The native runtime tests keep Apple Clang’s known missing-promise-member
diagnostic visible with -Wno-error=coroutine-missing-unhandled-exception for
their no-exceptions consumers. This test-local setting is not exported by the
library targets; external strict builds must account for the upstream diagnostic
described in Differences.
Native MSVC rejects co_main with exceptions disabled because Boost 1.92’s
main_promise omits unhandled_exception. The test suite classifies that
specific configuration as an upstream compile limitation, paired with the
same coroutine compiling with exceptions enabled and a no-exceptions witness
for the missing promise member. This does not disable other no-exceptions
consumers or change the native Boost type. Use exceptions for native MSVC
co_main applications.
Testing
The migration remains under local validation. The current Original applications and environment counterparts records each original application and its available counterparts against the shipped implementation. Historical Asio/Cobalt results do not certify the new source layout. The independent whole-candidate review and its correction reviews accompany the local lint gate. Remote CI has separate evidence; a local review is not a remote CI verdict.
Native identity preserves Boost 1.92 defects outside the corrected SSL wrapper.
The upstream-limitations lane records precise failures for composed accept and
composition cancellation. Its output says UPSTREAM-LIMITATION; a passing
negative probe means the limitation was reproduced, not that cancellation works.
An unexpected exit, diagnostic or newly fixed upstream behavior fails the probe
and requires the compatibility record to be revisited.
The finite echo counterpart uses accept(socket&), which forwards cancellation
directly to the pending accept. It avoids Boost 1.92’s composed accept limitation
without changing native types or sources. Its TCP driver checks concurrent
traffic, EOF, cancellation before any connection, cancellation with a live
session, peer reset after a confirmed echo and invalid arguments. Both
cancellation cases require the server to finish normally and report its exact
session and byte counts.
The affected owned coroutine tests scope GCC 14’s allocation false-positive
diagnostic to their coroutine functions. The immutable upstream HTTP and ticker
compile probes instead keep that warning visible with
-Wno-error=mismatched-new-delete, only for their GCC 14 targets. Other warnings
remain errors, and neither setting is a library usage requirement. See
Differences for the upstream explanation.
The immutable upstream ticker declares a local msg that shadows its msg
parameter. MSVC diagnoses that valid source with C4457 under /W4; /WX then
rejects the compile probe. Only original_ticker selects /wd4457 on MSVC.
The source remains byte-identical, other warnings remain errors, and this option
is not exported to library consumers or other test programs.
That same MSVC probe uses /bigobj: its template-heavy Debug translation unit
exceeds the default COFF section limit (C1128). This changes the object-file
format limit, not the source, runtime behavior or warning policy.
The tests distinguish immutable upstream programs, namespace-only migrations and
behavioral counterparts. test/applications/upstream retains original hashes;
test/applications/prepare.py produces the separately identified namespace
migrations. A missing host facility is documented or diagnosed, never replaced
by an empty successful program.
The spsc-wakeup lane checks only the await_ready/await_suspend lost-wakeup
window. The adaptation closes that window but retains the original queue’s
delayed-notification race: a notification can resume a writer after its free
slot has already been reused, and a failed push is ignored. The spsc-sequence
lane is a deterministic limitation probe, with the immutable original on native
and the unchanged counterpart on native, Emscripten and WASIp3. Passing means
the known value loss was reproduced, not that concurrent transfer is reliable.
The browser workbench marks SPSC unavailable and does not start it. A native
failure in the original does not establish support for a Wasm backend.
Native SSL has compile-time type-identity tests for both header orders, plus runtime client, handshake-state, transfer-cancellation and shutdown tests against real local peers. The same TLS 1.2/1.3 state tests run on WASIp2/P3. A native CMake consumer also compiles the complete source inventory and runs those tests from a copied source snapshot without B2, Git or replacement Boost headers.
The Cobalt-owned tls-integration lane runs the SSL client, state and
cancellation fixtures in native single/multi-thread profiles with exceptions
on/off, WASIp3 single-thread profiles with exceptions on/off, and WASIp3
cooperative threads with exceptions off. It reuses the shared real TLS peer
driver and builds each Cobalt source directly, keeping the test report under
Cobalt ownership. Native MSVC single-thread fixtures select the static CRT so
the provider uses its no-threads configuration. The existing tls lane retains
its native, WASIp2 and WASIp3 coverage.
The SSL client also checks move construction, socket identity and cancellation after destroying the source, both before and after handshake completion.
Browser examples keep cancellation state in the owning module instance, with a scoped C++ registration. Duplicate registration cannot replace an active run; normal completion and exception unwinding remove it. Browser tests also call the exported cancellation function after completion and require a zero result.
The state cases cover STARTTLS, a buffered ClientHello, state observed at await
time, trust rejection, malformed CA data, hostname verification and SNI. Transfer
cancellation covers primitive reads/writes and complete-buffer operations with
partial progress. The peer applies backpressure; only an actual
operation_aborted satisfies a cancellation case. A completion that wins the
race requires a fresh connection and a bounded retry, rather than being counted
as cancellation. An outer watchdog catches lost completion.
Shutdown tests first wait until the peer observes close_notify. The peer then
either closes without its own TLS shutdown, producing stream_truncated, or
holds the connection while the coroutine requests cancellation, producing
operation_aborted. Both cases must clear secure(), block application I/O and
reject a second shutdown. Elapsed time alone is not proof of a pending operation.
b2 -a variant=release libs/cobalt/test/tls//tls
b2 -a libs/cobalt/test/tls//tls-integration
b2 -a toolset=clang-wasip3 libs/cobalt/test/tls//tls-integration
b2 -a variant=release libs/cobalt/test/tls//native_ssl_identity libs/cobalt/test/tls//native_ssl_identity_boost_first
b2 -a toolset=clang-wasip2 variant=release libs/cobalt/test/applications//migrated-portable
b2 -a toolset=clang-wasip3 variant=release libs/cobalt/test/applications//migrated-portable-p3
The TLS drivers grant networking explicitly and create short-lived local certificates. Run them with a Python/OpenSSL installation supporting TLS 1.3. The browser lanes use real Chrome and check page output and worker completion; Node-only examples are labeled separately. The shared published test report is the WebCPP report; it is evidence for the commit it names, not for an unsubmitted local candidate.
The worker lifecycle test receives a posted readiness notification after the worker has started its asynchronous timer wait. It then cancels and awaits the worker, checking completion and executor affinity. Readiness changes a timer’s expiry on the creator’s executor, so a notification arriving before the wait is retained. The test avoids repeated one-millisecond readiness polling, which overloaded the browser under controlled CPU throttling. Its five-second deadline still bounds the test; it is not a general worker-latency guarantee or a change to the SDK’s timer polling.
Performance, memory and binary-size comparisons must use optimized builds and record source hashes, compiler/linker flags, EH, threads, suspension mode and runtime. Raw Wasm and JavaScript sizes are separate artifacts. An unavailable measurement is not zero. The Release measurements of the shipped implementation records fresh observations of the shipped implementation, with source and artifact identities. Prior Asio benchmark results retain their original dates and environments.
bench/results/2026-11-07/ retains the quantitative report and raw archive.
test/applications/results/2026-11-07/ separately retains the three libraries’
application matrices, commands, logs and source snapshots. Each archive has a
SHA-256 file. These local results do not stand in for CI of the coordinated commits.
The independent review and local closure evidence are recorded separately in the
superproject’s execution ledger.
Original applications and environment counterparts
The inventory pins all twelve Cobalt 1.92 applications. Native core types are Boost types; Wasm uses the shipped WebCPP backend. Every cell below names the actual target, thread/exception setting and host. Browser page and dedicated worker observations are separate from Node.
A controlled network counterpart changes endpoints, ports and certificate setup. It retains the application protocol. Infinite original servers are stopped after observed traffic; that does not prove graceful shutdown. Browser HTTP/WSS counterparts use host transports and do not provide raw TCP listeners.
Original examples run in Release, so an original assert may be disabled by NDEBUG. A normal exit is not evidence for an assertion that did not execute. Separate finite portable counterparts check values, cancellation and completion explicitly in the same feasible profiles.
Current support includes the SPSC delayed-notification limitation: both the original queue and its wakeup adaptation can lose values. Their deterministic limitation probes preserve queue behavior; the browser workbench disables the counterpart. Earlier dated successful observations remain historical and do not establish reliable concurrent transfer.
source-requires-exceptions means the original application contains exception syntax. It does not mean Cobalt itself requires exceptions. Native no-EH Clang builds retain the documented upstream promise warning; only that diagnostic is nonfatal in these test consumers. P3 multi means cooperative threads, not CPU parallelism.
native
| Profile | channel | delay | delay_op | echo_server | http | outcome | python | signals | spsc | thread | thread_pool | ticker |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
native-single-on-native |
pass |
pass |
pass |
fixture |
fixture |
pass |
upstream defect + counterpart |
pass |
upstream defect + counterpart unavailable |
pass |
pass |
fixture |
native-single-off-native |
pass |
pass |
pass |
source EH |
fixture |
pass |
source EH |
pass |
upstream defect + counterpart unavailable |
source EH |
source EH |
source EH |
native-multi-on-native |
pass |
pass |
pass |
fixture |
fixture |
pass |
upstream defect + counterpart |
pass |
upstream defect + counterpart unavailable |
pass |
pass |
fixture |
native-multi-off-native |
pass |
pass |
pass |
source EH |
fixture |
pass |
source EH |
pass |
upstream defect + counterpart unavailable |
source EH |
source EH |
source EH |
wasip2
| Profile | channel | delay | delay_op | echo_server | http | outcome | python | signals | spsc | thread | thread_pool | ticker |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
wasip2-single-on-wasmtime |
pass |
pass |
pass |
fixture |
fixture |
pass |
dependency |
pass |
threads |
threads |
threads |
fixture |
wasip2-single-off-wasmtime |
pass |
pass |
pass |
source EH |
fixture |
pass |
dependency |
pass |
threads |
source EH |
source EH |
source EH |
wasip2-multi-on-wasmtime |
SDK |
SDK |
SDK |
SDK |
SDK |
SDK |
SDK |
SDK |
SDK |
SDK |
SDK |
SDK |
wasip2-multi-off-wasmtime |
SDK |
SDK |
SDK |
SDK |
SDK |
SDK |
SDK |
SDK |
SDK |
SDK |
SDK |
SDK |
wasip3
| Profile | channel | delay | delay_op | echo_server | http | outcome | python | signals | spsc | thread | thread_pool | ticker |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
wasip3-single-on-wasmtime |
pass |
pass |
pass |
fixture |
fixture |
pass |
dependency |
pass |
threads |
threads |
threads |
fixture |
wasip3-single-off-wasmtime |
pass |
pass |
pass |
source EH |
fixture |
pass |
dependency |
pass |
threads |
source EH |
source EH |
source EH |
wasip3-multi-on-wasmtime |
SDK |
SDK |
SDK |
SDK |
SDK |
SDK |
SDK |
SDK |
SDK |
SDK |
SDK |
SDK |
wasip3-multi-off-wasmtime |
pass |
pass |
pass |
source EH |
fixture |
pass |
dependency |
pass |
upstream defect + counterpart unavailable |
source EH |
source EH |
source EH |
jspi
| Profile | channel | delay | delay_op | echo_server | http | outcome | python | signals | spsc | thread | thread_pool | ticker |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
jspi-single-on-node |
pass |
pass |
pass |
platform |
platform |
pass |
dependency |
pass |
threads |
threads |
threads |
platform |
jspi-single-on-page |
pass |
pass |
pass |
platform |
platform + counterpart |
pass |
dependency |
pass |
threads |
threads |
threads |
platform + counterpart |
jspi-single-on-worker |
pass |
pass |
pass |
platform |
platform + counterpart |
pass |
dependency |
pass |
threads |
threads |
threads |
platform + counterpart |
jspi-single-off-node |
pass |
pass |
pass |
platform |
platform |
pass |
dependency |
pass |
threads |
source EH |
source EH |
platform |
jspi-single-off-page |
pass |
pass |
pass |
platform |
platform + counterpart |
pass |
dependency |
pass |
threads |
source EH |
source EH |
platform + counterpart |
jspi-single-off-worker |
pass |
pass |
pass |
platform |
platform + counterpart |
pass |
dependency |
pass |
threads |
source EH |
source EH |
platform + counterpart |
jspi-multi-on-node |
pass |
pass |
pass |
platform |
platform |
pass |
dependency |
pass |
upstream defect + counterpart unavailable |
pass |
pass |
platform |
jspi-multi-on-page |
pass |
pass |
pass |
platform |
platform + counterpart |
pass |
dependency |
pass |
upstream defect + counterpart unavailable |
pass |
pass |
platform + counterpart |
jspi-multi-on-worker |
pass |
pass |
pass |
platform |
platform + counterpart |
pass |
dependency |
pass |
upstream defect + counterpart unavailable |
pass |
pass |
platform + counterpart |
jspi-multi-off-node |
pass |
pass |
pass |
platform |
platform |
pass |
dependency |
pass |
upstream defect + counterpart unavailable |
source EH |
source EH |
platform |
jspi-multi-off-page |
pass |
pass |
pass |
platform |
platform + counterpart |
pass |
dependency |
pass |
upstream defect + counterpart unavailable |
source EH |
source EH |
platform + counterpart |
jspi-multi-off-worker |
pass |
pass |
pass |
platform |
platform + counterpart |
pass |
dependency |
pass |
upstream defect + counterpart unavailable |
source EH |
source EH |
platform + counterpart |
asyncify
| Profile | channel | delay | delay_op | echo_server | http | outcome | python | signals | spsc | thread | thread_pool | ticker |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
asyncify-single-on-node |
pass |
pass |
pass |
platform |
platform |
pass |
dependency |
pass |
threads |
threads |
threads |
platform |
asyncify-single-on-page |
pass |
pass |
pass |
platform |
platform + counterpart |
pass |
dependency |
pass |
threads |
threads |
threads |
platform + counterpart |
asyncify-single-on-worker |
pass |
pass |
pass |
platform |
platform + counterpart |
pass |
dependency |
pass |
threads |
threads |
threads |
platform + counterpart |
asyncify-single-off-node |
pass |
pass |
pass |
platform |
platform |
pass |
dependency |
pass |
threads |
source EH |
source EH |
platform |
asyncify-single-off-page |
pass |
pass |
pass |
platform |
platform + counterpart |
pass |
dependency |
pass |
threads |
source EH |
source EH |
platform + counterpart |
asyncify-single-off-worker |
pass |
pass |
pass |
platform |
platform + counterpart |
pass |
dependency |
pass |
threads |
source EH |
source EH |
platform + counterpart |
asyncify-multi-on-node |
pass |
pass |
pass |
platform |
platform |
pass |
dependency |
pass |
upstream defect + counterpart unavailable |
pass |
pass |
platform |
asyncify-multi-on-page |
pass |
pass |
pass |
platform |
platform + counterpart |
pass |
dependency |
pass |
upstream defect + counterpart unavailable |
pass |
pass |
platform + counterpart |
asyncify-multi-on-worker |
pass |
pass |
pass |
platform |
platform + counterpart |
pass |
dependency |
pass |
upstream defect + counterpart unavailable |
pass |
pass |
platform + counterpart |
asyncify-multi-off-node |
pass |
pass |
pass |
platform |
platform |
pass |
dependency |
pass |
upstream defect + counterpart unavailable |
source EH |
source EH |
platform |
asyncify-multi-off-page |
pass |
pass |
pass |
platform |
platform + counterpart |
pass |
dependency |
pass |
upstream defect + counterpart unavailable |
source EH |
source EH |
platform + counterpart |
asyncify-multi-off-worker |
pass |
pass |
pass |
platform |
platform + counterpart |
pass |
dependency |
pass |
upstream defect + counterpart unavailable |
source EH |
source EH |
platform + counterpart |
Evidence boundaries
-
pass: the original or namespace-only migrated program ran and completed. -
fixture: a controlled endpoint adaptation ran against real TCP/TLS peers. -
SDK,platform,threads,dependency: the exact prerequisite is absent in that profile. -
source EH: explicit exception syntax in the example is rejected with exceptions disabled. -
upstream defect: the original failure has its own evidence; counterpart status remains separate.counterpart unavailablepreserves a limitation inherited by the adaptation.
Raw JSON cells retain source hashes, implementation snapshots, commands and evidence filenames. The native Python extension is an optional CPython 3.14/nanobind experiment, not a Wasm capability. The five upstream benchmark sources remain inventoried and compile-probed; current performance uses separately identified finite matched workloads.
Pinned upstream benchmark sources
All five benchmark sources are compiled with the measured profiles and strict diagnostics. These are compile probes, not performance results. The original sources remain unchanged; Wasm variants change namespaces/includes and make the original concurrency-hint macro include explicit.
| Source | Observed source constraints |
|---|---|
channel |
The original printf format does not match chrono::duration::rep under strict format warnings. |
immediate |
The original printf format does not match chrono::duration::rep under strict format warnings. |
monotonic |
The original names asio::cobalt_compose, absent in the pinned Asio interface. The original printf format does not match chrono::duration::rep under strict format warnings. |
parallel |
The original contains throw syntax in a no-EH profile. The original printf format does not match chrono::duration::rep under strict format warnings. |
post |
The original printf format does not match chrono::duration::rep under strict format warnings. |
Release measurements of the shipped implementation
These local measurements compare Boost-backed native interfaces with the shipped Wasm implementation on one machine. They are not a cross-machine performance guarantee. See the raw environment, commands, hashes and samples accompanying the tables.
Sizes cover -O2, -O3, -Os and -Oz, each with full LTO off/on. Runtime comparisons use -O3 without LTO across every feasible profile. All builds use Release and NDEBUG. EH-on uses -fwasm-exceptions for JSPI/WASI and -fexceptions for Asyncify in this toolchain. Those comparisons include the selected exception code generation, not suspension alone. The workload executable contains seven finite matched workloads, not an unchanged upstream application. The baseline contains only an empty runner. Browser payload is JavaScript plus Wasm; native is a Mach-O executable. Runtime installations and native system shared libraries are outside these payload sizes.
All 136 optimization/profile builds also execute the empty runner and seven short checked workloads for both libraries: 2,176 payload qualification runs in native, Node or Wasmtime. They validate the size artifacts; their timings are excluded from the performance tables. Real-browser measurements and correctness observations below remain separate.
Single/multi are B2 threading properties. Native single does not disable Boost threads; Wasm multi selects the thread-capable SDK profile. Each workload runs one context on one execution thread. This is not a multicore throughput comparison. P3 threads are cooperative.
Build time is one forced rebuild of six programs per setting with three compiler jobs and a warmed SDK cache. No other build/test from this session ran concurrently; other user and OS activity is not controlled. A single build is an observation, not a timing distribution.
Interpreting the native comparison
Every workload constructs a channel and timer before its loop, including the post-only loop. The initialized reactor remains part of that context. On this macOS/Boost combination, the native scheduler polls kqueue between posted completions. Therefore the ratios compare these initialized-reactor applications, not the intrinsic cost of an isolated coroutine or post. They must not be generalized to Linux, Windows, or a context that never initializes I/O. The native Cobalt runner also registers POSIX signals, unlike its WASI runner.
A separate diagnostic uses unmodified Boost and changes only whether it constructs an idle timer before 10,000 checked posts. Five pairs observed 59.5-98.9 ns/post without that timer and 10.50-12.15 us/post with it. This supports the initialized-reactor explanation for the large native post/channel ratios. It is a boundary check with fixed order and no discarded warmup, not a replacement statistical benchmark. Its source, command and output are retained as native-reactor-control files beside the results; the immutable source snapshot is .cpp.txt. Copy it to the recorded .cpp path before replaying that historical command. The main samples are unchanged.
Versioned toolchain diagnostics
8 WASIp3 LTO build configurations report the SDK 34 linker diagnostic function signature mismatch: cabi_realloc. A minimal malloc/printf program using only the SDK reproduces it and runs successfully in Wasmtime. The warning is preserved in build receipts; all measured payloads also pass their separate short execution checks. This does not establish that every canonical-ABI path is safe with this SDK/LTO combination. The primary runtime comparison uses no LTO. Native no-EH Cobalt retains its upstream promise diagnostic and issue-287 warning; neither warning is hidden or treated as proof that the affected move path is safe.
Baseline payload (KiB)
| Profile | Library | O2 | O2 + LTO | O3 | O3 + LTO | Os | Os + LTO | Oz | Oz + LTO |
|---|---|---|---|---|---|---|---|---|---|
native-single-on |
asio |
116.53 |
107.97 |
116.53 |
108.08 |
118.69 |
91.39 |
115.06 |
99.33 |
native-single-on |
cobalt |
237.66 |
183.53 |
236.06 |
183.64 |
245.03 |
168.34 |
258.03 |
173.44 |
native-single-off |
asio |
88.03 |
81.33 |
87.48 |
81.05 |
90.83 |
81.83 |
102.73 |
87.20 |
native-single-off |
cobalt |
196.59 |
134.05 |
194.91 |
133.77 |
186.66 |
137.05 |
212.06 |
132.31 |
native-multi-on |
asio |
116.53 |
107.97 |
116.53 |
108.08 |
118.69 |
91.39 |
115.06 |
99.33 |
native-multi-on |
cobalt |
237.66 |
183.53 |
236.06 |
183.64 |
245.03 |
168.34 |
258.03 |
173.44 |
native-multi-off |
asio |
88.03 |
81.33 |
87.48 |
81.05 |
90.83 |
81.83 |
102.73 |
87.20 |
native-multi-off |
cobalt |
196.59 |
134.05 |
194.91 |
133.77 |
186.66 |
137.05 |
212.06 |
132.31 |
wasip2-single-on |
asio |
1048.99 |
743.46 |
1047.54 |
742.39 |
1048.14 |
741.21 |
1059.20 |
745.41 |
wasip2-single-on |
cobalt |
1504.96 |
1013.75 |
1502.76 |
1012.71 |
1526.98 |
1016.81 |
1585.76 |
1053.16 |
wasip2-single-off |
asio |
920.70 |
671.77 |
918.03 |
669.80 |
918.92 |
667.83 |
932.36 |
673.53 |
wasip2-single-off |
cobalt |
1384.36 |
950.20 |
1381.63 |
948.20 |
1406.83 |
951.55 |
1443.40 |
963.58 |
wasip3-single-on |
asio |
1154.12 |
840.33 |
1152.83 |
839.40 |
1152.57 |
837.76 |
1164.26 |
842.55 |
wasip3-single-on |
cobalt |
1602.26 |
1115.95 |
1600.25 |
1115.05 |
1623.50 |
1118.60 |
1682.94 |
1155.53 |
wasip3-single-off |
asio |
1040.12 |
781.24 |
1037.44 |
779.17 |
1037.74 |
776.50 |
1051.93 |
783.01 |
wasip3-single-off |
cobalt |
1480.71 |
1057.57 |
1477.89 |
1055.38 |
1502.52 |
1058.02 |
1539.83 |
1070.89 |
wasip3-multi-off |
asio |
1145.74 |
799.82 |
1143.13 |
798.26 |
1145.94 |
797.22 |
1162.28 |
802.89 |
wasip3-multi-off |
cobalt |
1565.32 |
1071.89 |
1562.55 |
1070.22 |
1574.31 |
1074.25 |
1627.81 |
1086.34 |
jspi-single-on |
asio |
153.83 |
159.28 |
153.18 |
158.94 |
146.32 |
146.25 |
142.67 |
138.40 |
jspi-single-on |
cobalt |
173.70 |
179.54 |
174.24 |
180.73 |
163.66 |
164.32 |
157.50 |
153.22 |
jspi-single-off |
asio |
137.84 |
138.91 |
136.13 |
137.28 |
130.27 |
129.83 |
128.58 |
126.47 |
jspi-single-off |
cobalt |
157.97 |
158.20 |
157.04 |
157.83 |
147.94 |
147.62 |
144.19 |
142.14 |
jspi-multi-on |
asio |
186.09 |
191.19 |
185.16 |
190.53 |
180.52 |
178.97 |
173.97 |
169.34 |
jspi-multi-on |
cobalt |
206.13 |
212.55 |
205.76 |
212.17 |
197.98 |
196.51 |
189.36 |
183.69 |
jspi-multi-off |
asio |
170.54 |
171.57 |
170.10 |
170.93 |
164.25 |
163.11 |
160.79 |
158.14 |
jspi-multi-off |
cobalt |
188.62 |
189.83 |
188.75 |
189.70 |
181.00 |
179.50 |
175.32 |
171.82 |
asyncify-single-on |
asio |
234.00 |
224.41 |
229.21 |
219.74 |
222.32 |
206.26 |
223.51 |
202.75 |
asyncify-single-on |
cobalt |
279.91 |
262.90 |
275.27 |
258.44 |
264.76 |
241.38 |
262.36 |
234.13 |
asyncify-single-off |
asio |
175.54 |
170.85 |
170.68 |
166.58 |
167.04 |
161.78 |
168.80 |
161.95 |
asyncify-single-off |
cobalt |
209.40 |
202.00 |
204.85 |
198.21 |
198.88 |
191.82 |
199.93 |
190.98 |
asyncify-multi-on |
asio |
300.67 |
289.45 |
290.86 |
281.14 |
288.68 |
271.62 |
284.08 |
260.63 |
asyncify-multi-on |
cobalt |
353.29 |
335.18 |
342.67 |
326.30 |
337.88 |
314.45 |
329.80 |
299.03 |
asyncify-multi-off |
asio |
231.42 |
231.33 |
225.68 |
225.64 |
220.67 |
217.28 |
220.75 |
216.46 |
asyncify-multi-off |
cobalt |
267.31 |
266.91 |
261.73 |
261.27 |
256.08 |
252.34 |
254.83 |
248.83 |
Workload payload (KiB)
| Profile | Library | O2 | O2 + LTO | O3 | O3 + LTO | Os | Os + LTO | Oz | Oz + LTO |
|---|---|---|---|---|---|---|---|---|---|
native-single-on |
asio |
430.36 |
327.14 |
431.89 |
325.42 |
439.11 |
296.55 |
520.80 |
298.20 |
native-single-on |
cobalt |
517.12 |
407.08 |
532.09 |
407.12 |
501.44 |
365.59 |
573.75 |
358.09 |
native-single-off |
asio |
362.92 |
263.55 |
363.75 |
265.50 |
371.58 |
243.69 |
464.17 |
251.59 |
native-single-off |
cobalt |
421.77 |
309.08 |
419.16 |
323.41 |
408.14 |
287.92 |
490.67 |
303.34 |
native-multi-on |
asio |
430.36 |
327.14 |
431.89 |
325.42 |
439.11 |
296.55 |
520.80 |
298.20 |
native-multi-on |
cobalt |
517.12 |
407.08 |
532.09 |
407.12 |
501.44 |
365.59 |
573.75 |
358.09 |
native-multi-off |
asio |
362.92 |
263.55 |
363.75 |
265.50 |
371.58 |
243.69 |
464.17 |
251.59 |
native-multi-off |
cobalt |
421.77 |
309.08 |
419.16 |
323.41 |
408.14 |
287.92 |
490.67 |
303.34 |
wasip2-single-on |
asio |
1829.24 |
1218.96 |
1841.07 |
1224.74 |
1936.16 |
1246.52 |
2186.69 |
1280.91 |
wasip2-single-on |
cobalt |
1851.02 |
1255.85 |
1850.45 |
1256.73 |
1877.98 |
1251.53 |
2025.85 |
1281.17 |
wasip2-single-off |
asio |
1664.43 |
1151.15 |
1663.11 |
1154.85 |
1761.48 |
1163.21 |
2003.72 |
1181.12 |
wasip2-single-off |
cobalt |
1681.74 |
1181.23 |
1675.41 |
1179.46 |
1714.51 |
1173.84 |
1848.80 |
1180.41 |
wasip3-single-on |
asio |
1929.09 |
1320.45 |
1940.83 |
1326.49 |
2026.98 |
1341.07 |
2285.22 |
1382.52 |
wasip3-single-on |
cobalt |
1951.96 |
1358.60 |
1951.46 |
1359.74 |
1969.76 |
1346.83 |
2125.34 |
1383.80 |
wasip3-single-off |
asio |
1761.63 |
1260.49 |
1760.12 |
1264.20 |
1850.46 |
1264.99 |
2100.84 |
1290.19 |
wasip3-single-off |
cobalt |
1779.17 |
1291.43 |
1772.58 |
1290.44 |
1803.66 |
1276.24 |
1946.11 |
1290.18 |
wasip3-multi-off |
asio |
1848.70 |
1284.83 |
1847.49 |
1288.54 |
1925.19 |
1290.25 |
2190.89 |
1314.30 |
wasip3-multi-off |
cobalt |
1866.37 |
1308.85 |
1859.36 |
1307.16 |
1878.36 |
1295.06 |
2036.01 |
1307.41 |
jspi-single-on |
asio |
249.83 |
262.50 |
251.81 |
261.40 |
227.08 |
223.55 |
210.50 |
200.86 |
jspi-single-on |
cobalt |
307.08 |
323.72 |
310.46 |
326.24 |
265.20 |
263.28 |
241.38 |
231.31 |
jspi-single-off |
asio |
229.74 |
228.92 |
233.81 |
233.38 |
203.59 |
198.86 |
193.67 |
187.21 |
jspi-single-off |
cobalt |
277.83 |
274.97 |
277.89 |
275.19 |
236.27 |
234.23 |
219.62 |
212.46 |
jspi-multi-on |
asio |
282.71 |
292.64 |
283.25 |
290.28 |
261.30 |
255.29 |
242.69 |
231.58 |
jspi-multi-on |
cobalt |
340.49 |
352.54 |
342.48 |
351.21 |
300.05 |
295.55 |
274.44 |
262.52 |
jspi-multi-off |
asio |
261.36 |
262.43 |
265.31 |
266.57 |
236.29 |
230.31 |
224.77 |
217.08 |
jspi-multi-off |
cobalt |
309.11 |
309.07 |
308.35 |
308.22 |
269.99 |
266.99 |
251.31 |
242.57 |
asyncify-single-on |
asio |
443.42 |
421.58 |
429.23 |
406.37 |
413.28 |
363.44 |
393.89 |
335.03 |
asyncify-single-on |
cobalt |
574.26 |
531.36 |
556.13 |
518.95 |
497.62 |
440.36 |
470.64 |
403.60 |
asyncify-single-off |
asio |
334.23 |
319.27 |
339.30 |
325.17 |
300.79 |
284.02 |
296.64 |
276.64 |
asyncify-single-off |
cobalt |
411.16 |
391.64 |
404.72 |
385.66 |
358.95 |
343.04 |
350.96 |
327.90 |
asyncify-multi-on |
asio |
520.30 |
495.71 |
501.08 |
477.87 |
488.43 |
436.68 |
464.02 |
402.14 |
asyncify-multi-on |
cobalt |
657.06 |
611.93 |
633.34 |
594.83 |
579.05 |
522.40 |
545.80 |
478.29 |
asyncify-multi-off |
asio |
397.75 |
391.73 |
401.07 |
395.28 |
359.61 |
346.31 |
353.46 |
337.05 |
asyncify-multi-off |
cobalt |
477.63 |
469.22 |
468.34 |
459.56 |
422.30 |
411.60 |
411.00 |
392.31 |
Runtime and memory scope
Each operation is one completed iteration: post resumes once; immediate sends then receives in a buffered Asio channel; channel transfers one value through an unbuffered channel; parallel joins one Asio-channel send/receive pair; allocation performs five posts and five arena allocations; timer awaits a zero-expiry timer; http parses one complete in-memory HTTP response. Asio uses parallel_group and Cobalt uses join for paired operations. HTTP does not measure network or TLS latency. Every iteration contributes to a checked checksum.
Thirty fresh executions per workload/profile use frozen common operation counts and an in-process warmup. The report records median, p95, min/max and a bootstrap 95% interval for the median. Ten baseline starts measure fresh process/page startup. The browser process is retained; each page/worker is new, with no-store HTTP. OS and engine caches are not cleared, so these are not cold-machine or empty-code-cache measurements. The explicit buffered loader measures fetch, compile and instantiate separately; it does not represent the default streaming loader.
Three instrumented executions describe C++ allocation counts/requested bytes and Cobalt frame PMR requests. Those nested counters must not be added. Direct C malloc and JavaScript allocations are outside their scope. Wasm linear memory is capacity, not live heap. Process RSS includes Node/Wasmtime; browser-module RSS/CPU are unavailable. Instrumentation timing is retained only as a diagnostic, not a comparative performance claim.
Asio median time per operation (ns)
| Profile | Host | Post | Immediate | Channel | Parallel | Allocation | Timer | HTTP parse |
|---|---|---|---|---|---|---|---|---|
asyncify-multi-off |
node |
827.2 |
2041.2 |
1388.9 |
1390.6 |
3853.5 |
3784.6 |
428.1 |
asyncify-multi-off |
page |
718.0 |
1772.5 |
1376.9 |
1357.5 |
3681.5 |
3408.2 |
382.1 |
asyncify-multi-off |
worker |
717.8 |
1762.8 |
1413.6 |
1418.4 |
3886.5 |
3393.6 |
397.0 |
asyncify-multi-on |
node |
3793.1 |
8164.8 |
4531.1 |
4519.8 |
15444.5 |
5380.2 |
548.0 |
asyncify-multi-on |
page |
2793.2 |
6528.2 |
4030.8 |
4003.9 |
12636.5 |
5339.2 |
446.5 |
asyncify-multi-on |
worker |
2746.5 |
6421.2 |
4021.0 |
3999.1 |
12490.5 |
4992.6 |
450.4 |
asyncify-single-off |
node |
950.3 |
2306.2 |
1370.3 |
1362.4 |
4345.3 |
4676.8 |
364.4 |
asyncify-single-off |
page |
649.4 |
1660.2 |
1093.8 |
1093.8 |
3232.4 |
3098.1 |
339.4 |
asyncify-single-off |
worker |
642.1 |
1611.3 |
1071.8 |
1064.5 |
3183.6 |
2998.0 |
341.8 |
asyncify-single-on |
node |
3765.8 |
7910.0 |
4343.9 |
4382.5 |
15651.9 |
4649.1 |
502.7 |
asyncify-single-on |
page |
2822.3 |
6323.2 |
3681.6 |
3664.6 |
12460.9 |
4392.1 |
398.9 |
asyncify-single-on |
worker |
2778.3 |
6206.1 |
3664.6 |
3625.5 |
12187.5 |
4348.1 |
400.7 |
jspi-multi-off |
node |
856.1 |
2104.0 |
1142.2 |
1139.5 |
3863.5 |
2102.8 |
329.9 |
jspi-multi-off |
page |
529.8 |
1396.5 |
1044.8 |
1042.5 |
2695.0 |
2214.2 |
307.0 |
jspi-multi-off |
worker |
517.5 |
1381.8 |
1035.2 |
1037.6 |
2627.5 |
2187.5 |
307.9 |
jspi-multi-on |
node |
904.2 |
2258.8 |
1199.4 |
1204.1 |
4018.5 |
2122.9 |
326.5 |
jspi-multi-on |
page |
581.0 |
1514.0 |
1130.4 |
1135.1 |
2871.0 |
2258.4 |
309.5 |
jspi-multi-on |
worker |
566.5 |
1513.5 |
1130.4 |
1128.0 |
2812.0 |
2221.5 |
311.6 |
jspi-single-off |
node |
784.0 |
1623.0 |
1190.9 |
1187.4 |
3661.2 |
1873.8 |
296.7 |
jspi-single-off |
page |
473.6 |
1289.1 |
869.1 |
876.5 |
2392.6 |
1826.2 |
287.5 |
jspi-single-off |
worker |
468.8 |
1289.1 |
859.4 |
854.5 |
2343.8 |
1796.9 |
289.6 |
jspi-single-on |
node |
853.9 |
2051.8 |
1397.8 |
1393.5 |
3899.8 |
1923.9 |
299.0 |
jspi-single-on |
page |
522.5 |
1430.7 |
961.9 |
959.5 |
2597.7 |
1855.5 |
288.7 |
jspi-single-on |
worker |
517.6 |
1401.4 |
954.6 |
942.4 |
2597.7 |
1955.6 |
289.9 |
native-multi-off |
native |
11873.1 |
24188.8 |
11739.4 |
11883.9 |
59530.9 |
642.4 |
121.9 |
native-multi-on |
native |
11587.2 |
23116.1 |
11974.8 |
12015.5 |
58142.3 |
652.6 |
119.3 |
native-single-off |
native |
12030.5 |
23807.9 |
12106.9 |
11880.9 |
59910.4 |
643.6 |
118.3 |
native-single-on |
native |
11360.7 |
24102.0 |
12065.1 |
12027.5 |
56108.8 |
652.2 |
120.5 |
wasip2-single-off |
wasmtime |
167.8 |
486.5 |
284.4 |
280.4 |
855.1 |
851.6 |
236.2 |
wasip2-single-on |
wasmtime |
268.7 |
768.6 |
499.7 |
493.3 |
1369.9 |
920.1 |
244.7 |
wasip3-multi-off |
wasmtime |
212.7 |
618.6 |
365.3 |
365.7 |
1095.9 |
837.5 |
240.7 |
wasip3-single-off |
wasmtime |
180.3 |
512.3 |
294.5 |
295.2 |
936.4 |
766.8 |
238.2 |
wasip3-single-on |
wasmtime |
268.7 |
778.0 |
505.8 |
507.9 |
1385.7 |
824.7 |
247.7 |
Cobalt median time per operation (ns)
| Profile | Host | Post | Immediate | Channel | Parallel | Allocation | Timer | HTTP parse |
|---|---|---|---|---|---|---|---|---|
asyncify-multi-off |
node |
2534.1 |
1090.5 |
2693.8 |
3745.5 |
11211.0 |
5441.1 |
455.3 |
asyncify-multi-off |
page |
2500.0 |
1142.5 |
2509.8 |
3903.8 |
12539.5 |
3525.4 |
395.8 |
asyncify-multi-off |
worker |
2636.8 |
1182.0 |
2585.5 |
3964.9 |
11845.5 |
3415.6 |
410.8 |
asyncify-multi-on |
node |
5723.2 |
5412.8 |
5024.1 |
7440.6 |
26678.0 |
5354.9 |
557.2 |
asyncify-multi-on |
page |
5747.0 |
5473.5 |
4919.5 |
7534.2 |
25820.5 |
5315.0 |
444.7 |
asyncify-multi-on |
worker |
5720.1 |
5376.0 |
4995.1 |
7492.6 |
25136.5 |
4995.2 |
441.3 |
asyncify-single-off |
node |
2846.7 |
1071.5 |
3001.0 |
3912.3 |
15319.9 |
4765.3 |
388.3 |
asyncify-single-off |
page |
2336.4 |
1132.8 |
2299.8 |
3596.2 |
10849.6 |
2973.6 |
353.4 |
asyncify-single-off |
worker |
2319.3 |
1132.8 |
2292.5 |
3625.5 |
11367.2 |
2973.6 |
355.8 |
asyncify-single-on |
node |
4987.2 |
5331.0 |
4517.9 |
6390.8 |
22082.5 |
4720.4 |
514.8 |
asyncify-single-on |
page |
4877.9 |
5405.3 |
4165.0 |
6367.2 |
23242.2 |
4528.8 |
399.8 |
asyncify-single-on |
worker |
4973.1 |
5317.4 |
4199.2 |
6616.2 |
22627.0 |
4389.6 |
405.3 |
jspi-multi-off |
node |
1433.9 |
885.0 |
1441.2 |
2300.8 |
6860.0 |
2085.1 |
361.8 |
jspi-multi-off |
page |
1350.1 |
976.5 |
1335.5 |
2253.5 |
6386.5 |
2275.5 |
311.9 |
jspi-multi-off |
worker |
1318.2 |
976.5 |
1306.1 |
2243.8 |
6739.0 |
2246.1 |
314.3 |
jspi-multi-on |
node |
1496.5 |
934.2 |
1490.0 |
2366.8 |
7130.0 |
2175.8 |
360.1 |
jspi-multi-on |
page |
1411.1 |
981.5 |
1398.9 |
2304.8 |
6709.5 |
2309.5 |
316.2 |
jspi-multi-on |
worker |
1382.0 |
986.2 |
1372.0 |
2304.6 |
7168.0 |
2299.8 |
320.8 |
jspi-single-off |
node |
1332.7 |
875.7 |
1293.5 |
2245.7 |
6102.5 |
1859.8 |
318.2 |
jspi-single-off |
page |
1206.1 |
966.8 |
1169.4 |
2070.3 |
5761.7 |
1860.4 |
291.7 |
jspi-single-off |
worker |
1189.0 |
981.4 |
1142.6 |
2033.7 |
5605.5 |
1972.7 |
295.4 |
jspi-single-on |
node |
1381.7 |
931.6 |
1349.1 |
2315.2 |
6410.2 |
1960.4 |
322.7 |
jspi-single-on |
page |
1264.6 |
1001.0 |
1225.6 |
2121.6 |
6035.2 |
1928.7 |
296.0 |
jspi-single-on |
worker |
1247.6 |
1005.9 |
1210.9 |
2148.4 |
5957.0 |
2031.2 |
297.5 |
native-multi-off |
native |
11994.1 |
129.4 |
12053.3 |
12130.8 |
60000.9 |
642.8 |
119.9 |
native-multi-on |
native |
12159.1 |
126.1 |
11954.7 |
12131.5 |
60672.0 |
648.7 |
119.1 |
native-single-off |
native |
12097.6 |
129.3 |
11968.5 |
12111.7 |
60581.2 |
641.8 |
121.5 |
native-single-on |
native |
12124.8 |
126.5 |
11969.7 |
12074.7 |
60563.2 |
656.8 |
119.3 |
wasip2-single-off |
wasmtime |
184.6 |
319.2 |
166.2 |
507.2 |
937.4 |
859.3 |
240.0 |
wasip2-single-on |
wasmtime |
301.8 |
467.3 |
266.2 |
755.3 |
1520.3 |
904.4 |
245.5 |
wasip3-multi-off |
wasmtime |
218.5 |
343.6 |
194.8 |
578.2 |
1116.1 |
836.5 |
239.3 |
wasip3-single-off |
wasmtime |
191.7 |
331.0 |
179.5 |
534.3 |
971.1 |
763.0 |
237.2 |
wasip3-single-on |
wasmtime |
299.2 |
474.4 |
270.9 |
769.4 |
1534.2 |
805.8 |
252.3 |
Baseline startup and instrumented allocation workload
Startup is the median of ten fresh starts. Process wall time includes harness setup, runtime launch and waiting; browser wall time begins inside a fresh page/worker, after its host page navigation, and includes buffered module loading. They are different observation boundaries. n/a means unavailable, not zero. Memory columns below use three instrumented allocation-workload runs. Complete workload-specific memory distributions and separate JS/Wasm/native payload bytes are in the CSV files.
| Profile | Host | Library | Start (ms) | C++ allocations | C++ peak (bytes) | Frame peak (bytes) | Linear capacity (KiB) | Process RSS (MiB) |
|---|---|---|---|---|---|---|---|---|
asyncify-multi-off |
node |
asio |
74.66 |
0.00 |
1077.00 |
n/a |
16384.00 |
74.00 |
asyncify-multi-off |
node |
cobalt |
75.66 |
0.00 |
272051.00 |
11664.00 |
16384.00 |
76.64 |
asyncify-multi-off |
page |
asio |
6.89 |
0.00 |
1077.00 |
n/a |
16384.00 |
n/a |
asyncify-multi-off |
page |
cobalt |
8.14 |
0.00 |
272051.00 |
11664.00 |
16384.00 |
n/a |
asyncify-multi-off |
worker |
asio |
6.07 |
0.00 |
1077.00 |
n/a |
16384.00 |
n/a |
asyncify-multi-off |
worker |
cobalt |
7.34 |
0.00 |
272051.00 |
11664.00 |
16384.00 |
n/a |
asyncify-multi-on |
node |
asio |
74.53 |
0.00 |
1077.00 |
n/a |
16384.00 |
67.75 |
asyncify-multi-on |
node |
cobalt |
76.43 |
0.00 |
272051.00 |
12048.00 |
16384.00 |
77.19 |
asyncify-multi-on |
page |
asio |
7.57 |
0.00 |
1077.00 |
n/a |
16384.00 |
n/a |
asyncify-multi-on |
page |
cobalt |
9.21 |
0.00 |
272051.00 |
12048.00 |
16384.00 |
n/a |
asyncify-multi-on |
worker |
asio |
6.65 |
0.00 |
1077.00 |
n/a |
16384.00 |
n/a |
asyncify-multi-on |
worker |
cobalt |
8.27 |
0.00 |
272051.00 |
12048.00 |
16384.00 |
n/a |
asyncify-single-off |
node |
asio |
73.15 |
0.00 |
937.00 |
n/a |
16512.00 |
71.45 |
asyncify-single-off |
node |
cobalt |
73.75 |
0.00 |
271911.00 |
11664.00 |
16512.00 |
80.03 |
asyncify-single-off |
page |
asio |
6.24 |
0.00 |
937.00 |
n/a |
16512.00 |
n/a |
asyncify-single-off |
page |
cobalt |
7.29 |
0.00 |
271911.00 |
11664.00 |
16512.00 |
n/a |
asyncify-single-off |
worker |
asio |
5.38 |
0.00 |
937.00 |
n/a |
16512.00 |
n/a |
asyncify-single-off |
worker |
cobalt |
6.51 |
0.00 |
271911.00 |
11664.00 |
16512.00 |
n/a |
asyncify-single-on |
node |
asio |
74.28 |
0.00 |
937.00 |
n/a |
16512.00 |
66.55 |
asyncify-single-on |
node |
cobalt |
74.99 |
0.00 |
271911.00 |
12048.00 |
16512.00 |
73.48 |
asyncify-single-on |
page |
asio |
6.84 |
0.00 |
937.00 |
n/a |
16512.00 |
n/a |
asyncify-single-on |
page |
cobalt |
8.06 |
0.00 |
271911.00 |
12048.00 |
16512.00 |
n/a |
asyncify-single-on |
worker |
asio |
5.88 |
0.00 |
937.00 |
n/a |
16512.00 |
n/a |
asyncify-single-on |
worker |
cobalt |
7.20 |
0.00 |
271911.00 |
12048.00 |
16512.00 |
n/a |
jspi-multi-off |
node |
asio |
74.61 |
0.00 |
1077.00 |
n/a |
16384.00 |
61.09 |
jspi-multi-off |
node |
cobalt |
75.46 |
0.00 |
272051.00 |
11664.00 |
16384.00 |
62.67 |
jspi-multi-off |
page |
asio |
6.99 |
0.00 |
1077.00 |
n/a |
16384.00 |
n/a |
jspi-multi-off |
page |
cobalt |
7.80 |
0.00 |
272051.00 |
11664.00 |
16384.00 |
n/a |
jspi-multi-off |
worker |
asio |
6.05 |
0.00 |
1077.00 |
n/a |
16384.00 |
n/a |
jspi-multi-off |
worker |
cobalt |
6.87 |
0.00 |
272051.00 |
11664.00 |
16384.00 |
n/a |
jspi-multi-on |
node |
asio |
74.26 |
0.00 |
1077.00 |
n/a |
16384.00 |
62.92 |
jspi-multi-on |
node |
cobalt |
75.45 |
0.00 |
1058483.00 |
33936.00 |
16384.00 |
64.94 |
jspi-multi-on |
page |
asio |
7.06 |
0.00 |
1077.00 |
n/a |
16384.00 |
n/a |
jspi-multi-on |
page |
cobalt |
7.94 |
0.00 |
1058483.00 |
33936.00 |
16384.00 |
n/a |
jspi-multi-on |
worker |
asio |
6.32 |
0.00 |
1077.00 |
n/a |
16384.00 |
n/a |
jspi-multi-on |
worker |
cobalt |
7.27 |
0.00 |
1058483.00 |
33936.00 |
16384.00 |
n/a |
jspi-single-off |
node |
asio |
73.09 |
0.00 |
937.00 |
n/a |
16512.00 |
61.28 |
jspi-single-off |
node |
cobalt |
73.55 |
0.00 |
271911.00 |
11664.00 |
16512.00 |
62.31 |
jspi-single-off |
page |
asio |
6.53 |
0.00 |
937.00 |
n/a |
16512.00 |
n/a |
jspi-single-off |
page |
cobalt |
7.11 |
0.00 |
271911.00 |
11664.00 |
16512.00 |
n/a |
jspi-single-off |
worker |
asio |
5.62 |
0.00 |
937.00 |
n/a |
16512.00 |
n/a |
jspi-single-off |
worker |
cobalt |
6.34 |
0.00 |
271911.00 |
11664.00 |
16512.00 |
n/a |
jspi-single-on |
node |
asio |
73.27 |
0.00 |
937.00 |
n/a |
16512.00 |
62.92 |
jspi-single-on |
node |
cobalt |
74.49 |
0.00 |
1058343.00 |
33936.00 |
16512.00 |
63.12 |
jspi-single-on |
page |
asio |
6.62 |
0.00 |
937.00 |
n/a |
16512.00 |
n/a |
jspi-single-on |
page |
cobalt |
7.43 |
0.00 |
1058343.00 |
33936.00 |
16512.00 |
n/a |
jspi-single-on |
worker |
asio |
5.78 |
0.00 |
937.00 |
n/a |
16512.00 |
n/a |
jspi-single-on |
worker |
cobalt |
6.61 |
0.00 |
1058343.00 |
33936.00 |
16512.00 |
n/a |
native-multi-off |
native |
asio |
3.15 |
0.00 |
930.00 |
n/a |
n/a |
9.25 |
native-multi-off |
native |
cobalt |
3.05 |
0.00 |
272303.00 |
12352.00 |
n/a |
9.25 |
native-multi-on |
native |
asio |
2.99 |
0.00 |
930.00 |
n/a |
n/a |
9.25 |
native-multi-on |
native |
cobalt |
3.15 |
0.00 |
272303.00 |
13040.00 |
n/a |
9.25 |
native-single-off |
native |
asio |
2.96 |
0.00 |
930.00 |
n/a |
n/a |
9.25 |
native-single-off |
native |
cobalt |
3.08 |
0.00 |
272303.00 |
12352.00 |
n/a |
9.25 |
native-single-on |
native |
asio |
3.15 |
0.00 |
930.00 |
n/a |
n/a |
9.25 |
native-single-on |
native |
cobalt |
3.03 |
0.00 |
272303.00 |
13040.00 |
n/a |
9.25 |
wasip2-single-off |
wasmtime |
asio |
7.53 |
0.00 |
12746.00 |
n/a |
128.00 |
15.11 |
wasip2-single-off |
wasmtime |
cobalt |
8.61 |
0.00 |
283281.00 |
11680.00 |
384.00 |
15.86 |
wasip2-single-on |
wasmtime |
asio |
8.21 |
0.00 |
12746.00 |
n/a |
128.00 |
16.27 |
wasip2-single-on |
wasmtime |
cobalt |
8.61 |
0.00 |
1069713.00 |
33936.00 |
1152.00 |
17.23 |
wasip3-multi-off |
wasmtime |
asio |
8.23 |
0.00 |
12906.00 |
n/a |
128.00 |
15.70 |
wasip3-multi-off |
wasmtime |
cobalt |
9.22 |
0.00 |
283441.00 |
11680.00 |
448.00 |
16.28 |
wasip3-single-off |
wasmtime |
asio |
7.46 |
0.00 |
12766.00 |
n/a |
128.00 |
15.48 |
wasip3-single-off |
wasmtime |
cobalt |
8.78 |
0.00 |
283301.00 |
11680.00 |
384.00 |
16.08 |
wasip3-single-on |
wasmtime |
asio |
8.18 |
0.00 |
12766.00 |
n/a |
128.00 |
16.56 |
wasip3-single-on |
wasmtime |
cobalt |
9.34 |
0.00 |
1069733.00 |
33936.00 |
1216.00 |
17.50 |
Buffered browser startup phases (ms)
Each column is an independent median of ten empty-runner observations. Fetch covers the Wasm response. Medians do not necessarily add; runtime-ready also includes JavaScript loading and setup.
| Profile | Host | Library | Fetch | Compile | Instantiate | Runtime ready |
|---|---|---|---|---|---|---|
asyncify-multi-off |
page |
asio |
0.59 |
0.33 |
0.12 |
5.58 |
asyncify-multi-off |
page |
cobalt |
0.62 |
0.38 |
0.14 |
5.65 |
asyncify-multi-off |
worker |
asio |
0.49 |
0.33 |
0.12 |
4.79 |
asyncify-multi-off |
worker |
cobalt |
0.52 |
0.38 |
0.13 |
4.90 |
asyncify-multi-on |
page |
asio |
0.62 |
0.40 |
0.15 |
5.90 |
asyncify-multi-on |
page |
cobalt |
0.68 |
0.45 |
0.16 |
6.10 |
asyncify-multi-on |
worker |
asio |
0.52 |
0.39 |
0.15 |
5.03 |
asyncify-multi-on |
worker |
cobalt |
0.53 |
0.45 |
0.15 |
5.16 |
asyncify-single-off |
page |
asio |
0.62 |
0.28 |
0.07 |
4.98 |
asyncify-single-off |
page |
cobalt |
0.64 |
0.33 |
0.07 |
5.01 |
asyncify-single-off |
worker |
asio |
0.47 |
0.28 |
0.06 |
4.17 |
asyncify-single-off |
worker |
cobalt |
0.49 |
0.32 |
0.06 |
4.25 |
asyncify-single-on |
page |
asio |
0.65 |
0.36 |
0.09 |
5.31 |
asyncify-single-on |
page |
cobalt |
0.61 |
0.40 |
0.09 |
5.25 |
asyncify-single-on |
worker |
asio |
0.49 |
0.36 |
0.08 |
4.37 |
asyncify-single-on |
worker |
cobalt |
0.52 |
0.37 |
0.09 |
4.47 |
jspi-multi-off |
page |
asio |
0.60 |
0.29 |
0.57 |
5.98 |
jspi-multi-off |
page |
cobalt |
0.62 |
0.34 |
0.59 |
6.07 |
jspi-multi-off |
worker |
asio |
0.47 |
0.30 |
0.55 |
5.06 |
jspi-multi-off |
worker |
cobalt |
0.46 |
0.33 |
0.56 |
5.17 |
jspi-multi-on |
page |
asio |
0.58 |
0.33 |
0.58 |
5.97 |
jspi-multi-on |
page |
cobalt |
0.59 |
0.36 |
0.58 |
6.04 |
jspi-multi-on |
worker |
asio |
0.49 |
0.34 |
0.56 |
5.22 |
jspi-multi-on |
worker |
cobalt |
0.53 |
0.36 |
0.59 |
5.36 |
jspi-single-off |
page |
asio |
0.64 |
0.26 |
0.52 |
5.51 |
jspi-single-off |
page |
cobalt |
0.58 |
0.30 |
0.52 |
5.42 |
jspi-single-off |
worker |
asio |
0.48 |
0.27 |
0.50 |
4.60 |
jspi-single-off |
worker |
cobalt |
0.49 |
0.29 |
0.50 |
4.67 |
jspi-single-on |
page |
asio |
0.60 |
0.30 |
0.52 |
5.47 |
jspi-single-on |
page |
cobalt |
0.63 |
0.32 |
0.51 |
5.54 |
jspi-single-on |
worker |
asio |
0.48 |
0.29 |
0.51 |
4.69 |
jspi-single-on |
worker |
cobalt |
0.49 |
0.34 |
0.51 |
4.73 |
Complete evidence and plots
bench/results/2026-11-07 contains sizes.csv, metrics.csv, summary.json, environment metadata and the raw evidence archive with its SHA-256 file. The archive retains commands, build logs, source/artifact hashes, calibration, frozen counts and every independent sample. The local experiment is not remote CI evidence.
Guarantees
-
Native core coroutine and plain I/O types retain their Boost.Cobalt identity. The corrected SSL wrapper is the documented exception.
-
Consumer builds use shipped source files and explicit dependencies. They do not patch Boost or select replacement Boost include trees.
-
The browser runner uses Asio’s configured handler limit. This limit is checked between handlers, not inside a running handler.
-
A failed or cancelled handshake on the corrected SSL stream prevents subsequent application I/O from falling back to plaintext.
Reference
The generated reference documents WebCPP’s additions and configuration. The Boost.Cobalt 1.92 reference describes the inherited coroutine concepts and operations; target restrictions and the SSL exception below also apply.
Macros
| Name | Description |
|---|---|
Iterates an asynchronous range with the selected coroutine backend. |
|
1 when JavaScript Promises and events can be awaited through the Asio browser bridge. |
|
1 for an Emscripten pthread configuration; browser isolation and worker hosting are runtime requirements. |
|
1 when the backend provides forward lookup; the host can still deny network or DNS capabilities. |
|
1 when native Boost.Asio supplies local sockets; WASI and browser transports do not impersonate Unix sockets. |
|
1 when the target can link guest OpenSSL; select io_ssl or cobalt_ssl separately. |
|
1 when guest-created asynchronous pipes support transfer, cancellation and EOF. |
|
1 when Asio’s explicit WASIp2 pollable stdio adapter is available with Cobalt tokens. |
|
1 when native POSIX stdio descriptors can participate in asynchronous stream operations. |
|
1 when native reverse lookup is available; WASI address lookup is forward-only. |
|
1 when the native backend supplies serial ports; a browser or WASI host adapter is a separate facility. |
|
1 when native process-signal delivery is available; application cancellation remains available elsewhere. |
|
1 when the configured Asio backend provides native or WASI TCP/UDP; host grants remain runtime requirements. |
|
1 when the configured executor supports threads; WASIp3 cooperative threads are not CPU parallelism. |
|
Defined when WebCPP’s Cobalt adapters are configured without exceptions. |
webcpp::cobalt
Native coroutine types retain Boost identity; the SSL wrapper is corrected separately.
Namespaces
| Name |
|---|
webcpp::cobalt::io
Functions
webcpp::cobalt::io::read
Reads the complete buffer through the corrected native SSL wrapper.
Synopsis
Declared in <webcpp/cobalt/detail/native_ssl_transfer.hpp>
/* implementation-defined */
read(
ssl::stream& stream,
mutable_buffer_sequence const& buffers);
Description
The operation starts when awaited. The stream and buffer storage must outlive completion. Cancellation returns operation_aborted and the transferred count. Keep the concrete SSL stream type when selecting this overload; erasing it to Boost’s generic stream selects the upstream composition and its limitations.
Return Value
A lazy operation with completion signature void(error_code, size_t).
Parameters
| Name | Description |
|---|---|
stream |
SSL stream, or a STARTTLS stream before its first handshake. |
buffers |
Writable storage that remains valid until completion. |
webcpp::cobalt::io::write
Writes the complete buffer through the corrected native SSL wrapper.
Synopsis
Declared in <webcpp/cobalt/detail/native_ssl_transfer.hpp>
/* implementation-defined */
write(
ssl::stream& stream,
const_buffer_sequence const& buffers);
Description
The operation starts when awaited. A failed or cancelled handshake prevents application writes. The buffer storage must outlive completion. Cancellation preserves the transferred count and does not close the underlying stream.
Return Value
A lazy operation with completion signature void(error_code, size_t).
Parameters
| Name | Description |
|---|---|
stream |
SSL stream, or a STARTTLS stream before its first handshake. |
buffers |
Readable storage that remains valid until completion. |
WEBCPP_COBALT_FOR
Iterates an asynchronous range with the selected coroutine backend.
Description
Include webcpp/cobalt/async_for.hpp before using this macro. The arguments follow Boost.Cobalt’s BOOST_COBALT_FOR syntax on every target.
Parameters
| Name | Description |
|---|---|
Value |
The declaration receiving each yielded value. |
Expression |
The asynchronous range expression. |
WEBCPP_COBALT_HAS_BROWSER_BRIDGE
1 when JavaScript Promises and events can be awaited through the Asio browser bridge.
WEBCPP_COBALT_HAS_BROWSER_WORKERS
1 for an Emscripten pthread configuration; browser isolation and worker hosting are runtime requirements.
WEBCPP_COBALT_HAS_HOST_DNS
1 when the backend provides forward lookup; the host can still deny network or DNS capabilities.
WEBCPP_COBALT_HAS_LOCAL_SOCKETS
1 when native Boost.Asio supplies local sockets; WASI and browser transports do not impersonate Unix sockets.
WEBCPP_COBALT_HAS_OPENSSL_BACKEND
1 when the target can link guest OpenSSL; select io_ssl or cobalt_ssl separately.
WEBCPP_COBALT_HAS_PIPE_CREATION
1 when guest-created asynchronous pipes support transfer, cancellation and EOF.
WEBCPP_COBALT_HAS_POLLABLE_STDIO
1 when Asio’s explicit WASIp2 pollable stdio adapter is available with Cobalt tokens.
WEBCPP_COBALT_HAS_POSIX_STDIO
1 when native POSIX stdio descriptors can participate in asynchronous stream operations.
WEBCPP_COBALT_HAS_REVERSE_DNS
1 when native reverse lookup is available; WASI address lookup is forward-only.
WEBCPP_COBALT_HAS_SERIAL_PORT
1 when the native backend supplies serial ports; a browser or WASI host adapter is a separate facility.
WEBCPP_COBALT_HAS_SIGNALS
1 when native process-signal delivery is available; application cancellation remains available elsewhere.
WEBCPP_COBALT_HAS_SOCKET_BACKEND
1 when the configured Asio backend provides native or WASI TCP/UDP; host grants remain runtime requirements.
WEBCPP_COBALT_HAS_THREADS
1 when the configured executor supports threads; WASIp3 cooperative threads are not CPU parallelism.
WEBCPP_COBALT_NO_EXCEPTIONS
Defined when WebCPP’s Cobalt adapters are configured without exceptions.
Description
The compiler and B2 exception-handling setting still determine the exception ABI of Boost.Cobalt and its compiled sources. This macro does not repair the pending-task move limitation in Boost.Cobalt issue 287 or change Boost APIs. Define WEBCPP_NO_EXCEPTIONS to select the policy for the whole collection, or this macro to select it for this integration alone.
See Also
"Exceptions and errors", in the guide.
Appendix A: Differences
On native targets, task, promise, generator, completion tokens and plain
I/O types are Boost.Cobalt types. The upstream native implementation and runner
sources remain byte-identical. One WebCPP translation unit checks the Boost
version while compiling the core; it adds no runtime behavior. This preserves native interoperability; it
also preserves upstream limitations outside the separately corrected SSL wrapper.
With Boost 1.92, the native limitation probes cover cancellation not reaching
composed accept or composed waits. Further native checks reproduced with
cleanup instantiation failures and a write_at constraint expecting write_op
instead of write_at_op. Apple Clang 21 also diagnoses the original main and
thread promises’ missing unhandled_exception members when compiling without exceptions;
with warnings treated as errors, that consumer fails to compile. MSVC rejects
the no-exceptions co_main with error C2039 for the same missing member;
the limitation probes preserve a control with exceptions enabled and verify
the promise shape. These are not new native guarantees supplied by WebCPP.
The Wasm implementation carries its
documented corrections, including the positioned-write constraint.
GCC 15.2 has a compiler regression for await_suspend with additional default
arguments, including Cobalt’s race. It can fail with an internal compiler error
in make_decl_rtl even in a program using only Boost.Cobalt. Use GCC 15.3 or a
compiler containing the fix for
GCC PR121643.
The CI provisions GCC 15.3 and verifies this coroutine pattern before the suite;
it does not patch the native Cobalt headers or skip the affected applications.
GCC 14 can emit -Wmismatched-new-delete for the upstream coroutine frame
allocator. The maintainer identifies this as a false positive in
Boost.Cobalt issue 7. Strict builds
that promote this warning to an error fail even though allocation and release
use the same stored memory resource. This compiler diagnostic is distinct from
the no-exceptions runtime limitation in issue 287. WebCPP does not change native
allocation or export a warning suppression to consumers. For an affected strict
build, scope the diagnostic adjustment to the coroutine functions or translation
units that reproduce it, rather than disabling allocation diagnostics globally.
The original io::udp_v6 constant also selects ICMP rather than UDP. Native
identity preserves that defect. Use an explicit static protocol with the same
IPv6 family and datagram type but the UDP protocol value; the udp_v6 regression
records the original mismatch and exchanges datagrams with that control. The
shipped Wasm backend corrects io::udp_v6 itself.
After the final writer closes a native Windows pipe, Boost.Asio reports
broken_pipe rather than eof on the next empty read. The native Cobalt pipe
retains that error and returns zero bytes. See
Asio issue 1442. Native POSIX
and WASIp3 tests require eof; none of these paths rewrites the native error.
WebCPP fixes Boost.Cobalt 1.92 SSL wrapper defects on native and WASIp2/P3.
The owner approved this native exception on 2026-11-06 and retained it on
2026-11-07. webcpp::cobalt::io::ssl::stream is a distinct, corrected type;
its native SSL context and plain Asio dependencies retain Boost identity.
These are Cobalt wrapper fixes, not changes to OpenSSL’s cryptographic
implementation or to native Cobalt’s core and runner.
The original handshake completion did not update the stream’s TLS state.
Consequently, a successful handshake could leave secure() false, subsequent
I/O selecting plain TCP and shutdown() returning no_protocol_option.
Lazy handshake/shutdown operations also captured state at construction instead
of checking it when awaited. The fix updates the stream before resuming the
coroutine and checks its current state when an operation starts.
The original wrapper also allowed application I/O to select plain TCP after a
failed or cancelled handshake. The fix records the first TLS attempt and blocks
that fallback with not_connected and zero transferred bytes. Shutdown completion
clears the TLS state even on error or cancellation. The precise state and retry
contract is in SSL handshake and stream state; successful negotiation still does not replace
the application’s trust and hostname verification settings.
Native complete-buffer SSL read/write overloads also fix lost cancellation by
using Asio’s composed operations with the completion handler’s associated
properties. This correction requires the concrete SSL type as explained in
Preserve the corrected native SSL composition. Erasing it to Boost’s generic I/O stream still selects
the unchanged upstream composition.
Move construction rebinds socket access to the destination’s SSL lowest layer and preserves handshake state, fixing the socket facade’s reference to the moved-from object. Copying and assignment are unavailable. Move only when no lazy or outstanding operation retains either stream’s address; moving does not transfer those operation references. After moving, the source is non-secure and may be destroyed. Tests destroy the source before handshake and after completed successful or rejected handshakes, then exercise the destination.
The TLS 1.2/1.3 regression fixtures are test/tls/cobalt_ssl_client.cpp,
cobalt_ssl_state.cpp and cobalt_ssl_cancel.cpp: they check real peers, state
transitions, blocked application I/O, moves, transfer cancellation and shutdown.
The native identity fixtures check both header orders. See Testing
for the lanes and their exception/threading profiles. These wrapper fixes do not
repair the other native Boost limitations described above.
On Wasm, library-owned headers and sources select the same shipped Asio backend
as WebCPP Beast. Corrections include the browser runner, WASI networking and
SSL state machine, and aligned storage handling. The importer verifies original
and corrected inputs and output hashes; application builds do not execute it.
See upstream/README.md and its manifests for provenance.
In the browser, the runner and the checked task runner yield between bounded turns, with Asio’s default of 100 handlers. A long-running individual handler still blocks its thread. JSPI delegates suspension to the engine; Asyncify uses instrumented Wasm. Availability, stack requirements and performance are separate from Cobalt’s coroutine semantics.
An upstream example depending on a native signal, Unix socket, filesystem, blocking thread or TCP listener may require an explicit host counterpart. The matrix identifies that adaptation instead of promising identical facilities in a browser. A browser WebSocket or fetch operation uses host TLS and security policy rather than the guest OpenSSL wrapper.
Appendix B: Changelog
Changes prepared for WebCPP’s independent Cobalt integration.
Unreleased
-
Changed the SPSC demo to report inherited queue data loss.
-
Fixed finite echo cancellation and synchronized peer-reset verification.
-
Added a bounded SDK witness for unsupported Emscripten pipe EOF behavior.
-
Fixed GCC 14 coroutine allocation false positives blocking native test consumers.
-
Added explicit native and WebAssembly coroutine sources for independent consumers.
-
Fixed native/WASI SSL state and plaintext fallback after failed or cancelled handshakes.
-
Fixed SSL transfer cancellation and socket ownership after moving streams.
-
Fixed the shipped Wasm IPv6 UDP protocol constant; native Boost behavior remains documented.
-
Added target configuration, application matrices and Release performance and memory documentation.