зеркало из https://github.com/mozilla/gecko-dev.git
Bug 845545: Part 4 - Create a worker implementation of CycleCollectedJSRuntime. r=mccr8,bent
This commit is contained in:
Родитель
9908c2bd93
Коммит
b0e1508e38
|
@ -21,6 +21,7 @@
|
|||
#include "GeckoProfiler.h"
|
||||
#include "jsdbgapi.h"
|
||||
#include "jsfriendapi.h"
|
||||
#include "mozilla/CycleCollectedJSRuntime.h"
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
#include "mozilla/dom/EventTargetBinding.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
|
@ -28,6 +29,7 @@
|
|||
#include "mozilla/Util.h"
|
||||
#include <Navigator.h>
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsCycleCollector.h"
|
||||
#include "nsDOMJSUtils.h"
|
||||
#include "nsLayoutStatics.h"
|
||||
#include "nsNetUtil.h"
|
||||
|
@ -748,20 +750,11 @@ CTypesActivityCallback(JSContext* aCx,
|
|||
}
|
||||
|
||||
JSContext*
|
||||
CreateJSContextForWorker(WorkerPrivate* aWorkerPrivate)
|
||||
CreateJSContextForWorker(WorkerPrivate* aWorkerPrivate, JSRuntime* aRuntime)
|
||||
{
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
NS_ASSERTION(!aWorkerPrivate->GetJSContext(), "Already has a context!");
|
||||
|
||||
// The number passed here doesn't matter, we're about to change it in the call
|
||||
// to JS_SetGCParameter.
|
||||
JSRuntime* runtime =
|
||||
JS_NewRuntime(WORKER_DEFAULT_RUNTIME_HEAPSIZE, JS_NO_HELPER_THREADS);
|
||||
if (!runtime) {
|
||||
NS_WARNING("Could not create new runtime!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JSSettings settings;
|
||||
aWorkerPrivate->CopyJSSettings(settings);
|
||||
|
||||
|
@ -779,45 +772,44 @@ CreateJSContextForWorker(WorkerPrivate* aWorkerPrivate)
|
|||
const JSSettings::JSGCSetting& setting = gcSettings[index];
|
||||
if (setting.IsSet()) {
|
||||
NS_ASSERTION(setting.value, "Can't handle 0 values!");
|
||||
JS_SetGCParameter(runtime, setting.key, setting.value);
|
||||
JS_SetGCParameter(aRuntime, setting.key, setting.value);
|
||||
}
|
||||
}
|
||||
|
||||
JS_SetNativeStackQuota(runtime, WORKER_CONTEXT_NATIVE_STACK_LIMIT);
|
||||
JS_SetNativeStackQuota(aRuntime, WORKER_CONTEXT_NATIVE_STACK_LIMIT);
|
||||
|
||||
// Security policy:
|
||||
static JSSecurityCallbacks securityCallbacks = {
|
||||
NULL,
|
||||
ContentSecurityPolicyAllows
|
||||
};
|
||||
JS_SetSecurityCallbacks(runtime, &securityCallbacks);
|
||||
JS_SetSecurityCallbacks(aRuntime, &securityCallbacks);
|
||||
|
||||
// DOM helpers:
|
||||
static js::DOMCallbacks DOMCallbacks = {
|
||||
InstanceClassHasProtoAtDepth
|
||||
};
|
||||
SetDOMCallbacks(runtime, &DOMCallbacks);
|
||||
SetDOMCallbacks(aRuntime, &DOMCallbacks);
|
||||
|
||||
JSContext* workerCx = JS_NewContext(runtime, 0);
|
||||
JSContext* workerCx = JS_NewContext(aRuntime, 0);
|
||||
if (!workerCx) {
|
||||
JS_DestroyRuntime(runtime);
|
||||
NS_WARNING("Could not create new context!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JS_SetRuntimePrivate(runtime, aWorkerPrivate);
|
||||
JS_SetRuntimePrivate(aRuntime, aWorkerPrivate);
|
||||
|
||||
JS_SetErrorReporter(workerCx, ErrorReporter);
|
||||
|
||||
JS_SetOperationCallback(workerCx, OperationCallback);
|
||||
|
||||
js::SetCTypesActivityCallback(runtime, CTypesActivityCallback);
|
||||
js::SetCTypesActivityCallback(aRuntime, CTypesActivityCallback);
|
||||
|
||||
JS_SetOptions(workerCx,
|
||||
aWorkerPrivate->IsChromeWorker() ? settings.chrome.options :
|
||||
settings.content.options);
|
||||
|
||||
JS_SetJitHardening(runtime, settings.jitHardening);
|
||||
JS_SetJitHardening(aRuntime, settings.jitHardening);
|
||||
|
||||
#ifdef JS_GC_ZEAL
|
||||
JS_SetGCZeal(workerCx, settings.gcZeal, settings.gcZealFrequency);
|
||||
|
@ -826,6 +818,57 @@ CreateJSContextForWorker(WorkerPrivate* aWorkerPrivate)
|
|||
return workerCx;
|
||||
}
|
||||
|
||||
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(WorkerPrivate* aWorkerPrivate)
|
||||
: CycleCollectedJSRuntime(WORKER_DEFAULT_RUNTIME_HEAPSIZE,
|
||||
JS_NO_HELPER_THREADS)
|
||||
{
|
||||
// We need to ensure that a JSContext outlives the cycle collector, and
|
||||
// that the internal JSContext created by ctypes is not the last JSContext
|
||||
// to die. So we create an unused JSContext here and destroy it after
|
||||
// the cycle collector shuts down. Thus all cycles will be broken before
|
||||
// the last GC and all finalizers will be run.
|
||||
mLastJSContext = JS_NewContext(Runtime(), 0);
|
||||
MOZ_ASSERT(mLastJSContext);
|
||||
}
|
||||
|
||||
~WorkerJSRuntime()
|
||||
{
|
||||
// All JSContexts except mLastJSContext should be destroyed now. The
|
||||
// worker global will be unrooted and the shutdown cycle collection
|
||||
// should break all remaining cycles. Destroying mLastJSContext will run
|
||||
// the GC the final time and finalize any JSObjects that were participating
|
||||
// in cycles that were broken during CC shutdown.
|
||||
nsCycleCollector_shutdownThreads();
|
||||
nsCycleCollector_shutdown();
|
||||
JS_DestroyContext(mLastJSContext);
|
||||
mLastJSContext = nullptr;
|
||||
}
|
||||
|
||||
// Make this public for now. Ideally we'd hide the JSRuntime inside.
|
||||
JSRuntime*
|
||||
Runtime() const
|
||||
{
|
||||
return mozilla::CycleCollectedJSRuntime::Runtime();
|
||||
}
|
||||
|
||||
void
|
||||
DispatchDeferredDeletion(bool aContinuation) MOZ_OVERRIDE
|
||||
{
|
||||
MOZ_ASSERT(!aContinuation);
|
||||
|
||||
// Do it immediately, no need for asynchronous behavior here.
|
||||
nsCycleCollector_doDeferredDeletion();
|
||||
}
|
||||
|
||||
private:
|
||||
JSContext* mLastJSContext;
|
||||
};
|
||||
|
||||
class WorkerThreadRunnable : public nsRunnable
|
||||
{
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
|
@ -845,50 +888,45 @@ public:
|
|||
|
||||
workerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
JSContext* cx = CreateJSContextForWorker(workerPrivate);
|
||||
if (!cx) {
|
||||
// XXX need to fire an error at parent.
|
||||
NS_ERROR("Failed to create runtime and context!");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
JSRuntime* rt = JS_GetRuntime(cx);
|
||||
|
||||
char aLocal;
|
||||
profiler_register_thread("WebWorker", &aLocal);
|
||||
#ifdef MOZ_ENABLE_PROFILER_SPS
|
||||
if (PseudoStack* stack = mozilla_get_pseudo_stack())
|
||||
stack->sampleRuntime(rt);
|
||||
#endif
|
||||
|
||||
{
|
||||
JSAutoRequest ar(cx);
|
||||
workerPrivate->DoRunLoop(cx);
|
||||
}
|
||||
nsCycleCollector_startup(CCSingleThread);
|
||||
|
||||
// XXX Bug 666963 - CTypes can create another JSContext for use with
|
||||
// closures, and then it holds that context in a reserved slot on the CType
|
||||
// prototype object. We have to destroy that context before we can destroy
|
||||
// the runtime, and we also have to make sure that it isn't the last context
|
||||
// to be destroyed (otherwise it will assert). To accomplish this we create
|
||||
// an unused dummy context, destroy our real context, and then destroy the
|
||||
// dummy. Once this bug is resolved we can remove this nastiness and simply
|
||||
// call JS_DestroyContextNoGC on our context.
|
||||
JSContext* dummyCx = JS_NewContext(rt, 0);
|
||||
if (dummyCx) {
|
||||
JS_DestroyContext(cx);
|
||||
JS_DestroyContext(dummyCx);
|
||||
}
|
||||
else {
|
||||
NS_WARNING("Failed to create dummy context!");
|
||||
WorkerJSRuntime runtime(workerPrivate);
|
||||
JSRuntime* rt = runtime.Runtime();
|
||||
JSContext* cx = CreateJSContextForWorker(workerPrivate, rt);
|
||||
if (!cx) {
|
||||
// XXX need to fire an error at parent.
|
||||
NS_ERROR("Failed to create runtime and context!");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
char aLocal;
|
||||
profiler_register_thread("WebWorker", &aLocal);
|
||||
#ifdef MOZ_ENABLE_PROFILER_SPS
|
||||
if (PseudoStack* stack = mozilla_get_pseudo_stack())
|
||||
stack->sampleRuntime(rt);
|
||||
#endif
|
||||
|
||||
{
|
||||
JSAutoRequest ar(cx);
|
||||
workerPrivate->DoRunLoop(cx);
|
||||
}
|
||||
|
||||
// Destroy the main context. This will unroot the main worker global and
|
||||
// GC. This is not the last JSContext (WorkerJSRuntime maintains an
|
||||
// internal JSContext).
|
||||
JS_DestroyContext(cx);
|
||||
|
||||
// Now WorkerJSRuntime goes out of scope and its destructor will shut
|
||||
// down the cycle collector and destroy the final JSContext. This
|
||||
// breaks any remaining cycles and collects the C++ and JS objects
|
||||
// participating.
|
||||
}
|
||||
|
||||
#ifdef MOZ_ENABLE_PROFILER_SPS
|
||||
if (PseudoStack* stack = mozilla_get_pseudo_stack())
|
||||
stack->sampleRuntime(nullptr);
|
||||
#endif
|
||||
JS_DestroyRuntime(rt);
|
||||
|
||||
workerPrivate->ScheduleDeletion(false);
|
||||
profiler_unregister_thread();
|
||||
|
|
|
@ -670,10 +670,10 @@ public:
|
|||
|
||||
virtual bool
|
||||
DescribeCustomObjects(JSObject* aObject, js::Class* aClasp,
|
||||
char (&aName)[72]) const;
|
||||
char (&aName)[72]) const MOZ_OVERRIDE;
|
||||
virtual bool
|
||||
NoteCustomGCThingXPCOMChildren(js::Class* aClasp, JSObject* aObj,
|
||||
nsCycleCollectionTraversalCallback& aCb) const;
|
||||
nsCycleCollectionTraversalCallback& aCb) const MOZ_OVERRIDE;
|
||||
|
||||
/**
|
||||
* Infrastructure for classes that need to defer part of the finalization
|
||||
|
@ -725,9 +725,9 @@ public:
|
|||
return mStrings[index];
|
||||
}
|
||||
|
||||
void TraceNativeBlackRoots(JSTracer* trc);
|
||||
void TraceAdditionalNativeGrayRoots(JSTracer* aTracer);
|
||||
void TraverseAdditionalNativeRoots(nsCycleCollectionNoteRootCallback& cb);
|
||||
void TraceNativeBlackRoots(JSTracer* trc) MOZ_OVERRIDE;
|
||||
void TraceAdditionalNativeGrayRoots(JSTracer* aTracer) MOZ_OVERRIDE;
|
||||
void TraverseAdditionalNativeRoots(nsCycleCollectionNoteRootCallback& cb) MOZ_OVERRIDE;
|
||||
void UnmarkSkippableJSHolders();
|
||||
void PrepareForForgetSkippable() MOZ_OVERRIDE;
|
||||
void PrepareForCollection() MOZ_OVERRIDE;
|
||||
|
|
|
@ -979,6 +979,10 @@ CycleCollectedJSRuntime::BeginCycleCollection(nsCycleCollectionNoteRootCallback
|
|||
bool
|
||||
CycleCollectedJSRuntime::UsefulToMergeZones() const
|
||||
{
|
||||
if (!NS_IsMainThread()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JSContext* iter = nullptr;
|
||||
JSContext* cx;
|
||||
JSAutoRequest ar(nsContentUtils::GetSafeJSContext());
|
||||
|
|
|
@ -96,8 +96,8 @@ protected:
|
|||
size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
|
||||
void UnmarkSkippableJSHolders();
|
||||
|
||||
virtual void TraverseAdditionalNativeRoots(nsCycleCollectionNoteRootCallback& aCb) = 0;
|
||||
virtual void TraceAdditionalNativeGrayRoots(JSTracer* aTracer) = 0;
|
||||
virtual void TraverseAdditionalNativeRoots(nsCycleCollectionNoteRootCallback& aCb) {}
|
||||
virtual void TraceAdditionalNativeGrayRoots(JSTracer* aTracer) {}
|
||||
|
||||
virtual void CustomGCCallback(JSGCStatus aStatus) {}
|
||||
virtual bool CustomContextCallback(JSContext* aCx, unsigned aOperation)
|
||||
|
@ -113,7 +113,10 @@ private:
|
|||
|
||||
virtual bool
|
||||
DescribeCustomObjects(JSObject* aObject, js::Class* aClasp,
|
||||
char (&aName)[72]) const = 0;
|
||||
char (&aName)[72]) const
|
||||
{
|
||||
return false; // We did nothing.
|
||||
}
|
||||
|
||||
void
|
||||
NoteGCThingJSChildren(void* aThing, JSGCTraceKind aTraceKind,
|
||||
|
@ -125,8 +128,10 @@ private:
|
|||
|
||||
virtual bool
|
||||
NoteCustomGCThingXPCOMChildren(js::Class* aClasp, JSObject* aObj,
|
||||
nsCycleCollectionTraversalCallback& aCb) const = 0;
|
||||
|
||||
nsCycleCollectionTraversalCallback& aCb) const
|
||||
{
|
||||
return false; // We did nothing.
|
||||
}
|
||||
|
||||
enum TraverseSelect {
|
||||
TRAVERSE_CPP,
|
||||
|
|
|
@ -903,76 +903,7 @@ enum ccType {
|
|||
ShutdownCC /* Shutdown CC, used for finding leaks. */
|
||||
};
|
||||
|
||||
class nsCycleCollector;
|
||||
|
||||
class nsCycleCollectorRunner : public nsRunnable
|
||||
{
|
||||
nsCycleCollector *mCollector;
|
||||
CCThreadingModel mModel;
|
||||
nsICycleCollectorListener *mListener;
|
||||
nsCOMPtr<nsIThread> mThread;
|
||||
Mutex mLock;
|
||||
CondVar mRequest;
|
||||
CondVar mReply;
|
||||
bool mRunning;
|
||||
bool mShutdown;
|
||||
bool mCollected;
|
||||
ccType mCCType;
|
||||
|
||||
public:
|
||||
nsCycleCollectorRunner(nsCycleCollector *collector,
|
||||
CCThreadingModel aModel)
|
||||
: mCollector(collector),
|
||||
mModel(aModel),
|
||||
mListener(nullptr),
|
||||
mLock("cycle collector lock"),
|
||||
mRequest(mLock, "cycle collector request condvar"),
|
||||
mReply(mLock, "cycle collector reply condvar"),
|
||||
mRunning(false),
|
||||
mShutdown(false),
|
||||
mCollected(false),
|
||||
mCCType(ScheduledCC)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
|
||||
}
|
||||
|
||||
NS_IMETHOD Run();
|
||||
|
||||
nsresult Init()
|
||||
{
|
||||
if (mModel == CCSingleThread)
|
||||
return NS_OK;
|
||||
|
||||
return NS_NewThread(getter_AddRefs(mThread), this);
|
||||
}
|
||||
|
||||
void Collect(ccType aCCType,
|
||||
nsCycleCollectorResults *aResults,
|
||||
nsICycleCollectorListener *aListener);
|
||||
|
||||
void Shutdown()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
if (!mThread)
|
||||
return;
|
||||
|
||||
MutexAutoLock autoLock(mLock);
|
||||
|
||||
mShutdown = true;
|
||||
|
||||
if (!mRunning)
|
||||
return;
|
||||
|
||||
mRunning = false;
|
||||
mRequest.Notify();
|
||||
mReply.Wait();
|
||||
|
||||
nsCOMPtr<nsIThread> thread;
|
||||
thread.swap(mThread);
|
||||
thread->Shutdown();
|
||||
}
|
||||
};
|
||||
class nsCycleCollectorRunner;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Top level structure for the cycle collector.
|
||||
|
@ -995,7 +926,7 @@ class nsCycleCollector
|
|||
|
||||
// Strong reference
|
||||
nsCycleCollectorRunner *mRunner;
|
||||
PRThread* mThread;
|
||||
nsIThread* mThread;
|
||||
|
||||
public:
|
||||
nsCycleCollectorParams mParams;
|
||||
|
@ -1099,6 +1030,75 @@ public:
|
|||
size_t *aPurpleBufferSize) const;
|
||||
};
|
||||
|
||||
class nsCycleCollectorRunner : public nsRunnable
|
||||
{
|
||||
nsCycleCollector *mCollector;
|
||||
CCThreadingModel mModel;
|
||||
nsICycleCollectorListener *mListener;
|
||||
nsCOMPtr<nsIThread> mThread;
|
||||
Mutex mLock;
|
||||
CondVar mRequest;
|
||||
CondVar mReply;
|
||||
bool mRunning;
|
||||
bool mShutdown;
|
||||
bool mCollected;
|
||||
ccType mCCType;
|
||||
|
||||
public:
|
||||
nsCycleCollectorRunner(nsCycleCollector *collector,
|
||||
CCThreadingModel aModel)
|
||||
: mCollector(collector),
|
||||
mModel(aModel),
|
||||
mListener(nullptr),
|
||||
mLock("cycle collector lock"),
|
||||
mRequest(mLock, "cycle collector request condvar"),
|
||||
mReply(mLock, "cycle collector reply condvar"),
|
||||
mRunning(false),
|
||||
mShutdown(false),
|
||||
mCollected(false),
|
||||
mCCType(ScheduledCC)
|
||||
{
|
||||
collector->CheckThreadSafety();
|
||||
}
|
||||
|
||||
NS_IMETHOD Run();
|
||||
|
||||
nsresult Init()
|
||||
{
|
||||
if (mModel == CCSingleThread)
|
||||
return NS_OK;
|
||||
|
||||
return NS_NewThread(getter_AddRefs(mThread), this);
|
||||
}
|
||||
|
||||
void Collect(ccType aCCType,
|
||||
nsCycleCollectorResults *aResults,
|
||||
nsICycleCollectorListener *aListener);
|
||||
|
||||
void Shutdown()
|
||||
{
|
||||
mCollector->CheckThreadSafety();
|
||||
|
||||
if (!mThread)
|
||||
return;
|
||||
|
||||
MutexAutoLock autoLock(mLock);
|
||||
|
||||
mShutdown = true;
|
||||
|
||||
if (!mRunning)
|
||||
return;
|
||||
|
||||
mRunning = false;
|
||||
mRequest.Notify();
|
||||
mReply.Wait();
|
||||
|
||||
nsCOMPtr<nsIThread> thread;
|
||||
thread.swap(mThread);
|
||||
thread->Shutdown();
|
||||
}
|
||||
};
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCycleCollectorRunner::Run()
|
||||
{
|
||||
|
@ -1148,7 +1148,7 @@ nsCycleCollectorRunner::Collect(ccType aCCType,
|
|||
nsCycleCollectorResults *aResults,
|
||||
nsICycleCollectorListener *aListener)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
|
||||
mCollector->CheckThreadSafety();
|
||||
|
||||
// On a WantAllTraces CC, force a synchronous global GC to prevent
|
||||
// hijinks from ForgetSkippable and compartmental GCs.
|
||||
|
@ -2680,7 +2680,7 @@ nsCycleCollector::nsCycleCollector(CCThreadingModel aModel) :
|
|||
mResults(nullptr),
|
||||
mJSRuntime(nullptr),
|
||||
mRunner(nullptr),
|
||||
mThread(PR_GetCurrentThread()),
|
||||
mThread(NS_GetCurrentThread()),
|
||||
mWhiteNodes(nullptr),
|
||||
mWhiteNodeCount(0),
|
||||
mVisitedRefCounted(0),
|
||||
|
@ -2785,7 +2785,10 @@ void
|
|||
nsCycleCollector::CheckThreadSafety()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
MOZ_ASSERT(mThread == PR_GetCurrentThread());
|
||||
nsIThread* currentThread = NS_GetCurrentThread();
|
||||
// XXXkhuey we can be called so late in shutdown that NS_GetCurrentThread
|
||||
// returns null (after the thread manager has shut down)
|
||||
MOZ_ASSERT(mThread == currentThread || !currentThread);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -2798,8 +2801,7 @@ nsCycleCollector::CheckThreadSafety()
|
|||
void
|
||||
nsCycleCollector::FixGrayBits(bool aForceGC)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(),
|
||||
"nsCycleCollector::FixGrayBits() must be called on the main thread.");
|
||||
CheckThreadSafety();
|
||||
|
||||
if (!mJSRuntime)
|
||||
return;
|
||||
|
|
Загрузка…
Ссылка в новой задаче