diff options
author | darin@google.com <darin@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-08-15 04:32:57 +0000 |
---|---|---|
committer | darin@google.com <darin@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-08-15 04:32:57 +0000 |
commit | 295039bdf97f08bf5c1c1c136dd977b7e97ddd31 (patch) | |
tree | ae3b47308e7d2f2db903f6a61c193e7a8c9ec882 /base/message_loop.cc | |
parent | 1c33790ef99bf8301260b9144110e71e7723d0f4 (diff) | |
download | chromium_src-295039bdf97f08bf5c1c1c136dd977b7e97ddd31.zip chromium_src-295039bdf97f08bf5c1c1c136dd977b7e97ddd31.tar.gz chromium_src-295039bdf97f08bf5c1c1c136dd977b7e97ddd31.tar.bz2 |
Introduce MessagePump to represent the native message pump used to drive a
MessageLoop. A MessageLoop now has a MessagePump.
This will make it possible to port the MessagePump interface to other platforms
as well as to use an IO completion port for our worker threads on Windows.
Currently, there is only MessagePumpWin, which attempts to preserve the
pre-existing behavior of the MessageLoop.
API changes to MessageLoop:
1. MessageLoop::Quit means return from Run when the MessageLoop would
otherwise wait for more work.
2. MessageLoop::Quit can no longer be called outside the context of an active Run
call. So, things like this:
MessageLoop::current()->Quit();
MessageLoop::current()->Run();
are now:
MessageLoop::current()->RunAllPending();
3. MessageLoop::Quit can no longer be called from other threads. This means that
PostTask(..., new MessageLoop::QuitTask()) must be used explicitly to Quit across
thread boundaries.
4. No protection is made to deal with nested MessageLoops involving watched
objects or APCs. In fact, an assertion is added to flag such cases. This is a
temporary measure until object watching and APC facilities are removed in favor
of a MessagePump designed around an IO completion port.
As part of this CL, I also changed the automation system to use an
IPC::ChannelProxy instead of an IPC::Channel. This moves the automation IPC
onto Chrome's IO thread where it belongs. I also fixed some abuses of
RefCounted in the AutomationProvider class. It was deleting itself in some
cases! This led to having to fix the ownership model for AutomationProvider,
which explains the changes to AutomationProviderList and so on.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@928 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base/message_loop.cc')
-rw-r--r-- | base/message_loop.cc | 692 |
1 files changed, 130 insertions, 562 deletions
diff --git a/base/message_loop.cc b/base/message_loop.cc index 18f4448..f91a6c9 100644 --- a/base/message_loop.cc +++ b/base/message_loop.cc @@ -27,14 +27,13 @@ // (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 <algorithm> - #include "base/message_loop.h" +#include <algorithm> + #include "base/logging.h" #include "base/string_util.h" #include "base/thread_local_storage.h" -#include "base/win_util.h" // a TLS index to the message loop for the current thread // Note that if we start doing complex stuff in other static initializers @@ -43,25 +42,10 @@ //------------------------------------------------------------------------------ -static const wchar_t kWndClass[] = L"Chrome_MessageLoopWindow"; - -// Windows Message numbers handled by WindowMessageProc. - -// Message sent to get an additional time slice for pumping (processing) another -// task (a series of such messages creates a continuous task pump). -static const int kMsgPumpATask = WM_USER + 1; - -// Message sent by Quit() to cause our main message pump to terminate as soon as -// all pending task and message queues have been emptied. -static const int kMsgQuit = WM_USER + 2; - // Logical events for Histogram profiling. Run with -message-loop-histogrammer // to get an accounting of messages and actions taken on each thread. -static const int kTaskRunEvent = WM_USER + 16; // 0x411 -static const int kSleepingApcEvent = WM_USER + 17; // 0x411 -static const int kPollingSignalEvent = WM_USER + 18; // 0x412 -static const int kSleepingSignalEvent = WM_USER + 19; // 0x413 -static const int kTimerEvent = WM_USER + 20; // 0x414 +static const int kTaskRunEvent = 0x1; +static const int kTimerEvent = 0x2; // Provide range of message IDs for use in histogramming and debug display. static const int kLeastNonZeroMessageId = 1; @@ -70,26 +54,8 @@ static const int kNumberOfDistinctMessagesDisplayed = 1100; //------------------------------------------------------------------------------ -#ifndef NDEBUG -// Force exercise of polling model. -#define CHROME_MAXIMUM_WAIT_OBJECTS 8 -#else -#define CHROME_MAXIMUM_WAIT_OBJECTS MAXIMUM_WAIT_OBJECTS -#endif - -//------------------------------------------------------------------------------ -// A strategy of -1 uses the default case. All strategies are selected as -// positive integers. -// static -int MessageLoop::strategy_selector_ = -1; - -// static -void MessageLoop::SetStrategy(int strategy) { - DCHECK(-1 == strategy_selector_); - strategy_selector_ = strategy; -} +#if defined(OS_WIN) -//------------------------------------------------------------------------------ // Upon a SEH exception in this thread, it restores the original unhandled // exception filter. static int SEHFilter(LPTOP_LEVEL_EXCEPTION_FILTER old_filter) { @@ -106,23 +72,22 @@ static LPTOP_LEVEL_EXCEPTION_FILTER GetTopSEHFilter() { return top_filter; } +#endif // defined(OS_WIN) + //------------------------------------------------------------------------------ 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) { + state_(NULL) { DCHECK(tls_index_) << "static initializer failed"; DCHECK(!current()) << "should only have one message loop per thread"; ThreadLocalStorage::Set(tls_index_, this); - InitMessageWnd(); +#if defined(OS_WIN) + pump_ = new base::MessagePumpWin(); +#endif } MessageLoop::~MessageLoop() { @@ -135,8 +100,7 @@ MessageLoop::~MessageLoop() { // OK, now make it so that no one can find us. ThreadLocalStorage::Set(tls_index_, NULL); - DCHECK(!dispatcher_); - DCHECK(!quit_received_ && !quit_now_); + DCHECK(!state_); // Most tasks that have not been Run() are deleted in the |timer_manager_| // destructor after we remove our tls index. We delete the tasks in our @@ -147,12 +111,6 @@ MessageLoop::~MessageLoop() { DeletePendingTasks(); } -void MessageLoop::SetThreadName(const std::string& thread_name) { - DCHECK(thread_name_.empty()); - thread_name_ = thread_name; - StartHistogrammer(); -} - void MessageLoop::AddDestructionObserver(DestructionObserver *obs) { DCHECK(this == current()); destruction_observers_.AddObserver(obs); @@ -163,26 +121,23 @@ void MessageLoop::RemoveDestructionObserver(DestructionObserver *obs) { destruction_observers_.RemoveObserver(obs); } -void MessageLoop::AddObserver(Observer *obs) { - DCHECK(this == current()); - observers_.AddObserver(obs); -} - -void MessageLoop::RemoveObserver(Observer *obs) { - DCHECK(this == current()); - observers_.RemoveObserver(obs); -} - void MessageLoop::Run() { - RunHandler(NULL, false); + AutoRunState save_state(this); + RunHandler(); } -void MessageLoop::Run(Dispatcher* dispatcher) { - RunHandler(dispatcher, false); +#if defined(OS_WIN) +void MessageLoop::Run(base::MessagePumpWin::Dispatcher* dispatcher) { + AutoRunState save_state(this); + state_->dispatcher = dispatcher; + RunHandler(); } +#endif void MessageLoop::RunAllPending() { - RunHandler(NULL, true); + AutoRunState save_state(this); + state_->quit_received = true; // Means run until we would otherwise block. + RunHandler(); } // Runs the loop in two different SEH modes: @@ -190,94 +145,43 @@ void MessageLoop::RunAllPending() { // one that calls SetUnhandledExceptionFilter(). // enable_SEH_restoration_ = true : any unhandled exception goes to the filter // that was existed before the loop was run. -void MessageLoop::RunHandler(Dispatcher* dispatcher, bool non_blocking) { +void MessageLoop::RunHandler() { +#if defined(OS_WIN) if (exception_restoration_) { LPTOP_LEVEL_EXCEPTION_FILTER current_filter = GetTopSEHFilter(); __try { - RunInternal(dispatcher, non_blocking); + RunInternal(); } __except(SEHFilter(current_filter)) { } - } else { - RunInternal(dispatcher, non_blocking); + return; } +#endif + + RunInternal(); } //------------------------------------------------------------------------------ -// IF this was just a simple PeekMessage() loop (servicing all passible work -// queues), then Windows would try to achieve the following order according to -// MSDN documentation about PeekMessage with no filter): -// * Sent messages -// * Posted messages -// * Sent messages (again) -// * WM_PAINT messages -// * WM_TIMER messages -// -// Summary: none of the above classes is starved, and sent messages has twice -// the chance of being processed (i.e., reduced service time). - -void MessageLoop::RunInternal(Dispatcher* dispatcher, bool non_blocking) { - // Preserve ability to be called recursively. - ScopedStateSave save(this); // State is restored on exit. - dispatcher_ = dispatcher; - StartHistogrammer(); +void MessageLoop::RunInternal() { DCHECK(this == current()); - // - // Process pending messages and signaled objects. - // - // Flush these queues before exiting due to a kMsgQuit or else we risk not - // shutting down properly as some operations may depend on further event - // processing. (Note: some tests may use quit_now_ to exit more swiftly, - // and leave messages pending, so don't assert the above fact). - RunTraditional(non_blocking); - DCHECK(non_blocking || quit_received_ || quit_now_); -} - -void MessageLoop::RunTraditional(bool non_blocking) { - for (;;) { - // If we do any work, we may create more messages etc., and more work - // may possibly be waiting in another task group. When we (for example) - // ProcessNextWindowsMessage(), there is a good chance there are still more - // messages waiting (same thing for ProcessNextObject(), which responds to - // only one signaled object; etc.). On the other hand, when any of these - // methods return having done no work, then it is pretty unlikely that - // calling them again quickly will find any work to do. - // Finally, if they all say they had no work, then it is a good time to - // consider sleeping (waiting) for more work. - bool more_work_is_plausible = ProcessNextWindowsMessage(); - if (quit_now_) - return; - - more_work_is_plausible |= ProcessNextDeferredTask(); - more_work_is_plausible |= ProcessNextObject(); - if (more_work_is_plausible) - continue; - - if (quit_received_) - return; - - // Run any timer that is ready to run. It may create messages etc. - if (ProcessSomeTimers()) - continue; - - // We run delayed non nestable tasks only after all nestable tasks have - // run, to preserve FIFO ordering. - if (ProcessNextDelayedNonNestableTask()) - continue; - if (non_blocking) - return; + StartHistogrammer(); - // We service APCs in WaitForWork, without returning. - WaitForWork(); // Wait (sleep) until we have work to do again. +#if defined(OS_WIN) + if (state_->dispatcher) { + pump_win()->RunWithDispatcher(this, state_->dispatcher); + return; } +#endif + + pump_->Run(this); } //------------------------------------------------------------------------------ // Wrapper functions for use in above message loop framework. bool MessageLoop::ProcessNextDelayedNonNestableTask() { - if (run_depth_ != 1) + if (state_->run_depth != 1) return false; if (delayed_non_nestable_queue_.Empty()) @@ -299,41 +203,12 @@ bool MessageLoop::ProcessSomeTimers() { //------------------------------------------------------------------------------ void MessageLoop::Quit() { - EnsureMessageGetsPosted(kMsgQuit); -} - -bool MessageLoop::WatchObject(HANDLE object, Watcher* watcher) { - DCHECK(this == current()); - DCHECK(object); - DCHECK_NE(object, INVALID_HANDLE_VALUE); - - std::vector<HANDLE>::iterator it = find(objects_.begin(), objects_.end(), - object); - if (watcher) { - if (it == objects_.end()) { - static size_t warning_multiple = 1; - if (objects_.size() >= warning_multiple * MAXIMUM_WAIT_OBJECTS / 2) { - LOG(INFO) << "More than " << warning_multiple * MAXIMUM_WAIT_OBJECTS / 2 - << " objects being watched"; - // This DCHECK() is an artificial limitation, meant to warn us if we - // start creating too many objects. It can safely be raised to a higher - // level, and the program is designed to handle much larger values. - // Before raising this limit, make sure that there is a very good reason - // (in your debug testing) to be watching this many objects. - DCHECK(2 <= warning_multiple); - ++warning_multiple; - } - objects_.push_back(object); - watchers_.push_back(watcher); - } else { - watchers_[it - objects_.begin()] = watcher; - } - } else if (it != objects_.end()) { - std::vector<HANDLE>::difference_type index = it - objects_.begin(); - objects_.erase(it); - watchers_.erase(watchers_.begin() + index); + DCHECK(current() == this); + if (state_) { + state_->quit_received = true; + } else { + NOTREACHED() << "Must be inside Run to call Quit"; } - return true; } // Possibly called on a background thread! @@ -352,88 +227,23 @@ void MessageLoop::PostTaskInternal(Task* task) { // directly, as it could starve handling of foreign threads. Put every task // into this queue. - // Local stack variables to use IF we need to process after releasing locks. - HWND message_hwnd; + scoped_refptr<base::MessagePump> pump; { - AutoLock lock1(incoming_queue_lock_); + AutoLock locked(incoming_queue_lock_); + bool was_empty = incoming_queue_.Empty(); incoming_queue_.Push(task); if (!was_empty) return; // Someone else should have started the sub-pump. - // We may have to start the sub-pump. - AutoLock lock2(task_pump_message_lock_); - if (task_pump_message_pending_) - return; // Someone else continued the pumping. - task_pump_message_pending_ = true; // We'll send one. - message_hwnd = message_hwnd_; - } // Release both locks. - // We may have just posted a kMsgQuit, and so this instance may now destroyed! - // Do not invoke non-static methods, or members in any way! - - // PostMessage may fail, as the hwnd may have vanished due to kMsgQuit. - PostMessage(message_hwnd, kMsgPumpATask, 0, 0); -} - -void MessageLoop::InitMessageWnd() { - HINSTANCE hinst = GetModuleHandle(NULL); - - WNDCLASSEX wc = {0}; - wc.cbSize = sizeof(wc); - wc.lpfnWndProc = WndProcThunk; - 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_); -} - -// static -LRESULT CALLBACK MessageLoop::WndProcThunk( - HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { - DCHECK(MessageLoop::current()); - return MessageLoop::current()->WndProc(hwnd, message, wparam, lparam); -} - -LRESULT MessageLoop::WndProc( - HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { - if (hwnd == message_hwnd_) { - switch (message) { - case kMsgPumpATask: { - ProcessPumpReplacementMessage(); // Avoid starving paint and timer. - if (!nestable_tasks_allowed_) - return 0; - PumpATaskDuringWndProc(); - 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 - // tests :-(. - // Discarding a second quit will cause a hang. - //CHECK(!quit_received_); - quit_received_ = true; - return 0; - } - } + pump = pump_; } - return ::DefWindowProc(hwnd, message, wparam, lparam); -} + // Since the incoming_queue_ may contain a task that destroys this message + // loop, we cannot exit incoming_queue_lock_ until we are done with |this|. + // We use a stack-based reference to the message pump so that we can call + // ScheduleWork outside of incoming_queue_lock_. -void MessageLoop::WillProcessMessage(const MSG& msg) { - FOR_EACH_OBSERVER(Observer, observers_, WillProcessMessage(msg)); -} - -void MessageLoop::DidProcessMessage(const MSG& msg) { - FOR_EACH_OBSERVER(Observer, observers_, DidProcessMessage(msg)); + pump->ScheduleWork(); } void MessageLoop::SetNestableTasksAllowed(bool allowed) { @@ -442,7 +252,7 @@ void MessageLoop::SetNestableTasksAllowed(bool allowed) { if (!nestable_tasks_allowed_) return; // Start the native pump if we are not already pumping. - EnsurePumpATaskWasPosted(); + pump_->ScheduleWork(); } } @@ -450,215 +260,7 @@ bool MessageLoop::NestableTasksAllowed() const { return nestable_tasks_allowed_; } -bool MessageLoop::ProcessNextWindowsMessage() { - MSG msg; - if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { - return ProcessMessageHelper(msg); - } - return false; -} - -bool MessageLoop::ProcessMessageHelper(const MSG& msg) { - HistogramEvent(msg.message); - - if (WM_QUIT == msg.message) { - // Repost the QUIT message so that it will be retrieved by the primary - // GetMessage() loop. - quit_now_ = true; - PostQuitMessage(static_cast<int>(msg.wParam)); - return false; - } - - // While running our main message pump, we discard kMsgPumpATask messages. - if (msg.message == kMsgPumpATask && msg.hwnd == message_hwnd_) - return ProcessPumpReplacementMessage(); - - WillProcessMessage(msg); - - if (dispatcher_) { - if (!dispatcher_->Dispatch(msg)) - quit_now_ = true; - } else { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - - DidProcessMessage(msg); - return true; -} - -bool MessageLoop::ProcessPumpReplacementMessage() { - MSG msg; - bool have_message = (0 != PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)); - DCHECK(!have_message || kMsgPumpATask != msg.message - || msg.hwnd != message_hwnd_); - { - // Since we discarded a kMsgPumpATask message, we must update the flag. - AutoLock lock(task_pump_message_lock_); - DCHECK(task_pump_message_pending_); - task_pump_message_pending_ = false; - } - return have_message && ProcessMessageHelper(msg); -} - -// Create a mini-message-pump to force immediate processing of only Windows -// WM_PAINT messages. -void MessageLoop::PumpOutPendingPaintMessages() { - // Don't provide an infinite loop, but do enough peeking to get the job done. - // Actual common max is 4 peeks, but we'll be a little safe here. - const int kMaxPeekCount = 20; - int peek_count; - bool win2k(true); - if (win_util::GetWinVersion() > win_util::WINVERSION_2000) - win2k = false; - for (peek_count = 0; peek_count < kMaxPeekCount; ++peek_count) { - MSG msg; - if (win2k) { - if (!PeekMessage(&msg, NULL, WM_PAINT, WM_PAINT, PM_REMOVE)) - break; - } else { - if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE | PM_QS_PAINT)) - break; - } - ProcessMessageHelper(msg); - if (quit_now_ ) // Handle WM_QUIT. - break; - } - // Histogram what was really being used, to help to adjust kMaxPeekCount. - DHISTOGRAM_COUNTS(L"Loop.PumpOutPendingPaintMessages Peeks", peek_count); -} - //------------------------------------------------------------------------------ -// If we handle more than the OS limit on the number of objects that can be -// waited for, we'll need to poll (sequencing through subsets of the objects -// that can be passed in a single OS wait call). The following is the polling -// interval used in that (unusual) case. (I don't have a lot of justifcation -// for the specific value, but it needed to be short enough that it would not -// add a lot of latency, and long enough that we wouldn't thrash the CPU for no -// reason... especially considering the silly user probably has a million tabs -// open, etc.) -static const int kMultipleWaitPollingInterval = 20; - -void MessageLoop::WaitForWork() { - bool original_can_run = nestable_tasks_allowed_; - int wait_flags = original_can_run ? MWMO_ALERTABLE | MWMO_INPUTAVAILABLE - : MWMO_INPUTAVAILABLE; - - bool use_polling = false; // Poll if too many objects for one OS Wait call. - for (;;) { - // Do initialization here, in case APC modifies object list. - size_t total_objs = original_can_run ? objects_.size() : 0; - - int delay; - size_t polling_index = 0; // The first unprocessed object index. - do { - size_t objs_len = - (polling_index < total_objs) ? total_objs - polling_index : 0; - if (objs_len >= CHROME_MAXIMUM_WAIT_OBJECTS) { - objs_len = CHROME_MAXIMUM_WAIT_OBJECTS - 1; - use_polling = true; - } - HANDLE* objs = objs_len ? polling_index + &objects_.front() : NULL; - - // Only wait up to the time needed by the timer manager to fire the next - // set of timers. - delay = timer_manager_.GetCurrentDelay(); - if (use_polling && delay > kMultipleWaitPollingInterval) - delay = kMultipleWaitPollingInterval; - if (delay < 0) // Negative value means no timers waiting. - delay = INFINITE; - - DWORD result; - result = MsgWaitForMultipleObjectsEx(static_cast<DWORD>(objs_len), objs, - delay, QS_ALLINPUT, wait_flags); - - if (WAIT_IO_COMPLETION == result) { - HistogramEvent(kSleepingApcEvent); - // We'll loop here when we service an APC. At it currently stands, - // *ONLY* the IO thread uses *any* APCs, so this should have no impact - // on the UI thread. - break; // Break to outer loop, and waitforwork() again. - } - - // Use unsigned type to simplify range detection; - size_t signaled_index = result - WAIT_OBJECT_0; - if (signaled_index < objs_len) { - SignalWatcher(polling_index + signaled_index); - HistogramEvent(kSleepingSignalEvent); - return; // We serviced a signaled object. - } - - if (objs_len == signaled_index) - return; // A WM_* message is available. - - DCHECK_NE(WAIT_FAILED, result) << GetLastError(); - - DCHECK(!objs || result == WAIT_TIMEOUT); - if (!use_polling) - return; - polling_index += objs_len; - } while (polling_index < total_objs); - // For compatibility, we didn't return sooner. This made us do *some* wait - // call(s) before returning. This will probably change in next rev. - if (!delay || !timer_manager_.GetCurrentDelay()) - return; // No work done, but timer is ready to fire. - } -} - -// Note: MsgWaitMultipleObjects() can't take a nil list, and that is why I had -// to use SleepEx() to handle APCs when there were no objects. -bool MessageLoop::ProcessNextObject() { - if (!nestable_tasks_allowed_) - return false; - - size_t total_objs = objects_.size(); - if (!total_objs) { - return false; - } - - size_t polling_index = 0; // The first unprocessed object index. - do { - DCHECK(polling_index < total_objs); - size_t objs_len = total_objs - polling_index; - if (objs_len >= CHROME_MAXIMUM_WAIT_OBJECTS) - objs_len = CHROME_MAXIMUM_WAIT_OBJECTS - 1; - HANDLE* objs = polling_index + &objects_.front(); - - // Identify 1 pending object, or allow an IO APC to be completed. - DWORD result = WaitForMultipleObjectsEx(static_cast<DWORD>(objs_len), objs, - FALSE, // 1 signal is sufficient. - 0, // Wait 0ms. - false); // Not alertable (no APC). - - // Use unsigned type to simplify range detection; - size_t signaled_index = result - WAIT_OBJECT_0; - if (signaled_index < objs_len) { - SignalWatcher(polling_index + signaled_index); - HistogramEvent(kPollingSignalEvent); - return true; // We serviced a signaled object. - } - - // If an handle is invalid, it will be WAIT_FAILED. - DCHECK_EQ(WAIT_TIMEOUT, result) << GetLastError(); - polling_index += objs_len; - } while (polling_index < total_objs); - return false; // We serviced nothing. -} - -bool MessageLoop::SignalWatcher(size_t object_index) { - BeforeTaskRunSetup(); - DCHECK(objects_.size() > object_index); - // On reception of OnObjectSignaled() to a Watcher object, it may call - // WatchObject(). watchers_ and objects_ will be modified. This is - // expected, so don't be afraid if, while tracing a OnObjectSignaled() - // function, the corresponding watchers_[result] is inexistant. - watchers_[object_index]->OnObjectSignaled(objects_[object_index]); - // Signaled objects tend to be removed from the watch list, and then added - // back (appended). As a result, they move to the end of the objects_ array, - // and this should make their service "fair" (no HANDLEs should be starved). - AfterTaskRunRestore(); - return true; -} bool MessageLoop::RunTimerTask(Timer* timer) { HistogramEvent(kTimerEvent); @@ -705,16 +307,15 @@ bool MessageLoop::QueueOrRunTask(Task* new_task) { // Execute oldest task. while (!work_queue_.Empty()) { Task* task = work_queue_.Pop(); - if (task->nestable() || run_depth_ == 1) { + if (task->nestable() || state_->run_depth == 1) { RunTask(task); // Show that we ran a task (Note: a new one might arrive as a // consequence!). return true; - } else { - // We couldn't run the task now because we're in a nested message loop - // and the task isn't nestable. - delayed_non_nestable_queue_.Push(task); } + // We couldn't run the task now because we're in a nested message loop + // and the task isn't nestable. + delayed_non_nestable_queue_.Push(task); } // Nothing happened. @@ -743,54 +344,12 @@ void MessageLoop::AfterTaskRunRestore() { nestable_tasks_allowed_ = true; } -void MessageLoop::PumpATaskDuringWndProc() { - // TODO(jar): Perchance we should check on signaled objects here?? - // Signals are generally starved during a native message loop. Even if we - // try to service a signaled object now, we wouldn't automatically get here - // (i.e., the native pump would not re-start) when the next object was - // signaled. If we really want to avoid starving signaled objects, we need - // to translate them into Tasks that can be passed in via PostTask. - // If these native message loops (and sub-pumping activities) are short - // lived, then the starvation won't be that long :-/. - - if (!ProcessNextDeferredTask()) - return; // Nothing to do, so lets stop the sub-pump. - - // We ran a task, so make sure we come back and try to run more tasks. - EnsurePumpATaskWasPosted(); -} - -void MessageLoop::EnsurePumpATaskWasPosted() { - { - AutoLock lock(task_pump_message_lock_); - if (task_pump_message_pending_) - return; // Someone else continued the pumping. - task_pump_message_pending_ = true; // We'll send one. - } - EnsureMessageGetsPosted(kMsgPumpATask); -} - -void MessageLoop::EnsureMessageGetsPosted(int message) const { - const int kRetryCount = 30; - const int kSleepDurationWhenFailing = 100; - for (int i = 0; i < kRetryCount; ++i) { - // Posting to our own windows should always succeed. If it doesn't we're in - // big trouble. - if (PostMessage(message_hwnd_, message, 0, 0)) - return; - Sleep(kSleepDurationWhenFailing); - } - LOG(FATAL) << "Crash with last error " << GetLastError(); - int* p = NULL; - *p = 0; // Crash. -} - void MessageLoop::ReloadWorkQueue() { // We can improve performance of our loading tasks from incoming_queue_ to - // work_queue_ by wating until the last minute (work_queue_ is empty) to load. - // That reduces the number of locks-per-task significantly when our queues get - // large. The optimization is disabled on threads that make use of the - // priority queue (prioritization requires all our tasks to be in the + // work_queue_ by waiting until the last minute (work_queue_ is empty) to + // load. That reduces the number of locks-per-task significantly when our + // queues get large. The optimization is disabled on threads that make use + // of the priority queue (prioritization requires all our tasks to be in the // work_queue_ ASAP). if (!work_queue_.Empty() && !work_queue_.use_priority_queue()) return; // Wait till we *really* need to lock and load. @@ -833,42 +392,75 @@ 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)); + if (delay == -1) + return; + + // 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; + + pump_->ScheduleDelayedWork(TimeDelta::FromMilliseconds(delay)); +} + +bool MessageLoop::DoWork() { + if (ProcessNextDeferredTask()) { + // Let the MessagePump know that we may have more work to do. It is + // tempting to only call this function if our work queue is not empty, but + // doing so is insufficient. The issue is that ReloadWorkQueue does not + // look at the incoming queue if the work queue is not empty, and we may + // have just processed the last task on the work queue. ScheduleWork is + // very low cost (in the case where it is redundant), so we don't worry + // about optimizing away spurious ScheduleWork calls. + pump_->ScheduleWork(); + return true; + } + return false; +} + +bool MessageLoop::DoDelayedWork() { + bool did_work = ProcessSomeTimers(); + + // We may not have run any timers, but we may still have future timers to + // run, so we need to inform the pump again of pending timers. + DidChangeNextTimerExpiry(); + + return did_work; +} + +bool MessageLoop::DoIdleWork() { + if (ProcessNextDelayedNonNestableTask()) + return true; + + if (state_->quit_received) + pump_->Quit(); + + return false; +} + +//------------------------------------------------------------------------------ +// MessageLoop::AutoRunState + +MessageLoop::AutoRunState::AutoRunState(MessageLoop* loop) : loop_(loop) { + // Make the loop reference us. + previous_state_ = loop_->state_; + if (previous_state_) { + run_depth = previous_state_->run_depth + 1; } 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); + run_depth = 1; } -#endif // defined(OS_WIN) + loop_->state_ = this; + + // Initialize the other fields: + quit_received = false; +#if defined(OS_WIN) + dispatcher = NULL; +#endif +} + +MessageLoop::AutoRunState::~AutoRunState() { + loop_->state_ = previous_state_; } //------------------------------------------------------------------------------ @@ -963,6 +555,7 @@ void MessageLoop::EnableHistogrammer(bool enable) { void MessageLoop::StartHistogrammer() { if (enable_histogrammer_ && !message_histogram_.get() && StatisticsRecorder::WasStarted()) { + DCHECK(!thread_name_.empty()); message_histogram_.reset(new LinearHistogram( ASCIIToWide("MsgLoop:" + thread_name_).c_str(), kLeastNonZeroMessageId, @@ -978,11 +571,6 @@ void MessageLoop::HistogramEvent(int event) { message_histogram_->Add(event); } -// Add one undocumented windows message to clean up our display. -#ifndef WM_SYSTIMER -#define WM_SYSTIMER 0x118 -#endif - // Provide a macro that takes an expression (such as a constant, or macro // constant) and creates a pair to initalize an array of pairs. In this case, // our pair consists of the expressions value, and the "stringized" version @@ -999,33 +587,13 @@ void MessageLoop::HistogramEvent(int event) { // in the pair (i.e., the quoted string) when printing out a histogram. #define VALUE_TO_NUMBER_AND_NAME(name) {name, #name}, - // static const LinearHistogram::DescriptionPair MessageLoop::event_descriptions_[] = { - // Only provide an extensive list in debug mode. In release mode, we have to - // read the octal values.... but we save about 450 strings, each of length - // 10 from our binary image. -#ifndef NDEBUG - // Prepare to include a list of names provided in a special header file4. -#define A_NAMED_MESSAGE_FROM_WINUSER_H VALUE_TO_NUMBER_AND_NAME -#include "base/windows_message_list.h" -#undef A_NAMED_MESSAGE_FROM_WINUSER_H - // Add an undocumented message that appeared in our list :-/. - VALUE_TO_NUMBER_AND_NAME(WM_SYSTIMER) -#endif // NDEBUG - // Provide some pretty print capability in our histogram for our internal // messages. - // Values we use for WM_USER+n - VALUE_TO_NUMBER_AND_NAME(kMsgPumpATask) - VALUE_TO_NUMBER_AND_NAME(kMsgQuit) - // A few events we handle (kindred to messages), and used to profile actions. VALUE_TO_NUMBER_AND_NAME(kTaskRunEvent) - VALUE_TO_NUMBER_AND_NAME(kSleepingApcEvent) - VALUE_TO_NUMBER_AND_NAME(kSleepingSignalEvent) - VALUE_TO_NUMBER_AND_NAME(kPollingSignalEvent) VALUE_TO_NUMBER_AND_NAME(kTimerEvent) {-1, NULL} // The list must be null terminated, per API to histogram. |