// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/timer.h" #include #if defined(OS_WIN) #include #endif #include "base/atomic_sequence_num.h" #include "base/logging.h" #include "base/message_loop.h" #include "base/task.h" namespace base { // A sequence number for all allocated times (used to break ties when // comparing times in the TimerManager, and assure FIFO execution sequence). static AtomicSequenceNumber timer_id_counter_(base::LINKER_INITIALIZED); //----------------------------------------------------------------------------- // Timer Timer::Timer(int delay, Task* task, bool repeating) : task_(task), delay_(delay), repeating_(repeating) { timer_id_ = timer_id_counter_.GetNext(); DCHECK(delay >= 0); Reset(); } Timer::Timer(Time fire_time, Task* task) : fire_time_(fire_time), task_(task), repeating_(false) { timer_id_ = timer_id_counter_.GetNext(); // TODO(darin): kill off this stuff. because we are forced to compute 'now' // in order to determine the delay, it is possible that our fire time could // be in the past. /sigh/ creation_time_ = Time::Now(); delay_ = static_cast((fire_time_ - creation_time_).InMilliseconds()); if (delay_ < 0) delay_ = 0; } void Timer::Reset() { creation_time_ = Time::Now(); fire_time_ = creation_time_ + TimeDelta::FromMilliseconds(delay_); DHISTOGRAM_COUNTS(L"Timer.Durations", delay_); } //----------------------------------------------------------------------------- // TimerPQueue void TimerPQueue::RemoveTimer(Timer* timer) { const std::vector::iterator location = find(c.begin(), c.end(), timer); if (location != c.end()) { c.erase(location); make_heap(c.begin(), c.end(), TimerComparison()); } } bool TimerPQueue::ContainsTimer(const Timer* timer) const { return find(c.begin(), c.end(), timer) != c.end(); } //----------------------------------------------------------------------------- // TimerManager TimerManager::TimerManager(MessageLoop* message_loop) : use_broken_delay_(false), message_loop_(message_loop) { #if defined(OS_WIN) // We've experimented with all sorts of timers, and initially tried // to avoid using timeBeginPeriod because it does affect the system // globally. However, after much investigation, it turns out that all // of the major plugins (flash, windows media 9-11, and quicktime) // already use timeBeginPeriod to increase the speed of the clock. // Since the browser must work with these plugins, the browser already // needs to support a fast clock. We may as well use this ourselves, // as it really is the best timer mechanism for our needs. timeBeginPeriod(1); #endif } TimerManager::~TimerManager() { #if defined(OS_WIN) // Match timeBeginPeriod() from construction. timeEndPeriod(1); #endif // Be nice to unit tests, and discard and delete all timers along with the // embedded task objects by handing off to MessageLoop (which would have Run() // and optionally deleted the objects). while (timers_.size()) { Timer* pending = timers_.top(); timers_.pop(); message_loop_->DiscardTimer(pending); } } Timer* TimerManager::StartTimer(int delay, Task* task, bool repeating) { Timer* t = new Timer(delay, task, repeating); StartTimer(t); return t; } void TimerManager::StopTimer(Timer* timer) { // Make sure the timer is actually running. if (!IsTimerRunning(timer)) return; // Kill the active timer, and remove the pending entry from the queue. if (timer != timers_.top()) { timers_.RemoveTimer(timer); } else { timers_.pop(); DidChangeNextTimer(); } } void TimerManager::ResetTimer(Timer* timer) { StopTimer(timer); timer->Reset(); StartTimer(timer); } bool TimerManager::IsTimerRunning(const Timer* timer) const { return timers_.ContainsTimer(timer); } Timer* TimerManager::PeekTopTimer() { if (timers_.empty()) return NULL; return timers_.top(); } bool TimerManager::RunSomePendingTimers() { bool did_work = false; // Process a small group of timers. Cap the maximum number of timers we can // process so we don't deny cycles to other parts of the process when lots of // timers have been set. const int kMaxTimersPerCall = 2; for (int i = 0; i < kMaxTimersPerCall; ++i) { if (timers_.empty() || timers_.top()->fire_time() > Time::Now()) break; // Get a pending timer. Deal with updating the timers_ queue and setting // the TopTimer. We'll execute the timer task only after the timer queue // is back in a consistent state. Timer* pending = timers_.top(); // If pending task isn't invoked_later, then it must be possible to run it // now (i.e., current task needs to be reentrant). // TODO(jar): We may block tasks that we can queue from being popped. if (!message_loop_->NestableTasksAllowed() && !pending->task()->owned_by_message_loop_) break; timers_.pop(); did_work = true; // If the timer is repeating, add it back to the list of timers to process. if (pending->repeating()) { pending->Reset(); timers_.push(pending); } message_loop_->RunTimerTask(pending); } // Restart the WM_TIMER (if necessary). if (did_work) DidChangeNextTimer(); return did_work; } // Note: Caller is required to call timer->Reset() before calling StartTimer(). // TODO(jar): change API so that Reset() is called as part of StartTimer, making // the API a little less error prone. void TimerManager::StartTimer(Timer* timer) { // Make sure the timer is not running. if (IsTimerRunning(timer)) return; timers_.push(timer); // Priority queue will sort the timer into place. if (timers_.top() == timer) // We are new head of queue. DidChangeNextTimer(); } Time TimerManager::GetNextFireTime() const { if (timers_.empty()) return Time(); return timers_.top()->fire_time(); } void TimerManager::DidChangeNextTimer() { // Determine if the next timer expiry actually changed... if (!timers_.empty()) { const Time& expiry = timers_.top()->fire_time(); if (expiry == next_timer_expiry_) return; next_timer_expiry_ = expiry; } else { next_timer_expiry_ = Time(); } message_loop_->DidChangeNextTimerExpiry(); } //----------------------------------------------------------------------------- // BaseTimer_Helper void BaseTimer_Helper::OrphanDelayedTask() { if (delayed_task_) { delayed_task_->timer_ = NULL; delayed_task_ = NULL; } } void BaseTimer_Helper::InitiateDelayedTask(TimerTask* timer_task) { OrphanDelayedTask(); delayed_task_ = timer_task; delayed_task_->timer_ = this; MessageLoop::current()->PostDelayedTask( FROM_HERE, timer_task, static_cast(delay_.InMilliseconds())); } } // namespace base