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/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.

Index: glibc-2.11.3/nptl/Makefile
===================================================================
--- glibc-2.11.3.orig/nptl/Makefile
+++ glibc-2.11.3/nptl/Makefile
@@ -205,7 +205,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-cond22 tst-cond23 \
+	tst-cond20 tst-cond21 tst-cond22 tst-cond23 tst-cond24 \
 	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 \
@@ -272,6 +272,7 @@ gen-as-const-headers = pthread-errnos.sy
 
 LDFLAGS-pthread.so = -Wl,--enable-new-dtags,-z,nodelete,-z,initfirst
 
+LDFLAGS-tst-cond24 = -lrt
 
 include ../Makeconfig
 
Index: glibc-2.11.3/nptl/sysdeps/unix/sysv/linux/x86_64/pthread_cond_timedwait.S
===================================================================
--- glibc-2.11.3.orig/nptl/sysdeps/unix/sysv/linux/x86_64/pthread_cond_timedwait.S
+++ glibc-2.11.3/nptl/sysdeps/unix/sysv/linux/x86_64/pthread_cond_timedwait.S
@@ -103,6 +103,8 @@ __pthread_cond_timedwait:
 	movq	%rsi, dep_mutex(%rdi)
 
 22:
+	xorb	%r15b, %r15b
+
 #ifndef __ASSUME_FUTEX_CLOCK_REALTIME
 #  ifdef PIC
 	cmpl	$0, __have_futex_clock_realtime(%rip)
@@ -184,18 +186,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.  */
@@ -242,7 +265,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)
@@ -292,7 +331,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: glibc-2.11.3/nptl/sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S
===================================================================
--- glibc-2.11.3.orig/nptl/sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S
+++ glibc-2.11.3/nptl/sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S
@@ -23,6 +23,7 @@
 #include <lowlevelcond.h>
 #include <tcb-offsets.h>
 #include <pthread-pi-defines.h>
+#include <pthread-errnos.h>
 
 #include <kernel-features.h>
 
@@ -137,12 +138,32 @@ __pthread_cond_wait:
 	movl	$SYS_futex, %eax
 	syscall
 
-	movl	$1, %r8d
+	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
@@ -155,7 +176,7 @@ __pthread_cond_wait:
 #else
 	orl	%fs:PRIVATE_FUTEX, %esi
 #endif
-60:	xorl	%r8d, %r8d
+60:	xorb	%r8b, %r8b
 	movl	$SYS_futex, %eax
 	syscall
 
@@ -185,10 +206,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)
 
@@ -230,7 +251,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
@@ -247,6 +268,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
Index: glibc-2.11.3/nptl/tst-cond24.c
===================================================================
--- /dev/null
+++ glibc-2.11.3/nptl/tst-cond24.c
@@ -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"
