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

Side by Side Diff: base/message_pump_glib.cc

Issue 115812: linux: fix main loop issues with windowless plugins (Closed)
Patch Set: Fixing comments Created 11 years, 6 months 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
« no previous file with comments | « base/message_pump_glib.h ('k') | no next file » | 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) 2008 The Chromium Authors. All rights reserved. 1 // Copyright (c) 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_glib.h" 5 #include "base/message_pump_glib.h"
6 6
7 #include <fcntl.h> 7 #include <fcntl.h>
8 #include <math.h> 8 #include <math.h>
9 9
10 #include "base/eintr_wrapper.h" 10 #include "base/eintr_wrapper.h"
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
43 // system. File descriptors can be attached to the sources. The poll may block 43 // system. File descriptors can be attached to the sources. The poll may block
44 // if none of the Prepare calls returned TRUE. It will block indefinitely, or 44 // if none of the Prepare calls returned TRUE. It will block indefinitely, or
45 // by the minimum time returned by a source in Prepare. 45 // by the minimum time returned by a source in Prepare.
46 // After the poll, GLib calls Check for each source that returned FALSE 46 // After the poll, GLib calls Check for each source that returned FALSE
47 // from Prepare. The return value of Check has the same meaning as for Prepare, 47 // from Prepare. The return value of Check has the same meaning as for Prepare,
48 // making Check a second chance to tell GLib we are ready for Dispatch. 48 // making Check a second chance to tell GLib we are ready for Dispatch.
49 // Finally, GLib calls Dispatch for each source that is ready. If Dispatch 49 // Finally, GLib calls Dispatch for each source that is ready. If Dispatch
50 // returns FALSE, GLib will destroy the source. Dispatch calls may be recursive 50 // returns FALSE, GLib will destroy the source. Dispatch calls may be recursive
51 // (i.e., you can call Run from them), but Prepare and Check cannot. 51 // (i.e., you can call Run from them), but Prepare and Check cannot.
52 // Finalize is called when the source is destroyed. 52 // Finalize is called when the source is destroyed.
53 // NOTE: It is common for subsytems to want to process pending events while
54 // doing intensive work, for example the flash plugin. They usually use the
55 // following pattern (recommended by the GTK docs):
56 // while (gtk_events_pending()) {
57 // gtk_main_iteration();
58 // }
59 //
60 // gtk_events_pending just calls g_main_context_pending, which does the
61 // following:
62 // - Call prepare on all the sources.
63 // - Do the poll with a timeout of 0 (not blocking).
64 // - Call check on all the sources.
65 // - *Does not* call dispatch on the sources.
66 // - Return true if any of prepare() or check() returned true.
67 //
68 // gtk_main_iteration just calls g_main_context_iteration, which does the whole
69 // thing, respecting the timeout for the poll (and block, although it is
70 // expected not to if gtk_events_pending returned true), and call dispatch.
71 //
72 // Thus it is important to only return true from prepare or check if we
73 // actually have events or work to do. We also need to make sure we keep
74 // internal state consistent so that if prepare/check return true when called
75 // from gtk_events_pending, they will still return true when called right
76 // after, from gtk_main_iteration.
77 //
78 // For the GLib pump we try to follow the Windows UI pump model:
79 // - Whenever we receive a wakeup event or the timer for delayed work expires,
80 // we run DoWork and/or DoDelayedWork. That part will also run in the other
81 // event pumps.
82 // - We also run DoWork, DoDelayedWork, and possibly DoIdleWork in the main
83 // loop, around event handling.
53 84
54 struct WorkSource : public GSource { 85 struct WorkSource : public GSource {
55 base::MessagePumpForUI* pump; 86 base::MessagePumpForUI* pump;
56 }; 87 };
57 88
58 gboolean WorkSourcePrepare(GSource* source, 89 gboolean WorkSourcePrepare(GSource* source,
59 gint* timeout_ms) { 90 gint* timeout_ms) {
60 *timeout_ms = static_cast<WorkSource*>(source)->pump->HandlePrepare(); 91 *timeout_ms = static_cast<WorkSource*>(source)->pump->HandlePrepare();
61 // We always return FALSE, so that our timeout is honored. If we were 92 // We always return FALSE, so that our timeout is honored. If we were
62 // to return TRUE, the timeout would be considered to be 0 and the poll 93 // to return TRUE, the timeout would be considered to be 0 and the poll
63 // would never block. Once the poll is finished, Check will be called. 94 // would never block. Once the poll is finished, Check will be called.
64 return FALSE; 95 return FALSE;
65 } 96 }
66 97
67 gboolean WorkSourceCheck(GSource* source) { 98 gboolean WorkSourceCheck(GSource* source) {
68 // Always return TRUE, and Dispatch will be called. 99 // Only return TRUE if Dispatch should be called.
69 return TRUE; 100 return static_cast<WorkSource*>(source)->pump->HandleCheck();
70 } 101 }
71 102
72 gboolean WorkSourceDispatch(GSource* source, 103 gboolean WorkSourceDispatch(GSource* source,
73 GSourceFunc unused_func, 104 GSourceFunc unused_func,
74 gpointer unused_data) { 105 gpointer unused_data) {
75 106
76 static_cast<WorkSource*>(source)->pump->HandleDispatch(); 107 static_cast<WorkSource*>(source)->pump->HandleDispatch();
77 // Always return TRUE so our source stays registered. 108 // Always return TRUE so our source stays registered.
78 return TRUE; 109 return TRUE;
79 } 110 }
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
129 static PlatformThreadId thread_id = PlatformThread::CurrentId(); 160 static PlatformThreadId thread_id = PlatformThread::CurrentId();
130 DCHECK(thread_id == PlatformThread::CurrentId()) << 161 DCHECK(thread_id == PlatformThread::CurrentId()) <<
131 "Running MessagePumpForUI on two different threads; " 162 "Running MessagePumpForUI on two different threads; "
132 "this is unsupported by GLib!"; 163 "this is unsupported by GLib!";
133 #endif 164 #endif
134 165
135 RunState state; 166 RunState state;
136 state.delegate = delegate; 167 state.delegate = delegate;
137 state.should_quit = false; 168 state.should_quit = false;
138 state.run_depth = state_ ? state_->run_depth + 1 : 1; 169 state.run_depth = state_ ? state_->run_depth + 1 : 1;
170 state.has_work = false;
171
172 RunState* previous_state = state_;
173 state_ = &state;
174
139 // We really only do a single task for each iteration of the loop. If we 175 // We really only do a single task for each iteration of the loop. If we
140 // have done something, assume there is likely something more to do. This 176 // have done something, assume there is likely something more to do. This
141 // will mean that we don't block on the message pump until there was nothing 177 // will mean that we don't block on the message pump until there was nothing
142 // more to do. We also set this to true to make sure not to block on the 178 // more to do. We also set this to true to make sure not to block on the
143 // first iteration of the loop, so RunAllPending() works correctly. 179 // first iteration of the loop, so RunAllPending() works correctly.
144 state.more_work_is_plausible = true; 180 bool more_work_is_plausible = true;
145
146 RunState* previous_state = state_;
147 state_ = &state;
148 181
149 // We run our own loop instead of using g_main_loop_quit in one of the 182 // We run our own loop instead of using g_main_loop_quit in one of the
150 // callbacks. This is so we only quit our own loops, and we don't quit 183 // callbacks. This is so we only quit our own loops, and we don't quit
151 // nested loops run by others. TODO(deanm): Is this what we want? 184 // nested loops run by others. TODO(deanm): Is this what we want?
152 while (!state_->should_quit) 185 for (;;) {
153 g_main_context_iteration(context_, true); 186 // Don't block if we think we have more work to do.
187 bool block = !more_work_is_plausible;
188
189 // g_main_context_iteration returns true if events have been dispatched.
190 more_work_is_plausible = g_main_context_iteration(context_, block);
191 if (state_->should_quit)
192 break;
193
194 more_work_is_plausible |= state_->delegate->DoWork();
195 if (state_->should_quit)
196 break;
197
198 more_work_is_plausible |=
199 state_->delegate->DoDelayedWork(&delayed_work_time_);
200 if (state_->should_quit)
201 break;
202
203 if (more_work_is_plausible)
204 continue;
205
206 more_work_is_plausible = state_->delegate->DoIdleWork();
207 if (state_->should_quit)
208 break;
209 }
154 210
155 state_ = previous_state; 211 state_ = previous_state;
156 } 212 }
157 213
158 // Return the timeout we want passed to poll. 214 // Return the timeout we want passed to poll.
159 int MessagePumpForUI::HandlePrepare() { 215 int MessagePumpForUI::HandlePrepare() {
160 // If it's likely that we have more work, don't let the pump 216 // We know we have work, but we haven't called HandleDispatch yet. Don't let
161 // block so that we can do some processing. 217 // the pump block so that we can do some processing.
162 if (state_->more_work_is_plausible) 218 if (state_->has_work)
163 return 0; 219 return 0;
164 220
165 // Work wasn't plausible, so we'll block. In the case where glib fires
166 // our Dispatch(), |more_work_is_plausible| will be reset to whatever it
167 // should be. However, so we don't get starved by more important work,
168 // we set |more_work_is_plausible| to true. This means if we come back
169 // here without having been through Dispatch(), we will get a chance to be
170 // fired and properly do our work in Dispatch().
171 state_->more_work_is_plausible = true;
172
173 // We don't think we have work to do, but make sure not to block 221 // We don't think we have work to do, but make sure not to block
174 // longer than the next time we need to run delayed work. 222 // longer than the next time we need to run delayed work.
175 return GetTimeIntervalMilliseconds(delayed_work_time_); 223 return GetTimeIntervalMilliseconds(delayed_work_time_);
176 } 224 }
177 225
178 void MessagePumpForUI::HandleDispatch() { 226 bool MessagePumpForUI::HandleCheck() {
179 // We should only ever have a single message on the wakeup pipe, since we 227 // We should only ever have a single message on the wakeup pipe, since we
180 // are only signaled when the queue went from empty to non-empty. The glib 228 // are only signaled when the queue went from empty to non-empty. The glib
181 // poll will tell us whether there was data, so this read shouldn't block. 229 // poll will tell us whether there was data, so this read shouldn't block.
182 if (wakeup_gpollfd_.revents & G_IO_IN) { 230 if (wakeup_gpollfd_.revents & G_IO_IN) {
183 char msg; 231 char msg;
184 if (HANDLE_EINTR(read(wakeup_pipe_read_, &msg, 1)) != 1 || msg != '!') { 232 if (HANDLE_EINTR(read(wakeup_pipe_read_, &msg, 1)) != 1 || msg != '!') {
185 NOTREACHED() << "Error reading from the wakeup pipe."; 233 NOTREACHED() << "Error reading from the wakeup pipe.";
186 } 234 }
235 // Since we ate the message, we need to record that we have more work,
236 // because HandleCheck() may be called without HandleDispatch being called
237 // afterwards.
238 state_->has_work = true;
239 }
240
241 if (state_->has_work)
242 return true;
243
244 if (GetTimeIntervalMilliseconds(delayed_work_time_) == 0) {
245 // The timer has expired. That condition will stay true until we process
246 // that delayed work, so we don't need to record this differently.
247 return true;
248 }
249
250 return false;
251 }
252
253 void MessagePumpForUI::HandleDispatch() {
254 state_->has_work = false;
255 if (state_->delegate->DoWork()) {
256 // NOTE: on Windows at this point we would call ScheduleWork (see
257 // MessagePumpForUI::HandleWorkMessage in message_pump_win.cc). But here,
258 // instead of posting a message on the wakeup pipe, we can avoid the
259 // syscalls and just signal that we have more work.
260 state_->has_work = true;
187 } 261 }
188 262
189 if (state_->should_quit) 263 if (state_->should_quit)
190 return; 264 return;
191 265
192 state_->more_work_is_plausible = false; 266 state_->delegate->DoDelayedWork(&delayed_work_time_);
193
194 if (state_->delegate->DoWork())
195 state_->more_work_is_plausible = true;
196
197 if (state_->should_quit)
198 return;
199
200 if (state_->delegate->DoDelayedWork(&delayed_work_time_))
201 state_->more_work_is_plausible = true;
202 if (state_->should_quit)
203 return;
204
205 // Don't do idle work if we think there are more important things
206 // that we could be doing.
207 if (state_->more_work_is_plausible)
208 return;
209
210 if (state_->delegate->DoIdleWork())
211 state_->more_work_is_plausible = true;
212 if (state_->should_quit)
213 return;
214 } 267 }
215 268
216 void MessagePumpForUI::AddObserver(Observer* observer) { 269 void MessagePumpForUI::AddObserver(Observer* observer) {
217 observers_.AddObserver(observer); 270 observers_.AddObserver(observer);
218 } 271 }
219 272
220 void MessagePumpForUI::RemoveObserver(Observer* observer) { 273 void MessagePumpForUI::RemoveObserver(Observer* observer) {
221 observers_.RemoveObserver(observer); 274 observers_.RemoveObserver(observer);
222 } 275 }
223 276
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
255 } 308 }
256 309
257 // static 310 // static
258 void MessagePumpForUI::EventDispatcher(GdkEvent* event, gpointer data) { 311 void MessagePumpForUI::EventDispatcher(GdkEvent* event, gpointer data) {
259 reinterpret_cast<MessagePumpForUI*>(data)->WillProcessEvent(event); 312 reinterpret_cast<MessagePumpForUI*>(data)->WillProcessEvent(event);
260 gtk_main_do_event(event); 313 gtk_main_do_event(event);
261 reinterpret_cast<MessagePumpForUI*>(data)->DidProcessEvent(event); 314 reinterpret_cast<MessagePumpForUI*>(data)->DidProcessEvent(event);
262 } 315 }
263 316
264 } // namespace base 317 } // namespace base
OLDNEW
« no previous file with comments | « base/message_pump_glib.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698