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; |
72 #endif | 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 // Marking timers as invalid at the right time by flipping their valid bit helps |
| 101 // significantly reduce power use (see the explanation in |
| 102 // RunDelayedWorkTimer()), however there is no public API for doing so. |
| 103 // CFRuntime.h states that CFRuntimeBase can change from release to release |
| 104 // and should not be accessed directly. The last known change of this struct |
| 105 // occurred in 2008 in CF-476 / 10.5; unfortunately the source for 10.11 and |
| 106 // 10.12 is not available for inspection at this time. |
| 107 // CanInvalidateCFRunLoopTimers() will at least prevent us from invalidating |
| 108 // timers if this function starts flipping the wrong bit on a future OS release. |
| 109 void __ChromeCFRunLoopTimerSetValid(CFRunLoopTimerRef timer, bool valid) { |
| 110 __CFBitfieldSetValue(((CFRuntimeBase*)timer)->_cfinfo[CF_INFO_BITS], 3, 3, |
| 111 valid); |
| 112 } |
| 113 #endif // !defined(OS_IOS) |
73 | 114 |
74 } // namespace | 115 } // namespace |
75 | 116 |
76 // static | 117 // static |
77 const CFStringRef kMessageLoopExclusiveRunLoopMode = | 118 const CFStringRef kMessageLoopExclusiveRunLoopMode = |
78 CFSTR("kMessageLoopExclusiveRunLoopMode"); | 119 CFSTR("kMessageLoopExclusiveRunLoopMode"); |
79 | 120 |
80 // A scoper for autorelease pools created from message pump run loops. | 121 // A scoper for autorelease pools created from message pump run loops. |
81 // Avoids dirtying up the ScopedNSAutoreleasePool interface for the rare | 122 // Avoids dirtying up the ScopedNSAutoreleasePool interface for the rare |
82 // case where an autorelease pool needs to be passed in. | 123 // case where an autorelease pool needs to be passed in. |
83 class MessagePumpScopedAutoreleasePool { | 124 class MessagePumpScopedAutoreleasePool { |
84 public: | 125 public: |
85 explicit MessagePumpScopedAutoreleasePool(MessagePumpCFRunLoopBase* pump) : | 126 explicit MessagePumpScopedAutoreleasePool(MessagePumpCFRunLoopBase* pump) : |
86 pool_(pump->CreateAutoreleasePool()) { | 127 pool_(pump->CreateAutoreleasePool()) { |
87 } | 128 } |
88 ~MessagePumpScopedAutoreleasePool() { | 129 ~MessagePumpScopedAutoreleasePool() { |
89 [pool_ drain]; | 130 [pool_ drain]; |
90 } | 131 } |
91 | 132 |
92 private: | 133 private: |
93 NSAutoreleasePool* pool_; | 134 NSAutoreleasePool* pool_; |
94 DISALLOW_COPY_AND_ASSIGN(MessagePumpScopedAutoreleasePool); | 135 DISALLOW_COPY_AND_ASSIGN(MessagePumpScopedAutoreleasePool); |
95 }; | 136 }; |
96 | 137 |
| 138 #if !defined(OS_IOS) |
| 139 // This function uses private API to modify a test timer's valid state and |
| 140 // uses public API to confirm that the private API changed the correct bit. |
| 141 // static |
| 142 bool MessagePumpCFRunLoopBase::CanInvalidateCFRunLoopTimers() { |
| 143 CFRunLoopTimerContext timer_context = CFRunLoopTimerContext(); |
| 144 timer_context.info = nullptr; |
| 145 ScopedCFTypeRef<CFRunLoopTimerRef> test_timer( |
| 146 CFRunLoopTimerCreate(NULL, // allocator |
| 147 kCFTimeIntervalMax, // fire time |
| 148 kCFTimeIntervalMax, // interval |
| 149 0, // flags |
| 150 0, // priority |
| 151 nullptr, &timer_context)); |
| 152 // Should be valid from the start. |
| 153 if (!CFRunLoopTimerIsValid(test_timer)) { |
| 154 return false; |
| 155 } |
| 156 // Confirm that the private API can mark the timer invalid. |
| 157 __ChromeCFRunLoopTimerSetValid(test_timer, false); |
| 158 if (CFRunLoopTimerIsValid(test_timer)) { |
| 159 return false; |
| 160 } |
| 161 // Confirm that the private API can mark the timer valid. |
| 162 __ChromeCFRunLoopTimerSetValid(test_timer, true); |
| 163 return CFRunLoopTimerIsValid(test_timer); |
| 164 } |
| 165 #endif // !defined(OS_IOS) |
| 166 |
| 167 // static |
| 168 void MessagePumpCFRunLoopBase::ChromeCFRunLoopTimerSetValid( |
| 169 CFRunLoopTimerRef timer, |
| 170 bool valid) { |
| 171 #if !defined(OS_IOS) |
| 172 static bool can_invalidate_timers = CanInvalidateCFRunLoopTimers(); |
| 173 if (can_invalidate_timers) { |
| 174 __ChromeCFRunLoopTimerSetValid(timer, valid); |
| 175 } |
| 176 #endif // !defined(OS_IOS) |
| 177 } |
| 178 |
97 // Must be called on the run loop thread. | 179 // Must be called on the run loop thread. |
98 MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase() | 180 MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase() |
99 : delegate_(NULL), | 181 : delegate_(NULL), |
100 delayed_work_fire_time_(kCFTimeIntervalMax), | 182 delayed_work_fire_time_(kCFTimeIntervalMax), |
101 timer_slack_(base::TIMER_SLACK_NONE), | 183 timer_slack_(base::TIMER_SLACK_NONE), |
102 nesting_level_(0), | 184 nesting_level_(0), |
103 run_nesting_level_(0), | 185 run_nesting_level_(0), |
104 deepest_nesting_level_(0), | 186 deepest_nesting_level_(0), |
105 delegateless_work_(false), | 187 delegateless_work_(false), |
106 delegateless_idle_work_(false) { | 188 delegateless_idle_work_(false) { |
(...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
236 void MessagePumpCFRunLoopBase::ScheduleWork() { | 318 void MessagePumpCFRunLoopBase::ScheduleWork() { |
237 CFRunLoopSourceSignal(work_source_); | 319 CFRunLoopSourceSignal(work_source_); |
238 CFRunLoopWakeUp(run_loop_); | 320 CFRunLoopWakeUp(run_loop_); |
239 } | 321 } |
240 | 322 |
241 // Must be called on the run loop thread. | 323 // Must be called on the run loop thread. |
242 void MessagePumpCFRunLoopBase::ScheduleDelayedWork( | 324 void MessagePumpCFRunLoopBase::ScheduleDelayedWork( |
243 const TimeTicks& delayed_work_time) { | 325 const TimeTicks& delayed_work_time) { |
244 TimeDelta delta = delayed_work_time - TimeTicks::Now(); | 326 TimeDelta delta = delayed_work_time - TimeTicks::Now(); |
245 delayed_work_fire_time_ = CFAbsoluteTimeGetCurrent() + delta.InSecondsF(); | 327 delayed_work_fire_time_ = CFAbsoluteTimeGetCurrent() + delta.InSecondsF(); |
| 328 |
| 329 // Flip the timer's validation bit just before setting the new fire time. Do |
| 330 // this now because CFRunLoopTimerSetNextFireDate() likely checks the validity |
| 331 // of a timer before proceeding to set its fire date. Making the timer valid |
| 332 // now won't have any side effects (such as a premature firing of the timer) |
| 333 // because we're only flipping a bit. |
| 334 // |
| 335 // Please see the comment in RunDelayedWorkTimer() for more info on the whys |
| 336 // of invalidation. |
| 337 ChromeCFRunLoopTimerSetValid(delayed_work_timer_, true); |
| 338 |
246 CFRunLoopTimerSetNextFireDate(delayed_work_timer_, delayed_work_fire_time_); | 339 CFRunLoopTimerSetNextFireDate(delayed_work_timer_, delayed_work_fire_time_); |
247 if (timer_slack_ == TIMER_SLACK_MAXIMUM) { | 340 if (timer_slack_ == TIMER_SLACK_MAXIMUM) { |
248 CFRunLoopTimerSetTolerance(delayed_work_timer_, delta.InSecondsF() * 0.5); | 341 CFRunLoopTimerSetTolerance(delayed_work_timer_, delta.InSecondsF() * 0.5); |
249 } else { | 342 } else { |
250 CFRunLoopTimerSetTolerance(delayed_work_timer_, 0); | 343 CFRunLoopTimerSetTolerance(delayed_work_timer_, 0); |
251 } | 344 } |
252 } | 345 } |
253 | 346 |
254 void MessagePumpCFRunLoopBase::SetTimerSlack(TimerSlack timer_slack) { | 347 void MessagePumpCFRunLoopBase::SetTimerSlack(TimerSlack timer_slack) { |
255 timer_slack_ = timer_slack; | 348 timer_slack_ = timer_slack; |
256 } | 349 } |
257 | 350 |
258 // Called from the run loop. | 351 // Called from the run loop. |
259 // static | 352 // static |
260 void MessagePumpCFRunLoopBase::RunDelayedWorkTimer(CFRunLoopTimerRef timer, | 353 void MessagePumpCFRunLoopBase::RunDelayedWorkTimer(CFRunLoopTimerRef timer, |
261 void* info) { | 354 void* info) { |
262 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); | 355 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); |
263 | 356 |
264 // The timer won't fire again until it's reset. | 357 // The timer won't fire again until it's reset. |
265 self->delayed_work_fire_time_ = kCFTimeIntervalMax; | 358 self->delayed_work_fire_time_ = kCFTimeIntervalMax; |
266 | 359 |
| 360 // The message pump's timer needs to fire at changing and unpredictable |
| 361 // intervals. Creating a new timer for each firing time is very expensive, so |
| 362 // the message pump instead uses a repeating timer with a very large repeat |
| 363 // rate. After each firing of the timer, the run loop sets the timer's next |
| 364 // firing time to the distant future, essentially pausing the timer until the |
| 365 // pump sets the next firing time. This is the solution recommended by Apple. |
| 366 // |
| 367 // It turns out, however, that scheduling timers is also quite expensive, and |
| 368 // that every one of the message pump's timer firings incurs two |
| 369 // reschedulings. The first rescheduling occurs in ScheduleDelayedWork(), |
| 370 // which sets the desired next firing time. The second comes after exiting |
| 371 // this method (the timer's callback method), when the run loop sets the |
| 372 // timer's next firing time to far in the future. |
| 373 // |
| 374 // The code in __CFRunLoopDoTimer() inside CFRunLoop.c calls the timer's |
| 375 // callback, confirms that the timer is valid, and then sets its future |
| 376 // firing time based on its repeat frequency. Flipping the valid bit here |
| 377 // causes the __CFRunLoopDoTimer() to skip setting the future firing time. |
| 378 // Note that there's public API to invalidate a timer but it goes beyond |
| 379 // flipping the valid bit, making the timer unusable in the future. |
| 380 // |
| 381 // ScheduleDelayedWork() flips the valid bit back just before setting the |
| 382 // timer's new firing time. |
| 383 ChromeCFRunLoopTimerSetValid(self->delayed_work_timer_, false); |
| 384 |
267 // CFRunLoopTimers fire outside of the priority scheme for CFRunLoopSources. | 385 // CFRunLoopTimers fire outside of the priority scheme for CFRunLoopSources. |
268 // In order to establish the proper priority in which work and delayed work | 386 // 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 | 387 // 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. | 388 // signal a CFRunLoopSource used to dispatch both work and delayed work. |
271 CFRunLoopSourceSignal(self->work_source_); | 389 CFRunLoopSourceSignal(self->work_source_); |
272 } | 390 } |
273 | 391 |
274 // Called from the run loop. | 392 // Called from the run loop. |
275 // static | 393 // static |
276 void MessagePumpCFRunLoopBase::RunWorkSource(void* info) { | 394 void MessagePumpCFRunLoopBase::RunWorkSource(void* info) { |
(...skipping 479 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
756 [NSApplication sharedApplication]; | 874 [NSApplication sharedApplication]; |
757 g_not_using_cr_app = true; | 875 g_not_using_cr_app = true; |
758 return new MessagePumpNSApplication; | 876 return new MessagePumpNSApplication; |
759 #endif | 877 #endif |
760 } | 878 } |
761 | 879 |
762 return new MessagePumpNSRunLoop; | 880 return new MessagePumpNSRunLoop; |
763 } | 881 } |
764 | 882 |
765 } // namespace base | 883 } // namespace base |
OLD | NEW |