diff options
author | Jakub Jelinek <jakub@redhat.com> | 2006-08-15 05:53:50 +0000 |
---|---|---|
committer | Jakub Jelinek <jakub@redhat.com> | 2006-08-15 05:53:50 +0000 |
commit | 3a2ba84afba2d9b315c65aa46ba036fd22ef4fb0 (patch) | |
tree | 47a553a26365a6738426804af7609434daf047b8 /nptl | |
parent | 256926546643ba909661c83e7a7d24d733b9b390 (diff) | |
download | glibc-3a2ba84afba2d9b315c65aa46ba036fd22ef4fb0.tar.gz glibc-3a2ba84afba2d9b315c65aa46ba036fd22ef4fb0.tar.xz glibc-3a2ba84afba2d9b315c65aa46ba036fd22ef4fb0.zip |
Updated to fedora-glibc-20060815T0533
Diffstat (limited to 'nptl')
-rw-r--r-- | nptl/ChangeLog | 51 | ||||
-rw-r--r-- | nptl/Makefile | 2 | ||||
-rw-r--r-- | nptl/descr.h | 11 | ||||
-rw-r--r-- | nptl/pthreadP.h | 32 | ||||
-rw-r--r-- | nptl/pthread_create.c | 9 | ||||
-rw-r--r-- | nptl/pthread_join.c | 78 | ||||
-rw-r--r-- | nptl/pthread_mutex_getprioceiling.c | 7 | ||||
-rw-r--r-- | nptl/pthread_mutex_init.c | 27 | ||||
-rw-r--r-- | nptl/pthread_mutex_lock.c | 84 | ||||
-rw-r--r-- | nptl/pthread_mutex_setprioceiling.c | 81 | ||||
-rw-r--r-- | nptl/pthread_mutex_timedlock.c | 113 | ||||
-rw-r--r-- | nptl/pthread_mutex_trylock.c | 73 | ||||
-rw-r--r-- | nptl/pthread_mutex_unlock.c | 43 | ||||
-rw-r--r-- | nptl/pthread_mutexattr_getprioceiling.c | 15 | ||||
-rw-r--r-- | nptl/pthread_mutexattr_setprioceiling.c | 10 | ||||
-rw-r--r-- | nptl/pthread_setschedparam.c | 17 | ||||
-rw-r--r-- | nptl/pthread_setschedprio.c | 6 | ||||
-rw-r--r-- | nptl/sysdeps/unix/sysv/linux/bits/posix_opt.h | 7 | ||||
-rw-r--r-- | nptl/tpp.c | 172 | ||||
-rw-r--r-- | nptl/tst-join5.c | 115 | ||||
-rw-r--r-- | nptl/tst-join6.c | 2 | ||||
-rw-r--r-- | nptl/tst-mutex1.c | 4 | ||||
-rw-r--r-- | nptl/tst-mutex6.c | 4 | ||||
-rw-r--r-- | nptl/tst-mutexpp1.c | 45 | ||||
-rw-r--r-- | nptl/tst-mutexpp10.c | 334 | ||||
-rw-r--r-- | nptl/tst-mutexpp6.c | 45 | ||||
-rw-r--r-- | nptl/tst-tpp.h | 94 |
27 files changed, 1383 insertions, 98 deletions
diff --git a/nptl/ChangeLog b/nptl/ChangeLog index 6450e42574..fe6e5c06c3 100644 --- a/nptl/ChangeLog +++ b/nptl/ChangeLog @@ -1,3 +1,54 @@ +2006-08-14 Jakub Jelinek <jakub@redhat.com> + + * sysdeps/unix/sysv/linux/bits/posix_opt.h + (_POSIX_THREAD_PRIO_PROTECT): Define to 200112L. + * descr.h (struct priority_protection_data): New type. + (struct pthread): Add tpp field. + * pthreadP.h (PTHREAD_MUTEX_PP_NORMAL_NP, + PTHREAD_MUTEX_PP_RECURSIVE_NP, PTHREAD_MUTEX_PP_ERRORCHECK_NP, + PTHREAD_MUTEX_PP_ADAPTIVE_NP): New enum values. + * pthread_mutex_init.c (__pthread_mutex_init): Handle non-robust + TPP mutexes. + * pthread_mutex_lock.c (__pthread_mutex_lock): Handle TPP mutexes. + * pthread_mutex_trylock.c (__pthread_mutex_trylock): Likewise. + * pthread_mutex_timedlock.c (pthread_mutex_timedlock): Likewise. + * pthread_mutex_unlock.c (__pthread_mutex_unlock_usercnt): Likewise. + * tpp.c: New file. + * pthread_setschedparam.c (__pthread_setschedparam): Handle priority + boosted by TPP. + * pthread_setschedprio.c (pthread_setschedprio): Likewise. + * pthread_mutexattr_getprioceiling.c + (pthread_mutexattr_getprioceiling): If ceiling is 0, ensure it is + in the SCHED_FIFO priority range. + * pthread_mutexattr_setprioceiling.c + (pthread_mutexattr_setprioceiling): Fix prioceiling validation. + * pthread_mutex_getprioceiling.c (pthread_mutex_getprioceiling): Fail + if mutex is not TPP. Ceiling is now in __data.__lock. + * pthread_mutex_setprioceiling.c: Include stdbool.h. + (pthread_mutex_setprioceiling): Fix prioceiling validation. Ceiling + is now in __data.__lock. Add locking. + * pthread_create.c (__free_tcb): Free pd->tpp structure. + * Makefile (libpthread-routines): Add tpp. + (xtests): Add tst-mutexpp1, tst-mutexpp6 and tst-mutexpp10. + * tst-tpp.h: New file. + * tst-mutexpp1.c: New file. + * tst-mutexpp6.c: New file. + * tst-mutexpp10.c: New file. + * tst-mutex1.c (TEST_FUNCTION): Don't redefine if already defined. + * tst-mutex6.c (TEST_FUNCTION): Likewise. + +2006-08-12 Ulrich Drepper <drepper@redhat.com> + + [BZ #2843] + * pthread_join.c (pthread_join): Account for self being canceled + when checking for deadlocks. + * tst-join5.c: Cleanups. Allow to be used in tst-join6. + (tf1): Don't print anything after pthread_join returns, this would be + another cancellation point. + (tf2): Likewise. + * tst-join6.c: New file. + * Makefile (tests): Add tst-join6. + 2006-08-03 Ulrich Drepper <drepper@redhat.com> [BZ #2892] diff --git a/nptl/Makefile b/nptl/Makefile index 3a72d3707e..c0c09e52fe 100644 --- a/nptl/Makefile +++ b/nptl/Makefile @@ -224,7 +224,7 @@ tests = tst-typesizes \ tst-basic1 tst-basic2 tst-basic3 tst-basic4 tst-basic5 tst-basic6 \ tst-kill1 tst-kill2 tst-kill3 tst-kill4 tst-kill5 tst-kill6 \ tst-raise1 \ - tst-join1 tst-join2 tst-join3 tst-join4 tst-join5 \ + tst-join1 tst-join2 tst-join3 tst-join4 tst-join5 tst-join6 \ tst-detach1 \ tst-eintr1 tst-eintr2 tst-eintr3 tst-eintr4 tst-eintr5 \ tst-tsd1 tst-tsd2 tst-tsd3 tst-tsd4 tst-tsd5 \ diff --git a/nptl/descr.h b/nptl/descr.h index 607aa9fcdb..7acd2f4f22 100644 --- a/nptl/descr.h +++ b/nptl/descr.h @@ -111,6 +111,14 @@ struct robust_list_head }; +/* Data strcture used to handle thread priority protection. */ +struct priority_protection_data +{ + int priomax; + unsigned int priomap[]; +}; + + /* Thread descriptor data structure. */ struct pthread { @@ -343,6 +351,9 @@ struct pthread /* This is what the user specified and what we will report. */ size_t reported_guardsize; + /* Thread Priority Protection data. */ + struct priority_protection_data *tpp; + /* Resolver state. */ struct __res_state res; diff --git a/nptl/pthreadP.h b/nptl/pthreadP.h index dc98bb19c0..503e99b2b4 100644 --- a/nptl/pthreadP.h +++ b/nptl/pthreadP.h @@ -86,17 +86,31 @@ enum = PTHREAD_MUTEX_PRIO_INHERIT_NP | PTHREAD_MUTEX_ROBUST_ERRORCHECK_NP, PTHREAD_MUTEX_PI_ROBUST_ADAPTIVE_NP = PTHREAD_MUTEX_PRIO_INHERIT_NP | PTHREAD_MUTEX_ROBUST_ADAPTIVE_NP, - PTHREAD_MUTEX_PRIO_PROTECT_NP = 64 + PTHREAD_MUTEX_PRIO_PROTECT_NP = 64, + PTHREAD_MUTEX_PP_NORMAL_NP + = PTHREAD_MUTEX_PRIO_PROTECT_NP | PTHREAD_MUTEX_NORMAL, + PTHREAD_MUTEX_PP_RECURSIVE_NP + = PTHREAD_MUTEX_PRIO_PROTECT_NP | PTHREAD_MUTEX_RECURSIVE_NP, + PTHREAD_MUTEX_PP_ERRORCHECK_NP + = PTHREAD_MUTEX_PRIO_PROTECT_NP | PTHREAD_MUTEX_ERRORCHECK_NP, + PTHREAD_MUTEX_PP_ADAPTIVE_NP + = PTHREAD_MUTEX_PRIO_PROTECT_NP | PTHREAD_MUTEX_ADAPTIVE_NP }; -#define PTHREAD_MUTEX_PRIO_CEILING_SHIFT 16 -#define PTHREAD_MUTEX_PRIO_CEILING_MASK 0x00ff0000 + +/* Ceiling in __data.__lock. __data.__lock is signed, so don't + use the MSB bit in there, but in the mask also include that bit, + so that the compiler can optimize & PTHREAD_MUTEX_PRIO_CEILING_MASK + masking if the value is then shifted down by + PTHREAD_MUTEX_PRIO_CEILING_SHIFT. */ +#define PTHREAD_MUTEX_PRIO_CEILING_SHIFT 19 +#define PTHREAD_MUTEX_PRIO_CEILING_MASK 0xfff80000 /* Flags in mutex attr. */ #define PTHREAD_MUTEXATTR_PROTOCOL_SHIFT 28 #define PTHREAD_MUTEXATTR_PROTOCOL_MASK 0x30000000 -#define PTHREAD_MUTEXATTR_PRIO_CEILING_SHIFT 16 -#define PTHREAD_MUTEXATTR_PRIO_CEILING_MASK 0x00ff0000 +#define PTHREAD_MUTEXATTR_PRIO_CEILING_SHIFT 12 +#define PTHREAD_MUTEXATTR_PRIO_CEILING_MASK 0x00fff000 #define PTHREAD_MUTEXATTR_FLAG_ROBUST 0x40000000 #define PTHREAD_MUTEXATTR_FLAG_PSHARED 0x80000000 #define PTHREAD_MUTEXATTR_FLAG_BITS \ @@ -151,6 +165,14 @@ extern unsigned int __nptl_nthreads attribute_hidden; extern int __set_robust_list_avail attribute_hidden; #endif +/* Thread Priority Protection. */ +extern int __sched_fifo_min_prio attribute_hidden; +extern int __sched_fifo_max_prio attribute_hidden; +extern void __init_sched_fifo_prio (void) attribute_hidden; +extern int __pthread_tpp_change_priority (int prev_prio, int new_prio) + attribute_hidden; +extern int __pthread_current_priority (void) attribute_hidden; + /* The library can run in debugging mode where it performs a lot more tests. */ extern int __pthread_debug attribute_hidden; diff --git a/nptl/pthread_create.c b/nptl/pthread_create.c index c1ac199bb9..315722643b 100644 --- a/nptl/pthread_create.c +++ b/nptl/pthread_create.c @@ -206,6 +206,15 @@ __free_tcb (struct pthread *pd) running thread is gone. */ abort (); + /* Free TPP data. */ + if (__builtin_expect (pd->tpp != NULL, 0)) + { + struct priority_protection_data *tpp = pd->tpp; + + pd->tpp = NULL; + free (tpp); + } + /* Queue the stack memory block for reuse and exit the process. The kernel will signal via writing to the address returned by QUEUE-STACK when the stack is available. */ diff --git a/nptl/pthread_join.c b/nptl/pthread_join.c index 70dc81a023..c88d85b52f 100644 --- a/nptl/pthread_join.c +++ b/nptl/pthread_join.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2002, 2003, 2005 Free Software Foundation, Inc. +/* Copyright (C) 2002, 2003, 2005, 2006 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper <drepper@redhat.com>, 2002. @@ -27,7 +27,11 @@ static void cleanup (void *arg) { - *(void **) arg = NULL; + /* If we already changed the waiter ID, reset it. The call cannot + fail for any reason but the thread not having done that yet so + there is no reason for a loop. */ + atomic_compare_and_exchange_bool_acq ((struct pthread **) arg, NULL, + THREAD_SELF); } @@ -36,7 +40,6 @@ pthread_join (threadid, thread_return) pthread_t threadid; void **thread_return; { - struct pthread *self; struct pthread *pd = (struct pthread *) threadid; /* Make sure the descriptor is valid. */ @@ -49,12 +52,23 @@ pthread_join (threadid, thread_return) /* We cannot wait for the thread. */ return EINVAL; - self = THREAD_SELF; - if (pd == self - || (self->joinid == pd - && (pd->cancelhandling - & (CANCELING_BITMASK | CANCELED_BITMASK | EXITING_BITMASK - | TERMINATED_BITMASK)) == 0)) + struct pthread *self = THREAD_SELF; + int result = 0; + + /* During the wait we change to asynchronous cancellation. If we + are canceled the thread we are waiting for must be marked as + un-wait-ed for again. */ + pthread_cleanup_push (cleanup, &pd->joinid); + + /* Switch to asynchronous cancellation. */ + int oldtype = CANCEL_ASYNC (); + + if ((pd == self + || (self->joinid == pd + && (pd->cancelhandling + & (CANCELING_BITMASK | CANCELED_BITMASK | EXITING_BITMASK + | TERMINATED_BITMASK)) == 0)) + && !CANCEL_ENABLED_AND_CANCELED (self->cancelhandling)) /* This is a deadlock situation. The threads are waiting for each other to finish. Note that this is a "may" error. To be 100% sure we catch this error we would have to lock the data @@ -62,28 +76,17 @@ pthread_join (threadid, thread_return) two threads are really caught in this situation they will deadlock. It is the programmer's problem to figure this out. */ - return EDEADLK; - + result = EDEADLK; /* Wait for the thread to finish. If it is already locked something is wrong. There can only be one waiter. */ - if (__builtin_expect (atomic_compare_and_exchange_bool_acq (&pd->joinid, - self, - NULL), 0)) + else if (__builtin_expect (atomic_compare_and_exchange_bool_acq (&pd->joinid, + self, + NULL), 0)) /* There is already somebody waiting for the thread. */ - return EINVAL; - - - /* During the wait we change to asynchronous cancellation. If we - are cancelled the thread we are waiting for must be marked as - un-wait-ed for again. */ - pthread_cleanup_push (cleanup, &pd->joinid); - - /* Switch to asynchronous cancellation. */ - int oldtype = CANCEL_ASYNC (); - - - /* Wait for the child. */ - lll_wait_tid (pd->tid); + result = EINVAL; + else + /* Wait for the child. */ + lll_wait_tid (pd->tid); /* Restore cancellation mode. */ @@ -93,16 +96,19 @@ pthread_join (threadid, thread_return) pthread_cleanup_pop (0); - /* We mark the thread as terminated and as joined. */ - pd->tid = -1; + if (__builtin_expect (result == 0, 1)) + { + /* We mark the thread as terminated and as joined. */ + pd->tid = -1; - /* Store the return value if the caller is interested. */ - if (thread_return != NULL) - *thread_return = pd->result; + /* Store the return value if the caller is interested. */ + if (thread_return != NULL) + *thread_return = pd->result; - /* Free the TCB. */ - __free_tcb (pd); + /* Free the TCB. */ + __free_tcb (pd); + } - return 0; + return result; } diff --git a/nptl/pthread_mutex_getprioceiling.c b/nptl/pthread_mutex_getprioceiling.c index 4d1bc28431..1ce5eaebef 100644 --- a/nptl/pthread_mutex_getprioceiling.c +++ b/nptl/pthread_mutex_getprioceiling.c @@ -18,6 +18,7 @@ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ +#include <errno.h> #include <pthreadP.h> @@ -26,7 +27,11 @@ pthread_mutex_getprioceiling (mutex, prioceiling) const pthread_mutex_t *mutex; int *prioceiling; { - *prioceiling = (mutex->__data.__kind & PTHREAD_MUTEX_PRIO_CEILING_MASK) + if (__builtin_expect ((mutex->__data.__kind + & PTHREAD_MUTEX_PRIO_PROTECT_NP) == 0, 0)) + return EINVAL; + + *prioceiling = (mutex->__data.__lock & PTHREAD_MUTEX_PRIO_CEILING_MASK) >> PTHREAD_MUTEX_PRIO_CEILING_SHIFT; return 0; diff --git a/nptl/pthread_mutex_init.c b/nptl/pthread_mutex_init.c index 6ceca86052..96f1fb00f8 100644 --- a/nptl/pthread_mutex_init.c +++ b/nptl/pthread_mutex_init.c @@ -46,7 +46,6 @@ __pthread_mutex_init (mutex, mutexattr) imutexattr = (const struct pthread_mutexattr *) mutexattr ?: &default_attr; /* Sanity checks. */ - // XXX For now we don't support priority protected mutexes. switch (__builtin_expect (imutexattr->mutexkind & PTHREAD_MUTEXATTR_PROTOCOL_MASK, PTHREAD_PRIO_NONE @@ -72,7 +71,10 @@ __pthread_mutex_init (mutex, mutexattr) break; default: - return ENOTSUP; + /* XXX: For now we don't support robust priority protected mutexes. */ + if (imutexattr->mutexkind & PTHREAD_MUTEXATTR_FLAG_ROBUST) + return ENOTSUP; + break; } /* Clear the whole variable. */ @@ -100,15 +102,18 @@ __pthread_mutex_init (mutex, mutexattr) case PTHREAD_PRIO_PROTECT << PTHREAD_MUTEXATTR_PROTOCOL_SHIFT: mutex->__data.__kind |= PTHREAD_MUTEX_PRIO_PROTECT_NP; - if (PTHREAD_MUTEX_PRIO_CEILING_MASK - == PTHREAD_MUTEXATTR_PRIO_CEILING_MASK) - mutex->__data.__kind |= (imutexattr->mutexkind - & PTHREAD_MUTEXATTR_PRIO_CEILING_MASK); - else - mutex->__data.__kind |= ((imutexattr->mutexkind - & PTHREAD_MUTEXATTR_PRIO_CEILING_MASK) - >> PTHREAD_MUTEXATTR_PRIO_CEILING_SHIFT) - << PTHREAD_MUTEX_PRIO_CEILING_SHIFT; + + int ceiling = (imutexattr->mutexkind + & PTHREAD_MUTEXATTR_PRIO_CEILING_MASK) + >> PTHREAD_MUTEXATTR_PRIO_CEILING_SHIFT; + if (! ceiling) + { + if (__sched_fifo_min_prio == -1) + __init_sched_fifo_prio (); + if (ceiling < __sched_fifo_min_prio) + ceiling = __sched_fifo_min_prio; + } + mutex->__data.__lock = ceiling << PTHREAD_MUTEX_PRIO_CEILING_SHIFT; break; default: diff --git a/nptl/pthread_mutex_lock.c b/nptl/pthread_mutex_lock.c index a19c907695..52cc47f4cc 100644 --- a/nptl/pthread_mutex_lock.c +++ b/nptl/pthread_mutex_lock.c @@ -335,6 +335,90 @@ __pthread_mutex_lock (mutex) } break; + case PTHREAD_MUTEX_PP_RECURSIVE_NP: + case PTHREAD_MUTEX_PP_ERRORCHECK_NP: + case PTHREAD_MUTEX_PP_NORMAL_NP: + case PTHREAD_MUTEX_PP_ADAPTIVE_NP: + { + int kind = mutex->__data.__kind & PTHREAD_MUTEX_KIND_MASK_NP; + + oldval = mutex->__data.__lock; + + /* Check whether we already hold the mutex. */ + if (mutex->__data.__owner == id) + { + if (kind == PTHREAD_MUTEX_ERRORCHECK_NP) + return EDEADLK; + + if (kind == PTHREAD_MUTEX_RECURSIVE_NP) + { + /* Just bump the counter. */ + if (__builtin_expect (mutex->__data.__count + 1 == 0, 0)) + /* Overflow of the counter. */ + return EAGAIN; + + ++mutex->__data.__count; + + return 0; + } + } + + int oldprio = -1, ceilval; + do + { + int ceiling = (oldval & PTHREAD_MUTEX_PRIO_CEILING_MASK) + >> PTHREAD_MUTEX_PRIO_CEILING_SHIFT; + + if (__pthread_current_priority () > ceiling) + { + if (oldprio != -1) + __pthread_tpp_change_priority (oldprio, -1); + return EINVAL; + } + + retval = __pthread_tpp_change_priority (oldprio, ceiling); + if (retval) + return retval; + + ceilval = ceiling << PTHREAD_MUTEX_PRIO_CEILING_SHIFT; + oldprio = ceiling; + + oldval + = atomic_compare_and_exchange_val_acq (&mutex->__data.__lock, +#ifdef NO_INCR + ceilval | 2, +#else + ceilval | 1, +#endif + ceilval); + + if (oldval == ceilval) + break; + + do + { + oldval + = atomic_compare_and_exchange_val_acq (&mutex->__data.__lock, + ceilval | 2, + ceilval | 1); + + if ((oldval & PTHREAD_MUTEX_PRIO_CEILING_MASK) != ceilval) + break; + + if (oldval != ceilval) + lll_futex_wait (&mutex->__data.__lock, ceilval | 2); + } + while (atomic_compare_and_exchange_val_acq (&mutex->__data.__lock, + ceilval | 2, ceilval) + != ceilval); + } + while ((oldval & PTHREAD_MUTEX_PRIO_CEILING_MASK) != ceilval); + + assert (mutex->__data.__owner == 0); + mutex->__data.__count = 1; + } + break; + default: /* Correct code cannot set any other type. */ return EINVAL; diff --git a/nptl/pthread_mutex_setprioceiling.c b/nptl/pthread_mutex_setprioceiling.c index 3271f8833a..cd13d1c14c 100644 --- a/nptl/pthread_mutex_setprioceiling.c +++ b/nptl/pthread_mutex_setprioceiling.c @@ -18,6 +18,7 @@ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ +#include <stdbool.h> #include <errno.h> #include <pthreadP.h> @@ -33,23 +34,83 @@ pthread_mutex_setprioceiling (mutex, prioceiling, old_ceiling) if ((mutex->__data.__kind & PTHREAD_MUTEX_PRIO_PROTECT_NP) == 0) return EINVAL; - if (prioceiling < 0 || __builtin_expect (prioceiling > 255, 0)) + if (__sched_fifo_min_prio == -1) + __init_sched_fifo_prio (); + + if (__builtin_expect (prioceiling < __sched_fifo_min_prio, 0) + || __builtin_expect (prioceiling > __sched_fifo_max_prio, 0) + || __builtin_expect ((prioceiling + & (PTHREAD_MUTEXATTR_PRIO_CEILING_MASK + >> PTHREAD_MUTEXATTR_PRIO_CEILING_SHIFT)) + != prioceiling, 0)) return EINVAL; - /* XXX This needs to lock with TID, but shouldn't obey priority protect - protocol. */ - /* lll_xxx_mutex_lock (mutex->__data.__lock); */ + /* Check whether we already hold the mutex. */ + bool locked = false; + if (mutex->__data.__owner == THREAD_GETMEM (THREAD_SELF, tid)) + { + if (mutex->__data.__kind == PTHREAD_MUTEX_PP_ERRORCHECK_NP) + return EDEADLK; + + if (mutex->__data.__kind == PTHREAD_MUTEX_PP_RECURSIVE_NP) + locked = true; + } + + int oldval = mutex->__data.__lock; + if (! locked) + do + { + /* Need to lock the mutex, but without obeying the priority + protect protocol. */ + int ceilval = (oldval & PTHREAD_MUTEX_PRIO_CEILING_MASK); + + oldval = atomic_compare_and_exchange_val_acq (&mutex->__data.__lock, + ceilval | 1, ceilval); + if (oldval == ceilval) + break; + + do + { + oldval + = atomic_compare_and_exchange_val_acq (&mutex->__data.__lock, + ceilval | 2, + ceilval | 1); + + if ((oldval & PTHREAD_MUTEX_PRIO_CEILING_MASK) != ceilval) + break; + + if (oldval != ceilval) + lll_futex_wait (&mutex->__data.__lock, ceilval | 2); + } + while (atomic_compare_and_exchange_val_acq (&mutex->__data.__lock, + ceilval | 2, ceilval) + != ceilval); + + if ((oldval & PTHREAD_MUTEX_PRIO_CEILING_MASK) != ceilval) + continue; + } + while (0); + + int oldprio = (oldval & PTHREAD_MUTEX_PRIO_CEILING_MASK) + >> PTHREAD_MUTEX_PRIO_CEILING_SHIFT; + if (locked) + { + int ret = __pthread_tpp_change_priority (oldprio, prioceiling); + if (ret) + return ret; + } if (old_ceiling != NULL) - *old_ceiling = (mutex->__data.__kind & PTHREAD_MUTEX_PRIO_CEILING_MASK) - >> PTHREAD_MUTEX_PRIO_CEILING_SHIFT; + *old_ceiling = oldprio; - int newkind = (mutex->__data.__kind & ~PTHREAD_MUTEX_PRIO_CEILING_MASK); - mutex->__data.__kind = newkind + int newlock = 0; + if (locked) + newlock = (mutex->__data.__lock & ~PTHREAD_MUTEX_PRIO_CEILING_MASK); + mutex->__data.__lock = newlock | (prioceiling << PTHREAD_MUTEX_PRIO_CEILING_SHIFT); + atomic_full_barrier (); - /* XXX This needs to unlock the above special kind of lock. */ - /* lll_xxx_mutex_unlock (mutex->__data.__lock); */ + lll_futex_wake (&mutex->__data.__lock, INT_MAX); return 0; } diff --git a/nptl/pthread_mutex_timedlock.c b/nptl/pthread_mutex_timedlock.c index dcff4f81a8..c8e6b8507a 100644 --- a/nptl/pthread_mutex_timedlock.c +++ b/nptl/pthread_mutex_timedlock.c @@ -340,6 +340,119 @@ pthread_mutex_timedlock (mutex, abstime) } break; + case PTHREAD_MUTEX_PP_RECURSIVE_NP: + case PTHREAD_MUTEX_PP_ERRORCHECK_NP: + case PTHREAD_MUTEX_PP_NORMAL_NP: + case PTHREAD_MUTEX_PP_ADAPTIVE_NP: + { + int kind = mutex->__data.__kind & PTHREAD_MUTEX_KIND_MASK_NP; + + oldval = mutex->__data.__lock; + + /* Check whether we already hold the mutex. */ + if (mutex->__data.__owner == id) + { + if (kind == PTHREAD_MUTEX_ERRORCHECK_NP) + return EDEADLK; + + if (kind == PTHREAD_MUTEX_RECURSIVE_NP) + { + /* Just bump the counter. */ + if (__builtin_expect (mutex->__data.__count + 1 == 0, 0)) + /* Overflow of the counter. */ + return EAGAIN; + + ++mutex->__data.__count; + + return 0; + } + } + + int oldprio = -1, ceilval; + do + { + int ceiling = (oldval & PTHREAD_MUTEX_PRIO_CEILING_MASK) + >> PTHREAD_MUTEX_PRIO_CEILING_SHIFT; + + if (__pthread_current_priority () > ceiling) + { + result = EINVAL; + failpp: + if (oldprio != -1) + __pthread_tpp_change_priority (oldprio, -1); + return result; + } + + result = __pthread_tpp_change_priority (oldprio, ceiling); + if (result) + return result; + + ceilval = ceiling << PTHREAD_MUTEX_PRIO_CEILING_SHIFT; + oldprio = ceiling; + + oldval + = atomic_compare_and_exchange_val_acq (&mutex->__data.__lock, + ceilval | 1, ceilval); + + if (oldval == ceilval) + break; + + do + { + oldval + = atomic_compare_and_exchange_val_acq (&mutex->__data.__lock, + ceilval | 2, + ceilval | 1); + + if ((oldval & PTHREAD_MUTEX_PRIO_CEILING_MASK) != ceilval) + break; + + if (oldval != ceilval) + { + /* Reject invalid timeouts. */ + if (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) + { + result = EINVAL; + goto failpp; + } + + struct timeval tv; + struct timespec rt; + + /* Get the current time. */ + (void) __gettimeofday (&tv, NULL); + + /* Compute relative timeout. */ + rt.tv_sec = abstime->tv_sec - tv.tv_sec; + rt.tv_nsec = abstime->tv_nsec - tv.tv_usec * 1000; + if (rt.tv_nsec < 0) + { + rt.tv_nsec += 1000000000; + --rt.tv_sec; + } + + /* Already timed out? */ + if (rt.tv_sec < 0) + { + result = ETIMEDOUT; + goto failpp; + } + + lll_futex_timed_wait (&mutex->__data.__lock, + ceilval | 2, &rt); + } + } + while (atomic_compare_and_exchange_val_acq (&mutex->__data.__lock, + ceilval | 2, ceilval) + != ceilval); + } + while ((oldval & PTHREAD_MUTEX_PRIO_CEILING_MASK) != ceilval); + + assert (mutex->__data.__owner == 0); + mutex->__data.__count = 1; + } + break; + default: /* Correct code cannot set any other type. */ return EINVAL; diff --git a/nptl/pthread_mutex_trylock.c b/nptl/pthread_mutex_trylock.c index f3a18569a1..94d519233b 100644 --- a/nptl/pthread_mutex_trylock.c +++ b/nptl/pthread_mutex_trylock.c @@ -297,6 +297,79 @@ __pthread_mutex_trylock (mutex) return 0; } + case PTHREAD_MUTEX_PP_RECURSIVE_NP: + case PTHREAD_MUTEX_PP_ERRORCHECK_NP: + case PTHREAD_MUTEX_PP_NORMAL_NP: + case PTHREAD_MUTEX_PP_ADAPTIVE_NP: + { + int kind = mutex->__data.__kind & PTHREAD_MUTEX_KIND_MASK_NP; + + oldval = mutex->__data.__lock; + + /* Check whether we already hold the mutex. */ + if (mutex->__data.__owner == id) + { + if (kind == PTHREAD_MUTEX_ERRORCHECK_NP) + return EDEADLK; + + if (kind == PTHREAD_MUTEX_RECURSIVE_NP) + { + /* Just bump the counter. */ + if (__builtin_expect (mutex->__data.__count + 1 == 0, 0)) + /* Overflow of the counter. */ + return EAGAIN; + + ++mutex->__data.__count; + + return 0; + } + } + + int oldprio = -1, ceilval; + do + { + int ceiling = (oldval & PTHREAD_MUTEX_PRIO_CEILING_MASK) + >> PTHREAD_MUTEX_PRIO_CEILING_SHIFT; + + if (__pthread_current_priority () > ceiling) + { + if (oldprio != -1) + __pthread_tpp_change_priority (oldprio, -1); + return EINVAL; + } + + int retval = __pthread_tpp_change_priority (oldprio, ceiling); + if (retval) + return retval; + + ceilval = ceiling << PTHREAD_MUTEX_PRIO_CEILING_SHIFT; + oldprio = ceiling; + + oldval + = atomic_compare_and_exchange_val_acq (&mutex->__data.__lock, + ceilval | 1, ceilval); + + if (oldval == ceilval) + break; + } + while ((oldval & PTHREAD_MUTEX_PRIO_CEILING_MASK) != ceilval); + + if (oldval != ceilval) + { + __pthread_tpp_change_priority (oldprio, -1); + break; + } + + assert (mutex->__data.__owner == 0); + /* Record the ownership. */ + mutex->__data.__owner = id; + ++mutex->__data.__nusers; + mutex->__data.__count = 1; + + return 0; + } + break; + default: /* Correct code cannot set any other type. */ return EINVAL; diff --git a/nptl/pthread_mutex_unlock.c b/nptl/pthread_mutex_unlock.c index 2b5064fbac..33919d60af 100644 --- a/nptl/pthread_mutex_unlock.c +++ b/nptl/pthread_mutex_unlock.c @@ -202,6 +202,49 @@ __pthread_mutex_unlock_usercnt (mutex, decr) THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL); break; + case PTHREAD_MUTEX_PP_RECURSIVE_NP: + /* Recursive mutex. */ + if (mutex->__data.__owner != THREAD_GETMEM (THREAD_SELF, tid)) + return EPERM; + + if (--mutex->__data.__count != 0) + /* We still hold the mutex. */ + return 0; + goto pp; + + case PTHREAD_MUTEX_PP_ERRORCHECK_NP: + /* Error checking mutex. */ + if (mutex->__data.__owner != THREAD_GETMEM (THREAD_SELF, tid) + || (mutex->__data.__lock & ~ PTHREAD_MUTEX_PRIO_CEILING_MASK) == 0) + return EPERM; + /* FALLTHROUGH */ + + case PTHREAD_MUTEX_PP_NORMAL_NP: + case PTHREAD_MUTEX_PP_ADAPTIVE_NP: + /* Always reset the owner field. */ + pp: + mutex->__data.__owner = 0; + + if (decr) + /* One less user. */ + --mutex->__data.__nusers; + + /* Unlock. */ + int newval, oldval; + do + { + oldval = mutex->__data.__lock; + newval = oldval & PTHREAD_MUTEX_PRIO_CEILING_MASK; + } + while (atomic_compare_and_exchange_bool_acq (&mutex->__data.__lock, + newval, oldval)); + + if ((oldval & ~PTHREAD_MUTEX_PRIO_CEILING_MASK) > 1) + lll_futex_wake (&mutex->__data.__lock, 1); + + int oldprio = newval >> PTHREAD_MUTEX_PRIO_CEILING_SHIFT; + return __pthread_tpp_change_priority (oldprio, -1); + default: /* Correct code cannot set any other type. */ return EINVAL; diff --git a/nptl/pthread_mutexattr_getprioceiling.c b/nptl/pthread_mutexattr_getprioceiling.c index da1beabf46..792e9a43d6 100644 --- a/nptl/pthread_mutexattr_getprioceiling.c +++ b/nptl/pthread_mutexattr_getprioceiling.c @@ -27,11 +27,22 @@ pthread_mutexattr_getprioceiling (attr, prioceiling) int *prioceiling; { const struct pthread_mutexattr *iattr; + int ceiling; iattr = (const struct pthread_mutexattr *) attr; - *prioceiling = ((iattr->mutexkind & PTHREAD_MUTEXATTR_PRIO_CEILING_MASK) - >> PTHREAD_MUTEXATTR_PRIO_CEILING_SHIFT); + ceiling = ((iattr->mutexkind & PTHREAD_MUTEXATTR_PRIO_CEILING_MASK) + >> PTHREAD_MUTEXATTR_PRIO_CEILING_SHIFT); + + if (! ceiling) + { + if (__sched_fifo_min_prio == -1) + __init_sched_fifo_prio (); + if (ceiling < __sched_fifo_min_prio) + ceiling = __sched_fifo_min_prio; + } + + *prioceiling = ceiling; return 0; } diff --git a/nptl/pthread_mutexattr_setprioceiling.c b/nptl/pthread_mutexattr_setprioceiling.c index 6c15b46833..9b7874f739 100644 --- a/nptl/pthread_mutexattr_setprioceiling.c +++ b/nptl/pthread_mutexattr_setprioceiling.c @@ -27,7 +27,15 @@ pthread_mutexattr_setprioceiling (attr, prioceiling) pthread_mutexattr_t *attr; int prioceiling; { - if (prioceiling < 0 || __builtin_expect (prioceiling > 255, 0)) + if (__sched_fifo_min_prio == -1) + __init_sched_fifo_prio (); + + if (__builtin_expect (prioceiling < __sched_fifo_min_prio, 0) + || __builtin_expect (prioceiling > __sched_fifo_max_prio, 0) + || __builtin_expect ((prioceiling + & (PTHREAD_MUTEXATTR_PRIO_CEILING_MASK + >> PTHREAD_MUTEXATTR_PRIO_CEILING_SHIFT)) + != prioceiling, 0)) return EINVAL; struct pthread_mutexattr *iattr = (struct pthread_mutexattr *) attr; diff --git a/nptl/pthread_setschedparam.c b/nptl/pthread_setschedparam.c index 5889cc98a9..30ac6b3e89 100644 --- a/nptl/pthread_setschedparam.c +++ b/nptl/pthread_setschedparam.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc. +/* Copyright (C) 2002, 2003, 2004, 2006 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper <drepper@redhat.com>, 2002. @@ -45,6 +45,19 @@ __pthread_setschedparam (threadid, policy, param) lll_lock (pd->lock); + struct sched_param p; + const struct sched_param *orig_param = param; + + /* If the thread should have higher priority because of some + PTHREAD_PRIO_PROTECT mutexes it holds, adjust the priority. */ + if (__builtin_expect (pd->tpp != NULL, 0) + && pd->tpp->priomax > param->sched_priority) + { + p = *param; + p.sched_priority = pd->tpp->priomax; + param = &p; + } + /* Try to set the scheduler information. */ if (__builtin_expect (__sched_setscheduler (pd->tid, policy, param) == -1, 0)) @@ -54,7 +67,7 @@ __pthread_setschedparam (threadid, policy, param) /* We succeeded changing the kernel information. Reflect this change in the thread descriptor. */ pd->schedpolicy = policy; - memcpy (&pd->schedparam, param, sizeof (struct sched_param)); + memcpy (&pd->schedparam, orig_param, sizeof (struct sched_param)); pd->flags |= ATTR_FLAG_SCHED_SET | ATTR_FLAG_POLICY_SET; } diff --git a/nptl/pthread_setschedprio.c b/nptl/pthread_setschedprio.c index 063f5232f5..4a71f6c7b3 100644 --- a/nptl/pthread_setschedprio.c +++ b/nptl/pthread_setschedprio.c @@ -47,6 +47,11 @@ pthread_setschedprio (threadid, prio) lll_lock (pd->lock); + /* If the thread should have higher priority because of some + PTHREAD_PRIO_PROTECT mutexes it holds, adjust the priority. */ + if (__builtin_expect (pd->tpp != NULL, 0) && pd->tpp->priomax > prio) + param.sched_priority = pd->tpp->priomax; + /* Try to set the scheduler information. */ if (__builtin_expect (sched_setparam (pd->tid, ¶m) == -1, 0)) result = errno; @@ -54,6 +59,7 @@ pthread_setschedprio (threadid, prio) { /* We succeeded changing the kernel information. Reflect this change in the thread descriptor. */ + param.sched_priority = prio; memcpy (&pd->schedparam, ¶m, sizeof (struct sched_param)); pd->flags |= ATTR_FLAG_SCHED_SET; } diff --git a/nptl/sysdeps/unix/sysv/linux/bits/posix_opt.h b/nptl/sysdeps/unix/sysv/linux/bits/posix_opt.h index d8eced1f22..40160c54de 100644 --- a/nptl/sysdeps/unix/sysv/linux/bits/posix_opt.h +++ b/nptl/sysdeps/unix/sysv/linux/bits/posix_opt.h @@ -82,6 +82,10 @@ /* We support priority inheritence. */ #define _POSIX_THREAD_PRIO_INHERIT 200112L +/* We support priority protection, though only for non-robust + mutexes. */ +#define _POSIX_THREAD_PRIO_PROTECT 200112L + /* We support POSIX.1b semaphores. */ #define _POSIX_SEMAPHORES 200112L @@ -174,7 +178,4 @@ /* Typed memory objects are not available. */ #define _POSIX_TYPED_MEMORY_OBJECTS -1 -/* No support for priority protection so far. */ -#define _POSIX_THREAD_PRIO_PROTECT -1 - #endif /* posix_opt.h */ diff --git a/nptl/tpp.c b/nptl/tpp.c new file mode 100644 index 0000000000..367dd8162a --- /dev/null +++ b/nptl/tpp.c @@ -0,0 +1,172 @@ +/* Thread Priority Protect helpers. + Copyright (C) 2006 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Jakub Jelinek <jakub@redhat.com>, 2006. + + 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, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include <assert.h> +#include <atomic.h> +#include <errno.h> +#include <pthreadP.h> +#include <sched.h> +#include <stdlib.h> + + +int __sched_fifo_min_prio = -1; +int __sched_fifo_max_prio = -1; + +void +__init_sched_fifo_prio (void) +{ + __sched_fifo_max_prio = sched_get_priority_max (SCHED_FIFO); + atomic_write_barrier (); + __sched_fifo_min_prio = sched_get_priority_min (SCHED_FIFO); +} + +int +__pthread_tpp_change_priority (int previous_prio, int new_prio) +{ + struct pthread *self = THREAD_SELF; + struct priority_protection_data *tpp = THREAD_GETMEM (self, tpp); + + if (tpp == NULL) + { + if (__sched_fifo_min_prio == -1) + __init_sched_fifo_prio (); + + size_t size = sizeof *tpp; + size += (__sched_fifo_max_prio - __sched_fifo_min_prio + 1) + * sizeof (tpp->priomap[0]); + tpp = calloc (size, 1); + if (tpp == NULL) + return ENOMEM; + tpp->priomax = __sched_fifo_min_prio - 1; + THREAD_SETMEM (self, tpp, tpp); + } + + assert (new_prio == -1 + || (new_prio >= __sched_fifo_min_prio + && new_prio <= __sched_fifo_max_prio)); + assert (previous_prio == -1 + || (previous_prio >= __sched_fifo_min_prio + && previous_prio <= __sched_fifo_max_prio)); + + int priomax = tpp->priomax; + int newpriomax = priomax; + if (new_prio != -1) + { + if (tpp->priomap[new_prio - __sched_fifo_min_prio] + 1 == 0) + return EAGAIN; + ++tpp->priomap[new_prio - __sched_fifo_min_prio]; + if (new_prio > priomax) + newpriomax = new_prio; + } + + if (previous_prio != -1) + { + if (--tpp->priomap[previous_prio - __sched_fifo_min_prio] == 0 + && priomax == previous_prio + && previous_prio > new_prio) + { + int i; + for (i = previous_prio - 1; i >= __sched_fifo_min_prio; --i) + if (tpp->priomap[i - __sched_fifo_min_prio]) + break; + newpriomax = i; + } + } + + if (priomax == newpriomax) + return 0; + + lll_lock (self->lock); + + tpp->priomax = newpriomax; + + int result = 0; + + if ((self->flags & ATTR_FLAG_SCHED_SET) == 0) + { + if (__sched_getparam (self->tid, &self->schedparam) != 0) + result = errno; + else + self->flags |= ATTR_FLAG_SCHED_SET; + } + + if ((self->flags & ATTR_FLAG_POLICY_SET) == 0) + { + self->schedpolicy = __sched_getscheduler (self->tid); + if (self->schedpolicy == -1) + result = errno; + else + self->flags |= ATTR_FLAG_POLICY_SET; + } + + if (result == 0) + { + struct sched_param sp = self->schedparam; + if (sp.sched_priority < newpriomax || sp.sched_priority < priomax) + { + if (sp.sched_priority < newpriomax) + sp.sched_priority = newpriomax; + + if (__sched_setscheduler (self->tid, self->schedpolicy, &sp) < 0) + result = errno; + } + } + + lll_unlock (self->lock); + + return result; +} + +int +__pthread_current_priority (void) +{ + struct pthread *self = THREAD_SELF; + if ((self->flags & (ATTR_FLAG_POLICY_SET | ATTR_FLAG_SCHED_SET)) + == (ATTR_FLAG_POLICY_SET | ATTR_FLAG_SCHED_SET)) + return self->schedparam.sched_priority; + + int result = 0; + + lll_lock (self->lock); + + if ((self->flags & ATTR_FLAG_SCHED_SET) == 0) + { + if (__sched_getparam (self->tid, &self->schedparam) != 0) + result = -1; + else + self->flags |= ATTR_FLAG_SCHED_SET; + } + + if ((self->flags & ATTR_FLAG_POLICY_SET) == 0) + { + self->schedpolicy = __sched_getscheduler (self->tid); + if (self->schedpolicy == -1) + result = -1; + else + self->flags |= ATTR_FLAG_POLICY_SET; + } + + if (result != -1) + result = self->schedparam.sched_priority; + + lll_unlock (self->lock); + + return result; +} diff --git a/nptl/tst-join5.c b/nptl/tst-join5.c index 589fac6b5f..db005f5b71 100644 --- a/nptl/tst-join5.c +++ b/nptl/tst-join5.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2003 Free Software Foundation, Inc. +/* Copyright (C) 2003, 2006 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper <drepper@redhat.com>, 2003. @@ -21,120 +21,187 @@ #include <pthread.h> #include <stdio.h> #include <stdlib.h> +#include <time.h> +#include <unistd.h> +#include <sys/syscall.h> + + +#define wait_code() \ + do { \ + struct timespec ts = { .tv_sec = 0, .tv_nsec = 200000000 }; \ + while (syscall (__NR_nanosleep, &ts, &ts) < 0) \ + /* nothing */; \ + } while (0) + + +#ifdef WAIT_IN_CHILD +static pthread_barrier_t b; +#endif static void * tf1 (void *arg) { - pthread_join ((pthread_t) arg, NULL); +#ifdef WAIT_IN_CHILD + int e = pthread_barrier_wait (&b); + if (e != 0 && e != PTHREAD_BARRIER_SERIAL_THREAD) + { + printf ("%s: barrier_wait failed\n", __func__); + exit (1); + } - puts ("1st join returned"); + wait_code (); +#endif - return (void *) 1l; + pthread_join ((pthread_t) arg, NULL); + + exit (42); } static void * tf2 (void *arg) { - pthread_join ((pthread_t) arg, NULL); +#ifdef WAIT_IN_CHILD + int e = pthread_barrier_wait (&b); + if (e != 0 && e != PTHREAD_BARRIER_SERIAL_THREAD) + { + printf ("%s: barrier_wait failed\n", __func__); + exit (1); + } - puts ("2nd join returned"); + wait_code (); +#endif + pthread_join ((pthread_t) arg, NULL); - return (void *) 1l; + exit (43); } static int do_test (void) { +#ifdef WAIT_IN_CHILD + if (pthread_barrier_init (&b, NULL, 2) != 0) + { + puts ("barrier_init failed"); + return 1; + } +#endif + pthread_t th; int err = pthread_join (pthread_self (), NULL); if (err == 0) { puts ("1st circular join succeeded"); - exit (1); + return 1; } if (err != EDEADLK) { printf ("1st circular join %d, not EDEADLK\n", err); - exit (1); + return 1; } if (pthread_create (&th, NULL, tf1, (void *) pthread_self ()) != 0) { puts ("1st create failed"); - exit (1); + return 1; } +#ifndef WAIT_IN_CHILD + wait_code (); +#endif + if (pthread_cancel (th) != 0) { puts ("cannot cancel 1st thread"); - exit (1); + return 1; + } + +#ifdef WAIT_IN_CHILD + int e = pthread_barrier_wait (&b); + if (e != 0 && e != PTHREAD_BARRIER_SERIAL_THREAD) + { + printf ("%s: barrier_wait failed\n", __func__); + return 1; } +#endif void *r; err = pthread_join (th, &r); if (err != 0) { printf ("cannot join 1st thread: %d\n", err); - exit (1); + return 1; } if (r != PTHREAD_CANCELED) { puts ("1st thread not canceled"); - exit (1); + return 1; } err = pthread_join (pthread_self (), NULL); if (err == 0) { puts ("2nd circular join succeeded"); - exit (1); + return 1; } if (err != EDEADLK) { printf ("2nd circular join %d, not EDEADLK\n", err); - exit (1); + return 1; } if (pthread_create (&th, NULL, tf2, (void *) pthread_self ()) != 0) { puts ("2nd create failed"); - exit (1); + return 1; } +#ifndef WAIT_IN_CHILD + wait_code (); +#endif + if (pthread_cancel (th) != 0) { puts ("cannot cancel 2nd thread"); - exit (1); + return 1; } +#ifdef WAIT_IN_CHILD + e = pthread_barrier_wait (&b); + if (e != 0 && e != PTHREAD_BARRIER_SERIAL_THREAD) + { + printf ("%s: barrier_wait failed\n", __func__); + return 1; + } +#endif + if (pthread_join (th, &r) != 0) { puts ("cannot join 2nd thread"); - exit (1); + return 1; } if (r != PTHREAD_CANCELED) { puts ("2nd thread not canceled"); - exit (1); + return 1; } err = pthread_join (pthread_self (), NULL); if (err == 0) { - puts ("2nd circular join succeeded"); - exit (1); + puts ("3rd circular join succeeded"); + return 1; } if (err != EDEADLK) { - printf ("2nd circular join %d, not EDEADLK\n", err); - exit (1); + printf ("3rd circular join %d, not EDEADLK\n", err); + return 1; } - exit (0); + return 0; } #define TEST_FUNCTION do_test () diff --git a/nptl/tst-join6.c b/nptl/tst-join6.c new file mode 100644 index 0000000000..0c9e7c056b --- /dev/null +++ b/nptl/tst-join6.c @@ -0,0 +1,2 @@ +#define WAIT_IN_CHILD 1 +#include "tst-join5.c" diff --git a/nptl/tst-mutex1.c b/nptl/tst-mutex1.c index c3ef5b2b51..9453f04918 100644 --- a/nptl/tst-mutex1.c +++ b/nptl/tst-mutex1.c @@ -71,5 +71,7 @@ do_test (void) return 0; } -#define TEST_FUNCTION do_test () +#ifndef TEST_FUNCTION +# define TEST_FUNCTION do_test () +#endif #include "../test-skeleton.c" diff --git a/nptl/tst-mutex6.c b/nptl/tst-mutex6.c index e5698c3eb0..de64bdb435 100644 --- a/nptl/tst-mutex6.c +++ b/nptl/tst-mutex6.c @@ -69,5 +69,7 @@ do_test (void) } #define EXPECTED_SIGNAL SIGALRM -#define TEST_FUNCTION do_test () +#ifndef TEST_FUNCTION +# define TEST_FUNCTION do_test () +#endif #include "../test-skeleton.c" diff --git a/nptl/tst-mutexpp1.c b/nptl/tst-mutexpp1.c new file mode 100644 index 0000000000..9b7d7fe263 --- /dev/null +++ b/nptl/tst-mutexpp1.c @@ -0,0 +1,45 @@ +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> + +#include "tst-tpp.h" + +static pthread_mutexattr_t a; + +static void +prepare (void) +{ + init_tpp_test (); + + if (pthread_mutexattr_init (&a) != 0) + { + puts ("mutexattr_init failed"); + exit (1); + } + + if (pthread_mutexattr_setprotocol (&a, PTHREAD_PRIO_PROTECT) != 0) + { + puts ("mutexattr_setprotocol failed"); + exit (1); + } + + if (pthread_mutexattr_setprioceiling (&a, 6) != 0) + { + puts ("mutexattr_setprioceiling failed"); + exit (1); + } +} +#define PREPARE(argc, argv) prepare () + +static int do_test (void); + +static int +do_test_wrapper (void) +{ + init_tpp_test (); + return do_test (); +} +#define TEST_FUNCTION do_test_wrapper () + +#define ATTR &a +#include "tst-mutex1.c" diff --git a/nptl/tst-mutexpp10.c b/nptl/tst-mutexpp10.c new file mode 100644 index 0000000000..78281a4d3b --- /dev/null +++ b/nptl/tst-mutexpp10.c @@ -0,0 +1,334 @@ +/* Copyright (C) 2006 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Jakub Jelinek <jakub@redhat.com>, 2006. + + 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, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include <errno.h> +#include <limits.h> +#include <pthread.h> +#include <sched.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "tst-tpp.h" + +static int +do_test (void) +{ + int ret = 0; + + init_tpp_test (); + + pthread_mutexattr_t ma; + if (pthread_mutexattr_init (&ma)) + { + puts ("mutexattr_init failed"); + return 1; + } + if (pthread_mutexattr_setprotocol (&ma, PTHREAD_PRIO_PROTECT)) + { + puts ("mutexattr_setprotocol failed"); + return 1; + } + + int prioceiling; + if (pthread_mutexattr_getprioceiling (&ma, &prioceiling)) + { + puts ("mutexattr_getprioceiling failed"); + return 1; + } + + if (prioceiling < fifo_min || prioceiling > fifo_max) + { + printf ("prioceiling %d not in %d..%d range\n", + prioceiling, fifo_min, fifo_max); + return 1; + } + + if (fifo_max < INT_MAX + && pthread_mutexattr_setprioceiling (&ma, fifo_max + 1) != EINVAL) + { + printf ("mutexattr_setprioceiling %d did not fail with EINVAL\n", + fifo_max + 1); + return 1; + } + + if (fifo_min > 0 + && pthread_mutexattr_setprioceiling (&ma, fifo_min - 1) != EINVAL) + { + printf ("mutexattr_setprioceiling %d did not fail with EINVAL\n", + fifo_min - 1); + return 1; + } + + if (pthread_mutexattr_setprioceiling (&ma, fifo_min)) + { + puts ("mutexattr_setprioceiling failed"); + return 1; + } + + if (pthread_mutexattr_setprioceiling (&ma, fifo_max)) + { + puts ("mutexattr_setprioceiling failed"); + return 1; + } + + if (pthread_mutexattr_setprioceiling (&ma, 6)) + { + puts ("mutexattr_setprioceiling failed"); + return 1; + } + + if (pthread_mutexattr_getprioceiling (&ma, &prioceiling)) + { + puts ("mutexattr_getprioceiling failed"); + return 1; + } + + if (prioceiling != 6) + { + printf ("mutexattr_getprioceiling returned %d != 6\n", + prioceiling); + return 1; + } + + pthread_mutex_t m1, m2, m3; + int e = pthread_mutex_init (&m1, &ma); + if (e == ENOTSUP) + { + puts ("cannot support selected type of mutexes"); + return 0; + } + else if (e != 0) + { + puts ("mutex_init failed"); + return 1; + } + + if (pthread_mutexattr_setprioceiling (&ma, 8)) + { + puts ("mutexattr_setprioceiling failed"); + return 1; + } + + if (pthread_mutex_init (&m2, &ma)) + { + puts ("mutex_init failed"); + return 1; + } + + if (pthread_mutexattr_setprioceiling (&ma, 5)) + { + puts ("mutexattr_setprioceiling failed"); + return 1; + } + + if (pthread_mutex_init (&m3, &ma)) + { + puts ("mutex_init failed"); + return 1; + } + + CHECK_TPP_PRIORITY (4, 4); + + if (pthread_mutex_lock (&m1) != 0) + { + puts ("mutex_lock failed"); + return 1; + } + + CHECK_TPP_PRIORITY (4, 6); + + if (pthread_mutex_trylock (&m2) != 0) + { + puts ("mutex_lock failed"); + return 1; + } + + CHECK_TPP_PRIORITY (4, 8); + + if (pthread_mutex_lock (&m3) != 0) + { + puts ("mutex_lock failed"); + return 1; + } + + CHECK_TPP_PRIORITY (4, 8); + + if (pthread_mutex_unlock (&m2) != 0) + { + puts ("mutex_unlock failed"); + return 1; + } + + CHECK_TPP_PRIORITY (4, 6); + + if (pthread_mutex_unlock (&m1) != 0) + { + puts ("mutex_unlock failed"); + return 1; + } + + CHECK_TPP_PRIORITY (4, 5); + + if (pthread_mutex_lock (&m2) != 0) + { + puts ("mutex_lock failed"); + return 1; + } + + CHECK_TPP_PRIORITY (4, 8); + + if (pthread_mutex_unlock (&m2) != 0) + { + puts ("mutex_unlock failed"); + return 1; + } + + CHECK_TPP_PRIORITY (4, 5); + + if (pthread_mutex_getprioceiling (&m1, &prioceiling)) + { + puts ("mutex_getprioceiling m1 failed"); + return 1; + } + else if (prioceiling != 6) + { + printf ("unexpected m1 prioceiling %d != 6\n", prioceiling); + return 1; + } + + if (pthread_mutex_getprioceiling (&m2, &prioceiling)) + { + puts ("mutex_getprioceiling m2 failed"); + return 1; + } + else if (prioceiling != 8) + { + printf ("unexpected m2 prioceiling %d != 8\n", prioceiling); + return 1; + } + + if (pthread_mutex_getprioceiling (&m3, &prioceiling)) + { + puts ("mutex_getprioceiling m3 failed"); + return 1; + } + else if (prioceiling != 5) + { + printf ("unexpected m3 prioceiling %d != 5\n", prioceiling); + return 1; + } + + if (pthread_mutex_setprioceiling (&m1, 7, &prioceiling)) + { + printf ("mutex_setprioceiling failed"); + return 1; + } + else if (prioceiling != 6) + { + printf ("unexpected m1 old prioceiling %d != 6\n", prioceiling); + return 1; + } + + if (pthread_mutex_getprioceiling (&m1, &prioceiling)) + { + puts ("mutex_getprioceiling m1 failed"); + return 1; + } + else if (prioceiling != 7) + { + printf ("unexpected m1 prioceiling %d != 7\n", prioceiling); + return 1; + } + + CHECK_TPP_PRIORITY (4, 5); + + if (pthread_mutex_unlock (&m3) != 0) + { + puts ("mutex_unlock failed"); + return 1; + } + + CHECK_TPP_PRIORITY (4, 4); + + if (pthread_mutex_trylock (&m1) != 0) + { + puts ("mutex_lock failed"); + return 1; + } + + CHECK_TPP_PRIORITY (4, 7); + + struct sched_param sp; + memset (&sp, 0, sizeof (sp)); + sp.sched_priority = 8; + if (pthread_setschedparam (pthread_self (), SCHED_FIFO, &sp)) + { + puts ("cannot set scheduling params"); + return 1; + } + + CHECK_TPP_PRIORITY (8, 8); + + if (pthread_mutex_unlock (&m1) != 0) + { + puts ("mutex_unlock failed"); + return 1; + } + + CHECK_TPP_PRIORITY (8, 8); + + if (pthread_mutex_lock (&m3) != EINVAL) + { + puts ("pthread_mutex_lock didn't fail with EINVAL"); + return 1; + } + + CHECK_TPP_PRIORITY (8, 8); + + if (pthread_mutex_destroy (&m1) != 0) + { + puts ("mutex_destroy failed"); + return 1; + } + + if (pthread_mutex_destroy (&m2) != 0) + { + puts ("mutex_destroy failed"); + return 1; + } + + if (pthread_mutex_destroy (&m3) != 0) + { + puts ("mutex_destroy failed"); + return 1; + } + + if (pthread_mutexattr_destroy (&ma) != 0) + { + puts ("mutexattr_destroy failed"); + return 1; + } + + return ret; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/nptl/tst-mutexpp6.c b/nptl/tst-mutexpp6.c new file mode 100644 index 0000000000..2ddf6b45c6 --- /dev/null +++ b/nptl/tst-mutexpp6.c @@ -0,0 +1,45 @@ +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> + +#include "tst-tpp.h" + +static pthread_mutexattr_t a; + +static void +prepare (void) +{ + init_tpp_test (); + + if (pthread_mutexattr_init (&a) != 0) + { + puts ("mutexattr_init failed"); + exit (1); + } + + if (pthread_mutexattr_setprotocol (&a, PTHREAD_PRIO_PROTECT) != 0) + { + puts ("mutexattr_setprotocol failed"); + exit (1); + } + + if (pthread_mutexattr_setprioceiling (&a, 6) != 0) + { + puts ("mutexattr_setprioceiling failed"); + exit (1); + } +} +#define PREPARE(argc, argv) prepare () + +static int do_test (void); + +static int +do_test_wrapper (void) +{ + init_tpp_test (); + return do_test (); +} +#define TEST_FUNCTION do_test_wrapper () + +#define ATTR &a +#include "tst-mutex6.c" diff --git a/nptl/tst-tpp.h b/nptl/tst-tpp.h new file mode 100644 index 0000000000..c5844b26dd --- /dev/null +++ b/nptl/tst-tpp.h @@ -0,0 +1,94 @@ +/* Copyright (C) 2006 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Jakub Jelinek <jakub@redhat.com>, 2006. + + 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, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include <errno.h> +#include <pthread.h> +#include <sched.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/syscall.h> + +/* This test is Linux specific. */ +#define CHECK_TPP_PRIORITY(normal, boosted) \ + do \ + { \ + pid_t tid = syscall (__NR_gettid); \ + \ + struct sched_param cep_sp; \ + int cep_policy; \ + if (pthread_getschedparam (pthread_self (), &cep_policy, \ + &cep_sp) != 0) \ + { \ + puts ("getschedparam failed"); \ + ret = 1; \ + } \ + else if (cep_sp.sched_priority != (normal)) \ + { \ + printf ("unexpected priority %d != %d\n", \ + cep_sp.sched_priority, (normal)); \ + } \ + if (syscall (__NR_sched_getparam, tid, &cep_sp) == 0 \ + && cep_sp.sched_priority != (boosted)) \ + { \ + printf ("unexpected boosted priority %d != %d\n", \ + cep_sp.sched_priority, (boosted)); \ + ret = 1; \ + } \ + } \ + while (0) + +int fifo_min, fifo_max; + +void +init_tpp_test (void) +{ + fifo_min = sched_get_priority_min (SCHED_FIFO); + if (fifo_min < 0) + { + printf ("couldn't get min priority for SCHED_FIFO: %m\n"); + exit (1); + } + + fifo_max = sched_get_priority_max (SCHED_FIFO); + if (fifo_max < 0) + { + printf ("couldn't get max priority for SCHED_FIFO: %m\n"); + exit (1); + } + + if (fifo_min > 4 || fifo_max < 10) + { + printf ("%d..%d SCHED_FIFO priority interval not suitable for this test\n", + fifo_min, fifo_max); + exit (0); + } + + struct sched_param sp; + memset (&sp, 0, sizeof (sp)); + sp.sched_priority = 4; + int e = pthread_setschedparam (pthread_self (), SCHED_FIFO, &sp); + if (e != 0) + { + errno = e; + printf ("cannot set scheduling params: %m\n"); + exit (0); + } +} |