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 mozilla_ipc_ProtocolUtils_h
8 #define mozilla_ipc_ProtocolUtils_h 1
10 #include "base/process.h"
11 #include "base/process_util.h"
12 #include "chrome/common/ipc_message_utils.h"
16 #include "IPCMessageStart.h"
17 #include "mozilla/AlreadyAddRefed.h"
18 #include "mozilla/Attributes.h"
19 #include "mozilla/ipc/ByteBuf.h"
20 #include "mozilla/ipc/FileDescriptor.h"
21 #include "mozilla/ipc/MessageChannel.h"
22 #include "mozilla/ipc/Shmem.h"
23 #include "mozilla/ipc/Transport.h"
24 #include "mozilla/ipc/MessageLink.h"
25 #include "mozilla/LinkedList.h"
26 #include "mozilla/Maybe.h"
27 #include "mozilla/MozPromise.h"
28 #include "mozilla/Mutex.h"
29 #include "mozilla/NotNull.h"
30 #include "mozilla/Scoped.h"
31 #include "mozilla/UniquePtr.h"
32 #include "MainThreadUtils.h"
34 #include "nsDataHashtable.h"
35 #include "nsHashKeys.h"
37 #if defined(ANDROID) && defined(DEBUG)
38 # include <android/log.h>
46 // WARNING: this takes into account the private, special-message-type
47 // enum in ipc_channel.h. They need to be kept in sync.
49 // XXX the max message ID is actually kuint32max now ... when this
50 // changed, the assumptions of the special message IDs changed in that
51 // they're not carving out messages from likely-unallocated space, but
52 // rather carving out messages from the end of space allocated to
53 // protocol 0. Oops! We can get away with this until protocol 0
54 // starts approaching its 65,536th message.
56 IMPENDING_SHUTDOWN_MESSAGE_TYPE
= kuint16max
- 9,
57 BUILD_IDS_MATCH_MESSAGE_TYPE
= kuint16max
- 8,
58 BUILD_ID_MESSAGE_TYPE
= kuint16max
- 7, // unused
59 CHANNEL_OPENED_MESSAGE_TYPE
= kuint16max
- 6,
60 SHMEM_DESTROYED_MESSAGE_TYPE
= kuint16max
- 5,
61 SHMEM_CREATED_MESSAGE_TYPE
= kuint16max
- 4,
62 GOODBYE_MESSAGE_TYPE
= kuint16max
- 3,
63 CANCEL_MESSAGE_TYPE
= kuint16max
- 2,
65 // kuint16max - 1 is used by ipc_channel.h.
70 class nsISerialEventTarget
;
86 class ProtocolFuzzerHelper
;
92 const base::ProcessHandle kInvalidProcessHandle
= INVALID_HANDLE_VALUE
;
94 // In theory, on Windows, this is a valid process ID, but in practice they are
95 // currently divisible by four. Process IDs share the kernel handle allocation
96 // code and they are guaranteed to be divisible by four.
97 // As this could change for process IDs we shouldn't generally rely on this
98 // property, however even if that were to change, it seems safe to rely on this
99 // particular value never being used.
100 const base::ProcessId kInvalidProcessId
= kuint32max
;
102 const base::ProcessHandle kInvalidProcessHandle
= -1;
103 const base::ProcessId kInvalidProcessId
= -1;
106 // Scoped base::ProcessHandle to ensure base::CloseProcessHandle is called.
107 struct ScopedProcessHandleTraits
{
108 typedef base::ProcessHandle type
;
110 static type
empty() { return kInvalidProcessHandle
; }
112 static void release(type aProcessHandle
) {
113 if (aProcessHandle
&& aProcessHandle
!= kInvalidProcessHandle
) {
114 base::CloseProcessHandle(aProcessHandle
);
118 typedef mozilla::Scoped
<ScopedProcessHandleTraits
> ScopedProcessHandle
;
120 class ProtocolFdMapping
;
121 class ProtocolCloneContext
;
123 // Used to pass references to protocol actors across the wire.
124 // Actors created on the parent-side have a positive ID, and actors
125 // allocated on the child side have a negative ID.
130 // What happens if Interrupt calls race?
131 enum RacyInterruptPolicy
{ RIPError
, RIPChildWins
, RIPParentWins
};
133 enum class LinkStatus
: uint8_t {
134 // The actor has not established a link yet, or the actor is no longer in use
135 // by IPC, and its 'Dealloc' method has been called or is being called.
137 // NOTE: This state is used instead of an explicit `Freed` state when IPC no
138 // longer holds references to the current actor as we currently re-open
139 // existing actors. Once we fix these poorly behaved actors, this loopback
140 // state can be split to have the final state not be the same as the initial
144 // A live link is connected to the other side of this actor.
147 // The link has begun being destroyed. Messages may still be received, but
148 // cannot be sent. (exception: sync/intr replies may be sent while Doomed).
151 // The link has been destroyed, and messages will no longer be sent or
156 typedef IPCMessageStart ProtocolId
;
158 // Generated by IPDL compiler
159 const char* ProtocolIdToName(IPCMessageStart aId
);
161 class IToplevelProtocol
;
162 class ActorLifecycleProxy
;
164 class IProtocol
: public HasResultCodes
{
166 enum ActorDestroyReason
{
174 typedef base::ProcessId ProcessId
;
175 typedef IPC::Message Message
;
176 typedef IPC::MessageInfo MessageInfo
;
178 IProtocol(ProtocolId aProtoId
, Side aSide
)
180 mProtocolId(aProtoId
),
182 mLinkStatus(LinkStatus::Inactive
),
183 mLifecycleProxy(nullptr),
185 mToplevel(nullptr) {}
187 IToplevelProtocol
* ToplevelProtocol() { return mToplevel
; }
189 // The following methods either directly forward to the toplevel protocol, or
190 // almost directly do.
191 int32_t Register(IProtocol
* aRouted
);
192 int32_t RegisterID(IProtocol
* aRouted
, int32_t aId
);
193 IProtocol
* Lookup(int32_t aId
);
194 void Unregister(int32_t aId
);
196 Shmem::SharedMemory
* CreateSharedMemory(size_t aSize
,
197 SharedMemory::SharedMemoryType aType
,
198 bool aUnsafe
, int32_t* aId
);
199 Shmem::SharedMemory
* LookupSharedMemory(int32_t aId
);
200 bool IsTrackingSharedMemory(Shmem::SharedMemory
* aSegment
);
201 bool DestroySharedMemory(Shmem
& aShmem
);
203 MessageChannel
* GetIPCChannel();
204 const MessageChannel
* GetIPCChannel() const;
206 // Sets an event target to which all messages for aActor will be
207 // dispatched. This method must be called before right before the SendPFoo
208 // message for aActor is sent. And SendPFoo *must* be called if
209 // SetEventTargetForActor is called. The receiver when calling
210 // SetEventTargetForActor must be the actor that will be the manager for
212 void SetEventTargetForActor(IProtocol
* aActor
,
213 nsISerialEventTarget
* aEventTarget
);
215 // Replace the event target for the messages of aActor. There must not be
216 // any messages of aActor in the task queue, or we might run into some
217 // unexpected behavior.
218 void ReplaceEventTargetForActor(IProtocol
* aActor
,
219 nsISerialEventTarget
* aEventTarget
);
221 nsISerialEventTarget
* GetActorEventTarget();
222 already_AddRefed
<nsISerialEventTarget
> GetActorEventTarget(IProtocol
* aActor
);
224 ProcessId
OtherPid() const;
226 // Actor lifecycle and other properties.
227 ProtocolId
GetProtocolId() const { return mProtocolId
; }
228 const char* GetProtocolName() const { return ProtocolIdToName(mProtocolId
); }
230 int32_t Id() const { return mId
; }
231 IProtocol
* Manager() const { return mManager
; }
233 ActorLifecycleProxy
* GetLifecycleProxy() { return mLifecycleProxy
; }
235 Side
GetSide() const { return mSide
; }
236 bool CanSend() const { return mLinkStatus
== LinkStatus::Connected
; }
237 bool CanRecv() const {
238 return mLinkStatus
== LinkStatus::Connected
||
239 mLinkStatus
== LinkStatus::Doomed
;
242 // Remove or deallocate a managee given its type.
243 virtual void RemoveManagee(int32_t, IProtocol
*) = 0;
244 virtual void DeallocManagee(int32_t, IProtocol
*) = 0;
246 Maybe
<IProtocol
*> ReadActor(const IPC::Message
* aMessage
,
247 PickleIterator
* aIter
, bool aNullable
,
248 const char* aActorDescription
,
249 int32_t aProtocolTypeId
);
251 virtual Result
OnMessageReceived(const Message
& aMessage
) = 0;
252 virtual Result
OnMessageReceived(const Message
& aMessage
,
253 Message
*& aReply
) = 0;
254 virtual Result
OnCallReceived(const Message
& aMessage
, Message
*& aReply
) = 0;
255 bool AllocShmem(size_t aSize
, Shmem::SharedMemory::SharedMemoryType aType
,
257 bool AllocUnsafeShmem(size_t aSize
,
258 Shmem::SharedMemory::SharedMemoryType aType
,
260 bool DeallocShmem(Shmem
& aMem
);
262 void FatalError(const char* const aErrorMsg
) const;
263 virtual void HandleFatalError(const char* aErrorMsg
) const;
266 virtual ~IProtocol();
268 friend class IToplevelProtocol
;
269 friend class ActorLifecycleProxy
;
271 void SetId(int32_t aId
);
273 // We have separate functions because the accessibility code manually
275 void SetManager(IProtocol
* aManager
);
277 // Sets the manager for the protocol and registers the protocol with
278 // its manager, setting up channels for the protocol as well. Not
279 // for use outside of IPDL.
280 void SetManagerAndRegister(IProtocol
* aManager
);
281 void SetManagerAndRegister(IProtocol
* aManager
, int32_t aId
);
283 // Helpers for calling `Send` on our underlying IPC channel.
284 bool ChannelSend(IPC::Message
* aMsg
);
285 bool ChannelSend(IPC::Message
* aMsg
, IPC::Message
* aReply
);
286 bool ChannelCall(IPC::Message
* aMsg
, IPC::Message
* aReply
);
287 template <typename Value
>
288 void ChannelSend(IPC::Message
* aMsg
, ResolveCallback
<Value
>&& aResolve
,
289 RejectCallback
&& aReject
) {
290 UniquePtr
<IPC::Message
> msg(aMsg
);
292 GetIPCChannel()->Send(std::move(msg
), this, std::move(aResolve
),
295 NS_WARNING("IPC message discarded: actor cannot send");
296 aReject(ResponseRejectReason::SendError
);
300 // Collect all actors managed by this object in an array. To make this safer
301 // to iterate, `ActorLifecycleProxy` references are returned rather than raw
303 virtual void AllManagedActors(
304 nsTArray
<RefPtr
<ActorLifecycleProxy
>>& aActors
) const = 0;
306 // Internal method called when the actor becomes connected.
307 void ActorConnected();
309 // Called immediately before setting the actor state to doomed, and triggering
310 // async actor destruction. Messages may be sent from this callback, but no
312 // FIXME(nika): This is currently unused!
313 virtual void ActorDoom() {}
316 // Called when the actor has been destroyed due to an error, a __delete__
317 // message, or a __doom__ reply.
318 virtual void ActorDestroy(ActorDestroyReason aWhy
) {}
319 void DestroySubtree(ActorDestroyReason aWhy
);
321 // Called when IPC has acquired its first reference to the actor. This method
322 // may take references which will later be freed by `ActorDealloc`.
323 virtual void ActorAlloc() {}
325 // Called when IPC has released its final reference to the actor. It will call
326 // the dealloc method, causing the actor to be actually freed.
328 // The actor has been freed after this method returns.
329 virtual void ActorDealloc() {
331 Manager()->DeallocManagee(mProtocolId
, this);
335 static const int32_t kNullActorId
= 0;
336 static const int32_t kFreedActorId
= 1;
340 ProtocolId mProtocolId
;
342 LinkStatus mLinkStatus
;
343 ActorLifecycleProxy
* mLifecycleProxy
;
345 IToplevelProtocol
* mToplevel
;
348 #define IPC_OK() mozilla::ipc::IPCResult::Ok()
349 #define IPC_FAIL(actor, why) \
350 mozilla::ipc::IPCResult::Fail(WrapNotNull(actor), __func__, (why))
351 #define IPC_FAIL_NO_REASON(actor) \
352 mozilla::ipc::IPCResult::Fail(WrapNotNull(actor), __func__)
355 * All message deserializer and message handler should return this
356 * type via above macros. We use a less generic name here to avoid
357 * conflict with mozilla::Result because we have quite a few using
358 * namespace mozilla::ipc; in the code base.
362 static IPCResult
Ok() { return IPCResult(true); }
363 static IPCResult
Fail(NotNull
<IProtocol
*> aActor
, const char* aWhere
,
364 const char* aWhy
= "");
365 MOZ_IMPLICIT
operator bool() const { return mSuccess
; }
368 explicit IPCResult(bool aResult
) : mSuccess(aResult
) {}
372 template <class PFooSide
>
376 * All top-level protocols should inherit this class.
378 * IToplevelProtocol tracks all top-level protocol actors created from
379 * this protocol actor.
381 class IToplevelProtocol
: public IProtocol
{
383 friend class mozilla::ipc::ProtocolFuzzerHelper
;
386 template <class PFooSide
>
387 friend class Endpoint
;
390 explicit IToplevelProtocol(const char* aName
, ProtocolId aProtoId
,
392 ~IToplevelProtocol() = default;
395 // Shadow methods on IProtocol which are implemented directly on toplevel
397 int32_t Register(IProtocol
* aRouted
);
398 int32_t RegisterID(IProtocol
* aRouted
, int32_t aId
);
399 IProtocol
* Lookup(int32_t aId
);
400 void Unregister(int32_t aId
);
402 Shmem::SharedMemory
* CreateSharedMemory(size_t aSize
,
403 SharedMemory::SharedMemoryType aType
,
404 bool aUnsafe
, int32_t* aId
);
405 Shmem::SharedMemory
* LookupSharedMemory(int32_t aId
);
406 bool IsTrackingSharedMemory(Shmem::SharedMemory
* aSegment
);
407 bool DestroySharedMemory(Shmem
& aShmem
);
409 MessageChannel
* GetIPCChannel() { return &mChannel
; }
410 const MessageChannel
* GetIPCChannel() const { return &mChannel
; }
412 // NOTE: The target actor's Manager must already be set.
413 void SetEventTargetForActorInternal(IProtocol
* aActor
,
414 nsISerialEventTarget
* aEventTarget
);
415 void ReplaceEventTargetForActor(IProtocol
* aActor
,
416 nsISerialEventTarget
* aEventTarget
);
417 nsISerialEventTarget
* GetActorEventTarget();
418 already_AddRefed
<nsISerialEventTarget
> GetActorEventTarget(IProtocol
* aActor
);
420 ProcessId
OtherPid() const;
421 void SetOtherProcessId(base::ProcessId aOtherPid
);
423 virtual void OnChannelClose() = 0;
424 virtual void OnChannelError() = 0;
425 virtual void ProcessingError(Result aError
, const char* aMsgName
) {}
426 virtual void OnChannelConnected(int32_t peer_pid
) {}
428 bool Open(UniquePtr
<Transport
> aTransport
, base::ProcessId aOtherPid
,
429 MessageLoop
* aThread
= nullptr,
430 mozilla::ipc::Side aSide
= mozilla::ipc::UnknownSide
);
432 bool Open(MessageChannel
* aChannel
, nsISerialEventTarget
* aEventTarget
,
433 mozilla::ipc::Side aSide
= mozilla::ipc::UnknownSide
);
435 // Open a toplevel actor such that both ends of the actor's channel are on
436 // the same thread. This method should be called on the thread to perform
439 // WARNING: Attempting to send a sync or intr message on the same thread
441 bool OpenOnSameThread(MessageChannel
* aChannel
,
442 mozilla::ipc::Side aSide
= mozilla::ipc::UnknownSide
);
445 * This sends a special message that is processed on the IO thread, so that
446 * other actors can know that the process will soon shutdown.
448 void NotifyImpendingShutdown();
452 void SetReplyTimeoutMs(int32_t aTimeoutMs
);
454 void DeallocShmems();
455 bool ShmemCreated(const Message
& aMsg
);
456 bool ShmemDestroyed(const Message
& aMsg
);
458 virtual bool ShouldContinueFromReplyTimeout() { return false; }
460 // WARNING: This function is called with the MessageChannel monitor held.
461 virtual void IntentionalCrash() { MOZ_CRASH("Intentional IPDL crash"); }
463 // The code here is only useful for fuzzing. It should not be used for any
466 // Returns true if we should simulate a timeout.
467 // WARNING: This is a testing-only function that is called with the
468 // MessageChannel monitor held. Don't do anything fancy here or we could
470 virtual bool ArtificialTimeout() { return false; }
472 // Returns true if we want to cause the worker thread to sleep with the
474 virtual bool NeedArtificialSleep() { return false; }
476 // This function should be implemented to sleep for some amount of time on
477 // the worker thread. Will only be called if NeedArtificialSleep() returns
479 virtual void ArtificialSleep() {}
481 bool ArtificialTimeout() { return false; }
482 bool NeedArtificialSleep() { return false; }
483 void ArtificialSleep() {}
486 virtual void EnteredCxxStack() {}
487 virtual void ExitedCxxStack() {}
488 virtual void EnteredCall() {}
489 virtual void ExitedCall() {}
491 bool IsOnCxxStack() const;
493 virtual RacyInterruptPolicy
MediateInterruptRace(const MessageInfo
& parent
,
494 const MessageInfo
& child
) {
499 * Return true if windows messages can be handled while waiting for a reply
500 * to a sync IPDL message.
502 virtual bool HandleWindowsMessages(const Message
& aMsg
) const { return true; }
504 virtual void OnEnteredSyncSend() {}
505 virtual void OnExitedSyncSend() {}
507 virtual void ProcessRemoteNativeEventsInInterruptCall() {}
509 virtual void OnChannelReceivedMessage(const Message
& aMsg
) {}
511 void OnIPCChannelOpened() { ActorConnected(); }
513 already_AddRefed
<nsISerialEventTarget
> GetMessageEventTarget(
514 const Message
& aMsg
);
517 base::ProcessId
OtherPidMaybeInvalid() const { return mOtherPid
; }
522 using IDMap
= nsDataHashtable
<nsUint32HashKey
, T
>;
524 base::ProcessId mOtherPid
;
527 // Used to be on mState
528 int32_t mLastLocalId
;
529 IDMap
<IProtocol
*> mActorMap
;
530 IDMap
<Shmem::SharedMemory
*> mShmemMap
;
532 // XXX: We no longer need mEventTargetMap for Quantum DOM, so it may be
533 // worthwhile to remove it before people start depending on it for other weird
535 Mutex mEventTargetMutex
;
536 IDMap
<nsCOMPtr
<nsISerialEventTarget
>> mEventTargetMap
;
538 MessageChannel mChannel
;
541 class IShmemAllocator
{
543 virtual bool AllocShmem(size_t aSize
,
544 mozilla::ipc::SharedMemory::SharedMemoryType aShmType
,
545 mozilla::ipc::Shmem
* aShmem
) = 0;
546 virtual bool AllocUnsafeShmem(
547 size_t aSize
, mozilla::ipc::SharedMemory::SharedMemoryType aShmType
,
548 mozilla::ipc::Shmem
* aShmem
) = 0;
549 virtual bool DeallocShmem(mozilla::ipc::Shmem
& aShmem
) = 0;
552 #define FORWARD_SHMEM_ALLOCATOR_TO(aImplClass) \
553 virtual bool AllocShmem( \
554 size_t aSize, mozilla::ipc::SharedMemory::SharedMemoryType aShmType, \
555 mozilla::ipc::Shmem* aShmem) override { \
556 return aImplClass::AllocShmem(aSize, aShmType, aShmem); \
558 virtual bool AllocUnsafeShmem( \
559 size_t aSize, mozilla::ipc::SharedMemory::SharedMemoryType aShmType, \
560 mozilla::ipc::Shmem* aShmem) override { \
561 return aImplClass::AllocUnsafeShmem(aSize, aShmType, aShmem); \
563 virtual bool DeallocShmem(mozilla::ipc::Shmem& aShmem) override { \
564 return aImplClass::DeallocShmem(aShmem); \
567 inline bool LoggingEnabled() {
568 #if defined(DEBUG) || defined(FUZZING)
569 return !!PR_GetEnv("MOZ_IPC_MESSAGE_LOG");
575 #if defined(DEBUG) || defined(FUZZING)
576 bool LoggingEnabledFor(const char* aTopLevelProtocol
, const char* aFilter
);
579 inline bool LoggingEnabledFor(const char* aTopLevelProtocol
) {
580 #if defined(DEBUG) || defined(FUZZING)
581 return LoggingEnabledFor(aTopLevelProtocol
, PR_GetEnv("MOZ_IPC_MESSAGE_LOG"));
587 MOZ_NEVER_INLINE
void LogMessageForProtocol(const char* aTopLevelProtocol
,
588 base::ProcessId aOtherPid
,
589 const char* aContextDescription
,
591 MessageDirection aDirection
);
593 MOZ_NEVER_INLINE
void ProtocolErrorBreakpoint(const char* aMsg
);
595 // The code generator calls this function for errors which come from the
596 // methods of protocols. Doing this saves codesize by making the error
597 // cases significantly smaller.
598 MOZ_NEVER_INLINE
void FatalError(const char* aMsg
, bool aIsParent
);
600 // The code generator calls this function for errors which are not
601 // protocol-specific: errors in generated struct methods or errors in
602 // transition functions, for instance. Doing this saves codesize by
603 // by making the error cases significantly smaller.
604 MOZ_NEVER_INLINE
void LogicError(const char* aMsg
);
606 MOZ_NEVER_INLINE
void ActorIdReadError(const char* aActorDescription
);
608 MOZ_NEVER_INLINE
void BadActorIdError(const char* aActorDescription
);
610 MOZ_NEVER_INLINE
void ActorLookupError(const char* aActorDescription
);
612 MOZ_NEVER_INLINE
void MismatchedActorTypeError(const char* aActorDescription
);
614 MOZ_NEVER_INLINE
void UnionTypeReadError(const char* aUnionName
);
616 MOZ_NEVER_INLINE
void ArrayLengthReadError(const char* aElementName
);
618 MOZ_NEVER_INLINE
void SentinelReadError(const char* aElementName
);
620 struct PrivateIPDLInterface
{};
623 // This is a restricted version of Windows' DuplicateHandle() function
624 // that works inside the sandbox and can send handles but not retrieve
625 // them. Unlike DuplicateHandle(), it takes a process ID rather than
626 // a process handle. It returns true on success, false otherwise.
627 bool DuplicateHandle(HANDLE aSourceHandle
, DWORD aTargetProcessId
,
628 HANDLE
* aTargetHandle
, DWORD aDesiredAccess
,
633 * Annotate the crash reporter with the error code from the most recent system
634 * call. Returns the system error.
636 void AnnotateSystemError();
639 * An endpoint represents one end of a partially initialized IPDL channel. To
640 * set up a new top-level protocol:
642 * Endpoint<PFooParent> parentEp;
643 * Endpoint<PFooChild> childEp;
645 * rv = PFoo::CreateEndpoints(parentPid, childPid, &parentEp, &childEp);
647 * You're required to pass in parentPid and childPid, which are the pids of the
648 * processes in which the parent and child endpoints will be used.
650 * Endpoints can be passed in IPDL messages or sent to other threads using
651 * PostTask. Once an Endpoint has arrived at its destination process and thread,
652 * you need to create the top-level actor and bind it to the endpoint:
654 * FooParent* parent = new FooParent();
655 * bool rv1 = parentEp.Bind(parent, processActor);
656 * bool rv2 = parent->SendBar(...);
658 * (See Bind below for an explanation of processActor.) Once the actor is bound
659 * to the endpoint, it can send and receive messages.
661 template <class PFooSide
>
664 typedef base::ProcessId ProcessId
;
668 mMode(static_cast<mozilla::ipc::Transport::Mode
>(0)),
672 Endpoint(const PrivateIPDLInterface
&, mozilla::ipc::Transport::Mode aMode
,
673 TransportDescriptor aTransport
, ProcessId aMyPid
,
677 mTransport(aTransport
),
679 mOtherPid(aOtherPid
) {}
681 Endpoint(Endpoint
&& aOther
)
682 : mValid(aOther
.mValid
),
683 mTransport(aOther
.mTransport
),
684 mMyPid(aOther
.mMyPid
),
685 mOtherPid(aOther
.mOtherPid
) {
687 mMode
= aOther
.mMode
;
689 aOther
.mValid
= false;
692 Endpoint
& operator=(Endpoint
&& aOther
) {
693 mValid
= aOther
.mValid
;
695 mMode
= aOther
.mMode
;
697 mTransport
= aOther
.mTransport
;
698 mMyPid
= aOther
.mMyPid
;
699 mOtherPid
= aOther
.mOtherPid
;
701 aOther
.mValid
= false;
707 CloseDescriptor(mTransport
);
711 ProcessId
OtherPid() const { return mOtherPid
; }
713 // This method binds aActor to this endpoint. After this call, the actor can
714 // be used to send and receive messages. The endpoint becomes invalid.
715 bool Bind(PFooSide
* aActor
) {
716 MOZ_RELEASE_ASSERT(mValid
);
717 MOZ_RELEASE_ASSERT(mMyPid
== base::GetCurrentProcId());
719 UniquePtr
<Transport
> transport
=
720 mozilla::ipc::OpenDescriptor(mTransport
, mMode
);
725 std::move(transport
), mOtherPid
, XRE_GetIOMessageLoop(),
726 mMode
== Transport::MODE_SERVER
? ParentSide
: ChildSide
)) {
733 bool IsValid() const { return mValid
; }
736 friend struct IPC::ParamTraits
<Endpoint
<PFooSide
>>;
738 Endpoint(const Endpoint
&) = delete;
739 Endpoint
& operator=(const Endpoint
&) = delete;
742 mozilla::ipc::Transport::Mode mMode
;
743 TransportDescriptor mTransport
;
744 ProcessId mMyPid
, mOtherPid
;
747 #if defined(XP_MACOSX)
748 void AnnotateCrashReportWithErrno(CrashReporter::Annotation tag
, int error
);
750 static inline void AnnotateCrashReportWithErrno(CrashReporter::Annotation tag
,
754 // This function is used internally to create a pair of Endpoints. See the
755 // comment above Endpoint for a description of how it might be used.
756 template <class PFooParent
, class PFooChild
>
757 nsresult
CreateEndpoints(const PrivateIPDLInterface
& aPrivate
,
758 base::ProcessId aParentDestPid
,
759 base::ProcessId aChildDestPid
,
760 Endpoint
<PFooParent
>* aParentEndpoint
,
761 Endpoint
<PFooChild
>* aChildEndpoint
) {
762 MOZ_RELEASE_ASSERT(aParentDestPid
);
763 MOZ_RELEASE_ASSERT(aChildDestPid
);
765 TransportDescriptor parentTransport
, childTransport
;
767 if (NS_FAILED(rv
= CreateTransport(aParentDestPid
, &parentTransport
,
769 AnnotateCrashReportWithErrno(
770 CrashReporter::Annotation::IpcCreateEndpointsNsresult
, int(rv
));
775 Endpoint
<PFooParent
>(aPrivate
, mozilla::ipc::Transport::MODE_SERVER
,
776 parentTransport
, aParentDestPid
, aChildDestPid
);
779 Endpoint
<PFooChild
>(aPrivate
, mozilla::ipc::Transport::MODE_CLIENT
,
780 childTransport
, aChildDestPid
, aParentDestPid
);
786 * A managed endpoint represents one end of a partially initialized managed
787 * IPDL actor. It is used for situations where the usual IPDL Constructor
788 * methods do not give sufficient control over the construction of actors, such
789 * as when constructing actors within replies, or constructing multiple related
790 * actors simultaneously.
792 * FooParent* parent = new FooParent();
793 * ManagedEndpoint<PFooChild> childEp = parentMgr->OpenPFooEndpoint(parent);
795 * ManagedEndpoints should be sent using IPDL messages or other mechanisms to
796 * the other side of the manager channel. Once the ManagedEndpoint has arrived
797 * at its destination, you can create the actor, and bind it to the endpoint.
799 * FooChild* child = new FooChild();
800 * childMgr->BindPFooEndpoint(childEp, child);
802 * WARNING: If the remote side of an endpoint has not been bound before it
803 * begins to receive messages, an IPC routing error will occur, likely causing
806 template <class PFooSide
>
807 class ManagedEndpoint
{
809 ManagedEndpoint() : mId(0) {}
811 ManagedEndpoint(const PrivateIPDLInterface
&, int32_t aId
) : mId(aId
) {}
813 ManagedEndpoint(ManagedEndpoint
&& aOther
) : mId(aOther
.mId
) {
817 ManagedEndpoint
& operator=(ManagedEndpoint
&& aOther
) {
823 bool IsValid() const { return mId
!= 0; }
825 Maybe
<int32_t> ActorId() const { return IsValid() ? Some(mId
) : Nothing(); }
827 bool operator==(const ManagedEndpoint
& _o
) const { return mId
== _o
.mId
; }
830 friend struct IPC::ParamTraits
<ManagedEndpoint
<PFooSide
>>;
832 ManagedEndpoint(const ManagedEndpoint
&) = delete;
833 ManagedEndpoint
& operator=(const ManagedEndpoint
&) = delete;
835 // The routing ID for the to-be-created endpoint.
838 // XXX(nika): Might be nice to have other info for assertions?
839 // e.g. mManagerId, mManagerType, etc.
842 // The ActorLifecycleProxy is a helper type used internally by IPC to maintain a
843 // maybe-owning reference to an IProtocol object. For well-behaved actors
844 // which are not freed until after their `Dealloc` method is called, a
845 // reference to an actor's `ActorLifecycleProxy` object is an owning one, as the
846 // `Dealloc` method will only be called when all references to the
847 // `ActorLifecycleProxy` are released.
849 // Unfortunately, some actors may be destroyed before their `Dealloc` method
850 // is called. For these actors, `ActorLifecycleProxy` acts as a weak pointer,
851 // and will begin to return `nullptr` from its `Get()` method once the
852 // corresponding actor object has been destroyed.
854 // When calling a `Recv` method, IPC will hold a `ActorLifecycleProxy` reference
855 // to the target actor, meaning that well-behaved actors can behave as though a
856 // strong reference is being held.
858 // Generic IPC code MUST treat ActorLifecycleProxy references as weak
860 class ActorLifecycleProxy
{
862 NS_INLINE_DECL_REFCOUNTING_ONEVENTTARGET(ActorLifecycleProxy
)
864 IProtocol
* Get() { return mActor
; }
867 friend class IProtocol
;
869 explicit ActorLifecycleProxy(IProtocol
* aActor
);
870 ~ActorLifecycleProxy();
872 ActorLifecycleProxy(const ActorLifecycleProxy
&) = delete;
873 ActorLifecycleProxy
& operator=(const ActorLifecycleProxy
&) = delete;
875 IProtocol
* MOZ_NON_OWNING_REF mActor
;
877 // Hold a reference to the actor's manager's ActorLifecycleProxy to help
878 // prevent it from dying while we're still alive!
879 RefPtr
<ActorLifecycleProxy
> mManager
;
882 void TableToArray(const nsTHashtable
<nsPtrHashKey
<void>>& aTable
,
883 nsTArray
<void*>& aArray
);
887 template <typename Protocol
>
888 class ManagedContainer
: public nsTHashtable
<nsPtrHashKey
<Protocol
>> {
889 typedef nsTHashtable
<nsPtrHashKey
<Protocol
>> BaseClass
;
892 // Having the core logic work on void pointers, rather than typed pointers,
893 // means that we can have one instance of this code out-of-line, rather
894 // than several hundred instances of this code out-of-lined. (Those
895 // repeated instances don't necessarily get folded together by the linker
896 // because they contain member offsets and such that differ between the
897 // functions.) We do have to pay for it with some eye-bleedingly bad casts,
899 void ToArray(nsTArray
<Protocol
*>& aArray
) const {
900 ::mozilla::ipc::TableToArray(
901 *reinterpret_cast<const nsTHashtable
<nsPtrHashKey
<void>>*>(
902 static_cast<const BaseClass
*>(this)),
903 reinterpret_cast<nsTArray
<void*>&>(aArray
));
907 template <typename Protocol
>
908 Protocol
* LoneManagedOrNullAsserts(
909 const ManagedContainer
<Protocol
>& aManagees
) {
910 if (aManagees
.IsEmpty()) {
913 MOZ_ASSERT(aManagees
.Count() == 1);
914 return aManagees
.ConstIter().Get()->GetKey();
917 template <typename Protocol
>
918 Protocol
* SingleManagedOrNull(const ManagedContainer
<Protocol
>& aManagees
) {
919 if (aManagees
.Count() != 1) {
922 return aManagees
.ConstIter().Get()->GetKey();
925 } // namespace mozilla
930 struct ParamTraits
<mozilla::ipc::ActorHandle
> {
931 typedef mozilla::ipc::ActorHandle paramType
;
933 static void Write(Message
* aMsg
, const paramType
& aParam
) {
934 IPC::WriteParam(aMsg
, aParam
.mId
);
937 static bool Read(const Message
* aMsg
, PickleIterator
* aIter
,
938 paramType
* aResult
) {
940 if (IPC::ReadParam(aMsg
, aIter
, &id
)) {
947 static void Log(const paramType
& aParam
, std::wstring
* aLog
) {
948 aLog
->append(StringPrintf(L
"(%d)", aParam
.mId
));
952 template <class PFooSide
>
953 struct ParamTraits
<mozilla::ipc::Endpoint
<PFooSide
>> {
954 typedef mozilla::ipc::Endpoint
<PFooSide
> paramType
;
956 static void Write(Message
* aMsg
, const paramType
& aParam
) {
957 IPC::WriteParam(aMsg
, aParam
.mValid
);
958 if (!aParam
.mValid
) {
962 IPC::WriteParam(aMsg
, static_cast<uint32_t>(aParam
.mMode
));
964 // We duplicate the descriptor so that our own file descriptor remains
965 // valid after the write. An alternative would be to set
966 // aParam.mTransport.mValid to false, but that won't work because aParam
968 mozilla::ipc::TransportDescriptor desc
=
969 mozilla::ipc::DuplicateDescriptor(aParam
.mTransport
);
970 IPC::WriteParam(aMsg
, desc
);
972 IPC::WriteParam(aMsg
, aParam
.mMyPid
);
973 IPC::WriteParam(aMsg
, aParam
.mOtherPid
);
976 static bool Read(const Message
* aMsg
, PickleIterator
* aIter
,
977 paramType
* aResult
) {
978 MOZ_RELEASE_ASSERT(!aResult
->mValid
);
980 if (!IPC::ReadParam(aMsg
, aIter
, &aResult
->mValid
)) {
983 if (!aResult
->mValid
) {
984 // Object is empty, but read succeeded.
989 if (!IPC::ReadParam(aMsg
, aIter
, &mode
) ||
990 !IPC::ReadParam(aMsg
, aIter
, &aResult
->mTransport
) ||
991 !IPC::ReadParam(aMsg
, aIter
, &aResult
->mMyPid
) ||
992 !IPC::ReadParam(aMsg
, aIter
, &aResult
->mOtherPid
)) {
995 aResult
->mMode
= Channel::Mode(mode
);
999 static void Log(const paramType
& aParam
, std::wstring
* aLog
) {
1000 aLog
->append(StringPrintf(L
"Endpoint"));
1004 template <class PFooSide
>
1005 struct ParamTraits
<mozilla::ipc::ManagedEndpoint
<PFooSide
>> {
1006 typedef mozilla::ipc::ManagedEndpoint
<PFooSide
> paramType
;
1008 static void Write(Message
* aMsg
, const paramType
& aParam
) {
1009 IPC::WriteParam(aMsg
, aParam
.mId
);
1012 static bool Read(const Message
* aMsg
, PickleIterator
* aIter
,
1013 paramType
* aResult
) {
1014 MOZ_RELEASE_ASSERT(aResult
->mId
== 0);
1016 if (!IPC::ReadParam(aMsg
, aIter
, &aResult
->mId
)) {
1022 static void Log(const paramType
& aParam
, std::wstring
* aLog
) {
1023 aLog
->append(StringPrintf(L
"ManagedEndpoint"));
1029 #endif // mozilla_ipc_ProtocolUtils_h