LCOV - code coverage report
Current view: top level - xpcom/threads - nsThreadManager.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 92 153 60.1 %
Date: 2017-07-14 16:53:18 Functions: 17 23 73.9 %
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 "nsThreadManager.h"
       8             : #include "nsThread.h"
       9             : #include "nsThreadUtils.h"
      10             : #include "nsIClassInfoImpl.h"
      11             : #include "nsTArray.h"
      12             : #include "nsAutoPtr.h"
      13             : #include "mozilla/AbstractThread.h"
      14             : #include "mozilla/ThreadLocal.h"
      15             : #ifdef MOZ_CANARY
      16             : #include <fcntl.h>
      17             : #include <unistd.h>
      18             : #endif
      19             : 
      20             : #include "MainThreadIdlePeriod.h"
      21             : 
      22             : using namespace mozilla;
      23             : 
      24             : static MOZ_THREAD_LOCAL(bool) sTLSIsMainThread;
      25             : 
      26             : bool
      27      613613 : NS_IsMainThread()
      28             : {
      29      613613 :   return sTLSIsMainThread.get();
      30             : }
      31             : 
      32             : void
      33          11 : NS_SetMainThread()
      34             : {
      35          11 :   if (!sTLSIsMainThread.init()) {
      36           0 :     MOZ_CRASH();
      37             :   }
      38          11 :   sTLSIsMainThread.set(true);
      39          11 :   MOZ_ASSERT(NS_IsMainThread());
      40          11 : }
      41             : 
      42             : typedef nsTArray<NotNull<RefPtr<nsThread>>> nsThreadArray;
      43             : 
      44             : //-----------------------------------------------------------------------------
      45             : 
      46             : static void
      47           1 : ReleaseObject(void* aData)
      48             : {
      49           1 :   static_cast<nsISupports*>(aData)->Release();
      50           1 : }
      51             : 
      52             : // statically allocated instance
      53             : NS_IMETHODIMP_(MozExternalRefCountType)
      54          28 : nsThreadManager::AddRef()
      55             : {
      56          28 :   return 2;
      57             : }
      58             : NS_IMETHODIMP_(MozExternalRefCountType)
      59          15 : nsThreadManager::Release()
      60             : {
      61          15 :   return 1;
      62             : }
      63           3 : NS_IMPL_CLASSINFO(nsThreadManager, nullptr,
      64             :                   nsIClassInfo::THREADSAFE | nsIClassInfo::SINGLETON,
      65             :                   NS_THREADMANAGER_CID)
      66          68 : NS_IMPL_QUERY_INTERFACE_CI(nsThreadManager, nsIThreadManager)
      67           1 : NS_IMPL_CI_INTERFACE_GETTER(nsThreadManager, nsIThreadManager)
      68             : 
      69             : //-----------------------------------------------------------------------------
      70             : 
      71             : nsresult
      72           5 : nsThreadManager::Init()
      73             : {
      74             :   // Child processes need to initialize the thread manager before they
      75             :   // initialize XPCOM in order to set up the crash reporter. This leads to
      76             :   // situations where we get initialized twice.
      77           5 :   if (mInitialized) {
      78           2 :     return NS_OK;
      79             :   }
      80             : 
      81           3 :   if (PR_NewThreadPrivateIndex(&mCurThreadIndex, ReleaseObject) == PR_FAILURE) {
      82           0 :     return NS_ERROR_FAILURE;
      83             :   }
      84             : 
      85             : 
      86             : #ifdef MOZ_CANARY
      87             :   const int flags = O_WRONLY | O_APPEND | O_CREAT | O_NONBLOCK;
      88             :   const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
      89             :   char* env_var_flag = getenv("MOZ_KILL_CANARIES");
      90             :   sCanaryOutputFD =
      91             :     env_var_flag ? (env_var_flag[0] ? open(env_var_flag, flags, mode) :
      92             :                                       STDERR_FILENO) :
      93             :                    0;
      94             : #endif
      95             : 
      96             :   // Setup "main" thread
      97           3 :   mMainThread = new nsThread(nsThread::MAIN_THREAD, 0);
      98             : 
      99           3 :   nsresult rv = mMainThread->InitCurrentThread();
     100           3 :   if (NS_FAILED(rv)) {
     101           0 :     mMainThread = nullptr;
     102           0 :     return rv;
     103             :   }
     104             : 
     105             :   {
     106           6 :     nsCOMPtr<nsIIdlePeriod> idlePeriod = new MainThreadIdlePeriod();
     107           3 :     mMainThread->RegisterIdlePeriod(idlePeriod.forget());
     108             :   }
     109             : 
     110             :   // We need to keep a pointer to the current thread, so we can satisfy
     111             :   // GetIsMainThread calls that occur post-Shutdown.
     112           3 :   mMainThread->GetPRThread(&mMainPRThread);
     113             : 
     114             :   // Init AbstractThread.
     115           3 :   AbstractThread::InitTLS();
     116           3 :   AbstractThread::InitMainThread();
     117             : 
     118           3 :   mInitialized = true;
     119           3 :   return NS_OK;
     120             : }
     121             : 
     122             : void
     123           0 : nsThreadManager::Shutdown()
     124             : {
     125           0 :   MOZ_ASSERT(NS_IsMainThread(), "shutdown not called from main thread");
     126             : 
     127             :   // Prevent further access to the thread manager (no more new threads!)
     128             :   //
     129             :   // What happens if shutdown happens before NewThread completes?
     130             :   // We Shutdown() the new thread, and return error if we've started Shutdown
     131             :   // between when NewThread started, and when the thread finished initializing
     132             :   // and registering with ThreadManager.
     133             :   //
     134           0 :   mInitialized = false;
     135             : 
     136             :   // Empty the main thread event queue before we begin shutting down threads.
     137           0 :   NS_ProcessPendingEvents(mMainThread);
     138             : 
     139             :   // We gather the threads from the hashtable into a list, so that we avoid
     140             :   // holding the hashtable lock while calling nsIThread::Shutdown.
     141           0 :   nsThreadArray threads;
     142             :   {
     143           0 :     OffTheBooksMutexAutoLock lock(mLock);
     144           0 :     for (auto iter = mThreadsByPRThread.Iter(); !iter.Done(); iter.Next()) {
     145           0 :       RefPtr<nsThread>& thread = iter.Data();
     146           0 :       threads.AppendElement(WrapNotNull(thread));
     147           0 :       iter.Remove();
     148             :     }
     149             :   }
     150             : 
     151             :   // It's tempting to walk the list of threads here and tell them each to stop
     152             :   // accepting new events, but that could lead to badness if one of those
     153             :   // threads is stuck waiting for a response from another thread.  To do it
     154             :   // right, we'd need some way to interrupt the threads.
     155             :   //
     156             :   // Instead, we process events on the current thread while waiting for threads
     157             :   // to shutdown.  This means that we have to preserve a mostly functioning
     158             :   // world until such time as the threads exit.
     159             : 
     160             :   // Shutdown all threads that require it (join with threads that we created).
     161           0 :   for (uint32_t i = 0; i < threads.Length(); ++i) {
     162           0 :     NotNull<nsThread*> thread = threads[i];
     163           0 :     if (thread->ShutdownRequired()) {
     164           0 :       thread->Shutdown();
     165             :     }
     166             :   }
     167             : 
     168             :   // NB: It's possible that there are events in the queue that want to *start*
     169             :   // an asynchronous shutdown. But we have already shutdown the threads above,
     170             :   // so there's no need to worry about them. We only have to wait for all
     171             :   // in-flight asynchronous thread shutdowns to complete.
     172           0 :   mMainThread->WaitForAllAsynchronousShutdowns();
     173             : 
     174             :   // In case there are any more events somehow...
     175           0 :   NS_ProcessPendingEvents(mMainThread);
     176             : 
     177             :   // There are no more background threads at this point.
     178             : 
     179             :   // Clear the table of threads.
     180             :   {
     181           0 :     OffTheBooksMutexAutoLock lock(mLock);
     182           0 :     mThreadsByPRThread.Clear();
     183             :   }
     184             : 
     185             :   // Normally thread shutdown clears the observer for the thread, but since the
     186             :   // main thread is special we do it manually here after we're sure all events
     187             :   // have been processed.
     188           0 :   mMainThread->SetObserver(nullptr);
     189           0 :   mMainThread->ClearObservers();
     190             : 
     191             :   // Release main thread object.
     192           0 :   mMainThread = nullptr;
     193             : 
     194             :   // Remove the TLS entry for the main thread.
     195           0 :   PR_SetThreadPrivate(mCurThreadIndex, nullptr);
     196           0 : }
     197             : 
     198             : void
     199          66 : nsThreadManager::RegisterCurrentThread(nsThread& aThread)
     200             : {
     201          66 :   MOZ_ASSERT(aThread.GetPRThread() == PR_GetCurrentThread(), "bad aThread");
     202             : 
     203         132 :   OffTheBooksMutexAutoLock lock(mLock);
     204             : 
     205          66 :   ++mCurrentNumberOfThreads;
     206          66 :   if (mCurrentNumberOfThreads > mHighestNumberOfThreads) {
     207          65 :     mHighestNumberOfThreads = mCurrentNumberOfThreads;
     208             :   }
     209             : 
     210          66 :   mThreadsByPRThread.Put(aThread.GetPRThread(), &aThread);  // XXX check OOM?
     211             : 
     212          66 :   aThread.AddRef();  // for TLS entry
     213          66 :   PR_SetThreadPrivate(mCurThreadIndex, &aThread);
     214          66 : }
     215             : 
     216             : void
     217           1 : nsThreadManager::UnregisterCurrentThread(nsThread& aThread)
     218             : {
     219           1 :   MOZ_ASSERT(aThread.GetPRThread() == PR_GetCurrentThread(), "bad aThread");
     220             : 
     221           2 :   OffTheBooksMutexAutoLock lock(mLock);
     222             : 
     223           1 :   --mCurrentNumberOfThreads;
     224           1 :   mThreadsByPRThread.Remove(aThread.GetPRThread());
     225             : 
     226           1 :   PR_SetThreadPrivate(mCurThreadIndex, nullptr);
     227             :   // Ref-count balanced via ReleaseObject
     228           1 : }
     229             : 
     230             : nsThread*
     231         909 : nsThreadManager::GetCurrentThread()
     232             : {
     233             :   // read thread local storage
     234         909 :   void* data = PR_GetThreadPrivate(mCurThreadIndex);
     235         909 :   if (data) {
     236         899 :     return static_cast<nsThread*>(data);
     237             :   }
     238             : 
     239          10 :   if (!mInitialized) {
     240           3 :     return nullptr;
     241             :   }
     242             : 
     243             :   // OK, that's fine.  We'll dynamically create one :-)
     244          14 :   RefPtr<nsThread> thread = new nsThread(nsThread::NOT_MAIN_THREAD, 0);
     245           7 :   if (!thread || NS_FAILED(thread->InitCurrentThread())) {
     246           0 :     return nullptr;
     247             :   }
     248             : 
     249           7 :   return thread.get();  // reference held in TLS
     250             : }
     251             : 
     252             : NS_IMETHODIMP
     253           0 : nsThreadManager::NewThread(uint32_t aCreationFlags,
     254             :                            uint32_t aStackSize,
     255             :                            nsIThread** aResult)
     256             : {
     257           0 :   return NewNamedThread(NS_LITERAL_CSTRING(""), aStackSize, aResult);
     258             : }
     259             : 
     260             : NS_IMETHODIMP
     261          55 : nsThreadManager::NewNamedThread(const nsACString& aName,
     262             :                                 uint32_t aStackSize,
     263             :                                 nsIThread** aResult)
     264             : {
     265             :   // Note: can be called from arbitrary threads
     266             : 
     267             :   // No new threads during Shutdown
     268          55 :   if (NS_WARN_IF(!mInitialized)) {
     269           0 :     return NS_ERROR_NOT_INITIALIZED;
     270             :   }
     271             : 
     272         110 :   RefPtr<nsThread> thr = new nsThread(nsThread::NOT_MAIN_THREAD, aStackSize);
     273          55 :   nsresult rv = thr->Init(aName);  // Note: blocks until the new thread has been set up
     274          55 :   if (NS_FAILED(rv)) {
     275           0 :     return rv;
     276             :   }
     277             : 
     278             :   // At this point, we expect that the thread has been registered in mThreadByPRThread;
     279             :   // however, it is possible that it could have also been replaced by now, so
     280             :   // we cannot really assert that it was added.  Instead, kill it if we entered
     281             :   // Shutdown() during/before Init()
     282             : 
     283          55 :   if (NS_WARN_IF(!mInitialized)) {
     284           0 :     if (thr->ShutdownRequired()) {
     285           0 :       thr->Shutdown(); // ok if it happens multiple times
     286             :     }
     287           0 :     return NS_ERROR_NOT_INITIALIZED;
     288             :   }
     289             : 
     290          55 :   thr.forget(aResult);
     291          55 :   return NS_OK;
     292             : }
     293             : 
     294             : NS_IMETHODIMP
     295         646 : nsThreadManager::GetThreadFromPRThread(PRThread* aThread, nsIThread** aResult)
     296             : {
     297             :   // Keep this functioning during Shutdown
     298         646 :   if (NS_WARN_IF(!mMainThread)) {
     299           0 :     return NS_ERROR_NOT_INITIALIZED;
     300             :   }
     301         646 :   if (NS_WARN_IF(!aThread)) {
     302           0 :     return NS_ERROR_INVALID_ARG;
     303             :   }
     304             : 
     305        1292 :   RefPtr<nsThread> temp;
     306             :   {
     307        1292 :     OffTheBooksMutexAutoLock lock(mLock);
     308         646 :     mThreadsByPRThread.Get(aThread, getter_AddRefs(temp));
     309             :   }
     310             : 
     311         646 :   NS_IF_ADDREF(*aResult = temp);
     312         646 :   return NS_OK;
     313             : }
     314             : 
     315             : NS_IMETHODIMP
     316         798 : nsThreadManager::GetMainThread(nsIThread** aResult)
     317             : {
     318             :   // Keep this functioning during Shutdown
     319         798 :   if (NS_WARN_IF(!mMainThread)) {
     320           0 :     return NS_ERROR_NOT_INITIALIZED;
     321             :   }
     322         798 :   NS_ADDREF(*aResult = mMainThread);
     323         798 :   return NS_OK;
     324             : }
     325             : 
     326             : NS_IMETHODIMP
     327         739 : nsThreadManager::GetCurrentThread(nsIThread** aResult)
     328             : {
     329             :   // Keep this functioning during Shutdown
     330         739 :   if (NS_WARN_IF(!mMainThread)) {
     331           0 :     return NS_ERROR_NOT_INITIALIZED;
     332             :   }
     333         739 :   *aResult = GetCurrentThread();
     334         739 :   if (!*aResult) {
     335           0 :     return NS_ERROR_OUT_OF_MEMORY;
     336             :   }
     337         739 :   NS_ADDREF(*aResult);
     338         739 :   return NS_OK;
     339             : }
     340             : 
     341             : NS_IMETHODIMP
     342           0 : nsThreadManager::SpinEventLoopUntil(nsINestedEventLoopCondition* aCondition)
     343             : {
     344           0 :   nsCOMPtr<nsINestedEventLoopCondition> condition(aCondition);
     345           0 :   nsresult rv = NS_OK;
     346             : 
     347           0 :   if (!mozilla::SpinEventLoopUntil([&]() -> bool {
     348           0 :         bool isDone = false;
     349           0 :         rv = condition->IsDone(&isDone);
     350             :         // JS failure should be unusual, but we need to stop and propagate
     351             :         // the error back to the caller.
     352           0 :         if (NS_FAILED(rv)) {
     353           0 :           return true;
     354             :         }
     355             : 
     356           0 :         return isDone;
     357             :       })) {
     358             :     // We stopped early for some reason, which is unexpected.
     359           0 :     return NS_ERROR_UNEXPECTED;
     360             :   }
     361             : 
     362             :   // If we exited when the condition told us to, we need to return whether
     363             :   // the condition encountered failure when executing.
     364           0 :   return rv;
     365             : }
     366             : 
     367             : NS_IMETHODIMP
     368           0 : nsThreadManager::SpinEventLoopUntilEmpty()
     369             : {
     370           0 :   nsIThread* thread = NS_GetCurrentThread();
     371             : 
     372           0 :   while (NS_HasPendingEvents(thread)) {
     373           0 :     (void)NS_ProcessNextEvent(thread, false);
     374             :   }
     375             : 
     376           0 :   return NS_OK;
     377             : }
     378             : 
     379             : uint32_t
     380           0 : nsThreadManager::GetHighestNumberOfThreads()
     381             : {
     382           0 :   OffTheBooksMutexAutoLock lock(mLock);
     383           0 :   return mHighestNumberOfThreads;
     384             : }
     385             : 
     386             : NS_IMETHODIMP
     387           6 : nsThreadManager::DispatchToMainThread(nsIRunnable *aEvent)
     388             : {
     389             :   // Note: C++ callers should instead use NS_DispatchToMainThread.
     390           6 :   MOZ_ASSERT(NS_IsMainThread());
     391             : 
     392             :   // Keep this functioning during Shutdown
     393           6 :   if (NS_WARN_IF(!mMainThread)) {
     394           0 :     return NS_ERROR_NOT_INITIALIZED;
     395             :   }
     396             : 
     397           6 :   return mMainThread->DispatchFromScript(aEvent, 0);
     398             : }
     399             : 
     400             : NS_IMETHODIMP
     401           1 : nsThreadManager::IdleDispatchToMainThread(nsIRunnable *aEvent, uint32_t aTimeout)
     402             : {
     403             :   // Note: C++ callers should instead use NS_IdleDispatchToThread or
     404             :   // NS_IdleDispatchToCurrentThread.
     405           1 :   MOZ_ASSERT(NS_IsMainThread());
     406             : 
     407           2 :   nsCOMPtr<nsIRunnable> event(aEvent);
     408           1 :   if (aTimeout) {
     409           0 :     return NS_IdleDispatchToThread(event.forget(), aTimeout, mMainThread);
     410             :   }
     411             : 
     412           1 :   return NS_IdleDispatchToThread(event.forget(), mMainThread);
     413             : }

Generated by: LCOV version 1.13