Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
83099c9
[test] Add overrides to SingleLineTestResult to support expected fail…
sbc100 Apr 3, 2026
9a23e37
Mark 5.0.5 as released (#26620)
emscripten-bot Apr 3, 2026
9c76574
Remove dead code from src/deterministic.js. NFC (#26618)
sbc100 Apr 3, 2026
07d59b6
[WasmFS] Add missing dependency in the Node backend. NFC (#26596)
kleisauke Apr 4, 2026
be9f95b
[WasmFS] Make the Node backend optional for web builds (#26608)
kleisauke Apr 4, 2026
a7804ea
[WasmFS] Ensure error codes are positive in `_wasmfs_node_*` APIs (#2…
kleisauke Apr 4, 2026
5a25d42
Fix asyncify mistake in docs (#26627)
BoryaGames Apr 5, 2026
bee9a1c
Fix mistake in `create_entry_points.py` (#26623)
podsvirov Apr 5, 2026
557c3fb
[WasmFS] Use `const uint8_t*` in JS API impl. NFC (#24706)
kleisauke Apr 6, 2026
49f63ca
[WasmFS] Make the Node backend multi-env compatible (#26624)
kleisauke Apr 6, 2026
ecb9e39
Add docs/design directory along with one initial design doc. (#26621)
sbc100 Apr 6, 2026
2b98693
Remove outdated note in "Interacting with code" docs (#26638)
jhcarl0814 Apr 7, 2026
8659483
Cleanup test_emscripten_thread_sleep. NFC (#26637)
sbc100 Apr 7, 2026
b69cb85
Bump minimum supported node version v12.22.9 to v18.3.0 (#26604)
sbc100 Apr 7, 2026
426fb2d
Design doc feedback from #26621. NFC (#26636)
sbc100 Apr 7, 2026
3ba6816
Simplify MIN_NODE_VERSION override from embind ts-gen. (#26643)
sbc100 Apr 7, 2026
a8e9415
Fix Pkgconfig for freetype2 (#26640)
clementperon Apr 7, 2026
12dcf75
Remove final usages of node's `global`. NFC (#26642)
sbc100 Apr 7, 2026
d5ebf26
Simplify FS missing assertions. NFC (#26641)
sbc100 Apr 7, 2026
eacafb3
Harfbuzz add Pkgconfig file (#26645)
clementperon Apr 7, 2026
f2ad693
Add design doc: Wasm Worker Pthread Compatibility (#26634)
sbc100 Apr 7, 2026
317c35d
Add testing for compiler built-in `atomic_is_lock_free` API. NFC (#26…
sbc100 Apr 7, 2026
d984d06
Fix/remove TODO in is_lock_free tests. NFC (#26649)
sbc100 Apr 8, 2026
339a66f
Automatic rebaseline of codesize expectations. NFC (#26651)
emscripten-bot Apr 8, 2026
54ef4c6
Add `@requires_wasm_workers` decorator. NFC (#26652)
sbc100 Apr 8, 2026
4cadbe3
Fix for exceptions in Wasm Workers under node (#26650)
sbc100 Apr 8, 2026
547fb26
Mark `-sDETERMINISIC` as deprecated. (#26653)
sbc100 Apr 8, 2026
9a755c0
Remove redundant min version checks (#26654)
sbc100 Apr 8, 2026
1738682
Automatic rebaseline of codesize expectations. NFC (#26656)
emscripten-bot Apr 9, 2026
03c9ee7
Minor cleanup of setitimer. NFC (#26655)
sbc100 Apr 9, 2026
7481de0
Change start WW id value in hybrid threading mode (#26660)
stephenduong1004 Apr 9, 2026
c22a176
[ci] Use newly official github action to install emsdk (#26662)
sbc100 Apr 9, 2026
fdbd73c
Fix multiple jscompiler errors (#26632)
stephenduong1004 Apr 9, 2026
7b7fd47
Temporarily disabled a bunch of circleci testing (#26664)
sbc100 Apr 9, 2026
dc413f8
Avoid shipping internal musl headers as part of the sysroot (#26658)
sbc100 Apr 9, 2026
2cf3138
Add more testing for pthread_kill. (#26663)
sbc100 Apr 10, 2026
4c4c5d4
Emsymbolizer: Remove VMA adjustment from llvm-symbolizer call (#26665)
dschuff Apr 10, 2026
670b4c7
[test] Add EMTEST_TIMEOUT environment variable (#26669)
sbc100 Apr 11, 2026
b3632b8
Remove special case from pthread_kill. (#26668)
sbc100 Apr 11, 2026
1177b42
cmdline: Allow overriding -jsD directives (#26639)
subhaushsingh Apr 13, 2026
74209a7
Add testing for libtty.js & make fsync a no-op if absent
hoodmane Apr 13, 2026
f54e4f6
Automatic rebaseline of codesize expectations. NFC
hoodmane Apr 13, 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
45 changes: 22 additions & 23 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,11 @@ commands:
cat ~/emsdk/.emscripten
echo "export PATH=\"$HOME/node-v${version}-linux-x64/bin:\$PATH\"" >> $BASH_ENV
install-node-oldest:
description: "install node 12.22.9 (oldest)"
description: "install node 18.3.0 (oldest)"
steps:
- install-node-version:
# Keep this in sync with `OLDEST_SUPPORTED_NODE` in `feature_matrix.py`
node_version: "12.22.9"
node_version: "18.3.0"
install-node-lts:
description: "install node 22.21.0 (current LTS)"
steps:
Expand Down Expand Up @@ -970,8 +970,7 @@ jobs:
# support in the generated code.
- install-node-oldest
- run-tests:
title: "node (oldest / 12.22.9)"
extra-cflags: "-sMIN_NODE_VERSION=122209"
title: "node (oldest / 18.3.0)"
# We include most but not all of the nodefs and node rawfs tests here.
# test_fs_nodefs_rw, test_fs_nodefs_statvfs, and test_unistd_io_nodefs_bigint fail.
test_targets: "
Expand Down Expand Up @@ -1368,15 +1367,15 @@ workflows:
- test-core0:
requires:
- build-linux
- test-core2:
requires:
- build-linux
#- test-core2:
# requires:
# - build-linux
- test-core3:
requires:
- build-linux
- test-wasm64-misc:
requires:
- build-linux
#- test-wasm64-misc:
# requires:
# - build-linux
- test-wasm64-4gb:
requires:
- build-linux
Expand All @@ -1389,9 +1388,9 @@ workflows:
- test-modularize-instance:
requires:
- build-linux
- test-esm-integration:
requires:
- build-linux
#- test-esm-integration:
# requires:
# - build-linux
- test-stress:
requires:
- build-linux
Expand All @@ -1412,14 +1411,14 @@ workflows:
- test-sockets-chrome:
requires:
- build-linux
- test-bun
- test-deno
- test-jsc
- test-spidermonkey
- test-node-compat
- test-windows
- test-windows-browser-firefox
#- test-bun
#- test-deno
#- test-jsc
#- test-spidermonkey
#- test-node-compat
#- test-windows
#- test-windows-browser-firefox
- build-windows-launcher
- test-mac-arm64:
requires:
- build-linux
#- test-mac-arm64:
# requires:
# - build-linux
20 changes: 7 additions & 13 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@ jobs:
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- name: Install emsdk
uses: emscripten-core/setup-emsdk@v16
with:
version: tot
- name: Set EM_CONFIG
run:
echo "EM_CONFIG=$EMSDK/.emscripten" >> $GITHUB_ENV
- name: Checkout repo
uses: actions/checkout@v4
with:
Expand All @@ -42,19 +49,6 @@ jobs:
which python3
python3 --version
python3 -m pip install -r requirements-dev.txt
- name: Install emsdk
run: |
EM_CONFIG=$HOME/emsdk/.emscripten
echo "emscripten config file: $EM_CONFIG"
echo "EM_CONFIG=$EM_CONFIG" >> $GITHUB_ENV
curl -# -L -o ~/emsdk-main.tar.gz https://github.com/emscripten-core/emsdk/archive/main.tar.gz
tar -C ~ -xf ~/emsdk-main.tar.gz
mv ~/emsdk-main ~/emsdk
cd ~/emsdk
./emsdk install tot
./emsdk activate tot
echo "emscripten config file ($EM_CONFIG) contents:"
cat $EM_CONFIG
- name: Check test expectations on target branch
# Skip this step for rebaseline PRs, since the target branch is expected
# to be out of date in this case.
Expand Down
21 changes: 7 additions & 14 deletions .github/workflows/rebaseline-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ jobs:
env:
GH_TOKEN: ${{ secrets.EMSCRIPTEN_BOT_TOKEN }}
steps:
- name: Install emsdk
uses: emscripten-core/setup-emsdk@v16
with:
version: tot
- name: Set EM_CONFIG
run:
echo "EM_CONFIG=$EMSDK/.emscripten" >> $GITHUB_ENV
- name: Checkout repo
uses: actions/checkout@v4
with:
Expand All @@ -27,20 +34,6 @@ jobs:
which python3
python3 --version
python3 -m pip install -r requirements-dev.txt
- name: Install emsdk
run: |
EM_CONFIG=$HOME/emsdk/.emscripten
echo $EM_CONFIG
curl -# -L -o ~/emsdk-main.tar.gz https://github.com/emscripten-core/emsdk/archive/main.tar.gz
tar -C ~ -xf ~/emsdk-main.tar.gz
mv ~/emsdk-main ~/emsdk
cd ~/emsdk
./emsdk install tot
./emsdk activate tot
echo "JS_ENGINES = [NODE_JS]" >> $EM_CONFIG
echo "final config:"
cat $EM_CONFIG
echo "EM_CONFIG=$EM_CONFIG" >> $GITHUB_ENV
- name: Rebaseline tests
run: |
git config user.name emscripten-bot
Expand Down
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -602,3 +602,4 @@ a license to everyone to use it as detailed in LICENSE.)
* Christian Lloyd <clloyd@teladochealth.com> (copyright owned by Teladoc Health, Inc.)
* Sean Morris <sean@seanmorr.is>
* Mitchell Wills <mwills@google.com> (copyright owned by Google, Inc.)
* Han Jiang <jhcarl0814@gmail.com>
11 changes: 10 additions & 1 deletion ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,17 @@ to browse the changes between the tags.

See docs/process.md for more on how version tagging works.

5.0.5 (in development)
5.0.6 (in development)
----------------------
- The minimum version of node supported by the generated code was bumped from
v12.22.0 to v18.3.0. (#26604)
- The DETERMINISIC settings was marked as deprecated (#26653)
- Some musl-internal headers are no longer installed into the sysroot include
directory. In particular, `syscall_arch.h` no longer exists, but can be
replaced with `emscripten/syscalls.h`. (#26658)

5.0.5 - 04/03/26
----------------
- C++ exceptions are now always thrown as CppException objects rather than raw
pointers/numbers. However, the `.message` and `.stack` fields of the thrown
object will only be populated if `-sEXCEPTION_STACK_TRACES` is set. (#26523)
Expand Down
138 changes: 138 additions & 0 deletions docs/design/01-precise-futex-wakeups.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# Design Doc: Precise Futex Wakeups

- **Status**: Draft
- **Bug**: https://github.com/emscripten-core/emscripten/issues/26633

## Context
Currently, `emscripten_futex_wait` (in
`system/lib/pthread/emscripten_futex_wait.c`) relies on a periodic wakeup loop
for pthreads and the main runtime thread. This is done for two primary reasons:

1. **Thread Cancellation**: To check if the calling thread has been cancelled while it is blocked.
2. **Main Runtime Thread Events**: To allow the main runtime thread (even when not the main browser thread) to process its mailbox/event queue.

The current implementation uses a 1ms wakeup interval for the main runtime
thread and a 100ms interval for cancellable pthreads. This leads to unnecessary
CPU wakeups and increased latency for events.

## Goals
- Remove the periodic wakeup loop from `emscripten_futex_wait`.
- Implement precise, event-driven wakeups for cancellation and mailbox events.
- Maintain the existing `emscripten_futex_wait` API signature.
- Focus implementation on threads that support `atomic.wait` (pthreads and workers).

## Non-Goals
- **Main Browser Thread**: Changes to the busy-wait loop in `futex_wait_main_browser_thread` are out of scope.
- **Direct Atomics Usage**: Threads that call `atomic.wait` directly (bypassing `emscripten_futex_wait`) will remain un-interruptible.
- **Wasm Workers**: Wasm Workers do not have a `pthread` structure, so they are not covered by this design.

## Proposed Design

The core idea is to allow "side-channel" wakeups (cancellation, mailbox events)
to interrupt the `atomic.wait` call by having the waker call `atomic.wake` on the
same address the waiter is currently blocked on.

As part of this design we will need to explicitly state that
`emscripten_futex_wait` now supports spurious wakeups. i.e. it may return `0`
(success) even if the underlying futex was not explicitly woken by the
application.

### 1. `struct pthread` Extensions
We will add a single atomic `wait_addr` field to `struct pthread` (in
`system/lib/libc/musl/src/internal/pthread_impl.h`).

```c
// The address the thread is currently waiting on in emscripten_futex_wait.
//
// This field encodes the state using the following bitmask:
// - NULL: Not waiting, no pending notification.
// - NOTIFY_BIT (0x1): Not waiting, but a notification was sent.
// - addr: Waiting on `addr`, no pending notification.
// - addr | NOTIFY_BIT: Waiting on `addr`, notification sent.
//
// Since futex addresses must be 4-byte aligned, the low bit is safe to use.
_Atomic uintptr_t wait_addr;

#define NOTIFY_BIT (1 << 0)
```

### 2. Waiter Logic (`emscripten_futex_wait`)
The waiter will follow this logic:

1. **Notification Loop**:
```c
uintptr_t expected_null = 0;
while (!atomic_compare_exchange_strong(&self->wait_addr, &expected_null, (uintptr_t)addr)) {
// If the CAS failed, it means NOTIFY_BIT was set by another thread.
assert(expected_null == NOTIFY_BIT);
// Let the notifier know that we received the wakeup notification by
// resetting wait_addr.
self->wait_addr = 0;
handle_wakeup(); // Process mailbox or handle cancellation
// Reset expected_null because CAS updates it to the observed value on failure.
expected_null = 0;
}
```
2. **Wait**: Call `ret = __builtin_wasm_memory_atomic_wait32(addr, val, timeout)`.
3. **Unpublish & Check**:
```c
// Clear wait_addr and check if a notification arrived while we were sleeping.
if ((atomic_exchange(&self->wait_addr, 0) & NOTIFY_BIT) != 0) {
handle_wakeup();
}
```
4. **Return**: Return the result of the wait.

Note: We do **not** loop internally if `ret == ATOMICS_WAIT_OK`. Even if we
suspect the wake was caused by a side-channel event, we must return to the user
to avoid "swallowing" a simultaneous real application wake.

### 3. Waker Logic
When a thread needs to wake another thread for a side-channel event:

1. **Enqueue Work**: Add the task to the target's mailbox or set the cancellation flag.
2. **Signal**:
```c
uintptr_t addr = atomic_fetch_or(&target->wait_addr, NOTIFY_BIT);
if (addr == 0 || (addr & NOTIFY_BIT) != 0) {
// Either the thread wasn't waiting (it will see NOTIFY_BIT later),
// or someone else is already in the process of notifying it.
return;
}
// We set the bit and are responsible for waking the target.
// The target is currently waiting on `addr`.
while (target->wait_addr == (addr | NOTIFY_BIT)) {
emscripten_futex_wake((void*)addr, INT_MAX);
sched_yield();
}
```

### 4. Handling the Race Condition
The protocol handles the "Lost Wakeup" race by having the waker loop until the
waiter clears its `wait_addr`. If the waker sets the `NOTIFY_BIT` just before
the waiter enters `atomic.wait`, the `atomic_wake` will be delivered once the
waiter is asleep. If the waiter wakes up for any reason (timeout, real wake, or
side-channel wake), its `atomic_exchange` will satisfy the waker's loop
condition.

## Benefits

- **Lower Power Consumption**: Threads can sleep indefinitely (or for the full duration of a user-requested timeout) without periodic wakeups.
- **Lower Latency**: Mailbox events and cancellation requests are processed immediately rather than waiting for the next 1ms or 100ms tick.
- **Simpler Loop**: The complex logic for calculating remaining timeout slices in `emscripten_futex_wait` is removed.

## Alternatives Considered
- **Signal-based wakeups**: Not currently feasible in Wasm as signals are not
implemented in a way that can interrupt `atomic.wait`.
- **A single global "wake-up" address per thread**: This would require the
waiter to wait on *two* addresses simultaneously (the user's futex and its
own wakeup address), which `atomic.wait` does not support. The proposed
design works around this by having the waker use the *user's* futex address.

## Security/Safety Considerations
- **The `wait_addr` must be managed carefully** to ensure wakers don't
call `atomic.wake` on stale addresses. Clearing the address upon wake
mitigates this.
- **The waker loop should have a reasonable fallback** (like a yield) to prevent a
busy-wait deadlock if the waiter is somehow prevented from waking up (though
`atomic.wait` is generally guaranteed to wake if `atomic.wake` is called).
75 changes: 75 additions & 0 deletions docs/design/02-wasm-worker-pthread-compat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Design Doc: Wasm Worker Pthread Compatibility

- **Status**: Draft
- **Bug**: https://github.com/emscripten-core/emscripten/issues/26631

## Context

Wasm Workers in Emscripten are a lightweight alternative to pthreads. They use
the same memory and can use the same synchronization primitives, but they do not
have a full `struct pthread` and thus many pthread-based APIs (like
`pthread_self()`) currently do not work when called from a Wasm Worker.

This is not an issue in pure Wasm Workers programs but we also support hybrid
programs that run both pthreads and Wasm Workers. In this cases the pthread
API is available, but will fail in undefined ways if called from Wasm Workers.

This document proposes a plan to improve the hybrid mode by adding the pthread
metadata (`struct pthread`) to each Wasm Worker, allowing the pthread API (or at
least some subset of it) APIs to used from Wasm Workers.

## Proposed Changes

### 1. Memory Layout

Currently, Wasm Workers allocate space for TLS and stack: `[TLS data] [Stack]`.
We propose to change this to: `[struct pthread] [TLS data] [Stack]`.

The `struct pthread` will be located at the very beginning of the allocated
memory block for each Wasm Worker.

### 2. `struct pthread` Initialization

The `struct pthread` will be initialized by the creator thread in `emscripten_create_wasm_worker` (or `emscripten_malloc_wasm_worker`).
This includes:
- Zero-initializing the structure.
- Setting the `self` pointer to the start of the `struct pthread`.
- Initializing essential fields like `tid`.

On the worker thread side, `_emscripten_wasm_worker_initialize` will need to set
the thread-local pointer (returned by `__get_tp()`) to the `struct pthread`
location.

### 3. `__get_tp` Support

We will modify `system/lib/pthread/emscripten_thread_state.S` to provide a
`__get_tp` implementation for Wasm workers that returns the address of the
`struct pthread`. This will allow `__pthread_self()` and other related functions
to work correctly.

### 4. Supported Pthread API Subset

We intend to support a subset of the pthread API within Wasm workers:
- `pthread_self()`: Returns the worker's `struct pthread` pointer.
- `pthread_equal()`: Works normally.
- `pthread_getspecific()` / `pthread_setspecific()`: TSD (Thread Specific Data) should work if `tsd` field in `struct pthread` is initialized.
- `pthread_mutex_*`: Mutexes will work as they rely on `struct pthread` for owner tracking.
- `pthread_cond_*`: Condition variables will work as they rely on `struct pthread` for waiter tracking.
- Low-level synchronization primitives that use `struct pthread` (e.g., some internal locks).

APIs that will NOT be supported (or will have limited support):
- `pthread_create()` / `pthread_join()` / `pthread_detach()`: Wasm workers have their own creation and lifecycle management.
- `pthread_cancel()`: Not supported in Wasm workers.
- `pthread_kill()`: Not supported in Wasm workers.

## Implementation Plan

1. Modify `emscripten_create_wasm_worker` in `system/lib/wasm_worker/library_wasm_worker.c` to account for `sizeof(struct pthread)` in memory allocation and initialize the structure.
2. Update `_emscripten_wasm_worker_initialize` in `system/lib/wasm_worker/wasm_worker_initialize.S` to set the thread pointer.
3. Modify `system/lib/pthread/emscripten_thread_state.S` to enable `__get_tp` for Wasm workers.
4. Review and test essential pthread functions (like TSD) in Wasm workers.
5. Document the supported and unsupported pthread APIs for Wasm workers.

## Verification
- Add a new test that makes use of `pthread_self()` and low level synchronization APIs from a Wasm Worker.
- Verify that existing Wasm worker tests still pass.
Loading
Loading