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

Unified 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, 2 months 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « base/message_pump_mac.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: base/message_pump_mac.mm
===================================================================
--- base/message_pump_mac.mm (revision 30239)
+++ base/message_pump_mac.mm (working copy)
@@ -6,6 +6,8 @@
#import <AppKit/AppKit.h>
#import <Foundation/Foundation.h>
+#include <IOKit/IOMessage.h>
+#include <IOKit/pwr_mgt/IOPMLib.h>
#include <limits>
@@ -27,6 +29,7 @@
// Must be called on the run loop thread.
MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase()
: delegate_(NULL),
+ delayed_work_fire_time_(kCFTimeIntervalMax),
nesting_level_(0),
run_nesting_level_(0),
deepest_nesting_level_(0),
@@ -103,12 +106,33 @@
EnterExitObserver,
&observer_context);
CFRunLoopAddObserver(run_loop_, enter_exit_observer_, kCFRunLoopCommonModes);
+
+ root_power_domain_ = IORegisterForSystemPower(this,
+ &power_notification_port_,
+ PowerStateNotification,
+ &power_notification_object_);
+ if (root_power_domain_ != MACH_PORT_NULL) {
+ CFRunLoopAddSource(
+ run_loop_,
+ IONotificationPortGetRunLoopSource(power_notification_port_),
+ kCFRunLoopCommonModes);
+ }
}
// Ideally called on the run loop thread. If other run loops were running
// lower on the run loop thread's stack when this object was created, the
// same number of run loops must be running when this object is destroyed.
MessagePumpCFRunLoopBase::~MessagePumpCFRunLoopBase() {
+ if (root_power_domain_ != MACH_PORT_NULL) {
+ CFRunLoopRemoveSource(
+ run_loop_,
+ IONotificationPortGetRunLoopSource(power_notification_port_),
+ kCFRunLoopCommonModes);
+ IODeregisterForSystemPower(&power_notification_object_);
+ IOServiceClose(root_power_domain_);
+ IONotificationPortDestroy(power_notification_port_);
+ }
+
CFRunLoopRemoveObserver(run_loop_, enter_exit_observer_,
kCFRunLoopCommonModes);
CFRelease(enter_exit_observer_);
@@ -198,9 +222,9 @@
exploded.minute,
seconds
};
- CFAbsoluteTime fire_time = CFGregorianDateGetAbsoluteTime(gregorian, NULL);
+ delayed_work_fire_time_ = CFGregorianDateGetAbsoluteTime(gregorian, NULL);
- CFRunLoopTimerSetNextFireDate(delayed_work_timer_, fire_time);
+ CFRunLoopTimerSetNextFireDate(delayed_work_timer_, delayed_work_fire_time_);
}
// Called from the run loop.
@@ -209,6 +233,9 @@
void* info) {
MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
+ // The timer won't fire again until it's reset.
+ self->delayed_work_fire_time_ = kCFTimeIntervalMax;
+
// CFRunLoopTimers fire outside of the priority scheme for CFRunLoopSources.
// In order to establish the proper priority where delegate_->DoDelayedWork
// can only be called if delegate_->DoWork returns false, the timer used
@@ -427,6 +454,7 @@
self->deepest_nesting_level_ = self->nesting_level_;
}
break;
+
case kCFRunLoopExit:
// Not all run loops go to sleep. If a run loop is stopped before it
// goes to sleep due to a CFRunLoopStop call, or if the timeout passed
@@ -447,6 +475,7 @@
self->MaybeScheduleNestingDeferredWork();
--self->nesting_level_;
break;
+
default:
break;
}
@@ -454,6 +483,72 @@
self->EnterExitRunLoop(activity);
}
+// Called from the run loop.
+// static
+void MessagePumpCFRunLoopBase::PowerStateNotification(void* info,
+ io_service_t service,
+ uint32_t message_type,
+ void* message_argument) {
+ // CFRunLoopTimer (NSTimer) is scheduled in terms of CFAbsoluteTime, which
+ // measures the number of seconds since 2001-01-01 00:00:00.0 Z. It is
+ // implemented in terms of kernel ticks, as in mach_absolute_time. While an
+ // offset and scale factor can be applied to convert between the two time
+ // bases at any time after boot, the kernel clock stops while the system is
+ // asleep, altering the offset. (The offset will also change when the
+ // real-time clock is adjusted.) CFRunLoopTimers are not readjusted to take
+ // this into account when the system wakes up, so any timers that were
+ // pending while the system was asleep will be delayed by the sleep
+ // duration.
+ //
+ // The MessagePump interface assumes that scheduled delayed work will be
+ // performed at the time ScheduleDelayedWork was asked to perform it. The
+ // delay caused by the CFRunLoopTimer not firing at the appropriate time
+ // results in a stall of queued delayed work when the system wakes up.
+ // With this limitation, scheduled work would not be performed until
+ // (system wake time + scheduled work time - system sleep time), while it
+ // would be expected to be performed at (scheduled work time).
+ //
+ // To work around this problem, when the system wakes up from sleep, if a
+ // delayed work timer is pending, it is rescheduled to fire at the original
+ // time that it was scheduled to fire.
+ //
+ // This mechanism is not resilient if the real-time clock does not maintain
+ // stable time while the system is sleeping, but it matches the behavior of
+ // the various other MessagePump implementations, and MessageLoop seems to
+ // be limited in the same way.
+ //
+ // References
+ // - Chris Kane, "NSTimer and deep sleep," cocoa-dev@lists.apple.com,
+ // http://lists.apple.com/archives/Cocoa-dev/2002/May/msg01547.html
+ // - Apple Technical Q&A QA1340, "Registering and unregistering for sleep
+ // and wake notifications,"
+ // http://developer.apple.com/mac/library/qa/qa2004/qa1340.html
+ // - Core Foundation source code, CF-550/CFRunLoop.c and CF-550/CFDate.c
+
+ MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
+
+ switch (message_type) {
+ case kIOMessageSystemWillPowerOn:
+ if (self->delayed_work_fire_time_ != kCFTimeIntervalMax) {
+ CFRunLoopTimerSetNextFireDate(self->delayed_work_timer_,
+ self->delayed_work_fire_time_);
+ }
+ break;
+
+ case kIOMessageSystemWillSleep:
+ case kIOMessageCanSystemSleep:
+ // The system will wait for 30 seconds before entering sleep if neither
+ // IOAllowPowerChange nor IOCancelPowerChange are called. That would be
+ // pretty antisocial.
+ IOAllowPowerChange(self->root_power_domain_,
+ reinterpret_cast<long>(message_argument));
+ break;
+
+ default:
+ break;
+ }
+}
+
// Called by MessagePumpCFRunLoopBase::EnterExitRunLoop. The default
// implementation is a no-op.
void MessagePumpCFRunLoopBase::EnterExitRunLoop(CFRunLoopActivity activity) {
« 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