| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "config.h" | 5 #include "config.h" |
| 6 #include "platform/scheduler/Scheduler.h" | 6 #include "platform/scheduler/Scheduler.h" |
| 7 | 7 |
| 8 #include "platform/PlatformThreadData.h" | 8 #include "platform/PlatformThreadData.h" |
| 9 #include "platform/Task.h" | 9 #include "platform/Task.h" |
| 10 #include "platform/ThreadTimers.h" | 10 #include "platform/ThreadTimers.h" |
| 11 #include "platform/TraceEvent.h" | 11 #include "platform/TraceEvent.h" |
| 12 #include "public/platform/Platform.h" | 12 #include "public/platform/Platform.h" |
| 13 #include "wtf/MainThread.h" | 13 #include "wtf/MainThread.h" |
| 14 #include "wtf/ThreadingPrimitives.h" | 14 #include "wtf/ThreadingPrimitives.h" |
| 15 | 15 |
| 16 | 16 |
| 17 namespace blink { | 17 namespace blink { |
| 18 | 18 |
| 19 namespace { | 19 namespace { |
| 20 | 20 |
| 21 // The time we should stay in CompositorPriority mode for, after a touch event. |
| 22 double kLowSchedulerPolicyAfterTouchTimeSeconds = 0.1; |
| 23 |
| 21 // Can be created from any thread. | 24 // Can be created from any thread. |
| 22 // Note if the scheduler gets shutdown, this may be run after. | 25 // Note if the scheduler gets shutdown, this may be run after. |
| 23 class MainThreadIdleTaskAdapter : public WebThread::Task { | 26 class MainThreadIdleTaskAdapter : public WebThread::Task { |
| 24 public: | 27 public: |
| 25 MainThreadIdleTaskAdapter(const Scheduler::IdleTask& idleTask, double allott
edTimeMs, const TraceLocation& location) | 28 MainThreadIdleTaskAdapter(const Scheduler::IdleTask& idleTask, double allott
edTimeMs, const TraceLocation& location) |
| 26 : m_idleTask(idleTask) | 29 : m_idleTask(idleTask) |
| 27 , m_allottedTimeMs(allottedTimeMs) | 30 , m_allottedTimeMs(allottedTimeMs) |
| 28 , m_location(location) | 31 , m_location(location) |
| 29 { | 32 { |
| 30 } | 33 } |
| (...skipping 25 matching lines...) Expand all Loading... |
| 56 } | 59 } |
| 57 | 60 |
| 58 // WebThread::Task implementation. | 61 // WebThread::Task implementation. |
| 59 virtual void run() OVERRIDE | 62 virtual void run() OVERRIDE |
| 60 { | 63 { |
| 61 Scheduler* scheduler = Scheduler::shared(); | 64 Scheduler* scheduler = Scheduler::shared(); |
| 62 // FIXME: This check should't be necessary, tasks should not outlive bli
nk. | 65 // FIXME: This check should't be necessary, tasks should not outlive bli
nk. |
| 63 ASSERT(scheduler); | 66 ASSERT(scheduler); |
| 64 if (!scheduler) | 67 if (!scheduler) |
| 65 return; | 68 return; |
| 69 // NOTE we must unconditionally execute high priority tasks here, since
if we're not in CompositorPriority |
| 70 // mode, then this is the only place where high priority tasks will be e
xecuted. |
| 66 scheduler->swapQueuesRunPendingTasksAndAllowHighPriorityTaskRunnerPostin
g(); | 71 scheduler->swapQueuesRunPendingTasksAndAllowHighPriorityTaskRunnerPostin
g(); |
| 67 } | 72 } |
| 68 }; | 73 }; |
| 69 | 74 |
| 70 | 75 |
| 71 // Can be created from any thread. | 76 // Can be created from any thread. |
| 72 // Note if the scheduler gets shutdown, this may be run after. | 77 // Note if the scheduler gets shutdown, this may be run after. |
| 73 class Scheduler::MainThreadPendingTaskRunner : public WebThread::Task { | 78 class Scheduler::MainThreadPendingTaskRunner : public WebThread::Task { |
| 74 public: | 79 public: |
| 75 MainThreadPendingTaskRunner( | 80 MainThreadPendingTaskRunner( |
| 76 const Scheduler::Task& task, const TraceLocation& location) | 81 const Scheduler::Task& task, const TraceLocation& location) |
| 77 : m_task(task, location) | 82 : m_task(task, location) |
| 78 { | 83 { |
| 79 ASSERT(Scheduler::shared()); | 84 ASSERT(Scheduler::shared()); |
| 80 } | 85 } |
| 81 | 86 |
| 82 // WebThread::Task implementation. | 87 // WebThread::Task implementation. |
| 83 virtual void run() OVERRIDE | 88 virtual void run() OVERRIDE |
| 84 { | 89 { |
| 85 Scheduler* scheduler = Scheduler::shared(); | 90 Scheduler* scheduler = Scheduler::shared(); |
| 86 // FIXME: This check should't be necessary, tasks should not outlive bli
nk. | 91 // FIXME: This check should't be necessary, tasks should not outlive bli
nk. |
| 87 ASSERT(scheduler); | 92 ASSERT(scheduler); |
| 88 if (scheduler) | 93 if (scheduler) |
| 89 Scheduler::shared()->swapQueuesAndRunPendingTasks(); | 94 Scheduler::shared()->runPendingHighPriorityTasksIfInCompositorPriori
ty(); |
| 90 m_task.run(); | 95 m_task.run(); |
| 91 } | 96 } |
| 92 | 97 |
| 93 Scheduler::TracedTask m_task; | 98 Scheduler::TracedTask m_task; |
| 94 }; | 99 }; |
| 95 | 100 |
| 96 Scheduler* Scheduler::s_sharedScheduler = nullptr; | 101 Scheduler* Scheduler::s_sharedScheduler = nullptr; |
| 97 | 102 |
| 98 void Scheduler::initializeOnMainThread() | 103 void Scheduler::initializeOnMainThread() |
| 99 { | 104 { |
| 100 s_sharedScheduler = new Scheduler(); | 105 s_sharedScheduler = new Scheduler(); |
| 101 } | 106 } |
| 102 | 107 |
| 103 void Scheduler::shutdown() | 108 void Scheduler::shutdown() |
| 104 { | 109 { |
| 105 delete s_sharedScheduler; | 110 delete s_sharedScheduler; |
| 106 s_sharedScheduler = nullptr; | 111 s_sharedScheduler = nullptr; |
| 107 } | 112 } |
| 108 | 113 |
| 109 Scheduler* Scheduler::shared() | 114 Scheduler* Scheduler::shared() |
| 110 { | 115 { |
| 111 return s_sharedScheduler; | 116 return s_sharedScheduler; |
| 112 } | 117 } |
| 113 | 118 |
| 114 Scheduler::Scheduler() | 119 Scheduler::Scheduler() |
| 115 : m_sharedTimerFunction(nullptr) | 120 : m_sharedTimerFunction(nullptr) |
| 116 , m_mainThread(blink::Platform::current()->currentThread()) | 121 , m_mainThread(blink::Platform::current()->currentThread()) |
| 122 , m_compositorPriorityPolicyEndTimeSeconds(0) |
| 117 , m_highPriorityTaskCount(0) | 123 , m_highPriorityTaskCount(0) |
| 118 , m_highPriorityTaskRunnerPosted(false) | 124 , m_highPriorityTaskRunnerPosted(false) |
| 125 , m_schedulerPolicy(Normal) |
| 119 { | 126 { |
| 120 } | 127 } |
| 121 | 128 |
| 122 Scheduler::~Scheduler() | 129 Scheduler::~Scheduler() |
| 123 { | 130 { |
| 124 while (hasPendingHighPriorityWork()) { | 131 while (hasPendingHighPriorityWork()) { |
| 125 swapQueuesAndRunPendingTasks(); | 132 swapQueuesAndRunPendingTasks(); |
| 126 } | 133 } |
| 127 } | 134 } |
| 128 | 135 |
| (...skipping 17 matching lines...) Expand all Loading... |
| 146 { | 153 { |
| 147 m_mainThread->postTask(new MainThreadPendingTaskRunner(task, location)); | 154 m_mainThread->postTask(new MainThreadPendingTaskRunner(task, location)); |
| 148 } | 155 } |
| 149 | 156 |
| 150 void Scheduler::postInputTask(const TraceLocation& location, const Task& task) | 157 void Scheduler::postInputTask(const TraceLocation& location, const Task& task) |
| 151 { | 158 { |
| 152 Locker<Mutex> lock(m_pendingTasksMutex); | 159 Locker<Mutex> lock(m_pendingTasksMutex); |
| 153 m_pendingHighPriorityTasks.append(TracedTask(task, location)); | 160 m_pendingHighPriorityTasks.append(TracedTask(task, location)); |
| 154 atomicIncrement(&m_highPriorityTaskCount); | 161 atomicIncrement(&m_highPriorityTaskCount); |
| 155 maybePostMainThreadPendingHighPriorityTaskRunner(); | 162 maybePostMainThreadPendingHighPriorityTaskRunner(); |
| 163 TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("blink.scheduler"), "PendingHighPri
orityTasks", m_highPriorityTaskCount); |
| 156 } | 164 } |
| 157 | 165 |
| 158 void Scheduler::postCompositorTask(const TraceLocation& location, const Task& ta
sk) | 166 void Scheduler::postCompositorTask(const TraceLocation& location, const Task& ta
sk) |
| 159 { | 167 { |
| 160 Locker<Mutex> lock(m_pendingTasksMutex); | 168 Locker<Mutex> lock(m_pendingTasksMutex); |
| 161 m_pendingHighPriorityTasks.append(TracedTask(task, location)); | 169 m_pendingHighPriorityTasks.append(TracedTask(task, location)); |
| 162 atomicIncrement(&m_highPriorityTaskCount); | 170 atomicIncrement(&m_highPriorityTaskCount); |
| 163 maybePostMainThreadPendingHighPriorityTaskRunner(); | 171 maybePostMainThreadPendingHighPriorityTaskRunner(); |
| 172 TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("blink.scheduler"), "PendingHighPri
orityTasks", m_highPriorityTaskCount); |
| 164 } | 173 } |
| 165 | 174 |
| 166 void Scheduler::maybePostMainThreadPendingHighPriorityTaskRunner() | 175 void Scheduler::maybePostMainThreadPendingHighPriorityTaskRunner() |
| 167 { | 176 { |
| 168 ASSERT(m_pendingTasksMutex.locked()); | 177 ASSERT(m_pendingTasksMutex.locked()); |
| 169 if (m_highPriorityTaskRunnerPosted) | 178 if (m_highPriorityTaskRunnerPosted) |
| 170 return; | 179 return; |
| 171 m_mainThread->postTask(new MainThreadPendingHighPriorityTaskRunner()); | 180 m_mainThread->postTask(new MainThreadPendingHighPriorityTaskRunner()); |
| 172 m_highPriorityTaskRunnerPosted = true; | 181 m_highPriorityTaskRunnerPosted = true; |
| 173 } | 182 } |
| 174 | 183 |
| 175 void Scheduler::postIdleTask(const TraceLocation& location, const IdleTask& idle
Task) | 184 void Scheduler::postIdleTask(const TraceLocation& location, const IdleTask& idle
Task) |
| 176 { | 185 { |
| 177 scheduleIdleTask(location, idleTask); | 186 scheduleIdleTask(location, idleTask); |
| 178 } | 187 } |
| 179 | 188 |
| 180 void Scheduler::tickSharedTimer() | 189 void Scheduler::tickSharedTimer() |
| 181 { | 190 { |
| 182 TRACE_EVENT0("blink", "Scheduler::tickSharedTimer"); | 191 TRACE_EVENT0("blink", "Scheduler::tickSharedTimer"); |
| 183 | 192 |
| 184 // Run any high priority tasks that are queued up, otherwise the blink timer
s will yield immediately. | 193 // Run any high priority tasks that are queued up, otherwise the blink timer
s will yield immediately. |
| 185 bool workDone = swapQueuesAndRunPendingTasks(); | 194 bool workDone = runPendingHighPriorityTasksIfInCompositorPriority(); |
| 186 m_sharedTimerFunction(); | 195 m_sharedTimerFunction(); |
| 187 | 196 |
| 188 // The blink timers may have just yielded, so run any high priority tasks th
at where queued up | 197 // The blink timers may have just yielded, so run any high priority tasks th
at where queued up |
| 189 // while the blink timers were executing. | 198 // while the blink timers were executing. |
| 190 if (!workDone) | 199 if (!workDone) |
| 191 swapQueuesAndRunPendingTasks(); | 200 runPendingHighPriorityTasksIfInCompositorPriority(); |
| 201 } |
| 202 |
| 203 bool Scheduler::runPendingHighPriorityTasksIfInCompositorPriority() |
| 204 { |
| 205 ASSERT(isMainThread()); |
| 206 if (schedulerPolicy() != CompositorPriority) |
| 207 return false; |
| 208 |
| 209 return swapQueuesAndRunPendingTasks(); |
| 192 } | 210 } |
| 193 | 211 |
| 194 bool Scheduler::swapQueuesAndRunPendingTasks() | 212 bool Scheduler::swapQueuesAndRunPendingTasks() |
| 195 { | 213 { |
| 196 ASSERT(isMainThread()); | 214 ASSERT(isMainThread()); |
| 197 | 215 |
| 198 // These locks guard against another thread posting input or compositor task
s while we swap the buffers. | 216 // These locks guard against another thread posting input or compositor task
s while we swap the buffers. |
| 199 // One the buffers have been swapped we can safely access the returned deque
without having to lock. | 217 // One the buffers have been swapped we can safely access the returned deque
without having to lock. |
| 200 m_pendingTasksMutex.lock(); | 218 m_pendingTasksMutex.lock(); |
| 201 Deque<TracedTask>& highPriorityTasks = m_pendingHighPriorityTasks.swapBuffer
s(); | 219 Deque<TracedTask>& highPriorityTasks = m_pendingHighPriorityTasks.swapBuffer
s(); |
| 220 maybeEnterNormalSchedulerPolicy(); |
| 202 m_pendingTasksMutex.unlock(); | 221 m_pendingTasksMutex.unlock(); |
| 203 return executeHighPriorityTasks(highPriorityTasks); | 222 return executeHighPriorityTasks(highPriorityTasks); |
| 204 } | 223 } |
| 205 | 224 |
| 206 void Scheduler::swapQueuesRunPendingTasksAndAllowHighPriorityTaskRunnerPosting() | 225 void Scheduler::swapQueuesRunPendingTasksAndAllowHighPriorityTaskRunnerPosting() |
| 207 { | 226 { |
| 208 ASSERT(isMainThread()); | 227 ASSERT(isMainThread()); |
| 209 | 228 |
| 210 // These locks guard against another thread posting input or compositor task
s while we swap the buffers. | 229 // These locks guard against another thread posting input or compositor task
s while we swap the buffers. |
| 211 // One the buffers have been swapped we can safely access the returned deque
without having to lock. | 230 // One the buffers have been swapped we can safely access the returned deque
without having to lock. |
| 212 m_pendingTasksMutex.lock(); | 231 m_pendingTasksMutex.lock(); |
| 213 Deque<TracedTask>& highPriorityTasks = m_pendingHighPriorityTasks.swapBuffer
s(); | 232 Deque<TracedTask>& highPriorityTasks = m_pendingHighPriorityTasks.swapBuffer
s(); |
| 214 m_highPriorityTaskRunnerPosted = false; | 233 m_highPriorityTaskRunnerPosted = false; |
| 234 maybeEnterNormalSchedulerPolicy(); |
| 215 m_pendingTasksMutex.unlock(); | 235 m_pendingTasksMutex.unlock(); |
| 216 executeHighPriorityTasks(highPriorityTasks); | 236 executeHighPriorityTasks(highPriorityTasks); |
| 217 } | 237 } |
| 218 | 238 |
| 239 void Scheduler::maybeEnterNormalSchedulerPolicy() |
| 240 { |
| 241 ASSERT(isMainThread()); |
| 242 ASSERT(m_pendingTasksMutex.locked()); |
| 243 |
| 244 // Go back to the normal scheduler policy if enough time has elapsed. |
| 245 if (schedulerPolicy() == CompositorPriority && Platform::current()->monotoni
callyIncreasingTime() > m_compositorPriorityPolicyEndTimeSeconds) |
| 246 enterSchedulerPolicyLocked(Normal); |
| 247 } |
| 248 |
| 219 bool Scheduler::executeHighPriorityTasks(Deque<TracedTask>& highPriorityTasks) | 249 bool Scheduler::executeHighPriorityTasks(Deque<TracedTask>& highPriorityTasks) |
| 220 { | 250 { |
| 221 TRACE_EVENT0("blink", "Scheduler::executeHighPriorityTasks"); | 251 TRACE_EVENT0("blink", "Scheduler::executeHighPriorityTasks"); |
| 222 int highPriorityTasksExecuted = 0; | 252 int highPriorityTasksExecuted = 0; |
| 223 while (!highPriorityTasks.isEmpty()) { | 253 while (!highPriorityTasks.isEmpty()) { |
| 224 highPriorityTasks.takeFirst().run(); | 254 highPriorityTasks.takeFirst().run(); |
| 225 highPriorityTasksExecuted++; | 255 highPriorityTasksExecuted++; |
| 226 } | 256 } |
| 227 | 257 |
| 228 int highPriorityTaskCount = atomicSubtract(&m_highPriorityTaskCount, highPri
orityTasksExecuted); | 258 int highPriorityTaskCount = atomicSubtract(&m_highPriorityTaskCount, highPri
orityTasksExecuted); |
| 229 ASSERT_UNUSED(highPriorityTaskCount, highPriorityTaskCount >= 0); | 259 ASSERT_UNUSED(highPriorityTaskCount, highPriorityTaskCount >= 0); |
| 260 TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("blink.scheduler"), "PendingHighPri
orityTasks", m_highPriorityTaskCount); |
| 230 return highPriorityTasksExecuted > 0; | 261 return highPriorityTasksExecuted > 0; |
| 231 } | 262 } |
| 232 | 263 |
| 233 void Scheduler::sharedTimerAdapter() | 264 void Scheduler::sharedTimerAdapter() |
| 234 { | 265 { |
| 235 shared()->tickSharedTimer(); | 266 shared()->tickSharedTimer(); |
| 236 } | 267 } |
| 237 | 268 |
| 238 void Scheduler::setSharedTimerFiredFunction(void (*function)()) | 269 void Scheduler::setSharedTimerFiredFunction(void (*function)()) |
| 239 { | 270 { |
| 240 m_sharedTimerFunction = function; | 271 m_sharedTimerFunction = function; |
| 241 blink::Platform::current()->setSharedTimerFiredFunction(function ? &Schedule
r::sharedTimerAdapter : nullptr); | 272 blink::Platform::current()->setSharedTimerFiredFunction(function ? &Schedule
r::sharedTimerAdapter : nullptr); |
| 242 } | 273 } |
| 243 | 274 |
| 244 void Scheduler::setSharedTimerFireInterval(double interval) | 275 void Scheduler::setSharedTimerFireInterval(double interval) |
| 245 { | 276 { |
| 246 blink::Platform::current()->setSharedTimerFireInterval(interval); | 277 blink::Platform::current()->setSharedTimerFireInterval(interval); |
| 247 } | 278 } |
| 248 | 279 |
| 249 void Scheduler::stopSharedTimer() | 280 void Scheduler::stopSharedTimer() |
| 250 { | 281 { |
| 251 blink::Platform::current()->stopSharedTimer(); | 282 blink::Platform::current()->stopSharedTimer(); |
| 252 } | 283 } |
| 253 | 284 |
| 254 bool Scheduler::shouldYieldForHighPriorityWork() const | 285 bool Scheduler::shouldYieldForHighPriorityWork() const |
| 255 { | 286 { |
| 287 // It's only worthwhile yielding in CompositorPriority mode. |
| 288 if (schedulerPolicy() != CompositorPriority) |
| 289 return false; |
| 290 |
| 256 return hasPendingHighPriorityWork(); | 291 return hasPendingHighPriorityWork(); |
| 257 } | 292 } |
| 258 | 293 |
| 259 bool Scheduler::hasPendingHighPriorityWork() const | 294 bool Scheduler::hasPendingHighPriorityWork() const |
| 260 { | 295 { |
| 261 // This method is expected to be run on the main thread, but the high priori
ty tasks will be posted by | 296 // This method is expected to be run on the main thread, but the high priori
ty tasks will be posted by |
| 262 // other threads. We could use locks here, but this function is (sometimes)
called a lot by | 297 // other threads. We could use locks here, but this function is (sometimes)
called a lot by |
| 263 // ThreadTimers::sharedTimerFiredInternal so we decided to use atomics + bar
rier loads here which | 298 // ThreadTimers::sharedTimerFiredInternal so we decided to use atomics + bar
rier loads here which |
| 264 // should be cheaper. | 299 // should be cheaper. |
| 265 // NOTE it's possible the barrier read is overkill here, since delayed yield
ing isn't a big deal. | 300 // NOTE it's possible the barrier read is overkill here, since delayed yield
ing isn't a big deal. |
| 266 return acquireLoad(&m_highPriorityTaskCount) != 0; | 301 return acquireLoad(&m_highPriorityTaskCount) != 0; |
| 267 } | 302 } |
| 268 | 303 |
| 304 Scheduler::SchedulerPolicy Scheduler::schedulerPolicy() const |
| 305 { |
| 306 ASSERT(isMainThread()); |
| 307 // It's important not to miss the transition from normal to low latency mode
, otherwise we're likely to |
| 308 // delay the processing of input tasks. Since that transition is triggered b
y a different thread, we |
| 309 // need either a lock or a memory barrier, and the memory barrier is probabl
y cheaper. |
| 310 return static_cast<SchedulerPolicy>(acquireLoad(&m_schedulerPolicy)); |
| 311 } |
| 312 |
| 313 void Scheduler::enterSchedulerPolicy(SchedulerPolicy schedulerPolicy) |
| 314 { |
| 315 Locker<Mutex> lock(m_pendingTasksMutex); |
| 316 enterSchedulerPolicyLocked(schedulerPolicy); |
| 317 } |
| 318 |
| 319 void Scheduler::enterSchedulerPolicyLocked(SchedulerPolicy schedulerPolicy) |
| 320 { |
| 321 ASSERT(m_pendingTasksMutex.locked()); |
| 322 if (schedulerPolicy == CompositorPriority) |
| 323 m_compositorPriorityPolicyEndTimeSeconds = Platform::current()->monotoni
callyIncreasingTime() + kLowSchedulerPolicyAfterTouchTimeSeconds; |
| 324 |
| 325 releaseStore(&m_schedulerPolicy, schedulerPolicy); |
| 326 TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("blink.scheduler"), "SchedulerPolic
y", schedulerPolicy); |
| 327 } |
| 328 |
| 269 void Scheduler::TracedTask::run() | 329 void Scheduler::TracedTask::run() |
| 270 { | 330 { |
| 271 TRACE_EVENT2("blink", "TracedTask::run", | 331 TRACE_EVENT2("blink", "TracedTask::run", |
| 272 "src_file", m_location.fileName(), | 332 "src_file", m_location.fileName(), |
| 273 "src_func", m_location.functionName()); | 333 "src_func", m_location.functionName()); |
| 274 m_task(); | 334 m_task(); |
| 275 } | 335 } |
| 276 | 336 |
| 277 } // namespace blink | 337 } // namespace blink |
| OLD | NEW |