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