Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(19)

Side by Side Diff: cc/scheduler/delay_based_time_source.cc

Issue 1200113003: cc: Cleanup DelayBasedTimeSource code. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@task_runner_refptr
Patch Set: Fix race between missed tick and normal tick Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 24 matching lines...) Expand all
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_(NULL),
44 last_tick_time_(base::TimeTicks() - interval), 44 last_tick_time_(base::TimeTicks() - interval),
45 current_parameters_(interval, base::TimeTicks()), 45 next_tick_time_(base::TimeTicks()),
46 next_parameters_(interval, base::TimeTicks()), 46 timebase_(base::TimeTicks()),
47 interval_(interval),
47 active_(false), 48 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 void 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;
59 active_ = active; 60 active_ = active;
60 61
61 if (!active_) { 62 if (!active_) {
63 next_tick_time_ = base::TimeTicks();
62 weak_factory_.InvalidateWeakPtrs(); 64 weak_factory_.InvalidateWeakPtrs();
mithro-old 2015/06/30 08:11:52 It's really weird that this works by invalidating
63 return base::TimeTicks(); 65 return;
64 } 66 }
65 67
66 PostNextTickTask(Now()); 68 base::TimeTicks now = Now();
69 base::TimeTicks next_tick_target = NextTickTarget(now);
67 70
68 // Determine if there was a tick that was missed while not active. 71 // Determine if there was a tick that was missed while not active.
69 base::TimeTicks last_tick_time_if_always_active = 72 base::TimeTicks last_tick_time_if_always_active =
70 current_parameters_.tick_target - current_parameters_.interval; 73 next_tick_target - interval_;
71 base::TimeTicks new_tick_time_threshold = 74 base::TimeTicks last_tick_time_threshold =
72 last_tick_time_ + current_parameters_.interval / kDoubleTickDivisor; 75 last_tick_time_ + interval_ / kDoubleTickDivisor;
73 if (last_tick_time_if_always_active > new_tick_time_threshold) { 76 if (last_tick_time_if_always_active > last_tick_time_threshold) {
74 last_tick_time_ = last_tick_time_if_always_active; 77 missed_tick_time_ = last_tick_time_if_always_active;
75 return last_tick_time_; 78 task_runner_->PostTask(FROM_HERE,
79 base::Bind(&DelayBasedTimeSource::OnMissedTick,
brianderson 2015/06/26 19:24:35 Can you bind to a Closure (in the constructor and
80 weak_factory_.GetWeakPtr()));
76 } 81 }
77 82
78 return base::TimeTicks(); 83 PostNextTickTask(now);
84 }
85
86 base::TimeDelta DelayBasedTimeSource::Interval() const {
87 return interval_;
79 } 88 }
80 89
81 bool DelayBasedTimeSource::Active() const { return active_; } 90 bool DelayBasedTimeSource::Active() const { return active_; }
82 91
83 base::TimeTicks DelayBasedTimeSource::LastTickTime() const { 92 base::TimeTicks DelayBasedTimeSource::LastTickTime() const {
84 return last_tick_time_; 93 return last_tick_time_;
85 } 94 }
86 95
87 base::TimeTicks DelayBasedTimeSource::NextTickTime() const { 96 base::TimeTicks DelayBasedTimeSource::MissedTickTime() const {
brianderson 2015/06/26 19:24:34 Is this needed? Looks like Client::OnMissedTick ca
88 return Active() ? current_parameters_.tick_target : base::TimeTicks(); 97 return missed_tick_time_;
89 } 98 }
90 99
91 void DelayBasedTimeSource::OnTimerFired() { 100 base::TimeTicks DelayBasedTimeSource::NextTickTime() const {
101 return next_tick_time_;
brianderson 2015/06/26 19:24:34 Probably doesn't matter for the current user of th
102 }
103
104 void DelayBasedTimeSource::OnMissedTick() {
brianderson 2015/06/26 19:24:34 Should missed_tick_time_ be reset somewhere in thi
92 DCHECK(active_); 105 DCHECK(active_);
93 106
94 last_tick_time_ = current_parameters_.tick_target; 107 last_tick_time_ = missed_tick_time_;
108
109 if (client_)
110 client_->OnMissedTick();
111 }
112
113 void DelayBasedTimeSource::OnTimerTick() {
114 DCHECK(active_);
115
116 last_tick_time_ = next_tick_time_;
95 117
96 PostNextTickTask(Now()); 118 PostNextTickTask(Now());
97 119
98 // Fire the tick. 120 // Fire the tick.
99 if (client_) 121 if (client_)
100 client_->OnTimerTick(); 122 client_->OnTimerTick();
101 } 123 }
102 124
103 void DelayBasedTimeSource::SetClient(TimeSourceClient* client) { 125 void DelayBasedTimeSource::SetClient(DelayBasedTimeSourceClient* client) {
104 client_ = client; 126 client_ = client;
105 } 127 }
106 128
107 void DelayBasedTimeSource::SetTimebaseAndInterval(base::TimeTicks timebase, 129 void DelayBasedTimeSource::SetTimebaseAndInterval(base::TimeTicks timebase,
108 base::TimeDelta interval) { 130 base::TimeDelta interval) {
109 DCHECK_GT(interval.ToInternalValue(), 0); 131 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 132
118 // If the change in interval is larger than the change threshold, 133 // If the change in interval is larger than the change threshold,
119 // request an immediate reset. 134 // request an immediate reset.
120 double interval_delta = 135 double interval_delta = std::abs((interval - interval_).InSecondsF());
121 std::abs((interval - current_parameters_.interval).InSecondsF()); 136 double target_delta = std::abs((timebase - timebase_).InSecondsF());
137
138 interval_ = interval;
139 timebase_ = timebase;
140
141 // If we aren't active, there's no need to reset the timer.
142 if (!active_)
143 return;
144
122 double interval_change = interval_delta / interval.InSecondsF(); 145 double interval_change = interval_delta / interval.InSecondsF();
123 if (interval_change > kIntervalChangeThreshold) { 146 if (interval_change > kIntervalChangeThreshold) {
124 TRACE_EVENT_INSTANT0("cc", "DelayBasedTimeSource::IntervalChanged", 147 TRACE_EVENT_INSTANT0("cc", "DelayBasedTimeSource::IntervalChanged",
125 TRACE_EVENT_SCOPE_THREAD); 148 TRACE_EVENT_SCOPE_THREAD);
126 SetActive(false); 149 ResetTickTask(Now());
127 SetActive(true);
128 return; 150 return;
129 } 151 }
130 152
131 // If the change in phase is greater than the change threshold in either 153 // 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 154 // 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 155 // 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 156 // 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 157 // 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. 158 // 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 = 159 double phase_change =
140 fmod(target_delta, interval.InSecondsF()) / interval.InSecondsF(); 160 fmod(target_delta, interval.InSecondsF()) / interval.InSecondsF();
141 if (phase_change > kPhaseChangeThreshold && 161 if (phase_change > kPhaseChangeThreshold &&
142 phase_change < (1.0 - kPhaseChangeThreshold)) { 162 phase_change < (1.0 - kPhaseChangeThreshold)) {
143 TRACE_EVENT_INSTANT0("cc", "DelayBasedTimeSource::PhaseChanged", 163 TRACE_EVENT_INSTANT0("cc", "DelayBasedTimeSource::PhaseChanged",
144 TRACE_EVENT_SCOPE_THREAD); 164 TRACE_EVENT_SCOPE_THREAD);
145 SetActive(false); 165 ResetTickTask(Now());
146 SetActive(true);
147 return; 166 return;
148 } 167 }
149 } 168 }
150 169
151 base::TimeTicks DelayBasedTimeSource::Now() const { 170 base::TimeTicks DelayBasedTimeSource::Now() const {
152 return base::TimeTicks::Now(); 171 return base::TimeTicks::Now();
153 } 172 }
154 173
155 // This code tries to achieve an average tick rate as close to interval_ as 174 // 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: 175 // 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
199 // Thus, we compute the new_target based on the old timebase: 218 // Thus, we compute the new_target based on the old timebase:
200 // now=18 tick_target=16.667 new_target=33.333 --> 219 // now=18 tick_target=16.667 new_target=33.333 -->
201 // tick(), PostDelayedTask(floor(33.333-18)) --> PostDelayedTask(15) 220 // 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 221 // This brings us back to 18+15 = 33, which was where we would have been if the
203 // task hadn't been late. 222 // task hadn't been late.
204 // 223 //
205 // For the really late delay, we we move to the next logical tick. The timebase 224 // For the really late delay, we we move to the next logical tick. The timebase
206 // is not reset. 225 // is not reset.
207 // now=37 tick_target=16.667 new_target=50.000 --> 226 // now=37 tick_target=16.667 new_target=50.000 -->
208 // tick(), PostDelayedTask(floor(50.000-37)) --> PostDelayedTask(13) 227 // tick(), PostDelayedTask(floor(50.000-37)) --> PostDelayedTask(13)
209 base::TimeTicks DelayBasedTimeSource::NextTickTarget(base::TimeTicks now) { 228 base::TimeTicks DelayBasedTimeSource::NextTickTarget(
210 base::TimeTicks new_tick_target = now.SnappedToNextTick( 229 base::TimeTicks now) const {
211 next_parameters_.tick_target, next_parameters_.interval); 230 base::TimeTicks next_tick_target =
212 DCHECK(now <= new_tick_target) 231 now.SnappedToNextTick(timebase_, interval_);
232 DCHECK(now <= next_tick_target)
213 << "now = " << now.ToInternalValue() 233 << "now = " << now.ToInternalValue()
214 << "; new_tick_target = " << new_tick_target.ToInternalValue() 234 << "; new_tick_target = " << next_tick_target.ToInternalValue()
215 << "; new_interval = " << next_parameters_.interval.InMicroseconds() 235 << "; new_interval = " << interval_.InMicroseconds()
216 << "; tick_target = " << next_parameters_.tick_target.ToInternalValue(); 236 << "; tick_target = " << timebase_.ToInternalValue();
217 237
218 // Avoid double ticks when: 238 // Avoid double ticks when:
219 // 1) Turning off the timer and turning it right back on. 239 // 1) Turning off the timer and turning it right back on.
220 // 2) Jittery data is passed to SetTimebaseAndInterval(). 240 // 2) Jittery data is passed to SetTimebaseAndInterval().
221 if (new_tick_target - last_tick_time_ <= 241 if (next_tick_target - last_tick_time_ <= interval_ / kDoubleTickDivisor)
222 next_parameters_.interval / kDoubleTickDivisor) 242 next_tick_target += interval_;
223 new_tick_target += next_parameters_.interval;
224 243
225 return new_tick_target; 244 return next_tick_target;
226 } 245 }
227 246
228 void DelayBasedTimeSource::PostNextTickTask(base::TimeTicks now) { 247 void DelayBasedTimeSource::PostNextTickTask(base::TimeTicks now) {
229 base::TimeTicks new_tick_target = NextTickTarget(now); 248 base::TimeTicks next_tick_target = NextTickTarget(now);
230 249 DCHECK(next_tick_target >= now);
231 // Post another task *before* the tick and update state 250 // Post another task *before* the tick and update state
232 base::TimeDelta delay; 251 base::TimeDelta delay = next_tick_target - now;
233 if (now <= new_tick_target)
234 delay = new_tick_target - now;
235 task_runner_->PostDelayedTask(FROM_HERE, 252 task_runner_->PostDelayedTask(FROM_HERE,
236 base::Bind(&DelayBasedTimeSource::OnTimerFired, 253 base::Bind(&DelayBasedTimeSource::OnTimerTick,
brianderson 2015/06/26 19:24:35 Ditto regarding dynamic bind.
237 weak_factory_.GetWeakPtr()), 254 weak_factory_.GetWeakPtr()),
238 delay); 255 delay);
239 256
240 next_parameters_.tick_target = new_tick_target; 257 next_tick_time_ = next_tick_target;
241 current_parameters_ = next_parameters_; 258 }
259
260 void DelayBasedTimeSource::ResetTickTask(base::TimeTicks now) {
261 weak_factory_.InvalidateWeakPtrs();
262 PostNextTickTask(now);
242 } 263 }
243 264
244 std::string DelayBasedTimeSource::TypeString() const { 265 std::string DelayBasedTimeSource::TypeString() const {
245 return "DelayBasedTimeSource"; 266 return "DelayBasedTimeSource";
246 } 267 }
247 268
248 void DelayBasedTimeSource::AsValueInto( 269 void DelayBasedTimeSource::AsValueInto(
249 base::trace_event::TracedValue* state) const { 270 base::trace_event::TracedValue* state) const {
250 state->SetString("type", TypeString()); 271 state->SetString("type", TypeString());
251 state->SetDouble("last_tick_time_us", LastTickTime().ToInternalValue()); 272 state->SetDouble("last_tick_time_us", LastTickTime().ToInternalValue());
273 state->SetDouble("missed_tick_time_us", MissedTickTime().ToInternalValue());
252 state->SetDouble("next_tick_time_us", NextTickTime().ToInternalValue()); 274 state->SetDouble("next_tick_time_us", NextTickTime().ToInternalValue());
253 275 state->SetDouble("interval_us", interval_.InMicroseconds());
254 state->BeginDictionary("current_parameters"); 276 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_); 277 state->SetBoolean("active", active_);
268 } 278 }
269 279
270 } // namespace cc 280 } // namespace cc
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698