Bug 1700051: part 49) Add some documentation to `Selection::GetRangesForInterval...
[gecko.git] / xpcom / threads / LazyIdleThread.cpp
blobf4f93de3ce98a37603f30f6d4f9b3fffdb956fd7
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/. */
7 #include "LazyIdleThread.h"
9 #include "nsIObserverService.h"
11 #include "GeckoProfiler.h"
12 #include "nsComponentManagerUtils.h"
13 #include "nsServiceManagerUtils.h"
14 #include "nsThreadUtils.h"
15 #include "mozilla/Services.h"
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
26 namespace mozilla {
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!");
46 LazyIdleThread::~LazyIdleThread() {
47 ASSERT_OWNING_THREAD();
49 Shutdown();
52 void LazyIdleThread::SetWeakIdleObserver(nsIObserver* aObserver) {
53 ASSERT_OWNING_THREAD();
55 if (mShutdown) {
56 NS_WARNING_ASSERTION(!aObserver,
57 "Setting an observer after Shutdown was called!");
58 return;
61 mIdleObserver = aObserver;
64 void LazyIdleThread::DisableIdleTimeout() {
65 ASSERT_OWNING_THREAD();
66 if (!mIdleTimeoutEnabled) {
67 return;
69 mIdleTimeoutEnabled = false;
71 if (mIdleTimer && NS_FAILED(mIdleTimer->Cancel())) {
72 NS_WARNING("Failed to cancel timer!");
75 MutexAutoLock lock(mMutex);
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++;
82 void LazyIdleThread::EnableIdleTimeout() {
83 ASSERT_OWNING_THREAD();
84 if (mIdleTimeoutEnabled) {
85 return;
87 mIdleTimeoutEnabled = true;
90 MutexAutoLock lock(mMutex);
92 MOZ_ASSERT(mPendingEventCount, "Mismatched calls to observer methods!");
93 --mPendingEventCount;
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!");
104 void LazyIdleThread::PreDispatch() {
105 MutexAutoLock lock(mMutex);
107 MOZ_ASSERT(mPendingEventCount < UINT32_MAX, "Way too many!");
108 mPendingEventCount++;
111 nsresult LazyIdleThread::EnsureThread() {
112 ASSERT_OWNING_THREAD();
114 if (mShutdown) {
115 return NS_ERROR_UNEXPECTED;
118 if (mThread) {
119 return NS_OK;
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!");
127 nsresult rv;
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;
136 rv = obs->AddObserver(this, "xpcom-shutdown-threads", false);
137 if (NS_WARN_IF(NS_FAILED(rv))) {
138 return rv;
142 mIdleTimer = NS_NewTimer();
143 if (NS_WARN_IF(!mIdleTimer)) {
144 return NS_ERROR_UNEXPECTED;
147 nsCOMPtr<nsIRunnable> runnable = NewRunnableMethod(
148 "LazyIdleThread::InitThread", this, &LazyIdleThread::InitThread);
149 if (NS_WARN_IF(!runnable)) {
150 return NS_ERROR_UNEXPECTED;
153 rv = NS_NewNamedThread(mName, getter_AddRefs(mThread), runnable);
154 if (NS_WARN_IF(NS_FAILED(rv))) {
155 return rv;
158 return NS_OK;
161 void LazyIdleThread::InitThread() {
162 // Happens on mThread but mThread may not be set yet...
164 nsCOMPtr<nsIThreadInternal> thread(do_QueryInterface(NS_GetCurrentThread()));
165 MOZ_ASSERT(thread, "This should always succeed!");
167 if (NS_FAILED(thread->SetObserver(this))) {
168 NS_WARNING("Failed to set thread observer!");
172 void LazyIdleThread::CleanupThread() {
173 nsCOMPtr<nsIThreadInternal> thread(do_QueryInterface(NS_GetCurrentThread()));
174 MOZ_ASSERT(thread, "This should always succeed!");
176 if (NS_FAILED(thread->SetObserver(nullptr))) {
177 NS_WARNING("Failed to set thread observer!");
181 MutexAutoLock lock(mMutex);
183 MOZ_ASSERT(!mThreadIsShuttingDown, "Shouldn't be true ever!");
184 mThreadIsShuttingDown = true;
188 void LazyIdleThread::ScheduleTimer() {
189 ASSERT_OWNING_THREAD();
191 bool shouldSchedule;
193 MutexAutoLock lock(mMutex);
195 MOZ_ASSERT(mIdleNotificationCount, "Should have at least one!");
196 --mIdleNotificationCount;
198 shouldSchedule = !mIdleNotificationCount && !mPendingEventCount;
201 if (mIdleTimer) {
202 if (NS_FAILED(mIdleTimer->Cancel())) {
203 NS_WARNING("Failed to cancel timer!");
206 if (shouldSchedule && NS_FAILED(mIdleTimer->InitWithCallback(
207 this, mIdleTimeoutMS, nsITimer::TYPE_ONE_SHOT))) {
208 NS_WARNING("Failed to schedule timer!");
213 nsresult LazyIdleThread::ShutdownThread() {
214 ASSERT_OWNING_THREAD();
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;
221 nsresult rv;
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;
232 mIdleTimer = nullptr;
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!");
241 if (obs &&
242 NS_FAILED(obs->RemoveObserver(this, "xpcom-shutdown-threads"))) {
243 NS_WARNING("Failed to remove observer!");
247 if (mIdleObserver) {
248 mIdleObserver->Observe(static_cast<nsIThread*>(this), IDLE_THREAD_TOPIC,
249 nullptr);
252 #ifdef DEBUG
254 MutexAutoLock lock(mMutex);
255 MOZ_ASSERT(!mThreadIsShuttingDown, "Huh?!");
257 #endif
259 nsCOMPtr<nsIRunnable> runnable = NewRunnableMethod(
260 "LazyIdleThread::CleanupThread", this, &LazyIdleThread::CleanupThread);
261 if (NS_WARN_IF(!runnable)) {
262 return NS_ERROR_UNEXPECTED;
265 PreDispatch();
267 rv = mThread->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
268 if (NS_WARN_IF(NS_FAILED(rv))) {
269 return rv;
272 // Put the temporary queue in place before calling Shutdown().
273 mQueuedRunnables = &queuedRunnables;
275 if (NS_FAILED(mThread->Shutdown())) {
276 NS_ERROR("Failed to shutdown the thread!");
279 // Now unset the queue.
280 mQueuedRunnables = nullptr;
282 mThread = nullptr;
285 MutexAutoLock lock(mMutex);
287 MOZ_ASSERT(!mPendingEventCount, "Huh?!");
288 MOZ_ASSERT(!mIdleNotificationCount, "Huh?!");
289 MOZ_ASSERT(mThreadIsShuttingDown, "Huh?!");
290 mThreadIsShuttingDown = false;
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;
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?!");
308 if (NS_FAILED(Dispatch(runnable.forget(), NS_DISPATCH_NORMAL))) {
309 NS_ERROR("Failed to re-dispatch queued runnable!");
314 return NS_OK;
317 void LazyIdleThread::SelfDestruct() {
318 MOZ_ASSERT(mRefCnt == 1, "Bad refcount!");
319 delete this;
322 NS_IMPL_ADDREF(LazyIdleThread)
324 NS_IMETHODIMP_(MozExternalRefCountType)
325 LazyIdleThread::Release() {
326 nsrefcnt count = --mRefCnt;
327 NS_LOG_RELEASE(this, count, "LazyIdleThread");
329 if (!count) {
330 // Stabilize refcount.
331 mRefCnt = 1;
333 nsCOMPtr<nsIRunnable> runnable = NewNonOwningRunnableMethod(
334 "LazyIdleThread::SelfDestruct", this, &LazyIdleThread::SelfDestruct);
335 NS_WARNING_ASSERTION(runnable, "Couldn't make runnable!");
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();
347 return count;
350 NS_IMPL_QUERY_INTERFACE(LazyIdleThread, nsIThread, nsIEventTarget,
351 nsISerialEventTarget, nsITimerCallback,
352 nsIThreadObserver, nsIObserver, nsINamed)
354 NS_IMETHODIMP
355 LazyIdleThread::DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags) {
356 nsCOMPtr<nsIRunnable> event(aEvent);
357 return Dispatch(event.forget(), aFlags);
360 NS_IMETHODIMP
361 LazyIdleThread::Dispatch(already_AddRefed<nsIRunnable> aEvent,
362 uint32_t aFlags) {
363 ASSERT_OWNING_THREAD();
364 nsCOMPtr<nsIRunnable> event(aEvent); // avoid leaks
366 // LazyIdleThread can't always support synchronous dispatch currently.
367 if (NS_WARN_IF(aFlags != NS_DISPATCH_NORMAL)) {
368 return NS_ERROR_NOT_IMPLEMENTED;
371 if (NS_WARN_IF(mShutdown)) {
372 return NS_ERROR_UNEXPECTED;
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;
382 nsresult rv = EnsureThread();
383 if (NS_WARN_IF(NS_FAILED(rv))) {
384 return rv;
387 PreDispatch();
389 return mThread->Dispatch(event.forget(), aFlags);
392 NS_IMETHODIMP
393 LazyIdleThread::DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t) {
394 return NS_ERROR_NOT_IMPLEMENTED;
397 NS_IMETHODIMP
398 LazyIdleThread::GetRunningEventDelay(TimeDuration* aDelay, TimeStamp* aStart) {
399 if (mThread) {
400 return mThread->GetRunningEventDelay(aDelay, aStart);
402 *aDelay = TimeDuration();
403 *aStart = TimeStamp();
404 return NS_OK;
407 NS_IMETHODIMP
408 LazyIdleThread::SetRunningEventDelay(TimeDuration aDelay, TimeStamp aStart) {
409 if (mThread) {
410 return mThread->SetRunningEventDelay(aDelay, aStart);
412 return NS_OK;
415 NS_IMETHODIMP
416 LazyIdleThread::IsOnCurrentThread(bool* aIsOnCurrentThread) {
417 if (mThread) {
418 return mThread->IsOnCurrentThread(aIsOnCurrentThread);
421 *aIsOnCurrentThread = false;
422 return NS_OK;
425 NS_IMETHODIMP_(bool)
426 LazyIdleThread::IsOnCurrentThreadInfallible() {
427 if (mThread) {
428 return mThread->IsOnCurrentThread();
431 return false;
434 NS_IMETHODIMP
435 LazyIdleThread::GetPRThread(PRThread** aPRThread) {
436 if (mThread) {
437 return mThread->GetPRThread(aPRThread);
440 *aPRThread = nullptr;
441 return NS_ERROR_NOT_AVAILABLE;
444 NS_IMETHODIMP
445 LazyIdleThread::GetCanInvokeJS(bool* aCanInvokeJS) {
446 *aCanInvokeJS = false;
447 return NS_OK;
450 NS_IMETHODIMP
451 LazyIdleThread::SetCanInvokeJS(bool aCanInvokeJS) {
452 return NS_ERROR_NOT_IMPLEMENTED;
455 NS_IMETHODIMP
456 LazyIdleThread::GetLastLongTaskEnd(TimeStamp* _retval) {
457 return NS_ERROR_NOT_IMPLEMENTED;
460 NS_IMETHODIMP
461 LazyIdleThread::GetLastLongNonIdleTaskEnd(TimeStamp* _retval) {
462 return NS_ERROR_NOT_IMPLEMENTED;
465 NS_IMETHODIMP
466 LazyIdleThread::SetNameForWakeupTelemetry(const nsACString& aName) {
467 return NS_ERROR_NOT_IMPLEMENTED;
470 NS_IMETHODIMP
471 LazyIdleThread::AsyncShutdown() {
472 ASSERT_OWNING_THREAD();
473 return NS_ERROR_NOT_IMPLEMENTED;
476 NS_IMETHODIMP
477 LazyIdleThread::Shutdown() {
478 ASSERT_OWNING_THREAD();
480 mShutdown = true;
482 nsresult rv = ShutdownThread();
483 MOZ_ASSERT(!mThread, "Should have destroyed this by now!");
485 mIdleObserver = nullptr;
487 if (NS_WARN_IF(NS_FAILED(rv))) {
488 return rv;
491 return NS_OK;
494 NS_IMETHODIMP
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;
502 NS_IMETHODIMP
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;
510 NS_IMETHODIMP
511 LazyIdleThread::DispatchToQueue(already_AddRefed<nsIRunnable> aEvent,
512 EventQueuePriority aQueue) {
513 return NS_ERROR_NOT_IMPLEMENTED;
516 NS_IMETHODIMP
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;
524 NS_IMETHODIMP
525 LazyIdleThread::Notify(nsITimer* aTimer) {
526 ASSERT_OWNING_THREAD();
529 MutexAutoLock lock(mMutex);
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;
538 nsresult rv = ShutdownThread();
539 if (NS_WARN_IF(NS_FAILED(rv))) {
540 return rv;
543 return NS_OK;
546 NS_IMETHODIMP
547 LazyIdleThread::GetName(nsACString& aName) {
548 aName.Assign(mName);
549 return NS_OK;
552 NS_IMETHODIMP
553 LazyIdleThread::OnDispatchedEvent() {
554 MOZ_ASSERT(mOwningEventTarget->IsOnCurrentThread());
555 return NS_OK;
558 NS_IMETHODIMP
559 LazyIdleThread::OnProcessNextEvent(nsIThreadInternal* /* aThread */,
560 bool /* aMayWait */) {
561 return NS_OK;
564 NS_IMETHODIMP
565 LazyIdleThread::AfterProcessNextEvent(nsIThreadInternal* /* aThread */,
566 bool aEventWasProcessed) {
567 bool shouldNotifyIdle;
569 MutexAutoLock lock(mMutex);
571 if (aEventWasProcessed) {
572 MOZ_ASSERT(mPendingEventCount, "Mismatched calls to observer methods!");
573 --mPendingEventCount;
576 if (mThreadIsShuttingDown) {
577 // We're shutting down, no need to fire any timer.
578 return NS_OK;
581 shouldNotifyIdle = !mPendingEventCount;
582 if (shouldNotifyIdle) {
583 MOZ_ASSERT(mIdleNotificationCount < UINT32_MAX, "Way too many!");
584 mIdleNotificationCount++;
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;
595 nsresult rv =
596 mOwningEventTarget->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
597 if (NS_WARN_IF(NS_FAILED(rv))) {
598 return rv;
602 return NS_OK;
605 NS_IMETHODIMP
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!");
613 Shutdown();
614 return NS_OK;
617 NS_IMETHODIMP
618 LazyIdleThread::GetEventTarget(nsIEventTarget** aEventTarget) {
619 nsCOMPtr<nsIEventTarget> target = this;
620 target.forget(aEventTarget);
621 return NS_OK;
624 nsIEventTarget* LazyIdleThread::EventTarget() { return this; }
626 nsISerialEventTarget* LazyIdleThread::SerialEventTarget() { return this; }
628 } // namespace mozilla