[clang] [llvm] [Support] Introduce new `AdvisoryLock` interface (PR #130989)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Mar 12 09:40:07 PDT 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Jan Svoboda (jansvoboda11)
<details>
<summary>Changes</summary>
This PR abstracts the `LockFileManager` API into new `AdvisoryLock` interface. This is so that we can create an alternative implementation for Clang implicitly-built modules that is optimized for single-process environment.
---
Full diff: https://github.com/llvm/llvm-project/pull/130989.diff
5 Files Affected:
- (modified) clang/lib/Frontend/CompilerInstance.cpp (+4-4)
- (added) llvm/include/llvm/Support/AdvisoryLock.h (+54)
- (modified) llvm/include/llvm/Support/LockFileManager.h (+9-24)
- (modified) llvm/lib/Support/LockFileManager.cpp (+5-6)
- (modified) llvm/lib/Target/AMDGPU/AMDGPUSplitModule.cpp (+4-4)
``````````diff
diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp
index 44f4f48ef94e8..ef09cd9cc43d7 100644
--- a/clang/lib/Frontend/CompilerInstance.cpp
+++ b/clang/lib/Frontend/CompilerInstance.cpp
@@ -1503,18 +1503,18 @@ static bool compileModuleAndReadASTBehindLock(
// Someone else is responsible for building the module. Wait for them to
// finish.
switch (Lock.waitForUnlock()) {
- case llvm::LockFileManager::Res_Success:
+ case llvm::WaitForUnlockResult::Success:
break; // The interesting case.
- case llvm::LockFileManager::Res_OwnerDied:
+ case llvm::WaitForUnlockResult::OwnerDied:
continue; // try again to get the lock.
- case llvm::LockFileManager::Res_Timeout:
+ case llvm::WaitForUnlockResult::Timeout:
// Since ModuleCache takes care of correctness, we try waiting for
// another process to complete the build so clang does not do it done
// twice. If case of timeout, build it ourselves.
Diags.Report(ModuleNameLoc, diag::remark_module_lock_timeout)
<< Module->Name;
// Clear the lock file so that future invocations can make progress.
- Lock.unsafeRemoveLockFile();
+ Lock.unsafeUnlockShared();
continue;
}
diff --git a/llvm/include/llvm/Support/AdvisoryLock.h b/llvm/include/llvm/Support/AdvisoryLock.h
new file mode 100644
index 0000000000000..57c993f48f240
--- /dev/null
+++ b/llvm/include/llvm/Support/AdvisoryLock.h
@@ -0,0 +1,54 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_SUPPORT_ADVISORYLOCK_H
+#define LLVM_SUPPORT_ADVISORYLOCK_H
+
+#include "llvm/Support/Error.h"
+
+namespace llvm {
+/// Describes the result of waiting for the owner to release the lock.
+enum class WaitForUnlockResult {
+ /// The lock was released successfully.
+ Success,
+ /// Owner died while holding the lock.
+ OwnerDied,
+ /// Reached timeout while waiting for the owner to release the lock.
+ Timeout,
+};
+
+/// A synchronization primitive with weak mutual exclusion guarantees.
+/// Implementations of this interface may allow multiple threads/processes to
+/// acquire the lock simultaneously. Typically, threads/processes waiting for
+/// the lock to be unlocked will validate the computation produced valid result.
+class AdvisoryLock {
+public:
+ /// Tries to acquire the lock without blocking.
+ ///
+ /// \returns true if the lock was successfully acquired (owned lock), false if
+ /// the lock is already held by someone else (shared lock), or \c Error in
+ /// case of unexpected failure.
+ virtual Expected<bool> tryLock() = 0;
+
+ /// For a shared lock, wait until the owner releases the lock.
+ ///
+ /// \param MaxSeconds the maximum total wait time in seconds.
+ virtual WaitForUnlockResult waitForUnlockFor(unsigned MaxSeconds) = 0;
+ WaitForUnlockResult waitForUnlock() { return waitForUnlockFor(90); }
+
+ /// Unlocks a shared lock. This may allow another thread/process to acquire
+ /// the lock before the existing owner released it and notify waiting
+ /// threads/processes. This is an unsafe operation.
+ virtual std::error_code unsafeUnlockShared() = 0;
+
+ /// Unlocks the lock if previously acquired by \c tryLock().
+ virtual ~AdvisoryLock() = default;
+};
+} // end namespace llvm
+
+#endif
diff --git a/llvm/include/llvm/Support/LockFileManager.h b/llvm/include/llvm/Support/LockFileManager.h
index cf02b41a6f729..1653a7416f667 100644
--- a/llvm/include/llvm/Support/LockFileManager.h
+++ b/llvm/include/llvm/Support/LockFileManager.h
@@ -9,15 +9,13 @@
#define LLVM_SUPPORT_LOCKFILEMANAGER_H
#include "llvm/ADT/SmallString.h"
-#include "llvm/Support/Error.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/AdvisoryLock.h"
#include <optional>
#include <string>
-#include <system_error>
#include <variant>
namespace llvm {
-class StringRef;
-
/// Class that manages the creation of a lock file to aid implicit coordination
/// between different processes.
///
@@ -25,19 +23,7 @@ class StringRef;
/// atomicity of the file system to ensure that only a single process can create
/// that ".lock" file. When the lock file is removed, the owning process has
/// finished the operation.
-class LockFileManager {
-public:
- /// Describes the result of waiting for the owner to release the lock.
- enum WaitForUnlockResult {
- /// The lock was released successfully.
- Res_Success,
- /// Owner died while holding the lock.
- Res_OwnerDied,
- /// Reached timeout while waiting for the owner to release the lock.
- Res_Timeout
- };
-
-private:
+class LockFileManager : public AdvisoryLock {
SmallString<128> FileName;
SmallString<128> LockFileName;
SmallString<128> UniqueLockFileName;
@@ -61,24 +47,23 @@ class LockFileManager {
/// Does not try to acquire the lock.
LockFileManager(StringRef FileName);
- /// Unlocks the lock if previously acquired by \c tryLock().
- ~LockFileManager();
-
/// Tries to acquire the lock without blocking.
/// \returns true if the lock was successfully acquired, false if the lock is
/// already held by someone else, or \c Error in case of unexpected failure.
- Expected<bool> tryLock();
+ Expected<bool> tryLock() override;
/// For a shared lock, wait until the owner releases the lock.
/// Total timeout for the file to appear is ~1.5 minutes.
/// \param MaxSeconds the maximum total wait time in seconds.
- WaitForUnlockResult waitForUnlock(const unsigned MaxSeconds = 90);
+ WaitForUnlockResult waitForUnlockFor(unsigned MaxSeconds) override;
/// Remove the lock file. This may delete a different lock file than
/// the one previously read if there is a race.
- std::error_code unsafeRemoveLockFile();
-};
+ std::error_code unsafeUnlockShared() override;
+ /// Unlocks the lock if previously acquired by \c tryLock().
+ ~LockFileManager() override;
+};
} // end namespace llvm
#endif // LLVM_SUPPORT_LOCKFILEMANAGER_H
diff --git a/llvm/lib/Support/LockFileManager.cpp b/llvm/lib/Support/LockFileManager.cpp
index 7cf9db379974f..1d507d8e3118a 100644
--- a/llvm/lib/Support/LockFileManager.cpp
+++ b/llvm/lib/Support/LockFileManager.cpp
@@ -264,8 +264,7 @@ LockFileManager::~LockFileManager() {
sys::DontRemoveFileOnSignal(UniqueLockFileName);
}
-LockFileManager::WaitForUnlockResult
-LockFileManager::waitForUnlock(const unsigned MaxSeconds) {
+WaitForUnlockResult LockFileManager::waitForUnlockFor(unsigned MaxSeconds) {
auto *LockFileOwner = std::get_if<OwnedByAnother>(&Owner);
assert(LockFileOwner &&
"waiting for lock to be unlocked without knowing the owner");
@@ -282,18 +281,18 @@ LockFileManager::waitForUnlock(const unsigned MaxSeconds) {
// FIXME: implement event-based waiting
if (sys::fs::access(LockFileName.c_str(), sys::fs::AccessMode::Exist) ==
errc::no_such_file_or_directory)
- return Res_Success;
+ return WaitForUnlockResult::Success;
// If the process owning the lock died without cleaning up, just bail out.
if (!processStillExecuting(LockFileOwner->OwnerHostName,
LockFileOwner->OwnerPID))
- return Res_OwnerDied;
+ return WaitForUnlockResult::OwnerDied;
}
// Give up.
- return Res_Timeout;
+ return WaitForUnlockResult::Timeout;
}
-std::error_code LockFileManager::unsafeRemoveLockFile() {
+std::error_code LockFileManager::unsafeUnlockShared() {
return sys::fs::remove(LockFileName);
}
diff --git a/llvm/lib/Target/AMDGPU/AMDGPUSplitModule.cpp b/llvm/lib/Target/AMDGPU/AMDGPUSplitModule.cpp
index e2e449f1c8a38..1e381c9987ced 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUSplitModule.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPUSplitModule.cpp
@@ -1554,16 +1554,16 @@ PreservedAnalyses AMDGPUSplitModulePass::run(Module &M,
"output may be mangled by other processes\n");
} else if (!Owned) {
switch (Lock.waitForUnlock()) {
- case LockFileManager::Res_Success:
+ case WaitForUnlockResult::Success:
break;
- case LockFileManager::Res_OwnerDied:
+ case WaitForUnlockResult::OwnerDied:
continue; // try again to get the lock.
- case LockFileManager::Res_Timeout:
+ case WaitForUnlockResult::Timeout:
LLVM_DEBUG(
dbgs()
<< "[amdgpu-split-module] unable to acquire lockfile, debug "
"output may be mangled by other processes\n");
- Lock.unsafeRemoveLockFile();
+ Lock.unsafeUnlockShared();
break; // give up
}
}
``````````
</details>
https://github.com/llvm/llvm-project/pull/130989
More information about the cfe-commits
mailing list