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