зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
f0e6e5fa63
Коммит
617be9e842
|
@ -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);
|
||||
};
|
||||
|
|
Загрузка…
Ссылка в новой задаче