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 #include +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 + * under sponsorship from the FreeBSD Foundation. + */ + +#include +#include + +#include +#include +#include + +#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); +}