Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 #import "base/message_loop/message_pump_mac.h" | 5 #import "base/message_loop/message_pump_mac.h" |
| 6 | 6 |
| 7 #import <Foundation/Foundation.h> | 7 #import <Foundation/Foundation.h> |
| 8 | 8 |
| 9 #include <limits> | 9 #include <limits> |
| 10 | 10 |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 62 void NoOp(void* info) { | 62 void NoOp(void* info) { |
| 63 } | 63 } |
| 64 | 64 |
| 65 const CFTimeInterval kCFTimeIntervalMax = | 65 const CFTimeInterval kCFTimeIntervalMax = |
| 66 std::numeric_limits<CFTimeInterval>::max(); | 66 std::numeric_limits<CFTimeInterval>::max(); |
| 67 | 67 |
| 68 #if !defined(OS_IOS) | 68 #if !defined(OS_IOS) |
| 69 // Set to true if MessagePumpMac::Create() is called before NSApp is | 69 // Set to true if MessagePumpMac::Create() is called before NSApp is |
| 70 // initialized. Only accessed from the main thread. | 70 // initialized. Only accessed from the main thread. |
| 71 bool g_not_using_cr_app = false; | 71 bool g_not_using_cr_app = false; |
| 72 | |
| 73 // Various CoreFoundation definitions. | |
| 74 typedef struct __CFRuntimeBase { | |
| 75 uintptr_t _cfisa; | |
| 76 uint8_t _cfinfo[4]; | |
| 77 #if __LP64__ | |
| 78 uint32_t _rc; | |
| 79 #endif | |
| 80 } CFRuntimeBase; | |
| 81 | |
| 82 #if defined(__BIG_ENDIAN__) | |
| 83 #define __CF_BIG_ENDIAN__ 1 | |
| 84 #define __CF_LITTLE_ENDIAN__ 0 | |
| 85 #endif | |
| 86 | |
| 87 #if defined(__LITTLE_ENDIAN__) | |
| 88 #define __CF_LITTLE_ENDIAN__ 1 | |
| 89 #define __CF_BIG_ENDIAN__ 0 | |
| 90 #endif | |
| 91 | |
| 92 #define CF_INFO_BITS (!!(__CF_BIG_ENDIAN__)*3) | |
| 93 | |
| 94 #define __CFBitfieldMask(N1, N2) \ | |
| 95 ((((UInt32)~0UL) << (31UL - (N1) + (N2))) >> (31UL - N1)) | |
| 96 #define __CFBitfieldSetValue(V, N1, N2, X) \ | |
| 97 ((V) = ((V) & ~__CFBitfieldMask(N1, N2)) | \ | |
| 98 (((X) << (N2)) & __CFBitfieldMask(N1, N2))) | |
| 99 | |
| 100 void ChromeCFSetValid(void* cf) { | |
| 101 __CFBitfieldSetValue(((CFRuntimeBase*)cf)->_cfinfo[CF_INFO_BITS], 3, 3, 1); | |
| 102 } | |
| 103 | |
| 104 void ChromeCFUnsetValid(void* cf) { | |
|
Mark Mentovai
2017/02/21 17:41:22
Might be better as a single function that takes a
shrike
2017/02/22 00:57:52
Acknowledged.
| |
| 105 __CFBitfieldSetValue(((CFRuntimeBase*)cf)->_cfinfo[CF_INFO_BITS], 3, 3, 0); | |
| 106 } | |
| 107 | |
| 108 // Marking timers as invalid at the right time helps significantly reduce power | |
| 109 // use (see the explanation in RunDelayedWorkTimer()), however there is no | |
| 110 // public API for doing so. CFRuntime.h states that CFRuntimeBase, upon which | |
| 111 // the above timer invalidation functions are based, can change from release to | |
| 112 // release and should not be accessed directly (this struct last changed in | |
| 113 // 2008 in CF-476). | |
|
Mark Mentovai
2017/02/21 18:10:57
Call out 10.5 by name too.
But this is kind of fu
shrike
2017/02/22 00:57:52
I added a unit test to make sure there's no side e
Mark Mentovai
2017/02/23 02:34:36
I’m mean that we’re flipping an internal bit that
shrike
2017/02/23 17:51:11
OK, I agree that there is a small chance of what y
| |
| 114 // | |
| 115 // This function uses private API to modify a test timer's valid state and | |
| 116 // uses public API to confirm that the private API changed the right bit. | |
| 117 bool CanInvalidateTimers() { | |
| 118 CFRunLoopTimerContext timer_context = CFRunLoopTimerContext(); | |
| 119 timer_context.info = NULL; | |
|
Mark Mentovai
2017/02/21 17:41:22
Use nullptr in new Chrome code whenever you have N
shrike
2017/02/22 00:57:52
Thank you. Fixed.
| |
| 120 ScopedCFTypeRef<CFRunLoopTimerRef> test_timer( | |
| 121 CFRunLoopTimerCreate(NULL, // allocator | |
| 122 kCFTimeIntervalMax, // fire time | |
| 123 kCFTimeIntervalMax, // interval | |
| 124 0, // flags | |
| 125 0, // priority | |
| 126 NULL, &timer_context)); | |
| 127 // Should be valid from the start. | |
| 128 if (!CFRunLoopTimerIsValid(test_timer)) { | |
| 129 return false; | |
| 130 } | |
| 131 // Confirm that the private API can mark the timer invalid. | |
| 132 ChromeCFUnsetValid(test_timer); | |
| 133 if (CFRunLoopTimerIsValid(test_timer)) { | |
| 134 return false; | |
| 135 } | |
| 136 // Confirm that the private API can mark the timer valid. | |
| 137 ChromeCFSetValid(test_timer); | |
| 138 return CFRunLoopTimerIsValid(test_timer); | |
| 139 } | |
| 72 #endif | 140 #endif |
| 73 | 141 |
| 74 } // namespace | 142 } // namespace |
| 75 | 143 |
| 76 // static | 144 // static |
| 77 const CFStringRef kMessageLoopExclusiveRunLoopMode = | 145 const CFStringRef kMessageLoopExclusiveRunLoopMode = |
| 78 CFSTR("kMessageLoopExclusiveRunLoopMode"); | 146 CFSTR("kMessageLoopExclusiveRunLoopMode"); |
| 79 | 147 |
| 80 // A scoper for autorelease pools created from message pump run loops. | 148 // A scoper for autorelease pools created from message pump run loops. |
| 81 // Avoids dirtying up the ScopedNSAutoreleasePool interface for the rare | 149 // Avoids dirtying up the ScopedNSAutoreleasePool interface for the rare |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 92 private: | 160 private: |
| 93 NSAutoreleasePool* pool_; | 161 NSAutoreleasePool* pool_; |
| 94 DISALLOW_COPY_AND_ASSIGN(MessagePumpScopedAutoreleasePool); | 162 DISALLOW_COPY_AND_ASSIGN(MessagePumpScopedAutoreleasePool); |
| 95 }; | 163 }; |
| 96 | 164 |
| 97 // Must be called on the run loop thread. | 165 // Must be called on the run loop thread. |
| 98 MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase() | 166 MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase() |
| 99 : delegate_(NULL), | 167 : delegate_(NULL), |
| 100 delayed_work_fire_time_(kCFTimeIntervalMax), | 168 delayed_work_fire_time_(kCFTimeIntervalMax), |
| 101 timer_slack_(base::TIMER_SLACK_NONE), | 169 timer_slack_(base::TIMER_SLACK_NONE), |
| 170 can_invalidate_timers_(false), | |
|
Mark Mentovai
2017/02/21 17:41:22
Might as well wrap this and the member variable in
shrike
2017/02/22 00:57:52
This has been removed, per your feedback below.
| |
| 102 nesting_level_(0), | 171 nesting_level_(0), |
| 103 run_nesting_level_(0), | 172 run_nesting_level_(0), |
| 104 deepest_nesting_level_(0), | 173 deepest_nesting_level_(0), |
| 105 delegateless_work_(false), | 174 delegateless_work_(false), |
| 106 delegateless_idle_work_(false) { | 175 delegateless_idle_work_(false) { |
| 176 #if !defined(OS_IOS) | |
| 177 can_invalidate_timers_ = CanInvalidateTimers(); | |
| 178 #endif // !defined(OS_IOS) | |
| 179 | |
| 107 run_loop_ = CFRunLoopGetCurrent(); | 180 run_loop_ = CFRunLoopGetCurrent(); |
| 108 CFRetain(run_loop_); | 181 CFRetain(run_loop_); |
| 109 | 182 |
| 110 // Set a repeating timer with a preposterous firing time and interval. The | 183 // Set a repeating timer with a preposterous firing time and interval. The |
| 111 // timer will effectively never fire as-is. The firing time will be adjusted | 184 // timer will effectively never fire as-is. The firing time will be adjusted |
| 112 // as needed when ScheduleDelayedWork is called. | 185 // as needed when ScheduleDelayedWork is called. |
| 113 CFRunLoopTimerContext timer_context = CFRunLoopTimerContext(); | 186 CFRunLoopTimerContext timer_context = CFRunLoopTimerContext(); |
| 114 timer_context.info = this; | 187 timer_context.info = this; |
| 115 delayed_work_timer_ = CFRunLoopTimerCreate(NULL, // allocator | 188 delayed_work_timer_ = CFRunLoopTimerCreate(NULL, // allocator |
| 116 kCFTimeIntervalMax, // fire time | 189 kCFTimeIntervalMax, // fire time |
| (...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 236 void MessagePumpCFRunLoopBase::ScheduleWork() { | 309 void MessagePumpCFRunLoopBase::ScheduleWork() { |
| 237 CFRunLoopSourceSignal(work_source_); | 310 CFRunLoopSourceSignal(work_source_); |
| 238 CFRunLoopWakeUp(run_loop_); | 311 CFRunLoopWakeUp(run_loop_); |
| 239 } | 312 } |
| 240 | 313 |
| 241 // Must be called on the run loop thread. | 314 // Must be called on the run loop thread. |
| 242 void MessagePumpCFRunLoopBase::ScheduleDelayedWork( | 315 void MessagePumpCFRunLoopBase::ScheduleDelayedWork( |
| 243 const TimeTicks& delayed_work_time) { | 316 const TimeTicks& delayed_work_time) { |
| 244 TimeDelta delta = delayed_work_time - TimeTicks::Now(); | 317 TimeDelta delta = delayed_work_time - TimeTicks::Now(); |
| 245 delayed_work_fire_time_ = CFAbsoluteTimeGetCurrent() + delta.InSecondsF(); | 318 delayed_work_fire_time_ = CFAbsoluteTimeGetCurrent() + delta.InSecondsF(); |
| 319 #if !defined(OS_IOS) | |
| 320 // Please see the comment in RunDelayedWorkTimer() for more info. | |
| 321 if (can_invalidate_timers_) { | |
|
Mark Mentovai
2017/02/21 17:41:22
Actually, I think that we can make this unconditio
shrike
2017/02/22 00:57:52
Got it. That's clean :-).
| |
| 322 ChromeCFSetValid(delayed_work_timer_); | |
|
Mark Mentovai
2017/02/21 17:41:22
Should this happen before (as done here) or after
shrike
2017/02/22 00:57:52
Done.
| |
| 323 } | |
| 324 #endif // !defined(OS_IOS) | |
| 246 CFRunLoopTimerSetNextFireDate(delayed_work_timer_, delayed_work_fire_time_); | 325 CFRunLoopTimerSetNextFireDate(delayed_work_timer_, delayed_work_fire_time_); |
| 247 if (timer_slack_ == TIMER_SLACK_MAXIMUM) { | 326 if (timer_slack_ == TIMER_SLACK_MAXIMUM) { |
| 248 CFRunLoopTimerSetTolerance(delayed_work_timer_, delta.InSecondsF() * 0.5); | 327 CFRunLoopTimerSetTolerance(delayed_work_timer_, delta.InSecondsF() * 0.5); |
| 249 } else { | 328 } else { |
| 250 CFRunLoopTimerSetTolerance(delayed_work_timer_, 0); | 329 CFRunLoopTimerSetTolerance(delayed_work_timer_, 0); |
| 251 } | 330 } |
| 252 } | 331 } |
| 253 | 332 |
| 254 void MessagePumpCFRunLoopBase::SetTimerSlack(TimerSlack timer_slack) { | 333 void MessagePumpCFRunLoopBase::SetTimerSlack(TimerSlack timer_slack) { |
| 255 timer_slack_ = timer_slack; | 334 timer_slack_ = timer_slack; |
| 256 } | 335 } |
| 257 | 336 |
| 258 // Called from the run loop. | 337 // Called from the run loop. |
| 259 // static | 338 // static |
| 260 void MessagePumpCFRunLoopBase::RunDelayedWorkTimer(CFRunLoopTimerRef timer, | 339 void MessagePumpCFRunLoopBase::RunDelayedWorkTimer(CFRunLoopTimerRef timer, |
| 261 void* info) { | 340 void* info) { |
| 262 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); | 341 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); |
| 263 | 342 |
| 264 // The timer won't fire again until it's reset. | 343 // The timer won't fire again until it's reset. |
| 265 self->delayed_work_fire_time_ = kCFTimeIntervalMax; | 344 self->delayed_work_fire_time_ = kCFTimeIntervalMax; |
| 266 | 345 |
| 346 #if !defined(OS_IOS) | |
| 347 // The message pump's timer needs to fire at changing and unpredictable | |
| 348 // intervals. Creating a new timer for each firing time is very expensive, so | |
| 349 // the message pump instead uses a repeating timer with a very large repeat | |
| 350 // rate. After each firing of the timer, the run loop sets the timer's next | |
| 351 // firing time to the distant future, essentially pausing the timer until the | |
| 352 // pump sets the next firing time. This is the solution recommended by Apple. | |
| 353 // | |
| 354 // It turns out, however, that scheduling timers is also quite expensive, and | |
| 355 // that every one of the message pump's timer firings incurs two | |
| 356 // reschedulings. The first rescheduling occurs in ScheduleDelayedWork(), | |
| 357 // which sets the desired next firing time. The second comes after exiting | |
| 358 // this method (the timer's callback method), when the run loop sets the | |
| 359 // timer's next firing time to far in the future. | |
| 360 // | |
| 361 // The code in __CFRunLoopDoTimer() inside CFRunLoop.c calls the timer's | |
| 362 // callback, confirms that the timer is valid, and then sets its future | |
| 363 // firing time based on its repeat frequency. Flipping the valid bit here | |
| 364 // causes the __CFRunLoopDoTimer() to skip setting the future firing time. | |
| 365 // Note that there's public API to invaildate a timer but it goes beyond | |
| 366 // flipping the valid bit, making the timer unusable in the future. | |
| 367 // | |
| 368 // ScheduleDelayedWork() flips the valid bit back just before setting the | |
| 369 // timer's new firing time. | |
| 370 if (self->can_invalidate_timers_) { | |
| 371 ChromeCFUnsetValid(self->delayed_work_timer_); | |
| 372 } | |
| 373 #endif // !defined(OS_IOS) | |
| 374 | |
| 267 // CFRunLoopTimers fire outside of the priority scheme for CFRunLoopSources. | 375 // CFRunLoopTimers fire outside of the priority scheme for CFRunLoopSources. |
| 268 // In order to establish the proper priority in which work and delayed work | 376 // In order to establish the proper priority in which work and delayed work |
| 269 // are processed one for one, the timer used to schedule delayed work must | 377 // are processed one for one, the timer used to schedule delayed work must |
| 270 // signal a CFRunLoopSource used to dispatch both work and delayed work. | 378 // signal a CFRunLoopSource used to dispatch both work and delayed work. |
| 271 CFRunLoopSourceSignal(self->work_source_); | 379 CFRunLoopSourceSignal(self->work_source_); |
| 272 } | 380 } |
| 273 | 381 |
| 274 // Called from the run loop. | 382 // Called from the run loop. |
| 275 // static | 383 // static |
| 276 void MessagePumpCFRunLoopBase::RunWorkSource(void* info) { | 384 void MessagePumpCFRunLoopBase::RunWorkSource(void* info) { |
| (...skipping 479 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 756 [NSApplication sharedApplication]; | 864 [NSApplication sharedApplication]; |
| 757 g_not_using_cr_app = true; | 865 g_not_using_cr_app = true; |
| 758 return new MessagePumpNSApplication; | 866 return new MessagePumpNSApplication; |
| 759 #endif | 867 #endif |
| 760 } | 868 } |
| 761 | 869 |
| 762 return new MessagePumpNSRunLoop; | 870 return new MessagePumpNSRunLoop; |
| 763 } | 871 } |
| 764 | 872 |
| 765 } // namespace base | 873 } // namespace base |
| OLD | NEW |