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 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is Mozilla code.
17 : *
18 : * The Initial Developer of the Original Code is Google Inc.
19 : * Portions created by the Initial Developer are Copyright (C) 2006
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Darin Fisher <darin@meer.net>
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either the GNU General Public License Version 2 or later (the "GPL"), or
27 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "mozilla/ReentrantMonitor.h"
40 : #include "nsThread.h"
41 : #include "nsThreadManager.h"
42 : #include "nsIClassInfoImpl.h"
43 : #include "nsIProgrammingLanguage.h"
44 : #include "nsAutoPtr.h"
45 : #include "nsCOMPtr.h"
46 : #include "prlog.h"
47 : #include "nsIObserverService.h"
48 : #include "mozilla/HangMonitor.h"
49 : #include "mozilla/Services.h"
50 :
51 : #define HAVE_UALARM _BSD_SOURCE || (_XOPEN_SOURCE >= 500 || \
52 : _XOPEN_SOURCE && _XOPEN_SOURCE_EXTENDED) && \
53 : !(_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700)
54 :
55 : #if defined(XP_UNIX) && !defined(ANDROID) && !defined(DEBUG) && HAVE_UALARM \
56 : && defined(_GNU_SOURCE)
57 : # define MOZ_CANARY
58 : # include <unistd.h>
59 : # include <execinfo.h>
60 : # include <signal.h>
61 : # include <fcntl.h>
62 : # include "nsXULAppAPI.h"
63 : #endif
64 :
65 : #include "mozilla/FunctionTimer.h"
66 : #if defined(NS_FUNCTION_TIMER) && defined(_MSC_VER)
67 : #include "nsTimerImpl.h"
68 : #include "nsStackWalk.h"
69 : #endif
70 : #ifdef NS_FUNCTION_TIMER
71 : #include "nsCRT.h"
72 : #endif
73 :
74 : using namespace mozilla;
75 :
76 : #ifdef PR_LOGGING
77 1464 : static PRLogModuleInfo *sLog = PR_NewLogModule("nsThread");
78 : #endif
79 : #define LOG(args) PR_LOG(sLog, PR_LOG_DEBUG, args)
80 :
81 : NS_DECL_CI_INTERFACE_GETTER(nsThread)
82 :
83 : namespace mozilla {
84 :
85 : // Fun fact: Android's GCC won't convert bool* to PRInt32*, so we can't
86 : // PR_ATOMIC_SET a bool.
87 : static PRInt32 sMemoryPressurePending = 0;
88 :
89 : /*
90 : * It's important that this function not acquire any locks, nor do anything
91 : * which might cause malloc to run.
92 : */
93 0 : void ScheduleMemoryPressureEvent()
94 : {
95 0 : PR_ATOMIC_SET(&sMemoryPressurePending, 1);
96 0 : }
97 :
98 : } // namespace mozilla
99 :
100 : //-----------------------------------------------------------------------------
101 : // Because we do not have our own nsIFactory, we have to implement nsIClassInfo
102 : // somewhat manually.
103 :
104 : class nsThreadClassInfo : public nsIClassInfo {
105 : public:
106 : NS_DECL_ISUPPORTS_INHERITED // no mRefCnt
107 : NS_DECL_NSICLASSINFO
108 :
109 1464 : nsThreadClassInfo() {}
110 : };
111 :
112 1464 : static nsThreadClassInfo sThreadClassInfo;
113 :
114 4617 : NS_IMETHODIMP_(nsrefcnt) nsThreadClassInfo::AddRef() { return 2; }
115 4617 : NS_IMETHODIMP_(nsrefcnt) nsThreadClassInfo::Release() { return 1; }
116 3095 : NS_IMPL_QUERY_INTERFACE1(nsThreadClassInfo, nsIClassInfo)
117 :
118 : NS_IMETHODIMP
119 1230 : nsThreadClassInfo::GetInterfaces(PRUint32 *count, nsIID ***array)
120 : {
121 1230 : return NS_CI_INTERFACE_GETTER_NAME(nsThread)(count, array);
122 : }
123 :
124 : NS_IMETHODIMP
125 1573 : nsThreadClassInfo::GetHelperForLanguage(PRUint32 lang, nsISupports **result)
126 : {
127 1573 : *result = nsnull;
128 1573 : return NS_OK;
129 : }
130 :
131 : NS_IMETHODIMP
132 0 : nsThreadClassInfo::GetContractID(char **result)
133 : {
134 0 : *result = nsnull;
135 0 : return NS_OK;
136 : }
137 :
138 : NS_IMETHODIMP
139 0 : nsThreadClassInfo::GetClassDescription(char **result)
140 : {
141 0 : *result = nsnull;
142 0 : return NS_OK;
143 : }
144 :
145 : NS_IMETHODIMP
146 0 : nsThreadClassInfo::GetClassID(nsCID **result)
147 : {
148 0 : *result = nsnull;
149 0 : return NS_OK;
150 : }
151 :
152 : NS_IMETHODIMP
153 0 : nsThreadClassInfo::GetImplementationLanguage(PRUint32 *result)
154 : {
155 0 : *result = nsIProgrammingLanguage::CPLUSPLUS;
156 0 : return NS_OK;
157 : }
158 :
159 : NS_IMETHODIMP
160 1847 : nsThreadClassInfo::GetFlags(PRUint32 *result)
161 : {
162 1847 : *result = THREADSAFE;
163 1847 : return NS_OK;
164 : }
165 :
166 : NS_IMETHODIMP
167 0 : nsThreadClassInfo::GetClassIDNoAlloc(nsCID *result)
168 : {
169 0 : return NS_ERROR_NOT_AVAILABLE;
170 : }
171 :
172 : //-----------------------------------------------------------------------------
173 :
174 730806 : NS_IMPL_THREADSAFE_ADDREF(nsThread)
175 738286 : NS_IMPL_THREADSAFE_RELEASE(nsThread)
176 435702 : NS_INTERFACE_MAP_BEGIN(nsThread)
177 435702 : NS_INTERFACE_MAP_ENTRY(nsIThread)
178 281388 : NS_INTERFACE_MAP_ENTRY(nsIThreadInternal)
179 276595 : NS_INTERFACE_MAP_ENTRY(nsIEventTarget)
180 117986 : NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
181 117925 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIThread)
182 62677 : if (aIID.Equals(NS_GET_IID(nsIClassInfo))) {
183 1573 : foundInterface = static_cast<nsIClassInfo*>(&sThreadClassInfo);
184 : } else
185 61104 : NS_INTERFACE_MAP_END
186 1230 : NS_IMPL_CI_INTERFACE_GETTER4(nsThread, nsIThread, nsIThreadInternal,
187 1230 : nsIEventTarget, nsISupportsPriority)
188 :
189 : //-----------------------------------------------------------------------------
190 :
191 : class nsThreadStartupEvent : public nsRunnable {
192 : public:
193 : // Create a new thread startup object.
194 6106 : static nsThreadStartupEvent *Create() {
195 6106 : return new nsThreadStartupEvent();
196 : }
197 :
198 : // This method does not return until the thread startup object is in the
199 : // completion state.
200 6106 : void Wait() {
201 6106 : if (mInitialized) // Maybe avoid locking...
202 0 : return;
203 12212 : ReentrantMonitorAutoEnter mon(mMon);
204 18318 : while (!mInitialized)
205 6106 : mon.Wait();
206 : }
207 :
208 : // This method needs to be public to support older compilers (xlC_r on AIX).
209 : // It should be called directly as this class type is reference counted.
210 12212 : virtual ~nsThreadStartupEvent() {
211 24424 : }
212 :
213 : private:
214 6106 : NS_IMETHOD Run() {
215 12212 : ReentrantMonitorAutoEnter mon(mMon);
216 6106 : mInitialized = true;
217 6106 : mon.Notify();
218 6106 : return NS_OK;
219 : }
220 :
221 6106 : nsThreadStartupEvent()
222 : : mMon("nsThreadStartupEvent.mMon")
223 6106 : , mInitialized(false) {
224 6106 : }
225 :
226 : ReentrantMonitor mMon;
227 : bool mInitialized;
228 : };
229 :
230 : //-----------------------------------------------------------------------------
231 :
232 : struct nsThreadShutdownContext {
233 : nsThread *joiningThread;
234 : bool shutdownAck;
235 : };
236 :
237 : // This event is responsible for notifying nsThread::Shutdown that it is time
238 : // to call PR_JoinThread.
239 24424 : class nsThreadShutdownAckEvent : public nsRunnable {
240 : public:
241 6106 : nsThreadShutdownAckEvent(nsThreadShutdownContext *ctx)
242 6106 : : mShutdownContext(ctx) {
243 6106 : }
244 6106 : NS_IMETHOD Run() {
245 6106 : mShutdownContext->shutdownAck = true;
246 6106 : return NS_OK;
247 : }
248 : private:
249 : nsThreadShutdownContext *mShutdownContext;
250 : };
251 :
252 : // This event is responsible for setting mShutdownContext
253 24424 : class nsThreadShutdownEvent : public nsRunnable {
254 : public:
255 6106 : nsThreadShutdownEvent(nsThread *thr, nsThreadShutdownContext *ctx)
256 6106 : : mThread(thr), mShutdownContext(ctx) {
257 6106 : }
258 6106 : NS_IMETHOD Run() {
259 6106 : mThread->mShutdownContext = mShutdownContext;
260 6106 : return NS_OK;
261 : }
262 : private:
263 : nsRefPtr<nsThread> mThread;
264 : nsThreadShutdownContext *mShutdownContext;
265 : };
266 :
267 : //-----------------------------------------------------------------------------
268 :
269 : /*static*/ void
270 6106 : nsThread::ThreadFunc(void *arg)
271 : {
272 6106 : nsThread *self = static_cast<nsThread *>(arg); // strong reference
273 6106 : self->mThread = PR_GetCurrentThread();
274 :
275 : // Inform the ThreadManager
276 6106 : nsThreadManager::get()->RegisterCurrentThread(self);
277 :
278 : // Wait for and process startup event
279 12212 : nsCOMPtr<nsIRunnable> event;
280 6106 : if (!self->GetEvent(true, getter_AddRefs(event))) {
281 0 : NS_WARNING("failed waiting for thread startup event");
282 : return;
283 : }
284 6106 : event->Run(); // unblocks nsThread::Init
285 6106 : event = nsnull;
286 :
287 : // Now, process incoming events...
288 83391 : while (!self->ShuttingDown())
289 71179 : NS_ProcessNextEvent(self);
290 :
291 : // Do NS_ProcessPendingEvents but with special handling to set
292 : // mEventsAreDoomed atomically with the removal of the last event. The key
293 : // invariant here is that we will never permit PutEvent to succeed if the
294 : // event would be left in the queue after our final call to
295 : // NS_ProcessPendingEvents.
296 0 : while (true) {
297 : {
298 12212 : MutexAutoLock lock(self->mLock);
299 6106 : if (!self->mEvents->HasPendingEvent()) {
300 : // No events in the queue, so we will stop now. Don't let any more
301 : // events be added, since they won't be processed. It is critical
302 : // that no PutEvent can occur between testing that the event queue is
303 : // empty and setting mEventsAreDoomed!
304 6106 : self->mEventsAreDoomed = true;
305 : break;
306 : }
307 : }
308 0 : NS_ProcessPendingEvents(self);
309 : }
310 :
311 : // Inform the threadmanager that this thread is going away
312 6106 : nsThreadManager::get()->UnregisterCurrentThread(self);
313 :
314 : // Dispatch shutdown ACK
315 6106 : event = new nsThreadShutdownAckEvent(self->mShutdownContext);
316 6106 : self->mShutdownContext->joiningThread->Dispatch(event, NS_DISPATCH_NORMAL);
317 :
318 : // Release any observer of the thread here.
319 6106 : self->SetObserver(nsnull);
320 :
321 6106 : NS_RELEASE(self);
322 : }
323 :
324 : //-----------------------------------------------------------------------------
325 :
326 7525 : nsThread::nsThread(MainThreadFlag aMainThread, PRUint32 aStackSize)
327 : : mLock("nsThread.mLock")
328 : , mEvents(&mEventsRoot)
329 : , mPriority(PRIORITY_NORMAL)
330 : , mThread(nsnull)
331 : , mRunningEvent(0)
332 : , mStackSize(aStackSize)
333 : , mShutdownContext(nsnull)
334 : , mShutdownRequired(false)
335 : , mEventsAreDoomed(false)
336 7525 : , mIsMainThread(aMainThread)
337 : {
338 7525 : }
339 :
340 7514 : nsThread::~nsThread()
341 : {
342 7514 : }
343 :
344 : nsresult
345 6106 : nsThread::Init()
346 : {
347 : // spawn thread and wait until it is fully setup
348 12212 : nsRefPtr<nsThreadStartupEvent> startup = nsThreadStartupEvent::Create();
349 6106 : NS_ENSURE_TRUE(startup, NS_ERROR_OUT_OF_MEMORY);
350 :
351 6106 : NS_ADDREF_THIS();
352 :
353 6106 : mShutdownRequired = true;
354 :
355 : // ThreadFunc is responsible for setting mThread
356 : PRThread *thr = PR_CreateThread(PR_USER_THREAD, ThreadFunc, this,
357 : PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
358 6106 : PR_JOINABLE_THREAD, mStackSize);
359 6106 : if (!thr) {
360 0 : NS_RELEASE_THIS();
361 0 : return NS_ERROR_OUT_OF_MEMORY;
362 : }
363 :
364 : // ThreadFunc will wait for this event to be run before it tries to access
365 : // mThread. By delaying insertion of this event into the queue, we ensure
366 : // that mThread is set properly.
367 : {
368 12212 : MutexAutoLock lock(mLock);
369 6106 : mEvents->PutEvent(startup);
370 : }
371 :
372 : // Wait for thread to call ThreadManager::SetupCurrentThread, which completes
373 : // initialization of ThreadFunc.
374 6106 : startup->Wait();
375 6106 : return NS_OK;
376 : }
377 :
378 : nsresult
379 1419 : nsThread::InitCurrentThread()
380 : {
381 1419 : mThread = PR_GetCurrentThread();
382 :
383 1419 : nsThreadManager::get()->RegisterCurrentThread(this);
384 1419 : return NS_OK;
385 : }
386 :
387 : nsresult
388 286514 : nsThread::PutEvent(nsIRunnable *event)
389 : {
390 : {
391 573025 : MutexAutoLock lock(mLock);
392 286514 : if (mEventsAreDoomed) {
393 0 : NS_WARNING("An event was posted to a thread that will never run it (rejected)");
394 0 : return NS_ERROR_UNEXPECTED;
395 : }
396 286514 : if (!mEvents->PutEvent(event))
397 0 : return NS_ERROR_OUT_OF_MEMORY;
398 : }
399 :
400 573026 : nsCOMPtr<nsIThreadObserver> obs = GetObserver();
401 286513 : if (obs)
402 213845 : obs->OnDispatchedEvent(this);
403 :
404 286513 : return NS_OK;
405 : }
406 :
407 : //-----------------------------------------------------------------------------
408 : // nsIEventTarget
409 :
410 : NS_IMETHODIMP
411 280408 : nsThread::Dispatch(nsIRunnable *event, PRUint32 flags)
412 : {
413 280408 : LOG(("THRD(%p) Dispatch [%p %x]\n", this, event, flags));
414 :
415 280405 : NS_ENSURE_ARG_POINTER(event);
416 :
417 280405 : if (flags & DISPATCH_SYNC) {
418 1 : nsThread *thread = nsThreadManager::get()->GetCurrentThread();
419 1 : NS_ENSURE_STATE(thread);
420 :
421 : // XXX we should be able to do something better here... we should
422 : // be able to monitor the slot occupied by this event and use
423 : // that to tell us when the event has been processed.
424 :
425 : nsRefPtr<nsThreadSyncDispatch> wrapper =
426 2 : new nsThreadSyncDispatch(thread, event);
427 1 : if (!wrapper)
428 0 : return NS_ERROR_OUT_OF_MEMORY;
429 1 : nsresult rv = PutEvent(wrapper);
430 : // Don't wait for the event to finish if we didn't dispatch it...
431 1 : if (NS_FAILED(rv))
432 0 : return rv;
433 :
434 3 : while (wrapper->IsPending())
435 1 : NS_ProcessNextEvent(thread);
436 1 : return wrapper->Result();
437 : }
438 :
439 280404 : NS_ASSERTION(flags == NS_DISPATCH_NORMAL, "unexpected dispatch flags");
440 280404 : return PutEvent(event);
441 : }
442 :
443 : NS_IMETHODIMP
444 152147 : nsThread::IsOnCurrentThread(bool *result)
445 : {
446 152147 : *result = (PR_GetCurrentThread() == mThread);
447 152147 : return NS_OK;
448 : }
449 :
450 : //-----------------------------------------------------------------------------
451 : // nsIThread
452 :
453 : NS_IMETHODIMP
454 1420 : nsThread::GetPRThread(PRThread **result)
455 : {
456 1420 : *result = mThread;
457 1420 : return NS_OK;
458 : }
459 :
460 : NS_IMETHODIMP
461 6106 : nsThread::Shutdown()
462 : {
463 6106 : LOG(("THRD(%p) shutdown\n", this));
464 :
465 : // XXX If we make this warn, then we hit that warning at xpcom shutdown while
466 : // shutting down a thread in a thread pool. That happens b/c the thread
467 : // in the thread pool is already shutdown by the thread manager.
468 6106 : if (!mThread)
469 0 : return NS_OK;
470 :
471 6106 : NS_ENSURE_STATE(mThread != PR_GetCurrentThread());
472 :
473 : // Prevent multiple calls to this method
474 : {
475 12212 : MutexAutoLock lock(mLock);
476 6106 : if (!mShutdownRequired)
477 0 : return NS_ERROR_UNEXPECTED;
478 12212 : mShutdownRequired = false;
479 : }
480 :
481 : nsThreadShutdownContext context;
482 6106 : context.joiningThread = nsThreadManager::get()->GetCurrentThread();
483 6106 : context.shutdownAck = false;
484 :
485 : // Set mShutdownContext and wake up the thread in case it is waiting for
486 : // events to process.
487 12212 : nsCOMPtr<nsIRunnable> event = new nsThreadShutdownEvent(this, &context);
488 6106 : if (!event)
489 0 : return NS_ERROR_OUT_OF_MEMORY;
490 : // XXXroc What if posting the event fails due to OOM?
491 6106 : PutEvent(event);
492 :
493 : // We could still end up with other events being added after the shutdown
494 : // task, but that's okay because we process pending events in ThreadFunc
495 : // after setting mShutdownContext just before exiting.
496 :
497 : // Process events on the current thread until we receive a shutdown ACK.
498 25136 : while (!context.shutdownAck)
499 12924 : NS_ProcessNextEvent(context.joiningThread);
500 :
501 : // Now, it should be safe to join without fear of dead-locking.
502 :
503 6106 : PR_JoinThread(mThread);
504 6106 : mThread = nsnull;
505 :
506 : // We hold strong references to our event observers, and once the thread is
507 : // shut down the observers can't easily unregister themselves. Do it here
508 : // to avoid leaking.
509 6106 : ClearObservers();
510 :
511 : #ifdef DEBUG
512 : {
513 12212 : MutexAutoLock lock(mLock);
514 6106 : NS_ASSERTION(!mObserver, "Should have been cleared at shutdown!");
515 : }
516 : #endif
517 :
518 6106 : return NS_OK;
519 : }
520 :
521 : NS_IMETHODIMP
522 492034 : nsThread::HasPendingEvents(bool *result)
523 : {
524 492034 : NS_ENSURE_STATE(PR_GetCurrentThread() == mThread);
525 :
526 492034 : *result = mEvents->GetEvent(false, nsnull);
527 492030 : return NS_OK;
528 : }
529 :
530 : #ifdef MOZ_CANARY
531 : void canary_alarm_handler (int signum);
532 :
533 : class Canary {
534 : //XXX ToDo: support nested loops
535 : public:
536 301368 : Canary() {
537 301368 : if (sOutputFD != 0 && EventLatencyIsImportant()) {
538 1419 : if (sOutputFD == -1) {
539 1419 : const int flags = O_WRONLY | O_APPEND | O_CREAT | O_NONBLOCK;
540 1419 : const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
541 1419 : char* env_var_flag = getenv("MOZ_KILL_CANARIES");
542 0 : sOutputFD = env_var_flag ? (env_var_flag[0] ?
543 : open(env_var_flag, flags, mode) :
544 1419 : STDERR_FILENO) : 0;
545 1419 : if (sOutputFD == 0)
546 1419 : return;
547 : }
548 0 : signal(SIGALRM, canary_alarm_handler);
549 0 : ualarm(15000, 0);
550 : }
551 : }
552 :
553 301365 : ~Canary() {
554 301365 : if (sOutputFD != 0 && EventLatencyIsImportant())
555 0 : ualarm(0, 0);
556 301365 : }
557 :
558 21400 : static bool EventLatencyIsImportant() {
559 21400 : return NS_IsMainThread() && XRE_GetProcessType() == GeckoProcessType_Default;
560 : }
561 :
562 : static int sOutputFD;
563 : };
564 :
565 : int Canary::sOutputFD = -1;
566 :
567 0 : void canary_alarm_handler (int signum)
568 : {
569 : void *array[30];
570 0 : const char msg[29] = "event took too long to run:\n";
571 : // use write to be safe in the signal handler
572 0 : write(Canary::sOutputFD, msg, sizeof(msg));
573 0 : backtrace_symbols_fd(array, backtrace(array, 30), Canary::sOutputFD);
574 0 : }
575 :
576 : #endif
577 :
578 : #define NOTIFY_EVENT_OBSERVERS(func_, params_) \
579 : PR_BEGIN_MACRO \
580 : if (!mEventObservers.IsEmpty()) { \
581 : nsAutoTObserverArray<nsCOMPtr<nsIThreadObserver>, 2>::ForwardIterator \
582 : iter_(mEventObservers); \
583 : nsCOMPtr<nsIThreadObserver> obs_; \
584 : while (iter_.HasMore()) { \
585 : obs_ = iter_.GetNext(); \
586 : obs_ -> func_ params_ ; \
587 : } \
588 : } \
589 : PR_END_MACRO
590 :
591 : NS_IMETHODIMP
592 301368 : nsThread::ProcessNextEvent(bool mayWait, bool *result)
593 : {
594 301368 : LOG(("THRD(%p) ProcessNextEvent [%u %u]\n", this, mayWait, mRunningEvent));
595 :
596 301368 : NS_ENSURE_STATE(PR_GetCurrentThread() == mThread);
597 :
598 301369 : if (MAIN_THREAD == mIsMainThread && mayWait && !ShuttingDown())
599 150874 : HangMonitor::Suspend();
600 :
601 : // Fire a memory pressure notification, if we're the main thread and one is
602 : // pending.
603 301369 : if (MAIN_THREAD == mIsMainThread && !ShuttingDown()) {
604 176160 : bool mpPending = PR_ATOMIC_SET(&sMemoryPressurePending, 0);
605 176160 : if (mpPending) {
606 0 : nsCOMPtr<nsIObserverService> os = services::GetObserverService();
607 0 : if (os) {
608 0 : os->NotifyObservers(nsnull, "memory-pressure",
609 0 : NS_LITERAL_STRING("low-memory").get());
610 : }
611 : else {
612 0 : NS_WARNING("Can't get observer service!");
613 : }
614 : }
615 : }
616 :
617 602735 : nsCOMPtr<nsIThreadObserver> obs = mObserver;
618 301356 : if (obs)
619 228769 : obs->OnProcessNextEvent(this, mayWait && !ShuttingDown(), mRunningEvent);
620 :
621 301360 : NOTIFY_EVENT_OBSERVERS(OnProcessNextEvent,
622 : (this, mayWait && !ShuttingDown(), mRunningEvent));
623 :
624 301368 : ++mRunningEvent;
625 :
626 : #ifdef MOZ_CANARY
627 602735 : Canary canary;
628 : #endif
629 301366 : nsresult rv = NS_OK;
630 :
631 : {
632 : // Scope for |event| to make sure that its destructor fires while
633 : // mRunningEvent has been incremented, since that destructor can
634 : // also do work.
635 :
636 : // If we are shutting down, then do not wait for new events.
637 602734 : nsCOMPtr<nsIRunnable> event;
638 301365 : mEvents->GetEvent(mayWait && !ShuttingDown(), getter_AddRefs(event));
639 :
640 : #ifdef NS_FUNCTION_TIMER
641 : char message[1024] = {'\0'};
642 : if (MAIN_THREAD == mIsMainThread) {
643 : mozilla::FunctionTimer::ft_snprintf(message, sizeof(message),
644 : "@ Main Thread Event %p", (void*)event.get());
645 : }
646 : // If message is empty, it means that we're not on the main thread, and
647 : // FunctionTimer won't time this function.
648 : NS_TIME_FUNCTION_MIN_FMT(5.0, message);
649 : #endif
650 :
651 301368 : *result = (event.get() != nsnull);
652 :
653 301368 : if (event) {
654 286511 : LOG(("THRD(%p) running [%p]\n", this, event.get()));
655 286514 : if (MAIN_THREAD == mIsMainThread)
656 164357 : HangMonitor::NotifyActivity();
657 286514 : event->Run();
658 14855 : } else if (mayWait) {
659 1626 : NS_ASSERTION(ShuttingDown(),
660 : "This should only happen when shutting down");
661 1626 : rv = NS_ERROR_UNEXPECTED;
662 : }
663 : }
664 :
665 301368 : --mRunningEvent;
666 :
667 301368 : NOTIFY_EVENT_OBSERVERS(AfterProcessNextEvent, (this, mRunningEvent));
668 :
669 301366 : if (obs)
670 228770 : obs->AfterProcessNextEvent(this, mRunningEvent);
671 :
672 301367 : return rv;
673 : }
674 :
675 : //-----------------------------------------------------------------------------
676 : // nsISupportsPriority
677 :
678 : NS_IMETHODIMP
679 0 : nsThread::GetPriority(PRInt32 *priority)
680 : {
681 0 : *priority = mPriority;
682 0 : return NS_OK;
683 : }
684 :
685 : NS_IMETHODIMP
686 61 : nsThread::SetPriority(PRInt32 priority)
687 : {
688 61 : NS_ENSURE_STATE(mThread);
689 :
690 : // NSPR defines the following four thread priorities:
691 : // PR_PRIORITY_LOW
692 : // PR_PRIORITY_NORMAL
693 : // PR_PRIORITY_HIGH
694 : // PR_PRIORITY_URGENT
695 : // We map the priority values defined on nsISupportsPriority to these values.
696 :
697 61 : mPriority = priority;
698 :
699 : PRThreadPriority pri;
700 61 : if (mPriority <= PRIORITY_HIGHEST) {
701 0 : pri = PR_PRIORITY_URGENT;
702 61 : } else if (mPriority < PRIORITY_NORMAL) {
703 0 : pri = PR_PRIORITY_HIGH;
704 61 : } else if (mPriority > PRIORITY_NORMAL) {
705 61 : pri = PR_PRIORITY_LOW;
706 : } else {
707 0 : pri = PR_PRIORITY_NORMAL;
708 : }
709 61 : PR_SetThreadPriority(mThread, pri);
710 :
711 61 : return NS_OK;
712 : }
713 :
714 : NS_IMETHODIMP
715 0 : nsThread::AdjustPriority(PRInt32 delta)
716 : {
717 0 : return SetPriority(mPriority + delta);
718 : }
719 :
720 : //-----------------------------------------------------------------------------
721 : // nsIThreadInternal
722 :
723 : NS_IMETHODIMP
724 286512 : nsThread::GetObserver(nsIThreadObserver **obs)
725 : {
726 573017 : MutexAutoLock lock(mLock);
727 286514 : NS_IF_ADDREF(*obs = mObserver);
728 286505 : return NS_OK;
729 : }
730 :
731 : NS_IMETHODIMP
732 10446 : nsThread::SetObserver(nsIThreadObserver *obs)
733 : {
734 10446 : NS_ENSURE_STATE(PR_GetCurrentThread() == mThread);
735 :
736 20892 : MutexAutoLock lock(mLock);
737 10446 : mObserver = obs;
738 10446 : return NS_OK;
739 : }
740 :
741 : NS_IMETHODIMP
742 0 : nsThread::PushEventQueue(nsIThreadEventFilter *filter)
743 : {
744 0 : nsChainedEventQueue *queue = new nsChainedEventQueue(filter);
745 :
746 0 : MutexAutoLock lock(mLock);
747 0 : queue->mNext = mEvents;
748 0 : mEvents = queue;
749 0 : return NS_OK;
750 : }
751 :
752 : NS_IMETHODIMP
753 0 : nsThread::PopEventQueue()
754 : {
755 0 : MutexAutoLock lock(mLock);
756 :
757 : // Make sure we do not pop too many!
758 0 : NS_ENSURE_STATE(mEvents != &mEventsRoot);
759 :
760 0 : nsChainedEventQueue *queue = mEvents;
761 0 : mEvents = mEvents->mNext;
762 :
763 0 : nsCOMPtr<nsIRunnable> event;
764 0 : while (queue->GetEvent(false, getter_AddRefs(event)))
765 0 : mEvents->PutEvent(event);
766 :
767 0 : delete queue;
768 :
769 0 : return NS_OK;
770 : }
771 :
772 : bool
773 292620 : nsThread::nsChainedEventQueue::PutEvent(nsIRunnable *event)
774 : {
775 : bool val;
776 292620 : if (!mFilter || mFilter->AcceptEvent(event)) {
777 292620 : val = mQueue.PutEvent(event);
778 : } else {
779 0 : val = mNext->PutEvent(event);
780 : }
781 292618 : return val;
782 : }
783 :
784 : NS_IMETHODIMP
785 469 : nsThread::GetRecursionDepth(PRUint32 *depth)
786 : {
787 469 : NS_ENSURE_ARG_POINTER(depth);
788 469 : NS_ENSURE_STATE(PR_GetCurrentThread() == mThread);
789 :
790 469 : *depth = mRunningEvent;
791 469 : return NS_OK;
792 : }
793 :
794 : NS_IMETHODIMP
795 1873 : nsThread::AddObserver(nsIThreadObserver *observer)
796 : {
797 1873 : NS_ENSURE_ARG_POINTER(observer);
798 1873 : NS_ENSURE_STATE(PR_GetCurrentThread() == mThread);
799 :
800 1873 : NS_WARN_IF_FALSE(!mEventObservers.Contains(observer),
801 : "Adding an observer twice!");
802 :
803 1873 : if (!mEventObservers.AppendElement(observer)) {
804 0 : NS_WARNING("Out of memory!");
805 0 : return NS_ERROR_OUT_OF_MEMORY;
806 : }
807 :
808 1873 : return NS_OK;
809 : }
810 :
811 : NS_IMETHODIMP
812 469 : nsThread::RemoveObserver(nsIThreadObserver *observer)
813 : {
814 469 : NS_ENSURE_STATE(PR_GetCurrentThread() == mThread);
815 :
816 469 : if (observer && !mEventObservers.RemoveElement(observer)) {
817 0 : NS_WARNING("Removing an observer that was never added!");
818 : }
819 :
820 469 : return NS_OK;
821 : }
822 :
823 : //-----------------------------------------------------------------------------
824 :
825 : NS_IMETHODIMP
826 2 : nsThreadSyncDispatch::Run()
827 : {
828 2 : if (mSyncTask) {
829 1 : mResult = mSyncTask->Run();
830 1 : mSyncTask = nsnull;
831 : // unblock the origin thread
832 1 : mOrigin->Dispatch(this, NS_DISPATCH_NORMAL);
833 : }
834 2 : return NS_OK;
835 4392 : }
|