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