summaryrefslogtreecommitdiff
path: root/clang-tools-extra/clangd/ClangdUnit.cpp
diff options
context:
space:
mode:
authorIlya Biryukov <ibiryukov@google.com>2018-02-09 10:17:23 +0000
committerIlya Biryukov <ibiryukov@google.com>2018-02-09 10:17:23 +0000
commitbb52c22ad4ff46de537c8ee8ed2f4888639a859b (patch)
treeb98c310ec3dcc2da4bc5a7cbcbe72d27707fb94e /clang-tools-extra/clangd/ClangdUnit.cpp
parent2fb2d6dee12a595557411f8ff33f93c9d2a23b39 (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.cpp431
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,