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