LLVM 19.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"
20#include <cerrno>
21#include <chrono>
22#include <ctime>
23#include <memory>
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 errnoAsErrorCode();
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));
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));
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));
206 setError(Out.error(), S);
207 sys::fs::remove(UniqueLockFileName);
208 // Don't call report_fatal_error.
209 Out.clear_error();
210 return;
211 }
212 }
213
214 // Clean up the unique file on signal, which also releases the lock if it is
215 // held since the .lock symlink will point to a nonexistent file.
216 RemoveUniqueLockFileOnSignal RemoveUniqueFile(UniqueLockFileName);
217
218 while (true) {
219 // Create a link from the lock file name. If this succeeds, we're done.
220 std::error_code EC =
221 sys::fs::create_link(UniqueLockFileName, LockFileName);
222 if (!EC) {
223 RemoveUniqueFile.lockAcquired();
224 return;
225 }
226
227 if (EC != errc::file_exists) {
228 std::string S("failed to create link ");
229 raw_string_ostream OSS(S);
230 OSS << LockFileName.str() << " to " << UniqueLockFileName.str();
231 setError(EC, OSS.str());
232 return;
233 }
234
235 // Someone else managed to create the lock file first. Read the process ID
236 // from the lock file.
237 if ((Owner = readLockFile(LockFileName))) {
238 // Wipe out our unique lock file (it's useless now)
239 sys::fs::remove(UniqueLockFileName);
240 return;
241 }
242
243 if (!sys::fs::exists(LockFileName)) {
244 // The previous owner released the lock file before we could read it.
245 // Try to get ownership again.
246 continue;
247 }
248
249 // There is a lock file that nobody owns; try to clean it up and get
250 // ownership.
251 if ((EC = sys::fs::remove(LockFileName))) {
252 std::string S("failed to remove lockfile ");
253 S.append(std::string(UniqueLockFileName));
254 setError(EC, S);
255 return;
256 }
257 }
258}
259
261 if (Owner)
262 return LFS_Shared;
263
264 if (ErrorCode)
265 return LFS_Error;
266
267 return LFS_Owned;
268}
269
271 if (ErrorCode) {
272 std::string Str(ErrorDiagMsg);
273 std::string ErrCodeMsg = ErrorCode.message();
274 raw_string_ostream OSS(Str);
275 if (!ErrCodeMsg.empty())
276 OSS << ": " << ErrCodeMsg;
277 return OSS.str();
278 }
279 return "";
280}
281
283 if (getState() != LFS_Owned)
284 return;
285
286 // Since we own the lock, remove the lock file and our own unique lock file.
287 sys::fs::remove(LockFileName);
288 sys::fs::remove(UniqueLockFileName);
289 // The unique file is now gone, so remove it from the signal handler. This
290 // matches a sys::RemoveFileOnSignal() in LockFileManager().
291 sys::DontRemoveFileOnSignal(UniqueLockFileName);
292}
293
295LockFileManager::waitForUnlock(const unsigned MaxSeconds) {
296 if (getState() != LFS_Shared)
297 return Res_Success;
298
299 // Since we don't yet have an event-based method to wait for the lock file,
300 // use randomized exponential backoff, similar to Ethernet collision
301 // algorithm. This improves performance on machines with high core counts
302 // when the file lock is heavily contended by multiple clang processes
303 using namespace std::chrono_literals;
304 ExponentialBackoff Backoff(std::chrono::seconds(MaxSeconds), 10ms, 500ms);
305
306 // Wait first as this is only called when the lock is known to be held.
307 while (Backoff.waitForNextAttempt()) {
308 // FIXME: implement event-based waiting
309 if (sys::fs::access(LockFileName.c_str(), sys::fs::AccessMode::Exist) ==
311 // If the original file wasn't created, somone thought the lock was dead.
312 if (!sys::fs::exists(FileName))
313 return Res_OwnerDied;
314 return Res_Success;
315 }
316
317 // If the process owning the lock died without cleaning up, just bail out.
318 if (!processStillExecuting((*Owner).first, (*Owner).second))
319 return Res_OwnerDied;
320 }
321
322 // Give up.
323 return Res_Timeout;
324}
325
327 return sys::fs::remove(LockFileName);
328}
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
A class to help implement exponential backoff.
bool waitForNextAttempt()
Blocks while waiting for the next attempt.
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:259
StringRef str() const
Explicit conversion to StringRef.
Definition: SmallString.h:254
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
Definition: SmallVector.h:586
void append(ItTy in_start, ItTy in_end)
Add the specified range to the end of the SmallVector.
Definition: SmallVector.h:696
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:463
constexpr StringRef substr(size_t Start, size_t N=npos) const
Return a reference to the substring from [Start, Start + N).
Definition: StringRef.h:564
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:471
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:562
std::error_code error() const
Definition: raw_ostream.h:556
void close()
Manually flush the stream and close the file.
void clear_error()
Set the flag read by has_error() to false.
Definition: raw_ostream.h:573
A raw_ostream that writes to an std::string.
Definition: raw_ostream.h:661
std::string & str()
Returns the string's reference.
Definition: raw_ostream.h:679
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
std::error_code errnoAsErrorCode()
Helper to get errno as an std::error_code.
Definition: Error.h:1193