2006-09-12 Ulrich Drepper <drepper@redhat.com> * tst-cond22.c (tf): Slight changes to the pthread_cond_wait use to guarantee the thread is always canceled. 2006-09-08 Jakub Jelinek <jakub@redhat.com> * tst-cond22.c: Include pthread.h instead of pthreadP.h. Include stdlib.h. * sysdeps/pthread/pthread_cond_wait.c (__condvar_cleanup): Only increase FUTEX if increasing WAKEUP_SEQ. Fix comment typo. * sysdeps/unix/sysv/linux/i386/i486/pthread_cond_wait.S: Likewise. * sysdeps/unix/sysv/linux/i386/i486/pthread_cond_timedwait.S: Likewise. * sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S: Likewise. 2006-09-08 Ulrich Drepper <drepper@redhat.com> [BZ #3123] * sysdeps/pthread/pthread_cond_wait.c (__condvar_cleanup): Don't increment WAKEUP_SEQ if this would increase the value beyond TOTAL_SEQ. * sysdeps/unix/sysv/linux/i386/i486/pthread_cond_wait.S: Likewise. * sysdeps/unix/sysv/linux/i386/i486/pthread_cond_timedwait.S: Likewise. * sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S: Likewise. * Makefile (tests): Add tst-cond22. * tst-cond22.c: New file. --- libc/nptl/Makefile 21 Aug 2006 21:02:10 -0000 1.186 +++ libc/nptl/Makefile 8 Sep 2006 10:40:49 -0000 1.188 @@ -207,7 +207,7 @@ tests = tst-typesizes \ 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-cond20 tst-cond21 tst-cond22 \ tst-robust1 tst-robust2 tst-robust3 tst-robust4 tst-robust5 \ tst-robust6 tst-robust7 tst-robust8 \ tst-robustpi1 tst-robustpi2 tst-robustpi3 tst-robustpi4 \ --- libc/nptl/tst-cond22.c 1 Jan 1970 00:00:00 -0000 +++ libc/nptl/tst-cond22.c 12 Sep 2006 12:24:28 -0000 1.5 @@ -0,0 +1,160 @@ +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> + + +static pthread_barrier_t b; +static pthread_cond_t c = PTHREAD_COND_INITIALIZER; +static pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER; + + +static void +cl (void *arg) +{ + pthread_mutex_unlock (&m); +} + + +static void * +tf (void *arg) +{ + if (pthread_mutex_lock (&m) != 0) + { + printf ("%s: mutex_lock failed\n", __func__); + exit (1); + } + int e = pthread_barrier_wait (&b); + if (e != 0 && e != PTHREAD_BARRIER_SERIAL_THREAD) + { + printf ("%s: barrier_wait failed\n", __func__); + exit (1); + } + pthread_cleanup_push (cl, NULL); + /* We have to loop here because the cancellation might come after + the cond_wait call left the cancelable area and is then waiting + on the mutex. In this case the beginning of the second cond_wait + call will cause the cancellation to happen. */ + do + if (pthread_cond_wait (&c, &m) != 0) + { + printf ("%s: cond_wait failed\n", __func__); + exit (1); + } + while (arg == NULL); + pthread_cleanup_pop (0); + if (pthread_mutex_unlock (&m) != 0) + { + printf ("%s: mutex_unlock failed\n", __func__); + exit (1); + } + return NULL; +} + + +static int +do_test (void) +{ + int status = 0; + + if (pthread_barrier_init (&b, NULL, 2) != 0) + { + puts ("barrier_init failed"); + return 1; + } + + pthread_t th; + if (pthread_create (&th, NULL, tf, NULL) != 0) + { + puts ("1st create failed"); + return 1; + } + int e = pthread_barrier_wait (&b); + if (e != 0 && e != PTHREAD_BARRIER_SERIAL_THREAD) + { + puts ("1st barrier_wait failed"); + return 1; + } + if (pthread_mutex_lock (&m) != 0) + { + puts ("1st mutex_lock failed"); + return 1; + } + if (pthread_cond_signal (&c) != 0) + { + puts ("1st cond_signal failed"); + return 1; + } + if (pthread_cancel (th) != 0) + { + puts ("cancel failed"); + return 1; + } + if (pthread_mutex_unlock (&m) != 0) + { + puts ("1st mutex_unlock failed"); + return 1; + } + void *res; + if (pthread_join (th, &res) != 0) + { + puts ("1st join failed"); + return 1; + } + if (res != PTHREAD_CANCELED) + { + puts ("first thread not canceled"); + status = 1; + } + + printf ("cond = { %d, %x, %lld, %lld, %lld, %p, %u, %u }\n", + c.__data.__lock, c.__data.__futex, c.__data.__total_seq, + c.__data.__wakeup_seq, c.__data.__woken_seq, c.__data.__mutex, + c.__data.__nwaiters, c.__data.__broadcast_seq); + + if (pthread_create (&th, NULL, tf, (void *) 1l) != 0) + { + puts ("2nd create failed"); + return 1; + } + e = pthread_barrier_wait (&b); + if (e != 0 && e != PTHREAD_BARRIER_SERIAL_THREAD) + { + puts ("2nd barrier_wait failed"); + return 1; + } + if (pthread_mutex_lock (&m) != 0) + { + puts ("2nd mutex_lock failed"); + return 1; + } + if (pthread_cond_signal (&c) != 0) + { + puts ("2nd cond_signal failed"); + return 1; + } + if (pthread_mutex_unlock (&m) != 0) + { + puts ("2nd mutex_unlock failed"); + return 1; + } + if (pthread_join (th, &res) != 0) + { + puts ("2nd join failed"); + return 1; + } + if (res != NULL) + { + puts ("2nd thread canceled"); + status = 1; + } + + printf ("cond = { %d, %x, %lld, %lld, %lld, %p, %u, %u }\n", + c.__data.__lock, c.__data.__futex, c.__data.__total_seq, + c.__data.__wakeup_seq, c.__data.__woken_seq, c.__data.__mutex, + c.__data.__nwaiters, c.__data.__broadcast_seq); + + return status; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" --- libc/nptl/sysdeps/pthread/pthread_cond_wait.c 2 Sep 2004 18:49:34 -0000 1.16 +++ libc/nptl/sysdeps/pthread/pthread_cond_wait.c 9 Sep 2006 11:21:23 -0000 1.18 @@ -50,10 +50,16 @@ __condvar_cleanup (void *arg) if (cbuffer->bc_seq == cbuffer->cond->__data.__broadcast_seq) { /* This thread is not waiting anymore. Adjust the sequence counters - appropriately. */ - ++cbuffer->cond->__data.__wakeup_seq; + appropriately. We do not increment WAKEUP_SEQ if this would + bump it over the value of TOTAL_SEQ. This can happen if a thread + was woken and then canceled. */ + if (cbuffer->cond->__data.__wakeup_seq + < cbuffer->cond->__data.__total_seq) + { + ++cbuffer->cond->__data.__wakeup_seq; + ++cbuffer->cond->__data.__futex; + } ++cbuffer->cond->__data.__woken_seq; - ++cbuffer->cond->__data.__futex; } cbuffer->cond->__data.__nwaiters -= 1 << COND_CLOCK_BITS; --- libc/nptl/sysdeps/unix/sysv/linux/i386/i486/pthread_cond_timedwait.S 2 Sep 2004 18:53:10 -0000 1.37 +++ libc/nptl/sysdeps/unix/sysv/linux/i386/i486/pthread_cond_timedwait.S 9 Sep 2006 11:21:23 -0000 1.39 @@ -406,12 +406,22 @@ __condvar_tw_cleanup: cmpl 20(%esp), %eax jne 3f - addl $1, wakeup_seq(%ebx) - adcl $0, wakeup_seq+4(%ebx) + /* We increment the wakeup_seq counter only if it is lower than + total_seq. If this is not the case the thread was woken and + then canceled. In this case we ignore the signal. */ + movl total_seq(%ebx), %eax + movl total_seq+4(%ebx), %edi + cmpl wakeup_seq+4(%ebx), %edi + jb 6f + ja 7f + cmpl wakeup_seq(%ebx), %eax + jbe 7f +6: addl $1, wakeup_seq(%ebx) + adcl $0, wakeup_seq+4(%ebx) addl $1, cond_futex(%ebx) - addl $1, woken_seq(%ebx) +7: addl $1, woken_seq(%ebx) adcl $0, woken_seq+4(%ebx) 3: subl $(1 << clock_bits), cond_nwaiters(%ebx) --- libc/nptl/sysdeps/unix/sysv/linux/i386/i486/pthread_cond_wait.S 2 Sep 2004 18:52:38 -0000 1.32 +++ libc/nptl/sysdeps/unix/sysv/linux/i386/i486/pthread_cond_wait.S 9 Sep 2006 11:21:23 -0000 1.34 @@ -297,12 +297,22 @@ __condvar_w_cleanup: cmpl 12(%esp), %eax jne 3f - addl $1, wakeup_seq(%ebx) - adcl $0, wakeup_seq+4(%ebx) + /* We increment the wakeup_seq counter only if it is lower than + total_seq. If this is not the case the thread was woken and + then canceled. In this case we ignore the signal. */ + movl total_seq(%ebx), %eax + movl total_seq+4(%ebx), %edi + cmpl wakeup_seq+4(%ebx), %edi + jb 6f + ja 7f + cmpl wakeup_seq(%ebx), %eax + jbe 7f +6: addl $1, wakeup_seq(%ebx) + adcl $0, wakeup_seq+4(%ebx) addl $1, cond_futex(%ebx) - addl $1, woken_seq(%ebx) +7: addl $1, woken_seq(%ebx) adcl $0, woken_seq+4(%ebx) 3: subl $(1 << clock_bits), cond_nwaiters(%ebx) --- libc/nptl/sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S 31 Mar 2005 10:00:15 -0000 1.22 +++ libc/nptl/sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S 9 Sep 2006 11:21:23 -0000 1.24 @@ -67,9 +67,15 @@ __condvar_cleanup: cmpl 4(%r8), %edx jne 3f + /* We increment the wakeup_seq counter only if it is lower than + total_seq. If this is not the case the thread was woken and + then canceled. In this case we ignore the signal. */ + movq total_seq(%rdi), %rax + cmpq wakeup_seq(%rdi), %rax + jbe 6f incq wakeup_seq(%rdi) - incq woken_seq(%rdi) incl cond_futex(%rdi) +6: incq woken_seq(%rdi) 3: subl $(1 << clock_bits), cond_nwaiters(%rdi)