Bug 1867190 - Initialise the PHC allocate delay later r=glandium
[gecko.git] / xpcom / threads / nsThread.cpp
blob98542e943801ee267b259fabb03e5be1fe566e6a
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/. */
7 #include "nsThread.h"
9 #include "base/message_loop.h"
10 #include "base/platform_thread.h"
12 // Chromium's logging can sometimes leak through...
13 #ifdef LOG
14 # undef LOG
15 #endif
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 "ThreadEventQueue.h"
51 #include "ThreadEventTarget.h"
52 #include "ThreadDelay.h"
54 #include <limits>
56 #ifdef XP_LINUX
57 # ifdef __GLIBC__
58 # include <gnu/libc-version.h>
59 # endif
60 # include <sys/mman.h>
61 # include <sys/time.h>
62 # include <sys/resource.h>
63 # include <sched.h>
64 # include <stdio.h>
65 #endif
67 #ifdef XP_WIN
68 # include "mozilla/DynamicallyLinkedFunctionPtr.h"
70 # include <winbase.h>
72 using GetCurrentThreadStackLimitsFn = void(WINAPI*)(PULONG_PTR LowLimit,
73 PULONG_PTR HighLimit);
74 #endif
76 #define HAVE_UALARM \
77 _BSD_SOURCE || \
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
83 #endif
85 #ifdef XP_MACOSX
86 # include <mach/mach.h>
87 # include <mach/thread_policy.h>
88 # include <sys/qos.h>
89 #endif
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
99 using namespace mozilla;
101 extern void InitThreadLocalVariables();
103 static LazyLogModule sThreadLog("nsThread");
104 #ifdef LOG
105 # undef LOG
106 #endif
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;
115 #endif
117 //-----------------------------------------------------------------------------
118 // Because we do not have our own nsIFactory, we have to implement nsIClassInfo
119 // somewhat manually.
121 class nsThreadClassInfo : public nsIClassInfo {
122 public:
123 NS_DECL_ISUPPORTS_INHERITED // no mRefCnt
124 NS_DECL_NSICLASSINFO
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)
135 NS_IMETHODIMP
136 nsThreadClassInfo::GetInterfaces(nsTArray<nsIID>& aArray) {
137 return NS_CI_INTERFACE_GETTER_NAME(nsThread)(aArray);
140 NS_IMETHODIMP
141 nsThreadClassInfo::GetScriptableHelper(nsIXPCScriptable** aResult) {
142 *aResult = nullptr;
143 return NS_OK;
146 NS_IMETHODIMP
147 nsThreadClassInfo::GetContractID(nsACString& aResult) {
148 aResult.SetIsVoid(true);
149 return NS_OK;
152 NS_IMETHODIMP
153 nsThreadClassInfo::GetClassDescription(nsACString& aResult) {
154 aResult.SetIsVoid(true);
155 return NS_OK;
158 NS_IMETHODIMP
159 nsThreadClassInfo::GetClassID(nsCID** aResult) {
160 *aResult = nullptr;
161 return NS_OK;
164 NS_IMETHODIMP
165 nsThreadClassInfo::GetFlags(uint32_t* aResult) {
166 *aResult = THREADSAFE;
167 return NS_OK;
170 NS_IMETHODIMP
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);
190 } else
191 NS_INTERFACE_MAP_END
192 NS_IMPL_CI_INTERFACE_GETTER(nsThread, nsIThread, nsIThreadInternal,
193 nsIEventTarget, nsISerialEventTarget,
194 nsISupportsPriority)
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 {
203 public:
204 explicit nsThreadShutdownAckEvent(NotNull<nsThreadShutdownContext*> aCtx)
205 : CancelableRunnable("nsThreadShutdownAckEvent"),
206 mShutdownContext(aCtx) {}
207 NS_IMETHOD Run() override {
208 mShutdownContext->mTerminatingThread->ShutdownComplete(mShutdownContext);
209 return NS_OK;
211 nsresult Cancel() override { return Run(); }
213 private:
214 virtual ~nsThreadShutdownAckEvent() = default;
216 NotNull<RefPtr<nsThreadShutdownContext>> mShutdownContext;
219 // This event is responsible for setting mShutdownContext
220 class nsThreadShutdownEvent : public Runnable {
221 public:
222 nsThreadShutdownEvent(NotNull<nsThread*> aThr,
223 NotNull<nsThreadShutdownContext*> aCtx)
224 : Runnable("nsThreadShutdownEvent"),
225 mThread(aThr),
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());
237 #endif
238 return NS_OK;
241 private:
242 NotNull<RefPtr<nsThread>> mThread;
243 NotNull<RefPtr<nsThreadShutdownContext>> mShutdownContext;
246 //-----------------------------------------------------------------------------
248 static void SetThreadAffinity(unsigned int cpu) {
249 #ifdef HAVE_SCHED_SETAFFINITY
250 cpu_set_t cpus;
251 CPU_ZERO(&cpus);
252 CPU_SET(cpu, &cpus);
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) !=
270 (DWORD)-1);
271 #endif
274 static void SetupCurrentThreadForChaosMode() {
275 if (!ChaosMode::isActive(ChaosFeature::ThreadScheduling)) {
276 return;
279 #ifdef XP_LINUX
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));
293 #else
294 // We should set the affinity here but NSPR doesn't provide a way to expose
295 // it.
296 uint32_t priority = ChaosMode::randomUint32LessThan(PR_PRIORITY_LAST + 1);
297 PR_SetThreadPriority(PR_GetCurrentThread(), PRThreadPriority(priority));
298 #endif
300 // Force half the threads to CPU 0 so they compete for CPU
301 if (ChaosMode::randomUint32LessThan(2)) {
302 SetThreadAffinity(0);
306 namespace {
308 struct ThreadInitData {
309 RefPtr<nsThread> thread;
310 nsCString name;
313 } // namespace
315 void nsThread::MaybeRemoveFromThreadList() {
316 nsThreadManager& tm = nsThreadManager::get();
317 OffTheBooksMutexAutoLock mal(tm.ThreadListMutex());
318 if (isInList()) {
319 removeFrom(tm.ThreadList());
323 /*static*/
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());
343 self->InitCommon();
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.
360 MessageLoop loop(
361 #if defined(XP_WIN) || defined(XP_MACOSX)
362 self->mIsUiThread ? MessageLoop::TYPE_MOZILLA_NONMAINUITHREAD
363 : MessageLoop::TYPE_MOZILLA_NONMAINTHREAD,
364 #else
365 MessageLoop::TYPE_MOZILLA_NONMAINTHREAD,
366 #endif
367 self);
369 // Now, process incoming events...
370 loop.Run();
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.
385 while (true) {
386 // Check and see if we're waiting on any threads.
387 self->WaitForAllAsynchronousShutdowns();
389 if (self->mEvents->ShutdownIfNoPendingEvents()) {
390 break;
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);
420 if (joiningThread) {
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());
437 #endif
438 } else {
439 NS_WARNING(
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)
457 pthread_attr_t attr;
458 pthread_attr_init(&attr);
459 pthread_getattr_np(pthread_self(), &attr);
461 size_t stackSize;
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
468 // compiled against.
469 static bool sAdjustForGuardSize = ({
470 # ifdef __GLIBC__
471 unsigned major, minor;
472 sscanf(gnu_get_libc_version(), "%u.%u", &major, &minor) < 2 ||
473 major < 2 || (major == 2 && minor < 27);
474 # else
475 false;
476 # endif
478 if (sAdjustForGuardSize) {
479 size_t guardSize;
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
498 // it beforehand.
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;
519 #endif
522 InitThreadLocalVariables();
525 //-----------------------------------------------------------------------------
527 #ifdef MOZ_CANARY
528 int sCanaryOutputFD = -1;
529 #endif
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),
549 mCanInvokeJS(false),
550 #ifdef EARLY_BETA_OR_EARLIER
551 mLastWakeupCheckTime(TimeStamp::Now()),
552 #endif
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");
558 #endif
559 if (mIsMainThread) {
560 MOZ_ASSERT(!mIsUiThread,
561 "Setting isUIThread is not supported for main threads");
562 mozilla::TaskController::Get()->SetPerformanceCounterState(
563 &mPerformanceCounterState);
567 nsThread::nsThread()
568 : mEvents(nullptr),
569 mEventTarget(nullptr),
570 mOutstandingShutdownContexts(0),
571 mShutdownContext(nullptr),
572 mScriptObserver(nullptr),
573 mThreadName("<uninitialized>"),
574 mStackSize(0),
575 mNestedEventLoopDepth(0),
576 mShutdownRequired(false),
577 mPriority(PRIORITY_NORMAL),
578 mIsMainThread(false),
579 mUseHangMonitor(false),
580 mIsUiThread(false),
581 mCanInvokeJS(false),
582 #ifdef EARLY_BETA_OR_EARLIER
583 mLastWakeupCheckTime(TimeStamp::Now()),
584 #endif
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) {
597 MOZ_ASSERT(mEvents);
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);
642 return NS_OK;
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();
659 InitCommon();
661 tm.RegisterCurrentThread(*this);
662 return NS_OK;
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();
672 lock->Assign(aName);
675 //-----------------------------------------------------------------------------
676 // nsIEventTarget
678 NS_IMETHODIMP
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);
687 NS_IMETHODIMP
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);
697 NS_IMETHODIMP
698 nsThread::DelayedDispatch(already_AddRefed<nsIRunnable> aEvent,
699 uint32_t aDelayMs) {
700 MOZ_ASSERT(mEventTarget);
701 NS_ENSURE_TRUE(mEventTarget, NS_ERROR_NOT_IMPLEMENTED);
703 return mEventTarget->DelayedDispatch(std::move(aEvent), aDelayMs);
706 NS_IMETHODIMP
707 nsThread::RegisterShutdownTask(nsITargetShutdownTask* aTask) {
708 MOZ_ASSERT(mEventTarget);
709 NS_ENSURE_TRUE(mEventTarget, NS_ERROR_NOT_IMPLEMENTED);
711 return mEventTarget->RegisterShutdownTask(aTask);
714 NS_IMETHODIMP
715 nsThread::UnregisterShutdownTask(nsITargetShutdownTask* aTask) {
716 MOZ_ASSERT(mEventTarget);
717 NS_ENSURE_TRUE(mEventTarget, NS_ERROR_NOT_IMPLEMENTED);
719 return mEventTarget->UnregisterShutdownTask(aTask);
722 NS_IMETHODIMP
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();
729 } else {
730 *aDelay = mLastEventDelay;
731 *aStart = mLastEventStart;
733 return NS_OK;
736 NS_IMETHODIMP
737 nsThread::SetRunningEventDelay(TimeDuration aDelay, TimeStamp aStart) {
738 mLastEventDelay = aDelay;
739 mLastEventStart = aStart;
740 return NS_OK;
743 NS_IMETHODIMP
744 nsThread::IsOnCurrentThread(bool* aResult) {
745 if (mEventTarget) {
746 return mEventTarget->IsOnCurrentThread(aResult);
748 *aResult = PR_GetCurrentThread() == mThread;
749 return NS_OK;
752 NS_IMETHODIMP_(bool)
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.
757 return false;
760 //-----------------------------------------------------------------------------
761 // nsIThread
763 NS_IMETHODIMP
764 nsThread::GetPRThread(PRThread** aResult) {
765 PRThread* thread = mThread; // atomic load
766 *aResult = thread;
767 return thread ? NS_OK : NS_ERROR_NOT_AVAILABLE;
770 NS_IMETHODIMP
771 nsThread::GetCanInvokeJS(bool* aResult) {
772 *aResult = mCanInvokeJS;
773 return NS_OK;
776 NS_IMETHODIMP
777 nsThread::SetCanInvokeJS(bool aCanInvokeJS) {
778 mCanInvokeJS = aCanInvokeJS;
779 return NS_OK;
782 NS_IMETHODIMP
783 nsThread::GetLastLongTaskEnd(TimeStamp* _retval) {
784 *_retval = mPerformanceCounterState.LastLongTaskEnd();
785 return NS_OK;
788 NS_IMETHODIMP
789 nsThread::GetLastLongNonIdleTaskEnd(TimeStamp* _retval) {
790 *_retval = mPerformanceCounterState.LastLongNonIdleTaskEnd();
791 return NS_OK;
794 NS_IMETHODIMP
795 nsThread::SetNameForWakeupTelemetry(const nsACString& aName) {
796 #ifdef EARLY_BETA_OR_EARLIER
797 mNameForWakeupTelemetry = aName;
798 #endif
799 return NS_OK;
802 NS_IMETHODIMP
803 nsThread::AsyncShutdown() {
804 LOG(("THRD(%p) async shutdown\n", this));
806 nsCOMPtr<nsIThreadShutdown> shutdown;
807 BeginShutdown(getter_AddRefs(shutdown));
808 return NS_OK;
811 NS_IMETHODIMP
812 nsThread::BeginShutdown(nsIThreadShutdown** aShutdown) {
813 LOG(("THRD(%p) begin shutdown\n", this));
815 MOZ_ASSERT(mEvents);
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;
826 MOZ_ASSERT(mThread);
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",
852 threadName.get());
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);
859 return NS_OK;
862 void nsThread::ShutdownComplete(NotNull<nsThreadShutdownContext*> aContext) {
863 MOZ_ASSERT(mEvents);
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);
876 #endif
878 MaybeRemoveFromThreadList();
880 // Now, it should be safe to join without fear of dead-locking.
881 PR_JoinThread(aContext->mTerminatingPRThread);
882 MOZ_ASSERT(!mThread);
884 #ifdef DEBUG
885 nsCOMPtr<nsIThreadObserver> obs = mEvents->GetObserver();
886 MOZ_ASSERT(!obs, "Should have been cleared at shutdown!");
887 #endif
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);
900 NS_IMETHODIMP
901 nsThread::Shutdown() {
902 LOG(("THRD(%p) sync shutdown\n", this));
904 nsCOMPtr<nsIThreadShutdown> context;
905 nsresult rv = BeginShutdown(getter_AddRefs(context));
906 if (NS_FAILED(rv)) {
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(); });
919 return NS_OK;
922 NS_IMETHODIMP
923 nsThread::HasPendingEvents(bool* aResult) {
924 if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
925 return NS_ERROR_NOT_SAME_THREAD;
928 if (mIsMainThread) {
929 *aResult = TaskController::Get()->HasMainThreadPendingTasks();
930 } else {
931 *aResult = mEvents->HasPendingEvent();
933 return NS_OK;
936 NS_IMETHODIMP
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.
943 *aResult = false;
944 return NS_OK;
947 NS_IMETHODIMP
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)) {
957 NS_WARNING(
958 "An idle event was posted to a thread that will never run it "
959 "(rejected)");
960 return NS_ERROR_UNEXPECTED;
963 return NS_OK;
966 NS_IMETHODIMP nsThread::SetThreadQoS(nsIThread::QoSPriority aPriority) {
967 if (!StaticPrefs::threads_use_low_power_enabled()) {
968 return NS_OK;
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);
985 } else {
986 pthread_set_qos_class_self_np(QOS_CLASS_DEFAULT, 0);
988 #endif
989 // Do nothing if an OS-specific implementation is unavailable.
990 return NS_OK;
993 #ifdef MOZ_CANARY
994 void canary_alarm_handler(int signum);
996 class Canary {
997 // XXX ToDo: support nested loops
998 public:
999 Canary() {
1000 if (sCanaryOutputFD > 0 && EventLatencyIsImportant()) {
1001 signal(SIGALRM, canary_alarm_handler);
1002 ualarm(15000, 0);
1006 ~Canary() {
1007 if (sCanaryOutputFD != 0 && EventLatencyIsImportant()) {
1008 ualarm(0, 0);
1012 static bool EventLatencyIsImportant() {
1013 return NS_IsMainThread() && XRE_IsParentProcess();
1017 void canary_alarm_handler(int signum) {
1018 void* array[30];
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);
1025 #endif
1027 #define NOTIFY_EVENT_OBSERVERS(observers_, func_, params_) \
1028 do { \
1029 if (!observers_.IsEmpty()) { \
1030 for (nsCOMPtr<nsIThreadObserver> obs_ : observers_.ForwardRange()) { \
1031 obs_->func_ params_; \
1034 } while (0)
1036 size_t nsThread::ShallowSizeOfIncludingThis(
1037 mozilla::MallocSizeOf aMallocSizeOf) const {
1038 size_t n = 0;
1039 if (mShutdownContext) {
1040 n += aMallocSizeOf(mShutdownContext);
1042 return aMallocSizeOf(this) + aMallocSizeOf(mThread) + n;
1045 size_t nsThread::SizeOfEventQueues(mozilla::MallocSizeOf aMallocSizeOf) const {
1046 size_t n = 0;
1047 if (mEventTarget) {
1048 // The size of mEvents is reported by mEventTarget.
1049 n += mEventTarget->SizeOfIncludingThis(aMallocSizeOf);
1051 return n;
1054 size_t nsThread::SizeOfIncludingThis(
1055 mozilla::MallocSizeOf aMallocSizeOf) const {
1056 return ShallowSizeOfIncludingThis(aMallocSizeOf) +
1057 SizeOfEventQueues(aMallocSizeOf);
1060 NS_IMETHODIMP
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
1096 // mScriptObserver.
1097 bool callScriptObserver = !!mScriptObserver;
1098 if (callScriptObserver) {
1099 noJSAPI.emplace();
1100 mScriptObserver->BeforeProcessTask(reallyWait);
1103 DrainDirectTasks();
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
1108 // events.
1109 bool mayWaitForWakeup = reallyWait && !mEvents->HasPendingEvent();
1110 #endif
1112 nsCOMPtr<nsIThreadObserver> obs = mEvents->GetObserverOnThread();
1113 if (obs) {
1114 obs->OnProcessNextEvent(this, reallyWait);
1117 NOTIFY_EVENT_OBSERVERS(EventQueue()->EventObservers(), OnProcessNextEvent,
1118 (this, reallyWait));
1120 DrainDirectTasks();
1122 #ifdef MOZ_CANARY
1123 Canary canary;
1124 #endif
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
1130 // also do work.
1131 nsCOMPtr<nsIRunnable> event;
1132 bool usingTaskController = mIsMainThread;
1133 if (usingTaskController) {
1134 event = TaskController::Get()->GetRunnableForMTTask(reallyWait);
1135 } else {
1136 event = mEvents->GetEvent(reallyWait, &mLastEventDelay);
1139 *aResult = (event.get() != nullptr);
1141 if (event) {
1142 #ifdef EARLY_BETA_OR_EARLIER
1143 if (mayWaitForWakeup && mThread) {
1144 ++mWakeupCount;
1145 if (mWakeupCount == kTelemetryWakeupCountLimit) {
1146 TimeStamp now = TimeStamp::Now();
1147 double ms = (now - mLastWakeupCheckTime).ToMilliseconds();
1148 if (ms < 0) {
1149 ms = 0;
1151 const char* name = !mNameForWakeupTelemetry.IsEmpty()
1152 ? mNameForWakeupTelemetry.get()
1153 : PR_GetThreadName(mThread);
1154 if (!name) {
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;
1161 mWakeupCount = 0;
1164 #endif
1166 LOG(("THRD(%p) running [%p]\n", this, event.get()));
1168 Maybe<LogRunnable::Run> log;
1170 if (!usingTaskController) {
1171 log.emplace(event);
1174 // Delay event processing to encourage whoever dispatched this event
1175 // to run.
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);
1193 event->Run();
1194 } else {
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.
1199 event->Run();
1202 if (usingTaskController) {
1203 *aResult = TaskController::Get()->MTTaskRunnableProcessedTask();
1204 } else {
1205 mPerformanceCounterState.RunnableDidRun(EmptyCString(),
1206 std::move(snapshot.ref()));
1209 // To cover the event's destructor code inside the LogRunnable span.
1210 event = nullptr;
1211 } else {
1212 mLastEventDelay = TimeDuration();
1213 mLastEventStart = TimeStamp();
1214 if (aMayWait) {
1215 MOZ_ASSERT(ShuttingDown(),
1216 "This should only happen when shutting down");
1217 rv = NS_ERROR_UNEXPECTED;
1222 DrainDirectTasks();
1224 NOTIFY_EVENT_OBSERVERS(EventQueue()->EventObservers(), AfterProcessNextEvent,
1225 (this, *aResult));
1227 if (obs) {
1228 obs->AfterProcessNextEvent(this, *aResult);
1231 // In case some EventObserver dispatched some direct tasks; process them
1232 // now.
1233 DrainDirectTasks();
1235 if (callScriptObserver) {
1236 if (mScriptObserver) {
1237 mScriptObserver->AfterProcessTask(mNestedEventLoopDepth);
1239 noJSAPI.reset();
1242 --mNestedEventLoopDepth;
1244 return rv;
1247 //-----------------------------------------------------------------------------
1248 // nsISupportsPriority
1250 NS_IMETHODIMP
1251 nsThread::GetPriority(int32_t* aPriority) {
1252 *aPriority = mPriority;
1253 return NS_OK;
1256 NS_IMETHODIMP
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:
1263 // PR_PRIORITY_LOW
1264 // PR_PRIORITY_NORMAL
1265 // PR_PRIORITY_HIGH
1266 // PR_PRIORITY_URGENT
1267 // We map the priority values defined on nsISupportsPriority to these
1268 // values.
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;
1279 } else {
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);
1287 return NS_OK;
1290 NS_IMETHODIMP
1291 nsThread::AdjustPriority(int32_t aDelta) {
1292 return SetPriority(mPriority + aDelta);
1295 //-----------------------------------------------------------------------------
1296 // nsIThreadInternal
1298 NS_IMETHODIMP
1299 nsThread::GetObserver(nsIThreadObserver** aObs) {
1300 MOZ_ASSERT(mEvents);
1301 NS_ENSURE_TRUE(mEvents, NS_ERROR_NOT_IMPLEMENTED);
1303 nsCOMPtr<nsIThreadObserver> obs = mEvents->GetObserver();
1304 obs.forget(aObs);
1305 return NS_OK;
1308 NS_IMETHODIMP
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);
1318 return NS_OK;
1321 uint32_t nsThread::RecursionDepth() const {
1322 MOZ_ASSERT(PR_GetCurrentThread() == mThread);
1323 return mNestedEventLoopDepth;
1326 NS_IMETHODIMP
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);
1340 return NS_OK;
1343 NS_IMETHODIMP
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);
1354 return NS_OK;
1357 void nsThread::SetScriptObserver(
1358 mozilla::CycleCollectedJSContext* aScriptObserver) {
1359 if (!aScriptObserver) {
1360 mScriptObserver = nullptr;
1361 return;
1364 MOZ_ASSERT(!mScriptObserver);
1365 mScriptObserver = aScriptObserver;
1368 void NS_DispatchMemoryPressure();
1370 void nsThread::DoMainThreadSpecificProcessing() const {
1371 MOZ_ASSERT(mIsMainThread);
1373 ipc::CancelCPOWs();
1375 // Fire a memory pressure notification, if one is pending.
1376 if (!ShuttingDown()) {
1377 NS_DispatchMemoryPressure();
1381 //-----------------------------------------------------------------------------
1382 // nsIDirectTaskDispatcher
1384 NS_IMETHODIMP
1385 nsThread::DispatchDirectTask(already_AddRefed<nsIRunnable> aEvent) {
1386 if (!IsOnCurrentThread()) {
1387 return NS_ERROR_FAILURE;
1389 mDirectTasks.AddTask(std::move(aEvent));
1390 return NS_OK;
1393 NS_IMETHODIMP nsThread::DrainDirectTasks() {
1394 if (!IsOnCurrentThread()) {
1395 return NS_ERROR_FAILURE;
1397 mDirectTasks.DrainTasks();
1398 return NS_OK;
1401 NS_IMETHODIMP nsThread::HaveDirectTasks(bool* aValue) {
1402 if (!IsOnCurrentThread()) {
1403 return NS_ERROR_FAILURE;
1406 *aValue = mDirectTasks.HaveTasks();
1407 return NS_OK;
1410 NS_IMPL_ISUPPORTS(nsThreadShutdownContext, nsIThreadShutdown)
1412 NS_IMETHODIMP
1413 nsThreadShutdownContext::OnCompletion(nsIRunnable* aEvent) {
1414 if (mCompleted) {
1415 aEvent->Run();
1416 } else {
1417 mCompletionCallbacks.AppendElement(aEvent);
1419 return NS_OK;
1422 NS_IMETHODIMP
1423 nsThreadShutdownContext::GetCompleted(bool* aCompleted) {
1424 *aCompleted = mCompleted;
1425 return NS_OK;
1428 NS_IMETHODIMP
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());
1445 MarkCompleted();
1447 return NS_OK;
1450 void nsThreadShutdownContext::MarkCompleted() {
1451 MOZ_ASSERT(!mCompleted);
1452 mCompleted = true;
1453 nsTArray<nsCOMPtr<nsIRunnable>> callbacks(std::move(mCompletionCallbacks));
1454 for (auto& callback : callbacks) {
1455 callback->Run();
1459 namespace mozilla {
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;
1476 return snapshot;
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
1486 // don't.
1487 TimeStamp now;
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;
1501 } else {
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,
1508 TimeStamp aNow) {
1509 MOZ_ASSERT(mCurrentTimeSliceStart,
1510 "How did we get here if we're not in a timeslice?");
1511 if (!mLongTaskLength.isSome()) {
1512 return;
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());
1521 #endif
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",
1544 MS::Format::String,
1545 MS::Searchable::Searchable);
1546 return schema;
1550 profiler_add_marker(mCurrentRunnableIsIdleRunnable
1551 ? ProfilerString8View("LongIdleTask")
1552 : ProfilerString8View("LongTask"),
1553 geckoprofiler::category::OTHER,
1554 MarkerTiming::Interval(mCurrentTimeSliceStart, aNow),
1555 LongTaskMarker{});
1560 } // namespace mozilla