mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 743112 - Incremental deferred release (r=smaug,jonco)
This commit is contained in:
parent
d478e845d3
commit
92f8b9be64
@ -541,6 +541,9 @@ struct JSRuntime : js::RuntimeFriendFields
|
||||
/* The gcNumber at the time of the most recent GC's first slice. */
|
||||
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. */
|
||||
bool gcIsFull;
|
||||
|
||||
|
@ -780,6 +780,12 @@ SetGCSliceCallback(JSRuntime *rt, GCSliceCallback callback)
|
||||
return old;
|
||||
}
|
||||
|
||||
JS_FRIEND_API(bool)
|
||||
WasIncrementalGC(JSRuntime *rt)
|
||||
{
|
||||
return rt->gcIsIncremental;
|
||||
}
|
||||
|
||||
jschar *
|
||||
GCDescription::formatMessage(JSRuntime *rt) const
|
||||
{
|
||||
|
@ -745,6 +745,10 @@ typedef void
|
||||
extern JS_FRIEND_API(GCSliceCallback)
|
||||
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
|
||||
* drawing a frame.
|
||||
|
@ -3186,7 +3186,7 @@ ShouldPreserveJITCode(JSCompartment *c, int64_t currentTime)
|
||||
}
|
||||
|
||||
static void
|
||||
BeginMarkPhase(JSRuntime *rt, bool isIncremental)
|
||||
BeginMarkPhase(JSRuntime *rt)
|
||||
{
|
||||
int64_t currentTime = PRMJ_Now();
|
||||
|
||||
@ -3197,7 +3197,7 @@ BeginMarkPhase(JSRuntime *rt, bool isIncremental)
|
||||
* arenas. This purge call ensures that we only mark arenas that have had
|
||||
* allocations after the incremental GC started.
|
||||
*/
|
||||
if (isIncremental) {
|
||||
if (rt->gcIsIncremental) {
|
||||
for (GCCompartmentsIter c(rt); !c.done(); c.next())
|
||||
c->arenas.purge();
|
||||
}
|
||||
@ -3221,7 +3221,7 @@ BeginMarkPhase(JSRuntime *rt, bool isIncremental)
|
||||
JS_ASSERT(IS_GC_MARKING_TRACER(&rt->gcMarker));
|
||||
|
||||
/* For non-incremental GC the following sweep discards the jit code. */
|
||||
if (isIncremental) {
|
||||
if (rt->gcIsIncremental) {
|
||||
for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
|
||||
gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_MARK_DISCARD_CODE);
|
||||
c->discardJitCode(rt->defaultFreeOp());
|
||||
@ -3314,7 +3314,7 @@ ValidateIncrementalMarking(JSRuntime *rt);
|
||||
#endif
|
||||
|
||||
static void
|
||||
EndMarkPhase(JSRuntime *rt, bool isIncremental)
|
||||
EndMarkPhase(JSRuntime *rt)
|
||||
{
|
||||
{
|
||||
gcstats::AutoPhase ap1(rt->gcStats, gcstats::PHASE_MARK);
|
||||
@ -3324,7 +3324,7 @@ EndMarkPhase(JSRuntime *rt, bool isIncremental)
|
||||
JS_ASSERT(rt->gcMarker.isDrained());
|
||||
|
||||
#ifdef DEBUG
|
||||
if (isIncremental)
|
||||
if (rt->gcIsIncremental)
|
||||
ValidateIncrementalMarking(rt);
|
||||
#endif
|
||||
|
||||
@ -3889,8 +3889,8 @@ IncrementalCollectSlice(JSRuntime *rt,
|
||||
}
|
||||
#endif
|
||||
|
||||
bool isIncremental = rt->gcIncrementalState != NO_INCREMENTAL ||
|
||||
budget != SliceBudget::Unlimited;
|
||||
rt->gcIsIncremental = rt->gcIncrementalState != NO_INCREMENTAL ||
|
||||
budget != SliceBudget::Unlimited;
|
||||
|
||||
if (zeal == ZealIncrementalRootsThenFinish || zeal == ZealIncrementalMarkAllThenFinish) {
|
||||
/*
|
||||
@ -3908,7 +3908,7 @@ IncrementalCollectSlice(JSRuntime *rt,
|
||||
switch (rt->gcIncrementalState) {
|
||||
|
||||
case MARK_ROOTS:
|
||||
BeginMarkPhase(rt, isIncremental);
|
||||
BeginMarkPhase(rt);
|
||||
PushZealSelectedObjects(rt);
|
||||
|
||||
rt->gcIncrementalState = MARK;
|
||||
@ -3943,7 +3943,7 @@ IncrementalCollectSlice(JSRuntime *rt,
|
||||
break;
|
||||
}
|
||||
|
||||
EndMarkPhase(rt, isIncremental);
|
||||
EndMarkPhase(rt);
|
||||
|
||||
rt->gcIncrementalState = SWEEP;
|
||||
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
|
||||
#include "nsLayoutStatics.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsCCUncollectableMarker.h"
|
||||
#include "jsfriendapi.h"
|
||||
@ -564,6 +565,103 @@ DoDeferredRelease(nsTArray<T> &array)
|
||||
}
|
||||
}
|
||||
|
||||
class XPCIncrementalReleaseRunnable : public nsRunnable
|
||||
{
|
||||
XPCJSRuntime *runtime;
|
||||
nsTArray<nsISupports *> items;
|
||||
|
||||
static const PRTime SliceMillis = 10; /* ms */
|
||||
|
||||
public:
|
||||
XPCIncrementalReleaseRunnable(XPCJSRuntime *rt, nsTArray<nsISupports *> &items);
|
||||
virtual ~XPCIncrementalReleaseRunnable();
|
||||
|
||||
void ReleaseNow(bool limited);
|
||||
|
||||
NS_DECL_NSIRUNNABLE
|
||||
};
|
||||
|
||||
XPCIncrementalReleaseRunnable::XPCIncrementalReleaseRunnable(XPCJSRuntime *rt,
|
||||
nsTArray<nsISupports *> &items)
|
||||
: runtime(rt)
|
||||
{
|
||||
nsLayoutStatics::AddRef();
|
||||
this->items.SwapElements(items);
|
||||
}
|
||||
|
||||
XPCIncrementalReleaseRunnable::~XPCIncrementalReleaseRunnable()
|
||||
{
|
||||
MOZ_ASSERT(this != runtime->mReleaseRunnable);
|
||||
nsLayoutStatics::Release();
|
||||
}
|
||||
|
||||
void
|
||||
XPCIncrementalReleaseRunnable::ReleaseNow(bool limited)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
TimeDuration sliceTime = TimeDuration::FromMilliseconds(SliceMillis);
|
||||
TimeStamp started = TimeStamp::Now();
|
||||
PRUint32 counter = 0;
|
||||
while (1) {
|
||||
PRUint32 count = items.Length();
|
||||
if (!count)
|
||||
break;
|
||||
|
||||
nsISupports *wrapper = items[count - 1];
|
||||
items.RemoveElementAt(count - 1);
|
||||
NS_RELEASE(wrapper);
|
||||
|
||||
if (limited) {
|
||||
/* We don't want to read the clock too often. */
|
||||
counter++;
|
||||
if (counter == 100) {
|
||||
counter = 0;
|
||||
if (TimeStamp::Now() - started >= sliceTime)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ASSERT_IF(items.Length(), limited);
|
||||
|
||||
if (!items.Length()) {
|
||||
MOZ_ASSERT(runtime->mReleaseRunnable == this);
|
||||
runtime->mReleaseRunnable = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
XPCIncrementalReleaseRunnable::Run()
|
||||
{
|
||||
if (runtime->mReleaseRunnable != this) {
|
||||
/* These items were already processed synchronously in JSGC_BEGIN. */
|
||||
MOZ_ASSERT(!items.Length());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
ReleaseNow(true);
|
||||
|
||||
if (items.Length()) {
|
||||
nsresult rv = NS_DispatchToMainThread(this);
|
||||
if (NS_FAILED(rv))
|
||||
ReleaseNow(false);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
XPCJSRuntime::ReleaseIncrementally(nsTArray<nsISupports *> &array)
|
||||
{
|
||||
MOZ_ASSERT(!mReleaseRunnable);
|
||||
mReleaseRunnable = new XPCIncrementalReleaseRunnable(this, array);
|
||||
|
||||
nsresult rv = NS_DispatchToMainThread(mReleaseRunnable);
|
||||
if (NS_FAILED(rv))
|
||||
mReleaseRunnable->ReleaseNow(false);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
XPCJSRuntime::GCCallback(JSRuntime *rt, JSGCStatus status)
|
||||
{
|
||||
@ -585,15 +683,21 @@ XPCJSRuntime::GCCallback(JSRuntime *rt, JSGCStatus status)
|
||||
}
|
||||
case JSGC_END:
|
||||
{
|
||||
/*
|
||||
* If the previous GC created a runnable to release objects
|
||||
* incrementally, and if it hasn't finished yet, finish it now. We
|
||||
* don't want these to build up. We also don't want to allow any
|
||||
* existing incremental release runnables to run after a
|
||||
* non-incremental GC, since they are often used to detect leaks.
|
||||
*/
|
||||
if (self->mReleaseRunnable)
|
||||
self->mReleaseRunnable->ReleaseNow(false);
|
||||
|
||||
// Do any deferred releases of native objects.
|
||||
#ifdef XPC_TRACK_DEFERRED_RELEASES
|
||||
printf("XPC - Begin deferred Release of %d nsISupports pointers\n",
|
||||
self->mNativesToReleaseArray.Length());
|
||||
#endif
|
||||
DoDeferredRelease(self->mNativesToReleaseArray);
|
||||
#ifdef XPC_TRACK_DEFERRED_RELEASES
|
||||
printf("XPC - End deferred Releases\n");
|
||||
#endif
|
||||
if (js::WasIncrementalGC(rt))
|
||||
self->ReleaseIncrementally(self->mNativesToReleaseArray);
|
||||
else
|
||||
DoDeferredRelease(self->mNativesToReleaseArray);
|
||||
|
||||
self->GetXPConnect()->ClearGCBeforeCC();
|
||||
break;
|
||||
@ -964,6 +1068,8 @@ XPCJSRuntime::GetJSCycleCollectionContext()
|
||||
|
||||
XPCJSRuntime::~XPCJSRuntime()
|
||||
{
|
||||
MOZ_ASSERT(!mReleaseRunnable);
|
||||
|
||||
if (mWatchdogWakeup) {
|
||||
// If the watchdog thread is running, tell it to terminate waking it
|
||||
// up if necessary and wait until it signals that it finished. As we
|
||||
@ -2000,6 +2106,7 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
|
||||
mWatchdogThread(nullptr),
|
||||
mWatchdogHibernating(false),
|
||||
mLastActiveTime(-1),
|
||||
mReleaseRunnable(nullptr),
|
||||
mExceptionManagerNotAvailable(false)
|
||||
{
|
||||
#ifdef XPC_CHECK_WRAPPERS_AT_SHUTDOWN
|
||||
|
@ -638,6 +638,7 @@ private:
|
||||
|
||||
// no virtuals. no refcounting.
|
||||
class XPCJSContextStack;
|
||||
class XPCIncrementalReleaseRunnable;
|
||||
class XPCJSRuntime
|
||||
{
|
||||
public:
|
||||
@ -866,6 +867,8 @@ private:
|
||||
|
||||
static void WatchdogMain(void *arg);
|
||||
|
||||
void ReleaseIncrementally(nsTArray<nsISupports *> &array);
|
||||
|
||||
static bool gNewDOMBindingsEnabled;
|
||||
static bool gExperimentalBindingsEnabled;
|
||||
|
||||
@ -906,12 +909,14 @@ private:
|
||||
nsTArray<JSGCCallback> extraGCCallbacks;
|
||||
bool mWatchdogHibernating;
|
||||
PRTime mLastActiveTime; // -1 if active NOW
|
||||
XPCIncrementalReleaseRunnable *mReleaseRunnable;
|
||||
|
||||
nsCOMPtr<nsIException> mPendingException;
|
||||
nsCOMPtr<nsIExceptionManager> mExceptionManager;
|
||||
bool mExceptionManagerNotAvailable;
|
||||
|
||||
friend class AutoLockWatchdog;
|
||||
friend class XPCIncrementalReleaseRunnable;
|
||||
};
|
||||
|
||||
/***************************************************************************/
|
||||
|
Loading…
Reference in New Issue
Block a user