[clang-tools-extra] r324725 - [clangd] Remove threading-related code from ClangdUnit.h

Ilya Biryukov via cfe-commits cfe-commits at lists.llvm.org
Fri Feb 9 02:17:24 PST 2018


Author: ibiryukov
Date: Fri Feb  9 02:17:23 2018
New Revision: 324725

URL: http://llvm.org/viewvc/llvm-project?rev=324725&view=rev
Log:
[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

Modified:
    clang-tools-extra/trunk/clangd/ClangdServer.h
    clang-tools-extra/trunk/clangd/ClangdUnit.cpp
    clang-tools-extra/trunk/clangd/ClangdUnit.h
    clang-tools-extra/trunk/clangd/TUScheduler.cpp

Modified: clang-tools-extra/trunk/clangd/ClangdServer.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdServer.h?rev=324725&r1=324724&r2=324725&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ClangdServer.h (original)
+++ clang-tools-extra/trunk/clangd/ClangdServer.h Fri Feb  9 02:17:23 2018
@@ -25,6 +25,7 @@
 #include "llvm/ADT/Optional.h"
 #include "llvm/ADT/StringRef.h"
 #include <functional>
+#include <future>
 #include <string>
 #include <type_traits>
 #include <utility>

Modified: clang-tools-extra/trunk/clangd/ClangdUnit.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdUnit.cpp?rev=324725&r1=324724&r2=324725&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ClangdUnit.cpp (original)
+++ clang-tools-extra/trunk/clangd/ClangdUnit.cpp Fri Feb  9 02:17:23 2018
@@ -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::
                  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<con
   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, " "));
+
+  std::vector<const char *> ArgStrs;
+  for (const auto &S : Inputs.CompileCommand.CommandLine)
+    ArgStrs.push_back(S.c_str());
 
-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");
+  Inputs.FS->setCurrentWorkingDirectory(Inputs.CompileCommand.Directory);
 
-    std::unique_ptr<llvm::MemoryBuffer> ContentsBuffer =
-        llvm::MemoryBuffer::getMemBufferCopy(Inputs.Contents, That->FileName);
+  // 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");
+
+  std::unique_ptr<llvm::MemoryBuffer> ContentsBuffer =
+      llvm::MemoryBuffer::getMemBufferCopy(Inputs.Contents, FileName);
+
+  // Compute updated Preamble.
+  std::shared_ptr<const PreambleData> NewPreamble =
+      rebuildPreamble(*CI, Inputs.FS, *ContentsBuffer);
+
+  // 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);
+  }
 
-    // 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) {
+  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());
-    }
-
-    // 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);
-    }
-
-    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();
-    }
-
-    // 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.
-
-      That->ASTMemUsage = NewAST ? NewAST->getUsedBytes() : 0;
-      That->ASTPromise.set_value(
-          std::make_shared<ParsedASTWrapper>(std::move(NewAST)));
-    } // unlock Mutex
-
-    return Diagnostics;
-  };
-
-  return BindWithForward(FinishRebuild, std::move(Inputs));
-}
+    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,

Modified: clang-tools-extra/trunk/clangd/ClangdUnit.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdUnit.h?rev=324725&r1=324724&r2=324725&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ClangdUnit.h (original)
+++ clang-tools-extra/trunk/clangd/ClangdUnit.h Fri Feb  9 02:17:23 2018
@@ -18,10 +18,9 @@
 #include "clang/Serialization/ASTBitCodes.h"
 #include "clang/Tooling/CompilationDatabase.h"
 #include "clang/Tooling/Core/Replacement.h"
-#include <atomic>
-#include <future>
 #include <memory>
-#include <mutex>
+#include <string>
+#include <vector>
 
 namespace llvm {
 class raw_ostream;
@@ -126,151 +125,44 @@ private:
   bool PreambleDeclsDeserialized;
 };
 
-// Provides thread-safe access to ParsedAST.
-class ParsedASTWrapper {
-public:
-  ParsedASTWrapper(ParsedASTWrapper &&Wrapper);
-  ParsedASTWrapper(llvm::Optional<ParsedAST> AST);
-
-  /// Runs \p F on wrapped ParsedAST under lock. Ensures it is not accessed
-  /// concurrently.
-  template <class Func> void runUnderLock(Func F) const {
-    std::lock_guard<std::mutex> Lock(Mutex);
-    F(AST ? AST.getPointer() : nullptr);
-  }
-
-private:
-  // This wrapper is used as an argument to std::shared_future (and it returns a
-  // const ref in get()), but we need to have non-const ref in order to
-  // implement some features.
-  mutable std::mutex Mutex;
-  mutable llvm::Optional<ParsedAST> AST;
-};
-
 using ASTParsedCallback = std::function<void(PathRef Path, ParsedAST *)>;
 
 /// Manages resources, required by clangd. Allows to rebuild file with new
 /// contents, and provides AST and Preamble for it.
-/// NOTE: Threading-related bits of CppFile are now deprecated and will be
-/// removed soon.
-class CppFile : public std::enable_shared_from_this<CppFile> {
+class CppFile {
 public:
-  // We only allow to create CppFile as shared_ptr, because a future returned by
-  // deferRebuild will hold references to it.
-  static std::shared_ptr<CppFile>
-  Create(PathRef FileName, bool StorePreamblesInMemory,
-         std::shared_ptr<PCHContainerOperations> PCHs,
-         ASTParsedCallback ASTCallback);
-
-private:
   CppFile(PathRef FileName, bool StorePreamblesInMemory,
           std::shared_ptr<PCHContainerOperations> PCHs,
           ASTParsedCallback ASTCallback);
 
-public:
-  CppFile(CppFile const &) = delete;
-  CppFile(CppFile &&) = delete;
-
-  /// Cancels a scheduled rebuild, if any, and sets AST and Preamble to nulls.
-  /// If a rebuild is in progress, will wait for it to finish.
-  void cancelRebuild();
-
-  /// Similar to deferRebuild, but sets both Preamble and AST to nulls instead
-  /// of doing an actual parsing. Returned function is a deferred computation
-  /// that will wait for any ongoing rebuilds to finish and actually set the AST
-  /// and Preamble to nulls. It can be run on a different thread. This function
-  /// is useful to cancel ongoing rebuilds, if any, before removing CppFile.
-  /// DEPRECATED. This function will be removed soon, please do not use it.
-  UniqueFunction<void()> deferCancelRebuild();
-
-  /// Rebuild AST and Preamble synchronously on the calling thread.
-  /// Returns a list of diagnostics or a llvm::None, if another rebuild was
-  /// requested in parallel (effectively cancelling this rebuild) before
-  /// diagnostics were produced.
+  /// Rebuild the AST and the preamble.
+  /// Returns a list of diagnostics or llvm::None, if an error occured.
   llvm::Optional<std::vector<DiagWithFixIts>> rebuild(ParseInputs &&Inputs);
-
-  /// Schedule a rebuild and return a deferred computation that will finish the
-  /// rebuild, that can be called on a different thread.
-  /// After calling this method, resources, available via futures returned by
-  /// getPreamble() and getAST(), will be waiting for rebuild to finish. A
-  /// continuation fininshing rebuild, returned by this function, must be
-  /// computed(i.e., operator() must be called on it) in order to make those
-  /// resources ready. If deferRebuild is called again before the rebuild is
-  /// finished (either because returned future had not been called or because it
-  /// had not returned yet), the previous rebuild request is cancelled and the
-  /// resource futures (returned by getPreamble() or getAST()) that were not
-  /// ready will be waiting for the last rebuild to finish instead.
-  /// The future to finish rebuild returns a list of diagnostics built during
-  /// reparse, or None, if another deferRebuild was called before this
-  /// rebuild was finished.
-  /// DEPRECATED. This function will be removed soon, please do not use it.
-  UniqueFunction<llvm::Optional<std::vector<DiagWithFixIts>>()>
-  deferRebuild(ParseInputs &&Inputs);
-
-  /// Returns a future to get the most fresh PreambleData for a file. The
-  /// future will wait until the Preamble is rebuilt.
-  std::shared_future<std::shared_ptr<const PreambleData>> getPreamble() const;
-  /// Return some preamble for a file. It might be stale, but won't wait for
-  /// rebuild to finish.
-  std::shared_ptr<const PreambleData> getPossiblyStalePreamble() const;
-
-  /// Returns a future to get the most fresh AST for a file. Returned AST is
-  /// wrapped to prevent concurrent accesses.
-  /// We use std::shared_ptr here because MVSC fails to compile non-copyable
-  /// classes as template arguments of promise/future. It is guaranteed to
-  /// always be non-null.
-  std::shared_future<std::shared_ptr<ParsedASTWrapper>> getAST() const;
-
+  /// Returns the last built preamble.
+  const std::shared_ptr<const PreambleData> &getPreamble() const;
+  /// Returns the last built AST.
+  ParsedAST *getAST() const;
   /// Returns an estimated size, in bytes, currently occupied by the AST and the
   /// Preamble.
   std::size_t getUsedBytes() const;
 
 private:
-  /// A helper guard that manages the state of CppFile during rebuild.
-  class RebuildGuard {
-  public:
-    RebuildGuard(CppFile &File, unsigned RequestRebuildCounter);
-    ~RebuildGuard();
-
-    bool wasCancelledBeforeConstruction() const;
-
-  private:
-    CppFile &File;
-    unsigned RequestRebuildCounter;
-    bool WasCancelledBeforeConstruction;
-  };
+  /// Build a new preamble for \p Inputs. If the current preamble can be reused,
+  /// it is returned instead.
+  /// This method is const to ensure we don't incidentally modify any fields.
+  std::shared_ptr<const PreambleData>
+  rebuildPreamble(CompilerInvocation &CI,
+                  IntrusiveRefCntPtr<vfs::FileSystem> FS,
+                  llvm::MemoryBuffer &ContentsBuffer) const;
 
   Path FileName;
   bool StorePreamblesInMemory;
 
-  /// Mutex protects all fields, declared below it, FileName and Command are not
-  /// mutated.
-  mutable std::mutex Mutex;
-  /// A counter to cancel old rebuilds.
-  unsigned RebuildCounter;
-  /// Used to wait when rebuild is finished before starting another one.
-  bool RebuildInProgress;
-  /// Condition variable to indicate changes to RebuildInProgress.
-  std::condition_variable RebuildCond;
-
-  /// Size of the last built AST, in bytes.
-  std::size_t ASTMemUsage;
-  /// Size of the last build Preamble, in bytes.
-  std::size_t PreambleMemUsage;
-
-  /// Promise and future for the latests AST. Fulfilled during rebuild.
-  /// We use std::shared_ptr here because MVSC fails to compile non-copyable
-  /// classes as template arguments of promise/future.
-  std::promise<std::shared_ptr<ParsedASTWrapper>> ASTPromise;
-  std::shared_future<std::shared_ptr<ParsedASTWrapper>> ASTFuture;
-
-  /// Promise and future for the latests Preamble. Fulfilled during rebuild.
-  std::promise<std::shared_ptr<const PreambleData>> PreamblePromise;
-  std::shared_future<std::shared_ptr<const PreambleData>> PreambleFuture;
-  /// Latest preamble that was built. May be stale, but always available without
-  /// waiting for rebuild to finish.
-  std::shared_ptr<const PreambleData> LatestAvailablePreamble;
-  /// Utility class, required by clang.
+  /// The last parsed AST.
+  llvm::Optional<ParsedAST> AST;
+  /// The last built Preamble.
+  std::shared_ptr<const PreambleData> Preamble;
+  /// Utility class required by clang
   std::shared_ptr<PCHContainerOperations> PCHs;
   /// This is called after the file is parsed. This can be nullptr if there is
   /// no callback.

Modified: clang-tools-extra/trunk/clangd/TUScheduler.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/TUScheduler.cpp?rev=324725&r1=324724&r2=324725&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/TUScheduler.cpp (original)
+++ clang-tools-extra/trunk/clangd/TUScheduler.cpp Fri Feb  9 02:17:23 2018
@@ -48,6 +48,7 @@
 #include "llvm/Support/Errc.h"
 #include <memory>
 #include <queue>
+#include <thread>
 
 namespace clang {
 namespace clangd {
@@ -66,7 +67,7 @@ class ASTWorkerHandle;
 /// worker.
 class ASTWorker {
   friend class ASTWorkerHandle;
-  ASTWorker(Semaphore &Barrier, std::shared_ptr<CppFile> AST, bool RunSync);
+  ASTWorker(Semaphore &Barrier, CppFile AST, bool RunSync);
 
 public:
   /// Create a new ASTWorker and return a handle to it.
@@ -75,7 +76,7 @@ public:
   /// synchronously instead. \p Barrier is acquired when processing each
   /// request, it is be used to limit the number of actively running threads.
   static ASTWorkerHandle Create(AsyncTaskRunner *Tasks, Semaphore &Barrier,
-                                std::shared_ptr<CppFile> AST);
+                                CppFile AST);
   ~ASTWorker();
 
   void update(ParseInputs Inputs,
@@ -102,11 +103,14 @@ private:
   const bool RunSync;
   Semaphore &Barrier;
   // AST and FileInputs are only accessed on the processing thread from run().
-  const std::shared_ptr<CppFile> AST;
+  CppFile AST;
   // Inputs, corresponding to the current state of AST.
   ParseInputs FileInputs;
   // Guards members used by both TUScheduler and the worker thread.
   mutable std::mutex Mutex;
+  std::shared_ptr<const PreambleData> LastBuiltPreamble; /* GUARDED_BY(Mutex) */
+  // Result of getUsedBytes() after the last rebuild or read of AST.
+  std::size_t LastASTSize; /* GUARDED_BY(Mutex) */
   // Set to true to signal run() to finish processing.
   bool Done;                           /* GUARDED_BY(Mutex) */
   std::queue<RequestWithCtx> Requests; /* GUARDED_BY(Mutex) */
@@ -159,7 +163,7 @@ private:
 };
 
 ASTWorkerHandle ASTWorker::Create(AsyncTaskRunner *Tasks, Semaphore &Barrier,
-                                  std::shared_ptr<CppFile> AST) {
+                                  CppFile AST) {
   std::shared_ptr<ASTWorker> Worker(
       new ASTWorker(Barrier, std::move(AST), /*RunSync=*/!Tasks));
   if (Tasks)
@@ -168,8 +172,7 @@ ASTWorkerHandle ASTWorker::Create(AsyncT
   return ASTWorkerHandle(std::move(Worker));
 }
 
-ASTWorker::ASTWorker(Semaphore &Barrier, std::shared_ptr<CppFile> AST,
-                     bool RunSync)
+ASTWorker::ASTWorker(Semaphore &Barrier, CppFile AST, bool RunSync)
     : RunSync(RunSync), Barrier(Barrier), AST(std::move(AST)), Done(false) {
   if (RunSync)
     return;
@@ -193,7 +196,14 @@ void ASTWorker::update(
       return;
     }
     FileInputs = Inputs;
-    auto Diags = AST->rebuild(std::move(Inputs));
+    auto Diags = AST.rebuild(std::move(Inputs));
+
+    {
+      std::lock_guard<std::mutex> Lock(Mutex);
+      if (AST.getPreamble())
+        LastBuiltPreamble = AST.getPreamble();
+      LastASTSize = AST.getUsedBytes();
+    }
     // We want to report the diagnostics even if this update was cancelled.
     // It seems more useful than making the clients wait indefinitely if they
     // spam us with updates.
@@ -208,17 +218,18 @@ void ASTWorker::update(
 void ASTWorker::runWithAST(
     UniqueFunction<void(llvm::Expected<InputsAndAST>)> Action) {
   auto Task = [=](decltype(Action) Action) {
-    auto ASTWrapper = this->AST->getAST().get();
-    // FIXME: no need to lock here, cleanup the CppFile interface to get rid of
-    // them.
-    ASTWrapper->runUnderLock([&](ParsedAST *AST) {
-      if (!AST) {
-        Action(llvm::make_error<llvm::StringError>(
-            "invalid AST", llvm::errc::invalid_argument));
-        return;
-      }
-      Action(InputsAndAST{FileInputs, *AST});
-    });
+    ParsedAST *ActualAST = AST.getAST();
+    if (!ActualAST) {
+      Action(llvm::make_error<llvm::StringError>("invalid AST",
+                                                 llvm::errc::invalid_argument));
+      return;
+    }
+    Action(InputsAndAST{FileInputs, *ActualAST});
+
+    // Size of the AST might have changed after reads too, e.g. if some decls
+    // were deserialized from preamble.
+    std::lock_guard<std::mutex> Lock(Mutex);
+    LastASTSize = ActualAST->getUsedBytes();
   };
 
   startTask(BindWithForward(Task, std::move(Action)), /*isUpdate=*/false,
@@ -227,14 +238,13 @@ void ASTWorker::runWithAST(
 
 std::shared_ptr<const PreambleData>
 ASTWorker::getPossiblyStalePreamble() const {
-  return AST->getPossiblyStalePreamble();
+  std::lock_guard<std::mutex> Lock(Mutex);
+  return LastBuiltPreamble;
 }
 
 std::size_t ASTWorker::getUsedBytes() const {
-  // FIXME(ibiryukov): we'll need to take locks here after we remove
-  // thread-safety from CppFile. For now, CppFile is thread-safe and we can
-  // safely call methods on it without acquiring a lock.
-  return AST->getUsedBytes();
+  std::lock_guard<std::mutex> Lock(Mutex);
+  return LastASTSize;
 }
 
 void ASTWorker::stop() {
@@ -343,7 +353,7 @@ void TUScheduler::update(
     // Create a new worker to process the AST-related tasks.
     ASTWorkerHandle Worker = ASTWorker::Create(
         Tasks ? Tasks.getPointer() : nullptr, Barrier,
-        CppFile::Create(File, StorePreamblesInMemory, PCHOps, ASTCallback));
+        CppFile(File, StorePreamblesInMemory, PCHOps, ASTCallback));
     FD = std::unique_ptr<FileData>(new FileData{Inputs, std::move(Worker)});
   } else {
     FD->Inputs = Inputs;




More information about the cfe-commits mailing list