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/. */
9 #include "base/message_loop.h"
10 #include "base/platform_thread.h"
12 // Chromium's logging can sometimes leak through...
17 #include "mozilla/ReentrantMonitor.h"
18 #include "nsMemoryPressure.h"
19 #include "nsThreadManager.h"
20 #include "nsIClassInfoImpl.h"
22 #include "nsQueryObject.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 "ThreadEventQueue.h"
51 #include "ThreadEventTarget.h"
52 #include "ThreadDelay.h"
58 # include <gnu/libc-version.h>
60 # include <sys/mman.h>
61 # include <sys/time.h>
62 # include <sys/resource.h>
68 # include "mozilla/DynamicallyLinkedFunctionPtr.h"
72 using GetCurrentThreadStackLimitsFn
= void(WINAPI
*)(PULONG_PTR LowLimit
,
73 PULONG_PTR HighLimit
);
78 (_XOPEN_SOURCE >= 500 || _XOPEN_SOURCE && _XOPEN_SOURCE_EXTENDED) && \
79 !(_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700)
81 #if defined(XP_LINUX) && !defined(ANDROID) && defined(_GNU_SOURCE)
82 # define HAVE_SCHED_SETAFFINITY
86 # include <mach/mach.h>
87 # include <mach/thread_policy.h>
93 # include <execinfo.h>
96 # include "nsXULAppAPI.h"
99 using namespace mozilla
;
101 extern void InitThreadLocalVariables();
103 static LazyLogModule
sThreadLog("nsThread");
107 #define LOG(args) MOZ_LOG(sThreadLog, mozilla::LogLevel::Debug, args)
109 NS_DECL_CI_INTERFACE_GETTER(nsThread
)
111 Array
<char, nsThread::kRunnableNameBufSize
> nsThread::sMainThreadRunnableName
;
113 #ifdef EARLY_BETA_OR_EARLIER
114 const uint32_t kTelemetryWakeupCountLimit
= 100;
117 //-----------------------------------------------------------------------------
118 // Because we do not have our own nsIFactory, we have to implement nsIClassInfo
119 // somewhat manually.
121 class nsThreadClassInfo
: public nsIClassInfo
{
123 NS_DECL_ISUPPORTS_INHERITED
// no mRefCnt
126 nsThreadClassInfo() = default;
129 NS_IMETHODIMP_(MozExternalRefCountType
)
130 nsThreadClassInfo::AddRef() { return 2; }
131 NS_IMETHODIMP_(MozExternalRefCountType
)
132 nsThreadClassInfo::Release() { return 1; }
133 NS_IMPL_QUERY_INTERFACE(nsThreadClassInfo
, nsIClassInfo
)
136 nsThreadClassInfo::GetInterfaces(nsTArray
<nsIID
>& aArray
) {
137 return NS_CI_INTERFACE_GETTER_NAME(nsThread
)(aArray
);
141 nsThreadClassInfo::GetScriptableHelper(nsIXPCScriptable
** aResult
) {
147 nsThreadClassInfo::GetContractID(nsACString
& aResult
) {
148 aResult
.SetIsVoid(true);
153 nsThreadClassInfo::GetClassDescription(nsACString
& aResult
) {
154 aResult
.SetIsVoid(true);
159 nsThreadClassInfo::GetClassID(nsCID
** aResult
) {
165 nsThreadClassInfo::GetFlags(uint32_t* aResult
) {
166 *aResult
= THREADSAFE
;
171 nsThreadClassInfo::GetClassIDNoAlloc(nsCID
* aResult
) {
172 return NS_ERROR_NOT_AVAILABLE
;
175 //-----------------------------------------------------------------------------
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(nsIDirectTaskDispatcher
)
186 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIThread
)
187 if (aIID
.Equals(NS_GET_IID(nsIClassInfo
))) {
188 static nsThreadClassInfo sThreadClassInfo
;
189 foundInterface
= static_cast<nsIClassInfo
*>(&sThreadClassInfo
);
192 NS_IMPL_CI_INTERFACE_GETTER(nsThread
, nsIThread
, nsIThreadInternal
,
193 nsIEventTarget
, nsISerialEventTarget
,
196 //-----------------------------------------------------------------------------
198 // This event is responsible for notifying nsThread::Shutdown that it is time
199 // to call PR_JoinThread. It implements nsICancelableRunnable so that it can
200 // run on a DOM Worker thread (where all events must implement
201 // nsICancelableRunnable.)
202 class nsThreadShutdownAckEvent
: public CancelableRunnable
{
204 explicit nsThreadShutdownAckEvent(NotNull
<nsThreadShutdownContext
*> aCtx
)
205 : CancelableRunnable("nsThreadShutdownAckEvent"),
206 mShutdownContext(aCtx
) {}
207 NS_IMETHOD
Run() override
{
208 mShutdownContext
->mTerminatingThread
->ShutdownComplete(mShutdownContext
);
211 nsresult
Cancel() override
{ return Run(); }
214 virtual ~nsThreadShutdownAckEvent() = default;
216 NotNull
<RefPtr
<nsThreadShutdownContext
>> mShutdownContext
;
219 // This event is responsible for setting mShutdownContext
220 class nsThreadShutdownEvent
: public Runnable
{
222 nsThreadShutdownEvent(NotNull
<nsThread
*> aThr
,
223 NotNull
<nsThreadShutdownContext
*> aCtx
)
224 : Runnable("nsThreadShutdownEvent"),
226 mShutdownContext(aCtx
) {}
227 NS_IMETHOD
Run() override
{
228 // Creates a cycle between `mThread` and the shutdown context which will be
229 // broken when the thread exits.
230 mThread
->mShutdownContext
= mShutdownContext
;
231 MessageLoop::current()->Quit();
232 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
233 // Let's leave a trace that we passed here in the thread's name.
234 nsAutoCString
threadName(PR_GetThreadName(PR_GetCurrentThread()));
235 threadName
.Append(",SHDRCV"_ns
);
236 NS_SetCurrentThreadName(threadName
.get());
242 NotNull
<RefPtr
<nsThread
>> mThread
;
243 NotNull
<RefPtr
<nsThreadShutdownContext
>> mShutdownContext
;
246 //-----------------------------------------------------------------------------
248 static void SetThreadAffinity(unsigned int cpu
) {
249 #ifdef HAVE_SCHED_SETAFFINITY
253 sched_setaffinity(0, sizeof(cpus
), &cpus
);
254 // Don't assert sched_setaffinity's return value because it intermittently (?)
255 // fails with EINVAL on Linux x64 try runs.
256 #elif defined(XP_MACOSX)
257 // OS X does not provide APIs to pin threads to specific processors, but you
258 // can tag threads as belonging to the same "affinity set" and the OS will try
259 // to run them on the same processor. To run threads on different processors,
260 // tag them as belonging to different affinity sets. Tag 0, the default, means
261 // "no affinity" so let's pretend each CPU has its own tag `cpu+1`.
262 thread_affinity_policy_data_t policy
;
263 policy
.affinity_tag
= cpu
+ 1;
264 kern_return_t kr
= thread_policy_set(
265 mach_thread_self(), THREAD_AFFINITY_POLICY
, &policy
.affinity_tag
, 1);
266 // Setting the thread affinity is not supported on ARM.
267 MOZ_ALWAYS_TRUE(kr
== KERN_SUCCESS
|| kr
== KERN_NOT_SUPPORTED
);
268 #elif defined(XP_WIN)
269 MOZ_ALWAYS_TRUE(SetThreadIdealProcessor(GetCurrentThread(), cpu
) !=
274 static void SetupCurrentThreadForChaosMode() {
275 if (!ChaosMode::isActive(ChaosFeature::ThreadScheduling
)) {
280 // PR_SetThreadPriority doesn't really work since priorities >
281 // PR_PRIORITY_NORMAL can't be set by non-root users. Instead we'll just use
282 // setpriority(2) to set random 'nice values'. In regular Linux this is only
283 // a dynamic adjustment so it still doesn't really do what we want, but tools
284 // like 'rr' can be more aggressive about honoring these values.
285 // Some of these calls may fail due to trying to lower the priority
286 // (e.g. something may have already called setpriority() for this thread).
287 // This makes it hard to have non-main threads with higher priority than the
288 // main thread, but that's hard to fix. Tools like rr can choose to honor the
289 // requested values anyway.
290 // Use just 4 priorities so there's a reasonable chance of any two threads
291 // having equal priority.
292 setpriority(PRIO_PROCESS
, 0, ChaosMode::randomUint32LessThan(4));
294 // We should set the affinity here but NSPR doesn't provide a way to expose
296 uint32_t priority
= ChaosMode::randomUint32LessThan(PR_PRIORITY_LAST
+ 1);
297 PR_SetThreadPriority(PR_GetCurrentThread(), PRThreadPriority(priority
));
300 // Force half the threads to CPU 0 so they compete for CPU
301 if (ChaosMode::randomUint32LessThan(2)) {
302 SetThreadAffinity(0);
308 struct ThreadInitData
{
309 RefPtr
<nsThread
> thread
;
315 void nsThread::MaybeRemoveFromThreadList() {
316 nsThreadManager
& tm
= nsThreadManager::get();
317 OffTheBooksMutexAutoLock
mal(tm
.ThreadListMutex());
319 removeFrom(tm
.ThreadList());
324 void nsThread::ThreadFunc(void* aArg
) {
325 using mozilla::ipc::BackgroundChild
;
327 UniquePtr
<ThreadInitData
> initData(static_cast<ThreadInitData
*>(aArg
));
328 RefPtr
<nsThread
>& self
= initData
->thread
;
330 MOZ_ASSERT(self
->mEventTarget
);
331 MOZ_ASSERT(self
->mEvents
);
333 // Note: see the comment in nsThread::Init, where we set these same values.
334 DebugOnly
<PRThread
*> prev
= self
->mThread
.exchange(PR_GetCurrentThread());
335 MOZ_ASSERT(!prev
|| prev
== PR_GetCurrentThread());
336 self
->mEventTarget
->SetCurrentThread(self
->mThread
);
337 SetupCurrentThreadForChaosMode();
339 if (!initData
->name
.IsEmpty()) {
340 NS_SetCurrentThreadName(initData
->name
.BeginReading());
345 // Inform the ThreadManager
346 nsThreadManager::get().RegisterCurrentThread(*self
);
348 mozilla::IOInterposer::RegisterCurrentThread();
350 // This must come after the call to nsThreadManager::RegisterCurrentThread(),
351 // because that call is needed to properly set up this thread as an nsThread,
352 // which profiler_register_thread() requires. See bug 1347007.
353 const bool registerWithProfiler
= !initData
->name
.IsEmpty();
354 if (registerWithProfiler
) {
355 PROFILER_REGISTER_THREAD(initData
->name
.BeginReading());
359 // Scope for MessageLoop.
361 #if defined(XP_WIN) || defined(XP_MACOSX)
362 self
->mIsUiThread
? MessageLoop::TYPE_MOZILLA_NONMAINUITHREAD
363 : MessageLoop::TYPE_MOZILLA_NONMAINTHREAD
,
365 MessageLoop::TYPE_MOZILLA_NONMAINTHREAD
,
369 // Now, process incoming events...
372 self
->mEvents
->RunShutdownTasks();
374 BackgroundChild::CloseForCurrentThread();
376 // NB: The main thread does not shut down here! It shuts down via
377 // nsThreadManager::Shutdown.
379 // Do NS_ProcessPendingEvents but with special handling to set
380 // mEventsAreDoomed atomically with the removal of the last event. The key
381 // invariant here is that we will never permit PutEvent to succeed if the
382 // event would be left in the queue after our final call to
383 // NS_ProcessPendingEvents. We also have to keep processing events as long
384 // as we have outstanding mRequestedShutdownContexts.
386 // Check and see if we're waiting on any threads.
387 self
->WaitForAllAsynchronousShutdowns();
389 if (self
->mEvents
->ShutdownIfNoPendingEvents()) {
392 NS_ProcessPendingEvents(self
);
396 mozilla::IOInterposer::UnregisterCurrentThread();
398 // Inform the threadmanager that this thread is going away
399 nsThreadManager::get().UnregisterCurrentThread(*self
);
401 // The thread should only unregister itself if it was registered above.
402 if (registerWithProfiler
) {
403 PROFILER_UNREGISTER_THREAD();
406 NotNull
<RefPtr
<nsThreadShutdownContext
>> context
=
407 WrapNotNull(self
->mShutdownContext
);
408 self
->mShutdownContext
= nullptr;
409 MOZ_ASSERT(context
->mTerminatingThread
== self
);
411 // Take the joining thread from our shutdown context. This may have been
412 // cleared by the joining thread if it decided to cancel waiting on us, in
413 // which case we won't notify our caller, and leak.
414 RefPtr
<nsThread
> joiningThread
;
416 MutexAutoLock
lock(context
->mJoiningThreadMutex
);
417 joiningThread
= context
->mJoiningThread
.forget();
418 MOZ_RELEASE_ASSERT(joiningThread
|| context
->mThreadLeaked
);
421 // Dispatch shutdown ACK
422 nsCOMPtr
<nsIRunnable
> event
= new nsThreadShutdownAckEvent(context
);
423 nsresult dispatch_ack_rv
=
424 joiningThread
->Dispatch(event
, NS_DISPATCH_NORMAL
);
426 // We do not expect this to ever happen, but If we cannot dispatch
427 // the ack event, someone probably blocks waiting on us and will
428 // crash with a hang later anyways. The best we can do is to tell
429 // the world what happened right here.
430 MOZ_RELEASE_ASSERT(NS_SUCCEEDED(dispatch_ack_rv
));
432 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
433 // Let's leave a trace that we passed here in the thread's name.
434 nsAutoCString
threadName(PR_GetThreadName(PR_GetCurrentThread()));
435 threadName
.Append(",SHDACK"_ns
);
436 NS_SetCurrentThreadName(threadName
.get());
440 "nsThread exiting after StopWaitingAndLeakThread was called, thread "
441 "resources will be leaked!");
444 // Release any observer of the thread here.
445 self
->SetObserver(nullptr);
447 // The PRThread will be deleted in PR_JoinThread(), so clear references.
448 self
->mThread
= nullptr;
449 self
->mEventTarget
->ClearCurrentThread();
452 void nsThread::InitCommon() {
453 mThreadId
= uint32_t(PlatformThread::CurrentId());
456 #if defined(XP_LINUX)
458 pthread_attr_init(&attr
);
459 pthread_getattr_np(pthread_self(), &attr
);
462 pthread_attr_getstack(&attr
, &mStackBase
, &stackSize
);
464 // Glibc prior to 2.27 reports the stack size and base including the guard
465 // region, so we need to compensate for it to get accurate accounting.
466 // Also, this behavior difference isn't guarded by a versioned symbol, so we
467 // actually need to check the runtime glibc version, not the version we were
469 static bool sAdjustForGuardSize
= ({
471 unsigned major
, minor
;
472 sscanf(gnu_get_libc_version(), "%u.%u", &major
, &minor
) < 2 ||
473 major
< 2 || (major
== 2 && minor
< 27);
478 if (sAdjustForGuardSize
) {
480 pthread_attr_getguardsize(&attr
, &guardSize
);
482 // Note: This assumes that the stack grows down, as is the case on all of
483 // our tier 1 platforms. On platforms where the stack grows up, the
484 // mStackBase adjustment is unnecessary, but doesn't cause any harm other
485 // than under-counting stack memory usage by one page.
486 mStackBase
= reinterpret_cast<char*>(mStackBase
) + guardSize
;
487 stackSize
-= guardSize
;
490 mStackSize
= stackSize
;
492 // This is a bit of a hack.
494 // We really do want the NOHUGEPAGE flag on our thread stacks, since we
495 // don't expect any of them to need anywhere near 2MB of space. But setting
496 // it here is too late to have an effect, since the first stack page has
497 // already been faulted in existence, and NSPR doesn't give us a way to set
500 // What this does get us, however, is a different set of VM flags on our
501 // thread stacks compared to normal heap memory. Which makes the Linux
502 // kernel report them as separate regions, even when they are adjacent to
503 // heap memory. This allows us to accurately track the actual memory
504 // consumption of our allocated stacks.
505 madvise(mStackBase
, stackSize
, MADV_NOHUGEPAGE
);
507 pthread_attr_destroy(&attr
);
508 #elif defined(XP_WIN)
509 static const StaticDynamicallyLinkedFunctionPtr
<
510 GetCurrentThreadStackLimitsFn
>
511 sGetStackLimits(L
"kernel32.dll", "GetCurrentThreadStackLimits");
513 if (sGetStackLimits
) {
514 ULONG_PTR stackBottom
, stackTop
;
515 sGetStackLimits(&stackBottom
, &stackTop
);
516 mStackBase
= reinterpret_cast<void*>(stackBottom
);
517 mStackSize
= stackTop
- stackBottom
;
522 InitThreadLocalVariables();
525 //-----------------------------------------------------------------------------
528 int sCanaryOutputFD
= -1;
531 nsThread::nsThread(NotNull
<SynchronizedEventQueue
*> aQueue
,
532 MainThreadFlag aMainThread
,
533 nsIThreadManager::ThreadCreationOptions aOptions
)
534 : mEvents(aQueue
.get()),
535 mEventTarget(new ThreadEventTarget(
536 mEvents
.get(), aMainThread
== MAIN_THREAD
, aOptions
.blockDispatch
)),
537 mOutstandingShutdownContexts(0),
538 mShutdownContext(nullptr),
539 mScriptObserver(nullptr),
540 mThreadName("<uninitialized>"),
541 mStackSize(aOptions
.stackSize
),
542 mNestedEventLoopDepth(0),
543 mShutdownRequired(false),
544 mPriority(PRIORITY_NORMAL
),
545 mIsMainThread(aMainThread
== MAIN_THREAD
),
546 mUseHangMonitor(aMainThread
== MAIN_THREAD
),
547 mIsUiThread(aOptions
.isUiThread
),
548 mIsAPoolThreadFree(nullptr),
550 #ifdef EARLY_BETA_OR_EARLIER
551 mLastWakeupCheckTime(TimeStamp::Now()),
553 mPerformanceCounterState(mNestedEventLoopDepth
, mIsMainThread
,
554 aOptions
.longTaskLength
) {
555 #if !(defined(XP_WIN) || defined(XP_MACOSX))
556 MOZ_ASSERT(!mIsUiThread
,
557 "Non-main UI threads are only supported on Windows and macOS");
560 MOZ_ASSERT(!mIsUiThread
,
561 "Setting isUIThread is not supported for main threads");
562 mozilla::TaskController::Get()->SetPerformanceCounterState(
563 &mPerformanceCounterState
);
569 mEventTarget(nullptr),
570 mOutstandingShutdownContexts(0),
571 mShutdownContext(nullptr),
572 mScriptObserver(nullptr),
573 mThreadName("<uninitialized>"),
575 mNestedEventLoopDepth(0),
576 mShutdownRequired(false),
577 mPriority(PRIORITY_NORMAL
),
578 mIsMainThread(false),
579 mUseHangMonitor(false),
582 #ifdef EARLY_BETA_OR_EARLIER
583 mLastWakeupCheckTime(TimeStamp::Now()),
585 mPerformanceCounterState(mNestedEventLoopDepth
) {
586 MOZ_ASSERT(!NS_IsMainThread());
589 nsThread::~nsThread() {
590 NS_ASSERTION(mOutstandingShutdownContexts
== 0,
591 "shouldn't be waiting on other threads to shutdown");
593 MaybeRemoveFromThreadList();
596 nsresult
nsThread::Init(const nsACString
& aName
) {
598 MOZ_ASSERT(mEventTarget
);
599 MOZ_ASSERT(!mThread
);
601 SetThreadNameInternal(aName
);
603 PRThread
* thread
= nullptr;
605 nsThreadManager
& tm
= nsThreadManager::get();
607 OffTheBooksMutexAutoLock
lock(tm
.ThreadListMutex());
608 if (!tm
.AllowNewXPCOMThreadsLocked()) {
609 return NS_ERROR_NOT_INITIALIZED
;
612 // We need to fully start the thread while holding the thread list lock, as
613 // the next acquire of the lock could try to shut down this thread (e.g.
614 // during xpcom shutdown), which would hang if `PR_CreateThread` failed.
616 UniquePtr
<ThreadInitData
> initData(
617 new ThreadInitData
{this, nsCString(aName
)});
619 // ThreadFunc is responsible for setting mThread
620 if (!(thread
= PR_CreateThread(PR_USER_THREAD
, ThreadFunc
, initData
.get(),
621 PR_PRIORITY_NORMAL
, PR_GLOBAL_THREAD
,
622 PR_JOINABLE_THREAD
, mStackSize
))) {
623 return NS_ERROR_OUT_OF_MEMORY
;
626 // The created thread now owns initData, so release our ownership of it.
627 Unused
<< initData
.release();
629 // The thread has successfully started, so we can mark it as requiring
630 // shutdown & add it to the thread list.
631 mShutdownRequired
= true;
632 tm
.ThreadList().insertBack(this);
635 // Note: we set these both here and inside ThreadFunc, to what should be
636 // the same value. This is because calls within ThreadFunc need these values
637 // to be set, and our callers need these values to be set.
638 DebugOnly
<PRThread
*> prev
= mThread
.exchange(thread
);
639 MOZ_ASSERT(!prev
|| prev
== thread
);
641 mEventTarget
->SetCurrentThread(thread
);
645 nsresult
nsThread::InitCurrentThread() {
646 mThread
= PR_GetCurrentThread();
648 nsThreadManager
& tm
= nsThreadManager::get();
650 OffTheBooksMutexAutoLock
lock(tm
.ThreadListMutex());
651 // NOTE: We don't check AllowNewXPCOMThreads here, as threads initialized
652 // this way do not need shutdown, so are OK to create after nsThreadManager
653 // shutdown. In addition, the main thread is initialized this way, which
654 // happens before AllowNewXPCOMThreads begins to return true.
655 tm
.ThreadList().insertBack(this);
658 SetupCurrentThreadForChaosMode();
661 tm
.RegisterCurrentThread(*this);
665 void nsThread::GetThreadName(nsACString
& aNameBuffer
) {
666 auto lock
= mThreadName
.Lock();
667 aNameBuffer
= lock
.ref();
670 void nsThread::SetThreadNameInternal(const nsACString
& aName
) {
671 auto lock
= mThreadName
.Lock();
675 //-----------------------------------------------------------------------------
679 nsThread::DispatchFromScript(nsIRunnable
* aEvent
, uint32_t aFlags
) {
680 MOZ_ASSERT(mEventTarget
);
681 NS_ENSURE_TRUE(mEventTarget
, NS_ERROR_NOT_IMPLEMENTED
);
683 nsCOMPtr
<nsIRunnable
> event(aEvent
);
684 return mEventTarget
->Dispatch(event
.forget(), aFlags
);
688 nsThread::Dispatch(already_AddRefed
<nsIRunnable
> aEvent
, uint32_t aFlags
) {
689 MOZ_ASSERT(mEventTarget
);
690 NS_ENSURE_TRUE(mEventTarget
, NS_ERROR_NOT_IMPLEMENTED
);
692 LOG(("THRD(%p) Dispatch [%p %x]\n", this, /* XXX aEvent */ nullptr, aFlags
));
694 return mEventTarget
->Dispatch(std::move(aEvent
), aFlags
);
698 nsThread::DelayedDispatch(already_AddRefed
<nsIRunnable
> aEvent
,
700 MOZ_ASSERT(mEventTarget
);
701 NS_ENSURE_TRUE(mEventTarget
, NS_ERROR_NOT_IMPLEMENTED
);
703 return mEventTarget
->DelayedDispatch(std::move(aEvent
), aDelayMs
);
707 nsThread::RegisterShutdownTask(nsITargetShutdownTask
* aTask
) {
708 MOZ_ASSERT(mEventTarget
);
709 NS_ENSURE_TRUE(mEventTarget
, NS_ERROR_NOT_IMPLEMENTED
);
711 return mEventTarget
->RegisterShutdownTask(aTask
);
715 nsThread::UnregisterShutdownTask(nsITargetShutdownTask
* aTask
) {
716 MOZ_ASSERT(mEventTarget
);
717 NS_ENSURE_TRUE(mEventTarget
, NS_ERROR_NOT_IMPLEMENTED
);
719 return mEventTarget
->UnregisterShutdownTask(aTask
);
723 nsThread::GetRunningEventDelay(TimeDuration
* aDelay
, TimeStamp
* aStart
) {
724 if (mIsAPoolThreadFree
&& *mIsAPoolThreadFree
) {
725 // if there are unstarted threads in the pool, a new event to the
726 // pool would not be delayed at all (beyond thread start time)
727 *aDelay
= TimeDuration();
728 *aStart
= TimeStamp();
730 *aDelay
= mLastEventDelay
;
731 *aStart
= mLastEventStart
;
737 nsThread::SetRunningEventDelay(TimeDuration aDelay
, TimeStamp aStart
) {
738 mLastEventDelay
= aDelay
;
739 mLastEventStart
= aStart
;
744 nsThread::IsOnCurrentThread(bool* aResult
) {
746 return mEventTarget
->IsOnCurrentThread(aResult
);
748 *aResult
= PR_GetCurrentThread() == mThread
;
753 nsThread::IsOnCurrentThreadInfallible() {
754 // This method is only going to be called if `mThread` is null, which
755 // only happens when the thread has exited the event loop. Therefore, when
756 // we are called, we can never be on this thread.
760 //-----------------------------------------------------------------------------
764 nsThread::GetPRThread(PRThread
** aResult
) {
765 PRThread
* thread
= mThread
; // atomic load
767 return thread
? NS_OK
: NS_ERROR_NOT_AVAILABLE
;
771 nsThread::GetCanInvokeJS(bool* aResult
) {
772 *aResult
= mCanInvokeJS
;
777 nsThread::SetCanInvokeJS(bool aCanInvokeJS
) {
778 mCanInvokeJS
= aCanInvokeJS
;
783 nsThread::GetLastLongTaskEnd(TimeStamp
* _retval
) {
784 *_retval
= mPerformanceCounterState
.LastLongTaskEnd();
789 nsThread::GetLastLongNonIdleTaskEnd(TimeStamp
* _retval
) {
790 *_retval
= mPerformanceCounterState
.LastLongNonIdleTaskEnd();
795 nsThread::SetNameForWakeupTelemetry(const nsACString
& aName
) {
796 #ifdef EARLY_BETA_OR_EARLIER
797 mNameForWakeupTelemetry
= aName
;
803 nsThread::AsyncShutdown() {
804 LOG(("THRD(%p) async shutdown\n", this));
806 nsCOMPtr
<nsIThreadShutdown
> shutdown
;
807 BeginShutdown(getter_AddRefs(shutdown
));
812 nsThread::BeginShutdown(nsIThreadShutdown
** aShutdown
) {
813 LOG(("THRD(%p) begin shutdown\n", this));
816 MOZ_ASSERT(mEventTarget
);
817 MOZ_ASSERT(mThread
!= PR_GetCurrentThread());
818 if (NS_WARN_IF(mThread
== PR_GetCurrentThread())) {
819 return NS_ERROR_UNEXPECTED
;
822 // Prevent multiple calls to this method.
823 if (!mShutdownRequired
.compareExchange(true, false)) {
824 return NS_ERROR_UNEXPECTED
;
828 RefPtr
<nsThread
> currentThread
= nsThreadManager::get().GetCurrentThread();
830 MOZ_DIAGNOSTIC_ASSERT(currentThread
->EventQueue(),
831 "Shutdown() may only be called from an XPCOM thread");
833 // Allocate a shutdown context, and record that we're waiting for it.
834 RefPtr
<nsThreadShutdownContext
> context
=
835 new nsThreadShutdownContext(WrapNotNull(this), currentThread
);
837 ++currentThread
->mOutstandingShutdownContexts
;
838 nsCOMPtr
<nsIRunnable
> clearOutstanding
= NS_NewRunnableFunction(
839 "nsThread::ClearOutstandingShutdownContext",
840 [currentThread
] { --currentThread
->mOutstandingShutdownContexts
; });
841 context
->OnCompletion(clearOutstanding
);
843 // Set mShutdownContext and wake up the thread in case it is waiting for
844 // events to process.
845 nsCOMPtr
<nsIRunnable
> event
=
846 new nsThreadShutdownEvent(WrapNotNull(this), WrapNotNull(context
));
847 if (!mEvents
->PutEvent(event
.forget(), EventQueuePriority::Normal
)) {
848 // We do not expect this to happen. Let's collect some diagnostics.
849 nsAutoCString threadName
;
850 GetThreadName(threadName
);
851 MOZ_CRASH_UNSAFE_PRINTF("Attempt to shutdown an already dead thread: %s",
855 // We could still end up with other events being added after the shutdown
856 // task, but that's okay because we process pending events in ThreadFunc
857 // after setting mShutdownContext just before exiting.
858 context
.forget(aShutdown
);
862 void nsThread::ShutdownComplete(NotNull
<nsThreadShutdownContext
*> aContext
) {
864 MOZ_ASSERT(mEventTarget
);
865 MOZ_ASSERT(aContext
->mTerminatingThread
== this);
867 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
869 MutexAutoLock
lock(aContext
->mJoiningThreadMutex
);
871 // StopWaitingAndLeakThread is explicitely meant to not cause a
872 // nsThreadShutdownAckEvent on the joining thread, which is the only
873 // caller of ShutdownComplete.
874 MOZ_DIAGNOSTIC_ASSERT(!aContext
->mThreadLeaked
);
878 MaybeRemoveFromThreadList();
880 // Now, it should be safe to join without fear of dead-locking.
881 PR_JoinThread(aContext
->mTerminatingPRThread
);
882 MOZ_ASSERT(!mThread
);
885 nsCOMPtr
<nsIThreadObserver
> obs
= mEvents
->GetObserver();
886 MOZ_ASSERT(!obs
, "Should have been cleared at shutdown!");
889 aContext
->MarkCompleted();
892 void nsThread::WaitForAllAsynchronousShutdowns() {
893 // This is the motivating example for why SpinEventLoopUntil
894 // has the template parameter we are providing here.
895 SpinEventLoopUntil
<ProcessFailureBehavior::IgnoreAndContinue
>(
896 "nsThread::WaitForAllAsynchronousShutdowns"_ns
,
897 [&]() { return mOutstandingShutdownContexts
== 0; }, this);
901 nsThread::Shutdown() {
902 LOG(("THRD(%p) sync shutdown\n", this));
904 nsCOMPtr
<nsIThreadShutdown
> context
;
905 nsresult rv
= BeginShutdown(getter_AddRefs(context
));
907 return NS_OK
; // The thread has already shut down.
910 // If we are going to hang here we want to see the thread's name
911 nsAutoCString threadName
;
912 GetThreadName(threadName
);
914 // Process events on the current thread until we receive a shutdown ACK.
915 // Allows waiting; ensure no locks are held that would deadlock us!
916 SpinEventLoopUntil("nsThread::Shutdown: "_ns
+ threadName
,
917 [&]() { return context
->GetCompleted(); });
923 nsThread::HasPendingEvents(bool* aResult
) {
924 if (NS_WARN_IF(PR_GetCurrentThread() != mThread
)) {
925 return NS_ERROR_NOT_SAME_THREAD
;
929 *aResult
= TaskController::Get()->HasMainThreadPendingTasks();
931 *aResult
= mEvents
->HasPendingEvent();
937 nsThread::HasPendingHighPriorityEvents(bool* aResult
) {
938 if (NS_WARN_IF(PR_GetCurrentThread() != mThread
)) {
939 return NS_ERROR_NOT_SAME_THREAD
;
942 // This function appears to never be called anymore.
948 nsThread::DispatchToQueue(already_AddRefed
<nsIRunnable
> aEvent
,
949 EventQueuePriority aQueue
) {
950 nsCOMPtr
<nsIRunnable
> event
= aEvent
;
952 if (NS_WARN_IF(!event
)) {
953 return NS_ERROR_INVALID_ARG
;
956 if (!mEvents
->PutEvent(event
.forget(), aQueue
)) {
958 "An idle event was posted to a thread that will never run it "
960 return NS_ERROR_UNEXPECTED
;
966 NS_IMETHODIMP
nsThread::SetThreadQoS(nsIThread::QoSPriority aPriority
) {
967 if (!StaticPrefs::threads_use_low_power_enabled()) {
970 // The approach here is to have a thread set itself for its QoS level,
971 // so we assert if we aren't on the current thread.
972 MOZ_ASSERT(IsOnCurrentThread(), "Can only change the current thread's QoS");
974 #if defined(XP_MACOSX)
975 // Only arm64 macs may possess heterogeneous cores. On these, we can tell
976 // a thread to set its own QoS status. On intel macs things should behave
977 // normally, and the OS will ignore the QoS state of the thread.
978 if (aPriority
== nsIThread::QOS_PRIORITY_LOW
) {
979 pthread_set_qos_class_self_np(QOS_CLASS_BACKGROUND
, 0);
980 } else if (NS_IsMainThread()) {
981 // MacOS documentation specifies that a main thread should be initialized at
982 // the USER_INTERACTIVE priority, so when we restore thread priorities the
983 // main thread should be setting itself to this.
984 pthread_set_qos_class_self_np(QOS_CLASS_USER_INTERACTIVE
, 0);
986 pthread_set_qos_class_self_np(QOS_CLASS_DEFAULT
, 0);
989 // Do nothing if an OS-specific implementation is unavailable.
994 void canary_alarm_handler(int signum
);
997 // XXX ToDo: support nested loops
1000 if (sCanaryOutputFD
> 0 && EventLatencyIsImportant()) {
1001 signal(SIGALRM
, canary_alarm_handler
);
1007 if (sCanaryOutputFD
!= 0 && EventLatencyIsImportant()) {
1012 static bool EventLatencyIsImportant() {
1013 return NS_IsMainThread() && XRE_IsParentProcess();
1017 void canary_alarm_handler(int signum
) {
1019 const char msg
[29] = "event took too long to run:\n";
1020 // use write to be safe in the signal handler
1021 write(sCanaryOutputFD
, msg
, sizeof(msg
));
1022 backtrace_symbols_fd(array
, backtrace(array
, 30), sCanaryOutputFD
);
1027 #define NOTIFY_EVENT_OBSERVERS(observers_, func_, params_) \
1029 if (!observers_.IsEmpty()) { \
1030 for (nsCOMPtr<nsIThreadObserver> obs_ : observers_.ForwardRange()) { \
1031 obs_->func_ params_; \
1036 size_t nsThread::ShallowSizeOfIncludingThis(
1037 mozilla::MallocSizeOf aMallocSizeOf
) const {
1039 if (mShutdownContext
) {
1040 n
+= aMallocSizeOf(mShutdownContext
);
1042 return aMallocSizeOf(this) + aMallocSizeOf(mThread
) + n
;
1045 size_t nsThread::SizeOfEventQueues(mozilla::MallocSizeOf aMallocSizeOf
) const {
1048 // The size of mEvents is reported by mEventTarget.
1049 n
+= mEventTarget
->SizeOfIncludingThis(aMallocSizeOf
);
1054 size_t nsThread::SizeOfIncludingThis(
1055 mozilla::MallocSizeOf aMallocSizeOf
) const {
1056 return ShallowSizeOfIncludingThis(aMallocSizeOf
) +
1057 SizeOfEventQueues(aMallocSizeOf
);
1061 nsThread::ProcessNextEvent(bool aMayWait
, bool* aResult
) {
1062 MOZ_ASSERT(mEvents
);
1063 NS_ENSURE_TRUE(mEvents
, NS_ERROR_NOT_IMPLEMENTED
);
1065 LOG(("THRD(%p) ProcessNextEvent [%u %u]\n", this, aMayWait
,
1066 mNestedEventLoopDepth
));
1068 if (NS_WARN_IF(PR_GetCurrentThread() != mThread
)) {
1069 return NS_ERROR_NOT_SAME_THREAD
;
1072 // The toplevel event loop normally blocks waiting for the next event, but
1073 // if we're trying to shut this thread down, we must exit the event loop
1074 // when the event queue is empty. This only applys to the toplevel event
1075 // loop! Nested event loops (e.g. during sync dispatch) are waiting for
1076 // some state change and must be able to block even if something has
1077 // requested shutdown of the thread. Otherwise we'll just busywait as we
1078 // endlessly look for an event, fail to find one, and repeat the nested
1079 // event loop since its state change hasn't happened yet.
1080 bool reallyWait
= aMayWait
&& (mNestedEventLoopDepth
> 0 || !ShuttingDown());
1082 Maybe
<dom::AutoNoJSAPI
> noJSAPI
;
1084 if (mUseHangMonitor
&& reallyWait
) {
1085 BackgroundHangMonitor().NotifyWait();
1088 if (mIsMainThread
) {
1089 DoMainThreadSpecificProcessing();
1092 ++mNestedEventLoopDepth
;
1094 // We only want to create an AutoNoJSAPI on threads that actually do DOM
1095 // stuff (including workers). Those are exactly the threads that have an
1097 bool callScriptObserver
= !!mScriptObserver
;
1098 if (callScriptObserver
) {
1100 mScriptObserver
->BeforeProcessTask(reallyWait
);
1105 #ifdef EARLY_BETA_OR_EARLIER
1106 // Need to capture mayWaitForWakeup state before OnProcessNextEvent,
1107 // since on the main thread OnProcessNextEvent ends up waiting for the new
1109 bool mayWaitForWakeup
= reallyWait
&& !mEvents
->HasPendingEvent();
1112 nsCOMPtr
<nsIThreadObserver
> obs
= mEvents
->GetObserverOnThread();
1114 obs
->OnProcessNextEvent(this, reallyWait
);
1117 NOTIFY_EVENT_OBSERVERS(EventQueue()->EventObservers(), OnProcessNextEvent
,
1118 (this, reallyWait
));
1125 nsresult rv
= NS_OK
;
1128 // Scope for |event| to make sure that its destructor fires while
1129 // mNestedEventLoopDepth has been incremented, since that destructor can
1131 nsCOMPtr
<nsIRunnable
> event
;
1132 bool usingTaskController
= mIsMainThread
;
1133 if (usingTaskController
) {
1134 event
= TaskController::Get()->GetRunnableForMTTask(reallyWait
);
1136 event
= mEvents
->GetEvent(reallyWait
, &mLastEventDelay
);
1139 *aResult
= (event
.get() != nullptr);
1142 #ifdef EARLY_BETA_OR_EARLIER
1143 if (mayWaitForWakeup
&& mThread
) {
1145 if (mWakeupCount
== kTelemetryWakeupCountLimit
) {
1146 TimeStamp now
= TimeStamp::Now();
1147 double ms
= (now
- mLastWakeupCheckTime
).ToMilliseconds();
1151 const char* name
= !mNameForWakeupTelemetry
.IsEmpty()
1152 ? mNameForWakeupTelemetry
.get()
1153 : PR_GetThreadName(mThread
);
1155 name
= mIsMainThread
? "MainThread" : "(nameless thread)";
1157 nsDependentCString
key(name
);
1158 Telemetry::Accumulate(Telemetry::THREAD_WAKEUP
, key
,
1159 static_cast<uint32_t>(ms
));
1160 mLastWakeupCheckTime
= now
;
1166 LOG(("THRD(%p) running [%p]\n", this, event
.get()));
1168 Maybe
<LogRunnable::Run
> log
;
1170 if (!usingTaskController
) {
1174 // Delay event processing to encourage whoever dispatched this event
1176 DelayForChaosMode(ChaosFeature::TaskRunning
, 1000);
1178 mozilla::TimeStamp now
= mozilla::TimeStamp::Now();
1180 if (mUseHangMonitor
) {
1181 BackgroundHangMonitor().NotifyActivity();
1184 Maybe
<PerformanceCounterState::Snapshot
> snapshot
;
1185 if (!usingTaskController
) {
1186 snapshot
.emplace(mPerformanceCounterState
.RunnableWillRun(now
, false));
1189 mLastEventStart
= now
;
1191 if (!usingTaskController
) {
1192 AUTO_PROFILE_FOLLOWING_RUNNABLE(event
);
1195 // Avoid generating "Runnable" profiler markers for the
1196 // "TaskController::ExecutePendingMTTasks" runnables created
1197 // by TaskController, which already adds "Runnable" markers
1198 // when executing tasks.
1202 if (usingTaskController
) {
1203 *aResult
= TaskController::Get()->MTTaskRunnableProcessedTask();
1205 mPerformanceCounterState
.RunnableDidRun(EmptyCString(),
1206 std::move(snapshot
.ref()));
1209 // To cover the event's destructor code inside the LogRunnable span.
1212 mLastEventDelay
= TimeDuration();
1213 mLastEventStart
= TimeStamp();
1215 MOZ_ASSERT(ShuttingDown(),
1216 "This should only happen when shutting down");
1217 rv
= NS_ERROR_UNEXPECTED
;
1224 NOTIFY_EVENT_OBSERVERS(EventQueue()->EventObservers(), AfterProcessNextEvent
,
1228 obs
->AfterProcessNextEvent(this, *aResult
);
1231 // In case some EventObserver dispatched some direct tasks; process them
1235 if (callScriptObserver
) {
1236 if (mScriptObserver
) {
1237 mScriptObserver
->AfterProcessTask(mNestedEventLoopDepth
);
1242 --mNestedEventLoopDepth
;
1247 //-----------------------------------------------------------------------------
1248 // nsISupportsPriority
1251 nsThread::GetPriority(int32_t* aPriority
) {
1252 *aPriority
= mPriority
;
1257 nsThread::SetPriority(int32_t aPriority
) {
1258 if (NS_WARN_IF(!mThread
)) {
1259 return NS_ERROR_NOT_INITIALIZED
;
1262 // NSPR defines the following four thread priorities:
1264 // PR_PRIORITY_NORMAL
1266 // PR_PRIORITY_URGENT
1267 // We map the priority values defined on nsISupportsPriority to these
1270 mPriority
= aPriority
;
1272 PRThreadPriority pri
;
1273 if (mPriority
<= PRIORITY_HIGHEST
) {
1274 pri
= PR_PRIORITY_URGENT
;
1275 } else if (mPriority
< PRIORITY_NORMAL
) {
1276 pri
= PR_PRIORITY_HIGH
;
1277 } else if (mPriority
> PRIORITY_NORMAL
) {
1278 pri
= PR_PRIORITY_LOW
;
1280 pri
= PR_PRIORITY_NORMAL
;
1282 // If chaos mode is active, retain the randomly chosen priority
1283 if (!ChaosMode::isActive(ChaosFeature::ThreadScheduling
)) {
1284 PR_SetThreadPriority(mThread
, pri
);
1291 nsThread::AdjustPriority(int32_t aDelta
) {
1292 return SetPriority(mPriority
+ aDelta
);
1295 //-----------------------------------------------------------------------------
1296 // nsIThreadInternal
1299 nsThread::GetObserver(nsIThreadObserver
** aObs
) {
1300 MOZ_ASSERT(mEvents
);
1301 NS_ENSURE_TRUE(mEvents
, NS_ERROR_NOT_IMPLEMENTED
);
1303 nsCOMPtr
<nsIThreadObserver
> obs
= mEvents
->GetObserver();
1309 nsThread::SetObserver(nsIThreadObserver
* aObs
) {
1310 MOZ_ASSERT(mEvents
);
1311 NS_ENSURE_TRUE(mEvents
, NS_ERROR_NOT_IMPLEMENTED
);
1313 if (NS_WARN_IF(PR_GetCurrentThread() != mThread
)) {
1314 return NS_ERROR_NOT_SAME_THREAD
;
1317 mEvents
->SetObserver(aObs
);
1321 uint32_t nsThread::RecursionDepth() const {
1322 MOZ_ASSERT(PR_GetCurrentThread() == mThread
);
1323 return mNestedEventLoopDepth
;
1327 nsThread::AddObserver(nsIThreadObserver
* aObserver
) {
1328 MOZ_ASSERT(mEvents
);
1329 NS_ENSURE_TRUE(mEvents
, NS_ERROR_NOT_IMPLEMENTED
);
1331 if (NS_WARN_IF(!aObserver
)) {
1332 return NS_ERROR_INVALID_ARG
;
1334 if (NS_WARN_IF(PR_GetCurrentThread() != mThread
)) {
1335 return NS_ERROR_NOT_SAME_THREAD
;
1338 EventQueue()->AddObserver(aObserver
);
1344 nsThread::RemoveObserver(nsIThreadObserver
* aObserver
) {
1345 MOZ_ASSERT(mEvents
);
1346 NS_ENSURE_TRUE(mEvents
, NS_ERROR_NOT_IMPLEMENTED
);
1348 if (NS_WARN_IF(PR_GetCurrentThread() != mThread
)) {
1349 return NS_ERROR_NOT_SAME_THREAD
;
1352 EventQueue()->RemoveObserver(aObserver
);
1357 void nsThread::SetScriptObserver(
1358 mozilla::CycleCollectedJSContext
* aScriptObserver
) {
1359 if (!aScriptObserver
) {
1360 mScriptObserver
= nullptr;
1364 MOZ_ASSERT(!mScriptObserver
);
1365 mScriptObserver
= aScriptObserver
;
1368 void NS_DispatchMemoryPressure();
1370 void nsThread::DoMainThreadSpecificProcessing() const {
1371 MOZ_ASSERT(mIsMainThread
);
1375 // Fire a memory pressure notification, if one is pending.
1376 if (!ShuttingDown()) {
1377 NS_DispatchMemoryPressure();
1381 //-----------------------------------------------------------------------------
1382 // nsIDirectTaskDispatcher
1385 nsThread::DispatchDirectTask(already_AddRefed
<nsIRunnable
> aEvent
) {
1386 if (!IsOnCurrentThread()) {
1387 return NS_ERROR_FAILURE
;
1389 mDirectTasks
.AddTask(std::move(aEvent
));
1393 NS_IMETHODIMP
nsThread::DrainDirectTasks() {
1394 if (!IsOnCurrentThread()) {
1395 return NS_ERROR_FAILURE
;
1397 mDirectTasks
.DrainTasks();
1401 NS_IMETHODIMP
nsThread::HaveDirectTasks(bool* aValue
) {
1402 if (!IsOnCurrentThread()) {
1403 return NS_ERROR_FAILURE
;
1406 *aValue
= mDirectTasks
.HaveTasks();
1410 NS_IMPL_ISUPPORTS(nsThreadShutdownContext
, nsIThreadShutdown
)
1413 nsThreadShutdownContext::OnCompletion(nsIRunnable
* aEvent
) {
1417 mCompletionCallbacks
.AppendElement(aEvent
);
1423 nsThreadShutdownContext::GetCompleted(bool* aCompleted
) {
1424 *aCompleted
= mCompleted
;
1429 nsThreadShutdownContext::StopWaitingAndLeakThread() {
1430 // Take the joining thread from `mJoiningThread` so that the terminating
1431 // thread won't try to dispatch nsThreadShutdownAckEvent to us anymore.
1432 RefPtr
<nsThread
> joiningThread
;
1434 MutexAutoLock
lock(mJoiningThreadMutex
);
1435 if (!mJoiningThread
) {
1436 // Shutdown is already being resolved, so there's nothing for us to do.
1437 return NS_ERROR_NOT_AVAILABLE
;
1439 joiningThread
= mJoiningThread
.forget();
1440 mThreadLeaked
= true;
1443 MOZ_DIAGNOSTIC_ASSERT(joiningThread
->IsOnCurrentThread());
1450 void nsThreadShutdownContext::MarkCompleted() {
1451 MOZ_ASSERT(!mCompleted
);
1453 nsTArray
<nsCOMPtr
<nsIRunnable
>> callbacks(std::move(mCompletionCallbacks
));
1454 for (auto& callback
: callbacks
) {
1460 PerformanceCounterState::Snapshot
PerformanceCounterState::RunnableWillRun(
1461 TimeStamp aNow
, bool aIsIdleRunnable
) {
1462 if (mIsMainThread
&& IsNestedRunnable()) {
1463 // Flush out any accumulated time that should be accounted to the
1464 // current runnable before we start running a nested runnable. Don't
1465 // do this for non-mainthread threads that may be running their own
1466 // event loops, like SocketThread.
1467 MaybeReportAccumulatedTime("nested runnable"_ns
, aNow
);
1470 Snapshot
snapshot(mCurrentEventLoopDepth
, mCurrentRunnableIsIdleRunnable
);
1472 mCurrentEventLoopDepth
= mNestedEventLoopDepth
;
1473 mCurrentRunnableIsIdleRunnable
= aIsIdleRunnable
;
1474 mCurrentTimeSliceStart
= aNow
;
1479 void PerformanceCounterState::RunnableDidRun(const nsCString
& aName
,
1480 Snapshot
&& aSnapshot
) {
1481 // First thing: Restore our mCurrentEventLoopDepth so we can use
1482 // IsNestedRunnable().
1483 mCurrentEventLoopDepth
= aSnapshot
.mOldEventLoopDepth
;
1485 // We may not need the current timestamp; don't bother computing it if we
1488 if (mLongTaskLength
.isSome() || IsNestedRunnable()) {
1489 now
= TimeStamp::Now();
1491 if (mLongTaskLength
.isSome()) {
1492 MaybeReportAccumulatedTime(aName
, now
);
1495 // And now restore the rest of our state.
1496 mCurrentRunnableIsIdleRunnable
= aSnapshot
.mOldIsIdleRunnable
;
1497 if (IsNestedRunnable()) {
1498 // Reset mCurrentTimeSliceStart to right now, so our parent runnable's
1499 // next slice can be properly accounted for.
1500 mCurrentTimeSliceStart
= now
;
1502 // We are done at the outermost level; we are no longer in a timeslice.
1503 mCurrentTimeSliceStart
= TimeStamp();
1507 void PerformanceCounterState::MaybeReportAccumulatedTime(const nsCString
& aName
,
1509 MOZ_ASSERT(mCurrentTimeSliceStart
,
1510 "How did we get here if we're not in a timeslice?");
1511 if (!mLongTaskLength
.isSome()) {
1515 TimeDuration duration
= aNow
- mCurrentTimeSliceStart
;
1516 #ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
1517 if (mIsMainThread
&& duration
.ToMilliseconds() > LONGTASK_TELEMETRY_MS
) {
1518 Telemetry::Accumulate(Telemetry::EVENT_LONGTASK
, aName
,
1519 duration
.ToMilliseconds());
1523 // Long tasks only matter on the main thread.
1524 if (duration
.ToMilliseconds() >= mLongTaskLength
.value()) {
1525 // Idle events (gc...) don't *really* count here
1526 if (!mCurrentRunnableIsIdleRunnable
) {
1527 mLastLongNonIdleTaskEnd
= aNow
;
1529 mLastLongTaskEnd
= aNow
;
1531 if (profiler_thread_is_being_profiled_for_markers()) {
1532 struct LongTaskMarker
{
1533 static constexpr Span
<const char> MarkerTypeName() {
1534 return MakeStringSpan("MainThreadLongTask");
1536 static void StreamJSONMarkerData(
1537 baseprofiler::SpliceableJSONWriter
& aWriter
) {
1538 aWriter
.StringProperty("category", "LongTask");
1540 static MarkerSchema
MarkerTypeDisplay() {
1541 using MS
= MarkerSchema
;
1542 MS schema
{MS::Location::MarkerChart
, MS::Location::MarkerTable
};
1543 schema
.AddKeyLabelFormatSearchable("category", "Type",
1545 MS::Searchable::Searchable
);
1550 profiler_add_marker(mCurrentRunnableIsIdleRunnable
1551 ? ProfilerString8View("LongIdleTask")
1552 : ProfilerString8View("LongTask"),
1553 geckoprofiler::category::OTHER
,
1554 MarkerTiming::Interval(mCurrentTimeSliceStart
, aNow
),
1560 } // namespace mozilla