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