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