LCOV - app.info - xpcom/threads/nsThread.cpp

LCOV - code coverage report
| Current view: | directory - xpcom/threads - nsThread.cpp (source / functions) |  | Found | Hit | Coverage |
| Test: | app.info | Lines: | 311 | 236 | 75.9 %
| Date: | 2012-04-21 | 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            1396 : 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            1396 :   nsThreadClassInfo() {}
         110                 : };
         111                 :
         112            1396 : static nsThreadClassInfo sThreadClassInfo;
         113                 :
         114            4615 : NS_IMETHODIMP_(nsrefcnt) nsThreadClassInfo::AddRef() { return 2; }
         115            4615 : NS_IMETHODIMP_(nsrefcnt) nsThreadClassInfo::Release() { return 1; }
         116            3094 : NS_IMPL_QUERY_INTERFACE1(nsThreadClassInfo, nsIClassInfo)
         117                 :
         118                 : NS_IMETHODIMP
         119            1229 : nsThreadClassInfo::GetInterfaces(PRUint32 *count, nsIID ***array)
         120                 : {
         121            1229 :   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          771932 : NS_IMPL_THREADSAFE_ADDREF(nsThread)
         175          779223 : NS_IMPL_THREADSAFE_RELEASE(nsThread)
         176          478376 : NS_INTERFACE_MAP_BEGIN(nsThread)
         177          478376 :   NS_INTERFACE_MAP_ENTRY(nsIThread)
         178          323703 :   NS_INTERFACE_MAP_ENTRY(nsIThreadInternal)
         179          318844 :   NS_INTERFACE_MAP_ENTRY(nsIEventTarget)
         180          161500 :   NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
         181          161439 :   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIThread)
         182           84434 :   if (aIID.Equals(NS_GET_IID(nsIClassInfo))) {
         183            1573 :     foundInterface = static_cast<nsIClassInfo*>(&sThreadClassInfo);
         184                 :   } else
         185           82861 : NS_INTERFACE_MAP_END
         186            1229 : NS_IMPL_CI_INTERFACE_GETTER4(nsThread, nsIThread, nsIThreadInternal,
         187            1229 :                              nsIEventTarget, nsISupportsPriority)
         188                 :
         189                 : //-----------------------------------------------------------------------------
         190                 :
         191                 : class nsThreadStartupEvent : public nsRunnable {
         192                 : public:
         193                 :   // Create a new thread startup object.
         194            5972 :   static nsThreadStartupEvent *Create() {
         195            5972 :     return new nsThreadStartupEvent();
         196                 :   }
         197                 :
         198                 :   // This method does not return until the thread startup object is in the
         199                 :   // completion state.
         200            5972 :   void Wait() {
         201            5972 :     if (mInitialized)  // Maybe avoid locking...
         202               0 :       return;
         203           11944 :     ReentrantMonitorAutoEnter mon(mMon);
         204           17916 :     while (!mInitialized)
         205            5972 :       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           11944 :   virtual ~nsThreadStartupEvent() {
         211           23888 :   }
         212                 :
         213                 : private:
         214            5972 :   NS_IMETHOD Run() {
         215           11944 :     ReentrantMonitorAutoEnter mon(mMon);
         216            5972 :     mInitialized = true;
         217            5972 :     mon.Notify();
         218            5972 :     return NS_OK;
         219                 :   }
         220                 :
         221            5972 :   nsThreadStartupEvent()
         222                 :     : mMon("nsThreadStartupEvent.mMon")
         223            5972 :     , mInitialized(false) {
         224            5972 :   }
         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           23888 : class nsThreadShutdownAckEvent : public nsRunnable {
         240                 : public:
         241            5972 :   nsThreadShutdownAckEvent(nsThreadShutdownContext *ctx)
         242            5972 :     : mShutdownContext(ctx) {
         243            5972 :   }
         244            5972 :   NS_IMETHOD Run() {
         245            5972 :     mShutdownContext->shutdownAck = true;
         246            5972 :     return NS_OK;
         247                 :   }
         248                 : private:
         249                 :   nsThreadShutdownContext *mShutdownContext;
         250                 : };
         251                 :
         252                 : // This event is responsible for setting mShutdownContext
         253           23888 : class nsThreadShutdownEvent : public nsRunnable {
         254                 : public:
         255            5972 :   nsThreadShutdownEvent(nsThread *thr, nsThreadShutdownContext *ctx)
         256            5972 :     : mThread(thr), mShutdownContext(ctx) {
         257            5972 :   }
         258            5972 :   NS_IMETHOD Run() {
         259            5972 :     mThread->mShutdownContext = mShutdownContext;
         260            5972 :     return NS_OK;
         261                 :   }
         262                 : private:
         263                 :   nsRefPtr<nsThread>       mThread;
         264                 :   nsThreadShutdownContext *mShutdownContext;
         265                 : };
         266                 :
         267                 : //-----------------------------------------------------------------------------
         268                 :
         269                 : /*static*/ void
         270            5972 : nsThread::ThreadFunc(void *arg)
         271                 : {
         272            5972 :   nsThread *self = static_cast<nsThread *>(arg);  // strong reference
         273            5972 :   self->mThread = PR_GetCurrentThread();
         274                 :
         275                 :   // Inform the ThreadManager
         276            5972 :   nsThreadManager::get()->RegisterCurrentThread(self);
         277                 :
         278                 :   // Wait for and process startup event
         279           11944 :   nsCOMPtr<nsIRunnable> event;
         280            5972 :   if (!self->GetEvent(true, getter_AddRefs(event))) {
         281               0 :     NS_WARNING("failed waiting for thread startup event");
         282                 :     return;
         283                 :   }
         284            5972 :   event->Run();  // unblocks nsThread::Init
         285            5972 :   event = nsnull;
         286                 :
         287                 :   // Now, process incoming events...
         288           83756 :   while (!self->ShuttingDown())
         289           71812 :     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           11944 :       MutexAutoLock lock(self->mLock);
         299            5972 :       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            5972 :         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            5972 :   nsThreadManager::get()->UnregisterCurrentThread(self);
         313                 :
         314                 :   // Dispatch shutdown ACK
         315            5972 :   event = new nsThreadShutdownAckEvent(self->mShutdownContext);
         316            5972 :   self->mShutdownContext->joiningThread->Dispatch(event, NS_DISPATCH_NORMAL);
         317                 :
         318                 :   // Release any observer of the thread here.
         319            5972 :   self->SetObserver(nsnull);
         320                 :
         321            5972 :   NS_RELEASE(self);
         322                 : }
         323                 :
         324                 : //-----------------------------------------------------------------------------
         325                 :
         326            7337 : 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            7337 :   , mIsMainThread(aMainThread)
         337                 : {
         338            7337 : }
         339                 :
         340            7326 : nsThread::~nsThread()
         341                 : {
         342            7326 : }
         343                 :
         344                 : nsresult
         345            5972 : nsThread::Init()
         346                 : {
         347                 :   // spawn thread and wait until it is fully setup
         348           11944 :   nsRefPtr<nsThreadStartupEvent> startup = nsThreadStartupEvent::Create();
         349            5972 :   NS_ENSURE_TRUE(startup, NS_ERROR_OUT_OF_MEMORY);
         350                 :
         351            5972 :   NS_ADDREF_THIS();
         352                 :
         353            5972 :   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            5972 :                                   PR_JOINABLE_THREAD, mStackSize);
         359            5972 :   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           11944 :     MutexAutoLock lock(mLock);
         369            5972 :     mEvents->PutEvent(startup);
         370                 :   }
         371                 :
         372                 :   // Wait for thread to call ThreadManager::SetupCurrentThread, which completes
         373                 :   // initialization of ThreadFunc.
         374            5972 :   startup->Wait();
         375            5972 :   return NS_OK;
         376                 : }
         377                 :
         378                 : nsresult
         379            1365 : nsThread::InitCurrentThread()
         380                 : {
         381            1365 :   mThread = PR_GetCurrentThread();
         382                 :
         383            1365 :   nsThreadManager::get()->RegisterCurrentThread(this);
         384            1365 :   return NS_OK;
         385                 : }
         386                 :
         387                 : nsresult
         388          308210 : nsThread::PutEvent(nsIRunnable *event)
         389                 : {
         390                 :   {
         391          616420 :     MutexAutoLock lock(mLock);
         392          308210 :     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          308210 :     if (!mEvents->PutEvent(event))
         397               0 :       return NS_ERROR_OUT_OF_MEMORY;
         398                 :   }
         399                 :
         400          616420 :   nsCOMPtr<nsIThreadObserver> obs = GetObserver();
         401          308210 :   if (obs)
         402          235076 :     obs->OnDispatchedEvent(this);
         403                 :
         404          308210 :   return NS_OK;
         405                 : }
         406                 :
         407                 : //-----------------------------------------------------------------------------
         408                 : // nsIEventTarget
         409                 :
         410                 : NS_IMETHODIMP
         411          302234 : nsThread::Dispatch(nsIRunnable *event, PRUint32 flags)
         412                 : {
         413          302234 :   LOG(("THRD(%p) Dispatch [%p %x]\n", this, event, flags));
         414                 :
         415          302238 :   NS_ENSURE_ARG_POINTER(event);
         416                 :
         417          302238 :   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          302237 :   NS_ASSERTION(flags == NS_DISPATCH_NORMAL, "unexpected dispatch flags");
         440          302237 :   return PutEvent(event);
         441                 : }
         442                 :
         443                 : NS_IMETHODIMP
         444          150291 : nsThread::IsOnCurrentThread(bool *result)
         445                 : {
         446          150291 :   *result = (PR_GetCurrentThread() == mThread);
         447          150291 :   return NS_OK;
         448                 : }
         449                 :
         450                 : //-----------------------------------------------------------------------------
         451                 : // nsIThread
         452                 :
         453                 : NS_IMETHODIMP
         454            1365 : nsThread::GetPRThread(PRThread **result)
         455                 : {
         456            1365 :   *result = mThread;
         457            1365 :   return NS_OK;
         458                 : }
         459                 :
         460                 : NS_IMETHODIMP
         461            5972 : nsThread::Shutdown()
         462                 : {
         463            5972 :   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            5972 :   if (!mThread)
         469               0 :     return NS_OK;
         470                 :
         471            5972 :   NS_ENSURE_STATE(mThread != PR_GetCurrentThread());
         472                 :
         473                 :   // Prevent multiple calls to this method
         474                 :   {
         475           11944 :     MutexAutoLock lock(mLock);
         476            5972 :     if (!mShutdownRequired)
         477               0 :       return NS_ERROR_UNEXPECTED;
         478           11944 :     mShutdownRequired = false;
         479                 :   }
         480                 :
         481                 :   nsThreadShutdownContext context;
         482            5972 :   context.joiningThread = nsThreadManager::get()->GetCurrentThread();
         483            5972 :   context.shutdownAck = false;
         484                 :
         485                 :   // Set mShutdownContext and wake up the thread in case it is waiting for
         486                 :   // events to process.
         487           11944 :   nsCOMPtr<nsIRunnable> event = new nsThreadShutdownEvent(this, &context);
         488            5972 :   if (!event)
         489               0 :     return NS_ERROR_OUT_OF_MEMORY;
         490                 :   // XXXroc What if posting the event fails due to OOM?
         491            5972 :   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           23563 :   while (!context.shutdownAck)
         499           11619 :     NS_ProcessNextEvent(context.joiningThread);
         500                 :
         501                 :   // Now, it should be safe to join without fear of dead-locking.
         502                 :
         503            5972 :   PR_JoinThread(mThread);
         504            5972 :   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            5972 :   ClearObservers();
         510                 :
         511                 : #ifdef DEBUG
         512                 :   {
         513           11944 :     MutexAutoLock lock(mLock);
         514            5972 :     NS_ASSERTION(!mObserver, "Should have been cleared at shutdown!");
         515                 :   }
         516                 : #endif
         517                 :
         518            5972 :   return NS_OK;
         519                 : }
         520                 :
         521                 : NS_IMETHODIMP
         522         1095611 : nsThread::HasPendingEvents(bool *result)
         523                 : {
         524         1095611 :   NS_ENSURE_STATE(PR_GetCurrentThread() == mThread);
         525                 :
         526         1095611 :   *result = mEvents->GetEvent(false, nsnull);
         527         1095611 :   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          436697 :   Canary() {
         537          436697 :     if (sOutputFD != 0 && EventLatencyIsImportant()) {
         538            1365 :       if (sOutputFD == -1) {
         539            1365 :         const int flags = O_WRONLY | O_APPEND | O_CREAT | O_NONBLOCK;
         540            1365 :         const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
         541            1365 :         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            1365 :                                     STDERR_FILENO) : 0;
         545            1365 :         if (sOutputFD == 0)
         546            1365 :           return;
         547                 :       }
         548               0 :       signal(SIGALRM, canary_alarm_handler);
         549               0 :       ualarm(15000, 0);
         550                 :     }
         551                 :   }
         552                 :
         553          436697 :   ~Canary() {
         554          436697 :     if (sOutputFD != 0 && EventLatencyIsImportant())
         555               0 :       ualarm(0, 0);
         556          436697 :   }
         557                 :
         558           18764 :   static bool EventLatencyIsImportant() {
         559           18764 :     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          436695 : nsThread::ProcessNextEvent(bool mayWait, bool *result)
         593                 : {
         594          436695 :   LOG(("THRD(%p) ProcessNextEvent [%u %u]\n", this, mayWait, mRunningEvent));
         595                 :
         596          436695 :   NS_ENSURE_STATE(PR_GetCurrentThread() == mThread);
         597                 :
         598          436697 :   if (MAIN_THREAD == mIsMainThread && mayWait && !ShuttingDown())
         599          172848 :     HangMonitor::Suspend();
         600                 :
         601                 :   // Fire a memory pressure notification, if we're the main thread and one is
         602                 :   // pending.
         603          436697 :   if (MAIN_THREAD == mIsMainThread && !ShuttingDown()) {
         604          313251 :     bool mpPending = PR_ATOMIC_SET(&sMemoryPressurePending, 0);
         605          313251 :     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          873394 :   nsCOMPtr<nsIThreadObserver> obs = mObserver;
         618          436697 :   if (obs)
         619          364965 :     obs->OnProcessNextEvent(this, mayWait && !ShuttingDown(), mRunningEvent);
         620                 :
         621          436697 :   NOTIFY_EVENT_OBSERVERS(OnProcessNextEvent,
         622                 :                          (this, mayWait && !ShuttingDown(), mRunningEvent));
         623                 :
         624          436697 :   ++mRunningEvent;
         625                 :
         626                 : #ifdef MOZ_CANARY
         627          873394 :   Canary canary;
         628                 : #endif
         629          436697 :   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          873394 :     nsCOMPtr<nsIRunnable> event;
         638          436697 :     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          436697 :     *result = (event.get() != nsnull);
         652                 :
         653          436697 :     if (event) {
         654          308210 :       LOG(("THRD(%p) running [%p]\n", this, event.get()));
         655          308210 :       if (MAIN_THREAD == mIsMainThread)
         656          186136 :         HangMonitor::NotifyActivity();
         657          308210 :       event->Run();
         658          128487 :     } else if (mayWait) {
         659               0 :       NS_ASSERTION(ShuttingDown(),
         660                 :                    "This should only happen when shutting down");
         661               0 :       rv = NS_ERROR_UNEXPECTED;
         662                 :     }
         663                 :   }
         664                 :
         665          436697 :   --mRunningEvent;
         666                 :
         667          436697 :   NOTIFY_EVENT_OBSERVERS(AfterProcessNextEvent, (this, mRunningEvent));
         668                 :
         669          436697 :   if (obs)
         670          364965 :     obs->AfterProcessNextEvent(this, mRunningEvent);
         671                 :
         672          436697 :   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          308210 : nsThread::GetObserver(nsIThreadObserver **obs)
         725                 : {
         726          616420 :   MutexAutoLock lock(mLock);
         727          308210 :   NS_IF_ADDREF(*obs = mObserver);
         728          308210 :   return NS_OK;
         729                 : }
         730                 :
         731                 : NS_IMETHODIMP
         732           10182 : nsThread::SetObserver(nsIThreadObserver *obs)
         733                 : {
         734           10182 :   NS_ENSURE_STATE(PR_GetCurrentThread() == mThread);
         735                 :
         736           20364 :   MutexAutoLock lock(mLock);
         737           10182 :   mObserver = obs;
         738           10182 :   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          314182 : nsThread::nsChainedEventQueue::PutEvent(nsIRunnable *event)
         774                 : {
         775                 :   bool val;
         776          314182 :   if (!mFilter || mFilter->AcceptEvent(event)) {
         777          314182 :     val = mQueue.PutEvent(event);
         778                 :   } else {
         779               0 :     val = mNext->PutEvent(event);
         780                 :   }
         781          314182 :   return val;
         782                 : }
         783                 :
         784                 : NS_IMETHODIMP
         785             649 : nsThread::GetRecursionDepth(PRUint32 *depth)
         786                 : {
         787             649 :   NS_ENSURE_ARG_POINTER(depth);
         788             649 :   NS_ENSURE_STATE(PR_GetCurrentThread() == mThread);
         789                 :
         790             649 :   *depth = mRunningEvent;
         791             649 :   return NS_OK;
         792                 : }
         793                 :
         794                 : NS_IMETHODIMP
         795            2014 : nsThread::AddObserver(nsIThreadObserver *observer)
         796                 : {
         797            2014 :   NS_ENSURE_ARG_POINTER(observer);
         798            2014 :   NS_ENSURE_STATE(PR_GetCurrentThread() == mThread);
         799                 :
         800            2014 :   NS_WARN_IF_FALSE(!mEventObservers.Contains(observer),
         801                 :                    "Adding an observer twice!");
         802                 :
         803            2014 :   if (!mEventObservers.AppendElement(observer)) {
         804               0 :     NS_WARNING("Out of memory!");
         805               0 :     return NS_ERROR_OUT_OF_MEMORY;
         806                 :   }
         807                 :
         808            2014 :   return NS_OK;
         809                 : }
         810                 :
         811                 : NS_IMETHODIMP
         812             649 : nsThread::RemoveObserver(nsIThreadObserver *observer)
         813                 : {
         814             649 :   NS_ENSURE_STATE(PR_GetCurrentThread() == mThread);
         815                 :
         816             649 :   if (observer && !mEventObservers.RemoveElement(observer)) {
         817               0 :     NS_WARNING("Removing an observer that was never added!");
         818                 :   }
         819                 :
         820             649 :   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            4188 : }

Generated by: LCOV version 1.7