mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-15 14:25:52 +00:00
9edd615af7
This patch refactors the nsThread event queue to clean it up and to make it easier to restructure. The fundamental concepts are as follows: Each nsThread will have a pointer to a refcounted SynchronizedEventQueue. A SynchronizedEQ takes care of doing the locking and condition variable work when posting and popping events. For the actual storage of events, it delegates to an AbstractEventQueue data structure. It keeps a UniquePtr to the AbstractEventQueue that it uses for storage. Both SynchronizedEQ and AbstractEventQueue are abstract classes. There is only one concrete implementation of SynchronizedEQ in this patch, which is called ThreadEventQueue. ThreadEventQueue uses locks and condition variables to post and pop events the same way nsThread does. It also encapsulates the functionality that DOM workers need to implement their special event loops (PushEventQueue and PopEventQueue). In later Quantum DOM work, I plan to have another SynchronizedEQ implementation for the main thread, called SchedulerEventQueue. It will have special code for the cooperatively scheduling threads in Quantum DOM. There are two concrete implementations of AbstractEventQueue in this patch: EventQueue and PrioritizedEventQueue. EventQueue replaces the old nsEventQueue. The other AbstractEventQueue implementation is PrioritizedEventQueue, which uses multiple queues for different event priorities. The final major piece here is ThreadEventTarget, which splits some of the code for posting events out of nsThread. Eventually, my plan is for multiple cooperatively scheduled nsThreads to be able to share a ThreadEventTarget. In this patch, though, each nsThread has its own ThreadEventTarget. The class's purpose is just to collect some related code together. One final note: I tried to avoid virtual dispatch overhead as much as possible. Calls to SynchronizedEQ methods do use virtual dispatch, since I plan to use different implementations for different threads with Quantum DOM. But all the calls to EventQueue methods should be non-virtual. Although the methods are declared virtual, all the classes used are final and the concrete classes involved should all be known through templatization. MozReview-Commit-ID: 9Evtr9oIJvx
646 lines
15 KiB
C++
646 lines
15 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "LazyIdleThread.h"
|
|
|
|
#include "nsIObserverService.h"
|
|
|
|
#include "GeckoProfiler.h"
|
|
#include "nsComponentManagerUtils.h"
|
|
#include "nsIIdlePeriod.h"
|
|
#include "nsServiceManagerUtils.h"
|
|
#include "nsThreadUtils.h"
|
|
#include "mozilla/Services.h"
|
|
|
|
#ifdef DEBUG
|
|
#define ASSERT_OWNING_THREAD() \
|
|
do { \
|
|
MOZ_ASSERT(mOwningEventTarget->IsOnCurrentThread()); \
|
|
} while(0)
|
|
#else
|
|
#define ASSERT_OWNING_THREAD() /* nothing */
|
|
#endif
|
|
|
|
namespace mozilla {
|
|
|
|
LazyIdleThread::LazyIdleThread(uint32_t aIdleTimeoutMS,
|
|
const nsACString& aName,
|
|
ShutdownMethod aShutdownMethod,
|
|
nsIObserver* aIdleObserver)
|
|
: mMutex("LazyIdleThread::mMutex")
|
|
, mOwningEventTarget(GetCurrentThreadSerialEventTarget())
|
|
, mIdleObserver(aIdleObserver)
|
|
, mQueuedRunnables(nullptr)
|
|
, mIdleTimeoutMS(aIdleTimeoutMS)
|
|
, mPendingEventCount(0)
|
|
, mIdleNotificationCount(0)
|
|
, mShutdownMethod(aShutdownMethod)
|
|
, mShutdown(false)
|
|
, mThreadIsShuttingDown(false)
|
|
, mIdleTimeoutEnabled(true)
|
|
, mName(aName)
|
|
{
|
|
MOZ_ASSERT(mOwningEventTarget, "Need owning thread!");
|
|
}
|
|
|
|
LazyIdleThread::~LazyIdleThread()
|
|
{
|
|
ASSERT_OWNING_THREAD();
|
|
|
|
Shutdown();
|
|
}
|
|
|
|
void
|
|
LazyIdleThread::SetWeakIdleObserver(nsIObserver* aObserver)
|
|
{
|
|
ASSERT_OWNING_THREAD();
|
|
|
|
if (mShutdown) {
|
|
NS_WARNING_ASSERTION(!aObserver,
|
|
"Setting an observer after Shutdown was called!");
|
|
return;
|
|
}
|
|
|
|
mIdleObserver = aObserver;
|
|
}
|
|
|
|
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++;
|
|
}
|
|
|
|
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 Runnable("LazyIdleThreadDummyRunnable"));
|
|
if (NS_FAILED(Dispatch(runnable.forget(), NS_DISPATCH_NORMAL))) {
|
|
NS_WARNING("Failed to dispatch!");
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
LazyIdleThread::PreDispatch()
|
|
{
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
MOZ_ASSERT(mPendingEventCount < UINT32_MAX, "Way too many!");
|
|
mPendingEventCount++;
|
|
}
|
|
|
|
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(
|
|
"LazyIdleThread::InitThread", this, &LazyIdleThread::InitThread);
|
|
if (NS_WARN_IF(!runnable)) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
rv = NS_NewNamedThread("Lazy Idle", getter_AddRefs(mThread), runnable);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
LazyIdleThread::InitThread()
|
|
{
|
|
// Happens on mThread but mThread may not be set yet...
|
|
|
|
nsCOMPtr<nsIThreadInternal> thread(do_QueryInterface(NS_GetCurrentThread()));
|
|
MOZ_ASSERT(thread, "This should always succeed!");
|
|
|
|
if (NS_FAILED(thread->SetObserver(this))) {
|
|
NS_WARNING("Failed to set thread observer!");
|
|
}
|
|
}
|
|
|
|
void
|
|
LazyIdleThread::CleanupThread()
|
|
{
|
|
nsCOMPtr<nsIThreadInternal> thread(do_QueryInterface(NS_GetCurrentThread()));
|
|
MOZ_ASSERT(thread, "This should always succeed!");
|
|
|
|
if (NS_FAILED(thread->SetObserver(nullptr))) {
|
|
NS_WARNING("Failed to set thread observer!");
|
|
}
|
|
|
|
{
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
MOZ_ASSERT(!mThreadIsShuttingDown, "Shouldn't be true ever!");
|
|
mThreadIsShuttingDown = true;
|
|
}
|
|
}
|
|
|
|
void
|
|
LazyIdleThread::ScheduleTimer()
|
|
{
|
|
ASSERT_OWNING_THREAD();
|
|
|
|
bool shouldSchedule;
|
|
{
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
MOZ_ASSERT(mIdleNotificationCount, "Should have at least one!");
|
|
--mIdleNotificationCount;
|
|
|
|
shouldSchedule = !mIdleNotificationCount && !mPendingEventCount;
|
|
}
|
|
|
|
if (mIdleTimer) {
|
|
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!");
|
|
}
|
|
}
|
|
}
|
|
|
|
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.
|
|
AutoTArray<nsCOMPtr<nsIRunnable>, 10> queuedRunnables;
|
|
|
|
nsresult rv;
|
|
|
|
// Make sure to cancel the shutdown timer before spinning the event loop
|
|
// during |mThread->Shutdown()| below. Otherwise the timer might fire and we
|
|
// could reenter here.
|
|
if (mIdleTimer) {
|
|
rv = mIdleTimer->Cancel();
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
mIdleTimer = nullptr;
|
|
}
|
|
|
|
if (mThread) {
|
|
if (mShutdownMethod == AutomaticShutdown && NS_IsMainThread()) {
|
|
nsCOMPtr<nsIObserverService> obs =
|
|
mozilla::services::GetObserverService();
|
|
NS_WARNING_ASSERTION(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 = NewRunnableMethod(
|
|
"LazyIdleThread::CleanupThread", this, &LazyIdleThread::CleanupThread);
|
|
if (NS_WARN_IF(!runnable)) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
PreDispatch();
|
|
|
|
rv = mThread->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return 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 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.forget(), NS_DISPATCH_NORMAL))) {
|
|
NS_ERROR("Failed to re-dispatch queued runnable!");
|
|
}
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
LazyIdleThread::SelfDestruct()
|
|
{
|
|
MOZ_ASSERT(mRefCnt == 1, "Bad refcount!");
|
|
delete this;
|
|
}
|
|
|
|
NS_IMPL_ADDREF(LazyIdleThread)
|
|
|
|
NS_IMETHODIMP_(MozExternalRefCountType)
|
|
LazyIdleThread::Release()
|
|
{
|
|
nsrefcnt count = --mRefCnt;
|
|
NS_LOG_RELEASE(this, count, "LazyIdleThread");
|
|
|
|
if (!count) {
|
|
// Stabilize refcount.
|
|
mRefCnt = 1;
|
|
|
|
nsCOMPtr<nsIRunnable> runnable = NewNonOwningRunnableMethod(
|
|
"LazyIdleThread::SelfDestruct", this, &LazyIdleThread::SelfDestruct);
|
|
NS_WARNING_ASSERTION(runnable, "Couldn't make runnable!");
|
|
|
|
if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
|
|
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
|
|
// The only way this could fail is if we're in shutdown, and in that case
|
|
// threads should have been joined already. Deleting here isn't dangerous
|
|
// anymore because we won't spin the event loop waiting to join the
|
|
// thread.
|
|
SelfDestruct();
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
NS_IMPL_QUERY_INTERFACE(LazyIdleThread, nsIThread,
|
|
nsIEventTarget,
|
|
nsISerialEventTarget,
|
|
nsITimerCallback,
|
|
nsIThreadObserver,
|
|
nsIObserver,
|
|
nsINamed)
|
|
|
|
NS_IMETHODIMP
|
|
LazyIdleThread::DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags)
|
|
{
|
|
nsCOMPtr<nsIRunnable> event(aEvent);
|
|
return Dispatch(event.forget(), aFlags);
|
|
}
|
|
|
|
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::DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t)
|
|
{
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
LazyIdleThread::IsOnCurrentThread(bool* aIsOnCurrentThread)
|
|
{
|
|
if (mThread) {
|
|
return mThread->IsOnCurrentThread(aIsOnCurrentThread);
|
|
}
|
|
|
|
*aIsOnCurrentThread = false;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP_(bool)
|
|
LazyIdleThread::IsOnCurrentThreadInfallible()
|
|
{
|
|
if (mThread) {
|
|
return mThread->IsOnCurrentThread();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
LazyIdleThread::GetPRThread(PRThread** aPRThread)
|
|
{
|
|
if (mThread) {
|
|
return mThread->GetPRThread(aPRThread);
|
|
}
|
|
|
|
*aPRThread = nullptr;
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
LazyIdleThread::GetCanInvokeJS(bool* aCanInvokeJS)
|
|
{
|
|
*aCanInvokeJS = false;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
LazyIdleThread::SetCanInvokeJS(bool aCanInvokeJS)
|
|
{
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
LazyIdleThread::AsyncShutdown()
|
|
{
|
|
ASSERT_OWNING_THREAD();
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
LazyIdleThread::HasPendingEvents(bool* aHasPendingEvents)
|
|
{
|
|
// This is only supposed to be called from the thread itself so it's not
|
|
// implemented here.
|
|
NS_NOTREACHED("Shouldn't ever call this!");
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
LazyIdleThread::IdleDispatch(already_AddRefed<nsIRunnable> aEvent)
|
|
{
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
LazyIdleThread::ProcessNextEvent(bool aMayWait,
|
|
bool* aEventWasProcessed)
|
|
{
|
|
// This is only supposed to be called from the thread itself so it's not
|
|
// implemented here.
|
|
NS_NOTREACHED("Shouldn't ever call this!");
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
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();
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
LazyIdleThread::GetName(nsACString& aName)
|
|
{
|
|
aName.AssignLiteral("LazyIdleThread");
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
LazyIdleThread::OnDispatchedEvent()
|
|
{
|
|
MOZ_ASSERT(mOwningEventTarget->IsOnCurrentThread());
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
LazyIdleThread::OnProcessNextEvent(nsIThreadInternal* /* aThread */,
|
|
bool /* aMayWait */)
|
|
{
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
LazyIdleThread::AfterProcessNextEvent(nsIThreadInternal* /* aThread */,
|
|
bool aEventWasProcessed)
|
|
{
|
|
bool shouldNotifyIdle;
|
|
{
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
if (aEventWasProcessed) {
|
|
MOZ_ASSERT(mPendingEventCount, "Mismatched calls to observer methods!");
|
|
--mPendingEventCount;
|
|
}
|
|
|
|
if (mThreadIsShuttingDown) {
|
|
// We're shutting down, no need to fire any timer.
|
|
return NS_OK;
|
|
}
|
|
|
|
shouldNotifyIdle = !mPendingEventCount;
|
|
if (shouldNotifyIdle) {
|
|
MOZ_ASSERT(mIdleNotificationCount < UINT32_MAX, "Way too many!");
|
|
mIdleNotificationCount++;
|
|
}
|
|
}
|
|
|
|
if (shouldNotifyIdle) {
|
|
nsCOMPtr<nsIRunnable> runnable = NewRunnableMethod(
|
|
"LazyIdleThread::ScheduleTimer", this, &LazyIdleThread::ScheduleTimer);
|
|
if (NS_WARN_IF(!runnable)) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
nsresult rv = mOwningEventTarget->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
LazyIdleThread::Observe(nsISupports* /* aSubject */,
|
|
const char* aTopic,
|
|
const char16_t* /* aData */)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
|
|
MOZ_ASSERT(mShutdownMethod == AutomaticShutdown,
|
|
"Should not receive notifications if not AutomaticShutdown!");
|
|
MOZ_ASSERT(!strcmp("xpcom-shutdown-threads", aTopic), "Bad topic!");
|
|
|
|
Shutdown();
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
LazyIdleThread::GetEventTarget(nsIEventTarget** aEventTarget)
|
|
{
|
|
nsCOMPtr<nsIEventTarget> target = this;
|
|
target.forget(aEventTarget);
|
|
return NS_OK;
|
|
}
|
|
|
|
nsIEventTarget*
|
|
LazyIdleThread::EventTarget()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
nsISerialEventTarget*
|
|
LazyIdleThread::SerialEventTarget()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
} // namespace mozilla
|