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