Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / base / timer / timer.cc
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/thread_task_runner_handle.h"
13 #include "base/threading/platform_thread.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:
24   explicit BaseTimerTaskInternal(Timer* timer)
25       : timer_(timer) {
26   }
27
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
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.
54   void Abandon() {
55     timer_ = NULL;
56   }
57
58  private:
59   Timer* timer_;
60 };
61
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
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
84 Timer::~Timer() {
85   StopAndAbandon();
86 }
87
88 bool Timer::IsRunning() const {
89   return is_running_;
90 }
91
92 TimeDelta Timer::GetCurrentDelay() const {
93   return delay_;
94 }
95
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
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
109 void Timer::Stop() {
110   is_running_ = false;
111   if (!retain_user_task_)
112     user_task_.Reset();
113 }
114
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
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
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     thread_id_ = static_cast<int>(PlatformThread::CurrentId());
168 }
169
170 scoped_refptr<SingleThreadTaskRunner> Timer::GetTaskRunner() {
171   return task_runner_.get() ? task_runner_ : ThreadTaskRunnerHandle::Get();
172 }
173
174 void Timer::AbandonScheduledTask() {
175   DCHECK(thread_id_ == 0 ||
176          thread_id_ == static_cast<int>(PlatformThread::CurrentId()));
177   if (scheduled_task_) {
178     scheduled_task_->Abandon();
179     scheduled_task_ = NULL;
180   }
181 }
182
183 void Timer::RunScheduledTask() {
184   // Task may have been disabled.
185   if (!is_running_)
186     return;
187
188   // First check if we need to delay the task because of a new target time.
189   if (desired_run_time_ > scheduled_run_time_) {
190     // TimeTicks::Now() can be expensive, so only call it if we know the user
191     // has changed the desired_run_time_.
192     TimeTicks now = TimeTicks::Now();
193     // Task runner may have called us late anyway, so only post a continuation
194     // task if the desired_run_time_ is in the future.
195     if (desired_run_time_ > now) {
196       // Post a new task to span the remaining time.
197       PostNewScheduledTask(desired_run_time_ - now);
198       return;
199     }
200   }
201
202   // Make a local copy of the task to run. The Stop method will reset the
203   // user_task_ member if retain_user_task_ is false.
204   base::Closure task = user_task_;
205
206   if (is_repeating_)
207     PostNewScheduledTask(delay_);
208   else
209     Stop();
210
211   task.Run();
212
213   // No more member accesses here: *this could be deleted at this point.
214 }
215
216 }  // namespace base