LLVM 22.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/Config/llvm-config.h" // for LLVM_ON_UNIX
13#include "llvm/Support/Errc.h"
21#include <cerrno>
22#include <chrono>
23#include <ctime>
24#include <memory>
25#include <system_error>
26#include <tuple>
27
28#ifdef _WIN32
29#include <windows.h>
30#endif
31#if LLVM_ON_UNIX
32#include <unistd.h>
33#endif
34
35#if defined(__APPLE__) && defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1050)
36#define USE_OSX_GETHOSTUUID 1
37#else
38#define USE_OSX_GETHOSTUUID 0
39#endif
40
41#if USE_OSX_GETHOSTUUID
42#include <uuid/uuid.h>
43#endif
44
45using namespace llvm;
46
47/// Attempt to read the lock file with the given name, if it exists.
48///
49/// \param LockFileName The name of the lock file to read.
50///
51/// \returns The process ID of the process that owns this lock file
52std::optional<LockFileManager::OwnedByAnother>
53LockFileManager::readLockFile(StringRef LockFileName) {
54 // Read the owning host and PID out of the lock file. If it appears that the
55 // owning process is dead, the lock file is invalid.
57 MemoryBuffer::getFile(LockFileName);
58 if (!MBOrErr) {
59 sys::fs::remove(LockFileName);
60 return std::nullopt;
61 }
62 MemoryBuffer &MB = *MBOrErr.get();
63
64 StringRef Hostname;
65 StringRef PIDStr;
66 std::tie(Hostname, PIDStr) = getToken(MB.getBuffer(), " ");
67 PIDStr = PIDStr.substr(PIDStr.find_first_not_of(' '));
68 int PID;
69 if (!PIDStr.getAsInteger(10, PID)) {
70 OwnedByAnother Owner;
71 Owner.OwnerHostName = Hostname;
72 Owner.OwnerPID = PID;
73 if (processStillExecuting(Owner.OwnerHostName, Owner.OwnerPID))
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 : FileName(FileName), Owner(OwnerUnknown{}) {}
162
164 assert(std::holds_alternative<OwnerUnknown>(Owner) &&
165 "lock has already been attempted");
166
167 SmallString<128> AbsoluteFileName(FileName);
168 if (std::error_code EC = sys::fs::make_absolute(AbsoluteFileName))
169 return createStringError(EC, "failed to obtain absolute path for " +
170 AbsoluteFileName);
171 LockFileName = AbsoluteFileName;
172 LockFileName += ".lock";
173
174 // If the lock file already exists, don't bother to try to create our own
175 // lock file; it won't work anyway. Just figure out who owns this lock file.
176 if (auto LockFileOwner = readLockFile(LockFileName)) {
177 Owner = std::move(*LockFileOwner);
178 return false;
179 }
180
181 // Create a lock file that is unique to this instance.
182 UniqueLockFileName = LockFileName;
183 UniqueLockFileName += "-%%%%%%%%";
184 int UniqueLockFileID;
185 if (std::error_code EC = sys::fs::createUniqueFile(
186 UniqueLockFileName, UniqueLockFileID, UniqueLockFileName))
187 return createStringError(EC, "failed to create unique file " +
188 UniqueLockFileName);
189
190 // Clean up the unique file on signal or scope exit.
191 RemoveUniqueLockFileOnSignal RemoveUniqueFile(UniqueLockFileName);
192
193 // Write our process ID to our unique lock file.
194 {
195 SmallString<256> HostID;
196 if (auto EC = getHostID(HostID))
197 return createStringError(EC, "failed to get host id");
198
199 raw_fd_ostream Out(UniqueLockFileID, /*shouldClose=*/true);
200 Out << HostID << ' ' << sys::Process::getProcessId();
201 Out.close();
202
203 if (Out.has_error()) {
204 // We failed to write out PID, so report the error and fail.
205 Error Err = createStringError(Out.error(),
206 "failed to write to " + UniqueLockFileName);
207 // Don't call report_fatal_error.
208 Out.clear_error();
209 return std::move(Err);
210 }
211 }
212
213 while (true) {
214 // Create a link from the lock file name. If this succeeds, we're done.
215 std::error_code EC =
216 sys::fs::create_link(UniqueLockFileName, LockFileName);
217 if (!EC) {
218 RemoveUniqueFile.lockAcquired();
219 Owner = OwnedByUs{};
220 return true;
221 }
222
223 if (EC != errc::file_exists)
224 return createStringError(EC, "failed to create link " + LockFileName +
225 " to " + UniqueLockFileName);
226
227 // Someone else managed to create the lock file first. Read the process ID
228 // from the lock file.
229 if (auto LockFileOwner = readLockFile(LockFileName)) {
230 Owner = std::move(*LockFileOwner);
231 return false;
232 }
233
234 if (!sys::fs::exists(LockFileName)) {
235 // The previous owner released the lock file before we could read it.
236 // Try to get ownership again.
237 continue;
238 }
239
240 // There is a lock file that nobody owns; try to clean it up and get
241 // ownership.
242 if ((EC = sys::fs::remove(LockFileName)))
243 return createStringError(EC, "failed to remove lockfile " +
244 UniqueLockFileName);
245 }
246}
247
249 if (!std::holds_alternative<OwnedByUs>(Owner))
250 return;
251
252 // Since we own the lock, remove the lock file and our own unique lock file.
253 sys::fs::remove(LockFileName);
254 sys::fs::remove(UniqueLockFileName);
255 // The unique file is now gone, so remove it from the signal handler. This
256 // matches a sys::RemoveFileOnSignal() in LockFileManager().
257 sys::DontRemoveFileOnSignal(UniqueLockFileName);
258}
259
261LockFileManager::waitForUnlockFor(std::chrono::seconds MaxSeconds) {
262 auto *LockFileOwner = std::get_if<OwnedByAnother>(&Owner);
263 assert(LockFileOwner &&
264 "waiting for lock to be unlocked without knowing the owner");
265
266 // Since we don't yet have an event-based method to wait for the lock file,
267 // use randomized exponential backoff, similar to Ethernet collision
268 // algorithm. This improves performance on machines with high core counts
269 // when the file lock is heavily contended by multiple clang processes
270 using namespace std::chrono_literals;
271 ExponentialBackoff Backoff(MaxSeconds, 10ms, 500ms);
272
273 // Wait first as this is only called when the lock is known to be held.
274 while (Backoff.waitForNextAttempt()) {
275 // FIXME: implement event-based waiting
276 if (sys::fs::access(LockFileName.c_str(), sys::fs::AccessMode::Exist) ==
279
280 // If the process owning the lock died without cleaning up, just bail out.
281 if (!processStillExecuting(LockFileOwner->OwnerHostName,
282 LockFileOwner->OwnerPID))
284 }
285
286 // Give up.
288}
289
291 return sys::fs::remove(LockFileName);
292}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
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
Lightweight error class with error context and mandatory checking.
Definition: Error.h:159
Tagged union holding either a T or a Error.
Definition: Error.h:485
A class to help implement exponential backoff.
LLVM_ABI bool waitForNextAttempt()
Blocks while waiting for the next attempt.
std::error_code unsafeMaybeUnlock() override
Remove the lock file.
WaitForUnlockResult waitForUnlockFor(std::chrono::seconds MaxSeconds) override
For a shared lock, wait until the owner releases the lock.
Expected< bool > tryLock() override
Tries to acquire the lock without blocking.
~LockFileManager() override
Unlocks the lock if previously acquired by tryLock().
This interface provides simple read-only access to a block of memory, and provides simple methods for...
Definition: MemoryBuffer.h:52
StringRef getBuffer() const
Definition: MemoryBuffer.h:71
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
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
Definition: SmallVector.h:574
void append(ItTy in_start, ItTy in_end)
Add the specified range to the end of the SmallVector.
Definition: SmallVector.h:684
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:55
bool getAsInteger(unsigned Radix, T &Result) const
Parse the current string as an integer of the specified radix.
Definition: StringRef.h:480
constexpr StringRef substr(size_t Start, size_t N=npos) const
Return a reference to the substring from [Start, Start + N).
Definition: StringRef.h:581
iterator begin() const
Definition: StringRef.h:120
iterator end() const
Definition: StringRef.h:122
LLVM_ABI 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:252
A raw_ostream that writes to a file descriptor.
Definition: raw_ostream.h:461
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:563
std::error_code error() const
Definition: raw_ostream.h:557
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:574
static LLVM_ABI Pid getProcessId()
Get the process's identifier.
LLVM_ABI void make_absolute(const Twine &current_directory, SmallVectorImpl< char > &path)
Make path an absolute path.
Definition: Path.cpp:906
LLVM_ABI std::error_code access(const Twine &Path, AccessMode Mode)
Can the file be accessed?
LLVM_ABI bool exists(const basic_file_status &status)
Does file exist?
Definition: Path.cpp:1077
LLVM_ABI std::error_code create_link(const Twine &to, const Twine &from)
Create a link from from to to.
LLVM_ABI 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:822
LLVM_ABI std::error_code remove(const Twine &path, bool IgnoreNonExisting=true)
Remove path.
LLVM_ABI void DontRemoveFileOnSignal(StringRef Filename)
This function removes a file from the list of files to be removed on signal delivery.
LLVM_ABI 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
Error createStringError(std::error_code EC, char const *Fmt, const Ts &... Vals)
Create formatted StringError object.
Definition: Error.h:1305
@ no_such_file_or_directory
WaitForUnlockResult
Describes the result of waiting for the owner to release the lock.
Definition: AdvisoryLock.h:18
@ Success
The lock was released successfully.
@ OwnerDied
Owner died while holding the lock.
@ Timeout
Reached timeout while waiting for the owner to release the lock.
std::error_code errnoAsErrorCode()
Helper to get errno as an std::error_code.
Definition: Error.h:1240