Home | History | Annotate | Download | only in Support
      1 //===--- LockFileManager.cpp - File-level Locking Utility------------------===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 #include "llvm/Support/LockFileManager.h"
     10 #include "llvm/Support/FileSystem.h"
     11 #include "llvm/Support/raw_ostream.h"
     12 #include <fstream>
     13 #include <sys/stat.h>
     14 #include <sys/types.h>
     15 #if LLVM_ON_WIN32
     16 #include <windows.h>
     17 #endif
     18 #if LLVM_ON_UNIX
     19 #include <unistd.h>
     20 #endif
     21 using namespace llvm;
     22 
     23 /// \brief Attempt to read the lock file with the given name, if it exists.
     24 ///
     25 /// \param LockFileName The name of the lock file to read.
     26 ///
     27 /// \returns The process ID of the process that owns this lock file
     28 Optional<std::pair<std::string, int> >
     29 LockFileManager::readLockFile(StringRef LockFileName) {
     30   // Check whether the lock file exists. If not, clearly there's nothing
     31   // to read, so we just return.
     32   bool Exists = false;
     33   if (sys::fs::exists(LockFileName, Exists) || !Exists)
     34     return None;
     35 
     36   // Read the owning host and PID out of the lock file. If it appears that the
     37   // owning process is dead, the lock file is invalid.
     38   int PID = 0;
     39   std::string Hostname;
     40   std::ifstream Input(LockFileName.str().c_str());
     41   if (Input >> Hostname >> PID && PID > 0 &&
     42       processStillExecuting(Hostname, PID))
     43     return std::make_pair(Hostname, PID);
     44 
     45   // Delete the lock file. It's invalid anyway.
     46   bool Existed;
     47   sys::fs::remove(LockFileName, Existed);
     48   return None;
     49 }
     50 
     51 bool LockFileManager::processStillExecuting(StringRef Hostname, int PID) {
     52 #if LLVM_ON_UNIX && !defined(__ANDROID__)
     53   char MyHostname[256];
     54   MyHostname[255] = 0;
     55   MyHostname[0] = 0;
     56   gethostname(MyHostname, 255);
     57   // Check whether the process is dead. If so, we're done.
     58   if (MyHostname == Hostname && getsid(PID) == -1 && errno == ESRCH)
     59     return false;
     60 #endif
     61 
     62   return true;
     63 }
     64 
     65 LockFileManager::LockFileManager(StringRef FileName)
     66 {
     67   this->FileName = FileName;
     68   LockFileName = FileName;
     69   LockFileName += ".lock";
     70 
     71   // If the lock file already exists, don't bother to try to create our own
     72   // lock file; it won't work anyway. Just figure out who owns this lock file.
     73   if ((Owner = readLockFile(LockFileName)))
     74     return;
     75 
     76   // Create a lock file that is unique to this instance.
     77   UniqueLockFileName = LockFileName;
     78   UniqueLockFileName += "-%%%%%%%%";
     79   int UniqueLockFileID;
     80   if (error_code EC
     81         = sys::fs::unique_file(UniqueLockFileName.str(),
     82                                      UniqueLockFileID,
     83                                      UniqueLockFileName,
     84                                      /*makeAbsolute=*/false)) {
     85     Error = EC;
     86     return;
     87   }
     88 
     89   // Write our process ID to our unique lock file.
     90   {
     91     raw_fd_ostream Out(UniqueLockFileID, /*shouldClose=*/true);
     92 
     93 #if LLVM_ON_UNIX
     94     // FIXME: move getpid() call into LLVM
     95     char hostname[256];
     96     hostname[255] = 0;
     97     hostname[0] = 0;
     98     gethostname(hostname, 255);
     99     Out << hostname << ' ' << getpid();
    100 #else
    101     Out << "localhost 1";
    102 #endif
    103     Out.close();
    104 
    105     if (Out.has_error()) {
    106       // We failed to write out PID, so make up an excuse, remove the
    107       // unique lock file, and fail.
    108       Error = make_error_code(errc::no_space_on_device);
    109       bool Existed;
    110       sys::fs::remove(UniqueLockFileName.c_str(), Existed);
    111       return;
    112     }
    113   }
    114 
    115   // Create a hard link from the lock file name. If this succeeds, we're done.
    116   error_code EC
    117     = sys::fs::create_hard_link(UniqueLockFileName.str(),
    118                                       LockFileName.str());
    119   if (EC == errc::success)
    120     return;
    121 
    122   // Creating the hard link failed.
    123 
    124 #ifdef LLVM_ON_UNIX
    125   // The creation of the hard link may appear to fail, but if stat'ing the
    126   // unique file returns a link count of 2, then we can still declare success.
    127   struct stat StatBuf;
    128   if (stat(UniqueLockFileName.c_str(), &StatBuf) == 0 &&
    129       StatBuf.st_nlink == 2)
    130     return;
    131 #endif
    132 
    133   // Someone else managed to create the lock file first. Wipe out our unique
    134   // lock file (it's useless now) and read the process ID from the lock file.
    135   bool Existed;
    136   sys::fs::remove(UniqueLockFileName.str(), Existed);
    137   if ((Owner = readLockFile(LockFileName)))
    138     return;
    139 
    140   // There is a lock file that nobody owns; try to clean it up and report
    141   // an error.
    142   sys::fs::remove(LockFileName.str(), Existed);
    143   Error = EC;
    144 }
    145 
    146 LockFileManager::LockFileState LockFileManager::getState() const {
    147   if (Owner)
    148     return LFS_Shared;
    149 
    150   if (Error)
    151     return LFS_Error;
    152 
    153   return LFS_Owned;
    154 }
    155 
    156 LockFileManager::~LockFileManager() {
    157   if (getState() != LFS_Owned)
    158     return;
    159 
    160   // Since we own the lock, remove the lock file and our own unique lock file.
    161   bool Existed;
    162   sys::fs::remove(LockFileName.str(), Existed);
    163   sys::fs::remove(UniqueLockFileName.str(), Existed);
    164 }
    165 
    166 void LockFileManager::waitForUnlock() {
    167   if (getState() != LFS_Shared)
    168     return;
    169 
    170 #if LLVM_ON_WIN32
    171   unsigned long Interval = 1;
    172 #else
    173   struct timespec Interval;
    174   Interval.tv_sec = 0;
    175   Interval.tv_nsec = 1000000;
    176 #endif
    177   // Don't wait more than an hour for the file to appear.
    178   const unsigned MaxSeconds = 3600;
    179   bool LockFileGone = false;
    180   do {
    181     // Sleep for the designated interval, to allow the owning process time to
    182     // finish up and remove the lock file.
    183     // FIXME: Should we hook in to system APIs to get a notification when the
    184     // lock file is deleted?
    185 #if LLVM_ON_WIN32
    186     Sleep(Interval);
    187 #else
    188     nanosleep(&Interval, NULL);
    189 #endif
    190     // If the lock file no longer exists, wait for the actual file.
    191     bool Exists = false;
    192     if (!LockFileGone) {
    193       if (!sys::fs::exists(LockFileName.str(), Exists) && !Exists) {
    194         LockFileGone = true;
    195         Exists = false;
    196       }
    197     }
    198     if (LockFileGone) {
    199       if (!sys::fs::exists(FileName.str(), Exists) && Exists)
    200         return;
    201     }
    202 
    203     if (!processStillExecuting((*Owner).first, (*Owner).second))
    204       return;
    205 
    206     // Exponentially increase the time we wait for the lock to be removed.
    207 #if LLVM_ON_WIN32
    208     Interval *= 2;
    209 #else
    210     Interval.tv_sec *= 2;
    211     Interval.tv_nsec *= 2;
    212     if (Interval.tv_nsec >= 1000000000) {
    213       ++Interval.tv_sec;
    214       Interval.tv_nsec -= 1000000000;
    215     }
    216 #endif
    217   } while (
    218 #if LLVM_ON_WIN32
    219            Interval < MaxSeconds * 1000
    220 #else
    221            Interval.tv_sec < (time_t)MaxSeconds
    222 #endif
    223            );
    224 
    225   // Give up.
    226 }
    227