diff --git a/xpcom/threads/TaskController.cpp b/xpcom/threads/TaskController.cpp index f322c74b1036..8dbc598370c5 100644 --- a/xpcom/threads/TaskController.cpp +++ b/xpcom/threads/TaskController.cpp @@ -11,6 +11,7 @@ #include #include #include "mozilla/AbstractEventQueue.h" +#include "mozilla/BackgroundHangMonitor.h" #include "mozilla/StaticMutex.h" #include "mozilla/SchedulerGroup.h" #include "mozilla/ScopeExit.h" @@ -80,6 +81,9 @@ bool TaskController::InitializeInternal() { mMTProcessingRunnable = NS_NewRunnableFunction( "TaskController::ExecutePendingMTTasks()", []() { TaskController::Get()->ProcessPendingMTTask(); }); + mMTBlockingProcessingRunnable = NS_NewRunnableFunction( + "TaskController::ExecutePendingMTTasks()", + []() { TaskController::Get()->ProcessPendingMTTask(true); }); return true; } @@ -134,6 +138,7 @@ void TaskController::AddTask(already_AddRefed&& aTask) { void TaskController::WaitForTaskOrMessage() { MutexAutoLock lock(mGraphMutex); while (!mMayHaveMainThreadTask) { + AUTO_PROFILER_LABEL("TaskController::WaitForTaskOrMessage", IDLE); mMainThreadCV.Wait(); } } @@ -144,12 +149,40 @@ void TaskController::ExecuteNextTaskOnlyMainThread() { ExecuteNextTaskOnlyMainThreadInternal(lock); } -void TaskController::ProcessPendingMTTask() { +void TaskController::ProcessPendingMTTask(bool aMayWait) { MOZ_ASSERT(NS_IsMainThread()); MutexAutoLock lock(mGraphMutex); - mHasScheduledMTRunnable = false; - ExecuteNextTaskOnlyMainThreadInternal(lock); + for (;;) { + // We only ever process one event here. However we may sometimes + // not actually process a real event because of suspended tasks. + // This loop allows us to wait until we've processed something + // in that scenario. + + mMTTaskRunnableProcessedTask = ExecuteNextTaskOnlyMainThreadInternal(lock); + + if (mMTTaskRunnableProcessedTask || !aMayWait) { + break; + } + nsCOMPtr mainIThread; + NS_GetMainThread(getter_AddRefs(mainIThread)); + nsThread* mainThread = static_cast(mainIThread.get()); + mainThread->SetRunningEventDelay(TimeDuration(), TimeStamp()); + + BackgroundHangMonitor().NotifyWait(); + + { + // ProcessNextEvent will also have attempted to wait, however we may have + // given it a Runnable when all the tasks in our task graph were suspended + // but we weren't able to cheaply determine that. + AUTO_PROFILER_LABEL("TaskController::ProcessPendingMTTask", IDLE); + mMainThreadCV.Wait(); + } + + // See bug 1651842 + mainThread->SetRunningEventDelay(TimeDuration(), TimeStamp::Now()); + BackgroundHangMonitor().NotifyActivity(); + } if (mMayHaveMainThreadTask) { EnsureMainThreadTasksScheduled(); @@ -247,7 +280,7 @@ nsIRunnable* TaskController::GetRunnableForMTTask(bool aReallyWait) { mMainThreadCV.Wait(); } - return mMTProcessingRunnable; + return aReallyWait ? mMTBlockingProcessingRunnable : mMTProcessingRunnable; } bool TaskController::HasMainThreadPendingTasks() { @@ -330,11 +363,12 @@ bool TaskController::HasMainThreadPendingTasks() { return false; } -void TaskController::ExecuteNextTaskOnlyMainThreadInternal( +bool TaskController::ExecuteNextTaskOnlyMainThreadInternal( const MutexAutoLock& aProofOfLock) { // Block to make it easier to jump to our cleanup. + bool taskRan = false; do { - bool taskRan = DoExecuteNextTaskOnlyMainThreadInternal(aProofOfLock); + taskRan = DoExecuteNextTaskOnlyMainThreadInternal(aProofOfLock); if (taskRan) { break; } @@ -357,7 +391,7 @@ void TaskController::ExecuteNextTaskOnlyMainThreadInternal( // When we unlocked, someone may have queued a new task on us. So try to // see whether we can run things again. - Unused << DoExecuteNextTaskOnlyMainThreadInternal(aProofOfLock); + taskRan = DoExecuteNextTaskOnlyMainThreadInternal(aProofOfLock); } while (false); if (mIdleTaskManager) { @@ -375,6 +409,8 @@ void TaskController::ExecuteNextTaskOnlyMainThreadInternal( mIdleTaskManager->State().RanOutOfTasks(unlock); } } + + return taskRan; } bool TaskController::DoExecuteNextTaskOnlyMainThreadInternal( diff --git a/xpcom/threads/TaskController.h b/xpcom/threads/TaskController.h index 3425272be29b..763929e525dd 100644 --- a/xpcom/threads/TaskController.h +++ b/xpcom/threads/TaskController.h @@ -283,7 +283,7 @@ class TaskController { void ExecuteNextTaskOnlyMainThread(); // Process all pending main thread tasks. - void ProcessPendingMTTask(); + void ProcessPendingMTTask(bool aMayWait = false); // This allows reprioritization of a task already in the task graph. // This may be called on any thread. @@ -296,10 +296,14 @@ class TaskController { bool HasMainThreadPendingTasks(); + // Let users know whether the last main thread task runnable did work. + bool MTTaskRunnableProcessedTask() { return mMTTaskRunnableProcessedTask; } + private: // This gets the next (highest priority) task that is only allowed to execute // on the main thread, if any, and executes it. - void ExecuteNextTaskOnlyMainThreadInternal(const MutexAutoLock& aProofOfLock); + // Returns true if it succeeded. + bool ExecuteNextTaskOnlyMainThreadInternal(const MutexAutoLock& aProofOfLock); // The guts of ExecuteNextTaskOnlyMainThreadInternal, which get idle handling // wrapped around them. Returns whether a task actually ran. @@ -338,10 +342,13 @@ class TaskController { // This ensures we keep running the main thread if we processed a task there. bool mMayHaveMainThreadTask = true; + // This stores whether the last main thread task runnable did work. + bool mMTTaskRunnableProcessedTask = false; + // Whether we have scheduled a runnable on the main thread event loop. // This is used for nsIRunnable compatibility. - bool mHasScheduledMTRunnable = false; RefPtr mMTProcessingRunnable; + RefPtr mMTBlockingProcessingRunnable; // XXX - Thread observer to notify when a new event has been dispatched nsIThreadObserver* mObserver = nullptr; diff --git a/xpcom/threads/nsThread.cpp b/xpcom/threads/nsThread.cpp index 398e22233a6e..1bad95036314 100644 --- a/xpcom/threads/nsThread.cpp +++ b/xpcom/threads/nsThread.cpp @@ -1233,7 +1233,9 @@ nsThread::ProcessNextEvent(bool aMayWait, bool* aResult) { event->Run(); - if (!usingTaskController) { + if (usingTaskController) { + *aResult = TaskController::Get()->MTTaskRunnableProcessedTask(); + } else { mEvents->DidRunEvent(); mPerformanceCounterState.RunnableDidRun(std::move(snapshot.ref())); }