LCOV - code coverage report
Current view: top level - lib/Support - LockFileManager.cpp (source / functions) Hit Total Coverage
Test: llvm-toolchain.info Lines: 65 120 54.2 %
Date: 2017-09-14 15:23:50 Functions: 7 10 70.0 %
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             : #if LLVM_ON_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             : /// \brief 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        1617 : 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        3234 :       MemoryBuffer::getFile(LockFileName);
      57        1617 :   if (!MBOrErr) {
      58        1616 :     sys::fs::remove(LockFileName);
      59             :     return None;
      60             :   }
      61           2 :   MemoryBuffer &MB = *MBOrErr.get();
      62             : 
      63             :   StringRef Hostname;
      64           1 :   StringRef PIDStr;
      65           4 :   std::tie(Hostname, PIDStr) = getToken(MB.getBuffer(), " ");
      66           2 :   PIDStr = PIDStr.substr(PIDStr.find_first_not_of(" "));
      67             :   int PID;
      68           1 :   if (!PIDStr.getAsInteger(10, PID)) {
      69           3 :     auto Owner = std::make_pair(std::string(Hostname), PID);
      70           2 :     if (processStillExecuting(Owner.first, Owner.second))
      71           2 :       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        1609 : static std::error_code getHostID(SmallVectorImpl<char> &HostID) {
      80        1609 :   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        1609 :   HostName[255] = 0;
      97        1609 :   HostName[0] = 0;
      98        1609 :   gethostname(HostName, 255);
      99        1609 :   StringRef HostNameRef(HostName);
     100        1609 :   HostID.append(HostNameRef.begin(), HostNameRef.end());
     101             : 
     102             : #else
     103             :   StringRef Dummy("localhost");
     104             :   HostID.append(Dummy.begin(), Dummy.end());
     105             : #endif
     106             : 
     107        1609 :   return std::error_code();
     108             : }
     109             : 
     110           1 : bool LockFileManager::processStillExecuting(StringRef HostID, int PID) {
     111             : #if LLVM_ON_UNIX && !defined(__ANDROID__)
     112           2 :   SmallString<256> StoredHostID;
     113           1 :   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           2 :   if (StoredHostID == HostID && getsid(PID) == -1 && errno == ESRCH)
     118             :     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        1608 :   : Filename(Name), RemoveImmediately(true) {
     139        1608 :     sys::RemoveFileOnSignal(Filename, nullptr);
     140             :   }
     141             : 
     142        1608 :   ~RemoveUniqueLockFileOnSignal() {
     143        1608 :     if (!RemoveImmediately) {
     144             :       // Leave the signal handler enabled. It will be removed when the lock is
     145             :       // released.
     146             :       return;
     147             :     }
     148           0 :     sys::fs::remove(Filename);
     149           0 :     sys::DontRemoveFileOnSignal(Filename);
     150        1608 :   }
     151             : 
     152        1608 :   void lockAcquired() { RemoveImmediately = false; }
     153             : };
     154             : 
     155             : } // end anonymous namespace
     156             : 
     157       11319 : LockFileManager::LockFileManager(StringRef FileName)
     158             : {
     159        3234 :   this->FileName = FileName;
     160        1617 :   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        3234 :   LockFileName = this->FileName;
     167        3234 :   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        4851 :   if ((Owner = readLockFile(LockFileName)))
     172             :     return;
     173             : 
     174             :   // Create a lock file that is unique to this instance.
     175        3232 :   UniqueLockFileName = LockFileName;
     176        3232 :   UniqueLockFileName += "-%%%%%%%%";
     177             :   int UniqueLockFileID;
     178        1616 :   if (std::error_code EC = sys::fs::createUniqueFile(
     179        3232 :           UniqueLockFileName, UniqueLockFileID, UniqueLockFileName)) {
     180          24 :     std::string S("failed to create unique file ");
     181          40 :     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        3216 :     SmallString<256> HostID;
     189        1608 :     if (auto EC = getHostID(HostID)) {
     190           0 :       setError(EC, "failed to get host id");
     191           0 :       return;
     192             :     }
     193             : 
     194        3216 :     raw_fd_ostream Out(UniqueLockFileID, /*shouldClose=*/true);
     195        3216 :     Out << HostID << ' ';
     196             : #if LLVM_ON_UNIX
     197        3216 :     Out << getpid();
     198             : #else
     199             :     Out << "1";
     200             : #endif
     201        1608 :     Out.close();
     202             : 
     203        1608 :     if (Out.has_error()) {
     204             :       // We failed to write out PID, so make up an excuse, remove the
     205             :       // unique lock file, and fail.
     206           0 :       auto EC = make_error_code(errc::no_space_on_device);
     207           0 :       std::string S("failed to write to ");
     208           0 :       S.append(UniqueLockFileName.str());
     209           0 :       setError(EC, S);
     210           0 :       sys::fs::remove(UniqueLockFileName);
     211             :       return;
     212             :     }
     213             :   }
     214             : 
     215             :   // Clean up the unique file on signal, which also releases the lock if it is
     216             :   // held since the .lock symlink will point to a nonexistent file.
     217        4824 :   RemoveUniqueLockFileOnSignal RemoveUniqueFile(UniqueLockFileName);
     218             : 
     219             :   while (true) {
     220             :     // Create a link from the lock file name. If this succeeds, we're done.
     221             :     std::error_code EC =
     222        4824 :         sys::fs::create_link(UniqueLockFileName, LockFileName);
     223        1608 :     if (!EC) {
     224        1608 :       RemoveUniqueFile.lockAcquired();
     225        1608 :       return;
     226             :     }
     227             : 
     228           0 :     if (EC != errc::file_exists) {
     229           0 :       std::string S("failed to create link ");
     230           0 :       raw_string_ostream OSS(S);
     231           0 :       OSS << LockFileName.str() << " to " << UniqueLockFileName.str();
     232           0 :       setError(EC, OSS.str());
     233             :       return;
     234             :     }
     235             : 
     236             :     // Someone else managed to create the lock file first. Read the process ID
     237             :     // from the lock file.
     238           0 :     if ((Owner = readLockFile(LockFileName))) {
     239             :       // Wipe out our unique lock file (it's useless now)
     240           0 :       sys::fs::remove(UniqueLockFileName);
     241           0 :       return;
     242             :     }
     243             : 
     244           0 :     if (!sys::fs::exists(LockFileName)) {
     245             :       // The previous owner released the lock file before we could read it.
     246             :       // Try to get ownership again.
     247           0 :       continue;
     248             :     }
     249             : 
     250             :     // There is a lock file that nobody owns; try to clean it up and get
     251             :     // ownership.
     252           0 :     if ((EC = sys::fs::remove(LockFileName))) {
     253           0 :       std::string S("failed to remove lockfile ");
     254           0 :       S.append(UniqueLockFileName.str());
     255           0 :       setError(EC, S);
     256             :       return;
     257             :     }
     258             :   }
     259             : }
     260             : 
     261        3234 : LockFileManager::LockFileState LockFileManager::getState() const {
     262        3234 :   if (Owner)
     263             :     return LFS_Shared;
     264             : 
     265        3232 :   if (Error)
     266             :     return LFS_Error;
     267             : 
     268        3216 :   return LFS_Owned;
     269             : }
     270             : 
     271           0 : std::string LockFileManager::getErrorMessage() const {
     272           0 :   if (Error) {
     273           0 :     std::string Str(ErrorDiagMsg);
     274           0 :     std::string ErrCodeMsg = Error->message();
     275           0 :     raw_string_ostream OSS(Str);
     276           0 :     if (!ErrCodeMsg.empty())
     277           0 :       OSS << ": " << Error->message();
     278           0 :     OSS.flush();
     279           0 :     return Str;
     280             :   }
     281           0 :   return "";
     282             : }
     283             : 
     284       12927 : LockFileManager::~LockFileManager() {
     285        1617 :   if (getState() != LFS_Owned)
     286             :     return;
     287             : 
     288             :   // Since we own the lock, remove the lock file and our own unique lock file.
     289        3216 :   sys::fs::remove(LockFileName);
     290        3216 :   sys::fs::remove(UniqueLockFileName);
     291             :   // The unique file is now gone, so remove it from the signal handler. This
     292             :   // matches a sys::RemoveFileOnSignal() in LockFileManager().
     293        3216 :   sys::DontRemoveFileOnSignal(UniqueLockFileName);
     294        1617 : }
     295             : 
     296           0 : LockFileManager::WaitForUnlockResult LockFileManager::waitForUnlock() {
     297           0 :   if (getState() != LFS_Shared)
     298             :     return Res_Success;
     299             : 
     300             : #if LLVM_ON_WIN32
     301             :   unsigned long Interval = 1;
     302             : #else
     303             :   struct timespec Interval;
     304           0 :   Interval.tv_sec = 0;
     305           0 :   Interval.tv_nsec = 1000000;
     306             : #endif
     307             :   // Don't wait more than 40s per iteration. Total timeout for the file
     308             :   // to appear is ~1.5 minutes.
     309           0 :   const unsigned MaxSeconds = 40;
     310             :   do {
     311             :     // Sleep for the designated interval, to allow the owning process time to
     312             :     // finish up and remove the lock file.
     313             :     // FIXME: Should we hook in to system APIs to get a notification when the
     314             :     // lock file is deleted?
     315             : #if LLVM_ON_WIN32
     316             :     Sleep(Interval);
     317             : #else
     318           0 :     nanosleep(&Interval, nullptr);
     319             : #endif
     320             : 
     321           0 :     if (sys::fs::access(LockFileName.c_str(), sys::fs::AccessMode::Exist) ==
     322             :         errc::no_such_file_or_directory) {
     323             :       // If the original file wasn't created, somone thought the lock was dead.
     324           0 :       if (!sys::fs::exists(FileName))
     325             :         return Res_OwnerDied;
     326           0 :       return Res_Success;
     327             :     }
     328             : 
     329             :     // If the process owning the lock died without cleaning up, just bail out.
     330           0 :     if (!processStillExecuting((*Owner).first, (*Owner).second))
     331             :       return Res_OwnerDied;
     332             : 
     333             :     // Exponentially increase the time we wait for the lock to be removed.
     334             : #if LLVM_ON_WIN32
     335             :     Interval *= 2;
     336             : #else
     337           0 :     Interval.tv_sec *= 2;
     338           0 :     Interval.tv_nsec *= 2;
     339           0 :     if (Interval.tv_nsec >= 1000000000) {
     340           0 :       ++Interval.tv_sec;
     341           0 :       Interval.tv_nsec -= 1000000000;
     342             :     }
     343             : #endif
     344             :   } while (
     345             : #if LLVM_ON_WIN32
     346             :            Interval < MaxSeconds * 1000
     347             : #else
     348           0 :            Interval.tv_sec < (time_t)MaxSeconds
     349             : #endif
     350             :            );
     351             : 
     352             :   // Give up.
     353             :   return Res_Timeout;
     354             : }
     355             : 
     356           0 : std::error_code LockFileManager::unsafeRemoveLockFile() {
     357           0 :   return sys::fs::remove(LockFileName);
     358             : }

Generated by: LCOV version 1.13