diff options
author | Siddhesh Poyarekar <siddhesh@redhat.com> | 2013-06-15 12:24:15 +0530 |
---|---|---|
committer | Siddhesh Poyarekar <siddhesh@redhat.com> | 2013-06-15 12:24:15 +0530 |
commit | 61dd6208fb1e59a423b6dfa712a3c896c34b2590 (patch) | |
tree | 3417035a17046120bfedeafe2e7db9e366380101 /nptl | |
parent | 601eb33debf0c7548f52ba72cec4b3f362105e39 (diff) | |
download | glibc-61dd6208fb1e59a423b6dfa712a3c896c34b2590.tar.gz glibc-61dd6208fb1e59a423b6dfa712a3c896c34b2590.tar.xz glibc-61dd6208fb1e59a423b6dfa712a3c896c34b2590.zip |
New API to set default thread attributes
This patch introduces two new convenience functions to set the default thread attributes used for creating threads. This allows a programmer to set the default thread attributes just once in a process and then run pthread_create without additional attributes.
Diffstat (limited to 'nptl')
-rw-r--r-- | nptl/ChangeLog | 27 | ||||
-rw-r--r-- | nptl/Makefile | 5 | ||||
-rw-r--r-- | nptl/Versions | 5 | ||||
-rw-r--r-- | nptl/allocatestack.c | 12 | ||||
-rw-r--r-- | nptl/nptl-init.c | 2 | ||||
-rw-r--r-- | nptl/pthreadP.h | 1 | ||||
-rw-r--r-- | nptl/pthread_attr_getstacksize.c | 10 | ||||
-rw-r--r-- | nptl/pthread_create.c | 51 | ||||
-rw-r--r-- | nptl/pthread_getattr_default_np.c | 37 | ||||
-rw-r--r-- | nptl/pthread_setattr_default_np.c | 110 | ||||
-rw-r--r-- | nptl/sysdeps/pthread/pthread.h | 8 | ||||
-rw-r--r-- | nptl/tst-default-attr.c | 385 | ||||
-rw-r--r-- | nptl/vars.c | 3 |
13 files changed, 643 insertions, 13 deletions
diff --git a/nptl/ChangeLog b/nptl/ChangeLog index 64b674c678..0e3b0187c4 100644 --- a/nptl/ChangeLog +++ b/nptl/ChangeLog @@ -1,3 +1,30 @@ +2013-06-15 Siddhesh Poyarekar <siddhesh@redhat.com> + + * Versions (libpthread): Add GLIBC_2.18. + (GLIBC_2.18): Add pthread_setattr_default_np and + pthread_getattr_default_np. + * allocatestack.c (allocate_stack): Synchronize read from + __default_pthread_attr. + (__reclaim_stacks): Initialize __default_pthread_attr_lock. + * nptl-init.c (__pthread_initialize_minimal_internal): + Synchronize write to __default_pthread_attr. + * pthreadP.h (__default_pthread_attr_lock): Declare. + * pthread_attr_getstacksize (__pthread_attr_getstacksize): + Synchronize read from __default_pthread_attr. + * pthread_create.c (__pthread_create_2_1): Make a local copy of + __default_pthread_attr. Check value of flags in IATTR even if + input ATTR is NULL. + * pthread_getattr_default_np.c: New file. + * pthread_setattr_default_np.c: New file. + * sysdeps/pthread/pthread.h [__USE_GNU] + (pthread_getattr_default_np, pthread_setattr_default_np): + Declare. + * tst-default-attr.c: New test case. + * Makefile (libpthread-routines): Add + pthread_setattr_default_np and pthread_getattr_default_np. + (tests): Add tst-default-attr. + * vars.c (__default_pthread_attr_lock): Declare and initialize. + 2013-06-13 Siddhesh Poyarekar <siddhesh@redhat.com> Carlos O'Donell <carlos@redhat.com> diff --git a/nptl/Makefile b/nptl/Makefile index 4788bd8035..cd601e5f5a 100644 --- a/nptl/Makefile +++ b/nptl/Makefile @@ -125,7 +125,8 @@ libpthread-routines = nptl-init vars events version \ pthread_mutexattr_setprioceiling tpp \ pthread_mutex_getprioceiling \ pthread_mutex_setprioceiling \ - pthread_setname pthread_getname + pthread_setname pthread_getname \ + pthread_setattr_default_np pthread_getattr_default_np # pthread_setuid pthread_seteuid pthread_setreuid \ # pthread_setresuid \ # pthread_setgid pthread_setegid pthread_setregid \ @@ -201,7 +202,7 @@ CFLAGS-pt-system.c = -fexceptions tests = tst-typesizes \ - tst-attr1 tst-attr2 tst-attr3 \ + tst-attr1 tst-attr2 tst-attr3 tst-default-attr \ tst-mutex1 tst-mutex2 tst-mutex3 tst-mutex4 tst-mutex5 tst-mutex6 \ tst-mutex7 tst-mutex8 tst-mutex9 tst-mutex5a tst-mutex7a \ tst-mutexpi1 tst-mutexpi2 tst-mutexpi3 tst-mutexpi4 tst-mutexpi5 \ diff --git a/nptl/Versions b/nptl/Versions index 6a10375502..bb1127701e 100644 --- a/nptl/Versions +++ b/nptl/Versions @@ -252,6 +252,11 @@ libpthread { pthread_setname_np; pthread_getname_np; }; + GLIBC_2.18 { + pthread_getattr_default_np; + pthread_setattr_default_np; + } + GLIBC_PRIVATE { __pthread_initialize_minimal; __pthread_clock_gettime; __pthread_clock_settime; diff --git a/nptl/allocatestack.c b/nptl/allocatestack.c index 56bf2570f8..1e0fe1f18d 100644 --- a/nptl/allocatestack.c +++ b/nptl/allocatestack.c @@ -358,7 +358,14 @@ allocate_stack (const struct pthread_attr *attr, struct pthread **pdp, /* Get the stack size from the attribute if it is set. Otherwise we use the default we determined at start time. */ - size = attr->stacksize ?: __default_pthread_attr.stacksize; + if (attr->stacksize != 0) + size = attr->stacksize; + else + { + lll_lock (__default_pthread_attr_lock, LLL_PRIVATE); + size = __default_pthread_attr.stacksize; + lll_unlock (__default_pthread_attr_lock, LLL_PRIVATE); + } /* Get memory for the stack. */ if (__builtin_expect (attr->flags & ATTR_FLAG_STACKADDR, 0)) @@ -919,8 +926,9 @@ __reclaim_stacks (void) in_flight_stack = 0; - /* Initialize the lock. */ + /* Initialize locks. */ stack_cache_lock = LLL_LOCK_INITIALIZER; + __default_pthread_attr_lock = LLL_LOCK_INITIALIZER; } diff --git a/nptl/nptl-init.c b/nptl/nptl-init.c index 63fb729fc6..4e99a38e9e 100644 --- a/nptl/nptl-init.c +++ b/nptl/nptl-init.c @@ -423,8 +423,10 @@ __pthread_initialize_minimal_internal (void) /* Round the resource limit up to page size. */ limit.rlim_cur = (limit.rlim_cur + pagesz - 1) & -pagesz; + lll_lock (__default_pthread_attr_lock, LLL_PRIVATE); __default_pthread_attr.stacksize = limit.rlim_cur; __default_pthread_attr.guardsize = GLRO (dl_pagesize); + lll_unlock (__default_pthread_attr_lock, LLL_PRIVATE); #ifdef SHARED /* Transfer the old value from the dynamic linker's internal location. */ diff --git a/nptl/pthreadP.h b/nptl/pthreadP.h index fd52b07482..7883fdfa10 100644 --- a/nptl/pthreadP.h +++ b/nptl/pthreadP.h @@ -149,6 +149,7 @@ enum /* Default pthread attributes. */ extern struct pthread_attr __default_pthread_attr attribute_hidden; +extern int __default_pthread_attr_lock attribute_hidden; /* Size and alignment of static TLS block. */ extern size_t __static_tls_size attribute_hidden; diff --git a/nptl/pthread_attr_getstacksize.c b/nptl/pthread_attr_getstacksize.c index 42d3f8f44c..84c31cd6e4 100644 --- a/nptl/pthread_attr_getstacksize.c +++ b/nptl/pthread_attr_getstacksize.c @@ -30,9 +30,17 @@ __pthread_attr_getstacksize (attr, stacksize) assert (sizeof (*attr) >= sizeof (struct pthread_attr)); iattr = (struct pthread_attr *) attr; + size_t size = iattr->stacksize; + /* If the user has not set a stack size we return what the system will use as the default. */ - *stacksize = iattr->stacksize ?: __default_pthread_attr.stacksize; + if (size == 0) + { + lll_lock (__default_pthread_attr_lock, LLL_PRIVATE); + size = __default_pthread_attr.stacksize; + lll_unlock (__default_pthread_attr_lock, LLL_PRIVATE); + } + *stacksize = size; return 0; } diff --git a/nptl/pthread_create.c b/nptl/pthread_create.c index b78bd95c46..7f714f8903 100644 --- a/nptl/pthread_create.c +++ b/nptl/pthread_create.c @@ -449,18 +449,47 @@ __pthread_create_2_1 (newthread, attr, start_routine, arg) STACK_VARIABLES; const struct pthread_attr *iattr = (struct pthread_attr *) attr; + struct pthread_attr default_attr; + bool free_cpuset = false; if (iattr == NULL) - /* Is this the best idea? On NUMA machines this could mean - accessing far-away memory. */ - iattr = &__default_pthread_attr; + { + lll_lock (__default_pthread_attr_lock, LLL_PRIVATE); + default_attr = __default_pthread_attr; + size_t cpusetsize = default_attr.cpusetsize; + if (cpusetsize > 0) + { + cpu_set_t *cpuset; + if (__glibc_likely (__libc_use_alloca (cpusetsize))) + cpuset = __alloca (cpusetsize); + else + { + cpuset = malloc (cpusetsize); + if (cpuset == NULL) + { + lll_unlock (__default_pthread_attr_lock, LLL_PRIVATE); + return ENOMEM; + } + free_cpuset = true; + } + memcpy (cpuset, default_attr.cpuset, cpusetsize); + default_attr.cpuset = cpuset; + } + lll_unlock (__default_pthread_attr_lock, LLL_PRIVATE); + iattr = &default_attr; + } struct pthread *pd = NULL; int err = ALLOCATE_STACK (iattr, &pd); + int retval = 0; + if (__builtin_expect (err != 0, 0)) /* Something went wrong. Maybe a parameter of the attributes is invalid or we could not allocate memory. Note we have to translate error codes. */ - return err == ENOMEM ? EAGAIN : err; + { + retval = err == ENOMEM ? EAGAIN : err; + goto out; + } /* Initialize the TCB. All initializations with zero should be @@ -511,8 +540,7 @@ __pthread_create_2_1 (newthread, attr, start_routine, arg) #endif /* Determine scheduling parameters for the thread. */ - if (attr != NULL - && __builtin_expect ((iattr->flags & ATTR_FLAG_NOTINHERITSCHED) != 0, 0) + if (__builtin_expect ((iattr->flags & ATTR_FLAG_NOTINHERITSCHED) != 0, 0) && (iattr->flags & (ATTR_FLAG_SCHED_SET | ATTR_FLAG_POLICY_SET)) != 0) { INTERNAL_SYSCALL_DECL (scerr); @@ -551,7 +579,8 @@ __pthread_create_2_1 (newthread, attr, start_routine, arg) __deallocate_stack (pd); - return EINVAL; + retval = EINVAL; + goto out; } } @@ -561,7 +590,13 @@ __pthread_create_2_1 (newthread, attr, start_routine, arg) LIBC_PROBE (pthread_create, 4, newthread, attr, start_routine, arg); /* Start the thread. */ - return create_thread (pd, iattr, STACK_VARIABLES_ARGS); + retval = create_thread (pd, iattr, STACK_VARIABLES_ARGS); + + out: + if (__glibc_unlikely (free_cpuset)) + free (default_attr.cpuset); + + return retval; } versioned_symbol (libpthread, __pthread_create_2_1, pthread_create, GLIBC_2_1); diff --git a/nptl/pthread_getattr_default_np.c b/nptl/pthread_getattr_default_np.c new file mode 100644 index 0000000000..f3a6b47464 --- /dev/null +++ b/nptl/pthread_getattr_default_np.c @@ -0,0 +1,37 @@ +/* Get the default attributes used by pthread_create in the process. + 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 <errno.h> +#include <stdlib.h> +#include <pthreadP.h> +#include <assert.h> + +int +pthread_getattr_default_np (pthread_attr_t *out) +{ + struct pthread_attr *real_out; + + assert (sizeof (*out) >= sizeof (struct pthread_attr)); + real_out = (struct pthread_attr *) out; + + lll_lock (__default_pthread_attr_lock, LLL_PRIVATE); + *real_out = __default_pthread_attr; + lll_unlock (__default_pthread_attr_lock, LLL_PRIVATE); + + return 0; +} diff --git a/nptl/pthread_setattr_default_np.c b/nptl/pthread_setattr_default_np.c new file mode 100644 index 0000000000..e0b5239612 --- /dev/null +++ b/nptl/pthread_setattr_default_np.c @@ -0,0 +1,110 @@ +/* Set the default attributes to be used by pthread_create in the process. + 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 <errno.h> +#include <stdlib.h> +#include <pthreadP.h> +#include <assert.h> + +int +pthread_setattr_default_np (const pthread_attr_t *in) +{ + const struct pthread_attr *real_in; + struct pthread_attr attrs; + int ret; + + assert (sizeof (*in) >= sizeof (struct pthread_attr)); + real_in = (struct pthread_attr *) in; + + /* Catch invalid values. */ + int policy = real_in->schedpolicy; + ret = check_sched_policy_attr (policy); + if (ret) + return ret; + + const struct sched_param *param = &real_in->schedparam; + if (param->sched_priority > 0) + { + ret = check_sched_priority_attr (param->sched_priority, policy); + if (ret) + return ret; + } + + ret = check_cpuset_attr (real_in->cpuset, real_in->cpusetsize); + if (ret) + return ret; + + /* stacksize == 0 is fine. It means that we don't change the current + value. */ + if (real_in->stacksize != 0) + { + ret = check_stacksize_attr (real_in->stacksize); + if (ret) + return ret; + } + + /* Having a default stack address is wrong. */ + if (real_in->flags & ATTR_FLAG_STACKADDR) + return EINVAL; + + attrs = *real_in; + + /* Now take the lock because we start writing into + __default_pthread_attr. */ + lll_lock (__default_pthread_attr_lock, LLL_PRIVATE); + + /* Free the cpuset if the input is 0. Otherwise copy in the cpuset + contents. */ + size_t cpusetsize = attrs.cpusetsize; + if (cpusetsize == 0) + { + free (__default_pthread_attr.cpuset); + __default_pthread_attr.cpuset = NULL; + } + else if (cpusetsize == __default_pthread_attr.cpusetsize) + { + attrs.cpuset = __default_pthread_attr.cpuset; + memcpy (attrs.cpuset, real_in->cpuset, cpusetsize); + } + else + { + /* This may look wrong at first sight, but it isn't. We're freeing + __default_pthread_attr.cpuset and allocating to attrs.cpuset because + we'll copy over all of attr to __default_pthread_attr later. */ + cpu_set_t *newp = realloc (__default_pthread_attr.cpuset, + cpusetsize); + + if (newp == NULL) + { + ret = ENOMEM; + goto out; + } + + attrs.cpuset = newp; + memcpy (attrs.cpuset, real_in->cpuset, cpusetsize); + } + + /* We don't want to accidentally set the default stacksize to zero. */ + if (attrs.stacksize == 0) + attrs.stacksize = __default_pthread_attr.stacksize; + __default_pthread_attr = attrs; + + out: + lll_unlock (__default_pthread_attr_lock, LLL_PRIVATE); + return ret; +} diff --git a/nptl/sysdeps/pthread/pthread.h b/nptl/sysdeps/pthread/pthread.h index 10bcb806b3..ded5ae59ff 100644 --- a/nptl/sysdeps/pthread/pthread.h +++ b/nptl/sysdeps/pthread/pthread.h @@ -404,6 +404,14 @@ extern int pthread_attr_getaffinity_np (const pthread_attr_t *__attr, cpu_set_t *__cpuset) __THROW __nonnull ((1, 3)); +/* Get the default attributes used by pthread_create in this process. */ +extern int pthread_getattr_default_np (pthread_attr_t *__attr) + __THROW __nonnull ((1)); + +/* Set the default attributes to be used by pthread_create in this + process. */ +extern int pthread_setattr_default_np (const pthread_attr_t *__attr) + __THROW __nonnull ((1)); /* Initialize thread attribute *ATTR with attributes corresponding to the already running thread TH. It shall be called on uninitialized ATTR 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" diff --git a/nptl/vars.c b/nptl/vars.c index 45ca486143..3e2db42361 100644 --- a/nptl/vars.c +++ b/nptl/vars.c @@ -24,6 +24,9 @@ provide any. */ struct pthread_attr __default_pthread_attr attribute_hidden; +/* Mutex protecting __default_pthread_attr. */ +int __default_pthread_attr_lock = LLL_LOCK_INITIALIZER; + /* Flag whether the machine is SMP or not. */ int __is_smp attribute_hidden; |