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 15 namespace blink { 16 17 namespace { 18 19 // The time we should stay in CompositorPriority mode for, after a touch event. 20 double kLowSchedulerPolicyAfterTouchTimeSeconds = 0.1; 21 22 // Can be created from any thread. 23 // Note if the scheduler gets shutdown, this may be run after. 24 class MainThreadIdleTaskAdapter : public WebThread::Task { 25 public: 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 42 private: 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. 52 class Scheduler::MainThreadPendingHighPriorityTaskRunner : public WebThread::Task { 53 public: 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. 76 class Scheduler::MainThreadPendingTaskRunner : public WebThread::Task { 77 public: 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 99 Scheduler* Scheduler::s_sharedScheduler = nullptr; 100 101 void Scheduler::initializeOnMainThread() 102 { 103 s_sharedScheduler = new Scheduler(); 104 } 105 106 void Scheduler::shutdown() 107 { 108 delete s_sharedScheduler; 109 s_sharedScheduler = nullptr; 110 } 111 112 Scheduler* Scheduler::shared() 113 { 114 return s_sharedScheduler; 115 } 116 117 Scheduler::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 127 Scheduler::~Scheduler() 128 { 129 while (hasPendingHighPriorityWork()) { 130 swapQueuesAndRunPendingTasks(); 131 } 132 } 133 134 void Scheduler::willBeginFrame(const WebBeginFrameArgs& args) 135 { 136 // TODO: Use frame deadline and interval to schedule idle tasks. 137 } 138 139 void Scheduler::didCommitFrameToCompositor() 140 { 141 // TODO: Trigger the frame deadline immediately. 142 } 143 144 void 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 150 void 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 160 void Scheduler::postTask(const TraceLocation& location, const Task& task) 161 { 162 m_mainThread->postTask(new MainThreadPendingTaskRunner(task, location, "Scheduler::MainThreadTask")); 163 } 164 165 void Scheduler::postInputTask(const TraceLocation& location, const Task& task) 166 { 167 postHighPriorityTaskInternal(location, task, "Scheduler::InputTask"); 168 } 169 170 void Scheduler::didReceiveInputEvent() 171 { 172 enterSchedulerPolicy(CompositorPriority); 173 } 174 175 void Scheduler::postCompositorTask(const TraceLocation& location, const Task& task) 176 { 177 postHighPriorityTaskInternal(location, task, "Scheduler::CompositorTask"); 178 } 179 180 void 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 187 void 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 196 void Scheduler::postIdleTask(const TraceLocation& location, const IdleTask& idleTask) 197 { 198 scheduleIdleTask(location, idleTask); 199 } 200 201 void 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 215 bool Scheduler::runPendingHighPriorityTasksIfInCompositorPriority() 216 { 217 ASSERT(isMainThread()); 218 if (schedulerPolicy() != CompositorPriority) 219 return false; 220 221 return swapQueuesAndRunPendingTasks(); 222 } 223 224 bool 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 237 void 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 251 void 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 261 bool 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 276 void Scheduler::sharedTimerAdapter() 277 { 278 shared()->tickSharedTimer(); 279 } 280 281 void Scheduler::setSharedTimerFiredFunction(void (*function)()) 282 { 283 m_sharedTimerFunction = function; 284 blink::Platform::current()->setSharedTimerFiredFunction(function ? &Scheduler::sharedTimerAdapter : nullptr); 285 } 286 287 void Scheduler::setSharedTimerFireInterval(double interval) 288 { 289 blink::Platform::current()->setSharedTimerFireInterval(interval); 290 } 291 292 void Scheduler::stopSharedTimer() 293 { 294 blink::Platform::current()->stopSharedTimer(); 295 } 296 297 bool 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 306 bool 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 316 Scheduler::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 325 void Scheduler::enterSchedulerPolicy(SchedulerPolicy schedulerPolicy) 326 { 327 Locker<Mutex> lock(m_pendingTasksMutex); 328 enterSchedulerPolicyLocked(schedulerPolicy); 329 } 330 331 void 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