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 <float.h> | 9 #include <float.h> |
10 | 10 |
11 #include "base/scoped_nsautorelease_pool.h" | 11 #include "base/scoped_nsautorelease_pool.h" |
12 #include "base/time.h" | 12 #include "base/time.h" |
13 | 13 |
14 namespace { | 14 namespace { |
15 | 15 |
16 void NoOp(void* info) { | 16 void NoOp(void* info) { |
17 } | 17 } |
18 | 18 |
19 } // namespace | 19 } // namespace |
20 | 20 |
21 namespace base { | 21 namespace base { |
22 | 22 |
23 // Must be called on the run loop thread. | 23 // Must be called on the run loop thread. |
24 MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase() | 24 MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase() |
25 : nesting_level_(0), | 25 : nesting_level_(0), |
| 26 run_nesting_level_(0), |
26 delegate_(NULL), | 27 delegate_(NULL), |
27 delegateless_work_(false), | 28 delegateless_work_(false), |
28 delegateless_delayed_work_(false), | 29 delegateless_delayed_work_(false), |
29 delegateless_idle_work_(false) | 30 delegateless_idle_work_(false) |
30 { | 31 { |
31 run_loop_ = CFRunLoopGetCurrent(); | 32 run_loop_ = CFRunLoopGetCurrent(); |
32 CFRetain(run_loop_); | 33 CFRetain(run_loop_); |
33 | 34 |
34 // Set a repeating timer with a preposterous firing time and interval. The | 35 // Set a repeating timer with a preposterous firing time and interval. The |
35 // timer will effectively never fire as-is. The firing time will be adjusted | 36 // timer will effectively never fire as-is. The firing time will be adjusted |
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
118 CFRelease(work_source_); | 119 CFRelease(work_source_); |
119 | 120 |
120 CFRunLoopRemoveTimer(run_loop_, delayed_work_timer_, kCFRunLoopCommonModes); | 121 CFRunLoopRemoveTimer(run_loop_, delayed_work_timer_, kCFRunLoopCommonModes); |
121 CFRelease(delayed_work_timer_); | 122 CFRelease(delayed_work_timer_); |
122 | 123 |
123 CFRelease(run_loop_); | 124 CFRelease(run_loop_); |
124 } | 125 } |
125 | 126 |
126 // Must be called on the run loop thread. | 127 // Must be called on the run loop thread. |
127 void MessagePumpCFRunLoopBase::Run(Delegate* delegate) { | 128 void MessagePumpCFRunLoopBase::Run(Delegate* delegate) { |
| 129 // nesting_level_ will be incremented in EnterExitRunLoop, so set |
| 130 // run_nesting_level_ accordingly. |
| 131 int last_run_nesting_level = run_nesting_level_; |
| 132 run_nesting_level_ = nesting_level_ + 1; |
| 133 |
128 Delegate* last_delegate = delegate_; | 134 Delegate* last_delegate = delegate_; |
129 delegate_ = delegate; | 135 delegate_ = delegate; |
130 | 136 |
131 // If any work showed up but could not be dispatched for want of a delegate, | 137 // If any work showed up but could not be dispatched for want of a delegate, |
132 // set it up for dispatch again now that a delegate is available. | 138 // set it up for dispatch again now that a delegate is available. |
133 if (delegateless_work_) { | 139 if (delegateless_work_) { |
134 CFRunLoopSourceSignal(work_source_); | 140 CFRunLoopSourceSignal(work_source_); |
135 delegateless_work_ = false; | 141 delegateless_work_ = false; |
136 } | 142 } |
137 if (delegateless_delayed_work_) { | 143 if (delegateless_delayed_work_) { |
138 CFRunLoopSourceSignal(delayed_work_source_); | 144 CFRunLoopSourceSignal(delayed_work_source_); |
139 delegateless_delayed_work_ = false; | 145 delegateless_delayed_work_ = false; |
140 } | 146 } |
141 if (delegateless_idle_work_) { | 147 if (delegateless_idle_work_) { |
142 CFRunLoopSourceSignal(idle_work_source_); | 148 CFRunLoopSourceSignal(idle_work_source_); |
143 delegateless_idle_work_ = false; | 149 delegateless_idle_work_ = false; |
144 } | 150 } |
145 | 151 |
146 DoRun(delegate); | 152 DoRun(delegate); |
147 | 153 |
| 154 // If this was an inner Run invocation, arrange to run nesting-deferred work |
| 155 // when the stack has unwound to an outer invocation. |
| 156 if (nesting_level_) |
| 157 CFRunLoopSourceSignal(nesting_deferred_work_source_); |
| 158 |
| 159 // Restore the previous state of the object. |
148 delegate_ = last_delegate; | 160 delegate_ = last_delegate; |
| 161 run_nesting_level_ = last_run_nesting_level; |
149 } | 162 } |
150 | 163 |
151 // May be called on any thread. | 164 // May be called on any thread. |
152 void MessagePumpCFRunLoopBase::ScheduleWork() { | 165 void MessagePumpCFRunLoopBase::ScheduleWork() { |
153 CFRunLoopSourceSignal(work_source_); | 166 CFRunLoopSourceSignal(work_source_); |
154 CFRunLoopWakeUp(run_loop_); | 167 CFRunLoopWakeUp(run_loop_); |
155 } | 168 } |
156 | 169 |
157 // Must be called on the run loop thread. | 170 // Must be called on the run loop thread. |
158 void MessagePumpCFRunLoopBase::ScheduleDelayedWork( | 171 void MessagePumpCFRunLoopBase::ScheduleDelayedWork( |
(...skipping 191 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
350 void MessagePumpCFRunLoopBase::EnterExitObserver(CFRunLoopObserverRef observer, | 363 void MessagePumpCFRunLoopBase::EnterExitObserver(CFRunLoopObserverRef observer, |
351 CFRunLoopActivity activity, | 364 CFRunLoopActivity activity, |
352 void* info) { | 365 void* info) { |
353 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); | 366 MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); |
354 | 367 |
355 switch (activity) { | 368 switch (activity) { |
356 case kCFRunLoopEntry: | 369 case kCFRunLoopEntry: |
357 ++self->nesting_level_; | 370 ++self->nesting_level_; |
358 break; | 371 break; |
359 case kCFRunLoopExit: | 372 case kCFRunLoopExit: |
| 373 // After decrementing self->nesting_level_, it will be one less than |
| 374 // self->run_nesting_level_ if the loop that is now exiting was directly |
| 375 // started by a DoRun call. |
360 --self->nesting_level_; | 376 --self->nesting_level_; |
361 if (self->nesting_level_) { | 377 |
| 378 if (self->nesting_level_ >= self->run_nesting_level_ && |
| 379 self->nesting_level_) { |
362 // It's possible that some work was not performed because it was | 380 // It's possible that some work was not performed because it was |
363 // inappropriate to do within a nested loop. When leaving any inner | 381 // inappropriate to do within a nested loop. When leaving any inner |
364 // loop, signal the nesting-deferred work source to ensure that such | 382 // loop not directly supervised by a DoRun call, such as nested native |
| 383 // loops, signal the nesting-deferred work source to ensure that such |
365 // work be afforded an opportunity to be processed if appropriate. | 384 // work be afforded an opportunity to be processed if appropriate. |
| 385 // This is not done for loops being run directly by Run/DoRun because |
| 386 // it can be done directly as Run exits. |
366 CFRunLoopSourceSignal(self->nesting_deferred_work_source_); | 387 CFRunLoopSourceSignal(self->nesting_deferred_work_source_); |
367 } | 388 } |
368 break; | 389 break; |
369 default: | 390 default: |
370 break; | 391 break; |
371 } | 392 } |
372 | 393 |
373 self->EnterExitRunLoop(activity); | 394 self->EnterExitRunLoop(activity); |
374 } | 395 } |
375 | 396 |
376 // Called by MessagePumpCFRunLoopBase::EnterExitRunLoop. The default | 397 // Called by MessagePumpCFRunLoopBase::EnterExitRunLoop. The default |
377 // implementation is a no-op. | 398 // implementation is a no-op. |
378 void MessagePumpCFRunLoopBase::EnterExitRunLoop(CFRunLoopActivity activity) { | 399 void MessagePumpCFRunLoopBase::EnterExitRunLoop(CFRunLoopActivity activity) { |
379 } | 400 } |
380 | 401 |
381 MessagePumpCFRunLoop::MessagePumpCFRunLoop() | 402 MessagePumpCFRunLoop::MessagePumpCFRunLoop() |
382 : innermost_quittable_(0), | 403 : quit_pending_(false) { |
383 quit_pending_(false) { | |
384 } | 404 } |
385 | 405 |
386 // Called by MessagePumpCFRunLoopBase::DoRun. If other CFRunLoopRun loops were | 406 // Called by MessagePumpCFRunLoopBase::DoRun. If other CFRunLoopRun loops were |
387 // running lower on the run loop thread's stack when this object was created, | 407 // running lower on the run loop thread's stack when this object was created, |
388 // the same number of CFRunLoopRun loops must be running for the outermost call | 408 // the same number of CFRunLoopRun loops must be running for the outermost call |
389 // to Run. Run/DoRun are reentrant after that point. | 409 // to Run. Run/DoRun are reentrant after that point. |
390 void MessagePumpCFRunLoop::DoRun(Delegate* delegate) { | 410 void MessagePumpCFRunLoop::DoRun(Delegate* delegate) { |
391 // nesting_level_ will be incremented in EnterExitRunLoop, so set | |
392 // innermost_quittable_ accordingly. | |
393 int last_innermost_quittable = innermost_quittable_; | |
394 innermost_quittable_ = nesting_level_ + 1; | |
395 | |
396 // This is completely identical to calling CFRunLoopRun(), except autorelease | 411 // This is completely identical to calling CFRunLoopRun(), except autorelease |
397 // pool management is introduced. | 412 // pool management is introduced. |
398 int result; | 413 int result; |
399 do { | 414 do { |
400 ScopedNSAutoreleasePool autorelease_pool; | 415 ScopedNSAutoreleasePool autorelease_pool; |
401 result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, DBL_MAX, false); | 416 result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, DBL_MAX, false); |
402 } while (result != kCFRunLoopRunStopped && result != kCFRunLoopRunFinished); | 417 } while (result != kCFRunLoopRunStopped && result != kCFRunLoopRunFinished); |
403 | |
404 // Restore the previous state of the object. | |
405 innermost_quittable_ = last_innermost_quittable; | |
406 } | 418 } |
407 | 419 |
408 // Must be called on the run loop thread. | 420 // Must be called on the run loop thread. |
409 void MessagePumpCFRunLoop::Quit() { | 421 void MessagePumpCFRunLoop::Quit() { |
410 // Stop the innermost run loop managed by this MessagePumpCFRunLoop object. | 422 // Stop the innermost run loop managed by this MessagePumpCFRunLoop object. |
411 if (nesting_level_ == innermost_quittable_) { | 423 if (nesting_level_ == run_nesting_level_) { |
412 // This object is running the innermost loop, just stop it. | 424 // This object is running the innermost loop, just stop it. |
413 CFRunLoopStop(run_loop_); | 425 CFRunLoopStop(run_loop_); |
414 } else { | 426 } else { |
415 // There's another loop running inside the loop managed by this object. | 427 // There's another loop running inside the loop managed by this object. |
416 // In other words, someone else called CFRunLoopRun on the same thread, | 428 // In other words, someone else called CFRunLoopRun on the same thread, |
417 // higher on the stack than our highest Run call. Don't preempt other | 429 // higher on the stack than our highest Run call. Don't preempt other |
418 // run loops, just mark the object to quit our innermost run loop as soon | 430 // run loops, just mark the object to quit our innermost run loop as soon |
419 // as the other inner loops we don't manage are done. | 431 // as the other inner loops we don't manage are done. |
420 quit_pending_ = true; | 432 quit_pending_ = true; |
421 } | 433 } |
422 } | 434 } |
423 | 435 |
424 // Called by MessagePumpCFRunLoopBase::EnterExitObserver. | 436 // Called by MessagePumpCFRunLoopBase::EnterExitObserver. |
425 void MessagePumpCFRunLoop::EnterExitRunLoop(CFRunLoopActivity activity) { | 437 void MessagePumpCFRunLoop::EnterExitRunLoop(CFRunLoopActivity activity) { |
426 if (activity == kCFRunLoopExit && | 438 if (activity == kCFRunLoopExit && |
427 nesting_level_ == innermost_quittable_ && | 439 nesting_level_ == run_nesting_level_ && |
428 quit_pending_) { | 440 quit_pending_) { |
429 // Quit was called while loops other than those managed by this object | 441 // Quit was called while loops other than those managed by this object |
430 // were running further inside a run loop managed by this object. Now | 442 // were running further inside a run loop managed by this object. Now |
431 // that all unmanaged inner run loops are gone, stop the loop running | 443 // that all unmanaged inner run loops are gone, stop the loop running |
432 // just inside Run. | 444 // just inside Run. |
433 CFRunLoopStop(run_loop_); | 445 CFRunLoopStop(run_loop_); |
434 quit_pending_ = false; | 446 quit_pending_ = false; |
435 } | 447 } |
436 } | 448 } |
437 | 449 |
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
521 // static | 533 // static |
522 MessagePump* MessagePumpMac::Create() { | 534 MessagePump* MessagePumpMac::Create() { |
523 if ([NSThread isMainThread]) { | 535 if ([NSThread isMainThread]) { |
524 return new MessagePumpNSApplication; | 536 return new MessagePumpNSApplication; |
525 } | 537 } |
526 | 538 |
527 return new MessagePumpNSRunLoop; | 539 return new MessagePumpNSRunLoop; |
528 } | 540 } |
529 | 541 |
530 } // namespace base | 542 } // namespace base |
OLD | NEW |