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 |
24 } // namespace | 22 } // namespace |
25 | 23 |
26 namespace base { | 24 namespace base { |
27 | 25 |
28 static const wchar_t kWndClass[] = L"Chrome_MessagePumpWindow"; | |
29 | |
30 // Message sent to get an additional time slice for pumping (processing) another | 26 // Message sent to get an additional time slice for pumping (processing) another |
31 // task (a series of such messages creates a continuous task pump). | 27 // task (a series of such messages creates a continuous task pump). |
32 static const int kMsgHaveWork = WM_USER + 1; | 28 static const int kMsgHaveWork = WM_USER + 1; |
33 | 29 |
| 30 // Used by MessagePumpUI to wake up the thread and check any pending timers. |
| 31 static const int kTimerId = 1; |
| 32 |
34 //----------------------------------------------------------------------------- | 33 //----------------------------------------------------------------------------- |
35 // MessagePumpWin public: | 34 // MessagePumpWin public: |
36 | 35 |
37 void MessagePumpWin::AddObserver(MessagePumpObserver* observer) { | 36 void MessagePumpWin::AddObserver(MessagePumpObserver* observer) { |
38 observers_.AddObserver(observer); | 37 observers_.AddObserver(observer); |
39 } | 38 } |
40 | 39 |
41 void MessagePumpWin::RemoveObserver(MessagePumpObserver* observer) { | 40 void MessagePumpWin::RemoveObserver(MessagePumpObserver* observer) { |
42 observers_.RemoveObserver(observer); | 41 observers_.RemoveObserver(observer); |
43 } | 42 } |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
89 if (delay < 0) | 88 if (delay < 0) |
90 delay = 0; | 89 delay = 0; |
91 | 90 |
92 return delay; | 91 return delay; |
93 } | 92 } |
94 | 93 |
95 //----------------------------------------------------------------------------- | 94 //----------------------------------------------------------------------------- |
96 // MessagePumpForUI public: | 95 // MessagePumpForUI public: |
97 | 96 |
98 MessagePumpForUI::MessagePumpForUI() | 97 MessagePumpForUI::MessagePumpForUI() |
99 : instance_(NULL), | 98 : message_filter_(new MessageFilter), |
100 message_filter_(new MessageFilter) { | 99 window_(new win::MessageWindow()) { |
101 InitMessageWnd(); | 100 CHECK(window_->Create(this)); |
102 } | 101 } |
103 | 102 |
104 MessagePumpForUI::~MessagePumpForUI() { | 103 MessagePumpForUI::~MessagePumpForUI() { |
105 DestroyWindow(message_hwnd_); | |
106 UnregisterClass(kWndClass, instance_); | |
107 } | 104 } |
108 | 105 |
109 void MessagePumpForUI::ScheduleWork() { | 106 void MessagePumpForUI::ScheduleWork() { |
110 if (InterlockedExchange(&have_work_, 1)) | 107 if (InterlockedExchange(&have_work_, 1)) |
111 return; // Someone else continued the pumping. | 108 return; // Someone else continued the pumping. |
112 | 109 |
113 // Make sure the MessagePump does some work for us. | 110 // Make sure the MessagePump does some work for us. |
114 BOOL ret = PostMessage(message_hwnd_, kMsgHaveWork, | 111 BOOL ret = PostMessage(window_->hwnd(), kMsgHaveWork, 0, 0); |
115 reinterpret_cast<WPARAM>(this), 0); | |
116 if (ret) | 112 if (ret) |
117 return; // There was room in the Window Message queue. | 113 return; // There was room in the Window Message queue. |
118 | 114 |
119 // We have failed to insert a have-work message, so there is a chance that we | 115 // 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 | 116 // 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 | 117 // 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. :-( | 118 // 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 | 119 // 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 | 120 // 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 | 121 // recovery. Nested loops are pretty transient (we think), so this will |
(...skipping 26 matching lines...) Expand all Loading... |
152 // | 148 // |
153 delayed_work_time_ = delayed_work_time; | 149 delayed_work_time_ = delayed_work_time; |
154 | 150 |
155 int delay_msec = GetCurrentDelay(); | 151 int delay_msec = GetCurrentDelay(); |
156 DCHECK_GE(delay_msec, 0); | 152 DCHECK_GE(delay_msec, 0); |
157 if (delay_msec < USER_TIMER_MINIMUM) | 153 if (delay_msec < USER_TIMER_MINIMUM) |
158 delay_msec = USER_TIMER_MINIMUM; | 154 delay_msec = USER_TIMER_MINIMUM; |
159 | 155 |
160 // Create a WM_TIMER event that will wake us up to check for any pending | 156 // 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). | 157 // timers (in case we are running within a nested, external sub-pump). |
162 BOOL ret = SetTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this), | 158 BOOL ret = SetTimer(window_->hwnd(), kTimerId, delay_msec, NULL); |
163 delay_msec, NULL); | |
164 if (ret) | 159 if (ret) |
165 return; | 160 return; |
166 // If we can't set timers, we are in big trouble... but cross our fingers for | 161 // If we can't set timers, we are in big trouble... but cross our fingers for |
167 // now. | 162 // now. |
168 // TODO(jar): If we don't see this error, use a CHECK() here instead. | 163 // TODO(jar): If we don't see this error, use a CHECK() here instead. |
169 UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", SET_TIMER_ERROR, | 164 UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", SET_TIMER_ERROR, |
170 MESSAGE_LOOP_PROBLEM_MAX); | 165 MESSAGE_LOOP_PROBLEM_MAX); |
171 } | 166 } |
172 | 167 |
173 void MessagePumpForUI::PumpOutPendingPaintMessages() { | 168 void MessagePumpForUI::PumpOutPendingPaintMessages() { |
(...skipping 16 matching lines...) Expand all Loading... |
190 if (state_->should_quit) // Handle WM_QUIT. | 185 if (state_->should_quit) // Handle WM_QUIT. |
191 break; | 186 break; |
192 } | 187 } |
193 // Histogram what was really being used, to help to adjust kMaxPeekCount. | 188 // Histogram what was really being used, to help to adjust kMaxPeekCount. |
194 DHISTOGRAM_COUNTS("Loop.PumpOutPendingPaintMessages Peeks", peek_count); | 189 DHISTOGRAM_COUNTS("Loop.PumpOutPendingPaintMessages Peeks", peek_count); |
195 } | 190 } |
196 | 191 |
197 //----------------------------------------------------------------------------- | 192 //----------------------------------------------------------------------------- |
198 // MessagePumpForUI private: | 193 // MessagePumpForUI private: |
199 | 194 |
200 // static | 195 bool MessagePumpForUI::HandleMessage(HWND hwnd, |
201 LRESULT CALLBACK MessagePumpForUI::WndProcThunk( | 196 UINT message, |
202 HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { | 197 WPARAM wparam, |
| 198 LPARAM lparam, |
| 199 LRESULT* result) { |
203 switch (message) { | 200 switch (message) { |
204 case kMsgHaveWork: | 201 case kMsgHaveWork: |
205 reinterpret_cast<MessagePumpForUI*>(wparam)->HandleWorkMessage(); | 202 HandleWorkMessage(); |
206 break; | 203 break; |
| 204 |
207 case WM_TIMER: | 205 case WM_TIMER: |
208 reinterpret_cast<MessagePumpForUI*>(wparam)->HandleTimerMessage(); | 206 HandleTimerMessage(); |
209 break; | 207 break; |
210 } | 208 } |
211 return DefWindowProc(hwnd, message, wparam, lparam); | 209 |
| 210 // Do default processing for all messages. |
| 211 return false; |
212 } | 212 } |
213 | 213 |
214 void MessagePumpForUI::DoRunLoop() { | 214 void MessagePumpForUI::DoRunLoop() { |
215 // IF this was just a simple PeekMessage() loop (servicing all possible work | 215 // IF this was just a simple PeekMessage() loop (servicing all possible work |
216 // queues), then Windows would try to achieve the following order according | 216 // queues), then Windows would try to achieve the following order according |
217 // to MSDN documentation about PeekMessage with no filter): | 217 // to MSDN documentation about PeekMessage with no filter): |
218 // * Sent messages | 218 // * Sent messages |
219 // * Posted messages | 219 // * Posted messages |
220 // * Sent messages (again) | 220 // * Sent messages (again) |
221 // * WM_PAINT messages | 221 // * WM_PAINT messages |
(...skipping 20 matching lines...) Expand all Loading... |
242 if (state_->should_quit) | 242 if (state_->should_quit) |
243 break; | 243 break; |
244 | 244 |
245 more_work_is_plausible |= | 245 more_work_is_plausible |= |
246 state_->delegate->DoDelayedWork(&delayed_work_time_); | 246 state_->delegate->DoDelayedWork(&delayed_work_time_); |
247 // If we did not process any delayed work, then we can assume that our | 247 // 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 | 248 // 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, | 249 // 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. | 250 // 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()) | 251 if (more_work_is_plausible && delayed_work_time_.is_null()) |
252 KillTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this)); | 252 KillTimer(window_->hwnd(), kTimerId); |
253 if (state_->should_quit) | 253 if (state_->should_quit) |
254 break; | 254 break; |
255 | 255 |
256 if (more_work_is_plausible) | 256 if (more_work_is_plausible) |
257 continue; | 257 continue; |
258 | 258 |
259 more_work_is_plausible = state_->delegate->DoIdleWork(); | 259 more_work_is_plausible = state_->delegate->DoIdleWork(); |
260 if (state_->should_quit) | 260 if (state_->should_quit) |
261 break; | 261 break; |
262 | 262 |
263 if (more_work_is_plausible) | 263 if (more_work_is_plausible) |
264 continue; | 264 continue; |
265 | 265 |
266 WaitForWork(); // Wait (sleep) until we have work to do again. | 266 WaitForWork(); // Wait (sleep) until we have work to do again. |
267 } | 267 } |
268 } | 268 } |
269 | 269 |
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() { | 270 void MessagePumpForUI::WaitForWork() { |
285 // Wait until a message is available, up to the time needed by the timer | 271 // Wait until a message is available, up to the time needed by the timer |
286 // manager to fire the next set of timers. | 272 // manager to fire the next set of timers. |
287 int delay = GetCurrentDelay(); | 273 int delay = GetCurrentDelay(); |
288 if (delay < 0) // Negative value means no timers waiting. | 274 if (delay < 0) // Negative value means no timers waiting. |
289 delay = INFINITE; | 275 delay = INFINITE; |
290 | 276 |
291 DWORD result; | 277 DWORD result; |
292 result = MsgWaitForMultipleObjectsEx(0, NULL, delay, QS_ALLINPUT, | 278 result = MsgWaitForMultipleObjectsEx(0, NULL, delay, QS_ALLINPUT, |
293 MWMO_INPUTAVAILABLE); | 279 MWMO_INPUTAVAILABLE); |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
331 // messages that may be in the Windows message queue. | 317 // messages that may be in the Windows message queue. |
332 ProcessPumpReplacementMessage(); | 318 ProcessPumpReplacementMessage(); |
333 | 319 |
334 // Now give the delegate a chance to do some work. He'll let us know if he | 320 // Now give the delegate a chance to do some work. He'll let us know if he |
335 // needs to do more work. | 321 // needs to do more work. |
336 if (state_->delegate->DoWork()) | 322 if (state_->delegate->DoWork()) |
337 ScheduleWork(); | 323 ScheduleWork(); |
338 } | 324 } |
339 | 325 |
340 void MessagePumpForUI::HandleTimerMessage() { | 326 void MessagePumpForUI::HandleTimerMessage() { |
341 KillTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this)); | 327 KillTimer(window_->hwnd(), kTimerId); |
342 | 328 |
343 // If we are being called outside of the context of Run, then don't do | 329 // 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 | 330 // anything. This could correspond to a MessageBox call or something of |
345 // that sort. | 331 // that sort. |
346 if (!state_) | 332 if (!state_) |
347 return; | 333 return; |
348 | 334 |
349 state_->delegate->DoDelayedWork(&delayed_work_time_); | 335 state_->delegate->DoDelayedWork(&delayed_work_time_); |
350 if (!delayed_work_time_.is_null()) { | 336 if (!delayed_work_time_.is_null()) { |
351 // A bit gratuitous to set delayed_work_time_ again, but oh well. | 337 // A bit gratuitous to set delayed_work_time_ again, but oh well. |
(...skipping 23 matching lines...) Expand all Loading... |
375 "message", msg.message); | 361 "message", msg.message); |
376 if (WM_QUIT == msg.message) { | 362 if (WM_QUIT == msg.message) { |
377 // Repost the QUIT message so that it will be retrieved by the primary | 363 // Repost the QUIT message so that it will be retrieved by the primary |
378 // GetMessage() loop. | 364 // GetMessage() loop. |
379 state_->should_quit = true; | 365 state_->should_quit = true; |
380 PostQuitMessage(static_cast<int>(msg.wParam)); | 366 PostQuitMessage(static_cast<int>(msg.wParam)); |
381 return false; | 367 return false; |
382 } | 368 } |
383 | 369 |
384 // While running our main message pump, we discard kMsgHaveWork messages. | 370 // While running our main message pump, we discard kMsgHaveWork messages. |
385 if (msg.message == kMsgHaveWork && msg.hwnd == message_hwnd_) | 371 if (msg.message == kMsgHaveWork && msg.hwnd == window_->hwnd()) |
386 return ProcessPumpReplacementMessage(); | 372 return ProcessPumpReplacementMessage(); |
387 | 373 |
388 if (CallMsgFilter(const_cast<MSG*>(&msg), kMessageFilterCode)) | 374 if (CallMsgFilter(const_cast<MSG*>(&msg), kMessageFilterCode)) |
389 return true; | 375 return true; |
390 | 376 |
391 WillProcessMessage(msg); | 377 WillProcessMessage(msg); |
392 | 378 |
393 if (!message_filter_->ProcessMessage(msg)) { | 379 if (!message_filter_->ProcessMessage(msg)) { |
394 if (state_->dispatcher) { | 380 if (state_->dispatcher) { |
395 if (!state_->dispatcher->Dispatch(msg)) | 381 if (!state_->dispatcher->Dispatch(msg)) |
(...skipping 26 matching lines...) Expand all Loading... |
422 if (MessageLoop::current()->os_modal_loop()) { | 408 if (MessageLoop::current()->os_modal_loop()) { |
423 // We only peek out WM_PAINT and WM_TIMER here for reasons mentioned above. | 409 // 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) || | 410 have_message = PeekMessage(&msg, NULL, WM_PAINT, WM_PAINT, PM_REMOVE) || |
425 PeekMessage(&msg, NULL, WM_TIMER, WM_TIMER, PM_REMOVE); | 411 PeekMessage(&msg, NULL, WM_TIMER, WM_TIMER, PM_REMOVE); |
426 } else { | 412 } else { |
427 have_message = !!message_filter_->DoPeekMessage(&msg, NULL, 0, 0, | 413 have_message = !!message_filter_->DoPeekMessage(&msg, NULL, 0, 0, |
428 PM_REMOVE); | 414 PM_REMOVE); |
429 } | 415 } |
430 | 416 |
431 DCHECK(!have_message || kMsgHaveWork != msg.message || | 417 DCHECK(!have_message || kMsgHaveWork != msg.message || |
432 msg.hwnd != message_hwnd_); | 418 msg.hwnd != window_->hwnd()); |
433 | 419 |
434 // Since we discarded a kMsgHaveWork message, we must update the flag. | 420 // Since we discarded a kMsgHaveWork message, we must update the flag. |
435 int old_have_work = InterlockedExchange(&have_work_, 0); | 421 int old_have_work = InterlockedExchange(&have_work_, 0); |
436 DCHECK(old_have_work); | 422 DCHECK(old_have_work); |
437 | 423 |
438 // We don't need a special time slice if we didn't have_message to process. | 424 // We don't need a special time slice if we didn't have_message to process. |
439 if (!have_message) | 425 if (!have_message) |
440 return false; | 426 return false; |
441 | 427 |
442 // Guarantee we'll get another time slice in the case where we go into native | 428 // Guarantee we'll get another time slice in the case where we go into native |
(...skipping 228 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
671 | 657 |
672 // static | 658 // static |
673 MessagePumpForIO::IOHandler* MessagePumpForIO::KeyToHandler( | 659 MessagePumpForIO::IOHandler* MessagePumpForIO::KeyToHandler( |
674 ULONG_PTR key, | 660 ULONG_PTR key, |
675 bool* has_valid_io_context) { | 661 bool* has_valid_io_context) { |
676 *has_valid_io_context = ((key & 1) == 0); | 662 *has_valid_io_context = ((key & 1) == 0); |
677 return reinterpret_cast<IOHandler*>(key & ~static_cast<ULONG_PTR>(1)); | 663 return reinterpret_cast<IOHandler*>(key & ~static_cast<ULONG_PTR>(1)); |
678 } | 664 } |
679 | 665 |
680 } // namespace base | 666 } // namespace base |
OLD | NEW |