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