llvm-mirror/lib/Support/LockFileManager.cpp
Rafael Espindola fe9037915e Cleanup the interface for creating soft or hard links.
Before this patch the unix code for creating hardlinks was unused. The code
for creating symbolic links was implemented in lib/Support/LockFileManager.cpp
and the code for creating hard links in lib/Support/*/Path.inc.

The only use we have for these is in LockFileManager.cpp and it can use both
soft and hard links. Just have a create_link function that creates one or the
other depending on the platform.

llvm-svn: 203596
2014-03-11 18:40:24 +00:00

259 lines
7.0 KiB
C++

//===--- LockFileManager.cpp - File-level Locking Utility------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "llvm/Support/LockFileManager.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/raw_ostream.h"
#include <sys/stat.h>
#include <sys/types.h>
#if LLVM_ON_WIN32
#include <windows.h>
#endif
#if LLVM_ON_UNIX
#include <unistd.h>
#endif
using namespace llvm;
/// \brief Attempt to read the lock file with the given name, if it exists.
///
/// \param LockFileName The name of the lock file to read.
///
/// \returns The process ID of the process that owns this lock file
Optional<std::pair<std::string, int> >
LockFileManager::readLockFile(StringRef LockFileName) {
// Check whether the lock file exists. If not, clearly there's nothing
// to read, so we just return.
if (!sys::fs::exists(LockFileName))
return None;
// Read the owning host and PID out of the lock file. If it appears that the
// owning process is dead, the lock file is invalid.
std::unique_ptr<MemoryBuffer> MB;
if (MemoryBuffer::getFile(LockFileName, MB))
return None;
StringRef Hostname;
StringRef PIDStr;
std::tie(Hostname, PIDStr) = getToken(MB->getBuffer(), " ");
PIDStr = PIDStr.substr(PIDStr.find_first_not_of(" "));
int PID;
if (!PIDStr.getAsInteger(10, PID))
return std::make_pair(std::string(Hostname), PID);
// Delete the lock file. It's invalid anyway.
sys::fs::remove(LockFileName);
return None;
}
bool LockFileManager::processStillExecuting(StringRef Hostname, int PID) {
#if LLVM_ON_UNIX && !defined(__ANDROID__)
char MyHostname[256];
MyHostname[255] = 0;
MyHostname[0] = 0;
gethostname(MyHostname, 255);
// Check whether the process is dead. If so, we're done.
if (MyHostname == Hostname && getsid(PID) == -1 && errno == ESRCH)
return false;
#endif
return true;
}
LockFileManager::LockFileManager(StringRef FileName)
{
this->FileName = FileName;
LockFileName = FileName;
LockFileName += ".lock";
// If the lock file already exists, don't bother to try to create our own
// lock file; it won't work anyway. Just figure out who owns this lock file.
if ((Owner = readLockFile(LockFileName)))
return;
// Create a lock file that is unique to this instance.
UniqueLockFileName = LockFileName;
UniqueLockFileName += "-%%%%%%%%";
int UniqueLockFileID;
if (error_code EC
= sys::fs::createUniqueFile(UniqueLockFileName.str(),
UniqueLockFileID,
UniqueLockFileName)) {
Error = EC;
return;
}
// Write our process ID to our unique lock file.
{
raw_fd_ostream Out(UniqueLockFileID, /*shouldClose=*/true);
#if LLVM_ON_UNIX
// FIXME: move getpid() call into LLVM
char hostname[256];
hostname[255] = 0;
hostname[0] = 0;
gethostname(hostname, 255);
Out << hostname << ' ' << getpid();
#else
Out << "localhost 1";
#endif
Out.close();
if (Out.has_error()) {
// We failed to write out PID, so make up an excuse, remove the
// unique lock file, and fail.
Error = make_error_code(errc::no_space_on_device);
sys::fs::remove(UniqueLockFileName.c_str());
return;
}
}
while (1) {
// Create a link from the lock file name. If this succeeds, we're done.
error_code EC =
sys::fs::create_link(UniqueLockFileName.str(), LockFileName.str());
if (EC == errc::success)
return;
if (EC != errc::file_exists) {
Error = EC;
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.str());
return;
}
if (!sys::fs::exists(LockFileName.str())) {
// The previous owner released the lock file before we could read it.
// Try to get ownership again.
continue;
}
// There is a lock file that nobody owns; try to clean it up and get
// ownership.
if ((EC = sys::fs::remove(LockFileName.str()))) {
Error = EC;
return;
}
}
}
LockFileManager::LockFileState LockFileManager::getState() const {
if (Owner)
return LFS_Shared;
if (Error)
return LFS_Error;
return LFS_Owned;
}
LockFileManager::~LockFileManager() {
if (getState() != LFS_Owned)
return;
// Since we own the lock, remove the lock file and our own unique lock file.
sys::fs::remove(LockFileName.str());
sys::fs::remove(UniqueLockFileName.str());
}
void LockFileManager::waitForUnlock() {
if (getState() != LFS_Shared)
return;
#if LLVM_ON_WIN32
unsigned long Interval = 1;
#else
struct timespec Interval;
Interval.tv_sec = 0;
Interval.tv_nsec = 1000000;
#endif
// Don't wait more than five minutes for the file to appear.
unsigned MaxSeconds = 300;
bool LockFileGone = false;
do {
// Sleep for the designated interval, to allow the owning process time to
// finish up and remove the lock file.
// FIXME: Should we hook in to system APIs to get a notification when the
// lock file is deleted?
#if LLVM_ON_WIN32
Sleep(Interval);
#else
nanosleep(&Interval, NULL);
#endif
bool LockFileJustDisappeared = false;
// If the lock file is still expected to be there, check whether it still
// is.
if (!LockFileGone) {
bool Exists;
if (!sys::fs::exists(LockFileName.str(), Exists) && !Exists) {
LockFileGone = true;
LockFileJustDisappeared = true;
}
}
// If the lock file is no longer there, check if the original file is
// available now.
if (LockFileGone) {
if (sys::fs::exists(FileName.str())) {
return;
}
// The lock file is gone, so now we're waiting for the original file to
// show up. If this just happened, reset our waiting intervals and keep
// waiting.
if (LockFileJustDisappeared) {
MaxSeconds = 5;
#if LLVM_ON_WIN32
Interval = 1;
#else
Interval.tv_sec = 0;
Interval.tv_nsec = 1000000;
#endif
continue;
}
}
// If we're looking for the lock file to disappear, but the process
// owning the lock died without cleaning up, just bail out.
if (!LockFileGone &&
!processStillExecuting((*Owner).first, (*Owner).second)) {
return;
}
// Exponentially increase the time we wait for the lock to be removed.
#if LLVM_ON_WIN32
Interval *= 2;
#else
Interval.tv_sec *= 2;
Interval.tv_nsec *= 2;
if (Interval.tv_nsec >= 1000000000) {
++Interval.tv_sec;
Interval.tv_nsec -= 1000000000;
}
#endif
} while (
#if LLVM_ON_WIN32
Interval < MaxSeconds * 1000
#else
Interval.tv_sec < (time_t)MaxSeconds
#endif
);
// Give up.
}