Index: Source/platform/scheduler/Scheduler.cpp |
diff --git a/Source/platform/scheduler/Scheduler.cpp b/Source/platform/scheduler/Scheduler.cpp |
index 4a447721f58921b8fe0500568ed583f76288b5c8..0460c36fd96a8c1b6da6681f52315c8eb7918aef 100644 |
--- a/Source/platform/scheduler/Scheduler.cpp |
+++ b/Source/platform/scheduler/Scheduler.cpp |
@@ -10,12 +10,16 @@ |
#include "platform/ThreadTimers.h" |
#include "platform/TraceEvent.h" |
#include "public/platform/Platform.h" |
+#include "public/platform/WebBeginFrameArgs.h" |
#include "wtf/MainThread.h" |
#include "wtf/ThreadingPrimitives.h" |
namespace blink { |
+// Pending low prioirty tasks have a maximum run time of 20ms per invocation of runPendingLowPriorityTasks. |
+const double Scheduler::s_maximumLowPrioirtyTaskExecutionTimeSeconds = 0.02; |
+ |
namespace { |
// Can be created from any thread. |
@@ -63,7 +67,7 @@ public: |
ASSERT(scheduler); |
if (!scheduler) |
return; |
- scheduler->swapQueuesRunPendingTasksAndAllowHighPriorityTaskRunnerPosting(); |
+ scheduler->runPendingHighPriorityTasksAndAllowTaskRunnerPosting(); |
} |
}; |
@@ -86,13 +90,26 @@ public: |
// FIXME: This check should't be necessary, tasks should not outlive blink. |
ASSERT(scheduler); |
if (scheduler) |
- Scheduler::shared()->swapQueuesAndRunPendingTasks(); |
+ Scheduler::shared()->runPendingHighPrioirtyTasks(); |
m_task.run(); |
} |
Scheduler::TracedTask m_task; |
}; |
+class Scheduler::MainThreadPendingLowPriorityTaskRunner : public WebThread::Task { |
+public: |
+ virtual void run() OVERRIDE |
+ { |
+ Scheduler* scheduler = Scheduler::shared(); |
+ // FIXME: This check should't be necessary, tasks should not outlive blink. |
+ ASSERT(scheduler); |
+ if (!scheduler) |
+ return; |
+ scheduler->runPendingLowPriorityTasks(); |
+ } |
+}; |
+ |
Scheduler* Scheduler::s_sharedScheduler = nullptr; |
void Scheduler::initializeOnMainThread() |
@@ -114,21 +131,30 @@ Scheduler* Scheduler::shared() |
Scheduler::Scheduler() |
: m_sharedTimerFunction(nullptr) |
, m_mainThread(blink::Platform::current()->currentThread()) |
+ , m_compositorStart(0.0) |
+ , m_compositorDeadline(0.0) |
+ , m_compositorInterval(0.0) |
, m_highPriorityTaskCount(0) |
- , m_highPriorityTaskRunnerPosted(false) |
+ , m_activeLowPrioirtyQueue(nullptr) |
{ |
+ m_mutexProtected.m_highPriorityTaskRunnerPosted = false; |
+ m_mutexProtected.m_lowPriorityTaskRunnerPosted = false; |
} |
Scheduler::~Scheduler() |
{ |
while (hasPendingHighPriorityWork()) { |
- swapQueuesAndRunPendingTasks(); |
+ runPendingHighPrioirtyTasks(); |
} |
} |
void Scheduler::willBeginFrame(const WebBeginFrameArgs& args) |
{ |
- // TODO: Use frame deadline and interval to schedule idle tasks. |
+ ASSERT(isMainThread()); |
+ |
+ m_compositorStart = args.lastFrameTimeMonotonic; |
+ m_compositorDeadline = args.deadline; |
+ m_compositorInterval = args.interval; |
} |
void Scheduler::didCommitFrameToCompositor() |
@@ -150,26 +176,87 @@ void Scheduler::postTask(const TraceLocation& location, const Task& task) |
void Scheduler::postInputTask(const TraceLocation& location, const Task& task) |
{ |
Locker<Mutex> lock(m_pendingTasksMutex); |
- m_pendingHighPriorityTasks.append(TracedTask(task, location)); |
+ m_mutexProtected.m_pendingHighPriorityTasks.append(TracedTask(task, location)); |
atomicIncrement(&m_highPriorityTaskCount); |
+ TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("blink.scheduler"), "PendingHighPriorityTasks", m_highPriorityTaskCount); |
maybePostMainThreadPendingHighPriorityTaskRunner(); |
} |
void Scheduler::postCompositorTask(const TraceLocation& location, const Task& task) |
{ |
Locker<Mutex> lock(m_pendingTasksMutex); |
- m_pendingHighPriorityTasks.append(TracedTask(task, location)); |
+ m_mutexProtected.m_pendingHighPriorityTasks.append(TracedTask(task, location)); |
atomicIncrement(&m_highPriorityTaskCount); |
+ TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("blink.scheduler"), "PendingHighPriorityTasks", m_highPriorityTaskCount); |
maybePostMainThreadPendingHighPriorityTaskRunner(); |
} |
+void Scheduler::postIpcTask(const TraceLocation& location, const Task& task) |
+{ |
+ Locker<Mutex> lock(m_pendingTasksMutex); |
+ m_mutexProtected.m_pendingLowPriorityTasks.append(TracedTask(task, location)); |
+ TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("blink.scheduler"), "PendingLowPriorityTasks", |
+ m_mutexProtected.m_pendingLowPriorityTasks.allQueuesSize()); |
+ maybePostMainThreadPendingLowPriorityTaskRunner(Platform::current()->monotonicallyIncreasingTime()); |
+} |
+ |
void Scheduler::maybePostMainThreadPendingHighPriorityTaskRunner() |
{ |
ASSERT(m_pendingTasksMutex.locked()); |
- if (m_highPriorityTaskRunnerPosted) |
+ if (m_mutexProtected.m_highPriorityTaskRunnerPosted) |
return; |
m_mainThread->postTask(new MainThreadPendingHighPriorityTaskRunner()); |
- m_highPriorityTaskRunnerPosted = true; |
+ m_mutexProtected.m_highPriorityTaskRunnerPosted = true; |
+} |
+ |
+double Scheduler::nextPredictedCompositorStart() |
+{ |
+ return nextPredictedCompositorStartInternal(Platform::current()->monotonicallyIncreasingTime()); |
+} |
+ |
+double Scheduler::nextPredictedCompositorDeadline() |
+{ |
+ return nextPredictedCompositorDeadlineInternal(Platform::current()->monotonicallyIncreasingTime()); |
+} |
+ |
+double Scheduler::nextPredictedCompositorStartInternal(double currentTime) |
+{ |
+ if (currentTime < m_compositorStart) |
+ return m_compositorStart; |
+ |
+ double interval = m_compositorInterval; |
+ if (interval == 0.0) |
+ return 0.0; |
+ |
+ double numIntervals = ceil((currentTime - m_compositorStart) / interval); |
+ return m_compositorStart + interval * numIntervals; |
+} |
+ |
+double Scheduler::nextPredictedCompositorDeadlineInternal(double currentTime) |
+{ |
+ if (currentTime < m_compositorDeadline) |
+ return m_compositorDeadline; |
+ |
+ double interval = m_compositorInterval; |
+ if (interval == 0.0) |
+ return 0.0; |
+ |
+ double numIntervals = ceil((currentTime - m_compositorDeadline) / interval); |
+ return m_compositorDeadline + interval * numIntervals; |
+} |
+ |
+void Scheduler::maybePostMainThreadPendingLowPriorityTaskRunner(double currentTime) |
+{ |
+ ASSERT(m_pendingTasksMutex.locked()); |
+ if (m_mutexProtected.m_lowPriorityTaskRunnerPosted) |
+ return; |
+ |
+ // MainThreadPendingLowPriorityTaskRunner should run after the compositor work is expected to have finished so that |
+ // it's less likely to be preempted by a high priority task. |
+ double delaySec = nextPredictedCompositorDeadlineInternal(currentTime) - currentTime; |
+ long long delayMs = static_cast<long long>(delaySec * 1000.0); |
+ m_mainThread->postDelayedTask(new MainThreadPendingLowPriorityTaskRunner(), delayMs); |
+ m_mutexProtected.m_lowPriorityTaskRunnerPosted = true; |
} |
void Scheduler::postIdleTask(const TraceLocation& location, const IdleTask& idleTask) |
@@ -182,36 +269,36 @@ void Scheduler::tickSharedTimer() |
TRACE_EVENT0("blink", "Scheduler::tickSharedTimer"); |
// Run any high priority tasks that are queued up, otherwise the blink timers will yield immediately. |
- bool workDone = swapQueuesAndRunPendingTasks(); |
+ bool workDone = runPendingHighPrioirtyTasks(); |
m_sharedTimerFunction(); |
// The blink timers may have just yielded, so run any high priority tasks that where queued up |
// while the blink timers were executing. |
if (!workDone) |
- swapQueuesAndRunPendingTasks(); |
+ runPendingHighPrioirtyTasks(); |
} |
-bool Scheduler::swapQueuesAndRunPendingTasks() |
+bool Scheduler::runPendingHighPrioirtyTasks() |
{ |
ASSERT(isMainThread()); |
// These locks guard against another thread posting input or compositor tasks while we swap the buffers. |
// One the buffers have been swapped we can safely access the returned deque without having to lock. |
m_pendingTasksMutex.lock(); |
- Deque<TracedTask>& highPriorityTasks = m_pendingHighPriorityTasks.swapBuffers(); |
+ Deque<TracedTask>& highPriorityTasks = m_mutexProtected.m_pendingHighPriorityTasks.swapBuffers(); |
m_pendingTasksMutex.unlock(); |
return executeHighPriorityTasks(highPriorityTasks); |
} |
-void Scheduler::swapQueuesRunPendingTasksAndAllowHighPriorityTaskRunnerPosting() |
+void Scheduler::runPendingHighPriorityTasksAndAllowTaskRunnerPosting() |
{ |
ASSERT(isMainThread()); |
// These locks guard against another thread posting input or compositor tasks while we swap the buffers. |
// One the buffers have been swapped we can safely access the returned deque without having to lock. |
m_pendingTasksMutex.lock(); |
- Deque<TracedTask>& highPriorityTasks = m_pendingHighPriorityTasks.swapBuffers(); |
- m_highPriorityTaskRunnerPosted = false; |
+ Deque<TracedTask>& highPriorityTasks = m_mutexProtected.m_pendingHighPriorityTasks.swapBuffers(); |
+ m_mutexProtected.m_highPriorityTaskRunnerPosted = false; |
m_pendingTasksMutex.unlock(); |
executeHighPriorityTasks(highPriorityTasks); |
} |
@@ -227,9 +314,46 @@ bool Scheduler::executeHighPriorityTasks(Deque<TracedTask>& highPriorityTasks) |
int highPriorityTaskCount = atomicSubtract(&m_highPriorityTaskCount, highPriorityTasksExecuted); |
ASSERT_UNUSED(highPriorityTaskCount, highPriorityTaskCount >= 0); |
+ TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("blink.scheduler"), "PendingHighPriorityTasks", m_highPriorityTaskCount); |
return highPriorityTasksExecuted > 0; |
} |
+void Scheduler::reloadEmptyLowPrioirtyQueue() |
+{ |
+ if (m_activeLowPrioirtyQueue && !m_activeLowPrioirtyQueue->isEmpty()) |
+ return; |
+ |
+ Locker<Mutex> lock(m_pendingTasksMutex); |
+ m_activeLowPrioirtyQueue = &m_mutexProtected.m_pendingLowPriorityTasks.swapBuffers(); |
+} |
+ |
+void Scheduler::runPendingLowPriorityTasks() |
+{ |
+ TRACE_EVENT0("blink", "Scheduler::runPendingLowPriorityTasks"); |
+ m_pendingTasksMutex.lock(); |
+ m_mutexProtected.m_lowPriorityTaskRunnerPosted = false; |
+ m_pendingTasksMutex.unlock(); |
+ |
+ double startTime = Platform::current()->monotonicallyIncreasingTime(); |
+ double endTime = startTime + s_maximumLowPrioirtyTaskExecutionTimeSeconds; |
+ |
+ for (;;) { |
+ reloadEmptyLowPrioirtyQueue(); |
+ if (m_activeLowPrioirtyQueue->isEmpty() || hasPendingHighPriorityWork()) |
+ break; |
+ m_activeLowPrioirtyQueue->takeFirst().run(); |
+ |
+ if (Platform::current()->monotonicallyIncreasingTime() >= endTime) |
+ break; |
+ } |
+ |
+ TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("blink.scheduler"), "PendingLowPriorityTasks", |
+ m_mutexProtected.m_pendingLowPriorityTasks.allQueuesSize()); |
+ |
+ if (!m_activeLowPrioirtyQueue->isEmpty()) |
+ maybePostMainThreadPendingLowPriorityTaskRunner(Platform::current()->monotonicallyIncreasingTime()); |
+} |
+ |
void Scheduler::sharedTimerAdapter() |
{ |
shared()->tickSharedTimer(); |