xref: /src/contrib/llvm-project/llvm/lib/Support/LockFileManager.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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