Bug 1247122 - Propagate the NS_ERROR_OUT_OF_MEMORY from CycleCollectedJSRuntime CTOR, r=khuey

This commit is contained in:
Andrea Marchesini 2016-02-14 13:30:25 +00:00
parent 245f6d9fcf
commit b532e955dc
6 changed files with 171 additions and 67 deletions

View File

@ -886,23 +886,10 @@ class WorkerJSRuntime : public mozilla::CycleCollectedJSRuntime
public:
// The heap size passed here doesn't matter, we will change it later in the
// call to JS_SetGCParameter inside CreateJSContextForWorker.
WorkerJSRuntime(JSRuntime* aParentRuntime, WorkerPrivate* aWorkerPrivate)
: CycleCollectedJSRuntime(aParentRuntime,
WORKER_DEFAULT_RUNTIME_HEAPSIZE,
WORKER_DEFAULT_NURSERY_SIZE),
mWorkerPrivate(aWorkerPrivate)
explicit WorkerJSRuntime(WorkerPrivate* aWorkerPrivate)
: mWorkerPrivate(aWorkerPrivate)
{
JSRuntime* rt = Runtime();
MOZ_ASSERT(rt);
JS_SetRuntimePrivate(rt, new WorkerThreadRuntimePrivate(aWorkerPrivate));
js::SetPreserveWrapperCallback(rt, PreserveWrapper);
JS_InitDestroyPrincipalsCallback(rt, DestroyWorkerPrincipals);
JS_SetWrapObjectCallbacks(rt, &WrapObjectCallbacks);
if (aWorkerPrivate->IsDedicatedWorker()) {
JS_SetFutexCanWait(rt);
}
MOZ_ASSERT(aWorkerPrivate);
}
~WorkerJSRuntime()
@ -924,6 +911,31 @@ public:
mWorkerPrivate = nullptr;
}
nsresult Initialize(JSRuntime* aParentRuntime)
{
nsresult rv =
CycleCollectedJSRuntime::Initialize(aParentRuntime,
WORKER_DEFAULT_RUNTIME_HEAPSIZE,
WORKER_DEFAULT_NURSERY_SIZE);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
JSRuntime* rt = Runtime();
MOZ_ASSERT(rt);
JS_SetRuntimePrivate(rt, new WorkerThreadRuntimePrivate(mWorkerPrivate));
js::SetPreserveWrapperCallback(rt, PreserveWrapper);
JS_InitDestroyPrincipalsCallback(rt, DestroyWorkerPrincipals);
JS_SetWrapObjectCallbacks(rt, &WrapObjectCallbacks);
if (mWorkerPrivate->IsDedicatedWorker()) {
JS_SetFutexCanWait(rt);
}
return NS_OK;
}
virtual void
PrepareForForgetSkippable() override
{
@ -2695,7 +2707,12 @@ WorkerThreadPrimaryRunnable::Run()
{
nsCycleCollector_startup();
WorkerJSRuntime runtime(mParentRuntime, mWorkerPrivate);
WorkerJSRuntime runtime(mWorkerPrivate);
nsresult rv = runtime.Initialize(mParentRuntime);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
JSRuntime* rt = runtime.Runtime();
JSContext* cx = CreateJSContextForWorker(mWorkerPrivate, rt);

View File

@ -3339,9 +3339,8 @@ static const JSWrapObjectCallbacks WrapObjectCallbacks = {
xpc::WrapperFactory::PrepareForWrapping
};
XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
: CycleCollectedJSRuntime(nullptr, JS::DefaultHeapMaxBytes, JS::DefaultNurseryBytes),
mJSContextStack(new XPCJSContextStack(this)),
XPCJSRuntime::XPCJSRuntime()
: mJSContextStack(new XPCJSContextStack(this)),
mCallContext(nullptr),
mAutoRoots(nullptr),
mResolveName(JSID_VOID),
@ -3362,18 +3361,31 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
mWrappedJSRoots(nullptr),
mObjectHolderRoots(nullptr),
mWatchdogManager(new WatchdogManager(this)),
mUnprivilegedJunkScope(this->Runtime(), nullptr),
mPrivilegedJunkScope(this->Runtime(), nullptr),
mCompilationScope(this->Runtime(), nullptr),
mAsyncSnowWhiteFreer(new AsyncFreeSnowWhite()),
mSlowScriptSecondHalf(false)
{
// these jsids filled in later when we have a JSContext to work with.
mStrIDs[0] = JSID_VOID;
}
nsresult
XPCJSRuntime::Initialize()
{
nsresult rv = CycleCollectedJSRuntime::Initialize(nullptr,
JS::DefaultHeapMaxBytes,
JS::DefaultNurseryBytes);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
MOZ_ASSERT(Runtime());
JSRuntime* runtime = Runtime();
mUnprivilegedJunkScope.init(runtime, nullptr);
mPrivilegedJunkScope.init(runtime, nullptr);
mCompilationScope.init(runtime, nullptr);
// these jsids filled in later when we have a JSContext to work with.
mStrIDs[0] = JSID_VOID;
auto rtPrivate = new PerThreadAtomCache();
memset(rtPrivate, 0, sizeof(PerThreadAtomCache));
JS_SetRuntimePrivate(runtime, rtPrivate);
@ -3520,18 +3532,23 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
// Watch for the JS boolean options.
ReloadPrefsCallback(nullptr, this);
Preferences::RegisterCallback(ReloadPrefsCallback, JS_OPTIONS_DOT_STR, this);
return NS_OK;
}
// static
XPCJSRuntime*
XPCJSRuntime::newXPCJSRuntime(nsXPConnect* aXPConnect)
XPCJSRuntime::newXPCJSRuntime()
{
NS_PRECONDITION(aXPConnect,"bad param");
XPCJSRuntime* self = new XPCJSRuntime();
nsresult rv = self->Initialize();
if (NS_FAILED(rv)) {
NS_RUNTIMEABORT("new XPCJSRuntime failed to initialize.");
delete self;
return nullptr;
}
XPCJSRuntime* self = new XPCJSRuntime(aXPConnect);
if (self &&
self->Runtime() &&
if (self->Runtime() &&
self->GetMultiCompartmentWrappedJSMap() &&
self->GetWrappedJSClassMap() &&
self->GetIID2NativeInterfaceMap() &&

View File

@ -60,7 +60,10 @@ nsXPConnect::nsXPConnect()
: mRuntime(nullptr),
mShuttingDown(false)
{
mRuntime = XPCJSRuntime::newXPCJSRuntime(this);
mRuntime = XPCJSRuntime::newXPCJSRuntime();
if (!mRuntime) {
NS_RUNTIMEABORT("Couldn't create XPCJSRuntime.");
}
char* reportableEnv = PR_GetEnv("MOZ_REPORT_ALL_JS_EXCEPTIONS");
if (reportableEnv && *reportableEnv)

View File

@ -425,7 +425,7 @@ private:
class XPCJSRuntime : public mozilla::CycleCollectedJSRuntime
{
public:
static XPCJSRuntime* newXPCJSRuntime(nsXPConnect* aXPConnect);
static XPCJSRuntime* newXPCJSRuntime();
static XPCJSRuntime* Get() { return nsXPConnect::XPConnect()->GetRuntime(); }
XPCJSContextStack* GetJSContextStack() {return mJSContextStack;}
@ -614,8 +614,9 @@ public:
PRTime GetWatchdogTimestamp(WatchdogTimestampCategory aCategory);
private:
XPCJSRuntime() = delete;
explicit XPCJSRuntime(nsXPConnect* aXPConnect);
XPCJSRuntime();
nsresult Initialize();
void ReleaseIncrementally(nsTArray<nsISupports*>& array);

View File

@ -409,9 +409,7 @@ void JSObjectsTenuredCb(JSRuntime* aRuntime, void* aData)
static_cast<CycleCollectedJSRuntime*>(aData)->JSObjectsTenured();
}
CycleCollectedJSRuntime::CycleCollectedJSRuntime(JSRuntime* aParentRuntime,
uint32_t aMaxBytes,
uint32_t aMaxNurseryBytes)
CycleCollectedJSRuntime::CycleCollectedJSRuntime()
: mGCThingCycleCollectorGlobal(sGCThingCycleCollectorGlobal)
, mJSZoneCycleCollectorGlobal(sJSZoneCycleCollectorGlobal)
, mJSRuntime(nullptr)
@ -425,16 +423,52 @@ CycleCollectedJSRuntime::CycleCollectedJSRuntime(JSRuntime* aParentRuntime,
nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
mOwningThread = thread.forget().downcast<nsThread>().take();
MOZ_RELEASE_ASSERT(mOwningThread);
}
CycleCollectedJSRuntime::~CycleCollectedJSRuntime()
{
// If the allocation failed, here we are.
if (!mJSRuntime) {
return;
}
MOZ_ASSERT(!mDeferredFinalizerTable.Count());
// Last chance to process any events.
ProcessMetastableStateQueue(mBaseRecursionDepth);
MOZ_ASSERT(mMetastableStateEvents.IsEmpty());
ProcessStableStateQueue();
MOZ_ASSERT(mStableStateEvents.IsEmpty());
// Clear mPendingException first, since it might be cycle collected.
mPendingException = nullptr;
JS_DestroyRuntime(mJSRuntime);
mJSRuntime = nullptr;
nsCycleCollector_forgetJSRuntime();
mozilla::dom::DestroyScriptSettings();
mOwningThread->SetScriptObserver(nullptr);
NS_RELEASE(mOwningThread);
}
nsresult
CycleCollectedJSRuntime::Initialize(JSRuntime* aParentRuntime,
uint32_t aMaxBytes,
uint32_t aMaxNurseryBytes)
{
MOZ_ASSERT(!mJSRuntime);
mOwningThread->SetScriptObserver(this);
// The main thread has a base recursion depth of 0, workers of 1.
mBaseRecursionDepth = RecursionDepth();
mozilla::dom::InitScriptSettings();
mJSRuntime = JS_NewRuntime(aMaxBytes, aMaxNurseryBytes, aParentRuntime);
if (!mJSRuntime) {
MOZ_CRASH();
return NS_ERROR_OUT_OF_MEMORY;
}
if (!JS_AddExtraGCRootsTracer(mJSRuntime, TraceBlackJS, this)) {
@ -476,31 +510,8 @@ CycleCollectedJSRuntime::CycleCollectedJSRuntime(JSRuntime* aParentRuntime,
JS::dbg::SetDebuggerMallocSizeOf(mJSRuntime, moz_malloc_size_of);
nsCycleCollector_registerJSRuntime(this);
}
CycleCollectedJSRuntime::~CycleCollectedJSRuntime()
{
MOZ_ASSERT(mJSRuntime);
MOZ_ASSERT(!mDeferredFinalizerTable.Count());
// Last chance to process any events.
ProcessMetastableStateQueue(mBaseRecursionDepth);
MOZ_ASSERT(mMetastableStateEvents.IsEmpty());
ProcessStableStateQueue();
MOZ_ASSERT(mStableStateEvents.IsEmpty());
// Clear mPendingException first, since it might be cycle collected.
mPendingException = nullptr;
JS_DestroyRuntime(mJSRuntime);
mJSRuntime = nullptr;
nsCycleCollector_forgetJSRuntime();
mozilla::dom::DestroyScriptSettings();
mOwningThread->SetScriptObserver(nullptr);
NS_RELEASE(mOwningThread);
return NS_OK;
}
size_t
@ -650,6 +661,8 @@ void
CycleCollectedJSRuntime::TraverseZone(JS::Zone* aZone,
nsCycleCollectionTraversalCallback& aCb)
{
MOZ_ASSERT(mJSRuntime);
/*
* We treat the zone as being gray. We handle non-gray GCthings in the
* zone by not reporting their children to the CC. The black-gray invariant
@ -963,6 +976,8 @@ mozilla::TraceScriptHolder(nsISupports* aHolder, JSTracer* aTracer)
void
CycleCollectedJSRuntime::TraceNativeGrayRoots(JSTracer* aTracer)
{
MOZ_ASSERT(mJSRuntime);
// NB: This is here just to preserve the existing XPConnect order. I doubt it
// would hurt to do this after the JS holders.
TraceAdditionalNativeGrayRoots(aTracer);
@ -977,6 +992,7 @@ CycleCollectedJSRuntime::TraceNativeGrayRoots(JSTracer* aTracer)
void
CycleCollectedJSRuntime::AddJSHolder(void* aHolder, nsScriptObjectTracer* aTracer)
{
MOZ_ASSERT(mJSRuntime);
mJSHolders.Put(aHolder, aTracer);
}
@ -1027,6 +1043,8 @@ struct ClearJSHolder : TraceCallbacks
void
CycleCollectedJSRuntime::RemoveJSHolder(void* aHolder)
{
MOZ_ASSERT(mJSRuntime);
nsScriptObjectTracer* tracer = mJSHolders.Get(aHolder);
if (!tracer) {
return;
@ -1039,6 +1057,7 @@ CycleCollectedJSRuntime::RemoveJSHolder(void* aHolder)
bool
CycleCollectedJSRuntime::IsJSHolder(void* aHolder)
{
MOZ_ASSERT(mJSRuntime);
return mJSHolders.Get(aHolder, nullptr);
}
@ -1051,6 +1070,8 @@ AssertNoGcThing(JS::GCCellPtr aGCThing, const char* aName, void* aClosure)
void
CycleCollectedJSRuntime::AssertNoObjectsToTrace(void* aPossibleJSHolder)
{
MOZ_ASSERT(mJSRuntime);
nsScriptObjectTracer* tracer = mJSHolders.Get(aPossibleJSHolder);
if (tracer) {
tracer->Trace(aPossibleJSHolder, TraceCallbackFunc(AssertNoGcThing), nullptr);
@ -1061,6 +1082,8 @@ CycleCollectedJSRuntime::AssertNoObjectsToTrace(void* aPossibleJSHolder)
already_AddRefed<nsIException>
CycleCollectedJSRuntime::GetPendingException() const
{
MOZ_ASSERT(mJSRuntime);
nsCOMPtr<nsIException> out = mPendingException;
return out.forget();
}
@ -1068,30 +1091,36 @@ CycleCollectedJSRuntime::GetPendingException() const
void
CycleCollectedJSRuntime::SetPendingException(nsIException* aException)
{
MOZ_ASSERT(mJSRuntime);
mPendingException = aException;
}
std::queue<nsCOMPtr<nsIRunnable>>&
CycleCollectedJSRuntime::GetPromiseMicroTaskQueue()
{
MOZ_ASSERT(mJSRuntime);
return mPromiseMicroTaskQueue;
}
nsCycleCollectionParticipant*
CycleCollectedJSRuntime::GCThingParticipant()
{
MOZ_ASSERT(mJSRuntime);
return &mGCThingCycleCollectorGlobal;
}
nsCycleCollectionParticipant*
CycleCollectedJSRuntime::ZoneParticipant()
{
MOZ_ASSERT(mJSRuntime);
return &mJSZoneCycleCollectorGlobal;
}
nsresult
CycleCollectedJSRuntime::TraverseRoots(nsCycleCollectionNoteRootCallback& aCb)
{
MOZ_ASSERT(mJSRuntime);
TraverseNativeRoots(aCb);
NoteWeakMapsTracer trc(mJSRuntime, aCb);
@ -1108,6 +1137,8 @@ CycleCollectedJSRuntime::TraverseRoots(nsCycleCollectionNoteRootCallback& aCb)
bool
CycleCollectedJSRuntime::UsefulToMergeZones() const
{
MOZ_ASSERT(mJSRuntime);
if (!NS_IsMainThread()) {
return false;
}
@ -1137,6 +1168,7 @@ CycleCollectedJSRuntime::UsefulToMergeZones() const
void
CycleCollectedJSRuntime::FixWeakMappingGrayBits() const
{
MOZ_ASSERT(mJSRuntime);
MOZ_ASSERT(!JS::IsIncrementalGCInProgress(mJSRuntime),
"Don't call FixWeakMappingGrayBits during a GC.");
FixWeakMappingGrayBitsTracer fixer(mJSRuntime);
@ -1146,12 +1178,15 @@ CycleCollectedJSRuntime::FixWeakMappingGrayBits() const
bool
CycleCollectedJSRuntime::AreGCGrayBitsValid() const
{
MOZ_ASSERT(mJSRuntime);
return js::AreGCGrayBitsValid(mJSRuntime);
}
void
CycleCollectedJSRuntime::GarbageCollect(uint32_t aReason) const
{
MOZ_ASSERT(mJSRuntime);
MOZ_ASSERT(aReason < JS::gcreason::NUM_REASONS);
JS::gcreason::Reason gcreason = static_cast<JS::gcreason::Reason>(aReason);
@ -1162,6 +1197,8 @@ CycleCollectedJSRuntime::GarbageCollect(uint32_t aReason) const
void
CycleCollectedJSRuntime::JSObjectsTenured()
{
MOZ_ASSERT(mJSRuntime);
for (auto iter = mNurseryObjects.Iter(); !iter.Done(); iter.Next()) {
nsWrapperCache* cache = iter.Get();
JSObject* wrapper = cache->GetWrapperPreserveColor();
@ -1186,6 +1223,7 @@ for (auto iter = mPreservedNurseryObjects.Iter(); !iter.Done(); iter.Next()) {
void
CycleCollectedJSRuntime::NurseryWrapperAdded(nsWrapperCache* aCache)
{
MOZ_ASSERT(mJSRuntime);
MOZ_ASSERT(aCache);
MOZ_ASSERT(aCache->GetWrapperPreserveColor());
MOZ_ASSERT(!JS::ObjectIsTenured(aCache->GetWrapperPreserveColor()));
@ -1195,6 +1233,8 @@ CycleCollectedJSRuntime::NurseryWrapperAdded(nsWrapperCache* aCache)
void
CycleCollectedJSRuntime::NurseryWrapperPreserved(JSObject* aWrapper)
{
MOZ_ASSERT(mJSRuntime);
mPreservedNurseryObjects.InfallibleAppend(
JS::PersistentRooted<JSObject*>(mJSRuntime, aWrapper));
}
@ -1204,6 +1244,8 @@ CycleCollectedJSRuntime::DeferredFinalize(DeferredFinalizeAppendFunction aAppend
DeferredFinalizeFunction aFunc,
void* aThing)
{
MOZ_ASSERT(mJSRuntime);
void* thingArray = nullptr;
bool hadThingArray = mDeferredFinalizerTable.Get(aFunc, &thingArray);
@ -1216,6 +1258,8 @@ CycleCollectedJSRuntime::DeferredFinalize(DeferredFinalizeAppendFunction aAppend
void
CycleCollectedJSRuntime::DeferredFinalize(nsISupports* aSupports)
{
MOZ_ASSERT(mJSRuntime);
typedef DeferredFinalizerImpl<nsISupports> Impl;
DeferredFinalize(Impl::AppendDeferredFinalizePointer, Impl::DeferredFinalize,
aSupports);
@ -1224,12 +1268,14 @@ CycleCollectedJSRuntime::DeferredFinalize(nsISupports* aSupports)
void
CycleCollectedJSRuntime::DumpJSHeap(FILE* aFile)
{
MOZ_ASSERT(mJSRuntime);
js::DumpHeap(Runtime(), aFile, js::CollectNurseryBeforeDump);
}
void
CycleCollectedJSRuntime::ProcessStableStateQueue()
{
MOZ_ASSERT(mJSRuntime);
MOZ_RELEASE_ASSERT(!mDoingStableStates);
mDoingStableStates = true;
@ -1245,6 +1291,7 @@ CycleCollectedJSRuntime::ProcessStableStateQueue()
void
CycleCollectedJSRuntime::ProcessMetastableStateQueue(uint32_t aRecursionDepth)
{
MOZ_ASSERT(mJSRuntime);
MOZ_RELEASE_ASSERT(!mDoingStableStates);
mDoingStableStates = true;
@ -1275,6 +1322,8 @@ CycleCollectedJSRuntime::ProcessMetastableStateQueue(uint32_t aRecursionDepth)
void
CycleCollectedJSRuntime::AfterProcessTask(uint32_t aRecursionDepth)
{
MOZ_ASSERT(mJSRuntime);
// See HTML 6.1.4.2 Processing model
// Execute any events that were waiting for a microtask to complete.
@ -1295,12 +1344,15 @@ CycleCollectedJSRuntime::AfterProcessTask(uint32_t aRecursionDepth)
void
CycleCollectedJSRuntime::AfterProcessMicrotask()
{
MOZ_ASSERT(mJSRuntime);
AfterProcessMicrotask(RecursionDepth());
}
void
CycleCollectedJSRuntime::AfterProcessMicrotask(uint32_t aRecursionDepth)
{
MOZ_ASSERT(mJSRuntime);
// Between microtasks, execute any events that were waiting for a microtask
// to complete.
ProcessMetastableStateQueue(aRecursionDepth);
@ -1322,6 +1374,8 @@ CycleCollectedJSRuntime::RunInStableState(already_AddRefed<nsIRunnable>&& aRunna
void
CycleCollectedJSRuntime::RunInMetastableState(already_AddRefed<nsIRunnable>&& aRunnable)
{
MOZ_ASSERT(mJSRuntime);
RunInMetastableStateData data;
data.mRunnable = aRunnable;
@ -1440,6 +1494,8 @@ IncrementalFinalizeRunnable::Run()
void
CycleCollectedJSRuntime::FinalizeDeferredThings(DeferredFinalizeType aType)
{
MOZ_ASSERT(mJSRuntime);
/*
* If the previous GC created a runnable to finalize objects
* incrementally, and if it hasn't finished yet, finish it now. We
@ -1478,6 +1534,8 @@ void
CycleCollectedJSRuntime::AnnotateAndSetOutOfMemory(OOMState* aStatePtr,
OOMState aNewState)
{
MOZ_ASSERT(mJSRuntime);
*aStatePtr = aNewState;
#ifdef MOZ_CRASHREPORTER
CrashReporter::AnnotateCrashReport(aStatePtr == &mOutOfMemoryState
@ -1494,6 +1552,8 @@ CycleCollectedJSRuntime::AnnotateAndSetOutOfMemory(OOMState* aStatePtr,
void
CycleCollectedJSRuntime::OnGC(JSGCStatus aStatus)
{
MOZ_ASSERT(mJSRuntime);
switch (aStatus) {
case JSGC_BEGIN:
nsCycleCollector_prepareForGarbageCollection();
@ -1523,6 +1583,8 @@ CycleCollectedJSRuntime::OnGC(JSGCStatus aStatus)
void
CycleCollectedJSRuntime::OnOutOfMemory()
{
MOZ_ASSERT(mJSRuntime);
AnnotateAndSetOutOfMemory(&mOutOfMemoryState, OOMState::Reporting);
CustomOutOfMemoryCallback();
AnnotateAndSetOutOfMemory(&mOutOfMemoryState, OOMState::Reported);
@ -1531,6 +1593,8 @@ CycleCollectedJSRuntime::OnOutOfMemory()
void
CycleCollectedJSRuntime::OnLargeAllocationFailure()
{
MOZ_ASSERT(mJSRuntime);
AnnotateAndSetOutOfMemory(&mLargeAllocationFailureState, OOMState::Reporting);
CustomLargeAllocationFailureCallback();
AnnotateAndSetOutOfMemory(&mLargeAllocationFailureState, OOMState::Reported);

View File

@ -134,11 +134,13 @@ class CycleCollectedJSRuntime
friend class JSZoneParticipant;
friend class IncrementalFinalizeRunnable;
protected:
CycleCollectedJSRuntime(JSRuntime* aParentRuntime,
uint32_t aMaxBytes,
uint32_t aMaxNurseryBytes);
CycleCollectedJSRuntime();
virtual ~CycleCollectedJSRuntime();
nsresult Initialize(JSRuntime* aParentRuntime,
uint32_t aMaxBytes,
uint32_t aMaxNurseryBytes);
size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
void UnmarkSkippableJSHolders();