rev |
line source |
nuclear@0
|
1 /************************************************************************************
|
nuclear@0
|
2
|
nuclear@0
|
3 Filename : OVR_SharedMemory.cpp
|
nuclear@0
|
4 Content : Inter-process shared memory subsystem
|
nuclear@0
|
5 Created : June 1, 2014
|
nuclear@0
|
6 Notes :
|
nuclear@0
|
7
|
nuclear@0
|
8 Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved.
|
nuclear@0
|
9
|
nuclear@0
|
10 Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License");
|
nuclear@0
|
11 you may not use the Oculus VR Rift SDK except in compliance with the License,
|
nuclear@0
|
12 which is provided at the time of installation or download, or which
|
nuclear@0
|
13 otherwise accompanies this software in either electronic or hard copy form.
|
nuclear@0
|
14
|
nuclear@0
|
15 You may obtain a copy of the License at
|
nuclear@0
|
16
|
nuclear@0
|
17 http://www.oculusvr.com/licenses/LICENSE-3.2
|
nuclear@0
|
18
|
nuclear@0
|
19 Unless required by applicable law or agreed to in writing, the Oculus VR SDK
|
nuclear@0
|
20 distributed under the License is distributed on an "AS IS" BASIS,
|
nuclear@0
|
21 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
nuclear@0
|
22 See the License for the specific language governing permissions and
|
nuclear@0
|
23 limitations under the License.
|
nuclear@0
|
24
|
nuclear@0
|
25 ************************************************************************************/
|
nuclear@0
|
26
|
nuclear@0
|
27 #include "OVR_SharedMemory.h"
|
nuclear@0
|
28 #include "OVR_Atomic.h"
|
nuclear@0
|
29 #include "OVR_Log.h"
|
nuclear@0
|
30 #include "OVR_String.h"
|
nuclear@0
|
31 #include "OVR_Array.h"
|
nuclear@0
|
32
|
nuclear@0
|
33 #if defined(OVR_OS_WIN32) && !defined(OVR_FAKE_SHAREDMEMORY)
|
nuclear@0
|
34 #include <Sddl.h> // ConvertStringSecurityDescriptorToSecurityDescriptor
|
nuclear@0
|
35 #endif // OVR_OS_WIN32
|
nuclear@0
|
36
|
nuclear@0
|
37 #if (defined(OVR_OS_LINUX) || defined(OVR_OS_MAC)) && !defined(OVR_FAKE_SHAREDMEMORY)
|
nuclear@0
|
38 #include <sys/mman.h> // shm_open(), mmap()
|
nuclear@0
|
39 #include <errno.h> // error results for mmap
|
nuclear@0
|
40 #include <sys/stat.h> // mode constants
|
nuclear@0
|
41 #include <fcntl.h> // O_ constants
|
nuclear@0
|
42 #include <unistd.h> // close()
|
nuclear@0
|
43 #endif // OVR_OS_LINUX
|
nuclear@0
|
44
|
nuclear@0
|
45 OVR_DEFINE_SINGLETON(OVR::SharedMemoryFactory);
|
nuclear@0
|
46
|
nuclear@0
|
47 namespace OVR {
|
nuclear@0
|
48
|
nuclear@0
|
49
|
nuclear@0
|
50 //// Fake version
|
nuclear@0
|
51
|
nuclear@0
|
52 #if defined(OVR_FAKE_SHAREDMEMORY)
|
nuclear@0
|
53
|
nuclear@0
|
54 class FakeMemoryBlock : public RefCountBase<FakeMemoryBlock>
|
nuclear@0
|
55 {
|
nuclear@0
|
56 String Name;
|
nuclear@0
|
57 char* Data;
|
nuclear@0
|
58 int SizeBytes;
|
nuclear@0
|
59 int References;
|
nuclear@0
|
60
|
nuclear@0
|
61 public:
|
nuclear@0
|
62 FakeMemoryBlock(const String& name, int size) :
|
nuclear@0
|
63 Name(name),
|
nuclear@0
|
64 Data(NULL),
|
nuclear@0
|
65 SizeBytes(size),
|
nuclear@0
|
66 References(1)
|
nuclear@0
|
67 {
|
nuclear@0
|
68 Data = new char[SizeBytes];
|
nuclear@0
|
69 }
|
nuclear@0
|
70 ~FakeMemoryBlock()
|
nuclear@0
|
71 {
|
nuclear@0
|
72 delete[] Data;
|
nuclear@0
|
73 }
|
nuclear@0
|
74
|
nuclear@0
|
75 bool IsNamed(const String& name)
|
nuclear@0
|
76 {
|
nuclear@0
|
77 return Name.CompareNoCase(name) == 0;
|
nuclear@0
|
78 }
|
nuclear@0
|
79 void* GetData()
|
nuclear@0
|
80 {
|
nuclear@0
|
81 return Data;
|
nuclear@0
|
82 }
|
nuclear@0
|
83 int GetSizeI()
|
nuclear@0
|
84 {
|
nuclear@0
|
85 return SizeBytes;
|
nuclear@0
|
86 }
|
nuclear@0
|
87 void IncrementReferences()
|
nuclear@0
|
88 {
|
nuclear@0
|
89 ++References;
|
nuclear@0
|
90 }
|
nuclear@0
|
91 bool DecrementReferences()
|
nuclear@0
|
92 {
|
nuclear@0
|
93 return --References <= 0;
|
nuclear@0
|
94 }
|
nuclear@0
|
95 };
|
nuclear@0
|
96
|
nuclear@0
|
97 class SharedMemoryInternal : public NewOverrideBase
|
nuclear@0
|
98 {
|
nuclear@0
|
99 public:
|
nuclear@0
|
100 void* FileView;
|
nuclear@0
|
101 Ptr<FakeMemoryBlock> Block;
|
nuclear@0
|
102
|
nuclear@0
|
103 void Close();
|
nuclear@0
|
104
|
nuclear@0
|
105 SharedMemoryInternal(FakeMemoryBlock* block) :
|
nuclear@0
|
106 Block(block)
|
nuclear@0
|
107 {
|
nuclear@0
|
108 FileView = Block->GetData();
|
nuclear@0
|
109 }
|
nuclear@0
|
110 ~SharedMemoryInternal()
|
nuclear@0
|
111 {
|
nuclear@0
|
112 Close();
|
nuclear@0
|
113 }
|
nuclear@0
|
114
|
nuclear@0
|
115 static SharedMemoryInternal* CreateSharedMemory(const SharedMemory::OpenParameters& params);
|
nuclear@0
|
116 };
|
nuclear@0
|
117
|
nuclear@0
|
118
|
nuclear@0
|
119 //// FakeMemoryManager
|
nuclear@0
|
120
|
nuclear@0
|
121 class FakeMemoryManager : public NewOverrideBase, public SystemSingletonBase<FakeMemoryManager>
|
nuclear@0
|
122 {
|
nuclear@0
|
123 OVR_DECLARE_SINGLETON(FakeMemoryManager);
|
nuclear@0
|
124
|
nuclear@0
|
125 Lock FakeLock;
|
nuclear@0
|
126 Array< Ptr<FakeMemoryBlock> > FakeArray;
|
nuclear@0
|
127
|
nuclear@0
|
128 public:
|
nuclear@0
|
129 SharedMemoryInternal* Open(const char *name, int bytes, bool openOnly)
|
nuclear@0
|
130 {
|
nuclear@0
|
131 Lock::Locker locker(&FakeLock);
|
nuclear@0
|
132
|
nuclear@0
|
133 const int count = FakeArray.GetSizeI();
|
nuclear@0
|
134 for (int ii = 0; ii < count; ++ii)
|
nuclear@0
|
135 {
|
nuclear@0
|
136 if (FakeArray[ii]->IsNamed(name))
|
nuclear@0
|
137 {
|
nuclear@0
|
138 FakeArray[ii]->IncrementReferences();
|
nuclear@0
|
139 return new SharedMemoryInternal(FakeArray[ii]);
|
nuclear@0
|
140 }
|
nuclear@0
|
141 }
|
nuclear@0
|
142
|
nuclear@0
|
143 if (openOnly)
|
nuclear@0
|
144 {
|
nuclear@0
|
145 return NULL;
|
nuclear@0
|
146 }
|
nuclear@0
|
147
|
nuclear@0
|
148 Ptr<FakeMemoryBlock> data = *new FakeMemoryBlock(name, bytes);
|
nuclear@0
|
149 FakeArray.PushBack(data);
|
nuclear@0
|
150 return new SharedMemoryInternal(data);
|
nuclear@0
|
151 }
|
nuclear@0
|
152
|
nuclear@0
|
153 void Free(FakeMemoryBlock* block)
|
nuclear@0
|
154 {
|
nuclear@0
|
155 Lock::Locker locker(&FakeLock);
|
nuclear@0
|
156
|
nuclear@0
|
157 const int count = FakeArray.GetSizeI();
|
nuclear@0
|
158 for (int ii = 0; ii < count; ++ii)
|
nuclear@0
|
159 {
|
nuclear@0
|
160 if (FakeArray[ii].GetPtr() == block)
|
nuclear@0
|
161 {
|
nuclear@0
|
162 // If the reference count hit zero,
|
nuclear@0
|
163 if (FakeArray[ii]->DecrementReferences())
|
nuclear@0
|
164 {
|
nuclear@0
|
165 // Toast
|
nuclear@0
|
166 FakeArray.RemoveAtUnordered(ii);
|
nuclear@0
|
167 }
|
nuclear@0
|
168 break;
|
nuclear@0
|
169 }
|
nuclear@0
|
170 }
|
nuclear@0
|
171 }
|
nuclear@0
|
172 };
|
nuclear@0
|
173
|
nuclear@0
|
174 FakeMemoryManager::FakeMemoryManager()
|
nuclear@0
|
175 {
|
nuclear@0
|
176 PushDestroyCallbacks();
|
nuclear@0
|
177 }
|
nuclear@0
|
178
|
nuclear@0
|
179 FakeMemoryManager::~FakeMemoryManager()
|
nuclear@0
|
180 {
|
nuclear@0
|
181 OVR_ASSERT(FakeArray.GetSizeI() == 0);
|
nuclear@0
|
182 }
|
nuclear@0
|
183
|
nuclear@0
|
184 void FakeMemoryManager::OnSystemDestroy()
|
nuclear@0
|
185 {
|
nuclear@0
|
186 delete this;
|
nuclear@0
|
187 }
|
nuclear@0
|
188
|
nuclear@0
|
189
|
nuclear@0
|
190 } // namespace OVR
|
nuclear@0
|
191
|
nuclear@0
|
192 OVR_DEFINE_SINGLETON(FakeMemoryManager);
|
nuclear@0
|
193
|
nuclear@0
|
194 namespace OVR {
|
nuclear@0
|
195
|
nuclear@0
|
196
|
nuclear@0
|
197 void SharedMemoryInternal::Close()
|
nuclear@0
|
198 {
|
nuclear@0
|
199 FakeMemoryManager::GetInstance()->Free(Block);
|
nuclear@0
|
200 Block.Clear();
|
nuclear@0
|
201 }
|
nuclear@0
|
202
|
nuclear@0
|
203 SharedMemoryInternal* SharedMemoryInternal::CreateSharedMemory(const SharedMemory::OpenParameters& params)
|
nuclear@0
|
204 {
|
nuclear@0
|
205 return FakeMemoryManager::GetInstance()->Open(params.globalName, params.minSizeBytes, params.openMode == SharedMemory::OpenMode_OpenOnly);
|
nuclear@0
|
206 }
|
nuclear@0
|
207
|
nuclear@0
|
208 #endif
|
nuclear@0
|
209
|
nuclear@0
|
210
|
nuclear@0
|
211 //// Windows version
|
nuclear@0
|
212
|
nuclear@0
|
213 #if defined(OVR_OS_WIN32) && !defined(OVR_FAKE_SHAREDMEMORY)
|
nuclear@0
|
214
|
nuclear@0
|
215 #pragma comment(lib, "advapi32.lib")
|
nuclear@0
|
216
|
nuclear@0
|
217 // Hidden implementation class for OS-specific behavior
|
nuclear@0
|
218 class SharedMemoryInternal : public NewOverrideBase
|
nuclear@0
|
219 {
|
nuclear@0
|
220 public:
|
nuclear@0
|
221 HANDLE FileMapping;
|
nuclear@0
|
222 void* FileView;
|
nuclear@0
|
223
|
nuclear@0
|
224 SharedMemoryInternal(HANDLE fileMapping, void* fileView) :
|
nuclear@0
|
225 FileMapping(fileMapping),
|
nuclear@0
|
226 FileView(fileView)
|
nuclear@0
|
227 {
|
nuclear@0
|
228 }
|
nuclear@0
|
229
|
nuclear@0
|
230 ~SharedMemoryInternal()
|
nuclear@0
|
231 {
|
nuclear@0
|
232 // If file view is set,
|
nuclear@0
|
233 if (FileView)
|
nuclear@0
|
234 {
|
nuclear@0
|
235 UnmapViewOfFile(FileView);
|
nuclear@0
|
236 FileView = NULL;
|
nuclear@0
|
237 }
|
nuclear@0
|
238
|
nuclear@0
|
239 // If file mapping is set,
|
nuclear@0
|
240 if (FileMapping != NULL)
|
nuclear@0
|
241 {
|
nuclear@0
|
242 CloseHandle(FileMapping);
|
nuclear@0
|
243 FileMapping = NULL;
|
nuclear@0
|
244 }
|
nuclear@0
|
245 }
|
nuclear@0
|
246
|
nuclear@0
|
247 static SharedMemoryInternal* DoFileMap(HANDLE hFileMapping, const char* fileName, bool openReadOnly, int minSize);
|
nuclear@0
|
248 static SharedMemoryInternal* AttemptOpenSharedMemory(const char* fileName, int minSize, bool openReadOnly);
|
nuclear@0
|
249 static SharedMemoryInternal* AttemptCreateSharedMemory(const char* fileName, int minSize, bool openReadOnly, bool allowRemoteWrite);
|
nuclear@0
|
250 static SharedMemoryInternal* CreateSharedMemory(const SharedMemory::OpenParameters& params);
|
nuclear@0
|
251 };
|
nuclear@0
|
252
|
nuclear@0
|
253 SharedMemoryInternal* SharedMemoryInternal::DoFileMap(HANDLE hFileMapping, const char* fileName, bool openReadOnly, int minSize)
|
nuclear@0
|
254 {
|
nuclear@0
|
255 // Interpret the access mode as a map desired access code
|
nuclear@0
|
256 DWORD mapDesiredAccess = openReadOnly ? FILE_MAP_READ : FILE_MAP_WRITE;
|
nuclear@0
|
257
|
nuclear@0
|
258 // Map view of the file to this process
|
nuclear@0
|
259 void* pFileView = MapViewOfFile(hFileMapping, mapDesiredAccess, 0, 0, minSize);
|
nuclear@0
|
260
|
nuclear@0
|
261 // If mapping could not be created,
|
nuclear@0
|
262 if (!pFileView)
|
nuclear@0
|
263 {
|
nuclear@0
|
264 CloseHandle(hFileMapping);
|
nuclear@0
|
265
|
nuclear@0
|
266 OVR_DEBUG_LOG(("[SharedMemory] FAILURE: Unable to map view of file for %s error code = %d", fileName, GetLastError()));
|
nuclear@0
|
267 OVR_UNUSED(fileName);
|
nuclear@0
|
268 return NULL;
|
nuclear@0
|
269 }
|
nuclear@0
|
270
|
nuclear@0
|
271 // Create internal representation
|
nuclear@0
|
272 SharedMemoryInternal* pimple = new SharedMemoryInternal(hFileMapping, pFileView);
|
nuclear@0
|
273
|
nuclear@0
|
274 // If memory allocation fails,
|
nuclear@0
|
275 if (!pimple)
|
nuclear@0
|
276 {
|
nuclear@0
|
277 UnmapViewOfFile(pFileView);
|
nuclear@0
|
278 CloseHandle(hFileMapping);
|
nuclear@0
|
279
|
nuclear@0
|
280 OVR_DEBUG_LOG(("[SharedMemory] FAILURE: Out of memory"));
|
nuclear@0
|
281 return NULL;
|
nuclear@0
|
282 }
|
nuclear@0
|
283
|
nuclear@0
|
284 return pimple;
|
nuclear@0
|
285 }
|
nuclear@0
|
286
|
nuclear@0
|
287 SharedMemoryInternal* SharedMemoryInternal::AttemptOpenSharedMemory(const char* fileName, int minSize, bool openReadOnly)
|
nuclear@0
|
288 {
|
nuclear@0
|
289 // Interpret the access mode as a map desired access code
|
nuclear@0
|
290 DWORD mapDesiredAccess = openReadOnly ? FILE_MAP_READ : FILE_MAP_WRITE;
|
nuclear@0
|
291
|
nuclear@0
|
292 // Open file mapping
|
nuclear@0
|
293 HANDLE hFileMapping = OpenFileMappingA(mapDesiredAccess, TRUE, fileName);
|
nuclear@0
|
294
|
nuclear@0
|
295 // If file was mapped unsuccessfully,
|
nuclear@0
|
296 if (NULL == hFileMapping)
|
nuclear@0
|
297 {
|
nuclear@0
|
298 OVR_DEBUG_LOG(("[SharedMemory] WARNING: Unable to open file mapping for %s error code = %d (not necessarily bad)", fileName, GetLastError()));
|
nuclear@0
|
299 return NULL;
|
nuclear@0
|
300 }
|
nuclear@0
|
301
|
nuclear@0
|
302 // Map the file
|
nuclear@0
|
303 return DoFileMap(hFileMapping, fileName, openReadOnly, minSize);
|
nuclear@0
|
304 }
|
nuclear@0
|
305
|
nuclear@0
|
306 SharedMemoryInternal* SharedMemoryInternal::AttemptCreateSharedMemory(const char* fileName, int minSize, bool openReadOnly, bool allowRemoteWrite)
|
nuclear@0
|
307 {
|
nuclear@0
|
308 // Prepare a SECURITY_ATTRIBUTES object
|
nuclear@0
|
309 SECURITY_ATTRIBUTES security;
|
nuclear@0
|
310 ZeroMemory(&security, sizeof(security));
|
nuclear@0
|
311 security.nLength = sizeof(security);
|
nuclear@0
|
312
|
nuclear@0
|
313 // Security descriptor by DACL strings:
|
nuclear@0
|
314 // ACE strings grant Allow(A), Object/Contains Inheritance (OICI) of:
|
nuclear@0
|
315 // + Grant All (GA) to System (SY)
|
nuclear@0
|
316 // + Grant All (GA) to Built-in Administrators (BA)
|
nuclear@0
|
317 // + Grant Read-Only (GR) or Read-Write (GWGR) to Interactive Users (IU) - ie. games
|
nuclear@0
|
318 static const char* DACLString_ReadOnly = "D:P(A;OICI;GA;;;SY)(A;OICI;GA;;;BA)(A;OICI;GR;;;IU)";
|
nuclear@0
|
319 static const char* DACLString_ReadWrite = "D:P(A;OICI;GA;;;SY)(A;OICI;GA;;;BA)(A;OICI;GWGR;;;IU)";
|
nuclear@0
|
320
|
nuclear@0
|
321 // Select the remote process access mode
|
nuclear@0
|
322 const char* remoteAccessString =
|
nuclear@0
|
323 allowRemoteWrite ? DACLString_ReadWrite : DACLString_ReadOnly;
|
nuclear@0
|
324
|
nuclear@0
|
325 // Attempt to convert access string to security attributes
|
nuclear@0
|
326 // Note: This will allocate the security descriptor with LocalAlloc() and must be freed later
|
nuclear@0
|
327 BOOL bConvertOkay = ConvertStringSecurityDescriptorToSecurityDescriptorA(
|
nuclear@0
|
328 remoteAccessString, SDDL_REVISION_1, &security.lpSecurityDescriptor, NULL);
|
nuclear@0
|
329
|
nuclear@0
|
330 // If conversion fails,
|
nuclear@0
|
331 if (!bConvertOkay)
|
nuclear@0
|
332 {
|
nuclear@0
|
333 OVR_DEBUG_LOG(("[SharedMemory] FAILURE: Unable to convert access string, error code = %d", GetLastError()));
|
nuclear@0
|
334 return NULL;
|
nuclear@0
|
335 }
|
nuclear@0
|
336
|
nuclear@0
|
337 // Interpret the access mode as a page protection code
|
nuclear@0
|
338 int pageProtectCode = openReadOnly ? PAGE_READONLY : PAGE_READWRITE;
|
nuclear@0
|
339
|
nuclear@0
|
340 // Attempt to create a file mapping
|
nuclear@0
|
341 HANDLE hFileMapping = CreateFileMappingA(INVALID_HANDLE_VALUE, // From page file
|
nuclear@0
|
342 &security, // Security attributes
|
nuclear@0
|
343 pageProtectCode, // Read-only?
|
nuclear@0
|
344 0, // High word for size = 0
|
nuclear@0
|
345 minSize, // Low word for size
|
nuclear@0
|
346 fileName); // Name of global shared memory file
|
nuclear@0
|
347
|
nuclear@0
|
348 // Free the security descriptor buffer
|
nuclear@0
|
349 LocalFree(security.lpSecurityDescriptor);
|
nuclear@0
|
350
|
nuclear@0
|
351 // If mapping could not be created,
|
nuclear@0
|
352 if (NULL == hFileMapping)
|
nuclear@0
|
353 {
|
nuclear@0
|
354 OVR_DEBUG_LOG(("[SharedMemory] FAILURE: Unable to create file mapping for %s error code = %d", fileName, GetLastError()));
|
nuclear@0
|
355 return NULL;
|
nuclear@0
|
356 }
|
nuclear@0
|
357
|
nuclear@0
|
358 #ifndef OVR_ALLOW_CREATE_FILE_MAPPING_IF_EXISTS
|
nuclear@0
|
359 // If the file mapping already exists,
|
nuclear@0
|
360 if (GetLastError() == ERROR_ALREADY_EXISTS)
|
nuclear@0
|
361 {
|
nuclear@0
|
362 CloseHandle(hFileMapping);
|
nuclear@0
|
363
|
nuclear@0
|
364 OVR_DEBUG_LOG(("[SharedMemory] FAILURE: File mapping at %s already exists", fileName));
|
nuclear@0
|
365 return NULL;
|
nuclear@0
|
366 }
|
nuclear@0
|
367 #endif
|
nuclear@0
|
368
|
nuclear@0
|
369 // Map the file
|
nuclear@0
|
370 return DoFileMap(hFileMapping, fileName, openReadOnly, minSize);
|
nuclear@0
|
371 }
|
nuclear@0
|
372
|
nuclear@0
|
373 SharedMemoryInternal* SharedMemoryInternal::CreateSharedMemory(const SharedMemory::OpenParameters& params)
|
nuclear@0
|
374 {
|
nuclear@0
|
375 SharedMemoryInternal* retval = NULL;
|
nuclear@0
|
376
|
nuclear@0
|
377 // Construct the file mapping name in a Windows-specific way
|
nuclear@0
|
378 OVR::String fileMappingName = params.globalName;
|
nuclear@0
|
379 const char *fileName = fileMappingName.ToCStr();
|
nuclear@0
|
380
|
nuclear@0
|
381 // Is being opened read-only?
|
nuclear@0
|
382 const bool openReadOnly = (params.accessMode == SharedMemory::AccessMode_ReadOnly);
|
nuclear@0
|
383
|
nuclear@0
|
384 // Try up to 3 times to reduce low-probability failures:
|
nuclear@0
|
385 static const int ATTEMPTS_MAX = 3;
|
nuclear@0
|
386 for (int attempts = 0; attempts < ATTEMPTS_MAX; ++attempts)
|
nuclear@0
|
387 {
|
nuclear@0
|
388 // If opening should be attempted first,
|
nuclear@0
|
389 if (params.openMode != SharedMemory::OpenMode_CreateOnly)
|
nuclear@0
|
390 {
|
nuclear@0
|
391 // Attempt to open a shared memory map
|
nuclear@0
|
392 retval = AttemptOpenSharedMemory(fileName, params.minSizeBytes, openReadOnly);
|
nuclear@0
|
393
|
nuclear@0
|
394 // If successful,
|
nuclear@0
|
395 if (retval)
|
nuclear@0
|
396 {
|
nuclear@0
|
397 // Done!
|
nuclear@0
|
398 break;
|
nuclear@0
|
399 }
|
nuclear@0
|
400 }
|
nuclear@0
|
401
|
nuclear@0
|
402 // If creating the shared memory is also acceptable,
|
nuclear@0
|
403 if (params.openMode != SharedMemory::OpenMode_OpenOnly)
|
nuclear@0
|
404 {
|
nuclear@0
|
405 // Interpret create mode
|
nuclear@0
|
406 const bool allowRemoteWrite = (params.remoteMode == SharedMemory::RemoteMode_ReadWrite);
|
nuclear@0
|
407
|
nuclear@0
|
408 // Attempt to create a shared memory map
|
nuclear@0
|
409 retval = AttemptCreateSharedMemory(fileName, params.minSizeBytes, openReadOnly, allowRemoteWrite);
|
nuclear@0
|
410
|
nuclear@0
|
411 // If successful,
|
nuclear@0
|
412 if (retval)
|
nuclear@0
|
413 {
|
nuclear@0
|
414 // Done!
|
nuclear@0
|
415 break;
|
nuclear@0
|
416 }
|
nuclear@0
|
417 }
|
nuclear@0
|
418 } // Re-attempt create/open
|
nuclear@0
|
419
|
nuclear@0
|
420 // Note: On Windows the initial contents of the region are guaranteed to be zero.
|
nuclear@0
|
421 return retval;
|
nuclear@0
|
422 }
|
nuclear@0
|
423
|
nuclear@0
|
424 #endif // OVR_OS_WIN32
|
nuclear@0
|
425
|
nuclear@0
|
426
|
nuclear@0
|
427 #if (defined(OVR_OS_LINUX) || defined(OVR_OS_MAC)) && !defined(OVR_FAKE_SHAREDMEMORY)
|
nuclear@0
|
428
|
nuclear@0
|
429 // Hidden implementation class for OS-specific behavior
|
nuclear@0
|
430 class SharedMemoryInternal
|
nuclear@0
|
431 {
|
nuclear@0
|
432 public:
|
nuclear@0
|
433 int FileMapping;
|
nuclear@0
|
434 void* FileView;
|
nuclear@0
|
435 int FileSize;
|
nuclear@0
|
436
|
nuclear@0
|
437 SharedMemoryInternal(int fileMapping, void* fileView, int fileSize) :
|
nuclear@0
|
438 FileMapping(fileMapping),
|
nuclear@0
|
439 FileView(fileView),
|
nuclear@0
|
440 FileSize(fileSize)
|
nuclear@0
|
441 {
|
nuclear@0
|
442 }
|
nuclear@0
|
443
|
nuclear@0
|
444 ~SharedMemoryInternal()
|
nuclear@0
|
445 {
|
nuclear@0
|
446 // If file view is set,
|
nuclear@0
|
447 if (FileView)
|
nuclear@0
|
448 {
|
nuclear@0
|
449 munmap(FileView, FileSize);
|
nuclear@0
|
450 FileView = MAP_FAILED;
|
nuclear@0
|
451 }
|
nuclear@0
|
452
|
nuclear@0
|
453 // If file mapping is set,
|
nuclear@0
|
454 if (FileMapping >= 0)
|
nuclear@0
|
455 {
|
nuclear@0
|
456 close(FileMapping);
|
nuclear@0
|
457 FileMapping = -1;
|
nuclear@0
|
458 }
|
nuclear@0
|
459 }
|
nuclear@0
|
460
|
nuclear@0
|
461 static SharedMemoryInternal* DoFileMap(int hFileMapping, const char* fileName, bool openReadOnly, int minSize);
|
nuclear@0
|
462 static SharedMemoryInternal* AttemptOpenSharedMemory(const char* fileName, int minSize, bool openReadOnly);
|
nuclear@0
|
463 static SharedMemoryInternal* AttemptCreateSharedMemory(const char* fileName, int minSize, bool openReadOnly, bool allowRemoteWrite);
|
nuclear@0
|
464 static SharedMemoryInternal* CreateSharedMemory(const SharedMemory::OpenParameters& params);
|
nuclear@0
|
465 };
|
nuclear@0
|
466
|
nuclear@0
|
467 SharedMemoryInternal* SharedMemoryInternal::DoFileMap(int hFileMapping, const char* fileName, bool openReadOnly, int minSize)
|
nuclear@0
|
468 {
|
nuclear@0
|
469 // Calculate the required flags based on read/write mode
|
nuclear@0
|
470 int prot = openReadOnly ? PROT_READ : (PROT_READ|PROT_WRITE);
|
nuclear@0
|
471
|
nuclear@0
|
472 // Map the file view
|
nuclear@0
|
473 void* pFileView = mmap(NULL, minSize, prot, MAP_SHARED, hFileMapping, 0);
|
nuclear@0
|
474
|
nuclear@0
|
475 if (pFileView == MAP_FAILED)
|
nuclear@0
|
476 {
|
nuclear@0
|
477 close(hFileMapping);
|
nuclear@0
|
478
|
nuclear@0
|
479 OVR_DEBUG_LOG(("[SharedMemory] FAILURE: Unable to map view of file for %s error code = %d", fileName, errno));
|
nuclear@0
|
480 OVR_UNUSED(fileName);
|
nuclear@0
|
481 return NULL;
|
nuclear@0
|
482 }
|
nuclear@0
|
483
|
nuclear@0
|
484 // Create internal representation
|
nuclear@0
|
485 SharedMemoryInternal* pimple = new SharedMemoryInternal(hFileMapping, pFileView, minSize);
|
nuclear@0
|
486
|
nuclear@0
|
487 // If memory allocation fails,
|
nuclear@0
|
488 if (!pimple)
|
nuclear@0
|
489 {
|
nuclear@0
|
490 munmap(pFileView, minSize);
|
nuclear@0
|
491 close(hFileMapping);
|
nuclear@0
|
492
|
nuclear@0
|
493 OVR_DEBUG_LOG(("[SharedMemory] FAILURE: Out of memory"));
|
nuclear@0
|
494 return NULL;
|
nuclear@0
|
495 }
|
nuclear@0
|
496
|
nuclear@0
|
497 return pimple;
|
nuclear@0
|
498 }
|
nuclear@0
|
499
|
nuclear@0
|
500 SharedMemoryInternal* SharedMemoryInternal::AttemptOpenSharedMemory(const char* fileName, int minSize, bool openReadOnly)
|
nuclear@0
|
501 {
|
nuclear@0
|
502 // Calculate permissions and flags based on read/write mode
|
nuclear@0
|
503 int flags = openReadOnly ? O_RDONLY : O_RDWR;
|
nuclear@0
|
504 int perms = openReadOnly ? S_IRUSR : (S_IRUSR | S_IWUSR);
|
nuclear@0
|
505
|
nuclear@0
|
506 // Attempt to open the shared memory file
|
nuclear@0
|
507 int hFileMapping = shm_open(fileName, flags, perms);
|
nuclear@0
|
508
|
nuclear@0
|
509 // If file was not opened successfully,
|
nuclear@0
|
510 if (hFileMapping < 0)
|
nuclear@0
|
511 {
|
nuclear@0
|
512 OVR_DEBUG_LOG(("[SharedMemory] WARNING: Unable to open file mapping for %s error code = %d (not necessarily bad)", fileName, errno));
|
nuclear@0
|
513 return NULL;
|
nuclear@0
|
514 }
|
nuclear@0
|
515
|
nuclear@0
|
516 // Map the file
|
nuclear@0
|
517 return DoFileMap(hFileMapping, fileName, openReadOnly, minSize);
|
nuclear@0
|
518 }
|
nuclear@0
|
519
|
nuclear@0
|
520 SharedMemoryInternal* SharedMemoryInternal::AttemptCreateSharedMemory(const char* fileName, int minSize, bool openReadOnly, bool allowRemoteWrite)
|
nuclear@0
|
521 {
|
nuclear@0
|
522 // Create mode
|
nuclear@0
|
523 // Note: Cannot create the shared memory file read-only because then ftruncate() will fail.
|
nuclear@0
|
524 int flags = O_CREAT | O_RDWR;
|
nuclear@0
|
525
|
nuclear@0
|
526 #ifndef OVR_ALLOW_CREATE_FILE_MAPPING_IF_EXISTS
|
nuclear@0
|
527 // Require exclusive access when creating (seems like a good idea without trying it yet..)
|
nuclear@0
|
528 if (shm_unlink(fileName) < 0)
|
nuclear@0
|
529 {
|
nuclear@0
|
530 OVR_DEBUG_LOG(("[SharedMemory] WARNING: Unable to unlink shared memory file %s error code = %d", fileName, errno));
|
nuclear@0
|
531 }
|
nuclear@0
|
532 flags |= O_EXCL;
|
nuclear@0
|
533 #endif
|
nuclear@0
|
534
|
nuclear@0
|
535 // Set own read/write permissions
|
nuclear@0
|
536 int perms = openReadOnly ? S_IRUSR : (S_IRUSR|S_IWUSR);
|
nuclear@0
|
537
|
nuclear@0
|
538 // Allow other users to read/write the shared memory file
|
nuclear@0
|
539 perms |= allowRemoteWrite ? (S_IWGRP|S_IWOTH|S_IRGRP|S_IROTH) : (S_IRGRP|S_IROTH);
|
nuclear@0
|
540
|
nuclear@0
|
541 // Attempt to open the shared memory file
|
nuclear@0
|
542 int hFileMapping = shm_open(fileName, flags, perms);
|
nuclear@0
|
543
|
nuclear@0
|
544 // If file was not opened successfully,
|
nuclear@0
|
545 if (hFileMapping < 0)
|
nuclear@0
|
546 {
|
nuclear@0
|
547 OVR_DEBUG_LOG(("[SharedMemory] FAILURE: Unable to create file mapping for %s error code = %d", fileName, errno));
|
nuclear@0
|
548 return NULL;
|
nuclear@0
|
549 }
|
nuclear@0
|
550
|
nuclear@0
|
551 int truncRes = ftruncate(hFileMapping, minSize);
|
nuclear@0
|
552
|
nuclear@0
|
553 // If file was not opened successfully,
|
nuclear@0
|
554 if (truncRes < 0)
|
nuclear@0
|
555 {
|
nuclear@0
|
556 close(hFileMapping);
|
nuclear@0
|
557 OVR_DEBUG_LOG(("[SharedMemory] FAILURE: Unable to truncate file for %s to %d error code = %d", fileName, minSize, errno));
|
nuclear@0
|
558 return NULL;
|
nuclear@0
|
559 }
|
nuclear@0
|
560
|
nuclear@0
|
561 // Map the file
|
nuclear@0
|
562 return DoFileMap(hFileMapping, fileName, openReadOnly, minSize);
|
nuclear@0
|
563 }
|
nuclear@0
|
564
|
nuclear@0
|
565 SharedMemoryInternal* SharedMemoryInternal::CreateSharedMemory(const SharedMemory::OpenParameters& params)
|
nuclear@0
|
566 {
|
nuclear@0
|
567 SharedMemoryInternal* retval = NULL;
|
nuclear@0
|
568
|
nuclear@0
|
569 // Construct the file mapping name in a Linux-specific way
|
nuclear@0
|
570 OVR::String fileMappingName = "/";
|
nuclear@0
|
571 fileMappingName += params.globalName;
|
nuclear@0
|
572 const char *fileName = fileMappingName.ToCStr();
|
nuclear@0
|
573
|
nuclear@0
|
574 // Is being opened read-only?
|
nuclear@0
|
575 const bool openReadOnly = (params.accessMode == SharedMemory::AccessMode_ReadOnly);
|
nuclear@0
|
576
|
nuclear@0
|
577 // Try up to 3 times to reduce low-probability failures:
|
nuclear@0
|
578 static const int ATTEMPTS_MAX = 3;
|
nuclear@0
|
579 for (int attempts = 0; attempts < ATTEMPTS_MAX; ++attempts)
|
nuclear@0
|
580 {
|
nuclear@0
|
581 // If opening should be attempted first,
|
nuclear@0
|
582 if (params.openMode != SharedMemory::OpenMode_CreateOnly)
|
nuclear@0
|
583 {
|
nuclear@0
|
584 // Attempt to open a shared memory map
|
nuclear@0
|
585 retval = AttemptOpenSharedMemory(fileName, params.minSizeBytes, openReadOnly);
|
nuclear@0
|
586
|
nuclear@0
|
587 // If successful,
|
nuclear@0
|
588 if (retval)
|
nuclear@0
|
589 {
|
nuclear@0
|
590 // Done!
|
nuclear@0
|
591 break;
|
nuclear@0
|
592 }
|
nuclear@0
|
593 }
|
nuclear@0
|
594
|
nuclear@0
|
595 // If creating the shared memory is also acceptable,
|
nuclear@0
|
596 if (params.openMode != SharedMemory::OpenMode_OpenOnly)
|
nuclear@0
|
597 {
|
nuclear@0
|
598 // Interpret create mode
|
nuclear@0
|
599 const bool allowRemoteWrite = (params.remoteMode == SharedMemory::RemoteMode_ReadWrite);
|
nuclear@0
|
600
|
nuclear@0
|
601 // Attempt to create a shared memory map
|
nuclear@0
|
602 retval = AttemptCreateSharedMemory(fileName, params.minSizeBytes, openReadOnly, allowRemoteWrite);
|
nuclear@0
|
603
|
nuclear@0
|
604 // If successful,
|
nuclear@0
|
605 if (retval)
|
nuclear@0
|
606 {
|
nuclear@0
|
607 // Done!
|
nuclear@0
|
608 break;
|
nuclear@0
|
609 }
|
nuclear@0
|
610 }
|
nuclear@0
|
611 } // Re-attempt create/open
|
nuclear@0
|
612
|
nuclear@0
|
613 // Note: On Windows the initial contents of the region are guaranteed to be zero.
|
nuclear@0
|
614 return retval;
|
nuclear@0
|
615 }
|
nuclear@0
|
616
|
nuclear@0
|
617 #endif // OVR_OS_LINUX
|
nuclear@0
|
618
|
nuclear@0
|
619
|
nuclear@0
|
620 //// SharedMemory
|
nuclear@0
|
621
|
nuclear@0
|
622 SharedMemory::SharedMemory(int size, void* data, SharedMemoryInternal* pInternal) :
|
nuclear@0
|
623 Size(size),
|
nuclear@0
|
624 Data(data),
|
nuclear@0
|
625 Internal(pInternal)
|
nuclear@0
|
626 {
|
nuclear@0
|
627 }
|
nuclear@0
|
628 // Call close when it goes out of scope
|
nuclear@0
|
629 SharedMemory::~SharedMemory()
|
nuclear@0
|
630 {
|
nuclear@0
|
631 Close();
|
nuclear@0
|
632 delete Internal;
|
nuclear@0
|
633 }
|
nuclear@0
|
634
|
nuclear@0
|
635 void SharedMemory::Close()
|
nuclear@0
|
636 {
|
nuclear@0
|
637 if (Internal)
|
nuclear@0
|
638 {
|
nuclear@0
|
639 delete Internal;
|
nuclear@0
|
640 Internal = NULL;
|
nuclear@0
|
641 }
|
nuclear@0
|
642 }
|
nuclear@0
|
643
|
nuclear@0
|
644
|
nuclear@0
|
645 //// SharedMemoryFactory
|
nuclear@0
|
646
|
nuclear@0
|
647 Ptr<SharedMemory> SharedMemoryFactory::Open(const SharedMemory::OpenParameters& params)
|
nuclear@0
|
648 {
|
nuclear@0
|
649 Ptr<SharedMemory> retval;
|
nuclear@0
|
650
|
nuclear@0
|
651 // If no name specified or no size requested,
|
nuclear@0
|
652 if (!params.globalName || (params.minSizeBytes <= 0))
|
nuclear@0
|
653 {
|
nuclear@0
|
654 OVR_DEBUG_LOG(("[SharedMemory] FAILURE: Invalid parameters to Create()"));
|
nuclear@0
|
655 return NULL;
|
nuclear@0
|
656 }
|
nuclear@0
|
657
|
nuclear@0
|
658 OVR_DEBUG_LOG(("[SharedMemory] Creating shared memory region: %s > %d bytes",
|
nuclear@0
|
659 params.globalName, params.minSizeBytes));
|
nuclear@0
|
660
|
nuclear@0
|
661 // Attempt to create a shared memory region from the parameters
|
nuclear@0
|
662 SharedMemoryInternal* pInternal = SharedMemoryInternal::CreateSharedMemory(params);
|
nuclear@0
|
663
|
nuclear@0
|
664 if (pInternal)
|
nuclear@0
|
665 {
|
nuclear@0
|
666 // Create the wrapper object
|
nuclear@0
|
667 retval = *new SharedMemory(params.minSizeBytes, pInternal->FileView, pInternal);
|
nuclear@0
|
668 }
|
nuclear@0
|
669
|
nuclear@0
|
670 return retval;
|
nuclear@0
|
671 }
|
nuclear@0
|
672
|
nuclear@0
|
673 SharedMemoryFactory::SharedMemoryFactory()
|
nuclear@0
|
674 {
|
nuclear@0
|
675 OVR_DEBUG_LOG(("[SharedMemory] Creating factory"));
|
nuclear@0
|
676
|
nuclear@0
|
677 PushDestroyCallbacks();
|
nuclear@0
|
678 }
|
nuclear@0
|
679
|
nuclear@0
|
680 SharedMemoryFactory::~SharedMemoryFactory()
|
nuclear@0
|
681 {
|
nuclear@0
|
682 OVR_DEBUG_LOG(("[SharedMemory] Destroying factory"));
|
nuclear@0
|
683 }
|
nuclear@0
|
684
|
nuclear@0
|
685 void SharedMemoryFactory::OnSystemDestroy()
|
nuclear@0
|
686 {
|
nuclear@0
|
687 delete this;
|
nuclear@0
|
688 }
|
nuclear@0
|
689
|
nuclear@0
|
690
|
nuclear@0
|
691 } // namespace OVR
|