[clang] [clang][modules][deps] Add mutex as an alternative to file lock (PR #129751)
Jan Svoboda via cfe-commits
cfe-commits at lists.llvm.org
Tue Mar 18 14:00:19 PDT 2025
https://github.com/jansvoboda11 updated https://github.com/llvm/llvm-project/pull/129751
>From 7dadb10f4e63e419a230270b5f26a145d7015ae7 Mon Sep 17 00:00:00 2001
From: Jan Svoboda <jan_svoboda at apple.com>
Date: Wed, 12 Mar 2025 10:20:16 -0700
Subject: [PATCH 1/2] [clang][deps] Implement efficient in-process
`ModuleCache`
---
.../DependencyScanningService.h | 5 ++
.../DependencyScanning/InProcessModuleCache.h | 31 +++++++
.../Tooling/DependencyScanning/CMakeLists.txt | 1 +
.../DependencyScanningWorker.cpp | 5 +-
.../InProcessModuleCache.cpp | 87 +++++++++++++++++++
5 files changed, 128 insertions(+), 1 deletion(-)
create mode 100644 clang/include/clang/Tooling/DependencyScanning/InProcessModuleCache.h
create mode 100644 clang/lib/Tooling/DependencyScanning/InProcessModuleCache.cpp
diff --git a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningService.h b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningService.h
index 816e122eb3003..996a26716eea2 100644
--- a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningService.h
+++ b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningService.h
@@ -10,6 +10,7 @@
#define LLVM_CLANG_TOOLING_DEPENDENCYSCANNING_DEPENDENCYSCANNINGSERVICE_H
#include "clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h"
+#include "clang/Tooling/DependencyScanning/InProcessModuleCache.h"
#include "llvm/ADT/BitmaskEnum.h"
namespace clang {
@@ -99,6 +100,8 @@ class DependencyScanningService {
return SharedCache;
}
+ ModuleCacheMutexes &getModuleCacheMutexes() { return ModuleCacheMutexes; }
+
private:
const ScanningMode Mode;
const ScanningOutputFormat Format;
@@ -110,6 +113,8 @@ class DependencyScanningService {
const bool TraceVFS;
/// The global file system cache.
DependencyScanningFilesystemSharedCache SharedCache;
+ /// The global module cache mutexes.
+ ModuleCacheMutexes ModuleCacheMutexes;
};
} // end namespace dependencies
diff --git a/clang/include/clang/Tooling/DependencyScanning/InProcessModuleCache.h b/clang/include/clang/Tooling/DependencyScanning/InProcessModuleCache.h
new file mode 100644
index 0000000000000..a22647584d8c5
--- /dev/null
+++ b/clang/include/clang/Tooling/DependencyScanning/InProcessModuleCache.h
@@ -0,0 +1,31 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_CLANG_TOOLING_DEPENDENCYSCANNING_INPROCESSMODULECACHE_H
+#define LLVM_CLANG_TOOLING_DEPENDENCYSCANNING_INPROCESSMODULECACHE_H
+
+#include "clang/Serialization/ModuleCache.h"
+#include "llvm/ADT/StringMap.h"
+
+#include <shared_mutex>
+
+namespace clang {
+namespace tooling {
+namespace dependencies {
+struct ModuleCacheMutexes {
+ std::mutex Mutex;
+ llvm::StringMap<std::unique_ptr<std::shared_mutex>> Map;
+};
+
+IntrusiveRefCntPtr<ModuleCache>
+makeInProcessModuleCache(ModuleCacheMutexes &Mutexes);
+} // namespace dependencies
+} // namespace tooling
+} // namespace clang
+
+#endif
diff --git a/clang/lib/Tooling/DependencyScanning/CMakeLists.txt b/clang/lib/Tooling/DependencyScanning/CMakeLists.txt
index 6b500a183bcfc..993dc093a781c 100644
--- a/clang/lib/Tooling/DependencyScanning/CMakeLists.txt
+++ b/clang/lib/Tooling/DependencyScanning/CMakeLists.txt
@@ -10,6 +10,7 @@ add_clang_library(clangDependencyScanning
DependencyScanningService.cpp
DependencyScanningWorker.cpp
DependencyScanningTool.cpp
+ InProcessModuleCache.cpp
ModuleDepCollector.cpp
DEPENDS
diff --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
index 697f26ee5d12f..ca15a088c308d 100644
--- a/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
+++ b/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
@@ -22,6 +22,7 @@
#include "clang/Lex/PreprocessorOptions.h"
#include "clang/Serialization/ObjectFilePCHContainerReader.h"
#include "clang/Tooling/DependencyScanning/DependencyScanningService.h"
+#include "clang/Tooling/DependencyScanning/InProcessModuleCache.h"
#include "clang/Tooling/DependencyScanning/ModuleDepCollector.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/ADT/IntrusiveRefCntPtr.h"
@@ -315,9 +316,11 @@ class DependencyScanningAction : public tooling::ToolAction {
Scanned = true;
// Create a compiler instance to handle the actual work.
- ScanInstanceStorage.emplace(std::move(PCHContainerOps));
+ auto ModCache = makeInProcessModuleCache(Service.getModuleCacheMutexes());
+ ScanInstanceStorage.emplace(std::move(PCHContainerOps), ModCache.get());
CompilerInstance &ScanInstance = *ScanInstanceStorage;
ScanInstance.setInvocation(std::move(Invocation));
+ ScanInstance.setBuildingModule(false);
// Create the compiler's actual diagnostics engine.
sanitizeDiagOpts(ScanInstance.getDiagnosticOpts());
diff --git a/clang/lib/Tooling/DependencyScanning/InProcessModuleCache.cpp b/clang/lib/Tooling/DependencyScanning/InProcessModuleCache.cpp
new file mode 100644
index 0000000000000..079e00c38b140
--- /dev/null
+++ b/clang/lib/Tooling/DependencyScanning/InProcessModuleCache.cpp
@@ -0,0 +1,87 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/DependencyScanning/InProcessModuleCache.h"
+
+#include "clang/Serialization/InMemoryModuleCache.h"
+#include "llvm/Support/AdvisoryLock.h"
+
+#include <mutex>
+
+using namespace clang;
+using namespace tooling;
+using namespace dependencies;
+
+namespace {
+class ReaderWriterLock : public llvm::AdvisoryLock {
+ // TODO: Consider using std::atomic::{wait,notify_all} when we move to C++20.
+ std::unique_lock<std::shared_mutex> OwningLock;
+
+public:
+ ReaderWriterLock(std::shared_mutex &Mutex)
+ : OwningLock(Mutex, std::defer_lock) {}
+
+ Expected<bool> tryLock() override { return OwningLock.try_lock(); }
+
+ llvm::WaitForUnlockResult
+ waitForUnlockFor(std::chrono::seconds MaxSeconds) override {
+ assert(!OwningLock);
+ // We do not respect the timeout here. It's very generous for implicit
+ // modules, so we'd typically only reach it if the owner crashed (but so did
+ // we, since we run in the same process), or encountered deadlock.
+ (void)MaxSeconds;
+ std::shared_lock Lock(*OwningLock.mutex());
+ return llvm::WaitForUnlockResult::Success;
+ }
+
+ std::error_code unsafeMaybeUnlock() override {
+ // Unlocking the mutex here would trigger UB and we don't expect this to be
+ // actually called when compiling scanning modules due to the no-timeout
+ // guarantee above.
+ return {};
+ }
+
+ ~ReaderWriterLock() override = default;
+};
+
+class InProcessModuleCache : public ModuleCache {
+ ModuleCacheMutexes &Mutexes;
+
+ // TODO: If we changed the InMemoryModuleCache API and relied on strict
+ // context hash, we could probably create more efficient thread-safe
+ // implementation of the InMemoryModuleCache such that it does need to be
+ // recreated for each translation unit.
+ InMemoryModuleCache InMemory;
+
+public:
+ InProcessModuleCache(ModuleCacheMutexes &Mutexes) : Mutexes(Mutexes) {}
+
+ void prepareForGetLock(StringRef Filename) override {}
+
+ std::unique_ptr<llvm::AdvisoryLock> getLock(StringRef Filename) override {
+ auto &Mtx = [&]() -> std::shared_mutex & {
+ std::lock_guard Lock(Mutexes.Mutex);
+ auto &Mutex = Mutexes.Map[Filename];
+ if (!Mutex)
+ Mutex = std::make_unique<std::shared_mutex>();
+ return *Mutex;
+ }();
+ return std::make_unique<ReaderWriterLock>(Mtx);
+ }
+
+ InMemoryModuleCache &getInMemoryModuleCache() override { return InMemory; }
+ const InMemoryModuleCache &getInMemoryModuleCache() const override {
+ return InMemory;
+ }
+};
+} // namespace
+
+IntrusiveRefCntPtr<ModuleCache>
+dependencies::makeInProcessModuleCache(ModuleCacheMutexes &Mutexes) {
+ return llvm::makeIntrusiveRefCnt<InProcessModuleCache>(Mutexes);
+}
>From 1eae01673bca40670abd80bc2aa2166541453a32 Mon Sep 17 00:00:00 2001
From: Jan Svoboda <jan_svoboda at apple.com>
Date: Tue, 18 Mar 2025 14:00:05 -0700
Subject: [PATCH 2/2] does -> doesn't
---
clang/lib/Tooling/DependencyScanning/InProcessModuleCache.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/Tooling/DependencyScanning/InProcessModuleCache.cpp b/clang/lib/Tooling/DependencyScanning/InProcessModuleCache.cpp
index 079e00c38b140..0ffe20ae52bcd 100644
--- a/clang/lib/Tooling/DependencyScanning/InProcessModuleCache.cpp
+++ b/clang/lib/Tooling/DependencyScanning/InProcessModuleCache.cpp
@@ -54,7 +54,7 @@ class InProcessModuleCache : public ModuleCache {
// TODO: If we changed the InMemoryModuleCache API and relied on strict
// context hash, we could probably create more efficient thread-safe
- // implementation of the InMemoryModuleCache such that it does need to be
+ // implementation of the InMemoryModuleCache such that it doesn't need to be
// recreated for each translation unit.
InMemoryModuleCache InMemory;
More information about the cfe-commits
mailing list