1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
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/. */
6 
7 #include "nsThread.h"
8 
9 #include "base/message_loop.h"
10 
11 // Chromium's logging can sometimes leak through...
12 #ifdef LOG
13 #undef LOG
14 #endif
15 
16 #include "mozilla/ReentrantMonitor.h"
17 #include "nsMemoryPressure.h"
18 #include "nsThreadManager.h"
19 #include "nsIClassInfoImpl.h"
20 #include "nsAutoPtr.h"
21 #include "nsCOMPtr.h"
22 #include "nsQueryObject.h"
23 #include "pratom.h"
24 #include "mozilla/CycleCollectedJSContext.h"
25 #include "mozilla/Logging.h"
26 #include "nsIObserverService.h"
27 #include "mozilla/HangMonitor.h"
28 #include "mozilla/IOInterposer.h"
29 #include "mozilla/ipc/MessageChannel.h"
30 #include "mozilla/ipc/BackgroundChild.h"
31 #include "mozilla/Services.h"
32 #include "nsXPCOMPrivate.h"
33 #include "mozilla/ChaosMode.h"
34 #include "mozilla/TimeStamp.h"
35 #include "mozilla/Unused.h"
36 #include "mozilla/dom/ScriptSettings.h"
37 #include "nsIIdlePeriod.h"
38 #include "nsIIncrementalRunnable.h"
39 #include "nsThreadSyncDispatch.h"
40 #include "LeakRefPtr.h"
41 
42 #ifdef MOZ_CRASHREPORTER
43 #include "nsServiceManagerUtils.h"
44 #include "nsICrashReporter.h"
45 #include "mozilla/dom/ContentChild.h"
46 #endif
47 
48 #ifdef XP_LINUX
49 #include <sys/time.h>
50 #include <sys/resource.h>
51 #include <sched.h>
52 #endif
53 
54 #define HAVE_UALARM _BSD_SOURCE || (_XOPEN_SOURCE >= 500 ||                 \
55                       _XOPEN_SOURCE && _XOPEN_SOURCE_EXTENDED) &&           \
56                       !(_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700)
57 
58 #if defined(XP_LINUX) && !defined(ANDROID) && defined(_GNU_SOURCE)
59 #define HAVE_SCHED_SETAFFINITY
60 #endif
61 
62 #ifdef XP_MACOSX
63 #include <mach/mach.h>
64 #include <mach/thread_policy.h>
65 #endif
66 
67 #ifdef MOZ_CANARY
68 # include <unistd.h>
69 # include <execinfo.h>
70 # include <signal.h>
71 # include <fcntl.h>
72 # include "nsXULAppAPI.h"
73 #endif
74 
75 #if defined(NS_FUNCTION_TIMER) && defined(_MSC_VER)
76 #include "nsTimerImpl.h"
77 #include "mozilla/StackWalk.h"
78 #endif
79 #ifdef NS_FUNCTION_TIMER
80 #include "nsCRT.h"
81 #endif
82 
83 #ifdef MOZ_TASK_TRACER
84 #include "GeckoTaskTracer.h"
85 #include "TracedTaskCommon.h"
86 using namespace mozilla::tasktracer;
87 #endif
88 
89 using namespace mozilla;
90 
91 static LazyLogModule sThreadLog("nsThread");
92 #ifdef LOG
93 #undef LOG
94 #endif
95 #define LOG(args) MOZ_LOG(sThreadLog, mozilla::LogLevel::Debug, args)
96 
97 NS_DECL_CI_INTERFACE_GETTER(nsThread)
98 
99 //-----------------------------------------------------------------------------
100 // Because we do not have our own nsIFactory, we have to implement nsIClassInfo
101 // somewhat manually.
102 
103 class nsThreadClassInfo : public nsIClassInfo
104 {
105 public:
106   NS_DECL_ISUPPORTS_INHERITED  // no mRefCnt
107   NS_DECL_NSICLASSINFO
108 
nsThreadClassInfo()109   nsThreadClassInfo()
110   {
111   }
112 };
113 
NS_IMETHODIMP_(MozExternalRefCountType)114 NS_IMETHODIMP_(MozExternalRefCountType)
115 nsThreadClassInfo::AddRef()
116 {
117   return 2;
118 }
NS_IMETHODIMP_(MozExternalRefCountType)119 NS_IMETHODIMP_(MozExternalRefCountType)
120 nsThreadClassInfo::Release()
121 {
122   return 1;
123 }
NS_IMPL_QUERY_INTERFACE(nsThreadClassInfo,nsIClassInfo)124 NS_IMPL_QUERY_INTERFACE(nsThreadClassInfo, nsIClassInfo)
125 
126 NS_IMETHODIMP
127 nsThreadClassInfo::GetInterfaces(uint32_t* aCount, nsIID*** aArray)
128 {
129   return NS_CI_INTERFACE_GETTER_NAME(nsThread)(aCount, aArray);
130 }
131 
132 NS_IMETHODIMP
GetScriptableHelper(nsIXPCScriptable ** aResult)133 nsThreadClassInfo::GetScriptableHelper(nsIXPCScriptable** aResult)
134 {
135   *aResult = nullptr;
136   return NS_OK;
137 }
138 
139 NS_IMETHODIMP
GetContractID(char ** aResult)140 nsThreadClassInfo::GetContractID(char** aResult)
141 {
142   *aResult = nullptr;
143   return NS_OK;
144 }
145 
146 NS_IMETHODIMP
GetClassDescription(char ** aResult)147 nsThreadClassInfo::GetClassDescription(char** aResult)
148 {
149   *aResult = nullptr;
150   return NS_OK;
151 }
152 
153 NS_IMETHODIMP
GetClassID(nsCID ** aResult)154 nsThreadClassInfo::GetClassID(nsCID** aResult)
155 {
156   *aResult = nullptr;
157   return NS_OK;
158 }
159 
160 NS_IMETHODIMP
GetFlags(uint32_t * aResult)161 nsThreadClassInfo::GetFlags(uint32_t* aResult)
162 {
163   *aResult = THREADSAFE;
164   return NS_OK;
165 }
166 
167 NS_IMETHODIMP
GetClassIDNoAlloc(nsCID * aResult)168 nsThreadClassInfo::GetClassIDNoAlloc(nsCID* aResult)
169 {
170   return NS_ERROR_NOT_AVAILABLE;
171 }
172 
173 //-----------------------------------------------------------------------------
174 
175 NS_IMPL_ADDREF(nsThread)
176 NS_IMPL_RELEASE(nsThread)
177 NS_INTERFACE_MAP_BEGIN(nsThread)
178   NS_INTERFACE_MAP_ENTRY(nsIThread)
179   NS_INTERFACE_MAP_ENTRY(nsIThreadInternal)
180   NS_INTERFACE_MAP_ENTRY(nsIEventTarget)
181   NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
182   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIThread)
183   if (aIID.Equals(NS_GET_IID(nsIClassInfo))) {
184     static nsThreadClassInfo sThreadClassInfo;
185     foundInterface = static_cast<nsIClassInfo*>(&sThreadClassInfo);
186   } else
187 NS_INTERFACE_MAP_END
188 NS_IMPL_CI_INTERFACE_GETTER(nsThread, nsIThread, nsIThreadInternal,
189                             nsIEventTarget, nsISupportsPriority)
190 
191 //-----------------------------------------------------------------------------
192 
193 class nsThreadStartupEvent : public Runnable
194 {
195 public:
nsThreadStartupEvent()196   nsThreadStartupEvent()
197     : mMon("nsThreadStartupEvent.mMon")
198     , mInitialized(false)
199   {
200   }
201 
202   // This method does not return until the thread startup object is in the
203   // completion state.
Wait()204   void Wait()
205   {
206     ReentrantMonitorAutoEnter mon(mMon);
207     while (!mInitialized) {
208       mon.Wait();
209     }
210   }
211 
212   // This method needs to be public to support older compilers (xlC_r on AIX).
213   // It should be called directly as this class type is reference counted.
~nsThreadStartupEvent()214   virtual ~nsThreadStartupEvent() {}
215 
216 private:
Run()217   NS_IMETHOD Run() override
218   {
219     ReentrantMonitorAutoEnter mon(mMon);
220     mInitialized = true;
221     mon.Notify();
222     return NS_OK;
223   }
224 
225   ReentrantMonitor mMon;
226   bool mInitialized;
227 };
228 //-----------------------------------------------------------------------------
229 
230 namespace {
231 class DelayedRunnable : public Runnable,
232                         public nsITimerCallback
233 {
234 public:
DelayedRunnable(already_AddRefed<nsIThread> aTargetThread,already_AddRefed<nsIRunnable> aRunnable,uint32_t aDelay)235   DelayedRunnable(already_AddRefed<nsIThread> aTargetThread,
236                   already_AddRefed<nsIRunnable> aRunnable,
237                   uint32_t aDelay)
238     : mTargetThread(aTargetThread),
239       mWrappedRunnable(aRunnable),
240       mDelayedFrom(TimeStamp::NowLoRes()),
241       mDelay(aDelay)
242   { }
243 
244   NS_DECL_ISUPPORTS_INHERITED
245 
Init()246   nsresult Init()
247   {
248     nsresult rv;
249     mTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
250     NS_ENSURE_SUCCESS(rv, rv);
251 
252     MOZ_ASSERT(mTimer);
253     rv = mTimer->SetTarget(mTargetThread);
254 
255     NS_ENSURE_SUCCESS(rv, rv);
256     return mTimer->InitWithCallback(this, mDelay, nsITimer::TYPE_ONE_SHOT);
257   }
258 
DoRun()259   nsresult DoRun()
260   {
261     nsCOMPtr<nsIRunnable> r = mWrappedRunnable.forget();
262     return r->Run();
263   }
264 
Run()265   NS_IMETHOD Run() override
266   {
267     // Already ran?
268     if (!mWrappedRunnable) {
269       return NS_OK;
270     }
271 
272     // Are we too early?
273     if ((TimeStamp::NowLoRes() - mDelayedFrom).ToMilliseconds() < mDelay) {
274       return NS_OK; // Let the nsITimer run us.
275     }
276 
277     mTimer->Cancel();
278     return DoRun();
279   }
280 
Notify(nsITimer * aTimer)281   NS_IMETHOD Notify(nsITimer* aTimer) override
282   {
283     // If we already ran, the timer should have been canceled.
284     MOZ_ASSERT(mWrappedRunnable);
285     MOZ_ASSERT(aTimer == mTimer);
286 
287     return DoRun();
288   }
289 
290 private:
~DelayedRunnable()291   ~DelayedRunnable() {}
292 
293   nsCOMPtr<nsIThread> mTargetThread;
294   nsCOMPtr<nsIRunnable> mWrappedRunnable;
295   nsCOMPtr<nsITimer> mTimer;
296   TimeStamp mDelayedFrom;
297   uint32_t mDelay;
298 };
299 
300 NS_IMPL_ISUPPORTS_INHERITED(DelayedRunnable, Runnable, nsITimerCallback)
301 
302 } // anonymous namespace
303 
304 //-----------------------------------------------------------------------------
305 
306 struct nsThreadShutdownContext
307 {
nsThreadShutdownContextnsThreadShutdownContext308   nsThreadShutdownContext(NotNull<nsThread*> aTerminatingThread,
309                           NotNull<nsThread*> aJoiningThread,
310                           bool      aAwaitingShutdownAck)
311     : mTerminatingThread(aTerminatingThread)
312     , mJoiningThread(aJoiningThread)
313     , mAwaitingShutdownAck(aAwaitingShutdownAck)
314   {
315     MOZ_COUNT_CTOR(nsThreadShutdownContext);
316   }
~nsThreadShutdownContextnsThreadShutdownContext317   ~nsThreadShutdownContext()
318   {
319     MOZ_COUNT_DTOR(nsThreadShutdownContext);
320   }
321 
322   // NB: This will be the last reference.
323   NotNull<RefPtr<nsThread>> mTerminatingThread;
324   NotNull<nsThread*> mJoiningThread;
325   bool mAwaitingShutdownAck;
326 };
327 
328 // This event is responsible for notifying nsThread::Shutdown that it is time
329 // to call PR_JoinThread. It implements nsICancelableRunnable so that it can
330 // run on a DOM Worker thread (where all events must implement
331 // nsICancelableRunnable.)
332 class nsThreadShutdownAckEvent : public CancelableRunnable
333 {
334 public:
nsThreadShutdownAckEvent(NotNull<nsThreadShutdownContext * > aCtx)335   explicit nsThreadShutdownAckEvent(NotNull<nsThreadShutdownContext*> aCtx)
336     : mShutdownContext(aCtx)
337   {
338   }
Run()339   NS_IMETHOD Run() override
340   {
341     mShutdownContext->mTerminatingThread->ShutdownComplete(mShutdownContext);
342     return NS_OK;
343   }
Cancel()344   nsresult Cancel() override
345   {
346     return Run();
347   }
348 private:
~nsThreadShutdownAckEvent()349   virtual ~nsThreadShutdownAckEvent() { }
350 
351   NotNull<nsThreadShutdownContext*> mShutdownContext;
352 };
353 
354 // This event is responsible for setting mShutdownContext
355 class nsThreadShutdownEvent : public Runnable
356 {
357 public:
nsThreadShutdownEvent(NotNull<nsThread * > aThr,NotNull<nsThreadShutdownContext * > aCtx)358   nsThreadShutdownEvent(NotNull<nsThread*> aThr,
359                         NotNull<nsThreadShutdownContext*> aCtx)
360     : mThread(aThr)
361     , mShutdownContext(aCtx)
362   {
363   }
Run()364   NS_IMETHOD Run() override
365   {
366     mThread->mShutdownContext = mShutdownContext;
367     MessageLoop::current()->Quit();
368     return NS_OK;
369   }
370 private:
371   NotNull<RefPtr<nsThread>> mThread;
372   NotNull<nsThreadShutdownContext*> mShutdownContext;
373 };
374 
375 //-----------------------------------------------------------------------------
376 
377 static void
SetThreadAffinity(unsigned int cpu)378 SetThreadAffinity(unsigned int cpu)
379 {
380 #ifdef HAVE_SCHED_SETAFFINITY
381   cpu_set_t cpus;
382   CPU_ZERO(&cpus);
383   CPU_SET(cpu, &cpus);
384   sched_setaffinity(0, sizeof(cpus), &cpus);
385   // Don't assert sched_setaffinity's return value because it intermittently (?)
386   // fails with EINVAL on Linux x64 try runs.
387 #elif defined(XP_MACOSX)
388   // OS X does not provide APIs to pin threads to specific processors, but you
389   // can tag threads as belonging to the same "affinity set" and the OS will try
390   // to run them on the same processor. To run threads on different processors,
391   // tag them as belonging to different affinity sets. Tag 0, the default, means
392   // "no affinity" so let's pretend each CPU has its own tag `cpu+1`.
393   thread_affinity_policy_data_t policy;
394   policy.affinity_tag = cpu + 1;
395   MOZ_ALWAYS_TRUE(thread_policy_set(mach_thread_self(), THREAD_AFFINITY_POLICY,
396                                     &policy.affinity_tag, 1) == KERN_SUCCESS);
397 #elif defined(XP_WIN)
398   MOZ_ALWAYS_TRUE(SetThreadIdealProcessor(GetCurrentThread(), cpu) != -1);
399 #endif
400 }
401 
402 static void
SetupCurrentThreadForChaosMode()403 SetupCurrentThreadForChaosMode()
404 {
405   if (!ChaosMode::isActive(ChaosFeature::ThreadScheduling)) {
406     return;
407   }
408 
409 #ifdef XP_LINUX
410   // PR_SetThreadPriority doesn't really work since priorities >
411   // PR_PRIORITY_NORMAL can't be set by non-root users. Instead we'll just use
412   // setpriority(2) to set random 'nice values'. In regular Linux this is only
413   // a dynamic adjustment so it still doesn't really do what we want, but tools
414   // like 'rr' can be more aggressive about honoring these values.
415   // Some of these calls may fail due to trying to lower the priority
416   // (e.g. something may have already called setpriority() for this thread).
417   // This makes it hard to have non-main threads with higher priority than the
418   // main thread, but that's hard to fix. Tools like rr can choose to honor the
419   // requested values anyway.
420   // Use just 4 priorities so there's a reasonable chance of any two threads
421   // having equal priority.
422   setpriority(PRIO_PROCESS, 0, ChaosMode::randomUint32LessThan(4));
423 #else
424   // We should set the affinity here but NSPR doesn't provide a way to expose it.
425   uint32_t priority = ChaosMode::randomUint32LessThan(PR_PRIORITY_LAST + 1);
426   PR_SetThreadPriority(PR_GetCurrentThread(), PRThreadPriority(priority));
427 #endif
428 
429   // Force half the threads to CPU 0 so they compete for CPU
430   if (ChaosMode::randomUint32LessThan(2)) {
431     SetThreadAffinity(0);
432   }
433 }
434 
435 /*static*/ void
ThreadFunc(void * aArg)436 nsThread::ThreadFunc(void* aArg)
437 {
438   using mozilla::ipc::BackgroundChild;
439 
440   nsThread* self = static_cast<nsThread*>(aArg);  // strong reference
441   self->mThread = PR_GetCurrentThread();
442   SetupCurrentThreadForChaosMode();
443 
444   // Inform the ThreadManager
445   nsThreadManager::get().RegisterCurrentThread(*self);
446 
447   mozilla::IOInterposer::RegisterCurrentThread();
448 
449   // Wait for and process startup event
450   nsCOMPtr<nsIRunnable> event;
451   {
452     MutexAutoLock lock(self->mLock);
453     if (!self->mEvents->GetEvent(true, getter_AddRefs(event), lock)) {
454       NS_WARNING("failed waiting for thread startup event");
455       return;
456     }
457   }
458   event->Run();  // unblocks nsThread::Init
459   event = nullptr;
460 
461   {
462     // Scope for MessageLoop.
463     nsAutoPtr<MessageLoop> loop(
464       new MessageLoop(MessageLoop::TYPE_MOZILLA_NONMAINTHREAD, self));
465 
466     // Now, process incoming events...
467     loop->Run();
468 
469     BackgroundChild::CloseForCurrentThread();
470 
471     // NB: The main thread does not shut down here!  It shuts down via
472     // nsThreadManager::Shutdown.
473 
474     // Do NS_ProcessPendingEvents but with special handling to set
475     // mEventsAreDoomed atomically with the removal of the last event. The key
476     // invariant here is that we will never permit PutEvent to succeed if the
477     // event would be left in the queue after our final call to
478     // NS_ProcessPendingEvents. We also have to keep processing events as long
479     // as we have outstanding mRequestedShutdownContexts.
480     while (true) {
481       // Check and see if we're waiting on any threads.
482       self->WaitForAllAsynchronousShutdowns();
483 
484       {
485         MutexAutoLock lock(self->mLock);
486         if (!self->mEvents->HasPendingEvent(lock)) {
487           // No events in the queue, so we will stop now. Don't let any more
488           // events be added, since they won't be processed. It is critical
489           // that no PutEvent can occur between testing that the event queue is
490           // empty and setting mEventsAreDoomed!
491           self->mEventsAreDoomed = true;
492           break;
493         }
494       }
495       NS_ProcessPendingEvents(self);
496     }
497   }
498 
499   mozilla::IOInterposer::UnregisterCurrentThread();
500 
501   // Inform the threadmanager that this thread is going away
502   nsThreadManager::get().UnregisterCurrentThread(*self);
503 
504   // Dispatch shutdown ACK
505   NotNull<nsThreadShutdownContext*> context =
506     WrapNotNull(self->mShutdownContext);
507   MOZ_ASSERT(context->mTerminatingThread == self);
508   event = do_QueryObject(new nsThreadShutdownAckEvent(context));
509   context->mJoiningThread->Dispatch(event, NS_DISPATCH_NORMAL);
510 
511   // Release any observer of the thread here.
512   self->SetObserver(nullptr);
513 
514 #ifdef MOZ_TASK_TRACER
515   FreeTraceInfo();
516 #endif
517 
518   NS_RELEASE(self);
519 }
520 
521 //-----------------------------------------------------------------------------
522 
523 #ifdef MOZ_CRASHREPORTER
524 // Tell the crash reporter to save a memory report if our heuristics determine
525 // that an OOM failure is likely to occur soon.
526 // Memory usage will not be checked more than every 30 seconds or saved more
527 // than every 3 minutes
528 // If |aShouldSave == kForceReport|, a report will be saved regardless of
529 // whether the process is low on memory or not. However, it will still not be
530 // saved if a report was saved less than 3 minutes ago.
531 bool
SaveMemoryReportNearOOM(ShouldSaveMemoryReport aShouldSave)532 nsThread::SaveMemoryReportNearOOM(ShouldSaveMemoryReport aShouldSave)
533 {
534   // Keep an eye on memory usage (cheap, ~7ms) somewhat frequently,
535   // but save memory reports (expensive, ~75ms) less frequently.
536   const size_t kLowMemoryCheckSeconds = 30;
537   const size_t kLowMemorySaveSeconds = 3 * 60;
538 
539   static TimeStamp nextCheck = TimeStamp::NowLoRes()
540     + TimeDuration::FromSeconds(kLowMemoryCheckSeconds);
541   static bool recentlySavedReport = false; // Keeps track of whether a report
542                                            // was saved last time we checked
543 
544   // Are we checking again too soon?
545   TimeStamp now = TimeStamp::NowLoRes();
546   if ((aShouldSave == ShouldSaveMemoryReport::kMaybeReport ||
547       recentlySavedReport) && now < nextCheck) {
548     return false;
549   }
550 
551   bool needMemoryReport = (aShouldSave == ShouldSaveMemoryReport::kForceReport);
552 #ifdef XP_WIN // XXX implement on other platforms as needed
553   // If the report is forced there is no need to check whether it is necessary
554   if (aShouldSave != ShouldSaveMemoryReport::kForceReport) {
555     const size_t LOWMEM_THRESHOLD_VIRTUAL = 200 * 1024 * 1024;
556     MEMORYSTATUSEX statex;
557     statex.dwLength = sizeof(statex);
558     if (GlobalMemoryStatusEx(&statex)) {
559       if (statex.ullAvailVirtual < LOWMEM_THRESHOLD_VIRTUAL) {
560         needMemoryReport = true;
561       }
562     }
563   }
564 #endif
565 
566   if (needMemoryReport) {
567     if (XRE_IsContentProcess()) {
568       dom::ContentChild* cc = dom::ContentChild::GetSingleton();
569       if (cc) {
570         cc->SendNotifyLowMemory();
571       }
572     } else {
573       nsCOMPtr<nsICrashReporter> cr =
574         do_GetService("@mozilla.org/toolkit/crash-reporter;1");
575       if (cr) {
576         cr->SaveMemoryReport();
577       }
578     }
579     recentlySavedReport = true;
580     nextCheck = now + TimeDuration::FromSeconds(kLowMemorySaveSeconds);
581   } else {
582     recentlySavedReport = false;
583     nextCheck = now + TimeDuration::FromSeconds(kLowMemoryCheckSeconds);
584   }
585 
586   return recentlySavedReport;
587 }
588 #endif
589 
590 #ifdef MOZ_CANARY
591 int sCanaryOutputFD = -1;
592 #endif
593 
nsThread(MainThreadFlag aMainThread,uint32_t aStackSize)594 nsThread::nsThread(MainThreadFlag aMainThread, uint32_t aStackSize)
595   : mLock("nsThread.mLock")
596   , mScriptObserver(nullptr)
597   , mEvents(WrapNotNull(&mEventsRoot))
598   , mEventsRoot(mLock)
599   , mIdleEventsAvailable(mLock, "[nsThread.mEventsAvailable]")
600   , mIdleEvents(mIdleEventsAvailable, nsEventQueue::eNormalQueue)
601   , mPriority(PRIORITY_NORMAL)
602   , mThread(nullptr)
603   , mNestedEventLoopDepth(0)
604   , mStackSize(aStackSize)
605   , mShutdownContext(nullptr)
606   , mShutdownRequired(false)
607   , mEventsAreDoomed(false)
608   , mIsMainThread(aMainThread)
609   , mCanInvokeJS(false)
610 {
611 }
612 
~nsThread()613 nsThread::~nsThread()
614 {
615   NS_ASSERTION(mRequestedShutdownContexts.IsEmpty(),
616                "shouldn't be waiting on other threads to shutdown");
617 #ifdef DEBUG
618   // We deliberately leak these so they can be tracked by the leak checker.
619   // If you're having nsThreadShutdownContext leaks, you can set:
620   //   XPCOM_MEM_LOG_CLASSES=nsThreadShutdownContext
621   // during a test run and that will at least tell you what thread is
622   // requesting shutdown on another, which can be helpful for diagnosing
623   // the leak.
624   for (size_t i = 0; i < mRequestedShutdownContexts.Length(); ++i) {
625     Unused << mRequestedShutdownContexts[i].forget();
626   }
627 #endif
628 }
629 
630 nsresult
Init()631 nsThread::Init()
632 {
633   // spawn thread and wait until it is fully setup
634   RefPtr<nsThreadStartupEvent> startup = new nsThreadStartupEvent();
635 
636   NS_ADDREF_THIS();
637 
638   mIdlePeriod = new IdlePeriod();
639 
640   mShutdownRequired = true;
641 
642   // ThreadFunc is responsible for setting mThread
643   if (!PR_CreateThread(PR_USER_THREAD, ThreadFunc, this,
644                        PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
645                        PR_JOINABLE_THREAD, mStackSize)) {
646     NS_RELEASE_THIS();
647     return NS_ERROR_OUT_OF_MEMORY;
648   }
649 
650   // ThreadFunc will wait for this event to be run before it tries to access
651   // mThread.  By delaying insertion of this event into the queue, we ensure
652   // that mThread is set properly.
653   {
654     MutexAutoLock lock(mLock);
655     mEventsRoot.PutEvent(startup, lock); // retain a reference
656   }
657 
658   // Wait for thread to call ThreadManager::SetupCurrentThread, which completes
659   // initialization of ThreadFunc.
660   startup->Wait();
661   return NS_OK;
662 }
663 
664 nsresult
InitCurrentThread()665 nsThread::InitCurrentThread()
666 {
667   mThread = PR_GetCurrentThread();
668   SetupCurrentThreadForChaosMode();
669 
670   mIdlePeriod = new IdlePeriod();
671 
672   nsThreadManager::get().RegisterCurrentThread(*this);
673   return NS_OK;
674 }
675 
676 nsresult
PutEvent(nsIRunnable * aEvent,nsNestedEventTarget * aTarget)677 nsThread::PutEvent(nsIRunnable* aEvent, nsNestedEventTarget* aTarget)
678 {
679   nsCOMPtr<nsIRunnable> event(aEvent);
680   return PutEvent(event.forget(), aTarget);
681 }
682 
683 nsresult
PutEvent(already_AddRefed<nsIRunnable> aEvent,nsNestedEventTarget * aTarget)684 nsThread::PutEvent(already_AddRefed<nsIRunnable> aEvent, nsNestedEventTarget* aTarget)
685 {
686   // We want to leak the reference when we fail to dispatch it, so that
687   // we won't release the event in a wrong thread.
688   LeakRefPtr<nsIRunnable> event(Move(aEvent));
689   nsCOMPtr<nsIThreadObserver> obs;
690 
691   {
692     MutexAutoLock lock(mLock);
693     nsChainedEventQueue* queue = aTarget ? aTarget->mQueue : &mEventsRoot;
694     if (!queue || (queue == &mEventsRoot && mEventsAreDoomed)) {
695       NS_WARNING("An event was posted to a thread that will never run it (rejected)");
696       return NS_ERROR_UNEXPECTED;
697     }
698     queue->PutEvent(event.take(), lock);
699 
700     // Make sure to grab the observer before dropping the lock, otherwise the
701     // event that we just placed into the queue could run and eventually delete
702     // this nsThread before the calling thread is scheduled again. We would then
703     // crash while trying to access a dead nsThread.
704     obs = mObserver;
705   }
706 
707   if (obs) {
708     obs->OnDispatchedEvent(this);
709   }
710 
711   return NS_OK;
712 }
713 
714 nsresult
DispatchInternal(already_AddRefed<nsIRunnable> aEvent,uint32_t aFlags,nsNestedEventTarget * aTarget)715 nsThread::DispatchInternal(already_AddRefed<nsIRunnable> aEvent, uint32_t aFlags,
716                            nsNestedEventTarget* aTarget)
717 {
718   // We want to leak the reference when we fail to dispatch it, so that
719   // we won't release the event in a wrong thread.
720   LeakRefPtr<nsIRunnable> event(Move(aEvent));
721   if (NS_WARN_IF(!event)) {
722     return NS_ERROR_INVALID_ARG;
723   }
724 
725   if (gXPCOMThreadsShutDown && MAIN_THREAD != mIsMainThread && !aTarget) {
726     NS_ASSERTION(false, "Failed Dispatch after xpcom-shutdown-threads");
727     return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
728   }
729 
730 #ifdef MOZ_TASK_TRACER
731   nsCOMPtr<nsIRunnable> tracedRunnable = CreateTracedRunnable(event.take());
732   (static_cast<TracedRunnable*>(tracedRunnable.get()))->DispatchTask();
733   // XXX tracedRunnable will always leaked when we fail to disptch.
734   event = tracedRunnable.forget();
735 #endif
736 
737   if (aFlags & DISPATCH_SYNC) {
738     nsThread* thread = nsThreadManager::get().GetCurrentThread();
739     if (NS_WARN_IF(!thread)) {
740       return NS_ERROR_NOT_AVAILABLE;
741     }
742 
743     // XXX we should be able to do something better here... we should
744     //     be able to monitor the slot occupied by this event and use
745     //     that to tell us when the event has been processed.
746 
747     RefPtr<nsThreadSyncDispatch> wrapper =
748       new nsThreadSyncDispatch(thread, event.take());
749     nsresult rv = PutEvent(wrapper, aTarget); // hold a ref
750     // Don't wait for the event to finish if we didn't dispatch it...
751     if (NS_FAILED(rv)) {
752       // PutEvent leaked the wrapper runnable object on failure, so we
753       // explicitly release this object once for that. Note that this
754       // object will be released again soon because it exits the scope.
755       wrapper.get()->Release();
756       return rv;
757     }
758 
759     // Allows waiting; ensure no locks are held that would deadlock us!
760     while (wrapper->IsPending()) {
761       NS_ProcessNextEvent(thread, true);
762     }
763     return NS_OK;
764   }
765 
766   NS_ASSERTION(aFlags == NS_DISPATCH_NORMAL ||
767                aFlags == NS_DISPATCH_AT_END, "unexpected dispatch flags");
768   return PutEvent(event.take(), aTarget);
769 }
770 
771 bool
GetEvent(bool aMayWait,nsIRunnable ** aEvent,mozilla::MutexAutoLock & aProofOfLock)772 nsThread::nsChainedEventQueue::GetEvent(bool aMayWait, nsIRunnable** aEvent,
773                                         mozilla::MutexAutoLock& aProofOfLock)
774 {
775   bool retVal = false;
776   do {
777     if (mProcessSecondaryQueueRunnable) {
778       MOZ_ASSERT(mSecondaryQueue->HasPendingEvent(aProofOfLock));
779       retVal = mSecondaryQueue->GetEvent(aMayWait, aEvent, aProofOfLock);
780       MOZ_ASSERT(*aEvent);
781       mProcessSecondaryQueueRunnable = false;
782       return retVal;
783     }
784 
785     // We don't want to wait if mSecondaryQueue has some events.
786     bool reallyMayWait =
787       aMayWait && !mSecondaryQueue->HasPendingEvent(aProofOfLock);
788     retVal =
789       mNormalQueue->GetEvent(reallyMayWait, aEvent, aProofOfLock);
790 
791     // Let's see if we should next time process an event from the secondary
792     // queue.
793     mProcessSecondaryQueueRunnable =
794       mSecondaryQueue->HasPendingEvent(aProofOfLock);
795 
796     if (*aEvent) {
797       // We got an event, return early.
798       return retVal;
799     }
800   } while(aMayWait || mProcessSecondaryQueueRunnable);
801 
802   return retVal;
803 }
804 
805 //-----------------------------------------------------------------------------
806 // nsIEventTarget
807 
808 NS_IMETHODIMP
DispatchFromScript(nsIRunnable * aEvent,uint32_t aFlags)809 nsThread::DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags)
810 {
811   nsCOMPtr<nsIRunnable> event(aEvent);
812   return Dispatch(event.forget(), aFlags);
813 }
814 
815 NS_IMETHODIMP
Dispatch(already_AddRefed<nsIRunnable> aEvent,uint32_t aFlags)816 nsThread::Dispatch(already_AddRefed<nsIRunnable> aEvent, uint32_t aFlags)
817 {
818   LOG(("THRD(%p) Dispatch [%p %x]\n", this, /* XXX aEvent */nullptr, aFlags));
819 
820   return DispatchInternal(Move(aEvent), aFlags, nullptr);
821 }
822 
823 NS_IMETHODIMP
DelayedDispatch(already_AddRefed<nsIRunnable> aEvent,uint32_t aDelayMs)824 nsThread::DelayedDispatch(already_AddRefed<nsIRunnable> aEvent, uint32_t aDelayMs)
825 {
826   NS_ENSURE_TRUE(!!aDelayMs, NS_ERROR_UNEXPECTED);
827 
828   RefPtr<DelayedRunnable> r = new DelayedRunnable(Move(do_AddRef(this)),
829                                                   Move(aEvent),
830                                                   aDelayMs);
831   nsresult rv = r->Init();
832   NS_ENSURE_SUCCESS(rv, rv);
833 
834   return DispatchInternal(r.forget(), 0, nullptr);
835 }
836 
837 NS_IMETHODIMP
IsOnCurrentThread(bool * aResult)838 nsThread::IsOnCurrentThread(bool* aResult)
839 {
840   *aResult = (PR_GetCurrentThread() == mThread);
841   return NS_OK;
842 }
843 
844 //-----------------------------------------------------------------------------
845 // nsIThread
846 
847 NS_IMETHODIMP
GetPRThread(PRThread ** aResult)848 nsThread::GetPRThread(PRThread** aResult)
849 {
850   *aResult = mThread;
851   return NS_OK;
852 }
853 
854 NS_IMETHODIMP
GetCanInvokeJS(bool * aResult)855 nsThread::GetCanInvokeJS(bool* aResult)
856 {
857   *aResult = mCanInvokeJS;
858   return NS_OK;
859 }
860 
861 NS_IMETHODIMP
SetCanInvokeJS(bool aCanInvokeJS)862 nsThread::SetCanInvokeJS(bool aCanInvokeJS)
863 {
864   mCanInvokeJS = aCanInvokeJS;
865   return NS_OK;
866 }
867 
868 NS_IMETHODIMP
AsyncShutdown()869 nsThread::AsyncShutdown()
870 {
871   LOG(("THRD(%p) async shutdown\n", this));
872 
873   // XXX If we make this warn, then we hit that warning at xpcom shutdown while
874   //     shutting down a thread in a thread pool.  That happens b/c the thread
875   //     in the thread pool is already shutdown by the thread manager.
876   if (!mThread) {
877     return NS_OK;
878   }
879 
880   return !!ShutdownInternal(/* aSync = */ false) ? NS_OK : NS_ERROR_UNEXPECTED;
881 }
882 
883 nsThreadShutdownContext*
ShutdownInternal(bool aSync)884 nsThread::ShutdownInternal(bool aSync)
885 {
886   MOZ_ASSERT(mThread);
887   MOZ_ASSERT(mThread != PR_GetCurrentThread());
888   if (NS_WARN_IF(mThread == PR_GetCurrentThread())) {
889     return nullptr;
890   }
891 
892   // Prevent multiple calls to this method
893   {
894     MutexAutoLock lock(mLock);
895     if (!mShutdownRequired) {
896       return nullptr;
897     }
898     mShutdownRequired = false;
899   }
900 
901   NotNull<nsThread*> currentThread =
902     WrapNotNull(nsThreadManager::get().GetCurrentThread());
903 
904   nsAutoPtr<nsThreadShutdownContext>& context =
905     *currentThread->mRequestedShutdownContexts.AppendElement();
906   context = new nsThreadShutdownContext(WrapNotNull(this), currentThread, aSync);
907 
908   // Set mShutdownContext and wake up the thread in case it is waiting for
909   // events to process.
910   nsCOMPtr<nsIRunnable> event =
911     new nsThreadShutdownEvent(WrapNotNull(this), WrapNotNull(context.get()));
912   // XXXroc What if posting the event fails due to OOM?
913   PutEvent(event.forget(), nullptr);
914 
915   // We could still end up with other events being added after the shutdown
916   // task, but that's okay because we process pending events in ThreadFunc
917   // after setting mShutdownContext just before exiting.
918   return context;
919 }
920 
921 void
ShutdownComplete(NotNull<nsThreadShutdownContext * > aContext)922 nsThread::ShutdownComplete(NotNull<nsThreadShutdownContext*> aContext)
923 {
924   MOZ_ASSERT(mThread);
925   MOZ_ASSERT(aContext->mTerminatingThread == this);
926 
927   if (aContext->mAwaitingShutdownAck) {
928     // We're in a synchronous shutdown, so tell whatever is up the stack that
929     // we're done and unwind the stack so it can call us again.
930     aContext->mAwaitingShutdownAck = false;
931     return;
932   }
933 
934   // Now, it should be safe to join without fear of dead-locking.
935 
936   PR_JoinThread(mThread);
937   mThread = nullptr;
938 
939   // We hold strong references to our event observers, and once the thread is
940   // shut down the observers can't easily unregister themselves. Do it here
941   // to avoid leaking.
942   ClearObservers();
943 
944 #ifdef DEBUG
945   {
946     MutexAutoLock lock(mLock);
947     MOZ_ASSERT(!mObserver, "Should have been cleared at shutdown!");
948   }
949 #endif
950 
951   // Delete aContext.
952   MOZ_ALWAYS_TRUE(
953     aContext->mJoiningThread->mRequestedShutdownContexts.RemoveElement(aContext));
954 }
955 
956 void
WaitForAllAsynchronousShutdowns()957 nsThread::WaitForAllAsynchronousShutdowns()
958 {
959   while (mRequestedShutdownContexts.Length()) {
960     NS_ProcessNextEvent(this, true);
961   }
962 }
963 
964 NS_IMETHODIMP
Shutdown()965 nsThread::Shutdown()
966 {
967   LOG(("THRD(%p) sync shutdown\n", this));
968 
969   // XXX If we make this warn, then we hit that warning at xpcom shutdown while
970   //     shutting down a thread in a thread pool.  That happens b/c the thread
971   //     in the thread pool is already shutdown by the thread manager.
972   if (!mThread) {
973     return NS_OK;
974   }
975 
976   nsThreadShutdownContext* maybeContext = ShutdownInternal(/* aSync = */ true);
977   NS_ENSURE_TRUE(maybeContext, NS_ERROR_UNEXPECTED);
978   NotNull<nsThreadShutdownContext*> context = WrapNotNull(maybeContext);
979 
980   // Process events on the current thread until we receive a shutdown ACK.
981   // Allows waiting; ensure no locks are held that would deadlock us!
982   while (context->mAwaitingShutdownAck) {
983     NS_ProcessNextEvent(context->mJoiningThread, true);
984   }
985 
986   ShutdownComplete(context);
987 
988   return NS_OK;
989 }
990 
991 NS_IMETHODIMP
HasPendingEvents(bool * aResult)992 nsThread::HasPendingEvents(bool* aResult)
993 {
994   if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
995     return NS_ERROR_NOT_SAME_THREAD;
996   }
997 
998   {
999     MutexAutoLock lock(mLock);
1000     *aResult = mEvents->HasPendingEvent(lock);
1001   }
1002   return NS_OK;
1003 }
1004 
1005 NS_IMETHODIMP
RegisterIdlePeriod(already_AddRefed<nsIIdlePeriod> aIdlePeriod)1006 nsThread::RegisterIdlePeriod(already_AddRefed<nsIIdlePeriod> aIdlePeriod)
1007 {
1008   if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
1009     return NS_ERROR_NOT_SAME_THREAD;
1010   }
1011 
1012   MutexAutoLock lock(mLock);
1013   mIdlePeriod = aIdlePeriod;
1014   return NS_OK;
1015 }
1016 
1017 NS_IMETHODIMP
IdleDispatch(already_AddRefed<nsIRunnable> aEvent)1018 nsThread::IdleDispatch(already_AddRefed<nsIRunnable> aEvent)
1019 {
1020   // Currently the only supported idle dispatch is from the same
1021   // thread. To support idle dispatch from another thread we need to
1022   // support waking threads that are waiting for an event queue that
1023   // isn't mIdleEvents.
1024   MOZ_ASSERT(PR_GetCurrentThread() == mThread);
1025 
1026   MutexAutoLock lock(mLock);
1027   LeakRefPtr<nsIRunnable> event(Move(aEvent));
1028 
1029   if (NS_WARN_IF(!event)) {
1030     return NS_ERROR_INVALID_ARG;
1031   }
1032 
1033   if (mEventsAreDoomed) {
1034     NS_WARNING("An idle event was posted to a thread that will never run it (rejected)");
1035     return NS_ERROR_UNEXPECTED;
1036   }
1037 
1038   mIdleEvents.PutEvent(event.take(), lock);
1039   return NS_OK;
1040 }
1041 
1042 #ifdef MOZ_CANARY
1043 void canary_alarm_handler(int signum);
1044 
1045 class Canary
1046 {
1047   //XXX ToDo: support nested loops
1048 public:
Canary()1049   Canary()
1050   {
1051     if (sCanaryOutputFD > 0 && EventLatencyIsImportant()) {
1052       signal(SIGALRM, canary_alarm_handler);
1053       ualarm(15000, 0);
1054     }
1055   }
1056 
~Canary()1057   ~Canary()
1058   {
1059     if (sCanaryOutputFD != 0 && EventLatencyIsImportant()) {
1060       ualarm(0, 0);
1061     }
1062   }
1063 
EventLatencyIsImportant()1064   static bool EventLatencyIsImportant()
1065   {
1066     return NS_IsMainThread() && XRE_IsParentProcess();
1067   }
1068 };
1069 
canary_alarm_handler(int signum)1070 void canary_alarm_handler(int signum)
1071 {
1072   void* array[30];
1073   const char msg[29] = "event took too long to run:\n";
1074   // use write to be safe in the signal handler
1075   write(sCanaryOutputFD, msg, sizeof(msg));
1076   backtrace_symbols_fd(array, backtrace(array, 30), sCanaryOutputFD);
1077 }
1078 
1079 #endif
1080 
1081 #define NOTIFY_EVENT_OBSERVERS(func_, params_)                                 \
1082   PR_BEGIN_MACRO                                                               \
1083     if (!mEventObservers.IsEmpty()) {                                          \
1084       nsAutoTObserverArray<NotNull<nsCOMPtr<nsIThreadObserver>>, 2>::ForwardIterator \
1085         iter_(mEventObservers);                                                \
1086       nsCOMPtr<nsIThreadObserver> obs_;                                        \
1087       while (iter_.HasMore()) {                                                \
1088         obs_ = iter_.GetNext();                                                \
1089         obs_ -> func_ params_ ;                                                \
1090       }                                                                        \
1091     }                                                                          \
1092   PR_END_MACRO
1093 
1094 void
GetIdleEvent(nsIRunnable ** aEvent,MutexAutoLock & aProofOfLock)1095 nsThread::GetIdleEvent(nsIRunnable** aEvent, MutexAutoLock& aProofOfLock)
1096 {
1097   MOZ_ASSERT(PR_GetCurrentThread() == mThread);
1098   MOZ_ASSERT(aEvent);
1099 
1100   TimeStamp idleDeadline;
1101   {
1102     MutexAutoUnlock unlock(mLock);
1103     mIdlePeriod->GetIdlePeriodHint(&idleDeadline);
1104   }
1105 
1106   if (!idleDeadline || idleDeadline < TimeStamp::Now()) {
1107     aEvent = nullptr;
1108     return;
1109   }
1110 
1111   mIdleEvents.GetEvent(false, aEvent, aProofOfLock);
1112 
1113   if (*aEvent) {
1114     nsCOMPtr<nsIIncrementalRunnable> incrementalEvent(do_QueryInterface(*aEvent));
1115     if (incrementalEvent) {
1116       incrementalEvent->SetDeadline(idleDeadline);
1117     }
1118   }
1119 }
1120 
1121 void
GetEvent(bool aWait,nsIRunnable ** aEvent,MutexAutoLock & aProofOfLock)1122 nsThread::GetEvent(bool aWait, nsIRunnable** aEvent, MutexAutoLock& aProofOfLock)
1123 {
1124   MOZ_ASSERT(PR_GetCurrentThread() == mThread);
1125   MOZ_ASSERT(aEvent);
1126 
1127   // We'll try to get an event to execute in three stages.
1128   // [1] First we just try to get it from the regular queue without waiting.
1129   mEvents->GetEvent(false, aEvent, aProofOfLock);
1130 
1131   // [2] If we didn't get an event from the regular queue, try to
1132   // get one from the idle queue
1133   if (!*aEvent) {
1134     // Since events in mEvents have higher priority than idle
1135     // events, we will only consider idle events when there are no
1136     // pending events in mEvents. We will for the same reason never
1137     // wait for an idle event, since a higher priority event might
1138     // appear at any time.
1139     GetIdleEvent(aEvent, aProofOfLock);
1140   }
1141 
1142   // [3] If we neither got an event from the regular queue nor the
1143   // idle queue, then if we should wait for events we block on the
1144   // main queue until an event is available.
1145   // If we are shutting down, then do not wait for new events.
1146   if (!*aEvent && aWait) {
1147     mEvents->GetEvent(aWait, aEvent, aProofOfLock);
1148   }
1149 }
1150 
1151 NS_IMETHODIMP
ProcessNextEvent(bool aMayWait,bool * aResult)1152 nsThread::ProcessNextEvent(bool aMayWait, bool* aResult)
1153 {
1154   LOG(("THRD(%p) ProcessNextEvent [%u %u]\n", this, aMayWait,
1155        mNestedEventLoopDepth));
1156 
1157   if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
1158     return NS_ERROR_NOT_SAME_THREAD;
1159   }
1160 
1161   // The toplevel event loop normally blocks waiting for the next event, but
1162   // if we're trying to shut this thread down, we must exit the event loop when
1163   // the event queue is empty.
1164   // This only applys to the toplevel event loop! Nested event loops (e.g.
1165   // during sync dispatch) are waiting for some state change and must be able
1166   // to block even if something has requested shutdown of the thread. Otherwise
1167   // we'll just busywait as we endlessly look for an event, fail to find one,
1168   // and repeat the nested event loop since its state change hasn't happened yet.
1169   bool reallyWait = aMayWait && (mNestedEventLoopDepth > 0 || !ShuttingDown());
1170 
1171   if (mIsMainThread == MAIN_THREAD) {
1172     DoMainThreadSpecificProcessing(reallyWait);
1173   }
1174 
1175   ++mNestedEventLoopDepth;
1176 
1177   // We only want to create an AutoNoJSAPI on threads that actually do DOM stuff
1178   // (including workers).  Those are exactly the threads that have an
1179   // mScriptObserver.
1180   Maybe<dom::AutoNoJSAPI> noJSAPI;
1181   bool callScriptObserver = !!mScriptObserver;
1182   if (callScriptObserver) {
1183     noJSAPI.emplace();
1184     mScriptObserver->BeforeProcessTask(reallyWait);
1185   }
1186 
1187   nsCOMPtr<nsIThreadObserver> obs = mObserver;
1188   if (obs) {
1189     obs->OnProcessNextEvent(this, reallyWait);
1190   }
1191 
1192   NOTIFY_EVENT_OBSERVERS(OnProcessNextEvent, (this, reallyWait));
1193 
1194 #ifdef MOZ_CANARY
1195   Canary canary;
1196 #endif
1197   nsresult rv = NS_OK;
1198 
1199   {
1200     // Scope for |event| to make sure that its destructor fires while
1201     // mNestedEventLoopDepth has been incremented, since that destructor can
1202     // also do work.
1203     nsCOMPtr<nsIRunnable> event;
1204     {
1205       MutexAutoLock lock(mLock);
1206       GetEvent(reallyWait, getter_AddRefs(event), lock);
1207     }
1208 
1209     *aResult = (event.get() != nullptr);
1210 
1211     if (event) {
1212       LOG(("THRD(%p) running [%p]\n", this, event.get()));
1213       if (MAIN_THREAD == mIsMainThread) {
1214         HangMonitor::NotifyActivity();
1215       }
1216       event->Run();
1217     } else if (aMayWait) {
1218       MOZ_ASSERT(ShuttingDown(),
1219                  "This should only happen when shutting down");
1220       rv = NS_ERROR_UNEXPECTED;
1221     }
1222   }
1223 
1224   NOTIFY_EVENT_OBSERVERS(AfterProcessNextEvent, (this, *aResult));
1225 
1226   if (obs) {
1227     obs->AfterProcessNextEvent(this, *aResult);
1228   }
1229 
1230   if (callScriptObserver) {
1231     if (mScriptObserver) {
1232       mScriptObserver->AfterProcessTask(mNestedEventLoopDepth);
1233     }
1234     noJSAPI.reset();
1235   }
1236 
1237   --mNestedEventLoopDepth;
1238 
1239   return rv;
1240 }
1241 
1242 //-----------------------------------------------------------------------------
1243 // nsISupportsPriority
1244 
1245 NS_IMETHODIMP
GetPriority(int32_t * aPriority)1246 nsThread::GetPriority(int32_t* aPriority)
1247 {
1248   *aPriority = mPriority;
1249   return NS_OK;
1250 }
1251 
1252 NS_IMETHODIMP
SetPriority(int32_t aPriority)1253 nsThread::SetPriority(int32_t aPriority)
1254 {
1255   if (NS_WARN_IF(!mThread)) {
1256     return NS_ERROR_NOT_INITIALIZED;
1257   }
1258 
1259   // NSPR defines the following four thread priorities:
1260   //   PR_PRIORITY_LOW
1261   //   PR_PRIORITY_NORMAL
1262   //   PR_PRIORITY_HIGH
1263   //   PR_PRIORITY_URGENT
1264   // We map the priority values defined on nsISupportsPriority to these values.
1265 
1266   mPriority = aPriority;
1267 
1268   PRThreadPriority pri;
1269   if (mPriority <= PRIORITY_HIGHEST) {
1270     pri = PR_PRIORITY_URGENT;
1271   } else if (mPriority < PRIORITY_NORMAL) {
1272     pri = PR_PRIORITY_HIGH;
1273   } else if (mPriority > PRIORITY_NORMAL) {
1274     pri = PR_PRIORITY_LOW;
1275   } else {
1276     pri = PR_PRIORITY_NORMAL;
1277   }
1278   // If chaos mode is active, retain the randomly chosen priority
1279   if (!ChaosMode::isActive(ChaosFeature::ThreadScheduling)) {
1280     PR_SetThreadPriority(mThread, pri);
1281   }
1282 
1283   return NS_OK;
1284 }
1285 
1286 NS_IMETHODIMP
AdjustPriority(int32_t aDelta)1287 nsThread::AdjustPriority(int32_t aDelta)
1288 {
1289   return SetPriority(mPriority + aDelta);
1290 }
1291 
1292 //-----------------------------------------------------------------------------
1293 // nsIThreadInternal
1294 
1295 NS_IMETHODIMP
GetObserver(nsIThreadObserver ** aObs)1296 nsThread::GetObserver(nsIThreadObserver** aObs)
1297 {
1298   MutexAutoLock lock(mLock);
1299   NS_IF_ADDREF(*aObs = mObserver);
1300   return NS_OK;
1301 }
1302 
1303 NS_IMETHODIMP
SetObserver(nsIThreadObserver * aObs)1304 nsThread::SetObserver(nsIThreadObserver* aObs)
1305 {
1306   if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
1307     return NS_ERROR_NOT_SAME_THREAD;
1308   }
1309 
1310   MutexAutoLock lock(mLock);
1311   mObserver = aObs;
1312   return NS_OK;
1313 }
1314 
1315 uint32_t
RecursionDepth() const1316 nsThread::RecursionDepth() const
1317 {
1318   MOZ_ASSERT(PR_GetCurrentThread() == mThread);
1319   return mNestedEventLoopDepth;
1320 }
1321 
1322 NS_IMETHODIMP
AddObserver(nsIThreadObserver * aObserver)1323 nsThread::AddObserver(nsIThreadObserver* aObserver)
1324 {
1325   if (NS_WARN_IF(!aObserver)) {
1326     return NS_ERROR_INVALID_ARG;
1327   }
1328   if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
1329     return NS_ERROR_NOT_SAME_THREAD;
1330   }
1331 
1332   NS_WARNING_ASSERTION(!mEventObservers.Contains(aObserver),
1333                        "Adding an observer twice!");
1334 
1335   if (!mEventObservers.AppendElement(WrapNotNull(aObserver))) {
1336     NS_WARNING("Out of memory!");
1337     return NS_ERROR_OUT_OF_MEMORY;
1338   }
1339 
1340   return NS_OK;
1341 }
1342 
1343 NS_IMETHODIMP
RemoveObserver(nsIThreadObserver * aObserver)1344 nsThread::RemoveObserver(nsIThreadObserver* aObserver)
1345 {
1346   if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
1347     return NS_ERROR_NOT_SAME_THREAD;
1348   }
1349 
1350   if (aObserver && !mEventObservers.RemoveElement(aObserver)) {
1351     NS_WARNING("Removing an observer that was never added!");
1352   }
1353 
1354   return NS_OK;
1355 }
1356 
1357 NS_IMETHODIMP
PushEventQueue(nsIEventTarget ** aResult)1358 nsThread::PushEventQueue(nsIEventTarget** aResult)
1359 {
1360   if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
1361     return NS_ERROR_NOT_SAME_THREAD;
1362   }
1363 
1364   NotNull<nsChainedEventQueue*> queue =
1365     WrapNotNull(new nsChainedEventQueue(mLock));
1366   queue->mEventTarget = new nsNestedEventTarget(WrapNotNull(this), queue);
1367 
1368   {
1369     MutexAutoLock lock(mLock);
1370     queue->mNext = mEvents;
1371     mEvents = queue;
1372   }
1373 
1374   NS_ADDREF(*aResult = queue->mEventTarget);
1375   return NS_OK;
1376 }
1377 
1378 NS_IMETHODIMP
PopEventQueue(nsIEventTarget * aInnermostTarget)1379 nsThread::PopEventQueue(nsIEventTarget* aInnermostTarget)
1380 {
1381   if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
1382     return NS_ERROR_NOT_SAME_THREAD;
1383   }
1384 
1385   if (NS_WARN_IF(!aInnermostTarget)) {
1386     return NS_ERROR_NULL_POINTER;
1387   }
1388 
1389   // Don't delete or release anything while holding the lock.
1390   nsAutoPtr<nsChainedEventQueue> queue;
1391   RefPtr<nsNestedEventTarget> target;
1392 
1393   {
1394     MutexAutoLock lock(mLock);
1395 
1396     // Make sure we're popping the innermost event target.
1397     if (NS_WARN_IF(mEvents->mEventTarget != aInnermostTarget)) {
1398       return NS_ERROR_UNEXPECTED;
1399     }
1400 
1401     MOZ_ASSERT(mEvents != &mEventsRoot);
1402 
1403     queue = mEvents;
1404     mEvents = WrapNotNull(mEvents->mNext);
1405 
1406     nsCOMPtr<nsIRunnable> event;
1407     while (queue->GetEvent(false, getter_AddRefs(event), lock)) {
1408       mEvents->PutEvent(event.forget(), lock);
1409     }
1410 
1411     // Don't let the event target post any more events.
1412     queue->mEventTarget.swap(target);
1413     target->mQueue = nullptr;
1414   }
1415 
1416   return NS_OK;
1417 }
1418 
1419 void
SetScriptObserver(mozilla::CycleCollectedJSContext * aScriptObserver)1420 nsThread::SetScriptObserver(mozilla::CycleCollectedJSContext* aScriptObserver)
1421 {
1422   if (!aScriptObserver) {
1423     mScriptObserver = nullptr;
1424     return;
1425   }
1426 
1427   MOZ_ASSERT(!mScriptObserver);
1428   mScriptObserver = aScriptObserver;
1429 }
1430 
1431 void
DoMainThreadSpecificProcessing(bool aReallyWait)1432 nsThread::DoMainThreadSpecificProcessing(bool aReallyWait)
1433 {
1434   MOZ_ASSERT(mIsMainThread == MAIN_THREAD);
1435 
1436   ipc::CancelCPOWs();
1437 
1438   if (aReallyWait) {
1439     HangMonitor::Suspend();
1440   }
1441 
1442   // Fire a memory pressure notification, if one is pending.
1443   if (!ShuttingDown()) {
1444     MemoryPressureState mpPending = NS_GetPendingMemoryPressure();
1445     if (mpPending != MemPressure_None) {
1446       nsCOMPtr<nsIObserverService> os = services::GetObserverService();
1447 
1448       // Use no-forward to prevent the notifications from being transferred to
1449       // the children of this process.
1450       NS_NAMED_LITERAL_STRING(lowMem, "low-memory-no-forward");
1451       NS_NAMED_LITERAL_STRING(lowMemOngoing, "low-memory-ongoing-no-forward");
1452 
1453       if (os) {
1454         os->NotifyObservers(nullptr, "memory-pressure",
1455                             mpPending == MemPressure_New ? lowMem.get() :
1456                             lowMemOngoing.get());
1457       } else {
1458         NS_WARNING("Can't get observer service!");
1459       }
1460     }
1461   }
1462 
1463 #ifdef MOZ_CRASHREPORTER
1464   if (!ShuttingDown()) {
1465     SaveMemoryReportNearOOM(ShouldSaveMemoryReport::kMaybeReport);
1466   }
1467 #endif
1468 }
1469 
1470 //-----------------------------------------------------------------------------
1471 
NS_IMPL_ISUPPORTS(nsThread::nsNestedEventTarget,nsIEventTarget)1472 NS_IMPL_ISUPPORTS(nsThread::nsNestedEventTarget, nsIEventTarget)
1473 
1474 NS_IMETHODIMP
1475 nsThread::nsNestedEventTarget::DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags)
1476 {
1477   nsCOMPtr<nsIRunnable> event(aEvent);
1478   return Dispatch(event.forget(), aFlags);
1479 }
1480 
1481 NS_IMETHODIMP
Dispatch(already_AddRefed<nsIRunnable> aEvent,uint32_t aFlags)1482 nsThread::nsNestedEventTarget::Dispatch(already_AddRefed<nsIRunnable> aEvent, uint32_t aFlags)
1483 {
1484   LOG(("THRD(%p) Dispatch [%p %x] to nested loop %p\n", mThread.get().get(),
1485        /*XXX aEvent*/ nullptr, aFlags, this));
1486 
1487   return mThread->DispatchInternal(Move(aEvent), aFlags, this);
1488 }
1489 
1490 NS_IMETHODIMP
DelayedDispatch(already_AddRefed<nsIRunnable>,uint32_t)1491 nsThread::nsNestedEventTarget::DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t)
1492 {
1493   return NS_ERROR_NOT_IMPLEMENTED;
1494 }
1495 
1496 NS_IMETHODIMP
IsOnCurrentThread(bool * aResult)1497 nsThread::nsNestedEventTarget::IsOnCurrentThread(bool* aResult)
1498 {
1499   return mThread->IsOnCurrentThread(aResult);
1500 }
1501