Bug 978639 - Filly rebase error. a=bustage
[gecko.git] / xpcom / threads / nsThread.cpp
blob2951ceb1e6ca157c3b3a71e478f1229e7fe9d6a4
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"
11 // Chromium's logging can sometimes leak through...
12 #ifdef LOG
13 #undef LOG
14 #endif
16 #include "mozilla/ReentrantMonitor.h"
17 #include "nsMemoryPressure.h"
18 #include "nsThreadManager.h"
19 #include "nsIClassInfoImpl.h"
20 #include "nsIProgrammingLanguage.h"
21 #include "nsAutoPtr.h"
22 #include "nsCOMPtr.h"
23 #include "pratom.h"
24 #include "prlog.h"
25 #include "nsIObserverService.h"
26 #include "mozilla/HangMonitor.h"
27 #include "mozilla/IOInterposer.h"
28 #include "mozilla/ipc/MessageChannel.h"
29 #include "mozilla/Services.h"
30 #include "nsXPCOMPrivate.h"
31 #include "mozilla/ChaosMode.h"
33 #ifdef MOZ_CRASHREPORTER
34 #include "nsServiceManagerUtils.h"
35 #include "nsICrashReporter.h"
36 #endif
38 #ifdef XP_LINUX
39 #include <sys/time.h>
40 #include <sys/resource.h>
41 #include <sched.h>
42 #endif
44 #define HAVE_UALARM _BSD_SOURCE || (_XOPEN_SOURCE >= 500 || \
45 _XOPEN_SOURCE && _XOPEN_SOURCE_EXTENDED) && \
46 !(_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700)
48 #if defined(XP_LINUX) && !defined(ANDROID) && defined(_GNU_SOURCE)
49 #define HAVE_SCHED_SETAFFINITY
50 #endif
52 #ifdef MOZ_CANARY
53 # include <unistd.h>
54 # include <execinfo.h>
55 # include <signal.h>
56 # include <fcntl.h>
57 # include "nsXULAppAPI.h"
58 #endif
60 #if defined(NS_FUNCTION_TIMER) && defined(_MSC_VER)
61 #include "nsTimerImpl.h"
62 #include "nsStackWalk.h"
63 #endif
64 #ifdef NS_FUNCTION_TIMER
65 #include "nsCRT.h"
66 #endif
68 #ifdef MOZ_TASK_TRACER
69 #include "GeckoTaskTracer.h"
70 using namespace mozilla::tasktracer;
71 #endif
73 using namespace mozilla;
75 #ifdef PR_LOGGING
76 static PRLogModuleInfo*
77 GetThreadLog()
79 static PRLogModuleInfo* sLog;
80 if (!sLog) {
81 sLog = PR_NewLogModule("nsThread");
83 return sLog;
85 #endif
86 #ifdef LOG
87 #undef LOG
88 #endif
89 #define LOG(args) PR_LOG(GetThreadLog(), PR_LOG_DEBUG, args)
91 NS_DECL_CI_INTERFACE_GETTER(nsThread)
93 nsIThreadObserver* nsThread::sMainThreadObserver = nullptr;
95 //-----------------------------------------------------------------------------
96 // Because we do not have our own nsIFactory, we have to implement nsIClassInfo
97 // somewhat manually.
99 class nsThreadClassInfo : public nsIClassInfo
101 public:
102 NS_DECL_ISUPPORTS_INHERITED // no mRefCnt
103 NS_DECL_NSICLASSINFO
105 nsThreadClassInfo()
110 NS_IMETHODIMP_(MozExternalRefCountType)
111 nsThreadClassInfo::AddRef()
113 return 2;
115 NS_IMETHODIMP_(MozExternalRefCountType)
116 nsThreadClassInfo::Release()
118 return 1;
120 NS_IMPL_QUERY_INTERFACE(nsThreadClassInfo, nsIClassInfo)
122 NS_IMETHODIMP
123 nsThreadClassInfo::GetInterfaces(uint32_t* aCount, nsIID*** aArray)
125 return NS_CI_INTERFACE_GETTER_NAME(nsThread)(aCount, aArray);
128 NS_IMETHODIMP
129 nsThreadClassInfo::GetHelperForLanguage(uint32_t aLang, nsISupports** aResult)
131 *aResult = nullptr;
132 return NS_OK;
135 NS_IMETHODIMP
136 nsThreadClassInfo::GetContractID(char** aResult)
138 *aResult = nullptr;
139 return NS_OK;
142 NS_IMETHODIMP
143 nsThreadClassInfo::GetClassDescription(char** aResult)
145 *aResult = nullptr;
146 return NS_OK;
149 NS_IMETHODIMP
150 nsThreadClassInfo::GetClassID(nsCID** aResult)
152 *aResult = nullptr;
153 return NS_OK;
156 NS_IMETHODIMP
157 nsThreadClassInfo::GetImplementationLanguage(uint32_t* aResult)
159 *aResult = nsIProgrammingLanguage::CPLUSPLUS;
160 return NS_OK;
163 NS_IMETHODIMP
164 nsThreadClassInfo::GetFlags(uint32_t* aResult)
166 *aResult = THREADSAFE;
167 return NS_OK;
170 NS_IMETHODIMP
171 nsThreadClassInfo::GetClassIDNoAlloc(nsCID* aResult)
173 return NS_ERROR_NOT_AVAILABLE;
176 //-----------------------------------------------------------------------------
178 NS_IMPL_ADDREF(nsThread)
179 NS_IMPL_RELEASE(nsThread)
180 NS_INTERFACE_MAP_BEGIN(nsThread)
181 NS_INTERFACE_MAP_ENTRY(nsIThread)
182 NS_INTERFACE_MAP_ENTRY(nsIThreadInternal)
183 NS_INTERFACE_MAP_ENTRY(nsIEventTarget)
184 NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
185 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIThread)
186 if (aIID.Equals(NS_GET_IID(nsIClassInfo))) {
187 static nsThreadClassInfo sThreadClassInfo;
188 foundInterface = static_cast<nsIClassInfo*>(&sThreadClassInfo);
189 } else
190 NS_INTERFACE_MAP_END
191 NS_IMPL_CI_INTERFACE_GETTER(nsThread, nsIThread, nsIThreadInternal,
192 nsIEventTarget, nsISupportsPriority)
194 //-----------------------------------------------------------------------------
196 class nsThreadStartupEvent : public nsRunnable
198 public:
199 nsThreadStartupEvent()
200 : mMon("nsThreadStartupEvent.mMon")
201 , mInitialized(false)
205 // This method does not return until the thread startup object is in the
206 // completion state.
207 void Wait()
209 if (mInitialized) {
210 // Maybe avoid locking...
211 return;
214 ReentrantMonitorAutoEnter mon(mMon);
215 while (!mInitialized) {
216 mon.Wait();
220 // This method needs to be public to support older compilers (xlC_r on AIX).
221 // It should be called directly as this class type is reference counted.
222 virtual ~nsThreadStartupEvent() {}
224 private:
225 NS_IMETHOD Run()
227 ReentrantMonitorAutoEnter mon(mMon);
228 mInitialized = true;
229 mon.Notify();
230 return NS_OK;
233 ReentrantMonitor mMon;
234 bool mInitialized;
237 //-----------------------------------------------------------------------------
239 struct nsThreadShutdownContext
241 nsThread* joiningThread;
242 bool shutdownAck;
245 // This event is responsible for notifying nsThread::Shutdown that it is time
246 // to call PR_JoinThread.
247 class nsThreadShutdownAckEvent : public nsRunnable
249 public:
250 explicit nsThreadShutdownAckEvent(nsThreadShutdownContext* aCtx)
251 : mShutdownContext(aCtx)
254 NS_IMETHOD Run()
256 mShutdownContext->shutdownAck = true;
257 return NS_OK;
259 private:
260 nsThreadShutdownContext* mShutdownContext;
263 // This event is responsible for setting mShutdownContext
264 class nsThreadShutdownEvent : public nsRunnable
266 public:
267 nsThreadShutdownEvent(nsThread* aThr, nsThreadShutdownContext* aCtx)
268 : mThread(aThr)
269 , mShutdownContext(aCtx)
272 NS_IMETHOD Run()
274 mThread->mShutdownContext = mShutdownContext;
275 MessageLoop::current()->Quit();
276 return NS_OK;
278 private:
279 nsRefPtr<nsThread> mThread;
280 nsThreadShutdownContext* mShutdownContext;
283 //-----------------------------------------------------------------------------
285 static void
286 SetupCurrentThreadForChaosMode()
288 if (!ChaosMode::isActive()) {
289 return;
292 #ifdef XP_LINUX
293 // PR_SetThreadPriority doesn't really work since priorities >
294 // PR_PRIORITY_NORMAL can't be set by non-root users. Instead we'll just use
295 // setpriority(2) to set random 'nice values'. In regular Linux this is only
296 // a dynamic adjustment so it still doesn't really do what we want, but tools
297 // like 'rr' can be more aggressive about honoring these values.
298 // Some of these calls may fail due to trying to lower the priority
299 // (e.g. something may have already called setpriority() for this thread).
300 // This makes it hard to have non-main threads with higher priority than the
301 // main thread, but that's hard to fix. Tools like rr can choose to honor the
302 // requested values anyway.
303 // Use just 4 priorities so there's a reasonable chance of any two threads
304 // having equal priority.
305 setpriority(PRIO_PROCESS, 0, ChaosMode::randomUint32LessThan(4));
306 #else
307 // We should set the affinity here but NSPR doesn't provide a way to expose it.
308 uint32_t priority = ChaosMode::randomUint32LessThan(PR_PRIORITY_LAST + 1);
309 PR_SetThreadPriority(PR_GetCurrentThread(), PRThreadPriority(priority));
310 #endif
312 #ifdef HAVE_SCHED_SETAFFINITY
313 // Force half the threads to CPU 0 so they compete for CPU
314 if (ChaosMode::randomUint32LessThan(2)) {
315 cpu_set_t cpus;
316 CPU_ZERO(&cpus);
317 CPU_SET(0, &cpus);
318 sched_setaffinity(0, sizeof(cpus), &cpus);
320 #endif
323 /*static*/ void
324 nsThread::ThreadFunc(void* aArg)
326 nsThread* self = static_cast<nsThread*>(aArg); // strong reference
327 self->mThread = PR_GetCurrentThread();
328 SetupCurrentThreadForChaosMode();
330 // Inform the ThreadManager
331 nsThreadManager::get()->RegisterCurrentThread(self);
333 mozilla::IOInterposer::RegisterCurrentThread();
335 // Wait for and process startup event
336 nsCOMPtr<nsIRunnable> event;
337 if (!self->GetEvent(true, getter_AddRefs(event))) {
338 NS_WARNING("failed waiting for thread startup event");
339 return;
341 event->Run(); // unblocks nsThread::Init
342 event = nullptr;
345 // Scope for MessageLoop.
346 nsAutoPtr<MessageLoop> loop(
347 new MessageLoop(MessageLoop::TYPE_MOZILLA_NONMAINTHREAD));
349 // Now, process incoming events...
350 loop->Run();
352 // Do NS_ProcessPendingEvents but with special handling to set
353 // mEventsAreDoomed atomically with the removal of the last event. The key
354 // invariant here is that we will never permit PutEvent to succeed if the
355 // event would be left in the queue after our final call to
356 // NS_ProcessPendingEvents.
357 while (true) {
359 MutexAutoLock lock(self->mLock);
360 if (!self->mEvents->HasPendingEvent()) {
361 // No events in the queue, so we will stop now. Don't let any more
362 // events be added, since they won't be processed. It is critical
363 // that no PutEvent can occur between testing that the event queue is
364 // empty and setting mEventsAreDoomed!
365 self->mEventsAreDoomed = true;
366 break;
369 NS_ProcessPendingEvents(self);
373 mozilla::IOInterposer::UnregisterCurrentThread();
375 // Inform the threadmanager that this thread is going away
376 nsThreadManager::get()->UnregisterCurrentThread(self);
378 // Dispatch shutdown ACK
379 event = new nsThreadShutdownAckEvent(self->mShutdownContext);
380 self->mShutdownContext->joiningThread->Dispatch(event, NS_DISPATCH_NORMAL);
382 // Release any observer of the thread here.
383 self->SetObserver(nullptr);
385 #ifdef MOZ_TASK_TRACER
386 FreeTraceInfo();
387 #endif
389 NS_RELEASE(self);
392 //-----------------------------------------------------------------------------
394 #ifdef MOZ_CRASHREPORTER
395 // Tell the crash reporter to save a memory report if our heuristics determine
396 // that an OOM failure is likely to occur soon.
397 static bool SaveMemoryReportNearOOM()
399 bool needMemoryReport = false;
401 #ifdef XP_WIN // XXX implement on other platforms as needed
402 const size_t LOWMEM_THRESHOLD_VIRTUAL = 200 * 1024 * 1024;
403 MEMORYSTATUSEX statex;
404 statex.dwLength = sizeof(statex);
405 if (GlobalMemoryStatusEx(&statex)) {
406 if (statex.ullAvailVirtual < LOWMEM_THRESHOLD_VIRTUAL) {
407 needMemoryReport = true;
410 #endif
412 if (needMemoryReport) {
413 nsCOMPtr<nsICrashReporter> cr =
414 do_GetService("@mozilla.org/toolkit/crash-reporter;1");
415 cr->SaveMemoryReport();
418 return needMemoryReport;
420 #endif
422 #ifdef MOZ_CANARY
423 int sCanaryOutputFD = -1;
424 #endif
426 nsThread::nsThread(MainThreadFlag aMainThread, uint32_t aStackSize)
427 : mLock("nsThread.mLock")
428 , mEvents(&mEventsRoot)
429 , mPriority(PRIORITY_NORMAL)
430 , mThread(nullptr)
431 , mRunningEvent(0)
432 , mStackSize(aStackSize)
433 , mShutdownContext(nullptr)
434 , mShutdownRequired(false)
435 , mEventsAreDoomed(false)
436 , mIsMainThread(aMainThread)
440 nsThread::~nsThread()
444 nsresult
445 nsThread::Init()
447 // spawn thread and wait until it is fully setup
448 nsRefPtr<nsThreadStartupEvent> startup = new nsThreadStartupEvent();
450 NS_ADDREF_THIS();
452 mShutdownRequired = true;
454 // ThreadFunc is responsible for setting mThread
455 PRThread* thr = PR_CreateThread(PR_USER_THREAD, ThreadFunc, this,
456 PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
457 PR_JOINABLE_THREAD, mStackSize);
458 if (!thr) {
459 NS_RELEASE_THIS();
460 return NS_ERROR_OUT_OF_MEMORY;
463 // ThreadFunc will wait for this event to be run before it tries to access
464 // mThread. By delaying insertion of this event into the queue, we ensure
465 // that mThread is set properly.
467 MutexAutoLock lock(mLock);
468 mEventsRoot.PutEvent(startup);
471 // Wait for thread to call ThreadManager::SetupCurrentThread, which completes
472 // initialization of ThreadFunc.
473 startup->Wait();
474 return NS_OK;
477 nsresult
478 nsThread::InitCurrentThread()
480 mThread = PR_GetCurrentThread();
481 SetupCurrentThreadForChaosMode();
483 nsThreadManager::get()->RegisterCurrentThread(this);
484 return NS_OK;
487 nsresult
488 nsThread::PutEvent(nsIRunnable* aEvent, nsNestedEventTarget* aTarget)
491 MutexAutoLock lock(mLock);
492 nsChainedEventQueue* queue = aTarget ? aTarget->mQueue : &mEventsRoot;
493 if (!queue || (queue == &mEventsRoot && mEventsAreDoomed)) {
494 NS_WARNING("An event was posted to a thread that will never run it (rejected)");
495 return NS_ERROR_UNEXPECTED;
497 queue->PutEvent(aEvent);
500 nsCOMPtr<nsIThreadObserver> obs = GetObserver();
501 if (obs) {
502 obs->OnDispatchedEvent(this);
505 return NS_OK;
508 nsresult
509 nsThread::DispatchInternal(nsIRunnable* aEvent, uint32_t aFlags,
510 nsNestedEventTarget* aTarget)
512 if (NS_WARN_IF(!aEvent)) {
513 return NS_ERROR_INVALID_ARG;
516 if (gXPCOMThreadsShutDown && MAIN_THREAD != mIsMainThread && !aTarget) {
517 return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
520 #ifdef MOZ_TASK_TRACER
521 nsRefPtr<nsIRunnable> tracedRunnable = CreateTracedRunnable(aEvent);
522 aEvent = tracedRunnable;
523 #endif
525 if (aFlags & DISPATCH_SYNC) {
526 nsThread* thread = nsThreadManager::get()->GetCurrentThread();
527 if (NS_WARN_IF(!thread)) {
528 return NS_ERROR_NOT_AVAILABLE;
531 // XXX we should be able to do something better here... we should
532 // be able to monitor the slot occupied by this event and use
533 // that to tell us when the event has been processed.
535 nsRefPtr<nsThreadSyncDispatch> wrapper =
536 new nsThreadSyncDispatch(thread, aEvent);
537 if (!wrapper) {
538 return NS_ERROR_OUT_OF_MEMORY;
540 nsresult rv = PutEvent(wrapper, aTarget);
541 // Don't wait for the event to finish if we didn't dispatch it...
542 if (NS_FAILED(rv)) {
543 return rv;
546 // Allows waiting; ensure no locks are held that would deadlock us!
547 while (wrapper->IsPending()) {
548 NS_ProcessNextEvent(thread, true);
550 return wrapper->Result();
553 NS_ASSERTION(aFlags == NS_DISPATCH_NORMAL, "unexpected dispatch flags");
554 return PutEvent(aEvent, aTarget);
557 //-----------------------------------------------------------------------------
558 // nsIEventTarget
560 NS_IMETHODIMP
561 nsThread::Dispatch(nsIRunnable* aEvent, uint32_t aFlags)
563 LOG(("THRD(%p) Dispatch [%p %x]\n", this, aEvent, aFlags));
565 return DispatchInternal(aEvent, aFlags, nullptr);
568 NS_IMETHODIMP
569 nsThread::IsOnCurrentThread(bool* aResult)
571 *aResult = (PR_GetCurrentThread() == mThread);
572 return NS_OK;
575 //-----------------------------------------------------------------------------
576 // nsIThread
578 NS_IMETHODIMP
579 nsThread::GetPRThread(PRThread** aResult)
581 *aResult = mThread;
582 return NS_OK;
585 NS_IMETHODIMP
586 nsThread::Shutdown()
588 LOG(("THRD(%p) shutdown\n", this));
590 // XXX If we make this warn, then we hit that warning at xpcom shutdown while
591 // shutting down a thread in a thread pool. That happens b/c the thread
592 // in the thread pool is already shutdown by the thread manager.
593 if (!mThread) {
594 return NS_OK;
597 if (NS_WARN_IF(mThread == PR_GetCurrentThread())) {
598 return NS_ERROR_UNEXPECTED;
601 // Prevent multiple calls to this method
603 MutexAutoLock lock(mLock);
604 if (!mShutdownRequired) {
605 return NS_ERROR_UNEXPECTED;
607 mShutdownRequired = false;
610 nsThreadShutdownContext context;
611 context.joiningThread = nsThreadManager::get()->GetCurrentThread();
612 context.shutdownAck = false;
614 // Set mShutdownContext and wake up the thread in case it is waiting for
615 // events to process.
616 nsCOMPtr<nsIRunnable> event = new nsThreadShutdownEvent(this, &context);
617 if (!event) {
618 return NS_ERROR_OUT_OF_MEMORY;
620 // XXXroc What if posting the event fails due to OOM?
621 PutEvent(event, nullptr);
623 // We could still end up with other events being added after the shutdown
624 // task, but that's okay because we process pending events in ThreadFunc
625 // after setting mShutdownContext just before exiting.
627 // Process events on the current thread until we receive a shutdown ACK.
628 // Allows waiting; ensure no locks are held that would deadlock us!
629 while (!context.shutdownAck) {
630 NS_ProcessNextEvent(context.joiningThread, true);
633 // Now, it should be safe to join without fear of dead-locking.
635 PR_JoinThread(mThread);
636 mThread = nullptr;
638 // We hold strong references to our event observers, and once the thread is
639 // shut down the observers can't easily unregister themselves. Do it here
640 // to avoid leaking.
641 ClearObservers();
643 #ifdef DEBUG
645 MutexAutoLock lock(mLock);
646 MOZ_ASSERT(!mObserver, "Should have been cleared at shutdown!");
648 #endif
650 return NS_OK;
653 NS_IMETHODIMP
654 nsThread::HasPendingEvents(bool* aResult)
656 if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
657 return NS_ERROR_NOT_SAME_THREAD;
660 *aResult = mEvents->GetEvent(false, nullptr);
661 return NS_OK;
664 #ifdef MOZ_CANARY
665 void canary_alarm_handler(int signum);
667 class Canary
669 //XXX ToDo: support nested loops
670 public:
671 Canary()
673 if (sCanaryOutputFD > 0 && EventLatencyIsImportant()) {
674 signal(SIGALRM, canary_alarm_handler);
675 ualarm(15000, 0);
679 ~Canary()
681 if (sCanaryOutputFD != 0 && EventLatencyIsImportant()) {
682 ualarm(0, 0);
686 static bool EventLatencyIsImportant()
688 return NS_IsMainThread() && XRE_GetProcessType() == GeckoProcessType_Default;
692 void canary_alarm_handler(int signum)
694 void* array[30];
695 const char msg[29] = "event took too long to run:\n";
696 // use write to be safe in the signal handler
697 write(sCanaryOutputFD, msg, sizeof(msg));
698 backtrace_symbols_fd(array, backtrace(array, 30), sCanaryOutputFD);
701 #endif
703 #define NOTIFY_EVENT_OBSERVERS(func_, params_) \
704 PR_BEGIN_MACRO \
705 if (!mEventObservers.IsEmpty()) { \
706 nsAutoTObserverArray<nsCOMPtr<nsIThreadObserver>, 2>::ForwardIterator \
707 iter_(mEventObservers); \
708 nsCOMPtr<nsIThreadObserver> obs_; \
709 while (iter_.HasMore()) { \
710 obs_ = iter_.GetNext(); \
711 obs_ -> func_ params_ ; \
714 PR_END_MACRO
716 NS_IMETHODIMP
717 nsThread::ProcessNextEvent(bool aMayWait, bool* aResult)
719 LOG(("THRD(%p) ProcessNextEvent [%u %u]\n", this, aMayWait, mRunningEvent));
721 // If we're on the main thread, we shouldn't be dispatching CPOWs.
722 MOZ_RELEASE_ASSERT(mIsMainThread != MAIN_THREAD ||
723 !ipc::ProcessingUrgentMessages());
725 if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
726 return NS_ERROR_NOT_SAME_THREAD;
729 // The toplevel event loop normally blocks waiting for the next event, but
730 // if we're trying to shut this thread down, we must exit the event loop when
731 // the event queue is empty.
732 // This only applys to the toplevel event loop! Nested event loops (e.g.
733 // during sync dispatch) are waiting for some state change and must be able
734 // to block even if something has requested shutdown of the thread. Otherwise
735 // we'll just busywait as we endlessly look for an event, fail to find one,
736 // and repeat the nested event loop since its state change hasn't happened yet.
737 bool reallyWait = aMayWait && (mRunningEvent > 0 || !ShuttingDown());
739 if (MAIN_THREAD == mIsMainThread && reallyWait) {
740 HangMonitor::Suspend();
743 // Fire a memory pressure notification, if we're the main thread and one is
744 // pending.
745 if (MAIN_THREAD == mIsMainThread && !ShuttingDown()) {
746 MemoryPressureState mpPending = NS_GetPendingMemoryPressure();
747 if (mpPending != MemPressure_None) {
748 nsCOMPtr<nsIObserverService> os = services::GetObserverService();
750 // Use no-forward to prevent the notifications from being transferred to
751 // the children of this process.
752 NS_NAMED_LITERAL_STRING(lowMem, "low-memory-no-forward");
753 NS_NAMED_LITERAL_STRING(lowMemOngoing, "low-memory-ongoing-no-forward");
755 if (os) {
756 os->NotifyObservers(nullptr, "memory-pressure",
757 mpPending == MemPressure_New ? lowMem.get() :
758 lowMemOngoing.get());
759 } else {
760 NS_WARNING("Can't get observer service!");
765 #ifdef MOZ_CRASHREPORTER
766 if (MAIN_THREAD == mIsMainThread && !ShuttingDown()) {
767 // Keep an eye on memory usage (cheap, ~7ms) somewhat frequently,
768 // but save memory reports (expensive, ~75ms) less frequently.
769 const size_t LOW_MEMORY_CHECK_SECONDS = 30;
770 const size_t LOW_MEMORY_SAVE_SECONDS = 3 * 60;
772 static TimeStamp nextCheck = TimeStamp::NowLoRes()
773 + TimeDuration::FromSeconds(LOW_MEMORY_CHECK_SECONDS);
775 TimeStamp now = TimeStamp::NowLoRes();
776 if (now >= nextCheck) {
777 if (SaveMemoryReportNearOOM()) {
778 nextCheck = now + TimeDuration::FromSeconds(LOW_MEMORY_SAVE_SECONDS);
779 } else {
780 nextCheck = now + TimeDuration::FromSeconds(LOW_MEMORY_CHECK_SECONDS);
784 #endif
786 bool notifyMainThreadObserver =
787 (MAIN_THREAD == mIsMainThread) && sMainThreadObserver;
788 if (notifyMainThreadObserver) {
789 sMainThreadObserver->OnProcessNextEvent(this, reallyWait, mRunningEvent);
792 nsCOMPtr<nsIThreadObserver> obs = mObserver;
793 if (obs) {
794 obs->OnProcessNextEvent(this, reallyWait, mRunningEvent);
797 NOTIFY_EVENT_OBSERVERS(OnProcessNextEvent,
798 (this, reallyWait, mRunningEvent));
800 ++mRunningEvent;
802 #ifdef MOZ_CANARY
803 Canary canary;
804 #endif
805 nsresult rv = NS_OK;
808 // Scope for |event| to make sure that its destructor fires while
809 // mRunningEvent has been incremented, since that destructor can
810 // also do work.
812 // If we are shutting down, then do not wait for new events.
813 nsCOMPtr<nsIRunnable> event;
814 mEvents->GetEvent(reallyWait, getter_AddRefs(event));
816 *aResult = (event.get() != nullptr);
818 if (event) {
819 LOG(("THRD(%p) running [%p]\n", this, event.get()));
820 if (MAIN_THREAD == mIsMainThread) {
821 HangMonitor::NotifyActivity();
823 event->Run();
824 } else if (aMayWait) {
825 MOZ_ASSERT(ShuttingDown(),
826 "This should only happen when shutting down");
827 rv = NS_ERROR_UNEXPECTED;
831 --mRunningEvent;
833 NOTIFY_EVENT_OBSERVERS(AfterProcessNextEvent,
834 (this, mRunningEvent, *aResult));
836 if (obs) {
837 obs->AfterProcessNextEvent(this, mRunningEvent, *aResult);
840 if (notifyMainThreadObserver && sMainThreadObserver) {
841 sMainThreadObserver->AfterProcessNextEvent(this, mRunningEvent, *aResult);
844 return rv;
847 //-----------------------------------------------------------------------------
848 // nsISupportsPriority
850 NS_IMETHODIMP
851 nsThread::GetPriority(int32_t* aPriority)
853 *aPriority = mPriority;
854 return NS_OK;
857 NS_IMETHODIMP
858 nsThread::SetPriority(int32_t aPriority)
860 if (NS_WARN_IF(!mThread)) {
861 return NS_ERROR_NOT_INITIALIZED;
864 // NSPR defines the following four thread priorities:
865 // PR_PRIORITY_LOW
866 // PR_PRIORITY_NORMAL
867 // PR_PRIORITY_HIGH
868 // PR_PRIORITY_URGENT
869 // We map the priority values defined on nsISupportsPriority to these values.
871 mPriority = aPriority;
873 PRThreadPriority pri;
874 if (mPriority <= PRIORITY_HIGHEST) {
875 pri = PR_PRIORITY_URGENT;
876 } else if (mPriority < PRIORITY_NORMAL) {
877 pri = PR_PRIORITY_HIGH;
878 } else if (mPriority > PRIORITY_NORMAL) {
879 pri = PR_PRIORITY_LOW;
880 } else {
881 pri = PR_PRIORITY_NORMAL;
883 // If chaos mode is active, retain the randomly chosen priority
884 if (!ChaosMode::isActive()) {
885 PR_SetThreadPriority(mThread, pri);
888 return NS_OK;
891 NS_IMETHODIMP
892 nsThread::AdjustPriority(int32_t aDelta)
894 return SetPriority(mPriority + aDelta);
897 //-----------------------------------------------------------------------------
898 // nsIThreadInternal
900 NS_IMETHODIMP
901 nsThread::GetObserver(nsIThreadObserver** aObs)
903 MutexAutoLock lock(mLock);
904 NS_IF_ADDREF(*aObs = mObserver);
905 return NS_OK;
908 NS_IMETHODIMP
909 nsThread::SetObserver(nsIThreadObserver* aObs)
911 if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
912 return NS_ERROR_NOT_SAME_THREAD;
915 MutexAutoLock lock(mLock);
916 mObserver = aObs;
917 return NS_OK;
920 NS_IMETHODIMP
921 nsThread::GetRecursionDepth(uint32_t* aDepth)
923 if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
924 return NS_ERROR_NOT_SAME_THREAD;
927 *aDepth = mRunningEvent;
928 return NS_OK;
931 NS_IMETHODIMP
932 nsThread::AddObserver(nsIThreadObserver* aObserver)
934 if (NS_WARN_IF(!aObserver)) {
935 return NS_ERROR_INVALID_ARG;
937 if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
938 return NS_ERROR_NOT_SAME_THREAD;
941 NS_WARN_IF_FALSE(!mEventObservers.Contains(aObserver),
942 "Adding an observer twice!");
944 if (!mEventObservers.AppendElement(aObserver)) {
945 NS_WARNING("Out of memory!");
946 return NS_ERROR_OUT_OF_MEMORY;
949 return NS_OK;
952 NS_IMETHODIMP
953 nsThread::RemoveObserver(nsIThreadObserver* aObserver)
955 if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
956 return NS_ERROR_NOT_SAME_THREAD;
959 if (aObserver && !mEventObservers.RemoveElement(aObserver)) {
960 NS_WARNING("Removing an observer that was never added!");
963 return NS_OK;
966 NS_IMETHODIMP
967 nsThread::PushEventQueue(nsIEventTarget** aResult)
969 if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
970 return NS_ERROR_NOT_SAME_THREAD;
973 nsChainedEventQueue* queue = new nsChainedEventQueue();
974 queue->mEventTarget = new nsNestedEventTarget(this, queue);
977 MutexAutoLock lock(mLock);
978 queue->mNext = mEvents;
979 mEvents = queue;
982 NS_ADDREF(*aResult = queue->mEventTarget);
983 return NS_OK;
986 NS_IMETHODIMP
987 nsThread::PopEventQueue(nsIEventTarget* aInnermostTarget)
989 if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
990 return NS_ERROR_NOT_SAME_THREAD;
993 if (NS_WARN_IF(!aInnermostTarget)) {
994 return NS_ERROR_NULL_POINTER;
997 // Don't delete or release anything while holding the lock.
998 nsAutoPtr<nsChainedEventQueue> queue;
999 nsRefPtr<nsNestedEventTarget> target;
1002 MutexAutoLock lock(mLock);
1004 // Make sure we're popping the innermost event target.
1005 if (NS_WARN_IF(mEvents->mEventTarget != aInnermostTarget)) {
1006 return NS_ERROR_UNEXPECTED;
1009 MOZ_ASSERT(mEvents != &mEventsRoot);
1011 queue = mEvents;
1012 mEvents = mEvents->mNext;
1014 nsCOMPtr<nsIRunnable> event;
1015 while (queue->GetEvent(false, getter_AddRefs(event))) {
1016 mEvents->PutEvent(event);
1019 // Don't let the event target post any more events.
1020 queue->mEventTarget.swap(target);
1021 target->mQueue = nullptr;
1024 return NS_OK;
1027 nsresult
1028 nsThread::SetMainThreadObserver(nsIThreadObserver* aObserver)
1030 if (aObserver && nsThread::sMainThreadObserver) {
1031 return NS_ERROR_NOT_AVAILABLE;
1034 if (!NS_IsMainThread()) {
1035 return NS_ERROR_UNEXPECTED;
1038 nsThread::sMainThreadObserver = aObserver;
1039 return NS_OK;
1042 //-----------------------------------------------------------------------------
1044 NS_IMETHODIMP
1045 nsThreadSyncDispatch::Run()
1047 if (mSyncTask) {
1048 mResult = mSyncTask->Run();
1049 mSyncTask = nullptr;
1050 // unblock the origin thread
1051 mOrigin->Dispatch(this, NS_DISPATCH_NORMAL);
1053 return NS_OK;
1056 //-----------------------------------------------------------------------------
1058 NS_IMPL_ISUPPORTS(nsThread::nsNestedEventTarget, nsIEventTarget)
1060 NS_IMETHODIMP
1061 nsThread::nsNestedEventTarget::Dispatch(nsIRunnable* aEvent, uint32_t aFlags)
1063 LOG(("THRD(%p) Dispatch [%p %x] to nested loop %p\n", mThread.get(), aEvent,
1064 aFlags, this));
1066 return mThread->DispatchInternal(aEvent, aFlags, this);
1069 NS_IMETHODIMP
1070 nsThread::nsNestedEventTarget::IsOnCurrentThread(bool* aResult)
1072 return mThread->IsOnCurrentThread(aResult);