From 794525914066594b1b9e7e2e125092aae4588333 Mon Sep 17 00:00:00 2001 From: Yingchi Long Date: Thu, 27 Jul 2023 20:55:18 +0800 Subject: [PATCH] nixd/Server: split the option worker --- nixd/include/nixd/Server/Controller.h | 26 ++++----- nixd/include/nixd/Server/OptionWorker.h | 34 +++++++++++ nixd/lib/Server/Controller.cpp | 57 +++++++++++++++---- .../Server/{Option.cpp => OptionWorker.cpp} | 51 ++++++++--------- nixd/lib/Server/meson.build | 2 +- nixd/tools/nixd/nixd.cpp | 6 ++ 6 files changed, 124 insertions(+), 52 deletions(-) create mode 100644 nixd/include/nixd/Server/OptionWorker.h rename nixd/lib/Server/{Option.cpp => OptionWorker.cpp} (79%) diff --git a/nixd/include/nixd/Server/Controller.h b/nixd/include/nixd/Server/Controller.h index 9f13e2829..06410ae01 100644 --- a/nixd/include/nixd/Server/Controller.h +++ b/nixd/include/nixd/Server/Controller.h @@ -112,19 +112,20 @@ class Controller : public lspserver::LSPServer { private: bool WaitWorker = false; + using WorkerContainerLock = std::shared_mutex; using WorkerContainer = std::deque>; using WC = std::tuple; 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; @@ -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 OptionIES; - // IPC Utils template @@ -308,6 +298,16 @@ class Controller : public lspserver::LSPServer { std::unique_ptr createEvalWorker(); + std::unique_ptr createOptionWorker(); + + bool createEnqueueEvalWorker(); + + bool createEnqueueOptionWorker(); + + static bool enqueueWorker(WorkerContainerLock &Lock, + WorkerContainer &Container, + std::unique_ptr Worker, size_t Size); + void onEvalDiagnostic(const ipc::Diagnostics &); void onFinished(const ipc::WorkerMessage &); diff --git a/nixd/include/nixd/Server/OptionWorker.h b/nixd/include/nixd/Server/OptionWorker.h new file mode 100644 index 000000000..a7e36f53d --- /dev/null +++ b/nixd/include/nixd/Server/OptionWorker.h @@ -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 + +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 OptionIES; + +public: + OptionWorker(std::unique_ptr In, + std::unique_ptr Out); + + void onEvalOptionSet(const configuration::TopLevel::Options &Config); + + void onDeclaration(const ipc::AttrPathParams &, + lspserver::Callback); + + void onCompletion(const ipc::AttrPathParams &, + lspserver::Callback); +}; + +} // namespace nixd diff --git a/nixd/lib/Server/Controller.cpp b/nixd/lib/Server/Controller.cpp index 7dffb9db3..6269f3250 100644 --- a/nixd/lib/Server/Controller.cpp +++ b/nixd/lib/Server/Controller.cpp @@ -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" @@ -128,6 +129,44 @@ std::unique_ptr Controller::createEvalWorker() { return EvalWorker; } +std::unique_ptr Controller::createOptionWorker() { + auto Args = nix::Strings{"nixd", "-role=option"}; + auto Worker = selfExec(nix::stringsToCharPtrs(Args).data()); + auto AskEval = mkOutNotifiction( + "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 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; @@ -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() { diff --git a/nixd/lib/Server/Option.cpp b/nixd/lib/Server/OptionWorker.cpp similarity index 79% rename from nixd/lib/Server/Option.cpp rename to nixd/lib/Server/OptionWorker.cpp index 562c2bf1c..617a70fc1 100644 --- a/nixd/lib/Server/Option.cpp +++ b/nixd/lib/Server/OptionWorker.cpp @@ -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 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 In, + std::unique_ptr 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 Reply) { - assert(Role == ServerRole::OptionProvider && - "option declaration should be calculated in option worker!"); using namespace lspserver; ReplyRAII RR(std::move(Reply)); if (!OptionAttrSet) @@ -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(); SessionOption->parseArgs(I.nArgs()); OptionAttrSet = SessionOption->eval(I.installable); @@ -104,9 +102,8 @@ void Controller::switchToOptionProvider() { } } -void Controller::onOptionCompletion( - const ipc::AttrPathParams &Params, - lspserver::Callback Reply) { +void OptionWorker::onCompletion(const ipc::AttrPathParams &Params, + lspserver::Callback Reply) { using namespace lspserver; using namespace nix::nixd; ReplyRAII RR(std::move(Reply)); diff --git a/nixd/lib/Server/meson.build b/nixd/lib/Server/meson.build index 9a005083d..930e57205 100644 --- a/nixd/lib/Server/meson.build +++ b/nixd/lib/Server/meson.build @@ -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 diff --git a/nixd/tools/nixd/nixd.cpp b/nixd/tools/nixd/nixd.cpp index 245ff9d6d..4c356ad4e 100644 --- a/nixd/tools/nixd/nixd.cpp +++ b/nixd/tools/nixd/nixd.cpp @@ -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" @@ -159,6 +160,11 @@ int main(int argc, char *argv[]) { break; } case NSS::OptionProvider: + nixd::OptionWorker Worker{ + std::make_unique( + STDIN_FILENO, lspserver::JSONStreamStyle::Standard), + std::make_unique(/*PrettyPrint=*/false)}; + Worker.run(); break; } return 0;