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 |