xpcom/threads/nsThreadManager.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++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
     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/. */
     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 #ifdef MOZ_CANARY
    14 #include <fcntl.h>
    15 #include <unistd.h>
    16 #endif
    18 using namespace mozilla;
    20 #ifdef XP_WIN
    21 #include <windows.h>
    22 DWORD gTLSThreadIDIndex = TlsAlloc();
    23 #elif defined(NS_TLS)
    24 NS_TLS mozilla::threads::ID gTLSThreadID = mozilla::threads::Generic;
    25 #endif
    27 typedef nsTArray< nsRefPtr<nsThread> > nsThreadArray;
    29 //-----------------------------------------------------------------------------
    31 static void
    32 ReleaseObject(void *data)
    33 {
    34   static_cast<nsISupports *>(data)->Release();
    35 }
    37 static PLDHashOperator
    38 AppendAndRemoveThread(PRThread *key, nsRefPtr<nsThread> &thread, void *arg)
    39 {
    40   nsThreadArray *threads = static_cast<nsThreadArray *>(arg);
    41   threads->AppendElement(thread);
    42   return PL_DHASH_REMOVE;
    43 }
    45 // statically allocated instance
    46 NS_IMETHODIMP_(MozExternalRefCountType) nsThreadManager::AddRef() { return 2; }
    47 NS_IMETHODIMP_(MozExternalRefCountType) nsThreadManager::Release() { return 1; }
    48 NS_IMPL_CLASSINFO(nsThreadManager, nullptr,
    49                   nsIClassInfo::THREADSAFE | nsIClassInfo::SINGLETON,
    50                   NS_THREADMANAGER_CID)
    51 NS_IMPL_QUERY_INTERFACE_CI(nsThreadManager, nsIThreadManager)
    52 NS_IMPL_CI_INTERFACE_GETTER(nsThreadManager, nsIThreadManager)
    54 //-----------------------------------------------------------------------------
    56 nsresult
    57 nsThreadManager::Init()
    58 {
    59   // Child processes need to initialize the thread manager before they
    60   // initialize XPCOM in order to set up the crash reporter. This leads to
    61   // situations where we get initialized twice.
    62   if (mInitialized)
    63     return NS_OK;
    65   if (PR_NewThreadPrivateIndex(&mCurThreadIndex, ReleaseObject) == PR_FAILURE)
    66     return NS_ERROR_FAILURE;
    68   mLock = new Mutex("nsThreadManager.mLock");
    70 #ifdef MOZ_CANARY
    71   const int flags = O_WRONLY | O_APPEND | O_CREAT | O_NONBLOCK;
    72   const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
    73   char* env_var_flag = getenv("MOZ_KILL_CANARIES");
    74   sCanaryOutputFD = env_var_flag ? (env_var_flag[0] ?
    75       open(env_var_flag, flags, mode) :
    76       STDERR_FILENO) : 0;
    77 #endif
    79   // Setup "main" thread
    80   mMainThread = new nsThread(nsThread::MAIN_THREAD, 0);
    82   nsresult rv = mMainThread->InitCurrentThread();
    83   if (NS_FAILED(rv)) {
    84     mMainThread = nullptr;
    85     return rv;
    86   }
    88   // We need to keep a pointer to the current thread, so we can satisfy
    89   // GetIsMainThread calls that occur post-Shutdown.
    90   mMainThread->GetPRThread(&mMainPRThread);
    92 #ifdef XP_WIN
    93   TlsSetValue(gTLSThreadIDIndex, (void*) mozilla::threads::Main);
    94 #elif defined(NS_TLS)
    95   gTLSThreadID = mozilla::threads::Main;
    96 #endif
    98   mInitialized = true;
    99   return NS_OK;
   100 }
   102 void
   103 nsThreadManager::Shutdown()
   104 {
   105   MOZ_ASSERT(NS_IsMainThread(), "shutdown not called from main thread");
   107   // Prevent further access to the thread manager (no more new threads!)
   108   //
   109   // XXX What happens if shutdown happens before NewThread completes?
   110   //     Fortunately, NewThread is only called on the main thread for now.
   111   //
   112   mInitialized = false;
   114   // Empty the main thread event queue before we begin shutting down threads.
   115   NS_ProcessPendingEvents(mMainThread);
   117   // We gather the threads from the hashtable into a list, so that we avoid
   118   // holding the hashtable lock while calling nsIThread::Shutdown.
   119   nsThreadArray threads;
   120   {
   121     MutexAutoLock lock(*mLock);
   122     mThreadsByPRThread.Enumerate(AppendAndRemoveThread, &threads);
   123   }
   125   // It's tempting to walk the list of threads here and tell them each to stop
   126   // accepting new events, but that could lead to badness if one of those
   127   // threads is stuck waiting for a response from another thread.  To do it
   128   // right, we'd need some way to interrupt the threads.
   129   // 
   130   // Instead, we process events on the current thread while waiting for threads
   131   // to shutdown.  This means that we have to preserve a mostly functioning
   132   // world until such time as the threads exit.
   134   // Shutdown all threads that require it (join with threads that we created).
   135   for (uint32_t i = 0; i < threads.Length(); ++i) {
   136     nsThread *thread = threads[i];
   137     if (thread->ShutdownRequired())
   138       thread->Shutdown();
   139   }
   141   // In case there are any more events somehow...
   142   NS_ProcessPendingEvents(mMainThread);
   144   // There are no more background threads at this point.
   146   // Clear the table of threads.
   147   {
   148     MutexAutoLock lock(*mLock);
   149     mThreadsByPRThread.Clear();
   150   }
   152   // Normally thread shutdown clears the observer for the thread, but since the
   153   // main thread is special we do it manually here after we're sure all events
   154   // have been processed.
   155   mMainThread->SetObserver(nullptr);
   156   mMainThread->ClearObservers();
   158   // Release main thread object.
   159   mMainThread = nullptr;
   160   mLock = nullptr;
   162   // Remove the TLS entry for the main thread.
   163   PR_SetThreadPrivate(mCurThreadIndex, nullptr);
   164 }
   166 void
   167 nsThreadManager::RegisterCurrentThread(nsThread *thread)
   168 {
   169   MOZ_ASSERT(thread->GetPRThread() == PR_GetCurrentThread(), "bad thread");
   171   MutexAutoLock lock(*mLock);
   173   ++mCurrentNumberOfThreads;
   174   if (mCurrentNumberOfThreads > mHighestNumberOfThreads) {
   175     mHighestNumberOfThreads = mCurrentNumberOfThreads;
   176   }
   178   mThreadsByPRThread.Put(thread->GetPRThread(), thread);  // XXX check OOM?
   180   NS_ADDREF(thread);  // for TLS entry
   181   PR_SetThreadPrivate(mCurThreadIndex, thread);
   182 }
   184 void
   185 nsThreadManager::UnregisterCurrentThread(nsThread *thread)
   186 {
   187   MOZ_ASSERT(thread->GetPRThread() == PR_GetCurrentThread(), "bad thread");
   189   MutexAutoLock lock(*mLock);
   191   --mCurrentNumberOfThreads;
   192   mThreadsByPRThread.Remove(thread->GetPRThread());
   194   PR_SetThreadPrivate(mCurThreadIndex, nullptr);
   195   // Ref-count balanced via ReleaseObject
   196 }
   198 nsThread *
   199 nsThreadManager::GetCurrentThread()
   200 {
   201   // read thread local storage
   202   void *data = PR_GetThreadPrivate(mCurThreadIndex);
   203   if (data)
   204     return static_cast<nsThread *>(data);
   206   if (!mInitialized) {
   207     return nullptr;
   208   }
   210   // OK, that's fine.  We'll dynamically create one :-)
   211   nsRefPtr<nsThread> thread = new nsThread(nsThread::NOT_MAIN_THREAD, 0);
   212   if (!thread || NS_FAILED(thread->InitCurrentThread()))
   213     return nullptr;
   215   return thread.get();  // reference held in TLS
   216 }
   218 NS_IMETHODIMP
   219 nsThreadManager::NewThread(uint32_t creationFlags,
   220                            uint32_t stackSize,
   221                            nsIThread **result)
   222 {
   223   // No new threads during Shutdown
   224   if (NS_WARN_IF(!mInitialized))
   225     return NS_ERROR_NOT_INITIALIZED;
   227   nsThread *thr = new nsThread(nsThread::NOT_MAIN_THREAD, stackSize);
   228   if (!thr)
   229     return NS_ERROR_OUT_OF_MEMORY;
   230   NS_ADDREF(thr);
   232   nsresult rv = thr->Init();
   233   if (NS_FAILED(rv)) {
   234     NS_RELEASE(thr);
   235     return rv;
   236   }
   238   // At this point, we expect that the thread has been registered in mThread;
   239   // however, it is possible that it could have also been replaced by now, so
   240   // we cannot really assert that it was added.
   242   *result = thr;
   243   return NS_OK;
   244 }
   246 NS_IMETHODIMP
   247 nsThreadManager::GetThreadFromPRThread(PRThread *thread, nsIThread **result)
   248 {
   249   // Keep this functioning during Shutdown
   250   if (NS_WARN_IF(!mMainThread))
   251     return NS_ERROR_NOT_INITIALIZED;
   252   if (NS_WARN_IF(!thread))
   253     return NS_ERROR_INVALID_ARG;
   255   nsRefPtr<nsThread> temp;
   256   {
   257     MutexAutoLock lock(*mLock);
   258     mThreadsByPRThread.Get(thread, getter_AddRefs(temp));
   259   }
   261   NS_IF_ADDREF(*result = temp);
   262   return NS_OK;
   263 }
   265 NS_IMETHODIMP
   266 nsThreadManager::GetMainThread(nsIThread **result)
   267 {
   268   // Keep this functioning during Shutdown
   269   if (NS_WARN_IF(!mMainThread))
   270     return NS_ERROR_NOT_INITIALIZED;
   271   NS_ADDREF(*result = mMainThread);
   272   return NS_OK;
   273 }
   275 NS_IMETHODIMP
   276 nsThreadManager::GetCurrentThread(nsIThread **result)
   277 {
   278   // Keep this functioning during Shutdown
   279   if (NS_WARN_IF(!mMainThread))
   280     return NS_ERROR_NOT_INITIALIZED;
   281   *result = GetCurrentThread();
   282   if (!*result)
   283     return NS_ERROR_OUT_OF_MEMORY;
   284   NS_ADDREF(*result);
   285   return NS_OK;
   286 }
   288 NS_IMETHODIMP
   289 nsThreadManager::GetIsMainThread(bool *result)
   290 {
   291   // This method may be called post-Shutdown
   293   *result = (PR_GetCurrentThread() == mMainPRThread);
   294   return NS_OK;
   295 }
   297 uint32_t
   298 nsThreadManager::GetHighestNumberOfThreads()
   299 {
   300   MutexAutoLock lock(*mLock);
   301   return mHighestNumberOfThreads;
   302 }

mercurial