chromium/base/message_loop/message_pump_glib.cc

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/message_loop/message_pump_glib.h"

#include <fcntl.h>
#include <glib.h>
#include <math.h>

#include "base/logging.h"
#include "base/memory/raw_ptr.h"
#include "base/notreached.h"
#include "base/numerics/safe_conversions.h"
#include "base/posix/eintr_wrapper.h"
#include "base/synchronization/lock.h"
#include "base/threading/platform_thread.h"

namespace base {

namespace {

// Priorities of event sources are important to let everything be processed.
// In particular, GTK event source should have the highest priority (because
// UI events come from it), then Wayland events (the ones coming from the FD
// watcher), and the lowest priority is GLib events (our base message pump).
//
// The g_source API uses ints to denote priorities, and the lower is its value,
// the higher is the priority (i.e., they are ordered backwards).
constexpr int kPriorityWork =;
constexpr int kPriorityFdWatch =;

// See the explanation above.
static_assert;

// Return a timeout suitable for the glib loop according to |next_task_time|, -1
// to block forever, 0 to return right away, or a timeout in milliseconds from
// now.
int GetTimeIntervalMilliseconds(TimeTicks next_task_time) {}

bool RunningOnMainThread() {}

// 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.
// NOTE: It is common for subsystems 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 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. That part will also run in the other event pumps.
// - We also run DoWork, and possibly DoIdleWork, in the main loop,
// around event handling.
//
// ---------------------------------------------------------------------------
//
// An overview on the way that we track work items:
//
//     ScopedDoWorkItems are used by this pump to track native work. They are
// stored by value in |state_| and are set/cleared as the pump runs. Their
// setting and clearing is done in the functions
// {Set,Clear,EnsureSet,EnsureCleared}ScopedWorkItem. Control flow in GLib is
// quite non-obvious because chrome is not notified when a nested loop is
// entered/exited. To detect nested loops, MessagePumpGlib uses
// |state_->do_work_depth| which is incremented when DoWork is entered, and a
// GLib library function, g_main_depth(), which indicates the current number of
// Dispatch() calls on the stack. To react to them, two separate
// ScopedDoWorkItems are used (a standard one used for all native work, and a
// second one used exclusively for forcing nesting when there is a native loop
// spinning).  Note that `ThreadController` flags all nesting as
// `Phase::kNested` so separating native and application work while nested isn't
// supported nor a goal.
//
//     It should also be noted that a second GSource has been added to GLib,
// referred to as the "observer" source. It is used because in the case where
// native work occurs on wakeup that is higher priority than Chrome (all of
// GTK), chrome won't even get notified that the pump is awake.
//
//     There are several cases to consider wrt. nesting level and order. In
// order, we have:
// A. [root] -> MessagePump::Run() -> native event -> g_main_context_iteration
// B. [root] -> MessagePump::Run() -> DoWork -> g_main_context_iteration
// C. [root] -> native -> DoWork -> MessagePump -> [...]
// The second two cases are identical for our purposes, and the last one turns
// out to be handled without any extra headache.
//
//     Consider nesting case A, where native work is called from
// |g_main_context_iteration()| from the pump, and that native work spins up a
// loop. For our purposes, this is a nested loop, because control is not
// returned to the pump once one iteration of the pump is complete. In this
// case, the pump needs to enter nesting without DoWork being involved at
// all. This is accomplished using |MessagePumpGlib::NestIfRequired()|, which is
// called during the Prepare() phase of GLib. As the pump records state on entry
// and exit from GLib using |OnEntryToGlib| and |OnExitFromGlib|, we can compare
// |g_main_depth| at |HandlePrepare| with the one before we entered
// |g_main_context_iteration|. If it is higher, there is a native loop being
// spun, and |RegisterNesting| is called, forcing nesting by initializing two
// work items at once. These are destroyed after the exit from
// |g_main_context_iteration| using |OnExitFromGlib|.
//
//     Then, considering nesting case B, |state_->do_work_depth| is incremented
// during any Chrome work, to allow the pump to detect re-entrancy during a
// chrome work item. This is required because `g_main_depth` is not incremented
// in any `DoWork` call not occuring during `Dispatch()` (i.e. during
// `MessagePumpGlib::Run()`). In this case, a nested loop is recorded, and the
// pump sets-and-clears scoped work items during Prepare, Check, and Dispatch. A
// work item can never be active when control flow returns to GLib (i.e. on
// return) during a nested loop, because the nested loop could exit at any
// point. This is fine because TimeKeeper is only concerned with the fact that a
// nested loop is in progress, as opposed to the various phases of the nested
// loop.
//
//     Finally, consider nesting case C, where a native loop is spinning
// entirely outside of Chrome, such as inside a signal handler, the pump might
// create and destroy DoWorkItems during Prepare() and Check(), but these work
// items will always get cleared during Dispatch(), before the pump enters a
// DoWork(), leading to the pump showing non-nested native work without the
// thread controller being active, the correct situation (which won't occur
// outside of startup or shutdown).  Once Dispatch() is called, the pump's
// nesting tracking works correctly, as state_->do_work_depth is increased, and
// upon re-entrancy we detect the nested loop, which is correct, as this is the
// only point at which the loop actually becomes "nested".
//
// -----------------------------------------------------------------------------
//
// As an overview of the steps taken by MessagePumpGLib to ensure that nested
// loops are detected adequately during each phase of the GLib loop:
//
// 0: Before entering GLib:
// 0.1: Record state about current state of GLib (g_main_depth()) for
// case 1.1.2.
//
// 1: Prepare.
// 1.1: Detection of nested loops

// 1.1.1: If |state_->do_work_depth| > 0, we are in nesting case B detailed
//        above. A work item must be newly created during this function to
//        trigger nesting, and is destroyed to ensure proper destruction order
//        in the case where GLib quits after Prepare().
//
// 1.1.2: Otherwise, check if we are in nesting case A above. If yes, trigger
//        nesting using ScopedDoWorkItems. The nesting will be cleared at exit
//        from GLib.
//
//        This check occurs only in |HandleObserverPrepare|, not in
//        |HandlePrepare|.
//
//        A third party is running a glib message loop. Since Chrome work is
//        registered with GLib at |G_PRIORITY_DEFAULT_IDLE|, a relatively low
//        priority, sources of default-or-higher priority will be Dispatch()ed
//        first. Since only one source is Dispatched per loop iteration,
//        |HandlePrepare| can get called several times in a row in the case that
//        there are any other events in the queue. A ScopedDoWorkItem is created
//        and destroyed to record this. That work item triggers nesting.
//
// 1.2: Other considerations
// 1.2.1: Sleep occurs between Prepare() and Check(). If Chrome will pass a
//        nonzero poll time to GLib, the inner ScopedDoWorkItem is cleared and
//        BeforeWait() is called. In nesting case A, the nesting work item will
//        not be cleared. A nested loop will typically not block.
//
//        Since Prepare() is called before Check() in all cases, the bulk of
//        nesting detection is done in Prepare().
//
// 2: Check.
// 2.1: Detection of nested loops:
// 2.1.1: In nesting case B, |ClearScopedWorkItem()| on exit.  A third party is
//        running a glib message loop. It is possible that at any point the
//        nested message loop will quit. In this case, we don't want to leave a
//        nested DoWorkItem on the stack.
//
// 2.2: Other considerations
// 2.2.1: A ScopedDoWorkItem may be created (if it was not already present) at
//        the entry to Check() to record a wakeup in the case that the pump
//        slept. It is important to note that this occurs both in
//        |HandleObserverCheck| and |HandleCheck| to ensure that at every point
//        as the pump enters the Dispatch phase it is awake. In the case it is
//        already awake, this is a very cheap operation.
//
// 3: Dispatch
// 3.1 Detection of nested loops
// 3.1.1: |state_->do_work_depth| is incremented on entry and decremented on
//        exit. This is used to detect nesting case B.
//
// 3.1.2: Nested loops can be quit at any point, and so ScopedDoWorkItems can't
//        be left on the stack for the same reasons as in 1.1.1/2.1.1.
//
// 3.2 Other considerations
// 3.2.1: Since DoWork creates its own work items, ScopedDoWorkItems are not
//        used as this would trigger nesting in all cases.
//
// 4: Post GLib
// 4.1: Detection of nested loops
// 4.1.1: |state_->do_work_depth| is also increased during the DoWork in Run()
//        as nesting in that case [calling glib from third party code] needs to
//        clear all work items after return to avoid improper destruction order.
//
// 4.2: Other considerations:
// 4.2.1: DoWork uses its own work item, so no ScopedDoWorkItems are active in
//        this case.

struct WorkSource : public GSource {};

gboolean WorkSourcePrepare(GSource* source, gint* timeout_ms) {}

gboolean WorkSourceCheck(GSource* source) {}

gboolean WorkSourceDispatch(GSource* source,
                            GSourceFunc unused_func,
                            gpointer unused_data) {}

void WorkSourceFinalize(GSource* source) {}

// I wish these could be const, but g_source_new wants non-const.
GSourceFuncs g_work_source_funcs =;

struct ObserverSource : public GSource {};

gboolean ObserverPrepare(GSource* gsource, gint* timeout_ms) {}

gboolean ObserverCheck(GSource* gsource) {}

void ObserverFinalize(GSource* source) {}

GSourceFuncs g_observer_funcs =;

struct FdWatchSource : public GSource {};

gboolean FdWatchSourcePrepare(GSource* source, gint* timeout_ms) {}

gboolean FdWatchSourceCheck(GSource* gsource) {}

gboolean FdWatchSourceDispatch(GSource* gsource,
                               GSourceFunc unused_func,
                               gpointer unused_data) {}

void FdWatchSourceFinalize(GSource* gsource) {}

GSourceFuncs g_fd_watch_source_funcs =;

}  // namespace

struct MessagePumpGlib::RunState {};

MessagePumpGlib::MessagePumpGlib()
    :{}

MessagePumpGlib::~MessagePumpGlib() {}

MessagePumpGlib::FdWatchController::FdWatchController(const Location& location)
    :{}

MessagePumpGlib::FdWatchController::~FdWatchController() {}

bool MessagePumpGlib::FdWatchController::StopWatchingFileDescriptor() {}

bool MessagePumpGlib::FdWatchController::IsInitialized() const {}

bool MessagePumpGlib::FdWatchController::InitOrUpdate(int fd,
                                                      int mode,
                                                      FdWatcher* watcher) {}

bool MessagePumpGlib::FdWatchController::Attach(MessagePumpGlib* pump) {}

void MessagePumpGlib::FdWatchController::NotifyCanRead() {}

void MessagePumpGlib::FdWatchController::NotifyCanWrite() {}

bool MessagePumpGlib::WatchFileDescriptor(int fd,
                                          bool persistent,
                                          int mode,
                                          FdWatchController* controller,
                                          FdWatcher* watcher) {}

void MessagePumpGlib::HandleObserverPrepare() {}

bool MessagePumpGlib::HandleObserverCheck() {}

// Return the timeout we want passed to poll.
int MessagePumpGlib::HandlePrepare() {}

bool MessagePumpGlib::HandleCheck() {}

void MessagePumpGlib::HandleDispatch() {}

void MessagePumpGlib::Run(Delegate* delegate) {}

void MessagePumpGlib::Quit() {}

void MessagePumpGlib::ScheduleWork() {}

void MessagePumpGlib::ScheduleDelayedWork(
    const Delegate::NextWorkInfo& next_work_info) {}

bool MessagePumpGlib::HandleFdWatchCheck(FdWatchController* controller) {}

void MessagePumpGlib::HandleFdWatchDispatch(FdWatchController* controller) {}

bool MessagePumpGlib::ShouldQuit() const {}

void MessagePumpGlib::SetScopedWorkItem() {}

void MessagePumpGlib::ClearScopedWorkItem() {}

void MessagePumpGlib::EnsureSetScopedWorkItem() {}

void MessagePumpGlib::EnsureClearedScopedWorkItem() {}

void MessagePumpGlib::RegisterNested() {}

void MessagePumpGlib::UnregisterNested() {}

void MessagePumpGlib::NestIfRequired() {}

void MessagePumpGlib::UnnestIfRequired() {}

void MessagePumpGlib::OnEntryToGlib() {}

void MessagePumpGlib::OnExitFromGlib() {}

}  // namespace base