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 212 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
223 any_thread().immediate_do_work_posted_count++; | 223 any_thread().immediate_do_work_posted_count++; |
224 } | 224 } |
225 | 225 |
226 TRACE_EVENT0(disabled_by_default_tracing_category_, | 226 TRACE_EVENT0(disabled_by_default_tracing_category_, |
227 "TaskQueueManager::MaybeScheduleImmediateWorkLocked::PostTask"); | 227 "TaskQueueManager::MaybeScheduleImmediateWorkLocked::PostTask"); |
228 delegate_->PostTask(from_here, immediate_do_work_closure_); | 228 delegate_->PostTask(from_here, immediate_do_work_closure_); |
229 } | 229 } |
230 | 230 |
231 void TaskQueueManager::MaybeScheduleDelayedWork( | 231 void TaskQueueManager::MaybeScheduleDelayedWork( |
232 const tracked_objects::Location& from_here, | 232 const tracked_objects::Location& from_here, |
| 233 TimeDomain* requesting_time_domain, |
233 base::TimeTicks now, | 234 base::TimeTicks now, |
234 base::TimeDelta delay) { | 235 base::TimeTicks run_time) { |
235 DCHECK(main_thread_checker_.CalledOnValidThread()); | 236 DCHECK(main_thread_checker_.CalledOnValidThread()); |
236 DCHECK_GE(delay, base::TimeDelta()); | 237 // Make sure we don't cancel another TimeDomain's wakeup. |
| 238 DCHECK(!next_delayed_do_work_ || |
| 239 next_delayed_do_work_.time_domain() == requesting_time_domain); |
237 { | 240 { |
238 base::AutoLock lock(any_thread_lock_); | 241 base::AutoLock lock(any_thread_lock_); |
239 | 242 |
240 // Unless we're nested, don't post a delayed DoWork if there's an immediate | 243 // Unless we're nested, don't post a delayed DoWork if there's an immediate |
241 // DoWork in flight or we're inside a DoWork. We can rely on DoWork posting | 244 // DoWork in flight or we're inside a DoWork. We can rely on DoWork posting |
242 // a delayed continuation as needed. | 245 // a delayed continuation as needed. |
243 if (!any_thread().is_nested && | 246 if (!any_thread().is_nested && |
244 (any_thread().immediate_do_work_posted_count > 0 || | 247 (any_thread().immediate_do_work_posted_count > 0 || |
245 any_thread().do_work_running_count == 1)) { | 248 any_thread().do_work_running_count == 1)) { |
246 return; | 249 return; |
247 } | 250 } |
248 } | 251 } |
249 | 252 |
250 // De-duplicate DoWork posts. | 253 // If there's a delayed DoWork scheduled to run sooner, we don't need to do |
251 base::TimeTicks run_time = now + delay; | 254 // anything because DoWork will post a delayed continuation as needed. |
252 if (next_scheduled_delayed_do_work_time_ <= run_time && | 255 if (next_delayed_do_work_ && next_delayed_do_work_.run_time() <= run_time) |
253 !next_scheduled_delayed_do_work_time_.is_null()) | |
254 return; | 256 return; |
255 | 257 |
| 258 cancelable_delayed_do_work_closure_.Reset(delayed_do_work_closure_); |
| 259 |
| 260 base::TimeDelta delay = std::max(base::TimeDelta(), run_time - now); |
256 TRACE_EVENT1(disabled_by_default_tracing_category_, | 261 TRACE_EVENT1(disabled_by_default_tracing_category_, |
257 "TaskQueueManager::MaybeScheduleDelayedWork::PostDelayedTask", | 262 "TaskQueueManager::MaybeScheduleDelayedWork::PostDelayedTask", |
258 "delay_ms", delay.InMillisecondsF()); | 263 "delay_ms", delay.InMillisecondsF()); |
259 | 264 |
260 cancelable_delayed_do_work_closure_.Reset(delayed_do_work_closure_); | 265 cancelable_delayed_do_work_closure_.Reset(delayed_do_work_closure_); |
261 next_scheduled_delayed_do_work_time_ = run_time; | 266 next_delayed_do_work_ = NextDelayedDoWork(run_time, requesting_time_domain); |
262 delegate_->PostDelayedTask( | 267 delegate_->PostDelayedTask( |
263 from_here, cancelable_delayed_do_work_closure_.callback(), delay); | 268 from_here, cancelable_delayed_do_work_closure_.callback(), delay); |
264 } | 269 } |
265 | 270 |
| 271 void TaskQueueManager::CancelDelayedWork(TimeDomain* requesting_time_domain, |
| 272 base::TimeTicks run_time) { |
| 273 DCHECK(main_thread_checker_.CalledOnValidThread()); |
| 274 if (next_delayed_do_work_.run_time() != run_time) |
| 275 return; |
| 276 |
| 277 DCHECK_EQ(next_delayed_do_work_.time_domain(), requesting_time_domain); |
| 278 cancelable_delayed_do_work_closure_.Cancel(); |
| 279 next_delayed_do_work_.Clear(); |
| 280 } |
| 281 |
266 void TaskQueueManager::DoWork(bool delayed) { | 282 void TaskQueueManager::DoWork(bool delayed) { |
267 DCHECK(main_thread_checker_.CalledOnValidThread()); | 283 DCHECK(main_thread_checker_.CalledOnValidThread()); |
268 TRACE_EVENT1(tracing_category_, "TaskQueueManager::DoWork", "delayed", | 284 TRACE_EVENT1(tracing_category_, "TaskQueueManager::DoWork", "delayed", |
269 delayed); | 285 delayed); |
270 | 286 |
271 LazyNow lazy_now(real_time_domain()->CreateLazyNow()); | 287 LazyNow lazy_now(real_time_domain()->CreateLazyNow()); |
272 bool is_nested = delegate_->IsNested(); | 288 bool is_nested = delegate_->IsNested(); |
273 if (!is_nested) | 289 if (!is_nested) |
274 queues_to_delete_.clear(); | 290 queues_to_delete_.clear(); |
275 | 291 |
276 // This must be done before running any tasks because they could invoke a | 292 // This must be done before running any tasks because they could invoke a |
277 // nested message loop and we risk having a stale | 293 // nested message loop and we risk having a stale |next_delayed_do_work_|. |
278 // |next_scheduled_delayed_do_work_time_|. | |
279 if (delayed) | 294 if (delayed) |
280 next_scheduled_delayed_do_work_time_ = base::TimeTicks(); | 295 next_delayed_do_work_.Clear(); |
281 | 296 |
282 for (int i = 0; i < work_batch_size_; i++) { | 297 for (int i = 0; i < work_batch_size_; i++) { |
283 IncomingImmediateWorkMap queues_to_reload; | 298 IncomingImmediateWorkMap queues_to_reload; |
284 | 299 |
285 { | 300 { |
286 base::AutoLock lock(any_thread_lock_); | 301 base::AutoLock lock(any_thread_lock_); |
287 if (i == 0) { | 302 if (i == 0) { |
288 any_thread().do_work_running_count++; | 303 any_thread().do_work_running_count++; |
289 | 304 |
290 if (!delayed) { | 305 if (!delayed) { |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
332 if (is_nested) | 347 if (is_nested) |
333 break; | 348 break; |
334 } | 349 } |
335 | 350 |
336 // TODO(alexclarke): Consider refactoring the above loop to terminate only | 351 // TODO(alexclarke): Consider refactoring the above loop to terminate only |
337 // when there's no more work left to be done, rather than posting a | 352 // when there's no more work left to be done, rather than posting a |
338 // continuation task. | 353 // continuation task. |
339 | 354 |
340 { | 355 { |
341 MoveableAutoLock lock(any_thread_lock_); | 356 MoveableAutoLock lock(any_thread_lock_); |
342 base::Optional<base::TimeDelta> next_delay = | 357 base::Optional<NextTaskDelay> next_delay = |
343 ComputeDelayTillNextTaskLocked(&lazy_now); | 358 ComputeDelayTillNextTaskLocked(&lazy_now); |
344 | 359 |
345 any_thread().do_work_running_count--; | 360 any_thread().do_work_running_count--; |
346 DCHECK_GE(any_thread().do_work_running_count, 0); | 361 DCHECK_GE(any_thread().do_work_running_count, 0); |
347 | 362 |
348 any_thread().is_nested = is_nested; | 363 any_thread().is_nested = is_nested; |
349 DCHECK_EQ(any_thread().is_nested, delegate_->IsNested()); | 364 DCHECK_EQ(any_thread().is_nested, delegate_->IsNested()); |
350 | 365 |
351 PostDoWorkContinuationLocked(next_delay, &lazy_now, std::move(lock)); | 366 PostDoWorkContinuationLocked(next_delay, &lazy_now, std::move(lock)); |
352 } | 367 } |
353 } | 368 } |
354 | 369 |
355 void TaskQueueManager::PostDoWorkContinuationLocked( | 370 void TaskQueueManager::PostDoWorkContinuationLocked( |
356 base::Optional<base::TimeDelta> next_delay, | 371 base::Optional<NextTaskDelay> next_delay, |
357 LazyNow* lazy_now, | 372 LazyNow* lazy_now, |
358 MoveableAutoLock&& lock) { | 373 MoveableAutoLock&& lock) { |
359 DCHECK(main_thread_checker_.CalledOnValidThread()); | 374 DCHECK(main_thread_checker_.CalledOnValidThread()); |
360 base::TimeDelta delay; | |
361 | 375 |
362 { | 376 { |
363 MoveableAutoLock auto_lock(std::move(lock)); | 377 MoveableAutoLock auto_lock(std::move(lock)); |
364 | 378 |
365 // If there are no tasks left then we don't need to post a continuation. | 379 // If there are no tasks left then we don't need to post a continuation. |
366 if (!next_delay) { | 380 if (!next_delay) { |
367 // If there's a pending delayed DoWork, cancel it because it's not needed. | 381 // If there's a pending delayed DoWork, cancel it because it's not needed. |
368 if (!next_scheduled_delayed_do_work_time_.is_null()) { | 382 if (next_delayed_do_work_) { |
369 next_scheduled_delayed_do_work_time_ = base::TimeTicks(); | 383 next_delayed_do_work_.Clear(); |
370 cancelable_delayed_do_work_closure_.Cancel(); | 384 cancelable_delayed_do_work_closure_.Cancel(); |
371 } | 385 } |
372 return; | 386 return; |
373 } | 387 } |
374 | 388 |
375 // If an immediate DoWork is posted, we don't need to post a continuation. | 389 // If an immediate DoWork is posted, we don't need to post a continuation. |
376 if (any_thread().immediate_do_work_posted_count > 0) | 390 if (any_thread().immediate_do_work_posted_count > 0) |
377 return; | 391 return; |
378 | 392 |
379 delay = next_delay.value(); | 393 if (next_delay->delay() <= base::TimeDelta()) { |
380 | |
381 // This isn't supposed to happen, but in case it does convert to | |
382 // non-delayed. | |
383 if (delay < base::TimeDelta()) | |
384 delay = base::TimeDelta(); | |
385 | |
386 if (delay.is_zero()) { | |
387 // If a delayed DoWork is pending then we don't need to post a | 394 // If a delayed DoWork is pending then we don't need to post a |
388 // continuation because it should run immediately. | 395 // continuation because it should run immediately. |
389 if (!next_scheduled_delayed_do_work_time_.is_null() && | 396 if (next_delayed_do_work_ && |
390 next_scheduled_delayed_do_work_time_ <= lazy_now->Now()) { | 397 next_delayed_do_work_.run_time() <= lazy_now->Now()) { |
391 return; | 398 return; |
392 } | 399 } |
393 | 400 |
394 any_thread().immediate_do_work_posted_count++; | 401 any_thread().immediate_do_work_posted_count++; |
395 } else { | |
396 base::TimeTicks run_time = lazy_now->Now() + delay; | |
397 if (next_scheduled_delayed_do_work_time_ == run_time) | |
398 return; | |
399 | |
400 next_scheduled_delayed_do_work_time_ = run_time; | |
401 } | 402 } |
402 } | 403 } |
403 | 404 |
404 // We avoid holding |any_thread_lock_| while posting the task. | 405 // We avoid holding |any_thread_lock_| while posting the task. |
405 if (delay.is_zero()) { | 406 if (next_delay->delay() <= base::TimeDelta()) { |
406 delegate_->PostTask(FROM_HERE, immediate_do_work_closure_); | 407 delegate_->PostTask(FROM_HERE, immediate_do_work_closure_); |
407 } else { | 408 } else { |
| 409 base::TimeTicks run_time = lazy_now->Now() + next_delay->delay(); |
| 410 |
| 411 if (next_delayed_do_work_.run_time() == run_time) |
| 412 return; |
| 413 |
| 414 next_delayed_do_work_ = |
| 415 NextDelayedDoWork(run_time, next_delay->time_domain()); |
408 cancelable_delayed_do_work_closure_.Reset(delayed_do_work_closure_); | 416 cancelable_delayed_do_work_closure_.Reset(delayed_do_work_closure_); |
409 delegate_->PostDelayedTask( | 417 delegate_->PostDelayedTask(FROM_HERE, |
410 FROM_HERE, cancelable_delayed_do_work_closure_.callback(), delay); | 418 cancelable_delayed_do_work_closure_.callback(), |
| 419 next_delay->delay()); |
411 } | 420 } |
412 } | 421 } |
413 | 422 |
414 base::Optional<base::TimeDelta> | 423 base::Optional<TaskQueueManager::NextTaskDelay> |
415 TaskQueueManager::ComputeDelayTillNextTaskLocked(LazyNow* lazy_now) { | 424 TaskQueueManager::ComputeDelayTillNextTaskLocked(LazyNow* lazy_now) { |
416 DCHECK(main_thread_checker_.CalledOnValidThread()); | 425 DCHECK(main_thread_checker_.CalledOnValidThread()); |
417 | 426 |
418 // Unfortunately because |any_thread_lock_| is held it's not safe to call | 427 // Unfortunately because |any_thread_lock_| is held it's not safe to call |
419 // ReloadEmptyWorkQueues here (possible lock order inversion), however this | 428 // ReloadEmptyWorkQueues here (possible lock order inversion), however this |
420 // check is equavalent to calling ReloadEmptyWorkQueues first. | 429 // check is equavalent to calling ReloadEmptyWorkQueues first. |
421 for (const auto& pair : any_thread().has_incoming_immediate_work) { | 430 for (const auto& pair : any_thread().has_incoming_immediate_work) { |
422 if (pair.first->CouldTaskRun(pair.second)) | 431 if (pair.first->CouldTaskRun(pair.second)) |
423 return base::TimeDelta(); | 432 return NextTaskDelay(); |
424 } | 433 } |
425 | 434 |
426 // If the selector has non-empty queues we trivially know there is immediate | 435 // If the selector has non-empty queues we trivially know there is immediate |
427 // work to be done. | 436 // work to be done. |
428 if (!selector_.EnabledWorkQueuesEmpty()) | 437 if (!selector_.EnabledWorkQueuesEmpty()) |
429 return base::TimeDelta(); | 438 return NextTaskDelay(); |
430 | 439 |
431 // Otherwise we need to find the shortest delay, if any. NB we don't need to | 440 // Otherwise we need to find the shortest delay, if any. NB we don't need to |
432 // call WakeupReadyDelayedQueues because it's assumed DelayTillNextTask will | 441 // call WakeupReadyDelayedQueues because it's assumed DelayTillNextTask will |
433 // return base::TimeDelta>() if the delayed task is due to run now. | 442 // return base::TimeDelta>() if the delayed task is due to run now. |
434 base::Optional<base::TimeDelta> next_continuation; | 443 base::Optional<NextTaskDelay> delay_till_next_task; |
435 for (TimeDomain* time_domain : time_domains_) { | 444 for (TimeDomain* time_domain : time_domains_) { |
436 base::Optional<base::TimeDelta> continuation = | 445 base::Optional<base::TimeDelta> delay = |
437 time_domain->DelayTillNextTask(lazy_now); | 446 time_domain->DelayTillNextTask(lazy_now); |
438 if (!continuation) | 447 if (!delay) |
439 continue; | 448 continue; |
440 if (!next_continuation || next_continuation.value() > continuation.value()) | 449 |
441 next_continuation = continuation; | 450 NextTaskDelay task_delay = (delay.value() == base::TimeDelta()) |
| 451 ? NextTaskDelay() |
| 452 : NextTaskDelay(delay.value(), time_domain); |
| 453 |
| 454 if (!delay_till_next_task || delay_till_next_task > task_delay) |
| 455 delay_till_next_task = task_delay; |
442 } | 456 } |
443 return next_continuation; | 457 return delay_till_next_task; |
444 } | 458 } |
445 | 459 |
446 bool TaskQueueManager::SelectWorkQueueToService( | 460 bool TaskQueueManager::SelectWorkQueueToService( |
447 internal::WorkQueue** out_work_queue) { | 461 internal::WorkQueue** out_work_queue) { |
448 bool should_run = selector_.SelectWorkQueueToService(out_work_queue); | 462 bool should_run = selector_.SelectWorkQueueToService(out_work_queue); |
449 TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID( | 463 TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID( |
450 disabled_by_default_tracing_category_, "TaskQueueManager", this, | 464 disabled_by_default_tracing_category_, "TaskQueueManager", this, |
451 AsValueWithSelectorResult(should_run, *out_work_queue)); | 465 AsValueWithSelectorResult(should_run, *out_work_queue)); |
452 return should_run; | 466 return should_run; |
453 } | 467 } |
(...skipping 237 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
691 for (const scoped_refptr<internal::TaskQueueImpl>& queue : queues_) { | 705 for (const scoped_refptr<internal::TaskQueueImpl>& queue : queues_) { |
692 TimeDomain* time_domain = queue->GetTimeDomain(); | 706 TimeDomain* time_domain = queue->GetTimeDomain(); |
693 if (time_domain_now.find(time_domain) == time_domain_now.end()) | 707 if (time_domain_now.find(time_domain) == time_domain_now.end()) |
694 time_domain_now.insert(std::make_pair(time_domain, time_domain->Now())); | 708 time_domain_now.insert(std::make_pair(time_domain, time_domain->Now())); |
695 queue->SweepCanceledDelayedTasks(time_domain_now[time_domain]); | 709 queue->SweepCanceledDelayedTasks(time_domain_now[time_domain]); |
696 } | 710 } |
697 } | 711 } |
698 | 712 |
699 } // namespace scheduler | 713 } // namespace scheduler |
700 } // namespace blink | 714 } // namespace blink |
OLD | NEW |