CppCommon 1.0.5.0
C++ Common Library
Loading...
Searching...
No Matches
file_lock.cpp
Go to the documentation of this file.
1
9#include "threads/file_lock.h"
10
11#include "errors/fatal.h"
12#include "threads/thread.h"
14
15#if (defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)) && !defined(__CYGWIN__)
16#include <sys/file.h>
17#include <fcntl.h>
18#include <unistd.h>
19#elif defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
20#include <windows.h>
21#undef Yield
22#endif
23
24namespace CppCommon {
25
27
28// In case we are on a system with glibc version earlier than 2.20
29#ifndef F_OFD_GETLK
30#define F_OFD_GETLK 36
31#define F_OFD_SETLK 37
32#define F_OFD_SETLKW 38
33#endif
34
35class FileLock::Impl
36{
37public:
38#if (defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)) && !defined(__CYGWIN__)
39 Impl() : _file(0) {}
40#elif defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
41 Impl() : _file(nullptr) {}
42#endif
43
44 ~Impl()
45 {
46 try
47 {
48 Reset();
49 }
50 catch (const SystemException& ex)
51 {
52 fatality(SystemException(ex.string()));
53 }
54 }
55
56 const Path& path() const noexcept { return _path; }
57
58 void Assign(const Path& path)
59 {
60 // Reset the previous file-lock
61 Reset();
62
63 _path = path;
64#if (defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)) && !defined(__CYGWIN__)
65 _file = open(_path.string().c_str(), O_CREAT | O_RDWR, 0644);
66 if (_file < 0)
67 throwex FileSystemException("Cannot create or open file-lock file!").Attach(path);
68#elif defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
69 // Retries in CreateFile, see http://support.microsoft.com/kb/316609
70 const std::wstring wpath = _path.wstring();
71 const int attempts = 1000;
72 const int sleep = 100;
73 for (int attempt = 0; attempt < attempts; ++attempt)
74 {
75 _file = CreateFileW(wpath.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_HIDDEN | FILE_FLAG_DELETE_ON_CLOSE, nullptr);
76 if (_file == INVALID_HANDLE_VALUE)
77 {
78 Sleep(sleep);
79 continue;
80 }
81 return;
82 }
83 throwex FileSystemException("Cannot create or open file-lock file!").Attach(path);
84#endif
85 }
86
87 void Reset()
88 {
89 if (!_file)
90 return;
91
92#if (defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)) && !defined(__CYGWIN__)
93 int result = close(_file);
94 if (result != 0)
95 fatality(FileSystemException("Cannot close the file-lock descriptor!").Attach(_path));
96 _file = 0;
97
98 // Remove the file-lock file
99 unlink(_path.string().c_str());
100#elif defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
101 if (!CloseHandle(_file))
102 fatality(FileSystemException("Cannot close the file-lock handle!").Attach(_path));
103 _file = nullptr;
104#endif
105 }
106
107 bool TryLockRead()
108 {
109#if defined(linux) || defined(__linux) || defined(__linux__)
110 struct flock lock;
111 lock.l_type = F_RDLCK;
112 lock.l_whence = SEEK_SET;
113 lock.l_start = 0;
114 lock.l_len = 0;
115 lock.l_pid = 0;
116 int result = fcntl(_file, F_OFD_SETLK, &lock);
117 if (result == -1)
118 {
119 if (errno == EAGAIN)
120 return false;
121 else
122 throwex FileSystemException("Failed to try lock for read!").Attach(_path);
123 }
124 else
125 return true;
126#elif (defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)) && !defined(__CYGWIN__)
127 int result = flock(_file, LOCK_SH | LOCK_NB);
128 if (result != 0)
129 {
130 if (errno == EWOULDBLOCK)
131 return false;
132 else
133 throwex FileSystemException("Failed to try lock for read!").Attach(_path);
134 }
135 else
136 return true;
137#elif defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
138 OVERLAPPED overlapped;
139 ZeroMemory(&overlapped, sizeof(OVERLAPPED));
140 return LockFileEx(_file, LOCKFILE_FAIL_IMMEDIATELY, 0, MAXDWORD, MAXDWORD, &overlapped) != 0;
141#endif
142 }
143
144 bool TryLockWrite()
145 {
146#if defined(linux) || defined(__linux) || defined(__linux__)
147 struct flock lock;
148 lock.l_type = F_WRLCK;
149 lock.l_whence = SEEK_SET;
150 lock.l_start = 0;
151 lock.l_len = 0;
152 lock.l_pid = 0;
153 int result = fcntl(_file, F_OFD_SETLK, &lock);
154 if (result == -1)
155 {
156 if (errno == EAGAIN)
157 return false;
158 else
159 throwex FileSystemException("Failed to try lock for write!").Attach(_path);
160 }
161 else
162 return true;
163#elif (defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)) && !defined(__CYGWIN__)
164 int result = flock(_file, LOCK_EX | LOCK_NB);
165 if (result != 0)
166 {
167 if (errno == EWOULDBLOCK)
168 return false;
169 else
170 throwex FileSystemException("Failed to try lock for write!").Attach(_path);
171 }
172 else
173 return true;
174#elif defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
175 OVERLAPPED overlapped;
176 ZeroMemory(&overlapped, sizeof(OVERLAPPED));
177 return LockFileEx(_file, LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY, 0, MAXDWORD, MAXDWORD, &overlapped) != 0;
178#endif
179 }
180
181 void LockRead()
182 {
183#if defined(linux) || defined(__linux) || defined(__linux__)
184 struct flock lock;
185 lock.l_type = F_RDLCK;
186 lock.l_whence = SEEK_SET;
187 lock.l_start = 0;
188 lock.l_len = 0;
189 lock.l_pid = 0;
190 int result = fcntl(_file, F_OFD_SETLKW, &lock);
191 if (result == -1)
192 throwex FileSystemException("Failed to lock for read!").Attach(_path);
193#elif (defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)) && !defined(__CYGWIN__)
194 int result = flock(_file, LOCK_SH);
195 if (result != 0)
196 throwex FileSystemException("Failed to lock for read!").Attach(_path);
197#elif defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
198 OVERLAPPED overlapped;
199 ZeroMemory(&overlapped, sizeof(OVERLAPPED));
200 if (!LockFileEx(_file, 0, 0, MAXDWORD, MAXDWORD, &overlapped))
201 throwex FileSystemException("Failed to lock for read!").Attach(_path);
202#endif
203 }
204
205 void LockWrite()
206 {
207#if defined(linux) || defined(__linux) || defined(__linux__)
208 struct flock lock;
209 lock.l_type = F_WRLCK;
210 lock.l_whence = SEEK_SET;
211 lock.l_start = 0;
212 lock.l_len = 0;
213 lock.l_pid = 0;
214 int result = fcntl(_file, F_OFD_SETLKW, &lock);
215 if (result == -1)
216 throwex FileSystemException("Failed to lock for write!").Attach(_path);
217#elif (defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)) && !defined(__CYGWIN__)
218 int result = flock(_file, LOCK_EX);
219 if (result != 0)
220 throwex FileSystemException("Failed to lock for write!").Attach(_path);
221#elif defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
222 OVERLAPPED overlapped;
223 ZeroMemory(&overlapped, sizeof(OVERLAPPED));
224 if (!LockFileEx(_file, LOCKFILE_EXCLUSIVE_LOCK, 0, MAXDWORD, MAXDWORD, &overlapped))
225 throwex FileSystemException("Failed to lock for write!").Attach(_path);
226#endif
227 }
228
229 void UnlockRead()
230 {
231#if defined(linux) || defined(__linux) || defined(__linux__)
232 struct flock lock;
233 lock.l_type = F_UNLCK;
234 lock.l_whence = SEEK_SET;
235 lock.l_start = 0;
236 lock.l_len = 0;
237 lock.l_pid = 0;
238 int result = fcntl(_file, F_OFD_SETLK, &lock);
239 if (result != 0)
240 throwex FileSystemException("Failed to unlock the read lock!").Attach(_path);
241#elif (defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)) && !defined(__CYGWIN__)
242 int result = flock(_file, LOCK_UN);
243 if (result != 0)
244 throwex FileSystemException("Failed to unlock the read lock!").Attach(_path);
245#elif defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
246 OVERLAPPED overlapped;
247 ZeroMemory(&overlapped, sizeof(OVERLAPPED));
248 if (!UnlockFileEx(_file, 0, MAXDWORD, MAXDWORD, &overlapped))
249 throwex FileSystemException("Failed to unlock the read lock!").Attach(_path);
250#endif
251 }
252
253 void UnlockWrite()
254 {
255#if defined(linux) || defined(__linux) || defined(__linux__)
256 struct flock lock;
257 lock.l_type = F_UNLCK;
258 lock.l_whence = SEEK_SET;
259 lock.l_start = 0;
260 lock.l_len = 0;
261 lock.l_pid = 0;
262 int result = fcntl(_file, F_OFD_SETLK, &lock);
263 if (result != 0)
264 throwex FileSystemException("Failed to unlock the write lock!").Attach(_path);
265#elif (defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)) && !defined(__CYGWIN__)
266 int result = flock(_file, LOCK_UN);
267 if (result != 0)
268 throwex FileSystemException("Failed to unlock the write lock!").Attach(_path);
269#elif defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
270 OVERLAPPED overlapped;
271 ZeroMemory(&overlapped, sizeof(OVERLAPPED));
272 if (!UnlockFileEx(_file, 0, MAXDWORD, MAXDWORD, &overlapped))
273 throwex FileSystemException("Failed to unlock the write lock!").Attach(_path);
274#endif
275 }
276
277private:
278 Path _path;
279#if (defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)) && !defined(__CYGWIN__)
280 int _file;
281#elif defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
282 HANDLE _file;
283#endif
284};
285
287
289{
290 // Check implementation storage parameters
291 [[maybe_unused]] ValidateAlignedStorage<sizeof(Impl), alignof(Impl), StorageSize, StorageAlign> _;
292 static_assert((StorageSize >= sizeof(Impl)), "FileLock::StorageSize must be increased!");
293 static_assert(((StorageAlign % alignof(Impl)) == 0), "FileLock::StorageAlign must be adjusted!");
294
295 // Create the implementation instance
296 new(&_storage)Impl();
297}
298
300{
301 // Check implementation storage parameters
302 [[maybe_unused]] ValidateAlignedStorage<sizeof(Impl), alignof(Impl), StorageSize, StorageAlign> _;
303 static_assert((StorageSize >= sizeof(Impl)), "FileLock::StorageSize must be increased!");
304 static_assert(((StorageAlign % alignof(Impl)) == 0), "FileLock::StorageAlign must be adjusted!");
305
306 // Create the implementation instance
307 new(&_storage)Impl();
308
309 Assign(path);
310}
311
313{
314 // Delete the implementation instance
315 reinterpret_cast<Impl*>(&_storage)->~Impl();
316}
317
319{
320 Assign(path);
321 return *this;
322}
323
324const Path& FileLock::path() const noexcept { return impl().path(); }
325
326void FileLock::Assign(const Path& path) { impl().Assign(path); }
327void FileLock::Reset() { impl().Reset(); }
328
329bool FileLock::TryLockRead() { return impl().TryLockRead(); }
330bool FileLock::TryLockWrite() { return impl().TryLockWrite(); }
331
333{
334 // Calculate a finish timestamp
335 Timestamp finish = NanoTimestamp() + timespan;
336
337 // Try to acquire read lock at least one time
338 if (TryLockRead())
339 return true;
340 else
341 {
342 // Try lock or yield for the given timespan
343 while (NanoTimestamp() < finish)
344 {
345 if (TryLockRead())
346 return true;
347 else
349 }
350
351 // Failed to acquire read lock
352 return false;
353 }
354}
355
357{
358 // Calculate a finish timestamp
359 Timestamp finish = NanoTimestamp() + timespan;
360
361 // Try to acquire write lock at least one time
362 if (TryLockWrite())
363 return true;
364 else
365 {
366 // Try lock or yield for the given timespan
367 while (NanoTimestamp() < finish)
368 {
369 if (TryLockWrite())
370 return true;
371 else
373 }
374
375 // Failed to acquire write lock
376 return false;
377 }
378}
379
380void FileLock::LockRead() { impl().LockRead(); }
381void FileLock::LockWrite() { impl().LockWrite(); }
382void FileLock::UnlockRead() { impl().UnlockRead(); }
383void FileLock::UnlockWrite() { impl().UnlockWrite(); }
384
385} // namespace CppCommon
File-lock synchronization primitive.
Definition file_lock.h:34
void UnlockRead()
Release read lock.
FileLock & operator=(const Path &path)
bool TryLockWrite()
Try to acquire write lock without block.
void LockRead()
Acquire read lock with block.
bool TryLockReadFor(const Timespan &timespan)
Try to acquire read lock for the given timespan.
const Path & path() const noexcept
Get the file-lock path.
void UnlockWrite()
Release write lock.
bool TryLockRead()
Try to acquire read lock without block.
void Reset()
Reset file-lock.
bool TryLockWriteFor(const Timespan &timespan)
Try to acquire write lock for the given timespan.
void Assign(const Path &path)
Assign a new file-lock path.
void LockWrite()
Acquire write lock with block.
High resolution timestamp.
Definition timestamp.h:272
Filesystem path.
Definition path.h:90
static void Yield() noexcept
Yield to other threads.
Definition thread.cpp:143
#define throwex
Throw extended exception macro.
Definition exceptions.h:23
Fatal abort execution definition.
#define fatality(...)
Fatal abort execution extended macro.
Definition fatal.h:22
File-lock synchronization primitive definition.
C++ Common project definitions.
Thread definition.
Aligned storage validator definition.