LCOV - code coverage report
Current view: directory - xpcom/threads - nsThread.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 311 238 76.5 %
Date: 2012-06-02 Functions: 62 50 80.6 %

       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 "mozilla/ReentrantMonitor.h"
      40                 : #include "nsThread.h"
      41                 : #include "nsThreadManager.h"
      42                 : #include "nsIClassInfoImpl.h"
      43                 : #include "nsIProgrammingLanguage.h"
      44                 : #include "nsAutoPtr.h"
      45                 : #include "nsCOMPtr.h"
      46                 : #include "prlog.h"
      47                 : #include "nsIObserverService.h"
      48                 : #include "mozilla/HangMonitor.h"
      49                 : #include "mozilla/Services.h"
      50                 : 
      51                 : #define HAVE_UALARM _BSD_SOURCE || (_XOPEN_SOURCE >= 500 ||                 \
      52                 :                       _XOPEN_SOURCE && _XOPEN_SOURCE_EXTENDED) &&           \
      53                 :                       !(_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700)
      54                 : 
      55                 : #if defined(XP_UNIX) && !defined(ANDROID) && !defined(DEBUG) && HAVE_UALARM \
      56                 :   && defined(_GNU_SOURCE)
      57                 : # define MOZ_CANARY
      58                 : # include <unistd.h>
      59                 : # include <execinfo.h>
      60                 : # include <signal.h>
      61                 : # include <fcntl.h>
      62                 : # include "nsXULAppAPI.h"
      63                 : #endif
      64                 : 
      65                 : #include "mozilla/FunctionTimer.h"
      66                 : #if defined(NS_FUNCTION_TIMER) && defined(_MSC_VER)
      67                 : #include "nsTimerImpl.h"
      68                 : #include "nsStackWalk.h"
      69                 : #endif
      70                 : #ifdef NS_FUNCTION_TIMER
      71                 : #include "nsCRT.h"
      72                 : #endif
      73                 : 
      74                 : using namespace mozilla;
      75                 : 
      76                 : #ifdef PR_LOGGING
      77            1464 : static PRLogModuleInfo *sLog = PR_NewLogModule("nsThread");
      78                 : #endif
      79                 : #define LOG(args) PR_LOG(sLog, PR_LOG_DEBUG, args)
      80                 : 
      81                 : NS_DECL_CI_INTERFACE_GETTER(nsThread)
      82                 : 
      83                 : namespace mozilla {
      84                 : 
      85                 : // Fun fact: Android's GCC won't convert bool* to PRInt32*, so we can't
      86                 : // PR_ATOMIC_SET a bool.
      87                 : static PRInt32 sMemoryPressurePending = 0;
      88                 : 
      89                 : /*
      90                 :  * It's important that this function not acquire any locks, nor do anything
      91                 :  * which might cause malloc to run.
      92                 :  */
      93               0 : void ScheduleMemoryPressureEvent()
      94                 : {
      95               0 :   PR_ATOMIC_SET(&sMemoryPressurePending, 1);
      96               0 : }
      97                 : 
      98                 : } // namespace mozilla
      99                 : 
     100                 : //-----------------------------------------------------------------------------
     101                 : // Because we do not have our own nsIFactory, we have to implement nsIClassInfo
     102                 : // somewhat manually.
     103                 : 
     104                 : class nsThreadClassInfo : public nsIClassInfo {
     105                 : public:
     106                 :   NS_DECL_ISUPPORTS_INHERITED  // no mRefCnt
     107                 :   NS_DECL_NSICLASSINFO
     108                 : 
     109            1464 :   nsThreadClassInfo() {}
     110                 : };
     111                 : 
     112            1464 : static nsThreadClassInfo sThreadClassInfo;
     113                 : 
     114            4617 : NS_IMETHODIMP_(nsrefcnt) nsThreadClassInfo::AddRef() { return 2; }
     115            4617 : NS_IMETHODIMP_(nsrefcnt) nsThreadClassInfo::Release() { return 1; }
     116            3095 : NS_IMPL_QUERY_INTERFACE1(nsThreadClassInfo, nsIClassInfo)
     117                 : 
     118                 : NS_IMETHODIMP
     119            1230 : nsThreadClassInfo::GetInterfaces(PRUint32 *count, nsIID ***array)
     120                 : {
     121            1230 :   return NS_CI_INTERFACE_GETTER_NAME(nsThread)(count, array);
     122                 : }
     123                 : 
     124                 : NS_IMETHODIMP
     125            1573 : nsThreadClassInfo::GetHelperForLanguage(PRUint32 lang, nsISupports **result)
     126                 : {
     127            1573 :   *result = nsnull;
     128            1573 :   return NS_OK;
     129                 : }
     130                 : 
     131                 : NS_IMETHODIMP
     132               0 : nsThreadClassInfo::GetContractID(char **result)
     133                 : {
     134               0 :   *result = nsnull;
     135               0 :   return NS_OK;
     136                 : }
     137                 : 
     138                 : NS_IMETHODIMP
     139               0 : nsThreadClassInfo::GetClassDescription(char **result)
     140                 : {
     141               0 :   *result = nsnull;
     142               0 :   return NS_OK;
     143                 : }
     144                 : 
     145                 : NS_IMETHODIMP
     146               0 : nsThreadClassInfo::GetClassID(nsCID **result)
     147                 : {
     148               0 :   *result = nsnull;
     149               0 :   return NS_OK;
     150                 : }
     151                 : 
     152                 : NS_IMETHODIMP
     153               0 : nsThreadClassInfo::GetImplementationLanguage(PRUint32 *result)
     154                 : {
     155               0 :   *result = nsIProgrammingLanguage::CPLUSPLUS;
     156               0 :   return NS_OK;
     157                 : }
     158                 : 
     159                 : NS_IMETHODIMP
     160            1847 : nsThreadClassInfo::GetFlags(PRUint32 *result)
     161                 : {
     162            1847 :   *result = THREADSAFE;
     163            1847 :   return NS_OK;
     164                 : }
     165                 : 
     166                 : NS_IMETHODIMP
     167               0 : nsThreadClassInfo::GetClassIDNoAlloc(nsCID *result)
     168                 : {
     169               0 :   return NS_ERROR_NOT_AVAILABLE;
     170                 : }
     171                 : 
     172                 : //-----------------------------------------------------------------------------
     173                 : 
     174          730806 : NS_IMPL_THREADSAFE_ADDREF(nsThread)
     175          738286 : NS_IMPL_THREADSAFE_RELEASE(nsThread)
     176          435702 : NS_INTERFACE_MAP_BEGIN(nsThread)
     177          435702 :   NS_INTERFACE_MAP_ENTRY(nsIThread)
     178          281388 :   NS_INTERFACE_MAP_ENTRY(nsIThreadInternal)
     179          276595 :   NS_INTERFACE_MAP_ENTRY(nsIEventTarget)
     180          117986 :   NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
     181          117925 :   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIThread)
     182           62677 :   if (aIID.Equals(NS_GET_IID(nsIClassInfo))) {
     183            1573 :     foundInterface = static_cast<nsIClassInfo*>(&sThreadClassInfo);
     184                 :   } else
     185           61104 : NS_INTERFACE_MAP_END
     186            1230 : NS_IMPL_CI_INTERFACE_GETTER4(nsThread, nsIThread, nsIThreadInternal,
     187            1230 :                              nsIEventTarget, nsISupportsPriority)
     188                 : 
     189                 : //-----------------------------------------------------------------------------
     190                 : 
     191                 : class nsThreadStartupEvent : public nsRunnable {
     192                 : public:
     193                 :   // Create a new thread startup object.
     194            6106 :   static nsThreadStartupEvent *Create() {
     195            6106 :     return new nsThreadStartupEvent();
     196                 :   }
     197                 : 
     198                 :   // This method does not return until the thread startup object is in the
     199                 :   // completion state.
     200            6106 :   void Wait() {
     201            6106 :     if (mInitialized)  // Maybe avoid locking...
     202               0 :       return;
     203           12212 :     ReentrantMonitorAutoEnter mon(mMon);
     204           18318 :     while (!mInitialized)
     205            6106 :       mon.Wait();
     206                 :   }
     207                 : 
     208                 :   // This method needs to be public to support older compilers (xlC_r on AIX).
     209                 :   // It should be called directly as this class type is reference counted.
     210           12212 :   virtual ~nsThreadStartupEvent() {
     211           24424 :   }
     212                 : 
     213                 : private:
     214            6106 :   NS_IMETHOD Run() {
     215           12212 :     ReentrantMonitorAutoEnter mon(mMon);
     216            6106 :     mInitialized = true;
     217            6106 :     mon.Notify();
     218            6106 :     return NS_OK;
     219                 :   }
     220                 : 
     221            6106 :   nsThreadStartupEvent()
     222                 :     : mMon("nsThreadStartupEvent.mMon")
     223            6106 :     , mInitialized(false) {
     224            6106 :   }
     225                 : 
     226                 :   ReentrantMonitor mMon;
     227                 :   bool       mInitialized;
     228                 : };
     229                 : 
     230                 : //-----------------------------------------------------------------------------
     231                 : 
     232                 : struct nsThreadShutdownContext {
     233                 :   nsThread *joiningThread;
     234                 :   bool      shutdownAck;
     235                 : };
     236                 : 
     237                 : // This event is responsible for notifying nsThread::Shutdown that it is time
     238                 : // to call PR_JoinThread.
     239           24424 : class nsThreadShutdownAckEvent : public nsRunnable {
     240                 : public:
     241            6106 :   nsThreadShutdownAckEvent(nsThreadShutdownContext *ctx)
     242            6106 :     : mShutdownContext(ctx) {
     243            6106 :   }
     244            6106 :   NS_IMETHOD Run() {
     245            6106 :     mShutdownContext->shutdownAck = true;
     246            6106 :     return NS_OK;
     247                 :   }
     248                 : private:
     249                 :   nsThreadShutdownContext *mShutdownContext;
     250                 : };
     251                 : 
     252                 : // This event is responsible for setting mShutdownContext
     253           24424 : class nsThreadShutdownEvent : public nsRunnable {
     254                 : public:
     255            6106 :   nsThreadShutdownEvent(nsThread *thr, nsThreadShutdownContext *ctx)
     256            6106 :     : mThread(thr), mShutdownContext(ctx) {
     257            6106 :   } 
     258            6106 :   NS_IMETHOD Run() {
     259            6106 :     mThread->mShutdownContext = mShutdownContext;
     260            6106 :     return NS_OK;
     261                 :   }
     262                 : private:
     263                 :   nsRefPtr<nsThread>       mThread;
     264                 :   nsThreadShutdownContext *mShutdownContext;
     265                 : };
     266                 : 
     267                 : //-----------------------------------------------------------------------------
     268                 : 
     269                 : /*static*/ void
     270            6106 : nsThread::ThreadFunc(void *arg)
     271                 : {
     272            6106 :   nsThread *self = static_cast<nsThread *>(arg);  // strong reference
     273            6106 :   self->mThread = PR_GetCurrentThread();
     274                 : 
     275                 :   // Inform the ThreadManager
     276            6106 :   nsThreadManager::get()->RegisterCurrentThread(self);
     277                 : 
     278                 :   // Wait for and process startup event
     279           12212 :   nsCOMPtr<nsIRunnable> event;
     280            6106 :   if (!self->GetEvent(true, getter_AddRefs(event))) {
     281               0 :     NS_WARNING("failed waiting for thread startup event");
     282                 :     return;
     283                 :   }
     284            6106 :   event->Run();  // unblocks nsThread::Init
     285            6106 :   event = nsnull;
     286                 : 
     287                 :   // Now, process incoming events...
     288           83391 :   while (!self->ShuttingDown())
     289           71179 :     NS_ProcessNextEvent(self);
     290                 : 
     291                 :   // Do NS_ProcessPendingEvents but with special handling to set
     292                 :   // mEventsAreDoomed atomically with the removal of the last event. The key
     293                 :   // invariant here is that we will never permit PutEvent to succeed if the
     294                 :   // event would be left in the queue after our final call to
     295                 :   // NS_ProcessPendingEvents.
     296               0 :   while (true) {
     297                 :     {
     298           12212 :       MutexAutoLock lock(self->mLock);
     299            6106 :       if (!self->mEvents->HasPendingEvent()) {
     300                 :         // No events in the queue, so we will stop now. Don't let any more
     301                 :         // events be added, since they won't be processed. It is critical
     302                 :         // that no PutEvent can occur between testing that the event queue is
     303                 :         // empty and setting mEventsAreDoomed!
     304            6106 :         self->mEventsAreDoomed = true;
     305                 :         break;
     306                 :       }
     307                 :     }
     308               0 :     NS_ProcessPendingEvents(self);
     309                 :   }
     310                 : 
     311                 :   // Inform the threadmanager that this thread is going away
     312            6106 :   nsThreadManager::get()->UnregisterCurrentThread(self);
     313                 : 
     314                 :   // Dispatch shutdown ACK
     315            6106 :   event = new nsThreadShutdownAckEvent(self->mShutdownContext);
     316            6106 :   self->mShutdownContext->joiningThread->Dispatch(event, NS_DISPATCH_NORMAL);
     317                 : 
     318                 :   // Release any observer of the thread here.
     319            6106 :   self->SetObserver(nsnull);
     320                 : 
     321            6106 :   NS_RELEASE(self);
     322                 : }
     323                 : 
     324                 : //-----------------------------------------------------------------------------
     325                 : 
     326            7525 : nsThread::nsThread(MainThreadFlag aMainThread, PRUint32 aStackSize)
     327                 :   : mLock("nsThread.mLock")
     328                 :   , mEvents(&mEventsRoot)
     329                 :   , mPriority(PRIORITY_NORMAL)
     330                 :   , mThread(nsnull)
     331                 :   , mRunningEvent(0)
     332                 :   , mStackSize(aStackSize)
     333                 :   , mShutdownContext(nsnull)
     334                 :   , mShutdownRequired(false)
     335                 :   , mEventsAreDoomed(false)
     336            7525 :   , mIsMainThread(aMainThread)
     337                 : {
     338            7525 : }
     339                 : 
     340            7514 : nsThread::~nsThread()
     341                 : {
     342            7514 : }
     343                 : 
     344                 : nsresult
     345            6106 : nsThread::Init()
     346                 : {
     347                 :   // spawn thread and wait until it is fully setup
     348           12212 :   nsRefPtr<nsThreadStartupEvent> startup = nsThreadStartupEvent::Create();
     349            6106 :   NS_ENSURE_TRUE(startup, NS_ERROR_OUT_OF_MEMORY);
     350                 :  
     351            6106 :   NS_ADDREF_THIS();
     352                 :  
     353            6106 :   mShutdownRequired = true;
     354                 : 
     355                 :   // ThreadFunc is responsible for setting mThread
     356                 :   PRThread *thr = PR_CreateThread(PR_USER_THREAD, ThreadFunc, this,
     357                 :                                   PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
     358            6106 :                                   PR_JOINABLE_THREAD, mStackSize);
     359            6106 :   if (!thr) {
     360               0 :     NS_RELEASE_THIS();
     361               0 :     return NS_ERROR_OUT_OF_MEMORY;
     362                 :   }
     363                 : 
     364                 :   // ThreadFunc will wait for this event to be run before it tries to access
     365                 :   // mThread.  By delaying insertion of this event into the queue, we ensure
     366                 :   // that mThread is set properly.
     367                 :   {
     368           12212 :     MutexAutoLock lock(mLock);
     369            6106 :     mEvents->PutEvent(startup);
     370                 :   }
     371                 : 
     372                 :   // Wait for thread to call ThreadManager::SetupCurrentThread, which completes
     373                 :   // initialization of ThreadFunc.
     374            6106 :   startup->Wait();
     375            6106 :   return NS_OK;
     376                 : }
     377                 : 
     378                 : nsresult
     379            1419 : nsThread::InitCurrentThread()
     380                 : {
     381            1419 :   mThread = PR_GetCurrentThread();
     382                 : 
     383            1419 :   nsThreadManager::get()->RegisterCurrentThread(this);
     384            1419 :   return NS_OK;
     385                 : }
     386                 : 
     387                 : nsresult
     388          286514 : nsThread::PutEvent(nsIRunnable *event)
     389                 : {
     390                 :   {
     391          573025 :     MutexAutoLock lock(mLock);
     392          286514 :     if (mEventsAreDoomed) {
     393               0 :       NS_WARNING("An event was posted to a thread that will never run it (rejected)");
     394               0 :       return NS_ERROR_UNEXPECTED;
     395                 :     }
     396          286514 :     if (!mEvents->PutEvent(event))
     397               0 :       return NS_ERROR_OUT_OF_MEMORY;
     398                 :   }
     399                 : 
     400          573026 :   nsCOMPtr<nsIThreadObserver> obs = GetObserver();
     401          286513 :   if (obs)
     402          213845 :     obs->OnDispatchedEvent(this);
     403                 : 
     404          286513 :   return NS_OK;
     405                 : }
     406                 : 
     407                 : //-----------------------------------------------------------------------------
     408                 : // nsIEventTarget
     409                 : 
     410                 : NS_IMETHODIMP
     411          280408 : nsThread::Dispatch(nsIRunnable *event, PRUint32 flags)
     412                 : {
     413          280408 :   LOG(("THRD(%p) Dispatch [%p %x]\n", this, event, flags));
     414                 : 
     415          280405 :   NS_ENSURE_ARG_POINTER(event);
     416                 : 
     417          280405 :   if (flags & DISPATCH_SYNC) {
     418               1 :     nsThread *thread = nsThreadManager::get()->GetCurrentThread();
     419               1 :     NS_ENSURE_STATE(thread);
     420                 : 
     421                 :     // XXX we should be able to do something better here... we should
     422                 :     //     be able to monitor the slot occupied by this event and use
     423                 :     //     that to tell us when the event has been processed.
     424                 :  
     425                 :     nsRefPtr<nsThreadSyncDispatch> wrapper =
     426               2 :         new nsThreadSyncDispatch(thread, event);
     427               1 :     if (!wrapper)
     428               0 :       return NS_ERROR_OUT_OF_MEMORY;
     429               1 :     nsresult rv = PutEvent(wrapper);
     430                 :     // Don't wait for the event to finish if we didn't dispatch it...
     431               1 :     if (NS_FAILED(rv))
     432               0 :       return rv;
     433                 : 
     434               3 :     while (wrapper->IsPending())
     435               1 :       NS_ProcessNextEvent(thread);
     436               1 :     return wrapper->Result();
     437                 :   }
     438                 : 
     439          280404 :   NS_ASSERTION(flags == NS_DISPATCH_NORMAL, "unexpected dispatch flags");
     440          280404 :   return PutEvent(event);
     441                 : }
     442                 : 
     443                 : NS_IMETHODIMP
     444          152147 : nsThread::IsOnCurrentThread(bool *result)
     445                 : {
     446          152147 :   *result = (PR_GetCurrentThread() == mThread);
     447          152147 :   return NS_OK;
     448                 : }
     449                 : 
     450                 : //-----------------------------------------------------------------------------
     451                 : // nsIThread
     452                 : 
     453                 : NS_IMETHODIMP
     454            1420 : nsThread::GetPRThread(PRThread **result)
     455                 : {
     456            1420 :   *result = mThread;
     457            1420 :   return NS_OK;
     458                 : }
     459                 : 
     460                 : NS_IMETHODIMP
     461            6106 : nsThread::Shutdown()
     462                 : {
     463            6106 :   LOG(("THRD(%p) shutdown\n", this));
     464                 : 
     465                 :   // XXX If we make this warn, then we hit that warning at xpcom shutdown while
     466                 :   //     shutting down a thread in a thread pool.  That happens b/c the thread
     467                 :   //     in the thread pool is already shutdown by the thread manager.
     468            6106 :   if (!mThread)
     469               0 :     return NS_OK;
     470                 : 
     471            6106 :   NS_ENSURE_STATE(mThread != PR_GetCurrentThread());
     472                 : 
     473                 :   // Prevent multiple calls to this method
     474                 :   {
     475           12212 :     MutexAutoLock lock(mLock);
     476            6106 :     if (!mShutdownRequired)
     477               0 :       return NS_ERROR_UNEXPECTED;
     478           12212 :     mShutdownRequired = false;
     479                 :   }
     480                 : 
     481                 :   nsThreadShutdownContext context;
     482            6106 :   context.joiningThread = nsThreadManager::get()->GetCurrentThread();
     483            6106 :   context.shutdownAck = false;
     484                 : 
     485                 :   // Set mShutdownContext and wake up the thread in case it is waiting for
     486                 :   // events to process.
     487           12212 :   nsCOMPtr<nsIRunnable> event = new nsThreadShutdownEvent(this, &context);
     488            6106 :   if (!event)
     489               0 :     return NS_ERROR_OUT_OF_MEMORY;
     490                 :   // XXXroc What if posting the event fails due to OOM?
     491            6106 :   PutEvent(event);
     492                 : 
     493                 :   // We could still end up with other events being added after the shutdown
     494                 :   // task, but that's okay because we process pending events in ThreadFunc
     495                 :   // after setting mShutdownContext just before exiting.
     496                 :   
     497                 :   // Process events on the current thread until we receive a shutdown ACK.
     498           25136 :   while (!context.shutdownAck)
     499           12924 :     NS_ProcessNextEvent(context.joiningThread);
     500                 : 
     501                 :   // Now, it should be safe to join without fear of dead-locking.
     502                 : 
     503            6106 :   PR_JoinThread(mThread);
     504            6106 :   mThread = nsnull;
     505                 : 
     506                 :   // We hold strong references to our event observers, and once the thread is
     507                 :   // shut down the observers can't easily unregister themselves. Do it here
     508                 :   // to avoid leaking.
     509            6106 :   ClearObservers();
     510                 : 
     511                 : #ifdef DEBUG
     512                 :   {
     513           12212 :     MutexAutoLock lock(mLock);
     514            6106 :     NS_ASSERTION(!mObserver, "Should have been cleared at shutdown!");
     515                 :   }
     516                 : #endif
     517                 : 
     518            6106 :   return NS_OK;
     519                 : }
     520                 : 
     521                 : NS_IMETHODIMP
     522          492034 : nsThread::HasPendingEvents(bool *result)
     523                 : {
     524          492034 :   NS_ENSURE_STATE(PR_GetCurrentThread() == mThread);
     525                 : 
     526          492034 :   *result = mEvents->GetEvent(false, nsnull);
     527          492030 :   return NS_OK;
     528                 : }
     529                 : 
     530                 : #ifdef MOZ_CANARY
     531                 : void canary_alarm_handler (int signum);
     532                 : 
     533                 : class Canary {
     534                 : //XXX ToDo: support nested loops
     535                 : public:
     536          301368 :   Canary() {
     537          301368 :     if (sOutputFD != 0 && EventLatencyIsImportant()) {
     538            1419 :       if (sOutputFD == -1) {
     539            1419 :         const int flags = O_WRONLY | O_APPEND | O_CREAT | O_NONBLOCK;
     540            1419 :         const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
     541            1419 :         char* env_var_flag = getenv("MOZ_KILL_CANARIES");
     542               0 :         sOutputFD = env_var_flag ? (env_var_flag[0] ?
     543                 :                                     open(env_var_flag, flags, mode) :
     544            1419 :                                     STDERR_FILENO) : 0;
     545            1419 :         if (sOutputFD == 0)
     546            1419 :           return;
     547                 :       }
     548               0 :       signal(SIGALRM, canary_alarm_handler);
     549               0 :       ualarm(15000, 0);      
     550                 :     }
     551                 :   }
     552                 : 
     553          301365 :   ~Canary() {
     554          301365 :     if (sOutputFD != 0 && EventLatencyIsImportant())
     555               0 :       ualarm(0, 0);
     556          301365 :   }
     557                 : 
     558           21400 :   static bool EventLatencyIsImportant() {
     559           21400 :     return NS_IsMainThread() && XRE_GetProcessType() == GeckoProcessType_Default;
     560                 :   }
     561                 : 
     562                 :   static int sOutputFD;
     563                 : };
     564                 : 
     565                 : int Canary::sOutputFD = -1;
     566                 : 
     567               0 : void canary_alarm_handler (int signum)
     568                 : {
     569                 :   void *array[30];
     570               0 :   const char msg[29] = "event took too long to run:\n";
     571                 :   // use write to be safe in the signal handler
     572               0 :   write(Canary::sOutputFD, msg, sizeof(msg)); 
     573               0 :   backtrace_symbols_fd(array, backtrace(array, 30), Canary::sOutputFD);
     574               0 : }
     575                 : 
     576                 : #endif
     577                 : 
     578                 : #define NOTIFY_EVENT_OBSERVERS(func_, params_)                                 \
     579                 :   PR_BEGIN_MACRO                                                               \
     580                 :     if (!mEventObservers.IsEmpty()) {                                          \
     581                 :       nsAutoTObserverArray<nsCOMPtr<nsIThreadObserver>, 2>::ForwardIterator    \
     582                 :         iter_(mEventObservers);                                                \
     583                 :       nsCOMPtr<nsIThreadObserver> obs_;                                        \
     584                 :       while (iter_.HasMore()) {                                                \
     585                 :         obs_ = iter_.GetNext();                                                \
     586                 :         obs_ -> func_ params_ ;                                                \
     587                 :       }                                                                        \
     588                 :     }                                                                          \
     589                 :   PR_END_MACRO
     590                 : 
     591                 : NS_IMETHODIMP
     592          301368 : nsThread::ProcessNextEvent(bool mayWait, bool *result)
     593                 : {
     594          301368 :   LOG(("THRD(%p) ProcessNextEvent [%u %u]\n", this, mayWait, mRunningEvent));
     595                 : 
     596          301368 :   NS_ENSURE_STATE(PR_GetCurrentThread() == mThread);
     597                 : 
     598          301369 :   if (MAIN_THREAD == mIsMainThread && mayWait && !ShuttingDown())
     599          150874 :     HangMonitor::Suspend();
     600                 : 
     601                 :   // Fire a memory pressure notification, if we're the main thread and one is
     602                 :   // pending.
     603          301369 :   if (MAIN_THREAD == mIsMainThread && !ShuttingDown()) {
     604          176160 :     bool mpPending = PR_ATOMIC_SET(&sMemoryPressurePending, 0);
     605          176160 :     if (mpPending) {
     606               0 :       nsCOMPtr<nsIObserverService> os = services::GetObserverService();
     607               0 :       if (os) {
     608               0 :         os->NotifyObservers(nsnull, "memory-pressure",
     609               0 :                             NS_LITERAL_STRING("low-memory").get());
     610                 :       }
     611                 :       else {
     612               0 :         NS_WARNING("Can't get observer service!");
     613                 :       }
     614                 :     }
     615                 :   }
     616                 : 
     617          602735 :   nsCOMPtr<nsIThreadObserver> obs = mObserver;
     618          301356 :   if (obs)
     619          228769 :     obs->OnProcessNextEvent(this, mayWait && !ShuttingDown(), mRunningEvent);
     620                 : 
     621          301360 :   NOTIFY_EVENT_OBSERVERS(OnProcessNextEvent,
     622                 :                          (this, mayWait && !ShuttingDown(), mRunningEvent));
     623                 : 
     624          301368 :   ++mRunningEvent;
     625                 : 
     626                 : #ifdef MOZ_CANARY
     627          602735 :   Canary canary;
     628                 : #endif
     629          301366 :   nsresult rv = NS_OK;
     630                 : 
     631                 :   {
     632                 :     // Scope for |event| to make sure that its destructor fires while
     633                 :     // mRunningEvent has been incremented, since that destructor can
     634                 :     // also do work.
     635                 : 
     636                 :     // If we are shutting down, then do not wait for new events.
     637          602734 :     nsCOMPtr<nsIRunnable> event;
     638          301365 :     mEvents->GetEvent(mayWait && !ShuttingDown(), getter_AddRefs(event));
     639                 : 
     640                 : #ifdef NS_FUNCTION_TIMER
     641                 :     char message[1024] = {'\0'};
     642                 :     if (MAIN_THREAD == mIsMainThread) {
     643                 :         mozilla::FunctionTimer::ft_snprintf(message, sizeof(message), 
     644                 :                                             "@ Main Thread Event %p", (void*)event.get());
     645                 :     }
     646                 :     // If message is empty, it means that we're not on the main thread, and
     647                 :     // FunctionTimer won't time this function.
     648                 :     NS_TIME_FUNCTION_MIN_FMT(5.0, message);
     649                 : #endif
     650                 : 
     651          301368 :     *result = (event.get() != nsnull);
     652                 : 
     653          301368 :     if (event) {
     654          286511 :       LOG(("THRD(%p) running [%p]\n", this, event.get()));
     655          286514 :       if (MAIN_THREAD == mIsMainThread)
     656          164357 :         HangMonitor::NotifyActivity();
     657          286514 :       event->Run();
     658           14855 :     } else if (mayWait) {
     659            1626 :       NS_ASSERTION(ShuttingDown(),
     660                 :                    "This should only happen when shutting down");
     661            1626 :       rv = NS_ERROR_UNEXPECTED;
     662                 :     }
     663                 :   }
     664                 : 
     665          301368 :   --mRunningEvent;
     666                 : 
     667          301368 :   NOTIFY_EVENT_OBSERVERS(AfterProcessNextEvent, (this, mRunningEvent));
     668                 : 
     669          301366 :   if (obs)
     670          228770 :     obs->AfterProcessNextEvent(this, mRunningEvent);
     671                 : 
     672          301367 :   return rv;
     673                 : }
     674                 : 
     675                 : //-----------------------------------------------------------------------------
     676                 : // nsISupportsPriority
     677                 : 
     678                 : NS_IMETHODIMP
     679               0 : nsThread::GetPriority(PRInt32 *priority)
     680                 : {
     681               0 :   *priority = mPriority;
     682               0 :   return NS_OK;
     683                 : }
     684                 : 
     685                 : NS_IMETHODIMP
     686              61 : nsThread::SetPriority(PRInt32 priority)
     687                 : {
     688              61 :   NS_ENSURE_STATE(mThread);
     689                 : 
     690                 :   // NSPR defines the following four thread priorities:
     691                 :   //   PR_PRIORITY_LOW
     692                 :   //   PR_PRIORITY_NORMAL
     693                 :   //   PR_PRIORITY_HIGH
     694                 :   //   PR_PRIORITY_URGENT
     695                 :   // We map the priority values defined on nsISupportsPriority to these values.
     696                 : 
     697              61 :   mPriority = priority;
     698                 : 
     699                 :   PRThreadPriority pri;
     700              61 :   if (mPriority <= PRIORITY_HIGHEST) {
     701               0 :     pri = PR_PRIORITY_URGENT;
     702              61 :   } else if (mPriority < PRIORITY_NORMAL) {
     703               0 :     pri = PR_PRIORITY_HIGH;
     704              61 :   } else if (mPriority > PRIORITY_NORMAL) {
     705              61 :     pri = PR_PRIORITY_LOW;
     706                 :   } else {
     707               0 :     pri = PR_PRIORITY_NORMAL;
     708                 :   }
     709              61 :   PR_SetThreadPriority(mThread, pri);
     710                 : 
     711              61 :   return NS_OK;
     712                 : }
     713                 : 
     714                 : NS_IMETHODIMP
     715               0 : nsThread::AdjustPriority(PRInt32 delta)
     716                 : {
     717               0 :   return SetPriority(mPriority + delta);
     718                 : }
     719                 : 
     720                 : //-----------------------------------------------------------------------------
     721                 : // nsIThreadInternal
     722                 : 
     723                 : NS_IMETHODIMP
     724          286512 : nsThread::GetObserver(nsIThreadObserver **obs)
     725                 : {
     726          573017 :   MutexAutoLock lock(mLock);
     727          286514 :   NS_IF_ADDREF(*obs = mObserver);
     728          286505 :   return NS_OK;
     729                 : }
     730                 : 
     731                 : NS_IMETHODIMP
     732           10446 : nsThread::SetObserver(nsIThreadObserver *obs)
     733                 : {
     734           10446 :   NS_ENSURE_STATE(PR_GetCurrentThread() == mThread);
     735                 : 
     736           20892 :   MutexAutoLock lock(mLock);
     737           10446 :   mObserver = obs;
     738           10446 :   return NS_OK;
     739                 : }
     740                 : 
     741                 : NS_IMETHODIMP
     742               0 : nsThread::PushEventQueue(nsIThreadEventFilter *filter)
     743                 : {
     744               0 :   nsChainedEventQueue *queue = new nsChainedEventQueue(filter);
     745                 : 
     746               0 :   MutexAutoLock lock(mLock);
     747               0 :   queue->mNext = mEvents;
     748               0 :   mEvents = queue;
     749               0 :   return NS_OK;
     750                 : }
     751                 : 
     752                 : NS_IMETHODIMP
     753               0 : nsThread::PopEventQueue()
     754                 : {
     755               0 :   MutexAutoLock lock(mLock);
     756                 : 
     757                 :   // Make sure we do not pop too many!
     758               0 :   NS_ENSURE_STATE(mEvents != &mEventsRoot);
     759                 : 
     760               0 :   nsChainedEventQueue *queue = mEvents;
     761               0 :   mEvents = mEvents->mNext;
     762                 : 
     763               0 :   nsCOMPtr<nsIRunnable> event;
     764               0 :   while (queue->GetEvent(false, getter_AddRefs(event)))
     765               0 :     mEvents->PutEvent(event);
     766                 : 
     767               0 :   delete queue;
     768                 :   
     769               0 :   return NS_OK;
     770                 : }
     771                 : 
     772                 : bool
     773          292620 : nsThread::nsChainedEventQueue::PutEvent(nsIRunnable *event)
     774                 : {
     775                 :   bool val;
     776          292620 :   if (!mFilter || mFilter->AcceptEvent(event)) {
     777          292620 :     val = mQueue.PutEvent(event);
     778                 :   } else {
     779               0 :     val = mNext->PutEvent(event);
     780                 :   }
     781          292618 :   return val;
     782                 : }
     783                 : 
     784                 : NS_IMETHODIMP
     785             469 : nsThread::GetRecursionDepth(PRUint32 *depth)
     786                 : {
     787             469 :   NS_ENSURE_ARG_POINTER(depth);
     788             469 :   NS_ENSURE_STATE(PR_GetCurrentThread() == mThread);
     789                 : 
     790             469 :   *depth = mRunningEvent;
     791             469 :   return NS_OK;
     792                 : }
     793                 : 
     794                 : NS_IMETHODIMP
     795            1873 : nsThread::AddObserver(nsIThreadObserver *observer)
     796                 : {
     797            1873 :   NS_ENSURE_ARG_POINTER(observer);
     798            1873 :   NS_ENSURE_STATE(PR_GetCurrentThread() == mThread);
     799                 : 
     800            1873 :   NS_WARN_IF_FALSE(!mEventObservers.Contains(observer),
     801                 :                    "Adding an observer twice!");
     802                 : 
     803            1873 :   if (!mEventObservers.AppendElement(observer)) {
     804               0 :     NS_WARNING("Out of memory!");
     805               0 :     return NS_ERROR_OUT_OF_MEMORY;
     806                 :   }
     807                 : 
     808            1873 :   return NS_OK;
     809                 : }
     810                 : 
     811                 : NS_IMETHODIMP
     812             469 : nsThread::RemoveObserver(nsIThreadObserver *observer)
     813                 : {
     814             469 :   NS_ENSURE_STATE(PR_GetCurrentThread() == mThread);
     815                 : 
     816             469 :   if (observer && !mEventObservers.RemoveElement(observer)) {
     817               0 :     NS_WARNING("Removing an observer that was never added!");
     818                 :   }
     819                 : 
     820             469 :   return NS_OK;
     821                 : }
     822                 : 
     823                 : //-----------------------------------------------------------------------------
     824                 : 
     825                 : NS_IMETHODIMP
     826               2 : nsThreadSyncDispatch::Run()
     827                 : {
     828               2 :   if (mSyncTask) {
     829               1 :     mResult = mSyncTask->Run();
     830               1 :     mSyncTask = nsnull;
     831                 :     // unblock the origin thread
     832               1 :     mOrigin->Dispatch(this, NS_DISPATCH_NORMAL);
     833                 :   }
     834               2 :   return NS_OK;
     835            4392 : }

Generated by: LCOV version 1.7