OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 "base/timer/timer.h" | 5 #include "base/timer/timer.h" |
6 | 6 |
7 #include <stddef.h> | 7 #include <stddef.h> |
8 | 8 |
9 #include <utility> | 9 #include <utility> |
10 | 10 |
11 #include "base/logging.h" | 11 #include "base/logging.h" |
12 #include "base/memory/ptr_util.h" | 12 #include "base/memory/ptr_util.h" |
13 #include "base/memory/ref_counted.h" | 13 #include "base/memory/ref_counted.h" |
14 #include "base/single_thread_task_runner.h" | 14 #include "base/sequenced_task_runner.h" |
15 #include "base/threading/platform_thread.h" | 15 #include "base/threading/sequenced_task_runner_handle.h" |
16 #include "base/threading/thread_task_runner_handle.h" | |
17 #include "base/time/tick_clock.h" | 16 #include "base/time/tick_clock.h" |
18 | 17 |
19 namespace base { | 18 namespace base { |
20 | 19 |
21 // BaseTimerTaskInternal is a simple delegate for scheduling a callback to | 20 // BaseTimerTaskInternal is a helper for scheduling Timer's callback on the |
22 // Timer in the thread's default task runner. It also handles the following | 21 // destination task runner (it is in charge of all methods to be executed on |
23 // edge cases: | 22 // destination task runner while Timer is in charge of all methods to be |
24 // - deleted by the task runner. | 23 // executed on origin task runner). It owns itself on the destination task |
25 // - abandoned (orphaned) by Timer. | 24 // runner and supports a few use cases: |
26 class BaseTimerTaskInternal { | 25 // - Non-repeating timers: |
26 // Invokes |stop_callback| on |origin_task_runner| when Run() fires, | |
27 // *before* running |task|. This ensures that anything posted to origin | |
28 // task runner from |task| will run in the scope of a stopped task runner | |
29 // as expected. | |
30 // Self-destructs when Run() or Abandon() fires, whichever happens first | |
31 // (any pending Run()/Abandon()/Reset() being cancelled through the | |
32 // WeakPtr). | |
33 // - Repeating timers: | |
34 // Re-enqueues |task| after each invocation of Run() until Abandon() is | |
35 // invoked (which will invalidate the WeakPtr of the pending Run()). | |
36 // | |
37 // An invariant assumed below is that there always is exactly one pending Run() | |
38 // task associated with this BaseTimerTaskInternal. That invariant is maintained | |
39 // by having Run() always either triggering self-destruction or reposting self. | |
40 // | |
41 // Note: Timer and BaseTimerTaskInternal try to communicate synchronously when | |
42 // possible (i.e. when SetTaskRunner() wasn't used, which they can verify | |
43 // through RunsTasksOnCurrentThread()). Timer must also always use asynchronous | |
44 // calls when invoking Abandon() to avoid calling "delete this" from a reentrant | |
45 // call while handling Run(). | |
46 class BaseTimerTaskInternal : public SupportsWeakPtr<BaseTimerTaskInternal> { | |
27 public: | 47 public: |
28 explicit BaseTimerTaskInternal(Timer* timer) | 48 // A helper which invokes a Closure when destroyed. Required to notify |
29 : timer_(timer) { | 49 // BaseTimerTaskInternal when its Run() task is suppressed before it's |
30 } | 50 // scheduled (e.g. because the sequence is shutdown). Note: such cleanups |
31 | 51 // can't be bound directly to ~BaseTimerTaskInternal() as it owns itself (its |
32 ~BaseTimerTaskInternal() { | 52 // tasks use WeakPtrs and deleting them won't trigger |
33 // This task may be getting cleared because the task runner has been | 53 // ~BaseTimerTaskInternal()). |
34 // destructed. If so, don't leave Timer with a dangling pointer | 54 class CleanupTrigger { |
35 // to this. | 55 public: |
36 if (timer_) | 56 ~CleanupTrigger() { std::move(on_destruction_).Run(); } |
37 timer_->StopAndAbandon(); | 57 |
38 } | 58 private: |
39 | 59 friend class BaseTimerTaskInternal; |
40 void Run() { | 60 CleanupTrigger(OnceClosure on_destruction) |
41 // timer_ is NULL if we were abandoned. | 61 : on_destruction_(std::move(on_destruction)) {} |
42 if (!timer_) | 62 |
63 OnceClosure on_destruction_; | |
64 | |
65 DISALLOW_COPY_AND_ASSIGN(CleanupTrigger); | |
66 }; | |
67 | |
68 BaseTimerTaskInternal(const tracked_objects::Location& posted_from, | |
69 const Closure& task, | |
70 TimeDelta delay, | |
71 bool is_repeating, | |
72 TickClock* tick_clock, | |
73 scoped_refptr<SequencedTaskRunner> origin_task_runner, | |
74 const Closure& stop_callback) | |
75 : posted_from_(posted_from), | |
76 task_(task), | |
77 delay_(delay), | |
78 is_repeating_(is_repeating), | |
79 tick_clock_(tick_clock), | |
80 desired_run_time_(Now() + delay_), | |
81 origin_task_runner_(std::move(origin_task_runner)), | |
82 stop_callback_(stop_callback) { | |
83 destination_sequence_checker_.DetachFromSequence(); | |
84 } | |
85 | |
86 ~BaseTimerTaskInternal() = default; | |
87 | |
88 void Run(std::unique_ptr<CleanupTrigger> cleanup_trigger) { | |
89 DCHECK(destination_sequence_checker_.CalledOnValidSequence()); | |
90 | |
91 const TimeDelta delay_remaining = desired_run_time_ - Now(); | |
92 if (delay_remaining > TimeDelta::FromMicroseconds(0)) { | |
93 #if DCHECK_IS_ON() | |
94 DCHECK(was_reset_) << delay_remaining; | |
95 #endif | |
96 SequencedTaskRunnerHandle::Get()->PostDelayedTask( | |
97 posted_from_, base::Bind(&BaseTimerTaskInternal::Run, AsWeakPtr(), | |
98 Passed(std::move(cleanup_trigger))), | |
99 delay_remaining); | |
43 return; | 100 return; |
44 | 101 } |
45 // *this will be deleted by the task runner, so Timer needs to | 102 |
46 // forget us: | 103 // Trigger cleanup before running the |task_|. e.g. to Stop() OneShotTimers |
47 timer_->scheduled_task_ = NULL; | 104 // so that anything posted from the task runs in the context of a stopped |
48 | 105 // timer as expected. |
49 // Although Timer should not call back into *this, let's clear | 106 if (!is_repeating_) |
50 // the timer_ member first to be pedantic. | 107 cleanup_trigger.reset(); |
51 Timer* timer = timer_; | 108 |
52 timer_ = NULL; | 109 task_.Run(); |
53 timer->RunScheduledTask(); | 110 |
54 } | 111 if (is_repeating_) { |
55 | 112 // TODO(gab): Currently rebasing on Now() to match prior status quo but |
56 // The task remains in the MessageLoop queue, but nothing will happen when it | 113 // think it would be better to merely |+= delay_| here to avoid the timer |
57 // runs. | 114 // drifting with the delayed task overhead: http://crbug.com/676836. |
115 desired_run_time_ = Now() + delay_; | |
116 SequencedTaskRunnerHandle::Get()->PostDelayedTask( | |
117 posted_from_, base::Bind(&BaseTimerTaskInternal::Run, AsWeakPtr(), | |
118 Passed(std::move(cleanup_trigger))), | |
119 delay_); | |
120 #if DCHECK_IS_ON() | |
121 was_reset_ = false; | |
122 #endif | |
123 } else { | |
124 delete this; | |
125 } | |
126 } | |
127 | |
128 // Postpones the |desired_run_time_| by |delay_|. | |
129 void Reset() { | |
130 DCHECK(destination_sequence_checker_.CalledOnValidSequence()); | |
131 DCHECK(!delay_.is_zero()); | |
132 | |
133 // Since Reset() is sequenced with Run() (and cancelled via the WeakPtr if | |
134 // Run() is scheduled before it -- i.e. delay expires as Reset() is posted): | |
135 // it is sufficient to merely update |desired_run_time_|. | |
136 desired_run_time_ = Now() + delay_; | |
137 | |
138 #if DCHECK_IS_ON() | |
139 was_reset_ = true; | |
140 #endif | |
141 } | |
142 | |
58 void Abandon() { | 143 void Abandon() { |
59 timer_ = NULL; | 144 DCHECK(destination_sequence_checker_.CalledOnValidSequence()); |
145 | |
146 // Delete self, invalidating WeakPtrs (and as such cancelling the pending | |
147 // Run() task). | |
148 delete this; | |
149 } | |
150 | |
151 std::unique_ptr<CleanupTrigger> CreateCleanupTrigger() { | |
152 // Note: it's important that CleanupTrigger's Closure be bound to a WeakPtr | |
153 // so that it only triggers if the Run() task is suppressed from its | |
154 // sequence while it was live (i.e. it shouldn't trigger when the task is | |
155 // destroyed because the WeakPtr was invalid when an abandoned Run() task | |
156 // was scheduled). | |
157 return WrapUnique( | |
158 new CleanupTrigger(Bind(&BaseTimerTaskInternal::Cleanup, AsWeakPtr()))); | |
60 } | 159 } |
61 | 160 |
62 private: | 161 private: |
63 Timer* timer_; | 162 TimeTicks Now() const { |
163 return tick_clock_ ? tick_clock_->NowTicks() : TimeTicks::Now(); | |
164 } | |
165 | |
166 // Cleans up state on the Timer side when this BaseTimerTaskInternal is done | |
167 // with its role. | |
168 void Cleanup() { | |
169 DCHECK(destination_sequence_checker_.CalledOnValidSequence()); | |
170 | |
171 if (origin_task_runner_->RunsTasksOnCurrentThread()) { | |
172 stop_callback_.Run(); | |
173 } else { | |
174 origin_task_runner_->PostTask(FROM_HERE, stop_callback_); | |
175 } | |
176 } | |
177 | |
178 const tracked_objects::Location posted_from_; | |
179 const Closure task_; | |
180 const TimeDelta delay_; | |
181 const bool is_repeating_; | |
182 | |
183 TickClock* const tick_clock_; | |
184 TimeTicks desired_run_time_; | |
185 | |
186 const scoped_refptr<SequencedTaskRunner> origin_task_runner_; | |
187 const Closure stop_callback_; | |
188 | |
189 // Verifies that every operation (but construction) happens on the sequence | |
190 // where the task is intended to run. | |
191 SequenceChecker destination_sequence_checker_; | |
192 | |
193 #if DCHECK_IS_ON() | |
194 // Used to verify that reposts only occur when a Reset() was involved. | |
195 bool was_reset_ = false; | |
196 #endif | |
197 | |
198 DISALLOW_COPY_AND_ASSIGN(BaseTimerTaskInternal); | |
64 }; | 199 }; |
65 | 200 |
66 Timer::Timer(bool retain_user_task, bool is_repeating) | 201 Timer::Timer(bool retain_user_task, bool is_repeating) |
67 : Timer(retain_user_task, is_repeating, nullptr) {} | 202 : Timer(retain_user_task, is_repeating, nullptr) {} |
68 | 203 |
69 Timer::Timer(bool retain_user_task, bool is_repeating, TickClock* tick_clock) | 204 Timer::Timer(bool retain_user_task, bool is_repeating, TickClock* tick_clock) |
70 : scheduled_task_(nullptr), | 205 : is_repeating_(is_repeating), |
71 thread_id_(0), | |
72 is_repeating_(is_repeating), | |
73 retain_user_task_(retain_user_task), | 206 retain_user_task_(retain_user_task), |
74 tick_clock_(tick_clock), | 207 tick_clock_(tick_clock), |
75 is_running_(false) {} | 208 is_running_(false), |
209 weak_ptr_factory_(this) { | |
210 // It is safe for the timer to be created on a different thread/sequence than | |
211 // the one from which the timer APIs are called. The first call to the | |
212 // checker's CalledOnValidSequence() method will re-bind the checker, and | |
213 // later calls will verify that the same task runner is used. | |
214 origin_sequence_checker_.DetachFromSequence(); | |
215 } | |
76 | 216 |
77 Timer::Timer(const tracked_objects::Location& posted_from, | 217 Timer::Timer(const tracked_objects::Location& posted_from, |
78 TimeDelta delay, | 218 TimeDelta delay, |
79 const base::Closure& user_task, | 219 const base::Closure& user_task, |
80 bool is_repeating) | 220 bool is_repeating) |
81 : Timer(posted_from, delay, user_task, is_repeating, nullptr) {} | 221 : Timer(posted_from, delay, user_task, is_repeating, nullptr) {} |
82 | 222 |
83 Timer::Timer(const tracked_objects::Location& posted_from, | 223 Timer::Timer(const tracked_objects::Location& posted_from, |
84 TimeDelta delay, | 224 TimeDelta delay, |
85 const base::Closure& user_task, | 225 const base::Closure& user_task, |
86 bool is_repeating, | 226 bool is_repeating, |
87 TickClock* tick_clock) | 227 TickClock* tick_clock) |
88 : scheduled_task_(nullptr), | 228 : posted_from_(posted_from), |
89 posted_from_(posted_from), | |
90 delay_(delay), | 229 delay_(delay), |
91 user_task_(user_task), | 230 user_task_(user_task), |
92 thread_id_(0), | |
93 is_repeating_(is_repeating), | 231 is_repeating_(is_repeating), |
94 retain_user_task_(true), | 232 retain_user_task_(true), |
95 tick_clock_(tick_clock), | 233 tick_clock_(tick_clock), |
96 is_running_(false) {} | 234 is_running_(false), |
235 weak_ptr_factory_(this) { | |
236 // See comment in other constructor. | |
237 origin_sequence_checker_.DetachFromSequence(); | |
238 } | |
97 | 239 |
98 Timer::~Timer() { | 240 Timer::~Timer() { |
99 StopAndAbandon(); | 241 // Note: |origin_sequence_checker_| is a SequenceCheckerImpl so this call |
242 // is valid in non-DCHECK builds. | |
243 if (origin_sequence_checker_.CalledOnValidSequence()) { | |
244 Stop(); | |
245 } else { | |
246 // As highlighted in the constructor. It's okay to start the Timer on a | |
247 // different sequence but it must then be sequentially stopped on that | |
248 // sequence as well before it can be deleted on its original sequence. | |
249 DCHECK(!is_running_); | |
250 } | |
100 } | 251 } |
101 | 252 |
102 bool Timer::IsRunning() const { | 253 bool Timer::IsRunning() const { |
254 DCHECK(origin_sequence_checker_.CalledOnValidSequence()); | |
103 return is_running_; | 255 return is_running_; |
104 } | 256 } |
105 | 257 |
106 TimeDelta Timer::GetCurrentDelay() const { | 258 TimeDelta Timer::GetCurrentDelay() const { |
259 DCHECK(origin_sequence_checker_.CalledOnValidSequence()); | |
107 return delay_; | 260 return delay_; |
108 } | 261 } |
109 | 262 |
110 void Timer::SetTaskRunner(scoped_refptr<SingleThreadTaskRunner> task_runner) { | 263 void Timer::SetTaskRunner(scoped_refptr<SequencedTaskRunner> task_runner) { |
111 // Do not allow changing the task runner once something has been scheduled. | 264 DCHECK(origin_sequence_checker_.CalledOnValidSequence()); |
112 DCHECK_EQ(thread_id_, 0); | 265 |
113 task_runner_.swap(task_runner); | 266 // Do not allow changing the task runner while something is scheduled. |
267 DCHECK(!is_running_); | |
268 destination_task_runner_.swap(task_runner); | |
114 } | 269 } |
115 | 270 |
116 void Timer::Start(const tracked_objects::Location& posted_from, | 271 void Timer::Start(const tracked_objects::Location& posted_from, |
117 TimeDelta delay, | 272 TimeDelta delay, |
118 const base::Closure& user_task) { | 273 const base::Closure& user_task) { |
119 SetTaskInfo(posted_from, delay, user_task); | 274 DCHECK(origin_sequence_checker_.CalledOnValidSequence()); |
120 Reset(); | 275 |
276 // Do a best-effort to cancel the previous task, it may still racily fire | |
277 // though. | |
278 AbandonScheduledTask(); | |
279 | |
280 DCHECK(!is_running_); | |
281 posted_from_ = posted_from; | |
282 delay_ = delay; | |
283 user_task_ = user_task; | |
284 | |
285 PostNewScheduledTask(delay_); | |
121 } | 286 } |
122 | 287 |
123 void Timer::Stop() { | 288 void Timer::Stop() { |
124 is_running_ = false; | 289 DCHECK(origin_sequence_checker_.CalledOnValidSequence()); |
290 | |
291 AbandonScheduledTask(); | |
125 if (!retain_user_task_) | 292 if (!retain_user_task_) |
126 user_task_.Reset(); | 293 user_task_.Reset(); |
127 } | 294 } |
128 | 295 |
129 void Timer::Reset() { | 296 void Timer::Reset() { |
297 DCHECK(origin_sequence_checker_.CalledOnValidSequence()); | |
130 DCHECK(!user_task_.is_null()); | 298 DCHECK(!user_task_.is_null()); |
131 | 299 |
132 // If there's no pending task, start one up and return. | 300 if (is_running_) { |
vmpstr
2017/01/12 20:04:22
I kinda prefer
if (!is_running_) {
PostNewSche
gab
2017/01/25 20:14:56
Agreed, done.
| |
133 if (!scheduled_task_) { | 301 if (destination_task_runner_) { |
302 destination_task_runner_->PostTask( | |
303 FROM_HERE, | |
304 base::Bind(&BaseTimerTaskInternal::Reset, scheduled_task_weak_ref_)); | |
305 } else { | |
306 // |is_running_| guarantees a valid pointer when not using an external | |
307 // |destination_task_runner_|. | |
308 DCHECK(scheduled_task_weak_ref_); | |
309 scheduled_task_weak_ref_->Reset(); | |
310 } | |
311 } else { | |
312 // This is required to handle the following use case (when | |
313 // |retain_user_task_ ==true|): Start() => Stop() => Reset(). | |
134 PostNewScheduledTask(delay_); | 314 PostNewScheduledTask(delay_); |
135 return; | |
136 } | 315 } |
137 | |
138 // Set the new desired_run_time_. | |
139 if (delay_ > TimeDelta::FromMicroseconds(0)) | |
140 desired_run_time_ = Now() + delay_; | |
141 else | |
142 desired_run_time_ = TimeTicks(); | |
143 | |
144 // We can use the existing scheduled task if it arrives before the new | |
145 // desired_run_time_. | |
146 if (desired_run_time_ >= scheduled_run_time_) { | |
147 is_running_ = true; | |
148 return; | |
149 } | |
150 | |
151 // We can't reuse the scheduled_task_, so abandon it and post a new one. | |
152 AbandonScheduledTask(); | |
153 PostNewScheduledTask(delay_); | |
154 } | |
155 | |
156 TimeTicks Timer::Now() const { | |
157 return tick_clock_ ? tick_clock_->NowTicks() : TimeTicks::Now(); | |
158 } | |
159 | |
160 void Timer::SetTaskInfo(const tracked_objects::Location& posted_from, | |
161 TimeDelta delay, | |
162 const base::Closure& user_task) { | |
163 posted_from_ = posted_from; | |
164 delay_ = delay; | |
165 user_task_ = user_task; | |
166 } | 316 } |
167 | 317 |
168 void Timer::PostNewScheduledTask(TimeDelta delay) { | 318 void Timer::PostNewScheduledTask(TimeDelta delay) { |
169 DCHECK(scheduled_task_ == NULL); | 319 DCHECK(origin_sequence_checker_.CalledOnValidSequence()); |
320 DCHECK(SequencedTaskRunnerHandle::IsSet()); | |
321 DCHECK(!is_running_); | |
322 | |
323 scoped_refptr<SequencedTaskRunner> destination_task_runner = | |
vmpstr
2017/01/12 20:04:22
Do you need a ref here? Can it be SequencedTaskRun
gab
2017/01/25 20:14:56
I had the same thought and had originally written
| |
324 destination_task_runner_ ? destination_task_runner_ | |
325 : SequencedTaskRunnerHandle::Get(); | |
326 | |
170 is_running_ = true; | 327 is_running_ = true; |
171 scheduled_task_ = new BaseTimerTaskInternal(this); | 328 BaseTimerTaskInternal* scheduled_task = new BaseTimerTaskInternal( |
vmpstr
2017/01/12 20:04:22
Can you add a comment saying that the task will de
gab
2017/01/25 20:14:56
Done.
| |
172 if (delay > TimeDelta::FromMicroseconds(0)) { | 329 posted_from_, user_task_, delay, is_repeating_, tick_clock_, |
173 GetTaskRunner()->PostDelayedTask(posted_from_, | 330 SequencedTaskRunnerHandle::Get(), |
174 base::Bind(&BaseTimerTaskInternal::Run, base::Owned(scheduled_task_)), | 331 base::Bind(&Timer::Stop, weak_ptr_factory_.GetWeakPtr())); |
175 delay); | 332 scheduled_task_weak_ref_ = scheduled_task->AsWeakPtr(); |
176 scheduled_run_time_ = desired_run_time_ = Now() + delay; | 333 destination_task_runner->PostDelayedTask( |
177 } else { | 334 posted_from_, |
178 GetTaskRunner()->PostTask(posted_from_, | 335 base::Bind(&BaseTimerTaskInternal::Run, scheduled_task_weak_ref_, |
179 base::Bind(&BaseTimerTaskInternal::Run, base::Owned(scheduled_task_))); | 336 Passed(scheduled_task->CreateCleanupTrigger())), |
180 scheduled_run_time_ = desired_run_time_ = TimeTicks(); | 337 delay); |
181 } | |
182 // Remember the thread ID that posts the first task -- this will be verified | |
183 // later when the task is abandoned to detect misuse from multiple threads. | |
184 if (!thread_id_) | |
185 thread_id_ = static_cast<int>(PlatformThread::CurrentId()); | |
186 } | |
187 | |
188 scoped_refptr<SingleThreadTaskRunner> Timer::GetTaskRunner() { | |
189 return task_runner_.get() ? task_runner_ : ThreadTaskRunnerHandle::Get(); | |
190 } | 338 } |
191 | 339 |
192 void Timer::AbandonScheduledTask() { | 340 void Timer::AbandonScheduledTask() { |
193 DCHECK(thread_id_ == 0 || | 341 DCHECK(origin_sequence_checker_.CalledOnValidSequence()); |
194 thread_id_ == static_cast<int>(PlatformThread::CurrentId())); | 342 |
195 if (scheduled_task_) { | 343 if (is_running_) { |
vmpstr
2017/01/12 20:04:22
if (!is_running_)
return;
...
gab
2017/01/25 20:14:56
Done.
| |
196 scheduled_task_->Abandon(); | 344 // Toggle |is_running| first to be reentrancy safe, just in case. |
197 scheduled_task_ = NULL; | 345 is_running_ = false; |
346 | |
347 const Closure abandon = | |
348 base::Bind(&BaseTimerTaskInternal::Abandon, scheduled_task_weak_ref_); | |
349 if (destination_task_runner_) { | |
350 destination_task_runner_->PostTask(FROM_HERE, abandon); | |
351 } else if (SequencedTaskRunnerHandle::IsSet()) { | |
352 // Post Abandon() as an asynchronous call to avoid deleting | |
353 // BaseTimerTaskInternal while it handles Run() (reentrancy is possible, | |
354 // e.g. when the task itself deletes the Timer or when a OneShotTimer's | |
355 // BaseTimerTaskInternal::Run() invoked its |stop_callback_| | |
356 // synchronously). | |
357 SequencedTaskRunnerHandle::Get()->PostTask(FROM_HERE, abandon); | |
358 } else { | |
359 // |is_running_| guarantees a valid pointer when not using an external | |
360 // |destination_task_runner_|. | |
361 DCHECK(scheduled_task_weak_ref_); | |
362 // The reentrancy situations described above happen while Run() is active | |
363 // which implies SequencedTaskRunnerHandle::IsSet(). | |
364 // SequencedTaskRunnerHandle not being set typically means this | |
365 // AbandonScheduledTask() was triggered by ~Timer() on shutdown per the | |
366 // sequence being destroyed and releasing unscheduled tasks in which case | |
367 // it's safe to abandon synchronously. | |
368 scheduled_task_weak_ref_->Abandon(); | |
369 } | |
370 scheduled_task_weak_ref_ = nullptr; | |
198 } | 371 } |
199 } | 372 } |
200 | 373 |
201 void Timer::RunScheduledTask() { | |
202 // Task may have been disabled. | |
203 if (!is_running_) | |
204 return; | |
205 | |
206 // First check if we need to delay the task because of a new target time. | |
207 if (desired_run_time_ > scheduled_run_time_) { | |
208 // Now() can be expensive, so only call it if we know the user has changed | |
209 // the desired_run_time_. | |
210 TimeTicks now = Now(); | |
211 // Task runner may have called us late anyway, so only post a continuation | |
212 // task if the desired_run_time_ is in the future. | |
213 if (desired_run_time_ > now) { | |
214 // Post a new task to span the remaining time. | |
215 PostNewScheduledTask(desired_run_time_ - now); | |
216 return; | |
217 } | |
218 } | |
219 | |
220 // Make a local copy of the task to run. The Stop method will reset the | |
221 // user_task_ member if retain_user_task_ is false. | |
222 base::Closure task = user_task_; | |
223 | |
224 if (is_repeating_) | |
225 PostNewScheduledTask(delay_); | |
226 else | |
227 Stop(); | |
228 | |
229 task.Run(); | |
230 | |
231 // No more member accesses here: *this could be deleted at this point. | |
232 } | |
233 | |
234 } // namespace base | 374 } // namespace base |
OLD | NEW |