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
15namespace 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.
22class 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
62Timer::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
70Timer::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
84Timer::~Timer() {
85  StopAndAbandon();
86}
87
88bool Timer::IsRunning() const {
89  return is_running_;
90}
91
92TimeDelta Timer::GetCurrentDelay() const {
93  return delay_;
94}
95
96void Timer::Start(const tracked_objects::Location& posted_from,
97                  TimeDelta delay,
98                  const base::Closure& user_task) {
99  SetTaskInfo(posted_from, delay, user_task);
100  Reset();
101}
102
103void Timer::Stop() {
104  is_running_ = false;
105  if (!retain_user_task_)
106    user_task_.Reset();
107}
108
109void Timer::Reset() {
110  DCHECK(!user_task_.is_null());
111
112  // If there's no pending task, start one up and return.
113  if (!scheduled_task_) {
114    PostNewScheduledTask(delay_);
115    return;
116  }
117
118  // Set the new desired_run_time_.
119  if (delay_ > TimeDelta::FromMicroseconds(0))
120    desired_run_time_ = TimeTicks::Now() + delay_;
121  else
122    desired_run_time_ = TimeTicks();
123
124  // We can use the existing scheduled task if it arrives before the new
125  // desired_run_time_.
126  if (desired_run_time_ >= scheduled_run_time_) {
127    is_running_ = true;
128    return;
129  }
130
131  // We can't reuse the scheduled_task_, so abandon it and post a new one.
132  AbandonScheduledTask();
133  PostNewScheduledTask(delay_);
134}
135
136void Timer::SetTaskInfo(const tracked_objects::Location& posted_from,
137                        TimeDelta delay,
138                        const base::Closure& user_task) {
139  posted_from_ = posted_from;
140  delay_ = delay;
141  user_task_ = user_task;
142}
143
144void Timer::PostNewScheduledTask(TimeDelta delay) {
145  DCHECK(scheduled_task_ == NULL);
146  is_running_ = true;
147  scheduled_task_ = new BaseTimerTaskInternal(this);
148  if (delay > TimeDelta::FromMicroseconds(0)) {
149    ThreadTaskRunnerHandle::Get()->PostDelayedTask(posted_from_,
150        base::Bind(&BaseTimerTaskInternal::Run, base::Owned(scheduled_task_)),
151        delay);
152    scheduled_run_time_ = desired_run_time_ = TimeTicks::Now() + delay;
153  } else {
154    ThreadTaskRunnerHandle::Get()->PostTask(posted_from_,
155        base::Bind(&BaseTimerTaskInternal::Run, base::Owned(scheduled_task_)));
156    scheduled_run_time_ = desired_run_time_ = TimeTicks();
157  }
158  // Remember the thread ID that posts the first task -- this will be verified
159  // later when the task is abandoned to detect misuse from multiple threads.
160  if (!thread_id_)
161    thread_id_ = static_cast<int>(PlatformThread::CurrentId());
162}
163
164void Timer::AbandonScheduledTask() {
165  DCHECK(thread_id_ == 0 ||
166         thread_id_ == static_cast<int>(PlatformThread::CurrentId()));
167  if (scheduled_task_) {
168    scheduled_task_->Abandon();
169    scheduled_task_ = NULL;
170  }
171}
172
173void Timer::RunScheduledTask() {
174  // Task may have been disabled.
175  if (!is_running_)
176    return;
177
178  // First check if we need to delay the task because of a new target time.
179  if (desired_run_time_ > scheduled_run_time_) {
180    // TimeTicks::Now() can be expensive, so only call it if we know the user
181    // has changed the desired_run_time_.
182    TimeTicks now = TimeTicks::Now();
183    // Task runner may have called us late anyway, so only post a continuation
184    // task if the desired_run_time_ is in the future.
185    if (desired_run_time_ > now) {
186      // Post a new task to span the remaining time.
187      PostNewScheduledTask(desired_run_time_ - now);
188      return;
189    }
190  }
191
192  // Make a local copy of the task to run. The Stop method will reset the
193  // user_task_ member if retain_user_task_ is false.
194  base::Closure task = user_task_;
195
196  if (is_repeating_)
197    PostNewScheduledTask(delay_);
198  else
199    Stop();
200
201  task.Run();
202
203  // No more member accesses here: *this could be deleted at this point.
204}
205
206}  // namespace base
207