Bug 1023755 - PJS: Array.prototype.scanPar per-element thunk reads and writes result array; vulnerable to bailout-and-restart. r=nmatsakis
--- a/js/src/builtin/Array.js
+++ b/js/src/builtin/Array.js
@@ -859,17 +859,22 @@ function ArrayScanPar(func, mode) {
ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, func));
var self = ToObject(this);
var length = self.length;
if (length === 0)
ThrowError(JSMSG_EMPTY_ARRAY_REDUCE);
+ // We need two buffers because phase2() will read an intermediate result and
+ // write a final result; that is safe against bailout-and-restart only if
+ // the intermediate and final buffers are distinct. (Bug 1023755)
+ // Obviously paying for a second buffer is undesirable.
var buffer = NewDenseArray(length);
+ var buffer2 = NewDenseArray(length);
parallel: for (;;) { // see ArrayMapPar() to explain why for(;;) etc
if (ShouldForceSequential())
break parallel;
if (!TRY_PARALLEL(mode))
break parallel;
var slicesInfo = ComputeSlicesInfo(length);
@@ -884,21 +889,22 @@ function ArrayScanPar(func, mode) {
ARRAY_PUSH(intermediates, accumulator);
for (var i = 1; i < numSlices - 1; i++) {
accumulator = func(accumulator, buffer[finalElement(i)]);
ARRAY_PUSH(intermediates, accumulator);
}
// Complete each slice using intermediates array (see comment on phase2()).
//
- // We start from slice 1 instead of 0 since there is no work to be done
- // for slice 0.
- if (numSlices > 1)
- ForkJoin(phase2, 1, numSlices, ForkJoinMode(mode), buffer);
- return buffer;
+ // Slice 0 must be handled specially - it's just a copy - since we don't
+ // have an identity value for the operation.
+ for ( var k=0, limit=finalElement(0) ; k <= limit ; k++ )
+ buffer2[k] = buffer[k];
+ ForkJoin(phase2, 1, numSlices, ForkJoinMode(mode), buffer2);
+ return buffer2;
}
// Sequential fallback:
ASSERT_SEQUENTIAL_IS_OK(mode);
scan(self[0], 0, length);
return buffer;
function scan(accumulator, start, end) {
@@ -981,17 +987,17 @@ function ArrayScanPar(func, mode) {
function phase2(workerId, sliceStart, sliceEnd) {
var sliceShift = slicesInfo.shift;
var sliceId;
while (GET_SLICE(sliceStart, sliceEnd, sliceId)) {
var indexPos = SLICE_START_INDEX(sliceShift, sliceId);
var indexEnd = SLICE_END_INDEX(sliceShift, indexPos, length);
var intermediate = intermediates[sliceId - 1];
for (; indexPos < indexEnd; indexPos++)
- UnsafePutElements(buffer, indexPos, func(intermediate, buffer[indexPos]));
+ UnsafePutElements(buffer2, indexPos, func(intermediate, buffer[indexPos]));
}
return sliceId;
}
return undefined;
}
/**