OLD | NEW |
---|---|
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 |
OLD | NEW |