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

Side by Side Diff: base/message_pump_glib.cc

Issue 10833: Rewrite the glib UI pump. (Closed)
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
« 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/lazy_instance.h" 10 #include "base/lazy_instance.h"
11 #include "base/logging.h" 11 #include "base/logging.h"
12 #include "base/platform_thread.h" 12 #include "base/platform_thread.h"
13 13
14 namespace base { 14 namespace {
15 15
16 static const char kWorkScheduled = '\0'; 16 // We send a byte across a pipe to wakeup the event loop.
17 static const char kDelayedWorkScheduled = '\1'; 17 const char kWorkScheduled = '\0';
18 18
19 // I wish these could be const, but g_source_new wants a non-const GSourceFunc 19 // Return a timeout suitable for the glib loop, -1 to block forever,
20 // pointer. 20 // 0 to return right away, or a timeout in milliseconds from now.
21 int GetTimeIntervalMilliseconds(base::Time from) {
22 if (from.is_null())
23 return -1;
21 24
22 // static 25 // Be careful here. TimeDelta has a precision of microseconds, but we want a
23 GSourceFuncs MessagePumpForUI::WorkSourceFuncs = { 26 // value in milliseconds. If there are 5.5ms left, should the delay be 5 or
27 // 6? It should be 6 to avoid executing delayed work too early.
28 int delay = static_cast<int>(
29 ceil((from - base::Time::Now()).InMillisecondsF()));
30
31 // If this value is negative, then we need to run delayed work soon.
32 return delay < 0 ? 0 : delay;
33 }
34
35 // A brief refresher on GLib:
36 // GLib sources have four callbacks: Prepare, Check, Dispatch and Finalize.
37 // On each iteration of the GLib pump, it calls each source's Prepare function.
38 // This function should return TRUE if it wants GLib to call its Dispatch, and
39 // FALSE otherwise. It can also set a timeout in this case for the next time
40 // Prepare should be called again (it may be called sooner).
41 // After the Prepare calls, GLib does a poll to check for events from the
42 // system. File descriptors can be attached to the sources. The poll may block
43 // if none of the Prepare calls returned TRUE. It will block indefinitely, or
44 // by the minimum time returned by a source in Prepare.
45 // After the poll, GLib calls Check for each source that returned FALSE
46 // from Prepare. The return value of Check has the same meaning as for Prepare,
47 // making Check a second chance to tell GLib we are ready for Dispatch.
48 // Finally, GLib calls Dispatch for each source that is ready. If Dispatch
49 // returns FALSE, GLib will destroy the source. Dispatch calls may be recursive
50 // (i.e., you can call Run from them), but Prepare and Check cannot.
51 // Finalize is called when the source is destroyed.
52
53 struct WorkSource : public GSource {
54 int timeout_ms;
55 };
56
57 gboolean WorkSourcePrepare(GSource* source,
58 gint* timeout_ms) {
59 *timeout_ms = static_cast<WorkSource*>(source)->timeout_ms;
60 return FALSE;
61 }
62
63 gboolean WorkSourceCheck(GSource* source) {
64 return FALSE;
65 }
66
67 gboolean WorkSourceDispatch(GSource* source,
68 GSourceFunc unused_func,
69 gpointer unused_data) {
70 NOTREACHED();
71 return TRUE;
72 }
73
74 // I wish these could be const, but g_source_new wants non-const.
75 GSourceFuncs WorkSourceFuncs = {
24 WorkSourcePrepare, 76 WorkSourcePrepare,
25 WorkSourceCheck, 77 WorkSourceCheck,
26 WorkSourceDispatch, 78 WorkSourceDispatch,
27 NULL 79 NULL
28 }; 80 };
29 81
30 // static 82 } // namespace
31 GSourceFuncs MessagePumpForUI::IdleSourceFuncs = {
32 IdleSourcePrepare,
33 IdleSourceCheck,
34 IdleSourceDispatch,
35 NULL
36 };
37 83
38 static int GetTimeIntervalMilliseconds(Time from) {
39 if (from.is_null())
40 return -1;
41 84
42 // Be careful here. TimeDelta has a precision of microseconds, but we want a 85 namespace base {
43 // value in milliseconds. If there are 5.5ms left, should the delay be 5 or
44 // 6? It should be 6 to avoid executing delayed work too early.
45 double timeout = ceil((from - Time::Now()).InMillisecondsF());
46
47 // If this value is negative, then we need to run delayed work soon.
48 int delay = static_cast<int>(timeout);
49 if (delay < 0)
50 delay = 0;
51
52 return delay;
53 }
54 86
55 MessagePumpForUI::MessagePumpForUI() 87 MessagePumpForUI::MessagePumpForUI()
56 : state_(NULL), 88 : state_(NULL),
57 context_(g_main_context_default()), 89 context_(g_main_context_default()),
58 work_source_poll_fd_(new GPollFD) { 90 work_source_poll_fd_(new GPollFD) {
59 // Create a pipe with a non-blocking read end for use by ScheduleWork to 91 // Create a pipe with a non-blocking read end for use by ScheduleWork to
60 // break us out of a poll. Create the work source and attach the file 92 // break us out of a poll. Create the work source and attach the file
61 // descriptor to it. 93 // descriptor to it.
62 int pipe_fd[2]; 94 int pipe_fd[2];
63 CHECK(0 == pipe(pipe_fd)) << "Could not create pipe!"; 95 CHECK(0 == pipe(pipe_fd)) << "Could not create pipe!";
64 write_fd_work_scheduled_ = pipe_fd[1]; 96 write_fd_work_scheduled_ = pipe_fd[1];
65 read_fd_work_scheduled_ = pipe_fd[0]; 97 read_fd_work_scheduled_ = pipe_fd[0];
66 int flags = fcntl(read_fd_work_scheduled_, F_GETFL, 0); 98 int flags = fcntl(read_fd_work_scheduled_, F_GETFL, 0);
67 if (-1 == flags) 99 if (-1 == flags)
68 flags = 0; 100 flags = 0;
69 CHECK(0 == fcntl(read_fd_work_scheduled_, F_SETFL, flags | O_NONBLOCK)) << 101 CHECK(0 == fcntl(read_fd_work_scheduled_, F_SETFL, flags | O_NONBLOCK)) <<
70 "Could not set file descriptor to non-blocking!"; 102 "Could not set file descriptor to non-blocking!";
71 work_source_poll_fd_->fd = read_fd_work_scheduled_; 103 work_source_poll_fd_->fd = read_fd_work_scheduled_;
72 work_source_poll_fd_->events = G_IO_IN | G_IO_HUP | G_IO_ERR; 104 work_source_poll_fd_->events = G_IO_IN | G_IO_HUP | G_IO_ERR;
73 work_source_ = AddSource(&WorkSourceFuncs, G_PRIORITY_DEFAULT, 105
74 work_source_poll_fd_.get()); 106 work_source_ = g_source_new(&WorkSourceFuncs, sizeof(WorkSource));
107 // This is needed to allow Run calls inside Dispatch.
108 g_source_set_can_recurse(work_source_, TRUE);
109 g_source_add_poll(work_source_, work_source_poll_fd_.get());
110 g_source_attach(work_source_, context_);
75 } 111 }
76 112
77 MessagePumpForUI::~MessagePumpForUI() { 113 MessagePumpForUI::~MessagePumpForUI() {
78 close(read_fd_work_scheduled_); 114 close(read_fd_work_scheduled_);
79 close(write_fd_work_scheduled_); 115 close(write_fd_work_scheduled_);
80 g_source_destroy(work_source_); 116 g_source_destroy(work_source_);
81 g_source_unref(work_source_); 117 g_source_unref(work_source_);
82 } 118 }
83 119
84 struct ThreadIdTraits {
85 static void New(void* instance) {
86 int* thread_id = static_cast<int*>(instance);
87 *thread_id = PlatformThread::CurrentId();
88 }
89 static void Delete(void* instance) {
90 }
91 };
92
93 void MessagePumpForUI::Run(Delegate* delegate) { 120 void MessagePumpForUI::Run(Delegate* delegate) {
121 #ifndef NDEBUG
94 // Make sure we only run this on one thread. GTK only has one message pump 122 // Make sure we only run this on one thread. GTK only has one message pump
95 // so we can only have one UI loop per process. 123 // so we can only have one UI loop per process.
96 static LazyInstance<int, ThreadIdTraits> thread_id(base::LINKER_INITIALIZED); 124 static int thread_id = PlatformThread::CurrentId();
97 DCHECK(thread_id.Get() == PlatformThread::CurrentId()) << 125 DCHECK(thread_id == PlatformThread::CurrentId()) <<
98 "Running MessagePumpForUI on two different threads; " 126 "Running MessagePumpForUI on two different threads; "
99 "this is unsupported by GLib!"; 127 "this is unsupported by GLib!";
128 #endif
100 129
101 RunState state; 130 RunState state;
102 state.delegate = delegate; 131 state.delegate = delegate;
103 state.keep_running = true; 132 state.should_quit = false;
104 // We emulate the behavior of MessagePumpDefault and try to do work at once. 133 state.run_depth = state_ ? state_->run_depth + 1 : 1;
105 state.should_do_work = true;
106 state.should_do_idle_work = false;
107 state.idle_source = NULL;
108 134
109 RunState* previous_state = state_; 135 RunState* previous_state = state_;
110 state_ = &state; 136 state_ = &state;
111 137
112 while (state.keep_running) 138 // We really only do a single task for each iteration of the loop. If we
113 g_main_context_iteration(context_, true); 139 // have done something, assume there is likely something more to do. This
140 // will mean that we don't block on the message pump until there was nothing
141 // more to do. We also set this to true to make sure not to block on the
142 // first iteration of the loop, so RunAllPending() works correctly.
143 bool more_work_is_plausible = true;
144 for (;;) {
145 // Set up our timeout for any delayed work.
146 static_cast<WorkSource*>(work_source_)->timeout_ms =
147 GetTimeIntervalMilliseconds(delayed_work_time_);
114 148
115 if (state.idle_source) { 149 // Process a single iteration of the event loop.
116 // This removes the source from the context and releases GLib's hold on it. 150 g_main_context_iteration(context_, !more_work_is_plausible);
117 g_source_destroy(state.idle_source); 151 if (state_->should_quit)
118 // This releases our hold and destroys the source. 152 break;
119 g_source_unref(state.idle_source); 153
154 more_work_is_plausible = false;
155
156 // Drain our wakeup pipe, this is a non-blocking read.
darin (slow to review) 2008/11/12 18:12:02 Hmm... have you ever seen this have more than 1 by
157 char tempbuf[16];
158 while (read(read_fd_work_scheduled_, tempbuf, sizeof(tempbuf)) > 0) { }
159
160 if (state_->delegate->DoWork())
dsh 2008/11/12 19:24:42 This is much simpler, but the problem is like you
161 more_work_is_plausible = true;
162
163 if (state_->should_quit)
164 break;
165
166 if (state_->delegate->DoDelayedWork(&delayed_work_time_))
167 more_work_is_plausible = true;
168 if (state_->should_quit)
169 break;
170
171 // Don't do idle work if we think there are more important things
172 // that we could be doing.
173 if (more_work_is_plausible)
174 continue;
175
176 if (state_->delegate->DoIdleWork())
177 more_work_is_plausible = true;
178 if (state_->should_quit)
179 break;
120 } 180 }
121 181
122 state_ = previous_state; 182 state_ = previous_state;
123 } 183 }
124 184
125 void MessagePumpForUI::Quit() { 185 void MessagePumpForUI::Quit() {
126 DCHECK(state_) << "Quit called outside Run!"; 186 if (state_) {
127 state_->keep_running = false; 187 state_->should_quit = true;
188 } else {
189 NOTREACHED() << "Quit called outside Run!";
190 }
128 } 191 }
129 192
130 void MessagePumpForUI::ScheduleWork() { 193 void MessagePumpForUI::ScheduleWork() {
131 // This can be called on any thread, so we don't want to touch any state 194 // This can be called on any thread, so we don't want to touch any state
132 // variables as we would then need locks all over. This ensures that if 195 // variables as we would then need locks all over. This ensures that if
133 // we are sleeping in a poll that we will wake up, and we check the pipe 196 // we are sleeping in a poll that we will wake up, and we check the pipe
134 // so we know when work was scheduled. 197 // so we know when work was scheduled.
135 CHECK(1 == write(write_fd_work_scheduled_, &kWorkScheduled, 1)) << 198 if (write(write_fd_work_scheduled_, &kWorkScheduled, 1) != 1) {
136 "Could not write to pipe!"; 199 NOTREACHED() << "Could not write to the UI message loop wakeup pipe!";
200 }
137 } 201 }
138 202
139 void MessagePumpForUI::ScheduleDelayedWork(const Time& delayed_work_time) { 203 void MessagePumpForUI::ScheduleDelayedWork(const Time& delayed_work_time) {
204 // We need to wake up the loop in case the poll timeout needs to be
205 // adjusted. This will cause us to try to do work, but that's ok.
140 delayed_work_time_ = delayed_work_time; 206 delayed_work_time_ = delayed_work_time;
141 // This is an optimization. Delayed work may not be imminent, we may just 207 ScheduleWork();
142 // need to update our timeout to poll. Hence we don't want to go overkill
143 // with kWorkScheduled.
144 CHECK(1 == write(write_fd_work_scheduled_, &kDelayedWorkScheduled, 1)) <<
145 "Could not write to pipe!";
146 }
147
148 // A brief refresher on GLib:
149 // GLib sources have four callbacks: Prepare, Check, Dispatch and Finalize.
150 // On each iteration of the GLib pump, it calls each source's Prepare function.
151 // This function should return TRUE if it wants GLib to call its Dispatch, and
152 // FALSE otherwise. It can also set a timeout in this case for the next time
153 // Prepare should be called again (it may be called sooner).
154 // After the Prepare calls, GLib does a poll to check for events from the
155 // system. File descriptors can be attached to the sources. The poll may block
156 // if none of the Prepare calls returned TRUE. It will block indefinitely, or
157 // by the minimum time returned by a source in Prepare.
158 // After the poll, GLib calls Check for each source that returned FALSE
159 // from Prepare. The return value of Check has the same meaning as for Prepare,
160 // making Check a second chance to tell GLib we are ready for Dispatch.
161 // Finally, GLib calls Dispatch for each source that is ready. If Dispatch
162 // returns FALSE, GLib will destroy the source. Dispatch calls may be recursive
163 // (i.e., you can call Run from them), but Prepare and Check cannot.
164 // Finalize is called when the source is destroyed.
165
166 // static
167 gboolean MessagePumpForUI::WorkSourcePrepare(GSource* source,
168 gint* timeout_ms) {
169 MessagePumpForUI* self = static_cast<WorkSource*>(source)->self;
170 RunState* state = self->state_;
171
172 if (state->should_do_work) {
173 state->should_do_idle_work = false;
174 *timeout_ms = 0;
175 return TRUE;
176 }
177
178 *timeout_ms = GetTimeIntervalMilliseconds(self->delayed_work_time_);
179
180 state->should_do_idle_work = true;
dsh 2008/11/12 19:24:42 It's not really necessary to redesign the whole th
181 // We want to do idle work right before poll goes to sleep. Obviously
182 // we are not currently asleep, but we may be about to since we have
183 // no work to do. If we don't have an idle source ready to go it's
184 // probably because it fired already (or we just started and it hasn't
185 // been added yet) and we should add one for when we are ready. Note
186 // that this new source will get Prepare called on this current pump
187 // iteration since it gets added at the end of the source list.
188 if (!state->idle_source) {
189 state->idle_source =
190 self->AddSource(&IdleSourceFuncs, G_PRIORITY_DEFAULT_IDLE, NULL);
191 }
192
193 return FALSE;
194 }
195
196 // static
197 gboolean MessagePumpForUI::WorkSourceCheck(GSource* source) {
198 MessagePumpForUI* self = static_cast<WorkSource*>(source)->self;
199 RunState* state = self->state_;
200
201 // Make sure we don't attempt idle work until we are really sure we don't
202 // have other work to do. We'll know this in the call to Prepare.
203 state->should_do_idle_work = false;
204
205 // Check if ScheduleWork or ScheduleDelayedWork was called. This is a
206 // non-blocking read.
207 char byte;
208 while (0 < read(self->read_fd_work_scheduled_, &byte, 1)) {
209 // Don't assume we actually have work yet unless the stronger ScheduleWork
210 // was called.
211 if (byte == kWorkScheduled)
212 state->should_do_work = true;
213 }
214
215 if (state->should_do_work)
216 return TRUE;
217
218 if (!self->delayed_work_time_.is_null())
219 return self->delayed_work_time_ <= Time::Now();
220
221 return FALSE;
222 }
223
224 // static
225 gboolean MessagePumpForUI::WorkSourceDispatch(GSource* source,
226 GSourceFunc unused_func,
227 gpointer unused_data) {
228 MessagePumpForUI* self = static_cast<WorkSource*>(source)->self;
229 RunState* state = self->state_;
230 DCHECK(!state->should_do_idle_work) <<
231 "Idle work should not be flagged while regular work exists.";
232
233 // Note that in this function we never return FALSE. This source is owned
234 // by GLib and shared by multiple calls to Run. It will only finally get
235 // destroyed when the loop is destroyed.
236
237 state->should_do_work = state->delegate->DoWork();
238 if (!state->keep_running)
239 return TRUE;
240
241 state->should_do_work |=
242 state->delegate->DoDelayedWork(&self->delayed_work_time_);
243
244 return TRUE;
245 }
246
247 // static
248 gboolean MessagePumpForUI::IdleSourcePrepare(GSource* source,
249 gint* timeout_ms) {
250 RunState* state = static_cast<WorkSource*>(source)->self->state_;
251 *timeout_ms = 0;
252 return state->should_do_idle_work;
253 }
254
255 // static
256 gboolean MessagePumpForUI::IdleSourceCheck(GSource* source) {
257 RunState* state = static_cast<WorkSource*>(source)->self->state_;
258 return state->should_do_idle_work;
259 }
260
261 // static
262 gboolean MessagePumpForUI::IdleSourceDispatch(GSource* source,
263 GSourceFunc unused_func,
264 gpointer unused_data) {
265 RunState* state = static_cast<WorkSource*>(source)->self->state_;
266 // We should not do idle work unless we didn't have other work to do.
267 DCHECK(!state->should_do_work) << "Doing idle work in non-idle time!";
268 state->should_do_idle_work = false;
269 state->should_do_work = state->delegate->DoIdleWork();
270
271 // This is an optimization. We could always remove ourselves right now,
272 // but we will just get re-added when WorkSourceCheck eventually returns
273 // FALSE.
274 if (!state->should_do_work) {
275 // This is so that when we return FALSE, GLib will not only remove us
276 // from the context, but since it holds the last reference, it will
277 // destroy us as well.
278 g_source_unref(source);
279 state->idle_source = NULL;
280 }
281
282 return state->should_do_work;
283 }
284
285 GSource* MessagePumpForUI::AddSource(GSourceFuncs* funcs, gint priority,
286 GPollFD *optional_poll_fd) {
287 GSource* source = g_source_new(funcs, sizeof(WorkSource));
288
289 // Setting the priority is actually a bit expensive since it causes GLib
290 // to resort an internal list.
291 if (priority != G_PRIORITY_DEFAULT)
292 g_source_set_priority(source, priority);
293
294 // This is needed to allow Run calls inside Dispatch.
295 g_source_set_can_recurse(source, TRUE);
296 static_cast<WorkSource*>(source)->self = this;
297
298 if (optional_poll_fd)
299 g_source_add_poll(source, optional_poll_fd);
300
301 g_source_attach(source, context_);
302 return source;
303 } 208 }
304 209
305 } // namespace base 210 } // 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