summaryrefslogtreecommitdiffstats
path: root/chromium/base/message_loop
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2017-11-20 15:06:40 +0100
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2017-11-22 11:48:58 +0000
commitdaa093eea7c773db06799a13bd7e4e2e2a9f8f14 (patch)
tree96cc5e7b9194c1b29eab927730bfa419e7111c25 /chromium/base/message_loop
parentbe59a35641616a4cf23c4a13fa0632624b021c1b (diff)
BASELINE: Update Chromium to 63.0.3239.58
Change-Id: Ia93b322a00ba4dd4004f3bcf1254063ba90e1605 Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
Diffstat (limited to 'chromium/base/message_loop')
-rw-r--r--chromium/base/message_loop/incoming_task_queue.cc324
-rw-r--r--chromium/base/message_loop/incoming_task_queue.h210
-rw-r--r--chromium/base/message_loop/message_loop.cc216
-rw-r--r--chromium/base/message_loop/message_loop.h83
-rw-r--r--chromium/base/message_loop/message_loop_io_posix_unittest.cc21
-rw-r--r--chromium/base/message_loop/message_loop_perftest.cc254
-rw-r--r--chromium/base/message_loop/message_loop_task_runner.cc13
-rw-r--r--chromium/base/message_loop/message_loop_task_runner.h8
-rw-r--r--chromium/base/message_loop/message_loop_unittest.cc91
-rw-r--r--chromium/base/message_loop/message_pump_default.cc19
-rw-r--r--chromium/base/message_loop/message_pump_default.h4
-rw-r--r--chromium/base/message_loop/message_pump_fuchsia.cc209
-rw-r--r--chromium/base/message_loop/message_pump_fuchsia.h73
-rw-r--r--chromium/base/message_loop/message_pump_glib_unittest.cc3
-rw-r--r--chromium/base/message_loop/message_pump_io_ios.cc2
-rw-r--r--chromium/base/message_loop/message_pump_io_ios.h8
-rw-r--r--chromium/base/message_loop/message_pump_libevent.cc2
-rw-r--r--chromium/base/message_loop/message_pump_libevent.h8
-rw-r--r--chromium/base/message_loop/message_pump_mac.h40
-rw-r--r--chromium/base/message_loop/message_pump_mac.mm141
-rw-r--r--chromium/base/message_loop/message_pump_mac_unittest.mm (renamed from chromium/base/message_loop/message_pump_mac_unittest.cc)116
-rw-r--r--chromium/base/message_loop/message_pump_perftest.cc2
22 files changed, 1325 insertions, 522 deletions
diff --git a/chromium/base/message_loop/incoming_task_queue.cc b/chromium/base/message_loop/incoming_task_queue.cc
index a945abbb475..844a64f10e6 100644
--- a/chromium/base/message_loop/incoming_task_queue.cc
+++ b/chromium/base/message_loop/incoming_task_queue.cc
@@ -50,19 +50,20 @@ TimeTicks CalculateDelayedRuntime(TimeDelta delay) {
} // namespace
IncomingTaskQueue::IncomingTaskQueue(MessageLoop* message_loop)
- : high_res_task_count_(0),
- message_loop_(message_loop),
- next_sequence_num_(0),
- message_loop_scheduled_(false),
- always_schedule_work_(AlwaysNotifyPump(message_loop_->type())),
- is_ready_for_scheduling_(false) {
-}
-
-bool IncomingTaskQueue::AddToIncomingQueue(
- const tracked_objects::Location& from_here,
- OnceClosure task,
- TimeDelta delay,
- bool nestable) {
+ : always_schedule_work_(AlwaysNotifyPump(message_loop->type())),
+ triage_tasks_(this),
+ delayed_tasks_(this),
+ deferred_tasks_(this),
+ message_loop_(message_loop) {
+ // The constructing sequence is not necessarily the running sequence in the
+ // case of base::Thread.
+ DETACH_FROM_SEQUENCE(sequence_checker_);
+}
+
+bool IncomingTaskQueue::AddToIncomingQueue(const Location& from_here,
+ OnceClosure task,
+ TimeDelta delay,
+ Nestable nestable) {
// Use CHECK instead of DCHECK to crash earlier. See http://crbug.com/711167
// for details.
CHECK(task);
@@ -91,29 +92,15 @@ bool IncomingTaskQueue::IsIdleForTesting() {
return incoming_queue_.empty();
}
-int IncomingTaskQueue::ReloadWorkQueue(TaskQueue* work_queue) {
- // Make sure no tasks are lost.
- DCHECK(work_queue->empty());
-
- // Acquire all we can from the inter-thread queue with one lock acquisition.
- AutoLock lock(incoming_queue_lock_);
- if (incoming_queue_.empty()) {
- // If the loop attempts to reload but there are no tasks in the incoming
- // queue, that means it will go to sleep waiting for more work. If the
- // incoming queue becomes nonempty we need to schedule it again.
- message_loop_scheduled_ = false;
- } else {
- incoming_queue_.swap(*work_queue);
- }
- // Reset the count of high resolution tasks since our queue is now empty.
- int high_res_tasks = high_res_task_count_;
- high_res_task_count_ = 0;
- return high_res_tasks;
-}
-
void IncomingTaskQueue::WillDestroyCurrentMessageLoop() {
- base::subtle::AutoWriteLock lock(message_loop_lock_);
- message_loop_ = NULL;
+ {
+ AutoLock auto_lock(incoming_queue_lock_);
+ accept_new_tasks_ = false;
+ }
+ {
+ AutoLock auto_lock(message_loop_lock_);
+ message_loop_ = nullptr;
+ }
}
void IncomingTaskQueue::StartScheduling() {
@@ -138,60 +125,251 @@ IncomingTaskQueue::~IncomingTaskQueue() {
DCHECK(!message_loop_);
}
+void IncomingTaskQueue::RunTask(PendingTask* pending_task) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ task_annotator_.RunTask("MessageLoop::PostTask", pending_task);
+}
+
+IncomingTaskQueue::TriageQueue::TriageQueue(IncomingTaskQueue* outer)
+ : outer_(outer) {}
+
+IncomingTaskQueue::TriageQueue::~TriageQueue() = default;
+
+const PendingTask& IncomingTaskQueue::TriageQueue::Peek() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(outer_->sequence_checker_);
+ ReloadFromIncomingQueueIfEmpty();
+ DCHECK(!queue_.empty());
+ return queue_.front();
+}
+
+PendingTask IncomingTaskQueue::TriageQueue::Pop() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(outer_->sequence_checker_);
+ ReloadFromIncomingQueueIfEmpty();
+ DCHECK(!queue_.empty());
+ PendingTask pending_task = std::move(queue_.front());
+ queue_.pop();
+
+ if (pending_task.is_high_res)
+ --outer_->pending_high_res_tasks_;
+
+ return pending_task;
+}
+
+bool IncomingTaskQueue::TriageQueue::HasTasks() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(outer_->sequence_checker_);
+ ReloadFromIncomingQueueIfEmpty();
+ return !queue_.empty();
+}
+
+void IncomingTaskQueue::TriageQueue::Clear() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(outer_->sequence_checker_);
+ // Previously, MessageLoop would delete all tasks including delayed and
+ // deferred tasks in a single round before attempting to reload from the
+ // incoming queue to see if more tasks remained. This gave it a chance to
+ // assess whether or not clearing should continue. As a result, while
+ // reloading is automatic for getting and seeing if tasks exist, it is not
+ // automatic for Clear().
+ while (!queue_.empty()) {
+ PendingTask pending_task = std::move(queue_.front());
+ queue_.pop();
+
+ if (pending_task.is_high_res)
+ --outer_->pending_high_res_tasks_;
+
+ if (!pending_task.delayed_run_time.is_null()) {
+ outer_->delayed_tasks().Push(std::move(pending_task));
+ }
+ }
+}
+
+void IncomingTaskQueue::TriageQueue::ReloadFromIncomingQueueIfEmpty() {
+ if (queue_.empty()) {
+ // TODO(robliao): Since these high resolution tasks aren't yet in the
+ // delayed queue, they technically shouldn't trigger high resolution timers
+ // until they are.
+ outer_->pending_high_res_tasks_ += outer_->ReloadWorkQueue(&queue_);
+ }
+}
+
+IncomingTaskQueue::DelayedQueue::DelayedQueue(IncomingTaskQueue* outer)
+ : outer_(outer) {}
+
+IncomingTaskQueue::DelayedQueue::~DelayedQueue() = default;
+
+void IncomingTaskQueue::DelayedQueue::Push(PendingTask pending_task) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(outer_->sequence_checker_);
+
+ if (pending_task.is_high_res)
+ ++outer_->pending_high_res_tasks_;
+
+ queue_.push(std::move(pending_task));
+}
+
+const PendingTask& IncomingTaskQueue::DelayedQueue::Peek() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(outer_->sequence_checker_);
+ DCHECK(!queue_.empty());
+ return queue_.top();
+}
+
+PendingTask IncomingTaskQueue::DelayedQueue::Pop() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(outer_->sequence_checker_);
+ DCHECK(!queue_.empty());
+ PendingTask delayed_task = std::move(const_cast<PendingTask&>(queue_.top()));
+ queue_.pop();
+
+ if (delayed_task.is_high_res)
+ --outer_->pending_high_res_tasks_;
+
+ return delayed_task;
+}
+
+bool IncomingTaskQueue::DelayedQueue::HasTasks() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(outer_->sequence_checker_);
+ // TODO(robliao): The other queues don't check for IsCancelled(). Should they?
+ while (!queue_.empty() && Peek().task.IsCancelled())
+ Pop();
+
+ return !queue_.empty();
+}
+
+void IncomingTaskQueue::DelayedQueue::Clear() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(outer_->sequence_checker_);
+ while (!queue_.empty())
+ Pop();
+}
+
+IncomingTaskQueue::DeferredQueue::DeferredQueue(IncomingTaskQueue* outer)
+ : outer_(outer) {}
+
+IncomingTaskQueue::DeferredQueue::~DeferredQueue() = default;
+
+void IncomingTaskQueue::DeferredQueue::Push(PendingTask pending_task) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(outer_->sequence_checker_);
+
+ // TODO(robliao): These tasks should not count towards the high res task count
+ // since they are no longer in the delayed queue.
+ if (pending_task.is_high_res)
+ ++outer_->pending_high_res_tasks_;
+
+ queue_.push(std::move(pending_task));
+}
+
+const PendingTask& IncomingTaskQueue::DeferredQueue::Peek() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(outer_->sequence_checker_);
+ DCHECK(!queue_.empty());
+ return queue_.front();
+}
+
+PendingTask IncomingTaskQueue::DeferredQueue::Pop() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(outer_->sequence_checker_);
+ DCHECK(!queue_.empty());
+ PendingTask deferred_task = std::move(queue_.front());
+ queue_.pop();
+
+ if (deferred_task.is_high_res)
+ --outer_->pending_high_res_tasks_;
+
+ return deferred_task;
+}
+
+bool IncomingTaskQueue::DeferredQueue::HasTasks() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(outer_->sequence_checker_);
+ return !queue_.empty();
+}
+
+void IncomingTaskQueue::DeferredQueue::Clear() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(outer_->sequence_checker_);
+ while (!queue_.empty())
+ Pop();
+}
+
bool IncomingTaskQueue::PostPendingTask(PendingTask* pending_task) {
// Warning: Don't try to short-circuit, and handle this thread's tasks more
// directly, as it could starve handling of foreign threads. Put every task
// into this queue.
+ bool accept_new_tasks;
+ bool schedule_work = false;
+ {
+ AutoLock auto_lock(incoming_queue_lock_);
+ accept_new_tasks = accept_new_tasks_;
+ if (accept_new_tasks)
+ schedule_work = PostPendingTaskLockRequired(pending_task);
+ }
- // Ensures |message_loop_| isn't destroyed while running.
- base::subtle::AutoReadLock hold_message_loop(message_loop_lock_);
-
- if (!message_loop_) {
+ if (!accept_new_tasks) {
+ // Clear the pending task outside of |incoming_queue_lock_| to prevent any
+ // chance of self-deadlock if destroying a task also posts a task to this
+ // queue.
+ DCHECK(!schedule_work);
pending_task->task.Reset();
return false;
}
- bool schedule_work = false;
- {
- AutoLock hold(incoming_queue_lock_);
+ // Wake up the message loop and schedule work. This is done outside
+ // |incoming_queue_lock_| to allow for multiple post tasks to occur while
+ // ScheduleWork() is running. For platforms (e.g. Android) that require one
+ // call to ScheduleWork() for each task, all pending tasks may serialize
+ // within the ScheduleWork() call. As a result, holding a lock to maintain the
+ // lifetime of |message_loop_| is less of a concern.
+ if (schedule_work) {
+ // Ensures |message_loop_| isn't destroyed while running.
+ AutoLock auto_lock(message_loop_lock_);
+ if (message_loop_)
+ message_loop_->ScheduleWork();
+ }
+
+ return true;
+}
+
+bool IncomingTaskQueue::PostPendingTaskLockRequired(PendingTask* pending_task) {
+ incoming_queue_lock_.AssertAcquired();
#if defined(OS_WIN)
- if (pending_task->is_high_res)
- ++high_res_task_count_;
+ if (pending_task->is_high_res)
+ ++high_res_task_count_;
#endif
- // Initialize the sequence number. The sequence number is used for delayed
- // tasks (to facilitate FIFO sorting when two tasks have the same
- // delayed_run_time value) and for identifying the task in about:tracing.
- pending_task->sequence_num = next_sequence_num_++;
-
- message_loop_->task_annotator()->DidQueueTask("MessageLoop::PostTask",
- *pending_task);
-
- bool was_empty = incoming_queue_.empty();
- incoming_queue_.push(std::move(*pending_task));
-
- if (is_ready_for_scheduling_ &&
- (always_schedule_work_ || (!message_loop_scheduled_ && was_empty))) {
- schedule_work = true;
- // After we've scheduled the message loop, we do not need to do so again
- // until we know it has processed all of the work in our queue and is
- // waiting for more work again. The message loop will always attempt to
- // reload from the incoming queue before waiting again so we clear this
- // flag in ReloadWorkQueue().
- message_loop_scheduled_ = true;
- }
+ // Initialize the sequence number. The sequence number is used for delayed
+ // tasks (to facilitate FIFO sorting when two tasks have the same
+ // delayed_run_time value) and for identifying the task in about:tracing.
+ pending_task->sequence_num = next_sequence_num_++;
+
+ task_annotator_.DidQueueTask("MessageLoop::PostTask", *pending_task);
+
+ bool was_empty = incoming_queue_.empty();
+ incoming_queue_.push(std::move(*pending_task));
+
+ if (is_ready_for_scheduling_ &&
+ (always_schedule_work_ || (!message_loop_scheduled_ && was_empty))) {
+ // After we've scheduled the message loop, we do not need to do so again
+ // until we know it has processed all of the work in our queue and is
+ // waiting for more work again. The message loop will always attempt to
+ // reload from the incoming queue before waiting again so we clear this
+ // flag in ReloadWorkQueue().
+ message_loop_scheduled_ = true;
+ return true;
}
+ return false;
+}
- // Wake up the message loop and schedule work. This is done outside
- // |incoming_queue_lock_| because signaling the message loop may cause this
- // thread to be switched. If |incoming_queue_lock_| is held, any other thread
- // that wants to post a task will be blocked until this thread switches back
- // in and releases |incoming_queue_lock_|.
- if (schedule_work)
- message_loop_->ScheduleWork();
+int IncomingTaskQueue::ReloadWorkQueue(TaskQueue* work_queue) {
+ // Make sure no tasks are lost.
+ DCHECK(work_queue->empty());
- return true;
+ // Acquire all we can from the inter-thread queue with one lock acquisition.
+ AutoLock lock(incoming_queue_lock_);
+ if (incoming_queue_.empty()) {
+ // If the loop attempts to reload but there are no tasks in the incoming
+ // queue, that means it will go to sleep waiting for more work. If the
+ // incoming queue becomes nonempty we need to schedule it again.
+ message_loop_scheduled_ = false;
+ } else {
+ incoming_queue_.swap(*work_queue);
+ }
+ // Reset the count of high resolution tasks since our queue is now empty.
+ int high_res_tasks = high_res_task_count_;
+ high_res_task_count_ = 0;
+ return high_res_tasks;
}
} // namespace internal
diff --git a/chromium/base/message_loop/incoming_task_queue.h b/chromium/base/message_loop/incoming_task_queue.h
index d6028cb914e..861f0fc1796 100644
--- a/chromium/base/message_loop/incoming_task_queue.h
+++ b/chromium/base/message_loop/incoming_task_queue.h
@@ -7,16 +7,18 @@
#include "base/base_export.h"
#include "base/callback.h"
+#include "base/debug/task_annotator.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/pending_task.h"
+#include "base/sequence_checker.h"
#include "base/synchronization/lock.h"
-#include "base/synchronization/read_write_lock.h"
#include "base/time/time.h"
namespace base {
class MessageLoop;
+class PostTaskTest;
namespace internal {
@@ -26,6 +28,41 @@ namespace internal {
class BASE_EXPORT IncomingTaskQueue
: public RefCountedThreadSafe<IncomingTaskQueue> {
public:
+ // Provides a read and remove only view into a task queue.
+ class ReadAndRemoveOnlyQueue {
+ public:
+ ReadAndRemoveOnlyQueue() = default;
+ virtual ~ReadAndRemoveOnlyQueue() = default;
+
+ // Returns the next task. HasTasks() is assumed to be true.
+ virtual const PendingTask& Peek() = 0;
+
+ // Removes and returns the next task. HasTasks() is assumed to be true.
+ virtual PendingTask Pop() = 0;
+
+ // Whether this queue has tasks.
+ virtual bool HasTasks() = 0;
+
+ // Removes all tasks.
+ virtual void Clear() = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ReadAndRemoveOnlyQueue);
+ };
+
+ // Provides a read-write task queue.
+ class Queue : public ReadAndRemoveOnlyQueue {
+ public:
+ Queue() = default;
+ ~Queue() override = default;
+
+ // Adds the task to the end of the queue.
+ virtual void Push(PendingTask pending_task) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Queue);
+ };
+
explicit IncomingTaskQueue(MessageLoop* message_loop);
// Appends a task to the incoming queue. Posting of all tasks is routed though
@@ -35,19 +72,14 @@ class BASE_EXPORT IncomingTaskQueue
// Returns true if the task was successfully added to the queue, otherwise
// returns false. In all cases, the ownership of |task| is transferred to the
// called method.
- bool AddToIncomingQueue(const tracked_objects::Location& from_here,
+ bool AddToIncomingQueue(const Location& from_here,
OnceClosure task,
TimeDelta delay,
- bool nestable);
+ Nestable nestable);
// Returns true if the message loop is "idle". Provided for testing.
bool IsIdleForTesting();
- // Loads tasks from the |incoming_queue_| into |*work_queue|. Must be called
- // from the thread that is running the loop. Returns the number of tasks that
- // require high resolution timers.
- int ReloadWorkQueue(TaskQueue* work_queue);
-
// Disconnects |this| from the parent message loop.
void WillDestroyCurrentMessageLoop();
@@ -55,8 +87,107 @@ class BASE_EXPORT IncomingTaskQueue
// scheduling work.
void StartScheduling();
+ // Runs |pending_task|.
+ void RunTask(PendingTask* pending_task);
+
+ ReadAndRemoveOnlyQueue& triage_tasks() { return triage_tasks_; }
+
+ Queue& delayed_tasks() { return delayed_tasks_; }
+
+ Queue& deferred_tasks() { return deferred_tasks_; }
+
+ bool HasPendingHighResolutionTasks() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ return pending_high_res_tasks_ > 0;
+ }
+
private:
+ friend class base::PostTaskTest;
friend class RefCountedThreadSafe<IncomingTaskQueue>;
+
+ // These queues below support the previous MessageLoop behavior of
+ // maintaining three queue queues to process tasks:
+ //
+ // TriageQueue
+ // The first queue to receive all tasks for the processing sequence. Tasks are
+ // generally either dispatched immediately or sent to the queues below.
+ //
+ // DelayedQueue
+ // The queue for holding tasks that should be run later and sorted by expected
+ // run time.
+ //
+ // DeferredQueue
+ // The queue for holding tasks that couldn't be run while the MessageLoop was
+ // nested. These are generally processed during the idle stage.
+ //
+ // Many of these do not share implementations even though they look like they
+ // could because of small quirks (reloading semantics) or differing underlying
+ // data strucutre (TaskQueue vs DelayedTaskQueue).
+
+ // The starting point for all tasks on the sequence processing the tasks.
+ class TriageQueue : public ReadAndRemoveOnlyQueue {
+ public:
+ TriageQueue(IncomingTaskQueue* outer);
+ ~TriageQueue() override;
+
+ // ReadAndRemoveOnlyQueue:
+ // In general, the methods below will attempt to reload from the incoming
+ // queue if the queue itself is empty except for Clear(). See Clear() for
+ // why it doesn't reload.
+ const PendingTask& Peek() override;
+ PendingTask Pop() override;
+ // Whether this queue has tasks after reloading from the incoming queue.
+ bool HasTasks() override;
+ void Clear() override;
+
+ private:
+ void ReloadFromIncomingQueueIfEmpty();
+
+ IncomingTaskQueue* const outer_;
+ TaskQueue queue_;
+
+ DISALLOW_COPY_AND_ASSIGN(TriageQueue);
+ };
+
+ class DelayedQueue : public Queue {
+ public:
+ DelayedQueue(IncomingTaskQueue* outer);
+ ~DelayedQueue() override;
+
+ // Queue:
+ const PendingTask& Peek() override;
+ PendingTask Pop() override;
+ // Whether this queue has tasks after sweeping the cancelled ones in front.
+ bool HasTasks() override;
+ void Clear() override;
+ void Push(PendingTask pending_task) override;
+
+ private:
+ IncomingTaskQueue* const outer_;
+ DelayedTaskQueue queue_;
+
+ DISALLOW_COPY_AND_ASSIGN(DelayedQueue);
+ };
+
+ class DeferredQueue : public Queue {
+ public:
+ DeferredQueue(IncomingTaskQueue* outer);
+ ~DeferredQueue() override;
+
+ // Queue:
+ const PendingTask& Peek() override;
+ PendingTask Pop() override;
+ bool HasTasks() override;
+ void Clear() override;
+ void Push(PendingTask pending_task) override;
+
+ private:
+ IncomingTaskQueue* const outer_;
+ TaskQueue queue_;
+
+ DISALLOW_COPY_AND_ASSIGN(DeferredQueue);
+ };
+
virtual ~IncomingTaskQueue();
// Adds a task to |incoming_queue_|. The caller retains ownership of
@@ -65,42 +196,67 @@ class BASE_EXPORT IncomingTaskQueue
// does not retain |pending_task->task| beyond this function call.
bool PostPendingTask(PendingTask* pending_task);
- // Wakes up the message loop and schedules work.
- void ScheduleWork();
+ // Does the real work of posting a pending task. Returns true if the caller
+ // should call ScheduleWork() on the message loop.
+ bool PostPendingTaskLockRequired(PendingTask* pending_task);
- // Number of tasks that require high resolution timing. This value is kept
- // so that ReloadWorkQueue() completes in constant time.
- int high_res_task_count_;
+ // Loads tasks from the |incoming_queue_| into |*work_queue|. Must be called
+ // from the sequence processing the tasks. Returns the number of tasks that
+ // require high resolution timers in |work_queue|.
+ int ReloadWorkQueue(TaskQueue* work_queue);
- // The lock that protects access to the members of this class, except
- // |message_loop_|.
+ // Checks calls made only on the MessageLoop thread.
+ SEQUENCE_CHECKER(sequence_checker_);
+
+ debug::TaskAnnotator task_annotator_;
+
+ // True if we always need to call ScheduleWork when receiving a new task, even
+ // if the incoming queue was not empty.
+ const bool always_schedule_work_;
+
+ // Queue for initial triaging of tasks on the |sequence_checker_| sequence.
+ TriageQueue triage_tasks_;
+
+ // Queue for delayed tasks on the |sequence_checker_| sequence.
+ DelayedQueue delayed_tasks_;
+
+ // Queue for non-nestable deferred tasks on the |sequence_checker_| sequence.
+ DeferredQueue deferred_tasks_;
+
+ // Number of high resolution tasks in the sequence affine queues above.
+ int pending_high_res_tasks_ = 0;
+
+ // Lock that protects |message_loop_| to prevent it from being deleted while
+ // a request is made to schedule work.
+ base::Lock message_loop_lock_;
+
+ // Points to the message loop that owns |this|.
+ MessageLoop* message_loop_;
+
+ // Synchronizes access to all members below this line.
base::Lock incoming_queue_lock_;
- // Lock that protects |message_loop_| to prevent it from being deleted while a
- // task is being posted.
- base::subtle::ReadWriteLock message_loop_lock_;
+ // Number of tasks that require high resolution timing. This value is kept
+ // so that ReloadWorkQueue() completes in constant time.
+ int high_res_task_count_ = 0;
// An incoming queue of tasks that are acquired under a mutex for processing
// on this instance's thread. These tasks have not yet been been pushed to
// |message_loop_|.
TaskQueue incoming_queue_;
- // Points to the message loop that owns |this|.
- MessageLoop* message_loop_;
+ // True if new tasks should be accepted.
+ bool accept_new_tasks_ = true;
// The next sequence number to use for delayed tasks.
- int next_sequence_num_;
+ int next_sequence_num_ = 0;
// True if our message loop has already been scheduled and does not need to be
// scheduled again until an empty reload occurs.
- bool message_loop_scheduled_;
-
- // True if we always need to call ScheduleWork when receiving a new task, even
- // if the incoming queue was not empty.
- const bool always_schedule_work_;
+ bool message_loop_scheduled_ = false;
// False until StartScheduling() is called.
- bool is_ready_for_scheduling_;
+ bool is_ready_for_scheduling_ = false;
DISALLOW_COPY_AND_ASSIGN(IncomingTaskQueue);
};
diff --git a/chromium/base/message_loop/message_loop.cc b/chromium/base/message_loop/message_loop.cc
index 67b9e225617..f60fd180641 100644
--- a/chromium/base/message_loop/message_loop.cc
+++ b/chromium/base/message_loop/message_loop.cc
@@ -118,16 +118,15 @@ MessageLoop::~MessageLoop() {
// tasks. Normally, we should only pass through this loop once or twice. If
// we end up hitting the loop limit, then it is probably due to one task that
// is being stubborn. Inspect the queues to see who is left.
- bool did_work;
+ bool tasks_remain;
for (int i = 0; i < 100; ++i) {
DeletePendingTasks();
- ReloadWorkQueue();
// If we end up with empty queues, then break out of the loop.
- did_work = DeletePendingTasks();
- if (!did_work)
+ tasks_remain = incoming_task_queue_->triage_tasks().HasTasks();
+ if (!tasks_remain)
break;
}
- DCHECK(!did_work);
+ DCHECK(!tasks_remain);
// Let interested parties have one last shot at accessing this.
for (auto& observer : destruction_observers_)
@@ -239,11 +238,11 @@ void MessageLoop::SetNestableTasksAllowed(bool allowed) {
// loop that does not go through RunLoop::Run().
pump_->ScheduleWork();
}
- nestable_tasks_allowed_ = allowed;
+ task_execution_allowed_ = allowed;
}
bool MessageLoop::NestableTasksAllowed() const {
- return nestable_tasks_allowed_ || run_loop_client_->ProcessingTasksAllowed();
+ return task_execution_allowed_;
}
// TODO(gab): Migrate TaskObservers to RunLoop as part of separating concerns
@@ -278,18 +277,11 @@ std::unique_ptr<MessageLoop> MessageLoop::CreateUnbound(
MessageLoop::MessageLoop(Type type, MessagePumpFactoryCallback pump_factory)
: type_(type),
-#if defined(OS_WIN)
- pending_high_res_tasks_(0),
- in_high_res_mode_(false),
-#endif
- nestable_tasks_allowed_(true),
pump_factory_(std::move(pump_factory)),
- current_pending_task_(nullptr),
incoming_task_queue_(new internal::IncomingTaskQueue(this)),
unbound_task_runner_(
new internal::MessageLoopTaskRunner(incoming_task_queue_)),
- task_runner_(unbound_task_runner_),
- thread_id_(kInvalidThreadId) {
+ task_runner_(unbound_task_runner_) {
// If type is TYPE_CUSTOM non-null pump_factory must be given.
DCHECK(type_ != TYPE_CUSTOM || !pump_factory_.is_null());
}
@@ -341,9 +333,17 @@ void MessageLoop::ClearTaskRunnerForTesting() {
thread_task_runner_handle_.reset();
}
-void MessageLoop::Run() {
+void MessageLoop::Run(bool application_tasks_allowed) {
DCHECK_EQ(this, current());
- pump_->Run(this);
+ if (application_tasks_allowed && !task_execution_allowed_) {
+ // Allow nested task execution as explicitly requested.
+ DCHECK(run_loop_client_->IsNested());
+ task_execution_allowed_ = true;
+ pump_->Run(this);
+ task_execution_allowed_ = false;
+ } else {
+ pump_->Run(this);
+ }
}
void MessageLoop::Quit() {
@@ -353,8 +353,7 @@ void MessageLoop::Quit() {
void MessageLoop::EnsureWorkScheduled() {
DCHECK_EQ(this, current());
- ReloadWorkQueue();
- if (!work_queue_.empty())
+ if (incoming_task_queue_->triage_tasks().HasTasks())
pump_->ScheduleWork();
}
@@ -370,50 +369,40 @@ bool MessageLoop::ProcessNextDelayedNonNestableTask() {
if (run_loop_client_->IsNested())
return false;
- while (!deferred_non_nestable_work_queue_.empty()) {
- PendingTask pending_task =
- std::move(deferred_non_nestable_work_queue_.front());
- deferred_non_nestable_work_queue_.pop();
-
+ while (incoming_task_queue_->deferred_tasks().HasTasks()) {
+ PendingTask pending_task = incoming_task_queue_->deferred_tasks().Pop();
if (!pending_task.task.IsCancelled()) {
RunTask(&pending_task);
return true;
}
-
-#if defined(OS_WIN)
- DecrementHighResTaskCountIfNeeded(pending_task);
-#endif
}
return false;
}
void MessageLoop::RunTask(PendingTask* pending_task) {
- DCHECK(NestableTasksAllowed());
+ DCHECK(task_execution_allowed_);
current_pending_task_ = pending_task;
-#if defined(OS_WIN)
- DecrementHighResTaskCountIfNeeded(*pending_task);
-#endif
-
// Execute the task and assume the worst: It is probably not reentrant.
- nestable_tasks_allowed_ = false;
+ task_execution_allowed_ = false;
TRACE_TASK_EXECUTION("MessageLoop::RunTask", *pending_task);
for (auto& observer : task_observers_)
observer.WillProcessTask(*pending_task);
- task_annotator_.RunTask("MessageLoop::PostTask", pending_task);
+ incoming_task_queue_->RunTask(pending_task);
for (auto& observer : task_observers_)
observer.DidProcessTask(*pending_task);
- nestable_tasks_allowed_ = true;
+ task_execution_allowed_ = true;
current_pending_task_ = nullptr;
}
bool MessageLoop::DeferOrRunPendingTask(PendingTask pending_task) {
- if (pending_task.nestable || !run_loop_client_->IsNested()) {
+ if (pending_task.nestable == Nestable::kNestable ||
+ !run_loop_client_->IsNested()) {
RunTask(&pending_task);
// Show that we ran a task (Note: a new one might arrive as a
// consequence!).
@@ -422,71 +411,17 @@ bool MessageLoop::DeferOrRunPendingTask(PendingTask pending_task) {
// We couldn't run the task now because we're in a nested run loop
// and the task isn't nestable.
- deferred_non_nestable_work_queue_.push(std::move(pending_task));
- return false;
-}
-
-void MessageLoop::AddToDelayedWorkQueue(PendingTask pending_task) {
- // Move to the delayed work queue.
- delayed_work_queue_.push(std::move(pending_task));
-}
-
-bool MessageLoop::SweepDelayedWorkQueueAndReturnTrueIfStillHasWork() {
- while (!delayed_work_queue_.empty()) {
- const PendingTask& pending_task = delayed_work_queue_.top();
- if (!pending_task.task.IsCancelled())
- return true;
-
-#if defined(OS_WIN)
- DecrementHighResTaskCountIfNeeded(pending_task);
-#endif
- delayed_work_queue_.pop();
- }
+ incoming_task_queue_->deferred_tasks().Push(std::move(pending_task));
return false;
}
-bool MessageLoop::DeletePendingTasks() {
- bool did_work = !work_queue_.empty();
- while (!work_queue_.empty()) {
- PendingTask pending_task = std::move(work_queue_.front());
- work_queue_.pop();
- if (!pending_task.delayed_run_time.is_null()) {
- // We want to delete delayed tasks in the same order in which they would
- // normally be deleted in case of any funny dependencies between delayed
- // tasks.
- AddToDelayedWorkQueue(std::move(pending_task));
- }
- }
- did_work |= !deferred_non_nestable_work_queue_.empty();
- while (!deferred_non_nestable_work_queue_.empty()) {
- deferred_non_nestable_work_queue_.pop();
- }
- did_work |= !delayed_work_queue_.empty();
-
- // Historically, we always delete the task regardless of valgrind status. It's
- // not completely clear why we want to leak them in the loops above. This
- // code is replicating legacy behavior, and should not be considered
- // absolutely "correct" behavior. See TODO above about deleting all tasks
- // when it's safe.
- while (!delayed_work_queue_.empty()) {
- delayed_work_queue_.pop();
- }
- return did_work;
-}
-
-void MessageLoop::ReloadWorkQueue() {
- // We can improve performance of our loading tasks from the incoming queue to
- // |*work_queue| by waiting until the last minute (|*work_queue| is empty) to
- // load. That reduces the number of locks-per-task significantly when our
- // queues get large.
- if (work_queue_.empty()) {
-#if defined(OS_WIN)
- pending_high_res_tasks_ +=
- incoming_task_queue_->ReloadWorkQueue(&work_queue_);
-#else
- incoming_task_queue_->ReloadWorkQueue(&work_queue_);
-#endif
- }
+void MessageLoop::DeletePendingTasks() {
+ incoming_task_queue_->triage_tasks().Clear();
+ incoming_task_queue_->deferred_tasks().Clear();
+ // TODO(robliao): Determine if we can move delayed task destruction before
+ // deferred tasks to maintain the MessagePump DoWork, DoDelayedWork, and
+ // DoIdleWork processing order.
+ incoming_task_queue_->delayed_tasks().Clear();
}
void MessageLoop::ScheduleWork() {
@@ -494,37 +429,27 @@ void MessageLoop::ScheduleWork() {
}
bool MessageLoop::DoWork() {
- if (!NestableTasksAllowed()) {
- // Task can't be executed right now.
+ if (!task_execution_allowed_)
return false;
- }
-
- for (;;) {
- ReloadWorkQueue();
- if (work_queue_.empty())
- break;
- // Execute oldest task.
- do {
- PendingTask pending_task = std::move(work_queue_.front());
- work_queue_.pop();
+ // Execute oldest task.
+ while (incoming_task_queue_->triage_tasks().HasTasks()) {
+ PendingTask pending_task = incoming_task_queue_->triage_tasks().Pop();
+ if (pending_task.task.IsCancelled())
+ continue;
- if (pending_task.task.IsCancelled()) {
-#if defined(OS_WIN)
- DecrementHighResTaskCountIfNeeded(pending_task);
-#endif
- } else if (!pending_task.delayed_run_time.is_null()) {
- int sequence_num = pending_task.sequence_num;
- TimeTicks delayed_run_time = pending_task.delayed_run_time;
- AddToDelayedWorkQueue(std::move(pending_task));
- // If we changed the topmost task, then it is time to reschedule.
- if (delayed_work_queue_.top().sequence_num == sequence_num)
- pump_->ScheduleDelayedWork(delayed_run_time);
- } else {
- if (DeferOrRunPendingTask(std::move(pending_task)))
- return true;
+ if (!pending_task.delayed_run_time.is_null()) {
+ int sequence_num = pending_task.sequence_num;
+ TimeTicks delayed_run_time = pending_task.delayed_run_time;
+ incoming_task_queue_->delayed_tasks().Push(std::move(pending_task));
+ // If we changed the topmost task, then it is time to reschedule.
+ if (incoming_task_queue_->delayed_tasks().Peek().sequence_num ==
+ sequence_num) {
+ pump_->ScheduleDelayedWork(delayed_run_time);
}
- } while (!work_queue_.empty());
+ } else if (DeferOrRunPendingTask(std::move(pending_task))) {
+ return true;
+ }
}
// Nothing happened.
@@ -532,8 +457,8 @@ bool MessageLoop::DoWork() {
}
bool MessageLoop::DoDelayedWork(TimeTicks* next_delayed_work_time) {
- if (!NestableTasksAllowed() ||
- !SweepDelayedWorkQueueAndReturnTrueIfStillHasWork()) {
+ if (!task_execution_allowed_ ||
+ !incoming_task_queue_->delayed_tasks().HasTasks()) {
recent_time_ = *next_delayed_work_time = TimeTicks();
return false;
}
@@ -545,7 +470,8 @@ bool MessageLoop::DoDelayedWork(TimeTicks* next_delayed_work_time) {
// fall behind (and have a lot of ready-to-run delayed tasks), the more
// efficient we'll be at handling the tasks.
- TimeTicks next_run_time = delayed_work_queue_.top().delayed_run_time;
+ TimeTicks next_run_time =
+ incoming_task_queue_->delayed_tasks().Peek().delayed_run_time;
if (next_run_time > recent_time_) {
recent_time_ = TimeTicks::Now(); // Get a better view of Now();
if (next_run_time > recent_time_) {
@@ -554,12 +480,12 @@ bool MessageLoop::DoDelayedWork(TimeTicks* next_delayed_work_time) {
}
}
- PendingTask pending_task =
- std::move(const_cast<PendingTask&>(delayed_work_queue_.top()));
- delayed_work_queue_.pop();
+ PendingTask pending_task = incoming_task_queue_->delayed_tasks().Pop();
- if (SweepDelayedWorkQueueAndReturnTrueIfStillHasWork())
- *next_delayed_work_time = delayed_work_queue_.top().delayed_run_time;
+ if (incoming_task_queue_->delayed_tasks().HasTasks()) {
+ *next_delayed_work_time =
+ incoming_task_queue_->delayed_tasks().Peek().delayed_run_time;
+ }
return DeferOrRunPendingTask(std::move(pending_task));
}
@@ -577,7 +503,7 @@ bool MessageLoop::DoIdleWork() {
// _if_ triggered by the timer happens with good resolution. If we don't
// do this the default resolution is 15ms which might not be acceptable
// for some tasks.
- bool high_res = pending_high_res_tasks_ > 0;
+ bool high_res = incoming_task_queue_->HasPendingHighResolutionTasks();
if (high_res != in_high_res_mode_) {
in_high_res_mode_ = high_res;
Time::ActivateHighResolutionTimer(in_high_res_mode_);
@@ -586,16 +512,6 @@ bool MessageLoop::DoIdleWork() {
return false;
}
-#if defined(OS_WIN)
-void MessageLoop::DecrementHighResTaskCountIfNeeded(
- const PendingTask& pending_task) {
- if (!pending_task.is_high_res)
- return;
- --pending_high_res_tasks_;
- DCHECK_GE(pending_high_res_tasks_, 0);
-}
-#endif
-
#if !defined(OS_NACL)
//------------------------------------------------------------------------------
// MessageLoopForUI
@@ -683,13 +599,13 @@ bool MessageLoopForIO::WatchFileDescriptor(int fd,
#if defined(OS_FUCHSIA)
// Additional watch API for native platform resources.
-bool MessageLoopForIO::WatchMxHandle(mx_handle_t handle,
+bool MessageLoopForIO::WatchZxHandle(zx_handle_t handle,
bool persistent,
- mx_signals_t signals,
- MxHandleWatchController* controller,
- MxHandleWatcher* delegate) {
+ zx_signals_t signals,
+ ZxHandleWatchController* controller,
+ ZxHandleWatcher* delegate) {
return ToPumpIO(pump_.get())
- ->WatchMxHandle(handle, persistent, signals, controller, delegate);
+ ->WatchZxHandle(handle, persistent, signals, controller, delegate);
}
#endif
diff --git a/chromium/base/message_loop/message_loop.h b/chromium/base/message_loop/message_loop.h
index a118917ea1c..46171f803a0 100644
--- a/chromium/base/message_loop/message_loop.h
+++ b/chromium/base/message_loop/message_loop.h
@@ -11,7 +11,6 @@
#include "base/base_export.h"
#include "base/callback_forward.h"
-#include "base/debug/task_annotator.h"
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
@@ -234,7 +233,7 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate,
// Enables nestable tasks on |loop| while in scope.
// DEPRECATED: This should not be used when the nested loop is driven by
- // RunLoop (use RunLoop::Type::KNestableTasksAllowed instead). It can however
+ // RunLoop (use RunLoop::Type::kNestableTasksAllowed instead). It can however
// still be useful in a few scenarios where re-entrancy is caused by a native
// message loop.
// TODO(gab): Remove usage of this class alongside RunLoop and rename it to
@@ -251,8 +250,8 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate,
}
private:
- MessageLoop* loop_;
- bool old_state_;
+ MessageLoop* const loop_;
+ const bool old_state_;
};
// A TaskObserver is an object that receives task notifications from the
@@ -281,10 +280,6 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate,
// Returns true if the message loop is "idle". Provided for testing.
bool IsIdleForTesting();
- // Returns the TaskAnnotator which is used to add debug information to posted
- // tasks.
- debug::TaskAnnotator* task_annotator() { return &task_annotator_; }
-
// Runs the specified PendingTask.
void RunTask(PendingTask* pending_task);
@@ -338,7 +333,7 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate,
void SetThreadTaskRunnerHandle();
// RunLoop::Delegate:
- void Run() override;
+ void Run(bool application_tasks_allowed) override;
void Quit() override;
void EnsureWorkScheduled() override;
@@ -349,21 +344,9 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate,
// cannot be run right now. Returns true if the task was run.
bool DeferOrRunPendingTask(PendingTask pending_task);
- // Adds the pending task to delayed_work_queue_.
- void AddToDelayedWorkQueue(PendingTask pending_task);
-
- // Sweeps any cancelled tasks from the front of the delayed work queue and
- // returns true if there is remaining work.
- bool SweepDelayedWorkQueueAndReturnTrueIfStillHasWork();
-
// Delete tasks that haven't run yet without running them. Used in the
- // destructor to make sure all the task's destructors get called. Returns
- // true if some work was done.
- bool DeletePendingTasks();
-
- // Loads tasks from the incoming queue to |work_queue_| if the latter is
- // empty.
- void ReloadWorkQueue();
+ // destructor to make sure all the task's destructors get called.
+ void DeletePendingTasks();
// Wakes up the message pump. Can be called on any thread. The caller is
// responsible for synchronizing ScheduleWork() calls.
@@ -376,42 +359,26 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate,
const Type type_;
- // A list of tasks that need to be processed by this instance. Note that
- // this queue is only accessed (push/pop) by our current thread.
- TaskQueue work_queue_;
-
#if defined(OS_WIN)
- // Helper to decrement the high resolution task count if |pending_task| is a
- // high resolution task.
- void DecrementHighResTaskCountIfNeeded(const PendingTask& pending_Task);
-
- // How many high resolution tasks are in the pending task queue. This value
- // increases by N every time we call ReloadWorkQueue() and decreases by 1
- // every time we call RunTask() if the task needs a high resolution timer.
- int pending_high_res_tasks_;
// Tracks if we have requested high resolution timers. Its only use is to
// turn off the high resolution timer upon loop destruction.
- bool in_high_res_mode_;
+ bool in_high_res_mode_ = false;
#endif
- // Contains delayed tasks, sorted by their 'delayed_run_time' property.
- DelayedTaskQueue delayed_work_queue_;
-
// A recent snapshot of Time::Now(), used to check delayed_work_queue_.
TimeTicks recent_time_;
- // A queue of non-nestable tasks that we had to defer because when it came
- // time to execute them we were in a nested run loop. They will execute
- // once we're out of nested run loops.
- TaskQueue deferred_non_nestable_work_queue_;
-
ObserverList<DestructionObserver> destruction_observers_;
- // A recursion block that prevents accidentally running additional tasks when
- // insider a (accidentally induced?) nested message pump. Deprecated in favor
- // of run_loop_client_->ProcessingTasksAllowed(), equivalent until then (both
- // need to be checked in conditionals).
- bool nestable_tasks_allowed_;
+ // A boolean which prevents unintentional reentrant task execution (e.g. from
+ // induced nested message loops). As such, nested message loops will only
+ // process system messages (not application tasks) by default. A nested loop
+ // layer must have been explicitly granted permission to be able to execute
+ // application tasks. This is granted either by
+ // RunLoop::Type::kNestableTasksAllowed when the loop is driven by the
+ // application or by a ScopedNestableTaskAllower preceding a system call that
+ // is known to generate a system-driven nested loop.
+ bool task_execution_allowed_ = true;
// pump_factory_.Run() is called to create a message pump for this loop
// if type_ is TYPE_CUSTOM and pump_ is null.
@@ -419,14 +386,12 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate,
ObserverList<TaskObserver> task_observers_;
- debug::TaskAnnotator task_annotator_;
-
// Used to allow creating a breadcrumb of program counters in PostTask.
// This variable is only initialized while a task is being executed and is
// meant only to store context for creating a backtrace breadcrumb. Do not
// attach other semantics to it without thinking through the use caes
// thoroughly.
- const PendingTask* current_pending_task_;
+ const PendingTask* current_pending_task_ = nullptr;
scoped_refptr<internal::IncomingTaskQueue> incoming_task_queue_;
@@ -439,7 +404,7 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate,
// Id of the thread this message loop is bound to. Initialized once when the
// MessageLoop is bound to its thread and constant forever after.
- PlatformThreadId thread_id_;
+ PlatformThreadId thread_id_ = kInvalidThreadId;
// Whether task observers are allowed.
bool allow_task_observers_ = true;
@@ -564,8 +529,8 @@ class BASE_EXPORT MessageLoopForIO : public MessageLoop {
WATCH_WRITE = MessagePumpFuchsia::WATCH_WRITE,
WATCH_READ_WRITE = MessagePumpFuchsia::WATCH_READ_WRITE};
- typedef MessagePumpFuchsia::MxHandleWatchController MxHandleWatchController;
- typedef MessagePumpFuchsia::MxHandleWatcher MxHandleWatcher;
+ typedef MessagePumpFuchsia::ZxHandleWatchController ZxHandleWatchController;
+ typedef MessagePumpFuchsia::ZxHandleWatcher ZxHandleWatcher;
#elif defined(OS_IOS)
typedef MessagePumpIOSForIO::Watcher Watcher;
typedef MessagePumpIOSForIO::FileDescriptorWatcher
@@ -604,11 +569,11 @@ class BASE_EXPORT MessageLoopForIO : public MessageLoop {
#if defined(OS_FUCHSIA)
// Additional watch API for native platform resources.
- bool WatchMxHandle(mx_handle_t handle,
+ bool WatchZxHandle(zx_handle_t handle,
bool persistent,
- mx_signals_t signals,
- MxHandleWatchController* controller,
- MxHandleWatcher* delegate);
+ zx_signals_t signals,
+ ZxHandleWatchController* controller,
+ ZxHandleWatcher* delegate);
#endif
};
diff --git a/chromium/base/message_loop/message_loop_io_posix_unittest.cc b/chromium/base/message_loop/message_loop_io_posix_unittest.cc
index 6e6a92a6bef..5038bea7f8a 100644
--- a/chromium/base/message_loop/message_loop_io_posix_unittest.cc
+++ b/chromium/base/message_loop/message_loop_io_posix_unittest.cc
@@ -225,6 +225,27 @@ TEST_F(MessageLoopForIoPosixTest, WatchWritable) {
ASSERT_TRUE(handler.is_writable_);
}
+// Verify that RunUntilIdle() receives IO notifications.
+TEST_F(MessageLoopForIoPosixTest, RunUntilIdle) {
+ MessageLoopForIO message_loop;
+ MessageLoopForIO::FileDescriptorWatcher watcher(FROM_HERE);
+ TestHandler handler;
+
+ // Watch the pipe for readability.
+ ASSERT_TRUE(MessageLoopForIO::current()->WatchFileDescriptor(
+ read_fd_.get(), /*persistent=*/false, MessageLoopForIO::WATCH_READ,
+ &watcher, &handler));
+
+ // The pipe should not be readable when first created.
+ RunLoop().RunUntilIdle();
+ ASSERT_FALSE(handler.is_readable_);
+
+ TriggerReadEvent();
+
+ while (!handler.is_readable_)
+ RunLoop().RunUntilIdle();
+}
+
void StopWatching(MessageLoopForIO::FileDescriptorWatcher* controller,
RunLoop* run_loop) {
controller->StopWatchingFileDescriptor();
diff --git a/chromium/base/message_loop/message_loop_perftest.cc b/chromium/base/message_loop/message_loop_perftest.cc
new file mode 100644
index 00000000000..867e8fe850d
--- /dev/null
+++ b/chromium/base/message_loop/message_loop_perftest.cc
@@ -0,0 +1,254 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+#include <vector>
+
+#include "base/atomicops.h"
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
+#include "base/synchronization/atomic_flag.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/time/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/perf/perf_test.h"
+
+namespace base {
+
+namespace {
+
+// A thread that waits for the caller to signal an event before proceeding to
+// call Action::Run().
+class PostingThread {
+ public:
+ class Action {
+ public:
+ virtual ~Action() = default;
+
+ // Called after the thread is started and |start_event_| is signalled.
+ virtual void Run() = 0;
+
+ protected:
+ Action() = default;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Action);
+ };
+
+ // Creates a PostingThread where the thread waits on |start_event| before
+ // calling action->Run(). If a thread is returned, the thread is guaranteed to
+ // be allocated and running and the caller must call Join() before destroying
+ // the PostingThread.
+ static std::unique_ptr<PostingThread> Create(WaitableEvent* start_event,
+ std::unique_ptr<Action> action) {
+ auto posting_thread =
+ WrapUnique(new PostingThread(start_event, std::move(action)));
+
+ if (!posting_thread->Start())
+ return nullptr;
+
+ return posting_thread;
+ }
+
+ ~PostingThread() { DCHECK_EQ(!thread_handle_.is_null(), join_called_); }
+
+ void Join() {
+ PlatformThread::Join(thread_handle_);
+ join_called_ = true;
+ }
+
+ private:
+ class Delegate final : public PlatformThread::Delegate {
+ public:
+ Delegate(PostingThread* outer, std::unique_ptr<Action> action)
+ : outer_(outer), action_(std::move(action)) {
+ DCHECK(outer_);
+ DCHECK(action_);
+ }
+
+ ~Delegate() override = default;
+
+ private:
+ void ThreadMain() override {
+ outer_->thread_started_.Signal();
+ outer_->start_event_->Wait();
+ action_->Run();
+ }
+
+ PostingThread* const outer_;
+ const std::unique_ptr<Action> action_;
+
+ DISALLOW_COPY_AND_ASSIGN(Delegate);
+ };
+
+ PostingThread(WaitableEvent* start_event, std::unique_ptr<Action> delegate)
+ : start_event_(start_event),
+ thread_started_(WaitableEvent::ResetPolicy::MANUAL,
+ WaitableEvent::InitialState::NOT_SIGNALED),
+ delegate_(this, std::move(delegate)) {
+ DCHECK(start_event_);
+ }
+
+ bool Start() {
+ bool thread_created =
+ PlatformThread::Create(0, &delegate_, &thread_handle_);
+ if (thread_created)
+ thread_started_.Wait();
+
+ return thread_created;
+ }
+
+ bool join_called_ = false;
+ WaitableEvent* const start_event_;
+ WaitableEvent thread_started_;
+ Delegate delegate_;
+
+ PlatformThreadHandle thread_handle_;
+
+ DISALLOW_COPY_AND_ASSIGN(PostingThread);
+};
+
+class MessageLoopPerfTest : public ::testing::TestWithParam<int> {
+ public:
+ MessageLoopPerfTest()
+ : message_loop_task_runner_(SequencedTaskRunnerHandle::Get()),
+ run_posting_threads_(WaitableEvent::ResetPolicy::MANUAL,
+ WaitableEvent::InitialState::NOT_SIGNALED) {}
+
+ static std::string ParamInfoToString(
+ ::testing::TestParamInfo<int> param_info) {
+ return PostingThreadCountToString(param_info.param);
+ }
+
+ static std::string PostingThreadCountToString(int posting_threads) {
+ // Special case 1 thread for thread vs threads.
+ if (posting_threads == 1)
+ return "1_Posting_Thread";
+
+ return StringPrintf("%d_Posting_Threads", posting_threads);
+ }
+
+ protected:
+ class ContinuouslyPostTasks final : public PostingThread::Action {
+ public:
+ ContinuouslyPostTasks(MessageLoopPerfTest* outer) : outer_(outer) {
+ DCHECK(outer_);
+ }
+ ~ContinuouslyPostTasks() override = default;
+
+ private:
+ void Run() override {
+ RepeatingClosure task_to_run =
+ BindRepeating([](size_t* num_tasks_run) { ++*num_tasks_run; },
+ &outer_->num_tasks_run_);
+ while (!outer_->stop_posting_threads_.IsSet()) {
+ outer_->message_loop_task_runner_->PostTask(FROM_HERE, task_to_run);
+ subtle::NoBarrier_AtomicIncrement(&outer_->num_tasks_posted_, 1);
+ }
+ }
+
+ MessageLoopPerfTest* const outer_;
+
+ DISALLOW_COPY_AND_ASSIGN(ContinuouslyPostTasks);
+ };
+
+ void SetUp() override {
+ // This check is here because we can't ASSERT_TRUE in the constructor.
+ ASSERT_TRUE(message_loop_task_runner_);
+ }
+
+ // Runs ActionType::Run() on |num_posting_threads| and requests test
+ // termination around |duration|.
+ template <typename ActionType>
+ void RunTest(const int num_posting_threads, TimeDelta duration) {
+ std::vector<std::unique_ptr<PostingThread>> threads;
+ for (int i = 0; i < num_posting_threads; ++i) {
+ threads.emplace_back(PostingThread::Create(
+ &run_posting_threads_, std::make_unique<ActionType>(this)));
+ // Don't assert here to simplify the code that requires a Join() call for
+ // every created PostingThread.
+ EXPECT_TRUE(threads[i]);
+ }
+
+ RunLoop run_loop;
+ message_loop_task_runner_->PostDelayedTask(
+ FROM_HERE,
+ BindOnce(
+ [](RunLoop* run_loop, AtomicFlag* stop_posting_threads) {
+ stop_posting_threads->Set();
+ run_loop->Quit();
+ },
+ &run_loop, &stop_posting_threads_),
+ duration);
+
+ TimeTicks post_task_start = TimeTicks::Now();
+ run_posting_threads_.Signal();
+
+ TimeTicks run_loop_start = TimeTicks::Now();
+ run_loop.Run();
+ tasks_run_duration_ = TimeTicks::Now() - run_loop_start;
+
+ for (auto& thread : threads)
+ thread->Join();
+
+ tasks_posted_duration_ = TimeTicks::Now() - post_task_start;
+ }
+
+ size_t num_tasks_posted() const {
+ return subtle::NoBarrier_Load(&num_tasks_posted_);
+ }
+
+ TimeDelta tasks_posted_duration() const { return tasks_posted_duration_; }
+
+ size_t num_tasks_run() const { return num_tasks_run_; }
+
+ TimeDelta tasks_run_duration() const { return tasks_run_duration_; }
+
+ private:
+ MessageLoop message_loop_;
+
+ // Accessed on multiple threads, thread-safe or constant:
+ const scoped_refptr<SequencedTaskRunner> message_loop_task_runner_;
+ WaitableEvent run_posting_threads_;
+ AtomicFlag stop_posting_threads_;
+ subtle::AtomicWord num_tasks_posted_ = 0;
+
+ // Accessed only on the test case thread:
+ TimeDelta tasks_posted_duration_;
+ TimeDelta tasks_run_duration_;
+ size_t num_tasks_run_ = 0;
+
+ DISALLOW_COPY_AND_ASSIGN(MessageLoopPerfTest);
+};
+
+} // namespace
+
+TEST_P(MessageLoopPerfTest, PostTaskRate) {
+ // Measures the average rate of posting tasks from different threads and the
+ // average rate that the message loop is running those tasks.
+ RunTest<ContinuouslyPostTasks>(GetParam(), TimeDelta::FromSeconds(3));
+ perf_test::PrintResult("task_posting", "",
+ PostingThreadCountToString(GetParam()),
+ tasks_posted_duration().InMicroseconds() /
+ static_cast<double>(num_tasks_posted()),
+ "us/task", true);
+ perf_test::PrintResult("task_running", "",
+ PostingThreadCountToString(GetParam()),
+ tasks_run_duration().InMicroseconds() /
+ static_cast<double>(num_tasks_run()),
+ "us/task", true);
+}
+
+INSTANTIATE_TEST_CASE_P(,
+ MessageLoopPerfTest,
+ ::testing::Values(1, 5, 10),
+ MessageLoopPerfTest::ParamInfoToString);
+} // namespace base
diff --git a/chromium/base/message_loop/message_loop_task_runner.cc b/chromium/base/message_loop/message_loop_task_runner.cc
index c7ac583e6f8..5d41986d995 100644
--- a/chromium/base/message_loop/message_loop_task_runner.cc
+++ b/chromium/base/message_loop/message_loop_task_runner.cc
@@ -24,22 +24,21 @@ void MessageLoopTaskRunner::BindToCurrentThread() {
valid_thread_id_ = PlatformThread::CurrentId();
}
-bool MessageLoopTaskRunner::PostDelayedTask(
- const tracked_objects::Location& from_here,
- OnceClosure task,
- base::TimeDelta delay) {
+bool MessageLoopTaskRunner::PostDelayedTask(const Location& from_here,
+ OnceClosure task,
+ base::TimeDelta delay) {
DCHECK(!task.is_null()) << from_here.ToString();
return incoming_queue_->AddToIncomingQueue(from_here, std::move(task), delay,
- true);
+ Nestable::kNestable);
}
bool MessageLoopTaskRunner::PostNonNestableDelayedTask(
- const tracked_objects::Location& from_here,
+ const Location& from_here,
OnceClosure task,
base::TimeDelta delay) {
DCHECK(!task.is_null()) << from_here.ToString();
return incoming_queue_->AddToIncomingQueue(from_here, std::move(task), delay,
- false);
+ Nestable::kNonNestable);
}
bool MessageLoopTaskRunner::RunsTasksInCurrentSequence() const {
diff --git a/chromium/base/message_loop/message_loop_task_runner.h b/chromium/base/message_loop/message_loop_task_runner.h
index f3ec51f8638..c7d48c28c15 100644
--- a/chromium/base/message_loop/message_loop_task_runner.h
+++ b/chromium/base/message_loop/message_loop_task_runner.h
@@ -31,12 +31,12 @@ class BASE_EXPORT MessageLoopTaskRunner : public SingleThreadTaskRunner {
void BindToCurrentThread();
// SingleThreadTaskRunner implementation
- bool PostDelayedTask(const tracked_objects::Location& from_here,
+ bool PostDelayedTask(const Location& from_here,
OnceClosure task,
- base::TimeDelta delay) override;
- bool PostNonNestableDelayedTask(const tracked_objects::Location& from_here,
+ TimeDelta delay) override;
+ bool PostNonNestableDelayedTask(const Location& from_here,
OnceClosure task,
- base::TimeDelta delay) override;
+ TimeDelta delay) override;
bool RunsTasksInCurrentSequence() const override;
private:
diff --git a/chromium/base/message_loop/message_loop_unittest.cc b/chromium/base/message_loop/message_loop_unittest.cc
index c01f7e2b800..25718c62050 100644
--- a/chromium/base/message_loop/message_loop_unittest.cc
+++ b/chromium/base/message_loop/message_loop_unittest.cc
@@ -1564,6 +1564,97 @@ TEST_P(MessageLoopTypedTest, RecursivePosts) {
RunLoop().Run();
}
+TEST_P(MessageLoopTypedTest, NestableTasksAllowedAtTopLevel) {
+ MessageLoop loop(GetParam());
+ EXPECT_TRUE(MessageLoop::current()->NestableTasksAllowed());
+}
+
+// Nestable tasks shouldn't be allowed to run reentrantly by default (regression
+// test for https://crbug.com/754112).
+TEST_P(MessageLoopTypedTest, NestableTasksDisallowedByDefault) {
+ MessageLoop loop(GetParam());
+ RunLoop run_loop;
+ loop.task_runner()->PostTask(
+ FROM_HERE,
+ BindOnce(
+ [](RunLoop* run_loop) {
+ EXPECT_FALSE(MessageLoop::current()->NestableTasksAllowed());
+ run_loop->Quit();
+ },
+ Unretained(&run_loop)));
+ run_loop.Run();
+}
+
+TEST_P(MessageLoopTypedTest, NestableTasksProcessedWhenRunLoopAllows) {
+ MessageLoop loop(GetParam());
+ RunLoop run_loop;
+ loop.task_runner()->PostTask(
+ FROM_HERE,
+ BindOnce(
+ [](RunLoop* run_loop) {
+ // This test would hang if this RunLoop wasn't of type
+ // kNestableTasksAllowed (i.e. this is testing that this is
+ // processed and doesn't hang).
+ RunLoop nested_run_loop(RunLoop::Type::kNestableTasksAllowed);
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ BindOnce(
+ [](RunLoop* nested_run_loop) {
+ // Each additional layer of application task nesting
+ // requires its own allowance. The kNestableTasksAllowed
+ // RunLoop allowed this task to be processed but further
+ // nestable tasks are by default disallowed from this
+ // layer.
+ EXPECT_FALSE(
+ MessageLoop::current()->NestableTasksAllowed());
+ nested_run_loop->Quit();
+ },
+ Unretained(&nested_run_loop)));
+ nested_run_loop.Run();
+
+ run_loop->Quit();
+ },
+ Unretained(&run_loop)));
+ run_loop.Run();
+}
+
+TEST_P(MessageLoopTypedTest, NestableTasksAllowedExplicitlyInScope) {
+ MessageLoop loop(GetParam());
+ RunLoop run_loop;
+ loop.task_runner()->PostTask(
+ FROM_HERE,
+ BindOnce(
+ [](RunLoop* run_loop) {
+ {
+ MessageLoop::ScopedNestableTaskAllower allow_nestable_tasks(
+ MessageLoop::current());
+ EXPECT_TRUE(MessageLoop::current()->NestableTasksAllowed());
+ }
+ EXPECT_FALSE(MessageLoop::current()->NestableTasksAllowed());
+ run_loop->Quit();
+ },
+ Unretained(&run_loop)));
+ run_loop.Run();
+}
+
+TEST_P(MessageLoopTypedTest, NestableTasksAllowedManually) {
+ MessageLoop loop(GetParam());
+ RunLoop run_loop;
+ loop.task_runner()->PostTask(
+ FROM_HERE,
+ BindOnce(
+ [](RunLoop* run_loop) {
+ EXPECT_FALSE(MessageLoop::current()->NestableTasksAllowed());
+ MessageLoop::current()->SetNestableTasksAllowed(true);
+ EXPECT_TRUE(MessageLoop::current()->NestableTasksAllowed());
+ MessageLoop::current()->SetNestableTasksAllowed(false);
+ EXPECT_FALSE(MessageLoop::current()->NestableTasksAllowed());
+ run_loop->Quit();
+ },
+ Unretained(&run_loop)));
+ run_loop.Run();
+}
+
INSTANTIATE_TEST_CASE_P(,
MessageLoopTypedTest,
::testing::Values(MessageLoop::TYPE_DEFAULT,
diff --git a/chromium/base/message_loop/message_pump_default.cc b/chromium/base/message_loop/message_pump_default.cc
index 651ebdc8ead..50dbc6f7183 100644
--- a/chromium/base/message_loop/message_pump_default.cc
+++ b/chromium/base/message_loop/message_pump_default.cc
@@ -10,6 +10,10 @@
#include "build/build_config.h"
#if defined(OS_MACOSX)
+#include <mach/thread_policy.h>
+
+#include "base/mac/mach_logging.h"
+#include "base/mac/scoped_mach_port.h"
#include "base/mac/scoped_nsautorelease_pool.h"
#endif
@@ -81,4 +85,19 @@ void MessagePumpDefault::ScheduleDelayedWork(
delayed_work_time_ = delayed_work_time;
}
+#if defined(OS_MACOSX)
+void MessagePumpDefault::SetTimerSlack(TimerSlack timer_slack) {
+ thread_latency_qos_policy_data_t policy{};
+ policy.thread_latency_qos_tier = timer_slack == TIMER_SLACK_MAXIMUM
+ ? LATENCY_QOS_TIER_5
+ : LATENCY_QOS_TIER_UNSPECIFIED;
+ mac::ScopedMachSendRight thread_port(mach_thread_self());
+ kern_return_t kr =
+ thread_policy_set(thread_port.get(), THREAD_LATENCY_QOS_POLICY,
+ reinterpret_cast<thread_policy_t>(&policy),
+ THREAD_LATENCY_QOS_POLICY_COUNT);
+ MACH_DVLOG_IF(1, kr != KERN_SUCCESS, kr) << "thread_policy_set";
+}
+#endif
+
} // namespace base
diff --git a/chromium/base/message_loop/message_pump_default.h b/chromium/base/message_loop/message_pump_default.h
index 4cd7cd17d56..dd11adcb6c0 100644
--- a/chromium/base/message_loop/message_pump_default.h
+++ b/chromium/base/message_loop/message_pump_default.h
@@ -10,6 +10,7 @@
#include "base/message_loop/message_pump.h"
#include "base/synchronization/waitable_event.h"
#include "base/time/time.h"
+#include "build/build_config.h"
namespace base {
@@ -23,6 +24,9 @@ class BASE_EXPORT MessagePumpDefault : public MessagePump {
void Quit() override;
void ScheduleWork() override;
void ScheduleDelayedWork(const TimeTicks& delayed_work_time) override;
+#if defined(OS_MACOSX)
+ void SetTimerSlack(TimerSlack timer_slack) override;
+#endif
private:
// This flag is set to false when Run should return.
diff --git a/chromium/base/message_loop/message_pump_fuchsia.cc b/chromium/base/message_loop/message_pump_fuchsia.cc
index 598bff28dcf..22fb3a1eb0a 100644
--- a/chromium/base/message_loop/message_pump_fuchsia.cc
+++ b/chromium/base/message_loop/message_pump_fuchsia.cc
@@ -4,24 +4,24 @@
#include "base/message_loop/message_pump_fuchsia.h"
-#include <magenta/status.h>
-#include <magenta/syscalls.h>
+#include <zircon/status.h>
+#include <zircon/syscalls.h>
#include "base/auto_reset.h"
#include "base/logging.h"
namespace base {
-MessagePumpFuchsia::MxHandleWatchController::MxHandleWatchController(
- const tracked_objects::Location& from_here)
+MessagePumpFuchsia::ZxHandleWatchController::ZxHandleWatchController(
+ const Location& from_here)
: created_from_location_(from_here) {}
-MessagePumpFuchsia::MxHandleWatchController::~MxHandleWatchController() {
- if (!StopWatchingMxHandle())
+MessagePumpFuchsia::ZxHandleWatchController::~ZxHandleWatchController() {
+ if (!StopWatchingZxHandle())
NOTREACHED();
}
-bool MessagePumpFuchsia::MxHandleWatchController::StopWatchingMxHandle() {
+bool MessagePumpFuchsia::ZxHandleWatchController::StopWatchingZxHandle() {
if (was_stopped_) {
DCHECK(!*was_stopped_);
*was_stopped_ = true;
@@ -42,36 +42,36 @@ bool MessagePumpFuchsia::MxHandleWatchController::StopWatchingMxHandle() {
if (!weak_pump_)
return true;
- int result = mx_port_cancel(weak_pump_->port_.get(), handle_, wait_key());
- DLOG_IF(ERROR, result != MX_OK)
- << "mx_port_cancel(handle=" << handle_
- << ") failed: " << mx_status_get_string(result);
+ int result = zx_port_cancel(weak_pump_->port_.get(), handle_, wait_key());
+ DLOG_IF(ERROR, result != ZX_OK)
+ << "zx_port_cancel(handle=" << handle_
+ << ") failed: " << zx_status_get_string(result);
- return result == MX_OK;
+ return result == ZX_OK;
}
-void MessagePumpFuchsia::FdWatchController::OnMxHandleSignalled(
- mx_handle_t handle,
- mx_signals_t signals) {
+void MessagePumpFuchsia::FdWatchController::OnZxHandleSignalled(
+ zx_handle_t handle,
+ zx_signals_t signals) {
uint32_t events;
- __mxio_wait_end(io_, signals, &events);
+ __fdio_wait_end(io_, signals, &events);
// Each |watcher_| callback we invoke may stop or delete |this|. The pump has
// set |was_stopped_| to point to a safe location on the calling stack, so we
// can use that to detect being stopped mid-callback and avoid doing further
// work that would touch |this|.
bool* was_stopped = was_stopped_;
- if (events & MXIO_EVT_WRITABLE)
+ if (events & FDIO_EVT_WRITABLE)
watcher_->OnFileCanWriteWithoutBlocking(fd_);
- if (!*was_stopped && (events & MXIO_EVT_READABLE))
+ if (!*was_stopped && (events & FDIO_EVT_READABLE))
watcher_->OnFileCanReadWithoutBlocking(fd_);
// Don't add additional work here without checking |*was_stopped_| again.
}
MessagePumpFuchsia::FdWatchController::FdWatchController(
- const tracked_objects::Location& from_here)
- : MxHandleWatchController(from_here) {}
+ const Location& from_here)
+ : ZxHandleWatchController(from_here) {}
MessagePumpFuchsia::FdWatchController::~FdWatchController() {
if (!StopWatchingFileDescriptor())
@@ -79,16 +79,16 @@ MessagePumpFuchsia::FdWatchController::~FdWatchController() {
}
bool MessagePumpFuchsia::FdWatchController::StopWatchingFileDescriptor() {
- bool success = StopWatchingMxHandle();
+ bool success = StopWatchingZxHandle();
if (io_) {
- __mxio_release(io_);
+ __fdio_release(io_);
io_ = nullptr;
}
return success;
}
MessagePumpFuchsia::MessagePumpFuchsia() : weak_factory_(this) {
- CHECK_EQ(MX_OK, mx_port_create(0, port_.receive()));
+ CHECK_EQ(ZX_OK, zx_port_create(0, port_.receive()));
}
bool MessagePumpFuchsia::WatchFileDescriptor(int fd,
@@ -107,7 +107,7 @@ bool MessagePumpFuchsia::WatchFileDescriptor(int fd,
controller->watcher_ = delegate;
DCHECK(!controller->io_);
- controller->io_ = __mxio_fd_to_io(fd);
+ controller->io_ = __fdio_fd_to_io(fd);
if (!controller->io_) {
DLOG(ERROR) << "Failed to get IO for FD";
return false;
@@ -115,23 +115,23 @@ bool MessagePumpFuchsia::WatchFileDescriptor(int fd,
switch (mode) {
case WATCH_READ:
- controller->desired_events_ = MXIO_EVT_READABLE;
+ controller->desired_events_ = FDIO_EVT_READABLE;
break;
case WATCH_WRITE:
- controller->desired_events_ = MXIO_EVT_WRITABLE;
+ controller->desired_events_ = FDIO_EVT_WRITABLE;
break;
case WATCH_READ_WRITE:
- controller->desired_events_ = MXIO_EVT_READABLE | MXIO_EVT_WRITABLE;
+ controller->desired_events_ = FDIO_EVT_READABLE | FDIO_EVT_WRITABLE;
break;
default:
NOTREACHED() << "unexpected mode: " << mode;
return false;
}
- // Pass dummy |handle| and |signals| values to WatchMxHandle(). The real
+ // Pass dummy |handle| and |signals| values to WatchZxHandle(). The real
// values will be populated by FdWatchController::WaitBegin(), before actually
// starting the wait operation.
- return WatchMxHandle(MX_HANDLE_INVALID, persistent, 1, controller,
+ return WatchZxHandle(ZX_HANDLE_INVALID, persistent, 1, controller,
controller);
}
@@ -139,28 +139,28 @@ bool MessagePumpFuchsia::FdWatchController::WaitBegin() {
// Refresh the |handle_| and |desired_signals_| from the mxio for the fd.
// Some types of mxio map read/write events to different signals depending on
// their current state, so we must do this every time we begin to wait.
- __mxio_wait_begin(io_, desired_events_, &handle_, &desired_signals_);
- if (handle_ == MX_HANDLE_INVALID) {
- DLOG(ERROR) << "mxio_wait_begin failed";
+ __fdio_wait_begin(io_, desired_events_, &handle_, &desired_signals_);
+ if (handle_ == ZX_HANDLE_INVALID) {
+ DLOG(ERROR) << "fdio_wait_begin failed";
return false;
}
- return MessagePumpFuchsia::MxHandleWatchController::WaitBegin();
+ return MessagePumpFuchsia::ZxHandleWatchController::WaitBegin();
}
-bool MessagePumpFuchsia::WatchMxHandle(mx_handle_t handle,
+bool MessagePumpFuchsia::WatchZxHandle(zx_handle_t handle,
bool persistent,
- mx_signals_t signals,
- MxHandleWatchController* controller,
- MxHandleWatcher* delegate) {
+ zx_signals_t signals,
+ ZxHandleWatchController* controller,
+ ZxHandleWatcher* delegate) {
DCHECK_NE(0u, signals);
DCHECK(controller);
DCHECK(delegate);
- DCHECK(handle == MX_HANDLE_INVALID ||
- controller->handle_ == MX_HANDLE_INVALID ||
+ DCHECK(handle == ZX_HANDLE_INVALID ||
+ controller->handle_ == ZX_HANDLE_INVALID ||
handle == controller->handle_);
- if (!controller->StopWatchingMxHandle())
+ if (!controller->StopWatchingZxHandle())
NOTREACHED();
controller->handle_ = handle;
@@ -173,15 +173,15 @@ bool MessagePumpFuchsia::WatchMxHandle(mx_handle_t handle,
return controller->WaitBegin();
}
-bool MessagePumpFuchsia::MxHandleWatchController::WaitBegin() {
+bool MessagePumpFuchsia::ZxHandleWatchController::WaitBegin() {
DCHECK(!has_begun_);
- mx_status_t status =
- mx_object_wait_async(handle_, weak_pump_->port_.get(), wait_key(),
- desired_signals_, MX_WAIT_ASYNC_ONCE);
- if (status != MX_OK) {
- DLOG(ERROR) << "mx_object_wait_async failed: "
- << mx_status_get_string(status)
+ zx_status_t status =
+ zx_object_wait_async(handle_, weak_pump_->port_.get(), wait_key(),
+ desired_signals_, ZX_WAIT_ASYNC_ONCE);
+ if (status != ZX_OK) {
+ DLOG(ERROR) << "zx_object_wait_async failed: "
+ << zx_status_get_string(status)
<< " (port=" << weak_pump_->port_.get() << ")";
return false;
}
@@ -191,8 +191,8 @@ bool MessagePumpFuchsia::MxHandleWatchController::WaitBegin() {
return true;
}
-uint32_t MessagePumpFuchsia::MxHandleWatchController::WaitEnd(
- mx_signals_t signals) {
+uint32_t MessagePumpFuchsia::ZxHandleWatchController::WaitEnd(
+ zx_signals_t signals) {
DCHECK(has_begun_);
has_begun_ = false;
@@ -206,6 +206,54 @@ uint32_t MessagePumpFuchsia::MxHandleWatchController::WaitEnd(
return signals;
}
+bool MessagePumpFuchsia::HandleEvents(zx_time_t deadline) {
+ zx_port_packet_t packet;
+ const zx_status_t wait_status =
+ zx_port_wait(port_.get(), deadline, &packet, 0);
+
+ if (wait_status == ZX_ERR_TIMED_OUT)
+ return false;
+
+ if (wait_status != ZX_OK) {
+ NOTREACHED() << "unexpected wait status: "
+ << zx_status_get_string(wait_status);
+ return false;
+ }
+
+ if (packet.type == ZX_PKT_TYPE_SIGNAL_ONE) {
+ // A watched fd caused the wakeup via zx_object_wait_async().
+ DCHECK_EQ(ZX_OK, packet.status);
+ ZxHandleWatchController* controller =
+ reinterpret_cast<ZxHandleWatchController*>(
+ static_cast<uintptr_t>(packet.key));
+
+ DCHECK_NE(0u, packet.signal.trigger & packet.signal.observed);
+
+ zx_signals_t signals = controller->WaitEnd(packet.signal.observed);
+
+ // In the case of a persistent Watch, the Watch may be stopped and
+ // potentially deleted by the caller within the callback, in which case
+ // |controller| should not be accessed again, and we mustn't continue the
+ // watch. We check for this with a bool on the stack, which the Watch
+ // receives a pointer to.
+ bool controller_was_stopped = false;
+ controller->was_stopped_ = &controller_was_stopped;
+
+ controller->watcher_->OnZxHandleSignalled(controller->handle_, signals);
+
+ if (!controller_was_stopped) {
+ controller->was_stopped_ = nullptr;
+ if (controller->persistent_)
+ controller->WaitBegin();
+ }
+ } else {
+ // Wakeup caused by ScheduleWork().
+ DCHECK_EQ(ZX_PKT_TYPE_USER, packet.type);
+ }
+
+ return true;
+}
+
void MessagePumpFuchsia::Run(Delegate* delegate) {
AutoReset<bool> auto_reset_keep_running(&keep_running_, true);
@@ -218,6 +266,10 @@ void MessagePumpFuchsia::Run(Delegate* delegate) {
if (!keep_running_)
break;
+ did_work |= HandleEvents(/*deadline=*/0);
+ if (!keep_running_)
+ break;
+
if (did_work)
continue;
@@ -228,51 +280,10 @@ void MessagePumpFuchsia::Run(Delegate* delegate) {
if (did_work)
continue;
- mx_time_t deadline = delayed_work_time_.is_null()
- ? MX_TIME_INFINITE
- : delayed_work_time_.ToMXTime();
- mx_port_packet_t packet;
-
- const mx_status_t wait_status =
- mx_port_wait(port_.get(), deadline, &packet, 0);
- if (wait_status != MX_OK) {
- if (wait_status != MX_ERR_TIMED_OUT) {
- NOTREACHED() << "unexpected wait status: "
- << mx_status_get_string(wait_status);
- }
- continue;
- }
-
- if (packet.type == MX_PKT_TYPE_SIGNAL_ONE) {
- // A watched fd caused the wakeup via mx_object_wait_async().
- DCHECK_EQ(MX_OK, packet.status);
- MxHandleWatchController* controller =
- reinterpret_cast<MxHandleWatchController*>(
- static_cast<uintptr_t>(packet.key));
-
- DCHECK_NE(0u, packet.signal.trigger & packet.signal.observed);
-
- mx_signals_t signals = controller->WaitEnd(packet.signal.observed);
-
- // In the case of a persistent Watch, the Watch may be stopped and
- // potentially deleted by the caller within the callback, in which case
- // |controller| should not be accessed again, and we mustn't continue the
- // watch. We check for this with a bool on the stack, which the Watch
- // receives a pointer to.
- bool controller_was_stopped = false;
- controller->was_stopped_ = &controller_was_stopped;
-
- controller->watcher_->OnMxHandleSignalled(controller->handle_, signals);
-
- if (!controller_was_stopped) {
- controller->was_stopped_ = nullptr;
- if (controller->persistent_)
- controller->WaitBegin();
- }
- } else {
- // Wakeup caused by ScheduleWork().
- DCHECK_EQ(MX_PKT_TYPE_USER, packet.type);
- }
+ zx_time_t deadline = delayed_work_time_.is_null()
+ ? ZX_TIME_INFINITE
+ : delayed_work_time_.ToZxTime();
+ HandleEvents(deadline);
}
}
@@ -283,11 +294,11 @@ void MessagePumpFuchsia::Quit() {
void MessagePumpFuchsia::ScheduleWork() {
// Since this can be called on any thread, we need to ensure that our Run loop
// wakes up.
- mx_port_packet_t packet = {};
- packet.type = MX_PKT_TYPE_USER;
- mx_status_t status = mx_port_queue(port_.get(), &packet, 0);
- DLOG_IF(ERROR, status != MX_OK)
- << "mx_port_queue failed: " << status << " (port=" << port_.get() << ")";
+ zx_port_packet_t packet = {};
+ packet.type = ZX_PKT_TYPE_USER;
+ zx_status_t status = zx_port_queue(port_.get(), &packet, 0);
+ DLOG_IF(ERROR, status != ZX_OK)
+ << "zx_port_queue failed: " << status << " (port=" << port_.get() << ")";
}
void MessagePumpFuchsia::ScheduleDelayedWork(
diff --git a/chromium/base/message_loop/message_pump_fuchsia.h b/chromium/base/message_loop/message_pump_fuchsia.h
index 7a221e15607..80d41538110 100644
--- a/chromium/base/message_loop/message_pump_fuchsia.h
+++ b/chromium/base/message_loop/message_pump_fuchsia.h
@@ -6,28 +6,28 @@
#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_FUCHSIA_H_
#include "base/base_export.h"
-#include "base/fuchsia/scoped_mx_handle.h"
+#include "base/fuchsia/scoped_zx_handle.h"
#include "base/location.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_pump.h"
-#include <magenta/syscalls/port.h>
-#include <mxio/io.h>
-#include <mxio/private.h>
+#include <fdio/io.h>
+#include <fdio/private.h>
+#include <zircon/syscalls/port.h>
namespace base {
class BASE_EXPORT MessagePumpFuchsia : public MessagePump {
public:
// Implemented by callers to receive notifications of handle & fd events.
- class MxHandleWatcher {
+ class ZxHandleWatcher {
public:
- virtual void OnMxHandleSignalled(mx_handle_t handle,
- mx_signals_t signals) = 0;
+ virtual void OnZxHandleSignalled(zx_handle_t handle,
+ zx_signals_t signals) = 0;
protected:
- virtual ~MxHandleWatcher() {}
+ virtual ~ZxHandleWatcher() {}
};
class FdWatcher {
@@ -38,24 +38,21 @@ class BASE_EXPORT MessagePumpFuchsia : public MessagePump {
virtual ~FdWatcher() {}
};
- // Manages an active watch on an mx_handle_t.
- class MxHandleWatchController {
+ // Manages an active watch on an zx_handle_t.
+ class ZxHandleWatchController {
public:
- explicit MxHandleWatchController(
- const tracked_objects::Location& from_here);
- // Deleting the Controller implicitly calls StopWatchingMxHandle.
- virtual ~MxHandleWatchController();
+ explicit ZxHandleWatchController(const Location& from_here);
+ // Deleting the Controller implicitly calls StopWatchingZxHandle.
+ virtual ~ZxHandleWatchController();
// Stop watching the handle, always safe to call. No-op if there's nothing
// to do.
- bool StopWatchingMxHandle();
+ bool StopWatchingZxHandle();
- const tracked_objects::Location& created_from_location() {
- return created_from_location_;
- }
+ const Location& created_from_location() { return created_from_location_; }
protected:
- // This bool is used by the pump when invoking the MxHandleWatcher callback,
+ // This bool is used by the pump when invoking the ZxHandleWatcher callback,
// and by the FdHandleWatchController when invoking read & write callbacks,
// to cope with the possibility of the caller deleting the *Watcher within
// the callback. The pump sets |was_stopped_| to a location on the stack,
@@ -72,19 +69,19 @@ class BASE_EXPORT MessagePumpFuchsia : public MessagePump {
// Called by MessagePumpFuchsia when the handle is signalled. Accepts the
// set of signals that fired, and returns the intersection with those the
// caller is interested in.
- mx_signals_t WaitEnd(mx_signals_t observed);
+ zx_signals_t WaitEnd(zx_signals_t observed);
// Returns the key to use to uniquely identify this object's wait operation.
uint64_t wait_key() const {
return static_cast<uint64_t>(reinterpret_cast<uintptr_t>(this));
}
- const tracked_objects::Location created_from_location_;
+ const Location created_from_location_;
// Set directly from the inputs to WatchFileDescriptor.
- MxHandleWatcher* watcher_ = nullptr;
- mx_handle_t handle_ = MX_HANDLE_INVALID;
- mx_signals_t desired_signals_ = 0;
+ ZxHandleWatcher* watcher_ = nullptr;
+ zx_handle_t handle_ = ZX_HANDLE_INVALID;
+ zx_signals_t desired_signals_ = 0;
// Used to safely access resources owned by the associated message pump.
WeakPtr<MessagePumpFuchsia> weak_pump_;
@@ -97,14 +94,14 @@ class BASE_EXPORT MessagePumpFuchsia : public MessagePump {
// this controller.
bool has_begun_ = false;
- DISALLOW_COPY_AND_ASSIGN(MxHandleWatchController);
+ DISALLOW_COPY_AND_ASSIGN(ZxHandleWatchController);
};
// Object returned by WatchFileDescriptor to manage further watching.
- class FdWatchController : public MxHandleWatchController,
- public MxHandleWatcher {
+ class FdWatchController : public ZxHandleWatchController,
+ public ZxHandleWatcher {
public:
- explicit FdWatchController(const tracked_objects::Location& from_here);
+ explicit FdWatchController(const Location& from_here);
~FdWatchController() override;
bool StopWatchingFileDescriptor();
@@ -115,8 +112,8 @@ class BASE_EXPORT MessagePumpFuchsia : public MessagePump {
// Determines the desires signals, and begins waiting on the handle.
bool WaitBegin() override;
- // MxHandleWatcher interface.
- void OnMxHandleSignalled(mx_handle_t handle, mx_signals_t signals) override;
+ // ZxHandleWatcher interface.
+ void OnZxHandleSignalled(zx_handle_t handle, zx_signals_t signals) override;
// Set directly from the inputs to WatchFileDescriptor.
FdWatcher* watcher_ = nullptr;
@@ -124,7 +121,7 @@ class BASE_EXPORT MessagePumpFuchsia : public MessagePump {
uint32_t desired_events_ = 0;
// Set by WatchFileDescriptor to hold a reference to the descriptor's mxio.
- mxio_t* io_ = nullptr;
+ fdio_t* io_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(FdWatchController);
};
@@ -137,11 +134,11 @@ class BASE_EXPORT MessagePumpFuchsia : public MessagePump {
MessagePumpFuchsia();
- bool WatchMxHandle(mx_handle_t handle,
+ bool WatchZxHandle(zx_handle_t handle,
bool persistent,
- mx_signals_t signals,
- MxHandleWatchController* controller,
- MxHandleWatcher* delegate);
+ zx_signals_t signals,
+ ZxHandleWatchController* controller,
+ ZxHandleWatcher* delegate);
bool WatchFileDescriptor(int fd,
bool persistent,
int mode,
@@ -155,10 +152,14 @@ class BASE_EXPORT MessagePumpFuchsia : public MessagePump {
void ScheduleDelayedWork(const TimeTicks& delayed_work_time) override;
private:
+ // Handles IO events from the |port_|. Returns true if any events were
+ // received.
+ bool HandleEvents(zx_time_t deadline);
+
// This flag is set to false when Run should return.
bool keep_running_ = true;
- ScopedMxHandle port_;
+ ScopedZxHandle port_;
// The time at which we should call DoDelayedWork.
TimeTicks delayed_work_time_;
diff --git a/chromium/base/message_loop/message_pump_glib_unittest.cc b/chromium/base/message_loop/message_pump_glib_unittest.cc
index 1bfa70e9be3..3ac58c85bc3 100644
--- a/chromium/base/message_loop/message_pump_glib_unittest.cc
+++ b/chromium/base/message_loop/message_pump_glib_unittest.cc
@@ -152,8 +152,7 @@ void ExpectProcessedEvents(EventInjector* injector, int count) {
}
// Posts a task on the current message loop.
-void PostMessageLoopTask(const tracked_objects::Location& from_here,
- OnceClosure task) {
+void PostMessageLoopTask(const Location& from_here, OnceClosure task) {
ThreadTaskRunnerHandle::Get()->PostTask(from_here, std::move(task));
}
diff --git a/chromium/base/message_loop/message_pump_io_ios.cc b/chromium/base/message_loop/message_pump_io_ios.cc
index 85cb2555c95..74a3f158303 100644
--- a/chromium/base/message_loop/message_pump_io_ios.cc
+++ b/chromium/base/message_loop/message_pump_io_ios.cc
@@ -7,7 +7,7 @@
namespace base {
MessagePumpIOSForIO::FileDescriptorWatcher::FileDescriptorWatcher(
- const tracked_objects::Location& from_here)
+ const Location& from_here)
: is_persistent_(false),
fdref_(NULL),
callback_types_(0),
diff --git a/chromium/base/message_loop/message_pump_io_ios.h b/chromium/base/message_loop/message_pump_io_ios.h
index f48e9573655..e842a6c9ecf 100644
--- a/chromium/base/message_loop/message_pump_io_ios.h
+++ b/chromium/base/message_loop/message_pump_io_ios.h
@@ -37,7 +37,7 @@ class BASE_EXPORT MessagePumpIOSForIO : public MessagePumpNSRunLoop {
// Object returned by WatchFileDescriptor to manage further watching.
class FileDescriptorWatcher {
public:
- explicit FileDescriptorWatcher(const tracked_objects::Location& from_here);
+ explicit FileDescriptorWatcher(const Location& from_here);
~FileDescriptorWatcher(); // Implicitly calls StopWatchingFileDescriptor.
// NOTE: These methods aren't called StartWatching()/StopWatching() to
@@ -47,9 +47,7 @@ class BASE_EXPORT MessagePumpIOSForIO : public MessagePumpNSRunLoop {
// to do.
bool StopWatchingFileDescriptor();
- const tracked_objects::Location& created_from_location() {
- return created_from_location_;
- }
+ const Location& created_from_location() { return created_from_location_; }
private:
friend class MessagePumpIOSForIO;
@@ -77,7 +75,7 @@ class BASE_EXPORT MessagePumpIOSForIO : public MessagePumpNSRunLoop {
base::WeakPtr<MessagePumpIOSForIO> pump_;
Watcher* watcher_;
- tracked_objects::Location created_from_location_;
+ Location created_from_location_;
DISALLOW_COPY_AND_ASSIGN(FileDescriptorWatcher);
};
diff --git a/chromium/base/message_loop/message_pump_libevent.cc b/chromium/base/message_loop/message_pump_libevent.cc
index 14bc75c92b9..63d85c86221 100644
--- a/chromium/base/message_loop/message_pump_libevent.cc
+++ b/chromium/base/message_loop/message_pump_libevent.cc
@@ -44,7 +44,7 @@
namespace base {
MessagePumpLibevent::FileDescriptorWatcher::FileDescriptorWatcher(
- const tracked_objects::Location& from_here)
+ const Location& from_here)
: event_(NULL),
pump_(NULL),
watcher_(NULL),
diff --git a/chromium/base/message_loop/message_pump_libevent.h b/chromium/base/message_loop/message_pump_libevent.h
index 1124560d66c..e14b58460b5 100644
--- a/chromium/base/message_loop/message_pump_libevent.h
+++ b/chromium/base/message_loop/message_pump_libevent.h
@@ -38,7 +38,7 @@ class BASE_EXPORT MessagePumpLibevent : public MessagePump {
// Object returned by WatchFileDescriptor to manage further watching.
class FileDescriptorWatcher {
public:
- explicit FileDescriptorWatcher(const tracked_objects::Location& from_here);
+ explicit FileDescriptorWatcher(const Location& from_here);
~FileDescriptorWatcher(); // Implicitly calls StopWatchingFileDescriptor.
// NOTE: These methods aren't called StartWatching()/StopWatching() to
@@ -48,9 +48,7 @@ class BASE_EXPORT MessagePumpLibevent : public MessagePump {
// to do.
bool StopWatchingFileDescriptor();
- const tracked_objects::Location& created_from_location() {
- return created_from_location_;
- }
+ const Location& created_from_location() { return created_from_location_; }
private:
friend class MessagePumpLibevent;
@@ -78,7 +76,7 @@ class BASE_EXPORT MessagePumpLibevent : public MessagePump {
// destructor.
bool* was_destroyed_;
- const tracked_objects::Location created_from_location_;
+ const Location created_from_location_;
DISALLOW_COPY_AND_ASSIGN(FileDescriptorWatcher);
};
diff --git a/chromium/base/message_loop/message_pump_mac.h b/chromium/base/message_loop/message_pump_mac.h
index 763c6b4868d..9a5243441ee 100644
--- a/chromium/base/message_loop/message_pump_mac.h
+++ b/chromium/base/message_loop/message_pump_mac.h
@@ -91,9 +91,10 @@ class BASE_EXPORT MessagePumpCFRunLoopBase : public MessagePump {
friend class MessagePumpScopedAutoreleasePool;
friend class TestMessagePumpCFRunLoopBase;
- // 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);
+ // Tasks will be pumped in the run loop modes described by
+ // |initial_mode_mask|, which maps bits to the index of an internal array of
+ // run loop mode identifiers.
+ explicit MessagePumpCFRunLoopBase(int initial_mode_mask);
~MessagePumpCFRunLoopBase() override;
// Subclasses should implement the work they need to do in MessagePump::Run
@@ -117,12 +118,18 @@ 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);
+ // Enable and disable entries in |enabled_modes_| to match |mode_mask|.
+ void SetModeMask(int mode_mask);
+
+ // Get the current mode mask from |enabled_modes_|.
+ int GetModeMask() const;
private:
+ class ScopedModeEnabler;
+
+ // The maximum number of run loop modes that can be monitored.
+ static constexpr int kNumModes = 4;
+
// Marking timers as invalid at the right time helps significantly reduce
// power use (see the comment in RunDelayedWorkTimer()), however there is no
// public API for doing so. CFRuntime.h states that CFRuntimeBase, upon which
@@ -198,8 +205,8 @@ 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 enabled modes. Posted tasks may run in any non-null entry.
+ std::unique_ptr<ScopedModeEnabler> enabled_modes_[kNumModes];
// The timer, sources, and observers are described above alongside their
// callbacks.
@@ -308,6 +315,19 @@ class MessagePumpUIApplication : public MessagePumpCFRunLoopBase {
#else
+// While in scope, permits posted tasks to be run in private AppKit run loop
+// modes that would otherwise make the UI unresponsive. E.g., menu fade out.
+class BASE_EXPORT ScopedPumpMessagesInPrivateModes {
+ public:
+ ScopedPumpMessagesInPrivateModes();
+ ~ScopedPumpMessagesInPrivateModes();
+
+ int GetModeMaskForTest();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ScopedPumpMessagesInPrivateModes);
+};
+
class MessagePumpNSApplication : public MessagePumpCFRunLoopBase {
public:
MessagePumpNSApplication();
@@ -317,6 +337,8 @@ class MessagePumpNSApplication : public MessagePumpCFRunLoopBase {
void Quit() override;
private:
+ friend class ScopedPumpMessagesInPrivateModes;
+
// False after Quit is called.
bool keep_running_;
diff --git a/chromium/base/message_loop/message_pump_mac.mm b/chromium/base/message_loop/message_pump_mac.mm
index 71cb00b80a1..0451e33ab21 100644
--- a/chromium/base/message_loop/message_pump_mac.mm
+++ b/chromium/base/message_loop/message_pump_mac.mm
@@ -12,6 +12,7 @@
#include "base/mac/call_with_eh_frame.h"
#include "base/mac/scoped_cftyperef.h"
#include "base/macros.h"
+#include "base/memory/ptr_util.h"
#include "base/message_loop/timer_slack.h"
#include "base/run_loop.h"
#include "base/time/time.h"
@@ -45,17 +46,17 @@ const CFStringRef kAllModes[] = {
};
// Mask that determines which modes in |kAllModes| to use.
-enum { kCommonModeMask = 0x1, kAllModesMask = ~0 };
+enum { kCommonModeMask = 0x1, kAllModesMask = 0xf };
-// Modes to use for MessagePumpNSApplication. Currently just common and
-// exclusive modes. TODO(tapted): Use kAllModesMask once http://crbug.com/640466
-// blockers are fixed.
-enum { kNSApplicationModeMask = 0x3 };
+// Modes to use for MessagePumpNSApplication that are considered "safe".
+// Currently just common and exclusive modes. Ideally, messages would be pumped
+// in all modes, but that interacts badly with app modal dialogs (e.g. NSAlert).
+enum { kNSApplicationModalSafeModeMask = 0x3 };
void NoOp(void* info) {
}
-const CFTimeInterval kCFTimeIntervalMax =
+constexpr CFTimeInterval kCFTimeIntervalMax =
std::numeric_limits<CFTimeInterval>::max();
#if !defined(OS_IOS)
@@ -63,6 +64,9 @@ const CFTimeInterval kCFTimeIntervalMax =
// initialized. Only accessed from the main thread.
bool g_not_using_cr_app = false;
+// The MessagePump controlling [NSApp run].
+MessagePumpNSApplication* g_app_pump;
+
// Various CoreFoundation definitions.
typedef struct __CFRuntimeBase {
uintptr_t _cfisa;
@@ -128,6 +132,40 @@ class MessagePumpScopedAutoreleasePool {
DISALLOW_COPY_AND_ASSIGN(MessagePumpScopedAutoreleasePool);
};
+class MessagePumpCFRunLoopBase::ScopedModeEnabler {
+ public:
+ ScopedModeEnabler(MessagePumpCFRunLoopBase* owner, int mode_index)
+ : owner_(owner), mode_index_(mode_index) {
+ CFRunLoopRef loop = owner_->run_loop_;
+ CFRunLoopAddTimer(loop, owner_->delayed_work_timer_, mode());
+ CFRunLoopAddSource(loop, owner_->work_source_, mode());
+ CFRunLoopAddSource(loop, owner_->idle_work_source_, mode());
+ CFRunLoopAddSource(loop, owner_->nesting_deferred_work_source_, mode());
+ CFRunLoopAddObserver(loop, owner_->pre_wait_observer_, mode());
+ CFRunLoopAddObserver(loop, owner_->pre_source_observer_, mode());
+ CFRunLoopAddObserver(loop, owner_->enter_exit_observer_, mode());
+ }
+
+ ~ScopedModeEnabler() {
+ CFRunLoopRef loop = owner_->run_loop_;
+ CFRunLoopRemoveObserver(loop, owner_->enter_exit_observer_, mode());
+ CFRunLoopRemoveObserver(loop, owner_->pre_source_observer_, mode());
+ CFRunLoopRemoveObserver(loop, owner_->pre_wait_observer_, mode());
+ CFRunLoopRemoveSource(loop, owner_->nesting_deferred_work_source_, mode());
+ CFRunLoopRemoveSource(loop, owner_->idle_work_source_, mode());
+ CFRunLoopRemoveSource(loop, owner_->work_source_, mode());
+ CFRunLoopRemoveTimer(loop, owner_->delayed_work_timer_, mode());
+ }
+
+ const CFStringRef& mode() const { return kAllModes[mode_index_]; }
+
+ private:
+ MessagePumpCFRunLoopBase* const owner_; // Weak. Owns this.
+ const int mode_index_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedModeEnabler);
+};
+
// Must be called on the run loop thread.
void MessagePumpCFRunLoopBase::Run(Delegate* delegate) {
// nesting_level_ will be incremented in EnterExitRunLoop, so set
@@ -180,9 +218,8 @@ void MessagePumpCFRunLoopBase::SetTimerSlack(TimerSlack timer_slack) {
}
// Must be called on the run loop thread.
-MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase(int mode_mask)
- : mode_mask_(mode_mask),
- delegate_(NULL),
+MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase(int initial_mode_mask)
+ : delegate_(NULL),
delayed_work_fire_time_(kCFTimeIntervalMax),
timer_slack_(base::TIMER_SLACK_NONE),
nesting_level_(0),
@@ -205,7 +242,6 @@ MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase(int mode_mask)
0, // priority
RunDelayedWorkTimer,
&timer_context);
- InvokeForEnabledModes(&CFRunLoopAddTimer, delayed_work_timer_);
CFRunLoopSourceContext source_context = CFRunLoopSourceContext();
source_context.info = this;
@@ -213,19 +249,14 @@ MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase(int mode_mask)
work_source_ = CFRunLoopSourceCreate(NULL, // allocator
1, // priority
&source_context);
- InvokeForEnabledModes(&CFRunLoopAddSource, work_source_);
-
source_context.perform = RunIdleWorkSource;
idle_work_source_ = CFRunLoopSourceCreate(NULL, // allocator
2, // priority
&source_context);
- InvokeForEnabledModes(&CFRunLoopAddSource, idle_work_source_);
-
source_context.perform = RunNestingDeferredWorkSource;
nesting_deferred_work_source_ = CFRunLoopSourceCreate(NULL, // allocator
0, // priority
&source_context);
- InvokeForEnabledModes(&CFRunLoopAddSource, nesting_deferred_work_source_);
CFRunLoopObserverContext observer_context = CFRunLoopObserverContext();
observer_context.info = this;
@@ -235,16 +266,12 @@ MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase(int mode_mask)
0, // priority
PreWaitObserver,
&observer_context);
- InvokeForEnabledModes(&CFRunLoopAddObserver, pre_wait_observer_);
-
pre_source_observer_ = CFRunLoopObserverCreate(NULL, // allocator
kCFRunLoopBeforeSources,
true, // repeat
0, // priority
PreSourceObserver,
&observer_context);
- InvokeForEnabledModes(&CFRunLoopAddObserver, pre_source_observer_);
-
enter_exit_observer_ = CFRunLoopObserverCreate(NULL, // allocator
kCFRunLoopEntry |
kCFRunLoopExit,
@@ -252,24 +279,20 @@ MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase(int mode_mask)
0, // priority
EnterExitObserver,
&observer_context);
- InvokeForEnabledModes(&CFRunLoopAddObserver, enter_exit_observer_);
+ SetModeMask(initial_mode_mask);
}
// 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() {
- 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_);
+ SetModeMask(0);
+ CFRelease(enter_exit_observer_);
+ CFRelease(pre_source_observer_);
+ CFRelease(pre_wait_observer_);
+ CFRelease(nesting_deferred_work_source_);
+ CFRelease(idle_work_source_);
+ CFRelease(work_source_);
CFRelease(delayed_work_timer_);
CFRelease(run_loop_);
}
@@ -297,17 +320,25 @@ AutoreleasePoolType* MessagePumpCFRunLoopBase::CreateAutoreleasePool() {
return [[NSAutoreleasePool alloc] init];
}
-template <typename Argument>
-void MessagePumpCFRunLoopBase::InvokeForEnabledModes(void method(CFRunLoopRef,
- Argument,
- CFStringRef),
- Argument argument) {
+void MessagePumpCFRunLoopBase::SetModeMask(int mode_mask) {
+ static_assert(arraysize(enabled_modes_) == arraysize(kAllModes),
+ "mode size mismatch");
for (size_t i = 0; i < arraysize(kAllModes); ++i) {
- if (mode_mask_ & (0x1 << i))
- method(run_loop_, argument, kAllModes[i]);
+ bool enable = mode_mask & (0x1 << i);
+ if (enable == !enabled_modes_[i]) {
+ enabled_modes_[i] =
+ enable ? base::MakeUnique<ScopedModeEnabler>(this, i) : nullptr;
+ }
}
}
+int MessagePumpCFRunLoopBase::GetModeMask() const {
+ int mask = 0;
+ for (size_t i = 0; i < arraysize(enabled_modes_); ++i)
+ mask |= enabled_modes_[i] ? (0x1 << i) : 0;
+ return mask;
+}
+
#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.
@@ -679,11 +710,11 @@ MessagePumpNSRunLoop::MessagePumpNSRunLoop()
quit_source_ = CFRunLoopSourceCreate(NULL, // allocator
0, // priority
&source_context);
- InvokeForEnabledModes(&CFRunLoopAddSource, quit_source_);
+ CFRunLoopAddSource(run_loop(), quit_source_, kCFRunLoopCommonModes);
}
MessagePumpNSRunLoop::~MessagePumpNSRunLoop() {
- InvokeForEnabledModes(&CFRunLoopRemoveSource, quit_source_);
+ CFRunLoopRemoveSource(run_loop(), quit_source_, kCFRunLoopCommonModes);
CFRelease(quit_source_);
}
@@ -726,12 +757,36 @@ void MessagePumpUIApplication::Attach(Delegate* delegate) {
#else
+ScopedPumpMessagesInPrivateModes::ScopedPumpMessagesInPrivateModes() {
+ DCHECK(g_app_pump);
+ DCHECK_EQ(kNSApplicationModalSafeModeMask, g_app_pump->GetModeMask());
+ // Pumping events in private runloop modes is known to interact badly with
+ // app modal windows like NSAlert.
+ if (![NSApp modalWindow])
+ g_app_pump->SetModeMask(kAllModesMask);
+}
+
+ScopedPumpMessagesInPrivateModes::~ScopedPumpMessagesInPrivateModes() {
+ DCHECK(g_app_pump);
+ g_app_pump->SetModeMask(kNSApplicationModalSafeModeMask);
+}
+
+int ScopedPumpMessagesInPrivateModes::GetModeMaskForTest() {
+ return g_app_pump ? g_app_pump->GetModeMask() : -1;
+}
+
MessagePumpNSApplication::MessagePumpNSApplication()
- : MessagePumpCFRunLoopBase(kNSApplicationModeMask),
+ : MessagePumpCFRunLoopBase(kNSApplicationModalSafeModeMask),
keep_running_(true),
- running_own_loop_(false) {}
+ running_own_loop_(false) {
+ DCHECK_EQ(nullptr, g_app_pump);
+ g_app_pump = this;
+}
-MessagePumpNSApplication::~MessagePumpNSApplication() {}
+MessagePumpNSApplication::~MessagePumpNSApplication() {
+ DCHECK_EQ(this, g_app_pump);
+ g_app_pump = nullptr;
+}
void MessagePumpNSApplication::DoRun(Delegate* delegate) {
bool last_running_own_loop_ = running_own_loop_;
diff --git a/chromium/base/message_loop/message_pump_mac_unittest.cc b/chromium/base/message_loop/message_pump_mac_unittest.mm
index 17c70c8a6cb..480b311fa38 100644
--- a/chromium/base/message_loop/message_pump_mac_unittest.cc
+++ b/chromium/base/message_loop/message_pump_mac_unittest.mm
@@ -5,9 +5,24 @@
#include "base/message_loop/message_pump_mac.h"
#include "base/mac/scoped_cftyperef.h"
+#import "base/mac/scoped_nsobject.h"
#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/threading/thread_task_runner_handle.h"
#include "testing/gtest/include/gtest/gtest.h"
+@interface TestModalAlertCloser : NSObject
+- (void)runTestThenCloseAlert:(NSAlert*)alert;
+@end
+
+namespace {
+
+// Internal constants from message_pump_mac.mm.
+constexpr int kAllModesMask = 0xf;
+constexpr int kNSApplicationModalSafeModeMask = 0x3;
+
+} // namespace
+
namespace base {
class TestMessagePumpCFRunLoopBase {
@@ -105,4 +120,105 @@ TEST(MessagePumpMacTest, TestInvalidatedTimerReuse) {
CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), test_timer,
kMessageLoopExclusiveRunLoopMode);
}
+
+namespace {
+
+// PostedTasks are only executed while the message pump has a delegate. That is,
+// when a base::RunLoop is running, so in order to test whether posted tasks
+// are run by CFRunLoopRunInMode and *not* by the regular RunLoop, we need to
+// be inside a task that is also calling CFRunLoopRunInMode. This task runs the
+// given |mode| after posting a task to increment a counter, then checks whether
+// the counter incremented after emptying that run loop mode.
+void IncrementInModeAndExpect(CFRunLoopMode mode, int result) {
+ // Since this task is "ours" rather than a system task, allow nesting.
+ MessageLoop::ScopedNestableTaskAllower allow(MessageLoop::current());
+ int counter = 0;
+ auto increment = BindRepeating([](int* i) { ++*i; }, &counter);
+ ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, increment);
+ while (CFRunLoopRunInMode(mode, 0, true) == kCFRunLoopRunHandledSource)
+ ;
+ ASSERT_EQ(result, counter);
+}
+
+} // namespace
+
+// Tests the correct behavior of ScopedPumpMessagesInPrivateModes.
+TEST(MessagePumpMacTest, ScopedPumpMessagesInPrivateModes) {
+ MessageLoopForUI message_loop;
+
+ CFRunLoopMode kRegular = kCFRunLoopDefaultMode;
+ CFRunLoopMode kPrivate = CFSTR("NSUnhighlightMenuRunLoopMode");
+
+ // Work is seen when running in the default mode.
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, BindOnce(&IncrementInModeAndExpect, kRegular, 1));
+ EXPECT_NO_FATAL_FAILURE(RunLoop().RunUntilIdle());
+
+ // But not seen when running in a private mode.
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, BindOnce(&IncrementInModeAndExpect, kPrivate, 0));
+ EXPECT_NO_FATAL_FAILURE(RunLoop().RunUntilIdle());
+
+ {
+ ScopedPumpMessagesInPrivateModes allow_private;
+ // Now the work should be seen.
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, BindOnce(&IncrementInModeAndExpect, kPrivate, 1));
+ EXPECT_NO_FATAL_FAILURE(RunLoop().RunUntilIdle());
+
+ // The regular mode should also work the same.
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, BindOnce(&IncrementInModeAndExpect, kRegular, 1));
+ EXPECT_NO_FATAL_FAILURE(RunLoop().RunUntilIdle());
+ }
+
+ // And now the scoper is out of scope, private modes should no longer see it.
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, BindOnce(&IncrementInModeAndExpect, kPrivate, 0));
+ EXPECT_NO_FATAL_FAILURE(RunLoop().RunUntilIdle());
+
+ // Only regular modes see it.
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, BindOnce(&IncrementInModeAndExpect, kRegular, 1));
+ EXPECT_NO_FATAL_FAILURE(RunLoop().RunUntilIdle());
+}
+
+// Tests that private message loop modes are not pumped while a modal dialog is
+// present.
+TEST(MessagePumpMacTest, ScopedPumpMessagesAttemptWithModalDialog) {
+ MessageLoopForUI message_loop;
+
+ {
+ base::ScopedPumpMessagesInPrivateModes allow_private;
+ // No modal window, so all modes should be pumped.
+ EXPECT_EQ(kAllModesMask, allow_private.GetModeMaskForTest());
+ }
+
+ base::scoped_nsobject<NSAlert> alert([[NSAlert alloc] init]);
+ [alert addButtonWithTitle:@"OK"];
+ base::scoped_nsobject<TestModalAlertCloser> closer(
+ [[TestModalAlertCloser alloc] init]);
+ [closer performSelector:@selector(runTestThenCloseAlert:)
+ withObject:alert
+ afterDelay:0
+ inModes:@[ NSModalPanelRunLoopMode ]];
+ NSInteger result = [alert runModal];
+ EXPECT_EQ(NSAlertFirstButtonReturn, result);
+}
+
} // namespace base
+
+@implementation TestModalAlertCloser
+
+- (void)runTestThenCloseAlert:(NSAlert*)alert {
+ EXPECT_TRUE([NSApp modalWindow]);
+ {
+ base::ScopedPumpMessagesInPrivateModes allow_private;
+ // With a modal window, only safe modes should be pumped.
+ EXPECT_EQ(kNSApplicationModalSafeModeMask,
+ allow_private.GetModeMaskForTest());
+ }
+ [[alert buttons][0] performClick:nil];
+}
+
+@end
diff --git a/chromium/base/message_loop/message_pump_perftest.cc b/chromium/base/message_loop/message_pump_perftest.cc
index 6fafa41bfd5..4b45341d58d 100644
--- a/chromium/base/message_loop/message_pump_perftest.cc
+++ b/chromium/base/message_loop/message_pump_perftest.cc
@@ -263,7 +263,7 @@ class PostTaskTest : public testing::Test {
for (int i = 0; i < batch_size; ++i) {
for (int j = 0; j < tasks_per_reload; ++j) {
queue->AddToIncomingQueue(FROM_HERE, base::BindOnce(&DoNothing),
- base::TimeDelta(), false);
+ base::TimeDelta(), Nestable::kNonNestable);
num_posted++;
}
TaskQueue loop_local_queue;