Douglas Gregor | 95fa400 | 2012-01-29 20:15:10 +0000 | [diff] [blame] | 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/Support/FileSystem.h" |
| 11 | #include "llvm/Support/raw_ostream.h" |
| 12 | #include <fstream> |
Douglas Gregor | 95fa400 | 2012-01-29 20:15:10 +0000 | [diff] [blame] | 13 | #include <sys/stat.h> |
Chandler Carruth | d04a8d4 | 2012-12-03 16:50:05 +0000 | [diff] [blame^] | 14 | #include <sys/types.h> |
Douglas Gregor | 95fa400 | 2012-01-29 20:15:10 +0000 | [diff] [blame] | 15 | #if LLVM_ON_WIN32 |
| 16 | #include <windows.h> |
| 17 | #endif |
| 18 | #if LLVM_ON_UNIX |
| 19 | #include <unistd.h> |
| 20 | #endif |
| 21 | using namespace llvm; |
| 22 | |
| 23 | /// \brief Attempt to read the lock file with the given name, if it exists. |
| 24 | /// |
| 25 | /// \param LockFileName The name of the lock file to read. |
| 26 | /// |
| 27 | /// \returns The process ID of the process that owns this lock file |
| 28 | Optional<std::pair<std::string, int> > |
| 29 | LockFileManager::readLockFile(StringRef LockFileName) { |
| 30 | // Check whether the lock file exists. If not, clearly there's nothing |
| 31 | // to read, so we just return. |
| 32 | bool Exists = false; |
| 33 | if (sys::fs::exists(LockFileName, Exists) || !Exists) |
| 34 | return Optional<std::pair<std::string, int> >(); |
| 35 | |
| 36 | // Read the owning host and PID out of the lock file. If it appears that the |
| 37 | // owning process is dead, the lock file is invalid. |
| 38 | int PID = 0; |
| 39 | std::string Hostname; |
| 40 | std::ifstream Input(LockFileName.str().c_str()); |
| 41 | if (Input >> Hostname >> PID && PID > 0 && |
| 42 | processStillExecuting(Hostname, PID)) |
| 43 | return std::make_pair(Hostname, PID); |
| 44 | |
| 45 | // Delete the lock file. It's invalid anyway. |
| 46 | bool Existed; |
| 47 | sys::fs::remove(LockFileName, Existed); |
| 48 | return Optional<std::pair<std::string, int> >(); |
| 49 | } |
| 50 | |
| 51 | bool LockFileManager::processStillExecuting(StringRef Hostname, int PID) { |
Evgeniy Stepanov | 68d92bd | 2012-09-04 09:14:45 +0000 | [diff] [blame] | 52 | #if LLVM_ON_UNIX && !defined(__ANDROID__) |
Douglas Gregor | 95fa400 | 2012-01-29 20:15:10 +0000 | [diff] [blame] | 53 | char MyHostname[256]; |
| 54 | MyHostname[255] = 0; |
| 55 | MyHostname[0] = 0; |
| 56 | gethostname(MyHostname, 255); |
| 57 | // Check whether the process is dead. If so, we're done. |
| 58 | if (MyHostname == Hostname && getsid(PID) == -1 && errno == ESRCH) |
| 59 | return false; |
| 60 | #endif |
| 61 | |
| 62 | return true; |
| 63 | } |
| 64 | |
| 65 | LockFileManager::LockFileManager(StringRef FileName) |
| 66 | { |
| 67 | LockFileName = FileName; |
| 68 | LockFileName += ".lock"; |
| 69 | |
| 70 | // If the lock file already exists, don't bother to try to create our own |
| 71 | // lock file; it won't work anyway. Just figure out who owns this lock file. |
| 72 | if ((Owner = readLockFile(LockFileName))) |
| 73 | return; |
| 74 | |
| 75 | // Create a lock file that is unique to this instance. |
| 76 | UniqueLockFileName = LockFileName; |
| 77 | UniqueLockFileName += "-%%%%%%%%"; |
| 78 | int UniqueLockFileID; |
| 79 | if (error_code EC |
| 80 | = sys::fs::unique_file(UniqueLockFileName.str(), |
| 81 | UniqueLockFileID, |
| 82 | UniqueLockFileName, |
| 83 | /*makeAbsolute=*/false)) { |
| 84 | Error = EC; |
| 85 | return; |
| 86 | } |
| 87 | |
| 88 | // Write our process ID to our unique lock file. |
| 89 | { |
| 90 | raw_fd_ostream Out(UniqueLockFileID, /*shouldClose=*/true); |
| 91 | |
| 92 | #if LLVM_ON_UNIX |
| 93 | // FIXME: move getpid() call into LLVM |
| 94 | char hostname[256]; |
| 95 | hostname[255] = 0; |
| 96 | hostname[0] = 0; |
| 97 | gethostname(hostname, 255); |
| 98 | Out << hostname << ' ' << getpid(); |
| 99 | #else |
| 100 | Out << "localhost 1"; |
| 101 | #endif |
| 102 | Out.close(); |
| 103 | |
| 104 | if (Out.has_error()) { |
| 105 | // We failed to write out PID, so make up an excuse, remove the |
| 106 | // unique lock file, and fail. |
| 107 | Error = make_error_code(errc::no_space_on_device); |
| 108 | bool Existed; |
| 109 | sys::fs::remove(UniqueLockFileName.c_str(), Existed); |
| 110 | return; |
| 111 | } |
| 112 | } |
| 113 | |
| 114 | // Create a hard link from the lock file name. If this succeeds, we're done. |
| 115 | error_code EC |
| 116 | = sys::fs::create_hard_link(UniqueLockFileName.str(), |
| 117 | LockFileName.str()); |
| 118 | if (EC == errc::success) |
| 119 | return; |
| 120 | |
| 121 | // Creating the hard link failed. |
| 122 | |
| 123 | #ifdef LLVM_ON_UNIX |
| 124 | // The creation of the hard link may appear to fail, but if stat'ing the |
| 125 | // unique file returns a link count of 2, then we can still declare success. |
| 126 | struct stat StatBuf; |
| 127 | if (stat(UniqueLockFileName.c_str(), &StatBuf) == 0 && |
| 128 | StatBuf.st_nlink == 2) |
| 129 | return; |
| 130 | #endif |
| 131 | |
| 132 | // Someone else managed to create the lock file first. Wipe out our unique |
| 133 | // lock file (it's useless now) and read the process ID from the lock file. |
| 134 | bool Existed; |
| 135 | sys::fs::remove(UniqueLockFileName.str(), Existed); |
| 136 | if ((Owner = readLockFile(LockFileName))) |
| 137 | return; |
| 138 | |
| 139 | // There is a lock file that nobody owns; try to clean it up and report |
| 140 | // an error. |
| 141 | sys::fs::remove(LockFileName.str(), Existed); |
| 142 | Error = EC; |
| 143 | } |
| 144 | |
| 145 | LockFileManager::LockFileState LockFileManager::getState() const { |
| 146 | if (Owner) |
| 147 | return LFS_Shared; |
| 148 | |
| 149 | if (Error) |
| 150 | return LFS_Error; |
| 151 | |
| 152 | return LFS_Owned; |
| 153 | } |
| 154 | |
| 155 | LockFileManager::~LockFileManager() { |
| 156 | if (getState() != LFS_Owned) |
| 157 | return; |
| 158 | |
| 159 | // Since we own the lock, remove the lock file and our own unique lock file. |
| 160 | bool Existed; |
| 161 | sys::fs::remove(LockFileName.str(), Existed); |
| 162 | sys::fs::remove(UniqueLockFileName.str(), Existed); |
| 163 | } |
| 164 | |
| 165 | void LockFileManager::waitForUnlock() { |
| 166 | if (getState() != LFS_Shared) |
| 167 | return; |
| 168 | |
| 169 | #if LLVM_ON_WIN32 |
| 170 | unsigned long Interval = 1; |
| 171 | #else |
| 172 | struct timespec Interval; |
| 173 | Interval.tv_sec = 0; |
| 174 | Interval.tv_nsec = 1000000; |
| 175 | #endif |
| 176 | // Don't wait more than an hour for the file to appear. |
| 177 | const unsigned MaxSeconds = 3600; |
| 178 | do { |
| 179 | // Sleep for the designated interval, to allow the owning process time to |
| 180 | // finish up and remove the lock file. |
| 181 | // FIXME: Should we hook in to system APIs to get a notification when the |
| 182 | // lock file is deleted? |
| 183 | #if LLVM_ON_WIN32 |
| 184 | Sleep(Interval); |
| 185 | #else |
| 186 | nanosleep(&Interval, NULL); |
| 187 | #endif |
| 188 | // If the file no longer exists, we're done. |
| 189 | bool Exists = false; |
| 190 | if (!sys::fs::exists(LockFileName.str(), Exists) && !Exists) |
| 191 | return; |
| 192 | |
| 193 | if (!processStillExecuting((*Owner).first, (*Owner).second)) |
| 194 | return; |
| 195 | |
| 196 | // Exponentially increase the time we wait for the lock to be removed. |
| 197 | #if LLVM_ON_WIN32 |
| 198 | Interval *= 2; |
| 199 | #else |
| 200 | Interval.tv_sec *= 2; |
| 201 | Interval.tv_nsec *= 2; |
| 202 | if (Interval.tv_nsec >= 1000000000) { |
| 203 | ++Interval.tv_sec; |
| 204 | Interval.tv_nsec -= 1000000000; |
| 205 | } |
| 206 | #endif |
| 207 | } while ( |
| 208 | #if LLVM_ON_WIN32 |
| 209 | Interval < MaxSeconds * 1000 |
| 210 | #else |
| 211 | Interval.tv_sec < (time_t)MaxSeconds |
| 212 | #endif |
| 213 | ); |
| 214 | |
| 215 | // Give up. |
| 216 | } |