15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/timer/timer.h" 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <stddef.h> 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h" 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/memory/ref_counted.h" 115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/single_thread_task_runner.h" 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/thread_task_runner_handle.h" 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/threading/platform_thread.h" 145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace base { 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// BaseTimerTaskInternal is a simple delegate for scheduling a callback to 185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Timer in the thread's default task runner. It also handles the following 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// edge cases: 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// - deleted by the task runner. 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// - abandoned (orphaned) by Timer. 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class BaseTimerTaskInternal { 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public: 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) explicit BaseTimerTaskInternal(Timer* timer) 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) : timer_(timer) { 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ~BaseTimerTaskInternal() { 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // This task may be getting cleared because the task runner has been 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // destructed. If so, don't leave Timer with a dangling pointer 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // to this. 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (timer_) 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) timer_->StopAndAbandon(); 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) void Run() { 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // timer_ is NULL if we were abandoned. 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!timer_) 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // *this will be deleted by the task runner, so Timer needs to 425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // forget us: 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) timer_->scheduled_task_ = NULL; 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Although Timer should not call back into *this, let's clear 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // the timer_ member first to be pedantic. 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Timer* timer = timer_; 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) timer_ = NULL; 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) timer->RunScheduledTask(); 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // The task remains in the MessageLoop queue, but nothing will happen when it 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // runs. 545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) void Abandon() { 555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) timer_ = NULL; 565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private: 595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Timer* timer_; 605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Timer::Timer(bool retain_user_task, bool is_repeating) 635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) : scheduled_task_(NULL), 645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) thread_id_(0), 655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) is_repeating_(is_repeating), 665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) retain_user_task_(retain_user_task), 675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) is_running_(false) { 685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Timer::Timer(const tracked_objects::Location& posted_from, 715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) TimeDelta delay, 725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const base::Closure& user_task, 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bool is_repeating) 745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) : scheduled_task_(NULL), 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) posted_from_(posted_from), 765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) delay_(delay), 775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) user_task_(user_task), 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) thread_id_(0), 795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) is_repeating_(is_repeating), 805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) retain_user_task_(true), 815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) is_running_(false) { 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Timer::~Timer() { 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) StopAndAbandon(); 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool Timer::IsRunning() const { 895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return is_running_; 905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TimeDelta Timer::GetCurrentDelay() const { 935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return delay_; 945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void Timer::Start(const tracked_objects::Location& posted_from, 975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) TimeDelta delay, 985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const base::Closure& user_task) { 995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SetTaskInfo(posted_from, delay, user_task); 1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Reset(); 1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void Timer::Stop() { 1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) is_running_ = false; 1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!retain_user_task_) 1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) user_task_.Reset(); 1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void Timer::Reset() { 1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(!user_task_.is_null()); 1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If there's no pending task, start one up and return. 1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!scheduled_task_) { 1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) PostNewScheduledTask(delay_); 1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Set the new desired_run_time_. 1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (delay_ > TimeDelta::FromMicroseconds(0)) 1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) desired_run_time_ = TimeTicks::Now() + delay_; 1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else 1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) desired_run_time_ = TimeTicks(); 1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // 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 136void 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 144void 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 164void 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 173void 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