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