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> | 9 #include <IOKit/IOMessage.h> |
10 #include <IOKit/pwr_mgt/IOPMLib.h> | 10 #include <IOKit/pwr_mgt/IOPMLib.h> |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
44 }; | 44 }; |
45 | 45 |
46 // Must be called on the run loop thread. | 46 // Must be called on the run loop thread. |
47 MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase() | 47 MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase() |
48 : delegate_(NULL), | 48 : delegate_(NULL), |
49 delayed_work_fire_time_(kCFTimeIntervalMax), | 49 delayed_work_fire_time_(kCFTimeIntervalMax), |
50 nesting_level_(0), | 50 nesting_level_(0), |
51 run_nesting_level_(0), | 51 run_nesting_level_(0), |
52 deepest_nesting_level_(0), | 52 deepest_nesting_level_(0), |
53 delegateless_work_(false), | 53 delegateless_work_(false), |
54 delegateless_delayed_work_(false), | |
55 delegateless_idle_work_(false) { | 54 delegateless_idle_work_(false) { |
56 run_loop_ = CFRunLoopGetCurrent(); | 55 run_loop_ = CFRunLoopGetCurrent(); |
57 CFRetain(run_loop_); | 56 CFRetain(run_loop_); |
58 | 57 |
59 // Set a repeating timer with a preposterous firing time and interval. The | 58 // Set a repeating timer with a preposterous firing time and interval. The |
60 // timer will effectively never fire as-is. The firing time will be adjusted | 59 // timer will effectively never fire as-is. The firing time will be adjusted |
61 // as needed when ScheduleDelayedWork is called. | 60 // as needed when ScheduleDelayedWork is called. |
62 CFRunLoopTimerContext timer_context = CFRunLoopTimerContext(); | 61 CFRunLoopTimerContext timer_context = CFRunLoopTimerContext(); |
63 timer_context.info = this; | 62 timer_context.info = this; |
64 delayed_work_timer_ = CFRunLoopTimerCreate(NULL, // allocator | 63 delayed_work_timer_ = CFRunLoopTimerCreate(NULL, // allocator |
65 kCFTimeIntervalMax, // fire time | 64 kCFTimeIntervalMax, // fire time |
66 kCFTimeIntervalMax, // interval | 65 kCFTimeIntervalMax, // interval |
67 0, // flags | 66 0, // flags |
68 0, // priority | 67 0, // priority |
69 RunDelayedWorkTimer, | 68 RunDelayedWorkTimer, |
70 &timer_context); | 69 &timer_context); |
71 CFRunLoopAddTimer(run_loop_, delayed_work_timer_, kCFRunLoopCommonModes); | 70 CFRunLoopAddTimer(run_loop_, delayed_work_timer_, kCFRunLoopCommonModes); |
72 | 71 |
73 CFRunLoopSourceContext source_context = CFRunLoopSourceContext(); | 72 CFRunLoopSourceContext source_context = CFRunLoopSourceContext(); |
74 source_context.info = this; | 73 source_context.info = this; |
75 source_context.perform = RunWorkSource; | 74 source_context.perform = RunWorkSource; |
76 work_source_ = CFRunLoopSourceCreate(NULL, // allocator | 75 work_source_ = CFRunLoopSourceCreate(NULL, // allocator |
77 1, // priority | 76 1, // priority |
78 &source_context); | 77 &source_context); |
79 CFRunLoopAddSource(run_loop_, work_source_, kCFRunLoopCommonModes); | 78 CFRunLoopAddSource(run_loop_, work_source_, kCFRunLoopCommonModes); |
80 | 79 |
81 source_context.perform = RunDelayedWorkSource; | |
82 delayed_work_source_ = CFRunLoopSourceCreate(NULL, // allocator | |
83 2, // priority | |
84 &source_context); | |
85 CFRunLoopAddSource(run_loop_, delayed_work_source_, kCFRunLoopCommonModes); | |
86 | |
87 source_context.perform = RunIdleWorkSource; | 80 source_context.perform = RunIdleWorkSource; |
88 idle_work_source_ = CFRunLoopSourceCreate(NULL, // allocator | 81 idle_work_source_ = CFRunLoopSourceCreate(NULL, // allocator |
89 3, // priority | 82 2, // priority |
90 &source_context); | 83 &source_context); |
91 CFRunLoopAddSource(run_loop_, idle_work_source_, kCFRunLoopCommonModes); | 84 CFRunLoopAddSource(run_loop_, idle_work_source_, kCFRunLoopCommonModes); |
92 | 85 |
93 source_context.perform = RunNestingDeferredWorkSource; | 86 source_context.perform = RunNestingDeferredWorkSource; |
94 nesting_deferred_work_source_ = CFRunLoopSourceCreate(NULL, // allocator | 87 nesting_deferred_work_source_ = CFRunLoopSourceCreate(NULL, // allocator |
95 0, // priority | 88 0, // priority |
96 &source_context); | 89 &source_context); |
97 CFRunLoopAddSource(run_loop_, nesting_deferred_work_source_, | 90 CFRunLoopAddSource(run_loop_, nesting_deferred_work_source_, |
98 kCFRunLoopCommonModes); | 91 kCFRunLoopCommonModes); |
99 | 92 |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
162 kCFRunLoopCommonModes); | 155 kCFRunLoopCommonModes); |
163 CFRelease(pre_wait_observer_); | 156 CFRelease(pre_wait_observer_); |
164 | 157 |
165 CFRunLoopRemoveSource(run_loop_, nesting_deferred_work_source_, | 158 CFRunLoopRemoveSource(run_loop_, nesting_deferred_work_source_, |
166 kCFRunLoopCommonModes); | 159 kCFRunLoopCommonModes); |
167 CFRelease(nesting_deferred_work_source_); | 160 CFRelease(nesting_deferred_work_source_); |
168 | 161 |
169 CFRunLoopRemoveSource(run_loop_, idle_work_source_, kCFRunLoopCommonModes); | 162 CFRunLoopRemoveSource(run_loop_, idle_work_source_, kCFRunLoopCommonModes); |
170 CFRelease(idle_work_source_); | 163 CFRelease(idle_work_source_); |
171 | 164 |
172 CFRunLoopRemoveSource(run_loop_, delayed_work_source_, kCFRunLoopCommonModes); | |
173 CFRelease(delayed_work_source_); | |
174 | |
175 CFRunLoopRemoveSource(run_loop_, work_source_, kCFRunLoopCommonModes); | 165 CFRunLoopRemoveSource(run_loop_, work_source_, kCFRunLoopCommonModes); |
176 CFRelease(work_source_); | 166 CFRelease(work_source_); |
177 | 167 |
178 CFRunLoopRemoveTimer(run_loop_, delayed_work_timer_, kCFRunLoopCommonModes); | 168 CFRunLoopRemoveTimer(run_loop_, delayed_work_timer_, kCFRunLoopCommonModes); |
179 CFRelease(delayed_work_timer_); | 169 CFRelease(delayed_work_timer_); |
180 | 170 |
181 CFRelease(run_loop_); | 171 CFRelease(run_loop_); |
182 } | 172 } |
183 | 173 |
184 // Must be called on the run loop thread. | 174 // Must be called on the run loop thread. |
185 void MessagePumpCFRunLoopBase::Run(Delegate* delegate) { | 175 void MessagePumpCFRunLoopBase::Run(Delegate* delegate) { |
186 // nesting_level_ will be incremented in EnterExitRunLoop, so set | 176 // nesting_level_ will be incremented in EnterExitRunLoop, so set |
187 // run_nesting_level_ accordingly. | 177 // run_nesting_level_ accordingly. |
188 int last_run_nesting_level = run_nesting_level_; | 178 int last_run_nesting_level = run_nesting_level_; |
189 run_nesting_level_ = nesting_level_ + 1; | 179 run_nesting_level_ = nesting_level_ + 1; |
190 | 180 |
191 Delegate* last_delegate = delegate_; | 181 Delegate* last_delegate = delegate_; |
192 delegate_ = delegate; | 182 delegate_ = delegate; |
193 | 183 |
194 if (delegate) { | 184 if (delegate) { |
195 // If any work showed up but could not be dispatched for want of a | 185 // If any work showed up but could not be dispatched for want of a |
196 // delegate, set it up for dispatch again now that a delegate is | 186 // delegate, set it up for dispatch again now that a delegate is |
197 // available. | 187 // available. |
198 if (delegateless_work_) { | 188 if (delegateless_work_) { |
199 CFRunLoopSourceSignal(work_source_); | 189 CFRunLoopSourceSignal(work_source_); |
200 delegateless_work_ = false; | 190 delegateless_work_ = false; |
201 } | 191 } |
202 if (delegateless_delayed_work_) { | |
203 CFRunLoopSourceSignal(delayed_work_source_); | |
204 delegateless_delayed_work_ = false; | |
205 } | |
206 if (delegateless_idle_work_) { | 192 if (delegateless_idle_work_) { |
207 CFRunLoopSourceSignal(idle_work_source_); | 193 CFRunLoopSourceSignal(idle_work_source_); |
208 delegateless_idle_work_ = false; | 194 delegateless_idle_work_ = false; |
209 } | 195 } |
210 } | 196 } |
211 | 197 |
212 DoRun(delegate); | 198 DoRun(delegate); |
213 | 199 |
214 // Restore the previous state of the object. | 200 // Restore the previous state of the object. |
215 delegate_ = last_delegate; | 201 delegate_ = last_delegate; |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
253 // Called from the run loop. | 239 // Called from the run loop. |
254 // static | 240 // static |
255 void MessagePumpCFRunLoopBase::RunDelayedWorkTimer(CFRunLoopTimerRef timer, | 241 void MessagePumpCFRunLoopBase::RunDelayedWorkTimer(CFRunLoopTimerRef timer, |
256 void* info) { | 242 void* info) { |
257 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); | 243 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); |
258 | 244 |
259 // The timer won't fire again until it's reset. | 245 // The timer won't fire again until it's reset. |
260 self->delayed_work_fire_time_ = kCFTimeIntervalMax; | 246 self->delayed_work_fire_time_ = kCFTimeIntervalMax; |
261 | 247 |
262 // CFRunLoopTimers fire outside of the priority scheme for CFRunLoopSources. | 248 // CFRunLoopTimers fire outside of the priority scheme for CFRunLoopSources. |
263 // In order to establish the proper priority where delegate_->DoDelayedWork | 249 // In order to establish the proper priority in which work and delayed work |
264 // can only be called if delegate_->DoWork returns false, the timer used | 250 // are processed one for one, the timer used to schedule delayed work must |
265 // to schedule delayed work must signal a CFRunLoopSource set at a lower | 251 // signal a CFRunLoopSource used to dispatch both work and delayed work. |
266 // priority than the one used for delegate_->DoWork. | 252 CFRunLoopSourceSignal(self->work_source_); |
267 CFRunLoopSourceSignal(self->delayed_work_source_); | |
268 } | 253 } |
269 | 254 |
270 // Called from the run loop. | 255 // Called from the run loop. |
271 // static | 256 // static |
272 void MessagePumpCFRunLoopBase::RunWorkSource(void* info) { | 257 void MessagePumpCFRunLoopBase::RunWorkSource(void* info) { |
273 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); | 258 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); |
274 self->RunWork(); | 259 self->RunWork(); |
275 } | 260 } |
276 | 261 |
277 // Called by MessagePumpCFRunLoopBase::RunWorkSource. | 262 // Called by MessagePumpCFRunLoopBase::RunWorkSource. |
278 bool MessagePumpCFRunLoopBase::RunWork() { | 263 bool MessagePumpCFRunLoopBase::RunWork() { |
279 if (!delegate_) { | 264 if (!delegate_) { |
280 // This point can be reached with a NULL delegate_ if Run is not on the | 265 // This point can be reached with a NULL delegate_ if Run is not on the |
281 // stack but foreign code is spinning the CFRunLoop. Arrange to come back | 266 // stack but foreign code is spinning the CFRunLoop. Arrange to come back |
282 // here when a delegate is available. | 267 // here when a delegate is available. |
283 delegateless_work_ = true; | 268 delegateless_work_ = true; |
284 return false; | 269 return false; |
285 } | 270 } |
286 | 271 |
287 // The NSApplication-based run loop only drains the autorelease pool at each | 272 // The NSApplication-based run loop only drains the autorelease pool at each |
288 // UI event (NSEvent). The autorelease pool is not drained for each | 273 // UI event (NSEvent). The autorelease pool is not drained for each |
289 // CFRunLoopSource target that's run. Use a local pool for any autoreleased | 274 // CFRunLoopSource target that's run. Use a local pool for any autoreleased |
290 // objects if the app is not currently handling a UI event to ensure they're | 275 // objects if the app is not currently handling a UI event to ensure they're |
291 // released promptly even in the absence of UI events. | 276 // released promptly even in the absence of UI events. |
292 MessagePumpScopedAutoreleasePool autorelease_pool(this); | 277 MessagePumpScopedAutoreleasePool autorelease_pool(this); |
293 | 278 |
294 // Call DoWork once, and if something was done, arrange to come back here | 279 // Call DoWork and DoDelayedWork once, and if something was done, arrange to |
295 // again as long as the loop is still running. | 280 // come back here again as long as the loop is still running. |
296 bool did_work = delegate_->DoWork(); | 281 bool did_work = delegate_->DoWork(); |
297 if (did_work) { | 282 bool resignal_work_source = did_work; |
| 283 |
| 284 TimeTicks next_time; |
| 285 delegate_->DoDelayedWork(&next_time); |
| 286 if (!did_work) { |
| 287 // Determine whether there's more delayed work, and if so, if it needs to |
| 288 // be done at some point in the future or if it's already time to do it. |
| 289 // Only do these checks if did_work is false. If did_work is true, this |
| 290 // function, and therefore any additional delayed work, will get another |
| 291 // chance to run before the loop goes to sleep. |
| 292 bool more_delayed_work = !next_time.is_null(); |
| 293 if (more_delayed_work) { |
| 294 TimeDelta delay = next_time - TimeTicks::Now(); |
| 295 if (delay > TimeDelta()) { |
| 296 // There's more delayed work to be done in the future. |
| 297 ScheduleDelayedWork(next_time); |
| 298 } else { |
| 299 // There's more delayed work to be done, and its time is in the past. |
| 300 // Arrange to come back here directly as long as the loop is still |
| 301 // running. |
| 302 resignal_work_source = true; |
| 303 } |
| 304 } |
| 305 } |
| 306 |
| 307 if (resignal_work_source) { |
298 CFRunLoopSourceSignal(work_source_); | 308 CFRunLoopSourceSignal(work_source_); |
299 } | 309 } |
300 | 310 |
301 return did_work; | 311 return resignal_work_source; |
302 } | 312 } |
303 | 313 |
304 // Called from the run loop. | 314 // Called from the run loop. |
305 // static | |
306 void MessagePumpCFRunLoopBase::RunDelayedWorkSource(void* info) { | |
307 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); | |
308 self->RunDelayedWork(); | |
309 } | |
310 | |
311 // Called by MessagePumpCFRunLoopBase::RunDelayedWorkSource. | |
312 bool MessagePumpCFRunLoopBase::RunDelayedWork() { | |
313 if (!delegate_) { | |
314 // This point can be reached with a NULL delegate_ if Run is not on the | |
315 // stack but foreign code is spinning the CFRunLoop. Arrange to come back | |
316 // here when a delegate is available. | |
317 delegateless_delayed_work_ = true; | |
318 return false; | |
319 } | |
320 | |
321 // The NSApplication-based run loop only drains the autorelease pool at each | |
322 // UI event (NSEvent). The autorelease pool is not drained for each | |
323 // CFRunLoopSource target that's run. Use a local pool for any autoreleased | |
324 // objects if the app is not currently handling a UI event to ensure they're | |
325 // released promptly even in the absence of UI events. | |
326 MessagePumpScopedAutoreleasePool autorelease_pool(this); | |
327 | |
328 TimeTicks next_time; | |
329 delegate_->DoDelayedWork(&next_time); | |
330 | |
331 bool more_work = !next_time.is_null(); | |
332 if (more_work) { | |
333 TimeDelta delay = next_time - TimeTicks::Now(); | |
334 if (delay > TimeDelta()) { | |
335 // There's more delayed work to be done in the future. | |
336 ScheduleDelayedWork(next_time); | |
337 } else { | |
338 // There's more delayed work to be done, and its time is in the past. | |
339 // Arrange to come back here directly as long as the loop is still | |
340 // running. | |
341 CFRunLoopSourceSignal(delayed_work_source_); | |
342 } | |
343 } | |
344 | |
345 return more_work; | |
346 } | |
347 | |
348 // Called from the run loop. | |
349 // static | 315 // static |
350 void MessagePumpCFRunLoopBase::RunIdleWorkSource(void* info) { | 316 void MessagePumpCFRunLoopBase::RunIdleWorkSource(void* info) { |
351 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); | 317 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); |
352 self->RunIdleWork(); | 318 self->RunIdleWork(); |
353 } | 319 } |
354 | 320 |
355 // Called by MessagePumpCFRunLoopBase::RunIdleWorkSource. | 321 // Called by MessagePumpCFRunLoopBase::RunIdleWorkSource. |
356 bool MessagePumpCFRunLoopBase::RunIdleWork() { | 322 bool MessagePumpCFRunLoopBase::RunIdleWork() { |
357 if (!delegate_) { | 323 if (!delegate_) { |
358 // This point can be reached with a NULL delegate_ if Run is not on the | 324 // This point can be reached with a NULL delegate_ if Run is not on the |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
391 if (!delegate_) { | 357 if (!delegate_) { |
392 // This point can be reached with a NULL delegate_ if Run is not on the | 358 // This point can be reached with a NULL delegate_ if Run is not on the |
393 // stack but foreign code is spinning the CFRunLoop. There's no sense in | 359 // stack but foreign code is spinning the CFRunLoop. There's no sense in |
394 // attempting to do any work or signalling the work sources because | 360 // attempting to do any work or signalling the work sources because |
395 // without a delegate, work is not possible. | 361 // without a delegate, work is not possible. |
396 return false; | 362 return false; |
397 } | 363 } |
398 | 364 |
399 // Immediately try work in priority order. | 365 // Immediately try work in priority order. |
400 if (!RunWork()) { | 366 if (!RunWork()) { |
401 if (!RunDelayedWork()) { | 367 if (!RunIdleWork()) { |
402 if (!RunIdleWork()) { | 368 return false; |
403 return false; | |
404 } | |
405 } else { | |
406 // There was no work, and delayed work was done. Arrange for the loop | |
407 // to try non-nestable idle work on a subsequent pass. | |
408 CFRunLoopSourceSignal(idle_work_source_); | |
409 } | 369 } |
410 } else { | 370 } else { |
411 // Work was done. Arrange for the loop to try non-nestable delayed and | 371 // Work was done. Arrange for the loop to try non-nestable idle work on |
412 // idle work on a subsequent pass. | 372 // a subsequent pass. |
413 CFRunLoopSourceSignal(delayed_work_source_); | |
414 CFRunLoopSourceSignal(idle_work_source_); | 373 CFRunLoopSourceSignal(idle_work_source_); |
415 } | 374 } |
416 | 375 |
417 return true; | 376 return true; |
418 } | 377 } |
419 | 378 |
420 // Called before the run loop goes to sleep or exits, or processes sources. | 379 // Called before the run loop goes to sleep or exits, or processes sources. |
421 void MessagePumpCFRunLoopBase::MaybeScheduleNestingDeferredWork() { | 380 void MessagePumpCFRunLoopBase::MaybeScheduleNestingDeferredWork() { |
422 // deepest_nesting_level_ is set as run loops are entered. If the deepest | 381 // deepest_nesting_level_ is set as run loops are entered. If the deepest |
423 // level encountered is deeper than the current level, a nested loop | 382 // level encountered is deeper than the current level, a nested loop |
(...skipping 334 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
758 // static | 717 // static |
759 MessagePump* MessagePumpMac::Create() { | 718 MessagePump* MessagePumpMac::Create() { |
760 if ([NSThread isMainThread]) { | 719 if ([NSThread isMainThread]) { |
761 return new MessagePumpNSApplication; | 720 return new MessagePumpNSApplication; |
762 } | 721 } |
763 | 722 |
764 return new MessagePumpNSRunLoop; | 723 return new MessagePumpNSRunLoop; |
765 } | 724 } |
766 | 725 |
767 } // namespace base | 726 } // namespace base |
OLD | NEW |