LockFileManager.cpp revision 36b56886974eae4f9c5ebc96befd3e7bfe5de338
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/ADT/STLExtras.h" 11#include "llvm/ADT/StringExtras.h" 12#include "llvm/Support/FileSystem.h" 13#include "llvm/Support/MemoryBuffer.h" 14#include "llvm/Support/Path.h" 15#include "llvm/Support/raw_ostream.h" 16#include <sys/stat.h> 17#include <sys/types.h> 18#if LLVM_ON_WIN32 19#include <windows.h> 20#endif 21#if LLVM_ON_UNIX 22#include <unistd.h> 23#endif 24using namespace llvm; 25 26/// \brief Attempt to read the lock file with the given name, if it exists. 27/// 28/// \param LockFileName The name of the lock file to read. 29/// 30/// \returns The process ID of the process that owns this lock file 31Optional<std::pair<std::string, int> > 32LockFileManager::readLockFile(StringRef LockFileName) { 33 // Read the owning host and PID out of the lock file. If it appears that the 34 // owning process is dead, the lock file is invalid. 35 std::unique_ptr<MemoryBuffer> MB; 36 if (MemoryBuffer::getFile(LockFileName, MB)) { 37 sys::fs::remove(LockFileName); 38 return None; 39 } 40 41 StringRef Hostname; 42 StringRef PIDStr; 43 std::tie(Hostname, PIDStr) = getToken(MB->getBuffer(), " "); 44 PIDStr = PIDStr.substr(PIDStr.find_first_not_of(" ")); 45 int PID; 46 if (!PIDStr.getAsInteger(10, PID)) 47 return std::make_pair(std::string(Hostname), PID); 48 49 // Delete the lock file. It's invalid anyway. 50 sys::fs::remove(LockFileName); 51 return None; 52} 53 54bool LockFileManager::processStillExecuting(StringRef Hostname, int PID) { 55#if LLVM_ON_UNIX && !defined(__ANDROID__) 56 char MyHostname[256]; 57 MyHostname[255] = 0; 58 MyHostname[0] = 0; 59 gethostname(MyHostname, 255); 60 // Check whether the process is dead. If so, we're done. 61 if (MyHostname == Hostname && getsid(PID) == -1 && errno == ESRCH) 62 return false; 63#endif 64 65 return true; 66} 67 68LockFileManager::LockFileManager(StringRef FileName) 69{ 70 this->FileName = FileName; 71 if (error_code EC = sys::fs::make_absolute(this->FileName)) { 72 Error = EC; 73 return; 74 } 75 LockFileName = this->FileName; 76 LockFileName += ".lock"; 77 78 // If the lock file already exists, don't bother to try to create our own 79 // lock file; it won't work anyway. Just figure out who owns this lock file. 80 if ((Owner = readLockFile(LockFileName))) 81 return; 82 83 // Create a lock file that is unique to this instance. 84 UniqueLockFileName = LockFileName; 85 UniqueLockFileName += "-%%%%%%%%"; 86 int UniqueLockFileID; 87 if (error_code EC 88 = sys::fs::createUniqueFile(UniqueLockFileName.str(), 89 UniqueLockFileID, 90 UniqueLockFileName)) { 91 Error = EC; 92 return; 93 } 94 95 // Write our process ID to our unique lock file. 96 { 97 raw_fd_ostream Out(UniqueLockFileID, /*shouldClose=*/true); 98 99#if LLVM_ON_UNIX 100 // FIXME: move getpid() call into LLVM 101 char hostname[256]; 102 hostname[255] = 0; 103 hostname[0] = 0; 104 gethostname(hostname, 255); 105 Out << hostname << ' ' << getpid(); 106#else 107 Out << "localhost 1"; 108#endif 109 Out.close(); 110 111 if (Out.has_error()) { 112 // We failed to write out PID, so make up an excuse, remove the 113 // unique lock file, and fail. 114 Error = make_error_code(errc::no_space_on_device); 115 sys::fs::remove(UniqueLockFileName.c_str()); 116 return; 117 } 118 } 119 120 while (1) { 121 // Create a link from the lock file name. If this succeeds, we're done. 122 error_code EC = 123 sys::fs::create_link(UniqueLockFileName.str(), LockFileName.str()); 124 if (EC == errc::success) 125 return; 126 127 if (EC != errc::file_exists) { 128 Error = EC; 129 return; 130 } 131 132 // Someone else managed to create the lock file first. Read the process ID 133 // from the lock file. 134 if ((Owner = readLockFile(LockFileName))) { 135 // Wipe out our unique lock file (it's useless now) 136 sys::fs::remove(UniqueLockFileName.str()); 137 return; 138 } 139 140 if (!sys::fs::exists(LockFileName.str())) { 141 // The previous owner released the lock file before we could read it. 142 // Try to get ownership again. 143 continue; 144 } 145 146 // There is a lock file that nobody owns; try to clean it up and get 147 // ownership. 148 if ((EC = sys::fs::remove(LockFileName.str()))) { 149 Error = EC; 150 return; 151 } 152 } 153} 154 155LockFileManager::LockFileState LockFileManager::getState() const { 156 if (Owner) 157 return LFS_Shared; 158 159 if (Error) 160 return LFS_Error; 161 162 return LFS_Owned; 163} 164 165LockFileManager::~LockFileManager() { 166 if (getState() != LFS_Owned) 167 return; 168 169 // Since we own the lock, remove the lock file and our own unique lock file. 170 sys::fs::remove(LockFileName.str()); 171 sys::fs::remove(UniqueLockFileName.str()); 172} 173 174void LockFileManager::waitForUnlock() { 175 if (getState() != LFS_Shared) 176 return; 177 178#if LLVM_ON_WIN32 179 unsigned long Interval = 1; 180#else 181 struct timespec Interval; 182 Interval.tv_sec = 0; 183 Interval.tv_nsec = 1000000; 184#endif 185 // Don't wait more than five minutes for the file to appear. 186 unsigned MaxSeconds = 300; 187 bool LockFileGone = false; 188 do { 189 // Sleep for the designated interval, to allow the owning process time to 190 // finish up and remove the lock file. 191 // FIXME: Should we hook in to system APIs to get a notification when the 192 // lock file is deleted? 193#if LLVM_ON_WIN32 194 Sleep(Interval); 195#else 196 nanosleep(&Interval, NULL); 197#endif 198 bool LockFileJustDisappeared = false; 199 200 // If the lock file is still expected to be there, check whether it still 201 // is. 202 if (!LockFileGone) { 203 bool Exists; 204 if (!sys::fs::exists(LockFileName.str(), Exists) && !Exists) { 205 LockFileGone = true; 206 LockFileJustDisappeared = true; 207 } 208 } 209 210 // If the lock file is no longer there, check if the original file is 211 // available now. 212 if (LockFileGone) { 213 if (sys::fs::exists(FileName.str())) { 214 return; 215 } 216 217 // The lock file is gone, so now we're waiting for the original file to 218 // show up. If this just happened, reset our waiting intervals and keep 219 // waiting. 220 if (LockFileJustDisappeared) { 221 MaxSeconds = 5; 222 223#if LLVM_ON_WIN32 224 Interval = 1; 225#else 226 Interval.tv_sec = 0; 227 Interval.tv_nsec = 1000000; 228#endif 229 continue; 230 } 231 } 232 233 // If we're looking for the lock file to disappear, but the process 234 // owning the lock died without cleaning up, just bail out. 235 if (!LockFileGone && 236 !processStillExecuting((*Owner).first, (*Owner).second)) { 237 return; 238 } 239 240 // Exponentially increase the time we wait for the lock to be removed. 241#if LLVM_ON_WIN32 242 Interval *= 2; 243#else 244 Interval.tv_sec *= 2; 245 Interval.tv_nsec *= 2; 246 if (Interval.tv_nsec >= 1000000000) { 247 ++Interval.tv_sec; 248 Interval.tv_nsec -= 1000000000; 249 } 250#endif 251 } while ( 252#if LLVM_ON_WIN32 253 Interval < MaxSeconds * 1000 254#else 255 Interval.tv_sec < (time_t)MaxSeconds 256#endif 257 ); 258 259 // Give up. 260} 261