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:
Ben Turner 2013-10-23 06:16:49 -07:00
parent 2e75b22c85
commit 357b12898a
14 changed files with 3684 additions and 2262 deletions

View File

@ -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)
{}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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)

View 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__

View File

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

View File

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

View File

@ -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')