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 3 // found in the LICENSE file. 4 5 #include "base/timer/timer.h" 6 7 #include <stddef.h> 8 9 #include <utility> 10 11 #include "base/logging.h" 12 #include "base/memory/ptr_util.h" 13 #include "base/memory/ref_counted.h" 14 #include "base/threading/platform_thread.h" 15 #include "base/threading/sequenced_task_runner_handle.h" 16 #include "base/time/tick_clock.h" 17 18 namespace base { 19 20 // BaseTimerTaskInternal is a simple delegate for scheduling a callback to Timer 21 // on the current sequence. It also handles the following edge cases: 22 // - deleted by the task runner. 23 // - abandoned (orphaned) by Timer. 24 class BaseTimerTaskInternal { 25 public: 26 explicit BaseTimerTaskInternal(Timer* timer) 27 : timer_(timer) { 28 } 29 30 ~BaseTimerTaskInternal() { 31 // This task may be getting cleared because the task runner has been 32 // destructed. If so, don't leave Timer with a dangling pointer 33 // to this. 34 if (timer_) 35 timer_->AbandonAndStop(); 36 } 37 38 void Run() { 39 // |timer_| is nullptr if we were abandoned. 40 if (!timer_) 41 return; 42 43 // |this| will be deleted by the task runner, so Timer needs to forget us: 44 timer_->scheduled_task_ = nullptr; 45 46 // Although Timer should not call back into |this|, let's clear |timer_| 47 // first to be pedantic. 48 Timer* timer = timer_; 49 timer_ = nullptr; 50 timer->RunScheduledTask(); 51 } 52 53 // The task remains in the queue, but nothing will happen when it runs. 54 void Abandon() { timer_ = nullptr; } 55 56 private: 57 Timer* timer_; 58 59 DISALLOW_COPY_AND_ASSIGN(BaseTimerTaskInternal); 60 }; 61 62 Timer::Timer(bool retain_user_task, bool is_repeating) 63 : Timer(retain_user_task, is_repeating, nullptr) {} 64 65 Timer::Timer(bool retain_user_task, 66 bool is_repeating, 67 const TickClock* tick_clock) 68 : scheduled_task_(nullptr), 69 is_repeating_(is_repeating), 70 retain_user_task_(retain_user_task), 71 tick_clock_(tick_clock), 72 is_running_(false) { 73 // It is safe for the timer to be created on a different thread/sequence than 74 // the one from which the timer APIs are called. The first call to the 75 // checker's CalledOnValidSequence() method will re-bind the checker, and 76 // later calls will verify that the same task runner is used. 77 origin_sequence_checker_.DetachFromSequence(); 78 } 79 80 Timer::Timer(const Location& posted_from, 81 TimeDelta delay, 82 const base::Closure& user_task, 83 bool is_repeating) 84 : Timer(posted_from, delay, user_task, is_repeating, nullptr) {} 85 86 Timer::Timer(const Location& posted_from, 87 TimeDelta delay, 88 const base::Closure& user_task, 89 bool is_repeating, 90 const TickClock* tick_clock) 91 : scheduled_task_(nullptr), 92 posted_from_(posted_from), 93 delay_(delay), 94 user_task_(user_task), 95 is_repeating_(is_repeating), 96 retain_user_task_(true), 97 tick_clock_(tick_clock), 98 is_running_(false) { 99 // See comment in other constructor. 100 origin_sequence_checker_.DetachFromSequence(); 101 } 102 103 Timer::~Timer() { 104 DCHECK(origin_sequence_checker_.CalledOnValidSequence()); 105 AbandonAndStop(); 106 } 107 108 bool Timer::IsRunning() const { 109 DCHECK(origin_sequence_checker_.CalledOnValidSequence()); 110 return is_running_; 111 } 112 113 TimeDelta Timer::GetCurrentDelay() const { 114 DCHECK(origin_sequence_checker_.CalledOnValidSequence()); 115 return delay_; 116 } 117 118 void Timer::SetTaskRunner(scoped_refptr<SequencedTaskRunner> task_runner) { 119 // Do not allow changing the task runner when the Timer is running. 120 // Don't check for |origin_sequence_checker_.CalledOnValidSequence()| here to 121 // allow the use case of constructing the Timer and immediatetly invoking 122 // SetTaskRunner() before starting it (CalledOnValidSequence() would undo the 123 // DetachFromSequence() from the constructor). The |!is_running| check kind of 124 // verifies the same thing (and TSAN should catch callers that do it wrong but 125 // somehow evade all debug checks). 126 DCHECK(!is_running_); 127 task_runner_.swap(task_runner); 128 } 129 130 void Timer::Start(const Location& posted_from, 131 TimeDelta delay, 132 const base::Closure& user_task) { 133 DCHECK(origin_sequence_checker_.CalledOnValidSequence()); 134 135 posted_from_ = posted_from; 136 delay_ = delay; 137 user_task_ = user_task; 138 139 Reset(); 140 } 141 142 void Timer::Stop() { 143 // TODO(gab): Enable this when it's no longer called racily from 144 // RunScheduledTask(): https://crbug.com/587199. 145 // DCHECK(origin_sequence_checker_.CalledOnValidSequence()); 146 147 is_running_ = false; 148 149 // It's safe to destroy or restart Timer on another sequence after Stop(). 150 origin_sequence_checker_.DetachFromSequence(); 151 152 if (!retain_user_task_) 153 user_task_.Reset(); 154 // No more member accesses here: |this| could be deleted after freeing 155 // |user_task_|. 156 } 157 158 void Timer::Reset() { 159 DCHECK(origin_sequence_checker_.CalledOnValidSequence()); 160 DCHECK(!user_task_.is_null()); 161 162 // If there's no pending task, start one up and return. 163 if (!scheduled_task_) { 164 PostNewScheduledTask(delay_); 165 return; 166 } 167 168 // Set the new |desired_run_time_|. 169 if (delay_ > TimeDelta::FromMicroseconds(0)) 170 desired_run_time_ = Now() + delay_; 171 else 172 desired_run_time_ = TimeTicks(); 173 174 // We can use the existing scheduled task if it arrives before the new 175 // |desired_run_time_|. 176 if (desired_run_time_ >= scheduled_run_time_) { 177 is_running_ = true; 178 return; 179 } 180 181 // We can't reuse the |scheduled_task_|, so abandon it and post a new one. 182 AbandonScheduledTask(); 183 PostNewScheduledTask(delay_); 184 } 185 186 TimeTicks Timer::Now() const { 187 // TODO(gab): Enable this when it's no longer called racily from 188 // RunScheduledTask(): https://crbug.com/587199. 189 // DCHECK(origin_sequence_checker_.CalledOnValidSequence()); 190 return tick_clock_ ? tick_clock_->NowTicks() : TimeTicks::Now(); 191 } 192 193 void Timer::PostNewScheduledTask(TimeDelta delay) { 194 // TODO(gab): Enable this when it's no longer called racily from 195 // RunScheduledTask(): https://crbug.com/587199. 196 // DCHECK(origin_sequence_checker_.CalledOnValidSequence()); 197 DCHECK(!scheduled_task_); 198 is_running_ = true; 199 scheduled_task_ = new BaseTimerTaskInternal(this); 200 if (delay > TimeDelta::FromMicroseconds(0)) { 201 // TODO(gab): Posting BaseTimerTaskInternal::Run to another sequence makes 202 // this code racy. https://crbug.com/587199 203 GetTaskRunner()->PostDelayedTask( 204 posted_from_, 205 base::BindOnce(&BaseTimerTaskInternal::Run, 206 base::Owned(scheduled_task_)), 207 delay); 208 scheduled_run_time_ = desired_run_time_ = Now() + delay; 209 } else { 210 GetTaskRunner()->PostTask(posted_from_, 211 base::BindOnce(&BaseTimerTaskInternal::Run, 212 base::Owned(scheduled_task_))); 213 scheduled_run_time_ = desired_run_time_ = TimeTicks(); 214 } 215 } 216 217 scoped_refptr<SequencedTaskRunner> Timer::GetTaskRunner() { 218 return task_runner_.get() ? task_runner_ : SequencedTaskRunnerHandle::Get(); 219 } 220 221 void Timer::AbandonScheduledTask() { 222 // TODO(gab): Enable this when it's no longer called racily from 223 // RunScheduledTask() -> Stop(): https://crbug.com/587199. 224 // DCHECK(origin_sequence_checker_.CalledOnValidSequence()); 225 if (scheduled_task_) { 226 scheduled_task_->Abandon(); 227 scheduled_task_ = nullptr; 228 } 229 } 230 231 void Timer::RunScheduledTask() { 232 // TODO(gab): Enable this when it's no longer called racily: 233 // https://crbug.com/587199. 234 // DCHECK(origin_sequence_checker_.CalledOnValidSequence()); 235 236 // Task may have been disabled. 237 if (!is_running_) 238 return; 239 240 // First check if we need to delay the task because of a new target time. 241 if (desired_run_time_ > scheduled_run_time_) { 242 // Now() can be expensive, so only call it if we know the user has changed 243 // the |desired_run_time_|. 244 TimeTicks now = Now(); 245 // Task runner may have called us late anyway, so only post a continuation 246 // task if the |desired_run_time_| is in the future. 247 if (desired_run_time_ > now) { 248 // Post a new task to span the remaining time. 249 PostNewScheduledTask(desired_run_time_ - now); 250 return; 251 } 252 } 253 254 // Make a local copy of the task to run. The Stop method will reset the 255 // |user_task_| member if |retain_user_task_| is false. 256 base::Closure task = user_task_; 257 258 if (is_repeating_) 259 PostNewScheduledTask(delay_); 260 else 261 Stop(); 262 263 task.Run(); 264 265 // No more member accesses here: |this| could be deleted at this point. 266 } 267 268 void OneShotTimer::FireNow() { 269 DCHECK(origin_sequence_checker_.CalledOnValidSequence()); 270 DCHECK(!task_runner_) << "FireNow() is incompatible with SetTaskRunner()"; 271 DCHECK(IsRunning()); 272 273 OnceClosure task = user_task(); 274 Stop(); 275 DCHECK(!user_task()); 276 std::move(task).Run(); 277 } 278 279 } // namespace base 280