Bug 1650629: Correctly wait when requested and report whether work was done by ProcessNextEvent when using TaskController. r=mstange

Differential Revision: https://phabricator.services.mozilla.com/D83004
This commit is contained in:
Bas Schouten 2020-07-09 23:41:15 +00:00
Родитель 56aa86bead
Коммит 43311c24a3
3 изменённых файлов: 56 добавлений и 11 удалений

Просмотреть файл

@ -11,6 +11,7 @@
#include <algorithm>
#include <initializer_list>
#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<Task>&& 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<nsIThread> mainIThread;
NS_GetMainThread(getter_AddRefs(mainIThread));
nsThread* mainThread = static_cast<nsThread*>(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(

Просмотреть файл

@ -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<nsIRunnable> mMTProcessingRunnable;
RefPtr<nsIRunnable> mMTBlockingProcessingRunnable;
// XXX - Thread observer to notify when a new event has been dispatched
nsIThreadObserver* mObserver = nullptr;

Просмотреть файл

@ -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()));
}