Skip to content

Commit

Permalink
Use TempFile in the implementation of LockFileManager.
Browse files Browse the repository at this point in the history
This move some of the complexity over to the lower level TempFile.

It also makes it a bit more explicit where errors are ignored since we
now have a call to consumeError.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@318550 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
espindola committed Nov 17, 2017
1 parent f5f6185 commit ec6f9b0
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 51 deletions.
3 changes: 2 additions & 1 deletion include/llvm/Support/LockFileManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

#include "llvm/ADT/Optional.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/FileSystem.h"
#include <system_error>
#include <utility> // for std::pair

Expand Down Expand Up @@ -53,7 +54,7 @@ class LockFileManager {
private:
SmallString<128> FileName;
SmallString<128> LockFileName;
SmallString<128> UniqueLockFileName;
Optional<sys::fs::TempFile> UniqueLockFile;

Optional<std::pair<std::string, int> > Owner;
std::error_code ErrorCode;
Expand Down
83 changes: 33 additions & 50 deletions lib/Support/LockFileManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,33 +123,21 @@ bool LockFileManager::processStillExecuting(StringRef HostID, int PID) {

namespace {

/// An RAII helper object ensure that the unique lock file is removed.
///
/// Ensures that if there is an error or a signal before we finish acquiring the
/// lock, the unique file will be removed. And if we successfully take the lock,
/// the signal handler is left in place so that signals while the lock is held
/// will remove the unique lock file. The caller should ensure there is a
/// matching call to sys::DontRemoveFileOnSignal when the lock is released.
class RemoveUniqueLockFileOnSignal {
StringRef Filename;
bool RemoveImmediately;
/// An RAII helper object for cleanups.
class RAIICleanup {
std::function<void()> Fn;
bool Canceled = false;

public:
RemoveUniqueLockFileOnSignal(StringRef Name)
: Filename(Name), RemoveImmediately(true) {
sys::RemoveFileOnSignal(Filename, nullptr);
}
RAIICleanup(std::function<void()> Fn) : Fn(Fn) {}

~RemoveUniqueLockFileOnSignal() {
if (!RemoveImmediately) {
// Leave the signal handler enabled. It will be removed when the lock is
// released.
~RAIICleanup() {
if (Canceled)
return;
}
sys::fs::remove(Filename);
sys::DontRemoveFileOnSignal(Filename);
Fn();
}

void lockAcquired() { RemoveImmediately = false; }
void cancel() { Canceled = true; }
};

} // end anonymous namespace
Expand All @@ -172,16 +160,22 @@ LockFileManager::LockFileManager(StringRef FileName)
return;

// Create a lock file that is unique to this instance.
UniqueLockFileName = LockFileName;
UniqueLockFileName += "-%%%%%%%%";
int UniqueLockFileID;
if (std::error_code EC = sys::fs::createUniqueFile(
UniqueLockFileName, UniqueLockFileID, UniqueLockFileName)) {
std::string S("failed to create unique file ");
S.append(UniqueLockFileName.str());
Expected<sys::fs::TempFile> Temp =
sys::fs::TempFile::create(LockFileName + "-%%%%%%%%");
if (!Temp) {
std::error_code EC = errorToErrorCode(Temp.takeError());
std::string S("failed to create unique file with prefix ");
S.append(LockFileName.str());
setError(EC, S);
return;
}
UniqueLockFile = std::move(*Temp);

// Make sure we discard the temporary file on exit.
RAIICleanup RemoveTempFile([&]() {
if (Error E = UniqueLockFile->discard())
setError(errorToErrorCode(std::move(E)));
});

// Write our process ID to our unique lock file.
{
Expand All @@ -191,54 +185,46 @@ LockFileManager::LockFileManager(StringRef FileName)
return;
}

raw_fd_ostream Out(UniqueLockFileID, /*shouldClose=*/true);
raw_fd_ostream Out(UniqueLockFile->FD, /*shouldClose=*/false);
Out << HostID << ' ';
#if LLVM_ON_UNIX
Out << getpid();
#else
Out << "1";
#endif
Out.close();
Out.flush();

if (Out.has_error()) {
// We failed to write out PID, so report the error, remove the
// unique lock file, and fail.
std::string S("failed to write to ");
S.append(UniqueLockFileName.str());
S.append(UniqueLockFile->TmpName);
setError(Out.error(), S);
sys::fs::remove(UniqueLockFileName);
return;
}
}

// Clean up the unique file on signal, which also releases the lock if it is
// held since the .lock symlink will point to a nonexistent file.
RemoveUniqueLockFileOnSignal RemoveUniqueFile(UniqueLockFileName);

while (true) {
// Create a link from the lock file name. If this succeeds, we're done.
std::error_code EC =
sys::fs::create_link(UniqueLockFileName, LockFileName);
sys::fs::create_link(UniqueLockFile->TmpName, LockFileName);
if (!EC) {
RemoveUniqueFile.lockAcquired();
RemoveTempFile.cancel();
return;
}

if (EC != errc::file_exists) {
std::string S("failed to create link ");
raw_string_ostream OSS(S);
OSS << LockFileName.str() << " to " << UniqueLockFileName.str();
OSS << LockFileName.str() << " to " << UniqueLockFile->TmpName;
setError(EC, OSS.str());
return;
}

// Someone else managed to create the lock file first. Read the process ID
// from the lock file.
if ((Owner = readLockFile(LockFileName))) {
// Wipe out our unique lock file (it's useless now)
sys::fs::remove(UniqueLockFileName);
return;
}
if ((Owner = readLockFile(LockFileName)))
return; // RemoveTempFile will delete out our unique lock file.

if (!sys::fs::exists(LockFileName)) {
// The previous owner released the lock file before we could read it.
Expand All @@ -250,7 +236,7 @@ LockFileManager::LockFileManager(StringRef FileName)
// ownership.
if ((EC = sys::fs::remove(LockFileName))) {
std::string S("failed to remove lockfile ");
S.append(UniqueLockFileName.str());
S.append(LockFileName.str());
setError(EC, S);
return;
}
Expand Down Expand Up @@ -285,10 +271,7 @@ LockFileManager::~LockFileManager() {

// Since we own the lock, remove the lock file and our own unique lock file.
sys::fs::remove(LockFileName);
sys::fs::remove(UniqueLockFileName);
// The unique file is now gone, so remove it from the signal handler. This
// matches a sys::RemoveFileOnSignal() in LockFileManager().
sys::DontRemoveFileOnSignal(UniqueLockFileName);
consumeError(UniqueLockFile->discard());
}

LockFileManager::WaitForUnlockResult LockFileManager::waitForUnlock() {
Expand Down

0 comments on commit ec6f9b0

Please sign in to comment.