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"
11 // Chromium's logging can sometimes leak through...
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"
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"
40 #include <sys/resource.h>
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
54 # include <execinfo.h>
57 # include "nsXULAppAPI.h"
60 #if defined(NS_FUNCTION_TIMER) && defined(_MSC_VER)
61 #include "nsTimerImpl.h"
62 #include "nsStackWalk.h"
64 #ifdef NS_FUNCTION_TIMER
68 #ifdef MOZ_TASK_TRACER
69 #include "GeckoTaskTracer.h"
70 using namespace mozilla::tasktracer
;
73 using namespace mozilla
;
76 static PRLogModuleInfo
*
79 static PRLogModuleInfo
* sLog
;
81 sLog
= PR_NewLogModule("nsThread");
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
99 class nsThreadClassInfo
: public nsIClassInfo
102 NS_DECL_ISUPPORTS_INHERITED
// no mRefCnt
110 NS_IMETHODIMP_(MozExternalRefCountType
)
111 nsThreadClassInfo::AddRef()
115 NS_IMETHODIMP_(MozExternalRefCountType
)
116 nsThreadClassInfo::Release()
120 NS_IMPL_QUERY_INTERFACE(nsThreadClassInfo
, nsIClassInfo
)
123 nsThreadClassInfo::GetInterfaces(uint32_t* aCount
, nsIID
*** aArray
)
125 return NS_CI_INTERFACE_GETTER_NAME(nsThread
)(aCount
, aArray
);
129 nsThreadClassInfo::GetHelperForLanguage(uint32_t aLang
, nsISupports
** aResult
)
136 nsThreadClassInfo::GetContractID(char** aResult
)
143 nsThreadClassInfo::GetClassDescription(char** aResult
)
150 nsThreadClassInfo::GetClassID(nsCID
** aResult
)
157 nsThreadClassInfo::GetImplementationLanguage(uint32_t* aResult
)
159 *aResult
= nsIProgrammingLanguage::CPLUSPLUS
;
164 nsThreadClassInfo::GetFlags(uint32_t* aResult
)
166 *aResult
= THREADSAFE
;
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
);
191 NS_IMPL_CI_INTERFACE_GETTER(nsThread
, nsIThread
, nsIThreadInternal
,
192 nsIEventTarget
, nsISupportsPriority
)
194 //-----------------------------------------------------------------------------
196 class nsThreadStartupEvent
: public nsRunnable
199 nsThreadStartupEvent()
200 : mMon("nsThreadStartupEvent.mMon")
201 , mInitialized(false)
205 // This method does not return until the thread startup object is in the
210 // Maybe avoid locking...
214 ReentrantMonitorAutoEnter
mon(mMon
);
215 while (!mInitialized
) {
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() {}
227 ReentrantMonitorAutoEnter
mon(mMon
);
233 ReentrantMonitor mMon
;
237 //-----------------------------------------------------------------------------
239 struct nsThreadShutdownContext
241 nsThread
* joiningThread
;
245 // This event is responsible for notifying nsThread::Shutdown that it is time
246 // to call PR_JoinThread.
247 class nsThreadShutdownAckEvent
: public nsRunnable
250 explicit nsThreadShutdownAckEvent(nsThreadShutdownContext
* aCtx
)
251 : mShutdownContext(aCtx
)
256 mShutdownContext
->shutdownAck
= true;
260 nsThreadShutdownContext
* mShutdownContext
;
263 // This event is responsible for setting mShutdownContext
264 class nsThreadShutdownEvent
: public nsRunnable
267 nsThreadShutdownEvent(nsThread
* aThr
, nsThreadShutdownContext
* aCtx
)
269 , mShutdownContext(aCtx
)
274 mThread
->mShutdownContext
= mShutdownContext
;
275 MessageLoop::current()->Quit();
279 nsRefPtr
<nsThread
> mThread
;
280 nsThreadShutdownContext
* mShutdownContext
;
283 //-----------------------------------------------------------------------------
286 SetupCurrentThreadForChaosMode()
288 if (!ChaosMode::isActive()) {
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));
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
));
312 #ifdef HAVE_SCHED_SETAFFINITY
313 // Force half the threads to CPU 0 so they compete for CPU
314 if (ChaosMode::randomUint32LessThan(2)) {
318 sched_setaffinity(0, sizeof(cpus
), &cpus
);
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");
341 event
->Run(); // unblocks nsThread::Init
345 // Scope for MessageLoop.
346 nsAutoPtr
<MessageLoop
> loop(
347 new MessageLoop(MessageLoop::TYPE_MOZILLA_NONMAINTHREAD
));
349 // Now, process incoming events...
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.
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;
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
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;
412 if (needMemoryReport
) {
413 nsCOMPtr
<nsICrashReporter
> cr
=
414 do_GetService("@mozilla.org/toolkit/crash-reporter;1");
415 cr
->SaveMemoryReport();
418 return needMemoryReport
;
423 int sCanaryOutputFD
= -1;
426 nsThread::nsThread(MainThreadFlag aMainThread
, uint32_t aStackSize
)
427 : mLock("nsThread.mLock")
428 , mEvents(&mEventsRoot
)
429 , mPriority(PRIORITY_NORMAL
)
432 , mStackSize(aStackSize
)
433 , mShutdownContext(nullptr)
434 , mShutdownRequired(false)
435 , mEventsAreDoomed(false)
436 , mIsMainThread(aMainThread
)
440 nsThread::~nsThread()
447 // spawn thread and wait until it is fully setup
448 nsRefPtr
<nsThreadStartupEvent
> startup
= new nsThreadStartupEvent();
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
);
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.
478 nsThread::InitCurrentThread()
480 mThread
= PR_GetCurrentThread();
481 SetupCurrentThreadForChaosMode();
483 nsThreadManager::get()->RegisterCurrentThread(this);
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();
502 obs
->OnDispatchedEvent(this);
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
;
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
);
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...
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 //-----------------------------------------------------------------------------
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);
569 nsThread::IsOnCurrentThread(bool* aResult
)
571 *aResult
= (PR_GetCurrentThread() == mThread
);
575 //-----------------------------------------------------------------------------
579 nsThread::GetPRThread(PRThread
** aResult
)
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.
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
);
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
);
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
645 MutexAutoLock
lock(mLock
);
646 MOZ_ASSERT(!mObserver
, "Should have been cleared at shutdown!");
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);
665 void canary_alarm_handler(int signum
);
669 //XXX ToDo: support nested loops
673 if (sCanaryOutputFD
> 0 && EventLatencyIsImportant()) {
674 signal(SIGALRM
, canary_alarm_handler
);
681 if (sCanaryOutputFD
!= 0 && EventLatencyIsImportant()) {
686 static bool EventLatencyIsImportant()
688 return NS_IsMainThread() && XRE_GetProcessType() == GeckoProcessType_Default
;
692 void canary_alarm_handler(int signum
)
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
);
703 #define NOTIFY_EVENT_OBSERVERS(func_, params_) \
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_ ; \
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
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");
756 os
->NotifyObservers(nullptr, "memory-pressure",
757 mpPending
== MemPressure_New
? lowMem
.get() :
758 lowMemOngoing
.get());
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
);
780 nextCheck
= now
+ TimeDuration::FromSeconds(LOW_MEMORY_CHECK_SECONDS
);
786 bool notifyMainThreadObserver
=
787 (MAIN_THREAD
== mIsMainThread
) && sMainThreadObserver
;
788 if (notifyMainThreadObserver
) {
789 sMainThreadObserver
->OnProcessNextEvent(this, reallyWait
, mRunningEvent
);
792 nsCOMPtr
<nsIThreadObserver
> obs
= mObserver
;
794 obs
->OnProcessNextEvent(this, reallyWait
, mRunningEvent
);
797 NOTIFY_EVENT_OBSERVERS(OnProcessNextEvent
,
798 (this, reallyWait
, mRunningEvent
));
808 // Scope for |event| to make sure that its destructor fires while
809 // mRunningEvent has been incremented, since that destructor can
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);
819 LOG(("THRD(%p) running [%p]\n", this, event
.get()));
820 if (MAIN_THREAD
== mIsMainThread
) {
821 HangMonitor::NotifyActivity();
824 } else if (aMayWait
) {
825 MOZ_ASSERT(ShuttingDown(),
826 "This should only happen when shutting down");
827 rv
= NS_ERROR_UNEXPECTED
;
833 NOTIFY_EVENT_OBSERVERS(AfterProcessNextEvent
,
834 (this, mRunningEvent
, *aResult
));
837 obs
->AfterProcessNextEvent(this, mRunningEvent
, *aResult
);
840 if (notifyMainThreadObserver
&& sMainThreadObserver
) {
841 sMainThreadObserver
->AfterProcessNextEvent(this, mRunningEvent
, *aResult
);
847 //-----------------------------------------------------------------------------
848 // nsISupportsPriority
851 nsThread::GetPriority(int32_t* aPriority
)
853 *aPriority
= mPriority
;
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:
866 // PR_PRIORITY_NORMAL
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
;
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
);
892 nsThread::AdjustPriority(int32_t aDelta
)
894 return SetPriority(mPriority
+ aDelta
);
897 //-----------------------------------------------------------------------------
901 nsThread::GetObserver(nsIThreadObserver
** aObs
)
903 MutexAutoLock
lock(mLock
);
904 NS_IF_ADDREF(*aObs
= mObserver
);
909 nsThread::SetObserver(nsIThreadObserver
* aObs
)
911 if (NS_WARN_IF(PR_GetCurrentThread() != mThread
)) {
912 return NS_ERROR_NOT_SAME_THREAD
;
915 MutexAutoLock
lock(mLock
);
921 nsThread::GetRecursionDepth(uint32_t* aDepth
)
923 if (NS_WARN_IF(PR_GetCurrentThread() != mThread
)) {
924 return NS_ERROR_NOT_SAME_THREAD
;
927 *aDepth
= mRunningEvent
;
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
;
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!");
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
;
982 NS_ADDREF(*aResult
= queue
->mEventTarget
);
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
);
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;
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
;
1042 //-----------------------------------------------------------------------------
1045 nsThreadSyncDispatch::Run()
1048 mResult
= mSyncTask
->Run();
1049 mSyncTask
= nullptr;
1050 // unblock the origin thread
1051 mOrigin
->Dispatch(this, NS_DISPATCH_NORMAL
);
1056 //-----------------------------------------------------------------------------
1058 NS_IMPL_ISUPPORTS(nsThread::nsNestedEventTarget
, nsIEventTarget
)
1061 nsThread::nsNestedEventTarget::Dispatch(nsIRunnable
* aEvent
, uint32_t aFlags
)
1063 LOG(("THRD(%p) Dispatch [%p %x] to nested loop %p\n", mThread
.get(), aEvent
,
1066 return mThread
->DispatchInternal(aEvent
, aFlags
, this);
1070 nsThread::nsNestedEventTarget::IsOnCurrentThread(bool* aResult
)
1072 return mThread
->IsOnCurrentThread(aResult
);