Chromium Code Reviews| 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 base::TimeTicks now, | 233 TimeDomain* requesting_time_domain, |
| 234 base::TimeDelta delay) { | 234 LazyNow* lazy_now, |
| 235 base::TimeTicks run_time) { | |
| 235 DCHECK(main_thread_checker_.CalledOnValidThread()); | 236 DCHECK(main_thread_checker_.CalledOnValidThread()); |
|
Sami
2017/02/01 07:30:05
DCHECK(!next_delayed_do_work_ || next_delayed_do_w
alex clarke (OOO till 29th)
2017/02/01 14:50:11
Done.
| |
| 236 DCHECK_GE(delay, base::TimeDelta()); | |
| 237 { | 237 { |
| 238 base::AutoLock lock(any_thread_lock_); | 238 base::AutoLock lock(any_thread_lock_); |
| 239 | 239 |
| 240 // Unless we're nested, don't post a delayed DoWork if there's an immediate | 240 // 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 | 241 // DoWork in flight or we're inside a DoWork. We can rely on DoWork posting |
| 242 // a delayed continuation as needed. | 242 // a delayed continuation as needed. |
| 243 if (!any_thread().is_nested && | 243 if (!any_thread().is_nested && |
| 244 (any_thread().immediate_do_work_posted_count > 0 || | 244 (any_thread().immediate_do_work_posted_count > 0 || |
| 245 any_thread().do_work_running_count == 1)) { | 245 any_thread().do_work_running_count == 1)) { |
| 246 return; | 246 return; |
| 247 } | 247 } |
| 248 } | 248 } |
| 249 | 249 |
| 250 // De-duplicate DoWork posts. | 250 // De-duplicate DoWork posts. |
| 251 base::TimeTicks run_time = now + delay; | 251 if (next_delayed_do_work_ && next_delayed_do_work_.run_time() <= run_time) |
| 252 if (next_scheduled_delayed_do_work_time_ <= run_time && | |
| 253 !next_scheduled_delayed_do_work_time_.is_null()) | |
| 254 return; | 252 return; |
| 255 | 253 |
| 254 cancelable_delayed_do_work_closure_.Reset(delayed_do_work_closure_); | |
| 255 | |
| 256 base::TimeDelta delay = | |
| 257 std::max(base::TimeDelta(), run_time - lazy_now->Now()); | |
| 258 | |
| 256 TRACE_EVENT1(disabled_by_default_tracing_category_, | 259 TRACE_EVENT1(disabled_by_default_tracing_category_, |
| 257 "TaskQueueManager::MaybeScheduleDelayedWork::PostDelayedTask", | 260 "TaskQueueManager::MaybeScheduleDelayedWork::PostDelayedTask", |
| 258 "delay_ms", delay.InMillisecondsF()); | 261 "delay_ms", delay.InMillisecondsF()); |
| 259 | 262 |
| 260 cancelable_delayed_do_work_closure_.Reset(delayed_do_work_closure_); | 263 cancelable_delayed_do_work_closure_.Reset(delayed_do_work_closure_); |
| 261 next_scheduled_delayed_do_work_time_ = run_time; | 264 next_delayed_do_work_ = NextDelayedDoWork(run_time, requesting_time_domain); |
| 262 delegate_->PostDelayedTask( | 265 delegate_->PostDelayedTask( |
| 263 from_here, cancelable_delayed_do_work_closure_.callback(), delay); | 266 from_here, cancelable_delayed_do_work_closure_.callback(), delay); |
| 264 } | 267 } |
| 265 | 268 |
| 269 void TaskQueueManager::CancelDelayedWork(TimeDomain* requesting_time_domain, | |
| 270 base::TimeTicks run_time) { | |
| 271 DCHECK(main_thread_checker_.CalledOnValidThread()); | |
| 272 if (next_delayed_do_work_.run_time() != run_time) | |
| 273 return; | |
| 274 | |
| 275 DCHECK_EQ(next_delayed_do_work_.time_domain(), requesting_time_domain); | |
| 276 cancelable_delayed_do_work_closure_.Cancel(); | |
| 277 next_delayed_do_work_.Clear(); | |
| 278 } | |
| 279 | |
| 266 void TaskQueueManager::DoWork(bool delayed) { | 280 void TaskQueueManager::DoWork(bool delayed) { |
| 267 DCHECK(main_thread_checker_.CalledOnValidThread()); | 281 DCHECK(main_thread_checker_.CalledOnValidThread()); |
| 268 TRACE_EVENT1(tracing_category_, "TaskQueueManager::DoWork", "delayed", | 282 TRACE_EVENT1(tracing_category_, "TaskQueueManager::DoWork", "delayed", |
| 269 delayed); | 283 delayed); |
| 270 | 284 |
| 271 LazyNow lazy_now(real_time_domain()->CreateLazyNow()); | 285 LazyNow lazy_now(real_time_domain()->CreateLazyNow()); |
| 272 bool is_nested = delegate_->IsNested(); | 286 bool is_nested = delegate_->IsNested(); |
| 273 if (!is_nested) | 287 if (!is_nested) |
| 274 queues_to_delete_.clear(); | 288 queues_to_delete_.clear(); |
| 275 | 289 |
| 276 // This must be done before running any tasks because they could invoke a | 290 // This must be done before running any tasks because they could invoke a |
| 277 // nested message loop and we risk having a stale | 291 // nested message loop and we risk having a stale |
| 278 // |next_scheduled_delayed_do_work_time_|. | 292 // |next_delayed_do_work_|. |
|
Sami
2017/02/01 07:30:04
nit: rewrap
alex clarke (OOO till 29th)
2017/02/01 14:50:11
Done.
| |
| 279 if (delayed) | 293 if (delayed) |
| 280 next_scheduled_delayed_do_work_time_ = base::TimeTicks(); | 294 next_delayed_do_work_.Clear(); |
| 295 ; | |
|
Sami
2017/02/01 07:30:04
Extra ';'?
alex clarke (OOO till 29th)
2017/02/01 14:50:11
Done.
| |
| 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; | 375 base::TimeDelta delay; |
| 361 | 376 |
| 362 { | 377 { |
| 363 MoveableAutoLock auto_lock(std::move(lock)); | 378 MoveableAutoLock auto_lock(std::move(lock)); |
| 364 | 379 |
| 365 // If there are no tasks left then we don't need to post a continuation. | 380 // If there are no tasks left then we don't need to post a continuation. |
| 366 if (!next_delay) { | 381 if (!next_delay) { |
| 367 // If there's a pending delayed DoWork, cancel it because it's not needed. | 382 // If there's a pending delayed DoWork, cancel it because it's not needed. |
| 368 if (!next_scheduled_delayed_do_work_time_.is_null()) { | 383 if (next_delayed_do_work_) { |
| 369 next_scheduled_delayed_do_work_time_ = base::TimeTicks(); | 384 next_delayed_do_work_.Clear(); |
| 370 cancelable_delayed_do_work_closure_.Cancel(); | 385 cancelable_delayed_do_work_closure_.Cancel(); |
| 371 } | 386 } |
| 372 return; | 387 return; |
| 373 } | 388 } |
| 374 | 389 |
| 375 // If an immediate DoWork is posted, we don't need to post a continuation. | 390 // If an immediate DoWork is posted, we don't need to post a continuation. |
| 376 if (any_thread().immediate_do_work_posted_count > 0) | 391 if (any_thread().immediate_do_work_posted_count > 0) |
| 377 return; | 392 return; |
| 378 | 393 |
| 379 delay = next_delay.value(); | 394 delay = next_delay->delay(); |
| 380 | 395 |
| 381 // This isn't supposed to happen, but in case it does convert to | 396 // This isn't supposed to happen, but in case it does convert to |
| 382 // non-delayed. | 397 // non-delayed. |
| 383 if (delay < base::TimeDelta()) | 398 if (delay < base::TimeDelta()) |
| 384 delay = base::TimeDelta(); | 399 delay = base::TimeDelta(); |
| 385 | 400 |
| 386 if (delay.is_zero()) { | 401 if (delay.is_zero()) { |
| 387 // If a delayed DoWork is pending then we don't need to post a | 402 // If a delayed DoWork is pending then we don't need to post a |
| 388 // continuation because it should run immediately. | 403 // continuation because it should run immediately. |
| 389 if (!next_scheduled_delayed_do_work_time_.is_null() && | 404 if (next_delayed_do_work_ && |
| 390 next_scheduled_delayed_do_work_time_ <= lazy_now->Now()) { | 405 next_delayed_do_work_.run_time() <= lazy_now->Now()) { |
| 391 return; | 406 return; |
| 392 } | 407 } |
| 393 | 408 |
| 394 any_thread().immediate_do_work_posted_count++; | 409 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 } | 410 } |
| 402 } | 411 } |
| 403 | 412 |
| 404 // We avoid holding |any_thread_lock_| while posting the task. | 413 // We avoid holding |any_thread_lock_| while posting the task. |
| 405 if (delay.is_zero()) { | 414 if (delay.is_zero()) { |
| 406 delegate_->PostTask(FROM_HERE, immediate_do_work_closure_); | 415 delegate_->PostTask(FROM_HERE, immediate_do_work_closure_); |
| 407 } else { | 416 } else { |
| 417 base::TimeTicks run_time = lazy_now->Now() + delay; | |
| 418 if (next_delayed_do_work_.run_time() == run_time) | |
| 419 return; | |
| 420 | |
| 421 next_delayed_do_work_ = | |
| 422 NextDelayedDoWork(run_time, next_delay->time_domain()); | |
| 408 cancelable_delayed_do_work_closure_.Reset(delayed_do_work_closure_); | 423 cancelable_delayed_do_work_closure_.Reset(delayed_do_work_closure_); |
| 409 delegate_->PostDelayedTask( | 424 delegate_->PostDelayedTask( |
| 410 FROM_HERE, cancelable_delayed_do_work_closure_.callback(), delay); | 425 FROM_HERE, cancelable_delayed_do_work_closure_.callback(), delay); |
| 411 } | 426 } |
| 412 } | 427 } |
| 413 | 428 |
| 414 base::Optional<base::TimeDelta> | 429 base::Optional<TaskQueueManager::NextTaskDelay> |
| 415 TaskQueueManager::ComputeDelayTillNextTaskLocked(LazyNow* lazy_now) { | 430 TaskQueueManager::ComputeDelayTillNextTaskLocked(LazyNow* lazy_now) { |
| 416 DCHECK(main_thread_checker_.CalledOnValidThread()); | 431 DCHECK(main_thread_checker_.CalledOnValidThread()); |
| 417 | 432 |
| 418 // Unfortunately because |any_thread_lock_| is held it's not safe to call | 433 // Unfortunately because |any_thread_lock_| is held it's not safe to call |
| 419 // ReloadEmptyWorkQueues here (possible lock order inversion), however this | 434 // ReloadEmptyWorkQueues here (possible lock order inversion), however this |
| 420 // check is equavalent to calling ReloadEmptyWorkQueues first. | 435 // check is equavalent to calling ReloadEmptyWorkQueues first. |
| 421 for (const auto& pair : any_thread().has_incoming_immediate_work) { | 436 for (const auto& pair : any_thread().has_incoming_immediate_work) { |
| 422 if (pair.first->CouldTaskRun(pair.second)) | 437 if (pair.first->CouldTaskRun(pair.second)) |
| 423 return base::TimeDelta(); | 438 return NextTaskDelay(); |
| 424 } | 439 } |
| 425 | 440 |
| 426 // If the selector has non-empty queues we trivially know there is immediate | 441 // If the selector has non-empty queues we trivially know there is immediate |
| 427 // work to be done. | 442 // work to be done. |
| 428 if (!selector_.EnabledWorkQueuesEmpty()) | 443 if (!selector_.EnabledWorkQueuesEmpty()) |
| 429 return base::TimeDelta(); | 444 return NextTaskDelay(); |
| 430 | 445 |
| 431 // Otherwise we need to find the shortest delay, if any. NB we don't need to | 446 // 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 | 447 // call WakeupReadyDelayedQueues because it's assumed DelayTillNextTask will |
| 433 // return base::TimeDelta>() if the delayed task is due to run now. | 448 // return base::TimeDelta>() if the delayed task is due to run now. |
| 434 base::Optional<base::TimeDelta> next_continuation; | 449 base::Optional<NextTaskDelay> delay_till_next_task; |
| 435 for (TimeDomain* time_domain : time_domains_) { | 450 for (TimeDomain* time_domain : time_domains_) { |
| 436 base::Optional<base::TimeDelta> continuation = | 451 base::Optional<base::TimeDelta> delay = |
| 437 time_domain->DelayTillNextTask(lazy_now); | 452 time_domain->DelayTillNextTask(lazy_now); |
| 438 if (!continuation) | 453 if (!delay) |
| 439 continue; | 454 continue; |
| 440 if (!next_continuation || next_continuation.value() > continuation.value()) | 455 |
| 441 next_continuation = continuation; | 456 NextTaskDelay task_delay = (delay.value() == base::TimeDelta()) |
| 457 ? NextTaskDelay() | |
| 458 : NextTaskDelay(delay.value(), time_domain); | |
| 459 | |
| 460 if (!delay_till_next_task || task_delay < delay_till_next_task.value()) | |
| 461 delay_till_next_task = task_delay; | |
| 442 } | 462 } |
| 443 return next_continuation; | 463 return delay_till_next_task; |
| 444 } | 464 } |
| 445 | 465 |
| 446 bool TaskQueueManager::SelectWorkQueueToService( | 466 bool TaskQueueManager::SelectWorkQueueToService( |
| 447 internal::WorkQueue** out_work_queue) { | 467 internal::WorkQueue** out_work_queue) { |
| 448 bool should_run = selector_.SelectWorkQueueToService(out_work_queue); | 468 bool should_run = selector_.SelectWorkQueueToService(out_work_queue); |
| 449 TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID( | 469 TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID( |
| 450 disabled_by_default_tracing_category_, "TaskQueueManager", this, | 470 disabled_by_default_tracing_category_, "TaskQueueManager", this, |
| 451 AsValueWithSelectorResult(should_run, *out_work_queue)); | 471 AsValueWithSelectorResult(should_run, *out_work_queue)); |
| 452 return should_run; | 472 return should_run; |
| 453 } | 473 } |
| (...skipping 237 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 691 for (const scoped_refptr<internal::TaskQueueImpl>& queue : queues_) { | 711 for (const scoped_refptr<internal::TaskQueueImpl>& queue : queues_) { |
| 692 TimeDomain* time_domain = queue->GetTimeDomain(); | 712 TimeDomain* time_domain = queue->GetTimeDomain(); |
| 693 if (time_domain_now.find(time_domain) == time_domain_now.end()) | 713 if (time_domain_now.find(time_domain) == time_domain_now.end()) |
| 694 time_domain_now.insert(std::make_pair(time_domain, time_domain->Now())); | 714 time_domain_now.insert(std::make_pair(time_domain, time_domain->Now())); |
| 695 queue->SweepCanceledDelayedTasks(time_domain_now[time_domain]); | 715 queue->SweepCanceledDelayedTasks(time_domain_now[time_domain]); |
| 696 } | 716 } |
| 697 } | 717 } |
| 698 | 718 |
| 699 } // namespace scheduler | 719 } // namespace scheduler |
| 700 } // namespace blink | 720 } // namespace blink |
| OLD | NEW |