Buckets:
| common_file_lock::common_file_lock(const std::string & lock_path) { | |
| this->lock_path = lock_path; | |
| } | |
| // the \\?\ prefix (for paths longer than 255 chars) is only accepted on absolute paths | |
| // with backslash separators: normalize first, then check the length of the absolute form | |
| static std::wstring as_extended_path(const std::string & path) { | |
| // convert from the narrow encoding std::filesystem uses on this system | |
| int wlen = MultiByteToWideChar(CP_ACP, 0, path.c_str(), (int) path.size(), nullptr, 0); | |
| if (wlen <= 0) { | |
| return std::wstring(path.begin(), path.end()); // best effort for exotic encodings | |
| } | |
| std::wstring wide(wlen, L'\0'); | |
| MultiByteToWideChar(CP_ACP, 0, path.c_str(), (int) path.size(), wide.data(), wlen); | |
| if (wide.rfind(L"\\\\?\\", 0) == 0) { | |
| return wide; | |
| } | |
| // GetFullPathNameW resolves relative paths and normalizes separators | |
| DWORD size = GetFullPathNameW(wide.c_str(), 0, nullptr, nullptr); | |
| if (size == 0) { | |
| return wide; | |
| } | |
| std::wstring full(size, L'\0'); | |
| DWORD written = GetFullPathNameW(wide.c_str(), size, full.data(), nullptr); | |
| if (written == 0 || written >= size) { | |
| return wide; | |
| } | |
| full.resize(written); | |
| if (full.size() <= 255) { | |
| return wide; // short enough: open the path exactly as given | |
| } | |
| if (full.rfind(L"\\\\", 0) == 0) { | |
| return L"\\\\?\\UNC\\" + full.substr(2); // \\server\share -> \\?\UNC\server\share | |
| } | |
| return L"\\\\?\\" + full; | |
| } | |
| common_file_lock::status common_file_lock::try_lock() { | |
| if (lock_path.empty()) { | |
| return status::acquired; // no lock path, nothing to lock | |
| } | |
| if (fd < 0) { | |
| // opened with a wide path: extended paths cannot go through the narrow CRT | |
| std::wstring lock_path_w = as_extended_path(lock_path); | |
| fd = _wopen(lock_path_w.c_str(), _O_RDWR | _O_CREAT | _O_BINARY | _O_NOINHERIT, _S_IREAD | _S_IWRITE); | |
| if (fd < 0) { | |
| LOG_ERR("%s: cannot open lock file: %s\n", __func__, lock_path.c_str()); | |
| return status::error; | |
| } | |
| } | |
| // lock the first byte, the file itself stays empty | |
| OVERLAPPED ov = {}; | |
| locked = LockFileEx((HANDLE) _get_osfhandle(fd), | |
| LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY, 0, 1, 0, &ov); | |
| if (locked) { | |
| return status::acquired; | |
| } | |
| if (GetLastError() == ERROR_LOCK_VIOLATION) { | |
| return status::busy; | |
| } | |
| LOG_ERR("%s: file locking failed (%d)\n", __func__, (int) GetLastError()); | |
| return status::error; | |
| if (fd < 0) { | |
| int open_flags = O_RDWR | O_CREAT | O_CLOEXEC; | |
| open_flags |= O_NOFOLLOW; | |
| // group-writable, so users sharing a cache can wait on the lock | |
| fd = open(lock_path.c_str(), open_flags, 0664); | |
| if (fd < 0) { | |
| LOG_ERR("%s: cannot open lock file: %s\n", __func__, lock_path.c_str()); | |
| return status::error; | |
| } | |
| // open() filters the mode through the umask | |
| (void) fchmod(fd, 0664); | |
| } | |
| if (flock(fd, LOCK_EX | LOCK_NB) == 0) { | |
| locked = true; | |
| return status::acquired; | |
| } | |
| // EACCES is reported as contention on some filesystems | |
| if (errno == EWOULDBLOCK || errno == EAGAIN || errno == EACCES) { | |
| return status::busy; | |
| } | |
| // flock is not implemented (some NFS/FUSE mounts): report it instead of racing | |
| LOG_ERR("%s: file locking failed (%s)\n", __func__, strerror(errno)); | |
| return status::error; | |
| } | |
| bool common_file_lock::acquire(const std::function<bool()> & keep_waiting) { | |
| // the lock file is opened by try_lock, its parent must exist by then | |
| if (!lock_path.empty()) { | |
| std::error_code ec; | |
| auto lock_dir = std::filesystem::path(lock_path).parent_path(); | |
| if (!lock_dir.empty()) { | |
| std::filesystem::create_directories(lock_dir, ec); | |
| if (ec) { | |
| LOG_ERR("%s: unable to create lock directory '%s': %s\n", __func__, lock_dir.string().c_str(), ec.message().c_str()); | |
| return false; | |
| } | |
| } | |
| } | |
| while (true) { | |
| switch (try_lock()) { | |
| case status::acquired: | |
| return true; | |
| case status::busy: | |
| if (keep_waiting && !keep_waiting()) { | |
| return false; | |
| } | |
| std::this_thread::sleep_for(std::chrono::milliseconds(100)); | |
| continue; | |
| case status::error: | |
| return false; | |
| } | |
| } | |
| } | |
| // the lock file is never deleted: another process could then create a fresh | |
| // file at the same path and lock that | |
| void common_file_lock::close() { | |
| if (fd >= 0) { | |
| if (locked) { | |
| OVERLAPPED ov = {}; | |
| UnlockFileEx((HANDLE) _get_osfhandle(fd), 0, 1, 0, &ov); | |
| } | |
| _close(fd); | |
| } | |
| if (fd >= 0) { | |
| if (locked) { | |
| flock(fd, LOCK_UN); | |
| } | |
| ::close(fd); | |
| } | |
| fd = -1; | |
| locked = false; | |
| } | |
| common_file_lock::~common_file_lock() { | |
| close(); | |
| } | |
Xet Storage Details
- Size:
- 5.55 kB
- Xet hash:
- 8eccd929f798315e496bb0f6b1856003b30f1520861b305dbd1a9608585d3846
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.