| OLD | NEW |
| 1 // Copyright (c) 2008 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2008 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/message_pump_mac.h" | 5 #include "base/message_pump_mac.h" |
| 6 | 6 |
| 7 #import <AppKit/AppKit.h> | 7 #import <AppKit/AppKit.h> |
| 8 #import <Foundation/Foundation.h> | 8 #import <Foundation/Foundation.h> |
| 9 #include <IOKit/IOMessage.h> |
| 10 #include <IOKit/pwr_mgt/IOPMLib.h> |
| 9 | 11 |
| 10 #include <limits> | 12 #include <limits> |
| 11 | 13 |
| 12 #include "base/scoped_nsautorelease_pool.h" | 14 #include "base/scoped_nsautorelease_pool.h" |
| 13 #include "base/time.h" | 15 #include "base/time.h" |
| 14 | 16 |
| 15 namespace { | 17 namespace { |
| 16 | 18 |
| 17 void NoOp(void* info) { | 19 void NoOp(void* info) { |
| 18 } | 20 } |
| 19 | 21 |
| 20 const CFTimeInterval kCFTimeIntervalMax = | 22 const CFTimeInterval kCFTimeIntervalMax = |
| 21 std::numeric_limits<CFTimeInterval>::max(); | 23 std::numeric_limits<CFTimeInterval>::max(); |
| 22 | 24 |
| 23 } // namespace | 25 } // namespace |
| 24 | 26 |
| 25 namespace base { | 27 namespace base { |
| 26 | 28 |
| 27 // Must be called on the run loop thread. | 29 // Must be called on the run loop thread. |
| 28 MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase() | 30 MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase() |
| 29 : delegate_(NULL), | 31 : delegate_(NULL), |
| 32 delayed_work_fire_time_(kCFTimeIntervalMax), |
| 30 nesting_level_(0), | 33 nesting_level_(0), |
| 31 run_nesting_level_(0), | 34 run_nesting_level_(0), |
| 32 deepest_nesting_level_(0), | 35 deepest_nesting_level_(0), |
| 33 delegateless_work_(false), | 36 delegateless_work_(false), |
| 34 delegateless_delayed_work_(false), | 37 delegateless_delayed_work_(false), |
| 35 delegateless_idle_work_(false) { | 38 delegateless_idle_work_(false) { |
| 36 run_loop_ = CFRunLoopGetCurrent(); | 39 run_loop_ = CFRunLoopGetCurrent(); |
| 37 CFRetain(run_loop_); | 40 CFRetain(run_loop_); |
| 38 | 41 |
| 39 // Set a repeating timer with a preposterous firing time and interval. The | 42 // Set a repeating timer with a preposterous firing time and interval. The |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 96 CFRunLoopAddObserver(run_loop_, pre_source_observer_, kCFRunLoopCommonModes); | 99 CFRunLoopAddObserver(run_loop_, pre_source_observer_, kCFRunLoopCommonModes); |
| 97 | 100 |
| 98 enter_exit_observer_ = CFRunLoopObserverCreate(NULL, // allocator | 101 enter_exit_observer_ = CFRunLoopObserverCreate(NULL, // allocator |
| 99 kCFRunLoopEntry | | 102 kCFRunLoopEntry | |
| 100 kCFRunLoopExit, | 103 kCFRunLoopExit, |
| 101 true, // repeat | 104 true, // repeat |
| 102 0, // priority | 105 0, // priority |
| 103 EnterExitObserver, | 106 EnterExitObserver, |
| 104 &observer_context); | 107 &observer_context); |
| 105 CFRunLoopAddObserver(run_loop_, enter_exit_observer_, kCFRunLoopCommonModes); | 108 CFRunLoopAddObserver(run_loop_, enter_exit_observer_, kCFRunLoopCommonModes); |
| 109 |
| 110 root_power_domain_ = IORegisterForSystemPower(this, |
| 111 &power_notification_port_, |
| 112 PowerStateNotification, |
| 113 &power_notification_object_); |
| 114 if (root_power_domain_ != MACH_PORT_NULL) { |
| 115 CFRunLoopAddSource( |
| 116 run_loop_, |
| 117 IONotificationPortGetRunLoopSource(power_notification_port_), |
| 118 kCFRunLoopCommonModes); |
| 119 } |
| 106 } | 120 } |
| 107 | 121 |
| 108 // Ideally called on the run loop thread. If other run loops were running | 122 // Ideally called on the run loop thread. If other run loops were running |
| 109 // lower on the run loop thread's stack when this object was created, the | 123 // lower on the run loop thread's stack when this object was created, the |
| 110 // same number of run loops must be running when this object is destroyed. | 124 // same number of run loops must be running when this object is destroyed. |
| 111 MessagePumpCFRunLoopBase::~MessagePumpCFRunLoopBase() { | 125 MessagePumpCFRunLoopBase::~MessagePumpCFRunLoopBase() { |
| 126 if (root_power_domain_ != MACH_PORT_NULL) { |
| 127 CFRunLoopRemoveSource( |
| 128 run_loop_, |
| 129 IONotificationPortGetRunLoopSource(power_notification_port_), |
| 130 kCFRunLoopCommonModes); |
| 131 IODeregisterForSystemPower(&power_notification_object_); |
| 132 IOServiceClose(root_power_domain_); |
| 133 IONotificationPortDestroy(power_notification_port_); |
| 134 } |
| 135 |
| 112 CFRunLoopRemoveObserver(run_loop_, enter_exit_observer_, | 136 CFRunLoopRemoveObserver(run_loop_, enter_exit_observer_, |
| 113 kCFRunLoopCommonModes); | 137 kCFRunLoopCommonModes); |
| 114 CFRelease(enter_exit_observer_); | 138 CFRelease(enter_exit_observer_); |
| 115 | 139 |
| 116 CFRunLoopRemoveObserver(run_loop_, pre_source_observer_, | 140 CFRunLoopRemoveObserver(run_loop_, pre_source_observer_, |
| 117 kCFRunLoopCommonModes); | 141 kCFRunLoopCommonModes); |
| 118 CFRelease(pre_source_observer_); | 142 CFRelease(pre_source_observer_); |
| 119 | 143 |
| 120 CFRunLoopRemoveObserver(run_loop_, pre_wait_observer_, | 144 CFRunLoopRemoveObserver(run_loop_, pre_wait_observer_, |
| 121 kCFRunLoopCommonModes); | 145 kCFRunLoopCommonModes); |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 191 Time::kMicrosecondsPerSecond) / | 215 Time::kMicrosecondsPerSecond) / |
| 192 Time::kMicrosecondsPerSecond); | 216 Time::kMicrosecondsPerSecond); |
| 193 CFGregorianDate gregorian = { | 217 CFGregorianDate gregorian = { |
| 194 exploded.year, | 218 exploded.year, |
| 195 exploded.month, | 219 exploded.month, |
| 196 exploded.day_of_month, | 220 exploded.day_of_month, |
| 197 exploded.hour, | 221 exploded.hour, |
| 198 exploded.minute, | 222 exploded.minute, |
| 199 seconds | 223 seconds |
| 200 }; | 224 }; |
| 201 CFAbsoluteTime fire_time = CFGregorianDateGetAbsoluteTime(gregorian, NULL); | 225 delayed_work_fire_time_ = CFGregorianDateGetAbsoluteTime(gregorian, NULL); |
| 202 | 226 |
| 203 CFRunLoopTimerSetNextFireDate(delayed_work_timer_, fire_time); | 227 CFRunLoopTimerSetNextFireDate(delayed_work_timer_, delayed_work_fire_time_); |
| 204 } | 228 } |
| 205 | 229 |
| 206 // Called from the run loop. | 230 // Called from the run loop. |
| 207 // static | 231 // static |
| 208 void MessagePumpCFRunLoopBase::RunDelayedWorkTimer(CFRunLoopTimerRef timer, | 232 void MessagePumpCFRunLoopBase::RunDelayedWorkTimer(CFRunLoopTimerRef timer, |
| 209 void* info) { | 233 void* info) { |
| 210 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); | 234 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); |
| 211 | 235 |
| 236 // The timer won't fire again until it's reset. |
| 237 self->delayed_work_fire_time_ = kCFTimeIntervalMax; |
| 238 |
| 212 // CFRunLoopTimers fire outside of the priority scheme for CFRunLoopSources. | 239 // CFRunLoopTimers fire outside of the priority scheme for CFRunLoopSources. |
| 213 // In order to establish the proper priority where delegate_->DoDelayedWork | 240 // In order to establish the proper priority where delegate_->DoDelayedWork |
| 214 // can only be called if delegate_->DoWork returns false, the timer used | 241 // can only be called if delegate_->DoWork returns false, the timer used |
| 215 // to schedule delayed work must signal a CFRunLoopSource set at a lower | 242 // to schedule delayed work must signal a CFRunLoopSource set at a lower |
| 216 // priority than the one used for delegate_->DoWork. | 243 // priority than the one used for delegate_->DoWork. |
| 217 CFRunLoopSourceSignal(self->delayed_work_source_); | 244 CFRunLoopSourceSignal(self->delayed_work_source_); |
| 218 } | 245 } |
| 219 | 246 |
| 220 // Called from the run loop. | 247 // Called from the run loop. |
| 221 // static | 248 // static |
| (...skipping 198 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 420 void* info) { | 447 void* info) { |
| 421 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); | 448 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); |
| 422 | 449 |
| 423 switch (activity) { | 450 switch (activity) { |
| 424 case kCFRunLoopEntry: | 451 case kCFRunLoopEntry: |
| 425 ++self->nesting_level_; | 452 ++self->nesting_level_; |
| 426 if (self->nesting_level_ > self->deepest_nesting_level_) { | 453 if (self->nesting_level_ > self->deepest_nesting_level_) { |
| 427 self->deepest_nesting_level_ = self->nesting_level_; | 454 self->deepest_nesting_level_ = self->nesting_level_; |
| 428 } | 455 } |
| 429 break; | 456 break; |
| 457 |
| 430 case kCFRunLoopExit: | 458 case kCFRunLoopExit: |
| 431 // Not all run loops go to sleep. If a run loop is stopped before it | 459 // Not all run loops go to sleep. If a run loop is stopped before it |
| 432 // goes to sleep due to a CFRunLoopStop call, or if the timeout passed | 460 // goes to sleep due to a CFRunLoopStop call, or if the timeout passed |
| 433 // to CFRunLoopRunInMode expires, the run loop may proceed directly from | 461 // to CFRunLoopRunInMode expires, the run loop may proceed directly from |
| 434 // handling sources to exiting without any sleep. This most commonly | 462 // handling sources to exiting without any sleep. This most commonly |
| 435 // occurs when CFRunLoopRunInMode is passed a timeout of 0, causing it | 463 // occurs when CFRunLoopRunInMode is passed a timeout of 0, causing it |
| 436 // to make a single pass through the loop and exit without sleep. Some | 464 // to make a single pass through the loop and exit without sleep. Some |
| 437 // native loops use CFRunLoop in this way. Because PreWaitObserver will | 465 // native loops use CFRunLoop in this way. Because PreWaitObserver will |
| 438 // not be called in these case, MaybeScheduleNestingDeferredWork needs | 466 // not be called in these case, MaybeScheduleNestingDeferredWork needs |
| 439 // to be called here, as the run loop exits. | 467 // to be called here, as the run loop exits. |
| 440 // | 468 // |
| 441 // MaybeScheduleNestingDeferredWork consults self->nesting_level_ | 469 // MaybeScheduleNestingDeferredWork consults self->nesting_level_ |
| 442 // to determine whether to schedule nesting-deferred work. It expects | 470 // to determine whether to schedule nesting-deferred work. It expects |
| 443 // the nesting level to be set to the depth of the loop that is going | 471 // the nesting level to be set to the depth of the loop that is going |
| 444 // to sleep or exiting. It must be called before decrementing the | 472 // to sleep or exiting. It must be called before decrementing the |
| 445 // value so that the value still corresponds to the level of the exiting | 473 // value so that the value still corresponds to the level of the exiting |
| 446 // loop. | 474 // loop. |
| 447 self->MaybeScheduleNestingDeferredWork(); | 475 self->MaybeScheduleNestingDeferredWork(); |
| 448 --self->nesting_level_; | 476 --self->nesting_level_; |
| 449 break; | 477 break; |
| 478 |
| 450 default: | 479 default: |
| 451 break; | 480 break; |
| 452 } | 481 } |
| 453 | 482 |
| 454 self->EnterExitRunLoop(activity); | 483 self->EnterExitRunLoop(activity); |
| 455 } | 484 } |
| 456 | 485 |
| 486 // Called from the run loop. |
| 487 // static |
| 488 void MessagePumpCFRunLoopBase::PowerStateNotification(void* info, |
| 489 io_service_t service, |
| 490 uint32_t message_type, |
| 491 void* message_argument) { |
| 492 // CFRunLoopTimer (NSTimer) is scheduled in terms of CFAbsoluteTime, which |
| 493 // measures the number of seconds since 2001-01-01 00:00:00.0 Z. It is |
| 494 // implemented in terms of kernel ticks, as in mach_absolute_time. While an |
| 495 // offset and scale factor can be applied to convert between the two time |
| 496 // bases at any time after boot, the kernel clock stops while the system is |
| 497 // asleep, altering the offset. (The offset will also change when the |
| 498 // real-time clock is adjusted.) CFRunLoopTimers are not readjusted to take |
| 499 // this into account when the system wakes up, so any timers that were |
| 500 // pending while the system was asleep will be delayed by the sleep |
| 501 // duration. |
| 502 // |
| 503 // The MessagePump interface assumes that scheduled delayed work will be |
| 504 // performed at the time ScheduleDelayedWork was asked to perform it. The |
| 505 // delay caused by the CFRunLoopTimer not firing at the appropriate time |
| 506 // results in a stall of queued delayed work when the system wakes up. |
| 507 // With this limitation, scheduled work would not be performed until |
| 508 // (system wake time + scheduled work time - system sleep time), while it |
| 509 // would be expected to be performed at (scheduled work time). |
| 510 // |
| 511 // To work around this problem, when the system wakes up from sleep, if a |
| 512 // delayed work timer is pending, it is rescheduled to fire at the original |
| 513 // time that it was scheduled to fire. |
| 514 // |
| 515 // This mechanism is not resilient if the real-time clock does not maintain |
| 516 // stable time while the system is sleeping, but it matches the behavior of |
| 517 // the various other MessagePump implementations, and MessageLoop seems to |
| 518 // be limited in the same way. |
| 519 // |
| 520 // References |
| 521 // - Chris Kane, "NSTimer and deep sleep," cocoa-dev@lists.apple.com, |
| 522 // http://lists.apple.com/archives/Cocoa-dev/2002/May/msg01547.html |
| 523 // - Apple Technical Q&A QA1340, "Registering and unregistering for sleep |
| 524 // and wake notifications," |
| 525 // http://developer.apple.com/mac/library/qa/qa2004/qa1340.html |
| 526 // - Core Foundation source code, CF-550/CFRunLoop.c and CF-550/CFDate.c |
| 527 |
| 528 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); |
| 529 |
| 530 switch (message_type) { |
| 531 case kIOMessageSystemWillPowerOn: |
| 532 if (self->delayed_work_fire_time_ != kCFTimeIntervalMax) { |
| 533 CFRunLoopTimerSetNextFireDate(self->delayed_work_timer_, |
| 534 self->delayed_work_fire_time_); |
| 535 } |
| 536 break; |
| 537 |
| 538 case kIOMessageSystemWillSleep: |
| 539 case kIOMessageCanSystemSleep: |
| 540 // The system will wait for 30 seconds before entering sleep if neither |
| 541 // IOAllowPowerChange nor IOCancelPowerChange are called. That would be |
| 542 // pretty antisocial. |
| 543 IOAllowPowerChange(self->root_power_domain_, |
| 544 reinterpret_cast<long>(message_argument)); |
| 545 break; |
| 546 |
| 547 default: |
| 548 break; |
| 549 } |
| 550 } |
| 551 |
| 457 // Called by MessagePumpCFRunLoopBase::EnterExitRunLoop. The default | 552 // Called by MessagePumpCFRunLoopBase::EnterExitRunLoop. The default |
| 458 // implementation is a no-op. | 553 // implementation is a no-op. |
| 459 void MessagePumpCFRunLoopBase::EnterExitRunLoop(CFRunLoopActivity activity) { | 554 void MessagePumpCFRunLoopBase::EnterExitRunLoop(CFRunLoopActivity activity) { |
| 460 } | 555 } |
| 461 | 556 |
| 462 MessagePumpCFRunLoop::MessagePumpCFRunLoop() | 557 MessagePumpCFRunLoop::MessagePumpCFRunLoop() |
| 463 : quit_pending_(false) { | 558 : quit_pending_(false) { |
| 464 } | 559 } |
| 465 | 560 |
| 466 // Called by MessagePumpCFRunLoopBase::DoRun. If other CFRunLoopRun loops were | 561 // Called by MessagePumpCFRunLoopBase::DoRun. If other CFRunLoopRun loops were |
| (...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 596 // static | 691 // static |
| 597 MessagePump* MessagePumpMac::Create() { | 692 MessagePump* MessagePumpMac::Create() { |
| 598 if ([NSThread isMainThread]) { | 693 if ([NSThread isMainThread]) { |
| 599 return new MessagePumpNSApplication; | 694 return new MessagePumpNSApplication; |
| 600 } | 695 } |
| 601 | 696 |
| 602 return new MessagePumpNSRunLoop; | 697 return new MessagePumpNSRunLoop; |
| 603 } | 698 } |
| 604 | 699 |
| 605 } // namespace base | 700 } // namespace base |
| OLD | NEW |