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