LCOV - code coverage report
Current view: top level - dom/workers - WorkerRunnable.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 208 334 62.3 %
Date: 2017-07-14 16:53:18 Functions: 34 59 57.6 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "WorkerRunnable.h"
       8             : 
       9             : #include "nsGlobalWindow.h"
      10             : #include "nsIEventTarget.h"
      11             : #include "nsIGlobalObject.h"
      12             : #include "nsIRunnable.h"
      13             : #include "nsThreadUtils.h"
      14             : 
      15             : #include "mozilla/DebugOnly.h"
      16             : #include "mozilla/ErrorResult.h"
      17             : #include "mozilla/dom/ScriptSettings.h"
      18             : #include "mozilla/Telemetry.h"
      19             : 
      20             : #include "js/RootingAPI.h"
      21             : #include "js/Value.h"
      22             : 
      23             : #include "WorkerPrivate.h"
      24             : #include "WorkerScope.h"
      25             : 
      26             : USING_WORKERS_NAMESPACE
      27             : 
      28             : namespace {
      29             : 
      30             : const nsIID kWorkerRunnableIID = {
      31             :   0x320cc0b5, 0xef12, 0x4084, { 0x88, 0x6e, 0xca, 0x6a, 0x81, 0xe4, 0x1d, 0x68 }
      32             : };
      33             : 
      34             : } // namespace
      35             : 
      36             : #ifdef DEBUG
      37          39 : WorkerRunnable::WorkerRunnable(WorkerPrivate* aWorkerPrivate,
      38          39 :                                TargetAndBusyBehavior aBehavior)
      39             : : mWorkerPrivate(aWorkerPrivate), mBehavior(aBehavior), mCanceled(0),
      40          39 :   mCallingCancelWithinRun(false)
      41             : {
      42          39 :   MOZ_ASSERT(aWorkerPrivate);
      43          39 : }
      44             : #endif
      45             : 
      46             : bool
      47           3 : WorkerRunnable::IsDebuggerRunnable() const
      48             : {
      49           3 :   return false;
      50             : }
      51             : 
      52             : nsIGlobalObject*
      53           2 : WorkerRunnable::DefaultGlobalObject() const
      54             : {
      55           2 :   if (IsDebuggerRunnable()) {
      56           0 :     return mWorkerPrivate->DebuggerGlobalScope();
      57             :   } else {
      58           2 :     return mWorkerPrivate->GlobalScope();
      59             :   }
      60             : }
      61             : 
      62             : bool
      63           3 : WorkerRunnable::PreDispatch(WorkerPrivate* aWorkerPrivate)
      64             : {
      65             : #ifdef DEBUG
      66           3 :   MOZ_ASSERT(aWorkerPrivate);
      67             : 
      68           3 :   switch (mBehavior) {
      69             :     case ParentThreadUnchangedBusyCount:
      70           1 :       aWorkerPrivate->AssertIsOnWorkerThread();
      71           1 :       break;
      72             : 
      73             :     case WorkerThreadModifyBusyCount:
      74             :     case WorkerThreadUnchangedBusyCount:
      75           2 :       aWorkerPrivate->AssertIsOnParentThread();
      76           2 :       break;
      77             : 
      78             :     default:
      79           0 :       MOZ_ASSERT_UNREACHABLE("Unknown behavior!");
      80             :   }
      81             : #endif
      82             : 
      83           3 :   if (mBehavior == WorkerThreadModifyBusyCount) {
      84           2 :     return aWorkerPrivate->ModifyBusyCount(true);
      85             :   }
      86             : 
      87           1 :   return true;
      88             : }
      89             : 
      90             : bool
      91          39 : WorkerRunnable::Dispatch()
      92             : {
      93          39 :   bool ok = PreDispatch(mWorkerPrivate);
      94          39 :   if (ok) {
      95          39 :     ok = DispatchInternal();
      96             :   }
      97          39 :   PostDispatch(mWorkerPrivate, ok);
      98          39 :   return ok;
      99             : }
     100             : 
     101             : bool
     102           2 : WorkerRunnable::DispatchInternal()
     103             : {
     104           4 :   RefPtr<WorkerRunnable> runnable(this);
     105             : 
     106           2 :   if (mBehavior == WorkerThreadModifyBusyCount ||
     107           0 :       mBehavior == WorkerThreadUnchangedBusyCount) {
     108           2 :     if (IsDebuggerRunnable()) {
     109           0 :       return NS_SUCCEEDED(mWorkerPrivate->DispatchDebuggerRunnable(runnable.forget()));
     110             :     } else {
     111           2 :       return NS_SUCCEEDED(mWorkerPrivate->Dispatch(runnable.forget()));
     112             :     }
     113             :   }
     114             : 
     115           0 :   MOZ_ASSERT(mBehavior == ParentThreadUnchangedBusyCount);
     116             : 
     117           0 :   if (WorkerPrivate* parent = mWorkerPrivate->GetParent()) {
     118           0 :     return NS_SUCCEEDED(parent->Dispatch(runnable.forget()));
     119             :   }
     120             : 
     121           0 :   return NS_SUCCEEDED(mWorkerPrivate->DispatchToMainThread(runnable.forget()));
     122             : }
     123             : 
     124             : void
     125           3 : WorkerRunnable::PostDispatch(WorkerPrivate* aWorkerPrivate,
     126             :                              bool aDispatchResult)
     127             : {
     128           3 :   MOZ_ASSERT(aWorkerPrivate);
     129             : 
     130             : #ifdef DEBUG
     131           3 :   switch (mBehavior) {
     132             :     case ParentThreadUnchangedBusyCount:
     133           1 :       aWorkerPrivate->AssertIsOnWorkerThread();
     134           1 :       break;
     135             : 
     136             :     case WorkerThreadModifyBusyCount:
     137           2 :       aWorkerPrivate->AssertIsOnParentThread();
     138           2 :       break;
     139             : 
     140             :     case WorkerThreadUnchangedBusyCount:
     141           0 :       aWorkerPrivate->AssertIsOnParentThread();
     142           0 :       break;
     143             : 
     144             :     default:
     145           0 :       MOZ_ASSERT_UNREACHABLE("Unknown behavior!");
     146             :   }
     147             : #endif
     148             : 
     149           3 :   if (!aDispatchResult) {
     150           0 :     if (mBehavior == WorkerThreadModifyBusyCount) {
     151           0 :       aWorkerPrivate->ModifyBusyCount(false);
     152             :     }
     153             :   }
     154           3 : }
     155             : 
     156             : bool
     157          30 : WorkerRunnable::PreRun(WorkerPrivate* aWorkerPrivate)
     158             : {
     159          30 :   return true;
     160             : }
     161             : 
     162             : void
     163          29 : WorkerRunnable::PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
     164             :                         bool aRunResult)
     165             : {
     166          29 :   MOZ_ASSERT(aCx);
     167          29 :   MOZ_ASSERT(aWorkerPrivate);
     168             : 
     169             : #ifdef DEBUG
     170          29 :   switch (mBehavior) {
     171             :     case ParentThreadUnchangedBusyCount:
     172           1 :       aWorkerPrivate->AssertIsOnParentThread();
     173           1 :       break;
     174             : 
     175             :     case WorkerThreadModifyBusyCount:
     176           0 :       aWorkerPrivate->AssertIsOnWorkerThread();
     177           0 :       break;
     178             : 
     179             :     case WorkerThreadUnchangedBusyCount:
     180          28 :       aWorkerPrivate->AssertIsOnWorkerThread();
     181          28 :       break;
     182             : 
     183             :     default:
     184           0 :       MOZ_ASSERT_UNREACHABLE("Unknown behavior!");
     185             :   }
     186             : #endif
     187             : 
     188          29 :   if (mBehavior == WorkerThreadModifyBusyCount) {
     189           0 :     aWorkerPrivate->ModifyBusyCountFromWorker(false);
     190             :   }
     191          29 : }
     192             : 
     193             : // static
     194             : WorkerRunnable*
     195          36 : WorkerRunnable::FromRunnable(nsIRunnable* aRunnable)
     196             : {
     197          36 :   MOZ_ASSERT(aRunnable);
     198             : 
     199             :   WorkerRunnable* runnable;
     200          72 :   nsresult rv = aRunnable->QueryInterface(kWorkerRunnableIID,
     201          72 :                                           reinterpret_cast<void**>(&runnable));
     202          36 :   if (NS_FAILED(rv)) {
     203           0 :     return nullptr;
     204             :   }
     205             : 
     206          36 :   MOZ_ASSERT(runnable);
     207          36 :   return runnable;
     208             : }
     209             : 
     210         229 : NS_IMPL_ADDREF(WorkerRunnable)
     211         222 : NS_IMPL_RELEASE(WorkerRunnable)
     212             : 
     213         188 : NS_INTERFACE_MAP_BEGIN(WorkerRunnable)
     214         188 :   NS_INTERFACE_MAP_ENTRY(nsIRunnable)
     215          76 :   NS_INTERFACE_MAP_ENTRY(nsICancelableRunnable)
     216          76 :   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRunnable)
     217             :   // kWorkerRunnableIID is special in that it does not AddRef its result.
     218          76 :   if (aIID.Equals(kWorkerRunnableIID)) {
     219          36 :     *aInstancePtr = this;
     220          36 :     return NS_OK;
     221             :   }
     222             :   else
     223          40 : NS_INTERFACE_MAP_END
     224             : 
     225             : NS_IMETHODIMP
     226          36 : WorkerRunnable::Run()
     227             : {
     228          71 :   bool targetIsWorkerThread = mBehavior == WorkerThreadModifyBusyCount ||
     229          71 :                               mBehavior == WorkerThreadUnchangedBusyCount;
     230             : 
     231             : #ifdef DEBUG
     232          36 :   MOZ_ASSERT_IF(mCallingCancelWithinRun, targetIsWorkerThread);
     233          36 :   if (targetIsWorkerThread) {
     234          35 :     mWorkerPrivate->AssertIsOnWorkerThread();
     235             :   }
     236             :   else {
     237           1 :     MOZ_ASSERT(mBehavior == ParentThreadUnchangedBusyCount);
     238           1 :     mWorkerPrivate->AssertIsOnParentThread();
     239             :   }
     240             : #endif
     241             : 
     242          36 :   if (IsCanceled() && !mCallingCancelWithinRun) {
     243           0 :     return NS_OK;
     244             :   }
     245             : 
     246          71 :   if (targetIsWorkerThread &&
     247          35 :       mWorkerPrivate->AllPendingRunnablesShouldBeCanceled() &&
     248          36 :       !IsCanceled() && !mCallingCancelWithinRun) {
     249             : 
     250             :     // Prevent recursion.
     251           0 :     mCallingCancelWithinRun = true;
     252             : 
     253           0 :     Cancel();
     254             : 
     255           0 :     MOZ_ASSERT(mCallingCancelWithinRun);
     256           0 :     mCallingCancelWithinRun = false;
     257             : 
     258           0 :     MOZ_ASSERT(IsCanceled(), "Subclass Cancel() didn't set IsCanceled()!");
     259             : 
     260           0 :     if (mBehavior == WorkerThreadModifyBusyCount) {
     261           0 :       mWorkerPrivate->ModifyBusyCountFromWorker(false);
     262             :     }
     263             : 
     264           0 :     return NS_OK;
     265             :   }
     266             : 
     267          36 :   bool result = PreRun(mWorkerPrivate);
     268          36 :   if (!result) {
     269           0 :     MOZ_ASSERT(targetIsWorkerThread,
     270             :                "The only PreRun implementation that can fail is "
     271             :                "ScriptExecutorRunnable");
     272           0 :     mWorkerPrivate->AssertIsOnWorkerThread();
     273           0 :     MOZ_ASSERT(!JS_IsExceptionPending(mWorkerPrivate->GetJSContext()));
     274             :     // We can't enter a useful compartment on the JSContext here; just pass it
     275             :     // in as-is.
     276           0 :     PostRun(mWorkerPrivate->GetJSContext(), mWorkerPrivate, false);
     277           0 :     return NS_ERROR_FAILURE;
     278             :   }
     279             : 
     280             :   // Track down the appropriate global, if any, to use for the AutoEntryScript.
     281          68 :   nsCOMPtr<nsIGlobalObject> globalObject;
     282          36 :   bool isMainThread = !targetIsWorkerThread && !mWorkerPrivate->GetParent();
     283          36 :   MOZ_ASSERT(isMainThread == NS_IsMainThread());
     284          68 :   RefPtr<WorkerPrivate> kungFuDeathGrip;
     285          36 :   if (targetIsWorkerThread) {
     286          35 :     JSContext* cx = GetCurrentThreadJSContext();
     287          35 :     if (NS_WARN_IF(!cx)) {
     288           0 :       return NS_ERROR_FAILURE;
     289             :     }
     290             : 
     291          35 :     JSObject* global = JS::CurrentGlobalOrNull(cx);
     292          35 :     if (global) {
     293          33 :       globalObject = xpc::NativeGlobal(global);
     294             :     } else {
     295           2 :       globalObject = DefaultGlobalObject();
     296             :     }
     297             : 
     298             :     // We may still not have a globalObject here: in the case of
     299             :     // CompileScriptRunnable, we don't actually create the global object until
     300             :     // we have the script data, which happens in a syncloop under
     301             :     // CompileScriptRunnable::WorkerRun, so we can't assert that it got created
     302             :     // in the PreRun call above.
     303             :   } else {
     304           1 :     kungFuDeathGrip = mWorkerPrivate;
     305           1 :     if (isMainThread) {
     306           1 :       globalObject = nsGlobalWindow::Cast(mWorkerPrivate->GetWindow());
     307             :     } else {
     308           0 :       globalObject = mWorkerPrivate->GetParent()->GlobalScope();
     309             :     }
     310             :   }
     311             : 
     312             :   // We might run script as part of WorkerRun, so we need an AutoEntryScript.
     313             :   // This is part of the HTML spec for workers at:
     314             :   // http://www.whatwg.org/specs/web-apps/current-work/#run-a-worker
     315             :   // If we don't have a globalObject we have to use an AutoJSAPI instead, but
     316             :   // this is OK as we won't be running script in these circumstances.
     317          68 :   Maybe<mozilla::dom::AutoJSAPI> maybeJSAPI;
     318          68 :   Maybe<mozilla::dom::AutoEntryScript> aes;
     319             :   JSContext* cx;
     320             :   AutoJSAPI* jsapi;
     321          36 :   if (globalObject) {
     322          34 :     aes.emplace(globalObject, "Worker runnable", isMainThread);
     323          34 :     jsapi = aes.ptr();
     324          34 :     cx = aes->cx();
     325             :   } else {
     326           2 :     maybeJSAPI.emplace();
     327           2 :     maybeJSAPI->Init();
     328           2 :     jsapi = maybeJSAPI.ptr();
     329           2 :     cx = jsapi->cx();
     330             :   }
     331             : 
     332             :   // Note that we can't assert anything about mWorkerPrivate->GetWrapper()
     333             :   // existing, since it may in fact have been GCed (and we may be one of the
     334             :   // runnables cleaning up the worker as a result).
     335             : 
     336             :   // If we are on the parent thread and that thread is not the main thread,
     337             :   // then we must be a dedicated worker (because there are no
     338             :   // Shared/ServiceWorkers whose parent is itself a worker) and then we
     339             :   // definitely have a globalObject.  If it _is_ the main thread, globalObject
     340             :   // can be null for workers started from JSMs or other non-window contexts,
     341             :   // sadly.
     342          36 :   MOZ_ASSERT_IF(!targetIsWorkerThread && !isMainThread,
     343             :                 mWorkerPrivate->IsDedicatedWorker() && globalObject);
     344             : 
     345             :   // If we're on the parent thread we might be in a null compartment in the
     346             :   // situation described above when globalObject is null.  Make sure to enter
     347             :   // the compartment of the worker's reflector if there is one.  There might
     348             :   // not be one if we're just starting to compile the script for this worker.
     349          68 :   Maybe<JSAutoCompartment> ac;
     350          36 :   if (!targetIsWorkerThread && mWorkerPrivate->GetWrapper()) {
     351             :     // If we're on the parent thread and have a reflector and a globalObject,
     352             :     // then the compartments of cx, globalObject, and the worker's reflector
     353             :     // should all match.
     354           1 :     MOZ_ASSERT_IF(globalObject,
     355             :                   js::GetObjectCompartment(mWorkerPrivate->GetWrapper()) ==
     356             :                     js::GetContextCompartment(cx));
     357           1 :     MOZ_ASSERT_IF(globalObject,
     358             :                   js::GetObjectCompartment(mWorkerPrivate->GetWrapper()) ==
     359             :                     js::GetObjectCompartment(globalObject->GetGlobalJSObject()));
     360             : 
     361             :     // If we're on the parent thread and have a reflector, then our
     362             :     // JSContext had better be either in the null compartment (and hence
     363             :     // have no globalObject) or in the compartment of our reflector.
     364           1 :     MOZ_ASSERT(!js::GetContextCompartment(cx) ||
     365             :                js::GetObjectCompartment(mWorkerPrivate->GetWrapper()) ==
     366             :                  js::GetContextCompartment(cx),
     367             :                "Must either be in the null compartment or in our reflector "
     368             :                "compartment");
     369             : 
     370           1 :     ac.emplace(cx, mWorkerPrivate->GetWrapper());
     371             :   }
     372             : 
     373          36 :   MOZ_ASSERT(!jsapi->HasException());
     374          36 :   result = WorkerRun(cx, mWorkerPrivate);
     375          32 :   MOZ_ASSERT_IF(result, !jsapi->HasException());
     376          32 :   jsapi->ReportException();
     377             : 
     378             :   // We can't even assert that this didn't create our global, since in the case
     379             :   // of CompileScriptRunnable it _does_.
     380             : 
     381             :   // It would be nice to avoid passing a JSContext to PostRun, but in the case
     382             :   // of ScriptExecutorRunnable we need to know the current compartment on the
     383             :   // JSContext (the one we set up based on the global returned from PreRun) so
     384             :   // that we can sanely do exception reporting.  In particular, we want to make
     385             :   // sure that we do our JS_SetPendingException while still in that compartment,
     386             :   // because otherwise we might end up trying to create a cross-compartment
     387             :   // wrapper when we try to move the JS exception from our runnable's
     388             :   // ErrorResult to the JSContext, and that's not desirable in this case.
     389             :   //
     390             :   // We _could_ skip passing a JSContext here and then in
     391             :   // ScriptExecutorRunnable::PostRun end up grabbing it from the WorkerPrivate
     392             :   // and looking at its current compartment.  But that seems like slightly weird
     393             :   // action-at-a-distance...
     394             :   //
     395             :   // In any case, we do NOT try to change the compartment on the JSContext at
     396             :   // this point; in the one case in which we could do that
     397             :   // (CompileScriptRunnable) it actually doesn't matter which compartment we're
     398             :   // in for PostRun.
     399          32 :   PostRun(cx, mWorkerPrivate, result);
     400          32 :   MOZ_ASSERT(!jsapi->HasException());
     401             : 
     402          32 :   return result ? NS_OK : NS_ERROR_FAILURE;
     403             : }
     404             : 
     405             : nsresult
     406           0 : WorkerRunnable::Cancel()
     407             : {
     408           0 :   uint32_t canceledCount = ++mCanceled;
     409             : 
     410           0 :   MOZ_ASSERT(canceledCount, "Cancel() overflow!");
     411             : 
     412             :   // The docs say that Cancel() should not be called more than once and that we
     413             :   // should throw NS_ERROR_UNEXPECTED if it is.
     414           0 :   return (canceledCount == 1) ? NS_OK : NS_ERROR_UNEXPECTED;
     415             : }
     416             : 
     417             : void
     418           0 : WorkerDebuggerRunnable::PostDispatch(WorkerPrivate* aWorkerPrivate,
     419             :                                      bool aDispatchResult)
     420             : {
     421           0 : }
     422             : 
     423           8 : WorkerSyncRunnable::WorkerSyncRunnable(WorkerPrivate* aWorkerPrivate,
     424           8 :                                        nsIEventTarget* aSyncLoopTarget)
     425             : : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
     426           8 :   mSyncLoopTarget(aSyncLoopTarget)
     427             : {
     428             : #ifdef DEBUG
     429           8 :   if (mSyncLoopTarget) {
     430           8 :     mWorkerPrivate->AssertValidSyncLoop(mSyncLoopTarget);
     431             :   }
     432             : #endif
     433           8 : }
     434             : 
     435          28 : WorkerSyncRunnable::WorkerSyncRunnable(
     436             :                                WorkerPrivate* aWorkerPrivate,
     437          28 :                                already_AddRefed<nsIEventTarget>&& aSyncLoopTarget)
     438             : : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
     439          28 :   mSyncLoopTarget(aSyncLoopTarget)
     440             : {
     441             : #ifdef DEBUG
     442          28 :   if (mSyncLoopTarget) {
     443          28 :     mWorkerPrivate->AssertValidSyncLoop(mSyncLoopTarget);
     444             :   }
     445             : #endif
     446          28 : }
     447             : 
     448          31 : WorkerSyncRunnable::~WorkerSyncRunnable()
     449             : {
     450          31 : }
     451             : 
     452             : bool
     453          27 : WorkerSyncRunnable::DispatchInternal()
     454             : {
     455          27 :   if (mSyncLoopTarget) {
     456          54 :     RefPtr<WorkerSyncRunnable> runnable(this);
     457          27 :     return NS_SUCCEEDED(mSyncLoopTarget->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL));
     458             :   }
     459             : 
     460           0 :   return WorkerRunnable::DispatchInternal();
     461             : }
     462             : 
     463             : void
     464          27 : MainThreadWorkerSyncRunnable::PostDispatch(WorkerPrivate* aWorkerPrivate,
     465             :                                            bool aDispatchResult)
     466             : {
     467          27 : }
     468             : 
     469           9 : MainThreadStopSyncLoopRunnable::MainThreadStopSyncLoopRunnable(
     470             :                                WorkerPrivate* aWorkerPrivate,
     471             :                                already_AddRefed<nsIEventTarget>&& aSyncLoopTarget,
     472           9 :                                bool aResult)
     473           9 : : WorkerSyncRunnable(aWorkerPrivate, Move(aSyncLoopTarget)), mResult(aResult)
     474             : {
     475           9 :   AssertIsOnMainThread();
     476             : #ifdef DEBUG
     477           9 :   mWorkerPrivate->AssertValidSyncLoop(mSyncLoopTarget);
     478             : #endif
     479           9 : }
     480             : 
     481             : nsresult
     482           0 : MainThreadStopSyncLoopRunnable::Cancel()
     483             : {
     484           0 :   nsresult rv = Run();
     485           0 :   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Run() failed");
     486             : 
     487           0 :   nsresult rv2 = WorkerSyncRunnable::Cancel();
     488           0 :   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv2), "Cancel() failed");
     489             : 
     490           0 :   return NS_FAILED(rv) ? rv : rv2;
     491             : }
     492             : 
     493             : bool
     494           9 : MainThreadStopSyncLoopRunnable::WorkerRun(JSContext* aCx,
     495             :                                           WorkerPrivate* aWorkerPrivate)
     496             : {
     497           9 :   aWorkerPrivate->AssertIsOnWorkerThread();
     498           9 :   MOZ_ASSERT(mSyncLoopTarget);
     499             : 
     500          18 :   nsCOMPtr<nsIEventTarget> syncLoopTarget;
     501           9 :   mSyncLoopTarget.swap(syncLoopTarget);
     502             : 
     503           9 :   aWorkerPrivate->StopSyncLoop(syncLoopTarget, mResult);
     504          18 :   return true;
     505             : }
     506             : 
     507             : bool
     508           9 : MainThreadStopSyncLoopRunnable::DispatchInternal()
     509             : {
     510           9 :   MOZ_ASSERT(mSyncLoopTarget);
     511             : 
     512          18 :   RefPtr<MainThreadStopSyncLoopRunnable> runnable(this);
     513          18 :   return NS_SUCCEEDED(mSyncLoopTarget->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL));
     514             : }
     515             : 
     516             : void
     517           9 : MainThreadStopSyncLoopRunnable::PostDispatch(WorkerPrivate* aWorkerPrivate,
     518             :                                              bool aDispatchResult)
     519             : {
     520           9 : }
     521             : 
     522             : #ifdef DEBUG
     523           1 : WorkerControlRunnable::WorkerControlRunnable(WorkerPrivate* aWorkerPrivate,
     524           1 :                                              TargetAndBusyBehavior aBehavior)
     525           1 : : WorkerRunnable(aWorkerPrivate, aBehavior)
     526             : {
     527           1 :   MOZ_ASSERT(aWorkerPrivate);
     528           1 :   MOZ_ASSERT(aBehavior == ParentThreadUnchangedBusyCount ||
     529             :              aBehavior == WorkerThreadUnchangedBusyCount,
     530             :              "WorkerControlRunnables should not modify the busy count");
     531           1 : }
     532             : #endif
     533             : 
     534             : nsresult
     535           0 : WorkerControlRunnable::Cancel()
     536             : {
     537           0 :   if (NS_FAILED(Run())) {
     538           0 :     NS_WARNING("WorkerControlRunnable::Run() failed.");
     539             :   }
     540             : 
     541           0 :   return WorkerRunnable::Cancel();
     542             : }
     543             : 
     544             : bool
     545           1 : WorkerControlRunnable::DispatchInternal()
     546             : {
     547           2 :   RefPtr<WorkerControlRunnable> runnable(this);
     548             : 
     549           1 :   if (mBehavior == WorkerThreadUnchangedBusyCount) {
     550           0 :     return NS_SUCCEEDED(mWorkerPrivate->DispatchControlRunnable(runnable.forget()));
     551             :   }
     552             : 
     553           1 :   if (WorkerPrivate* parent = mWorkerPrivate->GetParent()) {
     554           0 :     return NS_SUCCEEDED(parent->DispatchControlRunnable(runnable.forget()));
     555             :   }
     556             : 
     557           1 :   return NS_SUCCEEDED(mWorkerPrivate->DispatchToMainThread(runnable.forget()));
     558             : }
     559             : 
     560          17 : NS_IMPL_ISUPPORTS_INHERITED0(WorkerControlRunnable, WorkerRunnable)
     561             : 
     562          10 : WorkerMainThreadRunnable::WorkerMainThreadRunnable(
     563             :   WorkerPrivate* aWorkerPrivate,
     564          10 :   const nsACString& aTelemetryKey)
     565             :   : mozilla::Runnable("dom::workers::WorkerMainThreadRunnable")
     566             :   , mWorkerPrivate(aWorkerPrivate)
     567          10 :   , mTelemetryKey(aTelemetryKey)
     568             : {
     569          10 :   mWorkerPrivate->AssertIsOnWorkerThread();
     570          10 : }
     571             : 
     572             : void
     573          10 : WorkerMainThreadRunnable::Dispatch(Status aFailStatus, ErrorResult& aRv)
     574             : {
     575          10 :   mWorkerPrivate->AssertIsOnWorkerThread();
     576             : 
     577          10 :   TimeStamp startTime = TimeStamp::NowLoRes();
     578             : 
     579          19 :   AutoSyncLoopHolder syncLoop(mWorkerPrivate, aFailStatus);
     580             : 
     581          10 :   mSyncLoopTarget = syncLoop.GetEventTarget();
     582          10 :   if (!mSyncLoopTarget) {
     583             :     // SyncLoop creation can fail if the worker is shutting down.
     584           0 :     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     585           0 :     return;
     586             :   }
     587             : 
     588          19 :   DebugOnly<nsresult> rv = mWorkerPrivate->DispatchToMainThread(this);
     589          10 :   MOZ_ASSERT(NS_SUCCEEDED(rv),
     590             :              "Should only fail after xpcom-shutdown-threads and we're gone by then");
     591             : 
     592          10 :   bool success = syncLoop.Run();
     593             : 
     594           9 :   Telemetry::Accumulate(Telemetry::SYNC_WORKER_OPERATION, mTelemetryKey,
     595          18 :                         static_cast<uint32_t>((TimeStamp::NowLoRes() - startTime)
     596          18 :                                               .ToMilliseconds()));
     597             : 
     598             :   Unused << startTime; // Shut the compiler up.
     599             : 
     600           9 :   if (!success) {
     601           0 :     aRv.ThrowUncatchableException();
     602             :   }
     603             : }
     604             : 
     605             : NS_IMETHODIMP
     606           9 : WorkerMainThreadRunnable::Run()
     607             : {
     608           9 :   AssertIsOnMainThread();
     609             : 
     610           9 :   bool runResult = MainThreadRun();
     611             : 
     612             :   RefPtr<MainThreadStopSyncLoopRunnable> response =
     613             :     new MainThreadStopSyncLoopRunnable(mWorkerPrivate,
     614          18 :                                        mSyncLoopTarget.forget(),
     615          27 :                                        runResult);
     616             : 
     617           9 :   MOZ_ALWAYS_TRUE(response->Dispatch());
     618             : 
     619          18 :   return NS_OK;
     620             : }
     621             : 
     622           2 : WorkerCheckAPIExposureOnMainThreadRunnable::WorkerCheckAPIExposureOnMainThreadRunnable(WorkerPrivate* aWorkerPrivate):
     623             :   WorkerMainThreadRunnable(aWorkerPrivate,
     624           2 :                            NS_LITERAL_CSTRING("WorkerCheckAPIExposureOnMainThread"))
     625           2 : {}
     626             : 
     627           2 : WorkerCheckAPIExposureOnMainThreadRunnable::~WorkerCheckAPIExposureOnMainThreadRunnable()
     628           2 : {}
     629             : 
     630             : bool
     631           2 : WorkerCheckAPIExposureOnMainThreadRunnable::Dispatch()
     632             : {
     633           4 :   ErrorResult rv;
     634           2 :   WorkerMainThreadRunnable::Dispatch(Terminating, rv);
     635           2 :   bool ok = !rv.Failed();
     636           2 :   rv.SuppressException();
     637           4 :   return ok;
     638             : }
     639             : 
     640             : bool
     641           0 : WorkerSameThreadRunnable::PreDispatch(WorkerPrivate* aWorkerPrivate)
     642             : {
     643             :   // We don't call WorkerRunnable::PreDispatch, because we're using
     644             :   // WorkerThreadModifyBusyCount for mBehavior, and WorkerRunnable will assert
     645             :   // that PreDispatch is on the parent thread in that case.
     646           0 :   aWorkerPrivate->AssertIsOnWorkerThread();
     647           0 :   return true;
     648             : }
     649             : 
     650             : void
     651           0 : WorkerSameThreadRunnable::PostDispatch(WorkerPrivate* aWorkerPrivate,
     652             :                                        bool aDispatchResult)
     653             : {
     654             :   // We don't call WorkerRunnable::PostDispatch, because we're using
     655             :   // WorkerThreadModifyBusyCount for mBehavior, and WorkerRunnable will assert
     656             :   // that PostDispatch is on the parent thread in that case.
     657           0 :   aWorkerPrivate->AssertIsOnWorkerThread();
     658           0 :   if (aDispatchResult) {
     659           0 :     DebugOnly<bool> willIncrement = aWorkerPrivate->ModifyBusyCountFromWorker(true);
     660             :     // Should never fail since if this thread is still running, so should the
     661             :     // parent and it should be able to process a control runnable.
     662           0 :     MOZ_ASSERT(willIncrement);
     663             :   }
     664           0 : }
     665             : 
     666           0 : WorkerProxyToMainThreadRunnable::WorkerProxyToMainThreadRunnable(
     667           0 :   WorkerPrivate* aWorkerPrivate)
     668             :   : mozilla::Runnable("dom::workers::WorkerProxyToMainThreadRunnable")
     669           0 :   , mWorkerPrivate(aWorkerPrivate)
     670             : {
     671           0 :   MOZ_ASSERT(mWorkerPrivate);
     672           0 :   mWorkerPrivate->AssertIsOnWorkerThread();
     673           0 : }
     674             : 
     675           0 : WorkerProxyToMainThreadRunnable::~WorkerProxyToMainThreadRunnable()
     676           0 : {}
     677             : 
     678             : bool
     679           0 : WorkerProxyToMainThreadRunnable::Dispatch()
     680             : {
     681           0 :   mWorkerPrivate->AssertIsOnWorkerThread();
     682             : 
     683           0 :   if (NS_WARN_IF(!HoldWorker())) {
     684           0 :     RunBackOnWorkerThreadForCleanup();
     685           0 :     return false;
     686             :   }
     687             : 
     688           0 :   if (NS_WARN_IF(NS_FAILED(mWorkerPrivate->DispatchToMainThread(this)))) {
     689           0 :     ReleaseWorker();
     690           0 :     RunBackOnWorkerThreadForCleanup();
     691           0 :     return false;
     692             :   }
     693             : 
     694           0 :   return true;
     695             : }
     696             : 
     697             : NS_IMETHODIMP
     698           0 : WorkerProxyToMainThreadRunnable::Run()
     699             : {
     700           0 :   AssertIsOnMainThread();
     701           0 :   RunOnMainThread();
     702           0 :   PostDispatchOnMainThread();
     703           0 :   return NS_OK;
     704             : }
     705             : 
     706             : void
     707           0 : WorkerProxyToMainThreadRunnable::PostDispatchOnMainThread()
     708             : {
     709             :   class ReleaseRunnable final : public MainThreadWorkerControlRunnable
     710             :   {
     711             :     RefPtr<WorkerProxyToMainThreadRunnable> mRunnable;
     712             : 
     713             :   public:
     714           0 :     ReleaseRunnable(WorkerPrivate* aWorkerPrivate,
     715             :                     WorkerProxyToMainThreadRunnable* aRunnable)
     716           0 :       : MainThreadWorkerControlRunnable(aWorkerPrivate)
     717           0 :       , mRunnable(aRunnable)
     718             :     {
     719           0 :       MOZ_ASSERT(aRunnable);
     720           0 :     }
     721             : 
     722             :     // We must call RunBackOnWorkerThreadForCleanup() also if the runnable is
     723             :     // canceled.
     724             :     nsresult
     725           0 :     Cancel() override
     726             :     {
     727           0 :       WorkerRun(nullptr, mWorkerPrivate);
     728           0 :       return MainThreadWorkerControlRunnable::Cancel();
     729             :     }
     730             : 
     731             :     virtual bool
     732           0 :     WorkerRun(JSContext* aCx, workers::WorkerPrivate* aWorkerPrivate) override
     733             :     {
     734           0 :       MOZ_ASSERT(aWorkerPrivate);
     735           0 :       aWorkerPrivate->AssertIsOnWorkerThread();
     736             : 
     737           0 :       if (mRunnable) {
     738           0 :         mRunnable->RunBackOnWorkerThreadForCleanup();
     739             : 
     740             :         // Let's release the worker thread.
     741           0 :         mRunnable->ReleaseWorker();
     742           0 :         mRunnable = nullptr;
     743             :       }
     744             : 
     745           0 :       return true;
     746             :     }
     747             : 
     748             :   private:
     749           0 :     ~ReleaseRunnable()
     750           0 :     {}
     751             :   };
     752             : 
     753             :   RefPtr<WorkerControlRunnable> runnable =
     754           0 :     new ReleaseRunnable(mWorkerPrivate, this);
     755           0 :   Unused << NS_WARN_IF(!runnable->Dispatch());
     756           0 : }
     757             : 
     758             : bool
     759           0 : WorkerProxyToMainThreadRunnable::HoldWorker()
     760             : {
     761           0 :   mWorkerPrivate->AssertIsOnWorkerThread();
     762           0 :   MOZ_ASSERT(!mWorkerHolder);
     763             : 
     764           0 :   class SimpleWorkerHolder final : public WorkerHolder
     765             :   {
     766             :   public:
     767           0 :     bool Notify(Status aStatus) override
     768             :     {
     769             :       // We don't care about the notification. We just want to keep the
     770             :       // mWorkerPrivate alive.
     771           0 :       return true;
     772             :     }
     773             :   };
     774             : 
     775           0 :   UniquePtr<WorkerHolder> workerHolder(new SimpleWorkerHolder());
     776           0 :   if (NS_WARN_IF(!workerHolder->HoldWorker(mWorkerPrivate, Canceling))) {
     777           0 :     return false;
     778             :   }
     779             : 
     780           0 :   mWorkerHolder = Move(workerHolder);
     781           0 :   return true;
     782             : }
     783             : 
     784             : void
     785           0 : WorkerProxyToMainThreadRunnable::ReleaseWorker()
     786             : {
     787           0 :   mWorkerPrivate->AssertIsOnWorkerThread();
     788           0 :   MOZ_ASSERT(mWorkerHolder);
     789           0 :   mWorkerHolder = nullptr;
     790           0 : }

Generated by: LCOV version 1.13