summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordarin@google.com <darin@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-08-15 04:32:57 +0000
committerdarin@google.com <darin@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-08-15 04:32:57 +0000
commit295039bdf97f08bf5c1c1c136dd977b7e97ddd31 (patch)
treeae3b47308e7d2f2db903f6a61c193e7a8c9ec882
parent1c33790ef99bf8301260b9144110e71e7723d0f4 (diff)
downloadchromium_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
-rw-r--r--base/build/base.vcproj28
-rw-r--r--base/message_loop.cc692
-rw-r--r--base/message_loop.h413
-rw-r--r--base/message_loop_unittest.cc29
-rw-r--r--base/message_pump.h143
-rw-r--r--base/message_pump_win.cc523
-rw-r--r--base/message_pump_win.h216
-rw-r--r--base/test_suite.h3
-rw-r--r--base/thread.cc2
-rw-r--r--chrome/app/chrome_dll_main.cc9
-rw-r--r--chrome/browser/automation/automation_provider.cc39
-rw-r--r--chrome/browser/automation/automation_provider.h7
-rw-r--r--chrome/browser/automation/automation_provider_list.cc8
-rw-r--r--chrome/browser/browser_init.cc9
-rw-r--r--chrome/browser/browser_main.cc2
-rw-r--r--chrome/browser/browser_process_impl.cc14
-rw-r--r--chrome/browser/navigation_controller_unittest.cc9
-rw-r--r--chrome/browser/printing/printing_layout_uitest.cc2
-rw-r--r--chrome/browser/render_process_host.cc1
-rw-r--r--chrome/browser/resource_dispatcher_host_unittest.cc6
-rw-r--r--chrome/browser/site_instance_unittest.cc3
-rw-r--r--chrome/browser/url_fetcher_unittest.cc6
-rw-r--r--chrome/browser/web_contents_unittest.cc3
-rw-r--r--chrome/common/child_process.cc2
-rw-r--r--chrome/common/chrome_switches.cc4
-rw-r--r--chrome/common/chrome_switches.h1
-rw-r--r--chrome/common/message_router.h1
-rw-r--r--chrome/renderer/render_thread.cc3
-rw-r--r--chrome/test/automation/automation_proxy.cc13
-rw-r--r--chrome/test/automation/automation_proxy.h8
-rw-r--r--chrome/test/ui/ui_test.cc4
-rw-r--r--net/http/http_cache_unittest.cc3
-rw-r--r--net/http/http_network_transaction_unittest.cc12
-rw-r--r--webkit/glue/bookmarklet_unittest.cc9
-rw-r--r--webkit/tools/test_shell/test_shell.cc6
-rw-r--r--webkit/tools/test_shell/test_shell_main.cc9
-rw-r--r--webkit/tools/test_shell/test_shell_test.cc5
37 files changed, 1268 insertions, 979 deletions
diff --git a/base/build/base.vcproj b/base/build/base.vcproj
index 35b9684..a6112e3 100644
--- a/base/build/base.vcproj
+++ b/base/build/base.vcproj
@@ -206,10 +206,6 @@
>
</File>
<File
- RelativePath="..\clipboard_win.cc"
- >
- </File>
- <File
RelativePath="..\clipboard.h"
>
</File>
@@ -222,6 +218,10 @@
>
</File>
<File
+ RelativePath="..\clipboard_win.cc"
+ >
+ </File>
+ <File
RelativePath="..\command_line.cc"
>
</File>
@@ -290,11 +290,11 @@
>
</File>
<File
- RelativePath="..\hash_tables.h"
+ RelativePath="..\float_util.h"
>
</File>
<File
- RelativePath="..\float_util.h"
+ RelativePath="..\hash_tables.h"
>
</File>
<File
@@ -418,6 +418,18 @@
>
</File>
<File
+ RelativePath="..\message_pump.h"
+ >
+ </File>
+ <File
+ RelativePath="..\message_pump_win.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\message_pump_win.h"
+ >
+ </File>
+ <File
RelativePath="..\non_thread_safe.cc"
>
</File>
@@ -582,11 +594,11 @@
>
</File>
<File
- RelativePath="..\shared_memory_win.cc"
+ RelativePath="..\shared_memory.h"
>
</File>
<File
- RelativePath="..\shared_memory.h"
+ RelativePath="..\shared_memory_win.cc"
>
</File>
<File
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.
diff --git a/base/message_loop.h b/base/message_loop.h
index 81c1e52..8ae12c8 100644
--- a/base/message_loop.h
+++ b/base/message_loop.h
@@ -27,8 +27,8 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#ifndef BASE_MESSAGE_LOOP_H__
-#define BASE_MESSAGE_LOOP_H__
+#ifndef BASE_MESSAGE_LOOP_H_
+#define BASE_MESSAGE_LOOP_H_
#include <deque>
#include <queue>
@@ -36,33 +36,39 @@
#include <vector>
#include "base/histogram.h"
+#include "base/message_pump.h"
#include "base/observer_list.h"
-#include "base/id_map.h"
+#include "base/ref_counted.h"
#include "base/task.h"
#include "base/timer.h"
#include "base/thread_local_storage.h"
-//
-// A MessageLoop is used to process events for a particular thread.
-// There is at most one MessageLoop instance per thread.
-// Events include Windows Message Queue messages, Tasks submitted to PostTask
-// or managed by TimerManager, APC calls (as time permits), and signals sent to
-// a registered set of HANDLES.
-// Processing events corresponds (respectively) to dispatching Windows messages,
-// running Tasks, yielding time to APCs, and calling Watchers when the
-// corresponding HANDLE is signaled.
+#if defined(OS_WIN)
+// We need this to declare base::MessagePumpWin::Dispatcher, which we should
+// really just eliminate.
+#include "base/message_pump_win.h"
+#endif
+// A MessageLoop is used to process events for a particular thread. There is
+// at most one MessageLoop instance per thread.
+//
+// Events include at minimum Task instances submitted to PostTask or those
+// managed by TimerManager. Depending on the type of message pump used by the
+// MessageLoop other events such as UI messages may be processed. On Windows
+// APC calls (as time permits) and signals sent to a registered set of HANDLEs
+// may also be processed.
//
// NOTE: Unless otherwise specified, a MessageLoop's methods may only be called
// on the thread where the MessageLoop's Run method executes.
//
-// WARNING: MessageLoop has task reentrancy protection. This means that if a
+// NOTE: MessageLoop has task reentrancy protection. This means that if a
// task is being processed, a second task cannot start until the first task is
-// finished. Reentrancy can happen when processing a task, and an inner message
-// pump is created. That inner pump then processes windows messages which could
-// implicitly start an inner task. Inner messages pumps are created with dialogs
-// (DialogBox), common dialogs (GetOpenFileName), OLE functions (DoDragDrop),
-// printer functions (StartDoc) and *many* others.
+// finished. Reentrancy can happen when processing a task, and an inner
+// message pump is created. That inner pump then processes native messages
+// which could implicitly start an inner task. Inner message pumps are created
+// with dialogs (DialogBox), common dialogs (GetOpenFileName), OLE functions
+// (DoDragDrop), printer functions (StartDoc) and *many* others.
+//
// Sample workaround when inner task processing is needed:
// bool old_state = MessageLoop::current()->NestableTasksAllowed();
// MessageLoop::current()->SetNestableTasksAllowed(true);
@@ -70,142 +76,13 @@
// MessageLoop::current()->SetNestableTasksAllowed(old_state);
// // Process hr (the result returned by DoDragDrop().
//
-// Please be **SURE** your task is reentrant and all global variables are stable
-// and accessible before calling SetNestableTasksAllowed(true).
-//
-
-// Message loop has several distinct functions. It provides message pumps,
-// responds to windows message dispatches, manipulates queues of Tasks.
-// The most central operation is the implementation of message pumps, along with
-// several subtleties.
-
-// MessageLoop currently implements several different message pumps. A message
-// pump is (traditionally) something that reads from an incoming queue, and then
-// dispatches the work.
-//
-// The first message pump, RunTraditional(), is among other things a
-// traditional Windows Message pump. It contains a nearly infinite loop that
-// peeks out messages, and then dispatches them.
-// Intermixed with those peeks are checks on a queue of Tasks, checks for
-// signaled objects, and checks to see if TimerManager has tasks to run.
-// When there are no events to be serviced, this pump goes into a wait state.
-// For 99.99% of all events, this first message pump handles all processing.
-//
-// When a task, or windows event, invokes on the stack a native dialog box or
-// such, that window typically provides a bare bones (native?) message pump.
-// That bare-bones message pump generally supports little more than a peek of
-// the Windows message queue, followed by a dispatch of the peeked message.
-// MessageLoop extends that bare-bones message pump to also service Tasks, at
-// the cost of some complexity.
-// The basic structure of the extension (refered to as a sub-pump) is that a
-// special message,kMsgPumpATask, is repeatedly injected into the Windows
-// Message queue. Each time the kMsgPumpATask message is peeked, checks are made
-// for an extended set of events, including the availability of Tasks to run.
-//
-// After running a task, the special message kMsgPumpATask is again posted to
-// the Windows Message queue, ensuring a future time slice for processing a
-// future event.
-//
-// To prevent flooding the Windows Message queue, care is taken to be sure that
-// at most one kMsgPumpATask message is EVER pending in the Winow's Message
-// queue.
+// Please be SURE your task is reentrant (nestable) and all global variables
+// are stable and accessible before calling SetNestableTasksAllowed(true).
//
-// There are a few additional complexities in this system where, when there are
-// no Tasks to run, this otherwise infinite stream of messages which drives the
-// sub-pump is halted. The pump is automatically re-started when Tasks are
-// queued.
-//
-// A second complexity is that the presence of this stream of posted tasks may
-// prevent a bare-bones message pump from ever peeking a WM_PAINT or WM_TIMER.
-// Such paint and timer events always give priority to a posted message, such as
-// kMsgPumpATask messages. As a result, care is taken to do some peeking in
-// between the posting of each kMsgPumpATask message (i.e., after kMsgPumpATask
-// is peeked, and before a replacement kMsgPumpATask is posted).
-//
-//
-// NOTE: Although it may seem odd that messages are used to start and stop this
-// flow (as opposed to signaling objects, etc.), it should be understood that
-// the native message pump will *only* respond to messages. As a result, it is
-// an excellent choice. It is also helpful that the starter messages that are
-// placed in the queue when new task arrive also awakens the RunTraditional()
-// loop.
-
-//------------------------------------------------------------------------------
-class MessageLoop {
+class MessageLoop : public base::MessagePump::Delegate {
public:
-
- // Select a non-default strategy for serving pending requests, that is to be
- // used by all MessageLoop instances. This is called only once before
- // constructing any instances.
- static void SetStrategy(int strategy);
static void EnableHistogrammer(bool enable_histogrammer);
-#ifdef OS_WIN
- // Used with WatchObject to asynchronously monitor the signaled state of a
- // HANDLE object.
- class Watcher {
- public:
- virtual ~Watcher() {}
- // Called from MessageLoop::Run when a signalled object is detected.
- virtual void OnObjectSignaled(HANDLE object) = 0;
- };
-
- // Have the current thread's message loop watch for a signaled object.
- // Pass a null watcher to stop watching the object.
- bool WatchObject(HANDLE, Watcher*);
-
- // An Observer is an object that receives global notifications from the
- // MessageLoop.
- //
- // NOTE: An Observer implementation should be extremely fast!
- //
- class Observer {
- public:
- virtual ~Observer() {}
-
- // This method is called before processing a message.
- // The message may be undefined in which case msg.message is 0
- virtual void WillProcessMessage(const MSG& msg) = 0;
-
- // This method is called when control returns from processing a UI message.
- // The message may be undefined in which case msg.message is 0
- virtual void DidProcessMessage(const MSG& msg) = 0;
- };
-
- // Add an Observer, which will start receiving notifications immediately.
- void AddObserver(Observer* observer);
-
- // Remove an Observer. It is safe to call this method while an Observer is
- // receiving a notification callback.
- void RemoveObserver(Observer* observer);
-
- // Give a chance to code processing additional messages to notify the
- // message loop observers that another message has been processed.
- void WillProcessMessage(const MSG& msg);
- void DidProcessMessage(const MSG& msg);
-
- // Dispatcher is used during a nested invocation of Run to dispatch events.
- // If Run is invoked with a non-NULL Dispatcher, MessageLoop does not
- // dispatch events (or invoke TranslateMessage), rather every message is
- // passed to Dispatcher's Dispatch method for dispatch. It is up to the
- // Dispatcher to dispatch, or not, the event.
- //
- // The nested loop is exited by either posting a quit, or returning false
- // from Dispatch.
- class Dispatcher {
- public:
- virtual ~Dispatcher() {}
- // Dispatches the event. If true is returned processing continues as
- // normal. If false is returned, the nested loop exits immediately.
- virtual bool Dispatch(const MSG& msg) = 0;
- };
-#else // !OS_WIN
- // On non-Windows platforms, the Dispatcher does not exist, but we allow the
- // typename to exist for convenience. On non-Windows platforms, a Dispatcher
- // pointer should always be NULL.
- class Dispatcher;
-#endif // OS_*
-
// A DestructionObserver is notified when the current MessageLoop is being
// destroyed. These obsevers are notified prior to MessageLoop::current()
// being changed to return NULL. This gives interested parties the chance to
@@ -283,19 +160,18 @@ class MessageLoop {
// Return as soon as all items that can be run are taken care of.
void RunAllPending();
- // See description of Dispatcher for how Run uses Dispatcher.
- void Run(Dispatcher* dispatcher);
-
// Signals the Run method to return after it is done processing all pending
- // messages. This method may be called from any thread, but no effort is
- // made to support concurrent calls to this method from multiple threads.
+ // messages. This method may only be called on the same thread that called
+ // Run, and Run must still be on the call stack.
+ //
+ // Use QuitTask if you need to Quit another thread's MessageLoop, but note
+ // that doing so is fairly dangerous if the target thread makes nested calls
+ // to MessageLoop::Run. The problem being that you won't know which nested
+ // run loop you are quiting, so be careful!
//
- // For example, the first call to Quit may lead to the MessageLoop being
- // deleted once its Run method returns, so a second call from another thread
- // could be problematic.
void Quit();
- // Invokes Quit on the current MessageLoop when run. Useful to schedule an
+ // Invokes Quit on the current MessageLoop when run. Useful to schedule an
// arbitrary MessageLoop to Quit.
class QuitTask : public Task {
public:
@@ -310,7 +186,10 @@ class MessageLoop {
~MessageLoop();
// Optional call to connect the thread name with this loop.
- void SetThreadName(const std::string& thread_name);
+ void set_thread_name(const std::string& thread_name) {
+ DCHECK(thread_name_.empty()) << "Should not rename this thread!";
+ thread_name_ = thread_name;
+ }
const std::string& thread_name() const { return thread_name_; }
// Returns the MessageLoop object for the current thread, or null if none.
@@ -347,51 +226,62 @@ class MessageLoop {
exception_restoration_ = restore;
}
- // Public entry point for TimerManager to request the Run() of a task. If we
- // created the task during an PostTask(FROM_HERE, ), then we will also perform
- // destructions, and we'll have the option of queueing the task. If we didn't
- // create the timer, then we will Run it immediately.
- bool RunTimerTask(Timer* timer);
+ //----------------------------------------------------------------------------
+#if defined(OS_WIN)
+ // Backwards-compat for the old Windows-specific MessageLoop API. These APIs
+ // are deprecated.
- // Since some Timer's are owned by MessageLoop, the TimerManager (when it is
- // being destructed) passses us the timers to discard (without doing a Run()).
- void DiscardTimer(Timer* timer);
+ typedef base::MessagePumpWin::Dispatcher Dispatcher;
+ typedef base::MessagePumpWin::Observer Observer;
+ typedef base::MessagePumpWin::Watcher Watcher;
- // Applications can call this to encourage us to process all pending WM_PAINT
- // messages.
- // This method will process all paint messages the Windows Message queue can
- // provide, up to some fixed number (to avoid any infinite loops).
- void PumpOutPendingPaintMessages();
+ void Run(Dispatcher* dispatcher);
+
+ void WatchObject(HANDLE object, Watcher* watcher) {
+ pump_win()->WatchObject(object, watcher);
+ }
+ void AddObserver(Observer* observer) {
+ pump_win()->AddObserver(observer);
+ }
+ void RemoveObserver(Observer* observer) {
+ pump_win()->RemoveObserver(observer);
+ }
+ void WillProcessMessage(const MSG& message) {
+ pump_win()->WillProcessMessage(message);
+ }
+ void DidProcessMessage(const MSG& message) {
+ pump_win()->DidProcessMessage(message);
+ }
+ void PumpOutPendingPaintMessages() {
+ pump_win()->PumpOutPendingPaintMessages();
+ }
+#endif // defined(OS_WIN)
//----------------------------------------------------------------------------
private:
friend class TimerManager; // So it can call DidChangeNextTimerExpiry
- struct ScopedStateSave {
- explicit ScopedStateSave(MessageLoop* loop)
- : loop_(loop),
- dispatcher_(loop->dispatcher_),
- quit_now_(loop->quit_now_),
- quit_received_(loop->quit_received_),
- run_depth_(loop->run_depth_) {
- loop->quit_now_ = loop->quit_received_ = false;
- ++loop->run_depth_;
- }
+ struct RunState {
+ // Used to count how many Run() invocations are on the stack.
+ int run_depth;
- ~ScopedStateSave() {
- loop_->run_depth_ = run_depth_;
- loop_->quit_received_ = quit_received_;
- loop_->quit_now_ = quit_now_;
- loop_->dispatcher_ = dispatcher_;
- }
+ // Used to record that Quit() was called, or that we should quit the pump
+ // once it becomes idle.
+ bool quit_received;
+
+#if defined(OS_WIN)
+ base::MessagePumpWin::Dispatcher* dispatcher;
+#endif
+ };
+ class AutoRunState : RunState {
+ public:
+ AutoRunState(MessageLoop* loop);
+ ~AutoRunState();
private:
MessageLoop* loop_;
- Dispatcher* dispatcher_;
- bool quit_now_;
- bool quit_received_;
- int run_depth_;
- }; // struct ScopedStateSave
+ RunState* previous_state_;
+ };
// A prioritized queue with interface that mostly matches std::queue<>.
// For debugging/performance testing, you can swap in std::queue<Task*>.
@@ -458,32 +348,22 @@ class MessageLoop {
DISALLOW_EVIL_CONSTRUCTORS(OptionallyPrioritizedTaskQueue);
};
-#ifdef OS_WIN
- void InitMessageWnd();
-
- // Windows procedure for message_hwnd_.
- static LRESULT CALLBACK WndProcThunk(
- HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam);
- LRESULT WndProc(
- HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam);
-#endif // OS_WIN
+#if defined(OS_WIN)
+ base::MessagePumpWin* pump_win() {
+ return static_cast<base::MessagePumpWin*>(pump_.get());
+ }
+#endif
// A function to encapsulate all the exception handling capability in the
- // stacks around the running of a main message loop.
- // It will run the message loop in a SEH try block or not depending on the
- // set_SEH_restoration() flag.
- void RunHandler(Dispatcher* dispatcher, bool non_blocking);
+ // stacks around the running of a main message loop. It will run the message
+ // loop in a SEH try block or not depending on the set_SEH_restoration()
+ // flag.
+ void RunHandler();
// A surrounding stack frame around the running of the message loop that
// supports all saving and restoring of state, as is needed for any/all (ugly)
// recursive calls.
- void RunInternal(Dispatcher* dispatcher, bool non_blocking);
-
- // An extended message loop (message pump) that loops mostly forever, and
- // processes task, signals, timers, etc.
- // If non-blocking is set, it will return rather than wait for new things to
- // arrive for processing.
- void RunTraditional(bool non_blocking);
+ void RunInternal();
//----------------------------------------------------------------------------
// A list of method wrappers with identical calling signatures (no arguments)
@@ -492,52 +372,18 @@ class MessageLoop {
bool ProcessNextDeferredTask();
bool ProcessNextDelayedNonNestableTask();
- bool ProcessNextObject();
bool ProcessSomeTimers();
//----------------------------------------------------------------------------
- // Process some pending messages. Returns true if a message was processed.
- bool ProcessNextWindowsMessage();
-
- // Wait until either an object is signaled, a message is available, a timer
- // needs attention, or our incoming_queue_ has gotten a task.
- // Handle (without returning) any APCs (only IO thread currently has APCs.)
- void WaitForWork();
-
-#ifdef OS_WIN
- // Helper function for processing window messages. This includes handling
- // WM_QUIT, message translation and dispatch, etc.
- //
- // If dispatcher_ is non-NULL this method does NOT dispatch the event, instead
- // it invokes Dispatch on the dispatcher_.
- bool ProcessMessageHelper(const MSG& msg);
-#endif // OS_WIN
-
- // When we encounter a kMsgPumpATask, the following helper can be called to
- // peek and process a replacement message, such as a WM_PAINT or WM_TIMER.
- // The goal is to make the kMsgPumpATask as non-intrusive as possible, even
- // though a continuous stream of such messages are posted. This method
- // carefully peeks a message while there is no chance for a kMsgPumpATask to
- // be pending, then releases the lock (allowing a replacement kMsgPumpATask to
- // possibly be posted), and finally dispatches that peeked replacement.
- // Note that the re-post of kMsgPumpATask may be asynchronous to this thread!!
- bool ProcessPumpReplacementMessage();
-
- // Signals a watcher if a wait falls within the range of objects we're
- // waiting on. object_index is the offset in objects_ that was signaled.
- // Returns true if an object was signaled.
- bool SignalWatcher(size_t object_index);
-
// Run a work_queue_ task or new_task, and delete it (if it was processed by
// PostTask). If there are queued tasks, the oldest one is executed and
// new_task is queued. new_task is optional and can be NULL. In this NULL
// case, the method will run one pending task (if any exist). Returns true if
- // it executes a task.
- // Queued tasks accumulate only when there is a nonreentrant task currently
- // processing, in which case the new_task is appended to the list
- // work_queue_. Such re-entrancy generally happens when an unrequested
- // message pump (typical of a native dialog) is executing in the context of a
- // task.
+ // it executes a task. Queued tasks accumulate only when there is a
+ // non-nestable task currently processing, in which case the new_task is
+ // appended to the list work_queue_. Such re-entrancy generally happens when
+ // an unrequested message pump (typical of a native dialog) is executing in
+ // the context of a task.
bool QueueOrRunTask(Task* new_task);
// Runs the specified task and deletes it.
@@ -548,14 +394,6 @@ class MessageLoop {
void BeforeTaskRunSetup();
void AfterTaskRunRestore();
- // When processing messages in our MessageWndProc(), we are sometimes called
- // by a native message pump (i.e., We are not called out of our Run() pump).
- // In those cases, we need to process tasks during the Windows Message
- // callback. This method processes a task, and also posts a new kMsgPumpATask
- // messages to the Windows Msg Queue so that we are called back later (to
- // process additional tasks).
- void PumpATaskDuringWndProc();
-
// Load tasks from the incoming_queue_ into work_queue_ if the latter is
// empty. The former requires a lock to access, while the latter is directly
// accessible on this thread.
@@ -565,19 +403,27 @@ class MessageLoop {
// destructor to make sure all the task's destructors get called.
void DeletePendingTasks();
- // Make sure a kPumpATask message is in flight, which starts/continues the
- // sub-pump.
- void EnsurePumpATaskWasPosted();
-
- // Do a PostMessage(), and crash if we can't eventually do the post.
- void EnsureMessageGetsPosted(int message) const;
-
// Post a task to our incomming queue.
void PostTaskInternal(Task* task);
// Called by the TimerManager when its next timer changes.
void DidChangeNextTimerExpiry();
+ // Entry point for TimerManager to request the Run() of a task. If we
+ // created the task during an PostTask(FROM_HERE, ), then we will also
+ // perform destructions, and we'll have the option of queueing the task. If
+ // we didn't create the timer, then we will Run it immediately.
+ bool RunTimerTask(Timer* timer);
+
+ // Since some Timer's are owned by MessageLoop, the TimerManager (when it is
+ // being destructed) passses us the timers to discard (without doing a Run()).
+ void DiscardTimer(Timer* timer);
+
+ // base::MessagePump::Delegate methods:
+ virtual bool DoWork();
+ virtual bool DoDelayedWork();
+ virtual bool DoIdleWork();
+
// 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();
@@ -588,7 +434,6 @@ class MessageLoop {
void HistogramEvent(int event);
static TLSSlot tls_index_;
- static int strategy_selector_;
static const LinearHistogram::DescriptionPair event_descriptions_[];
static bool enable_histogrammer_;
@@ -603,29 +448,15 @@ class MessageLoop {
// there was no real prioritization.
OptionallyPrioritizedTaskQueue work_queue_;
-#ifdef OS_WIN
- HWND message_hwnd_;
-
- // A vector of objects (and corresponding watchers) that are routinely
- // serviced by this message loop's pump.
- std::vector<HANDLE> objects_;
- std::vector<Watcher*> watchers_;
-
- ObserverList<Observer> observers_;
-#endif // OS_WIN
+ scoped_refptr<base::MessagePump> pump_;
ObserverList<DestructionObserver> destruction_observers_;
- IDMap<Task> timed_tasks_;
// A recursion block that prevents accidentally running additonal tasks when
// insider a (accidentally induced?) nested message pump.
bool nestable_tasks_allowed_;
bool exception_restoration_;
- Dispatcher* dispatcher_;
- bool quit_received_;
- bool quit_now_;
-
std::string thread_name_;
// A profiling histogram showing the counts of various messages and events.
scoped_ptr<LinearHistogram> message_histogram_;
@@ -643,17 +474,9 @@ class MessageLoop {
// will execute once we're out of nested message loops.
TaskQueue delayed_non_nestable_queue_;
- // Indicate if there is a kMsgPumpATask message pending in the Windows Message
- // queue. There is at most one such message, and it can drive execution of
- // tasks when a native message pump is running.
- bool task_pump_message_pending_;
- // Protect access to task_pump_message_pending_.
- Lock task_pump_message_lock_;
-
- // Used to count how many Run() invocations are on the stack.
- int run_depth_;
+ RunState* state_;
- DISALLOW_EVIL_CONSTRUCTORS(MessageLoop);
+ DISALLOW_COPY_AND_ASSIGN(MessageLoop);
};
-#endif // BASE_MESSAGE_LOOP_H__
+#endif // BASE_MESSAGE_LOOP_H_
diff --git a/base/message_loop_unittest.cc b/base/message_loop_unittest.cc
index 5cf4a99..fdd36ea 100644
--- a/base/message_loop_unittest.cc
+++ b/base/message_loop_unittest.cc
@@ -34,6 +34,10 @@
#include "base/ref_counted.h"
#include "testing/gtest/include/gtest/gtest.h"
+#if defined(OS_WIN)
+#include "base/message_pump_win.h"
+#endif
+
namespace {
class MessageLoopTest : public testing::Test {
@@ -258,6 +262,8 @@ LONG WINAPI HandleCrasherTaskException(EXCEPTION_POINTERS *ex_info) {
} // namespace
+#if defined(OS_WIN)
+
TEST(MessageLoopTest, Crasher) {
if (::IsDebuggerPresent())
return;
@@ -273,7 +279,6 @@ TEST(MessageLoopTest, Crasher) {
::SetUnhandledExceptionFilter(old_SEH_filter);
}
-
TEST(MessageLoopTest, CrasherNasty) {
if (::IsDebuggerPresent())
return;
@@ -289,6 +294,8 @@ TEST(MessageLoopTest, CrasherNasty) {
::SetUnhandledExceptionFilter(old_SEH_filter);
}
+#endif // defined(OS_WIN)
+
TEST(MessageLoopTest, Nesting) {
int depth = 100;
@@ -477,6 +484,8 @@ class QuitTask : public OrderedTasks {
}
};
+#if defined(OS_WIN)
+
class Recursive2Tasks : public Task {
public:
Recursive2Tasks(MessageLoop* target,
@@ -538,6 +547,8 @@ class Recursive2Tasks : public Task {
bool is_reentrant_;
};
+#endif // defined(OS_WIN)
+
} // namespace
TEST(MessageLoop, RecursiveDenial1) {
@@ -599,6 +610,10 @@ TEST(MessageLoop, RecursiveSupport1) {
EXPECT_EQ(order[13], TaskItem(RECURSIVE, 2, false));
}
+#if defined(OS_WIN)
+// TODO(darin): These tests need to be ported since they test critical
+// message loop functionality.
+
// A side effect of this test is the generation a beep. Sorry.
TEST(MessageLoop, RecursiveDenial2) {
Thread worker("RecursiveDenial2_worker");
@@ -680,6 +695,8 @@ TEST(MessageLoop, RecursiveSupport2) {
EXPECT_EQ(order[17], TaskItem(RECURSIVE, 3, false));
}
+#endif // defined(OS_WIN)
+
class TaskThatPumps : public OrderedTasks {
public:
TaskThatPumps(TaskList* order, int cookie)
@@ -689,9 +706,8 @@ class TaskThatPumps : public OrderedTasks {
virtual void Run() {
RunStart();
bool old_state = MessageLoop::current()->NestableTasksAllowed();
- MessageLoop::current()->Quit();
MessageLoop::current()->SetNestableTasksAllowed(true);
- MessageLoop::current()->Run();
+ MessageLoop::current()->RunAllPending();
MessageLoop::current()->SetNestableTasksAllowed(old_state);
RunEnd();
}
@@ -753,13 +769,16 @@ TEST(MessageLoop, NonNestableInNestedLoop) {
EXPECT_EQ(order[ 9], TaskItem(QUITMESSAGELOOP, 5, false));
}
+#if defined(OS_WIN)
namespace {
class AutoresetWatcher : public MessageLoop::Watcher {
public:
AutoresetWatcher(HANDLE signal, MessageLoop* message_loop)
- : signal_(signal), message_loop_(message_loop) {}
+ : signal_(signal),
+ message_loop_(message_loop) {
+ }
virtual void OnObjectSignaled(HANDLE object);
private:
HANDLE signal_;
@@ -848,3 +867,5 @@ TEST(MessageLoop, Dispatcher) {
MessageLoop::current()->Run(&dispatcher);
ASSERT_EQ(2, dispatcher.dispatch_count_);
}
+
+#endif
diff --git a/base/message_pump.h b/base/message_pump.h
new file mode 100644
index 0000000..8e80fc8
--- /dev/null
+++ b/base/message_pump.h
@@ -0,0 +1,143 @@
+// 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.
+
+#ifndef BASE_MESSAGE_PUMP_H_
+#define BASE_MESSAGE_PUMP_H_
+
+#include "base/ref_counted.h"
+
+class TimeDelta;
+
+namespace base {
+
+class MessagePump : public RefCountedThreadSafe<MessagePump> {
+ public:
+ // Please see the comments above the Run method for an illustration of how
+ // these delegate methods are used.
+ class Delegate {
+ public:
+ // Called from within Run in response to ScheduleWork or when the message
+ // pump would otherwise call DoDelayedWork. Returns true to indicate that
+ // work was done. DoDelayedWork will not be called if DoWork returns true.
+ virtual bool DoWork() = 0;
+
+ // Called from within Run in response to ScheduleDelayedWork or when the
+ // message pump would otherwise sleep waiting for more work. Returns true
+ // to indicate that delayed work was done. DoIdleWork will not be called
+ // if DoDelayedWork returns true.
+ virtual bool DoDelayedWork() = 0;
+
+ // Called from within Run just before the message pump goes to sleep.
+ // Returns true to indicate that idle work was done.
+ virtual bool DoIdleWork() = 0;
+ };
+
+ virtual ~MessagePump() {}
+
+ // The Run method is called to enter the message pump's run loop.
+ //
+ // Within the method, the message pump is responsible for processing native
+ // messages as well as for giving cycles to the delegate periodically. The
+ // message pump should take care to mix delegate callbacks with native
+ // message processing so neither type of event starves the other of cycles.
+ //
+ // The anatomy of a typical run loop:
+ //
+ // for (;;) {
+ // bool did_work = DoInternalWork();
+ // if (should_quit_)
+ // break;
+ // did_work |= delegate_->DoWork();
+ // if (should_quit_)
+ // break;
+ // if (did_work)
+ // continue;
+ //
+ // did_work = delegate_->DoDelayedWork();
+ // if (should_quit_)
+ // break;
+ // if (did_work)
+ // continue;
+ //
+ // did_work = delegate_->DoIdleWork();
+ // if (should_quit_)
+ // break;
+ // if (did_work)
+ // continue;
+ //
+ // WaitForWork();
+ // }
+ //
+ // Here, DoInternalWork is some private method of the message pump that is
+ // responsible for dispatching the next UI message or notifying the next IO
+ // completion (for example). WaitForWork is a private method that simply
+ // blocks until there is more work of any type to do.
+ //
+ // Notice that the run loop alternates between calling DoInternalWork and
+ // calling the delegate's DoWork method. This helps ensure that neither work
+ // queue starves the other. However, DoDelayedWork may be starved. The
+ // implementation may decide to periodically let some DoDelayedWork calls go
+ // through if either DoInternalWork or DoWork is dominating the run loop.
+ // This can be important for message pumps that are used to drive animations,
+ // for example.
+ //
+ // Notice also that after each callout to foreign code, the run loop checks
+ // to see if it should quit. The Quit method is responsible for setting this
+ // flag. No further work is done once the quit flag is set.
+ //
+ // NOTE: Care must be taken to handle Run being called again from within any
+ // of the callouts to foreign code. Native message pumps may also need to
+ // deal with other native message pumps being run outside their control
+ // (e.g., the MessageBox API on Windows pumps UI messages!). To be specific,
+ // the callouts (DoWork and DoDelayedWork) MUST still be provided even in
+ // nested sub-loops that are "seemingly" outside the control of this message
+ // pump. DoWork in particular must never be starved for time slices unless
+ // it returns false (meaning it has run out of things to do).
+ //
+ virtual void Run(Delegate* delegate) = 0;
+
+ // Quit immediately from the most recently entered run loop. This method may
+ // only be used on the thread that called Run.
+ virtual void Quit() = 0;
+
+ // Schedule a DoWork callback to happen reasonably soon. Does nothing if a
+ // DoWork callback is already scheduled. This method may be called from any
+ // thread. Once this call is made, DoWork should not be "starved" at least
+ // until it returns a value of false.
+ virtual void ScheduleWork() = 0;
+
+ // Schedule a DoDelayedWork callback to happen after the specified delay,
+ // cancelling any pending DoDelayedWork callback. This method may only be
+ // used on the thread that called Run.
+ virtual void ScheduleDelayedWork(const TimeDelta& delay) = 0;
+};
+
+} // namespace base
+
+#endif // BASE_MESSAGE_PUMP_H_
diff --git a/base/message_pump_win.cc b/base/message_pump_win.cc
new file mode 100644
index 0000000..b1809be
--- /dev/null
+++ b/base/message_pump_win.cc
@@ -0,0 +1,523 @@
+// 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/message_pump_win.h"
+
+#include "base/histogram.h"
+#include "base/win_util.h"
+
+namespace base {
+
+static const wchar_t kWndClass[] = L"Chrome_MessagePumpWindow";
+
+// 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 kMsgHaveWork = WM_USER + 1;
+
+#ifndef NDEBUG
+// Force exercise of polling model.
+static const int kMaxWaitObjects = 8;
+#else
+static const int kMaxWaitObjects = MAXIMUM_WAIT_OBJECTS;
+#endif
+
+//-----------------------------------------------------------------------------
+// MessagePumpWin public:
+
+MessagePumpWin::MessagePumpWin() : have_work_(0), state_(NULL) {
+ InitMessageWnd();
+}
+
+MessagePumpWin::~MessagePumpWin() {
+ DestroyWindow(message_hwnd_);
+}
+
+void MessagePumpWin::WatchObject(HANDLE object, Watcher* watcher) {
+ 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);
+ }
+}
+
+void MessagePumpWin::AddObserver(Observer* observer) {
+ observers_.AddObserver(observer);
+}
+
+void MessagePumpWin::RemoveObserver(Observer* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+void MessagePumpWin::WillProcessMessage(const MSG& msg) {
+ FOR_EACH_OBSERVER(Observer, observers_, WillProcessMessage(msg));
+}
+
+void MessagePumpWin::DidProcessMessage(const MSG& msg) {
+ FOR_EACH_OBSERVER(Observer, observers_, DidProcessMessage(msg));
+}
+
+void MessagePumpWin::PumpOutPendingPaintMessages() {
+ // Create a mini-message-pump to force immediate processing of only Windows
+ // WM_PAINT messages. 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;
+ bool win2k = win_util::GetWinVersion() <= win_util::WINVERSION_2000;
+ int peek_count;
+ 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 (state_->should_quit) // Handle WM_QUIT.
+ break;
+ }
+ // Histogram what was really being used, to help to adjust kMaxPeekCount.
+ DHISTOGRAM_COUNTS(L"Loop.PumpOutPendingPaintMessages Peeks", peek_count);
+}
+
+void MessagePumpWin::RunWithDispatcher(
+ Delegate* delegate, Dispatcher* dispatcher) {
+ RunState s;
+ s.delegate = delegate;
+ s.dispatcher = dispatcher;
+ s.should_quit = false;
+ s.run_depth = state_ ? state_->run_depth + 1 : 1;
+
+ RunState* previous_state = state_;
+ state_ = &s;
+
+ DoRunLoop();
+
+ state_ = previous_state;
+}
+
+void MessagePumpWin::Quit() {
+ DCHECK(state_);
+ state_->should_quit = true;
+}
+
+void MessagePumpWin::ScheduleWork() {
+ if (InterlockedExchange(&have_work_, 1))
+ return; // Someone else continued the pumping.
+
+ // Make sure the MessagePump does some work for us.
+ PostMessage(message_hwnd_, kMsgHaveWork, reinterpret_cast<WPARAM>(this), 0);
+}
+
+void MessagePumpWin::ScheduleDelayedWork(const TimeDelta& delay) {
+ //
+ // 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_msec = static_cast<int>(delay.InMilliseconds());
+ if (delay_msec < USER_TIMER_MINIMUM)
+ delay_msec = USER_TIMER_MINIMUM;
+
+ // 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_msec, NULL);
+}
+
+//-----------------------------------------------------------------------------
+// MessagePumpWin private:
+
+// static
+LRESULT CALLBACK MessagePumpWin::WndProcThunk(
+ HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {
+ switch (message) {
+ case kMsgHaveWork:
+ reinterpret_cast<MessagePumpWin*>(wparam)->HandleWorkMessage();
+ break;
+ case WM_TIMER:
+ reinterpret_cast<MessagePumpWin*>(wparam)->HandleTimerMessage();
+ break;
+ }
+ return DefWindowProc(hwnd, message, wparam, lparam);
+}
+
+void MessagePumpWin::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_);
+}
+
+void MessagePumpWin::HandleWorkMessage() {
+ // If we are being called outside of the context of Run, then don't do
+ // anything. This could correspond to a MessageBox call or something of
+ // that sort.
+ if (!state_)
+ return;
+
+ // Let whatever would have run had we not been putting messages in the queue
+ // run now. This is an attempt to make our dummy message not starve other
+ // messages that may be in the Windows message queue.
+ ProcessPumpReplacementMessage();
+
+ // Now give the delegate a chance to do some work. He'll let us know if he
+ // needs to do more work.
+ state_->delegate->DoWork();
+}
+
+void MessagePumpWin::HandleTimerMessage() {
+ KillTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this));
+
+ state_->delegate->DoDelayedWork();
+}
+
+void MessagePumpWin::DoRunLoop() {
+ // 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).
+
+ 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 (state_->should_quit)
+ break;
+
+ more_work_is_plausible |= state_->delegate->DoWork();
+ if (state_->should_quit)
+ break;
+
+ more_work_is_plausible |= ProcessNextObject();
+ if (state_->should_quit)
+ break;
+
+ if (more_work_is_plausible)
+ continue;
+
+ more_work_is_plausible = state_->delegate->DoDelayedWork();
+ if (state_->should_quit)
+ break;
+
+ if (more_work_is_plausible)
+ continue;
+
+ more_work_is_plausible = state_->delegate->DoIdleWork();
+ if (state_->should_quit)
+ break;
+
+ if (more_work_is_plausible)
+ continue;
+
+ // We service APCs in WaitForWork, without returning.
+ WaitForWork(); // Wait (sleep) until we have work to do again.
+ }
+}
+
+// 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 MessagePumpWin::WaitForWork() {
+ // Wait until either an object is signaled or a message is available. Handle
+ // (without returning) any APCs (only the IO thread currently has APCs.)
+
+ // We do not support nested message loops when we have watched objects. This
+ // is to avoid messy recursion problems.
+ DCHECK(objects_.empty() || state_->run_depth == 1) <<
+ "Cannot nest a message loop when there are watched objects!";
+
+ int wait_flags = MWMO_ALERTABLE | 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 = objects_.size();
+
+ 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 >= MAXIMUM_WAIT_OBJECTS) {
+ objs_len = 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 = 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) {
+ // 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);
+ 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 || !GetCurrentDelay())
+ return; // No work done, but timer is ready to fire.
+ }
+}
+
+bool MessagePumpWin::ProcessNextWindowsMessage() {
+ MSG msg;
+ if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
+ return ProcessMessageHelper(msg);
+ return false;
+}
+
+bool MessagePumpWin::ProcessMessageHelper(const MSG& msg) {
+ if (WM_QUIT == msg.message) {
+ // Repost the QUIT message so that it will be retrieved by the primary
+ // GetMessage() loop.
+ state_->should_quit = true;
+ PostQuitMessage(static_cast<int>(msg.wParam));
+ return false;
+ }
+
+ // While running our main message pump, we discard kMsgHaveWork messages.
+ if (msg.message == kMsgHaveWork && msg.hwnd == message_hwnd_)
+ return ProcessPumpReplacementMessage();
+
+ WillProcessMessage(msg);
+
+ if (state_->dispatcher) {
+ if (!state_->dispatcher->Dispatch(msg))
+ state_->should_quit = true;
+ } else {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+
+ DidProcessMessage(msg);
+ return true;
+}
+
+bool MessagePumpWin::ProcessPumpReplacementMessage() {
+ // When we encounter a kMsgHaveWork message, this method is called to peek
+ // and process a replacement message, such as a WM_PAINT or WM_TIMER. The
+ // goal is to make the kMsgHaveWork as non-intrusive as possible, even though
+ // a continuous stream of such messages are posted. This method carefully
+ // peeks a message while there is no chance for a kMsgHaveWork to be pending,
+ // then resets the have_work_ flag (allowing a replacement kMsgHaveWork to
+ // possibly be posted), and finally dispatches that peeked replacement. Note
+ // that the re-post of kMsgHaveWork may be asynchronous to this thread!!
+
+ MSG msg;
+ bool have_message = (0 != PeekMessage(&msg, NULL, 0, 0, PM_REMOVE));
+ DCHECK(!have_message || kMsgHaveWork != msg.message ||
+ msg.hwnd != message_hwnd_);
+
+#if 1
+
+ // Since we discarded a kMsgHaveWork message, we must update the flag.
+ InterlockedExchange(&have_work_, 0);
+
+ // TODO(darin,jar): There is risk of being lost in a sub-pump within the call
+ // to ProcessMessageHelper, which could result in no longer getting a
+ // kMsgHaveWork message until the next out-of-band call to ScheduleWork.
+
+ return have_message && ProcessMessageHelper(msg);
+
+#else
+
+ PostMessage(message_hwnd_, kMsgHaveWork, reinterpret_cast<WPARAM>(this), 0);
+
+ bool result = have_message && ProcessMessageHelper(msg);
+
+ PeekMessage(&msg, message_hwnd_, kMsgHaveWork, kMsgHaveWork, PM_REMOVE);
+ InterlockedExchange(&have_work_, 0);
+ // We know that are going to call DoWork next!
+
+ return result;
+
+#endif
+}
+
+// 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 MessagePumpWin::ProcessNextObject() {
+ 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 >= kMaxWaitObjects)
+ objs_len = kMaxWaitObjects - 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);
+ 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 MessagePumpWin::SignalWatcher(size_t object_index) {
+ // Signal the watcher corresponding to the given index.
+
+ 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 non-existant.
+ 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).
+
+ return true;
+}
+
+int MessagePumpWin::GetCurrentDelay() const {
+ if (delayed_work_time_.is_null())
+ return -1;
+
+ // This could be a negative value, but that's OK.
+ return static_cast<int>((Time::Now() - delayed_work_time_).InMilliseconds());
+}
+
+} // namespace base
diff --git a/base/message_pump_win.h b/base/message_pump_win.h
new file mode 100644
index 0000000..6699fde
--- /dev/null
+++ b/base/message_pump_win.h
@@ -0,0 +1,216 @@
+// 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.
+
+#ifndef BASE_MESSAGE_PUMP_WIN_H_
+#define BASE_MESSAGE_PUMP_WIN_H_
+
+#include <vector>
+
+#include <windows.h>
+
+#include "base/lock.h"
+#include "base/message_pump.h"
+#include "base/observer_list.h"
+#include "base/time.h"
+
+namespace base {
+
+// MessagePumpWin implements a "traditional" Windows message pump. It contains
+// a nearly infinite loop that peeks out messages, and then dispatches them.
+// Intermixed with those peeks are callouts to DoWork for pending tasks,
+// DoDelayedWork for pending timers, and OnObjectSignaled for signaled objects.
+// When there are no events to be serviced, this pump goes into a wait state.
+// In most cases, this message pump handles all processing.
+//
+// However, when a task, or windows event, invokes on the stack a native dialog
+// box or such, that window typically provides a bare bones (native?) message
+// pump. That bare-bones message pump generally supports little more than a
+// peek of the Windows message queue, followed by a dispatch of the peeked
+// message. MessageLoop extends that bare-bones message pump to also service
+// Tasks, at the cost of some complexity.
+//
+// The basic structure of the extension (refered to as a sub-pump) is that a
+// special message, kMsgHaveWork, is repeatedly injected into the Windows
+// Message queue. Each time the kMsgHaveWork message is peeked, checks are
+// made for an extended set of events, including the availability of Tasks to
+// run.
+//
+// After running a task, the special message kMsgHaveWork is again posted to
+// the Windows Message queue, ensuring a future time slice for processing a
+// future event. To prevent flooding the Windows Message queue, care is taken
+// to be sure that at most one kMsgHaveWork message is EVER pending in the
+// Window's Message queue.
+//
+// There are a few additional complexities in this system where, when there are
+// no Tasks to run, this otherwise infinite stream of messages which drives the
+// sub-pump is halted. The pump is automatically re-started when Tasks are
+// queued.
+//
+// A second complexity is that the presence of this stream of posted tasks may
+// prevent a bare-bones message pump from ever peeking a WM_PAINT or WM_TIMER.
+// Such paint and timer events always give priority to a posted message, such as
+// kMsgHaveWork messages. As a result, care is taken to do some peeking in
+// between the posting of each kMsgHaveWork message (i.e., after kMsgHaveWork
+// is peeked, and before a replacement kMsgHaveWork is posted).
+//
+// NOTE: Although it may seem odd that messages are used to start and stop this
+// flow (as opposed to signaling objects, etc.), it should be understood that
+// the native message pump will *only* respond to messages. As a result, it is
+// an excellent choice. It is also helpful that the starter messages that are
+// placed in the queue when new task arrive also awakens DoRunLoop.
+//
+class MessagePumpWin : public MessagePump {
+ public:
+ // Used with WatchObject to asynchronously monitor the signaled state of a
+ // HANDLE object.
+ class Watcher {
+ public:
+ virtual ~Watcher() {}
+ // Called from MessageLoop::Run when a signalled object is detected.
+ virtual void OnObjectSignaled(HANDLE object) = 0;
+ };
+
+ // An Observer is an object that receives global notifications from the
+ // MessageLoop.
+ //
+ // NOTE: An Observer implementation should be extremely fast!
+ //
+ class Observer {
+ public:
+ virtual ~Observer() {}
+
+ // This method is called before processing a message.
+ // The message may be undefined in which case msg.message is 0
+ virtual void WillProcessMessage(const MSG& msg) = 0;
+
+ // This method is called when control returns from processing a UI message.
+ // The message may be undefined in which case msg.message is 0
+ virtual void DidProcessMessage(const MSG& msg) = 0;
+ };
+
+ // Dispatcher is used during a nested invocation of Run to dispatch events.
+ // If Run is invoked with a non-NULL Dispatcher, MessageLoop does not
+ // dispatch events (or invoke TranslateMessage), rather every message is
+ // passed to Dispatcher's Dispatch method for dispatch. It is up to the
+ // Dispatcher to dispatch, or not, the event.
+ //
+ // The nested loop is exited by either posting a quit, or returning false
+ // from Dispatch.
+ class Dispatcher {
+ public:
+ virtual ~Dispatcher() {}
+ // Dispatches the event. If true is returned processing continues as
+ // normal. If false is returned, the nested loop exits immediately.
+ virtual bool Dispatch(const MSG& msg) = 0;
+ };
+
+ MessagePumpWin();
+ ~MessagePumpWin();
+
+ // Have the current thread's message loop watch for a signaled object.
+ // Pass a null watcher to stop watching the object.
+ void WatchObject(HANDLE, Watcher*);
+
+ // Add an Observer, which will start receiving notifications immediately.
+ void AddObserver(Observer* observer);
+
+ // Remove an Observer. It is safe to call this method while an Observer is
+ // receiving a notification callback.
+ void RemoveObserver(Observer* observer);
+
+ // Give a chance to code processing additional messages to notify the
+ // message loop observers that another message has been processed.
+ void WillProcessMessage(const MSG& msg);
+ void DidProcessMessage(const MSG& msg);
+
+ // Applications can call this to encourage us to process all pending WM_PAINT
+ // messages. This method will process all paint messages the Windows Message
+ // queue can provide, up to some fixed number (to avoid any infinite loops).
+ void PumpOutPendingPaintMessages();
+
+ // Like MessagePump::Run, but MSG objects are routed through dispatcher.
+ void RunWithDispatcher(Delegate* delegate, Dispatcher* dispatcher);
+
+ // MessagePump methods:
+ virtual void Run(Delegate* delegate) { RunWithDispatcher(delegate, NULL); }
+ virtual void Quit();
+ virtual void ScheduleWork();
+ virtual void ScheduleDelayedWork(const TimeDelta& delay);
+
+ private:
+ struct RunState {
+ Delegate* delegate;
+ Dispatcher* dispatcher;
+
+ // Used to flag that the current Run() invocation should return ASAP.
+ bool should_quit;
+
+ // Used to count how many Run() invocations are on the stack.
+ int run_depth;
+ };
+
+ static LRESULT CALLBACK WndProcThunk(
+ HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam);
+ void InitMessageWnd();
+ void HandleWorkMessage();
+ void HandleTimerMessage();
+ void DoRunLoop();
+ void WaitForWork();
+ bool ProcessNextWindowsMessage();
+ bool ProcessMessageHelper(const MSG& msg);
+ bool ProcessPumpReplacementMessage();
+ bool ProcessNextObject();
+ bool SignalWatcher(size_t object_index);
+ int GetCurrentDelay() const;
+
+ // A hidden message-only window.
+ HWND message_hwnd_;
+
+ // A vector of objects (and corresponding watchers) that are routinely
+ // serviced by this message pump.
+ std::vector<HANDLE> objects_;
+ std::vector<Watcher*> watchers_;
+
+ ObserverList<Observer> observers_;
+
+ // The time at which delayed work should run.
+ Time delayed_work_time_;
+
+ // A boolean value used to indicate if there is a kMsgDoWork message pending
+ // in the Windows Message queue. There is at most one such message, and it
+ // can drive execution of tasks when a native message pump is running.
+ LONG have_work_;
+
+ // State for the current invocation of Run.
+ RunState* state_;
+};
+
+} // namespace base
+
+#endif // BASE_MESSAGE_PUMP_WIN_H_
diff --git a/base/test_suite.h b/base/test_suite.h
index 3ee0787..2362f03 100644
--- a/base/test_suite.h
+++ b/base/test_suite.h
@@ -63,8 +63,7 @@ class TestSuite {
// Flush any remaining messages. This ensures that any accumulated Task
// objects get destroyed before we exit, which avoids noise in purify
// leak-test results.
- message_loop_.Quit();
- message_loop_.Run();
+ message_loop_.RunAllPending();
#endif
}
diff --git a/base/thread.cc b/base/thread.cc
index 4e626e3..629c7e0 100644
--- a/base/thread.cc
+++ b/base/thread.cc
@@ -212,7 +212,7 @@ unsigned __stdcall Thread::ThreadFunc(void* param) {
self = info->self;
self->message_loop_ = &message_loop;
SetThreadName(self->thread_name().c_str(), GetCurrentThreadId());
- message_loop.SetThreadName(self->thread_name());
+ message_loop.set_thread_name(self->thread_name());
info->start_event.Signal();
// info can't be touched anymore since the starting thread is now unlocked.
}
diff --git a/chrome/app/chrome_dll_main.cc b/chrome/app/chrome_dll_main.cc
index 0294bd2..8bf6493 100644
--- a/chrome/app/chrome_dll_main.cc
+++ b/chrome/app/chrome_dll_main.cc
@@ -193,15 +193,6 @@ DLLEXPORT int __cdecl ChromeMain(HINSTANCE instance,
exit(-1);
// Enable Message Loop related state asap.
- if (parsed_command_line.HasSwitch(switches::kMessageLoopStrategy)) {
- std::wstring details =
- parsed_command_line.GetSwitchValue(switches::kMessageLoopStrategy);
- int strategy = 0;
- if (details.size()) {
- strategy = static_cast<int>(StringToInt64(details));
- }
- MessageLoop::SetStrategy(strategy);
- }
if (parsed_command_line.HasSwitch(switches::kMessageLoopHistogrammer))
MessageLoop::EnableHistogrammer(true);
diff --git a/chrome/browser/automation/automation_provider.cc b/chrome/browser/automation/automation_provider.cc
index f23e7d1..d6e6b08 100644
--- a/chrome/browser/automation/automation_provider.cc
+++ b/chrome/browser/automation/automation_provider.cc
@@ -582,13 +582,8 @@ class DocumentPrintedNotificationObserver : public NotificationObserver {
};
AutomationProvider::AutomationProvider(Profile* profile)
- : connected_(false),
- redirect_query_(0),
+ : redirect_query_(0),
profile_(profile) {
- AutomationProviderList* list =
- g_browser_process->InitAutomationProviderList();
- DCHECK(NULL != list);
- list->AddProvider(this);
browser_tracker_.reset(new AutomationBrowserTracker(this));
window_tracker_.reset(new AutomationWindowTracker(this));
tab_tracker_.reset(new AutomationTabTracker(this));
@@ -601,21 +596,13 @@ AutomationProvider::AutomationProvider(Profile* profile)
}
AutomationProvider::~AutomationProvider() {
- // TODO(vibhor) : Delete the pending observer objects.
- AutomationProviderList* list =
- g_browser_process->InitAutomationProviderList();
- DCHECK(NULL != list);
- list->RemoveProvider(this);
}
void AutomationProvider::ConnectToChannel(const std::wstring& channel_id) {
- scoped_ptr<IPC::Channel> channel(
- new IPC::Channel(channel_id, IPC::Channel::MODE_CLIENT, this));
- connected_ = channel->Connect();
- if (connected_) {
- channel_.swap(channel);
- channel_->Send(new AutomationMsg_Hello(0));
- }
+ channel_.reset(
+ new IPC::ChannelProxy(channel_id, IPC::Channel::MODE_CLIENT, this, NULL,
+ g_browser_process->io_thread()->message_loop()));
+ channel_->Send(new AutomationMsg_Hello(0));
}
void AutomationProvider::SetExpectedTabCount(size_t expected_tabs) {
@@ -1450,7 +1437,7 @@ void AutomationProvider::HandleUnused(const IPC::Message& message, int handle) {
void AutomationProvider::OnChannelError() {
LOG(ERROR) << "AutomationProxy went away, shutting down app.";
- delete this;
+ AutomationProviderList::GetInstance()->RemoveProvider(this);
}
// TODO(brettw) change this to accept GURLs when history supports it
@@ -1478,11 +1465,8 @@ void AutomationProvider::OnRedirectQueryComplete(
}
bool AutomationProvider::Send(IPC::Message* msg) {
- if (connected_) {
- DCHECK(channel_.get());
- return channel_->Send(msg);
- }
- return false;
+ DCHECK(channel_.get());
+ return channel_->Send(msg);
}
Browser* AutomationProvider::FindAndActivateTab(
@@ -2235,7 +2219,8 @@ void TestingAutomationProvider::OnBrowserRemoving(const Browser* browser) {
// last browser goes away.
if (BrowserList::size() == 1) {
// If you change this, update Observer for NOTIFY_SESSION_END below.
- MessageLoop::current()->ReleaseSoon(FROM_HERE, this);
+ MessageLoop::current()->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &TestingAutomationProvider::OnRemoveProvider));
}
}
@@ -2248,3 +2233,7 @@ void TestingAutomationProvider::Observe(NotificationType type,
// Release balance out the Release scheduled by OnBrowserRemoving.
Release();
}
+
+void TestingAutomationProvider::OnRemoveProvider() {
+ AutomationProviderList::GetInstance()->RemoveProvider(this);
+}
diff --git a/chrome/browser/automation/automation_provider.h b/chrome/browser/automation/automation_provider.h
index 20f34a1..1bde370 100644
--- a/chrome/browser/automation/automation_provider.h
+++ b/chrome/browser/automation/automation_provider.h
@@ -47,7 +47,7 @@
#include "chrome/browser/automation/automation_autocomplete_edit_tracker.h"
#include "chrome/browser/browser_list.h"
#include "chrome/browser/history/history.h"
-#include "chrome/common/ipc_channel.h"
+#include "chrome/common/ipc_channel_proxy.h"
#include "chrome/common/ipc_message.h"
#include "chrome/common/notification_service.h"
@@ -321,8 +321,7 @@ class AutomationProvider : public base::RefCounted<AutomationProvider>,
typedef ObserverList<NotificationObserver> NotificationObserverList;
typedef std::map<NavigationController*, LoginHandler*> LoginHandlerMap;
- bool connected_;
- scoped_ptr<IPC::Channel> channel_;
+ scoped_ptr<IPC::ChannelProxy> channel_;
scoped_ptr<NotificationObserver> initial_load_observer_;
scoped_ptr<NotificationObserver> new_tab_ui_load_observer_;
scoped_ptr<NotificationObserver> find_in_page_observer_;
@@ -383,5 +382,7 @@ class TestingAutomationProvider : public AutomationProvider,
virtual void Observe(NotificationType type,
const NotificationSource& source,
const NotificationDetails& details);
+
+ void OnRemoveProvider(); // Called via PostTask
};
#endif // CHROME_BROWSER_AUTOMATION_AUTOMATION_PROVIDER_H_
diff --git a/chrome/browser/automation/automation_provider_list.cc b/chrome/browser/automation/automation_provider_list.cc
index 4d9bb93..8d683d8 100644
--- a/chrome/browser/automation/automation_provider_list.cc
+++ b/chrome/browser/automation/automation_provider_list.cc
@@ -41,18 +41,15 @@ AutomationProviderList::AutomationProviderList() {
AutomationProviderList::~AutomationProviderList() {
iterator iter = automation_providers_.begin();
while (iter != automation_providers_.end()) {
- AutomationProvider* provider = (*iter);
- // Delete the entry first and update the iterator first because the d'tor
- // of AutomationProvider will call RemoveProvider, making this iterator
- // invalid
+ (*iter)->Release();
iter = automation_providers_.erase(iter);
g_browser_process->ReleaseModule();
- delete provider;
}
instance_ = NULL;
}
bool AutomationProviderList::AddProvider(AutomationProvider* provider) {
+ provider->AddRef();
automation_providers_.push_back(provider);
g_browser_process->AddRefModule();
return true;
@@ -62,6 +59,7 @@ bool AutomationProviderList::RemoveProvider(AutomationProvider* provider) {
const iterator remove_provider =
find(automation_providers_.begin(), automation_providers_.end(), provider);
if (remove_provider != automation_providers_.end()) {
+ (*remove_provider)->Release();
automation_providers_.erase(remove_provider);
g_browser_process->ReleaseModule();
return true;
diff --git a/chrome/browser/browser_init.cc b/chrome/browser/browser_init.cc
index ff1fa47..e3a4630 100644
--- a/chrome/browser/browser_init.cc
+++ b/chrome/browser/browser_init.cc
@@ -718,8 +718,13 @@ template <class AutomationProviderClass>
void BrowserInit::CreateAutomationProvider(const std::wstring& channel_id,
Profile* profile,
size_t expected_tabs) {
- AutomationProviderClass* automation = new AutomationProviderClass(profile);
- automation->AddRef();
+ scoped_refptr<AutomationProviderClass> automation =
+ new AutomationProviderClass(profile);
automation->ConnectToChannel(channel_id);
automation->SetExpectedTabCount(expected_tabs);
+
+ AutomationProviderList* list =
+ g_browser_process->InitAutomationProviderList();
+ DCHECK(list);
+ list->AddProvider(automation);
}
diff --git a/chrome/browser/browser_main.cc b/chrome/browser/browser_main.cc
index 24f07ce..2287533 100644
--- a/chrome/browser/browser_main.cc
+++ b/chrome/browser/browser_main.cc
@@ -271,7 +271,7 @@ int BrowserMain(CommandLine &parsed_command_line, int show_command,
const char* main_thread_name = "Chrome_BrowserMain";
Thread::SetThreadName(main_thread_name, GetCurrentThreadId());
- MessageLoop::current()->SetThreadName(main_thread_name);
+ MessageLoop::current()->set_thread_name(main_thread_name);
bool already_running = CreateUniqueChromeEvent();
// Make the selection of network stacks early on before any consumers try to
diff --git a/chrome/browser/browser_process_impl.cc b/chrome/browser/browser_process_impl.cc
index ad12c305..3b0560b 100644
--- a/chrome/browser/browser_process_impl.cc
+++ b/chrome/browser/browser_process_impl.cc
@@ -216,14 +216,10 @@ BrowserProcessImpl::~BrowserProcessImpl() {
g_browser_process = NULL;
}
-// Need to define this so InvokeLater on the MessageLoop works. It's ok
-// not to addref/release the MessageLoop here as we *know* the main thread
-// isn't going to go away on us.
-template <>
-struct RunnableMethodTraits<MessageLoop> {
- static void RetainCallee(MessageLoop* obj) { }
- static void ReleaseCallee(MessageLoop* obj) { }
-};
+// Send a QuitTask to the given MessageLoop.
+static void PostQuit(MessageLoop* message_loop) {
+ message_loop->PostTask(FROM_HERE, new MessageLoop::QuitTask());
+}
void BrowserProcessImpl::EndSession() {
// Notify we are going away.
@@ -249,7 +245,7 @@ void BrowserProcessImpl::EndSession() {
// otherwise on startup we'll think we crashed. So we block until done and
// then proceed with normal shutdown.
g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE,
- NewRunnableMethod(MessageLoop::current(), &MessageLoop::Quit));
+ NewRunnableFunction(PostQuit, MessageLoop::current()));
MessageLoop::current()->Run();
}
diff --git a/chrome/browser/navigation_controller_unittest.cc b/chrome/browser/navigation_controller_unittest.cc
index 51eeb67..cce2cf9 100644
--- a/chrome/browser/navigation_controller_unittest.cc
+++ b/chrome/browser/navigation_controller_unittest.cc
@@ -666,8 +666,7 @@ TEST_F(NavigationControllerTest, SwitchTypes) {
EXPECT_TRUE(contents->controller()->CanGoForward());
// There may be TabContentsCollector tasks pending, so flush them from queue.
- MessageLoop::current()->Quit();
- MessageLoop::current()->Run();
+ MessageLoop::current()->RunAllPending();
}
// Tests what happens when we begin to navigate to a new contents type, but
@@ -700,8 +699,7 @@ TEST_F(NavigationControllerTest, SwitchTypes_Discard) {
EXPECT_FALSE(contents->controller()->CanGoForward());
// There may be TabContentsCollector tasks pending, so flush them from queue.
- MessageLoop::current()->Quit();
- MessageLoop::current()->Run();
+ MessageLoop::current()->RunAllPending();
}
// Tests that TabContentsTypes that are not in use are deleted (via a
@@ -729,8 +727,7 @@ TEST_F(NavigationControllerTest, SwitchTypesCleanup) {
contents->CompleteNavigation(1);
// There may be TabContentsCollector tasks pending, so flush them from queue.
- MessageLoop::current()->Quit();
- MessageLoop::current()->Run();
+ MessageLoop::current()->RunAllPending();
// Now that the tasks have been flushed, the first tab type should be gone.
ASSERT_TRUE(
diff --git a/chrome/browser/printing/printing_layout_uitest.cc b/chrome/browser/printing/printing_layout_uitest.cc
index 87c2c13..c3e2060 100644
--- a/chrome/browser/printing/printing_layout_uitest.cc
+++ b/chrome/browser/printing/printing_layout_uitest.cc
@@ -492,7 +492,7 @@ class DismissTheWindow : public Task {
MessageLoop::current()->timer_manager()->StopTimer(timer_);
timer_ = NULL;
// Unlock the other thread.
- other_thread_->Quit();
+ other_thread_->PostTask(FROM_HERE, new MessageLoop::QuitTask());
} else {
// Maybe it's time to try to click it again. Restart from the begining.
dialog_window_ = NULL;
diff --git a/chrome/browser/render_process_host.cc b/chrome/browser/render_process_host.cc
index be36149..558a161 100644
--- a/chrome/browser/render_process_host.cc
+++ b/chrome/browser/render_process_host.cc
@@ -285,7 +285,6 @@ bool RenderProcessHost::Init() {
switches::kAllowAllActiveX,
switches::kMemoryProfiling,
switches::kEnableWatchdog,
- switches::kMessageLoopStrategy,
switches::kMessageLoopHistogrammer,
switches::kEnableDCHECK,
switches::kSilentDumpOnDCHECK,
diff --git a/chrome/browser/resource_dispatcher_host_unittest.cc b/chrome/browser/resource_dispatcher_host_unittest.cc
index f2d8154..7d00f6a 100644
--- a/chrome/browser/resource_dispatcher_host_unittest.cc
+++ b/chrome/browser/resource_dispatcher_host_unittest.cc
@@ -146,11 +146,7 @@ class ResourceDispatcherHostTest : public testing::Test,
// Spin up the message loop to kick off the request.
static void KickOffRequest() {
- // First we post a Quit message to the loop, causing it to terminate after
- // processing the messages it currently has queued up.
- MessageLoop::current()->Quit();
- // Then we spin up the loop.
- MessageLoop::current()->Run();
+ MessageLoop::current()->RunAllPending();
}
void ResourceDispatcherHostTest::MakeTestRequest(int request_id,
diff --git a/chrome/browser/site_instance_unittest.cc b/chrome/browser/site_instance_unittest.cc
index 61fb7f0..d1cb055 100644
--- a/chrome/browser/site_instance_unittest.cc
+++ b/chrome/browser/site_instance_unittest.cc
@@ -129,8 +129,7 @@ TEST(SiteInstanceTest, SiteInstanceDestructor) {
contents->CloseContents();
// Make sure that we flush any messages related to WebContents destruction.
- MessageLoop::current()->Quit();
- MessageLoop::current()->Run();
+ MessageLoop::current()->RunAllPending();
EXPECT_EQ(2, siteDeleteCounter);
EXPECT_EQ(2, browsingDeleteCounter);
diff --git a/chrome/browser/url_fetcher_unittest.cc b/chrome/browser/url_fetcher_unittest.cc
index 2a3b034..4948e27 100644
--- a/chrome/browser/url_fetcher_unittest.cc
+++ b/chrome/browser/url_fetcher_unittest.cc
@@ -157,7 +157,7 @@ namespace {
// because the destructor won't necessarily run on the
// same thread that CreateFetcher() did.
- main_loop_->Quit();
+ main_loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask());
// If MessageLoop::current() != main_loop_, it will be shut down when the
// main loop returns and this thread subsequently goes out of scope.
}
@@ -209,7 +209,7 @@ namespace {
start_time_ = Time::Now();
fetcher_->Start();
}
-
+
void URLFetcherProtectTest::OnURLFetchComplete(const URLFetcher* source,
const GURL& url,
const URLRequestStatus& status,
@@ -224,7 +224,7 @@ namespace {
EXPECT_TRUE(status.is_success());
EXPECT_FALSE(data.empty());
delete fetcher_;
- main_loop_->Quit();
+ main_loop_->Quit();
} else {
// Now running Overload test.
static int count = 0;
diff --git a/chrome/browser/web_contents_unittest.cc b/chrome/browser/web_contents_unittest.cc
index 3b7450c..c62fcfd 100644
--- a/chrome/browser/web_contents_unittest.cc
+++ b/chrome/browser/web_contents_unittest.cc
@@ -316,8 +316,7 @@ class WebContentsTest : public testing::Test {
// Make sure that we flush any messages related to WebContents destruction
// before we destroy the profile.
- MessageLoop::current()->Quit();
- MessageLoop::current()->Run();
+ MessageLoop::current()->RunAllPending();
}
scoped_ptr<WebContentsTestingProfile> profile;
diff --git a/chrome/common/child_process.cc b/chrome/common/child_process.cc
index f501ba5..7d84338 100644
--- a/chrome/common/child_process.cc
+++ b/chrome/common/child_process.cc
@@ -68,7 +68,7 @@ bool ChildProcess::ProcessRefCountIsZero() {
void ChildProcess::OnFinalRelease() {
DCHECK(main_thread_loop_);
- main_thread_loop_->Quit();
+ main_thread_loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask());
}
HANDLE ChildProcess::GetShutDownEvent() {
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc
index 5b9985e..47f3080 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -289,10 +289,6 @@ const wchar_t kEnableWatchdog[] = L"enable-watchdog";
// whether or not it's actually the first run.
const wchar_t kFirstRun[] = L"first-run";
-// Select an alternate message loop task dispatch strategy.
-// Usage -message-loop-strategy=n
-const wchar_t kMessageLoopStrategy[] = L"message-loop-strategy";
-
// Enable histograming of tasks served by MessageLoop. See about:histograms/Loop
// for results, which show frequency of messages on each thread, including APC
// count, object signalling count, etc.
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h
index 591eded..fbffc88 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -117,7 +117,6 @@ extern const wchar_t kEnableWatchdog[];
extern const wchar_t kFirstRun[];
-extern const wchar_t kMessageLoopStrategy[];
extern const wchar_t kMessageLoopHistogrammer[];
extern const wchar_t kImport[];
diff --git a/chrome/common/message_router.h b/chrome/common/message_router.h
index a5fabf4..0558646 100644
--- a/chrome/common/message_router.h
+++ b/chrome/common/message_router.h
@@ -30,6 +30,7 @@
#ifndef CHROME_COMMON_MESSAGE_ROUTER_H__
#define CHROME_COMMON_MESSAGE_ROUTER_H__
+#include "base/id_map.h"
#include "chrome/common/ipc_channel.h"
// The MessageRouter handles all incoming messages sent to it by routing them
diff --git a/chrome/renderer/render_thread.cc b/chrome/renderer/render_thread.cc
index ddc8ebd..5282cad 100644
--- a/chrome/renderer/render_thread.cc
+++ b/chrome/renderer/render_thread.cc
@@ -68,8 +68,7 @@ RenderThread::~RenderThread() {
}
void RenderThread::OnChannelError() {
- // XXX(darin): is this really correct/sufficient?
- owner_loop_->Quit();
+ owner_loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask());
}
bool RenderThread::Send(IPC::Message* msg) {
diff --git a/chrome/test/automation/automation_proxy.cc b/chrome/test/automation/automation_proxy.cc
index 07e5387..6d98f0d 100644
--- a/chrome/test/automation/automation_proxy.cc
+++ b/chrome/test/automation/automation_proxy.cc
@@ -325,6 +325,10 @@ bool AutomationProxy::SetFilteredInet(bool enabled) {
return Send(new AutomationMsg_SetFilteredInet(0, enabled));
}
+void AutomationProxy::Disconnect() {
+ channel_.reset();
+}
+
void AutomationProxy::OnMessageReceived(const IPC::Message& msg) {
// This won't get called unless AutomationProxy is run from
// inside a message loop.
@@ -491,6 +495,15 @@ AutocompleteEditProxy* AutomationProxy::GetAutocompleteEditForBrowser(
autocomplete_edit_handle);
}
+bool AutomationProxy::Send(IPC::Message* message) {
+ if (channel_.get())
+ return channel_->Send(message);
+
+ DLOG(WARNING) << "Channel has been closed; dropping message!";
+ delete message;
+ return false;
+}
+
bool AutomationProxy::SendAndWaitForResponse(IPC::Message* request,
IPC::Message** response,
int response_type) {
diff --git a/chrome/test/automation/automation_proxy.h b/chrome/test/automation/automation_proxy.h
index e8d63b5..58ffbac 100644
--- a/chrome/test/automation/automation_proxy.h
+++ b/chrome/test/automation/automation_proxy.h
@@ -91,6 +91,9 @@ class AutomationProxy : public IPC::Channel::Listener,
virtual void OnMessageReceived(const IPC::Message& msg);
virtual void OnChannelError();
+ // Close the automation IPC channel.
+ void Disconnect();
+
// Waits for the app to launch and the automation provider to say hello
// (the app isn't fully done loading by this point).
// Returns true if the launch is successful
@@ -188,7 +191,7 @@ class AutomationProxy : public IPC::Channel::Listener,
const std::wstring& channel_id() const { return channel_id_; }
// AutomationMessageSender implementations.
- virtual bool Send(IPC::Message* message) { return channel_->Send(message); }
+ virtual bool Send(IPC::Message* message);
virtual bool SendAndWaitForResponse(IPC::Message* request,
IPC::Message** response,
int response_type);
@@ -211,7 +214,8 @@ class AutomationProxy : public IPC::Channel::Listener,
// Creates a tab that can hosted in an external process. The function
// returns a TabProxy representing the tab as well as a window handle
// that can be reparented in another process.
- TabProxy* AutomationProxy::CreateExternalTab(HWND* external_tab_container);
+ TabProxy* CreateExternalTab(HWND* external_tab_container);
+
private:
DISALLOW_EVIL_CONSTRUCTORS(AutomationProxy);
diff --git a/chrome/test/ui/ui_test.cc b/chrome/test/ui/ui_test.cc
index cd439118..439846f 100644
--- a/chrome/test/ui/ui_test.cc
+++ b/chrome/test/ui/ui_test.cc
@@ -296,6 +296,10 @@ void UITest::QuitBrowser() {
++iter) {
::PostMessage(*iter, WM_CLOSE, 0, 0);
}
+
+ // Now, drop the automation IPC channel so that the automation provider in
+ // the browser notices and drops its reference to the browser process.
+ server_->Disconnect();
// Wait for the browser process to quit. It should quit once all tabs have
// been closed.
diff --git a/net/http/http_cache_unittest.cc b/net/http/http_cache_unittest.cc
index 8f5db81..1e3ed15 100644
--- a/net/http/http_cache_unittest.cc
+++ b/net/http/http_cache_unittest.cc
@@ -651,8 +651,7 @@ TEST(HttpCache, SimpleGET_AbandonedCacheRead) {
// Make sure we pump any pending events, which should include a call to
// HttpCache::Transaction::OnCacheReadCompleted.
- MessageLoop::current()->Quit();
- MessageLoop::current()->Run();
+ MessageLoop::current()->RunAllPending();
}
TEST(HttpCache, TypicalGET_ConditionalRequest) {
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc
index 2dc9721..7a891c7 100644
--- a/net/http/http_network_transaction_unittest.cc
+++ b/net/http/http_network_transaction_unittest.cc
@@ -248,8 +248,7 @@ TEST_F(HttpNetworkTransactionTest, SimpleGET) {
trans->Destroy();
// Empty the current queue.
- MessageLoop::current()->Quit();
- MessageLoop::current()->Run();
+ MessageLoop::current()->RunAllPending();
}
TEST_F(HttpNetworkTransactionTest, ReuseConnection) {
@@ -304,8 +303,7 @@ TEST_F(HttpNetworkTransactionTest, ReuseConnection) {
trans->Destroy();
// Empty the current queue.
- MessageLoop::current()->Quit();
- MessageLoop::current()->Run();
+ MessageLoop::current()->RunAllPending();
}
}
@@ -355,8 +353,7 @@ TEST_F(HttpNetworkTransactionTest, Ignores100) {
trans->Destroy();
// Empty the current queue.
- MessageLoop::current()->Quit();
- MessageLoop::current()->Run();
+ MessageLoop::current()->RunAllPending();
}
TEST_F(HttpNetworkTransactionTest, KeepAliveConnectionReset) {
@@ -419,7 +416,6 @@ TEST_F(HttpNetworkTransactionTest, KeepAliveConnectionReset) {
trans->Destroy();
// Empty the current queue.
- MessageLoop::current()->Quit();
- MessageLoop::current()->Run();
+ MessageLoop::current()->RunAllPending();
}
}
diff --git a/webkit/glue/bookmarklet_unittest.cc b/webkit/glue/bookmarklet_unittest.cc
index cadff00..a191dfe 100644
--- a/webkit/glue/bookmarklet_unittest.cc
+++ b/webkit/glue/bookmarklet_unittest.cc
@@ -60,14 +60,12 @@ TEST_F(BookmarkletTest, Redirect) {
TEST_F(BookmarkletTest, NonEmptyResult) {
test_shell_->LoadURL(L"javascript:false");
- MessageLoop::current()->Quit();
- MessageLoop::current()->Run();
+ MessageLoop::current()->RunAllPending();
std::wstring text = test_shell_->GetDocumentText();
EXPECT_EQ(L"false", text);
test_shell_->LoadURL(L"javascript:'hello world'");
- MessageLoop::current()->Quit();
- MessageLoop::current()->Run();
+ MessageLoop::current()->RunAllPending();
text = test_shell_->GetDocumentText();
EXPECT_EQ(L"hello world", text);
}
@@ -77,8 +75,7 @@ TEST_F(BookmarkletTest, DocumentWrite) {
L"javascript:document.open();"
L"document.write('hello world');"
L"document.close()");
- MessageLoop::current()->Quit();
- MessageLoop::current()->Run();
+ MessageLoop::current()->RunAllPending();
std::wstring text = test_shell_->GetDocumentText();
EXPECT_EQ(L"hello world", text);
}
diff --git a/webkit/tools/test_shell/test_shell.cc b/webkit/tools/test_shell/test_shell.cc
index a256646..89585ae 100644
--- a/webkit/tools/test_shell/test_shell.cc
+++ b/webkit/tools/test_shell/test_shell.cc
@@ -769,8 +769,10 @@ LRESULT CALLBACK TestShell::WndProc(HWND hwnd, UINT message, WPARAM wParam, LPAR
if (entry != TestShell::windowList()->end())
TestShell::windowList()->erase(entry);
- if (TestShell::windowList()->empty() || shell->is_modal())
- MessageLoop::current()->Quit();
+ if (TestShell::windowList()->empty() || shell->is_modal()) {
+ MessageLoop::current()->PostTask(
+ FROM_HERE, new MessageLoop::QuitTask());
+ }
delete shell;
}
return 0;
diff --git a/webkit/tools/test_shell/test_shell_main.cc b/webkit/tools/test_shell/test_shell_main.cc
index 6c71ecf..0d35724 100644
--- a/webkit/tools/test_shell/test_shell_main.cc
+++ b/webkit/tools/test_shell/test_shell_main.cc
@@ -340,11 +340,10 @@ int main(int argc, char* argv[])
MessageLoop::current()->Run();
}
- // Flush any remaining messages. This ensures that any
- // accumulated Task objects get destroyed before we exit,
- // which avoids noise in purify leak-test results.
- MessageLoop::current()->Quit();
- MessageLoop::current()->Run();
+ // Flush any remaining messages. This ensures that any accumulated
+ // Task objects get destroyed before we exit, which avoids noise in
+ // purify leak-test results.
+ MessageLoop::current()->RunAllPending();
if (record_mode)
base::EventRecorder::current()->StopRecording();
diff --git a/webkit/tools/test_shell/test_shell_test.cc b/webkit/tools/test_shell/test_shell_test.cc
index 8b433d2..8f8b251 100644
--- a/webkit/tools/test_shell/test_shell_test.cc
+++ b/webkit/tools/test_shell/test_shell_test.cc
@@ -28,7 +28,9 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "webkit/tools/test_shell/test_shell_test.h"
+
#include "base/file_util.h"
+#include "base/message_loop.h"
#include "base/path_service.h"
#include "base/string_util.h"
@@ -56,6 +58,9 @@ void TestShellTest::TearDown() {
test_shell_->LoadURL(L"about:blank");
DestroyWindow(test_shell_->mainWnd());
LayoutTestController::ClearShell();
+
+ // Flush the MessageLoop of any residual tasks.
+ MessageLoop::current()->RunAllPending();
}
void TestShellTest::CreateEmptyWindow() {