UNB/ CS/ David Bremner/ teaching/ cs2613/ books/ mdn/ Reference/ Global Objects/ SharedArrayBuffer

The SharedArrayBuffer object is used to represent a generic raw binary data buffer, similar to the ArrayBuffer object, but in a way that they can be used to create views on shared memory. A SharedArrayBuffer is not a Transferable Object, unlike an ArrayBuffer which is transferable.

Description

To share memory using SharedArrayBuffer objects from one agent in the cluster to another (an agent is either the web page's main program or one of its web workers), postMessage and structured cloning is used.

The structured clone algorithm accepts SharedArrayBuffer objects and typed arrays mapped onto SharedArrayBuffer objects. In both cases, the SharedArrayBuffer object is transmitted to the receiver resulting in a new, private SharedArrayBuffer object in the receiving agent (just as for ArrayBuffer). However, the shared data block referenced by the two SharedArrayBuffer objects is the same data block, and a side effect to the block in one agent will eventually become visible in the other agent.

const sab = new SharedArrayBuffer(1024);
worker.postMessage(sab);

Shared memory can be created and updated simultaneously in workers or the main thread. Depending on the system (the CPU, the OS, the Browser) it can take a while until the change is propagated to all contexts. To synchronize, atomic operations are needed.

SharedArrayBuffer objects are used by some web APIs, such as:

Security requirements

Shared memory and high-resolution timers were effectively disabled at the start of 2018 in light of Spectre. In 2020, a new, secure approach has been standardized to re-enable shared memory.

As a baseline requirement, your document needs to be in a secure context.

For top-level documents, two headers need to be set to cross-origin isolate your site:

Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp

To check if cross origin isolation has been successful, you can test against the crossOriginIsolated property available to window and worker contexts:

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);
}

With these two headers set, postMessage() no longer throws for SharedArrayBuffer objects and shared memory across threads is therefore available.

Nested documents and dedicated workers need to set the Cross-Origin-Embedder-Policy header as well, with the same value. No further changes are needed for same-origin nested documents and subresources. Same-site (but cross-origin) nested documents and subresources need to set the Cross-Origin-Resource-Policy header with same-site as value. And their cross-origin (and cross-site) counterparts need to set the same header with cross-origin as value. Note that setting the Cross-Origin-Resource-Policy header to any other value than same-origin opens up the resource to potential attacks, such as Spectre.

Note that the Cross-Origin-Opener-Policy header limits your ability to retain a reference to popups. Direct access between two top-level window contexts essentially only work if they are same-origin and carry the same two headers with the same two values.

API availability

Depending on whether the above security measures are taken, the various memory-sharing APIs have different availabilities:

WebAssembly shared memory

WebAssembly.Memory objects can be created with the shared constructor flag. When this flag is set to true, the constructed Memory object can be shared between workers via postMessage(), just like SharedArrayBuffer, and the backing buffer of the Memory object is a SharedArrayBuffer. Therefore, the requirements listed above for sharing a SharedArrayBuffer between workers also apply to sharing a WebAssembly.Memory.

The WebAssembly Threads proposal also defines a new set of atomic instructions. Just as SharedArrayBuffer and its methods are unconditionally enabled (and only sharing between threads is gated on the new headers), the WebAssembly atomic instructions are also unconditionally allowed.

Growing SharedArrayBuffers

SharedArrayBuffer objects can be made growable by including the maxByteLength option when calling the SharedArrayBuffer() constructor. You can query whether a SharedArrayBuffer is growable and what its maximum size is by accessing its growable and maxByteLength properties, respectively. You can assign a new size to a growable SharedArrayBuffer with a grow() call. New bytes are initialized to 0.

These features make growing SharedArrayBuffers more efficient — otherwise, you have to make a copy of the buffer with a new size. It also gives JavaScript parity with WebAssembly in this regard (Wasm linear memory can be resized with WebAssembly.Memory.prototype.grow()).

For security reasons, SharedArrayBuffers cannot be reduced in size, only grown.

Constructor

Static properties

Instance properties

These properties are defined on SharedArrayBuffer.prototype and shared by all SharedArrayBuffer instances.

Instance methods

Examples

Creating a new SharedArrayBuffer

const sab = new SharedArrayBuffer(1024);

Slicing the 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 }

Using it in a WebGL buffer

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);

Specifications

Browser compatibility

See also