Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(259)

Side by Side Diff: base/message_pump_win.cc

Issue 8156: Switch MessagePumpForIO to use completion ports on Windows.... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 12 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « base/message_pump_win.h ('k') | chrome/common/ipc_channel.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2006-2008 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/histogram.h" 9 #include "base/histogram.h"
10 #include "base/win_util.h" 10 #include "base/win_util.h"
11 11
12 using base::Time; 12 using base::Time;
13 13
14 namespace {
15
16 class HandlerData : public base::MessagePumpForIO::Watcher {
17 public:
18 typedef base::MessagePumpForIO::IOHandler IOHandler;
19 HandlerData(OVERLAPPED* context, IOHandler* handler)
20 : context_(context), handler_(handler) {}
21 ~HandlerData() {}
22
23 virtual void OnObjectSignaled(HANDLE object);
24
25 private:
26 OVERLAPPED* context_;
27 IOHandler* handler_;
28
29 DISALLOW_COPY_AND_ASSIGN(HandlerData);
30 };
31
32 void HandlerData::OnObjectSignaled(HANDLE object) {
33 DCHECK(object == context_->hEvent);
34 DWORD transfered;
35 DWORD error = ERROR_SUCCESS;
36 BOOL ret = GetOverlappedResult(NULL, context_, &transfered, FALSE);
37 if (!ret) {
38 error = GetLastError();
39 DCHECK(ERROR_HANDLE_EOF == error || ERROR_BROKEN_PIPE == error);
40 transfered = 0;
41 }
42
43 ResetEvent(context_->hEvent);
44 handler_->OnIOCompleted(context_, transfered, error);
45 }
46
47 } // namespace
48
49 namespace base { 14 namespace base {
50 15
51 static const wchar_t kWndClass[] = L"Chrome_MessagePumpWindow"; 16 static const wchar_t kWndClass[] = L"Chrome_MessagePumpWindow";
52 17
53 // Message sent to get an additional time slice for pumping (processing) another 18 // Message sent to get an additional time slice for pumping (processing) another
54 // task (a series of such messages creates a continuous task pump). 19 // task (a series of such messages creates a continuous task pump).
55 static const int kMsgHaveWork = WM_USER + 1; 20 static const int kMsgHaveWork = WM_USER + 1;
56 21
57 #ifndef NDEBUG
58 // Force exercise of polling model.
59 static const int kMaxWaitObjects = 8;
60 #else
61 static const int kMaxWaitObjects = MAXIMUM_WAIT_OBJECTS;
62 #endif
63
64 //----------------------------------------------------------------------------- 22 //-----------------------------------------------------------------------------
65 // MessagePumpWin public: 23 // MessagePumpWin public:
66 24
67 MessagePumpWin::MessagePumpWin() : have_work_(0), state_(NULL) {
68 InitMessageWnd();
69 }
70
71 MessagePumpWin::~MessagePumpWin() {
72 DestroyWindow(message_hwnd_);
73 }
74
75 void MessagePumpWin::AddObserver(Observer* observer) { 25 void MessagePumpWin::AddObserver(Observer* observer) {
76 observers_.AddObserver(observer); 26 observers_.AddObserver(observer);
77 } 27 }
78 28
79 void MessagePumpWin::RemoveObserver(Observer* observer) { 29 void MessagePumpWin::RemoveObserver(Observer* observer) {
80 observers_.RemoveObserver(observer); 30 observers_.RemoveObserver(observer);
81 } 31 }
82 32
83 void MessagePumpWin::WillProcessMessage(const MSG& msg) { 33 void MessagePumpWin::WillProcessMessage(const MSG& msg) {
84 FOR_EACH_OBSERVER(Observer, observers_, WillProcessMessage(msg)); 34 FOR_EACH_OBSERVER(Observer, observers_, WillProcessMessage(msg));
85 } 35 }
86 36
87 void MessagePumpWin::DidProcessMessage(const MSG& msg) { 37 void MessagePumpWin::DidProcessMessage(const MSG& msg) {
88 FOR_EACH_OBSERVER(Observer, observers_, DidProcessMessage(msg)); 38 FOR_EACH_OBSERVER(Observer, observers_, DidProcessMessage(msg));
89 } 39 }
90 40
91 void MessagePumpWin::PumpOutPendingPaintMessages() {
92 // If we are being called outside of the context of Run, then don't try to do
93 // any work.
94 if (!state_)
95 return;
96
97 // Create a mini-message-pump to force immediate processing of only Windows
98 // WM_PAINT messages. Don't provide an infinite loop, but do enough peeking
99 // to get the job done. Actual common max is 4 peeks, but we'll be a little
100 // safe here.
101 const int kMaxPeekCount = 20;
102 bool win2k = win_util::GetWinVersion() <= win_util::WINVERSION_2000;
103 int peek_count;
104 for (peek_count = 0; peek_count < kMaxPeekCount; ++peek_count) {
105 MSG msg;
106 if (win2k) {
107 if (!PeekMessage(&msg, NULL, WM_PAINT, WM_PAINT, PM_REMOVE))
108 break;
109 } else {
110 if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE | PM_QS_PAINT))
111 break;
112 }
113 ProcessMessageHelper(msg);
114 if (state_->should_quit) // Handle WM_QUIT.
115 break;
116 }
117 // Histogram what was really being used, to help to adjust kMaxPeekCount.
118 DHISTOGRAM_COUNTS(L"Loop.PumpOutPendingPaintMessages Peeks", peek_count);
119 }
120
121 void MessagePumpWin::RunWithDispatcher( 41 void MessagePumpWin::RunWithDispatcher(
122 Delegate* delegate, Dispatcher* dispatcher) { 42 Delegate* delegate, Dispatcher* dispatcher) {
123 RunState s; 43 RunState s;
124 s.delegate = delegate; 44 s.delegate = delegate;
125 s.dispatcher = dispatcher; 45 s.dispatcher = dispatcher;
126 s.should_quit = false; 46 s.should_quit = false;
127 s.run_depth = state_ ? state_->run_depth + 1 : 1; 47 s.run_depth = state_ ? state_->run_depth + 1 : 1;
128 48
129 RunState* previous_state = state_; 49 RunState* previous_state = state_;
130 state_ = &s; 50 state_ = &s;
131 51
132 DoRunLoop(); 52 DoRunLoop();
133 53
134 state_ = previous_state; 54 state_ = previous_state;
135 } 55 }
136 56
137 void MessagePumpWin::Quit() { 57 void MessagePumpWin::Quit() {
138 DCHECK(state_); 58 DCHECK(state_);
139 state_->should_quit = true; 59 state_->should_quit = true;
140 } 60 }
141 61
142 void MessagePumpWin::ScheduleWork() { 62 //-----------------------------------------------------------------------------
63 // MessagePumpWin protected:
64
65 int MessagePumpWin::GetCurrentDelay() const {
66 if (delayed_work_time_.is_null())
67 return -1;
68
69 // Be careful here. TimeDelta has a precision of microseconds, but we want a
70 // value in milliseconds. If there are 5.5ms left, should the delay be 5 or
71 // 6? It should be 6 to avoid executing delayed work too early.
72 double timeout = ceil((delayed_work_time_ - Time::Now()).InMillisecondsF());
73
74 // If this value is negative, then we need to run delayed work soon.
75 int delay = static_cast<int>(timeout);
76 if (delay < 0)
77 delay = 0;
78
79 return delay;
80 }
81
82 //-----------------------------------------------------------------------------
83 // MessagePumpForUI public:
84
85 MessagePumpForUI::MessagePumpForUI() {
86 InitMessageWnd();
87 }
88
89 MessagePumpForUI::~MessagePumpForUI() {
90 DestroyWindow(message_hwnd_);
91 }
92
93 void MessagePumpForUI::ScheduleWork() {
143 if (InterlockedExchange(&have_work_, 1)) 94 if (InterlockedExchange(&have_work_, 1))
144 return; // Someone else continued the pumping. 95 return; // Someone else continued the pumping.
145 96
146 // Make sure the MessagePump does some work for us. 97 // Make sure the MessagePump does some work for us.
147 PostMessage(message_hwnd_, kMsgHaveWork, reinterpret_cast<WPARAM>(this), 0); 98 PostMessage(message_hwnd_, kMsgHaveWork, reinterpret_cast<WPARAM>(this), 0);
148 } 99 }
149 100
150 void MessagePumpWin::ScheduleDelayedWork(const Time& delayed_work_time) { 101 void MessagePumpForUI::ScheduleDelayedWork(const Time& delayed_work_time) {
151 // 102 //
152 // We would *like* to provide high resolution timers. Windows timers using 103 // We would *like* to provide high resolution timers. Windows timers using
153 // SetTimer() have a 10ms granularity. We have to use WM_TIMER as a wakeup 104 // SetTimer() have a 10ms granularity. We have to use WM_TIMER as a wakeup
154 // mechanism because the application can enter modal windows loops where it 105 // mechanism because the application can enter modal windows loops where it
155 // is not running our MessageLoop; the only way to have our timers fire in 106 // is not running our MessageLoop; the only way to have our timers fire in
156 // these cases is to post messages there. 107 // these cases is to post messages there.
157 // 108 //
158 // To provide sub-10ms timers, we process timers directly from our run loop. 109 // To provide sub-10ms timers, we process timers directly from our run loop.
159 // For the common case, timers will be processed there as the run loop does 110 // For the common case, timers will be processed there as the run loop does
160 // its normal work. However, we *also* set the system timer so that WM_TIMER 111 // its normal work. However, we *also* set the system timer so that WM_TIMER
(...skipping 12 matching lines...) Expand all
173 int delay_msec = GetCurrentDelay(); 124 int delay_msec = GetCurrentDelay();
174 DCHECK(delay_msec >= 0); 125 DCHECK(delay_msec >= 0);
175 if (delay_msec < USER_TIMER_MINIMUM) 126 if (delay_msec < USER_TIMER_MINIMUM)
176 delay_msec = USER_TIMER_MINIMUM; 127 delay_msec = USER_TIMER_MINIMUM;
177 128
178 // Create a WM_TIMER event that will wake us up to check for any pending 129 // Create a WM_TIMER event that will wake us up to check for any pending
179 // timers (in case we are running within a nested, external sub-pump). 130 // timers (in case we are running within a nested, external sub-pump).
180 SetTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this), delay_msec, NULL); 131 SetTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this), delay_msec, NULL);
181 } 132 }
182 133
134 void MessagePumpForUI::PumpOutPendingPaintMessages() {
135 // If we are being called outside of the context of Run, then don't try to do
136 // any work.
137 if (!state_)
138 return;
139
140 // Create a mini-message-pump to force immediate processing of only Windows
141 // WM_PAINT messages. Don't provide an infinite loop, but do enough peeking
142 // to get the job done. Actual common max is 4 peeks, but we'll be a little
143 // safe here.
144 const int kMaxPeekCount = 20;
145 bool win2k = win_util::GetWinVersion() <= win_util::WINVERSION_2000;
146 int peek_count;
147 for (peek_count = 0; peek_count < kMaxPeekCount; ++peek_count) {
148 MSG msg;
149 if (win2k) {
150 if (!PeekMessage(&msg, NULL, WM_PAINT, WM_PAINT, PM_REMOVE))
151 break;
152 } else {
153 if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE | PM_QS_PAINT))
154 break;
155 }
156 ProcessMessageHelper(msg);
157 if (state_->should_quit) // Handle WM_QUIT.
158 break;
159 }
160 // Histogram what was really being used, to help to adjust kMaxPeekCount.
161 DHISTOGRAM_COUNTS(L"Loop.PumpOutPendingPaintMessages Peeks", peek_count);
162 }
163
183 //----------------------------------------------------------------------------- 164 //-----------------------------------------------------------------------------
184 // MessagePumpWin protected: 165 // MessagePumpForUI private:
185 166
186 // static 167 // static
187 LRESULT CALLBACK MessagePumpWin::WndProcThunk( 168 LRESULT CALLBACK MessagePumpForUI::WndProcThunk(
188 HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { 169 HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {
189 switch (message) { 170 switch (message) {
190 case kMsgHaveWork: 171 case kMsgHaveWork:
191 reinterpret_cast<MessagePumpWin*>(wparam)->HandleWorkMessage(); 172 reinterpret_cast<MessagePumpForUI*>(wparam)->HandleWorkMessage();
192 break; 173 break;
193 case WM_TIMER: 174 case WM_TIMER:
194 reinterpret_cast<MessagePumpWin*>(wparam)->HandleTimerMessage(); 175 reinterpret_cast<MessagePumpForUI*>(wparam)->HandleTimerMessage();
195 break; 176 break;
196 } 177 }
197 return DefWindowProc(hwnd, message, wparam, lparam); 178 return DefWindowProc(hwnd, message, wparam, lparam);
198 } 179 }
199 180
200 void MessagePumpWin::InitMessageWnd() { 181 void MessagePumpForUI::DoRunLoop() {
182 // IF this was just a simple PeekMessage() loop (servicing all possible work
183 // queues), then Windows would try to achieve the following order according
184 // to MSDN documentation about PeekMessage with no filter):
185 // * Sent messages
186 // * Posted messages
187 // * Sent messages (again)
188 // * WM_PAINT messages
189 // * WM_TIMER messages
190 //
191 // Summary: none of the above classes is starved, and sent messages has twice
192 // the chance of being processed (i.e., reduced service time).
193
194 for (;;) {
195 // If we do any work, we may create more messages etc., and more work may
196 // possibly be waiting in another task group. When we (for example)
197 // ProcessNextWindowsMessage(), there is a good chance there are still more
198 // messages waiting. On the other hand, when any of these methods return
199 // having done no work, then it is pretty unlikely that calling them again
200 // quickly will find any work to do. Finally, if they all say they had no
201 // work, then it is a good time to consider sleeping (waiting) for more
202 // work.
203
204 bool more_work_is_plausible = ProcessNextWindowsMessage();
205 if (state_->should_quit)
206 break;
207
208 more_work_is_plausible |= state_->delegate->DoWork();
209 if (state_->should_quit)
210 break;
211
212 more_work_is_plausible |=
213 state_->delegate->DoDelayedWork(&delayed_work_time_);
214 // If we did not process any delayed work, then we can assume that our
215 // existing WM_TIMER if any will fire when delayed work should run. We
216 // don't want to disturb that timer if it is already in flight. However,
217 // if we did do all remaining delayed work, then lets kill the WM_TIMER.
218 if (more_work_is_plausible && delayed_work_time_.is_null())
219 KillTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this));
220 if (state_->should_quit)
221 break;
222
223 if (more_work_is_plausible)
224 continue;
225
226 more_work_is_plausible = state_->delegate->DoIdleWork();
227 if (state_->should_quit)
228 break;
229
230 if (more_work_is_plausible)
231 continue;
232
233 WaitForWork(); // Wait (sleep) until we have work to do again.
234 }
235 }
236
237 void MessagePumpForUI::InitMessageWnd() {
201 HINSTANCE hinst = GetModuleHandle(NULL); 238 HINSTANCE hinst = GetModuleHandle(NULL);
202 239
203 WNDCLASSEX wc = {0}; 240 WNDCLASSEX wc = {0};
204 wc.cbSize = sizeof(wc); 241 wc.cbSize = sizeof(wc);
205 wc.lpfnWndProc = WndProcThunk; 242 wc.lpfnWndProc = WndProcThunk;
206 wc.hInstance = hinst; 243 wc.hInstance = hinst;
207 wc.lpszClassName = kWndClass; 244 wc.lpszClassName = kWndClass;
208 RegisterClassEx(&wc); 245 RegisterClassEx(&wc);
209 246
210 message_hwnd_ = 247 message_hwnd_ =
211 CreateWindow(kWndClass, 0, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, hinst, 0); 248 CreateWindow(kWndClass, 0, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, hinst, 0);
212 DCHECK(message_hwnd_); 249 DCHECK(message_hwnd_);
213 } 250 }
214 251
215 void MessagePumpWin::HandleWorkMessage() { 252 void MessagePumpForUI::WaitForWork() {
253 // Wait until a message is available, up to the time needed by the timer
254 // manager to fire the next set of timers.
255 int delay = GetCurrentDelay();
256 if (delay < 0) // Negative value means no timers waiting.
257 delay = INFINITE;
258
259 DWORD result;
260 result = MsgWaitForMultipleObjectsEx(0, NULL, delay, QS_ALLINPUT,
261 MWMO_INPUTAVAILABLE);
262
263 if (WAIT_OBJECT_0 == result) {
264 // A WM_* message is available.
265 // If a parent child relationship exists between windows across threads
266 // then their thread inputs are implicitly attached.
267 // This causes the MsgWaitForMultipleObjectsEx API to return indicating
268 // that messages are ready for processing (specifically mouse messages
269 // intended for the child window. Occurs if the child window has capture)
270 // The subsequent PeekMessages call fails to return any messages thus
271 // causing us to enter a tight loop at times.
272 // The WaitMessage call below is a workaround to give the child window
273 // sometime to process its input messages.
274 MSG msg = {0};
275 DWORD queue_status = GetQueueStatus(QS_MOUSE);
276 if (HIWORD(queue_status) & QS_MOUSE &&
277 !PeekMessage(&msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST, PM_NOREMOVE)) {
278 WaitMessage();
279 }
280 return;
281 }
282
283 DCHECK_NE(WAIT_FAILED, result) << GetLastError();
284 }
285
286 void MessagePumpForUI::HandleWorkMessage() {
216 // If we are being called outside of the context of Run, then don't try to do 287 // If we are being called outside of the context of Run, then don't try to do
217 // any work. This could correspond to a MessageBox call or something of that 288 // any work. This could correspond to a MessageBox call or something of that
218 // sort. 289 // sort.
219 if (!state_) { 290 if (!state_) {
220 // Since we handled a kMsgHaveWork message, we must still update this flag. 291 // Since we handled a kMsgHaveWork message, we must still update this flag.
221 InterlockedExchange(&have_work_, 0); 292 InterlockedExchange(&have_work_, 0);
222 return; 293 return;
223 } 294 }
224 295
225 // Let whatever would have run had we not been putting messages in the queue 296 // Let whatever would have run had we not been putting messages in the queue
226 // run now. This is an attempt to make our dummy message not starve other 297 // run now. This is an attempt to make our dummy message not starve other
227 // messages that may be in the Windows message queue. 298 // messages that may be in the Windows message queue.
228 ProcessPumpReplacementMessage(); 299 ProcessPumpReplacementMessage();
229 300
230 // Now give the delegate a chance to do some work. He'll let us know if he 301 // Now give the delegate a chance to do some work. He'll let us know if he
231 // needs to do more work. 302 // needs to do more work.
232 if (state_->delegate->DoWork()) 303 if (state_->delegate->DoWork())
233 ScheduleWork(); 304 ScheduleWork();
234 } 305 }
235 306
236 void MessagePumpWin::HandleTimerMessage() { 307 void MessagePumpForUI::HandleTimerMessage() {
237 KillTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this)); 308 KillTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this));
238 309
239 // If we are being called outside of the context of Run, then don't do 310 // If we are being called outside of the context of Run, then don't do
240 // anything. This could correspond to a MessageBox call or something of 311 // anything. This could correspond to a MessageBox call or something of
241 // that sort. 312 // that sort.
242 if (!state_) 313 if (!state_)
243 return; 314 return;
244 315
245 state_->delegate->DoDelayedWork(&delayed_work_time_); 316 state_->delegate->DoDelayedWork(&delayed_work_time_);
246 if (!delayed_work_time_.is_null()) { 317 if (!delayed_work_time_.is_null()) {
247 // A bit gratuitous to set delayed_work_time_ again, but oh well. 318 // A bit gratuitous to set delayed_work_time_ again, but oh well.
248 ScheduleDelayedWork(delayed_work_time_); 319 ScheduleDelayedWork(delayed_work_time_);
249 } 320 }
250 } 321 }
251 322
252 bool MessagePumpWin::ProcessNextWindowsMessage() { 323 bool MessagePumpForUI::ProcessNextWindowsMessage() {
253 // If there are sent messages in the queue then PeekMessage internally 324 // If there are sent messages in the queue then PeekMessage internally
254 // dispatches the message and returns false. We return true in this 325 // dispatches the message and returns false. We return true in this
255 // case to ensure that the message loop peeks again instead of calling 326 // case to ensure that the message loop peeks again instead of calling
256 // MsgWaitForMultipleObjectsEx again. 327 // MsgWaitForMultipleObjectsEx again.
257 bool sent_messages_in_queue = false; 328 bool sent_messages_in_queue = false;
258 DWORD queue_status = GetQueueStatus(QS_SENDMESSAGE); 329 DWORD queue_status = GetQueueStatus(QS_SENDMESSAGE);
259 if (HIWORD(queue_status) & QS_SENDMESSAGE) 330 if (HIWORD(queue_status) & QS_SENDMESSAGE)
260 sent_messages_in_queue = true; 331 sent_messages_in_queue = true;
261 332
262 MSG msg; 333 MSG msg;
263 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) 334 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
264 return ProcessMessageHelper(msg); 335 return ProcessMessageHelper(msg);
265 336
266 return sent_messages_in_queue; 337 return sent_messages_in_queue;
267 } 338 }
268 339
269 bool MessagePumpWin::ProcessMessageHelper(const MSG& msg) { 340 bool MessagePumpForUI::ProcessMessageHelper(const MSG& msg) {
270 if (WM_QUIT == msg.message) { 341 if (WM_QUIT == msg.message) {
271 // Repost the QUIT message so that it will be retrieved by the primary 342 // Repost the QUIT message so that it will be retrieved by the primary
272 // GetMessage() loop. 343 // GetMessage() loop.
273 state_->should_quit = true; 344 state_->should_quit = true;
274 PostQuitMessage(static_cast<int>(msg.wParam)); 345 PostQuitMessage(static_cast<int>(msg.wParam));
275 return false; 346 return false;
276 } 347 }
277 348
278 // While running our main message pump, we discard kMsgHaveWork messages. 349 // While running our main message pump, we discard kMsgHaveWork messages.
279 if (msg.message == kMsgHaveWork && msg.hwnd == message_hwnd_) 350 if (msg.message == kMsgHaveWork && msg.hwnd == message_hwnd_)
280 return ProcessPumpReplacementMessage(); 351 return ProcessPumpReplacementMessage();
281 352
282 WillProcessMessage(msg); 353 WillProcessMessage(msg);
283 354
284 if (state_->dispatcher) { 355 if (state_->dispatcher) {
285 if (!state_->dispatcher->Dispatch(msg)) 356 if (!state_->dispatcher->Dispatch(msg))
286 state_->should_quit = true; 357 state_->should_quit = true;
287 } else { 358 } else {
288 TranslateMessage(&msg); 359 TranslateMessage(&msg);
289 DispatchMessage(&msg); 360 DispatchMessage(&msg);
290 } 361 }
291 362
292 DidProcessMessage(msg); 363 DidProcessMessage(msg);
293 return true; 364 return true;
294 } 365 }
295 366
296 bool MessagePumpWin::ProcessPumpReplacementMessage() { 367 bool MessagePumpForUI::ProcessPumpReplacementMessage() {
297 // When we encounter a kMsgHaveWork message, this method is called to peek 368 // When we encounter a kMsgHaveWork message, this method is called to peek
298 // and process a replacement message, such as a WM_PAINT or WM_TIMER. The 369 // and process a replacement message, such as a WM_PAINT or WM_TIMER. The
299 // goal is to make the kMsgHaveWork as non-intrusive as possible, even though 370 // goal is to make the kMsgHaveWork as non-intrusive as possible, even though
300 // a continuous stream of such messages are posted. This method carefully 371 // a continuous stream of such messages are posted. This method carefully
301 // peeks a message while there is no chance for a kMsgHaveWork to be pending, 372 // peeks a message while there is no chance for a kMsgHaveWork to be pending,
302 // then resets the have_work_ flag (allowing a replacement kMsgHaveWork to 373 // then resets the have_work_ flag (allowing a replacement kMsgHaveWork to
303 // possibly be posted), and finally dispatches that peeked replacement. Note 374 // possibly be posted), and finally dispatches that peeked replacement. Note
304 // that the re-post of kMsgHaveWork may be asynchronous to this thread!! 375 // that the re-post of kMsgHaveWork may be asynchronous to this thread!!
305 376
306 MSG msg; 377 MSG msg;
307 bool have_message = (0 != PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)); 378 bool have_message = (0 != PeekMessage(&msg, NULL, 0, 0, PM_REMOVE));
308 DCHECK(!have_message || kMsgHaveWork != msg.message || 379 DCHECK(!have_message || kMsgHaveWork != msg.message ||
309 msg.hwnd != message_hwnd_); 380 msg.hwnd != message_hwnd_);
310 381
311 // Since we discarded a kMsgHaveWork message, we must update the flag. 382 // Since we discarded a kMsgHaveWork message, we must update the flag.
312 InterlockedExchange(&have_work_, 0); 383 InterlockedExchange(&have_work_, 0);
313 384
314 // TODO(darin,jar): There is risk of being lost in a sub-pump within the call 385 // TODO(darin,jar): There is risk of being lost in a sub-pump within the call
315 // to ProcessMessageHelper, which could result in no longer getting a 386 // to ProcessMessageHelper, which could result in no longer getting a
316 // kMsgHaveWork message until the next out-of-band call to ScheduleWork. 387 // kMsgHaveWork message until the next out-of-band call to ScheduleWork.
317 388
318 return have_message && ProcessMessageHelper(msg); 389 return have_message && ProcessMessageHelper(msg);
319 } 390 }
320 391
321 int MessagePumpWin::GetCurrentDelay() const {
322 if (delayed_work_time_.is_null())
323 return -1;
324
325 // Be careful here. TimeDelta has a precision of microseconds, but we want a
326 // value in milliseconds. If there are 5.5ms left, should the delay be 5 or
327 // 6? It should be 6 to avoid executing delayed work too early.
328 double timeout = ceil((delayed_work_time_ - Time::Now()).InMillisecondsF());
329
330 // If this value is negative, then we need to run delayed work soon.
331 int delay = static_cast<int>(timeout);
332 if (delay < 0)
333 delay = 0;
334
335 return delay;
336 }
337
338 //-----------------------------------------------------------------------------
339 // MessagePumpForUI private:
340
341 void MessagePumpForUI::DoRunLoop() {
342 // IF this was just a simple PeekMessage() loop (servicing all possible work
343 // queues), then Windows would try to achieve the following order according
344 // to MSDN documentation about PeekMessage with no filter):
345 // * Sent messages
346 // * Posted messages
347 // * Sent messages (again)
348 // * WM_PAINT messages
349 // * WM_TIMER messages
350 //
351 // Summary: none of the above classes is starved, and sent messages has twice
352 // the chance of being processed (i.e., reduced service time).
353
354 for (;;) {
355 // If we do any work, we may create more messages etc., and more work may
356 // possibly be waiting in another task group. When we (for example)
357 // ProcessNextWindowsMessage(), there is a good chance there are still more
358 // messages waiting. On the other hand, when any of these methods return
359 // having done no work, then it is pretty unlikely that calling them again
360 // quickly will find any work to do. Finally, if they all say they had no
361 // work, then it is a good time to consider sleeping (waiting) for more
362 // work.
363
364 bool more_work_is_plausible = ProcessNextWindowsMessage();
365 if (state_->should_quit)
366 break;
367
368 more_work_is_plausible |= state_->delegate->DoWork();
369 if (state_->should_quit)
370 break;
371
372 more_work_is_plausible |=
373 state_->delegate->DoDelayedWork(&delayed_work_time_);
374 // If we did not process any delayed work, then we can assume that our
375 // existing WM_TIMER if any will fire when delayed work should run. We
376 // don't want to disturb that timer if it is already in flight. However,
377 // if we did do all remaining delayed work, then lets kill the WM_TIMER.
378 if (more_work_is_plausible && delayed_work_time_.is_null())
379 KillTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this));
380 if (state_->should_quit)
381 break;
382
383 if (more_work_is_plausible)
384 continue;
385
386 more_work_is_plausible = state_->delegate->DoIdleWork();
387 if (state_->should_quit)
388 break;
389
390 if (more_work_is_plausible)
391 continue;
392
393 WaitForWork(); // Wait (sleep) until we have work to do again.
394 }
395 }
396
397 void MessagePumpForUI::WaitForWork() {
398 // Wait until a message is available, up to the time needed by the timer
399 // manager to fire the next set of timers.
400 int delay = GetCurrentDelay();
401 if (delay < 0) // Negative value means no timers waiting.
402 delay = INFINITE;
403
404 DWORD result;
405 result = MsgWaitForMultipleObjectsEx(0, NULL, delay, QS_ALLINPUT,
406 MWMO_INPUTAVAILABLE);
407
408 if (WAIT_OBJECT_0 == result) {
409 // A WM_* message is available.
410 // If a parent child relationship exists between windows across threads
411 // then their thread inputs are implicitly attached.
412 // This causes the MsgWaitForMultipleObjectsEx API to return indicating
413 // that messages are ready for processing (specifically mouse messages
414 // intended for the child window. Occurs if the child window has capture)
415 // The subsequent PeekMessages call fails to return any messages thus
416 // causing us to enter a tight loop at times.
417 // The WaitMessage call below is a workaround to give the child window
418 // sometime to process its input messages.
419 MSG msg = {0};
420 DWORD queue_status = GetQueueStatus(QS_MOUSE);
421 if (HIWORD(queue_status) & QS_MOUSE &&
422 !PeekMessage(&msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST, PM_NOREMOVE)) {
423 WaitMessage();
424 }
425 return;
426 }
427
428 DCHECK_NE(WAIT_FAILED, result) << GetLastError();
429 }
430
431 //----------------------------------------------------------------------------- 392 //-----------------------------------------------------------------------------
432 // MessagePumpForIO public: 393 // MessagePumpForIO public:
433 394
434 void MessagePumpForIO::WatchObject(HANDLE object, Watcher* watcher) { 395 MessagePumpForIO::MessagePumpForIO() {
435 DCHECK(object); 396 port_.Set(CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, NULL, 1));
436 DCHECK_NE(object, INVALID_HANDLE_VALUE); 397 DCHECK(port_.IsValid());
398 }
437 399
438 std::vector<HANDLE>::iterator it = 400 void MessagePumpForIO::ScheduleWork() {
439 find(objects_.begin(), objects_.end(), object); 401 if (InterlockedExchange(&have_work_, 1))
440 if (watcher) { 402 return; // Someone else continued the pumping.
441 if (it == objects_.end()) { 403
442 static size_t warning_multiple = 1; 404 // Make sure the MessagePump does some work for us.
443 if (objects_.size() >= warning_multiple * MAXIMUM_WAIT_OBJECTS / 2) { 405 BOOL ret = PostQueuedCompletionStatus(port_, 0,
444 LOG(INFO) << "More than " << warning_multiple * MAXIMUM_WAIT_OBJECTS / 2 406 reinterpret_cast<ULONG_PTR>(this),
445 << " objects being watched"; 407 reinterpret_cast<OVERLAPPED*>(this));
446 // This DCHECK() is an artificial limitation, meant to warn us if we 408 DCHECK(ret);
447 // start creating too many objects. It can safely be raised to a higher 409 }
448 // level, and the program is designed to handle much larger values. 410
449 // Before raising this limit, make sure that there is a very good reason 411 void MessagePumpForIO::ScheduleDelayedWork(const Time& delayed_work_time) {
450 // (in your debug testing) to be watching this many objects. 412 // We know that we can't be blocked right now since this method can only be
451 DCHECK(2 <= warning_multiple); 413 // called on the same thread as Run, so we only need to update our record of
452 ++warning_multiple; 414 // how long to sleep when we do sleep.
453 } 415 delayed_work_time_ = delayed_work_time;
454 objects_.push_back(object);
455 watchers_.push_back(watcher);
456 } else {
457 watchers_[it - objects_.begin()] = watcher;
458 }
459 } else if (it != objects_.end()) {
460 std::vector<HANDLE>::difference_type index = it - objects_.begin();
461 objects_.erase(it);
462 watchers_.erase(watchers_.begin() + index);
463 }
464 } 416 }
465 417
466 void MessagePumpForIO::RegisterIOHandler(HANDLE file_handle, 418 void MessagePumpForIO::RegisterIOHandler(HANDLE file_handle,
467 IOHandler* handler) { 419 IOHandler* handler) {
468 #if 0
469 // TODO(rvargas): This is just to give an idea of what this code will look
470 // like when we actually move to completion ports. Of course, we cannot
471 // do this without calling GetQueuedCompletionStatus().
472 ULONG_PTR key = reinterpret_cast<ULONG_PTR>(handler); 420 ULONG_PTR key = reinterpret_cast<ULONG_PTR>(handler);
473 HANDLE port = CreateIoCompletionPort(file_handle, port_, key, 1); 421 HANDLE port = CreateIoCompletionPort(file_handle, port_, key, 1);
474 if (!port_.IsValid()) 422 DCHECK(port == port_.Get());
475 port_.Set(port);
476 #endif
477 }
478
479 void MessagePumpForIO::RegisterIOContext(OVERLAPPED* context,
480 IOHandler* handler) {
481 DCHECK(context->hEvent);
482 if (handler) {
483 HandlerData* watcher = new HandlerData(context, handler);
484 WatchObject(context->hEvent, watcher);
485 } else {
486 std::vector<HANDLE>::iterator it =
487 find(objects_.begin(), objects_.end(), context->hEvent);
488
489 if (it == objects_.end()) {
490 NOTREACHED();
491 return;
492 }
493
494 std::vector<HANDLE>::difference_type index = it - objects_.begin();
495 objects_.erase(it);
496 delete watchers_[index];
497 watchers_.erase(watchers_.begin() + index);
498 }
499 } 423 }
500 424
501 //----------------------------------------------------------------------------- 425 //-----------------------------------------------------------------------------
502 // MessagePumpForIO private: 426 // MessagePumpForIO private:
503 427
504 void MessagePumpForIO::DoRunLoop() { 428 void MessagePumpForIO::DoRunLoop() {
505 // IF this was just a simple PeekMessage() loop (servicing all possible work
506 // queues), then Windows would try to achieve the following order according
507 // to MSDN documentation about PeekMessage with no filter):
508 // * Sent messages
509 // * Posted messages
510 // * Sent messages (again)
511 // * WM_PAINT messages
512 // * WM_TIMER messages
513 //
514 // Summary: none of the above classes is starved, and sent messages has twice
515 // the chance of being processed (i.e., reduced service time).
516
517 for (;;) { 429 for (;;) {
518 // If we do any work, we may create more messages etc., and more work may 430 // If we do any work, we may create more messages etc., and more work may
519 // possibly be waiting in another task group. When we (for example) 431 // possibly be waiting in another task group. When we (for example)
520 // ProcessNextWindowsMessage(), there is a good chance there are still more 432 // WaitForIOCompletion(), there is a good chance there are still more
521 // messages waiting (same thing for ProcessNextObject(), which responds to 433 // messages waiting. On the other hand, when any of these methods return
522 // only one signaled object; etc.). On the other hand, when any of these 434 // having done no work, then it is pretty unlikely that calling them
523 // methods return having done no work, then it is pretty unlikely that 435 // again quickly will find any work to do. Finally, if they all say they
524 // calling them again quickly will find any work to do. Finally, if they 436 // had no work, then it is a good time to consider sleeping (waiting) for
525 // all say they had no work, then it is a good time to consider sleeping 437 // more work.
526 // (waiting) for more work.
527 438
528 bool more_work_is_plausible = ProcessNextWindowsMessage(); 439 bool more_work_is_plausible = state_->delegate->DoWork();
529 if (state_->should_quit) 440 if (state_->should_quit)
530 break; 441 break;
531 442
532 more_work_is_plausible |= state_->delegate->DoWork(); 443 more_work_is_plausible |= WaitForIOCompletion(0, NULL);
533 if (state_->should_quit)
534 break;
535
536 more_work_is_plausible |= ProcessNextObject();
537 if (state_->should_quit) 444 if (state_->should_quit)
538 break; 445 break;
539 446
540 more_work_is_plausible |= 447 more_work_is_plausible |=
541 state_->delegate->DoDelayedWork(&delayed_work_time_); 448 state_->delegate->DoDelayedWork(&delayed_work_time_);
542 // If we did not process any delayed work, then we can assume that our
543 // existing WM_TIMER if any will fire when delayed work should run. We
544 // don't want to disturb that timer if it is already in flight. However,
545 // if we did do all remaining delayed work, then lets kill the WM_TIMER.
546 if (more_work_is_plausible && delayed_work_time_.is_null())
547 KillTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this));
548 if (state_->should_quit) 449 if (state_->should_quit)
549 break; 450 break;
550 451
551 if (more_work_is_plausible) 452 if (more_work_is_plausible)
552 continue; 453 continue;
553 454
554 more_work_is_plausible = state_->delegate->DoIdleWork(); 455 more_work_is_plausible = state_->delegate->DoIdleWork();
555 if (state_->should_quit) 456 if (state_->should_quit)
556 break; 457 break;
557 458
558 if (more_work_is_plausible) 459 if (more_work_is_plausible)
559 continue; 460 continue;
560 461
561 // We service APCs in WaitForWork, without returning.
562 WaitForWork(); // Wait (sleep) until we have work to do again. 462 WaitForWork(); // Wait (sleep) until we have work to do again.
563 } 463 }
564 } 464 }
565 465
566 // If we handle more than the OS limit on the number of objects that can be 466 // Wait until IO completes, up to the time needed by the timer manager to fire
567 // waited for, we'll need to poll (sequencing through subsets of the objects 467 // the next set of timers.
568 // that can be passed in a single OS wait call). The following is the polling 468 void MessagePumpForIO::WaitForWork() {
569 // interval used in that (unusual) case. (I don't have a lot of justifcation 469 // We do not support nested IO message loops. This is to avoid messy
570 // for the specific value, but it needed to be short enough that it would not 470 // recursion problems.
571 // add a lot of latency, and long enough that we wouldn't thrash the CPU for no 471 DCHECK(state_->run_depth == 1) << "Cannot nest an IO message loop!";
572 // reason... especially considering the silly user probably has a million tabs
573 // open, etc.)
574 static const int kMultipleWaitPollingInterval = 20;
575 472
576 void MessagePumpForIO::WaitForWork() { 473 int timeout = GetCurrentDelay();
577 // Wait until either an object is signaled or a message is available. Handle 474 if (timeout < 0) // Negative value means no timers waiting.
578 // (without returning) any APCs (only the IO thread currently has APCs.) 475 timeout = INFINITE;
579 476
580 // We do not support nested message loops when we have watched objects. This 477 WaitForIOCompletion(timeout, NULL);
581 // is to avoid messy recursion problems.
582 DCHECK(objects_.empty() || state_->run_depth == 1) <<
583 "Cannot nest a message loop when there are watched objects!";
584
585 int wait_flags = MWMO_ALERTABLE | MWMO_INPUTAVAILABLE;
586
587 bool use_polling = false; // Poll if too many objects for one OS Wait call.
588 for (;;) {
589 // Do initialization here, in case APC modifies object list.
590 size_t total_objs = objects_.size();
591
592 int delay;
593 size_t polling_index = 0; // The first unprocessed object index.
594 do {
595 size_t objs_len =
596 (polling_index < total_objs) ? total_objs - polling_index : 0;
597 if (objs_len >= MAXIMUM_WAIT_OBJECTS) {
598 objs_len = MAXIMUM_WAIT_OBJECTS - 1;
599 use_polling = true;
600 }
601 HANDLE* objs = objs_len ? polling_index + &objects_.front() : NULL;
602
603 // Only wait up to the time needed by the timer manager to fire the next
604 // set of timers.
605 delay = GetCurrentDelay();
606 if (use_polling && delay > kMultipleWaitPollingInterval)
607 delay = kMultipleWaitPollingInterval;
608 if (delay < 0) // Negative value means no timers waiting.
609 delay = INFINITE;
610
611 DWORD result;
612 result = MsgWaitForMultipleObjectsEx(static_cast<DWORD>(objs_len), objs,
613 delay, QS_ALLINPUT, wait_flags);
614
615 if (WAIT_IO_COMPLETION == result) {
616 // We'll loop here when we service an APC. At it currently stands,
617 // *ONLY* the IO thread uses *any* APCs, so this should have no impact
618 // on the UI thread.
619 break; // Break to outer loop, and waitforwork() again.
620 }
621
622 // Use unsigned type to simplify range detection;
623 size_t signaled_index = result - WAIT_OBJECT_0;
624 if (signaled_index < objs_len) {
625 SignalWatcher(polling_index + signaled_index);
626 return; // We serviced a signaled object.
627 }
628
629 if (objs_len == signaled_index)
630 return; // A WM_* message is available.
631
632 DCHECK_NE(WAIT_FAILED, result) << GetLastError();
633
634 DCHECK(!objs || result == WAIT_TIMEOUT);
635 if (!use_polling)
636 return;
637 polling_index += objs_len;
638 } while (polling_index < total_objs);
639 // For compatibility, we didn't return sooner. This made us do *some* wait
640 // call(s) before returning. This will probably change in next rev.
641 if (!delay || !GetCurrentDelay())
642 return; // No work done, but timer is ready to fire.
643 }
644 } 478 }
645 479
646 bool MessagePumpForIO::ProcessNextObject() { 480 bool MessagePumpForIO::WaitForIOCompletion(DWORD timeout, IOHandler* filter) {
647 size_t total_objs = objects_.size(); 481 IOItem item;
648 if (!total_objs) { 482 if (completed_io_.empty() || !MatchCompletedIOItem(filter, &item)) {
649 return false; 483 // We have to ask the system for another IO completion.
484 if (!GetIOItem(timeout, &item))
485 return false;
486
487 if (ProcessInternalIOItem(item))
488 return true;
650 } 489 }
651 490
652 size_t polling_index = 0; // The first unprocessed object index. 491 if (item.context->handler) {
653 do { 492 if (filter && item.handler != filter) {
654 DCHECK(polling_index < total_objs); 493 // Save this item for later
655 size_t objs_len = total_objs - polling_index; 494 completed_io_.push_back(item);
656 if (objs_len >= kMaxWaitObjects) 495 } else {
657 objs_len = kMaxWaitObjects - 1; 496 DCHECK(item.context->handler == item.handler);
658 HANDLE* objs = polling_index + &objects_.front(); 497 item.handler->OnIOCompleted(item.context, item.bytes_transfered,
659 498 item.error);
660 // Identify 1 pending object, or allow an IO APC to be completed.
661 DWORD result = WaitForMultipleObjectsEx(static_cast<DWORD>(objs_len), objs,
662 FALSE, // 1 signal is sufficient.
663 0, // Wait 0ms.
664 false); // Not alertable (no APC).
665
666 // Use unsigned type to simplify range detection;
667 size_t signaled_index = result - WAIT_OBJECT_0;
668 if (signaled_index < objs_len) {
669 SignalWatcher(polling_index + signaled_index);
670 return true; // We serviced a signaled object.
671 } 499 }
672 500 } else {
673 // If an handle is invalid, it will be WAIT_FAILED. 501 // The handler must be gone by now, just cleanup the mess.
674 DCHECK_EQ(WAIT_TIMEOUT, result) << GetLastError(); 502 delete item.context;
675 polling_index += objs_len; 503 }
676 } while (polling_index < total_objs);
677 return false; // We serviced nothing.
678 }
679
680 bool MessagePumpForIO::SignalWatcher(size_t object_index) {
681 // Signal the watcher corresponding to the given index.
682
683 DCHECK(objects_.size() > object_index);
684
685 // On reception of OnObjectSignaled() to a Watcher object, it may call
686 // WatchObject(). watchers_ and objects_ will be modified. This is expected,
687 // so don't be afraid if, while tracing a OnObjectSignaled() function, the
688 // corresponding watchers_[result] is non-existant.
689 watchers_[object_index]->OnObjectSignaled(objects_[object_index]);
690
691 // Signaled objects tend to be removed from the watch list, and then added
692 // back (appended). As a result, they move to the end of the objects_ array,
693 // and this should make their service "fair" (no HANDLEs should be starved).
694
695 return true; 504 return true;
696 } 505 }
697 506
507 // Asks the OS for another IO completion result.
508 bool MessagePumpForIO::GetIOItem(DWORD timeout, IOItem* item) {
509 memset(item, 0, sizeof(*item));
510 ULONG_PTR key = NULL;
511 OVERLAPPED* overlapped = NULL;
512 if (!GetQueuedCompletionStatus(port_.Get(), &item->bytes_transfered, &key,
513 &overlapped, timeout)) {
514 if (!overlapped)
515 return false; // Nothing in the queue.
516 item->error = GetLastError();
517 item->bytes_transfered = 0;
518 }
519
520 item->handler = reinterpret_cast<IOHandler*>(key);
521 item->context = reinterpret_cast<IOContext*>(overlapped);
522 return true;
523 }
524
525 bool MessagePumpForIO::ProcessInternalIOItem(const IOItem& item) {
526 if (this == reinterpret_cast<MessagePumpForIO*>(item.context) &&
527 this == reinterpret_cast<MessagePumpForIO*>(item.handler)) {
528 // This is our internal completion.
529 DCHECK(!item.bytes_transfered);
530 InterlockedExchange(&have_work_, 0);
531 return true;
532 }
533 return false;
534 }
535
536 // Returns a completion item that was previously received.
537 bool MessagePumpForIO::MatchCompletedIOItem(IOHandler* filter, IOItem* item) {
538 DCHECK(!completed_io_.empty());
539 for (std::list<IOItem>::iterator it = completed_io_.begin();
540 it != completed_io_.end(); ++it) {
541 if (!filter || it->handler == filter) {
542 *item = *it;
543 completed_io_.erase(it);
544 return true;
545 }
546 }
547 return false;
548 }
549
698 } // namespace base 550 } // namespace base
OLDNEW
« no previous file with comments | « base/message_pump_win.h ('k') | chrome/common/ipc_channel.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698