xref: /aosp_15_r20/external/cronet/base/message_loop/message_pump.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef BASE_MESSAGE_LOOP_MESSAGE_PUMP_H_
6 #define BASE_MESSAGE_LOOP_MESSAGE_PUMP_H_
7 
8 #include <memory>
9 #include <utility>
10 
11 #include "base/base_export.h"
12 #include "base/check.h"
13 #include "base/check_op.h"
14 #include "base/memory/raw_ptr_exclusion.h"
15 #include "base/message_loop/message_pump_type.h"
16 #include "base/sequence_checker.h"
17 #include "base/time/time.h"
18 #include "build/build_config.h"
19 
20 namespace base {
21 
22 class TimeTicks;
23 
24 class BASE_EXPORT MessagePump {
25  public:
26   using MessagePumpFactory = std::unique_ptr<MessagePump>();
27   // Uses the given base::MessagePumpFactory to override the default MessagePump
28   // implementation for 'MessagePumpType::UI'. May only be called once.
29   static void OverrideMessagePumpForUIFactory(MessagePumpFactory* factory);
30 
31   // Returns true if the MessagePumpForUI has been overidden.
32   static bool IsMessagePumpForUIFactoryOveridden();
33 
34   static void InitializeFeatures();
35 
36   // Manage the state of |kAlignWakeUps| and the leeway of the process.
37   static void OverrideAlignWakeUpsState(bool enabled, TimeDelta leeway);
38   static void ResetAlignWakeUpsState();
39   static bool GetAlignWakeUpsEnabled();
40   static TimeDelta GetLeewayIgnoringThreadOverride();
41   static TimeDelta GetLeewayForCurrentThread();
42 
43   // Creates the default MessagePump based on |type|. Caller owns return value.
44   static std::unique_ptr<MessagePump> Create(MessagePumpType type);
45 
46   // Please see the comments above the Run method for an illustration of how
47   // these delegate methods are used.
48   class BASE_EXPORT Delegate {
49    public:
50     virtual ~Delegate() = default;
51 
52     struct NextWorkInfo {
53       // Helper to extract a TimeDelta for pumps that need a
54       // timeout-till-next-task.
remaining_delayNextWorkInfo55       TimeDelta remaining_delay() const {
56         DCHECK(!delayed_run_time.is_null() && !delayed_run_time.is_max());
57         DCHECK_GE(TimeTicks::Now(), recent_now);
58         return delayed_run_time - recent_now;
59       }
60 
61       // Helper to verify if the next task is ready right away.
is_immediateNextWorkInfo62       bool is_immediate() const { return delayed_run_time.is_null(); }
63 
64       // The next PendingTask's |delayed_run_time|. is_null() if there's extra
65       // work to run immediately. is_max() if there are no more immediate nor
66       // delayed tasks.
67       TimeTicks delayed_run_time;
68 
69       // |leeway| determines the preferred time range for scheduling
70       // work. A larger leeway provides more freedom to schedule work at
71       // an optimal time for power consumption. This field is ignored
72       // for immediate work.
73       TimeDelta leeway;
74 
75       // A recent view of TimeTicks::Now(). Only valid if |delayed_run_time|
76       // isn't null nor max. MessagePump impls should use remaining_delay()
77       // instead of resampling Now() if they wish to sleep for a TimeDelta.
78       TimeTicks recent_now;
79 
80       // If true, native messages should be processed before executing more work
81       // from the Delegate. This is an optional hint; not all message pumps
82       // implement this.
83       bool yield_to_native = false;
84     };
85 
86     // Executes an immediate task or a ripe delayed task. Returns information
87     // about when DoWork() should be called again. If the returned NextWorkInfo
88     // is_immediate(), DoWork() must be invoked again shortly. Else, DoWork()
89     // must be invoked at |NextWorkInfo::delayed_run_time| or when
90     // ScheduleWork() is invoked, whichever comes first. Redundant/spurious
91     // invocations of DoWork() outside of those requirements are tolerated.
92     // DoIdleWork() will not be called so long as this returns a NextWorkInfo
93     // which is_immediate().
94     virtual NextWorkInfo DoWork() = 0;
95 
96     // Called from within Run just before the message pump goes to sleep.
97     // Returns true to indicate that idle work was done; in which case Run()
98     // should resume with calling DoWork(). Returning false means the pump
99     // should now wait.
100     virtual bool DoIdleWork() = 0;
101 
102     class ScopedDoWorkItem {
103      public:
ScopedDoWorkItem()104       ScopedDoWorkItem() : outer_(nullptr), work_item_depth_(0) {}
105 
~ScopedDoWorkItem()106       ~ScopedDoWorkItem() {
107         if (outer_) {
108           outer_->OnEndWorkItem(work_item_depth_);
109         }
110       }
111 
ScopedDoWorkItem(ScopedDoWorkItem && rhs)112       ScopedDoWorkItem(ScopedDoWorkItem&& rhs)
113           : outer_(std::exchange(rhs.outer_, nullptr)),
114             work_item_depth_(rhs.work_item_depth_) {}
115       ScopedDoWorkItem& operator=(ScopedDoWorkItem&& rhs) {
116         // We should only ever go from an empty ScopedDoWorkItem to an
117         // initialized one, or from an initialized one to an empty one.
118         CHECK_NE(IsNull(), rhs.IsNull());
119         // Since we're overwriting this ScopedDoWorkItem, we need to record its
120         // destruction.
121         if (outer_) {
122           outer_->OnEndWorkItem(work_item_depth_);
123         }
124 
125         work_item_depth_ = rhs.work_item_depth_;
126         outer_ = std::exchange(rhs.outer_, nullptr);
127         return *this;
128       }
129 
IsNull()130       bool IsNull() { return !outer_; }
131 
132      private:
133       friend Delegate;
134 
ScopedDoWorkItem(Delegate * outer)135       explicit ScopedDoWorkItem(Delegate* outer) : outer_(outer) {
136         outer_->OnBeginWorkItem();
137         work_item_depth_ = outer_->RunDepth();
138       }
139 
140       // `outer_` is not a raw_ptr<...> for performance reasons (based on
141       // analysis of sampling profiler data and tab_search:top100:2020).
142       RAW_PTR_EXCLUSION Delegate* outer_;
143 
144       // Records the run level at which this DoWorkItem was created to allow
145       // detection of exits of nested loops.
146       int work_item_depth_;
147     };
148 
149     // Called before a unit of work is executed. This allows reports
150     // about individual units of work to be produced. The unit of work ends when
151     // the returned ScopedDoWorkItem goes out of scope.
152     // TODO(crbug.com/851163): Place calls for all platforms. Without this, some
153     // state like the top-level "ThreadController active" trace event will not
154     // be correct when work is performed.
BeginWorkItem()155     [[nodiscard]] ScopedDoWorkItem BeginWorkItem() {
156       return ScopedDoWorkItem(this);
157     }
158 
159     // Called before the message pump starts waiting for work. This indicates
160     // that the message pump is idle (out of application work and ideally out of
161     // native work -- if it can tell).
162     virtual void BeforeWait() = 0;
163 
164     // May be called when starting to process native work and it is guaranteed
165     // that DoWork() will be called again before sleeping. Allows the delegate
166     // to skip unnecessary ScheduleWork() calls.
167     virtual void BeginNativeWorkBeforeDoWork() = 0;
168 
169     // Returns the nesting level at which the Delegate is currently running.
170     virtual int RunDepth() = 0;
171 
172    private:
173     // Called upon entering/exiting a ScopedDoWorkItem.
174     virtual void OnBeginWorkItem() = 0;
175     virtual void OnEndWorkItem(int work_item_depth) = 0;
176   };
177 
178   MessagePump();
179   virtual ~MessagePump();
180 
181   // The Run method is called to enter the message pump's run loop.
182   //
183   // Within the method, the message pump is responsible for processing native
184   // messages as well as for giving cycles to the delegate periodically. The
185   // message pump should take care to mix delegate callbacks with native message
186   // processing so neither type of event starves the other of cycles. Each call
187   // to a delegate function is considered the beginning of a new "unit of work".
188   //
189   // The anatomy of a typical run loop:
190   //
191   //   for (;;) {
192   //     bool did_native_work = false;
193   //     {
194   //       auto scoped_do_work_item = state_->delegate->BeginWorkItem();
195   //       did_native_work = DoNativeWork();
196   //     }
197   //     if (should_quit_)
198   //       break;
199   //
200   //     Delegate::NextWorkInfo next_work_info = delegate->DoWork();
201   //     if (should_quit_)
202   //       break;
203   //
204   //     if (did_native_work || next_work_info.is_immediate())
205   //       continue;
206   //
207   //     bool did_idle_work = delegate_->DoIdleWork();
208   //     if (should_quit_)
209   //       break;
210   //
211   //     if (did_idle_work)
212   //       continue;
213   //
214   //     WaitForWork();
215   //   }
216   //
217 
218   // Here, DoNativeWork is some private method of the message pump that is
219   // responsible for dispatching the next UI message or notifying the next IO
220   // completion (for example).  WaitForWork is a private method that simply
221   // blocks until there is more work of any type to do.
222   //
223   // Notice that the run loop cycles between calling DoNativeWork and DoWork
224   // methods. This helps ensure that none of these work queues starve the
225   // others. This is important for message pumps that are used to drive
226   // animations, for example.
227   //
228   // Notice also that after each callout to foreign code, the run loop checks to
229   // see if it should quit.  The Quit method is responsible for setting this
230   // flag.  No further work is done once the quit flag is set.
231   //
232   // NOTE 1: Run may be called reentrantly from any of the callouts to foreign
233   // code (internal work, DoWork, DoIdleWork). As a result, DoWork and
234   // DoIdleWork must be reentrant.
235   //
236   // NOTE 2: Run implementations must arrange for DoWork to be invoked as
237   // expected if a callout to foreign code enters a message pump outside their
238   // control. For example, the MessageBox API on Windows pumps UI messages. If
239   // the MessageBox API is called (indirectly) from within Run, it is expected
240   // that DoWork will be invoked from within that call in response to
241   // ScheduleWork or as requested by the last NextWorkInfo returned by DoWork.
242   // The MessagePump::Delegate may then elect to do nested work or not depending
243   // on its policy in that context. Regardless of that decision (and return
244   // value of the nested DoWork() call), DoWork() will be invoked again when the
245   // nested loop unwinds.
246   virtual void Run(Delegate* delegate) = 0;
247 
248   // Quit immediately from the most recently entered run loop.  This method may
249   // only be used on the thread that called Run.
250   virtual void Quit() = 0;
251 
252   // Schedule a DoWork callback to happen reasonably soon.  Does nothing if a
253   // DoWork callback is already scheduled. Once this call is made, DoWork is
254   // guaranteed to be called repeatedly at least until it returns a
255   // non-immediate NextWorkInfo. This call can be expensive and callers should
256   // attempt not to invoke it again before a non-immediate NextWorkInfo was
257   // returned from DoWork(). Thread-safe (and callers should avoid holding a
258   // Lock at all cost while making this call as some platforms' priority
259   // boosting features have been observed to cause the caller to get descheduled
260   // : https://crbug.com/890978).
261   virtual void ScheduleWork() = 0;
262 
263   // Schedule a DoWork callback to happen at the specified time, cancelling any
264   // pending callback scheduled by this method. This method may only be used on
265   // the thread that called Run.
266   //
267   // It isn't necessary to call this during normal execution, as the pump wakes
268   // up as requested by the return value of DoWork().
269   // TODO(crbug.com/885371): Determine if this must be called to ensure that
270   // delayed tasks run when a message pump outside the control of Run is
271   // entered.
272   virtual void ScheduleDelayedWork(
273       const Delegate::NextWorkInfo& next_work_info) = 0;
274 
275   // Returns an adjusted |run_time| based on alignment policies of the pump.
276   virtual TimeTicks AdjustDelayedRunTime(TimeTicks earliest_time,
277                                          TimeTicks run_time,
278                                          TimeTicks latest_time);
279 
280   // Requests the pump to handle either the likely imminent creation (`true`) or
281   // destruction (`false`) of a native nested loop in which application tasks
282   // are desired to be run. The pump should override and return `true` if it
283   // supports this call and has scheduled work in response. The default
284   // implementation returns `false` and does nothing.
285   virtual bool HandleNestedNativeLoopWithApplicationTasks(
286       bool application_tasks_desired);
287 };
288 
289 }  // namespace base
290 
291 #endif  // BASE_MESSAGE_LOOP_MESSAGE_PUMP_H_
292