mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 928050 - Remove worker pausing mechanism, r=billm.
This commit is contained in:
parent
5c6a926e44
commit
8df8a7201a
@ -4788,8 +4788,6 @@ EmitFor(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top
|
|||||||
static JS_NEVER_INLINE bool
|
static JS_NEVER_INLINE bool
|
||||||
EmitFunc(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
EmitFunc(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||||
{
|
{
|
||||||
cx->maybePause();
|
|
||||||
|
|
||||||
FunctionBox *funbox = pn->pn_funbox;
|
FunctionBox *funbox = pn->pn_funbox;
|
||||||
RootedFunction fun(cx, funbox->function());
|
RootedFunction fun(cx, funbox->function());
|
||||||
JS_ASSERT_IF(fun->isInterpretedLazy(), fun->lazyScript());
|
JS_ASSERT_IF(fun->isInterpretedLazy(), fun->lazyScript());
|
||||||
|
@ -402,7 +402,10 @@ Parser<ParseHandler>::Parser(ExclusiveContext *cx, LifoAlloc *alloc,
|
|||||||
isUnexpectedEOF_(false),
|
isUnexpectedEOF_(false),
|
||||||
handler(cx, *alloc, tokenStream, foldConstants, syntaxParser, lazyOuterFunction)
|
handler(cx, *alloc, tokenStream, foldConstants, syntaxParser, lazyOuterFunction)
|
||||||
{
|
{
|
||||||
cx->perThreadData->activeCompilations++;
|
{
|
||||||
|
AutoLockForExclusiveAccess lock(cx);
|
||||||
|
cx->perThreadData->addActiveCompilation();
|
||||||
|
}
|
||||||
|
|
||||||
// The Mozilla specific JSOPTION_EXTRA_WARNINGS option adds extra warnings
|
// The Mozilla specific JSOPTION_EXTRA_WARNINGS option adds extra warnings
|
||||||
// which are not generated if functions are parsed lazily. Note that the
|
// which are not generated if functions are parsed lazily. Note that the
|
||||||
@ -416,8 +419,6 @@ Parser<ParseHandler>::Parser(ExclusiveContext *cx, LifoAlloc *alloc,
|
|||||||
template <typename ParseHandler>
|
template <typename ParseHandler>
|
||||||
Parser<ParseHandler>::~Parser()
|
Parser<ParseHandler>::~Parser()
|
||||||
{
|
{
|
||||||
context->perThreadData->activeCompilations--;
|
|
||||||
|
|
||||||
alloc.release(tempPoolMark);
|
alloc.release(tempPoolMark);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -426,6 +427,11 @@ Parser<ParseHandler>::~Parser()
|
|||||||
* next GC) to avoid unnecessary OOMs.
|
* next GC) to avoid unnecessary OOMs.
|
||||||
*/
|
*/
|
||||||
alloc.freeAllIfHugeAndUnused();
|
alloc.freeAllIfHugeAndUnused();
|
||||||
|
|
||||||
|
{
|
||||||
|
AutoLockForExclusiveAccess lock(context);
|
||||||
|
context->perThreadData->removeActiveCompilation();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ParseHandler>
|
template <typename ParseHandler>
|
||||||
@ -2245,8 +2251,6 @@ Parser<ParseHandler>::functionArgsAndBodyGeneric(Node pn, HandleFunction fun, Fu
|
|||||||
// function without concern for conversion to strict mode, use of lazy
|
// function without concern for conversion to strict mode, use of lazy
|
||||||
// parsing and such.
|
// parsing and such.
|
||||||
|
|
||||||
context->maybePause();
|
|
||||||
|
|
||||||
Node prelude = null();
|
Node prelude = null();
|
||||||
bool hasRest;
|
bool hasRest;
|
||||||
if (!functionArguments(kind, &prelude, pn, &hasRest))
|
if (!functionArguments(kind, &prelude, pn, &hasRest))
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#ifndef gc_GCInternals_h
|
#ifndef gc_GCInternals_h
|
||||||
#define gc_GCInternals_h
|
#define gc_GCInternals_h
|
||||||
|
|
||||||
|
#include "jscntxt.h"
|
||||||
#include "jsworkers.h"
|
#include "jsworkers.h"
|
||||||
|
|
||||||
#include "gc/Zone.h"
|
#include "gc/Zone.h"
|
||||||
@ -51,19 +52,19 @@ class AutoTraceSession
|
|||||||
~AutoTraceSession();
|
~AutoTraceSession();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
AutoLockForExclusiveAccess lock;
|
||||||
JSRuntime *runtime;
|
JSRuntime *runtime;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
AutoTraceSession(const AutoTraceSession&) MOZ_DELETE;
|
AutoTraceSession(const AutoTraceSession&) MOZ_DELETE;
|
||||||
void operator=(const AutoTraceSession&) MOZ_DELETE;
|
void operator=(const AutoTraceSession&) MOZ_DELETE;
|
||||||
|
|
||||||
js::HeapState prevState;
|
HeapState prevState;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AutoPrepareForTracing
|
struct AutoPrepareForTracing
|
||||||
{
|
{
|
||||||
AutoFinishGC finish;
|
AutoFinishGC finish;
|
||||||
AutoPauseWorkersForTracing pause;
|
|
||||||
AutoTraceSession session;
|
AutoTraceSession session;
|
||||||
AutoCopyFreeListToArenas copy;
|
AutoCopyFreeListToArenas copy;
|
||||||
|
|
||||||
|
@ -22,7 +22,6 @@ js::TraceRuntime(JSTracer *trc)
|
|||||||
{
|
{
|
||||||
JS_ASSERT(!IS_GC_MARKING_TRACER(trc));
|
JS_ASSERT(!IS_GC_MARKING_TRACER(trc));
|
||||||
|
|
||||||
AutoLockForExclusiveAccess lock(trc->runtime);
|
|
||||||
AutoPrepareForTracing prep(trc->runtime, WithAtoms);
|
AutoPrepareForTracing prep(trc->runtime, WithAtoms);
|
||||||
MarkRuntime(trc);
|
MarkRuntime(trc);
|
||||||
}
|
}
|
||||||
@ -56,7 +55,6 @@ js::IterateZonesCompartmentsArenasCells(JSRuntime *rt, void *data,
|
|||||||
IterateArenaCallback arenaCallback,
|
IterateArenaCallback arenaCallback,
|
||||||
IterateCellCallback cellCallback)
|
IterateCellCallback cellCallback)
|
||||||
{
|
{
|
||||||
AutoLockForExclusiveAccess lock(rt);
|
|
||||||
AutoPrepareForTracing prop(rt, WithAtoms);
|
AutoPrepareForTracing prop(rt, WithAtoms);
|
||||||
|
|
||||||
for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
|
for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
|
||||||
@ -73,7 +71,6 @@ js::IterateZoneCompartmentsArenasCells(JSRuntime *rt, Zone *zone, void *data,
|
|||||||
IterateArenaCallback arenaCallback,
|
IterateArenaCallback arenaCallback,
|
||||||
IterateCellCallback cellCallback)
|
IterateCellCallback cellCallback)
|
||||||
{
|
{
|
||||||
AutoLockForExclusiveAccess lock(rt);
|
|
||||||
AutoPrepareForTracing prop(rt, WithAtoms);
|
AutoPrepareForTracing prop(rt, WithAtoms);
|
||||||
|
|
||||||
(*zoneCallback)(rt, data, zone);
|
(*zoneCallback)(rt, data, zone);
|
||||||
@ -130,8 +127,6 @@ JS_IterateCompartments(JSRuntime *rt, void *data,
|
|||||||
{
|
{
|
||||||
JS_ASSERT(!rt->isHeapBusy());
|
JS_ASSERT(!rt->isHeapBusy());
|
||||||
|
|
||||||
AutoLockForExclusiveAccess lock(rt);
|
|
||||||
AutoPauseWorkersForTracing pause(rt);
|
|
||||||
AutoTraceSession session(rt);
|
AutoTraceSession session(rt);
|
||||||
|
|
||||||
for (CompartmentsIter c(rt, WithAtoms); !c.done(); c.next())
|
for (CompartmentsIter c(rt, WithAtoms); !c.done(); c.next())
|
||||||
|
@ -707,15 +707,14 @@ js::gc::MarkRuntime(JSTracer *trc, bool useSavedRoots)
|
|||||||
MarkScriptRoot(trc, &vec[i].script, "scriptAndCountsVector");
|
MarkScriptRoot(trc, &vec[i].script, "scriptAndCountsVector");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!rt->isBeingDestroyed() &&
|
if (!rt->isBeingDestroyed() && !trc->runtime->isHeapMinorCollecting()) {
|
||||||
!trc->runtime->isHeapMinorCollecting() &&
|
if (!IS_GC_MARKING_TRACER(trc) || rt->atomsCompartment()->zone()->isCollecting()) {
|
||||||
(!IS_GC_MARKING_TRACER(trc) || rt->atomsCompartment()->zone()->isCollecting()))
|
MarkAtoms(trc);
|
||||||
{
|
rt->staticStrings.trace(trc);
|
||||||
MarkAtoms(trc);
|
|
||||||
rt->staticStrings.trace(trc);
|
|
||||||
#ifdef JS_ION
|
#ifdef JS_ION
|
||||||
jit::JitRuntime::Mark(trc);
|
jit::JitRuntime::Mark(trc);
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (ContextIter acx(rt); !acx.done(); acx.next())
|
for (ContextIter acx(rt); !acx.done(); acx.next())
|
||||||
|
@ -448,7 +448,6 @@ gc::StartVerifyPreBarriers(JSRuntime *rt)
|
|||||||
|
|
||||||
MinorGC(rt, JS::gcreason::API);
|
MinorGC(rt, JS::gcreason::API);
|
||||||
|
|
||||||
AutoLockForExclusiveAccess lock(rt);
|
|
||||||
AutoPrepareForTracing prep(rt, WithAtoms);
|
AutoPrepareForTracing prep(rt, WithAtoms);
|
||||||
|
|
||||||
if (!IsIncrementalGCSafe(rt))
|
if (!IsIncrementalGCSafe(rt))
|
||||||
@ -814,6 +813,7 @@ MaybeVerifyPreBarriers(JSRuntime *rt, bool always)
|
|||||||
|
|
||||||
EndVerifyPreBarriers(rt);
|
EndVerifyPreBarriers(rt);
|
||||||
}
|
}
|
||||||
|
|
||||||
StartVerifyPreBarriers(rt);
|
StartVerifyPreBarriers(rt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,6 +237,14 @@ Zone::discardJitCode(FreeOp *fop)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint64_t
|
||||||
|
Zone::gcNumber()
|
||||||
|
{
|
||||||
|
// Zones in use by exclusive threads are not collected, and threads using
|
||||||
|
// them cannot access the main runtime's gcNumber without racing.
|
||||||
|
return usedByExclusiveThread ? 0 : runtimeFromMainThread()->gcNumber;
|
||||||
|
}
|
||||||
|
|
||||||
JS::Zone *
|
JS::Zone *
|
||||||
js::ZoneOfObject(const JSObject &obj)
|
js::ZoneOfObject(const JSObject &obj)
|
||||||
{
|
{
|
||||||
|
@ -193,7 +193,7 @@ struct Zone : public JS::shadow::Zone,
|
|||||||
// Zones cannot be collected while in use by other threads.
|
// Zones cannot be collected while in use by other threads.
|
||||||
if (usedByExclusiveThread)
|
if (usedByExclusiveThread)
|
||||||
return false;
|
return false;
|
||||||
JSRuntime *rt = runtimeFromMainThread();
|
JSRuntime *rt = runtimeFromAnyThread();
|
||||||
if (rt->isAtomsZone(this) && rt->exclusiveThreadsPresent())
|
if (rt->isAtomsZone(this) && rt->exclusiveThreadsPresent())
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
@ -238,6 +238,12 @@ struct Zone : public JS::shadow::Zone,
|
|||||||
/* Whether this zone is being used by a thread with an ExclusiveContext. */
|
/* Whether this zone is being used by a thread with an ExclusiveContext. */
|
||||||
bool usedByExclusiveThread;
|
bool usedByExclusiveThread;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get a number that is incremented whenever this zone is collected, and
|
||||||
|
* possibly at other times too.
|
||||||
|
*/
|
||||||
|
uint64_t gcNumber();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* These flags help us to discover if a compartment that shouldn't be alive
|
* These flags help us to discover if a compartment that shouldn't be alive
|
||||||
* manages to outlive a GC.
|
* manages to outlive a GC.
|
||||||
|
@ -5119,7 +5119,6 @@ static AsmJSParallelTask *
|
|||||||
GetFinishedCompilation(ModuleCompiler &m, ParallelGroupState &group)
|
GetFinishedCompilation(ModuleCompiler &m, ParallelGroupState &group)
|
||||||
{
|
{
|
||||||
AutoLockWorkerThreadState lock(*m.cx()->workerThreadState());
|
AutoLockWorkerThreadState lock(*m.cx()->workerThreadState());
|
||||||
AutoPauseCurrentWorkerThread maybePause(m.cx());
|
|
||||||
|
|
||||||
while (!group.state.asmJSWorkerFailed()) {
|
while (!group.state.asmJSWorkerFailed()) {
|
||||||
if (!group.state.asmJSFinishedList.empty()) {
|
if (!group.state.asmJSFinishedList.empty()) {
|
||||||
@ -5244,8 +5243,6 @@ CancelOutstandingJobs(ModuleCompiler &m, ParallelGroupState &group)
|
|||||||
// Eliminate tasks that failed without adding to the finished list.
|
// Eliminate tasks that failed without adding to the finished list.
|
||||||
group.outstandingJobs -= group.state.harvestFailedAsmJSJobs();
|
group.outstandingJobs -= group.state.harvestFailedAsmJSJobs();
|
||||||
|
|
||||||
AutoPauseCurrentWorkerThread maybePause(m.cx());
|
|
||||||
|
|
||||||
// Any remaining tasks are therefore undergoing active compilation.
|
// Any remaining tasks are therefore undergoing active compilation.
|
||||||
JS_ASSERT(group.outstandingJobs >= 0);
|
JS_ASSERT(group.outstandingJobs >= 0);
|
||||||
while (group.outstandingJobs > 0) {
|
while (group.outstandingJobs > 0) {
|
||||||
|
@ -200,9 +200,9 @@ JitRuntime::~JitRuntime()
|
|||||||
bool
|
bool
|
||||||
JitRuntime::initialize(JSContext *cx)
|
JitRuntime::initialize(JSContext *cx)
|
||||||
{
|
{
|
||||||
|
JS_ASSERT(cx->runtime()->currentThreadHasExclusiveAccess());
|
||||||
JS_ASSERT(cx->runtime()->currentThreadOwnsOperationCallbackLock());
|
JS_ASSERT(cx->runtime()->currentThreadOwnsOperationCallbackLock());
|
||||||
|
|
||||||
AutoLockForExclusiveAccess lock(cx);
|
|
||||||
AutoCompartment ac(cx, cx->atomsCompartment());
|
AutoCompartment ac(cx, cx->atomsCompartment());
|
||||||
|
|
||||||
IonContext ictx(cx, nullptr);
|
IonContext ictx(cx, nullptr);
|
||||||
|
@ -285,11 +285,7 @@ struct ThreadSafeContext : ContextFriendFields,
|
|||||||
size_t helperThreadCount() { return runtime_->helperThreadCount(); }
|
size_t helperThreadCount() { return runtime_->helperThreadCount(); }
|
||||||
void *runtimeAddressForJit() { return runtime_; }
|
void *runtimeAddressForJit() { return runtime_; }
|
||||||
void *stackLimitAddress(StackKind kind) { return &runtime_->mainThread.nativeStackLimit[kind]; }
|
void *stackLimitAddress(StackKind kind) { return &runtime_->mainThread.nativeStackLimit[kind]; }
|
||||||
|
|
||||||
// GCs cannot happen while non-main threads are running.
|
|
||||||
uint64_t gcNumber() { return runtime_->gcNumber; }
|
|
||||||
size_t gcSystemPageSize() { return runtime_->gcSystemPageSize; }
|
size_t gcSystemPageSize() { return runtime_->gcSystemPageSize; }
|
||||||
bool isHeapBusy() { return runtime_->isHeapBusy(); }
|
|
||||||
bool signalHandlersInstalled() const { return runtime_->signalHandlersInstalled(); }
|
bool signalHandlersInstalled() const { return runtime_->signalHandlersInstalled(); }
|
||||||
bool jitSupportsFloatingPoint() const { return runtime_->jitSupportsFloatingPoint; }
|
bool jitSupportsFloatingPoint() const { return runtime_->jitSupportsFloatingPoint; }
|
||||||
|
|
||||||
@ -355,9 +351,6 @@ class ExclusiveContext : public ThreadSafeContext
|
|||||||
void setWorkerThread(WorkerThread *workerThread);
|
void setWorkerThread(WorkerThread *workerThread);
|
||||||
WorkerThread *workerThread() const { return workerThread_; }
|
WorkerThread *workerThread() const { return workerThread_; }
|
||||||
|
|
||||||
// If required, pause this thread until notified to continue by the main thread.
|
|
||||||
inline void maybePause() const;
|
|
||||||
|
|
||||||
// Threads with an ExclusiveContext may freely access any data in their
|
// Threads with an ExclusiveContext may freely access any data in their
|
||||||
// compartment and zone.
|
// compartment and zone.
|
||||||
JSCompartment *compartment() const {
|
JSCompartment *compartment() const {
|
||||||
@ -1021,6 +1014,61 @@ bool intrinsic_HaveSameClass(JSContext *cx, unsigned argc, Value *vp);
|
|||||||
bool intrinsic_ShouldForceSequential(JSContext *cx, unsigned argc, Value *vp);
|
bool intrinsic_ShouldForceSequential(JSContext *cx, unsigned argc, Value *vp);
|
||||||
bool intrinsic_NewParallelArray(JSContext *cx, unsigned argc, Value *vp);
|
bool intrinsic_NewParallelArray(JSContext *cx, unsigned argc, Value *vp);
|
||||||
|
|
||||||
|
class AutoLockForExclusiveAccess
|
||||||
|
{
|
||||||
|
#ifdef JS_WORKER_THREADS
|
||||||
|
JSRuntime *runtime;
|
||||||
|
|
||||||
|
void init(JSRuntime *rt) {
|
||||||
|
runtime = rt;
|
||||||
|
if (runtime->numExclusiveThreads) {
|
||||||
|
runtime->assertCanLock(JSRuntime::ExclusiveAccessLock);
|
||||||
|
PR_Lock(runtime->exclusiveAccessLock);
|
||||||
|
runtime->exclusiveAccessOwner = PR_GetCurrentThread();
|
||||||
|
} else {
|
||||||
|
JS_ASSERT(!runtime->mainThreadHasExclusiveAccess);
|
||||||
|
runtime->mainThreadHasExclusiveAccess = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
AutoLockForExclusiveAccess(ExclusiveContext *cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) {
|
||||||
|
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||||
|
init(cx->runtime_);
|
||||||
|
}
|
||||||
|
AutoLockForExclusiveAccess(JSRuntime *rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM) {
|
||||||
|
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||||
|
init(rt);
|
||||||
|
}
|
||||||
|
~AutoLockForExclusiveAccess() {
|
||||||
|
if (runtime->numExclusiveThreads) {
|
||||||
|
JS_ASSERT(runtime->exclusiveAccessOwner == PR_GetCurrentThread());
|
||||||
|
#ifdef DEBUG
|
||||||
|
runtime->exclusiveAccessOwner = nullptr;
|
||||||
|
#endif
|
||||||
|
PR_Unlock(runtime->exclusiveAccessLock);
|
||||||
|
} else {
|
||||||
|
JS_ASSERT(runtime->mainThreadHasExclusiveAccess);
|
||||||
|
runtime->mainThreadHasExclusiveAccess = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else // JS_WORKER_THREADS
|
||||||
|
public:
|
||||||
|
AutoLockForExclusiveAccess(ExclusiveContext *cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) {
|
||||||
|
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||||
|
}
|
||||||
|
AutoLockForExclusiveAccess(JSRuntime *rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM) {
|
||||||
|
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||||
|
}
|
||||||
|
~AutoLockForExclusiveAccess() {
|
||||||
|
// An empty destructor is needed to avoid warnings from clang about
|
||||||
|
// unused local variables of this type.
|
||||||
|
}
|
||||||
|
#endif // JS_WORKER_THREADS
|
||||||
|
|
||||||
|
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
||||||
|
};
|
||||||
|
|
||||||
} /* namespace js */
|
} /* namespace js */
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
|
@ -135,7 +135,7 @@ class CompartmentChecker
|
|||||||
#define START_ASSERT_SAME_COMPARTMENT() \
|
#define START_ASSERT_SAME_COMPARTMENT() \
|
||||||
if (!cx->isExclusiveContext()) \
|
if (!cx->isExclusiveContext()) \
|
||||||
return; \
|
return; \
|
||||||
if (cx->isHeapBusy()) \
|
if (cx->isJSContext() && cx->asJSContext()->runtime()->isHeapBusy()) \
|
||||||
return; \
|
return; \
|
||||||
CompartmentChecker c(cx->asExclusiveContext())
|
CompartmentChecker c(cx->asExclusiveContext())
|
||||||
|
|
||||||
@ -349,71 +349,6 @@ GetNativeStackLimit(ThreadSafeContext *cx)
|
|||||||
return cx->perThreadData->nativeStackLimit[kind];
|
return cx->perThreadData->nativeStackLimit[kind];
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void
|
|
||||||
ExclusiveContext::maybePause() const
|
|
||||||
{
|
|
||||||
#ifdef JS_WORKER_THREADS
|
|
||||||
if (workerThread())
|
|
||||||
workerThread()->maybePause();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
class AutoLockForExclusiveAccess
|
|
||||||
{
|
|
||||||
#ifdef JS_WORKER_THREADS
|
|
||||||
JSRuntime *runtime;
|
|
||||||
|
|
||||||
void init(JSRuntime *rt) {
|
|
||||||
runtime = rt;
|
|
||||||
if (runtime->numExclusiveThreads) {
|
|
||||||
PR_Lock(runtime->exclusiveAccessLock);
|
|
||||||
#ifdef DEBUG
|
|
||||||
runtime->exclusiveAccessOwner = PR_GetCurrentThread();
|
|
||||||
#endif
|
|
||||||
} else {
|
|
||||||
JS_ASSERT(!runtime->mainThreadHasExclusiveAccess);
|
|
||||||
runtime->mainThreadHasExclusiveAccess = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
AutoLockForExclusiveAccess(ExclusiveContext *cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) {
|
|
||||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
|
||||||
init(cx->runtime_);
|
|
||||||
}
|
|
||||||
AutoLockForExclusiveAccess(JSRuntime *rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM) {
|
|
||||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
|
||||||
init(rt);
|
|
||||||
}
|
|
||||||
~AutoLockForExclusiveAccess() {
|
|
||||||
if (runtime->numExclusiveThreads) {
|
|
||||||
JS_ASSERT(runtime->exclusiveAccessOwner == PR_GetCurrentThread());
|
|
||||||
#ifdef DEBUG
|
|
||||||
runtime->exclusiveAccessOwner = nullptr;
|
|
||||||
#endif
|
|
||||||
PR_Unlock(runtime->exclusiveAccessLock);
|
|
||||||
} else {
|
|
||||||
JS_ASSERT(runtime->mainThreadHasExclusiveAccess);
|
|
||||||
runtime->mainThreadHasExclusiveAccess = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else // JS_WORKER_THREADS
|
|
||||||
public:
|
|
||||||
AutoLockForExclusiveAccess(ExclusiveContext *cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) {
|
|
||||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
|
||||||
}
|
|
||||||
AutoLockForExclusiveAccess(JSRuntime *rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM) {
|
|
||||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
|
||||||
}
|
|
||||||
~AutoLockForExclusiveAccess() {
|
|
||||||
// An empty destructor is needed to avoid warnings from clang about
|
|
||||||
// unused local variables of this type.
|
|
||||||
}
|
|
||||||
#endif // JS_WORKER_THREADS
|
|
||||||
|
|
||||||
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
|
||||||
};
|
|
||||||
|
|
||||||
inline LifoAlloc &
|
inline LifoAlloc &
|
||||||
ExclusiveContext::typeLifoAlloc()
|
ExclusiveContext::typeLifoAlloc()
|
||||||
{
|
{
|
||||||
|
@ -123,6 +123,10 @@ JSCompartment::init(JSContext *cx)
|
|||||||
jit::JitRuntime *
|
jit::JitRuntime *
|
||||||
JSRuntime::createJitRuntime(JSContext *cx)
|
JSRuntime::createJitRuntime(JSContext *cx)
|
||||||
{
|
{
|
||||||
|
// The shared stubs are created in the atoms compartment, which may be
|
||||||
|
// accessed by other threads with an exclusive context.
|
||||||
|
AutoLockForExclusiveAccess atomsLock(cx);
|
||||||
|
|
||||||
// The runtime will only be created on its owning thread, but reads of a
|
// The runtime will only be created on its owning thread, but reads of a
|
||||||
// runtime's jitRuntime() can occur when another thread is triggering an
|
// runtime's jitRuntime() can occur when another thread is triggering an
|
||||||
// operation callback.
|
// operation callback.
|
||||||
@ -139,8 +143,6 @@ JSRuntime::createJitRuntime(JSContext *cx)
|
|||||||
js_delete(jitRuntime_);
|
js_delete(jitRuntime_);
|
||||||
jitRuntime_ = nullptr;
|
jitRuntime_ = nullptr;
|
||||||
|
|
||||||
AutoLockForExclusiveAccess atomsLock(cx);
|
|
||||||
|
|
||||||
JSCompartment *comp = cx->runtime()->atomsCompartment();
|
JSCompartment *comp = cx->runtime()->atomsCompartment();
|
||||||
if (comp->jitCompartment_) {
|
if (comp->jitCompartment_) {
|
||||||
js_delete(comp->jitCompartment_);
|
js_delete(comp->jitCompartment_);
|
||||||
|
198
js/src/jsgc.cpp
198
js/src/jsgc.cpp
@ -782,8 +782,11 @@ Chunk::allocateArena(Zone *zone, AllocKind thingKind)
|
|||||||
|
|
||||||
rt->gcBytes += ArenaSize;
|
rt->gcBytes += ArenaSize;
|
||||||
zone->gcBytes += ArenaSize;
|
zone->gcBytes += ArenaSize;
|
||||||
if (zone->gcBytes >= zone->gcTriggerBytes)
|
|
||||||
|
if (zone->gcBytes >= zone->gcTriggerBytes) {
|
||||||
|
AutoUnlockGC unlock(rt);
|
||||||
TriggerZoneGC(zone, JS::gcreason::ALLOC_TRIGGER);
|
TriggerZoneGC(zone, JS::gcreason::ALLOC_TRIGGER);
|
||||||
|
}
|
||||||
|
|
||||||
return aheader;
|
return aheader;
|
||||||
}
|
}
|
||||||
@ -947,11 +950,6 @@ js_InitGC(JSRuntime *rt, uint32_t maxbytes)
|
|||||||
if (!rt->gcRootsHash.init(256))
|
if (!rt->gcRootsHash.init(256))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
#ifdef JS_THREADSAFE
|
|
||||||
rt->gcLock = PR_NewLock();
|
|
||||||
if (!rt->gcLock)
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
if (!rt->gcHelperThread.init())
|
if (!rt->gcHelperThread.init())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -1524,7 +1522,7 @@ template <AllowGC allowGC>
|
|||||||
ArenaLists::refillFreeList(ThreadSafeContext *cx, AllocKind thingKind)
|
ArenaLists::refillFreeList(ThreadSafeContext *cx, AllocKind thingKind)
|
||||||
{
|
{
|
||||||
JS_ASSERT(cx->allocator()->arenas.freeLists[thingKind].isEmpty());
|
JS_ASSERT(cx->allocator()->arenas.freeLists[thingKind].isEmpty());
|
||||||
JS_ASSERT(!cx->isHeapBusy());
|
JS_ASSERT_IF(cx->isJSContext(), !cx->asJSContext()->runtime()->isHeapBusy());
|
||||||
|
|
||||||
Zone *zone = cx->allocator()->zone_;
|
Zone *zone = cx->allocator()->zone_;
|
||||||
|
|
||||||
@ -1532,31 +1530,53 @@ ArenaLists::refillFreeList(ThreadSafeContext *cx, AllocKind thingKind)
|
|||||||
cx->asJSContext()->runtime()->gcIncrementalState != NO_INCREMENTAL &&
|
cx->asJSContext()->runtime()->gcIncrementalState != NO_INCREMENTAL &&
|
||||||
zone->gcBytes > zone->gcTriggerBytes;
|
zone->gcBytes > zone->gcTriggerBytes;
|
||||||
|
|
||||||
|
#ifdef JS_THREADSAFE
|
||||||
|
JS_ASSERT_IF(cx->isJSContext() && allowGC,
|
||||||
|
!cx->asJSContext()->runtime()->currentThreadHasExclusiveAccess());
|
||||||
|
#endif
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (JS_UNLIKELY(runGC)) {
|
if (JS_UNLIKELY(runGC)) {
|
||||||
if (void *thing = RunLastDitchGC(cx->asJSContext(), zone, thingKind))
|
if (void *thing = RunLastDitchGC(cx->asJSContext(), zone, thingKind))
|
||||||
return thing;
|
return thing;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
if (cx->isJSContext()) {
|
||||||
* allocateFromArena may fail while the background finalization still
|
/*
|
||||||
* run. If we aren't in a fork join, we want to wait for it to finish
|
* allocateFromArena may fail while the background finalization still
|
||||||
* and restart. However, checking for that is racy as the background
|
* run. If we are on the main thread, we want to wait for it to finish
|
||||||
* finalization could free some things after allocateFromArena decided
|
* and restart. However, checking for that is racy as the background
|
||||||
* to fail but at this point it may have already stopped. To avoid
|
* finalization could free some things after allocateFromArena decided
|
||||||
* this race we always try to allocate twice.
|
* to fail but at this point it may have already stopped. To avoid
|
||||||
*
|
* this race we always try to allocate twice.
|
||||||
* If we're in a fork join, we simply try it once and return whatever
|
*/
|
||||||
* value we get.
|
for (bool secondAttempt = false; ; secondAttempt = true) {
|
||||||
*/
|
void *thing = cx->allocator()->arenas.allocateFromArenaInline(zone, thingKind);
|
||||||
for (bool secondAttempt = false; ; secondAttempt = true) {
|
if (JS_LIKELY(!!thing))
|
||||||
void *thing = cx->allocator()->arenas.allocateFromArenaInline(zone, thingKind);
|
return thing;
|
||||||
if (JS_LIKELY(!!thing) || !cx->isJSContext())
|
if (secondAttempt)
|
||||||
return thing;
|
break;
|
||||||
if (secondAttempt)
|
|
||||||
break;
|
|
||||||
|
|
||||||
cx->asJSContext()->runtime()->gcHelperThread.waitBackgroundSweepEnd();
|
cx->asJSContext()->runtime()->gcHelperThread.waitBackgroundSweepEnd();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
#ifdef JS_THREADSAFE
|
||||||
|
/*
|
||||||
|
* If we're off the main thread, we try to allocate once and return
|
||||||
|
* whatever value we get. First, though, we need to ensure the main
|
||||||
|
* thread is not in a GC session.
|
||||||
|
*/
|
||||||
|
JSRuntime *rt = zone->runtimeFromAnyThread();
|
||||||
|
AutoLockWorkerThreadState lock(*rt->workerThreadState);
|
||||||
|
while (rt->isHeapBusy())
|
||||||
|
rt->workerThreadState->wait(WorkerThreadState::PRODUCER);
|
||||||
|
|
||||||
|
void *thing = cx->allocator()->arenas.allocateFromArenaInline(zone, thingKind);
|
||||||
|
if (thing)
|
||||||
|
return thing;
|
||||||
|
#else
|
||||||
|
MOZ_CRASH();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cx->allowGC() || !allowGC)
|
if (!cx->allowGC() || !allowGC)
|
||||||
@ -2318,6 +2338,16 @@ GCHelperThread::threadMain(void *arg)
|
|||||||
static_cast<GCHelperThread *>(arg)->threadLoop();
|
static_cast<GCHelperThread *>(arg)->threadLoop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
GCHelperThread::wait(PRCondVar *which)
|
||||||
|
{
|
||||||
|
rt->gcLockOwner = nullptr;
|
||||||
|
PR_WaitCondVar(which, PR_INTERVAL_NO_TIMEOUT);
|
||||||
|
#ifdef DEBUG
|
||||||
|
rt->gcLockOwner = PR_GetCurrentThread();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
GCHelperThread::threadLoop()
|
GCHelperThread::threadLoop()
|
||||||
{
|
{
|
||||||
@ -2337,7 +2367,7 @@ GCHelperThread::threadLoop()
|
|||||||
case SHUTDOWN:
|
case SHUTDOWN:
|
||||||
return;
|
return;
|
||||||
case IDLE:
|
case IDLE:
|
||||||
PR_WaitCondVar(wakeup, PR_INTERVAL_NO_TIMEOUT);
|
wait(wakeup);
|
||||||
break;
|
break;
|
||||||
case SWEEPING:
|
case SWEEPING:
|
||||||
#if JS_TRACE_LOGGING
|
#if JS_TRACE_LOGGING
|
||||||
@ -2445,7 +2475,7 @@ GCHelperThread::waitBackgroundSweepEnd()
|
|||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
AutoLockGC lock(rt);
|
AutoLockGC lock(rt);
|
||||||
while (state == SWEEPING)
|
while (state == SWEEPING)
|
||||||
PR_WaitCondVar(done, PR_INTERVAL_NO_TIMEOUT);
|
wait(done);
|
||||||
if (rt->gcIncrementalState == NO_INCREMENTAL)
|
if (rt->gcIncrementalState == NO_INCREMENTAL)
|
||||||
AssertBackgroundSweepingFinished(rt);
|
AssertBackgroundSweepingFinished(rt);
|
||||||
#endif /* JS_THREADSAFE */
|
#endif /* JS_THREADSAFE */
|
||||||
@ -2464,7 +2494,7 @@ GCHelperThread::waitBackgroundSweepOrAllocEnd()
|
|||||||
if (state == ALLOCATING)
|
if (state == ALLOCATING)
|
||||||
state = CANCEL_ALLOCATION;
|
state = CANCEL_ALLOCATION;
|
||||||
while (state == SWEEPING || state == CANCEL_ALLOCATION)
|
while (state == SWEEPING || state == CANCEL_ALLOCATION)
|
||||||
PR_WaitCondVar(done, PR_INTERVAL_NO_TIMEOUT);
|
wait(done);
|
||||||
if (rt->gcIncrementalState == NO_INCREMENTAL)
|
if (rt->gcIncrementalState == NO_INCREMENTAL)
|
||||||
AssertBackgroundSweepingFinished(rt);
|
AssertBackgroundSweepingFinished(rt);
|
||||||
#endif /* JS_THREADSAFE */
|
#endif /* JS_THREADSAFE */
|
||||||
@ -2662,10 +2692,7 @@ PurgeRuntime(JSRuntime *rt)
|
|||||||
rt->sourceDataCache.purge();
|
rt->sourceDataCache.purge();
|
||||||
rt->evalCache.clear();
|
rt->evalCache.clear();
|
||||||
|
|
||||||
bool activeCompilations = false;
|
if (!rt->hasActiveCompilations())
|
||||||
for (ThreadDataIter iter(rt); !iter.done(); iter.next())
|
|
||||||
activeCompilations |= iter->activeCompilations;
|
|
||||||
if (!activeCompilations)
|
|
||||||
rt->parseMapPool().purgeAll();
|
rt->parseMapPool().purgeAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2832,26 +2859,18 @@ BeginMarkPhase(JSRuntime *rt)
|
|||||||
* zones that are not being collected, we are not allowed to collect
|
* zones that are not being collected, we are not allowed to collect
|
||||||
* atoms. Otherwise, the non-collected zones could contain pointers
|
* atoms. Otherwise, the non-collected zones could contain pointers
|
||||||
* to atoms that we would miss.
|
* to atoms that we would miss.
|
||||||
|
*
|
||||||
|
* keepAtoms() will only change on the main thread, which we are currently
|
||||||
|
* on. If the value of keepAtoms() changes between GC slices, then we'll
|
||||||
|
* cancel the incremental GC. See IsIncrementalGCSafe.
|
||||||
*/
|
*/
|
||||||
Zone *atomsZone = rt->atomsCompartment()->zone();
|
if (rt->gcIsFull && !rt->keepAtoms()) {
|
||||||
|
Zone *atomsZone = rt->atomsCompartment()->zone();
|
||||||
bool keepAtoms = false;
|
if (atomsZone->isGCScheduled()) {
|
||||||
for (ThreadDataIter iter(rt); !iter.done(); iter.next())
|
JS_ASSERT(!atomsZone->isCollecting());
|
||||||
keepAtoms |= iter->gcKeepAtoms;
|
atomsZone->setGCState(Zone::Mark);
|
||||||
|
any = true;
|
||||||
/*
|
}
|
||||||
* We don't scan the stacks of exclusive threads, so we need to avoid
|
|
||||||
* collecting their objects in another way. The only GC thing pointers they
|
|
||||||
* have are to their exclusive compartment (which is not collected) or to
|
|
||||||
* the atoms compartment. Therefore, we avoid collecting the atoms
|
|
||||||
* compartment when exclusive threads are running.
|
|
||||||
*/
|
|
||||||
keepAtoms |= rt->exclusiveThreadsPresent();
|
|
||||||
|
|
||||||
if (atomsZone->isGCScheduled() && rt->gcIsFull && !keepAtoms) {
|
|
||||||
JS_ASSERT(!atomsZone->isCollecting());
|
|
||||||
atomsZone->setGCState(Zone::Mark);
|
|
||||||
any = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check that at least one zone is scheduled for collection. */
|
/* Check that at least one zone is scheduled for collection. */
|
||||||
@ -4099,7 +4118,6 @@ namespace {
|
|||||||
class AutoGCSession
|
class AutoGCSession
|
||||||
{
|
{
|
||||||
JSRuntime *runtime;
|
JSRuntime *runtime;
|
||||||
AutoPauseWorkersForTracing pause;
|
|
||||||
AutoTraceSession session;
|
AutoTraceSession session;
|
||||||
bool canceled;
|
bool canceled;
|
||||||
|
|
||||||
@ -4114,24 +4132,55 @@ class AutoGCSession
|
|||||||
|
|
||||||
/* Start a new heap session. */
|
/* Start a new heap session. */
|
||||||
AutoTraceSession::AutoTraceSession(JSRuntime *rt, js::HeapState heapState)
|
AutoTraceSession::AutoTraceSession(JSRuntime *rt, js::HeapState heapState)
|
||||||
: runtime(rt),
|
: lock(rt),
|
||||||
|
runtime(rt),
|
||||||
prevState(rt->heapState)
|
prevState(rt->heapState)
|
||||||
{
|
{
|
||||||
JS_ASSERT(!rt->noGCOrAllocationCheck);
|
JS_ASSERT(!rt->noGCOrAllocationCheck);
|
||||||
JS_ASSERT(!rt->isHeapBusy());
|
JS_ASSERT(!rt->isHeapBusy());
|
||||||
JS_ASSERT(heapState != Idle);
|
JS_ASSERT(heapState != Idle);
|
||||||
rt->heapState = heapState;
|
|
||||||
|
// Threads with an exclusive context can hit refillFreeList while holding
|
||||||
|
// the exclusive access lock. To avoid deadlocking when we try to acquire
|
||||||
|
// this lock during GC and the other thread is waiting, make sure we hold
|
||||||
|
// the exclusive access lock during GC sessions.
|
||||||
|
JS_ASSERT(rt->currentThreadHasExclusiveAccess());
|
||||||
|
|
||||||
|
if (rt->exclusiveThreadsPresent()) {
|
||||||
|
// Lock the worker thread state when changing the heap state in the
|
||||||
|
// presence of exclusive threads, to avoid racing with refillFreeList.
|
||||||
|
#ifdef JS_THREADSAFE
|
||||||
|
AutoLockWorkerThreadState lock(*rt->workerThreadState);
|
||||||
|
rt->heapState = heapState;
|
||||||
|
#else
|
||||||
|
MOZ_CRASH();
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
rt->heapState = heapState;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AutoTraceSession::~AutoTraceSession()
|
AutoTraceSession::~AutoTraceSession()
|
||||||
{
|
{
|
||||||
JS_ASSERT(runtime->isHeapBusy());
|
JS_ASSERT(runtime->isHeapBusy());
|
||||||
runtime->heapState = prevState;
|
|
||||||
|
if (runtime->exclusiveThreadsPresent()) {
|
||||||
|
#ifdef JS_THREADSAFE
|
||||||
|
AutoLockWorkerThreadState lock(*runtime->workerThreadState);
|
||||||
|
runtime->heapState = prevState;
|
||||||
|
|
||||||
|
// Notify any worker threads waiting for the trace session to end.
|
||||||
|
runtime->workerThreadState->notifyAll(WorkerThreadState::PRODUCER);
|
||||||
|
#else
|
||||||
|
MOZ_CRASH();
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
runtime->heapState = prevState;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AutoGCSession::AutoGCSession(JSRuntime *rt)
|
AutoGCSession::AutoGCSession(JSRuntime *rt)
|
||||||
: runtime(rt),
|
: runtime(rt),
|
||||||
pause(rt),
|
|
||||||
session(rt, MajorCollecting),
|
session(rt, MajorCollecting),
|
||||||
canceled(false)
|
canceled(false)
|
||||||
{
|
{
|
||||||
@ -4140,12 +4189,9 @@ AutoGCSession::AutoGCSession(JSRuntime *rt)
|
|||||||
|
|
||||||
runtime->gcNumber++;
|
runtime->gcNumber++;
|
||||||
|
|
||||||
#ifdef DEBUG
|
// It's ok if threads other than the main thread have suppressGC set, as
|
||||||
// Threads with an exclusive context should never pause while they are in
|
// they are operating on zones which will not be collected from here.
|
||||||
// the middle of a suppressGC.
|
JS_ASSERT(!runtime->mainThread.suppressGC);
|
||||||
for (ThreadDataIter iter(rt); !iter.done(); iter.next())
|
|
||||||
JS_ASSERT(!iter->suppressGC);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AutoGCSession::~AutoGCSession()
|
AutoGCSession::~AutoGCSession()
|
||||||
@ -4193,16 +4239,13 @@ class AutoCopyFreeListToArenasForGC
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
AutoCopyFreeListToArenasForGC(JSRuntime *rt) : runtime(rt) {
|
AutoCopyFreeListToArenasForGC(JSRuntime *rt) : runtime(rt) {
|
||||||
for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
|
JS_ASSERT(rt->currentThreadHasExclusiveAccess());
|
||||||
//if (zone->canCollect())
|
for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next())
|
||||||
zone->allocator.arenas.copyFreeListsToArenas();
|
zone->allocator.arenas.copyFreeListsToArenas();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
~AutoCopyFreeListToArenasForGC() {
|
~AutoCopyFreeListToArenasForGC() {
|
||||||
for (ZonesIter zone(runtime, WithAtoms); !zone.done(); zone.next()) {
|
for (ZonesIter zone(runtime, WithAtoms); !zone.done(); zone.next())
|
||||||
//if (zone->canCollect())
|
zone->allocator.arenas.clearFreeListsInArenas();
|
||||||
zone->allocator.arenas.clearFreeListsInArenas();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -4359,6 +4402,8 @@ IncrementalCollectSlice(JSRuntime *rt,
|
|||||||
JS::gcreason::Reason reason,
|
JS::gcreason::Reason reason,
|
||||||
JSGCInvocationKind gckind)
|
JSGCInvocationKind gckind)
|
||||||
{
|
{
|
||||||
|
JS_ASSERT(rt->currentThreadHasExclusiveAccess());
|
||||||
|
|
||||||
AutoCopyFreeListToArenasForGC copy(rt);
|
AutoCopyFreeListToArenasForGC copy(rt);
|
||||||
AutoGCSlice slice(rt);
|
AutoGCSlice slice(rt);
|
||||||
|
|
||||||
@ -4485,14 +4530,8 @@ gc::IsIncrementalGCSafe(JSRuntime *rt)
|
|||||||
{
|
{
|
||||||
JS_ASSERT(!rt->mainThread.suppressGC);
|
JS_ASSERT(!rt->mainThread.suppressGC);
|
||||||
|
|
||||||
bool keepAtoms = false;
|
if (rt->keepAtoms())
|
||||||
for (ThreadDataIter iter(rt); !iter.done(); iter.next())
|
return IncrementalSafety::Unsafe("keepAtoms set");
|
||||||
keepAtoms |= iter->gcKeepAtoms;
|
|
||||||
|
|
||||||
keepAtoms |= rt->exclusiveThreadsPresent();
|
|
||||||
|
|
||||||
if (keepAtoms)
|
|
||||||
return IncrementalSafety::Unsafe("gcKeepAtoms set");
|
|
||||||
|
|
||||||
if (!rt->gcIncrementalEnabled)
|
if (!rt->gcIncrementalEnabled)
|
||||||
return IncrementalSafety::Unsafe("incremental permanently disabled");
|
return IncrementalSafety::Unsafe("incremental permanently disabled");
|
||||||
@ -4593,7 +4632,6 @@ GCCycle(JSRuntime *rt, bool incremental, int64_t budget,
|
|||||||
}
|
}
|
||||||
|
|
||||||
IncrementalCollectSlice(rt, budget, reason, gckind);
|
IncrementalCollectSlice(rt, budget, reason, gckind);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4858,7 +4896,6 @@ AutoFinishGC::AutoFinishGC(JSRuntime *rt)
|
|||||||
|
|
||||||
AutoPrepareForTracing::AutoPrepareForTracing(JSRuntime *rt, ZoneSelector selector)
|
AutoPrepareForTracing::AutoPrepareForTracing(JSRuntime *rt, ZoneSelector selector)
|
||||||
: finish(rt),
|
: finish(rt),
|
||||||
pause(rt),
|
|
||||||
session(rt),
|
session(rt),
|
||||||
copy(rt, selector)
|
copy(rt, selector)
|
||||||
{
|
{
|
||||||
@ -4916,6 +4953,7 @@ void
|
|||||||
gc::MergeCompartments(JSCompartment *source, JSCompartment *target)
|
gc::MergeCompartments(JSCompartment *source, JSCompartment *target)
|
||||||
{
|
{
|
||||||
JSRuntime *rt = source->runtimeFromMainThread();
|
JSRuntime *rt = source->runtimeFromMainThread();
|
||||||
|
|
||||||
AutoPrepareForTracing prepare(rt, SkipAtoms);
|
AutoPrepareForTracing prepare(rt, SkipAtoms);
|
||||||
|
|
||||||
// Cleanup tables and other state in the source compartment that will be
|
// Cleanup tables and other state in the source compartment that will be
|
||||||
|
@ -790,6 +790,8 @@ class GCHelperThread {
|
|||||||
PRCondVar *done;
|
PRCondVar *done;
|
||||||
volatile State state;
|
volatile State state;
|
||||||
|
|
||||||
|
void wait(PRCondVar *which);
|
||||||
|
|
||||||
bool sweepFlag;
|
bool sweepFlag;
|
||||||
bool shrinkFlag;
|
bool shrinkFlag;
|
||||||
|
|
||||||
|
@ -3746,7 +3746,7 @@ ExclusiveContext::getNewType(const Class *clasp, TaggedProto proto_, JSFunction
|
|||||||
|
|
||||||
TypeObjectSet::AddPtr p = newTypeObjects.lookupForAdd(TypeObjectSet::Lookup(clasp, proto_));
|
TypeObjectSet::AddPtr p = newTypeObjects.lookupForAdd(TypeObjectSet::Lookup(clasp, proto_));
|
||||||
SkipRoot skipHash(this, &p); /* Prevent the hash from being poisoned. */
|
SkipRoot skipHash(this, &p); /* Prevent the hash from being poisoned. */
|
||||||
uint64_t originalGcNumber = gcNumber();
|
uint64_t originalGcNumber = zone()->gcNumber();
|
||||||
if (p) {
|
if (p) {
|
||||||
TypeObject *type = *p;
|
TypeObject *type = *p;
|
||||||
JS_ASSERT(type->clasp == clasp);
|
JS_ASSERT(type->clasp == clasp);
|
||||||
@ -3788,7 +3788,7 @@ ExclusiveContext::getNewType(const Class *clasp, TaggedProto proto_, JSFunction
|
|||||||
* If a GC has occured, then the hash we calculated may be invalid, as it
|
* If a GC has occured, then the hash we calculated may be invalid, as it
|
||||||
* is based on proto, which may have been moved.
|
* is based on proto, which may have been moved.
|
||||||
*/
|
*/
|
||||||
bool gcHappened = gcNumber() != originalGcNumber;
|
bool gcHappened = zone()->gcNumber() != originalGcNumber;
|
||||||
bool added =
|
bool added =
|
||||||
gcHappened ? newTypeObjects.putNew(TypeObjectSet::Lookup(clasp, proto), type.get())
|
gcHappened ? newTypeObjects.putNew(TypeObjectSet::Lookup(clasp, proto), type.get())
|
||||||
: newTypeObjects.relookupOrAdd(p, TypeObjectSet::Lookup(clasp, proto), type.get());
|
: newTypeObjects.relookupOrAdd(p, TypeObjectSet::Lookup(clasp, proto), type.get());
|
||||||
|
@ -1200,7 +1200,6 @@ SourceCompressionTask::compress()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
cont = cont && !abort_;
|
cont = cont && !abort_;
|
||||||
maybePause();
|
|
||||||
}
|
}
|
||||||
compressedLength = comp.outWritten();
|
compressedLength = comp.outWritten();
|
||||||
if (abort_ || compressedLength == nbytes)
|
if (abort_ || compressedLength == nbytes)
|
||||||
@ -1553,10 +1552,8 @@ js::SweepScriptData(JSRuntime *rt)
|
|||||||
JS_ASSERT(rt->gcIsFull);
|
JS_ASSERT(rt->gcIsFull);
|
||||||
ScriptDataTable &table = rt->scriptDataTable();
|
ScriptDataTable &table = rt->scriptDataTable();
|
||||||
|
|
||||||
for (ThreadDataIter iter(rt); !iter.done(); iter.next()) {
|
if (rt->keepAtoms())
|
||||||
if (iter->gcKeepAtoms)
|
return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (ScriptDataTable::Enum e(table); !e.empty(); e.popFront()) {
|
for (ScriptDataTable::Enum e(table); !e.empty(); e.popFront()) {
|
||||||
SharedScriptData *entry = e.front();
|
SharedScriptData *entry = e.front();
|
||||||
|
@ -38,11 +38,11 @@ js::EnsureWorkerThreadsInitialized(ExclusiveContext *cx)
|
|||||||
if (rt->workerThreadState)
|
if (rt->workerThreadState)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
rt->workerThreadState = rt->new_<WorkerThreadState>();
|
rt->workerThreadState = rt->new_<WorkerThreadState>(rt);
|
||||||
if (!rt->workerThreadState)
|
if (!rt->workerThreadState)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!rt->workerThreadState->init(rt)) {
|
if (!rt->workerThreadState->init()) {
|
||||||
js_delete(rt->workerThreadState);
|
js_delete(rt->workerThreadState);
|
||||||
rt->workerThreadState = nullptr;
|
rt->workerThreadState = nullptr;
|
||||||
return false;
|
return false;
|
||||||
@ -309,9 +309,9 @@ js::WaitForOffThreadParsingToFinish(JSRuntime *rt)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
WorkerThreadState::init(JSRuntime *rt)
|
WorkerThreadState::init()
|
||||||
{
|
{
|
||||||
if (!rt->useHelperThreads()) {
|
if (!runtime->useHelperThreads()) {
|
||||||
numThreads = 0;
|
numThreads = 0;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -328,9 +328,9 @@ WorkerThreadState::init(JSRuntime *rt)
|
|||||||
if (!producerWakeup)
|
if (!producerWakeup)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
numThreads = rt->helperThreadCount();
|
numThreads = runtime->helperThreadCount();
|
||||||
|
|
||||||
threads = (WorkerThread*) rt->calloc_(sizeof(WorkerThread) * numThreads);
|
threads = (WorkerThread*) js_pod_calloc<WorkerThread>(numThreads);
|
||||||
if (!threads) {
|
if (!threads) {
|
||||||
numThreads = 0;
|
numThreads = 0;
|
||||||
return false;
|
return false;
|
||||||
@ -338,8 +338,8 @@ WorkerThreadState::init(JSRuntime *rt)
|
|||||||
|
|
||||||
for (size_t i = 0; i < numThreads; i++) {
|
for (size_t i = 0; i < numThreads; i++) {
|
||||||
WorkerThread &helper = threads[i];
|
WorkerThread &helper = threads[i];
|
||||||
helper.runtime = rt;
|
helper.runtime = runtime;
|
||||||
helper.threadData.construct(rt);
|
helper.threadData.construct(runtime);
|
||||||
helper.threadData.ref().addToThreadList();
|
helper.threadData.ref().addToThreadList();
|
||||||
helper.thread = PR_CreateThread(PR_USER_THREAD,
|
helper.thread = PR_CreateThread(PR_USER_THREAD,
|
||||||
WorkerThread::ThreadMain, &helper,
|
WorkerThread::ThreadMain, &helper,
|
||||||
@ -359,7 +359,7 @@ WorkerThreadState::init(JSRuntime *rt)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
WorkerThreadState::cleanup(JSRuntime *rt)
|
WorkerThreadState::cleanup()
|
||||||
{
|
{
|
||||||
// Do preparatory work for shutdown before the final GC has destroyed most
|
// Do preparatory work for shutdown before the final GC has destroyed most
|
||||||
// of the GC heap.
|
// of the GC heap.
|
||||||
@ -375,7 +375,7 @@ WorkerThreadState::cleanup(JSRuntime *rt)
|
|||||||
|
|
||||||
// Clean up any parse tasks which haven't been finished yet.
|
// Clean up any parse tasks which haven't been finished yet.
|
||||||
while (!parseFinishedList.empty())
|
while (!parseFinishedList.empty())
|
||||||
finishParseTask(/* maybecx = */ nullptr, rt, parseFinishedList[0]);
|
finishParseTask(/* maybecx = */ nullptr, runtime, parseFinishedList[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
WorkerThreadState::~WorkerThreadState()
|
WorkerThreadState::~WorkerThreadState()
|
||||||
@ -396,7 +396,7 @@ WorkerThreadState::~WorkerThreadState()
|
|||||||
void
|
void
|
||||||
WorkerThreadState::lock()
|
WorkerThreadState::lock()
|
||||||
{
|
{
|
||||||
JS_ASSERT(!isLocked());
|
runtime->assertCanLock(JSRuntime::WorkerThreadStateLock);
|
||||||
PR_Lock(workerLock);
|
PR_Lock(workerLock);
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
lockOwner = PR_GetCurrentThread();
|
lockOwner = PR_GetCurrentThread();
|
||||||
@ -820,11 +820,8 @@ SourceCompressionTask::complete()
|
|||||||
WorkerThreadState &state = *cx->workerThreadState();
|
WorkerThreadState &state = *cx->workerThreadState();
|
||||||
AutoLockWorkerThreadState lock(state);
|
AutoLockWorkerThreadState lock(state);
|
||||||
|
|
||||||
{
|
while (state.compressionInProgress(this))
|
||||||
AutoPauseCurrentWorkerThread maybePause(cx);
|
state.wait(WorkerThreadState::CONSUMER);
|
||||||
while (state.compressionInProgress(this))
|
|
||||||
state.wait(WorkerThreadState::CONSUMER);
|
|
||||||
}
|
|
||||||
|
|
||||||
ss->ready_ = true;
|
ss->ready_ = true;
|
||||||
|
|
||||||
@ -897,8 +894,6 @@ WorkerThread::threadLoop()
|
|||||||
|
|
||||||
// Block until a task is available.
|
// Block until a task is available.
|
||||||
while (true) {
|
while (true) {
|
||||||
if (state.shouldPause)
|
|
||||||
pause();
|
|
||||||
if (terminate)
|
if (terminate)
|
||||||
return;
|
return;
|
||||||
if (state.canStartIonCompile() ||
|
if (state.canStartIonCompile() ||
|
||||||
@ -925,112 +920,6 @@ WorkerThread::threadLoop()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AutoPauseWorkersForTracing::AutoPauseWorkersForTracing(JSRuntime *rt
|
|
||||||
MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
|
|
||||||
: runtime(rt), needsUnpause(false), oldExclusiveThreadsPaused(rt->exclusiveThreadsPaused)
|
|
||||||
{
|
|
||||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
|
||||||
|
|
||||||
rt->exclusiveThreadsPaused = true;
|
|
||||||
|
|
||||||
if (!runtime->workerThreadState)
|
|
||||||
return;
|
|
||||||
|
|
||||||
JS_ASSERT(CurrentThreadCanAccessRuntime(runtime));
|
|
||||||
|
|
||||||
WorkerThreadState &state = *runtime->workerThreadState;
|
|
||||||
if (!state.numThreads)
|
|
||||||
return;
|
|
||||||
|
|
||||||
AutoLockWorkerThreadState lock(state);
|
|
||||||
|
|
||||||
// Tolerate reentrant use of AutoPauseWorkersForTracing.
|
|
||||||
if (state.shouldPause) {
|
|
||||||
JS_ASSERT(state.numPaused == state.numThreads);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
needsUnpause = true;
|
|
||||||
|
|
||||||
state.shouldPause = 1;
|
|
||||||
|
|
||||||
while (state.numPaused != state.numThreads) {
|
|
||||||
state.notifyAll(WorkerThreadState::PRODUCER);
|
|
||||||
state.wait(WorkerThreadState::CONSUMER);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AutoPauseWorkersForTracing::~AutoPauseWorkersForTracing()
|
|
||||||
{
|
|
||||||
runtime->exclusiveThreadsPaused = oldExclusiveThreadsPaused;
|
|
||||||
|
|
||||||
if (!needsUnpause)
|
|
||||||
return;
|
|
||||||
|
|
||||||
WorkerThreadState &state = *runtime->workerThreadState;
|
|
||||||
AutoLockWorkerThreadState lock(state);
|
|
||||||
|
|
||||||
state.shouldPause = 0;
|
|
||||||
|
|
||||||
// Notify all workers, to ensure that each wakes up.
|
|
||||||
state.notifyAll(WorkerThreadState::PRODUCER);
|
|
||||||
}
|
|
||||||
|
|
||||||
AutoPauseCurrentWorkerThread::AutoPauseCurrentWorkerThread(ExclusiveContext *cx
|
|
||||||
MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
|
|
||||||
: cx(cx)
|
|
||||||
{
|
|
||||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
|
||||||
|
|
||||||
// If the current thread is a worker thread, treat it as paused while
|
|
||||||
// the caller is waiting for another worker thread to complete. Otherwise
|
|
||||||
// we will not wake up and mark this as paused due to the loop in
|
|
||||||
// AutoPauseWorkersForTracing.
|
|
||||||
if (cx->workerThread()) {
|
|
||||||
WorkerThreadState &state = *cx->workerThreadState();
|
|
||||||
JS_ASSERT(state.isLocked());
|
|
||||||
|
|
||||||
state.numPaused++;
|
|
||||||
if (state.numPaused == state.numThreads)
|
|
||||||
state.notifyAll(WorkerThreadState::CONSUMER);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AutoPauseCurrentWorkerThread::~AutoPauseCurrentWorkerThread()
|
|
||||||
{
|
|
||||||
if (cx->workerThread()) {
|
|
||||||
WorkerThreadState &state = *cx->workerThreadState();
|
|
||||||
JS_ASSERT(state.isLocked());
|
|
||||||
|
|
||||||
state.numPaused--;
|
|
||||||
|
|
||||||
// Before resuming execution of the worker thread, make sure the main
|
|
||||||
// thread does not expect worker threads to be paused.
|
|
||||||
if (state.shouldPause)
|
|
||||||
cx->workerThread()->pause();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
WorkerThread::pause()
|
|
||||||
{
|
|
||||||
WorkerThreadState &state = *runtime->workerThreadState;
|
|
||||||
JS_ASSERT(state.isLocked());
|
|
||||||
JS_ASSERT(state.shouldPause);
|
|
||||||
|
|
||||||
JS_ASSERT(state.numPaused < state.numThreads);
|
|
||||||
state.numPaused++;
|
|
||||||
|
|
||||||
// Don't bother to notify the main thread until all workers have paused.
|
|
||||||
if (state.numPaused == state.numThreads)
|
|
||||||
state.notifyAll(WorkerThreadState::CONSUMER);
|
|
||||||
|
|
||||||
while (state.shouldPause)
|
|
||||||
state.wait(WorkerThreadState::PRODUCER);
|
|
||||||
|
|
||||||
state.numPaused--;
|
|
||||||
}
|
|
||||||
|
|
||||||
#else /* JS_WORKER_THREADS */
|
#else /* JS_WORKER_THREADS */
|
||||||
|
|
||||||
using namespace js;
|
using namespace js;
|
||||||
@ -1085,26 +974,6 @@ ScriptSource::getOffThreadCompressionChars(ExclusiveContext *cx)
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
AutoPauseWorkersForTracing::AutoPauseWorkersForTracing(JSRuntime *rt
|
|
||||||
MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
|
|
||||||
{
|
|
||||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
|
||||||
}
|
|
||||||
|
|
||||||
AutoPauseWorkersForTracing::~AutoPauseWorkersForTracing()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
AutoPauseCurrentWorkerThread::AutoPauseCurrentWorkerThread(ExclusiveContext *cx
|
|
||||||
MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
|
|
||||||
{
|
|
||||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
|
||||||
}
|
|
||||||
|
|
||||||
AutoPauseCurrentWorkerThread::~AutoPauseCurrentWorkerThread()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
frontend::CompileError &
|
frontend::CompileError &
|
||||||
ExclusiveContext::addPendingCompileError()
|
ExclusiveContext::addPendingCompileError()
|
||||||
{
|
{
|
||||||
|
@ -41,15 +41,6 @@ class WorkerThreadState
|
|||||||
WorkerThread *threads;
|
WorkerThread *threads;
|
||||||
size_t numThreads;
|
size_t numThreads;
|
||||||
|
|
||||||
/*
|
|
||||||
* Whether all worker threads thread should pause their activity. This acts
|
|
||||||
* like the runtime's interrupt field and may be read without locking.
|
|
||||||
*/
|
|
||||||
volatile size_t shouldPause;
|
|
||||||
|
|
||||||
/* After shouldPause is set, the number of threads which are paused. */
|
|
||||||
uint32_t numPaused;
|
|
||||||
|
|
||||||
enum CondVar {
|
enum CondVar {
|
||||||
/* For notifying threads waiting for work that they may be able to make progress. */
|
/* For notifying threads waiting for work that they may be able to make progress. */
|
||||||
CONSUMER,
|
CONSUMER,
|
||||||
@ -83,11 +74,14 @@ class WorkerThreadState
|
|||||||
/* Worklist for source compression worker threads. */
|
/* Worklist for source compression worker threads. */
|
||||||
Vector<SourceCompressionTask *, 0, SystemAllocPolicy> compressionWorklist;
|
Vector<SourceCompressionTask *, 0, SystemAllocPolicy> compressionWorklist;
|
||||||
|
|
||||||
WorkerThreadState() { mozilla::PodZero(this); }
|
WorkerThreadState(JSRuntime *rt) {
|
||||||
|
mozilla::PodZero(this);
|
||||||
|
runtime = rt;
|
||||||
|
}
|
||||||
~WorkerThreadState();
|
~WorkerThreadState();
|
||||||
|
|
||||||
bool init(JSRuntime *rt);
|
bool init();
|
||||||
void cleanup(JSRuntime *rt);
|
void cleanup();
|
||||||
|
|
||||||
void lock();
|
void lock();
|
||||||
void unlock();
|
void unlock();
|
||||||
@ -134,6 +128,8 @@ class WorkerThreadState
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
JSRuntime *runtime;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Lock protecting all mutable shared state accessed by helper threads, and
|
* Lock protecting all mutable shared state accessed by helper threads, and
|
||||||
* used by all condition variables.
|
* used by all condition variables.
|
||||||
@ -188,9 +184,6 @@ struct WorkerThread
|
|||||||
return !ionBuilder && !asmData && !parseTask && !compressionTask;
|
return !ionBuilder && !asmData && !parseTask && !compressionTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void maybePause();
|
|
||||||
|
|
||||||
void pause();
|
|
||||||
void destroy();
|
void destroy();
|
||||||
|
|
||||||
void handleAsmJSWorkload(WorkerThreadState &state);
|
void handleAsmJSWorkload(WorkerThreadState &state);
|
||||||
@ -314,59 +307,6 @@ class AutoUnlockWorkerThreadState
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef JS_WORKER_THREADS
|
|
||||||
|
|
||||||
inline void
|
|
||||||
WorkerThread::maybePause()
|
|
||||||
{
|
|
||||||
if (runtime->workerThreadState->shouldPause) {
|
|
||||||
AutoLockWorkerThreadState lock(*runtime->workerThreadState);
|
|
||||||
pause();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // JS_WORKER_THREADS
|
|
||||||
|
|
||||||
/* Pause any threads that are running jobs off thread during GC activity. */
|
|
||||||
class AutoPauseWorkersForTracing
|
|
||||||
{
|
|
||||||
#ifdef JS_WORKER_THREADS
|
|
||||||
JSRuntime *runtime;
|
|
||||||
bool needsUnpause;
|
|
||||||
mozilla::DebugOnly<bool> oldExclusiveThreadsPaused;
|
|
||||||
#endif
|
|
||||||
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
|
||||||
|
|
||||||
public:
|
|
||||||
AutoPauseWorkersForTracing(JSRuntime *rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
|
|
||||||
~AutoPauseWorkersForTracing();
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If the current thread is a worker thread, treat it as paused during this
|
|
||||||
* class's lifetime. This should be used at any time the current thread is
|
|
||||||
* waiting for a worker to complete.
|
|
||||||
*/
|
|
||||||
class AutoPauseCurrentWorkerThread
|
|
||||||
{
|
|
||||||
#ifdef JS_WORKER_THREADS
|
|
||||||
ExclusiveContext *cx;
|
|
||||||
#endif
|
|
||||||
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
|
||||||
|
|
||||||
public:
|
|
||||||
AutoPauseCurrentWorkerThread(ExclusiveContext *cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
|
|
||||||
~AutoPauseCurrentWorkerThread();
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Wait for any in progress off thread parses to halt. */
|
|
||||||
void
|
|
||||||
PauseOffThreadParsing();
|
|
||||||
|
|
||||||
/* Resume any paused off thread parses. */
|
|
||||||
void
|
|
||||||
ResumeOffThreadParsing();
|
|
||||||
|
|
||||||
#ifdef JS_ION
|
#ifdef JS_ION
|
||||||
struct AsmJSParallelTask
|
struct AsmJSParallelTask
|
||||||
{
|
{
|
||||||
@ -464,12 +404,6 @@ struct SourceCompressionTask
|
|||||||
complete();
|
complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
void maybePause() {
|
|
||||||
#ifdef JS_WORKER_THREADS
|
|
||||||
workerThread->maybePause();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
bool compress();
|
bool compress();
|
||||||
bool complete();
|
bool complete();
|
||||||
void abort() { abort_ = 1; }
|
void abort() { abort_ = 1; }
|
||||||
|
@ -700,7 +700,8 @@ RegExpCompartment::get(ExclusiveContext *cx, JSAtom *source, RegExpFlag flags, R
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ScopedJSDeletePtr<RegExpShared> shared(cx->new_<RegExpShared>(source, flags, cx->gcNumber()));
|
uint64_t gcNumber = cx->zone()->gcNumber();
|
||||||
|
ScopedJSDeletePtr<RegExpShared> shared(cx->new_<RegExpShared>(source, flags, gcNumber));
|
||||||
if (!shared)
|
if (!shared)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#include "jsproxy.h"
|
#include "jsproxy.h"
|
||||||
|
|
||||||
#include "gc/Marking.h"
|
#include "gc/Marking.h"
|
||||||
|
#include "gc/Zone.h"
|
||||||
#if ENABLE_YARR_JIT
|
#if ENABLE_YARR_JIT
|
||||||
#include "yarr/YarrJIT.h"
|
#include "yarr/YarrJIT.h"
|
||||||
#else
|
#else
|
||||||
@ -189,7 +190,7 @@ class RegExpShared
|
|||||||
|
|
||||||
/* Called when a RegExpShared is installed into a RegExpObject. */
|
/* Called when a RegExpShared is installed into a RegExpObject. */
|
||||||
void prepareForUse(ExclusiveContext *cx) {
|
void prepareForUse(ExclusiveContext *cx) {
|
||||||
gcNumberWhenUsed = cx->gcNumber();
|
gcNumberWhenUsed = cx->zone()->gcNumber();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Primary interface: run this regular expression on the given string. */
|
/* Primary interface: run this regular expression on the given string. */
|
||||||
|
@ -66,7 +66,6 @@ PerThreadData::PerThreadData(JSRuntime *runtime)
|
|||||||
asmJSActivationStack_(nullptr),
|
asmJSActivationStack_(nullptr),
|
||||||
dtoaState(nullptr),
|
dtoaState(nullptr),
|
||||||
suppressGC(0),
|
suppressGC(0),
|
||||||
gcKeepAtoms(0),
|
|
||||||
activeCompilations(0)
|
activeCompilations(0)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
@ -126,7 +125,6 @@ JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads)
|
|||||||
exclusiveAccessLock(nullptr),
|
exclusiveAccessLock(nullptr),
|
||||||
exclusiveAccessOwner(nullptr),
|
exclusiveAccessOwner(nullptr),
|
||||||
mainThreadHasExclusiveAccess(false),
|
mainThreadHasExclusiveAccess(false),
|
||||||
exclusiveThreadsPaused(false),
|
|
||||||
numExclusiveThreads(0),
|
numExclusiveThreads(0),
|
||||||
#endif
|
#endif
|
||||||
systemZone(nullptr),
|
systemZone(nullptr),
|
||||||
@ -249,6 +247,7 @@ JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads)
|
|||||||
haveCreatedContext(false),
|
haveCreatedContext(false),
|
||||||
data(nullptr),
|
data(nullptr),
|
||||||
gcLock(nullptr),
|
gcLock(nullptr),
|
||||||
|
gcLockOwner(nullptr),
|
||||||
gcHelperThread(thisFromCtor()),
|
gcHelperThread(thisFromCtor()),
|
||||||
signalHandlersInstalled_(false),
|
signalHandlersInstalled_(false),
|
||||||
defaultFreeOp_(thisFromCtor(), false),
|
defaultFreeOp_(thisFromCtor(), false),
|
||||||
@ -265,6 +264,8 @@ JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads)
|
|||||||
numGrouping(0),
|
numGrouping(0),
|
||||||
#endif
|
#endif
|
||||||
mathCache_(nullptr),
|
mathCache_(nullptr),
|
||||||
|
activeCompilations_(0),
|
||||||
|
keepAtoms_(0),
|
||||||
trustedPrincipals_(nullptr),
|
trustedPrincipals_(nullptr),
|
||||||
atomsCompartment_(nullptr),
|
atomsCompartment_(nullptr),
|
||||||
beingDestroyed_(false),
|
beingDestroyed_(false),
|
||||||
@ -333,6 +334,10 @@ JSRuntime::init(uint32_t maxbytes)
|
|||||||
operationCallbackLock = PR_NewLock();
|
operationCallbackLock = PR_NewLock();
|
||||||
if (!operationCallbackLock)
|
if (!operationCallbackLock)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
gcLock = PR_NewLock();
|
||||||
|
if (!gcLock)
|
||||||
|
return false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef JS_WORKER_THREADS
|
#ifdef JS_WORKER_THREADS
|
||||||
@ -414,7 +419,7 @@ JSRuntime::~JSRuntime()
|
|||||||
|
|
||||||
#ifdef JS_WORKER_THREADS
|
#ifdef JS_WORKER_THREADS
|
||||||
if (workerThreadState)
|
if (workerThreadState)
|
||||||
workerThreadState->cleanup(this);
|
workerThreadState->cleanup();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Poison common names before final GC. */
|
/* Poison common names before final GC. */
|
||||||
@ -457,10 +462,9 @@ JSRuntime::~JSRuntime()
|
|||||||
if (exclusiveAccessLock)
|
if (exclusiveAccessLock)
|
||||||
PR_DestroyLock(exclusiveAccessLock);
|
PR_DestroyLock(exclusiveAccessLock);
|
||||||
|
|
||||||
JS_ASSERT(!numExclusiveThreads);
|
|
||||||
|
|
||||||
// Avoid bogus asserts during teardown.
|
// Avoid bogus asserts during teardown.
|
||||||
exclusiveThreadsPaused = true;
|
JS_ASSERT(!numExclusiveThreads);
|
||||||
|
mainThreadHasExclusiveAccess = true;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
@ -835,3 +839,30 @@ js::CurrentThreadCanAccessZone(Zone *zone)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
|
||||||
|
void
|
||||||
|
JSRuntime::assertCanLock(RuntimeLock which)
|
||||||
|
{
|
||||||
|
#ifdef JS_THREADSAFE
|
||||||
|
// In the switch below, each case falls through to the one below it. None
|
||||||
|
// of the runtime locks are reentrant, and when multiple locks are acquired
|
||||||
|
// it must be done in the order below.
|
||||||
|
switch (which) {
|
||||||
|
case ExclusiveAccessLock:
|
||||||
|
JS_ASSERT(exclusiveAccessOwner != PR_GetCurrentThread());
|
||||||
|
case WorkerThreadStateLock:
|
||||||
|
JS_ASSERT_IF(workerThreadState, !workerThreadState->isLocked());
|
||||||
|
case OperationCallbackLock:
|
||||||
|
JS_ASSERT(!currentThreadOwnsOperationCallbackLock());
|
||||||
|
case GCLock:
|
||||||
|
JS_ASSERT(gcLockOwner != PR_GetCurrentThread());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
MOZ_CRASH();
|
||||||
|
}
|
||||||
|
#endif // JS_THREADSAFE
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // DEBUG
|
||||||
|
@ -55,6 +55,7 @@ namespace js {
|
|||||||
|
|
||||||
class PerThreadData;
|
class PerThreadData;
|
||||||
class ThreadSafeContext;
|
class ThreadSafeContext;
|
||||||
|
class AutoKeepAtoms;
|
||||||
|
|
||||||
/* Thread Local Storage slot for storing the runtime for a thread. */
|
/* Thread Local Storage slot for storing the runtime for a thread. */
|
||||||
extern mozilla::ThreadLocal<PerThreadData*> TlsPerThreadData;
|
extern mozilla::ThreadLocal<PerThreadData*> TlsPerThreadData;
|
||||||
@ -571,16 +572,7 @@ class PerThreadData : public PerThreadDataFriendFields,
|
|||||||
*/
|
*/
|
||||||
int32_t suppressGC;
|
int32_t suppressGC;
|
||||||
|
|
||||||
/*
|
// Whether there is an active compilation on this thread.
|
||||||
* Count of AutoKeepAtoms instances on the stack. When any instances exist,
|
|
||||||
* atoms in the runtime will not be collected.
|
|
||||||
*/
|
|
||||||
unsigned gcKeepAtoms;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Count of currently active compilations. When any compilations exist,
|
|
||||||
* the runtime's parseMapPool will not be purged.
|
|
||||||
*/
|
|
||||||
unsigned activeCompilations;
|
unsigned activeCompilations;
|
||||||
|
|
||||||
PerThreadData(JSRuntime *runtime);
|
PerThreadData(JSRuntime *runtime);
|
||||||
@ -593,6 +585,10 @@ class PerThreadData : public PerThreadDataFriendFields,
|
|||||||
bool associatedWith(const JSRuntime *rt) { return runtime_ == rt; }
|
bool associatedWith(const JSRuntime *rt) { return runtime_ == rt; }
|
||||||
inline JSRuntime *runtimeFromMainThread();
|
inline JSRuntime *runtimeFromMainThread();
|
||||||
inline JSRuntime *runtimeIfOnOwnerThread();
|
inline JSRuntime *runtimeIfOnOwnerThread();
|
||||||
|
|
||||||
|
inline bool exclusiveThreadsPresent();
|
||||||
|
inline void addActiveCompilation();
|
||||||
|
inline void removeActiveCompilation();
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class Client>
|
template<class Client>
|
||||||
@ -676,8 +672,6 @@ class MarkingValidator;
|
|||||||
typedef Vector<JS::Zone *, 4, SystemAllocPolicy> ZoneVector;
|
typedef Vector<JS::Zone *, 4, SystemAllocPolicy> ZoneVector;
|
||||||
|
|
||||||
class AutoLockForExclusiveAccess;
|
class AutoLockForExclusiveAccess;
|
||||||
class AutoPauseWorkersForTracing;
|
|
||||||
class ThreadDataIter;
|
|
||||||
|
|
||||||
void RecomputeStackLimit(JSRuntime *rt, StackKind kind);
|
void RecomputeStackLimit(JSRuntime *rt, StackKind kind);
|
||||||
|
|
||||||
@ -720,6 +714,21 @@ struct JSRuntime : public JS::shadow::Runtime,
|
|||||||
/* Branch callback */
|
/* Branch callback */
|
||||||
JSOperationCallback operationCallback;
|
JSOperationCallback operationCallback;
|
||||||
|
|
||||||
|
// There are several per-runtime locks indicated by the enum below. When
|
||||||
|
// acquiring multiple of these locks, the acquisition must be done in the
|
||||||
|
// order below to avoid deadlocks.
|
||||||
|
enum RuntimeLock {
|
||||||
|
ExclusiveAccessLock,
|
||||||
|
WorkerThreadStateLock,
|
||||||
|
OperationCallbackLock,
|
||||||
|
GCLock
|
||||||
|
};
|
||||||
|
#ifdef DEBUG
|
||||||
|
void assertCanLock(RuntimeLock which);
|
||||||
|
#else
|
||||||
|
void assertCanLock(RuntimeLock which) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/*
|
/*
|
||||||
* Lock taken when triggering the operation callback from another thread.
|
* Lock taken when triggering the operation callback from another thread.
|
||||||
@ -738,7 +747,7 @@ struct JSRuntime : public JS::shadow::Runtime,
|
|||||||
public:
|
public:
|
||||||
AutoLockForOperationCallback(JSRuntime *rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM) : rt(rt) {
|
AutoLockForOperationCallback(JSRuntime *rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM) : rt(rt) {
|
||||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||||
JS_ASSERT(!rt->currentThreadOwnsOperationCallbackLock());
|
rt->assertCanLock(JSRuntime::OperationCallbackLock);
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
PR_Lock(rt->operationCallbackLock);
|
PR_Lock(rt->operationCallbackLock);
|
||||||
rt->operationCallbackOwner = PR_GetCurrentThread();
|
rt->operationCallbackOwner = PR_GetCurrentThread();
|
||||||
@ -784,14 +793,11 @@ struct JSRuntime : public JS::shadow::Runtime,
|
|||||||
PRLock *exclusiveAccessLock;
|
PRLock *exclusiveAccessLock;
|
||||||
mozilla::DebugOnly<PRThread *> exclusiveAccessOwner;
|
mozilla::DebugOnly<PRThread *> exclusiveAccessOwner;
|
||||||
mozilla::DebugOnly<bool> mainThreadHasExclusiveAccess;
|
mozilla::DebugOnly<bool> mainThreadHasExclusiveAccess;
|
||||||
mozilla::DebugOnly<bool> exclusiveThreadsPaused;
|
|
||||||
|
|
||||||
/* Number of non-main threads with an ExclusiveContext. */
|
/* Number of non-main threads with an ExclusiveContext. */
|
||||||
size_t numExclusiveThreads;
|
size_t numExclusiveThreads;
|
||||||
|
|
||||||
friend class js::AutoLockForExclusiveAccess;
|
friend class js::AutoLockForExclusiveAccess;
|
||||||
friend class js::AutoPauseWorkersForTracing;
|
|
||||||
friend class js::ThreadDataIter;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void setUsedByExclusiveThread(JS::Zone *zone);
|
void setUsedByExclusiveThread(JS::Zone *zone);
|
||||||
@ -802,7 +808,6 @@ struct JSRuntime : public JS::shadow::Runtime,
|
|||||||
bool currentThreadHasExclusiveAccess() {
|
bool currentThreadHasExclusiveAccess() {
|
||||||
#if defined(JS_WORKER_THREADS) && defined(DEBUG)
|
#if defined(JS_WORKER_THREADS) && defined(DEBUG)
|
||||||
return (!numExclusiveThreads && mainThreadHasExclusiveAccess) ||
|
return (!numExclusiveThreads && mainThreadHasExclusiveAccess) ||
|
||||||
exclusiveThreadsPaused ||
|
|
||||||
exclusiveAccessOwner == PR_GetCurrentThread();
|
exclusiveAccessOwner == PR_GetCurrentThread();
|
||||||
#else
|
#else
|
||||||
return true;
|
return true;
|
||||||
@ -1332,8 +1337,32 @@ struct JSRuntime : public JS::shadow::Runtime,
|
|||||||
/* Client opaque pointers */
|
/* Client opaque pointers */
|
||||||
void *data;
|
void *data;
|
||||||
|
|
||||||
|
private:
|
||||||
/* Synchronize GC heap access between main thread and GCHelperThread. */
|
/* Synchronize GC heap access between main thread and GCHelperThread. */
|
||||||
PRLock *gcLock;
|
PRLock *gcLock;
|
||||||
|
mozilla::DebugOnly<PRThread *> gcLockOwner;
|
||||||
|
|
||||||
|
friend class js::GCHelperThread;
|
||||||
|
public:
|
||||||
|
|
||||||
|
void lockGC() {
|
||||||
|
#ifdef JS_THREADSAFE
|
||||||
|
assertCanLock(GCLock);
|
||||||
|
PR_Lock(gcLock);
|
||||||
|
JS_ASSERT(!gcLockOwner);
|
||||||
|
#ifdef DEBUG
|
||||||
|
gcLockOwner = PR_GetCurrentThread();
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void unlockGC() {
|
||||||
|
#ifdef JS_THREADSAFE
|
||||||
|
JS_ASSERT(gcLockOwner == PR_GetCurrentThread());
|
||||||
|
gcLockOwner = nullptr;
|
||||||
|
PR_Unlock(gcLock);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
js::GCHelperThread gcHelperThread;
|
js::GCHelperThread gcHelperThread;
|
||||||
|
|
||||||
@ -1410,14 +1439,45 @@ struct JSRuntime : public JS::shadow::Runtime,
|
|||||||
js::ConservativeGCData conservativeGC;
|
js::ConservativeGCData conservativeGC;
|
||||||
|
|
||||||
// Pool of maps used during parse/emit. This may be modified by threads
|
// Pool of maps used during parse/emit. This may be modified by threads
|
||||||
// with an ExclusiveContext and requires a lock.
|
// with an ExclusiveContext and requires a lock. Active compilations
|
||||||
|
// prevent the pool from being purged during GCs.
|
||||||
private:
|
private:
|
||||||
js::frontend::ParseMapPool parseMapPool_;
|
js::frontend::ParseMapPool parseMapPool_;
|
||||||
|
unsigned activeCompilations_;
|
||||||
public:
|
public:
|
||||||
js::frontend::ParseMapPool &parseMapPool() {
|
js::frontend::ParseMapPool &parseMapPool() {
|
||||||
JS_ASSERT(currentThreadHasExclusiveAccess());
|
JS_ASSERT(currentThreadHasExclusiveAccess());
|
||||||
return parseMapPool_;
|
return parseMapPool_;
|
||||||
}
|
}
|
||||||
|
bool hasActiveCompilations() {
|
||||||
|
return activeCompilations_ != 0;
|
||||||
|
}
|
||||||
|
void addActiveCompilation() {
|
||||||
|
JS_ASSERT(currentThreadHasExclusiveAccess());
|
||||||
|
activeCompilations_++;
|
||||||
|
}
|
||||||
|
void removeActiveCompilation() {
|
||||||
|
JS_ASSERT(currentThreadHasExclusiveAccess());
|
||||||
|
activeCompilations_--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count of AutoKeepAtoms instances on the main thread's stack. When any
|
||||||
|
// instances exist, atoms in the runtime will not be collected. Threads
|
||||||
|
// with an ExclusiveContext do not increment this value, but the presence
|
||||||
|
// of any such threads also inhibits collection of atoms. We don't scan the
|
||||||
|
// stacks of exclusive threads, so we need to avoid collecting their
|
||||||
|
// objects in another way. The only GC thing pointers they have are to
|
||||||
|
// their exclusive compartment (which is not collected) or to the atoms
|
||||||
|
// compartment. Therefore, we avoid collecting the atoms compartment when
|
||||||
|
// exclusive threads are running.
|
||||||
|
private:
|
||||||
|
unsigned keepAtoms_;
|
||||||
|
friend class js::AutoKeepAtoms;
|
||||||
|
public:
|
||||||
|
bool keepAtoms() {
|
||||||
|
JS_ASSERT(CurrentThreadCanAccessRuntime(this));
|
||||||
|
return keepAtoms_ != 0 || exclusiveThreadsPresent();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const JSPrincipals *trustedPrincipals_;
|
const JSPrincipals *trustedPrincipals_;
|
||||||
@ -1722,14 +1782,6 @@ FreeOp::free_(void *p)
|
|||||||
js_free(p);
|
js_free(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef JS_THREADSAFE
|
|
||||||
# define JS_LOCK_GC(rt) PR_Lock((rt)->gcLock)
|
|
||||||
# define JS_UNLOCK_GC(rt) PR_Unlock((rt)->gcLock)
|
|
||||||
#else
|
|
||||||
# define JS_LOCK_GC(rt) do { } while (0)
|
|
||||||
# define JS_UNLOCK_GC(rt) do { } while (0)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class AutoLockGC
|
class AutoLockGC
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -1740,13 +1792,13 @@ class AutoLockGC
|
|||||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||||
// Avoid MSVC warning C4390 for non-threadsafe builds.
|
// Avoid MSVC warning C4390 for non-threadsafe builds.
|
||||||
if (rt)
|
if (rt)
|
||||||
JS_LOCK_GC(rt);
|
rt->lockGC();
|
||||||
}
|
}
|
||||||
|
|
||||||
~AutoLockGC()
|
~AutoLockGC()
|
||||||
{
|
{
|
||||||
if (runtime)
|
if (runtime)
|
||||||
JS_UNLOCK_GC(runtime);
|
runtime->unlockGC();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool locked() const {
|
bool locked() const {
|
||||||
@ -1757,7 +1809,7 @@ class AutoLockGC
|
|||||||
JS_ASSERT(rt);
|
JS_ASSERT(rt);
|
||||||
JS_ASSERT(!runtime);
|
JS_ASSERT(!runtime);
|
||||||
runtime = rt;
|
runtime = rt;
|
||||||
JS_LOCK_GC(rt);
|
rt->lockGC();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -1768,22 +1820,18 @@ class AutoLockGC
|
|||||||
class AutoUnlockGC
|
class AutoUnlockGC
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
#ifdef JS_THREADSAFE
|
|
||||||
JSRuntime *rt;
|
JSRuntime *rt;
|
||||||
#endif
|
|
||||||
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit AutoUnlockGC(JSRuntime *rt
|
explicit AutoUnlockGC(JSRuntime *rt
|
||||||
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
||||||
#ifdef JS_THREADSAFE
|
|
||||||
: rt(rt)
|
: rt(rt)
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||||
JS_UNLOCK_GC(rt);
|
rt->unlockGC();
|
||||||
}
|
}
|
||||||
~AutoUnlockGC() { JS_LOCK_GC(rt); }
|
~AutoUnlockGC() { rt->lockGC(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class MOZ_STACK_CLASS AutoKeepAtoms
|
class MOZ_STACK_CLASS AutoKeepAtoms
|
||||||
@ -1797,10 +1845,19 @@ class MOZ_STACK_CLASS AutoKeepAtoms
|
|||||||
: pt(pt)
|
: pt(pt)
|
||||||
{
|
{
|
||||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||||
pt->gcKeepAtoms++;
|
if (JSRuntime *rt = pt->runtimeIfOnOwnerThread()) {
|
||||||
|
rt->keepAtoms_++;
|
||||||
|
} else {
|
||||||
|
// This should be a thread with an exclusive context, which will
|
||||||
|
// always inhibit collection of atoms.
|
||||||
|
JS_ASSERT(pt->exclusiveThreadsPresent());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
~AutoKeepAtoms() {
|
~AutoKeepAtoms() {
|
||||||
pt->gcKeepAtoms--;
|
if (JSRuntime *rt = pt->runtimeIfOnOwnerThread()) {
|
||||||
|
JS_ASSERT(rt->keepAtoms_);
|
||||||
|
rt->keepAtoms_--;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1814,14 +1871,35 @@ PerThreadData::setIonStackLimit(uintptr_t limit)
|
|||||||
inline JSRuntime *
|
inline JSRuntime *
|
||||||
PerThreadData::runtimeFromMainThread()
|
PerThreadData::runtimeFromMainThread()
|
||||||
{
|
{
|
||||||
JS_ASSERT(js::CurrentThreadCanAccessRuntime(runtime_));
|
JS_ASSERT(CurrentThreadCanAccessRuntime(runtime_));
|
||||||
return runtime_;
|
return runtime_;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline JSRuntime *
|
inline JSRuntime *
|
||||||
PerThreadData::runtimeIfOnOwnerThread()
|
PerThreadData::runtimeIfOnOwnerThread()
|
||||||
{
|
{
|
||||||
return js::CurrentThreadCanAccessRuntime(runtime_) ? runtime_ : nullptr;
|
return CurrentThreadCanAccessRuntime(runtime_) ? runtime_ : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool
|
||||||
|
PerThreadData::exclusiveThreadsPresent()
|
||||||
|
{
|
||||||
|
return runtime_->exclusiveThreadsPresent();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void
|
||||||
|
PerThreadData::addActiveCompilation()
|
||||||
|
{
|
||||||
|
activeCompilations++;
|
||||||
|
runtime_->addActiveCompilation();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void
|
||||||
|
PerThreadData::removeActiveCompilation()
|
||||||
|
{
|
||||||
|
JS_ASSERT(activeCompilations);
|
||||||
|
activeCompilations--;
|
||||||
|
runtime_->removeActiveCompilation();
|
||||||
}
|
}
|
||||||
|
|
||||||
/************************************************************************/
|
/************************************************************************/
|
||||||
@ -1913,45 +1991,6 @@ class RuntimeAllocPolicy
|
|||||||
void reportAllocOverflow() const {}
|
void reportAllocOverflow() const {}
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
|
||||||
* Enumerate all the per thread data in a runtime.
|
|
||||||
*/
|
|
||||||
class ThreadDataIter {
|
|
||||||
PerThreadData *iter;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit ThreadDataIter(JSRuntime *rt) {
|
|
||||||
#ifdef JS_WORKER_THREADS
|
|
||||||
// Only allow iteration over a runtime's threads when those threads are
|
|
||||||
// paused, to avoid racing when reading data from the PerThreadData.
|
|
||||||
JS_ASSERT(rt->exclusiveThreadsPaused);
|
|
||||||
#endif
|
|
||||||
iter = rt->threadList.getFirst();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool done() const {
|
|
||||||
return !iter;
|
|
||||||
}
|
|
||||||
|
|
||||||
void next() {
|
|
||||||
JS_ASSERT(!done());
|
|
||||||
iter = iter->getNext();
|
|
||||||
}
|
|
||||||
|
|
||||||
PerThreadData *get() const {
|
|
||||||
JS_ASSERT(!done());
|
|
||||||
return iter;
|
|
||||||
}
|
|
||||||
|
|
||||||
operator PerThreadData *() const {
|
|
||||||
return get();
|
|
||||||
}
|
|
||||||
|
|
||||||
PerThreadData *operator ->() const {
|
|
||||||
return get();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
extern const JSSecurityCallbacks NullSecurityCallbacks;
|
extern const JSSecurityCallbacks NullSecurityCallbacks;
|
||||||
|
|
||||||
} /* namespace js */
|
} /* namespace js */
|
||||||
|
Loading…
Reference in New Issue
Block a user