Chromium Code Reviews| Index: base/message_pump_glib.cc |
| diff --git a/base/message_pump_glib.cc b/base/message_pump_glib.cc |
| index f837f597c12569f5104b7b921eaf7b4d7915cf9b..e4efd8b715531362157377e720f8a6853fd9dc4f 100644 |
| --- a/base/message_pump_glib.cc |
| +++ b/base/message_pump_glib.cc |
| @@ -11,47 +11,79 @@ |
| #include "base/logging.h" |
| #include "base/platform_thread.h" |
| -namespace base { |
| - |
| -static const char kWorkScheduled = '\0'; |
| -static const char kDelayedWorkScheduled = '\1'; |
| +namespace { |
| -// I wish these could be const, but g_source_new wants a non-const GSourceFunc |
| -// pointer. |
| - |
| -// static |
| -GSourceFuncs MessagePumpForUI::WorkSourceFuncs = { |
| - WorkSourcePrepare, |
| - WorkSourceCheck, |
| - WorkSourceDispatch, |
| - NULL |
| -}; |
| +// We send a byte across a pipe to wakeup the event loop. |
| +const char kWorkScheduled = '\0'; |
| -// static |
| -GSourceFuncs MessagePumpForUI::IdleSourceFuncs = { |
| - IdleSourcePrepare, |
| - IdleSourceCheck, |
| - IdleSourceDispatch, |
| - NULL |
| -}; |
| - |
| -static int GetTimeIntervalMilliseconds(Time from) { |
| +// Return a timeout suitable for the glib loop, -1 to block forever, |
| +// 0 to return right away, or a timeout in milliseconds from now. |
| +int GetTimeIntervalMilliseconds(base::Time from) { |
| if (from.is_null()) |
| return -1; |
| // Be careful here. TimeDelta has a precision of microseconds, but we want a |
| // value in milliseconds. If there are 5.5ms left, should the delay be 5 or |
| // 6? It should be 6 to avoid executing delayed work too early. |
| - double timeout = ceil((from - Time::Now()).InMillisecondsF()); |
| + int delay = static_cast<int>( |
| + ceil((from - base::Time::Now()).InMillisecondsF())); |
| // If this value is negative, then we need to run delayed work soon. |
| - int delay = static_cast<int>(timeout); |
| - if (delay < 0) |
| - delay = 0; |
| + return delay < 0 ? 0 : delay; |
| +} |
| - return delay; |
| +// A brief refresher on GLib: |
| +// GLib sources have four callbacks: Prepare, Check, Dispatch and Finalize. |
| +// On each iteration of the GLib pump, it calls each source's Prepare function. |
| +// This function should return TRUE if it wants GLib to call its Dispatch, and |
| +// FALSE otherwise. It can also set a timeout in this case for the next time |
| +// Prepare should be called again (it may be called sooner). |
| +// After the Prepare calls, GLib does a poll to check for events from the |
| +// system. File descriptors can be attached to the sources. The poll may block |
| +// if none of the Prepare calls returned TRUE. It will block indefinitely, or |
| +// by the minimum time returned by a source in Prepare. |
| +// After the poll, GLib calls Check for each source that returned FALSE |
| +// from Prepare. The return value of Check has the same meaning as for Prepare, |
| +// making Check a second chance to tell GLib we are ready for Dispatch. |
| +// Finally, GLib calls Dispatch for each source that is ready. If Dispatch |
| +// returns FALSE, GLib will destroy the source. Dispatch calls may be recursive |
| +// (i.e., you can call Run from them), but Prepare and Check cannot. |
| +// Finalize is called when the source is destroyed. |
| + |
| +struct WorkSource : public GSource { |
| + int timeout_ms; |
| +}; |
| + |
| +gboolean WorkSourcePrepare(GSource* source, |
| + gint* timeout_ms) { |
| + *timeout_ms = static_cast<WorkSource*>(source)->timeout_ms; |
| + return FALSE; |
| +} |
| + |
| +gboolean WorkSourceCheck(GSource* source) { |
| + return FALSE; |
| } |
| +gboolean WorkSourceDispatch(GSource* source, |
| + GSourceFunc unused_func, |
| + gpointer unused_data) { |
| + NOTREACHED(); |
| + return TRUE; |
| +} |
| + |
| +// I wish these could be const, but g_source_new wants non-const. |
| +GSourceFuncs WorkSourceFuncs = { |
| + WorkSourcePrepare, |
| + WorkSourceCheck, |
| + WorkSourceDispatch, |
| + NULL |
| +}; |
| + |
| +} // namespace |
| + |
| + |
| +namespace base { |
| + |
| MessagePumpForUI::MessagePumpForUI() |
| : state_(NULL), |
| context_(g_main_context_default()), |
| @@ -70,8 +102,12 @@ MessagePumpForUI::MessagePumpForUI() |
| "Could not set file descriptor to non-blocking!"; |
| work_source_poll_fd_->fd = read_fd_work_scheduled_; |
| work_source_poll_fd_->events = G_IO_IN | G_IO_HUP | G_IO_ERR; |
| - work_source_ = AddSource(&WorkSourceFuncs, G_PRIORITY_DEFAULT, |
| - work_source_poll_fd_.get()); |
| + |
| + work_source_ = g_source_new(&WorkSourceFuncs, sizeof(WorkSource)); |
| + // This is needed to allow Run calls inside Dispatch. |
| + g_source_set_can_recurse(work_source_, TRUE); |
| + g_source_add_poll(work_source_, work_source_poll_fd_.get()); |
| + g_source_attach(work_source_, context_); |
| } |
| MessagePumpForUI::~MessagePumpForUI() { |
| @@ -81,50 +117,77 @@ MessagePumpForUI::~MessagePumpForUI() { |
| g_source_unref(work_source_); |
| } |
| -struct ThreadIdTraits { |
| - static void New(void* instance) { |
| - int* thread_id = static_cast<int*>(instance); |
| - *thread_id = PlatformThread::CurrentId(); |
| - } |
| - static void Delete(void* instance) { |
| - } |
| -}; |
| - |
| void MessagePumpForUI::Run(Delegate* delegate) { |
| +#ifndef NDEBUG |
| // Make sure we only run this on one thread. GTK only has one message pump |
| // so we can only have one UI loop per process. |
| - static LazyInstance<int, ThreadIdTraits> thread_id(base::LINKER_INITIALIZED); |
| - DCHECK(thread_id.Get() == PlatformThread::CurrentId()) << |
| + static int thread_id = PlatformThread::CurrentId(); |
| + DCHECK(thread_id == PlatformThread::CurrentId()) << |
| "Running MessagePumpForUI on two different threads; " |
| "this is unsupported by GLib!"; |
| +#endif |
| RunState state; |
| state.delegate = delegate; |
| - state.keep_running = true; |
| - // We emulate the behavior of MessagePumpDefault and try to do work at once. |
| - state.should_do_work = true; |
| - state.should_do_idle_work = false; |
| - state.idle_source = NULL; |
| + state.should_quit = false; |
| + state.run_depth = state_ ? state_->run_depth + 1 : 1; |
| RunState* previous_state = state_; |
| state_ = &state; |
| - while (state.keep_running) |
| - g_main_context_iteration(context_, true); |
| - |
| - if (state.idle_source) { |
| - // This removes the source from the context and releases GLib's hold on it. |
| - g_source_destroy(state.idle_source); |
| - // This releases our hold and destroys the source. |
| - g_source_unref(state.idle_source); |
| + // We really only do a single task for each iteration of the loop. If we |
| + // have done something, assume there is likely something more to do. This |
| + // will mean that we don't block on the message pump until there was nothing |
| + // more to do. We also set this to true to make sure not to block on the |
| + // first iteration of the loop, so RunAllPending() works correctly. |
| + bool more_work_is_plausible = true; |
| + for (;;) { |
| + // Set up our timeout for any delayed work. |
| + static_cast<WorkSource*>(work_source_)->timeout_ms = |
| + GetTimeIntervalMilliseconds(delayed_work_time_); |
| + |
| + // Process a single iteration of the event loop. |
| + g_main_context_iteration(context_, !more_work_is_plausible); |
| + if (state_->should_quit) |
| + break; |
| + |
| + more_work_is_plausible = false; |
| + |
| + // 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
|
| + char tempbuf[16]; |
| + while (read(read_fd_work_scheduled_, tempbuf, sizeof(tempbuf)) > 0) { } |
| + |
| + if (state_->delegate->DoWork()) |
|
dsh
2008/11/12 19:24:42
This is much simpler, but the problem is like you
|
| + more_work_is_plausible = true; |
| + |
| + if (state_->should_quit) |
| + break; |
| + |
| + if (state_->delegate->DoDelayedWork(&delayed_work_time_)) |
| + more_work_is_plausible = true; |
| + if (state_->should_quit) |
| + break; |
| + |
| + // Don't do idle work if we think there are more important things |
| + // that we could be doing. |
| + if (more_work_is_plausible) |
| + continue; |
| + |
| + if (state_->delegate->DoIdleWork()) |
| + more_work_is_plausible = true; |
| + if (state_->should_quit) |
| + break; |
| } |
| state_ = previous_state; |
| } |
| void MessagePumpForUI::Quit() { |
| - DCHECK(state_) << "Quit called outside Run!"; |
| - state_->keep_running = false; |
| + if (state_) { |
| + state_->should_quit = true; |
| + } else { |
| + NOTREACHED() << "Quit called outside Run!"; |
| + } |
| } |
| void MessagePumpForUI::ScheduleWork() { |
| @@ -132,174 +195,16 @@ void MessagePumpForUI::ScheduleWork() { |
| // variables as we would then need locks all over. This ensures that if |
| // we are sleeping in a poll that we will wake up, and we check the pipe |
| // so we know when work was scheduled. |
| - CHECK(1 == write(write_fd_work_scheduled_, &kWorkScheduled, 1)) << |
| - "Could not write to pipe!"; |
| + if (write(write_fd_work_scheduled_, &kWorkScheduled, 1) != 1) { |
| + NOTREACHED() << "Could not write to the UI message loop wakeup pipe!"; |
| + } |
| } |
| void MessagePumpForUI::ScheduleDelayedWork(const Time& delayed_work_time) { |
| + // We need to wake up the loop in case the poll timeout needs to be |
| + // adjusted. This will cause us to try to do work, but that's ok. |
| delayed_work_time_ = delayed_work_time; |
| - // This is an optimization. Delayed work may not be imminent, we may just |
| - // need to update our timeout to poll. Hence we don't want to go overkill |
| - // with kWorkScheduled. |
| - CHECK(1 == write(write_fd_work_scheduled_, &kDelayedWorkScheduled, 1)) << |
| - "Could not write to pipe!"; |
| -} |
| - |
| -// A brief refresher on GLib: |
| -// GLib sources have four callbacks: Prepare, Check, Dispatch and Finalize. |
| -// On each iteration of the GLib pump, it calls each source's Prepare function. |
| -// This function should return TRUE if it wants GLib to call its Dispatch, and |
| -// FALSE otherwise. It can also set a timeout in this case for the next time |
| -// Prepare should be called again (it may be called sooner). |
| -// After the Prepare calls, GLib does a poll to check for events from the |
| -// system. File descriptors can be attached to the sources. The poll may block |
| -// if none of the Prepare calls returned TRUE. It will block indefinitely, or |
| -// by the minimum time returned by a source in Prepare. |
| -// After the poll, GLib calls Check for each source that returned FALSE |
| -// from Prepare. The return value of Check has the same meaning as for Prepare, |
| -// making Check a second chance to tell GLib we are ready for Dispatch. |
| -// Finally, GLib calls Dispatch for each source that is ready. If Dispatch |
| -// returns FALSE, GLib will destroy the source. Dispatch calls may be recursive |
| -// (i.e., you can call Run from them), but Prepare and Check cannot. |
| -// Finalize is called when the source is destroyed. |
| - |
| -// static |
| -gboolean MessagePumpForUI::WorkSourcePrepare(GSource* source, |
| - gint* timeout_ms) { |
| - MessagePumpForUI* self = static_cast<WorkSource*>(source)->self; |
| - RunState* state = self->state_; |
| - |
| - if (state->should_do_work) { |
| - state->should_do_idle_work = false; |
| - *timeout_ms = 0; |
| - return TRUE; |
| - } |
| - |
| - *timeout_ms = GetTimeIntervalMilliseconds(self->delayed_work_time_); |
| - |
| - state->should_do_idle_work = true; |
|
dsh
2008/11/12 19:24:42
It's not really necessary to redesign the whole th
|
| - // We want to do idle work right before poll goes to sleep. Obviously |
| - // we are not currently asleep, but we may be about to since we have |
| - // no work to do. If we don't have an idle source ready to go it's |
| - // probably because it fired already (or we just started and it hasn't |
| - // been added yet) and we should add one for when we are ready. Note |
| - // that this new source will get Prepare called on this current pump |
| - // iteration since it gets added at the end of the source list. |
| - if (!state->idle_source) { |
| - state->idle_source = |
| - self->AddSource(&IdleSourceFuncs, G_PRIORITY_DEFAULT_IDLE, NULL); |
| - } |
| - |
| - return FALSE; |
| -} |
| - |
| -// static |
| -gboolean MessagePumpForUI::WorkSourceCheck(GSource* source) { |
| - MessagePumpForUI* self = static_cast<WorkSource*>(source)->self; |
| - RunState* state = self->state_; |
| - |
| - // Make sure we don't attempt idle work until we are really sure we don't |
| - // have other work to do. We'll know this in the call to Prepare. |
| - state->should_do_idle_work = false; |
| - |
| - // Check if ScheduleWork or ScheduleDelayedWork was called. This is a |
| - // non-blocking read. |
| - char byte; |
| - while (0 < read(self->read_fd_work_scheduled_, &byte, 1)) { |
| - // Don't assume we actually have work yet unless the stronger ScheduleWork |
| - // was called. |
| - if (byte == kWorkScheduled) |
| - state->should_do_work = true; |
| - } |
| - |
| - if (state->should_do_work) |
| - return TRUE; |
| - |
| - if (!self->delayed_work_time_.is_null()) |
| - return self->delayed_work_time_ <= Time::Now(); |
| - |
| - return FALSE; |
| -} |
| - |
| -// static |
| -gboolean MessagePumpForUI::WorkSourceDispatch(GSource* source, |
| - GSourceFunc unused_func, |
| - gpointer unused_data) { |
| - MessagePumpForUI* self = static_cast<WorkSource*>(source)->self; |
| - RunState* state = self->state_; |
| - DCHECK(!state->should_do_idle_work) << |
| - "Idle work should not be flagged while regular work exists."; |
| - |
| - // Note that in this function we never return FALSE. This source is owned |
| - // by GLib and shared by multiple calls to Run. It will only finally get |
| - // destroyed when the loop is destroyed. |
| - |
| - state->should_do_work = state->delegate->DoWork(); |
| - if (!state->keep_running) |
| - return TRUE; |
| - |
| - state->should_do_work |= |
| - state->delegate->DoDelayedWork(&self->delayed_work_time_); |
| - |
| - return TRUE; |
| -} |
| - |
| -// static |
| -gboolean MessagePumpForUI::IdleSourcePrepare(GSource* source, |
| - gint* timeout_ms) { |
| - RunState* state = static_cast<WorkSource*>(source)->self->state_; |
| - *timeout_ms = 0; |
| - return state->should_do_idle_work; |
| -} |
| - |
| -// static |
| -gboolean MessagePumpForUI::IdleSourceCheck(GSource* source) { |
| - RunState* state = static_cast<WorkSource*>(source)->self->state_; |
| - return state->should_do_idle_work; |
| -} |
| - |
| -// static |
| -gboolean MessagePumpForUI::IdleSourceDispatch(GSource* source, |
| - GSourceFunc unused_func, |
| - gpointer unused_data) { |
| - RunState* state = static_cast<WorkSource*>(source)->self->state_; |
| - // We should not do idle work unless we didn't have other work to do. |
| - DCHECK(!state->should_do_work) << "Doing idle work in non-idle time!"; |
| - state->should_do_idle_work = false; |
| - state->should_do_work = state->delegate->DoIdleWork(); |
| - |
| - // This is an optimization. We could always remove ourselves right now, |
| - // but we will just get re-added when WorkSourceCheck eventually returns |
| - // FALSE. |
| - if (!state->should_do_work) { |
| - // This is so that when we return FALSE, GLib will not only remove us |
| - // from the context, but since it holds the last reference, it will |
| - // destroy us as well. |
| - g_source_unref(source); |
| - state->idle_source = NULL; |
| - } |
| - |
| - return state->should_do_work; |
| -} |
| - |
| -GSource* MessagePumpForUI::AddSource(GSourceFuncs* funcs, gint priority, |
| - GPollFD *optional_poll_fd) { |
| - GSource* source = g_source_new(funcs, sizeof(WorkSource)); |
| - |
| - // Setting the priority is actually a bit expensive since it causes GLib |
| - // to resort an internal list. |
| - if (priority != G_PRIORITY_DEFAULT) |
| - g_source_set_priority(source, priority); |
| - |
| - // This is needed to allow Run calls inside Dispatch. |
| - g_source_set_can_recurse(source, TRUE); |
| - static_cast<WorkSource*>(source)->self = this; |
| - |
| - if (optional_poll_fd) |
| - g_source_add_poll(source, optional_poll_fd); |
| - |
| - g_source_attach(source, context_); |
| - return source; |
| + ScheduleWork(); |
| } |
| } // namespace base |