mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 966646 - Use JS helper threads for GC background sweeping / allocation, r=billm.
This commit is contained in:
parent
a4ad3e8ea8
commit
6e6fb4bd6f
@ -144,18 +144,30 @@ class GCRuntime
|
|||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
void notifyRequestEnd() { conservativeGC.updateForRequestEnd(); }
|
void notifyRequestEnd() { conservativeGC.updateForRequestEnd(); }
|
||||||
#endif
|
#endif
|
||||||
bool isBackgroundSweeping() { return helperThread.sweeping(); }
|
bool isBackgroundSweeping() { return helperState.isBackgroundSweeping(); }
|
||||||
void waitBackgroundSweepEnd() { helperThread.waitBackgroundSweepEnd(); }
|
void waitBackgroundSweepEnd() { helperState.waitBackgroundSweepEnd(); }
|
||||||
void waitBackgroundSweepOrAllocEnd() { helperThread.waitBackgroundSweepOrAllocEnd(); }
|
void waitBackgroundSweepOrAllocEnd() { helperState.waitBackgroundSweepOrAllocEnd(); }
|
||||||
void startBackgroundShrink() { helperThread.startBackgroundShrink(); }
|
void startBackgroundShrink() { helperState.startBackgroundShrink(); }
|
||||||
void freeLater(void *p) { helperThread.freeLater(p); }
|
void startBackgroundAllocationIfIdle() { helperState.startBackgroundAllocationIfIdle(); }
|
||||||
|
void freeLater(void *p) { helperState.freeLater(p); }
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
bool onBackgroundThread() { return helperThread.onBackgroundThread(); }
|
|
||||||
|
bool onBackgroundThread() { return helperState.onBackgroundThread(); }
|
||||||
|
|
||||||
|
bool currentThreadOwnsGCLock() {
|
||||||
|
#ifdef JS_THREADSAFE
|
||||||
|
return lockOwner == PR_GetCurrentThread();
|
||||||
|
#else
|
||||||
|
return true;
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // DEBUG
|
||||||
|
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
void assertCanLock() {
|
void assertCanLock() {
|
||||||
JS_ASSERT(lockOwner != PR_GetCurrentThread());
|
JS_ASSERT(!currentThreadOwnsGCLock());
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -203,7 +215,7 @@ class GCRuntime
|
|||||||
private:
|
private:
|
||||||
// For ArenaLists::allocateFromArenaInline()
|
// For ArenaLists::allocateFromArenaInline()
|
||||||
friend class ArenaLists;
|
friend class ArenaLists;
|
||||||
Chunk *pickChunk(Zone *zone);
|
Chunk *pickChunk(Zone *zone, AutoMaybeStartBackgroundAllocation &maybeStartBackgroundAllocation);
|
||||||
|
|
||||||
inline bool wantBackgroundAllocation() const;
|
inline bool wantBackgroundAllocation() const;
|
||||||
|
|
||||||
@ -533,16 +545,16 @@ class GCRuntime
|
|||||||
size_t noGCOrAllocationCheck;
|
size_t noGCOrAllocationCheck;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Synchronize GC heap access between main thread and GCHelperThread. */
|
/* Synchronize GC heap access between main thread and GCHelperState. */
|
||||||
PRLock *lock;
|
PRLock *lock;
|
||||||
mozilla::DebugOnly<PRThread *> lockOwner;
|
mozilla::DebugOnly<PRThread *> lockOwner;
|
||||||
|
|
||||||
js::GCHelperThread helperThread;
|
GCHelperState helperState;
|
||||||
|
|
||||||
ConservativeGCData conservativeGC;
|
ConservativeGCData conservativeGC;
|
||||||
|
|
||||||
//friend class js::gc::Chunk; // todo: remove
|
//friend class js::gc::Chunk; // todo: remove
|
||||||
friend class js::GCHelperThread;
|
friend class js::GCHelperState;
|
||||||
friend class js::gc::MarkingValidator;
|
friend class js::gc::MarkingValidator;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
321
js/src/jsgc.cpp
321
js/src/jsgc.cpp
@ -960,14 +960,40 @@ GCRuntime::wantBackgroundAllocation() const
|
|||||||
* allocation if we have empty chunks or when the runtime needs just few
|
* allocation if we have empty chunks or when the runtime needs just few
|
||||||
* of them.
|
* of them.
|
||||||
*/
|
*/
|
||||||
return helperThread.canBackgroundAllocate() &&
|
return helperState.canBackgroundAllocate() &&
|
||||||
chunkPool.getEmptyCount() == 0 &&
|
chunkPool.getEmptyCount() == 0 &&
|
||||||
chunkSet.count() >= 4;
|
chunkSet.count() >= 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class js::gc::AutoMaybeStartBackgroundAllocation
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
JSRuntime *runtime;
|
||||||
|
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
||||||
|
|
||||||
|
public:
|
||||||
|
AutoMaybeStartBackgroundAllocation(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM)
|
||||||
|
: runtime(nullptr)
|
||||||
|
{
|
||||||
|
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tryToStartBackgroundAllocation(JSRuntime *rt) {
|
||||||
|
runtime = rt;
|
||||||
|
}
|
||||||
|
|
||||||
|
~AutoMaybeStartBackgroundAllocation() {
|
||||||
|
if (runtime && !runtime->currentThreadOwnsInterruptLock()) {
|
||||||
|
AutoLockWorkerThreadState workerLock;
|
||||||
|
AutoLockGC lock(runtime);
|
||||||
|
runtime->gc.startBackgroundAllocationIfIdle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/* The caller must hold the GC lock. */
|
/* The caller must hold the GC lock. */
|
||||||
Chunk *
|
Chunk *
|
||||||
GCRuntime::pickChunk(Zone *zone)
|
GCRuntime::pickChunk(Zone *zone, AutoMaybeStartBackgroundAllocation &maybeStartBackgroundAllocation)
|
||||||
{
|
{
|
||||||
Chunk **listHeadp = GetAvailableChunkList(zone);
|
Chunk **listHeadp = GetAvailableChunkList(zone);
|
||||||
Chunk *chunk = *listHeadp;
|
Chunk *chunk = *listHeadp;
|
||||||
@ -986,7 +1012,7 @@ GCRuntime::pickChunk(Zone *zone)
|
|||||||
JS_ASSERT(!chunkSet.has(chunk));
|
JS_ASSERT(!chunkSet.has(chunk));
|
||||||
|
|
||||||
if (wantBackgroundAllocation())
|
if (wantBackgroundAllocation())
|
||||||
helperThread.startBackgroundAllocationIfIdle();
|
maybeStartBackgroundAllocation.tryToStartBackgroundAllocation(rt);
|
||||||
|
|
||||||
chunkAllocationSinceLastGC = true;
|
chunkAllocationSinceLastGC = true;
|
||||||
|
|
||||||
@ -1094,7 +1120,7 @@ GCRuntime::GCRuntime(JSRuntime *rt) :
|
|||||||
#endif
|
#endif
|
||||||
lock(nullptr),
|
lock(nullptr),
|
||||||
lockOwner(nullptr),
|
lockOwner(nullptr),
|
||||||
helperThread(rt)
|
helperState(rt)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1191,7 +1217,7 @@ GCRuntime::init(uint32_t maxbytes)
|
|||||||
if (!rootsHash.init(256))
|
if (!rootsHash.init(256))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!helperThread.init())
|
if (!helperState.init())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1242,7 +1268,7 @@ GCRuntime::finish()
|
|||||||
* Wait until the background finalization stops and the helper thread
|
* Wait until the background finalization stops and the helper thread
|
||||||
* shuts down before we forcefully release any remaining GC memory.
|
* shuts down before we forcefully release any remaining GC memory.
|
||||||
*/
|
*/
|
||||||
helperThread.finish();
|
helperState.finish();
|
||||||
|
|
||||||
#ifdef JS_GC_ZEAL
|
#ifdef JS_GC_ZEAL
|
||||||
/* Free memory associated with GC verification. */
|
/* Free memory associated with GC verification. */
|
||||||
@ -1502,7 +1528,8 @@ PushArenaAllocatedDuringSweep(JSRuntime *runtime, ArenaHeader *arena)
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline void *
|
inline void *
|
||||||
ArenaLists::allocateFromArenaInline(Zone *zone, AllocKind thingKind)
|
ArenaLists::allocateFromArenaInline(Zone *zone, AllocKind thingKind,
|
||||||
|
AutoMaybeStartBackgroundAllocation &maybeStartBackgroundAllocation)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Parallel JS Note:
|
* Parallel JS Note:
|
||||||
@ -1575,7 +1602,7 @@ ArenaLists::allocateFromArenaInline(Zone *zone, AllocKind thingKind)
|
|||||||
JSRuntime *rt = zone->runtimeFromAnyThread();
|
JSRuntime *rt = zone->runtimeFromAnyThread();
|
||||||
if (!maybeLock.locked())
|
if (!maybeLock.locked())
|
||||||
maybeLock.lock(rt);
|
maybeLock.lock(rt);
|
||||||
Chunk *chunk = rt->gc.pickChunk(zone);
|
Chunk *chunk = rt->gc.pickChunk(zone, maybeStartBackgroundAllocation);
|
||||||
if (!chunk)
|
if (!chunk)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
@ -1620,7 +1647,8 @@ ArenaLists::allocateFromArenaInline(Zone *zone, AllocKind thingKind)
|
|||||||
void *
|
void *
|
||||||
ArenaLists::allocateFromArena(JS::Zone *zone, AllocKind thingKind)
|
ArenaLists::allocateFromArena(JS::Zone *zone, AllocKind thingKind)
|
||||||
{
|
{
|
||||||
return allocateFromArenaInline(zone, thingKind);
|
AutoMaybeStartBackgroundAllocation maybeStartBackgroundAllocation;
|
||||||
|
return allocateFromArenaInline(zone, thingKind, maybeStartBackgroundAllocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -1695,7 +1723,7 @@ ArenaLists::queueForBackgroundSweep(FreeOp *fop, AllocKind thingKind)
|
|||||||
JS_ASSERT(IsBackgroundFinalized(thingKind));
|
JS_ASSERT(IsBackgroundFinalized(thingKind));
|
||||||
|
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
JS_ASSERT(!fop->runtime()->gc.helperThread.sweeping());
|
JS_ASSERT(!fop->runtime()->gc.isBackgroundSweeping());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ArenaList *al = &arenaLists[thingKind];
|
ArenaList *al = &arenaLists[thingKind];
|
||||||
@ -1867,6 +1895,8 @@ ArenaLists::refillFreeList(ThreadSafeContext *cx, AllocKind thingKind)
|
|||||||
return thing;
|
return thing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AutoMaybeStartBackgroundAllocation maybeStartBackgroundAllocation;
|
||||||
|
|
||||||
if (cx->isJSContext()) {
|
if (cx->isJSContext()) {
|
||||||
/*
|
/*
|
||||||
* allocateFromArena may fail while the background finalization still
|
* allocateFromArena may fail while the background finalization still
|
||||||
@ -1877,13 +1907,14 @@ ArenaLists::refillFreeList(ThreadSafeContext *cx, AllocKind thingKind)
|
|||||||
* this race we always try to allocate twice.
|
* this race we always try to allocate twice.
|
||||||
*/
|
*/
|
||||||
for (bool secondAttempt = false; ; secondAttempt = true) {
|
for (bool secondAttempt = false; ; secondAttempt = true) {
|
||||||
void *thing = cx->allocator()->arenas.allocateFromArenaInline(zone, thingKind);
|
void *thing = cx->allocator()->arenas.allocateFromArenaInline(zone, thingKind,
|
||||||
|
maybeStartBackgroundAllocation);
|
||||||
if (MOZ_LIKELY(!!thing))
|
if (MOZ_LIKELY(!!thing))
|
||||||
return thing;
|
return thing;
|
||||||
if (secondAttempt)
|
if (secondAttempt)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
cx->asJSContext()->runtime()->gc.helperThread.waitBackgroundSweepEnd();
|
cx->asJSContext()->runtime()->gc.waitBackgroundSweepEnd();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
@ -1902,7 +1933,8 @@ ArenaLists::refillFreeList(ThreadSafeContext *cx, AllocKind thingKind)
|
|||||||
WorkerThreadState().wait(GlobalWorkerThreadState::PRODUCER);
|
WorkerThreadState().wait(GlobalWorkerThreadState::PRODUCER);
|
||||||
}
|
}
|
||||||
|
|
||||||
void *thing = cx->allocator()->arenas.allocateFromArenaInline(zone, thingKind);
|
void *thing = cx->allocator()->arenas.allocateFromArenaInline(zone, thingKind,
|
||||||
|
maybeStartBackgroundAllocation);
|
||||||
if (thing)
|
if (thing)
|
||||||
return thing;
|
return thing;
|
||||||
#else
|
#else
|
||||||
@ -2102,7 +2134,7 @@ GCRuntime::maybeGC(Zone *zone)
|
|||||||
if (zone->gcBytes > 1024 * 1024 &&
|
if (zone->gcBytes > 1024 * 1024 &&
|
||||||
zone->gcBytes >= factor * zone->gcTriggerBytes &&
|
zone->gcBytes >= factor * zone->gcTriggerBytes &&
|
||||||
incrementalState == NO_INCREMENTAL &&
|
incrementalState == NO_INCREMENTAL &&
|
||||||
!helperThread.sweeping())
|
!isBackgroundSweeping())
|
||||||
{
|
{
|
||||||
PrepareZoneForGC(zone);
|
PrepareZoneForGC(zone);
|
||||||
GCSlice(rt, GC_NORMAL, JS::gcreason::MAYBEGC);
|
GCSlice(rt, GC_NORMAL, JS::gcreason::MAYBEGC);
|
||||||
@ -2284,6 +2316,7 @@ SweepBackgroundThings(JSRuntime* rt, bool onBackgroundThread)
|
|||||||
static void
|
static void
|
||||||
AssertBackgroundSweepingFinished(JSRuntime *rt)
|
AssertBackgroundSweepingFinished(JSRuntime *rt)
|
||||||
{
|
{
|
||||||
|
#ifdef DEBUG
|
||||||
JS_ASSERT(!rt->gc.sweepingZones);
|
JS_ASSERT(!rt->gc.sweepingZones);
|
||||||
for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
|
for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
|
||||||
for (unsigned i = 0; i < FINALIZE_LIMIT; ++i) {
|
for (unsigned i = 0; i < FINALIZE_LIMIT; ++i) {
|
||||||
@ -2291,6 +2324,7 @@ AssertBackgroundSweepingFinished(JSRuntime *rt)
|
|||||||
JS_ASSERT(zone->allocator.arenas.doneBackgroundFinalize(AllocKind(i)));
|
JS_ASSERT(zone->allocator.arenas.doneBackgroundFinalize(AllocKind(i)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned
|
unsigned
|
||||||
@ -2312,7 +2346,7 @@ js::GetCPUCount()
|
|||||||
#endif /* JS_THREADSAFE */
|
#endif /* JS_THREADSAFE */
|
||||||
|
|
||||||
bool
|
bool
|
||||||
GCHelperThread::init()
|
GCHelperState::init()
|
||||||
{
|
{
|
||||||
if (!rt->useHelperThreads()) {
|
if (!rt->useHelperThreads()) {
|
||||||
backgroundAllocation = false;
|
backgroundAllocation = false;
|
||||||
@ -2320,174 +2354,172 @@ GCHelperThread::init()
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
if (!(wakeup = PR_NewCondVar(rt->gc.lock)))
|
|
||||||
return false;
|
|
||||||
if (!(done = PR_NewCondVar(rt->gc.lock)))
|
if (!(done = PR_NewCondVar(rt->gc.lock)))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
thread = PR_CreateThread(PR_USER_THREAD, threadMain, this, PR_PRIORITY_NORMAL,
|
|
||||||
PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
|
|
||||||
if (!thread)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
backgroundAllocation = (GetCPUCount() >= 2);
|
backgroundAllocation = (GetCPUCount() >= 2);
|
||||||
|
|
||||||
|
WorkerThreadState().ensureInitialized();
|
||||||
#endif /* JS_THREADSAFE */
|
#endif /* JS_THREADSAFE */
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
GCHelperThread::finish()
|
GCHelperState::finish()
|
||||||
{
|
{
|
||||||
if (!rt->useHelperThreads() || !rt->gc.lock) {
|
if (!rt->useHelperThreads() || !rt->gc.lock) {
|
||||||
JS_ASSERT(state == IDLE);
|
JS_ASSERT(state_ == IDLE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
PRThread *join = nullptr;
|
// Wait for any lingering background sweeping to finish.
|
||||||
{
|
waitBackgroundSweepEnd();
|
||||||
AutoLockGC lock(rt);
|
|
||||||
if (thread && state != SHUTDOWN) {
|
|
||||||
/*
|
|
||||||
* We cannot be in the ALLOCATING or CANCEL_ALLOCATION states as
|
|
||||||
* the allocations should have been stopped during the last GC.
|
|
||||||
*/
|
|
||||||
JS_ASSERT(state == IDLE || state == SWEEPING);
|
|
||||||
if (state == IDLE)
|
|
||||||
PR_NotifyCondVar(wakeup);
|
|
||||||
state = SHUTDOWN;
|
|
||||||
join = thread;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (join) {
|
|
||||||
/* PR_DestroyThread is not necessary. */
|
|
||||||
PR_JoinThread(join);
|
|
||||||
}
|
|
||||||
if (wakeup)
|
|
||||||
PR_DestroyCondVar(wakeup);
|
|
||||||
if (done)
|
if (done)
|
||||||
PR_DestroyCondVar(done);
|
PR_DestroyCondVar(done);
|
||||||
|
#else
|
||||||
|
MOZ_CRASH();
|
||||||
#endif /* JS_THREADSAFE */
|
#endif /* JS_THREADSAFE */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GCHelperState::State
|
||||||
|
GCHelperState::state()
|
||||||
|
{
|
||||||
|
JS_ASSERT(rt->gc.currentThreadOwnsGCLock());
|
||||||
|
return state_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
GCHelperState::setState(State state)
|
||||||
|
{
|
||||||
|
JS_ASSERT(rt->gc.currentThreadOwnsGCLock());
|
||||||
|
state_ = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
GCHelperState::startBackgroundThread(State newState)
|
||||||
|
{
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
#ifdef MOZ_NUWA_PROCESS
|
JS_ASSERT(!thread && state() == IDLE && newState != IDLE);
|
||||||
extern "C" {
|
setState(newState);
|
||||||
MFBT_API bool IsNuwaProcess();
|
|
||||||
MFBT_API void NuwaMarkCurrentThread(void (*recreate)(void *), void *arg);
|
if (!WorkerThreadState().gcHelperWorklist().append(this))
|
||||||
}
|
CrashAtUnhandlableOOM("Could not add to pending GC helpers list");
|
||||||
|
WorkerThreadState().notifyAll(GlobalWorkerThreadState::PRODUCER);
|
||||||
|
#else
|
||||||
|
MOZ_CRASH();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* static */
|
|
||||||
void
|
|
||||||
GCHelperThread::threadMain(void *arg)
|
|
||||||
{
|
|
||||||
PR_SetCurrentThreadName("JS GC Helper");
|
|
||||||
|
|
||||||
#ifdef MOZ_NUWA_PROCESS
|
|
||||||
if (IsNuwaProcess && IsNuwaProcess()) {
|
|
||||||
JS_ASSERT(NuwaMarkCurrentThread != nullptr);
|
|
||||||
NuwaMarkCurrentThread(nullptr, nullptr);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static_cast<GCHelperThread *>(arg)->threadLoop();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
GCHelperThread::wait(PRCondVar *which)
|
GCHelperState::waitForBackgroundThread()
|
||||||
{
|
{
|
||||||
|
#ifdef JS_THREADSAFE
|
||||||
|
JS_ASSERT(CurrentThreadCanAccessRuntime(rt));
|
||||||
|
|
||||||
rt->gc.lockOwner = nullptr;
|
rt->gc.lockOwner = nullptr;
|
||||||
PR_WaitCondVar(which, PR_INTERVAL_NO_TIMEOUT);
|
PR_WaitCondVar(done, PR_INTERVAL_NO_TIMEOUT);
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
rt->gc.lockOwner = PR_GetCurrentThread();
|
rt->gc.lockOwner = PR_GetCurrentThread();
|
||||||
#endif
|
#endif
|
||||||
|
#else
|
||||||
|
MOZ_CRASH();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
GCHelperThread::threadLoop()
|
GCHelperState::work()
|
||||||
{
|
{
|
||||||
|
#ifdef JS_THREADSAFE
|
||||||
AutoLockGC lock(rt);
|
AutoLockGC lock(rt);
|
||||||
|
|
||||||
TraceLogger *logger = TraceLoggerForCurrentThread();
|
JS_ASSERT(!thread);
|
||||||
|
thread = PR_GetCurrentThread();
|
||||||
|
|
||||||
/*
|
switch (state()) {
|
||||||
* Even on the first iteration the state can be SHUTDOWN or SWEEPING if
|
|
||||||
* the stop request or the GC and the corresponding startBackgroundSweep call
|
|
||||||
* happen before this thread has a chance to run.
|
|
||||||
*/
|
|
||||||
for (;;) {
|
|
||||||
switch (state) {
|
|
||||||
case SHUTDOWN:
|
|
||||||
return;
|
|
||||||
case IDLE:
|
|
||||||
wait(wakeup);
|
|
||||||
break;
|
|
||||||
case SWEEPING: {
|
|
||||||
AutoTraceLog logSweeping(logger, TraceLogger::GCSweeping);
|
|
||||||
doSweep();
|
|
||||||
if (state == SWEEPING)
|
|
||||||
state = IDLE;
|
|
||||||
PR_NotifyAllCondVar(done);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ALLOCATING: {
|
|
||||||
AutoTraceLog logAllocating(logger, TraceLogger::GCAllocation);
|
|
||||||
do {
|
|
||||||
Chunk *chunk;
|
|
||||||
{
|
|
||||||
AutoUnlockGC unlock(rt);
|
|
||||||
chunk = Chunk::allocate(rt);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* OOM stops the background allocation. */
|
case IDLE:
|
||||||
if (!chunk)
|
MOZ_ASSUME_UNREACHABLE("GC helper triggered on idle state");
|
||||||
break;
|
break;
|
||||||
JS_ASSERT(chunk->info.numArenasFreeCommitted == 0);
|
|
||||||
rt->gc.chunkPool.put(chunk);
|
case SWEEPING: {
|
||||||
} while (state == ALLOCATING && rt->gc.wantBackgroundAllocation());
|
#if JS_TRACE_LOGGING
|
||||||
if (state == ALLOCATING)
|
AutoTraceLog logger(TraceLogging::getLogger(TraceLogging::GC_BACKGROUND),
|
||||||
state = IDLE;
|
TraceLogging::GC_SWEEPING_START,
|
||||||
break;
|
TraceLogging::GC_SWEEPING_STOP);
|
||||||
}
|
#endif
|
||||||
case CANCEL_ALLOCATION:
|
doSweep();
|
||||||
state = IDLE;
|
JS_ASSERT(state() == SWEEPING);
|
||||||
PR_NotifyAllCondVar(done);
|
break;
|
||||||
break;
|
}
|
||||||
}
|
|
||||||
|
case ALLOCATING: {
|
||||||
|
#if JS_TRACE_LOGGING
|
||||||
|
AutoTraceLog logger(TraceLogging::getLogger(TraceLogging::GC_BACKGROUND),
|
||||||
|
TraceLogging::GC_ALLOCATING_START,
|
||||||
|
TraceLogging::GC_ALLOCATING_STOP);
|
||||||
|
#endif
|
||||||
|
do {
|
||||||
|
Chunk *chunk;
|
||||||
|
{
|
||||||
|
AutoUnlockGC unlock(rt);
|
||||||
|
chunk = Chunk::allocate(rt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* OOM stops the background allocation. */
|
||||||
|
if (!chunk)
|
||||||
|
break;
|
||||||
|
JS_ASSERT(chunk->info.numArenasFreeCommitted == 0);
|
||||||
|
rt->gc.chunkPool.put(chunk);
|
||||||
|
} while (state() == ALLOCATING && rt->gc.wantBackgroundAllocation());
|
||||||
|
|
||||||
|
JS_ASSERT(state() == ALLOCATING || state() == CANCEL_ALLOCATION);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case CANCEL_ALLOCATION:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setState(IDLE);
|
||||||
|
thread = nullptr;
|
||||||
|
|
||||||
|
PR_NotifyAllCondVar(done);
|
||||||
|
#else
|
||||||
|
MOZ_CRASH();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
#endif /* JS_THREADSAFE */
|
|
||||||
|
|
||||||
void
|
void
|
||||||
GCHelperThread::startBackgroundSweep(bool shouldShrink)
|
GCHelperState::startBackgroundSweep(bool shouldShrink)
|
||||||
{
|
{
|
||||||
JS_ASSERT(rt->useHelperThreads());
|
JS_ASSERT(rt->useHelperThreads());
|
||||||
|
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
|
AutoLockWorkerThreadState workerLock;
|
||||||
AutoLockGC lock(rt);
|
AutoLockGC lock(rt);
|
||||||
JS_ASSERT(state == IDLE);
|
JS_ASSERT(state() == IDLE);
|
||||||
JS_ASSERT(!sweepFlag);
|
JS_ASSERT(!sweepFlag);
|
||||||
sweepFlag = true;
|
sweepFlag = true;
|
||||||
shrinkFlag = shouldShrink;
|
shrinkFlag = shouldShrink;
|
||||||
state = SWEEPING;
|
startBackgroundThread(SWEEPING);
|
||||||
PR_NotifyCondVar(wakeup);
|
|
||||||
#endif /* JS_THREADSAFE */
|
#endif /* JS_THREADSAFE */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Must be called with the GC lock taken. */
|
/* Must be called with the GC lock taken. */
|
||||||
void
|
void
|
||||||
GCHelperThread::startBackgroundShrink()
|
GCHelperState::startBackgroundShrink()
|
||||||
{
|
{
|
||||||
JS_ASSERT(rt->useHelperThreads());
|
JS_ASSERT(rt->useHelperThreads());
|
||||||
|
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
switch (state) {
|
switch (state()) {
|
||||||
case IDLE:
|
case IDLE:
|
||||||
JS_ASSERT(!sweepFlag);
|
JS_ASSERT(!sweepFlag);
|
||||||
shrinkFlag = true;
|
shrinkFlag = true;
|
||||||
state = SWEEPING;
|
startBackgroundThread(SWEEPING);
|
||||||
PR_NotifyCondVar(wakeup);
|
|
||||||
break;
|
break;
|
||||||
case SWEEPING:
|
case SWEEPING:
|
||||||
shrinkFlag = true;
|
shrinkFlag = true;
|
||||||
@ -2499,43 +2531,41 @@ GCHelperThread::startBackgroundShrink()
|
|||||||
* shrink.
|
* shrink.
|
||||||
*/
|
*/
|
||||||
break;
|
break;
|
||||||
case SHUTDOWN:
|
|
||||||
MOZ_ASSUME_UNREACHABLE("No shrink on shutdown");
|
|
||||||
}
|
}
|
||||||
#endif /* JS_THREADSAFE */
|
#endif /* JS_THREADSAFE */
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
GCHelperThread::waitBackgroundSweepEnd()
|
GCHelperState::waitBackgroundSweepEnd()
|
||||||
{
|
{
|
||||||
if (!rt->useHelperThreads()) {
|
if (!rt->useHelperThreads()) {
|
||||||
JS_ASSERT(state == IDLE);
|
JS_ASSERT(state_ == IDLE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
AutoLockGC lock(rt);
|
AutoLockGC lock(rt);
|
||||||
while (state == SWEEPING)
|
while (state() == SWEEPING)
|
||||||
wait(done);
|
waitForBackgroundThread();
|
||||||
if (rt->gc.incrementalState == NO_INCREMENTAL)
|
if (rt->gc.incrementalState == NO_INCREMENTAL)
|
||||||
AssertBackgroundSweepingFinished(rt);
|
AssertBackgroundSweepingFinished(rt);
|
||||||
#endif /* JS_THREADSAFE */
|
#endif /* JS_THREADSAFE */
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
GCHelperThread::waitBackgroundSweepOrAllocEnd()
|
GCHelperState::waitBackgroundSweepOrAllocEnd()
|
||||||
{
|
{
|
||||||
if (!rt->useHelperThreads()) {
|
if (!rt->useHelperThreads()) {
|
||||||
JS_ASSERT(state == IDLE);
|
JS_ASSERT(state_ == IDLE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
AutoLockGC lock(rt);
|
AutoLockGC lock(rt);
|
||||||
if (state == ALLOCATING)
|
if (state() == ALLOCATING)
|
||||||
state = CANCEL_ALLOCATION;
|
setState(CANCEL_ALLOCATION);
|
||||||
while (state == SWEEPING || state == CANCEL_ALLOCATION)
|
while (state() == SWEEPING || state() == CANCEL_ALLOCATION)
|
||||||
wait(done);
|
waitForBackgroundThread();
|
||||||
if (rt->gc.incrementalState == NO_INCREMENTAL)
|
if (rt->gc.incrementalState == NO_INCREMENTAL)
|
||||||
AssertBackgroundSweepingFinished(rt);
|
AssertBackgroundSweepingFinished(rt);
|
||||||
#endif /* JS_THREADSAFE */
|
#endif /* JS_THREADSAFE */
|
||||||
@ -2543,20 +2573,18 @@ GCHelperThread::waitBackgroundSweepOrAllocEnd()
|
|||||||
|
|
||||||
/* Must be called with the GC lock taken. */
|
/* Must be called with the GC lock taken. */
|
||||||
inline void
|
inline void
|
||||||
GCHelperThread::startBackgroundAllocationIfIdle()
|
GCHelperState::startBackgroundAllocationIfIdle()
|
||||||
{
|
{
|
||||||
JS_ASSERT(rt->useHelperThreads());
|
JS_ASSERT(rt->useHelperThreads());
|
||||||
|
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
if (state == IDLE) {
|
if (state_ == IDLE)
|
||||||
state = ALLOCATING;
|
startBackgroundThread(ALLOCATING);
|
||||||
PR_NotifyCondVar(wakeup);
|
|
||||||
}
|
|
||||||
#endif /* JS_THREADSAFE */
|
#endif /* JS_THREADSAFE */
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
GCHelperThread::replenishAndFreeLater(void *ptr)
|
GCHelperState::replenishAndFreeLater(void *ptr)
|
||||||
{
|
{
|
||||||
JS_ASSERT(freeCursor == freeCursorEnd);
|
JS_ASSERT(freeCursor == freeCursorEnd);
|
||||||
do {
|
do {
|
||||||
@ -2577,7 +2605,7 @@ GCHelperThread::replenishAndFreeLater(void *ptr)
|
|||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
/* Must be called with the GC lock taken. */
|
/* Must be called with the GC lock taken. */
|
||||||
void
|
void
|
||||||
GCHelperThread::doSweep()
|
GCHelperState::doSweep()
|
||||||
{
|
{
|
||||||
if (sweepFlag) {
|
if (sweepFlag) {
|
||||||
sweepFlag = false;
|
sweepFlag = false;
|
||||||
@ -2617,10 +2645,10 @@ GCHelperThread::doSweep()
|
|||||||
#endif /* JS_THREADSAFE */
|
#endif /* JS_THREADSAFE */
|
||||||
|
|
||||||
bool
|
bool
|
||||||
GCHelperThread::onBackgroundThread()
|
GCHelperState::onBackgroundThread()
|
||||||
{
|
{
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
return PR_GetCurrentThread() == getThread();
|
return PR_GetCurrentThread() == thread;
|
||||||
#else
|
#else
|
||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
@ -4143,7 +4171,7 @@ GCRuntime::endSweepPhase(JSGCInvocationKind gckind, bool lastGC)
|
|||||||
/*
|
/*
|
||||||
* Destroy arenas after we finished the sweeping so finalizers can
|
* Destroy arenas after we finished the sweeping so finalizers can
|
||||||
* safely use IsAboutToBeFinalized(). This is done on the
|
* safely use IsAboutToBeFinalized(). This is done on the
|
||||||
* GCHelperThread if possible. We acquire the lock only because
|
* GCHelperState if possible. We acquire the lock only because
|
||||||
* Expire needs to unlock it for other callers.
|
* Expire needs to unlock it for other callers.
|
||||||
*/
|
*/
|
||||||
AutoLockGC lock(rt);
|
AutoLockGC lock(rt);
|
||||||
@ -4407,7 +4435,7 @@ GCRuntime::resetIncrementalGC(const char *reason)
|
|||||||
|
|
||||||
{
|
{
|
||||||
gcstats::AutoPhase ap(stats, gcstats::PHASE_WAIT_BACKGROUND_THREAD);
|
gcstats::AutoPhase ap(stats, gcstats::PHASE_WAIT_BACKGROUND_THREAD);
|
||||||
helperThread.waitBackgroundSweepOrAllocEnd();
|
rt->gc.waitBackgroundSweepOrAllocEnd();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -4618,7 +4646,7 @@ GCRuntime::incrementalCollectSlice(int64_t budget,
|
|||||||
endSweepPhase(gckind, lastGC);
|
endSweepPhase(gckind, lastGC);
|
||||||
|
|
||||||
if (sweepOnBackgroundThread)
|
if (sweepOnBackgroundThread)
|
||||||
helperThread.startBackgroundSweep(gckind == GC_SHRINK);
|
helperState.startBackgroundSweep(gckind == GC_SHRINK);
|
||||||
|
|
||||||
incrementalState = NO_INCREMENTAL;
|
incrementalState = NO_INCREMENTAL;
|
||||||
break;
|
break;
|
||||||
@ -4712,7 +4740,7 @@ GCRuntime::gcCycle(bool incremental, int64_t budget, JSGCInvocationKind gckind,
|
|||||||
*/
|
*/
|
||||||
{
|
{
|
||||||
gcstats::AutoPhase ap(stats, gcstats::PHASE_WAIT_BACKGROUND_THREAD);
|
gcstats::AutoPhase ap(stats, gcstats::PHASE_WAIT_BACKGROUND_THREAD);
|
||||||
helperThread.waitBackgroundSweepOrAllocEnd();
|
waitBackgroundSweepOrAllocEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
State prevState = incrementalState;
|
State prevState = incrementalState;
|
||||||
@ -4959,6 +4987,7 @@ js::PrepareForDebugGC(JSRuntime *rt)
|
|||||||
JS_FRIEND_API(void)
|
JS_FRIEND_API(void)
|
||||||
JS::ShrinkGCBuffers(JSRuntime *rt)
|
JS::ShrinkGCBuffers(JSRuntime *rt)
|
||||||
{
|
{
|
||||||
|
AutoLockWorkerThreadState workerLock;
|
||||||
AutoLockGC lock(rt);
|
AutoLockGC lock(rt);
|
||||||
JS_ASSERT(!rt->isHeapBusy());
|
JS_ASSERT(!rt->isHeapBusy());
|
||||||
|
|
||||||
|
@ -33,7 +33,6 @@ class ArrayBufferViewObject;
|
|||||||
class SharedArrayBufferObject;
|
class SharedArrayBufferObject;
|
||||||
class BaseShape;
|
class BaseShape;
|
||||||
class DebugScopeObject;
|
class DebugScopeObject;
|
||||||
class GCHelperThread;
|
|
||||||
class GlobalObject;
|
class GlobalObject;
|
||||||
class LazyScript;
|
class LazyScript;
|
||||||
class Nursery;
|
class Nursery;
|
||||||
@ -355,6 +354,12 @@ GetGCKindSlots(AllocKind thingKind, const Class *clasp)
|
|||||||
return nslots;
|
return nslots;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Class to assist in triggering background chunk allocation. This cannot be done
|
||||||
|
// while holding the GC or worker thread state lock due to lock ordering issues.
|
||||||
|
// As a result, the triggering is delayed using this class until neither of the
|
||||||
|
// above locks is held.
|
||||||
|
class AutoMaybeStartBackgroundAllocation;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Arena lists have a head and a cursor. The cursor conceptually lies on arena
|
* Arena lists have a head and a cursor. The cursor conceptually lies on arena
|
||||||
* boundaries, i.e. before the first arena, between two arenas, or after the
|
* boundaries, i.e. before the first arena, between two arenas, or after the
|
||||||
@ -771,7 +776,8 @@ class ArenaLists
|
|||||||
inline void queueForBackgroundSweep(FreeOp *fop, AllocKind thingKind);
|
inline void queueForBackgroundSweep(FreeOp *fop, AllocKind thingKind);
|
||||||
|
|
||||||
void *allocateFromArena(JS::Zone *zone, AllocKind thingKind);
|
void *allocateFromArena(JS::Zone *zone, AllocKind thingKind);
|
||||||
inline void *allocateFromArenaInline(JS::Zone *zone, AllocKind thingKind);
|
inline void *allocateFromArenaInline(JS::Zone *zone, AllocKind thingKind,
|
||||||
|
AutoMaybeStartBackgroundAllocation &maybeStartBackgroundAllocation);
|
||||||
|
|
||||||
inline void normalizeBackgroundFinalizeState(AllocKind thingKind);
|
inline void normalizeBackgroundFinalizeState(AllocKind thingKind);
|
||||||
|
|
||||||
@ -914,20 +920,20 @@ extern void
|
|||||||
NotifyGCPostSwap(JSObject *a, JSObject *b, unsigned preResult);
|
NotifyGCPostSwap(JSObject *a, JSObject *b, unsigned preResult);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Helper that implements sweeping and allocation for kinds that can be swept
|
* Helper state for use when JS helper threads sweep and allocate GC thing kinds
|
||||||
* and allocated off the main thread.
|
* that can be swept and allocated off the main thread.
|
||||||
*
|
*
|
||||||
* In non-threadsafe builds, all actual sweeping and allocation is performed
|
* In non-threadsafe builds, all actual sweeping and allocation is performed
|
||||||
* on the main thread, but GCHelperThread encapsulates this from clients as
|
* on the main thread, but GCHelperState encapsulates this from clients as
|
||||||
* much as possible.
|
* much as possible.
|
||||||
*/
|
*/
|
||||||
class GCHelperThread {
|
class GCHelperState
|
||||||
|
{
|
||||||
enum State {
|
enum State {
|
||||||
IDLE,
|
IDLE,
|
||||||
SWEEPING,
|
SWEEPING,
|
||||||
ALLOCATING,
|
ALLOCATING,
|
||||||
CANCEL_ALLOCATION,
|
CANCEL_ALLOCATION
|
||||||
SHUTDOWN
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -943,13 +949,25 @@ class GCHelperThread {
|
|||||||
static const size_t FREE_ARRAY_SIZE = size_t(1) << 16;
|
static const size_t FREE_ARRAY_SIZE = size_t(1) << 16;
|
||||||
static const size_t FREE_ARRAY_LENGTH = FREE_ARRAY_SIZE / sizeof(void *);
|
static const size_t FREE_ARRAY_LENGTH = FREE_ARRAY_SIZE / sizeof(void *);
|
||||||
|
|
||||||
JSRuntime *const rt;
|
// Associated runtime.
|
||||||
PRThread *thread;
|
JSRuntime *const rt;
|
||||||
PRCondVar *wakeup;
|
|
||||||
PRCondVar *done;
|
|
||||||
volatile State state;
|
|
||||||
|
|
||||||
void wait(PRCondVar *which);
|
// Condvar for notifying the main thread when work has finished. This is
|
||||||
|
// associated with the runtime's GC lock --- the worker thread state
|
||||||
|
// condvars can't be used here due to lock ordering issues.
|
||||||
|
PRCondVar *done;
|
||||||
|
|
||||||
|
// Activity for the helper to do, protected by the GC lock.
|
||||||
|
State state_;
|
||||||
|
|
||||||
|
// Thread which work is being performed on, or null.
|
||||||
|
PRThread *thread;
|
||||||
|
|
||||||
|
void startBackgroundThread(State newState);
|
||||||
|
void waitForBackgroundThread();
|
||||||
|
|
||||||
|
State state();
|
||||||
|
void setState(State state);
|
||||||
|
|
||||||
bool sweepFlag;
|
bool sweepFlag;
|
||||||
bool shrinkFlag;
|
bool shrinkFlag;
|
||||||
@ -972,19 +990,15 @@ class GCHelperThread {
|
|||||||
js_free(array);
|
js_free(array);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void threadMain(void* arg);
|
|
||||||
void threadLoop();
|
|
||||||
|
|
||||||
/* Must be called with the GC lock taken. */
|
/* Must be called with the GC lock taken. */
|
||||||
void doSweep();
|
void doSweep();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
GCHelperThread(JSRuntime *rt)
|
GCHelperState(JSRuntime *rt)
|
||||||
: rt(rt),
|
: rt(rt),
|
||||||
thread(nullptr),
|
|
||||||
wakeup(nullptr),
|
|
||||||
done(nullptr),
|
done(nullptr),
|
||||||
state(IDLE),
|
state_(IDLE),
|
||||||
|
thread(nullptr),
|
||||||
sweepFlag(false),
|
sweepFlag(false),
|
||||||
shrinkFlag(false),
|
shrinkFlag(false),
|
||||||
freeCursor(nullptr),
|
freeCursor(nullptr),
|
||||||
@ -995,6 +1009,8 @@ class GCHelperThread {
|
|||||||
bool init();
|
bool init();
|
||||||
void finish();
|
void finish();
|
||||||
|
|
||||||
|
void work();
|
||||||
|
|
||||||
/* Must be called with the GC lock taken. */
|
/* Must be called with the GC lock taken. */
|
||||||
void startBackgroundSweep(bool shouldShrink);
|
void startBackgroundSweep(bool shouldShrink);
|
||||||
|
|
||||||
@ -1008,7 +1024,7 @@ class GCHelperThread {
|
|||||||
void waitBackgroundSweepOrAllocEnd();
|
void waitBackgroundSweepOrAllocEnd();
|
||||||
|
|
||||||
/* Must be called with the GC lock taken. */
|
/* Must be called with the GC lock taken. */
|
||||||
inline void startBackgroundAllocationIfIdle();
|
void startBackgroundAllocationIfIdle();
|
||||||
|
|
||||||
bool canBackgroundAllocate() const {
|
bool canBackgroundAllocate() const {
|
||||||
return backgroundAllocation;
|
return backgroundAllocation;
|
||||||
@ -1018,27 +1034,23 @@ class GCHelperThread {
|
|||||||
backgroundAllocation = false;
|
backgroundAllocation = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
PRThread *getThread() const {
|
|
||||||
return thread;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool onBackgroundThread();
|
bool onBackgroundThread();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Outside the GC lock may give true answer when in fact the sweeping has
|
* Outside the GC lock may give true answer when in fact the sweeping has
|
||||||
* been done.
|
* been done.
|
||||||
*/
|
*/
|
||||||
bool sweeping() const {
|
bool isBackgroundSweeping() const {
|
||||||
return state == SWEEPING;
|
return state_ == SWEEPING;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool shouldShrink() const {
|
bool shouldShrink() const {
|
||||||
JS_ASSERT(sweeping());
|
JS_ASSERT(isBackgroundSweeping());
|
||||||
return shrinkFlag;
|
return shrinkFlag;
|
||||||
}
|
}
|
||||||
|
|
||||||
void freeLater(void *ptr) {
|
void freeLater(void *ptr) {
|
||||||
JS_ASSERT(!sweeping());
|
JS_ASSERT(!isBackgroundSweeping());
|
||||||
if (freeCursor != freeCursorEnd)
|
if (freeCursor != freeCursorEnd)
|
||||||
*freeCursor++ = ptr;
|
*freeCursor++ = ptr;
|
||||||
else
|
else
|
||||||
|
@ -578,6 +578,12 @@ GlobalWorkerThreadState::canStartCompressionTask()
|
|||||||
return !compressionWorklist().empty();
|
return !compressionWorklist().empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
GlobalWorkerThreadState::canStartGCHelperTask()
|
||||||
|
{
|
||||||
|
return !gcHelperWorklist().empty();
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
CallNewScriptHookForAllScripts(JSContext *cx, HandleScript script)
|
CallNewScriptHookForAllScripts(JSContext *cx, HandleScript script)
|
||||||
{
|
{
|
||||||
@ -1020,6 +1026,24 @@ GlobalWorkerThreadState::compressionTaskForSource(ScriptSource *ss)
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
WorkerThread::handleGCHelperWorkload()
|
||||||
|
{
|
||||||
|
JS_ASSERT(WorkerThreadState().isLocked());
|
||||||
|
JS_ASSERT(WorkerThreadState().canStartGCHelperTask());
|
||||||
|
JS_ASSERT(idle());
|
||||||
|
|
||||||
|
JS_ASSERT(!gcHelperState);
|
||||||
|
gcHelperState = WorkerThreadState().gcHelperWorklist().popCopy();
|
||||||
|
|
||||||
|
{
|
||||||
|
AutoUnlockWorkerThreadState unlock;
|
||||||
|
gcHelperState->work();
|
||||||
|
}
|
||||||
|
|
||||||
|
gcHelperState = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
WorkerThread::threadLoop()
|
WorkerThread::threadLoop()
|
||||||
{
|
{
|
||||||
@ -1048,7 +1072,8 @@ WorkerThread::threadLoop()
|
|||||||
if (WorkerThreadState().canStartIonCompile() ||
|
if (WorkerThreadState().canStartIonCompile() ||
|
||||||
WorkerThreadState().canStartAsmJSCompile() ||
|
WorkerThreadState().canStartAsmJSCompile() ||
|
||||||
WorkerThreadState().canStartParseTask() ||
|
WorkerThreadState().canStartParseTask() ||
|
||||||
WorkerThreadState().canStartCompressionTask())
|
WorkerThreadState().canStartCompressionTask() ||
|
||||||
|
WorkerThreadState().canStartGCHelperTask())
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1064,6 +1089,8 @@ WorkerThread::threadLoop()
|
|||||||
handleParseWorkload();
|
handleParseWorkload();
|
||||||
else if (WorkerThreadState().canStartCompressionTask())
|
else if (WorkerThreadState().canStartCompressionTask())
|
||||||
handleCompressionWorkload();
|
handleCompressionWorkload();
|
||||||
|
else if (WorkerThreadState().canStartGCHelperTask())
|
||||||
|
handleGCHelperWorkload();
|
||||||
else
|
else
|
||||||
MOZ_ASSUME_UNREACHABLE("No task to perform");
|
MOZ_ASSUME_UNREACHABLE("No task to perform");
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,7 @@ class GlobalWorkerThreadState
|
|||||||
typedef Vector<AsmJSParallelTask*, 0, SystemAllocPolicy> AsmJSParallelTaskVector;
|
typedef Vector<AsmJSParallelTask*, 0, SystemAllocPolicy> AsmJSParallelTaskVector;
|
||||||
typedef Vector<ParseTask*, 0, SystemAllocPolicy> ParseTaskVector;
|
typedef Vector<ParseTask*, 0, SystemAllocPolicy> ParseTaskVector;
|
||||||
typedef Vector<SourceCompressionTask*, 0, SystemAllocPolicy> SourceCompressionTaskVector;
|
typedef Vector<SourceCompressionTask*, 0, SystemAllocPolicy> SourceCompressionTaskVector;
|
||||||
|
typedef Vector<GCHelperState *, 0, SystemAllocPolicy> GCHelperStateVector;
|
||||||
|
|
||||||
// List of available threads, or null if the thread state has not been initialized.
|
// List of available threads, or null if the thread state has not been initialized.
|
||||||
WorkerThread *threads;
|
WorkerThread *threads;
|
||||||
@ -81,6 +82,9 @@ class GlobalWorkerThreadState
|
|||||||
// Source compression worklist.
|
// Source compression worklist.
|
||||||
SourceCompressionTaskVector compressionWorklist_;
|
SourceCompressionTaskVector compressionWorklist_;
|
||||||
|
|
||||||
|
// Runtimes which have sweeping / allocating work to do.
|
||||||
|
GCHelperStateVector gcHelperWorklist_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
GlobalWorkerThreadState();
|
GlobalWorkerThreadState();
|
||||||
|
|
||||||
@ -150,10 +154,16 @@ class GlobalWorkerThreadState
|
|||||||
return compressionWorklist_;
|
return compressionWorklist_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GCHelperStateVector &gcHelperWorklist() {
|
||||||
|
JS_ASSERT(isLocked());
|
||||||
|
return gcHelperWorklist_;
|
||||||
|
}
|
||||||
|
|
||||||
bool canStartAsmJSCompile();
|
bool canStartAsmJSCompile();
|
||||||
bool canStartIonCompile();
|
bool canStartIonCompile();
|
||||||
bool canStartParseTask();
|
bool canStartParseTask();
|
||||||
bool canStartCompressionTask();
|
bool canStartCompressionTask();
|
||||||
|
bool canStartGCHelperTask();
|
||||||
|
|
||||||
uint32_t harvestFailedAsmJSJobs() {
|
uint32_t harvestFailedAsmJSJobs() {
|
||||||
JS_ASSERT(isLocked());
|
JS_ASSERT(isLocked());
|
||||||
@ -240,8 +250,11 @@ struct WorkerThread
|
|||||||
/* Any source being compressed on this thread. */
|
/* Any source being compressed on this thread. */
|
||||||
SourceCompressionTask *compressionTask;
|
SourceCompressionTask *compressionTask;
|
||||||
|
|
||||||
|
/* Any GC state for background sweeping or allocating being performed. */
|
||||||
|
GCHelperState *gcHelperState;
|
||||||
|
|
||||||
bool idle() const {
|
bool idle() const {
|
||||||
return !ionBuilder && !asmData && !parseTask && !compressionTask;
|
return !ionBuilder && !asmData && !parseTask && !compressionTask && !gcHelperState;
|
||||||
}
|
}
|
||||||
|
|
||||||
void destroy();
|
void destroy();
|
||||||
@ -250,6 +263,7 @@ struct WorkerThread
|
|||||||
void handleIonWorkload();
|
void handleIonWorkload();
|
||||||
void handleParseWorkload();
|
void handleParseWorkload();
|
||||||
void handleCompressionWorkload();
|
void handleCompressionWorkload();
|
||||||
|
void handleGCHelperWorkload();
|
||||||
|
|
||||||
static void ThreadMain(void *arg);
|
static void ThreadMain(void *arg);
|
||||||
void threadLoop();
|
void threadLoop();
|
||||||
|
Loading…
Reference in New Issue
Block a user