Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(451)

Side by Side Diff: base/message_loop/incoming_task_queue.cc

Issue 1991623002: Avoid holding |incoming_queue_lock_| while waking up the message loop. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@base-rw-lock
Patch Set: Rebase Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « base/message_loop/incoming_task_queue.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "base/message_loop/incoming_task_queue.h" 5 #include "base/message_loop/incoming_task_queue.h"
6 6
7 #include <limits> 7 #include <limits>
8 8
9 #include "base/location.h" 9 #include "base/location.h"
10 #include "base/message_loop/message_loop.h" 10 #include "base/message_loop/message_loop.h"
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
61 const tracked_objects::Location& from_here, 61 const tracked_objects::Location& from_here,
62 const Closure& task, 62 const Closure& task,
63 TimeDelta delay, 63 TimeDelta delay,
64 bool nestable) { 64 bool nestable) {
65 DLOG_IF(WARNING, 65 DLOG_IF(WARNING,
66 delay.InSeconds() > kTaskDelayWarningThresholdInSeconds) 66 delay.InSeconds() > kTaskDelayWarningThresholdInSeconds)
67 << "Requesting super-long task delay period of " << delay.InSeconds() 67 << "Requesting super-long task delay period of " << delay.InSeconds()
68 << " seconds from here: " << from_here.ToString(); 68 << " seconds from here: " << from_here.ToString();
69 69
70 PendingTask pending_task( 70 PendingTask pending_task(
71 from_here, task, CalculateDelayedRuntime(delay), nestable); 71 from_here, task, CalculateDelayedRuntime(delay), nestable);
72 AutoLock locked(incoming_queue_lock_);
73 #if defined(OS_WIN) 72 #if defined(OS_WIN)
74 // We consider the task needs a high resolution timer if the delay is 73 // We consider the task needs a high resolution timer if the delay is
75 // more than 0 and less than 32ms. This caps the relative error to 74 // more than 0 and less than 32ms. This caps the relative error to
76 // less than 50% : a 33ms wait can wake at 48ms since the default 75 // less than 50% : a 33ms wait can wake at 48ms since the default
77 // resolution on Windows is between 10 and 15ms. 76 // resolution on Windows is between 10 and 15ms.
78 if (delay > TimeDelta() && 77 if (delay > TimeDelta() &&
79 delay.InMilliseconds() < (2 * Time::kMinLowResolutionThresholdMs)) { 78 delay.InMilliseconds() < (2 * Time::kMinLowResolutionThresholdMs)) {
80 ++high_res_task_count_;
81 pending_task.is_high_res = true; 79 pending_task.is_high_res = true;
82 } 80 }
83 #endif 81 #endif
84 return PostPendingTask(&pending_task); 82 return PostPendingTask(&pending_task);
85 } 83 }
86 84
87 bool IncomingTaskQueue::HasHighResolutionTasks() { 85 bool IncomingTaskQueue::HasHighResolutionTasks() {
88 AutoLock lock(incoming_queue_lock_); 86 AutoLock lock(incoming_queue_lock_);
89 return high_res_task_count_ > 0; 87 return high_res_task_count_ > 0;
90 } 88 }
(...skipping 17 matching lines...) Expand all
108 } else { 106 } else {
109 incoming_queue_.Swap(work_queue); 107 incoming_queue_.Swap(work_queue);
110 } 108 }
111 // Reset the count of high resolution tasks since our queue is now empty. 109 // Reset the count of high resolution tasks since our queue is now empty.
112 int high_res_tasks = high_res_task_count_; 110 int high_res_tasks = high_res_task_count_;
113 high_res_task_count_ = 0; 111 high_res_task_count_ = 0;
114 return high_res_tasks; 112 return high_res_tasks;
115 } 113 }
116 114
117 void IncomingTaskQueue::WillDestroyCurrentMessageLoop() { 115 void IncomingTaskQueue::WillDestroyCurrentMessageLoop() {
118 AutoLock lock(incoming_queue_lock_); 116 base::subtle::AutoWriteLock lock(message_loop_lock_);
119 message_loop_ = NULL; 117 message_loop_ = NULL;
120 } 118 }
121 119
122 void IncomingTaskQueue::StartScheduling() { 120 void IncomingTaskQueue::StartScheduling() {
123 AutoLock lock(incoming_queue_lock_); 121 bool schedule_work;
124 DCHECK(!is_ready_for_scheduling_); 122 {
125 DCHECK(!message_loop_scheduled_); 123 AutoLock lock(incoming_queue_lock_);
126 is_ready_for_scheduling_ = true; 124 DCHECK(!is_ready_for_scheduling_);
127 if (!incoming_queue_.empty()) 125 DCHECK(!message_loop_scheduled_);
128 ScheduleWork(); 126 is_ready_for_scheduling_ = true;
127 schedule_work = !incoming_queue_.empty();
128 }
129 if (schedule_work) {
130 DCHECK(message_loop_);
131 // Don't need to lock |message_loop_lock_| here because this function is
132 // called by MessageLoop on its thread.
133 message_loop_->ScheduleWork();
134 }
129 } 135 }
130 136
131 IncomingTaskQueue::~IncomingTaskQueue() { 137 IncomingTaskQueue::~IncomingTaskQueue() {
132 // Verify that WillDestroyCurrentMessageLoop() has been called. 138 // Verify that WillDestroyCurrentMessageLoop() has been called.
133 DCHECK(!message_loop_); 139 DCHECK(!message_loop_);
134 } 140 }
135 141
136 bool IncomingTaskQueue::PostPendingTask(PendingTask* pending_task) { 142 bool IncomingTaskQueue::PostPendingTask(PendingTask* pending_task) {
137 // Warning: Don't try to short-circuit, and handle this thread's tasks more 143 // Warning: Don't try to short-circuit, and handle this thread's tasks more
138 // directly, as it could starve handling of foreign threads. Put every task 144 // directly, as it could starve handling of foreign threads. Put every task
139 // into this queue. 145 // into this queue.
140 146
141 // This should only be called while the lock is taken. 147 // Ensures |message_loop_| isn't destroyed while running.
142 incoming_queue_lock_.AssertAcquired(); 148 base::subtle::AutoReadLock hold_message_loop(message_loop_lock_);
143 149
144 if (!message_loop_) { 150 if (!message_loop_) {
145 pending_task->task.Reset(); 151 pending_task->task.Reset();
146 return false; 152 return false;
147 } 153 }
148 154
149 // Initialize the sequence number. The sequence number is used for delayed 155 bool schedule_work = false;
150 // tasks (to facilitate FIFO sorting when two tasks have the same 156 {
151 // delayed_run_time value) and for identifying the task in about:tracing. 157 AutoLock hold(incoming_queue_lock_);
152 pending_task->sequence_num = next_sequence_num_++;
153 158
154 message_loop_->task_annotator()->DidQueueTask("MessageLoop::PostTask", 159 #if defined(OS_WIN)
155 *pending_task); 160 if (pending_task->is_high_res)
161 ++high_res_task_count_;
162 #endif
156 163
157 bool was_empty = incoming_queue_.empty(); 164 // Initialize the sequence number. The sequence number is used for delayed
158 incoming_queue_.push(*pending_task); 165 // tasks (to facilitate FIFO sorting when two tasks have the same
159 pending_task->task.Reset(); 166 // delayed_run_time value) and for identifying the task in about:tracing.
167 pending_task->sequence_num = next_sequence_num_++;
160 168
161 if (is_ready_for_scheduling_ && 169 message_loop_->task_annotator()->DidQueueTask("MessageLoop::PostTask",
162 (always_schedule_work_ || (!message_loop_scheduled_ && was_empty))) { 170 *pending_task);
163 ScheduleWork(); 171
172 bool was_empty = incoming_queue_.empty();
173 incoming_queue_.push(std::move(*pending_task));
174
175 if (is_ready_for_scheduling_ &&
176 (always_schedule_work_ || (!message_loop_scheduled_ && was_empty))) {
177 schedule_work = true;
178 // After we've scheduled the message loop, we do not need to do so again
179 // until we know it has processed all of the work in our queue and is
180 // waiting for more work again. The message loop will always attempt to
181 // reload from the incoming queue before waiting again so we clear this
182 // flag in ReloadWorkQueue().
183 message_loop_scheduled_ = true;
184 }
164 } 185 }
165 186
187 // Wake up the message loop and schedule work. This is done outside
188 // |incoming_queue_lock_| because signaling the message loop may cause this
189 // thread to be switched. If |incoming_queue_lock_| is held, any other thread
190 // that wants to post a task will be blocked until this thread switches back
191 // in and releases |incoming_queue_lock_|.
192 if (schedule_work)
193 message_loop_->ScheduleWork();
194
166 return true; 195 return true;
167 } 196 }
168 197
169 void IncomingTaskQueue::ScheduleWork() {
170 DCHECK(is_ready_for_scheduling_);
171 // Wake up the message loop.
172 message_loop_->ScheduleWork();
173 // After we've scheduled the message loop, we do not need to do so again
174 // until we know it has processed all of the work in our queue and is
175 // waiting for more work again. The message loop will always attempt to
176 // reload from the incoming queue before waiting again so we clear this flag
177 // in ReloadWorkQueue().
178 message_loop_scheduled_ = true;
179 }
180
181 } // namespace internal 198 } // namespace internal
182 } // namespace base 199 } // namespace base
OLDNEW
« no previous file with comments | « base/message_loop/incoming_task_queue.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698