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/StringExtras.h" 11 #include "llvm/Support/Errc.h" 12 #include "llvm/Support/FileSystem.h" 13 #include "llvm/Support/MemoryBuffer.h" 14 #include "llvm/Support/raw_ostream.h" 15 #include "llvm/Support/Signals.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 24 using 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 31 Optional<std::pair<std::string, int> > 32 LockFileManager::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 ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr = 36 MemoryBuffer::getFile(LockFileName); 37 if (!MBOrErr) { 38 sys::fs::remove(LockFileName); 39 return None; 40 } 41 MemoryBuffer &MB = *MBOrErr.get(); 42 43 StringRef Hostname; 44 StringRef PIDStr; 45 std::tie(Hostname, PIDStr) = getToken(MB.getBuffer(), " "); 46 PIDStr = PIDStr.substr(PIDStr.find_first_not_of(" ")); 47 int PID; 48 if (!PIDStr.getAsInteger(10, PID)) { 49 auto Owner = std::make_pair(std::string(Hostname), PID); 50 if (processStillExecuting(Owner.first, Owner.second)) 51 return Owner; 52 } 53 54 // Delete the lock file. It's invalid anyway. 55 sys::fs::remove(LockFileName); 56 return None; 57 } 58 59 bool LockFileManager::processStillExecuting(StringRef Hostname, int PID) { 60 #if LLVM_ON_UNIX && !defined(__ANDROID__) 61 char MyHostname[256]; 62 MyHostname[255] = 0; 63 MyHostname[0] = 0; 64 gethostname(MyHostname, 255); 65 // Check whether the process is dead. If so, we're done. 66 if (MyHostname == Hostname && getsid(PID) == -1 && errno == ESRCH) 67 return false; 68 #endif 69 70 return true; 71 } 72 73 namespace { 74 /// An RAII helper object ensure that the unique lock file is removed. 75 /// 76 /// Ensures that if there is an error or a signal before we finish acquiring the 77 /// lock, the unique file will be removed. And if we successfully take the lock, 78 /// the signal handler is left in place so that signals while the lock is held 79 /// will remove the unique lock file. The caller should ensure there is a 80 /// matching call to sys::DontRemoveFileOnSignal when the lock is released. 81 class RemoveUniqueLockFileOnSignal { 82 StringRef Filename; 83 bool RemoveImmediately; 84 public: 85 RemoveUniqueLockFileOnSignal(StringRef Name) 86 : Filename(Name), RemoveImmediately(true) { 87 sys::RemoveFileOnSignal(Filename, nullptr); 88 } 89 ~RemoveUniqueLockFileOnSignal() { 90 if (!RemoveImmediately) { 91 // Leave the signal handler enabled. It will be removed when the lock is 92 // released. 93 return; 94 } 95 sys::fs::remove(Filename); 96 sys::DontRemoveFileOnSignal(Filename); 97 } 98 void lockAcquired() { RemoveImmediately = false; } 99 }; 100 } // end anonymous namespace 101 102 LockFileManager::LockFileManager(StringRef FileName) 103 { 104 this->FileName = FileName; 105 if (std::error_code EC = sys::fs::make_absolute(this->FileName)) { 106 Error = EC; 107 return; 108 } 109 LockFileName = this->FileName; 110 LockFileName += ".lock"; 111 112 // If the lock file already exists, don't bother to try to create our own 113 // lock file; it won't work anyway. Just figure out who owns this lock file. 114 if ((Owner = readLockFile(LockFileName))) 115 return; 116 117 // Create a lock file that is unique to this instance. 118 UniqueLockFileName = LockFileName; 119 UniqueLockFileName += "-%%%%%%%%"; 120 int UniqueLockFileID; 121 if (std::error_code EC = sys::fs::createUniqueFile( 122 UniqueLockFileName, UniqueLockFileID, UniqueLockFileName)) { 123 Error = EC; 124 return; 125 } 126 127 // Write our process ID to our unique lock file. 128 { 129 raw_fd_ostream Out(UniqueLockFileID, /*shouldClose=*/true); 130 131 #if LLVM_ON_UNIX 132 // FIXME: move getpid() call into LLVM 133 char hostname[256]; 134 hostname[255] = 0; 135 hostname[0] = 0; 136 gethostname(hostname, 255); 137 Out << hostname << ' ' << getpid(); 138 #else 139 Out << "localhost 1"; 140 #endif 141 Out.close(); 142 143 if (Out.has_error()) { 144 // We failed to write out PID, so make up an excuse, remove the 145 // unique lock file, and fail. 146 Error = make_error_code(errc::no_space_on_device); 147 sys::fs::remove(UniqueLockFileName); 148 return; 149 } 150 } 151 152 // Clean up the unique file on signal, which also releases the lock if it is 153 // held since the .lock symlink will point to a nonexistent file. 154 RemoveUniqueLockFileOnSignal RemoveUniqueFile(UniqueLockFileName); 155 156 while (1) { 157 // Create a link from the lock file name. If this succeeds, we're done. 158 std::error_code EC = 159 sys::fs::create_link(UniqueLockFileName, LockFileName); 160 if (!EC) { 161 RemoveUniqueFile.lockAcquired(); 162 return; 163 } 164 165 if (EC != errc::file_exists) { 166 Error = EC; 167 return; 168 } 169 170 // Someone else managed to create the lock file first. Read the process ID 171 // from the lock file. 172 if ((Owner = readLockFile(LockFileName))) { 173 // Wipe out our unique lock file (it's useless now) 174 sys::fs::remove(UniqueLockFileName); 175 return; 176 } 177 178 if (!sys::fs::exists(LockFileName)) { 179 // The previous owner released the lock file before we could read it. 180 // Try to get ownership again. 181 continue; 182 } 183 184 // There is a lock file that nobody owns; try to clean it up and get 185 // ownership. 186 if ((EC = sys::fs::remove(LockFileName))) { 187 Error = EC; 188 return; 189 } 190 } 191 } 192 193 LockFileManager::LockFileState LockFileManager::getState() const { 194 if (Owner) 195 return LFS_Shared; 196 197 if (Error) 198 return LFS_Error; 199 200 return LFS_Owned; 201 } 202 203 LockFileManager::~LockFileManager() { 204 if (getState() != LFS_Owned) 205 return; 206 207 // Since we own the lock, remove the lock file and our own unique lock file. 208 sys::fs::remove(LockFileName); 209 sys::fs::remove(UniqueLockFileName); 210 // The unique file is now gone, so remove it from the signal handler. This 211 // matches a sys::RemoveFileOnSignal() in LockFileManager(). 212 sys::DontRemoveFileOnSignal(UniqueLockFileName); 213 } 214 215 LockFileManager::WaitForUnlockResult LockFileManager::waitForUnlock() { 216 if (getState() != LFS_Shared) 217 return Res_Success; 218 219 #if LLVM_ON_WIN32 220 unsigned long Interval = 1; 221 #else 222 struct timespec Interval; 223 Interval.tv_sec = 0; 224 Interval.tv_nsec = 1000000; 225 #endif 226 // Don't wait more than five minutes per iteration. Total timeout for the file 227 // to appear is ~8.5 mins. 228 const unsigned MaxSeconds = 5*60; 229 do { 230 // Sleep for the designated interval, to allow the owning process time to 231 // finish up and remove the lock file. 232 // FIXME: Should we hook in to system APIs to get a notification when the 233 // lock file is deleted? 234 #if LLVM_ON_WIN32 235 Sleep(Interval); 236 #else 237 nanosleep(&Interval, nullptr); 238 #endif 239 240 if (sys::fs::access(LockFileName.c_str(), sys::fs::AccessMode::Exist) == 241 errc::no_such_file_or_directory) { 242 // If the original file wasn't created, somone thought the lock was dead. 243 if (!sys::fs::exists(FileName)) 244 return Res_OwnerDied; 245 return Res_Success; 246 } 247 248 // If the process owning the lock died without cleaning up, just bail out. 249 if (!processStillExecuting((*Owner).first, (*Owner).second)) 250 return Res_OwnerDied; 251 252 // Exponentially increase the time we wait for the lock to be removed. 253 #if LLVM_ON_WIN32 254 Interval *= 2; 255 #else 256 Interval.tv_sec *= 2; 257 Interval.tv_nsec *= 2; 258 if (Interval.tv_nsec >= 1000000000) { 259 ++Interval.tv_sec; 260 Interval.tv_nsec -= 1000000000; 261 } 262 #endif 263 } while ( 264 #if LLVM_ON_WIN32 265 Interval < MaxSeconds * 1000 266 #else 267 Interval.tv_sec < (time_t)MaxSeconds 268 #endif 269 ); 270 271 // Give up. 272 return Res_Timeout; 273 } 274 275 std::error_code LockFileManager::unsafeRemoveLockFile() { 276 return sys::fs::remove(LockFileName); 277 } 278