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 <utility>
     10 
     11 #include "base/logging.h"
     12 #include "base/memory/ptr_util.h"
     13 #include "base/memory/ref_counted.h"
     14 #include "base/threading/platform_thread.h"
     15 #include "base/threading/sequenced_task_runner_handle.h"
     16 #include "base/time/tick_clock.h"
     17 
     18 namespace base {
     19 
     20 // BaseTimerTaskInternal is a simple delegate for scheduling a callback to Timer
     21 // on the current sequence. It also handles the following edge cases:
     22 // - deleted by the task runner.
     23 // - abandoned (orphaned) by Timer.
     24 class BaseTimerTaskInternal {
     25  public:
     26   explicit BaseTimerTaskInternal(Timer* timer)
     27       : timer_(timer) {
     28   }
     29 
     30   ~BaseTimerTaskInternal() {
     31     // This task may be getting cleared because the task runner has been
     32     // destructed.  If so, don't leave Timer with a dangling pointer
     33     // to this.
     34     if (timer_)
     35       timer_->AbandonAndStop();
     36   }
     37 
     38   void Run() {
     39     // |timer_| is nullptr if we were abandoned.
     40     if (!timer_)
     41       return;
     42 
     43     // |this| will be deleted by the task runner, so Timer needs to forget us:
     44     timer_->scheduled_task_ = nullptr;
     45 
     46     // Although Timer should not call back into |this|, let's clear |timer_|
     47     // first to be pedantic.
     48     Timer* timer = timer_;
     49     timer_ = nullptr;
     50     timer->RunScheduledTask();
     51   }
     52 
     53   // The task remains in the queue, but nothing will happen when it runs.
     54   void Abandon() { timer_ = nullptr; }
     55 
     56  private:
     57   Timer* timer_;
     58 
     59   DISALLOW_COPY_AND_ASSIGN(BaseTimerTaskInternal);
     60 };
     61 
     62 Timer::Timer(bool retain_user_task, bool is_repeating)
     63     : Timer(retain_user_task, is_repeating, nullptr) {}
     64 
     65 Timer::Timer(bool retain_user_task,
     66              bool is_repeating,
     67              const TickClock* tick_clock)
     68     : scheduled_task_(nullptr),
     69       is_repeating_(is_repeating),
     70       retain_user_task_(retain_user_task),
     71       tick_clock_(tick_clock),
     72       is_running_(false) {
     73   // It is safe for the timer to be created on a different thread/sequence than
     74   // the one from which the timer APIs are called. The first call to the
     75   // checker's CalledOnValidSequence() method will re-bind the checker, and
     76   // later calls will verify that the same task runner is used.
     77   origin_sequence_checker_.DetachFromSequence();
     78 }
     79 
     80 Timer::Timer(const Location& posted_from,
     81              TimeDelta delay,
     82              const base::Closure& user_task,
     83              bool is_repeating)
     84     : Timer(posted_from, delay, user_task, is_repeating, nullptr) {}
     85 
     86 Timer::Timer(const Location& posted_from,
     87              TimeDelta delay,
     88              const base::Closure& user_task,
     89              bool is_repeating,
     90              const TickClock* tick_clock)
     91     : scheduled_task_(nullptr),
     92       posted_from_(posted_from),
     93       delay_(delay),
     94       user_task_(user_task),
     95       is_repeating_(is_repeating),
     96       retain_user_task_(true),
     97       tick_clock_(tick_clock),
     98       is_running_(false) {
     99   // See comment in other constructor.
    100   origin_sequence_checker_.DetachFromSequence();
    101 }
    102 
    103 Timer::~Timer() {
    104   DCHECK(origin_sequence_checker_.CalledOnValidSequence());
    105   AbandonAndStop();
    106 }
    107 
    108 bool Timer::IsRunning() const {
    109   DCHECK(origin_sequence_checker_.CalledOnValidSequence());
    110   return is_running_;
    111 }
    112 
    113 TimeDelta Timer::GetCurrentDelay() const {
    114   DCHECK(origin_sequence_checker_.CalledOnValidSequence());
    115   return delay_;
    116 }
    117 
    118 void Timer::SetTaskRunner(scoped_refptr<SequencedTaskRunner> task_runner) {
    119   // Do not allow changing the task runner when the Timer is running.
    120   // Don't check for |origin_sequence_checker_.CalledOnValidSequence()| here to
    121   // allow the use case of constructing the Timer and immediatetly invoking
    122   // SetTaskRunner() before starting it (CalledOnValidSequence() would undo the
    123   // DetachFromSequence() from the constructor). The |!is_running| check kind of
    124   // verifies the same thing (and TSAN should catch callers that do it wrong but
    125   // somehow evade all debug checks).
    126   DCHECK(!is_running_);
    127   task_runner_.swap(task_runner);
    128 }
    129 
    130 void Timer::Start(const Location& posted_from,
    131                   TimeDelta delay,
    132                   const base::Closure& user_task) {
    133   DCHECK(origin_sequence_checker_.CalledOnValidSequence());
    134 
    135   posted_from_ = posted_from;
    136   delay_ = delay;
    137   user_task_ = user_task;
    138 
    139   Reset();
    140 }
    141 
    142 void Timer::Stop() {
    143   // TODO(gab): Enable this when it's no longer called racily from
    144   // RunScheduledTask(): https://crbug.com/587199.
    145   // DCHECK(origin_sequence_checker_.CalledOnValidSequence());
    146 
    147   is_running_ = false;
    148 
    149   // It's safe to destroy or restart Timer on another sequence after Stop().
    150   origin_sequence_checker_.DetachFromSequence();
    151 
    152   if (!retain_user_task_)
    153     user_task_.Reset();
    154   // No more member accesses here: |this| could be deleted after freeing
    155   // |user_task_|.
    156 }
    157 
    158 void Timer::Reset() {
    159   DCHECK(origin_sequence_checker_.CalledOnValidSequence());
    160   DCHECK(!user_task_.is_null());
    161 
    162   // If there's no pending task, start one up and return.
    163   if (!scheduled_task_) {
    164     PostNewScheduledTask(delay_);
    165     return;
    166   }
    167 
    168   // Set the new |desired_run_time_|.
    169   if (delay_ > TimeDelta::FromMicroseconds(0))
    170     desired_run_time_ = Now() + delay_;
    171   else
    172     desired_run_time_ = TimeTicks();
    173 
    174   // We can use the existing scheduled task if it arrives before the new
    175   // |desired_run_time_|.
    176   if (desired_run_time_ >= scheduled_run_time_) {
    177     is_running_ = true;
    178     return;
    179   }
    180 
    181   // We can't reuse the |scheduled_task_|, so abandon it and post a new one.
    182   AbandonScheduledTask();
    183   PostNewScheduledTask(delay_);
    184 }
    185 
    186 TimeTicks Timer::Now() const {
    187   // TODO(gab): Enable this when it's no longer called racily from
    188   // RunScheduledTask(): https://crbug.com/587199.
    189   // DCHECK(origin_sequence_checker_.CalledOnValidSequence());
    190   return tick_clock_ ? tick_clock_->NowTicks() : TimeTicks::Now();
    191 }
    192 
    193 void Timer::PostNewScheduledTask(TimeDelta delay) {
    194   // TODO(gab): Enable this when it's no longer called racily from
    195   // RunScheduledTask(): https://crbug.com/587199.
    196   // DCHECK(origin_sequence_checker_.CalledOnValidSequence());
    197   DCHECK(!scheduled_task_);
    198   is_running_ = true;
    199   scheduled_task_ = new BaseTimerTaskInternal(this);
    200   if (delay > TimeDelta::FromMicroseconds(0)) {
    201     // TODO(gab): Posting BaseTimerTaskInternal::Run to another sequence makes
    202     // this code racy. https://crbug.com/587199
    203     GetTaskRunner()->PostDelayedTask(
    204         posted_from_,
    205         base::BindOnce(&BaseTimerTaskInternal::Run,
    206                        base::Owned(scheduled_task_)),
    207         delay);
    208     scheduled_run_time_ = desired_run_time_ = Now() + delay;
    209   } else {
    210     GetTaskRunner()->PostTask(posted_from_,
    211                               base::BindOnce(&BaseTimerTaskInternal::Run,
    212                                              base::Owned(scheduled_task_)));
    213     scheduled_run_time_ = desired_run_time_ = TimeTicks();
    214   }
    215 }
    216 
    217 scoped_refptr<SequencedTaskRunner> Timer::GetTaskRunner() {
    218   return task_runner_.get() ? task_runner_ : SequencedTaskRunnerHandle::Get();
    219 }
    220 
    221 void Timer::AbandonScheduledTask() {
    222   // TODO(gab): Enable this when it's no longer called racily from
    223   // RunScheduledTask() -> Stop(): https://crbug.com/587199.
    224   // DCHECK(origin_sequence_checker_.CalledOnValidSequence());
    225   if (scheduled_task_) {
    226     scheduled_task_->Abandon();
    227     scheduled_task_ = nullptr;
    228   }
    229 }
    230 
    231 void Timer::RunScheduledTask() {
    232   // TODO(gab): Enable this when it's no longer called racily:
    233   // https://crbug.com/587199.
    234   // DCHECK(origin_sequence_checker_.CalledOnValidSequence());
    235 
    236   // Task may have been disabled.
    237   if (!is_running_)
    238     return;
    239 
    240   // First check if we need to delay the task because of a new target time.
    241   if (desired_run_time_ > scheduled_run_time_) {
    242     // Now() can be expensive, so only call it if we know the user has changed
    243     // the |desired_run_time_|.
    244     TimeTicks now = Now();
    245     // Task runner may have called us late anyway, so only post a continuation
    246     // task if the |desired_run_time_| is in the future.
    247     if (desired_run_time_ > now) {
    248       // Post a new task to span the remaining time.
    249       PostNewScheduledTask(desired_run_time_ - now);
    250       return;
    251     }
    252   }
    253 
    254   // Make a local copy of the task to run. The Stop method will reset the
    255   // |user_task_| member if |retain_user_task_| is false.
    256   base::Closure task = user_task_;
    257 
    258   if (is_repeating_)
    259     PostNewScheduledTask(delay_);
    260   else
    261     Stop();
    262 
    263   task.Run();
    264 
    265   // No more member accesses here: |this| could be deleted at this point.
    266 }
    267 
    268 void OneShotTimer::FireNow() {
    269   DCHECK(origin_sequence_checker_.CalledOnValidSequence());
    270   DCHECK(!task_runner_) << "FireNow() is incompatible with SetTaskRunner()";
    271   DCHECK(IsRunning());
    272 
    273   OnceClosure task = user_task();
    274   Stop();
    275   DCHECK(!user_task());
    276   std::move(task).Run();
    277 }
    278 
    279 }  // namespace base
    280