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

Side by Side Diff: base/message_pump_glib.cc

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

Powered by Google App Engine
This is Rietveld 408576698