зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1337491 - Off thread parsing changes for multithreaded runtimes, r=jandem,jonco.
--HG-- extra : rebase_source : 5c8f0f3bff71caf03cddfd67057667c8454addc4
This commit is contained in:
Родитель
6df0361de9
Коммит
80eaee2561
|
@ -1636,8 +1636,10 @@ OutlineTypedObject::obj_trace(JSTracer* trc, JSObject* object)
|
|||
newData += reinterpret_cast<uint8_t*>(owner) - reinterpret_cast<uint8_t*>(oldOwner);
|
||||
typedObj.setData(newData);
|
||||
|
||||
Nursery& nursery = typedObj.zoneFromAnyThread()->group()->nursery();
|
||||
nursery.maybeSetForwardingPointer(trc, oldData, newData, /* direct = */ false);
|
||||
if (trc->isTenuringTracer()) {
|
||||
Nursery& nursery = typedObj.zoneFromAnyThread()->group()->nursery();
|
||||
nursery.maybeSetForwardingPointer(trc, oldData, newData, /* direct = */ false);
|
||||
}
|
||||
}
|
||||
|
||||
if (!descr.opaque() || !typedObj.isAttached())
|
||||
|
|
|
@ -41,6 +41,9 @@ js::Allocate(JSContext* cx, AllocKind kind, size_t nDynamicSlots, InitialHeap he
|
|||
|
||||
// Off-thread alloc cannot trigger GC or make runtime assertions.
|
||||
if (cx->helperThread()) {
|
||||
// The zone group used by the helper thread should have been created
|
||||
// with a disabled nursery.
|
||||
MOZ_ASSERT(!cx->nursery().isEnabled());
|
||||
JSObject* obj = GCRuntime::tryNewTenuredObject<NoGC>(cx, kind, thingSize, nDynamicSlots);
|
||||
if (MOZ_UNLIKELY(allowGC && !obj))
|
||||
ReportOutOfMemory(cx);
|
||||
|
|
|
@ -2414,7 +2414,7 @@ JSObject*
|
|||
js::TenuringTracer::moveToTenured(JSObject* src)
|
||||
{
|
||||
MOZ_ASSERT(IsInsideNursery(src));
|
||||
MOZ_ASSERT(!src->zone()->usedByExclusiveThread);
|
||||
MOZ_ASSERT(!src->zone()->usedByHelperThread());
|
||||
|
||||
AllocKind dstKind = src->allocKindForTenure(nursery());
|
||||
Zone* zone = src->zone();
|
||||
|
|
|
@ -39,28 +39,22 @@ template <typename T>
|
|||
static inline T*
|
||||
AllocateObjectBuffer(JSContext* cx, uint32_t count)
|
||||
{
|
||||
if (!cx->helperThread()) {
|
||||
size_t nbytes = JS_ROUNDUP(count * sizeof(T), sizeof(Value));
|
||||
T* buffer = static_cast<T*>(cx->nursery().allocateBuffer(cx->zone(), nbytes));
|
||||
if (!buffer)
|
||||
ReportOutOfMemory(cx);
|
||||
return buffer;
|
||||
}
|
||||
return cx->zone()->pod_malloc<T>(count);
|
||||
size_t nbytes = JS_ROUNDUP(count * sizeof(T), sizeof(Value));
|
||||
T* buffer = static_cast<T*>(cx->nursery().allocateBuffer(cx->zone(), nbytes));
|
||||
if (!buffer)
|
||||
ReportOutOfMemory(cx);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline T*
|
||||
AllocateObjectBuffer(JSContext* cx, JSObject* obj, uint32_t count)
|
||||
{
|
||||
if (!cx->helperThread()) {
|
||||
size_t nbytes = JS_ROUNDUP(count * sizeof(T), sizeof(Value));
|
||||
T* buffer = static_cast<T*>(cx->nursery().allocateBuffer(obj, nbytes));
|
||||
if (!buffer)
|
||||
ReportOutOfMemory(cx);
|
||||
return buffer;
|
||||
}
|
||||
return obj->zone()->pod_malloc<T>(count);
|
||||
size_t nbytes = JS_ROUNDUP(count * sizeof(T), sizeof(Value));
|
||||
T* buffer = static_cast<T*>(cx->nursery().allocateBuffer(obj, nbytes));
|
||||
if (!buffer)
|
||||
ReportOutOfMemory(cx);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// If this returns null then the old buffer will be left alone.
|
||||
|
@ -69,15 +63,12 @@ static inline T*
|
|||
ReallocateObjectBuffer(JSContext* cx, JSObject* obj, T* oldBuffer,
|
||||
uint32_t oldCount, uint32_t newCount)
|
||||
{
|
||||
if (!cx->helperThread()) {
|
||||
T* buffer = static_cast<T*>(cx->nursery().reallocateBuffer(obj, oldBuffer,
|
||||
oldCount * sizeof(T),
|
||||
newCount * sizeof(T)));
|
||||
if (!buffer)
|
||||
ReportOutOfMemory(cx);
|
||||
return buffer;
|
||||
}
|
||||
return obj->zone()->pod_realloc<T>(oldBuffer, oldCount, newCount);
|
||||
T* buffer = static_cast<T*>(cx->nursery().reallocateBuffer(obj, oldBuffer,
|
||||
oldCount * sizeof(T),
|
||||
newCount * sizeof(T)));
|
||||
if (!buffer)
|
||||
ReportOutOfMemory(cx);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static inline void
|
||||
|
|
|
@ -298,6 +298,7 @@ js::Nursery::allocate(size_t size)
|
|||
{
|
||||
MOZ_ASSERT(isEnabled());
|
||||
MOZ_ASSERT(!JS::CurrentThreadIsHeapBusy());
|
||||
MOZ_ASSERT(CurrentThreadCanAccessRuntime(zoneGroup()->runtime));
|
||||
MOZ_ASSERT_IF(currentChunk_ == currentStartChunk_, position() >= currentStartPosition_);
|
||||
MOZ_ASSERT(position() % gc::CellSize == 0);
|
||||
MOZ_ASSERT(size % gc::CellSize == 0);
|
||||
|
|
|
@ -237,6 +237,8 @@ class Nursery
|
|||
return numChunks() * gc::ChunkSize;
|
||||
}
|
||||
size_t sizeOfMallocedBuffers(mozilla::MallocSizeOf mallocSizeOf) const {
|
||||
if (!mallocedBuffers.initialized())
|
||||
return 0;
|
||||
size_t total = 0;
|
||||
for (MallocedBuffersSet::Range r = mallocedBuffers.all(); !r.empty(); r.popFront())
|
||||
total += mallocSizeOf(r.front());
|
||||
|
|
|
@ -179,7 +179,7 @@ gc::GCRuntime::startVerifyPreBarriers()
|
|||
if (verifyPreData || isIncrementalGCInProgress())
|
||||
return;
|
||||
|
||||
if (IsIncrementalGCUnsafe(rt) != AbortReason::None || TlsContext.get()->keepAtoms || rt->exclusiveThreadsPresent())
|
||||
if (IsIncrementalGCUnsafe(rt) != AbortReason::None || TlsContext.get()->keepAtoms || rt->hasHelperThreadZones())
|
||||
return;
|
||||
|
||||
number++;
|
||||
|
@ -239,11 +239,10 @@ gc::GCRuntime::startVerifyPreBarriers()
|
|||
marker.start();
|
||||
|
||||
for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
|
||||
MOZ_ASSERT(!zone->usedByHelperThread());
|
||||
PurgeJITCaches(zone);
|
||||
if (!zone->usedByExclusiveThread) {
|
||||
zone->setNeedsIncrementalBarrier(true, Zone::UpdateJit);
|
||||
zone->arenas.purge();
|
||||
}
|
||||
zone->setNeedsIncrementalBarrier(true, Zone::UpdateJit);
|
||||
zone->arenas.purge();
|
||||
}
|
||||
|
||||
return;
|
||||
|
@ -353,7 +352,7 @@ gc::GCRuntime::endVerifyPreBarriers()
|
|||
if (!compartmentCreated &&
|
||||
IsIncrementalGCUnsafe(rt) == AbortReason::None &&
|
||||
!TlsContext.get()->keepAtoms &&
|
||||
!rt->exclusiveThreadsPresent())
|
||||
!rt->hasHelperThreadZones())
|
||||
{
|
||||
CheckEdgeTracer cetrc(rt);
|
||||
|
||||
|
|
|
@ -51,7 +51,6 @@ JS::Zone::Zone(JSRuntime* rt, ZoneGroup* group)
|
|||
initialShapes_(group, this, InitialShapeSet()),
|
||||
data(group, nullptr),
|
||||
isSystem(group, false),
|
||||
usedByExclusiveThread(false),
|
||||
#ifdef DEBUG
|
||||
gcLastZoneGroupIndex(group, 0),
|
||||
#endif
|
||||
|
@ -105,7 +104,8 @@ Zone::setNeedsIncrementalBarrier(bool needs, ShouldUpdateJit updateJit)
|
|||
jitUsingBarriers_ = needs;
|
||||
}
|
||||
|
||||
MOZ_ASSERT_IF(needs && isAtomsZone(), !runtimeFromActiveCooperatingThread()->exclusiveThreadsPresent());
|
||||
MOZ_ASSERT_IF(needs && isAtomsZone(),
|
||||
!runtimeFromActiveCooperatingThread()->hasHelperThreadZones());
|
||||
MOZ_ASSERT_IF(needs, canCollect());
|
||||
needsIncrementalBarrier_ = needs;
|
||||
}
|
||||
|
@ -295,7 +295,7 @@ 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 : runtimeFromActiveCooperatingThread()->gc.gcNumber();
|
||||
return usedByHelperThread() ? 0 : runtimeFromActiveCooperatingThread()->gc.gcNumber();
|
||||
}
|
||||
|
||||
js::jit::JitZone*
|
||||
|
@ -324,10 +324,10 @@ bool
|
|||
Zone::canCollect()
|
||||
{
|
||||
// Zones cannot be collected while in use by other threads.
|
||||
if (usedByExclusiveThread)
|
||||
if (usedByHelperThread())
|
||||
return false;
|
||||
JSRuntime* rt = runtimeFromAnyThread();
|
||||
if (isAtomsZone() && rt->exclusiveThreadsPresent())
|
||||
if (isAtomsZone() && rt->hasHelperThreadZones())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -468,7 +468,9 @@ struct Zone : public JS::shadow::Zone,
|
|||
|
||||
js::ZoneGroupData<bool> isSystem;
|
||||
|
||||
mozilla::Atomic<bool> usedByExclusiveThread;
|
||||
bool usedByHelperThread() {
|
||||
return !isAtomsZone() && group()->usedByHelperThread;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
js::ZoneGroupData<unsigned> gcLastZoneGroupIndex;
|
||||
|
@ -598,11 +600,8 @@ struct Zone : public JS::shadow::Zone,
|
|||
|
||||
namespace js {
|
||||
|
||||
// Iterate over all zone groups except those which may be in use by parse
|
||||
// threads. Pretty soon this will exclude zone groups in use by parse threads
|
||||
// (as for ZonesIter), i.e. the zone groups in use by cooperating threads,
|
||||
// except that right now parse threads use zones in the same zone group as
|
||||
// cooperating threads (bug 1323066).
|
||||
// Iterate over all zone groups except those which may be in use by helper
|
||||
// thread parse tasks.
|
||||
class ZoneGroupsIter
|
||||
{
|
||||
gc::AutoEnterIteration iterMarker;
|
||||
|
@ -613,13 +612,18 @@ class ZoneGroupsIter
|
|||
explicit ZoneGroupsIter(JSRuntime* rt) : iterMarker(&rt->gc) {
|
||||
it = rt->gc.groups.ref().begin();
|
||||
end = rt->gc.groups.ref().end();
|
||||
|
||||
if (!done() && (*it)->usedByHelperThread)
|
||||
next();
|
||||
}
|
||||
|
||||
bool done() const { return it == end; }
|
||||
|
||||
void next() {
|
||||
MOZ_ASSERT(!done());
|
||||
it++;
|
||||
do {
|
||||
it++;
|
||||
} while (!done() && (*it)->usedByHelperThread);
|
||||
}
|
||||
|
||||
ZoneGroup* get() const {
|
||||
|
@ -703,7 +707,7 @@ class ZonesIter
|
|||
if (zone.ref().done()) {
|
||||
zone.reset();
|
||||
group.next();
|
||||
} else if (!zone.ref().get()->usedByExclusiveThread) {
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,8 +15,9 @@ namespace js {
|
|||
ZoneGroup::ZoneGroup(JSRuntime* runtime)
|
||||
: runtime(runtime),
|
||||
ownerContext_(TlsContext.get()),
|
||||
enterCount(this, 1),
|
||||
zones_(),
|
||||
enterCount(1),
|
||||
zones_(this),
|
||||
usedByHelperThread(false),
|
||||
nursery_(this, this),
|
||||
storeBuffer_(this, runtime, nursery()),
|
||||
blocksToFreeAfterMinorGC((size_t) JSContext::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
|
||||
|
|
|
@ -22,19 +22,12 @@ class AutoKeepAtoms;
|
|||
typedef Vector<JS::Zone*, 4, SystemAllocPolicy> ZoneVector;
|
||||
|
||||
// Zone groups encapsulate data about a group of zones that are logically
|
||||
// related in some way. Currently, each runtime has a single zone group, and
|
||||
// all zones except the atoms zone (which has no group) are in that group.
|
||||
// This will change soon.
|
||||
// related in some way.
|
||||
//
|
||||
// When JSRuntimes become multithreaded (also happening soon; see bug 1323066),
|
||||
// zone groups will be the primary means by which threads ensure exclusive
|
||||
// access to the data they are using. Most data in a zone group, its zones,
|
||||
// Zone groups are the primary means by which threads ensure exclusive access
|
||||
// to the data they are using. Most data in a zone group, its zones,
|
||||
// compartments, GC things and so forth may only be used by the thread that has
|
||||
// entered the zone group.
|
||||
//
|
||||
// This restriction is not quite in place yet: zones used by an parse thread
|
||||
// are accessed by that thread even though it does not have exclusive access
|
||||
// to the entire zone group. This will also be changing soon.
|
||||
|
||||
class ZoneGroup
|
||||
{
|
||||
|
@ -46,7 +39,7 @@ class ZoneGroup
|
|||
UnprotectedData<CooperatingContext> ownerContext_;
|
||||
|
||||
// The number of times the context has entered this zone group.
|
||||
ZoneGroupData<size_t> enterCount;
|
||||
UnprotectedData<size_t> enterCount;
|
||||
|
||||
public:
|
||||
CooperatingContext& ownerContext() { return ownerContext_.ref(); }
|
||||
|
@ -58,10 +51,13 @@ class ZoneGroup
|
|||
|
||||
// All zones in the group.
|
||||
private:
|
||||
ActiveThreadOrGCTaskData<ZoneVector> zones_;
|
||||
ZoneGroupOrGCTaskData<ZoneVector> zones_;
|
||||
public:
|
||||
ZoneVector& zones() { return zones_.ref(); }
|
||||
|
||||
// Whether a zone in this group is in use by a helper thread.
|
||||
mozilla::Atomic<bool> usedByHelperThread;
|
||||
|
||||
explicit ZoneGroup(JSRuntime* runtime);
|
||||
~ZoneGroup();
|
||||
|
||||
|
|
|
@ -2216,6 +2216,7 @@ class JS_PUBLIC_API(CompartmentCreationOptions)
|
|||
traceGlobal_(nullptr),
|
||||
zoneSpec_(NewZoneInSystemZoneGroup),
|
||||
zonePointer_(nullptr),
|
||||
disableNursery_(false),
|
||||
invisibleToDebugger_(false),
|
||||
mergeable_(false),
|
||||
preserveJitCode_(false),
|
||||
|
@ -2251,6 +2252,14 @@ class JS_PUBLIC_API(CompartmentCreationOptions)
|
|||
CompartmentCreationOptions& setNewZoneInSystemZoneGroup();
|
||||
CompartmentCreationOptions& setNewZoneInExistingZoneGroup(JSObject* obj);
|
||||
|
||||
// If these options are creating a new zone group, prevent the use of a
|
||||
// generational GC nursery by that group.
|
||||
bool disableNursery() const { return disableNursery_; }
|
||||
CompartmentCreationOptions& setDisableNursery(bool flag) {
|
||||
disableNursery_ = flag;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Certain scopes (i.e. XBL compilation scopes) are implementation details
|
||||
// of the embedding, and references to them should never leak out to script.
|
||||
// This flag causes the this compartment to skip firing onNewGlobalObject
|
||||
|
@ -2320,6 +2329,7 @@ class JS_PUBLIC_API(CompartmentCreationOptions)
|
|||
JSTraceOp traceGlobal_;
|
||||
ZoneSpecifier zoneSpec_;
|
||||
void* zonePointer_; // Per zoneSpec_, either a Zone, ZoneGroup, or null.
|
||||
bool disableNursery_;
|
||||
bool invisibleToDebugger_;
|
||||
bool mergeable_;
|
||||
bool preserveJitCode_;
|
||||
|
|
|
@ -1039,8 +1039,8 @@ JSContext::JSContext(JSRuntime* runtime, const JS::ContextOptions& options)
|
|||
for (size_t i = 0; i < mozilla::ArrayLength(nativeStackQuota); i++)
|
||||
nativeStackQuota[i] = 0;
|
||||
|
||||
if (!TlsContext.get())
|
||||
TlsContext.set(this);
|
||||
MOZ_ASSERT(!TlsContext.get());
|
||||
TlsContext.set(this);
|
||||
}
|
||||
|
||||
JSContext::~JSContext()
|
||||
|
@ -1070,8 +1070,8 @@ JSContext::~JSContext()
|
|||
DestroyTraceLogger(traceLogger);
|
||||
#endif
|
||||
|
||||
if (TlsContext.get() == this)
|
||||
TlsContext.set(nullptr);
|
||||
MOZ_ASSERT(TlsContext.get() == this);
|
||||
TlsContext.set(nullptr);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -1144,7 +1144,7 @@ class MOZ_RAII AutoLockForExclusiveAccess
|
|||
|
||||
void init(JSRuntime* rt) {
|
||||
runtime = rt;
|
||||
if (runtime->numExclusiveThreads) {
|
||||
if (runtime->hasHelperThreadZones()) {
|
||||
runtime->exclusiveAccessLock.lock();
|
||||
} else {
|
||||
MOZ_ASSERT(!runtime->activeThreadHasExclusiveAccess);
|
||||
|
@ -1164,7 +1164,7 @@ class MOZ_RAII AutoLockForExclusiveAccess
|
|||
init(rt);
|
||||
}
|
||||
~AutoLockForExclusiveAccess() {
|
||||
if (runtime->numExclusiveThreads) {
|
||||
if (runtime->hasHelperThreadZones()) {
|
||||
runtime->exclusiveAccessLock.unlock();
|
||||
} else {
|
||||
MOZ_ASSERT(runtime->activeThreadHasExclusiveAccess);
|
||||
|
|
|
@ -443,7 +443,7 @@ JSContext::enterCompartment(
|
|||
{
|
||||
enterCompartmentDepth_++;
|
||||
|
||||
if (!c->zone()->isAtomsZone() && !c->zone()->usedByExclusiveThread)
|
||||
if (!c->zone()->isAtomsZone())
|
||||
enterZoneGroup(c->zone()->group());
|
||||
|
||||
c->enter();
|
||||
|
@ -479,7 +479,7 @@ JSContext::leaveCompartment(
|
|||
setCompartment(oldCompartment, maybeLock);
|
||||
if (startingCompartment) {
|
||||
startingCompartment->leave();
|
||||
if (!startingCompartment->zone()->isAtomsZone() && !startingCompartment->zone()->usedByExclusiveThread)
|
||||
if (!startingCompartment->zone()->isAtomsZone())
|
||||
leaveZoneGroup(startingCompartment->zone()->group());
|
||||
}
|
||||
}
|
||||
|
@ -488,14 +488,6 @@ inline void
|
|||
JSContext::setCompartment(JSCompartment* comp,
|
||||
const js::AutoLockForExclusiveAccess* maybeLock /* = nullptr */)
|
||||
{
|
||||
// Contexts operating on helper threads can only be in the atoms zone or in exclusive zones.
|
||||
MOZ_ASSERT_IF(helperThread() && !runtime_->isAtomsCompartment(comp),
|
||||
comp->zone()->usedByExclusiveThread);
|
||||
|
||||
// Normal JSContexts cannot enter exclusive zones.
|
||||
MOZ_ASSERT_IF(this == runtime()->activeContext() && comp,
|
||||
!comp->zone()->usedByExclusiveThread);
|
||||
|
||||
// Only one thread can be in the atoms compartment at a time.
|
||||
MOZ_ASSERT_IF(runtime_->isAtomsCompartment(comp), maybeLock != nullptr);
|
||||
MOZ_ASSERT_IF(runtime_->isAtomsCompartment(comp) || runtime_->isAtomsCompartment(compartment_),
|
||||
|
@ -510,9 +502,8 @@ JSContext::setCompartment(JSCompartment* comp,
|
|||
MOZ_ASSERT_IF(compartment_, compartment_->hasBeenEntered());
|
||||
MOZ_ASSERT_IF(comp, comp->hasBeenEntered());
|
||||
|
||||
// This context must have exclusive access to the zone's group. There is an
|
||||
// exception, for now, for zones used by exclusive threads.
|
||||
MOZ_ASSERT_IF(comp && !comp->zone()->isAtomsZone() && !comp->zone()->usedByExclusiveThread,
|
||||
// This context must have exclusive access to the zone's group.
|
||||
MOZ_ASSERT_IF(comp && !comp->zone()->isAtomsZone(),
|
||||
comp->zone()->group()->ownedByCurrentThread());
|
||||
|
||||
compartment_ = comp;
|
||||
|
|
|
@ -3031,9 +3031,9 @@ GCRuntime::maybeAllocTriggerZoneGC(Zone* zone, const AutoLockGC& lock)
|
|||
bool
|
||||
GCRuntime::triggerZoneGC(Zone* zone, JS::gcreason::Reason reason)
|
||||
{
|
||||
/* Zones in use by a thread with an exclusive context can't be collected. */
|
||||
/* Zones in use by a helper thread can't be collected. */
|
||||
if (!CurrentThreadCanAccessRuntime(rt)) {
|
||||
MOZ_ASSERT(zone->usedByExclusiveThread || zone->isAtomsZone());
|
||||
MOZ_ASSERT(zone->usedByHelperThread() || zone->isAtomsZone());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -3050,7 +3050,7 @@ GCRuntime::triggerZoneGC(Zone* zone, JS::gcreason::Reason reason)
|
|||
|
||||
if (zone->isAtomsZone()) {
|
||||
/* We can't do a zone GC of the atoms compartment. */
|
||||
if (TlsContext.get()->keepAtoms || rt->exclusiveThreadsPresent()) {
|
||||
if (TlsContext.get()->keepAtoms || rt->hasHelperThreadZones()) {
|
||||
/* Skip GC and retrigger later, since atoms zone won't be collected
|
||||
* if keepAtoms is true. */
|
||||
fullGCForAtomsRequested_ = true;
|
||||
|
@ -3492,14 +3492,6 @@ Zone::sweepCompartments(FreeOp* fop, bool keepAtleastOne, bool destroyingRuntime
|
|||
void
|
||||
GCRuntime::sweepZones(FreeOp* fop, ZoneGroup* group, bool destroyingRuntime)
|
||||
{
|
||||
MOZ_ASSERT_IF(destroyingRuntime, numActiveZoneIters == 0);
|
||||
MOZ_ASSERT_IF(destroyingRuntime, arenasEmptyAtShutdown);
|
||||
|
||||
if (rt->gc.numActiveZoneIters)
|
||||
return;
|
||||
|
||||
assertBackgroundSweepingFinished();
|
||||
|
||||
JSZoneCallback callback = rt->destroyZoneCallback;
|
||||
|
||||
Zone** read = group->zones().begin();
|
||||
|
@ -3547,6 +3539,14 @@ GCRuntime::sweepZones(FreeOp* fop, ZoneGroup* group, bool destroyingRuntime)
|
|||
void
|
||||
GCRuntime::sweepZoneGroups(FreeOp* fop, bool destroyingRuntime)
|
||||
{
|
||||
MOZ_ASSERT_IF(destroyingRuntime, numActiveZoneIters == 0);
|
||||
MOZ_ASSERT_IF(destroyingRuntime, arenasEmptyAtShutdown);
|
||||
|
||||
if (rt->gc.numActiveZoneIters)
|
||||
return;
|
||||
|
||||
assertBackgroundSweepingFinished();
|
||||
|
||||
ZoneGroup** read = groups.ref().begin();
|
||||
ZoneGroup** end = groups.ref().end();
|
||||
ZoneGroup** write = read;
|
||||
|
@ -3853,7 +3853,7 @@ GCRuntime::beginMarkPhase(JS::gcreason::Reason reason, AutoLockForExclusiveAcces
|
|||
* the other collected zones are using are marked, and we can update the
|
||||
* set of atoms in use by the other collected zones at the end of the GC.
|
||||
*/
|
||||
if (!TlsContext.get()->keepAtoms || rt->exclusiveThreadsPresent()) {
|
||||
if (!TlsContext.get()->keepAtoms || rt->hasHelperThreadZones()) {
|
||||
Zone* atomsZone = rt->atomsCompartment(lock)->zone();
|
||||
if (atomsZone->isGCScheduled()) {
|
||||
MOZ_ASSERT(!atomsZone->isCollecting());
|
||||
|
@ -6290,7 +6290,7 @@ GCRuntime::gcCycle(bool nonincrementalByAPI, SliceBudget& budget, JS::gcreason::
|
|||
|
||||
// We don't allow off-thread parsing to start while we're doing an
|
||||
// incremental GC.
|
||||
MOZ_ASSERT_IF(rt->activeGCInAtomsZone(), !rt->exclusiveThreadsPresent());
|
||||
MOZ_ASSERT_IF(rt->activeGCInAtomsZone(), !rt->hasHelperThreadZones());
|
||||
|
||||
auto result = budgetIncrementalGC(nonincrementalByAPI, reason, budget, session.lock);
|
||||
|
||||
|
@ -6683,7 +6683,7 @@ ZoneGroup::minorGC(JS::gcreason::Reason reason, gcstats::Phase phase)
|
|||
|
||||
{
|
||||
AutoLockGC lock(runtime);
|
||||
for (ZonesIter zone(runtime, WithAtoms); !zone.done(); zone.next())
|
||||
for (ZonesInGroupIter zone(this); !zone.done(); zone.next())
|
||||
runtime->gc.maybeAllocTriggerZoneGC(zone, lock);
|
||||
}
|
||||
}
|
||||
|
@ -6790,7 +6790,10 @@ js::NewCompartment(JSContext* cx, JSPrincipals* principals,
|
|||
break;
|
||||
}
|
||||
|
||||
if (!group) {
|
||||
if (group) {
|
||||
// Take over ownership of the group while we create the compartment/zone.
|
||||
group->enter();
|
||||
} else {
|
||||
MOZ_ASSERT(!zone);
|
||||
group = cx->new_<ZoneGroup>(rt);
|
||||
if (!group)
|
||||
|
@ -6798,7 +6801,11 @@ js::NewCompartment(JSContext* cx, JSPrincipals* principals,
|
|||
|
||||
groupHolder.reset(group);
|
||||
|
||||
if (!group->init(rt->gc.tunables.gcMaxNurseryBytes())) {
|
||||
size_t nurseryBytes =
|
||||
options.creationOptions().disableNursery()
|
||||
? 0
|
||||
: rt->gc.tunables.gcMaxNurseryBytes();
|
||||
if (!group->init(nurseryBytes)) {
|
||||
ReportOutOfMemory(cx);
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -6865,6 +6872,7 @@ js::NewCompartment(JSContext* cx, JSPrincipals* principals,
|
|||
|
||||
zoneHolder.forget();
|
||||
groupHolder.forget();
|
||||
group->leave();
|
||||
return compartment.forget();
|
||||
}
|
||||
|
||||
|
@ -7823,16 +7831,23 @@ js::gc::detail::CellIsMarkedGrayIfKnown(const Cell* cell)
|
|||
if (!cell->isTenured())
|
||||
return false;
|
||||
|
||||
// We ignore the gray marking state of cells and return false in two cases:
|
||||
// We ignore the gray marking state of cells and return false in the
|
||||
// following cases:
|
||||
//
|
||||
// 1) When OOM has caused us to clear the gcGrayBitsValid_ flag.
|
||||
//
|
||||
// 2) When we are in an incremental GC and examine a cell that is in a zone
|
||||
// that is not being collected. Gray targets of CCWs that are marked black
|
||||
// by a barrier will eventually be marked black in the next GC slice.
|
||||
//
|
||||
// 3) When we are not on the runtime's active thread. Helper threads might
|
||||
// call this while parsing, and they are not allowed to inspect the
|
||||
// runtime's incremental state. The objects being operated on are not able
|
||||
// to be collected and will not be marked any color.
|
||||
auto tc = &cell->asTenured();
|
||||
auto rt = tc->runtimeFromActiveCooperatingThread();
|
||||
if (!rt->gc.areGrayBitsValid() ||
|
||||
auto rt = tc->runtimeFromAnyThread();
|
||||
if (!CurrentThreadCanAccessRuntime(rt) ||
|
||||
!rt->gc.areGrayBitsValid() ||
|
||||
(rt->gc.isIncrementalGCInProgress() && !tc->zone()->wasGCStarted()))
|
||||
{
|
||||
return false;
|
||||
|
|
|
@ -47,7 +47,7 @@ CheckActiveThread<Helper>::check() const
|
|||
return;
|
||||
|
||||
JSContext* cx = TlsContext.get();
|
||||
MOZ_ASSERT(cx == cx->runtime()->activeContext());
|
||||
MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
|
||||
#endif // XP_WIN
|
||||
}
|
||||
|
||||
|
@ -63,10 +63,22 @@ CheckZoneGroup<Helper>::check() const
|
|||
return;
|
||||
|
||||
if (group) {
|
||||
// This check is disabled for now because helper thread parse tasks
|
||||
// access data in the same zone group that the single active thread is
|
||||
// using. This will be fixed soon (bug 1323066).
|
||||
//MOZ_ASSERT(group->context && group->context == TlsContext.get());
|
||||
if (group->usedByHelperThread) {
|
||||
MOZ_ASSERT(group->ownedByCurrentThread());
|
||||
} else {
|
||||
// This check is disabled on windows for the same reason as in
|
||||
// CheckActiveThread.
|
||||
#ifndef XP_WIN
|
||||
// In a cooperatively scheduled runtime the active thread is
|
||||
// permitted access to all zone groups --- even those it has not
|
||||
// entered --- for GC and similar purposes. Since all other
|
||||
// cooperative threads are suspended, these accesses are threadsafe
|
||||
// if the zone group is not in use by a helper thread which is not
|
||||
// cooperatively scheduled.
|
||||
JSContext* cx = TlsContext.get();
|
||||
MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
// |group| will be null for data in the atoms zone. This is protected
|
||||
// by the exclusive access lock.
|
||||
|
|
|
@ -23,11 +23,6 @@ namespace js {
|
|||
// associated with the data, consider using the ExclusiveData class instead.
|
||||
// Otherwise, ProtectedData should be used to document whatever synchronization
|
||||
// method is used.
|
||||
//
|
||||
// Note that several of the checks below are currently disabled
|
||||
// (e.g. ThreadLocalData, ZoneGroupData). These will be fixed soon (bug 1323066),
|
||||
// but for now this class is largely documenting how we would like data to be
|
||||
// protected, rather than how it actually is protected.
|
||||
|
||||
#define DECLARE_ONE_BOOL_OPERATOR(OP, T) \
|
||||
template <typename U> \
|
||||
|
@ -179,10 +174,7 @@ class CheckThreadLocal
|
|||
{}
|
||||
|
||||
inline void check() const {
|
||||
// This check is currently disabled because JSContexts used for off
|
||||
// thread parsing are created on different threads than where they run.
|
||||
// This will be fixed soon (bug 1323066).
|
||||
//MOZ_ASSERT(id == ThisThread::GetId());
|
||||
MOZ_ASSERT(id == ThisThread::GetId());
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
@ -239,7 +231,8 @@ class CheckZoneGroup
|
|||
};
|
||||
|
||||
// Data which may only be accessed by threads with exclusive access to the
|
||||
// associated zone group.
|
||||
// associated zone group, or by the runtime's cooperatively scheduled
|
||||
// active thread for zone groups which are not in use by a helper thread.
|
||||
template <typename T>
|
||||
using ZoneGroupData =
|
||||
ProtectedDataZoneGroupArg<CheckZoneGroup<AllowedHelperThread::None>, T>;
|
||||
|
|
|
@ -1484,8 +1484,10 @@ ArrayBufferViewObject::trace(JSTracer* trc, JSObject* objArg)
|
|||
// We can't use a direct forwarding pointer here, as there might
|
||||
// not be enough bytes available, and other views might have data
|
||||
// pointers whose forwarding pointers would overlap this one.
|
||||
Nursery& nursery = obj->zoneFromAnyThread()->group()->nursery();
|
||||
nursery.maybeSetForwardingPointer(trc, srcData, dstData, /* direct = */ false);
|
||||
if (trc->isTenuringTracer()) {
|
||||
Nursery& nursery = obj->zoneFromAnyThread()->group()->nursery();
|
||||
nursery.maybeSetForwardingPointer(trc, srcData, dstData, /* direct = */ false);
|
||||
}
|
||||
} else {
|
||||
MOZ_ASSERT_IF(buf.dataPointer() == nullptr, offset == 0);
|
||||
|
||||
|
|
|
@ -290,27 +290,27 @@ static const JSClass parseTaskGlobalClass = {
|
|||
&parseTaskGlobalClassOps
|
||||
};
|
||||
|
||||
ParseTask::ParseTask(ParseTaskKind kind, JSContext* cx, JSObject* exclusiveContextGlobal,
|
||||
JSContext* initCx, const char16_t* chars, size_t length,
|
||||
ParseTask::ParseTask(ParseTaskKind kind, JSContext* cx, JSObject* parseGlobal,
|
||||
const char16_t* chars, size_t length,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData)
|
||||
: kind(kind), cx(cx), options(initCx), chars(chars), length(length),
|
||||
: kind(kind), options(cx), chars(chars), length(length),
|
||||
alloc(JSContext::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
|
||||
exclusiveContextGlobal(exclusiveContextGlobal),
|
||||
parseGlobal(parseGlobal),
|
||||
callback(callback), callbackData(callbackData),
|
||||
script(nullptr), sourceObject(nullptr),
|
||||
errors(cx), overRecursed(false), outOfMemory(false)
|
||||
overRecursed(false), outOfMemory(false)
|
||||
{
|
||||
}
|
||||
|
||||
ParseTask::ParseTask(ParseTaskKind kind, JSContext* cx, JSObject* exclusiveContextGlobal,
|
||||
JSContext* initCx, JS::TranscodeBuffer& buffer, size_t cursor,
|
||||
ParseTask::ParseTask(ParseTaskKind kind, JSContext* cx, JSObject* parseGlobal,
|
||||
JS::TranscodeBuffer& buffer, size_t cursor,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData)
|
||||
: kind(kind), cx(cx), options(initCx), buffer(&buffer), cursor(cursor),
|
||||
: kind(kind), options(cx), buffer(&buffer), cursor(cursor),
|
||||
alloc(JSContext::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
|
||||
exclusiveContextGlobal(exclusiveContextGlobal),
|
||||
parseGlobal(parseGlobal),
|
||||
callback(callback), callbackData(callbackData),
|
||||
script(nullptr), sourceObject(nullptr),
|
||||
errors(cx), overRecursed(false), outOfMemory(false)
|
||||
overRecursed(false), outOfMemory(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -326,8 +326,7 @@ ParseTask::init(JSContext* cx, const ReadOnlyCompileOptions& options)
|
|||
void
|
||||
ParseTask::activate(JSRuntime* rt)
|
||||
{
|
||||
rt->setUsedByExclusiveThread(exclusiveContextGlobal->zone());
|
||||
cx->enterCompartmentOf(exclusiveContextGlobal);
|
||||
rt->setUsedByHelperThread(parseGlobal->zone());
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -344,9 +343,6 @@ ParseTask::finish(JSContext* cx)
|
|||
|
||||
ParseTask::~ParseTask()
|
||||
{
|
||||
// ParseTask takes over ownership of its input exclusive context.
|
||||
js_delete(cx);
|
||||
|
||||
for (size_t i = 0; i < errors.length(); i++)
|
||||
js_delete(errors[i]);
|
||||
}
|
||||
|
@ -354,26 +350,26 @@ ParseTask::~ParseTask()
|
|||
void
|
||||
ParseTask::trace(JSTracer* trc)
|
||||
{
|
||||
if (!cx->runtimeMatches(trc->runtime()))
|
||||
if (parseGlobal->runtimeFromAnyThread() != trc->runtime())
|
||||
return;
|
||||
|
||||
TraceManuallyBarrieredEdge(trc, &exclusiveContextGlobal, "ParseTask::exclusiveContextGlobal");
|
||||
TraceManuallyBarrieredEdge(trc, &parseGlobal, "ParseTask::parseGlobal");
|
||||
if (script)
|
||||
TraceManuallyBarrieredEdge(trc, &script, "ParseTask::script");
|
||||
if (sourceObject)
|
||||
TraceManuallyBarrieredEdge(trc, &sourceObject, "ParseTask::sourceObject");
|
||||
}
|
||||
|
||||
ScriptParseTask::ScriptParseTask(JSContext* cx, JSObject* exclusiveContextGlobal,
|
||||
JSContext* initCx, const char16_t* chars, size_t length,
|
||||
ScriptParseTask::ScriptParseTask(JSContext* cx, JSObject* parseGlobal,
|
||||
const char16_t* chars, size_t length,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData)
|
||||
: ParseTask(ParseTaskKind::Script, cx, exclusiveContextGlobal, initCx, chars, length, callback,
|
||||
: ParseTask(ParseTaskKind::Script, cx, parseGlobal, chars, length, callback,
|
||||
callbackData)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
ScriptParseTask::parse()
|
||||
ScriptParseTask::parse(JSContext* cx)
|
||||
{
|
||||
SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::NoOwnership);
|
||||
script = frontend::CompileGlobalScript(cx, alloc, ScopeKind::Global,
|
||||
|
@ -382,16 +378,16 @@ ScriptParseTask::parse()
|
|||
/* sourceObjectOut = */ &sourceObject);
|
||||
}
|
||||
|
||||
ModuleParseTask::ModuleParseTask(JSContext* cx, JSObject* exclusiveContextGlobal,
|
||||
JSContext* initCx, const char16_t* chars, size_t length,
|
||||
ModuleParseTask::ModuleParseTask(JSContext* cx, JSObject* parseGlobal,
|
||||
const char16_t* chars, size_t length,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData)
|
||||
: ParseTask(ParseTaskKind::Module, cx, exclusiveContextGlobal, initCx, chars, length, callback,
|
||||
: ParseTask(ParseTaskKind::Module, cx, parseGlobal, chars, length, callback,
|
||||
callbackData)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
ModuleParseTask::parse()
|
||||
ModuleParseTask::parse(JSContext* cx)
|
||||
{
|
||||
SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::NoOwnership);
|
||||
ModuleObject* module = frontend::CompileModule(cx, options, srcBuf, alloc, &sourceObject);
|
||||
|
@ -399,16 +395,16 @@ ModuleParseTask::parse()
|
|||
script = module->script();
|
||||
}
|
||||
|
||||
ScriptDecodeTask::ScriptDecodeTask(JSContext* cx, JSObject* exclusiveContextGlobal,
|
||||
JSContext* initCx, JS::TranscodeBuffer& buffer, size_t cursor,
|
||||
ScriptDecodeTask::ScriptDecodeTask(JSContext* cx, JSObject* parseGlobal,
|
||||
JS::TranscodeBuffer& buffer, size_t cursor,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData)
|
||||
: ParseTask(ParseTaskKind::ScriptDecode, cx, exclusiveContextGlobal, initCx,
|
||||
: ParseTask(ParseTaskKind::ScriptDecode, cx, parseGlobal,
|
||||
buffer, cursor, callback, callbackData)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
ScriptDecodeTask::parse()
|
||||
ScriptDecodeTask::parse(JSContext* cx)
|
||||
{
|
||||
RootedScript resultScript(cx);
|
||||
XDROffThreadDecoder decoder(cx, alloc, &options, /* sourceObjectOut = */ &sourceObject,
|
||||
|
@ -541,7 +537,8 @@ CreateGlobalForOffThreadParse(JSContext* cx, ParseTaskKind kind, const gc::AutoS
|
|||
|
||||
creationOptions.setInvisibleToDebugger(true)
|
||||
.setMergeable(true)
|
||||
.setNewZoneInSystemZoneGroup();
|
||||
.setNewZoneInNewZoneGroup()
|
||||
.setDisableNursery(true);
|
||||
|
||||
// Don't falsely inherit the host's global trace hook.
|
||||
creationOptions.setTrace(nullptr);
|
||||
|
@ -605,17 +602,10 @@ StartOffThreadParseTask(JSContext* cx, const ReadOnlyCompileOptions& options,
|
|||
if (!global)
|
||||
return false;
|
||||
|
||||
ScopedJSDeletePtr<JSContext> helpercx(
|
||||
cx->new_<JSContext>(cx->runtime(), cx->options()));
|
||||
if (!helpercx)
|
||||
return false;
|
||||
|
||||
ScopedJSDeletePtr<ParseTask> task(taskFunctor(helpercx.get(), global));
|
||||
ScopedJSDeletePtr<ParseTask> task(taskFunctor(global));
|
||||
if (!task)
|
||||
return false;
|
||||
|
||||
helpercx.forget();
|
||||
|
||||
if (!task->init(cx, options) || !QueueOffThreadParseTask(cx, task))
|
||||
return false;
|
||||
|
||||
|
@ -630,8 +620,8 @@ js::StartOffThreadParseScript(JSContext* cx, const ReadOnlyCompileOptions& optio
|
|||
const char16_t* chars, size_t length,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData)
|
||||
{
|
||||
auto functor = [&](JSContext* helpercx, JSObject* global) -> ScriptParseTask* {
|
||||
return cx->new_<ScriptParseTask>(helpercx, global, cx, chars, length,
|
||||
auto functor = [&](JSObject* global) -> ScriptParseTask* {
|
||||
return cx->new_<ScriptParseTask>(cx, global, chars, length,
|
||||
callback, callbackData);
|
||||
};
|
||||
return StartOffThreadParseTask(cx, options, ParseTaskKind::Script, functor);
|
||||
|
@ -642,8 +632,8 @@ js::StartOffThreadParseModule(JSContext* cx, const ReadOnlyCompileOptions& optio
|
|||
const char16_t* chars, size_t length,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData)
|
||||
{
|
||||
auto functor = [&](JSContext* helpercx, JSObject* global) -> ModuleParseTask* {
|
||||
return cx->new_<ModuleParseTask>(helpercx, global, cx, chars, length,
|
||||
auto functor = [&](JSObject* global) -> ModuleParseTask* {
|
||||
return cx->new_<ModuleParseTask>(cx, global, chars, length,
|
||||
callback, callbackData);
|
||||
};
|
||||
return StartOffThreadParseTask(cx, options, ParseTaskKind::Module, functor);
|
||||
|
@ -654,8 +644,8 @@ js::StartOffThreadDecodeScript(JSContext* cx, const ReadOnlyCompileOptions& opti
|
|||
JS::TranscodeBuffer& buffer, size_t cursor,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData)
|
||||
{
|
||||
auto functor = [&](JSContext* helpercx, JSObject* global) -> ScriptDecodeTask* {
|
||||
return cx->new_<ScriptDecodeTask>(helpercx, global, cx, buffer, cursor,
|
||||
auto functor = [&](JSObject* global) -> ScriptDecodeTask* {
|
||||
return cx->new_<ScriptDecodeTask>(cx, global, buffer, cursor,
|
||||
callback, callbackData);
|
||||
};
|
||||
return StartOffThreadParseTask(cx, options, ParseTaskKind::ScriptDecode, functor);
|
||||
|
@ -674,7 +664,7 @@ js::EnqueuePendingParseTasksAfterGC(JSRuntime* rt)
|
|||
|
||||
for (size_t i = 0; i < waiting.length(); i++) {
|
||||
ParseTask* task = waiting[i];
|
||||
if (task->runtimeMatches(rt) && !task->exclusiveContextGlobal->zone()->wasGCStarted()) {
|
||||
if (task->runtimeMatches(rt) && !task->parseGlobal->zone()->wasGCStarted()) {
|
||||
AutoEnterOOMUnsafeRegion oomUnsafe;
|
||||
if (!newTasks.append(task))
|
||||
oomUnsafe.crash("EnqueuePendingParseTasksAfterGC");
|
||||
|
@ -1241,8 +1231,7 @@ LeaveParseTaskZone(JSRuntime* rt, ParseTask* task)
|
|||
{
|
||||
// Mark the zone as no longer in use by an JSContext, and available
|
||||
// to be collected by the GC.
|
||||
task->cx->leaveCompartment(task->cx->compartment());
|
||||
rt->clearUsedByExclusiveThread(task->cx->zone());
|
||||
rt->clearUsedByHelperThread(task->parseGlobal->zone());
|
||||
}
|
||||
|
||||
ParseTask*
|
||||
|
@ -1378,12 +1367,13 @@ GlobalHelperThreadState::mergeParseTaskCompartment(JSContext* cx, ParseTask* par
|
|||
JS::AutoAssertNoGC nogc(cx);
|
||||
|
||||
LeaveParseTaskZone(cx->runtime(), parseTask);
|
||||
AutoCompartment ac(cx, parseTask->parseGlobal);
|
||||
|
||||
{
|
||||
// Generator functions don't have Function.prototype as prototype but a
|
||||
// different function object, so the IdentifyStandardPrototype trick
|
||||
// below won't work. Just special-case it.
|
||||
GlobalObject* parseGlobal = &parseTask->exclusiveContextGlobal->as<GlobalObject>();
|
||||
GlobalObject* parseGlobal = &parseTask->parseGlobal->as<GlobalObject>();
|
||||
JSObject* parseTaskStarGenFunctionProto = parseGlobal->getStarGeneratorFunctionPrototype();
|
||||
|
||||
// Module objects don't have standard prototypes either.
|
||||
|
@ -1395,7 +1385,8 @@ GlobalHelperThreadState::mergeParseTaskCompartment(JSContext* cx, ParseTask* par
|
|||
// to the corresponding prototype in the new compartment. This will briefly
|
||||
// create cross compartment pointers, which will be fixed by the
|
||||
// MergeCompartments call below.
|
||||
for (auto group = parseTask->cx->zone()->cellIter<ObjectGroup>(); !group.done(); group.next()) {
|
||||
Zone* parseZone = parseTask->parseGlobal->zone();
|
||||
for (auto group = parseZone->cellIter<ObjectGroup>(); !group.done(); group.next()) {
|
||||
TaggedProto proto(group->proto());
|
||||
if (!proto.isObject())
|
||||
continue;
|
||||
|
@ -1426,7 +1417,7 @@ GlobalHelperThreadState::mergeParseTaskCompartment(JSContext* cx, ParseTask* par
|
|||
}
|
||||
|
||||
// Move the parsed script and all its contents into the desired compartment.
|
||||
gc::MergeCompartments(parseTask->cx->compartment(), dest);
|
||||
gc::MergeCompartments(parseTask->parseGlobal->compartment(), dest);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1636,20 +1627,16 @@ js::PauseCurrentHelperThread()
|
|||
HelperThreadState().wait(lock, GlobalHelperThreadState::PAUSE);
|
||||
}
|
||||
|
||||
void
|
||||
JSContext::setHelperThread(HelperThread* thread)
|
||||
{
|
||||
helperThread_ = thread;
|
||||
}
|
||||
|
||||
bool
|
||||
JSContext::addPendingCompileError(frontend::CompileError** error)
|
||||
{
|
||||
UniquePtr<frontend::CompileError> errorPtr(new_<frontend::CompileError>());
|
||||
if (!errorPtr)
|
||||
return false;
|
||||
if (!helperThread()->parseTask()->errors.append(errorPtr.get()))
|
||||
if (!helperThread()->parseTask()->errors.append(errorPtr.get())) {
|
||||
ReportOutOfMemory(this);
|
||||
return false;
|
||||
}
|
||||
*error = errorPtr.release();
|
||||
return true;
|
||||
}
|
||||
|
@ -1677,18 +1664,16 @@ HelperThread::handleParseWorkload(AutoLockHelperThreadState& locked, uintptr_t s
|
|||
|
||||
currentTask.emplace(HelperThreadState().parseWorklist(locked).popCopy());
|
||||
ParseTask* task = parseTask();
|
||||
task->cx->setHelperThread(this);
|
||||
|
||||
for (size_t i = 0; i < ArrayLength(task->cx->nativeStackLimit); i++)
|
||||
task->cx->nativeStackLimit[i] = stackLimit;
|
||||
|
||||
JSContext* oldcx = TlsContext.get();
|
||||
TlsContext.set(task->cx);
|
||||
{
|
||||
AutoUnlockHelperThreadState unlock(locked);
|
||||
task->parse();
|
||||
AutoSetContextRuntime ascr(task->parseGlobal->runtimeFromAnyThread());
|
||||
|
||||
JSContext* cx = TlsContext.get();
|
||||
AutoCompartment ac(cx, task->parseGlobal);
|
||||
|
||||
task->parse(cx);
|
||||
}
|
||||
TlsContext.set(oldcx);
|
||||
|
||||
// The callback is invoked while we are still off thread.
|
||||
task->callback(task, task->callbackData);
|
||||
|
@ -1891,6 +1876,12 @@ HelperThread::handleGCHelperWorkload(AutoLockHelperThreadState& locked)
|
|||
HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, locked);
|
||||
}
|
||||
|
||||
void
|
||||
JSContext::setHelperThread(HelperThread* thread)
|
||||
{
|
||||
helperThread_ = thread;
|
||||
}
|
||||
|
||||
void
|
||||
HelperThread::threadLoop()
|
||||
{
|
||||
|
@ -1900,6 +1891,7 @@ HelperThread::threadLoop()
|
|||
AutoLockHelperThreadState lock;
|
||||
|
||||
JSContext cx(nullptr, JS::ContextOptions());
|
||||
cx.setHelperThread(this);
|
||||
|
||||
// Compute the thread's stack limit, for over-recursed checks.
|
||||
uintptr_t stackLimit = GetNativeStackBase();
|
||||
|
|
|
@ -568,7 +568,6 @@ class MOZ_RAII AutoUnlockHelperThreadState : public UnlockGuard<Mutex>
|
|||
struct ParseTask
|
||||
{
|
||||
ParseTaskKind kind;
|
||||
JSContext* cx;
|
||||
OwningCompileOptions options;
|
||||
// Anonymous union, the only correct interpretation is provided by the
|
||||
// ParseTaskKind value, or from the virtual parse function.
|
||||
|
@ -587,8 +586,8 @@ struct ParseTask
|
|||
};
|
||||
LifoAlloc alloc;
|
||||
|
||||
// Rooted pointer to the global object used by 'cx'.
|
||||
JSObject* exclusiveContextGlobal;
|
||||
// Rooted pointer to the global object to use while parsing.
|
||||
JSObject* parseGlobal;
|
||||
|
||||
// Callback invoked off thread when the parse finishes.
|
||||
JS::OffThreadCompileCallback callback;
|
||||
|
@ -604,24 +603,24 @@ struct ParseTask
|
|||
|
||||
// Any errors or warnings produced during compilation. These are reported
|
||||
// when finishing the script.
|
||||
Vector<frontend::CompileError*> errors;
|
||||
Vector<frontend::CompileError*, 0, SystemAllocPolicy> errors;
|
||||
bool overRecursed;
|
||||
bool outOfMemory;
|
||||
|
||||
ParseTask(ParseTaskKind kind, JSContext* cx, JSObject* exclusiveContextGlobal,
|
||||
JSContext* initCx, const char16_t* chars, size_t length,
|
||||
ParseTask(ParseTaskKind kind, JSContext* cx, JSObject* parseGlobal,
|
||||
const char16_t* chars, size_t length,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData);
|
||||
ParseTask(ParseTaskKind kind, JSContext* cx, JSObject* exclusiveContextGlobal,
|
||||
JSContext* initCx, JS::TranscodeBuffer& buffer, size_t cursor,
|
||||
ParseTask(ParseTaskKind kind, JSContext* cx, JSObject* parseGlobal,
|
||||
JS::TranscodeBuffer& buffer, size_t cursor,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData);
|
||||
bool init(JSContext* cx, const ReadOnlyCompileOptions& options);
|
||||
|
||||
void activate(JSRuntime* rt);
|
||||
virtual void parse() = 0;
|
||||
virtual void parse(JSContext* cx) = 0;
|
||||
bool finish(JSContext* cx);
|
||||
|
||||
bool runtimeMatches(JSRuntime* rt) {
|
||||
return cx->runtimeMatches(rt);
|
||||
return parseGlobal->runtimeFromAnyThread() == rt;
|
||||
}
|
||||
|
||||
virtual ~ParseTask();
|
||||
|
@ -631,26 +630,26 @@ struct ParseTask
|
|||
|
||||
struct ScriptParseTask : public ParseTask
|
||||
{
|
||||
ScriptParseTask(JSContext* cx, JSObject* exclusiveContextGlobal,
|
||||
JSContext* initCx, const char16_t* chars, size_t length,
|
||||
ScriptParseTask(JSContext* cx, JSObject* parseGlobal,
|
||||
const char16_t* chars, size_t length,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData);
|
||||
void parse() override;
|
||||
void parse(JSContext* cx) override;
|
||||
};
|
||||
|
||||
struct ModuleParseTask : public ParseTask
|
||||
{
|
||||
ModuleParseTask(JSContext* cx, JSObject* exclusiveContextGlobal,
|
||||
JSContext* initCx, const char16_t* chars, size_t length,
|
||||
ModuleParseTask(JSContext* cx, JSObject* parseGlobal,
|
||||
const char16_t* chars, size_t length,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData);
|
||||
void parse() override;
|
||||
void parse(JSContext* cx) override;
|
||||
};
|
||||
|
||||
struct ScriptDecodeTask : public ParseTask
|
||||
{
|
||||
ScriptDecodeTask(JSContext* cx, JSObject* exclusiveContextGlobal,
|
||||
JSContext* initCx, JS::TranscodeBuffer& buffer, size_t cursor,
|
||||
ScriptDecodeTask(JSContext* cx, JSObject* parseGlobal,
|
||||
JS::TranscodeBuffer& buffer, size_t cursor,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData);
|
||||
void parse() override;
|
||||
void parse(JSContext* cx) override;
|
||||
};
|
||||
|
||||
// Return whether, if a new parse task was started, it would need to wait for
|
||||
|
|
|
@ -483,10 +483,7 @@ NativeObject::growSlotsDontReportOOM(JSContext* cx, NativeObject* obj, uint32_t
|
|||
static void
|
||||
FreeSlots(JSContext* cx, HeapSlot* slots)
|
||||
{
|
||||
// Note: off thread parse tasks do not have access to GGC nursery allocated things.
|
||||
if (!cx->helperThread())
|
||||
return cx->nursery().freeBuffer(slots);
|
||||
js_free(slots);
|
||||
return cx->nursery().freeBuffer(slots);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -133,7 +133,7 @@ JSRuntime::JSRuntime(JSRuntime* parentRuntime)
|
|||
#ifdef DEBUG
|
||||
activeThreadHasExclusiveAccess(false),
|
||||
#endif
|
||||
numExclusiveThreads(0),
|
||||
numHelperThreadZones(0),
|
||||
numCompartments(0),
|
||||
localeCallbacks(nullptr),
|
||||
defaultLocale(nullptr),
|
||||
|
@ -306,7 +306,7 @@ JSRuntime::destroyRuntime()
|
|||
MOZ_ASSERT(ionLazyLinkListSize_ == 0);
|
||||
MOZ_ASSERT(ionLazyLinkList().isEmpty());
|
||||
|
||||
MOZ_ASSERT(!numExclusiveThreads);
|
||||
MOZ_ASSERT(!hasHelperThreadZones());
|
||||
AutoLockForExclusiveAccess lock(this);
|
||||
|
||||
/*
|
||||
|
@ -752,20 +752,20 @@ JSRuntime::activeGCInAtomsZone()
|
|||
}
|
||||
|
||||
void
|
||||
JSRuntime::setUsedByExclusiveThread(Zone* zone)
|
||||
JSRuntime::setUsedByHelperThread(Zone* zone)
|
||||
{
|
||||
MOZ_ASSERT(!zone->usedByExclusiveThread);
|
||||
MOZ_ASSERT(!zone->group()->usedByHelperThread);
|
||||
MOZ_ASSERT(!zone->wasGCStarted());
|
||||
zone->usedByExclusiveThread = true;
|
||||
numExclusiveThreads++;
|
||||
zone->group()->usedByHelperThread = true;
|
||||
numHelperThreadZones++;
|
||||
}
|
||||
|
||||
void
|
||||
JSRuntime::clearUsedByExclusiveThread(Zone* zone)
|
||||
JSRuntime::clearUsedByHelperThread(Zone* zone)
|
||||
{
|
||||
MOZ_ASSERT(zone->usedByExclusiveThread);
|
||||
zone->usedByExclusiveThread = false;
|
||||
numExclusiveThreads--;
|
||||
MOZ_ASSERT(zone->group()->usedByHelperThread);
|
||||
zone->group()->usedByHelperThread = false;
|
||||
numHelperThreadZones--;
|
||||
if (gc.fullGCForAtomsRequested() && !TlsContext.get())
|
||||
gc.triggerFullGCForAtoms();
|
||||
}
|
||||
|
@ -782,10 +782,8 @@ js::CurrentThreadCanAccessZone(Zone* zone)
|
|||
if (CurrentThreadCanAccessRuntime(zone->runtime_))
|
||||
return true;
|
||||
|
||||
// Only zones in use by an exclusive thread can be used off thread.
|
||||
// We don't keep track of which thread owns such zones though, so this check
|
||||
// is imperfect.
|
||||
return zone->usedByExclusiveThread;
|
||||
// Only zones marked for use by a helper thread can be used off thread.
|
||||
return zone->usedByHelperThread() && zone->group()->ownedByCurrentThread();
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
|
|
|
@ -571,22 +571,22 @@ struct JSRuntime : public js::MallocProvider<JSRuntime>
|
|||
bool activeThreadHasExclusiveAccess;
|
||||
#endif
|
||||
|
||||
/* Number of non-cooperating threads with exclusive access to some zone. */
|
||||
js::UnprotectedData<size_t> numExclusiveThreads;
|
||||
/* Number of zones which may be operated on by non-cooperating helper threads. */
|
||||
js::UnprotectedData<size_t> numHelperThreadZones;
|
||||
|
||||
friend class js::AutoLockForExclusiveAccess;
|
||||
|
||||
public:
|
||||
void setUsedByExclusiveThread(JS::Zone* zone);
|
||||
void clearUsedByExclusiveThread(JS::Zone* zone);
|
||||
void setUsedByHelperThread(JS::Zone* zone);
|
||||
void clearUsedByHelperThread(JS::Zone* zone);
|
||||
|
||||
bool exclusiveThreadsPresent() const {
|
||||
return numExclusiveThreads > 0;
|
||||
bool hasHelperThreadZones() const {
|
||||
return numHelperThreadZones > 0;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
bool currentThreadHasExclusiveAccess() const {
|
||||
return (!exclusiveThreadsPresent() && activeThreadHasExclusiveAccess) ||
|
||||
return (!hasHelperThreadZones() && activeThreadHasExclusiveAccess) ||
|
||||
exclusiveAccessLock.ownedByCurrentThread();
|
||||
}
|
||||
#endif
|
||||
|
|
Загрузка…
Ссылка в новой задаче