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 sys::fs::remove(UniqueLockFileName.c_str()); 115 return; 116 } 117 } 118 119 // Create a hard link from the lock file name. If this succeeds, we're done. 120 error_code EC 121 = sys::fs::create_hard_link(UniqueLockFileName.str(), 122 LockFileName.str()); 123 if (EC == errc::success) 124 return; 125 126 // Creating the hard link failed. 127 128 #ifdef LLVM_ON_UNIX 129 // The creation of the hard link may appear to fail, but if stat'ing the 130 // unique file returns a link count of 2, then we can still declare success. 131 struct stat StatBuf; 132 if (stat(UniqueLockFileName.c_str(), &StatBuf) == 0 && 133 StatBuf.st_nlink == 2) 134 return; 135 #endif 136 137 // Someone else managed to create the lock file first. Wipe out our unique 138 // lock file (it's useless now) and read the process ID from the lock file. 139 sys::fs::remove(UniqueLockFileName.str()); 140 if ((Owner = readLockFile(LockFileName))) 141 return; 142 143 // There is a lock file that nobody owns; try to clean it up and report 144 // an error. 145 sys::fs::remove(LockFileName.str()); 146 Error = EC; 147 } 148 149 LockFileManager::LockFileState LockFileManager::getState() const { 150 if (Owner) 151 return LFS_Shared; 152 153 if (Error) 154 return LFS_Error; 155 156 return LFS_Owned; 157 } 158 159 LockFileManager::~LockFileManager() { 160 if (getState() != LFS_Owned) 161 return; 162 163 // Since we own the lock, remove the lock file and our own unique lock file. 164 sys::fs::remove(LockFileName.str()); 165 sys::fs::remove(UniqueLockFileName.str()); 166 } 167 168 void LockFileManager::waitForUnlock() { 169 if (getState() != LFS_Shared) 170 return; 171 172 #if LLVM_ON_WIN32 173 unsigned long Interval = 1; 174 #else 175 struct timespec Interval; 176 Interval.tv_sec = 0; 177 Interval.tv_nsec = 1000000; 178 #endif 179 // Don't wait more than five minutes for the file to appear. 180 unsigned MaxSeconds = 300; 181 bool LockFileGone = false; 182 do { 183 // Sleep for the designated interval, to allow the owning process time to 184 // finish up and remove the lock file. 185 // FIXME: Should we hook in to system APIs to get a notification when the 186 // lock file is deleted? 187 #if LLVM_ON_WIN32 188 Sleep(Interval); 189 #else 190 nanosleep(&Interval, NULL); 191 #endif 192 bool Exists = false; 193 bool LockFileJustDisappeared = false; 194 195 // If the lock file is still expected to be there, check whether it still 196 // is. 197 if (!LockFileGone) { 198 if (!sys::fs::exists(LockFileName.str(), Exists) && !Exists) { 199 LockFileGone = true; 200 LockFileJustDisappeared = true; 201 Exists = false; 202 } 203 } 204 205 // If the lock file is no longer there, check if the original file is 206 // available now. 207 if (LockFileGone) { 208 if (!sys::fs::exists(FileName.str(), Exists) && Exists) { 209 return; 210 } 211 212 // The lock file is gone, so now we're waiting for the original file to 213 // show up. If this just happened, reset our waiting intervals and keep 214 // waiting. 215 if (LockFileJustDisappeared) { 216 MaxSeconds = 5; 217 218 #if LLVM_ON_WIN32 219 Interval = 1; 220 #else 221 Interval.tv_sec = 0; 222 Interval.tv_nsec = 1000000; 223 #endif 224 continue; 225 } 226 } 227 228 // If we're looking for the lock file to disappear, but the process 229 // owning the lock died without cleaning up, just bail out. 230 if (!LockFileGone && 231 !processStillExecuting((*Owner).first, (*Owner).second)) { 232 return; 233 } 234 235 // Exponentially increase the time we wait for the lock to be removed. 236 #if LLVM_ON_WIN32 237 Interval *= 2; 238 #else 239 Interval.tv_sec *= 2; 240 Interval.tv_nsec *= 2; 241 if (Interval.tv_nsec >= 1000000000) { 242 ++Interval.tv_sec; 243 Interval.tv_nsec -= 1000000000; 244 } 245 #endif 246 } while ( 247 #if LLVM_ON_WIN32 248 Interval < MaxSeconds * 1000 249 #else 250 Interval.tv_sec < (time_t)MaxSeconds 251 #endif 252 ); 253 254 // Give up. 255 } 256