Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(465)

Side by Side Diff: base/message_pump_mac.mm

Issue 342011: Reset the Mac message pump's delayed work timer upon waking from system sleep (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 11 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « base/message_pump_mac.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
OLDNEW
« no previous file with comments | « base/message_pump_mac.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698