Skip to content

Commit

Permalink
nixd: add eval client
Browse files Browse the repository at this point in the history
  • Loading branch information
inclyc committed Mar 14, 2024
1 parent 8743217 commit b122cab
Show file tree
Hide file tree
Showing 8 changed files with 168 additions and 8 deletions.
4 changes: 3 additions & 1 deletion nixd/libutil/include/nixd/util/AutoHUPPID.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
3 changes: 2 additions & 1 deletion nixd/tools/nixd/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -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 ]
)


Expand Down
2 changes: 2 additions & 0 deletions nixd/tools/nixd/src/Controller.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include "Convert.h"
#include "EvalClient.h"
#include "NixTU.h"

#include "lspserver/DraftStore.h"
Expand All @@ -9,6 +10,7 @@
namespace nixd {

class Controller : public lspserver::LSPServer {
std::unique_ptr<EvalClient> Eval;
lspserver::DraftStore Store;

llvm::unique_function<void(const lspserver::PublishDiagnosticsParams &)>
Expand Down
50 changes: 50 additions & 0 deletions nixd/tools/nixd/src/EvalClient.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#include "nixd-config.h"

#include "EvalClient.h"

#include "nixd/rpc/Protocol.h"
#include "nixd/util/ForkPiped.h"

#include <bc/Read.h>
#include <bc/Write.h>

#include <unistd.h>

namespace nixd {

using namespace rpc;
using namespace nixd::util;

template <class T> using M = Message<T>;

std::unique_ptr<EvalClient> 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<PipedProc>(Child, In, Out, Err);
return std::make_unique<EvalClient>(Out, In, std::move(Proc));
}

void EvalClient::registerBC(const RegisterBCParams &Params) {
sendPacket<M<RegisterBCParams>>({RPCKind::RegisterBC, Params});
}

ExprValueResponse EvalClient::exprValue(const ExprValueParams &Params) {
sendPacket<M<ExprValueParams>>({RPCKind::ExprValue, Params});
return recvPacket<ExprValueResponse>();
}

} // namespace nixd
35 changes: 35 additions & 0 deletions nixd/tools/nixd/src/EvalClient.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#pragma once

#include "nixd/rpc/Protocol.h"
#include "nixd/rpc/Transport.h"
#include "nixd/util/PipedProc.h"

#include <memory>

namespace nixd {

class EvalClient : public rpc::Transport {
// Owned process of the evaluator
std::unique_ptr<util::PipedProc> Proc;
void handleInbound(const std::vector<char> &Buf) override{};

public:
EvalClient(int InboundFD, int OutboundFD,
std::unique_ptr<util::PipedProc> 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<EvalClient> create(int &Fail);

void registerBC(const rpc::RegisterBCParams &Params);

rpc::ExprValueResponse exprValue(const rpc::ExprValueParams &Params);

util::PipedProc *proc() { return Proc.get(); }
};

} // namespace nixd
15 changes: 15 additions & 0 deletions nixd/tools/nixd/src/LifeTime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,15 @@
#include "nixd-config.h"

#include "Controller.h"
#include "EvalClient.h"

#include "nixd/util/PipedProc.h"

#include <cstring>

namespace nixd {

using namespace util;
using namespace llvm::json;
using namespace lspserver;

Expand Down Expand Up @@ -47,6 +53,15 @@ void Controller::

PublishDiagnostic = mkOutNotifiction<PublishDiagnosticsParams>(
"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
11 changes: 8 additions & 3 deletions nixd/tools/nixd/src/NixTU.h
Original file line number Diff line number Diff line change
@@ -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 {

Expand All @@ -11,12 +13,15 @@ namespace nixd {
class NixTU {
std::vector<nixf::Diagnostic> Diagnostics;
std::unique_ptr<nixf::Node> AST;
std::optional<util::OwnedRegion> ASTByteCode;

public:
NixTU() = default;
NixTU(std::vector<nixf::Diagnostic> Diagnostics,
std::unique_ptr<nixf::Node> AST)
: Diagnostics(std::move(Diagnostics)), AST(std::move(AST)) {}
std::unique_ptr<nixf::Node> AST,
std::optional<util::OwnedRegion> ASTByteCode)
: Diagnostics(std::move(Diagnostics)), AST(std::move(AST)),
ASTByteCode(std::move(ASTByteCode)) {}

[[nodiscard]] const std::vector<nixf::Diagnostic> &diagnostics() const {
return Diagnostics;
Expand Down
56 changes: 53 additions & 3 deletions nixd/tools/nixd/src/Support.cpp
Original file line number Diff line number Diff line change
@@ -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<std::uintptr_t>(File.data());
return SS.str();
}

} // namespace

namespace nixd {

void Controller::actOnDocumentAdd(PathRef File,
std::optional<int64_t> Version) {
auto Draft = Store.getDraft(File);
assert(Draft && "Added document is not in the store?");

std::vector<nixf::Diagnostic> Diagnostics;
std::unique_ptr<nixf::Node> 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<util::AutoRemoveShm>(
ShmName, static_cast<bipc::offset_t>(Buf.size()));

auto Region =
std::make_unique<bipc::mapped_region>(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<lspserver::InboundPort> In,
Expand Down

0 comments on commit b122cab

Please sign in to comment.