Bug 845545: Part 4 - Create a worker implementation of CycleCollectedJSRuntime. r=mccr8,bent

This commit is contained in:
Kyle Huey 2013-08-03 16:55:40 -07:00
Родитель 9908c2bd93
Коммит b0e1508e38
5 изменённых файлов: 189 добавлений и 140 удалений

Просмотреть файл

@ -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;