Skip to content

Commit

Permalink
nixd/Server: split the option worker
Browse files Browse the repository at this point in the history
  • Loading branch information
inclyc committed Jul 27, 2023
1 parent e95425b commit 7945259
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 52 deletions.
26 changes: 13 additions & 13 deletions nixd/include/nixd/Server/Controller.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,19 +112,20 @@ class Controller : public lspserver::LSPServer {
private:
bool WaitWorker = false;

using WorkerContainerLock = std::shared_mutex;
using WorkerContainer = std::deque<std::unique_ptr<Proc>>;

using WC = std::tuple<const WorkerContainer &, std::shared_mutex &, size_t>;

WorkspaceVersionTy WorkspaceVersion = 1;

std::shared_mutex EvalWorkerLock;
WorkerContainerLock EvalWorkerLock;
WorkerContainer EvalWorkers; // GUARDED_BY(EvalWorkerLock)

// Used for lit tests, ensure that workers have finished their job.
std::counting_semaphore<> FinishSmp = std::counting_semaphore(0);

std::shared_mutex OptionWorkerLock;
WorkerContainerLock OptionWorkerLock;
WorkerContainer OptionWorkers; // GUARDED_BY(OptionWorkerLock)

EvalDraftStore DraftMgr;
Expand Down Expand Up @@ -159,17 +160,6 @@ class Controller : public lspserver::LSPServer {

boost::asio::thread_pool Pool;

//---------------------------------------------------------------------------/
// Worker members

// Worker::Option

/// The AttrSet having options, we use this for any nixpkgs options.
/// nixpkgs basically defined options under "options" attrpath
/// we can use this for completion (to support ALL option system)
nix::Value *OptionAttrSet;
std::unique_ptr<IValueEvalSession> OptionIES;

// IPC Utils

template <class Resp, class Arg>
Expand Down Expand Up @@ -308,6 +298,16 @@ class Controller : public lspserver::LSPServer {

std::unique_ptr<Proc> createEvalWorker();

std::unique_ptr<Proc> createOptionWorker();

bool createEnqueueEvalWorker();

bool createEnqueueOptionWorker();

static bool enqueueWorker(WorkerContainerLock &Lock,
WorkerContainer &Container,
std::unique_ptr<Proc> Worker, size_t Size);

void onEvalDiagnostic(const ipc::Diagnostics &);

void onFinished(const ipc::WorkerMessage &);
Expand Down
34 changes: 34 additions & 0 deletions nixd/include/nixd/Server/OptionWorker.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#pragma once

#include "nixd/Server/Config.h"
#include "nixd/Server/EvalDraftStore.h"
#include "nixd/Server/IPC.h"

#include "lspserver/LSPServer.h"

#include <nix/value.hh>

namespace nixd {

class OptionWorker : public lspserver::LSPServer {
private:
/// The AttrSet having options, we use this for any nixpkgs options.
/// nixpkgs basically defined options under "options" attrpath
/// we can use this for completion (to support ALL option system)
nix::Value *OptionAttrSet;
std::unique_ptr<IValueEvalSession> OptionIES;

public:
OptionWorker(std::unique_ptr<lspserver::InboundPort> In,
std::unique_ptr<lspserver::OutboundPort> Out);

void onEvalOptionSet(const configuration::TopLevel::Options &Config);

void onDeclaration(const ipc::AttrPathParams &,
lspserver::Callback<lspserver::Location>);

void onCompletion(const ipc::AttrPathParams &,
lspserver::Callback<llvm::json::Value>);
};

} // namespace nixd
57 changes: 46 additions & 11 deletions nixd/lib/Server/Controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "nixd/Parser/Parser.h"
#include "nixd/Parser/Require.h"
#include "nixd/Server/ASTManager.h"
#include "nixd/Server/ConfigSerialization.h"
#include "nixd/Server/Controller.h"
#include "nixd/Server/EvalDraftStore.h"
#include "nixd/Server/IPC.h"
Expand Down Expand Up @@ -128,6 +129,44 @@ std::unique_ptr<Controller::Proc> Controller::createEvalWorker() {
return EvalWorker;
}

std::unique_ptr<Controller::Proc> Controller::createOptionWorker() {
auto Args = nix::Strings{"nixd", "-role=option"};
auto Worker = selfExec(nix::stringsToCharPtrs(Args).data());
auto AskEval = mkOutNotifiction<configuration::TopLevel::Options>(
"nixd/ipc/evalOptionSet", Worker->OutPort.get());
{
std::lock_guard _(ConfigLock);
AskEval(Config.options);
}
return Worker;
}

bool Controller::createEnqueueEvalWorker() {
size_t Size;
{
std::lock_guard _(ConfigLock);
Size = Config.eval.workers;
}
return enqueueWorker(EvalWorkerLock, EvalWorkers, createEvalWorker(), Size);
}

bool Controller::createEnqueueOptionWorker() {
return enqueueWorker(OptionWorkerLock, OptionWorkers, createOptionWorker(),
1);
}

bool Controller::enqueueWorker(WorkerContainerLock &Lock,
WorkerContainer &Container,
std::unique_ptr<Proc> Worker, size_t Size) {
std::lock_guard _(Lock);
Container.emplace_back(std::move(Worker));
if (Container.size() > Size) {
Container.pop_front();
return true;
}
return false;
}

void Controller::addDocument(lspserver::PathRef File, llvm::StringRef Contents,
llvm::StringRef Version) {
using namespace lspserver;
Expand All @@ -142,20 +181,16 @@ void Controller::addDocument(lspserver::PathRef File, llvm::StringRef Contents,

DraftMgr.addDraft(File, Version, Contents);
ASTMgr.schedParse(Contents.str(), File.str(), IVersion.value_or(0));
try {
auto EvalWorker = createEvalWorker();
std::lock_guard Guard1(EvalWorkerLock);
std::lock_guard Guard2(ConfigLock);
EvalWorkers.emplace_back(std::move(EvalWorker));
if (EvalWorkers.size() > Config.eval.workers)
EvalWorkers.pop_front();
} catch (...) {
}
createEnqueueEvalWorker();
}

void Controller::updateConfig(configuration::TopLevel &&NewConfig) {
Config = std::move(NewConfig);
// forkOptionWorker();
{
std::lock_guard _(ConfigLock);
Config = std::move(NewConfig);
}
createEnqueueEvalWorker();
createEnqueueOptionWorker();
}

void Controller::fetchConfig() {
Expand Down
51 changes: 24 additions & 27 deletions nixd/lib/Server/Option.cpp → nixd/lib/Server/OptionWorker.cpp
Original file line number Diff line number Diff line change
@@ -1,32 +1,32 @@
#include "nixd/Nix/Option.h"
#include "nixd/Server/OptionWorker.h"
#include "nixd/Nix/Init.h"
#include "nixd/Nix/Option.h"
#include "nixd/Nix/Value.h"
#include "nixd/Server/Controller.h"
#include "nixd/Server/ConfigSerialization.h"
#include "nixd/Support/Diagnostic.h"
#include "nixd/Support/ReplyRAII.h"

#include <mutex>

namespace nixd {

void Controller::forkOptionWorker() {
std::lock_guard _(OptionWorkerLock);
forkWorker(
[this]() {
switchToOptionProvider();
Registry.addMethod("nixd/ipc/textDocument/completion/options", this,
&Controller::onOptionCompletion);
for (auto &W : OptionWorkers) {
W->Pid.release();
}
},
OptionWorkers, 1);
OptionWorker::OptionWorker(std::unique_ptr<lspserver::InboundPort> In,
std::unique_ptr<lspserver::OutboundPort> Out)
: lspserver::LSPServer(std::move(In), std::move(Out)) {

initEval();

Registry.addNotification("nixd/ipc/evalOptionSet", this,
&OptionWorker::onEvalOptionSet);
Registry.addMethod("nixd/ipc/textDocument/completion/options", this,
&OptionWorker::onCompletion);
Registry.addMethod("nixd/ipc/option/textDocument/declaration", this,
&OptionWorker::onDeclaration);
}

void Controller::onOptionDeclaration(
void OptionWorker::onDeclaration(
const ipc::AttrPathParams &Params,
lspserver::Callback<lspserver::Location> Reply) {
assert(Role == ServerRole::OptionProvider &&
"option declaration should be calculated in option worker!");
using namespace lspserver;
ReplyRAII<Location> RR(std::move(Reply));
if (!OptionAttrSet)
Expand Down Expand Up @@ -78,19 +78,17 @@ void Controller::onOptionDeclaration(
}
}

void Controller::switchToOptionProvider() {
initEval();
Role = ServerRole::OptionProvider;

if (!Config.options.enable)
void OptionWorker::onEvalOptionSet(
const configuration::TopLevel::Options &Config) {
if (!Config.enable)
return;
if (Config.options.target.empty()) {
if (Config.target.empty()) {
lspserver::elog(
"enabled options completion, but the target set is unspecified!");
return;
}
try {
auto I = Config.options.target;
auto I = Config.target;
auto SessionOption = std::make_unique<IValueEvalSession>();
SessionOption->parseArgs(I.nArgs());
OptionAttrSet = SessionOption->eval(I.installable);
Expand All @@ -104,9 +102,8 @@ void Controller::switchToOptionProvider() {
}
}

void Controller::onOptionCompletion(
const ipc::AttrPathParams &Params,
lspserver::Callback<llvm::json::Value> Reply) {
void OptionWorker::onCompletion(const ipc::AttrPathParams &Params,
lspserver::Callback<llvm::json::Value> Reply) {
using namespace lspserver;
using namespace nix::nixd;
ReplyRAII<CompletionList> RR(std::move(Reply));
Expand Down
2 changes: 1 addition & 1 deletion nixd/lib/Server/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ libnixdServer = library('nixdServer'
, 'EvalDraftStore.cpp'
, 'EvalWorker.cpp'
, 'IPCSerialization.cpp'
# , 'Option.cpp'
, 'OptionWorker.cpp'
, include_directories: nixd_inc
, dependencies: libnixdServerDeps
, install: true
Expand Down
6 changes: 6 additions & 0 deletions nixd/tools/nixd/nixd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "nixd/Server/Controller.h"
#include "nixd/Server/EvalWorker.h"
#include "nixd/Server/OptionWorker.h"
#include "nixd/Server/Role.h"

#include "lspserver/Connection.h"
Expand Down Expand Up @@ -159,6 +160,11 @@ int main(int argc, char *argv[]) {
break;
}
case NSS::OptionProvider:
nixd::OptionWorker Worker{
std::make_unique<lspserver::InboundPort>(
STDIN_FILENO, lspserver::JSONStreamStyle::Standard),
std::make_unique<lspserver::OutboundPort>(/*PrettyPrint=*/false)};
Worker.run();
break;
}
return 0;
Expand Down

0 comments on commit 7945259

Please sign in to comment.