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/RuntimeEnabledFeatures.h" | 9 #include "platform/RuntimeEnabledFeatures.h" |
10 #include "platform/Task.h" | 10 #include "platform/Task.h" |
11 #include "platform/ThreadTimers.h" | 11 #include "platform/ThreadTimers.h" |
12 #include "platform/TraceEvent.h" | 12 #include "platform/TraceEvent.h" |
13 #include "public/platform/Platform.h" | 13 #include "public/platform/Platform.h" |
14 #include "wtf/MainThread.h" | 14 #include "wtf/MainThread.h" |
15 | 15 |
16 namespace blink { | 16 namespace blink { |
17 | 17 |
18 namespace { | 18 namespace { |
19 | 19 |
20 // The time we should stay in CompositorPriority mode for, after a touch event. | 20 // The time we should stay in CompositorPriority mode for, after a touch event. |
21 double kLowSchedulerPolicyAfterTouchTimeSeconds = 0.1; | 21 double kLowSchedulerPolicyAfterTouchTimeSeconds = 0.1; |
22 | 22 |
23 // Can be created from any thread. | |
24 // Note if the scheduler gets shutdown, this may be run after. | |
25 class MainThreadIdleTaskAdapter : public WebThread::Task { | |
26 public: | |
27 MainThreadIdleTaskAdapter(const Scheduler::IdleTask& idleTask, double allott
edTimeMs, const TraceLocation& location) | |
28 : m_idleTask(idleTask) | |
29 , m_allottedTimeMs(allottedTimeMs) | |
30 , m_location(location) | |
31 { | |
32 } | |
33 | |
34 // WebThread::Task implementation. | |
35 virtual void run() OVERRIDE | |
36 { | |
37 TRACE_EVENT2("blink", "MainThreadIdleTaskAdapter::run", | |
38 "src_file", m_location.fileName(), | |
39 "src_func", m_location.functionName()); | |
40 m_idleTask(m_allottedTimeMs); | |
41 } | |
42 | |
43 private: | |
44 Scheduler::IdleTask m_idleTask; | |
45 double m_allottedTimeMs; | |
46 TraceLocation m_location; | |
47 }; | |
48 | |
49 } // namespace | 23 } // namespace |
50 | 24 |
51 // Typically only created from compositor or render threads. | 25 // Typically only created from compositor or render threads. |
52 // Note if the scheduler gets shutdown, this may be run after. | 26 // Note if the scheduler gets shutdown, this may be run after. |
53 class Scheduler::MainThreadPendingHighPriorityTaskRunner : public WebThread::Tas
k { | 27 class Scheduler::MainThreadPendingHighPriorityTaskRunner : public WebThread::Tas
k { |
54 public: | 28 public: |
55 MainThreadPendingHighPriorityTaskRunner( | 29 MainThreadPendingHighPriorityTaskRunner( |
56 const Scheduler::Task& task, const TraceLocation& location, const char*
traceName) | 30 const Scheduler::Task& task, const TraceLocation& location, const char*
traceName) |
57 : m_task(task, location, traceName) | 31 : m_task(internal::TracedStandardTask::Create(task, location, traceName)
) |
58 { | 32 { |
59 ASSERT(Scheduler::shared()); | 33 ASSERT(Scheduler::shared()); |
60 } | 34 } |
| 35 |
| 36 // WebThread::Task implementation. |
| 37 virtual void run() OVERRIDE |
| 38 { |
| 39 m_task->run(); |
| 40 if (Scheduler* scheduler = Scheduler::shared()) { |
| 41 scheduler->updatePolicy(); |
| 42 scheduler->didRunHighPriorityTask(); |
| 43 } |
| 44 } |
| 45 |
| 46 private: |
| 47 OwnPtr<internal::TracedStandardTask> m_task; |
| 48 }; |
| 49 |
| 50 // Can be created from any thread. |
| 51 // Note if the scheduler gets shutdown, this may be run after. |
| 52 class Scheduler::MainThreadPendingTaskRunner : public WebThread::Task { |
| 53 public: |
| 54 MainThreadPendingTaskRunner( |
| 55 const Scheduler::Task& task, const TraceLocation& location, const char*
traceName) |
| 56 : m_task(internal::TracedStandardTask::Create(task, location, traceName)
) |
| 57 { |
| 58 ASSERT(Scheduler::shared()); |
| 59 } |
61 | 60 |
62 // WebThread::Task implementation. | 61 // WebThread::Task implementation. |
63 virtual void run() OVERRIDE | 62 virtual void run() OVERRIDE |
64 { | 63 { |
65 m_task.run(); | 64 m_task->run(); |
66 if (Scheduler* scheduler = Scheduler::shared()) { | 65 if (Scheduler* scheduler = Scheduler::shared()) { |
67 scheduler->updatePolicy(); | 66 scheduler->updatePolicy(); |
68 scheduler->didRunHighPriorityTask(); | |
69 } | 67 } |
70 } | 68 } |
71 | 69 |
72 private: | 70 private: |
73 TracedTask m_task; | 71 OwnPtr<internal::TracedStandardTask> m_task; |
74 }; | 72 }; |
75 | 73 |
| 74 |
76 // Can be created from any thread. | 75 // Can be created from any thread. |
77 // Note if the scheduler gets shutdown, this may be run after. | 76 // Note if the scheduler gets shutdown, this may be run after. |
78 class Scheduler::MainThreadPendingTaskRunner : public WebThread::Task { | 77 class Scheduler::MainThreadPendingIdleTaskRunner : public WebThread::Task { |
79 public: | 78 public: |
80 MainThreadPendingTaskRunner( | 79 MainThreadPendingIdleTaskRunner() |
81 const Scheduler::Task& task, const TraceLocation& location, const char*
traceName) | |
82 : m_task(task, location, traceName) | |
83 { | 80 { |
84 ASSERT(Scheduler::shared()); | 81 ASSERT(Scheduler::shared()); |
85 } | 82 } |
86 | 83 |
87 // WebThread::Task implementation. | 84 // WebThread::Task implementation. |
88 virtual void run() OVERRIDE | 85 virtual void run() OVERRIDE |
89 { | 86 { |
90 m_task.run(); | |
91 if (Scheduler* scheduler = Scheduler::shared()) { | 87 if (Scheduler* scheduler = Scheduler::shared()) { |
92 scheduler->updatePolicy(); | 88 scheduler->maybeRunPendingIdleTask(); |
| 89 // If possible, run the next idle task by reposting on the main thre
ad. |
| 90 scheduler->maybePostMainThreadPendingIdleTask(); |
93 } | 91 } |
94 } | 92 } |
95 | 93 |
96 TracedTask m_task; | |
97 }; | 94 }; |
98 | 95 |
| 96 |
99 Scheduler* Scheduler::s_sharedScheduler = nullptr; | 97 Scheduler* Scheduler::s_sharedScheduler = nullptr; |
100 | 98 |
101 void Scheduler::initializeOnMainThread() | 99 void Scheduler::initializeOnMainThread() |
102 { | 100 { |
103 s_sharedScheduler = new Scheduler(); | 101 s_sharedScheduler = new Scheduler(); |
104 } | 102 } |
105 | 103 |
106 void Scheduler::shutdown() | 104 void Scheduler::shutdown() |
107 { | 105 { |
108 delete s_sharedScheduler; | 106 delete s_sharedScheduler; |
109 s_sharedScheduler = nullptr; | 107 s_sharedScheduler = nullptr; |
110 } | 108 } |
111 | 109 |
112 Scheduler* Scheduler::shared() | 110 Scheduler* Scheduler::shared() |
113 { | 111 { |
114 return s_sharedScheduler; | 112 return s_sharedScheduler; |
115 } | 113 } |
116 | 114 |
117 Scheduler::Scheduler() | 115 Scheduler::Scheduler() |
118 : m_sharedTimerFunction(nullptr) | 116 : m_sharedTimerFunction(nullptr) |
119 , m_mainThread(blink::Platform::current()->currentThread()) | 117 , m_mainThread(blink::Platform::current()->currentThread()) |
| 118 , m_estimatedNextBeginFrameSeconds(0) |
120 , m_highPriorityTaskCount(0) | 119 , m_highPriorityTaskCount(0) |
121 , m_highPriorityTaskRunnerPosted(false) | 120 , m_highPriorityTaskRunnerPosted(false) |
122 , m_compositorPriorityPolicyEndTimeSeconds(0) | 121 , m_compositorPriorityPolicyEndTimeSeconds(0) |
123 , m_schedulerPolicy(Normal) | 122 , m_schedulerPolicy(Normal) |
124 { | 123 { |
125 } | 124 } |
126 | 125 |
127 Scheduler::~Scheduler() | 126 Scheduler::~Scheduler() |
128 { | 127 { |
129 } | 128 } |
130 | 129 |
131 void Scheduler::willBeginFrame(const WebBeginFrameArgs& args) | 130 void Scheduler::willBeginFrame(double estimatedNextBeginFrameSeconds) |
132 { | 131 { |
133 // TODO: Use frame deadline and interval to schedule idle tasks. | 132 ASSERT(isMainThread()); |
| 133 m_currentFrameCommitted = false; |
| 134 m_estimatedNextBeginFrameSeconds = estimatedNextBeginFrameSeconds; |
| 135 // TODO: Schedule a deferred task here to run idle work if didCommitFrameToC
ompositor never gets called. |
134 } | 136 } |
135 | 137 |
136 void Scheduler::didCommitFrameToCompositor() | 138 void Scheduler::didCommitFrameToCompositor() |
137 { | 139 { |
138 // TODO: Trigger the frame deadline immediately. | 140 ASSERT(isMainThread()); |
139 } | 141 m_currentFrameCommitted = true; |
140 | 142 maybePostMainThreadPendingIdleTask(); |
141 void Scheduler::scheduleIdleTask(const TraceLocation& location, const IdleTask&
idleTask) | |
142 { | |
143 // TODO: send a real allottedTime here. | |
144 m_mainThread->postTask(new MainThreadIdleTaskAdapter(idleTask, 0, location))
; | |
145 } | 143 } |
146 | 144 |
147 void Scheduler::postHighPriorityTaskInternal(const TraceLocation& location, cons
t Task& task, const char* traceName) | 145 void Scheduler::postHighPriorityTaskInternal(const TraceLocation& location, cons
t Task& task, const char* traceName) |
148 { | 146 { |
149 m_mainThread->postTask(new MainThreadPendingHighPriorityTaskRunner(task, loc
ation, traceName)); | 147 m_mainThread->postTask(new MainThreadPendingHighPriorityTaskRunner(task, loc
ation, traceName)); |
150 atomicIncrement(&m_highPriorityTaskCount); | 148 atomicIncrement(&m_highPriorityTaskCount); |
151 TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("blink.scheduler"), "PendingHighPri
orityTasks", m_highPriorityTaskCount); | 149 TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("blink.scheduler"), "PendingHighPri
orityTasks", m_highPriorityTaskCount); |
152 } | 150 } |
153 | 151 |
154 void Scheduler::didRunHighPriorityTask() | 152 void Scheduler::didRunHighPriorityTask() |
155 { | 153 { |
156 atomicDecrement(&m_highPriorityTaskCount); | 154 atomicDecrement(&m_highPriorityTaskCount); |
157 TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("blink.scheduler"), "PendingHighPri
orityTasks", m_highPriorityTaskCount); | 155 TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("blink.scheduler"), "PendingHighPri
orityTasks", m_highPriorityTaskCount); |
158 } | 156 } |
159 | 157 |
| 158 void Scheduler::postIdleTaskInternal(const TraceLocation& location, const IdleTa
sk& idleTask, const char* traceName) |
| 159 { |
| 160 Locker<Mutex> lock(m_pendingIdleTasksMutex); |
| 161 m_pendingIdleTasks.append(internal::TracedIdleTask::Create(idleTask, locatio
n, traceName)); |
| 162 } |
| 163 |
160 void Scheduler::postTask(const TraceLocation& location, const Task& task) | 164 void Scheduler::postTask(const TraceLocation& location, const Task& task) |
161 { | 165 { |
162 m_mainThread->postTask(new MainThreadPendingTaskRunner(task, location, "Sche
duler::MainThreadTask")); | 166 m_mainThread->postTask(new MainThreadPendingTaskRunner(task, location, "Sche
duler::MainThreadTask")); |
163 } | 167 } |
164 | 168 |
165 void Scheduler::postInputTask(const TraceLocation& location, const Task& task) | 169 void Scheduler::postInputTask(const TraceLocation& location, const Task& task) |
166 { | 170 { |
167 postHighPriorityTaskInternal(location, task, "Scheduler::InputTask"); | 171 postHighPriorityTaskInternal(location, task, "Scheduler::InputTask"); |
168 } | 172 } |
169 | 173 |
(...skipping 11 matching lines...) Expand all Loading... |
181 | 185 |
182 void Scheduler::postIpcTask(const TraceLocation& location, const Task& task) | 186 void Scheduler::postIpcTask(const TraceLocation& location, const Task& task) |
183 { | 187 { |
184 // FIXME: we want IPCs to be high priority, but we can't currently do that b
ecause some of them can take a very long | 188 // FIXME: we want IPCs to be high priority, but we can't currently do that b
ecause some of them can take a very long |
185 // time to process. These need refactoring but we need to add some infrastru
cture to identify them. | 189 // time to process. These need refactoring but we need to add some infrastru
cture to identify them. |
186 m_mainThread->postTask(new MainThreadPendingTaskRunner(task, location, "Sche
duler::IpcTask")); | 190 m_mainThread->postTask(new MainThreadPendingTaskRunner(task, location, "Sche
duler::IpcTask")); |
187 } | 191 } |
188 | 192 |
189 void Scheduler::postIdleTask(const TraceLocation& location, const IdleTask& idle
Task) | 193 void Scheduler::postIdleTask(const TraceLocation& location, const IdleTask& idle
Task) |
190 { | 194 { |
191 scheduleIdleTask(location, idleTask); | 195 postIdleTaskInternal(location, idleTask, "Scheduler::IdleTask"); |
| 196 } |
| 197 |
| 198 bool Scheduler::maybePostMainThreadPendingIdleTask() |
| 199 { |
| 200 ASSERT(isMainThread()); |
| 201 TRACE_EVENT0("blink", "Scheduler::maybePostMainThreadPendingIdleTask"); |
| 202 if (canRunIdleTask()) { |
| 203 Locker<Mutex> lock(m_pendingIdleTasksMutex); |
| 204 if (!m_pendingIdleTasks.isEmpty()) { |
| 205 m_mainThread->postTask(new MainThreadPendingIdleTaskRunner()); |
| 206 return true; |
| 207 } |
| 208 } |
| 209 return false; |
192 } | 210 } |
193 | 211 |
194 void Scheduler::tickSharedTimer() | 212 void Scheduler::tickSharedTimer() |
195 { | 213 { |
196 TRACE_EVENT0("blink", "Scheduler::tickSharedTimer"); | 214 TRACE_EVENT0("blink", "Scheduler::tickSharedTimer"); |
197 m_sharedTimerFunction(); | 215 m_sharedTimerFunction(); |
198 } | 216 } |
199 | 217 |
200 void Scheduler::updatePolicy() | 218 void Scheduler::updatePolicy() |
201 { | 219 { |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
235 return false; | 253 return false; |
236 | 254 |
237 // This method is expected to be run on the main thread, but the high priori
ty tasks will be posted by | 255 // This method is expected to be run on the main thread, but the high priori
ty tasks will be posted by |
238 // other threads. We could use locks here, but this function is (sometimes)
called a lot by | 256 // other threads. We could use locks here, but this function is (sometimes)
called a lot by |
239 // ThreadTimers::sharedTimerFiredInternal so we decided to use atomics + bar
rier loads here which | 257 // ThreadTimers::sharedTimerFiredInternal so we decided to use atomics + bar
rier loads here which |
240 // should be cheaper. | 258 // should be cheaper. |
241 // NOTE it's possible the barrier read is overkill here, since delayed yield
ing isn't a big deal. | 259 // NOTE it's possible the barrier read is overkill here, since delayed yield
ing isn't a big deal. |
242 return acquireLoad(&m_highPriorityTaskCount) != 0; | 260 return acquireLoad(&m_highPriorityTaskCount) != 0; |
243 } | 261 } |
244 | 262 |
| 263 bool Scheduler::maybeRunPendingIdleTask() |
| 264 { |
| 265 ASSERT(isMainThread()); |
| 266 if (!canRunIdleTask()) |
| 267 return false; |
| 268 |
| 269 takeFirstPendingIdleTask()->run(); |
| 270 return true; |
| 271 } |
| 272 |
| 273 PassOwnPtr<internal::TracedIdleTask> Scheduler::takeFirstPendingIdleTask() |
| 274 { |
| 275 Locker<Mutex> lock(m_pendingIdleTasksMutex); |
| 276 ASSERT(!m_pendingIdleTasks.isEmpty()); |
| 277 return m_pendingIdleTasks.takeFirst(); |
| 278 } |
| 279 |
| 280 double Scheduler::currentFrameDeadlineForIdleTasks() const |
| 281 { |
| 282 ASSERT(isMainThread()); |
| 283 // TODO: Make idle time more fine-grain chunks when in Compositor priority. |
| 284 return m_estimatedNextBeginFrameSeconds; |
| 285 } |
| 286 |
| 287 bool Scheduler::canRunIdleTask() const |
| 288 { |
| 289 ASSERT(isMainThread()); |
| 290 return m_currentFrameCommitted |
| 291 && !shouldYieldForHighPriorityWork() |
| 292 && (m_estimatedNextBeginFrameSeconds > Platform::current()->monotonicall
yIncreasingTime()); |
| 293 } |
| 294 |
245 Scheduler::SchedulerPolicy Scheduler::schedulerPolicy() const | 295 Scheduler::SchedulerPolicy Scheduler::schedulerPolicy() const |
246 { | 296 { |
247 ASSERT(isMainThread()); | 297 ASSERT(isMainThread()); |
248 // It's important not to miss the transition from normal to low latency mode
, otherwise we're likely to | 298 // It's important not to miss the transition from normal to low latency mode
, otherwise we're likely to |
249 // delay the processing of input tasks. Since that transition is triggered b
y a different thread, we | 299 // delay the processing of input tasks. Since that transition is triggered b
y a different thread, we |
250 // need either a lock or a memory barrier, and the memory barrier is probabl
y cheaper. | 300 // need either a lock or a memory barrier, and the memory barrier is probabl
y cheaper. |
251 return static_cast<SchedulerPolicy>(acquireLoad(&m_schedulerPolicy)); | 301 return static_cast<SchedulerPolicy>(acquireLoad(&m_schedulerPolicy)); |
252 } | 302 } |
253 | 303 |
254 void Scheduler::enterSchedulerPolicy(SchedulerPolicy schedulerPolicy) | 304 void Scheduler::enterSchedulerPolicy(SchedulerPolicy schedulerPolicy) |
255 { | 305 { |
256 Locker<Mutex> lock(m_policyStateMutex); | 306 Locker<Mutex> lock(m_policyStateMutex); |
257 enterSchedulerPolicyLocked(schedulerPolicy); | 307 enterSchedulerPolicyLocked(schedulerPolicy); |
258 } | 308 } |
259 | 309 |
260 void Scheduler::enterSchedulerPolicyLocked(SchedulerPolicy schedulerPolicy) | 310 void Scheduler::enterSchedulerPolicyLocked(SchedulerPolicy schedulerPolicy) |
261 { | 311 { |
262 ASSERT(m_policyStateMutex.locked()); | 312 ASSERT(m_policyStateMutex.locked()); |
263 if (schedulerPolicy == CompositorPriority) | 313 if (schedulerPolicy == CompositorPriority) |
264 m_compositorPriorityPolicyEndTimeSeconds = Platform::current()->monotoni
callyIncreasingTime() + kLowSchedulerPolicyAfterTouchTimeSeconds; | 314 m_compositorPriorityPolicyEndTimeSeconds = Platform::current()->monotoni
callyIncreasingTime() + kLowSchedulerPolicyAfterTouchTimeSeconds; |
265 | 315 |
266 releaseStore(&m_schedulerPolicy, schedulerPolicy); | 316 releaseStore(&m_schedulerPolicy, schedulerPolicy); |
267 TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("blink.scheduler"), "SchedulerPolic
y", schedulerPolicy); | 317 TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("blink.scheduler"), "SchedulerPolic
y", schedulerPolicy); |
268 } | 318 } |
269 | 319 |
270 } // namespace blink | 320 } // namespace blink |
OLD | NEW |