BUILD_AS_WORKER: onmessage is installed too late under MODULARIZE, and is installed even on the main thread
メンテナーはふだん 1 日以内に返信
まだ誰も着手していません。
評価
- 難易度
- 4/5
- 見積もり時間
- 3〜5日
- 初心者へのやさしさ
- 55/100
- issue の種類
- バグ
- 明瞭さ
- おおむね明確
- 活発さ
- 活発
- 技術スタック
- javascript, wasm
調査の方向性
src/build_as_worker.js から始め、MODULARIZE 下でその IIFE がどのように出力され、呼び出されるかを追跡します。これには、issue で説明されている worker の bootstrap または --pre-js パスも含まれます。メインスレッド上での動作と、factory の実行前に送信されたメッセージについて確認します。非 worker ページが onmessage ハンドラーを保持し、factory 前のメッセージが保持されて初期化後に配信されれば完了です。
索引モデルが issue の本文から書いたものです。
説明
src/build_as_worker.js wraps everything in an IIFE that installs the worker's onmessage:
(() => {
var messageBuffer = null, buffer = 0;
function flushMessages() { ... }
function messageResender() { ... }
onmessage = (msg) => {
// if main has not yet been called (mem init file, other async things), buffer messages
if (!runtimeInitialized) {
if (!messageBuffer) {
messageBuffer = [];
setTimeout(messageResender, 100);
}
messageBuffer.push(msg);
return;
}
...
}
})();
Two problems follow from when and where that IIFE runs. They are independent, so I have described both here and am happy to split them.
1. The handler is installed even when the module is not running in a worker
The IIFE runs in whatever environment loads the module, and assigns onmessage unconditionally. A module linked with -sBUILD_AS_WORKER that also runs on the main thread therefore overwrites window.onmessage, taking over message delivery for the whole page.
That is not a quiet takeover. Any message the page receives from another source now reaches this handler, which either aborts:
var func = Module['_' + msg.data['funcName']];
if (!func) abort('invalid worker function to call: ' + msg.data['funcName']);
or throws a TypeError outright, if msg.data is not an object with a funcName (a plain postMessage('...') from an embedding page or another library, for instance).
A guard at the top of the IIFE seems like the right fix:
if (!ENVIRONMENT_IS_WORKER) return;
2. Under MODULARIZE, messages that arrive before the factory runs are dropped
With -sMODULARIZE the IIFE lives inside the module factory, which the application invokes whenever it is ready. A worker, however, starts receiving messages the moment it is created — so there is a window between "worker script began executing" and "factory was called" during which these messages arrive.
The existing buffering does not cover that window. messageBuffer only starts collecting from inside the handler this IIFE installs, so it protects the interval between the factory running and runtimeInitialized — not the earlier one.
The natural workaround is to install a small buffering onmessage in the worker bootstrap (or via --pre-js) that collects messages until the factory is called. That does not work either: this IIFE overwrites onmessage and starts from messageBuffer = null, so everything the earlier handler collected is discarded.
The net effect is that a MODULARIZE'd BUILD_AS_WORKER module silently loses any message sent before the application gets around to instantiating it — with no way for the application to bridge the gap.
What we ship locally is for the IIFE to adopt a pre-existing buffer if one is present:
var messageBuffer = typeof workerMessageBuffer != 'undefined' ? workerMessageBuffer : null, buffer = 0;
// Upstream only starts the resender from inside its own handler, so an adopted
// buffer would otherwise never be flushed.
if (messageBuffer) setTimeout(messageResender, 100);
The bootstrap then just pushes into workerMessageBuffer until the factory is instantiated, and the module drains it. This has been running in production for us for a while.
I am raising this as an issue rather than sending the patch straight away, because the second half proposes a convention (workerMessageBuffer) that emscripten does not currently define, and you may well prefer a different shape — an explicit Module option, or having build_as_worker.js emit its own pre-factory bootstrap. Happy to send a PR for whichever you would like, and #1 can go on its own regardless.
Related, in that they are all variations of "the generated worker's onmessage is not reachable by the application": #20192, #11962, #8854.
Version
emscripten 6.0.3; the same code is on main as of today.
- 主要言語
- C++
- スター
- 27.8k
- フォーク
- 3.6k
- 平均マージ
- 1日 3時間
- マージ済み PR(30日)
- 179
環境構築
- Dockerfile・Docker Compose ファイルなし
- プルリクエストのテンプレートなし
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
emscripten-core/emscripten のほかの issue
-
`other.test_cmake_cxx_import_std` fails on CMake 3.30–4.0.2 due to missing `CMAKE_EXPERIMENTAL_CXX_IMPORT_STD` UUIDs対応中かも @subhaushsingh が 1 日前に担当しました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 85/100
emscripten-core/emscripten#28160 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 62/100
emscripten-core/emscripten#27994 · コメント 2 件 ·
メンテナーはふだん 1 日以内に返信
-
LEGACY_GL_EMULATION: generated fixed-function fragment shaders use mediump — fp16 overflow where mediump is half precision対応中かも @TheTomBritton が 13 日前に担当しました。 オープンDesktop OpenGL emulation
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
emscripten-core/emscripten#27948 · コメント 4 件 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 64/100
emscripten-core/emscripten#27906 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
run_python, run_python_compiler could readlink -f対応中かも @kapouer が 128 日前に担当しました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 74/100
emscripten-core/emscripten#27211 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
emscripten-core/emscripten の issue をすべて見る
似ている issue
-
難易度 1/5 1〜3時間 初心者へのやさしさ 68/100
eic/TimeframeBuilder#102 ·
-
難易度 1/5 1時間未満 初心者へのやさしさ 65/100
-
error not reported I/O low priority rt_lib
難易度 2/5 1〜3時間 初心者へのやさしさ 67/100
メンテナーはふだん 1 日以内に返信
-
[XLA] hlo/pass/README.md says passes implement Run; since the NVI change they implement RunImplオープン
難易度 1/5 1時間未満 初心者へのやさしさ 91/100
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 88/100
objectionary/game-of-life#78 ·