| 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 |