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