Chromium Code Reviews| 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 |