Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use kevent queue for waiting for file unlocking #40

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions llvm/include/llvm/Support/LockFileManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ class LockFileManager {
static Optional<std::pair<std::string, int> >
readLockFile(StringRef LockFileName);

bool waitForUnlockUsingSystemEvents(WaitForUnlockResult *);

static bool processStillExecuting(StringRef Hostname, int PID);

public:
Expand Down
98 changes: 97 additions & 1 deletion llvm/lib/Support/LockFileManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <uuid/uuid.h>
#endif

#if USE_OSX_FILEWATCHER
#include <fcntl.h>
#include <sys/event.h>
#endif
using namespace llvm;

/// Attempt to read the lock file with the given name, if it exists.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -345,7 +442,6 @@ LockFileManager::WaitForUnlockResult LockFileManager::waitForUnlock() {
Interval.tv_sec < (time_t)MaxSeconds
#endif
);

// Give up.
return Res_Timeout;
}
Expand Down