LCOV - code coverage report
Current view: top level - dom/workers - WorkerThread.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 79 132 59.8 %
Date: 2017-07-14 16:53:18 Functions: 17 24 70.8 %
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 "WorkerThread.h"
       8             : 
       9             : #include "mozilla/Assertions.h"
      10             : #include "mozilla/ipc/BackgroundChild.h"
      11             : #include "nsIThreadInternal.h"
      12             : #include "WorkerPrivate.h"
      13             : #include "WorkerRunnable.h"
      14             : 
      15             : #ifdef DEBUG
      16             : #include "nsThreadManager.h"
      17             : #endif
      18             : 
      19             : namespace mozilla {
      20             : namespace dom {
      21             : namespace workers {
      22             : 
      23             : using namespace mozilla::ipc;
      24             : 
      25             : namespace {
      26             : 
      27             : // The C stack size. We use the same stack size on all platforms for
      28             : // consistency.
      29             : const uint32_t kWorkerStackSize = 256 * sizeof(size_t) * 1024;
      30             : 
      31             : } // namespace
      32             : 
      33           2 : WorkerThreadFriendKey::WorkerThreadFriendKey()
      34             : {
      35           2 :   MOZ_COUNT_CTOR(WorkerThreadFriendKey);
      36           2 : }
      37             : 
      38           2 : WorkerThreadFriendKey::~WorkerThreadFriendKey()
      39             : {
      40           2 :   MOZ_COUNT_DTOR(WorkerThreadFriendKey);
      41           2 : }
      42             : 
      43             : class WorkerThread::Observer final
      44             :   : public nsIThreadObserver
      45             : {
      46             :   WorkerPrivate* mWorkerPrivate;
      47             : 
      48             : public:
      49           1 :   explicit Observer(WorkerPrivate* aWorkerPrivate)
      50           1 :   : mWorkerPrivate(aWorkerPrivate)
      51             :   {
      52           1 :     MOZ_ASSERT(aWorkerPrivate);
      53           1 :     aWorkerPrivate->AssertIsOnWorkerThread();
      54           1 :   }
      55             : 
      56             :   NS_DECL_THREADSAFE_ISUPPORTS
      57             : 
      58             : private:
      59           0 :   ~Observer()
      60           0 :   {
      61           0 :     mWorkerPrivate->AssertIsOnWorkerThread();
      62           0 :   }
      63             : 
      64             :   NS_DECL_NSITHREADOBSERVER
      65             : };
      66             : 
      67           1 : WorkerThread::WorkerThread()
      68             :   : nsThread(nsThread::NOT_MAIN_THREAD, kWorkerStackSize)
      69             :   , mWorkerPrivateCondVar(mLock, "WorkerThread::mWorkerPrivateCondVar")
      70             :   , mWorkerPrivate(nullptr)
      71             :   , mOtherThreadsDispatchingViaEventTarget(0)
      72             : #ifdef DEBUG
      73           1 :   , mAcceptingNonWorkerRunnables(true)
      74             : #endif
      75             : {
      76           1 : }
      77             : 
      78           0 : WorkerThread::~WorkerThread()
      79             : {
      80           0 :   MOZ_ASSERT(!mWorkerPrivate);
      81           0 :   MOZ_ASSERT(!mOtherThreadsDispatchingViaEventTarget);
      82           0 :   MOZ_ASSERT(mAcceptingNonWorkerRunnables);
      83           0 : }
      84             : 
      85             : // static
      86             : already_AddRefed<WorkerThread>
      87           1 : WorkerThread::Create(const WorkerThreadFriendKey& /* aKey */)
      88             : {
      89           2 :   RefPtr<WorkerThread> thread = new WorkerThread();
      90           1 :   if (NS_FAILED(thread->Init())) {
      91           0 :     NS_WARNING("Failed to create new thread!");
      92           0 :     return nullptr;
      93             :   }
      94             : 
      95           1 :   return thread.forget();
      96             : }
      97             : 
      98             : void
      99           1 : WorkerThread::SetWorker(const WorkerThreadFriendKey& /* aKey */,
     100             :                         WorkerPrivate* aWorkerPrivate)
     101             : {
     102           1 :   MOZ_ASSERT(PR_GetCurrentThread() == mThread);
     103             : 
     104           1 :   if (aWorkerPrivate) {
     105             :     {
     106           2 :       MutexAutoLock lock(mLock);
     107             : 
     108           1 :       MOZ_ASSERT(!mWorkerPrivate);
     109           1 :       MOZ_ASSERT(mAcceptingNonWorkerRunnables);
     110             : 
     111           1 :       mWorkerPrivate = aWorkerPrivate;
     112             : #ifdef DEBUG
     113           1 :       mAcceptingNonWorkerRunnables = false;
     114             : #endif
     115             :     }
     116             : 
     117           1 :     mObserver = new Observer(aWorkerPrivate);
     118           1 :     MOZ_ALWAYS_SUCCEEDS(AddObserver(mObserver));
     119             :   } else {
     120           0 :     MOZ_ALWAYS_SUCCEEDS(RemoveObserver(mObserver));
     121           0 :     mObserver = nullptr;
     122             : 
     123             :     {
     124           0 :       MutexAutoLock lock(mLock);
     125             : 
     126           0 :       MOZ_ASSERT(mWorkerPrivate);
     127           0 :       MOZ_ASSERT(!mAcceptingNonWorkerRunnables);
     128           0 :       MOZ_ASSERT(!mOtherThreadsDispatchingViaEventTarget,
     129             :                  "XPCOM Dispatch hapenning at the same time our thread is "
     130             :                  "being unset! This should not be possible!");
     131             : 
     132           0 :       while (mOtherThreadsDispatchingViaEventTarget) {
     133           0 :         mWorkerPrivateCondVar.Wait();
     134             :       }
     135             : 
     136             : #ifdef DEBUG
     137           0 :       mAcceptingNonWorkerRunnables = true;
     138             : #endif
     139           0 :       mWorkerPrivate = nullptr;
     140             :     }
     141             :   }
     142           1 : }
     143             : 
     144             : nsresult
     145           1 : WorkerThread::DispatchPrimaryRunnable(const WorkerThreadFriendKey& /* aKey */,
     146             :                                       already_AddRefed<nsIRunnable> aRunnable)
     147             : {
     148           2 :   nsCOMPtr<nsIRunnable> runnable(aRunnable);
     149             : 
     150             : #ifdef DEBUG
     151           1 :   MOZ_ASSERT(PR_GetCurrentThread() != mThread);
     152           1 :   MOZ_ASSERT(runnable);
     153             :   {
     154           2 :     MutexAutoLock lock(mLock);
     155             : 
     156           1 :     MOZ_ASSERT(!mWorkerPrivate);
     157           1 :     MOZ_ASSERT(mAcceptingNonWorkerRunnables);
     158             :   }
     159             : #endif
     160             : 
     161           1 :   nsresult rv = nsThread::Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
     162           1 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     163           0 :     return rv;
     164             :   }
     165             : 
     166           1 :   return NS_OK;
     167             : }
     168             : 
     169             : nsresult
     170           2 : WorkerThread::DispatchAnyThread(const WorkerThreadFriendKey& /* aKey */,
     171             :                        already_AddRefed<WorkerRunnable> aWorkerRunnable)
     172             : {
     173             :   // May be called on any thread!
     174             : 
     175             : #ifdef DEBUG
     176             :   {
     177           2 :     const bool onWorkerThread = PR_GetCurrentThread() == mThread;
     178             :     {
     179           4 :       MutexAutoLock lock(mLock);
     180             : 
     181           2 :       MOZ_ASSERT(mWorkerPrivate);
     182           2 :       MOZ_ASSERT(!mAcceptingNonWorkerRunnables);
     183             : 
     184           2 :       if (onWorkerThread) {
     185           2 :         mWorkerPrivate->AssertIsOnWorkerThread();
     186             :       }
     187             :     }
     188             :   }
     189             : #endif
     190           4 :   nsCOMPtr<nsIRunnable> runnable(aWorkerRunnable);
     191             : 
     192           2 :   nsresult rv = nsThread::Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
     193           2 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     194           0 :     return rv;
     195             :   }
     196             : 
     197             :   // We don't need to notify the worker's condition variable here because we're
     198             :   // being called from worker-controlled code and it will make sure to wake up
     199             :   // the worker thread if needed.
     200             : 
     201           2 :   return NS_OK;
     202             : }
     203             : 
     204        4686 : NS_IMPL_ISUPPORTS_INHERITED0(WorkerThread, nsThread)
     205             : 
     206             : NS_IMETHODIMP
     207           0 : WorkerThread::DispatchFromScript(nsIRunnable* aRunnable, uint32_t aFlags)
     208             : {
     209           0 :   nsCOMPtr<nsIRunnable> runnable(aRunnable);
     210           0 :   return Dispatch(runnable.forget(), aFlags);
     211             : }
     212             : 
     213             : NS_IMETHODIMP
     214           1 : WorkerThread::Dispatch(already_AddRefed<nsIRunnable> aRunnable, uint32_t aFlags)
     215             : {
     216             :   // May be called on any thread!
     217           2 :   nsCOMPtr<nsIRunnable> runnable(aRunnable); // in case we exit early
     218             : 
     219             :   // Workers only support asynchronous dispatch.
     220           1 :   if (NS_WARN_IF(aFlags != NS_DISPATCH_NORMAL)) {
     221           0 :     return NS_ERROR_UNEXPECTED;
     222             :   }
     223             : 
     224           1 :   const bool onWorkerThread = PR_GetCurrentThread() == mThread;
     225             : 
     226             : #ifdef DEBUG
     227           1 :   if (runnable && !onWorkerThread) {
     228           2 :     nsCOMPtr<nsICancelableRunnable> cancelable = do_QueryInterface(runnable);
     229             : 
     230             :     {
     231           2 :       MutexAutoLock lock(mLock);
     232             : 
     233             :       // Only enforce cancelable runnables after we've started the worker loop.
     234           1 :       if (!mAcceptingNonWorkerRunnables) {
     235           0 :         MOZ_ASSERT(cancelable,
     236             :                    "Only nsICancelableRunnable may be dispatched to a worker!");
     237             :       }
     238             :     }
     239             :   }
     240             : #endif
     241             : 
     242           1 :   WorkerPrivate* workerPrivate = nullptr;
     243           1 :   if (onWorkerThread) {
     244             :     // No need to lock here because it is only modified on this thread.
     245           0 :     MOZ_ASSERT(mWorkerPrivate);
     246           0 :     mWorkerPrivate->AssertIsOnWorkerThread();
     247             : 
     248           0 :     workerPrivate = mWorkerPrivate;
     249             :   } else {
     250           2 :     MutexAutoLock lock(mLock);
     251             : 
     252           1 :     MOZ_ASSERT(mOtherThreadsDispatchingViaEventTarget < UINT32_MAX);
     253             : 
     254           1 :     if (mWorkerPrivate) {
     255           0 :       workerPrivate = mWorkerPrivate;
     256             : 
     257             :       // Incrementing this counter will make the worker thread sleep if it
     258             :       // somehow tries to unset mWorkerPrivate while we're using it.
     259           0 :       mOtherThreadsDispatchingViaEventTarget++;
     260             :     }
     261             :   }
     262             : 
     263             :   nsresult rv;
     264           1 :   if (runnable && onWorkerThread) {
     265           0 :     RefPtr<WorkerRunnable> workerRunnable = workerPrivate->MaybeWrapAsWorkerRunnable(runnable.forget());
     266           0 :     rv = nsThread::Dispatch(workerRunnable.forget(), NS_DISPATCH_NORMAL);
     267             :   } else {
     268           1 :     rv = nsThread::Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
     269             :   }
     270             : 
     271           1 :   if (!onWorkerThread && workerPrivate) {
     272             :     // We need to wake the worker thread if we're not already on the right
     273             :     // thread and the dispatch succeeded.
     274           0 :     if (NS_SUCCEEDED(rv)) {
     275           0 :       MutexAutoLock workerLock(workerPrivate->mMutex);
     276             : 
     277           0 :       workerPrivate->mCondVar.Notify();
     278             :     }
     279             : 
     280             :     // Now unset our waiting flag.
     281             :     {
     282           0 :       MutexAutoLock lock(mLock);
     283             : 
     284           0 :       MOZ_ASSERT(mOtherThreadsDispatchingViaEventTarget);
     285             : 
     286           0 :       if (!--mOtherThreadsDispatchingViaEventTarget) {
     287           0 :         mWorkerPrivateCondVar.Notify();
     288             :       }
     289             :     }
     290             :   }
     291             : 
     292           1 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     293           0 :     return rv;
     294             :   }
     295             : 
     296           1 :   return NS_OK;
     297             : }
     298             : 
     299             : NS_IMETHODIMP
     300           0 : WorkerThread::DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t)
     301             : {
     302           0 :   return NS_ERROR_NOT_IMPLEMENTED;
     303             : }
     304             : 
     305             : uint32_t
     306           0 : WorkerThread::RecursionDepth(const WorkerThreadFriendKey& /* aKey */) const
     307             : {
     308           0 :   MOZ_ASSERT(PR_GetCurrentThread() == mThread);
     309             : 
     310           0 :   return mNestedEventLoopDepth;
     311             : }
     312             : 
     313         137 : NS_IMPL_ISUPPORTS(WorkerThread::Observer, nsIThreadObserver)
     314             : 
     315             : NS_IMETHODIMP
     316           0 : WorkerThread::Observer::OnDispatchedEvent(nsIThreadInternal* /* aThread */)
     317             : {
     318           0 :   MOZ_CRASH("OnDispatchedEvent() should never be called!");
     319             : }
     320             : 
     321             : NS_IMETHODIMP
     322          35 : WorkerThread::Observer::OnProcessNextEvent(nsIThreadInternal* /* aThread */,
     323             :                                            bool aMayWait)
     324             : {
     325          35 :   mWorkerPrivate->AssertIsOnWorkerThread();
     326             : 
     327             :   // If the PBackground child is not created yet, then we must permit
     328             :   // blocking event processing to support
     329             :   // BackgroundChild::SynchronouslyCreateForCurrentThread(). If this occurs
     330             :   // then we are spinning on the event queue at the start of
     331             :   // PrimaryWorkerRunnable::Run() and don't want to process the event in
     332             :   // mWorkerPrivate yet.
     333          35 :   if (aMayWait) {
     334           0 :     MOZ_ASSERT(CycleCollectedJSContext::Get()->RecursionDepth() == 2);
     335           0 :     MOZ_ASSERT(!BackgroundChild::GetForCurrentThread());
     336           0 :     return NS_OK;
     337             :   }
     338             : 
     339          35 :   mWorkerPrivate->OnProcessNextEvent();
     340          35 :   return NS_OK;
     341             : }
     342             : 
     343             : NS_IMETHODIMP
     344          31 : WorkerThread::Observer::AfterProcessNextEvent(nsIThreadInternal* /* aThread */,
     345             :                                               bool /* aEventWasProcessed */)
     346             : {
     347          31 :   mWorkerPrivate->AssertIsOnWorkerThread();
     348             : 
     349          31 :   mWorkerPrivate->AfterProcessNextEvent();
     350          31 :   return NS_OK;
     351             : }
     352             : 
     353             : } // namespace workers
     354             : } // namespace dom
     355             : } // namespace mozilla

Generated by: LCOV version 1.13