1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2015 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "nel/misc/shared_memory.h"
23 #include "nel/misc/debug.h"
26 # include <sys/types.h>
41 // Storage for file handles, necessary to close the handles
42 map
<void*,HANDLE
> AccessAddressesToHandles
;
44 // Storage for shmid, necessary to destroy the segments
45 map
<TSharedMemId
, sint
> SharedMemIdsToShmids
;
50 * Create a shared memory segment
52 void *CSharedMemory::createSharedMemory( TSharedMemId sharedMemId
, uint32 size
)
56 // Create a file mapping backed by the virtual memory swap file (not a data file)
57 HANDLE hMapFile
= CreateFileMappingA( INVALID_HANDLE_VALUE
, NULL
, PAGE_READWRITE
, 0, size
, sharedMemId
);
58 if ( (hMapFile
== NULL
) || (GetLastError() == ERROR_ALREADY_EXISTS
) )
60 nlwarning( "SHDMEM: Cannot create file mapping for smid %s: error %u%s, mapFile %p", sharedMemId
, GetLastError(), (GetLastError()==ERROR_ALREADY_EXISTS
) ? " (already exists) ": "", hMapFile
);
64 // nldebug( "SHDMEM: Creating smid %s --> mapFile %p", sharedMemId, hMapFile );
67 // Map the file into memory address space
68 void *accessAddress
= MapViewOfFile( hMapFile
, FILE_MAP_ALL_ACCESS
, 0, 0, 0 );
69 AccessAddressesToHandles
.insert( make_pair( accessAddress
, hMapFile
) );
70 /*if ( accessAddress == NULL )
72 nlwarning( "SHDMEM: Cannot map view of file: error %u", GetLastError() );
78 // Create a shared memory segment
79 sint shmid
= shmget( sharedMemId
, size
, IPC_CREAT
| IPC_EXCL
| 0666 );
82 SharedMemIdsToShmids
.insert( make_pair( sharedMemId
, shmid
) );
84 // Map the segment into memory address space
85 void *accessAddress
= (void*)shmat( shmid
, 0, 0 );
86 if ( accessAddress
== (void*)-1 )
96 * Get access to an existing shared memory segment
98 void *CSharedMemory::accessSharedMemory( TSharedMemId sharedMemId
)
102 // Open the existing file mapping by name
103 HANDLE hMapFile
= OpenFileMappingA( FILE_MAP_ALL_ACCESS
, false, sharedMemId
);
104 if ( hMapFile
== NULL
)
106 //nldebug( "SHDMEM: Opening smid %s --> mapFile %p", sharedMemId, hMapFile );
108 // Map the file into memory address space
109 void *accessAddress
= MapViewOfFile( hMapFile
, FILE_MAP_ALL_ACCESS
, 0, 0, 0 );
110 AccessAddressesToHandles
.insert( make_pair( accessAddress
, hMapFile
) );
111 return accessAddress
;
115 // Open an existing shared memory segment
116 int shmid
= shmget( sharedMemId
, 0, 0666 );
120 // Map the segment into memory address space
121 void *accessAddress
= (void*)shmat( shmid
, 0, 0 );
122 if ( accessAddress
== (void*)-1 )
125 return accessAddress
;
132 * Close (detach) a shared memory segment
134 bool CSharedMemory::closeSharedMemory( void *accessAddress
)
140 // Unmap the file from memory address space
141 if ( UnmapViewOfFile( accessAddress
) == 0 )
143 nlwarning( "SHDMEM: UnmapViewOfFile failed: error %u", GetLastError() );
147 // Close the corresponding handle
148 map
<void*,HANDLE
>::iterator im
= AccessAddressesToHandles
.find( accessAddress
);
149 if ( im
!= AccessAddressesToHandles
.end() )
151 //nldebug( "SHDMEM: CloseHandle mapFile %u", (*im).second );
152 if ( ! CloseHandle( (*im
).second
) )
153 nlwarning( "SHDMEM: CloseHandle failed: error %u, mapFile %u", GetLastError(), (*im
).second
);
154 AccessAddressesToHandles
.erase( im
);
164 // Detach the shared memory segment
165 return ( shmdt( accessAddress
) != -1 );
172 * Destroy a shared memory segment (to call only by the process that created the segment)
173 * Note: does nothing under Windows, it is automatic.
174 * "Rescue feature": set "force" to true if a segment was created and left out of
175 * control (meaning a new createSharedMemory() with the same sharedMemId fails), but
176 * before, make sure the segment really belongs to you!
178 * Note: this method does nothing under Windows, destroying is automatic.
179 * Under Unix, the segment will actually be destroyed after the last detach
180 * (quoting shmctl man page). It means after calling destroySharedMemory(), the
181 * segment is still accessible by another process until it calls closeSharedMemory().
183 void CSharedMemory::destroySharedMemory( TSharedMemId sharedMemId
, bool force
)
185 #ifndef NL_OS_WINDOWS
186 // Set the segment to auto-destroying (when the last process detaches)
187 map
<TSharedMemId
,int>::iterator im
= SharedMemIdsToShmids
.find( sharedMemId
);
188 if ( im
!= SharedMemIdsToShmids
.end() )
190 // Destroy the segment created before
191 shmctl( (*im
).second
, IPC_RMID
, 0 );
192 SharedMemIdsToShmids
.erase( im
);
196 // Open and destroy the segment
197 int shmid
= shmget( sharedMemId
, 0, 0666 );
200 // Destroy the segment
201 shmctl( shmid
, IPC_RMID
, 0 );