LLVM 23.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"
19#include "llvm/Support/Path.h"
23#include <cerrno>
24#include <chrono>
25#include <ctime>
26#include <memory>
27#include <system_error>
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<LockFileManager::OwnedByAnother>
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 OwnedByAnother Owner;
73 Owner.OwnerHostName = Hostname;
74 Owner.OwnerPID = PID;
75 if (processStillExecuting(Owner.OwnerHostName, Owner.OwnerPID))
76 return Owner;
77 }
78
79 // Delete the lock file. It's invalid anyway.
80 sys::fs::remove(LockFileName);
81 return std::nullopt;
82}
83
84static std::error_code getHostID(SmallVectorImpl<char> &HostID) {
85 HostID.clear();
86
87#if USE_OSX_GETHOSTUUID
88 // On OS X, use the more stable hardware UUID instead of hostname.
89 struct timespec wait = {1, 0}; // 1 second.
90 uuid_t uuid;
91 if (gethostuuid(uuid, &wait) != 0)
92 return errnoAsErrorCode();
93
94 uuid_string_t UUIDStr;
95 uuid_unparse(uuid, UUIDStr);
96 StringRef UUIDRef(UUIDStr);
97 HostID.append(UUIDRef.begin(), UUIDRef.end());
98
99#elif LLVM_ON_UNIX
100 char HostName[256];
101 HostName[255] = 0;
102 HostName[0] = 0;
103 gethostname(HostName, 255);
104 StringRef HostNameRef(HostName);
105 HostID.append(HostNameRef.begin(), HostNameRef.end());
106
107#else
108 StringRef Dummy("localhost");
109 HostID.append(Dummy.begin(), Dummy.end());
110#endif
111
112 return std::error_code();
113}
114
115bool LockFileManager::processStillExecuting(StringRef HostID, int PID) {
116#if LLVM_ON_UNIX && !defined(__ANDROID__)
117 SmallString<256> StoredHostID;
118 if (getHostID(StoredHostID))
119 return true; // Conservatively assume it's executing on error.
120
121 // Check whether the process is dead. If so, we're done.
122 if (StoredHostID == HostID && getsid(PID) == -1 && errno == ESRCH)
123 return false;
124#endif
125
126 return true;
127}
128
129namespace {
130
131/// An RAII helper object ensure that the unique lock file is removed.
132///
133/// Ensures that if there is an error or a signal before we finish acquiring the
134/// lock, the unique file will be removed. And if we successfully take the lock,
135/// the signal handler is left in place so that signals while the lock is held
136/// will remove the unique lock file. The caller should ensure there is a
137/// matching call to sys::DontRemoveFileOnSignal when the lock is released.
138class RemoveUniqueLockFileOnSignal {
139 StringRef Filename;
140 bool RemoveImmediately;
141public:
142 RemoveUniqueLockFileOnSignal(StringRef Name)
143 : Filename(Name), RemoveImmediately(true) {
145 }
146
147 ~RemoveUniqueLockFileOnSignal() {
148 if (!RemoveImmediately) {
149 // Leave the signal handler enabled. It will be removed when the lock is
150 // released.
151 return;
152 }
155 }
156
157 void lockAcquired() { RemoveImmediately = false; }
158};
159
160} // end anonymous namespace
161
162LockFileManager::LockFileManager(StringRef FileName)
163 : FileName(FileName), Owner(OwnerUnknown{}) {}
164
166 auto BypassSandbox = sys::sandbox::scopedDisable();
167
168 assert(std::holds_alternative<OwnerUnknown>(Owner) &&
169 "lock has already been attempted");
170
171 SmallString<128> AbsoluteFileName(FileName);
172 if (std::error_code EC = sys::fs::make_absolute(AbsoluteFileName))
173 return createStringError(EC, "failed to obtain absolute path for " +
174 AbsoluteFileName);
175 LockFileName = AbsoluteFileName;
176 LockFileName += ".lock";
177
178 // If the lock file already exists, don't bother to try to create our own
179 // lock file; it won't work anyway. Just figure out who owns this lock file.
180 if (auto LockFileOwner = readLockFile(LockFileName)) {
181 Owner = std::move(*LockFileOwner);
182 return false;
183 }
184
185 // Create a lock file that is unique to this instance.
186 int UniqueLockFileID;
187 {
188 SmallString<128> UniqueLockFilePattern = LockFileName;
189 UniqueLockFilePattern += "-%%%%%%%%";
190 SmallString<128> UniquePath;
191 std::error_code EC = sys::fs::createUniqueFile(
192 UniqueLockFilePattern, UniqueLockFileID, UniquePath);
194 SmallString<128> Dir = sys::path::parent_path(UniqueLockFilePattern);
195 if (!Dir.empty()) {
196 if (std::error_code DirEC = sys::fs::create_directories(Dir))
197 return createStringError(DirEC,
198 "failed to create lock directory " + Dir);
199 }
200
201 // Retry creating lock file
202 EC = sys::fs::createUniqueFile(UniqueLockFilePattern, UniqueLockFileID,
203 UniquePath);
204 }
205
206 if (EC)
207 return createStringError(EC,
208 "failed to create unique file " + UniquePath);
209
210 UniqueLockFileName = UniquePath;
211 }
212
213 // Clean up the unique file on signal or scope exit.
214 RemoveUniqueLockFileOnSignal RemoveUniqueFile(UniqueLockFileName);
215
216 // Write our process ID to our unique lock file.
217 {
218 SmallString<256> HostID;
219 if (auto EC = getHostID(HostID))
220 return createStringError(EC, "failed to get host id");
221
222 raw_fd_ostream Out(UniqueLockFileID, /*shouldClose=*/true);
223 Out << HostID << ' ' << sys::Process::getProcessId();
224 Out.close();
225
226 if (Out.has_error()) {
227 // We failed to write out PID, so report the error and fail.
228 Error Err = createStringError(Out.error(),
229 "failed to write to " + UniqueLockFileName);
230 // Don't call report_fatal_error.
231 Out.clear_error();
232 return std::move(Err);
233 }
234 }
235
236 while (true) {
237 // Create a link from the lock file name. If this succeeds, we're done.
238 std::error_code EC =
239 sys::fs::create_link(UniqueLockFileName, LockFileName);
240 if (!EC) {
241 RemoveUniqueFile.lockAcquired();
242 Owner = OwnedByUs{};
243 return true;
244 }
245
246 if (EC != errc::file_exists)
247 return createStringError(EC, "failed to create link " + LockFileName +
248 " to " + UniqueLockFileName);
249
250 // Someone else managed to create the lock file first. Read the process ID
251 // from the lock file.
252 if (auto LockFileOwner = readLockFile(LockFileName)) {
253 Owner = std::move(*LockFileOwner);
254 return false;
255 }
256
257 if (!sys::fs::exists(LockFileName)) {
258 // The previous owner released the lock file before we could read it.
259 // Try to get ownership again.
260 continue;
261 }
262
263 // There is a lock file that nobody owns; try to clean it up and get
264 // ownership.
265 if ((EC = sys::fs::remove(LockFileName)))
266 return createStringError(EC, "failed to remove lockfile " +
267 UniqueLockFileName);
268 }
269}
270
272 auto BypassSandbox = sys::sandbox::scopedDisable();
273
274 if (!std::holds_alternative<OwnedByUs>(Owner))
275 return;
276
277 // Since we own the lock, remove the lock file and our own unique lock file.
278 sys::fs::remove(LockFileName);
279 sys::fs::remove(UniqueLockFileName);
280 // The unique file is now gone, so remove it from the signal handler. This
281 // matches a sys::RemoveFileOnSignal() in LockFileManager().
282 sys::DontRemoveFileOnSignal(UniqueLockFileName);
283}
284
286LockFileManager::waitForUnlockFor(std::chrono::seconds MaxSeconds) {
287 auto BypassSandbox = sys::sandbox::scopedDisable();
288
289 auto *LockFileOwner = std::get_if<OwnedByAnother>(&Owner);
290 assert(LockFileOwner &&
291 "waiting for lock to be unlocked without knowing the owner");
292
293 // Since we don't yet have an event-based method to wait for the lock file,
294 // use randomized exponential backoff, similar to Ethernet collision
295 // algorithm. This improves performance on machines with high core counts
296 // when the file lock is heavily contended by multiple clang processes
297 using namespace std::chrono_literals;
298 ExponentialBackoff Backoff(MaxSeconds, 10ms, 500ms);
299
300 // Wait first as this is only called when the lock is known to be held.
301 while (Backoff.waitForNextAttempt()) {
302 // FIXME: implement event-based waiting
303 if (sys::fs::access(LockFileName.c_str(), sys::fs::AccessMode::Exist) ==
306
307 // If the process owning the lock died without cleaning up, just bail out.
308 if (!processStillExecuting(LockFileOwner->OwnerHostName,
309 LockFileOwner->OwnerPID))
311 }
312
313 // Give up.
315}
316
318 auto BypassSandbox = sys::sandbox::scopedDisable();
319
320 return sys::fs::remove(LockFileName);
321}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
basic Basic Alias true
static ManagedStatic< DebugCounterOwner > Owner
Provides ErrorOr<T> smart pointer.
static std::error_code getHostID(SmallVectorImpl< char > &HostID)
static constexpr StringLiteral Filename
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 unsafeUnlock() 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...
StringRef getBuffer() const
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
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
void append(ItTy in_start, ItTy in_end)
Add the specified range to the end of the SmallVector.
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:490
constexpr StringRef substr(size_t Start, size_t N=npos) const
Return a reference to the substring from [Start, Start + N).
Definition StringRef.h:591
iterator begin() const
Definition StringRef.h:113
iterator end() const
Definition StringRef.h:115
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.
A raw_ostream that writes to a file descriptor.
bool has_error() const
Return the value of the flag in this raw_fd_ostream indicating whether an output error has been encou...
std::error_code error() const
void close()
Manually flush the stream and close the file.
void clear_error()
Set the flag read by has_error() to false.
static LLVM_ABI Pid getProcessId()
Get the process's identifier.
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:1091
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:875
LLVM_ABI std::error_code remove(const Twine &path, bool IgnoreNonExisting=true)
Remove path.
LLVM_ABI std::error_code create_directories(const Twine &path, bool IgnoreExisting=true, perms Perms=owner_all|group_all)
Create all the non-existent directories in path.
Definition Path.cpp:977
LLVM_ABI std::error_code make_absolute(SmallVectorImpl< char > &path)
Make path an absolute path.
Definition Path.cpp:963
LLVM_ABI StringRef parent_path(StringRef path LLVM_LIFETIME_BOUND, Style style=Style::native)
Get parent path.
Definition Path.cpp:468
ScopedSetting scopedDisable()
Definition IOSandbox.h:36
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.
LLVM_ABI std::pair< StringRef, StringRef > getToken(StringRef Source, StringRef Delimiters=" \t\n\v\f\r")
getToken - This function extracts one token from source, ignoring any leading characters that appear ...
Error createStringError(std::error_code EC, char const *Fmt, const Ts &... Vals)
Create formatted StringError object.
Definition Error.h:1328
@ no_such_file_or_directory
Definition Errc.h:65
@ file_exists
Definition Errc.h:48
WaitForUnlockResult
Describes the result of waiting for the owner to release the lock.
@ 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:1263