Bug 544882, bug 545312: Only tell glib that work is pending if there has been a pollin or timer expiration. Merged from chromium latest, r=chromium

This commit is contained in:
Chris Jones 2010-02-10 22:04:56 -06:00
Родитель f0e6e5fa63
Коммит 617be9e842
2 изменённых файлов: 215 добавлений и 56 удалений

Просмотреть файл

@ -7,8 +7,10 @@
#include <fcntl.h>
#include <math.h>
#include <gtk/gtk.h>
#include <glib.h>
#include "base/eintr_wrapper.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/platform_thread.h"
@ -50,6 +52,37 @@ int GetTimeIntervalMilliseconds(base::Time from) {
// 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.
// NOTE: It is common for subsytems to want to process pending events while
// doing intensive work, for example the flash plugin. They usually use the
// following pattern (recommended by the GTK docs):
// while (gtk_events_pending()) {
// gtk_main_iteration();
// }
//
// gtk_events_pending just calls g_main_context_pending, which does the
// following:
// - Call prepare on all the sources.
// - Do the poll with a timeout of 0 (not blocking).
// - Call check on all the sources.
// - *Does not* call dispatch on the sources.
// - Return true if any of prepare() or check() returned true.
//
// gtk_main_iteration just calls g_main_context_iteration, which does the whole
// thing, respecting the timeout for the poll (and block, although it is
// expected not to if gtk_events_pending returned true), and call dispatch.
//
// Thus it is important to only return true from prepare or check if we
// actually have events or work to do. We also need to make sure we keep
// internal state consistent so that if prepare/check return true when called
// from gtk_events_pending, they will still return true when called right
// after, from gtk_main_iteration.
//
// For the GLib pump we try to follow the Windows UI pump model:
// - Whenever we receive a wakeup event or the timer for delayed work expires,
// we run DoWork and/or DoDelayedWork. That part will also run in the other
// event pumps.
// - We also run DoWork, DoDelayedWork, and possibly DoIdleWork in the main
// loop, around event handling.
struct WorkSource : public GSource {
base::MessagePumpForUI* pump;
@ -65,8 +98,8 @@ gboolean WorkSourcePrepare(GSource* source,
}
gboolean WorkSourceCheck(GSource* source) {
// Always return TRUE, and Dispatch will be called.
return TRUE;
// Only return TRUE if Dispatch should be called.
return static_cast<WorkSource*>(source)->pump->HandleCheck();
}
gboolean WorkSourceDispatch(GSource* source,
@ -93,33 +126,38 @@ namespace base {
MessagePumpForUI::MessagePumpForUI()
: state_(NULL),
context_(g_main_context_default()) {
context_(g_main_context_default()),
wakeup_gpollfd_(new GPollFD) {
// Create our wakeup pipe, which is used to flag when work was scheduled.
int fds[2];
CHECK(pipe(fds) == 0);
wakeup_pipe_read_ = fds[0];
wakeup_pipe_write_ = fds[1];
wakeup_gpollfd_.fd = wakeup_pipe_read_;
wakeup_gpollfd_.events = G_IO_IN;
wakeup_gpollfd_->fd = wakeup_pipe_read_;
wakeup_gpollfd_->events = G_IO_IN;
work_source_ = g_source_new(&WorkSourceFuncs, sizeof(WorkSource));
static_cast<WorkSource*>(work_source_)->pump = this;
g_source_add_poll(work_source_, &wakeup_gpollfd_);
g_source_add_poll(work_source_, wakeup_gpollfd_.get());
// Use a low priority so that we let other events in the queue go first.
g_source_set_priority(work_source_, G_PRIORITY_DEFAULT_IDLE);
// This is needed to allow Run calls inside Dispatch.
g_source_set_can_recurse(work_source_, TRUE);
g_source_attach(work_source_, context_);
gdk_event_handler_set(&EventDispatcher, this, NULL);
}
MessagePumpForUI::~MessagePumpForUI() {
gdk_event_handler_set(reinterpret_cast<GdkEventFunc>(gtk_main_do_event),
this, NULL);
g_source_destroy(work_source_);
g_source_unref(work_source_);
close(wakeup_pipe_read_);
close(wakeup_pipe_write_);
}
void MessagePumpForUI::Run(Delegate* delegate) {
void MessagePumpForUI::RunWithDispatcher(Delegate* delegate,
Dispatcher* dispatcher) {
#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.
@ -131,83 +169,126 @@ void MessagePumpForUI::Run(Delegate* delegate) {
RunState state;
state.delegate = delegate;
state.dispatcher = dispatcher;
state.should_quit = false;
state.run_depth = state_ ? state_->run_depth + 1 : 1;
state.has_work = false;
RunState* previous_state = state_;
state_ = &state;
// 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.
state.more_work_is_plausible = true;
RunState* previous_state = state_;
state_ = &state;
bool more_work_is_plausible = true;
// We run our own loop instead of using g_main_loop_quit in one of the
// callbacks. This is so we only quit our own loops, and we don't quit
// nested loops run by others. TODO(deanm): Is this what we want?
while (!state_->should_quit)
g_main_context_iteration(context_, true);
for (;;) {
// Don't block if we think we have more work to do.
bool block = !more_work_is_plausible;
// g_main_context_iteration returns true if events have been dispatched.
more_work_is_plausible = g_main_context_iteration(context_, block);
if (state_->should_quit)
break;
more_work_is_plausible |= state_->delegate->DoWork();
if (state_->should_quit)
break;
more_work_is_plausible |=
state_->delegate->DoDelayedWork(&delayed_work_time_);
if (state_->should_quit)
break;
if (more_work_is_plausible)
continue;
more_work_is_plausible = state_->delegate->DoIdleWork();
if (state_->should_quit)
break;
}
state_ = previous_state;
}
// Return the timeout we want passed to poll.
int MessagePumpForUI::HandlePrepare() {
// If it's likely that we have more work, don't let the pump
// block so that we can do some processing.
if (state_->more_work_is_plausible)
// We know we have work, but we haven't called HandleDispatch yet. Don't let
// the pump block so that we can do some processing.
if (state_ && // state_ may be null during tests.
state_->has_work)
return 0;
// Work wasn't plausible, so we'll block. In the case where glib fires
// our Dispatch(), |more_work_is_plausible| will be reset to whatever it
// should be. However, so we don't get starved by more important work,
// we set |more_work_is_plausible| to true. This means if we come back
// here without having been through Dispatch(), we will get a chance to be
// fired and properly do our work in Dispatch().
state_->more_work_is_plausible = true;
// We don't think we have work to do, but make sure not to block
// longer than the next time we need to run delayed work.
return GetTimeIntervalMilliseconds(delayed_work_time_);
}
void MessagePumpForUI::HandleDispatch() {
bool MessagePumpForUI::HandleCheck() {
if (!state_) // state_ may be null during tests.
return false;
// We should only ever have a single message on the wakeup pipe, since we
// are only signaled when the queue went from empty to non-empty. The glib
// poll will tell us whether there was data, so this read shouldn't block.
if (wakeup_gpollfd_.revents & G_IO_IN) {
if (wakeup_gpollfd_->revents & G_IO_IN) {
char msg;
if (HANDLE_EINTR(read(wakeup_pipe_read_, &msg, 1)) != 1 || msg != '!') {
NOTREACHED() << "Error reading from the wakeup pipe.";
}
// Since we ate the message, we need to record that we have more work,
// because HandleCheck() may be called without HandleDispatch being called
// afterwards.
state_->has_work = true;
}
if (state_->has_work)
return true;
if (GetTimeIntervalMilliseconds(delayed_work_time_) == 0) {
// The timer has expired. That condition will stay true until we process
// that delayed work, so we don't need to record this differently.
return true;
}
return false;
}
void MessagePumpForUI::HandleDispatch() {
state_->has_work = false;
if (state_->delegate->DoWork()) {
// NOTE: on Windows at this point we would call ScheduleWork (see
// MessagePumpForUI::HandleWorkMessage in message_pump_win.cc). But here,
// instead of posting a message on the wakeup pipe, we can avoid the
// syscalls and just signal that we have more work.
state_->has_work = true;
}
if (state_->should_quit)
return;
state_->more_work_is_plausible = false;
state_->delegate->DoDelayedWork(&delayed_work_time_);
}
if (state_->delegate->DoWork())
state_->more_work_is_plausible = true;
void MessagePumpForUI::AddObserver(Observer* observer) {
observers_.AddObserver(observer);
}
if (state_->should_quit)
return;
void MessagePumpForUI::RemoveObserver(Observer* observer) {
observers_.RemoveObserver(observer);
}
if (state_->delegate->DoDelayedWork(&delayed_work_time_))
state_->more_work_is_plausible = true;
if (state_->should_quit)
return;
void MessagePumpForUI::WillProcessEvent(GdkEvent* event) {
FOR_EACH_OBSERVER(Observer, observers_, WillProcessEvent(event));
}
// Don't do idle work if we think there are more important things
// that we could be doing.
if (state_->more_work_is_plausible)
return;
if (state_->delegate->DoIdleWork())
state_->more_work_is_plausible = true;
if (state_->should_quit)
return;
void MessagePumpForUI::DidProcessEvent(GdkEvent* event) {
FOR_EACH_OBSERVER(Observer, observers_, DidProcessEvent(event));
}
void MessagePumpForUI::Quit() {
@ -235,4 +316,19 @@ void MessagePumpForUI::ScheduleDelayedWork(const Time& delayed_work_time) {
ScheduleWork();
}
// static
void MessagePumpForUI::EventDispatcher(GdkEvent* event, gpointer data) {
MessagePumpForUI* message_pump = reinterpret_cast<MessagePumpForUI*>(data);
message_pump->WillProcessEvent(event);
if (message_pump->state_ && // state_ may be null during tests.
message_pump->state_->dispatcher) {
if (!message_pump->state_->dispatcher->Dispatch(event))
message_pump->state_->should_quit = true;
} else {
gtk_main_do_event(event);
}
message_pump->DidProcessEvent(event);
}
} // namespace base

Просмотреть файл

@ -5,21 +5,58 @@
#ifndef BASE_MESSAGE_PUMP_GLIB_H_
#define BASE_MESSAGE_PUMP_GLIB_H_
#include <glib.h>
#include "base/message_pump.h"
#include "base/observer_list.h"
#include "base/scoped_ptr.h"
#include "base/time.h"
typedef union _GdkEvent GdkEvent;
typedef struct _GMainContext GMainContext;
typedef struct _GPollFD GPollFD;
typedef struct _GSource GSource;
namespace base {
// This class implements a MessagePump needed for TYPE_UI MessageLoops on
// OS_LINUX platforms using GLib.
class MessagePumpForUI : public MessagePump {
public:
MessagePumpForUI();
~MessagePumpForUI();
// Observer is notified prior to a GdkEvent event being dispatched. As
// Observers are notified of every change, they have to be FAST!
class Observer {
public:
virtual ~Observer() {}
virtual void Run(Delegate* delegate);
// This method is called before processing a message.
virtual void WillProcessEvent(GdkEvent* event) = 0;
// This method is called after processing a message.
virtual void DidProcessEvent(GdkEvent* event) = 0;
};
// Dispatcher is used during a nested invocation of Run to dispatch events.
// If Run is invoked with a non-NULL Dispatcher, MessageLoop does not
// dispatch events (or invoke gtk_main_do_event), rather every event is
// passed to Dispatcher's Dispatch method for dispatch. It is up to the
// Dispatcher to dispatch, or not, the event.
//
// The nested loop is exited by either posting a quit, or returning false
// from Dispatch.
class Dispatcher {
public:
virtual ~Dispatcher() {}
// Dispatches the event. If true is returned processing continues as
// normal. If false is returned, the nested loop exits immediately.
virtual bool Dispatch(GdkEvent* event) = 0;
};
MessagePumpForUI();
virtual ~MessagePumpForUI();
// Like MessagePump::Run, but GdkEvent objects are routed through dispatcher.
virtual void RunWithDispatcher(Delegate* delegate, Dispatcher* dispatcher);
virtual void Run(Delegate* delegate) { RunWithDispatcher(delegate, NULL); }
virtual void Quit();
virtual void ScheduleWork();
virtual void ScheduleDelayedWork(const Time& delayed_work_time);
@ -27,16 +64,26 @@ class MessagePumpForUI : public MessagePump {
// Internal methods used for processing the pump callbacks. They are
// public for simplicity but should not be used directly. HandlePrepare
// is called during the prepare step of glib, and returns a timeout that
// will be passed to the poll. HandleDispatch is called after the poll
// has completed.
// will be passed to the poll. HandleCheck is called after the poll
// has completed, and returns whether or not HandleDispatch should be called.
// HandleDispatch is called if HandleCheck returned true.
int HandlePrepare();
bool HandleCheck();
void HandleDispatch();
// Adds an Observer, which will start receiving notifications immediately.
void AddObserver(Observer* observer);
// Removes an Observer. It is safe to call this method while an Observer is
// receiving a notification callback.
void RemoveObserver(Observer* observer);
private:
// We may make recursive calls to Run, so we save state that needs to be
// separate between them in this structure type.
struct RunState {
Delegate* delegate;
Dispatcher* dispatcher;
// Used to flag that the current Run() invocation should return ASAP.
bool should_quit;
@ -44,11 +91,23 @@ class MessagePumpForUI : public MessagePump {
// Used to count how many Run() invocations are on the stack.
int run_depth;
// Used internally for controlling whether we want a message pump
// iteration to be blocking or not.
bool more_work_is_plausible;
// This keeps the state of whether the pump got signaled that there was new
// work to be done. Since we eat the message on the wake up pipe as soon as
// we get it, we keep that state here to stay consistent.
bool has_work;
};
// Invoked from EventDispatcher. Notifies all observers we're about to
// process an event.
void WillProcessEvent(GdkEvent* event);
// Invoked from EventDispatcher. Notifies all observers we processed an
// event.
void DidProcessEvent(GdkEvent* event);
// Callback prior to gdk dispatching an event.
static void EventDispatcher(GdkEvent* event, void* data);
RunState* state_;
// This is a GLib structure that we can add event sources to. We use the
@ -69,7 +128,11 @@ class MessagePumpForUI : public MessagePump {
// Dispatch() will be called.
int wakeup_pipe_read_;
int wakeup_pipe_write_;
GPollFD wakeup_gpollfd_;
// Use a scoped_ptr to avoid needing the definition of GPollFD in the header.
scoped_ptr<GPollFD> wakeup_gpollfd_;
// List of observers.
ObserverList<Observer> observers_;
DISALLOW_COPY_AND_ASSIGN(MessagePumpForUI);
};