From 7de8310ad811b4451a7b2288ceace270b61253c2 Mon Sep 17 00:00:00 2001 From: Paul Taykalo Date: Wed, 20 Nov 2019 00:19:20 +0200 Subject: [PATCH] Use kevent queue for waiting for file unlocking --- llvm/include/llvm/Support/LockFileManager.h | 2 + llvm/lib/Support/LockFileManager.cpp | 98 ++++++++++++++++++++- 2 files changed, 99 insertions(+), 1 deletion(-) diff --git a/llvm/include/llvm/Support/LockFileManager.h b/llvm/include/llvm/Support/LockFileManager.h index 57e4fbd84cd95..16c943a7bbc09 100644 --- a/llvm/include/llvm/Support/LockFileManager.h +++ b/llvm/include/llvm/Support/LockFileManager.h @@ -64,6 +64,8 @@ class LockFileManager { static Optional > readLockFile(StringRef LockFileName); + bool waitForUnlockUsingSystemEvents(WaitForUnlockResult *); + static bool processStillExecuting(StringRef Hostname, int PID); public: diff --git a/llvm/lib/Support/LockFileManager.cpp b/llvm/lib/Support/LockFileManager.cpp index 10181192afbd8..a549a43e0d8f4 100644 --- a/llvm/lib/Support/LockFileManager.cpp +++ b/llvm/lib/Support/LockFileManager.cpp @@ -32,14 +32,20 @@ #if defined(__APPLE__) && defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && (__MAC_OS_X_VERSION_MIN_REQUIRED > 1050) #define USE_OSX_GETHOSTUUID 1 +#define USE_OSX_FILEWATCHER 1 #else #define USE_OSX_GETHOSTUUID 0 +#define USE_OSX_FILEWATCHER 0 #endif #if USE_OSX_GETHOSTUUID #include #endif +#if USE_OSX_FILEWATCHER +#include +#include +#endif using namespace llvm; /// Attempt to read the lock file with the given name, if it exists. @@ -290,10 +296,101 @@ LockFileManager::~LockFileManager() { sys::DontRemoveFileOnSignal(UniqueLockFileName); } +bool LockFileManager::waitForUnlockUsingSystemEvents( + LockFileManager::WaitForUnlockResult *Result) { +#if !USE_OSX_FILEWATCHER + return false; +#else + + // Class that employs RAII to save a file descriptor + class FileDescriptorKeeper { + int FileDescriptor; + + public: + FileDescriptorKeeper(int Descriptor) { FileDescriptor = Descriptor; } + ~FileDescriptorKeeper() { close(FileDescriptor); } + }; + + + const unsigned MaxSeconds = 90; + struct timespec timeout = {1, 0}; + + int EventQueue; + int LockFileDescriptor; + int EventResult; + + if ((EventQueue = kqueue()) == -1) + return false; + + FileDescriptorKeeper queueRAII = FileDescriptorKeeper(EventQueue); + + // Opening file for lock descriptor + if ((LockFileDescriptor = open(LockFileName.c_str(), O_RDONLY)) == -1) + return false; + + FileDescriptorKeeper fileRAII = FileDescriptorKeeper(LockFileDescriptor); + + struct kevent FileRemovingEvent; + EV_SET(&FileRemovingEvent, LockFileDescriptor, EVFILT_VNODE, + EV_ADD | EV_ENABLE | EV_ONESHOT, NOTE_DELETE, 0, 0); + + unsigned Iterations = 0; + while (Iterations < MaxSeconds) { + + struct kevent ReceivedEvent; + EventResult = + kevent(EventQueue, &FileRemovingEvent, 1, &ReceivedEvent, 1, &timeout); + + // Unexpected event result, something went wrong, let's back to the polling + if (EventResult == -1) + return false; + + // File was succesfully deleted + if (EventResult > 0 && ReceivedEvent.fflags & NOTE_DELETE) { + *Result = Res_Success; + return true; + } + + // Check if file was deleted + if (sys::fs::access(LockFileName.c_str(), sys::fs::AccessMode::Exist) == + errc::no_such_file_or_directory) { + // If the original file wasn't created, somone thought the lock was + // dead. + if (!sys::fs::exists(FileName)) { + *Result = Res_OwnerDied; + return true; + } + *Result = Res_Success; + return true; + } + + // If the process owning the lock died without cleaning up, just bail + // out. + if (!processStillExecuting((*Owner).first, (*Owner).second)) { + *Result = Res_Success; + return true; + } + + Iterations++; + } + + // For some reason we haven't received correct event from the file system + // And lock file is still present and owner process is still alive so we will + // fallback to default implementation + *Result = Res_Timeout; + return true; +#endif +} + LockFileManager::WaitForUnlockResult LockFileManager::waitForUnlock() { if (getState() != LFS_Shared) return Res_Success; + WaitForUnlockResult SystemEventsResult; + if (waitForUnlockUsingSystemEvents(&SystemEventsResult)) { + return SystemEventsResult; + } + #ifdef _WIN32 unsigned long Interval = 1; #else @@ -345,7 +442,6 @@ LockFileManager::WaitForUnlockResult LockFileManager::waitForUnlock() { Interval.tv_sec < (time_t)MaxSeconds #endif ); - // Give up. return Res_Timeout; }