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_loop/message_pump_win.h" | 5 #include "base/message_loop/message_pump_win.h" |
| 6 | 6 |
| 7 #include <math.h> | 7 #include <math.h> |
| 8 #include <stdint.h> | 8 #include <stdint.h> |
| 9 | 9 |
| 10 #include <limits> | 10 #include <limits> |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 73 // Range check the |timeout| while converting to an integer. If the |timeout| | 73 // Range check the |timeout| while converting to an integer. If the |timeout| |
| 74 // is negative, then we need to run delayed work soon. If the |timeout| is | 74 // is negative, then we need to run delayed work soon. If the |timeout| is |
| 75 // "overflowingly" large, that means a delayed task was posted with a | 75 // "overflowingly" large, that means a delayed task was posted with a |
| 76 // super-long delay. | 76 // super-long delay. |
| 77 return timeout < 0 ? 0 : | 77 return timeout < 0 ? 0 : |
| 78 (timeout > std::numeric_limits<int>::max() ? | 78 (timeout > std::numeric_limits<int>::max() ? |
| 79 std::numeric_limits<int>::max() : static_cast<int>(timeout)); | 79 std::numeric_limits<int>::max() : static_cast<int>(timeout)); |
| 80 } | 80 } |
| 81 | 81 |
| 82 //----------------------------------------------------------------------------- | 82 //----------------------------------------------------------------------------- |
| 83 // MessagePumpForUI public: | 83 // MessagePumpForUIBase public: |
| 84 | 84 |
| 85 MessagePumpForUI::MessagePumpForUI() | 85 MessagePumpForUIBase::MessagePumpForUIBase() |
| 86 : atom_(0) { | 86 : atom_(0) { |
| 87 InitMessageWnd(); | 87 InitMessageWnd(); |
|
jbauman
2016/03/18 01:00:50
We could essentially get rid of MessagePumpForUIBa
stanisc
2016/03/18 18:16:46
Thanks for the feedback! I've tried a separate cla
| |
| 88 } | 88 } |
| 89 | 89 |
| 90 MessagePumpForUI::~MessagePumpForUI() { | 90 MessagePumpForUIBase::~MessagePumpForUIBase() { |
| 91 DestroyWindow(message_hwnd_); | 91 DestroyWindow(message_hwnd_); |
| 92 UnregisterClass(MAKEINTATOM(atom_), | 92 UnregisterClass(MAKEINTATOM(atom_), |
| 93 GetModuleFromAddress(&WndProcThunk)); | 93 GetModuleFromAddress(&WndProcThunk)); |
| 94 } | 94 } |
| 95 | 95 |
| 96 void MessagePumpForUI::ScheduleWork() { | 96 void MessagePumpForUIBase::ScheduleWork() { |
| 97 if (InterlockedExchange(&have_work_, 1)) | 97 if (READY != InterlockedExchange(&work_state_, HAVE_WORK)) |
| 98 return; // Someone else continued the pumping. | 98 return; // Someone else continued the pumping. |
| 99 | 99 |
| 100 // Make sure the MessagePump does some work for us. | 100 // Make sure the MessagePump does some work for us. |
| 101 BOOL ret = PostMessage(message_hwnd_, kMsgHaveWork, | 101 BOOL ret = PostMessage(message_hwnd_, kMsgHaveWork, |
| 102 reinterpret_cast<WPARAM>(this), 0); | 102 reinterpret_cast<WPARAM>(this), 0); |
| 103 if (ret) | 103 if (ret) |
| 104 return; // There was room in the Window Message queue. | 104 return; // There was room in the Window Message queue. |
| 105 | 105 |
| 106 // We have failed to insert a have-work message, so there is a chance that we | 106 // We have failed to insert a have-work message, so there is a chance that we |
| 107 // will starve tasks/timers while sitting in a nested message loop. Nested | 107 // will starve tasks/timers while sitting in a nested message loop. Nested |
| 108 // loops only look at Windows Message queues, and don't look at *our* task | 108 // loops only look at Windows Message queues, and don't look at *our* task |
| 109 // queues, etc., so we might not get a time slice in such. :-( | 109 // queues, etc., so we might not get a time slice in such. :-( |
| 110 // We could abort here, but the fear is that this failure mode is plausibly | 110 // We could abort here, but the fear is that this failure mode is plausibly |
| 111 // common (queue is full, of about 2000 messages), so we'll do a near-graceful | 111 // common (queue is full, of about 2000 messages), so we'll do a near-graceful |
| 112 // recovery. Nested loops are pretty transient (we think), so this will | 112 // recovery. Nested loops are pretty transient (we think), so this will |
| 113 // probably be recoverable. | 113 // probably be recoverable. |
| 114 InterlockedExchange(&have_work_, 0); // Clarify that we didn't really insert. | 114 |
| 115 // Clarify that we didn't really insert. | |
| 116 InterlockedExchange(&work_state_, READY); | |
| 115 UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", MESSAGE_POST_ERROR, | 117 UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", MESSAGE_POST_ERROR, |
| 116 MESSAGE_LOOP_PROBLEM_MAX); | 118 MESSAGE_LOOP_PROBLEM_MAX); |
| 117 } | 119 } |
| 118 | 120 |
| 119 void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { | 121 //----------------------------------------------------------------------------- |
| 120 delayed_work_time_ = delayed_work_time; | 122 // MessagePumpForUIBase protected: |
| 121 RescheduleTimer(); | 123 |
| 124 void MessagePumpForUIBase::SetTimer(int delay_msec) { | |
| 125 // We would *like* to provide high resolution timers. Windows timers using | |
| 126 // SetTimer() have a 10ms granularity. We have to use WM_TIMER as a wakeup | |
| 127 // mechanism because the application can enter modal windows loops where it | |
| 128 // is not running our MessageLoop; the only way to have our timers fire in | |
| 129 // these cases is to post messages there. | |
| 130 // | |
| 131 // To provide sub-10ms timers, we process timers directly from our run loop. | |
| 132 // For the common case, timers will be processed there as the run loop does | |
| 133 // its normal work. However, we *also* set the system timer so that WM_TIMER | |
| 134 // events fire. This mops up the case of timers not being able to work in | |
| 135 // modal message loops. It is possible for the SetTimer to pop and have no | |
| 136 // pending timers, because they could have already been processed by the | |
| 137 // run loop itself. | |
| 138 // | |
| 139 // We use a single SetTimer corresponding to the timer that will expire | |
| 140 // soonest. As new timers are created and destroyed, we update SetTimer. | |
| 141 // Getting a spurious SetTimer event firing is benign, as we'll just be | |
| 142 // processing an empty timer queue. | |
| 143 if (delay_msec < USER_TIMER_MINIMUM) | |
| 144 delay_msec = USER_TIMER_MINIMUM; | |
| 145 | |
| 146 // Create a WM_TIMER event that will wake us up to check for any pending | |
| 147 // timers (in case we are running within a nested, external sub-pump). | |
| 148 BOOL ret = ::SetTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this), | |
| 149 delay_msec, NULL); | |
| 150 if (ret) | |
| 151 return; | |
| 152 // If we can't set timers, we are in big trouble... but cross our fingers | |
| 153 // for now. | |
| 154 // TODO(jar): If we don't see this error, use a CHECK() here instead. | |
| 155 UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", SET_TIMER_ERROR, | |
| 156 MESSAGE_LOOP_PROBLEM_MAX); | |
| 157 } | |
| 158 | |
| 159 void MessagePumpForUIBase::KillTimer() { | |
| 160 ::KillTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this)); | |
| 161 } | |
| 162 | |
| 163 bool MessagePumpForUIBase::IsWorkMessage(const MSG& msg) const { | |
| 164 return msg.message == kMsgHaveWork && msg.hwnd == message_hwnd_; | |
| 165 } | |
| 166 | |
| 167 //----------------------------------------------------------------------------- | |
| 168 // MessagePumpForUIBase private: | |
| 169 | |
| 170 // static | |
| 171 LRESULT CALLBACK MessagePumpForUIBase::WndProcThunk( | |
| 172 HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { | |
| 173 switch (message) { | |
| 174 case kMsgHaveWork: | |
| 175 reinterpret_cast<MessagePumpForUIBase*>(wparam)->HandleWorkMessage(); | |
| 176 break; | |
| 177 case WM_TIMER: | |
| 178 reinterpret_cast<MessagePumpForUIBase*>(wparam)->HandleTimerMessage(); | |
| 179 break; | |
| 180 } | |
| 181 return DefWindowProc(hwnd, message, wparam, lparam); | |
| 182 } | |
| 183 | |
| 184 void MessagePumpForUIBase::InitMessageWnd() { | |
| 185 // Generate a unique window class name. | |
| 186 string16 class_name = StringPrintf(kWndClassFormat, this); | |
| 187 | |
| 188 HINSTANCE instance = GetModuleFromAddress(&WndProcThunk); | |
| 189 WNDCLASSEX wc = {0}; | |
| 190 wc.cbSize = sizeof(wc); | |
| 191 wc.lpfnWndProc = base::win::WrappedWindowProc<WndProcThunk>; | |
| 192 wc.hInstance = instance; | |
| 193 wc.lpszClassName = class_name.c_str(); | |
| 194 atom_ = RegisterClassEx(&wc); | |
| 195 DCHECK(atom_); | |
| 196 | |
| 197 message_hwnd_ = CreateWindow(MAKEINTATOM(atom_), 0, 0, 0, 0, 0, 0, | |
| 198 HWND_MESSAGE, 0, instance, 0); | |
| 199 DCHECK(message_hwnd_); | |
| 200 } | |
| 201 | |
| 202 //----------------------------------------------------------------------------- | |
| 203 // MessagePumpForUI public: | |
| 204 | |
| 205 MessagePumpForUI::MessagePumpForUI() { | |
| 206 } | |
| 207 | |
| 208 MessagePumpForUI::~MessagePumpForUI() { | |
| 122 } | 209 } |
| 123 | 210 |
| 124 //----------------------------------------------------------------------------- | 211 //----------------------------------------------------------------------------- |
| 125 // MessagePumpForUI private: | 212 // MessagePumpForUI private: |
| 126 | 213 |
| 127 // static | 214 void MessagePumpForUI::ScheduleDelayedWork( |
| 128 LRESULT CALLBACK MessagePumpForUI::WndProcThunk( | 215 const TimeTicks& delayed_work_time) { |
| 129 HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { | 216 delayed_work_time_ = delayed_work_time; |
| 130 switch (message) { | 217 RescheduleTimer(); |
| 131 case kMsgHaveWork: | |
| 132 reinterpret_cast<MessagePumpForUI*>(wparam)->HandleWorkMessage(); | |
| 133 break; | |
| 134 case WM_TIMER: | |
| 135 reinterpret_cast<MessagePumpForUI*>(wparam)->HandleTimerMessage(); | |
| 136 break; | |
| 137 } | |
| 138 return DefWindowProc(hwnd, message, wparam, lparam); | |
| 139 } | 218 } |
| 140 | 219 |
| 141 void MessagePumpForUI::DoRunLoop() { | 220 void MessagePumpForUI::DoRunLoop() { |
| 142 // IF this was just a simple PeekMessage() loop (servicing all possible work | 221 // IF this was just a simple PeekMessage() loop (servicing all possible work |
| 143 // queues), then Windows would try to achieve the following order according | 222 // queues), then Windows would try to achieve the following order according |
| 144 // to MSDN documentation about PeekMessage with no filter): | 223 // to MSDN documentation about PeekMessage with no filter): |
| 145 // * Sent messages | 224 // * Sent messages |
| 146 // * Posted messages | 225 // * Posted messages |
| 147 // * Sent messages (again) | 226 // * Sent messages (again) |
| 148 // * WM_PAINT messages | 227 // * WM_PAINT messages |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 169 if (state_->should_quit) | 248 if (state_->should_quit) |
| 170 break; | 249 break; |
| 171 | 250 |
| 172 more_work_is_plausible |= | 251 more_work_is_plausible |= |
| 173 state_->delegate->DoDelayedWork(&delayed_work_time_); | 252 state_->delegate->DoDelayedWork(&delayed_work_time_); |
| 174 // If we did not process any delayed work, then we can assume that our | 253 // If we did not process any delayed work, then we can assume that our |
| 175 // existing WM_TIMER if any will fire when delayed work should run. We | 254 // existing WM_TIMER if any will fire when delayed work should run. We |
| 176 // don't want to disturb that timer if it is already in flight. However, | 255 // don't want to disturb that timer if it is already in flight. However, |
| 177 // if we did do all remaining delayed work, then lets kill the WM_TIMER. | 256 // if we did do all remaining delayed work, then lets kill the WM_TIMER. |
| 178 if (more_work_is_plausible && delayed_work_time_.is_null()) | 257 if (more_work_is_plausible && delayed_work_time_.is_null()) |
| 179 KillTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this)); | 258 KillTimer(); |
| 180 if (state_->should_quit) | 259 if (state_->should_quit) |
| 181 break; | 260 break; |
| 182 | 261 |
| 183 if (more_work_is_plausible) | 262 if (more_work_is_plausible) |
| 184 continue; | 263 continue; |
| 185 | 264 |
| 186 more_work_is_plausible = state_->delegate->DoIdleWork(); | 265 more_work_is_plausible = state_->delegate->DoIdleWork(); |
| 187 if (state_->should_quit) | 266 if (state_->should_quit) |
| 188 break; | 267 break; |
| 189 | 268 |
| 190 if (more_work_is_plausible) | 269 if (more_work_is_plausible) |
| 191 continue; | 270 continue; |
| 192 | 271 |
| 193 WaitForWork(); // Wait (sleep) until we have work to do again. | 272 WaitForWork(); // Wait (sleep) until we have work to do again. |
| 194 } | 273 } |
| 195 } | 274 } |
| 196 | 275 |
| 197 void MessagePumpForUI::InitMessageWnd() { | |
| 198 // Generate a unique window class name. | |
| 199 string16 class_name = StringPrintf(kWndClassFormat, this); | |
| 200 | |
| 201 HINSTANCE instance = GetModuleFromAddress(&WndProcThunk); | |
| 202 WNDCLASSEX wc = {0}; | |
| 203 wc.cbSize = sizeof(wc); | |
| 204 wc.lpfnWndProc = base::win::WrappedWindowProc<WndProcThunk>; | |
| 205 wc.hInstance = instance; | |
| 206 wc.lpszClassName = class_name.c_str(); | |
| 207 atom_ = RegisterClassEx(&wc); | |
| 208 DCHECK(atom_); | |
| 209 | |
| 210 message_hwnd_ = CreateWindow(MAKEINTATOM(atom_), 0, 0, 0, 0, 0, 0, | |
| 211 HWND_MESSAGE, 0, instance, 0); | |
| 212 DCHECK(message_hwnd_); | |
| 213 } | |
| 214 | |
| 215 void MessagePumpForUI::WaitForWork() { | 276 void MessagePumpForUI::WaitForWork() { |
| 216 // Wait until a message is available, up to the time needed by the timer | 277 // Wait until a message is available, up to the time needed by the timer |
| 217 // manager to fire the next set of timers. | 278 // manager to fire the next set of timers. |
| 218 int delay = GetCurrentDelay(); | 279 int delay = GetCurrentDelay(); |
| 219 if (delay < 0) // Negative value means no timers waiting. | 280 if (delay < 0) // Negative value means no timers waiting. |
| 220 delay = INFINITE; | 281 delay = INFINITE; |
| 221 | 282 |
| 222 DWORD result; | 283 DWORD result; |
| 223 result = MsgWaitForMultipleObjectsEx(0, NULL, delay, QS_ALLINPUT, | 284 result = MsgWaitForMultipleObjectsEx(0, NULL, delay, QS_ALLINPUT, |
| 224 MWMO_INPUTAVAILABLE); | 285 MWMO_INPUTAVAILABLE); |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 246 | 307 |
| 247 DCHECK_NE(WAIT_FAILED, result) << GetLastError(); | 308 DCHECK_NE(WAIT_FAILED, result) << GetLastError(); |
| 248 } | 309 } |
| 249 | 310 |
| 250 void MessagePumpForUI::HandleWorkMessage() { | 311 void MessagePumpForUI::HandleWorkMessage() { |
| 251 // If we are being called outside of the context of Run, then don't try to do | 312 // If we are being called outside of the context of Run, then don't try to do |
| 252 // any work. This could correspond to a MessageBox call or something of that | 313 // any work. This could correspond to a MessageBox call or something of that |
| 253 // sort. | 314 // sort. |
| 254 if (!state_) { | 315 if (!state_) { |
| 255 // Since we handled a kMsgHaveWork message, we must still update this flag. | 316 // Since we handled a kMsgHaveWork message, we must still update this flag. |
| 256 InterlockedExchange(&have_work_, 0); | 317 InterlockedExchange(&work_state_, READY); |
| 257 return; | 318 return; |
| 258 } | 319 } |
| 259 | 320 |
| 260 // Let whatever would have run had we not been putting messages in the queue | 321 // Let whatever would have run had we not been putting messages in the queue |
| 261 // run now. This is an attempt to make our dummy message not starve other | 322 // run now. This is an attempt to make our dummy message not starve other |
| 262 // messages that may be in the Windows message queue. | 323 // messages that may be in the Windows message queue. |
| 263 ProcessPumpReplacementMessage(); | 324 ProcessPumpReplacementMessage(); |
| 264 | 325 |
| 265 // Now give the delegate a chance to do some work. He'll let us know if he | 326 // Now give the delegate a chance to do some work. He'll let us know if he |
| 266 // needs to do more work. | 327 // needs to do more work. |
| 267 if (state_->delegate->DoWork()) | 328 if (state_->delegate->DoWork()) |
| 268 ScheduleWork(); | 329 ScheduleWork(); |
| 269 state_->delegate->DoDelayedWork(&delayed_work_time_); | 330 state_->delegate->DoDelayedWork(&delayed_work_time_); |
| 270 RescheduleTimer(); | 331 RescheduleTimer(); |
| 271 } | 332 } |
| 272 | 333 |
| 273 void MessagePumpForUI::HandleTimerMessage() { | 334 void MessagePumpForUI::HandleTimerMessage() { |
| 274 KillTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this)); | 335 KillTimer(); |
| 275 | 336 |
| 276 // If we are being called outside of the context of Run, then don't do | 337 // If we are being called outside of the context of Run, then don't do |
| 277 // anything. This could correspond to a MessageBox call or something of | 338 // anything. This could correspond to a MessageBox call or something of |
| 278 // that sort. | 339 // that sort. |
| 279 if (!state_) | 340 if (!state_) |
| 280 return; | 341 return; |
| 281 | 342 |
| 282 state_->delegate->DoDelayedWork(&delayed_work_time_); | 343 state_->delegate->DoDelayedWork(&delayed_work_time_); |
| 283 RescheduleTimer(); | 344 RescheduleTimer(); |
| 284 } | 345 } |
| 285 | 346 |
| 286 void MessagePumpForUI::RescheduleTimer() { | 347 void MessagePumpForUI::RescheduleTimer() { |
| 287 if (delayed_work_time_.is_null()) | 348 if (delayed_work_time_.is_null()) |
| 288 return; | 349 return; |
| 289 // | 350 |
| 290 // We would *like* to provide high resolution timers. Windows timers using | |
| 291 // SetTimer() have a 10ms granularity. We have to use WM_TIMER as a wakeup | |
| 292 // mechanism because the application can enter modal windows loops where it | |
| 293 // is not running our MessageLoop; the only way to have our timers fire in | |
| 294 // these cases is to post messages there. | |
| 295 // | |
| 296 // To provide sub-10ms timers, we process timers directly from our run loop. | |
| 297 // For the common case, timers will be processed there as the run loop does | |
| 298 // its normal work. However, we *also* set the system timer so that WM_TIMER | |
| 299 // events fire. This mops up the case of timers not being able to work in | |
| 300 // modal message loops. It is possible for the SetTimer to pop and have no | |
| 301 // pending timers, because they could have already been processed by the | |
| 302 // run loop itself. | |
| 303 // | |
| 304 // We use a single SetTimer corresponding to the timer that will expire | |
| 305 // soonest. As new timers are created and destroyed, we update SetTimer. | |
| 306 // Getting a spurious SetTimer event firing is benign, as we'll just be | |
| 307 // processing an empty timer queue. | |
| 308 // | |
| 309 int delay_msec = GetCurrentDelay(); | 351 int delay_msec = GetCurrentDelay(); |
| 310 DCHECK_GE(delay_msec, 0); | 352 DCHECK_GE(delay_msec, 0); |
| 311 if (delay_msec == 0) { | 353 if (delay_msec == 0) { |
| 312 ScheduleWork(); | 354 ScheduleWork(); |
| 313 } else { | 355 } else { |
| 314 if (delay_msec < USER_TIMER_MINIMUM) | 356 SetTimer(delay_msec); |
| 315 delay_msec = USER_TIMER_MINIMUM; | |
| 316 | |
| 317 // Create a WM_TIMER event that will wake us up to check for any pending | |
| 318 // timers (in case we are running within a nested, external sub-pump). | |
| 319 BOOL ret = SetTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this), | |
| 320 delay_msec, NULL); | |
| 321 if (ret) | |
| 322 return; | |
| 323 // If we can't set timers, we are in big trouble... but cross our fingers | |
| 324 // for now. | |
| 325 // TODO(jar): If we don't see this error, use a CHECK() here instead. | |
| 326 UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", SET_TIMER_ERROR, | |
| 327 MESSAGE_LOOP_PROBLEM_MAX); | |
| 328 } | 357 } |
| 329 } | 358 } |
| 330 | 359 |
| 331 bool MessagePumpForUI::ProcessNextWindowsMessage() { | 360 bool MessagePumpForUI::ProcessNextWindowsMessage() { |
| 332 // If there are sent messages in the queue then PeekMessage internally | 361 // If there are sent messages in the queue then PeekMessage internally |
| 333 // dispatches the message and returns false. We return true in this | 362 // dispatches the message and returns false. We return true in this |
| 334 // case to ensure that the message loop peeks again instead of calling | 363 // case to ensure that the message loop peeks again instead of calling |
| 335 // MsgWaitForMultipleObjectsEx again. | 364 // MsgWaitForMultipleObjectsEx again. |
| 336 bool sent_messages_in_queue = false; | 365 bool sent_messages_in_queue = false; |
| 337 DWORD queue_status = GetQueueStatus(QS_SENDMESSAGE); | 366 DWORD queue_status = GetQueueStatus(QS_SENDMESSAGE); |
| 338 if (HIWORD(queue_status) & QS_SENDMESSAGE) | 367 if (HIWORD(queue_status) & QS_SENDMESSAGE) |
| 339 sent_messages_in_queue = true; | 368 sent_messages_in_queue = true; |
| 340 | 369 |
| 341 MSG msg; | 370 MSG msg; |
| 342 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) != FALSE) | 371 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) != FALSE) |
| 343 return ProcessMessageHelper(msg); | 372 return ProcessMessageHelper(msg); |
| 344 | 373 |
| 345 return sent_messages_in_queue; | 374 return sent_messages_in_queue; |
| 346 } | 375 } |
| 347 | 376 |
| 348 bool MessagePumpForUI::ProcessMessageHelper(const MSG& msg) { | 377 bool MessagePumpForUI::ProcessMessageHelper(const MSG& msg) { |
| 349 TRACE_EVENT1("base", "MessagePumpForUI::ProcessMessageHelper", | 378 TRACE_EVENT1("base", "MessagePumpForUIBase::ProcessMessageHelper", |
| 350 "message", msg.message); | 379 "message", msg.message); |
| 351 if (WM_QUIT == msg.message) { | 380 if (WM_QUIT == msg.message) { |
| 352 // Repost the QUIT message so that it will be retrieved by the primary | 381 // Repost the QUIT message so that it will be retrieved by the primary |
| 353 // GetMessage() loop. | 382 // GetMessage() loop. |
| 354 state_->should_quit = true; | 383 state_->should_quit = true; |
| 355 PostQuitMessage(static_cast<int>(msg.wParam)); | 384 PostQuitMessage(static_cast<int>(msg.wParam)); |
| 356 return false; | 385 return false; |
| 357 } | 386 } |
| 358 | 387 |
| 359 // While running our main message pump, we discard kMsgHaveWork messages. | 388 // While running our main message pump, we discard kMsgHaveWork messages. |
| 360 if (msg.message == kMsgHaveWork && msg.hwnd == message_hwnd_) | 389 if (IsWorkMessage(msg)) |
| 361 return ProcessPumpReplacementMessage(); | 390 return ProcessPumpReplacementMessage(); |
| 362 | 391 |
| 363 if (CallMsgFilter(const_cast<MSG*>(&msg), kMessageFilterCode)) | 392 if (CallMsgFilter(const_cast<MSG*>(&msg), kMessageFilterCode)) |
| 364 return true; | 393 return true; |
| 365 | 394 |
| 366 TranslateMessage(&msg); | 395 TranslateMessage(&msg); |
| 367 DispatchMessage(&msg); | 396 DispatchMessage(&msg); |
| 368 | 397 |
| 369 return true; | 398 return true; |
| 370 } | 399 } |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 385 // OS modal loop, i.e. in the context of a windows API call like MessageBox. | 414 // OS modal loop, i.e. in the context of a windows API call like MessageBox. |
| 386 // This is to ensure that these messages are peeked out by the OS modal loop. | 415 // This is to ensure that these messages are peeked out by the OS modal loop. |
| 387 if (MessageLoop::current()->os_modal_loop()) { | 416 if (MessageLoop::current()->os_modal_loop()) { |
| 388 // We only peek out WM_PAINT and WM_TIMER here for reasons mentioned above. | 417 // We only peek out WM_PAINT and WM_TIMER here for reasons mentioned above. |
| 389 have_message = PeekMessage(&msg, NULL, WM_PAINT, WM_PAINT, PM_REMOVE) || | 418 have_message = PeekMessage(&msg, NULL, WM_PAINT, WM_PAINT, PM_REMOVE) || |
| 390 PeekMessage(&msg, NULL, WM_TIMER, WM_TIMER, PM_REMOVE); | 419 PeekMessage(&msg, NULL, WM_TIMER, WM_TIMER, PM_REMOVE); |
| 391 } else { | 420 } else { |
| 392 have_message = PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) != FALSE; | 421 have_message = PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) != FALSE; |
| 393 } | 422 } |
| 394 | 423 |
| 395 DCHECK(!have_message || kMsgHaveWork != msg.message || | 424 DCHECK(!have_message || !IsWorkMessage(msg)); |
| 396 msg.hwnd != message_hwnd_); | |
| 397 | 425 |
| 398 // Since we discarded a kMsgHaveWork message, we must update the flag. | 426 // Since we discarded a kMsgHaveWork message, we must update the flag. |
| 399 int old_have_work = InterlockedExchange(&have_work_, 0); | 427 int old_work_state_ = InterlockedExchange(&work_state_, READY); |
| 400 DCHECK(old_have_work); | 428 DCHECK_EQ(HAVE_WORK, old_work_state_); |
| 401 | 429 |
| 402 // We don't need a special time slice if we didn't have_message to process. | 430 // We don't need a special time slice if we didn't have_message to process. |
| 403 if (!have_message) | 431 if (!have_message) |
| 404 return false; | 432 return false; |
| 405 | 433 |
| 406 // Guarantee we'll get another time slice in the case where we go into native | 434 // Guarantee we'll get another time slice in the case where we go into native |
| 407 // windows code. This ScheduleWork() may hurt performance a tiny bit when | 435 // windows code. This ScheduleWork() may hurt performance a tiny bit when |
| 408 // tasks appear very infrequently, but when the event queue is busy, the | 436 // tasks appear very infrequently, but when the event queue is busy, the |
| 409 // kMsgHaveWork events get (percentage wise) rarer and rarer. | 437 // kMsgHaveWork events get (percentage wise) rarer and rarer. |
| 410 ScheduleWork(); | 438 ScheduleWork(); |
| 411 return ProcessMessageHelper(msg); | 439 return ProcessMessageHelper(msg); |
| 412 } | 440 } |
| 413 | 441 |
| 414 //----------------------------------------------------------------------------- | 442 //----------------------------------------------------------------------------- |
| 443 // MessagePumpForGpu public: | |
| 444 | |
| 445 MessagePumpForGpu::MessagePumpForGpu() { | |
| 446 } | |
| 447 | |
| 448 MessagePumpForGpu::~MessagePumpForGpu() { | |
| 449 } | |
| 450 | |
| 451 //----------------------------------------------------------------------------- | |
| 452 // MessagePumpForGpu private: | |
| 453 | |
| 454 void MessagePumpForGpu::ScheduleDelayedWork( | |
| 455 const TimeTicks& delayed_work_time) { | |
| 456 // We know that we can't be blocked right now since this method can only be | |
| 457 // called on the same thread as Run, so we only need to update our record of | |
| 458 // how long to sleep when we do sleep. | |
| 459 delayed_work_time_ = delayed_work_time; | |
| 460 } | |
| 461 | |
| 462 void MessagePumpForGpu::DoRunLoop() { | |
| 463 while (!state_->should_quit) { | |
| 464 // Indicate that the loop is handling the work. | |
| 465 // If there is a race condition between switching to WORKING state here and | |
| 466 // the producer thread setting the HAVE_WORK state after exiting the wait, | |
| 467 // the event might remain in the signalled state. That might be less than | |
| 468 // optimal but wouldn't result in failing to handle the work. | |
| 469 InterlockedExchange(&work_state_, WORKING); | |
| 470 | |
| 471 bool more_work_is_plausible = state_->delegate->DoWork(); | |
| 472 if (state_->should_quit) | |
| 473 break; | |
| 474 | |
| 475 more_work_is_plausible |= | |
| 476 state_->delegate->DoDelayedWork(&delayed_work_time_); | |
| 477 if (state_->should_quit) | |
| 478 break; | |
| 479 | |
| 480 if (more_work_is_plausible) | |
| 481 continue; | |
| 482 | |
| 483 more_work_is_plausible = state_->delegate->DoIdleWork(); | |
| 484 if (state_->should_quit) | |
| 485 break; | |
| 486 | |
| 487 if (more_work_is_plausible) | |
| 488 continue; | |
| 489 | |
| 490 // Switch that working state to READY to indicate that the loop is | |
| 491 // waiting for accepting new work if it is still in WORKING state and hasn't | |
| 492 // been signalled. Otherwise if it is in HAVE_WORK state skip the wait | |
| 493 // and proceed to handing the work. | |
| 494 if (HAVE_WORK == InterlockedCompareExchange(&work_state_, READY, WORKING)) | |
| 495 continue; // Skip wait, more work was requested. | |
| 496 | |
| 497 WaitForWork(); // Wait (sleep) until we have work to do again. | |
| 498 } | |
| 499 } | |
| 500 | |
| 501 void MessagePumpForGpu::WaitForWork() { | |
| 502 // Wait until a message is available, up to the time needed by the timer | |
| 503 // manager to fire the next set of timers. | |
| 504 int delay; | |
| 505 | |
| 506 // The while loop handles the situation where on Windows 7 and later versions | |
| 507 // MsgWaitForMultipleObjectsEx might time out slightly earlier (less than one | |
| 508 // ms) than the specified |delay|. In that situation it is more optimal to | |
| 509 // just wait again rather than waste a DoRunLoop cycle. | |
| 510 while((delay = GetCurrentDelay()) != 0) { | |
| 511 if (delay < 0) // Negative value means no timers waiting. | |
| 512 delay = INFINITE; | |
| 513 | |
| 514 DWORD result = MsgWaitForMultipleObjectsEx(0, NULL, delay, QS_ALLINPUT, | |
| 515 MWMO_INPUTAVAILABLE); | |
| 516 if (WAIT_OBJECT_0 == result) { | |
| 517 // A WM_* message is available. | |
| 518 ProcessWindowsMessages(); | |
| 519 return; | |
| 520 } | |
| 521 | |
| 522 DCHECK_NE(WAIT_FAILED, result) << GetLastError(); | |
| 523 } | |
| 524 } | |
| 525 | |
| 526 void MessagePumpForGpu::ProcessWindowsMessages() { | |
| 527 MSG msg; | |
| 528 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) != FALSE) { | |
| 529 if (WM_QUIT == msg.message) { | |
| 530 // Repost the QUIT message so that it will be retrieved by the primary | |
| 531 // GetMessage() loop. | |
| 532 state_->should_quit = true; | |
| 533 PostQuitMessage(static_cast<int>(msg.wParam)); | |
| 534 return; | |
| 535 } | |
| 536 | |
| 537 if (!IsWorkMessage(msg)) { | |
| 538 TranslateMessage(&msg); | |
| 539 DispatchMessage(&msg); | |
| 540 } | |
| 541 } | |
| 542 } | |
| 543 | |
| 544 void MessagePumpForGpu::HandleWorkMessage() { | |
| 545 NOTREACHED(); | |
| 546 } | |
| 547 | |
| 548 void MessagePumpForGpu::HandleTimerMessage() { | |
| 549 NOTREACHED(); | |
| 550 } | |
| 551 | |
| 552 void MessagePumpForGpu::InitFactory() { | |
| 553 MessageLoop::InitMessagePumpForUIFactory( | |
| 554 &MessagePumpForGpu::CreateMessagePumpForGpu); | |
| 555 } | |
| 556 | |
| 557 scoped_ptr<MessagePump> MessagePumpForGpu::CreateMessagePumpForGpu() { | |
| 558 return scoped_ptr<MessagePump>(new MessagePumpForGpu); | |
| 559 } | |
| 560 | |
| 561 //----------------------------------------------------------------------------- | |
| 415 // MessagePumpForIO public: | 562 // MessagePumpForIO public: |
| 416 | 563 |
| 417 MessagePumpForIO::MessagePumpForIO() { | 564 MessagePumpForIO::MessagePumpForIO() { |
| 418 port_.Set(CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, NULL, 1)); | 565 port_.Set(CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, NULL, 1)); |
| 419 DCHECK(port_.IsValid()); | 566 DCHECK(port_.IsValid()); |
| 420 } | 567 } |
| 421 | 568 |
| 422 MessagePumpForIO::~MessagePumpForIO() { | 569 MessagePumpForIO::~MessagePumpForIO() { |
| 423 } | 570 } |
| 424 | 571 |
| 425 void MessagePumpForIO::ScheduleWork() { | 572 void MessagePumpForIO::ScheduleWork() { |
| 426 if (InterlockedExchange(&have_work_, 1)) | 573 if (READY != InterlockedExchange(&work_state_, HAVE_WORK)) |
| 427 return; // Someone else continued the pumping. | 574 return; // Someone else continued the pumping. |
| 428 | 575 |
| 429 // Make sure the MessagePump does some work for us. | 576 // Make sure the MessagePump does some work for us. |
| 430 BOOL ret = PostQueuedCompletionStatus(port_.Get(), 0, | 577 BOOL ret = PostQueuedCompletionStatus(port_.Get(), 0, |
| 431 reinterpret_cast<ULONG_PTR>(this), | 578 reinterpret_cast<ULONG_PTR>(this), |
| 432 reinterpret_cast<OVERLAPPED*>(this)); | 579 reinterpret_cast<OVERLAPPED*>(this)); |
| 433 if (ret) | 580 if (ret) |
| 434 return; // Post worked perfectly. | 581 return; // Post worked perfectly. |
| 435 | 582 |
| 436 // See comment in MessagePumpForUI::ScheduleWork() for this error recovery. | 583 // See comment in MessagePumpForUI::ScheduleWork() for this error recovery. |
| 437 InterlockedExchange(&have_work_, 0); // Clarify that we didn't succeed. | 584 InterlockedExchange(&work_state_, READY); // Clarify that we didn't succeed. |
| 438 UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", COMPLETION_POST_ERROR, | 585 UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", COMPLETION_POST_ERROR, |
| 439 MESSAGE_LOOP_PROBLEM_MAX); | 586 MESSAGE_LOOP_PROBLEM_MAX); |
| 440 } | 587 } |
| 441 | 588 |
| 442 void MessagePumpForIO::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { | 589 void MessagePumpForIO::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { |
| 443 // We know that we can't be blocked right now since this method can only be | 590 // We know that we can't be blocked right now since this method can only be |
| 444 // called on the same thread as Run, so we only need to update our record of | 591 // called on the same thread as Run, so we only need to update our record of |
| 445 // how long to sleep when we do sleep. | 592 // how long to sleep when we do sleep. |
| 446 delayed_work_time_ = delayed_work_time; | 593 delayed_work_time_ = delayed_work_time; |
| 447 } | 594 } |
| (...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 572 item->handler = KeyToHandler(key, &item->has_valid_io_context); | 719 item->handler = KeyToHandler(key, &item->has_valid_io_context); |
| 573 item->context = reinterpret_cast<IOContext*>(overlapped); | 720 item->context = reinterpret_cast<IOContext*>(overlapped); |
| 574 return true; | 721 return true; |
| 575 } | 722 } |
| 576 | 723 |
| 577 bool MessagePumpForIO::ProcessInternalIOItem(const IOItem& item) { | 724 bool MessagePumpForIO::ProcessInternalIOItem(const IOItem& item) { |
| 578 if (reinterpret_cast<void*>(this) == reinterpret_cast<void*>(item.context) && | 725 if (reinterpret_cast<void*>(this) == reinterpret_cast<void*>(item.context) && |
| 579 reinterpret_cast<void*>(this) == reinterpret_cast<void*>(item.handler)) { | 726 reinterpret_cast<void*>(this) == reinterpret_cast<void*>(item.handler)) { |
| 580 // This is our internal completion. | 727 // This is our internal completion. |
| 581 DCHECK(!item.bytes_transfered); | 728 DCHECK(!item.bytes_transfered); |
| 582 InterlockedExchange(&have_work_, 0); | 729 InterlockedExchange(&work_state_, READY); |
| 583 return true; | 730 return true; |
| 584 } | 731 } |
| 585 return false; | 732 return false; |
| 586 } | 733 } |
| 587 | 734 |
| 588 // Returns a completion item that was previously received. | 735 // Returns a completion item that was previously received. |
| 589 bool MessagePumpForIO::MatchCompletedIOItem(IOHandler* filter, IOItem* item) { | 736 bool MessagePumpForIO::MatchCompletedIOItem(IOHandler* filter, IOItem* item) { |
| 590 DCHECK(!completed_io_.empty()); | 737 DCHECK(!completed_io_.empty()); |
| 591 for (std::list<IOItem>::iterator it = completed_io_.begin(); | 738 for (std::list<IOItem>::iterator it = completed_io_.begin(); |
| 592 it != completed_io_.end(); ++it) { | 739 it != completed_io_.end(); ++it) { |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 633 | 780 |
| 634 // static | 781 // static |
| 635 MessagePumpForIO::IOHandler* MessagePumpForIO::KeyToHandler( | 782 MessagePumpForIO::IOHandler* MessagePumpForIO::KeyToHandler( |
| 636 ULONG_PTR key, | 783 ULONG_PTR key, |
| 637 bool* has_valid_io_context) { | 784 bool* has_valid_io_context) { |
| 638 *has_valid_io_context = ((key & 1) == 0); | 785 *has_valid_io_context = ((key & 1) == 0); |
| 639 return reinterpret_cast<IOHandler*>(key & ~static_cast<ULONG_PTR>(1)); | 786 return reinterpret_cast<IOHandler*>(key & ~static_cast<ULONG_PTR>(1)); |
| 640 } | 787 } |
| 641 | 788 |
| 642 } // namespace base | 789 } // namespace base |
| OLD | NEW |