ipc/glue/Shmem.h

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/ipc/glue/Shmem.h	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,313 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
     1.5 + * vim: sw=2 ts=8 et :
     1.6 + */
     1.7 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.8 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.9 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
    1.10 +
    1.11 +#ifndef mozilla_ipc_Shmem_h
    1.12 +#define mozilla_ipc_Shmem_h
    1.13 +
    1.14 +#include "mozilla/Attributes.h"
    1.15 +
    1.16 +#include "base/basictypes.h"
    1.17 +#include "base/process.h"
    1.18 +
    1.19 +#include "nscore.h"
    1.20 +#include "nsDebug.h"
    1.21 +
    1.22 +#include "ipc/IPCMessageUtils.h"
    1.23 +#include "mozilla/ipc/SharedMemory.h"
    1.24 +
    1.25 +/**
    1.26 + * |Shmem| is one agent in the IPDL shared memory scheme.  The way it
    1.27 +    works is essentially
    1.28 + *
    1.29 + *  (1) C++ code calls, say, |parentActor->AllocShmem(size)|
    1.30 +
    1.31 + *  (2) IPDL-generated code creates a |mozilla::ipc::SharedMemory|
    1.32 + *  wrapping the bare OS shmem primitives.  The code then adds the new
    1.33 + *  SharedMemory to the set of shmem segments being managed by IPDL.
    1.34 + *
    1.35 + *  (3) IPDL-generated code "shares" the new SharedMemory to the child
    1.36 + *  process, and then sends a special asynchronous IPC message to the
    1.37 + *  child notifying it of the creation of the segment.  (What this
    1.38 + *  means is OS specific.)
    1.39 + *
    1.40 + *  (4a) The child receives the special IPC message, and using the
    1.41 + *  |SharedMemory{SysV,Basic}::Handle| it was passed, creates a
    1.42 + *  |mozilla::ipc::SharedMemory| in the child
    1.43 + *  process.
    1.44 + *
    1.45 + *  (4b) After sending the "shmem-created" IPC message, IPDL-generated
    1.46 + *  code in the parent returns a |mozilla::ipc::Shmem| back to the C++
    1.47 + *  caller of |parentActor->AllocShmem()|.  The |Shmem| is a "weak
    1.48 + *  reference" to the underlying |SharedMemory|, which is managed by
    1.49 + *  IPDL-generated code.  C++ consumers of |Shmem| can't get at the
    1.50 + *  underlying |SharedMemory|.
    1.51 + *
    1.52 + * If parent code wants to give access rights to the Shmem to the
    1.53 + * child, it does so by sending its |Shmem| to the child, in an IPDL
    1.54 + * message.  The parent's |Shmem| then "dies", i.e. becomes
    1.55 + * inaccessible.  This process could be compared to passing a
    1.56 + * "shmem-access baton" between parent and child.
    1.57 + */
    1.58 +
    1.59 +namespace mozilla {
    1.60 +namespace layers {
    1.61 +class ShadowLayerForwarder;
    1.62 +}
    1.63 +
    1.64 +namespace ipc {
    1.65 +
    1.66 +class Shmem MOZ_FINAL
    1.67 +{
    1.68 +  friend struct IPC::ParamTraits<mozilla::ipc::Shmem>;
    1.69 +#ifdef DEBUG
    1.70 +  // For ShadowLayerForwarder::CheckSurfaceDescriptor
    1.71 +  friend class mozilla::layers::ShadowLayerForwarder;
    1.72 +#endif
    1.73 +
    1.74 +public:
    1.75 +  typedef int32_t id_t;
    1.76 +  // Low-level wrapper around platform shmem primitives.
    1.77 +  typedef mozilla::ipc::SharedMemory SharedMemory;
    1.78 +  typedef SharedMemory::SharedMemoryType SharedMemoryType;
    1.79 +  struct IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead {};
    1.80 +
    1.81 +  Shmem() :
    1.82 +    mSegment(0),
    1.83 +    mData(0),
    1.84 +    mSize(0),
    1.85 +    mId(0)
    1.86 +  {
    1.87 +  }
    1.88 +
    1.89 +  Shmem(const Shmem& aOther) :
    1.90 +    mSegment(aOther.mSegment),
    1.91 +    mData(aOther.mData),
    1.92 +    mSize(aOther.mSize),
    1.93 +    mId(aOther.mId)
    1.94 +  {
    1.95 +  }
    1.96 +
    1.97 +#if !defined(DEBUG)
    1.98 +  Shmem(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
    1.99 +        SharedMemory* aSegment, id_t aId) :
   1.100 +    mSegment(aSegment),
   1.101 +    mData(aSegment->memory()),
   1.102 +    mSize(0),
   1.103 +    mId(aId)
   1.104 +  {
   1.105 +    mSize = static_cast<size_t>(*PtrToSize(mSegment));
   1.106 +  }
   1.107 +#else
   1.108 +  Shmem(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
   1.109 +        SharedMemory* aSegment, id_t aId);
   1.110 +#endif
   1.111 +
   1.112 +  ~Shmem()
   1.113 +  {
   1.114 +    // Shmem only holds a "weak ref" to the actual segment, which is
   1.115 +    // owned by IPDL. So there's nothing interesting to be done here
   1.116 +    forget(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead());
   1.117 +  }
   1.118 +
   1.119 +  Shmem& operator=(const Shmem& aRhs)
   1.120 +  {
   1.121 +    mSegment = aRhs.mSegment;
   1.122 +    mData = aRhs.mData;
   1.123 +    mSize = aRhs.mSize;
   1.124 +    mId = aRhs.mId;
   1.125 +    return *this;
   1.126 +  }
   1.127 +
   1.128 +  bool operator==(const Shmem& aRhs) const
   1.129 +  {
   1.130 +    // need to compare IDs because of AdoptShmem(); two Shmems might
   1.131 +    // refer to the same segment but with different IDs for different
   1.132 +    // protocol trees.  (NB: it's possible for this method to
   1.133 +    // spuriously return true if AdoptShmem() gives the same ID for
   1.134 +    // two protocol trees, but I don't think that can cause any
   1.135 +    // problems since the Shmems really would be indistinguishable.)
   1.136 +    return mSegment == aRhs.mSegment && mId == aRhs.mId;
   1.137 +  }
   1.138 +
   1.139 +  // Returns whether this Shmem is writable by you, and thus whether you can
   1.140 +  // transfer writability to another actor.
   1.141 +  bool
   1.142 +  IsWritable() const
   1.143 +  {
   1.144 +    return mSegment != nullptr;
   1.145 +  }
   1.146 +
   1.147 +  // Returns whether this Shmem is readable by you, and thus whether you can
   1.148 +  // transfer readability to another actor.
   1.149 +  bool
   1.150 +  IsReadable() const
   1.151 +  {
   1.152 +    return mSegment != nullptr;
   1.153 +  }
   1.154 +
   1.155 +  // Return a pointer to the user-visible data segment.
   1.156 +  template<typename T>
   1.157 +  T*
   1.158 +  get() const
   1.159 +  {
   1.160 +    AssertInvariants();
   1.161 +    AssertAligned<T>();
   1.162 +
   1.163 +    return reinterpret_cast<T*>(mData);
   1.164 +  }
   1.165 +
   1.166 +  // Return the size of the segment as requested when this shmem
   1.167 +  // segment was allocated, in units of T.  The underlying mapping may
   1.168 +  // actually be larger because of page alignment and private data,
   1.169 +  // but this isn't exposed to clients.
   1.170 +  template<typename T>
   1.171 +  size_t
   1.172 +  Size() const
   1.173 +  {
   1.174 +    AssertInvariants();
   1.175 +    AssertAligned<T>();
   1.176 +
   1.177 +    return mSize / sizeof(T);
   1.178 +  }
   1.179 +
   1.180 +  int GetSysVID() const;
   1.181 +
   1.182 +  // These shouldn't be used directly, use the IPDL interface instead.
   1.183 +  id_t Id(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead) const {
   1.184 +    return mId;
   1.185 +  }
   1.186 +
   1.187 +  SharedMemory* Segment(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead) const {
   1.188 +    return mSegment;
   1.189 +  }
   1.190 +
   1.191 +#ifndef DEBUG
   1.192 +  void RevokeRights(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead)
   1.193 +  {
   1.194 +  }
   1.195 +#else
   1.196 +  void RevokeRights(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead);
   1.197 +#endif
   1.198 +
   1.199 +  void forget(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead)
   1.200 +  {
   1.201 +    mSegment = 0;
   1.202 +    mData = 0;
   1.203 +    mSize = 0;
   1.204 +    mId = 0;
   1.205 +  }
   1.206 +
   1.207 +  static SharedMemory*
   1.208 +  Alloc(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
   1.209 +        size_t aNBytes,
   1.210 +        SharedMemoryType aType,
   1.211 +        bool aUnsafe,
   1.212 +        bool aProtect=false);
   1.213 +
   1.214 +  // Prepare this to be shared with |aProcess|.  Return an IPC message
   1.215 +  // that contains enough information for the other process to map
   1.216 +  // this segment in OpenExisting() below.  Return a new message if
   1.217 +  // successful (owned by the caller), nullptr if not.
   1.218 +  IPC::Message*
   1.219 +  ShareTo(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
   1.220 +          base::ProcessHandle aProcess,
   1.221 +          int32_t routingId);
   1.222 +
   1.223 +  // Stop sharing this with |aProcess|.  Return an IPC message that
   1.224 +  // contains enough information for the other process to unmap this
   1.225 +  // segment.  Return a new message if successful (owned by the
   1.226 +  // caller), nullptr if not.
   1.227 +  IPC::Message*
   1.228 +  UnshareFrom(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
   1.229 +              base::ProcessHandle aProcess,
   1.230 +              int32_t routingId);
   1.231 +
   1.232 +  // Return a SharedMemory instance in this process using the
   1.233 +  // descriptor shared to us by the process that created the
   1.234 +  // underlying OS shmem resource.  The contents of the descriptor
   1.235 +  // depend on the type of SharedMemory that was passed to us.
   1.236 +  static SharedMemory*
   1.237 +  OpenExisting(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
   1.238 +               const IPC::Message& aDescriptor,
   1.239 +               id_t* aId,
   1.240 +               bool aProtect=false);
   1.241 +
   1.242 +  static void
   1.243 +  Dealloc(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
   1.244 +          SharedMemory* aSegment);
   1.245 +
   1.246 +private:
   1.247 +  template<typename T>
   1.248 +  void AssertAligned() const
   1.249 +  {
   1.250 +    if (0 != (mSize % sizeof(T)))
   1.251 +      NS_RUNTIMEABORT("shmem is not T-aligned");
   1.252 +  }
   1.253 +
   1.254 +#if !defined(DEBUG)
   1.255 +  void AssertInvariants() const
   1.256 +  { }
   1.257 +
   1.258 +  static uint32_t*
   1.259 +  PtrToSize(SharedMemory* aSegment)
   1.260 +  {
   1.261 +    char* endOfSegment =
   1.262 +      reinterpret_cast<char*>(aSegment->memory()) + aSegment->Size();
   1.263 +    return reinterpret_cast<uint32_t*>(endOfSegment - sizeof(uint32_t));
   1.264 +  }
   1.265 +
   1.266 +#else
   1.267 +  void AssertInvariants() const;
   1.268 +#endif
   1.269 +
   1.270 +  SharedMemory* mSegment;
   1.271 +  void* mData;
   1.272 +  size_t mSize;
   1.273 +  id_t mId;
   1.274 +};
   1.275 +
   1.276 +
   1.277 +} // namespace ipc
   1.278 +} // namespace mozilla
   1.279 +
   1.280 +
   1.281 +namespace IPC {
   1.282 +
   1.283 +template<>
   1.284 +struct ParamTraits<mozilla::ipc::Shmem>
   1.285 +{
   1.286 +  typedef mozilla::ipc::Shmem paramType;
   1.287 +
   1.288 +  // NB: Read()/Write() look creepy in that Shmems have a pointer
   1.289 +  // member, but IPDL internally uses mId to properly initialize a
   1.290 +  // "real" Shmem
   1.291 +
   1.292 +  static void Write(Message* aMsg, const paramType& aParam)
   1.293 +  {
   1.294 +    WriteParam(aMsg, aParam.mId);
   1.295 +  }
   1.296 +
   1.297 +  static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
   1.298 +  {
   1.299 +    paramType::id_t id;
   1.300 +    if (!ReadParam(aMsg, aIter, &id))
   1.301 +      return false;
   1.302 +    aResult->mId = id;
   1.303 +    return true;
   1.304 +  }
   1.305 +
   1.306 +  static void Log(const paramType& aParam, std::wstring* aLog)
   1.307 +  {
   1.308 +    aLog->append(L"(shmem segment)");
   1.309 +  }
   1.310 +};
   1.311 +
   1.312 +
   1.313 +} // namespace IPC
   1.314 +
   1.315 +
   1.316 +#endif // ifndef mozilla_ipc_Shmem_h

mercurial