git: 588bd8bb6c13 - main - libthr: pthread_query_np(3) and pthread_query_all_np(3)

From: Konstantin Belousov <kib_at_FreeBSD.org>
Date: Fri, 30 Oct 2026 13:52:36 UTC
The branch main has been updated by kib:

URL: https://cgit.FreeBSD.org/src/commit/?id=588bd8bb6c1331dad1bbe1cf222db970f6476c44

commit 588bd8bb6c1331dad1bbe1cf222db970f6476c44
Author:     Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2026-10-18 09:46:56 +0000
Commit:     Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2026-10-30 13:51:03 +0000

    libthr: pthread_query_np(3) and pthread_query_all_np(3)
    
    Reviewed by:    aokblast, markj
    Sponsored by:   The FreeBSD Foundation
    MFC after:      1 week
    Differential revision:  https://reviews.freebsd.org/D60838
---
 include/pthread_np.h           |  30 +++++++++++
 lib/libthr/pthread.map         |   2 +
 lib/libthr/thread/Makefile.inc |   1 +
 lib/libthr/thread/thr_query.c  | 116 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 149 insertions(+)

diff --git a/include/pthread_np.h b/include/pthread_np.h
index 04c64a748b72..dc38abb9b729 100644
--- a/include/pthread_np.h
+++ b/include/pthread_np.h
@@ -34,6 +34,34 @@
 #include <sys/param.h>
 #include <sys/cpuset.h>
 
+enum pthread_state_np {
+	PTHREAD_STATE_RUNNING_NP = 100,
+	PTHREAD_STATE_SUSPENDED_NP,
+	PTHREAD_STATE_DEAD_NP,
+};
+
+#define	PTHREAD_INFO_DETACHED_NP	0x0001
+#define	PTHREAD_INFO_INITIAL_NP		0x0002
+
+#define	PTHREAD_INFO_CANCEL_ENABLED_NP	0x0001
+#define	PTHREAD_INFO_CANCEL_ASYNC_NP	0x0002
+#define	PTHREAD_INFO_CANCEL_PENDING_NP	0x0004
+#define	PTHREAD_INFO_CANCEL_AT_POINT_NP	0x0008
+#define	PTHREAD_INFO_CANCEL_IN_PROG_NP	0x0010
+
+struct pthread_info_np {
+	size_t pthread_info_size;
+	pthread_t thread;
+	long lwpid;
+	enum pthread_state_np state;
+	int flags;
+	int cancel_state;
+	int *errno_location;
+	struct tcb *tcb;
+	struct uexterror *exterr;
+	char name[128];
+};
+
 /*
  * Non-POSIX thread function prototype definitions:
  */
@@ -57,6 +85,8 @@ int pthread_mutex_isowned_np(pthread_mutex_t *mutex);
 void pthread_resume_all_np(void);
 int pthread_resume_np(pthread_t);
 int pthread_peekjoin_np(pthread_t, void **);
+int pthread_query_np(pthread_t, struct pthread_info_np *, size_t);
+int pthread_query_all_np(struct pthread_info_np *, size_t *);
 void pthread_set_name_np(pthread_t, const char *);
 int pthread_setaffinity_np(pthread_t, size_t, const cpuset_t *);
 void pthread_signals_block_np(void);
diff --git a/lib/libthr/pthread.map b/lib/libthr/pthread.map
index 4fe5f2c28331..80a07d472a0e 100644
--- a/lib/libthr/pthread.map
+++ b/lib/libthr/pthread.map
@@ -345,5 +345,7 @@ FBSD_1.8 {
 
 FBSD_1.9 {
 	 pthread_cond_clockwait;
+	 pthread_query_np;
+	 pthread_query_all_np;
 	 pthread_tryjoin_np;
 };
diff --git a/lib/libthr/thread/Makefile.inc b/lib/libthr/thread/Makefile.inc
index c7442284bd51..1910fcada64c 100644
--- a/lib/libthr/thread/Makefile.inc
+++ b/lib/libthr/thread/Makefile.inc
@@ -37,6 +37,7 @@ SRCS+= \
 	thr_printf.c \
 	thr_pshared.c \
 	thr_pspinlock.c \
+	thr_query.c \
 	thr_resume_np.c \
 	thr_rtld.c \
 	thr_rwlock.c \
diff --git a/lib/libthr/thread/thr_query.c b/lib/libthr/thread/thr_query.c
new file mode 100644
index 000000000000..bc9104fd848f
--- /dev/null
+++ b/lib/libthr/thread/thr_query.c
@@ -0,0 +1,116 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright 2026 The FreeBSD Foundation
+ *
+ * This software was developed by Konstantin Belousov <kib@FreeBSD.org>
+ * under sponsorship from the FreeBSD Foundation.
+ */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <string.h>
+#include <pthread.h>
+#include <pthread_np.h>
+
+#include "libc_private.h"
+#include "thr_private.h"
+
+static void
+pthread_getinfo(struct pthread *curthread, struct pthread *thread, bool locked,
+    struct pthread_info_np *info)
+{
+	if (!locked)
+		THR_THREAD_LOCK(curthread, thread);
+	info->pthread_info_size = sizeof(*info);
+	info->thread = thread;
+	info->lwpid = thread->tid;
+	if (thread->state == PS_DEAD)
+		info->state = PTHREAD_STATE_DEAD_NP;
+	else if ((thread->flags & THR_FLAGS_SUSPENDED) != 0)
+		info->state = PTHREAD_STATE_SUSPENDED_NP;
+	else
+		info->state = PTHREAD_STATE_RUNNING_NP;
+	info->flags = 0;
+	if ((thread->flags & THR_FLAGS_DETACHED) != 0)
+		info->flags |= PTHREAD_INFO_DETACHED_NP;
+	if (thread == _thr_initial)
+		info->flags |= PTHREAD_INFO_INITIAL_NP;
+	info->cancel_state = 0;
+	if (thread->cancel_enable)
+		info->cancel_state |= PTHREAD_INFO_CANCEL_ENABLED_NP;
+	if (thread->cancel_async)
+		info->cancel_state |= PTHREAD_INFO_CANCEL_ASYNC_NP;
+	if (thread->cancel_point)
+		info->cancel_state |= PTHREAD_INFO_CANCEL_AT_POINT_NP;
+	if (thread->cancel_pending)
+		info->cancel_state |= PTHREAD_INFO_CANCEL_PENDING_NP;
+	if (thread->cancelling)
+		info->cancel_state |= PTHREAD_INFO_CANCEL_IN_PROG_NP;
+	info->errno_location = thread == _thr_initial ? &__libsys_errno :
+	    &thread->error;
+	info->tcb = thread->tcb;
+	if (thread->name != NULL)
+		strlcpy(info->name, thread->name, sizeof(info->name));
+	else
+		info->name[0] = '\0';
+	info->exterr = &thread->uexterr;
+	THR_THREAD_UNLOCK(curthread, thread);
+}
+
+int
+pthread_query_np(pthread_t thread, struct pthread_info_np *info, size_t bufsz)
+{
+	struct pthread *curthread;
+	struct pthread_info_np pi;
+	int ret;
+
+	if (_thr_initial == NULL)
+		return (EAGAIN);
+
+	curthread = _get_curthread();
+	if (thread != curthread) {
+		ret = _thr_find_thread(curthread, thread, 1);
+		if (ret != 0)
+			return (ret);
+	}
+	pthread_getinfo(curthread, thread, thread != curthread, &pi);
+	memcpy(info, &pi, MIN(sizeof(pi), bufsz));
+	return (0);
+}
+
+int
+pthread_query_all_np(struct pthread_info_np *info, size_t *bufsz)
+{
+	struct pthread *curthread, *thr;
+	size_t cnt, i;
+	int res;
+
+	if (_thr_initial == NULL) {
+		/* Exactly one thread exists. */
+		*bufsz = sizeof(*info);
+		return (EAGAIN);
+	}
+
+	curthread = _get_curthread();
+	res = 0;
+	i = 0;
+	cnt = *bufsz / sizeof(*info);
+	THREAD_LIST_RDLOCK(curthread);
+	TAILQ_FOREACH(thr, &_thread_list, tle) {
+		if (i + 1 > cnt)
+			res = ENOMEM;
+		/*
+		 * Continue the loop even after the buffer is filled,
+		 * to report required buffer size.
+		 */
+		if (res == 0)
+			pthread_getinfo(curthread, thr, false, &info[i]);
+		i++;
+	}
+	THREAD_LIST_UNLOCK(curthread);
+
+	*bufsz = i * sizeof(*info);
+	return (res);
+}