Bug 1843532 - Refactor Statistics::computeMMU to make it easier to understand r=sfink
[gecko.git] / ipc / glue / Endpoint.h
blobd7eea94dfc6023a125a58e1d5bc8d4f827c00e06
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
7 #ifndef IPC_GLUE_ENDPOINT_H_
8 #define IPC_GLUE_ENDPOINT_H_
10 #include <utility>
11 #include "CrashAnnotations.h"
12 #include "base/process.h"
13 #include "base/process_util.h"
14 #include "mozilla/Assertions.h"
15 #include "mozilla/Maybe.h"
16 #include "mozilla/UniquePtr.h"
17 #include "mozilla/ipc/MessageLink.h"
18 #include "mozilla/ipc/ProtocolUtils.h"
19 #include "mozilla/ipc/NodeController.h"
20 #include "mozilla/ipc/ScopedPort.h"
21 #include "nsXULAppAPI.h"
22 #include "nscore.h"
24 namespace IPC {
25 template <class P>
26 struct ParamTraits;
29 namespace mozilla {
30 namespace ipc {
32 namespace endpoint_detail {
34 template <class T>
35 static auto ActorNeedsOtherPidHelper(int)
36 -> decltype(std::declval<T>().OtherPid(), std::true_type{});
37 template <class>
38 static auto ActorNeedsOtherPidHelper(long) -> std::false_type;
40 template <typename T>
41 constexpr bool ActorNeedsOtherPid =
42 decltype(ActorNeedsOtherPidHelper<T>(0))::value;
44 } // namespace endpoint_detail
46 struct PrivateIPDLInterface {};
48 class UntypedEndpoint {
49 public:
50 using ProcessId = base::ProcessId;
52 UntypedEndpoint() = default;
54 UntypedEndpoint(const PrivateIPDLInterface&, ScopedPort aPort,
55 const nsID& aMessageChannelId,
56 ProcessId aMyPid = base::kInvalidProcessId,
57 ProcessId aOtherPid = base::kInvalidProcessId)
58 : mPort(std::move(aPort)),
59 mMessageChannelId(aMessageChannelId),
60 mMyPid(aMyPid),
61 mOtherPid(aOtherPid) {}
63 UntypedEndpoint(const UntypedEndpoint&) = delete;
64 UntypedEndpoint(UntypedEndpoint&& aOther) = default;
66 UntypedEndpoint& operator=(const UntypedEndpoint&) = delete;
67 UntypedEndpoint& operator=(UntypedEndpoint&& aOther) = default;
69 // This method binds aActor to this endpoint. After this call, the actor can
70 // be used to send and receive messages. The endpoint becomes invalid.
72 // If specified, aEventTarget is the target the actor will be bound to, and
73 // must be on the current thread. Otherwise, GetCurrentSerialEventTarget() is
74 // used.
75 bool Bind(IToplevelProtocol* aActor,
76 nsISerialEventTarget* aEventTarget = nullptr) {
77 MOZ_RELEASE_ASSERT(IsValid());
78 MOZ_RELEASE_ASSERT(mMyPid == base::kInvalidProcessId ||
79 mMyPid == base::GetCurrentProcId());
80 MOZ_RELEASE_ASSERT(!aEventTarget || aEventTarget->IsOnCurrentThread());
81 return aActor->Open(std::move(mPort), mMessageChannelId, mOtherPid,
82 aEventTarget);
85 bool IsValid() const { return mPort.IsValid(); }
87 protected:
88 friend struct IPC::ParamTraits<UntypedEndpoint>;
90 ScopedPort mPort;
91 nsID mMessageChannelId{};
92 ProcessId mMyPid = base::kInvalidProcessId;
93 ProcessId mOtherPid = base::kInvalidProcessId;
96 /**
97 * An endpoint represents one end of a partially initialized IPDL channel. To
98 * set up a new top-level protocol:
100 * Endpoint<PFooParent> parentEp;
101 * Endpoint<PFooChild> childEp;
102 * nsresult rv;
103 * rv = PFoo::CreateEndpoints(&parentEp, &childEp);
105 * Endpoints can be passed in IPDL messages or sent to other threads using
106 * PostTask. Once an Endpoint has arrived at its destination process and thread,
107 * you need to create the top-level actor and bind it to the endpoint:
109 * FooParent* parent = new FooParent();
110 * bool rv1 = parentEp.Bind(parent, processActor);
111 * bool rv2 = parent->SendBar(...);
113 * (See Bind below for an explanation of processActor.) Once the actor is bound
114 * to the endpoint, it can send and receive messages.
116 * If creating endpoints for a [NeedsOtherPid] actor, you're required to also
117 * pass in parentPid and childPid, which are the pids of the processes in which
118 * the parent and child endpoints will be used.
120 template <class PFooSide>
121 class Endpoint final : public UntypedEndpoint {
122 public:
123 using UntypedEndpoint::IsValid;
124 using UntypedEndpoint::UntypedEndpoint;
126 base::ProcessId OtherPid() const {
127 static_assert(
128 endpoint_detail::ActorNeedsOtherPid<PFooSide>,
129 "OtherPid may only be called on Endpoints for actors which are "
130 "[NeedsOtherPid]");
131 MOZ_RELEASE_ASSERT(mOtherPid != base::kInvalidProcessId);
132 return mOtherPid;
135 // This method binds aActor to this endpoint. After this call, the actor can
136 // be used to send and receive messages. The endpoint becomes invalid.
138 // If specified, aEventTarget is the target the actor will be bound to, and
139 // must be on the current thread. Otherwise, GetCurrentSerialEventTarget() is
140 // used.
141 bool Bind(PFooSide* aActor, nsISerialEventTarget* aEventTarget = nullptr) {
142 return UntypedEndpoint::Bind(aActor, aEventTarget);
146 #if defined(XP_MACOSX)
147 void AnnotateCrashReportWithErrno(CrashReporter::Annotation tag, int error);
148 #else
149 inline void AnnotateCrashReportWithErrno(CrashReporter::Annotation tag,
150 int error) {}
151 #endif
153 // This function is used internally to create a pair of Endpoints. See the
154 // comment above Endpoint for a description of how it might be used.
155 template <class PFooParent, class PFooChild>
156 nsresult CreateEndpoints(const PrivateIPDLInterface& aPrivate,
157 Endpoint<PFooParent>* aParentEndpoint,
158 Endpoint<PFooChild>* aChildEndpoint) {
159 static_assert(
160 !endpoint_detail::ActorNeedsOtherPid<PFooParent> &&
161 !endpoint_detail::ActorNeedsOtherPid<PFooChild>,
162 "Pids are required when creating endpoints for [NeedsOtherPid] actors");
164 auto [parentPort, childPort] =
165 NodeController::GetSingleton()->CreatePortPair();
166 nsID channelId = nsID::GenerateUUID();
167 *aParentEndpoint =
168 Endpoint<PFooParent>(aPrivate, std::move(parentPort), channelId);
169 *aChildEndpoint =
170 Endpoint<PFooChild>(aPrivate, std::move(childPort), channelId);
171 return NS_OK;
174 template <class PFooParent, class PFooChild>
175 nsresult CreateEndpoints(const PrivateIPDLInterface& aPrivate,
176 base::ProcessId aParentDestPid,
177 base::ProcessId aChildDestPid,
178 Endpoint<PFooParent>* aParentEndpoint,
179 Endpoint<PFooChild>* aChildEndpoint) {
180 MOZ_RELEASE_ASSERT(aParentDestPid != base::kInvalidProcessId);
181 MOZ_RELEASE_ASSERT(aChildDestPid != base::kInvalidProcessId);
183 auto [parentPort, childPort] =
184 NodeController::GetSingleton()->CreatePortPair();
185 nsID channelId = nsID::GenerateUUID();
186 *aParentEndpoint =
187 Endpoint<PFooParent>(aPrivate, std::move(parentPort), channelId,
188 aParentDestPid, aChildDestPid);
189 *aChildEndpoint = Endpoint<PFooChild>(
190 aPrivate, std::move(childPort), channelId, aChildDestPid, aParentDestPid);
191 return NS_OK;
194 class UntypedManagedEndpoint {
195 public:
196 bool IsValid() const { return mInner.isSome(); }
198 UntypedManagedEndpoint(const UntypedManagedEndpoint&) = delete;
199 UntypedManagedEndpoint& operator=(const UntypedManagedEndpoint&) = delete;
201 protected:
202 UntypedManagedEndpoint() = default;
203 explicit UntypedManagedEndpoint(IProtocol* aActor);
205 UntypedManagedEndpoint(UntypedManagedEndpoint&& aOther) noexcept
206 : mInner(std::move(aOther.mInner)) {
207 aOther.mInner = Nothing();
209 UntypedManagedEndpoint& operator=(UntypedManagedEndpoint&& aOther) noexcept {
210 this->~UntypedManagedEndpoint();
211 new (this) UntypedManagedEndpoint(std::move(aOther));
212 return *this;
215 ~UntypedManagedEndpoint() noexcept;
217 bool BindCommon(IProtocol* aActor, IProtocol* aManager);
219 private:
220 friend struct IPDLParamTraits<UntypedManagedEndpoint>;
222 struct Inner {
223 // Pointers to the toplevel actor which will manage this connection. When
224 // created, only `mOtherSide` will be set, and will reference the
225 // toplevel actor which the other side is managed by. After being sent over
226 // IPC, only `mToplevel` will be set, and will be the toplevel actor for the
227 // channel which received the IPC message.
228 RefPtr<WeakActorLifecycleProxy> mOtherSide;
229 RefPtr<WeakActorLifecycleProxy> mToplevel;
231 int32_t mId = 0;
232 ProtocolId mType = LastMsgIndex;
233 int32_t mManagerId = 0;
234 ProtocolId mManagerType = LastMsgIndex;
236 Maybe<Inner> mInner;
240 * A managed endpoint represents one end of a partially initialized managed
241 * IPDL actor. It is used for situations where the usual IPDL Constructor
242 * methods do not give sufficient control over the construction of actors, such
243 * as when constructing actors within replies, or constructing multiple related
244 * actors simultaneously.
246 * FooParent* parent = new FooParent();
247 * ManagedEndpoint<PFooChild> childEp = parentMgr->OpenPFooEndpoint(parent);
249 * ManagedEndpoints should be sent using IPDL messages or other mechanisms to
250 * the other side of the manager channel. Once the ManagedEndpoint has arrived
251 * at its destination, you can create the actor, and bind it to the endpoint.
253 * FooChild* child = new FooChild();
254 * childMgr->BindPFooEndpoint(childEp, child);
256 * WARNING: If the remote side of an endpoint has not been bound before it
257 * begins to receive messages, an IPC routing error will occur, likely causing
258 * a browser crash.
260 template <class PFooSide>
261 class ManagedEndpoint : public UntypedManagedEndpoint {
262 public:
263 ManagedEndpoint() = default;
264 ManagedEndpoint(ManagedEndpoint&&) noexcept = default;
265 ManagedEndpoint& operator=(ManagedEndpoint&&) noexcept = default;
267 ManagedEndpoint(const PrivateIPDLInterface&, IProtocol* aActor)
268 : UntypedManagedEndpoint(aActor) {}
270 bool Bind(const PrivateIPDLInterface&, PFooSide* aActor, IProtocol* aManager,
271 ManagedContainer<PFooSide>& aContainer) {
272 if (!BindCommon(aActor, aManager)) {
273 return false;
275 aContainer.Insert(aActor);
276 return true;
279 // Only invalid ManagedEndpoints can be equal, as valid endpoints are unique.
280 bool operator==(const ManagedEndpoint& _o) const {
281 return !IsValid() && !_o.IsValid();
285 } // namespace ipc
286 } // namespace mozilla
288 #endif // IPC_GLUE_ENDPOINT_H_