• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
MainThreadIdleTaskAdapter(const Scheduler::IdleTask & idleTask,double allottedTimeMs,const TraceLocation & location)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.
run()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:
MainThreadPendingHighPriorityTaskRunner()54     MainThreadPendingHighPriorityTaskRunner()
55     {
56         ASSERT(Scheduler::shared());
57     }
58 
59     // WebThread::Task implementation.
run()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:
MainThreadPendingTaskRunner(const Scheduler::Task & task,const TraceLocation & location,const char * traceName)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.
run()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 
initializeOnMainThread()101 void Scheduler::initializeOnMainThread()
102 {
103     s_sharedScheduler = new Scheduler();
104 }
105 
shutdown()106 void Scheduler::shutdown()
107 {
108     delete s_sharedScheduler;
109     s_sharedScheduler = nullptr;
110 }
111 
shared()112 Scheduler* Scheduler::shared()
113 {
114     return s_sharedScheduler;
115 }
116 
Scheduler()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 
~Scheduler()127 Scheduler::~Scheduler()
128 {
129     while (hasPendingHighPriorityWork()) {
130         swapQueuesAndRunPendingTasks();
131     }
132 }
133 
willBeginFrame(const WebBeginFrameArgs & args)134 void Scheduler::willBeginFrame(const WebBeginFrameArgs& args)
135 {
136     // TODO: Use frame deadline and interval to schedule idle tasks.
137 }
138 
didCommitFrameToCompositor()139 void Scheduler::didCommitFrameToCompositor()
140 {
141     // TODO: Trigger the frame deadline immediately.
142 }
143 
scheduleIdleTask(const TraceLocation & location,const IdleTask & idleTask)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 
postHighPriorityTaskInternal(const TraceLocation & location,const Task & task,const char * traceName)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 
postTask(const TraceLocation & location,const Task & task)160 void Scheduler::postTask(const TraceLocation& location, const Task& task)
161 {
162     m_mainThread->postTask(new MainThreadPendingTaskRunner(task, location, "Scheduler::MainThreadTask"));
163 }
164 
postInputTask(const TraceLocation & location,const Task & task)165 void Scheduler::postInputTask(const TraceLocation& location, const Task& task)
166 {
167     postHighPriorityTaskInternal(location, task, "Scheduler::InputTask");
168 }
169 
didReceiveInputEvent()170 void Scheduler::didReceiveInputEvent()
171 {
172     enterSchedulerPolicy(CompositorPriority);
173 }
174 
postCompositorTask(const TraceLocation & location,const Task & task)175 void Scheduler::postCompositorTask(const TraceLocation& location, const Task& task)
176 {
177     postHighPriorityTaskInternal(location, task, "Scheduler::CompositorTask");
178 }
179 
postIpcTask(const TraceLocation & location,const Task & task)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 
maybePostMainThreadPendingHighPriorityTaskRunner()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 
postIdleTask(const TraceLocation & location,const IdleTask & idleTask)196 void Scheduler::postIdleTask(const TraceLocation& location, const IdleTask& idleTask)
197 {
198     scheduleIdleTask(location, idleTask);
199 }
200 
tickSharedTimer()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 
runPendingHighPriorityTasksIfInCompositorPriority()215 bool Scheduler::runPendingHighPriorityTasksIfInCompositorPriority()
216 {
217     ASSERT(isMainThread());
218     if (schedulerPolicy() != CompositorPriority)
219         return false;
220 
221     return swapQueuesAndRunPendingTasks();
222 }
223 
swapQueuesAndRunPendingTasks()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 
swapQueuesRunPendingTasksAndAllowHighPriorityTaskRunnerPosting()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 
maybeEnterNormalSchedulerPolicy()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 
executeHighPriorityTasks(Deque<TracedTask> & highPriorityTasks)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 
sharedTimerAdapter()276 void Scheduler::sharedTimerAdapter()
277 {
278     shared()->tickSharedTimer();
279 }
280 
setSharedTimerFiredFunction(void (* function)())281 void Scheduler::setSharedTimerFiredFunction(void (*function)())
282 {
283     m_sharedTimerFunction = function;
284     blink::Platform::current()->setSharedTimerFiredFunction(function ? &Scheduler::sharedTimerAdapter : nullptr);
285 }
286 
setSharedTimerFireInterval(double interval)287 void Scheduler::setSharedTimerFireInterval(double interval)
288 {
289     blink::Platform::current()->setSharedTimerFireInterval(interval);
290 }
291 
stopSharedTimer()292 void Scheduler::stopSharedTimer()
293 {
294     blink::Platform::current()->stopSharedTimer();
295 }
296 
shouldYieldForHighPriorityWork() const297 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 
hasPendingHighPriorityWork() const306 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 
schedulerPolicy() const316 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 
enterSchedulerPolicy(SchedulerPolicy schedulerPolicy)325 void Scheduler::enterSchedulerPolicy(SchedulerPolicy schedulerPolicy)
326 {
327     Locker<Mutex> lock(m_pendingTasksMutex);
328     enterSchedulerPolicyLocked(schedulerPolicy);
329 }
330 
enterSchedulerPolicyLocked(SchedulerPolicy schedulerPolicy)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