LLVM 17.0.0git
LockFileManager.cpp
Go to the documentation of this file.
1//===--- LockFileManager.cpp - File-level Locking Utility------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
12#include "llvm/Support/Errc.h"
19#include <cerrno>
20#include <chrono>
21#include <ctime>
22#include <memory>
23#include <random>
24#include <sys/stat.h>
25#include <sys/types.h>
26#include <system_error>
27#include <thread>
28#include <tuple>
29
30#ifdef _WIN32
31#include <windows.h>
32#endif
33#if LLVM_ON_UNIX
34#include <unistd.h>
35#endif
36
37#if defined(__APPLE__) && defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1050)
38#define USE_OSX_GETHOSTUUID 1
39#else
40#define USE_OSX_GETHOSTUUID 0
41#endif
42
43#if USE_OSX_GETHOSTUUID
44#include <uuid/uuid.h>
45#endif
46
47using namespace llvm;
48
49/// Attempt to read the lock file with the given name, if it exists.
50///
51/// \param LockFileName The name of the lock file to read.
52///
53/// \returns The process ID of the process that owns this lock file
54std::optional<std::pair<std::string, int>>
55LockFileManager::readLockFile(StringRef LockFileName) {
56 // Read the owning host and PID out of the lock file. If it appears that the
57 // owning process is dead, the lock file is invalid.
59 MemoryBuffer::getFile(LockFileName);
60 if (!MBOrErr) {
61 sys::fs::remove(LockFileName);
62 return std::nullopt;
63 }
64 MemoryBuffer &MB = *MBOrErr.get();
65
66 StringRef Hostname;
67 StringRef PIDStr;
68 std::tie(Hostname, PIDStr) = getToken(MB.getBuffer(), " ");
69 PIDStr = PIDStr.substr(PIDStr.find_first_not_of(" "));
70 int PID;
71 if (!PIDStr.getAsInteger(10, PID)) {
72 auto Owner = std::make_pair(std::string(Hostname), PID);
73 if (processStillExecuting(Owner.first, Owner.second))
74 return Owner;
75 }
76
77 // Delete the lock file. It's invalid anyway.
78 sys::fs::remove(LockFileName);
79 return std::nullopt;
80}
81
82static std::error_code getHostID(SmallVectorImpl<char> &HostID) {
83 HostID.clear();
84
85#if USE_OSX_GETHOSTUUID
86 // On OS X, use the more stable hardware UUID instead of hostname.
87 struct timespec wait = {1, 0}; // 1 second.
88 uuid_t uuid;
89 if (gethostuuid(uuid, &wait) != 0)
90 return std::error_code(errno, std::system_category());
91
92 uuid_string_t UUIDStr;
93 uuid_unparse(uuid, UUIDStr);
94 StringRef UUIDRef(UUIDStr);
95 HostID.append(UUIDRef.begin(), UUIDRef.end());
96
97#elif LLVM_ON_UNIX
98 char HostName[256];
99 HostName[255] = 0;
100 HostName[0] = 0;
101 gethostname(HostName, 255);
102 StringRef HostNameRef(HostName);
103 HostID.append(HostNameRef.begin(), HostNameRef.end());
104
105#else
106 StringRef Dummy("localhost");
107 HostID.append(Dummy.begin(), Dummy.end());
108#endif
109
110 return std::error_code();
111}
112
113bool LockFileManager::processStillExecuting(StringRef HostID, int PID) {
114#if LLVM_ON_UNIX && !defined(__ANDROID__)
115 SmallString<256> StoredHostID;
116 if (getHostID(StoredHostID))
117 return true; // Conservatively assume it's executing on error.
118
119 // Check whether the process is dead. If so, we're done.
120 if (StoredHostID == HostID && getsid(PID) == -1 && errno == ESRCH)
121 return false;
122#endif
123
124 return true;
125}
126
127namespace {
128
129/// An RAII helper object ensure that the unique lock file is removed.
130///
131/// Ensures that if there is an error or a signal before we finish acquiring the
132/// lock, the unique file will be removed. And if we successfully take the lock,
133/// the signal handler is left in place so that signals while the lock is held
134/// will remove the unique lock file. The caller should ensure there is a
135/// matching call to sys::DontRemoveFileOnSignal when the lock is released.
136class RemoveUniqueLockFileOnSignal {
138 bool RemoveImmediately;
139public:
140 RemoveUniqueLockFileOnSignal(StringRef Name)
141 : Filename(Name), RemoveImmediately(true) {
142 sys::RemoveFileOnSignal(Filename, nullptr);
143 }
144
145 ~RemoveUniqueLockFileOnSignal() {
146 if (!RemoveImmediately) {
147 // Leave the signal handler enabled. It will be removed when the lock is
148 // released.
149 return;
150 }
151 sys::fs::remove(Filename);
153 }
154
155 void lockAcquired() { RemoveImmediately = false; }
156};
157
158} // end anonymous namespace
159
160LockFileManager::LockFileManager(StringRef FileName)
161{
162 this->FileName = FileName;
163 if (std::error_code EC = sys::fs::make_absolute(this->FileName)) {
164 std::string S("failed to obtain absolute path for ");
165 S.append(std::string(this->FileName.str()));
166 setError(EC, S);
167 return;
168 }
169 LockFileName = this->FileName;
170 LockFileName += ".lock";
171
172 // If the lock file already exists, don't bother to try to create our own
173 // lock file; it won't work anyway. Just figure out who owns this lock file.
174 if ((Owner = readLockFile(LockFileName)))
175 return;
176
177 // Create a lock file that is unique to this instance.
178 UniqueLockFileName = LockFileName;
179 UniqueLockFileName += "-%%%%%%%%";
180 int UniqueLockFileID;
181 if (std::error_code EC = sys::fs::createUniqueFile(
182 UniqueLockFileName, UniqueLockFileID, UniqueLockFileName)) {
183 std::string S("failed to create unique file ");
184 S.append(std::string(UniqueLockFileName.str()));
185 setError(EC, S);
186 return;
187 }
188
189 // Write our process ID to our unique lock file.
190 {
191 SmallString<256> HostID;
192 if (auto EC = getHostID(HostID)) {
193 setError(EC, "failed to get host id");
194 return;
195 }
196
197 raw_fd_ostream Out(UniqueLockFileID, /*shouldClose=*/true);
198 Out << HostID << ' ' << sys::Process::getProcessId();
199 Out.close();
200
201 if (Out.has_error()) {
202 // We failed to write out PID, so report the error, remove the
203 // unique lock file, and fail.
204 std::string S("failed to write to ");
205 S.append(std::string(UniqueLockFileName.str()));
206 setError(Out.error(), S);
207 sys::fs::remove(UniqueLockFileName);
208 return;
209 }
210 }
211
212 // Clean up the unique file on signal, which also releases the lock if it is
213 // held since the .lock symlink will point to a nonexistent file.
214 RemoveUniqueLockFileOnSignal RemoveUniqueFile(UniqueLockFileName);
215
216 while (true) {
217 // Create a link from the lock file name. If this succeeds, we're done.
218 std::error_code EC =
219 sys::fs::create_link(UniqueLockFileName, LockFileName);
220 if (!EC) {
221 RemoveUniqueFile.lockAcquired();
222 return;
223 }
224
225 if (EC != errc::file_exists) {
226 std::string S("failed to create link ");
227 raw_string_ostream OSS(S);
228 OSS << LockFileName.str() << " to " << UniqueLockFileName.str();
229 setError(EC, OSS.str());
230 return;
231 }
232
233 // Someone else managed to create the lock file first. Read the process ID
234 // from the lock file.
235 if ((Owner = readLockFile(LockFileName))) {
236 // Wipe out our unique lock file (it's useless now)
237 sys::fs::remove(UniqueLockFileName);
238 return;
239 }
240
241 if (!sys::fs::exists(LockFileName)) {
242 // The previous owner released the lock file before we could read it.
243 // Try to get ownership again.
244 continue;
245 }
246
247 // There is a lock file that nobody owns; try to clean it up and get
248 // ownership.
249 if ((EC = sys::fs::remove(LockFileName))) {
250 std::string S("failed to remove lockfile ");
251 S.append(std::string(UniqueLockFileName.str()));
252 setError(EC, S);
253 return;
254 }
255 }
256}
257
259 if (Owner)
260 return LFS_Shared;
261
262 if (ErrorCode)
263 return LFS_Error;
264
265 return LFS_Owned;
266}
267
269 if (ErrorCode) {
270 std::string Str(ErrorDiagMsg);
271 std::string ErrCodeMsg = ErrorCode.message();
272 raw_string_ostream OSS(Str);
273 if (!ErrCodeMsg.empty())
274 OSS << ": " << ErrCodeMsg;
275 return OSS.str();
276 }
277 return "";
278}
279
281 if (getState() != LFS_Owned)
282 return;
283
284 // Since we own the lock, remove the lock file and our own unique lock file.
285 sys::fs::remove(LockFileName);
286 sys::fs::remove(UniqueLockFileName);
287 // The unique file is now gone, so remove it from the signal handler. This
288 // matches a sys::RemoveFileOnSignal() in LockFileManager().
289 sys::DontRemoveFileOnSignal(UniqueLockFileName);
290}
291
293LockFileManager::waitForUnlock(const unsigned MaxSeconds) {
294 if (getState() != LFS_Shared)
295 return Res_Success;
296
297 // Since we don't yet have an event-based method to wait for the lock file,
298 // implement randomized exponential backoff, similar to Ethernet collision
299 // algorithm. This improves performance on machines with high core counts
300 // when the file lock is heavily contended by multiple clang processes
301 const unsigned long MinWaitDurationMS = 10;
302 const unsigned long MaxWaitMultiplier = 50; // 500ms max wait
303 unsigned long WaitMultiplier = 1;
304 unsigned long ElapsedTimeSeconds = 0;
305
306 std::random_device Device;
307 std::default_random_engine Engine(Device());
308
309 auto StartTime = std::chrono::steady_clock::now();
310
311 do {
312 // FIXME: implement event-based waiting
313
314 // Sleep for the designated interval, to allow the owning process time to
315 // finish up and remove the lock file.
316 std::uniform_int_distribution<unsigned long> Distribution(1,
317 WaitMultiplier);
318 unsigned long WaitDurationMS = MinWaitDurationMS * Distribution(Engine);
319 std::this_thread::sleep_for(std::chrono::milliseconds(WaitDurationMS));
320
321 if (sys::fs::access(LockFileName.c_str(), sys::fs::AccessMode::Exist) ==
323 // If the original file wasn't created, somone thought the lock was dead.
324 if (!sys::fs::exists(FileName))
325 return Res_OwnerDied;
326 return Res_Success;
327 }
328
329 // If the process owning the lock died without cleaning up, just bail out.
330 if (!processStillExecuting((*Owner).first, (*Owner).second))
331 return Res_OwnerDied;
332
333 WaitMultiplier *= 2;
334 if (WaitMultiplier > MaxWaitMultiplier) {
335 WaitMultiplier = MaxWaitMultiplier;
336 }
337
338 ElapsedTimeSeconds = std::chrono::duration_cast<std::chrono::seconds>(
339 std::chrono::steady_clock::now() - StartTime)
340 .count();
341
342 } while (ElapsedTimeSeconds < MaxSeconds);
343
344 // Give up.
345 return Res_Timeout;
346}
347
349 return sys::fs::remove(LockFileName);
350}
basic Basic Alias true
std::string Name
Provides ErrorOr<T> smart pointer.
static std::error_code getHostID(SmallVectorImpl< char > &HostID)
Provides a library for accessing information about this process and other processes on the operating ...
This file defines the SmallVector class.
This file contains some functions that are useful when dealing with strings.
Represents either an error or a value T.
Definition: ErrorOr.h:56
reference get()
Definition: ErrorOr.h:149
LockFileState
Describes the state of a lock file.
@ LFS_Error
An error occurred while trying to create or find the lock file.
@ LFS_Owned
The lock file has been created and is owned by this instance of the object.
@ LFS_Shared
The lock file already exists and is owned by some other instance.
std::string getErrorMessage() const
Get error message, or "" if there is no error.
void setError(const std::error_code &EC, StringRef ErrorMsg="")
Set error and error message.
std::error_code unsafeRemoveLockFile()
Remove the lock file.
WaitForUnlockResult waitForUnlock(const unsigned MaxSeconds=90)
For a shared lock, wait until the owner releases the lock.
WaitForUnlockResult
Describes the result of waiting for the owner to release the lock.
@ Res_Success
The lock was released successfully.
@ Res_Timeout
Reached timeout while waiting for the owner to release the lock.
@ Res_OwnerDied
Owner died while holding the lock.
LockFileState getState() const
Determine the state of the lock file.
This interface provides simple read-only access to a block of memory, and provides simple methods for...
Definition: MemoryBuffer.h:51
StringRef getBuffer() const
Definition: MemoryBuffer.h:70
static ErrorOr< std::unique_ptr< MemoryBuffer > > getFile(const Twine &Filename, bool IsText=false, bool RequiresNullTerminator=true, bool IsVolatile=false, std::optional< Align > Alignment=std::nullopt)
Open the specified file as a MemoryBuffer, returning a new MemoryBuffer if successful,...
SmallString - A SmallString is just a SmallVector with methods and accessors that make it work better...
Definition: SmallString.h:26
const char * c_str()
Definition: SmallString.h:264
StringRef str() const
Explicit conversion to StringRef.
Definition: SmallString.h:261
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
Definition: SmallVector.h:577
void append(ItTy in_start, ItTy in_end)
Add the specified range to the end of the SmallVector.
Definition: SmallVector.h:687
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:50
bool getAsInteger(unsigned Radix, T &Result) const
Parse the current string as an integer of the specified radix.
Definition: StringRef.h:468
std::string str() const
str - Get the contents as an std::string.
Definition: StringRef.h:222
constexpr StringRef substr(size_t Start, size_t N=npos) const
Return a reference to the substring from [Start, Start + N).
Definition: StringRef.h:569
iterator begin() const
Definition: StringRef.h:111
iterator end() const
Definition: StringRef.h:113
size_t find_first_not_of(char C, size_t From=0) const
Find the first character in the string that is not C or npos if not found.
Definition: StringRef.cpp:251
A raw_ostream that writes to a file descriptor.
Definition: raw_ostream.h:454
bool has_error() const
Return the value of the flag in this raw_fd_ostream indicating whether an output error has been encou...
Definition: raw_ostream.h:545
std::error_code error() const
Definition: raw_ostream.h:539
void close()
Manually flush the stream and close the file.
A raw_ostream that writes to an std::string.
Definition: raw_ostream.h:642
std::string & str()
Returns the string's reference.
Definition: raw_ostream.h:660
static Pid getProcessId()
Get the process's identifier.
std::error_code create_link(const Twine &to, const Twine &from)
Create a link from from to to.
void make_absolute(const Twine &current_directory, SmallVectorImpl< char > &path)
Make path an absolute path.
Definition: Path.cpp:907
std::error_code access(const Twine &Path, AccessMode Mode)
Can the file be accessed?
bool exists(const basic_file_status &status)
Does file exist?
Definition: Path.cpp:1078
std::error_code createUniqueFile(const Twine &Model, int &ResultFD, SmallVectorImpl< char > &ResultPath, OpenFlags Flags=OF_None, unsigned Mode=all_read|all_write)
Create a uniquely named file.
Definition: Path.cpp:823
std::error_code remove(const Twine &path, bool IgnoreNonExisting=true)
Remove path.
void DontRemoveFileOnSignal(StringRef Filename)
This function removes a file from the list of files to be removed on signal delivery.
bool RemoveFileOnSignal(StringRef Filename, std::string *ErrMsg=nullptr)
This function registers signal handlers to ensure that if a signal gets delivered that the named file...
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
@ no_such_file_or_directory