зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1674994 part 1 - Make Atomics work with large ArrayBuffers. r=lth
The remaining three uint32_t uses in AtomicsObject.h/cpp are unrelated. This also fixes the byteLength getter on ArrayBuffer and SharedArrayBuffer. Differential Revision: https://phabricator.services.mozilla.com/D95688
This commit is contained in:
Родитель
5957a1ba9c
Коммит
9503e4fa46
|
@ -1233,8 +1233,10 @@ class MutableWrappedPtrOperations<JS::Value, Wrapper>
|
|||
void setInfinity() { set(JS::InfinityValue()); }
|
||||
void setBoolean(bool b) { set(JS::BooleanValue(b)); }
|
||||
void setMagic(JSWhyMagic why) { set(JS::MagicValue(why)); }
|
||||
void setNumber(uint32_t ui) { set(JS::NumberValue(ui)); }
|
||||
void setNumber(double d) { set(JS::NumberValue(d)); }
|
||||
template <typename T>
|
||||
void setNumber(T t) {
|
||||
set(JS::NumberValue(t));
|
||||
}
|
||||
void setString(JSString* str) { set(JS::StringValue(str)); }
|
||||
void setSymbol(JS::Symbol* sym) { set(JS::SymbolValue(sym)); }
|
||||
void setBigInt(JS::BigInt* bi) { set(JS::BigIntValue(bi)); }
|
||||
|
|
|
@ -112,11 +112,11 @@ static bool ValidateIntegerTypedArray(
|
|||
// 24.4.1.2 ValidateAtomicAccess ( typedArray, requestIndex )
|
||||
static bool ValidateAtomicAccess(JSContext* cx,
|
||||
Handle<TypedArrayObject*> typedArray,
|
||||
HandleValue requestIndex, uint32_t* index) {
|
||||
HandleValue requestIndex, size_t* index) {
|
||||
// Step 1 (implicit).
|
||||
|
||||
MOZ_ASSERT(!typedArray->hasDetachedBuffer());
|
||||
uint32_t length = typedArray->length().deprecatedGetUint32();
|
||||
size_t length = typedArray->length().get();
|
||||
|
||||
// Step 2.
|
||||
uint64_t accessIndex;
|
||||
|
@ -130,7 +130,7 @@ static bool ValidateAtomicAccess(JSContext* cx,
|
|||
}
|
||||
|
||||
// Step 6.
|
||||
*index = uint32_t(accessIndex);
|
||||
*index = size_t(accessIndex);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -251,7 +251,7 @@ bool AtomicAccess(JSContext* cx, HandleValue obj, HandleValue index, Op op) {
|
|||
}
|
||||
|
||||
// Step 2.
|
||||
uint32_t intIndex;
|
||||
size_t intIndex;
|
||||
if (!ValidateAtomicAccess(cx, unwrappedTypedArray, index, &intIndex)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -286,7 +286,7 @@ bool AtomicAccess(JSContext* cx, HandleValue obj, HandleValue index, Op op) {
|
|||
|
||||
template <typename T>
|
||||
static SharedMem<T*> TypedArrayData(JSContext* cx, TypedArrayObject* typedArray,
|
||||
uint32_t index) {
|
||||
size_t index) {
|
||||
if (typedArray->hasDetachedBuffer()) {
|
||||
ReportDetachedArrayBuffer(cx);
|
||||
return {};
|
||||
|
@ -307,7 +307,7 @@ static bool atomics_compareExchange(JSContext* cx, unsigned argc, Value* vp) {
|
|||
return AtomicAccess(
|
||||
cx, typedArray, index,
|
||||
[cx, &args](auto ops, Handle<TypedArrayObject*> unwrappedTypedArray,
|
||||
uint32_t index) {
|
||||
size_t index) {
|
||||
using T = typename decltype(ops)::Type;
|
||||
|
||||
HandleValue expectedValue = args.get(2);
|
||||
|
@ -344,7 +344,7 @@ static bool atomics_load(JSContext* cx, unsigned argc, Value* vp) {
|
|||
return AtomicAccess(
|
||||
cx, typedArray, index,
|
||||
[cx, &args](auto ops, Handle<TypedArrayObject*> unwrappedTypedArray,
|
||||
uint32_t index) {
|
||||
size_t index) {
|
||||
using T = typename decltype(ops)::Type;
|
||||
|
||||
SharedMem<T*> addr = TypedArrayData<T>(cx, unwrappedTypedArray, index);
|
||||
|
@ -369,7 +369,7 @@ static bool atomics_store(JSContext* cx, unsigned argc, Value* vp) {
|
|||
return AtomicAccess(
|
||||
cx, typedArray, index,
|
||||
[cx, &args](auto ops, Handle<TypedArrayObject*> unwrappedTypedArray,
|
||||
uint32_t index) {
|
||||
size_t index) {
|
||||
using T = typename decltype(ops)::Type;
|
||||
|
||||
HandleValue value = args.get(2);
|
||||
|
@ -399,7 +399,7 @@ static bool AtomicReadModifyWrite(JSContext* cx, const CallArgs& args,
|
|||
return AtomicAccess(
|
||||
cx, typedArray, index,
|
||||
[cx, &args, op](auto ops, Handle<TypedArrayObject*> unwrappedTypedArray,
|
||||
uint32_t index) {
|
||||
size_t index) {
|
||||
using T = typename decltype(ops)::Type;
|
||||
|
||||
HandleValue value = args.get(2);
|
||||
|
@ -524,10 +524,10 @@ namespace js {
|
|||
|
||||
class FutexWaiter {
|
||||
public:
|
||||
FutexWaiter(uint32_t offset, JSContext* cx)
|
||||
FutexWaiter(size_t offset, JSContext* cx)
|
||||
: offset(offset), cx(cx), lower_pri(nullptr), back(nullptr) {}
|
||||
|
||||
uint32_t offset; // int32 element index within the SharedArrayBuffer
|
||||
size_t offset; // int32 element index within the SharedArrayBuffer
|
||||
JSContext* cx; // The waiting thread
|
||||
FutexWaiter* lower_pri; // Lower priority nodes in circular doubly-linked
|
||||
// list of waiters
|
||||
|
@ -556,7 +556,7 @@ class AutoLockFutexAPI {
|
|||
// 24.4.11 Atomics.wait ( typedArray, index, value, timeout ), steps 8-9, 14-25.
|
||||
template <typename T>
|
||||
static FutexThread::WaitResult AtomicsWait(
|
||||
JSContext* cx, SharedArrayRawBuffer* sarb, uint32_t byteOffset, T value,
|
||||
JSContext* cx, SharedArrayRawBuffer* sarb, size_t byteOffset, T value,
|
||||
const mozilla::Maybe<mozilla::TimeDuration>& timeout) {
|
||||
// Validation and other guards should ensure that this does not happen.
|
||||
MOZ_ASSERT(sarb, "wait is only applicable to shared memory");
|
||||
|
@ -610,14 +610,14 @@ static FutexThread::WaitResult AtomicsWait(
|
|||
}
|
||||
|
||||
FutexThread::WaitResult js::atomics_wait_impl(
|
||||
JSContext* cx, SharedArrayRawBuffer* sarb, uint32_t byteOffset,
|
||||
int32_t value, const mozilla::Maybe<mozilla::TimeDuration>& timeout) {
|
||||
JSContext* cx, SharedArrayRawBuffer* sarb, size_t byteOffset, int32_t value,
|
||||
const mozilla::Maybe<mozilla::TimeDuration>& timeout) {
|
||||
return AtomicsWait(cx, sarb, byteOffset, value, timeout);
|
||||
}
|
||||
|
||||
FutexThread::WaitResult js::atomics_wait_impl(
|
||||
JSContext* cx, SharedArrayRawBuffer* sarb, uint32_t byteOffset,
|
||||
int64_t value, const mozilla::Maybe<mozilla::TimeDuration>& timeout) {
|
||||
JSContext* cx, SharedArrayRawBuffer* sarb, size_t byteOffset, int64_t value,
|
||||
const mozilla::Maybe<mozilla::TimeDuration>& timeout) {
|
||||
return AtomicsWait(cx, sarb, byteOffset, value, timeout);
|
||||
}
|
||||
|
||||
|
@ -626,7 +626,7 @@ FutexThread::WaitResult js::atomics_wait_impl(
|
|||
template <typename T>
|
||||
static bool DoAtomicsWait(JSContext* cx,
|
||||
Handle<TypedArrayObject*> unwrappedTypedArray,
|
||||
uint32_t index, T value, HandleValue timeoutv,
|
||||
size_t index, T value, HandleValue timeoutv,
|
||||
MutableHandleValue r) {
|
||||
mozilla::Maybe<mozilla::TimeDuration> timeout;
|
||||
if (!timeoutv.isUndefined()) {
|
||||
|
@ -652,12 +652,12 @@ static bool DoAtomicsWait(JSContext* cx,
|
|||
cx, unwrappedTypedArray->bufferShared());
|
||||
|
||||
// Step 11.
|
||||
uint32_t offset = unwrappedTypedArray->byteOffset().deprecatedGetUint32();
|
||||
size_t offset = unwrappedTypedArray->byteOffset().get();
|
||||
|
||||
// Steps 12-13.
|
||||
// The computation will not overflow because range checks have been
|
||||
// performed.
|
||||
uint32_t indexedPosition = index * sizeof(T) + offset;
|
||||
size_t indexedPosition = index * sizeof(T) + offset;
|
||||
|
||||
// Steps 8-9, 14-25.
|
||||
switch (atomics_wait_impl(cx, unwrappedSab->rawBufferObject(),
|
||||
|
@ -702,7 +702,7 @@ static bool atomics_wait(JSContext* cx, unsigned argc, Value* vp) {
|
|||
}
|
||||
|
||||
// Step 2.
|
||||
uint32_t intIndex;
|
||||
size_t intIndex;
|
||||
if (!ValidateAtomicAccess(cx, unwrappedTypedArray, index, &intIndex)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -733,7 +733,7 @@ static bool atomics_wait(JSContext* cx, unsigned argc, Value* vp) {
|
|||
|
||||
// ES2021 draft rev bd868f20b8c574ad6689fba014b62a1dba819e56
|
||||
// 24.4.12 Atomics.notify ( typedArray, index, count ), steps 10-16.
|
||||
int64_t js::atomics_notify_impl(SharedArrayRawBuffer* sarb, uint32_t byteOffset,
|
||||
int64_t js::atomics_notify_impl(SharedArrayRawBuffer* sarb, size_t byteOffset,
|
||||
int64_t count) {
|
||||
// Validation should ensure this does not happen.
|
||||
MOZ_ASSERT(sarb, "notify is only applicable to shared memory");
|
||||
|
@ -790,7 +790,7 @@ static bool atomics_notify(JSContext* cx, unsigned argc, Value* vp) {
|
|||
unwrappedTypedArray->type() == Scalar::BigInt64);
|
||||
|
||||
// Step 2.
|
||||
uint32_t intIndex;
|
||||
size_t intIndex;
|
||||
if (!ValidateAtomicAccess(cx, unwrappedTypedArray, index, &intIndex)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -821,13 +821,13 @@ static bool atomics_notify(JSContext* cx, unsigned argc, Value* vp) {
|
|||
cx, unwrappedTypedArray->bufferShared());
|
||||
|
||||
// Step 6.
|
||||
uint32_t offset = unwrappedTypedArray->byteOffset().deprecatedGetUint32();
|
||||
size_t offset = unwrappedTypedArray->byteOffset().get();
|
||||
|
||||
// Steps 7-9.
|
||||
// The computation will not overflow because range checks have been
|
||||
// performed.
|
||||
uint32_t elementSize = Scalar::byteSize(unwrappedTypedArray->type());
|
||||
uint32_t indexedPosition = intIndex * elementSize + offset;
|
||||
size_t elementSize = Scalar::byteSize(unwrappedTypedArray->type());
|
||||
size_t indexedPosition = intIndex * elementSize + offset;
|
||||
|
||||
// Steps 10-16.
|
||||
r.setNumber(double(atomics_notify_impl(unwrappedSab->rawBufferObject(),
|
||||
|
|
|
@ -123,20 +123,20 @@ class FutexThread {
|
|||
|
||||
// Go to sleep if the int32_t value at the given address equals `value`.
|
||||
MOZ_MUST_USE FutexThread::WaitResult atomics_wait_impl(
|
||||
JSContext* cx, SharedArrayRawBuffer* sarb, uint32_t byteOffset,
|
||||
int32_t value, const mozilla::Maybe<mozilla::TimeDuration>& timeout);
|
||||
JSContext* cx, SharedArrayRawBuffer* sarb, size_t byteOffset, int32_t value,
|
||||
const mozilla::Maybe<mozilla::TimeDuration>& timeout);
|
||||
|
||||
// Go to sleep if the int64_t value at the given address equals `value`.
|
||||
MOZ_MUST_USE FutexThread::WaitResult atomics_wait_impl(
|
||||
JSContext* cx, SharedArrayRawBuffer* sarb, uint32_t byteOffset,
|
||||
int64_t value, const mozilla::Maybe<mozilla::TimeDuration>& timeout);
|
||||
JSContext* cx, SharedArrayRawBuffer* sarb, size_t byteOffset, int64_t value,
|
||||
const mozilla::Maybe<mozilla::TimeDuration>& timeout);
|
||||
|
||||
// Notify some waiters on the given address. If `count` is negative then notify
|
||||
// all. The return value is nonnegative and is the number of waiters woken. If
|
||||
// the number of waiters woken exceeds INT64_MAX then this never returns. If
|
||||
// `count` is nonnegative then the return value is never greater than `count`.
|
||||
MOZ_MUST_USE int64_t atomics_notify_impl(SharedArrayRawBuffer* sarb,
|
||||
uint32_t byteOffset, int64_t count);
|
||||
size_t byteOffset, int64_t count);
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
// |jit-test| skip-if: !this.SharedArrayBuffer || !this.Atomics
|
||||
|
||||
let gb = 1 * 1024 * 1024 * 1024;
|
||||
let buflen = 4 * gb + 64;
|
||||
let sab = new SharedArrayBuffer(buflen);
|
||||
assertEq(sab.byteLength, buflen);
|
||||
|
||||
function testBasic(base) {
|
||||
var uint8 = new Uint8Array(sab);
|
||||
var uint8Part = new Uint8Array(sab, base, 64);
|
||||
|
||||
for (var i = 0; i < 50; i++) {
|
||||
var index = base + i;
|
||||
uint8Part[i] = 123;
|
||||
assertEq(uint8[index], 123);
|
||||
|
||||
// Binary ops.
|
||||
assertEq(Atomics.add(uint8, index, 1), 123);
|
||||
assertEq(Atomics.and(uint8, index, 0xf), 124);
|
||||
assertEq(Atomics.or(uint8, index, 0xf), 12);
|
||||
assertEq(Atomics.xor(uint8, index, 0xee), 0xf);
|
||||
assertEq(Atomics.sub(uint8, index, 100), 225);
|
||||
assertEq(uint8Part[i], 125);
|
||||
|
||||
// compareExchange.
|
||||
assertEq(Atomics.compareExchange(uint8, index, 125, 90), 125);
|
||||
assertEq(Atomics.compareExchange(uint8, index, 125, 90), 90);
|
||||
assertEq(uint8Part[i], 90);
|
||||
|
||||
// exchange.
|
||||
assertEq(Atomics.exchange(uint8, index, 42), 90);
|
||||
assertEq(uint8Part[i], 42);
|
||||
|
||||
// load/store.
|
||||
assertEq(Atomics.load(uint8, index), 42);
|
||||
assertEq(Atomics.store(uint8, index, 99), 99);
|
||||
assertEq(uint8Part[i], 99);
|
||||
}
|
||||
}
|
||||
for (let i = 0; i <= 4; i++) {
|
||||
testBasic(i * gb);
|
||||
}
|
||||
|
||||
function testWait() {
|
||||
let int32 = new Int32Array(sab);
|
||||
let index = int32.length - 1;
|
||||
assertEq(int32[index], 0);
|
||||
assertEq(Atomics.wait(int32, index, 1), "not-equal");
|
||||
int32[index] = 12345;
|
||||
assertEq(Atomics.wait(int32, index, 12345, 1), "timed-out");
|
||||
assertEq(Atomics.notify(int32, index), 0);
|
||||
|
||||
let int32WithOffset = new Int32Array(sab, int32.byteLength - 4);
|
||||
assertEq(int32WithOffset[0], 12345);
|
||||
assertEq(Atomics.wait(int32WithOffset, 0, 12345, 1), "timed-out");
|
||||
}
|
||||
testWait();
|
|
@ -4,6 +4,7 @@ let gb = 1 * 1024 * 1024 * 1024;
|
|||
|
||||
function test1() {
|
||||
let ab = new ArrayBuffer(7 * gb);
|
||||
assertEq(ab.byteLength, 7 * gb);
|
||||
|
||||
let taInt16 = new Int16Array(ab);
|
||||
assertEq(taInt16.byteOffset, 0);
|
||||
|
|
|
@ -369,7 +369,7 @@ MOZ_ALWAYS_INLINE bool ArrayBufferObject::byteLengthGetterImpl(
|
|||
JSContext* cx, const CallArgs& args) {
|
||||
MOZ_ASSERT(IsArrayBuffer(args.thisv()));
|
||||
auto* buffer = &args.thisv().toObject().as<ArrayBufferObject>();
|
||||
args.rval().setInt32(buffer->byteLength().deprecatedGetUint32());
|
||||
args.rval().setNumber(buffer->byteLength().get());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -176,7 +176,7 @@ MOZ_ALWAYS_INLINE bool SharedArrayBufferObject::byteLengthGetterImpl(
|
|||
JSContext* cx, const CallArgs& args) {
|
||||
MOZ_ASSERT(IsSharedArrayBuffer(args.thisv()));
|
||||
auto* buffer = &args.thisv().toObject().as<SharedArrayBufferObject>();
|
||||
args.rval().setInt32(buffer->byteLength().deprecatedGetUint32());
|
||||
args.rval().setNumber(buffer->byteLength().get());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче