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:
BaseTimerTaskInternal(Timer * timer)26 explicit BaseTimerTaskInternal(Timer* timer)
27 : timer_(timer) {
28 }
29
~BaseTimerTaskInternal()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
Run()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.
Abandon()54 void Abandon() { timer_ = nullptr; }
55
56 private:
57 Timer* timer_;
58
59 DISALLOW_COPY_AND_ASSIGN(BaseTimerTaskInternal);
60 };
61
Timer(bool retain_user_task,bool is_repeating)62 Timer::Timer(bool retain_user_task, bool is_repeating)
63 : Timer(retain_user_task, is_repeating, nullptr) {}
64
Timer(bool retain_user_task,bool is_repeating,const TickClock * tick_clock)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
Timer(const Location & posted_from,TimeDelta delay,const base::Closure & user_task,bool is_repeating)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
Timer(const Location & posted_from,TimeDelta delay,const base::Closure & user_task,bool is_repeating,const TickClock * tick_clock)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
~Timer()103 Timer::~Timer() {
104 DCHECK(origin_sequence_checker_.CalledOnValidSequence());
105 AbandonAndStop();
106 }
107
IsRunning() const108 bool Timer::IsRunning() const {
109 DCHECK(origin_sequence_checker_.CalledOnValidSequence());
110 return is_running_;
111 }
112
GetCurrentDelay() const113 TimeDelta Timer::GetCurrentDelay() const {
114 DCHECK(origin_sequence_checker_.CalledOnValidSequence());
115 return delay_;
116 }
117
SetTaskRunner(scoped_refptr<SequencedTaskRunner> task_runner)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
Start(const Location & posted_from,TimeDelta delay,const base::Closure & user_task)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
Stop()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
Reset()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
Now() const186 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
PostNewScheduledTask(TimeDelta delay)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
GetTaskRunner()217 scoped_refptr<SequencedTaskRunner> Timer::GetTaskRunner() {
218 return task_runner_.get() ? task_runner_ : SequencedTaskRunnerHandle::Get();
219 }
220
AbandonScheduledTask()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
RunScheduledTask()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
FireNow()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