summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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() {