]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
process-util: add helper get_process_threads()
authorLennart Poettering <lennart@poettering.net>
Wed, 8 Feb 2023 17:01:26 +0000 (18:01 +0100)
committerLennart Poettering <lennart@poettering.net>
Fri, 17 Feb 2023 13:27:58 +0000 (14:27 +0100)
Let's add a proper helper for querying the number of threads in a
process.

src/basic/hashmap.c
src/basic/process-util.c
src/basic/process-util.h
src/test/meson.build
src/test/test-fileio.c
src/test/test-process-util.c

index 322b148b313fee67f9d25422ecb68701bc20d528..b9efaafa3cc5f686cc0cafd372e5fa1f05c0f8ed 100644 (file)
@@ -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);
index b6bf83c2cfdf498eefc67937d1ff78804a2f5788..be82d0ffe71fab65a8ded2ff316fde946c0ff16c 100644 (file)
@@ -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",
index 96da0bb2921062f9291c1f27b0eb39f4610968a9..279f5d6e6b70a290311b79aa1c665b41408ab70a 100644 (file)
@@ -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);
index 34dbd6dcad41e751935c0e485d6b0de739c3bef4..f05e6a56947bf385fbe60fee7a9d816800be1f4f 100644 (file)
@@ -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')],
 
index 9d9c7198756697d5041c578729e489d6ca645204..206444093b0fd85fa3cf976fb5f6e750e927a8e7 100644 (file)
@@ -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);
index 1864f8a750b6d43437072c60a9404344fa59daea..9c24ca8204ff255ef78acdc2e05b040269656938 100644 (file)
@@ -2,6 +2,7 @@
 
 #include <fcntl.h>
 #include <linux/oom.h>
+#include <pthread.h>
 #include <sys/mount.h>
 #include <sys/personality.h>
 #include <sys/prctl.h>
@@ -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;