xref: /aosp_15_r20/external/cronet/ipc/ipc_mojo_bootstrap.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2014 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 IPC_IPC_MOJO_BOOTSTRAP_H_
6 #define IPC_IPC_MOJO_BOOTSTRAP_H_
7 
8 #include <stdint.h>
9 
10 #include <memory>
11 
12 #include "base/auto_reset.h"
13 #include "base/component_export.h"
14 #include "base/memory/ref_counted.h"
15 #include "base/task/single_thread_task_runner.h"
16 #include "build/build_config.h"
17 #include "ipc/ipc.mojom.h"
18 #include "ipc/ipc_channel.h"
19 #include "ipc/ipc_listener.h"
20 #include "mojo/public/cpp/bindings/associated_group.h"
21 #include "mojo/public/cpp/bindings/associated_remote.h"
22 #include "mojo/public/cpp/bindings/pending_associated_receiver.h"
23 #include "mojo/public/cpp/system/message_pipe.h"
24 
25 namespace IPC {
26 
27 class UrgentMessageObserver;
28 
29 // Incoming legacy IPCs have always been dispatched to one of two threads: the
30 // IO thread (when an installed MessageFilter handles the message), or the
31 // thread which owns the corresponding ChannelProxy receiving the message. There
32 // were no other places to route legacy IPC messages, so when a message arrived
33 // the legacy IPC system would run through its MessageFilters and if the message
34 // was still unhandled, it would be posted to the ChannelProxy thread for
35 // further processing.
36 //
37 // Mojo on the other hand allows for mutually associated endpoints (that is,
38 // endpoints which locally share the same message pipe) to span any number of
39 // threads while still guaranteeing that each endpoint on a given thread
40 // preserves FIFO order of messages dispatched there. This means that if a
41 // message arrives carrying a PendingAssociatedRemote/Receiver endpoint, and
42 // then another message arrives which targets that endpoint, the entire pipe
43 // will be blocked from dispatch until the endpoint is bound: otherwise we have
44 // no idea where to dispatch the message such that we can uphold the FIFO
45 // guarantee between the new endpoint and any other endpoints on the thread it
46 // ends up binding to.
47 //
48 // Channel-associated interfaces share a message pipe with the legacy IPC
49 // Channel, and in order to avoid nasty surprises during the migration process
50 // we decided to constrain how incoming Channel-associated endpoints could be
51 // bound: you must either bind them immediately as they arrive on the IO thread,
52 // or you must immediately post a task to the ChannelProxy thread to bind them.
53 // This allows all aforementioned FIFO guaratees to be upheld without ever
54 // stalling dispatch of legacy IPCs (particularly on the IO thread), because
55 // when we see a message targeting an unbound endpoint we can safely post it to
56 // the ChannelProxy's task runner before forging ahead to dispatch subsequent
57 // messages. No stalling.
58 //
59 // As there are some cases where a Channel-associated endpoint really wants to
60 // receive messages on a different TaskRunner, we want to allow that now. It's
61 // safe as long as the application can guarantee that the endpoint in question
62 // will be bound to a task runner *before* any messages are received for that
63 // endpoint.
64 //
65 // HOWEVER, it turns out that we cannot simply adhere to the application's
66 // wishes when an alternative TaskRunner is provided at binding time: over time
67 // we have accumulated application code which binds Channel-associated endpoints
68 // to task runners which -- while running tasks exclusively on the ChannelProxy
69 // thread -- are not the ChannelProxy's own task runner. Such code now
70 // implicitly relies on the behavior of Channel-associated interfaces always
71 // dispatching their messages to the ChannelProxy task runner. This is tracked
72 // by https://crbug.com/1209188.
73 //
74 // Finally, the point: if you really know you want to bind your endpoint to an
75 // alternative task runner and you can really guarantee that no messages may
76 // have already arrived for it on the IO thread, you can do the binding within
77 // the extent of a ScopedAllowOffSequenceChannelAssociatedBindings. This will
78 // flag the endpoint such that it honors your binding configuration, and its
79 // incoming messages will actually dispatch to the task runner you provide.
COMPONENT_EXPORT(IPC)80 class COMPONENT_EXPORT(IPC)
81     [[maybe_unused,
82       nodiscard]] ScopedAllowOffSequenceChannelAssociatedBindings {
83  public:
84   ScopedAllowOffSequenceChannelAssociatedBindings();
85   ~ScopedAllowOffSequenceChannelAssociatedBindings();
86 
87  private:
88   const base::AutoReset<bool> resetter_;
89 };
90 
91 // MojoBootstrap establishes a pair of associated interfaces between two
92 // processes in Chrome.
93 //
94 // Clients should implement MojoBootstrap::Delegate to get the associated pipes
95 // from MojoBootstrap object.
96 //
97 // This lives on IO thread other than Create(), which can be called from
98 // UI thread as Channel::Create() can be.
COMPONENT_EXPORT(IPC)99 class COMPONENT_EXPORT(IPC) MojoBootstrap {
100  public:
101   virtual ~MojoBootstrap() {}
102 
103   // Create the MojoBootstrap instance, using |handle| as the message pipe, in
104   // mode as specified by |mode|. The result is passed to |delegate|.
105   static std::unique_ptr<MojoBootstrap> Create(
106       mojo::ScopedMessagePipeHandle handle,
107       Channel::Mode mode,
108       const scoped_refptr<base::SingleThreadTaskRunner>& ipc_task_runner,
109       const scoped_refptr<base::SingleThreadTaskRunner>& proxy_task_runner);
110 
111   // Initialize the Channel pipe and interface endpoints. This performs all
112   // setup except actually starting to read messages off the pipe.
113   virtual void Connect(
114       mojo::PendingAssociatedRemote<mojom::Channel>* sender,
115       mojo::PendingAssociatedReceiver<mojom::Channel>* receiver) = 0;
116 
117   // Enable incoming messages to start being read off the pipe and routed to
118   // endpoints. Must not be called until the pending endpoints created by
119   // Connect() are actually bound somewhere.
120   virtual void StartReceiving() = 0;
121 
122   // Stop transmitting messages and start queueing them instead.
123   virtual void Pause() = 0;
124 
125   // Stop queuing new messages and start transmitting them instead.
126   virtual void Unpause() = 0;
127 
128   // Flush outgoing messages which were queued before Start().
129   virtual void Flush() = 0;
130 
131   virtual mojo::AssociatedGroup* GetAssociatedGroup() = 0;
132 
133   virtual void SetUrgentMessageObserver(UrgentMessageObserver* observer) = 0;
134 };
135 
136 }  // namespace IPC
137 
138 #endif  // IPC_IPC_MOJO_BOOTSTRAP_H_
139