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 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:
enum IncrementalResult
@ -1096,7 +1097,10 @@ class GCRuntime
UnprotectedData<ZoneGroup*> systemZoneGroup;
// 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.
WriteOnceData<Zone*> atomsZone;

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

@ -73,6 +73,8 @@ JS::Zone::Zone(JSRuntime* rt, ZoneGroup* group)
Zone::~Zone()
{
MOZ_ASSERT(compartments_.ref().empty());
JSRuntime* rt = runtimeFromAnyThread();
if (this == rt->gc.systemZone)
rt->gc.systemZone = nullptr;
@ -87,7 +89,8 @@ Zone::~Zone()
#endif
}
bool Zone::init(bool isSystemArg)
bool
Zone::init(bool isSystemArg)
{
isSystem = isSystemArg;
return uniqueIds().init() &&
@ -376,6 +379,21 @@ Zone::addTypeDescrObject(JSContext* cx, HandleObject obj)
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()
: head(nullptr), tail(nullptr)
{}

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

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

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

@ -130,4 +130,20 @@ ZoneGroup::ionLazyLinkListAdd(jit::IonBuilder* builder)
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

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

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

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

@ -863,6 +863,7 @@ struct JSCompartment
~JSCompartment();
MOZ_MUST_USE bool init(JSContext* maybecx);
void destroy(js::FreeOp* fop);
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 (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next())
js_delete(comp.get());
zone->compartments().clear();
js_delete(zone.get());
}
}
groups.ref().clear();
groups().clear();
FreeChunkPool(rt, fullChunks_.ref());
FreeChunkPool(rt, availableChunks_.ref());
@ -3483,6 +3484,25 @@ JS::Zone::sweepUniqueIds(js::FreeOp* fop)
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
* compartment. If we know we're deleting the entire zone, then
@ -3495,8 +3515,9 @@ JS::Zone::sweepUniqueIds(js::FreeOp* fop)
void
Zone::sweepCompartments(FreeOp* fop, bool keepAtleastOne, bool destroyingRuntime)
{
JSRuntime* rt = runtimeFromActiveCooperatingThread();
JSDestroyCompartmentCallback callback = rt->destroyCompartmentCallback;
MOZ_ASSERT(!compartments().empty());
mozilla::DebugOnly<JSRuntime*> rt = runtimeFromActiveCooperatingThread();
JSCompartment** read = compartments().begin();
JSCompartment** end = compartments().end();
@ -3512,12 +3533,7 @@ Zone::sweepCompartments(FreeOp* fop, bool keepAtleastOne, bool destroyingRuntime
*/
bool dontDelete = read == end && !foundOne && keepAtleastOne;
if ((!comp->marked && !dontDelete) || destroyingRuntime) {
if (callback)
callback(fop, comp);
if (comp->principals())
JS_DropPrincipals(TlsContext.get(), comp->principals());
js_delete(comp);
rt->gc.stats().sweptCompartment();
comp->destroy(fop);
} else {
*write++ = comp;
foundOne = true;
@ -3530,6 +3546,8 @@ Zone::sweepCompartments(FreeOp* fop, bool keepAtleastOne, bool destroyingRuntime
void
GCRuntime::sweepZones(FreeOp* fop, ZoneGroup* group, bool destroyingRuntime)
{
MOZ_ASSERT(!group->zones().empty());
Zone** read = group->zones().begin();
Zone** end = group->zones().end();
Zone** write = read;
@ -3558,8 +3576,7 @@ GCRuntime::sweepZones(FreeOp* fop, ZoneGroup* group, bool destroyingRuntime)
zone->sweepCompartments(fop, false, destroyingRuntime);
MOZ_ASSERT(zone->compartments().empty());
MOZ_ASSERT_IF(arenasEmptyAtShutdown, zone->typeDescrObjects().empty());
fop->delete_(zone);
stats().sweptZone();
zone->destroy(fop);
continue;
}
zone->sweepCompartments(fop, true, destroyingRuntime);
@ -3580,8 +3597,8 @@ GCRuntime::sweepZoneGroups(FreeOp* fop, bool destroyingRuntime)
assertBackgroundSweepingFinished();
ZoneGroup** read = groups.ref().begin();
ZoneGroup** end = groups.ref().end();
ZoneGroup** read = groups().begin();
ZoneGroup** end = groups().end();
ZoneGroup** write = read;
while (read < end) {
@ -3595,7 +3612,7 @@ GCRuntime::sweepZoneGroups(FreeOp* fop, bool destroyingRuntime)
*write++ = group;
}
}
groups.ref().shrinkTo(write - groups.ref().begin());
groups().shrinkTo(write - groups().begin());
}
#ifdef DEBUG
@ -7367,7 +7384,7 @@ js::NewCompartment(JSContext* cx, JSPrincipals* principals,
}
if (groupHolder) {
if (!rt->gc.groups.ref().append(group)) {
if (!rt->gc.groups().append(group)) {
ReportOutOfMemory(cx);
return nullptr;
}
@ -7397,6 +7414,10 @@ gc::MergeCompartments(JSCompartment* source, JSCompartment* target)
MOZ_ASSERT(source->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();
MOZ_ASSERT(!source->zone()->wasGCStarted());
@ -7489,6 +7510,32 @@ gc::MergeCompartments(JSCompartment* source, JSCompartment* target)
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

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

@ -1598,9 +1598,10 @@ GlobalHelperThreadState::mergeParseTaskCompartment(JSContext* cx, ParseTask* par
JS::AutoAssertNoGC nogc(cx);
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
// different function object, so the IdentifyStandardPrototype trick
// below won't work. Just special-case it.