xref: /aosp_15_r20/external/cronet/base/message_loop/message_pump_win.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_WIN_H_
6 #define BASE_MESSAGE_LOOP_MESSAGE_PUMP_WIN_H_
7 
8 #include <windows.h>
9 
10 #include <atomic>
11 #include <memory>
12 #include <optional>
13 
14 #include "base/base_export.h"
15 #include "base/compiler_specific.h"
16 #include "base/location.h"
17 #include "base/memory/raw_ptr.h"
18 #include "base/message_loop/message_pump.h"
19 #include "base/observer_list.h"
20 #include "base/synchronization/waitable_event.h"
21 #include "base/threading/thread_checker.h"
22 #include "base/time/time.h"
23 #include "base/win/message_window.h"
24 #include "base/win/scoped_handle.h"
25 
26 namespace base {
27 
28 // MessagePumpWin serves as the base for specialized versions of the MessagePump
29 // for Windows. It provides basic functionality like handling of observers and
30 // controlling the lifetime of the message pump.
31 class BASE_EXPORT MessagePumpWin : public MessagePump {
32  public:
33   MessagePumpWin();
34   ~MessagePumpWin() override;
35 
36   // MessagePump methods:
37   void Run(Delegate* delegate) override;
38   void Quit() override;
39 
40   static void InitializeFeatures();
41 
42  protected:
43   struct RunState {
RunStateRunState44     explicit RunState(Delegate* delegate_in) : delegate(delegate_in) {}
45 
46     const raw_ptr<Delegate> delegate;
47 
48     // Used to flag that the current Run() invocation should return ASAP.
49     bool should_quit = false;
50 
51     // Set to true if this Run() is nested within another Run().
52     bool is_nested = false;
53   };
54 
55   virtual void DoRunLoop() = 0;
56 
57   // True iff:
58   //   * MessagePumpForUI: there's a kMsgDoWork message pending in the Windows
59   //     Message queue. i.e. when:
60   //      a. The pump is about to wakeup from idle and kUIPumpImprovementsWin
61   //         is not enabled.
62   //      b. The pump is about to enter a nested native loop and a
63   //         `ScopedAllowApplicationTasksInNativeNestedLoop` was instantiated to
64   //         allow application tasks to execute in that nested loop
65   //         (`ScopedAllowApplicationTasksInNativeNestedLoop` invokes
66   //         ScheduleWork()).
67   //      c. While in a native (nested) loop : HandleWorkMessage() =>
68   //         ProcessPumpReplacementMessage() invokes ScheduleWork() before
69   //         processing a native message to guarantee this pump will get another
70   //         time slice if it goes into native Windows code and enters a native
71   //         nested loop. This is different from (b.) because we're not yet
72   //         processing an application task at the current run level and
73   //         therefore are expected to keep pumping application tasks without
74   //         necessitating a `ScopedAllowApplicationTasksInNativeNestedLoop`.
75   //
76   //   * MessagePumpforIO: there's a dummy IO completion item with `this` as an
77   //     lpCompletionKey in the queue which is about to wakeup
78   //     WaitForIOCompletion(). MessagePumpForIO doesn't support nesting so
79   //     this is simpler than MessagePumpForUI.
80   //
81   // Note that this should not be used for memory ordering. It is accessed via
82   // `memory_order_relaxed` in all cases.
83   std::atomic_bool native_msg_scheduled_{false};
84 
85   raw_ptr<RunState> run_state_ = nullptr;
86 
87   THREAD_CHECKER(bound_thread_);
88 };
89 
90 //-----------------------------------------------------------------------------
91 // MessagePumpForUI extends MessagePumpWin with methods that are particular to a
92 // MessageLoop instantiated with TYPE_UI.
93 //
94 // MessagePumpForUI implements a "traditional" Windows message pump. It contains
95 // a nearly infinite loop that peeks out messages, and then dispatches them.
96 // Intermixed with those peeks are callouts to DoWork. When there are no
97 // events to be serviced, this pump goes into a wait state. In most cases, this
98 // message pump handles all processing.
99 //
100 // However, when a task, or windows event, invokes on the stack a native dialog
101 // box or such, that window typically provides a bare bones (native?) message
102 // pump.  That bare-bones message pump generally supports little more than a
103 // peek of the Windows message queue, followed by a dispatch of the peeked
104 // message.  MessageLoop extends that bare-bones message pump to also service
105 // Tasks, at the cost of some complexity.
106 //
107 // The basic structure of the extension (referred to as a sub-pump) is that a
108 // special message, kMsgHaveWork, is repeatedly injected into the Windows
109 // Message queue.  Each time the kMsgHaveWork message is peeked, checks are made
110 // for an extended set of events, including the availability of Tasks to run.
111 //
112 // After running a task, the special message kMsgHaveWork is again posted to the
113 // Windows Message queue, ensuring a future time slice for processing a future
114 // event.  To prevent flooding the Windows Message queue, care is taken to be
115 // sure that at most one kMsgHaveWork message is EVER pending in the Window's
116 // Message queue.
117 //
118 // There are a few additional complexities in this system where, when there are
119 // no Tasks to run, this otherwise infinite stream of messages which drives the
120 // sub-pump is halted.  The pump is automatically re-started when Tasks are
121 // queued.
122 //
123 // A second complexity is that the presence of this stream of posted tasks may
124 // prevent a bare-bones message pump from ever peeking a WM_PAINT or WM_TIMER.
125 // Such paint and timer events always give priority to a posted message, such as
126 // kMsgHaveWork messages.  As a result, care is taken to do some peeking in
127 // between the posting of each kMsgHaveWork message (i.e., after kMsgHaveWork is
128 // peeked, and before a replacement kMsgHaveWork is posted).
129 //
130 // NOTE: Although it may seem odd that messages are used to start and stop this
131 // flow (as opposed to signaling objects, etc.), it should be understood that
132 // the native message pump will *only* respond to messages.  As a result, it is
133 // an excellent choice.  It is also helpful that the starter messages that are
134 // placed in the queue when new task arrive also awakens DoRunLoop.
135 //
136 class BASE_EXPORT MessagePumpForUI : public MessagePumpWin {
137  public:
138   MessagePumpForUI();
139   ~MessagePumpForUI() override;
140 
141   // MessagePump methods:
142   void ScheduleWork() override;
143   void ScheduleDelayedWork(
144       const Delegate::NextWorkInfo& next_work_info) override;
145   bool HandleNestedNativeLoopWithApplicationTasks(
146       bool application_tasks_desired) override;
147 
148   // An observer interface to give the scheduler an opportunity to log
149   // information about MSGs before and after they are dispatched.
150   class BASE_EXPORT Observer {
151    public:
152     virtual void WillDispatchMSG(const MSG& msg) = 0;
153     virtual void DidDispatchMSG(const MSG& msg) = 0;
154   };
155 
156   void AddObserver(Observer* observer);
157   void RemoveObserver(Observer* obseerver);
158 
159  private:
160   bool MessageCallback(UINT message,
161                        WPARAM wparam,
162                        LPARAM lparam,
163                        LRESULT* result);
164   void DoRunLoop() override;
165   NOINLINE NOT_TAIL_CALLED void WaitForWork(
166       Delegate::NextWorkInfo next_work_info);
167   void HandleWorkMessage();
168   void HandleTimerMessage();
169   void ScheduleNativeTimer(Delegate::NextWorkInfo next_work_info);
170   void KillNativeTimer();
171   bool ProcessNextWindowsMessage();
172   bool ProcessMessageHelper(const MSG& msg);
173   bool ProcessPumpReplacementMessage();
174 
175   base::win::MessageWindow message_window_;
176 
177   // Non-nullopt if there's currently a native timer installed. If so, it
178   // indicates when the timer is set to fire and can be used to avoid setting
179   // redundant timers.
180   std::optional<TimeTicks> installed_native_timer_;
181 
182   // This is used to wake up the pump when the UIPumpImprovementsWin experiment
183   // is enabled.
184   WaitableEvent event_{WaitableEvent::ResetPolicy::AUTOMATIC};
185 
186   // This is set when HandleNestedNativeLoopWithApplicationTasks(true) was
187   // called (when a `ScopedAllowApplicationTasksInNativeNestedLoop` is
188   // instantiated).
189   //
190   // When running with `event_`, switches to pumping
191   // `kMsgHaveWork` MSGs when there are application tasks to be done during
192   // native runloops. In this state, ScheduleDelayedWork() will start a native
193   // timer.
194   //
195   // It is reset when:
196   //   - DoRunLoop() gets control back after ProcessNextWindowsMessage().
197   //   - HandleNestedNativeLoopWithApplicationTasks(false) is called.
198   bool in_nested_native_loop_with_application_tasks_ = false;
199 
200   enum class WakeupState {
201     kApplicationTask,
202     kNative,
203     kRunning,
204     kInactive,
205   };
206   // Used to keep track of what the pump knows about the state of its work
207   // sources at wakeup for the experiment 'UIPumpImprovementsWin'. Its value is
208   // `kInactive` at construction, but set to `kRunning` on entry to DoRunLoop().
209   WakeupState wakeup_state_ = WakeupState::kInactive;
210 
211   ObserverList<Observer>::Unchecked observers_;
212 };
213 
214 //-----------------------------------------------------------------------------
215 // MessagePumpForIO extends MessagePumpWin with methods that are particular to a
216 // MessageLoop instantiated with TYPE_IO. This version of MessagePump does not
217 // deal with Windows mesagges, and instead has a Run loop based on Completion
218 // Ports so it is better suited for IO operations.
219 //
220 class BASE_EXPORT MessagePumpForIO : public MessagePumpWin {
221  public:
222   struct BASE_EXPORT IOContext {
223     IOContext();
224     OVERLAPPED overlapped;
225   };
226 
227   // Clients interested in receiving OS notifications when asynchronous IO
228   // operations complete should implement this interface and register themselves
229   // with the message pump.
230   //
231   // Typical use #1:
232   //   class MyFile : public IOHandler {
233   //     MyFile() : IOHandler(FROM_HERE) {
234   //       ...
235   //       message_pump->RegisterIOHandler(file_, this);
236   //     }
237   //     // Plus some code to make sure that this destructor is not called
238   //     // while there are pending IO operations.
239   //     ~MyFile() {
240   //     }
241   //     virtual void OnIOCompleted(IOContext* context, DWORD bytes_transfered,
242   //                                DWORD error) {
243   //       ...
244   //       delete context;
245   //     }
246   //     void DoSomeIo() {
247   //       ...
248   //       IOContext* context = new IOContext;
249   //       ReadFile(file_, buffer, num_bytes, &read, &context);
250   //     }
251   //     HANDLE file_;
252   //   };
253   //
254   // Typical use #2:
255   // Same as the previous example, except that in order to deal with the
256   // requirement stated for the destructor, the class calls WaitForIOCompletion
257   // from the destructor to block until all IO finishes.
258   //     ~MyFile() {
259   //       while(pending_)
260   //         message_pump->WaitForIOCompletion(INFINITE, this);
261   //     }
262   //
263   class BASE_EXPORT IOHandler {
264    public:
265     explicit IOHandler(const Location& from_here);
266     virtual ~IOHandler();
267 
268     IOHandler(const IOHandler&) = delete;
269     IOHandler& operator=(const IOHandler&) = delete;
270 
271     // This will be called once the pending IO operation associated with
272     // |context| completes. |error| is the Win32 error code of the IO operation
273     // (ERROR_SUCCESS if there was no error). |bytes_transfered| will be zero
274     // on error.
275     virtual void OnIOCompleted(IOContext* context,
276                                DWORD bytes_transfered,
277                                DWORD error) = 0;
278 
io_handler_location()279     const Location& io_handler_location() { return io_handler_location_; }
280 
281    private:
282     const Location io_handler_location_;
283   };
284 
285   MessagePumpForIO();
286   ~MessagePumpForIO() override;
287 
288   // MessagePump methods:
289   void ScheduleWork() override;
290   void ScheduleDelayedWork(
291       const Delegate::NextWorkInfo& next_work_info) override;
292 
293   // Register the handler to be used when asynchronous IO for the given file
294   // completes. The registration persists as long as |file_handle| is valid, so
295   // |handler| must be valid as long as there is pending IO for the given file.
296   HRESULT RegisterIOHandler(HANDLE file_handle, IOHandler* handler);
297 
298   // Register the handler to be used to process job events. The registration
299   // persists as long as the job object is live, so |handler| must be valid
300   // until the job object is destroyed. Returns true if the registration
301   // succeeded, and false otherwise.
302   bool RegisterJobObject(HANDLE job_handle, IOHandler* handler);
303 
304  private:
305   struct IOItem {
306     raw_ptr<IOHandler> handler;
307     raw_ptr<IOContext> context;
308     DWORD bytes_transfered;
309     DWORD error;
310   };
311 
312   void DoRunLoop() override;
313   NOINLINE NOT_TAIL_CALLED void WaitForWork(
314       Delegate::NextWorkInfo next_work_info);
315   bool GetIOItem(DWORD timeout, IOItem* item);
316   bool ProcessInternalIOItem(const IOItem& item);
317   // Waits for the next IO completion for up to |timeout| milliseconds.
318   // Return true if any IO operation completed, and false if the timeout
319   // expired. If the completion port received any messages, the associated
320   // handlers will have been invoked before returning from this code.
321   bool WaitForIOCompletion(DWORD timeout);
322 
323   // The completion port associated with this thread.
324   win::ScopedHandle port_;
325 };
326 
327 }  // namespace base
328 
329 #endif  // BASE_MESSAGE_LOOP_MESSAGE_PUMP_WIN_H_
330