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