Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2011 The Chromium Authors. All rights reserved. | 1 // Copyright 2011 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 "cc/scheduler/delay_based_time_source.h" | 5 #include "cc/scheduler/delay_based_time_source.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <cmath> | 8 #include <cmath> |
| 9 #include <string> | 9 #include <string> |
| 10 | 10 |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 33 static const double kIntervalChangeThreshold = 0.25; | 33 static const double kIntervalChangeThreshold = 0.25; |
| 34 static const double kPhaseChangeThreshold = 0.25; | 34 static const double kPhaseChangeThreshold = 0.25; |
| 35 | 35 |
| 36 } // namespace | 36 } // namespace |
| 37 | 37 |
| 38 // The following methods correspond to the DelayBasedTimeSource that uses | 38 // The following methods correspond to the DelayBasedTimeSource that uses |
| 39 // the base::TimeTicks::Now as the timebase. | 39 // the base::TimeTicks::Now as the timebase. |
| 40 DelayBasedTimeSource::DelayBasedTimeSource( | 40 DelayBasedTimeSource::DelayBasedTimeSource( |
| 41 base::TimeDelta interval, | 41 base::TimeDelta interval, |
| 42 base::SingleThreadTaskRunner* task_runner) | 42 base::SingleThreadTaskRunner* task_runner) |
| 43 : client_(NULL), | 43 : client_(nullptr), |
| 44 active_(false), | |
| 45 timebase_(base::TimeTicks()), | |
| 46 interval_(interval), | |
| 44 last_tick_time_(base::TimeTicks() - interval), | 47 last_tick_time_(base::TimeTicks() - interval), |
| 45 current_parameters_(interval, base::TimeTicks()), | 48 next_tick_time_(base::TimeTicks()), |
| 46 next_parameters_(interval, base::TimeTicks()), | |
| 47 active_(false), | |
| 48 task_runner_(task_runner), | 49 task_runner_(task_runner), |
| 49 weak_factory_(this) { | 50 weak_factory_(this) { |
| 50 DCHECK_GT(interval.ToInternalValue(), 0); | 51 DCHECK_GT(interval, base::TimeDelta()); |
| 51 } | 52 } |
| 52 | 53 |
| 53 DelayBasedTimeSource::~DelayBasedTimeSource() {} | 54 DelayBasedTimeSource::~DelayBasedTimeSource() {} |
| 54 | 55 |
| 55 base::TimeTicks DelayBasedTimeSource::SetActive(bool active) { | 56 base::TimeTicks DelayBasedTimeSource::SetActive(bool active) { |
| 56 TRACE_EVENT1("cc", "DelayBasedTimeSource::SetActive", "active", active); | 57 TRACE_EVENT1("cc", "DelayBasedTimeSource::SetActive", "active", active); |
| 57 if (active == active_) | 58 if (active == active_) |
| 58 return base::TimeTicks(); | 59 return base::TimeTicks(); |
|
mithro-old
2015/07/06 11:29:12
nit: I think it would be good to have a new line b
| |
| 59 active_ = active; | 60 active_ = active; |
| 60 | 61 |
| 61 if (!active_) { | 62 if (!active_) { |
| 62 weak_factory_.InvalidateWeakPtrs(); | 63 next_tick_time_ = base::TimeTicks(); |
| 64 tick_closure_.Cancel(); | |
| 63 return base::TimeTicks(); | 65 return base::TimeTicks(); |
| 64 } | 66 } |
| 65 | 67 |
| 66 PostNextTickTask(Now()); | 68 ResetTickTask(Now()); |
| 67 | 69 |
| 68 // Determine if there was a tick that was missed while not active. | 70 // Determine if there was a tick that was missed while not active. |
| 69 base::TimeTicks last_tick_time_if_always_active = | 71 base::TimeTicks last_tick_time_if_always_active = next_tick_time_ - interval_; |
| 70 current_parameters_.tick_target - current_parameters_.interval; | 72 base::TimeTicks last_tick_time_threshold = |
| 71 base::TimeTicks new_tick_time_threshold = | 73 last_tick_time_ + interval_ / kDoubleTickDivisor; |
| 72 last_tick_time_ + current_parameters_.interval / kDoubleTickDivisor; | 74 if (last_tick_time_if_always_active > last_tick_time_threshold) { |
| 73 if (last_tick_time_if_always_active > new_tick_time_threshold) { | |
| 74 last_tick_time_ = last_tick_time_if_always_active; | 75 last_tick_time_ = last_tick_time_if_always_active; |
| 75 return last_tick_time_; | 76 return last_tick_time_; |
| 76 } | 77 } |
| 77 | 78 |
| 78 return base::TimeTicks(); | 79 return base::TimeTicks(); |
| 79 } | 80 } |
| 80 | 81 |
| 82 base::TimeDelta DelayBasedTimeSource::Interval() const { | |
| 83 return interval_; | |
| 84 } | |
| 85 | |
| 81 bool DelayBasedTimeSource::Active() const { return active_; } | 86 bool DelayBasedTimeSource::Active() const { return active_; } |
| 82 | 87 |
| 83 base::TimeTicks DelayBasedTimeSource::LastTickTime() const { | 88 base::TimeTicks DelayBasedTimeSource::LastTickTime() const { |
| 84 return last_tick_time_; | 89 return last_tick_time_; |
| 85 } | 90 } |
| 86 | 91 |
| 87 base::TimeTicks DelayBasedTimeSource::NextTickTime() const { | 92 base::TimeTicks DelayBasedTimeSource::NextTickTime() const { |
| 88 return Active() ? current_parameters_.tick_target : base::TimeTicks(); | 93 return next_tick_time_; |
| 89 } | 94 } |
| 90 | 95 |
| 91 void DelayBasedTimeSource::OnTimerFired() { | 96 void DelayBasedTimeSource::OnTimerTick() { |
| 92 DCHECK(active_); | 97 DCHECK(active_); |
| 93 | 98 |
| 94 last_tick_time_ = current_parameters_.tick_target; | 99 last_tick_time_ = next_tick_time_; |
| 95 | 100 |
| 96 PostNextTickTask(Now()); | 101 PostNextTickTask(Now()); |
| 97 | 102 |
| 98 // Fire the tick. | 103 // Fire the tick. |
| 99 if (client_) | 104 if (client_) |
| 100 client_->OnTimerTick(); | 105 client_->OnTimerTick(); |
| 101 } | 106 } |
| 102 | 107 |
| 103 void DelayBasedTimeSource::SetClient(TimeSourceClient* client) { | 108 void DelayBasedTimeSource::SetClient(DelayBasedTimeSourceClient* client) { |
| 104 client_ = client; | 109 client_ = client; |
| 105 } | 110 } |
| 106 | 111 |
| 107 void DelayBasedTimeSource::SetTimebaseAndInterval(base::TimeTicks timebase, | 112 void DelayBasedTimeSource::SetTimebaseAndInterval(base::TimeTicks timebase, |
| 108 base::TimeDelta interval) { | 113 base::TimeDelta interval) { |
| 109 DCHECK_GT(interval.ToInternalValue(), 0); | 114 DCHECK_GT(interval, base::TimeDelta()); |
| 110 next_parameters_.interval = interval; | |
| 111 next_parameters_.tick_target = timebase; | |
| 112 | |
| 113 if (!active_) { | |
| 114 // If we aren't active, there's no need to reset the timer. | |
| 115 return; | |
| 116 } | |
| 117 | 115 |
| 118 // If the change in interval is larger than the change threshold, | 116 // If the change in interval is larger than the change threshold, |
| 119 // request an immediate reset. | 117 // request an immediate reset. |
| 120 double interval_delta = | 118 double interval_delta = std::abs((interval - interval_).InSecondsF()); |
| 121 std::abs((interval - current_parameters_.interval).InSecondsF()); | 119 // Comparing with next_tick_time_ is the right thing to do because we want to |
| 120 // know if we want to cancel the existing tick task and schedule a new one. | |
| 121 // Also next_tick_time_ = timebase_ mod interval_. | |
| 122 double timebase_delta = std::abs((timebase - next_tick_time_).InSecondsF()); | |
| 123 | |
| 124 interval_ = interval; | |
| 125 timebase_ = timebase; | |
| 126 | |
| 127 // If we aren't active, there's no need to reset the timer. | |
| 128 if (!active_) | |
| 129 return; | |
| 130 | |
| 122 double interval_change = interval_delta / interval.InSecondsF(); | 131 double interval_change = interval_delta / interval.InSecondsF(); |
| 123 if (interval_change > kIntervalChangeThreshold) { | 132 if (interval_change > kIntervalChangeThreshold) { |
| 124 TRACE_EVENT_INSTANT0("cc", "DelayBasedTimeSource::IntervalChanged", | 133 TRACE_EVENT_INSTANT0("cc", "DelayBasedTimeSource::IntervalChanged", |
| 125 TRACE_EVENT_SCOPE_THREAD); | 134 TRACE_EVENT_SCOPE_THREAD); |
| 126 SetActive(false); | 135 ResetTickTask(Now()); |
| 127 SetActive(true); | |
| 128 return; | 136 return; |
| 129 } | 137 } |
| 130 | 138 |
| 131 // If the change in phase is greater than the change threshold in either | 139 // If the change in phase is greater than the change threshold in either |
| 132 // direction, request an immediate reset. This logic might result in a false | 140 // direction, request an immediate reset. This logic might result in a false |
| 133 // negative if there is a simultaneous small change in the interval and the | 141 // negative if there is a simultaneous small change in the interval and the |
| 134 // fmod just happens to return something near zero. Assuming the timebase | 142 // fmod just happens to return something near zero. Assuming the timebase |
| 135 // is very recent though, which it should be, we'll still be ok because the | 143 // is very recent though, which it should be, we'll still be ok because the |
| 136 // old clock and new clock just happen to line up. | 144 // old clock and new clock just happen to line up. |
| 137 double target_delta = | |
| 138 std::abs((timebase - current_parameters_.tick_target).InSecondsF()); | |
| 139 double phase_change = | 145 double phase_change = |
| 140 fmod(target_delta, interval.InSecondsF()) / interval.InSecondsF(); | 146 fmod(timebase_delta, interval.InSecondsF()) / interval.InSecondsF(); |
| 141 if (phase_change > kPhaseChangeThreshold && | 147 if (phase_change > kPhaseChangeThreshold && |
| 142 phase_change < (1.0 - kPhaseChangeThreshold)) { | 148 phase_change < (1.0 - kPhaseChangeThreshold)) { |
| 143 TRACE_EVENT_INSTANT0("cc", "DelayBasedTimeSource::PhaseChanged", | 149 TRACE_EVENT_INSTANT0("cc", "DelayBasedTimeSource::PhaseChanged", |
| 144 TRACE_EVENT_SCOPE_THREAD); | 150 TRACE_EVENT_SCOPE_THREAD); |
| 145 SetActive(false); | 151 ResetTickTask(Now()); |
| 146 SetActive(true); | |
| 147 return; | 152 return; |
| 148 } | 153 } |
| 149 } | 154 } |
| 150 | 155 |
| 151 base::TimeTicks DelayBasedTimeSource::Now() const { | 156 base::TimeTicks DelayBasedTimeSource::Now() const { |
| 152 return base::TimeTicks::Now(); | 157 return base::TimeTicks::Now(); |
| 153 } | 158 } |
| 154 | 159 |
| 155 // This code tries to achieve an average tick rate as close to interval_ as | 160 // This code tries to achieve an average tick rate as close to interval_ as |
| 156 // possible. To do this, it has to deal with a few basic issues: | 161 // possible. To do this, it has to deal with a few basic issues: |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 199 // Thus, we compute the new_target based on the old timebase: | 204 // Thus, we compute the new_target based on the old timebase: |
| 200 // now=18 tick_target=16.667 new_target=33.333 --> | 205 // now=18 tick_target=16.667 new_target=33.333 --> |
| 201 // tick(), PostDelayedTask(floor(33.333-18)) --> PostDelayedTask(15) | 206 // tick(), PostDelayedTask(floor(33.333-18)) --> PostDelayedTask(15) |
| 202 // This brings us back to 18+15 = 33, which was where we would have been if the | 207 // This brings us back to 18+15 = 33, which was where we would have been if the |
| 203 // task hadn't been late. | 208 // task hadn't been late. |
| 204 // | 209 // |
| 205 // For the really late delay, we we move to the next logical tick. The timebase | 210 // For the really late delay, we we move to the next logical tick. The timebase |
| 206 // is not reset. | 211 // is not reset. |
| 207 // now=37 tick_target=16.667 new_target=50.000 --> | 212 // now=37 tick_target=16.667 new_target=50.000 --> |
| 208 // tick(), PostDelayedTask(floor(50.000-37)) --> PostDelayedTask(13) | 213 // tick(), PostDelayedTask(floor(50.000-37)) --> PostDelayedTask(13) |
| 209 base::TimeTicks DelayBasedTimeSource::NextTickTarget(base::TimeTicks now) { | 214 base::TimeTicks DelayBasedTimeSource::NextTickTarget( |
| 210 base::TimeTicks new_tick_target = now.SnappedToNextTick( | 215 base::TimeTicks now) const { |
| 211 next_parameters_.tick_target, next_parameters_.interval); | 216 base::TimeTicks next_tick_target = |
| 212 DCHECK(now <= new_tick_target) | 217 now.SnappedToNextTick(timebase_, interval_); |
| 218 DCHECK(now <= next_tick_target) | |
| 213 << "now = " << now.ToInternalValue() | 219 << "now = " << now.ToInternalValue() |
| 214 << "; new_tick_target = " << new_tick_target.ToInternalValue() | 220 << "; new_tick_target = " << next_tick_target.ToInternalValue() |
| 215 << "; new_interval = " << next_parameters_.interval.InMicroseconds() | 221 << "; new_interval = " << interval_.InMicroseconds() |
| 216 << "; tick_target = " << next_parameters_.tick_target.ToInternalValue(); | 222 << "; new_timbase = " << timebase_.ToInternalValue(); |
| 217 | 223 |
| 218 // Avoid double ticks when: | 224 // Avoid double ticks when: |
| 219 // 1) Turning off the timer and turning it right back on. | 225 // 1) Turning off the timer and turning it right back on. |
| 220 // 2) Jittery data is passed to SetTimebaseAndInterval(). | 226 // 2) Jittery data is passed to SetTimebaseAndInterval(). |
| 221 if (new_tick_target - last_tick_time_ <= | 227 if (next_tick_target - last_tick_time_ <= interval_ / kDoubleTickDivisor) |
| 222 next_parameters_.interval / kDoubleTickDivisor) | 228 next_tick_target += interval_; |
| 223 new_tick_target += next_parameters_.interval; | |
| 224 | 229 |
| 225 return new_tick_target; | 230 return next_tick_target; |
| 226 } | 231 } |
| 227 | 232 |
| 228 void DelayBasedTimeSource::PostNextTickTask(base::TimeTicks now) { | 233 void DelayBasedTimeSource::PostNextTickTask(base::TimeTicks now) { |
| 229 base::TimeTicks new_tick_target = NextTickTarget(now); | 234 next_tick_time_ = NextTickTarget(now); |
| 235 DCHECK(next_tick_time_ >= now); | |
| 236 // Post another task *before* the tick and update state | |
| 237 base::TimeDelta delay = next_tick_time_ - now; | |
| 238 task_runner_->PostDelayedTask(FROM_HERE, tick_closure_.callback(), delay); | |
| 239 } | |
| 230 | 240 |
| 231 // Post another task *before* the tick and update state | 241 void DelayBasedTimeSource::ResetTickTask(base::TimeTicks now) { |
| 232 base::TimeDelta delay; | 242 tick_closure_.Reset(base::Bind(&DelayBasedTimeSource::OnTimerTick, |
| 233 if (now <= new_tick_target) | 243 weak_factory_.GetWeakPtr())); |
| 234 delay = new_tick_target - now; | 244 PostNextTickTask(now); |
| 235 task_runner_->PostDelayedTask(FROM_HERE, | |
| 236 base::Bind(&DelayBasedTimeSource::OnTimerFired, | |
| 237 weak_factory_.GetWeakPtr()), | |
| 238 delay); | |
| 239 | |
| 240 next_parameters_.tick_target = new_tick_target; | |
| 241 current_parameters_ = next_parameters_; | |
| 242 } | 245 } |
| 243 | 246 |
| 244 std::string DelayBasedTimeSource::TypeString() const { | 247 std::string DelayBasedTimeSource::TypeString() const { |
| 245 return "DelayBasedTimeSource"; | 248 return "DelayBasedTimeSource"; |
| 246 } | 249 } |
| 247 | 250 |
| 248 void DelayBasedTimeSource::AsValueInto( | 251 void DelayBasedTimeSource::AsValueInto( |
| 249 base::trace_event::TracedValue* state) const { | 252 base::trace_event::TracedValue* state) const { |
| 250 state->SetString("type", TypeString()); | 253 state->SetString("type", TypeString()); |
| 251 state->SetDouble("last_tick_time_us", LastTickTime().ToInternalValue()); | 254 state->SetDouble("last_tick_time_us", LastTickTime().ToInternalValue()); |
| 252 state->SetDouble("next_tick_time_us", NextTickTime().ToInternalValue()); | 255 state->SetDouble("next_tick_time_us", NextTickTime().ToInternalValue()); |
| 253 | 256 state->SetDouble("interval_us", interval_.InMicroseconds()); |
| 254 state->BeginDictionary("current_parameters"); | 257 state->SetDouble("timebase_us", timebase_.ToInternalValue()); |
| 255 state->SetDouble("interval_us", | |
| 256 current_parameters_.interval.InMicroseconds()); | |
| 257 state->SetDouble("tick_target_us", | |
| 258 current_parameters_.tick_target.ToInternalValue()); | |
| 259 state->EndDictionary(); | |
| 260 | |
| 261 state->BeginDictionary("next_parameters"); | |
| 262 state->SetDouble("interval_us", next_parameters_.interval.InMicroseconds()); | |
| 263 state->SetDouble("tick_target_us", | |
| 264 next_parameters_.tick_target.ToInternalValue()); | |
| 265 state->EndDictionary(); | |
| 266 | |
| 267 state->SetBoolean("active", active_); | 258 state->SetBoolean("active", active_); |
| 268 } | 259 } |
| 269 | 260 |
| 270 } // namespace cc | 261 } // namespace cc |
| OLD | NEW |