205 lines
5.8 KiB
C++
205 lines
5.8 KiB
C++
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
|
|
// Copyright (C) 2010 Winch Gate Property Limited
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License as
|
|
// published by the Free Software Foundation, either version 3 of the
|
|
// License, or (at your option) any later version.
|
|
//
|
|
// This program 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 Affero General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
#include "stdmisc.h"
|
|
|
|
#include "nel/misc/shared_memory.h"
|
|
#include "nel/misc/debug.h"
|
|
|
|
#ifndef NL_OS_WINDOWS
|
|
# include <sys/types.h>
|
|
# include <sys/ipc.h>
|
|
# include <sys/shm.h>
|
|
#endif
|
|
|
|
using namespace std;
|
|
|
|
#ifdef DEBUG_NEW
|
|
#define new DEBUG_NEW
|
|
#endif
|
|
|
|
namespace NLMISC {
|
|
|
|
|
|
#ifdef NL_OS_WINDOWS
|
|
// Storage for file handles, necessary to close the handles
|
|
map<void*,HANDLE> AccessAddressesToHandles;
|
|
#else
|
|
// Storage for shmid, necessary to destroy the segments
|
|
map<TSharedMemId, sint> SharedMemIdsToShmids;
|
|
#endif
|
|
|
|
|
|
/*
|
|
* Create a shared memory segment
|
|
*/
|
|
void *CSharedMemory::createSharedMemory( TSharedMemId sharedMemId, uint32 size )
|
|
{
|
|
#ifdef NL_OS_WINDOWS
|
|
|
|
// Create a file mapping backed by the virtual memory swap file (not a data file)
|
|
HANDLE hMapFile = CreateFileMapping( INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, size, sharedMemId );
|
|
if ( (hMapFile == NULL) || (GetLastError() == ERROR_ALREADY_EXISTS) )
|
|
{
|
|
nlwarning( "SHDMEM: Cannot create file mapping for smid %s: error %u%s, mapFile %p", sharedMemId, GetLastError(), (GetLastError()==ERROR_ALREADY_EXISTS) ? " (already exists) ": "", hMapFile );
|
|
return NULL;
|
|
}
|
|
//else
|
|
// nldebug( "SHDMEM: Creating smid %s --> mapFile %p", sharedMemId, hMapFile );
|
|
|
|
|
|
// Map the file into memory address space
|
|
void *accessAddress = MapViewOfFile( hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, 0 );
|
|
AccessAddressesToHandles.insert( make_pair( accessAddress, hMapFile ) );
|
|
/*if ( accessAddress == NULL )
|
|
{
|
|
nlwarning( "SHDMEM: Cannot map view of file: error %u", GetLastError() );
|
|
}*/
|
|
return accessAddress;
|
|
|
|
#else
|
|
|
|
// Create a shared memory segment
|
|
sint shmid = shmget( sharedMemId, size, IPC_CREAT | IPC_EXCL | 0666 );
|
|
if ( shmid == -1 )
|
|
return NULL;
|
|
SharedMemIdsToShmids.insert( make_pair( sharedMemId, shmid ) );
|
|
|
|
// Map the segment into memory address space
|
|
void *accessAddress = (void*)shmat( shmid, 0, 0 );
|
|
if ( accessAddress == (void*)-1 )
|
|
return NULL;
|
|
else
|
|
return accessAddress;
|
|
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
* Get access to an existing shared memory segment
|
|
*/
|
|
void *CSharedMemory::accessSharedMemory( TSharedMemId sharedMemId )
|
|
{
|
|
#ifdef NL_OS_WINDOWS
|
|
|
|
// Open the existing file mapping by name
|
|
HANDLE hMapFile = OpenFileMapping( FILE_MAP_ALL_ACCESS, false, sharedMemId );
|
|
if ( hMapFile == NULL )
|
|
return NULL;
|
|
//nldebug( "SHDMEM: Opening smid %s --> mapFile %p", sharedMemId, hMapFile );
|
|
|
|
// Map the file into memory address space
|
|
void *accessAddress = MapViewOfFile( hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, 0 );
|
|
AccessAddressesToHandles.insert( make_pair( accessAddress, hMapFile ) );
|
|
return accessAddress;
|
|
|
|
#else
|
|
|
|
// Open an existing shared memory segment
|
|
int shmid = shmget( sharedMemId, 0, 0666 );
|
|
if ( shmid == -1 )
|
|
return NULL;
|
|
|
|
// Map the segment into memory address space
|
|
void *accessAddress = (void*)shmat( shmid, 0, 0 );
|
|
if ( accessAddress == (void*)-1 )
|
|
return NULL;
|
|
else
|
|
return accessAddress;
|
|
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
* Close (detach) a shared memory segment
|
|
*/
|
|
bool CSharedMemory::closeSharedMemory( void *accessAddress )
|
|
{
|
|
#ifdef NL_OS_WINDOWS
|
|
|
|
bool result = true;
|
|
|
|
// Unmap the file from memory address space
|
|
if ( UnmapViewOfFile( accessAddress ) == 0 )
|
|
{
|
|
nlwarning( "SHDMEM: UnmapViewOfFile failed: error %u", GetLastError() );
|
|
result = false;
|
|
}
|
|
|
|
// Close the corresponding handle
|
|
map<void*,HANDLE>::iterator im = AccessAddressesToHandles.find( accessAddress );
|
|
if ( im != AccessAddressesToHandles.end() )
|
|
{
|
|
//nldebug( "SHDMEM: CloseHandle mapFile %u", (*im).second );
|
|
if ( ! CloseHandle( (*im).second ) )
|
|
nlwarning( "SHDMEM: CloseHandle failed: error %u, mapFile %u", GetLastError(), (*im).second );
|
|
AccessAddressesToHandles.erase( im );
|
|
return result;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
|
|
#else
|
|
|
|
// Detach the shared memory segment
|
|
return ( shmdt( accessAddress ) != -1 );
|
|
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
* Destroy a shared memory segment (to call only by the process that created the segment)
|
|
* Note: does nothing under Windows, it is automatic.
|
|
* "Rescue feature": set "force" to true if a segment was created and left out of
|
|
* control (meaning a new createSharedMemory() with the same sharedMemId fails), but
|
|
* before, make sure the segment really belongs to you!
|
|
*
|
|
* Note: this method does nothing under Windows, destroying is automatic.
|
|
* Under Unix, the segment will actually be destroyed after the last detach
|
|
* (quoting shmctl man page). It means after calling destroySharedMemory(), the
|
|
* segment is still accessible by another process until it calls closeSharedMemory().
|
|
*/
|
|
void CSharedMemory::destroySharedMemory( TSharedMemId sharedMemId, bool force )
|
|
{
|
|
#ifndef NL_OS_WINDOWS
|
|
// Set the segment to auto-destroying (when the last process detaches)
|
|
map<TSharedMemId,int>::iterator im = SharedMemIdsToShmids.find( sharedMemId );
|
|
if ( im != SharedMemIdsToShmids.end() )
|
|
{
|
|
// Destroy the segment created before
|
|
shmctl( (*im).second, IPC_RMID, 0 );
|
|
SharedMemIdsToShmids.erase( im );
|
|
}
|
|
else if ( force )
|
|
{
|
|
// Open and destroy the segment
|
|
int shmid = shmget( sharedMemId, 0, 0666 );
|
|
if ( shmid != -1 )
|
|
{
|
|
// Destroy the segment
|
|
shmctl( shmid, IPC_RMID, 0 );
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
} // NLMISC
|