dom/workers/WorkerRunnable.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "WorkerRunnable.h"
     8 #include "nsIEventTarget.h"
     9 #include "nsIRunnable.h"
    10 #include "nsThreadUtils.h"
    12 #include "mozilla/DebugOnly.h"
    14 #include "js/RootingAPI.h"
    15 #include "js/Value.h"
    17 #include "WorkerPrivate.h"
    19 USING_WORKERS_NAMESPACE
    21 namespace {
    23 const nsIID kWorkerRunnableIID = {
    24   0x320cc0b5, 0xef12, 0x4084, { 0x88, 0x6e, 0xca, 0x6a, 0x81, 0xe4, 0x1d, 0x68 }
    25 };
    27 void
    28 MaybeReportMainThreadException(JSContext* aCx, bool aResult)
    29 {
    30   AssertIsOnMainThread();
    32   if (aCx && !aResult) {
    33     JS_ReportPendingException(aCx);
    34   }
    35 }
    37 } // anonymous namespace
    39 #ifdef DEBUG
    40 WorkerRunnable::WorkerRunnable(WorkerPrivate* aWorkerPrivate,
    41                                TargetAndBusyBehavior aBehavior)
    42 : mWorkerPrivate(aWorkerPrivate), mBehavior(aBehavior), mCanceled(0),
    43   mCallingCancelWithinRun(false)
    44 {
    45   MOZ_ASSERT(aWorkerPrivate);
    46 }
    47 #endif
    49 bool
    50 WorkerRunnable::PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
    51 {
    52 #ifdef DEBUG
    53   MOZ_ASSERT(aWorkerPrivate);
    55   switch (mBehavior) {
    56     case ParentThreadUnchangedBusyCount:
    57       aWorkerPrivate->AssertIsOnWorkerThread();
    58       break;
    60     case WorkerThreadModifyBusyCount:
    61       aWorkerPrivate->AssertIsOnParentThread();
    62       MOZ_ASSERT(aCx);
    63       break;
    65     case WorkerThreadUnchangedBusyCount:
    66       aWorkerPrivate->AssertIsOnParentThread();
    67       break;
    69     default:
    70       MOZ_ASSUME_UNREACHABLE("Unknown behavior!");
    71   }
    72 #endif
    74   if (mBehavior == WorkerThreadModifyBusyCount) {
    75     return aWorkerPrivate->ModifyBusyCount(aCx, true);
    76   }
    78   return true;
    79 }
    81 bool
    82 WorkerRunnable::Dispatch(JSContext* aCx)
    83 {
    84   bool ok;
    86   if (!aCx) {
    87     ok = PreDispatch(nullptr, mWorkerPrivate);
    88     if (ok) {
    89       ok = DispatchInternal();
    90     }
    91     PostDispatch(nullptr, mWorkerPrivate, ok);
    92     return ok;
    93   }
    95   JSAutoRequest ar(aCx);
    97   JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
    99   Maybe<JSAutoCompartment> ac;
   100   if (global) {
   101     ac.construct(aCx, global);
   102   }
   104   ok = PreDispatch(aCx, mWorkerPrivate);
   106   if (ok && !DispatchInternal()) {
   107     ok = false;
   108   }
   110   PostDispatch(aCx, mWorkerPrivate, ok);
   112   return ok;
   113 }
   115 bool
   116 WorkerRunnable::DispatchInternal()
   117 {
   118   if (mBehavior == WorkerThreadModifyBusyCount ||
   119       mBehavior == WorkerThreadUnchangedBusyCount) {
   120     return NS_SUCCEEDED(mWorkerPrivate->Dispatch(this));
   121   }
   123   MOZ_ASSERT(mBehavior == ParentThreadUnchangedBusyCount);
   125   if (WorkerPrivate* parent = mWorkerPrivate->GetParent()) {
   126     return NS_SUCCEEDED(parent->Dispatch(this));
   127   }
   129   nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
   130   MOZ_ASSERT(mainThread);
   132   return NS_SUCCEEDED(mainThread->Dispatch(this, NS_DISPATCH_NORMAL));
   133 }
   135 void
   136 WorkerRunnable::PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
   137                              bool aDispatchResult)
   138 {
   139   MOZ_ASSERT(aWorkerPrivate);
   141 #ifdef DEBUG
   142   switch (mBehavior) {
   143     case ParentThreadUnchangedBusyCount:
   144       aWorkerPrivate->AssertIsOnWorkerThread();
   145       break;
   147     case WorkerThreadModifyBusyCount:
   148       aWorkerPrivate->AssertIsOnParentThread();
   149       MOZ_ASSERT(aCx);
   150       break;
   152     case WorkerThreadUnchangedBusyCount:
   153       aWorkerPrivate->AssertIsOnParentThread();
   154       break;
   156     default:
   157       MOZ_ASSUME_UNREACHABLE("Unknown behavior!");
   158   }
   159 #endif
   161   if (!aDispatchResult) {
   162     if (mBehavior == WorkerThreadModifyBusyCount) {
   163       aWorkerPrivate->ModifyBusyCount(aCx, false);
   164     }
   165     if (aCx) {
   166       JS_ReportPendingException(aCx);
   167     }
   168   }
   169 }
   171 void
   172 WorkerRunnable::PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
   173                         bool aRunResult)
   174 {
   175   MOZ_ASSERT(aCx);
   176   MOZ_ASSERT(aWorkerPrivate);
   178 #ifdef DEBUG
   179   switch (mBehavior) {
   180     case ParentThreadUnchangedBusyCount:
   181       aWorkerPrivate->AssertIsOnParentThread();
   182       break;
   184     case WorkerThreadModifyBusyCount:
   185       aWorkerPrivate->AssertIsOnWorkerThread();
   186       break;
   188     case WorkerThreadUnchangedBusyCount:
   189       aWorkerPrivate->AssertIsOnWorkerThread();
   190       break;
   192     default:
   193       MOZ_ASSUME_UNREACHABLE("Unknown behavior!");
   194   }
   195 #endif
   197   if (mBehavior == WorkerThreadModifyBusyCount) {
   198     if (!aWorkerPrivate->ModifyBusyCountFromWorker(aCx, false)) {
   199       aRunResult = false;
   200     }
   201   }
   203   if (!aRunResult) {
   204     JS_ReportPendingException(aCx);
   205   }
   206 }
   208 // static
   209 WorkerRunnable*
   210 WorkerRunnable::FromRunnable(nsIRunnable* aRunnable)
   211 {
   212   MOZ_ASSERT(aRunnable);
   214   WorkerRunnable* runnable;
   215   nsresult rv = aRunnable->QueryInterface(kWorkerRunnableIID,
   216                                           reinterpret_cast<void**>(&runnable));
   217   if (NS_FAILED(rv)) {
   218     return nullptr;
   219   }
   221   MOZ_ASSERT(runnable);
   222   return runnable;
   223 }
   225 NS_IMPL_ADDREF(WorkerRunnable)
   226 NS_IMPL_RELEASE(WorkerRunnable)
   228 NS_INTERFACE_MAP_BEGIN(WorkerRunnable)
   229   NS_INTERFACE_MAP_ENTRY(nsIRunnable)
   230   NS_INTERFACE_MAP_ENTRY(nsICancelableRunnable)
   231   NS_INTERFACE_MAP_ENTRY(nsISupports)
   232   // kWorkerRunnableIID is special in that it does not AddRef its result.
   233   if (aIID.Equals(kWorkerRunnableIID)) {
   234     *aInstancePtr = this;
   235     return NS_OK;
   236   }
   237   else
   238 NS_INTERFACE_MAP_END
   240 NS_IMETHODIMP
   241 WorkerRunnable::Run()
   242 {
   243   bool targetIsWorkerThread = mBehavior == WorkerThreadModifyBusyCount ||
   244                               mBehavior == WorkerThreadUnchangedBusyCount;
   246 #ifdef DEBUG
   247   MOZ_ASSERT_IF(mCallingCancelWithinRun, targetIsWorkerThread);
   248   if (targetIsWorkerThread) {
   249     mWorkerPrivate->AssertIsOnWorkerThread();
   250   }
   251   else {
   252     MOZ_ASSERT(mBehavior == ParentThreadUnchangedBusyCount);
   253     mWorkerPrivate->AssertIsOnParentThread();
   254   }
   255 #endif
   257   if (IsCanceled() && !mCallingCancelWithinRun) {
   258     return NS_OK;
   259   }
   261   JSContext* cx;
   262   nsRefPtr<WorkerPrivate> kungFuDeathGrip;
   263   nsCxPusher pusher;
   265   if (targetIsWorkerThread) {
   266     if (mWorkerPrivate->AllPendingRunnablesShouldBeCanceled() &&
   267         !IsCanceled() &&
   268         !mCallingCancelWithinRun) {
   270       // Prevent recursion.
   271       mCallingCancelWithinRun = true;
   273       Cancel();
   275       MOZ_ASSERT(mCallingCancelWithinRun);
   276       mCallingCancelWithinRun = false;
   278       MOZ_ASSERT(IsCanceled(), "Subclass Cancel() didn't set IsCanceled()!");
   280       return NS_OK;
   281     }
   283     cx = mWorkerPrivate->GetJSContext();
   284     MOZ_ASSERT(cx);
   285   }
   286   else {
   287     cx = mWorkerPrivate->ParentJSContext();
   288     MOZ_ASSERT(cx);
   290     kungFuDeathGrip = mWorkerPrivate;
   292     if (!mWorkerPrivate->GetParent()) {
   293       AssertIsOnMainThread();
   294       pusher.Push(cx);
   295     }
   296   }
   298   JSAutoRequest ar(cx);
   300   JS::Rooted<JSObject*> targetCompartmentObject(cx);
   301   if (targetIsWorkerThread) {
   302     targetCompartmentObject = JS::CurrentGlobalOrNull(cx);
   303   } else {
   304     targetCompartmentObject = mWorkerPrivate->GetWrapper();
   305   }
   307   Maybe<JSAutoCompartment> ac;
   308   if (targetCompartmentObject) {
   309     ac.construct(cx, targetCompartmentObject);
   310   }
   312   bool result = WorkerRun(cx, mWorkerPrivate);
   314   // In the case of CompileScriptRunnnable, WorkerRun above can cause us to
   315   // lazily create a global, in which case we need to be in its compartment
   316   // when calling PostRun() below. Maybe<> this time...
   317   if (targetIsWorkerThread &&
   318       ac.empty() &&
   319       js::DefaultObjectForContextOrNull(cx)) {
   320     ac.construct(cx, js::DefaultObjectForContextOrNull(cx));
   321   }
   323   PostRun(cx, mWorkerPrivate, result);
   325   return result ? NS_OK : NS_ERROR_FAILURE;
   326 }
   328 NS_IMETHODIMP
   329 WorkerRunnable::Cancel()
   330 {
   331   uint32_t canceledCount = ++mCanceled;
   333   MOZ_ASSERT(canceledCount, "Cancel() overflow!");
   335   // The docs say that Cancel() should not be called more than once and that we
   336   // should throw NS_ERROR_UNEXPECTED if it is.
   337   return (canceledCount == 1) ? NS_OK : NS_ERROR_UNEXPECTED;
   338 }
   340 WorkerSyncRunnable::WorkerSyncRunnable(WorkerPrivate* aWorkerPrivate,
   341                                        nsIEventTarget* aSyncLoopTarget)
   342 : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
   343   mSyncLoopTarget(aSyncLoopTarget)
   344 {
   345 #ifdef DEBUG
   346   if (mSyncLoopTarget) {
   347     mWorkerPrivate->AssertValidSyncLoop(mSyncLoopTarget);
   348   }
   349 #endif
   350 }
   352 WorkerSyncRunnable::WorkerSyncRunnable(
   353                                WorkerPrivate* aWorkerPrivate,
   354                                already_AddRefed<nsIEventTarget>&& aSyncLoopTarget)
   355 : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
   356   mSyncLoopTarget(aSyncLoopTarget)
   357 {
   358 #ifdef DEBUG
   359   if (mSyncLoopTarget) {
   360     mWorkerPrivate->AssertValidSyncLoop(mSyncLoopTarget);
   361   }
   362 #endif
   363 }
   365 WorkerSyncRunnable::~WorkerSyncRunnable()
   366 {
   367 }
   369 bool
   370 WorkerSyncRunnable::DispatchInternal()
   371 {
   372   if (mSyncLoopTarget) {
   373     return NS_SUCCEEDED(mSyncLoopTarget->Dispatch(this, NS_DISPATCH_NORMAL));
   374   }
   376   return WorkerRunnable::DispatchInternal();
   377 }
   379 void
   380 MainThreadWorkerSyncRunnable::PostDispatch(JSContext* aCx,
   381                                            WorkerPrivate* aWorkerPrivate,
   382                                            bool aDispatchResult)
   383 {
   384   MaybeReportMainThreadException(aCx, aDispatchResult);
   385 }
   387 StopSyncLoopRunnable::StopSyncLoopRunnable(
   388                                WorkerPrivate* aWorkerPrivate,
   389                                already_AddRefed<nsIEventTarget>&& aSyncLoopTarget,
   390                                bool aResult)
   391 : WorkerSyncRunnable(aWorkerPrivate, Move(aSyncLoopTarget)), mResult(aResult)
   392 {
   393 #ifdef DEBUG
   394   mWorkerPrivate->AssertValidSyncLoop(mSyncLoopTarget);
   395 #endif
   396 }
   398 NS_IMETHODIMP
   399 StopSyncLoopRunnable::Cancel()
   400 {
   401   nsresult rv = Run();
   402   NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Run() failed");
   404   nsresult rv2 = WorkerSyncRunnable::Cancel();
   405   NS_WARN_IF_FALSE(NS_SUCCEEDED(rv2), "Cancel() failed");
   407   return NS_FAILED(rv) ? rv : rv2;
   408 }
   410 bool
   411 StopSyncLoopRunnable::WorkerRun(JSContext* aCx,
   412                                 WorkerPrivate* aWorkerPrivate)
   413 {
   414   aWorkerPrivate->AssertIsOnWorkerThread();
   415   MOZ_ASSERT(mSyncLoopTarget);
   417   nsCOMPtr<nsIEventTarget> syncLoopTarget;
   418   mSyncLoopTarget.swap(syncLoopTarget);
   420   if (!mResult) {
   421     MaybeSetException(aCx);
   422   }
   424   aWorkerPrivate->StopSyncLoop(syncLoopTarget, mResult);
   425   return true;
   426 }
   428 bool
   429 StopSyncLoopRunnable::DispatchInternal()
   430 {
   431   MOZ_ASSERT(mSyncLoopTarget);
   433   return NS_SUCCEEDED(mSyncLoopTarget->Dispatch(this, NS_DISPATCH_NORMAL));
   434 }
   436 void
   437 MainThreadStopSyncLoopRunnable::PostDispatch(JSContext* aCx,
   438                                              WorkerPrivate* aWorkerPrivate,
   439                                              bool aDispatchResult)
   440 {
   441   MaybeReportMainThreadException(aCx, aDispatchResult);
   442 }
   444 #ifdef DEBUG
   445 WorkerControlRunnable::WorkerControlRunnable(WorkerPrivate* aWorkerPrivate,
   446                                              TargetAndBusyBehavior aBehavior)
   447 : WorkerRunnable(aWorkerPrivate, aBehavior)
   448 {
   449   MOZ_ASSERT(aWorkerPrivate);
   450   MOZ_ASSERT(aBehavior == ParentThreadUnchangedBusyCount ||
   451              aBehavior == WorkerThreadUnchangedBusyCount,
   452              "WorkerControlRunnables should not modify the busy count");
   453 }
   454 #endif
   456 bool
   457 WorkerControlRunnable::DispatchInternal()
   458 {
   459   if (mBehavior == WorkerThreadUnchangedBusyCount) {
   460     return NS_SUCCEEDED(mWorkerPrivate->DispatchControlRunnable(this));
   461   }
   463   if (WorkerPrivate* parent = mWorkerPrivate->GetParent()) {
   464     return NS_SUCCEEDED(parent->DispatchControlRunnable(this));
   465   }
   467   nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
   468   MOZ_ASSERT(mainThread);
   470   return NS_SUCCEEDED(mainThread->Dispatch(this, NS_DISPATCH_NORMAL));
   471 }
   473 void
   474 MainThreadWorkerControlRunnable::PostDispatch(JSContext* aCx,
   475                                               WorkerPrivate* aWorkerPrivate,
   476                                               bool aDispatchResult)
   477 {
   478   AssertIsOnMainThread();
   480   if (aCx && !aDispatchResult) {
   481     JS_ReportPendingException(aCx);
   482   }
   483 }
   485 NS_IMPL_ISUPPORTS_INHERITED0(WorkerControlRunnable, WorkerRunnable)
   487 bool
   488 WorkerSameThreadRunnable::PreDispatch(JSContext* aCx,
   489                                       WorkerPrivate* aWorkerPrivate)
   490 {
   491   aWorkerPrivate->AssertIsOnWorkerThread();
   492   return true;
   493 }
   495 void
   496 WorkerSameThreadRunnable::PostDispatch(JSContext* aCx,
   497                                        WorkerPrivate* aWorkerPrivate,
   498                                        bool aDispatchResult)
   499 {
   500   aWorkerPrivate->AssertIsOnWorkerThread();
   501   if (aDispatchResult) {
   502     DebugOnly<bool> willIncrement = aWorkerPrivate->ModifyBusyCountFromWorker(aCx, true);
   503     // Should never fail since if this thread is still running, so should the
   504     // parent and it should be able to process a control runnable.
   505     MOZ_ASSERT(willIncrement);
   506   }
   507 }
   509 void
   510 WorkerSameThreadRunnable::PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
   511                                   bool aRunResult)
   512 {
   513   MOZ_ASSERT(aCx);
   514   MOZ_ASSERT(aWorkerPrivate);
   516   aWorkerPrivate->AssertIsOnWorkerThread();
   518   DebugOnly<bool> willDecrement = aWorkerPrivate->ModifyBusyCountFromWorker(aCx, false);
   519   MOZ_ASSERT(willDecrement);
   521   if (!aRunResult) {
   522     JS_ReportPendingException(aCx);
   523   }
   524 }

mercurial