-
Notifications
You must be signed in to change notification settings - Fork 480
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SharedArrayBuffer and Atomics tests #839
Changes from 2 commits
b07a01e
d1fccaa
40bb0d2
6e2b2e5
9d97d3c
49ab6c5
291563d
388466d
ab2ca76
1297867
89a0f73
c55033e
32ba9c0
b970a8d
03e19ad
2d74325
28abb1d
adae9e8
70e2166
f076541
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
// Copyright (C) 2015 Mozilla Corporation. All rights reserved. | ||
// This code is governed by the BSD license found in the LICENSE file. | ||
// | ||
// An implementation of '$.agent' for the SpiderMonkey shell. | ||
// See ../INTERPRETING.md for the API definition. | ||
|
||
if (typeof $ == 'undefined') | ||
$ = {}; | ||
|
||
$.agent = (function () { | ||
|
||
// The SpiderMonkey implementation uses a designated shared buffer _ia | ||
// for coordination, and spinlocks for everything except sleeping. | ||
|
||
var _MSG_LOC = 0; // Low bit set: broadcast available; High bits: seq # | ||
var _ID_LOC = 1; // ID sent with broadcast | ||
var _ACK_LOC = 2; // Worker increments this to ack that broadcast was received | ||
var _RDY_LOC = 3; // Worker increments this to ack that worker is up and running | ||
var _LOCKTXT_LOC = 4; // Writer lock for the text buffer: 0=open, 1=closed | ||
var _NUMTXT_LOC = 5; // Count of messages in text buffer | ||
var _NEXT_LOC = 6; // First free location in the buffer | ||
var _SLEEP_LOC = 7; // Used for sleeping | ||
|
||
var _FIRST = 10; // First location of first message | ||
|
||
var _ia = new Int32Array(new SharedArrayBuffer(65536)); | ||
_ia[_NEXT_LOC] = _FIRST; | ||
|
||
var _worker_prefix = ` | ||
if (typeof $ == 'undefined') | ||
$ = {}; | ||
|
||
$.agent = (function () { | ||
|
||
var _ia; | ||
|
||
var agent = { | ||
_ia: null, | ||
|
||
receiveBroadcast: function(receiver) { | ||
var k; | ||
while (((k = Atomics.load(_ia, ${_MSG_LOC})) & 1) == 0) | ||
; | ||
var received_sab = getSharedArrayBuffer(); | ||
var received_id = Atomics.load(_ia, ${_ID_LOC}); | ||
Atomics.add(_ia, ${_ACK_LOC}, 1); | ||
while (Atomics.load(_ia, ${_MSG_LOC}) == k) | ||
; | ||
receiver(received_sab, received_id); | ||
}, | ||
|
||
report: function(msg) { | ||
while (Atomics.compareExchange(_ia, ${_LOCKTXT_LOC}, 0, 1) == 1) | ||
; | ||
msg = "" + msg; | ||
var i = _ia[${_NEXT_LOC}]; | ||
_ia[i++] = msg.length; | ||
for ( let j=0 ; j < msg.length ; j++ ) | ||
_ia[i++] = msg.charCodeAt(j); | ||
_ia[${_NEXT_LOC}] = i; | ||
Atomics.add(_ia, ${_NUMTXT_LOC}, 1); | ||
Atomics.store(_ia, ${_LOCKTXT_LOC}, 0); | ||
}, | ||
|
||
sleep: function(s) { | ||
Atomics.wait(_ia, ${_SLEEP_LOC}, 0, s); | ||
}, | ||
|
||
leaving: function() {} | ||
}; | ||
|
||
_ia = new Int32Array(getSharedArrayBuffer()); | ||
Atomics.add(_ia, ${_RDY_LOC}, 1); | ||
|
||
return agent; | ||
})(); | ||
`; | ||
|
||
var agent = | ||
{ | ||
_numWorkers: 0, | ||
_numReports: 0, | ||
_reportPtr: _FIRST, | ||
|
||
start: function (script) { | ||
setSharedArrayBuffer(_ia.buffer); | ||
var oldrdy = Atomics.load(_ia, _RDY_LOC); | ||
evalInWorker(_worker_prefix + script); | ||
while (Atomics.load(_ia, _RDY_LOC) == oldrdy) | ||
; | ||
this._numWorkers++; | ||
}, | ||
|
||
broadcast: function (sab, id) { | ||
setSharedArrayBuffer(sab); | ||
Atomics.store(_ia, _ID_LOC, id); | ||
Atomics.store(_ia, _ACK_LOC, 0); | ||
Atomics.add(_ia, _MSG_LOC, 1); | ||
while (Atomics.load(_ia, _ACK_LOC) < this._numWorkers) | ||
; | ||
Atomics.add(_ia, _MSG_LOC, 1); | ||
}, | ||
|
||
getReport: function () { | ||
if (this._numReports == Atomics.load(_ia, _NUMTXT_LOC)) | ||
return null; | ||
var s = ""; | ||
var i = this._reportPtr; | ||
var len = _ia[i++]; | ||
for ( let j=0 ; j < len ; j++ ) | ||
s += String.fromCharCode(_ia[i++]); | ||
this._reportPtr = i; | ||
this._numReports++; | ||
return s; | ||
}, | ||
|
||
sleep: function(s) { | ||
Atomics.wait(_ia, _SLEEP_LOC, 0, s); | ||
}, | ||
} | ||
|
||
return agent; | ||
})(); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
// Copyright (C) 2017 Mozilla Corporation. All rights reserved. | ||
// This code is governed by the BSD license found in the LICENSE file. | ||
/*--- | ||
description: > | ||
`Symbol.toStringTag` property descriptor on Atomics | ||
info: > | ||
The initial value of the @@toStringTag property is the String value | ||
"Atomics". | ||
|
||
This property has the attributes { [[Writable]]: false, [[Enumerable]]: | ||
false, [[Configurable]]: true }. | ||
includes: [propertyHelper.js] | ||
features: [Symbol.toStringTag] | ||
---*/ | ||
|
||
assert.sameValue(Atomics[Symbol.toStringTag], 'Atomics'); | ||
|
||
verifyNotEnumerable(Atomics, Symbol.toStringTag); | ||
verifyNotWritable(Atomics, Symbol.toStringTag); | ||
verifyConfigurable(Atomics, Symbol.toStringTag); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
// Copyright (C) 2015 Mozilla Corporation. All rights reserved. | ||
// This code is governed by the BSD license found in the LICENSE file. | ||
|
||
/*--- | ||
description: > | ||
Test range checking of Atomics.add on arrays that allow atomic operations | ||
---*/ | ||
|
||
var sab = new SharedArrayBuffer(4); | ||
|
||
var views = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array]; | ||
|
||
var bad_indices = [ (view) => -1, | ||
(view) => view.length, | ||
(view) => view.length*2, | ||
(view) => undefined, | ||
(view) => Number.NaN, | ||
(view) => Number.POSITIVE_INFINITY, | ||
(view) => Number.NEGATIVE_INFINITY, | ||
(view) => '3.5', | ||
(view) => 3.5, | ||
(view) => { password: "qumquat" }, | ||
(view) => ({ valueOf: () => 125 }), | ||
(view) => ({ toString: () => '125', valueOf: false }) // non-callable valueOf triggers invocation of toString | ||
]; | ||
|
||
for ( let View of views ) { | ||
let view = new View(sab); | ||
for ( let IdxGen of bad_indices ) { | ||
var Idx = IdxGen(view); | ||
assert.throws(RangeError, () => Atomics.add(view, Idx, 10)); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
// Copyright 2015 Microsoft Corporation. All rights reserved. | ||
// This code is governed by the license found in the LICENSE file. | ||
|
||
/*--- | ||
description: Testing descriptor property of Atomics.add | ||
includes: [propertyHelper.js] | ||
---*/ | ||
|
||
verifyWritable(Atomics, "add"); | ||
verifyNotEnumerable(Atomics, "add"); | ||
verifyConfigurable(Atomics, "add"); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
// Copyright (C) 2016 Mozilla Corporation. All rights reserved. | ||
// This code is governed by the BSD license found in the LICENSE file. | ||
|
||
/*--- | ||
description: Test Atomics.add on arrays that allow atomic operations. | ||
---*/ | ||
|
||
var sab = new SharedArrayBuffer(1024); | ||
var ab = new ArrayBuffer(16); | ||
|
||
var int_views = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array]; | ||
|
||
var good_indices = [ (view) => 0/-1, // -0 | ||
(view) => '-0', | ||
(view) => view.length - 1, | ||
(view) => ({ valueOf: () => 0 }), | ||
(view) => ({ toString: () => '0', valueOf: false }) // non-callable valueOf triggers invocation of toString | ||
]; | ||
|
||
for ( let View of int_views ) { | ||
// Make it interesting - use non-zero byteOffsets and non-zero indexes. | ||
|
||
var view = new View(sab, 32, 20); | ||
var control = new View(ab, 0, 2); | ||
|
||
// Add positive number | ||
view[8] = 0; | ||
assert.sameValue(Atomics.add(view, 8, 10), 0); | ||
assert.sameValue(view[8], 10); | ||
|
||
// Add negative number | ||
assert.sameValue(Atomics.add(view, 8, -5), 10); | ||
assert.sameValue(view[8], 5); | ||
|
||
// Result is "negative" though subject to coercion | ||
view[3] = -5; | ||
control[0] = -5; | ||
assert.sameValue(Atomics.add(view, 3, 0), control[0]); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. those comments would serve great as assertion messages. Once again, it helps identifying the point of failure. assert.sameValue(Atomics.add(view, 3, 0), control[0], 'Result is "negative" though subject to coercion'); |
||
|
||
// Result is subject to chopping | ||
control[0] = 12345; | ||
view[3] = 12345; | ||
assert.sameValue(Atomics.add(view, 3, 0), control[0]); | ||
|
||
// And again | ||
control[0] = 123456789; | ||
view[3] = 123456789; | ||
assert.sameValue(Atomics.add(view, 3, 0), control[0]); | ||
|
||
// In-bounds boundary cases for indexing | ||
for ( let IdxGen of good_indices ) { | ||
let Idx = IdxGen(view); | ||
view.fill(0); | ||
// Atomics.store() computes an index from Idx in the same way as other | ||
// Atomics operations, not quite like view[Idx]. | ||
Atomics.store(view, Idx, 37); | ||
assert.sameValue(Atomics.add(view, Idx, 0), 37); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. once again an assertion message here indicating the used index |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
// Copyright (C) 2015 André Bargull. All rights reserved. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This test format is based from a work of André Bargull, but the test for this specific case (length prop of |
||
// This code is governed by the BSD license found in the LICENSE file. | ||
|
||
/*--- | ||
description: > | ||
Atomics.add.length is 3. | ||
info: > | ||
Atomics.add ( ia, index, val ) | ||
|
||
17 ECMAScript Standard Built-in Objects: | ||
Every built-in Function object, including constructors, has a length | ||
property whose value is an integer. Unless otherwise specified, this | ||
value is equal to the largest number of named arguments shown in the | ||
subclause headings for the function description, including optional | ||
parameters. However, rest parameters shown using the form “...name” | ||
are not included in the default argument count. | ||
|
||
Unless otherwise specified, the length property of a built-in Function | ||
object has the attributes { [[Writable]]: false, [[Enumerable]]: false, | ||
[[Configurable]]: true }. | ||
includes: [propertyHelper.js] | ||
---*/ | ||
|
||
assert.sameValue(Atomics.add.length, 3); | ||
|
||
verifyNotEnumerable(Atomics.add, "length"); | ||
verifyNotWritable(Atomics.add, "length"); | ||
verifyConfigurable(Atomics.add, "length"); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
// Copyright (C) 2015 André Bargull. All rights reserved. | ||
// This code is governed by the BSD license found in the LICENSE file. | ||
|
||
/*--- | ||
description: > | ||
Atomics.add.name is "add". | ||
includes: [propertyHelper.js] | ||
---*/ | ||
|
||
assert.sameValue(Atomics.add.name, "add"); | ||
|
||
verifyNotEnumerable(Atomics.add, "name"); | ||
verifyNotWritable(Atomics.add, "name"); | ||
verifyConfigurable(Atomics.add, "name"); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
// Copyright (C) 2015 Mozilla Corporation. All rights reserved. | ||
// This code is governed by the BSD license found in the LICENSE file. | ||
|
||
/*--- | ||
description: > | ||
Test Atomics.add on view values other than TypedArrays | ||
---*/ | ||
|
||
var values = [null, | ||
undefined, | ||
true, | ||
false, | ||
new Boolean(true), | ||
10, | ||
3.14, | ||
new Number(4), | ||
"Hi there", | ||
new Date, | ||
/a*utomaton/g, | ||
{ password: "qumquat" }, | ||
new DataView(new ArrayBuffer(10)), | ||
new ArrayBuffer(128), | ||
new SharedArrayBuffer(128), | ||
new Error("Ouch"), | ||
[1,1,2,3,5,8], | ||
((x) => -x), | ||
new Map(), | ||
new Set(), | ||
new WeakMap(), | ||
new WeakSet(), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. while it does not hurt adding more and more, I don't believe it's really necessary to have extra objects as instances of Map, Set, Date, Error, etc... A case with all the primitive values, an array, an ordinary object, should be enough. |
||
this.Promise ? new Promise(() => "done") : undefined, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this should be removed as well |
||
Symbol("halleluja"), | ||
// TODO: Proxy? | ||
Object, | ||
Int32Array, | ||
Date, | ||
Math, | ||
Atomics ]; | ||
|
||
for ( var view of values ) { | ||
assert.throws(TypeError, (() => Atomics.add(view, 0, 0))); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
// Copyright (C) 2015 Mozilla Corporation. All rights reserved. | ||
// This code is governed by the BSD license found in the LICENSE file. | ||
|
||
/*--- | ||
description: > | ||
Test Atomics.add on non-shared integer TypedArrays | ||
---*/ | ||
|
||
var ab = new ArrayBuffer(16); | ||
|
||
var int_views = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array]; | ||
|
||
for ( var View of int_views ) { | ||
var view = new View(ab); | ||
|
||
assert.throws(TypeError, (() => Atomics.add(view, 0, 0))); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there's already a harness helper that can be used to loop on different TypedArray constructors, even on specific constructors...
testWithTypedArrayConstructors(fn(View) { let view = new View(sab); ... }, views)
can be used as it also tells where the error is if an assertion fail.