/** * @file llpluginsharedmemory.cpp * LLPluginSharedMemory manages a shared memory segment for use by the LLPlugin API. * * @cond * $LicenseInfo:firstyear=2008&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ * @endcond */ #include "linden_common.h" #include "llpluginsharedmemory.h" // on Mac and Linux, we use the native shm_open/mmap interface by using // #define USE_SHM_OPEN_SHARED_MEMORY 1 // in the appropriate sections below. // For Windows, use: // #define USE_WIN32_SHARED_MEMORY 1 // If we ever want to fall back to the apr implementation for a platform, use: // #define USE_APR_SHARED_MEMORY 1 #if LL_WINDOWS // #define USE_APR_SHARED_MEMORY 1 #define USE_WIN32_SHARED_MEMORY 1 #elif LL_DARWIN #define USE_SHM_OPEN_SHARED_MEMORY 1 #elif LL_LINUX #define USE_SHM_OPEN_SHARED_MEMORY 1 #endif // FIXME: This path thing is evil and unacceptable. #if LL_WINDOWS #define APR_SHARED_MEMORY_PREFIX_STRING "C:\\LLPlugin_" // Apparnently using the "Global\\" prefix here only works from administrative accounts under Vista. // Other options I've seen referenced are "Local\\" and "Session\\". #define WIN32_SHARED_MEMORY_PREFIX_STRING "Local\\LL_" #else // mac and linux #define APR_SHARED_MEMORY_PREFIX_STRING "/tmp/LLPlugin_" #define SHM_OPEN_SHARED_MEMORY_PREFIX_STRING "/LL" #endif #if USE_APR_SHARED_MEMORY #include "llapr.h" #include "apr_shm.h" #elif USE_SHM_OPEN_SHARED_MEMORY #include <sys/fcntl.h> #include <sys/mman.h> #include <errno.h> #elif USE_WIN32_SHARED_MEMORY #include <windows.h> #endif // USE_APR_SHARED_MEMORY int LLPluginSharedMemory::sSegmentNumber = 0; std::string LLPluginSharedMemory::createName(void) { std::stringstream newname; #if LL_WINDOWS newname << GetCurrentProcessId(); #else // LL_WINDOWS newname << getpid(); #endif // LL_WINDOWS newname << "_" << sSegmentNumber++; return newname.str(); } /** * @brief LLPluginSharedMemoryImpl is the platform-dependent implementation of LLPluginSharedMemory. TODO:DOC is this necessary/sufficient? kinda obvious. * */ class LLPluginSharedMemoryPlatformImpl { public: LLPluginSharedMemoryPlatformImpl(); ~LLPluginSharedMemoryPlatformImpl(); #if USE_APR_SHARED_MEMORY apr_shm_t* mAprSharedMemory; #elif USE_SHM_OPEN_SHARED_MEMORY int mSharedMemoryFD; #elif USE_WIN32_SHARED_MEMORY HANDLE mMapFile; #endif }; /** * Constructor. Creates a shared memory segment. */ LLPluginSharedMemory::LLPluginSharedMemory() { mSize = 0; mMappedAddress = NULL; mNeedsDestroy = false; mImpl = new LLPluginSharedMemoryPlatformImpl; } /** * Destructor. Uses destroy() and detach() to ensure shared memory segment is cleaned up. */ LLPluginSharedMemory::~LLPluginSharedMemory() { if(mNeedsDestroy) destroy(); else detach(); unlink(); delete mImpl; } #if USE_APR_SHARED_MEMORY // MARK: apr implementation LLPluginSharedMemoryPlatformImpl::LLPluginSharedMemoryPlatformImpl() { mAprSharedMemory = NULL; } LLPluginSharedMemoryPlatformImpl::~LLPluginSharedMemoryPlatformImpl() { } bool LLPluginSharedMemory::map(void) { mMappedAddress = apr_shm_baseaddr_get(mImpl->mAprSharedMemory); if(mMappedAddress == NULL) { return false; } return true; } bool LLPluginSharedMemory::unmap(void) { // This is a no-op under apr. return true; } bool LLPluginSharedMemory::close(void) { // This is a no-op under apr. return true; } bool LLPluginSharedMemory::unlink(void) { // This is a no-op under apr. return true; } bool LLPluginSharedMemory::create(size_t size) { mName = APR_SHARED_MEMORY_PREFIX_STRING; mName += createName(); mSize = size; apr_status_t status = apr_shm_create( &(mImpl->mAprSharedMemory), mSize, mName.c_str(), gAPRPoolp ); if(ll_apr_warn_status(status)) { return false; } mNeedsDestroy = true; return map(); } bool LLPluginSharedMemory::destroy(void) { if(mImpl->mAprSharedMemory) { apr_status_t status = apr_shm_destroy(mImpl->mAprSharedMemory); if(ll_apr_warn_status(status)) { // TODO: Is this a fatal error? I think not... } mImpl->mAprSharedMemory = NULL; } return true; } bool LLPluginSharedMemory::attach(const std::string &name, size_t size) { mName = name; mSize = size; apr_status_t status = apr_shm_attach( &(mImpl->mAprSharedMemory), mName.c_str(), gAPRPoolp ); if(ll_apr_warn_status(status)) { return false; } return map(); } bool LLPluginSharedMemory::detach(void) { if(mImpl->mAprSharedMemory) { apr_status_t status = apr_shm_detach(mImpl->mAprSharedMemory); if(ll_apr_warn_status(status)) { // TODO: Is this a fatal error? I think not... } mImpl->mAprSharedMemory = NULL; } return true; } #elif USE_SHM_OPEN_SHARED_MEMORY // MARK: shm_open/mmap implementation LLPluginSharedMemoryPlatformImpl::LLPluginSharedMemoryPlatformImpl() { mSharedMemoryFD = -1; } LLPluginSharedMemoryPlatformImpl::~LLPluginSharedMemoryPlatformImpl() { } bool LLPluginSharedMemory::map(void) { mMappedAddress = ::mmap(NULL, mSize, PROT_READ | PROT_WRITE, MAP_SHARED, mImpl->mSharedMemoryFD, 0); if(mMappedAddress == NULL) { return false; } LL_DEBUGS("Plugin") << "memory mapped at " << mMappedAddress << LL_ENDL; return true; } bool LLPluginSharedMemory::unmap(void) { if(mMappedAddress != NULL) { LL_DEBUGS("Plugin") << "calling munmap(" << mMappedAddress << ", " << mSize << ")" << LL_ENDL; if(::munmap(mMappedAddress, mSize) == -1) { // TODO: Is this a fatal error? I think not... } mMappedAddress = NULL; } return true; } bool LLPluginSharedMemory::close(void) { if(mImpl->mSharedMemoryFD != -1) { LL_DEBUGS("Plugin") << "calling close(" << mImpl->mSharedMemoryFD << ")" << LL_ENDL; if(::close(mImpl->mSharedMemoryFD) == -1) { // TODO: Is this a fatal error? I think not... } mImpl->mSharedMemoryFD = -1; } return true; } bool LLPluginSharedMemory::unlink(void) { if(!mName.empty()) { if(::shm_unlink(mName.c_str()) == -1) { return false; } } return true; } bool LLPluginSharedMemory::create(size_t size) { mName = SHM_OPEN_SHARED_MEMORY_PREFIX_STRING; mName += createName(); mSize = size; // Preemptive unlink, just in case something didn't get cleaned up. unlink(); mImpl->mSharedMemoryFD = ::shm_open(mName.c_str(), O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); if(mImpl->mSharedMemoryFD == -1) { return false; } mNeedsDestroy = true; if(::ftruncate(mImpl->mSharedMemoryFD, mSize) == -1) { return false; } return map(); } bool LLPluginSharedMemory::destroy(void) { unmap(); close(); return true; } bool LLPluginSharedMemory::attach(const std::string &name, size_t size) { mName = name; mSize = size; mImpl->mSharedMemoryFD = ::shm_open(mName.c_str(), O_RDWR, S_IRUSR | S_IWUSR); if(mImpl->mSharedMemoryFD == -1) { return false; } // unlink here so the segment will be cleaned up automatically after the last close. unlink(); return map(); } bool LLPluginSharedMemory::detach(void) { unmap(); close(); return true; } #elif USE_WIN32_SHARED_MEMORY // MARK: Win32 CreateFileMapping-based implementation // Reference: http://msdn.microsoft.com/en-us/library/aa366551(VS.85).aspx LLPluginSharedMemoryPlatformImpl::LLPluginSharedMemoryPlatformImpl() { mMapFile = NULL; } LLPluginSharedMemoryPlatformImpl::~LLPluginSharedMemoryPlatformImpl() { } bool LLPluginSharedMemory::map(void) { mMappedAddress = MapViewOfFile( mImpl->mMapFile, // handle to map object FILE_MAP_ALL_ACCESS, // read/write permission 0, 0, mSize); if(mMappedAddress == NULL) { LL_WARNS("Plugin") << "MapViewOfFile failed: " << GetLastError() << LL_ENDL; return false; } LL_DEBUGS("Plugin") << "memory mapped at " << mMappedAddress << LL_ENDL; return true; } bool LLPluginSharedMemory::unmap(void) { if(mMappedAddress != NULL) { UnmapViewOfFile(mMappedAddress); mMappedAddress = NULL; } return true; } bool LLPluginSharedMemory::close(void) { if(mImpl->mMapFile != NULL) { CloseHandle(mImpl->mMapFile); mImpl->mMapFile = NULL; } return true; } bool LLPluginSharedMemory::unlink(void) { // This is a no-op on Windows. return true; } bool LLPluginSharedMemory::create(size_t size) { mName = WIN32_SHARED_MEMORY_PREFIX_STRING; mName += createName(); mSize = size; mImpl->mMapFile = CreateFileMappingA( INVALID_HANDLE_VALUE, // use paging file NULL, // default security PAGE_READWRITE, // read/write access 0, // max. object size mSize, // buffer size mName.c_str()); // name of mapping object if(mImpl->mMapFile == NULL) { LL_WARNS("Plugin") << "CreateFileMapping failed: " << GetLastError() << LL_ENDL; return false; } mNeedsDestroy = true; return map(); } bool LLPluginSharedMemory::destroy(void) { unmap(); close(); return true; } bool LLPluginSharedMemory::attach(const std::string &name, size_t size) { mName = name; mSize = size; mImpl->mMapFile = OpenFileMappingA( FILE_MAP_ALL_ACCESS, // read/write access FALSE, // do not inherit the name mName.c_str()); // name of mapping object if(mImpl->mMapFile == NULL) { LL_WARNS("Plugin") << "OpenFileMapping failed: " << GetLastError() << LL_ENDL; return false; } return map(); } bool LLPluginSharedMemory::detach(void) { unmap(); close(); return true; } #endif