Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | // Copyright (c) 2008 The Chromium Authors. All rights reserved. |
michael@0 | 2 | // Use of this source code is governed by a BSD-style license that can be |
michael@0 | 3 | // found in the LICENSE file. |
michael@0 | 4 | |
michael@0 | 5 | #include "base/message_pump_mac.h" |
michael@0 | 6 | |
michael@0 | 7 | #import <AppKit/AppKit.h> |
michael@0 | 8 | #import <Foundation/Foundation.h> |
michael@0 | 9 | #include <IOKit/IOMessage.h> |
michael@0 | 10 | #include <IOKit/pwr_mgt/IOPMLib.h> |
michael@0 | 11 | |
michael@0 | 12 | #include <limits> |
michael@0 | 13 | |
michael@0 | 14 | #import "base/chrome_application_mac.h" |
michael@0 | 15 | #include "base/logging.h" |
michael@0 | 16 | #include "base/time.h" |
michael@0 | 17 | |
michael@0 | 18 | namespace { |
michael@0 | 19 | |
michael@0 | 20 | void NoOp(void* info) { |
michael@0 | 21 | } |
michael@0 | 22 | |
michael@0 | 23 | const CFTimeInterval kCFTimeIntervalMax = |
michael@0 | 24 | std::numeric_limits<CFTimeInterval>::max(); |
michael@0 | 25 | |
michael@0 | 26 | } // namespace |
michael@0 | 27 | |
michael@0 | 28 | namespace base { |
michael@0 | 29 | |
michael@0 | 30 | // A scoper for autorelease pools created from message pump run loops. |
michael@0 | 31 | // Avoids dirtying up the ScopedNSAutoreleasePool interface for the rare |
michael@0 | 32 | // case where an autorelease pool needs to be passed in. |
michael@0 | 33 | class MessagePumpScopedAutoreleasePool { |
michael@0 | 34 | public: |
michael@0 | 35 | explicit MessagePumpScopedAutoreleasePool(MessagePumpCFRunLoopBase* pump) : |
michael@0 | 36 | pool_(pump->CreateAutoreleasePool()) { |
michael@0 | 37 | } |
michael@0 | 38 | ~MessagePumpScopedAutoreleasePool() { |
michael@0 | 39 | [pool_ drain]; |
michael@0 | 40 | } |
michael@0 | 41 | |
michael@0 | 42 | private: |
michael@0 | 43 | NSAutoreleasePool* pool_; |
michael@0 | 44 | DISALLOW_COPY_AND_ASSIGN(MessagePumpScopedAutoreleasePool); |
michael@0 | 45 | }; |
michael@0 | 46 | |
michael@0 | 47 | // Must be called on the run loop thread. |
michael@0 | 48 | MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase() |
michael@0 | 49 | : delegate_(NULL), |
michael@0 | 50 | delayed_work_fire_time_(kCFTimeIntervalMax), |
michael@0 | 51 | nesting_level_(0), |
michael@0 | 52 | run_nesting_level_(0), |
michael@0 | 53 | deepest_nesting_level_(0), |
michael@0 | 54 | delegateless_work_(false), |
michael@0 | 55 | delegateless_delayed_work_(false), |
michael@0 | 56 | delegateless_idle_work_(false) { |
michael@0 | 57 | run_loop_ = CFRunLoopGetCurrent(); |
michael@0 | 58 | CFRetain(run_loop_); |
michael@0 | 59 | |
michael@0 | 60 | // Set a repeating timer with a preposterous firing time and interval. The |
michael@0 | 61 | // timer will effectively never fire as-is. The firing time will be adjusted |
michael@0 | 62 | // as needed when ScheduleDelayedWork is called. |
michael@0 | 63 | CFRunLoopTimerContext timer_context = CFRunLoopTimerContext(); |
michael@0 | 64 | timer_context.info = this; |
michael@0 | 65 | delayed_work_timer_ = CFRunLoopTimerCreate(NULL, // allocator |
michael@0 | 66 | kCFTimeIntervalMax, // fire time |
michael@0 | 67 | kCFTimeIntervalMax, // interval |
michael@0 | 68 | 0, // flags |
michael@0 | 69 | 0, // priority |
michael@0 | 70 | RunDelayedWorkTimer, |
michael@0 | 71 | &timer_context); |
michael@0 | 72 | CFRunLoopAddTimer(run_loop_, delayed_work_timer_, kCFRunLoopCommonModes); |
michael@0 | 73 | |
michael@0 | 74 | CFRunLoopSourceContext source_context = CFRunLoopSourceContext(); |
michael@0 | 75 | source_context.info = this; |
michael@0 | 76 | source_context.perform = RunWorkSource; |
michael@0 | 77 | work_source_ = CFRunLoopSourceCreate(NULL, // allocator |
michael@0 | 78 | 1, // priority |
michael@0 | 79 | &source_context); |
michael@0 | 80 | CFRunLoopAddSource(run_loop_, work_source_, kCFRunLoopCommonModes); |
michael@0 | 81 | |
michael@0 | 82 | source_context.perform = RunDelayedWorkSource; |
michael@0 | 83 | delayed_work_source_ = CFRunLoopSourceCreate(NULL, // allocator |
michael@0 | 84 | 2, // priority |
michael@0 | 85 | &source_context); |
michael@0 | 86 | CFRunLoopAddSource(run_loop_, delayed_work_source_, kCFRunLoopCommonModes); |
michael@0 | 87 | |
michael@0 | 88 | source_context.perform = RunIdleWorkSource; |
michael@0 | 89 | idle_work_source_ = CFRunLoopSourceCreate(NULL, // allocator |
michael@0 | 90 | 3, // priority |
michael@0 | 91 | &source_context); |
michael@0 | 92 | CFRunLoopAddSource(run_loop_, idle_work_source_, kCFRunLoopCommonModes); |
michael@0 | 93 | |
michael@0 | 94 | source_context.perform = RunNestingDeferredWorkSource; |
michael@0 | 95 | nesting_deferred_work_source_ = CFRunLoopSourceCreate(NULL, // allocator |
michael@0 | 96 | 0, // priority |
michael@0 | 97 | &source_context); |
michael@0 | 98 | CFRunLoopAddSource(run_loop_, nesting_deferred_work_source_, |
michael@0 | 99 | kCFRunLoopCommonModes); |
michael@0 | 100 | |
michael@0 | 101 | CFRunLoopObserverContext observer_context = CFRunLoopObserverContext(); |
michael@0 | 102 | observer_context.info = this; |
michael@0 | 103 | pre_wait_observer_ = CFRunLoopObserverCreate(NULL, // allocator |
michael@0 | 104 | kCFRunLoopBeforeWaiting, |
michael@0 | 105 | true, // repeat |
michael@0 | 106 | 0, // priority |
michael@0 | 107 | PreWaitObserver, |
michael@0 | 108 | &observer_context); |
michael@0 | 109 | CFRunLoopAddObserver(run_loop_, pre_wait_observer_, kCFRunLoopCommonModes); |
michael@0 | 110 | |
michael@0 | 111 | pre_source_observer_ = CFRunLoopObserverCreate(NULL, // allocator |
michael@0 | 112 | kCFRunLoopBeforeSources, |
michael@0 | 113 | true, // repeat |
michael@0 | 114 | 0, // priority |
michael@0 | 115 | PreSourceObserver, |
michael@0 | 116 | &observer_context); |
michael@0 | 117 | CFRunLoopAddObserver(run_loop_, pre_source_observer_, kCFRunLoopCommonModes); |
michael@0 | 118 | |
michael@0 | 119 | enter_exit_observer_ = CFRunLoopObserverCreate(NULL, // allocator |
michael@0 | 120 | kCFRunLoopEntry | |
michael@0 | 121 | kCFRunLoopExit, |
michael@0 | 122 | true, // repeat |
michael@0 | 123 | 0, // priority |
michael@0 | 124 | EnterExitObserver, |
michael@0 | 125 | &observer_context); |
michael@0 | 126 | CFRunLoopAddObserver(run_loop_, enter_exit_observer_, kCFRunLoopCommonModes); |
michael@0 | 127 | |
michael@0 | 128 | root_power_domain_ = IORegisterForSystemPower(this, |
michael@0 | 129 | &power_notification_port_, |
michael@0 | 130 | PowerStateNotification, |
michael@0 | 131 | &power_notification_object_); |
michael@0 | 132 | if (root_power_domain_ != MACH_PORT_NULL) { |
michael@0 | 133 | CFRunLoopAddSource( |
michael@0 | 134 | run_loop_, |
michael@0 | 135 | IONotificationPortGetRunLoopSource(power_notification_port_), |
michael@0 | 136 | kCFRunLoopCommonModes); |
michael@0 | 137 | } |
michael@0 | 138 | } |
michael@0 | 139 | |
michael@0 | 140 | // Ideally called on the run loop thread. If other run loops were running |
michael@0 | 141 | // lower on the run loop thread's stack when this object was created, the |
michael@0 | 142 | // same number of run loops must be running when this object is destroyed. |
michael@0 | 143 | MessagePumpCFRunLoopBase::~MessagePumpCFRunLoopBase() { |
michael@0 | 144 | if (root_power_domain_ != MACH_PORT_NULL) { |
michael@0 | 145 | CFRunLoopRemoveSource( |
michael@0 | 146 | run_loop_, |
michael@0 | 147 | IONotificationPortGetRunLoopSource(power_notification_port_), |
michael@0 | 148 | kCFRunLoopCommonModes); |
michael@0 | 149 | IODeregisterForSystemPower(&power_notification_object_); |
michael@0 | 150 | IOServiceClose(root_power_domain_); |
michael@0 | 151 | IONotificationPortDestroy(power_notification_port_); |
michael@0 | 152 | } |
michael@0 | 153 | |
michael@0 | 154 | CFRunLoopRemoveObserver(run_loop_, enter_exit_observer_, |
michael@0 | 155 | kCFRunLoopCommonModes); |
michael@0 | 156 | CFRelease(enter_exit_observer_); |
michael@0 | 157 | |
michael@0 | 158 | CFRunLoopRemoveObserver(run_loop_, pre_source_observer_, |
michael@0 | 159 | kCFRunLoopCommonModes); |
michael@0 | 160 | CFRelease(pre_source_observer_); |
michael@0 | 161 | |
michael@0 | 162 | CFRunLoopRemoveObserver(run_loop_, pre_wait_observer_, |
michael@0 | 163 | kCFRunLoopCommonModes); |
michael@0 | 164 | CFRelease(pre_wait_observer_); |
michael@0 | 165 | |
michael@0 | 166 | CFRunLoopRemoveSource(run_loop_, nesting_deferred_work_source_, |
michael@0 | 167 | kCFRunLoopCommonModes); |
michael@0 | 168 | CFRelease(nesting_deferred_work_source_); |
michael@0 | 169 | |
michael@0 | 170 | CFRunLoopRemoveSource(run_loop_, idle_work_source_, kCFRunLoopCommonModes); |
michael@0 | 171 | CFRelease(idle_work_source_); |
michael@0 | 172 | |
michael@0 | 173 | CFRunLoopRemoveSource(run_loop_, delayed_work_source_, kCFRunLoopCommonModes); |
michael@0 | 174 | CFRelease(delayed_work_source_); |
michael@0 | 175 | |
michael@0 | 176 | CFRunLoopRemoveSource(run_loop_, work_source_, kCFRunLoopCommonModes); |
michael@0 | 177 | CFRelease(work_source_); |
michael@0 | 178 | |
michael@0 | 179 | CFRunLoopRemoveTimer(run_loop_, delayed_work_timer_, kCFRunLoopCommonModes); |
michael@0 | 180 | CFRelease(delayed_work_timer_); |
michael@0 | 181 | |
michael@0 | 182 | CFRelease(run_loop_); |
michael@0 | 183 | } |
michael@0 | 184 | |
michael@0 | 185 | // Must be called on the run loop thread. |
michael@0 | 186 | void MessagePumpCFRunLoopBase::Run(Delegate* delegate) { |
michael@0 | 187 | // nesting_level_ will be incremented in EnterExitRunLoop, so set |
michael@0 | 188 | // run_nesting_level_ accordingly. |
michael@0 | 189 | int last_run_nesting_level = run_nesting_level_; |
michael@0 | 190 | run_nesting_level_ = nesting_level_ + 1; |
michael@0 | 191 | |
michael@0 | 192 | Delegate* last_delegate = delegate_; |
michael@0 | 193 | delegate_ = delegate; |
michael@0 | 194 | |
michael@0 | 195 | if (delegate) { |
michael@0 | 196 | // If any work showed up but could not be dispatched for want of a |
michael@0 | 197 | // delegate, set it up for dispatch again now that a delegate is |
michael@0 | 198 | // available. |
michael@0 | 199 | if (delegateless_work_) { |
michael@0 | 200 | CFRunLoopSourceSignal(work_source_); |
michael@0 | 201 | delegateless_work_ = false; |
michael@0 | 202 | } |
michael@0 | 203 | if (delegateless_delayed_work_) { |
michael@0 | 204 | CFRunLoopSourceSignal(delayed_work_source_); |
michael@0 | 205 | delegateless_delayed_work_ = false; |
michael@0 | 206 | } |
michael@0 | 207 | if (delegateless_idle_work_) { |
michael@0 | 208 | CFRunLoopSourceSignal(idle_work_source_); |
michael@0 | 209 | delegateless_idle_work_ = false; |
michael@0 | 210 | } |
michael@0 | 211 | } |
michael@0 | 212 | |
michael@0 | 213 | DoRun(delegate); |
michael@0 | 214 | |
michael@0 | 215 | // Restore the previous state of the object. |
michael@0 | 216 | delegate_ = last_delegate; |
michael@0 | 217 | run_nesting_level_ = last_run_nesting_level; |
michael@0 | 218 | } |
michael@0 | 219 | |
michael@0 | 220 | // May be called on any thread. |
michael@0 | 221 | void MessagePumpCFRunLoopBase::ScheduleWork() { |
michael@0 | 222 | CFRunLoopSourceSignal(work_source_); |
michael@0 | 223 | CFRunLoopWakeUp(run_loop_); |
michael@0 | 224 | } |
michael@0 | 225 | |
michael@0 | 226 | // Must be called on the run loop thread. |
michael@0 | 227 | void MessagePumpCFRunLoopBase::ScheduleDelayedWork( |
michael@0 | 228 | const TimeTicks& delayed_work_time) { |
michael@0 | 229 | TimeDelta delta = delayed_work_time - TimeTicks::Now(); |
michael@0 | 230 | delayed_work_fire_time_ = CFAbsoluteTimeGetCurrent() + delta.InSecondsF(); |
michael@0 | 231 | CFRunLoopTimerSetNextFireDate(delayed_work_timer_, delayed_work_fire_time_); |
michael@0 | 232 | } |
michael@0 | 233 | |
michael@0 | 234 | // Called from the run loop. |
michael@0 | 235 | // static |
michael@0 | 236 | void MessagePumpCFRunLoopBase::RunDelayedWorkTimer(CFRunLoopTimerRef timer, |
michael@0 | 237 | void* info) { |
michael@0 | 238 | MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); |
michael@0 | 239 | |
michael@0 | 240 | // The timer won't fire again until it's reset. |
michael@0 | 241 | self->delayed_work_fire_time_ = kCFTimeIntervalMax; |
michael@0 | 242 | |
michael@0 | 243 | // CFRunLoopTimers fire outside of the priority scheme for CFRunLoopSources. |
michael@0 | 244 | // In order to establish the proper priority where delegate_->DoDelayedWork |
michael@0 | 245 | // can only be called if delegate_->DoWork returns false, the timer used |
michael@0 | 246 | // to schedule delayed work must signal a CFRunLoopSource set at a lower |
michael@0 | 247 | // priority than the one used for delegate_->DoWork. |
michael@0 | 248 | CFRunLoopSourceSignal(self->delayed_work_source_); |
michael@0 | 249 | } |
michael@0 | 250 | |
michael@0 | 251 | // Called from the run loop. |
michael@0 | 252 | // static |
michael@0 | 253 | void MessagePumpCFRunLoopBase::RunWorkSource(void* info) { |
michael@0 | 254 | MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); |
michael@0 | 255 | self->RunWork(); |
michael@0 | 256 | } |
michael@0 | 257 | |
michael@0 | 258 | // Called by MessagePumpCFRunLoopBase::RunWorkSource. |
michael@0 | 259 | bool MessagePumpCFRunLoopBase::RunWork() { |
michael@0 | 260 | if (!delegate_) { |
michael@0 | 261 | // This point can be reached with a NULL delegate_ if Run is not on the |
michael@0 | 262 | // stack but foreign code is spinning the CFRunLoop. Arrange to come back |
michael@0 | 263 | // here when a delegate is available. |
michael@0 | 264 | delegateless_work_ = true; |
michael@0 | 265 | return false; |
michael@0 | 266 | } |
michael@0 | 267 | |
michael@0 | 268 | // The NSApplication-based run loop only drains the autorelease pool at each |
michael@0 | 269 | // UI event (NSEvent). The autorelease pool is not drained for each |
michael@0 | 270 | // CFRunLoopSource target that's run. Use a local pool for any autoreleased |
michael@0 | 271 | // objects if the app is not currently handling a UI event to ensure they're |
michael@0 | 272 | // released promptly even in the absence of UI events. |
michael@0 | 273 | MessagePumpScopedAutoreleasePool autorelease_pool(this); |
michael@0 | 274 | |
michael@0 | 275 | // Call DoWork once, and if something was done, arrange to come back here |
michael@0 | 276 | // again as long as the loop is still running. |
michael@0 | 277 | bool did_work = delegate_->DoWork(); |
michael@0 | 278 | if (did_work) { |
michael@0 | 279 | CFRunLoopSourceSignal(work_source_); |
michael@0 | 280 | } |
michael@0 | 281 | |
michael@0 | 282 | return did_work; |
michael@0 | 283 | } |
michael@0 | 284 | |
michael@0 | 285 | // Called from the run loop. |
michael@0 | 286 | // static |
michael@0 | 287 | void MessagePumpCFRunLoopBase::RunDelayedWorkSource(void* info) { |
michael@0 | 288 | MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); |
michael@0 | 289 | self->RunDelayedWork(); |
michael@0 | 290 | } |
michael@0 | 291 | |
michael@0 | 292 | // Called by MessagePumpCFRunLoopBase::RunDelayedWorkSource. |
michael@0 | 293 | bool MessagePumpCFRunLoopBase::RunDelayedWork() { |
michael@0 | 294 | if (!delegate_) { |
michael@0 | 295 | // This point can be reached with a NULL delegate_ if Run is not on the |
michael@0 | 296 | // stack but foreign code is spinning the CFRunLoop. Arrange to come back |
michael@0 | 297 | // here when a delegate is available. |
michael@0 | 298 | delegateless_delayed_work_ = true; |
michael@0 | 299 | return false; |
michael@0 | 300 | } |
michael@0 | 301 | |
michael@0 | 302 | // The NSApplication-based run loop only drains the autorelease pool at each |
michael@0 | 303 | // UI event (NSEvent). The autorelease pool is not drained for each |
michael@0 | 304 | // CFRunLoopSource target that's run. Use a local pool for any autoreleased |
michael@0 | 305 | // objects if the app is not currently handling a UI event to ensure they're |
michael@0 | 306 | // released promptly even in the absence of UI events. |
michael@0 | 307 | MessagePumpScopedAutoreleasePool autorelease_pool(this); |
michael@0 | 308 | |
michael@0 | 309 | TimeTicks next_time; |
michael@0 | 310 | delegate_->DoDelayedWork(&next_time); |
michael@0 | 311 | |
michael@0 | 312 | bool more_work = !next_time.is_null(); |
michael@0 | 313 | if (more_work) { |
michael@0 | 314 | TimeDelta delay = next_time - TimeTicks::Now(); |
michael@0 | 315 | if (delay > TimeDelta()) { |
michael@0 | 316 | // There's more delayed work to be done in the future. |
michael@0 | 317 | ScheduleDelayedWork(next_time); |
michael@0 | 318 | } else { |
michael@0 | 319 | // There's more delayed work to be done, and its time is in the past. |
michael@0 | 320 | // Arrange to come back here directly as long as the loop is still |
michael@0 | 321 | // running. |
michael@0 | 322 | CFRunLoopSourceSignal(delayed_work_source_); |
michael@0 | 323 | } |
michael@0 | 324 | } |
michael@0 | 325 | |
michael@0 | 326 | return more_work; |
michael@0 | 327 | } |
michael@0 | 328 | |
michael@0 | 329 | // Called from the run loop. |
michael@0 | 330 | // static |
michael@0 | 331 | void MessagePumpCFRunLoopBase::RunIdleWorkSource(void* info) { |
michael@0 | 332 | MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); |
michael@0 | 333 | self->RunIdleWork(); |
michael@0 | 334 | } |
michael@0 | 335 | |
michael@0 | 336 | // Called by MessagePumpCFRunLoopBase::RunIdleWorkSource. |
michael@0 | 337 | bool MessagePumpCFRunLoopBase::RunIdleWork() { |
michael@0 | 338 | if (!delegate_) { |
michael@0 | 339 | // This point can be reached with a NULL delegate_ if Run is not on the |
michael@0 | 340 | // stack but foreign code is spinning the CFRunLoop. Arrange to come back |
michael@0 | 341 | // here when a delegate is available. |
michael@0 | 342 | delegateless_idle_work_ = true; |
michael@0 | 343 | return false; |
michael@0 | 344 | } |
michael@0 | 345 | |
michael@0 | 346 | // The NSApplication-based run loop only drains the autorelease pool at each |
michael@0 | 347 | // UI event (NSEvent). The autorelease pool is not drained for each |
michael@0 | 348 | // CFRunLoopSource target that's run. Use a local pool for any autoreleased |
michael@0 | 349 | // objects if the app is not currently handling a UI event to ensure they're |
michael@0 | 350 | // released promptly even in the absence of UI events. |
michael@0 | 351 | MessagePumpScopedAutoreleasePool autorelease_pool(this); |
michael@0 | 352 | |
michael@0 | 353 | // Call DoIdleWork once, and if something was done, arrange to come back here |
michael@0 | 354 | // again as long as the loop is still running. |
michael@0 | 355 | bool did_work = delegate_->DoIdleWork(); |
michael@0 | 356 | if (did_work) { |
michael@0 | 357 | CFRunLoopSourceSignal(idle_work_source_); |
michael@0 | 358 | } |
michael@0 | 359 | |
michael@0 | 360 | return did_work; |
michael@0 | 361 | } |
michael@0 | 362 | |
michael@0 | 363 | // Called from the run loop. |
michael@0 | 364 | // static |
michael@0 | 365 | void MessagePumpCFRunLoopBase::RunNestingDeferredWorkSource(void* info) { |
michael@0 | 366 | MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); |
michael@0 | 367 | self->RunNestingDeferredWork(); |
michael@0 | 368 | } |
michael@0 | 369 | |
michael@0 | 370 | // Called by MessagePumpCFRunLoopBase::RunNestingDeferredWorkSource. |
michael@0 | 371 | bool MessagePumpCFRunLoopBase::RunNestingDeferredWork() { |
michael@0 | 372 | if (!delegate_) { |
michael@0 | 373 | // This point can be reached with a NULL delegate_ if Run is not on the |
michael@0 | 374 | // stack but foreign code is spinning the CFRunLoop. There's no sense in |
michael@0 | 375 | // attempting to do any work or signalling the work sources because |
michael@0 | 376 | // without a delegate, work is not possible. |
michael@0 | 377 | return false; |
michael@0 | 378 | } |
michael@0 | 379 | |
michael@0 | 380 | // Immediately try work in priority order. |
michael@0 | 381 | if (!RunWork()) { |
michael@0 | 382 | if (!RunDelayedWork()) { |
michael@0 | 383 | if (!RunIdleWork()) { |
michael@0 | 384 | return false; |
michael@0 | 385 | } |
michael@0 | 386 | } else { |
michael@0 | 387 | // There was no work, and delayed work was done. Arrange for the loop |
michael@0 | 388 | // to try non-nestable idle work on a subsequent pass. |
michael@0 | 389 | CFRunLoopSourceSignal(idle_work_source_); |
michael@0 | 390 | } |
michael@0 | 391 | } else { |
michael@0 | 392 | // Work was done. Arrange for the loop to try non-nestable delayed and |
michael@0 | 393 | // idle work on a subsequent pass. |
michael@0 | 394 | CFRunLoopSourceSignal(delayed_work_source_); |
michael@0 | 395 | CFRunLoopSourceSignal(idle_work_source_); |
michael@0 | 396 | } |
michael@0 | 397 | |
michael@0 | 398 | return true; |
michael@0 | 399 | } |
michael@0 | 400 | |
michael@0 | 401 | // Called before the run loop goes to sleep or exits, or processes sources. |
michael@0 | 402 | void MessagePumpCFRunLoopBase::MaybeScheduleNestingDeferredWork() { |
michael@0 | 403 | // deepest_nesting_level_ is set as run loops are entered. If the deepest |
michael@0 | 404 | // level encountered is deeper than the current level, a nested loop |
michael@0 | 405 | // (relative to the current level) ran since the last time nesting-deferred |
michael@0 | 406 | // work was scheduled. When that situation is encountered, schedule |
michael@0 | 407 | // nesting-deferred work in case any work was deferred because nested work |
michael@0 | 408 | // was disallowed. |
michael@0 | 409 | if (deepest_nesting_level_ > nesting_level_) { |
michael@0 | 410 | deepest_nesting_level_ = nesting_level_; |
michael@0 | 411 | CFRunLoopSourceSignal(nesting_deferred_work_source_); |
michael@0 | 412 | } |
michael@0 | 413 | } |
michael@0 | 414 | |
michael@0 | 415 | // Called from the run loop. |
michael@0 | 416 | // static |
michael@0 | 417 | void MessagePumpCFRunLoopBase::PreWaitObserver(CFRunLoopObserverRef observer, |
michael@0 | 418 | CFRunLoopActivity activity, |
michael@0 | 419 | void* info) { |
michael@0 | 420 | MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); |
michael@0 | 421 | |
michael@0 | 422 | // Attempt to do some idle work before going to sleep. |
michael@0 | 423 | self->RunIdleWork(); |
michael@0 | 424 | |
michael@0 | 425 | // The run loop is about to go to sleep. If any of the work done since it |
michael@0 | 426 | // started or woke up resulted in a nested run loop running, |
michael@0 | 427 | // nesting-deferred work may have accumulated. Schedule it for processing |
michael@0 | 428 | // if appropriate. |
michael@0 | 429 | self->MaybeScheduleNestingDeferredWork(); |
michael@0 | 430 | } |
michael@0 | 431 | |
michael@0 | 432 | // Called from the run loop. |
michael@0 | 433 | // static |
michael@0 | 434 | void MessagePumpCFRunLoopBase::PreSourceObserver(CFRunLoopObserverRef observer, |
michael@0 | 435 | CFRunLoopActivity activity, |
michael@0 | 436 | void* info) { |
michael@0 | 437 | MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); |
michael@0 | 438 | |
michael@0 | 439 | // The run loop has reached the top of the loop and is about to begin |
michael@0 | 440 | // processing sources. If the last iteration of the loop at this nesting |
michael@0 | 441 | // level did not sleep or exit, nesting-deferred work may have accumulated |
michael@0 | 442 | // if a nested loop ran. Schedule nesting-deferred work for processing if |
michael@0 | 443 | // appropriate. |
michael@0 | 444 | self->MaybeScheduleNestingDeferredWork(); |
michael@0 | 445 | } |
michael@0 | 446 | |
michael@0 | 447 | // Called from the run loop. |
michael@0 | 448 | // static |
michael@0 | 449 | void MessagePumpCFRunLoopBase::EnterExitObserver(CFRunLoopObserverRef observer, |
michael@0 | 450 | CFRunLoopActivity activity, |
michael@0 | 451 | void* info) { |
michael@0 | 452 | MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); |
michael@0 | 453 | |
michael@0 | 454 | switch (activity) { |
michael@0 | 455 | case kCFRunLoopEntry: |
michael@0 | 456 | ++self->nesting_level_; |
michael@0 | 457 | if (self->nesting_level_ > self->deepest_nesting_level_) { |
michael@0 | 458 | self->deepest_nesting_level_ = self->nesting_level_; |
michael@0 | 459 | } |
michael@0 | 460 | break; |
michael@0 | 461 | |
michael@0 | 462 | case kCFRunLoopExit: |
michael@0 | 463 | // Not all run loops go to sleep. If a run loop is stopped before it |
michael@0 | 464 | // goes to sleep due to a CFRunLoopStop call, or if the timeout passed |
michael@0 | 465 | // to CFRunLoopRunInMode expires, the run loop may proceed directly from |
michael@0 | 466 | // handling sources to exiting without any sleep. This most commonly |
michael@0 | 467 | // occurs when CFRunLoopRunInMode is passed a timeout of 0, causing it |
michael@0 | 468 | // to make a single pass through the loop and exit without sleep. Some |
michael@0 | 469 | // native loops use CFRunLoop in this way. Because PreWaitObserver will |
michael@0 | 470 | // not be called in these case, MaybeScheduleNestingDeferredWork needs |
michael@0 | 471 | // to be called here, as the run loop exits. |
michael@0 | 472 | // |
michael@0 | 473 | // MaybeScheduleNestingDeferredWork consults self->nesting_level_ |
michael@0 | 474 | // to determine whether to schedule nesting-deferred work. It expects |
michael@0 | 475 | // the nesting level to be set to the depth of the loop that is going |
michael@0 | 476 | // to sleep or exiting. It must be called before decrementing the |
michael@0 | 477 | // value so that the value still corresponds to the level of the exiting |
michael@0 | 478 | // loop. |
michael@0 | 479 | self->MaybeScheduleNestingDeferredWork(); |
michael@0 | 480 | --self->nesting_level_; |
michael@0 | 481 | break; |
michael@0 | 482 | |
michael@0 | 483 | default: |
michael@0 | 484 | break; |
michael@0 | 485 | } |
michael@0 | 486 | |
michael@0 | 487 | self->EnterExitRunLoop(activity); |
michael@0 | 488 | } |
michael@0 | 489 | |
michael@0 | 490 | // Called from the run loop. |
michael@0 | 491 | // static |
michael@0 | 492 | void MessagePumpCFRunLoopBase::PowerStateNotification(void* info, |
michael@0 | 493 | io_service_t service, |
michael@0 | 494 | uint32_t message_type, |
michael@0 | 495 | void* message_argument) { |
michael@0 | 496 | // CFRunLoopTimer (NSTimer) is scheduled in terms of CFAbsoluteTime, which |
michael@0 | 497 | // measures the number of seconds since 2001-01-01 00:00:00.0 Z. It is |
michael@0 | 498 | // implemented in terms of kernel ticks, as in mach_absolute_time. While an |
michael@0 | 499 | // offset and scale factor can be applied to convert between the two time |
michael@0 | 500 | // bases at any time after boot, the kernel clock stops while the system is |
michael@0 | 501 | // asleep, altering the offset. (The offset will also change when the |
michael@0 | 502 | // real-time clock is adjusted.) CFRunLoopTimers are not readjusted to take |
michael@0 | 503 | // this into account when the system wakes up, so any timers that were |
michael@0 | 504 | // pending while the system was asleep will be delayed by the sleep |
michael@0 | 505 | // duration. |
michael@0 | 506 | // |
michael@0 | 507 | // The MessagePump interface assumes that scheduled delayed work will be |
michael@0 | 508 | // performed at the time ScheduleDelayedWork was asked to perform it. The |
michael@0 | 509 | // delay caused by the CFRunLoopTimer not firing at the appropriate time |
michael@0 | 510 | // results in a stall of queued delayed work when the system wakes up. |
michael@0 | 511 | // With this limitation, scheduled work would not be performed until |
michael@0 | 512 | // (system wake time + scheduled work time - system sleep time), while it |
michael@0 | 513 | // would be expected to be performed at (scheduled work time). |
michael@0 | 514 | // |
michael@0 | 515 | // To work around this problem, when the system wakes up from sleep, if a |
michael@0 | 516 | // delayed work timer is pending, it is rescheduled to fire at the original |
michael@0 | 517 | // time that it was scheduled to fire. |
michael@0 | 518 | // |
michael@0 | 519 | // This mechanism is not resilient if the real-time clock does not maintain |
michael@0 | 520 | // stable time while the system is sleeping, but it matches the behavior of |
michael@0 | 521 | // the various other MessagePump implementations, and MessageLoop seems to |
michael@0 | 522 | // be limited in the same way. |
michael@0 | 523 | // |
michael@0 | 524 | // References |
michael@0 | 525 | // - Chris Kane, "NSTimer and deep sleep," cocoa-dev@lists.apple.com, |
michael@0 | 526 | // http://lists.apple.com/archives/Cocoa-dev/2002/May/msg01547.html |
michael@0 | 527 | // - Apple Technical Q&A QA1340, "Registering and unregistering for sleep |
michael@0 | 528 | // and wake notifications," |
michael@0 | 529 | // http://developer.apple.com/mac/library/qa/qa2004/qa1340.html |
michael@0 | 530 | // - Core Foundation source code, CF-550/CFRunLoop.c and CF-550/CFDate.c, |
michael@0 | 531 | // http://www.opensource.apple.com/ |
michael@0 | 532 | |
michael@0 | 533 | MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); |
michael@0 | 534 | |
michael@0 | 535 | switch (message_type) { |
michael@0 | 536 | case kIOMessageSystemWillPowerOn: |
michael@0 | 537 | if (self->delayed_work_fire_time_ != kCFTimeIntervalMax) { |
michael@0 | 538 | CFRunLoopTimerSetNextFireDate(self->delayed_work_timer_, |
michael@0 | 539 | self->delayed_work_fire_time_); |
michael@0 | 540 | } |
michael@0 | 541 | break; |
michael@0 | 542 | |
michael@0 | 543 | case kIOMessageSystemWillSleep: |
michael@0 | 544 | case kIOMessageCanSystemSleep: |
michael@0 | 545 | // The system will wait for 30 seconds before entering sleep if neither |
michael@0 | 546 | // IOAllowPowerChange nor IOCancelPowerChange are called. That would be |
michael@0 | 547 | // pretty antisocial. |
michael@0 | 548 | IOAllowPowerChange(self->root_power_domain_, |
michael@0 | 549 | reinterpret_cast<long>(message_argument)); |
michael@0 | 550 | break; |
michael@0 | 551 | |
michael@0 | 552 | default: |
michael@0 | 553 | break; |
michael@0 | 554 | } |
michael@0 | 555 | } |
michael@0 | 556 | |
michael@0 | 557 | // Called by MessagePumpCFRunLoopBase::EnterExitRunLoop. The default |
michael@0 | 558 | // implementation is a no-op. |
michael@0 | 559 | void MessagePumpCFRunLoopBase::EnterExitRunLoop(CFRunLoopActivity activity) { |
michael@0 | 560 | } |
michael@0 | 561 | |
michael@0 | 562 | // Base version returns a standard NSAutoreleasePool. |
michael@0 | 563 | NSAutoreleasePool* MessagePumpCFRunLoopBase::CreateAutoreleasePool() { |
michael@0 | 564 | return [[NSAutoreleasePool alloc] init]; |
michael@0 | 565 | } |
michael@0 | 566 | |
michael@0 | 567 | MessagePumpCFRunLoop::MessagePumpCFRunLoop() |
michael@0 | 568 | : quit_pending_(false) { |
michael@0 | 569 | } |
michael@0 | 570 | |
michael@0 | 571 | // Called by MessagePumpCFRunLoopBase::DoRun. If other CFRunLoopRun loops were |
michael@0 | 572 | // running lower on the run loop thread's stack when this object was created, |
michael@0 | 573 | // the same number of CFRunLoopRun loops must be running for the outermost call |
michael@0 | 574 | // to Run. Run/DoRun are reentrant after that point. |
michael@0 | 575 | void MessagePumpCFRunLoop::DoRun(Delegate* delegate) { |
michael@0 | 576 | // This is completely identical to calling CFRunLoopRun(), except autorelease |
michael@0 | 577 | // pool management is introduced. |
michael@0 | 578 | int result; |
michael@0 | 579 | do { |
michael@0 | 580 | MessagePumpScopedAutoreleasePool autorelease_pool(this); |
michael@0 | 581 | result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, |
michael@0 | 582 | kCFTimeIntervalMax, |
michael@0 | 583 | false); |
michael@0 | 584 | } while (result != kCFRunLoopRunStopped && result != kCFRunLoopRunFinished); |
michael@0 | 585 | } |
michael@0 | 586 | |
michael@0 | 587 | // Must be called on the run loop thread. |
michael@0 | 588 | void MessagePumpCFRunLoop::Quit() { |
michael@0 | 589 | // Stop the innermost run loop managed by this MessagePumpCFRunLoop object. |
michael@0 | 590 | if (nesting_level() == run_nesting_level()) { |
michael@0 | 591 | // This object is running the innermost loop, just stop it. |
michael@0 | 592 | CFRunLoopStop(run_loop()); |
michael@0 | 593 | } else { |
michael@0 | 594 | // There's another loop running inside the loop managed by this object. |
michael@0 | 595 | // In other words, someone else called CFRunLoopRunInMode on the same |
michael@0 | 596 | // thread, deeper on the stack than the deepest Run call. Don't preempt |
michael@0 | 597 | // other run loops, just mark this object to quit the innermost Run as |
michael@0 | 598 | // soon as the other inner loops not managed by Run are done. |
michael@0 | 599 | quit_pending_ = true; |
michael@0 | 600 | } |
michael@0 | 601 | } |
michael@0 | 602 | |
michael@0 | 603 | // Called by MessagePumpCFRunLoopBase::EnterExitObserver. |
michael@0 | 604 | void MessagePumpCFRunLoop::EnterExitRunLoop(CFRunLoopActivity activity) { |
michael@0 | 605 | if (activity == kCFRunLoopExit && |
michael@0 | 606 | nesting_level() == run_nesting_level() && |
michael@0 | 607 | quit_pending_) { |
michael@0 | 608 | // Quit was called while loops other than those managed by this object |
michael@0 | 609 | // were running further inside a run loop managed by this object. Now |
michael@0 | 610 | // that all unmanaged inner run loops are gone, stop the loop running |
michael@0 | 611 | // just inside Run. |
michael@0 | 612 | CFRunLoopStop(run_loop()); |
michael@0 | 613 | quit_pending_ = false; |
michael@0 | 614 | } |
michael@0 | 615 | } |
michael@0 | 616 | |
michael@0 | 617 | MessagePumpNSRunLoop::MessagePumpNSRunLoop() |
michael@0 | 618 | : keep_running_(true) { |
michael@0 | 619 | CFRunLoopSourceContext source_context = CFRunLoopSourceContext(); |
michael@0 | 620 | source_context.perform = NoOp; |
michael@0 | 621 | quit_source_ = CFRunLoopSourceCreate(NULL, // allocator |
michael@0 | 622 | 0, // priority |
michael@0 | 623 | &source_context); |
michael@0 | 624 | CFRunLoopAddSource(run_loop(), quit_source_, kCFRunLoopCommonModes); |
michael@0 | 625 | } |
michael@0 | 626 | |
michael@0 | 627 | MessagePumpNSRunLoop::~MessagePumpNSRunLoop() { |
michael@0 | 628 | CFRunLoopRemoveSource(run_loop(), quit_source_, kCFRunLoopCommonModes); |
michael@0 | 629 | CFRelease(quit_source_); |
michael@0 | 630 | } |
michael@0 | 631 | |
michael@0 | 632 | void MessagePumpNSRunLoop::DoRun(Delegate* delegate) { |
michael@0 | 633 | while (keep_running_) { |
michael@0 | 634 | // NSRunLoop manages autorelease pools itself. |
michael@0 | 635 | [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode |
michael@0 | 636 | beforeDate:[NSDate distantFuture]]; |
michael@0 | 637 | } |
michael@0 | 638 | |
michael@0 | 639 | keep_running_ = true; |
michael@0 | 640 | } |
michael@0 | 641 | |
michael@0 | 642 | void MessagePumpNSRunLoop::Quit() { |
michael@0 | 643 | keep_running_ = false; |
michael@0 | 644 | CFRunLoopSourceSignal(quit_source_); |
michael@0 | 645 | CFRunLoopWakeUp(run_loop()); |
michael@0 | 646 | } |
michael@0 | 647 | |
michael@0 | 648 | MessagePumpNSApplication::MessagePumpNSApplication() |
michael@0 | 649 | : keep_running_(true), |
michael@0 | 650 | running_own_loop_(false) { |
michael@0 | 651 | } |
michael@0 | 652 | |
michael@0 | 653 | void MessagePumpNSApplication::DoRun(Delegate* delegate) { |
michael@0 | 654 | bool last_running_own_loop_ = running_own_loop_; |
michael@0 | 655 | |
michael@0 | 656 | // TODO(dmaclach): Get rid of this gratuitous sharedApplication. |
michael@0 | 657 | // Tests should be setting up their applications on their own. |
michael@0 | 658 | [CrApplication sharedApplication]; |
michael@0 | 659 | |
michael@0 | 660 | if (![NSApp isRunning]) { |
michael@0 | 661 | running_own_loop_ = false; |
michael@0 | 662 | // NSApplication manages autorelease pools itself when run this way. |
michael@0 | 663 | [NSApp run]; |
michael@0 | 664 | } else { |
michael@0 | 665 | running_own_loop_ = true; |
michael@0 | 666 | NSDate* distant_future = [NSDate distantFuture]; |
michael@0 | 667 | while (keep_running_) { |
michael@0 | 668 | MessagePumpScopedAutoreleasePool autorelease_pool(this); |
michael@0 | 669 | NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask |
michael@0 | 670 | untilDate:distant_future |
michael@0 | 671 | inMode:NSDefaultRunLoopMode |
michael@0 | 672 | dequeue:YES]; |
michael@0 | 673 | if (event) { |
michael@0 | 674 | [NSApp sendEvent:event]; |
michael@0 | 675 | } |
michael@0 | 676 | } |
michael@0 | 677 | keep_running_ = true; |
michael@0 | 678 | } |
michael@0 | 679 | |
michael@0 | 680 | running_own_loop_ = last_running_own_loop_; |
michael@0 | 681 | } |
michael@0 | 682 | |
michael@0 | 683 | void MessagePumpNSApplication::Quit() { |
michael@0 | 684 | if (!running_own_loop_) { |
michael@0 | 685 | [NSApp stop:nil]; |
michael@0 | 686 | } else { |
michael@0 | 687 | keep_running_ = false; |
michael@0 | 688 | } |
michael@0 | 689 | |
michael@0 | 690 | // Send a fake event to wake the loop up. |
michael@0 | 691 | [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined |
michael@0 | 692 | location:NSMakePoint(0, 0) |
michael@0 | 693 | modifierFlags:0 |
michael@0 | 694 | timestamp:0 |
michael@0 | 695 | windowNumber:0 |
michael@0 | 696 | context:NULL |
michael@0 | 697 | subtype:0 |
michael@0 | 698 | data1:0 |
michael@0 | 699 | data2:0] |
michael@0 | 700 | atStart:NO]; |
michael@0 | 701 | } |
michael@0 | 702 | |
michael@0 | 703 | // Prevents an autorelease pool from being created if the app is in the midst of |
michael@0 | 704 | // handling a UI event because various parts of AppKit depend on objects that |
michael@0 | 705 | // are created while handling a UI event to be autoreleased in the event loop. |
michael@0 | 706 | // An example of this is NSWindowController. When a window with a window |
michael@0 | 707 | // controller is closed it goes through a stack like this: |
michael@0 | 708 | // (Several stack frames elided for clarity) |
michael@0 | 709 | // |
michael@0 | 710 | // #0 [NSWindowController autorelease] |
michael@0 | 711 | // #1 DoAClose |
michael@0 | 712 | // #2 MessagePumpCFRunLoopBase::DoWork() |
michael@0 | 713 | // #3 [NSRunLoop run] |
michael@0 | 714 | // #4 [NSButton performClick:] |
michael@0 | 715 | // #5 [NSWindow sendEvent:] |
michael@0 | 716 | // #6 [NSApp sendEvent:] |
michael@0 | 717 | // #7 [NSApp run] |
michael@0 | 718 | // |
michael@0 | 719 | // -performClick: spins a nested run loop. If the pool created in DoWork was a |
michael@0 | 720 | // standard NSAutoreleasePool, it would release the objects that were |
michael@0 | 721 | // autoreleased into it once DoWork released it. This would cause the window |
michael@0 | 722 | // controller, which autoreleased itself in frame #0, to release itself, and |
michael@0 | 723 | // possibly free itself. Unfortunately this window controller controls the |
michael@0 | 724 | // window in frame #5. When the stack is unwound to frame #5, the window would |
michael@0 | 725 | // no longer exists and crashes may occur. Apple gets around this by never |
michael@0 | 726 | // releasing the pool it creates in frame #4, and letting frame #7 clean it up |
michael@0 | 727 | // when it cleans up the pool that wraps frame #7. When an autorelease pool is |
michael@0 | 728 | // released it releases all other pools that were created after it on the |
michael@0 | 729 | // autorelease pool stack. |
michael@0 | 730 | // |
michael@0 | 731 | // CrApplication is responsible for setting handlingSendEvent to true just |
michael@0 | 732 | // before it sends the event throught the event handling mechanism, and |
michael@0 | 733 | // returning it to its previous value once the event has been sent. |
michael@0 | 734 | NSAutoreleasePool* MessagePumpNSApplication::CreateAutoreleasePool() { |
michael@0 | 735 | NSAutoreleasePool* pool = nil; |
michael@0 | 736 | DCHECK([NSApp isKindOfClass:[CrApplication class]]); |
michael@0 | 737 | if (![static_cast<CrApplication*>(NSApp) isHandlingSendEvent]) { |
michael@0 | 738 | pool = MessagePumpCFRunLoopBase::CreateAutoreleasePool(); |
michael@0 | 739 | } |
michael@0 | 740 | return pool; |
michael@0 | 741 | } |
michael@0 | 742 | |
michael@0 | 743 | // static |
michael@0 | 744 | MessagePump* MessagePumpMac::Create() { |
michael@0 | 745 | if ([NSThread isMainThread]) { |
michael@0 | 746 | return new MessagePumpNSApplication; |
michael@0 | 747 | } |
michael@0 | 748 | |
michael@0 | 749 | return new MessagePumpNSRunLoop; |
michael@0 | 750 | } |
michael@0 | 751 | |
michael@0 | 752 | } // namespace base |