diff options
author | Joseph Myers <josmyers@redhat.com> | 2024-11-22 16:58:51 +0000 |
---|---|---|
committer | Joseph Myers <josmyers@redhat.com> | 2024-11-22 16:58:51 +0000 |
commit | 99671e72bb27a3cb98860bdc4c0e25961ce96b3e (patch) | |
tree | ff308d9a845369d565dc444fe9b4b4fef0852eca /sysdeps | |
parent | bccb0648ea29f89a7b1b64f3e5674d2338e3798e (diff) | |
download | glibc-99671e72bb27a3cb98860bdc4c0e25961ce96b3e.zip glibc-99671e72bb27a3cb98860bdc4c0e25961ce96b3e.tar.gz glibc-99671e72bb27a3cb98860bdc4c0e25961ce96b3e.tar.bz2 |
Add multithreaded test of sem_getvalue
Test coverage of sem_getvalue is fairly limited. Add a test that runs
it on threads on each CPU. For this purpose I adapted
tst-skeleton-thread-affinity.c; it didn't seem very suitable to use
as-is or include directly in a different test doing things per-CPU,
but did seem a suitable starting point (thus sharing
tst-skeleton-affinity.c) for such testing.
Tested for x86_64.
Diffstat (limited to 'sysdeps')
-rw-r--r-- | sysdeps/unix/sysv/linux/Makefile | 1 | ||||
-rw-r--r-- | sysdeps/unix/sysv/linux/tst-sem_getvalue-affinity.c | 185 |
2 files changed, 186 insertions, 0 deletions
diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile index 527c7a5..eb9c697 100644 --- a/sysdeps/unix/sysv/linux/Makefile +++ b/sysdeps/unix/sysv/linux/Makefile @@ -668,6 +668,7 @@ ifeq ($(subdir),nptl) tests += \ tst-align-clone \ tst-getpid1 \ + tst-sem_getvalue-affinity \ # tests # tst-rseq-nptl is an internal test because it requires a definition of diff --git a/sysdeps/unix/sysv/linux/tst-sem_getvalue-affinity.c b/sysdeps/unix/sysv/linux/tst-sem_getvalue-affinity.c new file mode 100644 index 0000000..4176f67 --- /dev/null +++ b/sysdeps/unix/sysv/linux/tst-sem_getvalue-affinity.c @@ -0,0 +1,185 @@ +/* Test sem_getvalue across CPUs. Based on tst-skeleton-thread-affinity.c. + Copyright (C) 2015-2024 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <https://www.gnu.org/licenses/>. */ + +#include <errno.h> +#include <pthread.h> +#include <semaphore.h> +#include <stdbool.h> +#include <stdlib.h> +#include <support/xthread.h> +#include <sys/time.h> + +struct conf; +static bool early_test (struct conf *); + +static int +setaffinity (size_t size, const cpu_set_t *set) +{ + int ret = pthread_setaffinity_np (pthread_self (), size, set); + if (ret != 0) + { + errno = ret; + return -1; + } + return 0; +} + +static int +getaffinity (size_t size, cpu_set_t *set) +{ + int ret = pthread_getaffinity_np (pthread_self (), size, set); + if (ret != 0) + { + errno = ret; + return -1; + } + return 0; +} + +#include "tst-skeleton-affinity.c" + +static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; +static sem_t sem; + +static void * +tf (void *arg) +{ + void *ret = NULL; + xpthread_mutex_lock (&lock); + int semval; + if (sem_getvalue (&sem, &semval) != 0) + { + printf ("sem_getvalue failed: %m\n"); + ret = (void *) 1; + } + else if (semval != 12345) + { + printf ("sem_getvalue returned %d not 12345\n", semval); + ret = (void *) 1; + } + xpthread_mutex_unlock (&lock); + return ret; +} + +static int +stop_and_join_threads (struct conf *conf, cpu_set_t *set, + pthread_t *pinned_first, pthread_t *pinned_last) +{ + int failed = 0; + for (pthread_t *p = pinned_first; p < pinned_last; ++p) + { + int cpu = p - pinned_first; + if (!CPU_ISSET_S (cpu, CPU_ALLOC_SIZE (conf->set_size), set)) + continue; + + void *retval = (void *) 1; + int ret = pthread_join (*p, &retval); + if (ret != 0) + { + printf ("error: Failed to join thread %d: %s\n", cpu, strerror (ret)); + fflush (stdout); + /* Cannot shut down cleanly with threads still running. */ + abort (); + } + if (retval != NULL) + failed = 1; + } + return failed; +} + +static bool +early_test (struct conf *conf) +{ + int ret; + ret = sem_init (&sem, 0, 12345); + if (ret != 0) + { + printf ("error: sem_init failed: %m\n"); + return false; + } + xpthread_mutex_lock (&lock); + pthread_t *pinned_threads + = calloc (conf->last_cpu + 1, sizeof (*pinned_threads)); + cpu_set_t *initial_set = CPU_ALLOC (conf->set_size); + cpu_set_t *scratch_set = CPU_ALLOC (conf->set_size); + + if (pinned_threads == NULL || initial_set == NULL || scratch_set == NULL) + { + puts ("error: Memory allocation failure"); + return false; + } + if (getaffinity (CPU_ALLOC_SIZE (conf->set_size), initial_set) < 0) + { + printf ("error: pthread_getaffinity_np failed: %m\n"); + return false; + } + + pthread_attr_t attr; + ret = pthread_attr_init (&attr); + if (ret != 0) + { + printf ("error: pthread_attr_init failed: %s\n", strerror (ret)); + return false; + } + support_set_small_thread_stack_size (&attr); + + /* Spawn a thread pinned to each available CPU. */ + for (int cpu = 0; cpu <= conf->last_cpu; ++cpu) + { + if (!CPU_ISSET_S (cpu, CPU_ALLOC_SIZE (conf->set_size), initial_set)) + continue; + CPU_ZERO_S (CPU_ALLOC_SIZE (conf->set_size), scratch_set); + CPU_SET_S (cpu, CPU_ALLOC_SIZE (conf->set_size), scratch_set); + ret = pthread_attr_setaffinity_np + (&attr, CPU_ALLOC_SIZE (conf->set_size), scratch_set); + if (ret != 0) + { + printf ("error: pthread_attr_setaffinity_np for CPU %d failed: %s\n", + cpu, strerror (ret)); + stop_and_join_threads (conf, initial_set, + pinned_threads, pinned_threads + cpu); + return false; + } + ret = pthread_create (pinned_threads + cpu, &attr, + tf, (void *) (uintptr_t) cpu); + if (ret != 0) + { + printf ("error: pthread_create for CPU %d failed: %s\n", + cpu, strerror (ret)); + stop_and_join_threads (conf, initial_set, + pinned_threads, pinned_threads + cpu); + return false; + } + } + + /* Main thread. */ + xpthread_mutex_unlock (&lock); + int failed = stop_and_join_threads (conf, initial_set, + pinned_threads, + pinned_threads + conf->last_cpu + 1); + + printf ("info: Main thread ran on %d CPU(s) of %d available CPU(s)\n", + CPU_COUNT_S (CPU_ALLOC_SIZE (conf->set_size), scratch_set), + CPU_COUNT_S (CPU_ALLOC_SIZE (conf->set_size), initial_set)); + + pthread_attr_destroy (&attr); + CPU_FREE (scratch_set); + CPU_FREE (initial_set); + free (pinned_threads); + return failed == 0; +} |