OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 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_pump_win.h" | 5 #include "base/message_pump_win.h" |
6 | 6 |
7 #include <math.h> | 7 #include <math.h> |
8 | 8 |
9 #include "base/debug/trace_event.h" | 9 #include "base/debug/trace_event.h" |
10 #include "base/message_loop.h" | 10 #include "base/message_loop.h" |
11 #include "base/metrics/histogram.h" | 11 #include "base/metrics/histogram.h" |
12 #include "base/process_util.h" | |
13 #include "base/win/wrapped_window_proc.h" | |
14 | 12 |
15 namespace { | 13 namespace { |
16 | 14 |
17 enum MessageLoopProblems { | 15 enum MessageLoopProblems { |
18 MESSAGE_POST_ERROR, | 16 MESSAGE_POST_ERROR, |
19 COMPLETION_POST_ERROR, | 17 COMPLETION_POST_ERROR, |
20 SET_TIMER_ERROR, | 18 SET_TIMER_ERROR, |
21 MESSAGE_LOOP_PROBLEM_MAX, | 19 MESSAGE_LOOP_PROBLEM_MAX, |
22 }; | 20 }; |
23 | 21 |
22 // Possible states of the message pump: | |
23 // - kPumpIdle: the thread is sleeping, waiting for work to be posted. | |
24 // - kPumpHaveWork: a window message or completion packet has been posted. | |
25 // - kPumpDisabled: the pump is disabled, no more work can be posted. | |
26 static const LONG kPumpIdle = 0; | |
27 static const LONG kPumpHaveWork = 1; | |
28 static const LONG kPumpDisabled = 2; | |
29 | |
30 VOID CALLBACK DummyApc(ULONG_PTR) {} | |
Wez
2013/06/06 20:20:33
nit: Add a comment explaining what this dummy is u
alexeypa (please no reviews)
2013/06/07 15:33:09
Done.
| |
31 | |
24 } // namespace | 32 } // namespace |
25 | 33 |
26 namespace base { | 34 namespace base { |
27 | 35 |
28 static const wchar_t kWndClass[] = L"Chrome_MessagePumpWindow"; | |
29 | |
30 // Message sent to get an additional time slice for pumping (processing) another | 36 // Message sent to get an additional time slice for pumping (processing) another |
31 // task (a series of such messages creates a continuous task pump). | 37 // task (a series of such messages creates a continuous task pump). |
32 static const int kMsgHaveWork = WM_USER + 1; | 38 static const int kMsgHaveWork = WM_USER + 1; |
33 | 39 |
40 // Used by MessagePumpUI to wake up the thread and check any pending timers. | |
41 static const int kTimerId = 1; | |
42 | |
34 //----------------------------------------------------------------------------- | 43 //----------------------------------------------------------------------------- |
35 // MessagePumpWin public: | 44 // MessagePumpWin public: |
36 | 45 |
46 MessagePumpWin::MessagePumpWin() : pump_state_(kPumpIdle), state_(NULL) {} | |
47 | |
37 void MessagePumpWin::AddObserver(MessagePumpObserver* observer) { | 48 void MessagePumpWin::AddObserver(MessagePumpObserver* observer) { |
38 observers_.AddObserver(observer); | 49 observers_.AddObserver(observer); |
39 } | 50 } |
40 | 51 |
41 void MessagePumpWin::RemoveObserver(MessagePumpObserver* observer) { | 52 void MessagePumpWin::RemoveObserver(MessagePumpObserver* observer) { |
42 observers_.RemoveObserver(observer); | 53 observers_.RemoveObserver(observer); |
43 } | 54 } |
44 | 55 |
45 void MessagePumpWin::WillProcessMessage(const MSG& msg) { | 56 void MessagePumpWin::WillProcessMessage(const MSG& msg) { |
46 FOR_EACH_OBSERVER(MessagePumpObserver, observers_, WillProcessEvent(msg)); | 57 FOR_EACH_OBSERVER(MessagePumpObserver, observers_, WillProcessEvent(msg)); |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
89 if (delay < 0) | 100 if (delay < 0) |
90 delay = 0; | 101 delay = 0; |
91 | 102 |
92 return delay; | 103 return delay; |
93 } | 104 } |
94 | 105 |
95 //----------------------------------------------------------------------------- | 106 //----------------------------------------------------------------------------- |
96 // MessagePumpForUI public: | 107 // MessagePumpForUI public: |
97 | 108 |
98 MessagePumpForUI::MessagePumpForUI() | 109 MessagePumpForUI::MessagePumpForUI() |
99 : instance_(NULL), | 110 : message_filter_(new MessageFilter), |
100 message_filter_(new MessageFilter) { | 111 window_(new win::MessageWindow()) { |
101 InitMessageWnd(); | 112 CHECK(DuplicateHandle(GetCurrentProcess(), |
113 GetCurrentThread(), | |
114 GetCurrentProcess(), | |
115 thread_.Receive(), | |
116 THREAD_SET_CONTEXT, | |
117 FALSE, // no not inherit this handle | |
118 0)); | |
119 CHECK(window_->Create(this)); | |
102 } | 120 } |
103 | 121 |
104 MessagePumpForUI::~MessagePumpForUI() { | 122 MessagePumpForUI::~MessagePumpForUI() { |
105 DestroyWindow(message_hwnd_); | |
106 UnregisterClass(kWndClass, instance_); | |
107 } | 123 } |
108 | 124 |
109 void MessagePumpForUI::ScheduleWork() { | 125 void MessagePumpForUI::ScheduleWork() { |
110 if (InterlockedExchange(&have_work_, 1)) | 126 if (InterlockedCompareExchange(&pump_state_, kPumpHaveWork, |
111 return; // Someone else continued the pumping. | 127 kPumpIdle) != kPumpIdle) { |
128 // Either someone else continued the pumping or the pump is disabled. | |
129 return; | |
130 } | |
112 | 131 |
113 // Make sure the MessagePump does some work for us. | 132 // Make sure the MessagePump does some work for us. |
114 BOOL ret = PostMessage(message_hwnd_, kMsgHaveWork, | 133 if (PostMessage(window_->hwnd(), kMsgHaveWork, 0, 0)) |
115 reinterpret_cast<WPARAM>(this), 0); | 134 return; |
116 if (ret) | |
117 return; // There was room in the Window Message queue. | |
118 | 135 |
119 // We have failed to insert a have-work message, so there is a chance that we | 136 // We have failed to insert a have-work message, so there is a chance that we |
120 // will starve tasks/timers while sitting in a nested message loop. Nested | 137 // will starve tasks/timers while sitting in a nested message loop. Nested |
121 // loops only look at Windows Message queues, and don't look at *our* task | 138 // loops only look at Windows Message queues, and don't look at *our* task |
122 // queues, etc., so we might not get a time slice in such. :-( | 139 // queues, etc., so we might not get a time slice in such. :-( |
123 // We could abort here, but the fear is that this failure mode is plausibly | 140 // We could abort here, but the fear is that this failure mode is plausibly |
124 // common (queue is full, of about 2000 messages), so we'll do a near-graceful | 141 // common (queue is full, of about 2000 messages), so we'll do a near-graceful |
125 // recovery. Nested loops are pretty transient (we think), so this will | 142 // recovery. Nested loops are pretty transient (we think), so this will |
126 // probably be recoverable. | 143 // probably be recoverable. |
127 InterlockedExchange(&have_work_, 0); // Clarify that we didn't really insert. | |
128 UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", MESSAGE_POST_ERROR, | 144 UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", MESSAGE_POST_ERROR, |
129 MESSAGE_LOOP_PROBLEM_MAX); | 145 MESSAGE_LOOP_PROBLEM_MAX); |
146 | |
147 // Clarify that we didn't really insert. | |
148 InterlockedExchange(&pump_state_, kPumpIdle); | |
149 | |
150 // Try to wake up the message loop thread by posting an APC. This might not | |
151 // work while a nested loop still running (see the comment above) but this | |
152 // will unblock WillDestroyCurrentMessageLoop() if it is waiting for | |
153 // ScheduleWork() to complete. | |
154 // | |
155 // According to the UMA metrics posting an I/O completing packet has very low | |
Wez
2013/06/06 20:20:33
typo: completing -> completion
alexeypa (please no reviews)
2013/06/07 15:33:09
Done.
| |
156 // error rate. Queuing an APC hits roughly the same path in the kernel so | |
157 // the error rate should be low as well. Given that we do it only when | |
158 // PostMessage() fails it should be safe to CHECK() here. | |
159 CHECK(QueueUserAPC(&DummyApc, thread_, 0)); | |
130 } | 160 } |
131 | 161 |
132 void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { | 162 void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { |
163 DCHECK(window_->CalledOnValidThread()); | |
164 | |
133 // | 165 // |
134 // We would *like* to provide high resolution timers. Windows timers using | 166 // We would *like* to provide high resolution timers. Windows timers using |
135 // SetTimer() have a 10ms granularity. We have to use WM_TIMER as a wakeup | 167 // SetTimer() have a 10ms granularity. We have to use WM_TIMER as a wakeup |
136 // mechanism because the application can enter modal windows loops where it | 168 // mechanism because the application can enter modal windows loops where it |
137 // is not running our MessageLoop; the only way to have our timers fire in | 169 // is not running our MessageLoop; the only way to have our timers fire in |
138 // these cases is to post messages there. | 170 // these cases is to post messages there. |
139 // | 171 // |
140 // To provide sub-10ms timers, we process timers directly from our run loop. | 172 // To provide sub-10ms timers, we process timers directly from our run loop. |
141 // For the common case, timers will be processed there as the run loop does | 173 // For the common case, timers will be processed there as the run loop does |
142 // its normal work. However, we *also* set the system timer so that WM_TIMER | 174 // its normal work. However, we *also* set the system timer so that WM_TIMER |
143 // events fire. This mops up the case of timers not being able to work in | 175 // events fire. This mops up the case of timers not being able to work in |
144 // modal message loops. It is possible for the SetTimer to pop and have no | 176 // modal message loops. It is possible for the SetTimer to pop and have no |
145 // pending timers, because they could have already been processed by the | 177 // pending timers, because they could have already been processed by the |
146 // run loop itself. | 178 // run loop itself. |
147 // | 179 // |
148 // We use a single SetTimer corresponding to the timer that will expire | 180 // We use a single SetTimer corresponding to the timer that will expire |
149 // soonest. As new timers are created and destroyed, we update SetTimer. | 181 // soonest. As new timers are created and destroyed, we update SetTimer. |
150 // Getting a spurrious SetTimer event firing is benign, as we'll just be | 182 // Getting a spurrious SetTimer event firing is benign, as we'll just be |
151 // processing an empty timer queue. | 183 // processing an empty timer queue. |
152 // | 184 // |
153 delayed_work_time_ = delayed_work_time; | 185 delayed_work_time_ = delayed_work_time; |
154 | 186 |
155 int delay_msec = GetCurrentDelay(); | 187 int delay_msec = GetCurrentDelay(); |
156 DCHECK_GE(delay_msec, 0); | 188 DCHECK_GE(delay_msec, 0); |
157 if (delay_msec < USER_TIMER_MINIMUM) | 189 if (delay_msec < USER_TIMER_MINIMUM) |
158 delay_msec = USER_TIMER_MINIMUM; | 190 delay_msec = USER_TIMER_MINIMUM; |
159 | 191 |
160 // Create a WM_TIMER event that will wake us up to check for any pending | 192 // Create a WM_TIMER event that will wake us up to check for any pending |
161 // timers (in case we are running within a nested, external sub-pump). | 193 // timers (in case we are running within a nested, external sub-pump). |
162 BOOL ret = SetTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this), | 194 BOOL ret = SetTimer(window_->hwnd(), kTimerId, delay_msec, NULL); |
163 delay_msec, NULL); | |
164 if (ret) | 195 if (ret) |
165 return; | 196 return; |
166 // If we can't set timers, we are in big trouble... but cross our fingers for | 197 // If we can't set timers, we are in big trouble... but cross our fingers for |
167 // now. | 198 // now. |
168 // TODO(jar): If we don't see this error, use a CHECK() here instead. | 199 // TODO(jar): If we don't see this error, use a CHECK() here instead. |
169 UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", SET_TIMER_ERROR, | 200 UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", SET_TIMER_ERROR, |
170 MESSAGE_LOOP_PROBLEM_MAX); | 201 MESSAGE_LOOP_PROBLEM_MAX); |
171 } | 202 } |
172 | 203 |
204 void MessagePumpForUI::WillDestroyCurrentMessageLoop() { | |
205 DCHECK(window_->CalledOnValidThread()); | |
206 | |
207 // Disable the message pump. If |pump_state_ == kPumpHaveWork| then | |
208 // ScheduleWork() might be still running on a different thread. Wait until | |
209 // |kMsgHaveWork| is received or |pump_state_| is reset back to |kPumpIdle|. | |
210 while (InterlockedCompareExchange(&pump_state_, kPumpDisabled, | |
211 kPumpIdle) == kPumpHaveWork) { | |
Wez
2013/06/06 20:20:33
Shouldn't this be != kPumpDisabled?
alexeypa (please no reviews)
2013/06/07 15:33:09
No. In most cases it will switch |pump_state_| fro
| |
212 MSG msg; | |
213 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { | |
214 if (msg.message == kMsgHaveWork && msg.hwnd == window_->hwnd()) { | |
215 // Now that we received |kMsgHaveWork| the pump can be safely disabled. | |
216 InterlockedExchange(&pump_state_, kPumpDisabled); | |
217 break; | |
218 } | |
219 } | |
220 | |
221 // Wait until |kMsgHaveWork| is posted or an APC is received. | |
222 WaitForWork(); | |
223 } | |
224 | |
225 // At this point the pump is diabled and other threads exited ScheduleWork(). | |
Wez
2013/06/06 20:20:33
typo: disabled
alexeypa (please no reviews)
2013/06/07 15:33:09
Done.
| |
226 window_.reset(); | |
227 } | |
228 | |
173 void MessagePumpForUI::PumpOutPendingPaintMessages() { | 229 void MessagePumpForUI::PumpOutPendingPaintMessages() { |
174 // If we are being called outside of the context of Run, then don't try to do | 230 // If we are being called outside of the context of Run, then don't try to do |
175 // any work. | 231 // any work. |
176 if (!state_) | 232 if (!state_) |
177 return; | 233 return; |
178 | 234 |
179 // Create a mini-message-pump to force immediate processing of only Windows | 235 // Create a mini-message-pump to force immediate processing of only Windows |
180 // WM_PAINT messages. Don't provide an infinite loop, but do enough peeking | 236 // WM_PAINT messages. Don't provide an infinite loop, but do enough peeking |
181 // to get the job done. Actual common max is 4 peeks, but we'll be a little | 237 // to get the job done. Actual common max is 4 peeks, but we'll be a little |
182 // safe here. | 238 // safe here. |
183 const int kMaxPeekCount = 20; | 239 const int kMaxPeekCount = 20; |
184 int peek_count; | 240 int peek_count; |
185 for (peek_count = 0; peek_count < kMaxPeekCount; ++peek_count) { | 241 for (peek_count = 0; peek_count < kMaxPeekCount; ++peek_count) { |
186 MSG msg; | 242 MSG msg; |
187 if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE | PM_QS_PAINT)) | 243 if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE | PM_QS_PAINT)) |
188 break; | 244 break; |
189 ProcessMessageHelper(msg); | 245 ProcessMessageHelper(msg); |
190 if (state_->should_quit) // Handle WM_QUIT. | 246 if (state_->should_quit) // Handle WM_QUIT. |
191 break; | 247 break; |
192 } | 248 } |
193 // Histogram what was really being used, to help to adjust kMaxPeekCount. | 249 // Histogram what was really being used, to help to adjust kMaxPeekCount. |
194 DHISTOGRAM_COUNTS("Loop.PumpOutPendingPaintMessages Peeks", peek_count); | 250 DHISTOGRAM_COUNTS("Loop.PumpOutPendingPaintMessages Peeks", peek_count); |
195 } | 251 } |
196 | 252 |
197 //----------------------------------------------------------------------------- | 253 //----------------------------------------------------------------------------- |
198 // MessagePumpForUI private: | 254 // MessagePumpForUI private: |
199 | 255 |
200 // static | 256 bool MessagePumpForUI::HandleMessage(HWND hwnd, |
201 LRESULT CALLBACK MessagePumpForUI::WndProcThunk( | 257 UINT message, |
202 HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { | 258 WPARAM wparam, |
259 LPARAM lparam, | |
260 LRESULT* result) { | |
203 switch (message) { | 261 switch (message) { |
204 case kMsgHaveWork: | 262 case kMsgHaveWork: |
205 reinterpret_cast<MessagePumpForUI*>(wparam)->HandleWorkMessage(); | 263 HandleWorkMessage(); |
206 break; | 264 break; |
265 | |
207 case WM_TIMER: | 266 case WM_TIMER: |
208 reinterpret_cast<MessagePumpForUI*>(wparam)->HandleTimerMessage(); | 267 HandleTimerMessage(); |
209 break; | 268 break; |
210 } | 269 } |
211 return DefWindowProc(hwnd, message, wparam, lparam); | 270 |
271 // Do default processing for all messages. | |
272 return false; | |
212 } | 273 } |
213 | 274 |
214 void MessagePumpForUI::DoRunLoop() { | 275 void MessagePumpForUI::DoRunLoop() { |
276 DCHECK(window_->CalledOnValidThread()); | |
277 | |
215 // IF this was just a simple PeekMessage() loop (servicing all possible work | 278 // IF this was just a simple PeekMessage() loop (servicing all possible work |
216 // queues), then Windows would try to achieve the following order according | 279 // queues), then Windows would try to achieve the following order according |
217 // to MSDN documentation about PeekMessage with no filter): | 280 // to MSDN documentation about PeekMessage with no filter): |
218 // * Sent messages | 281 // * Sent messages |
219 // * Posted messages | 282 // * Posted messages |
220 // * Sent messages (again) | 283 // * Sent messages (again) |
221 // * WM_PAINT messages | 284 // * WM_PAINT messages |
222 // * WM_TIMER messages | 285 // * WM_TIMER messages |
223 // | 286 // |
224 // Summary: none of the above classes is starved, and sent messages has twice | 287 // Summary: none of the above classes is starved, and sent messages has twice |
(...skipping 17 matching lines...) Expand all Loading... | |
242 if (state_->should_quit) | 305 if (state_->should_quit) |
243 break; | 306 break; |
244 | 307 |
245 more_work_is_plausible |= | 308 more_work_is_plausible |= |
246 state_->delegate->DoDelayedWork(&delayed_work_time_); | 309 state_->delegate->DoDelayedWork(&delayed_work_time_); |
247 // If we did not process any delayed work, then we can assume that our | 310 // If we did not process any delayed work, then we can assume that our |
248 // existing WM_TIMER if any will fire when delayed work should run. We | 311 // existing WM_TIMER if any will fire when delayed work should run. We |
249 // don't want to disturb that timer if it is already in flight. However, | 312 // don't want to disturb that timer if it is already in flight. However, |
250 // if we did do all remaining delayed work, then lets kill the WM_TIMER. | 313 // if we did do all remaining delayed work, then lets kill the WM_TIMER. |
251 if (more_work_is_plausible && delayed_work_time_.is_null()) | 314 if (more_work_is_plausible && delayed_work_time_.is_null()) |
252 KillTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this)); | 315 KillTimer(window_->hwnd(), kTimerId); |
253 if (state_->should_quit) | 316 if (state_->should_quit) |
254 break; | 317 break; |
255 | 318 |
256 if (more_work_is_plausible) | 319 if (more_work_is_plausible) |
257 continue; | 320 continue; |
258 | 321 |
259 more_work_is_plausible = state_->delegate->DoIdleWork(); | 322 more_work_is_plausible = state_->delegate->DoIdleWork(); |
260 if (state_->should_quit) | 323 if (state_->should_quit) |
261 break; | 324 break; |
262 | 325 |
263 if (more_work_is_plausible) | 326 if (more_work_is_plausible) |
264 continue; | 327 continue; |
265 | 328 |
266 WaitForWork(); // Wait (sleep) until we have work to do again. | 329 WaitForWork(); // Wait (sleep) until we have work to do again. |
267 } | 330 } |
268 } | 331 } |
269 | 332 |
270 void MessagePumpForUI::InitMessageWnd() { | |
271 WNDCLASSEX wc = {0}; | |
272 wc.cbSize = sizeof(wc); | |
273 wc.lpfnWndProc = base::win::WrappedWindowProc<WndProcThunk>; | |
274 wc.hInstance = base::GetModuleFromAddress(wc.lpfnWndProc); | |
275 wc.lpszClassName = kWndClass; | |
276 instance_ = wc.hInstance; | |
277 RegisterClassEx(&wc); | |
278 | |
279 message_hwnd_ = | |
280 CreateWindow(kWndClass, 0, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, instance_, 0); | |
281 DCHECK(message_hwnd_); | |
282 } | |
283 | |
284 void MessagePumpForUI::WaitForWork() { | 333 void MessagePumpForUI::WaitForWork() { |
285 // Wait until a message is available, up to the time needed by the timer | 334 // Wait until a message is available, up to the time needed by the timer |
286 // manager to fire the next set of timers. | 335 // manager to fire the next set of timers. |
287 int delay = GetCurrentDelay(); | 336 int delay = GetCurrentDelay(); |
288 if (delay < 0) // Negative value means no timers waiting. | 337 if (delay < 0) // Negative value means no timers waiting. |
289 delay = INFINITE; | 338 delay = INFINITE; |
290 | 339 |
291 DWORD result; | 340 DWORD result; |
292 result = MsgWaitForMultipleObjectsEx(0, NULL, delay, QS_ALLINPUT, | 341 result = MsgWaitForMultipleObjectsEx(0, NULL, delay, QS_ALLINPUT, |
293 MWMO_INPUTAVAILABLE); | 342 MWMO_ALERTABLE | MWMO_INPUTAVAILABLE); |
294 | 343 |
295 if (WAIT_OBJECT_0 == result) { | 344 if (WAIT_OBJECT_0 == result) { |
296 // A WM_* message is available. | 345 // A WM_* message is available. |
297 // If a parent child relationship exists between windows across threads | 346 // If a parent child relationship exists between windows across threads |
298 // then their thread inputs are implicitly attached. | 347 // then their thread inputs are implicitly attached. |
299 // This causes the MsgWaitForMultipleObjectsEx API to return indicating | 348 // This causes the MsgWaitForMultipleObjectsEx API to return indicating |
300 // that messages are ready for processing (Specifically, mouse messages | 349 // that messages are ready for processing (Specifically, mouse messages |
301 // intended for the child window may appear if the child window has | 350 // intended for the child window may appear if the child window has |
302 // capture). | 351 // capture). |
303 // The subsequent PeekMessages call may fail to return any messages thus | 352 // The subsequent PeekMessages call may fail to return any messages thus |
304 // causing us to enter a tight loop at times. | 353 // causing us to enter a tight loop at times. |
305 // The WaitMessage call below is a workaround to give the child window | 354 // The WaitMessage call below is a workaround to give the child window |
306 // some time to process its input messages. | 355 // some time to process its input messages. |
307 MSG msg = {0}; | 356 MSG msg = {0}; |
308 DWORD queue_status = GetQueueStatus(QS_MOUSE); | 357 DWORD queue_status = GetQueueStatus(QS_MOUSE); |
309 if (HIWORD(queue_status) & QS_MOUSE && | 358 if (HIWORD(queue_status) & QS_MOUSE && |
310 !PeekMessage(&msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST, PM_NOREMOVE)) { | 359 !PeekMessage(&msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST, PM_NOREMOVE)) { |
311 WaitMessage(); | 360 WaitMessage(); |
312 } | 361 } |
313 return; | 362 return; |
363 } else if (WAIT_IO_COMPLETION == result) { | |
364 return; | |
Wez
2013/06/06 20:20:33
nit: Add a comment explaining that we just use IO_
alexeypa (please no reviews)
2013/06/07 15:33:09
Done.
| |
314 } | 365 } |
315 | 366 |
316 DCHECK_NE(WAIT_FAILED, result) << GetLastError(); | 367 DCHECK_NE(WAIT_FAILED, result) << GetLastError(); |
317 } | 368 } |
318 | 369 |
319 void MessagePumpForUI::HandleWorkMessage() { | 370 void MessagePumpForUI::HandleWorkMessage() { |
320 // If we are being called outside of the context of Run, then don't try to do | 371 // If we are being called outside of the context of Run, then don't try to do |
321 // any work. This could correspond to a MessageBox call or something of that | 372 // any work. This could correspond to a MessageBox call or something of that |
322 // sort. | 373 // sort. |
323 if (!state_) { | 374 if (!state_) { |
324 // Since we handled a kMsgHaveWork message, we must still update this flag. | 375 // Since we handled a kMsgHaveWork message, we must still update this flag. |
325 InterlockedExchange(&have_work_, 0); | 376 InterlockedExchange(&pump_state_, kPumpIdle); |
326 return; | 377 return; |
327 } | 378 } |
328 | 379 |
329 // Let whatever would have run had we not been putting messages in the queue | 380 // Let whatever would have run had we not been putting messages in the queue |
330 // run now. This is an attempt to make our dummy message not starve other | 381 // run now. This is an attempt to make our dummy message not starve other |
331 // messages that may be in the Windows message queue. | 382 // messages that may be in the Windows message queue. |
332 ProcessPumpReplacementMessage(); | 383 ProcessPumpReplacementMessage(); |
333 | 384 |
334 // Now give the delegate a chance to do some work. He'll let us know if he | 385 // Now give the delegate a chance to do some work. He'll let us know if he |
335 // needs to do more work. | 386 // needs to do more work. |
336 if (state_->delegate->DoWork()) | 387 if (state_->delegate->DoWork()) |
337 ScheduleWork(); | 388 ScheduleWork(); |
338 } | 389 } |
339 | 390 |
340 void MessagePumpForUI::HandleTimerMessage() { | 391 void MessagePumpForUI::HandleTimerMessage() { |
341 KillTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this)); | 392 KillTimer(window_->hwnd(), kTimerId); |
342 | 393 |
343 // If we are being called outside of the context of Run, then don't do | 394 // If we are being called outside of the context of Run, then don't do |
344 // anything. This could correspond to a MessageBox call or something of | 395 // anything. This could correspond to a MessageBox call or something of |
345 // that sort. | 396 // that sort. |
346 if (!state_) | 397 if (!state_) |
347 return; | 398 return; |
348 | 399 |
349 state_->delegate->DoDelayedWork(&delayed_work_time_); | 400 state_->delegate->DoDelayedWork(&delayed_work_time_); |
350 if (!delayed_work_time_.is_null()) { | 401 if (!delayed_work_time_.is_null()) { |
351 // A bit gratuitous to set delayed_work_time_ again, but oh well. | 402 // A bit gratuitous to set delayed_work_time_ again, but oh well. |
(...skipping 23 matching lines...) Expand all Loading... | |
375 "message", msg.message); | 426 "message", msg.message); |
376 if (WM_QUIT == msg.message) { | 427 if (WM_QUIT == msg.message) { |
377 // Repost the QUIT message so that it will be retrieved by the primary | 428 // Repost the QUIT message so that it will be retrieved by the primary |
378 // GetMessage() loop. | 429 // GetMessage() loop. |
379 state_->should_quit = true; | 430 state_->should_quit = true; |
380 PostQuitMessage(static_cast<int>(msg.wParam)); | 431 PostQuitMessage(static_cast<int>(msg.wParam)); |
381 return false; | 432 return false; |
382 } | 433 } |
383 | 434 |
384 // While running our main message pump, we discard kMsgHaveWork messages. | 435 // While running our main message pump, we discard kMsgHaveWork messages. |
385 if (msg.message == kMsgHaveWork && msg.hwnd == message_hwnd_) | 436 if (msg.message == kMsgHaveWork && msg.hwnd == window_->hwnd()) |
386 return ProcessPumpReplacementMessage(); | 437 return ProcessPumpReplacementMessage(); |
387 | 438 |
388 if (CallMsgFilter(const_cast<MSG*>(&msg), kMessageFilterCode)) | 439 if (CallMsgFilter(const_cast<MSG*>(&msg), kMessageFilterCode)) |
389 return true; | 440 return true; |
390 | 441 |
391 WillProcessMessage(msg); | 442 WillProcessMessage(msg); |
392 | 443 |
393 if (!message_filter_->ProcessMessage(msg)) { | 444 if (!message_filter_->ProcessMessage(msg)) { |
394 if (state_->dispatcher) { | 445 if (state_->dispatcher) { |
395 if (!state_->dispatcher->Dispatch(msg)) | 446 if (!state_->dispatcher->Dispatch(msg)) |
396 state_->should_quit = true; | 447 state_->should_quit = true; |
397 } else { | 448 } else { |
398 TranslateMessage(&msg); | 449 TranslateMessage(&msg); |
399 DispatchMessage(&msg); | 450 DispatchMessage(&msg); |
400 } | 451 } |
401 } | 452 } |
402 | 453 |
403 DidProcessMessage(msg); | 454 DidProcessMessage(msg); |
404 return true; | 455 return true; |
405 } | 456 } |
406 | 457 |
407 bool MessagePumpForUI::ProcessPumpReplacementMessage() { | 458 bool MessagePumpForUI::ProcessPumpReplacementMessage() { |
408 // When we encounter a kMsgHaveWork message, this method is called to peek | 459 // When we encounter a kMsgHaveWork message, this method is called to peek |
409 // and process a replacement message, such as a WM_PAINT or WM_TIMER. The | 460 // and process a replacement message, such as a WM_PAINT or WM_TIMER. The |
410 // goal is to make the kMsgHaveWork as non-intrusive as possible, even though | 461 // goal is to make the kMsgHaveWork as non-intrusive as possible, even though |
411 // a continuous stream of such messages are posted. This method carefully | 462 // a continuous stream of such messages are posted. This method carefully |
412 // peeks a message while there is no chance for a kMsgHaveWork to be pending, | 463 // peeks a message while there is no chance for a kMsgHaveWork to be pending, |
413 // then resets the have_work_ flag (allowing a replacement kMsgHaveWork to | 464 // then resets the pump_state_ flag (allowing a replacement kMsgHaveWork to |
414 // possibly be posted), and finally dispatches that peeked replacement. Note | 465 // possibly be posted), and finally dispatches that peeked replacement. Note |
415 // that the re-post of kMsgHaveWork may be asynchronous to this thread!! | 466 // that the re-post of kMsgHaveWork may be asynchronous to this thread!! |
416 | 467 |
417 bool have_message = false; | 468 bool have_message = false; |
418 MSG msg; | 469 MSG msg; |
419 // We should not process all window messages if we are in the context of an | 470 // We should not process all window messages if we are in the context of an |
420 // OS modal loop, i.e. in the context of a windows API call like MessageBox. | 471 // OS modal loop, i.e. in the context of a windows API call like MessageBox. |
421 // This is to ensure that these messages are peeked out by the OS modal loop. | 472 // This is to ensure that these messages are peeked out by the OS modal loop. |
422 if (MessageLoop::current()->os_modal_loop()) { | 473 if (MessageLoop::current()->os_modal_loop()) { |
423 // We only peek out WM_PAINT and WM_TIMER here for reasons mentioned above. | 474 // We only peek out WM_PAINT and WM_TIMER here for reasons mentioned above. |
424 have_message = PeekMessage(&msg, NULL, WM_PAINT, WM_PAINT, PM_REMOVE) || | 475 have_message = PeekMessage(&msg, NULL, WM_PAINT, WM_PAINT, PM_REMOVE) || |
425 PeekMessage(&msg, NULL, WM_TIMER, WM_TIMER, PM_REMOVE); | 476 PeekMessage(&msg, NULL, WM_TIMER, WM_TIMER, PM_REMOVE); |
426 } else { | 477 } else { |
427 have_message = !!message_filter_->DoPeekMessage(&msg, NULL, 0, 0, | 478 have_message = !!message_filter_->DoPeekMessage(&msg, NULL, 0, 0, |
428 PM_REMOVE); | 479 PM_REMOVE); |
429 } | 480 } |
430 | 481 |
431 DCHECK(!have_message || kMsgHaveWork != msg.message || | 482 DCHECK(!have_message || kMsgHaveWork != msg.message || |
432 msg.hwnd != message_hwnd_); | 483 msg.hwnd != window_->hwnd()); |
433 | 484 |
434 // Since we discarded a kMsgHaveWork message, we must update the flag. | 485 // Since we discarded a kMsgHaveWork message, we must update the flag. |
435 int old_have_work = InterlockedExchange(&have_work_, 0); | 486 int old_pump_state = InterlockedExchange(&pump_state_, kPumpIdle); |
436 DCHECK(old_have_work); | 487 DCHECK(old_pump_state); |
437 | 488 |
438 // We don't need a special time slice if we didn't have_message to process. | 489 // We don't need a special time slice if we didn't have_message to process. |
439 if (!have_message) | 490 if (!have_message) |
440 return false; | 491 return false; |
441 | 492 |
442 // Guarantee we'll get another time slice in the case where we go into native | 493 // Guarantee we'll get another time slice in the case where we go into native |
443 // windows code. This ScheduleWork() may hurt performance a tiny bit when | 494 // windows code. This ScheduleWork() may hurt performance a tiny bit when |
444 // tasks appear very infrequently, but when the event queue is busy, the | 495 // tasks appear very infrequently, but when the event queue is busy, the |
445 // kMsgHaveWork events get (percentage wise) rarer and rarer. | 496 // kMsgHaveWork events get (percentage wise) rarer and rarer. |
446 ScheduleWork(); | 497 ScheduleWork(); |
447 return ProcessMessageHelper(msg); | 498 return ProcessMessageHelper(msg); |
448 } | 499 } |
449 | 500 |
450 void MessagePumpForUI::SetMessageFilter( | 501 void MessagePumpForUI::SetMessageFilter( |
451 scoped_ptr<MessageFilter> message_filter) { | 502 scoped_ptr<MessageFilter> message_filter) { |
452 message_filter_ = message_filter.Pass(); | 503 message_filter_ = message_filter.Pass(); |
453 } | 504 } |
454 | 505 |
455 //----------------------------------------------------------------------------- | 506 //----------------------------------------------------------------------------- |
456 // MessagePumpForIO public: | 507 // MessagePumpForIO public: |
457 | 508 |
458 MessagePumpForIO::MessagePumpForIO() { | 509 MessagePumpForIO::MessagePumpForIO() { |
459 port_.Set(CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, NULL, 1)); | 510 port_.Set(CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, NULL, 1)); |
460 DCHECK(port_.IsValid()); | 511 DCHECK(port_.IsValid()); |
461 } | 512 } |
462 | 513 |
463 void MessagePumpForIO::ScheduleWork() { | 514 void MessagePumpForIO::ScheduleWork() { |
464 if (InterlockedExchange(&have_work_, 1)) | 515 if (InterlockedExchange(&pump_state_, kPumpHaveWork)) |
465 return; // Someone else continued the pumping. | 516 return; // Someone else continued the pumping. |
466 | 517 |
467 // Make sure the MessagePump does some work for us. | 518 // Make sure the MessagePump does some work for us. |
468 BOOL ret = PostQueuedCompletionStatus(port_, 0, | 519 BOOL ret = PostQueuedCompletionStatus(port_, 0, |
469 reinterpret_cast<ULONG_PTR>(this), | 520 reinterpret_cast<ULONG_PTR>(this), |
470 reinterpret_cast<OVERLAPPED*>(this)); | 521 reinterpret_cast<OVERLAPPED*>(this)); |
471 if (ret) | 522 if (ret) |
472 return; // Post worked perfectly. | 523 return; // Post worked perfectly. |
473 | 524 |
474 // See comment in MessagePumpForUI::ScheduleWork() for this error recovery. | 525 // See comment in MessagePumpForUI::ScheduleWork() for this error recovery. |
475 InterlockedExchange(&have_work_, 0); // Clarify that we didn't succeed. | 526 InterlockedExchange(&pump_state_, kPumpIdle); |
476 UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", COMPLETION_POST_ERROR, | 527 UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", COMPLETION_POST_ERROR, |
477 MESSAGE_LOOP_PROBLEM_MAX); | 528 MESSAGE_LOOP_PROBLEM_MAX); |
478 } | 529 } |
479 | 530 |
480 void MessagePumpForIO::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { | 531 void MessagePumpForIO::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { |
481 // We know that we can't be blocked right now since this method can only be | 532 // We know that we can't be blocked right now since this method can only be |
482 // called on the same thread as Run, so we only need to update our record of | 533 // called on the same thread as Run, so we only need to update our record of |
483 // how long to sleep when we do sleep. | 534 // how long to sleep when we do sleep. |
484 delayed_work_time_ = delayed_work_time; | 535 delayed_work_time_ = delayed_work_time; |
485 } | 536 } |
(...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
610 item->handler = KeyToHandler(key, &item->has_valid_io_context); | 661 item->handler = KeyToHandler(key, &item->has_valid_io_context); |
611 item->context = reinterpret_cast<IOContext*>(overlapped); | 662 item->context = reinterpret_cast<IOContext*>(overlapped); |
612 return true; | 663 return true; |
613 } | 664 } |
614 | 665 |
615 bool MessagePumpForIO::ProcessInternalIOItem(const IOItem& item) { | 666 bool MessagePumpForIO::ProcessInternalIOItem(const IOItem& item) { |
616 if (this == reinterpret_cast<MessagePumpForIO*>(item.context) && | 667 if (this == reinterpret_cast<MessagePumpForIO*>(item.context) && |
617 this == reinterpret_cast<MessagePumpForIO*>(item.handler)) { | 668 this == reinterpret_cast<MessagePumpForIO*>(item.handler)) { |
618 // This is our internal completion. | 669 // This is our internal completion. |
619 DCHECK(!item.bytes_transfered); | 670 DCHECK(!item.bytes_transfered); |
620 InterlockedExchange(&have_work_, 0); | 671 InterlockedExchange(&pump_state_, kPumpIdle); |
621 return true; | 672 return true; |
622 } | 673 } |
623 return false; | 674 return false; |
624 } | 675 } |
625 | 676 |
626 // Returns a completion item that was previously received. | 677 // Returns a completion item that was previously received. |
627 bool MessagePumpForIO::MatchCompletedIOItem(IOHandler* filter, IOItem* item) { | 678 bool MessagePumpForIO::MatchCompletedIOItem(IOHandler* filter, IOItem* item) { |
628 DCHECK(!completed_io_.empty()); | 679 DCHECK(!completed_io_.empty()); |
629 for (std::list<IOItem>::iterator it = completed_io_.begin(); | 680 for (std::list<IOItem>::iterator it = completed_io_.begin(); |
630 it != completed_io_.end(); ++it) { | 681 it != completed_io_.end(); ++it) { |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
671 | 722 |
672 // static | 723 // static |
673 MessagePumpForIO::IOHandler* MessagePumpForIO::KeyToHandler( | 724 MessagePumpForIO::IOHandler* MessagePumpForIO::KeyToHandler( |
674 ULONG_PTR key, | 725 ULONG_PTR key, |
675 bool* has_valid_io_context) { | 726 bool* has_valid_io_context) { |
676 *has_valid_io_context = ((key & 1) == 0); | 727 *has_valid_io_context = ((key & 1) == 0); |
677 return reinterpret_cast<IOHandler*>(key & ~static_cast<ULONG_PTR>(1)); | 728 return reinterpret_cast<IOHandler*>(key & ~static_cast<ULONG_PTR>(1)); |
678 } | 729 } |
679 | 730 |
680 } // namespace base | 731 } // namespace base |
OLD | NEW |