• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "base/logging.h"
10 #include "base/memory/ref_counted.h"
11 #include "base/single_thread_task_runner.h"
12 #include "base/threading/platform_thread.h"
13 #include "base/threading/thread_task_runner_handle.h"
14 
15 namespace base {
16 
17 // BaseTimerTaskInternal is a simple delegate for scheduling a callback to
18 // Timer in the thread's default task runner. It also handles the following
19 // edge cases:
20 // - deleted by the task runner.
21 // - abandoned (orphaned) by Timer.
22 class BaseTimerTaskInternal {
23  public:
BaseTimerTaskInternal(Timer * timer)24   explicit BaseTimerTaskInternal(Timer* timer)
25       : timer_(timer) {
26   }
27 
~BaseTimerTaskInternal()28   ~BaseTimerTaskInternal() {
29     // This task may be getting cleared because the task runner has been
30     // destructed.  If so, don't leave Timer with a dangling pointer
31     // to this.
32     if (timer_)
33       timer_->StopAndAbandon();
34   }
35 
Run()36   void Run() {
37     // timer_ is NULL if we were abandoned.
38     if (!timer_)
39       return;
40 
41     // *this will be deleted by the task runner, so Timer needs to
42     // forget us:
43     timer_->scheduled_task_ = NULL;
44 
45     // Although Timer should not call back into *this, let's clear
46     // the timer_ member first to be pedantic.
47     Timer* timer = timer_;
48     timer_ = NULL;
49     timer->RunScheduledTask();
50   }
51 
52   // The task remains in the MessageLoop queue, but nothing will happen when it
53   // runs.
Abandon()54   void Abandon() {
55     timer_ = NULL;
56   }
57 
58  private:
59   Timer* timer_;
60 };
61 
Timer(bool retain_user_task,bool is_repeating)62 Timer::Timer(bool retain_user_task, bool is_repeating)
63     : scheduled_task_(NULL),
64       thread_id_(0),
65       is_repeating_(is_repeating),
66       retain_user_task_(retain_user_task),
67       is_running_(false) {
68 }
69 
Timer(const tracked_objects::Location & posted_from,TimeDelta delay,const base::Closure & user_task,bool is_repeating)70 Timer::Timer(const tracked_objects::Location& posted_from,
71              TimeDelta delay,
72              const base::Closure& user_task,
73              bool is_repeating)
74     : scheduled_task_(NULL),
75       posted_from_(posted_from),
76       delay_(delay),
77       user_task_(user_task),
78       thread_id_(0),
79       is_repeating_(is_repeating),
80       retain_user_task_(true),
81       is_running_(false) {
82 }
83 
~Timer()84 Timer::~Timer() {
85   StopAndAbandon();
86 }
87 
IsRunning() const88 bool Timer::IsRunning() const {
89   return is_running_;
90 }
91 
GetCurrentDelay() const92 TimeDelta Timer::GetCurrentDelay() const {
93   return delay_;
94 }
95 
SetTaskRunner(scoped_refptr<SingleThreadTaskRunner> task_runner)96 void Timer::SetTaskRunner(scoped_refptr<SingleThreadTaskRunner> task_runner) {
97   // Do not allow changing the task runner once something has been scheduled.
98   DCHECK_EQ(thread_id_, 0);
99   task_runner_.swap(task_runner);
100 }
101 
Start(const tracked_objects::Location & posted_from,TimeDelta delay,const base::Closure & user_task)102 void Timer::Start(const tracked_objects::Location& posted_from,
103                   TimeDelta delay,
104                   const base::Closure& user_task) {
105   SetTaskInfo(posted_from, delay, user_task);
106   Reset();
107 }
108 
Stop()109 void Timer::Stop() {
110   is_running_ = false;
111   if (!retain_user_task_)
112     user_task_.Reset();
113 }
114 
Reset()115 void Timer::Reset() {
116   DCHECK(!user_task_.is_null());
117 
118   // If there's no pending task, start one up and return.
119   if (!scheduled_task_) {
120     PostNewScheduledTask(delay_);
121     return;
122   }
123 
124   // Set the new desired_run_time_.
125   if (delay_ > TimeDelta::FromMicroseconds(0))
126     desired_run_time_ = TimeTicks::Now() + delay_;
127   else
128     desired_run_time_ = TimeTicks();
129 
130   // We can use the existing scheduled task if it arrives before the new
131   // desired_run_time_.
132   if (desired_run_time_ >= scheduled_run_time_) {
133     is_running_ = true;
134     return;
135   }
136 
137   // We can't reuse the scheduled_task_, so abandon it and post a new one.
138   AbandonScheduledTask();
139   PostNewScheduledTask(delay_);
140 }
141 
SetTaskInfo(const tracked_objects::Location & posted_from,TimeDelta delay,const base::Closure & user_task)142 void Timer::SetTaskInfo(const tracked_objects::Location& posted_from,
143                         TimeDelta delay,
144                         const base::Closure& user_task) {
145   posted_from_ = posted_from;
146   delay_ = delay;
147   user_task_ = user_task;
148 }
149 
PostNewScheduledTask(TimeDelta delay)150 void Timer::PostNewScheduledTask(TimeDelta delay) {
151   DCHECK(scheduled_task_ == NULL);
152   is_running_ = true;
153   scheduled_task_ = new BaseTimerTaskInternal(this);
154   if (delay > TimeDelta::FromMicroseconds(0)) {
155     GetTaskRunner()->PostDelayedTask(posted_from_,
156         base::Bind(&BaseTimerTaskInternal::Run, base::Owned(scheduled_task_)),
157         delay);
158     scheduled_run_time_ = desired_run_time_ = TimeTicks::Now() + delay;
159   } else {
160     GetTaskRunner()->PostTask(posted_from_,
161         base::Bind(&BaseTimerTaskInternal::Run, base::Owned(scheduled_task_)));
162     scheduled_run_time_ = desired_run_time_ = TimeTicks();
163   }
164   // Remember the thread ID that posts the first task -- this will be verified
165   // later when the task is abandoned to detect misuse from multiple threads.
166   if (!thread_id_) {
167     DCHECK(GetTaskRunner()->BelongsToCurrentThread());
168     thread_id_ = static_cast<int>(PlatformThread::CurrentId());
169   }
170 }
171 
GetTaskRunner()172 scoped_refptr<SingleThreadTaskRunner> Timer::GetTaskRunner() {
173   return task_runner_.get() ? task_runner_ : ThreadTaskRunnerHandle::Get();
174 }
175 
AbandonScheduledTask()176 void Timer::AbandonScheduledTask() {
177   DCHECK(thread_id_ == 0 ||
178          thread_id_ == static_cast<int>(PlatformThread::CurrentId()));
179   if (scheduled_task_) {
180     scheduled_task_->Abandon();
181     scheduled_task_ = NULL;
182   }
183 }
184 
RunScheduledTask()185 void Timer::RunScheduledTask() {
186   // Task may have been disabled.
187   if (!is_running_)
188     return;
189 
190   // First check if we need to delay the task because of a new target time.
191   if (desired_run_time_ > scheduled_run_time_) {
192     // TimeTicks::Now() can be expensive, so only call it if we know the user
193     // has changed the desired_run_time_.
194     TimeTicks now = TimeTicks::Now();
195     // Task runner may have called us late anyway, so only post a continuation
196     // task if the desired_run_time_ is in the future.
197     if (desired_run_time_ > now) {
198       // Post a new task to span the remaining time.
199       PostNewScheduledTask(desired_run_time_ - now);
200       return;
201     }
202   }
203 
204   // Make a local copy of the task to run. The Stop method will reset the
205   // user_task_ member if retain_user_task_ is false.
206   base::Closure task = user_task_;
207 
208   if (is_repeating_)
209     PostNewScheduledTask(delay_);
210   else
211     Stop();
212 
213   task.Run();
214 
215   // No more member accesses here: *this could be deleted at this point.
216 }
217 
218 }  // namespace base
219