Kick the pump when allowing nestable tasks on a message loop
BUG=318998
Review URL: https://codereview.chromium.org/65173003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@236405 0039d316-1c4b-4281-b951-d872f2087c98
CrOS-Libchrome-Original-Commit: ea769a47f69d4ca03585009f86190f59dbf2c675
diff --git a/base/message_loop/message_loop.cc b/base/message_loop/message_loop.cc
index e035726..3f9d01c 100644
--- a/base/message_loop/message_loop.cc
+++ b/base/message_loop/message_loop.cc
@@ -358,13 +358,12 @@
}
void MessageLoop::SetNestableTasksAllowed(bool allowed) {
- if (nestable_tasks_allowed_ != allowed) {
- nestable_tasks_allowed_ = allowed;
- if (!nestable_tasks_allowed_)
- return;
- // Start the native pump if we are not already pumping.
+ if (allowed) {
+ // Kick the native pump just in case we enter a OS-driven nested message
+ // loop.
pump_->ScheduleWork();
}
+ nestable_tasks_allowed_ = allowed;
}
bool MessageLoop::NestableTasksAllowed() const {
diff --git a/base/message_loop/message_loop_unittest.cc b/base/message_loop/message_loop_unittest.cc
index fe2d728..f378db2 100644
--- a/base/message_loop/message_loop_unittest.cc
+++ b/base/message_loop/message_loop_unittest.cc
@@ -23,6 +23,8 @@
#if defined(OS_WIN)
#include "base/message_loop/message_pump_win.h"
+#include "base/process/memory.h"
+#include "base/strings/string16.h"
#include "base/win/scoped_handle.h"
#endif
@@ -1080,4 +1082,92 @@
EXPECT_FALSE(loop.IsType(MessageLoop::TYPE_DEFAULT));
}
+#if defined(OS_WIN)
+void EmptyFunction() {}
+
+void PostMultipleTasks() {
+ MessageLoop::current()->PostTask(FROM_HERE, base::Bind(&EmptyFunction));
+ MessageLoop::current()->PostTask(FROM_HERE, base::Bind(&EmptyFunction));
+}
+
+static const int kSignalMsg = WM_USER + 2;
+
+void PostWindowsMessage(HWND message_hwnd) {
+ PostMessage(message_hwnd, kSignalMsg, 0, 2);
+}
+
+void EndTest(bool* did_run, HWND hwnd) {
+ *did_run = true;
+ PostMessage(hwnd, WM_CLOSE, 0, 0);
+}
+
+int kMyMessageFilterCode = 0x5002;
+
+LRESULT CALLBACK TestWndProcThunk(HWND hwnd, UINT message,
+ WPARAM wparam, LPARAM lparam) {
+ if (message == WM_CLOSE)
+ EXPECT_TRUE(DestroyWindow(hwnd));
+ if (message != kSignalMsg)
+ return DefWindowProc(hwnd, message, wparam, lparam);
+
+ switch (lparam) {
+ case 1:
+ // First, we post a task that will post multiple no-op tasks to make sure
+ // that the pump's incoming task queue does not become empty during the
+ // test.
+ MessageLoop::current()->PostTask(FROM_HERE, base::Bind(&PostMultipleTasks));
+ // Next, we post a task that posts a windows message to trigger the second
+ // stage of the test.
+ MessageLoop::current()->PostTask(FROM_HERE,
+ base::Bind(&PostWindowsMessage, hwnd));
+ break;
+ case 2:
+ // Since we're about to enter a modal loop, tell the message loop that we
+ // intend to nest tasks.
+ MessageLoop::current()->SetNestableTasksAllowed(true);
+ bool did_run = false;
+ MessageLoop::current()->PostTask(FROM_HERE,
+ base::Bind(&EndTest, &did_run, hwnd));
+ // Run a nested windows-style message loop and verify that our task runs. If
+ // it doesn't, then we'll loop here until the test times out.
+ MSG msg;
+ while (GetMessage(&msg, 0, 0, 0)) {
+ if (!CallMsgFilter(&msg, kMyMessageFilterCode))
+ DispatchMessage(&msg);
+ // If this message is a WM_CLOSE, explicitly exit the modal loop. Posting
+ // a WM_QUIT should handle this, but unfortunately MessagePumpWin eats
+ // WM_QUIT messages even when running inside a modal loop.
+ if (msg.message == WM_CLOSE)
+ break;
+ }
+ EXPECT_TRUE(did_run);
+ MessageLoop::current()->Quit();
+ break;
+ }
+ return 0;
+}
+
+TEST(MessageLoopTest, AlwaysHaveUserMessageWhenNesting) {
+ MessageLoop loop(MessageLoop::TYPE_UI);
+ HINSTANCE instance = GetModuleFromAddress(&TestWndProcThunk);
+ WNDCLASSEX wc = {0};
+ wc.cbSize = sizeof(wc);
+ wc.lpfnWndProc = TestWndProcThunk;
+ wc.hInstance = instance;
+ wc.lpszClassName = L"MessageLoopTest_HWND";
+ ATOM atom = RegisterClassEx(&wc);
+ ASSERT_TRUE(atom);
+
+ HWND message_hwnd = CreateWindow(MAKEINTATOM(atom), 0, 0, 0, 0, 0, 0,
+ HWND_MESSAGE, 0, instance, 0);
+ ASSERT_TRUE(message_hwnd) << GetLastError();
+
+ ASSERT_TRUE(PostMessage(message_hwnd, kSignalMsg, 0, 1));
+
+ loop.Run();
+
+ ASSERT_TRUE(UnregisterClass(MAKEINTATOM(atom), instance));
+}
+#endif // defined(OS_WIN)
+
} // namespace base