From 001c70d2bab8a06c648cbc456e59a271792fe99d Mon Sep 17 00:00:00 2001 From: eldritch horrors Date: Tue, 19 Aug 2025 15:00:10 +0200 Subject: [PATCH] libstore: mark all non-local stores as thread-unsafe this pretty much only impacts store verification via the nix3 cli. no other thread pools are left, and the verification pool may *actually* be important for throughput since verification involves much hashing. Change-Id: I32152e6169a82a1268a790e333f21a0430ede7f4 --- lix/libcmd/command.cc | 14 ++++++++------ lix/libcmd/command.hh | 4 ++-- lix/libstore/local-store.hh | 5 +++++ lix/libstore/store-api.hh | 12 ++++++++++++ lix/nix/verify.cc | 2 ++ 5 files changed, 29 insertions(+), 8 deletions(-) diff --git a/lix/libcmd/command.cc b/lix/libcmd/command.cc index ce306c545..4c9626d8c 100644 --- a/lix/libcmd/command.cc +++ b/lix/libcmd/command.cc @@ -4,6 +4,7 @@ #include "lix/libstore/derivations.hh" #include "lix/libstore/profiles.hh" #include "lix/libcmd/repl.hh" +#include "lix/libutil/async.hh" extern char * * environ __attribute__((weak)); @@ -39,14 +40,15 @@ StoreCommand::StoreCommand() ref StoreCommand::getStore() { - if (!_store) - _store = createStore(); + if (!_store) { + _store = createStore(aio()); + } return *_store; } -ref StoreCommand::createStore() +ref StoreCommand::createStore(AsyncIoRoot & in) { - return aio().blockOn(openStore()); + return in.blockOn(openStore()); } void StoreCommand::run() @@ -71,9 +73,9 @@ CopyCommand::CopyCommand() }); } -ref CopyCommand::createStore() +ref CopyCommand::createStore(AsyncIoRoot & in) { - return srcUri.empty() ? StoreCommand::createStore() : aio().blockOn(openStore(srcUri)); + return srcUri.empty() ? StoreCommand::createStore(in) : in.blockOn(openStore(srcUri)); } ref CopyCommand::getDstStore() diff --git a/lix/libcmd/command.hh b/lix/libcmd/command.hh index 249d5bdbe..da8cc4934 100644 --- a/lix/libcmd/command.hh +++ b/lix/libcmd/command.hh @@ -39,7 +39,7 @@ struct StoreCommand : virtual Command StoreCommand(); void run() override; ref getStore(); - virtual ref createStore(); + virtual ref createStore(AsyncIoRoot & in); /** * Main entry point, with a `Store` provided */ @@ -59,7 +59,7 @@ struct CopyCommand : virtual StoreCommand CopyCommand(); - ref createStore() override; + ref createStore(AsyncIoRoot & in) override; ref getDstStore(); }; diff --git a/lix/libstore/local-store.hh b/lix/libstore/local-store.hh index 19d9b6558..c16bb9e21 100644 --- a/lix/libstore/local-store.hh +++ b/lix/libstore/local-store.hh @@ -160,6 +160,11 @@ public: association = {user, group}; } + bool isThreadSafe() const override + { + return true; + } + private: const PublicKeys & getPublicKeys(); diff --git a/lix/libstore/store-api.hh b/lix/libstore/store-api.hh index 3f9138806..c3a5a1d10 100644 --- a/lix/libstore/store-api.hh +++ b/lix/libstore/store-api.hh @@ -266,6 +266,18 @@ public: virtual std::string getUri() = 0; + /** + * whether this store can safely be used by multiple threads. Stores with + * async state (such as network connections) cannot be thread-safe due to + * kj async objects' thread binding. realistically only stores using only + * file system state can be thread-safe, i.e. only our local stores. even + * daemon stores can't be safe as they hold onto unix socket connections. + */ + virtual bool isThreadSafe() const + { + return false; + } + StorePath parseStorePath(std::string_view path) const; std::optional maybeParseStorePath(std::string_view path) const; diff --git a/lix/nix/verify.cc b/lix/nix/verify.cc index 6feb44294..4e7c10e72 100644 --- a/lix/nix/verify.cc +++ b/lix/nix/verify.cc @@ -85,6 +85,8 @@ struct CmdVerify : StorePathsCommand ThreadPool pool{"Verify pool"}; auto doPath = [&](AsyncIoRoot & aio, const StorePath & storePath) { + thread_local auto store = getStore()->isThreadSafe() ? getStore() : createStore(aio); + try { MaintainCount> mcActive(active); update();