diff options
Diffstat (limited to 'contrib/llvm/lib/Support/LockFileManager.cpp')
| -rw-r--r-- | contrib/llvm/lib/Support/LockFileManager.cpp | 216 | 
1 files changed, 216 insertions, 0 deletions
| diff --git a/contrib/llvm/lib/Support/LockFileManager.cpp b/contrib/llvm/lib/Support/LockFileManager.cpp new file mode 100644 index 000000000000..64404a1a8e77 --- /dev/null +++ b/contrib/llvm/lib/Support/LockFileManager.cpp @@ -0,0 +1,216 @@ +//===--- 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/Support/FileSystem.h" +#include "llvm/Support/raw_ostream.h" +#include <fstream> +#include <sys/types.h> +#include <sys/stat.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. +  bool Exists = false; +  if (sys::fs::exists(LockFileName, Exists) || !Exists) +    return Optional<std::pair<std::string, int> >(); + +  // 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. +  int PID = 0; +  std::string Hostname; +  std::ifstream Input(LockFileName.str().c_str()); +  if (Input >> Hostname >> PID && PID > 0 && +      processStillExecuting(Hostname, PID)) +    return std::make_pair(Hostname, PID); + +  // Delete the lock file. It's invalid anyway. +  bool Existed; +  sys::fs::remove(LockFileName, Existed); +  return Optional<std::pair<std::string, int> >(); +} + +bool LockFileManager::processStillExecuting(StringRef Hostname, int PID) { +#if LLVM_ON_UNIX +  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) +{ +  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::unique_file(UniqueLockFileName.str(), +                                     UniqueLockFileID, +                                     UniqueLockFileName, +                                     /*makeAbsolute=*/false)) { +    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); +      bool Existed; +      sys::fs::remove(UniqueLockFileName.c_str(), Existed); +      return; +    } +  } + +  // Create a hard link from the lock file name. If this succeeds, we're done. +  error_code EC +    = sys::fs::create_hard_link(UniqueLockFileName.str(), +                                      LockFileName.str()); +  if (EC == errc::success) +    return; + +  // Creating the hard link failed. + +#ifdef LLVM_ON_UNIX +  // The creation of the hard link may appear to fail, but if stat'ing the +  // unique file returns a link count of 2, then we can still declare success. +  struct stat StatBuf; +  if (stat(UniqueLockFileName.c_str(), &StatBuf) == 0 && +      StatBuf.st_nlink == 2) +    return; +#endif + +  // Someone else managed to create the lock file first. Wipe out our unique +  // lock file (it's useless now) and read the process ID from the lock file. +  bool Existed; +  sys::fs::remove(UniqueLockFileName.str(), Existed); +  if ((Owner = readLockFile(LockFileName))) +    return; + +  // There is a lock file that nobody owns; try to clean it up and report +  // an error. +  sys::fs::remove(LockFileName.str(), Existed); +  Error = EC; +} + +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. +  bool Existed; +  sys::fs::remove(LockFileName.str(), Existed); +  sys::fs::remove(UniqueLockFileName.str(), Existed); +} + +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 an hour for the file to appear. +  const unsigned MaxSeconds = 3600; +  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 +    // If the file no longer exists, we're done. +    bool Exists = false; +    if (!sys::fs::exists(LockFileName.str(), Exists) && !Exists) +      return; + +    if (!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. +} | 
