From 73f7c32c47ab2397935d9fc2aeaa594794b38c7e Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Thu, 2 Sep 2004 18:59:24 +0000 Subject: [BZ #357] Update. 2004-09-02 Steven Munroe [BZ #357] * stdlib/tst-setcontext.c (test_stack): Added test for stack clobber. (main): Call test_stack. * sysdeps/unix/sysv/linux/powerpc/powerpc32/getcontext.S (__getcontext): Push stack frame then save parms in local frame. Improve instruction scheduling. * sysdeps/unix/sysv/linux/powerpc/powerpc32/swapcontext.S (__swapcontext): Likewise. --- nptl/ChangeLog | 59 +++++++ nptl/Makefile | 1 + nptl/pthread_cond_destroy.c | 32 +++- nptl/pthread_cond_init.c | 5 +- nptl/pthread_condattr_getclock.c | 6 +- nptl/pthread_condattr_setclock.c | 13 +- nptl/sysdeps/pthread/pthread_cond_timedwait.c | 14 +- nptl/sysdeps/pthread/pthread_cond_wait.c | 28 +++- .../unix/sysv/linux/alpha/bits/pthreadtypes.h | 2 +- .../unix/sysv/linux/i386/bits/pthreadtypes.h | 2 +- .../sysv/linux/i386/i486/pthread_cond_timedwait.S | 54 ++++++- .../unix/sysv/linux/i386/i486/pthread_cond_wait.S | 49 +++++- .../unix/sysv/linux/ia64/bits/pthreadtypes.h | 2 +- nptl/sysdeps/unix/sysv/linux/internaltypes.h | 6 + nptl/sysdeps/unix/sysv/linux/lowlevelcond.sym | 5 +- .../unix/sysv/linux/powerpc/bits/pthreadtypes.h | 2 +- .../unix/sysv/linux/s390/bits/pthreadtypes.h | 2 +- .../sysdeps/unix/sysv/linux/sh/bits/pthreadtypes.h | 2 +- .../unix/sysv/linux/sparc/bits/pthreadtypes.h | 2 +- .../unix/sysv/linux/x86_64/bits/pthreadtypes.h | 2 +- .../sysv/linux/x86_64/pthread_cond_timedwait.S | 24 ++- .../unix/sysv/linux/x86_64/pthread_cond_wait.S | 51 ++++++- nptl/tst-cond20.c | 170 +++++++++++++++++++++ nptl/tst-cond21.c | 3 + 24 files changed, 496 insertions(+), 40 deletions(-) create mode 100644 nptl/tst-cond20.c create mode 100644 nptl/tst-cond21.c (limited to 'nptl') diff --git a/nptl/ChangeLog b/nptl/ChangeLog index 2aa77d9133..6306a7b0c6 100644 --- a/nptl/ChangeLog +++ b/nptl/ChangeLog @@ -1,3 +1,62 @@ +2004-09-02 Ulrich Drepper + + * sysdeps/unix/sysv/linux/i386/bits/pthreadtypes.h (pthread_cond_t): + Rename __data.__clock to __data.__nwaiters, make it unsigned int. + * sysdeps/unix/sysv/linux/x86_64/bits/pthreadtypes.h (pthread_cond_t): + Likewise. + * sysdeps/unix/sysv/linux/i386/i486/pthread_cond_wait.S: + Decrement __nwaiters. If pthread_cond_destroy has been called and + this is the last waiter, signal pthread_cond_destroy caller and + avoid using the pthread_cond_t structure after unlock. + * sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S: Likewise. + * sysdeps/unix/sysv/linux/i386/i486/pthread_cond_timedwait.S: Likewise. + Read clock type from the least significant bits of __nwaiters instead + of __clock. + * sysdeps/unix/sysv/linux/x86_64/pthread_cond_timedwait.S: Likewise. + * sysdeps/unix/sysv/linux/internaltypes.h: Define COND_CLOCK_BITS. + +2004-08-31 Jakub Jelinek + + [BZ #342] + * Makefile (tests): Add tst-cond20 and tst-cond21. + * tst-cond20.c: New test. + * tst-cond21.c: New test. + * sysdeps/unix/sysv/linux/alpha/bits/pthreadtypes.h + (pthread_cond_t): Rename __data.__clock to __data.__nwaiters, make + it unsigned int. + * sysdeps/unix/sysv/linux/s390/bits/pthreadtypes.h (pthread_cond_t): + Likewise. + * sysdeps/unix/sysv/linux/powerpc/bits/pthreadtypes.h + (pthread_cond_t): Likewise. + * sysdeps/unix/sysv/linux/sparc/bits/pthreadtypes.h (pthread_cond_t): + Likewise. + * sysdeps/unix/sysv/linux/sh/bits/pthreadtypes.h (pthread_cond_t): + Likewise. + * sysdeps/unix/sysv/linux/ia64/bits/pthreadtypes.h (pthread_cond_t): + Likewise. + * sysdeps/unix/sysv/linux/lowlevelcond.sym (cond_clock): Remove. + (cond_nwaiters): New. + (clock_bits): New. + * pthread_cond_destroy.c (__pthread_cond_destroy): Return EBUSY + if there are waiters not signalled yet. + Wait until all already signalled waiters wake up. + * sysdeps/pthread/pthread_cond_wait.c (__condvar_cleanup): Decrement + __nwaiters. If pthread_cond_destroy has been called and this is the + last waiter, signal pthread_cond_destroy caller and avoid using + the pthread_cond_t structure after unlock. + (__pthread_cond_wait): Increment __nwaiters in the beginning, + decrement it when leaving. If pthread_cond_destroy has been called + and this is the last waiter, signal pthread_cond_destroy caller. + * sysdeps/pthread/pthread_cond_timedwait.c (__pthread_cond_timedwait): + Likewise. Read clock type from the least significant bits of + __nwaiters instead of __clock. + * pthread_condattr_setclock.c (pthread_condattr_setclock): Check + whether clock ID can be encoded in COND_CLOCK_BITS bits. + * pthread_condattr_getclock.c (pthread_condattr_getclock): Decode + clock type just from the last COND_CLOCK_BITS bits of value. + * pthread_cond_init.c (__pthread_cond_init): Initialize __nwaiters + instead of __clock, just from second bit of condattr's value. + 2004-08-30 Jakub Jelinek * sysdeps/unix/sysv/linux/x86_64/bits/pthreadtypes.h: Include diff --git a/nptl/Makefile b/nptl/Makefile index e797f4d486..e75752f801 100644 --- a/nptl/Makefile +++ b/nptl/Makefile @@ -193,6 +193,7 @@ tests = tst-attr1 tst-attr2 tst-attr3 \ tst-cond1 tst-cond2 tst-cond3 tst-cond4 tst-cond5 tst-cond6 tst-cond7 \ tst-cond8 tst-cond9 tst-cond10 tst-cond11 tst-cond12 tst-cond13 \ tst-cond14 tst-cond15 tst-cond16 tst-cond17 tst-cond18 tst-cond19 \ + tst-cond20 tst-cond21 \ tst-rwlock1 tst-rwlock2 tst-rwlock3 tst-rwlock4 tst-rwlock5 \ tst-rwlock6 tst-rwlock7 tst-rwlock8 tst-rwlock9 tst-rwlock10 \ tst-rwlock11 tst-rwlock12 tst-rwlock13 tst-rwlock14 \ diff --git a/nptl/pthread_cond_destroy.c b/nptl/pthread_cond_destroy.c index 5ade3e63db..0208d18ce4 100644 --- a/nptl/pthread_cond_destroy.c +++ b/nptl/pthread_cond_destroy.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2002, 2003 Free Software Foundation, Inc. +/* Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper , 2002. @@ -17,6 +17,7 @@ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ +#include #include #include "pthreadP.h" @@ -25,6 +26,35 @@ int __pthread_cond_destroy (cond) pthread_cond_t *cond; { + /* Make sure we are alone. */ + lll_mutex_lock (cond->__data.__lock); + + if (cond->__data.__total_seq > cond->__data.__wakeup_seq) + { + /* If there are still some waiters which have not been + woken up, this is an application bug. */ + lll_mutex_unlock (cond->__data.__lock); + return EBUSY; + } + + /* Tell pthread_cond_*wait that this condvar is being destroyed. */ + cond->__data.__total_seq = -1ULL; + + /* If there are waiters which have been already signalled or + broadcasted, but still are using the pthread_cond_t structure, + pthread_cond_destroy needs to wait for them. */ + unsigned int nwaiters = cond->__data.__nwaiters; + while (nwaiters >= (1 << COND_CLOCK_BITS)) + { + lll_mutex_unlock (cond->__data.__lock); + + lll_futex_wait (&cond->__data.__nwaiters, nwaiters); + + lll_mutex_lock (cond->__data.__lock); + + nwaiters = cond->__data.__nwaiters; + } + return 0; } versioned_symbol (libpthread, __pthread_cond_destroy, diff --git a/nptl/pthread_cond_init.c b/nptl/pthread_cond_init.c index f5fbd64e96..03ac59dbd2 100644 --- a/nptl/pthread_cond_init.c +++ b/nptl/pthread_cond_init.c @@ -32,8 +32,9 @@ __pthread_cond_init (cond, cond_attr) cond->__data.__lock = LLL_MUTEX_LOCK_INITIALIZER; cond->__data.__futex = 0; - cond->__data.__clock = (icond_attr == NULL - ? CLOCK_REALTIME : (icond_attr->value & 0xfe) >> 1); + cond->__data.__nwaiters = (icond_attr != NULL + && ((icond_attr->value & (COND_CLOCK_BITS << 1)) + >> 1)); cond->__data.__total_seq = 0; cond->__data.__wakeup_seq = 0; cond->__data.__woken_seq = 0; diff --git a/nptl/pthread_condattr_getclock.c b/nptl/pthread_condattr_getclock.c index f8be655bf9..84de918a54 100644 --- a/nptl/pthread_condattr_getclock.c +++ b/nptl/pthread_condattr_getclock.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2003 Free Software Foundation, Inc. +/* Copyright (C) 2003, 2004 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper , 2003. @@ -25,7 +25,7 @@ pthread_condattr_getclock (attr, clock_id) const pthread_condattr_t *attr; clockid_t *clock_id; { - *clock_id = ((((const struct pthread_condattr *) attr)->value) & 0xfe) >> 1; - + *clock_id = (((((const struct pthread_condattr *) attr)->value) >> 1) + & ((1 << COND_CLOCK_BITS) - 1)); return 0; } diff --git a/nptl/pthread_condattr_setclock.c b/nptl/pthread_condattr_setclock.c index 0f1829c502..04e246b74d 100644 --- a/nptl/pthread_condattr_setclock.c +++ b/nptl/pthread_condattr_setclock.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2003 Free Software Foundation, Inc. +/* Copyright (C) 2003, 2004 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper , 2003. @@ -17,6 +17,7 @@ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ +#include #include #include #include @@ -45,8 +46,7 @@ pthread_condattr_setclock (attr, clock_id) INTERNAL_SYSCALL_DECL (err); int val; - val = INTERNAL_SYSCALL (clock_getres, err, 2, CLOCK_MONOTONIC, - &ts); + val = INTERNAL_SYSCALL (clock_getres, err, 2, CLOCK_MONOTONIC, &ts); avail = INTERNAL_SYSCALL_ERROR_P (val, err) ? -1 : 1; } @@ -57,11 +57,16 @@ pthread_condattr_setclock (attr, clock_id) #endif } else if (clock_id != CLOCK_REALTIME) + /* If more clocks are allowed some day the storing of the clock ID + in the pthread_cond_t structure needs to be adjusted. */ return EINVAL; + /* Make sure the value fits in the bits we reserved. */ + assert (clock_id < (1 << COND_CLOCK_BITS)); + int *valuep = &((struct pthread_condattr *) attr)->value; - *valuep = (*valuep & ~0xfe) | (clock_id << 1); + *valuep = (*valuep & ~(1 << (COND_CLOCK_BITS + 1)) & ~1) | (clock_id << 1); return 0; } diff --git a/nptl/sysdeps/pthread/pthread_cond_timedwait.c b/nptl/sysdeps/pthread/pthread_cond_timedwait.c index 7de2b2936f..c6606c9bf9 100644 --- a/nptl/sysdeps/pthread/pthread_cond_timedwait.c +++ b/nptl/sysdeps/pthread/pthread_cond_timedwait.c @@ -67,6 +67,7 @@ __pthread_cond_timedwait (cond, mutex, abstime) /* We have one new user of the condvar. */ ++cond->__data.__total_seq; ++cond->__data.__futex; + cond->__data.__nwaiters += 1 << COND_CLOCK_BITS; /* Remember the mutex we are using here. If there is already a different address store this is a bad user bug. Do not store @@ -98,7 +99,8 @@ __pthread_cond_timedwait (cond, mutex, abstime) INTERNAL_SYSCALL_DECL (err); int ret; ret = INTERNAL_SYSCALL (clock_gettime, err, 2, - cond->__data.__clock, &rt); + cond->__data.__nwaiters & COND_CLOCK_BITS, + &rt); # ifndef __ASSUME_POSIX_TIMERS if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (ret, err), 0)) { @@ -185,6 +187,16 @@ __pthread_cond_timedwait (cond, mutex, abstime) ++cond->__data.__woken_seq; bc_out: + + cond->__data.__nwaiters -= 1 << COND_CLOCK_BITS; + + /* If pthread_cond_destroy was called on this variable already, + notify the pthread_cond_destroy caller all waiters have left + and it can be successfully destroyed. */ + if (cond->__data.__total_seq == -1ULL + && cond->__data.__nwaiters < (1 << COND_CLOCK_BITS)) + lll_futex_wake (&cond->__data.__nwaiters, 1); + /* We are done with the condvar. */ lll_mutex_unlock (cond->__data.__lock); diff --git a/nptl/sysdeps/pthread/pthread_cond_wait.c b/nptl/sysdeps/pthread/pthread_cond_wait.c index 45187b5240..86669458a0 100644 --- a/nptl/sysdeps/pthread/pthread_cond_wait.c +++ b/nptl/sysdeps/pthread/pthread_cond_wait.c @@ -42,6 +42,7 @@ __condvar_cleanup (void *arg) { struct _condvar_cleanup_buffer *cbuffer = (struct _condvar_cleanup_buffer *) arg; + unsigned int destroying; /* We are going to modify shared data. */ lll_mutex_lock (cbuffer->cond->__data.__lock); @@ -55,11 +56,25 @@ __condvar_cleanup (void *arg) ++cbuffer->cond->__data.__futex; } + cbuffer->cond->__data.__nwaiters -= 1 << COND_CLOCK_BITS; + + /* If pthread_cond_destroy was called on this variable already, + notify the pthread_cond_destroy caller all waiters have left + and it can be successfully destroyed. */ + destroying = 0; + if (cbuffer->cond->__data.__total_seq == -1ULL + && cbuffer->cond->__data.__nwaiters < (1 << COND_CLOCK_BITS)) + { + lll_futex_wake (&cbuffer->cond->__data.__nwaiters, 1); + destroying = 1; + } + /* We are done. */ lll_mutex_unlock (cbuffer->cond->__data.__lock); /* Wake everybody to make sure no condvar signal gets lost. */ - lll_futex_wake (&cbuffer->cond->__data.__futex, INT_MAX); + if (! destroying) + lll_futex_wake (&cbuffer->cond->__data.__futex, INT_MAX); /* Get the mutex before returning unless asynchronous cancellation is in effect. */ @@ -90,6 +105,7 @@ __pthread_cond_wait (cond, mutex) /* We have one new user of the condvar. */ ++cond->__data.__total_seq; ++cond->__data.__futex; + cond->__data.__nwaiters += 1 << COND_CLOCK_BITS; /* Remember the mutex we are using here. If there is already a different address store this is a bad user bug. Do not store @@ -145,6 +161,16 @@ __pthread_cond_wait (cond, mutex) ++cond->__data.__woken_seq; bc_out: + + cond->__data.__nwaiters -= 1 << COND_CLOCK_BITS; + + /* If pthread_cond_destroy was called on this varaible already, + notify the pthread_cond_destroy caller all waiters have left + and it can be successfully destroyed. */ + if (cond->__data.__total_seq == -1ULL + && cond->__data.__nwaiters < (1 << COND_CLOCK_BITS)) + lll_futex_wake (&cond->__data.__nwaiters, 1); + /* We are done with the condvar. */ lll_mutex_unlock (cond->__data.__lock); diff --git a/nptl/sysdeps/unix/sysv/linux/alpha/bits/pthreadtypes.h b/nptl/sysdeps/unix/sysv/linux/alpha/bits/pthreadtypes.h index 62c853cee9..fd20d572af 100644 --- a/nptl/sysdeps/unix/sysv/linux/alpha/bits/pthreadtypes.h +++ b/nptl/sysdeps/unix/sysv/linux/alpha/bits/pthreadtypes.h @@ -81,7 +81,7 @@ typedef union unsigned long long int __wakeup_seq; unsigned long long int __woken_seq; void *__mutex; - int __clock; + unsigned int __nwaiters; unsigned int __broadcast_seq; } __data; char __size[__SIZEOF_PTHREAD_COND_T]; diff --git a/nptl/sysdeps/unix/sysv/linux/i386/bits/pthreadtypes.h b/nptl/sysdeps/unix/sysv/linux/i386/bits/pthreadtypes.h index 8d7858a071..fb62c0d99b 100644 --- a/nptl/sysdeps/unix/sysv/linux/i386/bits/pthreadtypes.h +++ b/nptl/sysdeps/unix/sysv/linux/i386/bits/pthreadtypes.h @@ -81,7 +81,7 @@ typedef union unsigned long long int __wakeup_seq; unsigned long long int __woken_seq; void *__mutex; - int __clock; + unsigned int __nwaiters; unsigned int __broadcast_seq; } __data; char __size[__SIZEOF_PTHREAD_COND_T]; diff --git a/nptl/sysdeps/unix/sysv/linux/i386/i486/pthread_cond_timedwait.S b/nptl/sysdeps/unix/sysv/linux/i386/i486/pthread_cond_timedwait.S index eecec8aee3..699c2cb227 100644 --- a/nptl/sysdeps/unix/sysv/linux/i386/i486/pthread_cond_timedwait.S +++ b/nptl/sysdeps/unix/sysv/linux/i386/i486/pthread_cond_timedwait.S @@ -87,6 +87,7 @@ __pthread_cond_timedwait: addl $1, total_seq(%ebx) adcl $0, total_seq+4(%ebx) addl $1, cond_futex(%ebx) + addl $(1 << clock_bits), cond_nwaiters(%ebx) #define FRAME_SIZE 24 subl $FRAME_SIZE, %esp @@ -104,8 +105,9 @@ __pthread_cond_timedwait: 8: movl %ebx, %edx #ifdef __NR_clock_gettime /* Get the clock number. */ - movl cond_clock(%ebx), %ebx - /* Only clocks 0 and 1 are allowed. Both are handled in the + movl cond_nwaiters(%ebx), %ebx + andl $((1 << clock_bits) - 1), %ebx + /* Only clocks 0 and 1 are allowed so far. Both are handled in the kernel. */ leal 4(%esp), %ecx movl $__NR_clock_gettime, %eax @@ -226,7 +228,25 @@ __pthread_cond_timedwait: 14: addl $1, woken_seq(%ebx) adcl $0, woken_seq+4(%ebx) -24: LOCK +24: subl $(1 << clock_bits), cond_nwaiters(%ebx) + + /* Wake up a thread which wants to destroy the condvar object. */ + movl total_seq(%ebx), %eax + andl total_seq+4(%ebx), %eax + cmpl $0xffffffff, %eax + jne 25f + movl cond_nwaiters(%ebx), %eax + andl $~((1 << clock_bits) - 1), %eax + jne 25f + + addl $cond_nwaiters, %ebx + movl $SYS_futex, %eax + movl $FUTEX_WAKE, %ecx + movl $1, %edx + ENTER_KERNEL + subl $cond_nwaiters, %ebx + +25: LOCK #if cond_lock == 0 subl $1, (%ebx) #else @@ -394,7 +414,27 @@ __condvar_tw_cleanup: addl $1, woken_seq(%ebx) adcl $0, woken_seq+4(%ebx) -3: LOCK +3: subl $(1 << clock_bits), cond_nwaiters(%ebx) + + /* Wake up a thread which wants to destroy the condvar object. */ + xorl %edi, %edi + movl total_seq(%ebx), %eax + andl total_seq+4(%ebx), %eax + cmpl $0xffffffff, %eax + jne 4f + movl cond_nwaiters(%ebx), %eax + andl $~((1 << clock_bits) - 1), %eax + jne 4f + + addl $cond_nwaiters, %ebx + movl $SYS_futex, %eax + movl $FUTEX_WAKE, %ecx + movl $1, %edx + ENTER_KERNEL + subl $cond_nwaiters, %ebx + movl $1, %edi + +4: LOCK #if cond_lock == 0 subl $1, (%ebx) #else @@ -410,13 +450,15 @@ __condvar_tw_cleanup: call __lll_mutex_unlock_wake /* Wake up all waiters to make sure no signal gets lost. */ -2: addl $cond_futex, %ebx +2: testl %edi, %edi + jnz 5f + addl $cond_futex, %ebx movl $FUTEX_WAKE, %ecx movl $SYS_futex, %eax movl $0x7fffffff, %edx ENTER_KERNEL - movl 24+FRAME_SIZE(%esp), %eax +5: movl 24+FRAME_SIZE(%esp), %eax call __pthread_mutex_cond_lock movl %esi, (%esp) diff --git a/nptl/sysdeps/unix/sysv/linux/i386/i486/pthread_cond_wait.S b/nptl/sysdeps/unix/sysv/linux/i386/i486/pthread_cond_wait.S index 3fe7f8c17a..d282785151 100644 --- a/nptl/sysdeps/unix/sysv/linux/i386/i486/pthread_cond_wait.S +++ b/nptl/sysdeps/unix/sysv/linux/i386/i486/pthread_cond_wait.S @@ -80,6 +80,7 @@ __pthread_cond_wait: addl $1, total_seq(%ebx) adcl $0, total_seq+4(%ebx) addl $1, cond_futex(%ebx) + addl $(1 << clock_bits), cond_nwaiters(%ebx) #define FRAME_SIZE 16 subl $FRAME_SIZE, %esp @@ -156,7 +157,25 @@ __pthread_cond_wait: adcl $0, woken_seq+4(%ebx) /* Unlock */ -16: LOCK +16: subl $(1 << clock_bits), cond_nwaiters(%ebx) + + /* Wake up a thread which wants to destroy the condvar object. */ + movl total_seq(%ebx), %eax + andl total_seq+4(%ebx), %eax + cmpl $0xffffffff, %eax + jne 17f + movl cond_nwaiters(%ebx), %eax + andl $~((1 << clock_bits) - 1), %eax + jne 17f + + addl $cond_nwaiters, %ebx + movl $SYS_futex, %eax + movl $FUTEX_WAKE, %ecx + movl $1, %edx + ENTER_KERNEL + subl $cond_nwaiters, %ebx + +17: LOCK #if cond_lock == 0 subl $1, (%ebx) #else @@ -286,7 +305,27 @@ __condvar_w_cleanup: addl $1, woken_seq(%ebx) adcl $0, woken_seq+4(%ebx) -3: LOCK +3: subl $(1 << clock_bits), cond_nwaiters(%ebx) + + /* Wake up a thread which wants to destroy the condvar object. */ + xorl %edi, %edi + movl total_seq(%ebx), %eax + andl total_seq+4(%ebx), %eax + cmpl $0xffffffff, %eax + jne 4f + movl cond_nwaiters(%ebx), %eax + andl $~((1 << clock_bits) - 1), %eax + jne 4f + + addl $cond_nwaiters, %ebx + movl $SYS_futex, %eax + movl $FUTEX_WAKE, %ecx + movl $1, %edx + ENTER_KERNEL + subl $cond_nwaiters, %ebx + movl $1, %edi + +4: LOCK #if cond_lock == 0 subl $1, (%ebx) #else @@ -302,13 +341,15 @@ __condvar_w_cleanup: call __lll_mutex_unlock_wake /* Wake up all waiters to make sure no signal gets lost. */ -2: addl $cond_futex, %ebx +2: testl %edi, %edi + jnz 5f + addl $cond_futex, %ebx movl $FUTEX_WAKE, %ecx movl $SYS_futex, %eax movl $0x7fffffff, %edx ENTER_KERNEL - movl 20+FRAME_SIZE(%esp), %eax +5: movl 20+FRAME_SIZE(%esp), %eax call __pthread_mutex_cond_lock movl %esi, (%esp) diff --git a/nptl/sysdeps/unix/sysv/linux/ia64/bits/pthreadtypes.h b/nptl/sysdeps/unix/sysv/linux/ia64/bits/pthreadtypes.h index 958af47162..5b442cbca7 100644 --- a/nptl/sysdeps/unix/sysv/linux/ia64/bits/pthreadtypes.h +++ b/nptl/sysdeps/unix/sysv/linux/ia64/bits/pthreadtypes.h @@ -81,7 +81,7 @@ typedef union unsigned long long int __wakeup_seq; unsigned long long int __woken_seq; void *__mutex; - int __clock; + unsigned int __nwaiters; unsigned int __broadcast_seq; } __data; char __size[__SIZEOF_PTHREAD_COND_T]; diff --git a/nptl/sysdeps/unix/sysv/linux/internaltypes.h b/nptl/sysdeps/unix/sysv/linux/internaltypes.h index 43b6856bbf..1dec19e57d 100644 --- a/nptl/sysdeps/unix/sysv/linux/internaltypes.h +++ b/nptl/sysdeps/unix/sysv/linux/internaltypes.h @@ -75,6 +75,12 @@ struct pthread_condattr }; +/* The __NWAITERS field is used as a counter and to house the number + of bits which represent the clock. COND_CLOCK_BITS is the number + of bits reserved for the clock. */ +#define COND_CLOCK_BITS 1 + + /* Read-write lock variable attribute data structure. */ struct pthread_rwlockattr { diff --git a/nptl/sysdeps/unix/sysv/linux/lowlevelcond.sym b/nptl/sysdeps/unix/sysv/linux/lowlevelcond.sym index 2e193e6d2e..c5e7978069 100644 --- a/nptl/sysdeps/unix/sysv/linux/lowlevelcond.sym +++ b/nptl/sysdeps/unix/sysv/linux/lowlevelcond.sym @@ -1,13 +1,16 @@ #include +#include #include +#include -- cond_lock offsetof (pthread_cond_t, __data.__lock) cond_futex offsetof (pthread_cond_t, __data.__futex) -cond_clock offsetof (pthread_cond_t, __data.__clock) +cond_nwaiters offsetof (pthread_cond_t, __data.__nwaiters) total_seq offsetof (pthread_cond_t, __data.__total_seq) wakeup_seq offsetof (pthread_cond_t, __data.__wakeup_seq) woken_seq offsetof (pthread_cond_t, __data.__woken_seq) dep_mutex offsetof (pthread_cond_t, __data.__mutex) broadcast_seq offsetof (pthread_cond_t, __data.__broadcast_seq) +clock_bits COND_CLOCK_BITS diff --git a/nptl/sysdeps/unix/sysv/linux/powerpc/bits/pthreadtypes.h b/nptl/sysdeps/unix/sysv/linux/powerpc/bits/pthreadtypes.h index 5a000d30af..a493821c07 100644 --- a/nptl/sysdeps/unix/sysv/linux/powerpc/bits/pthreadtypes.h +++ b/nptl/sysdeps/unix/sysv/linux/powerpc/bits/pthreadtypes.h @@ -101,7 +101,7 @@ typedef union unsigned long long int __wakeup_seq; unsigned long long int __woken_seq; void *__mutex; - int __clock; + unsigned int __nwaiters; unsigned int __broadcast_seq; } __data; char __size[__SIZEOF_PTHREAD_COND_T]; diff --git a/nptl/sysdeps/unix/sysv/linux/s390/bits/pthreadtypes.h b/nptl/sysdeps/unix/sysv/linux/s390/bits/pthreadtypes.h index 142286451c..17cfaa981d 100644 --- a/nptl/sysdeps/unix/sysv/linux/s390/bits/pthreadtypes.h +++ b/nptl/sysdeps/unix/sysv/linux/s390/bits/pthreadtypes.h @@ -100,7 +100,7 @@ typedef union unsigned long long int __wakeup_seq; unsigned long long int __woken_seq; void *__mutex; - int __clock; + unsigned int __nwaiters; unsigned int __broadcast_seq; } __data; char __size[__SIZEOF_PTHREAD_COND_T]; diff --git a/nptl/sysdeps/unix/sysv/linux/sh/bits/pthreadtypes.h b/nptl/sysdeps/unix/sysv/linux/sh/bits/pthreadtypes.h index b8afbc50b5..5bf3aff46a 100644 --- a/nptl/sysdeps/unix/sysv/linux/sh/bits/pthreadtypes.h +++ b/nptl/sysdeps/unix/sysv/linux/sh/bits/pthreadtypes.h @@ -82,7 +82,7 @@ typedef union unsigned long long int __wakeup_seq; unsigned long long int __woken_seq; void *__mutex; - int __clock; + unsigned int __nwaiters; unsigned int __broadcast_seq; } __data; char __size[__SIZEOF_PTHREAD_COND_T]; diff --git a/nptl/sysdeps/unix/sysv/linux/sparc/bits/pthreadtypes.h b/nptl/sysdeps/unix/sysv/linux/sparc/bits/pthreadtypes.h index e86dd000a1..34d3df7abb 100644 --- a/nptl/sysdeps/unix/sysv/linux/sparc/bits/pthreadtypes.h +++ b/nptl/sysdeps/unix/sysv/linux/sparc/bits/pthreadtypes.h @@ -101,7 +101,7 @@ typedef union unsigned long long int __wakeup_seq; unsigned long long int __woken_seq; void *__mutex; - int __clock; + unsigned int __nwaiters; unsigned int __broadcast_seq; } __data; char __size[__SIZEOF_PTHREAD_COND_T]; diff --git a/nptl/sysdeps/unix/sysv/linux/x86_64/bits/pthreadtypes.h b/nptl/sysdeps/unix/sysv/linux/x86_64/bits/pthreadtypes.h index a7bc5816cc..e5cc605f24 100644 --- a/nptl/sysdeps/unix/sysv/linux/x86_64/bits/pthreadtypes.h +++ b/nptl/sysdeps/unix/sysv/linux/x86_64/bits/pthreadtypes.h @@ -100,7 +100,7 @@ typedef union unsigned long long int __wakeup_seq; unsigned long long int __woken_seq; void *__mutex; - int __clock; + int __nwaiters; unsigned int __broadcast_seq; } __data; char __size[__SIZEOF_PTHREAD_COND_T]; diff --git a/nptl/sysdeps/unix/sysv/linux/x86_64/pthread_cond_timedwait.S b/nptl/sysdeps/unix/sysv/linux/x86_64/pthread_cond_timedwait.S index 95f8aabd11..67bec6caa7 100644 --- a/nptl/sysdeps/unix/sysv/linux/x86_64/pthread_cond_timedwait.S +++ b/nptl/sysdeps/unix/sysv/linux/x86_64/pthread_cond_timedwait.S @@ -111,6 +111,7 @@ __pthread_cond_timedwait: movq 8(%rsp), %rdi incq total_seq(%rdi) incl cond_futex(%rdi) + addl $(1 << clock_bits), cond_nwaiters(%rdi) /* Install cancellation handler. */ #ifdef PIC @@ -135,8 +136,9 @@ __pthread_cond_timedwait: /* Get the clock number. Note that the field in the condvar structure stores the number minus 1. */ movq 8(%rsp), %rdi - movl cond_clock(%rdi), %edi - /* Only clocks 0 and 1 are allowed. Both are handled in the + movl cond_nwaiters(%rdi), %edi + andl $((1 << clock_bits) - 1), %edi + /* Only clocks 0 and 1 are allowed so far. Both are handled in the kernel. */ leaq 24(%rsp), %rsi movq $__NR_clock_gettime, %rax @@ -244,7 +246,23 @@ __pthread_cond_timedwait: 9: xorq %r14, %r14 14: incq woken_seq(%rdi) -24: LOCK +24: subl $(1 << clock_bits), cond_nwaiters(%rdi) + + /* Wake up a thread which wants to destroy the condvar object. */ + cmpq $0xffffffffffffffff, total_seq(%rdi) + jne 25f + movl cond_nwaiters(%rdi), %eax + andl $~((1 << clock_bits) - 1), %eax + jne 25f + + addq $cond_nwaiters, %rdi + movq $SYS_futex, %rax + movq $FUTEX_WAKE, %rsi + movl $1, %edx + syscall + subq $cond_nwaiters, %rdi + +25: LOCK #if cond_lock == 0 decl (%rdi) #else diff --git a/nptl/sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S b/nptl/sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S index 9e7da301d3..f5de0a280c 100644 --- a/nptl/sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S +++ b/nptl/sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S @@ -40,6 +40,8 @@ .globl __condvar_cleanup .hidden __condvar_cleanup __condvar_cleanup: + pushq %r12 + /* Get internal lock. */ movq %rdi, %r8 movq 8(%rdi), %rdi @@ -66,12 +68,28 @@ __condvar_cleanup: jne 3f incq wakeup_seq(%rdi) - incq woken_seq(%rdi) - incl cond_futex(%rdi) -3: LOCK +3: subl $(1 << clock_bits), cond_nwaiters(%rdi) + + /* Wake up a thread which wants to destroy the condvar object. */ + xorq %r12, %r12 + cmpq $0xffffffffffffffff, total_seq(%rdi) + jne 4f + movl cond_nwaiters(%rdi), %eax + andl $~((1 << clock_bits) - 1), %eax + jne 4f + + addq $cond_nwaiters, %rdi + movq $SYS_futex, %rax + movq $FUTEX_WAKE, %rsi + movl $1, %edx + syscall + subq $cond_nwaiters, %rdi + movq $1, %r12 + +4: LOCK #if cond_lock == 0 decl (%rdi) #else @@ -84,15 +102,19 @@ __condvar_cleanup: callq __lll_mutex_unlock_wake /* Wake up all waiters to make sure no signal gets lost. */ -2: addq $cond_futex, %rdi +2: testq %r12, %r12 + jnz 5f + addq $cond_futex, %rdi movq $FUTEX_WAKE, %rsi movl $0x7fffffff, %edx movq $SYS_futex, %rax syscall - movq 16(%r8), %rdi +5: movq 16(%r8), %rdi callq __pthread_mutex_cond_lock + popq %r12 + retq .size __condvar_cleanup, .-__condvar_cleanup @@ -157,6 +179,7 @@ __pthread_cond_wait: movq 8(%rsp), %rdi incq total_seq(%rdi) incl cond_futex(%rdi) + addl $(1 << clock_bits), cond_nwaiters(%rdi) /* Install cancellation handler. */ #ifdef PIC @@ -229,7 +252,23 @@ __pthread_cond_wait: incq woken_seq(%rdi) /* Unlock */ -16: LOCK +16: subl $(1 << clock_bits), cond_nwaiters(%rdi) + + /* Wake up a thread which wants to destroy the condvar object. */ + cmpq $0xffffffffffffffff, total_seq(%rdi) + jne 17f + movl cond_nwaiters(%rdi), %eax + andl $~((1 << clock_bits) - 1), %eax + jne 17f + + addq $cond_nwaiters, %rdi + movq $SYS_futex, %rax + movq $FUTEX_WAKE, %rsi + movl $1, %edx + syscall + subq $cond_nwaiters, %rdi + +17: LOCK #if cond_lock == 0 decl (%rdi) #else diff --git a/nptl/tst-cond20.c b/nptl/tst-cond20.c new file mode 100644 index 0000000000..ba50d58534 --- /dev/null +++ b/nptl/tst-cond20.c @@ -0,0 +1,170 @@ +/* Copyright (C) 2004 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Jakub Jelinek , 2004. + + 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 +#include +#include +#include +#include + +#define N 10 +#define ROUNDS 1000 +static pthread_cond_t cond = PTHREAD_COND_INITIALIZER; +static pthread_cond_t cond2 = PTHREAD_COND_INITIALIZER; +static pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER; +static pthread_barrier_t b; +static int count; + +static void * +tf (void *p) +{ + int i; + for (i = 0; i < ROUNDS; ++i) + { + pthread_mutex_lock (&mut); + + if (++count == N) + pthread_cond_signal (&cond2); + +#ifdef TIMED + struct timeval tv; + gettimeofday (&tv, NULL); + struct timespec ts; + /* Wait three second. */ + ts.tv_sec = tv.tv_sec + 3; + ts.tv_nsec = tv.tv_usec * 1000; + pthread_cond_timedwait (&cond, &mut, &ts); +#else + pthread_cond_wait (&cond, &mut); +#endif + + pthread_mutex_unlock (&mut); + + int err = pthread_barrier_wait (&b); + if (err != 0 && err != PTHREAD_BARRIER_SERIAL_THREAD) + { + puts ("child: barrier_wait failed"); + exit (1); + } + + err = pthread_barrier_wait (&b); + if (err != 0 && err != PTHREAD_BARRIER_SERIAL_THREAD) + { + puts ("child: barrier_wait failed"); + exit (1); + } + } + + return NULL; +} + + +static int +do_test (void) +{ + if (pthread_barrier_init (&b, NULL, N + 1) != 0) + { + puts ("barrier_init failed"); + return 1; + } + + pthread_mutex_lock (&mut); + + int i, j, err; + pthread_t th[N]; + for (i = 0; i < N; ++i) + if ((err = pthread_create (&th[i], NULL, tf, NULL)) != 0) + { + printf ("cannot create thread %d: %s\n", i, strerror (err)); + return 1; + } + + for (i = 0; i < ROUNDS; ++i) + { + pthread_cond_wait (&cond2, &mut); + + if (i & 1) + pthread_mutex_unlock (&mut); + + if (i & 2) + pthread_cond_broadcast (&cond); + else if (i & 4) + for (j = 0; j < N; ++j) + pthread_cond_signal (&cond); + else + { + for (j = 0; j < (i / 8) % N; ++j) + pthread_cond_signal (&cond); + pthread_cond_broadcast (&cond); + } + + if ((i & 1) == 0) + pthread_mutex_unlock (&mut); + + err = pthread_cond_destroy (&cond); + if (err) + { + printf ("pthread_cond_destroy failed: %s\n", strerror (err)); + return 1; + } + + /* Now clobber the cond variable which has been successfully + destroyed above. */ + memset (&cond, (char) i, sizeof (cond)); + + err = pthread_barrier_wait (&b); + if (err != 0 && err != PTHREAD_BARRIER_SERIAL_THREAD) + { + puts ("parent: barrier_wait failed"); + return 1; + } + + pthread_mutex_lock (&mut); + + err = pthread_barrier_wait (&b); + if (err != 0 && err != PTHREAD_BARRIER_SERIAL_THREAD) + { + puts ("parent: barrier_wait failed"); + return 1; + } + + count = 0; + err = pthread_cond_init (&cond, NULL); + if (err) + { + printf ("pthread_cond_init failed: %s\n", strerror (err)); + return 1; + } + } + + for (i = 0; i < N; ++i) + if ((err = pthread_join (th[i], NULL)) != 0) + { + printf ("failed to join thread %d: %s\n", i, strerror (err)); + return 1; + } + + puts ("done"); + + return 0; +} + + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/nptl/tst-cond21.c b/nptl/tst-cond21.c new file mode 100644 index 0000000000..89cb771b57 --- /dev/null +++ b/nptl/tst-cond21.c @@ -0,0 +1,3 @@ +#include +#define TIMED 1 +#include "tst-cond20.c" -- cgit 1.4.1