xpcom/threads/nsThread.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:df929aa7aab6
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
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/. */
6
7 #include "nsThread.h"
8
9 #include "base/message_loop.h"
10
11 // Chromium's logging can sometimes leak through...
12 #ifdef LOG
13 #undef LOG
14 #endif
15
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/Services.h"
29 #include "nsXPCOMPrivate.h"
30 #include "mozilla/ChaosMode.h"
31
32 #ifdef XP_LINUX
33 #include <sys/time.h>
34 #include <sys/resource.h>
35 #include <sched.h>
36 #endif
37
38 #define HAVE_UALARM _BSD_SOURCE || (_XOPEN_SOURCE >= 500 || \
39 _XOPEN_SOURCE && _XOPEN_SOURCE_EXTENDED) && \
40 !(_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700)
41
42 #if defined(XP_LINUX) && !defined(ANDROID) && defined(_GNU_SOURCE)
43 #define HAVE_SCHED_SETAFFINITY
44 #endif
45
46 #ifdef MOZ_CANARY
47 # include <unistd.h>
48 # include <execinfo.h>
49 # include <signal.h>
50 # include <fcntl.h>
51 # include "nsXULAppAPI.h"
52 #endif
53
54 #if defined(NS_FUNCTION_TIMER) && defined(_MSC_VER)
55 #include "nsTimerImpl.h"
56 #include "nsStackWalk.h"
57 #endif
58 #ifdef NS_FUNCTION_TIMER
59 #include "nsCRT.h"
60 #endif
61
62 #ifdef MOZ_TASK_TRACER
63 #include "GeckoTaskTracer.h"
64 using namespace mozilla::tasktracer;
65 #endif
66
67 using namespace mozilla;
68
69 #ifdef PR_LOGGING
70 static PRLogModuleInfo *
71 GetThreadLog()
72 {
73 static PRLogModuleInfo *sLog;
74 if (!sLog)
75 sLog = PR_NewLogModule("nsThread");
76 return sLog;
77 }
78 #endif
79 #ifdef LOG
80 #undef LOG
81 #endif
82 #define LOG(args) PR_LOG(GetThreadLog(), PR_LOG_DEBUG, args)
83
84 NS_DECL_CI_INTERFACE_GETTER(nsThread)
85
86 nsIThreadObserver* nsThread::sMainThreadObserver = nullptr;
87
88 //-----------------------------------------------------------------------------
89 // Because we do not have our own nsIFactory, we have to implement nsIClassInfo
90 // somewhat manually.
91
92 class nsThreadClassInfo : public nsIClassInfo {
93 public:
94 NS_DECL_ISUPPORTS_INHERITED // no mRefCnt
95 NS_DECL_NSICLASSINFO
96
97 nsThreadClassInfo() {}
98 };
99
100 NS_IMETHODIMP_(MozExternalRefCountType) nsThreadClassInfo::AddRef() { return 2; }
101 NS_IMETHODIMP_(MozExternalRefCountType) nsThreadClassInfo::Release() { return 1; }
102 NS_IMPL_QUERY_INTERFACE(nsThreadClassInfo, nsIClassInfo)
103
104 NS_IMETHODIMP
105 nsThreadClassInfo::GetInterfaces(uint32_t *count, nsIID ***array)
106 {
107 return NS_CI_INTERFACE_GETTER_NAME(nsThread)(count, array);
108 }
109
110 NS_IMETHODIMP
111 nsThreadClassInfo::GetHelperForLanguage(uint32_t lang, nsISupports **result)
112 {
113 *result = nullptr;
114 return NS_OK;
115 }
116
117 NS_IMETHODIMP
118 nsThreadClassInfo::GetContractID(char **result)
119 {
120 *result = nullptr;
121 return NS_OK;
122 }
123
124 NS_IMETHODIMP
125 nsThreadClassInfo::GetClassDescription(char **result)
126 {
127 *result = nullptr;
128 return NS_OK;
129 }
130
131 NS_IMETHODIMP
132 nsThreadClassInfo::GetClassID(nsCID **result)
133 {
134 *result = nullptr;
135 return NS_OK;
136 }
137
138 NS_IMETHODIMP
139 nsThreadClassInfo::GetImplementationLanguage(uint32_t *result)
140 {
141 *result = nsIProgrammingLanguage::CPLUSPLUS;
142 return NS_OK;
143 }
144
145 NS_IMETHODIMP
146 nsThreadClassInfo::GetFlags(uint32_t *result)
147 {
148 *result = THREADSAFE;
149 return NS_OK;
150 }
151
152 NS_IMETHODIMP
153 nsThreadClassInfo::GetClassIDNoAlloc(nsCID *result)
154 {
155 return NS_ERROR_NOT_AVAILABLE;
156 }
157
158 //-----------------------------------------------------------------------------
159
160 NS_IMPL_ADDREF(nsThread)
161 NS_IMPL_RELEASE(nsThread)
162 NS_INTERFACE_MAP_BEGIN(nsThread)
163 NS_INTERFACE_MAP_ENTRY(nsIThread)
164 NS_INTERFACE_MAP_ENTRY(nsIThreadInternal)
165 NS_INTERFACE_MAP_ENTRY(nsIEventTarget)
166 NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
167 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIThread)
168 if (aIID.Equals(NS_GET_IID(nsIClassInfo))) {
169 static nsThreadClassInfo sThreadClassInfo;
170 foundInterface = static_cast<nsIClassInfo*>(&sThreadClassInfo);
171 } else
172 NS_INTERFACE_MAP_END
173 NS_IMPL_CI_INTERFACE_GETTER(nsThread, nsIThread, nsIThreadInternal,
174 nsIEventTarget, nsISupportsPriority)
175
176 //-----------------------------------------------------------------------------
177
178 class nsThreadStartupEvent : public nsRunnable {
179 public:
180 nsThreadStartupEvent()
181 : mMon("nsThreadStartupEvent.mMon")
182 , mInitialized(false) {
183 }
184
185 // This method does not return until the thread startup object is in the
186 // completion state.
187 void Wait() {
188 if (mInitialized) // Maybe avoid locking...
189 return;
190 ReentrantMonitorAutoEnter mon(mMon);
191 while (!mInitialized)
192 mon.Wait();
193 }
194
195 // This method needs to be public to support older compilers (xlC_r on AIX).
196 // It should be called directly as this class type is reference counted.
197 virtual ~nsThreadStartupEvent() {
198 }
199
200 private:
201 NS_IMETHOD Run() {
202 ReentrantMonitorAutoEnter mon(mMon);
203 mInitialized = true;
204 mon.Notify();
205 return NS_OK;
206 }
207
208 ReentrantMonitor mMon;
209 bool mInitialized;
210 };
211
212 //-----------------------------------------------------------------------------
213
214 struct nsThreadShutdownContext {
215 nsThread *joiningThread;
216 bool shutdownAck;
217 };
218
219 // This event is responsible for notifying nsThread::Shutdown that it is time
220 // to call PR_JoinThread.
221 class nsThreadShutdownAckEvent : public nsRunnable {
222 public:
223 nsThreadShutdownAckEvent(nsThreadShutdownContext *ctx)
224 : mShutdownContext(ctx) {
225 }
226 NS_IMETHOD Run() {
227 mShutdownContext->shutdownAck = true;
228 return NS_OK;
229 }
230 private:
231 nsThreadShutdownContext *mShutdownContext;
232 };
233
234 // This event is responsible for setting mShutdownContext
235 class nsThreadShutdownEvent : public nsRunnable {
236 public:
237 nsThreadShutdownEvent(nsThread *thr, nsThreadShutdownContext *ctx)
238 : mThread(thr), mShutdownContext(ctx) {
239 }
240 NS_IMETHOD Run() {
241 mThread->mShutdownContext = mShutdownContext;
242 MessageLoop::current()->Quit();
243 return NS_OK;
244 }
245 private:
246 nsRefPtr<nsThread> mThread;
247 nsThreadShutdownContext *mShutdownContext;
248 };
249
250 //-----------------------------------------------------------------------------
251
252 static void
253 SetupCurrentThreadForChaosMode()
254 {
255 if (!ChaosMode::isActive()) {
256 return;
257 }
258
259 #ifdef XP_LINUX
260 // PR_SetThreadPriority doesn't really work since priorities >
261 // PR_PRIORITY_NORMAL can't be set by non-root users. Instead we'll just use
262 // setpriority(2) to set random 'nice values'. In regular Linux this is only
263 // a dynamic adjustment so it still doesn't really do what we want, but tools
264 // like 'rr' can be more aggressive about honoring these values.
265 // Some of these calls may fail due to trying to lower the priority
266 // (e.g. something may have already called setpriority() for this thread).
267 // This makes it hard to have non-main threads with higher priority than the
268 // main thread, but that's hard to fix. Tools like rr can choose to honor the
269 // requested values anyway.
270 // Use just 4 priorities so there's a reasonable chance of any two threads
271 // having equal priority.
272 setpriority(PRIO_PROCESS, 0, ChaosMode::randomUint32LessThan(4));
273 #else
274 // We should set the affinity here but NSPR doesn't provide a way to expose it.
275 PR_SetThreadPriority(PR_GetCurrentThread(),
276 PRThreadPriority(ChaosMode::randomUint32LessThan(PR_PRIORITY_LAST + 1)));
277 #endif
278
279 #ifdef HAVE_SCHED_SETAFFINITY
280 // Force half the threads to CPU 0 so they compete for CPU
281 if (ChaosMode::randomUint32LessThan(2)) {
282 cpu_set_t cpus;
283 CPU_ZERO(&cpus);
284 CPU_SET(0, &cpus);
285 sched_setaffinity(0, sizeof(cpus), &cpus);
286 }
287 #endif
288 }
289
290 /*static*/ void
291 nsThread::ThreadFunc(void *arg)
292 {
293 nsThread *self = static_cast<nsThread *>(arg); // strong reference
294 self->mThread = PR_GetCurrentThread();
295 SetupCurrentThreadForChaosMode();
296
297 // Inform the ThreadManager
298 nsThreadManager::get()->RegisterCurrentThread(self);
299
300 mozilla::IOInterposer::RegisterCurrentThread();
301
302 // Wait for and process startup event
303 nsCOMPtr<nsIRunnable> event;
304 if (!self->GetEvent(true, getter_AddRefs(event))) {
305 NS_WARNING("failed waiting for thread startup event");
306 return;
307 }
308 event->Run(); // unblocks nsThread::Init
309 event = nullptr;
310
311 { // Scope for MessageLoop.
312 nsAutoPtr<MessageLoop> loop(
313 new MessageLoop(MessageLoop::TYPE_MOZILLA_NONMAINTHREAD));
314
315 // Now, process incoming events...
316 loop->Run();
317
318 // Do NS_ProcessPendingEvents but with special handling to set
319 // mEventsAreDoomed atomically with the removal of the last event. The key
320 // invariant here is that we will never permit PutEvent to succeed if the
321 // event would be left in the queue after our final call to
322 // NS_ProcessPendingEvents.
323 while (true) {
324 {
325 MutexAutoLock lock(self->mLock);
326 if (!self->mEvents->HasPendingEvent()) {
327 // No events in the queue, so we will stop now. Don't let any more
328 // events be added, since they won't be processed. It is critical
329 // that no PutEvent can occur between testing that the event queue is
330 // empty and setting mEventsAreDoomed!
331 self->mEventsAreDoomed = true;
332 break;
333 }
334 }
335 NS_ProcessPendingEvents(self);
336 }
337 }
338
339 mozilla::IOInterposer::UnregisterCurrentThread();
340
341 // Inform the threadmanager that this thread is going away
342 nsThreadManager::get()->UnregisterCurrentThread(self);
343
344 // Dispatch shutdown ACK
345 event = new nsThreadShutdownAckEvent(self->mShutdownContext);
346 self->mShutdownContext->joiningThread->Dispatch(event, NS_DISPATCH_NORMAL);
347
348 // Release any observer of the thread here.
349 self->SetObserver(nullptr);
350
351 #ifdef MOZ_TASK_TRACER
352 FreeTraceInfo();
353 #endif
354
355 NS_RELEASE(self);
356 }
357
358 //-----------------------------------------------------------------------------
359
360 #ifdef MOZ_CANARY
361 int sCanaryOutputFD = -1;
362 #endif
363
364 nsThread::nsThread(MainThreadFlag aMainThread, uint32_t aStackSize)
365 : mLock("nsThread.mLock")
366 , mEvents(&mEventsRoot)
367 , mPriority(PRIORITY_NORMAL)
368 , mThread(nullptr)
369 , mRunningEvent(0)
370 , mStackSize(aStackSize)
371 , mShutdownContext(nullptr)
372 , mShutdownRequired(false)
373 , mEventsAreDoomed(false)
374 , mIsMainThread(aMainThread)
375 {
376 }
377
378 nsThread::~nsThread()
379 {
380 }
381
382 nsresult
383 nsThread::Init()
384 {
385 // spawn thread and wait until it is fully setup
386 nsRefPtr<nsThreadStartupEvent> startup = new nsThreadStartupEvent();
387
388 NS_ADDREF_THIS();
389
390 mShutdownRequired = true;
391
392 // ThreadFunc is responsible for setting mThread
393 PRThread *thr = PR_CreateThread(PR_USER_THREAD, ThreadFunc, this,
394 PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
395 PR_JOINABLE_THREAD, mStackSize);
396 if (!thr) {
397 NS_RELEASE_THIS();
398 return NS_ERROR_OUT_OF_MEMORY;
399 }
400
401 // ThreadFunc will wait for this event to be run before it tries to access
402 // mThread. By delaying insertion of this event into the queue, we ensure
403 // that mThread is set properly.
404 {
405 MutexAutoLock lock(mLock);
406 mEventsRoot.PutEvent(startup);
407 }
408
409 // Wait for thread to call ThreadManager::SetupCurrentThread, which completes
410 // initialization of ThreadFunc.
411 startup->Wait();
412 return NS_OK;
413 }
414
415 nsresult
416 nsThread::InitCurrentThread()
417 {
418 mThread = PR_GetCurrentThread();
419 SetupCurrentThreadForChaosMode();
420
421 nsThreadManager::get()->RegisterCurrentThread(this);
422 return NS_OK;
423 }
424
425 nsresult
426 nsThread::PutEvent(nsIRunnable *event, nsNestedEventTarget *target)
427 {
428 {
429 MutexAutoLock lock(mLock);
430 nsChainedEventQueue *queue = target ? target->mQueue : &mEventsRoot;
431 if (!queue || (queue == &mEventsRoot && mEventsAreDoomed)) {
432 NS_WARNING("An event was posted to a thread that will never run it (rejected)");
433 return NS_ERROR_UNEXPECTED;
434 }
435 if (!queue->PutEvent(event))
436 return NS_ERROR_OUT_OF_MEMORY;
437 }
438
439 nsCOMPtr<nsIThreadObserver> obs = GetObserver();
440 if (obs)
441 obs->OnDispatchedEvent(this);
442
443 return NS_OK;
444 }
445
446 nsresult
447 nsThread::DispatchInternal(nsIRunnable *event, uint32_t flags,
448 nsNestedEventTarget *target)
449 {
450 if (NS_WARN_IF(!event))
451 return NS_ERROR_INVALID_ARG;
452
453 if (gXPCOMThreadsShutDown && MAIN_THREAD != mIsMainThread && !target) {
454 return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
455 }
456
457 #ifdef MOZ_TASK_TRACER
458 nsRefPtr<nsIRunnable> tracedRunnable = CreateTracedRunnable(event);
459 event = tracedRunnable;
460 #endif
461
462 if (flags & DISPATCH_SYNC) {
463 nsThread *thread = nsThreadManager::get()->GetCurrentThread();
464 if (NS_WARN_IF(!thread))
465 return NS_ERROR_NOT_AVAILABLE;
466
467 // XXX we should be able to do something better here... we should
468 // be able to monitor the slot occupied by this event and use
469 // that to tell us when the event has been processed.
470
471 nsRefPtr<nsThreadSyncDispatch> wrapper =
472 new nsThreadSyncDispatch(thread, event);
473 if (!wrapper)
474 return NS_ERROR_OUT_OF_MEMORY;
475 nsresult rv = PutEvent(wrapper, target);
476 // Don't wait for the event to finish if we didn't dispatch it...
477 if (NS_FAILED(rv))
478 return rv;
479
480 // Allows waiting; ensure no locks are held that would deadlock us!
481 while (wrapper->IsPending())
482 NS_ProcessNextEvent(thread, true);
483 return wrapper->Result();
484 }
485
486 NS_ASSERTION(flags == NS_DISPATCH_NORMAL, "unexpected dispatch flags");
487 return PutEvent(event, target);
488 }
489
490 //-----------------------------------------------------------------------------
491 // nsIEventTarget
492
493 NS_IMETHODIMP
494 nsThread::Dispatch(nsIRunnable *event, uint32_t flags)
495 {
496 LOG(("THRD(%p) Dispatch [%p %x]\n", this, event, flags));
497
498 return DispatchInternal(event, flags, nullptr);
499 }
500
501 NS_IMETHODIMP
502 nsThread::IsOnCurrentThread(bool *result)
503 {
504 *result = (PR_GetCurrentThread() == mThread);
505 return NS_OK;
506 }
507
508 //-----------------------------------------------------------------------------
509 // nsIThread
510
511 NS_IMETHODIMP
512 nsThread::GetPRThread(PRThread **result)
513 {
514 *result = mThread;
515 return NS_OK;
516 }
517
518 NS_IMETHODIMP
519 nsThread::Shutdown()
520 {
521 LOG(("THRD(%p) shutdown\n", this));
522
523 // XXX If we make this warn, then we hit that warning at xpcom shutdown while
524 // shutting down a thread in a thread pool. That happens b/c the thread
525 // in the thread pool is already shutdown by the thread manager.
526 if (!mThread)
527 return NS_OK;
528
529 if (NS_WARN_IF(mThread == PR_GetCurrentThread()))
530 return NS_ERROR_UNEXPECTED;
531
532 // Prevent multiple calls to this method
533 {
534 MutexAutoLock lock(mLock);
535 if (!mShutdownRequired)
536 return NS_ERROR_UNEXPECTED;
537 mShutdownRequired = false;
538 }
539
540 nsThreadShutdownContext context;
541 context.joiningThread = nsThreadManager::get()->GetCurrentThread();
542 context.shutdownAck = false;
543
544 // Set mShutdownContext and wake up the thread in case it is waiting for
545 // events to process.
546 nsCOMPtr<nsIRunnable> event = new nsThreadShutdownEvent(this, &context);
547 if (!event)
548 return NS_ERROR_OUT_OF_MEMORY;
549 // XXXroc What if posting the event fails due to OOM?
550 PutEvent(event, nullptr);
551
552 // We could still end up with other events being added after the shutdown
553 // task, but that's okay because we process pending events in ThreadFunc
554 // after setting mShutdownContext just before exiting.
555
556 // Process events on the current thread until we receive a shutdown ACK.
557 // Allows waiting; ensure no locks are held that would deadlock us!
558 while (!context.shutdownAck)
559 NS_ProcessNextEvent(context.joiningThread, true);
560
561 // Now, it should be safe to join without fear of dead-locking.
562
563 PR_JoinThread(mThread);
564 mThread = nullptr;
565
566 // We hold strong references to our event observers, and once the thread is
567 // shut down the observers can't easily unregister themselves. Do it here
568 // to avoid leaking.
569 ClearObservers();
570
571 #ifdef DEBUG
572 {
573 MutexAutoLock lock(mLock);
574 MOZ_ASSERT(!mObserver, "Should have been cleared at shutdown!");
575 }
576 #endif
577
578 return NS_OK;
579 }
580
581 NS_IMETHODIMP
582 nsThread::HasPendingEvents(bool *result)
583 {
584 if (NS_WARN_IF(PR_GetCurrentThread() != mThread))
585 return NS_ERROR_NOT_SAME_THREAD;
586
587 *result = mEvents->GetEvent(false, nullptr);
588 return NS_OK;
589 }
590
591 #ifdef MOZ_CANARY
592 void canary_alarm_handler (int signum);
593
594 class Canary {
595 //XXX ToDo: support nested loops
596 public:
597 Canary() {
598 if (sCanaryOutputFD > 0 && EventLatencyIsImportant()) {
599 signal(SIGALRM, canary_alarm_handler);
600 ualarm(15000, 0);
601 }
602 }
603
604 ~Canary() {
605 if (sCanaryOutputFD != 0 && EventLatencyIsImportant())
606 ualarm(0, 0);
607 }
608
609 static bool EventLatencyIsImportant() {
610 return NS_IsMainThread() && XRE_GetProcessType() == GeckoProcessType_Default;
611 }
612 };
613
614 void canary_alarm_handler (int signum)
615 {
616 void *array[30];
617 const char msg[29] = "event took too long to run:\n";
618 // use write to be safe in the signal handler
619 write(sCanaryOutputFD, msg, sizeof(msg));
620 backtrace_symbols_fd(array, backtrace(array, 30), sCanaryOutputFD);
621 }
622
623 #endif
624
625 #define NOTIFY_EVENT_OBSERVERS(func_, params_) \
626 PR_BEGIN_MACRO \
627 if (!mEventObservers.IsEmpty()) { \
628 nsAutoTObserverArray<nsCOMPtr<nsIThreadObserver>, 2>::ForwardIterator \
629 iter_(mEventObservers); \
630 nsCOMPtr<nsIThreadObserver> obs_; \
631 while (iter_.HasMore()) { \
632 obs_ = iter_.GetNext(); \
633 obs_ -> func_ params_ ; \
634 } \
635 } \
636 PR_END_MACRO
637
638 NS_IMETHODIMP
639 nsThread::ProcessNextEvent(bool mayWait, bool *result)
640 {
641 LOG(("THRD(%p) ProcessNextEvent [%u %u]\n", this, mayWait, mRunningEvent));
642
643 if (NS_WARN_IF(PR_GetCurrentThread() != mThread))
644 return NS_ERROR_NOT_SAME_THREAD;
645
646 // The toplevel event loop normally blocks waiting for the next event, but
647 // if we're trying to shut this thread down, we must exit the event loop when
648 // the event queue is empty.
649 // This only applys to the toplevel event loop! Nested event loops (e.g.
650 // during sync dispatch) are waiting for some state change and must be able
651 // to block even if something has requested shutdown of the thread. Otherwise
652 // we'll just busywait as we endlessly look for an event, fail to find one,
653 // and repeat the nested event loop since its state change hasn't happened yet.
654 bool reallyWait = mayWait && (mRunningEvent > 0 || !ShuttingDown());
655
656 if (MAIN_THREAD == mIsMainThread && reallyWait)
657 HangMonitor::Suspend();
658
659 // Fire a memory pressure notification, if we're the main thread and one is
660 // pending.
661 if (MAIN_THREAD == mIsMainThread && !ShuttingDown()) {
662 MemoryPressureState mpPending = NS_GetPendingMemoryPressure();
663 if (mpPending != MemPressure_None) {
664 nsCOMPtr<nsIObserverService> os = services::GetObserverService();
665
666 // Use no-forward to prevent the notifications from being transferred to
667 // the children of this process.
668 NS_NAMED_LITERAL_STRING(lowMem, "low-memory-no-forward");
669 NS_NAMED_LITERAL_STRING(lowMemOngoing, "low-memory-ongoing-no-forward");
670
671 if (os) {
672 os->NotifyObservers(nullptr, "memory-pressure",
673 mpPending == MemPressure_New ? lowMem.get() :
674 lowMemOngoing.get());
675 } else {
676 NS_WARNING("Can't get observer service!");
677 }
678 }
679 }
680
681 bool notifyMainThreadObserver =
682 (MAIN_THREAD == mIsMainThread) && sMainThreadObserver;
683 if (notifyMainThreadObserver)
684 sMainThreadObserver->OnProcessNextEvent(this, reallyWait, mRunningEvent);
685
686 nsCOMPtr<nsIThreadObserver> obs = mObserver;
687 if (obs)
688 obs->OnProcessNextEvent(this, reallyWait, mRunningEvent);
689
690 NOTIFY_EVENT_OBSERVERS(OnProcessNextEvent,
691 (this, reallyWait, mRunningEvent));
692
693 ++mRunningEvent;
694
695 #ifdef MOZ_CANARY
696 Canary canary;
697 #endif
698 nsresult rv = NS_OK;
699
700 {
701 // Scope for |event| to make sure that its destructor fires while
702 // mRunningEvent has been incremented, since that destructor can
703 // also do work.
704
705 // If we are shutting down, then do not wait for new events.
706 nsCOMPtr<nsIRunnable> event;
707 mEvents->GetEvent(reallyWait, getter_AddRefs(event));
708
709 *result = (event.get() != nullptr);
710
711 if (event) {
712 LOG(("THRD(%p) running [%p]\n", this, event.get()));
713 if (MAIN_THREAD == mIsMainThread)
714 HangMonitor::NotifyActivity();
715 event->Run();
716 } else if (mayWait) {
717 MOZ_ASSERT(ShuttingDown(),
718 "This should only happen when shutting down");
719 rv = NS_ERROR_UNEXPECTED;
720 }
721 }
722
723 --mRunningEvent;
724
725 NOTIFY_EVENT_OBSERVERS(AfterProcessNextEvent,
726 (this, mRunningEvent, *result));
727
728 if (obs)
729 obs->AfterProcessNextEvent(this, mRunningEvent, *result);
730
731 if (notifyMainThreadObserver && sMainThreadObserver)
732 sMainThreadObserver->AfterProcessNextEvent(this, mRunningEvent, *result);
733
734 return rv;
735 }
736
737 //-----------------------------------------------------------------------------
738 // nsISupportsPriority
739
740 NS_IMETHODIMP
741 nsThread::GetPriority(int32_t *priority)
742 {
743 *priority = mPriority;
744 return NS_OK;
745 }
746
747 NS_IMETHODIMP
748 nsThread::SetPriority(int32_t priority)
749 {
750 if (NS_WARN_IF(!mThread))
751 return NS_ERROR_NOT_INITIALIZED;
752
753 // NSPR defines the following four thread priorities:
754 // PR_PRIORITY_LOW
755 // PR_PRIORITY_NORMAL
756 // PR_PRIORITY_HIGH
757 // PR_PRIORITY_URGENT
758 // We map the priority values defined on nsISupportsPriority to these values.
759
760 mPriority = priority;
761
762 PRThreadPriority pri;
763 if (mPriority <= PRIORITY_HIGHEST) {
764 pri = PR_PRIORITY_URGENT;
765 } else if (mPriority < PRIORITY_NORMAL) {
766 pri = PR_PRIORITY_HIGH;
767 } else if (mPriority > PRIORITY_NORMAL) {
768 pri = PR_PRIORITY_LOW;
769 } else {
770 pri = PR_PRIORITY_NORMAL;
771 }
772 // If chaos mode is active, retain the randomly chosen priority
773 if (!ChaosMode::isActive()) {
774 PR_SetThreadPriority(mThread, pri);
775 }
776
777 return NS_OK;
778 }
779
780 NS_IMETHODIMP
781 nsThread::AdjustPriority(int32_t delta)
782 {
783 return SetPriority(mPriority + delta);
784 }
785
786 //-----------------------------------------------------------------------------
787 // nsIThreadInternal
788
789 NS_IMETHODIMP
790 nsThread::GetObserver(nsIThreadObserver **obs)
791 {
792 MutexAutoLock lock(mLock);
793 NS_IF_ADDREF(*obs = mObserver);
794 return NS_OK;
795 }
796
797 NS_IMETHODIMP
798 nsThread::SetObserver(nsIThreadObserver *obs)
799 {
800 if (NS_WARN_IF(PR_GetCurrentThread() != mThread))
801 return NS_ERROR_NOT_SAME_THREAD;
802
803 MutexAutoLock lock(mLock);
804 mObserver = obs;
805 return NS_OK;
806 }
807
808 NS_IMETHODIMP
809 nsThread::GetRecursionDepth(uint32_t *depth)
810 {
811 if (NS_WARN_IF(PR_GetCurrentThread() != mThread))
812 return NS_ERROR_NOT_SAME_THREAD;
813
814 *depth = mRunningEvent;
815 return NS_OK;
816 }
817
818 NS_IMETHODIMP
819 nsThread::AddObserver(nsIThreadObserver *observer)
820 {
821 if (NS_WARN_IF(!observer))
822 return NS_ERROR_INVALID_ARG;
823 if (NS_WARN_IF(PR_GetCurrentThread() != mThread))
824 return NS_ERROR_NOT_SAME_THREAD;
825
826 NS_WARN_IF_FALSE(!mEventObservers.Contains(observer),
827 "Adding an observer twice!");
828
829 if (!mEventObservers.AppendElement(observer)) {
830 NS_WARNING("Out of memory!");
831 return NS_ERROR_OUT_OF_MEMORY;
832 }
833
834 return NS_OK;
835 }
836
837 NS_IMETHODIMP
838 nsThread::RemoveObserver(nsIThreadObserver *observer)
839 {
840 if (NS_WARN_IF(PR_GetCurrentThread() != mThread))
841 return NS_ERROR_NOT_SAME_THREAD;
842
843 if (observer && !mEventObservers.RemoveElement(observer)) {
844 NS_WARNING("Removing an observer that was never added!");
845 }
846
847 return NS_OK;
848 }
849
850 NS_IMETHODIMP
851 nsThread::PushEventQueue(nsIEventTarget **result)
852 {
853 if (NS_WARN_IF(PR_GetCurrentThread() != mThread))
854 return NS_ERROR_NOT_SAME_THREAD;
855
856 nsChainedEventQueue *queue = new nsChainedEventQueue();
857 queue->mEventTarget = new nsNestedEventTarget(this, queue);
858
859 {
860 MutexAutoLock lock(mLock);
861 queue->mNext = mEvents;
862 mEvents = queue;
863 }
864
865 NS_ADDREF(*result = queue->mEventTarget);
866 return NS_OK;
867 }
868
869 NS_IMETHODIMP
870 nsThread::PopEventQueue(nsIEventTarget *innermostTarget)
871 {
872 if (NS_WARN_IF(PR_GetCurrentThread() != mThread))
873 return NS_ERROR_NOT_SAME_THREAD;
874
875 if (NS_WARN_IF(!innermostTarget))
876 return NS_ERROR_NULL_POINTER;
877
878 // Don't delete or release anything while holding the lock.
879 nsAutoPtr<nsChainedEventQueue> queue;
880 nsRefPtr<nsNestedEventTarget> target;
881
882 {
883 MutexAutoLock lock(mLock);
884
885 // Make sure we're popping the innermost event target.
886 if (NS_WARN_IF(mEvents->mEventTarget != innermostTarget))
887 return NS_ERROR_UNEXPECTED;
888
889 MOZ_ASSERT(mEvents != &mEventsRoot);
890
891 queue = mEvents;
892 mEvents = mEvents->mNext;
893
894 nsCOMPtr<nsIRunnable> event;
895 while (queue->GetEvent(false, getter_AddRefs(event)))
896 mEvents->PutEvent(event);
897
898 // Don't let the event target post any more events.
899 queue->mEventTarget.swap(target);
900 target->mQueue = nullptr;
901 }
902
903 return NS_OK;
904 }
905
906 nsresult
907 nsThread::SetMainThreadObserver(nsIThreadObserver* aObserver)
908 {
909 if (aObserver && nsThread::sMainThreadObserver) {
910 return NS_ERROR_NOT_AVAILABLE;
911 }
912
913 if (!NS_IsMainThread()) {
914 return NS_ERROR_UNEXPECTED;
915 }
916
917 nsThread::sMainThreadObserver = aObserver;
918 return NS_OK;
919 }
920
921 //-----------------------------------------------------------------------------
922
923 NS_IMETHODIMP
924 nsThreadSyncDispatch::Run()
925 {
926 if (mSyncTask) {
927 mResult = mSyncTask->Run();
928 mSyncTask = nullptr;
929 // unblock the origin thread
930 mOrigin->Dispatch(this, NS_DISPATCH_NORMAL);
931 }
932 return NS_OK;
933 }
934
935 //-----------------------------------------------------------------------------
936
937 NS_IMPL_ISUPPORTS(nsThread::nsNestedEventTarget, nsIEventTarget)
938
939 NS_IMETHODIMP
940 nsThread::nsNestedEventTarget::Dispatch(nsIRunnable *event, uint32_t flags)
941 {
942 LOG(("THRD(%p) Dispatch [%p %x] to nested loop %p\n", mThread.get(), event,
943 flags, this));
944
945 return mThread->DispatchInternal(event, flags, this);
946 }
947
948 NS_IMETHODIMP
949 nsThread::nsNestedEventTarget::IsOnCurrentThread(bool *result)
950 {
951 return mThread->IsOnCurrentThread(result);
952 }

mercurial