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