SharedArrayBuffer
Baseline Widely available
This feature is well established and works across many devices and browser versions. It’s been available across browsers since December 2021.
SharedArrayBuffer
オブジェクトは、一般的な、生のバイナリーデータバッファーを表すために使用されます。ArrayBuffer
オブジェクトと似ていますが、こちらは共有メモリー上にビューを生成するために使用されます。SharedArrayBuffer
は移譲可能オブジェクトではありません。この点では ArrayBuffer
が移譲可能であるのとは異なります。
解説
SharedArrayBuffer
オブジェクトを使用して、クラスター内のあるエージェントから別のエージェント (エージェントとは、ウェブページのメインプログラムまたはそのウェブワーカーのひとつ) へ、SharedArrayBuffer
オブジェクトを使用してメモリーを共有するために、postMessage
と構造化複製を使用します。
構造化複製アルゴリズムは SharedArrayBuffer
と、SharedArrayBuffer
にマッピングされた型付き配列を受け入れます。どちらの場合も SharedArrayBuffer
オブジェクトは受信者に転送されて、受信側のエージェントで新たなプライベートの SharedArrayBuffer
オブジェクトになります(ArrayBuffer
と同じように)。しかし、2 つの SharedArrayBuffer
オブジェクトから参照される共有データブロックは同一のデータブロックであり、あるエージェントによるブロックへの副作用は、結果的に他方のエージェントからも見えます。
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
の値を指定すること(被害者を自分のオリジンから守るため)
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
オリジン間の分離が成功したかどうかは、ウィンドウとワーカーのコンテキストで利用できる crossOriginIsolated
プロパティを使って確認することができます。
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
および maxByteLength
プロパティを参照すれば、そのサイズが成長可能かどうかを調べることが可能であり、最大サイズは何であるかがわかります。成長可能な SharedArrayBuffer
には grow()
を呼び出して新しいサイズを割り当てることができます。新しいバイトは 0 に初期化されます。
これらの機能により、SharedArrayBuffer
をより効率的に成長させることができます。そうしないと、新しいサイズのバッファーコピーを作成しなければなりません。また、この点において、JavaScript は WebAssembly と同等になります(WASM のリニアメモリーは WebAssembly.Memory.prototype.grow()
でサイズを変更することができます)。
セキュリティ上の理由から、SharedArrayBuffer
はサイズを縮小することはできませんが、大きくすることはできます。
コンストラクター
-
新しい
SharedArrayBuffer
オブジェクトを生成します。
インスタンスプロパティ
これらのプロパティは SharedArrayBuffer.prototype
で定義されており、すべての SharedArrayBuffer
インスタンスで共有されます。
-
配列のサイズ(バイト単位)。これは配列の構築時に設定され、
SharedArrayBuffer
が成長可能である場合にのみSharedArrayBuffer.prototype.grow()
メソッドを使用して変更することができます。 -
読み取り専用で、
SharedArrayBuffer
が成長できる最大長をバイト数で指定します。これは配列が構築される際に設定され、変更することはできません。 -
読み取り専用です。
SharedArrayBuffer
が成長可能な場合はtrue
を、そうでない場合はfalse
を返します。 -
インスタンスオブジェクトを作成したコンストラクター関数です。SharedArrayBuffer`インスタンスの場合、初期値は
SharedArrayBuffer
コンストラクターです。 -
@@toStringTag
プロパティの初期値は文字列"SharedArrayBuffer"
です。このプロパティはObject.prototype.toString()
で使用されます。
インスタンスメソッド
-
新しい
SharedArrayBuffer
を作成し、その中身をこのSharedArrayBuffer
のbegin
の位置からend
の位置の一つ手前までのバイトをコピーして返します。begin
またはend
が負の数の場合は、配列の先頭からではなく末尾からの位置で参照します。 -
SharedArrayBuffer
を指定したサイズ(バイト単位)まで成長させる。
例
新しい SharedArrayBuffer の生成
const sab = new SharedArrayBuffer(1024);
SharedArrayBuffer の分割
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 バッファー内での使用
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
関連情報
Atomics
ArrayBuffer
- 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
: whatwg/html issue #4732, whatwg/html issue #4872, draft specification.- SharedArrayBuffer updates in Android Chrome 88 and Desktop Chrome 92