Index: cc/scheduler/delay_based_time_source.cc |
=================================================================== |
--- cc/scheduler/delay_based_time_source.cc (revision 226578) |
+++ cc/scheduler/delay_based_time_source.cc (working copy) |
@@ -17,10 +17,10 @@ |
namespace { |
-// kDoubleTickDivisor prevents ticks from running within the specified |
+// kDoubleTickThreshold prevents ticks from running within the specified |
// fraction of an interval. This helps account for jitter in the timebase as |
// well as quick timer reactivation. |
-static const int kDoubleTickDivisor = 4; |
+static const double kDoubleTickThreshold = 0.25; |
// kIntervalChangeThreshold is the fraction of the interval that will trigger an |
// immediate interval change. kPhaseChangeThreshold is the fraction of the |
@@ -42,10 +42,10 @@ |
DelayBasedTimeSource::DelayBasedTimeSource( |
base::TimeDelta interval, base::SingleThreadTaskRunner* task_runner) |
: client_(NULL), |
- last_tick_time_(base::TimeTicks() - interval), |
+ has_tick_target_(false), |
current_parameters_(interval, base::TimeTicks()), |
next_parameters_(interval, base::TimeTicks()), |
- active_(false), |
+ state_(STATE_INACTIVE), |
task_runner_(task_runner), |
weak_factory_(this) {} |
@@ -53,19 +53,32 @@ |
void DelayBasedTimeSource::SetActive(bool active) { |
TRACE_EVENT1("cc", "DelayBasedTimeSource::SetActive", "active", active); |
- if (active == active_) |
+ if (!active) { |
+ state_ = STATE_INACTIVE; |
+ weak_factory_.InvalidateWeakPtrs(); |
return; |
- active_ = active; |
+ } |
- if (!active_) { |
- weak_factory_.InvalidateWeakPtrs(); |
+ if (state_ == STATE_STARTING || state_ == STATE_ACTIVE) |
return; |
+ |
+ if (!has_tick_target_) { |
+ // Becoming active the first time is deferred: we post a 0-delay task. |
+ // When it runs, we use that to establish the timebase, become truly |
+ // active, and fire the first tick. |
+ state_ = STATE_STARTING; |
+ task_runner_->PostTask(FROM_HERE, |
+ base::Bind(&DelayBasedTimeSource::OnTimerFired, |
+ weak_factory_.GetWeakPtr())); |
+ return; |
} |
+ state_ = STATE_ACTIVE; |
+ |
PostNextTickTask(Now()); |
} |
-bool DelayBasedTimeSource::Active() const { return active_; } |
+bool DelayBasedTimeSource::Active() const { return state_ != STATE_INACTIVE; } |
base::TimeTicks DelayBasedTimeSource::LastTickTime() { return last_tick_time_; } |
@@ -74,12 +87,18 @@ |
} |
void DelayBasedTimeSource::OnTimerFired() { |
- DCHECK(active_); |
+ DCHECK(state_ != STATE_INACTIVE); |
- last_tick_time_ = current_parameters_.tick_target; |
+ base::TimeTicks now = this->Now(); |
+ last_tick_time_ = now; |
- PostNextTickTask(Now()); |
+ if (state_ == STATE_STARTING) { |
+ SetTimebaseAndInterval(now, current_parameters_.interval); |
+ state_ = STATE_ACTIVE; |
+ } |
+ PostNextTickTask(now); |
+ |
// Fire the tick. |
if (client_) |
client_->OnTimerTick(); |
@@ -93,8 +112,9 @@ |
base::TimeDelta interval) { |
next_parameters_.interval = interval; |
next_parameters_.tick_target = timebase; |
+ has_tick_target_ = true; |
- if (!active_) { |
+ if (state_ != STATE_ACTIVE) { |
// If we aren't active, there's no need to reset the timer. |
return; |
} |
@@ -105,8 +125,6 @@ |
std::abs((interval - current_parameters_.interval).InSecondsF()); |
double interval_change = interval_delta / interval.InSecondsF(); |
if (interval_change > kIntervalChangeThreshold) { |
- TRACE_EVENT_INSTANT0("cc", "DelayBasedTimeSource::IntervalChanged", |
- TRACE_EVENT_SCOPE_THREAD); |
SetActive(false); |
SetActive(true); |
return; |
@@ -124,8 +142,6 @@ |
fmod(target_delta, interval.InSecondsF()) / interval.InSecondsF(); |
if (phase_change > kPhaseChangeThreshold && |
phase_change < (1.0 - kPhaseChangeThreshold)) { |
- TRACE_EVENT_INSTANT0("cc", "DelayBasedTimeSource::PhaseChanged", |
- TRACE_EVENT_SCOPE_THREAD); |
SetActive(false); |
SetActive(true); |
return; |
@@ -191,24 +207,26 @@ |
// now=37 tick_target=16.667 new_target=50.000 --> |
// tick(), PostDelayedTask(floor(50.000-37)) --> PostDelayedTask(13) |
base::TimeTicks DelayBasedTimeSource::NextTickTarget(base::TimeTicks now) { |
- const base::TimeDelta epsilon(base::TimeDelta::FromMicroseconds(1)); |
base::TimeDelta new_interval = next_parameters_.interval; |
int intervals_elapsed = |
- (now - next_parameters_.tick_target + new_interval - epsilon) / |
- new_interval; |
- base::TimeTicks new_tick_target = |
+ static_cast<int>(floor((now - next_parameters_.tick_target).InSecondsF() / |
+ new_interval.InSecondsF())); |
+ base::TimeTicks last_effective_tick = |
next_parameters_.tick_target + new_interval * intervals_elapsed; |
- DCHECK(now <= new_tick_target) |
+ base::TimeTicks new_tick_target = last_effective_tick + new_interval; |
+ DCHECK(now < new_tick_target) |
<< "now = " << now.ToInternalValue() |
<< "; new_tick_target = " << new_tick_target.ToInternalValue() |
<< "; new_interval = " << new_interval.InMicroseconds() |
<< "; tick_target = " << next_parameters_.tick_target.ToInternalValue() |
- << "; intervals_elapsed = " << intervals_elapsed; |
+ << "; intervals_elapsed = " << intervals_elapsed |
+ << "; last_effective_tick = " << last_effective_tick.ToInternalValue(); |
// Avoid double ticks when: |
// 1) Turning off the timer and turning it right back on. |
// 2) Jittery data is passed to SetTimebaseAndInterval(). |
- if (new_tick_target - last_tick_time_ <= new_interval / kDoubleTickDivisor) |
+ if (new_tick_target - last_tick_time_ <= |
+ new_interval / static_cast<int>(1.0 / kDoubleTickThreshold)) |
new_tick_target += new_interval; |
return new_tick_target; |
@@ -218,9 +236,10 @@ |
base::TimeTicks new_tick_target = NextTickTarget(now); |
// Post another task *before* the tick and update state |
- base::TimeDelta delay; |
- if (now <= new_tick_target) |
- delay = new_tick_target - now; |
+ base::TimeDelta delay = new_tick_target - now; |
+ DCHECK(delay.InMillisecondsF() <= |
+ next_parameters_.interval.InMillisecondsF() * |
+ (1.0 + kDoubleTickThreshold)); |
task_runner_->PostDelayedTask(FROM_HERE, |
base::Bind(&DelayBasedTimeSource::OnTimerFired, |
weak_factory_.GetWeakPtr()), |