Bug 978387 - Mark the ArrayBufferObject view list weakly during minor collections; r=sfink

--HG--
extra : rebase_source : c085f148836a8d23b8e6d1276625a1dd169cbbf1
This commit is contained in:
Terrence Cole 2014-03-03 18:41:58 -08:00
parent 394d5f9fd5
commit 79e32315b4
5 changed files with 90 additions and 65 deletions

View File

@ -366,9 +366,19 @@ IsAboutToBeFinalized(T **thingp)
return !(*thingp)->isMarked();
}
template <typename T>
T *
UpdateIfRelocated(JSRuntime *rt, T **thingp)
{
JS_ASSERT(thingp);
if (*thingp && rt->isHeapMinorCollecting())
IsAboutToBeFinalized<T>(thingp);
return *thingp;
}
#define DeclMarkerImpl(base, type) \
void \
Mark##base(JSTracer *trc, BarrieredPtr<type> *thing, const char *name) \
Mark##base(JSTracer *trc, BarrieredPtr<type> *thing, const char *name) \
{ \
Mark<type>(trc, thing, name); \
} \
@ -409,7 +419,7 @@ Is##base##Marked(type **thingp)
} \
\
bool \
Is##base##Marked(BarrieredPtr<type> *thingp) \
Is##base##Marked(BarrieredPtr<type> *thingp) \
{ \
return IsMarked<type>(thingp->unsafeGet()); \
} \
@ -421,11 +431,24 @@ Is##base##AboutToBeFinalized(type **thingp)
} \
\
bool \
Is##base##AboutToBeFinalized(BarrieredPtr<type> *thingp) \
Is##base##AboutToBeFinalized(BarrieredPtr<type> *thingp) \
{ \
return IsAboutToBeFinalized<type>(thingp->unsafeGet()); \
} \
\
type * \
Update##base##IfRelocated(JSRuntime *rt, BarrieredPtr<type> *thingp) \
{ \
return UpdateIfRelocated<type>(rt, thingp->unsafeGet()); \
} \
\
type * \
Update##base##IfRelocated(JSRuntime *rt, type **thingp) \
{ \
return UpdateIfRelocated<type>(rt, thingp); \
}
DeclMarkerImpl(BaseShape, BaseShape)
DeclMarkerImpl(BaseShape, UnownedBaseShape)
DeclMarkerImpl(JitCode, jit::JitCode)

View File

@ -80,17 +80,25 @@ namespace gc {
* IsObjectMarked(JSObject **thing);
* This function is indended to be used in rare cases in code used to mark
* GC things. It indicates whether the object is currently marked.
*
* UpdateObjectIfRelocated(JSObject **thingp);
* In some circumstances -- e.g. optional weak marking -- it is necessary
* to look at the pointer before marking it strongly or weakly. In these
* cases, the following must be called to update the pointer before use.
*/
#define DeclMarker(base, type) \
void Mark##base(JSTracer *trc, BarrieredPtr<type> *thing, const char *name); \
void Mark##base(JSTracer *trc, BarrieredPtr<type> *thing, const char *name); \
void Mark##base##Root(JSTracer *trc, type **thingp, const char *name); \
void Mark##base##Unbarriered(JSTracer *trc, type **thingp, const char *name); \
void Mark##base##Range(JSTracer *trc, size_t len, HeapPtr<type> *thing, const char *name); \
void Mark##base##RootRange(JSTracer *trc, size_t len, type **thing, const char *name); \
bool Is##base##Marked(type **thingp); \
bool Is##base##Marked(BarrieredPtr<type> *thingp); \
bool Is##base##Marked(BarrieredPtr<type> *thingp); \
bool Is##base##AboutToBeFinalized(type **thingp); \
bool Is##base##AboutToBeFinalized(BarrieredPtr<type> *thingp); \
bool Is##base##AboutToBeFinalized(BarrieredPtr<type> *thingp); \
type *Update##base##IfRelocated(JSRuntime *rt, BarrieredPtr<type> *thingp); \
type *Update##base##IfRelocated(JSRuntime *rt, type **thingp);
DeclMarker(BaseShape, BaseShape)
DeclMarker(BaseShape, UnownedBaseShape)

View File

@ -722,10 +722,10 @@ js::Nursery::collect(JSRuntime *rt, JS::gcreason::Reason reason, TypeObjectList
AutoStopVerifyingBarriers av(rt, false);
/* Move objects pointed to by roots from the nursery to the major heap. */
// Move objects pointed to by roots from the nursery to the major heap.
MinorCollectionTracer trc(rt, this);
/* Mark the store buffer. This must happen first. */
// Mark the store buffer. This must happen first.
StoreBuffer &sb = rt->gcStoreBuffer;
TIME_START(markValues);
sb.markValues(&trc);
@ -771,25 +771,31 @@ js::Nursery::collect(JSRuntime *rt, JS::gcreason::Reason reason, TypeObjectList
rt->newObjectCache.clearNurseryObjects(rt);
TIME_END(clearNewObjectCache);
/*
* Most of the work is done here. This loop iterates over objects that have
* been moved to the major heap. If these objects have any outgoing pointers
* to the nursery, then those nursery objects get moved as well, until no
* objects are left to move. That is, we iterate to a fixed point.
*/
// Most of the work is done here. This loop iterates over objects that have
// been moved to the major heap. If these objects have any outgoing pointers
// to the nursery, then those nursery objects get moved as well, until no
// objects are left to move. That is, we iterate to a fixed point.
TIME_START(collectToFP);
TenureCountCache tenureCounts;
collectToFixedPoint(&trc, tenureCounts);
TIME_END(collectToFP);
// Update the array buffer object's view lists.
TIME_START(sweepArrayBufferViewList);
for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
if (c->gcLiveArrayBuffers)
ArrayBufferObject::sweep(c);
}
TIME_END(sweepArrayBufferViewList);
// Update any slot or element pointers whose destination has been tenured.
TIME_START(updateJitActivations);
#ifdef JS_ION
/* Update any slot or element pointers whose destination has been tenured. */
js::jit::UpdateJitActivationsForMinorGC(rt, &trc);
#endif
TIME_END(updateJitActivations);
/* Resize the nursery. */
// Resize the nursery.
TIME_START(resize);
double promotionRate = trc.tenuredSize / double(allocationEnd() - start());
if (promotionRate > 0.05)
@ -798,11 +804,11 @@ js::Nursery::collect(JSRuntime *rt, JS::gcreason::Reason reason, TypeObjectList
shrinkAllocableSpace();
TIME_END(resize);
TIME_START(pretenure);
// If we are promoting the nursery, or exhausted the store buffer with
// pointers to nursery things, which will force a collection well before
// the nursery is full, look for object types that are getting promoted
// excessively and try to pretenure them.
TIME_START(pretenure);
if (pretenureTypes && (promotionRate > 0.8 || reason == JS::gcreason::FULL_STORE_BUFFER)) {
for (size_t i = 0; i < ArrayLength(tenureCounts.entries); i++) {
const TenureCount &entry = tenureCounts.entries[i];
@ -812,7 +818,7 @@ js::Nursery::collect(JSRuntime *rt, JS::gcreason::Reason reason, TypeObjectList
}
TIME_END(pretenure);
/* Sweep. */
// Sweep.
TIME_START(freeHugeSlots);
freeHugeSlots(rt);
TIME_END(freeHugeSlots);
@ -825,11 +831,9 @@ js::Nursery::collect(JSRuntime *rt, JS::gcreason::Reason reason, TypeObjectList
rt->gcStoreBuffer.clear();
TIME_END(clearStoreBuffer);
/*
* We ignore gcMaxBytes when allocating for minor collection. However, if we
* overflowed, we disable the nursery. The next time we allocate, we'll fail
* because gcBytes >= gcMaxBytes.
*/
// We ignore gcMaxBytes when allocating for minor collection. However, if we
// overflowed, we disable the nursery. The next time we allocate, we'll fail
// because gcBytes >= gcMaxBytes.
if (rt->gcBytes >= rt->gcMaxBytes)
disable();
@ -842,13 +846,13 @@ js::Nursery::collect(JSRuntime *rt, JS::gcreason::Reason reason, TypeObjectList
static bool printedHeader = false;
if (!printedHeader) {
fprintf(stderr,
"MinorGC: Reason PRate Size Time mkVals mkClls mkSlts mkWCll mkRVal mkRCll mkGnrc ckTbls mkRntm mkDbgr clrNOC collct updtIn resize pretnr frSlts clrSB sweep\n");
"MinorGC: Reason PRate Size Time mkVals mkClls mkSlts mkWCll mkRVal mkRCll mkGnrc ckTbls mkRntm mkDbgr clrNOC collct swpABO updtIn resize pretnr frSlts clrSB sweep\n");
printedHeader = true;
}
#define FMT " %6" PRIu64
fprintf(stderr,
"MinorGC: %20s %5.1f%% %4d" FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT "\n",
"MinorGC: %20s %5.1f%% %4d" FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT "\n",
js::gcstats::ExplainReason(reason),
promotionRate * 100,
numActiveChunks_,
@ -865,6 +869,7 @@ js::Nursery::collect(JSRuntime *rt, JS::gcreason::Reason reason, TypeObjectList
TIME_TOTAL(markDebugger),
TIME_TOTAL(clearNewObjectCache),
TIME_TOTAL(collectToFP),
TIME_TOTAL(sweepArrayBufferViewList),
TIME_TOTAL(updateJitActivations),
TIME_TOTAL(resize),
TIME_TOTAL(pretenure),

View File

@ -100,7 +100,7 @@ StoreBuffer::MonoTypeBuffer<T>::handleOverflow(StoreBuffer *owner)
* compacting unless the buffer is totally full.
*/
if (storage_->availableInCurrentChunk() < sizeof(T))
compact(owner);
maybeCompact(owner);
}
}

View File

@ -821,6 +821,9 @@ ArrayBufferObject::obj_trace(JSTracer *trc, JSObject *obj)
obj->setPrivateUnbarriered(delegate);
}
if (!IS_GC_MARKING_TRACER(trc) && !trc->runtime->isHeapMinorCollecting())
return;
// ArrayBufferObjects need to maintain a list of possibly-weak pointers to
// their views. The straightforward way to update the weak pointers would
// be in the views' finalizers, but giving views finalizers means they
@ -834,69 +837,55 @@ ArrayBufferObject::obj_trace(JSTracer *trc, JSObject *obj)
// then swept to prune out their dead views.
ArrayBufferObject &buffer = AsArrayBuffer(obj);
ArrayBufferViewObject *viewsHead = GetViewList(&buffer);
ArrayBufferViewObject *viewsHead = UpdateObjectIfRelocated(trc->runtime,
&GetViewListRef(&buffer));
if (!viewsHead)
return;
// During minor collections, mark weak pointers on the buffer strongly.
if (trc->runtime->isHeapMinorCollecting()) {
MarkObject(trc, &GetViewListRef(&buffer), "arraybuffer.viewlist");
ArrayBufferViewObject *prior = GetViewList(&buffer);
for (ArrayBufferViewObject *view = prior->nextView();
view;
prior = view, view = view->nextView())
{
MarkObjectUnbarriered(trc, &view, "arraybuffer.views");
prior->setNextView(view);
}
return;
}
viewsHead = UpdateObjectIfRelocated(trc->runtime, &GetViewListRef(&buffer));
ArrayBufferViewObject *firstView = viewsHead;
if (firstView->nextView() == nullptr) {
// Single view: mark it, but only if we're actually doing a GC pass
// right now. Otherwise, the tracing pass for barrier verification will
// fail if we add another view and the pointer becomes weak.
if (IS_GC_MARKING_TRACER(trc))
MarkObject(trc, &GetViewListRef(&buffer), "arraybuffer.singleview");
MarkObject(trc, &GetViewListRef(&buffer), "arraybuffer.singleview");
} else {
// Multiple views: do not mark, but append buffer to list.
if (IS_GC_MARKING_TRACER(trc)) {
// obj_trace may be called multiple times before sweep(), so avoid
// adding this buffer to the list multiple times.
if (firstView->bufferLink() == UNSET_BUFFER_LINK) {
JS_ASSERT(obj->compartment() == firstView->compartment());
ArrayBufferObject **bufList = &obj->compartment()->gcLiveArrayBuffers;
firstView->setBufferLink(*bufList);
*bufList = &AsArrayBuffer(obj);
} else {
// obj_trace may be called multiple times before sweep(), so avoid
// adding this buffer to the list multiple times.
if (firstView->bufferLink() == UNSET_BUFFER_LINK) {
JS_ASSERT(obj->compartment() == firstView->compartment());
ArrayBufferObject **bufList = &obj->compartment()->gcLiveArrayBuffers;
firstView->setBufferLink(*bufList);
*bufList = &AsArrayBuffer(obj);
} else {
#ifdef DEBUG
bool found = false;
for (ArrayBufferObject *p = obj->compartment()->gcLiveArrayBuffers;
p;
p = GetViewList(p)->bufferLink())
bool found = false;
for (ArrayBufferObject *p = obj->compartment()->gcLiveArrayBuffers;
p;
p = GetViewList(p)->bufferLink())
{
if (p == obj)
{
if (p == obj)
{
JS_ASSERT(!found);
found = true;
}
JS_ASSERT(!found);
found = true;
}
#endif
}
#endif
}
}
}
void
/* static */ void
ArrayBufferObject::sweep(JSCompartment *compartment)
{
JSRuntime *rt = compartment->runtimeFromMainThread();
ArrayBufferObject *buffer = compartment->gcLiveArrayBuffers;
JS_ASSERT(buffer != UNSET_BUFFER_LINK);
compartment->gcLiveArrayBuffers = nullptr;
while (buffer) {
ArrayBufferViewObject *viewsHead = GetViewList(buffer);
ArrayBufferViewObject *viewsHead = UpdateObjectIfRelocated(rt, &GetViewListRef(buffer));
JS_ASSERT(viewsHead);
ArrayBufferObject *nextBuffer = viewsHead->bufferLink();
@ -914,7 +903,7 @@ ArrayBufferObject::sweep(JSCompartment *compartment)
view->setNextView(prevLiveView);
prevLiveView = view;
}
view = nextView;
view = UpdateObjectIfRelocated(rt, &nextView);
}
SetViewList(buffer, prevLiveView);