LLVM 24.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
33#include <windows.h>
34#endif
35#if LLVM_ON_UNIX
36#include <unistd.h>
37#endif
38
39#if defined(__APPLE__) && defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1050)
40#define USE_OSX_GETHOSTUUID 1
41#else
42#define USE_OSX_GETHOSTUUID 0
43#endif
44
45#if USE_OSX_GETHOSTUUID
46#include <uuid/uuid.h>
47#endif
48
49using namespace llvm;
50
51/// Attempt to read the lock file with the given name, if it exists.
52///
53/// \param LockFileName The name of the lock file to read.
54///
55/// \returns The process ID of the process that owns this lock file
56std::optional<LockFileManager::OwnedByAnother>
57LockFileManager::readLockFile(StringRef LockFileName) {
58 // Read the owning host and PID out of the lock file. If it appears that the
59 // owning process is dead, the lock file is invalid.
61 MemoryBuffer::getFile(LockFileName);
62 if (!MBOrErr) {
63 sys::fs::remove(LockFileName);
64 return std::nullopt;
65 }
66 MemoryBuffer &MB = *MBOrErr.get();
67
68 StringRef Hostname;
69 StringRef PIDStr;
70 std::tie(Hostname, PIDStr) = getToken(MB.getBuffer(), " ");
71 PIDStr = PIDStr.substr(PIDStr.find_first_not_of(' '));
72 int PID;
73 if (!PIDStr.getAsInteger(10, PID)) {
74 OwnedByAnother Owner;
75 Owner.OwnerHostName = Hostname;
76 Owner.OwnerPID = PID;
77 if (processStillExecuting(Owner.OwnerHostName, Owner.OwnerPID))
78 return Owner;
79 }
80
81 // Delete the lock file. It's invalid anyway.
82 sys::fs::remove(LockFileName);
83 return std::nullopt;
84}
85
86/// Get an identifier for this machine. It is recorded in lock files so that the
87/// owner of a lock file is only checked for on the machine that created it, and
88/// a lock file created by another machine, for example on a network drive, is
89/// not mistaken for one created by a process on this machine.
90static std::error_code getHostID(SmallVectorImpl<char> &HostID) {
91 HostID.clear();
92
93#if USE_OSX_GETHOSTUUID
94 // On OS X, use the more stable hardware UUID instead of hostname.
95 struct timespec wait = {1, 0}; // 1 second.
96 uuid_t uuid;
97 if (gethostuuid(uuid, &wait) != 0)
98 return errnoAsErrorCode();
99
100 uuid_string_t UUIDStr;
101 uuid_unparse(uuid, UUIDStr);
102 StringRef UUIDRef(UUIDStr);
103 HostID.append(UUIDRef.begin(), UUIDRef.end());
104
105#elif LLVM_ON_UNIX
106 char HostName[256];
107 HostName[255] = 0;
108 HostName[0] = 0;
109 gethostname(HostName, 255);
110 StringRef HostNameRef(HostName);
111 HostID.append(HostNameRef.begin(), HostNameRef.end());
112
113#elif defined(_WIN32)
114 wchar_t HostName[256];
115 DWORD Size = static_cast<DWORD>(std::size(HostName));
116 if (!::GetComputerNameExW(ComputerNamePhysicalDnsHostname, HostName, &Size))
117 return mapWindowsError(::GetLastError());
118 std::string HostNameUTF8;
119 if (!convertWideToUTF8(std::wstring_view(HostName, Size), HostNameUTF8))
120 return std::make_error_code(std::errc::illegal_byte_sequence);
121 HostID.append(HostNameUTF8.begin(), HostNameUTF8.end());
122
123#else
124 StringRef Dummy("localhost");
125 HostID.append(Dummy.begin(), Dummy.end());
126#endif
127
128 return std::error_code();
129}
130
131bool LockFileManager::processStillExecuting(StringRef HostID, int PID) {
132#if LLVM_ON_UNIX && !defined(__ANDROID__)
133 SmallString<256> StoredHostID;
134 if (getHostID(StoredHostID))
135 return true; // Conservatively assume it's executing on error.
136
137 // Check whether the process is dead. If so, we're done.
138 if (StoredHostID == HostID && getsid(PID) == -1 && errno == ESRCH)
139 return false;
140#elif defined(_WIN32)
141 SmallString<256> StoredHostID;
142 if (getHostID(StoredHostID))
143 return true; // Conservatively assume it's executing on error.
144 if (StoredHostID != HostID)
145 return true;
146
147 // Windows reuses process IDs, so a live process with this ID may be an
148 // unrelated process that started after the owner exited. Treating the lock
149 // as owned in that case only costs a wait for the lock to time out.
150 HANDLE Process =
151 ::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION,
152 /*bInheritHandle=*/FALSE, static_cast<DWORD>(PID));
153 // ERROR_INVALID_PARAMETER means that there is no process with this ID. Any
154 // other error, such as ERROR_ACCESS_DENIED, means that the process exists.
155 if (!Process)
156 return ::GetLastError() != ERROR_INVALID_PARAMETER;
157 // A process that has exited can still be opened while handles to it remain
158 // open, so check whether it is still running. A process that exited with the
159 // code STILL_ACTIVE is conservatively considered to be executing.
160 DWORD ExitCode = 0;
161 bool Exited =
162 ::GetExitCodeProcess(Process, &ExitCode) && ExitCode != STILL_ACTIVE;
163 ::CloseHandle(Process);
164 return !Exited;
165#endif
166
167 return true;
168}
169
170namespace {
171
172/// An RAII helper object ensure that the unique lock file is removed.
173///
174/// Ensures that if there is an error or a signal before we finish acquiring the
175/// lock, the unique file will be removed. And if we successfully take the lock,
176/// the signal handler is left in place so that signals while the lock is held
177/// will remove the unique lock file. The caller should ensure there is a
178/// matching call to sys::DontRemoveFileOnSignal when the lock is released.
179class RemoveUniqueLockFileOnSignal {
180 StringRef Filename;
181 bool RemoveImmediately;
182public:
183 RemoveUniqueLockFileOnSignal(StringRef Name)
184 : Filename(Name), RemoveImmediately(true) {
186 }
187
188 ~RemoveUniqueLockFileOnSignal() {
189 if (!RemoveImmediately) {
190 // Leave the signal handler enabled. It will be removed when the lock is
191 // released.
192 return;
193 }
196 }
197
198 void lockAcquired() { RemoveImmediately = false; }
199};
200
201} // end anonymous namespace
202
203LockFileManager::LockFileManager(StringRef FileName)
204 : FileName(FileName), Owner(OwnerUnknown{}) {}
205
207 auto BypassSandbox = sys::sandbox::scopedDisable();
208
209 assert(std::holds_alternative<OwnerUnknown>(Owner) &&
210 "lock has already been attempted");
211
212 SmallString<128> AbsoluteFileName(FileName);
213 if (std::error_code EC = sys::fs::make_absolute(AbsoluteFileName))
214 return createStringError(EC, "failed to obtain absolute path for " +
215 AbsoluteFileName);
216 LockFileName = AbsoluteFileName;
217 LockFileName += ".lock";
218
219 // If the lock file already exists, don't bother to try to create our own
220 // lock file; it won't work anyway. Just figure out who owns this lock file.
221 if (auto LockFileOwner = readLockFile(LockFileName)) {
222 Owner = std::move(*LockFileOwner);
223 return false;
224 }
225
226 // Create a lock file that is unique to this instance.
227 int UniqueLockFileID;
228 {
229 SmallString<128> UniqueLockFilePattern = LockFileName;
230 UniqueLockFilePattern += "-%%%%%%%%";
231 SmallString<128> UniquePath;
232 std::error_code EC = sys::fs::createUniqueFile(
233 UniqueLockFilePattern, UniqueLockFileID, UniquePath);
235 SmallString<128> Dir = sys::path::parent_path(UniqueLockFilePattern);
236 if (!Dir.empty()) {
237 if (std::error_code DirEC = sys::fs::create_directories(Dir))
238 return createStringError(DirEC,
239 "failed to create lock directory " + Dir);
240 }
241
242 // Retry creating lock file
243 EC = sys::fs::createUniqueFile(UniqueLockFilePattern, UniqueLockFileID,
244 UniquePath);
245 }
246
247 if (EC)
248 return createStringError(EC,
249 "failed to create unique file " + UniquePath);
250
251 UniqueLockFileName = UniquePath;
252 }
253
254 // Clean up the unique file on signal or scope exit.
255 RemoveUniqueLockFileOnSignal RemoveUniqueFile(UniqueLockFileName);
256
257 // Write our process ID to our unique lock file.
258 {
259 SmallString<256> HostID;
260 if (auto EC = getHostID(HostID))
261 return createStringError(EC, "failed to get host id");
262
263 raw_fd_ostream Out(UniqueLockFileID, /*shouldClose=*/true);
264 Out << HostID << ' ' << sys::Process::getProcessId();
265 Out.close();
266
267 if (Out.has_error()) {
268 // We failed to write out PID, so report the error and fail.
269 Error Err = createStringError(Out.error(),
270 "failed to write to " + UniqueLockFileName);
271 // Don't call report_fatal_error.
272 Out.clear_error();
273 return std::move(Err);
274 }
275 }
276
277 while (true) {
278 // Create a link from the lock file name. If this succeeds, we're done.
279 std::error_code EC =
280 sys::fs::create_link(UniqueLockFileName, LockFileName);
281 if (!EC) {
282 RemoveUniqueFile.lockAcquired();
283 Owner = OwnedByUs{};
284 return true;
285 }
286
287 if (EC != errc::file_exists)
288 return createStringError(EC, "failed to create link " + LockFileName +
289 " to " + UniqueLockFileName);
290
291 // Someone else managed to create the lock file first. Read the process ID
292 // from the lock file.
293 if (auto LockFileOwner = readLockFile(LockFileName)) {
294 Owner = std::move(*LockFileOwner);
295 return false;
296 }
297
298 if (!sys::fs::exists(LockFileName)) {
299 // The previous owner released the lock file before we could read it.
300 // Try to get ownership again.
301 continue;
302 }
303
304 // There is a lock file that nobody owns; try to clean it up and get
305 // ownership.
306 if ((EC = sys::fs::remove(LockFileName)))
307 return createStringError(EC, "failed to remove lockfile " +
308 UniqueLockFileName);
309 }
310}
311
313 auto BypassSandbox = sys::sandbox::scopedDisable();
314
315 if (!std::holds_alternative<OwnedByUs>(Owner))
316 return;
317
318 // Since we own the lock, remove the lock file and our own unique lock file.
319 sys::fs::remove(LockFileName);
320 sys::fs::remove(UniqueLockFileName);
321 // The unique file is now gone, so remove it from the signal handler. This
322 // matches a sys::RemoveFileOnSignal() in LockFileManager().
323 sys::DontRemoveFileOnSignal(UniqueLockFileName);
324}
325
327LockFileManager::waitForUnlockFor(std::chrono::seconds MaxSeconds) {
328 auto BypassSandbox = sys::sandbox::scopedDisable();
329
330 auto *LockFileOwner = std::get_if<OwnedByAnother>(&Owner);
331 assert(LockFileOwner &&
332 "waiting for lock to be unlocked without knowing the owner");
333
334 // Since we don't yet have an event-based method to wait for the lock file,
335 // use randomized exponential backoff, similar to Ethernet collision
336 // algorithm. This improves performance on machines with high core counts
337 // when the file lock is heavily contended by multiple clang processes
338 using namespace std::chrono_literals;
339 ExponentialBackoff Backoff(MaxSeconds, 10ms, 500ms);
340
341 // Wait first as this is only called when the lock is known to be held.
342 while (Backoff.waitForNextAttempt()) {
343 // FIXME: implement event-based waiting
344 if (sys::fs::access(LockFileName.c_str(), sys::fs::AccessMode::Exist) ==
347
348 // If the process owning the lock died without cleaning up, just bail out.
349 if (!processStillExecuting(LockFileOwner->OwnerHostName,
350 LockFileOwner->OwnerPID))
352 }
353
354 // Give up.
356}
357
359 auto BypassSandbox = sys::sandbox::scopedDisable();
360
361 return sys::fs::remove(LockFileName);
362}
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)
Get an identifier for this machine.
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.
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
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:597
iterator begin() const
Definition StringRef.h:114
iterator end() const
Definition StringRef.h:116
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.
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:1107
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:891
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:993
LLVM_ABI std::error_code make_absolute(SmallVectorImpl< char > &path)
Make path an absolute path.
Definition Path.cpp:979
LLVM_ABI StringRef parent_path(StringRef path LLVM_LIFETIME_BOUND, Style style=Style::native)
Get parent path.
Definition Path.cpp:478
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:1321
LLVM_ABI bool convertWideToUTF8(std::wstring_view Source, std::string &Result)
Converts a wide string view to a UTF-8 encoded std::string.
@ 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.
LLVM_ABI std::error_code mapWindowsError(unsigned EV)
std::error_code errnoAsErrorCode()
Helper to get errno as an std::error_code.
Definition Error.h:1256