Home | History | Annotate | Download | only in timer
      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     DCHECK(GetTaskRunner()->BelongsToCurrentThread());
    168     thread_id_ = static_cast<int>(PlatformThread::CurrentId());
    169   }
    170 }
    171 
    172 scoped_refptr<SingleThreadTaskRunner> Timer::GetTaskRunner() {
    173   return task_runner_.get() ? task_runner_ : ThreadTaskRunnerHandle::Get();
    174 }
    175 
    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 
    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