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 //-----------------------------------------------------------------------------
114 // Because we do not have our own nsIFactory, we have to implement nsIClassInfo
115 // somewhat manually.
117 class nsThreadClassInfo
: public nsIClassInfo
{
119 NS_DECL_ISUPPORTS_INHERITED
// no mRefCnt
122 nsThreadClassInfo() = default;
125 NS_IMETHODIMP_(MozExternalRefCountType
)
126 nsThreadClassInfo::AddRef() { return 2; }
127 NS_IMETHODIMP_(MozExternalRefCountType
)
128 nsThreadClassInfo::Release() { return 1; }
129 NS_IMPL_QUERY_INTERFACE(nsThreadClassInfo
, nsIClassInfo
)
132 nsThreadClassInfo::GetInterfaces(nsTArray
<nsIID
>& aArray
) {
133 return NS_CI_INTERFACE_GETTER_NAME(nsThread
)(aArray
);
137 nsThreadClassInfo::GetScriptableHelper(nsIXPCScriptable
** aResult
) {
143 nsThreadClassInfo::GetContractID(nsACString
& aResult
) {
144 aResult
.SetIsVoid(true);
149 nsThreadClassInfo::GetClassDescription(nsACString
& aResult
) {
150 aResult
.SetIsVoid(true);
155 nsThreadClassInfo::GetClassID(nsCID
** aResult
) {
161 nsThreadClassInfo::GetFlags(uint32_t* aResult
) {
162 *aResult
= THREADSAFE
;
167 nsThreadClassInfo::GetClassIDNoAlloc(nsCID
* aResult
) {
168 return NS_ERROR_NOT_AVAILABLE
;
171 //-----------------------------------------------------------------------------
173 NS_IMPL_ADDREF(nsThread
)
174 NS_IMPL_RELEASE(nsThread
)
175 NS_INTERFACE_MAP_BEGIN(nsThread
)
176 NS_INTERFACE_MAP_ENTRY(nsIThread
)
177 NS_INTERFACE_MAP_ENTRY(nsIThreadInternal
)
178 NS_INTERFACE_MAP_ENTRY(nsIEventTarget
)
179 NS_INTERFACE_MAP_ENTRY(nsISerialEventTarget
)
180 NS_INTERFACE_MAP_ENTRY(nsISupportsPriority
)
181 NS_INTERFACE_MAP_ENTRY(nsIDirectTaskDispatcher
)
182 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIThread
)
183 if (aIID
.Equals(NS_GET_IID(nsIClassInfo
))) {
184 static nsThreadClassInfo sThreadClassInfo
;
185 foundInterface
= static_cast<nsIClassInfo
*>(&sThreadClassInfo
);
188 NS_IMPL_CI_INTERFACE_GETTER(nsThread
, nsIThread
, nsIThreadInternal
,
189 nsIEventTarget
, nsISerialEventTarget
,
192 //-----------------------------------------------------------------------------
194 // This event is responsible for notifying nsThread::Shutdown that it is time
195 // to call PR_JoinThread. It implements nsICancelableRunnable so that it can
196 // run on a DOM Worker thread (where all events must implement
197 // nsICancelableRunnable.)
198 class nsThreadShutdownAckEvent
: public CancelableRunnable
{
200 explicit nsThreadShutdownAckEvent(NotNull
<nsThreadShutdownContext
*> aCtx
)
201 : CancelableRunnable("nsThreadShutdownAckEvent"),
202 mShutdownContext(aCtx
) {}
203 NS_IMETHOD
Run() override
{
204 mShutdownContext
->mTerminatingThread
->ShutdownComplete(mShutdownContext
);
207 nsresult
Cancel() override
{ return Run(); }
210 virtual ~nsThreadShutdownAckEvent() = default;
212 NotNull
<RefPtr
<nsThreadShutdownContext
>> mShutdownContext
;
215 // This event is responsible for setting mShutdownContext
216 class nsThreadShutdownEvent
: public Runnable
{
218 nsThreadShutdownEvent(NotNull
<nsThread
*> aThr
,
219 NotNull
<nsThreadShutdownContext
*> aCtx
)
220 : Runnable("nsThreadShutdownEvent"),
222 mShutdownContext(aCtx
) {}
223 NS_IMETHOD
Run() override
{
224 // Creates a cycle between `mThread` and the shutdown context which will be
225 // broken when the thread exits.
226 mThread
->mShutdownContext
= mShutdownContext
;
227 MessageLoop::current()->Quit();
228 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
229 // Let's leave a trace that we passed here in the thread's name.
230 nsAutoCString
threadName(PR_GetThreadName(PR_GetCurrentThread()));
231 threadName
.Append(",SHDRCV"_ns
);
232 NS_SetCurrentThreadName(threadName
.get());
238 NotNull
<RefPtr
<nsThread
>> mThread
;
239 NotNull
<RefPtr
<nsThreadShutdownContext
>> mShutdownContext
;
242 //-----------------------------------------------------------------------------
244 static void SetThreadAffinity(unsigned int cpu
) {
245 #ifdef HAVE_SCHED_SETAFFINITY
249 sched_setaffinity(0, sizeof(cpus
), &cpus
);
250 // Don't assert sched_setaffinity's return value because it intermittently (?)
251 // fails with EINVAL on Linux x64 try runs.
252 #elif defined(XP_MACOSX)
253 // OS X does not provide APIs to pin threads to specific processors, but you
254 // can tag threads as belonging to the same "affinity set" and the OS will try
255 // to run them on the same processor. To run threads on different processors,
256 // tag them as belonging to different affinity sets. Tag 0, the default, means
257 // "no affinity" so let's pretend each CPU has its own tag `cpu+1`.
258 thread_affinity_policy_data_t policy
;
259 policy
.affinity_tag
= cpu
+ 1;
260 kern_return_t kr
= thread_policy_set(
261 mach_thread_self(), THREAD_AFFINITY_POLICY
, &policy
.affinity_tag
, 1);
262 // Setting the thread affinity is not supported on ARM.
263 MOZ_ALWAYS_TRUE(kr
== KERN_SUCCESS
|| kr
== KERN_NOT_SUPPORTED
);
264 #elif defined(XP_WIN)
265 MOZ_ALWAYS_TRUE(SetThreadIdealProcessor(GetCurrentThread(), cpu
) !=
270 static void SetupCurrentThreadForChaosMode() {
271 if (!ChaosMode::isActive(ChaosFeature::ThreadScheduling
)) {
276 // PR_SetThreadPriority doesn't really work since priorities >
277 // PR_PRIORITY_NORMAL can't be set by non-root users. Instead we'll just use
278 // setpriority(2) to set random 'nice values'. In regular Linux this is only
279 // a dynamic adjustment so it still doesn't really do what we want, but tools
280 // like 'rr' can be more aggressive about honoring these values.
281 // Some of these calls may fail due to trying to lower the priority
282 // (e.g. something may have already called setpriority() for this thread).
283 // This makes it hard to have non-main threads with higher priority than the
284 // main thread, but that's hard to fix. Tools like rr can choose to honor the
285 // requested values anyway.
286 // Use just 4 priorities so there's a reasonable chance of any two threads
287 // having equal priority.
288 setpriority(PRIO_PROCESS
, 0, ChaosMode::randomUint32LessThan(4));
290 // We should set the affinity here but NSPR doesn't provide a way to expose
292 uint32_t priority
= ChaosMode::randomUint32LessThan(PR_PRIORITY_LAST
+ 1);
293 PR_SetThreadPriority(PR_GetCurrentThread(), PRThreadPriority(priority
));
296 // Force half the threads to CPU 0 so they compete for CPU
297 if (ChaosMode::randomUint32LessThan(2)) {
298 SetThreadAffinity(0);
304 struct ThreadInitData
{
305 RefPtr
<nsThread
> thread
;
311 void nsThread::MaybeRemoveFromThreadList() {
312 nsThreadManager
& tm
= nsThreadManager::get();
313 OffTheBooksMutexAutoLock
mal(tm
.ThreadListMutex());
315 removeFrom(tm
.ThreadList());
320 void nsThread::ThreadFunc(void* aArg
) {
321 using mozilla::ipc::BackgroundChild
;
323 UniquePtr
<ThreadInitData
> initData(static_cast<ThreadInitData
*>(aArg
));
324 RefPtr
<nsThread
>& self
= initData
->thread
;
326 MOZ_ASSERT(self
->mEventTarget
);
327 MOZ_ASSERT(self
->mEvents
);
329 // Note: see the comment in nsThread::Init, where we set these same values.
330 DebugOnly
<PRThread
*> prev
= self
->mThread
.exchange(PR_GetCurrentThread());
331 MOZ_ASSERT(!prev
|| prev
== PR_GetCurrentThread());
332 self
->mEventTarget
->SetCurrentThread(self
->mThread
);
333 SetupCurrentThreadForChaosMode();
335 if (!initData
->name
.IsEmpty()) {
336 NS_SetCurrentThreadName(initData
->name
.BeginReading());
341 // Inform the ThreadManager
342 nsThreadManager::get().RegisterCurrentThread(*self
);
344 mozilla::IOInterposer::RegisterCurrentThread();
346 // This must come after the call to nsThreadManager::RegisterCurrentThread(),
347 // because that call is needed to properly set up this thread as an nsThread,
348 // which profiler_register_thread() requires. See bug 1347007.
349 const bool registerWithProfiler
= !initData
->name
.IsEmpty();
350 if (registerWithProfiler
) {
351 PROFILER_REGISTER_THREAD(initData
->name
.BeginReading());
355 // Scope for MessageLoop.
357 #if defined(XP_WIN) || defined(XP_MACOSX)
358 self
->mIsUiThread
? MessageLoop::TYPE_MOZILLA_NONMAINUITHREAD
359 : MessageLoop::TYPE_MOZILLA_NONMAINTHREAD
,
361 MessageLoop::TYPE_MOZILLA_NONMAINTHREAD
,
365 // Now, process incoming events...
368 self
->mEvents
->RunShutdownTasks();
370 BackgroundChild::CloseForCurrentThread();
372 // NB: The main thread does not shut down here! It shuts down via
373 // nsThreadManager::Shutdown.
375 // Do NS_ProcessPendingEvents but with special handling to set
376 // mEventsAreDoomed atomically with the removal of the last event. The key
377 // invariant here is that we will never permit PutEvent to succeed if the
378 // event would be left in the queue after our final call to
379 // NS_ProcessPendingEvents. We also have to keep processing events as long
380 // as we have outstanding mRequestedShutdownContexts.
382 // Check and see if we're waiting on any threads.
383 self
->WaitForAllAsynchronousShutdowns();
385 if (self
->mEvents
->ShutdownIfNoPendingEvents()) {
388 NS_ProcessPendingEvents(self
);
392 mozilla::IOInterposer::UnregisterCurrentThread();
394 // Inform the threadmanager that this thread is going away
395 nsThreadManager::get().UnregisterCurrentThread(*self
);
397 // The thread should only unregister itself if it was registered above.
398 if (registerWithProfiler
) {
399 PROFILER_UNREGISTER_THREAD();
402 NotNull
<RefPtr
<nsThreadShutdownContext
>> context
=
403 WrapNotNull(self
->mShutdownContext
);
404 self
->mShutdownContext
= nullptr;
405 MOZ_ASSERT(context
->mTerminatingThread
== self
);
407 // Take the joining thread from our shutdown context. This may have been
408 // cleared by the joining thread if it decided to cancel waiting on us, in
409 // which case we won't notify our caller, and leak.
410 RefPtr
<nsThread
> joiningThread
;
412 MutexAutoLock
lock(context
->mJoiningThreadMutex
);
413 joiningThread
= context
->mJoiningThread
.forget();
414 MOZ_RELEASE_ASSERT(joiningThread
|| context
->mThreadLeaked
);
417 // Dispatch shutdown ACK
418 nsCOMPtr
<nsIRunnable
> event
= new nsThreadShutdownAckEvent(context
);
419 nsresult dispatch_ack_rv
=
420 joiningThread
->Dispatch(event
, NS_DISPATCH_NORMAL
);
422 // We do not expect this to ever happen, but If we cannot dispatch
423 // the ack event, someone probably blocks waiting on us and will
424 // crash with a hang later anyways. The best we can do is to tell
425 // the world what happened right here.
426 MOZ_RELEASE_ASSERT(NS_SUCCEEDED(dispatch_ack_rv
));
428 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
429 // Let's leave a trace that we passed here in the thread's name.
430 nsAutoCString
threadName(PR_GetThreadName(PR_GetCurrentThread()));
431 threadName
.Append(",SHDACK"_ns
);
432 NS_SetCurrentThreadName(threadName
.get());
436 "nsThread exiting after StopWaitingAndLeakThread was called, thread "
437 "resources will be leaked!");
440 // Release any observer of the thread here.
441 self
->SetObserver(nullptr);
443 // The PRThread will be deleted in PR_JoinThread(), so clear references.
444 self
->mThread
= nullptr;
445 self
->mEventTarget
->ClearCurrentThread();
448 void nsThread::InitCommon() {
449 mThreadId
= uint32_t(PlatformThread::CurrentId());
452 #if defined(XP_LINUX)
454 int res
= pthread_attr_init(&attr
);
455 MOZ_RELEASE_ASSERT(!res
);
456 res
= pthread_getattr_np(pthread_self(), &attr
);
457 MOZ_RELEASE_ASSERT(!res
);
460 res
= pthread_attr_getstack(&attr
, &mStackBase
, &stackSize
);
461 MOZ_RELEASE_ASSERT(!res
);
463 // Glibc prior to 2.27 reports the stack size and base including the guard
464 // region, so we need to compensate for it to get accurate accounting.
465 // Also, this behavior difference isn't guarded by a versioned symbol, so we
466 // actually need to check the runtime glibc version, not the version we were
468 static bool sAdjustForGuardSize
= ({
470 unsigned major
, minor
;
471 sscanf(gnu_get_libc_version(), "%u.%u", &major
, &minor
) < 2 ||
472 major
< 2 || (major
== 2 && minor
< 27);
477 if (sAdjustForGuardSize
) {
479 res
= pthread_attr_getguardsize(&attr
, &guardSize
);
480 MOZ_RELEASE_ASSERT(!res
);
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 res
= pthread_attr_destroy(&attr
);
508 MOZ_RELEASE_ASSERT(!res
);
509 #elif defined(XP_WIN)
510 static const StaticDynamicallyLinkedFunctionPtr
<
511 GetCurrentThreadStackLimitsFn
>
512 sGetStackLimits(L
"kernel32.dll", "GetCurrentThreadStackLimits");
514 if (sGetStackLimits
) {
515 ULONG_PTR stackBottom
, stackTop
;
516 sGetStackLimits(&stackBottom
, &stackTop
);
517 mStackBase
= reinterpret_cast<void*>(stackBottom
);
518 mStackSize
= stackTop
- stackBottom
;
523 InitThreadLocalVariables();
526 //-----------------------------------------------------------------------------
529 int sCanaryOutputFD
= -1;
532 nsThread::nsThread(NotNull
<SynchronizedEventQueue
*> aQueue
,
533 MainThreadFlag aMainThread
,
534 nsIThreadManager::ThreadCreationOptions aOptions
)
535 : mEvents(aQueue
.get()),
536 mEventTarget(new ThreadEventTarget(
537 mEvents
.get(), aMainThread
== MAIN_THREAD
, aOptions
.blockDispatch
)),
538 mOutstandingShutdownContexts(0),
539 mShutdownContext(nullptr),
540 mScriptObserver(nullptr),
541 mThreadName("<uninitialized>"),
542 mStackSize(aOptions
.stackSize
),
543 mNestedEventLoopDepth(0),
544 mShutdownRequired(false),
545 mPriority(PRIORITY_NORMAL
),
546 mIsMainThread(aMainThread
== MAIN_THREAD
),
547 mUseHangMonitor(aMainThread
== MAIN_THREAD
),
548 mIsUiThread(aOptions
.isUiThread
),
549 mIsAPoolThreadFree(nullptr),
551 mPerformanceCounterState(mNestedEventLoopDepth
, mIsMainThread
,
552 aOptions
.longTaskLength
) {
553 #if !(defined(XP_WIN) || defined(XP_MACOSX))
554 MOZ_ASSERT(!mIsUiThread
,
555 "Non-main UI threads are only supported on Windows and macOS");
558 MOZ_ASSERT(!mIsUiThread
,
559 "Setting isUIThread is not supported for main threads");
560 mozilla::TaskController::Get()->SetPerformanceCounterState(
561 &mPerformanceCounterState
);
567 mEventTarget(nullptr),
568 mOutstandingShutdownContexts(0),
569 mShutdownContext(nullptr),
570 mScriptObserver(nullptr),
571 mThreadName("<uninitialized>"),
573 mNestedEventLoopDepth(0),
574 mShutdownRequired(false),
575 mPriority(PRIORITY_NORMAL
),
576 mIsMainThread(false),
577 mUseHangMonitor(false),
580 mPerformanceCounterState(mNestedEventLoopDepth
) {
581 MOZ_ASSERT(!NS_IsMainThread());
584 nsThread::~nsThread() {
585 NS_ASSERTION(mOutstandingShutdownContexts
== 0,
586 "shouldn't be waiting on other threads to shutdown");
588 MaybeRemoveFromThreadList();
591 nsresult
nsThread::Init(const nsACString
& aName
) {
593 MOZ_ASSERT(mEventTarget
);
594 MOZ_ASSERT(!mThread
);
596 SetThreadNameInternal(aName
);
598 PRThread
* thread
= nullptr;
600 nsThreadManager
& tm
= nsThreadManager::get();
602 OffTheBooksMutexAutoLock
lock(tm
.ThreadListMutex());
603 if (!tm
.AllowNewXPCOMThreadsLocked()) {
604 return NS_ERROR_NOT_INITIALIZED
;
607 // We need to fully start the thread while holding the thread list lock, as
608 // the next acquire of the lock could try to shut down this thread (e.g.
609 // during xpcom shutdown), which would hang if `PR_CreateThread` failed.
611 UniquePtr
<ThreadInitData
> initData(
612 new ThreadInitData
{this, nsCString(aName
)});
614 // ThreadFunc is responsible for setting mThread
615 if (!(thread
= PR_CreateThread(PR_USER_THREAD
, ThreadFunc
, initData
.get(),
616 PR_PRIORITY_NORMAL
, PR_GLOBAL_THREAD
,
617 PR_JOINABLE_THREAD
, mStackSize
))) {
618 return NS_ERROR_OUT_OF_MEMORY
;
621 // The created thread now owns initData, so release our ownership of it.
622 Unused
<< initData
.release();
624 // The thread has successfully started, so we can mark it as requiring
625 // shutdown & add it to the thread list.
626 mShutdownRequired
= true;
627 tm
.ThreadList().insertBack(this);
630 // Note: we set these both here and inside ThreadFunc, to what should be
631 // the same value. This is because calls within ThreadFunc need these values
632 // to be set, and our callers need these values to be set.
633 DebugOnly
<PRThread
*> prev
= mThread
.exchange(thread
);
634 MOZ_ASSERT(!prev
|| prev
== thread
);
636 mEventTarget
->SetCurrentThread(thread
);
640 nsresult
nsThread::InitCurrentThread() {
641 mThread
= PR_GetCurrentThread();
643 nsThreadManager
& tm
= nsThreadManager::get();
645 OffTheBooksMutexAutoLock
lock(tm
.ThreadListMutex());
646 // NOTE: We don't check AllowNewXPCOMThreads here, as threads initialized
647 // this way do not need shutdown, so are OK to create after nsThreadManager
648 // shutdown. In addition, the main thread is initialized this way, which
649 // happens before AllowNewXPCOMThreads begins to return true.
650 tm
.ThreadList().insertBack(this);
653 SetupCurrentThreadForChaosMode();
656 tm
.RegisterCurrentThread(*this);
660 void nsThread::GetThreadName(nsACString
& aNameBuffer
) {
661 auto lock
= mThreadName
.Lock();
662 aNameBuffer
= lock
.ref();
665 void nsThread::SetThreadNameInternal(const nsACString
& aName
) {
666 auto lock
= mThreadName
.Lock();
670 //-----------------------------------------------------------------------------
674 nsThread::DispatchFromScript(nsIRunnable
* aEvent
, uint32_t aFlags
) {
675 MOZ_ASSERT(mEventTarget
);
676 NS_ENSURE_TRUE(mEventTarget
, NS_ERROR_NOT_IMPLEMENTED
);
678 nsCOMPtr
<nsIRunnable
> event(aEvent
);
679 return mEventTarget
->Dispatch(event
.forget(), aFlags
);
683 nsThread::Dispatch(already_AddRefed
<nsIRunnable
> aEvent
, uint32_t aFlags
) {
684 MOZ_ASSERT(mEventTarget
);
685 NS_ENSURE_TRUE(mEventTarget
, NS_ERROR_NOT_IMPLEMENTED
);
687 LOG(("THRD(%p) Dispatch [%p %x]\n", this, /* XXX aEvent */ nullptr, aFlags
));
689 return mEventTarget
->Dispatch(std::move(aEvent
), aFlags
);
693 nsThread::DelayedDispatch(already_AddRefed
<nsIRunnable
> aEvent
,
695 MOZ_ASSERT(mEventTarget
);
696 NS_ENSURE_TRUE(mEventTarget
, NS_ERROR_NOT_IMPLEMENTED
);
698 return mEventTarget
->DelayedDispatch(std::move(aEvent
), aDelayMs
);
702 nsThread::RegisterShutdownTask(nsITargetShutdownTask
* aTask
) {
703 MOZ_ASSERT(mEventTarget
);
704 NS_ENSURE_TRUE(mEventTarget
, NS_ERROR_NOT_IMPLEMENTED
);
706 return mEventTarget
->RegisterShutdownTask(aTask
);
710 nsThread::UnregisterShutdownTask(nsITargetShutdownTask
* aTask
) {
711 MOZ_ASSERT(mEventTarget
);
712 NS_ENSURE_TRUE(mEventTarget
, NS_ERROR_NOT_IMPLEMENTED
);
714 return mEventTarget
->UnregisterShutdownTask(aTask
);
718 nsThread::GetRunningEventDelay(TimeDuration
* aDelay
, TimeStamp
* aStart
) {
719 if (mIsAPoolThreadFree
&& *mIsAPoolThreadFree
) {
720 // if there are unstarted threads in the pool, a new event to the
721 // pool would not be delayed at all (beyond thread start time)
722 *aDelay
= TimeDuration();
723 *aStart
= TimeStamp();
725 *aDelay
= mLastEventDelay
;
726 *aStart
= mLastEventStart
;
732 nsThread::SetRunningEventDelay(TimeDuration aDelay
, TimeStamp aStart
) {
733 mLastEventDelay
= aDelay
;
734 mLastEventStart
= aStart
;
739 nsThread::IsOnCurrentThread(bool* aResult
) {
741 return mEventTarget
->IsOnCurrentThread(aResult
);
743 *aResult
= PR_GetCurrentThread() == mThread
;
748 nsThread::IsOnCurrentThreadInfallible() {
749 // This method is only going to be called if `mThread` is null, which
750 // only happens when the thread has exited the event loop. Therefore, when
751 // we are called, we can never be on this thread.
755 //-----------------------------------------------------------------------------
759 nsThread::GetPRThread(PRThread
** aResult
) {
760 PRThread
* thread
= mThread
; // atomic load
762 return thread
? NS_OK
: NS_ERROR_NOT_AVAILABLE
;
766 nsThread::GetCanInvokeJS(bool* aResult
) {
767 *aResult
= mCanInvokeJS
;
772 nsThread::SetCanInvokeJS(bool aCanInvokeJS
) {
773 mCanInvokeJS
= aCanInvokeJS
;
778 nsThread::GetLastLongTaskEnd(TimeStamp
* _retval
) {
779 *_retval
= mPerformanceCounterState
.LastLongTaskEnd();
784 nsThread::GetLastLongNonIdleTaskEnd(TimeStamp
* _retval
) {
785 *_retval
= mPerformanceCounterState
.LastLongNonIdleTaskEnd();
790 nsThread::AsyncShutdown() {
791 LOG(("THRD(%p) async shutdown\n", this));
793 nsCOMPtr
<nsIThreadShutdown
> shutdown
;
794 BeginShutdown(getter_AddRefs(shutdown
));
799 nsThread::BeginShutdown(nsIThreadShutdown
** aShutdown
) {
800 LOG(("THRD(%p) begin shutdown\n", this));
803 MOZ_ASSERT(mEventTarget
);
804 MOZ_ASSERT(mThread
!= PR_GetCurrentThread());
805 if (NS_WARN_IF(mThread
== PR_GetCurrentThread())) {
806 return NS_ERROR_UNEXPECTED
;
809 // Prevent multiple calls to this method.
810 if (!mShutdownRequired
.compareExchange(true, false)) {
811 return NS_ERROR_UNEXPECTED
;
815 RefPtr
<nsThread
> currentThread
= nsThreadManager::get().GetCurrentThread();
817 MOZ_DIAGNOSTIC_ASSERT(currentThread
->EventQueue(),
818 "Shutdown() may only be called from an XPCOM thread");
820 // Allocate a shutdown context, and record that we're waiting for it.
821 RefPtr
<nsThreadShutdownContext
> context
=
822 new nsThreadShutdownContext(WrapNotNull(this), currentThread
);
824 ++currentThread
->mOutstandingShutdownContexts
;
825 nsCOMPtr
<nsIRunnable
> clearOutstanding
= NS_NewRunnableFunction(
826 "nsThread::ClearOutstandingShutdownContext",
827 [currentThread
] { --currentThread
->mOutstandingShutdownContexts
; });
828 context
->OnCompletion(clearOutstanding
);
830 // Set mShutdownContext and wake up the thread in case it is waiting for
831 // events to process.
832 nsCOMPtr
<nsIRunnable
> event
=
833 new nsThreadShutdownEvent(WrapNotNull(this), WrapNotNull(context
));
834 if (!mEvents
->PutEvent(event
.forget(), EventQueuePriority::Normal
)) {
835 // We do not expect this to happen. Let's collect some diagnostics.
836 nsAutoCString threadName
;
837 GetThreadName(threadName
);
838 MOZ_CRASH_UNSAFE_PRINTF("Attempt to shutdown an already dead thread: %s",
842 // We could still end up with other events being added after the shutdown
843 // task, but that's okay because we process pending events in ThreadFunc
844 // after setting mShutdownContext just before exiting.
845 context
.forget(aShutdown
);
849 void nsThread::ShutdownComplete(NotNull
<nsThreadShutdownContext
*> aContext
) {
851 MOZ_ASSERT(mEventTarget
);
852 MOZ_ASSERT(aContext
->mTerminatingThread
== this);
854 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
856 MutexAutoLock
lock(aContext
->mJoiningThreadMutex
);
858 // StopWaitingAndLeakThread is explicitely meant to not cause a
859 // nsThreadShutdownAckEvent on the joining thread, which is the only
860 // caller of ShutdownComplete.
861 MOZ_DIAGNOSTIC_ASSERT(!aContext
->mThreadLeaked
);
865 MaybeRemoveFromThreadList();
867 // Now, it should be safe to join without fear of dead-locking.
868 PR_JoinThread(aContext
->mTerminatingPRThread
);
869 MOZ_ASSERT(!mThread
);
872 nsCOMPtr
<nsIThreadObserver
> obs
= mEvents
->GetObserver();
873 MOZ_ASSERT(!obs
, "Should have been cleared at shutdown!");
876 aContext
->MarkCompleted();
879 void nsThread::WaitForAllAsynchronousShutdowns() {
880 // This is the motivating example for why SpinEventLoopUntil
881 // has the template parameter we are providing here.
882 SpinEventLoopUntil
<ProcessFailureBehavior::IgnoreAndContinue
>(
883 "nsThread::WaitForAllAsynchronousShutdowns"_ns
,
884 [&]() { return mOutstandingShutdownContexts
== 0; }, this);
888 nsThread::Shutdown() {
889 LOG(("THRD(%p) sync shutdown\n", this));
891 nsCOMPtr
<nsIThreadShutdown
> context
;
892 nsresult rv
= BeginShutdown(getter_AddRefs(context
));
894 return NS_OK
; // The thread has already shut down.
897 // If we are going to hang here we want to see the thread's name
898 nsAutoCString threadName
;
899 GetThreadName(threadName
);
901 // Process events on the current thread until we receive a shutdown ACK.
902 // Allows waiting; ensure no locks are held that would deadlock us!
903 SpinEventLoopUntil("nsThread::Shutdown: "_ns
+ threadName
,
904 [&]() { return context
->GetCompleted(); });
910 nsThread::HasPendingEvents(bool* aResult
) {
911 if (NS_WARN_IF(PR_GetCurrentThread() != mThread
)) {
912 return NS_ERROR_NOT_SAME_THREAD
;
916 *aResult
= TaskController::Get()->HasMainThreadPendingTasks();
918 *aResult
= mEvents
->HasPendingEvent();
924 nsThread::HasPendingHighPriorityEvents(bool* aResult
) {
925 if (NS_WARN_IF(PR_GetCurrentThread() != mThread
)) {
926 return NS_ERROR_NOT_SAME_THREAD
;
929 // This function appears to never be called anymore.
935 nsThread::DispatchToQueue(already_AddRefed
<nsIRunnable
> aEvent
,
936 EventQueuePriority aQueue
) {
937 nsCOMPtr
<nsIRunnable
> event
= aEvent
;
939 if (NS_WARN_IF(!event
)) {
940 return NS_ERROR_INVALID_ARG
;
943 if (!mEvents
->PutEvent(event
.forget(), aQueue
)) {
945 "An idle event was posted to a thread that will never run it "
947 return NS_ERROR_UNEXPECTED
;
953 NS_IMETHODIMP
nsThread::SetThreadQoS(nsIThread::QoSPriority aPriority
) {
954 if (!StaticPrefs::threads_use_low_power_enabled()) {
957 // The approach here is to have a thread set itself for its QoS level,
958 // so we assert if we aren't on the current thread.
959 MOZ_ASSERT(IsOnCurrentThread(), "Can only change the current thread's QoS");
961 #if defined(XP_MACOSX)
962 // Only arm64 macs may possess heterogeneous cores. On these, we can tell
963 // a thread to set its own QoS status. On intel macs things should behave
964 // normally, and the OS will ignore the QoS state of the thread.
965 if (aPriority
== nsIThread::QOS_PRIORITY_LOW
) {
966 pthread_set_qos_class_self_np(QOS_CLASS_BACKGROUND
, 0);
967 } else if (NS_IsMainThread()) {
968 // MacOS documentation specifies that a main thread should be initialized at
969 // the USER_INTERACTIVE priority, so when we restore thread priorities the
970 // main thread should be setting itself to this.
971 pthread_set_qos_class_self_np(QOS_CLASS_USER_INTERACTIVE
, 0);
973 pthread_set_qos_class_self_np(QOS_CLASS_DEFAULT
, 0);
976 // Do nothing if an OS-specific implementation is unavailable.
981 void canary_alarm_handler(int signum
);
984 // XXX ToDo: support nested loops
987 if (sCanaryOutputFD
> 0 && EventLatencyIsImportant()) {
988 signal(SIGALRM
, canary_alarm_handler
);
994 if (sCanaryOutputFD
!= 0 && EventLatencyIsImportant()) {
999 static bool EventLatencyIsImportant() {
1000 return NS_IsMainThread() && XRE_IsParentProcess();
1004 void canary_alarm_handler(int signum
) {
1006 const char msg
[29] = "event took too long to run:\n";
1007 // use write to be safe in the signal handler
1008 write(sCanaryOutputFD
, msg
, sizeof(msg
));
1009 backtrace_symbols_fd(array
, backtrace(array
, 30), sCanaryOutputFD
);
1014 #define NOTIFY_EVENT_OBSERVERS(observers_, func_, params_) \
1016 if (!observers_.IsEmpty()) { \
1017 for (nsCOMPtr<nsIThreadObserver> obs_ : observers_.ForwardRange()) { \
1018 obs_->func_ params_; \
1023 size_t nsThread::ShallowSizeOfIncludingThis(
1024 mozilla::MallocSizeOf aMallocSizeOf
) const {
1026 if (mShutdownContext
) {
1027 n
+= aMallocSizeOf(mShutdownContext
);
1029 return aMallocSizeOf(this) + aMallocSizeOf(mThread
) + n
;
1032 size_t nsThread::SizeOfEventQueues(mozilla::MallocSizeOf aMallocSizeOf
) const {
1035 // The size of mEvents is reported by mEventTarget.
1036 n
+= mEventTarget
->SizeOfIncludingThis(aMallocSizeOf
);
1041 size_t nsThread::SizeOfIncludingThis(
1042 mozilla::MallocSizeOf aMallocSizeOf
) const {
1043 return ShallowSizeOfIncludingThis(aMallocSizeOf
) +
1044 SizeOfEventQueues(aMallocSizeOf
);
1048 nsThread::ProcessNextEvent(bool aMayWait
, bool* aResult
) {
1049 MOZ_ASSERT(mEvents
);
1050 NS_ENSURE_TRUE(mEvents
, NS_ERROR_NOT_IMPLEMENTED
);
1052 LOG(("THRD(%p) ProcessNextEvent [%u %u]\n", this, aMayWait
,
1053 mNestedEventLoopDepth
));
1055 if (NS_WARN_IF(PR_GetCurrentThread() != mThread
)) {
1056 return NS_ERROR_NOT_SAME_THREAD
;
1059 // The toplevel event loop normally blocks waiting for the next event, but
1060 // if we're trying to shut this thread down, we must exit the event loop
1061 // when the event queue is empty. This only applys to the toplevel event
1062 // loop! Nested event loops (e.g. during sync dispatch) are waiting for
1063 // some state change and must be able to block even if something has
1064 // requested shutdown of the thread. Otherwise we'll just busywait as we
1065 // endlessly look for an event, fail to find one, and repeat the nested
1066 // event loop since its state change hasn't happened yet.
1067 bool reallyWait
= aMayWait
&& (mNestedEventLoopDepth
> 0 || !ShuttingDown());
1069 Maybe
<dom::AutoNoJSAPI
> noJSAPI
;
1071 if (mUseHangMonitor
&& reallyWait
) {
1072 BackgroundHangMonitor().NotifyWait();
1075 if (mIsMainThread
) {
1076 DoMainThreadSpecificProcessing();
1080 BlockingResourceBase::AssertSafeToProcessEventLoop();
1083 ++mNestedEventLoopDepth
;
1085 // We only want to create an AutoNoJSAPI on threads that actually do DOM
1086 // stuff (including workers). Those are exactly the threads that have an
1088 bool callScriptObserver
= !!mScriptObserver
;
1089 if (callScriptObserver
) {
1091 mScriptObserver
->BeforeProcessTask(reallyWait
);
1096 nsCOMPtr
<nsIThreadObserver
> obs
= mEvents
->GetObserverOnThread();
1098 obs
->OnProcessNextEvent(this, reallyWait
);
1101 NOTIFY_EVENT_OBSERVERS(EventQueue()->EventObservers(), OnProcessNextEvent
,
1102 (this, reallyWait
));
1109 nsresult rv
= NS_OK
;
1112 // Scope for |event| to make sure that its destructor fires while
1113 // mNestedEventLoopDepth has been incremented, since that destructor can
1115 nsCOMPtr
<nsIRunnable
> event
;
1116 bool usingTaskController
= mIsMainThread
;
1117 if (usingTaskController
) {
1118 event
= TaskController::Get()->GetRunnableForMTTask(reallyWait
);
1120 event
= mEvents
->GetEvent(reallyWait
, &mLastEventDelay
);
1123 *aResult
= (event
.get() != nullptr);
1126 LOG(("THRD(%p) running [%p]\n", this, event
.get()));
1128 Maybe
<LogRunnable::Run
> log
;
1130 if (!usingTaskController
) {
1134 // Delay event processing to encourage whoever dispatched this event
1136 DelayForChaosMode(ChaosFeature::TaskRunning
, 1000);
1138 mozilla::TimeStamp now
= mozilla::TimeStamp::Now();
1140 if (mUseHangMonitor
) {
1141 BackgroundHangMonitor().NotifyActivity();
1144 Maybe
<PerformanceCounterState::Snapshot
> snapshot
;
1145 if (!usingTaskController
) {
1146 snapshot
.emplace(mPerformanceCounterState
.RunnableWillRun(now
, false));
1149 mLastEventStart
= now
;
1151 if (!usingTaskController
) {
1152 AUTO_PROFILE_FOLLOWING_RUNNABLE(event
);
1155 // Avoid generating "Runnable" profiler markers for the
1156 // "TaskController::ExecutePendingMTTasks" runnables created
1157 // by TaskController, which already adds "Runnable" markers
1158 // when executing tasks.
1162 if (usingTaskController
) {
1163 *aResult
= TaskController::Get()->MTTaskRunnableProcessedTask();
1165 mPerformanceCounterState
.RunnableDidRun(EmptyCString(),
1166 std::move(snapshot
.ref()));
1169 // To cover the event's destructor code inside the LogRunnable span.
1172 mLastEventDelay
= TimeDuration();
1173 mLastEventStart
= TimeStamp();
1175 MOZ_ASSERT(ShuttingDown(),
1176 "This should only happen when shutting down");
1177 rv
= NS_ERROR_UNEXPECTED
;
1184 NOTIFY_EVENT_OBSERVERS(EventQueue()->EventObservers(), AfterProcessNextEvent
,
1188 obs
->AfterProcessNextEvent(this, *aResult
);
1191 // In case some EventObserver dispatched some direct tasks; process them
1195 if (callScriptObserver
) {
1196 if (mScriptObserver
) {
1197 mScriptObserver
->AfterProcessTask(mNestedEventLoopDepth
);
1202 --mNestedEventLoopDepth
;
1207 //-----------------------------------------------------------------------------
1208 // nsISupportsPriority
1211 nsThread::GetPriority(int32_t* aPriority
) {
1212 *aPriority
= mPriority
;
1217 nsThread::SetPriority(int32_t aPriority
) {
1218 if (NS_WARN_IF(!mThread
)) {
1219 return NS_ERROR_NOT_INITIALIZED
;
1222 // NSPR defines the following four thread priorities:
1224 // PR_PRIORITY_NORMAL
1226 // PR_PRIORITY_URGENT
1227 // We map the priority values defined on nsISupportsPriority to these
1230 mPriority
= aPriority
;
1232 PRThreadPriority pri
;
1233 if (mPriority
<= PRIORITY_HIGHEST
) {
1234 pri
= PR_PRIORITY_URGENT
;
1235 } else if (mPriority
< PRIORITY_NORMAL
) {
1236 pri
= PR_PRIORITY_HIGH
;
1237 } else if (mPriority
> PRIORITY_NORMAL
) {
1238 pri
= PR_PRIORITY_LOW
;
1240 pri
= PR_PRIORITY_NORMAL
;
1242 // If chaos mode is active, retain the randomly chosen priority
1243 if (!ChaosMode::isActive(ChaosFeature::ThreadScheduling
)) {
1244 PR_SetThreadPriority(mThread
, pri
);
1251 nsThread::AdjustPriority(int32_t aDelta
) {
1252 return SetPriority(mPriority
+ aDelta
);
1255 //-----------------------------------------------------------------------------
1256 // nsIThreadInternal
1259 nsThread::GetObserver(nsIThreadObserver
** aObs
) {
1260 MOZ_ASSERT(mEvents
);
1261 NS_ENSURE_TRUE(mEvents
, NS_ERROR_NOT_IMPLEMENTED
);
1263 nsCOMPtr
<nsIThreadObserver
> obs
= mEvents
->GetObserver();
1269 nsThread::SetObserver(nsIThreadObserver
* aObs
) {
1270 MOZ_ASSERT(mEvents
);
1271 NS_ENSURE_TRUE(mEvents
, NS_ERROR_NOT_IMPLEMENTED
);
1273 if (NS_WARN_IF(PR_GetCurrentThread() != mThread
)) {
1274 return NS_ERROR_NOT_SAME_THREAD
;
1277 mEvents
->SetObserver(aObs
);
1281 uint32_t nsThread::RecursionDepth() const {
1282 MOZ_ASSERT(PR_GetCurrentThread() == mThread
);
1283 return mNestedEventLoopDepth
;
1287 nsThread::AddObserver(nsIThreadObserver
* aObserver
) {
1288 MOZ_ASSERT(mEvents
);
1289 NS_ENSURE_TRUE(mEvents
, NS_ERROR_NOT_IMPLEMENTED
);
1291 if (NS_WARN_IF(!aObserver
)) {
1292 return NS_ERROR_INVALID_ARG
;
1294 if (NS_WARN_IF(PR_GetCurrentThread() != mThread
)) {
1295 return NS_ERROR_NOT_SAME_THREAD
;
1298 EventQueue()->AddObserver(aObserver
);
1304 nsThread::RemoveObserver(nsIThreadObserver
* aObserver
) {
1305 MOZ_ASSERT(mEvents
);
1306 NS_ENSURE_TRUE(mEvents
, NS_ERROR_NOT_IMPLEMENTED
);
1308 if (NS_WARN_IF(PR_GetCurrentThread() != mThread
)) {
1309 return NS_ERROR_NOT_SAME_THREAD
;
1312 EventQueue()->RemoveObserver(aObserver
);
1317 void nsThread::SetScriptObserver(
1318 mozilla::CycleCollectedJSContext
* aScriptObserver
) {
1319 if (!aScriptObserver
) {
1320 mScriptObserver
= nullptr;
1324 MOZ_ASSERT(!mScriptObserver
);
1325 mScriptObserver
= aScriptObserver
;
1328 void NS_DispatchMemoryPressure();
1330 void nsThread::DoMainThreadSpecificProcessing() const {
1331 MOZ_ASSERT(mIsMainThread
);
1335 // Fire a memory pressure notification, if one is pending.
1336 if (!ShuttingDown()) {
1337 NS_DispatchMemoryPressure();
1341 //-----------------------------------------------------------------------------
1342 // nsIDirectTaskDispatcher
1345 nsThread::DispatchDirectTask(already_AddRefed
<nsIRunnable
> aEvent
) {
1346 if (!IsOnCurrentThread()) {
1347 return NS_ERROR_FAILURE
;
1349 mDirectTasks
.AddTask(std::move(aEvent
));
1353 NS_IMETHODIMP
nsThread::DrainDirectTasks() {
1354 if (!IsOnCurrentThread()) {
1355 return NS_ERROR_FAILURE
;
1357 mDirectTasks
.DrainTasks();
1361 NS_IMETHODIMP
nsThread::HaveDirectTasks(bool* aValue
) {
1362 if (!IsOnCurrentThread()) {
1363 return NS_ERROR_FAILURE
;
1366 *aValue
= mDirectTasks
.HaveTasks();
1370 NS_IMPL_ISUPPORTS(nsThreadShutdownContext
, nsIThreadShutdown
)
1373 nsThreadShutdownContext::OnCompletion(nsIRunnable
* aEvent
) {
1377 mCompletionCallbacks
.AppendElement(aEvent
);
1383 nsThreadShutdownContext::GetCompleted(bool* aCompleted
) {
1384 *aCompleted
= mCompleted
;
1389 nsThreadShutdownContext::StopWaitingAndLeakThread() {
1390 // Take the joining thread from `mJoiningThread` so that the terminating
1391 // thread won't try to dispatch nsThreadShutdownAckEvent to us anymore.
1392 RefPtr
<nsThread
> joiningThread
;
1394 MutexAutoLock
lock(mJoiningThreadMutex
);
1395 if (!mJoiningThread
) {
1396 // Shutdown is already being resolved, so there's nothing for us to do.
1397 return NS_ERROR_NOT_AVAILABLE
;
1399 joiningThread
= mJoiningThread
.forget();
1400 mThreadLeaked
= true;
1403 MOZ_DIAGNOSTIC_ASSERT(joiningThread
->IsOnCurrentThread());
1410 void nsThreadShutdownContext::MarkCompleted() {
1411 MOZ_ASSERT(!mCompleted
);
1413 nsTArray
<nsCOMPtr
<nsIRunnable
>> callbacks(std::move(mCompletionCallbacks
));
1414 for (auto& callback
: callbacks
) {
1420 PerformanceCounterState::Snapshot
PerformanceCounterState::RunnableWillRun(
1421 TimeStamp aNow
, bool aIsIdleRunnable
) {
1422 if (mIsMainThread
&& IsNestedRunnable()) {
1423 // Flush out any accumulated time that should be accounted to the
1424 // current runnable before we start running a nested runnable. Don't
1425 // do this for non-mainthread threads that may be running their own
1426 // event loops, like SocketThread.
1427 MaybeReportAccumulatedTime("nested runnable"_ns
, aNow
);
1430 Snapshot
snapshot(mCurrentEventLoopDepth
, mCurrentRunnableIsIdleRunnable
);
1432 mCurrentEventLoopDepth
= mNestedEventLoopDepth
;
1433 mCurrentRunnableIsIdleRunnable
= aIsIdleRunnable
;
1434 mCurrentTimeSliceStart
= aNow
;
1439 void PerformanceCounterState::RunnableDidRun(const nsCString
& aName
,
1440 Snapshot
&& aSnapshot
) {
1441 // First thing: Restore our mCurrentEventLoopDepth so we can use
1442 // IsNestedRunnable().
1443 mCurrentEventLoopDepth
= aSnapshot
.mOldEventLoopDepth
;
1445 // We may not need the current timestamp; don't bother computing it if we
1448 if (mLongTaskLength
.isSome() || IsNestedRunnable()) {
1449 now
= TimeStamp::Now();
1451 if (mLongTaskLength
.isSome()) {
1452 MaybeReportAccumulatedTime(aName
, now
);
1455 // And now restore the rest of our state.
1456 mCurrentRunnableIsIdleRunnable
= aSnapshot
.mOldIsIdleRunnable
;
1457 if (IsNestedRunnable()) {
1458 // Reset mCurrentTimeSliceStart to right now, so our parent runnable's
1459 // next slice can be properly accounted for.
1460 mCurrentTimeSliceStart
= now
;
1462 // We are done at the outermost level; we are no longer in a timeslice.
1463 mCurrentTimeSliceStart
= TimeStamp();
1467 void PerformanceCounterState::MaybeReportAccumulatedTime(const nsCString
& aName
,
1469 MOZ_ASSERT(mCurrentTimeSliceStart
,
1470 "How did we get here if we're not in a timeslice?");
1471 if (!mLongTaskLength
.isSome()) {
1475 TimeDuration duration
= aNow
- mCurrentTimeSliceStart
;
1476 #ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
1477 if (mIsMainThread
&& duration
.ToMilliseconds() > LONGTASK_TELEMETRY_MS
) {
1478 Telemetry::Accumulate(Telemetry::EVENT_LONGTASK
, aName
,
1479 duration
.ToMilliseconds());
1483 // Long tasks only matter on the main thread.
1484 if (duration
.ToMilliseconds() >= mLongTaskLength
.value()) {
1485 // Idle events (gc...) don't *really* count here
1486 if (!mCurrentRunnableIsIdleRunnable
) {
1487 mLastLongNonIdleTaskEnd
= aNow
;
1489 mLastLongTaskEnd
= aNow
;
1491 if (profiler_thread_is_being_profiled_for_markers()) {
1492 struct LongTaskMarker
{
1493 static constexpr Span
<const char> MarkerTypeName() {
1494 return MakeStringSpan("MainThreadLongTask");
1496 static void StreamJSONMarkerData(
1497 baseprofiler::SpliceableJSONWriter
& aWriter
) {
1498 aWriter
.StringProperty("category", "LongTask");
1500 static MarkerSchema
MarkerTypeDisplay() {
1501 using MS
= MarkerSchema
;
1502 MS schema
{MS::Location::MarkerChart
, MS::Location::MarkerTable
};
1503 schema
.AddKeyLabelFormatSearchable("category", "Type",
1505 MS::Searchable::Searchable
);
1510 profiler_add_marker(mCurrentRunnableIsIdleRunnable
1511 ? ProfilerString8View("LongIdleTask")
1512 : ProfilerString8View("LongTask"),
1513 geckoprofiler::category::OTHER
,
1514 MarkerTiming::Interval(mCurrentTimeSliceStart
, aNow
),
1520 } // namespace mozilla