Index: Source/platform/scheduler/Scheduler.cpp |
diff --git a/Source/platform/scheduler/Scheduler.cpp b/Source/platform/scheduler/Scheduler.cpp |
index 61ae7aef1d486bd618417a2d2d62c14c08db06bc..39cb5cac817693d04ac6c4480ff835f857059ffc 100644 |
--- a/Source/platform/scheduler/Scheduler.cpp |
+++ b/Source/platform/scheduler/Scheduler.cpp |
@@ -5,59 +5,93 @@ |
#include "config.h" |
#include "platform/scheduler/Scheduler.h" |
+#include "platform/PlatformThreadData.h" |
#include "platform/Task.h" |
+#include "platform/ThreadTimers.h" |
#include "platform/TraceEvent.h" |
-#include "platform/TraceLocation.h" |
#include "public/platform/Platform.h" |
-#include "public/platform/WebThread.h" |
+#include "wtf/MainThread.h" |
+#include "wtf/ThreadingPrimitives.h" |
+ |
namespace blink { |
namespace { |
-class MainThreadTaskAdapter : public blink::WebThread::Task { |
+// Can be created from any thread. |
+// Note if the scheduler gets shutdown, this may be run after. |
+class MainThreadIdleTaskAdapter : public WebThread::Task { |
public: |
- explicit MainThreadTaskAdapter(const TraceLocation& location, const Scheduler::Task& task) |
- : m_location(location) |
- , m_task(task) |
+ MainThreadIdleTaskAdapter(const Scheduler::IdleTask& idleTask, double allottedTimeMs, const TraceLocation& location) |
+ : m_idleTask(idleTask) |
+ , m_allottedTimeMs(allottedTimeMs) |
+ , m_location(location) |
{ |
} |
// WebThread::Task implementation. |
virtual void run() OVERRIDE |
{ |
- TRACE_EVENT2("blink", "MainThreadTaskAdapter::run", |
+ TRACE_EVENT2("blink", "MainThreadIdleTaskAdapter::run", |
"src_file", m_location.fileName(), |
"src_func", m_location.functionName()); |
- m_task(); |
+ m_idleTask(m_allottedTimeMs); |
} |
private: |
- const TraceLocation m_location; |
- Scheduler::Task m_task; |
+ Scheduler::IdleTask m_idleTask; |
+ double m_allottedTimeMs; |
+ TraceLocation m_location; |
}; |
-class MainThreadIdleTaskAdapter : public blink::WebThread::Task { |
+} // namespace |
+ |
+// Typically only created from compositor or render threads. |
+// Note if the scheduler gets shutdown, this may be run after. |
+class Scheduler::MainThreadPendingHighPriorityTaskRunner : public WebThread::Task { |
public: |
- MainThreadIdleTaskAdapter(const Scheduler::IdleTask& idleTask, double allottedTimeMs) |
- : m_idleTask(idleTask) |
- , m_allottedTimeMs(allottedTimeMs) |
+ MainThreadPendingHighPriorityTaskRunner() |
{ |
+ ASSERT(Scheduler::shared()); |
} |
// WebThread::Task implementation. |
virtual void run() OVERRIDE |
{ |
- TRACE_EVENT1("blink", "MainThreadIdleTaskAdapter::run", "allottedTime", m_allottedTimeMs); |
- m_idleTask(m_allottedTimeMs); |
+ Scheduler* scheduler = Scheduler::shared(); |
+ // FIXME: This check should't be necessary, tasks should not outlive blink. |
+ ASSERT(scheduler); |
+ if (!scheduler) |
+ return; |
+ scheduler->runHighPriorityTasks(); |
} |
- |
-private: |
- Scheduler::IdleTask m_idleTask; |
- double m_allottedTimeMs; |
}; |
-} |
+ |
+// Can be created from any thread. |
+// Note if the scheduler gets shutdown, this may be run after. |
+class Scheduler::MainThreadPendingTaskRunner : public WebThread::Task { |
+public: |
+ MainThreadPendingTaskRunner( |
+ const Scheduler::Task& task, const TraceLocation& location) |
+ : m_task(task, location) |
+ { |
+ ASSERT(Scheduler::shared()); |
+ } |
+ |
+ // WebThread::Task implementation. |
+ virtual void run() OVERRIDE |
+ { |
+ Scheduler* scheduler = Scheduler::shared(); |
+ // FIXME: This check should't be necessary, tasks should not outlive blink. |
+ ASSERT(scheduler); |
+ if (scheduler) |
+ Scheduler::shared()->runHighPriorityTasks(); |
+ m_task.run(); |
+ } |
+ |
+ Scheduler::TracedTask m_task; |
+}; |
Scheduler* Scheduler::s_sharedScheduler = nullptr; |
@@ -78,50 +112,89 @@ Scheduler* Scheduler::shared() |
} |
Scheduler::Scheduler() |
- : m_mainThread(blink::Platform::current()->currentThread()) |
- , m_sharedTimerFunction(nullptr) |
+ : m_sharedTimerFunction(nullptr) |
+ , m_mainThread(blink::Platform::current()->currentThread()) |
+ , m_highPriorityTaskCount(0) |
{ |
} |
Scheduler::~Scheduler() |
{ |
+ while (hasPendingHighPriorityWork()) { |
+ runHighPriorityTasks(); |
+ } |
} |
-void Scheduler::scheduleTask(const TraceLocation& location, const Task& task) |
-{ |
- m_mainThread->postTask(new MainThreadTaskAdapter(location, task)); |
-} |
- |
-void Scheduler::scheduleIdleTask(const IdleTask& idleTask) |
+void Scheduler::scheduleIdleTask(const TraceLocation& location, const IdleTask& idleTask) |
{ |
// TODO: send a real allottedTime here. |
- m_mainThread->postTask(new MainThreadIdleTaskAdapter(idleTask, 0)); |
+ m_mainThread->postTask(new MainThreadIdleTaskAdapter(idleTask, 0, location)); |
} |
void Scheduler::postTask(const TraceLocation& location, const Task& task) |
{ |
- scheduleTask(location, task); |
+ m_mainThread->postTask(new MainThreadPendingTaskRunner(task, location)); |
} |
void Scheduler::postInputTask(const TraceLocation& location, const Task& task) |
{ |
- scheduleTask(location, task); |
+ Locker<Mutex> lock(m_pendingTasksMutex); |
+ m_pendingInputTasks.append(TracedTask(task, location)); |
+ atomicIncrement(&m_highPriorityTaskCount); |
+ m_mainThread->postTask(new MainThreadPendingHighPriorityTaskRunner()); |
} |
void Scheduler::postCompositorTask(const TraceLocation& location, const Task& task) |
{ |
- scheduleTask(location, task); |
+ Locker<Mutex> lock(m_pendingTasksMutex); |
+ m_pendingCompositorTasks.append(TracedTask(task, location)); |
+ atomicIncrement(&m_highPriorityTaskCount); |
+ m_mainThread->postTask(new MainThreadPendingHighPriorityTaskRunner()); |
} |
-void Scheduler::postIdleTask(const IdleTask& idleTask) |
+void Scheduler::postIdleTask(const TraceLocation& location, const IdleTask& idleTask) |
{ |
- scheduleIdleTask(idleTask); |
+ scheduleIdleTask(location, idleTask); |
} |
void Scheduler::tickSharedTimer() |
{ |
TRACE_EVENT0("blink", "Scheduler::tickSharedTimer"); |
+ |
+ // Run any high priority tasks that are queued up, otherwise the blink timers will yield immediately. |
+ runHighPriorityTasks(); |
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. |
+ runHighPriorityTasks(); |
+} |
+ |
+void Scheduler::runHighPriorityTasks() |
+{ |
+ ASSERT(isMainThread()); |
+ TRACE_EVENT0("blink", "Scheduler::runHighPriorityTasks"); |
+ |
+ // 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>& inputTasks = m_pendingInputTasks.swapBuffers(); |
+ Deque<TracedTask>& compositorTasks = m_pendingCompositorTasks.swapBuffers(); |
+ m_pendingTasksMutex.unlock(); |
+ |
+ int highPriorityTasksExecuted = 0; |
+ while (!inputTasks.isEmpty()) { |
+ inputTasks.takeFirst().run(); |
+ highPriorityTasksExecuted++; |
+ } |
+ |
+ while (!compositorTasks.isEmpty()) { |
+ compositorTasks.takeFirst().run(); |
+ highPriorityTasksExecuted++; |
+ } |
+ |
+ int highPriorityTaskCount = atomicSubtract(&m_highPriorityTaskCount, highPriorityTasksExecuted); |
+ ASSERT_UNUSED(highPriorityTaskCount, highPriorityTaskCount >= 0); |
} |
void Scheduler::sharedTimerAdapter() |
@@ -145,9 +218,27 @@ void Scheduler::stopSharedTimer() |
blink::Platform::current()->stopSharedTimer(); |
} |
-bool Scheduler::shouldYieldForHighPriorityWork() |
+bool Scheduler::shouldYieldForHighPriorityWork() const |
+{ |
+ return hasPendingHighPriorityWork(); |
+} |
+ |
+bool Scheduler::hasPendingHighPriorityWork() const |
+{ |
+ // This method is expected to be run on the main thread, but the high priority tasks will be posted by |
+ // other threads. We could use locks here, but this function is (sometimes) called a lot by |
+ // ThreadTimers::sharedTimerFiredInternal so we decided to use atomics + barrier loads here which |
+ // should be cheaper. |
+ // NOTE it's possible the barrier read is overkill here, since delayed yielding isn't a big deal. |
+ return acquireLoad(&m_highPriorityTaskCount) != 0; |
+} |
+ |
+void Scheduler::TracedTask::run() |
{ |
- return false; |
+ TRACE_EVENT2("blink", "TracedTask::run", |
+ "src_file", m_location.fileName(), |
+ "src_func", m_location.functionName()); |
+ m_task(); |
} |
} // namespace blink |