void LazyIdleThread::ScheduleTimer() { ASSERT_OWNING_THREAD(); bool shouldSchedule; { MutexAutoLock lock(mMutex); MOZ_ASSERT(mIdleNotificationCount, "Should have at least one!"); --mIdleNotificationCount; shouldSchedule = !mIdleNotificationCount && !mPendingEventCount; } if (NS_FAILED(mIdleTimer->Cancel())) { NS_WARNING("Failed to cancel timer!"); } if (shouldSchedule && NS_FAILED(mIdleTimer->InitWithCallback(this, mIdleTimeoutMS, nsITimer::TYPE_ONE_SHOT))) { NS_WARNING("Failed to schedule timer!"); } }
NS_IMETHODIMP LazyIdleThread::Dispatch(already_AddRefed<nsIRunnable> aEvent, uint32_t aFlags) { ASSERT_OWNING_THREAD(); nsCOMPtr<nsIRunnable> event(aEvent); // avoid leaks // LazyIdleThread can't always support synchronous dispatch currently. if (NS_WARN_IF(aFlags != NS_DISPATCH_NORMAL)) { return NS_ERROR_NOT_IMPLEMENTED; } if (NS_WARN_IF(mShutdown)) { return NS_ERROR_UNEXPECTED; } // If our thread is shutting down then we can't actually dispatch right now. // Queue this runnable for later. if (UseRunnableQueue()) { mQueuedRunnables->AppendElement(event); return NS_OK; } nsresult rv = EnsureThread(); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } PreDispatch(); return mThread->Dispatch(event.forget(), aFlags); }
NS_IMETHODIMP LazyIdleThread::Dispatch(nsIRunnable* aEvent, uint32_t aFlags) { ASSERT_OWNING_THREAD(); // LazyIdleThread can't always support synchronous dispatch currently. if (NS_WARN_IF(aFlags != NS_DISPATCH_NORMAL)) return NS_ERROR_NOT_IMPLEMENTED; // If our thread is shutting down then we can't actually dispatch right now. // Queue this runnable for later. if (UseRunnableQueue()) { mQueuedRunnables->AppendElement(aEvent); return NS_OK; } nsresult rv = EnsureThread(); if (NS_WARN_IF(NS_FAILED(rv))) return rv; PreDispatch(); return mThread->Dispatch(aEvent, aFlags); }
nsresult LazyIdleThread::EnsureThread() { ASSERT_OWNING_THREAD(); if (mShutdown) { return NS_ERROR_UNEXPECTED; } if (mThread) { return NS_OK; } MOZ_ASSERT(!mPendingEventCount, "Shouldn't have events yet!"); MOZ_ASSERT(!mIdleNotificationCount, "Shouldn't have idle events yet!"); MOZ_ASSERT(!mIdleTimer, "Should have killed this long ago!"); MOZ_ASSERT(!mThreadIsShuttingDown, "Should have cleared that!"); nsresult rv; if (mShutdownMethod == AutomaticShutdown && NS_IsMainThread()) { nsCOMPtr<nsIObserverService> obs = do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } rv = obs->AddObserver(this, "xpcom-shutdown-threads", false); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } } mIdleTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv); if (NS_WARN_IF(!mIdleTimer)) { return NS_ERROR_UNEXPECTED; } nsCOMPtr<nsIRunnable> runnable = NewRunnableMethod(this, &LazyIdleThread::InitThread); if (NS_WARN_IF(!runnable)) { return NS_ERROR_UNEXPECTED; } rv = NS_NewThread(getter_AddRefs(mThread), runnable); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } return NS_OK; }
NS_IMETHODIMP LazyIdleThread::Shutdown() { ASSERT_OWNING_THREAD(); mShutdown = true; nsresult rv = ShutdownThread(); MOZ_ASSERT(!mThread, "Should have destroyed this by now!"); mIdleObserver = nullptr; NS_ENSURE_SUCCESS(rv, rv); return NS_OK; }
NS_IMETHODIMP LazyIdleThread::Shutdown() { ASSERT_OWNING_THREAD(); mShutdown = true; nsresult rv = ShutdownThread(); MOZ_ASSERT(!mThread, "Should have destroyed this by now!"); mIdleObserver = nullptr; if (NS_WARN_IF(NS_FAILED(rv))) return rv; return NS_OK; }
void LazyIdleThread::DisableIdleTimeout() { ASSERT_OWNING_THREAD(); if (!mIdleTimeoutEnabled) { return; } mIdleTimeoutEnabled = false; if (mIdleTimer && NS_FAILED(mIdleTimer->Cancel())) { NS_WARNING("Failed to cancel timer!"); } MutexAutoLock lock(mMutex); // Pretend we have a pending event to keep the idle timer from firing. MOZ_ASSERT(mPendingEventCount < UINT32_MAX, "Way too many!"); mPendingEventCount++; }
NS_IMETHODIMP LazyIdleThread::Notify(nsITimer* aTimer) { ASSERT_OWNING_THREAD(); { MutexAutoLock lock(mMutex); if (mPendingEventCount || mIdleNotificationCount) { // Another event was scheduled since this timer was set. Don't do // anything and wait for the timer to fire again. return NS_OK; } } nsresult rv = ShutdownThread(); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; }
void LazyIdleThread::EnableIdleTimeout() { ASSERT_OWNING_THREAD(); if (mIdleTimeoutEnabled) { return; } mIdleTimeoutEnabled = true; { MutexAutoLock lock(mMutex); MOZ_ASSERT(mPendingEventCount, "Mismatched calls to observer methods!"); --mPendingEventCount; } if (mThread) { nsCOMPtr<nsIRunnable> runnable(new nsRunnable()); if (NS_FAILED(Dispatch(runnable, NS_DISPATCH_NORMAL))) { NS_WARNING("Failed to dispatch!"); } } }
nsresult LazyIdleThread::ShutdownThread() { ASSERT_OWNING_THREAD(); // Before calling Shutdown() on the real thread we need to put a queue in // place in case a runnable is posted to the thread while it's in the // process of shutting down. This will be our queue. nsAutoTArray<nsCOMPtr<nsIRunnable>, 10> queuedRunnables; nsresult rv; if (mThread) { if (mShutdownMethod == AutomaticShutdown && NS_IsMainThread()) { nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); NS_WARN_IF_FALSE(obs, "Failed to get observer service!"); if (obs && NS_FAILED(obs->RemoveObserver(this, "xpcom-shutdown-threads"))) { NS_WARNING("Failed to remove observer!"); } } if (mIdleObserver) { mIdleObserver->Observe(static_cast<nsIThread*>(this), IDLE_THREAD_TOPIC, nullptr); } #ifdef DEBUG { MutexAutoLock lock(mMutex); MOZ_ASSERT(!mThreadIsShuttingDown, "Huh?!"); } #endif nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableMethod(this, &LazyIdleThread::CleanupThread); NS_ENSURE_TRUE(runnable, NS_ERROR_FAILURE); PreDispatch(); rv = mThread->Dispatch(runnable, NS_DISPATCH_NORMAL); NS_ENSURE_SUCCESS(rv, rv); // Put the temporary queue in place before calling Shutdown(). mQueuedRunnables = &queuedRunnables; if (NS_FAILED(mThread->Shutdown())) { NS_ERROR("Failed to shutdown the thread!"); } // Now unset the queue. mQueuedRunnables = nullptr; mThread = nullptr; { MutexAutoLock lock(mMutex); MOZ_ASSERT(!mPendingEventCount, "Huh?!"); MOZ_ASSERT(!mIdleNotificationCount, "Huh?!"); MOZ_ASSERT(mThreadIsShuttingDown, "Huh?!"); mThreadIsShuttingDown = false; } } if (mIdleTimer) { rv = mIdleTimer->Cancel(); NS_ENSURE_SUCCESS(rv, rv); mIdleTimer = nullptr; } // If our temporary queue has any runnables then we need to dispatch them. if (queuedRunnables.Length()) { // If the thread manager has gone away then these runnables will never run. if (mShutdown) { NS_ERROR("Runnables dispatched to LazyIdleThread will never run!"); return NS_OK; } // Re-dispatch the queued runnables. for (uint32_t index = 0; index < queuedRunnables.Length(); index++) { nsCOMPtr<nsIRunnable> runnable; runnable.swap(queuedRunnables[index]); MOZ_ASSERT(runnable, "Null runnable?!"); if (NS_FAILED(Dispatch(runnable, NS_DISPATCH_NORMAL))) { NS_ERROR("Failed to re-dispatch queued runnable!"); } } } return NS_OK; }
NS_IMETHODIMP LazyIdleThread::AsyncShutdown() { ASSERT_OWNING_THREAD(); return NS_ERROR_NOT_IMPLEMENTED; }