diff options
author | Ilya Biryukov <ibiryukov@google.com> | 2018-02-09 10:17:23 +0000 |
---|---|---|
committer | Ilya Biryukov <ibiryukov@google.com> | 2018-02-09 10:17:23 +0000 |
commit | bb52c22ad4ff46de537c8ee8ed2f4888639a859b (patch) | |
tree | b98c310ec3dcc2da4bc5a7cbcbe72d27707fb94e /clang-tools-extra/clangd/ClangdUnit.cpp | |
parent | 2fb2d6dee12a595557411f8ff33f93c9d2a23b39 (diff) |
[clangd] Remove threading-related code from ClangdUnit.h
Reviewers: sammccall, hokein, ioeric
Reviewed By: sammccall
Subscribers: klimek, jkorous-apple, cfe-commits
Differential Revision: https://reviews.llvm.org/D43065
Diffstat (limited to 'clang-tools-extra/clangd/ClangdUnit.cpp')
-rw-r--r-- | clang-tools-extra/clangd/ClangdUnit.cpp | 431 |
1 files changed, 113 insertions, 318 deletions
diff --git a/clang-tools-extra/clangd/ClangdUnit.cpp b/clang-tools-extra/clangd/ClangdUnit.cpp index 1da2e8f282d..c16c218fbae 100644 --- a/clang-tools-extra/clangd/ClangdUnit.cpp +++ b/clang-tools-extra/clangd/ClangdUnit.cpp @@ -29,7 +29,6 @@ #include "llvm/Support/Format.h" #include "llvm/Support/raw_ostream.h" #include <algorithm> -#include <chrono> using namespace clang::clangd; using namespace clang; @@ -234,10 +233,6 @@ private: llvm::Optional<LangOptions> LangOpts; }; -template <class T> bool futureIsReady(std::shared_future<T> const &Future) { - return Future.wait_for(std::chrono::seconds(0)) == std::future_status::ready; -} - } // namespace void clangd::dumpAST(ParsedAST &AST, llvm::raw_ostream &OS) { @@ -250,7 +245,6 @@ ParsedAST::Build(std::unique_ptr<clang::CompilerInvocation> CI, std::unique_ptr<llvm::MemoryBuffer> Buffer, std::shared_ptr<PCHContainerOperations> PCHs, IntrusiveRefCntPtr<vfs::FileSystem> VFS) { - std::vector<DiagWithFixIts> ASTDiags; StoreDiagsConsumer UnitDiagsConsumer(/*ref*/ ASTDiags); @@ -380,344 +374,145 @@ ParsedAST::ParsedAST(std::shared_ptr<const PreambleData> Preamble, assert(this->Action); } -ParsedASTWrapper::ParsedASTWrapper(ParsedASTWrapper &&Wrapper) - : AST(std::move(Wrapper.AST)) {} - -ParsedASTWrapper::ParsedASTWrapper(llvm::Optional<ParsedAST> AST) - : AST(std::move(AST)) {} - -std::shared_ptr<CppFile> -CppFile::Create(PathRef FileName, bool StorePreamblesInMemory, - std::shared_ptr<PCHContainerOperations> PCHs, - ASTParsedCallback ASTCallback) { - return std::shared_ptr<CppFile>(new CppFile(FileName, StorePreamblesInMemory, - std::move(PCHs), - std::move(ASTCallback))); -} - CppFile::CppFile(PathRef FileName, bool StorePreamblesInMemory, std::shared_ptr<PCHContainerOperations> PCHs, ASTParsedCallback ASTCallback) : FileName(FileName), StorePreamblesInMemory(StorePreamblesInMemory), - RebuildCounter(0), RebuildInProgress(false), ASTMemUsage(0), - PreambleMemUsage(0), PCHs(std::move(PCHs)), - ASTCallback(std::move(ASTCallback)) { + PCHs(std::move(PCHs)), ASTCallback(std::move(ASTCallback)) { log("Created CppFile for " + FileName); - - std::lock_guard<std::mutex> Lock(Mutex); - LatestAvailablePreamble = nullptr; - PreamblePromise.set_value(nullptr); - PreambleFuture = PreamblePromise.get_future(); - - ASTPromise.set_value(std::make_shared<ParsedASTWrapper>(llvm::None)); - ASTFuture = ASTPromise.get_future(); -} - -void CppFile::cancelRebuild() { deferCancelRebuild()(); } - -UniqueFunction<void()> CppFile::deferCancelRebuild() { - std::unique_lock<std::mutex> Lock(Mutex); - // Cancel an ongoing rebuild, if any, and wait for it to finish. - unsigned RequestRebuildCounter = ++this->RebuildCounter; - // Rebuild asserts that futures aren't ready if rebuild is cancelled. - // We want to keep this invariant. - if (futureIsReady(PreambleFuture)) { - PreamblePromise = std::promise<std::shared_ptr<const PreambleData>>(); - PreambleFuture = PreamblePromise.get_future(); - } - if (futureIsReady(ASTFuture)) { - ASTPromise = std::promise<std::shared_ptr<ParsedASTWrapper>>(); - ASTFuture = ASTPromise.get_future(); - } - - Lock.unlock(); - // Notify about changes to RebuildCounter. - RebuildCond.notify_all(); - - std::shared_ptr<CppFile> That = shared_from_this(); - return [That, RequestRebuildCounter]() { - std::unique_lock<std::mutex> Lock(That->Mutex); - CppFile *This = &*That; - This->RebuildCond.wait(Lock, [This, RequestRebuildCounter]() { - return !This->RebuildInProgress || - This->RebuildCounter != RequestRebuildCounter; - }); - - // This computation got cancelled itself, do nothing. - if (This->RebuildCounter != RequestRebuildCounter) - return; - - // Set empty results for Promises. - That->PreambleMemUsage = 0; - That->PreamblePromise.set_value(nullptr); - That->ASTMemUsage = 0; - That->ASTPromise.set_value(std::make_shared<ParsedASTWrapper>(llvm::None)); - }; } llvm::Optional<std::vector<DiagWithFixIts>> CppFile::rebuild(ParseInputs &&Inputs) { - return deferRebuild(std::move(Inputs))(); -} + log("Rebuilding file " + FileName + " with command [" + + Inputs.CompileCommand.Directory + "] " + + llvm::join(Inputs.CompileCommand.CommandLine, " ")); -UniqueFunction<llvm::Optional<std::vector<DiagWithFixIts>>()> -CppFile::deferRebuild(ParseInputs &&Inputs) { - std::shared_ptr<const PreambleData> OldPreamble; - std::shared_ptr<PCHContainerOperations> PCHs; - unsigned RequestRebuildCounter; - { - std::unique_lock<std::mutex> Lock(Mutex); - // Increase RebuildCounter to cancel all ongoing FinishRebuild operations. - // They will try to exit as early as possible and won't call set_value on - // our promises. - RequestRebuildCounter = ++this->RebuildCounter; - PCHs = this->PCHs; - - // Remember the preamble to be used during rebuild. - OldPreamble = this->LatestAvailablePreamble; - // Setup std::promises and std::futures for Preamble and AST. Corresponding - // futures will wait until the rebuild process is finished. - if (futureIsReady(this->PreambleFuture)) { - this->PreamblePromise = - std::promise<std::shared_ptr<const PreambleData>>(); - this->PreambleFuture = this->PreamblePromise.get_future(); - } - if (futureIsReady(this->ASTFuture)) { - this->ASTPromise = std::promise<std::shared_ptr<ParsedASTWrapper>>(); - this->ASTFuture = this->ASTPromise.get_future(); - } - } // unlock Mutex. - // Notify about changes to RebuildCounter. - RebuildCond.notify_all(); - - // A helper to function to finish the rebuild. May be run on a different - // thread. - - // Don't let this CppFile die before rebuild is finished. - std::shared_ptr<CppFile> That = shared_from_this(); - auto FinishRebuild = - [OldPreamble, RequestRebuildCounter, PCHs, - That](ParseInputs Inputs) mutable /* to allow changing OldPreamble. */ - -> llvm::Optional<std::vector<DiagWithFixIts>> { - log("Rebuilding file " + That->FileName + " with command [" + - Inputs.CompileCommand.Directory + "] " + - llvm::join(Inputs.CompileCommand.CommandLine, " ")); - - // Only one execution of this method is possible at a time. - // RebuildGuard will wait for any ongoing rebuilds to finish and will put us - // into a state for doing a rebuild. - RebuildGuard Rebuild(*That, RequestRebuildCounter); - if (Rebuild.wasCancelledBeforeConstruction()) - return llvm::None; - - std::vector<const char *> ArgStrs; - for (const auto &S : Inputs.CompileCommand.CommandLine) - ArgStrs.push_back(S.c_str()); - - Inputs.FS->setCurrentWorkingDirectory(Inputs.CompileCommand.Directory); - - std::unique_ptr<CompilerInvocation> CI; - { - // FIXME(ibiryukov): store diagnostics from CommandLine when we start - // reporting them. - IgnoreDiagnostics IgnoreDiagnostics; - IntrusiveRefCntPtr<DiagnosticsEngine> CommandLineDiagsEngine = - CompilerInstance::createDiagnostics(new DiagnosticOptions, - &IgnoreDiagnostics, false); - CI = createInvocationFromCommandLine(ArgStrs, CommandLineDiagsEngine, - Inputs.FS); - // createInvocationFromCommandLine sets DisableFree. - CI->getFrontendOpts().DisableFree = false; - } - assert(CI && "Couldn't create CompilerInvocation"); - - std::unique_ptr<llvm::MemoryBuffer> ContentsBuffer = - llvm::MemoryBuffer::getMemBufferCopy(Inputs.Contents, That->FileName); - - // A helper function to rebuild the preamble or reuse the existing one. Does - // not mutate any fields of CppFile, only does the actual computation. - // Lamdba is marked mutable to call reset() on OldPreamble. - auto DoRebuildPreamble = - [&]() mutable -> std::shared_ptr<const PreambleData> { - auto Bounds = - ComputePreambleBounds(*CI->getLangOpts(), ContentsBuffer.get(), 0); - if (OldPreamble && - OldPreamble->Preamble.CanReuse(*CI, ContentsBuffer.get(), Bounds, - Inputs.FS.get())) { - log("Reusing preamble for file " + Twine(That->FileName)); - return OldPreamble; - } - log("Preamble for file " + Twine(That->FileName) + - " cannot be reused. Attempting to rebuild it."); - // We won't need the OldPreamble anymore, release it so it can be - // deleted (if there are no other references to it). - OldPreamble.reset(); - - trace::Span Tracer("Preamble"); - SPAN_ATTACH(Tracer, "File", That->FileName); - std::vector<DiagWithFixIts> PreambleDiags; - StoreDiagsConsumer PreambleDiagnosticsConsumer(/*ref*/ PreambleDiags); - IntrusiveRefCntPtr<DiagnosticsEngine> PreambleDiagsEngine = - CompilerInstance::createDiagnostics( - &CI->getDiagnosticOpts(), &PreambleDiagnosticsConsumer, false); - - // Skip function bodies when building the preamble to speed up building - // the preamble and make it smaller. - assert(!CI->getFrontendOpts().SkipFunctionBodies); - CI->getFrontendOpts().SkipFunctionBodies = true; - - CppFilePreambleCallbacks SerializedDeclsCollector; - auto BuiltPreamble = PrecompiledPreamble::Build( - *CI, ContentsBuffer.get(), Bounds, *PreambleDiagsEngine, Inputs.FS, - PCHs, - /*StoreInMemory=*/That->StorePreamblesInMemory, - SerializedDeclsCollector); - - // When building the AST for the main file, we do want the function - // bodies. - CI->getFrontendOpts().SkipFunctionBodies = false; - - if (BuiltPreamble) { - log("Built preamble of size " + Twine(BuiltPreamble->getSize()) + - " for file " + Twine(That->FileName)); - - return std::make_shared<PreambleData>( - std::move(*BuiltPreamble), - SerializedDeclsCollector.takeTopLevelDeclIDs(), - std::move(PreambleDiags)); - } else { - log("Could not build a preamble for file " + Twine(That->FileName)); - return nullptr; - } - }; - - // Compute updated Preamble. - std::shared_ptr<const PreambleData> NewPreamble = DoRebuildPreamble(); - // Publish the new Preamble. - { - std::lock_guard<std::mutex> Lock(That->Mutex); - // We always set LatestAvailablePreamble to the new value, hoping that it - // will still be usable in the further requests. - That->LatestAvailablePreamble = NewPreamble; - if (RequestRebuildCounter != That->RebuildCounter) - return llvm::None; // Our rebuild request was cancelled, do nothing. - That->PreambleMemUsage = - NewPreamble ? NewPreamble->Preamble.getSize() : 0; - That->PreamblePromise.set_value(NewPreamble); - } // unlock Mutex - - // Prepare the Preamble and supplementary data for rebuilding AST. - std::vector<DiagWithFixIts> Diagnostics; - if (NewPreamble) { - Diagnostics.insert(Diagnostics.begin(), NewPreamble->Diags.begin(), - NewPreamble->Diags.end()); - } + std::vector<const char *> ArgStrs; + for (const auto &S : Inputs.CompileCommand.CommandLine) + ArgStrs.push_back(S.c_str()); - // Compute updated AST. - llvm::Optional<ParsedAST> NewAST; - { - trace::Span Tracer("Build"); - SPAN_ATTACH(Tracer, "File", That->FileName); - NewAST = ParsedAST::Build(std::move(CI), std::move(NewPreamble), - std::move(ContentsBuffer), PCHs, Inputs.FS); - } + Inputs.FS->setCurrentWorkingDirectory(Inputs.CompileCommand.Directory); - if (NewAST) { - Diagnostics.insert(Diagnostics.end(), NewAST->getDiagnostics().begin(), - NewAST->getDiagnostics().end()); - if (That->ASTCallback) - That->ASTCallback(That->FileName, NewAST.getPointer()); - } else { - // Don't report even Preamble diagnostics if we couldn't build AST. - Diagnostics.clear(); - } + // Prepare CompilerInvocation. + std::unique_ptr<CompilerInvocation> CI; + { + // FIXME(ibiryukov): store diagnostics from CommandLine when we start + // reporting them. + IgnoreDiagnostics IgnoreDiagnostics; + IntrusiveRefCntPtr<DiagnosticsEngine> CommandLineDiagsEngine = + CompilerInstance::createDiagnostics(new DiagnosticOptions, + &IgnoreDiagnostics, false); + CI = createInvocationFromCommandLine(ArgStrs, CommandLineDiagsEngine, + Inputs.FS); + // createInvocationFromCommandLine sets DisableFree. + CI->getFrontendOpts().DisableFree = false; + } + assert(CI && "Couldn't create CompilerInvocation"); - // Publish the new AST. - { - std::lock_guard<std::mutex> Lock(That->Mutex); - if (RequestRebuildCounter != That->RebuildCounter) - return Diagnostics; // Our rebuild request was cancelled, don't set - // ASTPromise. + std::unique_ptr<llvm::MemoryBuffer> ContentsBuffer = + llvm::MemoryBuffer::getMemBufferCopy(Inputs.Contents, FileName); - That->ASTMemUsage = NewAST ? NewAST->getUsedBytes() : 0; - That->ASTPromise.set_value( - std::make_shared<ParsedASTWrapper>(std::move(NewAST))); - } // unlock Mutex + // Compute updated Preamble. + std::shared_ptr<const PreambleData> NewPreamble = + rebuildPreamble(*CI, Inputs.FS, *ContentsBuffer); - return Diagnostics; - }; + // Remove current AST to avoid wasting memory. + AST = llvm::None; + // Compute updated AST. + llvm::Optional<ParsedAST> NewAST; + { + trace::Span Tracer("Build"); + SPAN_ATTACH(Tracer, "File", FileName); + NewAST = ParsedAST::Build(std::move(CI), NewPreamble, + std::move(ContentsBuffer), PCHs, Inputs.FS); + } - return BindWithForward(FinishRebuild, std::move(Inputs)); -} + std::vector<DiagWithFixIts> Diagnostics; + if (NewAST) { + // Collect diagnostics from both the preamble and the AST. + if (NewPreamble) + Diagnostics.insert(Diagnostics.begin(), NewPreamble->Diags.begin(), + NewPreamble->Diags.end()); + Diagnostics.insert(Diagnostics.end(), NewAST->getDiagnostics().begin(), + NewAST->getDiagnostics().end()); + } + if (ASTCallback && NewAST) + ASTCallback(FileName, NewAST.getPointer()); -std::shared_future<std::shared_ptr<const PreambleData>> -CppFile::getPreamble() const { - std::lock_guard<std::mutex> Lock(Mutex); - return PreambleFuture; + // Write the results of rebuild into class fields. + Preamble = std::move(NewPreamble); + AST = std::move(NewAST); + return Diagnostics; } -std::shared_ptr<const PreambleData> CppFile::getPossiblyStalePreamble() const { - std::lock_guard<std::mutex> Lock(Mutex); - return LatestAvailablePreamble; +const std::shared_ptr<const PreambleData> &CppFile::getPreamble() const { + return Preamble; } -std::shared_future<std::shared_ptr<ParsedASTWrapper>> CppFile::getAST() const { - std::lock_guard<std::mutex> Lock(Mutex); - return ASTFuture; +ParsedAST *CppFile::getAST() const { + // We could add mutable to AST instead of const_cast here, but that would also + // allow writing to AST from const methods. + return AST ? const_cast<ParsedAST *>(AST.getPointer()) : nullptr; } std::size_t CppFile::getUsedBytes() const { - std::lock_guard<std::mutex> Lock(Mutex); - // FIXME: We should not store extra size fields. When we store AST and - // Preamble directly, not inside futures, we could compute the sizes from the - // stored AST and the preamble in this function directly. - return ASTMemUsage + PreambleMemUsage; -} - -CppFile::RebuildGuard::RebuildGuard(CppFile &File, - unsigned RequestRebuildCounter) - : File(File), RequestRebuildCounter(RequestRebuildCounter) { - std::unique_lock<std::mutex> Lock(File.Mutex); - WasCancelledBeforeConstruction = File.RebuildCounter != RequestRebuildCounter; - if (WasCancelledBeforeConstruction) - return; - - File.RebuildCond.wait(Lock, [&File, RequestRebuildCounter]() { - return !File.RebuildInProgress || - File.RebuildCounter != RequestRebuildCounter; - }); - - WasCancelledBeforeConstruction = File.RebuildCounter != RequestRebuildCounter; - if (WasCancelledBeforeConstruction) - return; - - File.RebuildInProgress = true; -} - -bool CppFile::RebuildGuard::wasCancelledBeforeConstruction() const { - return WasCancelledBeforeConstruction; -} - -CppFile::RebuildGuard::~RebuildGuard() { - if (WasCancelledBeforeConstruction) - return; - - std::unique_lock<std::mutex> Lock(File.Mutex); - assert(File.RebuildInProgress); - File.RebuildInProgress = false; - - if (File.RebuildCounter == RequestRebuildCounter) { - // Our rebuild request was successful. - assert(futureIsReady(File.ASTFuture)); - assert(futureIsReady(File.PreambleFuture)); + std::size_t Total = 0; + if (AST) + Total += AST->getUsedBytes(); + if (StorePreamblesInMemory && Preamble) + Total += Preamble->Preamble.getSize(); + return Total; +} + +std::shared_ptr<const PreambleData> +CppFile::rebuildPreamble(CompilerInvocation &CI, + IntrusiveRefCntPtr<vfs::FileSystem> FS, + llvm::MemoryBuffer &ContentsBuffer) const { + const auto &OldPreamble = this->Preamble; + auto Bounds = ComputePreambleBounds(*CI.getLangOpts(), &ContentsBuffer, 0); + if (OldPreamble && + OldPreamble->Preamble.CanReuse(CI, &ContentsBuffer, Bounds, FS.get())) { + log("Reusing preamble for file " + Twine(FileName)); + return OldPreamble; + } + log("Preamble for file " + Twine(FileName) + + " cannot be reused. Attempting to rebuild it."); + + trace::Span Tracer("Preamble"); + SPAN_ATTACH(Tracer, "File", FileName); + std::vector<DiagWithFixIts> PreambleDiags; + StoreDiagsConsumer PreambleDiagnosticsConsumer(/*ref*/ PreambleDiags); + IntrusiveRefCntPtr<DiagnosticsEngine> PreambleDiagsEngine = + CompilerInstance::createDiagnostics(&CI.getDiagnosticOpts(), + &PreambleDiagnosticsConsumer, false); + + // Skip function bodies when building the preamble to speed up building + // the preamble and make it smaller. + assert(!CI.getFrontendOpts().SkipFunctionBodies); + CI.getFrontendOpts().SkipFunctionBodies = true; + + CppFilePreambleCallbacks SerializedDeclsCollector; + auto BuiltPreamble = PrecompiledPreamble::Build( + CI, &ContentsBuffer, Bounds, *PreambleDiagsEngine, FS, PCHs, + /*StoreInMemory=*/StorePreamblesInMemory, SerializedDeclsCollector); + + // When building the AST for the main file, we do want the function + // bodies. + CI.getFrontendOpts().SkipFunctionBodies = false; + + if (BuiltPreamble) { + log("Built preamble of size " + Twine(BuiltPreamble->getSize()) + + " for file " + Twine(FileName)); + + return std::make_shared<PreambleData>( + std::move(*BuiltPreamble), + SerializedDeclsCollector.takeTopLevelDeclIDs(), + std::move(PreambleDiags)); } else { - // Our rebuild request was cancelled, because further reparse was requested. - assert(!futureIsReady(File.ASTFuture)); - assert(!futureIsReady(File.PreambleFuture)); + log("Could not build a preamble for file " + Twine(FileName)); + return nullptr; } - - Lock.unlock(); - File.RebuildCond.notify_all(); } SourceLocation clangd::getBeginningOfIdentifier(ParsedAST &Unit, |