mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-21 09:49:14 +00:00
Bug 914762 - Switch Workers to use a normal nsThread event loop, r=mrbkap.
--HG-- extra : transplant_source : /%93%60%CC%3A%16%B16%A1%5Bmx%9F%FA%7B%10JD%F0%E7
This commit is contained in:
parent
2e75b22c85
commit
357b12898a
@ -15,6 +15,7 @@
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "WorkerPrivate.h"
|
||||
#include "WorkerRunnable.h"
|
||||
#include "nsJSPrincipals.h"
|
||||
#include "nsJSUtils.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
@ -60,8 +61,7 @@ class WorkerPromiseTask MOZ_FINAL : public WorkerRunnable
|
||||
{
|
||||
public:
|
||||
WorkerPromiseTask(WorkerPrivate* aWorkerPrivate, Promise* aPromise)
|
||||
: WorkerRunnable(aWorkerPrivate, WorkerThread,
|
||||
UnchangedBusyCount, SkipWhenClearing)
|
||||
: WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
|
||||
, mPromise(aPromise)
|
||||
{
|
||||
MOZ_ASSERT(aPromise);
|
||||
@ -169,8 +169,7 @@ public:
|
||||
Promise* aPromise,
|
||||
JS::Handle<JS::Value> aValue,
|
||||
Promise::PromiseState aState)
|
||||
: WorkerRunnable(aWorkerPrivate, WorkerThread,
|
||||
UnchangedBusyCount, SkipWhenClearing),
|
||||
: WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
|
||||
PromiseResolverMixin(aPromise, aValue, aState)
|
||||
{}
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
#include "SharedWorker.h"
|
||||
#include "WorkerPrivate.h"
|
||||
#include "WorkerRunnable.h"
|
||||
|
||||
using mozilla::dom::EventHandlerNonNull;
|
||||
using mozilla::dom::MessagePortBase;
|
||||
@ -28,13 +29,10 @@ class DelayedEventRunnable MOZ_FINAL : public WorkerRunnable
|
||||
|
||||
public:
|
||||
DelayedEventRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
Target aTarget,
|
||||
TargetAndBusyBehavior aBehavior,
|
||||
MessagePort* aMessagePort,
|
||||
nsTArray<nsCOMPtr<nsIDOMEvent>>& aEvents)
|
||||
: WorkerRunnable(aWorkerPrivate, aTarget,
|
||||
aTarget == WorkerThread ? ModifyBusyCount : UnchangedBusyCount,
|
||||
SkipWhenClearing),
|
||||
mMessagePort(aMessagePort)
|
||||
: WorkerRunnable(aWorkerPrivate, aBehavior), mMessagePort(aMessagePort)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aMessagePort);
|
||||
@ -109,16 +107,22 @@ MessagePort::Start()
|
||||
mStarted = true;
|
||||
|
||||
if (!mQueuedEvents.IsEmpty()) {
|
||||
WorkerRunnable::Target target = WorkerRunnable::WorkerThread;
|
||||
WorkerPrivate* workerPrivate = mWorkerPrivate;
|
||||
WorkerPrivate* workerPrivate;
|
||||
WorkerRunnable::TargetAndBusyBehavior behavior;
|
||||
|
||||
if (!workerPrivate) {
|
||||
target = WorkerRunnable::ParentThread;
|
||||
if (mWorkerPrivate) {
|
||||
workerPrivate = mWorkerPrivate;
|
||||
behavior = WorkerRunnable::WorkerThreadModifyBusyCount;
|
||||
}
|
||||
else {
|
||||
workerPrivate = mSharedWorker->GetWorkerPrivate();
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
|
||||
behavior = WorkerRunnable::ParentThreadUnchangedBusyCount;
|
||||
}
|
||||
|
||||
nsRefPtr<DelayedEventRunnable> runnable =
|
||||
new DelayedEventRunnable(workerPrivate, target, this, mQueuedEvents);
|
||||
new DelayedEventRunnable(workerPrivate, behavior, this, mQueuedEvents);
|
||||
runnable->Dispatch(nullptr);
|
||||
}
|
||||
}
|
||||
|
@ -43,6 +43,7 @@
|
||||
#include "nsLayoutStatics.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsThread.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsTraceRefcnt.h"
|
||||
#include "nsXPCOM.h"
|
||||
@ -50,13 +51,18 @@
|
||||
#include "OSFileConstants.h"
|
||||
#include "xpcpublic.h"
|
||||
|
||||
#include "SharedWorker.h"
|
||||
#include "WorkerPrivate.h"
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
#include "ipc/Nuwa.h"
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
#include "nsThreadManager.h"
|
||||
#endif
|
||||
|
||||
#include "SharedWorker.h"
|
||||
#include "WorkerPrivate.h"
|
||||
#include "WorkerRunnable.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
@ -593,6 +599,8 @@ void
|
||||
ErrorReporter(JSContext* aCx, const char* aMessage, JSErrorReport* aReport)
|
||||
{
|
||||
WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
|
||||
MOZ_ASSERT(worker);
|
||||
|
||||
return worker->ReportError(aCx, aMessage, aReport);
|
||||
}
|
||||
|
||||
@ -600,6 +608,7 @@ bool
|
||||
OperationCallback(JSContext* aCx)
|
||||
{
|
||||
WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
|
||||
MOZ_ASSERT(worker);
|
||||
|
||||
// Now is a good time to turn on profiling if it's pending.
|
||||
profiler_js_operation_callback();
|
||||
@ -607,98 +616,42 @@ OperationCallback(JSContext* aCx)
|
||||
return worker->OperationCallback(aCx);
|
||||
}
|
||||
|
||||
class LogViolationDetailsRunnable : public nsRunnable
|
||||
class LogViolationDetailsRunnable MOZ_FINAL : public nsRunnable
|
||||
{
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
|
||||
nsString mFileName;
|
||||
uint32_t mLineNum;
|
||||
uint32_t mSyncQueueKey;
|
||||
|
||||
private:
|
||||
class LogViolationDetailsResponseRunnable : public WorkerSyncRunnable
|
||||
{
|
||||
uint32_t mSyncQueueKey;
|
||||
|
||||
public:
|
||||
LogViolationDetailsResponseRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
uint32_t aSyncQueueKey)
|
||||
: WorkerSyncRunnable(aWorkerPrivate, aSyncQueueKey, false),
|
||||
mSyncQueueKey(aSyncQueueKey)
|
||||
{
|
||||
NS_ASSERTION(aWorkerPrivate, "Don't hand me a null WorkerPrivate!");
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
aWorkerPrivate->StopSyncLoop(mSyncQueueKey, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
|
||||
bool aDispatchResult)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
LogViolationDetailsRunnable(WorkerPrivate* aWorker,
|
||||
const nsString& aFileName,
|
||||
uint32_t aLineNum)
|
||||
: mWorkerPrivate(aWorker),
|
||||
mFileName(aFileName),
|
||||
mLineNum(aLineNum),
|
||||
mSyncQueueKey(0)
|
||||
: mWorkerPrivate(aWorker), mFileName(aFileName), mLineNum(aLineNum)
|
||||
{
|
||||
NS_ASSERTION(aWorker, "WorkerPrivate cannot be null");
|
||||
MOZ_ASSERT(aWorker);
|
||||
}
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
bool
|
||||
Dispatch(JSContext* aCx)
|
||||
{
|
||||
AutoSyncLoopHolder syncLoop(mWorkerPrivate);
|
||||
mSyncQueueKey = syncLoop.SyncQueueKey();
|
||||
|
||||
mSyncLoopTarget = syncLoop.EventTarget();
|
||||
MOZ_ASSERT(mSyncLoopTarget);
|
||||
|
||||
if (NS_FAILED(NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL))) {
|
||||
JS_ReportError(aCx, "Failed to dispatch to main thread!");
|
||||
return false;
|
||||
}
|
||||
|
||||
return syncLoop.RunAndForget(aCx);
|
||||
return syncLoop.Run();
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
Run()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
nsIContentSecurityPolicy* csp = mWorkerPrivate->GetCSP();
|
||||
if (csp) {
|
||||
NS_NAMED_LITERAL_STRING(scriptSample,
|
||||
"Call to eval() or related function blocked by CSP.");
|
||||
if (mWorkerPrivate->GetReportCSPViolations()) {
|
||||
csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_EVAL,
|
||||
mFileName, scriptSample, mLineNum, EmptyString());
|
||||
}
|
||||
}
|
||||
|
||||
nsRefPtr<LogViolationDetailsResponseRunnable> response =
|
||||
new LogViolationDetailsResponseRunnable(mWorkerPrivate, mSyncQueueKey);
|
||||
if (!response->Dispatch(nullptr)) {
|
||||
NS_WARNING("Failed to dispatch response!");
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
private:
|
||||
NS_DECL_NSIRUNNABLE
|
||||
};
|
||||
|
||||
bool
|
||||
@ -715,7 +668,7 @@ ContentSecurityPolicyAllows(JSContext* aCx)
|
||||
const char* file;
|
||||
if (JS_DescribeScriptedCaller(aCx, &script, &lineNum) &&
|
||||
(file = JS_GetScriptFilename(aCx, script))) {
|
||||
fileName.AssignASCII(file);
|
||||
fileName = NS_ConvertUTF8toUTF16(file);
|
||||
} else {
|
||||
JS_ReportPendingException(aCx);
|
||||
}
|
||||
@ -959,81 +912,158 @@ private:
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
};
|
||||
|
||||
class WorkerThreadRunnable : public nsRunnable
|
||||
class WorkerThreadPrimaryRunnable MOZ_FINAL : public nsRunnable
|
||||
{
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
nsRefPtr<RuntimeService::WorkerThread> mThread;
|
||||
|
||||
class FinishedRunnable MOZ_FINAL : public nsRunnable
|
||||
{
|
||||
nsRefPtr<RuntimeService::WorkerThread> mThread;
|
||||
|
||||
public:
|
||||
FinishedRunnable(already_AddRefed<RuntimeService::WorkerThread> aThread)
|
||||
: mThread(aThread)
|
||||
{
|
||||
MOZ_ASSERT(mThread);
|
||||
}
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
private:
|
||||
~FinishedRunnable()
|
||||
{ }
|
||||
|
||||
NS_DECL_NSIRUNNABLE
|
||||
};
|
||||
|
||||
public:
|
||||
WorkerThreadRunnable(WorkerPrivate* aWorkerPrivate)
|
||||
: mWorkerPrivate(aWorkerPrivate)
|
||||
WorkerThreadPrimaryRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
RuntimeService::WorkerThread* aThread)
|
||||
: mWorkerPrivate(aWorkerPrivate), mThread(aThread)
|
||||
{
|
||||
NS_ASSERTION(mWorkerPrivate, "This should never be null!");
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
MOZ_ASSERT(aThread);
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
Run()
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
private:
|
||||
~WorkerThreadPrimaryRunnable()
|
||||
{ }
|
||||
|
||||
NS_DECL_NSIRUNNABLE
|
||||
};
|
||||
|
||||
class WorkerTaskRunnable MOZ_FINAL : public WorkerRunnable
|
||||
{
|
||||
nsRefPtr<WorkerTask> mTask;
|
||||
|
||||
public:
|
||||
WorkerTaskRunnable(WorkerPrivate* aWorkerPrivate, WorkerTask* aTask)
|
||||
: WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount), mTask(aTask)
|
||||
{
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
if (IsNuwaProcess()) {
|
||||
NS_ASSERTION(NuwaMarkCurrentThread != nullptr,
|
||||
"NuwaMarkCurrentThread is undefined!");
|
||||
NuwaMarkCurrentThread(nullptr, nullptr);
|
||||
NuwaFreezeCurrentThread();
|
||||
}
|
||||
#endif
|
||||
WorkerPrivate* workerPrivate = mWorkerPrivate;
|
||||
mWorkerPrivate = nullptr;
|
||||
MOZ_ASSERT(aTask);
|
||||
}
|
||||
|
||||
workerPrivate->AssertIsOnWorkerThread();
|
||||
private:
|
||||
virtual bool
|
||||
PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
|
||||
{
|
||||
// May be called on any thread!
|
||||
return true;
|
||||
}
|
||||
|
||||
{
|
||||
nsCycleCollector_startup();
|
||||
virtual void
|
||||
PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
|
||||
bool aDispatchResult) MOZ_OVERRIDE
|
||||
{
|
||||
// May be called on any thread!
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
workerPrivate->ScheduleDeletion(false);
|
||||
profiler_unregister_thread();
|
||||
return NS_OK;
|
||||
virtual bool
|
||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
|
||||
{
|
||||
return mTask->RunTask(aCx);
|
||||
}
|
||||
};
|
||||
|
||||
} /* anonymous namespace */
|
||||
|
||||
class RuntimeService::WorkerThread MOZ_FINAL : public nsThread
|
||||
{
|
||||
class Observer MOZ_FINAL : public nsIThreadObserver
|
||||
{
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
|
||||
public:
|
||||
Observer(WorkerPrivate* aWorkerPrivate)
|
||||
: mWorkerPrivate(aWorkerPrivate)
|
||||
{
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
}
|
||||
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
private:
|
||||
~Observer()
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
}
|
||||
|
||||
NS_DECL_NSITHREADOBSERVER
|
||||
};
|
||||
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
nsRefPtr<Observer> mObserver;
|
||||
|
||||
#ifdef DEBUG
|
||||
// Protected by nsThread::mLock.
|
||||
bool mAcceptingNonWorkerRunnables;
|
||||
#endif
|
||||
|
||||
public:
|
||||
static already_AddRefed<WorkerThread>
|
||||
Create();
|
||||
|
||||
void
|
||||
SetWorker(WorkerPrivate* aWorkerPrivate);
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
NS_IMETHOD
|
||||
Dispatch(nsIRunnable* aRunnable, uint32_t aFlags) MOZ_OVERRIDE;
|
||||
|
||||
#ifdef DEBUG
|
||||
bool
|
||||
IsAcceptingNonWorkerRunnables()
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
return mAcceptingNonWorkerRunnables;
|
||||
}
|
||||
|
||||
void
|
||||
SetAcceptingNonWorkerRunnables(bool aAcceptingNonWorkerRunnables)
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
mAcceptingNonWorkerRunnables = aAcceptingNonWorkerRunnables;
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
WorkerThread()
|
||||
: nsThread(nsThread::NOT_MAIN_THREAD, WORKER_STACK_SIZE),
|
||||
mWorkerPrivate(nullptr)
|
||||
#ifdef DEBUG
|
||||
, mAcceptingNonWorkerRunnables(true)
|
||||
#endif
|
||||
{ }
|
||||
|
||||
~WorkerThread()
|
||||
{ }
|
||||
};
|
||||
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
||||
// Entry point for main thread non-window globals.
|
||||
@ -1113,71 +1143,64 @@ ResumeWorkersForWindow(nsPIDOMWindow* aWindow)
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class WorkerTaskRunnable : public WorkerRunnable
|
||||
WorkerCrossThreadDispatcher::WorkerCrossThreadDispatcher(
|
||||
WorkerPrivate* aWorkerPrivate)
|
||||
: mMutex("WorkerCrossThreadDispatcher::mMutex"),
|
||||
mWorkerPrivate(aWorkerPrivate)
|
||||
{
|
||||
public:
|
||||
WorkerTaskRunnable(WorkerPrivate* aPrivate, WorkerTask* aTask)
|
||||
: WorkerRunnable(aPrivate, WorkerThread, UnchangedBusyCount,
|
||||
SkipWhenClearing),
|
||||
mTask(aTask)
|
||||
{ }
|
||||
|
||||
virtual bool PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) {
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
|
||||
bool aDispatchResult)
|
||||
{ }
|
||||
|
||||
virtual bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate);
|
||||
|
||||
private:
|
||||
nsRefPtr<WorkerTask> mTask;
|
||||
};
|
||||
|
||||
bool
|
||||
WorkerTaskRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
return mTask->RunTask(aCx);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerCrossThreadDispatcher::PostTask(WorkerTask* aTask)
|
||||
{
|
||||
mozilla::MutexAutoLock lock(mMutex);
|
||||
if (!mPrivate) {
|
||||
MOZ_ASSERT(aTask);
|
||||
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
if (!mWorkerPrivate) {
|
||||
NS_WARNING("Posted a task to a WorkerCrossThreadDispatcher that is no "
|
||||
"longer accepting tasks!");
|
||||
return false;
|
||||
}
|
||||
|
||||
nsRefPtr<WorkerTaskRunnable> runnable = new WorkerTaskRunnable(mPrivate, aTask);
|
||||
runnable->Dispatch(nullptr);
|
||||
return true;
|
||||
nsRefPtr<WorkerTaskRunnable> runnable =
|
||||
new WorkerTaskRunnable(mWorkerPrivate, aTask);
|
||||
return runnable->Dispatch(nullptr);
|
||||
}
|
||||
|
||||
WorkerPrivate*
|
||||
GetWorkerPrivateFromContext(JSContext* aCx)
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
return static_cast<WorkerThreadRuntimePrivate*>(JS_GetRuntimePrivate(JS_GetRuntime(aCx)))->mWorkerPrivate;
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
MOZ_ASSERT(aCx);
|
||||
|
||||
JSRuntime* rt = JS_GetRuntime(aCx);
|
||||
MOZ_ASSERT(rt);
|
||||
|
||||
void* rtPrivate = JS_GetRuntimePrivate(rt);
|
||||
MOZ_ASSERT(rtPrivate);
|
||||
|
||||
return static_cast<WorkerThreadRuntimePrivate*>(rtPrivate)->mWorkerPrivate;
|
||||
}
|
||||
|
||||
WorkerPrivate*
|
||||
GetCurrentThreadWorkerPrivate()
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread(), "Wrong thread!");
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
CycleCollectedJSRuntime* ccrt = CycleCollectedJSRuntime::Get();
|
||||
if (!ccrt) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JSRuntime* rt = ccrt->Runtime();
|
||||
return static_cast<WorkerThreadRuntimePrivate*>(JS_GetRuntimePrivate(rt))->
|
||||
mWorkerPrivate;
|
||||
MOZ_ASSERT(rt);
|
||||
|
||||
void* rtPrivate = JS_GetRuntimePrivate(rt);
|
||||
MOZ_ASSERT(rtPrivate);
|
||||
|
||||
return static_cast<WorkerThreadRuntimePrivate*>(rtPrivate)->mWorkerPrivate;
|
||||
}
|
||||
|
||||
bool
|
||||
@ -1427,7 +1450,7 @@ RuntimeService::UnregisterWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
||||
}
|
||||
|
||||
if (!domainInfo->ActiveWorkerCount()) {
|
||||
NS_ASSERTION(domainInfo->mQueuedWorkers.IsEmpty(), "Huh?!");
|
||||
MOZ_ASSERT(domainInfo->mQueuedWorkers.IsEmpty());
|
||||
mDomainMap.Remove(domain);
|
||||
}
|
||||
}
|
||||
@ -1455,16 +1478,11 @@ RuntimeService::UnregisterWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
||||
nsPIDOMWindow* window = aWorkerPrivate->GetWindow();
|
||||
|
||||
nsTArray<WorkerPrivate*>* windowArray;
|
||||
if (!mWindowMap.Get(window, &windowArray)) {
|
||||
MOZ_ASSERT(false, "Don't have an entry for this window!");
|
||||
}
|
||||
MOZ_ALWAYS_TRUE(mWindowMap.Get(window, &windowArray));
|
||||
|
||||
if (!windowArray->RemoveElement(aWorkerPrivate)) {
|
||||
MOZ_ASSERT(false, "Worker wasn't in the correct window array!");
|
||||
}
|
||||
MOZ_ALWAYS_TRUE(windowArray->RemoveElement(aWorkerPrivate));
|
||||
|
||||
if (windowArray->IsEmpty()) {
|
||||
MOZ_ASSERT(!queuedWorker, "queuedWorker should be in this array!");
|
||||
mWindowMap.Remove(window);
|
||||
}
|
||||
}
|
||||
@ -1482,7 +1500,7 @@ RuntimeService::ScheduleWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
||||
return true;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIThread> thread;
|
||||
nsRefPtr<WorkerThread> thread;
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
if (!mIdleThreadArray.IsEmpty()) {
|
||||
@ -1493,35 +1511,36 @@ RuntimeService::ScheduleWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
||||
}
|
||||
|
||||
if (!thread) {
|
||||
if (NS_FAILED(NS_NewNamedThread("DOM Worker",
|
||||
getter_AddRefs(thread), nullptr,
|
||||
WORKER_STACK_SIZE))) {
|
||||
thread = WorkerThread::Create();
|
||||
if (!thread) {
|
||||
UnregisterWorker(aCx, aWorkerPrivate);
|
||||
JS_ReportError(aCx, "Could not create new thread!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ASSERT(thread->IsAcceptingNonWorkerRunnables());
|
||||
|
||||
int32_t priority = aWorkerPrivate->IsChromeWorker() ?
|
||||
nsISupportsPriority::PRIORITY_NORMAL :
|
||||
nsISupportsPriority::PRIORITY_LOW;
|
||||
|
||||
nsCOMPtr<nsISupportsPriority> threadPriority = do_QueryInterface(thread);
|
||||
if (!threadPriority || NS_FAILED(threadPriority->SetPriority(priority))) {
|
||||
if (NS_FAILED(thread->SetPriority(priority))) {
|
||||
NS_WARNING("Could not set the thread's priority!");
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
aWorkerPrivate->SetThread(thread);
|
||||
#endif
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable = new WorkerThreadRunnable(aWorkerPrivate);
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
new WorkerThreadPrimaryRunnable(aWorkerPrivate, thread);
|
||||
if (NS_FAILED(thread->Dispatch(runnable, NS_DISPATCH_NORMAL))) {
|
||||
UnregisterWorker(aCx, aWorkerPrivate);
|
||||
JS_ReportError(aCx, "Could not dispatch to thread!");
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
thread->SetAcceptingNonWorkerRunnables(false);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1541,7 +1560,7 @@ RuntimeService::ShutdownIdleThreads(nsITimer* aTimer, void* /* aClosure */)
|
||||
|
||||
TimeStamp nextExpiration;
|
||||
|
||||
nsAutoTArray<nsCOMPtr<nsIThread>, 20> expiredThreads;
|
||||
nsAutoTArray<nsRefPtr<WorkerThread>, 20> expiredThreads;
|
||||
{
|
||||
MutexAutoLock lock(runtime->mMutex);
|
||||
|
||||
@ -1553,7 +1572,7 @@ RuntimeService::ShutdownIdleThreads(nsITimer* aTimer, void* /* aClosure */)
|
||||
break;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIThread>* thread = expiredThreads.AppendElement();
|
||||
nsRefPtr<WorkerThread>* thread = expiredThreads.AppendElement();
|
||||
thread->swap(info.mThread);
|
||||
}
|
||||
|
||||
@ -1756,7 +1775,6 @@ RuntimeService::Shutdown()
|
||||
mDomainMap.EnumerateRead(AddAllTopLevelWorkersToArray, &workers);
|
||||
|
||||
if (!workers.IsEmpty()) {
|
||||
|
||||
// Cancel all top-level workers.
|
||||
{
|
||||
MutexAutoUnlock unlock(mMutex);
|
||||
@ -1803,7 +1821,7 @@ RuntimeService::Cleanup()
|
||||
|
||||
// Shut down any idle threads.
|
||||
if (!mIdleThreadArray.IsEmpty()) {
|
||||
nsAutoTArray<nsCOMPtr<nsIThread>, 20> idleThreads;
|
||||
nsAutoTArray<nsRefPtr<WorkerThread>, 20> idleThreads;
|
||||
|
||||
uint32_t idleThreadCount = mIdleThreadArray.Length();
|
||||
idleThreads.SetLength(idleThreadCount);
|
||||
@ -2186,10 +2204,14 @@ RuntimeService::ForgetSharedWorker(WorkerPrivate* aWorkerPrivate)
|
||||
}
|
||||
|
||||
void
|
||||
RuntimeService::NoteIdleThread(nsIThread* aThread)
|
||||
RuntimeService::NoteIdleThread(WorkerThread* aThread)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
NS_ASSERTION(aThread, "Null pointer!");
|
||||
MOZ_ASSERT(aThread);
|
||||
|
||||
#ifdef DEBUG
|
||||
aThread->SetAcceptingNonWorkerRunnables(true);
|
||||
#endif
|
||||
|
||||
static TimeDuration timeout =
|
||||
TimeDuration::FromSeconds(IDLE_THREAD_TIMEOUT_SEC);
|
||||
@ -2216,19 +2238,15 @@ RuntimeService::NoteIdleThread(nsIThread* aThread)
|
||||
|
||||
// Too many idle threads, just shut this one down.
|
||||
if (shutdown) {
|
||||
if (NS_FAILED(aThread->Shutdown())) {
|
||||
NS_WARNING("Failed to shutdown thread!");
|
||||
}
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aThread->Shutdown()));
|
||||
return;
|
||||
}
|
||||
|
||||
// Schedule timer.
|
||||
if (NS_FAILED(mIdleThreadTimer->
|
||||
InitWithFuncCallback(ShutdownIdleThreads, nullptr,
|
||||
IDLE_THREAD_TIMEOUT_SEC * 1000,
|
||||
nsITimer::TYPE_ONE_SHOT))) {
|
||||
NS_ERROR("Can't schedule timer!");
|
||||
}
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mIdleThreadTimer->InitWithFuncCallback(
|
||||
ShutdownIdleThreads, nullptr,
|
||||
IDLE_THREAD_TIMEOUT_SEC * 1000,
|
||||
nsITimer::TYPE_ONE_SHOT)));
|
||||
}
|
||||
|
||||
void
|
||||
@ -2354,3 +2372,272 @@ RuntimeService::JSVersionChanged(const char* /* aPrefName */, void* /* aClosure
|
||||
JS::CompartmentOptions& options = sDefaultJSSettings.content.compartmentOptions;
|
||||
options.setVersion(useLatest ? JSVERSION_LATEST : JSVERSION_DEFAULT);
|
||||
}
|
||||
|
||||
// static
|
||||
already_AddRefed<RuntimeService::WorkerThread>
|
||||
RuntimeService::WorkerThread::Create()
|
||||
{
|
||||
MOZ_ASSERT(nsThreadManager::get());
|
||||
|
||||
nsRefPtr<WorkerThread> thread = new WorkerThread();
|
||||
if (NS_FAILED(thread->Init())) {
|
||||
NS_WARNING("Failed to create new thread!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
NS_SetThreadName(thread, "DOM Worker");
|
||||
|
||||
return thread.forget();
|
||||
}
|
||||
|
||||
void
|
||||
RuntimeService::WorkerThread::SetWorker(WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
MOZ_ASSERT(PR_GetCurrentThread() == mThread);
|
||||
MOZ_ASSERT_IF(aWorkerPrivate, !mWorkerPrivate);
|
||||
MOZ_ASSERT_IF(!aWorkerPrivate, mWorkerPrivate);
|
||||
|
||||
// No need to lock here because mWorkerPrivate is only modified on mThread.
|
||||
|
||||
if (mWorkerPrivate) {
|
||||
MOZ_ASSERT(mObserver);
|
||||
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(RemoveObserver(mObserver)));
|
||||
|
||||
mObserver = nullptr;
|
||||
mWorkerPrivate->SetThread(nullptr);
|
||||
}
|
||||
|
||||
mWorkerPrivate = aWorkerPrivate;
|
||||
|
||||
if (mWorkerPrivate) {
|
||||
mWorkerPrivate->SetThread(this);
|
||||
|
||||
nsRefPtr<Observer> observer = new Observer(mWorkerPrivate);
|
||||
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(AddObserver(observer)));
|
||||
|
||||
mObserver.swap(observer);
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED0(RuntimeService::WorkerThread, nsThread)
|
||||
|
||||
NS_IMETHODIMP
|
||||
RuntimeService::WorkerThread::Dispatch(nsIRunnable* aRunnable, uint32_t aFlags)
|
||||
{
|
||||
// May be called on any thread!
|
||||
|
||||
#ifdef DEBUG
|
||||
if (PR_GetCurrentThread() == mThread) {
|
||||
MOZ_ASSERT(mWorkerPrivate);
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
}
|
||||
else if (aRunnable && !IsAcceptingNonWorkerRunnables()) {
|
||||
// Only enforce cancelable runnables after we've started the worker loop.
|
||||
nsCOMPtr<nsICancelableRunnable> cancelable = do_QueryInterface(aRunnable);
|
||||
MOZ_ASSERT(cancelable,
|
||||
"Should have been wrapped by the worker's event target!");
|
||||
}
|
||||
#endif
|
||||
|
||||
// Workers only support asynchronous dispatch for now.
|
||||
if (NS_WARN_IF(aFlags != NS_DISPATCH_NORMAL)) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
nsIRunnable* runnableToDispatch;
|
||||
nsRefPtr<WorkerRunnable> workerRunnable;
|
||||
|
||||
if (aRunnable && PR_GetCurrentThread() == mThread) {
|
||||
// No need to lock here because mWorkerPrivate is only modified on mThread.
|
||||
workerRunnable = mWorkerPrivate->MaybeWrapAsWorkerRunnable(aRunnable);
|
||||
runnableToDispatch = workerRunnable;
|
||||
}
|
||||
else {
|
||||
runnableToDispatch = aRunnable;
|
||||
}
|
||||
|
||||
nsresult rv = nsThread::Dispatch(runnableToDispatch, NS_DISPATCH_NORMAL);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS1(RuntimeService::WorkerThread::Observer, nsIThreadObserver)
|
||||
|
||||
NS_IMETHODIMP
|
||||
RuntimeService::WorkerThread::Observer::OnDispatchedEvent(
|
||||
nsIThreadInternal* /*aThread */)
|
||||
{
|
||||
MOZ_ASSUME_UNREACHABLE("This should never be called!");
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
RuntimeService::WorkerThread::Observer::OnProcessNextEvent(
|
||||
nsIThreadInternal* /* aThread */,
|
||||
bool aMayWait,
|
||||
uint32_t aRecursionDepth)
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
MOZ_ASSERT(!aMayWait);
|
||||
|
||||
mWorkerPrivate->OnProcessNextEvent(aRecursionDepth);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
RuntimeService::WorkerThread::Observer::AfterProcessNextEvent(
|
||||
nsIThreadInternal* /* aThread */,
|
||||
uint32_t aRecursionDepth,
|
||||
bool /* aEventWasProcessed */)
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
mWorkerPrivate->AfterProcessNextEvent(aRecursionDepth);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED0(LogViolationDetailsRunnable, nsRunnable)
|
||||
|
||||
NS_IMETHODIMP
|
||||
LogViolationDetailsRunnable::Run()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
nsIContentSecurityPolicy* csp = mWorkerPrivate->GetCSP();
|
||||
if (csp) {
|
||||
NS_NAMED_LITERAL_STRING(scriptSample,
|
||||
"Call to eval() or related function blocked by CSP.");
|
||||
if (mWorkerPrivate->GetReportCSPViolations()) {
|
||||
csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_EVAL,
|
||||
mFileName, scriptSample, mLineNum,
|
||||
EmptyString());
|
||||
}
|
||||
}
|
||||
|
||||
nsRefPtr<MainThreadStopSyncLoopRunnable> response =
|
||||
new MainThreadStopSyncLoopRunnable(mWorkerPrivate, mSyncLoopTarget.forget(),
|
||||
true);
|
||||
MOZ_ALWAYS_TRUE(response->Dispatch(nullptr));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED0(WorkerThreadPrimaryRunnable, nsRunnable)
|
||||
|
||||
NS_IMETHODIMP
|
||||
WorkerThreadPrimaryRunnable::Run()
|
||||
{
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
if (IsNuwaProcess()) {
|
||||
NS_ASSERTION(NuwaMarkCurrentThread != nullptr,
|
||||
"NuwaMarkCurrentThread is undefined!");
|
||||
NuwaMarkCurrentThread(nullptr, nullptr);
|
||||
NuwaFreezeCurrentThread();
|
||||
}
|
||||
#endif
|
||||
|
||||
char stackBaseGuess;
|
||||
|
||||
nsAutoCString threadName;
|
||||
threadName.AssignLiteral("WebWorker '");
|
||||
threadName.Append(NS_LossyConvertUTF16toASCII(mWorkerPrivate->ScriptURL()));
|
||||
threadName.Append('\'');
|
||||
|
||||
profiler_register_thread(threadName.get(), &stackBaseGuess);
|
||||
|
||||
mThread->SetWorker(mWorkerPrivate);
|
||||
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
{
|
||||
nsCycleCollector_startup();
|
||||
|
||||
WorkerJSRuntime runtime(mWorkerPrivate);
|
||||
JSRuntime* rt = runtime.Runtime();
|
||||
|
||||
JSContext* cx = CreateJSContextForWorker(mWorkerPrivate, rt);
|
||||
if (!cx) {
|
||||
// XXX need to fire an error at parent.
|
||||
NS_ERROR("Failed to create runtime and context!");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
{
|
||||
#ifdef MOZ_ENABLE_PROFILER_SPS
|
||||
PseudoStack* stack = mozilla_get_pseudo_stack();
|
||||
if (stack) {
|
||||
stack->sampleRuntime(rt);
|
||||
}
|
||||
#endif
|
||||
|
||||
{
|
||||
JSAutoRequest ar(cx);
|
||||
|
||||
mWorkerPrivate->DoRunLoop(cx);
|
||||
|
||||
JS_ReportPendingException(cx);
|
||||
}
|
||||
|
||||
#ifdef MOZ_ENABLE_PROFILER_SPS
|
||||
if (stack) {
|
||||
stack->sampleRuntime(nullptr);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// 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.
|
||||
}
|
||||
|
||||
mThread->SetWorker(nullptr);
|
||||
|
||||
mWorkerPrivate->ScheduleDeletion();
|
||||
|
||||
// It is no longer safe to touch mWorkerPrivate.
|
||||
mWorkerPrivate = nullptr;
|
||||
|
||||
// Now recycle this thread.
|
||||
nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
|
||||
MOZ_ASSERT(mainThread);
|
||||
|
||||
nsRefPtr<FinishedRunnable> finishedRunnable =
|
||||
new FinishedRunnable(mThread.forget());
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mainThread->Dispatch(finishedRunnable,
|
||||
NS_DISPATCH_NORMAL)));
|
||||
|
||||
profiler_unregister_thread();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED0(WorkerThreadPrimaryRunnable::FinishedRunnable,
|
||||
nsRunnable)
|
||||
|
||||
NS_IMETHODIMP
|
||||
WorkerThreadPrimaryRunnable::FinishedRunnable::Run()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
nsRefPtr<RuntimeService::WorkerThread> thread;
|
||||
mThread.swap(thread);
|
||||
|
||||
RuntimeService* rts = RuntimeService::GetService();
|
||||
if (rts) {
|
||||
rts->NoteIdleThread(thread);
|
||||
}
|
||||
else if (thread->ShutdownRequired()) {
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(thread->Shutdown()));
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -11,18 +11,13 @@
|
||||
|
||||
#include "nsIObserver.h"
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsHashKeys.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
class nsIRunnable;
|
||||
class nsIThread;
|
||||
class nsITimer;
|
||||
class nsPIDOMWindow;
|
||||
@ -34,6 +29,10 @@ class WorkerPrivate;
|
||||
|
||||
class RuntimeService MOZ_FINAL : public nsIObserver
|
||||
{
|
||||
public:
|
||||
class WorkerThread;
|
||||
|
||||
private:
|
||||
struct SharedWorkerInfo
|
||||
{
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
@ -68,7 +67,7 @@ class RuntimeService MOZ_FINAL : public nsIObserver
|
||||
|
||||
struct IdleThreadInfo
|
||||
{
|
||||
nsCOMPtr<nsIThread> mThread;
|
||||
nsRefPtr<WorkerThread> mThread;
|
||||
mozilla::TimeStamp mExpirationTime;
|
||||
};
|
||||
|
||||
@ -173,7 +172,7 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
NoteIdleThread(nsIThread* aThread);
|
||||
NoteIdleThread(WorkerThread* aThread);
|
||||
|
||||
static void
|
||||
GetDefaultJSSettings(JSSettings& aSettings)
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include "Principal.h"
|
||||
#include "WorkerFeature.h"
|
||||
#include "WorkerPrivate.h"
|
||||
#include "WorkerRunnable.h"
|
||||
|
||||
#define MAX_CONCURRENT_SCRIPTS 1000
|
||||
|
||||
@ -131,8 +132,6 @@ ChannelFromScriptURL(nsIPrincipal* principal,
|
||||
return rv;
|
||||
}
|
||||
|
||||
class ScriptLoaderRunnable;
|
||||
|
||||
struct ScriptLoadInfo
|
||||
{
|
||||
ScriptLoadInfo()
|
||||
@ -155,7 +154,9 @@ struct ScriptLoadInfo
|
||||
bool mExecutionResult;
|
||||
};
|
||||
|
||||
class ScriptExecutorRunnable : public WorkerSyncRunnable
|
||||
class ScriptLoaderRunnable;
|
||||
|
||||
class ScriptExecutorRunnable MOZ_FINAL : public MainThreadWorkerSyncRunnable
|
||||
{
|
||||
ScriptLoaderRunnable& mScriptLoader;
|
||||
uint32_t mFirstIndex;
|
||||
@ -163,38 +164,29 @@ class ScriptExecutorRunnable : public WorkerSyncRunnable
|
||||
|
||||
public:
|
||||
ScriptExecutorRunnable(ScriptLoaderRunnable& aScriptLoader,
|
||||
uint32_t aSyncQueueKey, uint32_t aFirstIndex,
|
||||
nsIEventTarget* aSyncLoopTarget, uint32_t aFirstIndex,
|
||||
uint32_t aLastIndex);
|
||||
|
||||
bool
|
||||
PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
return true;
|
||||
}
|
||||
private:
|
||||
~ScriptExecutorRunnable()
|
||||
{ }
|
||||
|
||||
void
|
||||
PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
|
||||
bool aDispatchResult)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
}
|
||||
virtual bool
|
||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE;
|
||||
|
||||
bool
|
||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate);
|
||||
|
||||
void
|
||||
PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult);
|
||||
virtual void
|
||||
PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult)
|
||||
MOZ_OVERRIDE;
|
||||
};
|
||||
|
||||
class ScriptLoaderRunnable : public WorkerFeature,
|
||||
public nsIRunnable,
|
||||
public nsIStreamLoaderObserver
|
||||
class ScriptLoaderRunnable MOZ_FINAL : public WorkerFeature,
|
||||
public nsIRunnable,
|
||||
public nsIStreamLoaderObserver
|
||||
{
|
||||
friend class ScriptExecutorRunnable;
|
||||
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
uint32_t mSyncQueueKey;
|
||||
nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
|
||||
nsTArray<ScriptLoadInfo> mLoadInfos;
|
||||
bool mIsWorkerScript;
|
||||
bool mCanceled;
|
||||
@ -204,21 +196,26 @@ public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
ScriptLoaderRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
uint32_t aSyncQueueKey,
|
||||
nsIEventTarget* aSyncLoopTarget,
|
||||
nsTArray<ScriptLoadInfo>& aLoadInfos,
|
||||
bool aIsWorkerScript)
|
||||
: mWorkerPrivate(aWorkerPrivate), mSyncQueueKey(aSyncQueueKey),
|
||||
: mWorkerPrivate(aWorkerPrivate), mSyncLoopTarget(aSyncLoopTarget),
|
||||
mIsWorkerScript(aIsWorkerScript), mCanceled(false),
|
||||
mCanceledMainThread(false)
|
||||
{
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
NS_ASSERTION(!aIsWorkerScript || aLoadInfos.Length() == 1, "Bad args!");
|
||||
MOZ_ASSERT(aSyncLoopTarget);
|
||||
MOZ_ASSERT_IF(aIsWorkerScript, aLoadInfos.Length() == 1);
|
||||
|
||||
mLoadInfos.SwapElements(aLoadInfos);
|
||||
}
|
||||
|
||||
private:
|
||||
~ScriptLoaderRunnable()
|
||||
{ }
|
||||
|
||||
NS_IMETHOD
|
||||
Run()
|
||||
Run() MOZ_OVERRIDE
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
@ -232,7 +229,7 @@ public:
|
||||
NS_IMETHOD
|
||||
OnStreamComplete(nsIStreamLoader* aLoader, nsISupports* aContext,
|
||||
nsresult aStatus, uint32_t aStringLen,
|
||||
const uint8_t* aString)
|
||||
const uint8_t* aString) MOZ_OVERRIDE
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
@ -256,8 +253,8 @@ public:
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
Notify(JSContext* aCx, Status aStatus)
|
||||
virtual bool
|
||||
Notify(JSContext* aCx, Status aStatus) MOZ_OVERRIDE
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
@ -587,9 +584,10 @@ public:
|
||||
|
||||
if (firstIndex != UINT32_MAX && lastIndex != UINT32_MAX) {
|
||||
nsRefPtr<ScriptExecutorRunnable> runnable =
|
||||
new ScriptExecutorRunnable(*this, mSyncQueueKey, firstIndex, lastIndex);
|
||||
new ScriptExecutorRunnable(*this, mSyncLoopTarget, firstIndex,
|
||||
lastIndex);
|
||||
if (!runnable->Dispatch(nullptr)) {
|
||||
NS_ERROR("This should never fail!");
|
||||
MOZ_ASSERT(false, "This should never fail!");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -597,45 +595,26 @@ public:
|
||||
|
||||
NS_IMPL_ISUPPORTS2(ScriptLoaderRunnable, nsIRunnable, nsIStreamLoaderObserver)
|
||||
|
||||
class StopSyncLoopRunnable MOZ_FINAL : public MainThreadSyncRunnable
|
||||
{
|
||||
public:
|
||||
StopSyncLoopRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
uint32_t aSyncQueueKey)
|
||||
: MainThreadSyncRunnable(aWorkerPrivate, SkipWhenClearing, aSyncQueueKey,
|
||||
false)
|
||||
{ }
|
||||
|
||||
bool
|
||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
|
||||
{
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
aWorkerPrivate->StopSyncLoop(mSyncQueueKey, true);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class ChannelGetterRunnable MOZ_FINAL : public nsRunnable
|
||||
{
|
||||
WorkerPrivate* mParentWorker;
|
||||
uint32_t mSyncQueueKey;
|
||||
nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
|
||||
const nsAString& mScriptURL;
|
||||
nsIChannel** mChannel;
|
||||
nsresult mResult;
|
||||
|
||||
public:
|
||||
ChannelGetterRunnable(WorkerPrivate* aParentWorker,
|
||||
uint32_t aSyncQueueKey,
|
||||
nsIEventTarget* aSyncLoopTarget,
|
||||
const nsAString& aScriptURL,
|
||||
nsIChannel** aChannel)
|
||||
: mParentWorker(aParentWorker), mSyncQueueKey(aSyncQueueKey),
|
||||
: mParentWorker(aParentWorker), mSyncLoopTarget(aSyncLoopTarget),
|
||||
mScriptURL(aScriptURL), mChannel(aChannel), mResult(NS_ERROR_FAILURE)
|
||||
{
|
||||
aParentWorker->AssertIsOnWorkerThread();
|
||||
MOZ_ASSERT(aSyncLoopTarget);
|
||||
}
|
||||
|
||||
virtual ~ChannelGetterRunnable() { }
|
||||
|
||||
NS_IMETHOD
|
||||
Run() MOZ_OVERRIDE
|
||||
{
|
||||
@ -660,8 +639,9 @@ public:
|
||||
channel.forget(mChannel);
|
||||
}
|
||||
|
||||
nsRefPtr<StopSyncLoopRunnable> runnable =
|
||||
new StopSyncLoopRunnable(mParentWorker, mSyncQueueKey);
|
||||
nsRefPtr<MainThreadStopSyncLoopRunnable> runnable =
|
||||
new MainThreadStopSyncLoopRunnable(mParentWorker,
|
||||
mSyncLoopTarget.forget(), true);
|
||||
if (!runnable->Dispatch(nullptr)) {
|
||||
NS_ERROR("This should never fail!");
|
||||
}
|
||||
@ -675,19 +655,21 @@ public:
|
||||
return mResult;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual ~ChannelGetterRunnable()
|
||||
{ }
|
||||
};
|
||||
|
||||
ScriptExecutorRunnable::ScriptExecutorRunnable(
|
||||
ScriptLoaderRunnable& aScriptLoader,
|
||||
uint32_t aSyncQueueKey,
|
||||
nsIEventTarget* aSyncLoopTarget,
|
||||
uint32_t aFirstIndex,
|
||||
uint32_t aLastIndex)
|
||||
: WorkerSyncRunnable(aScriptLoader.mWorkerPrivate, aSyncQueueKey),
|
||||
: MainThreadWorkerSyncRunnable(aScriptLoader.mWorkerPrivate, aSyncLoopTarget),
|
||||
mScriptLoader(aScriptLoader), mFirstIndex(aFirstIndex), mLastIndex(aLastIndex)
|
||||
{
|
||||
NS_ASSERTION(aFirstIndex <= aLastIndex, "Bad first index!");
|
||||
NS_ASSERTION(aLastIndex < aScriptLoader.mLoadInfos.Length(),
|
||||
"Bad last index!");
|
||||
MOZ_ASSERT(aFirstIndex <= aLastIndex);
|
||||
MOZ_ASSERT(aLastIndex < aScriptLoader.mLoadInfos.Length());
|
||||
}
|
||||
|
||||
bool
|
||||
@ -759,7 +741,7 @@ ScriptExecutorRunnable::PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
|
||||
}
|
||||
|
||||
aWorkerPrivate->RemoveFeature(aCx, &mScriptLoader);
|
||||
aWorkerPrivate->StopSyncLoop(mSyncQueueKey, result);
|
||||
aWorkerPrivate->StopSyncLoop(mSyncLoopTarget, result);
|
||||
}
|
||||
}
|
||||
|
||||
@ -773,7 +755,7 @@ LoadAllScripts(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
|
||||
AutoSyncLoopHolder syncLoop(aWorkerPrivate);
|
||||
|
||||
nsRefPtr<ScriptLoaderRunnable> loader =
|
||||
new ScriptLoaderRunnable(aWorkerPrivate, syncLoop.SyncQueueKey(),
|
||||
new ScriptLoaderRunnable(aWorkerPrivate, syncLoop.EventTarget(),
|
||||
aLoadInfos, aIsWorkerScript);
|
||||
|
||||
NS_ASSERTION(aLoadInfos.IsEmpty(), "Should have swapped!");
|
||||
@ -789,7 +771,7 @@ LoadAllScripts(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
|
||||
return false;
|
||||
}
|
||||
|
||||
return syncLoop.RunAndForget(aCx);
|
||||
return syncLoop.Run();
|
||||
}
|
||||
|
||||
} /* anonymous namespace */
|
||||
@ -832,15 +814,15 @@ ChannelFromScriptURLWorkerThread(JSContext* aCx,
|
||||
AutoSyncLoopHolder syncLoop(aParent);
|
||||
|
||||
nsRefPtr<ChannelGetterRunnable> getter =
|
||||
new ChannelGetterRunnable(aParent, syncLoop.SyncQueueKey(),
|
||||
aScriptURL, aChannel);
|
||||
new ChannelGetterRunnable(aParent, syncLoop.EventTarget(), aScriptURL,
|
||||
aChannel);
|
||||
|
||||
if (NS_FAILED(NS_DispatchToMainThread(getter, NS_DISPATCH_NORMAL))) {
|
||||
NS_ERROR("Failed to dispatch!");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (!syncLoop.RunAndForget(aCx)) {
|
||||
if (!syncLoop.Run()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
|
@ -4,24 +4,24 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "URL.h"
|
||||
#include "File.h"
|
||||
|
||||
#include "WorkerPrivate.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
#include "nsHostObjectProtocolHandler.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
|
||||
#include "nsIDocument.h"
|
||||
#include "nsIDOMFile.h"
|
||||
#include "nsIIOService.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
|
||||
#include "mozilla/dom/URL.h"
|
||||
#include "mozilla/dom/URLBinding.h"
|
||||
#include "mozilla/dom/URLSearchParams.h"
|
||||
#include "nsIIOService.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
#include "nsHostObjectProtocolHandler.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
#include "File.h"
|
||||
#include "WorkerPrivate.h"
|
||||
#include "WorkerRunnable.h"
|
||||
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
using mozilla::dom::GlobalObject;
|
||||
@ -67,43 +67,7 @@ class URLRunnable : public nsRunnable
|
||||
{
|
||||
protected:
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
uint32_t mSyncQueueKey;
|
||||
|
||||
private:
|
||||
class ResponseRunnable : public WorkerSyncRunnable
|
||||
{
|
||||
uint32_t mSyncQueueKey;
|
||||
|
||||
public:
|
||||
ResponseRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
uint32_t aSyncQueueKey)
|
||||
: WorkerSyncRunnable(aWorkerPrivate, aSyncQueueKey, false),
|
||||
mSyncQueueKey(aSyncQueueKey)
|
||||
{
|
||||
NS_ASSERTION(aWorkerPrivate, "Don't hand me a null WorkerPrivate!");
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
aWorkerPrivate->StopSyncLoop(mSyncQueueKey, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
|
||||
bool aDispatchResult)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
}
|
||||
};
|
||||
nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
|
||||
|
||||
protected:
|
||||
URLRunnable(WorkerPrivate* aWorkerPrivate)
|
||||
@ -117,15 +81,17 @@ public:
|
||||
Dispatch(JSContext* aCx)
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
AutoSyncLoopHolder syncLoop(mWorkerPrivate);
|
||||
mSyncQueueKey = syncLoop.SyncQueueKey();
|
||||
|
||||
mSyncLoopTarget = syncLoop.EventTarget();
|
||||
|
||||
if (NS_FAILED(NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL))) {
|
||||
JS_ReportError(aCx, "Failed to dispatch to main thread!");
|
||||
return false;
|
||||
}
|
||||
|
||||
return syncLoop.RunAndForget(aCx);
|
||||
return syncLoop.Run();
|
||||
}
|
||||
|
||||
private:
|
||||
@ -135,8 +101,10 @@ private:
|
||||
|
||||
MainThreadRun();
|
||||
|
||||
nsRefPtr<ResponseRunnable> response =
|
||||
new ResponseRunnable(mWorkerPrivate, mSyncQueueKey);
|
||||
nsRefPtr<MainThreadStopSyncLoopRunnable> response =
|
||||
new MainThreadStopSyncLoopRunnable(mWorkerPrivate,
|
||||
mSyncLoopTarget.forget(),
|
||||
true);
|
||||
if (!response->Dispatch(nullptr)) {
|
||||
NS_WARNING("Failed to dispatch response!");
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -9,25 +9,19 @@
|
||||
#include "Workers.h"
|
||||
|
||||
#include "nsIContentSecurityPolicy.h"
|
||||
#include "nsIRunnable.h"
|
||||
#include "nsIThread.h"
|
||||
#include "nsIThreadInternal.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/CondVar.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsDataHashtable.h"
|
||||
#include "nsDOMEventTargetHelper.h"
|
||||
#include "nsEventQueue.h"
|
||||
#include "nsHashKeys.h"
|
||||
#include "nsRefPtrHashtable.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsTPriorityQueue.h"
|
||||
#include "StructuredCloneTags.h"
|
||||
|
||||
#include "Queue.h"
|
||||
@ -36,8 +30,10 @@
|
||||
class JSAutoStructuredCloneBuffer;
|
||||
class nsIChannel;
|
||||
class nsIDocument;
|
||||
class nsIEventTarget;
|
||||
class nsIPrincipal;
|
||||
class nsIScriptContext;
|
||||
class nsIThread;
|
||||
class nsITimer;
|
||||
class nsIURI;
|
||||
|
||||
@ -51,142 +47,19 @@ class Function;
|
||||
}
|
||||
}
|
||||
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
||||
class MessagePort;
|
||||
class SharedWorker;
|
||||
class WorkerGlobalScope;
|
||||
class WorkerPrivate;
|
||||
|
||||
class WorkerRunnable : public nsIRunnable
|
||||
{
|
||||
public:
|
||||
enum Target { ParentThread, WorkerThread };
|
||||
enum BusyBehavior { ModifyBusyCount, UnchangedBusyCount };
|
||||
enum ClearingBehavior { SkipWhenClearing, RunWhenClearing };
|
||||
|
||||
protected:
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
Target mTarget;
|
||||
BusyBehavior mBusyBehavior;
|
||||
ClearingBehavior mClearingBehavior;
|
||||
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
bool
|
||||
Dispatch(JSContext* aCx);
|
||||
|
||||
static bool
|
||||
DispatchToMainThread(nsIRunnable*);
|
||||
|
||||
bool
|
||||
WantsToRunDuringClear()
|
||||
{
|
||||
return mClearingBehavior == RunWhenClearing;
|
||||
}
|
||||
|
||||
protected:
|
||||
WorkerRunnable(WorkerPrivate* aWorkerPrivate, Target aTarget,
|
||||
BusyBehavior aBusyBehavior,
|
||||
ClearingBehavior aClearingBehavior)
|
||||
#ifdef DEBUG
|
||||
;
|
||||
#else
|
||||
: mWorkerPrivate(aWorkerPrivate), mTarget(aTarget),
|
||||
mBusyBehavior(aBusyBehavior), mClearingBehavior(aClearingBehavior)
|
||||
{ }
|
||||
struct PRThread;
|
||||
#endif
|
||||
|
||||
virtual ~WorkerRunnable()
|
||||
{ }
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
||||
virtual bool
|
||||
PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate);
|
||||
|
||||
virtual void
|
||||
PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
|
||||
bool aDispatchResult);
|
||||
|
||||
virtual bool
|
||||
DispatchInternal();
|
||||
|
||||
virtual bool
|
||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) = 0;
|
||||
|
||||
virtual void
|
||||
PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult);
|
||||
|
||||
public:
|
||||
NS_DECL_NSIRUNNABLE
|
||||
};
|
||||
|
||||
class WorkerSyncRunnable : public WorkerRunnable
|
||||
{
|
||||
protected:
|
||||
uint32_t mSyncQueueKey;
|
||||
bool mBypassSyncQueue;
|
||||
|
||||
protected:
|
||||
friend class WorkerPrivate;
|
||||
|
||||
WorkerSyncRunnable(WorkerPrivate* aWorkerPrivate, uint32_t aSyncQueueKey,
|
||||
bool aBypassSyncQueue = false,
|
||||
ClearingBehavior aClearingBehavior = SkipWhenClearing)
|
||||
: WorkerRunnable(aWorkerPrivate, WorkerThread, UnchangedBusyCount,
|
||||
aClearingBehavior),
|
||||
mSyncQueueKey(aSyncQueueKey), mBypassSyncQueue(aBypassSyncQueue)
|
||||
{ }
|
||||
|
||||
virtual ~WorkerSyncRunnable()
|
||||
{ }
|
||||
|
||||
virtual bool
|
||||
DispatchInternal() MOZ_OVERRIDE;
|
||||
};
|
||||
|
||||
class MainThreadSyncRunnable : public WorkerSyncRunnable
|
||||
{
|
||||
public:
|
||||
MainThreadSyncRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
ClearingBehavior aClearingBehavior,
|
||||
uint32_t aSyncQueueKey,
|
||||
bool aBypassSyncEventQueue)
|
||||
: WorkerSyncRunnable(aWorkerPrivate, aSyncQueueKey, aBypassSyncEventQueue,
|
||||
aClearingBehavior)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
}
|
||||
|
||||
bool
|
||||
PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
|
||||
bool aDispatchResult) MOZ_OVERRIDE
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
}
|
||||
};
|
||||
|
||||
class WorkerControlRunnable : public WorkerRunnable
|
||||
{
|
||||
protected:
|
||||
WorkerControlRunnable(WorkerPrivate* aWorkerPrivate, Target aTarget,
|
||||
BusyBehavior aBusyBehavior)
|
||||
: WorkerRunnable(aWorkerPrivate, aTarget, aBusyBehavior, SkipWhenClearing)
|
||||
{ }
|
||||
|
||||
virtual ~WorkerControlRunnable()
|
||||
{ }
|
||||
|
||||
virtual bool
|
||||
DispatchInternal() MOZ_OVERRIDE;
|
||||
};
|
||||
class AutoSyncLoopHolder;
|
||||
class MessagePort;
|
||||
class SharedWorker;
|
||||
class WorkerControlRunnable;
|
||||
class WorkerGlobalScope;
|
||||
class WorkerPrivate;
|
||||
class WorkerRunnable;
|
||||
|
||||
// SharedMutex is a small wrapper around an (internal) reference-counted Mutex
|
||||
// object. It exists to avoid changing a lot of code to use Mutex* instead of
|
||||
@ -195,7 +68,7 @@ class SharedMutex
|
||||
{
|
||||
typedef mozilla::Mutex Mutex;
|
||||
|
||||
class RefCountedMutex : public Mutex
|
||||
class RefCountedMutex MOZ_FINAL : public Mutex
|
||||
{
|
||||
public:
|
||||
RefCountedMutex(const char* aName)
|
||||
@ -222,20 +95,17 @@ public:
|
||||
|
||||
operator Mutex&()
|
||||
{
|
||||
MOZ_ASSERT(mMutex);
|
||||
return *mMutex;
|
||||
}
|
||||
|
||||
operator const Mutex&() const
|
||||
{
|
||||
MOZ_ASSERT(mMutex);
|
||||
return *mMutex;
|
||||
}
|
||||
|
||||
void
|
||||
AssertCurrentThreadOwns() const
|
||||
{
|
||||
MOZ_ASSERT(mMutex);
|
||||
mMutex->AssertCurrentThreadOwns();
|
||||
}
|
||||
};
|
||||
@ -245,6 +115,10 @@ class WorkerPrivateParent : public nsDOMEventTargetHelper
|
||||
{
|
||||
class SynchronizeAndResumeRunnable;
|
||||
|
||||
protected:
|
||||
class EventTarget;
|
||||
friend class EventTarget;
|
||||
|
||||
public:
|
||||
struct LocationInfo
|
||||
{
|
||||
@ -312,6 +186,10 @@ protected:
|
||||
mozilla::CondVar mCondVar;
|
||||
mozilla::CondVar mMemoryReportCondVar;
|
||||
|
||||
// Protected by mMutex.
|
||||
nsRefPtr<EventTarget> mEventTarget;
|
||||
nsTArray<nsRefPtr<WorkerRunnable>> mPreStartRunnables;
|
||||
|
||||
private:
|
||||
WorkerPrivate* mParent;
|
||||
nsString mScriptURL;
|
||||
@ -322,7 +200,7 @@ private:
|
||||
LoadInfo mLoadInfo;
|
||||
|
||||
// Only used for top level workers.
|
||||
nsTArray<nsRefPtr<WorkerRunnable> > mQueuedRunnables;
|
||||
nsTArray<nsCOMPtr<nsIRunnable>> mQueuedRunnables;
|
||||
nsRevocableEventPtr<SynchronizeAndResumeRunnable> mSynchronizeRunnable;
|
||||
|
||||
// Only for ChromeWorkers without window and only touched on the main thread.
|
||||
@ -376,6 +254,9 @@ private:
|
||||
bool aToMessagePort, uint64_t aMessagePortSerial,
|
||||
ErrorResult& aRv);
|
||||
|
||||
nsresult
|
||||
DispatchPrivate(WorkerRunnable* aRunnable, nsIEventTarget* aSyncLoopTarget);
|
||||
|
||||
public:
|
||||
virtual JSObject*
|
||||
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
|
||||
@ -384,6 +265,21 @@ public:
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(WorkerPrivateParent,
|
||||
nsDOMEventTargetHelper)
|
||||
|
||||
nsresult
|
||||
Dispatch(WorkerRunnable* aRunnable)
|
||||
{
|
||||
return DispatchPrivate(aRunnable, nullptr);
|
||||
}
|
||||
|
||||
nsresult
|
||||
DispatchControlRunnable(WorkerControlRunnable* aWorkerControlRunnable);
|
||||
|
||||
already_AddRefed<WorkerRunnable>
|
||||
MaybeWrapAsWorkerRunnable(nsIRunnable* aRunnable);
|
||||
|
||||
already_AddRefed<nsIEventTarget>
|
||||
GetEventTarget();
|
||||
|
||||
// May be called on any thread...
|
||||
bool
|
||||
Start();
|
||||
@ -515,7 +411,7 @@ public:
|
||||
WorkerScriptLoaded();
|
||||
|
||||
void
|
||||
QueueRunnable(WorkerRunnable* aRunnable)
|
||||
QueueRunnable(nsIRunnable* aRunnable)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
mQueuedRunnables.AppendElement(aRunnable);
|
||||
@ -538,13 +434,10 @@ public:
|
||||
IsAcceptingEvents()
|
||||
{
|
||||
AssertIsOnParentThread();
|
||||
bool acceptingEvents;
|
||||
{
|
||||
mozilla::MutexAutoLock lock(mMutex);
|
||||
acceptingEvents = mParentStatus < Terminating;
|
||||
|
||||
MutexAutoLock lock(mMutex);
|
||||
return mParentStatus < Terminating;
|
||||
}
|
||||
return acceptingEvents;
|
||||
}
|
||||
|
||||
Status
|
||||
ParentStatus() const
|
||||
@ -775,53 +668,62 @@ class WorkerPrivate : public WorkerPrivateParent<WorkerPrivate>
|
||||
{
|
||||
friend class WorkerPrivateParent<WorkerPrivate>;
|
||||
typedef WorkerPrivateParent<WorkerPrivate> ParentType;
|
||||
friend class AutoSyncLoopHolder;
|
||||
|
||||
struct TimeoutInfo;
|
||||
|
||||
typedef Queue<WorkerRunnable*, 50> EventQueue;
|
||||
EventQueue mQueue;
|
||||
EventQueue mControlQueue;
|
||||
|
||||
struct SyncQueue
|
||||
{
|
||||
Queue<WorkerRunnable*, 10> mQueue;
|
||||
bool mComplete;
|
||||
bool mResult;
|
||||
|
||||
SyncQueue()
|
||||
: mComplete(false), mResult(false)
|
||||
{ }
|
||||
|
||||
~SyncQueue()
|
||||
{
|
||||
WorkerRunnable* event;
|
||||
while (mQueue.Pop(event)) {
|
||||
event->Release();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class MemoryReporter;
|
||||
friend class MemoryReporter;
|
||||
|
||||
nsTArray<nsAutoPtr<SyncQueue> > mSyncQueues;
|
||||
enum GCTimerMode
|
||||
{
|
||||
PeriodicTimer = 0,
|
||||
IdleTimer,
|
||||
NoTimer
|
||||
};
|
||||
|
||||
Queue<WorkerControlRunnable*, 4> mControlQueue;
|
||||
|
||||
// Touched on multiple threads, protected with mMutex.
|
||||
JSContext* mJSContext;
|
||||
nsRefPtr<WorkerCrossThreadDispatcher> mCrossThreadDispatcher;
|
||||
nsTArray<nsCOMPtr<nsIRunnable>> mUndispatchedRunnablesForSyncLoop;
|
||||
nsCOMPtr<nsIThread> mThread;
|
||||
|
||||
// Things touched on worker thread only.
|
||||
nsRefPtr<WorkerGlobalScope> mScope;
|
||||
nsTArray<ParentType*> mChildWorkers;
|
||||
nsTArray<WorkerFeature*> mFeatures;
|
||||
nsTArray<nsAutoPtr<TimeoutInfo> > mTimeouts;
|
||||
nsTArray<nsAutoPtr<TimeoutInfo>> mTimeouts;
|
||||
|
||||
struct SyncLoopInfo
|
||||
{
|
||||
SyncLoopInfo(EventTarget* aEventTarget);
|
||||
|
||||
nsRefPtr<EventTarget> mEventTarget;
|
||||
bool mCompleted;
|
||||
bool mResult;
|
||||
#ifdef DEBUG
|
||||
bool mHasRun;
|
||||
#endif
|
||||
};
|
||||
|
||||
// This is only modified on the worker thread, but in DEBUG builds
|
||||
// AssertValidSyncLoop function iterates it on other threads. Therefore
|
||||
// modifications are done with mMutex held *only* in DEBUG builds.
|
||||
nsTArray<nsAutoPtr<SyncLoopInfo>> mSyncLoopStack;
|
||||
|
||||
nsCOMPtr<nsITimer> mTimer;
|
||||
|
||||
nsCOMPtr<nsITimer> mGCTimer;
|
||||
nsCOMPtr<nsIEventTarget> mPeriodicGCTimerTarget;
|
||||
nsCOMPtr<nsIEventTarget> mIdleGCTimerTarget;
|
||||
|
||||
nsRefPtr<MemoryReporter> mMemoryReporter;
|
||||
|
||||
nsRefPtrHashtable<nsUint64HashKey, MessagePort> mWorkerPorts;
|
||||
|
||||
mozilla::TimeStamp mKillTime;
|
||||
TimeStamp mKillTime;
|
||||
uint32_t mErrorHandlerRecursionCount;
|
||||
uint32_t mNextTimeoutId;
|
||||
Status mStatus;
|
||||
@ -832,9 +734,11 @@ class WorkerPrivate : public WorkerPrivateParent<WorkerPrivate>
|
||||
bool mCloseHandlerFinished;
|
||||
bool mMemoryReporterRunning;
|
||||
bool mBlockedForMemoryReporter;
|
||||
bool mCancelAllPendingRunnables;
|
||||
bool mPeriodicGCTimerRunning;
|
||||
|
||||
#ifdef DEBUG
|
||||
nsCOMPtr<nsIThread> mThread;
|
||||
PRThread* mPRThread;
|
||||
#endif
|
||||
|
||||
bool mPreferences[WORKERPREF_COUNT];
|
||||
@ -867,27 +771,8 @@ public:
|
||||
bool
|
||||
OperationCallback(JSContext* aCx);
|
||||
|
||||
bool
|
||||
Dispatch(WorkerRunnable* aEvent)
|
||||
{
|
||||
return Dispatch(aEvent, &mQueue);
|
||||
}
|
||||
|
||||
bool
|
||||
Dispatch(WorkerSyncRunnable* aEvent)
|
||||
{
|
||||
if (aEvent->mBypassSyncQueue) {
|
||||
return Dispatch(aEvent, &mQueue);
|
||||
}
|
||||
|
||||
return DispatchToSyncQueue(aEvent);
|
||||
}
|
||||
|
||||
bool
|
||||
Dispatch(WorkerControlRunnable* aEvent)
|
||||
{
|
||||
return Dispatch(aEvent, &mControlQueue);
|
||||
}
|
||||
nsresult
|
||||
IsOnCurrentThread(bool* aIsOnCurrentThread);
|
||||
|
||||
bool
|
||||
CloseInternal(JSContext* aCx)
|
||||
@ -930,18 +815,6 @@ public:
|
||||
mFeatures.IsEmpty());
|
||||
}
|
||||
|
||||
uint32_t
|
||||
CreateNewSyncLoop();
|
||||
|
||||
bool
|
||||
RunSyncLoop(JSContext* aCx, uint32_t aSyncLoopKey);
|
||||
|
||||
void
|
||||
StopSyncLoop(uint32_t aSyncLoopKey, bool aSyncResult);
|
||||
|
||||
void
|
||||
DestroySyncLoop(uint32_t aSyncLoopKey);
|
||||
|
||||
void
|
||||
PostMessageToParent(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aMessage,
|
||||
@ -1008,7 +881,7 @@ public:
|
||||
UpdateJSWorkerMemoryParameterInternal(JSContext* aCx, JSGCParamKey key, uint32_t aValue);
|
||||
|
||||
void
|
||||
ScheduleDeletion(bool aWasPending);
|
||||
ScheduleDeletion();
|
||||
|
||||
bool
|
||||
BlockAndCollectRuntimeStats(JS::RuntimeStats* aRtStats);
|
||||
@ -1042,18 +915,14 @@ public:
|
||||
return mScope;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void
|
||||
AssertIsOnWorkerThread() const;
|
||||
SetThread(nsIThread* aThread);
|
||||
|
||||
void
|
||||
SetThread(nsIThread* aThread)
|
||||
{
|
||||
mThread = aThread;
|
||||
}
|
||||
#else
|
||||
void
|
||||
AssertIsOnWorkerThread() const
|
||||
#ifdef DEBUG
|
||||
;
|
||||
#else
|
||||
{ }
|
||||
#endif
|
||||
|
||||
@ -1113,20 +982,37 @@ public:
|
||||
return mPreferences[WORKERPREF_PROMISE];
|
||||
}
|
||||
|
||||
void
|
||||
StopSyncLoop(nsIEventTarget* aSyncLoopTarget, bool aResult);
|
||||
|
||||
bool
|
||||
AllPendingRunnablesShouldBeCanceled() const
|
||||
{
|
||||
return mCancelAllPendingRunnables;
|
||||
}
|
||||
|
||||
void
|
||||
OnProcessNextEvent(uint32_t aRecursionDepth);
|
||||
|
||||
void
|
||||
AfterProcessNextEvent(uint32_t aRecursionDepth);
|
||||
|
||||
void
|
||||
AssertValidSyncLoop(nsIEventTarget* aSyncLoopTarget)
|
||||
#ifdef DEBUG
|
||||
;
|
||||
#else
|
||||
{ }
|
||||
#endif
|
||||
|
||||
private:
|
||||
WorkerPrivate(JSContext* aCx, WorkerPrivate* aParent,
|
||||
const nsAString& aScriptURL, bool aIsChromeWorker,
|
||||
WorkerType aWorkerType, const nsAString& aSharedWorkerName,
|
||||
LoadInfo& aLoadInfo);
|
||||
|
||||
bool
|
||||
Dispatch(WorkerRunnable* aEvent, EventQueue* aQueue);
|
||||
|
||||
bool
|
||||
DispatchToSyncQueue(WorkerSyncRunnable* aEvent);
|
||||
|
||||
void
|
||||
ClearQueue(EventQueue* aQueue);
|
||||
ClearMainEventQueue();
|
||||
|
||||
bool
|
||||
MayContinueRunning()
|
||||
@ -1135,7 +1021,7 @@ private:
|
||||
|
||||
Status status;
|
||||
{
|
||||
mozilla::MutexAutoLock lock(mMutex);
|
||||
MutexAutoLock lock(mMutex);
|
||||
status = mStatus;
|
||||
}
|
||||
|
||||
@ -1157,22 +1043,15 @@ private:
|
||||
bool
|
||||
ScheduleKillCloseEventRunnable(JSContext* aCx);
|
||||
|
||||
void
|
||||
StopAcceptingEvents()
|
||||
bool
|
||||
ProcessAllControlRunnables()
|
||||
{
|
||||
AssertIsOnWorkerThread();
|
||||
|
||||
mozilla::MutexAutoLock lock(mMutex);
|
||||
|
||||
mStatus = Dead;
|
||||
mJSContext = nullptr;
|
||||
|
||||
ClearQueue(&mControlQueue);
|
||||
ClearQueue(&mQueue);
|
||||
MutexAutoLock lock(mMutex);
|
||||
return ProcessAllControlRunnablesLocked();
|
||||
}
|
||||
|
||||
bool
|
||||
ProcessAllControlRunnables();
|
||||
ProcessAllControlRunnablesLocked();
|
||||
|
||||
void
|
||||
EnableMemoryReporter();
|
||||
@ -1197,6 +1076,21 @@ private:
|
||||
AssertIsOnWorkerThread();
|
||||
memcpy(aPreferences, mPreferences, WORKERPREF_COUNT * sizeof(bool));
|
||||
}
|
||||
|
||||
already_AddRefed<nsIEventTarget>
|
||||
CreateNewSyncLoop();
|
||||
|
||||
bool
|
||||
RunCurrentSyncLoop();
|
||||
|
||||
void
|
||||
InitializeGCTimers();
|
||||
|
||||
void
|
||||
SetGCTimerMode(GCTimerMode aMode);
|
||||
|
||||
void
|
||||
ShutdownGCTimers();
|
||||
};
|
||||
|
||||
// This class is only used to trick the DOM bindings. We never create
|
||||
@ -1246,38 +1140,40 @@ ChromeWorkerStructuredCloneCallbacks(bool aMainRuntime);
|
||||
|
||||
class AutoSyncLoopHolder
|
||||
{
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
nsCOMPtr<nsIEventTarget> mTarget;
|
||||
|
||||
public:
|
||||
AutoSyncLoopHolder(WorkerPrivate* aWorkerPrivate)
|
||||
: mWorkerPrivate(aWorkerPrivate), mSyncLoopKey(UINT32_MAX)
|
||||
: mWorkerPrivate(aWorkerPrivate), mTarget(aWorkerPrivate->CreateNewSyncLoop())
|
||||
{
|
||||
mSyncLoopKey = mWorkerPrivate->CreateNewSyncLoop();
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
}
|
||||
|
||||
~AutoSyncLoopHolder()
|
||||
{
|
||||
if (mWorkerPrivate) {
|
||||
mWorkerPrivate->StopSyncLoop(mSyncLoopKey, false);
|
||||
mWorkerPrivate->DestroySyncLoop(mSyncLoopKey);
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
mWorkerPrivate->StopSyncLoop(mTarget, false);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
RunAndForget(JSContext* aCx)
|
||||
Run()
|
||||
{
|
||||
WorkerPrivate* workerPrivate = mWorkerPrivate;
|
||||
mWorkerPrivate = nullptr;
|
||||
return workerPrivate->RunSyncLoop(aCx, mSyncLoopKey);
|
||||
|
||||
workerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
return workerPrivate->RunCurrentSyncLoop();
|
||||
}
|
||||
|
||||
uint32_t
|
||||
SyncQueueKey() const
|
||||
nsIEventTarget*
|
||||
EventTarget() const
|
||||
{
|
||||
return mSyncLoopKey;
|
||||
return mTarget;
|
||||
}
|
||||
|
||||
private:
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
uint32_t mSyncLoopKey;
|
||||
};
|
||||
|
||||
END_WORKERS_NAMESPACE
|
||||
|
480
dom/workers/WorkerRunnable.cpp
Normal file
480
dom/workers/WorkerRunnable.cpp
Normal file
@ -0,0 +1,480 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "WorkerRunnable.h"
|
||||
|
||||
#include "nsIEventTarget.h"
|
||||
#include "nsIRunnable.h"
|
||||
|
||||
#include "js/RootingAPI.h"
|
||||
#include "js/Value.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
#include "WorkerPrivate.h"
|
||||
|
||||
USING_WORKERS_NAMESPACE
|
||||
|
||||
namespace {
|
||||
|
||||
const nsIID kWorkerRunnableIID = {
|
||||
0x320cc0b5, 0xef12, 0x4084, { 0x88, 0x6e, 0xca, 0x6a, 0x81, 0xe4, 0x1d, 0x68 }
|
||||
};
|
||||
|
||||
void
|
||||
MaybeReportMainThreadException(JSContext* aCx, bool aResult)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
if (aCx && !aResult) {
|
||||
JS_ReportPendingException(aCx);
|
||||
}
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
#ifdef DEBUG
|
||||
WorkerRunnable::WorkerRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
TargetAndBusyBehavior aBehavior)
|
||||
: mWorkerPrivate(aWorkerPrivate), mBehavior(aBehavior), mCanceled(0),
|
||||
mCallingCancelWithinRun(false)
|
||||
{
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool
|
||||
WorkerRunnable::PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
|
||||
switch (mBehavior) {
|
||||
case ParentThreadUnchangedBusyCount:
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
break;
|
||||
|
||||
case WorkerThreadModifyBusyCount:
|
||||
aWorkerPrivate->AssertIsOnParentThread();
|
||||
MOZ_ASSERT(aCx);
|
||||
break;
|
||||
|
||||
case WorkerThreadUnchangedBusyCount:
|
||||
aWorkerPrivate->AssertIsOnParentThread();
|
||||
break;
|
||||
|
||||
default:
|
||||
MOZ_ASSUME_UNREACHABLE("Unknown behavior!");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (mBehavior == WorkerThreadModifyBusyCount) {
|
||||
return aWorkerPrivate->ModifyBusyCount(aCx, true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerRunnable::Dispatch(JSContext* aCx)
|
||||
{
|
||||
bool ok;
|
||||
|
||||
if (!aCx) {
|
||||
ok = PreDispatch(nullptr, mWorkerPrivate);
|
||||
if (ok) {
|
||||
ok = DispatchInternal();
|
||||
}
|
||||
PostDispatch(nullptr, mWorkerPrivate, ok);
|
||||
return ok;
|
||||
}
|
||||
|
||||
JSAutoRequest ar(aCx);
|
||||
|
||||
JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
|
||||
|
||||
Maybe<JSAutoCompartment> ac;
|
||||
if (global) {
|
||||
ac.construct(aCx, global);
|
||||
}
|
||||
|
||||
ok = PreDispatch(aCx, mWorkerPrivate);
|
||||
|
||||
if (ok && !DispatchInternal()) {
|
||||
ok = false;
|
||||
}
|
||||
|
||||
PostDispatch(aCx, mWorkerPrivate, ok);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerRunnable::DispatchInternal()
|
||||
{
|
||||
if (mBehavior == WorkerThreadModifyBusyCount ||
|
||||
mBehavior == WorkerThreadUnchangedBusyCount) {
|
||||
return NS_SUCCEEDED(mWorkerPrivate->Dispatch(this));
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mBehavior == ParentThreadUnchangedBusyCount);
|
||||
|
||||
if (WorkerPrivate* parent = mWorkerPrivate->GetParent()) {
|
||||
return NS_SUCCEEDED(parent->Dispatch(this));
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
|
||||
MOZ_ASSERT(mainThread);
|
||||
|
||||
return NS_SUCCEEDED(mainThread->Dispatch(this, NS_DISPATCH_NORMAL));
|
||||
}
|
||||
|
||||
void
|
||||
WorkerRunnable::PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
|
||||
bool aDispatchResult)
|
||||
{
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
|
||||
#ifdef DEBUG
|
||||
switch (mBehavior) {
|
||||
case ParentThreadUnchangedBusyCount:
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
break;
|
||||
|
||||
case WorkerThreadModifyBusyCount:
|
||||
aWorkerPrivate->AssertIsOnParentThread();
|
||||
MOZ_ASSERT(aCx);
|
||||
break;
|
||||
|
||||
case WorkerThreadUnchangedBusyCount:
|
||||
aWorkerPrivate->AssertIsOnParentThread();
|
||||
break;
|
||||
|
||||
default:
|
||||
MOZ_ASSUME_UNREACHABLE("Unknown behavior!");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!aDispatchResult) {
|
||||
if (mBehavior == WorkerThreadModifyBusyCount) {
|
||||
aWorkerPrivate->ModifyBusyCount(aCx, false);
|
||||
}
|
||||
if (aCx) {
|
||||
JS_ReportPendingException(aCx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
WorkerRunnable::PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
|
||||
bool aRunResult)
|
||||
{
|
||||
MOZ_ASSERT(aCx);
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
|
||||
#ifdef DEBUG
|
||||
switch (mBehavior) {
|
||||
case ParentThreadUnchangedBusyCount:
|
||||
aWorkerPrivate->AssertIsOnParentThread();
|
||||
break;
|
||||
|
||||
case WorkerThreadModifyBusyCount:
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
break;
|
||||
|
||||
case WorkerThreadUnchangedBusyCount:
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
break;
|
||||
|
||||
default:
|
||||
MOZ_ASSUME_UNREACHABLE("Unknown behavior!");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (mBehavior == WorkerThreadModifyBusyCount) {
|
||||
if (!aWorkerPrivate->ModifyBusyCountFromWorker(aCx, false)) {
|
||||
aRunResult = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!aRunResult) {
|
||||
JS_ReportPendingException(aCx);
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
WorkerRunnable*
|
||||
WorkerRunnable::FromRunnable(nsIRunnable* aRunnable)
|
||||
{
|
||||
MOZ_ASSERT(aRunnable);
|
||||
|
||||
WorkerRunnable* runnable;
|
||||
nsresult rv = aRunnable->QueryInterface(kWorkerRunnableIID,
|
||||
reinterpret_cast<void**>(&runnable));
|
||||
if (NS_FAILED(rv)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(runnable);
|
||||
return runnable;
|
||||
}
|
||||
|
||||
NS_IMPL_ADDREF(WorkerRunnable)
|
||||
NS_IMPL_RELEASE(WorkerRunnable)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(WorkerRunnable)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIRunnable)
|
||||
NS_INTERFACE_MAP_ENTRY(nsICancelableRunnable)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
// kWorkerRunnableIID is special in that it does not AddRef its result.
|
||||
if (aIID.Equals(kWorkerRunnableIID)) {
|
||||
*aInstancePtr = this;
|
||||
return NS_OK;
|
||||
}
|
||||
else
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMETHODIMP
|
||||
WorkerRunnable::Run()
|
||||
{
|
||||
bool targetIsWorkerThread = mBehavior == WorkerThreadModifyBusyCount ||
|
||||
mBehavior == WorkerThreadUnchangedBusyCount;
|
||||
|
||||
#ifdef DEBUG
|
||||
MOZ_ASSERT_IF(mCallingCancelWithinRun, targetIsWorkerThread);
|
||||
if (targetIsWorkerThread) {
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
}
|
||||
else {
|
||||
MOZ_ASSERT(mBehavior == ParentThreadUnchangedBusyCount);
|
||||
mWorkerPrivate->AssertIsOnParentThread();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (IsCanceled() && !mCallingCancelWithinRun) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
JSContext* cx;
|
||||
nsRefPtr<WorkerPrivate> kungFuDeathGrip;
|
||||
nsCxPusher pusher;
|
||||
|
||||
if (targetIsWorkerThread) {
|
||||
if (mWorkerPrivate->AllPendingRunnablesShouldBeCanceled() &&
|
||||
!IsCanceled() &&
|
||||
!mCallingCancelWithinRun) {
|
||||
|
||||
// Prevent recursion.
|
||||
mCallingCancelWithinRun = true;
|
||||
|
||||
Cancel();
|
||||
|
||||
MOZ_ASSERT(mCallingCancelWithinRun);
|
||||
mCallingCancelWithinRun = false;
|
||||
|
||||
MOZ_ASSERT(IsCanceled(), "Subclass Cancel() didn't set IsCanceled()!");
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
cx = mWorkerPrivate->GetJSContext();
|
||||
MOZ_ASSERT(cx);
|
||||
}
|
||||
else {
|
||||
cx = mWorkerPrivate->ParentJSContext();
|
||||
MOZ_ASSERT(cx);
|
||||
|
||||
kungFuDeathGrip = mWorkerPrivate;
|
||||
|
||||
if (!mWorkerPrivate->GetParent()) {
|
||||
AssertIsOnMainThread();
|
||||
pusher.Push(cx);
|
||||
}
|
||||
}
|
||||
|
||||
JSAutoRequest ar(cx);
|
||||
|
||||
JS::Rooted<JSObject*> targetCompartmentObject(cx);
|
||||
if (targetIsWorkerThread) {
|
||||
targetCompartmentObject = JS::CurrentGlobalOrNull(cx);
|
||||
} else {
|
||||
targetCompartmentObject = mWorkerPrivate->GetWrapper();
|
||||
}
|
||||
|
||||
Maybe<JSAutoCompartment> ac;
|
||||
if (targetCompartmentObject) {
|
||||
ac.construct(cx, targetCompartmentObject);
|
||||
}
|
||||
|
||||
bool result = WorkerRun(cx, mWorkerPrivate);
|
||||
|
||||
// In the case of CompileScriptRunnnable, WorkerRun above can cause us to
|
||||
// lazily create a global, in which case we need to be in its compartment
|
||||
// when calling PostRun() below. Maybe<> this time...
|
||||
if (targetIsWorkerThread &&
|
||||
ac.empty() &&
|
||||
js::DefaultObjectForContextOrNull(cx)) {
|
||||
ac.construct(cx, js::DefaultObjectForContextOrNull(cx));
|
||||
}
|
||||
|
||||
PostRun(cx, mWorkerPrivate, result);
|
||||
|
||||
return result ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WorkerRunnable::Cancel()
|
||||
{
|
||||
uint32_t canceledCount = ++mCanceled;
|
||||
|
||||
MOZ_ASSERT(canceledCount, "Cancel() overflow!");
|
||||
|
||||
// The docs say that Cancel() should not be called more than once and that we
|
||||
// should throw NS_ERROR_UNEXPECTED if it is.
|
||||
return (canceledCount == 1) ? NS_OK : NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
WorkerSyncRunnable::WorkerSyncRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
nsIEventTarget* aSyncLoopTarget)
|
||||
: WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
|
||||
mSyncLoopTarget(aSyncLoopTarget)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
if (mSyncLoopTarget) {
|
||||
mWorkerPrivate->AssertValidSyncLoop(mSyncLoopTarget);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
WorkerSyncRunnable::WorkerSyncRunnable(
|
||||
WorkerPrivate* aWorkerPrivate,
|
||||
already_AddRefed<nsIEventTarget> aSyncLoopTarget)
|
||||
: WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
|
||||
mSyncLoopTarget(aSyncLoopTarget)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
if (mSyncLoopTarget) {
|
||||
mWorkerPrivate->AssertValidSyncLoop(mSyncLoopTarget);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
WorkerSyncRunnable::~WorkerSyncRunnable()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerSyncRunnable::DispatchInternal()
|
||||
{
|
||||
if (mSyncLoopTarget) {
|
||||
return NS_SUCCEEDED(mSyncLoopTarget->Dispatch(this, NS_DISPATCH_NORMAL));
|
||||
}
|
||||
|
||||
return WorkerRunnable::DispatchInternal();
|
||||
}
|
||||
|
||||
void
|
||||
MainThreadWorkerSyncRunnable::PostDispatch(JSContext* aCx,
|
||||
WorkerPrivate* aWorkerPrivate,
|
||||
bool aDispatchResult)
|
||||
{
|
||||
MaybeReportMainThreadException(aCx, aDispatchResult);
|
||||
}
|
||||
|
||||
StopSyncLoopRunnable::StopSyncLoopRunnable(
|
||||
WorkerPrivate* aWorkerPrivate,
|
||||
already_AddRefed<nsIEventTarget> aSyncLoopTarget,
|
||||
bool aResult)
|
||||
: WorkerSyncRunnable(aWorkerPrivate, aSyncLoopTarget), mResult(aResult)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
mWorkerPrivate->AssertValidSyncLoop(mSyncLoopTarget);
|
||||
#endif
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
StopSyncLoopRunnable::Cancel()
|
||||
{
|
||||
nsresult rv = Run();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
StopSyncLoopRunnable::WorkerRun(JSContext* aCx,
|
||||
WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
MOZ_ASSERT(mSyncLoopTarget);
|
||||
|
||||
nsCOMPtr<nsIEventTarget> syncLoopTarget;
|
||||
mSyncLoopTarget.swap(syncLoopTarget);
|
||||
|
||||
if (!mResult) {
|
||||
MaybeSetException(aCx);
|
||||
}
|
||||
|
||||
aWorkerPrivate->StopSyncLoop(syncLoopTarget, mResult);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
StopSyncLoopRunnable::DispatchInternal()
|
||||
{
|
||||
MOZ_ASSERT(mSyncLoopTarget);
|
||||
|
||||
return NS_SUCCEEDED(mSyncLoopTarget->Dispatch(this, NS_DISPATCH_NORMAL));
|
||||
}
|
||||
|
||||
void
|
||||
MainThreadStopSyncLoopRunnable::PostDispatch(JSContext* aCx,
|
||||
WorkerPrivate* aWorkerPrivate,
|
||||
bool aDispatchResult)
|
||||
{
|
||||
MaybeReportMainThreadException(aCx, aDispatchResult);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
WorkerControlRunnable::WorkerControlRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
TargetAndBusyBehavior aBehavior)
|
||||
: WorkerRunnable(aWorkerPrivate, aBehavior)
|
||||
{
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
MOZ_ASSERT(aBehavior == ParentThreadUnchangedBusyCount ||
|
||||
aBehavior == WorkerThreadUnchangedBusyCount,
|
||||
"WorkerControlRunnables should not modify the busy count");
|
||||
}
|
||||
#endif
|
||||
|
||||
bool
|
||||
WorkerControlRunnable::DispatchInternal()
|
||||
{
|
||||
if (mBehavior == WorkerThreadUnchangedBusyCount) {
|
||||
return NS_SUCCEEDED(mWorkerPrivate->DispatchControlRunnable(this));
|
||||
}
|
||||
|
||||
if (WorkerPrivate* parent = mWorkerPrivate->GetParent()) {
|
||||
return NS_SUCCEEDED(parent->DispatchControlRunnable(this));
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
|
||||
MOZ_ASSERT(mainThread);
|
||||
|
||||
return NS_SUCCEEDED(mainThread->Dispatch(this, NS_DISPATCH_NORMAL));
|
||||
}
|
||||
|
||||
void
|
||||
MainThreadWorkerControlRunnable::PostDispatch(JSContext* aCx,
|
||||
WorkerPrivate* aWorkerPrivate,
|
||||
bool aDispatchResult)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
if (aCx && !aDispatchResult) {
|
||||
JS_ReportPendingException(aCx);
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED0(WorkerControlRunnable, WorkerRunnable)
|
319
dom/workers/WorkerRunnable.h
Normal file
319
dom/workers/WorkerRunnable.h
Normal file
@ -0,0 +1,319 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_workers_workerrunnable_h__
|
||||
#define mozilla_dom_workers_workerrunnable_h__
|
||||
|
||||
#include "Workers.h"
|
||||
|
||||
#include "nsICancelableRunnable.h"
|
||||
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
|
||||
class JSContext;
|
||||
class nsIEventTarget;
|
||||
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
||||
class WorkerPrivate;
|
||||
|
||||
// Use this runnable to communicate from the worker to its parent or vice-versa.
|
||||
// The busy count must be taken into consideration and declared at construction
|
||||
// time.
|
||||
class WorkerRunnable : public nsICancelableRunnable
|
||||
{
|
||||
public:
|
||||
enum TargetAndBusyBehavior {
|
||||
// Target the main thread for top-level workers, otherwise target the
|
||||
// WorkerThread of the worker's parent. No change to the busy count.
|
||||
ParentThreadUnchangedBusyCount,
|
||||
|
||||
// Target the thread where the worker event loop runs. The busy count will
|
||||
// be incremented before dispatching and decremented (asynchronously) after
|
||||
// running.
|
||||
WorkerThreadModifyBusyCount,
|
||||
|
||||
// Target the thread where the worker event loop runs. The busy count will
|
||||
// not be modified in any way. Besides worker-internal runnables this is
|
||||
// almost always the wrong choice.
|
||||
WorkerThreadUnchangedBusyCount
|
||||
};
|
||||
|
||||
protected:
|
||||
// The WorkerPrivate that this runnable is associated with.
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
|
||||
// See above.
|
||||
TargetAndBusyBehavior mBehavior;
|
||||
|
||||
// It's unclear whether or not Cancel() is supposed to work when called on any
|
||||
// thread. To be safe we're using an atomic but it's likely overkill.
|
||||
Atomic<uint32_t> mCanceled;
|
||||
|
||||
private:
|
||||
// Whether or not Cancel() is currently being called from inside the Run()
|
||||
// method. Avoids infinite recursion when a subclass calls Run() from inside
|
||||
// Cancel(). Only checked and modified on the target thread.
|
||||
bool mCallingCancelWithinRun;
|
||||
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
// If you override Cancel() then you'll need to either call the base class
|
||||
// Cancel() method or override IsCanceled() so that the Run() method bails out
|
||||
// appropriately.
|
||||
NS_DECL_NSICANCELABLERUNNABLE
|
||||
|
||||
// Passing a JSContext here is required for the WorkerThreadModifyBusyCount
|
||||
// behavior. It also guarantees that any failure (false return) will throw an
|
||||
// exception on the given context. If a context is not passed then failures
|
||||
// must be dealt with by the caller.
|
||||
bool
|
||||
Dispatch(JSContext* aCx);
|
||||
|
||||
// See above note about Cancel().
|
||||
virtual bool
|
||||
IsCanceled() const
|
||||
{
|
||||
return mCanceled != 0;
|
||||
}
|
||||
|
||||
static WorkerRunnable*
|
||||
FromRunnable(nsIRunnable* aRunnable);
|
||||
|
||||
protected:
|
||||
WorkerRunnable(WorkerPrivate* aWorkerPrivate, TargetAndBusyBehavior aBehavior)
|
||||
#ifdef DEBUG
|
||||
;
|
||||
#else
|
||||
: mWorkerPrivate(aWorkerPrivate), mBehavior(aBehavior), mCanceled(0),
|
||||
mCallingCancelWithinRun(false)
|
||||
{ }
|
||||
#endif
|
||||
|
||||
// This class is reference counted.
|
||||
virtual ~WorkerRunnable()
|
||||
{ }
|
||||
|
||||
// By default asserts that Dispatch() is being called on the right thread
|
||||
// (ParentThread if |mTarget| is WorkerThread, or WorkerThread otherwise).
|
||||
// Also increments the busy count of |mWorkerPrivate| if targeting the
|
||||
// WorkerThread.
|
||||
virtual bool
|
||||
PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate);
|
||||
|
||||
// By default asserts that Dispatch() is being called on the right thread
|
||||
// (ParentThread if |mTarget| is WorkerThread, or WorkerThread otherwise).
|
||||
// Also reports any Dispatch() failures as an exception on |aCx|, and
|
||||
// busy count if targeting the WorkerThread and Dispatch() failed.
|
||||
virtual void
|
||||
PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
|
||||
bool aDispatchResult);
|
||||
|
||||
// Must be implemented by subclasses. Called on the target thread.
|
||||
virtual bool
|
||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) = 0;
|
||||
|
||||
// By default asserts that Run() (and WorkerRun()) were called on the correct
|
||||
// thread. Any failures (false return from WorkerRun) are reported on |aCx|.
|
||||
// Also sends an asynchronous message to the ParentThread if the busy
|
||||
// count was previously modified in PreDispatch().
|
||||
virtual void
|
||||
PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult);
|
||||
|
||||
virtual bool
|
||||
DispatchInternal();
|
||||
|
||||
// Calling Run() directly is not supported. Just call Dispatch() and
|
||||
// WorkerRun() will be called on the correct thread automatically.
|
||||
NS_DECL_NSIRUNNABLE
|
||||
};
|
||||
|
||||
// This runnable is used to send a message directly to a worker's sync loop.
|
||||
class WorkerSyncRunnable : public WorkerRunnable
|
||||
{
|
||||
protected:
|
||||
nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
|
||||
|
||||
// Passing null for aSyncLoopTarget is allowed and will result in the behavior
|
||||
// of a normal WorkerRunnable.
|
||||
WorkerSyncRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
nsIEventTarget* aSyncLoopTarget);
|
||||
|
||||
WorkerSyncRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
already_AddRefed<nsIEventTarget> aSyncLoopTarget);
|
||||
|
||||
virtual ~WorkerSyncRunnable();
|
||||
|
||||
private:
|
||||
virtual bool
|
||||
DispatchInternal() MOZ_OVERRIDE;
|
||||
};
|
||||
|
||||
// This runnable is identical to WorkerSyncRunnable except it is meant to be
|
||||
// used on the main thread only.
|
||||
class MainThreadWorkerSyncRunnable : public WorkerSyncRunnable
|
||||
{
|
||||
protected:
|
||||
// Passing null for aSyncLoopTarget is allowed and will result in the behavior
|
||||
// of a normal WorkerRunnable.
|
||||
MainThreadWorkerSyncRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
nsIEventTarget* aSyncLoopTarget)
|
||||
: WorkerSyncRunnable(aWorkerPrivate, aSyncLoopTarget)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
}
|
||||
|
||||
MainThreadWorkerSyncRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
already_AddRefed<nsIEventTarget> aSyncLoopTarget)
|
||||
: WorkerSyncRunnable(aWorkerPrivate, aSyncLoopTarget)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
}
|
||||
|
||||
virtual ~MainThreadWorkerSyncRunnable()
|
||||
{ }
|
||||
|
||||
private:
|
||||
virtual bool
|
||||
PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void
|
||||
PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
|
||||
bool aDispatchResult) MOZ_OVERRIDE;
|
||||
};
|
||||
|
||||
// This runnable is used to stop a sync loop . As sync loops keep the busy count
|
||||
// incremented as long as they run this runnable does not modify the busy count
|
||||
// in any way.
|
||||
class StopSyncLoopRunnable : public WorkerSyncRunnable
|
||||
{
|
||||
bool mResult;
|
||||
|
||||
public:
|
||||
// Passing null for aSyncLoopTarget is not allowed.
|
||||
StopSyncLoopRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
already_AddRefed<nsIEventTarget> aSyncLoopTarget,
|
||||
bool aResult);
|
||||
|
||||
// By default StopSyncLoopRunnables cannot be canceled since they could leave
|
||||
// a sync loop spinning forever.
|
||||
NS_DECL_NSICANCELABLERUNNABLE
|
||||
|
||||
protected:
|
||||
virtual ~StopSyncLoopRunnable()
|
||||
{ }
|
||||
|
||||
// Called on the worker thread to set an exception on the context if mResult
|
||||
// is false. Override if you need an exception.
|
||||
virtual void
|
||||
MaybeSetException(JSContext* aCx)
|
||||
{ }
|
||||
|
||||
private:
|
||||
virtual bool
|
||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE;
|
||||
|
||||
virtual bool
|
||||
DispatchInternal() MOZ_OVERRIDE;
|
||||
};
|
||||
|
||||
// This runnable is identical to StopSyncLoopRunnable except it is meant to be
|
||||
// used on the main thread only.
|
||||
class MainThreadStopSyncLoopRunnable : public StopSyncLoopRunnable
|
||||
{
|
||||
public:
|
||||
// Passing null for aSyncLoopTarget is not allowed.
|
||||
MainThreadStopSyncLoopRunnable(
|
||||
WorkerPrivate* aWorkerPrivate,
|
||||
already_AddRefed<nsIEventTarget> aSyncLoopTarget,
|
||||
bool aResult)
|
||||
: StopSyncLoopRunnable(aWorkerPrivate, aSyncLoopTarget, aResult)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ~MainThreadStopSyncLoopRunnable()
|
||||
{ }
|
||||
|
||||
private:
|
||||
virtual bool
|
||||
PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void
|
||||
PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
|
||||
bool aDispatchResult) MOZ_OVERRIDE;
|
||||
};
|
||||
|
||||
// This runnable is processed as soon as it is received by the worker,
|
||||
// potentially running before previously queued runnables and perhaps even with
|
||||
// other JS code executing on the stack. These runnables must not alter the
|
||||
// state of the JS runtime and should only twiddle state values. The busy count
|
||||
// is never modified.
|
||||
class WorkerControlRunnable : public WorkerRunnable
|
||||
{
|
||||
friend class WorkerPrivate;
|
||||
|
||||
protected:
|
||||
WorkerControlRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
TargetAndBusyBehavior aBehavior)
|
||||
#ifdef DEBUG
|
||||
;
|
||||
#else
|
||||
: WorkerRunnable(aWorkerPrivate, aBehavior)
|
||||
{ }
|
||||
#endif
|
||||
|
||||
virtual ~WorkerControlRunnable()
|
||||
{ }
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
private:
|
||||
virtual bool
|
||||
DispatchInternal() MOZ_OVERRIDE;
|
||||
|
||||
// Should only be called by WorkerPrivate::DoRunLoop.
|
||||
using WorkerRunnable::Cancel;
|
||||
};
|
||||
|
||||
// A convenience class for WorkerControlRunnables that originate on the main
|
||||
// thread.
|
||||
class MainThreadWorkerControlRunnable : public WorkerControlRunnable
|
||||
{
|
||||
protected:
|
||||
MainThreadWorkerControlRunnable(WorkerPrivate* aWorkerPrivate)
|
||||
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
|
||||
{ }
|
||||
|
||||
virtual ~MainThreadWorkerControlRunnable()
|
||||
{ }
|
||||
|
||||
virtual bool
|
||||
PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void
|
||||
PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
|
||||
bool aDispatchResult) MOZ_OVERRIDE;
|
||||
};
|
||||
|
||||
END_WORKERS_NAMESPACE
|
||||
|
||||
#endif // mozilla_dom_workers_workerrunnable_h__
|
@ -184,39 +184,50 @@ SuspendWorkersForWindow(nsPIDOMWindow* aWindow);
|
||||
void
|
||||
ResumeWorkersForWindow(nsPIDOMWindow* aWindow);
|
||||
|
||||
class WorkerTask {
|
||||
class WorkerTask
|
||||
{
|
||||
protected:
|
||||
WorkerTask()
|
||||
{ }
|
||||
|
||||
virtual ~WorkerTask()
|
||||
{ }
|
||||
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WorkerTask)
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WorkerTask)
|
||||
|
||||
virtual ~WorkerTask() { }
|
||||
|
||||
virtual bool RunTask(JSContext* aCx) = 0;
|
||||
virtual bool
|
||||
RunTask(JSContext* aCx) = 0;
|
||||
};
|
||||
|
||||
class WorkerCrossThreadDispatcher {
|
||||
class WorkerCrossThreadDispatcher
|
||||
{
|
||||
friend class WorkerPrivate;
|
||||
|
||||
// Must be acquired *before* the WorkerPrivate's mutex, when they're both
|
||||
// held.
|
||||
Mutex mMutex;
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
|
||||
private:
|
||||
// Only created by WorkerPrivate.
|
||||
WorkerCrossThreadDispatcher(WorkerPrivate* aWorkerPrivate);
|
||||
|
||||
// Only called by WorkerPrivate.
|
||||
void
|
||||
Forget()
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
mWorkerPrivate = nullptr;
|
||||
}
|
||||
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WorkerCrossThreadDispatcher)
|
||||
|
||||
WorkerCrossThreadDispatcher(WorkerPrivate* aPrivate) :
|
||||
mMutex("WorkerCrossThreadDispatcher"), mPrivate(aPrivate) {}
|
||||
void Forget()
|
||||
{
|
||||
mozilla::MutexAutoLock lock(mMutex);
|
||||
mPrivate = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generically useful function for running a bit of C++ code on the worker
|
||||
* thread.
|
||||
*/
|
||||
bool PostTask(WorkerTask* aTask);
|
||||
|
||||
protected:
|
||||
friend class WorkerPrivate;
|
||||
|
||||
// Must be acquired *before* the WorkerPrivate's mutex, when they're both held.
|
||||
mozilla::Mutex mMutex;
|
||||
WorkerPrivate* mPrivate;
|
||||
// Generically useful function for running a bit of C++ code on the worker
|
||||
// thread.
|
||||
bool
|
||||
PostTask(WorkerTask* aTask);
|
||||
};
|
||||
|
||||
WorkerCrossThreadDispatcher*
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -22,8 +22,8 @@ class Proxy;
|
||||
class XMLHttpRequestUpload;
|
||||
class WorkerPrivate;
|
||||
|
||||
class XMLHttpRequest : public nsXHREventTarget,
|
||||
public WorkerFeature
|
||||
class XMLHttpRequest MOZ_FINAL: public nsXHREventTarget,
|
||||
public WorkerFeature
|
||||
{
|
||||
public:
|
||||
struct StateData
|
||||
@ -61,10 +61,6 @@ private:
|
||||
bool mMozAnon;
|
||||
bool mMozSystem;
|
||||
|
||||
protected:
|
||||
XMLHttpRequest(WorkerPrivate* aWorkerPrivate);
|
||||
virtual ~XMLHttpRequest();
|
||||
|
||||
public:
|
||||
virtual JSObject*
|
||||
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
|
||||
@ -272,6 +268,9 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
XMLHttpRequest(WorkerPrivate* aWorkerPrivate);
|
||||
~XMLHttpRequest();
|
||||
|
||||
enum ReleaseType { Default, XHRIsGoingAway, WorkerIsGoingAway };
|
||||
|
||||
void
|
||||
|
@ -9,6 +9,7 @@ TEST_DIRS += ['test']
|
||||
# Public stuff.
|
||||
EXPORTS.mozilla.dom += [
|
||||
'WorkerPrivate.h',
|
||||
'WorkerRunnable.h',
|
||||
'WorkerScope.h',
|
||||
]
|
||||
|
||||
@ -43,6 +44,7 @@ SOURCES += [
|
||||
'SharedWorker.cpp',
|
||||
'URL.cpp',
|
||||
'WorkerPrivate.cpp',
|
||||
'WorkerRunnable.cpp',
|
||||
'WorkerScope.cpp',
|
||||
'XMLHttpRequest.cpp',
|
||||
'XMLHttpRequestUpload.cpp',
|
||||
@ -58,6 +60,7 @@ LOCAL_INCLUDES += [
|
||||
'/content/base/src',
|
||||
'/content/events/src',
|
||||
'/xpcom/build',
|
||||
'/xpcom/threads',
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
Loading…
x
Reference in New Issue
Block a user