Skip to content

Commit

Permalink
Normative: Allow Atomics methods to work on ArrayBuffers (#1908)
Browse files Browse the repository at this point in the history
Allow Atomics methods to work on ArrayBuffers in a fully deterministic
fashion. Atomics.wait still throws when used on ArrayBuffers, while
Atomics.notify always returns 0.
  • Loading branch information
syg authored and ljharb committed Aug 11, 2020
1 parent 29fedc2 commit ac0740b
Showing 1 changed file with 36 additions and 24 deletions.
60 changes: 36 additions & 24 deletions spec.html
Original file line number Diff line number Diff line change
Expand Up @@ -37437,7 +37437,7 @@ <h1>GetValueFromBuffer ( _arrayBuffer_, _byteIndex_, _type_, _isTypedArray_, _or
1. Let _readEvent_ be ReadSharedMemory { [[Order]]: _order_, [[NoTear]]: _noTear_, [[Block]]: _block_, [[ByteIndex]]: _byteIndex_, [[ElementSize]]: _elementSize_ }.
1. Append _readEvent_ to _eventList_.
1. Append Chosen Value Record { [[Event]]: _readEvent_, [[ChosenValue]]: _rawValue_ } to _execution_.[[ChosenValues]].
1. Else, let _rawValue_ be a List of _elementSize_ containing, in order, the _elementSize_ sequence of bytes starting with _block_[_byteIndex_].
1. Else, let _rawValue_ be a List of size _elementSize_ containing the sequence of _elementSize_ bytes starting with _block_[_byteIndex_].
1. If _isLittleEndian_ is not present, set _isLittleEndian_ to the value of the [[LittleEndian]] field of the surrounding agent's Agent Record.
1. Return RawBytesToNumeric(_type_, _rawValue_, _isLittleEndian_).
</emu-alg>
Expand Down Expand Up @@ -37487,23 +37487,28 @@ <h1>SetValueInBuffer ( _arrayBuffer_, _byteIndex_, _type_, _value_, _isTypedArra

<emu-clause id="sec-getmodifysetvalueinbuffer" aoid="GetModifySetValueInBuffer">
<h1>GetModifySetValueInBuffer ( _arrayBuffer_, _byteIndex_, _type_, _value_, _op_ [ , _isLittleEndian_ ] )</h1>
<p>The abstract operation GetModifySetValueInBuffer takes arguments _arrayBuffer_ (a SharedArrayBuffer object), _byteIndex_ (a non-negative integer), _type_ (a TypedArray element type), _value_ (a Number or a BigInt), and _op_ (a read-modify-write modification function) and optional argument _isLittleEndian_ (a Boolean). It performs the following steps when called:</p>
<p>The abstract operation GetModifySetValueInBuffer takes arguments _arrayBuffer_ (an ArrayBuffer object or a SharedArrayBuffer object), _byteIndex_ (a non-negative integer), _type_ (a TypedArray element type), _value_ (a Number or a BigInt), and _op_ (a read-modify-write modification function) and optional argument _isLittleEndian_ (a Boolean). It performs the following steps when called:</p>
<emu-alg>
1. Assert: IsSharedArrayBuffer(_arrayBuffer_) is *true*.
1. Assert: IsDetachedBuffer(_arrayBuffer_) is *false*.
1. Assert: There are sufficient bytes in _arrayBuffer_ starting at _byteIndex_ to represent a value of _type_.
1. Assert: ! IsNonNegativeInteger(_byteIndex_) is *true*.
1. Assert: Type(_value_) is BigInt if ! IsBigIntElementType(_type_) is *true*; otherwise, Type(_value_) is Number.
1. Let _block_ be _arrayBuffer_.[[ArrayBufferData]].
1. Let _elementSize_ be the Element Size value specified in <emu-xref href="#table-the-typedarray-constructors"></emu-xref> for Element Type _type_.
1. If _isLittleEndian_ is not present, set _isLittleEndian_ to the value of the [[LittleEndian]] field of the surrounding agent's Agent Record.
1. Let _rawBytes_ be NumericToRawBytes(_type_, _value_, _isLittleEndian_).
1. Let _execution_ be the [[CandidateExecution]] field of the surrounding agent's Agent Record.
1. Let _eventList_ be the [[EventList]] field of the element in _execution_.[[EventsRecords]] whose [[AgentSignifier]] is AgentSignifier().
1. Let _rawBytesRead_ be a List of length _elementSize_ of nondeterministically chosen byte values.
1. NOTE: In implementations, _rawBytesRead_ is the result of a load-link, of a load-exclusive, or of an operand of a read-modify-write instruction on the underlying hardware. The nondeterminism is a semantic prescription of the memory model to describe observable behaviour of hardware with weak consistency.
1. Let _rmwEvent_ be ReadModifyWriteSharedMemory { [[Order]]: ~SeqCst~, [[NoTear]]: *true*, [[Block]]: _block_, [[ByteIndex]]: _byteIndex_, [[ElementSize]]: _elementSize_, [[Payload]]: _rawBytes_, [[ModifyOp]]: _op_ }.
1. Append _rmwEvent_ to _eventList_.
1. Append Chosen Value Record { [[Event]]: _rmwEvent_, [[ChosenValue]]: _rawBytesRead_ } to _execution_.[[ChosenValues]].
1. If IsSharedArrayBuffer(_arrayBuffer_) is *true*, then
1. Let _execution_ be the [[CandidateExecution]] field of the surrounding agent's Agent Record.
1. Let _eventList_ be the [[EventList]] field of the element in _execution_.[[EventsRecords]] whose [[AgentSignifier]] is AgentSignifier().
1. Let _rawBytesRead_ be a List of length _elementSize_ of nondeterministically chosen byte values.
1. NOTE: In implementations, _rawBytesRead_ is the result of a load-link, of a load-exclusive, or of an operand of a read-modify-write instruction on the underlying hardware. The nondeterminism is a semantic prescription of the memory model to describe observable behaviour of hardware with weak consistency.
1. Let _rmwEvent_ be ReadModifyWriteSharedMemory { [[Order]]: ~SeqCst~, [[NoTear]]: *true*, [[Block]]: _block_, [[ByteIndex]]: _byteIndex_, [[ElementSize]]: _elementSize_, [[Payload]]: _rawBytes_, [[ModifyOp]]: _op_ }.
1. Append _rmwEvent_ to _eventList_.
1. Append Chosen Value Record { [[Event]]: _rmwEvent_, [[ChosenValue]]: _rawBytesRead_ } to _execution_.[[ChosenValues]].
1. Else,
1. Let _rawBytesRead_ be a List of size _elementSize_ containing the sequence of _elementSize_ bytes starting with _block_[_byteIndex_].
1. Let _rawBytesModified_ be _op_(_rawBytesRead_, _rawBytes_).
1. Store the individual bytes of _rawBytesModified_ into _block_, in order, starting at _block_[_byteIndex_].
1. Return RawBytesToNumeric(_type_, _rawBytesRead_, _isLittleEndian_).
</emu-alg>
</emu-clause>
Expand Down Expand Up @@ -38176,21 +38181,18 @@ <h1>The Atomics Object</h1>
<emu-clause id="sec-abstract-operations-for-atomics">
<h1>Abstract Operations for Atomics</h1>

<emu-clause id="sec-validatesharedintegertypedarray" aoid="ValidateSharedIntegerTypedArray">
<h1>ValidateSharedIntegerTypedArray ( _typedArray_ [ , _waitable_ ] )</h1>
<p>The abstract operation ValidateSharedIntegerTypedArray takes argument _typedArray_ and optional argument _waitable_ (a Boolean). It performs the following steps when called:</p>
<emu-clause id="sec-validateintegertypedarray" aoid="ValidateIntegerTypedArray" oldid="sec-validatesharedintegertypedarray">
<h1>ValidateIntegerTypedArray ( _typedArray_ [ , _waitable_ ] )</h1>
<p>The abstract operation ValidateIntegerTypedArray takes argument _typedArray_ and optional argument _waitable_ (a Boolean). It performs the following steps when called:</p>
<emu-alg>
1. If _waitable_ is not present, set _waitable_ to *false*.
1. Perform ? RequireInternalSlot(_typedArray_, [[TypedArrayName]]).
1. Let _buffer_ be ? ValidateTypedArray(_typedArray_).
1. Let _typeName_ be _typedArray_.[[TypedArrayName]].
1. Let _type_ be the Element Type value in <emu-xref href="#table-the-typedarray-constructors"></emu-xref> for _typeName_.
1. If _waitable_ is *true*, then
1. If _typeName_ is not *"Int32Array"* or *"BigInt64Array"*, throw a *TypeError* exception.
1. Else,
1. If ! IsUnclampedIntegerElementType(_type_) is *false* and ! IsBigIntElementType(_type_) is *false*, throw a *TypeError* exception.
1. Assert: _typedArray_ has a [[ViewedArrayBuffer]] internal slot.
1. Let _buffer_ be _typedArray_.[[ViewedArrayBuffer]].
1. If IsSharedArrayBuffer(_buffer_) is *false*, throw a *TypeError* exception.
1. Return _buffer_.
</emu-alg>
</emu-clause>
Expand All @@ -38200,8 +38202,8 @@ <h1>ValidateAtomicAccess ( _typedArray_, _requestIndex_ )</h1>
<p>The abstract operation ValidateAtomicAccess takes arguments _typedArray_ and _requestIndex_. It performs the following steps when called:</p>
<emu-alg>
1. Assert: _typedArray_ is an Object that has a [[ViewedArrayBuffer]] internal slot.
1. Let _accessIndex_ be ? ToIndex(_requestIndex_).
1. Let _length_ be _typedArray_.[[ArrayLength]].
1. Let _accessIndex_ be ? ToIndex(_requestIndex_).
1. Assert: _accessIndex_ &ge; 0.
1. If _accessIndex_ &ge; _length_, throw a *RangeError* exception.
1. Return _accessIndex_.
Expand Down Expand Up @@ -38324,11 +38326,13 @@ <h1>NotifyWaiter ( _WL_, _W_ )</h1>
<h1>AtomicReadModifyWrite ( _typedArray_, _index_, _value_, _op_ )</h1>
<p>The abstract operation AtomicReadModifyWrite takes arguments _typedArray_, _index_, _value_, and _op_ (a read-modify-write modification function). _op_ takes two List of byte values arguments and returns a List of byte values. This operation atomically loads a value, combines it with another value, and stores the result of the combination. It returns the loaded value. It performs the following steps when called:</p>
<emu-alg>
1. Let _buffer_ be ? ValidateSharedIntegerTypedArray(_typedArray_).
1. Let _buffer_ be ? ValidateIntegerTypedArray(_typedArray_).
1. Let _i_ be ? ValidateAtomicAccess(_typedArray_, _index_).
1. Let _arrayTypeName_ be _typedArray_.[[TypedArrayName]].
1. If _typedArray_.[[ContentType]] is ~BigInt~, let _v_ be ? ToBigInt(_value_).
1. Otherwise, let _v_ be ? ToInteger(_value_).
1. If IsDetachedBuffer(_buffer_) is *true*, throw a *TypeError* exception.
1. NOTE: The above check is not redundant with the check in ValidateIntegerTypedArray because the call to ToBigInt or ToInteger on the preceding lines can have arbitrary side effects, which could cause the buffer to become detached.
1. Let _elementSize_ be the Element Size value specified in <emu-xref href="#table-the-typedarray-constructors"></emu-xref> for _arrayTypeName_.
1. Let _elementType_ be the Element Type value in <emu-xref href="#table-the-typedarray-constructors"></emu-xref> for _arrayTypeName_.
1. Let _offset_ be _typedArray_.[[ByteOffset]].
Expand All @@ -38341,8 +38345,10 @@ <h1>AtomicReadModifyWrite ( _typedArray_, _index_, _value_, _op_ )</h1>
<h1>AtomicLoad ( _typedArray_, _index_ )</h1>
<p>The abstract operation AtomicLoad takes arguments _typedArray_ and _index_. It atomically loads a value and returns the loaded value. It performs the following steps when called:</p>
<emu-alg>
1. Let _buffer_ be ? ValidateSharedIntegerTypedArray(_typedArray_).
1. Let _buffer_ be ? ValidateIntegerTypedArray(_typedArray_).
1. Let _i_ be ? ValidateAtomicAccess(_typedArray_, _index_).
1. If IsDetachedBuffer(_buffer_) is *true*, throw a *TypeError* exception.
1. NOTE: The above check is not redundant with the check in ValidateIntegerTypedArray because the call to ValidateAtomicAccess on the preceding line can have arbitrary side effects, which could cause the buffer to become detached.
1. Let _arrayTypeName_ be _typedArray_.[[TypedArrayName]].
1. Let _elementSize_ be the Element Size value specified in <emu-xref href="#table-the-typedarray-constructors"></emu-xref> for _arrayTypeName_.
1. Let _elementType_ be the Element Type value in <emu-xref href="#table-the-typedarray-constructors"></emu-xref> for _arrayTypeName_.
Expand Down Expand Up @@ -38418,7 +38424,7 @@ <h1>Atomics.and ( _typedArray_, _index_, _value_ )</h1>
<h1>Atomics.compareExchange ( _typedArray_, _index_, _expectedValue_, _replacementValue_ )</h1>
<p>The following steps are taken:</p>
<emu-alg>
1. Let _buffer_ be ? ValidateSharedIntegerTypedArray(_typedArray_).
1. Let _buffer_ be ? ValidateIntegerTypedArray(_typedArray_).
1. Let _i_ be ? ValidateAtomicAccess(_typedArray_, _index_).
1. Let _arrayTypeName_ be _typedArray_.[[TypedArrayName]].
1. If _typedArray_.[[ContentType]] is ~BigInt~, then
Expand All @@ -38427,6 +38433,8 @@ <h1>Atomics.compareExchange ( _typedArray_, _index_, _expectedValue_, _replaceme
1. Else,
1. Let _expected_ be ? ToInteger(_expectedValue_).
1. Let _replacement_ be ? ToInteger(_replacementValue_).
1. If IsDetachedBuffer(_buffer_) is *true*, throw a *TypeError* exception.
1. NOTE: The above check is not redundant with the check in ValidateIntegerTypedArray because the call to ToBigInt or ToInteger on the preceding lines can have arbitrary side effects, which could cause the buffer to become detached.
1. Let _elementType_ be the Element Type value in <emu-xref href="#table-the-typedarray-constructors"></emu-xref> for _arrayTypeName_.
1. Let _isLittleEndian_ be the value of the [[LittleEndian]] field of the surrounding agent's Agent Record.
1. Let _expectedBytes_ be NumericToRawBytes(_elementType_, _expected_, _isLittleEndian_).
Expand Down Expand Up @@ -38490,11 +38498,13 @@ <h1>Atomics.or ( _typedArray_, _index_, _value_ )</h1>
<h1>Atomics.store ( _typedArray_, _index_, _value_ )</h1>
<p>The following steps are taken:</p>
<emu-alg>
1. Let _buffer_ be ? ValidateSharedIntegerTypedArray(_typedArray_).
1. Let _buffer_ be ? ValidateIntegerTypedArray(_typedArray_).
1. Let _i_ be ? ValidateAtomicAccess(_typedArray_, _index_).
1. Let _arrayTypeName_ be _typedArray_.[[TypedArrayName]].
1. If _arrayTypeName_ is *"BigUint64Array"* or *"BigInt64Array"*, let _v_ be ? ToBigInt(_value_).
1. Otherwise, let _v_ be ? ToInteger(_value_).
1. If IsDetachedBuffer(_buffer_) is *true*, throw a *TypeError* exception.
1. NOTE: The above check is not redundant with the check in ValidateIntegerTypedArray because the call to ToBigInt or ToInteger on the preceding lines can have arbitrary side effects, which could cause the buffer to become detached.
1. Let _elementSize_ be the Element Size value specified in <emu-xref href="#table-the-typedarray-constructors"></emu-xref> for _arrayTypeName_.
1. Let _elementType_ be the Element Type value in <emu-xref href="#table-the-typedarray-constructors"></emu-xref> for _arrayTypeName_.
1. Let _offset_ be _typedArray_.[[ByteOffset]].
Expand Down Expand Up @@ -38526,7 +38536,8 @@ <h1>Atomics.sub ( _typedArray_, _index_, _value_ )</h1>
<h1>Atomics.wait ( _typedArray_, _index_, _value_, _timeout_ )</h1>
<p>`Atomics.wait` puts the calling agent in a wait queue and puts it to sleep until it is notified or the sleep times out. The following steps are taken:</p>
<emu-alg>
1. Let _buffer_ be ? ValidateSharedIntegerTypedArray(_typedArray_, *true*).
1. Let _buffer_ be ? ValidateIntegerTypedArray(_typedArray_, *true*).
1. If IsSharedArrayBuffer(_buffer_) is *false*, throw a *TypeError* exception.
1. Let _i_ be ? ValidateAtomicAccess(_typedArray_, _index_).
1. Let _arrayTypeName_ be _typedArray_.[[TypedArrayName]].
1. If _arrayTypeName_ is *"BigInt64Array"*, let _v_ be ? ToBigInt64(_value_).
Expand Down Expand Up @@ -38562,7 +38573,7 @@ <h1>Atomics.wait ( _typedArray_, _index_, _value_, _timeout_ )</h1>
<h1>Atomics.notify ( _typedArray_, _index_, _count_ )</h1>
<p>`Atomics.notify` notifies some agents that are sleeping in the wait queue. The following steps are taken:</p>
<emu-alg>
1. Let _buffer_ be ? ValidateSharedIntegerTypedArray(_typedArray_, *true*).
1. Let _buffer_ be ? ValidateIntegerTypedArray(_typedArray_, *true*).
1. Let _i_ be ? ValidateAtomicAccess(_typedArray_, _index_).
1. If _count_ is *undefined*, let _c_ be *+&infin;*.
1. Else,
Expand All @@ -38573,6 +38584,7 @@ <h1>Atomics.notify ( _typedArray_, _index_, _count_ )</h1>
1. Let _arrayTypeName_ be _typedArray_.[[TypedArrayName]].
1. Let _elementSize_ be the Element Size value specified in <emu-xref href="#table-the-typedarray-constructors"></emu-xref> for _arrayTypeName_.
1. Let _indexedPosition_ be (_i_ &times; _elementSize_) + _offset_.
1. If IsSharedArrayBuffer(_buffer_) is *false*, return 0.
1. Let _WL_ be GetWaiterList(_block_, _indexedPosition_).
1. Let _n_ be 0.
1. Perform EnterCriticalSection(_WL_).
Expand Down

0 comments on commit ac0740b

Please sign in to comment.