message_pump_mac.mm 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009
  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
  3. // found in the LICENSE file.
  4. #include "base/memory/raw_ptr.h"
  5. #import "base/message_loop/message_pump_mac.h"
  6. #import <Foundation/Foundation.h>
  7. #include <atomic>
  8. #include <limits>
  9. #include <memory>
  10. #include "base/auto_reset.h"
  11. #include "base/check_op.h"
  12. #include "base/feature_list.h"
  13. #include "base/mac/call_with_eh_frame.h"
  14. #include "base/mac/scoped_cftyperef.h"
  15. #include "base/message_loop/timer_slack.h"
  16. #include "base/metrics/histogram_samples.h"
  17. #include "base/notreached.h"
  18. #include "base/run_loop.h"
  19. #include "base/threading/platform_thread.h"
  20. #include "base/time/time.h"
  21. #include "build/build_config.h"
  22. #include "third_party/abseil-cpp/absl/types/optional.h"
  23. #if !BUILDFLAG(IS_IOS)
  24. #import <AppKit/AppKit.h>
  25. #endif // !BUILDFLAG(IS_IOS)
  26. namespace base {
  27. const CFStringRef kMessageLoopExclusiveRunLoopMode =
  28. CFSTR("kMessageLoopExclusiveRunLoopMode");
  29. namespace {
  30. // Enables two optimizations in MessagePumpCFRunLoop:
  31. // - Skip calling CFRunLoopTimerSetNextFireDate if the next delayed wake up
  32. // time hasn't changed.
  33. // - Cancel an already scheduled timer wake up if there is no delayed work.
  34. const base::Feature kMessagePumpMacDelayedWorkOptimizations{
  35. "MessagePumpMacDelayedWorkOptimizations",
  36. base::FEATURE_DISABLED_BY_DEFAULT};
  37. // Caches the state of the "MessagePumpMacDelayedWorkOptimizations"
  38. // feature for efficiency.
  39. std::atomic_bool g_enable_optimizations = false;
  40. // Mask that determines which modes to use.
  41. enum { kCommonModeMask = 0x1, kAllModesMask = 0xf };
  42. // Modes to use for MessagePumpNSApplication that are considered "safe".
  43. // Currently just common and exclusive modes. Ideally, messages would be pumped
  44. // in all modes, but that interacts badly with app modal dialogs (e.g. NSAlert).
  45. enum { kNSApplicationModalSafeModeMask = 0x3 };
  46. void NoOp(void* info) {
  47. }
  48. constexpr CFTimeInterval kCFTimeIntervalMax =
  49. std::numeric_limits<CFTimeInterval>::max();
  50. #if !BUILDFLAG(IS_IOS)
  51. // Set to true if MessagePumpMac::Create() is called before NSApp is
  52. // initialized. Only accessed from the main thread.
  53. bool g_not_using_cr_app = false;
  54. // The MessagePump controlling [NSApp run].
  55. MessagePumpNSApplication* g_app_pump;
  56. #endif // !BUILDFLAG(IS_IOS)
  57. } // namespace
  58. // A scoper for autorelease pools created from message pump run loops.
  59. // Avoids dirtying up the ScopedNSAutoreleasePool interface for the rare
  60. // case where an autorelease pool needs to be passed in.
  61. class MessagePumpScopedAutoreleasePool {
  62. public:
  63. explicit MessagePumpScopedAutoreleasePool(MessagePumpCFRunLoopBase* pump) :
  64. pool_(pump->CreateAutoreleasePool()) {
  65. }
  66. MessagePumpScopedAutoreleasePool(const MessagePumpScopedAutoreleasePool&) =
  67. delete;
  68. MessagePumpScopedAutoreleasePool& operator=(
  69. const MessagePumpScopedAutoreleasePool&) = delete;
  70. ~MessagePumpScopedAutoreleasePool() { [pool_ drain]; }
  71. private:
  72. NSAutoreleasePool* pool_;
  73. };
  74. class MessagePumpCFRunLoopBase::ScopedModeEnabler {
  75. public:
  76. ScopedModeEnabler(MessagePumpCFRunLoopBase* owner, int mode_index)
  77. : owner_(owner), mode_index_(mode_index) {
  78. CFRunLoopRef loop = owner_->run_loop_;
  79. CFRunLoopAddTimer(loop, owner_->delayed_work_timer_, mode());
  80. CFRunLoopAddSource(loop, owner_->work_source_, mode());
  81. CFRunLoopAddSource(loop, owner_->idle_work_source_, mode());
  82. CFRunLoopAddSource(loop, owner_->nesting_deferred_work_source_, mode());
  83. CFRunLoopAddObserver(loop, owner_->pre_wait_observer_, mode());
  84. CFRunLoopAddObserver(loop, owner_->after_wait_observer_, mode());
  85. CFRunLoopAddObserver(loop, owner_->pre_source_observer_, mode());
  86. CFRunLoopAddObserver(loop, owner_->enter_exit_observer_, mode());
  87. }
  88. ScopedModeEnabler(const ScopedModeEnabler&) = delete;
  89. ScopedModeEnabler& operator=(const ScopedModeEnabler&) = delete;
  90. ~ScopedModeEnabler() {
  91. CFRunLoopRef loop = owner_->run_loop_;
  92. CFRunLoopRemoveObserver(loop, owner_->enter_exit_observer_, mode());
  93. CFRunLoopRemoveObserver(loop, owner_->pre_source_observer_, mode());
  94. CFRunLoopRemoveObserver(loop, owner_->pre_wait_observer_, mode());
  95. CFRunLoopRemoveObserver(loop, owner_->after_wait_observer_, mode());
  96. CFRunLoopRemoveSource(loop, owner_->nesting_deferred_work_source_, mode());
  97. CFRunLoopRemoveSource(loop, owner_->idle_work_source_, mode());
  98. CFRunLoopRemoveSource(loop, owner_->work_source_, mode());
  99. CFRunLoopRemoveTimer(loop, owner_->delayed_work_timer_, mode());
  100. }
  101. // This function knows about the AppKit RunLoop modes observed to potentially
  102. // run tasks posted to Chrome's main thread task runner. Some are internal to
  103. // AppKit but must be observed to keep Chrome's UI responsive. Others that may
  104. // be interesting, but are not watched:
  105. // - com.apple.hitoolbox.windows.transitionmode
  106. // - com.apple.hitoolbox.windows.flushmode
  107. const CFStringRef& mode() const {
  108. static const CFStringRef modes[] = {
  109. // The standard Core Foundation "common modes" constant. Must always be
  110. // first in this list to match the value of kCommonModeMask.
  111. kCFRunLoopCommonModes,
  112. // Mode that only sees Chrome work sources.
  113. kMessageLoopExclusiveRunLoopMode,
  114. // Process work when NSMenus are fading out.
  115. CFSTR("com.apple.hitoolbox.windows.windowfadingmode"),
  116. // Process work when AppKit is highlighting an item on the main menubar.
  117. CFSTR("NSUnhighlightMenuRunLoopMode"),
  118. };
  119. static_assert(std::size(modes) == kNumModes, "mode size mismatch");
  120. static_assert((1 << kNumModes) - 1 == kAllModesMask,
  121. "kAllModesMask not large enough");
  122. return modes[mode_index_];
  123. }
  124. private:
  125. const raw_ptr<MessagePumpCFRunLoopBase> owner_; // Weak. Owns this.
  126. const int mode_index_;
  127. };
  128. // Must be called on the run loop thread.
  129. void MessagePumpCFRunLoopBase::Run(Delegate* delegate) {
  130. AutoReset<bool> auto_reset_keep_running(&keep_running_, true);
  131. // nesting_level_ will be incremented in EnterExitRunLoop, so set
  132. // run_nesting_level_ accordingly.
  133. int last_run_nesting_level = run_nesting_level_;
  134. run_nesting_level_ = nesting_level_ + 1;
  135. Delegate* last_delegate = delegate_;
  136. SetDelegate(delegate);
  137. ScheduleWork();
  138. DoRun(delegate);
  139. // Restore the previous state of the object.
  140. SetDelegate(last_delegate);
  141. run_nesting_level_ = last_run_nesting_level;
  142. }
  143. void MessagePumpCFRunLoopBase::Quit() {
  144. if (DoQuit())
  145. OnDidQuit();
  146. }
  147. void MessagePumpCFRunLoopBase::OnDidQuit() {
  148. keep_running_ = false;
  149. }
  150. // May be called on any thread.
  151. void MessagePumpCFRunLoopBase::ScheduleWork() {
  152. CFRunLoopSourceSignal(work_source_);
  153. CFRunLoopWakeUp(run_loop_);
  154. }
  155. // Must be called on the run loop thread.
  156. void MessagePumpCFRunLoopBase::ScheduleDelayedWork(
  157. const Delegate::NextWorkInfo& next_work_info) {
  158. DCHECK(!next_work_info.is_immediate());
  159. if (g_enable_optimizations.load(std::memory_order_relaxed)) {
  160. // No-op if the delayed run time hasn't changed.
  161. if (next_work_info.delayed_run_time == delayed_work_scheduled_at_)
  162. return;
  163. } else {
  164. // Preserve the old behavior of not adjusting the timer when
  165. // `delayed_run_time.is_max()`.
  166. //
  167. // TODO(crbug.com/1335524): Remove this once the
  168. // "MessagePumpMacDelayedWorkOptimizations" feature is shipped.
  169. if (next_work_info.delayed_run_time.is_max())
  170. return;
  171. }
  172. if (next_work_info.delayed_run_time.is_max()) {
  173. CFRunLoopTimerSetNextFireDate(delayed_work_timer_, kCFTimeIntervalMax);
  174. } else {
  175. const double delay_seconds = next_work_info.remaining_delay().InSecondsF();
  176. // The tolerance needs to be set before the fire date or it may be ignored.
  177. if (timer_slack_ == TIMER_SLACK_MAXIMUM) {
  178. CFRunLoopTimerSetTolerance(delayed_work_timer_, delay_seconds * 0.5);
  179. } else {
  180. CFRunLoopTimerSetTolerance(delayed_work_timer_, 0);
  181. }
  182. CFRunLoopTimerSetNextFireDate(delayed_work_timer_,
  183. CFAbsoluteTimeGetCurrent() + delay_seconds);
  184. }
  185. delayed_work_scheduled_at_ = next_work_info.delayed_run_time;
  186. }
  187. void MessagePumpCFRunLoopBase::SetTimerSlack(TimerSlack timer_slack) {
  188. timer_slack_ = timer_slack;
  189. }
  190. #if BUILDFLAG(IS_IOS)
  191. void MessagePumpCFRunLoopBase::Attach(Delegate* delegate) {}
  192. void MessagePumpCFRunLoopBase::Detach() {}
  193. #endif // BUILDFLAG(IS_IOS)
  194. // Must be called on the run loop thread.
  195. MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase(int initial_mode_mask)
  196. : delegate_(nullptr),
  197. timer_slack_(base::TIMER_SLACK_NONE),
  198. nesting_level_(0),
  199. run_nesting_level_(0),
  200. deepest_nesting_level_(0),
  201. keep_running_(true),
  202. delegateless_work_(false),
  203. delegateless_idle_work_(false) {
  204. run_loop_ = CFRunLoopGetCurrent();
  205. CFRetain(run_loop_);
  206. // Set a repeating timer with a preposterous firing time and interval. The
  207. // timer will effectively never fire as-is. The firing time will be adjusted
  208. // as needed when ScheduleDelayedWork is called.
  209. CFRunLoopTimerContext timer_context = CFRunLoopTimerContext();
  210. timer_context.info = this;
  211. delayed_work_timer_ = CFRunLoopTimerCreate(NULL, // allocator
  212. kCFTimeIntervalMax, // fire time
  213. kCFTimeIntervalMax, // interval
  214. 0, // flags
  215. 0, // priority
  216. RunDelayedWorkTimer,
  217. &timer_context);
  218. CFRunLoopSourceContext source_context = CFRunLoopSourceContext();
  219. source_context.info = this;
  220. source_context.perform = RunWorkSource;
  221. work_source_ = CFRunLoopSourceCreate(NULL, // allocator
  222. 1, // priority
  223. &source_context);
  224. source_context.perform = RunIdleWorkSource;
  225. idle_work_source_ = CFRunLoopSourceCreate(NULL, // allocator
  226. 2, // priority
  227. &source_context);
  228. source_context.perform = RunNestingDeferredWorkSource;
  229. nesting_deferred_work_source_ = CFRunLoopSourceCreate(NULL, // allocator
  230. 0, // priority
  231. &source_context);
  232. CFRunLoopObserverContext observer_context = CFRunLoopObserverContext();
  233. observer_context.info = this;
  234. pre_wait_observer_ = CFRunLoopObserverCreate(NULL, // allocator
  235. kCFRunLoopBeforeWaiting,
  236. true, // repeat
  237. 0, // priority
  238. PreWaitObserver,
  239. &observer_context);
  240. after_wait_observer_ =
  241. CFRunLoopObserverCreate(NULL, // allocator
  242. kCFRunLoopAfterWaiting,
  243. true, // repeat
  244. 0, // priority
  245. AfterWaitObserver, &observer_context);
  246. pre_source_observer_ = CFRunLoopObserverCreate(NULL, // allocator
  247. kCFRunLoopBeforeSources,
  248. true, // repeat
  249. 0, // priority
  250. PreSourceObserver,
  251. &observer_context);
  252. enter_exit_observer_ = CFRunLoopObserverCreate(NULL, // allocator
  253. kCFRunLoopEntry |
  254. kCFRunLoopExit,
  255. true, // repeat
  256. 0, // priority
  257. EnterExitObserver,
  258. &observer_context);
  259. SetModeMask(initial_mode_mask);
  260. }
  261. // Ideally called on the run loop thread. If other run loops were running
  262. // lower on the run loop thread's stack when this object was created, the
  263. // same number of run loops must be running when this object is destroyed.
  264. MessagePumpCFRunLoopBase::~MessagePumpCFRunLoopBase() {
  265. SetModeMask(0);
  266. CFRelease(enter_exit_observer_);
  267. CFRelease(pre_source_observer_);
  268. CFRelease(pre_wait_observer_);
  269. CFRelease(after_wait_observer_);
  270. CFRelease(nesting_deferred_work_source_);
  271. CFRelease(idle_work_source_);
  272. CFRelease(work_source_);
  273. CFRelease(delayed_work_timer_);
  274. CFRelease(run_loop_);
  275. }
  276. // static
  277. void MessagePumpCFRunLoopBase::InitializeFeatures() {
  278. g_enable_optimizations.store(
  279. base::FeatureList::IsEnabled(kMessagePumpMacDelayedWorkOptimizations),
  280. std::memory_order_relaxed);
  281. }
  282. #if BUILDFLAG(IS_IOS)
  283. void MessagePumpCFRunLoopBase::OnAttach() {
  284. CHECK_EQ(nesting_level_, 0);
  285. // On iOS: the MessagePump is attached while it's already running.
  286. nesting_level_ = 1;
  287. // There could be some native work done after attaching to the loop and before
  288. // |work_source_| is invoked.
  289. PushWorkItemScope();
  290. }
  291. void MessagePumpCFRunLoopBase::OnDetach() {
  292. // This function is called on shutdown. This can happen at either
  293. // `nesting_level` >=1 or 0:
  294. // `nesting_level_ == 0`: When this is detached as part of tear down outside
  295. // of a run loop (e.g. ~TaskEnvironment). `nesting_level_ >= 1`: When this
  296. // is detached as part of a native shutdown notification ran from the
  297. // message pump itself. Nesting levels higher than 1 can happen in
  298. // legitimate nesting situations like the browser being dismissed while
  299. // displaying a long press context menu (CRWContextMenuController).
  300. CHECK_GE(nesting_level_, 0);
  301. }
  302. #endif // BUILDFLAG(IS_IOS)
  303. void MessagePumpCFRunLoopBase::SetDelegate(Delegate* delegate) {
  304. delegate_ = delegate;
  305. if (delegate) {
  306. // If any work showed up but could not be dispatched for want of a
  307. // delegate, set it up for dispatch again now that a delegate is
  308. // available.
  309. if (delegateless_work_) {
  310. CFRunLoopSourceSignal(work_source_);
  311. delegateless_work_ = false;
  312. }
  313. if (delegateless_idle_work_) {
  314. CFRunLoopSourceSignal(idle_work_source_);
  315. delegateless_idle_work_ = false;
  316. }
  317. }
  318. }
  319. // Base version returns a standard NSAutoreleasePool.
  320. AutoreleasePoolType* MessagePumpCFRunLoopBase::CreateAutoreleasePool() {
  321. return [[NSAutoreleasePool alloc] init];
  322. }
  323. void MessagePumpCFRunLoopBase::SetModeMask(int mode_mask) {
  324. for (size_t i = 0; i < kNumModes; ++i) {
  325. bool enable = mode_mask & (0x1 << i);
  326. if (enable == !enabled_modes_[i]) {
  327. enabled_modes_[i] =
  328. enable ? std::make_unique<ScopedModeEnabler>(this, i) : nullptr;
  329. }
  330. }
  331. }
  332. int MessagePumpCFRunLoopBase::GetModeMask() const {
  333. int mask = 0;
  334. for (size_t i = 0; i < kNumModes; ++i)
  335. mask |= enabled_modes_[i] ? (0x1 << i) : 0;
  336. return mask;
  337. }
  338. void MessagePumpCFRunLoopBase::PopWorkItemScope() {
  339. // A WorkItemScope should never have been pushed unless the loop was entered.
  340. DCHECK_NE(nesting_level_, 0);
  341. // If no WorkItemScope was pushed it cannot be popped.
  342. DCHECK_GT(stack_.size(), 0u);
  343. stack_.pop();
  344. }
  345. void MessagePumpCFRunLoopBase::PushWorkItemScope() {
  346. // A WorkItemScope should never be pushed unless the loop was entered.
  347. DCHECK_NE(nesting_level_, 0);
  348. // See RunWork() comments on why the size of |stack| is never bigger than
  349. // |nesting_level_| even in nested loops.
  350. DCHECK_LT(stack_.size(), static_cast<size_t>(nesting_level_));
  351. if (delegate_) {
  352. stack_.push(delegate_->BeginWorkItem());
  353. } else {
  354. stack_.push(absl::nullopt);
  355. }
  356. }
  357. // Called from the run loop.
  358. // static
  359. void MessagePumpCFRunLoopBase::RunDelayedWorkTimer(CFRunLoopTimerRef timer,
  360. void* info) {
  361. MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
  362. // The timer fired, assume we have work and let RunWork() figure out what to
  363. // do and what to schedule after.
  364. base::mac::CallWithEHFrame(^{
  365. // It would be incorrect to expect that `self->delayed_work_scheduled_at_`
  366. // is smaller than or equal to `TimeTicks::Now()` because the fire date of a
  367. // CFRunLoopTimer can be adjusted slightly.
  368. // https://developer.apple.com/documentation/corefoundation/1543570-cfrunlooptimercreate?language=objc
  369. DCHECK(!self->delayed_work_scheduled_at_.is_max());
  370. self->delayed_work_scheduled_at_ = base::TimeTicks::Max();
  371. self->RunWork();
  372. });
  373. }
  374. // Called from the run loop.
  375. // static
  376. void MessagePumpCFRunLoopBase::RunWorkSource(void* info) {
  377. MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
  378. base::mac::CallWithEHFrame(^{
  379. self->RunWork();
  380. });
  381. }
  382. // Called by MessagePumpCFRunLoopBase::RunWorkSource and RunDelayedWorkTimer.
  383. bool MessagePumpCFRunLoopBase::RunWork() {
  384. if (!delegate_) {
  385. // This point can be reached with a nullptr |delegate_| if Run is not on the
  386. // stack but foreign code is spinning the CFRunLoop. Arrange to come back
  387. // here when a delegate is available.
  388. delegateless_work_ = true;
  389. return false;
  390. }
  391. if (!keep_running())
  392. return false;
  393. // The NSApplication-based run loop only drains the autorelease pool at each
  394. // UI event (NSEvent). The autorelease pool is not drained for each
  395. // CFRunLoopSource target that's run. Use a local pool for any autoreleased
  396. // objects if the app is not currently handling a UI event to ensure they're
  397. // released promptly even in the absence of UI events.
  398. MessagePumpScopedAutoreleasePool autorelease_pool(this);
  399. // Pop the current work item scope as it captures any native work happening
  400. // *between* DoWork()'s. This DoWork() happens in sequence to that native
  401. // work, not nested within it.
  402. PopWorkItemScope();
  403. Delegate::NextWorkInfo next_work_info = delegate_->DoWork();
  404. // DoWork() (and its own work item coverage) is over so push a new scope to
  405. // cover any native work that could possibly happen before the next RunWork().
  406. PushWorkItemScope();
  407. if (next_work_info.is_immediate()) {
  408. CFRunLoopSourceSignal(work_source_);
  409. return true;
  410. } else {
  411. // This adjusts the next delayed wake up time (potentially cancels an
  412. // already scheduled wake up if there is no delayed work).
  413. ScheduleDelayedWork(next_work_info);
  414. return false;
  415. }
  416. }
  417. // Called from the run loop.
  418. // static
  419. void MessagePumpCFRunLoopBase::RunIdleWorkSource(void* info) {
  420. MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
  421. base::mac::CallWithEHFrame(^{
  422. self->RunIdleWork();
  423. });
  424. }
  425. // Called by MessagePumpCFRunLoopBase::RunIdleWorkSource.
  426. void MessagePumpCFRunLoopBase::RunIdleWork() {
  427. if (!delegate_) {
  428. // This point can be reached with a nullptr delegate_ if Run is not on the
  429. // stack but foreign code is spinning the CFRunLoop. Arrange to come back
  430. // here when a delegate is available.
  431. delegateless_idle_work_ = true;
  432. return;
  433. }
  434. if (!keep_running())
  435. return;
  436. // The NSApplication-based run loop only drains the autorelease pool at each
  437. // UI event (NSEvent). The autorelease pool is not drained for each
  438. // CFRunLoopSource target that's run. Use a local pool for any autoreleased
  439. // objects if the app is not currently handling a UI event to ensure they're
  440. // released promptly even in the absence of UI events.
  441. MessagePumpScopedAutoreleasePool autorelease_pool(this);
  442. // Pop the current work item scope as it captures any native work happening
  443. // *between* the last DoWork() and this DoIdleWork()
  444. PopWorkItemScope();
  445. bool did_work = delegate_->DoIdleWork();
  446. // As in DoWork(), push a new scope to cover any native work that could
  447. // possibly happen between now and BeforeWait().
  448. PushWorkItemScope();
  449. if (did_work)
  450. CFRunLoopSourceSignal(idle_work_source_);
  451. }
  452. // Called from the run loop.
  453. // static
  454. void MessagePumpCFRunLoopBase::RunNestingDeferredWorkSource(void* info) {
  455. MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
  456. base::mac::CallWithEHFrame(^{
  457. self->RunNestingDeferredWork();
  458. });
  459. }
  460. // Called by MessagePumpCFRunLoopBase::RunNestingDeferredWorkSource.
  461. void MessagePumpCFRunLoopBase::RunNestingDeferredWork() {
  462. if (!delegate_) {
  463. // This point can be reached with a nullptr |delegate_| if Run is not on the
  464. // stack but foreign code is spinning the CFRunLoop. There's no sense in
  465. // attempting to do any work or signalling the work sources because
  466. // without a delegate, work is not possible.
  467. return;
  468. }
  469. if (RunWork()) {
  470. // Work was done. Arrange for the loop to try non-nestable idle work on
  471. // a subsequent pass.
  472. CFRunLoopSourceSignal(idle_work_source_);
  473. } else {
  474. RunIdleWork();
  475. }
  476. }
  477. void MessagePumpCFRunLoopBase::BeforeWait() {
  478. // Current work item tracking needs to go away since execution will stop.
  479. // Matches the PushWorkItemScope() in AfterWaitObserver() (with an arbitrary
  480. // amount of matching Pop/Push in between when running work items).
  481. PopWorkItemScope();
  482. if (!delegate_) {
  483. // This point can be reached with a nullptr |delegate_| if Run is not on the
  484. // stack but foreign code is spinning the CFRunLoop.
  485. return;
  486. }
  487. delegate_->BeforeWait();
  488. }
  489. // Called before the run loop goes to sleep or exits, or processes sources.
  490. void MessagePumpCFRunLoopBase::MaybeScheduleNestingDeferredWork() {
  491. // deepest_nesting_level_ is set as run loops are entered. If the deepest
  492. // level encountered is deeper than the current level, a nested loop
  493. // (relative to the current level) ran since the last time nesting-deferred
  494. // work was scheduled. When that situation is encountered, schedule
  495. // nesting-deferred work in case any work was deferred because nested work
  496. // was disallowed.
  497. if (deepest_nesting_level_ > nesting_level_) {
  498. deepest_nesting_level_ = nesting_level_;
  499. CFRunLoopSourceSignal(nesting_deferred_work_source_);
  500. }
  501. }
  502. // Called from the run loop.
  503. // static
  504. void MessagePumpCFRunLoopBase::PreWaitObserver(CFRunLoopObserverRef observer,
  505. CFRunLoopActivity activity,
  506. void* info) {
  507. MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
  508. base::mac::CallWithEHFrame(^{
  509. // Attempt to do some idle work before going to sleep.
  510. self->RunIdleWork();
  511. // The run loop is about to go to sleep. If any of the work done since it
  512. // started or woke up resulted in a nested run loop running,
  513. // nesting-deferred work may have accumulated. Schedule it for processing
  514. // if appropriate.
  515. self->MaybeScheduleNestingDeferredWork();
  516. // Notify the delegate that the loop is about to sleep.
  517. self->BeforeWait();
  518. });
  519. }
  520. // Called from the run loop.
  521. // static
  522. void MessagePumpCFRunLoopBase::AfterWaitObserver(CFRunLoopObserverRef observer,
  523. CFRunLoopActivity activity,
  524. void* info) {
  525. MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
  526. base::mac::CallWithEHFrame(^{
  527. // Emerging from sleep, any work happening after this (outside of a
  528. // RunWork()) should be considered native work. Matching PopWorkItemScope()
  529. // is in BeforeWait().
  530. self->PushWorkItemScope();
  531. });
  532. }
  533. // Called from the run loop.
  534. // static
  535. void MessagePumpCFRunLoopBase::PreSourceObserver(CFRunLoopObserverRef observer,
  536. CFRunLoopActivity activity,
  537. void* info) {
  538. MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
  539. // The run loop has reached the top of the loop and is about to begin
  540. // processing sources. If the last iteration of the loop at this nesting
  541. // level did not sleep or exit, nesting-deferred work may have accumulated
  542. // if a nested loop ran. Schedule nesting-deferred work for processing if
  543. // appropriate.
  544. base::mac::CallWithEHFrame(^{
  545. self->MaybeScheduleNestingDeferredWork();
  546. });
  547. }
  548. // Called from the run loop.
  549. // static
  550. void MessagePumpCFRunLoopBase::EnterExitObserver(CFRunLoopObserverRef observer,
  551. CFRunLoopActivity activity,
  552. void* info) {
  553. MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
  554. switch (activity) {
  555. case kCFRunLoopEntry:
  556. ++self->nesting_level_;
  557. // There could be some native work done after entering the loop and before
  558. // the next observer.
  559. self->PushWorkItemScope();
  560. if (self->nesting_level_ > self->deepest_nesting_level_) {
  561. self->deepest_nesting_level_ = self->nesting_level_;
  562. }
  563. break;
  564. case kCFRunLoopExit:
  565. // Not all run loops go to sleep. If a run loop is stopped before it
  566. // goes to sleep due to a CFRunLoopStop call, or if the timeout passed
  567. // to CFRunLoopRunInMode expires, the run loop may proceed directly from
  568. // handling sources to exiting without any sleep. This most commonly
  569. // occurs when CFRunLoopRunInMode is passed a timeout of 0, causing it
  570. // to make a single pass through the loop and exit without sleep. Some
  571. // native loops use CFRunLoop in this way. Because PreWaitObserver will
  572. // not be called in these case, MaybeScheduleNestingDeferredWork needs
  573. // to be called here, as the run loop exits.
  574. //
  575. // MaybeScheduleNestingDeferredWork consults self->nesting_level_
  576. // to determine whether to schedule nesting-deferred work. It expects
  577. // the nesting level to be set to the depth of the loop that is going
  578. // to sleep or exiting. It must be called before decrementing the
  579. // value so that the value still corresponds to the level of the exiting
  580. // loop.
  581. base::mac::CallWithEHFrame(^{
  582. self->MaybeScheduleNestingDeferredWork();
  583. });
  584. // Current work item tracking needs to go away since execution will stop.
  585. self->PopWorkItemScope();
  586. --self->nesting_level_;
  587. break;
  588. default:
  589. break;
  590. }
  591. base::mac::CallWithEHFrame(^{
  592. self->EnterExitRunLoop(activity);
  593. });
  594. }
  595. // Called by MessagePumpCFRunLoopBase::EnterExitRunLoop. The default
  596. // implementation is a no-op.
  597. void MessagePumpCFRunLoopBase::EnterExitRunLoop(CFRunLoopActivity activity) {
  598. }
  599. MessagePumpCFRunLoop::MessagePumpCFRunLoop()
  600. : MessagePumpCFRunLoopBase(kCommonModeMask), quit_pending_(false) {}
  601. MessagePumpCFRunLoop::~MessagePumpCFRunLoop() {}
  602. // Called by MessagePumpCFRunLoopBase::DoRun. If other CFRunLoopRun loops were
  603. // running lower on the run loop thread's stack when this object was created,
  604. // the same number of CFRunLoopRun loops must be running for the outermost call
  605. // to Run. Run/DoRun are reentrant after that point.
  606. void MessagePumpCFRunLoop::DoRun(Delegate* delegate) {
  607. // This is completely identical to calling CFRunLoopRun(), except autorelease
  608. // pool management is introduced.
  609. int result;
  610. do {
  611. MessagePumpScopedAutoreleasePool autorelease_pool(this);
  612. result = CFRunLoopRunInMode(kCFRunLoopDefaultMode,
  613. kCFTimeIntervalMax,
  614. false);
  615. } while (result != kCFRunLoopRunStopped && result != kCFRunLoopRunFinished);
  616. }
  617. // Must be called on the run loop thread.
  618. bool MessagePumpCFRunLoop::DoQuit() {
  619. // Stop the innermost run loop managed by this MessagePumpCFRunLoop object.
  620. if (nesting_level() == run_nesting_level()) {
  621. // This object is running the innermost loop, just stop it.
  622. CFRunLoopStop(run_loop());
  623. return true;
  624. } else {
  625. // There's another loop running inside the loop managed by this object.
  626. // In other words, someone else called CFRunLoopRunInMode on the same
  627. // thread, deeper on the stack than the deepest Run call. Don't preempt
  628. // other run loops, just mark this object to quit the innermost Run as
  629. // soon as the other inner loops not managed by Run are done.
  630. quit_pending_ = true;
  631. return false;
  632. }
  633. }
  634. // Called by MessagePumpCFRunLoopBase::EnterExitObserver.
  635. void MessagePumpCFRunLoop::EnterExitRunLoop(CFRunLoopActivity activity) {
  636. if (activity == kCFRunLoopExit &&
  637. nesting_level() == run_nesting_level() &&
  638. quit_pending_) {
  639. // Quit was called while loops other than those managed by this object
  640. // were running further inside a run loop managed by this object. Now
  641. // that all unmanaged inner run loops are gone, stop the loop running
  642. // just inside Run.
  643. CFRunLoopStop(run_loop());
  644. quit_pending_ = false;
  645. OnDidQuit();
  646. }
  647. }
  648. MessagePumpNSRunLoop::MessagePumpNSRunLoop()
  649. : MessagePumpCFRunLoopBase(kCommonModeMask) {
  650. CFRunLoopSourceContext source_context = CFRunLoopSourceContext();
  651. source_context.perform = NoOp;
  652. quit_source_ = CFRunLoopSourceCreate(NULL, // allocator
  653. 0, // priority
  654. &source_context);
  655. CFRunLoopAddSource(run_loop(), quit_source_, kCFRunLoopCommonModes);
  656. }
  657. MessagePumpNSRunLoop::~MessagePumpNSRunLoop() {
  658. CFRunLoopRemoveSource(run_loop(), quit_source_, kCFRunLoopCommonModes);
  659. CFRelease(quit_source_);
  660. }
  661. void MessagePumpNSRunLoop::DoRun(Delegate* delegate) {
  662. while (keep_running()) {
  663. // NSRunLoop manages autorelease pools itself.
  664. [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
  665. beforeDate:[NSDate distantFuture]];
  666. }
  667. }
  668. bool MessagePumpNSRunLoop::DoQuit() {
  669. CFRunLoopSourceSignal(quit_source_);
  670. CFRunLoopWakeUp(run_loop());
  671. return true;
  672. }
  673. #if BUILDFLAG(IS_IOS)
  674. MessagePumpUIApplication::MessagePumpUIApplication()
  675. : MessagePumpCFRunLoopBase(kCommonModeMask), run_loop_(NULL) {}
  676. MessagePumpUIApplication::~MessagePumpUIApplication() {}
  677. void MessagePumpUIApplication::DoRun(Delegate* delegate) {
  678. NOTREACHED();
  679. }
  680. bool MessagePumpUIApplication::DoQuit() {
  681. NOTREACHED();
  682. return false;
  683. }
  684. void MessagePumpUIApplication::Attach(Delegate* delegate) {
  685. DCHECK(!run_loop_);
  686. run_loop_ = new RunLoop();
  687. CHECK(run_loop_->BeforeRun());
  688. SetDelegate(delegate);
  689. OnAttach();
  690. }
  691. void MessagePumpUIApplication::Detach() {
  692. DCHECK(run_loop_);
  693. run_loop_->AfterRun();
  694. SetDelegate(nullptr);
  695. run_loop_ = nullptr;
  696. OnDetach();
  697. }
  698. #else
  699. ScopedPumpMessagesInPrivateModes::ScopedPumpMessagesInPrivateModes() {
  700. DCHECK(g_app_pump);
  701. DCHECK_EQ(kNSApplicationModalSafeModeMask, g_app_pump->GetModeMask());
  702. // Pumping events in private runloop modes is known to interact badly with
  703. // app modal windows like NSAlert.
  704. if ([NSApp modalWindow])
  705. return;
  706. g_app_pump->SetModeMask(kAllModesMask);
  707. }
  708. ScopedPumpMessagesInPrivateModes::~ScopedPumpMessagesInPrivateModes() {
  709. DCHECK(g_app_pump);
  710. g_app_pump->SetModeMask(kNSApplicationModalSafeModeMask);
  711. }
  712. int ScopedPumpMessagesInPrivateModes::GetModeMaskForTest() {
  713. return g_app_pump ? g_app_pump->GetModeMask() : -1;
  714. }
  715. MessagePumpNSApplication::MessagePumpNSApplication()
  716. : MessagePumpCFRunLoopBase(kNSApplicationModalSafeModeMask),
  717. running_own_loop_(false),
  718. quit_pending_(false) {
  719. DCHECK_EQ(nullptr, g_app_pump);
  720. g_app_pump = this;
  721. }
  722. MessagePumpNSApplication::~MessagePumpNSApplication() {
  723. DCHECK_EQ(this, g_app_pump);
  724. g_app_pump = nullptr;
  725. }
  726. void MessagePumpNSApplication::DoRun(Delegate* delegate) {
  727. bool last_running_own_loop_ = running_own_loop_;
  728. // NSApp must be initialized by calling:
  729. // [{some class which implements CrAppProtocol} sharedApplication]
  730. // Most likely candidates are CrApplication or BrowserCrApplication.
  731. // These can be initialized from C++ code by calling
  732. // RegisterCrApp() or RegisterBrowserCrApp().
  733. CHECK(NSApp);
  734. if (![NSApp isRunning]) {
  735. running_own_loop_ = false;
  736. // NSApplication manages autorelease pools itself when run this way.
  737. [NSApp run];
  738. } else {
  739. running_own_loop_ = true;
  740. NSDate* distant_future = [NSDate distantFuture];
  741. while (keep_running()) {
  742. MessagePumpScopedAutoreleasePool autorelease_pool(this);
  743. NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny
  744. untilDate:distant_future
  745. inMode:NSDefaultRunLoopMode
  746. dequeue:YES];
  747. if (event) {
  748. [NSApp sendEvent:event];
  749. }
  750. }
  751. }
  752. running_own_loop_ = last_running_own_loop_;
  753. }
  754. bool MessagePumpNSApplication::DoQuit() {
  755. // If the app is displaying a modal window in a native run loop, we can only
  756. // quit our run loop after the window is closed. Otherwise the [NSApplication
  757. // stop] below will apply to the modal window run loop instead. To work around
  758. // this, the quit is applied when we re-enter our own run loop after the
  759. // window is gone (see MessagePumpNSApplication::EnterExitRunLoop).
  760. if (nesting_level() > run_nesting_level() &&
  761. [[NSApplication sharedApplication] modalWindow] != nil) {
  762. quit_pending_ = true;
  763. return false;
  764. }
  765. if (!running_own_loop_) {
  766. [[NSApplication sharedApplication] stop:nil];
  767. }
  768. // Send a fake event to wake the loop up.
  769. [NSApp postEvent:[NSEvent otherEventWithType:NSEventTypeApplicationDefined
  770. location:NSZeroPoint
  771. modifierFlags:0
  772. timestamp:0
  773. windowNumber:0
  774. context:nil
  775. subtype:0
  776. data1:0
  777. data2:0]
  778. atStart:NO];
  779. return true;
  780. }
  781. void MessagePumpNSApplication::EnterExitRunLoop(CFRunLoopActivity activity) {
  782. // If we previously tried quitting while a modal window was active, check if
  783. // the window is gone now and we're no longer nested in a system run loop.
  784. if (activity == kCFRunLoopEntry && quit_pending_ &&
  785. nesting_level() <= run_nesting_level() &&
  786. [[NSApplication sharedApplication] modalWindow] == nil) {
  787. quit_pending_ = false;
  788. if (DoQuit())
  789. OnDidQuit();
  790. }
  791. }
  792. MessagePumpCrApplication::MessagePumpCrApplication() {
  793. }
  794. MessagePumpCrApplication::~MessagePumpCrApplication() {
  795. }
  796. // Prevents an autorelease pool from being created if the app is in the midst of
  797. // handling a UI event because various parts of AppKit depend on objects that
  798. // are created while handling a UI event to be autoreleased in the event loop.
  799. // An example of this is NSWindowController. When a window with a window
  800. // controller is closed it goes through a stack like this:
  801. // (Several stack frames elided for clarity)
  802. //
  803. // #0 [NSWindowController autorelease]
  804. // #1 DoAClose
  805. // #2 MessagePumpCFRunLoopBase::DoWork()
  806. // #3 [NSRunLoop run]
  807. // #4 [NSButton performClick:]
  808. // #5 [NSWindow sendEvent:]
  809. // #6 [NSApp sendEvent:]
  810. // #7 [NSApp run]
  811. //
  812. // -performClick: spins a nested run loop. If the pool created in DoWork was a
  813. // standard NSAutoreleasePool, it would release the objects that were
  814. // autoreleased into it once DoWork released it. This would cause the window
  815. // controller, which autoreleased itself in frame #0, to release itself, and
  816. // possibly free itself. Unfortunately this window controller controls the
  817. // window in frame #5. When the stack is unwound to frame #5, the window would
  818. // no longer exists and crashes may occur. Apple gets around this by never
  819. // releasing the pool it creates in frame #4, and letting frame #7 clean it up
  820. // when it cleans up the pool that wraps frame #7. When an autorelease pool is
  821. // released it releases all other pools that were created after it on the
  822. // autorelease pool stack.
  823. //
  824. // CrApplication is responsible for setting handlingSendEvent to true just
  825. // before it sends the event through the event handling mechanism, and
  826. // returning it to its previous value once the event has been sent.
  827. AutoreleasePoolType* MessagePumpCrApplication::CreateAutoreleasePool() {
  828. if (MessagePumpMac::IsHandlingSendEvent())
  829. return nil;
  830. return MessagePumpNSApplication::CreateAutoreleasePool();
  831. }
  832. // static
  833. bool MessagePumpMac::UsingCrApp() {
  834. DCHECK([NSThread isMainThread]);
  835. // If NSApp is still not initialized, then the subclass used cannot
  836. // be determined.
  837. DCHECK(NSApp);
  838. // The pump was created using MessagePumpNSApplication.
  839. if (g_not_using_cr_app)
  840. return false;
  841. return [NSApp conformsToProtocol:@protocol(CrAppProtocol)];
  842. }
  843. // static
  844. bool MessagePumpMac::IsHandlingSendEvent() {
  845. DCHECK([NSApp conformsToProtocol:@protocol(CrAppProtocol)]);
  846. NSObject<CrAppProtocol>* app = static_cast<NSObject<CrAppProtocol>*>(NSApp);
  847. return [app isHandlingSendEvent];
  848. }
  849. #endif // BUILDFLAG(IS_IOS)
  850. // static
  851. std::unique_ptr<MessagePump> MessagePumpMac::Create() {
  852. if ([NSThread isMainThread]) {
  853. #if BUILDFLAG(IS_IOS)
  854. return std::make_unique<MessagePumpUIApplication>();
  855. #else
  856. if ([NSApp conformsToProtocol:@protocol(CrAppProtocol)])
  857. return std::make_unique<MessagePumpCrApplication>();
  858. // The main-thread MessagePump implementations REQUIRE an NSApp.
  859. // Executables which have specific requirements for their
  860. // NSApplication subclass should initialize appropriately before
  861. // creating an event loop.
  862. [NSApplication sharedApplication];
  863. g_not_using_cr_app = true;
  864. return std::make_unique<MessagePumpNSApplication>();
  865. #endif
  866. }
  867. return std::make_unique<MessagePumpNSRunLoop>();
  868. }
  869. } // namespace base