https://github.com/jansvoboda11 updated https://github.com/llvm/llvm-project/pull/181772
>From 7aa5806853848cea8022cff02d443c00ac54fa6a Mon Sep 17 00:00:00 2001 From: Jan Svoboda <[email protected]> Date: Mon, 16 Feb 2026 15:55:35 -0800 Subject: [PATCH 1/3] [clang][deps] Ensure the service outlives async module compiles --- .../DependencyScannerImpl.cpp | 73 ++++++++++++++----- 1 file changed, 54 insertions(+), 19 deletions(-) diff --git a/clang/lib/DependencyScanning/DependencyScannerImpl.cpp b/clang/lib/DependencyScanning/DependencyScannerImpl.cpp index 28fa2571a24dc..ef2712ac3b56e 100644 --- a/clang/lib/DependencyScanning/DependencyScannerImpl.cpp +++ b/clang/lib/DependencyScanning/DependencyScannerImpl.cpp @@ -554,11 +554,42 @@ dependencies::initializeScanInstanceDependencyCollector( return MDC; } +/// Manages (and terminates) the asynchronous compilation of modules. +class AsyncModuleCompiles { + std::mutex Mutex; + bool Stop = false; + // FIXME: Have the service own a thread pool and use that instead. + std::vector<std::thread> Compiles; + +public: + /// Registers the module compilation, unless this instance is about to be + /// destroyed. + void add(llvm::unique_function<void()> Compile) { + std::lock_guard Lock(Mutex); + if (!Stop) + Compiles.emplace_back(std::move(Compile)); + } + + ~AsyncModuleCompiles() { + { + // Prevent registration of further module compiles. + std::lock_guard Lock(Mutex); + Stop = true; + } + + // Wait for outstanding module compiles to finish. + for (std::thread &Compile : Compiles) + Compile.join(); + } +}; + struct SingleModuleWithAsyncModuleCompiles : PreprocessOnlyAction { DependencyScanningService &Service; + AsyncModuleCompiles &Compiles; - SingleModuleWithAsyncModuleCompiles(DependencyScanningService &Service) - : Service(Service) {} + SingleModuleWithAsyncModuleCompiles(DependencyScanningService &Service, + AsyncModuleCompiles &Compiles) + : Service(Service), Compiles(Compiles) {} bool BeginSourceFileAction(CompilerInstance &CI) override; }; @@ -568,9 +599,11 @@ struct SingleModuleWithAsyncModuleCompiles : PreprocessOnlyAction { struct AsyncModuleCompile : PPCallbacks { CompilerInstance &CI; DependencyScanningService &Service; + AsyncModuleCompiles &Compiles; - AsyncModuleCompile(CompilerInstance &CI, DependencyScanningService &Service) - : CI(CI), Service(Service) {} + AsyncModuleCompile(CompilerInstance &CI, DependencyScanningService &Service, + AsyncModuleCompiles &Compiles) + : CI(CI), Service(Service), Compiles(Compiles) {} void moduleLoadSkipped(Module *M) override { M = M->getTopLevelModule(); @@ -635,25 +668,23 @@ struct AsyncModuleCompile : PPCallbacks { auto ModCI2 = CI.cloneForModuleCompile(SourceLocation(), M, ModuleFileName, CloneConfig); - // FIXME: Have the service own a thread pool and use that instead. - // FIXME: This lock belongs to a module cache that might not outlive the - // thread. (This should work for now, because the in-process lock only - // refers to an object managed by the service, which should outlive this - // thread.) - std::thread([Lock = std::move(Lock), ModCI1 = std::move(ModCI1), - ModCI2 = std::move(ModCI2), DC = std::move(DC), - Service = &Service] { + // Note: This lock belongs to a module cache that might not outlive the + // thread. This works, because the in-process lock only refers to an object + // managed by the service, which does outlive the thread. + Compiles.add([Lock = std::move(Lock), ModCI1 = std::move(ModCI1), + ModCI2 = std::move(ModCI2), DC = std::move(DC), + Service = &Service, Compiles = &Compiles] { llvm::CrashRecoveryContext CRC; (void)CRC.RunSafely([&] { // Quickly discovers and compiles modules for the real scan below. - SingleModuleWithAsyncModuleCompiles Action1(*Service); + SingleModuleWithAsyncModuleCompiles Action1(*Service, *Compiles); (void)ModCI1->ExecuteAction(Action1); // The real scan below. ModCI2->getPreprocessorOpts().SingleModuleParseMode = false; GenerateModuleFromModuleMapAction Action2; (void)ModCI2->ExecuteAction(Action2); }); - }).detach(); + }); } }; @@ -661,14 +692,16 @@ struct AsyncModuleCompile : PPCallbacks { /// modules asynchronously without blocking or importing them. struct SingleTUWithAsyncModuleCompiles : PreprocessOnlyAction { DependencyScanningService &Service; + AsyncModuleCompiles &Compiles; - SingleTUWithAsyncModuleCompiles(DependencyScanningService &Service) - : Service(Service) {} + SingleTUWithAsyncModuleCompiles(DependencyScanningService &Service, + AsyncModuleCompiles &Compiles) + : Service(Service), Compiles(Compiles) {} bool BeginSourceFileAction(CompilerInstance &CI) override { CI.getInvocation().getPreprocessorOpts().SingleModuleParseMode = true; CI.getPreprocessor().addPPCallbacks( - std::make_unique<AsyncModuleCompile>(CI, Service)); + std::make_unique<AsyncModuleCompile>(CI, Service, Compiles)); return true; } }; @@ -677,7 +710,7 @@ bool SingleModuleWithAsyncModuleCompiles::BeginSourceFileAction( CompilerInstance &CI) { CI.getInvocation().getPreprocessorOpts().SingleModuleParseMode = true; CI.getPreprocessor().addPPCallbacks( - std::make_unique<AsyncModuleCompile>(CI, Service)); + std::make_unique<AsyncModuleCompile>(CI, Service, Compiles)); return true; } @@ -711,6 +744,7 @@ bool DependencyScanningAction::runInvocation( createScanCompilerInvocation(*OriginalInvocation, Service); // Quickly discovers and compiles modules for the real scan below. + std::optional<AsyncModuleCompiles> AsyncCompiles; if (Service.getOpts().AsyncScanModules) { auto ModCache = makeInProcessModuleCache(Service.getModuleCacheEntries()); auto ScanInstanceStorage = std::make_unique<CompilerInstance>( @@ -733,7 +767,8 @@ bool DependencyScanningAction::runInvocation( if (ScanInstance.getFrontendOpts().ProgramAction == frontend::GeneratePCH) ScanInstance.getLangOpts().CompilingPCH = true; - SingleTUWithAsyncModuleCompiles Action(Service); + AsyncCompiles.emplace(); + SingleTUWithAsyncModuleCompiles Action(Service, *AsyncCompiles); (void)ScanInstance.ExecuteAction(Action); } >From 8eeb93364004fd8dd0d5b0a0e361f44e36117cc1 Mon Sep 17 00:00:00 2001 From: Jan Svoboda <[email protected]> Date: Tue, 17 Feb 2026 09:25:20 -0800 Subject: [PATCH 2/3] Add missing include --- clang/lib/DependencyScanning/DependencyScannerImpl.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/clang/lib/DependencyScanning/DependencyScannerImpl.cpp b/clang/lib/DependencyScanning/DependencyScannerImpl.cpp index ef2712ac3b56e..89c96c7bf9876 100644 --- a/clang/lib/DependencyScanning/DependencyScannerImpl.cpp +++ b/clang/lib/DependencyScanning/DependencyScannerImpl.cpp @@ -21,6 +21,7 @@ #include "llvm/Support/VirtualFileSystem.h" #include "llvm/TargetParser/Host.h" +#include <mutex> #include <thread> using namespace clang; >From c398127df091437d10b62af94359bf72b6a38d48 Mon Sep 17 00:00:00 2001 From: Jan Svoboda <[email protected]> Date: Tue, 17 Feb 2026 09:47:01 -0800 Subject: [PATCH 3/3] Don't rely on CTAD --- clang/lib/DependencyScanning/DependencyScannerImpl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/lib/DependencyScanning/DependencyScannerImpl.cpp b/clang/lib/DependencyScanning/DependencyScannerImpl.cpp index 89c96c7bf9876..51f98cdefb9b7 100644 --- a/clang/lib/DependencyScanning/DependencyScannerImpl.cpp +++ b/clang/lib/DependencyScanning/DependencyScannerImpl.cpp @@ -566,7 +566,7 @@ class AsyncModuleCompiles { /// Registers the module compilation, unless this instance is about to be /// destroyed. void add(llvm::unique_function<void()> Compile) { - std::lock_guard Lock(Mutex); + std::lock_guard<std::mutex> Lock(Mutex); if (!Stop) Compiles.emplace_back(std::move(Compile)); } @@ -574,7 +574,7 @@ class AsyncModuleCompiles { ~AsyncModuleCompiles() { { // Prevent registration of further module compiles. - std::lock_guard Lock(Mutex); + std::lock_guard<std::mutex> Lock(Mutex); Stop = true; } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
