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,25 @@ #include "index/Symbol.h" #include "index/remote/marshalling/Marshalling.h" #include "support/Logger.h" +#include "support/Shutdown.h" +#include "support/ThreadsafeFS.h" #include "support/Trace.h" +#include "llvm/ADT/IntrusiveRefCntPtr.h" +#include "llvm/ADT/None.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 +73,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 +96,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 +129,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 +160,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 +193,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 +215,56 @@ 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::vfs::Status &LastStatus, + llvm::IntrusiveRefCntPtr &FS) { + auto Status = FS->status(IndexPath); + // Requested file is same as loaded index: no reload is needed. + if (!Status || (Status->getLastModificationTime() == + LastStatus.getLastModificationTime() && + Status->getSize() == LastStatus.getSize())) + return; + vlog("Found different index version: existing index was modified at {0}, new " + "index was modified at {1}. Attempting to reload.", + LastStatus.getLastModificationTime(), Status->getLastModificationTime()); + std::unique_ptr NewIndex = loadIndex(IndexPath); + if (!NewIndex) { + elog("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}, size: {1} bytes.", + Status->getLastModificationTime(), Status->getSize()); + LastStatus = *Status; +} + +void runServerAndWait(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([&]() { + static constexpr auto WatcherFrequency = std::chrono::seconds(5); + while (!clang::clangd::shutdownRequested()) + std::this_thread::sleep_for(WatcherFrequency); + Server->Shutdown(); + }); + Server->Wait(); + ServerShutdownWatcher.join(); } } // namespace @@ -239,6 +278,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 +313,32 @@ if (Tracer) TracingSession.emplace(*Tracer); - std::unique_ptr Index = openIndex(IndexPath); + clang::clangd::RealThreadsafeFS TFS; + auto FS = TFS.view(llvm::None); + auto Status = FS->status(IndexPath); + if (!Status) { + elog("{0} does not exist.", IndexPath); + return Status.getError().value(); + } + + 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([&Index, &Status, &FS]() { + llvm::vfs::Status LastStatus = *Status; + static constexpr auto RefreshFrequency = std::chrono::seconds(90); + while (!clang::clangd::shutdownRequested()) { + hotReload(*Index, llvm::StringRef(IndexPath), LastStatus, FS); + std::this_thread::sleep_for(RefreshFrequency); + } + }); + + runServerAndWait(*Index, ServerAddress, IndexPath); + + HotReloadThread.join(); }