11#ifndef MMAPFILE_H
22#define MMAPFILE_H
33
4+ #include < cstdint>
45#include < string>
56#include < stdexcept>
67
1011#include < sys/stat.h> // For fstat
1112#include < fcntl.h> // For open()
1213#include < unistd.h> // For ftruncate(), close()
14+ #include < unordered_set>
1315
1416// #include <android/log.h>
1517// #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "rtnmmrray", __VA_ARGS__))
1618
19+
20+ // in-memory set of open file (pairs of dev and ino)
21+ using DeviceAndInode = std::pair<uint64_t , uint64_t >;
22+
23+ struct DeviceAndInodeHash {
24+ std::size_t operator ()(const DeviceAndInode& p) const {
25+ return std::hash<uint64_t >{}(p.first ) ^ (std::hash<uint64_t >{}(p.second ) << 1 );
26+ }
27+ };
28+
29+ static std::unordered_set<DeviceAndInode, DeviceAndInodeHash> fileLocks;
30+
1731// Helper functions
1832static inline long long getFileSizeFromName (const std::string& path) {
1933 struct stat fileStat;
@@ -24,20 +38,21 @@ static inline long long getFileSizeFromName(const std::string& path) {
2438 return S_ISDIR (fileStat.st_mode ) ? 0 : fileStat.st_size ; // Return 0 for directories, otherwise return file size
2539}
2640
27- static inline long long getFileSizeFromFd (int fd) {
28- struct stat fileStat;
29- if (fstat (fd, &fileStat) != 0 ) {
30- perror (" fstat" );
31- return -1 ; // Return -1 to indicate an error
32- }
33- return fileStat.st_size ;
34- }
35-
3641// Resize a file to a given size
3742static inline bool fileResize (int fd, size_t newSize) {
3843 return ftruncate (fd, newSize) == 0 ;
3944}
4045
46+ // Lock a file, i.e. add it to the in-memory set of device/inode pairs
47+ static inline bool fileLock (uint64_t dev, uint64_t ino) {
48+ return fileLocks.insert (std::make_pair (dev, ino)).second ;
49+ }
50+
51+ // Unlock a file, i.e. remove it from the in-memory set of device/inode pairs
52+ static inline void fileUnlock (uint64_t dev, uint64_t ino) {
53+ fileLocks.erase (std::make_pair (dev, ino));
54+ }
55+
4156// Create a directory and all parent directories if they do not exist
4257static inline bool createParentDir (const std::string &path) {
4358 size_t pos = 0 ;
@@ -75,15 +90,19 @@ class MMapFile {
7590 capacity_ (0 ),
7691 data_ (nullptr ),
7792 fd_ (-1 ),
78- readOnly_ (false ) {}
93+ readOnly_ (false ),
94+ dev_ (-1 ),
95+ ino_ (-1 ) {}
7996
8097 // Constructor
8198 MMapFile (const std::string& filePath, bool readOnly = false ) :
8299 size_ (0 ),
83100 capacity_ (0 ),
84101 data_ (nullptr ),
85102 fd_ (-1 ),
86- readOnly_ (false )
103+ readOnly_ (false ),
104+ dev_ (-1 ),
105+ ino_ (-1 )
87106 {
88107 open (filePath, readOnly);
89108 }
@@ -140,6 +159,8 @@ class MMapFile {
140159 if (!createParentDir (filePath)) [[unlikely]] {
141160 throw std::runtime_error (std::string (" Failed to create parent directory for file: " ) + filePath);
142161 }
162+
163+ // Open the file
143164 fd_ = ::open (filePath.c_str (), readOnly ? O_RDONLY : O_RDWR | O_CREAT, 0600 );
144165 if (fd_ < 0 )
145166 {
@@ -148,19 +169,29 @@ class MMapFile {
148169 filePath_ = filePath;
149170 readOnly_ = readOnly;
150171
151- size_ = getFileSizeFromFd (fd_);
152- if (size_ == (size_t )-1 ) [[unlikely]]
153- {
172+ struct stat fileStat;
173+ if (fstat (fd_, &fileStat) != 0 ) [[unlikely]] {
154174 close ();
155175 throw std::runtime_error (" Failed to get file size" );
156176 }
157- capacity_ = size_;
158177
178+ // Lock the file
179+ if (!fileLock (fileStat.st_dev , fileStat.st_ino )) [[unlikely]] {
180+ close ();
181+ throw std::runtime_error (" Failed to lock file, someone else is already using it: " + filePath);
182+ }
183+ dev_ = fileStat.st_dev ;
184+ ino_ = fileStat.st_ino ;
185+
186+ // Get the file size
187+ size_ = fileStat.st_size ;
188+ capacity_ = size_;
159189 if (size_ == 0 ) [[unlikely]] {
160190 data_ = nullptr ;
161191 return ;
162192 }
163193
194+ // Map the file into memory
164195 data_ = static_cast <uint8_t *>(mmap (nullptr , capacity_, readOnly ? PROT_READ : PROT_READ | PROT_WRITE, MAP_SHARED, fd_, 0 ));
165196 if (data_ == MAP_FAILED) [[unlikely]] {
166197 close ();
@@ -170,13 +201,15 @@ class MMapFile {
170201
171202 void close (bool dontTouchFile = false )
172203 {
204+ // Unmap the file from memory
173205 if (data_) {
174206 if (data_ != MAP_FAILED) {
175207 munmap (data_, capacity_);
176208 }
177209 data_ = nullptr ;
178210 }
179211
212+ // Close the file, resize it if necessary, and delete it if empty
180213 if (fd_ >= 0 ) {
181214 if (!readOnly_ && !dontTouchFile) {
182215 fileResize (fd_, size_);
@@ -190,6 +223,13 @@ class MMapFile {
190223 }
191224 }
192225
226+ // Unlock the file if it was locked
227+ if (dev_ != -1ull || ino_ != -1ull ) {
228+ fileUnlock (dev_, ino_);
229+ }
230+ dev_ = -1 ;
231+ ino_ = -1 ;
232+
193233 size_ = 0 ;
194234 capacity_ = 0 ;
195235 filePath_.clear ();
@@ -320,6 +360,7 @@ class MMapFile {
320360 uint8_t * data_;
321361 int fd_;
322362 bool readOnly_;
363+ uint64_t dev_, ino_;
323364};
324365
325366#endif // MMAPFILE_H
0 commit comments