unnamed threads: Obliterate

Ever read gdb output and you just kinda get a headache because you have
to infer what a thread is by reading the stack trace? It's not hard, but
we could also just never have to do that again, which is also not hard.

Sample:

(gdb) info thr
  Id   Target Id                    Frame
* 1    LWP 3719283 "nix-daemon"     0x00007e558587da0f in accept ()
   from target:/nix/store/c10zhkbp6jmyh0xc5kd123ga8yy2p4hk-glibc-2.39-52/lib/libc.so.6
  2    LWP 3719284 "signal handler" 0x00007e55857b2bea in sigtimedwait ()
   from target:/nix/store/c10zhkbp6jmyh0xc5kd123ga8yy2p4hk-glibc-2.39-52/lib/libc.so.6

The API design for this is forced by the macOS pthread_setname_np only
being able to change the current thread's name, but if we just conform
everything to that, it works everywhere.

Change-Id: I2b1d6ed41e3c94170cb0b4e73ad66f239ebd9c88
This commit is contained in:
jade 2024-11-18 18:37:24 -08:00
parent 6a9b66357e
commit 519957bd59
19 changed files with 93 additions and 15 deletions

View file

@ -0,0 +1,17 @@
---
synopsis: All Lix threads are named
cls: [2210]
category: Development
credits: [jade]
---
Lix now sets thread names on all of its secondary threads, which will make debugger usage slightly nicer and easier.
```
(gdb) info thr
Id Target Id Frame
* 1 LWP 3719283 "nix-daemon" 0x00007e558587da0f in accept ()
from target:/nix/store/c10zhkbp6jmyh0xc5kd123ga8yy2p4hk-glibc-2.39-52/lib/libc.so.6
2 LWP 3719284 "signal handler" 0x00007e55857b2bea in sigtimedwait ()
from target:/nix/store/c10zhkbp6jmyh0xc5kd123ga8yy2p4hk-glibc-2.39-52/lib/libc.so.6
```

View file

@ -4,6 +4,7 @@
#include "lix/libstore/names.hh" #include "lix/libstore/names.hh"
#include "lix/libutil/terminal.hh" #include "lix/libutil/terminal.hh"
#include "lix/libutil/strings.hh" #include "lix/libutil/strings.hh"
#include "lix/libutil/thread-name.hh"
#include <map> #include <map>
#include <thread> #include <thread>
@ -84,6 +85,7 @@ void ProgressBar::resume()
if (state->paused > 0) return; // recursive pause, wait for the parents to resume too if (state->paused > 0) return; // recursive pause, wait for the parents to resume too
state->haveUpdate = true; state->haveUpdate = true;
updateThread = std::thread([&]() { updateThread = std::thread([&]() {
setCurrentThreadName("progress bar");
auto state(state_.lock()); auto state(state_.lock());
auto nextWakeup = A_LONG_TIME; auto nextWakeup = A_LONG_TIME;
while (state->paused == 0) { while (state->paused == 0) {

View file

@ -196,7 +196,7 @@ ref<const ValidPathInfo> BinaryCacheStore::addToStoreCommon(
if (narAccessor->stat(buildIdDir).type == FSAccessor::tDirectory) { if (narAccessor->stat(buildIdDir).type == FSAccessor::tDirectory) {
ThreadPool threadPool(25); ThreadPool threadPool("write debuginfo pool", 25);
auto doFile = [&](std::string member, std::string key, std::string target) { auto doFile = [&](std::string member, std::string key, std::string target) {
checkInterrupt(); checkInterrupt();

View file

@ -19,6 +19,7 @@
#include "lix/libutil/unix-domain-socket.hh" #include "lix/libutil/unix-domain-socket.hh"
#include "lix/libutil/mount.hh" #include "lix/libutil/mount.hh"
#include "lix/libutil/strings.hh" #include "lix/libutil/strings.hh"
#include "lix/libutil/thread-name.hh"
#include <regex> #include <regex>
#include <queue> #include <queue>
@ -1224,6 +1225,7 @@ void LocalDerivationGoal::startDaemon()
chownToBuilder(socketPath); chownToBuilder(socketPath);
daemonThread = std::thread([this, store]() { daemonThread = std::thread([this, store]() {
setCurrentThreadName("recursive nix daemon");
while (true) { while (true) {
@ -1244,6 +1246,7 @@ void LocalDerivationGoal::startDaemon()
debug("received daemon connection"); debug("received daemon connection");
auto workerThread = std::thread([store, remote{std::move(remote)}]() { auto workerThread = std::thread([store, remote{std::move(remote)}]() {
setCurrentThreadName("recursive nix worker");
FdSource from(remote.get()); FdSource from(remote.get());
FdSink to(remote.get()); FdSink to(remote.get());
try { try {

View file

@ -5,6 +5,7 @@
#include "lix/libstore/s3.hh" #include "lix/libstore/s3.hh"
#include "lix/libutil/signals.hh" #include "lix/libutil/signals.hh"
#include "lix/libutil/strings.hh" #include "lix/libutil/strings.hh"
#include "lix/libutil/thread-name.hh"
#include <cstddef> #include <cstddef>
#include <cstdio> #include <cstdio>
@ -454,7 +455,10 @@ struct curlFileTransfer : public FileTransfer
curl_multi_setopt(curlm.get(), CURLMOPT_MAX_TOTAL_CONNECTIONS, curl_multi_setopt(curlm.get(), CURLMOPT_MAX_TOTAL_CONNECTIONS,
fileTransferSettings.httpConnections.get()); fileTransferSettings.httpConnections.get());
workerThread = std::thread([&]() { workerThreadEntry(); }); workerThread = std::thread([&]() {
setCurrentThreadName("curlFileTransfer worker");
workerThreadEntry();
});
} }
~curlFileTransfer() ~curlFileTransfer()

View file

@ -6,6 +6,7 @@
#include "lix/libutil/finally.hh" #include "lix/libutil/finally.hh"
#include "lix/libutil/unix-domain-socket.hh" #include "lix/libutil/unix-domain-socket.hh"
#include "lix/libutil/strings.hh" #include "lix/libutil/strings.hh"
#include "lix/libutil/thread-name.hh"
#include <queue> #include <queue>
#include <regex> #include <regex>
@ -420,7 +421,10 @@ class GCOperation {
throw SysError("making socket '%1%' non-blocking", socketPath); throw SysError("making socket '%1%' non-blocking", socketPath);
} }
serverThread = std::thread([this]() { runServerThread(); }); serverThread = std::thread([this]() {
setCurrentThreadName("gc server");
runServerThread();
});
} }
void addTempRoot(std::string rootHashPart) void addTempRoot(std::string rootHashPart)
@ -491,6 +495,7 @@ void GCOperation::runServerThread()
/* Process the connection in a separate thread. */ /* Process the connection in a separate thread. */
auto fdClient_ = fdClient.get(); auto fdClient_ = fdClient.get();
std::thread clientThread([&, fdClient = std::move(fdClient)]() { std::thread clientThread([&, fdClient = std::move(fdClient)]() {
setCurrentThreadName("gc server connection");
Finally cleanup([&]() { Finally cleanup([&]() {
auto conn(connections.lock()); auto conn(connections.lock());
auto i = conn->find(fdClient.get()); auto i = conn->find(fdClient.get());
@ -900,6 +905,7 @@ void LocalStore::autoGC(bool sync)
future = state->gcFuture = promise.get_future().share(); future = state->gcFuture = promise.get_future().share();
std::thread([promise{std::move(promise)}, this, avail, getAvail]() mutable { std::thread([promise{std::move(promise)}, this, avail, getAvail]() mutable {
setCurrentThreadName("auto gc");
try { try {

View file

@ -86,7 +86,7 @@ void Store::queryMissing(const std::vector<DerivedPath> & targets,
downloadSize_ = narSize_ = 0; downloadSize_ = narSize_ = 0;
// FIXME: make async. // FIXME: make async.
ThreadPool pool(fileTransferSettings.httpConnections); ThreadPool pool("queryMissing pool", fileTransferSettings.httpConnections);
struct State struct State
{ {

View file

@ -17,6 +17,7 @@
#include "lix/libutil/logging.hh" #include "lix/libutil/logging.hh"
#include "lix/libstore/filetransfer.hh" #include "lix/libstore/filetransfer.hh"
#include "lix/libutil/strings.hh" #include "lix/libutil/strings.hh"
#include "lix/libutil/thread-name.hh"
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
@ -971,6 +972,7 @@ void RemoteStore::ConnectionHandle::withFramedSink(std::function<void(Sink & sin
thread. */ thread. */
std::thread stderrThread([&]() std::thread stderrThread([&]()
{ {
setCurrentThreadName("remote stderr thread");
try { try {
ReceiveInterrupts receiveInterrupts; ReceiveInterrupts receiveInterrupts;
processStderr(nullptr, nullptr, false); processStderr(nullptr, nullptr, false);

View file

@ -306,7 +306,7 @@ void Store::addMultipleToStore(
act.progress(nrDone, pathsToCopy.size(), nrRunning, nrFailed); act.progress(nrDone, pathsToCopy.size(), nrRunning, nrFailed);
}; };
ThreadPool pool; ThreadPool pool{"addMultipleToStore pool"};
processGraph<StorePath>(pool, processGraph<StorePath>(pool,
storePathsToAdd, storePathsToAdd,
@ -835,7 +835,7 @@ StorePathSet Store::queryValidPaths(const StorePathSet & paths, SubstituteFlag m
Sync<State> state_(State{paths.size(), StorePathSet()}); Sync<State> state_(State{paths.size(), StorePathSet()});
std::condition_variable wakeup; std::condition_variable wakeup;
ThreadPool pool; ThreadPool pool{"queryValidPaths pool"};
auto doQuery = [&](const StorePath & path) { auto doQuery = [&](const StorePath & path) {
checkInterrupt(); checkInterrupt();
@ -1136,7 +1136,7 @@ std::map<StorePath, StorePath> copyPaths(
} }
auto pathsMap = copyPaths(srcStore, dstStore, storePaths, repair, checkSigs, substitute); auto pathsMap = copyPaths(srcStore, dstStore, storePaths, repair, checkSigs, substitute);
ThreadPool pool; ThreadPool pool{"copyPaths pool"};
try { try {
// Copy the realisation closure // Copy the realisation closure

View file

@ -38,6 +38,7 @@ libutil_sources = files(
'suggestions.cc', 'suggestions.cc',
'tarfile.cc', 'tarfile.cc',
'terminal.cc', 'terminal.cc',
'thread-name.cc',
'thread-pool.cc', 'thread-pool.cc',
'unix-domain-socket.cc', 'unix-domain-socket.cc',
'url.cc', 'url.cc',
@ -118,6 +119,7 @@ libutil_headers = files(
'sync.hh', 'sync.hh',
'tarfile.hh', 'tarfile.hh',
'terminal.hh', 'terminal.hh',
'thread-name.hh',
'thread-pool.hh', 'thread-pool.hh',
'topo-sort.hh', 'topo-sort.hh',
'types.hh', 'types.hh',

View file

@ -4,15 +4,14 @@
#include <thread> #include <thread>
#include <atomic> #include <atomic>
#include <cstdlib>
#include <poll.h> #include <poll.h>
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h> #include <unistd.h>
#include <signal.h>
#include "lix/libutil/error.hh" #include "lix/libutil/error.hh"
#include "lix/libutil/file-descriptor.hh" #include "lix/libutil/file-descriptor.hh"
#include "lix/libutil/signals.hh" #include "lix/libutil/signals.hh"
#include "lix/libutil/thread-name.hh"
namespace nix { namespace nix {
@ -34,6 +33,7 @@ public:
auto &quit_ = this->quit; auto &quit_ = this->quit;
int terminateFd = terminatePipe.readSide.get(); int terminateFd = terminatePipe.readSide.get();
thread = std::thread([fd, terminateFd, &quit_]() { thread = std::thread([fd, terminateFd, &quit_]() {
setCurrentThreadName("MonitorFdHup");
while (!quit_) { while (!quit_) {
/* Wait indefinitely until a POLLHUP occurs. */ /* Wait indefinitely until a POLLHUP occurs. */
struct pollfd fds[2]; struct pollfd fds[2];

View file

@ -2,6 +2,7 @@
#include "lix/libutil/error.hh" #include "lix/libutil/error.hh"
#include "lix/libutil/sync.hh" #include "lix/libutil/sync.hh"
#include "lix/libutil/terminal.hh" #include "lix/libutil/terminal.hh"
#include "lix/libutil/thread-name.hh"
#include <map> #include <map>
#include <thread> #include <thread>
@ -49,6 +50,7 @@ static Sync<InterruptCallbacks> _interruptCallbacks;
static void signalHandlerThread(sigset_t set) static void signalHandlerThread(sigset_t set)
{ {
setCurrentThreadName("signal handler");
while (true) { while (true) {
int signal = 0; int signal = 0;
sigwait(&set, &signal); sigwait(&set, &signal);

View file

@ -0,0 +1,20 @@
#include <pthread.h>
#if defined(__FreeBSD__) || defined(__OpenBSD__)
#include <pthread_np.h>
#endif
namespace nix {
void setCurrentThreadName(const char * name)
{
// https://stackoverflow.com/questions/2369738/how-to-set-the-name-of-a-thread-in-linux-pthreads/7989973
#if defined(__linux__)
pthread_setname_np(pthread_self(), name);
#elif defined(__APPLE__)
pthread_setname_np(name);
#elif defined(__FreeBSD__) || defined(__OpenBSD__)
pthread_set_name_np(pthread_self(), name);
#endif
}
}

View file

@ -0,0 +1,12 @@
#pragma once
///@file
namespace nix {
/**
* Sets the name of the current operating system thread for the benefit of
* debuggers.
*/
void setCurrentThreadName(const char * name);
}

View file

@ -1,11 +1,12 @@
#include "lix/libutil/thread-pool.hh" #include "lix/libutil/thread-pool.hh"
#include "lix/libutil/logging.hh" #include "lix/libutil/logging.hh"
#include "lix/libutil/signals.hh" #include "lix/libutil/signals.hh"
#include "lix/libutil/thread-name.hh"
namespace nix { namespace nix {
ThreadPool::ThreadPool(size_t _maxThreads) ThreadPool::ThreadPool(const char * name, size_t _maxThreads)
: maxThreads(_maxThreads) : maxThreads(_maxThreads), name(name)
{ {
if (!maxThreads) { if (!maxThreads) {
maxThreads = std::thread::hardware_concurrency(); maxThreads = std::thread::hardware_concurrency();
@ -81,8 +82,10 @@ void ThreadPool::doWork(bool mainThread)
{ {
ReceiveInterrupts receiveInterrupts; ReceiveInterrupts receiveInterrupts;
if (!mainThread) if (!mainThread) {
setCurrentThreadName(this->name);
interruptCheck = [&]() { return (bool) quit; }; interruptCheck = [&]() { return (bool) quit; };
}
bool didWork = false; bool didWork = false;
std::exception_ptr exc; std::exception_ptr exc;

View file

@ -22,7 +22,7 @@ class ThreadPool
{ {
public: public:
ThreadPool(size_t maxThreads = 0); ThreadPool(const char * name, size_t maxThreads = 0);
~ThreadPool(); ~ThreadPool();
@ -56,6 +56,8 @@ private:
size_t maxThreads; size_t maxThreads;
const char * name;
struct State struct State
{ {
std::queue<work_t> pending; std::queue<work_t> pending;

View file

@ -38,7 +38,7 @@ struct CmdCopySigs : StorePathsCommand
for (auto & s : substituterUris) for (auto & s : substituterUris)
substituters.push_back(openStore(s)); substituters.push_back(openStore(s));
ThreadPool pool; ThreadPool pool{"CopySigs pool"};
std::string doneLabel = "done"; std::string doneLabel = "done";
std::atomic<size_t> added{0}; std::atomic<size_t> added{0};

View file

@ -79,7 +79,7 @@ struct CmdVerify : StorePathsCommand
act.progress(done, storePaths.size(), active, failed); act.progress(done, storePaths.size(), active, failed);
}; };
ThreadPool pool; ThreadPool pool{"Verify pool"};
auto doPath = [&](const StorePath & storePath) { auto doPath = [&](const StorePath & storePath) {
try { try {

View file

@ -1,5 +1,6 @@
#include "lix/libstore/filetransfer.hh" #include "lix/libstore/filetransfer.hh"
#include "lix/libutil/compression.hh" #include "lix/libutil/compression.hh"
#include "lix/libutil/thread-name.hh"
#include <cstdint> #include <cstdint>
#include <exception> #include <exception>
@ -90,6 +91,7 @@ serveHTTP(std::vector<Reply> replies)
std::thread( std::thread(
[replies, at{0}](AutoCloseFD socket, AutoCloseFD trigger) mutable { [replies, at{0}](AutoCloseFD socket, AutoCloseFD trigger) mutable {
setCurrentThreadName("test httpd server");
while (true) { while (true) {
pollfd pfds[2] = { pollfd pfds[2] = {
{ {
@ -120,6 +122,7 @@ serveHTTP(std::vector<Reply> replies)
const auto & reply = replies[at++ % replies.size()]; const auto & reply = replies[at++ % replies.size()];
std::thread([=, conn{std::move(conn)}] { std::thread([=, conn{std::move(conn)}] {
setCurrentThreadName("test httpd connection");
auto send = [&](std::string_view bit) { auto send = [&](std::string_view bit) {
while (!bit.empty()) { while (!bit.empty()) {
auto written = ::send(conn.get(), bit.data(), bit.size(), MSG_NOSIGNAL); auto written = ::send(conn.get(), bit.data(), bit.size(), MSG_NOSIGNAL);