SharedArrayBuffer
SharedArrayBuffer オブジェクトは、一般的な、生のバイナリーデータバッファーを表すために使用されます。ArrayBuffer オブジェクトと似ていますが、こちらは共有メモリー上にビューを生成するために使用されます。SharedArrayBuffer は移譲可能オブジェクトではありません。この点では ArrayBuffer が移譲可能であるのとは異なります。
解説
SharedArrayBuffer オブジェクトを使用して、クラスター内のあるエージェントから別のエージェント (エージェントとは、ウェブページのメインプログラムまたはそのウェブワーカーのひとつ) へ、SharedArrayBuffer オブジェクトを使用してメモリーを共有するために、postMessage と構造化複製を使用します。
構造化複製アルゴリズムは SharedArrayBuffer と、SharedArrayBuffer にマッピングされた型付き配列を受け入れます。どちらの場合も SharedArrayBuffer オブジェクトは受信者に転送されて、受信側のエージェントで新たなプライベートの SharedArrayBuffer オブジェクトになります(ArrayBuffer と同じように)。しかし、2 つの SharedArrayBuffer オブジェクトから参照される共有データブロックは同一のデータブロックであり、あるエージェントによるブロックへの副作用は、結果的に他方のエージェントからも見えます。
js
const sab = new SharedArrayBuffer(1024);
worker.postMessage(sab);
共有メモリーは、ワーカー内でもメインスレッド内でも同時に生成や更新ができます。システム (CPU、OS、ブラウザー) によっては、変更がすべてのコンテキストに通知されるまでに少々時間がかかります。同期するためには、不可分操作が必要です。
SharedArrayBuffer オブジェクトは、以下のように一部のウェブ API で使用されています。
セキュリティの要件
共有メモリーと高解像度タイマーは、Spectre の対策として 2018 年の初めに事実上無効化されました。 2020 年には、共有メモリーを再び有効にするために、新しい安全なアプローチが標準化されました。
基本的な要件として、文書が安全なコンテキストにある必要があります。
最上位の文書では、サイトにオリジン間の分離性を持たせるため、次の 2 つのヘッダーを設定する必要があります。
Cross-Origin-Opener-Policyでsame-originの値を指定すること(オリジンを攻撃者から守るため)Cross-Origin-Embedder-Policyでrequire-corpまたはcredentiallessの値を指定すること(被害者を自分のオリジンから守るため)
http
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
オリジン間の分離が成功したかどうかは、ウィンドウとワーカーのコンテキストで利用できる crossOriginIsolated (en-US) プロパティを使って確認することができます。
js
const myWorker = new Worker("worker.js");
if (crossOriginIsolated) {
const buffer = new SharedArrayBuffer(16);
myWorker.postMessage(buffer);
} else {
const buffer = new ArrayBuffer(16);
myWorker.postMessage(buffer);
}
これらの 2 つのヘッダーが設定されていた場合、 postMessage() は SharedArrayBuffer オブジェクトに例外を発生させなくなり、従ってスレッド間での共有メモリーが利用できるようになります。
入れ子の文書と専用ワーカーは同様に、 Cross-Origin-Embedder-Policy ヘッダーを同じ値で設定する必要があります。同一オリジンの入れ子の文書とサブリソースについては、これ以上の変更は必要ありません。同一サイト(ただし別オリジン)の入れ子の文書とサブリソースは、 Cross-Origin-Resource-Policy ヘッダーを same-site という値で設定する必要があります。そして、同様に別オリジン(かつ別サイト)のものは、 cross-origin を値として同じヘッダーを設定する必要があります。 Cross-Origin-Resource-Policy ヘッダーを same-origin 以外の値に設定すると、Spectre などの潜在的な攻撃にリソースがさらされることになることに注意してください。
Cross-Origin-Opener-Policy ヘッダーはポップアップへの参照を保持するための能力を制限していることに注意してください。2 つの最上位のウィンドウコンテキスト間の直接アクセスは、基本的に、同一オリジンであり、同じ 2 つの値を持つヘッダーを運んでいる場合にのみ動作するようになっています。
API の利用可能性
上記のセキュリティ対策の有無により、各種メモリー共有 API の利用可能性は異なります。
Atomicsオブジェクトは常に利用できます。SharedArrayBufferオブジェクトは原則として常に利用できますが、残念ながら、ウェブコンテンツとの互換性のために、上記の 2 つのヘッダーが設定されていない限り、グローバルオブジェクトのコンストラクターは隠されます。この制限は将来的に取り除かれることが期待されています。WebAssembly.Memoryはまだインスタンスを取得するために使用することができます。- 上記の 2 つのヘッダーが設定されていない限り、さまざまな
postMessage()API がSharedArrayBufferオブジェクトに対して例外を発生することになります。これらが設定された場合は、Windowオブジェクトと専用ワーカーのpostMessage()が機能し、メモリーを共有できるようになります。
WebAssembly の共有メモリー
WebAssembly.Memory オブジェクトは、コンストラクターの shared フラグで作成することができます。このフラグを true に設定すると、構築されたメモリーオブジェクトは SharedArrayBuffer と同様に postMessage() を通じてワーカー間で共有でき、メモリーオブジェクトの背後となる buffer は SharedArrayBuffer となります。したがって、ワーカー間で SharedArrayBuffer を共有するための上記の要件は、WebAssembly.Memory.Buffer を共有する場合にも当てはまります。
WebAssembly Threads の提案では、新しい不可分命令の集合も定義されています。SharedArrayBuffer とそのメソッドが無条件に有効であるように(そしてスレッド間の共有のみが新しいヘッダー上で制限されます)、WebAssembly の不可分命令も無条件に許可されます。
SharedArrayBuffer の成長
SharedArrayBuffer オブジェクトは SharedArrayBuffer() コンストラクターを呼び出す際に maxByteLength オプションを含めることで成長可能にすることができます。また、SharedArrayBuffer の growable (en-US) および maxByteLength (en-US) プロパティを参照すれば、そのサイズが成長可能かどうかを調べることが可能であり、最大サイズは何であるかがわかります。成長可能な SharedArrayBuffer には grow() (en-US) を呼び出して新しいサイズを割り当てることができます。新しいバイトは 0 に初期化されます。
これらの機能により、SharedArrayBuffer をより効率的に成長させることができます。そうしないと、新しいサイズのバッファーコピーを作成しなければなりません。また、この点において、JavaScript は WebAssembly と同等になります(WASM のリニアメモリーは WebAssembly.Memory.prototype.grow() でサイズを変更することができます)。
セキュリティ上の理由から、SharedArrayBuffer はサイズを縮小することはできませんが、大きくすることはできます。
コンストラクター
-
新しい
SharedArrayBufferオブジェクトを生成します。
インスタンスプロパティ
これらのプロパティは SharedArrayBuffer.prototype で定義されており、すべての SharedArrayBuffer インスタンスで共有されます。
-
配列のサイズ(バイト単位)。これは配列の構築時に設定され、
SharedArrayBufferが成長可能である場合にのみSharedArrayBuffer.prototype.grow()(en-US) メソッドを使用して変更することができます。 -
読み取り専用で、
SharedArrayBufferが成長できる最大長をバイト数で指定します。これは配列が構築される際に設定され、変更することはできません。 -
読み取り専用です。
SharedArrayBufferが成長可能な場合はtrueを、そうでない場合はfalseを返します。 -
インスタンスオブジェクトを作成したコンストラクター関数です。SharedArrayBuffer`インスタンスの場合、初期値は
SharedArrayBufferコンストラクターです。 -
@@toStringTagプロパティの初期値は文字列"SharedArrayBuffer"です。このプロパティはObject.prototype.toString()で使用されます。
インスタンスメソッド
-
新しい
SharedArrayBufferを作成し、その中身をこのSharedArrayBufferのbeginの位置からendの位置の一つ手前までのバイトをコピーして返します。beginまたはendが負の数の場合は、配列の先頭からではなく末尾からの位置で参照します。 -
SharedArrayBufferを指定したサイズ(バイト単位)まで成長させる。
例
新しい SharedArrayBuffer の生成
js
const sab = new SharedArrayBuffer(1024);
SharedArrayBuffer の分割
js
sab.slice(); // SharedArrayBuffer { byteLength: 1024 }
sab.slice(2); // SharedArrayBuffer { byteLength: 1022 }
sab.slice(-2); // SharedArrayBuffer { byteLength: 2 }
sab.slice(0, 1); // SharedArrayBuffer { byteLength: 1 }
WebGL バッファー内での使用
js
const canvas = document.querySelector("canvas");
const gl = canvas.getContext("webgl");
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, sab, gl.STATIC_DRAW);
仕様書
| Specification |
|---|
| ECMAScript Language Specification # sec-sharedarraybuffer-objects |
ブラウザーの互換性
BCD tables only load in the browser
関連情報
AtomicsArrayBuffer- JavaScript 型付き配列
- ウェブワーカー
- parlib-simple – 同期と作業分配抽象化を提供するシンプルなライブラリーです。
- 共有メモリー – 簡潔なチュートリアル
- JavaScript の並列処理機能を味見してみる
- COOP and COEP explained.
Cross-Origin-Opener-Policy: whatwg/html issue #3740, draft specification.Cross-Origin-Embedder-Policy: whatwg/html issue #4175, draft specification.Cross-Origin-Resource-Policy: Fetch で標準化され、新しいcross-origin値がCross-Origin-Embedder-Policyの効果の一部になります。postMessage()の変更とself.crossOriginIsolated(en-US): whatwg/html issue #4732, whatwg/html issue #4872, draft specification.- SharedArrayBuffer updates in Android Chrome 88 and Desktop Chrome 92