From b122caba6178bc732493e3b9e19c8769079e94d0 Mon Sep 17 00:00:00 2001 From: Yingchi Long Date: Thu, 14 Mar 2024 13:38:23 +0800 Subject: [PATCH] nixd: add eval client --- nixd/libutil/include/nixd/util/AutoHUPPID.h | 4 +- nixd/tools/nixd/meson.build | 3 +- nixd/tools/nixd/src/Controller.h | 2 + nixd/tools/nixd/src/EvalClient.cpp | 50 ++++++++++++++++++ nixd/tools/nixd/src/EvalClient.h | 35 +++++++++++++ nixd/tools/nixd/src/LifeTime.cpp | 15 ++++++ nixd/tools/nixd/src/NixTU.h | 11 ++-- nixd/tools/nixd/src/Support.cpp | 56 +++++++++++++++++++-- 8 files changed, 168 insertions(+), 8 deletions(-) create mode 100644 nixd/tools/nixd/src/EvalClient.cpp create mode 100644 nixd/tools/nixd/src/EvalClient.h diff --git a/nixd/libutil/include/nixd/util/AutoHUPPID.h b/nixd/libutil/include/nixd/util/AutoHUPPID.h index 874713b28..6a7383238 100644 --- a/nixd/libutil/include/nixd/util/AutoHUPPID.h +++ b/nixd/libutil/include/nixd/util/AutoHUPPID.h @@ -10,7 +10,9 @@ class AutoHUPPID { public: AutoHUPPID(pid_t Pid) noexcept : Pid(Pid) {} - ~AutoHUPPID() { kill(Pid, SIGHUP); } + ~AutoHUPPID() { kill(Pid, SIGKILL); } + + operator pid_t() const { return Pid; } }; } // namespace nixd::util diff --git a/nixd/tools/nixd/meson.build b/nixd/tools/nixd/meson.build index 1d35c1a98..3a3c8a674 100644 --- a/nixd/tools/nixd/meson.build +++ b/nixd/tools/nixd/meson.build @@ -2,13 +2,14 @@ nixd_next = executable('nixd', 'src/CodeAction.cpp', 'src/Convert.cpp', 'src/Diagnostics.cpp', + 'src/EvalClient.cpp', 'src/Hover.cpp', 'src/LifeTime.cpp', 'src/Main.cpp', 'src/Support.cpp', 'src/TextDocumentSync.cpp', install: true, - dependencies: [ nixd_lsp_server, nixf, llvm ] + dependencies: [ nixd_lsp_server, nixf, llvm, libnixdrpc, libnixdutil ] ) diff --git a/nixd/tools/nixd/src/Controller.h b/nixd/tools/nixd/src/Controller.h index b30d1b407..5cad419d0 100644 --- a/nixd/tools/nixd/src/Controller.h +++ b/nixd/tools/nixd/src/Controller.h @@ -1,6 +1,7 @@ #pragma once #include "Convert.h" +#include "EvalClient.h" #include "NixTU.h" #include "lspserver/DraftStore.h" @@ -9,6 +10,7 @@ namespace nixd { class Controller : public lspserver::LSPServer { + std::unique_ptr Eval; lspserver::DraftStore Store; llvm::unique_function diff --git a/nixd/tools/nixd/src/EvalClient.cpp b/nixd/tools/nixd/src/EvalClient.cpp new file mode 100644 index 000000000..8f1f31b17 --- /dev/null +++ b/nixd/tools/nixd/src/EvalClient.cpp @@ -0,0 +1,50 @@ +#include "nixd-config.h" + +#include "EvalClient.h" + +#include "nixd/rpc/Protocol.h" +#include "nixd/util/ForkPiped.h" + +#include +#include + +#include + +namespace nixd { + +using namespace rpc; +using namespace nixd::util; + +template using M = Message; + +std::unique_ptr EvalClient::create(int &Fail) { + int In; + int Out; + int Err; + + pid_t Child = forkPiped(In, Out, Err); + if (Child == 0) { + execl(NIXD_LIBEXEC "/nix-node-eval", "nix-node-eval", nullptr); + exit(-1); + } else if (Child < 0) { + // Error. + Fail = Child; + return nullptr; + } + + Fail = 0; + // Parent process. + auto Proc = std::make_unique(Child, In, Out, Err); + return std::make_unique(Out, In, std::move(Proc)); +} + +void EvalClient::registerBC(const RegisterBCParams &Params) { + sendPacket>({RPCKind::RegisterBC, Params}); +} + +ExprValueResponse EvalClient::exprValue(const ExprValueParams &Params) { + sendPacket>({RPCKind::ExprValue, Params}); + return recvPacket(); +} + +} // namespace nixd diff --git a/nixd/tools/nixd/src/EvalClient.h b/nixd/tools/nixd/src/EvalClient.h new file mode 100644 index 000000000..22b5f7903 --- /dev/null +++ b/nixd/tools/nixd/src/EvalClient.h @@ -0,0 +1,35 @@ +#pragma once + +#include "nixd/rpc/Protocol.h" +#include "nixd/rpc/Transport.h" +#include "nixd/util/PipedProc.h" + +#include + +namespace nixd { + +class EvalClient : public rpc::Transport { + // Owned process of the evaluator + std::unique_ptr Proc; + void handleInbound(const std::vector &Buf) override{}; + +public: + EvalClient(int InboundFD, int OutboundFD, + std::unique_ptr Proc) + : rpc::Transport(InboundFD, OutboundFD), Proc(std::move(Proc)) {} + + virtual ~EvalClient() = default; + + /// Lanch nix-node-eval, with properly handled file descriptors. + /// System-wide errno will be written into "Fail" variable and thus cannot be + /// discarded. + static std::unique_ptr create(int &Fail); + + void registerBC(const rpc::RegisterBCParams &Params); + + rpc::ExprValueResponse exprValue(const rpc::ExprValueParams &Params); + + util::PipedProc *proc() { return Proc.get(); } +}; + +} // namespace nixd diff --git a/nixd/tools/nixd/src/LifeTime.cpp b/nixd/tools/nixd/src/LifeTime.cpp index 607f05ce6..23e20eb17 100644 --- a/nixd/tools/nixd/src/LifeTime.cpp +++ b/nixd/tools/nixd/src/LifeTime.cpp @@ -6,9 +6,15 @@ #include "nixd-config.h" #include "Controller.h" +#include "EvalClient.h" + +#include "nixd/util/PipedProc.h" + +#include namespace nixd { +using namespace util; using namespace llvm::json; using namespace lspserver; @@ -47,6 +53,15 @@ void Controller:: PublishDiagnostic = mkOutNotifiction( "textDocument/publishDiagnostics"); + + int Fail; + Eval = EvalClient::create(Fail); + if (Fail != 0) { + lspserver::elog("failed to create nix-node-eval worker: {0}", + strerror(-Fail)); + } else { + lspserver::log("launched nix-node-eval instance: {0}", Eval->proc()->PID); + } } } // namespace nixd diff --git a/nixd/tools/nixd/src/NixTU.h b/nixd/tools/nixd/src/NixTU.h index 7d1e040db..69c9a4713 100644 --- a/nixd/tools/nixd/src/NixTU.h +++ b/nixd/tools/nixd/src/NixTU.h @@ -1,7 +1,9 @@ #pragma once +#include "nixd/util/OwnedRegion.h" + #include "nixf/Basic/Diagnostic.h" -#include "nixf/Basic/Nodes.h" +#include "nixf/Basic/Nodes/Basic.h" namespace nixd { @@ -11,12 +13,15 @@ namespace nixd { class NixTU { std::vector Diagnostics; std::unique_ptr AST; + std::optional ASTByteCode; public: NixTU() = default; NixTU(std::vector Diagnostics, - std::unique_ptr AST) - : Diagnostics(std::move(Diagnostics)), AST(std::move(AST)) {} + std::unique_ptr AST, + std::optional ASTByteCode) + : Diagnostics(std::move(Diagnostics)), AST(std::move(AST)), + ASTByteCode(std::move(ASTByteCode)) {} [[nodiscard]] const std::vector &diagnostics() const { return Diagnostics; diff --git a/nixd/tools/nixd/src/Support.cpp b/nixd/tools/nixd/src/Support.cpp index 5f4ee6763..34999fb80 100644 --- a/nixd/tools/nixd/src/Support.cpp +++ b/nixd/tools/nixd/src/Support.cpp @@ -1,25 +1,75 @@ #include "Controller.h" -#include "Convert.h" + +#include "nixd/util/OwnedRegion.h" #include "nixf/Basic/Diagnostic.h" -#include "nixf/Basic/Nodes.h" +#include "nixf/Bytecode/Write.h" #include "nixf/Parse/Parser.h" #include "nixf/Sema/Lowering.h" using namespace lspserver; using namespace nixd; +namespace bipc = boost::interprocess; + +namespace { + +std::string getShmName(std::string_view File) { + std::stringstream SS; + SS << "nixd-tu-" << getpid() << "-" + << reinterpret_cast(File.data()); + return SS.str(); +} + +} // namespace + namespace nixd { void Controller::actOnDocumentAdd(PathRef File, std::optional Version) { auto Draft = Store.getDraft(File); assert(Draft && "Added document is not in the store?"); + std::vector Diagnostics; std::unique_ptr AST = nixf::parse(*Draft->Contents, Diagnostics); nixf::lower(AST.get(), *Draft->Contents, Diagnostics); publishDiagnostics(File, Version, Diagnostics); - TUs[File] = NixTU{std::move(Diagnostics), std::move(AST)}; + + if (!AST) { + TUs[File] = NixTU(std::move(Diagnostics), std::move(AST), std::nullopt); + return; + } + + // Serialize the AST into shared memory. Prepare for evaluation. + std::stringstream OS; + nixf::writeBytecode(OS, *AST); + std::string Buf = OS.str(); + if (Buf.empty()) { + lspserver::log("empty AST for {0}", File); + TUs[File] = NixTU(std::move(Diagnostics), std::move(AST), std::nullopt); + return; + } + + // Create an mmap()-ed region, and write AST byte code there. + std::string ShmName = getShmName(File); + + auto Shm = std::make_unique( + ShmName, static_cast(Buf.size())); + + auto Region = + std::make_unique(Shm->get(), bipc::read_write); + + std::memcpy(Region->get_address(), Buf.data(), Buf.size()); + + lspserver::log("serialized AST {0} to {1}, size: {2}", File, ShmName, + Buf.size()); + + TUs[File] = NixTU(std::move(Diagnostics), std::move(AST), + util::OwnedRegion{std::move(Shm), std::move(Region)}); + + if (Eval) { + Eval->registerBC({ShmName, ".", ".", Buf.size()}); + } } Controller::Controller(std::unique_ptr In,