llvm-project/clang/lib/DependencyScanning/InProcessModuleCache.cpp
Naveen Seth Hanig b70be3dc14
[clang][DependencyScanning] Separate clangDependencyScanning and DependencyScanningTool (NFC) (#169962)
This patch is the first of two in refactoring Clang's dependency
scanning tooling to remove its dependency on clangDriver.

It separates Tooling/DependencyScanningTool.cpp from the rest of
clangDependencyScanning and moves clangDependencyScanning out of
clangTooling into its own library. No functional changes are
introduced.

The follow-up patch (#169964) will restrict clangDependencyScanning to
handling only -cc1 command line inputs and will move all functionality
related to handling driver commands into clangTooling.
(Tooling/DependencyScanningTool.cpp).

This is part of a broader effort to support driver-managed builds for
compilations using C++ named modules and/or Clang modules. It is
required for linking the dependency scanning tooling against the driver
without introducing cyclic dependencies, which would otherwise cause
build failures when dynamic linking is enabled.

The RFC for this change can be found here:

https://discourse.llvm.org/t/rfc-new-clangoptions-library-remove-dependency-on-clangdriver-from-clangfrontend-and-flangfrontend/88773?u=naveen-seth
2025-12-04 00:38:21 +01:00

120 lines
4.1 KiB
C++

//===- InProcessModuleCache.cpp - Implicit Module Cache ---------*- C++ -*-===//
//
// 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/DependencyScanning/InProcessModuleCache.h"
#include "clang/Serialization/InMemoryModuleCache.h"
#include "llvm/Support/AdvisoryLock.h"
#include "llvm/Support/Chrono.h"
#include <mutex>
using namespace clang;
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<std::shared_mutex> 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 {
ModuleCacheEntries &Entries;
// 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 doesn't need to be
// recreated for each translation unit.
InMemoryModuleCache InMemory;
public:
InProcessModuleCache(ModuleCacheEntries &Entries) : Entries(Entries) {}
void prepareForGetLock(StringRef Filename) override {}
std::unique_ptr<llvm::AdvisoryLock> getLock(StringRef Filename) override {
auto &CompilationMutex = [&]() -> std::shared_mutex & {
std::lock_guard<std::mutex> Lock(Entries.Mutex);
auto &Entry = Entries.Map[Filename];
if (!Entry)
Entry = std::make_unique<ModuleCacheEntry>();
return Entry->CompilationMutex;
}();
return std::make_unique<ReaderWriterLock>(CompilationMutex);
}
std::time_t getModuleTimestamp(StringRef Filename) override {
auto &Timestamp = [&]() -> std::atomic<std::time_t> & {
std::lock_guard<std::mutex> Lock(Entries.Mutex);
auto &Entry = Entries.Map[Filename];
if (!Entry)
Entry = std::make_unique<ModuleCacheEntry>();
return Entry->Timestamp;
}();
return Timestamp.load();
}
void updateModuleTimestamp(StringRef Filename) override {
// Note: This essentially replaces FS contention with mutex contention.
auto &Timestamp = [&]() -> std::atomic<std::time_t> & {
std::lock_guard<std::mutex> Lock(Entries.Mutex);
auto &Entry = Entries.Map[Filename];
if (!Entry)
Entry = std::make_unique<ModuleCacheEntry>();
return Entry->Timestamp;
}();
Timestamp.store(llvm::sys::toTimeT(std::chrono::system_clock::now()));
}
void maybePrune(StringRef Path, time_t PruneInterval,
time_t PruneAfter) override {
// FIXME: This only needs to be ran once per build, not in every
// compilation. Call it once per service.
maybePruneImpl(Path, PruneInterval, PruneAfter);
}
InMemoryModuleCache &getInMemoryModuleCache() override { return InMemory; }
const InMemoryModuleCache &getInMemoryModuleCache() const override {
return InMemory;
}
};
} // namespace
IntrusiveRefCntPtr<ModuleCache>
dependencies::makeInProcessModuleCache(ModuleCacheEntries &Entries) {
return llvm::makeIntrusiveRefCnt<InProcessModuleCache>(Entries);
}