diff options
author | Joel Rosdahl <joel@rosdahl.net> | 2020-02-26 22:50:12 +0100 |
---|---|---|
committer | Joel Rosdahl <joel@rosdahl.net> | 2020-03-10 19:57:11 +0100 |
commit | 01de3d5c703407d658c5adcd753d4fc5da89ba3d (patch) | |
tree | ed0c49d4e5aa8c080643f7f16c76f9a9131eebce | |
parent | 41425fc94b1df95d36c3254db5b67e29c257cb6b (diff) | |
download | ccache-01de3d5c703407d658c5adcd753d4fc5da89ba3d.tar.gz ccache-01de3d5c703407d658c5adcd753d4fc5da89ba3d.tar.bz2 ccache-01de3d5c703407d658c5adcd753d4fc5da89ba3d.zip |
Rewrite the Windows version of the lockfile routines
Instead of emulating the POSIX version, use the native Windows file API
to create a lockfile. This should mitigate several problems with the old
implementation.
Hopefully fixes #537.
(cherry picked from commit 53b9fdd6272896bb4a2dc42c9680d4030f86b33c)
-rw-r--r-- | src/lockfile.cpp | 168 |
1 files changed, 103 insertions, 65 deletions
diff --git a/src/lockfile.cpp b/src/lockfile.cpp index 6905b3e3..1faef4f1 100644 --- a/src/lockfile.cpp +++ b/src/lockfile.cpp @@ -21,6 +21,8 @@ #include "Util.hpp" #include "logging.hpp" +#ifndef _WIN32 + // This function acquires a lockfile for the given path. Returns true if the // lock was acquired, otherwise false. If the lock has been considered stale // for the number of microseconds specified by staleness_limit, the function @@ -47,59 +49,6 @@ lockfile_acquire(const char* path, unsigned staleness_limit) my_content = format("%s:%d:%d", hostname, (int)getpid(), (int)time(nullptr)); -#if defined(_WIN32) || defined(__CYGWIN__) - int fd = open(lockfile, O_WRONLY | O_CREAT | O_EXCL | O_BINARY, 0666); - if (fd == -1) { - int saved_errno = errno; - cc_log("lockfile_acquire: open WRONLY %s: %s", lockfile, strerror(errno)); - if (saved_errno == ENOENT) { - // Directory doesn't exist? - if (Util::create_dir(Util::dir_name(lockfile))) { - // OK. Retry. - continue; - } - } - if (saved_errno != EEXIST) { - // Directory doesn't exist or isn't writable? - goto out; - } - // Someone else has the lock. - fd = open(lockfile, O_RDONLY | O_BINARY); - if (fd == -1) { - if (errno == ENOENT) { - // The file was removed after the open() call above, so retry - // acquiring it. - continue; - } else { - cc_log( - "lockfile_acquire: open RDONLY %s: %s", lockfile, strerror(errno)); - goto out; - } - } - free(content); - const size_t bufsize = 1024; - content = static_cast<char*>(x_malloc(bufsize)); - int len = read(fd, content, bufsize - 1); - if (len == -1) { - cc_log("lockfile_acquire: read %s: %s", lockfile, strerror(errno)); - close(fd); - goto out; - } - close(fd); - content[len] = '\0'; - } else { - // We got the lock. - if (write(fd, my_content, strlen(my_content)) == -1) { - cc_log("lockfile_acquire: write %s: %s", lockfile, strerror(errno)); - close(fd); - x_unlink(lockfile); - goto out; - } - close(fd); - acquired = true; - goto out; - } -#else if (symlink(my_content, lockfile) == 0) { // We got the lock. acquired = true; @@ -136,7 +85,6 @@ lockfile_acquire(const char* path, unsigned staleness_limit) goto out; } } -#endif if (str_eq(content, my_content)) { // Lost NFS reply? @@ -199,23 +147,113 @@ lockfile_release(const char* path) free(lockfile); } +#else + +HANDLE lockfile_handle = NULL; + +// This function acquires a lockfile for the given path. Returns true if the +// lock was acquired, otherwise false. If the lock has been acquired within the +// limit (in microseconds) the function will give up and return false. The time +// limit should be reasonably larger than the longest time the lock can be +// expected to be held. +bool +lockfile_acquire(const char* path, unsigned time_limit) +{ + char* lockfile = format("%s.lock", path); + unsigned to_sleep = 1000; // Microseconds. + unsigned max_to_sleep = 10000; // Microseconds. + unsigned slept = 0; // Microseconds. + bool acquired = false; + + while (true) { + DWORD flags = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE; + lockfile_handle = CreateFile(lockfile, + GENERIC_WRITE, // desired access + 0, // shared mode (0 = not shared) + NULL, // security attributes + CREATE_ALWAYS, // creation disposition, + flags, // flags and attributes + NULL // template file + ); + if (lockfile_handle != INVALID_HANDLE_VALUE) { + acquired = true; + break; + } + + DWORD error = GetLastError(); + cc_log("lockfile_acquire: CreateFile %s: error code %lu", lockfile, error); + if (error == ERROR_PATH_NOT_FOUND) { + // Directory doesn't exist? + if (Util::create_dir(Util::dir_name(lockfile)) == 0) { + // OK. Retry. + continue; + } + } + + // ERROR_SHARING_VIOLATION: lock already held. + // ERROR_ACCESS_DENIED: maybe pending delete. + if (error != ERROR_SHARING_VIOLATION && error != ERROR_ACCESS_DENIED) { + // Fatal error, give up. + break; + } + + if (slept > time_limit) { + cc_log("lockfile_acquire: gave up acquiring %s", lockfile); + break; + } + + cc_log("lockfile_acquire: failed to acquire %s; sleeping %u microseconds", + lockfile, + to_sleep); + usleep(to_sleep); + slept += to_sleep; + to_sleep = std::min(max_to_sleep, 2 * to_sleep); + } + + if (acquired) { + cc_log("Acquired lock %s", lockfile); + } else { + cc_log("Failed to acquire lock %s", lockfile); + } + free(lockfile); + return acquired; +} + +// Release the lockfile for the given path. Assumes that we are the legitimate +// owner. +void +lockfile_release(const char* path) +{ + assert(lockfile_handle != INVALID_HANDLE_VALUE); + cc_log("Releasing lock %s.lock", path); + CloseHandle(lockfile_handle); + lockfile_handle = NULL; +} + +#endif + #ifdef TEST_LOCKFILE + int main(int argc, char** argv) { - extern char* cache_logfile; - cache_logfile = "/dev/stdout"; - if (argc == 4) { + if (argc == 3) { unsigned staleness_limit = atoi(argv[1]); - if (str_eq(argv[2], "acquire")) { - return lockfile_acquire(argv[3], staleness_limit) == 0; - } else if (str_eq(argv[2], "release")) { - lockfile_release(argv[3]); - return 0; + printf("Acquiring\n"); + bool acquired = lockfile_acquire(argv[2], staleness_limit); + if (acquired) { + printf("Sleeping 2 seconds\n"); + sleep(2); + lockfile_release(argv[2]); + printf("Released\n"); + } else { + printf("Failed to acquire\n"); } + } else { + fprintf(stderr, "Usage: testlockfile <staleness_limit> <path>\n"); + + return 1; } - fprintf(stderr, - "Usage: testlockfile <staleness_limit> <acquire|release> <path>\n"); - return 1; } + #endif |