• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 The Chromium Authors
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/check.h"
12 #include "base/feature_list.h"
13 #include "base/memory/ptr_util.h"
14 #include "base/memory/raw_ptr_exclusion.h"
15 #include "base/memory/ref_counted.h"
16 #include "base/task/sequenced_task_runner.h"
17 #include "base/task/task_features.h"
18 #include "base/threading/platform_thread.h"
19 #include "base/time/tick_clock.h"
20 
21 namespace base {
22 namespace internal {
23 
TimerBase(const Location & posted_from)24 TimerBase::TimerBase(const Location& posted_from) : posted_from_(posted_from) {
25   // It is safe for the timer to be created on a different thread/sequence than
26   // the one from which the timer APIs are called. The first call to the
27   // checker's CalledOnValidSequence() method will re-bind the checker, and
28   // later calls will verify that the same task runner is used.
29   DETACH_FROM_SEQUENCE(sequence_checker_);
30 }
31 
~TimerBase()32 TimerBase::~TimerBase() {
33   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
34   AbandonScheduledTask();
35 }
36 
IsRunning() const37 bool TimerBase::IsRunning() const {
38   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
39   return delayed_task_handle_.IsValid();
40 }
41 
SetTaskRunner(scoped_refptr<SequencedTaskRunner> task_runner)42 void TimerBase::SetTaskRunner(scoped_refptr<SequencedTaskRunner> task_runner) {
43   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
44   DCHECK(task_runner->RunsTasksInCurrentSequence());
45   DCHECK(!IsRunning());
46   task_runner_.swap(task_runner);
47 }
48 
GetTaskRunner()49 scoped_refptr<SequencedTaskRunner> TimerBase::GetTaskRunner() {
50   return task_runner_ ? task_runner_ : SequencedTaskRunner::GetCurrentDefault();
51 }
52 
Stop()53 void TimerBase::Stop() {
54   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
55 
56   AbandonScheduledTask();
57 
58   OnStop();
59   // No more member accesses here: |this| could be deleted after Stop() call.
60 }
61 
AbandonScheduledTask()62 void TimerBase::AbandonScheduledTask() {
63   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
64 
65   if (delayed_task_handle_.IsValid())
66     delayed_task_handle_.CancelTask();
67 
68   // It's safe to destroy or restart Timer on another sequence after the task is
69   // abandoned.
70   DETACH_FROM_SEQUENCE(sequence_checker_);
71 }
72 
DelayTimerBase(const TickClock * tick_clock)73 DelayTimerBase::DelayTimerBase(const TickClock* tick_clock)
74     : tick_clock_(tick_clock) {}
75 
DelayTimerBase(const Location & posted_from,TimeDelta delay,const TickClock * tick_clock)76 DelayTimerBase::DelayTimerBase(const Location& posted_from,
77                                TimeDelta delay,
78                                const TickClock* tick_clock)
79     : TimerBase(posted_from), delay_(delay), tick_clock_(tick_clock) {}
80 
81 DelayTimerBase::~DelayTimerBase() = default;
82 
GetCurrentDelay() const83 TimeDelta DelayTimerBase::GetCurrentDelay() const {
84   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
85   return delay_;
86 }
87 
StartInternal(const Location & posted_from,TimeDelta delay)88 void DelayTimerBase::StartInternal(const Location& posted_from,
89                                    TimeDelta delay) {
90   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
91 
92   posted_from_ = posted_from;
93   delay_ = delay;
94 
95   Reset();
96 }
97 
Reset()98 void DelayTimerBase::Reset() {
99   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
100 
101   EnsureNonNullUserTask();
102 
103   // We can't reuse the |scheduled_task_|, so abandon it and post a new one.
104   AbandonScheduledTask();
105   ScheduleNewTask(delay_);
106 }
107 
ScheduleNewTask(TimeDelta delay)108 void DelayTimerBase::ScheduleNewTask(TimeDelta delay) {
109   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
110   DCHECK(!delayed_task_handle_.IsValid());
111 
112   // Ignore negative deltas.
113   // TODO(pmonette): Fix callers providing negative deltas and ban passing them.
114   if (delay < TimeDelta())
115     delay = TimeDelta();
116 
117   if (!timer_callback_) {
118     timer_callback_ = BindRepeating(&DelayTimerBase::OnScheduledTaskInvoked,
119                                     Unretained(this));
120   }
121   delayed_task_handle_ = GetTaskRunner()->PostCancelableDelayedTask(
122       base::subtle::PostDelayedTaskPassKey(), posted_from_, timer_callback_,
123       delay);
124   desired_run_time_ = Now() + delay;
125 }
126 
Now() const127 TimeTicks DelayTimerBase::Now() const {
128   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
129   return tick_clock_ ? tick_clock_->NowTicks() : TimeTicks::Now();
130 }
131 
OnScheduledTaskInvoked()132 void DelayTimerBase::OnScheduledTaskInvoked() {
133   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
134   DCHECK(!delayed_task_handle_.IsValid()) << posted_from_.ToString();
135 
136   RunUserTask();
137   // No more member accesses here: |this| could be deleted at this point.
138 }
139 
140 }  // namespace internal
141 
142 OneShotTimer::OneShotTimer() = default;
OneShotTimer(const TickClock * tick_clock)143 OneShotTimer::OneShotTimer(const TickClock* tick_clock)
144     : internal::DelayTimerBase(tick_clock) {}
145 OneShotTimer::~OneShotTimer() = default;
146 
Start(const Location & posted_from,TimeDelta delay,OnceClosure user_task)147 void OneShotTimer::Start(const Location& posted_from,
148                          TimeDelta delay,
149                          OnceClosure user_task) {
150   user_task_ = std::move(user_task);
151   StartInternal(posted_from, delay);
152 }
153 
FireNow()154 void OneShotTimer::FireNow() {
155   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
156   DCHECK(!task_runner_) << "FireNow() is incompatible with SetTaskRunner()";
157   DCHECK(IsRunning());
158 
159   RunUserTask();
160 }
161 
OnStop()162 void OneShotTimer::OnStop() {
163   user_task_.Reset();
164   // No more member accesses here: |this| could be deleted after freeing
165   // |user_task_|.
166 }
167 
RunUserTask()168 void OneShotTimer::RunUserTask() {
169   // Make a local copy of the task to run. The Stop method will reset the
170   // |user_task_| member.
171   OnceClosure task = std::move(user_task_);
172   Stop();
173   DCHECK(task);
174   std::move(task).Run();
175   // No more member accesses here: |this| could be deleted at this point.
176 }
177 
EnsureNonNullUserTask()178 void OneShotTimer::EnsureNonNullUserTask() {
179   CHECK(user_task_);
180 }
181 
182 RepeatingTimer::RepeatingTimer() = default;
RepeatingTimer(const TickClock * tick_clock)183 RepeatingTimer::RepeatingTimer(const TickClock* tick_clock)
184     : internal::DelayTimerBase(tick_clock) {}
185 RepeatingTimer::~RepeatingTimer() = default;
186 
RepeatingTimer(const Location & posted_from,TimeDelta delay,RepeatingClosure user_task)187 RepeatingTimer::RepeatingTimer(const Location& posted_from,
188                                TimeDelta delay,
189                                RepeatingClosure user_task)
190     : internal::DelayTimerBase(posted_from, delay),
191       user_task_(std::move(user_task)) {}
RepeatingTimer(const Location & posted_from,TimeDelta delay,RepeatingClosure user_task,const TickClock * tick_clock)192 RepeatingTimer::RepeatingTimer(const Location& posted_from,
193                                TimeDelta delay,
194                                RepeatingClosure user_task,
195                                const TickClock* tick_clock)
196     : internal::DelayTimerBase(posted_from, delay, tick_clock),
197       user_task_(std::move(user_task)) {}
198 
Start(const Location & posted_from,TimeDelta delay,RepeatingClosure user_task)199 void RepeatingTimer::Start(const Location& posted_from,
200                            TimeDelta delay,
201                            RepeatingClosure user_task) {
202   user_task_ = std::move(user_task);
203   StartInternal(posted_from, delay);
204 }
205 
OnStop()206 void RepeatingTimer::OnStop() {}
207 
RunUserTask()208 void RepeatingTimer::RunUserTask() {
209   // Make a local copy of the task to run in case the task destroy the timer
210   // instance.
211   RepeatingClosure task = user_task_;
212   ScheduleNewTask(GetCurrentDelay());
213   task.Run();
214   // No more member accesses here: |this| could be deleted at this point.
215 }
216 
EnsureNonNullUserTask()217 void RepeatingTimer::EnsureNonNullUserTask() {
218   DCHECK(user_task_);
219 }
220 
221 RetainingOneShotTimer::RetainingOneShotTimer() = default;
RetainingOneShotTimer(const TickClock * tick_clock)222 RetainingOneShotTimer::RetainingOneShotTimer(const TickClock* tick_clock)
223     : internal::DelayTimerBase(tick_clock) {}
224 RetainingOneShotTimer::~RetainingOneShotTimer() = default;
225 
RetainingOneShotTimer(const Location & posted_from,TimeDelta delay,RepeatingClosure user_task)226 RetainingOneShotTimer::RetainingOneShotTimer(const Location& posted_from,
227                                              TimeDelta delay,
228                                              RepeatingClosure user_task)
229     : internal::DelayTimerBase(posted_from, delay),
230       user_task_(std::move(user_task)) {}
RetainingOneShotTimer(const Location & posted_from,TimeDelta delay,RepeatingClosure user_task,const TickClock * tick_clock)231 RetainingOneShotTimer::RetainingOneShotTimer(const Location& posted_from,
232                                              TimeDelta delay,
233                                              RepeatingClosure user_task,
234                                              const TickClock* tick_clock)
235     : internal::DelayTimerBase(posted_from, delay, tick_clock),
236       user_task_(std::move(user_task)) {}
237 
Start(const Location & posted_from,TimeDelta delay,RepeatingClosure user_task)238 void RetainingOneShotTimer::Start(const Location& posted_from,
239                                   TimeDelta delay,
240                                   RepeatingClosure user_task) {
241   user_task_ = std::move(user_task);
242   StartInternal(posted_from, delay);
243 }
244 
OnStop()245 void RetainingOneShotTimer::OnStop() {}
246 
RunUserTask()247 void RetainingOneShotTimer::RunUserTask() {
248   // Make a local copy of the task to run in case the task destroys the timer
249   // instance.
250   RepeatingClosure task = user_task_;
251   Stop();
252   task.Run();
253   // No more member accesses here: |this| could be deleted at this point.
254 }
255 
EnsureNonNullUserTask()256 void RetainingOneShotTimer::EnsureNonNullUserTask() {
257   DCHECK(user_task_);
258 }
259 
260 DeadlineTimer::DeadlineTimer() = default;
261 DeadlineTimer::~DeadlineTimer() = default;
262 
Start(const Location & posted_from,TimeTicks deadline,OnceClosure user_task,subtle::DelayPolicy delay_policy)263 void DeadlineTimer::Start(const Location& posted_from,
264                           TimeTicks deadline,
265                           OnceClosure user_task,
266                           subtle::DelayPolicy delay_policy) {
267   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
268   AbandonScheduledTask();
269   user_task_ = std::move(user_task);
270   posted_from_ = posted_from;
271   ScheduleNewTask(deadline, delay_policy);
272 }
273 
OnStop()274 void DeadlineTimer::OnStop() {
275   user_task_.Reset();
276   // No more member accesses here: |this| could be deleted after freeing
277   // |user_task_|.
278 }
279 
ScheduleNewTask(TimeTicks deadline,subtle::DelayPolicy delay_policy)280 void DeadlineTimer::ScheduleNewTask(TimeTicks deadline,
281                                     subtle::DelayPolicy delay_policy) {
282   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
283 
284   if (!timer_callback_) {
285     timer_callback_ =
286         BindRepeating(&DeadlineTimer::OnScheduledTaskInvoked, Unretained(this));
287   }
288   delayed_task_handle_ = GetTaskRunner()->PostCancelableDelayedTaskAt(
289       base::subtle::PostDelayedTaskPassKey(), posted_from_, timer_callback_,
290       deadline, delay_policy);
291 }
292 
OnScheduledTaskInvoked()293 void DeadlineTimer::OnScheduledTaskInvoked() {
294   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
295   DCHECK(!delayed_task_handle_.IsValid());
296 
297   // Make a local copy of the task to run. The Stop method will reset the
298   // |user_task_| member.
299   OnceClosure task = std::move(user_task_);
300   Stop();
301   std::move(task).Run();
302   // No more member accesses here: |this| could be deleted at this point.
303 }
304 
305 MetronomeTimer::MetronomeTimer() = default;
306 MetronomeTimer::~MetronomeTimer() = default;
307 
MetronomeTimer(const Location & posted_from,TimeDelta interval,RepeatingClosure user_task,TimeTicks phase)308 MetronomeTimer::MetronomeTimer(const Location& posted_from,
309                                TimeDelta interval,
310                                RepeatingClosure user_task,
311                                TimeTicks phase)
312     : TimerBase(posted_from),
313       interval_(interval),
314       user_task_(user_task),
315       phase_(phase) {}
316 
Start(const Location & posted_from,TimeDelta interval,RepeatingClosure user_task,TimeTicks phase)317 void MetronomeTimer::Start(const Location& posted_from,
318                            TimeDelta interval,
319                            RepeatingClosure user_task,
320                            TimeTicks phase) {
321   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
322   user_task_ = std::move(user_task);
323   posted_from_ = posted_from;
324   interval_ = interval;
325   phase_ = phase;
326 
327   Reset();
328 }
329 
OnStop()330 void MetronomeTimer::OnStop() {
331   user_task_.Reset();
332   // No more member accesses here: |this| could be deleted after freeing
333   // |user_task_|.
334 }
335 
Reset()336 void MetronomeTimer::Reset() {
337   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
338   DCHECK(user_task_);
339   // We can't reuse the |scheduled_task_|, so abandon it and post a new one.
340   AbandonScheduledTask();
341   ScheduleNewTask();
342 }
343 
ScheduleNewTask()344 void MetronomeTimer::ScheduleNewTask() {
345   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
346 
347   // The next wake up is scheduled at the next aligned time which is at least
348   // `interval_ / 2` after now. `interval_ / 2` is added to avoid playing
349   // "catch-up" if wake ups are late.
350   TimeTicks deadline =
351       (TimeTicks::Now() + interval_ / 2).SnappedToNextTick(phase_, interval_);
352 
353   if (!timer_callback_) {
354     timer_callback_ = BindRepeating(&MetronomeTimer::OnScheduledTaskInvoked,
355                                     Unretained(this));
356   }
357   delayed_task_handle_ = GetTaskRunner()->PostCancelableDelayedTaskAt(
358       base::subtle::PostDelayedTaskPassKey(), posted_from_, timer_callback_,
359       deadline, subtle::DelayPolicy::kPrecise);
360 }
361 
OnScheduledTaskInvoked()362 void MetronomeTimer::OnScheduledTaskInvoked() {
363   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
364   DCHECK(!delayed_task_handle_.IsValid());
365 
366   // Make a local copy of the task to run in case the task destroy the timer
367   // instance.
368   RepeatingClosure task = user_task_;
369   ScheduleNewTask();
370   std::move(task).Run();
371   // No more member accesses here: |this| could be deleted at this point.
372 }
373 
374 }  // namespace base
375