diff options
author | brettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-06-18 20:27:52 +0000 |
---|---|---|
committer | brettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-06-18 20:27:52 +0000 |
commit | 59e69e745ba3fa290ff3c50e65c3db03ee9dde6b (patch) | |
tree | 64b82fd1e2375845b59f278f2a1e892dcf54377b /base/message_loop | |
parent | bd41e70e44d19eb809eb8565b1d8479daef5d8f5 (diff) | |
download | chromium_src-59e69e745ba3fa290ff3c50e65c3db03ee9dde6b.zip chromium_src-59e69e745ba3fa290ff3c50e65c3db03ee9dde6b.tar.gz chromium_src-59e69e745ba3fa290ff3c50e65c3db03ee9dde6b.tar.bz2 |
Move message_pump to base/message_loop.
This also fixes some namespace usage inside the message pump files and updates all users of these files to use the new location.
Reland of 206507.
Original review https://codereview.chromium.org/17078005/
TBR=sky
Review URL: https://codereview.chromium.org/16897006
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@207075 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base/message_loop')
30 files changed, 5755 insertions, 94 deletions
diff --git a/base/message_loop/message_loop.cc b/base/message_loop/message_loop.cc index 0364a52..4e0c5f6 100644 --- a/base/message_loop/message_loop.cc +++ b/base/message_loop/message_loop.cc @@ -14,7 +14,7 @@ #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop_proxy_impl.h" -#include "base/message_pump_default.h" +#include "base/message_loop/message_pump_default.h" #include "base/metrics/histogram.h" #include "base/metrics/statistics_recorder.h" #include "base/run_loop.h" @@ -25,13 +25,13 @@ #include "base/tracked_objects.h" #if defined(OS_MACOSX) -#include "base/message_pump_mac.h" +#include "base/message_loop/message_pump_mac.h" #endif #if defined(OS_POSIX) && !defined(OS_IOS) -#include "base/message_pump_libevent.h" +#include "base/message_loop/message_pump_libevent.h" #endif #if defined(OS_ANDROID) -#include "base/message_pump_android.h" +#include "base/message_loop/message_pump_android.h" #endif #if defined(TOOLKIT_GTK) diff --git a/base/message_loop/message_loop.h b/base/message_loop/message_loop.h index d26b673..e765cef 100644 --- a/base/message_loop/message_loop.h +++ b/base/message_loop/message_loop.h @@ -14,7 +14,7 @@ #include "base/location.h" #include "base/memory/ref_counted.h" #include "base/message_loop/message_loop_proxy.h" -#include "base/message_pump.h" +#include "base/message_loop/message_pump.h" #include "base/observer_list.h" #include "base/pending_task.h" #include "base/sequenced_task_runner_helpers.h" @@ -25,25 +25,26 @@ #if defined(OS_WIN) // We need this to declare base::MessagePumpWin::Dispatcher, which we should // really just eliminate. -#include "base/message_pump_win.h" +#include "base/message_loop/message_pump_win.h" #elif defined(OS_IOS) -#include "base/message_pump_io_ios.h" +#include "base/message_loop/message_pump_io_ios.h" #elif defined(OS_POSIX) -#include "base/message_pump_libevent.h" +#include "base/message_loop/message_pump_libevent.h" #if !defined(OS_MACOSX) && !defined(OS_ANDROID) #if defined(USE_AURA) && defined(USE_X11) && !defined(OS_NACL) -#include "base/message_pump_aurax11.h" +#include "base/message_loop/message_pump_aurax11.h" #elif defined(USE_OZONE) && !defined(OS_NACL) -#include "base/message_pump_ozone.h" +#include "base/message_loop/message_pump_ozone.h" #else -#include "base/message_pump_gtk.h" +#include "base/message_loop/message_pump_gtk.h" #endif #endif #endif namespace base { + class HistogramBase; class MessageLoopLockTest; class RunLoop; @@ -83,12 +84,12 @@ class MessagePumpForUI; // Please be SURE your task is reentrant (nestable) and all global variables // are stable and accessible before calling SetNestableTasksAllowed(true). // -class BASE_EXPORT MessageLoop : public base::MessagePump::Delegate { +class BASE_EXPORT MessageLoop : public MessagePump::Delegate { public: #if !defined(OS_MACOSX) && !defined(OS_ANDROID) - typedef base::MessagePumpDispatcher Dispatcher; - typedef base::MessagePumpObserver Observer; + typedef MessagePumpDispatcher Dispatcher; + typedef MessagePumpObserver Observer; #endif // A MessageLoop has a particular type, which indicates the set of @@ -121,7 +122,7 @@ class BASE_EXPORT MessageLoop : public base::MessagePump::Delegate { static void EnableHistogrammer(bool enable_histogrammer); - typedef base::MessagePump* (MessagePumpFactory)(); + typedef MessagePump* (MessagePumpFactory)(); // Uses the given base::MessagePumpForUIFactory to override the default // MessagePump implementation for 'TYPE_UI'. Returns true if the factory // was successfully registered. @@ -174,27 +175,22 @@ class BASE_EXPORT MessageLoop : public base::MessagePump::Delegate { // // NOTE: These methods may be called on any thread. The Task will be invoked // on the thread that executes MessageLoop::Run(). - void PostTask( - const tracked_objects::Location& from_here, - const base::Closure& task); + void PostTask(const tracked_objects::Location& from_here, + const Closure& task); - bool TryPostTask( - const tracked_objects::Location& from_here, - const base::Closure& task); + bool TryPostTask(const tracked_objects::Location& from_here, + const Closure& task); - void PostDelayedTask( - const tracked_objects::Location& from_here, - const base::Closure& task, - base::TimeDelta delay); + void PostDelayedTask(const tracked_objects::Location& from_here, + const Closure& task, + TimeDelta delay); - void PostNonNestableTask( - const tracked_objects::Location& from_here, - const base::Closure& task); + void PostNonNestableTask(const tracked_objects::Location& from_here, + const Closure& task); - void PostNonNestableDelayedTask( - const tracked_objects::Location& from_here, - const base::Closure& task, - base::TimeDelta delay); + void PostNonNestableDelayedTask(const tracked_objects::Location& from_here, + const Closure& task, + TimeDelta delay); // A variant on PostTask that deletes the given object. This is useful // if the object needs to live until the next run of the MessageLoop (for @@ -264,12 +260,12 @@ class BASE_EXPORT MessageLoop : public base::MessagePump::Delegate { void QuitNow(); // TODO(jbates) remove this. crbug.com/131220. See QuitWhenIdleClosure(). - static base::Closure QuitClosure() { return QuitWhenIdleClosure(); } + static Closure QuitClosure() { return QuitWhenIdleClosure(); } // Deprecated: use RunLoop instead. // Construct a Closure that will call QuitWhenIdle(). Useful to schedule an // arbitrary MessageLoop to QuitWhenIdle. - static base::Closure QuitWhenIdleClosure(); + static Closure QuitWhenIdleClosure(); // Returns true if this loop is |type|. This allows subclasses (especially // those in tests) to specialize how they are identified. @@ -286,7 +282,7 @@ class BASE_EXPORT MessageLoop : public base::MessagePump::Delegate { const std::string& thread_name() const { return thread_name_; } // Gets the message loop proxy associated with this message loop. - scoped_refptr<base::MessageLoopProxy> message_loop_proxy() { + scoped_refptr<MessageLoopProxy> message_loop_proxy() { return message_loop_proxy_.get(); } @@ -349,10 +345,10 @@ class BASE_EXPORT MessageLoop : public base::MessagePump::Delegate { TaskObserver(); // This method is called before processing a task. - virtual void WillProcessTask(const base::PendingTask& pending_task) = 0; + virtual void WillProcessTask(const PendingTask& pending_task) = 0; // This method is called after processing a task. - virtual void DidProcessTask(const base::PendingTask& pending_task) = 0; + virtual void DidProcessTask(const PendingTask& pending_task) = 0; protected: virtual ~TaskObserver(); @@ -397,20 +393,20 @@ class BASE_EXPORT MessageLoop : public base::MessagePump::Delegate { protected: #if defined(OS_WIN) - base::MessagePumpWin* pump_win() { - return static_cast<base::MessagePumpWin*>(pump_.get()); + MessagePumpWin* pump_win() { + return static_cast<MessagePumpWin*>(pump_.get()); } #elif defined(OS_POSIX) && !defined(OS_IOS) - base::MessagePumpLibevent* pump_libevent() { - return static_cast<base::MessagePumpLibevent*>(pump_.get()); + MessagePumpLibevent* pump_libevent() { + return static_cast<MessagePumpLibevent*>(pump_.get()); } #endif - scoped_refptr<base::MessagePump> pump_; + scoped_refptr<MessagePump> pump_; private: - friend class base::RunLoop; - friend class base::MessageLoopLockTest; + friend class RunLoop; + friend class MessageLoopLockTest; // 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 @@ -431,14 +427,14 @@ class BASE_EXPORT MessageLoop : public base::MessagePump::Delegate { bool ProcessNextDelayedNonNestableTask(); // Runs the specified PendingTask. - void RunTask(const base::PendingTask& pending_task); + void RunTask(const PendingTask& pending_task); // Calls RunTask or queues the pending_task on the deferred task list if it // cannot be run right now. Returns true if the task was run. - bool DeferOrRunPendingTask(const base::PendingTask& pending_task); + bool DeferOrRunPendingTask(const PendingTask& pending_task); // Adds the pending task to delayed_work_queue_. - void AddToDelayedWorkQueue(const base::PendingTask& pending_task); + void AddToDelayedWorkQueue(const PendingTask& pending_task); // This function attempts to add pending task to our incoming_queue_. // The append can only possibly fail when |use_try_lock| is true. @@ -454,7 +450,7 @@ class BASE_EXPORT MessageLoop : public base::MessagePump::Delegate { // function will reset the value of pending_task->task. This is needed to // ensure that the posting call stack does not retain pending_task->task // beyond this function call. - bool AddToIncomingQueue(base::PendingTask* pending_task, bool use_try_lock); + bool AddToIncomingQueue(PendingTask* pending_task, bool use_try_lock); // 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 @@ -467,7 +463,7 @@ class BASE_EXPORT MessageLoop : public base::MessagePump::Delegate { bool DeletePendingTasks(); // Calculates the time at which a PendingTask should run. - base::TimeTicks CalculateDelayedRuntime(base::TimeDelta delay); + TimeTicks CalculateDelayedRuntime(TimeDelta delay); // Start recording histogram info about events and action IF it was enabled // and IF the statistics recorder can accept a registration of our histogram. @@ -478,27 +474,27 @@ class BASE_EXPORT MessageLoop : public base::MessagePump::Delegate { // If message_histogram_ is NULL, this is a no-op. void HistogramEvent(int event); - // base::MessagePump::Delegate methods: + // MessagePump::Delegate methods: virtual bool DoWork() OVERRIDE; - virtual bool DoDelayedWork(base::TimeTicks* next_delayed_work_time) OVERRIDE; + virtual bool DoDelayedWork(TimeTicks* next_delayed_work_time) OVERRIDE; virtual bool DoIdleWork() OVERRIDE; Type type_; // A list of tasks that need to be processed by this instance. Note that // this queue is only accessed (push/pop) by our current thread. - base::TaskQueue work_queue_; + TaskQueue work_queue_; // Contains delayed tasks, sorted by their 'delayed_run_time' property. - base::DelayedTaskQueue delayed_work_queue_; + DelayedTaskQueue delayed_work_queue_; // A recent snapshot of Time::Now(), used to check delayed_work_queue_. - base::TimeTicks recent_time_; + TimeTicks recent_time_; // A queue of non-nestable tasks that we had to defer because when it came // time to execute them we were in a nested message loop. They will execute // once we're out of nested message loops. - base::TaskQueue deferred_non_nestable_work_queue_; + TaskQueue deferred_non_nestable_work_queue_; ObserverList<DestructionObserver> destruction_observers_; @@ -510,19 +506,19 @@ class BASE_EXPORT MessageLoop : public base::MessagePump::Delegate { std::string thread_name_; // A profiling histogram showing the counts of various messages and events. - base::HistogramBase* message_histogram_; + HistogramBase* message_histogram_; // An incoming queue of tasks that are acquired under a mutex for processing // on this instance's thread. These tasks have not yet been sorted out into // items for our work_queue_ vs delayed_work_queue_. - base::TaskQueue incoming_queue_; + TaskQueue incoming_queue_; // Protect access to incoming_queue_. - mutable base::Lock incoming_queue_lock_; + mutable Lock incoming_queue_lock_; - base::RunLoop* run_loop_; + RunLoop* run_loop_; #if defined(OS_WIN) - base::TimeTicks high_resolution_timer_expiration_; + TimeTicks high_resolution_timer_expiration_; // Should be set to true before calling Windows APIs like TrackPopupMenu, etc // which enter a modal message loop. bool os_modal_loop_; @@ -535,8 +531,8 @@ class BASE_EXPORT MessageLoop : public base::MessagePump::Delegate { ObserverList<TaskObserver> task_observers_; // The message loop proxy associated with this message loop, if one exists. - scoped_refptr<base::MessageLoopProxy> message_loop_proxy_; - scoped_ptr<base::ThreadTaskRunnerHandle> thread_task_runner_handle_; + scoped_refptr<MessageLoopProxy> message_loop_proxy_; + scoped_ptr<ThreadTaskRunnerHandle> thread_task_runner_handle_; template <class T, class R> friend class base::subtle::DeleteHelperInternal; template <class T, class R> friend class base::subtle::ReleaseHelperInternal; @@ -561,7 +557,7 @@ class BASE_EXPORT MessageLoop : public base::MessagePump::Delegate { class BASE_EXPORT MessageLoopForUI : public MessageLoop { public: #if defined(OS_WIN) - typedef base::MessagePumpForUI::MessageFilter MessageFilter; + typedef MessagePumpForUI::MessageFilter MessageFilter; #endif MessageLoopForUI() : MessageLoop(TYPE_UI) { @@ -607,15 +603,15 @@ class BASE_EXPORT MessageLoopForUI : public MessageLoop { protected: #if defined(USE_AURA) && defined(USE_X11) && !defined(OS_NACL) - friend class base::MessagePumpAuraX11; + friend class MessagePumpAuraX11; #endif #if defined(USE_OZONE) && !defined(OS_NACL) - friend class base::MessagePumpOzone; + friend class MessagePumpOzone; #endif // TODO(rvargas): Make this platform independent. - base::MessagePumpForUI* pump_ui() { - return static_cast<base::MessagePumpForUI*>(pump_.get()); + MessagePumpForUI* pump_ui() { + return static_cast<MessagePumpForUI*>(pump_.get()); } #endif // !defined(OS_MACOSX) }; @@ -636,30 +632,30 @@ COMPILE_ASSERT(sizeof(MessageLoop) == sizeof(MessageLoopForUI), class BASE_EXPORT MessageLoopForIO : public MessageLoop { public: #if defined(OS_WIN) - typedef base::MessagePumpForIO::IOHandler IOHandler; - typedef base::MessagePumpForIO::IOContext IOContext; - typedef base::MessagePumpForIO::IOObserver IOObserver; + typedef MessagePumpForIO::IOHandler IOHandler; + typedef MessagePumpForIO::IOContext IOContext; + typedef MessagePumpForIO::IOObserver IOObserver; #elif defined(OS_IOS) - typedef base::MessagePumpIOSForIO::Watcher Watcher; - typedef base::MessagePumpIOSForIO::FileDescriptorWatcher + typedef MessagePumpIOSForIO::Watcher Watcher; + typedef MessagePumpIOSForIO::FileDescriptorWatcher FileDescriptorWatcher; - typedef base::MessagePumpIOSForIO::IOObserver IOObserver; + typedef MessagePumpIOSForIO::IOObserver IOObserver; enum Mode { - WATCH_READ = base::MessagePumpIOSForIO::WATCH_READ, - WATCH_WRITE = base::MessagePumpIOSForIO::WATCH_WRITE, - WATCH_READ_WRITE = base::MessagePumpIOSForIO::WATCH_READ_WRITE + WATCH_READ = MessagePumpIOSForIO::WATCH_READ, + WATCH_WRITE = MessagePumpIOSForIO::WATCH_WRITE, + WATCH_READ_WRITE = MessagePumpIOSForIO::WATCH_READ_WRITE }; #elif defined(OS_POSIX) - typedef base::MessagePumpLibevent::Watcher Watcher; - typedef base::MessagePumpLibevent::FileDescriptorWatcher + typedef MessagePumpLibevent::Watcher Watcher; + typedef MessagePumpLibevent::FileDescriptorWatcher FileDescriptorWatcher; - typedef base::MessagePumpLibevent::IOObserver IOObserver; + typedef MessagePumpLibevent::IOObserver IOObserver; enum Mode { - WATCH_READ = base::MessagePumpLibevent::WATCH_READ, - WATCH_WRITE = base::MessagePumpLibevent::WATCH_WRITE, - WATCH_READ_WRITE = base::MessagePumpLibevent::WATCH_READ_WRITE + WATCH_READ = MessagePumpLibevent::WATCH_READ, + WATCH_WRITE = MessagePumpLibevent::WATCH_WRITE, + WATCH_READ_WRITE = MessagePumpLibevent::WATCH_READ_WRITE }; #endif @@ -690,8 +686,8 @@ class BASE_EXPORT MessageLoopForIO : public MessageLoop { protected: // TODO(rvargas): Make this platform independent. - base::MessagePumpForIO* pump_io() { - return static_cast<base::MessagePumpForIO*>(pump_.get()); + MessagePumpForIO* pump_io() { + return static_cast<MessagePumpForIO*>(pump_.get()); } #elif defined(OS_IOS) @@ -703,8 +699,8 @@ class BASE_EXPORT MessageLoopForIO : public MessageLoop { Watcher *delegate); private: - base::MessagePumpIOSForIO* pump_io() { - return static_cast<base::MessagePumpIOSForIO*>(pump_.get()); + MessagePumpIOSForIO* pump_io() { + return static_cast<MessagePumpIOSForIO*>(pump_.get()); } #elif defined(OS_POSIX) @@ -716,8 +712,8 @@ class BASE_EXPORT MessageLoopForIO : public MessageLoop { Watcher* delegate); private: - base::MessagePumpLibevent* pump_io() { - return static_cast<base::MessagePumpLibevent*>(pump_.get()); + MessagePumpLibevent* pump_io() { + return static_cast<MessagePumpLibevent*>(pump_.get()); } #endif // defined(OS_POSIX) }; diff --git a/base/message_loop/message_loop_unittest.cc b/base/message_loop/message_loop_unittest.cc index 0f85b14..504c8e3 100644 --- a/base/message_loop/message_loop_unittest.cc +++ b/base/message_loop/message_loop_unittest.cc @@ -9,7 +9,7 @@ #include "base/compiler_specific.h" #include "base/logging.h" #include "base/memory/ref_counted.h" -#include "base/message_loop.h" +#include "base/message_loop/message_loop.h" #include "base/pending_task.h" #include "base/posix/eintr_wrapper.h" #include "base/run_loop.h" @@ -20,7 +20,7 @@ #include "testing/gtest/include/gtest/gtest.h" #if defined(OS_WIN) -#include "base/message_pump_win.h" +#include "base/message_loop/message_pump_win.h" #include "base/win/scoped_handle.h" #endif @@ -29,8 +29,8 @@ namespace base { class MessageLoopLockTest { public: static void LockWaitUnLock(MessageLoop* loop, - base::WaitableEvent* caller_wait, - base::WaitableEvent* caller_signal) { + WaitableEvent* caller_wait, + WaitableEvent* caller_signal) { loop->incoming_queue_lock_.Acquire(); caller_wait->Signal(); @@ -121,7 +121,7 @@ void RunTest_PostTask(MessageLoop::Type message_loop_type) { thread.Start(); thread.message_loop()->PostTask( FROM_HERE, - base::Bind(&MessageLoopLockTest::LockWaitUnLock, + Bind(&MessageLoopLockTest::LockWaitUnLock, MessageLoop::current(), &wait, &signal)); @@ -1419,7 +1419,7 @@ void RunTest_RecursivePosts(MessageLoop::Type message_loop_type, #if defined(OS_WIN) -class DispatcherImpl : public base::MessageLoopForUI::Dispatcher { +class DispatcherImpl : public MessageLoopForUI::Dispatcher { public: DispatcherImpl() : dispatch_count_(0) {} diff --git a/base/message_loop/message_pump.cc b/base/message_loop/message_pump.cc new file mode 100644 index 0000000..7ffc2b1 --- /dev/null +++ b/base/message_loop/message_pump.cc @@ -0,0 +1,15 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/message_loop/message_pump.h" + +namespace base { + +MessagePump::MessagePump() { +} + +MessagePump::~MessagePump() { +} + +} // namespace base diff --git a/base/message_loop/message_pump.h b/base/message_loop/message_pump.h new file mode 100644 index 0000000..5b72232 --- /dev/null +++ b/base/message_loop/message_pump.h @@ -0,0 +1,129 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MESSAGE_LOOP_MESSAGE_PUMP_H_ +#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_H_ + +#include "base/base_export.h" +#include "base/memory/ref_counted.h" + +namespace base { + +class TimeTicks; + +class BASE_EXPORT MessagePump : public RefCountedThreadSafe<MessagePump> { + public: + // Please see the comments above the Run method for an illustration of how + // these delegate methods are used. + class BASE_EXPORT Delegate { + public: + virtual ~Delegate() {} + + // 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 still be called if DoWork returns + // true, but DoIdleWork will not. + 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. Upon return |next_delayed_work_time| + // indicates the time when DoDelayedWork should be called again. If + // |next_delayed_work_time| is null (per Time::is_null), then the queue of + // future delayed work (timer events) is currently empty, and no additional + // calls to this function need to be scheduled. + virtual bool DoDelayedWork(TimeTicks* next_delayed_work_time) = 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; + }; + + 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; + // + // TimeTicks next_time; + // did_work |= delegate_->DoDelayedWork(&next_time); + // 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 cycles between calling DoInternalWork, DoWork, + // and DoDelayedWork methods. This helps ensure that none of these work + // queues starve the others. This is 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 at the specified time, + // cancelling any pending DoDelayedWork callback. This method may only be + // used on the thread that called Run. + virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time) = 0; + + protected: + virtual ~MessagePump(); + friend class RefCountedThreadSafe<MessagePump>; +}; + +} // namespace base + +#endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_H_ diff --git a/base/message_loop/message_pump_android.cc b/base/message_loop/message_pump_android.cc new file mode 100644 index 0000000..d7bf9a9 --- /dev/null +++ b/base/message_loop/message_pump_android.cc @@ -0,0 +1,139 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/message_loop/message_pump_android.h" + +#include <jni.h> + +#include "base/android/jni_android.h" +#include "base/android/scoped_java_ref.h" +#include "base/lazy_instance.h" +#include "base/logging.h" +#include "base/run_loop.h" +#include "base/time.h" +#include "jni/SystemMessageHandler_jni.h" + +using base::android::ScopedJavaLocalRef; + +namespace { + +base::LazyInstance<base::android::ScopedJavaGlobalRef<jobject> > + g_system_message_handler_obj = LAZY_INSTANCE_INITIALIZER; + +} // namespace + +// ---------------------------------------------------------------------------- +// Native JNI methods called by Java. +// ---------------------------------------------------------------------------- +// This method can not move to anonymous namespace as it has been declared as +// 'static' in system_message_handler_jni.h. +static jboolean DoRunLoopOnce(JNIEnv* env, jobject obj, jint native_delegate) { + base::MessagePump::Delegate* delegate = + reinterpret_cast<base::MessagePump::Delegate*>(native_delegate); + DCHECK(delegate); + // This is based on MessagePumpForUI::DoRunLoop() from desktop. + // Note however that our system queue is handled in the java side. + // In desktop we inspect and process a single system message and then + // we call DoWork() / DoDelayedWork(). + // On Android, the java message queue may contain messages for other handlers + // that will be processed before calling here again. + bool more_work_is_plausible = delegate->DoWork(); + + // This is the time when we need to do delayed work. + base::TimeTicks delayed_work_time; + more_work_is_plausible |= delegate->DoDelayedWork(&delayed_work_time); + + // This is a major difference between android and other platforms: since we + // can't inspect it and process just one single message, instead we'll yeld + // the callstack, and post a message to call us back soon. + if (more_work_is_plausible) + return true; + + more_work_is_plausible = delegate->DoIdleWork(); + if (!more_work_is_plausible && !delayed_work_time.is_null()) { + // We only set the timer here as returning true would post a message. + jlong millis = + (delayed_work_time - base::TimeTicks::Now()).InMillisecondsRoundedUp(); + Java_SystemMessageHandler_setDelayedTimer(env, obj, millis); + } + return more_work_is_plausible; +} + +namespace base { + +MessagePumpForUI::MessagePumpForUI() + : run_loop_(NULL) { +} + +MessagePumpForUI::~MessagePumpForUI() { +} + +void MessagePumpForUI::Run(Delegate* delegate) { + NOTREACHED() << "UnitTests should rely on MessagePumpForUIStub in" + " test_stub_android.h"; +} + +void MessagePumpForUI::Start(Delegate* delegate) { + run_loop_ = new RunLoop(); + // Since the RunLoop was just created above, BeforeRun should be guaranteed to + // return true (it only returns false if the RunLoop has been Quit already). + if (!run_loop_->BeforeRun()) + NOTREACHED(); + + DCHECK(g_system_message_handler_obj.Get().is_null()); + + JNIEnv* env = base::android::AttachCurrentThread(); + DCHECK(env); + + g_system_message_handler_obj.Get().Reset( + Java_SystemMessageHandler_create(env, reinterpret_cast<jint>(delegate))); +} + +void MessagePumpForUI::Quit() { + if (!g_system_message_handler_obj.Get().is_null()) { + JNIEnv* env = base::android::AttachCurrentThread(); + DCHECK(env); + + Java_SystemMessageHandler_removeTimer(env, + g_system_message_handler_obj.Get().obj()); + g_system_message_handler_obj.Get().Reset(); + } + + if (run_loop_) { + run_loop_->AfterRun(); + delete run_loop_; + run_loop_ = NULL; + } +} + +void MessagePumpForUI::ScheduleWork() { + DCHECK(!g_system_message_handler_obj.Get().is_null()); + + JNIEnv* env = base::android::AttachCurrentThread(); + DCHECK(env); + + Java_SystemMessageHandler_setTimer(env, + g_system_message_handler_obj.Get().obj()); +} + +void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { + DCHECK(!g_system_message_handler_obj.Get().is_null()); + + JNIEnv* env = base::android::AttachCurrentThread(); + DCHECK(env); + + jlong millis = + (delayed_work_time - TimeTicks::Now()).InMillisecondsRoundedUp(); + // Note that we're truncating to milliseconds as required by the java side, + // even though delayed_work_time is microseconds resolution. + Java_SystemMessageHandler_setDelayedTimer(env, + g_system_message_handler_obj.Get().obj(), millis); +} + +// static +bool MessagePumpForUI::RegisterBindings(JNIEnv* env) { + return RegisterNativesImpl(env); +} + +} // namespace base diff --git a/base/message_loop/message_pump_android.h b/base/message_loop/message_pump_android.h new file mode 100644 index 0000000..fd934a7 --- /dev/null +++ b/base/message_loop/message_pump_android.h @@ -0,0 +1,45 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MESSAGE_LOOP_MESSAGE_PUMP_ANDROID_H_ +#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_ANDROID_H_ + +#include <jni.h> + +#include "base/base_export.h" +#include "base/compiler_specific.h" +#include "base/message_loop/message_pump.h" + +namespace base { + +class RunLoop; +class TimeTicks; + +// This class implements a MessagePump needed for TYPE_UI MessageLoops on +// OS_ANDROID platform. +class BASE_EXPORT MessagePumpForUI : public MessagePump { + public: + MessagePumpForUI(); + + virtual void Run(Delegate* delegate) OVERRIDE; + virtual void Quit() OVERRIDE; + virtual void ScheduleWork() OVERRIDE; + virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time) OVERRIDE; + + virtual void Start(Delegate* delegate); + + static bool RegisterBindings(JNIEnv* env); + + protected: + virtual ~MessagePumpForUI(); + + private: + RunLoop* run_loop_; + + DISALLOW_COPY_AND_ASSIGN(MessagePumpForUI); +}; + +} // namespace base + +#endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_ANDROID_H_ diff --git a/base/message_loop/message_pump_aurax11.cc b/base/message_loop/message_pump_aurax11.cc new file mode 100644 index 0000000..70cae64 --- /dev/null +++ b/base/message_loop/message_pump_aurax11.cc @@ -0,0 +1,307 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/message_loop/message_pump_aurax11.h" + +#include <glib.h> +#include <X11/X.h> +#include <X11/extensions/XInput2.h> +#include <X11/XKBlib.h> + +#include "base/basictypes.h" +#include "base/message_loop.h" + +namespace base { + +namespace { + +gboolean XSourcePrepare(GSource* source, gint* timeout_ms) { + if (XPending(MessagePumpAuraX11::GetDefaultXDisplay())) + *timeout_ms = 0; + else + *timeout_ms = -1; + return FALSE; +} + +gboolean XSourceCheck(GSource* source) { + return XPending(MessagePumpAuraX11::GetDefaultXDisplay()); +} + +gboolean XSourceDispatch(GSource* source, + GSourceFunc unused_func, + gpointer data) { + MessagePumpAuraX11* pump = static_cast<MessagePumpAuraX11*>(data); + return pump->DispatchXEvents(); +} + +GSourceFuncs XSourceFuncs = { + XSourcePrepare, + XSourceCheck, + XSourceDispatch, + NULL +}; + +// The connection is essentially a global that's accessed through a static +// method and destroyed whenever ~MessagePumpAuraX11() is called. We do this +// for historical reasons so user code can call +// MessagePumpForUI::GetDefaultXDisplay() where MessagePumpForUI is a typedef +// to whatever type in the current build. +// +// TODO(erg): This can be changed to something more sane like +// MessagePumpAuraX11::Current()->display() once MessagePumpGtk goes away. +Display* g_xdisplay = NULL; +int g_xinput_opcode = -1; + +bool InitializeXInput2Internal() { + Display* display = MessagePumpAuraX11::GetDefaultXDisplay(); + if (!display) + return false; + + int event, err; + + int xiopcode; + if (!XQueryExtension(display, "XInputExtension", &xiopcode, &event, &err)) { + DVLOG(1) << "X Input extension not available."; + return false; + } + g_xinput_opcode = xiopcode; + +#if defined(USE_XI2_MT) + // USE_XI2_MT also defines the required XI2 minor minimum version. + int major = 2, minor = USE_XI2_MT; +#else + int major = 2, minor = 0; +#endif + if (XIQueryVersion(display, &major, &minor) == BadRequest) { + DVLOG(1) << "XInput2 not supported in the server."; + return false; + } +#if defined(USE_XI2_MT) + if (major < 2 || (major == 2 && minor < USE_XI2_MT)) { + DVLOG(1) << "XI version on server is " << major << "." << minor << ". " + << "But 2." << USE_XI2_MT << " is required."; + return false; + } +#endif + + return true; +} + +Window FindEventTarget(const NativeEvent& xev) { + Window target = xev->xany.window; + if (xev->type == GenericEvent && + static_cast<XIEvent*>(xev->xcookie.data)->extension == g_xinput_opcode) { + target = static_cast<XIDeviceEvent*>(xev->xcookie.data)->event; + } + return target; +} + +bool InitializeXInput2() { + static bool xinput2_supported = InitializeXInput2Internal(); + return xinput2_supported; +} + +bool InitializeXkb() { + Display* display = MessagePumpAuraX11::GetDefaultXDisplay(); + if (!display) + return false; + + int opcode, event, error; + int major = XkbMajorVersion; + int minor = XkbMinorVersion; + if (!XkbQueryExtension(display, &opcode, &event, &error, &major, &minor)) { + DVLOG(1) << "Xkb extension not available."; + return false; + } + + // Ask the server not to send KeyRelease event when the user holds down a key. + // crbug.com/138092 + Bool supported_return; + if (!XkbSetDetectableAutoRepeat(display, True, &supported_return)) { + DVLOG(1) << "XKB not supported in the server."; + return false; + } + + return true; +} + +} // namespace + +MessagePumpAuraX11::MessagePumpAuraX11() : MessagePumpGlib(), + x_source_(NULL) { + InitializeXInput2(); + InitializeXkb(); + InitXSource(); + + // Can't put this in the initializer list because g_xdisplay may not exist + // until after InitXSource(). + x_root_window_ = DefaultRootWindow(g_xdisplay); +} + +// static +Display* MessagePumpAuraX11::GetDefaultXDisplay() { + if (!g_xdisplay) + g_xdisplay = XOpenDisplay(NULL); + return g_xdisplay; +} + +// static +bool MessagePumpAuraX11::HasXInput2() { + return InitializeXInput2(); +} + +// static +MessagePumpAuraX11* MessagePumpAuraX11::Current() { + MessageLoopForUI* loop = MessageLoopForUI::current(); + return static_cast<MessagePumpAuraX11*>(loop->pump_ui()); +} + +void MessagePumpAuraX11::AddDispatcherForWindow( + MessagePumpDispatcher* dispatcher, + unsigned long xid) { + dispatchers_.insert(std::make_pair(xid, dispatcher)); +} + +void MessagePumpAuraX11::RemoveDispatcherForWindow(unsigned long xid) { + dispatchers_.erase(xid); +} + +void MessagePumpAuraX11::AddDispatcherForRootWindow( + MessagePumpDispatcher* dispatcher) { + root_window_dispatchers_.AddObserver(dispatcher); +} + +void MessagePumpAuraX11::RemoveDispatcherForRootWindow( + MessagePumpDispatcher* dispatcher) { + root_window_dispatchers_.RemoveObserver(dispatcher); +} + +bool MessagePumpAuraX11::DispatchXEvents() { + Display* display = GetDefaultXDisplay(); + DCHECK(display); + MessagePumpDispatcher* dispatcher = + GetDispatcher() ? GetDispatcher() : this; + + // In the general case, we want to handle all pending events before running + // the tasks. This is what happens in the message_pump_glib case. + while (XPending(display)) { + XEvent xev; + XNextEvent(display, &xev); + if (dispatcher && ProcessXEvent(dispatcher, &xev)) + return TRUE; + } + return TRUE; +} + +void MessagePumpAuraX11::BlockUntilWindowMapped(unsigned long xid) { + XEvent event; + + Display* display = GetDefaultXDisplay(); + DCHECK(display); + + MessagePumpDispatcher* dispatcher = + GetDispatcher() ? GetDispatcher() : this; + + do { + // Block until there's a message of |event_mask| type on |w|. Then remove + // it from the queue and stuff it in |event|. + XWindowEvent(display, xid, StructureNotifyMask, &event); + ProcessXEvent(dispatcher, &event); + } while (event.type != MapNotify); +} + +MessagePumpAuraX11::~MessagePumpAuraX11() { + g_source_destroy(x_source_); + g_source_unref(x_source_); + XCloseDisplay(g_xdisplay); + g_xdisplay = NULL; +} + +void MessagePumpAuraX11::InitXSource() { + // CHECKs are to help track down crbug.com/113106. + CHECK(!x_source_); + Display* display = GetDefaultXDisplay(); + CHECK(display) << "Unable to get connection to X server"; + x_poll_.reset(new GPollFD()); + CHECK(x_poll_.get()); + x_poll_->fd = ConnectionNumber(display); + x_poll_->events = G_IO_IN; + + x_source_ = g_source_new(&XSourceFuncs, sizeof(GSource)); + g_source_add_poll(x_source_, x_poll_.get()); + g_source_set_can_recurse(x_source_, TRUE); + g_source_set_callback(x_source_, NULL, this, NULL); + g_source_attach(x_source_, g_main_context_default()); +} + +bool MessagePumpAuraX11::ProcessXEvent(MessagePumpDispatcher* dispatcher, + XEvent* xev) { + bool should_quit = false; + + bool have_cookie = false; + if (xev->type == GenericEvent && + XGetEventData(xev->xgeneric.display, &xev->xcookie)) { + have_cookie = true; + } + + if (!WillProcessXEvent(xev)) { + if (!dispatcher->Dispatch(xev)) { + should_quit = true; + Quit(); + } + DidProcessXEvent(xev); + } + + if (have_cookie) { + XFreeEventData(xev->xgeneric.display, &xev->xcookie); + } + + return should_quit; +} + +bool MessagePumpAuraX11::WillProcessXEvent(XEvent* xevent) { + if (!observers().might_have_observers()) + return false; + ObserverListBase<MessagePumpObserver>::Iterator it(observers()); + MessagePumpObserver* obs; + while ((obs = it.GetNext()) != NULL) { + if (obs->WillProcessEvent(xevent)) + return true; + } + return false; +} + +void MessagePumpAuraX11::DidProcessXEvent(XEvent* xevent) { + FOR_EACH_OBSERVER(MessagePumpObserver, observers(), DidProcessEvent(xevent)); +} + +MessagePumpDispatcher* MessagePumpAuraX11::GetDispatcherForXEvent( + const NativeEvent& xev) const { + ::Window x_window = FindEventTarget(xev); + DispatchersMap::const_iterator it = dispatchers_.find(x_window); + return it != dispatchers_.end() ? it->second : NULL; +} + +bool MessagePumpAuraX11::Dispatch(const NativeEvent& xev) { + // MappingNotify events (meaning that the keyboard or pointer buttons have + // been remapped) aren't associated with a window; send them to all + // dispatchers. + if (xev->type == MappingNotify) { + for (DispatchersMap::const_iterator it = dispatchers_.begin(); + it != dispatchers_.end(); ++it) { + it->second->Dispatch(xev); + } + return true; + } + + if (FindEventTarget(xev) == x_root_window_) { + FOR_EACH_OBSERVER(MessagePumpDispatcher, root_window_dispatchers_, + Dispatch(xev)); + return true; + } + MessagePumpDispatcher* dispatcher = GetDispatcherForXEvent(xev); + return dispatcher ? dispatcher->Dispatch(xev) : true; +} + +} // namespace base diff --git a/base/message_loop/message_pump_aurax11.h b/base/message_loop/message_pump_aurax11.h new file mode 100644 index 0000000..cec6934 --- /dev/null +++ b/base/message_loop/message_pump_aurax11.h @@ -0,0 +1,122 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MESSAGE_LOOP_MESSAGE_PUMP_AURAX11_H +#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_AURAX11_H + +#include <bitset> +#include <map> + +#include "base/memory/scoped_ptr.h" +#include "base/message_loop/message_pump.h" +#include "base/message_loop/message_pump_dispatcher.h" +#include "base/message_loop/message_pump_glib.h" +#include "base/message_loop/message_pump_observer.h" +#include "base/observer_list.h" + +// It would be nice to include the X11 headers here so that we use Window +// instead of its typedef of unsigned long, but we can't because everything in +// chrome includes us through base/message_loop.h, and X11's crappy #define +// heavy headers muck up half of chrome. + +typedef struct _GPollFD GPollFD; +typedef struct _GSource GSource; +typedef struct _XDisplay Display; + +namespace base { + +// This class implements a message-pump for dispatching X events. +// +// If there's a current dispatcher given through RunWithDispatcher(), that +// dispatcher receives events. Otherwise, we route to messages to dispatchers +// who have subscribed to messages from a specific X11 window. +class BASE_EXPORT MessagePumpAuraX11 : public MessagePumpGlib, + public MessagePumpDispatcher { + public: + MessagePumpAuraX11(); + + // Returns default X Display. + static Display* GetDefaultXDisplay(); + + // Returns true if the system supports XINPUT2. + static bool HasXInput2(); + + // Returns the UI message pump. + static MessagePumpAuraX11* Current(); + + // Adds/Removes |dispatcher| for the |xid|. This will route all messages from + // the window |xid| to |dispatcher. + void AddDispatcherForWindow(MessagePumpDispatcher* dispatcher, + unsigned long xid); + void RemoveDispatcherForWindow(unsigned long xid); + + // Adds/Removes |dispatcher| to receive all events sent to the X root + // window. A root window can have multiple dispatchers, and events on root + // windows will be dispatched to all. + void AddDispatcherForRootWindow(MessagePumpDispatcher* dispatcher); + void RemoveDispatcherForRootWindow(MessagePumpDispatcher* dispatcher); + + // Internal function. Called by the glib source dispatch function. Processes + // all available X events. + bool DispatchXEvents(); + + // Blocks on the X11 event queue until we receive notification from the + // xserver that |w| has been mapped; StructureNotifyMask events on |w| are + // pulled out from the queue and dispatched out of order. + // + // For those that know X11, this is really a wrapper around XWindowEvent + // which still makes sure the preempted event is dispatched instead of + // dropped on the floor. This method exists because mapping a window is + // asynchronous (and we receive an XEvent when mapped), while there are also + // functions which require a mapped window. + void BlockUntilWindowMapped(unsigned long xid); + + protected: + virtual ~MessagePumpAuraX11(); + + private: + typedef std::map<unsigned long, MessagePumpDispatcher*> DispatchersMap; + + // Initializes the glib event source for X. + void InitXSource(); + + // Dispatches the XEvent and returns true if we should exit the current loop + // of message processing. + bool ProcessXEvent(MessagePumpDispatcher* dispatcher, XEvent* event); + + // Sends the event to the observers. If an observer returns true, then it does + // not send the event to any other observers and returns true. Returns false + // if no observer returns true. + bool WillProcessXEvent(XEvent* xevent); + void DidProcessXEvent(XEvent* xevent); + + // Returns the Dispatcher based on the event's target window. + MessagePumpDispatcher* GetDispatcherForXEvent(const NativeEvent& xev) const; + + // Overridden from MessagePumpDispatcher: + virtual bool Dispatch(const NativeEvent& event) OVERRIDE; + + // The event source for X events. + GSource* x_source_; + + // The poll attached to |x_source_|. + scoped_ptr<GPollFD> x_poll_; + + DispatchersMap dispatchers_; + + // Dispatch calls can cause addition of new dispatchers as we iterate + // through them. Use ObserverList to ensure the iterator remains valid across + // additions. + ObserverList<MessagePumpDispatcher> root_window_dispatchers_; + + unsigned long x_root_window_; + + DISALLOW_COPY_AND_ASSIGN(MessagePumpAuraX11); +}; + +typedef MessagePumpAuraX11 MessagePumpForUI; + +} // namespace base + +#endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_AURAX11_H diff --git a/base/message_loop/message_pump_default.cc b/base/message_loop/message_pump_default.cc new file mode 100644 index 0000000..b36ff21 --- /dev/null +++ b/base/message_loop/message_pump_default.cc @@ -0,0 +1,85 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/message_loop/message_pump_default.h" + +#include "base/logging.h" +#include "base/threading/thread_restrictions.h" + +#if defined(OS_MACOSX) +#include "base/mac/scoped_nsautorelease_pool.h" +#endif + +namespace base { + +MessagePumpDefault::MessagePumpDefault() + : keep_running_(true), + event_(false, false) { +} + +void MessagePumpDefault::Run(Delegate* delegate) { + DCHECK(keep_running_) << "Quit must have been called outside of Run!"; + + for (;;) { +#if defined(OS_MACOSX) + mac::ScopedNSAutoreleasePool autorelease_pool; +#endif + + bool did_work = delegate->DoWork(); + if (!keep_running_) + break; + + did_work |= delegate->DoDelayedWork(&delayed_work_time_); + if (!keep_running_) + break; + + if (did_work) + continue; + + did_work = delegate->DoIdleWork(); + if (!keep_running_) + break; + + if (did_work) + continue; + + ThreadRestrictions::ScopedAllowWait allow_wait; + if (delayed_work_time_.is_null()) { + event_.Wait(); + } else { + TimeDelta delay = delayed_work_time_ - TimeTicks::Now(); + if (delay > TimeDelta()) { + event_.TimedWait(delay); + } else { + // It looks like delayed_work_time_ indicates a time in the past, so we + // need to call DoDelayedWork now. + delayed_work_time_ = TimeTicks(); + } + } + // Since event_ is auto-reset, we don't need to do anything special here + // other than service each delegate method. + } + + keep_running_ = true; +} + +void MessagePumpDefault::Quit() { + keep_running_ = false; +} + +void MessagePumpDefault::ScheduleWork() { + // Since this can be called on any thread, we need to ensure that our Run + // loop wakes up. + event_.Signal(); +} + +void MessagePumpDefault::ScheduleDelayedWork( + const TimeTicks& delayed_work_time) { + // We know that we can't be blocked on Wait right now since this method can + // only be called on the same thread as Run, so we only need to update our + // record of how long to sleep when we do sleep. + delayed_work_time_ = delayed_work_time; +} + +} // namespace base diff --git a/base/message_loop/message_pump_default.h b/base/message_loop/message_pump_default.h new file mode 100644 index 0000000..dd65973 --- /dev/null +++ b/base/message_loop/message_pump_default.h @@ -0,0 +1,42 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MESSAGE_LOOP_MESSAGE_PUMP_DEFAULT_H_ +#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_DEFAULT_H_ + +#include "base/message_loop/message_pump.h" +#include "base/synchronization/waitable_event.h" +#include "base/time.h" + +namespace base { + +class MessagePumpDefault : public MessagePump { + public: + MessagePumpDefault(); + + // MessagePump methods: + virtual void Run(Delegate* delegate) OVERRIDE; + virtual void Quit() OVERRIDE; + virtual void ScheduleWork() OVERRIDE; + virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time) OVERRIDE; + + protected: + virtual ~MessagePumpDefault() {} + + private: + // This flag is set to false when Run should return. + bool keep_running_; + + // Used to sleep until there is more work to do. + WaitableEvent event_; + + // The time at which we should call DoDelayedWork. + TimeTicks delayed_work_time_; + + DISALLOW_COPY_AND_ASSIGN(MessagePumpDefault); +}; + +} // namespace base + +#endif // BASE__MESSAGE_LOOPMESSAGE_PUMP_DEFAULT_H_ diff --git a/base/message_loop/message_pump_dispatcher.h b/base/message_loop/message_pump_dispatcher.h new file mode 100644 index 0000000..e49fa4f --- /dev/null +++ b/base/message_loop/message_pump_dispatcher.h @@ -0,0 +1,32 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MESSAGE_LOOP_MESSAGE_PUMP_DISPATCHER_H +#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_DISPATCHER_H + +#include "base/base_export.h" +#include "base/event_types.h" + +namespace base { + +// Dispatcher is used during a nested invocation of Run to dispatch events when +// |RunLoop(dispatcher).Run()| is used. If |RunLoop().Run()| is invoked, +// 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 whether or not to dispatch the event. +// +// The nested loop is exited by either posting a quit, or returning false +// from Dispatch. +class BASE_EXPORT MessagePumpDispatcher { + public: + virtual ~MessagePumpDispatcher() {} + + // Dispatches the event. If true is returned processing continues as + // normal. If false is returned, the nested loop exits immediately. + virtual bool Dispatch(const NativeEvent& event) = 0; +}; + +} // namespace base + +#endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_DISPATCHER_H diff --git a/base/message_loop/message_pump_glib.cc b/base/message_loop/message_pump_glib.cc new file mode 100644 index 0000000..de012fd --- /dev/null +++ b/base/message_loop/message_pump_glib.cc @@ -0,0 +1,334 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/message_loop/message_pump_glib.h" + +#include <fcntl.h> +#include <math.h> + +#include <glib.h> + +#include "base/logging.h" +#include "base/posix/eintr_wrapper.h" +#include "base/threading/platform_thread.h" + +namespace base { + +namespace { + +// Return a timeout suitable for the glib loop, -1 to block forever, +// 0 to return right away, or a timeout in milliseconds from now. +int GetTimeIntervalMilliseconds(const TimeTicks& from) { + if (from.is_null()) + return -1; + + // Be careful here. TimeDelta has a precision of microseconds, but we want a + // value in milliseconds. If there are 5.5ms left, should the delay be 5 or + // 6? It should be 6 to avoid executing delayed work too early. + int delay = static_cast<int>( + ceil((from - TimeTicks::Now()).InMillisecondsF())); + + // If this value is negative, then we need to run delayed work soon. + return delay < 0 ? 0 : delay; +} + +// A brief refresher on GLib: +// GLib sources have four callbacks: Prepare, Check, Dispatch and Finalize. +// On each iteration of the GLib pump, it calls each source's Prepare function. +// This function should return TRUE if it wants GLib to call its Dispatch, and +// FALSE otherwise. It can also set a timeout in this case for the next time +// Prepare should be called again (it may be called sooner). +// After the Prepare calls, GLib does a poll to check for events from the +// system. File descriptors can be attached to the sources. The poll may block +// if none of the Prepare calls returned TRUE. It will block indefinitely, or +// by the minimum time returned by a source in Prepare. +// After the poll, GLib calls Check for each source that returned FALSE +// from Prepare. The return value of Check has the same meaning as for Prepare, +// making Check a second chance to tell GLib we are ready for Dispatch. +// Finally, GLib calls Dispatch for each source that is ready. If Dispatch +// returns FALSE, GLib will destroy the source. Dispatch calls may be recursive +// (i.e., you can call Run from them), but Prepare and Check cannot. +// Finalize is called when the source is destroyed. +// NOTE: It is common for subsytems to want to process pending events while +// doing intensive work, for example the flash plugin. They usually use the +// following pattern (recommended by the GTK docs): +// while (gtk_events_pending()) { +// gtk_main_iteration(); +// } +// +// gtk_events_pending just calls g_main_context_pending, which does the +// following: +// - Call prepare on all the sources. +// - Do the poll with a timeout of 0 (not blocking). +// - Call check on all the sources. +// - *Does not* call dispatch on the sources. +// - Return true if any of prepare() or check() returned true. +// +// gtk_main_iteration just calls g_main_context_iteration, which does the whole +// thing, respecting the timeout for the poll (and block, although it is +// expected not to if gtk_events_pending returned true), and call dispatch. +// +// Thus it is important to only return true from prepare or check if we +// actually have events or work to do. We also need to make sure we keep +// internal state consistent so that if prepare/check return true when called +// from gtk_events_pending, they will still return true when called right +// after, from gtk_main_iteration. +// +// For the GLib pump we try to follow the Windows UI pump model: +// - Whenever we receive a wakeup event or the timer for delayed work expires, +// we run DoWork and/or DoDelayedWork. That part will also run in the other +// event pumps. +// - We also run DoWork, DoDelayedWork, and possibly DoIdleWork in the main +// loop, around event handling. + +struct WorkSource : public GSource { + MessagePumpGlib* pump; +}; + +gboolean WorkSourcePrepare(GSource* source, + gint* timeout_ms) { + *timeout_ms = static_cast<WorkSource*>(source)->pump->HandlePrepare(); + // We always return FALSE, so that our timeout is honored. If we were + // to return TRUE, the timeout would be considered to be 0 and the poll + // would never block. Once the poll is finished, Check will be called. + return FALSE; +} + +gboolean WorkSourceCheck(GSource* source) { + // Only return TRUE if Dispatch should be called. + return static_cast<WorkSource*>(source)->pump->HandleCheck(); +} + +gboolean WorkSourceDispatch(GSource* source, + GSourceFunc unused_func, + gpointer unused_data) { + + static_cast<WorkSource*>(source)->pump->HandleDispatch(); + // Always return TRUE so our source stays registered. + return TRUE; +} + +// I wish these could be const, but g_source_new wants non-const. +GSourceFuncs WorkSourceFuncs = { + WorkSourcePrepare, + WorkSourceCheck, + WorkSourceDispatch, + NULL +}; + +} // namespace + +struct MessagePumpGlib::RunState { + Delegate* delegate; + MessagePumpDispatcher* 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; + + // This keeps the state of whether the pump got signaled that there was new + // work to be done. Since we eat the message on the wake up pipe as soon as + // we get it, we keep that state here to stay consistent. + bool has_work; +}; + +MessagePumpGlib::MessagePumpGlib() + : state_(NULL), + context_(g_main_context_default()), + wakeup_gpollfd_(new GPollFD) { + // Create our wakeup pipe, which is used to flag when work was scheduled. + int fds[2]; + int ret = pipe(fds); + DCHECK_EQ(ret, 0); + (void)ret; // Prevent warning in release mode. + + wakeup_pipe_read_ = fds[0]; + wakeup_pipe_write_ = fds[1]; + wakeup_gpollfd_->fd = wakeup_pipe_read_; + wakeup_gpollfd_->events = G_IO_IN; + + work_source_ = g_source_new(&WorkSourceFuncs, sizeof(WorkSource)); + static_cast<WorkSource*>(work_source_)->pump = this; + g_source_add_poll(work_source_, wakeup_gpollfd_.get()); + // Use a low priority so that we let other events in the queue go first. + g_source_set_priority(work_source_, G_PRIORITY_DEFAULT_IDLE); + // This is needed to allow Run calls inside Dispatch. + g_source_set_can_recurse(work_source_, TRUE); + g_source_attach(work_source_, context_); +} + +void MessagePumpGlib::RunWithDispatcher(Delegate* delegate, + MessagePumpDispatcher* dispatcher) { +#ifndef NDEBUG + // Make sure we only run this on one thread. X/GTK only has one message pump + // so we can only have one UI loop per process. + static PlatformThreadId thread_id = PlatformThread::CurrentId(); + DCHECK(thread_id == PlatformThread::CurrentId()) << + "Running MessagePumpGlib on two different threads; " + "this is unsupported by GLib!"; +#endif + + RunState state; + state.delegate = delegate; + state.dispatcher = dispatcher; + state.should_quit = false; + state.run_depth = state_ ? state_->run_depth + 1 : 1; + state.has_work = false; + + RunState* previous_state = state_; + state_ = &state; + + // We really only do a single task for each iteration of the loop. If we + // have done something, assume there is likely something more to do. This + // will mean that we don't block on the message pump until there was nothing + // more to do. We also set this to true to make sure not to block on the + // first iteration of the loop, so RunUntilIdle() works correctly. + bool more_work_is_plausible = true; + + // We run our own loop instead of using g_main_loop_quit in one of the + // callbacks. This is so we only quit our own loops, and we don't quit + // nested loops run by others. TODO(deanm): Is this what we want? + for (;;) { + // Don't block if we think we have more work to do. + bool block = !more_work_is_plausible; + + more_work_is_plausible = g_main_context_iteration(context_, block); + if (state_->should_quit) + break; + + more_work_is_plausible |= state_->delegate->DoWork(); + if (state_->should_quit) + break; + + more_work_is_plausible |= + state_->delegate->DoDelayedWork(&delayed_work_time_); + if (state_->should_quit) + break; + + if (more_work_is_plausible) + continue; + + more_work_is_plausible = state_->delegate->DoIdleWork(); + if (state_->should_quit) + break; + } + + state_ = previous_state; +} + +// Return the timeout we want passed to poll. +int MessagePumpGlib::HandlePrepare() { + // We know we have work, but we haven't called HandleDispatch yet. Don't let + // the pump block so that we can do some processing. + if (state_ && // state_ may be null during tests. + state_->has_work) + return 0; + + // We don't think we have work to do, but make sure not to block + // longer than the next time we need to run delayed work. + return GetTimeIntervalMilliseconds(delayed_work_time_); +} + +bool MessagePumpGlib::HandleCheck() { + if (!state_) // state_ may be null during tests. + return false; + + // We usually have a single message on the wakeup pipe, since we are only + // signaled when the queue went from empty to non-empty, but there can be + // two messages if a task posted a task, hence we read at most two bytes. + // The glib poll will tell us whether there was data, so this read + // shouldn't block. + if (wakeup_gpollfd_->revents & G_IO_IN) { + char msg[2]; + const int num_bytes = HANDLE_EINTR(read(wakeup_pipe_read_, msg, 2)); + if (num_bytes < 1) { + NOTREACHED() << "Error reading from the wakeup pipe."; + } + DCHECK((num_bytes == 1 && msg[0] == '!') || + (num_bytes == 2 && msg[0] == '!' && msg[1] == '!')); + // Since we ate the message, we need to record that we have more work, + // because HandleCheck() may be called without HandleDispatch being called + // afterwards. + state_->has_work = true; + } + + if (state_->has_work) + return true; + + if (GetTimeIntervalMilliseconds(delayed_work_time_) == 0) { + // The timer has expired. That condition will stay true until we process + // that delayed work, so we don't need to record this differently. + return true; + } + + return false; +} + +void MessagePumpGlib::HandleDispatch() { + state_->has_work = false; + if (state_->delegate->DoWork()) { + // NOTE: on Windows at this point we would call ScheduleWork (see + // MessagePumpGlib::HandleWorkMessage in message_pump_win.cc). But here, + // instead of posting a message on the wakeup pipe, we can avoid the + // syscalls and just signal that we have more work. + state_->has_work = true; + } + + if (state_->should_quit) + return; + + state_->delegate->DoDelayedWork(&delayed_work_time_); +} + +void MessagePumpGlib::AddObserver(MessagePumpObserver* observer) { + observers_.AddObserver(observer); +} + +void MessagePumpGlib::RemoveObserver(MessagePumpObserver* observer) { + observers_.RemoveObserver(observer); +} + +void MessagePumpGlib::Run(Delegate* delegate) { + RunWithDispatcher(delegate, NULL); +} + +void MessagePumpGlib::Quit() { + if (state_) { + state_->should_quit = true; + } else { + NOTREACHED() << "Quit called outside Run!"; + } +} + +void MessagePumpGlib::ScheduleWork() { + // This can be called on any thread, so we don't want to touch any state + // variables as we would then need locks all over. This ensures that if + // we are sleeping in a poll that we will wake up. + char msg = '!'; + if (HANDLE_EINTR(write(wakeup_pipe_write_, &msg, 1)) != 1) { + NOTREACHED() << "Could not write to the UI message loop wakeup pipe!"; + } +} + +void MessagePumpGlib::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { + // We need to wake up the loop in case the poll timeout needs to be + // adjusted. This will cause us to try to do work, but that's ok. + delayed_work_time_ = delayed_work_time; + ScheduleWork(); +} + +MessagePumpGlib::~MessagePumpGlib() { + g_source_destroy(work_source_); + g_source_unref(work_source_); + close(wakeup_pipe_read_); + close(wakeup_pipe_write_); +} + +MessagePumpDispatcher* MessagePumpGlib::GetDispatcher() { + return state_ ? state_->dispatcher : NULL; +} + +} // namespace base diff --git a/base/message_loop/message_pump_glib.h b/base/message_loop/message_pump_glib.h new file mode 100644 index 0000000..e45591b --- /dev/null +++ b/base/message_loop/message_pump_glib.h @@ -0,0 +1,110 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MESSAGE_LOOP_MESSAGE_PUMP_GLIB_H_ +#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_GLIB_H_ + +#include "base/base_export.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop/message_pump.h" +#include "base/observer_list.h" +#include "base/time.h" + +typedef struct _GMainContext GMainContext; +typedef struct _GPollFD GPollFD; +typedef struct _GSource GSource; + +namespace base { + +// MessagePumpObserver is notified prior to an event being dispatched. As +// Observers are notified of every change, they have to be FAST! The platform +// specific implementation of the class is in message_pump_gtk/message_pump_x. +class MessagePumpObserver; + +// MessagePumpDispatcher is used during a nested invocation of Run to dispatch +// events. If Run is invoked with a non-NULL MessagePumpDispatcher, MessageLoop +// does not dispatch events (or invoke gtk_main_do_event), rather every event is +// passed to Dispatcher's Dispatch method for dispatch. It is up to the +// Dispatcher to dispatch, or not, the event. The platform specific +// implementation of the class is in message_pump_gtk/message_pump_x. +class MessagePumpDispatcher; + +// This class implements a base MessagePump needed for TYPE_UI MessageLoops on +// platforms using GLib. +class BASE_EXPORT MessagePumpGlib : public MessagePump { + public: + MessagePumpGlib(); + + // Like MessagePump::Run, but events are routed through dispatcher. + virtual void RunWithDispatcher(Delegate* delegate, + MessagePumpDispatcher* dispatcher); + + // Internal methods used for processing the pump callbacks. They are + // public for simplicity but should not be used directly. HandlePrepare + // is called during the prepare step of glib, and returns a timeout that + // will be passed to the poll. HandleCheck is called after the poll + // has completed, and returns whether or not HandleDispatch should be called. + // HandleDispatch is called if HandleCheck returned true. + int HandlePrepare(); + bool HandleCheck(); + void HandleDispatch(); + + // Adds an Observer, which will start receiving notifications immediately. + void AddObserver(MessagePumpObserver* observer); + + // Removes an Observer. It is safe to call this method while an Observer is + // receiving a notification callback. + void RemoveObserver(MessagePumpObserver* observer); + + // Overridden from MessagePump: + virtual void Run(Delegate* delegate) OVERRIDE; + virtual void Quit() OVERRIDE; + virtual void ScheduleWork() OVERRIDE; + virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time) OVERRIDE; + + protected: + virtual ~MessagePumpGlib(); + + // Returns the dispatcher for the current run state (|state_->dispatcher|). + MessagePumpDispatcher* GetDispatcher(); + + ObserverList<MessagePumpObserver>& observers() { return observers_; } + + private: + // We may make recursive calls to Run, so we save state that needs to be + // separate between them in this structure type. + struct RunState; + + RunState* state_; + + // This is a GLib structure that we can add event sources to. We use the + // default GLib context, which is the one to which all GTK events are + // dispatched. + GMainContext* context_; + + // This is the time when we need to do delayed work. + TimeTicks delayed_work_time_; + + // The work source. It is shared by all calls to Run and destroyed when + // the message pump is destroyed. + GSource* work_source_; + + // We use a wakeup pipe to make sure we'll get out of the glib polling phase + // when another thread has scheduled us to do some work. There is a glib + // mechanism g_main_context_wakeup, but this won't guarantee that our event's + // Dispatch() will be called. + int wakeup_pipe_read_; + int wakeup_pipe_write_; + // Use a scoped_ptr to avoid needing the definition of GPollFD in the header. + scoped_ptr<GPollFD> wakeup_gpollfd_; + + // List of observers. + ObserverList<MessagePumpObserver> observers_; + + DISALLOW_COPY_AND_ASSIGN(MessagePumpGlib); +}; + +} // namespace base + +#endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_GLIB_H_ diff --git a/base/message_loop/message_pump_glib_unittest.cc b/base/message_loop/message_pump_glib_unittest.cc new file mode 100644 index 0000000..cb30bb0 --- /dev/null +++ b/base/message_loop/message_pump_glib_unittest.cc @@ -0,0 +1,580 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/message_loop/message_pump_glib.h" + +#include <glib.h> +#include <math.h> + +#include <algorithm> +#include <vector> + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/callback.h" +#include "base/memory/ref_counted.h" +#include "base/message_loop.h" +#include "base/run_loop.h" +#include "base/threading/thread.h" +#include "testing/gtest/include/gtest/gtest.h" + +#if defined(TOOLKIT_GTK) +#include <gtk/gtk.h> +#endif + +namespace base { +namespace { + +// This class injects dummy "events" into the GLib loop. When "handled" these +// events can run tasks. This is intended to mock gtk events (the corresponding +// GLib source runs at the same priority). +class EventInjector { + public: + EventInjector() : processed_events_(0) { + source_ = static_cast<Source*>(g_source_new(&SourceFuncs, sizeof(Source))); + source_->injector = this; + g_source_attach(source_, NULL); + g_source_set_can_recurse(source_, TRUE); + } + + ~EventInjector() { + g_source_destroy(source_); + g_source_unref(source_); + } + + int HandlePrepare() { + // If the queue is empty, block. + if (events_.empty()) + return -1; + TimeDelta delta = events_[0].time - Time::NowFromSystemTime(); + return std::max(0, static_cast<int>(ceil(delta.InMillisecondsF()))); + } + + bool HandleCheck() { + if (events_.empty()) + return false; + return events_[0].time <= Time::NowFromSystemTime(); + } + + void HandleDispatch() { + if (events_.empty()) + return; + Event event = events_[0]; + events_.erase(events_.begin()); + ++processed_events_; + if (!event.callback.is_null()) + event.callback.Run(); + else if (!event.task.is_null()) + event.task.Run(); + } + + // Adds an event to the queue. When "handled", executes |callback|. + // delay_ms is relative to the last event if any, or to Now() otherwise. + void AddEvent(int delay_ms, const Closure& callback) { + AddEventHelper(delay_ms, callback, Closure()); + } + + void AddDummyEvent(int delay_ms) { + AddEventHelper(delay_ms, Closure(), Closure()); + } + + void AddEventAsTask(int delay_ms, const Closure& task) { + AddEventHelper(delay_ms, Closure(), task); + } + + void Reset() { + processed_events_ = 0; + events_.clear(); + } + + int processed_events() const { return processed_events_; } + + private: + struct Event { + Time time; + Closure callback; + Closure task; + }; + + struct Source : public GSource { + EventInjector* injector; + }; + + void AddEventHelper( + int delay_ms, const Closure& callback, const Closure& task) { + Time last_time; + if (!events_.empty()) + last_time = (events_.end()-1)->time; + else + last_time = Time::NowFromSystemTime(); + + Time future = last_time + TimeDelta::FromMilliseconds(delay_ms); + EventInjector::Event event = {future, callback, task}; + events_.push_back(event); + } + + static gboolean Prepare(GSource* source, gint* timeout_ms) { + *timeout_ms = static_cast<Source*>(source)->injector->HandlePrepare(); + return FALSE; + } + + static gboolean Check(GSource* source) { + return static_cast<Source*>(source)->injector->HandleCheck(); + } + + static gboolean Dispatch(GSource* source, + GSourceFunc unused_func, + gpointer unused_data) { + static_cast<Source*>(source)->injector->HandleDispatch(); + return TRUE; + } + + Source* source_; + std::vector<Event> events_; + int processed_events_; + static GSourceFuncs SourceFuncs; + DISALLOW_COPY_AND_ASSIGN(EventInjector); +}; + +GSourceFuncs EventInjector::SourceFuncs = { + EventInjector::Prepare, + EventInjector::Check, + EventInjector::Dispatch, + NULL +}; + +void IncrementInt(int *value) { + ++*value; +} + +// Checks how many events have been processed by the injector. +void ExpectProcessedEvents(EventInjector* injector, int count) { + EXPECT_EQ(injector->processed_events(), count); +} + +// Posts a task on the current message loop. +void PostMessageLoopTask(const tracked_objects::Location& from_here, + const Closure& task) { + MessageLoop::current()->PostTask(from_here, task); +} + +// Test fixture. +class MessagePumpGLibTest : public testing::Test { + public: + MessagePumpGLibTest() : loop_(NULL), injector_(NULL) { } + + // Overridden from testing::Test: + virtual void SetUp() OVERRIDE { + loop_ = new MessageLoop(MessageLoop::TYPE_UI); + injector_ = new EventInjector(); + } + virtual void TearDown() OVERRIDE { + delete injector_; + injector_ = NULL; + delete loop_; + loop_ = NULL; + } + + MessageLoop* loop() const { return loop_; } + EventInjector* injector() const { return injector_; } + + private: + MessageLoop* loop_; + EventInjector* injector_; + DISALLOW_COPY_AND_ASSIGN(MessagePumpGLibTest); +}; + +} // namespace + +TEST_F(MessagePumpGLibTest, TestQuit) { + // Checks that Quit works and that the basic infrastructure is working. + + // Quit from a task + RunLoop().RunUntilIdle(); + EXPECT_EQ(0, injector()->processed_events()); + + injector()->Reset(); + // Quit from an event + injector()->AddEvent(0, MessageLoop::QuitWhenIdleClosure()); + loop()->Run(); + EXPECT_EQ(1, injector()->processed_events()); +} + +TEST_F(MessagePumpGLibTest, TestEventTaskInterleave) { + // Checks that tasks posted by events are executed before the next event if + // the posted task queue is empty. + // MessageLoop doesn't make strong guarantees that it is the case, but the + // current implementation ensures it and the tests below rely on it. + // If changes cause this test to fail, it is reasonable to change it, but + // TestWorkWhileWaitingForEvents and TestEventsWhileWaitingForWork have to be + // changed accordingly, otherwise they can become flaky. + injector()->AddEventAsTask(0, Bind(&DoNothing)); + Closure check_task = + Bind(&ExpectProcessedEvents, Unretained(injector()), 2); + Closure posted_task = + Bind(&PostMessageLoopTask, FROM_HERE, check_task); + injector()->AddEventAsTask(0, posted_task); + injector()->AddEventAsTask(0, Bind(&DoNothing)); + injector()->AddEvent(0, MessageLoop::QuitWhenIdleClosure()); + loop()->Run(); + EXPECT_EQ(4, injector()->processed_events()); + + injector()->Reset(); + injector()->AddEventAsTask(0, Bind(&DoNothing)); + check_task = + Bind(&ExpectProcessedEvents, Unretained(injector()), 2); + posted_task = Bind(&PostMessageLoopTask, FROM_HERE, check_task); + injector()->AddEventAsTask(0, posted_task); + injector()->AddEventAsTask(10, Bind(&DoNothing)); + injector()->AddEvent(0, MessageLoop::QuitWhenIdleClosure()); + loop()->Run(); + EXPECT_EQ(4, injector()->processed_events()); +} + +TEST_F(MessagePumpGLibTest, TestWorkWhileWaitingForEvents) { + int task_count = 0; + // Tests that we process tasks while waiting for new events. + // The event queue is empty at first. + for (int i = 0; i < 10; ++i) { + loop()->PostTask(FROM_HERE, Bind(&IncrementInt, &task_count)); + } + // After all the previous tasks have executed, enqueue an event that will + // quit. + loop()->PostTask( + FROM_HERE, + Bind(&EventInjector::AddEvent, Unretained(injector()), 0, + MessageLoop::QuitWhenIdleClosure())); + loop()->Run(); + ASSERT_EQ(10, task_count); + EXPECT_EQ(1, injector()->processed_events()); + + // Tests that we process delayed tasks while waiting for new events. + injector()->Reset(); + task_count = 0; + for (int i = 0; i < 10; ++i) { + loop()->PostDelayedTask( + FROM_HERE, + Bind(&IncrementInt, &task_count), + TimeDelta::FromMilliseconds(10*i)); + } + // After all the previous tasks have executed, enqueue an event that will + // quit. + // This relies on the fact that delayed tasks are executed in delay order. + // That is verified in message_loop_unittest.cc. + loop()->PostDelayedTask( + FROM_HERE, + Bind(&EventInjector::AddEvent, Unretained(injector()), 10, + MessageLoop::QuitWhenIdleClosure()), + TimeDelta::FromMilliseconds(150)); + loop()->Run(); + ASSERT_EQ(10, task_count); + EXPECT_EQ(1, injector()->processed_events()); +} + +TEST_F(MessagePumpGLibTest, TestEventsWhileWaitingForWork) { + // Tests that we process events while waiting for work. + // The event queue is empty at first. + for (int i = 0; i < 10; ++i) { + injector()->AddDummyEvent(0); + } + // After all the events have been processed, post a task that will check that + // the events have been processed (note: the task executes after the event + // that posted it has been handled, so we expect 11 at that point). + Closure check_task = + Bind(&ExpectProcessedEvents, Unretained(injector()), 11); + Closure posted_task = + Bind(&PostMessageLoopTask, FROM_HERE, check_task); + injector()->AddEventAsTask(10, posted_task); + + // And then quit (relies on the condition tested by TestEventTaskInterleave). + injector()->AddEvent(10, MessageLoop::QuitWhenIdleClosure()); + loop()->Run(); + + EXPECT_EQ(12, injector()->processed_events()); +} + +namespace { + +// This class is a helper for the concurrent events / posted tasks test below. +// It will quit the main loop once enough tasks and events have been processed, +// while making sure there is always work to do and events in the queue. +class ConcurrentHelper : public RefCounted<ConcurrentHelper> { + public: + explicit ConcurrentHelper(EventInjector* injector) + : injector_(injector), + event_count_(kStartingEventCount), + task_count_(kStartingTaskCount) { + } + + void FromTask() { + if (task_count_ > 0) { + --task_count_; + } + if (task_count_ == 0 && event_count_ == 0) { + MessageLoop::current()->QuitWhenIdle(); + } else { + MessageLoop::current()->PostTask( + FROM_HERE, Bind(&ConcurrentHelper::FromTask, this)); + } + } + + void FromEvent() { + if (event_count_ > 0) { + --event_count_; + } + if (task_count_ == 0 && event_count_ == 0) { + MessageLoop::current()->QuitWhenIdle(); + } else { + injector_->AddEventAsTask( + 0, Bind(&ConcurrentHelper::FromEvent, this)); + } + } + + int event_count() const { return event_count_; } + int task_count() const { return task_count_; } + + private: + friend class RefCounted<ConcurrentHelper>; + + ~ConcurrentHelper() {} + + static const int kStartingEventCount = 20; + static const int kStartingTaskCount = 20; + + EventInjector* injector_; + int event_count_; + int task_count_; +}; + +} // namespace + +TEST_F(MessagePumpGLibTest, TestConcurrentEventPostedTask) { + // Tests that posted tasks don't starve events, nor the opposite. + // We use the helper class above. We keep both event and posted task queues + // full, the helper verifies that both tasks and events get processed. + // If that is not the case, either event_count_ or task_count_ will not get + // to 0, and MessageLoop::QuitWhenIdle() will never be called. + scoped_refptr<ConcurrentHelper> helper = new ConcurrentHelper(injector()); + + // Add 2 events to the queue to make sure it is always full (when we remove + // the event before processing it). + injector()->AddEventAsTask( + 0, Bind(&ConcurrentHelper::FromEvent, helper.get())); + injector()->AddEventAsTask( + 0, Bind(&ConcurrentHelper::FromEvent, helper.get())); + + // Similarly post 2 tasks. + loop()->PostTask( + FROM_HERE, Bind(&ConcurrentHelper::FromTask, helper.get())); + loop()->PostTask( + FROM_HERE, Bind(&ConcurrentHelper::FromTask, helper.get())); + + loop()->Run(); + EXPECT_EQ(0, helper->event_count()); + EXPECT_EQ(0, helper->task_count()); +} + +namespace { + +void AddEventsAndDrainGLib(EventInjector* injector) { + // Add a couple of dummy events + injector->AddDummyEvent(0); + injector->AddDummyEvent(0); + // Then add an event that will quit the main loop. + injector->AddEvent(0, MessageLoop::QuitWhenIdleClosure()); + + // Post a couple of dummy tasks + MessageLoop::current()->PostTask(FROM_HERE, Bind(&DoNothing)); + MessageLoop::current()->PostTask(FROM_HERE, Bind(&DoNothing)); + + // Drain the events + while (g_main_context_pending(NULL)) { + g_main_context_iteration(NULL, FALSE); + } +} + +} // namespace + +TEST_F(MessagePumpGLibTest, TestDrainingGLib) { + // Tests that draining events using GLib works. + loop()->PostTask( + FROM_HERE, + Bind(&AddEventsAndDrainGLib, Unretained(injector()))); + loop()->Run(); + + EXPECT_EQ(3, injector()->processed_events()); +} + + +namespace { + +#if defined(TOOLKIT_GTK) +void AddEventsAndDrainGtk(EventInjector* injector) { + // Add a couple of dummy events + injector->AddDummyEvent(0); + injector->AddDummyEvent(0); + // Then add an event that will quit the main loop. + injector->AddEvent(0, MessageLoop::QuitWhenIdleClosure()); + + // Post a couple of dummy tasks + MessageLoop::current()->PostTask(FROM_HERE, Bind(&DoNothing)); + MessageLoop::current()->PostTask(FROM_HERE, Bind(&DoNothing)); + + // Drain the events + while (gtk_events_pending()) { + gtk_main_iteration(); + } +} +#endif + +} // namespace + +#if defined(TOOLKIT_GTK) +TEST_F(MessagePumpGLibTest, TestDrainingGtk) { + // Tests that draining events using Gtk works. + loop()->PostTask( + FROM_HERE, + Bind(&AddEventsAndDrainGtk, Unretained(injector()))); + loop()->Run(); + + EXPECT_EQ(3, injector()->processed_events()); +} +#endif + +namespace { + +// Helper class that lets us run the GLib message loop. +class GLibLoopRunner : public RefCounted<GLibLoopRunner> { + public: + GLibLoopRunner() : quit_(false) { } + + void RunGLib() { + while (!quit_) { + g_main_context_iteration(NULL, TRUE); + } + } + + void RunLoop() { +#if defined(TOOLKIT_GTK) + while (!quit_) { + gtk_main_iteration(); + } +#else + while (!quit_) { + g_main_context_iteration(NULL, TRUE); + } +#endif + } + + void Quit() { + quit_ = true; + } + + void Reset() { + quit_ = false; + } + + private: + friend class RefCounted<GLibLoopRunner>; + + ~GLibLoopRunner() {} + + bool quit_; +}; + +void TestGLibLoopInternal(EventInjector* injector) { + // Allow tasks to be processed from 'native' event loops. + MessageLoop::current()->SetNestableTasksAllowed(true); + scoped_refptr<GLibLoopRunner> runner = new GLibLoopRunner(); + + int task_count = 0; + // Add a couple of dummy events + injector->AddDummyEvent(0); + injector->AddDummyEvent(0); + // Post a couple of dummy tasks + MessageLoop::current()->PostTask( + FROM_HERE, Bind(&IncrementInt, &task_count)); + MessageLoop::current()->PostTask( + FROM_HERE, Bind(&IncrementInt, &task_count)); + // Delayed events + injector->AddDummyEvent(10); + injector->AddDummyEvent(10); + // Delayed work + MessageLoop::current()->PostDelayedTask( + FROM_HERE, + Bind(&IncrementInt, &task_count), + TimeDelta::FromMilliseconds(30)); + MessageLoop::current()->PostDelayedTask( + FROM_HERE, + Bind(&GLibLoopRunner::Quit, runner.get()), + TimeDelta::FromMilliseconds(40)); + + // Run a nested, straight GLib message loop. + runner->RunGLib(); + + ASSERT_EQ(3, task_count); + EXPECT_EQ(4, injector->processed_events()); + MessageLoop::current()->QuitWhenIdle(); +} + +void TestGtkLoopInternal(EventInjector* injector) { + // Allow tasks to be processed from 'native' event loops. + MessageLoop::current()->SetNestableTasksAllowed(true); + scoped_refptr<GLibLoopRunner> runner = new GLibLoopRunner(); + + int task_count = 0; + // Add a couple of dummy events + injector->AddDummyEvent(0); + injector->AddDummyEvent(0); + // Post a couple of dummy tasks + MessageLoop::current()->PostTask( + FROM_HERE, Bind(&IncrementInt, &task_count)); + MessageLoop::current()->PostTask( + FROM_HERE, Bind(&IncrementInt, &task_count)); + // Delayed events + injector->AddDummyEvent(10); + injector->AddDummyEvent(10); + // Delayed work + MessageLoop::current()->PostDelayedTask( + FROM_HERE, + Bind(&IncrementInt, &task_count), + TimeDelta::FromMilliseconds(30)); + MessageLoop::current()->PostDelayedTask( + FROM_HERE, + Bind(&GLibLoopRunner::Quit, runner.get()), + TimeDelta::FromMilliseconds(40)); + + // Run a nested, straight Gtk message loop. + runner->RunLoop(); + + ASSERT_EQ(3, task_count); + EXPECT_EQ(4, injector->processed_events()); + MessageLoop::current()->QuitWhenIdle(); +} + +} // namespace + +TEST_F(MessagePumpGLibTest, TestGLibLoop) { + // Tests that events and posted tasks are correctly executed if the message + // loop is not run by MessageLoop::Run() but by a straight GLib loop. + // Note that in this case we don't make strong guarantees about niceness + // between events and posted tasks. + loop()->PostTask( + FROM_HERE, + Bind(&TestGLibLoopInternal, Unretained(injector()))); + loop()->Run(); +} + +TEST_F(MessagePumpGLibTest, TestGtkLoop) { + // Tests that events and posted tasks are correctly executed if the message + // loop is not run by MessageLoop::Run() but by a straight Gtk loop. + // Note that in this case we don't make strong guarantees about niceness + // between events and posted tasks. + loop()->PostTask( + FROM_HERE, + Bind(&TestGtkLoopInternal, Unretained(injector()))); + loop()->Run(); +} + +} // namespace base diff --git a/base/message_loop/message_pump_gtk.cc b/base/message_loop/message_pump_gtk.cc new file mode 100644 index 0000000..8fa8cf2 --- /dev/null +++ b/base/message_loop/message_pump_gtk.cc @@ -0,0 +1,114 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/message_loop/message_pump_gtk.h" + +#include <gtk/gtk.h> +#include <gdk/gdkx.h> + +#include "base/debug/trace_event.h" +#include "base/profiler/scoped_profile.h" + +namespace base { + +namespace { + +const char* EventToTypeString(const GdkEvent* event) { + switch (event->type) { + case GDK_NOTHING: return "GDK_NOTHING"; + case GDK_DELETE: return "GDK_DELETE"; + case GDK_DESTROY: return "GDK_DESTROY"; + case GDK_EXPOSE: return "GDK_EXPOSE"; + case GDK_MOTION_NOTIFY: return "GDK_MOTION_NOTIFY"; + case GDK_BUTTON_PRESS: return "GDK_BUTTON_PRESS"; + case GDK_2BUTTON_PRESS: return "GDK_2BUTTON_PRESS"; + case GDK_3BUTTON_PRESS: return "GDK_3BUTTON_PRESS"; + case GDK_BUTTON_RELEASE: return "GDK_BUTTON_RELEASE"; + case GDK_KEY_PRESS: return "GDK_KEY_PRESS"; + case GDK_KEY_RELEASE: return "GDK_KEY_RELEASE"; + case GDK_ENTER_NOTIFY: return "GDK_ENTER_NOTIFY"; + case GDK_LEAVE_NOTIFY: return "GDK_LEAVE_NOTIFY"; + case GDK_FOCUS_CHANGE: return "GDK_FOCUS_CHANGE"; + case GDK_CONFIGURE: return "GDK_CONFIGURE"; + case GDK_MAP: return "GDK_MAP"; + case GDK_UNMAP: return "GDK_UNMAP"; + case GDK_PROPERTY_NOTIFY: return "GDK_PROPERTY_NOTIFY"; + case GDK_SELECTION_CLEAR: return "GDK_SELECTION_CLEAR"; + case GDK_SELECTION_REQUEST: return "GDK_SELECTION_REQUEST"; + case GDK_SELECTION_NOTIFY: return "GDK_SELECTION_NOTIFY"; + case GDK_PROXIMITY_IN: return "GDK_PROXIMITY_IN"; + case GDK_PROXIMITY_OUT: return "GDK_PROXIMITY_OUT"; + case GDK_DRAG_ENTER: return "GDK_DRAG_ENTER"; + case GDK_DRAG_LEAVE: return "GDK_DRAG_LEAVE"; + case GDK_DRAG_MOTION: return "GDK_DRAG_MOTION"; + case GDK_DRAG_STATUS: return "GDK_DRAG_STATUS"; + case GDK_DROP_START: return "GDK_DROP_START"; + case GDK_DROP_FINISHED: return "GDK_DROP_FINISHED"; + case GDK_CLIENT_EVENT: return "GDK_CLIENT_EVENT"; + case GDK_VISIBILITY_NOTIFY: return "GDK_VISIBILITY_NOTIFY"; + case GDK_NO_EXPOSE: return "GDK_NO_EXPOSE"; + case GDK_SCROLL: return "GDK_SCROLL"; + case GDK_WINDOW_STATE: return "GDK_WINDOW_STATE"; + case GDK_SETTING: return "GDK_SETTING"; + case GDK_OWNER_CHANGE: return "GDK_OWNER_CHANGE"; + case GDK_GRAB_BROKEN: return "GDK_GRAB_BROKEN"; + case GDK_DAMAGE: return "GDK_DAMAGE"; + default: + return "Unknown Gdk Event"; + } +} + +} // namespace + +MessagePumpGtk::MessagePumpGtk() : MessagePumpGlib() { + gdk_event_handler_set(&EventDispatcher, this, NULL); +} + +void MessagePumpGtk::DispatchEvents(GdkEvent* event) { + UNSHIPPED_TRACE_EVENT1("task", "MessagePumpGtk::DispatchEvents", + "type", EventToTypeString(event)); + + WillProcessEvent(event); + + MessagePumpDispatcher* dispatcher = GetDispatcher(); + if (!dispatcher) + gtk_main_do_event(event); + else if (!dispatcher->Dispatch(event)) + Quit(); + + DidProcessEvent(event); +} + +// static +Display* MessagePumpGtk::GetDefaultXDisplay() { + static GdkDisplay* display = gdk_display_get_default(); + if (!display) { + // GTK / GDK has not been initialized, which is a decision we wish to + // support, for example for the GPU process. + static Display* xdisplay = XOpenDisplay(NULL); + return xdisplay; + } + return GDK_DISPLAY_XDISPLAY(display); +} + +MessagePumpGtk::~MessagePumpGtk() { + gdk_event_handler_set(reinterpret_cast<GdkEventFunc>(gtk_main_do_event), + this, NULL); +} + +void MessagePumpGtk::WillProcessEvent(GdkEvent* event) { + FOR_EACH_OBSERVER(MessagePumpObserver, observers(), WillProcessEvent(event)); +} + +void MessagePumpGtk::DidProcessEvent(GdkEvent* event) { + FOR_EACH_OBSERVER(MessagePumpObserver, observers(), DidProcessEvent(event)); +} + +// static +void MessagePumpGtk::EventDispatcher(GdkEvent* event, gpointer data) { + MessagePumpGtk* message_pump = reinterpret_cast<MessagePumpGtk*>(data); + message_pump->DispatchEvents(event); +} + +} // namespace base diff --git a/base/message_loop/message_pump_gtk.h b/base/message_loop/message_pump_gtk.h new file mode 100644 index 0000000..e22e04f --- /dev/null +++ b/base/message_loop/message_pump_gtk.h @@ -0,0 +1,76 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MESSAGE_LOOP_MESSAGE_PUMP_GTK_H_ +#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_GTK_H_ + +#include "base/message_loop/message_pump_glib.h" + +typedef union _GdkEvent GdkEvent; +typedef struct _XDisplay Display; + +namespace base { + +// The documentation for this class is in message_pump_glib.h +class MessagePumpObserver { + public: + // This method is called before processing a message. + virtual void WillProcessEvent(GdkEvent* event) = 0; + + // This method is called after processing a message. + virtual void DidProcessEvent(GdkEvent* event) = 0; + + protected: + virtual ~MessagePumpObserver() {} +}; + +// The documentation for this class is in message_pump_glib.h +// +// The nested loop is exited by either posting a quit, or returning false +// from Dispatch. +class MessagePumpDispatcher { + public: + // Dispatches the event. If true is returned processing continues as + // normal. If false is returned, the nested loop exits immediately. + virtual bool Dispatch(GdkEvent* event) = 0; + + protected: + virtual ~MessagePumpDispatcher() {} +}; + +// This class implements a message-pump for dispatching GTK events. +class BASE_EXPORT MessagePumpGtk : public MessagePumpGlib { + public: + MessagePumpGtk(); + + // Dispatch an available GdkEvent. Essentially this allows a subclass to do + // some task before/after calling the default handler (EventDispatcher). + void DispatchEvents(GdkEvent* event); + + // Returns default X Display. + static Display* GetDefaultXDisplay(); + + protected: + virtual ~MessagePumpGtk(); + + private: + // Invoked from EventDispatcher. Notifies all observers we're about to + // process an event. + void WillProcessEvent(GdkEvent* event); + + // Invoked from EventDispatcher. Notifies all observers we processed an + // event. + void DidProcessEvent(GdkEvent* event); + + // Callback prior to gdk dispatching an event. + static void EventDispatcher(GdkEvent* event, void* data); + + DISALLOW_COPY_AND_ASSIGN(MessagePumpGtk); +}; + +typedef MessagePumpGtk MessagePumpForUI; + +} // namespace base + +#endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_GTK_H_ diff --git a/base/message_loop/message_pump_io_ios.cc b/base/message_loop/message_pump_io_ios.cc new file mode 100644 index 0000000..5868195 --- /dev/null +++ b/base/message_loop/message_pump_io_ios.cc @@ -0,0 +1,209 @@ +// Copyright 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/message_loop/message_pump_io_ios.h" + +namespace base { + +MessagePumpIOSForIO::FileDescriptorWatcher::FileDescriptorWatcher() + : is_persistent_(false), + fdref_(NULL), + callback_types_(0), + fd_source_(NULL), + pump_(NULL), + watcher_(NULL) { +} + +MessagePumpIOSForIO::FileDescriptorWatcher::~FileDescriptorWatcher() { + StopWatchingFileDescriptor(); +} + +bool MessagePumpIOSForIO::FileDescriptorWatcher::StopWatchingFileDescriptor() { + if (fdref_ == NULL) + return true; + + CFFileDescriptorDisableCallBacks(fdref_, callback_types_); + pump_->RemoveRunLoopSource(fd_source_); + fd_source_.reset(); + fdref_.reset(); + callback_types_ = 0; + pump_ = NULL; + watcher_ = NULL; + return true; +} + +void MessagePumpIOSForIO::FileDescriptorWatcher::Init( + CFFileDescriptorRef fdref, + CFOptionFlags callback_types, + CFRunLoopSourceRef fd_source, + bool is_persistent) { + DCHECK(fdref); + DCHECK(!fdref_); + + is_persistent_ = is_persistent; + fdref_.reset(fdref); + callback_types_ = callback_types; + fd_source_.reset(fd_source); +} + +void MessagePumpIOSForIO::FileDescriptorWatcher::OnFileCanReadWithoutBlocking( + int fd, + MessagePumpIOSForIO* pump) { + DCHECK(callback_types_ & kCFFileDescriptorReadCallBack); + pump->WillProcessIOEvent(); + watcher_->OnFileCanReadWithoutBlocking(fd); + pump->DidProcessIOEvent(); +} + +void MessagePumpIOSForIO::FileDescriptorWatcher::OnFileCanWriteWithoutBlocking( + int fd, + MessagePumpIOSForIO* pump) { + DCHECK(callback_types_ & kCFFileDescriptorWriteCallBack); + pump->WillProcessIOEvent(); + watcher_->OnFileCanWriteWithoutBlocking(fd); + pump->DidProcessIOEvent(); +} + +MessagePumpIOSForIO::MessagePumpIOSForIO() { +} + +MessagePumpIOSForIO::~MessagePumpIOSForIO() { +} + +bool MessagePumpIOSForIO::WatchFileDescriptor( + int fd, + bool persistent, + int mode, + FileDescriptorWatcher *controller, + Watcher *delegate) { + DCHECK_GE(fd, 0); + DCHECK(controller); + DCHECK(delegate); + DCHECK(mode == WATCH_READ || mode == WATCH_WRITE || mode == WATCH_READ_WRITE); + + // WatchFileDescriptor should be called on the pump thread. It is not + // threadsafe, and your watcher may never be registered. + DCHECK(watch_file_descriptor_caller_checker_.CalledOnValidThread()); + + CFFileDescriptorContext source_context = {0}; + source_context.info = controller; + + CFOptionFlags callback_types = 0; + if (mode & WATCH_READ) { + callback_types |= kCFFileDescriptorReadCallBack; + } + if (mode & WATCH_WRITE) { + callback_types |= kCFFileDescriptorWriteCallBack; + } + + CFFileDescriptorRef fdref = controller->fdref_; + if (fdref == NULL) { + base::mac::ScopedCFTypeRef<CFFileDescriptorRef> scoped_fdref( + CFFileDescriptorCreate(kCFAllocatorDefault, fd, false, HandleFdIOEvent, + &source_context)); + if (scoped_fdref == NULL) { + NOTREACHED() << "CFFileDescriptorCreate failed"; + return false; + } + + CFFileDescriptorEnableCallBacks(scoped_fdref, callback_types); + + // TODO(wtc): what should the 'order' argument be? + base::mac::ScopedCFTypeRef<CFRunLoopSourceRef> scoped_fd_source( + CFFileDescriptorCreateRunLoopSource(kCFAllocatorDefault, + scoped_fdref, + 0)); + if (scoped_fd_source == NULL) { + NOTREACHED() << "CFFileDescriptorCreateRunLoopSource failed"; + return false; + } + CFRunLoopAddSource(run_loop(), scoped_fd_source, kCFRunLoopCommonModes); + + // Transfer ownership of scoped_fdref and fd_source to controller. + controller->Init(scoped_fdref.release(), callback_types, + scoped_fd_source.release(), persistent); + } else { + // It's illegal to use this function to listen on 2 separate fds with the + // same |controller|. + if (CFFileDescriptorGetNativeDescriptor(fdref) != fd) { + NOTREACHED() << "FDs don't match: " + << CFFileDescriptorGetNativeDescriptor(fdref) + << " != " << fd; + return false; + } + if (persistent != controller->is_persistent_) { + NOTREACHED() << "persistent doesn't match"; + return false; + } + + // Combine old/new event masks. + CFFileDescriptorDisableCallBacks(fdref, controller->callback_types_); + controller->callback_types_ |= callback_types; + CFFileDescriptorEnableCallBacks(fdref, controller->callback_types_); + } + + controller->set_watcher(delegate); + controller->set_pump(this); + + return true; +} + +void MessagePumpIOSForIO::RemoveRunLoopSource(CFRunLoopSourceRef source) { + CFRunLoopRemoveSource(run_loop(), source, kCFRunLoopCommonModes); +} + +void MessagePumpIOSForIO::AddIOObserver(IOObserver *obs) { + io_observers_.AddObserver(obs); +} + +void MessagePumpIOSForIO::RemoveIOObserver(IOObserver *obs) { + io_observers_.RemoveObserver(obs); +} + +void MessagePumpIOSForIO::WillProcessIOEvent() { + FOR_EACH_OBSERVER(IOObserver, io_observers_, WillProcessIOEvent()); +} + +void MessagePumpIOSForIO::DidProcessIOEvent() { + FOR_EACH_OBSERVER(IOObserver, io_observers_, DidProcessIOEvent()); +} + +// static +void MessagePumpIOSForIO::HandleFdIOEvent(CFFileDescriptorRef fdref, + CFOptionFlags callback_types, + void* context) { + FileDescriptorWatcher* controller = + static_cast<FileDescriptorWatcher*>(context); + DCHECK_EQ(fdref, controller->fdref_); + + // Ensure that |fdref| will remain live for the duration of this function + // call even if |controller| is deleted or |StopWatchingFileDescriptor()| is + // called, either of which will cause |fdref| to be released. + mac::ScopedCFTypeRef<CFFileDescriptorRef> scoped_fdref( + fdref, base::scoped_policy::RETAIN); + + int fd = CFFileDescriptorGetNativeDescriptor(fdref); + MessagePumpIOSForIO* pump = controller->pump(); + if (callback_types & kCFFileDescriptorWriteCallBack) + controller->OnFileCanWriteWithoutBlocking(fd, pump); + + // Perform the read callback only if the file descriptor has not been + // invalidated in the write callback. As |FileDescriptorWatcher| invalidates + // its file descriptor on destruction, the file descriptor being valid also + // guarantees that |controller| has not been deleted. + if (callback_types & kCFFileDescriptorReadCallBack && + CFFileDescriptorIsValid(fdref)) { + DCHECK_EQ(fdref, controller->fdref_); + controller->OnFileCanReadWithoutBlocking(fd, pump); + } + + // Re-enable callbacks after the read/write if the file descriptor is still + // valid and the controller is persistent. + if (CFFileDescriptorIsValid(fdref) && controller->is_persistent_) { + DCHECK_EQ(fdref, controller->fdref_); + CFFileDescriptorEnableCallBacks(fdref, callback_types); + } +} + +} // namespace base diff --git a/base/message_loop/message_pump_io_ios.h b/base/message_loop/message_pump_io_ios.h new file mode 100644 index 0000000..3aaf037 --- /dev/null +++ b/base/message_loop/message_pump_io_ios.h @@ -0,0 +1,142 @@ +// Copyright 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MESSAGE_LOOP_MESSAGE_PUMP_IO_IOS_H_ +#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_IO_IOS_H_ + +#include "base/base_export.h" +#include "base/mac/scoped_cffiledescriptorref.h" +#include "base/mac/scoped_cftyperef.h" +#include "base/memory/ref_counted.h" +#include "base/message_loop/message_pump_mac.h" +#include "base/observer_list.h" + +namespace base { + +// This file introduces a class to monitor sockets and issue callbacks when +// sockets are ready for I/O on iOS. +class BASE_EXPORT MessagePumpIOSForIO : public MessagePumpNSRunLoop { + public: + class IOObserver { + public: + IOObserver() {} + + // An IOObserver is an object that receives IO notifications from the + // MessagePump. + // + // NOTE: An IOObserver implementation should be extremely fast! + virtual void WillProcessIOEvent() = 0; + virtual void DidProcessIOEvent() = 0; + + protected: + virtual ~IOObserver() {} + }; + + // Used with WatchFileDescriptor to asynchronously monitor the I/O readiness + // of a file descriptor. + class Watcher { + public: + // Called from MessageLoop::Run when an FD can be read from/written to + // without blocking + virtual void OnFileCanReadWithoutBlocking(int fd) = 0; + virtual void OnFileCanWriteWithoutBlocking(int fd) = 0; + + protected: + virtual ~Watcher() {} + }; + + // Object returned by WatchFileDescriptor to manage further watching. + class FileDescriptorWatcher { + public: + FileDescriptorWatcher(); + ~FileDescriptorWatcher(); // Implicitly calls StopWatchingFileDescriptor. + + // NOTE: These methods aren't called StartWatching()/StopWatching() to + // avoid confusion with the win32 ObjectWatcher class. + + // Stop watching the FD, always safe to call. No-op if there's nothing + // to do. + bool StopWatchingFileDescriptor(); + + private: + friend class MessagePumpIOSForIO; + friend class MessagePumpIOSForIOTest; + + // Called by MessagePumpIOSForIO, ownership of |fdref| and |fd_source| + // is transferred to this object. + void Init(CFFileDescriptorRef fdref, + CFOptionFlags callback_types, + CFRunLoopSourceRef fd_source, + bool is_persistent); + + void set_pump(MessagePumpIOSForIO* pump) { pump_ = pump; } + MessagePumpIOSForIO* pump() const { return pump_; } + + void set_watcher(Watcher* watcher) { watcher_ = watcher; } + + void OnFileCanReadWithoutBlocking(int fd, MessagePumpIOSForIO* pump); + void OnFileCanWriteWithoutBlocking(int fd, MessagePumpIOSForIO* pump); + + bool is_persistent_; // false if this event is one-shot. + base::mac::ScopedCFFileDescriptorRef fdref_; + CFOptionFlags callback_types_; + base::mac::ScopedCFTypeRef<CFRunLoopSourceRef> fd_source_; + scoped_refptr<MessagePumpIOSForIO> pump_; + Watcher* watcher_; + + DISALLOW_COPY_AND_ASSIGN(FileDescriptorWatcher); + }; + + enum Mode { + WATCH_READ = 1 << 0, + WATCH_WRITE = 1 << 1, + WATCH_READ_WRITE = WATCH_READ | WATCH_WRITE + }; + + MessagePumpIOSForIO(); + + // Have the current thread's message loop watch for a a situation in which + // reading/writing to the FD can be performed without blocking. + // Callers must provide a preallocated FileDescriptorWatcher object which + // can later be used to manage the lifetime of this event. + // If a FileDescriptorWatcher is passed in which is already attached to + // an event, then the effect is cumulative i.e. after the call |controller| + // will watch both the previous event and the new one. + // If an error occurs while calling this method in a cumulative fashion, the + // event previously attached to |controller| is aborted. + // Returns true on success. + // Must be called on the same thread the message_pump is running on. + bool WatchFileDescriptor(int fd, + bool persistent, + int mode, + FileDescriptorWatcher *controller, + Watcher *delegate); + + void RemoveRunLoopSource(CFRunLoopSourceRef source); + + void AddIOObserver(IOObserver* obs); + void RemoveIOObserver(IOObserver* obs); + + protected: + virtual ~MessagePumpIOSForIO(); + + private: + friend class MessagePumpIOSForIOTest; + + void WillProcessIOEvent(); + void DidProcessIOEvent(); + + static void HandleFdIOEvent(CFFileDescriptorRef fdref, + CFOptionFlags callback_types, + void* context); + + ObserverList<IOObserver> io_observers_; + ThreadChecker watch_file_descriptor_caller_checker_; + + DISALLOW_COPY_AND_ASSIGN(MessagePumpIOSForIO); +}; + +} // namespace base + +#endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_IO_IOS_H_ diff --git a/base/message_loop/message_pump_io_ios_unittest.cc b/base/message_loop/message_pump_io_ios_unittest.cc new file mode 100644 index 0000000..9e94c6b --- /dev/null +++ b/base/message_loop/message_pump_io_ios_unittest.cc @@ -0,0 +1,188 @@ +// Copyright 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/message_loop/message_pump_io_ios.h" + +#include <unistd.h> + +#include "base/message_loop.h" +#include "base/posix/eintr_wrapper.h" +#include "base/threading/thread.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { + +class MessagePumpIOSForIOTest : public testing::Test { + protected: + MessagePumpIOSForIOTest() + : ui_loop_(MessageLoop::TYPE_UI), + io_thread_("MessagePumpIOSForIOTestIOThread") {} + virtual ~MessagePumpIOSForIOTest() {} + + virtual void SetUp() OVERRIDE { + Thread::Options options(MessageLoop::TYPE_IO, 0); + ASSERT_TRUE(io_thread_.StartWithOptions(options)); + ASSERT_EQ(MessageLoop::TYPE_IO, io_thread_.message_loop()->type()); + int ret = pipe(pipefds_); + ASSERT_EQ(0, ret); + ret = pipe(alternate_pipefds_); + ASSERT_EQ(0, ret); + } + + virtual void TearDown() OVERRIDE { + if (HANDLE_EINTR(close(pipefds_[0])) < 0) + PLOG(ERROR) << "close"; + if (HANDLE_EINTR(close(pipefds_[1])) < 0) + PLOG(ERROR) << "close"; + } + + MessageLoop* ui_loop() { return &ui_loop_; } + MessageLoopForIO* io_loop() const { + return static_cast<MessageLoopForIO*>(io_thread_.message_loop()); + } + + void HandleFdIOEvent(MessageLoopForIO::FileDescriptorWatcher* watcher) { + MessagePumpIOSForIO::HandleFdIOEvent(watcher->fdref_, + kCFFileDescriptorReadCallBack | kCFFileDescriptorWriteCallBack, + watcher); + } + + int pipefds_[2]; + int alternate_pipefds_[2]; + + private: + MessageLoop ui_loop_; + Thread io_thread_; + + DISALLOW_COPY_AND_ASSIGN(MessagePumpIOSForIOTest); +}; + +namespace { + +// Concrete implementation of MessagePumpIOSForIO::Watcher that does +// nothing useful. +class StupidWatcher : public MessagePumpIOSForIO::Watcher { + public: + virtual ~StupidWatcher() {} + + // base:MessagePumpIOSForIO::Watcher interface + virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE {} + virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE {} +}; + +#if GTEST_HAS_DEATH_TEST && !defined(NDEBUG) + +// Test to make sure that we catch calling WatchFileDescriptor off of the +// wrong thread. +TEST_F(MessagePumpIOSForIOTest, TestWatchingFromBadThread) { + MessagePumpIOSForIO::FileDescriptorWatcher watcher; + StupidWatcher delegate; + + ASSERT_DEBUG_DEATH(io_loop()->WatchFileDescriptor( + STDOUT_FILENO, false, MessageLoopForIO::WATCH_READ, &watcher, &delegate), + "Check failed: " + "watch_file_descriptor_caller_checker_.CalledOnValidThread()"); +} + +#endif // GTEST_HAS_DEATH_TEST && !defined(NDEBUG) + +class BaseWatcher : public MessagePumpIOSForIO::Watcher { + public: + BaseWatcher(MessagePumpIOSForIO::FileDescriptorWatcher* controller) + : controller_(controller) { + DCHECK(controller_); + } + virtual ~BaseWatcher() {} + + // MessagePumpIOSForIO::Watcher interface + virtual void OnFileCanReadWithoutBlocking(int /* fd */) OVERRIDE { + NOTREACHED(); + } + + virtual void OnFileCanWriteWithoutBlocking(int /* fd */) OVERRIDE { + NOTREACHED(); + } + + protected: + MessagePumpIOSForIO::FileDescriptorWatcher* controller_; +}; + +class DeleteWatcher : public BaseWatcher { + public: + explicit DeleteWatcher( + MessagePumpIOSForIO::FileDescriptorWatcher* controller) + : BaseWatcher(controller) {} + + virtual ~DeleteWatcher() { + DCHECK(!controller_); + } + + virtual void OnFileCanWriteWithoutBlocking(int /* fd */) OVERRIDE { + DCHECK(controller_); + delete controller_; + controller_ = NULL; + } +}; + +TEST_F(MessagePumpIOSForIOTest, DeleteWatcher) { + scoped_refptr<MessagePumpIOSForIO> pump(new MessagePumpIOSForIO); + MessagePumpIOSForIO::FileDescriptorWatcher* watcher = + new MessagePumpIOSForIO::FileDescriptorWatcher; + DeleteWatcher delegate(watcher); + pump->WatchFileDescriptor(pipefds_[1], + false, MessagePumpIOSForIO::WATCH_READ_WRITE, watcher, &delegate); + + // Spoof a callback. + HandleFdIOEvent(watcher); +} + +class StopWatcher : public BaseWatcher { + public: + StopWatcher(MessagePumpIOSForIO::FileDescriptorWatcher* controller, + MessagePumpIOSForIO* pump, + int fd_to_start_watching = -1) + : BaseWatcher(controller), + pump_(pump), + fd_to_start_watching_(fd_to_start_watching) {} + + virtual ~StopWatcher() {} + + virtual void OnFileCanWriteWithoutBlocking(int /* fd */) OVERRIDE { + controller_->StopWatchingFileDescriptor(); + if (fd_to_start_watching_ >= 0) { + pump_->WatchFileDescriptor(fd_to_start_watching_, + false, MessagePumpIOSForIO::WATCH_READ_WRITE, controller_, this); + } + } + + private: + MessagePumpIOSForIO* pump_; + int fd_to_start_watching_; +}; + +TEST_F(MessagePumpIOSForIOTest, StopWatcher) { + scoped_refptr<MessagePumpIOSForIO> pump(new MessagePumpIOSForIO); + MessagePumpIOSForIO::FileDescriptorWatcher watcher; + StopWatcher delegate(&watcher, pump); + pump->WatchFileDescriptor(pipefds_[1], + false, MessagePumpIOSForIO::WATCH_READ_WRITE, &watcher, &delegate); + + // Spoof a callback. + HandleFdIOEvent(&watcher); +} + +TEST_F(MessagePumpIOSForIOTest, StopWatcherAndWatchSomethingElse) { + scoped_refptr<MessagePumpIOSForIO> pump(new MessagePumpIOSForIO); + MessagePumpIOSForIO::FileDescriptorWatcher watcher; + StopWatcher delegate(&watcher, pump, alternate_pipefds_[1]); + pump->WatchFileDescriptor(pipefds_[1], + false, MessagePumpIOSForIO::WATCH_READ_WRITE, &watcher, &delegate); + + // Spoof a callback. + HandleFdIOEvent(&watcher); +} + +} // namespace + +} // namespace base diff --git a/base/message_loop/message_pump_libevent.cc b/base/message_loop/message_pump_libevent.cc new file mode 100644 index 0000000..8de0db2 --- /dev/null +++ b/base/message_loop/message_pump_libevent.cc @@ -0,0 +1,375 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/message_loop/message_pump_libevent.h" + +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> + +#include "base/auto_reset.h" +#include "base/compiler_specific.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/observer_list.h" +#include "base/posix/eintr_wrapper.h" +#include "base/time.h" +#include "third_party/libevent/event.h" + +#if defined(OS_MACOSX) +#include "base/mac/scoped_nsautorelease_pool.h" +#endif + +// Lifecycle of struct event +// Libevent uses two main data structures: +// struct event_base (of which there is one per message pump), and +// struct event (of which there is roughly one per socket). +// The socket's struct event is created in +// MessagePumpLibevent::WatchFileDescriptor(), +// is owned by the FileDescriptorWatcher, and is destroyed in +// StopWatchingFileDescriptor(). +// It is moved into and out of lists in struct event_base by +// the libevent functions event_add() and event_del(). +// +// TODO(dkegel): +// At the moment bad things happen if a FileDescriptorWatcher +// is active after its MessagePumpLibevent has been destroyed. +// See MessageLoopTest.FileDescriptorWatcherOutlivesMessageLoop +// Not clear yet whether that situation occurs in practice, +// but if it does, we need to fix it. + +namespace base { + +// Return 0 on success +// Too small a function to bother putting in a library? +static int SetNonBlocking(int fd) { + int flags = fcntl(fd, F_GETFL, 0); + if (flags == -1) + flags = 0; + return fcntl(fd, F_SETFL, flags | O_NONBLOCK); +} + +MessagePumpLibevent::FileDescriptorWatcher::FileDescriptorWatcher() + : event_(NULL), + pump_(NULL), + watcher_(NULL), + weak_factory_(this) { +} + +MessagePumpLibevent::FileDescriptorWatcher::~FileDescriptorWatcher() { + if (event_) { + StopWatchingFileDescriptor(); + } +} + +bool MessagePumpLibevent::FileDescriptorWatcher::StopWatchingFileDescriptor() { + event* e = ReleaseEvent(); + if (e == NULL) + return true; + + // event_del() is a no-op if the event isn't active. + int rv = event_del(e); + delete e; + pump_ = NULL; + watcher_ = NULL; + return (rv == 0); +} + +void MessagePumpLibevent::FileDescriptorWatcher::Init(event *e) { + DCHECK(e); + DCHECK(!event_); + + event_ = e; +} + +event *MessagePumpLibevent::FileDescriptorWatcher::ReleaseEvent() { + struct event *e = event_; + event_ = NULL; + return e; +} + +void MessagePumpLibevent::FileDescriptorWatcher::OnFileCanReadWithoutBlocking( + int fd, MessagePumpLibevent* pump) { + // Since OnFileCanWriteWithoutBlocking() gets called first, it can stop + // watching the file descriptor. + if (!watcher_) + return; + pump->WillProcessIOEvent(); + watcher_->OnFileCanReadWithoutBlocking(fd); + pump->DidProcessIOEvent(); +} + +void MessagePumpLibevent::FileDescriptorWatcher::OnFileCanWriteWithoutBlocking( + int fd, MessagePumpLibevent* pump) { + DCHECK(watcher_); + pump->WillProcessIOEvent(); + watcher_->OnFileCanWriteWithoutBlocking(fd); + pump->DidProcessIOEvent(); +} + +MessagePumpLibevent::MessagePumpLibevent() + : keep_running_(true), + in_run_(false), + processed_io_events_(false), + event_base_(event_base_new()), + wakeup_pipe_in_(-1), + wakeup_pipe_out_(-1) { + if (!Init()) + NOTREACHED(); +} + +MessagePumpLibevent::~MessagePumpLibevent() { + DCHECK(wakeup_event_); + DCHECK(event_base_); + event_del(wakeup_event_); + delete wakeup_event_; + if (wakeup_pipe_in_ >= 0) { + if (HANDLE_EINTR(close(wakeup_pipe_in_)) < 0) + DPLOG(ERROR) << "close"; + } + if (wakeup_pipe_out_ >= 0) { + if (HANDLE_EINTR(close(wakeup_pipe_out_)) < 0) + DPLOG(ERROR) << "close"; + } + event_base_free(event_base_); +} + +bool MessagePumpLibevent::WatchFileDescriptor(int fd, + bool persistent, + int mode, + FileDescriptorWatcher *controller, + Watcher *delegate) { + DCHECK_GE(fd, 0); + DCHECK(controller); + DCHECK(delegate); + DCHECK(mode == WATCH_READ || mode == WATCH_WRITE || mode == WATCH_READ_WRITE); + // WatchFileDescriptor should be called on the pump thread. It is not + // threadsafe, and your watcher may never be registered. + DCHECK(watch_file_descriptor_caller_checker_.CalledOnValidThread()); + + int event_mask = persistent ? EV_PERSIST : 0; + if (mode & WATCH_READ) { + event_mask |= EV_READ; + } + if (mode & WATCH_WRITE) { + event_mask |= EV_WRITE; + } + + scoped_ptr<event> evt(controller->ReleaseEvent()); + if (evt.get() == NULL) { + // Ownership is transferred to the controller. + evt.reset(new event); + } else { + // Make sure we don't pick up any funky internal libevent masks. + int old_interest_mask = evt.get()->ev_events & + (EV_READ | EV_WRITE | EV_PERSIST); + + // Combine old/new event masks. + event_mask |= old_interest_mask; + + // Must disarm the event before we can reuse it. + event_del(evt.get()); + + // It's illegal to use this function to listen on 2 separate fds with the + // same |controller|. + if (EVENT_FD(evt.get()) != fd) { + NOTREACHED() << "FDs don't match" << EVENT_FD(evt.get()) << "!=" << fd; + return false; + } + } + + // Set current interest mask and message pump for this event. + event_set(evt.get(), fd, event_mask, OnLibeventNotification, controller); + + // Tell libevent which message pump this socket will belong to when we add it. + if (event_base_set(event_base_, evt.get())) { + return false; + } + + // Add this socket to the list of monitored sockets. + if (event_add(evt.get(), NULL)) { + return false; + } + + // Transfer ownership of evt to controller. + controller->Init(evt.release()); + + controller->set_watcher(delegate); + controller->set_pump(this); + + return true; +} + +void MessagePumpLibevent::AddIOObserver(IOObserver *obs) { + io_observers_.AddObserver(obs); +} + +void MessagePumpLibevent::RemoveIOObserver(IOObserver *obs) { + io_observers_.RemoveObserver(obs); +} + +// Tell libevent to break out of inner loop. +static void timer_callback(int fd, short events, void *context) +{ + event_base_loopbreak((struct event_base *)context); +} + +// Reentrant! +void MessagePumpLibevent::Run(Delegate* delegate) { + DCHECK(keep_running_) << "Quit must have been called outside of Run!"; + AutoReset<bool> auto_reset_in_run(&in_run_, true); + + // event_base_loopexit() + EVLOOP_ONCE is leaky, see http://crbug.com/25641. + // Instead, make our own timer and reuse it on each call to event_base_loop(). + scoped_ptr<event> timer_event(new event); + + for (;;) { +#if defined(OS_MACOSX) + mac::ScopedNSAutoreleasePool autorelease_pool; +#endif + + bool did_work = delegate->DoWork(); + if (!keep_running_) + break; + + event_base_loop(event_base_, EVLOOP_NONBLOCK); + did_work |= processed_io_events_; + processed_io_events_ = false; + if (!keep_running_) + break; + + did_work |= delegate->DoDelayedWork(&delayed_work_time_); + if (!keep_running_) + break; + + if (did_work) + continue; + + did_work = delegate->DoIdleWork(); + if (!keep_running_) + break; + + if (did_work) + continue; + + // EVLOOP_ONCE tells libevent to only block once, + // but to service all pending events when it wakes up. + if (delayed_work_time_.is_null()) { + event_base_loop(event_base_, EVLOOP_ONCE); + } else { + TimeDelta delay = delayed_work_time_ - TimeTicks::Now(); + if (delay > TimeDelta()) { + struct timeval poll_tv; + poll_tv.tv_sec = delay.InSeconds(); + poll_tv.tv_usec = delay.InMicroseconds() % Time::kMicrosecondsPerSecond; + event_set(timer_event.get(), -1, 0, timer_callback, event_base_); + event_base_set(event_base_, timer_event.get()); + event_add(timer_event.get(), &poll_tv); + event_base_loop(event_base_, EVLOOP_ONCE); + event_del(timer_event.get()); + } else { + // It looks like delayed_work_time_ indicates a time in the past, so we + // need to call DoDelayedWork now. + delayed_work_time_ = TimeTicks(); + } + } + } + + keep_running_ = true; +} + +void MessagePumpLibevent::Quit() { + DCHECK(in_run_); + // Tell both libevent and Run that they should break out of their loops. + keep_running_ = false; + ScheduleWork(); +} + +void MessagePumpLibevent::ScheduleWork() { + // Tell libevent (in a threadsafe way) that it should break out of its loop. + char buf = 0; + int nwrite = HANDLE_EINTR(write(wakeup_pipe_in_, &buf, 1)); + DCHECK(nwrite == 1 || errno == EAGAIN) + << "[nwrite:" << nwrite << "] [errno:" << errno << "]"; +} + +void MessagePumpLibevent::ScheduleDelayedWork( + const TimeTicks& delayed_work_time) { + // We know that we can't be blocked on Wait right now since this method can + // only be called on the same thread as Run, so we only need to update our + // record of how long to sleep when we do sleep. + delayed_work_time_ = delayed_work_time; +} + +void MessagePumpLibevent::WillProcessIOEvent() { + FOR_EACH_OBSERVER(IOObserver, io_observers_, WillProcessIOEvent()); +} + +void MessagePumpLibevent::DidProcessIOEvent() { + FOR_EACH_OBSERVER(IOObserver, io_observers_, DidProcessIOEvent()); +} + +bool MessagePumpLibevent::Init() { + int fds[2]; + if (pipe(fds)) { + DLOG(ERROR) << "pipe() failed, errno: " << errno; + return false; + } + if (SetNonBlocking(fds[0])) { + DLOG(ERROR) << "SetNonBlocking for pipe fd[0] failed, errno: " << errno; + return false; + } + if (SetNonBlocking(fds[1])) { + DLOG(ERROR) << "SetNonBlocking for pipe fd[1] failed, errno: " << errno; + return false; + } + wakeup_pipe_out_ = fds[0]; + wakeup_pipe_in_ = fds[1]; + + wakeup_event_ = new event; + event_set(wakeup_event_, wakeup_pipe_out_, EV_READ | EV_PERSIST, + OnWakeup, this); + event_base_set(event_base_, wakeup_event_); + + if (event_add(wakeup_event_, 0)) + return false; + return true; +} + +// static +void MessagePumpLibevent::OnLibeventNotification(int fd, short flags, + void* context) { + WeakPtr<FileDescriptorWatcher> controller = + static_cast<FileDescriptorWatcher*>(context)->weak_factory_.GetWeakPtr(); + DCHECK(controller.get()); + + MessagePumpLibevent* pump = controller->pump(); + pump->processed_io_events_ = true; + + if (flags & EV_WRITE) { + controller->OnFileCanWriteWithoutBlocking(fd, pump); + } + // Check |controller| in case it's been deleted in + // controller->OnFileCanWriteWithoutBlocking(). + if (controller.get() && flags & EV_READ) { + controller->OnFileCanReadWithoutBlocking(fd, pump); + } +} + +// Called if a byte is received on the wakeup pipe. +// static +void MessagePumpLibevent::OnWakeup(int socket, short flags, void* context) { + MessagePumpLibevent* that = static_cast<MessagePumpLibevent*>(context); + DCHECK(that->wakeup_pipe_out_ == socket); + + // Remove and discard the wakeup byte. + char buf; + int nread = HANDLE_EINTR(read(socket, &buf, 1)); + DCHECK_EQ(nread, 1); + that->processed_io_events_ = true; + // Tell libevent to break out of inner loop. + event_base_loopbreak(that->event_base_); +} + +} // namespace base diff --git a/base/message_loop/message_pump_libevent.h b/base/message_loop/message_pump_libevent.h new file mode 100644 index 0000000..a54ef88 --- /dev/null +++ b/base/message_loop/message_pump_libevent.h @@ -0,0 +1,179 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MESSAGE_LOOP_MESSAGE_PUMP_LIBEVENT_H_ +#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_LIBEVENT_H_ + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/memory/weak_ptr.h" +#include "base/message_loop/message_pump.h" +#include "base/observer_list.h" +#include "base/threading/thread_checker.h" +#include "base/time.h" + +// Declare structs we need from libevent.h rather than including it +struct event_base; +struct event; + +namespace base { + +// Class to monitor sockets and issue callbacks when sockets are ready for I/O +// TODO(dkegel): add support for background file IO somehow +class BASE_EXPORT MessagePumpLibevent : public MessagePump { + public: + class IOObserver { + public: + IOObserver() {} + + // An IOObserver is an object that receives IO notifications from the + // MessagePump. + // + // NOTE: An IOObserver implementation should be extremely fast! + virtual void WillProcessIOEvent() = 0; + virtual void DidProcessIOEvent() = 0; + + protected: + virtual ~IOObserver() {} + }; + + // Used with WatchFileDescriptor to asynchronously monitor the I/O readiness + // of a file descriptor. + class Watcher { + public: + // Called from MessageLoop::Run when an FD can be read from/written to + // without blocking + virtual void OnFileCanReadWithoutBlocking(int fd) = 0; + virtual void OnFileCanWriteWithoutBlocking(int fd) = 0; + + protected: + virtual ~Watcher() {} + }; + + // Object returned by WatchFileDescriptor to manage further watching. + class FileDescriptorWatcher { + public: + FileDescriptorWatcher(); + ~FileDescriptorWatcher(); // Implicitly calls StopWatchingFileDescriptor. + + // NOTE: These methods aren't called StartWatching()/StopWatching() to + // avoid confusion with the win32 ObjectWatcher class. + + // Stop watching the FD, always safe to call. No-op if there's nothing + // to do. + bool StopWatchingFileDescriptor(); + + private: + friend class MessagePumpLibevent; + friend class MessagePumpLibeventTest; + + // Called by MessagePumpLibevent, ownership of |e| is transferred to this + // object. + void Init(event* e); + + // Used by MessagePumpLibevent to take ownership of event_. + event* ReleaseEvent(); + + void set_pump(MessagePumpLibevent* pump) { pump_ = pump; } + MessagePumpLibevent* pump() const { return pump_; } + + void set_watcher(Watcher* watcher) { watcher_ = watcher; } + + void OnFileCanReadWithoutBlocking(int fd, MessagePumpLibevent* pump); + void OnFileCanWriteWithoutBlocking(int fd, MessagePumpLibevent* pump); + + event* event_; + MessagePumpLibevent* pump_; + Watcher* watcher_; + WeakPtrFactory<FileDescriptorWatcher> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(FileDescriptorWatcher); + }; + + enum Mode { + WATCH_READ = 1 << 0, + WATCH_WRITE = 1 << 1, + WATCH_READ_WRITE = WATCH_READ | WATCH_WRITE + }; + + MessagePumpLibevent(); + + // Have the current thread's message loop watch for a a situation in which + // reading/writing to the FD can be performed without blocking. + // Callers must provide a preallocated FileDescriptorWatcher object which + // can later be used to manage the lifetime of this event. + // If a FileDescriptorWatcher is passed in which is already attached to + // an event, then the effect is cumulative i.e. after the call |controller| + // will watch both the previous event and the new one. + // If an error occurs while calling this method in a cumulative fashion, the + // event previously attached to |controller| is aborted. + // Returns true on success. + // Must be called on the same thread the message_pump is running on. + // TODO(dkegel): switch to edge-triggered readiness notification + bool WatchFileDescriptor(int fd, + bool persistent, + int mode, + FileDescriptorWatcher *controller, + Watcher *delegate); + + void AddIOObserver(IOObserver* obs); + void RemoveIOObserver(IOObserver* obs); + + // MessagePump methods: + virtual void Run(Delegate* delegate) OVERRIDE; + virtual void Quit() OVERRIDE; + virtual void ScheduleWork() OVERRIDE; + virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time) OVERRIDE; + + protected: + virtual ~MessagePumpLibevent(); + + private: + friend class MessagePumpLibeventTest; + + void WillProcessIOEvent(); + void DidProcessIOEvent(); + + // Risky part of constructor. Returns true on success. + bool Init(); + + // Called by libevent to tell us a registered FD can be read/written to. + static void OnLibeventNotification(int fd, short flags, + void* context); + + // Unix pipe used to implement ScheduleWork() + // ... callback; called by libevent inside Run() when pipe is ready to read + static void OnWakeup(int socket, short flags, void* context); + + // This flag is set to false when Run should return. + bool keep_running_; + + // This flag is set when inside Run. + bool in_run_; + + // This flag is set if libevent has processed I/O events. + bool processed_io_events_; + + // The time at which we should call DoDelayedWork. + TimeTicks delayed_work_time_; + + // Libevent dispatcher. Watches all sockets registered with it, and sends + // readiness callbacks when a socket is ready for I/O. + event_base* event_base_; + + // ... write end; ScheduleWork() writes a single byte to it + int wakeup_pipe_in_; + // ... read end; OnWakeup reads it and then breaks Run() out of its sleep + int wakeup_pipe_out_; + // ... libevent wrapper for read end + event* wakeup_event_; + + ObserverList<IOObserver> io_observers_; + ThreadChecker watch_file_descriptor_caller_checker_; + DISALLOW_COPY_AND_ASSIGN(MessagePumpLibevent); +}; + +} // namespace base + +#endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_LIBEVENT_H_ diff --git a/base/message_loop/message_pump_libevent_unittest.cc b/base/message_loop/message_pump_libevent_unittest.cc new file mode 100644 index 0000000..657ac7d --- /dev/null +++ b/base/message_loop/message_pump_libevent_unittest.cc @@ -0,0 +1,162 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/message_loop/message_pump_libevent.h" + +#include <unistd.h> + +#include "base/message_loop/message_loop.h" +#include "base/posix/eintr_wrapper.h" +#include "base/threading/thread.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/libevent/event.h" + +namespace base { + +class MessagePumpLibeventTest : public testing::Test { + protected: + MessagePumpLibeventTest() + : ui_loop_(MessageLoop::TYPE_UI), + io_thread_("MessagePumpLibeventTestIOThread") {} + virtual ~MessagePumpLibeventTest() {} + + virtual void SetUp() OVERRIDE { + Thread::Options options(MessageLoop::TYPE_IO, 0); + ASSERT_TRUE(io_thread_.StartWithOptions(options)); + ASSERT_EQ(MessageLoop::TYPE_IO, io_thread_.message_loop()->type()); + int ret = pipe(pipefds_); + ASSERT_EQ(0, ret); + } + + virtual void TearDown() OVERRIDE { + if (HANDLE_EINTR(close(pipefds_[0])) < 0) + PLOG(ERROR) << "close"; + if (HANDLE_EINTR(close(pipefds_[1])) < 0) + PLOG(ERROR) << "close"; + } + + MessageLoop* ui_loop() { return &ui_loop_; } + MessageLoopForIO* io_loop() const { + return static_cast<MessageLoopForIO*>(io_thread_.message_loop()); + } + + void OnLibeventNotification( + MessagePumpLibevent* pump, + MessagePumpLibevent::FileDescriptorWatcher* controller) { + pump->OnLibeventNotification(0, EV_WRITE | EV_READ, controller); + } + + int pipefds_[2]; + + private: + MessageLoop ui_loop_; + Thread io_thread_; +}; + +namespace { + +// Concrete implementation of MessagePumpLibevent::Watcher that does +// nothing useful. +class StupidWatcher : public MessagePumpLibevent::Watcher { + public: + virtual ~StupidWatcher() {} + + // base:MessagePumpLibevent::Watcher interface + virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE {} + virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE {} +}; + +#if GTEST_HAS_DEATH_TEST && !defined(NDEBUG) + +// Test to make sure that we catch calling WatchFileDescriptor off of the +// wrong thread. +TEST_F(MessagePumpLibeventTest, TestWatchingFromBadThread) { + MessagePumpLibevent::FileDescriptorWatcher watcher; + StupidWatcher delegate; + + ASSERT_DEATH(io_loop()->WatchFileDescriptor( + STDOUT_FILENO, false, MessageLoopForIO::WATCH_READ, &watcher, &delegate), + "Check failed: " + "watch_file_descriptor_caller_checker_.CalledOnValidThread()"); +} + +#endif // GTEST_HAS_DEATH_TEST && !defined(NDEBUG) + +class BaseWatcher : public MessagePumpLibevent::Watcher { + public: + explicit BaseWatcher(MessagePumpLibevent::FileDescriptorWatcher* controller) + : controller_(controller) { + DCHECK(controller_); + } + virtual ~BaseWatcher() {} + + // base:MessagePumpLibevent::Watcher interface + virtual void OnFileCanReadWithoutBlocking(int /* fd */) OVERRIDE { + NOTREACHED(); + } + + virtual void OnFileCanWriteWithoutBlocking(int /* fd */) OVERRIDE { + NOTREACHED(); + } + + protected: + MessagePumpLibevent::FileDescriptorWatcher* controller_; +}; + +class DeleteWatcher : public BaseWatcher { + public: + explicit DeleteWatcher( + MessagePumpLibevent::FileDescriptorWatcher* controller) + : BaseWatcher(controller) {} + + virtual ~DeleteWatcher() { + DCHECK(!controller_); + } + + virtual void OnFileCanWriteWithoutBlocking(int /* fd */) OVERRIDE { + DCHECK(controller_); + delete controller_; + controller_ = NULL; + } +}; + +TEST_F(MessagePumpLibeventTest, DeleteWatcher) { + scoped_refptr<MessagePumpLibevent> pump(new MessagePumpLibevent); + MessagePumpLibevent::FileDescriptorWatcher* watcher = + new MessagePumpLibevent::FileDescriptorWatcher; + DeleteWatcher delegate(watcher); + pump->WatchFileDescriptor(pipefds_[1], + false, MessagePumpLibevent::WATCH_READ_WRITE, watcher, &delegate); + + // Spoof a libevent notification. + OnLibeventNotification(pump.get(), watcher); +} + +class StopWatcher : public BaseWatcher { + public: + explicit StopWatcher( + MessagePumpLibevent::FileDescriptorWatcher* controller) + : BaseWatcher(controller) {} + + virtual ~StopWatcher() {} + + virtual void OnFileCanWriteWithoutBlocking(int /* fd */) OVERRIDE { + controller_->StopWatchingFileDescriptor(); + } +}; + +TEST_F(MessagePumpLibeventTest, StopWatcher) { + scoped_refptr<MessagePumpLibevent> pump(new MessagePumpLibevent); + MessagePumpLibevent::FileDescriptorWatcher watcher; + StopWatcher delegate(&watcher); + pump->WatchFileDescriptor(pipefds_[1], + false, MessagePumpLibevent::WATCH_READ_WRITE, &watcher, &delegate); + + // Spoof a libevent notification. + OnLibeventNotification(pump.get(), &watcher); +} + +} // namespace + +} // namespace base diff --git a/base/message_loop/message_pump_mac.h b/base/message_loop/message_pump_mac.h new file mode 100644 index 0000000..68c2a3d --- /dev/null +++ b/base/message_loop/message_pump_mac.h @@ -0,0 +1,337 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// The basis for all native run loops on the Mac is the CFRunLoop. It can be +// used directly, it can be used as the driving force behind the similar +// Foundation NSRunLoop, and it can be used to implement higher-level event +// loops such as the NSApplication event loop. +// +// This file introduces a basic CFRunLoop-based implementation of the +// MessagePump interface called CFRunLoopBase. CFRunLoopBase contains all +// of the machinery necessary to dispatch events to a delegate, but does not +// implement the specific run loop. Concrete subclasses must provide their +// own DoRun and Quit implementations. +// +// A concrete subclass that just runs a CFRunLoop loop is provided in +// MessagePumpCFRunLoop. For an NSRunLoop, the similar MessagePumpNSRunLoop +// is provided. +// +// For the application's event loop, an implementation based on AppKit's +// NSApplication event system is provided in MessagePumpNSApplication. +// +// Typically, MessagePumpNSApplication only makes sense on a Cocoa +// application's main thread. If a CFRunLoop-based message pump is needed on +// any other thread, one of the other concrete subclasses is preferrable. +// MessagePumpMac::Create is defined, which returns a new NSApplication-based +// or NSRunLoop-based MessagePump subclass depending on which thread it is +// called on. + +#ifndef BASE_MESSAGE_LOOP_MESSAGE_PUMP_MAC_H_ +#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_MAC_H_ + +#include "base/message_loop/message_pump.h" + +#include <CoreFoundation/CoreFoundation.h> + +#if !defined(__OBJC__) +class NSAutoreleasePool; +#else // !defined(__OBJC__) +#if defined(OS_IOS) +#import <Foundation/Foundation.h> +#else +#import <AppKit/AppKit.h> + +// Clients must subclass NSApplication and implement this protocol if they use +// MessagePumpMac. +@protocol CrAppProtocol +// Must return true if -[NSApplication sendEvent:] is currently on the stack. +// See the comment for |CreateAutoreleasePool()| in the cc file for why this is +// necessary. +- (BOOL)isHandlingSendEvent; +@end +#endif // !defined(OS_IOS) +#endif // !defined(__OBJC__) + +namespace base { + +class RunLoop; +class TimeTicks; + +class MessagePumpCFRunLoopBase : public MessagePump { + // Needs access to CreateAutoreleasePool. + friend class MessagePumpScopedAutoreleasePool; + public: + MessagePumpCFRunLoopBase(); + + // Subclasses should implement the work they need to do in MessagePump::Run + // in the DoRun method. MessagePumpCFRunLoopBase::Run calls DoRun directly. + // This arrangement is used because MessagePumpCFRunLoopBase needs to set + // up and tear down things before and after the "meat" of DoRun. + virtual void Run(Delegate* delegate) OVERRIDE; + virtual void DoRun(Delegate* delegate) = 0; + + virtual void ScheduleWork() OVERRIDE; + virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time) OVERRIDE; + + protected: + virtual ~MessagePumpCFRunLoopBase(); + + // Accessors for private data members to be used by subclasses. + CFRunLoopRef run_loop() const { return run_loop_; } + int nesting_level() const { return nesting_level_; } + int run_nesting_level() const { return run_nesting_level_; } + + // Sets this pump's delegate. Signals the appropriate sources if + // |delegateless_work_| is true. |delegate| can be NULL. + void SetDelegate(Delegate* delegate); + + // Return an autorelease pool to wrap around any work being performed. + // In some cases, CreateAutoreleasePool may return nil intentionally to + // preventing an autorelease pool from being created, allowing any + // objects autoreleased by work to fall into the current autorelease pool. + virtual NSAutoreleasePool* CreateAutoreleasePool(); + + private: + // Timer callback scheduled by ScheduleDelayedWork. This does not do any + // work, but it signals work_source_ so that delayed work can be performed + // within the appropriate priority constraints. + static void RunDelayedWorkTimer(CFRunLoopTimerRef timer, void* info); + + // Perform highest-priority work. This is associated with work_source_ + // signalled by ScheduleWork or RunDelayedWorkTimer. The static method calls + // the instance method; the instance method returns true if it resignalled + // work_source_ to be called again from the loop. + static void RunWorkSource(void* info); + bool RunWork(); + + // Perform idle-priority work. This is normally called by PreWaitObserver, + // but is also associated with idle_work_source_. When this function + // actually does perform idle work, it will resignal that source. The + // static method calls the instance method; the instance method returns + // true if idle work was done. + static void RunIdleWorkSource(void* info); + bool RunIdleWork(); + + // Perform work that may have been deferred because it was not runnable + // within a nested run loop. This is associated with + // nesting_deferred_work_source_ and is signalled by + // MaybeScheduleNestingDeferredWork when returning from a nested loop, + // so that an outer loop will be able to perform the necessary tasks if it + // permits nestable tasks. + static void RunNestingDeferredWorkSource(void* info); + bool RunNestingDeferredWork(); + + // Schedules possible nesting-deferred work to be processed before the run + // loop goes to sleep, exits, or begins processing sources at the top of its + // loop. If this function detects that a nested loop had run since the + // previous attempt to schedule nesting-deferred work, it will schedule a + // call to RunNestingDeferredWorkSource. + void MaybeScheduleNestingDeferredWork(); + + // Observer callback responsible for performing idle-priority work, before + // the run loop goes to sleep. Associated with idle_work_observer_. + static void PreWaitObserver(CFRunLoopObserverRef observer, + CFRunLoopActivity activity, void* info); + + // Observer callback called before the run loop processes any sources. + // Associated with pre_source_observer_. + static void PreSourceObserver(CFRunLoopObserverRef observer, + CFRunLoopActivity activity, void* info); + + // Observer callback called when the run loop starts and stops, at the + // beginning and end of calls to CFRunLoopRun. This is used to maintain + // nesting_level_. Associated with enter_exit_observer_. + static void EnterExitObserver(CFRunLoopObserverRef observer, + CFRunLoopActivity activity, void* info); + + // Called by EnterExitObserver after performing maintenance on nesting_level_. + // This allows subclasses an opportunity to perform additional processing on + // the basis of run loops starting and stopping. + virtual void EnterExitRunLoop(CFRunLoopActivity activity); + + // The thread's run loop. + CFRunLoopRef run_loop_; + + // The timer, sources, and observers are described above alongside their + // callbacks. + CFRunLoopTimerRef delayed_work_timer_; + CFRunLoopSourceRef work_source_; + CFRunLoopSourceRef idle_work_source_; + CFRunLoopSourceRef nesting_deferred_work_source_; + CFRunLoopObserverRef pre_wait_observer_; + CFRunLoopObserverRef pre_source_observer_; + CFRunLoopObserverRef enter_exit_observer_; + + // (weak) Delegate passed as an argument to the innermost Run call. + Delegate* delegate_; + + // The time that delayed_work_timer_ is scheduled to fire. This is tracked + // independently of CFRunLoopTimerGetNextFireDate(delayed_work_timer_) + // to be able to reset the timer properly after waking from system sleep. + // See PowerStateNotification. + CFAbsoluteTime delayed_work_fire_time_; + + // The recursion depth of the currently-executing CFRunLoopRun loop on the + // run loop's thread. 0 if no run loops are running inside of whatever scope + // the object was created in. + int nesting_level_; + + // The recursion depth (calculated in the same way as nesting_level_) of the + // innermost executing CFRunLoopRun loop started by a call to Run. + int run_nesting_level_; + + // The deepest (numerically highest) recursion depth encountered since the + // most recent attempt to run nesting-deferred work. + int deepest_nesting_level_; + + // "Delegateless" work flags are set when work is ready to be performed but + // must wait until a delegate is available to process it. This can happen + // when a MessagePumpCFRunLoopBase is instantiated and work arrives without + // any call to Run on the stack. The Run method will check for delegateless + // work on entry and redispatch it as needed once a delegate is available. + bool delegateless_work_; + bool delegateless_idle_work_; + + DISALLOW_COPY_AND_ASSIGN(MessagePumpCFRunLoopBase); +}; + +class MessagePumpCFRunLoop : public MessagePumpCFRunLoopBase { + public: + MessagePumpCFRunLoop(); + + virtual void DoRun(Delegate* delegate) OVERRIDE; + virtual void Quit() OVERRIDE; + + protected: + virtual ~MessagePumpCFRunLoop(); + + private: + virtual void EnterExitRunLoop(CFRunLoopActivity activity) OVERRIDE; + + // True if Quit is called to stop the innermost MessagePump + // (innermost_quittable_) but some other CFRunLoopRun loop (nesting_level_) + // is running inside the MessagePump's innermost Run call. + bool quit_pending_; + + DISALLOW_COPY_AND_ASSIGN(MessagePumpCFRunLoop); +}; + +class MessagePumpNSRunLoop : public MessagePumpCFRunLoopBase { + public: + BASE_EXPORT MessagePumpNSRunLoop(); + + virtual void DoRun(Delegate* delegate) OVERRIDE; + virtual void Quit() OVERRIDE; + + protected: + virtual ~MessagePumpNSRunLoop(); + + private: + // A source that doesn't do anything but provide something signalable + // attached to the run loop. This source will be signalled when Quit + // is called, to cause the loop to wake up so that it can stop. + CFRunLoopSourceRef quit_source_; + + // False after Quit is called. + bool keep_running_; + + DISALLOW_COPY_AND_ASSIGN(MessagePumpNSRunLoop); +}; + +#if defined(OS_IOS) +// This is a fake message pump. It attaches sources to the main thread's +// CFRunLoop, so PostTask() will work, but it is unable to drive the loop +// directly, so calling Run() or Quit() are errors. +class MessagePumpUIApplication : public MessagePumpCFRunLoopBase { + public: + MessagePumpUIApplication(); + virtual void DoRun(Delegate* delegate) OVERRIDE; + virtual void Quit() OVERRIDE; + + // This message pump can not spin the main message loop directly. Instead, + // call |Attach()| to set up a delegate. It is an error to call |Run()|. + virtual void Attach(Delegate* delegate); + + protected: + virtual ~MessagePumpUIApplication(); + + private: + RunLoop* run_loop_; + + DISALLOW_COPY_AND_ASSIGN(MessagePumpUIApplication); +}; + +#else + +class MessagePumpNSApplication : public MessagePumpCFRunLoopBase { + public: + MessagePumpNSApplication(); + + virtual void DoRun(Delegate* delegate) OVERRIDE; + virtual void Quit() OVERRIDE; + + protected: + virtual ~MessagePumpNSApplication(); + + private: + // False after Quit is called. + bool keep_running_; + + // True if DoRun is managing its own run loop as opposed to letting + // -[NSApplication run] handle it. The outermost run loop in the application + // is managed by -[NSApplication run], inner run loops are handled by a loop + // in DoRun. + bool running_own_loop_; + + DISALLOW_COPY_AND_ASSIGN(MessagePumpNSApplication); +}; + +class MessagePumpCrApplication : public MessagePumpNSApplication { + public: + MessagePumpCrApplication(); + + protected: + virtual ~MessagePumpCrApplication() {} + + // Returns nil if NSApp is currently in the middle of calling + // -sendEvent. Requires NSApp implementing CrAppProtocol. + virtual NSAutoreleasePool* CreateAutoreleasePool() OVERRIDE; + + private: + DISALLOW_COPY_AND_ASSIGN(MessagePumpCrApplication); +}; +#endif // !defined(OS_IOS) + +class MessagePumpMac { + public: + // If not on the main thread, returns a new instance of + // MessagePumpNSRunLoop. + // + // On the main thread, if NSApp exists and conforms to + // CrAppProtocol, creates an instances of MessagePumpCrApplication. + // + // Otherwise creates an instance of MessagePumpNSApplication using a + // default NSApplication. + static MessagePump* Create(); + +#if !defined(OS_IOS) + // If a pump is created before the required CrAppProtocol is + // created, the wrong MessagePump subclass could be used. + // UsingCrApp() returns false if the message pump was created before + // NSApp was initialized, or if NSApp does not implement + // CrAppProtocol. NSApp must be initialized before calling. + BASE_EXPORT static bool UsingCrApp(); + + // Wrapper to query -[NSApp isHandlingSendEvent] from C++ code. + // Requires NSApp to implement CrAppProtocol. + BASE_EXPORT static bool IsHandlingSendEvent(); +#endif // !defined(OS_IOS) + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(MessagePumpMac); +}; + +} // namespace base + +#endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_MAC_H_ diff --git a/base/message_loop/message_pump_mac.mm b/base/message_loop/message_pump_mac.mm new file mode 100644 index 0000000..a419d48 --- /dev/null +++ b/base/message_loop/message_pump_mac.mm @@ -0,0 +1,701 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "base/message_loop/message_pump_mac.h" + +#import <Foundation/Foundation.h> + +#include <limits> + +#include "base/logging.h" +#include "base/run_loop.h" +#include "base/time.h" + +#if !defined(OS_IOS) +#import <AppKit/AppKit.h> +#endif // !defined(OS_IOS) + +namespace { + +void NoOp(void* info) { +} + +const CFTimeInterval kCFTimeIntervalMax = + std::numeric_limits<CFTimeInterval>::max(); + +#if !defined(OS_IOS) +// Set to true if MessagePumpMac::Create() is called before NSApp is +// initialized. Only accessed from the main thread. +bool g_not_using_cr_app = false; +#endif + +} // namespace + +namespace base { + +// A scoper for autorelease pools created from message pump run loops. +// Avoids dirtying up the ScopedNSAutoreleasePool interface for the rare +// case where an autorelease pool needs to be passed in. +class MessagePumpScopedAutoreleasePool { + public: + explicit MessagePumpScopedAutoreleasePool(MessagePumpCFRunLoopBase* pump) : + pool_(pump->CreateAutoreleasePool()) { + } + ~MessagePumpScopedAutoreleasePool() { + [pool_ drain]; + } + + private: + NSAutoreleasePool* pool_; + DISALLOW_COPY_AND_ASSIGN(MessagePumpScopedAutoreleasePool); +}; + +// Must be called on the run loop thread. +MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase() + : delegate_(NULL), + delayed_work_fire_time_(kCFTimeIntervalMax), + nesting_level_(0), + run_nesting_level_(0), + deepest_nesting_level_(0), + delegateless_work_(false), + delegateless_idle_work_(false) { + run_loop_ = CFRunLoopGetCurrent(); + CFRetain(run_loop_); + + // Set a repeating timer with a preposterous firing time and interval. The + // timer will effectively never fire as-is. The firing time will be adjusted + // as needed when ScheduleDelayedWork is called. + CFRunLoopTimerContext timer_context = CFRunLoopTimerContext(); + timer_context.info = this; + delayed_work_timer_ = CFRunLoopTimerCreate(NULL, // allocator + kCFTimeIntervalMax, // fire time + kCFTimeIntervalMax, // interval + 0, // flags + 0, // priority + RunDelayedWorkTimer, + &timer_context); + CFRunLoopAddTimer(run_loop_, delayed_work_timer_, kCFRunLoopCommonModes); + + CFRunLoopSourceContext source_context = CFRunLoopSourceContext(); + source_context.info = this; + source_context.perform = RunWorkSource; + work_source_ = CFRunLoopSourceCreate(NULL, // allocator + 1, // priority + &source_context); + CFRunLoopAddSource(run_loop_, work_source_, kCFRunLoopCommonModes); + + source_context.perform = RunIdleWorkSource; + idle_work_source_ = CFRunLoopSourceCreate(NULL, // allocator + 2, // priority + &source_context); + CFRunLoopAddSource(run_loop_, idle_work_source_, kCFRunLoopCommonModes); + + source_context.perform = RunNestingDeferredWorkSource; + nesting_deferred_work_source_ = CFRunLoopSourceCreate(NULL, // allocator + 0, // priority + &source_context); + CFRunLoopAddSource(run_loop_, nesting_deferred_work_source_, + kCFRunLoopCommonModes); + + CFRunLoopObserverContext observer_context = CFRunLoopObserverContext(); + observer_context.info = this; + pre_wait_observer_ = CFRunLoopObserverCreate(NULL, // allocator + kCFRunLoopBeforeWaiting, + true, // repeat + 0, // priority + PreWaitObserver, + &observer_context); + CFRunLoopAddObserver(run_loop_, pre_wait_observer_, kCFRunLoopCommonModes); + + pre_source_observer_ = CFRunLoopObserverCreate(NULL, // allocator + kCFRunLoopBeforeSources, + true, // repeat + 0, // priority + PreSourceObserver, + &observer_context); + CFRunLoopAddObserver(run_loop_, pre_source_observer_, kCFRunLoopCommonModes); + + enter_exit_observer_ = CFRunLoopObserverCreate(NULL, // allocator + kCFRunLoopEntry | + kCFRunLoopExit, + true, // repeat + 0, // priority + EnterExitObserver, + &observer_context); + CFRunLoopAddObserver(run_loop_, enter_exit_observer_, kCFRunLoopCommonModes); +} + +// Ideally called on the run loop thread. If other run loops were running +// lower on the run loop thread's stack when this object was created, the +// same number of run loops must be running when this object is destroyed. +MessagePumpCFRunLoopBase::~MessagePumpCFRunLoopBase() { + CFRunLoopRemoveObserver(run_loop_, enter_exit_observer_, + kCFRunLoopCommonModes); + CFRelease(enter_exit_observer_); + + CFRunLoopRemoveObserver(run_loop_, pre_source_observer_, + kCFRunLoopCommonModes); + CFRelease(pre_source_observer_); + + CFRunLoopRemoveObserver(run_loop_, pre_wait_observer_, + kCFRunLoopCommonModes); + CFRelease(pre_wait_observer_); + + CFRunLoopRemoveSource(run_loop_, nesting_deferred_work_source_, + kCFRunLoopCommonModes); + CFRelease(nesting_deferred_work_source_); + + CFRunLoopRemoveSource(run_loop_, idle_work_source_, kCFRunLoopCommonModes); + CFRelease(idle_work_source_); + + CFRunLoopRemoveSource(run_loop_, work_source_, kCFRunLoopCommonModes); + CFRelease(work_source_); + + CFRunLoopRemoveTimer(run_loop_, delayed_work_timer_, kCFRunLoopCommonModes); + CFRelease(delayed_work_timer_); + + CFRelease(run_loop_); +} + +// Must be called on the run loop thread. +void MessagePumpCFRunLoopBase::Run(Delegate* delegate) { + // nesting_level_ will be incremented in EnterExitRunLoop, so set + // run_nesting_level_ accordingly. + int last_run_nesting_level = run_nesting_level_; + run_nesting_level_ = nesting_level_ + 1; + + Delegate* last_delegate = delegate_; + SetDelegate(delegate); + + DoRun(delegate); + + // Restore the previous state of the object. + SetDelegate(last_delegate); + run_nesting_level_ = last_run_nesting_level; +} + +void MessagePumpCFRunLoopBase::SetDelegate(Delegate* delegate) { + delegate_ = delegate; + + if (delegate) { + // If any work showed up but could not be dispatched for want of a + // delegate, set it up for dispatch again now that a delegate is + // available. + if (delegateless_work_) { + CFRunLoopSourceSignal(work_source_); + delegateless_work_ = false; + } + if (delegateless_idle_work_) { + CFRunLoopSourceSignal(idle_work_source_); + delegateless_idle_work_ = false; + } + } +} + +// May be called on any thread. +void MessagePumpCFRunLoopBase::ScheduleWork() { + CFRunLoopSourceSignal(work_source_); + CFRunLoopWakeUp(run_loop_); +} + +// Must be called on the run loop thread. +void MessagePumpCFRunLoopBase::ScheduleDelayedWork( + const TimeTicks& delayed_work_time) { + TimeDelta delta = delayed_work_time - TimeTicks::Now(); + delayed_work_fire_time_ = CFAbsoluteTimeGetCurrent() + delta.InSecondsF(); + CFRunLoopTimerSetNextFireDate(delayed_work_timer_, delayed_work_fire_time_); +} + +// Called from the run loop. +// static +void MessagePumpCFRunLoopBase::RunDelayedWorkTimer(CFRunLoopTimerRef timer, + void* info) { + MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); + + // The timer won't fire again until it's reset. + self->delayed_work_fire_time_ = kCFTimeIntervalMax; + + // CFRunLoopTimers fire outside of the priority scheme for CFRunLoopSources. + // In order to establish the proper priority in which work and delayed work + // are processed one for one, the timer used to schedule delayed work must + // signal a CFRunLoopSource used to dispatch both work and delayed work. + CFRunLoopSourceSignal(self->work_source_); +} + +// Called from the run loop. +// static +void MessagePumpCFRunLoopBase::RunWorkSource(void* info) { + MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); + self->RunWork(); +} + +// Called by MessagePumpCFRunLoopBase::RunWorkSource. +bool MessagePumpCFRunLoopBase::RunWork() { + if (!delegate_) { + // This point can be reached with a NULL delegate_ if Run is not on the + // stack but foreign code is spinning the CFRunLoop. Arrange to come back + // here when a delegate is available. + delegateless_work_ = true; + return false; + } + + // The NSApplication-based run loop only drains the autorelease pool at each + // UI event (NSEvent). The autorelease pool is not drained for each + // CFRunLoopSource target that's run. Use a local pool for any autoreleased + // objects if the app is not currently handling a UI event to ensure they're + // released promptly even in the absence of UI events. + MessagePumpScopedAutoreleasePool autorelease_pool(this); + + // Call DoWork and DoDelayedWork once, and if something was done, arrange to + // come back here again as long as the loop is still running. + bool did_work = delegate_->DoWork(); + bool resignal_work_source = did_work; + + TimeTicks next_time; + delegate_->DoDelayedWork(&next_time); + if (!did_work) { + // Determine whether there's more delayed work, and if so, if it needs to + // be done at some point in the future or if it's already time to do it. + // Only do these checks if did_work is false. If did_work is true, this + // function, and therefore any additional delayed work, will get another + // chance to run before the loop goes to sleep. + bool more_delayed_work = !next_time.is_null(); + if (more_delayed_work) { + TimeDelta delay = next_time - TimeTicks::Now(); + if (delay > TimeDelta()) { + // There's more delayed work to be done in the future. + ScheduleDelayedWork(next_time); + } else { + // There's more delayed work to be done, and its time is in the past. + // Arrange to come back here directly as long as the loop is still + // running. + resignal_work_source = true; + } + } + } + + if (resignal_work_source) { + CFRunLoopSourceSignal(work_source_); + } + + return resignal_work_source; +} + +// Called from the run loop. +// static +void MessagePumpCFRunLoopBase::RunIdleWorkSource(void* info) { + MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); + self->RunIdleWork(); +} + +// Called by MessagePumpCFRunLoopBase::RunIdleWorkSource. +bool MessagePumpCFRunLoopBase::RunIdleWork() { + if (!delegate_) { + // This point can be reached with a NULL delegate_ if Run is not on the + // stack but foreign code is spinning the CFRunLoop. Arrange to come back + // here when a delegate is available. + delegateless_idle_work_ = true; + return false; + } + + // The NSApplication-based run loop only drains the autorelease pool at each + // UI event (NSEvent). The autorelease pool is not drained for each + // CFRunLoopSource target that's run. Use a local pool for any autoreleased + // objects if the app is not currently handling a UI event to ensure they're + // released promptly even in the absence of UI events. + MessagePumpScopedAutoreleasePool autorelease_pool(this); + + // Call DoIdleWork once, and if something was done, arrange to come back here + // again as long as the loop is still running. + bool did_work = delegate_->DoIdleWork(); + if (did_work) { + CFRunLoopSourceSignal(idle_work_source_); + } + + return did_work; +} + +// Called from the run loop. +// static +void MessagePumpCFRunLoopBase::RunNestingDeferredWorkSource(void* info) { + MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); + self->RunNestingDeferredWork(); +} + +// Called by MessagePumpCFRunLoopBase::RunNestingDeferredWorkSource. +bool MessagePumpCFRunLoopBase::RunNestingDeferredWork() { + if (!delegate_) { + // This point can be reached with a NULL delegate_ if Run is not on the + // stack but foreign code is spinning the CFRunLoop. There's no sense in + // attempting to do any work or signalling the work sources because + // without a delegate, work is not possible. + return false; + } + + // Immediately try work in priority order. + if (!RunWork()) { + if (!RunIdleWork()) { + return false; + } + } else { + // Work was done. Arrange for the loop to try non-nestable idle work on + // a subsequent pass. + CFRunLoopSourceSignal(idle_work_source_); + } + + return true; +} + +// Called before the run loop goes to sleep or exits, or processes sources. +void MessagePumpCFRunLoopBase::MaybeScheduleNestingDeferredWork() { + // deepest_nesting_level_ is set as run loops are entered. If the deepest + // level encountered is deeper than the current level, a nested loop + // (relative to the current level) ran since the last time nesting-deferred + // work was scheduled. When that situation is encountered, schedule + // nesting-deferred work in case any work was deferred because nested work + // was disallowed. + if (deepest_nesting_level_ > nesting_level_) { + deepest_nesting_level_ = nesting_level_; + CFRunLoopSourceSignal(nesting_deferred_work_source_); + } +} + +// Called from the run loop. +// static +void MessagePumpCFRunLoopBase::PreWaitObserver(CFRunLoopObserverRef observer, + CFRunLoopActivity activity, + void* info) { + MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); + + // Attempt to do some idle work before going to sleep. + self->RunIdleWork(); + + // The run loop is about to go to sleep. If any of the work done since it + // started or woke up resulted in a nested run loop running, + // nesting-deferred work may have accumulated. Schedule it for processing + // if appropriate. + self->MaybeScheduleNestingDeferredWork(); +} + +// Called from the run loop. +// static +void MessagePumpCFRunLoopBase::PreSourceObserver(CFRunLoopObserverRef observer, + CFRunLoopActivity activity, + void* info) { + MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); + + // The run loop has reached the top of the loop and is about to begin + // processing sources. If the last iteration of the loop at this nesting + // level did not sleep or exit, nesting-deferred work may have accumulated + // if a nested loop ran. Schedule nesting-deferred work for processing if + // appropriate. + self->MaybeScheduleNestingDeferredWork(); +} + +// Called from the run loop. +// static +void MessagePumpCFRunLoopBase::EnterExitObserver(CFRunLoopObserverRef observer, + CFRunLoopActivity activity, + void* info) { + MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); + + switch (activity) { + case kCFRunLoopEntry: + ++self->nesting_level_; + if (self->nesting_level_ > self->deepest_nesting_level_) { + self->deepest_nesting_level_ = self->nesting_level_; + } + break; + + case kCFRunLoopExit: + // Not all run loops go to sleep. If a run loop is stopped before it + // goes to sleep due to a CFRunLoopStop call, or if the timeout passed + // to CFRunLoopRunInMode expires, the run loop may proceed directly from + // handling sources to exiting without any sleep. This most commonly + // occurs when CFRunLoopRunInMode is passed a timeout of 0, causing it + // to make a single pass through the loop and exit without sleep. Some + // native loops use CFRunLoop in this way. Because PreWaitObserver will + // not be called in these case, MaybeScheduleNestingDeferredWork needs + // to be called here, as the run loop exits. + // + // MaybeScheduleNestingDeferredWork consults self->nesting_level_ + // to determine whether to schedule nesting-deferred work. It expects + // the nesting level to be set to the depth of the loop that is going + // to sleep or exiting. It must be called before decrementing the + // value so that the value still corresponds to the level of the exiting + // loop. + self->MaybeScheduleNestingDeferredWork(); + --self->nesting_level_; + break; + + default: + break; + } + + self->EnterExitRunLoop(activity); +} + +// Called by MessagePumpCFRunLoopBase::EnterExitRunLoop. The default +// implementation is a no-op. +void MessagePumpCFRunLoopBase::EnterExitRunLoop(CFRunLoopActivity activity) { +} + +// Base version returns a standard NSAutoreleasePool. +NSAutoreleasePool* MessagePumpCFRunLoopBase::CreateAutoreleasePool() { + return [[NSAutoreleasePool alloc] init]; +} + +MessagePumpCFRunLoop::MessagePumpCFRunLoop() + : quit_pending_(false) { +} + +MessagePumpCFRunLoop::~MessagePumpCFRunLoop() {} + +// Called by MessagePumpCFRunLoopBase::DoRun. If other CFRunLoopRun loops were +// running lower on the run loop thread's stack when this object was created, +// the same number of CFRunLoopRun loops must be running for the outermost call +// to Run. Run/DoRun are reentrant after that point. +void MessagePumpCFRunLoop::DoRun(Delegate* delegate) { + // This is completely identical to calling CFRunLoopRun(), except autorelease + // pool management is introduced. + int result; + do { + MessagePumpScopedAutoreleasePool autorelease_pool(this); + result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, + kCFTimeIntervalMax, + false); + } while (result != kCFRunLoopRunStopped && result != kCFRunLoopRunFinished); +} + +// Must be called on the run loop thread. +void MessagePumpCFRunLoop::Quit() { + // Stop the innermost run loop managed by this MessagePumpCFRunLoop object. + if (nesting_level() == run_nesting_level()) { + // This object is running the innermost loop, just stop it. + CFRunLoopStop(run_loop()); + } else { + // There's another loop running inside the loop managed by this object. + // In other words, someone else called CFRunLoopRunInMode on the same + // thread, deeper on the stack than the deepest Run call. Don't preempt + // other run loops, just mark this object to quit the innermost Run as + // soon as the other inner loops not managed by Run are done. + quit_pending_ = true; + } +} + +// Called by MessagePumpCFRunLoopBase::EnterExitObserver. +void MessagePumpCFRunLoop::EnterExitRunLoop(CFRunLoopActivity activity) { + if (activity == kCFRunLoopExit && + nesting_level() == run_nesting_level() && + quit_pending_) { + // Quit was called while loops other than those managed by this object + // were running further inside a run loop managed by this object. Now + // that all unmanaged inner run loops are gone, stop the loop running + // just inside Run. + CFRunLoopStop(run_loop()); + quit_pending_ = false; + } +} + +MessagePumpNSRunLoop::MessagePumpNSRunLoop() + : keep_running_(true) { + CFRunLoopSourceContext source_context = CFRunLoopSourceContext(); + source_context.perform = NoOp; + quit_source_ = CFRunLoopSourceCreate(NULL, // allocator + 0, // priority + &source_context); + CFRunLoopAddSource(run_loop(), quit_source_, kCFRunLoopCommonModes); +} + +MessagePumpNSRunLoop::~MessagePumpNSRunLoop() { + CFRunLoopRemoveSource(run_loop(), quit_source_, kCFRunLoopCommonModes); + CFRelease(quit_source_); +} + +void MessagePumpNSRunLoop::DoRun(Delegate* delegate) { + while (keep_running_) { + // NSRunLoop manages autorelease pools itself. + [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode + beforeDate:[NSDate distantFuture]]; + } + + keep_running_ = true; +} + +void MessagePumpNSRunLoop::Quit() { + keep_running_ = false; + CFRunLoopSourceSignal(quit_source_); + CFRunLoopWakeUp(run_loop()); +} + +#if defined(OS_IOS) +MessagePumpUIApplication::MessagePumpUIApplication() + : run_loop_(NULL) { +} + +MessagePumpUIApplication::~MessagePumpUIApplication() {} + +void MessagePumpUIApplication::DoRun(Delegate* delegate) { + NOTREACHED(); +} + +void MessagePumpUIApplication::Quit() { + NOTREACHED(); +} + +void MessagePumpUIApplication::Attach(Delegate* delegate) { + DCHECK(!run_loop_); + run_loop_ = new RunLoop(); + CHECK(run_loop_->BeforeRun()); + SetDelegate(delegate); +} + +#else + +MessagePumpNSApplication::MessagePumpNSApplication() + : keep_running_(true), + running_own_loop_(false) { +} + +MessagePumpNSApplication::~MessagePumpNSApplication() {} + +void MessagePumpNSApplication::DoRun(Delegate* delegate) { + bool last_running_own_loop_ = running_own_loop_; + + // NSApp must be initialized by calling: + // [{some class which implements CrAppProtocol} sharedApplication] + // Most likely candidates are CrApplication or BrowserCrApplication. + // These can be initialized from C++ code by calling + // RegisterCrApp() or RegisterBrowserCrApp(). + CHECK(NSApp); + + if (![NSApp isRunning]) { + running_own_loop_ = false; + // NSApplication manages autorelease pools itself when run this way. + [NSApp run]; + } else { + running_own_loop_ = true; + NSDate* distant_future = [NSDate distantFuture]; + while (keep_running_) { + MessagePumpScopedAutoreleasePool autorelease_pool(this); + NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask + untilDate:distant_future + inMode:NSDefaultRunLoopMode + dequeue:YES]; + if (event) { + [NSApp sendEvent:event]; + } + } + keep_running_ = true; + } + + running_own_loop_ = last_running_own_loop_; +} + +void MessagePumpNSApplication::Quit() { + if (!running_own_loop_) { + [[NSApplication sharedApplication] stop:nil]; + } else { + keep_running_ = false; + } + + // Send a fake event to wake the loop up. + [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined + location:NSMakePoint(0, 0) + modifierFlags:0 + timestamp:0 + windowNumber:0 + context:NULL + subtype:0 + data1:0 + data2:0] + atStart:NO]; +} + +MessagePumpCrApplication::MessagePumpCrApplication() { +} + +// Prevents an autorelease pool from being created if the app is in the midst of +// handling a UI event because various parts of AppKit depend on objects that +// are created while handling a UI event to be autoreleased in the event loop. +// An example of this is NSWindowController. When a window with a window +// controller is closed it goes through a stack like this: +// (Several stack frames elided for clarity) +// +// #0 [NSWindowController autorelease] +// #1 DoAClose +// #2 MessagePumpCFRunLoopBase::DoWork() +// #3 [NSRunLoop run] +// #4 [NSButton performClick:] +// #5 [NSWindow sendEvent:] +// #6 [NSApp sendEvent:] +// #7 [NSApp run] +// +// -performClick: spins a nested run loop. If the pool created in DoWork was a +// standard NSAutoreleasePool, it would release the objects that were +// autoreleased into it once DoWork released it. This would cause the window +// controller, which autoreleased itself in frame #0, to release itself, and +// possibly free itself. Unfortunately this window controller controls the +// window in frame #5. When the stack is unwound to frame #5, the window would +// no longer exists and crashes may occur. Apple gets around this by never +// releasing the pool it creates in frame #4, and letting frame #7 clean it up +// when it cleans up the pool that wraps frame #7. When an autorelease pool is +// released it releases all other pools that were created after it on the +// autorelease pool stack. +// +// CrApplication is responsible for setting handlingSendEvent to true just +// before it sends the event through the event handling mechanism, and +// returning it to its previous value once the event has been sent. +NSAutoreleasePool* MessagePumpCrApplication::CreateAutoreleasePool() { + if (MessagePumpMac::IsHandlingSendEvent()) + return nil; + return MessagePumpNSApplication::CreateAutoreleasePool(); +} + +// static +bool MessagePumpMac::UsingCrApp() { + DCHECK([NSThread isMainThread]); + + // If NSApp is still not initialized, then the subclass used cannot + // be determined. + DCHECK(NSApp); + + // The pump was created using MessagePumpNSApplication. + if (g_not_using_cr_app) + return false; + + return [NSApp conformsToProtocol:@protocol(CrAppProtocol)]; +} + +// static +bool MessagePumpMac::IsHandlingSendEvent() { + DCHECK([NSApp conformsToProtocol:@protocol(CrAppProtocol)]); + NSObject<CrAppProtocol>* app = static_cast<NSObject<CrAppProtocol>*>(NSApp); + return [app isHandlingSendEvent]; +} +#endif // !defined(OS_IOS) + +// static +MessagePump* MessagePumpMac::Create() { + if ([NSThread isMainThread]) { +#if defined(OS_IOS) + return new MessagePumpUIApplication; +#else + if ([NSApp conformsToProtocol:@protocol(CrAppProtocol)]) + return new MessagePumpCrApplication; + + // The main-thread MessagePump implementations REQUIRE an NSApp. + // Executables which have specific requirements for their + // NSApplication subclass should initialize appropriately before + // creating an event loop. + [NSApplication sharedApplication]; + g_not_using_cr_app = true; + return new MessagePumpNSApplication; +#endif + } + + return new MessagePumpNSRunLoop; +} + +} // namespace base diff --git a/base/message_loop/message_pump_observer.h b/base/message_loop/message_pump_observer.h new file mode 100644 index 0000000..cb46fa3 --- /dev/null +++ b/base/message_loop/message_pump_observer.h @@ -0,0 +1,47 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MESSAGE_LOOP_MESSAGE_PUMP_OBSERVER_H +#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_OBSERVER_H + +#include "base/base_export.h" +#include "base/event_types.h" + +namespace base { + +enum EventStatus { + EVENT_CONTINUE, // The event should be dispatched as normal. +#if defined(USE_X11) + EVENT_HANDLED // The event should not be processed any farther. +#endif +}; + +// A MessagePumpObserver is an object that receives global +// notifications from the UI MessageLoop with MessagePumpWin or +// MessagePumpAuraX11. +// +// NOTE: An Observer implementation should be extremely fast! +// +// For use with MessagePumpAuraX11, please see message_pump_glib.h for more +// info about how this is invoked in this environment. +class BASE_EXPORT MessagePumpObserver { + public: + // This method is called before processing a NativeEvent. If the + // method returns EVENT_HANDLED, it indicates the event has already + // been handled, so the event is not processed any farther. If the + // method returns EVENT_CONTINUE, the event dispatching proceeds as + // normal. + virtual EventStatus WillProcessEvent(const NativeEvent& event) = 0; + + // This method is called after processing a message. This method + // will not be called if WillProcessEvent returns EVENT_HANDLED. + virtual void DidProcessEvent(const NativeEvent& event) = 0; + + protected: + virtual ~MessagePumpObserver() {} +}; + +} // namespace base + +#endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_OBSERVER_H diff --git a/base/message_loop/message_pump_ozone.cc b/base/message_loop/message_pump_ozone.cc new file mode 100644 index 0000000..5c0bf83 --- /dev/null +++ b/base/message_loop/message_pump_ozone.cc @@ -0,0 +1,61 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/message_loop/message_pump_ozone.h" + +#include "base/logging.h" +#include "base/message_loop.h" + +namespace base { + +MessagePumpOzone::MessagePumpOzone() + : MessagePumpLibevent() { +} + +MessagePumpOzone::~MessagePumpOzone() { +} + +void MessagePumpOzone::AddObserver(MessagePumpObserver* /* observer */) { + NOTIMPLEMENTED(); +} + +void MessagePumpOzone::RemoveObserver(MessagePumpObserver* /* observer */) { + NOTIMPLEMENTED(); +} + +// static +MessagePumpOzone* MessagePumpOzone::Current() { + MessageLoopForUI* loop = MessageLoopForUI::current(); + return static_cast<MessagePumpOzone*>(loop->pump_ui()); +} + +void MessagePumpOzone::AddDispatcherForRootWindow( + MessagePumpDispatcher* dispatcher) { + // Only one root window is supported. + DCHECK(dispatcher_.size() == 0); + dispatcher_.insert(dispatcher_.begin(),dispatcher); +} + +void MessagePumpOzone::RemoveDispatcherForRootWindow( + MessagePumpDispatcher* dispatcher) { + DCHECK(dispatcher_.size() == 1); + dispatcher_.pop_back(); +} + +bool MessagePumpOzone::Dispatch(const NativeEvent& dev) { + if (dispatcher_.size() > 0) + return dispatcher_[0]->Dispatch(dev); + else + return true; +} + +// This code assumes that the caller tracks the lifetime of the |dispatcher|. +void MessagePumpOzone::RunWithDispatcher( + Delegate* delegate, MessagePumpDispatcher* dispatcher) { + dispatcher_.push_back(dispatcher); + Run(delegate); + dispatcher_.pop_back(); +} + +} // namespace base diff --git a/base/message_loop/message_pump_ozone.h b/base/message_loop/message_pump_ozone.h new file mode 100644 index 0000000..de75ab4 --- /dev/null +++ b/base/message_loop/message_pump_ozone.h @@ -0,0 +1,52 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MESSAGE_LOOP_MESSAGE_PUMP_OZONE_H_ +#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_OZONE_H_ + +#include "base/memory/scoped_ptr.h" +#include "base/memory/scoped_vector.h" +#include "base/message_loop/message_pump_dispatcher.h" +#include "base/message_loop/message_pump_libevent.h" +#include "base/message_loop/message_pump_observer.h" +#include "base/observer_list.h" + +namespace base { + +// This class implements a message-pump for processing events from input devices +// Refer to MessagePump for further documentation. +class BASE_EXPORT MessagePumpOzone : public MessagePumpLibevent, + public MessagePumpDispatcher { + public: + MessagePumpOzone(); + + // Returns the UI message pump. + static MessagePumpOzone* Current(); + + // Add/Remove the root window dispatcher. + void AddDispatcherForRootWindow(MessagePumpDispatcher* dispatcher); + void RemoveDispatcherForRootWindow(MessagePumpDispatcher* dispatcher); + + void RunWithDispatcher(Delegate* delegate, MessagePumpDispatcher* dispatcher); + + // Add / remove an Observer, which will start receiving notifications + // immediately. + void AddObserver(MessagePumpObserver* observer); + void RemoveObserver(MessagePumpObserver* observer); + + // Overridden from MessagePumpDispatcher. + virtual bool Dispatch(const NativeEvent& event) OVERRIDE; + + private: + virtual ~MessagePumpOzone(); + std::vector<MessagePumpDispatcher*> dispatcher_; + + DISALLOW_COPY_AND_ASSIGN(MessagePumpOzone); +}; + +typedef MessagePumpOzone MessagePumpForUI; + +} // namespace base + +#endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_OZONE_H_ diff --git a/base/message_loop/message_pump_win.cc b/base/message_loop/message_pump_win.cc new file mode 100644 index 0000000..36a3210 --- /dev/null +++ b/base/message_loop/message_pump_win.cc @@ -0,0 +1,686 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/message_loop/message_pump_win.h" + +#include <math.h> + +#include "base/debug/trace_event.h" +#include "base/message_loop/message_loop.h" +#include "base/metrics/histogram.h" +#include "base/process_util.h" +#include "base/stringprintf.h" +#include "base/win/wrapped_window_proc.h" + +namespace base { + +namespace { + +enum MessageLoopProblems { + MESSAGE_POST_ERROR, + COMPLETION_POST_ERROR, + SET_TIMER_ERROR, + MESSAGE_LOOP_PROBLEM_MAX, +}; + +} // namespace + +static const wchar_t kWndClassFormat[] = L"Chrome_MessagePumpWindow_%p"; + +// 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; + +//----------------------------------------------------------------------------- +// MessagePumpWin public: + +void MessagePumpWin::AddObserver(MessagePumpObserver* observer) { + observers_.AddObserver(observer); +} + +void MessagePumpWin::RemoveObserver(MessagePumpObserver* observer) { + observers_.RemoveObserver(observer); +} + +void MessagePumpWin::WillProcessMessage(const MSG& msg) { + FOR_EACH_OBSERVER(MessagePumpObserver, observers_, WillProcessEvent(msg)); +} + +void MessagePumpWin::DidProcessMessage(const MSG& msg) { + FOR_EACH_OBSERVER(MessagePumpObserver, observers_, DidProcessEvent(msg)); +} + +void MessagePumpWin::RunWithDispatcher( + Delegate* delegate, MessagePumpDispatcher* 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; +} + +//----------------------------------------------------------------------------- +// MessagePumpWin protected: + +int MessagePumpWin::GetCurrentDelay() const { + if (delayed_work_time_.is_null()) + return -1; + + // Be careful here. TimeDelta has a precision of microseconds, but we want a + // value in milliseconds. If there are 5.5ms left, should the delay be 5 or + // 6? It should be 6 to avoid executing delayed work too early. + double timeout = + ceil((delayed_work_time_ - TimeTicks::Now()).InMillisecondsF()); + + // If this value is negative, then we need to run delayed work soon. + int delay = static_cast<int>(timeout); + if (delay < 0) + delay = 0; + + return delay; +} + +//----------------------------------------------------------------------------- +// MessagePumpForUI public: + +MessagePumpForUI::MessagePumpForUI() + : atom_(0), + message_filter_(new MessageFilter) { + InitMessageWnd(); +} + +MessagePumpForUI::~MessagePumpForUI() { + DestroyWindow(message_hwnd_); + UnregisterClass(MAKEINTATOM(atom_), + GetModuleFromAddress(&WndProcThunk)); +} + +void MessagePumpForUI::ScheduleWork() { + if (InterlockedExchange(&have_work_, 1)) + return; // Someone else continued the pumping. + + // Make sure the MessagePump does some work for us. + BOOL ret = PostMessage(message_hwnd_, kMsgHaveWork, + reinterpret_cast<WPARAM>(this), 0); + if (ret) + return; // There was room in the Window Message queue. + + // We have failed to insert a have-work message, so there is a chance that we + // will starve tasks/timers while sitting in a nested message loop. Nested + // loops only look at Windows Message queues, and don't look at *our* task + // queues, etc., so we might not get a time slice in such. :-( + // We could abort here, but the fear is that this failure mode is plausibly + // common (queue is full, of about 2000 messages), so we'll do a near-graceful + // recovery. Nested loops are pretty transient (we think), so this will + // probably be recoverable. + InterlockedExchange(&have_work_, 0); // Clarify that we didn't really insert. + UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", MESSAGE_POST_ERROR, + MESSAGE_LOOP_PROBLEM_MAX); +} + +void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { + // + // 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. + // + delayed_work_time_ = delayed_work_time; + + int delay_msec = GetCurrentDelay(); + DCHECK_GE(delay_msec, 0); + 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). + BOOL ret = SetTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this), + delay_msec, NULL); + if (ret) + return; + // If we can't set timers, we are in big trouble... but cross our fingers for + // now. + // TODO(jar): If we don't see this error, use a CHECK() here instead. + UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", SET_TIMER_ERROR, + MESSAGE_LOOP_PROBLEM_MAX); +} + +void MessagePumpForUI::PumpOutPendingPaintMessages() { + // If we are being called outside of the context of Run, then don't try to do + // any work. + if (!state_) + return; + + // 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; + int peek_count; + for (peek_count = 0; peek_count < kMaxPeekCount; ++peek_count) { + MSG msg; + 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("Loop.PumpOutPendingPaintMessages Peeks", peek_count); +} + +//----------------------------------------------------------------------------- +// MessagePumpForUI private: + +// static +LRESULT CALLBACK MessagePumpForUI::WndProcThunk( + HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { + switch (message) { + case kMsgHaveWork: + reinterpret_cast<MessagePumpForUI*>(wparam)->HandleWorkMessage(); + break; + case WM_TIMER: + reinterpret_cast<MessagePumpForUI*>(wparam)->HandleTimerMessage(); + break; + } + return DefWindowProc(hwnd, message, wparam, lparam); +} + +void MessagePumpForUI::DoRunLoop() { + // IF this was just a simple PeekMessage() loop (servicing all possible 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. 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 |= + state_->delegate->DoDelayedWork(&delayed_work_time_); + // If we did not process any delayed work, then we can assume that our + // existing WM_TIMER if any will fire when delayed work should run. We + // don't want to disturb that timer if it is already in flight. However, + // if we did do all remaining delayed work, then lets kill the WM_TIMER. + if (more_work_is_plausible && delayed_work_time_.is_null()) + KillTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this)); + 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; + + WaitForWork(); // Wait (sleep) until we have work to do again. + } +} + +void MessagePumpForUI::InitMessageWnd() { + // Generate a unique window class name. + string16 class_name = StringPrintf(kWndClassFormat, this); + + HINSTANCE instance = GetModuleFromAddress(&WndProcThunk); + WNDCLASSEX wc = {0}; + wc.cbSize = sizeof(wc); + wc.lpfnWndProc = base::win::WrappedWindowProc<WndProcThunk>; + wc.hInstance = instance; + wc.lpszClassName = class_name.c_str(); + atom_ = RegisterClassEx(&wc); + DCHECK(atom_); + + message_hwnd_ = CreateWindow(MAKEINTATOM(atom_), 0, 0, 0, 0, 0, 0, + HWND_MESSAGE, 0, instance, 0); + DCHECK(message_hwnd_); +} + +void MessagePumpForUI::WaitForWork() { + // Wait until a message is available, up to the time needed by the timer + // manager to fire the next set of timers. + int delay = GetCurrentDelay(); + if (delay < 0) // Negative value means no timers waiting. + delay = INFINITE; + + DWORD result; + result = MsgWaitForMultipleObjectsEx(0, NULL, delay, QS_ALLINPUT, + MWMO_INPUTAVAILABLE); + + if (WAIT_OBJECT_0 == result) { + // A WM_* message is available. + // If a parent child relationship exists between windows across threads + // then their thread inputs are implicitly attached. + // This causes the MsgWaitForMultipleObjectsEx API to return indicating + // that messages are ready for processing (Specifically, mouse messages + // intended for the child window may appear if the child window has + // capture). + // The subsequent PeekMessages call may fail to return any messages thus + // causing us to enter a tight loop at times. + // The WaitMessage call below is a workaround to give the child window + // some time to process its input messages. + MSG msg = {0}; + DWORD queue_status = GetQueueStatus(QS_MOUSE); + if (HIWORD(queue_status) & QS_MOUSE && + !PeekMessage(&msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST, PM_NOREMOVE)) { + WaitMessage(); + } + return; + } + + DCHECK_NE(WAIT_FAILED, result) << GetLastError(); +} + +void MessagePumpForUI::HandleWorkMessage() { + // If we are being called outside of the context of Run, then don't try to do + // any work. This could correspond to a MessageBox call or something of that + // sort. + if (!state_) { + // Since we handled a kMsgHaveWork message, we must still update this flag. + InterlockedExchange(&have_work_, 0); + 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. + if (state_->delegate->DoWork()) + ScheduleWork(); +} + +void MessagePumpForUI::HandleTimerMessage() { + KillTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this)); + + // 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; + + state_->delegate->DoDelayedWork(&delayed_work_time_); + if (!delayed_work_time_.is_null()) { + // A bit gratuitous to set delayed_work_time_ again, but oh well. + ScheduleDelayedWork(delayed_work_time_); + } +} + +bool MessagePumpForUI::ProcessNextWindowsMessage() { + // If there are sent messages in the queue then PeekMessage internally + // dispatches the message and returns false. We return true in this + // case to ensure that the message loop peeks again instead of calling + // MsgWaitForMultipleObjectsEx again. + bool sent_messages_in_queue = false; + DWORD queue_status = GetQueueStatus(QS_SENDMESSAGE); + if (HIWORD(queue_status) & QS_SENDMESSAGE) + sent_messages_in_queue = true; + + MSG msg; + if (message_filter_->DoPeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) + return ProcessMessageHelper(msg); + + return sent_messages_in_queue; +} + +bool MessagePumpForUI::ProcessMessageHelper(const MSG& msg) { + TRACE_EVENT1("base", "MessagePumpForUI::ProcessMessageHelper", + "message", msg.message); + 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(); + + if (CallMsgFilter(const_cast<MSG*>(&msg), kMessageFilterCode)) + return true; + + WillProcessMessage(msg); + + if (!message_filter_->ProcessMessage(msg)) { + if (state_->dispatcher) { + if (!state_->dispatcher->Dispatch(msg)) + state_->should_quit = true; + } else { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + DidProcessMessage(msg); + return true; +} + +bool MessagePumpForUI::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!! + + bool have_message = false; + MSG msg; + // We should not process all window messages if we are in the context of an + // OS modal loop, i.e. in the context of a windows API call like MessageBox. + // This is to ensure that these messages are peeked out by the OS modal loop. + if (MessageLoop::current()->os_modal_loop()) { + // We only peek out WM_PAINT and WM_TIMER here for reasons mentioned above. + have_message = PeekMessage(&msg, NULL, WM_PAINT, WM_PAINT, PM_REMOVE) || + PeekMessage(&msg, NULL, WM_TIMER, WM_TIMER, PM_REMOVE); + } else { + have_message = !!message_filter_->DoPeekMessage(&msg, NULL, 0, 0, + PM_REMOVE); + } + + DCHECK(!have_message || kMsgHaveWork != msg.message || + msg.hwnd != message_hwnd_); + + // Since we discarded a kMsgHaveWork message, we must update the flag. + int old_have_work = InterlockedExchange(&have_work_, 0); + DCHECK(old_have_work); + + // We don't need a special time slice if we didn't have_message to process. + if (!have_message) + return false; + + // Guarantee we'll get another time slice in the case where we go into native + // windows code. This ScheduleWork() may hurt performance a tiny bit when + // tasks appear very infrequently, but when the event queue is busy, the + // kMsgHaveWork events get (percentage wise) rarer and rarer. + ScheduleWork(); + return ProcessMessageHelper(msg); +} + +void MessagePumpForUI::SetMessageFilter( + scoped_ptr<MessageFilter> message_filter) { + message_filter_ = message_filter.Pass(); +} + +//----------------------------------------------------------------------------- +// MessagePumpForIO public: + +MessagePumpForIO::MessagePumpForIO() { + port_.Set(CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, NULL, 1)); + DCHECK(port_.IsValid()); +} + +void MessagePumpForIO::ScheduleWork() { + if (InterlockedExchange(&have_work_, 1)) + return; // Someone else continued the pumping. + + // Make sure the MessagePump does some work for us. + BOOL ret = PostQueuedCompletionStatus(port_, 0, + reinterpret_cast<ULONG_PTR>(this), + reinterpret_cast<OVERLAPPED*>(this)); + if (ret) + return; // Post worked perfectly. + + // See comment in MessagePumpForUI::ScheduleWork() for this error recovery. + InterlockedExchange(&have_work_, 0); // Clarify that we didn't succeed. + UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", COMPLETION_POST_ERROR, + MESSAGE_LOOP_PROBLEM_MAX); +} + +void MessagePumpForIO::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { + // We know that we can't be blocked right now since this method can only be + // called on the same thread as Run, so we only need to update our record of + // how long to sleep when we do sleep. + delayed_work_time_ = delayed_work_time; +} + +void MessagePumpForIO::RegisterIOHandler(HANDLE file_handle, + IOHandler* handler) { + ULONG_PTR key = HandlerToKey(handler, true); + HANDLE port = CreateIoCompletionPort(file_handle, port_, key, 1); + DPCHECK(port); +} + +bool MessagePumpForIO::RegisterJobObject(HANDLE job_handle, + IOHandler* handler) { + // Job object notifications use the OVERLAPPED pointer to carry the message + // data. Mark the completion key correspondingly, so we will not try to + // convert OVERLAPPED* to IOContext*. + ULONG_PTR key = HandlerToKey(handler, false); + JOBOBJECT_ASSOCIATE_COMPLETION_PORT info; + info.CompletionKey = reinterpret_cast<void*>(key); + info.CompletionPort = port_; + return SetInformationJobObject(job_handle, + JobObjectAssociateCompletionPortInformation, + &info, + sizeof(info)) != FALSE; +} + +//----------------------------------------------------------------------------- +// MessagePumpForIO private: + +void MessagePumpForIO::DoRunLoop() { + 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) + // WaitForIOCompletion(), there is a good chance there are still more + // messages waiting. 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 = state_->delegate->DoWork(); + if (state_->should_quit) + break; + + more_work_is_plausible |= WaitForIOCompletion(0, NULL); + if (state_->should_quit) + break; + + more_work_is_plausible |= + state_->delegate->DoDelayedWork(&delayed_work_time_); + 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; + + WaitForWork(); // Wait (sleep) until we have work to do again. + } +} + +// Wait until IO completes, up to the time needed by the timer manager to fire +// the next set of timers. +void MessagePumpForIO::WaitForWork() { + // We do not support nested IO message loops. This is to avoid messy + // recursion problems. + DCHECK_EQ(1, state_->run_depth) << "Cannot nest an IO message loop!"; + + int timeout = GetCurrentDelay(); + if (timeout < 0) // Negative value means no timers waiting. + timeout = INFINITE; + + WaitForIOCompletion(timeout, NULL); +} + +bool MessagePumpForIO::WaitForIOCompletion(DWORD timeout, IOHandler* filter) { + IOItem item; + if (completed_io_.empty() || !MatchCompletedIOItem(filter, &item)) { + // We have to ask the system for another IO completion. + if (!GetIOItem(timeout, &item)) + return false; + + if (ProcessInternalIOItem(item)) + return true; + } + + // If |item.has_valid_io_context| is false then |item.context| does not point + // to a context structure, and so should not be dereferenced, although it may + // still hold valid non-pointer data. + if (!item.has_valid_io_context || item.context->handler) { + if (filter && item.handler != filter) { + // Save this item for later + completed_io_.push_back(item); + } else { + DCHECK(!item.has_valid_io_context || + (item.context->handler == item.handler)); + WillProcessIOEvent(); + item.handler->OnIOCompleted(item.context, item.bytes_transfered, + item.error); + DidProcessIOEvent(); + } + } else { + // The handler must be gone by now, just cleanup the mess. + delete item.context; + } + return true; +} + +// Asks the OS for another IO completion result. +bool MessagePumpForIO::GetIOItem(DWORD timeout, IOItem* item) { + memset(item, 0, sizeof(*item)); + ULONG_PTR key = NULL; + OVERLAPPED* overlapped = NULL; + if (!GetQueuedCompletionStatus(port_.Get(), &item->bytes_transfered, &key, + &overlapped, timeout)) { + if (!overlapped) + return false; // Nothing in the queue. + item->error = GetLastError(); + item->bytes_transfered = 0; + } + + item->handler = KeyToHandler(key, &item->has_valid_io_context); + item->context = reinterpret_cast<IOContext*>(overlapped); + return true; +} + +bool MessagePumpForIO::ProcessInternalIOItem(const IOItem& item) { + if (this == reinterpret_cast<MessagePumpForIO*>(item.context) && + this == reinterpret_cast<MessagePumpForIO*>(item.handler)) { + // This is our internal completion. + DCHECK(!item.bytes_transfered); + InterlockedExchange(&have_work_, 0); + return true; + } + return false; +} + +// Returns a completion item that was previously received. +bool MessagePumpForIO::MatchCompletedIOItem(IOHandler* filter, IOItem* item) { + DCHECK(!completed_io_.empty()); + for (std::list<IOItem>::iterator it = completed_io_.begin(); + it != completed_io_.end(); ++it) { + if (!filter || it->handler == filter) { + *item = *it; + completed_io_.erase(it); + return true; + } + } + return false; +} + +void MessagePumpForIO::AddIOObserver(IOObserver *obs) { + io_observers_.AddObserver(obs); +} + +void MessagePumpForIO::RemoveIOObserver(IOObserver *obs) { + io_observers_.RemoveObserver(obs); +} + +void MessagePumpForIO::WillProcessIOEvent() { + FOR_EACH_OBSERVER(IOObserver, io_observers_, WillProcessIOEvent()); +} + +void MessagePumpForIO::DidProcessIOEvent() { + FOR_EACH_OBSERVER(IOObserver, io_observers_, DidProcessIOEvent()); +} + +// static +ULONG_PTR MessagePumpForIO::HandlerToKey(IOHandler* handler, + bool has_valid_io_context) { + ULONG_PTR key = reinterpret_cast<ULONG_PTR>(handler); + + // |IOHandler| is at least pointer-size aligned, so the lowest two bits are + // always cleared. We use the lowest bit to distinguish completion keys with + // and without the associated |IOContext|. + DCHECK((key & 1) == 0); + + // Mark the completion key as context-less. + if (!has_valid_io_context) + key = key | 1; + return key; +} + +// static +MessagePumpForIO::IOHandler* MessagePumpForIO::KeyToHandler( + ULONG_PTR key, + bool* has_valid_io_context) { + *has_valid_io_context = ((key & 1) == 0); + return reinterpret_cast<IOHandler*>(key & ~static_cast<ULONG_PTR>(1)); +} + +} // namespace base diff --git a/base/message_loop/message_pump_win.h b/base/message_loop/message_pump_win.h new file mode 100644 index 0000000..dbb41bd --- /dev/null +++ b/base/message_loop/message_pump_win.h @@ -0,0 +1,396 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MESSAGE_LOOP_MESSAGE_PUMP_WIN_H_ +#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_WIN_H_ + +#include <windows.h> + +#include <list> + +#include "base/base_export.h" +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop/message_pump.h" +#include "base/message_loop/message_pump_dispatcher.h" +#include "base/message_loop/message_pump_observer.h" +#include "base/observer_list.h" +#include "base/time.h" +#include "base/win/scoped_handle.h" + +namespace base { + +// MessagePumpWin serves as the base for specialized versions of the MessagePump +// for Windows. It provides basic functionality like handling of observers and +// controlling the lifetime of the message pump. +class BASE_EXPORT MessagePumpWin : public MessagePump { + public: + MessagePumpWin() : have_work_(0), state_(NULL) {} + virtual ~MessagePumpWin() {} + + // Add an Observer, which will start receiving notifications immediately. + void AddObserver(MessagePumpObserver* observer); + + // Remove an Observer. It is safe to call this method while an Observer is + // receiving a notification callback. + void RemoveObserver(MessagePumpObserver* 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); + + // Like MessagePump::Run, but MSG objects are routed through dispatcher. + void RunWithDispatcher(Delegate* delegate, MessagePumpDispatcher* dispatcher); + + // MessagePump methods: + virtual void Run(Delegate* delegate) { RunWithDispatcher(delegate, NULL); } + virtual void Quit(); + + protected: + struct RunState { + Delegate* delegate; + MessagePumpDispatcher* 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; + }; + + virtual void DoRunLoop() = 0; + int GetCurrentDelay() const; + + ObserverList<MessagePumpObserver> observers_; + + // The time at which delayed work should run. + TimeTicks 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_; +}; + +//----------------------------------------------------------------------------- +// MessagePumpForUI extends MessagePumpWin with methods that are particular to a +// MessageLoop instantiated with TYPE_UI. +// +// MessagePumpForUI 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, and +// DoDelayedWork for pending timers. 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 BASE_EXPORT MessagePumpForUI : public MessagePumpWin { + public: + // A MessageFilter implements the common Peek/Translate/Dispatch code to deal + // with windows messages. + // This abstraction is used to inject TSF message peeking. See + // TextServicesMessageFilter. + class BASE_EXPORT MessageFilter { + public: + virtual ~MessageFilter() {} + // Implements the functionality exposed by the OS through PeekMessage. + virtual BOOL DoPeekMessage(MSG* msg, + HWND window_handle, + UINT msg_filter_min, + UINT msg_filter_max, + UINT remove_msg) { + return PeekMessage(msg, window_handle, msg_filter_min, msg_filter_max, + remove_msg); + } + // Returns true if |message| was consumed by the filter and no extra + // processing is required. If this method returns false, it is the + // responsibility of the caller to ensure that normal processing takes + // place. + // The priority to consume messages is the following: + // - Native Windows' message filter (CallMsgFilter). + // - MessageFilter::ProcessMessage. + // - MessagePumpDispatcher. + // - TranslateMessage / DispatchMessage. + virtual bool ProcessMessage(const MSG& msg) { return false;} + }; + // The application-defined code passed to the hook procedure. + static const int kMessageFilterCode = 0x5001; + + MessagePumpForUI(); + virtual ~MessagePumpForUI(); + + // Sets a new MessageFilter. MessagePumpForUI takes ownership of + // |message_filter|. When SetMessageFilter is called, old MessageFilter is + // deleted. + void SetMessageFilter(scoped_ptr<MessageFilter> message_filter); + + // MessagePump methods: + virtual void ScheduleWork(); + virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time); + + // 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(); + + private: + static LRESULT CALLBACK WndProcThunk(HWND window_handle, + UINT message, + WPARAM wparam, + LPARAM lparam); + virtual void DoRunLoop(); + void InitMessageWnd(); + void WaitForWork(); + void HandleWorkMessage(); + void HandleTimerMessage(); + bool ProcessNextWindowsMessage(); + bool ProcessMessageHelper(const MSG& msg); + bool ProcessPumpReplacementMessage(); + + // Atom representing the registered window class. + ATOM atom_; + + // A hidden message-only window. + HWND message_hwnd_; + + scoped_ptr<MessageFilter> message_filter_; +}; + +//----------------------------------------------------------------------------- +// MessagePumpForIO extends MessagePumpWin with methods that are particular to a +// MessageLoop instantiated with TYPE_IO. This version of MessagePump does not +// deal with Windows mesagges, and instead has a Run loop based on Completion +// Ports so it is better suited for IO operations. +// +class BASE_EXPORT MessagePumpForIO : public MessagePumpWin { + public: + struct IOContext; + + // Clients interested in receiving OS notifications when asynchronous IO + // operations complete should implement this interface and register themselves + // with the message pump. + // + // Typical use #1: + // // Use only when there are no user's buffers involved on the actual IO, + // // so that all the cleanup can be done by the message pump. + // class MyFile : public IOHandler { + // MyFile() { + // ... + // context_ = new IOContext; + // context_->handler = this; + // message_pump->RegisterIOHandler(file_, this); + // } + // ~MyFile() { + // if (pending_) { + // // By setting the handler to NULL, we're asking for this context + // // to be deleted when received, without calling back to us. + // context_->handler = NULL; + // } else { + // delete context_; + // } + // } + // virtual void OnIOCompleted(IOContext* context, DWORD bytes_transfered, + // DWORD error) { + // pending_ = false; + // } + // void DoSomeIo() { + // ... + // // The only buffer required for this operation is the overlapped + // // structure. + // ConnectNamedPipe(file_, &context_->overlapped); + // pending_ = true; + // } + // bool pending_; + // IOContext* context_; + // HANDLE file_; + // }; + // + // Typical use #2: + // class MyFile : public IOHandler { + // MyFile() { + // ... + // message_pump->RegisterIOHandler(file_, this); + // } + // // Plus some code to make sure that this destructor is not called + // // while there are pending IO operations. + // ~MyFile() { + // } + // virtual void OnIOCompleted(IOContext* context, DWORD bytes_transfered, + // DWORD error) { + // ... + // delete context; + // } + // void DoSomeIo() { + // ... + // IOContext* context = new IOContext; + // // This is not used for anything. It just prevents the context from + // // being considered "abandoned". + // context->handler = this; + // ReadFile(file_, buffer, num_bytes, &read, &context->overlapped); + // } + // HANDLE file_; + // }; + // + // Typical use #3: + // Same as the previous example, except that in order to deal with the + // requirement stated for the destructor, the class calls WaitForIOCompletion + // from the destructor to block until all IO finishes. + // ~MyFile() { + // while(pending_) + // message_pump->WaitForIOCompletion(INFINITE, this); + // } + // + class IOHandler { + public: + virtual ~IOHandler() {} + // This will be called once the pending IO operation associated with + // |context| completes. |error| is the Win32 error code of the IO operation + // (ERROR_SUCCESS if there was no error). |bytes_transfered| will be zero + // on error. + virtual void OnIOCompleted(IOContext* context, DWORD bytes_transfered, + DWORD error) = 0; + }; + + // An IOObserver is an object that receives IO notifications from the + // MessagePump. + // + // NOTE: An IOObserver implementation should be extremely fast! + class IOObserver { + public: + IOObserver() {} + + virtual void WillProcessIOEvent() = 0; + virtual void DidProcessIOEvent() = 0; + + protected: + virtual ~IOObserver() {} + }; + + // The extended context that should be used as the base structure on every + // overlapped IO operation. |handler| must be set to the registered IOHandler + // for the given file when the operation is started, and it can be set to NULL + // before the operation completes to indicate that the handler should not be + // called anymore, and instead, the IOContext should be deleted when the OS + // notifies the completion of this operation. Please remember that any buffers + // involved with an IO operation should be around until the callback is + // received, so this technique can only be used for IO that do not involve + // additional buffers (other than the overlapped structure itself). + struct IOContext { + OVERLAPPED overlapped; + IOHandler* handler; + }; + + MessagePumpForIO(); + virtual ~MessagePumpForIO() {} + + // MessagePump methods: + virtual void ScheduleWork(); + virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time); + + // Register the handler to be used when asynchronous IO for the given file + // completes. The registration persists as long as |file_handle| is valid, so + // |handler| must be valid as long as there is pending IO for the given file. + void RegisterIOHandler(HANDLE file_handle, IOHandler* handler); + + // Register the handler to be used to process job events. The registration + // persists as long as the job object is live, so |handler| must be valid + // until the job object is destroyed. Returns true if the registration + // succeeded, and false otherwise. + bool RegisterJobObject(HANDLE job_handle, IOHandler* handler); + + // Waits for the next IO completion that should be processed by |filter|, for + // up to |timeout| milliseconds. Return true if any IO operation completed, + // regardless of the involved handler, and false if the timeout expired. If + // the completion port received any message and the involved IO handler + // matches |filter|, the callback is called before returning from this code; + // if the handler is not the one that we are looking for, the callback will + // be postponed for another time, so reentrancy problems can be avoided. + // External use of this method should be reserved for the rare case when the + // caller is willing to allow pausing regular task dispatching on this thread. + bool WaitForIOCompletion(DWORD timeout, IOHandler* filter); + + void AddIOObserver(IOObserver* obs); + void RemoveIOObserver(IOObserver* obs); + + private: + struct IOItem { + IOHandler* handler; + IOContext* context; + DWORD bytes_transfered; + DWORD error; + + // In some cases |context| can be a non-pointer value casted to a pointer. + // |has_valid_io_context| is true if |context| is a valid IOContext + // pointer, and false otherwise. + bool has_valid_io_context; + }; + + virtual void DoRunLoop(); + void WaitForWork(); + bool MatchCompletedIOItem(IOHandler* filter, IOItem* item); + bool GetIOItem(DWORD timeout, IOItem* item); + bool ProcessInternalIOItem(const IOItem& item); + void WillProcessIOEvent(); + void DidProcessIOEvent(); + + // Converts an IOHandler pointer to a completion port key. + // |has_valid_io_context| specifies whether completion packets posted to + // |handler| will have valid OVERLAPPED pointers. + static ULONG_PTR HandlerToKey(IOHandler* handler, bool has_valid_io_context); + + // Converts a completion port key to an IOHandler pointer. + static IOHandler* KeyToHandler(ULONG_PTR key, bool* has_valid_io_context); + + // The completion port associated with this thread. + win::ScopedHandle port_; + // This list will be empty almost always. It stores IO completions that have + // not been delivered yet because somebody was doing cleanup. + std::list<IOItem> completed_io_; + + ObserverList<IOObserver> io_observers_; +}; + +} // namespace base + +#endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_WIN_H_ |