Shmem.h (6203B)
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 http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef mozilla_ipc_Shmem_h 8 #define mozilla_ipc_Shmem_h 9 10 #include "base/basictypes.h" 11 #include "base/process.h" 12 #include "chrome/common/ipc_message_utils.h" 13 14 #include "nsISupports.h" 15 #include "nscore.h" 16 #include "nsDebug.h" 17 18 #include "mozilla/ipc/SharedMemoryMapping.h" 19 #include "mozilla/Range.h" 20 #include "mozilla/UniquePtr.h" 21 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)| 27 28 * (2) IPDL-generated code creates a |mozilla::ipc::SharedMemoryMapping| 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 * |MutableSharedMemoryHandle| it was passed, creates a |SharedMemoryMapping| 39 * in the child process. 40 * 41 * (4b) After sending the "shmem-created" IPC message, IPDL-generated 42 * code in the parent returns a |mozilla::ipc::Shmem| back to the C++ 43 * caller of |parentActor->AllocShmem()|. The |Shmem| is a "weak 44 * reference" to the underlying |SharedMemory|, which is managed by 45 * IPDL-generated code. C++ consumers of |Shmem| can't get at the 46 * underlying |SharedMemoryMapping|. 47 * 48 * If parent code wants to give access rights to the Shmem to the 49 * child, it does so by sending its |Shmem| to the child, in an IPDL 50 * message. The parent's |Shmem| then "dies", i.e. becomes 51 * inaccessible. This process could be compared to passing a 52 * "shmem-access baton" between parent and child. 53 */ 54 55 namespace mozilla::ipc { 56 57 class IProtocol; 58 class IToplevelProtocol; 59 60 class Shmem final { 61 friend struct IPC::ParamTraits<Shmem>; 62 friend class IProtocol; 63 friend class IToplevelProtocol; 64 65 public: 66 using id_t = int64_t; 67 // Low-level wrapper around platform shmem primitives. 68 class Segment final : public SharedMemoryMapping { 69 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Segment); 70 71 explicit Segment(SharedMemoryMapping&& aMapping) 72 : SharedMemoryMapping(std::move(aMapping)) {} 73 74 private: 75 ~Segment() = default; 76 }; 77 78 class Builder { 79 public: 80 explicit Builder(size_t aSize); 81 82 explicit operator bool() const { return mSegment && mSegment->IsValid(); } 83 84 // Prepare this to be shared with another process. Return an IPC message 85 // that contains enough information for the other process to map this 86 // segment in OpenExisting(), and the shmem. 87 std::tuple<UniquePtr<IPC::Message>, Shmem> Build( 88 id_t aId, bool aUnsafe, IPC::Message::routeid_t aRoutingId); 89 90 private: 91 size_t mSize; 92 MutableSharedMemoryHandle mHandle; 93 RefPtr<Segment> mSegment; 94 }; 95 96 Shmem() : mSegment(nullptr), mData(nullptr), mSize(0), mId(0) {} 97 Shmem(const Shmem& aOther) = default; 98 ~Shmem() { forget(); } 99 100 Shmem& operator=(const Shmem& aRhs) = default; 101 102 bool operator==(const Shmem& aRhs) const { return mSegment == aRhs.mSegment; } 103 104 // Returns whether this Shmem is writable by you, and thus whether you can 105 // transfer writability to another actor. 106 bool IsWritable() const { return mSegment != nullptr; } 107 108 // Returns whether this Shmem is readable by you, and thus whether you can 109 // transfer readability to another actor. 110 bool IsReadable() const { return mSegment != nullptr; } 111 112 // Return a pointer to the user-visible data segment. 113 template <typename T> 114 T* get() const { 115 AssertInvariants(); 116 AssertAligned<T>(); 117 118 return reinterpret_cast<T*>(mData); 119 } 120 121 // Return the size of the segment as requested when this shmem 122 // segment was allocated, in units of T. The underlying mapping may 123 // actually be larger because of page alignment and private data, 124 // but this isn't exposed to clients. 125 template <typename T> 126 size_t Size() const { 127 AssertInvariants(); 128 AssertAligned<T>(); 129 130 return mSize / sizeof(T); 131 } 132 133 template <typename T> 134 Range<T> Range() const { 135 return {get<T>(), Size<T>()}; 136 } 137 138 private: 139 // These shouldn't be used directly, use the IPDL interface instead. 140 141 Shmem(RefPtr<Segment>&& aSegment, id_t aId, size_t aSize, bool aUnsafe); 142 143 id_t Id() const { return mId; } 144 145 Segment* GetSegment() const { return mSegment; } 146 147 #ifndef DEBUG 148 void RevokeRights() {} 149 #else 150 void RevokeRights(); 151 #endif 152 153 void forget() { 154 mSegment = nullptr; 155 mData = nullptr; 156 mSize = 0; 157 mId = 0; 158 #ifdef DEBUG 159 mUnsafe = false; 160 #endif 161 } 162 163 // Stop sharing this with another process. Return an IPC message that 164 // contains enough information for the other process to unmap this 165 // segment. Return a new message if successful (owned by the 166 // caller), nullptr if not. 167 UniquePtr<IPC::Message> MkDestroyedMessage(IPC::Message::routeid_t routingId); 168 169 // Return a Segment instance in this process using the descriptor shared 170 // to us by the process that created the underlying OS shmem resource. The 171 // contents of the descriptor depend on the type of SharedMemory that was 172 // passed to us. 173 static already_AddRefed<Segment> OpenExisting(const IPC::Message& aDescriptor, 174 id_t* aId, 175 bool aProtect = false); 176 177 template <typename T> 178 void AssertAligned() const { 179 if (0 != (mSize % sizeof(T))) MOZ_CRASH("shmem is not T-aligned"); 180 } 181 182 #if !defined(DEBUG) 183 void AssertInvariants() const {} 184 #else 185 void AssertInvariants() const; 186 #endif 187 188 RefPtr<Segment> mSegment; 189 void* mData; 190 size_t mSize; 191 id_t mId; 192 #ifdef DEBUG 193 bool mUnsafe = false; 194 #endif 195 }; 196 197 } // namespace mozilla::ipc 198 199 #endif // ifndef mozilla_ipc_Shmem_h