nuclear@0: /************************************************************************************ nuclear@0: nuclear@0: Filename : OVR_SharedMemory.cpp nuclear@0: Content : Inter-process shared memory subsystem nuclear@0: Created : June 1, 2014 nuclear@0: Notes : nuclear@0: nuclear@0: Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved. nuclear@0: nuclear@0: Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License"); nuclear@0: you may not use the Oculus VR Rift SDK except in compliance with the License, nuclear@0: which is provided at the time of installation or download, or which nuclear@0: otherwise accompanies this software in either electronic or hard copy form. nuclear@0: nuclear@0: You may obtain a copy of the License at nuclear@0: nuclear@0: http://www.oculusvr.com/licenses/LICENSE-3.2 nuclear@0: nuclear@0: Unless required by applicable law or agreed to in writing, the Oculus VR SDK nuclear@0: distributed under the License is distributed on an "AS IS" BASIS, nuclear@0: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. nuclear@0: See the License for the specific language governing permissions and nuclear@0: limitations under the License. nuclear@0: nuclear@0: ************************************************************************************/ nuclear@0: nuclear@0: #include "OVR_SharedMemory.h" nuclear@0: #include "OVR_Atomic.h" nuclear@0: #include "OVR_Log.h" nuclear@0: #include "OVR_String.h" nuclear@0: #include "OVR_Array.h" nuclear@0: nuclear@0: #if defined(OVR_OS_WIN32) && !defined(OVR_FAKE_SHAREDMEMORY) nuclear@0: #include // ConvertStringSecurityDescriptorToSecurityDescriptor nuclear@0: #endif // OVR_OS_WIN32 nuclear@0: nuclear@0: #if (defined(OVR_OS_LINUX) || defined(OVR_OS_MAC)) && !defined(OVR_FAKE_SHAREDMEMORY) nuclear@0: #include // shm_open(), mmap() nuclear@0: #include // error results for mmap nuclear@0: #include // mode constants nuclear@0: #include // O_ constants nuclear@0: #include // close() nuclear@0: #endif // OVR_OS_LINUX nuclear@0: nuclear@0: OVR_DEFINE_SINGLETON(OVR::SharedMemoryFactory); nuclear@0: nuclear@0: namespace OVR { nuclear@0: nuclear@0: nuclear@0: //// Fake version nuclear@0: nuclear@0: #if defined(OVR_FAKE_SHAREDMEMORY) nuclear@0: nuclear@0: class FakeMemoryBlock : public RefCountBase nuclear@0: { nuclear@0: String Name; nuclear@0: char* Data; nuclear@0: int SizeBytes; nuclear@0: int References; nuclear@0: nuclear@0: public: nuclear@0: FakeMemoryBlock(const String& name, int size) : nuclear@0: Name(name), nuclear@0: Data(NULL), nuclear@0: SizeBytes(size), nuclear@0: References(1) nuclear@0: { nuclear@0: Data = new char[SizeBytes]; nuclear@0: } nuclear@0: ~FakeMemoryBlock() nuclear@0: { nuclear@0: delete[] Data; nuclear@0: } nuclear@0: nuclear@0: bool IsNamed(const String& name) nuclear@0: { nuclear@0: return Name.CompareNoCase(name) == 0; nuclear@0: } nuclear@0: void* GetData() nuclear@0: { nuclear@0: return Data; nuclear@0: } nuclear@0: int GetSizeI() nuclear@0: { nuclear@0: return SizeBytes; nuclear@0: } nuclear@0: void IncrementReferences() nuclear@0: { nuclear@0: ++References; nuclear@0: } nuclear@0: bool DecrementReferences() nuclear@0: { nuclear@0: return --References <= 0; nuclear@0: } nuclear@0: }; nuclear@0: nuclear@0: class SharedMemoryInternal : public NewOverrideBase nuclear@0: { nuclear@0: public: nuclear@0: void* FileView; nuclear@0: Ptr Block; nuclear@0: nuclear@0: void Close(); nuclear@0: nuclear@0: SharedMemoryInternal(FakeMemoryBlock* block) : nuclear@0: Block(block) nuclear@0: { nuclear@0: FileView = Block->GetData(); nuclear@0: } nuclear@0: ~SharedMemoryInternal() nuclear@0: { nuclear@0: Close(); nuclear@0: } nuclear@0: nuclear@0: static SharedMemoryInternal* CreateSharedMemory(const SharedMemory::OpenParameters& params); nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: //// FakeMemoryManager nuclear@0: nuclear@0: class FakeMemoryManager : public NewOverrideBase, public SystemSingletonBase nuclear@0: { nuclear@0: OVR_DECLARE_SINGLETON(FakeMemoryManager); nuclear@0: nuclear@0: Lock FakeLock; nuclear@0: Array< Ptr > FakeArray; nuclear@0: nuclear@0: public: nuclear@0: SharedMemoryInternal* Open(const char *name, int bytes, bool openOnly) nuclear@0: { nuclear@0: Lock::Locker locker(&FakeLock); nuclear@0: nuclear@0: const int count = FakeArray.GetSizeI(); nuclear@0: for (int ii = 0; ii < count; ++ii) nuclear@0: { nuclear@0: if (FakeArray[ii]->IsNamed(name)) nuclear@0: { nuclear@0: FakeArray[ii]->IncrementReferences(); nuclear@0: return new SharedMemoryInternal(FakeArray[ii]); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: if (openOnly) nuclear@0: { nuclear@0: return NULL; nuclear@0: } nuclear@0: nuclear@0: Ptr data = *new FakeMemoryBlock(name, bytes); nuclear@0: FakeArray.PushBack(data); nuclear@0: return new SharedMemoryInternal(data); nuclear@0: } nuclear@0: nuclear@0: void Free(FakeMemoryBlock* block) nuclear@0: { nuclear@0: Lock::Locker locker(&FakeLock); nuclear@0: nuclear@0: const int count = FakeArray.GetSizeI(); nuclear@0: for (int ii = 0; ii < count; ++ii) nuclear@0: { nuclear@0: if (FakeArray[ii].GetPtr() == block) nuclear@0: { nuclear@0: // If the reference count hit zero, nuclear@0: if (FakeArray[ii]->DecrementReferences()) nuclear@0: { nuclear@0: // Toast nuclear@0: FakeArray.RemoveAtUnordered(ii); nuclear@0: } nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: }; nuclear@0: nuclear@0: FakeMemoryManager::FakeMemoryManager() nuclear@0: { nuclear@0: PushDestroyCallbacks(); nuclear@0: } nuclear@0: nuclear@0: FakeMemoryManager::~FakeMemoryManager() nuclear@0: { nuclear@0: OVR_ASSERT(FakeArray.GetSizeI() == 0); nuclear@0: } nuclear@0: nuclear@0: void FakeMemoryManager::OnSystemDestroy() nuclear@0: { nuclear@0: delete this; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: } // namespace OVR nuclear@0: nuclear@0: OVR_DEFINE_SINGLETON(FakeMemoryManager); nuclear@0: nuclear@0: namespace OVR { nuclear@0: nuclear@0: nuclear@0: void SharedMemoryInternal::Close() nuclear@0: { nuclear@0: FakeMemoryManager::GetInstance()->Free(Block); nuclear@0: Block.Clear(); nuclear@0: } nuclear@0: nuclear@0: SharedMemoryInternal* SharedMemoryInternal::CreateSharedMemory(const SharedMemory::OpenParameters& params) nuclear@0: { nuclear@0: return FakeMemoryManager::GetInstance()->Open(params.globalName, params.minSizeBytes, params.openMode == SharedMemory::OpenMode_OpenOnly); nuclear@0: } nuclear@0: nuclear@0: #endif nuclear@0: nuclear@0: nuclear@0: //// Windows version nuclear@0: nuclear@0: #if defined(OVR_OS_WIN32) && !defined(OVR_FAKE_SHAREDMEMORY) nuclear@0: nuclear@0: #pragma comment(lib, "advapi32.lib") nuclear@0: nuclear@0: // Hidden implementation class for OS-specific behavior nuclear@0: class SharedMemoryInternal : public NewOverrideBase nuclear@0: { nuclear@0: public: nuclear@0: HANDLE FileMapping; nuclear@0: void* FileView; nuclear@0: nuclear@0: SharedMemoryInternal(HANDLE fileMapping, void* fileView) : nuclear@0: FileMapping(fileMapping), nuclear@0: FileView(fileView) nuclear@0: { nuclear@0: } nuclear@0: nuclear@0: ~SharedMemoryInternal() nuclear@0: { nuclear@0: // If file view is set, nuclear@0: if (FileView) nuclear@0: { nuclear@0: UnmapViewOfFile(FileView); nuclear@0: FileView = NULL; nuclear@0: } nuclear@0: nuclear@0: // If file mapping is set, nuclear@0: if (FileMapping != NULL) nuclear@0: { nuclear@0: CloseHandle(FileMapping); nuclear@0: FileMapping = NULL; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: static SharedMemoryInternal* DoFileMap(HANDLE hFileMapping, const char* fileName, bool openReadOnly, int minSize); nuclear@0: static SharedMemoryInternal* AttemptOpenSharedMemory(const char* fileName, int minSize, bool openReadOnly); nuclear@0: static SharedMemoryInternal* AttemptCreateSharedMemory(const char* fileName, int minSize, bool openReadOnly, bool allowRemoteWrite); nuclear@0: static SharedMemoryInternal* CreateSharedMemory(const SharedMemory::OpenParameters& params); nuclear@0: }; nuclear@0: nuclear@0: SharedMemoryInternal* SharedMemoryInternal::DoFileMap(HANDLE hFileMapping, const char* fileName, bool openReadOnly, int minSize) nuclear@0: { nuclear@0: // Interpret the access mode as a map desired access code nuclear@0: DWORD mapDesiredAccess = openReadOnly ? FILE_MAP_READ : FILE_MAP_WRITE; nuclear@0: nuclear@0: // Map view of the file to this process nuclear@0: void* pFileView = MapViewOfFile(hFileMapping, mapDesiredAccess, 0, 0, minSize); nuclear@0: nuclear@0: // If mapping could not be created, nuclear@0: if (!pFileView) nuclear@0: { nuclear@0: CloseHandle(hFileMapping); nuclear@0: nuclear@0: OVR_DEBUG_LOG(("[SharedMemory] FAILURE: Unable to map view of file for %s error code = %d", fileName, GetLastError())); nuclear@0: OVR_UNUSED(fileName); nuclear@0: return NULL; nuclear@0: } nuclear@0: nuclear@0: // Create internal representation nuclear@0: SharedMemoryInternal* pimple = new SharedMemoryInternal(hFileMapping, pFileView); nuclear@0: nuclear@0: // If memory allocation fails, nuclear@0: if (!pimple) nuclear@0: { nuclear@0: UnmapViewOfFile(pFileView); nuclear@0: CloseHandle(hFileMapping); nuclear@0: nuclear@0: OVR_DEBUG_LOG(("[SharedMemory] FAILURE: Out of memory")); nuclear@0: return NULL; nuclear@0: } nuclear@0: nuclear@0: return pimple; nuclear@0: } nuclear@0: nuclear@0: SharedMemoryInternal* SharedMemoryInternal::AttemptOpenSharedMemory(const char* fileName, int minSize, bool openReadOnly) nuclear@0: { nuclear@0: // Interpret the access mode as a map desired access code nuclear@0: DWORD mapDesiredAccess = openReadOnly ? FILE_MAP_READ : FILE_MAP_WRITE; nuclear@0: nuclear@0: // Open file mapping nuclear@0: HANDLE hFileMapping = OpenFileMappingA(mapDesiredAccess, TRUE, fileName); nuclear@0: nuclear@0: // If file was mapped unsuccessfully, nuclear@0: if (NULL == hFileMapping) nuclear@0: { nuclear@0: OVR_DEBUG_LOG(("[SharedMemory] WARNING: Unable to open file mapping for %s error code = %d (not necessarily bad)", fileName, GetLastError())); nuclear@0: return NULL; nuclear@0: } nuclear@0: nuclear@0: // Map the file nuclear@0: return DoFileMap(hFileMapping, fileName, openReadOnly, minSize); nuclear@0: } nuclear@0: nuclear@0: SharedMemoryInternal* SharedMemoryInternal::AttemptCreateSharedMemory(const char* fileName, int minSize, bool openReadOnly, bool allowRemoteWrite) nuclear@0: { nuclear@0: // Prepare a SECURITY_ATTRIBUTES object nuclear@0: SECURITY_ATTRIBUTES security; nuclear@0: ZeroMemory(&security, sizeof(security)); nuclear@0: security.nLength = sizeof(security); nuclear@0: nuclear@0: // Security descriptor by DACL strings: nuclear@0: // ACE strings grant Allow(A), Object/Contains Inheritance (OICI) of: nuclear@0: // + Grant All (GA) to System (SY) nuclear@0: // + Grant All (GA) to Built-in Administrators (BA) nuclear@0: // + Grant Read-Only (GR) or Read-Write (GWGR) to Interactive Users (IU) - ie. games nuclear@0: static const char* DACLString_ReadOnly = "D:P(A;OICI;GA;;;SY)(A;OICI;GA;;;BA)(A;OICI;GR;;;IU)"; nuclear@0: static const char* DACLString_ReadWrite = "D:P(A;OICI;GA;;;SY)(A;OICI;GA;;;BA)(A;OICI;GWGR;;;IU)"; nuclear@0: nuclear@0: // Select the remote process access mode nuclear@0: const char* remoteAccessString = nuclear@0: allowRemoteWrite ? DACLString_ReadWrite : DACLString_ReadOnly; nuclear@0: nuclear@0: // Attempt to convert access string to security attributes nuclear@0: // Note: This will allocate the security descriptor with LocalAlloc() and must be freed later nuclear@0: BOOL bConvertOkay = ConvertStringSecurityDescriptorToSecurityDescriptorA( nuclear@0: remoteAccessString, SDDL_REVISION_1, &security.lpSecurityDescriptor, NULL); nuclear@0: nuclear@0: // If conversion fails, nuclear@0: if (!bConvertOkay) nuclear@0: { nuclear@0: OVR_DEBUG_LOG(("[SharedMemory] FAILURE: Unable to convert access string, error code = %d", GetLastError())); nuclear@0: return NULL; nuclear@0: } nuclear@0: nuclear@0: // Interpret the access mode as a page protection code nuclear@0: int pageProtectCode = openReadOnly ? PAGE_READONLY : PAGE_READWRITE; nuclear@0: nuclear@0: // Attempt to create a file mapping nuclear@0: HANDLE hFileMapping = CreateFileMappingA(INVALID_HANDLE_VALUE, // From page file nuclear@0: &security, // Security attributes nuclear@0: pageProtectCode, // Read-only? nuclear@0: 0, // High word for size = 0 nuclear@0: minSize, // Low word for size nuclear@0: fileName); // Name of global shared memory file nuclear@0: nuclear@0: // Free the security descriptor buffer nuclear@0: LocalFree(security.lpSecurityDescriptor); nuclear@0: nuclear@0: // If mapping could not be created, nuclear@0: if (NULL == hFileMapping) nuclear@0: { nuclear@0: OVR_DEBUG_LOG(("[SharedMemory] FAILURE: Unable to create file mapping for %s error code = %d", fileName, GetLastError())); nuclear@0: return NULL; nuclear@0: } nuclear@0: nuclear@0: #ifndef OVR_ALLOW_CREATE_FILE_MAPPING_IF_EXISTS nuclear@0: // If the file mapping already exists, nuclear@0: if (GetLastError() == ERROR_ALREADY_EXISTS) nuclear@0: { nuclear@0: CloseHandle(hFileMapping); nuclear@0: nuclear@0: OVR_DEBUG_LOG(("[SharedMemory] FAILURE: File mapping at %s already exists", fileName)); nuclear@0: return NULL; nuclear@0: } nuclear@0: #endif nuclear@0: nuclear@0: // Map the file nuclear@0: return DoFileMap(hFileMapping, fileName, openReadOnly, minSize); nuclear@0: } nuclear@0: nuclear@0: SharedMemoryInternal* SharedMemoryInternal::CreateSharedMemory(const SharedMemory::OpenParameters& params) nuclear@0: { nuclear@0: SharedMemoryInternal* retval = NULL; nuclear@0: nuclear@0: // Construct the file mapping name in a Windows-specific way nuclear@0: OVR::String fileMappingName = params.globalName; nuclear@0: const char *fileName = fileMappingName.ToCStr(); nuclear@0: nuclear@0: // Is being opened read-only? nuclear@0: const bool openReadOnly = (params.accessMode == SharedMemory::AccessMode_ReadOnly); nuclear@0: nuclear@0: // Try up to 3 times to reduce low-probability failures: nuclear@0: static const int ATTEMPTS_MAX = 3; nuclear@0: for (int attempts = 0; attempts < ATTEMPTS_MAX; ++attempts) nuclear@0: { nuclear@0: // If opening should be attempted first, nuclear@0: if (params.openMode != SharedMemory::OpenMode_CreateOnly) nuclear@0: { nuclear@0: // Attempt to open a shared memory map nuclear@0: retval = AttemptOpenSharedMemory(fileName, params.minSizeBytes, openReadOnly); nuclear@0: nuclear@0: // If successful, nuclear@0: if (retval) nuclear@0: { nuclear@0: // Done! nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // If creating the shared memory is also acceptable, nuclear@0: if (params.openMode != SharedMemory::OpenMode_OpenOnly) nuclear@0: { nuclear@0: // Interpret create mode nuclear@0: const bool allowRemoteWrite = (params.remoteMode == SharedMemory::RemoteMode_ReadWrite); nuclear@0: nuclear@0: // Attempt to create a shared memory map nuclear@0: retval = AttemptCreateSharedMemory(fileName, params.minSizeBytes, openReadOnly, allowRemoteWrite); nuclear@0: nuclear@0: // If successful, nuclear@0: if (retval) nuclear@0: { nuclear@0: // Done! nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: } // Re-attempt create/open nuclear@0: nuclear@0: // Note: On Windows the initial contents of the region are guaranteed to be zero. nuclear@0: return retval; nuclear@0: } nuclear@0: nuclear@0: #endif // OVR_OS_WIN32 nuclear@0: nuclear@0: nuclear@0: #if (defined(OVR_OS_LINUX) || defined(OVR_OS_MAC)) && !defined(OVR_FAKE_SHAREDMEMORY) nuclear@0: nuclear@0: // Hidden implementation class for OS-specific behavior nuclear@0: class SharedMemoryInternal nuclear@0: { nuclear@0: public: nuclear@0: int FileMapping; nuclear@0: void* FileView; nuclear@0: int FileSize; nuclear@0: nuclear@0: SharedMemoryInternal(int fileMapping, void* fileView, int fileSize) : nuclear@0: FileMapping(fileMapping), nuclear@0: FileView(fileView), nuclear@0: FileSize(fileSize) nuclear@0: { nuclear@0: } nuclear@0: nuclear@0: ~SharedMemoryInternal() nuclear@0: { nuclear@0: // If file view is set, nuclear@0: if (FileView) nuclear@0: { nuclear@0: munmap(FileView, FileSize); nuclear@0: FileView = MAP_FAILED; nuclear@0: } nuclear@0: nuclear@0: // If file mapping is set, nuclear@0: if (FileMapping >= 0) nuclear@0: { nuclear@0: close(FileMapping); nuclear@0: FileMapping = -1; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: static SharedMemoryInternal* DoFileMap(int hFileMapping, const char* fileName, bool openReadOnly, int minSize); nuclear@0: static SharedMemoryInternal* AttemptOpenSharedMemory(const char* fileName, int minSize, bool openReadOnly); nuclear@0: static SharedMemoryInternal* AttemptCreateSharedMemory(const char* fileName, int minSize, bool openReadOnly, bool allowRemoteWrite); nuclear@0: static SharedMemoryInternal* CreateSharedMemory(const SharedMemory::OpenParameters& params); nuclear@0: }; nuclear@0: nuclear@0: SharedMemoryInternal* SharedMemoryInternal::DoFileMap(int hFileMapping, const char* fileName, bool openReadOnly, int minSize) nuclear@0: { nuclear@0: // Calculate the required flags based on read/write mode nuclear@0: int prot = openReadOnly ? PROT_READ : (PROT_READ|PROT_WRITE); nuclear@0: nuclear@0: // Map the file view nuclear@0: void* pFileView = mmap(NULL, minSize, prot, MAP_SHARED, hFileMapping, 0); nuclear@0: nuclear@0: if (pFileView == MAP_FAILED) nuclear@0: { nuclear@0: close(hFileMapping); nuclear@0: nuclear@0: OVR_DEBUG_LOG(("[SharedMemory] FAILURE: Unable to map view of file for %s error code = %d", fileName, errno)); nuclear@0: OVR_UNUSED(fileName); nuclear@0: return NULL; nuclear@0: } nuclear@0: nuclear@0: // Create internal representation nuclear@0: SharedMemoryInternal* pimple = new SharedMemoryInternal(hFileMapping, pFileView, minSize); nuclear@0: nuclear@0: // If memory allocation fails, nuclear@0: if (!pimple) nuclear@0: { nuclear@0: munmap(pFileView, minSize); nuclear@0: close(hFileMapping); nuclear@0: nuclear@0: OVR_DEBUG_LOG(("[SharedMemory] FAILURE: Out of memory")); nuclear@0: return NULL; nuclear@0: } nuclear@0: nuclear@0: return pimple; nuclear@0: } nuclear@0: nuclear@0: SharedMemoryInternal* SharedMemoryInternal::AttemptOpenSharedMemory(const char* fileName, int minSize, bool openReadOnly) nuclear@0: { nuclear@0: // Calculate permissions and flags based on read/write mode nuclear@0: int flags = openReadOnly ? O_RDONLY : O_RDWR; nuclear@0: int perms = openReadOnly ? S_IRUSR : (S_IRUSR | S_IWUSR); nuclear@0: nuclear@0: // Attempt to open the shared memory file nuclear@0: int hFileMapping = shm_open(fileName, flags, perms); nuclear@0: nuclear@0: // If file was not opened successfully, nuclear@0: if (hFileMapping < 0) nuclear@0: { nuclear@0: OVR_DEBUG_LOG(("[SharedMemory] WARNING: Unable to open file mapping for %s error code = %d (not necessarily bad)", fileName, errno)); nuclear@0: return NULL; nuclear@0: } nuclear@0: nuclear@0: // Map the file nuclear@0: return DoFileMap(hFileMapping, fileName, openReadOnly, minSize); nuclear@0: } nuclear@0: nuclear@0: SharedMemoryInternal* SharedMemoryInternal::AttemptCreateSharedMemory(const char* fileName, int minSize, bool openReadOnly, bool allowRemoteWrite) nuclear@0: { nuclear@0: // Create mode nuclear@0: // Note: Cannot create the shared memory file read-only because then ftruncate() will fail. nuclear@0: int flags = O_CREAT | O_RDWR; nuclear@0: nuclear@0: #ifndef OVR_ALLOW_CREATE_FILE_MAPPING_IF_EXISTS nuclear@0: // Require exclusive access when creating (seems like a good idea without trying it yet..) nuclear@0: if (shm_unlink(fileName) < 0) nuclear@0: { nuclear@0: OVR_DEBUG_LOG(("[SharedMemory] WARNING: Unable to unlink shared memory file %s error code = %d", fileName, errno)); nuclear@0: } nuclear@0: flags |= O_EXCL; nuclear@0: #endif nuclear@0: nuclear@0: // Set own read/write permissions nuclear@0: int perms = openReadOnly ? S_IRUSR : (S_IRUSR|S_IWUSR); nuclear@0: nuclear@0: // Allow other users to read/write the shared memory file nuclear@0: perms |= allowRemoteWrite ? (S_IWGRP|S_IWOTH|S_IRGRP|S_IROTH) : (S_IRGRP|S_IROTH); nuclear@0: nuclear@0: // Attempt to open the shared memory file nuclear@0: int hFileMapping = shm_open(fileName, flags, perms); nuclear@0: nuclear@0: // If file was not opened successfully, nuclear@0: if (hFileMapping < 0) nuclear@0: { nuclear@0: OVR_DEBUG_LOG(("[SharedMemory] FAILURE: Unable to create file mapping for %s error code = %d", fileName, errno)); nuclear@0: return NULL; nuclear@0: } nuclear@0: nuclear@0: int truncRes = ftruncate(hFileMapping, minSize); nuclear@0: nuclear@0: // If file was not opened successfully, nuclear@0: if (truncRes < 0) nuclear@0: { nuclear@0: close(hFileMapping); nuclear@0: OVR_DEBUG_LOG(("[SharedMemory] FAILURE: Unable to truncate file for %s to %d error code = %d", fileName, minSize, errno)); nuclear@0: return NULL; nuclear@0: } nuclear@0: nuclear@0: // Map the file nuclear@0: return DoFileMap(hFileMapping, fileName, openReadOnly, minSize); nuclear@0: } nuclear@0: nuclear@0: SharedMemoryInternal* SharedMemoryInternal::CreateSharedMemory(const SharedMemory::OpenParameters& params) nuclear@0: { nuclear@0: SharedMemoryInternal* retval = NULL; nuclear@0: nuclear@0: // Construct the file mapping name in a Linux-specific way nuclear@0: OVR::String fileMappingName = "/"; nuclear@0: fileMappingName += params.globalName; nuclear@0: const char *fileName = fileMappingName.ToCStr(); nuclear@0: nuclear@0: // Is being opened read-only? nuclear@0: const bool openReadOnly = (params.accessMode == SharedMemory::AccessMode_ReadOnly); nuclear@0: nuclear@0: // Try up to 3 times to reduce low-probability failures: nuclear@0: static const int ATTEMPTS_MAX = 3; nuclear@0: for (int attempts = 0; attempts < ATTEMPTS_MAX; ++attempts) nuclear@0: { nuclear@0: // If opening should be attempted first, nuclear@0: if (params.openMode != SharedMemory::OpenMode_CreateOnly) nuclear@0: { nuclear@0: // Attempt to open a shared memory map nuclear@0: retval = AttemptOpenSharedMemory(fileName, params.minSizeBytes, openReadOnly); nuclear@0: nuclear@0: // If successful, nuclear@0: if (retval) nuclear@0: { nuclear@0: // Done! nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // If creating the shared memory is also acceptable, nuclear@0: if (params.openMode != SharedMemory::OpenMode_OpenOnly) nuclear@0: { nuclear@0: // Interpret create mode nuclear@0: const bool allowRemoteWrite = (params.remoteMode == SharedMemory::RemoteMode_ReadWrite); nuclear@0: nuclear@0: // Attempt to create a shared memory map nuclear@0: retval = AttemptCreateSharedMemory(fileName, params.minSizeBytes, openReadOnly, allowRemoteWrite); nuclear@0: nuclear@0: // If successful, nuclear@0: if (retval) nuclear@0: { nuclear@0: // Done! nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: } // Re-attempt create/open nuclear@0: nuclear@0: // Note: On Windows the initial contents of the region are guaranteed to be zero. nuclear@0: return retval; nuclear@0: } nuclear@0: nuclear@0: #endif // OVR_OS_LINUX nuclear@0: nuclear@0: nuclear@0: //// SharedMemory nuclear@0: nuclear@0: SharedMemory::SharedMemory(int size, void* data, SharedMemoryInternal* pInternal) : nuclear@0: Size(size), nuclear@0: Data(data), nuclear@0: Internal(pInternal) nuclear@0: { nuclear@0: } nuclear@0: // Call close when it goes out of scope nuclear@0: SharedMemory::~SharedMemory() nuclear@0: { nuclear@0: Close(); nuclear@0: delete Internal; nuclear@0: } nuclear@0: nuclear@0: void SharedMemory::Close() nuclear@0: { nuclear@0: if (Internal) nuclear@0: { nuclear@0: delete Internal; nuclear@0: Internal = NULL; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: nuclear@0: //// SharedMemoryFactory nuclear@0: nuclear@0: Ptr SharedMemoryFactory::Open(const SharedMemory::OpenParameters& params) nuclear@0: { nuclear@0: Ptr retval; nuclear@0: nuclear@0: // If no name specified or no size requested, nuclear@0: if (!params.globalName || (params.minSizeBytes <= 0)) nuclear@0: { nuclear@0: OVR_DEBUG_LOG(("[SharedMemory] FAILURE: Invalid parameters to Create()")); nuclear@0: return NULL; nuclear@0: } nuclear@0: nuclear@0: OVR_DEBUG_LOG(("[SharedMemory] Creating shared memory region: %s > %d bytes", nuclear@0: params.globalName, params.minSizeBytes)); nuclear@0: nuclear@0: // Attempt to create a shared memory region from the parameters nuclear@0: SharedMemoryInternal* pInternal = SharedMemoryInternal::CreateSharedMemory(params); nuclear@0: nuclear@0: if (pInternal) nuclear@0: { nuclear@0: // Create the wrapper object nuclear@0: retval = *new SharedMemory(params.minSizeBytes, pInternal->FileView, pInternal); nuclear@0: } nuclear@0: nuclear@0: return retval; nuclear@0: } nuclear@0: nuclear@0: SharedMemoryFactory::SharedMemoryFactory() nuclear@0: { nuclear@0: OVR_DEBUG_LOG(("[SharedMemory] Creating factory")); nuclear@0: nuclear@0: PushDestroyCallbacks(); nuclear@0: } nuclear@0: nuclear@0: SharedMemoryFactory::~SharedMemoryFactory() nuclear@0: { nuclear@0: OVR_DEBUG_LOG(("[SharedMemory] Destroying factory")); nuclear@0: } nuclear@0: nuclear@0: void SharedMemoryFactory::OnSystemDestroy() nuclear@0: { nuclear@0: delete this; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: } // namespace OVR