mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 978387 - Mark the ArrayBufferObject view list weakly during minor collections; r=sfink
--HG-- extra : rebase_source : c085f148836a8d23b8e6d1276625a1dd169cbbf1
This commit is contained in:
parent
394d5f9fd5
commit
79e32315b4
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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),
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user