10b57cec5SDimitry Andric //===--- LockFileManager.cpp - File-level Locking Utility------------------===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric 90b57cec5SDimitry Andric #include "llvm/Support/LockFileManager.h" 100b57cec5SDimitry Andric #include "llvm/ADT/SmallVector.h" 110b57cec5SDimitry Andric #include "llvm/ADT/StringExtras.h" 120b57cec5SDimitry Andric #include "llvm/Support/Errc.h" 130b57cec5SDimitry Andric #include "llvm/Support/ErrorOr.h" 14*0fca6ea1SDimitry Andric #include "llvm/Support/ExponentialBackoff.h" 150b57cec5SDimitry Andric #include "llvm/Support/FileSystem.h" 160b57cec5SDimitry Andric #include "llvm/Support/MemoryBuffer.h" 175ffd83dbSDimitry Andric #include "llvm/Support/Process.h" 180b57cec5SDimitry Andric #include "llvm/Support/Signals.h" 190b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h" 200b57cec5SDimitry Andric #include <cerrno> 215ffd83dbSDimitry Andric #include <chrono> 220b57cec5SDimitry Andric #include <ctime> 230b57cec5SDimitry Andric #include <memory> 240b57cec5SDimitry Andric #include <sys/stat.h> 250b57cec5SDimitry Andric #include <sys/types.h> 260b57cec5SDimitry Andric #include <system_error> 275ffd83dbSDimitry Andric #include <thread> 280b57cec5SDimitry Andric #include <tuple> 295ffd83dbSDimitry Andric 300b57cec5SDimitry Andric #ifdef _WIN32 310b57cec5SDimitry Andric #include <windows.h> 320b57cec5SDimitry Andric #endif 330b57cec5SDimitry Andric #if LLVM_ON_UNIX 340b57cec5SDimitry Andric #include <unistd.h> 350b57cec5SDimitry Andric #endif 360b57cec5SDimitry Andric 37349cc55cSDimitry Andric #if defined(__APPLE__) && defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1050) 380b57cec5SDimitry Andric #define USE_OSX_GETHOSTUUID 1 390b57cec5SDimitry Andric #else 400b57cec5SDimitry Andric #define USE_OSX_GETHOSTUUID 0 410b57cec5SDimitry Andric #endif 420b57cec5SDimitry Andric 430b57cec5SDimitry Andric #if USE_OSX_GETHOSTUUID 440b57cec5SDimitry Andric #include <uuid/uuid.h> 450b57cec5SDimitry Andric #endif 460b57cec5SDimitry Andric 470b57cec5SDimitry Andric using namespace llvm; 480b57cec5SDimitry Andric 490b57cec5SDimitry Andric /// Attempt to read the lock file with the given name, if it exists. 500b57cec5SDimitry Andric /// 510b57cec5SDimitry Andric /// \param LockFileName The name of the lock file to read. 520b57cec5SDimitry Andric /// 530b57cec5SDimitry Andric /// \returns The process ID of the process that owns this lock file 54bdd1243dSDimitry Andric std::optional<std::pair<std::string, int>> 550b57cec5SDimitry Andric LockFileManager::readLockFile(StringRef LockFileName) { 560b57cec5SDimitry Andric // Read the owning host and PID out of the lock file. If it appears that the 570b57cec5SDimitry Andric // owning process is dead, the lock file is invalid. 580b57cec5SDimitry Andric ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr = 590b57cec5SDimitry Andric MemoryBuffer::getFile(LockFileName); 600b57cec5SDimitry Andric if (!MBOrErr) { 610b57cec5SDimitry Andric sys::fs::remove(LockFileName); 62bdd1243dSDimitry Andric return std::nullopt; 630b57cec5SDimitry Andric } 640b57cec5SDimitry Andric MemoryBuffer &MB = *MBOrErr.get(); 650b57cec5SDimitry Andric 660b57cec5SDimitry Andric StringRef Hostname; 670b57cec5SDimitry Andric StringRef PIDStr; 680b57cec5SDimitry Andric std::tie(Hostname, PIDStr) = getToken(MB.getBuffer(), " "); 69*0fca6ea1SDimitry Andric PIDStr = PIDStr.substr(PIDStr.find_first_not_of(' ')); 700b57cec5SDimitry Andric int PID; 710b57cec5SDimitry Andric if (!PIDStr.getAsInteger(10, PID)) { 720b57cec5SDimitry Andric auto Owner = std::make_pair(std::string(Hostname), PID); 730b57cec5SDimitry Andric if (processStillExecuting(Owner.first, Owner.second)) 740b57cec5SDimitry Andric return Owner; 750b57cec5SDimitry Andric } 760b57cec5SDimitry Andric 770b57cec5SDimitry Andric // Delete the lock file. It's invalid anyway. 780b57cec5SDimitry Andric sys::fs::remove(LockFileName); 79bdd1243dSDimitry Andric return std::nullopt; 800b57cec5SDimitry Andric } 810b57cec5SDimitry Andric 820b57cec5SDimitry Andric static std::error_code getHostID(SmallVectorImpl<char> &HostID) { 830b57cec5SDimitry Andric HostID.clear(); 840b57cec5SDimitry Andric 850b57cec5SDimitry Andric #if USE_OSX_GETHOSTUUID 860b57cec5SDimitry Andric // On OS X, use the more stable hardware UUID instead of hostname. 870b57cec5SDimitry Andric struct timespec wait = {1, 0}; // 1 second. 880b57cec5SDimitry Andric uuid_t uuid; 890b57cec5SDimitry Andric if (gethostuuid(uuid, &wait) != 0) 90*0fca6ea1SDimitry Andric return errnoAsErrorCode(); 910b57cec5SDimitry Andric 920b57cec5SDimitry Andric uuid_string_t UUIDStr; 930b57cec5SDimitry Andric uuid_unparse(uuid, UUIDStr); 940b57cec5SDimitry Andric StringRef UUIDRef(UUIDStr); 950b57cec5SDimitry Andric HostID.append(UUIDRef.begin(), UUIDRef.end()); 960b57cec5SDimitry Andric 970b57cec5SDimitry Andric #elif LLVM_ON_UNIX 980b57cec5SDimitry Andric char HostName[256]; 990b57cec5SDimitry Andric HostName[255] = 0; 1000b57cec5SDimitry Andric HostName[0] = 0; 1010b57cec5SDimitry Andric gethostname(HostName, 255); 1020b57cec5SDimitry Andric StringRef HostNameRef(HostName); 1030b57cec5SDimitry Andric HostID.append(HostNameRef.begin(), HostNameRef.end()); 1040b57cec5SDimitry Andric 1050b57cec5SDimitry Andric #else 1060b57cec5SDimitry Andric StringRef Dummy("localhost"); 1070b57cec5SDimitry Andric HostID.append(Dummy.begin(), Dummy.end()); 1080b57cec5SDimitry Andric #endif 1090b57cec5SDimitry Andric 1100b57cec5SDimitry Andric return std::error_code(); 1110b57cec5SDimitry Andric } 1120b57cec5SDimitry Andric 1130b57cec5SDimitry Andric bool LockFileManager::processStillExecuting(StringRef HostID, int PID) { 1140b57cec5SDimitry Andric #if LLVM_ON_UNIX && !defined(__ANDROID__) 1150b57cec5SDimitry Andric SmallString<256> StoredHostID; 1160b57cec5SDimitry Andric if (getHostID(StoredHostID)) 1170b57cec5SDimitry Andric return true; // Conservatively assume it's executing on error. 1180b57cec5SDimitry Andric 1190b57cec5SDimitry Andric // Check whether the process is dead. If so, we're done. 1200b57cec5SDimitry Andric if (StoredHostID == HostID && getsid(PID) == -1 && errno == ESRCH) 1210b57cec5SDimitry Andric return false; 1220b57cec5SDimitry Andric #endif 1230b57cec5SDimitry Andric 1240b57cec5SDimitry Andric return true; 1250b57cec5SDimitry Andric } 1260b57cec5SDimitry Andric 1270b57cec5SDimitry Andric namespace { 1280b57cec5SDimitry Andric 1290b57cec5SDimitry Andric /// An RAII helper object ensure that the unique lock file is removed. 1300b57cec5SDimitry Andric /// 1310b57cec5SDimitry Andric /// Ensures that if there is an error or a signal before we finish acquiring the 1320b57cec5SDimitry Andric /// lock, the unique file will be removed. And if we successfully take the lock, 1330b57cec5SDimitry Andric /// the signal handler is left in place so that signals while the lock is held 1340b57cec5SDimitry Andric /// will remove the unique lock file. The caller should ensure there is a 1350b57cec5SDimitry Andric /// matching call to sys::DontRemoveFileOnSignal when the lock is released. 1360b57cec5SDimitry Andric class RemoveUniqueLockFileOnSignal { 1370b57cec5SDimitry Andric StringRef Filename; 1380b57cec5SDimitry Andric bool RemoveImmediately; 1390b57cec5SDimitry Andric public: 1400b57cec5SDimitry Andric RemoveUniqueLockFileOnSignal(StringRef Name) 1410b57cec5SDimitry Andric : Filename(Name), RemoveImmediately(true) { 1420b57cec5SDimitry Andric sys::RemoveFileOnSignal(Filename, nullptr); 1430b57cec5SDimitry Andric } 1440b57cec5SDimitry Andric 1450b57cec5SDimitry Andric ~RemoveUniqueLockFileOnSignal() { 1460b57cec5SDimitry Andric if (!RemoveImmediately) { 1470b57cec5SDimitry Andric // Leave the signal handler enabled. It will be removed when the lock is 1480b57cec5SDimitry Andric // released. 1490b57cec5SDimitry Andric return; 1500b57cec5SDimitry Andric } 1510b57cec5SDimitry Andric sys::fs::remove(Filename); 1520b57cec5SDimitry Andric sys::DontRemoveFileOnSignal(Filename); 1530b57cec5SDimitry Andric } 1540b57cec5SDimitry Andric 1550b57cec5SDimitry Andric void lockAcquired() { RemoveImmediately = false; } 1560b57cec5SDimitry Andric }; 1570b57cec5SDimitry Andric 1580b57cec5SDimitry Andric } // end anonymous namespace 1590b57cec5SDimitry Andric 1600b57cec5SDimitry Andric LockFileManager::LockFileManager(StringRef FileName) 1610b57cec5SDimitry Andric { 1620b57cec5SDimitry Andric this->FileName = FileName; 1630b57cec5SDimitry Andric if (std::error_code EC = sys::fs::make_absolute(this->FileName)) { 1640b57cec5SDimitry Andric std::string S("failed to obtain absolute path for "); 1657a6dacacSDimitry Andric S.append(std::string(this->FileName)); 1660b57cec5SDimitry Andric setError(EC, S); 1670b57cec5SDimitry Andric return; 1680b57cec5SDimitry Andric } 1690b57cec5SDimitry Andric LockFileName = this->FileName; 1700b57cec5SDimitry Andric LockFileName += ".lock"; 1710b57cec5SDimitry Andric 1720b57cec5SDimitry Andric // If the lock file already exists, don't bother to try to create our own 1730b57cec5SDimitry Andric // lock file; it won't work anyway. Just figure out who owns this lock file. 1740b57cec5SDimitry Andric if ((Owner = readLockFile(LockFileName))) 1750b57cec5SDimitry Andric return; 1760b57cec5SDimitry Andric 1770b57cec5SDimitry Andric // Create a lock file that is unique to this instance. 1780b57cec5SDimitry Andric UniqueLockFileName = LockFileName; 1790b57cec5SDimitry Andric UniqueLockFileName += "-%%%%%%%%"; 1800b57cec5SDimitry Andric int UniqueLockFileID; 1810b57cec5SDimitry Andric if (std::error_code EC = sys::fs::createUniqueFile( 1820b57cec5SDimitry Andric UniqueLockFileName, UniqueLockFileID, UniqueLockFileName)) { 1830b57cec5SDimitry Andric std::string S("failed to create unique file "); 1847a6dacacSDimitry Andric S.append(std::string(UniqueLockFileName)); 1850b57cec5SDimitry Andric setError(EC, S); 1860b57cec5SDimitry Andric return; 1870b57cec5SDimitry Andric } 1880b57cec5SDimitry Andric 1890b57cec5SDimitry Andric // Write our process ID to our unique lock file. 1900b57cec5SDimitry Andric { 1910b57cec5SDimitry Andric SmallString<256> HostID; 1920b57cec5SDimitry Andric if (auto EC = getHostID(HostID)) { 1930b57cec5SDimitry Andric setError(EC, "failed to get host id"); 1940b57cec5SDimitry Andric return; 1950b57cec5SDimitry Andric } 1960b57cec5SDimitry Andric 1970b57cec5SDimitry Andric raw_fd_ostream Out(UniqueLockFileID, /*shouldClose=*/true); 1985ffd83dbSDimitry Andric Out << HostID << ' ' << sys::Process::getProcessId(); 1990b57cec5SDimitry Andric Out.close(); 2000b57cec5SDimitry Andric 2010b57cec5SDimitry Andric if (Out.has_error()) { 2020b57cec5SDimitry Andric // We failed to write out PID, so report the error, remove the 2030b57cec5SDimitry Andric // unique lock file, and fail. 2040b57cec5SDimitry Andric std::string S("failed to write to "); 2057a6dacacSDimitry Andric S.append(std::string(UniqueLockFileName)); 2060b57cec5SDimitry Andric setError(Out.error(), S); 2070b57cec5SDimitry Andric sys::fs::remove(UniqueLockFileName); 208*0fca6ea1SDimitry Andric // Don't call report_fatal_error. 209*0fca6ea1SDimitry Andric Out.clear_error(); 2100b57cec5SDimitry Andric return; 2110b57cec5SDimitry Andric } 2120b57cec5SDimitry Andric } 2130b57cec5SDimitry Andric 2140b57cec5SDimitry Andric // Clean up the unique file on signal, which also releases the lock if it is 2150b57cec5SDimitry Andric // held since the .lock symlink will point to a nonexistent file. 2160b57cec5SDimitry Andric RemoveUniqueLockFileOnSignal RemoveUniqueFile(UniqueLockFileName); 2170b57cec5SDimitry Andric 2180b57cec5SDimitry Andric while (true) { 2190b57cec5SDimitry Andric // Create a link from the lock file name. If this succeeds, we're done. 2200b57cec5SDimitry Andric std::error_code EC = 2210b57cec5SDimitry Andric sys::fs::create_link(UniqueLockFileName, LockFileName); 2220b57cec5SDimitry Andric if (!EC) { 2230b57cec5SDimitry Andric RemoveUniqueFile.lockAcquired(); 2240b57cec5SDimitry Andric return; 2250b57cec5SDimitry Andric } 2260b57cec5SDimitry Andric 2270b57cec5SDimitry Andric if (EC != errc::file_exists) { 2280b57cec5SDimitry Andric std::string S("failed to create link "); 2290b57cec5SDimitry Andric raw_string_ostream OSS(S); 2300b57cec5SDimitry Andric OSS << LockFileName.str() << " to " << UniqueLockFileName.str(); 231*0fca6ea1SDimitry Andric setError(EC, S); 2320b57cec5SDimitry Andric return; 2330b57cec5SDimitry Andric } 2340b57cec5SDimitry Andric 2350b57cec5SDimitry Andric // Someone else managed to create the lock file first. Read the process ID 2360b57cec5SDimitry Andric // from the lock file. 2370b57cec5SDimitry Andric if ((Owner = readLockFile(LockFileName))) { 2380b57cec5SDimitry Andric // Wipe out our unique lock file (it's useless now) 2390b57cec5SDimitry Andric sys::fs::remove(UniqueLockFileName); 2400b57cec5SDimitry Andric return; 2410b57cec5SDimitry Andric } 2420b57cec5SDimitry Andric 2430b57cec5SDimitry Andric if (!sys::fs::exists(LockFileName)) { 2440b57cec5SDimitry Andric // The previous owner released the lock file before we could read it. 2450b57cec5SDimitry Andric // Try to get ownership again. 2460b57cec5SDimitry Andric continue; 2470b57cec5SDimitry Andric } 2480b57cec5SDimitry Andric 2490b57cec5SDimitry Andric // There is a lock file that nobody owns; try to clean it up and get 2500b57cec5SDimitry Andric // ownership. 2510b57cec5SDimitry Andric if ((EC = sys::fs::remove(LockFileName))) { 2520b57cec5SDimitry Andric std::string S("failed to remove lockfile "); 2537a6dacacSDimitry Andric S.append(std::string(UniqueLockFileName)); 2540b57cec5SDimitry Andric setError(EC, S); 2550b57cec5SDimitry Andric return; 2560b57cec5SDimitry Andric } 2570b57cec5SDimitry Andric } 2580b57cec5SDimitry Andric } 2590b57cec5SDimitry Andric 2600b57cec5SDimitry Andric LockFileManager::LockFileState LockFileManager::getState() const { 2610b57cec5SDimitry Andric if (Owner) 2620b57cec5SDimitry Andric return LFS_Shared; 2630b57cec5SDimitry Andric 2640b57cec5SDimitry Andric if (ErrorCode) 2650b57cec5SDimitry Andric return LFS_Error; 2660b57cec5SDimitry Andric 2670b57cec5SDimitry Andric return LFS_Owned; 2680b57cec5SDimitry Andric } 2690b57cec5SDimitry Andric 2700b57cec5SDimitry Andric std::string LockFileManager::getErrorMessage() const { 2710b57cec5SDimitry Andric if (ErrorCode) { 2720b57cec5SDimitry Andric std::string Str(ErrorDiagMsg); 2730b57cec5SDimitry Andric std::string ErrCodeMsg = ErrorCode.message(); 2740b57cec5SDimitry Andric raw_string_ostream OSS(Str); 2750b57cec5SDimitry Andric if (!ErrCodeMsg.empty()) 2760b57cec5SDimitry Andric OSS << ": " << ErrCodeMsg; 277*0fca6ea1SDimitry Andric return Str; 2780b57cec5SDimitry Andric } 2790b57cec5SDimitry Andric return ""; 2800b57cec5SDimitry Andric } 2810b57cec5SDimitry Andric 2820b57cec5SDimitry Andric LockFileManager::~LockFileManager() { 2830b57cec5SDimitry Andric if (getState() != LFS_Owned) 2840b57cec5SDimitry Andric return; 2850b57cec5SDimitry Andric 2860b57cec5SDimitry Andric // Since we own the lock, remove the lock file and our own unique lock file. 2870b57cec5SDimitry Andric sys::fs::remove(LockFileName); 2880b57cec5SDimitry Andric sys::fs::remove(UniqueLockFileName); 2890b57cec5SDimitry Andric // The unique file is now gone, so remove it from the signal handler. This 2900b57cec5SDimitry Andric // matches a sys::RemoveFileOnSignal() in LockFileManager(). 2910b57cec5SDimitry Andric sys::DontRemoveFileOnSignal(UniqueLockFileName); 2920b57cec5SDimitry Andric } 2930b57cec5SDimitry Andric 294480093f4SDimitry Andric LockFileManager::WaitForUnlockResult 295480093f4SDimitry Andric LockFileManager::waitForUnlock(const unsigned MaxSeconds) { 2960b57cec5SDimitry Andric if (getState() != LFS_Shared) 2970b57cec5SDimitry Andric return Res_Success; 2980b57cec5SDimitry Andric 2995ffd83dbSDimitry Andric // Since we don't yet have an event-based method to wait for the lock file, 300*0fca6ea1SDimitry Andric // use randomized exponential backoff, similar to Ethernet collision 3015ffd83dbSDimitry Andric // algorithm. This improves performance on machines with high core counts 3025ffd83dbSDimitry Andric // when the file lock is heavily contended by multiple clang processes 303*0fca6ea1SDimitry Andric using namespace std::chrono_literals; 304*0fca6ea1SDimitry Andric ExponentialBackoff Backoff(std::chrono::seconds(MaxSeconds), 10ms, 500ms); 3055ffd83dbSDimitry Andric 306*0fca6ea1SDimitry Andric // Wait first as this is only called when the lock is known to be held. 307*0fca6ea1SDimitry Andric while (Backoff.waitForNextAttempt()) { 3085ffd83dbSDimitry Andric // FIXME: implement event-based waiting 3090b57cec5SDimitry Andric if (sys::fs::access(LockFileName.c_str(), sys::fs::AccessMode::Exist) == 3100b57cec5SDimitry Andric errc::no_such_file_or_directory) { 3110b57cec5SDimitry Andric // If the original file wasn't created, somone thought the lock was dead. 3120b57cec5SDimitry Andric if (!sys::fs::exists(FileName)) 3130b57cec5SDimitry Andric return Res_OwnerDied; 3140b57cec5SDimitry Andric return Res_Success; 3150b57cec5SDimitry Andric } 3160b57cec5SDimitry Andric 3170b57cec5SDimitry Andric // If the process owning the lock died without cleaning up, just bail out. 3180b57cec5SDimitry Andric if (!processStillExecuting((*Owner).first, (*Owner).second)) 3190b57cec5SDimitry Andric return Res_OwnerDied; 3200b57cec5SDimitry Andric } 3215ffd83dbSDimitry Andric 3220b57cec5SDimitry Andric // Give up. 3230b57cec5SDimitry Andric return Res_Timeout; 3240b57cec5SDimitry Andric } 3250b57cec5SDimitry Andric 3260b57cec5SDimitry Andric std::error_code LockFileManager::unsafeRemoveLockFile() { 3270b57cec5SDimitry Andric return sys::fs::remove(LockFileName); 3280b57cec5SDimitry Andric } 329