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

Unified Diff: components/scheduler/child/idle_helper.cc

Issue 1151353003: [scheduler]: Avoid waking up the scheduler to end long idle periods. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@end_idle_sync_2
Patch Set: Fix Win for realz hopefully... Created 5 years, 7 months 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
« no previous file with comments | « components/scheduler/child/idle_helper.h ('k') | components/scheduler/child/idle_helper_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: components/scheduler/child/idle_helper.cc
diff --git a/components/scheduler/child/idle_helper.cc b/components/scheduler/child/idle_helper.cc
index 54939d23c6c1804b327a7d90aabc9cfa7d116e6e..8d8580b72eed7e131a558cc0ddcbbf2f4902f6c3 100644
--- a/components/scheduler/child/idle_helper.cc
+++ b/components/scheduler/child/idle_helper.cc
@@ -4,6 +4,7 @@
#include "components/scheduler/child/idle_helper.h"
+#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
#include "base/trace_event/trace_event_argument.h"
#include "components/scheduler/child/scheduler_helper.h"
@@ -21,38 +22,37 @@ IdleHelper::IdleHelper(
: helper_(helper),
delegate_(delegate),
idle_queue_index_(idle_queue_index),
- idle_period_state_(IdlePeriodState::NOT_IN_IDLE_PERIOD),
+ state_(helper,
+ tracing_category,
+ disabled_by_default_tracing_category,
+ idle_period_tracing_name),
quiescence_monitored_task_queue_mask_(
helper_->GetQuiescenceMonitoredTaskQueueMask() &
~(1ull << idle_queue_index_)),
required_quiescence_duration_before_long_idle_period_(
required_quiescence_duration_before_long_idle_period),
- tracing_category_(tracing_category),
disabled_by_default_tracing_category_(
disabled_by_default_tracing_category),
- idle_period_tracing_name_(idle_period_tracing_name),
weak_factory_(this) {
weak_idle_helper_ptr_ = weak_factory_.GetWeakPtr();
- end_idle_period_closure_.Reset(
- base::Bind(&IdleHelper::EndIdlePeriod, weak_idle_helper_ptr_));
enable_next_long_idle_period_closure_.Reset(
base::Bind(&IdleHelper::EnableLongIdlePeriod, weak_idle_helper_ptr_));
- enable_next_long_idle_period_after_wakeup_closure_.Reset(base::Bind(
- &IdleHelper::EnableLongIdlePeriodAfterWakeup, weak_idle_helper_ptr_));
+ on_idle_task_posted_closure_.Reset(base::Bind(
+ &IdleHelper::OnIdleTaskPostedOnMainThread, weak_idle_helper_ptr_));
idle_task_runner_ = make_scoped_refptr(new SingleThreadIdleTaskRunner(
helper_->TaskRunnerForQueue(idle_queue_index_),
- helper_->ControlAfterWakeUpTaskRunner(),
- base::Bind(&IdleHelper::CurrentIdleTaskDeadlineCallback,
- weak_idle_helper_ptr_),
- tracing_category));
+ helper_->ControlAfterWakeUpTaskRunner(), this, tracing_category));
helper_->DisableQueue(idle_queue_index_);
helper_->SetPumpPolicy(idle_queue_index_,
TaskQueueManager::PumpPolicy::MANUAL);
+
+ helper_->AddTaskObserver(this);
}
IdleHelper::~IdleHelper() {
+ helper_->RemoveTaskObserver(this);
}
IdleHelper::Delegate::Delegate() {
@@ -66,12 +66,6 @@ scoped_refptr<SingleThreadIdleTaskRunner> IdleHelper::IdleTaskRunner() {
return idle_task_runner_;
}
-void IdleHelper::CurrentIdleTaskDeadlineCallback(
- base::TimeTicks* deadline_out) const {
- helper_->CheckOnValidThread();
- *deadline_out = idle_period_deadline_;
-}
-
IdleHelper::IdlePeriodState IdleHelper::ComputeNewLongIdlePeriodState(
const base::TimeTicks now,
base::TimeDelta* next_long_idle_period_delay_out) {
@@ -97,9 +91,13 @@ IdleHelper::IdlePeriodState IdleHelper::ComputeNewLongIdlePeriodState(
if (long_idle_period_duration > base::TimeDelta()) {
*next_long_idle_period_delay_out = long_idle_period_duration;
- return long_idle_period_duration == max_long_idle_period_duration
- ? IdlePeriodState::IN_LONG_IDLE_PERIOD_WITH_MAX_DEADLINE
- : IdlePeriodState::IN_LONG_IDLE_PERIOD;
+ if (helper_->IsQueueEmpty(idle_queue_index_)) {
+ return IdlePeriodState::IN_LONG_IDLE_PERIOD_PAUSED;
+ } else if (long_idle_period_duration == max_long_idle_period_duration) {
+ return IdlePeriodState::IN_LONG_IDLE_PERIOD_WITH_MAX_DEADLINE;
+ } else {
+ return IdlePeriodState::IN_LONG_IDLE_PERIOD;
+ }
} else {
// If we can't start the idle period yet then try again after wakeup.
*next_long_idle_period_delay_out = base::TimeDelta::FromMilliseconds(
@@ -134,6 +132,8 @@ bool IdleHelper::ShouldWaitForQuiescence() {
void IdleHelper::EnableLongIdlePeriod() {
TRACE_EVENT0(disabled_by_default_tracing_category_, "EnableLongIdlePeriod");
helper_->CheckOnValidThread();
+ if (helper_->IsShutdown())
+ return;
// End any previous idle period.
EndIdlePeriod();
@@ -152,98 +152,140 @@ void IdleHelper::EnableLongIdlePeriod() {
ComputeNewLongIdlePeriodState(now, &next_long_idle_period_delay);
if (IsInIdlePeriod(new_idle_period_state)) {
StartIdlePeriod(new_idle_period_state, now,
- now + next_long_idle_period_delay, false);
- }
-
- if (helper_->IsQueueEmpty(idle_queue_index_)) {
- // If there are no current idle tasks then post the call to initiate the
- // next idle for execution after wakeup (at which point after-wakeup idle
- // tasks might be eligible to run or more idle tasks posted).
- helper_->ControlAfterWakeUpTaskRunner()->PostDelayedTask(
- FROM_HERE,
- enable_next_long_idle_period_after_wakeup_closure_.callback(),
- next_long_idle_period_delay);
+ now + next_long_idle_period_delay);
} else {
- // Otherwise post on the normal control task queue.
+ // Otherwise wait for the next long idle period delay before trying again.
helper_->ControlTaskRunner()->PostDelayedTask(
FROM_HERE, enable_next_long_idle_period_closure_.callback(),
next_long_idle_period_delay);
}
}
-void IdleHelper::EnableLongIdlePeriodAfterWakeup() {
- TRACE_EVENT0(disabled_by_default_tracing_category_,
- "EnableLongIdlePeriodAfterWakeup");
- helper_->CheckOnValidThread();
-
- if (IsInIdlePeriod(idle_period_state_)) {
- // Since we were asleep until now, end the async idle period trace event at
- // the time when it would have ended were we awake.
- TRACE_EVENT_ASYNC_END_WITH_TIMESTAMP0(
- tracing_category_, idle_period_tracing_name_, this,
- std::min(idle_period_deadline_, helper_->Now()).ToInternalValue());
- idle_period_state_ = IdlePeriodState::ENDING_LONG_IDLE_PERIOD;
- EndIdlePeriod();
- }
-
- // Post a task to initiate the next long idle period rather than calling it
- // directly to allow all pending PostIdleTaskAfterWakeup tasks to get enqueued
- // on the idle task queue before the next idle period starts so they are
- // eligible to be run during the new idle period.
- helper_->ControlTaskRunner()->PostTask(
- FROM_HERE, enable_next_long_idle_period_closure_.callback());
-}
-
void IdleHelper::StartIdlePeriod(IdlePeriodState new_state,
base::TimeTicks now,
- base::TimeTicks idle_period_deadline,
- bool post_end_idle_period) {
+ base::TimeTicks idle_period_deadline) {
DCHECK_GT(idle_period_deadline, now);
- TRACE_EVENT_ASYNC_BEGIN0(tracing_category_, idle_period_tracing_name_, this);
helper_->CheckOnValidThread();
DCHECK(IsInIdlePeriod(new_state));
+ TRACE_EVENT0(disabled_by_default_tracing_category_, "StartIdlePeriod");
helper_->EnableQueue(idle_queue_index_,
PrioritizingTaskQueueSelector::BEST_EFFORT_PRIORITY);
helper_->PumpQueue(idle_queue_index_);
- idle_period_state_ = new_state;
- idle_period_deadline_ = idle_period_deadline;
- if (post_end_idle_period) {
- helper_->ControlTaskRunner()->PostDelayedTask(
- FROM_HERE, end_idle_period_closure_.callback(),
- idle_period_deadline_ - now);
- }
+ state_.UpdateState(new_state, idle_period_deadline, now);
}
void IdleHelper::EndIdlePeriod() {
helper_->CheckOnValidThread();
+ TRACE_EVENT0(disabled_by_default_tracing_category_, "EndIdlePeriod");
- end_idle_period_closure_.Cancel();
enable_next_long_idle_period_closure_.Cancel();
- enable_next_long_idle_period_after_wakeup_closure_.Cancel();
+ on_idle_task_posted_closure_.Cancel();
// If we weren't already within an idle period then early-out.
- if (!IsInIdlePeriod(idle_period_state_))
+ if (!IsInIdlePeriod(state_.idle_period_state()))
return;
- // If we are in the ENDING_LONG_IDLE_PERIOD state we have already logged the
- // trace event.
- if (idle_period_state_ != IdlePeriodState::ENDING_LONG_IDLE_PERIOD) {
- bool is_tracing;
- TRACE_EVENT_CATEGORY_GROUP_ENABLED(tracing_category_, &is_tracing);
- if (is_tracing && !idle_period_deadline_.is_null() &&
- helper_->Now() > idle_period_deadline_) {
- TRACE_EVENT_ASYNC_STEP_INTO_WITH_TIMESTAMP0(
- tracing_category_, idle_period_tracing_name_, this, "DeadlineOverrun",
- idle_period_deadline_.ToInternalValue());
+ helper_->DisableQueue(idle_queue_index_);
+ state_.UpdateState(IdlePeriodState::NOT_IN_IDLE_PERIOD, base::TimeTicks(),
+ base::TimeTicks());
+}
+
+void IdleHelper::WillProcessTask(const base::PendingTask& pending_task) {
+}
+
+void IdleHelper::DidProcessTask(const base::PendingTask& pending_task) {
+ helper_->CheckOnValidThread();
+ TRACE_EVENT0(disabled_by_default_tracing_category_, "DidProcessTask");
+ if (IsInIdlePeriod(state_.idle_period_state()) &&
+ state_.idle_period_state() !=
+ IdlePeriodState::IN_LONG_IDLE_PERIOD_PAUSED &&
+ helper_->Now() >= state_.idle_period_deadline()) {
+ // If the idle period deadline has now been reached, either end the idle
+ // period or trigger a new long-idle period.
+ if (IsInLongIdlePeriod(state_.idle_period_state())) {
+ EnableLongIdlePeriod();
+ } else {
+ DCHECK(IdlePeriodState::IN_SHORT_IDLE_PERIOD ==
+ state_.idle_period_state());
+ EndIdlePeriod();
}
- TRACE_EVENT_ASYNC_END0(tracing_category_, idle_period_tracing_name_, this);
}
+}
- helper_->DisableQueue(idle_queue_index_);
- idle_period_state_ = IdlePeriodState::NOT_IN_IDLE_PERIOD;
- idle_period_deadline_ = base::TimeTicks();
+void IdleHelper::UpdateLongIdlePeriodStateAfterIdleTask() {
+ helper_->CheckOnValidThread();
+ DCHECK(IsInLongIdlePeriod(state_.idle_period_state()));
+ TRACE_EVENT0(disabled_by_default_tracing_category_,
+ "UpdateLongIdlePeriodStateAfterIdleTask");
+ TaskQueueManager::QueueState queue_state =
+ helper_->GetQueueState(idle_queue_index_);
+ if (queue_state == TaskQueueManager::QueueState::EMPTY) {
+ // If there are no more idle tasks then pause long idle period ticks until a
+ // new idle task is posted.
+ state_.UpdateState(IdlePeriodState::IN_LONG_IDLE_PERIOD_PAUSED,
+ state_.idle_period_deadline(), base::TimeTicks());
+ } else if (queue_state == TaskQueueManager::QueueState::NEEDS_PUMPING) {
+ // If there is still idle work to do then just start the next idle period.
+ base::TimeDelta next_long_idle_period_delay;
+ if (state_.idle_period_state() ==
+ IdlePeriodState::IN_LONG_IDLE_PERIOD_WITH_MAX_DEADLINE) {
+ // If we are in a max deadline long idle period then start the next
+ // idle period immediately.
+ next_long_idle_period_delay = base::TimeDelta();
+ } else {
+ // Otherwise ensure that we kick the scheduler at the right time to
+ // initiate the next idle period.
+ next_long_idle_period_delay = std::max(
+ base::TimeDelta(), state_.idle_period_deadline() - helper_->Now());
+ }
+ helper_->ControlTaskRunner()->PostDelayedTask(
+ FROM_HERE, enable_next_long_idle_period_closure_.callback(),
+ next_long_idle_period_delay);
+ }
+}
+
+base::TimeTicks IdleHelper::CurrentIdleTaskDeadline() const {
+ helper_->CheckOnValidThread();
+ return state_.idle_period_deadline();
+}
+
+void IdleHelper::OnIdleTaskPosted() {
+ TRACE_EVENT0(disabled_by_default_tracing_category_, "OnIdleTaskPosted");
+ if (idle_task_runner_->RunsTasksOnCurrentThread()) {
+ OnIdleTaskPostedOnMainThread();
+ } else {
+ helper_->ControlTaskRunner()->PostTask(
+ FROM_HERE, on_idle_task_posted_closure_.callback());
+ }
+}
+
+void IdleHelper::OnIdleTaskPostedOnMainThread() {
+ TRACE_EVENT0(disabled_by_default_tracing_category_,
+ "OnIdleTaskPostedOnMainThread");
+ if (state_.idle_period_state() ==
+ IdlePeriodState::IN_LONG_IDLE_PERIOD_PAUSED) {
+ // Restart long idle period ticks.
+ helper_->ControlTaskRunner()->PostTask(
+ FROM_HERE, enable_next_long_idle_period_closure_.callback());
+ }
+}
+
+base::TimeTicks IdleHelper::WillProcessIdleTask() {
+ helper_->CheckOnValidThread();
+ DCHECK(IsInIdlePeriod(state_.idle_period_state()));
+
+ state_.TraceIdleIdleTaskStart();
+ return CurrentIdleTaskDeadline();
+}
+
+void IdleHelper::DidProcessIdleTask() {
+ helper_->CheckOnValidThread();
+ state_.TraceIdleIdleTaskEnd();
+ if (IsInLongIdlePeriod(state_.idle_period_state())) {
+ UpdateLongIdlePeriodStateAfterIdleTask();
+ }
}
// static
@@ -251,17 +293,157 @@ bool IdleHelper::IsInIdlePeriod(IdlePeriodState state) {
return state != IdlePeriodState::NOT_IN_IDLE_PERIOD;
}
+// static
+bool IdleHelper::IsInLongIdlePeriod(IdlePeriodState state) {
+ return state == IdlePeriodState::IN_LONG_IDLE_PERIOD ||
+ state == IdlePeriodState::IN_LONG_IDLE_PERIOD_WITH_MAX_DEADLINE ||
+ state == IdlePeriodState::IN_LONG_IDLE_PERIOD_PAUSED;
+}
+
bool IdleHelper::CanExceedIdleDeadlineIfRequired() const {
- TRACE_EVENT0(tracing_category_, "CanExceedIdleDeadlineIfRequired");
+ TRACE_EVENT0(disabled_by_default_tracing_category_,
+ "CanExceedIdleDeadlineIfRequired");
helper_->CheckOnValidThread();
- return idle_period_state_ ==
+ return state_.idle_period_state() ==
IdlePeriodState::IN_LONG_IDLE_PERIOD_WITH_MAX_DEADLINE;
}
IdleHelper::IdlePeriodState IdleHelper::SchedulerIdlePeriodState() const {
+ return state_.idle_period_state();
+}
+
+IdleHelper::State::State(SchedulerHelper* helper,
+ const char* tracing_category,
+ const char* disabled_by_default_tracing_category,
+ const char* idle_period_tracing_name)
+ : helper_(helper),
+ idle_period_state_(IdlePeriodState::NOT_IN_IDLE_PERIOD),
+ nestable_events_started_(false),
+ tracing_category_(tracing_category),
+ disabled_by_default_tracing_category_(
+ disabled_by_default_tracing_category),
+ idle_period_tracing_name_(idle_period_tracing_name) {
+}
+
+IdleHelper::State::~State() {
+}
+
+IdleHelper::IdlePeriodState IdleHelper::State::idle_period_state() const {
+ helper_->CheckOnValidThread();
return idle_period_state_;
}
+base::TimeTicks IdleHelper::State::idle_period_deadline() const {
+ helper_->CheckOnValidThread();
+ return idle_period_deadline_;
+}
+
+void IdleHelper::State::UpdateState(IdlePeriodState new_state,
+ base::TimeTicks new_deadline,
+ base::TimeTicks optional_now) {
+ helper_->CheckOnValidThread();
+ if (new_state == idle_period_state_) {
+ DCHECK_EQ(new_deadline, idle_period_deadline_);
+ return;
+ }
+
+ bool is_tracing;
+ TRACE_EVENT_CATEGORY_GROUP_ENABLED(tracing_category_, &is_tracing);
+ if (is_tracing) {
+ base::TimeTicks now(optional_now.is_null() ? helper_->Now() : optional_now);
+ TraceEventIdlePeriodStateChange(new_state, new_deadline, now);
+ idle_period_deadline_for_tracing_ =
+ base::TraceTicks::Now() + (new_deadline - now);
+ }
+
+ idle_period_state_ = new_state;
+ idle_period_deadline_ = new_deadline;
+}
+
+void IdleHelper::State::TraceIdleIdleTaskStart() {
+ helper_->CheckOnValidThread();
+
+ bool is_tracing;
+ TRACE_EVENT_CATEGORY_GROUP_ENABLED(tracing_category_, &is_tracing);
+ if (is_tracing && nestable_events_started_) {
+ last_idle_task_trace_time_ = base::TraceTicks::Now();
+ TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0(
+ tracing_category_, "RunningIdleTask", this,
+ last_idle_task_trace_time_.ToInternalValue());
+ }
+}
+
+void IdleHelper::State::TraceIdleIdleTaskEnd() {
+ helper_->CheckOnValidThread();
+
+ bool is_tracing;
+ TRACE_EVENT_CATEGORY_GROUP_ENABLED(tracing_category_, &is_tracing);
+ if (is_tracing && nestable_events_started_) {
+ if (!idle_period_deadline_for_tracing_.is_null() &&
+ base::TraceTicks::Now() > idle_period_deadline_for_tracing_) {
+ TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0(
+ tracing_category_, "DeadlineOverrun", this,
+ std::max(idle_period_deadline_for_tracing_,
+ last_idle_task_trace_time_).ToInternalValue());
+ TRACE_EVENT_NESTABLE_ASYNC_END0(tracing_category_, "DeadlineOverrun",
+ this);
+ }
+ TRACE_EVENT_NESTABLE_ASYNC_END0(tracing_category_, "RunningIdleTask", this);
+ }
+}
+
+void IdleHelper::State::TraceEventIdlePeriodStateChange(
+ IdlePeriodState new_state,
+ base::TimeTicks new_deadline,
+ base::TimeTicks now) {
+ TRACE_EVENT2(disabled_by_default_tracing_category_, "SetIdlePeriodState",
+ "old_state",
+ IdleHelper::IdlePeriodStateToString(idle_period_state_),
+ "new_state", IdleHelper::IdlePeriodStateToString(new_state));
+ if (nestable_events_started_) {
+ // End async tracing events for the state we are leaving.
+ if (idle_period_state_ == IdlePeriodState::IN_LONG_IDLE_PERIOD_PAUSED) {
+ TRACE_EVENT_NESTABLE_ASYNC_END0(tracing_category_, "LongIdlePeriodPaused",
+ this);
+ }
+ if (IsInLongIdlePeriod(idle_period_state_) &&
+ !IsInLongIdlePeriod(new_state)) {
+ TRACE_EVENT_NESTABLE_ASYNC_END0(tracing_category_, "LongIdlePeriod",
+ this);
+ }
+ if (idle_period_state_ == IdlePeriodState::IN_SHORT_IDLE_PERIOD) {
+ TRACE_EVENT_NESTABLE_ASYNC_END0(tracing_category_, "ShortIdlePeriod",
+ this);
+ }
+ if (IsInIdlePeriod(idle_period_state_) && !IsInIdlePeriod(new_state)) {
+ TRACE_EVENT_NESTABLE_ASYNC_END0(tracing_category_,
+ idle_period_tracing_name_, this);
+ nestable_events_started_ = false;
+ }
+ }
+
+ // Start async tracing events for the state we are entering.
+ if (IsInIdlePeriod(new_state) && !IsInIdlePeriod(idle_period_state_)) {
+ nestable_events_started_ = true;
+ TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(
+ tracing_category_, idle_period_tracing_name_, this,
+ "idle_period_length_ms", (new_deadline - now).ToInternalValue());
+ }
+ if (new_state == IdlePeriodState::IN_SHORT_IDLE_PERIOD) {
+ TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(tracing_category_, "ShortIdlePeriod",
+ this);
+ }
+ if (IsInLongIdlePeriod(new_state) &&
+ !IsInLongIdlePeriod(idle_period_state_)) {
+ TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(tracing_category_, "LongIdlePeriod",
+ this);
+ }
+ if (new_state == IdlePeriodState::IN_LONG_IDLE_PERIOD_PAUSED) {
+ TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(tracing_category_, "LongIdlePeriodPaused",
+ this);
+ }
+}
+
// static
const char* IdleHelper::IdlePeriodStateToString(
IdlePeriodState idle_period_state) {
@@ -274,8 +456,8 @@ const char* IdleHelper::IdlePeriodStateToString(
return "in_long_idle_period";
case IdlePeriodState::IN_LONG_IDLE_PERIOD_WITH_MAX_DEADLINE:
return "in_long_idle_period_with_max_deadline";
- case IdlePeriodState::ENDING_LONG_IDLE_PERIOD:
- return "ending_long_idle_period";
+ case IdlePeriodState::IN_LONG_IDLE_PERIOD_PAUSED:
+ return "in_long_idle_period_paused";
default:
NOTREACHED();
return nullptr;
« no previous file with comments | « components/scheduler/child/idle_helper.h ('k') | components/scheduler/child/idle_helper_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698