Backed out changeset 2a613f5a5866 (bug 1119537) for hazard failures

MozReview-Commit-ID: HjSuYxycsVu
This commit is contained in:
Wes Kocher 2016-02-29 13:02:01 -08:00
Родитель bdd8f0ca83
Коммит 2f82f5bc02
7 изменённых файлов: 49 добавлений и 124 удалений

Просмотреть файл

@ -798,8 +798,6 @@ GCState(JSContext* cx, unsigned argc, Value* vp)
state = "finalize"; state = "finalize";
else if (globalState == gc::COMPACT) else if (globalState == gc::COMPACT)
state = "compact"; state = "compact";
else if (globalState == gc::DECOMMIT)
state = "decommit";
else else
MOZ_CRASH("Unobserveable global GC state"); MOZ_CRASH("Unobserveable global GC state");

Просмотреть файл

@ -81,24 +81,7 @@ class BackgroundAllocTask : public GCParallelTask
bool enabled() const { return enabled_; } bool enabled() const { return enabled_; }
protected: protected:
void run() override; virtual void run() override;
};
// Search the provided Chunks for free arenas and decommit them.
class BackgroundDecommitTask : public GCParallelTask
{
public:
using ChunkVector = mozilla::Vector<Chunk*>;
explicit BackgroundDecommitTask(JSRuntime *rt) : runtime(rt) {}
void setChunksToScan(ChunkVector &chunks);
protected:
void run() override;
private:
JSRuntime* runtime;
ChunkVector toDecommit;
}; };
/* /*
@ -967,7 +950,7 @@ class GCRuntime
void endSweepPhase(bool lastGC); void endSweepPhase(bool lastGC);
void sweepZones(FreeOp* fop, bool lastGC); void sweepZones(FreeOp* fop, bool lastGC);
void decommitAllWithoutUnlocking(const AutoLockGC& lock); void decommitAllWithoutUnlocking(const AutoLockGC& lock);
void startDecommit(); void decommitArenas(AutoLockGC& lock);
void expireChunksAndArenas(bool shouldShrink, AutoLockGC& lock); void expireChunksAndArenas(bool shouldShrink, AutoLockGC& lock);
void queueZonesForBackgroundSweep(ZoneList& zones); void queueZonesForBackgroundSweep(ZoneList& zones);
void sweepBackgroundThings(ZoneList& zones, LifoAlloc& freeBlocks, ThreadType threadType); void sweepBackgroundThings(ZoneList& zones, LifoAlloc& freeBlocks, ThreadType threadType);
@ -1344,7 +1327,6 @@ class GCRuntime
mozilla::DebugOnly<mozilla::Atomic<PRThread*>> lockOwner; mozilla::DebugOnly<mozilla::Atomic<PRThread*>> lockOwner;
BackgroundAllocTask allocTask; BackgroundAllocTask allocTask;
BackgroundDecommitTask decommitTask;
GCHelperState helperState; GCHelperState helperState;
/* /*

Просмотреть файл

@ -593,7 +593,7 @@ js::Nursery::FreeMallocedBuffersTask::transferBuffersToFree(MallocedBuffersSet&
{ {
// Transfer the contents of the source set to the task's buffers_ member by // Transfer the contents of the source set to the task's buffers_ member by
// swapping the sets, which also clears the source. // swapping the sets, which also clears the source.
MOZ_ASSERT(!isRunningWithLockHeld()); MOZ_ASSERT(!isRunning());
MOZ_ASSERT(buffers_.empty()); MOZ_ASSERT(buffers_.empty());
mozilla::Swap(buffers_, buffersToFree); mozilla::Swap(buffers_, buffersToFree);
} }

Просмотреть файл

@ -12,7 +12,6 @@ assertEq(gcstate(), "none");
// sized slices while background finalization is on-going, so we need to loop. // sized slices while background finalization is on-going, so we need to loop.
gcslice(1000000); gcslice(1000000);
while (gcstate() == "finalize") { gcslice(1); } while (gcstate() == "finalize") { gcslice(1); }
while (gcstate() == "decommit") { gcslice(1); }
assertEq(gcstate(), "none"); assertEq(gcstate(), "none");
// Incremental GC in multiple slices: if marking takes more than one slice, // Incremental GC in multiple slices: if marking takes more than one slice,
@ -24,7 +23,6 @@ gcslice(1000000);
assertEq(gcstate(), "mark"); assertEq(gcstate(), "mark");
gcslice(1000000); gcslice(1000000);
while (gcstate() == "finalize") { gcslice(1); } while (gcstate() == "finalize") { gcslice(1); }
while (gcstate() == "decommit") { gcslice(1); }
assertEq(gcstate(), "none"); assertEq(gcstate(), "none");
// Zeal mode 8: Incremental GC in two main slices: // Zeal mode 8: Incremental GC in two main slices:
@ -36,7 +34,6 @@ gcslice(1);
assertEq(gcstate(), "mark"); assertEq(gcstate(), "mark");
gcslice(1); gcslice(1);
while (gcstate() == "finalize") { gcslice(1); } while (gcstate() == "finalize") { gcslice(1); }
while (gcstate() == "decommit") { gcslice(1); }
assertEq(gcstate(), "none"); assertEq(gcstate(), "none");
// Zeal mode 9: Incremental GC in two main slices: // Zeal mode 9: Incremental GC in two main slices:
@ -48,7 +45,6 @@ gcslice(1);
assertEq(gcstate(), "mark"); assertEq(gcstate(), "mark");
gcslice(1); gcslice(1);
while (gcstate() == "finalize") { gcslice(1); } while (gcstate() == "finalize") { gcslice(1); }
while (gcstate() == "decommit") { gcslice(1); }
assertEq(gcstate(), "none"); assertEq(gcstate(), "none");
// Zeal mode 10: Incremental GC in multiple slices (always yeilds before // Zeal mode 10: Incremental GC in multiple slices (always yeilds before
@ -59,5 +55,4 @@ gcslice(1000000);
assertEq(gcstate(), "sweep"); assertEq(gcstate(), "sweep");
gcslice(1000000); gcslice(1000000);
while (gcstate() == "finalize") { gcslice(1); } while (gcstate() == "finalize") { gcslice(1); }
while (gcstate() == "decommit") { gcslice(1); }
assertEq(gcstate(), "none"); assertEq(gcstate(), "none");

Просмотреть файл

@ -1031,7 +1031,7 @@ void
GCRuntime::startBackgroundAllocTaskIfIdle() GCRuntime::startBackgroundAllocTaskIfIdle()
{ {
AutoLockHelperThreadState helperLock; AutoLockHelperThreadState helperLock;
if (allocTask.isRunningWithLockHeld()) if (allocTask.isRunning())
return; return;
// Join the previous invocation of the task. This will return immediately // Join the previous invocation of the task. This will return immediately
@ -1178,7 +1178,6 @@ GCRuntime::GCRuntime(JSRuntime* rt) :
#endif #endif
lock(nullptr), lock(nullptr),
allocTask(rt, emptyChunks_), allocTask(rt, emptyChunks_),
decommitTask(rt),
helperState(rt) helperState(rt)
{ {
setGCMode(JSGC_MODE_GLOBAL); setGCMode(JSGC_MODE_GLOBAL);
@ -1362,7 +1361,6 @@ GCRuntime::finish()
*/ */
helperState.finish(); helperState.finish();
allocTask.cancel(GCParallelTask::CancelAndWait); allocTask.cancel(GCParallelTask::CancelAndWait);
decommitTask.cancel(GCParallelTask::CancelAndWait);
#ifdef JS_GC_ZEAL #ifdef JS_GC_ZEAL
/* Free memory associated with GC verification. */ /* Free memory associated with GC verification. */
@ -3341,67 +3339,43 @@ GCRuntime::decommitAllWithoutUnlocking(const AutoLockGC& lock)
} }
void void
GCRuntime::startDecommit() GCRuntime::decommitArenas(AutoLockGC& lock)
{ {
MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt)); // Verify that all entries in the empty chunks pool are decommitted.
MOZ_ASSERT(!decommitTask.isRunning()); for (ChunkPool::Iter chunk(emptyChunks(lock)); !chunk.done(); chunk.next())
MOZ_ASSERT(!chunk->info.numArenasFreeCommitted);
BackgroundDecommitTask::ChunkVector toDecommit; // Build a Vector of all current available Chunks. Since we release the
{ // gc lock while doing the decommit syscall, it is dangerous to iterate
AutoLockGC lock(rt); // the available list directly, as concurrent operations can modify it.
mozilla::Vector<Chunk*> toDecommit;
// Verify that all entries in the empty chunks pool are already decommitted. MOZ_ASSERT(availableChunks(lock).verify());
for (ChunkPool::Iter chunk(emptyChunks(lock)); !chunk.done(); chunk.next()) for (ChunkPool::Iter iter(availableChunks(lock)); !iter.done(); iter.next()) {
MOZ_ASSERT(!chunk->info.numArenasFreeCommitted); if (!toDecommit.append(iter.get())) {
// The OOM handler does a full, immediate decommit, so there is
// Since we release the GC lock while doing the decommit syscall below, // nothing more to do here in any case.
// it is dangerous to iterate the available list directly, as the main return onOutOfMallocMemory(lock);
// thread could modify it concurrently. Instead, we build and pass an
// explicit Vector containing the Chunks we want to visit.
MOZ_ASSERT(availableChunks(lock).verify());
for (ChunkPool::Iter iter(availableChunks(lock)); !iter.done(); iter.next()) {
if (!toDecommit.append(iter.get())) {
// The OOM handler does a full, immediate decommit.
return onOutOfMallocMemory(lock);
}
} }
} }
decommitTask.setChunksToScan(toDecommit);
if (sweepOnBackgroundThread && decommitTask.start()) // Start at the tail and stop before the first chunk: we allocate from the
return; // head and don't want to thrash with the mutator.
for (size_t i = toDecommit.length(); i > 1; --i) {
Chunk* chunk = toDecommit[i - 1];
MOZ_ASSERT(chunk);
decommitTask.runFromMainThread(rt);
}
void
js::gc::BackgroundDecommitTask::setChunksToScan(ChunkVector &chunks)
{
MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime));
MOZ_ASSERT(!isRunning());
MOZ_ASSERT(toDecommit.empty());
Swap(toDecommit, chunks);
}
/* virtual */ void
js::gc::BackgroundDecommitTask::run()
{
AutoLockGC lock(runtime);
for (Chunk* chunk : toDecommit) {
// The arena list is not doubly-linked, so we have to work in the free // The arena list is not doubly-linked, so we have to work in the free
// list order and not in the natural order. // list order and not in the natural order.
while (chunk->info.numArenasFreeCommitted) { while (chunk->info.numArenasFreeCommitted) {
bool ok = chunk->decommitOneFreeArena(runtime, lock); bool ok = chunk->decommitOneFreeArena(rt, lock);
// If we are low enough on memory that we can't update the page // FIXME Bug 1095620: add cancellation support when this becomes
// tables, or if we need to return for any other reason, break out // a ParallelTask.
// of the loop. if (/* cancel_ || */ !ok)
if (cancel_ || !ok) return;
break;
} }
} }
toDecommit.clearAndFree(); MOZ_ASSERT(availableChunks(lock).verify());
} }
void void
@ -3412,6 +3386,9 @@ GCRuntime::expireChunksAndArenas(bool shouldShrink, AutoLockGC& lock)
AutoUnlockGC unlock(lock); AutoUnlockGC unlock(lock);
FreeChunkPool(rt, toFree); FreeChunkPool(rt, toFree);
} }
if (shouldShrink)
decommitArenas(lock);
} }
void void
@ -5721,7 +5698,6 @@ GCRuntime::endCompactPhase(JS::gcreason::Reason reason)
void void
GCRuntime::finishCollection(JS::gcreason::Reason reason) GCRuntime::finishCollection(JS::gcreason::Reason reason)
{ {
assertBackgroundSweepingFinished();
MOZ_ASSERT(marker.isDrained()); MOZ_ASSERT(marker.isDrained());
marker.stop(); marker.stop();
clearBufferedGrayRoots(); clearBufferedGrayRoots();
@ -5750,6 +5726,15 @@ GCRuntime::finishCollection(JS::gcreason::Reason reason)
} }
lastGCTime = currentTime; lastGCTime = currentTime;
// If this is an OOM GC reason, wait on the background sweeping thread
// before returning to ensure that we free as much as possible. If this is
// a zeal-triggered GC, we want to ensure that the mutator can continue
// allocating on the same pages to reduce fragmentation.
if (IsOOMReason(reason) || reason == JS::gcreason::DEBUG_GC) {
gcstats::AutoPhase ap(stats, gcstats::PHASE_WAIT_BACKGROUND_THREAD);
rt->gc.waitBackgroundSweepOrAllocEnd();
}
} }
static const char* static const char*
@ -5898,12 +5883,6 @@ GCRuntime::resetIncrementalGC(const char* reason)
break; break;
} }
case DECOMMIT: {
auto unlimited = SliceBudget::unlimited();
incrementalCollectSlice(unlimited, JS::gcreason::RESET);
break;
}
default: default:
MOZ_CRASH("Invalid incremental GC state"); MOZ_CRASH("Invalid incremental GC state");
} }
@ -6165,28 +6144,13 @@ GCRuntime::incrementalCollectSlice(SliceBudget& budget, JS::gcreason::Reason rea
endCompactPhase(reason); endCompactPhase(reason);
} }
startDecommit();
incrementalState = DECOMMIT;
MOZ_FALLTHROUGH;
case DECOMMIT:
{
gcstats::AutoPhase ap(stats, gcstats::PHASE_WAIT_BACKGROUND_THREAD);
// Yield until background decommit is done.
if (isIncremental && decommitTask.isRunning())
break;
decommitTask.join();
}
finishCollection(reason); finishCollection(reason);
incrementalState = NO_INCREMENTAL; incrementalState = NO_INCREMENTAL;
break; break;
default: default:
MOZ_CRASH("unexpected GC incrementalState"); MOZ_ASSERT(false);
} }
} }
@ -6315,12 +6279,10 @@ GCRuntime::gcCycle(bool nonincrementalByAPI, SliceBudget& budget, JS::gcreason::
{ {
gcstats::AutoPhase ap(stats, gcstats::PHASE_WAIT_BACKGROUND_THREAD); gcstats::AutoPhase ap(stats, gcstats::PHASE_WAIT_BACKGROUND_THREAD);
// Background finalization and decommit are finished by defininition // As we are about to clear the mark bits, wait for background
// before we can start a new GC session. // finalization to finish. We only need to wait on the first slice.
if (!isIncrementalGCInProgress()) { if (!isIncrementalGCInProgress())
assertBackgroundSweepingFinished(); waitBackgroundSweepEnd();
MOZ_ASSERT(!decommitTask.isRunning());
}
// We must also wait for background allocation to finish so we can // We must also wait for background allocation to finish so we can
// avoid taking the GC lock when manipulating the chunks during the GC. // avoid taking the GC lock when manipulating the chunks during the GC.
@ -6657,9 +6619,6 @@ GCRuntime::onOutOfMallocMemory()
// Stop allocating new chunks. // Stop allocating new chunks.
allocTask.cancel(GCParallelTask::CancelAndWait); allocTask.cancel(GCParallelTask::CancelAndWait);
// Make sure we release anything queued for release.
decommitTask.join();
// Wait for background free of nursery huge slots to finish. // Wait for background free of nursery huge slots to finish.
nursery.waitBackgroundFreeEnd(); nursery.waitBackgroundFreeEnd();

Просмотреть файл

@ -49,8 +49,7 @@ enum State {
MARK, MARK,
SWEEP, SWEEP,
FINALIZE, FINALIZE,
COMPACT, COMPACT
DECOMMIT
}; };
/* Map from C++ type to alloc kind. JSObject does not have a 1:1 mapping, so must use Arena::thingSize. */ /* Map from C++ type to alloc kind. JSObject does not have a 1:1 mapping, so must use Arena::thingSize. */
@ -962,7 +961,6 @@ class GCParallelTask
} }
// Check if a task is actively running. // Check if a task is actively running.
bool isRunningWithLockHeld() const;
bool isRunning() const; bool isRunning() const;
// This should be friended to HelperThread, but cannot be because it // This should be friended to HelperThread, but cannot be because it

Просмотреть файл

@ -1089,19 +1089,12 @@ js::GCParallelTask::runFromHelperThread()
} }
bool bool
js::GCParallelTask::isRunningWithLockHeld() const js::GCParallelTask::isRunning() const
{ {
MOZ_ASSERT(HelperThreadState().isLocked()); MOZ_ASSERT(HelperThreadState().isLocked());
return state == Dispatched; return state == Dispatched;
} }
bool
js::GCParallelTask::isRunning() const
{
AutoLockHelperThreadState helperLock;
return isRunningWithLockHeld();
}
void void
HelperThread::handleGCParallelWorkload() HelperThread::handleGCParallelWorkload()
{ {