LCOV - code coverage report
Current view: directory - xpcom/threads - nsThreadManager.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 110 97 88.2 %
Date: 2012-06-02 Functions: 20 19 95.0 %

       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                 : /* ***** BEGIN LICENSE BLOCK *****
       4                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       5                 :  *
       6                 :  * The contents of this file are subject to the Mozilla Public License Version
       7                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       8                 :  * the License. You may obtain a copy of the License at
       9                 :  * http://www.mozilla.org/MPL/
      10                 :  *
      11                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      12                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      13                 :  * for the specific language governing rights and limitations under the
      14                 :  * License.
      15                 :  *
      16                 :  * The Original Code is Mozilla code.
      17                 :  *
      18                 :  * The Initial Developer of the Original Code is Google Inc.
      19                 :  * Portions created by the Initial Developer are Copyright (C) 2006
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *  Darin Fisher <darin@meer.net>
      24                 :  *
      25                 :  * Alternatively, the contents of this file may be used under the terms of
      26                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      27                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      28                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      29                 :  * of those above. If you wish to allow use of your version of this file only
      30                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      31                 :  * use your version of this file under the terms of the MPL, indicate your
      32                 :  * decision by deleting the provisions above and replace them with the notice
      33                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      34                 :  * the provisions above, a recipient may use your version of this file under
      35                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      36                 :  *
      37                 :  * ***** END LICENSE BLOCK ***** */
      38                 : 
      39                 : #include "nsThreadManager.h"
      40                 : #include "nsThread.h"
      41                 : #include "nsThreadUtils.h"
      42                 : #include "nsIClassInfoImpl.h"
      43                 : #include "nsTArray.h"
      44                 : #include "nsAutoPtr.h"
      45                 : #include "nsCycleCollectorUtils.h"
      46                 : 
      47                 : using namespace mozilla;
      48                 : 
      49                 : #ifdef XP_WIN
      50                 : #include <windows.h>
      51                 : DWORD gTLSThreadIDIndex = TlsAlloc();
      52                 : #elif defined(NS_TLS)
      53                 : NS_TLS mozilla::threads::ID gTLSThreadID = mozilla::threads::Generic;
      54                 : #endif
      55                 : 
      56                 : typedef nsTArray< nsRefPtr<nsThread> > nsThreadArray;
      57                 : 
      58                 : //-----------------------------------------------------------------------------
      59                 : 
      60                 : static void
      61            7525 : ReleaseObject(void *data)
      62                 : {
      63            7525 :   static_cast<nsISupports *>(data)->Release();
      64            7525 : }
      65                 : 
      66                 : static PLDHashOperator
      67            3077 : AppendAndRemoveThread(const void *key, nsRefPtr<nsThread> &thread, void *arg)
      68                 : {
      69            3077 :   nsThreadArray *threads = static_cast<nsThreadArray *>(arg);
      70            3077 :   threads->AppendElement(thread);
      71            3077 :   return PL_DHASH_REMOVE;
      72                 : }
      73                 : 
      74                 : //-----------------------------------------------------------------------------
      75                 : 
      76            1464 : nsThreadManager nsThreadManager::sInstance;
      77                 : 
      78                 : // statically allocated instance
      79          355023 : NS_IMETHODIMP_(nsrefcnt) nsThreadManager::AddRef() { return 2; }
      80          355023 : NS_IMETHODIMP_(nsrefcnt) nsThreadManager::Release() { return 1; }
      81                 : NS_IMPL_CLASSINFO(nsThreadManager, NULL,
      82                 :                   nsIClassInfo::THREADSAFE | nsIClassInfo::SINGLETON,
      83                 :                   NS_THREADMANAGER_CID)
      84          207602 : NS_IMPL_QUERY_INTERFACE1_CI(nsThreadManager, nsIThreadManager)
      85            1178 : NS_IMPL_CI_INTERFACE_GETTER1(nsThreadManager, nsIThreadManager)
      86                 : 
      87                 : //-----------------------------------------------------------------------------
      88                 : 
      89                 : nsresult
      90            1419 : nsThreadManager::Init()
      91                 : {
      92            1419 :   if (!mThreadsByPRThread.Init())
      93               0 :     return NS_ERROR_OUT_OF_MEMORY;
      94                 : 
      95            1419 :   if (PR_NewThreadPrivateIndex(&mCurThreadIndex, ReleaseObject) == PR_FAILURE)
      96               0 :     return NS_ERROR_FAILURE;
      97                 : 
      98            1419 :   mLock = new Mutex("nsThreadManager.mLock");
      99                 : 
     100                 :   // Setup "main" thread
     101            1419 :   mMainThread = new nsThread(nsThread::MAIN_THREAD, 0);
     102            1419 :   if (!mMainThread)
     103               0 :     return NS_ERROR_OUT_OF_MEMORY;
     104                 : 
     105            1419 :   nsresult rv = mMainThread->InitCurrentThread();
     106            1419 :   if (NS_FAILED(rv)) {
     107               0 :     mMainThread = nsnull;
     108               0 :     return rv;
     109                 :   }
     110                 : 
     111                 :   // We need to keep a pointer to the current thread, so we can satisfy
     112                 :   // GetIsMainThread calls that occur post-Shutdown.
     113            1419 :   mMainThread->GetPRThread(&mMainPRThread);
     114                 : 
     115                 : #ifdef XP_WIN
     116                 :   TlsSetValue(gTLSThreadIDIndex, (void*) mozilla::threads::Main);
     117                 : #elif defined(NS_TLS)
     118            1419 :   gTLSThreadID = mozilla::threads::Main;
     119                 : #endif
     120                 : 
     121            1419 :   mInitialized = true;
     122            1419 :   return NS_OK;
     123                 : }
     124                 : 
     125                 : void
     126            1419 : nsThreadManager::Shutdown()
     127                 : {
     128            1419 :   NS_ASSERTION(NS_IsMainThread(), "shutdown not called from main thread");
     129                 : 
     130                 :   // Prevent further access to the thread manager (no more new threads!)
     131                 :   //
     132                 :   // XXX What happens if shutdown happens before NewThread completes?
     133                 :   //     Fortunately, NewThread is only called on the main thread for now.
     134                 :   //
     135            1419 :   mInitialized = false;
     136                 : 
     137                 :   // Empty the main thread event queue before we begin shutting down threads.
     138            1419 :   NS_ProcessPendingEvents(mMainThread);
     139                 : 
     140                 :   // We gather the threads from the hashtable into a list, so that we avoid
     141                 :   // holding the hashtable lock while calling nsIThread::Shutdown.
     142            2838 :   nsThreadArray threads;
     143                 :   {
     144            2838 :     MutexAutoLock lock(*mLock);
     145            1419 :     mThreadsByPRThread.Enumerate(AppendAndRemoveThread, &threads);
     146                 :   }
     147                 : 
     148                 :   // It's tempting to walk the list of threads here and tell them each to stop
     149                 :   // accepting new events, but that could lead to badness if one of those
     150                 :   // threads is stuck waiting for a response from another thread.  To do it
     151                 :   // right, we'd need some way to interrupt the threads.
     152                 :   // 
     153                 :   // Instead, we process events on the current thread while waiting for threads
     154                 :   // to shutdown.  This means that we have to preserve a mostly functioning
     155                 :   // world until such time as the threads exit.
     156                 : 
     157                 :   // Shutdown all threads that require it (join with threads that we created).
     158            4496 :   for (PRUint32 i = 0; i < threads.Length(); ++i) {
     159            3077 :     nsThread *thread = threads[i];
     160            3077 :     if (thread->ShutdownRequired())
     161            1658 :       thread->Shutdown();
     162                 :   }
     163                 : 
     164                 :   // In case there are any more events somehow...
     165            1419 :   NS_ProcessPendingEvents(mMainThread);
     166                 : 
     167                 :   // There are no more background threads at this point.
     168                 : 
     169                 :   // Clear the table of threads.
     170                 :   {
     171            2838 :     MutexAutoLock lock(*mLock);
     172            1419 :     mThreadsByPRThread.Clear();
     173                 :   }
     174                 : 
     175                 :   // Normally thread shutdown clears the observer for the thread, but since the
     176                 :   // main thread is special we do it manually here after we're sure all events
     177                 :   // have been processed.
     178            1419 :   mMainThread->SetObserver(nsnull);
     179            1419 :   mMainThread->ClearObservers();
     180                 : 
     181                 :   // Release main thread object.
     182            1419 :   mMainThread = nsnull;
     183            1419 :   mLock = nsnull;
     184                 : 
     185                 :   // Remove the TLS entry for the main thread.
     186            1419 :   PR_SetThreadPrivate(mCurThreadIndex, nsnull);
     187            1419 : }
     188                 : 
     189                 : void
     190            7525 : nsThreadManager::RegisterCurrentThread(nsThread *thread)
     191                 : {
     192            7525 :   NS_ASSERTION(thread->GetPRThread() == PR_GetCurrentThread(), "bad thread");
     193                 : 
     194           15050 :   MutexAutoLock lock(*mLock);
     195                 : 
     196            7525 :   mThreadsByPRThread.Put(thread->GetPRThread(), thread);  // XXX check OOM?
     197                 : 
     198            7525 :   NS_ADDREF(thread);  // for TLS entry
     199            7525 :   PR_SetThreadPrivate(mCurThreadIndex, thread);
     200            7525 : }
     201                 : 
     202                 : void
     203            6106 : nsThreadManager::UnregisterCurrentThread(nsThread *thread)
     204                 : {
     205            6106 :   NS_ASSERTION(thread->GetPRThread() == PR_GetCurrentThread(), "bad thread");
     206                 : 
     207           12212 :   MutexAutoLock lock(*mLock);
     208                 : 
     209            6106 :   mThreadsByPRThread.Remove(thread->GetPRThread());
     210                 : 
     211            6106 :   PR_SetThreadPrivate(mCurThreadIndex, nsnull);
     212                 :   // Ref-count balanced via ReleaseObject
     213            6106 : }
     214                 : 
     215                 : nsThread *
     216          106687 : nsThreadManager::GetCurrentThread()
     217                 : {
     218                 :   // read thread local storage
     219          106687 :   void *data = PR_GetThreadPrivate(mCurThreadIndex);
     220          106688 :   if (data)
     221          105039 :     return static_cast<nsThread *>(data);
     222                 : 
     223            1649 :   if (!mInitialized) {
     224            1649 :     return nsnull;
     225                 :   }
     226                 : 
     227                 :   // OK, that's fine.  We'll dynamically create one :-)
     228               0 :   nsRefPtr<nsThread> thread = new nsThread(nsThread::NOT_MAIN_THREAD, 0);
     229               0 :   if (!thread || NS_FAILED(thread->InitCurrentThread()))
     230               0 :     return nsnull;
     231                 : 
     232               0 :   return thread.get();  // reference held in TLS
     233                 : }
     234                 : 
     235                 : NS_IMETHODIMP
     236            6106 : nsThreadManager::NewThread(PRUint32 creationFlags,
     237                 :                            PRUint32 stackSize,
     238                 :                            nsIThread **result)
     239                 : {
     240                 :   // No new threads during Shutdown
     241            6106 :   NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
     242                 : 
     243            6106 :   nsThread *thr = new nsThread(nsThread::NOT_MAIN_THREAD, stackSize);
     244            6106 :   if (!thr)
     245               0 :     return NS_ERROR_OUT_OF_MEMORY;
     246            6106 :   NS_ADDREF(thr);
     247                 : 
     248            6106 :   nsresult rv = thr->Init();
     249            6106 :   if (NS_FAILED(rv)) {
     250               0 :     NS_RELEASE(thr);
     251               0 :     return rv;
     252                 :   }
     253                 : 
     254                 :   // At this point, we expect that the thread has been registered in mThread;
     255                 :   // however, it is possible that it could have also been replaced by now, so
     256                 :   // we cannot really assert that it was added.
     257                 : 
     258            6106 :   *result = thr;
     259            6106 :   return NS_OK;
     260                 : }
     261                 : 
     262                 : NS_IMETHODIMP
     263               1 : nsThreadManager::GetThreadFromPRThread(PRThread *thread, nsIThread **result)
     264                 : {
     265                 :   // Keep this functioning during Shutdown
     266               1 :   NS_ENSURE_TRUE(mMainThread, NS_ERROR_NOT_INITIALIZED);
     267               1 :   NS_ENSURE_ARG_POINTER(thread);
     268                 : 
     269               2 :   nsRefPtr<nsThread> temp;
     270                 :   {
     271               2 :     MutexAutoLock lock(*mLock);
     272               1 :     mThreadsByPRThread.Get(thread, getter_AddRefs(temp));
     273                 :   }
     274                 : 
     275               1 :   NS_IF_ADDREF(*result = temp);
     276               1 :   return NS_OK;
     277                 : }
     278                 : 
     279                 : NS_IMETHODIMP
     280           71324 : nsThreadManager::GetMainThread(nsIThread **result)
     281                 : {
     282                 :   // Keep this functioning during Shutdown
     283           71324 :   NS_ENSURE_TRUE(mMainThread, NS_ERROR_NOT_INITIALIZED);
     284           71307 :   NS_ADDREF(*result = mMainThread);
     285           71307 :   return NS_OK;
     286                 : }
     287                 : 
     288                 : NS_IMETHODIMP
     289           72958 : nsThreadManager::GetCurrentThread(nsIThread **result)
     290                 : {
     291                 :   // Keep this functioning during Shutdown
     292           72958 :   NS_ENSURE_TRUE(mMainThread, NS_ERROR_NOT_INITIALIZED);
     293           72941 :   *result = GetCurrentThread();
     294           72941 :   if (!*result)
     295               0 :     return NS_ERROR_OUT_OF_MEMORY;
     296           72941 :   NS_ADDREF(*result);
     297           72941 :   return NS_OK;
     298                 : }
     299                 : 
     300                 : NS_IMETHODIMP
     301              54 : nsThreadManager::GetIsMainThread(bool *result)
     302                 : {
     303                 :   // This method may be called post-Shutdown
     304                 : 
     305              54 :   *result = (PR_GetCurrentThread() == mMainPRThread);
     306              54 :   return NS_OK;
     307                 : }
     308                 : 
     309                 : NS_IMETHODIMP
     310          158361 : nsThreadManager::GetIsCycleCollectorThread(bool *result)
     311                 : {
     312          158361 :   *result = bool(NS_IsCycleCollectorThread());
     313          158361 :   return NS_OK;
     314            4392 : }

Generated by: LCOV version 1.7