| Index: base/message_pump_glib.cc
|
| ===================================================================
|
| --- base/message_pump_glib.cc (revision 0)
|
| +++ base/message_pump_glib.cc (revision 0)
|
| @@ -0,0 +1,304 @@
|
| +// Copyright (c) 2008 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "base/message_pump_glib.h"
|
| +
|
| +#include <fcntl.h>
|
| +#include <math.h>
|
| +
|
| +#include "base/lazy_instance.h"
|
| +#include "base/logging.h"
|
| +#include "base/platform_thread.h"
|
| +
|
| +namespace base {
|
| +
|
| +static const char kWorkScheduled = '\0';
|
| +static const char kDelayedWorkScheduled = '\1';
|
| +
|
| +// I wish these could be const, but g_source_new wants a non-const GSourceFunc
|
| +// pointer.
|
| +
|
| +// static
|
| +GSourceFuncs MessagePumpForUI::WorkSourceFuncs = {
|
| + WorkSourcePrepare,
|
| + WorkSourceCheck,
|
| + WorkSourceDispatch,
|
| + NULL
|
| +};
|
| +
|
| +// static
|
| +GSourceFuncs MessagePumpForUI::IdleSourceFuncs = {
|
| + IdleSourcePrepare,
|
| + IdleSourceCheck,
|
| + IdleSourceDispatch,
|
| + NULL
|
| +};
|
| +
|
| +static int GetTimeIntervalMilliseconds(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());
|
| +
|
| + // 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;
|
| +}
|
| +
|
| +MessagePumpForUI::MessagePumpForUI()
|
| + : state_(NULL),
|
| + context_(g_main_context_default()) {
|
| + // Create a pipe with a non-blocking read end for use by ScheduleWork to
|
| + // break us out of a poll. Create the work source and attach the file
|
| + // descriptor to it.
|
| + int pipe_fd[2];
|
| + CHECK(0 == pipe(pipe_fd)) << "Could not create pipe!";
|
| + write_fd_work_scheduled_ = pipe_fd[1];
|
| + read_fd_work_scheduled_ = pipe_fd[0];
|
| + int flags = fcntl(read_fd_work_scheduled_, F_GETFL, 0);
|
| + if (-1 == flags)
|
| + flags = 0;
|
| + CHECK(0 == fcntl(read_fd_work_scheduled_, F_SETFL, flags | O_NONBLOCK)) <<
|
| + "Could not set file descriptor to non-blocking!";
|
| + GPollFD poll_fd;
|
| + poll_fd.fd = read_fd_work_scheduled_;
|
| + poll_fd.events = G_IO_IN | G_IO_HUP | G_IO_ERR;
|
| + work_source_ = AddSource(&WorkSourceFuncs, G_PRIORITY_DEFAULT, &poll_fd);
|
| +}
|
| +
|
| +MessagePumpForUI::~MessagePumpForUI() {
|
| + close(read_fd_work_scheduled_);
|
| + close(write_fd_work_scheduled_);
|
| + g_source_destroy(work_source_);
|
| + 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) {
|
| + // 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()) <<
|
| + "Running MessagePumpForUI on two different threads; "
|
| + "this is unsupported by GLib!";
|
| +
|
| + 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;
|
| +
|
| + 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);
|
| + }
|
| +
|
| + state_ = previous_state;
|
| +}
|
| +
|
| +void MessagePumpForUI::Quit() {
|
| + DCHECK(state_) << "Quit called outside Run!";
|
| + state_->keep_running = false;
|
| +}
|
| +
|
| +void MessagePumpForUI::ScheduleWork() {
|
| + // This can be called on any thread, so we don't want to touch any state
|
| + // 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!";
|
| +}
|
| +
|
| +void MessagePumpForUI::ScheduleDelayedWork(const Time& delayed_work_time) {
|
| + 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;
|
| + // 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;
|
| +}
|
| +
|
| +} // namespace base
|
|
|