Commit 80575da0 authored by tapted's avatar tapted Committed by Commit bot

Filter run loop modes in message_pump_mac.mm

There's a non-trivial overhead for the machinery to run posted tasks in
a given run loop mode. Only observe modes known to be run for a particular
thread.

BUG=640466, 724076

Review-Url: https://codereview.chromium.org/2889293002
Cr-Commit-Position: refs/heads/master@{#473391}
parent bd5768e5
......@@ -79,26 +79,29 @@ typedef NSAutoreleasePool AutoreleasePoolType;
#endif // !defined(__OBJC__) || __has_feature(objc_arc)
class BASE_EXPORT MessagePumpCFRunLoopBase : public MessagePump {
public:
// MessagePump:
void Run(Delegate* delegate) override;
void ScheduleWork() override;
void ScheduleDelayedWork(const TimeTicks& delayed_work_time) override;
void SetTimerSlack(TimerSlack timer_slack) override;
protected:
// Needs access to CreateAutoreleasePool.
friend class MessagePumpScopedAutoreleasePool;
friend class TestMessagePumpCFRunLoopBase;
public:
MessagePumpCFRunLoopBase();
// Tasks will be pumped in the run loop modes described by |mode_mask|, which
// maps bits to the index of an internal array of run loop mode identifiers.
explicit MessagePumpCFRunLoopBase(int mode_mask);
~MessagePumpCFRunLoopBase() override;
// Subclasses should implement the work they need to do in MessagePump::Run
// in the DoRun method. MessagePumpCFRunLoopBase::Run calls DoRun directly.
// This arrangement is used because MessagePumpCFRunLoopBase needs to set
// up and tear down things before and after the "meat" of DoRun.
void Run(Delegate* delegate) override;
virtual void DoRun(Delegate* delegate) = 0;
void ScheduleWork() override;
void ScheduleDelayedWork(const TimeTicks& delayed_work_time) override;
void SetTimerSlack(TimerSlack timer_slack) override;
protected:
// Accessors for private data members to be used by subclasses.
CFRunLoopRef run_loop() const { return run_loop_; }
int nesting_level() const { return nesting_level_; }
......@@ -114,6 +117,11 @@ class BASE_EXPORT MessagePumpCFRunLoopBase : public MessagePump {
// objects autoreleased by work to fall into the current autorelease pool.
virtual AutoreleasePoolType* CreateAutoreleasePool();
// Invokes function(run_loop_, arg, mode) for all the modes in |mode_mask_|.
template <typename Argument>
void InvokeForEnabledModes(void function(CFRunLoopRef, Argument, CFStringRef),
Argument argument);
private:
// Marking timers as invalid at the right time helps significantly reduce
// power use (see the comment in RunDelayedWorkTimer()), however there is no
......@@ -190,6 +198,9 @@ class BASE_EXPORT MessagePumpCFRunLoopBase : public MessagePump {
// The thread's run loop.
CFRunLoopRef run_loop_;
// Bitmask controlling the run loop modes in which posted tasks may run.
const int mode_mask_;
// The timer, sources, and observers are described above alongside their
// callbacks.
CFRunLoopTimerRef delayed_work_timer_;
......
......@@ -44,39 +44,8 @@ const CFStringRef kAllModes[] = {
CFSTR("NSUnhighlightMenuRunLoopMode"),
};
void CFRunLoopAddSourceToAllModes(CFRunLoopRef rl, CFRunLoopSourceRef source) {
for (const CFStringRef& mode : kAllModes)
CFRunLoopAddSource(rl, source, mode);
}
void CFRunLoopRemoveSourceFromAllModes(CFRunLoopRef rl,
CFRunLoopSourceRef source) {
for (const CFStringRef& mode : kAllModes)
CFRunLoopRemoveSource(rl, source, mode);
}
void CFRunLoopAddTimerToAllModes(CFRunLoopRef rl, CFRunLoopTimerRef timer) {
for (const CFStringRef& mode : kAllModes)
CFRunLoopAddTimer(rl, timer, mode);
}
void CFRunLoopRemoveTimerFromAllModes(CFRunLoopRef rl,
CFRunLoopTimerRef timer) {
for (const CFStringRef& mode : kAllModes)
CFRunLoopRemoveTimer(rl, timer, mode);
}
void CFRunLoopAddObserverToAllModes(CFRunLoopRef rl,
CFRunLoopObserverRef observer) {
for (const CFStringRef& mode : kAllModes)
CFRunLoopAddObserver(rl, observer, mode);
}
void CFRunLoopRemoveObserverFromAllModes(CFRunLoopRef rl,
CFRunLoopObserverRef observer) {
for (const CFStringRef& mode : kAllModes)
CFRunLoopRemoveObserver(rl, observer, mode);
}
// Mask that determines which modes in |kAllModes| to use.
enum { kCommonModeMask = 0x1, kAllModesMask = ~0 };
void NoOp(void* info) {
}
......@@ -154,50 +123,61 @@ class MessagePumpScopedAutoreleasePool {
DISALLOW_COPY_AND_ASSIGN(MessagePumpScopedAutoreleasePool);
};
#if !defined(OS_IOS)
// This function uses private API to modify a test timer's valid state and
// uses public API to confirm that the private API changed the correct bit.
// static
bool MessagePumpCFRunLoopBase::CanInvalidateCFRunLoopTimers() {
CFRunLoopTimerContext timer_context = CFRunLoopTimerContext();
timer_context.info = nullptr;
ScopedCFTypeRef<CFRunLoopTimerRef> test_timer(
CFRunLoopTimerCreate(NULL, // allocator
kCFTimeIntervalMax, // fire time
kCFTimeIntervalMax, // interval
0, // flags
0, // priority
nullptr, &timer_context));
// Should be valid from the start.
if (!CFRunLoopTimerIsValid(test_timer)) {
return false;
}
// Confirm that the private API can mark the timer invalid.
__ChromeCFRunLoopTimerSetValid(test_timer, false);
if (CFRunLoopTimerIsValid(test_timer)) {
return false;
}
// Confirm that the private API can mark the timer valid.
__ChromeCFRunLoopTimerSetValid(test_timer, true);
return CFRunLoopTimerIsValid(test_timer);
// Must be called on the run loop thread.
void MessagePumpCFRunLoopBase::Run(Delegate* delegate) {
// nesting_level_ will be incremented in EnterExitRunLoop, so set
// run_nesting_level_ accordingly.
int last_run_nesting_level = run_nesting_level_;
run_nesting_level_ = nesting_level_ + 1;
Delegate* last_delegate = delegate_;
SetDelegate(delegate);
DoRun(delegate);
// Restore the previous state of the object.
SetDelegate(last_delegate);
run_nesting_level_ = last_run_nesting_level;
}
#endif // !defined(OS_IOS)
// static
void MessagePumpCFRunLoopBase::ChromeCFRunLoopTimerSetValid(
CFRunLoopTimerRef timer,
bool valid) {
#if !defined(OS_IOS)
static bool can_invalidate_timers = CanInvalidateCFRunLoopTimers();
if (can_invalidate_timers) {
__ChromeCFRunLoopTimerSetValid(timer, valid);
// May be called on any thread.
void MessagePumpCFRunLoopBase::ScheduleWork() {
CFRunLoopSourceSignal(work_source_);
CFRunLoopWakeUp(run_loop_);
}
// Must be called on the run loop thread.
void MessagePumpCFRunLoopBase::ScheduleDelayedWork(
const TimeTicks& delayed_work_time) {
TimeDelta delta = delayed_work_time - TimeTicks::Now();
delayed_work_fire_time_ = CFAbsoluteTimeGetCurrent() + delta.InSecondsF();
// Flip the timer's validation bit just before setting the new fire time. Do
// this now because CFRunLoopTimerSetNextFireDate() likely checks the validity
// of a timer before proceeding to set its fire date. Making the timer valid
// now won't have any side effects (such as a premature firing of the timer)
// because we're only flipping a bit.
//
// Please see the comment in RunDelayedWorkTimer() for more info on the whys
// of invalidation.
ChromeCFRunLoopTimerSetValid(delayed_work_timer_, true);
CFRunLoopTimerSetNextFireDate(delayed_work_timer_, delayed_work_fire_time_);
if (timer_slack_ == TIMER_SLACK_MAXIMUM) {
CFRunLoopTimerSetTolerance(delayed_work_timer_, delta.InSecondsF() * 0.5);
} else {
CFRunLoopTimerSetTolerance(delayed_work_timer_, 0);
}
#endif // !defined(OS_IOS)
}
void MessagePumpCFRunLoopBase::SetTimerSlack(TimerSlack timer_slack) {
timer_slack_ = timer_slack;
}
// Must be called on the run loop thread.
MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase()
: delegate_(NULL),
MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase(int mode_mask)
: mode_mask_(mode_mask),
delegate_(NULL),
delayed_work_fire_time_(kCFTimeIntervalMax),
timer_slack_(base::TIMER_SLACK_NONE),
nesting_level_(0),
......@@ -220,7 +200,7 @@ MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase()
0, // priority
RunDelayedWorkTimer,
&timer_context);
CFRunLoopAddTimerToAllModes(run_loop_, delayed_work_timer_);
InvokeForEnabledModes(&CFRunLoopAddTimer, delayed_work_timer_);
CFRunLoopSourceContext source_context = CFRunLoopSourceContext();
source_context.info = this;
......@@ -228,19 +208,19 @@ MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase()
work_source_ = CFRunLoopSourceCreate(NULL, // allocator
1, // priority
&source_context);
CFRunLoopAddSourceToAllModes(run_loop_, work_source_);
InvokeForEnabledModes(&CFRunLoopAddSource, work_source_);
source_context.perform = RunIdleWorkSource;
idle_work_source_ = CFRunLoopSourceCreate(NULL, // allocator
2, // priority
&source_context);
CFRunLoopAddSourceToAllModes(run_loop_, idle_work_source_);
InvokeForEnabledModes(&CFRunLoopAddSource, idle_work_source_);
source_context.perform = RunNestingDeferredWorkSource;
nesting_deferred_work_source_ = CFRunLoopSourceCreate(NULL, // allocator
0, // priority
&source_context);
CFRunLoopAddSourceToAllModes(run_loop_, nesting_deferred_work_source_);
InvokeForEnabledModes(&CFRunLoopAddSource, nesting_deferred_work_source_);
CFRunLoopObserverContext observer_context = CFRunLoopObserverContext();
observer_context.info = this;
......@@ -250,7 +230,7 @@ MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase()
0, // priority
PreWaitObserver,
&observer_context);
CFRunLoopAddObserverToAllModes(run_loop_, pre_wait_observer_);
InvokeForEnabledModes(&CFRunLoopAddObserver, pre_wait_observer_);
pre_source_observer_ = CFRunLoopObserverCreate(NULL, // allocator
kCFRunLoopBeforeSources,
......@@ -258,7 +238,7 @@ MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase()
0, // priority
PreSourceObserver,
&observer_context);
CFRunLoopAddObserverToAllModes(run_loop_, pre_source_observer_);
InvokeForEnabledModes(&CFRunLoopAddObserver, pre_source_observer_);
enter_exit_observer_ = CFRunLoopObserverCreate(NULL, // allocator
kCFRunLoopEntry |
......@@ -267,54 +247,28 @@ MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase()
0, // priority
EnterExitObserver,
&observer_context);
CFRunLoopAddObserverToAllModes(run_loop_, enter_exit_observer_);
InvokeForEnabledModes(&CFRunLoopAddObserver, enter_exit_observer_);
}
// Ideally called on the run loop thread. If other run loops were running
// lower on the run loop thread's stack when this object was created, the
// same number of run loops must be running when this object is destroyed.
MessagePumpCFRunLoopBase::~MessagePumpCFRunLoopBase() {
CFRunLoopRemoveObserverFromAllModes(run_loop_, enter_exit_observer_);
CFRelease(enter_exit_observer_);
CFRunLoopRemoveObserverFromAllModes(run_loop_, pre_source_observer_);
CFRelease(pre_source_observer_);
CFRunLoopRemoveObserverFromAllModes(run_loop_, pre_wait_observer_);
CFRelease(pre_wait_observer_);
CFRunLoopRemoveSourceFromAllModes(run_loop_, nesting_deferred_work_source_);
CFRelease(nesting_deferred_work_source_);
CFRunLoopRemoveSourceFromAllModes(run_loop_, idle_work_source_);
CFRelease(idle_work_source_);
CFRunLoopRemoveSourceFromAllModes(run_loop_, work_source_);
CFRelease(work_source_);
CFRunLoopRemoveTimerFromAllModes(run_loop_, delayed_work_timer_);
for (const CFRunLoopObserverRef& observer :
{enter_exit_observer_, pre_source_observer_, pre_wait_observer_}) {
InvokeForEnabledModes(&CFRunLoopRemoveObserver, observer);
CFRelease(observer);
}
for (const CFRunLoopSourceRef& source :
{nesting_deferred_work_source_, idle_work_source_, work_source_}) {
InvokeForEnabledModes(&CFRunLoopRemoveSource, source);
CFRelease(source);
}
InvokeForEnabledModes(&CFRunLoopRemoveTimer, delayed_work_timer_);
CFRelease(delayed_work_timer_);
CFRelease(run_loop_);
}
// Must be called on the run loop thread.
void MessagePumpCFRunLoopBase::Run(Delegate* delegate) {
// nesting_level_ will be incremented in EnterExitRunLoop, so set
// run_nesting_level_ accordingly.
int last_run_nesting_level = run_nesting_level_;
run_nesting_level_ = nesting_level_ + 1;
Delegate* last_delegate = delegate_;
SetDelegate(delegate);
DoRun(delegate);
// Restore the previous state of the object.
SetDelegate(last_delegate);
run_nesting_level_ = last_run_nesting_level;
}
void MessagePumpCFRunLoopBase::SetDelegate(Delegate* delegate) {
delegate_ = delegate;
......@@ -333,38 +287,61 @@ void MessagePumpCFRunLoopBase::SetDelegate(Delegate* delegate) {
}
}
// May be called on any thread.
void MessagePumpCFRunLoopBase::ScheduleWork() {
CFRunLoopSourceSignal(work_source_);
CFRunLoopWakeUp(run_loop_);
// Base version returns a standard NSAutoreleasePool.
AutoreleasePoolType* MessagePumpCFRunLoopBase::CreateAutoreleasePool() {
return [[NSAutoreleasePool alloc] init];
}
// Must be called on the run loop thread.
void MessagePumpCFRunLoopBase::ScheduleDelayedWork(
const TimeTicks& delayed_work_time) {
TimeDelta delta = delayed_work_time - TimeTicks::Now();
delayed_work_fire_time_ = CFAbsoluteTimeGetCurrent() + delta.InSecondsF();
// Flip the timer's validation bit just before setting the new fire time. Do
// this now because CFRunLoopTimerSetNextFireDate() likely checks the validity
// of a timer before proceeding to set its fire date. Making the timer valid
// now won't have any side effects (such as a premature firing of the timer)
// because we're only flipping a bit.
//
// Please see the comment in RunDelayedWorkTimer() for more info on the whys
// of invalidation.
ChromeCFRunLoopTimerSetValid(delayed_work_timer_, true);
template <typename Argument>
void MessagePumpCFRunLoopBase::InvokeForEnabledModes(void method(CFRunLoopRef,
Argument,
CFStringRef),
Argument argument) {
for (size_t i = 0; i < arraysize(kAllModes); ++i) {
if (mode_mask_ & (0x1 << i))
method(run_loop_, argument, kAllModes[i]);
}
}
CFRunLoopTimerSetNextFireDate(delayed_work_timer_, delayed_work_fire_time_);
if (timer_slack_ == TIMER_SLACK_MAXIMUM) {
CFRunLoopTimerSetTolerance(delayed_work_timer_, delta.InSecondsF() * 0.5);
} else {
CFRunLoopTimerSetTolerance(delayed_work_timer_, 0);
#if !defined(OS_IOS)
// This function uses private API to modify a test timer's valid state and
// uses public API to confirm that the private API changed the correct bit.
// static
bool MessagePumpCFRunLoopBase::CanInvalidateCFRunLoopTimers() {
CFRunLoopTimerContext timer_context = CFRunLoopTimerContext();
timer_context.info = nullptr;
ScopedCFTypeRef<CFRunLoopTimerRef> test_timer(
CFRunLoopTimerCreate(NULL, // allocator
kCFTimeIntervalMax, // fire time
kCFTimeIntervalMax, // interval
0, // flags
0, // priority
nullptr, &timer_context));
// Should be valid from the start.
if (!CFRunLoopTimerIsValid(test_timer)) {
return false;
}
// Confirm that the private API can mark the timer invalid.
__ChromeCFRunLoopTimerSetValid(test_timer, false);
if (CFRunLoopTimerIsValid(test_timer)) {
return false;
}
// Confirm that the private API can mark the timer valid.
__ChromeCFRunLoopTimerSetValid(test_timer, true);
return CFRunLoopTimerIsValid(test_timer);
}
#endif // !defined(OS_IOS)
void MessagePumpCFRunLoopBase::SetTimerSlack(TimerSlack timer_slack) {
timer_slack_ = timer_slack;
// static
void MessagePumpCFRunLoopBase::ChromeCFRunLoopTimerSetValid(
CFRunLoopTimerRef timer,
bool valid) {
#if !defined(OS_IOS)
static bool can_invalidate_timers = CanInvalidateCFRunLoopTimers();
if (can_invalidate_timers) {
__ChromeCFRunLoopTimerSetValid(timer, valid);
}
#endif // !defined(OS_IOS)
}
// Called from the run loop.
......@@ -639,14 +616,8 @@ void MessagePumpCFRunLoopBase::EnterExitObserver(CFRunLoopObserverRef observer,
void MessagePumpCFRunLoopBase::EnterExitRunLoop(CFRunLoopActivity activity) {
}
// Base version returns a standard NSAutoreleasePool.
AutoreleasePoolType* MessagePumpCFRunLoopBase::CreateAutoreleasePool() {
return [[NSAutoreleasePool alloc] init];
}
MessagePumpCFRunLoop::MessagePumpCFRunLoop()
: quit_pending_(false) {
}
: MessagePumpCFRunLoopBase(kCommonModeMask), quit_pending_(false) {}
MessagePumpCFRunLoop::~MessagePumpCFRunLoop() {}
......@@ -697,17 +668,17 @@ void MessagePumpCFRunLoop::EnterExitRunLoop(CFRunLoopActivity activity) {
}
MessagePumpNSRunLoop::MessagePumpNSRunLoop()
: keep_running_(true) {
: MessagePumpCFRunLoopBase(kCommonModeMask), keep_running_(true) {
CFRunLoopSourceContext source_context = CFRunLoopSourceContext();
source_context.perform = NoOp;
quit_source_ = CFRunLoopSourceCreate(NULL, // allocator
0, // priority
&source_context);
CFRunLoopAddSourceToAllModes(run_loop(), quit_source_);
InvokeForEnabledModes(&CFRunLoopAddSource, quit_source_);
}
MessagePumpNSRunLoop::~MessagePumpNSRunLoop() {
CFRunLoopRemoveSourceFromAllModes(run_loop(), quit_source_);
InvokeForEnabledModes(&CFRunLoopRemoveSource, quit_source_);
CFRelease(quit_source_);
}
......@@ -729,8 +700,7 @@ void MessagePumpNSRunLoop::Quit() {
#if defined(OS_IOS)
MessagePumpUIApplication::MessagePumpUIApplication()
: run_loop_(NULL) {
}
: MessagePumpCFRunLoopBase(kCommonModeMask), run_loop_(NULL) {}
MessagePumpUIApplication::~MessagePumpUIApplication() {}
......@@ -752,9 +722,9 @@ void MessagePumpUIApplication::Attach(Delegate* delegate) {
#else
MessagePumpNSApplication::MessagePumpNSApplication()
: keep_running_(true),
running_own_loop_(false) {
}
: MessagePumpCFRunLoopBase(kAllModesMask),
keep_running_(true),
running_own_loop_(false) {}
MessagePumpNSApplication::~MessagePumpNSApplication() {}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment