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