From: Lennart Poettering Date: Wed, 8 Feb 2023 17:01:26 +0000 (+0100) Subject: process-util: add helper get_process_threads() X-Git-Tag: v254-rc1~1246 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6aa90884a093040ddb2dd656c1bf93892420c804;p=thirdparty%2Fsystemd.git process-util: add helper get_process_threads() Let's add a proper helper for querying the number of threads in a process. --- diff --git a/src/basic/hashmap.c b/src/basic/hashmap.c index 322b148b313..b9efaafa3cc 100644 --- a/src/basic/hashmap.c +++ b/src/basic/hashmap.c @@ -277,7 +277,6 @@ static _used_ const struct hashmap_type_info hashmap_type_info[_HASHMAP_TYPE_MAX #if VALGRIND _destructor_ static void cleanup_pools(void) { _cleanup_free_ char *t = NULL; - int r; /* Be nice to valgrind */ @@ -291,8 +290,7 @@ _destructor_ static void cleanup_pools(void) { if (getpid() != gettid()) return; - r = get_proc_field("/proc/self/status", "Threads", WHITESPACE, &t); - if (r < 0 || !streq(t, "1")) + if (get_process_threads(0) != 1) return; mempool_drop(&hashmap_pool); diff --git a/src/basic/process-util.c b/src/basic/process-util.c index b6bf83c2cfd..be82d0ffe71 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -1549,6 +1549,31 @@ _noreturn_ void freeze(void) { pause(); } +int get_process_threads(pid_t pid) { + _cleanup_free_ char *t = NULL; + const char *p; + int n, r; + + if (pid < 0) + return -EINVAL; + + p = procfs_file_alloca(pid, "status"); + + r = get_proc_field(p, "Threads", WHITESPACE, &t); + if (r == -ENOENT) + return proc_mounted() == 0 ? -ENOSYS : -ESRCH; + if (r < 0) + return r; + + r = safe_atoi(t, &n); + if (r < 0) + return r; + if (n < 0) + return -EINVAL; + + return n; +} + static const char *const sigchld_code_table[] = { [CLD_EXITED] = "exited", [CLD_KILLED] = "killed", diff --git a/src/basic/process-util.h b/src/basic/process-util.h index 96da0bb2921..279f5d6e6b7 100644 --- a/src/basic/process-util.h +++ b/src/basic/process-util.h @@ -189,3 +189,5 @@ int pidfd_verify_pid(int pidfd, pid_t pid); int setpriority_closest(int priority); _noreturn_ void freeze(void); + +int get_process_threads(pid_t pid); diff --git a/src/test/meson.build b/src/test/meson.build index 34dbd6dcad4..f05e6a56947 100644 --- a/src/test/meson.build +++ b/src/test/meson.build @@ -310,7 +310,9 @@ tests += [ [files('test-hostname-util.c')], - [files('test-process-util.c')], + [files('test-process-util.c'), + [], + [threads]], [files('test-terminal-util.c')], diff --git a/src/test/test-fileio.c b/src/test/test-fileio.c index 9d9c7198756..206444093b0 100644 --- a/src/test/test-fileio.c +++ b/src/test/test-fileio.c @@ -337,14 +337,10 @@ TEST(executable_is_script) { } TEST(status_field) { - _cleanup_free_ char *t = NULL, *p = NULL, *s = NULL, *z = NULL; + _cleanup_free_ char *p = NULL, *s = NULL, *z = NULL; unsigned long long total = 0, buffers = 0; int r; - assert_se(get_proc_field("/proc/self/status", "Threads", WHITESPACE, &t) == 0); - puts(t); - assert_se(streq(t, "1")); - r = get_proc_field("/proc/meminfo", "MemTotal", WHITESPACE, &p); if (r != -ENOENT) { assert_se(r == 0); diff --git a/src/test/test-process-util.c b/src/test/test-process-util.c index 1864f8a750b..9c24ca8204f 100644 --- a/src/test/test-process-util.c +++ b/src/test/test-process-util.c @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -793,6 +794,59 @@ TEST(set_oom_score_adjust) { assert_se(b == a); } +static void* dummy_thread(void *p) { + int fd = PTR_TO_FD(p); + char x; + + /* let main thread know we are ready */ + assert_se(write(fd, &(const char) { 'x' }, 1) == 1); + + /* wait for the main thread to tell us to shut down */ + assert_se(read(fd, &x, 1) == 1); + return NULL; +} + +TEST(get_process_threads) { + int r; + + /* Run this test in a child, so that we can guarantee there's exactly one thread around in the child */ + r = safe_fork("(nthreads)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_REOPEN_LOG|FORK_WAIT|FORK_LOG, NULL); + assert_se(r >= 0); + + if (r == 0) { + _cleanup_close_pair_ int pfd[2] = PIPE_EBADF, ppfd[2] = PIPE_EBADF; + pthread_t t, tt; + char x; + + assert_se(socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0, pfd) >= 0); + assert_se(socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0, ppfd) >= 0); + + assert_se(get_process_threads(0) == 1); + assert_se(pthread_create(&t, NULL, &dummy_thread, FD_TO_PTR(pfd[0])) == 0); + assert_se(read(pfd[1], &x, 1) == 1); + assert_se(get_process_threads(0) == 2); + assert_se(pthread_create(&tt, NULL, &dummy_thread, FD_TO_PTR(ppfd[0])) == 0); + assert_se(read(ppfd[1], &x, 1) == 1); + assert_se(get_process_threads(0) == 3); + + assert_se(write(pfd[1], &(const char) { 'x' }, 1) == 1); + assert_se(pthread_join(t, NULL) == 0); + + /* the value reported via /proc/ is decreased asynchronously, and there appears to be no nice + * way to sync on it. Hence we do the weak >= 2 check, even though == 2 is what we'd actually + * like to check here */ + assert_se(get_process_threads(0) >= 2); + + assert_se(write(ppfd[1], &(const char) { 'x' }, 1) == 1); + assert_se(pthread_join(tt, NULL) == 0); + + /* similar here */ + assert_se(get_process_threads(0) >= 1); + + _exit(EXIT_SUCCESS); + } +} + static int intro(void) { log_show_color(true); return EXIT_SUCCESS;