michael@0: // Copyright (c) 2008 The Chromium Authors. All rights reserved. michael@0: // Use of this source code is governed by a BSD-style license that can be michael@0: // found in the LICENSE file. michael@0: michael@0: // The basis for all native run loops on the Mac is the CFRunLoop. It can be michael@0: // used directly, it can be used as the driving force behind the similar michael@0: // Foundation NSRunLoop, and it can be used to implement higher-level event michael@0: // loops such as the NSApplication event loop. michael@0: // michael@0: // This file introduces a basic CFRunLoop-based implementation of the michael@0: // MessagePump interface called CFRunLoopBase. CFRunLoopBase contains all michael@0: // of the machinery necessary to dispatch events to a delegate, but does not michael@0: // implement the specific run loop. Concrete subclasses must provide their michael@0: // own DoRun and Quit implementations. michael@0: // michael@0: // A concrete subclass that just runs a CFRunLoop loop is provided in michael@0: // MessagePumpCFRunLoop. For an NSRunLoop, the similar MessagePumpNSRunLoop michael@0: // is provided. michael@0: // michael@0: // For the application's event loop, an implementation based on AppKit's michael@0: // NSApplication event system is provided in MessagePumpNSApplication. michael@0: // michael@0: // Typically, MessagePumpNSApplication only makes sense on a Cocoa michael@0: // application's main thread. If a CFRunLoop-based message pump is needed on michael@0: // any other thread, one of the other concrete subclasses is preferrable. michael@0: // MessagePumpMac::Create is defined, which returns a new NSApplication-based michael@0: // or NSRunLoop-based MessagePump subclass depending on which thread it is michael@0: // called on. michael@0: michael@0: #ifndef BASE_MESSAGE_PUMP_MAC_H_ michael@0: #define BASE_MESSAGE_PUMP_MAC_H_ michael@0: michael@0: #include "base/message_pump.h" michael@0: michael@0: #include michael@0: #include michael@0: michael@0: #if defined(__OBJC__) michael@0: @class NSAutoreleasePool; michael@0: #else // defined(__OBJC__) michael@0: class NSAutoreleasePool; michael@0: #endif // defined(__OBJC__) michael@0: michael@0: namespace base { michael@0: michael@0: class TimeTicks; michael@0: michael@0: class MessagePumpCFRunLoopBase : public MessagePump { michael@0: // Needs access to CreateAutoreleasePool. michael@0: friend class MessagePumpScopedAutoreleasePool; michael@0: public: michael@0: MessagePumpCFRunLoopBase(); michael@0: virtual ~MessagePumpCFRunLoopBase(); michael@0: michael@0: // Subclasses should implement the work they need to do in MessagePump::Run michael@0: // in the DoRun method. MessagePumpCFRunLoopBase::Run calls DoRun directly. michael@0: // This arrangement is used because MessagePumpCFRunLoopBase needs to set michael@0: // up and tear down things before and after the "meat" of DoRun. michael@0: virtual void Run(Delegate* delegate); michael@0: virtual void DoRun(Delegate* delegate) = 0; michael@0: michael@0: virtual void ScheduleWork(); michael@0: virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time); michael@0: michael@0: protected: michael@0: // Accessors for private data members to be used by subclasses. michael@0: CFRunLoopRef run_loop() const { return run_loop_; } michael@0: int nesting_level() const { return nesting_level_; } michael@0: int run_nesting_level() const { return run_nesting_level_; } michael@0: michael@0: // Return an autorelease pool to wrap around any work being performed. michael@0: // In some cases, CreateAutoreleasePool may return nil intentionally to michael@0: // preventing an autorelease pool from being created, allowing any michael@0: // objects autoreleased by work to fall into the current autorelease pool. michael@0: virtual NSAutoreleasePool* CreateAutoreleasePool(); michael@0: michael@0: private: michael@0: // Timer callback scheduled by ScheduleDelayedWork. This does not do any michael@0: // work, but it signals delayed_work_source_ so that delayed work can be michael@0: // performed within the appropriate priority constraints. michael@0: static void RunDelayedWorkTimer(CFRunLoopTimerRef timer, void* info); michael@0: michael@0: // Perform highest-priority work. This is associated with work_source_ michael@0: // signalled by ScheduleWork. The static method calls the instance method; michael@0: // the instance method returns true if work was done. michael@0: static void RunWorkSource(void* info); michael@0: bool RunWork(); michael@0: michael@0: // Perform delayed-priority work. This is associated with michael@0: // delayed_work_source_ signalled by RunDelayedWorkTimer, and is responsible michael@0: // for calling ScheduleDelayedWork again if appropriate. The static method michael@0: // calls the instance method; the instance method returns true if more michael@0: // delayed work is available. michael@0: static void RunDelayedWorkSource(void* info); michael@0: bool RunDelayedWork(); michael@0: michael@0: // Perform idle-priority work. This is normally called by PreWaitObserver, michael@0: // but is also associated with idle_work_source_. When this function michael@0: // actually does perform idle work, it will resignal that source. The michael@0: // static method calls the instance method; the instance method returns michael@0: // true if idle work was done. michael@0: static void RunIdleWorkSource(void* info); michael@0: bool RunIdleWork(); michael@0: michael@0: // Perform work that may have been deferred because it was not runnable michael@0: // within a nested run loop. This is associated with michael@0: // nesting_deferred_work_source_ and is signalled by michael@0: // MaybeScheduleNestingDeferredWork when returning from a nested loop, michael@0: // so that an outer loop will be able to perform the necessary tasks if it michael@0: // permits nestable tasks. michael@0: static void RunNestingDeferredWorkSource(void* info); michael@0: bool RunNestingDeferredWork(); michael@0: michael@0: // Schedules possible nesting-deferred work to be processed before the run michael@0: // loop goes to sleep, exits, or begins processing sources at the top of its michael@0: // loop. If this function detects that a nested loop had run since the michael@0: // previous attempt to schedule nesting-deferred work, it will schedule a michael@0: // call to RunNestingDeferredWorkSource. michael@0: void MaybeScheduleNestingDeferredWork(); michael@0: michael@0: // Observer callback responsible for performing idle-priority work, before michael@0: // the run loop goes to sleep. Associated with idle_work_observer_. michael@0: static void PreWaitObserver(CFRunLoopObserverRef observer, michael@0: CFRunLoopActivity activity, void* info); michael@0: michael@0: // Observer callback called before the run loop processes any sources. michael@0: // Associated with pre_source_observer_. michael@0: static void PreSourceObserver(CFRunLoopObserverRef observer, michael@0: CFRunLoopActivity activity, void* info); michael@0: michael@0: // Observer callback called when the run loop starts and stops, at the michael@0: // beginning and end of calls to CFRunLoopRun. This is used to maintain michael@0: // nesting_level_. Associated with enter_exit_observer_. michael@0: static void EnterExitObserver(CFRunLoopObserverRef observer, michael@0: CFRunLoopActivity activity, void* info); michael@0: michael@0: // Called by EnterExitObserver after performing maintenance on nesting_level_. michael@0: // This allows subclasses an opportunity to perform additional processing on michael@0: // the basis of run loops starting and stopping. michael@0: virtual void EnterExitRunLoop(CFRunLoopActivity activity); michael@0: michael@0: // IOKit power state change notification callback, called when the system michael@0: // enters and leaves the sleep state. michael@0: static void PowerStateNotification(void* info, io_service_t service, michael@0: uint32_t message_type, michael@0: void* message_argument); michael@0: michael@0: // The thread's run loop. michael@0: CFRunLoopRef run_loop_; michael@0: michael@0: // The timer, sources, and observers are described above alongside their michael@0: // callbacks. michael@0: CFRunLoopTimerRef delayed_work_timer_; michael@0: CFRunLoopSourceRef work_source_; michael@0: CFRunLoopSourceRef delayed_work_source_; michael@0: CFRunLoopSourceRef idle_work_source_; michael@0: CFRunLoopSourceRef nesting_deferred_work_source_; michael@0: CFRunLoopObserverRef pre_wait_observer_; michael@0: CFRunLoopObserverRef pre_source_observer_; michael@0: CFRunLoopObserverRef enter_exit_observer_; michael@0: michael@0: // Objects used for power state notification. See PowerStateNotification. michael@0: io_connect_t root_power_domain_; michael@0: IONotificationPortRef power_notification_port_; michael@0: io_object_t power_notification_object_; michael@0: michael@0: // (weak) Delegate passed as an argument to the innermost Run call. michael@0: Delegate* delegate_; michael@0: michael@0: // The time that delayed_work_timer_ is scheduled to fire. This is tracked michael@0: // independently of CFRunLoopTimerGetNextFireDate(delayed_work_timer_) michael@0: // to be able to reset the timer properly after waking from system sleep. michael@0: // See PowerStateNotification. michael@0: CFAbsoluteTime delayed_work_fire_time_; michael@0: michael@0: // The recursion depth of the currently-executing CFRunLoopRun loop on the michael@0: // run loop's thread. 0 if no run loops are running inside of whatever scope michael@0: // the object was created in. michael@0: int nesting_level_; michael@0: michael@0: // The recursion depth (calculated in the same way as nesting_level_) of the michael@0: // innermost executing CFRunLoopRun loop started by a call to Run. michael@0: int run_nesting_level_; michael@0: michael@0: // The deepest (numerically highest) recursion depth encountered since the michael@0: // most recent attempt to run nesting-deferred work. michael@0: int deepest_nesting_level_; michael@0: michael@0: // "Delegateless" work flags are set when work is ready to be performed but michael@0: // must wait until a delegate is available to process it. This can happen michael@0: // when a MessagePumpCFRunLoopBase is instantiated and work arrives without michael@0: // any call to Run on the stack. The Run method will check for delegateless michael@0: // work on entry and redispatch it as needed once a delegate is available. michael@0: bool delegateless_work_; michael@0: bool delegateless_delayed_work_; michael@0: bool delegateless_idle_work_; michael@0: michael@0: DISALLOW_COPY_AND_ASSIGN(MessagePumpCFRunLoopBase); michael@0: }; michael@0: michael@0: class MessagePumpCFRunLoop : public MessagePumpCFRunLoopBase { michael@0: public: michael@0: MessagePumpCFRunLoop(); michael@0: michael@0: virtual void DoRun(Delegate* delegate); michael@0: virtual void Quit(); michael@0: michael@0: private: michael@0: virtual void EnterExitRunLoop(CFRunLoopActivity activity); michael@0: michael@0: // True if Quit is called to stop the innermost MessagePump michael@0: // (innermost_quittable_) but some other CFRunLoopRun loop (nesting_level_) michael@0: // is running inside the MessagePump's innermost Run call. michael@0: bool quit_pending_; michael@0: michael@0: DISALLOW_COPY_AND_ASSIGN(MessagePumpCFRunLoop); michael@0: }; michael@0: michael@0: class MessagePumpNSRunLoop : public MessagePumpCFRunLoopBase { michael@0: public: michael@0: MessagePumpNSRunLoop(); michael@0: virtual ~MessagePumpNSRunLoop(); michael@0: michael@0: virtual void DoRun(Delegate* delegate); michael@0: virtual void Quit(); michael@0: michael@0: private: michael@0: // A source that doesn't do anything but provide something signalable michael@0: // attached to the run loop. This source will be signalled when Quit michael@0: // is called, to cause the loop to wake up so that it can stop. michael@0: CFRunLoopSourceRef quit_source_; michael@0: michael@0: // False after Quit is called. michael@0: bool keep_running_; michael@0: michael@0: DISALLOW_COPY_AND_ASSIGN(MessagePumpNSRunLoop); michael@0: }; michael@0: michael@0: class MessagePumpNSApplication : public MessagePumpCFRunLoopBase { michael@0: public: michael@0: MessagePumpNSApplication(); michael@0: michael@0: virtual void DoRun(Delegate* delegate); michael@0: virtual void Quit(); michael@0: michael@0: protected: michael@0: // Returns nil if NSApp is currently in the middle of calling -sendEvent. michael@0: virtual NSAutoreleasePool* CreateAutoreleasePool(); michael@0: michael@0: private: michael@0: // False after Quit is called. michael@0: bool keep_running_; michael@0: michael@0: // True if DoRun is managing its own run loop as opposed to letting michael@0: // -[NSApplication run] handle it. The outermost run loop in the application michael@0: // is managed by -[NSApplication run], inner run loops are handled by a loop michael@0: // in DoRun. michael@0: bool running_own_loop_; michael@0: michael@0: DISALLOW_COPY_AND_ASSIGN(MessagePumpNSApplication); michael@0: }; michael@0: michael@0: class MessagePumpMac { michael@0: public: michael@0: // Returns a new instance of MessagePumpNSApplication if called on the main michael@0: // thread. Otherwise, returns a new instance of MessagePumpNSRunLoop. michael@0: static MessagePump* Create(); michael@0: michael@0: private: michael@0: DISALLOW_IMPLICIT_CONSTRUCTORS(MessagePumpMac); michael@0: }; michael@0: michael@0: } // namespace base michael@0: michael@0: #endif // BASE_MESSAGE_PUMP_MAC_H_