LCOV - code coverage report
Current view: top level - lib/Support - LockFileManager.cpp (source / functions) Hit Total Coverage
Test: llvm-toolchain.info Lines: 67 102 65.7 %
Date: 2018-10-20 13:21:21 Functions: 8 11 72.7 %
Legend: Lines: hit not hit

          Line data    Source code
       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             : 
      10             : #include "llvm/Support/LockFileManager.h"
      11             : #include "llvm/ADT/None.h"
      12             : #include "llvm/ADT/SmallVector.h"
      13             : #include "llvm/ADT/StringExtras.h"
      14             : #include "llvm/Support/Errc.h"
      15             : #include "llvm/Support/ErrorOr.h"
      16             : #include "llvm/Support/FileSystem.h"
      17             : #include "llvm/Support/MemoryBuffer.h"
      18             : #include "llvm/Support/Signals.h"
      19             : #include "llvm/Support/raw_ostream.h"
      20             : #include <cerrno>
      21             : #include <ctime>
      22             : #include <memory>
      23             : #include <sys/stat.h>
      24             : #include <sys/types.h>
      25             : #include <system_error>
      26             : #include <tuple>
      27             : #ifdef _WIN32
      28             : #include <windows.h>
      29             : #endif
      30             : #if LLVM_ON_UNIX
      31             : #include <unistd.h>
      32             : #endif
      33             : 
      34             : #if defined(__APPLE__) && defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && (__MAC_OS_X_VERSION_MIN_REQUIRED > 1050)
      35             : #define USE_OSX_GETHOSTUUID 1
      36             : #else
      37             : #define USE_OSX_GETHOSTUUID 0
      38             : #endif
      39             : 
      40             : #if USE_OSX_GETHOSTUUID
      41             : #include <uuid/uuid.h>
      42             : #endif
      43             : 
      44             : using namespace llvm;
      45             : 
      46             : /// Attempt to read the lock file with the given name, if it exists.
      47             : ///
      48             : /// \param LockFileName The name of the lock file to read.
      49             : ///
      50             : /// \returns The process ID of the process that owns this lock file
      51             : Optional<std::pair<std::string, int> >
      52        1765 : LockFileManager::readLockFile(StringRef LockFileName) {
      53             :   // Read the owning host and PID out of the lock file. If it appears that the
      54             :   // owning process is dead, the lock file is invalid.
      55             :   ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr =
      56        1765 :       MemoryBuffer::getFile(LockFileName);
      57        1765 :   if (!MBOrErr) {
      58        1760 :     sys::fs::remove(LockFileName);
      59             :     return None;
      60             :   }
      61             :   MemoryBuffer &MB = *MBOrErr.get();
      62             : 
      63             :   StringRef Hostname;
      64           5 :   StringRef PIDStr;
      65           5 :   std::tie(Hostname, PIDStr) = getToken(MB.getBuffer(), " ");
      66          10 :   PIDStr = PIDStr.substr(PIDStr.find_first_not_of(" "));
      67             :   int PID;
      68           5 :   if (!PIDStr.getAsInteger(10, PID)) {
      69           5 :     auto Owner = std::make_pair(std::string(Hostname), PID);
      70           5 :     if (processStillExecuting(Owner.first, Owner.second))
      71             :       return Owner;
      72             :   }
      73             : 
      74             :   // Delete the lock file. It's invalid anyway.
      75           0 :   sys::fs::remove(LockFileName);
      76             :   return None;
      77             : }
      78             : 
      79        1805 : static std::error_code getHostID(SmallVectorImpl<char> &HostID) {
      80             :   HostID.clear();
      81             : 
      82             : #if USE_OSX_GETHOSTUUID
      83             :   // On OS X, use the more stable hardware UUID instead of hostname.
      84             :   struct timespec wait = {1, 0}; // 1 second.
      85             :   uuid_t uuid;
      86             :   if (gethostuuid(uuid, &wait) != 0)
      87             :     return std::error_code(errno, std::system_category());
      88             : 
      89             :   uuid_string_t UUIDStr;
      90             :   uuid_unparse(uuid, UUIDStr);
      91             :   StringRef UUIDRef(UUIDStr);
      92             :   HostID.append(UUIDRef.begin(), UUIDRef.end());
      93             : 
      94             : #elif LLVM_ON_UNIX
      95             :   char HostName[256];
      96        1805 :   HostName[255] = 0;
      97        1805 :   HostName[0] = 0;
      98        1805 :   gethostname(HostName, 255);
      99             :   StringRef HostNameRef(HostName);
     100        1805 :   HostID.append(HostNameRef.begin(), HostNameRef.end());
     101             : 
     102             : #else
     103             :   StringRef Dummy("localhost");
     104             :   HostID.append(Dummy.begin(), Dummy.end());
     105             : #endif
     106             : 
     107        1805 :   return std::error_code();
     108             : }
     109             : 
     110          53 : bool LockFileManager::processStillExecuting(StringRef HostID, int PID) {
     111             : #if LLVM_ON_UNIX && !defined(__ANDROID__)
     112             :   SmallString<256> StoredHostID;
     113          53 :   if (getHostID(StoredHostID))
     114             :     return true; // Conservatively assume it's executing on error.
     115             : 
     116             :   // Check whether the process is dead. If so, we're done.
     117          53 :   if (StoredHostID == HostID && getsid(PID) == -1 && errno == ESRCH)
     118           0 :     return false;
     119             : #endif
     120             : 
     121             :   return true;
     122             : }
     123             : 
     124             : namespace {
     125             : 
     126             : /// An RAII helper object ensure that the unique lock file is removed.
     127             : ///
     128             : /// Ensures that if there is an error or a signal before we finish acquiring the
     129             : /// lock, the unique file will be removed. And if we successfully take the lock,
     130             : /// the signal handler is left in place so that signals while the lock is held
     131             : /// will remove the unique lock file. The caller should ensure there is a
     132             : /// matching call to sys::DontRemoveFileOnSignal when the lock is released.
     133             : class RemoveUniqueLockFileOnSignal {
     134             :   StringRef Filename;
     135             :   bool RemoveImmediately;
     136             : public:
     137             :   RemoveUniqueLockFileOnSignal(StringRef Name)
     138        1752 :   : Filename(Name), RemoveImmediately(true) {
     139        1752 :     sys::RemoveFileOnSignal(Filename, nullptr);
     140             :   }
     141             : 
     142           0 :   ~RemoveUniqueLockFileOnSignal() {
     143        1752 :     if (!RemoveImmediately) {
     144             :       // Leave the signal handler enabled. It will be removed when the lock is
     145             :       // released.
     146        1752 :       return;
     147             :     }
     148           0 :     sys::fs::remove(Filename);
     149           0 :     sys::DontRemoveFileOnSignal(Filename);
     150        1752 :   }
     151             : 
     152        1752 :   void lockAcquired() { RemoveImmediately = false; }
     153             : };
     154             : 
     155             : } // end anonymous namespace
     156             : 
     157        1765 : LockFileManager::LockFileManager(StringRef FileName)
     158             : {
     159             :   this->FileName = FileName;
     160        1765 :   if (std::error_code EC = sys::fs::make_absolute(this->FileName)) {
     161           0 :     std::string S("failed to obtain absolute path for ");
     162           0 :     S.append(this->FileName.str());
     163           0 :     setError(EC, S);
     164             :     return;
     165             :   }
     166             :   LockFileName = this->FileName;
     167             :   LockFileName += ".lock";
     168             : 
     169             :   // If the lock file already exists, don't bother to try to create our own
     170             :   // lock file; it won't work anyway. Just figure out who owns this lock file.
     171        3535 :   if ((Owner = readLockFile(LockFileName)))
     172             :     return;
     173             : 
     174             :   // Create a lock file that is unique to this instance.
     175             :   UniqueLockFileName = LockFileName;
     176             :   UniqueLockFileName += "-%%%%%%%%";
     177             :   int UniqueLockFileID;
     178        1760 :   if (std::error_code EC = sys::fs::createUniqueFile(
     179        1760 :           UniqueLockFileName, UniqueLockFileID, UniqueLockFileName)) {
     180           8 :     std::string S("failed to create unique file ");
     181           8 :     S.append(UniqueLockFileName.str());
     182           8 :     setError(EC, S);
     183             :     return;
     184             :   }
     185             : 
     186             :   // Write our process ID to our unique lock file.
     187             :   {
     188             :     SmallString<256> HostID;
     189        1752 :     if (auto EC = getHostID(HostID)) {
     190           0 :       setError(EC, "failed to get host id");
     191           0 :       return;
     192             :     }
     193             : 
     194        3504 :     raw_fd_ostream Out(UniqueLockFileID, /*shouldClose=*/true);
     195             :     Out << HostID << ' ';
     196             : #if LLVM_ON_UNIX
     197        1752 :     Out << getpid();
     198             : #else
     199             :     Out << "1";
     200             : #endif
     201        1752 :     Out.close();
     202             : 
     203        1752 :     if (Out.has_error()) {
     204             :       // We failed to write out PID, so report the error, remove the
     205             :       // unique lock file, and fail.
     206           0 :       std::string S("failed to write to ");
     207           0 :       S.append(UniqueLockFileName.str());
     208           0 :       setError(Out.error(), S);
     209           0 :       sys::fs::remove(UniqueLockFileName);
     210             :       return;
     211             :     }
     212             :   }
     213             : 
     214             :   // Clean up the unique file on signal, which also releases the lock if it is
     215             :   // held since the .lock symlink will point to a nonexistent file.
     216        1752 :   RemoveUniqueLockFileOnSignal RemoveUniqueFile(UniqueLockFileName);
     217             : 
     218             :   while (true) {
     219             :     // Create a link from the lock file name. If this succeeds, we're done.
     220             :     std::error_code EC =
     221        1752 :         sys::fs::create_link(UniqueLockFileName, LockFileName);
     222        1752 :     if (!EC) {
     223             :       RemoveUniqueFile.lockAcquired();
     224        1752 :       return;
     225             :     }
     226             : 
     227             :     if (EC != errc::file_exists) {
     228           0 :       std::string S("failed to create link ");
     229           0 :       raw_string_ostream OSS(S);
     230           0 :       OSS << LockFileName.str() << " to " << UniqueLockFileName.str();
     231           0 :       setError(EC, OSS.str());
     232             :       return;
     233             :     }
     234             : 
     235             :     // Someone else managed to create the lock file first. Read the process ID
     236             :     // from the lock file.
     237           0 :     if ((Owner = readLockFile(LockFileName))) {
     238             :       // Wipe out our unique lock file (it's useless now)
     239           0 :       sys::fs::remove(UniqueLockFileName);
     240           0 :       return;
     241             :     }
     242             : 
     243           0 :     if (!sys::fs::exists(LockFileName)) {
     244             :       // The previous owner released the lock file before we could read it.
     245             :       // Try to get ownership again.
     246           0 :       continue;
     247             :     }
     248             : 
     249             :     // There is a lock file that nobody owns; try to clean it up and get
     250             :     // ownership.
     251           0 :     if ((EC = sys::fs::remove(LockFileName))) {
     252           0 :       std::string S("failed to remove lockfile ");
     253           0 :       S.append(UniqueLockFileName.str());
     254           0 :       setError(EC, S);
     255             :       return;
     256             :     }
     257             :   }
     258             : }
     259             : 
     260        3535 : LockFileManager::LockFileState LockFileManager::getState() const {
     261        3535 :   if (Owner)
     262             :     return LFS_Shared;
     263             : 
     264        3521 :   if (ErrorCode)
     265          16 :     return LFS_Error;
     266             : 
     267             :   return LFS_Owned;
     268             : }
     269             : 
     270           0 : std::string LockFileManager::getErrorMessage() const {
     271           0 :   if (ErrorCode) {
     272             :     std::string Str(ErrorDiagMsg);
     273             :     std::string ErrCodeMsg = ErrorCode.message();
     274           0 :     raw_string_ostream OSS(Str);
     275           0 :     if (!ErrCodeMsg.empty())
     276           0 :       OSS << ": " << ErrCodeMsg;
     277             :     return OSS.str();
     278             :   }
     279           0 :   return "";
     280             : }
     281             : 
     282        1752 : LockFileManager::~LockFileManager() {
     283        1765 :   if (getState() != LFS_Owned)
     284             :     return;
     285             : 
     286             :   // Since we own the lock, remove the lock file and our own unique lock file.
     287        3504 :   sys::fs::remove(LockFileName);
     288        3504 :   sys::fs::remove(UniqueLockFileName);
     289             :   // The unique file is now gone, so remove it from the signal handler. This
     290             :   // matches a sys::RemoveFileOnSignal() in LockFileManager().
     291        1752 :   sys::DontRemoveFileOnSignal(UniqueLockFileName);
     292        1765 : }
     293             : 
     294           4 : LockFileManager::WaitForUnlockResult LockFileManager::waitForUnlock() {
     295           4 :   if (getState() != LFS_Shared)
     296             :     return Res_Success;
     297             : 
     298             : #ifdef _WIN32
     299             :   unsigned long Interval = 1;
     300             : #else
     301             :   struct timespec Interval;
     302           4 :   Interval.tv_sec = 0;
     303           4 :   Interval.tv_nsec = 1000000;
     304             : #endif
     305             :   // Don't wait more than 40s per iteration. Total timeout for the file
     306             :   // to appear is ~1.5 minutes.
     307             :   const unsigned MaxSeconds = 40;
     308             :   do {
     309             :     // Sleep for the designated interval, to allow the owning process time to
     310             :     // finish up and remove the lock file.
     311             :     // FIXME: Should we hook in to system APIs to get a notification when the
     312             :     // lock file is deleted?
     313             : #ifdef _WIN32
     314             :     Sleep(Interval);
     315             : #else
     316          52 :     nanosleep(&Interval, nullptr);
     317             : #endif
     318             : 
     319         100 :     if (sys::fs::access(LockFileName.c_str(), sys::fs::AccessMode::Exist) ==
     320             :         errc::no_such_file_or_directory) {
     321             :       // If the original file wasn't created, somone thought the lock was dead.
     322           4 :       if (!sys::fs::exists(FileName))
     323             :         return Res_OwnerDied;
     324           4 :       return Res_Success;
     325             :     }
     326             : 
     327             :     // If the process owning the lock died without cleaning up, just bail out.
     328          96 :     if (!processStillExecuting((*Owner).first, (*Owner).second))
     329             :       return Res_OwnerDied;
     330             : 
     331             :     // Exponentially increase the time we wait for the lock to be removed.
     332             : #ifdef _WIN32
     333             :     Interval *= 2;
     334             : #else
     335          48 :     Interval.tv_sec *= 2;
     336          48 :     Interval.tv_nsec *= 2;
     337          48 :     if (Interval.tv_nsec >= 1000000000) {
     338           4 :       ++Interval.tv_sec;
     339           4 :       Interval.tv_nsec -= 1000000000;
     340             :     }
     341             : #endif
     342             :   } while (
     343             : #ifdef _WIN32
     344             :            Interval < MaxSeconds * 1000
     345             : #else
     346          48 :            Interval.tv_sec < (time_t)MaxSeconds
     347             : #endif
     348             :            );
     349             : 
     350             :   // Give up.
     351             :   return Res_Timeout;
     352             : }
     353             : 
     354           0 : std::error_code LockFileManager::unsafeRemoveLockFile() {
     355           0 :   return sys::fs::remove(LockFileName);
     356             : }

Generated by: LCOV version 1.13