Skip to content

[skwasm] Implement Wasm worker futex semaphores - #194139

Merged
auto-submit[bot] merged 5 commits into
flutter:masterfrom
kevmoo:wimp-skwasm-worker-futex-semaphore
Oct 17, 2026
Merged

auto-submit[bot] merged 5 commits into
flutter:masterfrom
kevmoo:wimp-skwasm-worker-futex-semaphore

Conversation

@kevmoo

@kevmoo kevmoo commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

Replaces Emscripten's no-op -sWASM_WORKERS=1 POSIX semaphore stubs (sem_init, sem_destroy, sem_post, sem_wait, sem_trywait) with a WebAssembly atomic futex implementation in multi-threaded Skwasm and Web Impeller (wimp) builds.

Rationale

Under Emscripten's -sWASM_WORKERS=1 runtime without -pthread (skwasm_heavy / wimp_heavy), libc-ww.a compiles system/lib/pthread/library_pthread_stub.c, which stubs sem_init, sem_wait, sem_trywait, sem_post, and sem_destroy (as well as emscripten_futex_wait / emscripten_futex_wake) to return 0.

In Skia (third_party/skia/src/core/SkSemaphore.cpp), SkSemaphore uses POSIX sem_t on __EMSCRIPTEN__ as the contended slow-path backing for SkMutex and SkSharedMutex. Because sem_wait is a no-op, any contended acquisition on FreeType's global f_t_mutex() (third_party/skia/src/ports/SkFontHost_FreeType.cpp:221-226) or SkTypeface_FreeType::fC2GCacheMutex (third_party/skia/src/ports/SkTypeface_FreeType.h:87) returns immediately when the browser main UI thread performs Paragraph::layout / MakeTextFrameFromTextBlobSkia concurrently with the background render worker rasterizing glyphs from the shared FT_Library / FT_Face, causing memory corruption and worker stalls (flutter/flutter#193695).

Summary of Changes

  • engine/src/flutter/skwasm/wasm_worker_semaphore.cc & engine/src/flutter/skwasm/BUILD.gn: Override sem_init, sem_destroy, sem_post, sem_wait, and sem_trywait via linker -Wl,--wrap=sem_* in multi-threaded skwasm_variant (#if defined(__EMSCRIPTEN_WASM_WORKERS__)) builds:
    • Stores the atomic counter inline in sem_t's 16-byte __val buffer (cache/sysroot/include/semaphore.h:17-19), requiring zero heap allocation or teardown in sem_destroy.
    • Background DedicatedWorker threads (emscripten_wasm_worker_self_id() != 0, cached in a thread_local bool to avoid a Wasm-to-JS FFI call per contended wait) sleep via emscripten_atomic_wait_u32 and wake via emscripten_atomic_notify.
    • The browser main thread (emscripten_wasm_worker_self_id() == 0, where memory.atomic.wait32 / Atomics.wait is disallowed by the WebAssembly Threads spec because [[CanBlock]] == false) uses an atomic compare-exchange spin-acquire loop.

Verification

  • Built wasm_release:skwasm/skwasm_heavy.js and wasm_release:skwasm/wimp_heavy.js and verified 20/20 multi-threaded text-grid runs (grid@100 and grid@1000) complete without FT_Face memory corruption or worker stalls in Chrome 154.

Fixes #193695

Pre-launch Checklist

Overrides Emscripten's no-op `-sWASM_WORKERS=1` POSIX semaphore stubs
(`sem_init`, `sem_destroy`, `sem_post`, `sem_wait`, `sem_trywait`) via
linker `--wrap` in multi-threaded Skwasm and WIMP builds, sleeping via
`emscripten_atomic_wait_u32` on DedicatedWorker threads and using an
atomic compare-exchange spin-acquire on the browser main thread.

Fixes flutter#193695
@kevmoo kevmoo added the CICD Run CI/CD label Oct 10, 2026
@github-actions github-actions Bot added the engine flutter/engine related. See also e: labels. label Oct 10, 2026
@flutter-dashboard

Copy link
Copy Markdown

It looks like this pull request may not have tests. Please make sure to add tests or get an explicit test exemption before merging.

If you are not sure if you need tests, consider this rule of thumb: the purpose of a test is to make sure someone doesn't accidentally revert the fix. Ask yourself, is there anything in your PR that you feel it is important we not accidentally revert back to how it was before your fix?

Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing. If both you and the PR author believe this PR qualifies for a test exemption, escalate to the tech lead for the changed area to review and apply the test-exempt label. All reviewers should feel empowered to ask for tests before escalating.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a custom semaphore implementation for Emscripten WASM workers in Skwasm, wrapping standard POSIX semaphore functions via linker flags. The review feedback suggests adding a null check to sem_destroy for POSIX compliance and optimizing the atomic retry loop in sem_post by moving the initial atomic load outside the loop.

@github-actions github-actions Bot added a: text input Entering text in a text field or keyboard related problems platform-web Web applications specifically team-web Owned by Web platform team labels Oct 16, 2026

@harryterkelsen harryterkelsen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this fix!

@kevmoo
kevmoo requested a review from harryterkelsen October 16, 2026 20:38
Comment thread engine/src/flutter/skwasm/BUILD.gn Resolved
@kevmoo
kevmoo requested a review from harryterkelsen October 16, 2026 22:52
@kevmoo kevmoo added the autosubmit Merge PR when tree becomes green via auto submit App label Oct 17, 2026
@auto-submit
auto-submit Bot added this pull request to the merge queue Oct 17, 2026
Merged via the queue into flutter:master with commit 095babc Oct 17, 2026
23 checks passed
@flutter-dashboard flutter-dashboard Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Oct 17, 2026
@kevmoo
kevmoo deleted the wimp-skwasm-worker-futex-semaphore branch October 17, 2026 01:59
This was referenced Oct 17, 2026
suryaKommana2662 pushed a commit to suryaKommana2662/flutter that referenced this pull request Oct 29, 2026
Replaces Emscripten's no-op `-sWASM_WORKERS=1` POSIX semaphore stubs
(`sem_init`, `sem_destroy`, `sem_post`, `sem_wait`, `sem_trywait`) with
a WebAssembly atomic futex implementation in multi-threaded Skwasm and
Web Impeller (`wimp`) builds.

### Rationale

Under Emscripten's `-sWASM_WORKERS=1` runtime without `-pthread`
(`skwasm_heavy` / `wimp_heavy`), `libc-ww.a` compiles
`system/lib/pthread/library_pthread_stub.c`, which stubs `sem_init`,
`sem_wait`, `sem_trywait`, `sem_post`, and `sem_destroy` (as well as
`emscripten_futex_wait` / `emscripten_futex_wake`) to `return 0`.

In Skia (`third_party/skia/src/core/SkSemaphore.cpp`), `SkSemaphore`
uses POSIX `sem_t` on `__EMSCRIPTEN__` as the contended slow-path
backing for `SkMutex` and `SkSharedMutex`. Because `sem_wait` is a
no-op, any contended acquisition on FreeType's global `f_t_mutex()`
(`third_party/skia/src/ports/SkFontHost_FreeType.cpp:221-226`) or
`SkTypeface_FreeType::fC2GCacheMutex`
(`third_party/skia/src/ports/SkTypeface_FreeType.h:87`) returns
immediately when the browser main UI thread performs `Paragraph::layout`
/ `MakeTextFrameFromTextBlobSkia` concurrently with the background
render worker rasterizing glyphs from the shared `FT_Library` /
`FT_Face`, causing memory corruption and worker stalls
([flutter#193695](flutter#193695)).

### Summary of Changes

-
[`engine/src/flutter/skwasm/wasm_worker_semaphore.cc`](https://github.com/kevmoo/flutter/blob/87e66a27b6b/engine/src/flutter/skwasm/wasm_worker_semaphore.cc)
&
[`engine/src/flutter/skwasm/BUILD.gn`](https://github.com/kevmoo/flutter/blob/87e66a27b6b/engine/src/flutter/skwasm/BUILD.gn):
Override `sem_init`, `sem_destroy`, `sem_post`, `sem_wait`, and
`sem_trywait` via linker `-Wl,--wrap=sem_*` in multi-threaded
`skwasm_variant` (`#if defined(__EMSCRIPTEN_WASM_WORKERS__)`) builds:
- Stores the atomic counter inline in `sem_t`'s 16-byte `__val` buffer
(`cache/sysroot/include/semaphore.h:17-19`), requiring zero heap
allocation or teardown in `sem_destroy`.
- Background `DedicatedWorker` threads
(`emscripten_wasm_worker_self_id() != 0`, cached in a `thread_local
bool` to avoid a Wasm-to-JS FFI call per contended wait) sleep via
`emscripten_atomic_wait_u32` and wake via `emscripten_atomic_notify`.
- The browser main thread (`emscripten_wasm_worker_self_id() == 0`,
where `memory.atomic.wait32` / `Atomics.wait` is disallowed by the
WebAssembly Threads spec because `[[CanBlock]] == false`) uses an atomic
compare-exchange spin-acquire loop.

### Verification

- Built `wasm_release:skwasm/skwasm_heavy.js` and
`wasm_release:skwasm/wimp_heavy.js` and verified `20/20` multi-threaded
text-grid runs (`grid@100` and `grid@1000`) complete without `FT_Face`
memory corruption or worker stalls in Chrome 154.

Fixes flutter#193695

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [AI contribution guidelines] and understand my
responsibilities, or I am not using AI tools.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant in-code documentation (doc comments with
`///`).
- [x] If this PR introduces a new feature or capability, I created and
linked a website documentation issue or PR in [flutter/website] (or
verified none is needed).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[AI contribution guidelines]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#ai-contribution-guidelines
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[flutter/website]: https://github.com/flutter/website
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
yexuan910812 pushed a commit to yexuan910812/flutter that referenced this pull request Oct 30, 2026
…188628)

Related: flutter#187660. Part 1 of 5 from the original hybrid-rasterizer
change. This PR is now limited to the independent benchmark
instrumentation.

Adds `presentation_duration` around preparation and frame submission,
including asynchronous raster work, and records it in the web benchmark
harness. The timer stops before reporting, and benchmark listeners are
cleaned up even when startup fails. The existing profile JSON schema is
unchanged.

Verified locally: two engine timing tests cover enabled/disabled
profiling and delayed preparation/rasterization; a browser regression
test covers listener cleanup after synchronous startup failures. Static
analysis and formatting pass. This change is independent of the hybrid
rasterizer.

The remaining pieces are separate drafts:

- flutter#195103: native callback IDs and optional bitmap capture.
- flutter#195104: platform-view DOM commit after rasterization.
- flutter#195105: opt-in hybrid presentation, depending on the two changes
above.
- flutter#195106: desktop default policy and benchmark results, depending on
the opt-in implementation.

The duplicate semaphore and codec changes were dropped on rebase because
master already includes flutter#194139 and flutter#193187.

## Pre-launch Checklist

- [ ] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [ ] I read the [AI contribution guidelines] and understand my
responsibilities, or I am not using AI tools.
- [ ] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [ ] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [ ] I signed the [CLA].
- [ ] I listed at least one issue that this PR fixes in the description
above.
- [ ] I updated/added relevant in-code documentation (doc comments with
`///`).
- [ ] If this PR introduces a new feature or capability, I created and
linked a website documentation issue or PR in [flutter/website] (or
verified none is needed).
- [ ] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [ ] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [ ] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

If this change needs to override an active code freeze, provide a
comment explaining why. The code freeze workflow can be overridden by
code reviewers. See pinned issues for any active code freezes with
guidance.

**Note**: The Flutter team is currently trialing the use of [Gemini Code
Assist for
GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code).
Comments from the `gemini-code-assist` bot should not be taken as
authoritative feedback from the Flutter team. If you find its comments
useful you can update your code accordingly, but if you are unsure or
disagree with the feedback, please feel free to wait for a Flutter team
member's review for guidance on which automated comments should be
addressed.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[AI contribution guidelines]:
https://github.com/flutter/flutter/blob/main/CONTRIBUTING.ai.md
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[flutter/website]: https://github.com/flutter/website
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
@erabti

erabti commented Nov 6, 2026

Copy link
Copy Markdown

Could this get cp: beta (and cp: stable if a 3.47 hotfix is possible)? It fixes #193439 / #193695, a freeze or crash in every --wasm app that is served cross-origin isolated, and nothing released has the fix yet. Our production app freezes on 3.47.8. Our only workaround is forceSingleThreadedSkwasm: true, which moves all drawing back to the main thread. When the cherry-pick PR is open, I can fill in its description.

@kevmoo

kevmoo commented Nov 6, 2026

Copy link
Copy Markdown
Contributor Author

@erabti I'm digging in here! If you don't get a reply by Tuesday next week, feel free to ping me directly!

@harryterkelsen harryterkelsen added the cp: beta cherry pick this pull request to beta release candidate branch label Nov 6, 2026
@kevmoo

kevmoo commented Nov 6, 2026

Copy link
Copy Markdown
Contributor Author

@erabti see #196127 - please "subscribe" to track progress on it!

devberkay added a commit to Chessever/chessever_frontend_desktop that referenced this pull request Nov 12, 2026
Adds a manually dispatched "Web deploy" workflow: scripts/build_web.sh on
ubuntu with the Flutter release given as an input (default 3.51.0-0.1.pre on
beta, which carries the multi-threaded Skwasm futex fix, flutter/flutter#194139,
for the text-layout/glyph-raster race 3.50.0 still has, #193695), then
scripts/deploy_web.sh from main in the release environment. The deploy is
tagged with the app version and messaged "summary; sha; version; webapp
only", like the hand-run deploys, and checked against the active Worker
version. Building alone works from any branch.

The deploy job needs CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID in the
release environment.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a: text input Entering text in a text field or keyboard related problems CICD Run CI/CD cp: beta cherry pick this pull request to beta release candidate branch engine flutter/engine related. See also e: labels. platform-web Web applications specifically team-web Owned by Web platform team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[web] SkSemaphore is a no-op under -sWASM_WORKERS=1, racing FT_Face

3 participants