Index: base/message_loop/incoming_task_queue.cc |
diff --git a/base/message_loop/incoming_task_queue.cc b/base/message_loop/incoming_task_queue.cc |
index 470e44b244b2d700c23bf26afd13c95f2133cbb5..8298c281de21b6398b6879549540a94bb11086cf 100644 |
--- a/base/message_loop/incoming_task_queue.cc |
+++ b/base/message_loop/incoming_task_queue.cc |
@@ -68,8 +68,7 @@ bool IncomingTaskQueue::AddToIncomingQueue( |
<< " seconds from here: " << from_here.ToString(); |
PendingTask pending_task( |
- from_here, task, CalculateDelayedRuntime(delay), nestable); |
- AutoLock locked(incoming_queue_lock_); |
+ from_here, task, CalculateDelayedRuntime(delay), nestable); |
#if defined(OS_WIN) |
// We consider the task needs a high resolution timer if the delay is |
// more than 0 and less than 32ms. This caps the relative error to |
@@ -77,7 +76,6 @@ bool IncomingTaskQueue::AddToIncomingQueue( |
// resolution on Windows is between 10 and 15ms. |
if (delay > TimeDelta() && |
delay.InMilliseconds() < (2 * Time::kMinLowResolutionThresholdMs)) { |
- ++high_res_task_count_; |
pending_task.is_high_res = true; |
} |
#endif |
@@ -115,17 +113,25 @@ int IncomingTaskQueue::ReloadWorkQueue(TaskQueue* work_queue) { |
} |
void IncomingTaskQueue::WillDestroyCurrentMessageLoop() { |
- AutoLock lock(incoming_queue_lock_); |
+ base::subtle::AutoWriteLock lock(message_loop_lock_); |
message_loop_ = NULL; |
} |
void IncomingTaskQueue::StartScheduling() { |
- AutoLock lock(incoming_queue_lock_); |
- DCHECK(!is_ready_for_scheduling_); |
- DCHECK(!message_loop_scheduled_); |
- is_ready_for_scheduling_ = true; |
- if (!incoming_queue_.empty()) |
- ScheduleWork(); |
+ bool schedule_work; |
+ { |
+ AutoLock lock(incoming_queue_lock_); |
+ DCHECK(!is_ready_for_scheduling_); |
+ DCHECK(!message_loop_scheduled_); |
+ is_ready_for_scheduling_ = true; |
+ schedule_work = !incoming_queue_.empty(); |
+ } |
+ if (schedule_work) { |
+ DCHECK(message_loop_); |
+ // Don't need to lock |message_loop_lock_| here because this function is |
+ // called by MessageLoop on its thread. |
+ message_loop_->ScheduleWork(); |
+ } |
} |
IncomingTaskQueue::~IncomingTaskQueue() { |
@@ -138,44 +144,55 @@ bool IncomingTaskQueue::PostPendingTask(PendingTask* pending_task) { |
// directly, as it could starve handling of foreign threads. Put every task |
// into this queue. |
- // This should only be called while the lock is taken. |
- incoming_queue_lock_.AssertAcquired(); |
+ // Ensures |message_loop_| isn't destroyed while running. |
+ base::subtle::AutoReadLock hold_message_loop(message_loop_lock_); |
if (!message_loop_) { |
pending_task->task.Reset(); |
return false; |
} |
- // 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 schedule_work = false; |
+ { |
+ AutoLock hold(incoming_queue_lock_); |
- bool was_empty = incoming_queue_.empty(); |
- incoming_queue_.push(*pending_task); |
- pending_task->task.Reset(); |
+#if defined(OS_WIN) |
+ if (pending_task->is_high_res) |
+ ++high_res_task_count_; |
+#endif |
- if (is_ready_for_scheduling_ && |
- (always_schedule_work_ || (!message_loop_scheduled_ && was_empty))) { |
- ScheduleWork(); |
+ // 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; |
+ } |
} |
- return true; |
-} |
+ // 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(); |
-void IncomingTaskQueue::ScheduleWork() { |
- DCHECK(is_ready_for_scheduling_); |
- // Wake up the message loop. |
- message_loop_->ScheduleWork(); |
- // 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; |
} |
} // namespace internal |