 Chromium Code Reviews
 Chromium Code Reviews Issue 2491613004:
  Make base::Timer sequence-friendly.  (Closed)
    
  
    Issue 2491613004:
  Make base::Timer sequence-friendly.  (Closed) 
  | 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 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 | 
| OLD | NEW |