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/. */
6
7 #include "LazyIdleThread.h"
8
9 #include "nsIObserverService.h"
10
11 #include "GeckoProfiler.h"
12 #include "nsComponentManagerUtils.h"
13 #include "nsServiceManagerUtils.h"
14 #include "nsThreadUtils.h"
15 #include "mozilla/Services.h"
16
17 #ifdef DEBUG
18 # define ASSERT_OWNING_THREAD() \
19 do { \
20 MOZ_ASSERT(mOwningEventTarget->IsOnCurrentThread()); \
21 } while (0)
22 #else
23 # define ASSERT_OWNING_THREAD() /* nothing */
24 #endif
25
26 namespace mozilla {
27
LazyIdleThread(uint32_t aIdleTimeoutMS,const nsACString & aName,ShutdownMethod aShutdownMethod,nsIObserver * aIdleObserver)28 LazyIdleThread::LazyIdleThread(uint32_t aIdleTimeoutMS, const nsACString& aName,
29 ShutdownMethod aShutdownMethod,
30 nsIObserver* aIdleObserver)
31 : mMutex("LazyIdleThread::mMutex"),
32 mOwningEventTarget(GetCurrentSerialEventTarget()),
33 mIdleObserver(aIdleObserver),
34 mQueuedRunnables(nullptr),
35 mIdleTimeoutMS(aIdleTimeoutMS),
36 mPendingEventCount(0),
37 mIdleNotificationCount(0),
38 mShutdownMethod(aShutdownMethod),
39 mShutdown(false),
40 mThreadIsShuttingDown(false),
41 mIdleTimeoutEnabled(true),
42 mName(aName) {
43 MOZ_ASSERT(mOwningEventTarget, "Need owning thread!");
44 }
45
~LazyIdleThread()46 LazyIdleThread::~LazyIdleThread() {
47 ASSERT_OWNING_THREAD();
48
49 Shutdown();
50 }
51
SetWeakIdleObserver(nsIObserver * aObserver)52 void LazyIdleThread::SetWeakIdleObserver(nsIObserver* aObserver) {
53 ASSERT_OWNING_THREAD();
54
55 if (mShutdown) {
56 NS_WARNING_ASSERTION(!aObserver,
57 "Setting an observer after Shutdown was called!");
58 return;
59 }
60
61 mIdleObserver = aObserver;
62 }
63
DisableIdleTimeout()64 void LazyIdleThread::DisableIdleTimeout() {
65 ASSERT_OWNING_THREAD();
66 if (!mIdleTimeoutEnabled) {
67 return;
68 }
69 mIdleTimeoutEnabled = false;
70
71 if (mIdleTimer && NS_FAILED(mIdleTimer->Cancel())) {
72 NS_WARNING("Failed to cancel timer!");
73 }
74
75 MutexAutoLock lock(mMutex);
76
77 // Pretend we have a pending event to keep the idle timer from firing.
78 MOZ_ASSERT(mPendingEventCount < UINT32_MAX, "Way too many!");
79 mPendingEventCount++;
80 }
81
EnableIdleTimeout()82 void LazyIdleThread::EnableIdleTimeout() {
83 ASSERT_OWNING_THREAD();
84 if (mIdleTimeoutEnabled) {
85 return;
86 }
87 mIdleTimeoutEnabled = true;
88
89 {
90 MutexAutoLock lock(mMutex);
91
92 MOZ_ASSERT(mPendingEventCount, "Mismatched calls to observer methods!");
93 --mPendingEventCount;
94 }
95
96 if (mThread) {
97 nsCOMPtr<nsIRunnable> runnable(new Runnable("LazyIdleThreadDummyRunnable"));
98 if (NS_FAILED(Dispatch(runnable.forget(), NS_DISPATCH_NORMAL))) {
99 NS_WARNING("Failed to dispatch!");
100 }
101 }
102 }
103
PreDispatch()104 void LazyIdleThread::PreDispatch() {
105 MutexAutoLock lock(mMutex);
106
107 MOZ_ASSERT(mPendingEventCount < UINT32_MAX, "Way too many!");
108 mPendingEventCount++;
109 }
110
EnsureThread()111 nsresult LazyIdleThread::EnsureThread() {
112 ASSERT_OWNING_THREAD();
113
114 if (mShutdown) {
115 return NS_ERROR_UNEXPECTED;
116 }
117
118 if (mThread) {
119 return NS_OK;
120 }
121
122 #ifdef DEBUG
123 {
124 MutexAutoLock lock(mMutex);
125 MOZ_ASSERT(!mPendingEventCount, "Shouldn't have events yet!");
126 MOZ_ASSERT(!mIdleNotificationCount, "Shouldn't have idle events yet!");
127 MOZ_ASSERT(!mIdleTimer, "Should have killed this long ago!");
128 MOZ_ASSERT(!mThreadIsShuttingDown, "Should have cleared that!");
129 }
130 #endif
131 nsresult rv;
132
133 if (mShutdownMethod == AutomaticShutdown && NS_IsMainThread()) {
134 nsCOMPtr<nsIObserverService> obs =
135 do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv);
136 if (NS_WARN_IF(NS_FAILED(rv))) {
137 return rv;
138 }
139
140 rv = obs->AddObserver(this, "xpcom-shutdown-threads", false);
141 if (NS_WARN_IF(NS_FAILED(rv))) {
142 return rv;
143 }
144 }
145
146 mIdleTimer = NS_NewTimer();
147 if (NS_WARN_IF(!mIdleTimer)) {
148 return NS_ERROR_UNEXPECTED;
149 }
150
151 nsCOMPtr<nsIRunnable> runnable = NewRunnableMethod(
152 "LazyIdleThread::InitThread", this, &LazyIdleThread::InitThread);
153 if (NS_WARN_IF(!runnable)) {
154 return NS_ERROR_UNEXPECTED;
155 }
156
157 rv = NS_NewNamedThread(mName, getter_AddRefs(mThread), runnable);
158 if (NS_WARN_IF(NS_FAILED(rv))) {
159 return rv;
160 }
161
162 return NS_OK;
163 }
164
InitThread()165 void LazyIdleThread::InitThread() {
166 // Happens on mThread but mThread may not be set yet...
167
168 nsCOMPtr<nsIThreadInternal> thread(do_QueryInterface(NS_GetCurrentThread()));
169 MOZ_ASSERT(thread, "This should always succeed!");
170
171 if (NS_FAILED(thread->SetObserver(this))) {
172 NS_WARNING("Failed to set thread observer!");
173 }
174 }
175
CleanupThread()176 void LazyIdleThread::CleanupThread() {
177 nsCOMPtr<nsIThreadInternal> thread(do_QueryInterface(NS_GetCurrentThread()));
178 MOZ_ASSERT(thread, "This should always succeed!");
179
180 if (NS_FAILED(thread->SetObserver(nullptr))) {
181 NS_WARNING("Failed to set thread observer!");
182 }
183
184 {
185 MutexAutoLock lock(mMutex);
186
187 MOZ_ASSERT(!mThreadIsShuttingDown, "Shouldn't be true ever!");
188 mThreadIsShuttingDown = true;
189 }
190 }
191
ScheduleTimer()192 void LazyIdleThread::ScheduleTimer() {
193 ASSERT_OWNING_THREAD();
194
195 bool shouldSchedule;
196 {
197 MutexAutoLock lock(mMutex);
198
199 MOZ_ASSERT(mIdleNotificationCount, "Should have at least one!");
200 --mIdleNotificationCount;
201
202 shouldSchedule = !mIdleNotificationCount && !mPendingEventCount;
203 }
204
205 if (mIdleTimer) {
206 if (NS_FAILED(mIdleTimer->Cancel())) {
207 NS_WARNING("Failed to cancel timer!");
208 }
209
210 if (shouldSchedule && NS_FAILED(mIdleTimer->InitWithCallback(
211 this, mIdleTimeoutMS, nsITimer::TYPE_ONE_SHOT))) {
212 NS_WARNING("Failed to schedule timer!");
213 }
214 }
215 }
216
ShutdownThread()217 nsresult LazyIdleThread::ShutdownThread() {
218 ASSERT_OWNING_THREAD();
219
220 // Before calling Shutdown() on the real thread we need to put a queue in
221 // place in case a runnable is posted to the thread while it's in the
222 // process of shutting down. This will be our queue.
223 AutoTArray<nsCOMPtr<nsIRunnable>, 10> queuedRunnables;
224
225 nsresult rv;
226
227 // Make sure to cancel the shutdown timer before spinning the event loop
228 // during |mThread->Shutdown()| below. Otherwise the timer might fire and we
229 // could reenter here.
230 if (mIdleTimer) {
231 rv = mIdleTimer->Cancel();
232 if (NS_WARN_IF(NS_FAILED(rv))) {
233 return rv;
234 }
235
236 mIdleTimer = nullptr;
237 }
238
239 if (mThread) {
240 if (mShutdownMethod == AutomaticShutdown && NS_IsMainThread()) {
241 nsCOMPtr<nsIObserverService> obs =
242 mozilla::services::GetObserverService();
243 NS_WARNING_ASSERTION(obs, "Failed to get observer service!");
244
245 if (obs &&
246 NS_FAILED(obs->RemoveObserver(this, "xpcom-shutdown-threads"))) {
247 NS_WARNING("Failed to remove observer!");
248 }
249 }
250
251 if (mIdleObserver) {
252 mIdleObserver->Observe(static_cast<nsIThread*>(this), IDLE_THREAD_TOPIC,
253 nullptr);
254 }
255
256 #ifdef DEBUG
257 {
258 MutexAutoLock lock(mMutex);
259 MOZ_ASSERT(!mThreadIsShuttingDown, "Huh?!");
260 }
261 #endif
262
263 nsCOMPtr<nsIRunnable> runnable = NewRunnableMethod(
264 "LazyIdleThread::CleanupThread", this, &LazyIdleThread::CleanupThread);
265 if (NS_WARN_IF(!runnable)) {
266 return NS_ERROR_UNEXPECTED;
267 }
268
269 PreDispatch();
270
271 rv = mThread->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
272 if (NS_WARN_IF(NS_FAILED(rv))) {
273 return rv;
274 }
275
276 // Put the temporary queue in place before calling Shutdown().
277 mQueuedRunnables = &queuedRunnables;
278
279 if (NS_FAILED(mThread->Shutdown())) {
280 NS_ERROR("Failed to shutdown the thread!");
281 }
282
283 // Now unset the queue.
284 mQueuedRunnables = nullptr;
285
286 mThread = nullptr;
287
288 {
289 MutexAutoLock lock(mMutex);
290
291 MOZ_ASSERT(!mPendingEventCount, "Huh?!");
292 MOZ_ASSERT(!mIdleNotificationCount, "Huh?!");
293 MOZ_ASSERT(mThreadIsShuttingDown, "Huh?!");
294 mThreadIsShuttingDown = false;
295 }
296 }
297
298 // If our temporary queue has any runnables then we need to dispatch them.
299 if (queuedRunnables.Length()) {
300 // If the thread manager has gone away then these runnables will never run.
301 if (mShutdown) {
302 NS_ERROR("Runnables dispatched to LazyIdleThread will never run!");
303 return NS_OK;
304 }
305
306 // Re-dispatch the queued runnables.
307 for (uint32_t index = 0; index < queuedRunnables.Length(); index++) {
308 nsCOMPtr<nsIRunnable> runnable;
309 runnable.swap(queuedRunnables[index]);
310 MOZ_ASSERT(runnable, "Null runnable?!");
311
312 if (NS_FAILED(Dispatch(runnable.forget(), NS_DISPATCH_NORMAL))) {
313 NS_ERROR("Failed to re-dispatch queued runnable!");
314 }
315 }
316 }
317
318 return NS_OK;
319 }
320
SelfDestruct()321 void LazyIdleThread::SelfDestruct() {
322 MOZ_ASSERT(mRefCnt == 1, "Bad refcount!");
323 delete this;
324 }
325
326 NS_IMPL_ADDREF(LazyIdleThread)
327
NS_IMETHODIMP_(MozExternalRefCountType)328 NS_IMETHODIMP_(MozExternalRefCountType)
329 LazyIdleThread::Release() {
330 nsrefcnt count = --mRefCnt;
331 NS_LOG_RELEASE(this, count, "LazyIdleThread");
332
333 if (!count) {
334 // Stabilize refcount.
335 mRefCnt = 1;
336
337 nsCOMPtr<nsIRunnable> runnable = NewNonOwningRunnableMethod(
338 "LazyIdleThread::SelfDestruct", this, &LazyIdleThread::SelfDestruct);
339 NS_WARNING_ASSERTION(runnable, "Couldn't make runnable!");
340
341 if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
342 MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
343 // The only way this could fail is if we're in shutdown, and in that case
344 // threads should have been joined already. Deleting here isn't dangerous
345 // anymore because we won't spin the event loop waiting to join the
346 // thread.
347 SelfDestruct();
348 }
349 }
350
351 return count;
352 }
353
NS_IMPL_QUERY_INTERFACE(LazyIdleThread,nsIThread,nsIEventTarget,nsISerialEventTarget,nsITimerCallback,nsIThreadObserver,nsIObserver,nsINamed)354 NS_IMPL_QUERY_INTERFACE(LazyIdleThread, nsIThread, nsIEventTarget,
355 nsISerialEventTarget, nsITimerCallback,
356 nsIThreadObserver, nsIObserver, nsINamed)
357
358 NS_IMETHODIMP
359 LazyIdleThread::DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags) {
360 nsCOMPtr<nsIRunnable> event(aEvent);
361 return Dispatch(event.forget(), aFlags);
362 }
363
364 NS_IMETHODIMP
Dispatch(already_AddRefed<nsIRunnable> aEvent,uint32_t aFlags)365 LazyIdleThread::Dispatch(already_AddRefed<nsIRunnable> aEvent,
366 uint32_t aFlags) {
367 ASSERT_OWNING_THREAD();
368 nsCOMPtr<nsIRunnable> event(aEvent); // avoid leaks
369
370 // LazyIdleThread can't always support synchronous dispatch currently.
371 if (NS_WARN_IF(aFlags != NS_DISPATCH_NORMAL)) {
372 return NS_ERROR_NOT_IMPLEMENTED;
373 }
374
375 if (NS_WARN_IF(mShutdown)) {
376 return NS_ERROR_UNEXPECTED;
377 }
378
379 // If our thread is shutting down then we can't actually dispatch right now.
380 // Queue this runnable for later.
381 if (UseRunnableQueue()) {
382 mQueuedRunnables->AppendElement(event);
383 return NS_OK;
384 }
385
386 nsresult rv = EnsureThread();
387 if (NS_WARN_IF(NS_FAILED(rv))) {
388 return rv;
389 }
390
391 PreDispatch();
392
393 return mThread->Dispatch(event.forget(), aFlags);
394 }
395
396 NS_IMETHODIMP
DelayedDispatch(already_AddRefed<nsIRunnable>,uint32_t)397 LazyIdleThread::DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t) {
398 return NS_ERROR_NOT_IMPLEMENTED;
399 }
400
401 NS_IMETHODIMP
GetRunningEventDelay(TimeDuration * aDelay,TimeStamp * aStart)402 LazyIdleThread::GetRunningEventDelay(TimeDuration* aDelay, TimeStamp* aStart) {
403 if (mThread) {
404 return mThread->GetRunningEventDelay(aDelay, aStart);
405 }
406 *aDelay = TimeDuration();
407 *aStart = TimeStamp();
408 return NS_OK;
409 }
410
411 NS_IMETHODIMP
SetRunningEventDelay(TimeDuration aDelay,TimeStamp aStart)412 LazyIdleThread::SetRunningEventDelay(TimeDuration aDelay, TimeStamp aStart) {
413 if (mThread) {
414 return mThread->SetRunningEventDelay(aDelay, aStart);
415 }
416 return NS_OK;
417 }
418
419 NS_IMETHODIMP
IsOnCurrentThread(bool * aIsOnCurrentThread)420 LazyIdleThread::IsOnCurrentThread(bool* aIsOnCurrentThread) {
421 if (mThread) {
422 return mThread->IsOnCurrentThread(aIsOnCurrentThread);
423 }
424
425 *aIsOnCurrentThread = false;
426 return NS_OK;
427 }
428
NS_IMETHODIMP_(bool)429 NS_IMETHODIMP_(bool)
430 LazyIdleThread::IsOnCurrentThreadInfallible() {
431 if (mThread) {
432 return mThread->IsOnCurrentThread();
433 }
434
435 return false;
436 }
437
438 NS_IMETHODIMP
GetPRThread(PRThread ** aPRThread)439 LazyIdleThread::GetPRThread(PRThread** aPRThread) {
440 if (mThread) {
441 return mThread->GetPRThread(aPRThread);
442 }
443
444 *aPRThread = nullptr;
445 return NS_ERROR_NOT_AVAILABLE;
446 }
447
448 NS_IMETHODIMP
GetCanInvokeJS(bool * aCanInvokeJS)449 LazyIdleThread::GetCanInvokeJS(bool* aCanInvokeJS) {
450 *aCanInvokeJS = false;
451 return NS_OK;
452 }
453
454 NS_IMETHODIMP
SetCanInvokeJS(bool aCanInvokeJS)455 LazyIdleThread::SetCanInvokeJS(bool aCanInvokeJS) {
456 return NS_ERROR_NOT_IMPLEMENTED;
457 }
458
459 NS_IMETHODIMP
GetLastLongTaskEnd(TimeStamp * _retval)460 LazyIdleThread::GetLastLongTaskEnd(TimeStamp* _retval) {
461 return NS_ERROR_NOT_IMPLEMENTED;
462 }
463
464 NS_IMETHODIMP
GetLastLongNonIdleTaskEnd(TimeStamp * _retval)465 LazyIdleThread::GetLastLongNonIdleTaskEnd(TimeStamp* _retval) {
466 return NS_ERROR_NOT_IMPLEMENTED;
467 }
468
469 NS_IMETHODIMP
SetNameForWakeupTelemetry(const nsACString & aName)470 LazyIdleThread::SetNameForWakeupTelemetry(const nsACString& aName) {
471 return NS_ERROR_NOT_IMPLEMENTED;
472 }
473
474 NS_IMETHODIMP
AsyncShutdown()475 LazyIdleThread::AsyncShutdown() {
476 ASSERT_OWNING_THREAD();
477 return NS_ERROR_NOT_IMPLEMENTED;
478 }
479
480 NS_IMETHODIMP
BeginShutdown(nsIThreadShutdown ** aShutdown)481 LazyIdleThread::BeginShutdown(nsIThreadShutdown** aShutdown) {
482 ASSERT_OWNING_THREAD();
483 *aShutdown = nullptr;
484 return NS_ERROR_NOT_IMPLEMENTED;
485 }
486
487 NS_IMETHODIMP
Shutdown()488 LazyIdleThread::Shutdown() {
489 ASSERT_OWNING_THREAD();
490
491 mShutdown = true;
492
493 nsresult rv = ShutdownThread();
494 MOZ_ASSERT(!mThread, "Should have destroyed this by now!");
495
496 mIdleObserver = nullptr;
497
498 if (NS_WARN_IF(NS_FAILED(rv))) {
499 return rv;
500 }
501
502 return NS_OK;
503 }
504
505 NS_IMETHODIMP
HasPendingEvents(bool * aHasPendingEvents)506 LazyIdleThread::HasPendingEvents(bool* aHasPendingEvents) {
507 // This is only supposed to be called from the thread itself so it's not
508 // implemented here.
509 MOZ_ASSERT_UNREACHABLE("Shouldn't ever call this!");
510 return NS_ERROR_UNEXPECTED;
511 }
512
513 NS_IMETHODIMP
HasPendingHighPriorityEvents(bool * aHasPendingEvents)514 LazyIdleThread::HasPendingHighPriorityEvents(bool* aHasPendingEvents) {
515 // This is only supposed to be called from the thread itself so it's not
516 // implemented here.
517 MOZ_ASSERT_UNREACHABLE("Shouldn't ever call this!");
518 return NS_ERROR_UNEXPECTED;
519 }
520
521 NS_IMETHODIMP
DispatchToQueue(already_AddRefed<nsIRunnable> aEvent,EventQueuePriority aQueue)522 LazyIdleThread::DispatchToQueue(already_AddRefed<nsIRunnable> aEvent,
523 EventQueuePriority aQueue) {
524 return NS_ERROR_NOT_IMPLEMENTED;
525 }
526
527 NS_IMETHODIMP
ProcessNextEvent(bool aMayWait,bool * aEventWasProcessed)528 LazyIdleThread::ProcessNextEvent(bool aMayWait, bool* aEventWasProcessed) {
529 // This is only supposed to be called from the thread itself so it's not
530 // implemented here.
531 MOZ_ASSERT_UNREACHABLE("Shouldn't ever call this!");
532 return NS_ERROR_UNEXPECTED;
533 }
534
535 NS_IMETHODIMP
Notify(nsITimer * aTimer)536 LazyIdleThread::Notify(nsITimer* aTimer) {
537 ASSERT_OWNING_THREAD();
538
539 {
540 MutexAutoLock lock(mMutex);
541
542 if (mPendingEventCount || mIdleNotificationCount) {
543 // Another event was scheduled since this timer was set. Don't do
544 // anything and wait for the timer to fire again.
545 return NS_OK;
546 }
547 }
548
549 nsresult rv = ShutdownThread();
550 if (NS_WARN_IF(NS_FAILED(rv))) {
551 return rv;
552 }
553
554 return NS_OK;
555 }
556
557 NS_IMETHODIMP
GetName(nsACString & aName)558 LazyIdleThread::GetName(nsACString& aName) {
559 aName.Assign(mName);
560 return NS_OK;
561 }
562
563 NS_IMETHODIMP
OnDispatchedEvent()564 LazyIdleThread::OnDispatchedEvent() {
565 MOZ_ASSERT(mOwningEventTarget->IsOnCurrentThread());
566 return NS_OK;
567 }
568
569 NS_IMETHODIMP
OnProcessNextEvent(nsIThreadInternal *,bool)570 LazyIdleThread::OnProcessNextEvent(nsIThreadInternal* /* aThread */,
571 bool /* aMayWait */) {
572 return NS_OK;
573 }
574
575 NS_IMETHODIMP
AfterProcessNextEvent(nsIThreadInternal *,bool aEventWasProcessed)576 LazyIdleThread::AfterProcessNextEvent(nsIThreadInternal* /* aThread */,
577 bool aEventWasProcessed) {
578 bool shouldNotifyIdle;
579 {
580 MutexAutoLock lock(mMutex);
581
582 if (aEventWasProcessed) {
583 MOZ_ASSERT(mPendingEventCount, "Mismatched calls to observer methods!");
584 --mPendingEventCount;
585 }
586
587 if (mThreadIsShuttingDown) {
588 // We're shutting down, no need to fire any timer.
589 return NS_OK;
590 }
591
592 shouldNotifyIdle = !mPendingEventCount;
593 if (shouldNotifyIdle) {
594 MOZ_ASSERT(mIdleNotificationCount < UINT32_MAX, "Way too many!");
595 mIdleNotificationCount++;
596 }
597 }
598
599 if (shouldNotifyIdle) {
600 nsCOMPtr<nsIRunnable> runnable = NewRunnableMethod(
601 "LazyIdleThread::ScheduleTimer", this, &LazyIdleThread::ScheduleTimer);
602 if (NS_WARN_IF(!runnable)) {
603 return NS_ERROR_UNEXPECTED;
604 }
605
606 nsresult rv =
607 mOwningEventTarget->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
608 if (NS_WARN_IF(NS_FAILED(rv))) {
609 return rv;
610 }
611 }
612
613 return NS_OK;
614 }
615
616 NS_IMETHODIMP
Observe(nsISupports *,const char * aTopic,const char16_t *)617 LazyIdleThread::Observe(nsISupports* /* aSubject */, const char* aTopic,
618 const char16_t* /* aData */) {
619 MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
620 MOZ_ASSERT(mShutdownMethod == AutomaticShutdown,
621 "Should not receive notifications if not AutomaticShutdown!");
622 MOZ_ASSERT(!strcmp("xpcom-shutdown-threads", aTopic), "Bad topic!");
623
624 Shutdown();
625 return NS_OK;
626 }
627
628 NS_IMETHODIMP
GetEventTarget(nsIEventTarget ** aEventTarget)629 LazyIdleThread::GetEventTarget(nsIEventTarget** aEventTarget) {
630 nsCOMPtr<nsIEventTarget> target = this;
631 target.forget(aEventTarget);
632 return NS_OK;
633 }
634
EventTarget()635 nsIEventTarget* LazyIdleThread::EventTarget() { return this; }
636
SerialEventTarget()637 nsISerialEventTarget* LazyIdleThread::SerialEventTarget() { return this; }
638
639 } // namespace mozilla
640