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 | 9 |
10 #include "base/bind.h" | 10 #include "base/bind.h" |
11 #include "base/debug/trace_event.h" | 11 #include "base/debug/trace_event.h" |
12 #include "base/location.h" | 12 #include "base/location.h" |
13 #include "base/logging.h" | 13 #include "base/logging.h" |
14 #include "base/single_thread_task_runner.h" | 14 #include "base/single_thread_task_runner.h" |
15 | 15 |
16 namespace cc { | 16 namespace cc { |
17 | 17 |
18 namespace { | 18 namespace { |
19 | 19 |
20 // kDoubleTickDivisor prevents ticks from running within the specified | 20 // kDoubleTickThreshold prevents ticks from running within the specified |
21 // fraction of an interval. This helps account for jitter in the timebase as | 21 // fraction of an interval. This helps account for jitter in the timebase as |
22 // well as quick timer reactivation. | 22 // well as quick timer reactivation. |
23 static const int kDoubleTickDivisor = 4; | 23 static const double kDoubleTickThreshold = 0.25; |
24 | 24 |
25 // kIntervalChangeThreshold is the fraction of the interval that will trigger an | 25 // kIntervalChangeThreshold is the fraction of the interval that will trigger an |
26 // immediate interval change. kPhaseChangeThreshold is the fraction of the | 26 // immediate interval change. kPhaseChangeThreshold is the fraction of the |
27 // interval that will trigger an immediate phase change. If the changes are | 27 // interval that will trigger an immediate phase change. If the changes are |
28 // within the thresholds, the change will take place on the next tick. If | 28 // within the thresholds, the change will take place on the next tick. If |
29 // either change is outside the thresholds, the next tick will be canceled and | 29 // either change is outside the thresholds, the next tick will be canceled and |
30 // reissued immediately. | 30 // reissued immediately. |
31 static const double kIntervalChangeThreshold = 0.25; | 31 static const double kIntervalChangeThreshold = 0.25; |
32 static const double kPhaseChangeThreshold = 0.25; | 32 static const double kPhaseChangeThreshold = 0.25; |
33 | 33 |
34 } // namespace | 34 } // namespace |
35 | 35 |
36 scoped_refptr<DelayBasedTimeSource> DelayBasedTimeSource::Create( | 36 scoped_refptr<DelayBasedTimeSource> DelayBasedTimeSource::Create( |
37 base::TimeDelta interval, | 37 base::TimeDelta interval, |
38 base::SingleThreadTaskRunner* task_runner) { | 38 base::SingleThreadTaskRunner* task_runner) { |
39 return make_scoped_refptr(new DelayBasedTimeSource(interval, task_runner)); | 39 return make_scoped_refptr(new DelayBasedTimeSource(interval, task_runner)); |
40 } | 40 } |
41 | 41 |
42 DelayBasedTimeSource::DelayBasedTimeSource( | 42 DelayBasedTimeSource::DelayBasedTimeSource( |
43 base::TimeDelta interval, base::SingleThreadTaskRunner* task_runner) | 43 base::TimeDelta interval, base::SingleThreadTaskRunner* task_runner) |
44 : client_(NULL), | 44 : client_(NULL), |
45 last_tick_time_(base::TimeTicks() - interval), | 45 has_tick_target_(false), |
46 current_parameters_(interval, base::TimeTicks()), | 46 current_parameters_(interval, base::TimeTicks()), |
47 next_parameters_(interval, base::TimeTicks()), | 47 next_parameters_(interval, base::TimeTicks()), |
48 active_(false), | 48 state_(STATE_INACTIVE), |
49 task_runner_(task_runner), | 49 task_runner_(task_runner), |
50 weak_factory_(this) {} | 50 weak_factory_(this) {} |
51 | 51 |
52 DelayBasedTimeSource::~DelayBasedTimeSource() {} | 52 DelayBasedTimeSource::~DelayBasedTimeSource() {} |
53 | 53 |
54 void DelayBasedTimeSource::SetActive(bool active) { | 54 void DelayBasedTimeSource::SetActive(bool active) { |
55 TRACE_EVENT1("cc", "DelayBasedTimeSource::SetActive", "active", active); | 55 TRACE_EVENT1("cc", "DelayBasedTimeSource::SetActive", "active", active); |
56 if (active == active_) | 56 if (!active) { |
57 return; | 57 state_ = STATE_INACTIVE; |
58 active_ = active; | |
59 | |
60 if (!active_) { | |
61 weak_factory_.InvalidateWeakPtrs(); | 58 weak_factory_.InvalidateWeakPtrs(); |
62 return; | 59 return; |
63 } | 60 } |
64 | 61 |
| 62 if (state_ == STATE_STARTING || state_ == STATE_ACTIVE) |
| 63 return; |
| 64 |
| 65 if (!has_tick_target_) { |
| 66 // Becoming active the first time is deferred: we post a 0-delay task. |
| 67 // When it runs, we use that to establish the timebase, become truly |
| 68 // active, and fire the first tick. |
| 69 state_ = STATE_STARTING; |
| 70 task_runner_->PostTask(FROM_HERE, |
| 71 base::Bind(&DelayBasedTimeSource::OnTimerFired, |
| 72 weak_factory_.GetWeakPtr())); |
| 73 return; |
| 74 } |
| 75 |
| 76 state_ = STATE_ACTIVE; |
| 77 |
65 PostNextTickTask(Now()); | 78 PostNextTickTask(Now()); |
66 } | 79 } |
67 | 80 |
68 bool DelayBasedTimeSource::Active() const { return active_; } | 81 bool DelayBasedTimeSource::Active() const { return state_ != STATE_INACTIVE; } |
69 | 82 |
70 base::TimeTicks DelayBasedTimeSource::LastTickTime() { return last_tick_time_; } | 83 base::TimeTicks DelayBasedTimeSource::LastTickTime() { return last_tick_time_; } |
71 | 84 |
72 base::TimeTicks DelayBasedTimeSource::NextTickTime() { | 85 base::TimeTicks DelayBasedTimeSource::NextTickTime() { |
73 return Active() ? current_parameters_.tick_target : base::TimeTicks(); | 86 return Active() ? current_parameters_.tick_target : base::TimeTicks(); |
74 } | 87 } |
75 | 88 |
76 void DelayBasedTimeSource::OnTimerFired() { | 89 void DelayBasedTimeSource::OnTimerFired() { |
77 DCHECK(active_); | 90 DCHECK(state_ != STATE_INACTIVE); |
78 | 91 |
79 last_tick_time_ = current_parameters_.tick_target; | 92 base::TimeTicks now = this->Now(); |
| 93 last_tick_time_ = now; |
80 | 94 |
81 PostNextTickTask(Now()); | 95 if (state_ == STATE_STARTING) { |
| 96 SetTimebaseAndInterval(now, current_parameters_.interval); |
| 97 state_ = STATE_ACTIVE; |
| 98 } |
| 99 |
| 100 PostNextTickTask(now); |
82 | 101 |
83 // Fire the tick. | 102 // Fire the tick. |
84 if (client_) | 103 if (client_) |
85 client_->OnTimerTick(); | 104 client_->OnTimerTick(); |
86 } | 105 } |
87 | 106 |
88 void DelayBasedTimeSource::SetClient(TimeSourceClient* client) { | 107 void DelayBasedTimeSource::SetClient(TimeSourceClient* client) { |
89 client_ = client; | 108 client_ = client; |
90 } | 109 } |
91 | 110 |
92 void DelayBasedTimeSource::SetTimebaseAndInterval(base::TimeTicks timebase, | 111 void DelayBasedTimeSource::SetTimebaseAndInterval(base::TimeTicks timebase, |
93 base::TimeDelta interval) { | 112 base::TimeDelta interval) { |
94 next_parameters_.interval = interval; | 113 next_parameters_.interval = interval; |
95 next_parameters_.tick_target = timebase; | 114 next_parameters_.tick_target = timebase; |
| 115 has_tick_target_ = true; |
96 | 116 |
97 if (!active_) { | 117 if (state_ != STATE_ACTIVE) { |
98 // If we aren't active, there's no need to reset the timer. | 118 // If we aren't active, there's no need to reset the timer. |
99 return; | 119 return; |
100 } | 120 } |
101 | 121 |
102 // If the change in interval is larger than the change threshold, | 122 // If the change in interval is larger than the change threshold, |
103 // request an immediate reset. | 123 // request an immediate reset. |
104 double interval_delta = | 124 double interval_delta = |
105 std::abs((interval - current_parameters_.interval).InSecondsF()); | 125 std::abs((interval - current_parameters_.interval).InSecondsF()); |
106 double interval_change = interval_delta / interval.InSecondsF(); | 126 double interval_change = interval_delta / interval.InSecondsF(); |
107 if (interval_change > kIntervalChangeThreshold) { | 127 if (interval_change > kIntervalChangeThreshold) { |
108 TRACE_EVENT_INSTANT0("cc", "DelayBasedTimeSource::IntervalChanged", | |
109 TRACE_EVENT_SCOPE_THREAD); | |
110 SetActive(false); | 128 SetActive(false); |
111 SetActive(true); | 129 SetActive(true); |
112 return; | 130 return; |
113 } | 131 } |
114 | 132 |
115 // If the change in phase is greater than the change threshold in either | 133 // If the change in phase is greater than the change threshold in either |
116 // direction, request an immediate reset. This logic might result in a false | 134 // direction, request an immediate reset. This logic might result in a false |
117 // negative if there is a simultaneous small change in the interval and the | 135 // negative if there is a simultaneous small change in the interval and the |
118 // fmod just happens to return something near zero. Assuming the timebase | 136 // fmod just happens to return something near zero. Assuming the timebase |
119 // is very recent though, which it should be, we'll still be ok because the | 137 // is very recent though, which it should be, we'll still be ok because the |
120 // old clock and new clock just happen to line up. | 138 // old clock and new clock just happen to line up. |
121 double target_delta = | 139 double target_delta = |
122 std::abs((timebase - current_parameters_.tick_target).InSecondsF()); | 140 std::abs((timebase - current_parameters_.tick_target).InSecondsF()); |
123 double phase_change = | 141 double phase_change = |
124 fmod(target_delta, interval.InSecondsF()) / interval.InSecondsF(); | 142 fmod(target_delta, interval.InSecondsF()) / interval.InSecondsF(); |
125 if (phase_change > kPhaseChangeThreshold && | 143 if (phase_change > kPhaseChangeThreshold && |
126 phase_change < (1.0 - kPhaseChangeThreshold)) { | 144 phase_change < (1.0 - kPhaseChangeThreshold)) { |
127 TRACE_EVENT_INSTANT0("cc", "DelayBasedTimeSource::PhaseChanged", | |
128 TRACE_EVENT_SCOPE_THREAD); | |
129 SetActive(false); | 145 SetActive(false); |
130 SetActive(true); | 146 SetActive(true); |
131 return; | 147 return; |
132 } | 148 } |
133 } | 149 } |
134 | 150 |
135 base::TimeTicks DelayBasedTimeSource::Now() const { | 151 base::TimeTicks DelayBasedTimeSource::Now() const { |
136 return base::TimeTicks::Now(); | 152 return base::TimeTicks::Now(); |
137 } | 153 } |
138 | 154 |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
184 // now=18 tick_target=16.667 new_target=33.333 --> | 200 // now=18 tick_target=16.667 new_target=33.333 --> |
185 // tick(), PostDelayedTask(floor(33.333-18)) --> PostDelayedTask(15) | 201 // tick(), PostDelayedTask(floor(33.333-18)) --> PostDelayedTask(15) |
186 // This brings us back to 18+15 = 33, which was where we would have been if the | 202 // This brings us back to 18+15 = 33, which was where we would have been if the |
187 // task hadn't been late. | 203 // task hadn't been late. |
188 // | 204 // |
189 // For the really late delay, we we move to the next logical tick. The timebase | 205 // For the really late delay, we we move to the next logical tick. The timebase |
190 // is not reset. | 206 // is not reset. |
191 // now=37 tick_target=16.667 new_target=50.000 --> | 207 // now=37 tick_target=16.667 new_target=50.000 --> |
192 // tick(), PostDelayedTask(floor(50.000-37)) --> PostDelayedTask(13) | 208 // tick(), PostDelayedTask(floor(50.000-37)) --> PostDelayedTask(13) |
193 base::TimeTicks DelayBasedTimeSource::NextTickTarget(base::TimeTicks now) { | 209 base::TimeTicks DelayBasedTimeSource::NextTickTarget(base::TimeTicks now) { |
194 const base::TimeDelta epsilon(base::TimeDelta::FromMicroseconds(1)); | |
195 base::TimeDelta new_interval = next_parameters_.interval; | 210 base::TimeDelta new_interval = next_parameters_.interval; |
196 int intervals_elapsed = | 211 int intervals_elapsed = |
197 (now - next_parameters_.tick_target + new_interval - epsilon) / | 212 static_cast<int>(floor((now - next_parameters_.tick_target).InSecondsF() / |
198 new_interval; | 213 new_interval.InSecondsF())); |
199 base::TimeTicks new_tick_target = | 214 base::TimeTicks last_effective_tick = |
200 next_parameters_.tick_target + new_interval * intervals_elapsed; | 215 next_parameters_.tick_target + new_interval * intervals_elapsed; |
201 DCHECK(now <= new_tick_target) | 216 base::TimeTicks new_tick_target = last_effective_tick + new_interval; |
| 217 DCHECK(now < new_tick_target) |
202 << "now = " << now.ToInternalValue() | 218 << "now = " << now.ToInternalValue() |
203 << "; new_tick_target = " << new_tick_target.ToInternalValue() | 219 << "; new_tick_target = " << new_tick_target.ToInternalValue() |
204 << "; new_interval = " << new_interval.InMicroseconds() | 220 << "; new_interval = " << new_interval.InMicroseconds() |
205 << "; tick_target = " << next_parameters_.tick_target.ToInternalValue() | 221 << "; tick_target = " << next_parameters_.tick_target.ToInternalValue() |
206 << "; intervals_elapsed = " << intervals_elapsed; | 222 << "; intervals_elapsed = " << intervals_elapsed |
| 223 << "; last_effective_tick = " << last_effective_tick.ToInternalValue(); |
207 | 224 |
208 // Avoid double ticks when: | 225 // Avoid double ticks when: |
209 // 1) Turning off the timer and turning it right back on. | 226 // 1) Turning off the timer and turning it right back on. |
210 // 2) Jittery data is passed to SetTimebaseAndInterval(). | 227 // 2) Jittery data is passed to SetTimebaseAndInterval(). |
211 if (new_tick_target - last_tick_time_ <= new_interval / kDoubleTickDivisor) | 228 if (new_tick_target - last_tick_time_ <= |
| 229 new_interval / static_cast<int>(1.0 / kDoubleTickThreshold)) |
212 new_tick_target += new_interval; | 230 new_tick_target += new_interval; |
213 | 231 |
214 return new_tick_target; | 232 return new_tick_target; |
215 } | 233 } |
216 | 234 |
217 void DelayBasedTimeSource::PostNextTickTask(base::TimeTicks now) { | 235 void DelayBasedTimeSource::PostNextTickTask(base::TimeTicks now) { |
218 base::TimeTicks new_tick_target = NextTickTarget(now); | 236 base::TimeTicks new_tick_target = NextTickTarget(now); |
219 | 237 |
220 // Post another task *before* the tick and update state | 238 // Post another task *before* the tick and update state |
221 base::TimeDelta delay; | 239 base::TimeDelta delay = new_tick_target - now; |
222 if (now <= new_tick_target) | 240 DCHECK(delay.InMillisecondsF() <= |
223 delay = new_tick_target - now; | 241 next_parameters_.interval.InMillisecondsF() * |
| 242 (1.0 + kDoubleTickThreshold)); |
224 task_runner_->PostDelayedTask(FROM_HERE, | 243 task_runner_->PostDelayedTask(FROM_HERE, |
225 base::Bind(&DelayBasedTimeSource::OnTimerFired, | 244 base::Bind(&DelayBasedTimeSource::OnTimerFired, |
226 weak_factory_.GetWeakPtr()), | 245 weak_factory_.GetWeakPtr()), |
227 delay); | 246 delay); |
228 | 247 |
229 next_parameters_.tick_target = new_tick_target; | 248 next_parameters_.tick_target = new_tick_target; |
230 current_parameters_ = next_parameters_; | 249 current_parameters_ = next_parameters_; |
231 } | 250 } |
232 | 251 |
233 } // namespace cc | 252 } // namespace cc |
OLD | NEW |