diff --git a/clang/include/clang/Serialization/ModuleCache.h b/clang/include/clang/Serialization/ModuleCache.h index a7ba26bc4daae..3117d954a09cc 100644 --- a/clang/include/clang/Serialization/ModuleCache.h +++ b/clang/include/clang/Serialization/ModuleCache.h @@ -12,6 +12,8 @@ #include "clang/Basic/LLVM.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" +#include + namespace llvm { class AdvisoryLock; } // namespace llvm @@ -31,11 +33,23 @@ class ModuleCache : public RefCountedBase { virtual std::unique_ptr getLock(StringRef ModuleFilename) = 0; + // TODO: Abstract away timestamps with isUpToDate() and markUpToDate(). + // TODO: Consider exposing a "validation lock" API to prevent multiple clients + // concurrently noticing an out-of-date module file and validating its inputs. + + /// Returns the timestamp denoting the last time inputs of the module file + /// were validated. + virtual std::time_t getModuleTimestamp(StringRef ModuleFilename) = 0; + + /// Updates the timestamp denoting the last time inputs of the module file + /// were validated. + virtual void updateModuleTimestamp(StringRef ModuleFilename) = 0; + /// Returns this process's view of the module cache. virtual InMemoryModuleCache &getInMemoryModuleCache() = 0; virtual const InMemoryModuleCache &getInMemoryModuleCache() const = 0; - // TODO: Virtualize writing/reading PCM files, timestamping, pruning, etc. + // TODO: Virtualize writing/reading PCM files, pruning, etc. virtual ~ModuleCache() = default; }; diff --git a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningService.h b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningService.h index acbc7712c6537..811c184b35a6b 100644 --- a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningService.h +++ b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningService.h @@ -15,6 +15,7 @@ #include "clang/Tooling/DependencyScanning/InProcessModuleCache.h" #include "llvm/ADT/BitmaskEnum.h" #include "llvm/CAS/ActionCache.h" +#include "llvm/Support/Chrono.h" namespace clang { namespace tooling { @@ -104,7 +105,9 @@ class DependencyScanningService { std::shared_ptr Cache, IntrusiveRefCntPtr SharedFS, ScanningOptimizations OptimizeArgs = ScanningOptimizations::Default, - bool EagerLoadModules = false, bool TraceVFS = false); + bool EagerLoadModules = false, bool TraceVFS = false, + std::time_t BuildSessionTimestamp = + llvm::sys::toTimeT(std::chrono::system_clock::now())); ScanningMode getMode() const { return Mode; } @@ -131,7 +134,9 @@ class DependencyScanningService { bool useCASFS() const { return (bool)SharedFS; } - ModuleCacheMutexes &getModuleCacheMutexes() { return ModCacheMutexes; } + ModuleCacheEntries &getModuleCacheEntries() { return ModCacheEntries; } + + std::time_t getBuildSessionTimestamp() const { return BuildSessionTimestamp; } private: const ScanningMode Mode; @@ -150,8 +155,10 @@ class DependencyScanningService { IntrusiveRefCntPtr SharedFS; /// The global file system cache. std::optional SharedCache; - /// The global module cache mutexes. - ModuleCacheMutexes ModCacheMutexes; + /// The global module cache entries. + ModuleCacheEntries ModCacheEntries; + /// The build session timestamp. + std::time_t BuildSessionTimestamp; }; } // end namespace dependencies diff --git a/clang/include/clang/Tooling/DependencyScanning/InProcessModuleCache.h b/clang/include/clang/Tooling/DependencyScanning/InProcessModuleCache.h index ba0454380b665..213e60b39c199 100644 --- a/clang/include/clang/Tooling/DependencyScanning/InProcessModuleCache.h +++ b/clang/include/clang/Tooling/DependencyScanning/InProcessModuleCache.h @@ -18,13 +18,18 @@ namespace clang { namespace tooling { namespace dependencies { -struct ModuleCacheMutexes { +struct ModuleCacheEntry { + std::shared_mutex CompilationMutex; + std::atomic Timestamp = 0; +}; + +struct ModuleCacheEntries { std::mutex Mutex; - llvm::StringMap> Map; + llvm::StringMap> Map; }; IntrusiveRefCntPtr -makeInProcessModuleCache(ModuleCacheMutexes &Mutexes); +makeInProcessModuleCache(ModuleCacheEntries &Entries); } // namespace dependencies } // namespace tooling } // namespace clang diff --git a/clang/lib/Serialization/ASTCommon.cpp b/clang/lib/Serialization/ASTCommon.cpp index 2a1e6974553a5..19d82c72be569 100644 --- a/clang/lib/Serialization/ASTCommon.cpp +++ b/clang/lib/Serialization/ASTCommon.cpp @@ -501,15 +501,3 @@ bool serialization::needsAnonymousDeclarationNumber(const NamedDecl *D) { return false; return isa(D); } - -void serialization::updateModuleTimestamp(StringRef ModuleFilename) { - // Overwrite the timestamp file contents so that file's mtime changes. - std::error_code EC; - llvm::raw_fd_ostream OS(ModuleFile::getTimestampFilename(ModuleFilename), EC, - llvm::sys::fs::OF_TextWithCRLF); - if (EC) - return; - OS << "Timestamp file\n"; - OS.close(); - OS.clear_error(); // Avoid triggering a fatal error. -} diff --git a/clang/lib/Serialization/ASTCommon.h b/clang/lib/Serialization/ASTCommon.h index 2a765eafe0895..2ba8cbb6cd26e 100644 --- a/clang/lib/Serialization/ASTCommon.h +++ b/clang/lib/Serialization/ASTCommon.h @@ -101,8 +101,6 @@ inline bool isPartOfPerModuleInitializer(const Decl *D) { return false; } -void updateModuleTimestamp(StringRef ModuleFilename); - } // namespace serialization } // namespace clang diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index bf4cdf0983432..8a04e70f7debf 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -4752,7 +4752,8 @@ ASTReader::ASTReadResult ASTReader::ReadAST(StringRef FileName, ModuleKind Type, ImportedModule &M = Loaded[I]; if (M.Mod->Kind == MK_ImplicitModule && M.Mod->InputFilesValidationTimestamp < HSOpts.BuildSessionTimestamp) - updateModuleTimestamp(M.Mod->FileName); + getModuleManager().getModuleCache().updateModuleTimestamp( + M.Mod->FileName); } } diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index c70a29cb505e3..0d69dc4bf9cbe 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -4995,7 +4995,7 @@ ASTWriter::WriteAST(llvm::PointerUnion Subject, if (WritingModule && PPRef.getHeaderSearchInfo() .getHeaderSearchOpts() .ModulesValidateOncePerBuildSession) - updateModuleTimestamp(OutputFile); + ModCache.updateModuleTimestamp(OutputFile); if (ShouldCacheASTInMemory) { // Construct MemoryBuffer and update buffer manager. diff --git a/clang/lib/Serialization/ModuleCache.cpp b/clang/lib/Serialization/ModuleCache.cpp index 955e5f322bcc3..4ae49c4ec9a05 100644 --- a/clang/lib/Serialization/ModuleCache.cpp +++ b/clang/lib/Serialization/ModuleCache.cpp @@ -9,6 +9,7 @@ #include "clang/Serialization/ModuleCache.h" #include "clang/Serialization/InMemoryModuleCache.h" +#include "clang/Serialization/ModuleFile.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/LockFileManager.h" #include "llvm/Support/Path.h" @@ -32,6 +33,28 @@ class CrossProcessModuleCache : public ModuleCache { return std::make_unique(ModuleFilename); } + std::time_t getModuleTimestamp(StringRef ModuleFilename) override { + std::string TimestampFilename = + serialization::ModuleFile::getTimestampFilename(ModuleFilename); + llvm::sys::fs::file_status Status; + if (llvm::sys::fs::status(ModuleFilename, Status) != std::error_code{}) + return 0; + return llvm::sys::toTimeT(Status.getLastModificationTime()); + } + + void updateModuleTimestamp(StringRef ModuleFilename) override { + // Overwrite the timestamp file contents so that file's mtime changes. + std::error_code EC; + llvm::raw_fd_ostream OS( + serialization::ModuleFile::getTimestampFilename(ModuleFilename), EC, + llvm::sys::fs::OF_TextWithCRLF); + if (EC) + return; + OS << "Timestamp file\n"; + OS.close(); + OS.clear_error(); // Avoid triggering a fatal error. + } + InMemoryModuleCache &getInMemoryModuleCache() override { return InMemory; } const InMemoryModuleCache &getInMemoryModuleCache() const override { return InMemory; diff --git a/clang/lib/Serialization/ModuleManager.cpp b/clang/lib/Serialization/ModuleManager.cpp index 672dfbae7c0f5..22be9d05782bb 100644 --- a/clang/lib/Serialization/ModuleManager.cpp +++ b/clang/lib/Serialization/ModuleManager.cpp @@ -180,15 +180,9 @@ ModuleManager::addModule(StringRef FileName, ModuleKind Type, NewModule->ImportLoc = ImportLoc; NewModule->InputFilesValidationTimestamp = 0; - if (NewModule->Kind == MK_ImplicitModule) { - std::string TimestampFilename = - ModuleFile::getTimestampFilename(NewModule->FileName); - llvm::vfs::Status Status; - // A cached stat value would be fine as well. - if (!FileMgr.getNoncachedStatValue(TimestampFilename, Status)) - NewModule->InputFilesValidationTimestamp = - llvm::sys::toTimeT(Status.getLastModificationTime()); - } + if (NewModule->Kind == MK_ImplicitModule) + NewModule->InputFilesValidationTimestamp = + ModCache->getModuleTimestamp(NewModule->FileName); // Load the contents of the module if (std::unique_ptr Buffer = lookupBuffer(FileName)) { diff --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningService.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningService.cpp index 8d6e0fb0f9fc9..75b05091623a8 100644 --- a/clang/lib/Tooling/DependencyScanning/DependencyScanningService.cpp +++ b/clang/lib/Tooling/DependencyScanning/DependencyScanningService.cpp @@ -21,10 +21,12 @@ DependencyScanningService::DependencyScanningService( std::shared_ptr CAS, std::shared_ptr Cache, IntrusiveRefCntPtr SharedFS, - ScanningOptimizations OptimizeArgs, bool EagerLoadModules, bool TraceVFS) + ScanningOptimizations OptimizeArgs, bool EagerLoadModules, bool TraceVFS, + std::time_t BuildSessionTimestamp) : Mode(Mode), Format(Format), CASOpts(std::move(CASOpts)), CAS(std::move(CAS)), Cache(std::move(Cache)), OptimizeArgs(OptimizeArgs), EagerLoadModules(EagerLoadModules), TraceVFS(TraceVFS), - SharedFS(std::move(SharedFS)) { + SharedFS(std::move(SharedFS)), + BuildSessionTimestamp(BuildSessionTimestamp) { if (!this->SharedFS) SharedCache.emplace(); diff --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp index fd1612befa0e5..41ac425e67f91 100644 --- a/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp +++ b/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp @@ -441,7 +441,7 @@ class DependencyScanningAction : public tooling::ToolAction { Scanned = true; // Create a compiler instance to handle the actual work. - auto ModCache = makeInProcessModuleCache(Service.getModuleCacheMutexes()); + auto ModCache = makeInProcessModuleCache(Service.getModuleCacheEntries()); ScanInstanceStorage.emplace(std::move(PCHContainerOps), ModCache.get()); CompilerInstance &ScanInstance = *ScanInstanceStorage; ScanInstance.setInvocation(std::move(Invocation)); @@ -461,6 +461,10 @@ class DependencyScanningAction : public tooling::ToolAction { ScanInstance.getPreprocessorOpts().AllowPCHWithDifferentModulesCachePath = true; + if (ScanInstance.getHeaderSearchOpts().ModulesValidateOncePerBuildSession) + ScanInstance.getHeaderSearchOpts().BuildSessionTimestamp = + Service.getBuildSessionTimestamp(); + ScanInstance.getFrontendOpts().GenerateGlobalModuleIndex = false; ScanInstance.getFrontendOpts().UseGlobalModuleIndex = false; // This will prevent us compiling individual modules asynchronously since diff --git a/clang/lib/Tooling/DependencyScanning/InProcessModuleCache.cpp b/clang/lib/Tooling/DependencyScanning/InProcessModuleCache.cpp index 71ce4d098932b..80db2d47d940e 100644 --- a/clang/lib/Tooling/DependencyScanning/InProcessModuleCache.cpp +++ b/clang/lib/Tooling/DependencyScanning/InProcessModuleCache.cpp @@ -10,6 +10,7 @@ #include "clang/Serialization/InMemoryModuleCache.h" #include "llvm/Support/AdvisoryLock.h" +#include "llvm/Support/Chrono.h" #include @@ -50,7 +51,7 @@ class ReaderWriterLock : public llvm::AdvisoryLock { }; class InProcessModuleCache : public ModuleCache { - ModuleCacheMutexes &Mutexes; + ModuleCacheEntries &Entries; // TODO: If we changed the InMemoryModuleCache API and relied on strict // context hash, we could probably create more efficient thread-safe @@ -59,19 +60,44 @@ class InProcessModuleCache : public ModuleCache { InMemoryModuleCache InMemory; public: - InProcessModuleCache(ModuleCacheMutexes &Mutexes) : Mutexes(Mutexes) {} + InProcessModuleCache(ModuleCacheEntries &Entries) : Entries(Entries) {} void prepareForGetLock(StringRef Filename) override {} std::unique_ptr 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(); - return *Mutex; + auto &CompilationMutex = [&]() -> std::shared_mutex & { + std::lock_guard Lock(Entries.Mutex); + auto &Entry = Entries.Map[Filename]; + if (!Entry) + Entry = std::make_unique(); + return Entry->CompilationMutex; }(); - return std::make_unique(Mtx); + return std::make_unique(CompilationMutex); + } + + std::time_t getModuleTimestamp(StringRef Filename) override { + auto &Timestamp = [&]() -> std::atomic & { + std::lock_guard Lock(Entries.Mutex); + auto &Entry = Entries.Map[Filename]; + if (!Entry) + Entry = std::make_unique(); + 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::lock_guard Lock(Entries.Mutex); + auto &Entry = Entries.Map[Filename]; + if (!Entry) + Entry = std::make_unique(); + return Entry->Timestamp; + }(); + + Timestamp.store(llvm::sys::toTimeT(std::chrono::system_clock::now())); } InMemoryModuleCache &getInMemoryModuleCache() override { return InMemory; } @@ -82,6 +108,6 @@ class InProcessModuleCache : public ModuleCache { } // namespace IntrusiveRefCntPtr -dependencies::makeInProcessModuleCache(ModuleCacheMutexes &Mutexes) { - return llvm::makeIntrusiveRefCnt(Mutexes); +dependencies::makeInProcessModuleCache(ModuleCacheEntries &Entries) { + return llvm::makeIntrusiveRefCnt(Entries); }