about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--nptl/ChangeLog7
-rw-r--r--nptl/sysdeps/unix/sysv/linux/i386/i486/lowlevellock.S6
-rw-r--r--nptl/sysdeps/unix/sysv/linux/lowlevellock.c23
-rw-r--r--nptl/sysdeps/unix/sysv/linux/x86_64/lowlevellock.S6
4 files changed, 29 insertions, 13 deletions
diff --git a/nptl/ChangeLog b/nptl/ChangeLog
index bcec5e9651..c5019e49f1 100644
--- a/nptl/ChangeLog
+++ b/nptl/ChangeLog
@@ -1,5 +1,12 @@
 2007-11-08  Ulrich Drepper  <drepper@redhat.com>
 
+	[BZ #5240]
+	* sysdeps/unix/sysv/linux/lowlevellock.c (__lll_timedlock_wait):
+	If we time out, try one last time to lock the futex to avoid
+	losing a wakeup signal.
+	* sysdeps/unix/sysv/linux/i386/i486/lowlevellock.S: Likewise.
+	* sysdeps/unix/sysv/linux/x86_64/lowlevellock.S: Likewise.
+
 	[BZ #5245]
 	* sysdeps/pthread/createthread.c (do_clone): Translate clone error
 	if necessary.
diff --git a/nptl/sysdeps/unix/sysv/linux/i386/i486/lowlevellock.S b/nptl/sysdeps/unix/sysv/linux/i386/i486/lowlevellock.S
index 745ab91239..6d3943b0eb 100644
--- a/nptl/sysdeps/unix/sysv/linux/i386/i486/lowlevellock.S
+++ b/nptl/sysdeps/unix/sysv/linux/i386/i486/lowlevellock.S
@@ -203,7 +203,7 @@ __lll_timedlock_wait:
 	addl	$1000000000, %edx
 	subl	$1, %ecx
 4:	testl	%ecx, %ecx
-	js	5f		/* Time is already up.  */
+	js	9f		/* Time is already up.  */
 
 	/* Store relative timeout.  */
 	movl	%ecx, (%esp)
@@ -271,6 +271,10 @@ __lll_timedlock_wait:
 
 5:	movl	$ETIMEDOUT, %eax
 	jmp	6b
+
+9:	movl	$-ETIMEDOUT, %ecx
+	movl	$2, %edx
+	jmp	8b
 	cfi_endproc
 	.size	__lll_timedlock_wait,.-__lll_timedlock_wait
 #endif
diff --git a/nptl/sysdeps/unix/sysv/linux/lowlevellock.c b/nptl/sysdeps/unix/sysv/linux/lowlevellock.c
index f0e42957c2..c2cdf3a1e4 100644
--- a/nptl/sysdeps/unix/sysv/linux/lowlevellock.c
+++ b/nptl/sysdeps/unix/sysv/linux/lowlevellock.c
@@ -59,10 +59,10 @@ __lll_timedlock_wait (int *futex, const struct timespec *abstime, int private)
   if (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000)
     return EINVAL;
 
+  struct timespec rt;
   do
     {
       struct timeval tv;
-      struct timespec rt;
 
       /* Get the current time.  */
       (void) __gettimeofday (&tv, NULL);
@@ -76,18 +76,21 @@ __lll_timedlock_wait (int *futex, const struct timespec *abstime, int private)
 	  --rt.tv_sec;
 	}
 
-      /* Already timed out?  */
-      if (rt.tv_sec < 0)
-	return ETIMEDOUT;
+      /* If timed out do not go to sleep.  */
+      if (__builtin_expect (rt.tv_sec >= 0, 1))
+	{
+	  /* Wait.  */
+	  int oldval = atomic_compare_and_exchange_val_acq (futex, 2, 1);
+	  if (oldval != 0)
+	    lll_futex_timed_wait (futex, 2, &rt, private);
+	}
 
-      /* Wait.  */
-      int oldval = atomic_compare_and_exchange_val_acq (futex, 2, 1);
-      if (oldval != 0)
-	lll_futex_timed_wait (futex, 2, &rt, private);
+      if (atomic_compare_and_exchange_bool_acq (futex, 2, 0) == 0)
+	return 0;
     }
-  while (atomic_compare_and_exchange_bool_acq (futex, 2, 0) != 0);
+  while (rt.tv_sec >= 0);
 
-  return 0;
+  return ETIMEDOUT;
 }
 
 
diff --git a/nptl/sysdeps/unix/sysv/linux/x86_64/lowlevellock.S b/nptl/sysdeps/unix/sysv/linux/x86_64/lowlevellock.S
index 7065cfac32..a5ad88c199 100644
--- a/nptl/sysdeps/unix/sysv/linux/x86_64/lowlevellock.S
+++ b/nptl/sysdeps/unix/sysv/linux/x86_64/lowlevellock.S
@@ -199,14 +199,16 @@ __lll_timedlock_wait:
 	addq	$1000000000, %rsi
 	decq	%rdi
 4:	testq	%rdi, %rdi
-	js	5f		/* Time is already up.  */
+	movq	$-ETIMEDOUT, %rcx
+	movl	$2, %edx
+	js	8f		/* Time is already up.  */
 
 	/* Futex call.  */
 	movq	%rdi, (%rsp)	/* Store relative timeout.  */
 	movq	%rsi, 8(%rsp)
 
 	movl	$1, %eax
-	movl	$2, %edx
+				/* NB: $edx has been loaded early.  */
 	LOCK
 	cmpxchgl %edx, (%r12)