diff options
author | darin@google.com <darin@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-08-12 20:04:36 +0000 |
---|---|---|
committer | darin@google.com <darin@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-08-12 20:04:36 +0000 |
commit | d37225fe08bb63a70cc52bcfbbd845af5726c419 (patch) | |
tree | 406c5bdd2cf18c8a5a7351e79a4c1e7a379d98b5 | |
parent | 604a7bee4d5ec7424ef71f094c2f4232f606b995 (diff) | |
download | chromium_src-d37225fe08bb63a70cc52bcfbbd845af5726c419.zip chromium_src-d37225fe08bb63a70cc52bcfbbd845af5726c419.tar.gz chromium_src-d37225fe08bb63a70cc52bcfbbd845af5726c419.tar.bz2 |
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@739 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | base/message_loop.cc | 78 | ||||
-rw-r--r-- | base/message_loop.h | 5 | ||||
-rw-r--r-- | base/timer.cc | 153 | ||||
-rw-r--r-- | base/timer.h | 114 | ||||
-rw-r--r-- | base/timer_unittest.cc | 3 | ||||
-rw-r--r-- | base/tracked_objects.cc | 2 | ||||
-rw-r--r-- | chrome/common/slide_animation.cc | 2 |
7 files changed, 197 insertions, 160 deletions
diff --git a/base/message_loop.cc b/base/message_loop.cc index 18f4448..5331bc6 100644 --- a/base/message_loop.cc +++ b/base/message_loop.cc @@ -108,17 +108,14 @@ static LPTOP_LEVEL_EXCEPTION_FILTER GetTopSEHFilter() { //------------------------------------------------------------------------------ -MessageLoop::MessageLoop() -#pragma warning(suppress: 4355) // OK, to use |this| in the initializer list. - : timer_manager_(this), - message_hwnd_(NULL), - exception_restoration_(false), - nestable_tasks_allowed_(true), - dispatcher_(NULL), - quit_received_(false), - quit_now_(false), - task_pump_message_pending_(false), - run_depth_(0) { +MessageLoop::MessageLoop() : message_hwnd_(NULL), + exception_restoration_(false), + nestable_tasks_allowed_(true), + dispatcher_(NULL), + quit_received_(false), + quit_now_(false), + task_pump_message_pending_(false), + run_depth_(0) { DCHECK(tls_index_) << "static initializer failed"; DCHECK(!current()) << "should only have one message loop per thread"; ThreadLocalStorage::Set(tls_index_, this); @@ -409,11 +406,6 @@ LRESULT MessageLoop::WndProc( return 0; } - case WM_TIMER: - ProcessSomeTimers(); // Give the TimerManager a tickle. - DidChangeNextTimerExpiry(); // Maybe generate another WM_TIMER. - return 0; - case kMsgQuit: { // TODO(jar): bug 1300541 The following assert should be used, but // currently too much code actually triggers the assert, especially in @@ -662,22 +654,21 @@ bool MessageLoop::SignalWatcher(size_t object_index) { bool MessageLoop::RunTimerTask(Timer* timer) { HistogramEvent(kTimerEvent); - Task* task = timer->task(); if (task->is_owned_by_message_loop()) { - // We constructed it through PostDelayedTask(). + // We constructed it through PostTask(). DCHECK(!timer->repeating()); timer->set_task(NULL); delete timer; task->ResetBirthTime(); return QueueOrRunTask(task); + } else { + // This is an unknown timer task, and we *can't* delay running it, as a + // user might try to cancel it with TimerManager at any moment. + DCHECK(nestable_tasks_allowed_); + RunTask(task); + return true; } - - // This is an unknown timer task, and we *can't* delay running it, as a user - // might try to cancel it with TimerManager at any moment. - DCHECK(nestable_tasks_allowed_); - RunTask(task); - return true; } void MessageLoop::DiscardTimer(Timer* timer) { @@ -832,45 +823,6 @@ void MessageLoop::DeletePendingTasks() { */ } -void MessageLoop::DidChangeNextTimerExpiry() { -#if defined(OS_WIN) - // - // We would *like* to provide high resolution timers. Windows timers using - // SetTimer() have a 10ms granularity. We have to use WM_TIMER as a wakeup - // mechanism because the application can enter modal windows loops where it - // is not running our MessageLoop; the only way to have our timers fire in - // these cases is to post messages there. - // - // To provide sub-10ms timers, we process timers directly from our run loop. - // For the common case, timers will be processed there as the run loop does - // its normal work. However, we *also* set the system timer so that WM_TIMER - // events fire. This mops up the case of timers not being able to work in - // modal message loops. It is possible for the SetTimer to pop and have no - // pending timers, because they could have already been processed by the - // run loop itself. - // - // We use a single SetTimer corresponding to the timer that will expire - // soonest. As new timers are created and destroyed, we update SetTimer. - // Getting a spurrious SetTimer event firing is benign, as we'll just be - // processing an empty timer queue. - // - int delay = timer_manager_.GetCurrentDelay(); - if (delay == -1) { - KillTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this)); - } else { - if (delay < USER_TIMER_MINIMUM) - delay = USER_TIMER_MINIMUM; - // Simulates malfunctioning, early firing timers. Pending tasks should only - // be invoked when the delay they specify has elapsed. - if (timer_manager_.use_broken_delay()) - delay = 10; - // Create a WM_TIMER event that will wake us up to check for any pending - // timers (in case we are running within a nested, external sub-pump). - SetTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this), delay, NULL); - } -#endif // defined(OS_WIN) -} - //------------------------------------------------------------------------------ // Implementation of the work_queue_ as a ProiritizedTaskQueue diff --git a/base/message_loop.h b/base/message_loop.h index 81c1e52..85535b4 100644 --- a/base/message_loop.h +++ b/base/message_loop.h @@ -365,8 +365,6 @@ class MessageLoop { //---------------------------------------------------------------------------- private: - friend class TimerManager; // So it can call DidChangeNextTimerExpiry - struct ScopedStateSave { explicit ScopedStateSave(MessageLoop* loop) : loop_(loop), @@ -575,9 +573,6 @@ class MessageLoop { // Post a task to our incomming queue. void PostTaskInternal(Task* task); - // Called by the TimerManager when its next timer changes. - void DidChangeNextTimerExpiry(); - // Start recording histogram info about events and action IF it was enabled // and IF the statistics recorder can accept a registration of our histogram. void StartHistogrammer(); diff --git a/base/timer.cc b/base/timer.cc index c5e4e87..7c3a636 100644 --- a/base/timer.cc +++ b/base/timer.cc @@ -28,17 +28,52 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "base/timer.h" - -#include <math.h> -#if defined(OS_WIN) #include <mmsystem.h> -#endif #include "base/atomic_sequence_num.h" #include "base/logging.h" #include "base/message_loop.h" #include "base/task.h" +// Note about hi-resolution timers. +// This class would *like* to provide high resolution timers. Windows timers +// using SetTimer() have a 10ms granularity. We have to use WM_TIMER as a +// wakeup mechanism because the application can enter modal windows loops where +// it is not running our MessageLoop; the only way to have our timers fire in +// these cases is to post messages there. +// +// To provide sub-10ms timers, we process timers directly from our main +// MessageLoop. For the common case, timers will be processed there as the +// message loop does its normal work. However, we *also* set the system timer +// so that WM_TIMER events fire. This mops up the case of timers not being +// able to work in modal message loops. It is possible for the SetTimer to +// pop and have no pending timers, because they could have already been +// processed by the message loop itself. +// +// We use a single SetTimer corresponding to the timer that will expire +// soonest. As new timers are created and destroyed, we update SetTimer. +// Getting a spurrious SetTimer event firing is benign, as we'll just be +// processing an empty timer queue. + +static const wchar_t kWndClass[] = L"Chrome_TimerMessageWindow"; + +static LRESULT CALLBACK MessageWndProc(HWND hwnd, + UINT message, + WPARAM wparam, + LPARAM lparam) { + if (message == WM_TIMER) { + // Timer not firing? Maybe you're suffering from a WM_PAINTstorm. Make sure + // any WM_PAINT handler you have calls BeginPaint and EndPaint to validate + // the invalid region, otherwise you will be flooded with paint messages + // that trump WM_TIMER when PeekMessage is called. + UINT_PTR timer_id = static_cast<UINT_PTR>(wparam); + TimerManager* tm = reinterpret_cast<TimerManager*>(timer_id); + return tm->MessageWndProc(hwnd, message, wparam, lparam); + } + + return DefWindowProc(hwnd, message, wparam, lparam); +} + // 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_; @@ -55,14 +90,6 @@ Timer::Timer(int delay, Task* task, bool repeating) 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<int>(delay); -} - void Timer::Reset() { creation_time_ = Time::Now(); fire_time_ = creation_time_ + TimeDelta::FromMilliseconds(delay_); @@ -88,10 +115,11 @@ bool TimerPQueue::ContainsTimer(const Timer* timer) const { //----------------------------------------------------------------------------- // TimerManager -TimerManager::TimerManager(MessageLoop* message_loop) - : use_broken_delay_(false), - message_loop_(message_loop) { -#if defined(OS_WIN) +TimerManager::TimerManager() + : message_hwnd_(NULL), + use_broken_delay_(false), + use_native_timers_(true), + message_loop_(NULL) { // 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 @@ -101,14 +129,18 @@ TimerManager::TimerManager(MessageLoop* message_loop) // 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 + + // Initialize the Message HWND in the constructor so that the window + // belongs to the same thread as the message loop (this is important!) + GetMessageHWND(); } TimerManager::~TimerManager() { -#if defined(OS_WIN) // Match timeBeginPeriod() from construction. timeEndPeriod(1); -#endif + + if (message_hwnd_ != NULL) + DestroyWindow(message_hwnd_); // 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() @@ -136,7 +168,7 @@ void TimerManager::StopTimer(Timer* timer) { timers_.RemoveTimer(timer); } else { timers_.pop(); - DidChangeNextTimer(); + UpdateWindowsWmTimer(); // We took away the head of our queue. } } @@ -158,6 +190,7 @@ Timer* TimerManager::PeekTopTimer() { bool TimerManager::RunSomePendingTimers() { bool did_work = false; + bool allowed_to_run = message_loop()->NestableTasksAllowed(); // 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. @@ -170,11 +203,10 @@ bool TimerManager::RunSomePendingTimers() { // 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() && + if (!message_loop()->NestableTasksAllowed() && !pending->task()->is_owned_by_message_loop()) break; @@ -187,12 +219,12 @@ bool TimerManager::RunSomePendingTimers() { timers_.push(pending); } - message_loop_->RunTimerTask(pending); + message_loop()->RunTimerTask(pending); } // Restart the WM_TIMER (if necessary). if (did_work) - DidChangeNextTimer(); + UpdateWindowsWmTimer(); return did_work; } @@ -207,30 +239,77 @@ void TimerManager::StartTimer(Timer* timer) { timers_.push(timer); // Priority queue will sort the timer into place. - if (timers_.top() == timer) // We are new head of queue. - DidChangeNextTimer(); + if (timers_.top() == timer) + UpdateWindowsWmTimer(); // We are new head of queue. +} + +void TimerManager::UpdateWindowsWmTimer() { + if (!use_native_timers_) + return; + + if (timers_.empty()) { + KillTimer(GetMessageHWND(), reinterpret_cast<UINT_PTR>(this)); + return; + } + + int delay = GetCurrentDelay(); + if (delay < USER_TIMER_MINIMUM) + delay = USER_TIMER_MINIMUM; + + // Simulates malfunctioning, early firing timers. Pending tasks should + // only be invoked when the delay they specify has elapsed. + if (use_broken_delay_) + delay = 10; + + // Create a WM_TIMER event that will wake us up to check for any pending + // timers (in case the message loop was otherwise starving us). + SetTimer(GetMessageHWND(), reinterpret_cast<UINT_PTR>(this), delay, NULL); } int TimerManager::GetCurrentDelay() { if (timers_.empty()) return -1; - int delay = timers_.top()->GetCurrentDelay(); + int delay = timers_.top()->current_delay(); 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(); +int TimerManager::MessageWndProc(HWND hwnd, UINT message, WPARAM wparam, + LPARAM lparam) { + DCHECK(!lparam); + DCHECK(this == message_loop()->timer_manager()); + if (message_loop()->NestableTasksAllowed()) + RunSomePendingTimers(); + else + UpdateWindowsWmTimer(); + return 0; +} + +MessageLoop* TimerManager::message_loop() { + if (!message_loop_) + message_loop_ = MessageLoop::current(); + DCHECK(message_loop_ == MessageLoop::current()); + return message_loop_; +} + + +HWND TimerManager::GetMessageHWND() { + if (!message_hwnd_) { + HINSTANCE hinst = GetModuleHandle(NULL); + + WNDCLASSEX wc = {0}; + wc.cbSize = sizeof(wc); + wc.lpfnWndProc = ::MessageWndProc; + wc.hInstance = hinst; + wc.lpszClassName = kWndClass; + RegisterClassEx(&wc); + + message_hwnd_ = CreateWindow(kWndClass, 0, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, + hinst, 0); + DCHECK(message_hwnd_); } - message_loop_->DidChangeNextTimerExpiry(); + return message_hwnd_; } //----------------------------------------------------------------------------- diff --git a/base/timer.h b/base/timer.h index c23f725..de22b64 100644 --- a/base/timer.h +++ b/base/timer.h @@ -30,50 +30,54 @@ #ifndef BASE_TIMER_H_ #define BASE_TIMER_H_ +#include <math.h> + #include <queue> #include <vector> #include "base/basictypes.h" #include "base/time.h" -//----------------------------------------------------------------------------- +#ifdef OS_WIN +#include <windows.h> +#endif // OS_WIN + // Timer/TimerManager are objects designed to help setting timers. // Goals of TimerManager: -// - have only one system timer for all app timer functionality +// - have only one Windows system timer for all app timer functionality // - work around bugs with timers firing arbitrarily earlier than specified // - provide the ability to run timers even if the application is in a // windows modal app loop. -//----------------------------------------------------------------------------- -class MessageLoop; class TimerManager; class Task; +class MessageLoop; -//----------------------------------------------------------------------------- -// The core timer object. Use TimerManager to create and control timers. class Timer { public: Timer(int delay, Task* task, bool repeating); - // The task to be run when the timer fires. Task* task() const { return task_; } void set_task(Task* task) { task_ = task; } - // Returns the time in msec relative to now that the timer should fire. - int GetCurrentDelay() const; + int current_delay() 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. Implement ceiling by adding 999us prior to + // conversion to ms. + double delay = ceil((fire_time_ - Time::Now()).InMillisecondsF()); + return static_cast<int>(delay); + } - // Returns the absolute time at which the timer should fire. const Time &fire_time() const { return fire_time_; } - // A repeating timer is a timer that is automatically scheduled to fire again - // after it fires. bool repeating() const { return repeating_; } // Update (or fill in) creation_time_, and calculate future fire_time_ based // on current time plus delay_. void Reset(); - // A unique identifier for this timer. int id() const { return timer_id_; } protected: @@ -89,8 +93,9 @@ class Timer { // Timer delay in milliseconds. int delay_; - // A monotonically increasing timer id. Used for ordering two timers which - // have the same timestamp in a FIFO manner. + // A monotonically increasing timer id. Used + // for ordering two timers which have the same + // timestamp in a FIFO manner. int timer_id_; // Whether or not this timer repeats. @@ -100,11 +105,9 @@ class Timer { // iteration started.) Time creation_time_; - DISALLOW_COPY_AND_ASSIGN(Timer); + DISALLOW_EVIL_CONSTRUCTORS(Timer); }; -//----------------------------------------------------------------------------- -// Used to implement TimerPQueue class TimerComparison { public: bool operator() (const Timer* t1, const Timer* t2) const { @@ -123,14 +126,14 @@ class TimerComparison { } }; -//----------------------------------------------------------------------------- // Subclass priority_queue to provide convenient access to removal from this // list. // // Terminology: The "pending" timer is the timer at the top of the queue, // i.e. the timer whose task needs to be Run next. -class TimerPQueue : - public std::priority_queue<Timer*, std::vector<Timer*>, TimerComparison> { +class TimerPQueue : public std::priority_queue<Timer*, + std::vector<Timer*>, + TimerComparison> { public: // Removes |timer| from the queue. void RemoveTimer(Timer* timer); @@ -139,26 +142,32 @@ class TimerPQueue : bool ContainsTimer(const Timer* timer) const; }; -//----------------------------------------------------------------------------- -// There is one TimerManager per thread, owned by the MessageLoop. Timers can -// either be fired by the MessageLoop from within its run loop or via a system -// timer event that the MesssageLoop constructs. The advantage of the former -// is that we can make timers fire significantly faster than the granularity -// provided by the system. The advantage of a system timer is that modal -// message loops which don't run our MessageLoop code will still be able to -// process system timer events. +// There is one TimerManager per thread, owned by the MessageLoop. +// Timers can either be fired directly by the MessageLoop, or by +// SetTimer and a WM_TIMER message. The advantage of the former +// is that we can make timers fire significantly faster than the 10ms +// granularity provided by SetTimer(). The advantage of SetTimer() +// is that modal message loops which don't run our MessageLoop +// code will still be able to process WM_TIMER messages. // -// NOTE: TimerManager is not thread safe. You cannot set timers onto a thread -// other than your own. +// Note: TimerManager is not thread safe. You cannot set timers +// onto a thread other than your own. class TimerManager { public: - explicit TimerManager(MessageLoop* message_loop); + TimerManager(); ~TimerManager(); // Create and start a new timer. |task| is owned by the caller, as is the // timer object that is returned. Timer* StartTimer(int delay, Task* task, bool repeating); + // Flag indicating whether the timer manager should use the OS + // timers or not. Default is true. MessageLoops which are not reliably + // called due to nested windows message loops should set this to + // true. + bool use_native_timers() { return use_native_timers_; } + void set_use_native_timers(bool value) { use_native_timers_ = value; } + // Starts a timer. This is a no-op if the timer is already started. void StartTimer(Timer* timer); @@ -181,6 +190,15 @@ class TimerManager { // pqueue) needs to be fired. Returns -1 if no timers are pending. int GetCurrentDelay(); + // A handler for WM_TIMER messages. + // If a task is not running currently, it runs some timer tasks (if there are + // some ready to fire), otherwise it just updates the WM_TIMER to be called + // again (hopefully when it is allowed to run a task). + int MessageWndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam); + + // Return cached copy of MessageLoop::current(). + MessageLoop* message_loop(); + #ifdef UNIT_TEST // For testing only, used to simulate broken early-firing WM_TIMER // notifications by setting arbitrarily small delays in SetTimer. @@ -189,33 +207,35 @@ class TimerManager { } #endif // UNIT_TEST - bool use_broken_delay() const { - return use_broken_delay_; - } - protected: // Peek at the timer which will fire soonest. Timer* PeekTopTimer(); private: - void DidChangeNextTimer(); + // Update our Windows WM_TIMER to match our most immediately pending timer. + void UpdateWindowsWmTimer(); + +#ifdef OS_WIN + // Retrieve the Message Window that handles WM_TIMER messages from the + // system. + HWND GetMessageHWND(); - // A cached value that indicates the time when we think the next timer is to - // fire. We use this to determine if we should call DidChangeNextTimerExpiry - // on the MessageLoop. - Time next_timer_expiry_; + HWND message_hwnd_; +#endif // OS_WIN TimerPQueue timers_; bool use_broken_delay_; + // Flag to enable/disable use of native timers. + bool use_native_timers_; + // A lazily cached copy of MessageLoop::current. MessageLoop* message_loop_; - DISALLOW_COPY_AND_ASSIGN(TimerManager); + DISALLOW_EVIL_CONSTRUCTORS(TimerManager); }; -//----------------------------------------------------------------------------- // A simple wrapper for the Timer / TimerManager API. This is a helper class. // Use OneShotTimer or RepeatingTimer instead. class SimpleTimer { @@ -261,10 +281,9 @@ class SimpleTimer { // we are deallocated. Defaults to true. bool owns_task_; - DISALLOW_COPY_AND_ASSIGN(SimpleTimer); + DISALLOW_EVIL_CONSTRUCTORS(SimpleTimer); }; -//----------------------------------------------------------------------------- // A simple, one-shot timer. The task is run after the specified delay once // the Start method is called. The task is deleted when the timer object is // destroyed. @@ -279,10 +298,9 @@ class OneShotTimer : public SimpleTimer { : SimpleTimer(delay, task, false) { } private: - DISALLOW_COPY_AND_ASSIGN(OneShotTimer); + DISALLOW_EVIL_CONSTRUCTORS(OneShotTimer); }; -//----------------------------------------------------------------------------- // A simple, repeating timer. The task is run at the specified interval once // the Start method is called. The task is deleted when the timer object is // destroyed. @@ -297,7 +315,7 @@ class RepeatingTimer : public SimpleTimer { : SimpleTimer(interval, task, true) { } private: - DISALLOW_COPY_AND_ASSIGN(RepeatingTimer); + DISALLOW_EVIL_CONSTRUCTORS(RepeatingTimer); }; #endif // BASE_TIMER_H_ diff --git a/base/timer_unittest.cc b/base/timer_unittest.cc index 4c2593f..814836b 100644 --- a/base/timer_unittest.cc +++ b/base/timer_unittest.cc @@ -295,9 +295,6 @@ TEST(TimerTest, FifoOrder) { class MockTimerManager : public TimerManager { public: - MockTimerManager() : TimerManager(MessageLoop::current()) { - } - // Pops the most-recent to fire timer and returns its timer id. // Returns -1 if there are no timers in the list. int pop() { diff --git a/base/tracked_objects.cc b/base/tracked_objects.cc index f94d77b..8694bfd 100644 --- a/base/tracked_objects.cc +++ b/base/tracked_objects.cc @@ -29,8 +29,6 @@ #include "base/tracked_objects.h" -#include <math.h> - #include "base/string_util.h" namespace tracked_objects { diff --git a/chrome/common/slide_animation.cc b/chrome/common/slide_animation.cc index 2be6d093..f3f9c5a 100644 --- a/chrome/common/slide_animation.cc +++ b/chrome/common/slide_animation.cc @@ -29,8 +29,6 @@ #include "chrome/common/slide_animation.h" -#include <math.h> - // How many frames per second to target. static const int kDefaultFramerateHz = 50; |