xpcom/threads/nsThread.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "nsThread.h"
     9 #include "base/message_loop.h"
    11 // Chromium's logging can sometimes leak through...
    12 #ifdef LOG
    13 #undef LOG
    14 #endif
    16 #include "mozilla/ReentrantMonitor.h"
    17 #include "nsMemoryPressure.h"
    18 #include "nsThreadManager.h"
    19 #include "nsIClassInfoImpl.h"
    20 #include "nsIProgrammingLanguage.h"
    21 #include "nsAutoPtr.h"
    22 #include "nsCOMPtr.h"
    23 #include "pratom.h"
    24 #include "prlog.h"
    25 #include "nsIObserverService.h"
    26 #include "mozilla/HangMonitor.h"
    27 #include "mozilla/IOInterposer.h"
    28 #include "mozilla/Services.h"
    29 #include "nsXPCOMPrivate.h"
    30 #include "mozilla/ChaosMode.h"
    32 #ifdef XP_LINUX
    33 #include <sys/time.h>
    34 #include <sys/resource.h>
    35 #include <sched.h>
    36 #endif
    38 #define HAVE_UALARM _BSD_SOURCE || (_XOPEN_SOURCE >= 500 ||                 \
    39                       _XOPEN_SOURCE && _XOPEN_SOURCE_EXTENDED) &&           \
    40                       !(_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700)
    42 #if defined(XP_LINUX) && !defined(ANDROID) && defined(_GNU_SOURCE)
    43 #define HAVE_SCHED_SETAFFINITY
    44 #endif
    46 #ifdef MOZ_CANARY
    47 # include <unistd.h>
    48 # include <execinfo.h>
    49 # include <signal.h>
    50 # include <fcntl.h>
    51 # include "nsXULAppAPI.h"
    52 #endif
    54 #if defined(NS_FUNCTION_TIMER) && defined(_MSC_VER)
    55 #include "nsTimerImpl.h"
    56 #include "nsStackWalk.h"
    57 #endif
    58 #ifdef NS_FUNCTION_TIMER
    59 #include "nsCRT.h"
    60 #endif
    62 #ifdef MOZ_TASK_TRACER
    63 #include "GeckoTaskTracer.h"
    64 using namespace mozilla::tasktracer;
    65 #endif
    67 using namespace mozilla;
    69 #ifdef PR_LOGGING
    70 static PRLogModuleInfo *
    71 GetThreadLog()
    72 {
    73   static PRLogModuleInfo *sLog;
    74   if (!sLog)
    75     sLog = PR_NewLogModule("nsThread");
    76   return sLog;
    77 }
    78 #endif
    79 #ifdef LOG
    80 #undef LOG
    81 #endif
    82 #define LOG(args) PR_LOG(GetThreadLog(), PR_LOG_DEBUG, args)
    84 NS_DECL_CI_INTERFACE_GETTER(nsThread)
    86 nsIThreadObserver* nsThread::sMainThreadObserver = nullptr;
    88 //-----------------------------------------------------------------------------
    89 // Because we do not have our own nsIFactory, we have to implement nsIClassInfo
    90 // somewhat manually.
    92 class nsThreadClassInfo : public nsIClassInfo {
    93 public:
    94   NS_DECL_ISUPPORTS_INHERITED  // no mRefCnt
    95   NS_DECL_NSICLASSINFO
    97   nsThreadClassInfo() {}
    98 };
   100 NS_IMETHODIMP_(MozExternalRefCountType) nsThreadClassInfo::AddRef() { return 2; }
   101 NS_IMETHODIMP_(MozExternalRefCountType) nsThreadClassInfo::Release() { return 1; }
   102 NS_IMPL_QUERY_INTERFACE(nsThreadClassInfo, nsIClassInfo)
   104 NS_IMETHODIMP
   105 nsThreadClassInfo::GetInterfaces(uint32_t *count, nsIID ***array)
   106 {
   107   return NS_CI_INTERFACE_GETTER_NAME(nsThread)(count, array);
   108 }
   110 NS_IMETHODIMP
   111 nsThreadClassInfo::GetHelperForLanguage(uint32_t lang, nsISupports **result)
   112 {
   113   *result = nullptr;
   114   return NS_OK;
   115 }
   117 NS_IMETHODIMP
   118 nsThreadClassInfo::GetContractID(char **result)
   119 {
   120   *result = nullptr;
   121   return NS_OK;
   122 }
   124 NS_IMETHODIMP
   125 nsThreadClassInfo::GetClassDescription(char **result)
   126 {
   127   *result = nullptr;
   128   return NS_OK;
   129 }
   131 NS_IMETHODIMP
   132 nsThreadClassInfo::GetClassID(nsCID **result)
   133 {
   134   *result = nullptr;
   135   return NS_OK;
   136 }
   138 NS_IMETHODIMP
   139 nsThreadClassInfo::GetImplementationLanguage(uint32_t *result)
   140 {
   141   *result = nsIProgrammingLanguage::CPLUSPLUS;
   142   return NS_OK;
   143 }
   145 NS_IMETHODIMP
   146 nsThreadClassInfo::GetFlags(uint32_t *result)
   147 {
   148   *result = THREADSAFE;
   149   return NS_OK;
   150 }
   152 NS_IMETHODIMP
   153 nsThreadClassInfo::GetClassIDNoAlloc(nsCID *result)
   154 {
   155   return NS_ERROR_NOT_AVAILABLE;
   156 }
   158 //-----------------------------------------------------------------------------
   160 NS_IMPL_ADDREF(nsThread)
   161 NS_IMPL_RELEASE(nsThread)
   162 NS_INTERFACE_MAP_BEGIN(nsThread)
   163   NS_INTERFACE_MAP_ENTRY(nsIThread)
   164   NS_INTERFACE_MAP_ENTRY(nsIThreadInternal)
   165   NS_INTERFACE_MAP_ENTRY(nsIEventTarget)
   166   NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
   167   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIThread)
   168   if (aIID.Equals(NS_GET_IID(nsIClassInfo))) {
   169     static nsThreadClassInfo sThreadClassInfo;
   170     foundInterface = static_cast<nsIClassInfo*>(&sThreadClassInfo);
   171   } else
   172 NS_INTERFACE_MAP_END
   173 NS_IMPL_CI_INTERFACE_GETTER(nsThread, nsIThread, nsIThreadInternal,
   174                             nsIEventTarget, nsISupportsPriority)
   176 //-----------------------------------------------------------------------------
   178 class nsThreadStartupEvent : public nsRunnable {
   179 public:
   180   nsThreadStartupEvent()
   181     : mMon("nsThreadStartupEvent.mMon")
   182     , mInitialized(false) {
   183   }
   185   // This method does not return until the thread startup object is in the
   186   // completion state.
   187   void Wait() {
   188     if (mInitialized)  // Maybe avoid locking...
   189       return;
   190     ReentrantMonitorAutoEnter mon(mMon);
   191     while (!mInitialized)
   192       mon.Wait();
   193   }
   195   // This method needs to be public to support older compilers (xlC_r on AIX).
   196   // It should be called directly as this class type is reference counted.
   197   virtual ~nsThreadStartupEvent() {
   198   }
   200 private:
   201   NS_IMETHOD Run() {
   202     ReentrantMonitorAutoEnter mon(mMon);
   203     mInitialized = true;
   204     mon.Notify();
   205     return NS_OK;
   206   }
   208   ReentrantMonitor mMon;
   209   bool       mInitialized;
   210 };
   212 //-----------------------------------------------------------------------------
   214 struct nsThreadShutdownContext {
   215   nsThread *joiningThread;
   216   bool      shutdownAck;
   217 };
   219 // This event is responsible for notifying nsThread::Shutdown that it is time
   220 // to call PR_JoinThread.
   221 class nsThreadShutdownAckEvent : public nsRunnable {
   222 public:
   223   nsThreadShutdownAckEvent(nsThreadShutdownContext *ctx)
   224     : mShutdownContext(ctx) {
   225   }
   226   NS_IMETHOD Run() {
   227     mShutdownContext->shutdownAck = true;
   228     return NS_OK;
   229   }
   230 private:
   231   nsThreadShutdownContext *mShutdownContext;
   232 };
   234 // This event is responsible for setting mShutdownContext
   235 class nsThreadShutdownEvent : public nsRunnable {
   236 public:
   237   nsThreadShutdownEvent(nsThread *thr, nsThreadShutdownContext *ctx)
   238     : mThread(thr), mShutdownContext(ctx) {
   239   } 
   240   NS_IMETHOD Run() {
   241     mThread->mShutdownContext = mShutdownContext;
   242     MessageLoop::current()->Quit();
   243     return NS_OK;
   244   }
   245 private:
   246   nsRefPtr<nsThread>       mThread;
   247   nsThreadShutdownContext *mShutdownContext;
   248 };
   250 //-----------------------------------------------------------------------------
   252 static void
   253 SetupCurrentThreadForChaosMode()
   254 {
   255   if (!ChaosMode::isActive()) {
   256     return;
   257   }
   259 #ifdef XP_LINUX
   260   // PR_SetThreadPriority doesn't really work since priorities >
   261   // PR_PRIORITY_NORMAL can't be set by non-root users. Instead we'll just use
   262   // setpriority(2) to set random 'nice values'. In regular Linux this is only
   263   // a dynamic adjustment so it still doesn't really do what we want, but tools
   264   // like 'rr' can be more aggressive about honoring these values.
   265   // Some of these calls may fail due to trying to lower the priority
   266   // (e.g. something may have already called setpriority() for this thread).
   267   // This makes it hard to have non-main threads with higher priority than the
   268   // main thread, but that's hard to fix. Tools like rr can choose to honor the
   269   // requested values anyway.
   270   // Use just 4 priorities so there's a reasonable chance of any two threads
   271   // having equal priority.
   272   setpriority(PRIO_PROCESS, 0, ChaosMode::randomUint32LessThan(4));
   273 #else
   274   // We should set the affinity here but NSPR doesn't provide a way to expose it.
   275   PR_SetThreadPriority(PR_GetCurrentThread(),
   276     PRThreadPriority(ChaosMode::randomUint32LessThan(PR_PRIORITY_LAST + 1)));
   277 #endif
   279 #ifdef HAVE_SCHED_SETAFFINITY
   280   // Force half the threads to CPU 0 so they compete for CPU
   281   if (ChaosMode::randomUint32LessThan(2)) {
   282     cpu_set_t cpus;
   283     CPU_ZERO(&cpus);
   284     CPU_SET(0, &cpus);
   285     sched_setaffinity(0, sizeof(cpus), &cpus);
   286   }
   287 #endif
   288 }
   290 /*static*/ void
   291 nsThread::ThreadFunc(void *arg)
   292 {
   293   nsThread *self = static_cast<nsThread *>(arg);  // strong reference
   294   self->mThread = PR_GetCurrentThread();
   295   SetupCurrentThreadForChaosMode();
   297   // Inform the ThreadManager
   298   nsThreadManager::get()->RegisterCurrentThread(self);
   300   mozilla::IOInterposer::RegisterCurrentThread();
   302   // Wait for and process startup event
   303   nsCOMPtr<nsIRunnable> event;
   304   if (!self->GetEvent(true, getter_AddRefs(event))) {
   305     NS_WARNING("failed waiting for thread startup event");
   306     return;
   307   }
   308   event->Run();  // unblocks nsThread::Init
   309   event = nullptr;
   311   { // Scope for MessageLoop.
   312     nsAutoPtr<MessageLoop> loop(
   313       new MessageLoop(MessageLoop::TYPE_MOZILLA_NONMAINTHREAD));
   315     // Now, process incoming events...
   316     loop->Run();
   318     // Do NS_ProcessPendingEvents but with special handling to set
   319     // mEventsAreDoomed atomically with the removal of the last event. The key
   320     // invariant here is that we will never permit PutEvent to succeed if the
   321     // event would be left in the queue after our final call to
   322     // NS_ProcessPendingEvents.
   323     while (true) {
   324       {
   325         MutexAutoLock lock(self->mLock);
   326         if (!self->mEvents->HasPendingEvent()) {
   327           // No events in the queue, so we will stop now. Don't let any more
   328           // events be added, since they won't be processed. It is critical
   329           // that no PutEvent can occur between testing that the event queue is
   330           // empty and setting mEventsAreDoomed!
   331           self->mEventsAreDoomed = true;
   332           break;
   333         }
   334       }
   335       NS_ProcessPendingEvents(self);
   336     }
   337   }
   339   mozilla::IOInterposer::UnregisterCurrentThread();
   341   // Inform the threadmanager that this thread is going away
   342   nsThreadManager::get()->UnregisterCurrentThread(self);
   344   // Dispatch shutdown ACK
   345   event = new nsThreadShutdownAckEvent(self->mShutdownContext);
   346   self->mShutdownContext->joiningThread->Dispatch(event, NS_DISPATCH_NORMAL);
   348   // Release any observer of the thread here.
   349   self->SetObserver(nullptr);
   351 #ifdef MOZ_TASK_TRACER
   352   FreeTraceInfo();
   353 #endif
   355   NS_RELEASE(self);
   356 }
   358 //-----------------------------------------------------------------------------
   360 #ifdef MOZ_CANARY
   361 int sCanaryOutputFD = -1;
   362 #endif
   364 nsThread::nsThread(MainThreadFlag aMainThread, uint32_t aStackSize)
   365   : mLock("nsThread.mLock")
   366   , mEvents(&mEventsRoot)
   367   , mPriority(PRIORITY_NORMAL)
   368   , mThread(nullptr)
   369   , mRunningEvent(0)
   370   , mStackSize(aStackSize)
   371   , mShutdownContext(nullptr)
   372   , mShutdownRequired(false)
   373   , mEventsAreDoomed(false)
   374   , mIsMainThread(aMainThread)
   375 {
   376 }
   378 nsThread::~nsThread()
   379 {
   380 }
   382 nsresult
   383 nsThread::Init()
   384 {
   385   // spawn thread and wait until it is fully setup
   386   nsRefPtr<nsThreadStartupEvent> startup = new nsThreadStartupEvent();
   388   NS_ADDREF_THIS();
   390   mShutdownRequired = true;
   392   // ThreadFunc is responsible for setting mThread
   393   PRThread *thr = PR_CreateThread(PR_USER_THREAD, ThreadFunc, this,
   394                                   PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
   395                                   PR_JOINABLE_THREAD, mStackSize);
   396   if (!thr) {
   397     NS_RELEASE_THIS();
   398     return NS_ERROR_OUT_OF_MEMORY;
   399   }
   401   // ThreadFunc will wait for this event to be run before it tries to access
   402   // mThread.  By delaying insertion of this event into the queue, we ensure
   403   // that mThread is set properly.
   404   {
   405     MutexAutoLock lock(mLock);
   406     mEventsRoot.PutEvent(startup);
   407   }
   409   // Wait for thread to call ThreadManager::SetupCurrentThread, which completes
   410   // initialization of ThreadFunc.
   411   startup->Wait();
   412   return NS_OK;
   413 }
   415 nsresult
   416 nsThread::InitCurrentThread()
   417 {
   418   mThread = PR_GetCurrentThread();
   419   SetupCurrentThreadForChaosMode();
   421   nsThreadManager::get()->RegisterCurrentThread(this);
   422   return NS_OK;
   423 }
   425 nsresult
   426 nsThread::PutEvent(nsIRunnable *event, nsNestedEventTarget *target)
   427 {
   428   {
   429     MutexAutoLock lock(mLock);
   430     nsChainedEventQueue *queue = target ? target->mQueue : &mEventsRoot;
   431     if (!queue || (queue == &mEventsRoot && mEventsAreDoomed)) {
   432       NS_WARNING("An event was posted to a thread that will never run it (rejected)");
   433       return NS_ERROR_UNEXPECTED;
   434     }
   435     if (!queue->PutEvent(event))
   436       return NS_ERROR_OUT_OF_MEMORY;
   437   }
   439   nsCOMPtr<nsIThreadObserver> obs = GetObserver();
   440   if (obs)
   441     obs->OnDispatchedEvent(this);
   443   return NS_OK;
   444 }
   446 nsresult
   447 nsThread::DispatchInternal(nsIRunnable *event, uint32_t flags,
   448                            nsNestedEventTarget *target)
   449 {
   450   if (NS_WARN_IF(!event))
   451     return NS_ERROR_INVALID_ARG;
   453   if (gXPCOMThreadsShutDown && MAIN_THREAD != mIsMainThread && !target) {
   454     return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
   455   }
   457 #ifdef MOZ_TASK_TRACER
   458   nsRefPtr<nsIRunnable> tracedRunnable = CreateTracedRunnable(event);
   459   event = tracedRunnable;
   460 #endif
   462   if (flags & DISPATCH_SYNC) {
   463     nsThread *thread = nsThreadManager::get()->GetCurrentThread();
   464     if (NS_WARN_IF(!thread))
   465       return NS_ERROR_NOT_AVAILABLE;
   467     // XXX we should be able to do something better here... we should
   468     //     be able to monitor the slot occupied by this event and use
   469     //     that to tell us when the event has been processed.
   471     nsRefPtr<nsThreadSyncDispatch> wrapper =
   472         new nsThreadSyncDispatch(thread, event);
   473     if (!wrapper)
   474       return NS_ERROR_OUT_OF_MEMORY;
   475     nsresult rv = PutEvent(wrapper, target);
   476     // Don't wait for the event to finish if we didn't dispatch it...
   477     if (NS_FAILED(rv))
   478       return rv;
   480     // Allows waiting; ensure no locks are held that would deadlock us!
   481     while (wrapper->IsPending())
   482       NS_ProcessNextEvent(thread, true);
   483     return wrapper->Result();
   484   }
   486   NS_ASSERTION(flags == NS_DISPATCH_NORMAL, "unexpected dispatch flags");
   487   return PutEvent(event, target);
   488 }
   490 //-----------------------------------------------------------------------------
   491 // nsIEventTarget
   493 NS_IMETHODIMP
   494 nsThread::Dispatch(nsIRunnable *event, uint32_t flags)
   495 {
   496   LOG(("THRD(%p) Dispatch [%p %x]\n", this, event, flags));
   498   return DispatchInternal(event, flags, nullptr);
   499 }
   501 NS_IMETHODIMP
   502 nsThread::IsOnCurrentThread(bool *result)
   503 {
   504   *result = (PR_GetCurrentThread() == mThread);
   505   return NS_OK;
   506 }
   508 //-----------------------------------------------------------------------------
   509 // nsIThread
   511 NS_IMETHODIMP
   512 nsThread::GetPRThread(PRThread **result)
   513 {
   514   *result = mThread;
   515   return NS_OK;
   516 }
   518 NS_IMETHODIMP
   519 nsThread::Shutdown()
   520 {
   521   LOG(("THRD(%p) shutdown\n", this));
   523   // XXX If we make this warn, then we hit that warning at xpcom shutdown while
   524   //     shutting down a thread in a thread pool.  That happens b/c the thread
   525   //     in the thread pool is already shutdown by the thread manager.
   526   if (!mThread)
   527     return NS_OK;
   529   if (NS_WARN_IF(mThread == PR_GetCurrentThread()))
   530     return NS_ERROR_UNEXPECTED;
   532   // Prevent multiple calls to this method
   533   {
   534     MutexAutoLock lock(mLock);
   535     if (!mShutdownRequired)
   536       return NS_ERROR_UNEXPECTED;
   537     mShutdownRequired = false;
   538   }
   540   nsThreadShutdownContext context;
   541   context.joiningThread = nsThreadManager::get()->GetCurrentThread();
   542   context.shutdownAck = false;
   544   // Set mShutdownContext and wake up the thread in case it is waiting for
   545   // events to process.
   546   nsCOMPtr<nsIRunnable> event = new nsThreadShutdownEvent(this, &context);
   547   if (!event)
   548     return NS_ERROR_OUT_OF_MEMORY;
   549   // XXXroc What if posting the event fails due to OOM?
   550   PutEvent(event, nullptr);
   552   // We could still end up with other events being added after the shutdown
   553   // task, but that's okay because we process pending events in ThreadFunc
   554   // after setting mShutdownContext just before exiting.
   556   // Process events on the current thread until we receive a shutdown ACK.
   557   // Allows waiting; ensure no locks are held that would deadlock us!
   558   while (!context.shutdownAck)
   559     NS_ProcessNextEvent(context.joiningThread, true);
   561   // Now, it should be safe to join without fear of dead-locking.
   563   PR_JoinThread(mThread);
   564   mThread = nullptr;
   566   // We hold strong references to our event observers, and once the thread is
   567   // shut down the observers can't easily unregister themselves. Do it here
   568   // to avoid leaking.
   569   ClearObservers();
   571 #ifdef DEBUG
   572   {
   573     MutexAutoLock lock(mLock);
   574     MOZ_ASSERT(!mObserver, "Should have been cleared at shutdown!");
   575   }
   576 #endif
   578   return NS_OK;
   579 }
   581 NS_IMETHODIMP
   582 nsThread::HasPendingEvents(bool *result)
   583 {
   584   if (NS_WARN_IF(PR_GetCurrentThread() != mThread))
   585     return NS_ERROR_NOT_SAME_THREAD;
   587   *result = mEvents->GetEvent(false, nullptr);
   588   return NS_OK;
   589 }
   591 #ifdef MOZ_CANARY
   592 void canary_alarm_handler (int signum);
   594 class Canary {
   595 //XXX ToDo: support nested loops
   596 public:
   597   Canary() {
   598     if (sCanaryOutputFD > 0 && EventLatencyIsImportant()) {
   599       signal(SIGALRM, canary_alarm_handler);
   600       ualarm(15000, 0);      
   601     }
   602   }
   604   ~Canary() {
   605     if (sCanaryOutputFD != 0 && EventLatencyIsImportant())
   606       ualarm(0, 0);
   607   }
   609   static bool EventLatencyIsImportant() {
   610     return NS_IsMainThread() && XRE_GetProcessType() == GeckoProcessType_Default;
   611   }
   612 };
   614 void canary_alarm_handler (int signum)
   615 {
   616   void *array[30];
   617   const char msg[29] = "event took too long to run:\n";
   618   // use write to be safe in the signal handler
   619   write(sCanaryOutputFD, msg, sizeof(msg)); 
   620   backtrace_symbols_fd(array, backtrace(array, 30), sCanaryOutputFD);
   621 }
   623 #endif
   625 #define NOTIFY_EVENT_OBSERVERS(func_, params_)                                 \
   626   PR_BEGIN_MACRO                                                               \
   627     if (!mEventObservers.IsEmpty()) {                                          \
   628       nsAutoTObserverArray<nsCOMPtr<nsIThreadObserver>, 2>::ForwardIterator    \
   629         iter_(mEventObservers);                                                \
   630       nsCOMPtr<nsIThreadObserver> obs_;                                        \
   631       while (iter_.HasMore()) {                                                \
   632         obs_ = iter_.GetNext();                                                \
   633         obs_ -> func_ params_ ;                                                \
   634       }                                                                        \
   635     }                                                                          \
   636   PR_END_MACRO
   638 NS_IMETHODIMP
   639 nsThread::ProcessNextEvent(bool mayWait, bool *result)
   640 {
   641   LOG(("THRD(%p) ProcessNextEvent [%u %u]\n", this, mayWait, mRunningEvent));
   643   if (NS_WARN_IF(PR_GetCurrentThread() != mThread))
   644     return NS_ERROR_NOT_SAME_THREAD;
   646   // The toplevel event loop normally blocks waiting for the next event, but
   647   // if we're trying to shut this thread down, we must exit the event loop when
   648   // the event queue is empty.
   649   // This only applys to the toplevel event loop! Nested event loops (e.g.
   650   // during sync dispatch) are waiting for some state change and must be able
   651   // to block even if something has requested shutdown of the thread. Otherwise
   652   // we'll just busywait as we endlessly look for an event, fail to find one,
   653   // and repeat the nested event loop since its state change hasn't happened yet.
   654   bool reallyWait = mayWait && (mRunningEvent > 0 || !ShuttingDown());
   656   if (MAIN_THREAD == mIsMainThread && reallyWait)
   657     HangMonitor::Suspend();
   659   // Fire a memory pressure notification, if we're the main thread and one is
   660   // pending.
   661   if (MAIN_THREAD == mIsMainThread && !ShuttingDown()) {
   662     MemoryPressureState mpPending = NS_GetPendingMemoryPressure();
   663     if (mpPending != MemPressure_None) {
   664       nsCOMPtr<nsIObserverService> os = services::GetObserverService();
   666       // Use no-forward to prevent the notifications from being transferred to
   667       // the children of this process.
   668       NS_NAMED_LITERAL_STRING(lowMem, "low-memory-no-forward");
   669       NS_NAMED_LITERAL_STRING(lowMemOngoing, "low-memory-ongoing-no-forward");
   671       if (os) {
   672         os->NotifyObservers(nullptr, "memory-pressure",
   673                             mpPending == MemPressure_New ? lowMem.get() :
   674                                                            lowMemOngoing.get());
   675       } else {
   676         NS_WARNING("Can't get observer service!");
   677       }
   678     }
   679   }
   681   bool notifyMainThreadObserver =
   682     (MAIN_THREAD == mIsMainThread) && sMainThreadObserver;
   683   if (notifyMainThreadObserver) 
   684    sMainThreadObserver->OnProcessNextEvent(this, reallyWait, mRunningEvent);
   686   nsCOMPtr<nsIThreadObserver> obs = mObserver;
   687   if (obs)
   688     obs->OnProcessNextEvent(this, reallyWait, mRunningEvent);
   690   NOTIFY_EVENT_OBSERVERS(OnProcessNextEvent,
   691                          (this, reallyWait, mRunningEvent));
   693   ++mRunningEvent;
   695 #ifdef MOZ_CANARY
   696   Canary canary;
   697 #endif
   698   nsresult rv = NS_OK;
   700   {
   701     // Scope for |event| to make sure that its destructor fires while
   702     // mRunningEvent has been incremented, since that destructor can
   703     // also do work.
   705     // If we are shutting down, then do not wait for new events.
   706     nsCOMPtr<nsIRunnable> event;
   707     mEvents->GetEvent(reallyWait, getter_AddRefs(event));
   709     *result = (event.get() != nullptr);
   711     if (event) {
   712       LOG(("THRD(%p) running [%p]\n", this, event.get()));
   713       if (MAIN_THREAD == mIsMainThread)
   714         HangMonitor::NotifyActivity();
   715       event->Run();
   716     } else if (mayWait) {
   717       MOZ_ASSERT(ShuttingDown(),
   718                  "This should only happen when shutting down");
   719       rv = NS_ERROR_UNEXPECTED;
   720     }
   721   }
   723   --mRunningEvent;
   725   NOTIFY_EVENT_OBSERVERS(AfterProcessNextEvent,
   726                          (this, mRunningEvent, *result));
   728   if (obs)
   729     obs->AfterProcessNextEvent(this, mRunningEvent, *result);
   731   if (notifyMainThreadObserver && sMainThreadObserver)
   732     sMainThreadObserver->AfterProcessNextEvent(this, mRunningEvent, *result);
   734   return rv;
   735 }
   737 //-----------------------------------------------------------------------------
   738 // nsISupportsPriority
   740 NS_IMETHODIMP
   741 nsThread::GetPriority(int32_t *priority)
   742 {
   743   *priority = mPriority;
   744   return NS_OK;
   745 }
   747 NS_IMETHODIMP
   748 nsThread::SetPriority(int32_t priority)
   749 {
   750   if (NS_WARN_IF(!mThread))
   751     return NS_ERROR_NOT_INITIALIZED;
   753   // NSPR defines the following four thread priorities:
   754   //   PR_PRIORITY_LOW
   755   //   PR_PRIORITY_NORMAL
   756   //   PR_PRIORITY_HIGH
   757   //   PR_PRIORITY_URGENT
   758   // We map the priority values defined on nsISupportsPriority to these values.
   760   mPriority = priority;
   762   PRThreadPriority pri;
   763   if (mPriority <= PRIORITY_HIGHEST) {
   764     pri = PR_PRIORITY_URGENT;
   765   } else if (mPriority < PRIORITY_NORMAL) {
   766     pri = PR_PRIORITY_HIGH;
   767   } else if (mPriority > PRIORITY_NORMAL) {
   768     pri = PR_PRIORITY_LOW;
   769   } else {
   770     pri = PR_PRIORITY_NORMAL;
   771   }
   772   // If chaos mode is active, retain the randomly chosen priority
   773   if (!ChaosMode::isActive()) {
   774     PR_SetThreadPriority(mThread, pri);
   775   }
   777   return NS_OK;
   778 }
   780 NS_IMETHODIMP
   781 nsThread::AdjustPriority(int32_t delta)
   782 {
   783   return SetPriority(mPriority + delta);
   784 }
   786 //-----------------------------------------------------------------------------
   787 // nsIThreadInternal
   789 NS_IMETHODIMP
   790 nsThread::GetObserver(nsIThreadObserver **obs)
   791 {
   792   MutexAutoLock lock(mLock);
   793   NS_IF_ADDREF(*obs = mObserver);
   794   return NS_OK;
   795 }
   797 NS_IMETHODIMP
   798 nsThread::SetObserver(nsIThreadObserver *obs)
   799 {
   800   if (NS_WARN_IF(PR_GetCurrentThread() != mThread))
   801     return NS_ERROR_NOT_SAME_THREAD;
   803   MutexAutoLock lock(mLock);
   804   mObserver = obs;
   805   return NS_OK;
   806 }
   808 NS_IMETHODIMP
   809 nsThread::GetRecursionDepth(uint32_t *depth)
   810 {
   811   if (NS_WARN_IF(PR_GetCurrentThread() != mThread))
   812     return NS_ERROR_NOT_SAME_THREAD;
   814   *depth = mRunningEvent;
   815   return NS_OK;
   816 }
   818 NS_IMETHODIMP
   819 nsThread::AddObserver(nsIThreadObserver *observer)
   820 {
   821   if (NS_WARN_IF(!observer))
   822     return NS_ERROR_INVALID_ARG;
   823   if (NS_WARN_IF(PR_GetCurrentThread() != mThread))
   824     return NS_ERROR_NOT_SAME_THREAD;
   826   NS_WARN_IF_FALSE(!mEventObservers.Contains(observer),
   827                    "Adding an observer twice!");
   829   if (!mEventObservers.AppendElement(observer)) {
   830     NS_WARNING("Out of memory!");
   831     return NS_ERROR_OUT_OF_MEMORY;
   832   }
   834   return NS_OK;
   835 }
   837 NS_IMETHODIMP
   838 nsThread::RemoveObserver(nsIThreadObserver *observer)
   839 {
   840   if (NS_WARN_IF(PR_GetCurrentThread() != mThread))
   841     return NS_ERROR_NOT_SAME_THREAD;
   843   if (observer && !mEventObservers.RemoveElement(observer)) {
   844     NS_WARNING("Removing an observer that was never added!");
   845   }
   847   return NS_OK;
   848 }
   850 NS_IMETHODIMP
   851 nsThread::PushEventQueue(nsIEventTarget **result)
   852 {
   853   if (NS_WARN_IF(PR_GetCurrentThread() != mThread))
   854     return NS_ERROR_NOT_SAME_THREAD;
   856   nsChainedEventQueue *queue = new nsChainedEventQueue();
   857   queue->mEventTarget = new nsNestedEventTarget(this, queue);
   859   {
   860     MutexAutoLock lock(mLock);
   861     queue->mNext = mEvents;
   862     mEvents = queue;
   863   }
   865   NS_ADDREF(*result = queue->mEventTarget);
   866   return NS_OK;
   867 }
   869 NS_IMETHODIMP
   870 nsThread::PopEventQueue(nsIEventTarget *innermostTarget)
   871 {
   872   if (NS_WARN_IF(PR_GetCurrentThread() != mThread))
   873     return NS_ERROR_NOT_SAME_THREAD;
   875   if (NS_WARN_IF(!innermostTarget))
   876     return NS_ERROR_NULL_POINTER;
   878   // Don't delete or release anything while holding the lock.
   879   nsAutoPtr<nsChainedEventQueue> queue;
   880   nsRefPtr<nsNestedEventTarget> target;
   882   {
   883     MutexAutoLock lock(mLock);
   885     // Make sure we're popping the innermost event target.
   886     if (NS_WARN_IF(mEvents->mEventTarget != innermostTarget))
   887       return NS_ERROR_UNEXPECTED;
   889     MOZ_ASSERT(mEvents != &mEventsRoot);
   891     queue = mEvents;
   892     mEvents = mEvents->mNext;
   894     nsCOMPtr<nsIRunnable> event;
   895     while (queue->GetEvent(false, getter_AddRefs(event)))
   896       mEvents->PutEvent(event);
   898     // Don't let the event target post any more events.
   899     queue->mEventTarget.swap(target);
   900     target->mQueue = nullptr;
   901   }
   903   return NS_OK;
   904 }
   906 nsresult
   907 nsThread::SetMainThreadObserver(nsIThreadObserver* aObserver)
   908 {
   909   if (aObserver && nsThread::sMainThreadObserver) {
   910     return NS_ERROR_NOT_AVAILABLE;
   911   }
   913   if (!NS_IsMainThread()) {
   914     return NS_ERROR_UNEXPECTED;
   915   }
   917   nsThread::sMainThreadObserver = aObserver;
   918   return NS_OK;
   919 }
   921 //-----------------------------------------------------------------------------
   923 NS_IMETHODIMP
   924 nsThreadSyncDispatch::Run()
   925 {
   926   if (mSyncTask) {
   927     mResult = mSyncTask->Run();
   928     mSyncTask = nullptr;
   929     // unblock the origin thread
   930     mOrigin->Dispatch(this, NS_DISPATCH_NORMAL);
   931   }
   932   return NS_OK;
   933 }
   935 //-----------------------------------------------------------------------------
   937 NS_IMPL_ISUPPORTS(nsThread::nsNestedEventTarget, nsIEventTarget)
   939 NS_IMETHODIMP
   940 nsThread::nsNestedEventTarget::Dispatch(nsIRunnable *event, uint32_t flags)
   941 {
   942   LOG(("THRD(%p) Dispatch [%p %x] to nested loop %p\n", mThread.get(), event,
   943        flags, this));
   945   return mThread->DispatchInternal(event, flags, this);
   946 }
   948 NS_IMETHODIMP
   949 nsThread::nsNestedEventTarget::IsOnCurrentThread(bool *result)
   950 {
   951   return mThread->IsOnCurrentThread(result);
   952 }

mercurial