Line data Source code
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 "mozilla/Attributes.h"
11 :
12 : #include "base/basictypes.h"
13 : #include "base/process.h"
14 :
15 : #include "nscore.h"
16 : #include "nsDebug.h"
17 :
18 : #include "ipc/IPCMessageUtils.h"
19 : #include "mozilla/ipc/SharedMemory.h"
20 :
21 : /**
22 : * |Shmem| is one agent in the IPDL shared memory scheme. The way it
23 : works is essentially
24 : *
25 : * (1) C++ code calls, say, |parentActor->AllocShmem(size)|
26 :
27 : * (2) IPDL-generated code creates a |mozilla::ipc::SharedMemory|
28 : * wrapping the bare OS shmem primitives. The code then adds the new
29 : * SharedMemory to the set of shmem segments being managed by IPDL.
30 : *
31 : * (3) IPDL-generated code "shares" the new SharedMemory to the child
32 : * process, and then sends a special asynchronous IPC message to the
33 : * child notifying it of the creation of the segment. (What this
34 : * means is OS specific.)
35 : *
36 : * (4a) The child receives the special IPC message, and using the
37 : * |SharedMemory{Basic}::Handle| it was passed, creates a
38 : * |mozilla::ipc::SharedMemory| in the child
39 : * 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 |SharedMemory|.
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 {
56 : namespace layers {
57 : class ShadowLayerForwarder;
58 : } // namespace layers
59 :
60 : namespace ipc {
61 :
62 : class Shmem final
63 : {
64 : friend struct IPC::ParamTraits<mozilla::ipc::Shmem>;
65 : #ifdef DEBUG
66 : // For ShadowLayerForwarder::CheckSurfaceDescriptor
67 : friend class mozilla::layers::ShadowLayerForwarder;
68 : #endif
69 :
70 : public:
71 : typedef int32_t id_t;
72 : // Low-level wrapper around platform shmem primitives.
73 : typedef mozilla::ipc::SharedMemory SharedMemory;
74 : typedef SharedMemory::SharedMemoryType SharedMemoryType;
75 : struct IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead {};
76 :
77 10 : Shmem() :
78 : mSegment(nullptr),
79 : mData(nullptr),
80 : mSize(0),
81 10 : mId(0)
82 : {
83 10 : }
84 :
85 7 : Shmem(const Shmem& aOther) :
86 : mSegment(aOther.mSegment),
87 7 : mData(aOther.mData),
88 7 : mSize(aOther.mSize),
89 21 : mId(aOther.mId)
90 : {
91 7 : }
92 :
93 : #if !defined(DEBUG)
94 : Shmem(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
95 : SharedMemory* aSegment, id_t aId) :
96 : mSegment(aSegment),
97 : mData(aSegment->memory()),
98 : mSize(0),
99 : mId(aId)
100 : {
101 : mSize = static_cast<size_t>(*PtrToSize(mSegment));
102 : }
103 : #else
104 : Shmem(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
105 : SharedMemory* aSegment, id_t aId);
106 : #endif
107 :
108 20 : ~Shmem()
109 20 : {
110 : // Shmem only holds a "weak ref" to the actual segment, which is
111 : // owned by IPDL. So there's nothing interesting to be done here
112 20 : forget(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead());
113 20 : }
114 :
115 8 : Shmem& operator=(const Shmem& aRhs)
116 : {
117 8 : mSegment = aRhs.mSegment;
118 8 : mData = aRhs.mData;
119 8 : mSize = aRhs.mSize;
120 8 : mId = aRhs.mId;
121 8 : return *this;
122 : }
123 :
124 0 : bool operator==(const Shmem& aRhs) const
125 : {
126 0 : return mSegment == aRhs.mSegment;
127 : }
128 :
129 : // Returns whether this Shmem is writable by you, and thus whether you can
130 : // transfer writability to another actor.
131 : bool
132 0 : IsWritable() const
133 : {
134 0 : return mSegment != nullptr;
135 : }
136 :
137 : // Returns whether this Shmem is readable by you, and thus whether you can
138 : // transfer readability to another actor.
139 : bool
140 1 : IsReadable() const
141 : {
142 1 : return mSegment != nullptr;
143 : }
144 :
145 : // Return a pointer to the user-visible data segment.
146 : template<typename T>
147 : T*
148 4 : get() const
149 : {
150 4 : AssertInvariants();
151 4 : AssertAligned<T>();
152 :
153 4 : return reinterpret_cast<T*>(mData);
154 : }
155 :
156 : // Return the size of the segment as requested when this shmem
157 : // segment was allocated, in units of T. The underlying mapping may
158 : // actually be larger because of page alignment and private data,
159 : // but this isn't exposed to clients.
160 : template<typename T>
161 : size_t
162 1 : Size() const
163 : {
164 1 : AssertInvariants();
165 1 : AssertAligned<T>();
166 :
167 1 : return mSize / sizeof(T);
168 : }
169 :
170 : // These shouldn't be used directly, use the IPDL interface instead.
171 5 : id_t Id(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead) const {
172 5 : return mId;
173 : }
174 :
175 : SharedMemory* Segment(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead) const {
176 : return mSegment;
177 : }
178 :
179 : #ifndef DEBUG
180 : void RevokeRights(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead)
181 : {
182 : }
183 : #else
184 : void RevokeRights(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead);
185 : #endif
186 :
187 23 : void forget(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead)
188 : {
189 23 : mSegment = nullptr;
190 23 : mData = nullptr;
191 23 : mSize = 0;
192 23 : mId = 0;
193 23 : }
194 :
195 : static already_AddRefed<Shmem::SharedMemory>
196 : Alloc(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
197 : size_t aNBytes,
198 : SharedMemoryType aType,
199 : bool aUnsafe,
200 : bool aProtect=false);
201 :
202 : // Prepare this to be shared with |aProcess|. Return an IPC message
203 : // that contains enough information for the other process to map
204 : // this segment in OpenExisting() below. Return a new message if
205 : // successful (owned by the caller), nullptr if not.
206 : IPC::Message*
207 : ShareTo(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
208 : base::ProcessId aTargetPid,
209 : int32_t routingId);
210 :
211 : // Stop sharing this with |aTargetPid|. Return an IPC message that
212 : // contains enough information for the other process to unmap this
213 : // segment. Return a new message if successful (owned by the
214 : // caller), nullptr if not.
215 : IPC::Message*
216 : UnshareFrom(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
217 : base::ProcessId aTargetPid,
218 : int32_t routingId);
219 :
220 : // Return a SharedMemory instance in this process using the
221 : // descriptor shared to us by the process that created the
222 : // underlying OS shmem resource. The contents of the descriptor
223 : // depend on the type of SharedMemory that was passed to us.
224 : static already_AddRefed<SharedMemory>
225 : OpenExisting(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
226 : const IPC::Message& aDescriptor,
227 : id_t* aId,
228 : bool aProtect=false);
229 :
230 : static void
231 : Dealloc(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
232 : SharedMemory* aSegment);
233 :
234 : private:
235 : template<typename T>
236 5 : void AssertAligned() const
237 : {
238 0 : if (0 != (mSize % sizeof(T)))
239 0 : MOZ_CRASH("shmem is not T-aligned");
240 5 : }
241 :
242 : #if !defined(DEBUG)
243 : void AssertInvariants() const
244 : { }
245 :
246 : static uint32_t*
247 : PtrToSize(SharedMemory* aSegment)
248 : {
249 : char* endOfSegment =
250 : reinterpret_cast<char*>(aSegment->memory()) + aSegment->Size();
251 : return reinterpret_cast<uint32_t*>(endOfSegment - sizeof(uint32_t));
252 : }
253 :
254 : #else
255 : void AssertInvariants() const;
256 : #endif
257 :
258 : RefPtr<SharedMemory> mSegment;
259 : void* mData;
260 : size_t mSize;
261 : id_t mId;
262 : };
263 :
264 :
265 : } // namespace ipc
266 : } // namespace mozilla
267 :
268 :
269 : namespace IPC {
270 :
271 : template<>
272 : struct ParamTraits<mozilla::ipc::Shmem>
273 : {
274 : typedef mozilla::ipc::Shmem paramType;
275 :
276 : // NB: Read()/Write() look creepy in that Shmems have a pointer
277 : // member, but IPDL internally uses mId to properly initialize a
278 : // "real" Shmem
279 :
280 3 : static void Write(Message* aMsg, const paramType& aParam)
281 : {
282 3 : WriteParam(aMsg, aParam.mId);
283 3 : }
284 :
285 2 : static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
286 : {
287 : paramType::id_t id;
288 2 : if (!ReadParam(aMsg, aIter, &id))
289 0 : return false;
290 2 : aResult->mId = id;
291 2 : return true;
292 : }
293 :
294 : static void Log(const paramType& aParam, std::wstring* aLog)
295 : {
296 : aLog->append(L"(shmem segment)");
297 : }
298 : };
299 :
300 :
301 : } // namespace IPC
302 :
303 :
304 : #endif // ifndef mozilla_ipc_Shmem_h
|