Making thread-local functions viable
まだ誰も着手していません。
評価
- 難易度
- 5/5
- 見積もり時間
- 1週間以上
- 初心者へのやさしさ
- 20/100
- issue の種類
- 機能追加
- 明瞭さ
- 説明が足りない
- 活発さ
- 停滞
- 技術スタック
- wasm
- 領域
- compilers
調査の方向性
ファイル、テスト、または実装のエントリーポイントは指定されていません。まず discussions/34 と issues/42 および /44 を読み、次に提案されている eval_realm、shared builtins、thread-local function の各アプローチを、提案の現在の shared-function および JS-API のセマンティクスと比較してください。完了には、局所的なコード変更ではなく、合意された設計が必要です。
索引モデルが issue の本文から書いたものです。
説明
The state of things
There are two key problems that we absolutely must solve to express any reasonable compilation scheme using this proposal.
- How do we support thread-local storage in a compilation scheme where all Wasm functions are marked shared?
- How can we call JS functions from shared Wasm functions?
Discussion recap
The initial draft of this proposal aimed to solve (1) with thread-local globals, and (2) with thread-local functions. However concerns around implementation feasibility were raised about both of these (https://github.com/WebAssembly/shared-everything-threads/discussions/34, https://github.com/WebAssembly/shared-everything-threads/issues/42).
In https://github.com/WebAssembly/shared-everything-threads/issues/42 @eqrion proposed a different approach, which (interpreted minimally) involves using function parameters/contexts to pass around a thread ID (allowing (1) to be handled programmatically) and a record of important JS functions in the current thread (allowing (2) to be handled through ref.call). The latter requires us to relax our interpretation of the shared annotation to allow nonshared reference parameters.
This alternate approach has implications for future shared continuations (work stealing) which I talk about here (https://github.com/WebAssembly/shared-everything-threads/issues/44). If we believe shared continuations will exist in the future, we will need both versions of shared at once - shared-fixed (allows non-shared params) and shared-suspendable (disallows as in our current semantics). The latter will need its own mechanism for solving (2), with all of the same constraints we're trying to avoid now. Moreover, we will need to work through the design for composing shared-fixed and shared-suspendable function calls, potentially requiring extra language features to facilitate this (such as the shared-barrier discussed in the issue).
Thesis
The more I work through the details of the above, the more I'm struck by the amount of future core language complication, and standardisation effort, that we could avoid if we can find an acceptable solution for (2) now that also works with the shared-suspendable semantics.
Therefore, I believe we should redouble our efforts towards this end. If we find a solution, we avoid a lot of future mess. For now, I'm happy to consider (1) "minimally solved" through the thread ID-passing strategy.
Some possible ways forward
These are to spark discussion. I hope people can brainstorm variations of these, or other fresh ideas.
Eval in current realm
Introduce into the JS-API a new function, which I'll call eval_realm for simplicity, importable as shared. This takes a shared externref, interpreted as a string, and calls the current JS realm's eval function on this string. As discussed with @syg, shared Wasm functions will need a per-realm prototype, so I think this per-realm dispatch can be semantically justified.
Through compilation scheme wizardry (such as creating an initial table of meaningful strings), arbitrary JS access can be bootstrapped from this function, although it would be quite slow! To make key functions faster, this could be combined with the below strategy, with eval_realm used as a fall-back to invoke arbitrary JS.
Make more JS builtins shared
In the spirit of @eqrion's string-builtins, expose a larger list of functions (such as Math.*) that are importable as shared. Possibly come up with some lightweight standards process to add additional functions. This has the bonus of clearly supporting inlining optimisations, but doesn't allow the execution of arbitrary user-defined JS, unless combined with the above eval_realm strategy.
Revisit thread-local functions
I still think a weak form (ref "flavor 2" here) of thread-local function could potentially be viable in the short-term. The semantics I envisage: the thread-local function's ephemeron in a thread is guaranteed not to be collected only so long as the thread-local function is rooted through purely non-shared references in the same thread. If the above ever becomes untrue, future calls to the function in this thread may non-deterministically trap.
There are two objections:
- this exposes implementation-specific behaviour due to the non-deterministic trapping if the function was ever eligible to be collected;
- this still requires some GC engineering, to ensure that the thread-local GC knows how to follow non-shared references up to the
sharedthread-local function, and handle the keeping-alive of the underlying ephemeron correctly in this case. My semantics above allows some simplifying assumptions:- there's no need to transitively walk the shared heap, only immediate peeks into it to find thread-local functions are needed;
- It's safe for the thread-local GC to walk into the thread-local function, since scary changes to the shared heap can only happen during a stop-the-world, which can't be carried out while the thread-local GC is taking place.
I believe the objections above could possibly be overcome if our alternative solutions are unattractive or require greater implementation effort, and through comparison to the implementation-specific behaviours already exposed through WeakRef and FinalizationRegistry.
If there are orthogonal concerns about the implementation complexity of thread-local function bind, it's possible to remove this function, creating a restricted form of thread-local function that can only be called in the thread in which it is first created/bound. Note the similarities to @syg's sketch for thread-bound JS objects. To facilitate "cross-thread" calls of (e.g.) console.log, the compilation scheme would need to create a thread-bound-wrapped console.log in each thread, and use the thread ID to dispatch to the correct one (so each call site for console.log in Wasm becomes a lookup in some table of thread-bound functions, based on the thread ID).
Thread-bound and weak thread-local functions still give us a forward-compatible path towards the "ideal" semantics. The former can be accomplished by re-introducing bind. The latter, through interpreting the strong semantics as turning the non-deterministic successes/failures of the weak semantics into deterministic successes.
- 主要言語
- WebAssembly
- スター
- 97
- フォーク
- 6
- PR マージ指標
- 30日以内にマージされた PR はありません
コントリビューションガイド
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
WebAssembly/shared-everything-threads のほかの issue
-
難易度 5/5 1週間以上 初心者へのやさしさ 35/100
WebAssembly/shared-everything-threads#119 · コメント 2 件 ·
-
難易度 5/5 1週間以上 初心者へのやさしさ 25/100
WebAssembly/shared-everything-threads#114 · コメント 7 件 ·
-
難易度 5/5 1週間以上 初心者へのやさしさ 25/100
-
難易度 5/5 1週間以上 初心者へのやさしさ 35/100
WebAssembly/shared-everything-threads#105 · コメント 6 件 ·
-
難易度 5/5 1週間以上 初心者へのやさしさ 25/100
WebAssembly/shared-everything-threads#99 · コメント 5 件 ·
WebAssembly/shared-everything-threads の issue をすべて見る
似ている issue
-
`String.Parser.chompUntilEndOr` leaves the column one short when it runs to the end past a newline オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
-
enhancement PyCDE
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
-
llvm:support
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
llvm/llvm-project#226296 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
-
難易度 2/5 1〜3時間 初心者へのやさしさ 88/100
abseil/abseil-cpp#2176 ·