ECMAScript® 2024 Language Specification

Draft ECMA-262 / February 15, 2024

25.2 SharedArrayBuffer Objects

25.2.1 Fixed-length and Growable SharedArrayBuffer Objects

A fixed-length SharedArrayBuffer is a SharedArrayBuffer whose byte length cannot change after creation.

A growable SharedArrayBuffer is a SharedArrayBuffer whose byte length may increase after creation via calls to SharedArrayBuffer.prototype.grow ( newLength ).

The kind of SharedArrayBuffer object that is created depends on the arguments passed to SharedArrayBuffer ( length [ , options ] ).

25.2.2 Abstract Operations for SharedArrayBuffer Objects

25.2.2.1 AllocateSharedArrayBuffer ( constructor, byteLength [ , maxByteLength ] )

The abstract operation AllocateSharedArrayBuffer takes arguments constructor (a constructor) and byteLength (a non-negative integer) and optional argument maxByteLength (a non-negative integer or empty) and returns either a normal completion containing a SharedArrayBuffer or a throw completion. It is used to create a SharedArrayBuffer. It performs the following steps when called:

  1. Let slots be « [[ArrayBufferData]] ».
  2. If maxByteLength is present and maxByteLength is not empty, let allocatingGrowableBuffer be true; otherwise let allocatingGrowableBuffer be false.
  3. If allocatingGrowableBuffer is true, then
    1. If byteLength > maxByteLength, throw a RangeError exception.
    2. Append [[ArrayBufferByteLengthData]] and [[ArrayBufferMaxByteLength]] to slots.
  4. Else,
    1. Append [[ArrayBufferByteLength]] to slots.
  5. Let obj be ? OrdinaryCreateFromConstructor(constructor, "%SharedArrayBuffer.prototype%", slots).
  6. If allocatingGrowableBuffer is true, let allocLength be maxByteLength; otherwise let allocLength be byteLength.
  7. Let block be ? CreateSharedByteDataBlock(allocLength).
  8. Set obj.[[ArrayBufferData]] to block.
  9. If allocatingGrowableBuffer is true, then
    1. Assert: byteLengthmaxByteLength.
    2. Let byteLengthBlock be ? CreateSharedByteDataBlock(8).
    3. Perform SetValueInBuffer(byteLengthBlock, 0, biguint64, (byteLength), true, seq-cst).
    4. Set obj.[[ArrayBufferByteLengthData]] to byteLengthBlock.
    5. Set obj.[[ArrayBufferMaxByteLength]] to maxByteLength.
  10. Else,
    1. Set obj.[[ArrayBufferByteLength]] to byteLength.
  11. Return obj.

25.2.2.2 IsSharedArrayBuffer ( obj )

The abstract operation IsSharedArrayBuffer takes argument obj (an ArrayBuffer or a SharedArrayBuffer) and returns a Boolean. It tests whether an object is an ArrayBuffer, a SharedArrayBuffer, or a subtype of either. It performs the following steps when called:

  1. Let bufferData be obj.[[ArrayBufferData]].
  2. If bufferData is null, return false.
  3. If bufferData is a Data Block, return false.
  4. Assert: bufferData is a Shared Data Block.
  5. Return true.

25.2.2.3 HostGrowSharedArrayBuffer ( buffer, newByteLength )

The host-defined abstract operation HostGrowSharedArrayBuffer takes arguments buffer (a SharedArrayBuffer) and newByteLength (a non-negative integer) and returns either a normal completion containing either handled or unhandled, or a throw completion. It gives the host an opportunity to perform implementation-defined growing of buffer. If the host chooses not to handle growing of buffer, it may return unhandled for the default behaviour.

The implementation of HostGrowSharedArrayBuffer must conform to the following requirements:

  • If the abstract operation does not complete normally with unhandled, and newByteLength < the current byte length of the buffer or newByteLength > buffer.[[ArrayBufferMaxByteLength]], throw a RangeError exception.
  • Let isLittleEndian be the value of the [[LittleEndian]] field of the surrounding agent's Agent Record. If the abstract operation completes normally with handled, a WriteSharedMemory or ReadModifyWriteSharedMemory event whose [[Order]] is seq-cst, [[Payload]] is NumericToRawBytes(biguint64, newByteLength, isLittleEndian), [[Block]] is buffer.[[ArrayBufferByteLengthData]], [[ByteIndex]] is 0, and [[ElementSize]] is 8 is added to the surrounding agent's candidate execution such that racing calls to SharedArrayBuffer.prototype.grow are not "lost", i.e. silently do nothing.
Note

The second requirement above is intentionally vague about how or when the current byte length of buffer is read. Because the byte length must be updated via an atomic read-modify-write operation on the underlying hardware, architectures that use load-link/store-conditional or load-exclusive/store-exclusive instruction pairs may wish to keep the paired instructions close in the instruction stream. As such, SharedArrayBuffer.prototype.grow itself does not perform bounds checking on newByteLength before calling HostGrowSharedArrayBuffer, nor is there a requirement on when the current byte length is read.

This is in contrast with HostResizeArrayBuffer, which is guaranteed that the value of newByteLength is ≥ 0 and ≤ buffer.[[ArrayBufferMaxByteLength]].

The default implementation of HostGrowSharedArrayBuffer is to return NormalCompletion(unhandled).

25.2.3 The SharedArrayBuffer Constructor

The SharedArrayBuffer constructor:

  • is %SharedArrayBuffer%.
  • is the initial value of the "SharedArrayBuffer" property of the global object, if that property is present (see below).
  • creates and initializes a new SharedArrayBuffer when called as a constructor.
  • is not intended to be called as a function and will throw an exception when called in that manner.
  • may be used as the value of an extends clause of a class definition. Subclass constructors that intend to inherit the specified SharedArrayBuffer behaviour must include a super call to the SharedArrayBuffer constructor to create and initialize subclass instances with the internal state necessary to support the SharedArrayBuffer.prototype built-in methods.

Whenever a host does not provide concurrent access to SharedArrayBuffers it may omit the "SharedArrayBuffer" property of the global object.

Note

Unlike an ArrayBuffer, a SharedArrayBuffer cannot become detached, and its internal [[ArrayBufferData]] slot is never null.

25.2.3.1 SharedArrayBuffer ( length [ , options ] )

This function performs the following steps when called:

  1. If NewTarget is undefined, throw a TypeError exception.
  2. Let byteLength be ? ToIndex(length).
  3. Let requestedMaxByteLength be ? GetArrayBufferMaxByteLengthOption(options).
  4. Return ? AllocateSharedArrayBuffer(NewTarget, byteLength, requestedMaxByteLength).

25.2.4 Properties of the SharedArrayBuffer Constructor

The SharedArrayBuffer constructor:

  • has a [[Prototype]] internal slot whose value is %Function.prototype%.
  • has the following properties:

25.2.4.1 SharedArrayBuffer.prototype

The initial value of SharedArrayBuffer.prototype is the SharedArrayBuffer prototype object.

This property has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }.

25.2.4.2 get SharedArrayBuffer [ @@species ]

SharedArrayBuffer[@@species] is an accessor property whose set accessor function is undefined. Its get accessor function performs the following steps when called:

  1. Return the this value.

The value of the "name" property of this function is "get [Symbol.species]".

25.2.5 Properties of the SharedArrayBuffer Prototype Object

The SharedArrayBuffer prototype object:

  • is %SharedArrayBuffer.prototype%.
  • has a [[Prototype]] internal slot whose value is %Object.prototype%.
  • is an ordinary object.
  • does not have an [[ArrayBufferData]] or [[ArrayBufferByteLength]] internal slot.

25.2.5.1 get SharedArrayBuffer.prototype.byteLength

SharedArrayBuffer.prototype.byteLength is an accessor property whose set accessor function is undefined. Its get accessor function performs the following steps when called:

  1. Let O be the this value.
  2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]).
  3. If IsSharedArrayBuffer(O) is false, throw a TypeError exception.
  4. Let length be ArrayBufferByteLength(O, seq-cst).
  5. Return 𝔽(length).

25.2.5.2 SharedArrayBuffer.prototype.constructor

The initial value of SharedArrayBuffer.prototype.constructor is %SharedArrayBuffer%.

25.2.5.3 SharedArrayBuffer.prototype.grow ( newLength )

This method performs the following steps when called:

  1. Let O be the this value.
  2. Perform ? RequireInternalSlot(O, [[ArrayBufferMaxByteLength]]).
  3. If IsSharedArrayBuffer(O) is false, throw a TypeError exception.
  4. Let newByteLength be ? ToIndex(newLength).
  5. Let hostHandled be ? HostGrowSharedArrayBuffer(O, newByteLength).
  6. If hostHandled is handled, return undefined.
  7. Let isLittleEndian be the value of the [[LittleEndian]] field of the surrounding agent's Agent Record.
  8. Let byteLengthBlock be O.[[ArrayBufferByteLengthData]].
  9. Let currentByteLengthRawBytes be GetRawBytesFromSharedBlock(byteLengthBlock, 0, biguint64, true, seq-cst).
  10. Let newByteLengthRawBytes be NumericToRawBytes(biguint64, (newByteLength), isLittleEndian).
  11. Repeat,
    1. NOTE: This is a compare-and-exchange loop to ensure that parallel, racing grows of the same buffer are totally ordered, are not lost, and do not silently do nothing. The loop exits if it was able to attempt to grow uncontended.
    2. Let currentByteLength be (RawBytesToNumeric(biguint64, currentByteLengthRawBytes, isLittleEndian)).
    3. If newByteLength = currentByteLength, return undefined.
    4. If newByteLength < currentByteLength or newByteLength > O.[[ArrayBufferMaxByteLength]], throw a RangeError exception.
    5. Let byteLengthDelta be newByteLength - currentByteLength.
    6. If it is impossible to create a new Shared Data Block value consisting of byteLengthDelta bytes, throw a RangeError exception.
    7. NOTE: No new Shared Data Block is constructed and used here. The observable behaviour of growable SharedArrayBuffers is specified by allocating a max-sized Shared Data Block at construction time, and this step captures the requirement that implementations that run out of memory must throw a RangeError.
    8. Let readByteLengthRawBytes be AtomicCompareExchangeInSharedBlock(byteLengthBlock, 0, 8, currentByteLengthRawBytes, newByteLengthRawBytes).
    9. If ByteListEqual(readByteLengthRawBytes, currentByteLengthRawBytes) is true, return undefined.
    10. Set currentByteLengthRawBytes to readByteLengthRawBytes.
Note

Spurious failures of the compare-exchange to update the length are prohibited. If the bounds checking for the new length passes and the implementation is not out of memory, a ReadModifyWriteSharedMemory event (i.e. a successful compare-exchange) is always added into the candidate execution.

Parallel calls to SharedArrayBuffer.prototype.grow are totally ordered. For example, consider two racing calls: sab.grow(10) and sab.grow(20). One of the two calls is guaranteed to win the race. The call to sab.grow(10) will never shrink sab even if sab.grow(20) happened first; in that case it will instead throw a RangeError.

25.2.5.4 get SharedArrayBuffer.prototype.growable

SharedArrayBuffer.prototype.growable is an accessor property whose set accessor function is undefined. Its get accessor function performs the following steps when called:

  1. Let O be the this value.
  2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]).
  3. If IsSharedArrayBuffer(O) is false, throw a TypeError exception.
  4. If IsFixedLengthArrayBuffer(O) is false, return true; otherwise return false.

25.2.5.5 get SharedArrayBuffer.prototype.maxByteLength

SharedArrayBuffer.prototype.maxByteLength is an accessor property whose set accessor function is undefined. Its get accessor function performs the following steps when called:

  1. Let O be the this value.
  2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]).
  3. If IsSharedArrayBuffer(O) is false, throw a TypeError exception.
  4. If IsFixedLengthArrayBuffer(O) is true, then
    1. Let length be O.[[ArrayBufferByteLength]].
  5. Else,
    1. Let length be O.[[ArrayBufferMaxByteLength]].
  6. Return 𝔽(length).

25.2.5.6 SharedArrayBuffer.prototype.slice ( start, end )

This method performs the following steps when called:

  1. Let O be the this value.
  2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]).
  3. If IsSharedArrayBuffer(O) is false, throw a TypeError exception.
  4. Let len be ArrayBufferByteLength(O, seq-cst).
  5. Let relativeStart be ? ToIntegerOrInfinity(start).
  6. If relativeStart = -∞, let first be 0.
  7. Else if relativeStart < 0, let first be max(len + relativeStart, 0).
  8. Else, let first be min(relativeStart, len).
  9. If end is undefined, let relativeEnd be len; else let relativeEnd be ? ToIntegerOrInfinity(end).
  10. If relativeEnd = -∞, let final be 0.
  11. Else if relativeEnd < 0, let final be max(len + relativeEnd, 0).
  12. Else, let final be min(relativeEnd, len).
  13. Let newLen be max(final - first, 0).
  14. Let ctor be ? SpeciesConstructor(O, %SharedArrayBuffer%).
  15. Let new be ? Construct(ctor, « 𝔽(newLen) »).
  16. Perform ? RequireInternalSlot(new, [[ArrayBufferData]]).
  17. If IsSharedArrayBuffer(new) is false, throw a TypeError exception.
  18. If new.[[ArrayBufferData]] is O.[[ArrayBufferData]], throw a TypeError exception.
  19. If ArrayBufferByteLength(new, seq-cst) < newLen, throw a TypeError exception.
  20. Let fromBuf be O.[[ArrayBufferData]].
  21. Let toBuf be new.[[ArrayBufferData]].
  22. Perform CopyDataBlockBytes(toBuf, 0, fromBuf, first, newLen).
  23. Return new.

25.2.5.7 SharedArrayBuffer.prototype [ @@toStringTag ]

The initial value of the @@toStringTag property is the String value "SharedArrayBuffer".

This property has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.

25.2.6 Properties of SharedArrayBuffer Instances

SharedArrayBuffer instances inherit properties from the SharedArrayBuffer prototype object. SharedArrayBuffer instances each have an [[ArrayBufferData]] internal slot. SharedArrayBuffer instances which are not growable each have an [[ArrayBufferByteLength]] internal slot. SharedArrayBuffer instances which are growable each have an [[ArrayBufferByteLengthData]] internal slot and an [[ArrayBufferMaxByteLength]] internal slot.

Note

SharedArrayBuffer instances, unlike ArrayBuffer instances, are never detached.

25.2.7 Growable SharedArrayBuffer Guidelines

Note 1

The following are guidelines for ECMAScript programmers working with growable SharedArrayBuffer.

We recommend that programs be tested in their deployment environments where possible. The amount of available physical memory differ greatly between hardware devices. Similarly, virtual memory subsystems also differ greatly between hardware devices as well as operating systems. An application that runs without out-of-memory errors on a 64-bit desktop web browser could run out of memory on a 32-bit mobile web browser.

When choosing a value for the "maxByteLength" option for growable SharedArrayBuffer, we recommend that the smallest possible size for the application be chosen. We recommend that "maxByteLength" does not exceed 1073741824, or 1GiB.

Please note that successfully constructing a growable SharedArrayBuffer for a particular maximum size does not guarantee that future grows will succeed.

Not all loads of a growable SharedArrayBuffer's length are synchronizing seq-cst loads. Loads of the length that are for bounds-checking of an integer-indexed property access, e.g. u8[idx], are not synchronizing. In general, in the absence of explicit synchronization, one property access being in-bound does not imply a subsequent property access in the same agent is also in-bound. In contrast, explicit loads of the length via the length and byteLength getters on SharedArrayBuffer, %TypedArray%.prototype, and DataView.prototype are synchronizing. Loads of the length that are performed by built-in methods to check if a TypedArray is entirely out-of-bounds are also synchronizing.

Note 2

The following are guidelines for ECMAScript implementers implementing growable SharedArrayBuffer.

We recommend growable SharedArrayBuffer be implemented as in-place growth via reserving virtual memory up front.

Because grow operations can happen in parallel with memory accesses on a growable SharedArrayBuffer, the constraints of the memory model require that even unordered accesses do not "tear" (bits of their values will not be mixed). In practice, this means the underlying data block of a growable SharedArrayBuffer cannot be grown by being copied without stopping the world. We do not recommend stopping the world as an implementation strategy because it introduces a serialization point and is slow.

Grown memory must appear zeroed from the moment of its creation, including to any racy accesses in parallel. This can be accomplished via zero-filled-on-demand virtual memory pages, or careful synchronization if manually zeroing memory.

Integer-indexed property access on TypedArray views of growable SharedArrayBuffers is intended to be optimizable similarly to access on TypedArray views of non-growable SharedArrayBuffers, because integer-indexed property loads on are not synchronizing on the underlying buffer's length (see programmer guidelines above). For example, bounds checks for property accesses may still be hoisted out of loops.

In practice it is difficult to implement growable SharedArrayBuffer by copying on hosts that do not have virtual memory, such as those running on embedded devices without an MMU. Memory usage behaviour of growable SharedArrayBuffers on such hosts may significantly differ from that of hosts with virtual memory. Such hosts should clearly communicate memory usage expectations to users.