1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/ipc/chromium/src/base/message_pump_mac.mm Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,752 @@ 1.4 +// Copyright (c) 2008 The Chromium Authors. All rights reserved. 1.5 +// Use of this source code is governed by a BSD-style license that can be 1.6 +// found in the LICENSE file. 1.7 + 1.8 +#include "base/message_pump_mac.h" 1.9 + 1.10 +#import <AppKit/AppKit.h> 1.11 +#import <Foundation/Foundation.h> 1.12 +#include <IOKit/IOMessage.h> 1.13 +#include <IOKit/pwr_mgt/IOPMLib.h> 1.14 + 1.15 +#include <limits> 1.16 + 1.17 +#import "base/chrome_application_mac.h" 1.18 +#include "base/logging.h" 1.19 +#include "base/time.h" 1.20 + 1.21 +namespace { 1.22 + 1.23 +void NoOp(void* info) { 1.24 +} 1.25 + 1.26 +const CFTimeInterval kCFTimeIntervalMax = 1.27 + std::numeric_limits<CFTimeInterval>::max(); 1.28 + 1.29 +} // namespace 1.30 + 1.31 +namespace base { 1.32 + 1.33 +// A scoper for autorelease pools created from message pump run loops. 1.34 +// Avoids dirtying up the ScopedNSAutoreleasePool interface for the rare 1.35 +// case where an autorelease pool needs to be passed in. 1.36 +class MessagePumpScopedAutoreleasePool { 1.37 + public: 1.38 + explicit MessagePumpScopedAutoreleasePool(MessagePumpCFRunLoopBase* pump) : 1.39 + pool_(pump->CreateAutoreleasePool()) { 1.40 + } 1.41 + ~MessagePumpScopedAutoreleasePool() { 1.42 + [pool_ drain]; 1.43 + } 1.44 + 1.45 + private: 1.46 + NSAutoreleasePool* pool_; 1.47 + DISALLOW_COPY_AND_ASSIGN(MessagePumpScopedAutoreleasePool); 1.48 +}; 1.49 + 1.50 +// Must be called on the run loop thread. 1.51 +MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase() 1.52 + : delegate_(NULL), 1.53 + delayed_work_fire_time_(kCFTimeIntervalMax), 1.54 + nesting_level_(0), 1.55 + run_nesting_level_(0), 1.56 + deepest_nesting_level_(0), 1.57 + delegateless_work_(false), 1.58 + delegateless_delayed_work_(false), 1.59 + delegateless_idle_work_(false) { 1.60 + run_loop_ = CFRunLoopGetCurrent(); 1.61 + CFRetain(run_loop_); 1.62 + 1.63 + // Set a repeating timer with a preposterous firing time and interval. The 1.64 + // timer will effectively never fire as-is. The firing time will be adjusted 1.65 + // as needed when ScheduleDelayedWork is called. 1.66 + CFRunLoopTimerContext timer_context = CFRunLoopTimerContext(); 1.67 + timer_context.info = this; 1.68 + delayed_work_timer_ = CFRunLoopTimerCreate(NULL, // allocator 1.69 + kCFTimeIntervalMax, // fire time 1.70 + kCFTimeIntervalMax, // interval 1.71 + 0, // flags 1.72 + 0, // priority 1.73 + RunDelayedWorkTimer, 1.74 + &timer_context); 1.75 + CFRunLoopAddTimer(run_loop_, delayed_work_timer_, kCFRunLoopCommonModes); 1.76 + 1.77 + CFRunLoopSourceContext source_context = CFRunLoopSourceContext(); 1.78 + source_context.info = this; 1.79 + source_context.perform = RunWorkSource; 1.80 + work_source_ = CFRunLoopSourceCreate(NULL, // allocator 1.81 + 1, // priority 1.82 + &source_context); 1.83 + CFRunLoopAddSource(run_loop_, work_source_, kCFRunLoopCommonModes); 1.84 + 1.85 + source_context.perform = RunDelayedWorkSource; 1.86 + delayed_work_source_ = CFRunLoopSourceCreate(NULL, // allocator 1.87 + 2, // priority 1.88 + &source_context); 1.89 + CFRunLoopAddSource(run_loop_, delayed_work_source_, kCFRunLoopCommonModes); 1.90 + 1.91 + source_context.perform = RunIdleWorkSource; 1.92 + idle_work_source_ = CFRunLoopSourceCreate(NULL, // allocator 1.93 + 3, // priority 1.94 + &source_context); 1.95 + CFRunLoopAddSource(run_loop_, idle_work_source_, kCFRunLoopCommonModes); 1.96 + 1.97 + source_context.perform = RunNestingDeferredWorkSource; 1.98 + nesting_deferred_work_source_ = CFRunLoopSourceCreate(NULL, // allocator 1.99 + 0, // priority 1.100 + &source_context); 1.101 + CFRunLoopAddSource(run_loop_, nesting_deferred_work_source_, 1.102 + kCFRunLoopCommonModes); 1.103 + 1.104 + CFRunLoopObserverContext observer_context = CFRunLoopObserverContext(); 1.105 + observer_context.info = this; 1.106 + pre_wait_observer_ = CFRunLoopObserverCreate(NULL, // allocator 1.107 + kCFRunLoopBeforeWaiting, 1.108 + true, // repeat 1.109 + 0, // priority 1.110 + PreWaitObserver, 1.111 + &observer_context); 1.112 + CFRunLoopAddObserver(run_loop_, pre_wait_observer_, kCFRunLoopCommonModes); 1.113 + 1.114 + pre_source_observer_ = CFRunLoopObserverCreate(NULL, // allocator 1.115 + kCFRunLoopBeforeSources, 1.116 + true, // repeat 1.117 + 0, // priority 1.118 + PreSourceObserver, 1.119 + &observer_context); 1.120 + CFRunLoopAddObserver(run_loop_, pre_source_observer_, kCFRunLoopCommonModes); 1.121 + 1.122 + enter_exit_observer_ = CFRunLoopObserverCreate(NULL, // allocator 1.123 + kCFRunLoopEntry | 1.124 + kCFRunLoopExit, 1.125 + true, // repeat 1.126 + 0, // priority 1.127 + EnterExitObserver, 1.128 + &observer_context); 1.129 + CFRunLoopAddObserver(run_loop_, enter_exit_observer_, kCFRunLoopCommonModes); 1.130 + 1.131 + root_power_domain_ = IORegisterForSystemPower(this, 1.132 + &power_notification_port_, 1.133 + PowerStateNotification, 1.134 + &power_notification_object_); 1.135 + if (root_power_domain_ != MACH_PORT_NULL) { 1.136 + CFRunLoopAddSource( 1.137 + run_loop_, 1.138 + IONotificationPortGetRunLoopSource(power_notification_port_), 1.139 + kCFRunLoopCommonModes); 1.140 + } 1.141 +} 1.142 + 1.143 +// Ideally called on the run loop thread. If other run loops were running 1.144 +// lower on the run loop thread's stack when this object was created, the 1.145 +// same number of run loops must be running when this object is destroyed. 1.146 +MessagePumpCFRunLoopBase::~MessagePumpCFRunLoopBase() { 1.147 + if (root_power_domain_ != MACH_PORT_NULL) { 1.148 + CFRunLoopRemoveSource( 1.149 + run_loop_, 1.150 + IONotificationPortGetRunLoopSource(power_notification_port_), 1.151 + kCFRunLoopCommonModes); 1.152 + IODeregisterForSystemPower(&power_notification_object_); 1.153 + IOServiceClose(root_power_domain_); 1.154 + IONotificationPortDestroy(power_notification_port_); 1.155 + } 1.156 + 1.157 + CFRunLoopRemoveObserver(run_loop_, enter_exit_observer_, 1.158 + kCFRunLoopCommonModes); 1.159 + CFRelease(enter_exit_observer_); 1.160 + 1.161 + CFRunLoopRemoveObserver(run_loop_, pre_source_observer_, 1.162 + kCFRunLoopCommonModes); 1.163 + CFRelease(pre_source_observer_); 1.164 + 1.165 + CFRunLoopRemoveObserver(run_loop_, pre_wait_observer_, 1.166 + kCFRunLoopCommonModes); 1.167 + CFRelease(pre_wait_observer_); 1.168 + 1.169 + CFRunLoopRemoveSource(run_loop_, nesting_deferred_work_source_, 1.170 + kCFRunLoopCommonModes); 1.171 + CFRelease(nesting_deferred_work_source_); 1.172 + 1.173 + CFRunLoopRemoveSource(run_loop_, idle_work_source_, kCFRunLoopCommonModes); 1.174 + CFRelease(idle_work_source_); 1.175 + 1.176 + CFRunLoopRemoveSource(run_loop_, delayed_work_source_, kCFRunLoopCommonModes); 1.177 + CFRelease(delayed_work_source_); 1.178 + 1.179 + CFRunLoopRemoveSource(run_loop_, work_source_, kCFRunLoopCommonModes); 1.180 + CFRelease(work_source_); 1.181 + 1.182 + CFRunLoopRemoveTimer(run_loop_, delayed_work_timer_, kCFRunLoopCommonModes); 1.183 + CFRelease(delayed_work_timer_); 1.184 + 1.185 + CFRelease(run_loop_); 1.186 +} 1.187 + 1.188 +// Must be called on the run loop thread. 1.189 +void MessagePumpCFRunLoopBase::Run(Delegate* delegate) { 1.190 + // nesting_level_ will be incremented in EnterExitRunLoop, so set 1.191 + // run_nesting_level_ accordingly. 1.192 + int last_run_nesting_level = run_nesting_level_; 1.193 + run_nesting_level_ = nesting_level_ + 1; 1.194 + 1.195 + Delegate* last_delegate = delegate_; 1.196 + delegate_ = delegate; 1.197 + 1.198 + if (delegate) { 1.199 + // If any work showed up but could not be dispatched for want of a 1.200 + // delegate, set it up for dispatch again now that a delegate is 1.201 + // available. 1.202 + if (delegateless_work_) { 1.203 + CFRunLoopSourceSignal(work_source_); 1.204 + delegateless_work_ = false; 1.205 + } 1.206 + if (delegateless_delayed_work_) { 1.207 + CFRunLoopSourceSignal(delayed_work_source_); 1.208 + delegateless_delayed_work_ = false; 1.209 + } 1.210 + if (delegateless_idle_work_) { 1.211 + CFRunLoopSourceSignal(idle_work_source_); 1.212 + delegateless_idle_work_ = false; 1.213 + } 1.214 + } 1.215 + 1.216 + DoRun(delegate); 1.217 + 1.218 + // Restore the previous state of the object. 1.219 + delegate_ = last_delegate; 1.220 + run_nesting_level_ = last_run_nesting_level; 1.221 +} 1.222 + 1.223 +// May be called on any thread. 1.224 +void MessagePumpCFRunLoopBase::ScheduleWork() { 1.225 + CFRunLoopSourceSignal(work_source_); 1.226 + CFRunLoopWakeUp(run_loop_); 1.227 +} 1.228 + 1.229 +// Must be called on the run loop thread. 1.230 +void MessagePumpCFRunLoopBase::ScheduleDelayedWork( 1.231 + const TimeTicks& delayed_work_time) { 1.232 + TimeDelta delta = delayed_work_time - TimeTicks::Now(); 1.233 + delayed_work_fire_time_ = CFAbsoluteTimeGetCurrent() + delta.InSecondsF(); 1.234 + CFRunLoopTimerSetNextFireDate(delayed_work_timer_, delayed_work_fire_time_); 1.235 +} 1.236 + 1.237 +// Called from the run loop. 1.238 +// static 1.239 +void MessagePumpCFRunLoopBase::RunDelayedWorkTimer(CFRunLoopTimerRef timer, 1.240 + void* info) { 1.241 + MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); 1.242 + 1.243 + // The timer won't fire again until it's reset. 1.244 + self->delayed_work_fire_time_ = kCFTimeIntervalMax; 1.245 + 1.246 + // CFRunLoopTimers fire outside of the priority scheme for CFRunLoopSources. 1.247 + // In order to establish the proper priority where delegate_->DoDelayedWork 1.248 + // can only be called if delegate_->DoWork returns false, the timer used 1.249 + // to schedule delayed work must signal a CFRunLoopSource set at a lower 1.250 + // priority than the one used for delegate_->DoWork. 1.251 + CFRunLoopSourceSignal(self->delayed_work_source_); 1.252 +} 1.253 + 1.254 +// Called from the run loop. 1.255 +// static 1.256 +void MessagePumpCFRunLoopBase::RunWorkSource(void* info) { 1.257 + MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); 1.258 + self->RunWork(); 1.259 +} 1.260 + 1.261 +// Called by MessagePumpCFRunLoopBase::RunWorkSource. 1.262 +bool MessagePumpCFRunLoopBase::RunWork() { 1.263 + if (!delegate_) { 1.264 + // This point can be reached with a NULL delegate_ if Run is not on the 1.265 + // stack but foreign code is spinning the CFRunLoop. Arrange to come back 1.266 + // here when a delegate is available. 1.267 + delegateless_work_ = true; 1.268 + return false; 1.269 + } 1.270 + 1.271 + // The NSApplication-based run loop only drains the autorelease pool at each 1.272 + // UI event (NSEvent). The autorelease pool is not drained for each 1.273 + // CFRunLoopSource target that's run. Use a local pool for any autoreleased 1.274 + // objects if the app is not currently handling a UI event to ensure they're 1.275 + // released promptly even in the absence of UI events. 1.276 + MessagePumpScopedAutoreleasePool autorelease_pool(this); 1.277 + 1.278 + // Call DoWork once, and if something was done, arrange to come back here 1.279 + // again as long as the loop is still running. 1.280 + bool did_work = delegate_->DoWork(); 1.281 + if (did_work) { 1.282 + CFRunLoopSourceSignal(work_source_); 1.283 + } 1.284 + 1.285 + return did_work; 1.286 +} 1.287 + 1.288 +// Called from the run loop. 1.289 +// static 1.290 +void MessagePumpCFRunLoopBase::RunDelayedWorkSource(void* info) { 1.291 + MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); 1.292 + self->RunDelayedWork(); 1.293 +} 1.294 + 1.295 +// Called by MessagePumpCFRunLoopBase::RunDelayedWorkSource. 1.296 +bool MessagePumpCFRunLoopBase::RunDelayedWork() { 1.297 + if (!delegate_) { 1.298 + // This point can be reached with a NULL delegate_ if Run is not on the 1.299 + // stack but foreign code is spinning the CFRunLoop. Arrange to come back 1.300 + // here when a delegate is available. 1.301 + delegateless_delayed_work_ = true; 1.302 + return false; 1.303 + } 1.304 + 1.305 + // The NSApplication-based run loop only drains the autorelease pool at each 1.306 + // UI event (NSEvent). The autorelease pool is not drained for each 1.307 + // CFRunLoopSource target that's run. Use a local pool for any autoreleased 1.308 + // objects if the app is not currently handling a UI event to ensure they're 1.309 + // released promptly even in the absence of UI events. 1.310 + MessagePumpScopedAutoreleasePool autorelease_pool(this); 1.311 + 1.312 + TimeTicks next_time; 1.313 + delegate_->DoDelayedWork(&next_time); 1.314 + 1.315 + bool more_work = !next_time.is_null(); 1.316 + if (more_work) { 1.317 + TimeDelta delay = next_time - TimeTicks::Now(); 1.318 + if (delay > TimeDelta()) { 1.319 + // There's more delayed work to be done in the future. 1.320 + ScheduleDelayedWork(next_time); 1.321 + } else { 1.322 + // There's more delayed work to be done, and its time is in the past. 1.323 + // Arrange to come back here directly as long as the loop is still 1.324 + // running. 1.325 + CFRunLoopSourceSignal(delayed_work_source_); 1.326 + } 1.327 + } 1.328 + 1.329 + return more_work; 1.330 +} 1.331 + 1.332 +// Called from the run loop. 1.333 +// static 1.334 +void MessagePumpCFRunLoopBase::RunIdleWorkSource(void* info) { 1.335 + MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); 1.336 + self->RunIdleWork(); 1.337 +} 1.338 + 1.339 +// Called by MessagePumpCFRunLoopBase::RunIdleWorkSource. 1.340 +bool MessagePumpCFRunLoopBase::RunIdleWork() { 1.341 + if (!delegate_) { 1.342 + // This point can be reached with a NULL delegate_ if Run is not on the 1.343 + // stack but foreign code is spinning the CFRunLoop. Arrange to come back 1.344 + // here when a delegate is available. 1.345 + delegateless_idle_work_ = true; 1.346 + return false; 1.347 + } 1.348 + 1.349 + // The NSApplication-based run loop only drains the autorelease pool at each 1.350 + // UI event (NSEvent). The autorelease pool is not drained for each 1.351 + // CFRunLoopSource target that's run. Use a local pool for any autoreleased 1.352 + // objects if the app is not currently handling a UI event to ensure they're 1.353 + // released promptly even in the absence of UI events. 1.354 + MessagePumpScopedAutoreleasePool autorelease_pool(this); 1.355 + 1.356 + // Call DoIdleWork once, and if something was done, arrange to come back here 1.357 + // again as long as the loop is still running. 1.358 + bool did_work = delegate_->DoIdleWork(); 1.359 + if (did_work) { 1.360 + CFRunLoopSourceSignal(idle_work_source_); 1.361 + } 1.362 + 1.363 + return did_work; 1.364 +} 1.365 + 1.366 +// Called from the run loop. 1.367 +// static 1.368 +void MessagePumpCFRunLoopBase::RunNestingDeferredWorkSource(void* info) { 1.369 + MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); 1.370 + self->RunNestingDeferredWork(); 1.371 +} 1.372 + 1.373 +// Called by MessagePumpCFRunLoopBase::RunNestingDeferredWorkSource. 1.374 +bool MessagePumpCFRunLoopBase::RunNestingDeferredWork() { 1.375 + if (!delegate_) { 1.376 + // This point can be reached with a NULL delegate_ if Run is not on the 1.377 + // stack but foreign code is spinning the CFRunLoop. There's no sense in 1.378 + // attempting to do any work or signalling the work sources because 1.379 + // without a delegate, work is not possible. 1.380 + return false; 1.381 + } 1.382 + 1.383 + // Immediately try work in priority order. 1.384 + if (!RunWork()) { 1.385 + if (!RunDelayedWork()) { 1.386 + if (!RunIdleWork()) { 1.387 + return false; 1.388 + } 1.389 + } else { 1.390 + // There was no work, and delayed work was done. Arrange for the loop 1.391 + // to try non-nestable idle work on a subsequent pass. 1.392 + CFRunLoopSourceSignal(idle_work_source_); 1.393 + } 1.394 + } else { 1.395 + // Work was done. Arrange for the loop to try non-nestable delayed and 1.396 + // idle work on a subsequent pass. 1.397 + CFRunLoopSourceSignal(delayed_work_source_); 1.398 + CFRunLoopSourceSignal(idle_work_source_); 1.399 + } 1.400 + 1.401 + return true; 1.402 +} 1.403 + 1.404 +// Called before the run loop goes to sleep or exits, or processes sources. 1.405 +void MessagePumpCFRunLoopBase::MaybeScheduleNestingDeferredWork() { 1.406 + // deepest_nesting_level_ is set as run loops are entered. If the deepest 1.407 + // level encountered is deeper than the current level, a nested loop 1.408 + // (relative to the current level) ran since the last time nesting-deferred 1.409 + // work was scheduled. When that situation is encountered, schedule 1.410 + // nesting-deferred work in case any work was deferred because nested work 1.411 + // was disallowed. 1.412 + if (deepest_nesting_level_ > nesting_level_) { 1.413 + deepest_nesting_level_ = nesting_level_; 1.414 + CFRunLoopSourceSignal(nesting_deferred_work_source_); 1.415 + } 1.416 +} 1.417 + 1.418 +// Called from the run loop. 1.419 +// static 1.420 +void MessagePumpCFRunLoopBase::PreWaitObserver(CFRunLoopObserverRef observer, 1.421 + CFRunLoopActivity activity, 1.422 + void* info) { 1.423 + MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); 1.424 + 1.425 + // Attempt to do some idle work before going to sleep. 1.426 + self->RunIdleWork(); 1.427 + 1.428 + // The run loop is about to go to sleep. If any of the work done since it 1.429 + // started or woke up resulted in a nested run loop running, 1.430 + // nesting-deferred work may have accumulated. Schedule it for processing 1.431 + // if appropriate. 1.432 + self->MaybeScheduleNestingDeferredWork(); 1.433 +} 1.434 + 1.435 +// Called from the run loop. 1.436 +// static 1.437 +void MessagePumpCFRunLoopBase::PreSourceObserver(CFRunLoopObserverRef observer, 1.438 + CFRunLoopActivity activity, 1.439 + void* info) { 1.440 + MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); 1.441 + 1.442 + // The run loop has reached the top of the loop and is about to begin 1.443 + // processing sources. If the last iteration of the loop at this nesting 1.444 + // level did not sleep or exit, nesting-deferred work may have accumulated 1.445 + // if a nested loop ran. Schedule nesting-deferred work for processing if 1.446 + // appropriate. 1.447 + self->MaybeScheduleNestingDeferredWork(); 1.448 +} 1.449 + 1.450 +// Called from the run loop. 1.451 +// static 1.452 +void MessagePumpCFRunLoopBase::EnterExitObserver(CFRunLoopObserverRef observer, 1.453 + CFRunLoopActivity activity, 1.454 + void* info) { 1.455 + MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); 1.456 + 1.457 + switch (activity) { 1.458 + case kCFRunLoopEntry: 1.459 + ++self->nesting_level_; 1.460 + if (self->nesting_level_ > self->deepest_nesting_level_) { 1.461 + self->deepest_nesting_level_ = self->nesting_level_; 1.462 + } 1.463 + break; 1.464 + 1.465 + case kCFRunLoopExit: 1.466 + // Not all run loops go to sleep. If a run loop is stopped before it 1.467 + // goes to sleep due to a CFRunLoopStop call, or if the timeout passed 1.468 + // to CFRunLoopRunInMode expires, the run loop may proceed directly from 1.469 + // handling sources to exiting without any sleep. This most commonly 1.470 + // occurs when CFRunLoopRunInMode is passed a timeout of 0, causing it 1.471 + // to make a single pass through the loop and exit without sleep. Some 1.472 + // native loops use CFRunLoop in this way. Because PreWaitObserver will 1.473 + // not be called in these case, MaybeScheduleNestingDeferredWork needs 1.474 + // to be called here, as the run loop exits. 1.475 + // 1.476 + // MaybeScheduleNestingDeferredWork consults self->nesting_level_ 1.477 + // to determine whether to schedule nesting-deferred work. It expects 1.478 + // the nesting level to be set to the depth of the loop that is going 1.479 + // to sleep or exiting. It must be called before decrementing the 1.480 + // value so that the value still corresponds to the level of the exiting 1.481 + // loop. 1.482 + self->MaybeScheduleNestingDeferredWork(); 1.483 + --self->nesting_level_; 1.484 + break; 1.485 + 1.486 + default: 1.487 + break; 1.488 + } 1.489 + 1.490 + self->EnterExitRunLoop(activity); 1.491 +} 1.492 + 1.493 +// Called from the run loop. 1.494 +// static 1.495 +void MessagePumpCFRunLoopBase::PowerStateNotification(void* info, 1.496 + io_service_t service, 1.497 + uint32_t message_type, 1.498 + void* message_argument) { 1.499 + // CFRunLoopTimer (NSTimer) is scheduled in terms of CFAbsoluteTime, which 1.500 + // measures the number of seconds since 2001-01-01 00:00:00.0 Z. It is 1.501 + // implemented in terms of kernel ticks, as in mach_absolute_time. While an 1.502 + // offset and scale factor can be applied to convert between the two time 1.503 + // bases at any time after boot, the kernel clock stops while the system is 1.504 + // asleep, altering the offset. (The offset will also change when the 1.505 + // real-time clock is adjusted.) CFRunLoopTimers are not readjusted to take 1.506 + // this into account when the system wakes up, so any timers that were 1.507 + // pending while the system was asleep will be delayed by the sleep 1.508 + // duration. 1.509 + // 1.510 + // The MessagePump interface assumes that scheduled delayed work will be 1.511 + // performed at the time ScheduleDelayedWork was asked to perform it. The 1.512 + // delay caused by the CFRunLoopTimer not firing at the appropriate time 1.513 + // results in a stall of queued delayed work when the system wakes up. 1.514 + // With this limitation, scheduled work would not be performed until 1.515 + // (system wake time + scheduled work time - system sleep time), while it 1.516 + // would be expected to be performed at (scheduled work time). 1.517 + // 1.518 + // To work around this problem, when the system wakes up from sleep, if a 1.519 + // delayed work timer is pending, it is rescheduled to fire at the original 1.520 + // time that it was scheduled to fire. 1.521 + // 1.522 + // This mechanism is not resilient if the real-time clock does not maintain 1.523 + // stable time while the system is sleeping, but it matches the behavior of 1.524 + // the various other MessagePump implementations, and MessageLoop seems to 1.525 + // be limited in the same way. 1.526 + // 1.527 + // References 1.528 + // - Chris Kane, "NSTimer and deep sleep," cocoa-dev@lists.apple.com, 1.529 + // http://lists.apple.com/archives/Cocoa-dev/2002/May/msg01547.html 1.530 + // - Apple Technical Q&A QA1340, "Registering and unregistering for sleep 1.531 + // and wake notifications," 1.532 + // http://developer.apple.com/mac/library/qa/qa2004/qa1340.html 1.533 + // - Core Foundation source code, CF-550/CFRunLoop.c and CF-550/CFDate.c, 1.534 + // http://www.opensource.apple.com/ 1.535 + 1.536 + MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); 1.537 + 1.538 + switch (message_type) { 1.539 + case kIOMessageSystemWillPowerOn: 1.540 + if (self->delayed_work_fire_time_ != kCFTimeIntervalMax) { 1.541 + CFRunLoopTimerSetNextFireDate(self->delayed_work_timer_, 1.542 + self->delayed_work_fire_time_); 1.543 + } 1.544 + break; 1.545 + 1.546 + case kIOMessageSystemWillSleep: 1.547 + case kIOMessageCanSystemSleep: 1.548 + // The system will wait for 30 seconds before entering sleep if neither 1.549 + // IOAllowPowerChange nor IOCancelPowerChange are called. That would be 1.550 + // pretty antisocial. 1.551 + IOAllowPowerChange(self->root_power_domain_, 1.552 + reinterpret_cast<long>(message_argument)); 1.553 + break; 1.554 + 1.555 + default: 1.556 + break; 1.557 + } 1.558 +} 1.559 + 1.560 +// Called by MessagePumpCFRunLoopBase::EnterExitRunLoop. The default 1.561 +// implementation is a no-op. 1.562 +void MessagePumpCFRunLoopBase::EnterExitRunLoop(CFRunLoopActivity activity) { 1.563 +} 1.564 + 1.565 +// Base version returns a standard NSAutoreleasePool. 1.566 +NSAutoreleasePool* MessagePumpCFRunLoopBase::CreateAutoreleasePool() { 1.567 + return [[NSAutoreleasePool alloc] init]; 1.568 +} 1.569 + 1.570 +MessagePumpCFRunLoop::MessagePumpCFRunLoop() 1.571 + : quit_pending_(false) { 1.572 +} 1.573 + 1.574 +// Called by MessagePumpCFRunLoopBase::DoRun. If other CFRunLoopRun loops were 1.575 +// running lower on the run loop thread's stack when this object was created, 1.576 +// the same number of CFRunLoopRun loops must be running for the outermost call 1.577 +// to Run. Run/DoRun are reentrant after that point. 1.578 +void MessagePumpCFRunLoop::DoRun(Delegate* delegate) { 1.579 + // This is completely identical to calling CFRunLoopRun(), except autorelease 1.580 + // pool management is introduced. 1.581 + int result; 1.582 + do { 1.583 + MessagePumpScopedAutoreleasePool autorelease_pool(this); 1.584 + result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.585 + kCFTimeIntervalMax, 1.586 + false); 1.587 + } while (result != kCFRunLoopRunStopped && result != kCFRunLoopRunFinished); 1.588 +} 1.589 + 1.590 +// Must be called on the run loop thread. 1.591 +void MessagePumpCFRunLoop::Quit() { 1.592 + // Stop the innermost run loop managed by this MessagePumpCFRunLoop object. 1.593 + if (nesting_level() == run_nesting_level()) { 1.594 + // This object is running the innermost loop, just stop it. 1.595 + CFRunLoopStop(run_loop()); 1.596 + } else { 1.597 + // There's another loop running inside the loop managed by this object. 1.598 + // In other words, someone else called CFRunLoopRunInMode on the same 1.599 + // thread, deeper on the stack than the deepest Run call. Don't preempt 1.600 + // other run loops, just mark this object to quit the innermost Run as 1.601 + // soon as the other inner loops not managed by Run are done. 1.602 + quit_pending_ = true; 1.603 + } 1.604 +} 1.605 + 1.606 +// Called by MessagePumpCFRunLoopBase::EnterExitObserver. 1.607 +void MessagePumpCFRunLoop::EnterExitRunLoop(CFRunLoopActivity activity) { 1.608 + if (activity == kCFRunLoopExit && 1.609 + nesting_level() == run_nesting_level() && 1.610 + quit_pending_) { 1.611 + // Quit was called while loops other than those managed by this object 1.612 + // were running further inside a run loop managed by this object. Now 1.613 + // that all unmanaged inner run loops are gone, stop the loop running 1.614 + // just inside Run. 1.615 + CFRunLoopStop(run_loop()); 1.616 + quit_pending_ = false; 1.617 + } 1.618 +} 1.619 + 1.620 +MessagePumpNSRunLoop::MessagePumpNSRunLoop() 1.621 + : keep_running_(true) { 1.622 + CFRunLoopSourceContext source_context = CFRunLoopSourceContext(); 1.623 + source_context.perform = NoOp; 1.624 + quit_source_ = CFRunLoopSourceCreate(NULL, // allocator 1.625 + 0, // priority 1.626 + &source_context); 1.627 + CFRunLoopAddSource(run_loop(), quit_source_, kCFRunLoopCommonModes); 1.628 +} 1.629 + 1.630 +MessagePumpNSRunLoop::~MessagePumpNSRunLoop() { 1.631 + CFRunLoopRemoveSource(run_loop(), quit_source_, kCFRunLoopCommonModes); 1.632 + CFRelease(quit_source_); 1.633 +} 1.634 + 1.635 +void MessagePumpNSRunLoop::DoRun(Delegate* delegate) { 1.636 + while (keep_running_) { 1.637 + // NSRunLoop manages autorelease pools itself. 1.638 + [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode 1.639 + beforeDate:[NSDate distantFuture]]; 1.640 + } 1.641 + 1.642 + keep_running_ = true; 1.643 +} 1.644 + 1.645 +void MessagePumpNSRunLoop::Quit() { 1.646 + keep_running_ = false; 1.647 + CFRunLoopSourceSignal(quit_source_); 1.648 + CFRunLoopWakeUp(run_loop()); 1.649 +} 1.650 + 1.651 +MessagePumpNSApplication::MessagePumpNSApplication() 1.652 + : keep_running_(true), 1.653 + running_own_loop_(false) { 1.654 +} 1.655 + 1.656 +void MessagePumpNSApplication::DoRun(Delegate* delegate) { 1.657 + bool last_running_own_loop_ = running_own_loop_; 1.658 + 1.659 + // TODO(dmaclach): Get rid of this gratuitous sharedApplication. 1.660 + // Tests should be setting up their applications on their own. 1.661 + [CrApplication sharedApplication]; 1.662 + 1.663 + if (![NSApp isRunning]) { 1.664 + running_own_loop_ = false; 1.665 + // NSApplication manages autorelease pools itself when run this way. 1.666 + [NSApp run]; 1.667 + } else { 1.668 + running_own_loop_ = true; 1.669 + NSDate* distant_future = [NSDate distantFuture]; 1.670 + while (keep_running_) { 1.671 + MessagePumpScopedAutoreleasePool autorelease_pool(this); 1.672 + NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask 1.673 + untilDate:distant_future 1.674 + inMode:NSDefaultRunLoopMode 1.675 + dequeue:YES]; 1.676 + if (event) { 1.677 + [NSApp sendEvent:event]; 1.678 + } 1.679 + } 1.680 + keep_running_ = true; 1.681 + } 1.682 + 1.683 + running_own_loop_ = last_running_own_loop_; 1.684 +} 1.685 + 1.686 +void MessagePumpNSApplication::Quit() { 1.687 + if (!running_own_loop_) { 1.688 + [NSApp stop:nil]; 1.689 + } else { 1.690 + keep_running_ = false; 1.691 + } 1.692 + 1.693 + // Send a fake event to wake the loop up. 1.694 + [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined 1.695 + location:NSMakePoint(0, 0) 1.696 + modifierFlags:0 1.697 + timestamp:0 1.698 + windowNumber:0 1.699 + context:NULL 1.700 + subtype:0 1.701 + data1:0 1.702 + data2:0] 1.703 + atStart:NO]; 1.704 +} 1.705 + 1.706 +// Prevents an autorelease pool from being created if the app is in the midst of 1.707 +// handling a UI event because various parts of AppKit depend on objects that 1.708 +// are created while handling a UI event to be autoreleased in the event loop. 1.709 +// An example of this is NSWindowController. When a window with a window 1.710 +// controller is closed it goes through a stack like this: 1.711 +// (Several stack frames elided for clarity) 1.712 +// 1.713 +// #0 [NSWindowController autorelease] 1.714 +// #1 DoAClose 1.715 +// #2 MessagePumpCFRunLoopBase::DoWork() 1.716 +// #3 [NSRunLoop run] 1.717 +// #4 [NSButton performClick:] 1.718 +// #5 [NSWindow sendEvent:] 1.719 +// #6 [NSApp sendEvent:] 1.720 +// #7 [NSApp run] 1.721 +// 1.722 +// -performClick: spins a nested run loop. If the pool created in DoWork was a 1.723 +// standard NSAutoreleasePool, it would release the objects that were 1.724 +// autoreleased into it once DoWork released it. This would cause the window 1.725 +// controller, which autoreleased itself in frame #0, to release itself, and 1.726 +// possibly free itself. Unfortunately this window controller controls the 1.727 +// window in frame #5. When the stack is unwound to frame #5, the window would 1.728 +// no longer exists and crashes may occur. Apple gets around this by never 1.729 +// releasing the pool it creates in frame #4, and letting frame #7 clean it up 1.730 +// when it cleans up the pool that wraps frame #7. When an autorelease pool is 1.731 +// released it releases all other pools that were created after it on the 1.732 +// autorelease pool stack. 1.733 +// 1.734 +// CrApplication is responsible for setting handlingSendEvent to true just 1.735 +// before it sends the event throught the event handling mechanism, and 1.736 +// returning it to its previous value once the event has been sent. 1.737 +NSAutoreleasePool* MessagePumpNSApplication::CreateAutoreleasePool() { 1.738 + NSAutoreleasePool* pool = nil; 1.739 + DCHECK([NSApp isKindOfClass:[CrApplication class]]); 1.740 + if (![static_cast<CrApplication*>(NSApp) isHandlingSendEvent]) { 1.741 + pool = MessagePumpCFRunLoopBase::CreateAutoreleasePool(); 1.742 + } 1.743 + return pool; 1.744 +} 1.745 + 1.746 +// static 1.747 +MessagePump* MessagePumpMac::Create() { 1.748 + if ([NSThread isMainThread]) { 1.749 + return new MessagePumpNSApplication; 1.750 + } 1.751 + 1.752 + return new MessagePumpNSRunLoop; 1.753 +} 1.754 + 1.755 +} // namespace base