Unlock mutex before going back to waiting for PI mutexes
authorSiddhesh Poyarekar <siddhesh@redhat.com>
Fri, 5 Oct 2012 13:22:35 +0000 (18:52 +0530)
committerSiddhesh Poyarekar <siddhesh@redhat.com>
Fri, 5 Oct 2012 13:22:36 +0000 (18:52 +0530)
[BZ #14417]
A futex call with FUTEX_WAIT_REQUEUE_PI returns with the mutex locked
on success.  If such a successful thread is pipped to the cond_lock by
another spuriously woken waiter, it could be sent back to wait on the
futex with the mutex lock held, thus causing a deadlock.  So it is
necessary that the thread relinquishes the mutex before going back to
sleep.

NEWS
nptl/ChangeLog
nptl/Makefile
nptl/sysdeps/unix/sysv/linux/i386/i486/pthread_cond_timedwait.S
nptl/sysdeps/unix/sysv/linux/i386/i486/pthread_cond_wait.S
nptl/sysdeps/unix/sysv/linux/x86_64/pthread_cond_timedwait.S
nptl/sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S
nptl/tst-cond24.c [new file with mode: 0644]

diff --git a/NEWS b/NEWS
index 03cc6c2..bd309ba 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -13,9 +13,9 @@ Version 2.17
   10038, 10631, 11438, 11607, 13412, 13542, 13629, 13679, 13696, 13717,
   13741, 13939, 13966, 14042, 14090, 14150, 14151, 14154, 14157, 14166,
   14173, 14195, 14237, 14251, 14252, 14283, 14298, 14303, 14307, 14328,
-  14331, 14336, 14337, 14347, 14349, 14376, 14459, 14476, 14477, 14505,
-  14510, 14516, 14518, 14519, 14530, 14532, 14538, 14543, 14544, 14545,
-  14562, 14576, 14579, 14583, 14587, 14621, 14638, 14645, 14648.
+  14331, 14336, 14337, 14347, 14349, 14376, 14417, 14459, 14476, 14477,
+  14505, 14510, 14516, 14518, 14519, 14530, 14532, 14538, 14543, 14544,
+  14545, 14562, 14576, 14579, 14583, 14587, 14621, 14638, 14645, 14648.
 
 * Support for STT_GNU_IFUNC symbols added for s390 and s390x.
   Optimized versions of memcpy, memset, and memcmp added for System z10 and
index 586e282..23b7753 100644 (file)
@@ -1,3 +1,22 @@
+2012-10-05  Siddhesh Poyarekar  <siddhesh@redhat.com>
+
+       [BZ #14417]
+       * Makefile (tests): New test case tst-cond24.
+       (LDFLAGS-tst-cond24): Link tst-cond24 against librt.
+       * sysdeps/unix/sysv/linux/i386/i486/pthread_cond_timedwait.S
+       (__pthread_cond_timedwait): Unlock mutex before going back to
+       wait in PI case.
+       * sysdeps/unix/sysv/linux/i386/i486/pthread_cond_wait.S
+       (__pthread_cond_wait): Likewise.  Revert handling of EAGAIN
+       return from futex_wait.
+       * sysdeps/unix/sysv/linux/x86_64/pthread_cond_timedwait.S
+       (__pthread_cond_timedwait): Unlock mutex before going back to
+       wait in PI case.  Set requeue_pi flag only if wait returned 0.
+       * sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S
+       (__pthread_cond_wait): Likewise.  Revert handling of EAGAIN
+       return from futex_wait.
+       * tst-cond24.c: New test case.
+
 2012-10-04  Roland McGrath  <roland@hack.frob.com>
 
        * pthread_create.c (start_thread): Use __madvise, not madvise.
index b9c73b3..f21276c 100644 (file)
@@ -206,7 +206,8 @@ 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-cond22 tst-cond23 tst-cond-except \
+       tst-cond20 tst-cond21 tst-cond22 tst-cond23 tst-cond24 \
+       tst-cond-except \
        tst-robust1 tst-robust2 tst-robust3 tst-robust4 tst-robust5 \
        tst-robust6 tst-robust7 tst-robust8 tst-robust9 \
        tst-robustpi1 tst-robustpi2 tst-robustpi3 tst-robustpi4 tst-robustpi5 \
@@ -274,6 +275,7 @@ gen-as-const-headers = pthread-errnos.sym
 
 LDFLAGS-pthread.so = -Wl,--enable-new-dtags,-z,nodelete,-z,initfirst
 
+LDFLAGS-tst-cond24 = -lrt
 
 include ../Makeconfig
 
index d14d7de..6761c13 100644 (file)
@@ -212,8 +212,23 @@ __pthread_cond_timedwait:
        sete    24(%esp)
        je      41f
 
-       /* Normal and PI futexes dont mix. Use normal futex functions only
-          if the kernel does not support the PI futex functions.  */
+       /* When a futex syscall with FUTEX_WAIT_REQUEUE_PI returns
+          successfully, it has already locked the mutex for us and the
+          pi_flag (24(%esp)) is set to denote that fact.  However, if another
+          thread changed the futex value before we entered the wait, the
+          syscall may return an EAGAIN and the mutex is not locked.  We go
+          ahead with a success anyway since later we look at the pi_flag to
+          decide if we got the mutex or not.  The sequence numbers then make
+          sure that only one of the threads actually wake up.  We retry using
+          normal FUTEX_WAIT only if the kernel returned ENOSYS, since normal
+          and PI futexes don't mix.
+
+          Note that we don't check for EAGAIN specifically; we assume that the
+          only other error the futex function could return is EAGAIN (barring
+          the ETIMEOUT of course, for the timeout case in futex) since
+          anything else would mean an error in our function.  It is too
+          expensive to do that check for every call (which is  quite common in
+          case of a large number of threads), so it has been skipped.  */
        cmpl    $-ENOSYS, %eax
        jne     41f
        xorl    %ecx, %ecx
@@ -273,9 +288,24 @@ __pthread_cond_timedwait:
        jne     9f
 
 15:    cmpl    $-ETIMEDOUT, %esi
-       jne     8b
+       je      28f
+
+       /* We need to go back to futex_wait.  If we're using requeue_pi, then
+          release the mutex we had acquired and go back.  */
+       movl    24(%esp), %edx
+       test    %edx, %edx
+       jz      8b
+
+       /* Adjust the mutex values first and then unlock it.  The unlock
+          should always succeed or else the kernel did not lock the mutex
+          correctly.  */
+       movl    dep_mutex(%ebx), %eax
+       call    __pthread_mutex_cond_lock_adjust
+       xorl    %edx, %edx
+       call    __pthread_mutex_unlock_usercnt
+       jmp     8b
 
-       addl    $1, wakeup_seq(%ebx)
+28:    addl    $1, wakeup_seq(%ebx)
        adcl    $0, wakeup_seq+4(%ebx)
        addl    $1, cond_futex(%ebx)
        movl    $ETIMEDOUT, %esi
index 366de69..0af06ac 100644 (file)
@@ -136,7 +136,6 @@ __pthread_cond_wait:
        cmpl    $PI_BIT, %eax
        jne     18f
 
-90:
        movl    $(FUTEX_WAIT_REQUEUE_PI|FUTEX_PRIVATE_FLAG), %ecx
        movl    %ebp, %edx
        xorl    %esi, %esi
@@ -152,11 +151,22 @@ __pthread_cond_wait:
        sete    16(%esp)
        je      19f
 
-       cmpl    $-EAGAIN, %eax
-       je      91f
-
-       /* Normal and PI futexes dont mix. Use normal futex functions only
-          if the kernel does not support the PI futex functions.  */
+       /* When a futex syscall with FUTEX_WAIT_REQUEUE_PI returns
+          successfully, it has already locked the mutex for us and the
+          pi_flag (16(%esp)) is set to denote that fact.  However, if another
+          thread changed the futex value before we entered the wait, the
+          syscall may return an EAGAIN and the mutex is not locked.  We go
+          ahead with a success anyway since later we look at the pi_flag to
+          decide if we got the mutex or not.  The sequence numbers then make
+          sure that only one of the threads actually wake up.  We retry using
+          normal FUTEX_WAIT only if the kernel returned ENOSYS, since normal
+          and PI futexes don't mix.
+
+          Note that we don't check for EAGAIN specifically; we assume that the
+          only other error the futex function could return is EAGAIN since
+          anything else would mean an error in our function.  It is too
+          expensive to do that check for every call (which is  quite common in
+          case of a large number of threads), so it has been skipped.  */
        cmpl    $-ENOSYS, %eax
        jne     19f
        xorl    %ecx, %ecx
@@ -206,12 +216,12 @@ __pthread_cond_wait:
        cmpl    8(%esp), %edx
        jne     7f
        cmpl    4(%esp), %edi
-       je      8b
+       je      22f
 
 7:     cmpl    %ecx, %edx
        jne     9f
        cmp     %eax, %edi
-       je      8b
+       je      22f
 
 9:     addl    $1, woken_seq(%ebx)
        adcl    $0, woken_seq+4(%ebx)
@@ -287,6 +297,22 @@ __pthread_cond_wait:
        jmp     20b
 
        cfi_adjust_cfa_offset(-FRAME_SIZE);
+
+       /* We need to go back to futex_wait.  If we're using requeue_pi, then
+          release the mutex we had acquired and go back.  */
+22:    movl    16(%esp), %edx
+       test    %edx, %edx
+       jz      8b
+
+       /* Adjust the mutex values first and then unlock it.  The unlock
+          should always succeed or else the kernel did not lock the mutex
+          correctly.  */
+       movl    dep_mutex(%ebx), %eax
+       call    __pthread_mutex_cond_lock_adjust
+       xorl    %edx, %edx
+       call    __pthread_mutex_unlock_usercnt
+       jmp     8b
+
        /* Initial locking failed.  */
 1:
 #if cond_lock == 0
@@ -400,77 +426,6 @@ __pthread_cond_wait:
        call    __lll_unlock_wake
        jmp     11b
 
-91:
-.LcleanupSTART2:
-       /* FUTEX_WAIT_REQUEUE_PI returned EAGAIN.  We need to
-          call it again.  */
-
-       /* Get internal lock.  */
-       movl    $1, %edx
-       xorl    %eax, %eax
-       LOCK
-#if cond_lock == 0
-       cmpxchgl %edx, (%ebx)
-#else
-       cmpxchgl %edx, cond_lock(%ebx)
-#endif
-       jz      92f
-
-#if cond_lock == 0
-       movl    %ebx, %edx
-#else
-       leal    cond_lock(%ebx), %edx
-#endif
-#if (LLL_SHARED-LLL_PRIVATE) > 255
-       xorl    %ecx, %ecx
-#endif
-       cmpl    $-1, dep_mutex(%ebx)
-       setne   %cl
-       subl    $1, %ecx
-       andl    $(LLL_SHARED-LLL_PRIVATE), %ecx
-#if LLL_PRIVATE != 0
-       addl    $LLL_PRIVATE, %ecx
-#endif
-       call    __lll_lock_wait
-
-92:
-       /* Increment the cond_futex value again, so it can be used as a new
-          expected value. */
-       addl    $1, cond_futex(%ebx)
-       movl    cond_futex(%ebx), %ebp
-
-       /* Unlock.  */
-       LOCK
-#if cond_lock == 0
-       subl    $1, (%ebx)
-#else
-       subl    $1, cond_lock(%ebx)
-#endif
-       je      93f
-#if cond_lock == 0
-       movl    %ebx, %eax
-#else
-       leal    cond_lock(%ebx), %eax
-#endif
-#if (LLL_SHARED-LLL_PRIVATE) > 255
-       xorl    %ecx, %ecx
-#endif
-       cmpl    $-1, dep_mutex(%ebx)
-       setne   %cl
-       subl    $1, %ecx
-       andl    $(LLL_SHARED-LLL_PRIVATE), %ecx
-#if LLL_PRIVATE != 0
-       addl    $LLL_PRIVATE, %ecx
-#endif
-       call    __lll_unlock_wake
-
-93:
-       /* Set the rest of SYS_futex args for FUTEX_WAIT_REQUEUE_PI. */
-       xorl    %ecx, %ecx
-       movl    dep_mutex(%ebx), %edi
-       jmp     90b
-.LcleanupEND2:
-
        .size   __pthread_cond_wait, .-__pthread_cond_wait
 versioned_symbol (libpthread, __pthread_cond_wait, pthread_cond_wait,
                  GLIBC_2_3_2)
@@ -651,10 +606,6 @@ __condvar_w_cleanup:
        .long   .LcleanupEND-.Lsub_cond_futex
        .long   __condvar_w_cleanup-.LSTARTCODE
        .uleb128  0
-       .long   .LcleanupSTART2-.LSTARTCODE
-       .long   .LcleanupEND2-.LcleanupSTART2
-       .long   __condvar_w_cleanup-.LSTARTCODE
-       .uleb128  0
        .long   .LcallUR-.LSTARTCODE
        .long   .LENDCODE-.LcallUR
        .long   0
index a1c8ca8..b669abb 100644 (file)
@@ -103,7 +103,7 @@ __pthread_cond_timedwait:
        mov     %RSI_LP, dep_mutex(%rdi)
 
 22:
-       xorl    %r15d, %r15d
+       xorb    %r15b, %r15b
 
 #ifndef __ASSUME_FUTEX_CLOCK_REALTIME
 #  ifdef PIC
@@ -190,18 +190,39 @@ __pthread_cond_timedwait:
        movl    $SYS_futex, %eax
        syscall
 
-       movl    $1, %r15d
+       cmpl    $0, %eax
+       sete    %r15b
+
 #ifdef __ASSUME_REQUEUE_PI
        jmp     62f
 #else
-       cmpq    $-4095, %rax
-       jnae    62f
+       je      62f
+
+       /* When a futex syscall with FUTEX_WAIT_REQUEUE_PI returns
+          successfully, it has already locked the mutex for us and the
+          pi_flag (%r15b) is set to denote that fact.  However, if another
+          thread changed the futex value before we entered the wait, the
+          syscall may return an EAGAIN and the mutex is not locked.  We go
+          ahead with a success anyway since later we look at the pi_flag to
+          decide if we got the mutex or not.  The sequence numbers then make
+          sure that only one of the threads actually wake up.  We retry using
+          normal FUTEX_WAIT only if the kernel returned ENOSYS, since normal
+          and PI futexes don't mix.
+
+          Note that we don't check for EAGAIN specifically; we assume that the
+          only other error the futex function could return is EAGAIN (barring
+          the ETIMEOUT of course, for the timeout case in futex) since
+          anything else would mean an error in our function.  It is too
+          expensive to do that check for every call (which is  quite common in
+          case of a large number of threads), so it has been skipped.  */
+       cmpl    $-ENOSYS, %eax
+       jne     62f
 
        subq    $cond_futex, %rdi
 #endif
 
 61:    movl    $(FUTEX_WAIT_BITSET|FUTEX_PRIVATE_FLAG), %esi
-60:    xorl    %r15d, %r15d
+60:    xorb    %r15b, %r15b
        xorl    %eax, %eax
        /* The following only works like this because we only support
           two clocks, represented using a single bit.  */
@@ -248,7 +269,23 @@ __pthread_cond_timedwait:
        ja      39f
 
 45:    cmpq    $-ETIMEDOUT, %r14
-       jne     38b
+       je      99f
+
+       /* We need to go back to futex_wait.  If we're using requeue_pi, then
+          release the mutex we had acquired and go back.  */
+       test    %r15b, %r15b
+       jz      38b
+
+       /* Adjust the mutex values first and then unlock it.  The unlock
+          should always succeed or else the kernel did not lock the
+          mutex correctly.  */
+       movq    %r8, %rdi
+       callq   __pthread_mutex_cond_lock_adjust
+       xorl    %esi, %esi
+       callq   __pthread_mutex_unlock_usercnt
+       /* Reload cond_var.  */
+       movq    8(%rsp), %rdi
+       jmp     38b
 
 99:    incq    wakeup_seq(%rdi)
        incl    cond_futex(%rdi)
@@ -298,7 +335,7 @@ __pthread_cond_timedwait:
        /* If requeue_pi is used the kernel performs the locking of the
           mutex. */
 41:    movq    16(%rsp), %rdi
-       testl   %r15d, %r15d
+       testb   %r15b, %r15b
        jnz     64f
 
        callq   __pthread_mutex_cond_lock
index 6194852..ec403cd 100644 (file)
@@ -136,19 +136,36 @@ __pthread_cond_wait:
        cmpl    $PI_BIT, %eax
        jne     61f
 
-90:
        movl    $(FUTEX_WAIT_REQUEUE_PI|FUTEX_PRIVATE_FLAG), %esi
        movl    $SYS_futex, %eax
        syscall
 
-       movl    $1, %r8d
-       cmpq    $-EAGAIN, %rax
-       je      91f
+       cmpl    $0, %eax
+       sete    %r8b
+
 #ifdef __ASSUME_REQUEUE_PI
        jmp     62f
 #else
-       cmpq    $-4095, %rax
-       jnae    62f
+       je      62f
+
+       /* When a futex syscall with FUTEX_WAIT_REQUEUE_PI returns
+          successfully, it has already locked the mutex for us and the
+          pi_flag (%r8b) is set to denote that fact.  However, if another
+          thread changed the futex value before we entered the wait, the
+          syscall may return an EAGAIN and the mutex is not locked.  We go
+          ahead with a success anyway since later we look at the pi_flag to
+          decide if we got the mutex or not.  The sequence numbers then make
+          sure that only one of the threads actually wake up.  We retry using
+          normal FUTEX_WAIT only if the kernel returned ENOSYS, since normal
+          and PI futexes don't mix.
+
+          Note that we don't check for EAGAIN specifically; we assume that the
+          only other error the futex function could return is EAGAIN since
+          anything else would mean an error in our function.  It is too
+          expensive to do that check for every call (which is  quite common in
+          case of a large number of threads), so it has been skipped.  */
+       cmpl    $-ENOSYS, %eax
+       jne     62f
 
 # ifndef __ASSUME_PRIVATE_FUTEX
        movl    $FUTEX_WAIT, %esi
@@ -161,7 +178,7 @@ __pthread_cond_wait:
 #else
        orl     %fs:PRIVATE_FUTEX, %esi
 #endif
-60:    xorl    %r8d, %r8d
+60:    xorb    %r8b, %r8b
        movl    $SYS_futex, %eax
        syscall
 
@@ -191,10 +208,10 @@ __pthread_cond_wait:
        jne     16f
 
        cmpq    24(%rsp), %r9
-       jbe     8b
+       jbe     19f
 
        cmpq    %rax, %r9
-       jna     8b
+       jna     19f
 
        incq    woken_seq(%rdi)
 
@@ -236,7 +253,7 @@ __pthread_cond_wait:
        /* If requeue_pi is used the kernel performs the locking of the
           mutex. */
 11:    movq    16(%rsp), %rdi
-       testl   %r8d, %r8d
+       testb   %r8b, %r8b
        jnz     18f
 
        callq   __pthread_mutex_cond_lock
@@ -253,6 +270,23 @@ __pthread_cond_wait:
        xorl    %eax, %eax
        jmp     14b
 
+       /* We need to go back to futex_wait.  If we're using requeue_pi, then
+          release the mutex we had acquired and go back.  */
+19:    testb   %r8b, %r8b
+       jz      8b
+
+       /* Adjust the mutex values first and then unlock it.  The unlock
+          should always succeed or else the kernel did not lock the mutex
+          correctly.  */
+       movq    16(%rsp), %rdi
+       callq   __pthread_mutex_cond_lock_adjust
+       movq    %rdi, %r8
+       xorl    %esi, %esi
+       callq   __pthread_mutex_unlock_usercnt
+       /* Reload cond_var.  */
+       movq    8(%rsp), %rdi
+       jmp     8b
+
        /* Initial locking failed.  */
 1:
 #if cond_lock != 0
@@ -331,69 +365,6 @@ __pthread_cond_wait:
 13:    movq    %r10, %rax
        jmp     14b
 
-91:
-.LcleanupSTART2:
-       /* FUTEX_WAIT_REQUEUE_PI returned EAGAIN.  We need to
-          call it again.  */
-       movq    8(%rsp), %rdi
-
-       /* Get internal lock.  */
-       movl    $1, %esi
-       xorl    %eax, %eax
-       LOCK
-#if cond_lock == 0
-       cmpxchgl %esi, (%rdi)
-#else
-       cmpxchgl %esi, cond_lock(%rdi)
-#endif
-       jz      92f
-
-#if cond_lock != 0
-       addq    $cond_lock, %rdi
-#endif
-       LP_OP(cmp) $-1, dep_mutex-cond_lock(%rdi)
-       movl    $LLL_PRIVATE, %eax
-       movl    $LLL_SHARED, %esi
-       cmovne  %eax, %esi
-       callq   __lll_lock_wait
-#if cond_lock != 0
-       subq    $cond_lock, %rdi
-#endif
-92:
-       /* Increment the cond_futex value again, so it can be used as a new
-          expected value. */
-       incl    cond_futex(%rdi)
-       movl    cond_futex(%rdi), %edx
-
-       /* Release internal lock.  */
-       LOCK
-#if cond_lock == 0
-       decl    (%rdi)
-#else
-       decl    cond_lock(%rdi)
-#endif
-       jz      93f
-
-#if cond_lock != 0
-       addq    $cond_lock, %rdi
-#endif
-       LP_OP(cmp) $-1, dep_mutex-cond_lock(%rdi)
-       movl    $LLL_PRIVATE, %eax
-       movl    $LLL_SHARED, %esi
-       cmovne  %eax, %esi
-       /* The call preserves %rdx.  */
-       callq   __lll_unlock_wake
-#if cond_lock != 0
-       subq    $cond_lock, %rdi
-#endif
-93:
-       /* Set the rest of SYS_futex args for FUTEX_WAIT_REQUEUE_PI. */
-       xorq    %r10, %r10
-       mov     dep_mutex(%rdi), %R8_LP
-       leaq    cond_futex(%rdi), %rdi
-       jmp     90b
-.LcleanupEND2:
-
        .size   __pthread_cond_wait, .-__pthread_cond_wait
 versioned_symbol (libpthread, __pthread_cond_wait, pthread_cond_wait,
                  GLIBC_2_3_2)
@@ -547,10 +518,6 @@ __condvar_cleanup1:
        .uleb128 .LcleanupEND-.LcleanupSTART
        .uleb128 __condvar_cleanup1-.LSTARTCODE
        .uleb128 0
-       .uleb128 .LcleanupSTART2-.LSTARTCODE
-       .uleb128 .LcleanupEND2-.LcleanupSTART2
-       .uleb128 __condvar_cleanup1-.LSTARTCODE
-       .uleb128 0
        .uleb128 .LcallUR-.LSTARTCODE
        .uleb128 .LENDCODE-.LcallUR
        .uleb128 0
diff --git a/nptl/tst-cond24.c b/nptl/tst-cond24.c
new file mode 100644 (file)
index 0000000..2eb2df1
--- /dev/null
@@ -0,0 +1,249 @@
+/* Verify that condition variables synchronized by PI mutexes don't hang.
+   Copyright (C) 2012 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 <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <time.h>
+
+#define THREADS_NUM 5
+#define MAXITER 50000
+
+static pthread_mutex_t mutex;
+static pthread_mutexattr_t mutex_attr;
+static pthread_cond_t cond;
+static pthread_t threads[THREADS_NUM];
+static int pending = 0;
+
+typedef void * (*threadfunc) (void *);
+
+void *
+thread_fun_timed (void *arg)
+{
+  int *ret = arg;
+  int rv, i;
+
+  printf ("Started thread_fun_timed[%d]\n", *ret);
+
+  for (i = 0; i < MAXITER / THREADS_NUM; i++)
+    {
+      rv = pthread_mutex_lock (&mutex);
+      if (rv)
+        {
+         printf ("pthread_mutex_lock: %s(%d)\n", strerror (rv), rv);
+         *ret = 1;
+         goto out;
+       }
+
+      while (!pending)
+       {
+         struct timespec ts;
+         clock_gettime(CLOCK_REALTIME, &ts);
+         ts.tv_sec += 20;
+         rv = pthread_cond_timedwait (&cond, &mutex, &ts);
+
+         /* There should be no timeout either.  */
+         if (rv)
+            {
+             printf ("pthread_cond_wait: %s(%d)\n", strerror (rv), rv);
+             *ret = 1;
+             goto out;
+           }
+       }
+
+      pending--;
+
+      rv = pthread_mutex_unlock (&mutex);
+      if (rv)
+        {
+         printf ("pthread_mutex_unlock: %s(%d)\n", strerror (rv), rv);
+         *ret = 1;
+         goto out;
+       }
+    }
+
+  *ret = 0;
+
+out:
+  return ret;
+}
+
+void *
+thread_fun (void *arg)
+{
+  int *ret = arg;
+  int rv, i;
+
+  printf ("Started thread_fun[%d]\n", *ret);
+
+  for (i = 0; i < MAXITER / THREADS_NUM; i++)
+    {
+      rv = pthread_mutex_lock (&mutex);
+      if (rv)
+        {
+         printf ("pthread_mutex_lock: %s(%d)\n", strerror (rv), rv);
+         *ret = 1;
+         goto out;
+       }
+
+      while (!pending)
+       {
+         rv = pthread_cond_wait (&cond, &mutex);
+
+         if (rv)
+            {
+             printf ("pthread_cond_wait: %s(%d)\n", strerror (rv), rv);
+             *ret = 1;
+             goto out;
+           }
+       }
+
+      pending--;
+
+      rv = pthread_mutex_unlock (&mutex);
+      if (rv)
+        {
+         printf ("pthread_mutex_unlock: %s(%d)\n", strerror (rv), rv);
+         *ret = 1;
+         goto out;
+       }
+    }
+
+  *ret = 0;
+
+out:
+  return ret;
+}
+
+static int
+do_test_wait (threadfunc f)
+{
+  int i;
+  int rv;
+  int counter = 0;
+  int retval[THREADS_NUM];
+
+  puts ("Starting test");
+
+  rv = pthread_mutexattr_init (&mutex_attr);
+  if (rv)
+    {
+      printf ("pthread_mutexattr_init: %s(%d)\n", strerror (rv), rv);
+      return 1;
+    }
+
+  rv = pthread_mutexattr_setprotocol (&mutex_attr, PTHREAD_PRIO_INHERIT);
+  if (rv)
+    {
+      printf ("pthread_mutexattr_setprotocol: %s(%d)\n", strerror (rv), rv);
+      return 1;
+    }
+
+  rv = pthread_mutex_init (&mutex, &mutex_attr);
+  if (rv)
+    {
+      printf ("pthread_mutex_init: %s(%d)\n", strerror (rv), rv);
+      return 1;
+    }
+
+  rv = pthread_cond_init (&cond, NULL);
+  if (rv)
+    {
+      printf ("pthread_cond_init: %s(%d)\n", strerror (rv), rv);
+      return 1;
+    }
+
+  for (i = 0; i < THREADS_NUM; i++)
+    {
+      retval[i] = i;
+      rv = pthread_create (&threads[i], NULL, f, &retval[i]);
+      if (rv)
+        {
+          printf ("pthread_create: %s(%d)\n", strerror (rv), rv);
+          return 1;
+        }
+    }
+
+  for (; counter < MAXITER; counter++)
+    {
+      rv = pthread_mutex_lock (&mutex);
+      if (rv)
+        {
+          printf ("pthread_mutex_lock: %s(%d)\n", strerror (rv), rv);
+          return 1;
+        }
+
+      if (!(counter % 100))
+       printf ("counter: %d\n", counter);
+      pending += 1;
+
+      rv = pthread_cond_signal (&cond);
+      if (rv)
+        {
+          printf ("pthread_cond_signal: %s(%d)\n", strerror (rv), rv);
+          return 1;
+        }
+
+      rv = pthread_mutex_unlock (&mutex);
+      if (rv)
+        {
+          printf ("pthread_mutex_unlock: %s(%d)\n", strerror (rv), rv);
+          return 1;
+        }
+    }
+
+  for (i = 0; i < THREADS_NUM; i++)
+    {
+      void *ret;
+      rv = pthread_join (threads[i], &ret);
+      if (rv)
+        {
+          printf ("pthread_join: %s(%d)\n", strerror (rv), rv);
+          return 1;
+        }
+      if (ret && *(int *)ret)
+        {
+         printf ("Thread %d returned with an error\n", i);
+         return 1;
+       }
+    }
+
+  return 0;
+}
+
+static int
+do_test (void)
+{
+  puts ("Testing pthread_cond_wait");
+  int ret = do_test_wait (thread_fun);
+  if (ret)
+    return ret;
+
+  puts ("Testing pthread_cond_timedwait");
+  return do_test_wait (thread_fun_timed);
+}
+
+#define TIMEOUT 10
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"