163faed5bSDimitry Andric //===--- LockFileManager.cpp - File-level Locking Utility------------------===//
263faed5bSDimitry Andric //
3e6d15924SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e6d15924SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5e6d15924SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
663faed5bSDimitry Andric //
763faed5bSDimitry Andric //===----------------------------------------------------------------------===//
8b915e9e0SDimitry Andric
963faed5bSDimitry Andric #include "llvm/Support/LockFileManager.h"
10b915e9e0SDimitry Andric #include "llvm/ADT/SmallVector.h"
11f8af5cf6SDimitry Andric #include "llvm/ADT/StringExtras.h"
125ca98fd9SDimitry Andric #include "llvm/Support/Errc.h"
13b915e9e0SDimitry Andric #include "llvm/Support/ErrorOr.h"
14ac9a064cSDimitry Andric #include "llvm/Support/ExponentialBackoff.h"
1563faed5bSDimitry Andric #include "llvm/Support/FileSystem.h"
16f8af5cf6SDimitry Andric #include "llvm/Support/MemoryBuffer.h"
17cfca06d7SDimitry Andric #include "llvm/Support/Process.h"
181a82d4c0SDimitry Andric #include "llvm/Support/Signals.h"
197ab83427SDimitry Andric #include "llvm/Support/raw_ostream.h"
20b915e9e0SDimitry Andric #include <cerrno>
21cfca06d7SDimitry Andric #include <chrono>
22b915e9e0SDimitry Andric #include <ctime>
23b915e9e0SDimitry Andric #include <memory>
2463faed5bSDimitry Andric #include <sys/stat.h>
254a16efa3SDimitry Andric #include <sys/types.h>
267ab83427SDimitry Andric #include <system_error>
27cfca06d7SDimitry Andric #include <thread>
287ab83427SDimitry Andric #include <tuple>
29cfca06d7SDimitry Andric
30d8e91e46SDimitry Andric #ifdef _WIN32
3163faed5bSDimitry Andric #include <windows.h>
3263faed5bSDimitry Andric #endif
3363faed5bSDimitry Andric #if LLVM_ON_UNIX
3463faed5bSDimitry Andric #include <unistd.h>
3563faed5bSDimitry Andric #endif
361a82d4c0SDimitry Andric
37c0981da4SDimitry Andric #if defined(__APPLE__) && defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1050)
381a82d4c0SDimitry Andric #define USE_OSX_GETHOSTUUID 1
391a82d4c0SDimitry Andric #else
401a82d4c0SDimitry Andric #define USE_OSX_GETHOSTUUID 0
411a82d4c0SDimitry Andric #endif
421a82d4c0SDimitry Andric
431a82d4c0SDimitry Andric #if USE_OSX_GETHOSTUUID
441a82d4c0SDimitry Andric #include <uuid/uuid.h>
451a82d4c0SDimitry Andric #endif
46b915e9e0SDimitry Andric
4763faed5bSDimitry Andric using namespace llvm;
4863faed5bSDimitry Andric
49eb11fae6SDimitry Andric /// Attempt to read the lock file with the given name, if it exists.
5063faed5bSDimitry Andric ///
5163faed5bSDimitry Andric /// \param LockFileName The name of the lock file to read.
5263faed5bSDimitry Andric ///
5363faed5bSDimitry Andric /// \returns The process ID of the process that owns this lock file
54e3b55780SDimitry Andric std::optional<std::pair<std::string, int>>
readLockFile(StringRef LockFileName)5563faed5bSDimitry Andric LockFileManager::readLockFile(StringRef LockFileName) {
5663faed5bSDimitry Andric // Read the owning host and PID out of the lock file. If it appears that the
5763faed5bSDimitry Andric // owning process is dead, the lock file is invalid.
585ca98fd9SDimitry Andric ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr =
595ca98fd9SDimitry Andric MemoryBuffer::getFile(LockFileName);
605ca98fd9SDimitry Andric if (!MBOrErr) {
615ca98fd9SDimitry Andric sys::fs::remove(LockFileName);
62e3b55780SDimitry Andric return std::nullopt;
635ca98fd9SDimitry Andric }
6467c32a98SDimitry Andric MemoryBuffer &MB = *MBOrErr.get();
65f8af5cf6SDimitry Andric
66f8af5cf6SDimitry Andric StringRef Hostname;
67f8af5cf6SDimitry Andric StringRef PIDStr;
6867c32a98SDimitry Andric std::tie(Hostname, PIDStr) = getToken(MB.getBuffer(), " ");
69ac9a064cSDimitry Andric PIDStr = PIDStr.substr(PIDStr.find_first_not_of(' '));
70f8af5cf6SDimitry Andric int PID;
715ca98fd9SDimitry Andric if (!PIDStr.getAsInteger(10, PID)) {
725ca98fd9SDimitry Andric auto Owner = std::make_pair(std::string(Hostname), PID);
735ca98fd9SDimitry Andric if (processStillExecuting(Owner.first, Owner.second))
745ca98fd9SDimitry Andric return Owner;
755ca98fd9SDimitry Andric }
7663faed5bSDimitry Andric
7763faed5bSDimitry Andric // Delete the lock file. It's invalid anyway.
78f8af5cf6SDimitry Andric sys::fs::remove(LockFileName);
79e3b55780SDimitry Andric return std::nullopt;
8063faed5bSDimitry Andric }
8163faed5bSDimitry Andric
getHostID(SmallVectorImpl<char> & HostID)821a82d4c0SDimitry Andric static std::error_code getHostID(SmallVectorImpl<char> &HostID) {
831a82d4c0SDimitry Andric HostID.clear();
841a82d4c0SDimitry Andric
851a82d4c0SDimitry Andric #if USE_OSX_GETHOSTUUID
861a82d4c0SDimitry Andric // On OS X, use the more stable hardware UUID instead of hostname.
871a82d4c0SDimitry Andric struct timespec wait = {1, 0}; // 1 second.
881a82d4c0SDimitry Andric uuid_t uuid;
891a82d4c0SDimitry Andric if (gethostuuid(uuid, &wait) != 0)
90ac9a064cSDimitry Andric return errnoAsErrorCode();
911a82d4c0SDimitry Andric
921a82d4c0SDimitry Andric uuid_string_t UUIDStr;
931a82d4c0SDimitry Andric uuid_unparse(uuid, UUIDStr);
941a82d4c0SDimitry Andric StringRef UUIDRef(UUIDStr);
951a82d4c0SDimitry Andric HostID.append(UUIDRef.begin(), UUIDRef.end());
961a82d4c0SDimitry Andric
971a82d4c0SDimitry Andric #elif LLVM_ON_UNIX
981a82d4c0SDimitry Andric char HostName[256];
991a82d4c0SDimitry Andric HostName[255] = 0;
1001a82d4c0SDimitry Andric HostName[0] = 0;
1011a82d4c0SDimitry Andric gethostname(HostName, 255);
1021a82d4c0SDimitry Andric StringRef HostNameRef(HostName);
1031a82d4c0SDimitry Andric HostID.append(HostNameRef.begin(), HostNameRef.end());
1041a82d4c0SDimitry Andric
1051a82d4c0SDimitry Andric #else
1061a82d4c0SDimitry Andric StringRef Dummy("localhost");
1071a82d4c0SDimitry Andric HostID.append(Dummy.begin(), Dummy.end());
1081a82d4c0SDimitry Andric #endif
1091a82d4c0SDimitry Andric
1101a82d4c0SDimitry Andric return std::error_code();
1111a82d4c0SDimitry Andric }
1121a82d4c0SDimitry Andric
processStillExecuting(StringRef HostID,int PID)1131a82d4c0SDimitry Andric bool LockFileManager::processStillExecuting(StringRef HostID, int PID) {
114522600a2SDimitry Andric #if LLVM_ON_UNIX && !defined(__ANDROID__)
1151a82d4c0SDimitry Andric SmallString<256> StoredHostID;
1161a82d4c0SDimitry Andric if (getHostID(StoredHostID))
1171a82d4c0SDimitry Andric return true; // Conservatively assume it's executing on error.
1181a82d4c0SDimitry Andric
11963faed5bSDimitry Andric // Check whether the process is dead. If so, we're done.
1201a82d4c0SDimitry Andric if (StoredHostID == HostID && getsid(PID) == -1 && errno == ESRCH)
12163faed5bSDimitry Andric return false;
12263faed5bSDimitry Andric #endif
12363faed5bSDimitry Andric
12463faed5bSDimitry Andric return true;
12563faed5bSDimitry Andric }
12663faed5bSDimitry Andric
1271a82d4c0SDimitry Andric namespace {
128b915e9e0SDimitry Andric
129eb11fae6SDimitry Andric /// An RAII helper object ensure that the unique lock file is removed.
130eb11fae6SDimitry Andric ///
131eb11fae6SDimitry Andric /// Ensures that if there is an error or a signal before we finish acquiring the
132eb11fae6SDimitry Andric /// lock, the unique file will be removed. And if we successfully take the lock,
133eb11fae6SDimitry Andric /// the signal handler is left in place so that signals while the lock is held
134eb11fae6SDimitry Andric /// will remove the unique lock file. The caller should ensure there is a
135eb11fae6SDimitry Andric /// matching call to sys::DontRemoveFileOnSignal when the lock is released.
136eb11fae6SDimitry Andric class RemoveUniqueLockFileOnSignal {
137eb11fae6SDimitry Andric StringRef Filename;
138eb11fae6SDimitry Andric bool RemoveImmediately;
1391a82d4c0SDimitry Andric public:
RemoveUniqueLockFileOnSignal(StringRef Name)140eb11fae6SDimitry Andric RemoveUniqueLockFileOnSignal(StringRef Name)
141eb11fae6SDimitry Andric : Filename(Name), RemoveImmediately(true) {
142eb11fae6SDimitry Andric sys::RemoveFileOnSignal(Filename, nullptr);
1431a82d4c0SDimitry Andric }
144b915e9e0SDimitry Andric
~RemoveUniqueLockFileOnSignal()145eb11fae6SDimitry Andric ~RemoveUniqueLockFileOnSignal() {
146eb11fae6SDimitry Andric if (!RemoveImmediately) {
147eb11fae6SDimitry Andric // Leave the signal handler enabled. It will be removed when the lock is
148eb11fae6SDimitry Andric // released.
149eb11fae6SDimitry Andric return;
150eb11fae6SDimitry Andric }
151eb11fae6SDimitry Andric sys::fs::remove(Filename);
152eb11fae6SDimitry Andric sys::DontRemoveFileOnSignal(Filename);
153eb11fae6SDimitry Andric }
154eb11fae6SDimitry Andric
lockAcquired()155eb11fae6SDimitry Andric void lockAcquired() { RemoveImmediately = false; }
1561a82d4c0SDimitry Andric };
157b915e9e0SDimitry Andric
1581a82d4c0SDimitry Andric } // end anonymous namespace
1591a82d4c0SDimitry Andric
LockFileManager(StringRef FileName)16063faed5bSDimitry Andric LockFileManager::LockFileManager(StringRef FileName)
16163faed5bSDimitry Andric {
1624a16efa3SDimitry Andric this->FileName = FileName;
1635ca98fd9SDimitry Andric if (std::error_code EC = sys::fs::make_absolute(this->FileName)) {
16401095a5dSDimitry Andric std::string S("failed to obtain absolute path for ");
1654df029ccSDimitry Andric S.append(std::string(this->FileName));
16601095a5dSDimitry Andric setError(EC, S);
1675ca98fd9SDimitry Andric return;
1685ca98fd9SDimitry Andric }
1695ca98fd9SDimitry Andric LockFileName = this->FileName;
17063faed5bSDimitry Andric LockFileName += ".lock";
17163faed5bSDimitry Andric
17263faed5bSDimitry Andric // If the lock file already exists, don't bother to try to create our own
17363faed5bSDimitry Andric // lock file; it won't work anyway. Just figure out who owns this lock file.
17463faed5bSDimitry Andric if ((Owner = readLockFile(LockFileName)))
17563faed5bSDimitry Andric return;
17663faed5bSDimitry Andric
17763faed5bSDimitry Andric // Create a lock file that is unique to this instance.
178eb11fae6SDimitry Andric UniqueLockFileName = LockFileName;
179eb11fae6SDimitry Andric UniqueLockFileName += "-%%%%%%%%";
180eb11fae6SDimitry Andric int UniqueLockFileID;
181eb11fae6SDimitry Andric if (std::error_code EC = sys::fs::createUniqueFile(
182eb11fae6SDimitry Andric UniqueLockFileName, UniqueLockFileID, UniqueLockFileName)) {
183eb11fae6SDimitry Andric std::string S("failed to create unique file ");
1844df029ccSDimitry Andric S.append(std::string(UniqueLockFileName));
18501095a5dSDimitry Andric setError(EC, S);
18663faed5bSDimitry Andric return;
18763faed5bSDimitry Andric }
18863faed5bSDimitry Andric
18963faed5bSDimitry Andric // Write our process ID to our unique lock file.
19063faed5bSDimitry Andric {
1911a82d4c0SDimitry Andric SmallString<256> HostID;
1921a82d4c0SDimitry Andric if (auto EC = getHostID(HostID)) {
19301095a5dSDimitry Andric setError(EC, "failed to get host id");
1941a82d4c0SDimitry Andric return;
1951a82d4c0SDimitry Andric }
19663faed5bSDimitry Andric
197eb11fae6SDimitry Andric raw_fd_ostream Out(UniqueLockFileID, /*shouldClose=*/true);
198cfca06d7SDimitry Andric Out << HostID << ' ' << sys::Process::getProcessId();
199eb11fae6SDimitry Andric Out.close();
20063faed5bSDimitry Andric
20163faed5bSDimitry Andric if (Out.has_error()) {
202044eb2f6SDimitry Andric // We failed to write out PID, so report the error, remove the
20363faed5bSDimitry Andric // unique lock file, and fail.
20401095a5dSDimitry Andric std::string S("failed to write to ");
2054df029ccSDimitry Andric S.append(std::string(UniqueLockFileName));
206044eb2f6SDimitry Andric setError(Out.error(), S);
207eb11fae6SDimitry Andric sys::fs::remove(UniqueLockFileName);
208ac9a064cSDimitry Andric // Don't call report_fatal_error.
209ac9a064cSDimitry Andric Out.clear_error();
21063faed5bSDimitry Andric return;
21163faed5bSDimitry Andric }
21263faed5bSDimitry Andric }
21363faed5bSDimitry Andric
214eb11fae6SDimitry Andric // Clean up the unique file on signal, which also releases the lock if it is
215eb11fae6SDimitry Andric // held since the .lock symlink will point to a nonexistent file.
216eb11fae6SDimitry Andric RemoveUniqueLockFileOnSignal RemoveUniqueFile(UniqueLockFileName);
217eb11fae6SDimitry Andric
218b915e9e0SDimitry Andric while (true) {
2195ca98fd9SDimitry Andric // Create a link from the lock file name. If this succeeds, we're done.
2205ca98fd9SDimitry Andric std::error_code EC =
221eb11fae6SDimitry Andric sys::fs::create_link(UniqueLockFileName, LockFileName);
2221a82d4c0SDimitry Andric if (!EC) {
223eb11fae6SDimitry Andric RemoveUniqueFile.lockAcquired();
22463faed5bSDimitry Andric return;
2251a82d4c0SDimitry Andric }
22663faed5bSDimitry Andric
2275ca98fd9SDimitry Andric if (EC != errc::file_exists) {
22801095a5dSDimitry Andric std::string S("failed to create link ");
22901095a5dSDimitry Andric raw_string_ostream OSS(S);
230eb11fae6SDimitry Andric OSS << LockFileName.str() << " to " << UniqueLockFileName.str();
231ac9a064cSDimitry Andric setError(EC, S);
2325ca98fd9SDimitry Andric return;
2335ca98fd9SDimitry Andric }
2345ca98fd9SDimitry Andric
2355ca98fd9SDimitry Andric // Someone else managed to create the lock file first. Read the process ID
2365ca98fd9SDimitry Andric // from the lock file.
237eb11fae6SDimitry Andric if ((Owner = readLockFile(LockFileName))) {
238eb11fae6SDimitry Andric // Wipe out our unique lock file (it's useless now)
239eb11fae6SDimitry Andric sys::fs::remove(UniqueLockFileName);
240eb11fae6SDimitry Andric return;
241eb11fae6SDimitry Andric }
2425ca98fd9SDimitry Andric
2435a5ac124SDimitry Andric if (!sys::fs::exists(LockFileName)) {
2445ca98fd9SDimitry Andric // The previous owner released the lock file before we could read it.
2455ca98fd9SDimitry Andric // Try to get ownership again.
2465ca98fd9SDimitry Andric continue;
2475ca98fd9SDimitry Andric }
2485ca98fd9SDimitry Andric
2495ca98fd9SDimitry Andric // There is a lock file that nobody owns; try to clean it up and get
2505ca98fd9SDimitry Andric // ownership.
2515a5ac124SDimitry Andric if ((EC = sys::fs::remove(LockFileName))) {
25201095a5dSDimitry Andric std::string S("failed to remove lockfile ");
2534df029ccSDimitry Andric S.append(std::string(UniqueLockFileName));
25401095a5dSDimitry Andric setError(EC, S);
2555ca98fd9SDimitry Andric return;
2565ca98fd9SDimitry Andric }
2575ca98fd9SDimitry Andric }
25863faed5bSDimitry Andric }
25963faed5bSDimitry Andric
getState() const26063faed5bSDimitry Andric LockFileManager::LockFileState LockFileManager::getState() const {
26163faed5bSDimitry Andric if (Owner)
26263faed5bSDimitry Andric return LFS_Shared;
26363faed5bSDimitry Andric
264044eb2f6SDimitry Andric if (ErrorCode)
26563faed5bSDimitry Andric return LFS_Error;
26663faed5bSDimitry Andric
26763faed5bSDimitry Andric return LFS_Owned;
26863faed5bSDimitry Andric }
26963faed5bSDimitry Andric
getErrorMessage() const27001095a5dSDimitry Andric std::string LockFileManager::getErrorMessage() const {
271044eb2f6SDimitry Andric if (ErrorCode) {
27201095a5dSDimitry Andric std::string Str(ErrorDiagMsg);
273044eb2f6SDimitry Andric std::string ErrCodeMsg = ErrorCode.message();
27401095a5dSDimitry Andric raw_string_ostream OSS(Str);
27501095a5dSDimitry Andric if (!ErrCodeMsg.empty())
276044eb2f6SDimitry Andric OSS << ": " << ErrCodeMsg;
277ac9a064cSDimitry Andric return Str;
27801095a5dSDimitry Andric }
27901095a5dSDimitry Andric return "";
28001095a5dSDimitry Andric }
28101095a5dSDimitry Andric
~LockFileManager()28263faed5bSDimitry Andric LockFileManager::~LockFileManager() {
28363faed5bSDimitry Andric if (getState() != LFS_Owned)
28463faed5bSDimitry Andric return;
28563faed5bSDimitry Andric
28663faed5bSDimitry Andric // Since we own the lock, remove the lock file and our own unique lock file.
2875a5ac124SDimitry Andric sys::fs::remove(LockFileName);
288eb11fae6SDimitry Andric sys::fs::remove(UniqueLockFileName);
289eb11fae6SDimitry Andric // The unique file is now gone, so remove it from the signal handler. This
290eb11fae6SDimitry Andric // matches a sys::RemoveFileOnSignal() in LockFileManager().
291eb11fae6SDimitry Andric sys::DontRemoveFileOnSignal(UniqueLockFileName);
29263faed5bSDimitry Andric }
29363faed5bSDimitry Andric
294706b4fc4SDimitry Andric LockFileManager::WaitForUnlockResult
waitForUnlock(const unsigned MaxSeconds)295706b4fc4SDimitry Andric LockFileManager::waitForUnlock(const unsigned MaxSeconds) {
29663faed5bSDimitry Andric if (getState() != LFS_Shared)
2975ca98fd9SDimitry Andric return Res_Success;
29863faed5bSDimitry Andric
299cfca06d7SDimitry Andric // Since we don't yet have an event-based method to wait for the lock file,
300ac9a064cSDimitry Andric // use randomized exponential backoff, similar to Ethernet collision
301cfca06d7SDimitry Andric // algorithm. This improves performance on machines with high core counts
302cfca06d7SDimitry Andric // when the file lock is heavily contended by multiple clang processes
303ac9a064cSDimitry Andric using namespace std::chrono_literals;
304ac9a064cSDimitry Andric ExponentialBackoff Backoff(std::chrono::seconds(MaxSeconds), 10ms, 500ms);
305cfca06d7SDimitry Andric
306ac9a064cSDimitry Andric // Wait first as this is only called when the lock is known to be held.
307ac9a064cSDimitry Andric while (Backoff.waitForNextAttempt()) {
308cfca06d7SDimitry Andric // FIXME: implement event-based waiting
30967c32a98SDimitry Andric if (sys::fs::access(LockFileName.c_str(), sys::fs::AccessMode::Exist) ==
31067c32a98SDimitry Andric errc::no_such_file_or_directory) {
3115a5ac124SDimitry Andric // If the original file wasn't created, somone thought the lock was dead.
3125a5ac124SDimitry Andric if (!sys::fs::exists(FileName))
3135a5ac124SDimitry Andric return Res_OwnerDied;
3145ca98fd9SDimitry Andric return Res_Success;
3154a16efa3SDimitry Andric }
31663faed5bSDimitry Andric
3175a5ac124SDimitry Andric // If the process owning the lock died without cleaning up, just bail out.
3185a5ac124SDimitry Andric if (!processStillExecuting((*Owner).first, (*Owner).second))
3195ca98fd9SDimitry Andric return Res_OwnerDied;
32063faed5bSDimitry Andric }
321cfca06d7SDimitry Andric
32263faed5bSDimitry Andric // Give up.
3235ca98fd9SDimitry Andric return Res_Timeout;
32463faed5bSDimitry Andric }
3255a5ac124SDimitry Andric
unsafeRemoveLockFile()3265a5ac124SDimitry Andric std::error_code LockFileManager::unsafeRemoveLockFile() {
3275a5ac124SDimitry Andric return sys::fs::remove(LockFileName);
3285a5ac124SDimitry Andric }
329