From 726e924bd733d7b6d40c7105e603f31f97bf0e35 Mon Sep 17 00:00:00 2001 From: Sergei Zimmerman Date: Thu, 15 Jan 2026 22:32:03 +0300 Subject: [PATCH 1/3] libstore: Do not mark connections as bad when RemoteStore::narFromPath is called as a coroutine forced_unwind is thrown by Boost.Context when destroying the coroutine. This lead to us resetting the remote connection for each narFromPath with the ssh-ng:// store, so copying was very slow. --- src/libutil/serialise.cc | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/libutil/serialise.cc b/src/libutil/serialise.cc index 8297f5599..240071932 100644 --- a/src/libutil/serialise.cc +++ b/src/libutil/serialise.cc @@ -412,7 +412,7 @@ std::unique_ptr sinkToSource(std::function fun, std::funct } if (cur.empty()) { - if (hasCoro) { + if (hasCoro && *coro) { (*coro)(); } if (*coro) { @@ -427,6 +427,16 @@ std::unique_ptr sinkToSource(std::function fun, std::funct size_t n = cur.copy(data, len); cur.remove_prefix(n); + /* This is necessary to ensure that the coroutine gets resumed + after the consumer has finished reading the Source. Otherwise the + coroutine is always abandoned (i.e. it is always destroyed when + suspended). */ + if (cur.empty() && coro && *coro) { + (*coro)(); + if (*coro) + cur = coro->get(); + } + return n; } }; From 9e496f9af2ff43a54d88ac7abebdde50901e74d2 Mon Sep 17 00:00:00 2001 From: Sergei Zimmerman Date: Thu, 15 Jan 2026 22:32:06 +0300 Subject: [PATCH 2/3] libutil: Make Pid destructor more robust Without this we can abort by throwing an exception in the destructor: [24/635/2958 copied (3.8/26.0 GiB)] copying path '/nix/store/ncd2iic2nwxwhqsf4gp9sdybkwnwz20b-ruby3.3-mini_portile2-2.8.9' from 'ssh-ng://localhost:22' Nix crashed. This is a bug. Please report this at https://github.com/NixOS/nix/issues with the following information included: Exception: nix::Interrupted: error: interrupted by the user Stack trace: 0# 0x00000000004AFFE9 in result/bin/nix 1# 0x00007F946290A1AA in /nix/store/cf1a53iqg6ncnygl698c4v0l8qam5a2q-gcc-14.3.0-lib/lib/libstdc++.so.6 2# __cxa_call_terminate in /nix/store/cf1a53iqg6ncnygl698c4v0l8qam5a2q-gcc-14.3.0-lib/lib/libstdc++.so.6 3# __gxx_personality_v0 in /nix/store/cf1a53iqg6ncnygl698c4v0l8qam5a2q-gcc-14.3.0-lib/lib/libstdc++.so.6 4# 0x00007F946283FA19 in /nix/store/cf1a53iqg6ncnygl698c4v0l8qam5a2q-gcc-14.3.0-lib/lib/libgcc_s.so.1 5# _Unwind_RaiseException in /nix/store/cf1a53iqg6ncnygl698c4v0l8qam5a2q-gcc-14.3.0-lib/lib/libgcc_s.so.1 6# __cxa_throw in /nix/store/cf1a53iqg6ncnygl698c4v0l8qam5a2q-gcc-14.3.0-lib/lib/libstdc++.so.6 7# 0x00007F94635D82D0 in /nix/store/9wrnk0nizdwba4sy9lg3h0xd30pg1x5a-nix-util-2.34.0pre/lib/libnixutil.so.2.34.0 8# nix::Pid::wait() in /nix/store/9wrnk0nizdwba4sy9lg3h0xd30pg1x5a-nix-util-2.34.0pre/lib/libnixutil.so.2.34.0 9# nix::Pid::~Pid() in /nix/store/9wrnk0nizdwba4sy9lg3h0xd30pg1x5a-nix-util-2.34.0pre/lib/libnixutil.so.2.34.0 --- src/libutil/include/nix/util/processes.hh | 4 ++-- src/libutil/unix/processes.cc | 15 +++++++++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/libutil/include/nix/util/processes.hh b/src/libutil/include/nix/util/processes.hh index 23dee8713..141079e74 100644 --- a/src/libutil/include/nix/util/processes.hh +++ b/src/libutil/include/nix/util/processes.hh @@ -44,8 +44,8 @@ public: void operator=(AutoCloseFD pid); #endif ~Pid(); - int kill(); - int wait(); + int kill(bool allowInterrupts = true); + int wait(bool allowInterrupts = true); // TODO: Implement for Windows #ifndef _WIN32 diff --git a/src/libutil/unix/processes.cc b/src/libutil/unix/processes.cc index 9582ff840..66423f011 100644 --- a/src/libutil/unix/processes.cc +++ b/src/libutil/unix/processes.cc @@ -41,9 +41,11 @@ Pid::Pid(pid_t pid) } Pid::~Pid() -{ +try { if (pid != -1) - kill(); + kill(/*allowInterrupts=*/false); +} catch (...) { + ignoreExceptionInDestructor(); } void Pid::operator=(pid_t pid) @@ -59,7 +61,7 @@ Pid::operator pid_t() return pid; } -int Pid::kill() +int Pid::kill(bool allowInterrupts) { assert(pid != -1); @@ -78,10 +80,10 @@ int Pid::kill() logError(SysError("killing process %d", pid).info()); } - return wait(); + return wait(allowInterrupts); } -int Pid::wait() +int Pid::wait(bool allowInterrupts) { assert(pid != -1); while (1) { @@ -93,7 +95,8 @@ int Pid::wait() } if (errno != EINTR) throw SysError("cannot get exit status of PID %d", pid); - checkInterrupt(); + if (allowInterrupts) + checkInterrupt(); } } From b40b786839bbcfcbbd0c4d22a2e2725b9f7a2de9 Mon Sep 17 00:00:00 2001 From: Sergei Zimmerman Date: Thu, 15 Jan 2026 22:32:08 +0300 Subject: [PATCH 3/3] libstore/remote-store: Add checkInterrupt in openConnectionWrapper This avoids the wall of text like, because ThreadPool doesn't print interrupts on shutdowns. error (ignored): opening a connection to remote store 'ssh-ng://127.0.0.1' previously failed error (ignored): opening a connection to remote store 'ssh-ng://127.0.0.1' previously failed error (ignored): opening a connection to remote store 'ssh-ng://127.0.0.1' previously failed error (ignored): opening a connection to remote store 'ssh-ng://127.0.0.1' previously failed error (ignored): opening a connection to remote store 'ssh-ng://127.0.0.1' previously failed error (ignored): opening a connection to remote store 'ssh-ng://127.0.0.1' previously failed error (ignored): opening a connection to remote store 'ssh-ng://127.0.0.1' previously failed --- src/libstore/remote-store.cc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc index 22af53ae8..f80080140 100644 --- a/src/libstore/remote-store.cc +++ b/src/libstore/remote-store.cc @@ -58,8 +58,13 @@ RemoteStore::RemoteStore(const Config & config) ref RemoteStore::openConnectionWrapper() { - if (failed) + if (failed) { + checkInterrupt(); + /* Throw Interrupted instead of the following error to silence pesky + warning messages that ThreadPool prints on shutdown if other threads + failed. */ throw Error("opening a connection to remote store '%s' previously failed", config.getHumanReadableURI()); + } try { return openConnection(); } catch (...) {