LCOV - code coverage report
Current view: top level - ipc/chromium/src/base - message_pump_glib.cc (source / functions) Hit Total Coverage
Test: output.info Lines: 0 131 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 19 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : // Copyright (c) 2008 The Chromium Authors. All rights reserved.
       4             : // Use of this source code is governed by a BSD-style license that can be
       5             : // found in the LICENSE file.
       6             : 
       7             : #include "base/message_pump_glib.h"
       8             : 
       9             : #include <fcntl.h>
      10             : #include <math.h>
      11             : 
      12             : #include <gtk/gtk.h>
      13             : #include <glib.h>
      14             : 
      15             : #include "base/eintr_wrapper.h"
      16             : #include "base/logging.h"
      17             : #include "base/platform_thread.h"
      18             : 
      19             : namespace {
      20             : 
      21             : // Return a timeout suitable for the glib loop, -1 to block forever,
      22             : // 0 to return right away, or a timeout in milliseconds from now.
      23           0 : int GetTimeIntervalMilliseconds(const base::TimeTicks& from) {
      24           0 :   if (from.is_null())
      25           0 :     return -1;
      26             : 
      27             :   // Be careful here.  TimeDelta has a precision of microseconds, but we want a
      28             :   // value in milliseconds.  If there are 5.5ms left, should the delay be 5 or
      29             :   // 6?  It should be 6 to avoid executing delayed work too early.
      30             :   int delay = static_cast<int>(
      31           0 :       ceil((from - base::TimeTicks::Now()).InMillisecondsF()));
      32             : 
      33             :   // If this value is negative, then we need to run delayed work soon.
      34           0 :   return delay < 0 ? 0 : delay;
      35             : }
      36             : 
      37             : // A brief refresher on GLib:
      38             : //     GLib sources have four callbacks: Prepare, Check, Dispatch and Finalize.
      39             : // On each iteration of the GLib pump, it calls each source's Prepare function.
      40             : // This function should return TRUE if it wants GLib to call its Dispatch, and
      41             : // FALSE otherwise.  It can also set a timeout in this case for the next time
      42             : // Prepare should be called again (it may be called sooner).
      43             : //     After the Prepare calls, GLib does a poll to check for events from the
      44             : // system.  File descriptors can be attached to the sources.  The poll may block
      45             : // if none of the Prepare calls returned TRUE.  It will block indefinitely, or
      46             : // by the minimum time returned by a source in Prepare.
      47             : //     After the poll, GLib calls Check for each source that returned FALSE
      48             : // from Prepare.  The return value of Check has the same meaning as for Prepare,
      49             : // making Check a second chance to tell GLib we are ready for Dispatch.
      50             : //     Finally, GLib calls Dispatch for each source that is ready.  If Dispatch
      51             : // returns FALSE, GLib will destroy the source.  Dispatch calls may be recursive
      52             : // (i.e., you can call Run from them), but Prepare and Check cannot.
      53             : //     Finalize is called when the source is destroyed.
      54             : // NOTE: It is common for subsytems to want to process pending events while
      55             : // doing intensive work, for example the flash plugin. They usually use the
      56             : // following pattern (recommended by the GTK docs):
      57             : // while (gtk_events_pending()) {
      58             : //   gtk_main_iteration();
      59             : // }
      60             : //
      61             : // gtk_events_pending just calls g_main_context_pending, which does the
      62             : // following:
      63             : // - Call prepare on all the sources.
      64             : // - Do the poll with a timeout of 0 (not blocking).
      65             : // - Call check on all the sources.
      66             : // - *Does not* call dispatch on the sources.
      67             : // - Return true if any of prepare() or check() returned true.
      68             : //
      69             : // gtk_main_iteration just calls g_main_context_iteration, which does the whole
      70             : // thing, respecting the timeout for the poll (and block, although it is
      71             : // expected not to if gtk_events_pending returned true), and call dispatch.
      72             : //
      73             : // Thus it is important to only return true from prepare or check if we
      74             : // actually have events or work to do. We also need to make sure we keep
      75             : // internal state consistent so that if prepare/check return true when called
      76             : // from gtk_events_pending, they will still return true when called right
      77             : // after, from gtk_main_iteration.
      78             : //
      79             : // For the GLib pump we try to follow the Windows UI pump model:
      80             : // - Whenever we receive a wakeup event or the timer for delayed work expires,
      81             : // we run DoWork and/or DoDelayedWork. That part will also run in the other
      82             : // event pumps.
      83             : // - We also run DoWork, DoDelayedWork, and possibly DoIdleWork in the main
      84             : // loop, around event handling.
      85             : 
      86             : struct WorkSource : public GSource {
      87             :   base::MessagePumpForUI* pump;
      88             : };
      89             : 
      90           0 : gboolean WorkSourcePrepare(GSource* source,
      91             :                            gint* timeout_ms) {
      92           0 :   *timeout_ms = static_cast<WorkSource*>(source)->pump->HandlePrepare();
      93             :   // We always return FALSE, so that our timeout is honored.  If we were
      94             :   // to return TRUE, the timeout would be considered to be 0 and the poll
      95             :   // would never block.  Once the poll is finished, Check will be called.
      96           0 :   return FALSE;
      97             : }
      98             : 
      99           0 : gboolean WorkSourceCheck(GSource* source) {
     100             :   // Only return TRUE if Dispatch should be called.
     101           0 :   return static_cast<WorkSource*>(source)->pump->HandleCheck();
     102             : }
     103             : 
     104           0 : gboolean WorkSourceDispatch(GSource* source,
     105             :                             GSourceFunc unused_func,
     106             :                             gpointer unused_data) {
     107             : 
     108           0 :   static_cast<WorkSource*>(source)->pump->HandleDispatch();
     109             :   // Always return TRUE so our source stays registered.
     110           0 :   return TRUE;
     111             : }
     112             : 
     113             : // I wish these could be const, but g_source_new wants non-const.
     114             : GSourceFuncs WorkSourceFuncs = {
     115             :   WorkSourcePrepare,
     116             :   WorkSourceCheck,
     117             :   WorkSourceDispatch,
     118             :   NULL
     119             : };
     120             : 
     121             : }  // namespace
     122             : 
     123             : 
     124             : namespace base {
     125             : 
     126           0 : MessagePumpForUI::MessagePumpForUI()
     127             :     : state_(NULL),
     128           0 :       context_(g_main_context_default()),
     129           0 :       wakeup_gpollfd_(new GPollFD),
     130           0 :       pipe_full_(false) {
     131             :   // Create our wakeup pipe, which is used to flag when work was scheduled.
     132             :   int fds[2];
     133           0 :   CHECK(pipe(fds) == 0);
     134           0 :   wakeup_pipe_read_  = fds[0];
     135           0 :   wakeup_pipe_write_ = fds[1];
     136           0 :   wakeup_gpollfd_->fd = wakeup_pipe_read_;
     137           0 :   wakeup_gpollfd_->events = G_IO_IN;
     138             : 
     139           0 :   work_source_ = g_source_new(&WorkSourceFuncs, sizeof(WorkSource));
     140           0 :   static_cast<WorkSource*>(work_source_)->pump = this;
     141           0 :   g_source_add_poll(work_source_, wakeup_gpollfd_.get());
     142             :   // Use a low priority so that we let other events in the queue go first.
     143           0 :   g_source_set_priority(work_source_, G_PRIORITY_DEFAULT_IDLE);
     144             :   // This is needed to allow Run calls inside Dispatch.
     145           0 :   g_source_set_can_recurse(work_source_, TRUE);
     146           0 :   g_source_attach(work_source_, context_);
     147           0 :   gdk_event_handler_set(&EventDispatcher, this, NULL);
     148           0 : }
     149             : 
     150           0 : MessagePumpForUI::~MessagePumpForUI() {
     151             :   gdk_event_handler_set(reinterpret_cast<GdkEventFunc>(gtk_main_do_event),
     152           0 :                         this, NULL);
     153           0 :   g_source_destroy(work_source_);
     154           0 :   g_source_unref(work_source_);
     155           0 :   close(wakeup_pipe_read_);
     156           0 :   close(wakeup_pipe_write_);
     157           0 : }
     158             : 
     159           0 : void MessagePumpForUI::RunWithDispatcher(Delegate* delegate,
     160             :                                          Dispatcher* dispatcher) {
     161             : #ifndef NDEBUG
     162             :   // Make sure we only run this on one thread.  GTK only has one message pump
     163             :   // so we can only have one UI loop per process.
     164           0 :   static PlatformThreadId thread_id = PlatformThread::CurrentId();
     165           0 :   DCHECK(thread_id == PlatformThread::CurrentId()) <<
     166             :       "Running MessagePumpForUI on two different threads; "
     167           0 :       "this is unsupported by GLib!";
     168             : #endif
     169             : 
     170             :   RunState state;
     171           0 :   state.delegate = delegate;
     172           0 :   state.dispatcher = dispatcher;
     173           0 :   state.should_quit = false;
     174           0 :   state.run_depth = state_ ? state_->run_depth + 1 : 1;
     175           0 :   state.has_work = false;
     176             : 
     177           0 :   RunState* previous_state = state_;
     178           0 :   state_ = &state;
     179             : 
     180             :   // We really only do a single task for each iteration of the loop.  If we
     181             :   // have done something, assume there is likely something more to do.  This
     182             :   // will mean that we don't block on the message pump until there was nothing
     183             :   // more to do.  We also set this to true to make sure not to block on the
     184             :   // first iteration of the loop, so RunAllPending() works correctly.
     185           0 :   bool more_work_is_plausible = true;
     186             : 
     187             :   // We run our own loop instead of using g_main_loop_quit in one of the
     188             :   // callbacks.  This is so we only quit our own loops, and we don't quit
     189             :   // nested loops run by others.  TODO(deanm): Is this what we want?
     190             :   for (;;) {
     191             :     // Don't block if we think we have more work to do.
     192           0 :     bool block = !more_work_is_plausible;
     193             : 
     194             :     // g_main_context_iteration returns true if events have been dispatched.
     195           0 :     more_work_is_plausible = g_main_context_iteration(context_, block);
     196           0 :     if (state_->should_quit)
     197           0 :       break;
     198             : 
     199           0 :     more_work_is_plausible |= state_->delegate->DoWork();
     200           0 :     if (state_->should_quit)
     201           0 :       break;
     202             : 
     203           0 :     more_work_is_plausible |=
     204           0 :         state_->delegate->DoDelayedWork(&delayed_work_time_);
     205           0 :     if (state_->should_quit)
     206           0 :       break;
     207             : 
     208           0 :     if (more_work_is_plausible)
     209           0 :       continue;
     210             : 
     211           0 :     more_work_is_plausible = state_->delegate->DoIdleWork();
     212           0 :     if (state_->should_quit)
     213           0 :       break;
     214           0 :   }
     215             : 
     216           0 :   state_ = previous_state;
     217           0 : }
     218             : 
     219             : // Return the timeout we want passed to poll.
     220           0 : int MessagePumpForUI::HandlePrepare() {
     221             :   // We know we have work, but we haven't called HandleDispatch yet. Don't let
     222             :   // the pump block so that we can do some processing.
     223           0 :   if (state_ &&  // state_ may be null during tests.
     224           0 :       state_->has_work)
     225           0 :     return 0;
     226             : 
     227             :   // We don't think we have work to do, but make sure not to block
     228             :   // longer than the next time we need to run delayed work.
     229           0 :   return GetTimeIntervalMilliseconds(delayed_work_time_);
     230             : }
     231             : 
     232           0 : bool MessagePumpForUI::HandleCheck() {
     233           0 :   if (!state_)  // state_ may be null during tests.
     234           0 :     return false;
     235             : 
     236             :   // We should only ever have a single message on the wakeup pipe since we only
     237             :   // write to the pipe when pipe_full_ is false. The glib poll will tell us
     238             :   // whether there was data, so this read shouldn't block.
     239           0 :   if (wakeup_gpollfd_->revents & G_IO_IN) {
     240           0 :     pipe_full_ = false;
     241             : 
     242             :     char msg;
     243           0 :     if (HANDLE_EINTR(read(wakeup_pipe_read_, &msg, 1)) != 1 || msg != '!') {
     244           0 :       NOTREACHED() << "Error reading from the wakeup pipe.";
     245             :     }
     246             :     // Since we ate the message, we need to record that we have more work,
     247             :     // because HandleCheck() may be called without HandleDispatch being called
     248             :     // afterwards.
     249           0 :     state_->has_work = true;
     250             :   }
     251             : 
     252           0 :   if (state_->has_work)
     253           0 :     return true;
     254             : 
     255           0 :   if (GetTimeIntervalMilliseconds(delayed_work_time_) == 0) {
     256             :     // The timer has expired. That condition will stay true until we process
     257             :     // that delayed work, so we don't need to record this differently.
     258           0 :     return true;
     259             :   }
     260             : 
     261           0 :   return false;
     262             : }
     263             : 
     264           0 : void MessagePumpForUI::HandleDispatch() {
     265           0 :   state_->has_work = false;
     266           0 :   if (state_->delegate->DoWork()) {
     267             :     // NOTE: on Windows at this point we would call ScheduleWork (see
     268             :     // MessagePumpForUI::HandleWorkMessage in message_pump_win.cc). But here,
     269             :     // instead of posting a message on the wakeup pipe, we can avoid the
     270             :     // syscalls and just signal that we have more work.
     271           0 :     state_->has_work = true;
     272             :   }
     273             : 
     274           0 :   if (state_->should_quit)
     275           0 :     return;
     276             : 
     277           0 :   state_->delegate->DoDelayedWork(&delayed_work_time_);
     278             : }
     279             : 
     280           0 : void MessagePumpForUI::AddObserver(Observer* observer) {
     281           0 :   observers_.AddObserver(observer);
     282           0 : }
     283             : 
     284           0 : void MessagePumpForUI::RemoveObserver(Observer* observer) {
     285           0 :   observers_.RemoveObserver(observer);
     286           0 : }
     287             : 
     288           0 : void MessagePumpForUI::WillProcessEvent(GdkEvent* event) {
     289           0 :   FOR_EACH_OBSERVER(Observer, observers_, WillProcessEvent(event));
     290           0 : }
     291             : 
     292           0 : void MessagePumpForUI::DidProcessEvent(GdkEvent* event) {
     293           0 :   FOR_EACH_OBSERVER(Observer, observers_, DidProcessEvent(event));
     294           0 : }
     295             : 
     296           0 : void MessagePumpForUI::Quit() {
     297           0 :   if (state_) {
     298           0 :     state_->should_quit = true;
     299             :   } else {
     300           0 :     NOTREACHED() << "Quit called outside Run!";
     301             :   }
     302           0 : }
     303             : 
     304           0 : void MessagePumpForUI::ScheduleWork() {
     305           0 :   bool was_full = pipe_full_.exchange(true);
     306           0 :   if (was_full) {
     307           0 :     return;
     308             :   }
     309             : 
     310             :   // This can be called on any thread, so we don't want to touch any state
     311             :   // variables as we would then need locks all over.  This ensures that if
     312             :   // we are sleeping in a poll that we will wake up.
     313           0 :   char msg = '!';
     314           0 :   if (HANDLE_EINTR(write(wakeup_pipe_write_, &msg, 1)) != 1) {
     315           0 :     NOTREACHED() << "Could not write to the UI message loop wakeup pipe!";
     316             :   }
     317             : }
     318             : 
     319           0 : void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) {
     320             :   // We need to wake up the loop in case the poll timeout needs to be
     321             :   // adjusted.  This will cause us to try to do work, but that's ok.
     322           0 :   delayed_work_time_ = delayed_work_time;
     323           0 :   ScheduleWork();
     324           0 : }
     325             : 
     326             : // static
     327           0 : void MessagePumpForUI::EventDispatcher(GdkEvent* event, gpointer data) {
     328           0 :   MessagePumpForUI* message_pump = reinterpret_cast<MessagePumpForUI*>(data);
     329             : 
     330           0 :   message_pump->WillProcessEvent(event);
     331           0 :   if (message_pump->state_ &&  // state_ may be null during tests.
     332           0 :       message_pump->state_->dispatcher) {
     333           0 :     if (!message_pump->state_->dispatcher->Dispatch(event))
     334           0 :       message_pump->state_->should_quit = true;
     335             :   } else {
     336           0 :     gtk_main_do_event(event);
     337             :   }
     338           0 :   message_pump->DidProcessEvent(event);
     339           0 : }
     340             : 
     341             : }  // namespace base

Generated by: LCOV version 1.13