15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/timer/timer.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <stddef.h>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/memory/ref_counted.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/single_thread_task_runner.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/thread_task_runner_handle.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/threading/platform_thread.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace base {
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// BaseTimerTaskInternal is a simple delegate for scheduling a callback to
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Timer in the thread's default task runner. It also handles the following
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// edge cases:
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// - deleted by the task runner.
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// - abandoned (orphaned) by Timer.
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class BaseTimerTaskInternal {
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  explicit BaseTimerTaskInternal(Timer* timer)
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      : timer_(timer) {
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ~BaseTimerTaskInternal() {
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // This task may be getting cleared because the task runner has been
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // destructed.  If so, don't leave Timer with a dangling pointer
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // to this.
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (timer_)
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      timer_->StopAndAbandon();
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void Run() {
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // timer_ is NULL if we were abandoned.
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!timer_)
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // *this will be deleted by the task runner, so Timer needs to
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // forget us:
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    timer_->scheduled_task_ = NULL;
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Although Timer should not call back into *this, let's clear
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // the timer_ member first to be pedantic.
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Timer* timer = timer_;
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    timer_ = NULL;
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    timer->RunScheduledTask();
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The task remains in the MessageLoop queue, but nothing will happen when it
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // runs.
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void Abandon() {
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    timer_ = NULL;
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Timer* timer_;
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Timer::Timer(bool retain_user_task, bool is_repeating)
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : scheduled_task_(NULL),
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      thread_id_(0),
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      is_repeating_(is_repeating),
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      retain_user_task_(retain_user_task),
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      is_running_(false) {
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Timer::Timer(const tracked_objects::Location& posted_from,
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             TimeDelta delay,
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             const base::Closure& user_task,
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             bool is_repeating)
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : scheduled_task_(NULL),
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      posted_from_(posted_from),
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      delay_(delay),
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      user_task_(user_task),
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      thread_id_(0),
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      is_repeating_(is_repeating),
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      retain_user_task_(true),
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      is_running_(false) {
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Timer::~Timer() {
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  StopAndAbandon();
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool Timer::IsRunning() const {
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return is_running_;
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TimeDelta Timer::GetCurrentDelay() const {
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return delay_;
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void Timer::Start(const tracked_objects::Location& posted_from,
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  TimeDelta delay,
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  const base::Closure& user_task) {
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetTaskInfo(posted_from, delay, user_task);
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Reset();
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void Timer::Stop() {
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  is_running_ = false;
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!retain_user_task_)
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    user_task_.Reset();
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void Timer::Reset() {
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!user_task_.is_null());
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If there's no pending task, start one up and return.
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!scheduled_task_) {
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    PostNewScheduledTask(delay_);
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Set the new desired_run_time_.
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (delay_ > TimeDelta::FromMicroseconds(0))
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    desired_run_time_ = TimeTicks::Now() + delay_;
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    desired_run_time_ = TimeTicks();
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // 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