ipc/chromium/src/base/message_pump_glib.cc

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/ipc/chromium/src/base/message_pump_glib.cc	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,334 @@
     1.4 +// Copyright (c) 2008 The Chromium Authors. All rights reserved.
     1.5 +// Use of this source code is governed by a BSD-style license that can be
     1.6 +// found in the LICENSE file.
     1.7 +
     1.8 +#include "base/message_pump_glib.h"
     1.9 +
    1.10 +#include <fcntl.h>
    1.11 +#include <math.h>
    1.12 +
    1.13 +#include <gtk/gtk.h>
    1.14 +#include <glib.h>
    1.15 +
    1.16 +#include "base/eintr_wrapper.h"
    1.17 +#include "base/logging.h"
    1.18 +#include "base/platform_thread.h"
    1.19 +
    1.20 +namespace {
    1.21 +
    1.22 +// We send a byte across a pipe to wakeup the event loop.
    1.23 +const char kWorkScheduled = '\0';
    1.24 +
    1.25 +// Return a timeout suitable for the glib loop, -1 to block forever,
    1.26 +// 0 to return right away, or a timeout in milliseconds from now.
    1.27 +int GetTimeIntervalMilliseconds(const base::TimeTicks& from) {
    1.28 +  if (from.is_null())
    1.29 +    return -1;
    1.30 +
    1.31 +  // Be careful here.  TimeDelta has a precision of microseconds, but we want a
    1.32 +  // value in milliseconds.  If there are 5.5ms left, should the delay be 5 or
    1.33 +  // 6?  It should be 6 to avoid executing delayed work too early.
    1.34 +  int delay = static_cast<int>(
    1.35 +      ceil((from - base::TimeTicks::Now()).InMillisecondsF()));
    1.36 +
    1.37 +  // If this value is negative, then we need to run delayed work soon.
    1.38 +  return delay < 0 ? 0 : delay;
    1.39 +}
    1.40 +
    1.41 +// A brief refresher on GLib:
    1.42 +//     GLib sources have four callbacks: Prepare, Check, Dispatch and Finalize.
    1.43 +// On each iteration of the GLib pump, it calls each source's Prepare function.
    1.44 +// This function should return TRUE if it wants GLib to call its Dispatch, and
    1.45 +// FALSE otherwise.  It can also set a timeout in this case for the next time
    1.46 +// Prepare should be called again (it may be called sooner).
    1.47 +//     After the Prepare calls, GLib does a poll to check for events from the
    1.48 +// system.  File descriptors can be attached to the sources.  The poll may block
    1.49 +// if none of the Prepare calls returned TRUE.  It will block indefinitely, or
    1.50 +// by the minimum time returned by a source in Prepare.
    1.51 +//     After the poll, GLib calls Check for each source that returned FALSE
    1.52 +// from Prepare.  The return value of Check has the same meaning as for Prepare,
    1.53 +// making Check a second chance to tell GLib we are ready for Dispatch.
    1.54 +//     Finally, GLib calls Dispatch for each source that is ready.  If Dispatch
    1.55 +// returns FALSE, GLib will destroy the source.  Dispatch calls may be recursive
    1.56 +// (i.e., you can call Run from them), but Prepare and Check cannot.
    1.57 +//     Finalize is called when the source is destroyed.
    1.58 +// NOTE: It is common for subsytems to want to process pending events while
    1.59 +// doing intensive work, for example the flash plugin. They usually use the
    1.60 +// following pattern (recommended by the GTK docs):
    1.61 +// while (gtk_events_pending()) {
    1.62 +//   gtk_main_iteration();
    1.63 +// }
    1.64 +//
    1.65 +// gtk_events_pending just calls g_main_context_pending, which does the
    1.66 +// following:
    1.67 +// - Call prepare on all the sources.
    1.68 +// - Do the poll with a timeout of 0 (not blocking).
    1.69 +// - Call check on all the sources.
    1.70 +// - *Does not* call dispatch on the sources.
    1.71 +// - Return true if any of prepare() or check() returned true.
    1.72 +//
    1.73 +// gtk_main_iteration just calls g_main_context_iteration, which does the whole
    1.74 +// thing, respecting the timeout for the poll (and block, although it is
    1.75 +// expected not to if gtk_events_pending returned true), and call dispatch.
    1.76 +//
    1.77 +// Thus it is important to only return true from prepare or check if we
    1.78 +// actually have events or work to do. We also need to make sure we keep
    1.79 +// internal state consistent so that if prepare/check return true when called
    1.80 +// from gtk_events_pending, they will still return true when called right
    1.81 +// after, from gtk_main_iteration.
    1.82 +//
    1.83 +// For the GLib pump we try to follow the Windows UI pump model:
    1.84 +// - Whenever we receive a wakeup event or the timer for delayed work expires,
    1.85 +// we run DoWork and/or DoDelayedWork. That part will also run in the other
    1.86 +// event pumps.
    1.87 +// - We also run DoWork, DoDelayedWork, and possibly DoIdleWork in the main
    1.88 +// loop, around event handling.
    1.89 +
    1.90 +struct WorkSource : public GSource {
    1.91 +  base::MessagePumpForUI* pump;
    1.92 +};
    1.93 +
    1.94 +gboolean WorkSourcePrepare(GSource* source,
    1.95 +                           gint* timeout_ms) {
    1.96 +  *timeout_ms = static_cast<WorkSource*>(source)->pump->HandlePrepare();
    1.97 +  // We always return FALSE, so that our timeout is honored.  If we were
    1.98 +  // to return TRUE, the timeout would be considered to be 0 and the poll
    1.99 +  // would never block.  Once the poll is finished, Check will be called.
   1.100 +  return FALSE;
   1.101 +}
   1.102 +
   1.103 +gboolean WorkSourceCheck(GSource* source) {
   1.104 +  // Only return TRUE if Dispatch should be called.
   1.105 +  return static_cast<WorkSource*>(source)->pump->HandleCheck();
   1.106 +}
   1.107 +
   1.108 +gboolean WorkSourceDispatch(GSource* source,
   1.109 +                            GSourceFunc unused_func,
   1.110 +                            gpointer unused_data) {
   1.111 +
   1.112 +  static_cast<WorkSource*>(source)->pump->HandleDispatch();
   1.113 +  // Always return TRUE so our source stays registered.
   1.114 +  return TRUE;
   1.115 +}
   1.116 +
   1.117 +// I wish these could be const, but g_source_new wants non-const.
   1.118 +GSourceFuncs WorkSourceFuncs = {
   1.119 +  WorkSourcePrepare,
   1.120 +  WorkSourceCheck,
   1.121 +  WorkSourceDispatch,
   1.122 +  NULL
   1.123 +};
   1.124 +
   1.125 +}  // namespace
   1.126 +
   1.127 +
   1.128 +namespace base {
   1.129 +
   1.130 +MessagePumpForUI::MessagePumpForUI()
   1.131 +    : state_(NULL),
   1.132 +      context_(g_main_context_default()),
   1.133 +      wakeup_gpollfd_(new GPollFD) {
   1.134 +  // Create our wakeup pipe, which is used to flag when work was scheduled.
   1.135 +  int fds[2];
   1.136 +  CHECK(pipe(fds) == 0);
   1.137 +  wakeup_pipe_read_  = fds[0];
   1.138 +  wakeup_pipe_write_ = fds[1];
   1.139 +  wakeup_gpollfd_->fd = wakeup_pipe_read_;
   1.140 +  wakeup_gpollfd_->events = G_IO_IN;
   1.141 +
   1.142 +  work_source_ = g_source_new(&WorkSourceFuncs, sizeof(WorkSource));
   1.143 +  static_cast<WorkSource*>(work_source_)->pump = this;
   1.144 +  g_source_add_poll(work_source_, wakeup_gpollfd_.get());
   1.145 +  // Use a low priority so that we let other events in the queue go first.
   1.146 +  g_source_set_priority(work_source_, G_PRIORITY_DEFAULT_IDLE);
   1.147 +  // This is needed to allow Run calls inside Dispatch.
   1.148 +  g_source_set_can_recurse(work_source_, TRUE);
   1.149 +  g_source_attach(work_source_, context_);
   1.150 +  gdk_event_handler_set(&EventDispatcher, this, NULL);
   1.151 +}
   1.152 +
   1.153 +MessagePumpForUI::~MessagePumpForUI() {
   1.154 +  gdk_event_handler_set(reinterpret_cast<GdkEventFunc>(gtk_main_do_event),
   1.155 +                        this, NULL);
   1.156 +  g_source_destroy(work_source_);
   1.157 +  g_source_unref(work_source_);
   1.158 +  close(wakeup_pipe_read_);
   1.159 +  close(wakeup_pipe_write_);
   1.160 +}
   1.161 +
   1.162 +void MessagePumpForUI::RunWithDispatcher(Delegate* delegate,
   1.163 +                                         Dispatcher* dispatcher) {
   1.164 +#ifndef NDEBUG
   1.165 +  // Make sure we only run this on one thread.  GTK only has one message pump
   1.166 +  // so we can only have one UI loop per process.
   1.167 +  static PlatformThreadId thread_id = PlatformThread::CurrentId();
   1.168 +  DCHECK(thread_id == PlatformThread::CurrentId()) <<
   1.169 +      "Running MessagePumpForUI on two different threads; "
   1.170 +      "this is unsupported by GLib!";
   1.171 +#endif
   1.172 +
   1.173 +  RunState state;
   1.174 +  state.delegate = delegate;
   1.175 +  state.dispatcher = dispatcher;
   1.176 +  state.should_quit = false;
   1.177 +  state.run_depth = state_ ? state_->run_depth + 1 : 1;
   1.178 +  state.has_work = false;
   1.179 +
   1.180 +  RunState* previous_state = state_;
   1.181 +  state_ = &state;
   1.182 +
   1.183 +  // We really only do a single task for each iteration of the loop.  If we
   1.184 +  // have done something, assume there is likely something more to do.  This
   1.185 +  // will mean that we don't block on the message pump until there was nothing
   1.186 +  // more to do.  We also set this to true to make sure not to block on the
   1.187 +  // first iteration of the loop, so RunAllPending() works correctly.
   1.188 +  bool more_work_is_plausible = true;
   1.189 +
   1.190 +  // We run our own loop instead of using g_main_loop_quit in one of the
   1.191 +  // callbacks.  This is so we only quit our own loops, and we don't quit
   1.192 +  // nested loops run by others.  TODO(deanm): Is this what we want?
   1.193 +  for (;;) {
   1.194 +    // Don't block if we think we have more work to do.
   1.195 +    bool block = !more_work_is_plausible;
   1.196 +
   1.197 +    // g_main_context_iteration returns true if events have been dispatched.
   1.198 +    more_work_is_plausible = g_main_context_iteration(context_, block);
   1.199 +    if (state_->should_quit)
   1.200 +      break;
   1.201 +
   1.202 +    more_work_is_plausible |= state_->delegate->DoWork();
   1.203 +    if (state_->should_quit)
   1.204 +      break;
   1.205 +
   1.206 +    more_work_is_plausible |=
   1.207 +        state_->delegate->DoDelayedWork(&delayed_work_time_);
   1.208 +    if (state_->should_quit)
   1.209 +      break;
   1.210 +
   1.211 +    if (more_work_is_plausible)
   1.212 +      continue;
   1.213 +
   1.214 +    more_work_is_plausible = state_->delegate->DoIdleWork();
   1.215 +    if (state_->should_quit)
   1.216 +      break;
   1.217 +  }
   1.218 +
   1.219 +  state_ = previous_state;
   1.220 +}
   1.221 +
   1.222 +// Return the timeout we want passed to poll.
   1.223 +int MessagePumpForUI::HandlePrepare() {
   1.224 +  // We know we have work, but we haven't called HandleDispatch yet. Don't let
   1.225 +  // the pump block so that we can do some processing.
   1.226 +  if (state_ &&  // state_ may be null during tests.
   1.227 +      state_->has_work)
   1.228 +    return 0;
   1.229 +
   1.230 +  // We don't think we have work to do, but make sure not to block
   1.231 +  // longer than the next time we need to run delayed work.
   1.232 +  return GetTimeIntervalMilliseconds(delayed_work_time_);
   1.233 +}
   1.234 +
   1.235 +bool MessagePumpForUI::HandleCheck() {
   1.236 +  if (!state_)  // state_ may be null during tests.
   1.237 +    return false;
   1.238 +
   1.239 +  // We should only ever have a single message on the wakeup pipe, since we
   1.240 +  // are only signaled when the queue went from empty to non-empty.  The glib
   1.241 +  // poll will tell us whether there was data, so this read shouldn't block.
   1.242 +  if (wakeup_gpollfd_->revents & G_IO_IN) {
   1.243 +    char msg;
   1.244 +    if (HANDLE_EINTR(read(wakeup_pipe_read_, &msg, 1)) != 1 || msg != '!') {
   1.245 +      NOTREACHED() << "Error reading from the wakeup pipe.";
   1.246 +    }
   1.247 +    // Since we ate the message, we need to record that we have more work,
   1.248 +    // because HandleCheck() may be called without HandleDispatch being called
   1.249 +    // afterwards.
   1.250 +    state_->has_work = true;
   1.251 +  }
   1.252 +
   1.253 +  if (state_->has_work)
   1.254 +    return true;
   1.255 +
   1.256 +  if (GetTimeIntervalMilliseconds(delayed_work_time_) == 0) {
   1.257 +    // The timer has expired. That condition will stay true until we process
   1.258 +    // that delayed work, so we don't need to record this differently.
   1.259 +    return true;
   1.260 +  }
   1.261 +
   1.262 +  return false;
   1.263 +}
   1.264 +
   1.265 +void MessagePumpForUI::HandleDispatch() {
   1.266 +  state_->has_work = false;
   1.267 +  if (state_->delegate->DoWork()) {
   1.268 +    // NOTE: on Windows at this point we would call ScheduleWork (see
   1.269 +    // MessagePumpForUI::HandleWorkMessage in message_pump_win.cc). But here,
   1.270 +    // instead of posting a message on the wakeup pipe, we can avoid the
   1.271 +    // syscalls and just signal that we have more work.
   1.272 +    state_->has_work = true;
   1.273 +  }
   1.274 +
   1.275 +  if (state_->should_quit)
   1.276 +    return;
   1.277 +
   1.278 +  state_->delegate->DoDelayedWork(&delayed_work_time_);
   1.279 +}
   1.280 +
   1.281 +void MessagePumpForUI::AddObserver(Observer* observer) {
   1.282 +  observers_.AddObserver(observer);
   1.283 +}
   1.284 +
   1.285 +void MessagePumpForUI::RemoveObserver(Observer* observer) {
   1.286 +  observers_.RemoveObserver(observer);
   1.287 +}
   1.288 +
   1.289 +void MessagePumpForUI::WillProcessEvent(GdkEvent* event) {
   1.290 +  FOR_EACH_OBSERVER(Observer, observers_, WillProcessEvent(event));
   1.291 +}
   1.292 +
   1.293 +void MessagePumpForUI::DidProcessEvent(GdkEvent* event) {
   1.294 +  FOR_EACH_OBSERVER(Observer, observers_, DidProcessEvent(event));
   1.295 +}
   1.296 +
   1.297 +void MessagePumpForUI::Quit() {
   1.298 +  if (state_) {
   1.299 +    state_->should_quit = true;
   1.300 +  } else {
   1.301 +    NOTREACHED() << "Quit called outside Run!";
   1.302 +  }
   1.303 +}
   1.304 +
   1.305 +void MessagePumpForUI::ScheduleWork() {
   1.306 +  // This can be called on any thread, so we don't want to touch any state
   1.307 +  // variables as we would then need locks all over.  This ensures that if
   1.308 +  // we are sleeping in a poll that we will wake up.
   1.309 +  char msg = '!';
   1.310 +  if (HANDLE_EINTR(write(wakeup_pipe_write_, &msg, 1)) != 1) {
   1.311 +    NOTREACHED() << "Could not write to the UI message loop wakeup pipe!";
   1.312 +  }
   1.313 +}
   1.314 +
   1.315 +void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) {
   1.316 +  // We need to wake up the loop in case the poll timeout needs to be
   1.317 +  // adjusted.  This will cause us to try to do work, but that's ok.
   1.318 +  delayed_work_time_ = delayed_work_time;
   1.319 +  ScheduleWork();
   1.320 +}
   1.321 +
   1.322 +// static
   1.323 +void MessagePumpForUI::EventDispatcher(GdkEvent* event, gpointer data) {
   1.324 +  MessagePumpForUI* message_pump = reinterpret_cast<MessagePumpForUI*>(data);
   1.325 +
   1.326 +  message_pump->WillProcessEvent(event);
   1.327 +  if (message_pump->state_ &&  // state_ may be null during tests.
   1.328 +      message_pump->state_->dispatcher) {
   1.329 +    if (!message_pump->state_->dispatcher->Dispatch(event))
   1.330 +      message_pump->state_->should_quit = true;
   1.331 +  } else {
   1.332 +    gtk_main_do_event(event);
   1.333 +  }
   1.334 +  message_pump->DidProcessEvent(event);
   1.335 +}
   1.336 +
   1.337 +}  // namespace base

mercurial