michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * vim: sw=2 ts=8 et : michael@0: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef mozilla_ipc_Shmem_h michael@0: #define mozilla_ipc_Shmem_h michael@0: michael@0: #include "mozilla/Attributes.h" michael@0: michael@0: #include "base/basictypes.h" michael@0: #include "base/process.h" michael@0: michael@0: #include "nscore.h" michael@0: #include "nsDebug.h" michael@0: michael@0: #include "ipc/IPCMessageUtils.h" michael@0: #include "mozilla/ipc/SharedMemory.h" michael@0: michael@0: /** michael@0: * |Shmem| is one agent in the IPDL shared memory scheme. The way it michael@0: works is essentially michael@0: * michael@0: * (1) C++ code calls, say, |parentActor->AllocShmem(size)| michael@0: michael@0: * (2) IPDL-generated code creates a |mozilla::ipc::SharedMemory| michael@0: * wrapping the bare OS shmem primitives. The code then adds the new michael@0: * SharedMemory to the set of shmem segments being managed by IPDL. michael@0: * michael@0: * (3) IPDL-generated code "shares" the new SharedMemory to the child michael@0: * process, and then sends a special asynchronous IPC message to the michael@0: * child notifying it of the creation of the segment. (What this michael@0: * means is OS specific.) michael@0: * michael@0: * (4a) The child receives the special IPC message, and using the michael@0: * |SharedMemory{SysV,Basic}::Handle| it was passed, creates a michael@0: * |mozilla::ipc::SharedMemory| in the child michael@0: * process. michael@0: * michael@0: * (4b) After sending the "shmem-created" IPC message, IPDL-generated michael@0: * code in the parent returns a |mozilla::ipc::Shmem| back to the C++ michael@0: * caller of |parentActor->AllocShmem()|. The |Shmem| is a "weak michael@0: * reference" to the underlying |SharedMemory|, which is managed by michael@0: * IPDL-generated code. C++ consumers of |Shmem| can't get at the michael@0: * underlying |SharedMemory|. michael@0: * michael@0: * If parent code wants to give access rights to the Shmem to the michael@0: * child, it does so by sending its |Shmem| to the child, in an IPDL michael@0: * message. The parent's |Shmem| then "dies", i.e. becomes michael@0: * inaccessible. This process could be compared to passing a michael@0: * "shmem-access baton" between parent and child. michael@0: */ michael@0: michael@0: namespace mozilla { michael@0: namespace layers { michael@0: class ShadowLayerForwarder; michael@0: } michael@0: michael@0: namespace ipc { michael@0: michael@0: class Shmem MOZ_FINAL michael@0: { michael@0: friend struct IPC::ParamTraits; michael@0: #ifdef DEBUG michael@0: // For ShadowLayerForwarder::CheckSurfaceDescriptor michael@0: friend class mozilla::layers::ShadowLayerForwarder; michael@0: #endif michael@0: michael@0: public: michael@0: typedef int32_t id_t; michael@0: // Low-level wrapper around platform shmem primitives. michael@0: typedef mozilla::ipc::SharedMemory SharedMemory; michael@0: typedef SharedMemory::SharedMemoryType SharedMemoryType; michael@0: struct IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead {}; michael@0: michael@0: Shmem() : michael@0: mSegment(0), michael@0: mData(0), michael@0: mSize(0), michael@0: mId(0) michael@0: { michael@0: } michael@0: michael@0: Shmem(const Shmem& aOther) : michael@0: mSegment(aOther.mSegment), michael@0: mData(aOther.mData), michael@0: mSize(aOther.mSize), michael@0: mId(aOther.mId) michael@0: { michael@0: } michael@0: michael@0: #if !defined(DEBUG) michael@0: Shmem(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead, michael@0: SharedMemory* aSegment, id_t aId) : michael@0: mSegment(aSegment), michael@0: mData(aSegment->memory()), michael@0: mSize(0), michael@0: mId(aId) michael@0: { michael@0: mSize = static_cast(*PtrToSize(mSegment)); michael@0: } michael@0: #else michael@0: Shmem(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead, michael@0: SharedMemory* aSegment, id_t aId); michael@0: #endif michael@0: michael@0: ~Shmem() michael@0: { michael@0: // Shmem only holds a "weak ref" to the actual segment, which is michael@0: // owned by IPDL. So there's nothing interesting to be done here michael@0: forget(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead()); michael@0: } michael@0: michael@0: Shmem& operator=(const Shmem& aRhs) michael@0: { michael@0: mSegment = aRhs.mSegment; michael@0: mData = aRhs.mData; michael@0: mSize = aRhs.mSize; michael@0: mId = aRhs.mId; michael@0: return *this; michael@0: } michael@0: michael@0: bool operator==(const Shmem& aRhs) const michael@0: { michael@0: // need to compare IDs because of AdoptShmem(); two Shmems might michael@0: // refer to the same segment but with different IDs for different michael@0: // protocol trees. (NB: it's possible for this method to michael@0: // spuriously return true if AdoptShmem() gives the same ID for michael@0: // two protocol trees, but I don't think that can cause any michael@0: // problems since the Shmems really would be indistinguishable.) michael@0: return mSegment == aRhs.mSegment && mId == aRhs.mId; michael@0: } michael@0: michael@0: // Returns whether this Shmem is writable by you, and thus whether you can michael@0: // transfer writability to another actor. michael@0: bool michael@0: IsWritable() const michael@0: { michael@0: return mSegment != nullptr; michael@0: } michael@0: michael@0: // Returns whether this Shmem is readable by you, and thus whether you can michael@0: // transfer readability to another actor. michael@0: bool michael@0: IsReadable() const michael@0: { michael@0: return mSegment != nullptr; michael@0: } michael@0: michael@0: // Return a pointer to the user-visible data segment. michael@0: template michael@0: T* michael@0: get() const michael@0: { michael@0: AssertInvariants(); michael@0: AssertAligned(); michael@0: michael@0: return reinterpret_cast(mData); michael@0: } michael@0: michael@0: // Return the size of the segment as requested when this shmem michael@0: // segment was allocated, in units of T. The underlying mapping may michael@0: // actually be larger because of page alignment and private data, michael@0: // but this isn't exposed to clients. michael@0: template michael@0: size_t michael@0: Size() const michael@0: { michael@0: AssertInvariants(); michael@0: AssertAligned(); michael@0: michael@0: return mSize / sizeof(T); michael@0: } michael@0: michael@0: int GetSysVID() const; michael@0: michael@0: // These shouldn't be used directly, use the IPDL interface instead. michael@0: id_t Id(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead) const { michael@0: return mId; michael@0: } michael@0: michael@0: SharedMemory* Segment(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead) const { michael@0: return mSegment; michael@0: } michael@0: michael@0: #ifndef DEBUG michael@0: void RevokeRights(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead) michael@0: { michael@0: } michael@0: #else michael@0: void RevokeRights(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead); michael@0: #endif michael@0: michael@0: void forget(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead) michael@0: { michael@0: mSegment = 0; michael@0: mData = 0; michael@0: mSize = 0; michael@0: mId = 0; michael@0: } michael@0: michael@0: static SharedMemory* michael@0: Alloc(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead, michael@0: size_t aNBytes, michael@0: SharedMemoryType aType, michael@0: bool aUnsafe, michael@0: bool aProtect=false); michael@0: michael@0: // Prepare this to be shared with |aProcess|. Return an IPC message michael@0: // that contains enough information for the other process to map michael@0: // this segment in OpenExisting() below. Return a new message if michael@0: // successful (owned by the caller), nullptr if not. michael@0: IPC::Message* michael@0: ShareTo(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead, michael@0: base::ProcessHandle aProcess, michael@0: int32_t routingId); michael@0: michael@0: // Stop sharing this with |aProcess|. Return an IPC message that michael@0: // contains enough information for the other process to unmap this michael@0: // segment. Return a new message if successful (owned by the michael@0: // caller), nullptr if not. michael@0: IPC::Message* michael@0: UnshareFrom(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead, michael@0: base::ProcessHandle aProcess, michael@0: int32_t routingId); michael@0: michael@0: // Return a SharedMemory instance in this process using the michael@0: // descriptor shared to us by the process that created the michael@0: // underlying OS shmem resource. The contents of the descriptor michael@0: // depend on the type of SharedMemory that was passed to us. michael@0: static SharedMemory* michael@0: OpenExisting(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead, michael@0: const IPC::Message& aDescriptor, michael@0: id_t* aId, michael@0: bool aProtect=false); michael@0: michael@0: static void michael@0: Dealloc(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead, michael@0: SharedMemory* aSegment); michael@0: michael@0: private: michael@0: template michael@0: void AssertAligned() const michael@0: { michael@0: if (0 != (mSize % sizeof(T))) michael@0: NS_RUNTIMEABORT("shmem is not T-aligned"); michael@0: } michael@0: michael@0: #if !defined(DEBUG) michael@0: void AssertInvariants() const michael@0: { } michael@0: michael@0: static uint32_t* michael@0: PtrToSize(SharedMemory* aSegment) michael@0: { michael@0: char* endOfSegment = michael@0: reinterpret_cast(aSegment->memory()) + aSegment->Size(); michael@0: return reinterpret_cast(endOfSegment - sizeof(uint32_t)); michael@0: } michael@0: michael@0: #else michael@0: void AssertInvariants() const; michael@0: #endif michael@0: michael@0: SharedMemory* mSegment; michael@0: void* mData; michael@0: size_t mSize; michael@0: id_t mId; michael@0: }; michael@0: michael@0: michael@0: } // namespace ipc michael@0: } // namespace mozilla michael@0: michael@0: michael@0: namespace IPC { michael@0: michael@0: template<> michael@0: struct ParamTraits michael@0: { michael@0: typedef mozilla::ipc::Shmem paramType; michael@0: michael@0: // NB: Read()/Write() look creepy in that Shmems have a pointer michael@0: // member, but IPDL internally uses mId to properly initialize a michael@0: // "real" Shmem michael@0: michael@0: static void Write(Message* aMsg, const paramType& aParam) michael@0: { michael@0: WriteParam(aMsg, aParam.mId); michael@0: } michael@0: michael@0: static bool Read(const Message* aMsg, void** aIter, paramType* aResult) michael@0: { michael@0: paramType::id_t id; michael@0: if (!ReadParam(aMsg, aIter, &id)) michael@0: return false; michael@0: aResult->mId = id; michael@0: return true; michael@0: } michael@0: michael@0: static void Log(const paramType& aParam, std::wstring* aLog) michael@0: { michael@0: aLog->append(L"(shmem segment)"); michael@0: } michael@0: }; michael@0: michael@0: michael@0: } // namespace IPC michael@0: michael@0: michael@0: #endif // ifndef mozilla_ipc_Shmem_h