// Copyright 2008, Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #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" // A sequence number for all allocated times (used to break ties when // comparing times in the TimerManager, and assure FIFO execution sequence). static base::AtomicSequenceNumber timer_id_counter_; //----------------------------------------------------------------------------- // 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(); } int Timer::GetCurrentDelay() const { // Be careful here. Timers have a precision of microseconds, but this API is // in milliseconds. If there are 5.5ms left, should the delay be 5 or 6? It // should be 6 to avoid timers firing early. double delay = ceil((fire_time_ - Time::Now()).InMillisecondsF()); return static_cast(delay); } 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() || GetCurrentDelay() > 0) 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()->is_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(); } int TimerManager::GetCurrentDelay() { if (timers_.empty()) return -1; int delay = timers_.top()->GetCurrentDelay(); if (delay < 0) delay = 0; return delay; } 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(); } //----------------------------------------------------------------------------- // SimpleTimer SimpleTimer::SimpleTimer(TimeDelta delay, Task* task, bool repeating) : timer_(static_cast(delay.InMilliseconds()), task, repeating), owns_task_(true) { } SimpleTimer::~SimpleTimer() { Stop(); if (owns_task_) delete timer_.task(); } void SimpleTimer::Start() { DCHECK(timer_.task()); timer_.Reset(); MessageLoop::current()->timer_manager()->StartTimer(&timer_); } void SimpleTimer::Stop() { MessageLoop::current()->timer_manager()->StopTimer(&timer_); } bool SimpleTimer::IsRunning() const { return MessageLoop::current()->timer_manager()->IsTimerRunning(&timer_); } void SimpleTimer::Reset() { DCHECK(timer_.task()); MessageLoop::current()->timer_manager()->ResetTimer(&timer_); }