Bug 1366340 - Delete parsing zones immediately after their contents are merged r=sfink

This commit is contained in:
Jon Coppeard 2017-07-24 10:42:50 +01:00
Родитель 4266b7c35e
Коммит 6063f3db71
8 изменённых файлов: 115 добавлений и 21 удалений

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

@ -937,7 +937,8 @@ class GCRuntime
void startTask(GCParallelTask& task, gcstats::PhaseKind phase, AutoLockHelperThreadState& locked); void startTask(GCParallelTask& task, gcstats::PhaseKind phase, AutoLockHelperThreadState& locked);
void joinTask(GCParallelTask& task, gcstats::PhaseKind phase, AutoLockHelperThreadState& locked); void joinTask(GCParallelTask& task, gcstats::PhaseKind phase, AutoLockHelperThreadState& locked);
private: // Delete an empty zone group after its contents have been merged.
void deleteEmptyZoneGroup(ZoneGroup* group);
private: private:
enum IncrementalResult enum IncrementalResult
@ -1096,7 +1097,10 @@ class GCRuntime
UnprotectedData<ZoneGroup*> systemZoneGroup; UnprotectedData<ZoneGroup*> systemZoneGroup;
// List of all zone groups (protected by the GC lock). // List of all zone groups (protected by the GC lock).
ActiveThreadOrGCTaskData<ZoneGroupVector> groups; private:
ActiveThreadOrGCTaskData<ZoneGroupVector> groups_;
public:
ZoneGroupVector& groups() { return groups_.ref(); }
// The unique atoms zone, which has no zone group. // The unique atoms zone, which has no zone group.
WriteOnceData<Zone*> atomsZone; WriteOnceData<Zone*> atomsZone;

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

@ -73,6 +73,8 @@ JS::Zone::Zone(JSRuntime* rt, ZoneGroup* group)
Zone::~Zone() Zone::~Zone()
{ {
MOZ_ASSERT(compartments_.ref().empty());
JSRuntime* rt = runtimeFromAnyThread(); JSRuntime* rt = runtimeFromAnyThread();
if (this == rt->gc.systemZone) if (this == rt->gc.systemZone)
rt->gc.systemZone = nullptr; rt->gc.systemZone = nullptr;
@ -87,7 +89,8 @@ Zone::~Zone()
#endif #endif
} }
bool Zone::init(bool isSystemArg) bool
Zone::init(bool isSystemArg)
{ {
isSystem = isSystemArg; isSystem = isSystemArg;
return uniqueIds().init() && return uniqueIds().init() &&
@ -376,6 +379,21 @@ Zone::addTypeDescrObject(JSContext* cx, HandleObject obj)
return true; return true;
} }
void
Zone::deleteEmptyCompartment(JSCompartment* comp)
{
MOZ_ASSERT(comp->zone() == this);
MOZ_ASSERT(arenas.checkEmptyArenaLists());
for (auto& i : compartments()) {
if (i == comp) {
compartments().erase(&i);
comp->destroy(runtimeFromActiveCooperatingThread()->defaultFreeOp());
return;
}
}
MOZ_CRASH("Compartment not found");
}
ZoneList::ZoneList() ZoneList::ZoneList()
: head(nullptr), tail(nullptr) : head(nullptr), tail(nullptr)
{} {}

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

@ -161,6 +161,7 @@ struct Zone : public JS::shadow::Zone,
explicit Zone(JSRuntime* rt, js::ZoneGroup* group); explicit Zone(JSRuntime* rt, js::ZoneGroup* group);
~Zone(); ~Zone();
MOZ_MUST_USE bool init(bool isSystem); MOZ_MUST_USE bool init(bool isSystem);
void destroy(js::FreeOp *fop);
private: private:
js::ZoneGroup* const group_; js::ZoneGroup* const group_;
@ -622,6 +623,9 @@ struct Zone : public JS::shadow::Zone,
keepShapeTables_ = b; keepShapeTables_ = b;
} }
// Delete an empty compartment after its contents have been merged.
void deleteEmptyCompartment(JSCompartment* comp);
private: private:
js::ZoneGroupData<js::jit::JitZone*> jitZone_; js::ZoneGroupData<js::jit::JitZone*> jitZone_;
@ -654,8 +658,8 @@ class ZoneGroupsIter
public: public:
explicit ZoneGroupsIter(JSRuntime* rt) : iterMarker(&rt->gc) { explicit ZoneGroupsIter(JSRuntime* rt) : iterMarker(&rt->gc) {
it = rt->gc.groups.ref().begin(); it = rt->gc.groups().begin();
end = rt->gc.groups.ref().end(); end = rt->gc.groups().end();
if (!done() && (*it)->usedByHelperThread) if (!done() && (*it)->usedByHelperThread)
next(); next();

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

@ -130,4 +130,20 @@ ZoneGroup::ionLazyLinkListAdd(jit::IonBuilder* builder)
ionLazyLinkListSize_++; ionLazyLinkListSize_++;
} }
void
ZoneGroup::deleteEmptyZone(Zone* zone)
{
MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime));
MOZ_ASSERT(zone->group() == this);
MOZ_ASSERT(zone->compartments().empty());
for (auto& i : zones()) {
if (i == zone) {
zones().erase(&i);
zone->destroy(runtime->defaultFreeOp());
return;
}
}
MOZ_CRASH("Zone not found");
}
} // namespace js } // namespace js

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

@ -76,6 +76,9 @@ class ZoneGroup
// See the useExclusiveLocking field above. // See the useExclusiveLocking field above.
void setUseExclusiveLocking() { useExclusiveLocking = true; } void setUseExclusiveLocking() { useExclusiveLocking = true; }
// Delete an empty zone after its contents have been merged.
void deleteEmptyZone(Zone* zone);
#ifdef DEBUG #ifdef DEBUG
private: private:
// The number of possible bailing places encounters before forcefully bailing // The number of possible bailing places encounters before forcefully bailing

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

@ -863,6 +863,7 @@ struct JSCompartment
~JSCompartment(); ~JSCompartment();
MOZ_MUST_USE bool init(JSContext* maybecx); MOZ_MUST_USE bool init(JSContext* maybecx);
void destroy(js::FreeOp* fop);
MOZ_MUST_USE inline bool wrap(JSContext* cx, JS::MutableHandleValue vp); MOZ_MUST_USE inline bool wrap(JSContext* cx, JS::MutableHandleValue vp);

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

@ -1220,11 +1220,12 @@ GCRuntime::finish()
for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) { for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next())
js_delete(comp.get()); js_delete(comp.get());
zone->compartments().clear();
js_delete(zone.get()); js_delete(zone.get());
} }
} }
groups.ref().clear(); groups().clear();
FreeChunkPool(rt, fullChunks_.ref()); FreeChunkPool(rt, fullChunks_.ref());
FreeChunkPool(rt, availableChunks_.ref()); FreeChunkPool(rt, availableChunks_.ref());
@ -3483,6 +3484,25 @@ JS::Zone::sweepUniqueIds(js::FreeOp* fop)
uniqueIds().sweep(); uniqueIds().sweep();
} }
void
JSCompartment::destroy(FreeOp* fop)
{
JSRuntime* rt = fop->runtime();
if (auto callback = rt->destroyCompartmentCallback)
callback(fop, this);
if (principals())
JS_DropPrincipals(TlsContext.get(), principals());
fop->delete_(this);
rt->gc.stats().sweptCompartment();
}
void
Zone::destroy(FreeOp* fop)
{
fop->delete_(this);
fop->runtime()->gc.stats().sweptZone();
}
/* /*
* It's simpler if we preserve the invariant that every zone has at least one * It's simpler if we preserve the invariant that every zone has at least one
* compartment. If we know we're deleting the entire zone, then * compartment. If we know we're deleting the entire zone, then
@ -3495,8 +3515,9 @@ JS::Zone::sweepUniqueIds(js::FreeOp* fop)
void void
Zone::sweepCompartments(FreeOp* fop, bool keepAtleastOne, bool destroyingRuntime) Zone::sweepCompartments(FreeOp* fop, bool keepAtleastOne, bool destroyingRuntime)
{ {
JSRuntime* rt = runtimeFromActiveCooperatingThread(); MOZ_ASSERT(!compartments().empty());
JSDestroyCompartmentCallback callback = rt->destroyCompartmentCallback;
mozilla::DebugOnly<JSRuntime*> rt = runtimeFromActiveCooperatingThread();
JSCompartment** read = compartments().begin(); JSCompartment** read = compartments().begin();
JSCompartment** end = compartments().end(); JSCompartment** end = compartments().end();
@ -3512,12 +3533,7 @@ Zone::sweepCompartments(FreeOp* fop, bool keepAtleastOne, bool destroyingRuntime
*/ */
bool dontDelete = read == end && !foundOne && keepAtleastOne; bool dontDelete = read == end && !foundOne && keepAtleastOne;
if ((!comp->marked && !dontDelete) || destroyingRuntime) { if ((!comp->marked && !dontDelete) || destroyingRuntime) {
if (callback) comp->destroy(fop);
callback(fop, comp);
if (comp->principals())
JS_DropPrincipals(TlsContext.get(), comp->principals());
js_delete(comp);
rt->gc.stats().sweptCompartment();
} else { } else {
*write++ = comp; *write++ = comp;
foundOne = true; foundOne = true;
@ -3530,6 +3546,8 @@ Zone::sweepCompartments(FreeOp* fop, bool keepAtleastOne, bool destroyingRuntime
void void
GCRuntime::sweepZones(FreeOp* fop, ZoneGroup* group, bool destroyingRuntime) GCRuntime::sweepZones(FreeOp* fop, ZoneGroup* group, bool destroyingRuntime)
{ {
MOZ_ASSERT(!group->zones().empty());
Zone** read = group->zones().begin(); Zone** read = group->zones().begin();
Zone** end = group->zones().end(); Zone** end = group->zones().end();
Zone** write = read; Zone** write = read;
@ -3558,8 +3576,7 @@ GCRuntime::sweepZones(FreeOp* fop, ZoneGroup* group, bool destroyingRuntime)
zone->sweepCompartments(fop, false, destroyingRuntime); zone->sweepCompartments(fop, false, destroyingRuntime);
MOZ_ASSERT(zone->compartments().empty()); MOZ_ASSERT(zone->compartments().empty());
MOZ_ASSERT_IF(arenasEmptyAtShutdown, zone->typeDescrObjects().empty()); MOZ_ASSERT_IF(arenasEmptyAtShutdown, zone->typeDescrObjects().empty());
fop->delete_(zone); zone->destroy(fop);
stats().sweptZone();
continue; continue;
} }
zone->sweepCompartments(fop, true, destroyingRuntime); zone->sweepCompartments(fop, true, destroyingRuntime);
@ -3580,8 +3597,8 @@ GCRuntime::sweepZoneGroups(FreeOp* fop, bool destroyingRuntime)
assertBackgroundSweepingFinished(); assertBackgroundSweepingFinished();
ZoneGroup** read = groups.ref().begin(); ZoneGroup** read = groups().begin();
ZoneGroup** end = groups.ref().end(); ZoneGroup** end = groups().end();
ZoneGroup** write = read; ZoneGroup** write = read;
while (read < end) { while (read < end) {
@ -3595,7 +3612,7 @@ GCRuntime::sweepZoneGroups(FreeOp* fop, bool destroyingRuntime)
*write++ = group; *write++ = group;
} }
} }
groups.ref().shrinkTo(write - groups.ref().begin()); groups().shrinkTo(write - groups().begin());
} }
#ifdef DEBUG #ifdef DEBUG
@ -7367,7 +7384,7 @@ js::NewCompartment(JSContext* cx, JSPrincipals* principals,
} }
if (groupHolder) { if (groupHolder) {
if (!rt->gc.groups.ref().append(group)) { if (!rt->gc.groups().append(group)) {
ReportOutOfMemory(cx); ReportOutOfMemory(cx);
return nullptr; return nullptr;
} }
@ -7397,6 +7414,10 @@ gc::MergeCompartments(JSCompartment* source, JSCompartment* target)
MOZ_ASSERT(source->creationOptions().addonIdOrNull() == MOZ_ASSERT(source->creationOptions().addonIdOrNull() ==
target->creationOptions().addonIdOrNull()); target->creationOptions().addonIdOrNull());
MOZ_ASSERT(!source->hasBeenEntered());
MOZ_ASSERT(source->zone()->compartments().length() == 1);
MOZ_ASSERT(source->zone()->group()->zones().length() == 1);
JSContext* cx = source->runtimeFromActiveCooperatingThread()->activeContextFromOwnThread(); JSContext* cx = source->runtimeFromActiveCooperatingThread()->activeContextFromOwnThread();
MOZ_ASSERT(!source->zone()->wasGCStarted()); MOZ_ASSERT(!source->zone()->wasGCStarted());
@ -7489,6 +7510,32 @@ gc::MergeCompartments(JSCompartment* source, JSCompartment* target)
source->scriptNameMap->clear(); source->scriptNameMap->clear();
} }
// The source compartment is now completely empty, and is the only
// compartment in its zone, which is the only zone in its group. Delete
// compartment, zone and group without waiting for this to be cleaned up by
// a full GC.
Zone* sourceZone = source->zone();
ZoneGroup* sourceGroup = sourceZone->group();
sourceZone->deleteEmptyCompartment(source);
sourceGroup->deleteEmptyZone(sourceZone);
cx->runtime()->gc.deleteEmptyZoneGroup(sourceGroup);
}
void
GCRuntime::deleteEmptyZoneGroup(ZoneGroup* group)
{
MOZ_ASSERT(group->zones().empty());
MOZ_ASSERT(groups().length() > 1);
for (auto& i : groups()) {
if (i == group) {
groups().erase(&i);
js_delete(group);
return;
}
}
MOZ_CRASH("ZoneGroup not found");
} }
void void

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

@ -1598,9 +1598,10 @@ GlobalHelperThreadState::mergeParseTaskCompartment(JSContext* cx, ParseTask* par
JS::AutoAssertNoGC nogc(cx); JS::AutoAssertNoGC nogc(cx);
LeaveParseTaskZone(cx->runtime(), parseTask); LeaveParseTaskZone(cx->runtime(), parseTask);
AutoCompartment ac(cx, parseTask->parseGlobal);
{ {
AutoCompartment ac(cx, parseTask->parseGlobal);
// Generator functions don't have Function.prototype as prototype but a // Generator functions don't have Function.prototype as prototype but a
// different function object, so the IdentifyStandardPrototype trick // different function object, so the IdentifyStandardPrototype trick
// below won't work. Just special-case it. // below won't work. Just special-case it.