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 #include "base/platform_thread.h"
11 
12 // Chromium's logging can sometimes leak through...
13 #ifdef LOG
14 #  undef LOG
15 #endif
16 
17 #include "mozilla/ReentrantMonitor.h"
18 #include "nsMemoryPressure.h"
19 #include "nsThreadManager.h"
20 #include "nsIClassInfoImpl.h"
21 #include "nsCOMPtr.h"
22 #include "nsQueryObject.h"
23 #include "pratom.h"
24 #include "mozilla/BackgroundHangMonitor.h"
25 #include "mozilla/CycleCollectedJSContext.h"
26 #include "mozilla/DebugOnly.h"
27 #include "mozilla/Logging.h"
28 #include "nsIObserverService.h"
29 #include "mozilla/IOInterposer.h"
30 #include "mozilla/ipc/MessageChannel.h"
31 #include "mozilla/ipc/BackgroundChild.h"
32 #include "mozilla/Preferences.h"
33 #include "mozilla/ProfilerRunnable.h"
34 #include "mozilla/SchedulerGroup.h"
35 #include "mozilla/Services.h"
36 #include "mozilla/SpinEventLoopUntil.h"
37 #include "mozilla/StaticLocalPtr.h"
38 #include "mozilla/StaticPrefs_threads.h"
39 #include "mozilla/TaskController.h"
40 #include "nsXPCOMPrivate.h"
41 #include "mozilla/ChaosMode.h"
42 #include "mozilla/Telemetry.h"
43 #include "mozilla/TimeStamp.h"
44 #include "mozilla/Unused.h"
45 #include "mozilla/dom/DocGroup.h"
46 #include "mozilla/dom/ScriptSettings.h"
47 #include "nsThreadSyncDispatch.h"
48 #include "nsServiceManagerUtils.h"
49 #include "GeckoProfiler.h"
50 #include "InputEventStatistics.h"
51 #include "ThreadEventQueue.h"
52 #include "ThreadEventTarget.h"
53 #include "ThreadDelay.h"
54 
55 #include <limits>
56 
57 #ifdef XP_LINUX
58 #  ifdef __GLIBC__
59 #    include <gnu/libc-version.h>
60 #  endif
61 #  include <sys/mman.h>
62 #  include <sys/time.h>
63 #  include <sys/resource.h>
64 #  include <sched.h>
65 #  include <stdio.h>
66 #endif
67 
68 #ifdef XP_WIN
69 #  include "mozilla/DynamicallyLinkedFunctionPtr.h"
70 
71 #  include <winbase.h>
72 
73 using GetCurrentThreadStackLimitsFn = void(WINAPI*)(PULONG_PTR LowLimit,
74                                                     PULONG_PTR HighLimit);
75 #endif
76 
77 #define HAVE_UALARM                                                        \
78   _BSD_SOURCE ||                                                           \
79       (_XOPEN_SOURCE >= 500 || _XOPEN_SOURCE && _XOPEN_SOURCE_EXTENDED) && \
80           !(_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700)
81 
82 #if defined(XP_LINUX) && !defined(ANDROID) && defined(_GNU_SOURCE)
83 #  define HAVE_SCHED_SETAFFINITY
84 #endif
85 
86 #ifdef XP_MACOSX
87 #  include <mach/mach.h>
88 #  include <mach/thread_policy.h>
89 #endif
90 
91 #ifdef MOZ_CANARY
92 #  include <unistd.h>
93 #  include <execinfo.h>
94 #  include <signal.h>
95 #  include <fcntl.h>
96 #  include "nsXULAppAPI.h"
97 #endif
98 
99 using namespace mozilla;
100 
101 extern void InitThreadLocalVariables();
102 
103 static LazyLogModule sThreadLog("nsThread");
104 #ifdef LOG
105 #  undef LOG
106 #endif
107 #define LOG(args) MOZ_LOG(sThreadLog, mozilla::LogLevel::Debug, args)
108 
109 NS_DECL_CI_INTERFACE_GETTER(nsThread)
110 
111 Array<char, nsThread::kRunnableNameBufSize> nsThread::sMainThreadRunnableName;
112 
113 #ifdef EARLY_BETA_OR_EARLIER
114 const uint32_t kTelemetryWakeupCountLimit = 100;
115 #endif
116 
117 //-----------------------------------------------------------------------------
118 // Because we do not have our own nsIFactory, we have to implement nsIClassInfo
119 // somewhat manually.
120 
121 class nsThreadClassInfo : public nsIClassInfo {
122  public:
123   NS_DECL_ISUPPORTS_INHERITED  // no mRefCnt
124       NS_DECL_NSICLASSINFO
125 
126       nsThreadClassInfo() = default;
127 };
128 
NS_IMETHODIMP_(MozExternalRefCountType)129 NS_IMETHODIMP_(MozExternalRefCountType)
130 nsThreadClassInfo::AddRef() { return 2; }
NS_IMETHODIMP_(MozExternalRefCountType)131 NS_IMETHODIMP_(MozExternalRefCountType)
132 nsThreadClassInfo::Release() { return 1; }
NS_IMPL_QUERY_INTERFACE(nsThreadClassInfo,nsIClassInfo)133 NS_IMPL_QUERY_INTERFACE(nsThreadClassInfo, nsIClassInfo)
134 
135 NS_IMETHODIMP
136 nsThreadClassInfo::GetInterfaces(nsTArray<nsIID>& aArray) {
137   return NS_CI_INTERFACE_GETTER_NAME(nsThread)(aArray);
138 }
139 
140 NS_IMETHODIMP
GetScriptableHelper(nsIXPCScriptable ** aResult)141 nsThreadClassInfo::GetScriptableHelper(nsIXPCScriptable** aResult) {
142   *aResult = nullptr;
143   return NS_OK;
144 }
145 
146 NS_IMETHODIMP
GetContractID(nsACString & aResult)147 nsThreadClassInfo::GetContractID(nsACString& aResult) {
148   aResult.SetIsVoid(true);
149   return NS_OK;
150 }
151 
152 NS_IMETHODIMP
GetClassDescription(nsACString & aResult)153 nsThreadClassInfo::GetClassDescription(nsACString& aResult) {
154   aResult.SetIsVoid(true);
155   return NS_OK;
156 }
157 
158 NS_IMETHODIMP
GetClassID(nsCID ** aResult)159 nsThreadClassInfo::GetClassID(nsCID** aResult) {
160   *aResult = nullptr;
161   return NS_OK;
162 }
163 
164 NS_IMETHODIMP
GetFlags(uint32_t * aResult)165 nsThreadClassInfo::GetFlags(uint32_t* aResult) {
166   *aResult = THREADSAFE;
167   return NS_OK;
168 }
169 
170 NS_IMETHODIMP
GetClassIDNoAlloc(nsCID * aResult)171 nsThreadClassInfo::GetClassIDNoAlloc(nsCID* aResult) {
172   return NS_ERROR_NOT_AVAILABLE;
173 }
174 
175 //-----------------------------------------------------------------------------
176 
177 NS_IMPL_ADDREF(nsThread)
178 NS_IMPL_RELEASE(nsThread)
179 NS_INTERFACE_MAP_BEGIN(nsThread)
180   NS_INTERFACE_MAP_ENTRY(nsIThread)
181   NS_INTERFACE_MAP_ENTRY(nsIThreadInternal)
182   NS_INTERFACE_MAP_ENTRY(nsIEventTarget)
183   NS_INTERFACE_MAP_ENTRY(nsISerialEventTarget)
184   NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
185   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIDelayedRunnableObserver, mEventTarget)
186   NS_INTERFACE_MAP_ENTRY(nsIDirectTaskDispatcher)
187   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIThread)
188   if (aIID.Equals(NS_GET_IID(nsIClassInfo))) {
189     static nsThreadClassInfo sThreadClassInfo;
190     foundInterface = static_cast<nsIClassInfo*>(&sThreadClassInfo);
191   } else
192 NS_INTERFACE_MAP_END
193 NS_IMPL_CI_INTERFACE_GETTER(nsThread, nsIThread, nsIThreadInternal,
194                             nsIEventTarget, nsISerialEventTarget,
195                             nsISupportsPriority)
196 
197 //-----------------------------------------------------------------------------
198 
199 // This event is responsible for notifying nsThread::Shutdown that it is time
200 // to call PR_JoinThread. It implements nsICancelableRunnable so that it can
201 // run on a DOM Worker thread (where all events must implement
202 // nsICancelableRunnable.)
203 class nsThreadShutdownAckEvent : public CancelableRunnable {
204  public:
nsThreadShutdownAckEvent(NotNull<nsThreadShutdownContext * > aCtx)205   explicit nsThreadShutdownAckEvent(NotNull<nsThreadShutdownContext*> aCtx)
206       : CancelableRunnable("nsThreadShutdownAckEvent"),
207         mShutdownContext(aCtx) {}
Run()208   NS_IMETHOD Run() override {
209     mShutdownContext->mTerminatingThread->ShutdownComplete(mShutdownContext);
210     return NS_OK;
211   }
Cancel()212   nsresult Cancel() override { return Run(); }
213 
214  private:
215   virtual ~nsThreadShutdownAckEvent() = default;
216 
217   NotNull<RefPtr<nsThreadShutdownContext>> mShutdownContext;
218 };
219 
220 // This event is responsible for setting mShutdownContext
221 class nsThreadShutdownEvent : public Runnable {
222  public:
nsThreadShutdownEvent(NotNull<nsThread * > aThr,NotNull<nsThreadShutdownContext * > aCtx)223   nsThreadShutdownEvent(NotNull<nsThread*> aThr,
224                         NotNull<nsThreadShutdownContext*> aCtx)
225       : Runnable("nsThreadShutdownEvent"),
226         mThread(aThr),
227         mShutdownContext(aCtx) {}
Run()228   NS_IMETHOD Run() override {
229     // Creates a cycle between `mThread` and the shutdown context which will be
230     // broken when the thread exits.
231     mThread->mShutdownContext = mShutdownContext;
232     if (mThread->mEventTarget) {
233       mThread->mEventTarget->NotifyShutdown();
234     }
235     MessageLoop::current()->Quit();
236     return NS_OK;
237   }
238 
239  private:
240   NotNull<RefPtr<nsThread>> mThread;
241   NotNull<RefPtr<nsThreadShutdownContext>> mShutdownContext;
242 };
243 
244 //-----------------------------------------------------------------------------
245 
SetThreadAffinity(unsigned int cpu)246 static void SetThreadAffinity(unsigned int cpu) {
247 #ifdef HAVE_SCHED_SETAFFINITY
248   cpu_set_t cpus;
249   CPU_ZERO(&cpus);
250   CPU_SET(cpu, &cpus);
251   sched_setaffinity(0, sizeof(cpus), &cpus);
252   // Don't assert sched_setaffinity's return value because it intermittently (?)
253   // fails with EINVAL on Linux x64 try runs.
254 #elif defined(XP_MACOSX)
255   // OS X does not provide APIs to pin threads to specific processors, but you
256   // can tag threads as belonging to the same "affinity set" and the OS will try
257   // to run them on the same processor. To run threads on different processors,
258   // tag them as belonging to different affinity sets. Tag 0, the default, means
259   // "no affinity" so let's pretend each CPU has its own tag `cpu+1`.
260   thread_affinity_policy_data_t policy;
261   policy.affinity_tag = cpu + 1;
262   kern_return_t kr = thread_policy_set(
263       mach_thread_self(), THREAD_AFFINITY_POLICY, &policy.affinity_tag, 1);
264   // Setting the thread affinity is not supported on ARM.
265   MOZ_ALWAYS_TRUE(kr == KERN_SUCCESS || kr == KERN_NOT_SUPPORTED);
266 #elif defined(XP_WIN)
267   MOZ_ALWAYS_TRUE(SetThreadIdealProcessor(GetCurrentThread(), cpu) !=
268                   (DWORD)-1);
269 #endif
270 }
271 
SetupCurrentThreadForChaosMode()272 static void SetupCurrentThreadForChaosMode() {
273   if (!ChaosMode::isActive(ChaosFeature::ThreadScheduling)) {
274     return;
275   }
276 
277 #ifdef XP_LINUX
278   // PR_SetThreadPriority doesn't really work since priorities >
279   // PR_PRIORITY_NORMAL can't be set by non-root users. Instead we'll just use
280   // setpriority(2) to set random 'nice values'. In regular Linux this is only
281   // a dynamic adjustment so it still doesn't really do what we want, but tools
282   // like 'rr' can be more aggressive about honoring these values.
283   // Some of these calls may fail due to trying to lower the priority
284   // (e.g. something may have already called setpriority() for this thread).
285   // This makes it hard to have non-main threads with higher priority than the
286   // main thread, but that's hard to fix. Tools like rr can choose to honor the
287   // requested values anyway.
288   // Use just 4 priorities so there's a reasonable chance of any two threads
289   // having equal priority.
290   setpriority(PRIO_PROCESS, 0, ChaosMode::randomUint32LessThan(4));
291 #else
292   // We should set the affinity here but NSPR doesn't provide a way to expose
293   // it.
294   uint32_t priority = ChaosMode::randomUint32LessThan(PR_PRIORITY_LAST + 1);
295   PR_SetThreadPriority(PR_GetCurrentThread(), PRThreadPriority(priority));
296 #endif
297 
298   // Force half the threads to CPU 0 so they compete for CPU
299   if (ChaosMode::randomUint32LessThan(2)) {
300     SetThreadAffinity(0);
301   }
302 }
303 
304 namespace {
305 
306 struct ThreadInitData {
307   nsThread* thread;
308   nsCString name;
309 };
310 
311 }  // namespace
312 
ThreadListMutex()313 /* static */ mozilla::OffTheBooksMutex& nsThread::ThreadListMutex() {
314   static StaticLocalAutoPtr<OffTheBooksMutex> sMutex(
315       new OffTheBooksMutex("nsThread::ThreadListMutex"));
316   return *sMutex;
317 }
318 
ThreadList()319 /* static */ LinkedList<nsThread>& nsThread::ThreadList() {
320   static StaticLocalAutoPtr<LinkedList<nsThread>> sList(
321       new LinkedList<nsThread>());
322   return *sList;
323 }
324 
325 /* static */
ClearThreadList()326 void nsThread::ClearThreadList() {
327   OffTheBooksMutexAutoLock mal(ThreadListMutex());
328   while (ThreadList().popFirst()) {
329   }
330 }
331 
332 /* static */
Enumerate()333 nsThreadEnumerator nsThread::Enumerate() { return {}; }
334 
AddToThreadList()335 void nsThread::AddToThreadList() {
336   OffTheBooksMutexAutoLock mal(ThreadListMutex());
337   MOZ_ASSERT(!isInList());
338 
339   ThreadList().insertBack(this);
340 }
341 
MaybeRemoveFromThreadList()342 void nsThread::MaybeRemoveFromThreadList() {
343   OffTheBooksMutexAutoLock mal(ThreadListMutex());
344   if (isInList()) {
345     removeFrom(ThreadList());
346   }
347 }
348 
349 /*static*/
ThreadFunc(void * aArg)350 void nsThread::ThreadFunc(void* aArg) {
351   using mozilla::ipc::BackgroundChild;
352 
353   UniquePtr<ThreadInitData> initData(static_cast<ThreadInitData*>(aArg));
354   nsThread* self = initData->thread;  // strong reference
355 
356   MOZ_ASSERT(self->mEventTarget);
357   MOZ_ASSERT(self->mEvents);
358 
359   // Note: see the comment in nsThread::Init, where we set these same values.
360   DebugOnly<PRThread*> prev = self->mThread.exchange(PR_GetCurrentThread());
361   MOZ_ASSERT(!prev || prev == PR_GetCurrentThread());
362   self->mEventTarget->SetCurrentThread(self->mThread);
363   SetupCurrentThreadForChaosMode();
364 
365   if (!initData->name.IsEmpty()) {
366     NS_SetCurrentThreadName(initData->name.BeginReading());
367   }
368 
369   self->InitCommon();
370 
371   // Inform the ThreadManager
372   nsThreadManager::get().RegisterCurrentThread(*self);
373 
374   mozilla::IOInterposer::RegisterCurrentThread();
375 
376   // This must come after the call to nsThreadManager::RegisterCurrentThread(),
377   // because that call is needed to properly set up this thread as an nsThread,
378   // which profiler_register_thread() requires. See bug 1347007.
379   const bool registerWithProfiler = !initData->name.IsEmpty();
380   if (registerWithProfiler) {
381     PROFILER_REGISTER_THREAD(initData->name.BeginReading());
382   }
383 
384   {
385     // Scope for MessageLoop.
386     MessageLoop loop(MessageLoop::TYPE_MOZILLA_NONMAINTHREAD, self);
387 
388     // Now, process incoming events...
389     loop.Run();
390 
391     BackgroundChild::CloseForCurrentThread();
392 
393     // NB: The main thread does not shut down here!  It shuts down via
394     // nsThreadManager::Shutdown.
395 
396     // Do NS_ProcessPendingEvents but with special handling to set
397     // mEventsAreDoomed atomically with the removal of the last event. The key
398     // invariant here is that we will never permit PutEvent to succeed if the
399     // event would be left in the queue after our final call to
400     // NS_ProcessPendingEvents. We also have to keep processing events as long
401     // as we have outstanding mRequestedShutdownContexts.
402     while (true) {
403       // Check and see if we're waiting on any threads.
404       self->WaitForAllAsynchronousShutdowns();
405 
406       if (self->mEvents->ShutdownIfNoPendingEvents()) {
407         break;
408       }
409       NS_ProcessPendingEvents(self);
410     }
411   }
412 
413   mozilla::IOInterposer::UnregisterCurrentThread();
414 
415   // Inform the threadmanager that this thread is going away
416   nsThreadManager::get().UnregisterCurrentThread(*self);
417 
418   // The thread should only unregister itself if it was registered above.
419   if (registerWithProfiler) {
420     PROFILER_UNREGISTER_THREAD();
421   }
422 
423   NotNull<RefPtr<nsThreadShutdownContext>> context =
424       WrapNotNull(self->mShutdownContext);
425   self->mShutdownContext = nullptr;
426   MOZ_ASSERT(context->mTerminatingThread == self);
427 
428   // Take the joining thread from our shutdown context. This may have been
429   // cleared by the joining thread if it decided to cancel waiting on us, in
430   // which case we won't notify our caller, and leak.
431   RefPtr<nsThread> joiningThread;
432   {
433     auto lock = context->mJoiningThread.Lock();
434     joiningThread = lock->forget();
435   }
436   if (joiningThread) {
437     // Dispatch shutdown ACK
438     nsCOMPtr<nsIRunnable> event = new nsThreadShutdownAckEvent(context);
439     nsresult dispatch_ack_rv =
440         joiningThread->Dispatch(event, NS_DISPATCH_NORMAL);
441 
442     // We do not expect this to ever happen, but If we cannot dispatch
443     // the ack event, someone probably blocks waiting on us and will
444     // crash with a hang later anyways. The best we can do is to tell
445     // the world what happened right here.
446     MOZ_RELEASE_ASSERT(NS_SUCCEEDED(dispatch_ack_rv));
447   } else {
448     NS_WARNING(
449         "nsThread exiting after StopWaitingAndLeakThread was called, thread "
450         "resources will be leaked!");
451   }
452 
453   // Release any observer of the thread here.
454   self->SetObserver(nullptr);
455 
456   // The PRThread will be deleted in PR_JoinThread(), so clear references.
457   self->mThread = nullptr;
458   self->mEventTarget->ClearCurrentThread();
459   NS_RELEASE(self);
460 }
461 
InitCommon()462 void nsThread::InitCommon() {
463   mThreadId = uint32_t(PlatformThread::CurrentId());
464 
465   {
466 #if defined(XP_LINUX)
467     pthread_attr_t attr;
468     pthread_attr_init(&attr);
469     pthread_getattr_np(pthread_self(), &attr);
470 
471     size_t stackSize;
472     pthread_attr_getstack(&attr, &mStackBase, &stackSize);
473 
474     // Glibc prior to 2.27 reports the stack size and base including the guard
475     // region, so we need to compensate for it to get accurate accounting.
476     // Also, this behavior difference isn't guarded by a versioned symbol, so we
477     // actually need to check the runtime glibc version, not the version we were
478     // compiled against.
479     static bool sAdjustForGuardSize = ({
480 #  ifdef __GLIBC__
481       unsigned major, minor;
482       sscanf(gnu_get_libc_version(), "%u.%u", &major, &minor) < 2 ||
483           major < 2 || (major == 2 && minor < 27);
484 #  else
485       false;
486 #  endif
487     });
488     if (sAdjustForGuardSize) {
489       size_t guardSize;
490       pthread_attr_getguardsize(&attr, &guardSize);
491 
492       // Note: This assumes that the stack grows down, as is the case on all of
493       // our tier 1 platforms. On platforms where the stack grows up, the
494       // mStackBase adjustment is unnecessary, but doesn't cause any harm other
495       // than under-counting stack memory usage by one page.
496       mStackBase = reinterpret_cast<char*>(mStackBase) + guardSize;
497       stackSize -= guardSize;
498     }
499 
500     mStackSize = stackSize;
501 
502     // This is a bit of a hack.
503     //
504     // We really do want the NOHUGEPAGE flag on our thread stacks, since we
505     // don't expect any of them to need anywhere near 2MB of space. But setting
506     // it here is too late to have an effect, since the first stack page has
507     // already been faulted in existence, and NSPR doesn't give us a way to set
508     // it beforehand.
509     //
510     // What this does get us, however, is a different set of VM flags on our
511     // thread stacks compared to normal heap memory. Which makes the Linux
512     // kernel report them as separate regions, even when they are adjacent to
513     // heap memory. This allows us to accurately track the actual memory
514     // consumption of our allocated stacks.
515     madvise(mStackBase, stackSize, MADV_NOHUGEPAGE);
516 
517     pthread_attr_destroy(&attr);
518 #elif defined(XP_WIN)
519     static const StaticDynamicallyLinkedFunctionPtr<
520         GetCurrentThreadStackLimitsFn>
521         sGetStackLimits(L"kernel32.dll", "GetCurrentThreadStackLimits");
522 
523     if (sGetStackLimits) {
524       ULONG_PTR stackBottom, stackTop;
525       sGetStackLimits(&stackBottom, &stackTop);
526       mStackBase = reinterpret_cast<void*>(stackBottom);
527       mStackSize = stackTop - stackBottom;
528     }
529 #endif
530   }
531 
532   InitThreadLocalVariables();
533   AddToThreadList();
534 }
535 
536 //-----------------------------------------------------------------------------
537 
538 #ifdef MOZ_CANARY
539 int sCanaryOutputFD = -1;
540 #endif
541 
nsThread(NotNull<SynchronizedEventQueue * > aQueue,MainThreadFlag aMainThread,uint32_t aStackSize)542 nsThread::nsThread(NotNull<SynchronizedEventQueue*> aQueue,
543                    MainThreadFlag aMainThread, uint32_t aStackSize)
544     : mEvents(aQueue.get()),
545       mEventTarget(
546           new ThreadEventTarget(mEvents.get(), aMainThread == MAIN_THREAD)),
547       mOutstandingShutdownContexts(0),
548       mShutdownContext(nullptr),
549       mScriptObserver(nullptr),
550       mThreadName("<uninitialized>"),
551       mStackSize(aStackSize),
552       mNestedEventLoopDepth(0),
553       mShutdownRequired(false),
554       mPriority(PRIORITY_NORMAL),
555       mIsMainThread(aMainThread == MAIN_THREAD),
556       mUseHangMonitor(aMainThread == MAIN_THREAD),
557       mIsAPoolThreadFree(nullptr),
558       mCanInvokeJS(false),
559 #ifdef EARLY_BETA_OR_EARLIER
560       mLastWakeupCheckTime(TimeStamp::Now()),
561 #endif
562       mPerformanceCounterState(mNestedEventLoopDepth, mIsMainThread) {
563   if (mIsMainThread) {
564     mozilla::TaskController::Get()->SetPerformanceCounterState(
565         &mPerformanceCounterState);
566   }
567 }
568 
nsThread()569 nsThread::nsThread()
570     : mEvents(nullptr),
571       mEventTarget(nullptr),
572       mOutstandingShutdownContexts(0),
573       mShutdownContext(nullptr),
574       mScriptObserver(nullptr),
575       mThreadName("<uninitialized>"),
576       mStackSize(0),
577       mNestedEventLoopDepth(0),
578       mShutdownRequired(false),
579       mPriority(PRIORITY_NORMAL),
580       mIsMainThread(false),
581       mUseHangMonitor(false),
582       mCanInvokeJS(false),
583 #ifdef EARLY_BETA_OR_EARLIER
584       mLastWakeupCheckTime(TimeStamp::Now()),
585 #endif
586       mPerformanceCounterState(mNestedEventLoopDepth, mIsMainThread) {
587   MOZ_ASSERT(!NS_IsMainThread());
588 }
589 
~nsThread()590 nsThread::~nsThread() {
591   NS_ASSERTION(mOutstandingShutdownContexts == 0,
592                "shouldn't be waiting on other threads to shutdown");
593 
594   MaybeRemoveFromThreadList();
595 }
596 
Init(const nsACString & aName)597 nsresult nsThread::Init(const nsACString& aName) {
598   MOZ_ASSERT(mEvents);
599   MOZ_ASSERT(mEventTarget);
600   MOZ_ASSERT(!mThread);
601 
602   NS_ADDREF_THIS();
603 
604   SetThreadNameInternal(aName);
605 
606   mShutdownRequired = true;
607 
608   UniquePtr<ThreadInitData> initData(
609       new ThreadInitData{this, nsCString(aName)});
610 
611   PRThread* thread = nullptr;
612   // ThreadFunc is responsible for setting mThread
613   if (!(thread = PR_CreateThread(PR_USER_THREAD, ThreadFunc, initData.get(),
614                                  PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
615                                  PR_JOINABLE_THREAD, mStackSize))) {
616     NS_RELEASE_THIS();
617     return NS_ERROR_OUT_OF_MEMORY;
618   }
619 
620   // The created thread now owns initData, so release our ownership of it.
621   Unused << initData.release();
622 
623   // Note: we set these both here and inside ThreadFunc, to what should be
624   // the same value. This is because calls within ThreadFunc need these values
625   // to be set, and our callers need these values to be set.
626   DebugOnly<PRThread*> prev = mThread.exchange(thread);
627   MOZ_ASSERT(!prev || prev == thread);
628 
629   mEventTarget->SetCurrentThread(thread);
630   return NS_OK;
631 }
632 
InitCurrentThread()633 nsresult nsThread::InitCurrentThread() {
634   mThread = PR_GetCurrentThread();
635   SetupCurrentThreadForChaosMode();
636   InitCommon();
637 
638   nsThreadManager::get().RegisterCurrentThread(*this);
639   return NS_OK;
640 }
641 
GetThreadName(nsACString & aNameBuffer)642 void nsThread::GetThreadName(nsACString& aNameBuffer) {
643   auto lock = mThreadName.Lock();
644   aNameBuffer = lock.ref();
645 }
646 
SetThreadNameInternal(const nsACString & aName)647 void nsThread::SetThreadNameInternal(const nsACString& aName) {
648   auto lock = mThreadName.Lock();
649   lock->Assign(aName);
650 }
651 
652 //-----------------------------------------------------------------------------
653 // nsIEventTarget
654 
655 NS_IMETHODIMP
DispatchFromScript(nsIRunnable * aEvent,uint32_t aFlags)656 nsThread::DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags) {
657   MOZ_ASSERT(mEventTarget);
658   NS_ENSURE_TRUE(mEventTarget, NS_ERROR_NOT_IMPLEMENTED);
659 
660   nsCOMPtr<nsIRunnable> event(aEvent);
661   return mEventTarget->Dispatch(event.forget(), aFlags);
662 }
663 
664 NS_IMETHODIMP
Dispatch(already_AddRefed<nsIRunnable> aEvent,uint32_t aFlags)665 nsThread::Dispatch(already_AddRefed<nsIRunnable> aEvent, uint32_t aFlags) {
666   MOZ_ASSERT(mEventTarget);
667   NS_ENSURE_TRUE(mEventTarget, NS_ERROR_NOT_IMPLEMENTED);
668 
669   LOG(("THRD(%p) Dispatch [%p %x]\n", this, /* XXX aEvent */ nullptr, aFlags));
670 
671   return mEventTarget->Dispatch(std::move(aEvent), aFlags);
672 }
673 
674 NS_IMETHODIMP
DelayedDispatch(already_AddRefed<nsIRunnable> aEvent,uint32_t aDelayMs)675 nsThread::DelayedDispatch(already_AddRefed<nsIRunnable> aEvent,
676                           uint32_t aDelayMs) {
677   MOZ_ASSERT(mEventTarget);
678   NS_ENSURE_TRUE(mEventTarget, NS_ERROR_NOT_IMPLEMENTED);
679 
680   return mEventTarget->DelayedDispatch(std::move(aEvent), aDelayMs);
681 }
682 
683 NS_IMETHODIMP
GetRunningEventDelay(TimeDuration * aDelay,TimeStamp * aStart)684 nsThread::GetRunningEventDelay(TimeDuration* aDelay, TimeStamp* aStart) {
685   if (mIsAPoolThreadFree && *mIsAPoolThreadFree) {
686     // if there are unstarted threads in the pool, a new event to the
687     // pool would not be delayed at all (beyond thread start time)
688     *aDelay = TimeDuration();
689     *aStart = TimeStamp();
690   } else {
691     *aDelay = mLastEventDelay;
692     *aStart = mLastEventStart;
693   }
694   return NS_OK;
695 }
696 
697 NS_IMETHODIMP
SetRunningEventDelay(TimeDuration aDelay,TimeStamp aStart)698 nsThread::SetRunningEventDelay(TimeDuration aDelay, TimeStamp aStart) {
699   mLastEventDelay = aDelay;
700   mLastEventStart = aStart;
701   return NS_OK;
702 }
703 
704 NS_IMETHODIMP
IsOnCurrentThread(bool * aResult)705 nsThread::IsOnCurrentThread(bool* aResult) {
706   if (mEventTarget) {
707     return mEventTarget->IsOnCurrentThread(aResult);
708   }
709   *aResult = PR_GetCurrentThread() == mThread;
710   return NS_OK;
711 }
712 
NS_IMETHODIMP_(bool)713 NS_IMETHODIMP_(bool)
714 nsThread::IsOnCurrentThreadInfallible() {
715   // This method is only going to be called if `mThread` is null, which
716   // only happens when the thread has exited the event loop.  Therefore, when
717   // we are called, we can never be on this thread.
718   return false;
719 }
720 
721 //-----------------------------------------------------------------------------
722 // nsIThread
723 
724 NS_IMETHODIMP
GetPRThread(PRThread ** aResult)725 nsThread::GetPRThread(PRThread** aResult) {
726   PRThread* thread = mThread;  // atomic load
727   *aResult = thread;
728   return thread ? NS_OK : NS_ERROR_NOT_AVAILABLE;
729 }
730 
731 NS_IMETHODIMP
GetCanInvokeJS(bool * aResult)732 nsThread::GetCanInvokeJS(bool* aResult) {
733   *aResult = mCanInvokeJS;
734   return NS_OK;
735 }
736 
737 NS_IMETHODIMP
SetCanInvokeJS(bool aCanInvokeJS)738 nsThread::SetCanInvokeJS(bool aCanInvokeJS) {
739   mCanInvokeJS = aCanInvokeJS;
740   return NS_OK;
741 }
742 
743 NS_IMETHODIMP
GetLastLongTaskEnd(TimeStamp * _retval)744 nsThread::GetLastLongTaskEnd(TimeStamp* _retval) {
745   *_retval = mPerformanceCounterState.LastLongTaskEnd();
746   return NS_OK;
747 }
748 
749 NS_IMETHODIMP
GetLastLongNonIdleTaskEnd(TimeStamp * _retval)750 nsThread::GetLastLongNonIdleTaskEnd(TimeStamp* _retval) {
751   *_retval = mPerformanceCounterState.LastLongNonIdleTaskEnd();
752   return NS_OK;
753 }
754 
755 NS_IMETHODIMP
SetNameForWakeupTelemetry(const nsACString & aName)756 nsThread::SetNameForWakeupTelemetry(const nsACString& aName) {
757 #ifdef EARLY_BETA_OR_EARLIER
758   mNameForWakeupTelemetry = aName;
759 #endif
760   return NS_OK;
761 }
762 
763 NS_IMETHODIMP
AsyncShutdown()764 nsThread::AsyncShutdown() {
765   LOG(("THRD(%p) async shutdown\n", this));
766 
767   nsCOMPtr<nsIThreadShutdown> shutdown;
768   BeginShutdown(getter_AddRefs(shutdown));
769   return NS_OK;
770 }
771 
772 NS_IMETHODIMP
BeginShutdown(nsIThreadShutdown ** aShutdown)773 nsThread::BeginShutdown(nsIThreadShutdown** aShutdown) {
774   LOG(("THRD(%p) begin shutdown\n", this));
775 
776   MOZ_ASSERT(mEvents);
777   MOZ_ASSERT(mEventTarget);
778   MOZ_ASSERT(mThread != PR_GetCurrentThread());
779   if (NS_WARN_IF(mThread == PR_GetCurrentThread())) {
780     return NS_ERROR_UNEXPECTED;
781   }
782 
783   // Prevent multiple calls to this method.
784   if (!mShutdownRequired.compareExchange(true, false)) {
785     return NS_ERROR_UNEXPECTED;
786   }
787   MOZ_ASSERT(mThread);
788 
789   MaybeRemoveFromThreadList();
790 
791   RefPtr<nsThread> currentThread = nsThreadManager::get().GetCurrentThread();
792 
793   MOZ_DIAGNOSTIC_ASSERT(currentThread->EventQueue(),
794                         "Shutdown() may only be called from an XPCOM thread");
795 
796   // Allocate a shutdown context, and record that we're waiting for it.
797   RefPtr<nsThreadShutdownContext> context =
798       new nsThreadShutdownContext(WrapNotNull(this), currentThread);
799 
800   ++currentThread->mOutstandingShutdownContexts;
801   nsCOMPtr<nsIRunnable> clearOutstanding = NS_NewRunnableFunction(
802       "nsThread::ClearOutstandingShutdownContext",
803       [currentThread] { --currentThread->mOutstandingShutdownContexts; });
804   context->OnCompletion(clearOutstanding);
805 
806   // Set mShutdownContext and wake up the thread in case it is waiting for
807   // events to process.
808   nsCOMPtr<nsIRunnable> event =
809       new nsThreadShutdownEvent(WrapNotNull(this), WrapNotNull(context));
810   if (!mEvents->PutEvent(event.forget(), EventQueuePriority::Normal)) {
811     // We do not expect this to happen. Let's collect some diagnostics.
812     nsAutoCString threadName;
813     GetThreadName(threadName);
814     MOZ_CRASH_UNSAFE_PRINTF("Attempt to shutdown an already dead thread: %s",
815                             threadName.get());
816   }
817 
818   // We could still end up with other events being added after the shutdown
819   // task, but that's okay because we process pending events in ThreadFunc
820   // after setting mShutdownContext just before exiting.
821   context.forget(aShutdown);
822   return NS_OK;
823 }
824 
ShutdownComplete(NotNull<nsThreadShutdownContext * > aContext)825 void nsThread::ShutdownComplete(NotNull<nsThreadShutdownContext*> aContext) {
826   MOZ_ASSERT(mEvents);
827   MOZ_ASSERT(mEventTarget);
828   MOZ_ASSERT(aContext->mTerminatingThread == this);
829 
830   MaybeRemoveFromThreadList();
831 
832   // Now, it should be safe to join without fear of dead-locking.
833   PR_JoinThread(aContext->mTerminatingPRThread);
834   MOZ_ASSERT(!mThread);
835 
836 #ifdef DEBUG
837   nsCOMPtr<nsIThreadObserver> obs = mEvents->GetObserver();
838   MOZ_ASSERT(!obs, "Should have been cleared at shutdown!");
839 #endif
840 
841   aContext->MarkCompleted();
842 }
843 
WaitForAllAsynchronousShutdowns()844 void nsThread::WaitForAllAsynchronousShutdowns() {
845   // This is the motivating example for why SpinEventLoopUntil
846   // has the template parameter we are providing here.
847   SpinEventLoopUntil<ProcessFailureBehavior::IgnoreAndContinue>(
848       "nsThread::WaitForAllAsynchronousShutdowns"_ns,
849       [&]() { return mOutstandingShutdownContexts == 0; }, this);
850 }
851 
852 NS_IMETHODIMP
Shutdown()853 nsThread::Shutdown() {
854   LOG(("THRD(%p) sync shutdown\n", this));
855 
856   nsCOMPtr<nsIThreadShutdown> context;
857   nsresult rv = BeginShutdown(getter_AddRefs(context));
858   if (NS_FAILED(rv)) {
859     return NS_OK;  // The thread has already shut down.
860   }
861 
862   // If we are going to hang here we want to see the thread's name
863   nsAutoCString threadName;
864   GetThreadName(threadName);
865 
866   // Process events on the current thread until we receive a shutdown ACK.
867   // Allows waiting; ensure no locks are held that would deadlock us!
868   SpinEventLoopUntil("nsThread::Shutdown: "_ns + threadName,
869                      [&]() { return context->GetCompleted(); });
870 
871   return NS_OK;
872 }
873 
874 NS_IMETHODIMP
HasPendingEvents(bool * aResult)875 nsThread::HasPendingEvents(bool* aResult) {
876   if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
877     return NS_ERROR_NOT_SAME_THREAD;
878   }
879 
880   if (mIsMainThread && !mIsInLocalExecutionMode) {
881     *aResult = TaskController::Get()->HasMainThreadPendingTasks();
882   } else {
883     *aResult = mEvents->HasPendingEvent();
884   }
885   return NS_OK;
886 }
887 
888 NS_IMETHODIMP
HasPendingHighPriorityEvents(bool * aResult)889 nsThread::HasPendingHighPriorityEvents(bool* aResult) {
890   if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
891     return NS_ERROR_NOT_SAME_THREAD;
892   }
893 
894   // This function appears to never be called anymore.
895   *aResult = false;
896   return NS_OK;
897 }
898 
899 NS_IMETHODIMP
DispatchToQueue(already_AddRefed<nsIRunnable> aEvent,EventQueuePriority aQueue)900 nsThread::DispatchToQueue(already_AddRefed<nsIRunnable> aEvent,
901                           EventQueuePriority aQueue) {
902   nsCOMPtr<nsIRunnable> event = aEvent;
903 
904   if (NS_WARN_IF(!event)) {
905     return NS_ERROR_INVALID_ARG;
906   }
907 
908   if (!mEvents->PutEvent(event.forget(), aQueue)) {
909     NS_WARNING(
910         "An idle event was posted to a thread that will never run it "
911         "(rejected)");
912     return NS_ERROR_UNEXPECTED;
913   }
914 
915   return NS_OK;
916 }
917 
918 #ifdef MOZ_CANARY
919 void canary_alarm_handler(int signum);
920 
921 class Canary {
922   // XXX ToDo: support nested loops
923  public:
Canary()924   Canary() {
925     if (sCanaryOutputFD > 0 && EventLatencyIsImportant()) {
926       signal(SIGALRM, canary_alarm_handler);
927       ualarm(15000, 0);
928     }
929   }
930 
~Canary()931   ~Canary() {
932     if (sCanaryOutputFD != 0 && EventLatencyIsImportant()) {
933       ualarm(0, 0);
934     }
935   }
936 
EventLatencyIsImportant()937   static bool EventLatencyIsImportant() {
938     return NS_IsMainThread() && XRE_IsParentProcess();
939   }
940 };
941 
canary_alarm_handler(int signum)942 void canary_alarm_handler(int signum) {
943   void* array[30];
944   const char msg[29] = "event took too long to run:\n";
945   // use write to be safe in the signal handler
946   write(sCanaryOutputFD, msg, sizeof(msg));
947   backtrace_symbols_fd(array, backtrace(array, 30), sCanaryOutputFD);
948 }
949 
950 #endif
951 
952 #define NOTIFY_EVENT_OBSERVERS(observers_, func_, params_)                 \
953   do {                                                                     \
954     if (!observers_.IsEmpty()) {                                           \
955       for (nsCOMPtr<nsIThreadObserver> obs_ : observers_.ForwardRange()) { \
956         obs_->func_ params_;                                               \
957       }                                                                    \
958     }                                                                      \
959   } while (0)
960 
961 #ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
962 // static
GetLabeledRunnableName(nsIRunnable * aEvent,nsACString & aName,EventQueuePriority aPriority)963 bool nsThread::GetLabeledRunnableName(nsIRunnable* aEvent, nsACString& aName,
964                                       EventQueuePriority aPriority) {
965   bool labeled = false;
966   if (RefPtr<SchedulerGroup::Runnable> groupRunnable = do_QueryObject(aEvent)) {
967     labeled = true;
968     MOZ_ALWAYS_TRUE(NS_SUCCEEDED(groupRunnable->GetName(aName)));
969   } else if (nsCOMPtr<nsINamed> named = do_QueryInterface(aEvent)) {
970     MOZ_ALWAYS_TRUE(NS_SUCCEEDED(named->GetName(aName)));
971   } else {
972     aName.AssignLiteral("non-nsINamed runnable");
973   }
974   if (aName.IsEmpty()) {
975     aName.AssignLiteral("anonymous runnable");
976   }
977 
978   if (!labeled && aPriority > EventQueuePriority::InputHigh) {
979     aName.AppendLiteral("(unlabeled)");
980   }
981 
982   return labeled;
983 }
984 #endif
985 
GetPerformanceCounter(nsIRunnable * aEvent) const986 mozilla::PerformanceCounter* nsThread::GetPerformanceCounter(
987     nsIRunnable* aEvent) const {
988   return GetPerformanceCounterBase(aEvent);
989 }
990 
991 // static
GetPerformanceCounterBase(nsIRunnable * aEvent)992 mozilla::PerformanceCounter* nsThread::GetPerformanceCounterBase(
993     nsIRunnable* aEvent) {
994   RefPtr<SchedulerGroup::Runnable> docRunnable = do_QueryObject(aEvent);
995   if (docRunnable) {
996     return docRunnable->GetPerformanceCounter();
997   }
998   return nullptr;
999 }
1000 
ShallowSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const1001 size_t nsThread::ShallowSizeOfIncludingThis(
1002     mozilla::MallocSizeOf aMallocSizeOf) const {
1003   size_t n = 0;
1004   if (mShutdownContext) {
1005     n += aMallocSizeOf(mShutdownContext);
1006   }
1007   return aMallocSizeOf(this) + aMallocSizeOf(mThread) + n;
1008 }
1009 
SizeOfEventQueues(mozilla::MallocSizeOf aMallocSizeOf) const1010 size_t nsThread::SizeOfEventQueues(mozilla::MallocSizeOf aMallocSizeOf) const {
1011   size_t n = 0;
1012   if (mEventTarget) {
1013     // The size of mEvents is reported by mEventTarget.
1014     n += mEventTarget->SizeOfIncludingThis(aMallocSizeOf);
1015   }
1016   return n;
1017 }
1018 
SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const1019 size_t nsThread::SizeOfIncludingThis(
1020     mozilla::MallocSizeOf aMallocSizeOf) const {
1021   return ShallowSizeOfIncludingThis(aMallocSizeOf) +
1022          SizeOfEventQueues(aMallocSizeOf);
1023 }
1024 
1025 NS_IMETHODIMP
ProcessNextEvent(bool aMayWait,bool * aResult)1026 nsThread::ProcessNextEvent(bool aMayWait, bool* aResult) {
1027   MOZ_ASSERT(mEvents);
1028   NS_ENSURE_TRUE(mEvents, NS_ERROR_NOT_IMPLEMENTED);
1029 
1030   LOG(("THRD(%p) ProcessNextEvent [%u %u]\n", this, aMayWait,
1031        mNestedEventLoopDepth));
1032 
1033   if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
1034     return NS_ERROR_NOT_SAME_THREAD;
1035   }
1036 
1037   // The toplevel event loop normally blocks waiting for the next event, but
1038   // if we're trying to shut this thread down, we must exit the event loop
1039   // when the event queue is empty. This only applys to the toplevel event
1040   // loop! Nested event loops (e.g. during sync dispatch) are waiting for
1041   // some state change and must be able to block even if something has
1042   // requested shutdown of the thread. Otherwise we'll just busywait as we
1043   // endlessly look for an event, fail to find one, and repeat the nested
1044   // event loop since its state change hasn't happened yet.
1045   bool reallyWait = aMayWait && (mNestedEventLoopDepth > 0 || !ShuttingDown());
1046 
1047   if (mIsInLocalExecutionMode) {
1048     if (nsCOMPtr<nsIRunnable> event = mEvents->GetEvent(reallyWait)) {
1049       *aResult = true;
1050       LogRunnable::Run log(event);
1051       event->Run();
1052       event = nullptr;
1053     } else {
1054       *aResult = false;
1055     }
1056     return NS_OK;
1057   }
1058 
1059   Maybe<dom::AutoNoJSAPI> noJSAPI;
1060 
1061   if (mUseHangMonitor && reallyWait) {
1062     BackgroundHangMonitor().NotifyWait();
1063   }
1064 
1065   if (mIsMainThread) {
1066     DoMainThreadSpecificProcessing();
1067   }
1068 
1069   ++mNestedEventLoopDepth;
1070 
1071   // We only want to create an AutoNoJSAPI on threads that actually do DOM
1072   // stuff (including workers).  Those are exactly the threads that have an
1073   // mScriptObserver.
1074   bool callScriptObserver = !!mScriptObserver;
1075   if (callScriptObserver) {
1076     noJSAPI.emplace();
1077     mScriptObserver->BeforeProcessTask(reallyWait);
1078   }
1079 
1080 #ifdef EARLY_BETA_OR_EARLIER
1081   // Need to capture mayWaitForWakeup state before OnProcessNextEvent,
1082   // since on the main thread OnProcessNextEvent ends up waiting for the new
1083   // events.
1084   bool mayWaitForWakeup = reallyWait && !mEvents->HasPendingEvent();
1085 #endif
1086 
1087   nsCOMPtr<nsIThreadObserver> obs = mEvents->GetObserverOnThread();
1088   if (obs) {
1089     obs->OnProcessNextEvent(this, reallyWait);
1090   }
1091 
1092   NOTIFY_EVENT_OBSERVERS(EventQueue()->EventObservers(), OnProcessNextEvent,
1093                          (this, reallyWait));
1094 
1095 #ifdef MOZ_CANARY
1096   Canary canary;
1097 #endif
1098   nsresult rv = NS_OK;
1099 
1100   {
1101     // Scope for |event| to make sure that its destructor fires while
1102     // mNestedEventLoopDepth has been incremented, since that destructor can
1103     // also do work.
1104     nsCOMPtr<nsIRunnable> event;
1105     bool usingTaskController = mIsMainThread;
1106     if (usingTaskController) {
1107       event = TaskController::Get()->GetRunnableForMTTask(reallyWait);
1108     } else {
1109       event = mEvents->GetEvent(reallyWait, &mLastEventDelay);
1110     }
1111 
1112     *aResult = (event.get() != nullptr);
1113 
1114     if (event) {
1115 #ifdef EARLY_BETA_OR_EARLIER
1116       if (mayWaitForWakeup && mThread) {
1117         ++mWakeupCount;
1118         if (mWakeupCount == kTelemetryWakeupCountLimit) {
1119           TimeStamp now = TimeStamp::Now();
1120           double ms = (now - mLastWakeupCheckTime).ToMilliseconds();
1121           if (ms < 0) {
1122             ms = 0;
1123           }
1124           const char* name = !mNameForWakeupTelemetry.IsEmpty()
1125                                  ? mNameForWakeupTelemetry.get()
1126                                  : PR_GetThreadName(mThread);
1127           if (!name) {
1128             name = mIsMainThread ? "MainThread" : "(nameless thread)";
1129           }
1130           nsDependentCString key(name);
1131           Telemetry::Accumulate(Telemetry::THREAD_WAKEUP, key,
1132                                 static_cast<uint32_t>(ms));
1133           mLastWakeupCheckTime = now;
1134           mWakeupCount = 0;
1135         }
1136       }
1137 #endif
1138 
1139       LOG(("THRD(%p) running [%p]\n", this, event.get()));
1140 
1141       Maybe<LogRunnable::Run> log;
1142 
1143       if (!usingTaskController) {
1144         log.emplace(event);
1145       }
1146 
1147       // Delay event processing to encourage whoever dispatched this event
1148       // to run.
1149       DelayForChaosMode(ChaosFeature::TaskRunning, 1000);
1150 
1151       mozilla::TimeStamp now = mozilla::TimeStamp::Now();
1152 
1153       if (mUseHangMonitor) {
1154         BackgroundHangMonitor().NotifyActivity();
1155       }
1156 
1157       Maybe<PerformanceCounterState::Snapshot> snapshot;
1158       if (!usingTaskController) {
1159         snapshot.emplace(mPerformanceCounterState.RunnableWillRun(
1160             GetPerformanceCounter(event), now, false));
1161       }
1162 
1163       mLastEventStart = now;
1164 
1165       if (!usingTaskController) {
1166         AUTO_PROFILE_FOLLOWING_RUNNABLE(event);
1167         event->Run();
1168       } else {
1169         // Avoid generating "Runnable" profiler markers for the
1170         // "TaskController::ExecutePendingMTTasks" runnables created
1171         // by TaskController, which already adds "Runnable" markers
1172         // when executing tasks.
1173         event->Run();
1174       }
1175 
1176       if (usingTaskController) {
1177         *aResult = TaskController::Get()->MTTaskRunnableProcessedTask();
1178       } else {
1179         mPerformanceCounterState.RunnableDidRun(std::move(snapshot.ref()));
1180       }
1181 
1182       // To cover the event's destructor code inside the LogRunnable span.
1183       event = nullptr;
1184     } else {
1185       mLastEventDelay = TimeDuration();
1186       mLastEventStart = TimeStamp();
1187       if (aMayWait) {
1188         MOZ_ASSERT(ShuttingDown(),
1189                    "This should only happen when shutting down");
1190         rv = NS_ERROR_UNEXPECTED;
1191       }
1192     }
1193   }
1194 
1195   DrainDirectTasks();
1196 
1197   NOTIFY_EVENT_OBSERVERS(EventQueue()->EventObservers(), AfterProcessNextEvent,
1198                          (this, *aResult));
1199 
1200   if (obs) {
1201     obs->AfterProcessNextEvent(this, *aResult);
1202   }
1203 
1204   // In case some EventObserver dispatched some direct tasks; process them
1205   // now.
1206   DrainDirectTasks();
1207 
1208   if (callScriptObserver) {
1209     if (mScriptObserver) {
1210       mScriptObserver->AfterProcessTask(mNestedEventLoopDepth);
1211     }
1212     noJSAPI.reset();
1213   }
1214 
1215   --mNestedEventLoopDepth;
1216 
1217   return rv;
1218 }
1219 
1220 //-----------------------------------------------------------------------------
1221 // nsISupportsPriority
1222 
1223 NS_IMETHODIMP
GetPriority(int32_t * aPriority)1224 nsThread::GetPriority(int32_t* aPriority) {
1225   *aPriority = mPriority;
1226   return NS_OK;
1227 }
1228 
1229 NS_IMETHODIMP
SetPriority(int32_t aPriority)1230 nsThread::SetPriority(int32_t aPriority) {
1231   if (NS_WARN_IF(!mThread)) {
1232     return NS_ERROR_NOT_INITIALIZED;
1233   }
1234 
1235   // NSPR defines the following four thread priorities:
1236   //   PR_PRIORITY_LOW
1237   //   PR_PRIORITY_NORMAL
1238   //   PR_PRIORITY_HIGH
1239   //   PR_PRIORITY_URGENT
1240   // We map the priority values defined on nsISupportsPriority to these
1241   // values.
1242 
1243   mPriority = aPriority;
1244 
1245   PRThreadPriority pri;
1246   if (mPriority <= PRIORITY_HIGHEST) {
1247     pri = PR_PRIORITY_URGENT;
1248   } else if (mPriority < PRIORITY_NORMAL) {
1249     pri = PR_PRIORITY_HIGH;
1250   } else if (mPriority > PRIORITY_NORMAL) {
1251     pri = PR_PRIORITY_LOW;
1252   } else {
1253     pri = PR_PRIORITY_NORMAL;
1254   }
1255   // If chaos mode is active, retain the randomly chosen priority
1256   if (!ChaosMode::isActive(ChaosFeature::ThreadScheduling)) {
1257     PR_SetThreadPriority(mThread, pri);
1258   }
1259 
1260   return NS_OK;
1261 }
1262 
1263 NS_IMETHODIMP
AdjustPriority(int32_t aDelta)1264 nsThread::AdjustPriority(int32_t aDelta) {
1265   return SetPriority(mPriority + aDelta);
1266 }
1267 
1268 //-----------------------------------------------------------------------------
1269 // nsIThreadInternal
1270 
1271 NS_IMETHODIMP
GetObserver(nsIThreadObserver ** aObs)1272 nsThread::GetObserver(nsIThreadObserver** aObs) {
1273   MOZ_ASSERT(mEvents);
1274   NS_ENSURE_TRUE(mEvents, NS_ERROR_NOT_IMPLEMENTED);
1275 
1276   nsCOMPtr<nsIThreadObserver> obs = mEvents->GetObserver();
1277   obs.forget(aObs);
1278   return NS_OK;
1279 }
1280 
1281 NS_IMETHODIMP
SetObserver(nsIThreadObserver * aObs)1282 nsThread::SetObserver(nsIThreadObserver* aObs) {
1283   MOZ_ASSERT(mEvents);
1284   NS_ENSURE_TRUE(mEvents, NS_ERROR_NOT_IMPLEMENTED);
1285 
1286   if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
1287     return NS_ERROR_NOT_SAME_THREAD;
1288   }
1289 
1290   mEvents->SetObserver(aObs);
1291   return NS_OK;
1292 }
1293 
RecursionDepth() const1294 uint32_t nsThread::RecursionDepth() const {
1295   MOZ_ASSERT(PR_GetCurrentThread() == mThread);
1296   return mNestedEventLoopDepth;
1297 }
1298 
1299 NS_IMETHODIMP
AddObserver(nsIThreadObserver * aObserver)1300 nsThread::AddObserver(nsIThreadObserver* aObserver) {
1301   MOZ_ASSERT(mEvents);
1302   NS_ENSURE_TRUE(mEvents, NS_ERROR_NOT_IMPLEMENTED);
1303 
1304   if (NS_WARN_IF(!aObserver)) {
1305     return NS_ERROR_INVALID_ARG;
1306   }
1307   if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
1308     return NS_ERROR_NOT_SAME_THREAD;
1309   }
1310 
1311   EventQueue()->AddObserver(aObserver);
1312 
1313   return NS_OK;
1314 }
1315 
1316 NS_IMETHODIMP
RemoveObserver(nsIThreadObserver * aObserver)1317 nsThread::RemoveObserver(nsIThreadObserver* aObserver) {
1318   MOZ_ASSERT(mEvents);
1319   NS_ENSURE_TRUE(mEvents, NS_ERROR_NOT_IMPLEMENTED);
1320 
1321   if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
1322     return NS_ERROR_NOT_SAME_THREAD;
1323   }
1324 
1325   EventQueue()->RemoveObserver(aObserver);
1326 
1327   return NS_OK;
1328 }
1329 
SetScriptObserver(mozilla::CycleCollectedJSContext * aScriptObserver)1330 void nsThread::SetScriptObserver(
1331     mozilla::CycleCollectedJSContext* aScriptObserver) {
1332   if (!aScriptObserver) {
1333     mScriptObserver = nullptr;
1334     return;
1335   }
1336 
1337   MOZ_ASSERT(!mScriptObserver);
1338   mScriptObserver = aScriptObserver;
1339 }
1340 
1341 void NS_DispatchMemoryPressure();
1342 
DoMainThreadSpecificProcessing() const1343 void nsThread::DoMainThreadSpecificProcessing() const {
1344   MOZ_ASSERT(mIsMainThread);
1345 
1346   ipc::CancelCPOWs();
1347 
1348   // Fire a memory pressure notification, if one is pending.
1349   if (!ShuttingDown()) {
1350     NS_DispatchMemoryPressure();
1351   }
1352 }
1353 
1354 NS_IMETHODIMP
GetEventTarget(nsIEventTarget ** aEventTarget)1355 nsThread::GetEventTarget(nsIEventTarget** aEventTarget) {
1356   nsCOMPtr<nsIEventTarget> target = this;
1357   target.forget(aEventTarget);
1358   return NS_OK;
1359 }
1360 
1361 //-----------------------------------------------------------------------------
1362 // nsIDirectTaskDispatcher
1363 
1364 NS_IMETHODIMP
DispatchDirectTask(already_AddRefed<nsIRunnable> aEvent)1365 nsThread::DispatchDirectTask(already_AddRefed<nsIRunnable> aEvent) {
1366   if (!IsOnCurrentThread()) {
1367     return NS_ERROR_FAILURE;
1368   }
1369   mDirectTasks.AddTask(std::move(aEvent));
1370   return NS_OK;
1371 }
1372 
DrainDirectTasks()1373 NS_IMETHODIMP nsThread::DrainDirectTasks() {
1374   if (!IsOnCurrentThread()) {
1375     return NS_ERROR_FAILURE;
1376   }
1377   mDirectTasks.DrainTasks();
1378   return NS_OK;
1379 }
1380 
HaveDirectTasks(bool * aValue)1381 NS_IMETHODIMP nsThread::HaveDirectTasks(bool* aValue) {
1382   if (!IsOnCurrentThread()) {
1383     return NS_ERROR_FAILURE;
1384   }
1385 
1386   *aValue = mDirectTasks.HaveTasks();
1387   return NS_OK;
1388 }
1389 
EventTarget()1390 nsIEventTarget* nsThread::EventTarget() { return this; }
1391 
SerialEventTarget()1392 nsISerialEventTarget* nsThread::SerialEventTarget() { return this; }
1393 
OnDelayedRunnableCreated(mozilla::DelayedRunnable * aRunnable)1394 void nsThread::OnDelayedRunnableCreated(mozilla::DelayedRunnable* aRunnable) {
1395   mEventTarget->OnDelayedRunnableCreated(aRunnable);
1396 }
1397 
OnDelayedRunnableScheduled(mozilla::DelayedRunnable * aRunnable)1398 void nsThread::OnDelayedRunnableScheduled(mozilla::DelayedRunnable* aRunnable) {
1399   mEventTarget->OnDelayedRunnableScheduled(aRunnable);
1400 }
1401 
OnDelayedRunnableRan(mozilla::DelayedRunnable * aRunnable)1402 void nsThread::OnDelayedRunnableRan(mozilla::DelayedRunnable* aRunnable) {
1403   mEventTarget->OnDelayedRunnableRan(aRunnable);
1404 }
1405 
EnterLocalExecution()1406 nsLocalExecutionRecord nsThread::EnterLocalExecution() {
1407   MOZ_RELEASE_ASSERT(!mIsInLocalExecutionMode);
1408   MOZ_ASSERT(IsOnCurrentThread());
1409   MOZ_ASSERT(EventQueue());
1410   return nsLocalExecutionRecord(*EventQueue(), mIsInLocalExecutionMode);
1411 }
1412 
nsLocalExecutionGuard(nsLocalExecutionRecord && aLocalExecutionRecord)1413 nsLocalExecutionGuard::nsLocalExecutionGuard(
1414     nsLocalExecutionRecord&& aLocalExecutionRecord)
1415     : mEventQueueStack(aLocalExecutionRecord.mEventQueueStack),
1416       mLocalEventTarget(mEventQueueStack.PushEventQueue()),
1417       mLocalExecutionFlag(aLocalExecutionRecord.mLocalExecutionFlag) {
1418   MOZ_ASSERT(mLocalEventTarget);
1419   MOZ_ASSERT(!mLocalExecutionFlag);
1420   mLocalExecutionFlag = true;
1421 }
1422 
~nsLocalExecutionGuard()1423 nsLocalExecutionGuard::~nsLocalExecutionGuard() {
1424   MOZ_ASSERT(mLocalExecutionFlag);
1425   mLocalExecutionFlag = false;
1426   mEventQueueStack.PopEventQueue(mLocalEventTarget);
1427 }
1428 
NS_IMPL_ISUPPORTS(nsThreadShutdownContext,nsIThreadShutdown)1429 NS_IMPL_ISUPPORTS(nsThreadShutdownContext, nsIThreadShutdown)
1430 
1431 NS_IMETHODIMP
1432 nsThreadShutdownContext::OnCompletion(nsIRunnable* aEvent) {
1433   if (mCompleted) {
1434     aEvent->Run();
1435   } else {
1436     mCompletionCallbacks.AppendElement(aEvent);
1437   }
1438   return NS_OK;
1439 }
1440 
1441 NS_IMETHODIMP
GetCompleted(bool * aCompleted)1442 nsThreadShutdownContext::GetCompleted(bool* aCompleted) {
1443   *aCompleted = mCompleted;
1444   return NS_OK;
1445 }
1446 
1447 NS_IMETHODIMP
StopWaitingAndLeakThread()1448 nsThreadShutdownContext::StopWaitingAndLeakThread() {
1449   // Take the joining thread from `mJoiningThread` so that the terminating
1450   // thread won't try to dispatch nsThreadShutdownAckEvent to us anymore.
1451   RefPtr<nsThread> joiningThread;
1452   {
1453     auto lock = mJoiningThread.Lock();
1454     joiningThread = lock->forget();
1455   }
1456   if (!joiningThread) {
1457     // Shutdown is already being resolved, so there's nothing for us to do.
1458     return NS_ERROR_NOT_AVAILABLE;
1459   }
1460 
1461   MOZ_DIAGNOSTIC_ASSERT(joiningThread->IsOnCurrentThread());
1462   MarkCompleted();
1463   return NS_OK;
1464 }
1465 
MarkCompleted()1466 void nsThreadShutdownContext::MarkCompleted() {
1467   MOZ_ASSERT(!mCompleted);
1468   mCompleted = true;
1469   nsTArray<nsCOMPtr<nsIRunnable>> callbacks(std::move(mCompletionCallbacks));
1470   for (auto& callback : callbacks) {
1471     callback->Run();
1472   }
1473 }
1474 
1475 namespace mozilla {
RunnableWillRun(PerformanceCounter * aCounter,TimeStamp aNow,bool aIsIdleRunnable)1476 PerformanceCounterState::Snapshot PerformanceCounterState::RunnableWillRun(
1477     PerformanceCounter* aCounter, TimeStamp aNow, bool aIsIdleRunnable) {
1478   if (IsNestedRunnable()) {
1479     // Flush out any accumulated time that should be accounted to the
1480     // current runnable before we start running a nested runnable.
1481     MaybeReportAccumulatedTime(aNow);
1482   }
1483 
1484   Snapshot snapshot(mCurrentEventLoopDepth, mCurrentPerformanceCounter,
1485                     mCurrentRunnableIsIdleRunnable);
1486 
1487   mCurrentEventLoopDepth = mNestedEventLoopDepth;
1488   mCurrentPerformanceCounter = aCounter;
1489   mCurrentRunnableIsIdleRunnable = aIsIdleRunnable;
1490   mCurrentTimeSliceStart = aNow;
1491 
1492   return snapshot;
1493 }
1494 
RunnableDidRun(Snapshot && aSnapshot)1495 void PerformanceCounterState::RunnableDidRun(Snapshot&& aSnapshot) {
1496   // First thing: Restore our mCurrentEventLoopDepth so we can use
1497   // IsNestedRunnable().
1498   mCurrentEventLoopDepth = aSnapshot.mOldEventLoopDepth;
1499 
1500   // We may not need the current timestamp; don't bother computing it if we
1501   // don't.
1502   TimeStamp now;
1503   if (mCurrentPerformanceCounter || mIsMainThread || IsNestedRunnable()) {
1504     now = TimeStamp::Now();
1505   }
1506   if (mCurrentPerformanceCounter || mIsMainThread) {
1507     MaybeReportAccumulatedTime(now);
1508   }
1509 
1510   // And now restore the rest of our state.
1511   mCurrentPerformanceCounter = std::move(aSnapshot.mOldPerformanceCounter);
1512   mCurrentRunnableIsIdleRunnable = aSnapshot.mOldIsIdleRunnable;
1513   if (IsNestedRunnable()) {
1514     // Reset mCurrentTimeSliceStart to right now, so our parent runnable's
1515     // next slice can be properly accounted for.
1516     mCurrentTimeSliceStart = now;
1517   } else {
1518     // We are done at the outermost level; we are no longer in a timeslice.
1519     mCurrentTimeSliceStart = TimeStamp();
1520   }
1521 }
1522 
MaybeReportAccumulatedTime(TimeStamp aNow)1523 void PerformanceCounterState::MaybeReportAccumulatedTime(TimeStamp aNow) {
1524   MOZ_ASSERT(mCurrentTimeSliceStart,
1525              "How did we get here if we're not in a timeslice?");
1526 
1527   if (!mCurrentPerformanceCounter && !mIsMainThread) {
1528     // No one cares about this timeslice.
1529     return;
1530   }
1531 
1532   TimeDuration duration = aNow - mCurrentTimeSliceStart;
1533   if (mCurrentPerformanceCounter) {
1534     mCurrentPerformanceCounter->IncrementExecutionDuration(
1535         duration.ToMicroseconds());
1536   }
1537 
1538   // Long tasks only matter on the main thread.
1539   if (mIsMainThread && duration.ToMilliseconds() > LONGTASK_BUSY_WINDOW_MS) {
1540     // Idle events (gc...) don't *really* count here
1541     if (!mCurrentRunnableIsIdleRunnable) {
1542       mLastLongNonIdleTaskEnd = aNow;
1543     }
1544     mLastLongTaskEnd = aNow;
1545 
1546     if (profiler_thread_is_being_profiled_for_markers()) {
1547       struct LongTaskMarker {
1548         static constexpr Span<const char> MarkerTypeName() {
1549           return MakeStringSpan("MainThreadLongTask");
1550         }
1551         static void StreamJSONMarkerData(
1552             baseprofiler::SpliceableJSONWriter& aWriter) {
1553           aWriter.StringProperty("category", "LongTask");
1554         }
1555         static MarkerSchema MarkerTypeDisplay() {
1556           using MS = MarkerSchema;
1557           MS schema{MS::Location::MarkerChart, MS::Location::MarkerTable};
1558           schema.AddKeyLabelFormat("category", "Type", MS::Format::String);
1559           return schema;
1560         }
1561       };
1562 
1563       profiler_add_marker(mCurrentRunnableIsIdleRunnable
1564                               ? ProfilerString8View("LongIdleTask")
1565                               : ProfilerString8View("LongTask"),
1566                           geckoprofiler::category::OTHER,
1567                           MarkerTiming::Interval(mCurrentTimeSliceStart, aNow),
1568                           LongTaskMarker{});
1569     }
1570   }
1571 }
1572 
1573 }  // namespace mozilla
1574