From 1ecc13d670d806f3442947f15a760e4e8b0facc1 Mon Sep 17 00:00:00 2001 From: "sadrul@chromium.org" Date: Mon, 28 Apr 2014 19:30:19 +0000 Subject: libevent message-pump: Fix a particular case of nested message-loop runs. If Quit() is called on the message-pump to terminate a nested loop, followed by a call to Run() again to start another nested loop, in the same iteration of the loop, then make sure it all works correctly. We have this scenario with nested menus, modal dialogs, drag-drop etc. in the browser UI. So this is a prerequisite fix to be able to use the libevent based message-pump on Chrome OS (and get rid of glib dep). BUG=240715 R=sky@chromium.org TBR=darin@chromium.org Review URL: https://codereview.chromium.org/245923005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@266628 0039d316-1c4b-4281-b951-d872f2087c98 --- base/message_loop/message_pump_libevent.cc | 6 +-- .../message_loop/message_pump_libevent_unittest.cc | 43 ++++++++++++++++++++++ 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/base/message_loop/message_pump_libevent.cc b/base/message_loop/message_pump_libevent.cc index 26be687..d52025a 100644 --- a/base/message_loop/message_pump_libevent.cc +++ b/base/message_loop/message_pump_libevent.cc @@ -217,7 +217,7 @@ static void timer_callback(int fd, short events, void *context) // Reentrant! void MessagePumpLibevent::Run(Delegate* delegate) { - DCHECK(keep_running_) << "Quit must have been called outside of Run!"; + AutoReset auto_reset_keep_running(&keep_running_, true); AutoReset auto_reset_in_run(&in_run_, true); // event_base_loopexit() + EVLOOP_ONCE is leaky, see http://crbug.com/25641. @@ -275,12 +275,10 @@ void MessagePumpLibevent::Run(Delegate* delegate) { } } } - - keep_running_ = true; } void MessagePumpLibevent::Quit() { - DCHECK(in_run_); + DCHECK(in_run_) << "Quit was called outside of Run!"; // Tell both libevent and Run that they should break out of their loops. keep_running_ = false; ScheduleWork(); diff --git a/base/message_loop/message_pump_libevent_unittest.cc b/base/message_loop/message_pump_libevent_unittest.cc index 4530e188..9298d68 100644 --- a/base/message_loop/message_pump_libevent_unittest.cc +++ b/base/message_loop/message_pump_libevent_unittest.cc @@ -6,8 +6,10 @@ #include +#include "base/bind.h" #include "base/message_loop/message_loop.h" #include "base/posix/eintr_wrapper.h" +#include "base/run_loop.h" #include "base/threading/thread.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/libevent/event.h" @@ -81,6 +83,12 @@ TEST_F(MessagePumpLibeventTest, TestWatchingFromBadThread) { "watch_file_descriptor_caller_checker_.CalledOnValidThread\\(\\)"); } +TEST_F(MessagePumpLibeventTest, QuitOutsideOfRun) { + scoped_ptr pump(new MessagePumpLibevent); + ASSERT_DEATH(pump->Quit(), "Check failed: in_run_. " + "Quit was called outside of Run!"); +} + #endif // GTEST_HAS_DEATH_TEST && !defined(NDEBUG) class BaseWatcher : public MessagePumpLibevent::Watcher { @@ -157,6 +165,41 @@ TEST_F(MessagePumpLibeventTest, StopWatcher) { OnLibeventNotification(pump.get(), &watcher); } +void QuitMessageLoopAndStart(const Closure& quit_closure) { + quit_closure.Run(); + + MessageLoop::ScopedNestableTaskAllower allow(MessageLoop::current()); + RunLoop runloop; + MessageLoop::current()->PostTask(FROM_HERE, runloop.QuitClosure()); + runloop.Run(); +} + +class NestedPumpWatcher : public MessagePumpLibevent::Watcher { + public: + NestedPumpWatcher() {} + virtual ~NestedPumpWatcher() {} + + virtual void OnFileCanReadWithoutBlocking(int /* fd */) OVERRIDE { + RunLoop runloop; + MessageLoop::current()->PostTask(FROM_HERE, Bind(&QuitMessageLoopAndStart, + runloop.QuitClosure())); + runloop.Run(); + } + + virtual void OnFileCanWriteWithoutBlocking(int /* fd */) OVERRIDE {} +}; + +TEST_F(MessagePumpLibeventTest, NestedPumpWatcher) { + scoped_ptr pump(new MessagePumpLibevent); + MessagePumpLibevent::FileDescriptorWatcher watcher; + NestedPumpWatcher delegate; + pump->WatchFileDescriptor(pipefds_[1], + false, MessagePumpLibevent::WATCH_READ, &watcher, &delegate); + + // Spoof a libevent notification. + OnLibeventNotification(pump.get(), &watcher); +} + } // namespace } // namespace base -- cgit v1.1