diff options
Diffstat (limited to 'clangd/TUScheduler.cpp')
-rw-r--r-- | clangd/TUScheduler.cpp | 193 |
1 files changed, 117 insertions, 76 deletions
diff --git a/clangd/TUScheduler.cpp b/clangd/TUScheduler.cpp index 23264004..c4e555b5 100644 --- a/clangd/TUScheduler.cpp +++ b/clangd/TUScheduler.cpp @@ -1,9 +1,8 @@ //===--- TUScheduler.cpp -----------------------------------------*-C++-*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // For each file, managed by TUScheduler, we create a single ASTWorker that @@ -44,10 +43,14 @@ #include "TUScheduler.h" #include "Cancellation.h" +#include "Compiler.h" +#include "GlobalCompilationDatabase.h" #include "Logger.h" #include "Trace.h" +#include "index/CanonicalIncludes.h" #include "clang/Frontend/CompilerInvocation.h" -#include "clang/Frontend/PCHContainerOperations.h" +#include "clang/Tooling/CompilationDatabase.h" +#include "llvm/ADT/Optional.h" #include "llvm/ADT/ScopeExit.h" #include "llvm/Support/Errc.h" #include "llvm/Support/Path.h" @@ -62,7 +65,7 @@ using std::chrono::steady_clock; namespace { class ASTWorker; -} +} // namespace static clang::clangd::Key<std::string> kFileBeingProcessed; @@ -154,11 +157,10 @@ class ASTWorkerHandle; /// worker. class ASTWorker { friend class ASTWorkerHandle; - ASTWorker(PathRef FileName, TUScheduler::ASTCache &LRUCache, - Semaphore &Barrier, bool RunSync, - steady_clock::duration UpdateDebounce, - std::shared_ptr<PCHContainerOperations> PCHs, - bool StorePreamblesInMemory, ParsingCallbacks &Callbacks); + ASTWorker(PathRef FileName, const GlobalCompilationDatabase &CDB, + TUScheduler::ASTCache &LRUCache, Semaphore &Barrier, bool RunSync, + steady_clock::duration UpdateDebounce, bool StorePreamblesInMemory, + ParsingCallbacks &Callbacks); public: /// Create a new ASTWorker and return a handle to it. @@ -166,13 +168,11 @@ public: /// is null, all requests will be processed on the calling thread /// synchronously instead. \p Barrier is acquired when processing each /// request, it is used to limit the number of actively running threads. - static ASTWorkerHandle create(PathRef FileName, - TUScheduler::ASTCache &IdleASTs, - AsyncTaskRunner *Tasks, Semaphore &Barrier, - steady_clock::duration UpdateDebounce, - std::shared_ptr<PCHContainerOperations> PCHs, - bool StorePreamblesInMemory, - ParsingCallbacks &Callbacks); + static ASTWorkerHandle + create(PathRef FileName, const GlobalCompilationDatabase &CDB, + TUScheduler::ASTCache &IdleASTs, AsyncTaskRunner *Tasks, + Semaphore &Barrier, steady_clock::duration UpdateDebounce, + bool StorePreamblesInMemory, ParsingCallbacks &Callbacks); ~ASTWorker(); void update(ParseInputs Inputs, WantDiagnostics); @@ -182,10 +182,14 @@ public: bool blockUntilIdle(Deadline Timeout) const; std::shared_ptr<const PreambleData> getPossiblyStalePreamble() const; + /// Obtain a preamble reflecting all updates so far. Threadsafe. /// It may be delivered immediately, or later on the worker thread. void getCurrentPreamble( llvm::unique_function<void(std::shared_ptr<const PreambleData>)>); + /// Returns compile command from the current file inputs. + tooling::CompileCommand getCurrentCompileCommand() const; + /// Wait for the first build of preamble to finish. Preamble itself can be /// accessed via getPossiblyStalePreamble(). Note that this function will /// return after an unsuccessful build of the preamble too, i.e. result of @@ -216,6 +220,10 @@ private: Deadline scheduleLocked(); /// Should the first task in the queue be skipped instead of run? bool shouldSkipHeadLocked() const; + /// This is private because `FileInputs.FS` is not thread-safe and thus not + /// safe to share. Callers should make sure not to expose `FS` via a public + /// interface. + std::shared_ptr<const ParseInputs> getCurrentFileInputs() const; struct Request { llvm::unique_function<void()> Action; @@ -232,24 +240,24 @@ private: const steady_clock::duration UpdateDebounce; /// File that ASTWorker is responsible for. const Path FileName; + const GlobalCompilationDatabase &CDB; /// Whether to keep the built preambles in memory or on disk. const bool StorePreambleInMemory; /// Callback invoked when preamble or main file AST is built. ParsingCallbacks &Callbacks; - /// Helper class required to build the ASTs. - const std::shared_ptr<PCHContainerOperations> PCHs; /// Only accessed by the worker thread. TUStatus Status; Semaphore &Barrier; - /// Inputs, corresponding to the current state of AST. - ParseInputs FileInputs; /// Whether the diagnostics for the current FileInputs were reported to the /// users before. bool DiagsWereReported = false; - /// Size of the last AST /// Guards members used by both TUScheduler and the worker thread. mutable std::mutex Mutex; + /// File inputs, currently being used by the worker. + /// Inputs are written and read by the worker thread, compile command can also + /// be consumed by clients of ASTWorker. + std::shared_ptr<const ParseInputs> FileInputs; /* GUARDED_BY(Mutex) */ std::shared_ptr<const PreambleData> LastBuiltPreamble; /* GUARDED_BY(Mutex) */ /// Becomes ready when the first preamble build finishes. Notification PreambleWasBuilt; @@ -310,16 +318,14 @@ private: std::shared_ptr<ASTWorker> Worker; }; -ASTWorkerHandle ASTWorker::create(PathRef FileName, - TUScheduler::ASTCache &IdleASTs, - AsyncTaskRunner *Tasks, Semaphore &Barrier, - steady_clock::duration UpdateDebounce, - std::shared_ptr<PCHContainerOperations> PCHs, - bool StorePreamblesInMemory, - ParsingCallbacks &Callbacks) { - std::shared_ptr<ASTWorker> Worker(new ASTWorker( - FileName, IdleASTs, Barrier, /*RunSync=*/!Tasks, UpdateDebounce, - std::move(PCHs), StorePreamblesInMemory, Callbacks)); +ASTWorkerHandle +ASTWorker::create(PathRef FileName, const GlobalCompilationDatabase &CDB, + TUScheduler::ASTCache &IdleASTs, AsyncTaskRunner *Tasks, + Semaphore &Barrier, steady_clock::duration UpdateDebounce, + bool StorePreamblesInMemory, ParsingCallbacks &Callbacks) { + std::shared_ptr<ASTWorker> Worker( + new ASTWorker(FileName, CDB, IdleASTs, Barrier, /*RunSync=*/!Tasks, + UpdateDebounce, StorePreamblesInMemory, Callbacks)); if (Tasks) Tasks->runAsync("worker:" + llvm::sys::path::filename(FileName), [Worker]() { Worker->run(); }); @@ -327,17 +333,23 @@ ASTWorkerHandle ASTWorker::create(PathRef FileName, return ASTWorkerHandle(std::move(Worker)); } -ASTWorker::ASTWorker(PathRef FileName, TUScheduler::ASTCache &LRUCache, - Semaphore &Barrier, bool RunSync, - steady_clock::duration UpdateDebounce, - std::shared_ptr<PCHContainerOperations> PCHs, +ASTWorker::ASTWorker(PathRef FileName, const GlobalCompilationDatabase &CDB, + TUScheduler::ASTCache &LRUCache, Semaphore &Barrier, + bool RunSync, steady_clock::duration UpdateDebounce, bool StorePreamblesInMemory, ParsingCallbacks &Callbacks) : IdleASTs(LRUCache), RunSync(RunSync), UpdateDebounce(UpdateDebounce), - FileName(FileName), StorePreambleInMemory(StorePreamblesInMemory), - Callbacks(Callbacks), - PCHs(std::move(PCHs)), Status{TUAction(TUAction::Idle, ""), - TUStatus::BuildDetails()}, - Barrier(Barrier), Done(false) {} + FileName(FileName), CDB(CDB), + StorePreambleInMemory(StorePreamblesInMemory), + Callbacks(Callbacks), Status{TUAction(TUAction::Idle, ""), + TUStatus::BuildDetails()}, + Barrier(Barrier), Done(false) { + auto Inputs = std::make_shared<ParseInputs>(); + // Set a fallback command because compile command can be accessed before + // `Inputs` is initialized. Other fields are only used after initialization + // from client inputs. + Inputs->CompileCommand = CDB.getFallbackCommand(FileName); + FileInputs = std::move(Inputs); +} ASTWorker::~ASTWorker() { // Make sure we remove the cached AST, if any. @@ -352,17 +364,32 @@ ASTWorker::~ASTWorker() { void ASTWorker::update(ParseInputs Inputs, WantDiagnostics WantDiags) { llvm::StringRef TaskName = "Update"; auto Task = [=]() mutable { + // Get the actual command as `Inputs` does not have a command. + // FIXME: some build systems like Bazel will take time to preparing + // environment to build the file, it would be nice if we could emit a + // "PreparingBuild" status to inform users, it is non-trivial given the + // current implementation. + if (auto Cmd = CDB.getCompileCommand(FileName)) + Inputs.CompileCommand = *Cmd; + else + // FIXME: consider using old command if it's not a fallback one. + Inputs.CompileCommand = CDB.getFallbackCommand(FileName); + auto PrevInputs = getCurrentFileInputs(); // Will be used to check if we can avoid rebuilding the AST. bool InputsAreTheSame = - std::tie(FileInputs.CompileCommand, FileInputs.Contents) == + std::tie(PrevInputs->CompileCommand, PrevInputs->Contents) == std::tie(Inputs.CompileCommand, Inputs.Contents); - tooling::CompileCommand OldCommand = std::move(FileInputs.CompileCommand); + tooling::CompileCommand OldCommand = PrevInputs->CompileCommand; bool PrevDiagsWereReported = DiagsWereReported; - FileInputs = Inputs; + { + std::lock_guard<std::mutex> Lock(Mutex); + FileInputs = std::make_shared<ParseInputs>(Inputs); + } DiagsWereReported = false; emitTUStatus({TUAction::BuildingPreamble, TaskName}); - log("Updating file {0} with command [{1}] {2}", FileName, + log("Updating file {0} with command {1}\n[{2}]\n{3}", FileName, + Inputs.CompileCommand.Heuristic, Inputs.CompileCommand.Directory, llvm::join(Inputs.CompileCommand.CommandLine, " ")); // Rebuild the preamble and the AST. @@ -384,10 +411,11 @@ void ASTWorker::update(ParseInputs Inputs, WantDiagnostics WantDiags) { std::shared_ptr<const PreambleData> OldPreamble = getPossiblyStalePreamble(); std::shared_ptr<const PreambleData> NewPreamble = buildPreamble( - FileName, *Invocation, OldPreamble, OldCommand, Inputs, PCHs, + FileName, *Invocation, OldPreamble, OldCommand, Inputs, StorePreambleInMemory, - [this](ASTContext &Ctx, std::shared_ptr<clang::Preprocessor> PP) { - Callbacks.onPreambleAST(FileName, Ctx, std::move(PP)); + [this](ASTContext &Ctx, std::shared_ptr<clang::Preprocessor> PP, + const CanonicalIncludes &CanonIncludes) { + Callbacks.onPreambleAST(FileName, Ctx, std::move(PP), CanonIncludes); }); bool CanReuseAST = InputsAreTheSame && (OldPreamble == NewPreamble); @@ -440,7 +468,7 @@ void ASTWorker::update(ParseInputs Inputs, WantDiagnostics WantDiags) { llvm::Optional<std::unique_ptr<ParsedAST>> AST = IdleASTs.take(this); if (!AST) { llvm::Optional<ParsedAST> NewAST = - buildAST(FileName, std::move(Invocation), Inputs, NewPreamble, PCHs); + buildAST(FileName, std::move(Invocation), Inputs, NewPreamble); AST = NewAST ? llvm::make_unique<ParsedAST>(std::move(*NewAST)) : nullptr; if (!(*AST)) { // buildAST fails. TUStatus::BuildDetails Details; @@ -480,15 +508,16 @@ void ASTWorker::runWithAST( if (isCancelled()) return Action(llvm::make_error<CancelledError>()); llvm::Optional<std::unique_ptr<ParsedAST>> AST = IdleASTs.take(this); + auto CurrentInputs = getCurrentFileInputs(); if (!AST) { std::unique_ptr<CompilerInvocation> Invocation = - buildCompilerInvocation(FileInputs); + buildCompilerInvocation(*CurrentInputs); // Try rebuilding the AST. llvm::Optional<ParsedAST> NewAST = Invocation ? buildAST(FileName, llvm::make_unique<CompilerInvocation>(*Invocation), - FileInputs, getPossiblyStalePreamble(), PCHs) + *CurrentInputs, getPossiblyStalePreamble()) : None; AST = NewAST ? llvm::make_unique<ParsedAST>(std::move(*NewAST)) : nullptr; } @@ -499,7 +528,7 @@ void ASTWorker::runWithAST( if (!*AST) return Action(llvm::make_error<llvm::StringError>( "invalid AST", llvm::errc::invalid_argument)); - Action(InputsAndAST{FileInputs, **AST}); + Action(InputsAndAST{*CurrentInputs, **AST}); }; startTask(Name, Bind(Task, std::move(Action)), /*UpdateType=*/None); @@ -541,6 +570,16 @@ void ASTWorker::getCurrentPreamble( void ASTWorker::waitForFirstPreamble() const { PreambleWasBuilt.wait(); } +std::shared_ptr<const ParseInputs> ASTWorker::getCurrentFileInputs() const { + std::unique_lock<std::mutex> Lock(Mutex); + return FileInputs; +} + +tooling::CompileCommand ASTWorker::getCurrentCompileCommand() const { + std::unique_lock<std::mutex> Lock(Mutex); + return FileInputs->CompileCommand; +} + std::size_t ASTWorker::getUsedBytes() const { // Note that we don't report the size of ASTs currently used for processing // the in-flight requests. We used this information for debugging purposes @@ -779,17 +818,16 @@ FileStatus TUStatus::render(PathRef File) const { struct TUScheduler::FileData { /// Latest inputs, passed to TUScheduler::update(). std::string Contents; - tooling::CompileCommand Command; ASTWorkerHandle Worker; }; -TUScheduler::TUScheduler(unsigned AsyncThreadsCount, +TUScheduler::TUScheduler(const GlobalCompilationDatabase &CDB, + unsigned AsyncThreadsCount, bool StorePreamblesInMemory, std::unique_ptr<ParsingCallbacks> Callbacks, std::chrono::steady_clock::duration UpdateDebounce, ASTRetentionPolicy RetentionPolicy) - : StorePreamblesInMemory(StorePreamblesInMemory), - PCHOps(std::make_shared<PCHContainerOperations>()), + : CDB(CDB), StorePreamblesInMemory(StorePreamblesInMemory), Callbacks(Callbacks ? move(Callbacks) : llvm::make_unique<ParsingCallbacks>()), Barrier(AsyncThreadsCount), @@ -828,13 +866,13 @@ void TUScheduler::update(PathRef File, ParseInputs Inputs, if (!FD) { // Create a new worker to process the AST-related tasks. ASTWorkerHandle Worker = ASTWorker::create( - File, *IdleASTs, WorkerThreads ? WorkerThreads.getPointer() : nullptr, - Barrier, UpdateDebounce, PCHOps, StorePreamblesInMemory, *Callbacks); - FD = std::unique_ptr<FileData>(new FileData{ - Inputs.Contents, Inputs.CompileCommand, std::move(Worker)}); + File, CDB, *IdleASTs, + WorkerThreads ? WorkerThreads.getPointer() : nullptr, Barrier, + UpdateDebounce, StorePreamblesInMemory, *Callbacks); + FD = std::unique_ptr<FileData>( + new FileData{Inputs.Contents, std::move(Worker)}); } else { FD->Contents = Inputs.Contents; - FD->Command = Inputs.CompileCommand; } FD->Worker->update(std::move(Inputs), WantDiags); } @@ -866,9 +904,9 @@ void TUScheduler::runWithAST( It->second->Worker->runWithAST(Name, std::move(Action)); } -void TUScheduler::runWithPreamble( - llvm::StringRef Name, PathRef File, PreambleConsistency Consistency, - llvm::unique_function<void(llvm::Expected<InputsAndPreamble>)> Action) { +void TUScheduler::runWithPreamble(llvm::StringRef Name, PathRef File, + PreambleConsistency Consistency, + Callback<InputsAndPreamble> Action) { auto It = Files.find(File); if (It == Files.end()) { Action(llvm::make_error<LSPError>( @@ -882,7 +920,8 @@ void TUScheduler::runWithPreamble( SPAN_ATTACH(Tracer, "file", File); std::shared_ptr<const PreambleData> Preamble = It->second->Worker->getPossiblyStalePreamble(); - Action(InputsAndPreamble{It->second->Contents, It->second->Command, + Action(InputsAndPreamble{It->second->Contents, + It->second->Worker->getCurrentCompileCommand(), Preamble.get()}); return; } @@ -901,19 +940,21 @@ void TUScheduler::runWithPreamble( } std::shared_ptr<const ASTWorker> Worker = It->second->Worker.lock(); - auto Task = [Worker, this](std::string Name, std::string File, - std::string Contents, - tooling::CompileCommand Command, Context Ctx, - decltype(ConsistentPreamble) ConsistentPreamble, - decltype(Action) Action) mutable { + auto Task = [Worker, Consistency, + this](std::string Name, std::string File, std::string Contents, + tooling::CompileCommand Command, Context Ctx, + decltype(ConsistentPreamble) ConsistentPreamble, + decltype(Action) Action) mutable { std::shared_ptr<const PreambleData> Preamble; if (ConsistentPreamble.valid()) { Preamble = ConsistentPreamble.get(); } else { - // We don't want to be running preamble actions before the preamble was - // built for the first time. This avoids extra work of processing the - // preamble headers in parallel multiple times. - Worker->waitForFirstPreamble(); + if (Consistency != PreambleConsistency::StaleOrAbsent) { + // Wait until the preamble is built for the first time, if preamble is + // required. This avoids extra work of processing the preamble headers + // in parallel multiple times. + Worker->waitForFirstPreamble(); + } Preamble = Worker->getPossiblyStalePreamble(); } @@ -927,7 +968,7 @@ void TUScheduler::runWithPreamble( PreambleTasks->runAsync( "task:" + llvm::sys::path::filename(File), Bind(Task, std::string(Name), std::string(File), It->second->Contents, - It->second->Command, + Worker->getCurrentCompileCommand(), Context::current().derive(kFileBeingProcessed, File), std::move(ConsistentPreamble), std::move(Action))); } |