Restored r303067 and fixed failing test.
Summary:
This commit restores r303067(reverted by r303094) and fixes the 'formatting.test'
failure.
The failure is due to destructors of `ClangdLSPServer`'s fields(`FixItsMap` and
`FixItsMutex`) being called before destructor of `Server`. It led to the worker
thread calling `consumeDiagnostics` after `FixItsMutex` and `FixItsMap`
destructors were called.
Also, clangd is now run with '-run-synchronously' flag in 'formatting.test'.
Reviewers: bkramer, krasimir
Reviewed By: krasimir
Subscribers: mgorny, cfe-commits, klimek
Differential Revision: https://reviews.llvm.org/D33233
llvm-svn: 303151
diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp
new file mode 100644
index 0000000..89e935a
--- /dev/null
+++ b/clang-tools-extra/clangd/ClangdServer.cpp
@@ -0,0 +1,149 @@
+//===--- ClangdServer.cpp - Main clangd server code --------------*- C++-*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===-------------------------------------------------------------------===//
+
+#include "ClangdServer.h"
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/CompilerInvocation.h"
+#include "clang/Tooling/CompilationDatabase.h"
+#include "llvm/Support/FileSystem.h"
+
+using namespace clang::clangd;
+
+WorkerRequest::WorkerRequest(WorkerRequestKind Kind, Path File,
+ DocVersion Version)
+ : Kind(Kind), File(File), Version(Version) {}
+
+ClangdScheduler::ClangdScheduler(ClangdServer &Server, bool RunSynchronously)
+ : RunSynchronously(RunSynchronously) {
+ if (RunSynchronously) {
+ // Don't start the worker thread if we're running synchronously
+ return;
+ }
+
+ // Initialize Worker in ctor body, rather than init list to avoid potentially
+ // using not-yet-initialized members
+ Worker = std::thread([&Server, this]() {
+ while (true) {
+ WorkerRequest Request;
+
+ // Pick request from the queue
+ {
+ std::unique_lock<std::mutex> Lock(Mutex);
+ // Wait for more requests.
+ RequestCV.wait(Lock, [this] { return !RequestQueue.empty() || Done; });
+ if (Done)
+ return;
+
+ assert(!RequestQueue.empty() && "RequestQueue was empty");
+
+ Request = std::move(RequestQueue.back());
+ RequestQueue.pop_back();
+
+ // Skip outdated requests
+ if (Request.Version != Server.DraftMgr.getVersion(Request.File)) {
+ // FIXME(ibiryukov): Logging
+ // Output.log("Version for " + Twine(Request.File) +
+ // " in request is outdated, skipping request\n");
+ continue;
+ }
+ } // unlock Mutex
+
+ Server.handleRequest(std::move(Request));
+ }
+ });
+}
+
+ClangdScheduler::~ClangdScheduler() {
+ if (RunSynchronously)
+ return; // no worker thread is running in that case
+
+ {
+ std::lock_guard<std::mutex> Lock(Mutex);
+ // Wake up the worker thread
+ Done = true;
+ RequestCV.notify_one();
+ } // unlock Mutex
+ Worker.join();
+}
+
+void ClangdScheduler::enqueue(ClangdServer &Server, WorkerRequest Request) {
+ if (RunSynchronously) {
+ Server.handleRequest(Request);
+ return;
+ }
+
+ std::lock_guard<std::mutex> Lock(Mutex);
+ RequestQueue.push_back(Request);
+ RequestCV.notify_one();
+}
+
+ClangdServer::ClangdServer(std::unique_ptr<GlobalCompilationDatabase> CDB,
+ std::unique_ptr<DiagnosticsConsumer> DiagConsumer,
+ bool RunSynchronously)
+ : CDB(std::move(CDB)), DiagConsumer(std::move(DiagConsumer)),
+ PCHs(std::make_shared<PCHContainerOperations>()),
+ WorkScheduler(*this, RunSynchronously) {}
+
+void ClangdServer::addDocument(PathRef File, StringRef Contents) {
+ DocVersion NewVersion = DraftMgr.updateDraft(File, Contents);
+ WorkScheduler.enqueue(
+ *this, WorkerRequest(WorkerRequestKind::ParseAndPublishDiagnostics, File,
+ NewVersion));
+}
+
+void ClangdServer::removeDocument(PathRef File) {
+ auto NewVersion = DraftMgr.removeDraft(File);
+ WorkScheduler.enqueue(
+ *this, WorkerRequest(WorkerRequestKind::RemoveDocData, File, NewVersion));
+}
+
+std::vector<CompletionItem> ClangdServer::codeComplete(PathRef File,
+ Position Pos) {
+ auto FileContents = DraftMgr.getDraft(File);
+ assert(FileContents.Draft && "codeComplete is called for non-added document");
+
+ std::vector<CompletionItem> Result;
+ Units.runOnUnitWithoutReparse(
+ File, *FileContents.Draft, *CDB, PCHs, [&](ClangdUnit &Unit) {
+ Result = Unit.codeComplete(*FileContents.Draft, Pos);
+ });
+ return Result;
+}
+
+std::string ClangdServer::getDocument(PathRef File) {
+ auto draft = DraftMgr.getDraft(File);
+ assert(draft.Draft && "File is not tracked, cannot get contents");
+ return *draft.Draft;
+}
+
+void ClangdServer::handleRequest(WorkerRequest Request) {
+ switch (Request.Kind) {
+ case WorkerRequestKind::ParseAndPublishDiagnostics: {
+ auto FileContents = DraftMgr.getDraft(Request.File);
+ if (FileContents.Version != Request.Version)
+ return; // This request is outdated, do nothing
+
+ assert(FileContents.Draft &&
+ "No contents inside a file that was scheduled for reparse");
+ Units.runOnUnit(Request.File, *FileContents.Draft, *CDB, PCHs,
+ [&](ClangdUnit const &Unit) {
+ DiagConsumer->onDiagnosticsReady(
+ Request.File, Unit.getLocalDiagnostics());
+ });
+ break;
+ }
+ case WorkerRequestKind::RemoveDocData:
+ if (Request.Version != DraftMgr.getVersion(Request.File))
+ return; // This request is outdated, do nothing
+
+ Units.removeUnitIfPresent(Request.File);
+ break;
+ }
+}