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