diff options
Diffstat (limited to 'ipc/glue/Endpoint.h')
-rw-r--r-- | ipc/glue/Endpoint.h | 239 |
1 files changed, 239 insertions, 0 deletions
diff --git a/ipc/glue/Endpoint.h b/ipc/glue/Endpoint.h new file mode 100644 index 0000000000..4a6a214d74 --- /dev/null +++ b/ipc/glue/Endpoint.h @@ -0,0 +1,239 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#ifndef IPC_GLUE_ENDPOINT_H_ +#define IPC_GLUE_ENDPOINT_H_ + +#include <utility> +#include "CrashAnnotations.h" +#include "base/process.h" +#include "base/process_util.h" +#include "mozilla/Assertions.h" +#include "mozilla/Maybe.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/ipc/MessageLink.h" +#include "mozilla/ipc/Transport.h" +#include "nsXULAppAPI.h" +#include "nscore.h" + +namespace IPC { +template <class P> +struct ParamTraits; +} + +namespace mozilla { +namespace ipc { + +struct PrivateIPDLInterface {}; + +/** + * An endpoint represents one end of a partially initialized IPDL channel. To + * set up a new top-level protocol: + * + * Endpoint<PFooParent> parentEp; + * Endpoint<PFooChild> childEp; + * nsresult rv; + * rv = PFoo::CreateEndpoints(parentPid, childPid, &parentEp, &childEp); + * + * You're required to pass in parentPid and childPid, which are the pids of the + * processes in which the parent and child endpoints will be used. + * + * Endpoints can be passed in IPDL messages or sent to other threads using + * PostTask. Once an Endpoint has arrived at its destination process and thread, + * you need to create the top-level actor and bind it to the endpoint: + * + * FooParent* parent = new FooParent(); + * bool rv1 = parentEp.Bind(parent, processActor); + * bool rv2 = parent->SendBar(...); + * + * (See Bind below for an explanation of processActor.) Once the actor is bound + * to the endpoint, it can send and receive messages. + */ +template <class PFooSide> +class Endpoint { + public: + typedef base::ProcessId ProcessId; + + Endpoint() + : mValid(false), + mMode(static_cast<mozilla::ipc::Transport::Mode>(0)), + mMyPid(0), + mOtherPid(0) {} + + Endpoint(const PrivateIPDLInterface&, mozilla::ipc::Transport::Mode aMode, + TransportDescriptor aTransport, ProcessId aMyPid, + ProcessId aOtherPid) + : mValid(true), + mMode(aMode), + mTransport(aTransport), + mMyPid(aMyPid), + mOtherPid(aOtherPid) {} + + Endpoint(Endpoint&& aOther) + : mValid(aOther.mValid), + mTransport(aOther.mTransport), + mMyPid(aOther.mMyPid), + mOtherPid(aOther.mOtherPid) { + if (aOther.mValid) { + mMode = aOther.mMode; + } + aOther.mValid = false; + } + + Endpoint& operator=(Endpoint&& aOther) { + mValid = aOther.mValid; + if (aOther.mValid) { + mMode = aOther.mMode; + } + mTransport = aOther.mTransport; + mMyPid = aOther.mMyPid; + mOtherPid = aOther.mOtherPid; + + aOther.mValid = false; + return *this; + } + + ~Endpoint() { + if (mValid) { + CloseDescriptor(mTransport); + } + } + + ProcessId OtherPid() const { return mOtherPid; } + + // This method binds aActor to this endpoint. After this call, the actor can + // be used to send and receive messages. The endpoint becomes invalid. + bool Bind(PFooSide* aActor) { + MOZ_RELEASE_ASSERT(mValid); + MOZ_RELEASE_ASSERT(mMyPid == base::GetCurrentProcId()); + + UniquePtr<Transport> transport = + mozilla::ipc::OpenDescriptor(mTransport, mMode); + if (!transport) { + return false; + } + if (!aActor->Open( + std::move(transport), mOtherPid, XRE_GetIOMessageLoop(), + mMode == Transport::MODE_SERVER ? ParentSide : ChildSide)) { + return false; + } + mValid = false; + return true; + } + + bool IsValid() const { return mValid; } + + private: + friend struct IPC::ParamTraits<Endpoint<PFooSide>>; + + Endpoint(const Endpoint&) = delete; + Endpoint& operator=(const Endpoint&) = delete; + + bool mValid; + mozilla::ipc::Transport::Mode mMode; + TransportDescriptor mTransport; + ProcessId mMyPid, mOtherPid; +}; + +#if defined(XP_MACOSX) +void AnnotateCrashReportWithErrno(CrashReporter::Annotation tag, int error); +#else +static inline void AnnotateCrashReportWithErrno(CrashReporter::Annotation tag, + int error) {} +#endif + +// This function is used internally to create a pair of Endpoints. See the +// comment above Endpoint for a description of how it might be used. +template <class PFooParent, class PFooChild> +nsresult CreateEndpoints(const PrivateIPDLInterface& aPrivate, + base::ProcessId aParentDestPid, + base::ProcessId aChildDestPid, + Endpoint<PFooParent>* aParentEndpoint, + Endpoint<PFooChild>* aChildEndpoint) { + MOZ_RELEASE_ASSERT(aParentDestPid); + MOZ_RELEASE_ASSERT(aChildDestPid); + + TransportDescriptor parentTransport, childTransport; + nsresult rv; + if (NS_FAILED(rv = CreateTransport(aParentDestPid, &parentTransport, + &childTransport))) { + AnnotateCrashReportWithErrno( + CrashReporter::Annotation::IpcCreateEndpointsNsresult, int(rv)); + return rv; + } + + *aParentEndpoint = + Endpoint<PFooParent>(aPrivate, mozilla::ipc::Transport::MODE_SERVER, + parentTransport, aParentDestPid, aChildDestPid); + + *aChildEndpoint = + Endpoint<PFooChild>(aPrivate, mozilla::ipc::Transport::MODE_CLIENT, + childTransport, aChildDestPid, aParentDestPid); + + return NS_OK; +} + +/** + * A managed endpoint represents one end of a partially initialized managed + * IPDL actor. It is used for situations where the usual IPDL Constructor + * methods do not give sufficient control over the construction of actors, such + * as when constructing actors within replies, or constructing multiple related + * actors simultaneously. + * + * FooParent* parent = new FooParent(); + * ManagedEndpoint<PFooChild> childEp = parentMgr->OpenPFooEndpoint(parent); + * + * ManagedEndpoints should be sent using IPDL messages or other mechanisms to + * the other side of the manager channel. Once the ManagedEndpoint has arrived + * at its destination, you can create the actor, and bind it to the endpoint. + * + * FooChild* child = new FooChild(); + * childMgr->BindPFooEndpoint(childEp, child); + * + * WARNING: If the remote side of an endpoint has not been bound before it + * begins to receive messages, an IPC routing error will occur, likely causing + * a browser crash. + */ +template <class PFooSide> +class ManagedEndpoint { + public: + ManagedEndpoint() : mId(0) {} + + ManagedEndpoint(const PrivateIPDLInterface&, int32_t aId) : mId(aId) {} + + ManagedEndpoint(ManagedEndpoint&& aOther) : mId(aOther.mId) { + aOther.mId = 0; + } + + ManagedEndpoint& operator=(ManagedEndpoint&& aOther) { + mId = aOther.mId; + aOther.mId = 0; + return *this; + } + + bool IsValid() const { return mId != 0; } + + Maybe<int32_t> ActorId() const { return IsValid() ? Some(mId) : Nothing(); } + + bool operator==(const ManagedEndpoint& _o) const { return mId == _o.mId; } + + private: + friend struct IPC::ParamTraits<ManagedEndpoint<PFooSide>>; + + ManagedEndpoint(const ManagedEndpoint&) = delete; + ManagedEndpoint& operator=(const ManagedEndpoint&) = delete; + + // The routing ID for the to-be-created endpoint. + int32_t mId; + + // XXX(nika): Might be nice to have other info for assertions? + // e.g. mManagerId, mManagerType, etc. +}; + +} // namespace ipc +} // namespace mozilla + +#endif // IPC_GLUE_ENDPOINT_H_ |