#include "clang/Tooling/CompilationDatabase.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/raw_ostream.h"
+#include <future>
using namespace clang;
using namespace clang::clangd;
return {Lines, Cols};
}
-WorkerRequest::WorkerRequest(WorkerRequestKind Kind, Path File,
- DocVersion Version)
- : Kind(Kind), File(File), Version(Version) {}
-
-ClangdScheduler::ClangdScheduler(ClangdServer &Server, bool RunSynchronously)
+ClangdScheduler::ClangdScheduler(bool RunSynchronously)
: RunSynchronously(RunSynchronously) {
if (RunSynchronously) {
// Don't start the worker thread if we're running synchronously
// Initialize Worker in ctor body, rather than init list to avoid potentially
// using not-yet-initialized members
- Worker = std::thread([&Server, this]() {
+ Worker = std::thread([this]() {
while (true) {
- WorkerRequest Request;
+ std::function<void()> Request;
// Pick request from the queue
{
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;
- }
+ // We process requests starting from the front of the queue. Users of
+ // ClangdScheduler have a way to prioritise their requests by putting
+ // them to the either side of the queue (using either addToEnd or
+ // addToFront).
+ Request = std::move(RequestQueue.front());
+ RequestQueue.pop_front();
} // unlock Mutex
- Server.handleRequest(std::move(Request));
+ Request();
}
});
}
std::lock_guard<std::mutex> Lock(Mutex);
// Wake up the worker thread
Done = true;
- RequestCV.notify_one();
} // unlock Mutex
+ RequestCV.notify_one();
Worker.join();
}
-void ClangdScheduler::enqueue(ClangdServer &Server, WorkerRequest Request) {
+void ClangdScheduler::addToFront(std::function<void()> Request) {
if (RunSynchronously) {
- Server.handleRequest(Request);
+ Request();
return;
}
- std::lock_guard<std::mutex> Lock(Mutex);
- RequestQueue.push_back(Request);
+ {
+ std::lock_guard<std::mutex> Lock(Mutex);
+ RequestQueue.push_front(Request);
+ }
+ RequestCV.notify_one();
+}
+
+void ClangdScheduler::addToEnd(std::function<void()> Request) {
+ if (RunSynchronously) {
+ Request();
+ return;
+ }
+
+ {
+ std::lock_guard<std::mutex> Lock(Mutex);
+ RequestQueue.push_back(Request);
+ }
RequestCV.notify_one();
}
bool RunSynchronously)
: CDB(std::move(CDB)), DiagConsumer(std::move(DiagConsumer)),
PCHs(std::make_shared<PCHContainerOperations>()),
- WorkScheduler(*this, RunSynchronously) {}
+ WorkScheduler(RunSynchronously) {}
void ClangdServer::addDocument(PathRef File, StringRef Contents) {
- DocVersion NewVersion = DraftMgr.updateDraft(File, Contents);
- WorkScheduler.enqueue(
- *this, WorkerRequest(WorkerRequestKind::ParseAndPublishDiagnostics, File,
- NewVersion));
+ DocVersion Version = DraftMgr.updateDraft(File, Contents);
+ Path FileStr = File;
+ WorkScheduler.addToFront([this, FileStr, Version]() {
+ auto FileContents = DraftMgr.getDraft(FileStr);
+ if (FileContents.Version != Version)
+ return; // This request is outdated, do nothing
+
+ assert(FileContents.Draft &&
+ "No contents inside a file that was scheduled for reparse");
+ Units.runOnUnit(
+ FileStr, *FileContents.Draft, *CDB, PCHs, [&](ClangdUnit const &Unit) {
+ DiagConsumer->onDiagnosticsReady(FileStr, Unit.getLocalDiagnostics());
+ });
+ });
}
void ClangdServer::removeDocument(PathRef File) {
- auto NewVersion = DraftMgr.removeDraft(File);
- WorkScheduler.enqueue(
- *this, WorkerRequest(WorkerRequestKind::RemoveDocData, File, NewVersion));
+ auto Version = DraftMgr.removeDraft(File);
+ Path FileStr = File;
+ WorkScheduler.addToFront([this, FileStr, Version]() {
+ if (Version != DraftMgr.getVersion(FileStr))
+ return; // This request is outdated, do nothing
+
+ Units.removeUnitIfPresent(FileStr);
+ });
}
std::vector<CompletionItem> ClangdServer::codeComplete(PathRef File,
});
return Result;
}
+
std::vector<tooling::Replacement> ClangdServer::formatRange(PathRef File,
Range Rng) {
std::string Code = getDocument(File);
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
+std::string ClangdServer::dumpAST(PathRef File) {
+ std::promise<std::string> DumpPromise;
+ auto DumpFuture = DumpPromise.get_future();
+ auto Version = DraftMgr.getVersion(File);
- 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
+ WorkScheduler.addToEnd([this, &DumpPromise, File, Version]() {
+ assert(DraftMgr.getVersion(File) == Version && "Version has changed");
- Units.removeUnitIfPresent(Request.File);
- break;
- }
+ Units.runOnExistingUnit(File, [&DumpPromise](ClangdUnit &Unit) {
+ std::string Result;
+
+ llvm::raw_string_ostream ResultOS(Result);
+ Unit.dumpAST(ResultOS);
+ ResultOS.flush();
+
+ DumpPromise.set_value(std::move(Result));
+ });
+ });
+ return DumpFuture.get();
}
#include "Protocol.h"
#include <condition_variable>
+#include <functional>
#include <mutex>
#include <string>
#include <thread>
std::vector<DiagWithFixIts> Diagnostics) = 0;
};
-enum class WorkerRequestKind { ParseAndPublishDiagnostics, RemoveDocData };
-
-/// A request to the worker thread
-class WorkerRequest {
-public:
- WorkerRequest() = default;
- WorkerRequest(WorkerRequestKind Kind, Path File, DocVersion Version);
-
- WorkerRequestKind Kind;
- Path File;
- DocVersion Version;
-};
-
class ClangdServer;
/// Handles running WorkerRequests of ClangdServer on a separate threads.
/// Currently runs only one worker thread.
class ClangdScheduler {
public:
- ClangdScheduler(ClangdServer &Server, bool RunSynchronously);
+ ClangdScheduler(bool RunSynchronously);
~ClangdScheduler();
- /// Enqueue WorkerRequest to be run on a worker thread
- void enqueue(ClangdServer &Server, WorkerRequest Request);
+ /// Add \p Request to the start of the queue. \p Request will be run on a
+ /// separate worker thread.
+ /// \p Request is scheduled to be executed before all currently added
+ /// requests.
+ void addToFront(std::function<void()> Request);
+ /// Add \p Request to the end of the queue. \p Request will be run on a
+ /// separate worker thread.
+ /// \p Request is scheduled to be executed after all currently added
+ /// requests.
+ void addToEnd(std::function<void()> Request);
private:
bool RunSynchronously;
std::thread Worker;
/// Setting Done to true will make the worker thread terminate.
bool Done = false;
- /// A LIFO queue of requests. Note that requests are discarded if the
- /// `version` field is not equal to the one stored inside DraftStore.
+ /// A queue of requests.
/// FIXME(krasimir): code completion should always have priority over parsing
/// for diagnostics.
- std::deque<WorkerRequest> RequestQueue;
+ std::deque<std::function<void()>> RequestQueue;
/// Condition variable to wake up the worker thread.
std::condition_variable RequestCV;
};
/// conversions in outside code, maybe there's a way to get rid of it.
std::string getDocument(PathRef File);
-private:
- friend class ClangdScheduler;
-
- /// This function is called on a worker thread.
- void handleRequest(WorkerRequest Request);
+ /// Only for testing purposes.
+ /// Waits until all requests to worker thread are finished and dumps AST for
+ /// \p File. \p File must be in the list of added documents.
+ std::string dumpAST(PathRef File);
+private:
std::unique_ptr<GlobalCompilationDatabase> CDB;
std::unique_ptr<DiagnosticsConsumer> DiagConsumer;
DraftStore DraftMgr;