diff --git a/clang-tools-extra/clangd/index/remote/server/Server.cpp b/clang-tools-extra/clangd/index/remote/server/Server.cpp --- a/clang-tools-extra/clangd/index/remote/server/Server.cpp +++ b/clang-tools-extra/clangd/index/remote/server/Server.cpp @@ -12,15 +12,22 @@ #include "index/Symbol.h" #include "index/remote/marshalling/Marshalling.h" #include "support/Logger.h" +#include "support/Shutdown.h" #include "support/Trace.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Support/Chrono.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Error.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include "llvm/Support/Signals.h" +#include "llvm/Support/VirtualFileSystem.h" +#include #include #include +#include +#include #include "Index.grpc.pb.h" @@ -63,15 +70,10 @@ "server-address", llvm::cl::init("0.0.0.0:50051"), llvm::cl::desc("Address of the invoked server. Defaults to 0.0.0.0:50051")); -std::unique_ptr openIndex(llvm::StringRef Index) { - return loadIndex(Index, /*UseIndex=*/true); -} - class RemoteIndexServer final : public SymbolIndex::Service { public: - RemoteIndexServer(std::unique_ptr Index, - llvm::StringRef IndexRoot) - : Index(std::move(Index)) { + RemoteIndexServer(clangd::SymbolIndex &Index, llvm::StringRef IndexRoot) + : Index(Index) { llvm::SmallString<256> NativePath = IndexRoot; llvm::sys::path::native(NativePath); ProtobufMarshaller = std::unique_ptr(new Marshaller( @@ -91,7 +93,7 @@ } unsigned Sent = 0; unsigned FailedToSend = 0; - Index->lookup(*Req, [&](const clangd::Symbol &Item) { + Index.lookup(*Req, [&](const clangd::Symbol &Item) { auto SerializedItem = ProtobufMarshaller->toProtobuf(Item); if (!SerializedItem) { elog("Unable to convert Symbol to protobuf: {0}", @@ -124,7 +126,7 @@ } unsigned Sent = 0; unsigned FailedToSend = 0; - bool HasMore = Index->fuzzyFind(*Req, [&](const clangd::Symbol &Item) { + bool HasMore = Index.fuzzyFind(*Req, [&](const clangd::Symbol &Item) { auto SerializedItem = ProtobufMarshaller->toProtobuf(Item); if (!SerializedItem) { elog("Unable to convert Symbol to protobuf: {0}", @@ -155,7 +157,7 @@ } unsigned Sent = 0; unsigned FailedToSend = 0; - bool HasMore = Index->refs(*Req, [&](const clangd::Ref &Item) { + bool HasMore = Index.refs(*Req, [&](const clangd::Ref &Item) { auto SerializedItem = ProtobufMarshaller->toProtobuf(Item); if (!SerializedItem) { elog("Unable to convert Ref to protobuf: {0}", @@ -188,7 +190,7 @@ } unsigned Sent = 0; unsigned FailedToSend = 0; - Index->relations( + Index.relations( *Req, [&](const SymbolID &Subject, const clangd::Symbol &Object) { auto SerializedItem = ProtobufMarshaller->toProtobuf(Subject, Object); if (!SerializedItem) { @@ -210,22 +212,57 @@ return grpc::Status::OK; } - std::unique_ptr Index; std::unique_ptr ProtobufMarshaller; + clangd::SymbolIndex &Index; }; -void runServer(std::unique_ptr Index, - const std::string &ServerAddress) { - RemoteIndexServer Service(std::move(Index), IndexRoot); +// Detect changes in \p IndexPath file and load new versions of the index +// whenever they become available. +void hotReload(clangd::SwapIndex *Index, llvm::StringRef IndexPath, + llvm::sys::fs::file_status &LastStatus) { + llvm::sys::fs::file_status Status; + const auto EC = llvm::sys::fs::status(IndexPath, Status, /*Follow=*/true); + if (EC) + return; + const auto NewModificationTime = Status.getLastModificationTime(); + // Current index is newer than the one before: no reload is needed. + if (NewModificationTime <= LastStatus.getLastModificationTime()) + return; + vlog("Found new index version: existing index was modified at {0}, new " + "index was modified at {1}. Attempting to reload.", + LastStatus.getLastModificationTime(), NewModificationTime); + std::unique_ptr NewIndex = loadIndex(IndexPath); + if (!NewIndex) { + vlog("Failed to load new index. Old index will be served."); + return; + } + Index->reset(std::move(NewIndex)); + log("New index version loaded. Last modification time: {0}.", + NewModificationTime); + LastStatus = Status; +} + +void runServer(clangd::SymbolIndex *Index, llvm::StringRef ServerAddress, + llvm::StringRef IndexPath) { + RemoteIndexServer Service(*Index, IndexRoot); grpc::EnableDefaultHealthCheckService(true); grpc::ServerBuilder Builder; - Builder.AddListeningPort(ServerAddress, grpc::InsecureServerCredentials()); + Builder.AddListeningPort(ServerAddress.str(), + grpc::InsecureServerCredentials()); Builder.RegisterService(&Service); std::unique_ptr Server(Builder.BuildAndStart()); log("Server listening on {0}", ServerAddress); + std::thread ServerShutdownWatcher([&]() { + const auto WATCHER_FREQUENCY = std::chrono::seconds(5); + while (!clang::clangd::shutdownRequested()) + std::this_thread::sleep_for(WATCHER_FREQUENCY); + Server->Shutdown(); + }); + Server->Wait(); + ServerShutdownWatcher.join(); } } // namespace @@ -239,6 +276,7 @@ using namespace clang::clangd::remote; llvm::cl::ParseCommandLineOptions(argc, argv, Overview); llvm::sys::PrintStackTraceOnErrorSignal(argv[0]); + llvm::sys::SetInterruptFunction(&clang::clangd::requestShutdown); if (!llvm::sys::path::is_absolute(IndexRoot)) { llvm::errs() << "Index root should be an absolute path.\n"; @@ -273,12 +311,27 @@ if (Tracer) TracingSession.emplace(*Tracer); - std::unique_ptr Index = openIndex(IndexPath); + auto Index = std::make_unique( + clang::clangd::loadIndex(IndexPath)); if (!Index) { llvm::errs() << "Failed to open the index.\n"; return -1; } - runServer(std::move(Index), ServerAddress); + std::thread HotReloadThread([&]() { + llvm::sys::fs::file_status LastStatus; + const auto EC = + llvm::sys::fs::status(IndexPath, LastStatus, /*Follow=*/true); + assert(!EC); + const auto REFRESH_FREQUENCY = std::chrono::seconds(90); + while (!clang::clangd::shutdownRequested()) { + std::this_thread::sleep_for(REFRESH_FREQUENCY); + hotReload(Index.get(), llvm::StringRef(IndexPath), LastStatus); + } + }); + + runServer(Index.get(), ServerAddress, IndexPath); + + HotReloadThread.join(); }