LCOV - code coverage report
Current view: top level - dom/workers - WorkerDebuggerManager.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 34 141 24.1 %
Date: 2017-07-14 16:53:18 Functions: 6 34 17.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 "WorkerDebuggerManager.h"
       8             : 
       9             : #include "nsISimpleEnumerator.h"
      10             : 
      11             : #include "mozilla/ClearOnShutdown.h"
      12             : 
      13             : #include "WorkerPrivate.h"
      14             : 
      15             : USING_WORKERS_NAMESPACE
      16             : 
      17             : namespace {
      18             : 
      19             : class RegisterDebuggerMainThreadRunnable final : public mozilla::Runnable
      20             : {
      21             :   WorkerPrivate* mWorkerPrivate;
      22             :   bool mNotifyListeners;
      23             : 
      24             : public:
      25           0 :   RegisterDebuggerMainThreadRunnable(WorkerPrivate* aWorkerPrivate,
      26             :                                      bool aNotifyListeners)
      27           0 :     : mozilla::Runnable("RegisterDebuggerMainThreadRunnable")
      28             :     , mWorkerPrivate(aWorkerPrivate)
      29           0 :     , mNotifyListeners(aNotifyListeners)
      30           0 :   { }
      31             : 
      32             : private:
      33           0 :   ~RegisterDebuggerMainThreadRunnable()
      34           0 :   { }
      35             : 
      36             :   NS_IMETHOD
      37           0 :   Run() override
      38             :   {
      39           0 :     WorkerDebuggerManager* manager = WorkerDebuggerManager::Get();
      40           0 :     MOZ_ASSERT(manager);
      41             : 
      42           0 :     manager->RegisterDebuggerMainThread(mWorkerPrivate, mNotifyListeners);
      43           0 :     return NS_OK;
      44             :   }
      45             : };
      46             : 
      47             : class UnregisterDebuggerMainThreadRunnable final : public mozilla::Runnable
      48             : {
      49             :   WorkerPrivate* mWorkerPrivate;
      50             : 
      51             : public:
      52           0 :   explicit UnregisterDebuggerMainThreadRunnable(WorkerPrivate* aWorkerPrivate)
      53           0 :     : mozilla::Runnable("UnregisterDebuggerMainThreadRunnable")
      54           0 :     , mWorkerPrivate(aWorkerPrivate)
      55           0 :   { }
      56             : 
      57             : private:
      58           0 :   ~UnregisterDebuggerMainThreadRunnable()
      59           0 :   { }
      60             : 
      61             :   NS_IMETHOD
      62           0 :   Run() override
      63             :   {
      64           0 :     WorkerDebuggerManager* manager = WorkerDebuggerManager::Get();
      65           0 :     MOZ_ASSERT(manager);
      66             : 
      67           0 :     manager->UnregisterDebuggerMainThread(mWorkerPrivate);
      68           0 :     return NS_OK;
      69             :   }
      70             : };
      71             : 
      72             : // Does not hold an owning reference.
      73             : static WorkerDebuggerManager* gWorkerDebuggerManager;
      74             : 
      75             : } /* anonymous namespace */
      76             : 
      77             : BEGIN_WORKERS_NAMESPACE
      78             : 
      79             : class WorkerDebuggerEnumerator final : public nsISimpleEnumerator
      80             : {
      81             :   nsTArray<RefPtr<WorkerDebugger>> mDebuggers;
      82             :   uint32_t mIndex;
      83             : 
      84             : public:
      85           0 :   explicit WorkerDebuggerEnumerator(
      86             :                              const nsTArray<RefPtr<WorkerDebugger>>& aDebuggers)
      87           0 :   : mDebuggers(aDebuggers), mIndex(0)
      88             :   {
      89           0 :   }
      90             : 
      91             :   NS_DECL_ISUPPORTS
      92             :   NS_DECL_NSISIMPLEENUMERATOR
      93             : 
      94             : private:
      95           0 :   ~WorkerDebuggerEnumerator() {}
      96             : };
      97             : 
      98           0 : NS_IMPL_ISUPPORTS(WorkerDebuggerEnumerator, nsISimpleEnumerator);
      99             : 
     100             : NS_IMETHODIMP
     101           0 : WorkerDebuggerEnumerator::HasMoreElements(bool* aResult)
     102             : {
     103           0 :   *aResult = mIndex < mDebuggers.Length();
     104           0 :   return NS_OK;
     105             : };
     106             : 
     107             : NS_IMETHODIMP
     108           0 : WorkerDebuggerEnumerator::GetNext(nsISupports** aResult)
     109             : {
     110           0 :   if (mIndex == mDebuggers.Length()) {
     111           0 :     return NS_ERROR_FAILURE;
     112             :   }
     113             : 
     114           0 :   mDebuggers.ElementAt(mIndex++).forget(aResult);
     115           0 :   return NS_OK;
     116             : };
     117             : 
     118           1 : WorkerDebuggerManager::WorkerDebuggerManager()
     119           1 : : mMutex("WorkerDebuggerManager::mMutex")
     120             : {
     121           1 :   AssertIsOnMainThread();
     122           1 : }
     123             : 
     124           0 : WorkerDebuggerManager::~WorkerDebuggerManager()
     125             : {
     126           0 :   AssertIsOnMainThread();
     127           0 : }
     128             : 
     129             : // static
     130             : already_AddRefed<WorkerDebuggerManager>
     131           0 : WorkerDebuggerManager::GetInstance()
     132             : {
     133           0 :   RefPtr<WorkerDebuggerManager> manager = WorkerDebuggerManager::GetOrCreate();
     134           0 :   return manager.forget();
     135             : }
     136             : 
     137             : // static
     138             : WorkerDebuggerManager*
     139           1 : WorkerDebuggerManager::GetOrCreate()
     140             : {
     141           1 :   AssertIsOnMainThread();
     142             : 
     143           1 :   if (!gWorkerDebuggerManager) {
     144             :     // The observer service now owns us until shutdown.
     145           1 :     gWorkerDebuggerManager = new WorkerDebuggerManager();
     146           1 :     if (NS_FAILED(gWorkerDebuggerManager->Init())) {
     147           0 :       NS_WARNING("Failed to initialize worker debugger manager!");
     148           0 :       gWorkerDebuggerManager = nullptr;
     149           0 :       return nullptr;
     150             :     }
     151             :   }
     152             : 
     153           1 :   return gWorkerDebuggerManager;
     154             : }
     155             : 
     156             : WorkerDebuggerManager*
     157           0 : WorkerDebuggerManager::Get()
     158             : {
     159           0 :   MOZ_ASSERT(gWorkerDebuggerManager);
     160           0 :   return gWorkerDebuggerManager;
     161             : }
     162             : 
     163           1 : NS_IMPL_ISUPPORTS(WorkerDebuggerManager, nsIObserver, nsIWorkerDebuggerManager);
     164             : 
     165             : NS_IMETHODIMP
     166           0 : WorkerDebuggerManager::Observe(nsISupports* aSubject, const char* aTopic,
     167             :                                const char16_t* aData)
     168             : {
     169           0 :   if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
     170           0 :     Shutdown();
     171           0 :     return NS_OK;
     172             :   }
     173             : 
     174           0 :   NS_NOTREACHED("Unknown observer topic!");
     175           0 :   return NS_OK;
     176             : }
     177             : 
     178             : NS_IMETHODIMP
     179           0 : WorkerDebuggerManager::GetWorkerDebuggerEnumerator(
     180             :                                                   nsISimpleEnumerator** aResult)
     181             : {
     182           0 :   AssertIsOnMainThread();
     183             : 
     184             :   RefPtr<WorkerDebuggerEnumerator> enumerator =
     185           0 :     new WorkerDebuggerEnumerator(mDebuggers);
     186           0 :   enumerator.forget(aResult);
     187           0 :   return NS_OK;
     188             : }
     189             : 
     190             : NS_IMETHODIMP
     191           0 : WorkerDebuggerManager::AddListener(nsIWorkerDebuggerManagerListener* aListener)
     192             : {
     193           0 :   AssertIsOnMainThread();
     194             : 
     195           0 :   MutexAutoLock lock(mMutex);
     196             : 
     197           0 :   if (mListeners.Contains(aListener)) {
     198           0 :     return NS_ERROR_INVALID_ARG;
     199             :   }
     200             : 
     201           0 :   mListeners.AppendElement(aListener);
     202           0 :   return NS_OK;
     203             : }
     204             : 
     205             : NS_IMETHODIMP
     206           0 : WorkerDebuggerManager::RemoveListener(
     207             :                                     nsIWorkerDebuggerManagerListener* aListener)
     208             : {
     209           0 :   AssertIsOnMainThread();
     210             : 
     211           0 :   MutexAutoLock lock(mMutex);
     212             : 
     213           0 :   if (!mListeners.Contains(aListener)) {
     214           0 :     return NS_OK;
     215             :   }
     216             : 
     217           0 :   mListeners.RemoveElement(aListener);
     218           0 :   return NS_OK;
     219             : }
     220             : 
     221             : nsresult
     222           1 : WorkerDebuggerManager::Init()
     223             : {
     224           2 :   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
     225           1 :   NS_ENSURE_TRUE(obs, NS_ERROR_FAILURE);
     226             : 
     227           1 :   nsresult rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
     228           1 :   NS_ENSURE_SUCCESS(rv, rv);
     229             : 
     230           1 :   return NS_OK;
     231             : }
     232             : 
     233             : void
     234           0 : WorkerDebuggerManager::Shutdown()
     235             : {
     236           0 :   AssertIsOnMainThread();
     237             : 
     238           0 :   MutexAutoLock lock(mMutex);
     239             : 
     240           0 :   mListeners.Clear();
     241           0 : }
     242             : 
     243             : void
     244           1 : WorkerDebuggerManager::RegisterDebugger(WorkerPrivate* aWorkerPrivate)
     245             : {
     246           1 :   aWorkerPrivate->AssertIsOnParentThread();
     247             : 
     248           1 :   if (NS_IsMainThread()) {
     249             :     // When the parent thread is the main thread, it will always block until all
     250             :     // register liseners have been called, since it cannot continue until the
     251             :     // call to RegisterDebuggerMainThread returns.
     252             :     //
     253             :     // In this case, it is always safe to notify all listeners on the main
     254             :     // thread, even if there were no listeners at the time this method was
     255             :     // called, so we can always pass true for the value of aNotifyListeners.
     256             :     // This avoids having to lock mMutex to check whether mListeners is empty.
     257           1 :     RegisterDebuggerMainThread(aWorkerPrivate, true);
     258             :   } else {
     259             :     // We guarantee that if any register listeners are called, the worker does
     260             :     // not start running until all register listeners have been called. To
     261             :     // guarantee this, the parent thread should block until all register
     262             :     // listeners have been called.
     263             :     //
     264             :     // However, to avoid overhead when the debugger is not being used, the
     265             :     // parent thread will only block if there were any listeners at the time
     266             :     // this method was called. As a result, we should not notify any listeners
     267             :     // on the main thread if there were no listeners at the time this method was
     268             :     // called, because the parent will not be blocking in that case.
     269           0 :     bool hasListeners = false;
     270             :     {
     271           0 :       MutexAutoLock lock(mMutex);
     272             : 
     273           0 :       hasListeners = !mListeners.IsEmpty();
     274             :     }
     275             : 
     276             :     nsCOMPtr<nsIRunnable> runnable =
     277           0 :       new RegisterDebuggerMainThreadRunnable(aWorkerPrivate, hasListeners);
     278           0 :     MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL));
     279             : 
     280           0 :     if (hasListeners) {
     281           0 :       aWorkerPrivate->WaitForIsDebuggerRegistered(true);
     282             :     }
     283             :   }
     284           1 : }
     285             : 
     286             : void
     287           0 : WorkerDebuggerManager::UnregisterDebugger(WorkerPrivate* aWorkerPrivate)
     288             : {
     289           0 :   aWorkerPrivate->AssertIsOnParentThread();
     290             : 
     291           0 :   if (NS_IsMainThread()) {
     292           0 :     UnregisterDebuggerMainThread(aWorkerPrivate);
     293             :   } else {
     294             :     nsCOMPtr<nsIRunnable> runnable =
     295           0 :       new UnregisterDebuggerMainThreadRunnable(aWorkerPrivate);
     296           0 :     MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL));
     297             : 
     298           0 :     aWorkerPrivate->WaitForIsDebuggerRegistered(false);
     299             :   }
     300           0 : }
     301             : 
     302             : void
     303           1 : WorkerDebuggerManager::RegisterDebuggerMainThread(WorkerPrivate* aWorkerPrivate,
     304             :                                                   bool aNotifyListeners)
     305             : {
     306           1 :   AssertIsOnMainThread();
     307             : 
     308           2 :   RefPtr<WorkerDebugger> debugger = new WorkerDebugger(aWorkerPrivate);
     309           1 :   mDebuggers.AppendElement(debugger);
     310             : 
     311           1 :   aWorkerPrivate->SetDebugger(debugger);
     312             : 
     313           1 :   if (aNotifyListeners) {
     314           2 :     nsTArray<nsCOMPtr<nsIWorkerDebuggerManagerListener>> listeners;
     315             :     {
     316           2 :       MutexAutoLock lock(mMutex);
     317             : 
     318           1 :       listeners = mListeners;
     319             :     }
     320             : 
     321           1 :     for (size_t index = 0; index < listeners.Length(); ++index) {
     322           0 :       listeners[index]->OnRegister(debugger);
     323             :     }
     324             :   }
     325             : 
     326           1 :   aWorkerPrivate->SetIsDebuggerRegistered(true);
     327           1 : }
     328             : 
     329             : void
     330           0 : WorkerDebuggerManager::UnregisterDebuggerMainThread(
     331             :                                                   WorkerPrivate* aWorkerPrivate)
     332             : {
     333           0 :   AssertIsOnMainThread();
     334             : 
     335             :   // There is nothing to do here if the debugger was never succesfully
     336             :   // registered. We need to check this on the main thread because the worker
     337             :   // does not wait for the registration to complete if there were no listeners
     338             :   // installed when it started.
     339           0 :   if (!aWorkerPrivate->IsDebuggerRegistered()) {
     340           0 :     return;
     341             :   }
     342             : 
     343           0 :   RefPtr<WorkerDebugger> debugger = aWorkerPrivate->Debugger();
     344           0 :   mDebuggers.RemoveElement(debugger);
     345             : 
     346           0 :   aWorkerPrivate->SetDebugger(nullptr);
     347             : 
     348           0 :   nsTArray<nsCOMPtr<nsIWorkerDebuggerManagerListener>> listeners;
     349             :   {
     350           0 :     MutexAutoLock lock(mMutex);
     351             : 
     352           0 :     listeners = mListeners;
     353             :   }
     354             : 
     355           0 :   for (size_t index = 0; index < listeners.Length(); ++index) {
     356           0 :     listeners[index]->OnUnregister(debugger);
     357             :   }
     358             : 
     359           0 :   debugger->Close();
     360           0 :   aWorkerPrivate->SetIsDebuggerRegistered(false);
     361             : }
     362             : 
     363             : END_WORKERS_NAMESPACE

Generated by: LCOV version 1.13