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::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 
    103 void Timer::Stop() {
    104   is_running_ = false;
    105   if (!retain_user_task_)
    106     user_task_.Reset();
    107 }
    108 
    109 void 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 
    136 void 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 
    144 void 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 
    164 void 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 
    173 void 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