ipc/glue/Shmem.h

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
     2  * vim: sw=2 ts=8 et :
     3  */
     4 /* This Source Code Form is subject to the terms of the Mozilla Public
     5  * License, v. 2.0. If a copy of the MPL was not distributed with this
     6  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     8 #ifndef mozilla_ipc_Shmem_h
     9 #define mozilla_ipc_Shmem_h
    11 #include "mozilla/Attributes.h"
    13 #include "base/basictypes.h"
    14 #include "base/process.h"
    16 #include "nscore.h"
    17 #include "nsDebug.h"
    19 #include "ipc/IPCMessageUtils.h"
    20 #include "mozilla/ipc/SharedMemory.h"
    22 /**
    23  * |Shmem| is one agent in the IPDL shared memory scheme.  The way it
    24     works is essentially
    25  *
    26  *  (1) C++ code calls, say, |parentActor->AllocShmem(size)|
    28  *  (2) IPDL-generated code creates a |mozilla::ipc::SharedMemory|
    29  *  wrapping the bare OS shmem primitives.  The code then adds the new
    30  *  SharedMemory to the set of shmem segments being managed by IPDL.
    31  *
    32  *  (3) IPDL-generated code "shares" the new SharedMemory to the child
    33  *  process, and then sends a special asynchronous IPC message to the
    34  *  child notifying it of the creation of the segment.  (What this
    35  *  means is OS specific.)
    36  *
    37  *  (4a) The child receives the special IPC message, and using the
    38  *  |SharedMemory{SysV,Basic}::Handle| it was passed, creates a
    39  *  |mozilla::ipc::SharedMemory| in the child
    40  *  process.
    41  *
    42  *  (4b) After sending the "shmem-created" IPC message, IPDL-generated
    43  *  code in the parent returns a |mozilla::ipc::Shmem| back to the C++
    44  *  caller of |parentActor->AllocShmem()|.  The |Shmem| is a "weak
    45  *  reference" to the underlying |SharedMemory|, which is managed by
    46  *  IPDL-generated code.  C++ consumers of |Shmem| can't get at the
    47  *  underlying |SharedMemory|.
    48  *
    49  * If parent code wants to give access rights to the Shmem to the
    50  * child, it does so by sending its |Shmem| to the child, in an IPDL
    51  * message.  The parent's |Shmem| then "dies", i.e. becomes
    52  * inaccessible.  This process could be compared to passing a
    53  * "shmem-access baton" between parent and child.
    54  */
    56 namespace mozilla {
    57 namespace layers {
    58 class ShadowLayerForwarder;
    59 }
    61 namespace ipc {
    63 class Shmem MOZ_FINAL
    64 {
    65   friend struct IPC::ParamTraits<mozilla::ipc::Shmem>;
    66 #ifdef DEBUG
    67   // For ShadowLayerForwarder::CheckSurfaceDescriptor
    68   friend class mozilla::layers::ShadowLayerForwarder;
    69 #endif
    71 public:
    72   typedef int32_t id_t;
    73   // Low-level wrapper around platform shmem primitives.
    74   typedef mozilla::ipc::SharedMemory SharedMemory;
    75   typedef SharedMemory::SharedMemoryType SharedMemoryType;
    76   struct IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead {};
    78   Shmem() :
    79     mSegment(0),
    80     mData(0),
    81     mSize(0),
    82     mId(0)
    83   {
    84   }
    86   Shmem(const Shmem& aOther) :
    87     mSegment(aOther.mSegment),
    88     mData(aOther.mData),
    89     mSize(aOther.mSize),
    90     mId(aOther.mId)
    91   {
    92   }
    94 #if !defined(DEBUG)
    95   Shmem(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
    96         SharedMemory* aSegment, id_t aId) :
    97     mSegment(aSegment),
    98     mData(aSegment->memory()),
    99     mSize(0),
   100     mId(aId)
   101   {
   102     mSize = static_cast<size_t>(*PtrToSize(mSegment));
   103   }
   104 #else
   105   Shmem(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
   106         SharedMemory* aSegment, id_t aId);
   107 #endif
   109   ~Shmem()
   110   {
   111     // Shmem only holds a "weak ref" to the actual segment, which is
   112     // owned by IPDL. So there's nothing interesting to be done here
   113     forget(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead());
   114   }
   116   Shmem& operator=(const Shmem& aRhs)
   117   {
   118     mSegment = aRhs.mSegment;
   119     mData = aRhs.mData;
   120     mSize = aRhs.mSize;
   121     mId = aRhs.mId;
   122     return *this;
   123   }
   125   bool operator==(const Shmem& aRhs) const
   126   {
   127     // need to compare IDs because of AdoptShmem(); two Shmems might
   128     // refer to the same segment but with different IDs for different
   129     // protocol trees.  (NB: it's possible for this method to
   130     // spuriously return true if AdoptShmem() gives the same ID for
   131     // two protocol trees, but I don't think that can cause any
   132     // problems since the Shmems really would be indistinguishable.)
   133     return mSegment == aRhs.mSegment && mId == aRhs.mId;
   134   }
   136   // Returns whether this Shmem is writable by you, and thus whether you can
   137   // transfer writability to another actor.
   138   bool
   139   IsWritable() const
   140   {
   141     return mSegment != nullptr;
   142   }
   144   // Returns whether this Shmem is readable by you, and thus whether you can
   145   // transfer readability to another actor.
   146   bool
   147   IsReadable() const
   148   {
   149     return mSegment != nullptr;
   150   }
   152   // Return a pointer to the user-visible data segment.
   153   template<typename T>
   154   T*
   155   get() const
   156   {
   157     AssertInvariants();
   158     AssertAligned<T>();
   160     return reinterpret_cast<T*>(mData);
   161   }
   163   // Return the size of the segment as requested when this shmem
   164   // segment was allocated, in units of T.  The underlying mapping may
   165   // actually be larger because of page alignment and private data,
   166   // but this isn't exposed to clients.
   167   template<typename T>
   168   size_t
   169   Size() const
   170   {
   171     AssertInvariants();
   172     AssertAligned<T>();
   174     return mSize / sizeof(T);
   175   }
   177   int GetSysVID() const;
   179   // These shouldn't be used directly, use the IPDL interface instead.
   180   id_t Id(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead) const {
   181     return mId;
   182   }
   184   SharedMemory* Segment(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead) const {
   185     return mSegment;
   186   }
   188 #ifndef DEBUG
   189   void RevokeRights(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead)
   190   {
   191   }
   192 #else
   193   void RevokeRights(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead);
   194 #endif
   196   void forget(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead)
   197   {
   198     mSegment = 0;
   199     mData = 0;
   200     mSize = 0;
   201     mId = 0;
   202   }
   204   static SharedMemory*
   205   Alloc(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
   206         size_t aNBytes,
   207         SharedMemoryType aType,
   208         bool aUnsafe,
   209         bool aProtect=false);
   211   // Prepare this to be shared with |aProcess|.  Return an IPC message
   212   // that contains enough information for the other process to map
   213   // this segment in OpenExisting() below.  Return a new message if
   214   // successful (owned by the caller), nullptr if not.
   215   IPC::Message*
   216   ShareTo(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
   217           base::ProcessHandle aProcess,
   218           int32_t routingId);
   220   // Stop sharing this with |aProcess|.  Return an IPC message that
   221   // contains enough information for the other process to unmap this
   222   // segment.  Return a new message if successful (owned by the
   223   // caller), nullptr if not.
   224   IPC::Message*
   225   UnshareFrom(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
   226               base::ProcessHandle aProcess,
   227               int32_t routingId);
   229   // Return a SharedMemory instance in this process using the
   230   // descriptor shared to us by the process that created the
   231   // underlying OS shmem resource.  The contents of the descriptor
   232   // depend on the type of SharedMemory that was passed to us.
   233   static SharedMemory*
   234   OpenExisting(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
   235                const IPC::Message& aDescriptor,
   236                id_t* aId,
   237                bool aProtect=false);
   239   static void
   240   Dealloc(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
   241           SharedMemory* aSegment);
   243 private:
   244   template<typename T>
   245   void AssertAligned() const
   246   {
   247     if (0 != (mSize % sizeof(T)))
   248       NS_RUNTIMEABORT("shmem is not T-aligned");
   249   }
   251 #if !defined(DEBUG)
   252   void AssertInvariants() const
   253   { }
   255   static uint32_t*
   256   PtrToSize(SharedMemory* aSegment)
   257   {
   258     char* endOfSegment =
   259       reinterpret_cast<char*>(aSegment->memory()) + aSegment->Size();
   260     return reinterpret_cast<uint32_t*>(endOfSegment - sizeof(uint32_t));
   261   }
   263 #else
   264   void AssertInvariants() const;
   265 #endif
   267   SharedMemory* mSegment;
   268   void* mData;
   269   size_t mSize;
   270   id_t mId;
   271 };
   274 } // namespace ipc
   275 } // namespace mozilla
   278 namespace IPC {
   280 template<>
   281 struct ParamTraits<mozilla::ipc::Shmem>
   282 {
   283   typedef mozilla::ipc::Shmem paramType;
   285   // NB: Read()/Write() look creepy in that Shmems have a pointer
   286   // member, but IPDL internally uses mId to properly initialize a
   287   // "real" Shmem
   289   static void Write(Message* aMsg, const paramType& aParam)
   290   {
   291     WriteParam(aMsg, aParam.mId);
   292   }
   294   static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
   295   {
   296     paramType::id_t id;
   297     if (!ReadParam(aMsg, aIter, &id))
   298       return false;
   299     aResult->mId = id;
   300     return true;
   301   }
   303   static void Log(const paramType& aParam, std::wstring* aLog)
   304   {
   305     aLog->append(L"(shmem segment)");
   306   }
   307 };
   310 } // namespace IPC
   313 #endif // ifndef mozilla_ipc_Shmem_h

mercurial