Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(958)

Unified Diff: third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.cc

Issue 2546423002: [Try # 3] Scheduler refactoring to virtually eliminate redundant DoWorks (Closed)
Patch Set: Added the MoveableAutoLock per Sami's suggestion Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.cc
diff --git a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.cc b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.cc
index 74a311b176169842a5b18c0f8dc41f83d0819383..a44ab4ad973c9dcb69fcb3c2d50e06e9cf83385b 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.cc
+++ b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.cc
@@ -58,7 +58,7 @@ TaskQueueManager::TaskQueueManager(
: real_time_domain_(new RealTimeDomain(tracing_category)),
delegate_(delegate),
task_was_run_on_quiescence_monitored_queue_(false),
- other_thread_pending_wakeup_(false),
+ record_task_delay_histograms_(true),
work_batch_size_(1),
task_count_(0),
tracing_category_(tracing_category),
@@ -75,12 +75,10 @@ TaskQueueManager::TaskQueueManager(
"TaskQueueManager", this);
selector_.SetTaskQueueSelectorObserver(this);
- from_main_thread_immediate_do_work_closure_ =
- base::Bind(&TaskQueueManager::DoWork, weak_factory_.GetWeakPtr(),
- base::TimeTicks(), true);
- from_other_thread_immediate_do_work_closure_ =
- base::Bind(&TaskQueueManager::DoWork, weak_factory_.GetWeakPtr(),
- base::TimeTicks(), false);
+ delayed_do_work_closure_ =
+ base::Bind(&TaskQueueManager::DoWork, weak_factory_.GetWeakPtr(), true);
+ immediate_do_work_closure_ =
+ base::Bind(&TaskQueueManager::DoWork, weak_factory_.GetWeakPtr(), false);
// TODO(alexclarke): Change this to be a parameter that's passed in.
RegisterTimeDomain(real_time_domain_.get());
@@ -100,6 +98,11 @@ TaskQueueManager::~TaskQueueManager() {
delegate_->RemoveNestingObserver(this);
}
+TaskQueueManager::AnyThread::AnyThread()
+ : do_work_running_count(0),
+ immediate_do_work_posted_count(0),
+ is_nested(false) {}
+
void TaskQueueManager::RegisterTimeDomain(TimeDomain* time_domain) {
time_domains_.insert(time_domain);
time_domain->OnRegisterWithTaskQueueManager(this);
@@ -145,45 +148,72 @@ void TaskQueueManager::UnregisterTaskQueue(
queues_.erase(task_queue);
selector_.RemoveQueue(task_queue.get());
-}
-void TaskQueueManager::UpdateWorkQueues(LazyNow lazy_now) {
- TRACE_EVENT0(disabled_by_default_tracing_category_,
- "TaskQueueManager::UpdateWorkQueues");
+ {
+ base::AutoLock lock(any_thread_lock_);
+ any_thread().has_incoming_immediate_work.erase(task_queue.get());
+ }
+}
+void TaskQueueManager::UpdateWorkQueues(LazyNow* lazy_now) {
for (TimeDomain* time_domain : time_domains_) {
- LazyNow lazy_now_in_domain = time_domain == real_time_domain_.get()
- ? lazy_now
- : time_domain->CreateLazyNow();
- time_domain->UpdateWorkQueues(lazy_now_in_domain);
+ if (time_domain == real_time_domain_.get()) {
+ time_domain->WakeupReadyDelayedQueues(lazy_now);
+ continue;
+ }
+ LazyNow time_domain_lazy_now = time_domain->CreateLazyNow();
+ time_domain->WakeupReadyDelayedQueues(&time_domain_lazy_now);
}
}
void TaskQueueManager::OnBeginNestedMessageLoop() {
// We just entered a nested message loop, make sure there's a DoWork posted or
// the system will grind to a halt.
- delegate_->PostTask(FROM_HERE, from_main_thread_immediate_do_work_closure_);
+ {
+ base::AutoLock lock(any_thread_lock_);
+ any_thread().immediate_do_work_posted_count++;
+ any_thread().is_nested = true;
+ }
+ delegate_->PostTask(FROM_HERE, immediate_do_work_closure_);
+}
+
+void TaskQueueManager::OnQueueHasImmediateWork(internal::TaskQueueImpl* queue,
+ bool ensure_do_work_posted) {
+ MoveableAutoLock lock(any_thread_lock_);
+ any_thread().has_incoming_immediate_work.insert(queue);
+ if (ensure_do_work_posted)
+ MaybeScheduleImmediateWorkLocked(FROM_HERE, std::move(lock));
+}
+
+void TaskQueueManager::NotifyQueuesOfIncomingImmediateWorkOnMainThreadLocked() {
+ for (internal::TaskQueueImpl* queue :
+ any_thread().has_incoming_immediate_work) {
+ queue->ReloadImmediateWorkQueueIfEmpty();
+ }
+ any_thread().has_incoming_immediate_work.clear();
}
void TaskQueueManager::MaybeScheduleImmediateWork(
const tracked_objects::Location& from_here) {
- bool on_main_thread = delegate_->BelongsToCurrentThread();
- // De-duplicate DoWork posts.
- if (on_main_thread) {
- if (!main_thread_pending_wakeups_.insert(base::TimeTicks()).second) {
+ MoveableAutoLock lock(any_thread_lock_);
+ MaybeScheduleImmediateWorkLocked(from_here, std::move(lock));
+}
+
+void TaskQueueManager::MaybeScheduleImmediateWorkLocked(
+ const tracked_objects::Location& from_here,
+ MoveableAutoLock&& lock) {
+ {
+ MoveableAutoLock auto_lock(std::move(lock));
+ // Unless we're nested, try to avoid posting redundant DoWorks.
+ if (!any_thread().is_nested &&
+ (any_thread().do_work_running_count == 1 ||
+ any_thread().immediate_do_work_posted_count > 0)) {
return;
}
- delegate_->PostTask(from_here, from_main_thread_immediate_do_work_closure_);
- } else {
- {
- base::AutoLock lock(other_thread_lock_);
- if (other_thread_pending_wakeup_)
- return;
- other_thread_pending_wakeup_ = true;
- }
- delegate_->PostTask(from_here,
- from_other_thread_immediate_do_work_closure_);
+
+ any_thread().immediate_do_work_posted_count++;
}
+ delegate_->PostTask(from_here, immediate_do_work_closure_);
}
void TaskQueueManager::MaybeScheduleDelayedWork(
@@ -192,54 +222,69 @@ void TaskQueueManager::MaybeScheduleDelayedWork(
base::TimeDelta delay) {
DCHECK(main_thread_checker_.CalledOnValidThread());
DCHECK_GE(delay, base::TimeDelta());
+ {
+ base::AutoLock lock(any_thread_lock_);
- // If there's a pending immediate DoWork then we rely on
- // TryAdvanceTimeDomains getting the TimeDomain to call
- // MaybeScheduleDelayedWork again when the immediate DoWork is complete.
- if (main_thread_pending_wakeups_.find(base::TimeTicks()) !=
- main_thread_pending_wakeups_.end()) {
- return;
+ // If there's a pending immediate DoWork then we rely on the logic in DoWork
+ // to post a continuation as needed.
+ if (any_thread().immediate_do_work_posted_count > 0)
+ return;
+
+ // If a non-nested DoWork is running we can also rely on the logic in DoWork
+ // to post a continuation as needed.
+ if (any_thread().do_work_running_count == 1 && !any_thread().is_nested)
+ return;
}
+
// De-duplicate DoWork posts.
base::TimeTicks run_time = now + delay;
- if (!main_thread_pending_wakeups_.empty() &&
- *main_thread_pending_wakeups_.begin() <= run_time) {
+ if (next_delayed_do_work_ <= run_time && !next_delayed_do_work_.is_null())
return;
- }
- main_thread_pending_wakeups_.insert(run_time);
+
+ TRACE_EVENT1("tracing_category_", "MaybeScheduleDelayedWorkInternal", "delay",
Sami 2016/12/09 11:04:02 Probably did not mean to add quotes around tracing
alex clarke (OOO till 29th) 2016/12/12 11:45:04 Done.
+ delay.InSecondsF());
Sami 2016/12/09 11:04:02 nit: Could you do InMillisecondsF() and "delay_ms"
alex clarke (OOO till 29th) 2016/12/12 11:45:04 Done.
+
+ cancelable_delayed_do_work_closure_.Reset(delayed_do_work_closure_);
+ next_delayed_do_work_ = run_time;
delegate_->PostDelayedTask(
- from_here, base::Bind(&TaskQueueManager::DoWork,
- weak_factory_.GetWeakPtr(), run_time, true),
- delay);
+ from_here, cancelable_delayed_do_work_closure_.callback(), delay);
}
-void TaskQueueManager::DoWork(base::TimeTicks run_time, bool from_main_thread) {
+void TaskQueueManager::DoWork(bool delayed) {
DCHECK(main_thread_checker_.CalledOnValidThread());
- TRACE_EVENT1(tracing_category_, "TaskQueueManager::DoWork",
- "from_main_thread", from_main_thread);
+ TRACE_EVENT1("tracing_category_", "TaskQueueManager::DoWork", "delayed",
+ delayed);
+ LazyNow lazy_now(real_time_domain()->CreateLazyNow());
- if (from_main_thread) {
- main_thread_pending_wakeups_.erase(run_time);
- } else {
- base::AutoLock lock(other_thread_lock_);
- other_thread_pending_wakeup_ = false;
- }
+ bool is_nested = delegate_->IsNested();
+ if (!is_nested)
+ queues_to_delete_.clear();
- // Posting a DoWork while a DoWork is running leads to spurious DoWorks.
- main_thread_pending_wakeups_.insert(base::TimeTicks());
+ for (int i = 0; i < work_batch_size_; i++) {
+ {
+ base::AutoLock lock(any_thread_lock_);
+ any_thread().is_nested = is_nested;
+ DCHECK_EQ(any_thread().is_nested, delegate_->IsNested());
- if (!delegate_->IsNested())
- queues_to_delete_.clear();
+ if (i == 0) {
+ any_thread().do_work_running_count++;
- LazyNow lazy_now(real_time_domain()->CreateLazyNow());
- UpdateWorkQueues(lazy_now);
+ if (!delayed) {
+ any_thread().immediate_do_work_posted_count--;
+ DCHECK_GE(any_thread().immediate_do_work_posted_count, 0);
+ }
+ }
- for (int i = 0; i < work_batch_size_; i++) {
- internal::WorkQueue* work_queue;
+ NotifyQueuesOfIncomingImmediateWorkOnMainThreadLocked();
+ }
+
+ UpdateWorkQueues(&lazy_now);
+
+ internal::WorkQueue* work_queue = nullptr;
if (!SelectWorkQueueToService(&work_queue))
break;
- switch (ProcessTaskFromWorkQueue(work_queue, &lazy_now)) {
+ switch (ProcessTaskFromWorkQueue(work_queue, is_nested, &lazy_now)) {
case ProcessTaskResult::DEFERRED:
// If a task was deferred, try again with another task.
continue;
@@ -251,29 +296,108 @@ void TaskQueueManager::DoWork(base::TimeTicks run_time, bool from_main_thread) {
work_queue = nullptr; // The queue may have been unregistered.
Sami 2016/12/09 11:04:02 Looks like this line could be removed now (even be
alex clarke (OOO till 29th) 2016/12/12 11:45:04 Done.
- UpdateWorkQueues(lazy_now);
-
// Only run a single task per batch in nested run loops so that we can
// properly exit the nested loop when someone calls RunLoop::Quit().
- if (delegate_->IsNested())
+ if (is_nested)
break;
}
- main_thread_pending_wakeups_.erase(base::TimeTicks());
-
// TODO(alexclarke): Consider refactoring the above loop to terminate only
// when there's no more work left to be done, rather than posting a
// continuation task.
- if (!selector_.EnabledWorkQueuesEmpty() || TryAdvanceTimeDomains())
- MaybeScheduleImmediateWork(FROM_HERE);
+ if (delayed)
+ next_delayed_do_work_ = base::TimeTicks();
+
+ {
+ MoveableAutoLock lock(any_thread_lock_);
+ base::Optional<base::TimeDelta> next_delay =
+ ComputeDelayTillNextTaskLocked(&lazy_now);
+
+ any_thread().do_work_running_count--;
+ DCHECK_GE(any_thread().do_work_running_count, 0);
+
+ any_thread().is_nested = is_nested;
+ DCHECK_EQ(any_thread().is_nested, delegate_->IsNested());
+
+ PostDoWorkContinuationLocked(next_delay, &lazy_now, std::move(lock));
+ }
+}
+
+void TaskQueueManager::PostDoWorkContinuationLocked(
+ base::Optional<base::TimeDelta> next_delay,
+ LazyNow* lazy_now,
+ MoveableAutoLock&& lock) {
+ base::TimeDelta delay;
+
+ {
+ MoveableAutoLock auto_lock(std::move(lock));
+
+ // If there are no tasks left then we don't need to post a continuation.
+ if (!next_delay) {
+ // If there's a pending delayed DoWork, cancel it because it's not needed.
+ if (!next_delayed_do_work_.is_null()) {
+ next_delayed_do_work_ = base::TimeTicks();
+ cancelable_delayed_do_work_closure_.Cancel();
+ }
+ return;
+ }
+
+ // If an immediate DoWork is posted, we don't need to post a continuation.
+ if (any_thread().immediate_do_work_posted_count > 0)
+ return;
+
+ delay = next_delay.value();
+ if (delay.is_zero()) {
+ // If a delayed DoWork is pending then we don't need to post a
Sami 2016/12/09 11:04:02 nit: reflow
alex clarke (OOO till 29th) 2016/12/12 11:45:04 Done.
+ // continuation
+ // because it should run immediately.
+ if (!next_delayed_do_work_.is_null() &&
+ next_delayed_do_work_ <= lazy_now->Now()) {
+ return;
+ }
+
+ any_thread().immediate_do_work_posted_count++;
+ } else {
+ base::TimeTicks run_time = lazy_now->Now() + delay;
+ if (next_delayed_do_work_ == run_time)
+ return;
+
+ cancelable_delayed_do_work_closure_.Reset(delayed_do_work_closure_);
+ next_delayed_do_work_ = run_time;
+ }
+ }
+
+ // We avoid holding |any_thread_lock_| while posting the task.
+ if (delay.is_zero()) {
+ delegate_->PostTask(FROM_HERE, immediate_do_work_closure_);
+ } else {
+ delegate_->PostDelayedTask(
+ FROM_HERE, cancelable_delayed_do_work_closure_.callback(), delay);
+ }
}
-bool TaskQueueManager::TryAdvanceTimeDomains() {
- bool can_advance = false;
+base::Optional<base::TimeDelta>
+TaskQueueManager::ComputeDelayTillNextTaskLocked(LazyNow* lazy_now) {
+ NotifyQueuesOfIncomingImmediateWorkOnMainThreadLocked();
+
+ // If the selector has non-empty queues we trivially know there is immediate
+ // word to be done.
+ if (!selector_.EnabledWorkQueuesEmpty())
+ return base::TimeDelta();
+
+ UpdateWorkQueues(lazy_now);
+
+ // Otherwise we need to find the shortest delay, if any.
+ base::Optional<base::TimeDelta> next_continuation;
for (TimeDomain* time_domain : time_domains_) {
- can_advance |= time_domain->MaybeAdvanceTime();
+ base::Optional<base::TimeDelta> continuation =
+ time_domain->DelayTillNextTask(lazy_now);
+ if (!continuation)
+ continue;
+ if (!next_continuation || next_continuation.value() > continuation.value())
+ next_continuation = continuation;
}
- return can_advance;
+ return next_continuation;
}
bool TaskQueueManager::SelectWorkQueueToService(
@@ -292,6 +416,7 @@ void TaskQueueManager::DidQueueTask(
TaskQueueManager::ProcessTaskResult TaskQueueManager::ProcessTaskFromWorkQueue(
internal::WorkQueue* work_queue,
+ bool is_nested,
LazyNow* lazy_now) {
DCHECK(main_thread_checker_.CalledOnValidThread());
scoped_refptr<DeletionSentinel> protect(deletion_sentinel_);
@@ -306,7 +431,7 @@ TaskQueueManager::ProcessTaskResult TaskQueueManager::ProcessTaskFromWorkQueue(
if (queue->GetQuiescenceMonitored())
task_was_run_on_quiescence_monitored_queue_ = true;
- if (!pending_task.nestable && delegate_->IsNested()) {
+ if (!pending_task.nestable && is_nested) {
// Defer non-nestable work to the main task runner. NOTE these tasks can be
// arbitrarily delayed so the additional delay should not be a problem.
// TODO(skyostil): Figure out a way to not forget which task queue the
@@ -319,7 +444,8 @@ TaskQueueManager::ProcessTaskResult TaskQueueManager::ProcessTaskFromWorkQueue(
return ProcessTaskResult::DEFERRED;
}
- MaybeRecordTaskDelayHistograms(pending_task, queue);
+ if (record_task_delay_histograms_)
+ MaybeRecordTaskDelayHistograms(pending_task, queue);
double task_start_time = 0;
TRACE_TASK_EXECUTION("TaskQueueManager::ProcessTaskFromWorkQueue",
@@ -470,6 +596,14 @@ TaskQueueManager::AsValueWithSelectorResult(
for (auto* time_domain : time_domains_)
time_domain->AsValueInto(state.get());
state->EndArray();
+
+ {
+ base::AutoLock lock(any_thread_lock_);
+ state->SetBoolean("is_nested", any_thread().is_nested);
+ state->SetInteger("do_work_count", any_thread().do_work_running_count);
+ state->SetInteger("immediate_do_work_posted",
Sami 2016/12/09 11:04:02 nit: _count
alex clarke (OOO till 29th) 2016/12/12 11:45:04 Done.
+ any_thread().immediate_do_work_posted_count);
+ }
return std::move(state);
}
@@ -494,5 +628,11 @@ bool TaskQueueManager::HasImmediateWorkForTesting() const {
return !selector_.EnabledWorkQueuesEmpty();
}
+void TaskQueueManager::SetRecordTaskDelayHistograms(
+ bool record_task_delay_histograms) {
+ DCHECK(main_thread_checker_.CalledOnValidThread());
+ record_task_delay_histograms_ = record_task_delay_histograms;
+}
+
} // namespace scheduler
} // namespace blink

Powered by Google App Engine
This is Rietveld 408576698