Plasma Engine  2.0
Loading...
Searching...
No Matches
MemoryMappedFile_Posix.h
1#include <Foundation/FoundationPCH.h>
2PL_FOUNDATION_INTERNAL_HEADER
3
4#include <Foundation/IO/MemoryMappedFile.h>
5#include <Foundation/Logging/Log.h>
6#include <Foundation/Strings/PathUtils.h>
7#include <errno.h>
8#include <fcntl.h>
9#include <sys/mman.h>
10#include <sys/stat.h>
11#include <sys/types.h>
12
13#if PL_ENABLED(PL_PLATFORM_LINUX)
14# include <linux/version.h>
15#endif
16
17#if PL_ENABLED(PL_PLATFORM_OSX)
18# include <unistd.h>
19#endif
20
22{
24 void* m_pMappedFilePtr = nullptr;
25 plUInt64 m_uiFileSize = 0;
26 int m_hFile = -1;
27 plString m_sSharedMemoryName;
28
30 {
31#if PL_ENABLED(PL_SUPPORTS_MEMORY_MAPPED_FILE)
32 if (m_pMappedFilePtr != nullptr)
33 {
34 munmap(m_pMappedFilePtr, m_uiFileSize);
35 m_pMappedFilePtr = nullptr;
36 }
37 if (m_hFile != -1)
38 {
39 close(m_hFile);
40 m_hFile = -1;
41 }
42# if PL_ENABLED(PL_PLATFORM_ANDROID)
43 // shm_open / shm_unlink deprecated.
44 // There is an alternative in ASharedMemory_create but that is only
45 // available in API 26 upwards.
46# else
47 if (!m_sSharedMemoryName.IsEmpty())
48 {
49 shm_unlink(m_sSharedMemoryName);
50 m_sSharedMemoryName.Clear();
51 }
52# endif
53 m_uiFileSize = 0;
54#endif
55 }
56};
57
58plMemoryMappedFile::plMemoryMappedFile()
59{
60 m_pImpl = PL_DEFAULT_NEW(plMemoryMappedFileImpl);
61}
62
63plMemoryMappedFile::~plMemoryMappedFile()
64{
65 Close();
66}
67
68#if PL_ENABLED(PL_SUPPORTS_MEMORY_MAPPED_FILE)
69plResult plMemoryMappedFile::Open(plStringView sAbsolutePath, Mode mode)
70{
71 PL_ASSERT_DEV(mode != Mode::None, "Invalid mode to open the memory mapped file");
72 PL_ASSERT_DEV(plPathUtils::IsAbsolutePath(sAbsolutePath), "plMemoryMappedFile::Open() can only be used with absolute file paths");
73
74 PL_LOG_BLOCK("MemoryMapFile", sAbsolutePath);
75
76 const plStringBuilder sPath = sAbsolutePath;
77
78 Close();
79
80 m_pImpl->m_Mode = mode;
81
82 int access = O_RDONLY;
83 int prot = PROT_READ;
84 int flags = MAP_PRIVATE;
85 if (mode == Mode::ReadWrite)
86 {
87 access = O_RDWR;
88 prot |= PROT_WRITE;
89 flags = MAP_SHARED;
90 }
91# if PL_ENABLED(PL_PLATFORM_LINUX)
92# if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 22)
93 flags |= MAP_POPULATE;
94# endif
95# endif
96 m_pImpl->m_hFile = open(sPath, access | O_CLOEXEC, 0);
97 if (m_pImpl->m_hFile == -1)
98 {
99 plLog::Error("Could not open file for memory mapping - {}", strerror(errno));
100 Close();
101 return PL_FAILURE;
102 }
103 struct stat sb;
104 if (stat(sPath, &sb) == -1 || sb.st_size == 0)
105 {
106 plLog::Error("File for memory mapping is empty - {}", strerror(errno));
107 Close();
108 return PL_FAILURE;
109 }
110 m_pImpl->m_uiFileSize = sb.st_size;
111
112 m_pImpl->m_pMappedFilePtr = mmap(nullptr, m_pImpl->m_uiFileSize, prot, flags, m_pImpl->m_hFile, 0);
113 if (m_pImpl->m_pMappedFilePtr == nullptr)
114 {
115 plLog::Error("Could not create memory mapping of file - {}", strerror(errno));
116 Close();
117 return PL_FAILURE;
118 }
119
120 return PL_SUCCESS;
121}
122#endif
123
124#if PL_ENABLED(PL_SUPPORTS_SHARED_MEMORY)
125plResult plMemoryMappedFile::OpenShared(plStringView sSharedName, plUInt64 uiSize, Mode mode)
126{
127 PL_ASSERT_DEV(mode != Mode::None, "Invalid mode to open the memory mapped file");
128 PL_ASSERT_DEV(uiSize > 0, "plMemoryMappedFile::OpenShared() needs a valid file size to map");
129
130 const plStringBuilder sName = sSharedName;
131
132 PL_LOG_BLOCK("MemoryMapFile", sName);
133
134 Close();
135
136 m_pImpl->m_Mode = mode;
137
138 int prot = PROT_READ;
139 int oflag = O_RDONLY;
140 int flags = MAP_SHARED;
141# if PL_ENABLED(PL_PLATFORM_LINUX)
142# if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 22)
143 flags |= MAP_POPULATE;
144# endif
145# endif
146
147 if (mode == Mode::ReadWrite)
148 {
149 oflag = O_RDWR;
150 prot |= PROT_WRITE;
151 }
152 oflag |= O_CREAT;
153
154 m_pImpl->m_hFile = shm_open(sName, oflag, 0666);
155 if (m_pImpl->m_hFile == -1)
156 {
157 plLog::Error("Could not open shared memory mapping - {}", strerror(errno));
158 Close();
159 return PL_FAILURE;
160 }
161 m_pImpl->m_sSharedMemoryName = sName;
162
163 if (ftruncate(m_pImpl->m_hFile, uiSize) == -1)
164 {
165 plLog::Error("Could not open shared memory mapping - {}", strerror(errno));
166 Close();
167 return PL_FAILURE;
168 }
169 m_pImpl->m_uiFileSize = uiSize;
170
171 m_pImpl->m_pMappedFilePtr = mmap(nullptr, m_pImpl->m_uiFileSize, prot, flags, m_pImpl->m_hFile, 0);
172 if (m_pImpl->m_pMappedFilePtr == nullptr)
173 {
174 plLog::Error("Could not create memory mapping of file - {}", strerror(errno));
175 Close();
176 return PL_FAILURE;
177 }
178 return PL_SUCCESS;
179}
180#endif
181
183{
184 m_pImpl = PL_DEFAULT_NEW(plMemoryMappedFileImpl);
185}
186
188{
189 return m_pImpl->m_Mode;
190}
191
192const void* plMemoryMappedFile::GetReadPointer(plUInt64 uiOffset /*= 0*/, OffsetBase base /*= OffsetBase::Start*/) const
193{
194 PL_ASSERT_DEBUG(m_pImpl->m_Mode >= Mode::ReadOnly, "File must be opened with read access before accessing it for reading.");
195 PL_ASSERT_DEBUG(uiOffset <= m_pImpl->m_uiFileSize, "Read offset must be smaller than mapped file size");
196
197 if (base == OffsetBase::Start)
198 {
199 return plMemoryUtils::AddByteOffset(m_pImpl->m_pMappedFilePtr, uiOffset);
200 }
201 else
202 {
203 return plMemoryUtils::AddByteOffset(m_pImpl->m_pMappedFilePtr, m_pImpl->m_uiFileSize - uiOffset);
204 }
205}
206
207void* plMemoryMappedFile::GetWritePointer(plUInt64 uiOffset /*= 0*/, OffsetBase base /*= OffsetBase::Start*/)
208{
209 PL_ASSERT_DEBUG(m_pImpl->m_Mode >= Mode::ReadWrite, "File must be opened with read/write access before accessing it for writing.");
210 PL_ASSERT_DEBUG(uiOffset <= m_pImpl->m_uiFileSize, "Read offset must be smaller than mapped file size");
211
212 if (base == OffsetBase::Start)
213 {
214 return plMemoryUtils::AddByteOffset(m_pImpl->m_pMappedFilePtr, uiOffset);
215 }
216 else
217 {
218 return plMemoryUtils::AddByteOffset(m_pImpl->m_pMappedFilePtr, m_pImpl->m_uiFileSize - uiOffset);
219 }
220}
221
222plUInt64 plMemoryMappedFile::GetFileSize() const
223{
224 return m_pImpl->m_uiFileSize;
225}
static void Error(plLogInterface *pInterface, const plFormatString &string)
An error that needs to be fixed as soon as possible.
Definition Log.cpp:375
@ Start
Byte offsets are relative to the start of the mapped memory.
void Close()
Removes the memory mapping. Outstanding modifications will be written back to disk at this point.
Definition MemoryMappedFile_NoImpl.h:27
const void * GetReadPointer(plUInt64 uiOffset=0, OffsetBase base=OffsetBase::Start) const
Returns a pointer for reading the mapped file. Asserts that the memory mapping was done successfully.
Definition MemoryMappedFile_NoImpl.h:37
Mode
Definition MemoryMappedFile.h:17
@ ReadOnly
File is mapped for read-only access.
@ None
Currently no file is mapped.
@ ReadWrite
File is mapped for read/write access.
plResult Open(plStringView sAbsolutePath, Mode mode)
Attempts to open the given file and map it into memory.
plResult OpenShared(plStringView sSharedName, plUInt64 uiSize, Mode mode)
Attempts to open or create the given shared memory block addressed by szSharedName.
void * GetWritePointer(plUInt64 uiOffset=0, OffsetBase base=OffsetBase::Start)
Returns a pointer for writing the mapped file. Asserts that the memory mapping was successful and the...
Definition MemoryMappedFile_NoImpl.h:43
Mode GetMode() const
Returns the mode with which the file was opened or None, if is currently not in use.
Definition MemoryMappedFile_NoImpl.h:32
plUInt64 GetFileSize() const
Returns the size (in bytes) of the memory mapping. Zero if no file is mapped at the moment.
Definition MemoryMappedFile_NoImpl.h:49
static T * AddByteOffset(T *pPtr, std::ptrdiff_t offset)
Returns the address stored in ptr plus the given byte offset iOffset, cast to type T.
static bool IsAbsolutePath(plStringView sPath)
Returns true, if the given path represents an absolute path on the current OS.
Definition PathUtils.cpp:136
plStringBuilder is a class that is meant for creating and modifying strings.
Definition StringBuilder.h:35
plStringView represent a read-only sub-string of a larger string, as it can store a dedicated string ...
Definition StringView.h:34
void Clear()
Resets this string to an empty string.
Definition String_inl.h:49
Definition MemoryMappedFile_NoImpl.h:9
Default enum for returning failure or success, instead of using a bool.
Definition Types.h:54
bool IsEmpty() const
Returns whether the string is an empty string.
Definition StringBase_inl.h:25