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 "platform/scheduler/base/task_queue_manager.h" | 5 #include "platform/scheduler/base/task_queue_manager.h" |
6 | 6 |
7 #include <queue> | 7 #include <queue> |
8 #include <set> | 8 #include <set> |
9 | 9 |
10 #include "base/bind.h" | 10 #include "base/bind.h" |
(...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
124 if (observer_) | 124 if (observer_) |
125 observer_->OnUnregisterTaskQueue(task_queue); | 125 observer_->OnUnregisterTaskQueue(task_queue); |
126 | 126 |
127 // Add |task_queue| to |queues_to_delete_| so we can prevent it from being | 127 // Add |task_queue| to |queues_to_delete_| so we can prevent it from being |
128 // freed while any of our structures hold hold a raw pointer to it. | 128 // freed while any of our structures hold hold a raw pointer to it. |
129 queues_to_delete_.insert(task_queue); | 129 queues_to_delete_.insert(task_queue); |
130 queues_.erase(task_queue); | 130 queues_.erase(task_queue); |
131 selector_.RemoveQueue(task_queue.get()); | 131 selector_.RemoveQueue(task_queue.get()); |
132 } | 132 } |
133 | 133 |
134 void TaskQueueManager::UpdateWorkQueues( | 134 void TaskQueueManager::UpdateWorkQueues(LazyNow lazy_now) { |
135 bool should_trigger_wakeup, | |
136 const internal::TaskQueueImpl::Task* previous_task, | |
137 LazyNow lazy_now) { | |
138 TRACE_EVENT0(disabled_by_default_tracing_category_, | 135 TRACE_EVENT0(disabled_by_default_tracing_category_, |
139 "TaskQueueManager::UpdateWorkQueues"); | 136 "TaskQueueManager::UpdateWorkQueues"); |
140 | 137 |
141 for (TimeDomain* time_domain : time_domains_) { | 138 for (TimeDomain* time_domain : time_domains_) { |
142 LazyNow lazy_now_in_domain = time_domain == real_time_domain_.get() | 139 LazyNow lazy_now_in_domain = time_domain == real_time_domain_.get() |
143 ? lazy_now | 140 ? lazy_now |
144 : time_domain->CreateLazyNow(); | 141 : time_domain->CreateLazyNow(); |
145 time_domain->UpdateWorkQueues(should_trigger_wakeup, previous_task, | 142 time_domain->UpdateWorkQueues(lazy_now_in_domain); |
146 lazy_now_in_domain); | |
147 } | 143 } |
148 } | 144 } |
149 | 145 |
150 void TaskQueueManager::MaybeScheduleImmediateWork( | 146 void TaskQueueManager::MaybeScheduleImmediateWork( |
151 const tracked_objects::Location& from_here) { | 147 const tracked_objects::Location& from_here) { |
152 bool on_main_thread = delegate_->BelongsToCurrentThread(); | 148 bool on_main_thread = delegate_->BelongsToCurrentThread(); |
153 // De-duplicate DoWork posts. | 149 // De-duplicate DoWork posts. |
154 if (on_main_thread) { | 150 if (on_main_thread) { |
155 if (!main_thread_pending_wakeups_.insert(base::TimeTicks()).second) { | 151 if (!main_thread_pending_wakeups_.insert(base::TimeTicks()).second) { |
156 return; | 152 return; |
(...skipping 30 matching lines...) Expand all Loading... |
187 DCHECK(main_thread_checker_.CalledOnValidThread()); | 183 DCHECK(main_thread_checker_.CalledOnValidThread()); |
188 TRACE_EVENT1(tracing_category_, "TaskQueueManager::DoWork", | 184 TRACE_EVENT1(tracing_category_, "TaskQueueManager::DoWork", |
189 "from_main_thread", from_main_thread); | 185 "from_main_thread", from_main_thread); |
190 if (from_main_thread) { | 186 if (from_main_thread) { |
191 main_thread_pending_wakeups_.erase(run_time); | 187 main_thread_pending_wakeups_.erase(run_time); |
192 } else { | 188 } else { |
193 base::AutoLock lock(other_thread_lock_); | 189 base::AutoLock lock(other_thread_lock_); |
194 other_thread_pending_wakeups_.erase(run_time); | 190 other_thread_pending_wakeups_.erase(run_time); |
195 } | 191 } |
196 | 192 |
| 193 // TODO(alexclarke): Add a base::RunLoop observer and prevent |
| 194 // MaybeScheduleImmediateWork posting MaybeScheduleImmediateWork while DoWork |
| 195 // is running. We'll need to post a DoWork on entering and leaving a nested |
| 196 // run loop. |
| 197 |
197 if (!delegate_->IsNested()) | 198 if (!delegate_->IsNested()) |
198 queues_to_delete_.clear(); | 199 queues_to_delete_.clear(); |
199 | 200 |
200 LazyNow lazy_now(real_time_domain()->CreateLazyNow()); | 201 LazyNow lazy_now(real_time_domain()->CreateLazyNow()); |
201 base::TimeTicks task_start_time; | 202 base::TimeTicks task_start_time; |
202 | 203 |
203 if (!delegate_->IsNested() && task_time_observers_.might_have_observers()) | 204 if (!delegate_->IsNested() && task_time_observers_.might_have_observers()) |
204 task_start_time = lazy_now.Now(); | 205 task_start_time = lazy_now.Now(); |
205 | 206 |
206 // Pass false and nullptr to UpdateWorkQueues here to prevent waking up a | 207 // TODO(alexclarke): Get rid of this and call once per loop iteration. |
207 // pump-after-wakeup queue. | 208 UpdateWorkQueues(lazy_now); |
208 UpdateWorkQueues(false, nullptr, lazy_now); | |
209 | |
210 internal::TaskQueueImpl::Task previous_task; | |
211 | 209 |
212 for (int i = 0; i < work_batch_size_; i++) { | 210 for (int i = 0; i < work_batch_size_; i++) { |
213 internal::WorkQueue* work_queue; | 211 internal::WorkQueue* work_queue; |
214 if (!SelectWorkQueueToService(&work_queue)) { | 212 if (!SelectWorkQueueToService(&work_queue)) |
215 break; | 213 break; |
216 } | |
217 | 214 |
218 bool should_trigger_wakeup = work_queue->task_queue()->wakeup_policy() == | 215 switch (ProcessTaskFromWorkQueue(work_queue)) { |
219 TaskQueue::WakeupPolicy::CAN_WAKE_OTHER_QUEUES; | |
220 | |
221 switch (ProcessTaskFromWorkQueue(work_queue, &previous_task)) { | |
222 case ProcessTaskResult::DEFERRED: | 216 case ProcessTaskResult::DEFERRED: |
223 // If a task was deferred, try again with another task. Note that this | 217 // If a task was deferred, try again with another task. |
224 // means deferred tasks (i.e. non-nestable tasks) will never trigger | |
225 // queue wake-ups. | |
226 continue; | 218 continue; |
227 case ProcessTaskResult::EXECUTED: | 219 case ProcessTaskResult::EXECUTED: |
228 break; | 220 break; |
229 case ProcessTaskResult::TASK_QUEUE_MANAGER_DELETED: | 221 case ProcessTaskResult::TASK_QUEUE_MANAGER_DELETED: |
230 return; // The TaskQueueManager got deleted, we must bail out. | 222 return; // The TaskQueueManager got deleted, we must bail out. |
231 } | 223 } |
232 | 224 |
233 lazy_now = real_time_domain()->CreateLazyNow(); | 225 lazy_now = real_time_domain()->CreateLazyNow(); |
234 if (!delegate_->IsNested() && task_start_time != base::TimeTicks()) { | 226 if (!delegate_->IsNested() && task_start_time != base::TimeTicks()) { |
235 // Only report top level task durations. | 227 // Only report top level task durations. |
236 base::TimeTicks task_end_time = lazy_now.Now(); | 228 base::TimeTicks task_end_time = lazy_now.Now(); |
237 FOR_EACH_OBSERVER(TaskTimeObserver, task_time_observers_, | 229 FOR_EACH_OBSERVER(TaskTimeObserver, task_time_observers_, |
238 ReportTaskTime(MonotonicTimeInSeconds(task_start_time), | 230 ReportTaskTime(MonotonicTimeInSeconds(task_start_time), |
239 MonotonicTimeInSeconds(task_end_time))); | 231 MonotonicTimeInSeconds(task_end_time))); |
240 task_start_time = task_end_time; | 232 task_start_time = task_end_time; |
241 } | 233 } |
242 | 234 |
243 work_queue = nullptr; // The queue may have been unregistered. | 235 work_queue = nullptr; // The queue may have been unregistered. |
244 | 236 |
245 UpdateWorkQueues(should_trigger_wakeup, &previous_task, lazy_now); | 237 UpdateWorkQueues(lazy_now); |
246 | 238 |
247 // Only run a single task per batch in nested run loops so that we can | 239 // Only run a single task per batch in nested run loops so that we can |
248 // properly exit the nested loop when someone calls RunLoop::Quit(). | 240 // properly exit the nested loop when someone calls RunLoop::Quit(). |
249 if (delegate_->IsNested()) | 241 if (delegate_->IsNested()) |
250 break; | 242 break; |
251 } | 243 } |
252 | 244 |
253 // TODO(alexclarke): Consider refactoring the above loop to terminate only | 245 // TODO(alexclarke): Consider refactoring the above loop to terminate only |
254 // when there's no more work left to be done, rather than posting a | 246 // when there's no more work left to be done, rather than posting a |
255 // continuation task. | 247 // continuation task. |
(...skipping 17 matching lines...) Expand all Loading... |
273 AsValueWithSelectorResult(should_run, *out_work_queue)); | 265 AsValueWithSelectorResult(should_run, *out_work_queue)); |
274 return should_run; | 266 return should_run; |
275 } | 267 } |
276 | 268 |
277 void TaskQueueManager::DidQueueTask( | 269 void TaskQueueManager::DidQueueTask( |
278 const internal::TaskQueueImpl::Task& pending_task) { | 270 const internal::TaskQueueImpl::Task& pending_task) { |
279 task_annotator_.DidQueueTask("TaskQueueManager::PostTask", pending_task); | 271 task_annotator_.DidQueueTask("TaskQueueManager::PostTask", pending_task); |
280 } | 272 } |
281 | 273 |
282 TaskQueueManager::ProcessTaskResult TaskQueueManager::ProcessTaskFromWorkQueue( | 274 TaskQueueManager::ProcessTaskResult TaskQueueManager::ProcessTaskFromWorkQueue( |
283 internal::WorkQueue* work_queue, | 275 internal::WorkQueue* work_queue) { |
284 internal::TaskQueueImpl::Task* out_previous_task) { | |
285 DCHECK(main_thread_checker_.CalledOnValidThread()); | 276 DCHECK(main_thread_checker_.CalledOnValidThread()); |
286 scoped_refptr<DeletionSentinel> protect(deletion_sentinel_); | 277 scoped_refptr<DeletionSentinel> protect(deletion_sentinel_); |
287 internal::TaskQueueImpl* queue = work_queue->task_queue(); | 278 internal::TaskQueueImpl* queue = work_queue->task_queue(); |
288 | 279 |
289 if (queue->GetQuiescenceMonitored()) | 280 if (queue->GetQuiescenceMonitored()) |
290 task_was_run_on_quiescence_monitored_queue_ = true; | 281 task_was_run_on_quiescence_monitored_queue_ = true; |
291 | 282 |
292 internal::TaskQueueImpl::Task pending_task = | 283 internal::TaskQueueImpl::Task pending_task = |
293 work_queue->TakeTaskFromWorkQueue(); | 284 work_queue->TakeTaskFromWorkQueue(); |
294 if (!pending_task.nestable && delegate_->IsNested()) { | 285 if (!pending_task.nestable && delegate_->IsNested()) { |
(...skipping 16 matching lines...) Expand all Loading... |
311 } | 302 } |
312 TRACE_EVENT1(tracing_category_, "TaskQueueManager::RunTask", "queue", | 303 TRACE_EVENT1(tracing_category_, "TaskQueueManager::RunTask", "queue", |
313 queue->GetName()); | 304 queue->GetName()); |
314 // NOTE when TaskQueues get unregistered a reference ends up getting retained | 305 // NOTE when TaskQueues get unregistered a reference ends up getting retained |
315 // by |queues_to_delete_| which is cleared at the top of |DoWork|. This means | 306 // by |queues_to_delete_| which is cleared at the top of |DoWork|. This means |
316 // we are OK to use raw pointers here. | 307 // we are OK to use raw pointers here. |
317 internal::TaskQueueImpl* prev_executing_task_queue = | 308 internal::TaskQueueImpl* prev_executing_task_queue = |
318 currently_executing_task_queue_; | 309 currently_executing_task_queue_; |
319 currently_executing_task_queue_ = queue; | 310 currently_executing_task_queue_ = queue; |
320 task_annotator_.RunTask("TaskQueueManager::PostTask", pending_task); | 311 task_annotator_.RunTask("TaskQueueManager::PostTask", pending_task); |
321 | |
322 // Detect if the TaskQueueManager just got deleted. If this happens we must | 312 // Detect if the TaskQueueManager just got deleted. If this happens we must |
323 // not access any member variables after this point. | 313 // not access any member variables after this point. |
324 if (protect->HasOneRef()) | 314 if (protect->HasOneRef()) |
325 return ProcessTaskResult::TASK_QUEUE_MANAGER_DELETED; | 315 return ProcessTaskResult::TASK_QUEUE_MANAGER_DELETED; |
326 | 316 |
327 currently_executing_task_queue_ = prev_executing_task_queue; | 317 currently_executing_task_queue_ = prev_executing_task_queue; |
328 | 318 |
329 if (queue->GetShouldNotifyObservers()) { | 319 if (queue->GetShouldNotifyObservers()) { |
330 FOR_EACH_OBSERVER(base::MessageLoop::TaskObserver, task_observers_, | 320 FOR_EACH_OBSERVER(base::MessageLoop::TaskObserver, task_observers_, |
331 DidProcessTask(pending_task)); | 321 DidProcessTask(pending_task)); |
332 queue->NotifyDidProcessTask(pending_task); | 322 queue->NotifyDidProcessTask(pending_task); |
333 } | 323 } |
334 | 324 |
335 pending_task.task.Reset(); | |
336 *out_previous_task = std::move(pending_task); | |
337 return ProcessTaskResult::EXECUTED; | 325 return ProcessTaskResult::EXECUTED; |
338 } | 326 } |
339 | 327 |
340 void TaskQueueManager::MaybeRecordTaskDelayHistograms( | 328 void TaskQueueManager::MaybeRecordTaskDelayHistograms( |
341 const internal::TaskQueueImpl::Task& pending_task, | 329 const internal::TaskQueueImpl::Task& pending_task, |
342 const internal::TaskQueueImpl* queue) { | 330 const internal::TaskQueueImpl* queue) { |
343 if ((task_count_++ % kRecordRecordTaskDelayHistogramsEveryNTasks) != 0) | 331 if ((task_count_++ % kRecordRecordTaskDelayHistogramsEveryNTasks) != 0) |
344 return; | 332 return; |
345 | 333 |
346 // Record delayed task lateness and immediate task queueing durations, but | 334 // Record delayed task lateness and immediate task queuing durations. |
347 // only for auto-pumped queues. Manually pumped and after wakeup queues can | 335 if (!pending_task.delayed_run_time.is_null()) { |
348 // have arbitarially large delayes, which would cloud any analysis. | 336 RecordDelayedTaskLateness(delegate_->NowTicks() - |
349 if (queue->GetPumpPolicy() == TaskQueue::PumpPolicy::AUTO) { | 337 pending_task.delayed_run_time); |
350 if (!pending_task.delayed_run_time.is_null()) { | 338 } else if (!pending_task.time_posted.is_null()) { |
351 RecordDelayedTaskLateness(delegate_->NowTicks() - | 339 RecordImmediateTaskQueueingDuration(tracked_objects::TrackedTime::Now() - |
352 pending_task.delayed_run_time); | 340 pending_task.time_posted); |
353 } else if (!pending_task.time_posted.is_null()) { | |
354 RecordImmediateTaskQueueingDuration(tracked_objects::TrackedTime::Now() - | |
355 pending_task.time_posted); | |
356 } | |
357 } | 341 } |
358 } | 342 } |
359 | 343 |
360 bool TaskQueueManager::RunsTasksOnCurrentThread() const { | 344 bool TaskQueueManager::RunsTasksOnCurrentThread() const { |
361 return delegate_->RunsTasksOnCurrentThread(); | 345 return delegate_->RunsTasksOnCurrentThread(); |
362 } | 346 } |
363 | 347 |
364 void TaskQueueManager::SetWorkBatchSize(int work_batch_size) { | 348 void TaskQueueManager::SetWorkBatchSize(int work_batch_size) { |
365 DCHECK(main_thread_checker_.CalledOnValidThread()); | 349 DCHECK(main_thread_checker_.CalledOnValidThread()); |
366 DCHECK_GE(work_batch_size, 1); | 350 DCHECK_GE(work_batch_size, 1); |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
432 state->BeginArray("time_domains"); | 416 state->BeginArray("time_domains"); |
433 for (auto* time_domain : time_domains_) | 417 for (auto* time_domain : time_domains_) |
434 time_domain->AsValueInto(state.get()); | 418 time_domain->AsValueInto(state.get()); |
435 state->EndArray(); | 419 state->EndArray(); |
436 return std::move(state); | 420 return std::move(state); |
437 } | 421 } |
438 | 422 |
439 void TaskQueueManager::OnTaskQueueEnabled(internal::TaskQueueImpl* queue) { | 423 void TaskQueueManager::OnTaskQueueEnabled(internal::TaskQueueImpl* queue) { |
440 DCHECK(main_thread_checker_.CalledOnValidThread()); | 424 DCHECK(main_thread_checker_.CalledOnValidThread()); |
441 // Only schedule DoWork if there's something to do. | 425 // Only schedule DoWork if there's something to do. |
442 if (!queue->immediate_work_queue()->Empty() || | 426 if (queue->HasPendingImmediateWork()) |
443 !queue->delayed_work_queue()->Empty()) { | |
444 MaybeScheduleImmediateWork(FROM_HERE); | 427 MaybeScheduleImmediateWork(FROM_HERE); |
445 } | |
446 } | 428 } |
447 | 429 |
448 void TaskQueueManager::OnTriedToSelectBlockedWorkQueue( | 430 void TaskQueueManager::OnTriedToSelectBlockedWorkQueue( |
449 internal::WorkQueue* work_queue) { | 431 internal::WorkQueue* work_queue) { |
450 DCHECK(main_thread_checker_.CalledOnValidThread()); | 432 DCHECK(main_thread_checker_.CalledOnValidThread()); |
451 DCHECK(!work_queue->Empty()); | 433 DCHECK(!work_queue->Empty()); |
452 if (observer_) { | 434 if (observer_) { |
453 observer_->OnTriedToExecuteBlockedTask(*work_queue->task_queue(), | 435 observer_->OnTriedToExecuteBlockedTask(*work_queue->task_queue(), |
454 *work_queue->GetFrontTask()); | 436 *work_queue->GetFrontTask()); |
455 } | 437 } |
456 } | 438 } |
457 | 439 |
458 } // namespace scheduler | 440 } // namespace scheduler |
459 } // namespace blink | 441 } // namespace blink |
OLD | NEW |