ovr_sdk

view LibOVR/Src/Kernel/OVR_SharedMemory.cpp @ 0:1b39a1b46319

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