diff options
Diffstat (limited to 'nptl/tst-default-attr.c')
-rw-r--r-- | nptl/tst-default-attr.c | 385 |
1 files changed, 385 insertions, 0 deletions
diff --git a/nptl/tst-default-attr.c b/nptl/tst-default-attr.c new file mode 100644 index 0000000000..d7e8611ac5 --- /dev/null +++ b/nptl/tst-default-attr.c @@ -0,0 +1,385 @@ +/* Verify that pthread_[gs]etattr_default_np work correctly. + + Copyright (C) 2013 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 + <http://www.gnu.org/licenses/>. */ + +#include <pthread.h> +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <stdbool.h> + +#define RETURN_IF_FAIL(f, ...) \ + ({ \ + int ret = f (__VA_ARGS__); \ + if (ret != 0) \ + { \ + printf ("%s:%d: %s returned %d (errno = %d)\n", __FILE__, __LINE__, \ + #f, ret, errno); \ + return ret; \ + } \ + }) + +static int (*verify_result) (pthread_attr_t *); +static size_t stacksize = 1024 * 1024; +static size_t guardsize; +static bool do_join = true; +static int running = 0; +static int detach_failed = 0; +static pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t c = PTHREAD_COND_INITIALIZER; + +static void * +thr (void *unused __attribute__ ((unused))) +{ + pthread_attr_t attr; + int ret; + + memset (&attr, 0xab, sizeof attr); + /* To verify that the pthread_setattr_default_np worked. */ + if ((ret = pthread_getattr_default_np (&attr)) != 0) + { + printf ("pthread_getattr_default_np failed: %s\n", strerror (ret)); + goto out; + } + + if ((ret = (*verify_result) (&attr)) != 0) + goto out; + + memset (&attr, 0xab, sizeof attr); + /* To verify that the attributes actually got applied. */ + if ((ret = pthread_getattr_np (pthread_self (), &attr)) != 0) + { + printf ("pthread_getattr_default_np failed: %s\n", strerror (ret)); + goto out; + } + + ret = (*verify_result) (&attr); + +out: + if (!do_join) + { + pthread_mutex_lock (&m); + running--; + pthread_cond_signal (&c); + pthread_mutex_unlock (&m); + + detach_failed |= ret; + } + + return (void *) (uintptr_t) ret; +} + +static int +run_threads (const pthread_attr_t *attr) +{ + pthread_t t; + void *tret = NULL; + + RETURN_IF_FAIL (pthread_setattr_default_np, attr); + + /* Run twice to ensure that the attributes do not get overwritten in the + first run somehow. */ + for (int i = 0; i < 2; i++) + { + RETURN_IF_FAIL (pthread_create, &t, NULL, thr, NULL); + if (do_join) + RETURN_IF_FAIL (pthread_join, t, &tret); + else + { + pthread_mutex_lock (&m); + running++; + pthread_mutex_unlock (&m); + } + + if (tret != NULL) + { + puts ("Thread failed"); + return 1; + } + } + + /* Stay in sync for detached threads and get their status. */ + while (!do_join) + { + pthread_mutex_lock (&m); + if (running == 0) + { + pthread_mutex_unlock (&m); + break; + } + pthread_cond_wait (&c, &m); + pthread_mutex_unlock (&m); + } + + return 0; +} + +static int +verify_detach_result (pthread_attr_t *attr) +{ + int state; + + RETURN_IF_FAIL (pthread_attr_getdetachstate, attr, &state); + + if (state != PTHREAD_CREATE_DETACHED) + { + puts ("failed to set detach state"); + return 1; + } + + return 0; +} + +static int +do_detach_test (void) +{ + pthread_attr_t attr; + + do_join = false; + RETURN_IF_FAIL (pthread_attr_init, &attr); + RETURN_IF_FAIL (pthread_attr_setdetachstate, &attr, PTHREAD_CREATE_DETACHED); + + RETURN_IF_FAIL (run_threads, &attr); + return detach_failed; +} + +static int +verify_affinity_result (pthread_attr_t *attr) +{ + cpu_set_t cpuset; + + RETURN_IF_FAIL (pthread_attr_getaffinity_np, attr, sizeof (cpuset), &cpuset); + if (!CPU_ISSET (0, &cpuset)) + { + puts ("failed to set cpu affinity"); + return 1; + } + + return 0; +} + +static int +do_affinity_test (void) +{ + pthread_attr_t attr; + + RETURN_IF_FAIL (pthread_attr_init, &attr); + + /* Processor affinity. Like scheduling policy, this could fail if the user + does not have the necessary privileges. So we only spew a warning if + pthread_create fails with EPERM. A computer has at least one CPU. */ + cpu_set_t cpuset; + CPU_ZERO (&cpuset); + CPU_SET (0, &cpuset); + RETURN_IF_FAIL (pthread_attr_setaffinity_np, &attr, sizeof (cpuset), &cpuset); + + int ret = run_threads (&attr); + + if (ret == EPERM) + { + printf ("Skipping CPU Affinity test: %s\n", strerror (ret)); + return 0; + } + else if (ret != 0) + return ret; + + return 0; +} + +static int +verify_sched_result (pthread_attr_t *attr) +{ + int inherited, policy; + struct sched_param param; + + RETURN_IF_FAIL (pthread_attr_getinheritsched, attr, &inherited); + if (inherited != PTHREAD_EXPLICIT_SCHED) + { + puts ("failed to set EXPLICIT_SCHED (%d != %d)"); + return 1; + } + + RETURN_IF_FAIL (pthread_attr_getschedpolicy, attr, &policy); + if (policy != SCHED_RR) + { + printf ("failed to set SCHED_RR (%d != %d)\n", policy, SCHED_RR); + return 1; + } + + RETURN_IF_FAIL (pthread_attr_getschedparam, attr, ¶m); + if (param.sched_priority != 42) + { + printf ("failed to set sched_priority (%d != %d)\n", + param.sched_priority, 42); + return 1; + } + + return 0; +} + +static int +do_sched_test (void) +{ + pthread_attr_t attr; + + RETURN_IF_FAIL (pthread_attr_init, &attr); + + /* Scheduling policy. Note that we don't always test these since it's + possible that the user the tests run as don't have the appropriate + privileges. */ + RETURN_IF_FAIL (pthread_attr_setinheritsched, &attr, PTHREAD_EXPLICIT_SCHED); + RETURN_IF_FAIL (pthread_attr_setschedpolicy, &attr, SCHED_RR); + + struct sched_param param; + param.sched_priority = 42; + RETURN_IF_FAIL (pthread_attr_setschedparam, &attr, ¶m); + + int ret = run_threads (&attr); + + if (ret == EPERM) + { + printf ("Skipping Scheduler Attributes test: %s\n", strerror (ret)); + return 0; + } + else if (ret != 0) + return ret; + + return 0; +} + +static int +verify_guardsize_result (pthread_attr_t *attr) +{ + size_t guard; + + RETURN_IF_FAIL (pthread_attr_getguardsize, attr, &guard); + + if (guardsize != guard) + { + printf ("failed to set guardsize (%zu, %zu)\n", guardsize, guard); + return 1; + } + + return 0; +} + +static int +do_guardsize_test (void) +{ + long int pagesize = sysconf (_SC_PAGESIZE); + pthread_attr_t attr; + + if (pagesize < 0) + { + printf ("sysconf failed: %s\n", strerror (errno)); + return 1; + } + + RETURN_IF_FAIL (pthread_getattr_default_np, &attr); + + /* Increase default guardsize by a page. */ + RETURN_IF_FAIL (pthread_attr_getguardsize, &attr, &guardsize); + guardsize += pagesize; + RETURN_IF_FAIL (pthread_attr_setguardsize, &attr, guardsize); + RETURN_IF_FAIL (run_threads, &attr); + + return 0; +} + +static int +verify_stacksize_result (pthread_attr_t *attr) +{ + size_t stack; + + RETURN_IF_FAIL (pthread_attr_getstacksize, attr, &stack); + + if (stacksize != stack) + { + printf ("failed to set default stacksize (%zu, %zu)\n", stacksize, stack); + return 1; + } + + return 0; +} + +static int +do_stacksize_test (void) +{ + long int pagesize = sysconf (_SC_PAGESIZE); + pthread_attr_t attr; + + if (pagesize < 0) + { + printf ("sysconf failed: %s\n", strerror (errno)); + return 1; + } + + /* Perturb the size by a page so that we're not aligned on the 64K boundary. + pthread_create does this perturbation on x86 to avoid causing the 64k + aliasing conflict. We want to prevent pthread_create from doing that + since it is not consistent for all architectures. */ + stacksize += pagesize; + + RETURN_IF_FAIL (pthread_attr_init, &attr); + + /* Run twice to ensure that we don't give a false positive. */ + RETURN_IF_FAIL (pthread_attr_setstacksize, &attr, stacksize); + RETURN_IF_FAIL (run_threads, &attr); + stacksize *= 2; + RETURN_IF_FAIL (pthread_attr_setstacksize, &attr, stacksize); + RETURN_IF_FAIL (run_threads, &attr); + return 0; +} + +/* We test each attribute separately because sched and affinity tests may need + additional user privileges that may not be available during the test run. + Each attribute test is a set of two functions, viz. a function to set the + default attribute (do_foo_test) and another to verify its result + (verify_foo_result). Each test spawns a thread and checks (1) if the + attribute values were applied correctly and (2) if the change in the default + value reflected. */ +static int +do_test (void) +{ + puts ("stacksize test"); + verify_result = verify_stacksize_result; + RETURN_IF_FAIL (do_stacksize_test); + + puts ("guardsize test"); + verify_result = verify_guardsize_result; + RETURN_IF_FAIL (do_guardsize_test); + + puts ("sched test"); + verify_result = verify_sched_result; + RETURN_IF_FAIL (do_sched_test); + + puts ("affinity test"); + verify_result = verify_affinity_result; + RETURN_IF_FAIL (do_affinity_test); + + puts ("detach test"); + verify_result = verify_detach_result; + RETURN_IF_FAIL (do_detach_test); + + return 0; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" |