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 |