mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-01 14:45:29 +00:00
Bug 743112 - Incremental release of C++ objects during GC (r=smaug,jonco)
This commit is contained in:
parent
a0d130c9ab
commit
7a352d77b6
@ -541,6 +541,9 @@ struct JSRuntime : js::RuntimeFriendFields
|
|||||||
/* The gcNumber at the time of the most recent GC's first slice. */
|
/* The gcNumber at the time of the most recent GC's first slice. */
|
||||||
uint64_t gcStartNumber;
|
uint64_t gcStartNumber;
|
||||||
|
|
||||||
|
/* Whether the currently running GC can finish in multiple slices. */
|
||||||
|
int gcIsIncremental;
|
||||||
|
|
||||||
/* Whether all compartments are being collected in first GC slice. */
|
/* Whether all compartments are being collected in first GC slice. */
|
||||||
bool gcIsFull;
|
bool gcIsFull;
|
||||||
|
|
||||||
|
@ -776,6 +776,12 @@ SetGCSliceCallback(JSRuntime *rt, GCSliceCallback callback)
|
|||||||
return old;
|
return old;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JS_FRIEND_API(bool)
|
||||||
|
WasIncrementalGC(JSRuntime *rt)
|
||||||
|
{
|
||||||
|
return rt->gcIsIncremental;
|
||||||
|
}
|
||||||
|
|
||||||
jschar *
|
jschar *
|
||||||
GCDescription::formatMessage(JSRuntime *rt) const
|
GCDescription::formatMessage(JSRuntime *rt) const
|
||||||
{
|
{
|
||||||
|
@ -745,6 +745,10 @@ typedef void
|
|||||||
extern JS_FRIEND_API(GCSliceCallback)
|
extern JS_FRIEND_API(GCSliceCallback)
|
||||||
SetGCSliceCallback(JSRuntime *rt, GCSliceCallback callback);
|
SetGCSliceCallback(JSRuntime *rt, GCSliceCallback callback);
|
||||||
|
|
||||||
|
/* Was the most recent GC run incrementally? */
|
||||||
|
extern JS_FRIEND_API(bool)
|
||||||
|
WasIncrementalGC(JSRuntime *rt);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Signals a good place to do an incremental slice, because the browser is
|
* Signals a good place to do an incremental slice, because the browser is
|
||||||
* drawing a frame.
|
* drawing a frame.
|
||||||
|
@ -3179,7 +3179,7 @@ ShouldPreserveJITCode(JSCompartment *c, int64_t currentTime)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
BeginMarkPhase(JSRuntime *rt, bool isIncremental)
|
BeginMarkPhase(JSRuntime *rt)
|
||||||
{
|
{
|
||||||
int64_t currentTime = PRMJ_Now();
|
int64_t currentTime = PRMJ_Now();
|
||||||
|
|
||||||
@ -3190,7 +3190,7 @@ BeginMarkPhase(JSRuntime *rt, bool isIncremental)
|
|||||||
* arenas. This purge call ensures that we only mark arenas that have had
|
* arenas. This purge call ensures that we only mark arenas that have had
|
||||||
* allocations after the incremental GC started.
|
* allocations after the incremental GC started.
|
||||||
*/
|
*/
|
||||||
if (isIncremental) {
|
if (rt->gcIsIncremental) {
|
||||||
for (GCCompartmentsIter c(rt); !c.done(); c.next())
|
for (GCCompartmentsIter c(rt); !c.done(); c.next())
|
||||||
c->arenas.purge();
|
c->arenas.purge();
|
||||||
}
|
}
|
||||||
@ -3214,7 +3214,7 @@ BeginMarkPhase(JSRuntime *rt, bool isIncremental)
|
|||||||
JS_ASSERT(IS_GC_MARKING_TRACER(&rt->gcMarker));
|
JS_ASSERT(IS_GC_MARKING_TRACER(&rt->gcMarker));
|
||||||
|
|
||||||
/* For non-incremental GC the following sweep discards the jit code. */
|
/* For non-incremental GC the following sweep discards the jit code. */
|
||||||
if (isIncremental) {
|
if (rt->gcIsIncremental) {
|
||||||
for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
|
for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
|
||||||
gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_MARK_DISCARD_CODE);
|
gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_MARK_DISCARD_CODE);
|
||||||
c->discardJitCode(rt->defaultFreeOp());
|
c->discardJitCode(rt->defaultFreeOp());
|
||||||
@ -3307,7 +3307,7 @@ ValidateIncrementalMarking(JSRuntime *rt);
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void
|
static void
|
||||||
EndMarkPhase(JSRuntime *rt, bool isIncremental)
|
EndMarkPhase(JSRuntime *rt)
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
gcstats::AutoPhase ap1(rt->gcStats, gcstats::PHASE_MARK);
|
gcstats::AutoPhase ap1(rt->gcStats, gcstats::PHASE_MARK);
|
||||||
@ -3317,7 +3317,7 @@ EndMarkPhase(JSRuntime *rt, bool isIncremental)
|
|||||||
JS_ASSERT(rt->gcMarker.isDrained());
|
JS_ASSERT(rt->gcMarker.isDrained());
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
if (isIncremental)
|
if (rt->gcIsIncremental)
|
||||||
ValidateIncrementalMarking(rt);
|
ValidateIncrementalMarking(rt);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -3884,10 +3884,10 @@ IncrementalCollectSlice(JSRuntime *rt,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool isIncremental = rt->gcIncrementalState != NO_INCREMENTAL ||
|
rt->gcIsIncremental = rt->gcIncrementalState != NO_INCREMENTAL ||
|
||||||
budget != SliceBudget::Unlimited ||
|
budget != SliceBudget::Unlimited ||
|
||||||
zeal == ZealIncrementalRootsThenFinish ||
|
zeal == ZealIncrementalRootsThenFinish ||
|
||||||
zeal == ZealIncrementalMarkAllThenFinish;
|
zeal == ZealIncrementalMarkAllThenFinish;
|
||||||
|
|
||||||
if (rt->gcIncrementalState == NO_INCREMENTAL) {
|
if (rt->gcIncrementalState == NO_INCREMENTAL) {
|
||||||
rt->gcIncrementalState = MARK_ROOTS;
|
rt->gcIncrementalState = MARK_ROOTS;
|
||||||
@ -3897,7 +3897,7 @@ IncrementalCollectSlice(JSRuntime *rt,
|
|||||||
switch (rt->gcIncrementalState) {
|
switch (rt->gcIncrementalState) {
|
||||||
|
|
||||||
case MARK_ROOTS:
|
case MARK_ROOTS:
|
||||||
BeginMarkPhase(rt, isIncremental);
|
BeginMarkPhase(rt);
|
||||||
PushZealSelectedObjects(rt);
|
PushZealSelectedObjects(rt);
|
||||||
|
|
||||||
rt->gcIncrementalState = MARK;
|
rt->gcIncrementalState = MARK;
|
||||||
@ -3932,7 +3932,7 @@ IncrementalCollectSlice(JSRuntime *rt,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
EndMarkPhase(rt, isIncremental);
|
EndMarkPhase(rt);
|
||||||
|
|
||||||
rt->gcIncrementalState = SWEEP;
|
rt->gcIncrementalState = SWEEP;
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include "mozilla/Preferences.h"
|
#include "mozilla/Preferences.h"
|
||||||
#include "mozilla/Telemetry.h"
|
#include "mozilla/Telemetry.h"
|
||||||
|
|
||||||
|
#include "nsLayoutStatics.h"
|
||||||
#include "nsContentUtils.h"
|
#include "nsContentUtils.h"
|
||||||
#include "nsCCUncollectableMarker.h"
|
#include "nsCCUncollectableMarker.h"
|
||||||
#include "jsfriendapi.h"
|
#include "jsfriendapi.h"
|
||||||
@ -552,7 +553,7 @@ xpc_UnmarkSkippableJSHolders()
|
|||||||
template<class T> static void
|
template<class T> static void
|
||||||
DoDeferredRelease(nsTArray<T> &array)
|
DoDeferredRelease(nsTArray<T> &array)
|
||||||
{
|
{
|
||||||
while (1) {
|
while (true) {
|
||||||
PRUint32 count = array.Length();
|
PRUint32 count = array.Length();
|
||||||
if (!count) {
|
if (!count) {
|
||||||
array.Compact();
|
array.Compact();
|
||||||
@ -564,6 +565,74 @@ DoDeferredRelease(nsTArray<T> &array)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class IncrementalReleaseRunnable : public nsRunnable
|
||||||
|
{
|
||||||
|
nsTArray<nsISupports *> items;
|
||||||
|
|
||||||
|
static const PRTime SliceMillis = 10; /* ms */
|
||||||
|
|
||||||
|
public:
|
||||||
|
IncrementalReleaseRunnable(nsTArray<nsISupports *> &items);
|
||||||
|
virtual ~IncrementalReleaseRunnable();
|
||||||
|
|
||||||
|
NS_DECL_NSIRUNNABLE
|
||||||
|
};
|
||||||
|
|
||||||
|
IncrementalReleaseRunnable::IncrementalReleaseRunnable(nsTArray<nsISupports *> &items)
|
||||||
|
{
|
||||||
|
nsLayoutStatics::AddRef();
|
||||||
|
this->items.SwapElements(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
IncrementalReleaseRunnable::~IncrementalReleaseRunnable()
|
||||||
|
{
|
||||||
|
nsLayoutStatics::Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
IncrementalReleaseRunnable::Run()
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
|
TimeDuration sliceTime = TimeDuration::FromMilliseconds(SliceMillis);
|
||||||
|
TimeStamp started = TimeStamp::Now();
|
||||||
|
PRUint32 counter = 0;
|
||||||
|
while (true) {
|
||||||
|
PRUint32 count = items.Length();
|
||||||
|
if (!count)
|
||||||
|
break;
|
||||||
|
|
||||||
|
nsISupports *wrapper = items[count - 1];
|
||||||
|
items.RemoveElementAt(count - 1);
|
||||||
|
NS_RELEASE(wrapper);
|
||||||
|
|
||||||
|
/* We don't want to read the clock too often. */
|
||||||
|
counter++;
|
||||||
|
if (counter == 100) {
|
||||||
|
counter = 0;
|
||||||
|
if (TimeStamp::Now() - started >= sliceTime)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (items.Length()) {
|
||||||
|
nsresult rv = NS_DispatchToMainThread(this);
|
||||||
|
if (NS_FAILED(rv))
|
||||||
|
Run();
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ReleaseIncrementally(nsTArray<nsISupports *> &array)
|
||||||
|
{
|
||||||
|
nsRefPtr<IncrementalReleaseRunnable> runnable = new IncrementalReleaseRunnable(array);
|
||||||
|
nsresult rv = NS_DispatchToMainThread(runnable);
|
||||||
|
if (NS_FAILED(rv))
|
||||||
|
runnable->Run();
|
||||||
|
}
|
||||||
|
|
||||||
/* static */ void
|
/* static */ void
|
||||||
XPCJSRuntime::GCCallback(JSRuntime *rt, JSGCStatus status)
|
XPCJSRuntime::GCCallback(JSRuntime *rt, JSGCStatus status)
|
||||||
{
|
{
|
||||||
@ -586,14 +655,10 @@ XPCJSRuntime::GCCallback(JSRuntime *rt, JSGCStatus status)
|
|||||||
case JSGC_END:
|
case JSGC_END:
|
||||||
{
|
{
|
||||||
// Do any deferred releases of native objects.
|
// Do any deferred releases of native objects.
|
||||||
#ifdef XPC_TRACK_DEFERRED_RELEASES
|
if (js::WasIncrementalGC(rt))
|
||||||
printf("XPC - Begin deferred Release of %d nsISupports pointers\n",
|
ReleaseIncrementally(self->mNativesToReleaseArray);
|
||||||
self->mNativesToReleaseArray.Length());
|
else
|
||||||
#endif
|
DoDeferredRelease(self->mNativesToReleaseArray);
|
||||||
DoDeferredRelease(self->mNativesToReleaseArray);
|
|
||||||
#ifdef XPC_TRACK_DEFERRED_RELEASES
|
|
||||||
printf("XPC - End deferred Releases\n");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
self->GetXPConnect()->ClearGCBeforeCC();
|
self->GetXPConnect()->ClearGCBeforeCC();
|
||||||
break;
|
break;
|
||||||
|
Loading…
Reference in New Issue
Block a user