// 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