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

Side by Side Diff: base/timer/timer.cc

Issue 2491613004: Make base::Timer sequence-friendly. (Closed)
Patch Set: rebase Created 4 years 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 (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 simple delegate for scheduling a callback to Timer
vmpstr 2016/12/03 01:14:04 s/simple // :) Can you also split up the comment
gab 2016/12/23 20:22:11 :)
22 // Timer in the thread's default task runner. It also handles the following 21 // in the destination task runner. It owns itself on the destination task
23 // edge cases: 22 // runner. If non-repeating, it will self-destruct when it fires otherwise it
24 // - deleted by the task runner. 23 // will re-enqueue until abandoned. If non-repeating it will also invoke
25 // - abandoned (orphaned) by Timer. 24 // |stop_callback| on |origin_task_runner| *before* running |task|. The WeakPtr
26 class BaseTimerTaskInternal { 25 // ensures that the task to trigger a self-destruction cancels all others. As
26 // such, cancellation is racing with the scheduled delayed Run() task but so
vmpstr 2016/12/03 01:14:04 By race here, do you mean that either Abandon will
gab 2016/12/23 20:22:11 I mean that Abandon() is not a delayed task while
27 // would any other cancellation method (e.g. using locks and sharing state with
28 // the origin task runner). An invariant assumed below is that there always is
29 // exactly one pending Run() task associated with this BaseTimerTaskInternal, as
30 // such only Run() needs to repost itself to maintain that invariant until self-
31 // destruction.
32 //
33 // Note: Timer and BaseTimerTaskInternal try to communicate synchronously when
34 // possible (i.e. when SetTaskRunner() wasn't used, which they can verify
35 // through RunsTasksOnCurrentThread()). Timer must also always use asynchronous
36 // calls when invoking Abandon() to avoid calling "delete this" from a reentrant
37 // call while handling Run().
38 class BaseTimerTaskInternal : public SupportsWeakPtr<BaseTimerTaskInternal> {
27 public: 39 public:
28 explicit BaseTimerTaskInternal(Timer* timer) 40 // A helper which invokes a Closure when destroyed. Required to notify
29 : timer_(timer) { 41 // BaseTimerTaskInternal when its Run() task is suppressed before it's
30 } 42 // scheduled (e.g. because the sequence is shutdown). Note: such cleanups
31 43 // can't be bound directly to ~BaseTimerTaskInternal() as it owns itself (its
32 ~BaseTimerTaskInternal() { 44 // tasks use WeakPtrs and deleting them won't trigger
33 // This task may be getting cleared because the task runner has been 45 // ~BaseTimerTaskInternal()).
34 // destructed. If so, don't leave Timer with a dangling pointer 46 class CleanupTrigger {
vmpstr 2016/12/03 01:14:04 Consider adding RunOnDestroy type of class to base
gab 2016/12/23 20:22:11 Whereelse? Tried to code search for it and didn't
35 // to this. 47 public:
36 if (timer_) 48 ~CleanupTrigger() { std::move(on_destruction_).Run(); }
37 timer_->StopAndAbandon(); 49
38 } 50 private:
39 51 friend class BaseTimerTaskInternal;
40 void Run() { 52 CleanupTrigger(OnceClosure on_destruction)
41 // timer_ is NULL if we were abandoned. 53 : on_destruction_(std::move(on_destruction)) {}
42 if (!timer_) 54
55 OnceClosure on_destruction_;
56
57 DISALLOW_COPY_AND_ASSIGN(CleanupTrigger);
58 };
59
60 BaseTimerTaskInternal(const tracked_objects::Location& posted_from,
61 const Closure& task,
62 TimeDelta delay,
63 bool is_repeating,
64 TickClock* tick_clock,
65 scoped_refptr<SequencedTaskRunner> origin_task_runner,
66 const Closure& stop_callback)
67 : posted_from_(posted_from),
68 task_(task),
69 delay_(delay),
70 is_repeating_(is_repeating),
71 tick_clock_(tick_clock),
72 desired_run_time_(Now()),
73 origin_task_runner_(std::move(origin_task_runner)),
74 stop_callback_(stop_callback) {
75 destination_sequence_checker_.DetachFromSequence();
vmpstr 2016/12/03 01:14:04 Can you comment that we need to detatch to ensure
gab 2016/12/23 20:22:11 Right, already have a comment to that effect on |d
76 }
77
78 ~BaseTimerTaskInternal() = default;
79
80 void Run(std::unique_ptr<CleanupTrigger> cleanup_trigger) {
81 DCHECK(destination_sequence_checker_.CalledOnValidSequence());
82
83 const TimeDelta delay_remaining = desired_run_time_ - Now();
84 if (delay_remaining > TimeDelta::FromMicroseconds(0)) {
85 #if DCHECK_IS_ON()
86 DCHECK(was_reset_) << delay_remaining;
87 #endif
88 SequencedTaskRunnerHandle::Get()->PostDelayedTask(
89 posted_from_, base::Bind(&BaseTimerTaskInternal::Run, AsWeakPtr(),
90 Passed(std::move(cleanup_trigger))),
91 delay_remaining);
43 return; 92 return;
44 93 }
45 // *this will be deleted by the task runner, so Timer needs to 94
46 // forget us: 95 // Trigger cleanup before running the |task_|. e.g. to Stop() OneShotTimers
47 timer_->scheduled_task_ = NULL; 96 // so that anything posted from the task runs in the context of a stopped
48 97 // timer as expected.
49 // Although Timer should not call back into *this, let's clear 98 if (!is_repeating_)
50 // the timer_ member first to be pedantic. 99 cleanup_trigger.reset();
51 Timer* timer = timer_; 100
52 timer_ = NULL; 101 task_.Run();
53 timer->RunScheduledTask(); 102
54 } 103 if (is_repeating_) {
55 104 // TODO(gab): Currently rebasing on Now() to match prior status quo but
vmpstr 2016/12/03 01:14:04 Can you file a bug for this and reference it here?
gab 2016/12/23 20:22:10 Done.
56 // The task remains in the MessageLoop queue, but nothing will happen when it 105 // think it would be better to merely |+= delay_| here to avoid the timer
57 // runs. 106 // drifting with the delayed task overhead.
107 desired_run_time_ = Now() + delay_;
108 SequencedTaskRunnerHandle::Get()->PostDelayedTask(
109 posted_from_, base::Bind(&BaseTimerTaskInternal::Run, AsWeakPtr(),
110 Passed(std::move(cleanup_trigger))),
111 delay_);
vmpstr 2016/12/03 01:14:04 was_reset_ = false?
gab 2016/12/23 20:22:10 Done.
112 } else {
113 delete this;
114 }
115 }
116
117 // Postpones the |desired_run_time_| by |delay_|.
118 void Reset() {
119 DCHECK(destination_sequence_checker_.CalledOnValidSequence());
120 DCHECK(!delay_.is_zero());
121
122 // Since this message is sent asynchronously, a few things might occur:
123 // - This is a one-shot timer and Run() hasn't occurred:
124 // postpone |desired_run_time_|, Run() will postpone itself when it
125 // runs.
126 // - This is a one-shot timer and Run() already occurred:
127 // Reset() came in too late and will be canceled via the WeakPtr.
128 // - This is a repeating timer and Run() has occurred zero to many times:
129 // postpone the next |desired_run_time_|.
130 // So overall, either the Reset() came in too late and will be canceled or
vmpstr 2016/12/03 01:14:04 This comment is a bit confusing to me. So does thi
gab 2016/12/23 20:22:11 This whole comment is just meant to say that altho
131 // the |desired_run_time_| merely needs to be postponed.
132
133 desired_run_time_ = Now() + delay_;
134
135 #if DCHECK_IS_ON()
136 was_reset_ = true;
137 #endif
138 }
139
58 void Abandon() { 140 void Abandon() {
59 timer_ = NULL; 141 DCHECK(destination_sequence_checker_.CalledOnValidSequence());
142
143 // Delete self, invalidating WeakPtrs and as such, pending Run()/Reset()
vmpstr 2016/12/03 01:14:04 Here as well, what's a reset task here?
gab 2016/12/23 20:22:11 Actually it can only cancel "the pending Run() tas
144 // tasks.
145 delete this;
146 }
147
148 std::unique_ptr<CleanupTrigger> GetCleanupTrigger() {
vmpstr 2016/12/03 01:14:04 nit: CreateCleanupTrigger
gab 2016/12/23 20:22:10 Done.
149 // Note: it's important that CleanupTrigger's Closure be bound to a WeakPtr
150 // so that it only triggers if the Run() task is suppressed from its
151 // sequence while it was live (i.e. it shouldn't trigger when the task is
152 // destroyed because the WeakPtr was invalid when an abandoned Run() task
153 // was scheduled).
154 return WrapUnique(
155 new CleanupTrigger(Bind(&BaseTimerTaskInternal::Cleanup, AsWeakPtr())));
60 } 156 }
61 157
62 private: 158 private:
63 Timer* timer_; 159 TimeTicks Now() const {
160 return tick_clock_ ? tick_clock_->NowTicks() : TimeTicks::Now();
161 }
162
163 // Cleans up state on the Timer side when this BaseTimerTaskInternal is done
164 // with its role.
165 void Cleanup() {
166 DCHECK(destination_sequence_checker_.CalledOnValidSequence());
167
168 if (origin_task_runner_->RunsTasksOnCurrentThread()) {
169 stop_callback_.Run();
170 } else {
171 origin_task_runner_->PostTask(FROM_HERE, stop_callback_);
172 }
173 }
174
175 const tracked_objects::Location posted_from_;
176 const Closure task_;
177 const TimeDelta delay_;
178 const bool is_repeating_;
179
180 TickClock* tick_clock_;
181 TimeTicks desired_run_time_;
182
183 const scoped_refptr<SequencedTaskRunner> origin_task_runner_;
184 const Closure stop_callback_;
185
186 // Verifies that every operation (but construction) happens on the sequence
187 // where the task is intended to run.
188 SequenceChecker destination_sequence_checker_;
189
190 #if DCHECK_IS_ON()
191 // Used to verify that reposts only occur when a Reset() was involved.
192 bool was_reset_ = false;
193 #endif
194
195 DISALLOW_COPY_AND_ASSIGN(BaseTimerTaskInternal);
64 }; 196 };
65 197
66 Timer::Timer(bool retain_user_task, bool is_repeating) 198 Timer::Timer(bool retain_user_task, bool is_repeating)
67 : Timer(retain_user_task, is_repeating, nullptr) {} 199 : Timer(retain_user_task, is_repeating, nullptr) {}
68 200
69 Timer::Timer(bool retain_user_task, bool is_repeating, TickClock* tick_clock) 201 Timer::Timer(bool retain_user_task, bool is_repeating, TickClock* tick_clock)
70 : scheduled_task_(nullptr), 202 : is_repeating_(is_repeating),
71 thread_id_(0),
72 is_repeating_(is_repeating),
73 retain_user_task_(retain_user_task), 203 retain_user_task_(retain_user_task),
74 tick_clock_(tick_clock), 204 tick_clock_(tick_clock),
75 is_running_(false) {} 205 is_running_(false),
206 weak_ptr_factory_(this) {
207 // It is safe for the timer to be created on a different thread/sequence than
208 // the one from which the timer APIs are called. The first call to the
209 // checker's CalledOnValidSequence() method will re-bind the checker, and
210 // later calls will verify that the same task runner is used.
211 origin_sequence_checker_.DetachFromSequence();
212 }
76 213
77 Timer::Timer(const tracked_objects::Location& posted_from, 214 Timer::Timer(const tracked_objects::Location& posted_from,
78 TimeDelta delay, 215 TimeDelta delay,
79 const base::Closure& user_task, 216 const base::Closure& user_task,
80 bool is_repeating) 217 bool is_repeating)
81 : Timer(posted_from, delay, user_task, is_repeating, nullptr) {} 218 : Timer(posted_from, delay, user_task, is_repeating, nullptr) {}
82 219
83 Timer::Timer(const tracked_objects::Location& posted_from, 220 Timer::Timer(const tracked_objects::Location& posted_from,
84 TimeDelta delay, 221 TimeDelta delay,
85 const base::Closure& user_task, 222 const base::Closure& user_task,
86 bool is_repeating, 223 bool is_repeating,
87 TickClock* tick_clock) 224 TickClock* tick_clock)
88 : scheduled_task_(nullptr), 225 : posted_from_(posted_from),
89 posted_from_(posted_from),
90 delay_(delay), 226 delay_(delay),
91 user_task_(user_task), 227 user_task_(user_task),
92 thread_id_(0),
93 is_repeating_(is_repeating), 228 is_repeating_(is_repeating),
94 retain_user_task_(true), 229 retain_user_task_(true),
95 tick_clock_(tick_clock), 230 tick_clock_(tick_clock),
96 is_running_(false) {} 231 is_running_(false),
232 weak_ptr_factory_(this) {
233 // See comment in other constructor.
234 origin_sequence_checker_.DetachFromSequence();
235 }
97 236
98 Timer::~Timer() { 237 Timer::~Timer() {
99 StopAndAbandon(); 238 if (origin_sequence_checker_.CalledOnValidSequence()) {
vmpstr 2016/12/03 01:14:04 This is changing code behavior based on whether DC
gab 2016/12/23 20:22:10 No it doesn't because the member is a SequenceChec
239 Stop();
240 } else {
241 // As highlighted in the constructor. It's okay to start the Timer on a
242 // different sequence but it must then be sequentially stopped on that
243 // sequence as well before it can be deleted on its original sequence.
244 DCHECK(!is_running_);
245 }
100 } 246 }
101 247
102 bool Timer::IsRunning() const { 248 bool Timer::IsRunning() const {
249 DCHECK(origin_sequence_checker_.CalledOnValidSequence());
103 return is_running_; 250 return is_running_;
104 } 251 }
105 252
106 TimeDelta Timer::GetCurrentDelay() const { 253 TimeDelta Timer::GetCurrentDelay() const {
254 DCHECK(origin_sequence_checker_.CalledOnValidSequence());
107 return delay_; 255 return delay_;
108 } 256 }
109 257
110 void Timer::SetTaskRunner(scoped_refptr<SingleThreadTaskRunner> task_runner) { 258 void Timer::SetTaskRunner(scoped_refptr<SequencedTaskRunner> task_runner) {
111 // Do not allow changing the task runner once something has been scheduled. 259 DCHECK(origin_sequence_checker_.CalledOnValidSequence());
112 DCHECK_EQ(thread_id_, 0); 260
113 task_runner_.swap(task_runner); 261 // Do not allow changing the task runner while something is been scheduled.
vmpstr 2016/12/03 01:14:04 s/been //
gab 2016/12/23 20:22:11 Done.
262 DCHECK(!is_running_);
263 destination_task_runner_ = std::move(task_runner);
114 } 264 }
115 265
116 void Timer::Start(const tracked_objects::Location& posted_from, 266 void Timer::Start(const tracked_objects::Location& posted_from,
117 TimeDelta delay, 267 TimeDelta delay,
118 const base::Closure& user_task) { 268 const base::Closure& user_task) {
269 DCHECK(origin_sequence_checker_.CalledOnValidSequence());
270
271 // Do a best-effort to cancel the previous task, it may still racily fire
272 // though.
273 AbandonScheduledTask();
274
119 SetTaskInfo(posted_from, delay, user_task); 275 SetTaskInfo(posted_from, delay, user_task);
120 Reset(); 276 Reset();
vmpstr 2016/12/03 01:14:04 Is this call always just PostNewScheduledTask?
gab 2016/12/23 20:22:11 Actually yes it is, inlined that instead and added
121 } 277 }
122 278
123 void Timer::Stop() { 279 void Timer::Stop() {
124 is_running_ = false; 280 DCHECK(origin_sequence_checker_.CalledOnValidSequence());
281
282 AbandonScheduledTask();
125 if (!retain_user_task_) 283 if (!retain_user_task_)
126 user_task_.Reset(); 284 user_task_.Reset();
127 } 285 }
128 286
129 void Timer::Reset() { 287 void Timer::Reset() {
288 DCHECK(origin_sequence_checker_.CalledOnValidSequence());
130 DCHECK(!user_task_.is_null()); 289 DCHECK(!user_task_.is_null());
131 290
132 // If there's no pending task, start one up and return. 291 if (is_running_) {
133 if (!scheduled_task_) { 292 if (destination_task_runner_) {
293 destination_task_runner_->PostTask(
294 FROM_HERE,
295 base::Bind(&BaseTimerTaskInternal::Reset, scheduled_task_weak_ref_));
296 } else {
297 // |is_running_| guarantees a valid pointer when not using an external
298 // |destination_task_runner_|.
299 DCHECK(scheduled_task_weak_ref_);
300 scheduled_task_weak_ref_->Reset();
301 }
302 } else {
134 PostNewScheduledTask(delay_); 303 PostNewScheduledTask(delay_);
vmpstr 2016/12/03 01:14:04 It's unclear to me if there other call sites other
gab 2016/12/23 20:22:11 The API is a bit of a mess but yes this is require
135 return;
136 } 304 }
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 } 305 }
155 306
156 TimeTicks Timer::Now() const { 307 TimeTicks Timer::Now() const {
157 return tick_clock_ ? tick_clock_->NowTicks() : TimeTicks::Now(); 308 return tick_clock_ ? tick_clock_->NowTicks() : TimeTicks::Now();
158 } 309 }
159 310
160 void Timer::SetTaskInfo(const tracked_objects::Location& posted_from, 311 void Timer::SetTaskInfo(const tracked_objects::Location& posted_from,
161 TimeDelta delay, 312 TimeDelta delay,
162 const base::Closure& user_task) { 313 const base::Closure& user_task) {
314 DCHECK(origin_sequence_checker_.CalledOnValidSequence());
315 DCHECK(!is_running_);
316
163 posted_from_ = posted_from; 317 posted_from_ = posted_from;
164 delay_ = delay; 318 delay_ = delay;
165 user_task_ = user_task; 319 user_task_ = user_task;
166 } 320 }
167 321
168 void Timer::PostNewScheduledTask(TimeDelta delay) { 322 void Timer::PostNewScheduledTask(TimeDelta delay) {
169 DCHECK(scheduled_task_ == NULL); 323 DCHECK(origin_sequence_checker_.CalledOnValidSequence());
324 DCHECK(SequencedTaskRunnerHandle::IsSet());
325
326 scoped_refptr<SequencedTaskRunner> destination_task_runner =
327 destination_task_runner_ ? destination_task_runner_
328 : SequencedTaskRunnerHandle::Get();
329
170 is_running_ = true; 330 is_running_ = true;
171 scheduled_task_ = new BaseTimerTaskInternal(this); 331 BaseTimerTaskInternal* scheduled_task = new BaseTimerTaskInternal(
172 if (delay > TimeDelta::FromMicroseconds(0)) { 332 posted_from_, user_task_, delay, is_repeating_, tick_clock_,
173 GetTaskRunner()->PostDelayedTask(posted_from_, 333 SequencedTaskRunnerHandle::Get(),
174 base::Bind(&BaseTimerTaskInternal::Run, base::Owned(scheduled_task_)), 334 base::Bind(&Timer::Stop, weak_ptr_factory_.GetWeakPtr()));
175 delay); 335 scheduled_task_weak_ref_ = scheduled_task->AsWeakPtr();
176 scheduled_run_time_ = desired_run_time_ = Now() + delay; 336 destination_task_runner->PostDelayedTask(
177 } else { 337 posted_from_,
178 GetTaskRunner()->PostTask(posted_from_, 338 base::Bind(&BaseTimerTaskInternal::Run, scheduled_task_weak_ref_,
179 base::Bind(&BaseTimerTaskInternal::Run, base::Owned(scheduled_task_))); 339 Passed(scheduled_task->GetCleanupTrigger())),
180 scheduled_run_time_ = desired_run_time_ = TimeTicks(); 340 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 } 341 }
191 342
192 void Timer::AbandonScheduledTask() { 343 void Timer::AbandonScheduledTask() {
193 DCHECK(thread_id_ == 0 || 344 DCHECK(origin_sequence_checker_.CalledOnValidSequence());
194 thread_id_ == static_cast<int>(PlatformThread::CurrentId())); 345
195 if (scheduled_task_) { 346 if (is_running_) {
196 scheduled_task_->Abandon(); 347 // Toggle |is_running| first to be reentrancy safe, just in case.
197 scheduled_task_ = NULL; 348 is_running_ = false;
349
350 const Closure abandon =
351 base::Bind(&BaseTimerTaskInternal::Abandon, scheduled_task_weak_ref_);
352 if (destination_task_runner_) {
353 destination_task_runner_->PostTask(FROM_HERE, abandon);
354 } else if (SequencedTaskRunnerHandle::IsSet()) {
355 // Post Abandon() as an asynchronous call to avoid deleting
356 // BaseTimerTaskInternal while it handles Run() (reentrancy is possible,
357 // e.g. when the task itself deletes the Timer or when a OneShotTimer's
358 // BaseTimerTaskInternal::Run() invoked its |stop_callback_|).
359 SequencedTaskRunnerHandle::Get()->PostTask(FROM_HERE, abandon);
360 } else {
361 // |is_running_| guarantees a valid pointer when not using an external
362 // |destination_task_runner_|.
363 DCHECK(scheduled_task_weak_ref_);
364 // The reentrancy situations described above happen while Run() is active
365 // which implies SequencedTaskRunnerHandle::IsSet().
366 // SequencedTaskRunnerHandle not being set typically means this
367 // AbandonScheduledTask() was triggered by ~Timer() on shutdown in which
368 // case it's safe to synchronously abandon on sequence.
369 scheduled_task_weak_ref_->Abandon();
370 }
371 scheduled_task_weak_ref_ = nullptr;
198 } 372 }
199 } 373 }
200 374
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 375 } // namespace base
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698