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); |
| 58 |
57 if (active == active_) | 59 if (active == active_) |
58 return base::TimeTicks(); | 60 return base::TimeTicks(); |
| 61 |
59 active_ = active; | 62 active_ = active; |
60 | 63 |
61 if (!active_) { | 64 if (!active_) { |
62 weak_factory_.InvalidateWeakPtrs(); | 65 next_tick_time_ = base::TimeTicks(); |
| 66 tick_closure_.Cancel(); |
63 return base::TimeTicks(); | 67 return base::TimeTicks(); |
64 } | 68 } |
65 | 69 |
66 PostNextTickTask(Now()); | 70 ResetTickTask(Now()); |
67 | 71 |
68 // Determine if there was a tick that was missed while not active. | 72 // Determine if there was a tick that was missed while not active. |
69 base::TimeTicks last_tick_time_if_always_active = | 73 base::TimeTicks last_tick_time_if_always_active = next_tick_time_ - interval_; |
70 current_parameters_.tick_target - current_parameters_.interval; | 74 base::TimeTicks last_tick_time_threshold = |
71 base::TimeTicks new_tick_time_threshold = | 75 last_tick_time_ + interval_ / kDoubleTickDivisor; |
72 last_tick_time_ + current_parameters_.interval / kDoubleTickDivisor; | 76 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; | 77 last_tick_time_ = last_tick_time_if_always_active; |
75 return last_tick_time_; | 78 return last_tick_time_; |
76 } | 79 } |
77 | 80 |
78 return base::TimeTicks(); | 81 return base::TimeTicks(); |
79 } | 82 } |
80 | 83 |
| 84 base::TimeDelta DelayBasedTimeSource::Interval() const { |
| 85 return interval_; |
| 86 } |
| 87 |
81 bool DelayBasedTimeSource::Active() const { return active_; } | 88 bool DelayBasedTimeSource::Active() const { return active_; } |
82 | 89 |
83 base::TimeTicks DelayBasedTimeSource::LastTickTime() const { | 90 base::TimeTicks DelayBasedTimeSource::LastTickTime() const { |
84 return last_tick_time_; | 91 return last_tick_time_; |
85 } | 92 } |
86 | 93 |
87 base::TimeTicks DelayBasedTimeSource::NextTickTime() const { | 94 base::TimeTicks DelayBasedTimeSource::NextTickTime() const { |
88 return Active() ? current_parameters_.tick_target : base::TimeTicks(); | 95 return next_tick_time_; |
89 } | 96 } |
90 | 97 |
91 void DelayBasedTimeSource::OnTimerFired() { | 98 void DelayBasedTimeSource::OnTimerTick() { |
92 DCHECK(active_); | 99 DCHECK(active_); |
93 | 100 |
94 last_tick_time_ = current_parameters_.tick_target; | 101 last_tick_time_ = next_tick_time_; |
95 | 102 |
96 PostNextTickTask(Now()); | 103 PostNextTickTask(Now()); |
97 | 104 |
98 // Fire the tick. | 105 // Fire the tick. |
99 if (client_) | 106 if (client_) |
100 client_->OnTimerTick(); | 107 client_->OnTimerTick(); |
101 } | 108 } |
102 | 109 |
103 void DelayBasedTimeSource::SetClient(TimeSourceClient* client) { | 110 void DelayBasedTimeSource::SetClient(DelayBasedTimeSourceClient* client) { |
104 client_ = client; | 111 client_ = client; |
105 } | 112 } |
106 | 113 |
107 void DelayBasedTimeSource::SetTimebaseAndInterval(base::TimeTicks timebase, | 114 void DelayBasedTimeSource::SetTimebaseAndInterval(base::TimeTicks timebase, |
108 base::TimeDelta interval) { | 115 base::TimeDelta interval) { |
109 DCHECK_GT(interval.ToInternalValue(), 0); | 116 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 | 117 |
118 // If the change in interval is larger than the change threshold, | 118 // If the change in interval is larger than the change threshold, |
119 // request an immediate reset. | 119 // request an immediate reset. |
120 double interval_delta = | 120 double interval_delta = std::abs((interval - interval_).InSecondsF()); |
121 std::abs((interval - current_parameters_.interval).InSecondsF()); | 121 // Comparing with next_tick_time_ is the right thing to do because we want to |
| 122 // know if we want to cancel the existing tick task and schedule a new one. |
| 123 // Also next_tick_time_ = timebase_ mod interval_. |
| 124 double timebase_delta = std::abs((timebase - next_tick_time_).InSecondsF()); |
| 125 |
| 126 interval_ = interval; |
| 127 timebase_ = timebase; |
| 128 |
| 129 // If we aren't active, there's no need to reset the timer. |
| 130 if (!active_) |
| 131 return; |
| 132 |
122 double interval_change = interval_delta / interval.InSecondsF(); | 133 double interval_change = interval_delta / interval.InSecondsF(); |
123 if (interval_change > kIntervalChangeThreshold) { | 134 if (interval_change > kIntervalChangeThreshold) { |
124 TRACE_EVENT_INSTANT0("cc", "DelayBasedTimeSource::IntervalChanged", | 135 TRACE_EVENT_INSTANT0("cc", "DelayBasedTimeSource::IntervalChanged", |
125 TRACE_EVENT_SCOPE_THREAD); | 136 TRACE_EVENT_SCOPE_THREAD); |
126 SetActive(false); | 137 ResetTickTask(Now()); |
127 SetActive(true); | |
128 return; | 138 return; |
129 } | 139 } |
130 | 140 |
131 // If the change in phase is greater than the change threshold in either | 141 // 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 | 142 // 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 | 143 // 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 | 144 // 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 | 145 // 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. | 146 // 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 = | 147 double phase_change = |
140 fmod(target_delta, interval.InSecondsF()) / interval.InSecondsF(); | 148 fmod(timebase_delta, interval.InSecondsF()) / interval.InSecondsF(); |
141 if (phase_change > kPhaseChangeThreshold && | 149 if (phase_change > kPhaseChangeThreshold && |
142 phase_change < (1.0 - kPhaseChangeThreshold)) { | 150 phase_change < (1.0 - kPhaseChangeThreshold)) { |
143 TRACE_EVENT_INSTANT0("cc", "DelayBasedTimeSource::PhaseChanged", | 151 TRACE_EVENT_INSTANT0("cc", "DelayBasedTimeSource::PhaseChanged", |
144 TRACE_EVENT_SCOPE_THREAD); | 152 TRACE_EVENT_SCOPE_THREAD); |
145 SetActive(false); | 153 ResetTickTask(Now()); |
146 SetActive(true); | |
147 return; | 154 return; |
148 } | 155 } |
149 } | 156 } |
150 | 157 |
151 base::TimeTicks DelayBasedTimeSource::Now() const { | 158 base::TimeTicks DelayBasedTimeSource::Now() const { |
152 return base::TimeTicks::Now(); | 159 return base::TimeTicks::Now(); |
153 } | 160 } |
154 | 161 |
155 // This code tries to achieve an average tick rate as close to interval_ as | 162 // 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: | 163 // 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: | 206 // Thus, we compute the new_target based on the old timebase: |
200 // now=18 tick_target=16.667 new_target=33.333 --> | 207 // now=18 tick_target=16.667 new_target=33.333 --> |
201 // tick(), PostDelayedTask(floor(33.333-18)) --> PostDelayedTask(15) | 208 // 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 | 209 // This brings us back to 18+15 = 33, which was where we would have been if the |
203 // task hadn't been late. | 210 // task hadn't been late. |
204 // | 211 // |
205 // For the really late delay, we we move to the next logical tick. The timebase | 212 // For the really late delay, we we move to the next logical tick. The timebase |
206 // is not reset. | 213 // is not reset. |
207 // now=37 tick_target=16.667 new_target=50.000 --> | 214 // now=37 tick_target=16.667 new_target=50.000 --> |
208 // tick(), PostDelayedTask(floor(50.000-37)) --> PostDelayedTask(13) | 215 // tick(), PostDelayedTask(floor(50.000-37)) --> PostDelayedTask(13) |
209 base::TimeTicks DelayBasedTimeSource::NextTickTarget(base::TimeTicks now) { | 216 base::TimeTicks DelayBasedTimeSource::NextTickTarget( |
210 base::TimeTicks new_tick_target = now.SnappedToNextTick( | 217 base::TimeTicks now) const { |
211 next_parameters_.tick_target, next_parameters_.interval); | 218 base::TimeTicks next_tick_target = |
212 DCHECK(now <= new_tick_target) | 219 now.SnappedToNextTick(timebase_, interval_); |
| 220 DCHECK(now <= next_tick_target) |
213 << "now = " << now.ToInternalValue() | 221 << "now = " << now.ToInternalValue() |
214 << "; new_tick_target = " << new_tick_target.ToInternalValue() | 222 << "; new_tick_target = " << next_tick_target.ToInternalValue() |
215 << "; new_interval = " << next_parameters_.interval.InMicroseconds() | 223 << "; new_interval = " << interval_.InMicroseconds() |
216 << "; tick_target = " << next_parameters_.tick_target.ToInternalValue(); | 224 << "; new_timbase = " << timebase_.ToInternalValue(); |
217 | 225 |
218 // Avoid double ticks when: | 226 // Avoid double ticks when: |
219 // 1) Turning off the timer and turning it right back on. | 227 // 1) Turning off the timer and turning it right back on. |
220 // 2) Jittery data is passed to SetTimebaseAndInterval(). | 228 // 2) Jittery data is passed to SetTimebaseAndInterval(). |
221 if (new_tick_target - last_tick_time_ <= | 229 if (next_tick_target - last_tick_time_ <= interval_ / kDoubleTickDivisor) |
222 next_parameters_.interval / kDoubleTickDivisor) | 230 next_tick_target += interval_; |
223 new_tick_target += next_parameters_.interval; | |
224 | 231 |
225 return new_tick_target; | 232 return next_tick_target; |
226 } | 233 } |
227 | 234 |
228 void DelayBasedTimeSource::PostNextTickTask(base::TimeTicks now) { | 235 void DelayBasedTimeSource::PostNextTickTask(base::TimeTicks now) { |
229 base::TimeTicks new_tick_target = NextTickTarget(now); | 236 next_tick_time_ = NextTickTarget(now); |
| 237 DCHECK(next_tick_time_ >= now); |
| 238 // Post another task *before* the tick and update state |
| 239 base::TimeDelta delay = next_tick_time_ - now; |
| 240 task_runner_->PostDelayedTask(FROM_HERE, tick_closure_.callback(), delay); |
| 241 } |
230 | 242 |
231 // Post another task *before* the tick and update state | 243 void DelayBasedTimeSource::ResetTickTask(base::TimeTicks now) { |
232 base::TimeDelta delay; | 244 tick_closure_.Reset(base::Bind(&DelayBasedTimeSource::OnTimerTick, |
233 if (now <= new_tick_target) | 245 weak_factory_.GetWeakPtr())); |
234 delay = new_tick_target - now; | 246 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 } | 247 } |
243 | 248 |
244 std::string DelayBasedTimeSource::TypeString() const { | 249 std::string DelayBasedTimeSource::TypeString() const { |
245 return "DelayBasedTimeSource"; | 250 return "DelayBasedTimeSource"; |
246 } | 251 } |
247 | 252 |
248 void DelayBasedTimeSource::AsValueInto( | 253 void DelayBasedTimeSource::AsValueInto( |
249 base::trace_event::TracedValue* state) const { | 254 base::trace_event::TracedValue* state) const { |
250 state->SetString("type", TypeString()); | 255 state->SetString("type", TypeString()); |
251 state->SetDouble("last_tick_time_us", LastTickTime().ToInternalValue()); | 256 state->SetDouble("last_tick_time_us", LastTickTime().ToInternalValue()); |
252 state->SetDouble("next_tick_time_us", NextTickTime().ToInternalValue()); | 257 state->SetDouble("next_tick_time_us", NextTickTime().ToInternalValue()); |
253 | 258 state->SetDouble("interval_us", interval_.InMicroseconds()); |
254 state->BeginDictionary("current_parameters"); | 259 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_); | 260 state->SetBoolean("active", active_); |
268 } | 261 } |
269 | 262 |
270 } // namespace cc | 263 } // namespace cc |
OLD | NEW |