1//===--- LockFileManager.cpp - File-level Locking Utility------------------===//
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
7//===----------------------------------------------------------------------===//
12#include "llvm/Config/llvm-config.h" // for LLVM_ON_UNIX
25#include <system_error>
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
38 #define USE_OSX_GETHOSTUUID 0
41#if USE_OSX_GETHOSTUUID
47/// Attempt to read the lock file with the given name, if it exists.
49/// \param LockFileName The name of the lock file to read.
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.
71 Owner.OwnerHostName = Hostname;
73 if (processStillExecuting(Owner.OwnerHostName, Owner.OwnerPID))
77 // Delete the lock file. It's invalid anyway.
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.
89 if (gethostuuid(uuid, &wait) != 0)
92 uuid_string_t UUIDStr;
93 uuid_unparse(uuid, UUIDStr);
101 gethostname(HostName, 255);
110 return std::error_code();
113bool LockFileManager::processStillExecuting(
StringRef HostID,
int PID) {
114#if LLVM_ON_UNIX && !defined(__ANDROID__)
115 SmallString<256> StoredHostID;
117 return true;
// Conservatively assume it's executing on error.
119 // Check whether the process is dead. If so, we're done.
120 if (StoredHostID == HostID && getsid(PID) == -1 && errno == ESRCH)
129/// An RAII helper object ensure that the unique lock file is removed.
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;
140 RemoveUniqueLockFileOnSignal(StringRef Name)
145 ~RemoveUniqueLockFileOnSignal() {
146 if (!RemoveImmediately) {
147 // Leave the signal handler enabled. It will be removed when the lock is
155 void lockAcquired() { RemoveImmediately =
false; }
158}
// end anonymous namespace
161 : FileName(FileName), Owner(OwnerUnknown{}) {}
164 assert(std::holds_alternative<OwnerUnknown>(Owner) &&
165 "lock has already been attempted");
171 LockFileName = AbsoluteFileName;
172 LockFileName +=
".lock";
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);
181 // Create a lock file that is unique to this instance.
182 UniqueLockFileName = LockFileName;
183 UniqueLockFileName +=
"-%%%%%%%%";
184 int UniqueLockFileID;
186 UniqueLockFileName, UniqueLockFileID, UniqueLockFileName))
190 // Clean up the unique file on signal or scope exit.
191 RemoveUniqueLockFileOnSignal RemoveUniqueFile(UniqueLockFileName);
193 // Write our process ID to our unique lock file.
204 // We failed to write out PID, so report the error and fail.
206 "failed to write to " + UniqueLockFileName);
207 // Don't call report_fatal_error.
209 return std::move(Err);
214 // Create a link from the lock file name. If this succeeds, we're done.
218 RemoveUniqueFile.lockAcquired();
225 " to " + UniqueLockFileName);
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);
235 // The previous owner released the lock file before we could read it.
236 // Try to get ownership again.
240 // There is a lock file that nobody owns; try to clean it up and get
249 if (!std::holds_alternative<OwnedByUs>(Owner))
252 // Since we own the lock, remove the lock file and our own unique lock file.
255 // The unique file is now gone, so remove it from the signal handler. This
256 // matches a sys::RemoveFileOnSignal() in LockFileManager().
262 auto *LockFileOwner = std::get_if<OwnedByAnother>(&Owner);
264 "waiting for lock to be unlocked without knowing the owner");
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;
273 // Wait first as this is only called when the lock is known to be held.
275 // FIXME: implement event-based waiting
280 // If the process owning the lock died without cleaning up, just bail out.
281 if (!processStillExecuting(LockFileOwner->OwnerHostName,
282 LockFileOwner->OwnerPID))
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
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.
Represents either an error or a value T.
Lightweight error class with error context and mandatory checking.
Tagged union holding either a T or a Error.
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...
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...
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.
bool getAsInteger(unsigned Radix, T &Result) const
Parse the current string as an integer of the specified radix.
constexpr StringRef substr(size_t Start, size_t N=npos) const
Return a reference to the substring from [Start, Start + N).
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?
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.
LLVM_ABI std::error_code remove(const Twine &path, bool IgnoreNonExisting=true)
Remove path.
LLVM_ABI std::error_code make_absolute(SmallVectorImpl< char > &path)
Make path an absolute 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.
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.
@ no_such_file_or_directory
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.