1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 : * vim: sw=2 ts=8 et :
3 : */
4 : /* ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
7 : * The contents of this file are subject to the Mozilla Public License Version
8 : * 1.1 (the "License"); you may not use this file except in compliance with
9 : * the License. You may obtain a copy of the License at
10 : * http://www.mozilla.org/MPL/
11 : *
12 : * Software distributed under the License is distributed on an "AS IS" basis,
13 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 : * for the specific language governing rights and limitations under the
15 : * License.
16 : *
17 : * The Original Code is Mozilla IPC.
18 : *
19 : * The Initial Developer of the Original Code is
20 : * The Mozilla Foundation
21 : * Portions created by the Initial Developer are Copyright (C) 2009
22 : * the Initial Developer. All Rights Reserved.
23 : *
24 : * Contributor(s):
25 : * Chris Jones <jones.chris.g@gmail.com>
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either the GNU General Public License Version 2 or later (the "GPL"), or
29 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : #ifndef mozilla_ipc_Shmem_h
42 : #define mozilla_ipc_Shmem_h
43 :
44 : #include "mozilla/Attributes.h"
45 :
46 : #include "base/basictypes.h"
47 : #include "base/process.h"
48 :
49 : #include "nscore.h"
50 : #include "nsDebug.h"
51 :
52 : #include "IPC/IPCMessageUtils.h"
53 : #include "mozilla/ipc/SharedMemory.h"
54 :
55 : /**
56 : * |Shmem| is one agent in the IPDL shared memory scheme. The way it
57 : works is essentially
58 : *
59 : * (1) C++ code calls, say, |parentActor->AllocShmem(size)|
60 :
61 : * (2) IPDL-generated code creates a |mozilla::ipc::SharedMemory|
62 : * wrapping the bare OS shmem primitives. The code then adds the new
63 : * SharedMemory to the set of shmem segments being managed by IPDL.
64 : *
65 : * (3) IPDL-generated code "shares" the new SharedMemory to the child
66 : * process, and then sends a special asynchronous IPC message to the
67 : * child notifying it of the creation of the segment. (What this
68 : * means is OS specific.)
69 : *
70 : * (4a) The child receives the special IPC message, and using the
71 : * |SharedMemory{SysV,Basic}::Handle| it was passed, creates a
72 : * |mozilla::ipc::SharedMemory| in the child
73 : * process.
74 : *
75 : * (4b) After sending the "shmem-created" IPC message, IPDL-generated
76 : * code in the parent returns a |mozilla::ipc::Shmem| back to the C++
77 : * caller of |parentActor->AllocShmem()|. The |Shmem| is a "weak
78 : * reference" to the underlying |SharedMemory|, which is managed by
79 : * IPDL-generated code. C++ consumers of |Shmem| can't get at the
80 : * underlying |SharedMemory|.
81 : *
82 : * If parent code wants to give access rights to the Shmem to the
83 : * child, it does so by sending its |Shmem| to the child, in an IPDL
84 : * message. The parent's |Shmem| then "dies", i.e. becomes
85 : * inaccessible. This process could be compared to passing a
86 : * "shmem-access baton" between parent and child.
87 : */
88 :
89 : namespace mozilla {
90 : namespace ipc {
91 :
92 : class Shmem MOZ_FINAL
93 : {
94 : friend struct IPC::ParamTraits<mozilla::ipc::Shmem>;
95 :
96 : public:
97 : typedef int32 id_t;
98 : // Low-level wrapper around platform shmem primitives.
99 : typedef mozilla::ipc::SharedMemory SharedMemory;
100 : typedef SharedMemory::SharedMemoryType SharedMemoryType;
101 : struct IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead {};
102 :
103 0 : Shmem() :
104 : mSegment(0),
105 : mData(0),
106 : mSize(0),
107 0 : mId(0)
108 : {
109 0 : }
110 :
111 0 : Shmem(const Shmem& aOther) :
112 : mSegment(aOther.mSegment),
113 : mData(aOther.mData),
114 : mSize(aOther.mSize),
115 0 : mId(aOther.mId)
116 : {
117 0 : }
118 :
119 : #if !defined(DEBUG)
120 : Shmem(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
121 : SharedMemory* aSegment, id_t aId) :
122 : mSegment(aSegment),
123 : mData(aSegment->memory()),
124 : mSize(0),
125 : mId(aId)
126 : {
127 : mSize = static_cast<size_t>(*PtrToSize(mSegment));
128 : }
129 : #else
130 : Shmem(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
131 : SharedMemory* aSegment, id_t aId);
132 : #endif
133 :
134 0 : ~Shmem()
135 : {
136 : // Shmem only holds a "weak ref" to the actual segment, which is
137 : // owned by IPDL. So there's nothing interesting to be done here
138 0 : forget(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead());
139 0 : }
140 :
141 0 : Shmem& operator=(const Shmem& aRhs)
142 : {
143 0 : mSegment = aRhs.mSegment;
144 0 : mData = aRhs.mData;
145 0 : mSize = aRhs.mSize;
146 0 : mId = aRhs.mId;
147 0 : return *this;
148 : }
149 :
150 0 : bool operator==(const Shmem& aRhs) const
151 : {
152 : // need to compare IDs because of AdoptShmem(); two Shmems might
153 : // refer to the same segment but with different IDs for different
154 : // protocol trees. (NB: it's possible for this method to
155 : // spuriously return true if AdoptShmem() gives the same ID for
156 : // two protocol trees, but I don't think that can cause any
157 : // problems since the Shmems really would be indistinguishable.)
158 0 : return mSegment == aRhs.mSegment && mId == aRhs.mId;
159 : }
160 :
161 : // Returns whether this Shmem is writable by you, and thus whether you can
162 : // transfer writability to another actor.
163 : bool
164 0 : IsWritable() const
165 : {
166 0 : return mSegment != NULL;
167 : }
168 :
169 : // Returns whether this Shmem is readable by you, and thus whether you can
170 : // transfer readability to another actor.
171 : bool
172 0 : IsReadable() const
173 : {
174 0 : return mSegment != NULL;
175 : }
176 :
177 : // Return a pointer to the user-visible data segment.
178 : template<typename T>
179 : T*
180 0 : get() const
181 : {
182 0 : AssertInvariants();
183 0 : AssertAligned<T>();
184 :
185 0 : return reinterpret_cast<T*>(mData);
186 : }
187 :
188 : // Return the size of the segment as requested when this shmem
189 : // segment was allocated, in units of T. The underlying mapping may
190 : // actually be larger because of page alignment and private data,
191 : // but this isn't exposed to clients.
192 : template<typename T>
193 : size_t
194 0 : Size() const
195 : {
196 0 : AssertInvariants();
197 0 : AssertAligned<T>();
198 :
199 0 : return mSize / sizeof(T);
200 : }
201 :
202 : int GetSysVID() const;
203 :
204 : // These shouldn't be used directly, use the IPDL interface instead.
205 0 : id_t Id(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead) const {
206 0 : return mId;
207 : }
208 :
209 0 : SharedMemory* Segment(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead) const {
210 0 : return mSegment;
211 : }
212 :
213 : #ifndef DEBUG
214 : void RevokeRights(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead)
215 : {
216 : }
217 : #else
218 : void RevokeRights(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead);
219 : #endif
220 :
221 0 : void forget(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead)
222 : {
223 0 : mSegment = 0;
224 0 : mData = 0;
225 0 : mSize = 0;
226 0 : mId = 0;
227 0 : }
228 :
229 : static SharedMemory*
230 : Alloc(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
231 : size_t aNBytes,
232 : SharedMemoryType aType,
233 : bool aUnsafe,
234 : bool aProtect=false);
235 :
236 : // Prepare this to be shared with |aProcess|. Return an IPC message
237 : // that contains enough information for the other process to map
238 : // this segment in OpenExisting() below. Return a new message if
239 : // successful (owned by the caller), NULL if not.
240 : IPC::Message*
241 : ShareTo(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
242 : base::ProcessHandle aProcess,
243 : int32 routingId);
244 :
245 : // Stop sharing this with |aProcess|. Return an IPC message that
246 : // contains enough information for the other process to unmap this
247 : // segment. Return a new message if successful (owned by the
248 : // caller), NULL if not.
249 : IPC::Message*
250 : UnshareFrom(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
251 : base::ProcessHandle aProcess,
252 : int32 routingId);
253 :
254 : // Return a SharedMemory instance in this process using the
255 : // descriptor shared to us by the process that created the
256 : // underlying OS shmem resource. The contents of the descriptor
257 : // depend on the type of SharedMemory that was passed to us.
258 : static SharedMemory*
259 : OpenExisting(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
260 : const IPC::Message& aDescriptor,
261 : id_t* aId,
262 : bool aProtect=false);
263 :
264 : static void
265 : Dealloc(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
266 : SharedMemory* aSegment);
267 :
268 : private:
269 : template<typename T>
270 0 : void AssertAligned() const
271 : {
272 0 : if (0 != (mSize % sizeof(T)))
273 0 : NS_RUNTIMEABORT("shmem is not T-aligned");
274 0 : }
275 :
276 : #if !defined(DEBUG)
277 : void AssertInvariants() const
278 : { }
279 :
280 : static uint32*
281 : PtrToSize(SharedMemory* aSegment)
282 : {
283 : char* endOfSegment =
284 : reinterpret_cast<char*>(aSegment->memory()) + aSegment->Size();
285 : return reinterpret_cast<uint32*>(endOfSegment - sizeof(uint32));
286 : }
287 :
288 : #else
289 : void AssertInvariants() const;
290 : #endif
291 :
292 : SharedMemory* mSegment;
293 : void* mData;
294 : size_t mSize;
295 : id_t mId;
296 : };
297 :
298 :
299 : } // namespace ipc
300 : } // namespace mozilla
301 :
302 :
303 : namespace IPC {
304 :
305 : template<>
306 : struct ParamTraits<mozilla::ipc::Shmem>
307 : {
308 : typedef mozilla::ipc::Shmem paramType;
309 :
310 : // NB: Read()/Write() look creepy in that Shmems have a pointer
311 : // member, but IPDL internally uses mId to properly initialize a
312 : // "real" Shmem
313 :
314 0 : static void Write(Message* aMsg, const paramType& aParam)
315 : {
316 0 : WriteParam(aMsg, aParam.mId);
317 0 : }
318 :
319 0 : static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
320 : {
321 : paramType::id_t id;
322 0 : if (!ReadParam(aMsg, aIter, &id))
323 0 : return false;
324 0 : aResult->mId = id;
325 0 : return true;
326 : }
327 :
328 : static void Log(const paramType& aParam, std::wstring* aLog)
329 : {
330 : aLog->append(L"(shmem segment)");
331 : }
332 : };
333 :
334 :
335 : } // namespace IPC
336 :
337 :
338 : #endif // ifndef mozilla_ipc_Shmem_h
|