[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[PATCH] Improved condition variables design and implementation



This patch replaces my previous condition variable patch and provides
a significantly improved design and implementation.

The most important design improvement is that doing {broadcast(cv);
destroy(cv); free(cv);} is now safe.
This is accomplished by waiting in cond_destroy until all sleepers are
woken up.

The second design improvement is the handling of waiting until all
sleepers are woken up (used by destroy and nr_wakers overflow
handling).

The new mechanism works by adding 0x7fffffff to nr_sleepers.
If this causes the variable to overflow (as in signed overflow), a
futex wait is initiated.
In wait, nr_sleepers is decremented and if this causes an overflow
(0x7fffffff + n -> 0x7fffffff), a futex wake is performed.

This also allows to return immediately from _signal and _broadcast if
nr_sleepers == 0 without taking any locks.


The implementation fixes are extensive:
- nr_sleepers is now atomically modified everywhere
- the broadcast futex wake value is corrected
- the lock is actually dropped in __cond_reset
- lll_cond_broadcast is removed: lll_cond_wake handles both signal and
   broadcast
- [timed]wait no longer use pthread_mutex_unlock. They use instead
   the new inline __pthread_mutex_unlockable to check if they should
   fail and __pthread_mutex_unlock_nocheck to quickly unlock the mutex
- all slow jumps are forward jumps


Finally, the test tst-cond7.c is added and tests the safety of
{broadcast(cv); destroy(cv); free(cv);}.


diff --exclude-from=/home/ldb/src/linux-exclude -urNdp nptl/DESIGN-condvar.txt nptl_ldb/DESIGN-condvar.txt
--- nptl/DESIGN-condvar.txt	2002-11-02 23:15:06.000000000 +0100
+++ nptl_ldb/DESIGN-condvar.txt	2002-11-03 03:54:12.000000000 +0100
@@ -13,7 +13,7 @@ struct pthread_cond_t {
 
    unsigned int nr_wakers:
 
-         number of threads signalled to be woken up.
+         (misnamed) wakeup sequence number 
 
    unsigned int nr_sleepers:
 
@@ -21,67 +21,83 @@ struct pthread_cond_t {
 
 }
 
-#define ALL_THREADS (1 << (BITS_PER_LONG-1))
+#define ALL_THREADS ((1 << (BITS_PER_LONG-1)) - 1)
 
-cond_wait_timeout(cv, mutex, timeout):
+cond_wait_timeout(cv, mutex, timeout)
 {
-   lll_lock(cv->lock);
-   mutex_unlock(mutex);
+	unsigned our_nr_wakers;
+	if(err = mutex_unlockable(mutex))
+	       return err;
 
-   cv->nr_sleepers++;
-   for (;;) {
+	lll_lock(cv->lock);
+	atomic_increment(&cv->nr_sleepers);
 
-       if (cv->nr_wakers) {
-           cv->nr_wakers--;
-           break;
-       }
-       val = cv->nr_wakers;
+	our_nr_wakers = cv->nr_wakers;
+	lll_unlock(cv->unlock);
 
-       lll_unlock(cv->lock);
+	mutex_unlock(mutex);
+      
+	while((*(volatile int*)&cv->nr_wakers == our_nr_wakers))
+	{
+		ret = FUTEX WAIT (cv->nr_wakers, our_nr_wakers, timeout)
 
-       ret = FUTEX WAIT (cv->nr_wakers, val, timeout)
+		if (ret == TIMEOUT)
+			break;
+		ret = 0;			
+	}
 
-       lll_lock(cv->lock);
+	if(atomic_decrement_test_overflow(&cv->nr_sleepers))
+		FUTEX WAKE (cv->nr_sleepers, 1);
 
-       if (ret == TIMEOUT)
-         break;
-       ret = 0;
-   }
-   if (!--cv->nr_sleepers)
-     cv->nr_wakers = 0; /* no memory of wakeups */
-   lll_unlock(cv->lock);
-   mutex_lock(mutex);
+	mutex_lock(mutex);
 
-   return ret;
+	return ret;
 }
 
-cond_signal(cv)
+cond_wait_sleepers(cv)
 {
-   int do_wakeup = 0;
+	signed sleepers;
+	if(atomic_add_check_overflow(0x7fffffff, &cv->nr_sleepers))
+	{
+		while((sleepers = cv->nr_sleepers) != 0x7fffffff)
+			FUTEX WAIT (cv->nr_sleepers, sleepers);
+	}
+	cv->nr_sleepers = 0;		
+	cv->nr_wakers = 0;
+	lll_unlock(cv->lock);
+}
 
-   lll_lock(cv->lock);
-   if (cv->nr_sleepers) {
-     if (!++cv->nr_wakers) /* overflow detection for the nutcase */
-       cv->nr_wakers = ALL_THREADS;
-     do_wakeup = 1;
-   }
-   lll_unlock(cv->lock);
-   if (do_wakeup)
-     FUTEX WAKE (cv->nr_wakers, 1)
+__cond_reset(cv)
+{
+        FUTEX_WAKE (cv->nr_wakers, ALL_THREADS);
+	cond_wait_sleepers(cv);
 }
 
-cond_broadcast(cv)
+cond_destroy(cv)
 {
-   int do_wakeup = 0;
+	if(cv->nr_sleepers)
+	{
+		lll_lock(cv->lock);
+		++cv->nr_wakers;
+		cond_wait_sleepers(cv);
+	}
+}
 
-   lll_lock(cv->lock);
-   if (cv->nr_sleepers) {
-     cv->nr_wakers |= ALL_THREADS;
-     do_wakeup = 1;
-   }
-   lll_unlock(cv->lock);
-   if (do_wakeup)
-     FUTEX WAKE (cv->nr_wakers, ALL_THREADS);
+cond_wake(cv, num_threads)
+{
+	int do_wakeup = 0;
+
+	if (cv->nr_sleepers)
+	{	
+		lll_lock(cv->lock);
+		if((signed)++cv->nr_wakers < 0)
+			__cond_reset(cv);
+		else
+		{
+			FUTEX WAKE (cv->nr_wakers, num_threads)
+			lll_unlock(cv->lock);
+		}
+	}
 }
 
 weaknesses of the implementation:
diff --exclude-from=/home/ldb/src/linux-exclude -urNdp nptl/Makefile nptl_ldb/Makefile
--- nptl/Makefile	2002-11-02 23:14:31.000000000 +0100
+++ nptl_ldb/Makefile	2002-11-02 22:59:38.000000000 +0100
@@ -112,7 +112,7 @@ libpthread-nonshared = pthread_atfork
 tests = tst-mutex1 tst-mutex2 tst-mutex3 tst-mutex4 tst-mutex5 tst-mutex6 \
 	tst-mutex7 \
 	tst-spin1 tst-spin2 tst-spin3 \
-	tst-cond1 tst-cond2 tst-cond3 tst-cond4 tst-cond5 tst-cond6 \
+	tst-cond1 tst-cond2 tst-cond3 tst-cond4 tst-cond5 tst-cond6 tst-cond7 \
 	tst-rwlock1 tst-rwlock2 tst-rwlock3 tst-rwlock4 tst-rwlock5 \
 	tst-rwlock6 tst-rwlock7 \
 	tst-once1 tst-once2 \
diff --exclude-from=/home/ldb/src/linux-exclude -urNdp nptl/pthread_cond_broadcast.c nptl_ldb/pthread_cond_broadcast.c
--- nptl/pthread_cond_broadcast.c	2002-09-19 11:57:31.000000000 +0200
+++ nptl_ldb/pthread_cond_broadcast.c	2002-11-03 02:52:54.000000000 +0100
@@ -24,7 +24,8 @@ int
 pthread_cond_broadcast (cond)
      pthread_cond_t *cond;
 {
-  lll_cond_broadcast (cond);
+  if(cond->__data.__nr_sleepers)
+    lll_cond_broadcast (cond);
 
   return 0;
 }
diff --exclude-from=/home/ldb/src/linux-exclude -urNdp nptl/pthread_cond_destroy.c nptl_ldb/pthread_cond_destroy.c
--- nptl/pthread_cond_destroy.c	2002-09-19 11:57:31.000000000 +0200
+++ nptl_ldb/pthread_cond_destroy.c	2002-11-03 03:50:28.000000000 +0100
@@ -25,5 +25,11 @@ int
 pthread_cond_destroy (cond)
      pthread_cond_t *cond;
 {
-  return 0;
+	if(cond->__data.__nr_sleepers)
+	{
+		lll_mutex_lock(cond->__data.__lock);
+		++cond->__data.__nr_wakers;
+		lll_cond_wait_sleepers(cond);
+	}
+	return 0;
 }
diff --exclude-from=/home/ldb/src/linux-exclude -urNdp nptl/pthread_cond_signal.c nptl_ldb/pthread_cond_signal.c
--- nptl/pthread_cond_signal.c	2002-09-19 11:57:31.000000000 +0200
+++ nptl_ldb/pthread_cond_signal.c	2002-11-03 02:52:58.000000000 +0100
@@ -24,7 +24,8 @@ int
 pthread_cond_signal (cond)
      pthread_cond_t *cond;
 {
-  lll_cond_wake (cond);
+  if(cond->__data.__nr_sleepers)
+    lll_cond_wake (cond);
 
   return 0;
 }
diff --exclude-from=/home/ldb/src/linux-exclude -urNdp nptl/pthread_cond_timedwait.c nptl_ldb/pthread_cond_timedwait.c
--- nptl/pthread_cond_timedwait.c	2002-11-02 23:15:06.000000000 +0100
+++ nptl_ldb/pthread_cond_timedwait.c	2002-11-03 01:54:50.000000000 +0100
@@ -19,7 +19,8 @@
 
 #include "pthreadP.h"
 #include <lowlevellock.h>
-
+#include <atomic.h>
+#include "pthread_mutex_inlines.h"
 
 int
 pthread_cond_timedwait (cond, mutex, abstime)
@@ -34,30 +35,21 @@ pthread_cond_timedwait (cond, mutex, abs
      go to sleep.  */
   CANCELLATION_P (THREAD_SELF);
 
+  if(err = __pthread_mutex_unlockable(mutex))
+	  return err;
+
   /* Make sure the condition is modified atomically.  */
   lll_mutex_lock (cond->__data.__lock);
 
-  /* Release the mutex.  This might fail.  */
-  err = pthread_mutex_unlock (mutex);
-  if (__builtin_expect (err != 0, 0))
-    {
-      lll_mutex_unlock (cond->__data.__lock);
-      return err;
-    }
-
-  /* One more tread waiting.  */
-  ++cond->__data.__nr_sleepers;
+  /* One more thread waiting.  */
+  atomic_increment(&cond->__data.__nr_sleepers);
+  
+  /* Release the mutex.  This doesn't fail.  */
+  __pthread_mutex_unlock_nocheck (mutex);
 
   /* The actual conditional variable implementation.  */
   result = lll_cond_timedwait (cond, abstime);
 
-  if (--cond->__data.__nr_sleepers == 0)
-    /* Forget about the current wakeups now that they are done.  */
-    cond->__data.__nr_wakers = 0;
-
-  /* Lose the condvar lock.  */
-  lll_mutex_unlock (cond->__data.__lock);
-
   /* We have to get the mutex before returning.  */
   err = pthread_mutex_lock (mutex);
   if (err != 0)
diff --exclude-from=/home/ldb/src/linux-exclude -urNdp nptl/pthread_cond_wait.c nptl_ldb/pthread_cond_wait.c
--- nptl/pthread_cond_wait.c	2002-11-02 23:15:06.000000000 +0100
+++ nptl_ldb/pthread_cond_wait.c	2002-11-03 01:53:39.000000000 +0100
@@ -19,7 +19,8 @@
 
 #include "pthreadP.h"
 #include <lowlevellock.h>
-
+#include <atomic.h>
+#include "pthread_mutex_inlines.h"
 
 int
 pthread_cond_wait (cond, mutex)
@@ -32,30 +33,21 @@ pthread_cond_wait (cond, mutex)
      go to sleep.  */
   CANCELLATION_P (THREAD_SELF);
 
+  if(err = __pthread_mutex_unlockable(mutex))
+	  return err;
+  
   /* Make sure the condition is modified atomically.  */
   lll_mutex_lock (cond->__data.__lock);
 
-  /* Release the mutex.  This might fail.  */
-  err = pthread_mutex_unlock (mutex);
-  if (__builtin_expect (err != 0, 0))
-    {
-      lll_mutex_unlock (cond->__data.__lock);
-      return err;
-    }
-
-  /* One more tread waiting.  */
-  ++cond->__data.__nr_sleepers;
+  /* One more thread waiting.  */
+  atomic_increment(&cond->__data.__nr_sleepers);
+  
+  /* Release the mutex.  This doesn't fail.  */
+  __pthread_mutex_unlock_nocheck (mutex);
 
   /* The actual conditional variable implementation.  */
   lll_cond_wait (cond);
 
-  if (--cond->__data.__nr_sleepers == 0)
-    /* Forget about the current wakeups now that they are done.  */
-    cond->__data.__nr_wakers = 0;
-
-  /* Lose the condvar lock.  */
-  lll_mutex_unlock (cond->__data.__lock);
-
   /* We have to get the mutex before returning.  */
   err = pthread_mutex_lock (mutex);
 
diff --exclude-from=/home/ldb/src/linux-exclude -urNdp nptl/pthread_mutex_inlines.h nptl_ldb/pthread_mutex_inlines.h
--- nptl/pthread_mutex_inlines.h	1970-01-01 01:00:00.000000000 +0100
+++ nptl_ldb/pthread_mutex_inlines.h	2002-11-03 01:52:48.000000000 +0100
@@ -0,0 +1,61 @@
+/* Copyright (C) 2002 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper redhat com>, 2002.
+
+   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.  */
+
+#ifndef __PTHREAD_MUTEX_INLINES_H
+#define __PTHREAD_MUTEX_INLINES_H
+
+#include <errno.h>
+#include "pthreadP.h"
+#include <lowlevellock.h>
+
+static inline int
+__pthread_mutex_unlockable (pthread_mutex_t *mutex)
+{
+  switch (__builtin_expect (mutex->__data.__kind, PTHREAD_MUTEX_TIMED_NP))
+    {
+    case PTHREAD_MUTEX_RECURSIVE_NP:
+      /* Recursive mutex.  */
+      if (mutex->__data.__owner != THREAD_SELF)
+	return EPERM;
+
+      return 0;
+
+    case PTHREAD_MUTEX_ERRORCHECK_NP:
+      /* Error checking mutex.  */
+      if (mutex->__data.__owner != THREAD_SELF
+	  || ! lll_mutex_islocked (mutex->__data.__lock))
+	return EPERM;
+
+      return 0;
+
+    default:
+      return 0;
+    }
+}
+
+static inline void
+__pthread_mutex_unlock_nocheck(pthread_mutex_t *mutex)
+{
+  mutex->__data.__owner = NULL;
+      
+  /* Unlock.  */
+  lll_mutex_unlock (mutex->__data.__lock);
+}
+
+#endif
diff --exclude-from=/home/ldb/src/linux-exclude -urNdp nptl/sysdeps/unix/sysv/linux/i386/i486/lowlevelcond.S nptl_ldb/sysdeps/unix/sysv/linux/i386/i486/lowlevelcond.S
--- nptl/sysdeps/unix/sysv/linux/i386/i486/lowlevelcond.S	2002-11-02 23:15:06.000000000 +0100
+++ nptl_ldb/sysdeps/unix/sysv/linux/i386/i486/lowlevelcond.S	2002-11-03 03:24:04.000000000 +0100
@@ -50,44 +50,49 @@ __lll_cond_wait:
 	pushl	%ebx
 
 	xorl	%esi, %esi
+	xorl	%ecx, %ecx	/* movl $FUTEX_WAIT, %ecx */
 
+	/* get nr_wakers */
 	leal	cond_nr_wakers(%eax), %ebx
+	movl	(%ebx), %edx
 
-4:	movl	(%ebx), %edx
-	testl	%edx, %edx
-	jne	1f
-
+	/* drop lock */
 	LOCK
 	decl	cond_lock-cond_nr_wakers(%ebx)
-	jne	2f
-
-3:	xorl	%ecx, %ecx
-	movl	$SYS_futex, %eax
+	jne	3f
+	
+1:	movl	$SYS_futex, %eax
 	int	$0x80
 
-	movl	$1, %eax
-	LOCK
-	xaddl	%eax, cond_lock-cond_nr_wakers(%ebx)
-	testl	%eax, %eax
-	je	4b
-
-	leal	cond_lock-cond_nr_wakers(%ebx), %ecx
-	/* Preserves %ebx, %edx, %edi, %esi.  */
-	call	__lll_mutex_lock_wait
-	jmp	4b
+	/* loop if no wakeup */
+	cmpl	%edx, (%ebx)
+	je	1b
 
-1:	decl	(%ebx)
+	/* decrement nr_sleepers */
+5:	LOCK
+	decl	cond_nr_sleepers-cond_nr_wakers(%ebx)
+	jo	4f
 
-	popl	%ebx
+2:	popl	%ebx
 	popl	%esi
 	ret
 
-2:	leal	cond_lock-cond_nr_wakers(%ebx), %eax
+	
+	/* drop lock: wakeup */
+3:	leal	cond_lock-cond_nr_wakers(%ebx), %eax
 	/* Preserves %ebx, %ecx, %edx, %edi, %esi.  */
 	call	__lll_mutex_unlock_wake
-	jmp	3b
-	.size	__lll_cond_wait,.-__lll_cond_wait
+	jmp	1b
 
+	/* decrement nr_sleepers: wakeup */
+4:	addl	$cond_nr_sleepers-cond_nr_wakers, %ebx
+	movl	$FUTEX_WAKE, %ecx
+	movl	%ecx, %edx	/* movl $1, %edx */
+	movl	$SYS_futex, %eax
+	int	$0x80
+	jmp 2b
+	.size	__lll_cond_wait,.-__lll_cond_wait	
+		
 
 	.global	__lll_cond_timedwait
 	.type	__lll_cond_timedwait,@function
@@ -96,7 +101,7 @@ __lll_cond_wait:
 __lll_cond_timedwait:
 	/* Check for a valid timeout value.  */
 	cmpl	$1000000000, 4(%edx)
-	jae	1f
+	jae	7f
 
 	pushl	%ebp
 	pushl	%edi
@@ -106,19 +111,19 @@ __lll_cond_timedwait:
 	/* Stack frame for the timespec and timeval structs.  */
 	subl	$8, %esp
 
+	/* get nr_wakers */
 	leal	cond_nr_wakers(%eax), %ebp	/* cond */
 	movl	%edx, %edi			/* timeout */
+	
+	movl	(%ebp), %esi
 
-9:	movl	(%ebp), %esi
-	testl	%esi, %esi
-	jne	5f
-
+	/* drop lock */	
 	LOCK
 	decl	cond_lock-cond_nr_wakers(%ebp)
-	jne	6f
+	jne	3f
 
 	/* Get current time.  */
-7:	movl	%esp, %ebx
+1:	movl	%esp, %ebx
 	xorl	%ecx, %ecx
 	movl	$SYS_gettimeofday, %eax
 	int	$0x80
@@ -131,11 +136,11 @@ __lll_cond_timedwait:
 	movl	4(%edi), %edx
 	subl	(%esp), %ecx
 	subl	%eax, %edx
-	jns	3f
+	jns	8f
 	addl	$1000000000, %edx
 	decl	%ecx
-3:	testl	%ecx, %ecx
-	js	4f		/* Time is already up.  */
+8:	testl	%ecx, %ecx
+	js	6f		/* Time is already up.  */
 
 	movl	%ecx, (%esp)	/* Store relative timeout.  */
 	movl	%edx, 4(%esp)
@@ -146,22 +151,21 @@ __lll_cond_timedwait:
 	movl	$SYS_futex, %eax
 	int	$0x80
 
-	movl	%eax, %edx
-
-	movl	$1, %eax
-	LOCK
-	xaddl	%eax, cond_lock-cond_nr_wakers(%ebp)
-	testl	%eax, %eax
-	jne	8f
+	cmpl	$-ETIMEDOUT, %eax
+	je	6f
 
-	cmpl	$-ETIMEDOUT, %edx
-	jne	9b
+	movl	%edx, %esi
+	
+	cmpl	%edx, (%ebx)
+	je	1b
 
-4:	movl	$ETIMEDOUT, %eax
-	jmp	2f
+	xorl	%eax, %eax	
 
-5:	decl	(%ebp)
-	xorl	%eax, %eax
+5:	
+	/* decrement nr_sleepers */
+	LOCK
+	decl	cond_nr_sleepers-cond_nr_wakers(%ebp)
+	jo	4f
 
 2:	addl	$8, %esp
 	popl	%ebx
@@ -170,20 +174,30 @@ __lll_cond_timedwait:
 	popl	%ebp
 	ret
 
-6:	leal	cond_lock-cond_nr_wakers(%ebp), %eax
+	
+6:	movl	$ETIMEDOUT, %eax
+	jmp	5b
+
+	/* drop lock: wakeup */
+3:	leal	cond_lock-cond_nr_wakers(%ebp), %eax
 	/* Preserves %ebx, %ecx, %edx, %edi, %esi.  */
 	call	__lll_mutex_unlock_wake
-	jmp	7b
-
-8:	leal	cond_lock-cond_nr_wakers(%ebp), %ecx
-	/* Preserves %ebx, %edx, %edi, %esi.  */
-	call	__lll_mutex_lock_wait
-	jmp	5b
+	jmp	1b
 
-1:	movl	$EINVAL, %eax
+7:	movl	$EINVAL, %eax
 	ret
-	.size	__lll_cond_timedwait,.-__lll_cond_timedwait
 
+	/* decrement nr_sleepers: wakeup */
+4:	movl	%eax, (%esp)
+	xorl	%esi, %esi
+	leal	cond_nr_sleepers-cond_nr_wakers(%ebp), %ebx
+	movl	$FUTEX_WAKE, %ecx
+	movl	%ecx, %edx	/* movl $1, %edx */
+	movl	$SYS_futex, %eax
+	int	$0x80
+	movl	(%esp), %eax
+	jmp	2b
+	.size	__lll_cond_timedwait,.-__lll_cond_timedwait	
 
 	.global	__lll_cond_wake
 	.type	__lll_cond_wake,@function
@@ -192,88 +206,100 @@ __lll_cond_timedwait:
 __lll_cond_wake:
 	pushl	%esi
 	pushl	%ebx
+	xorl	%esi, %esi
 
-	movl	%eax, %ebx
+	/* caller sets %ecx = cond_nr_wakers(cond) */
+	movl	%ecx, %ebx
 
-	movl	$1, %eax
+	/* take lock - caller sets %eax = 1 */
 	LOCK
-	xaddl	%eax, (%ebx)
+	xaddl	%eax, cond_lock-cond_nr_wakers(%ebx)
 	testl	%eax, %eax
-	jne	1f
-
-2:	leal	cond_nr_wakers(%ebx), %ebx
-	cmpl	$0, cond_nr_sleepers-cond_nr_wakers(%ebx)
-	je	3f
+	jne	3f
 
+1:	movl	$FUTEX_WAKE, %ecx
+	movl	$SYS_futex, %eax
+	
+	/* increment nr_wakers, do the reset procedure if it gets big */
 	incl	(%ebx)
-	jz	5f
+	js	5f
 
-6:	movl	$FUTEX_WAKE, %ecx
-	xorl	%esi, %esi
-	movl	%ecx, %edx	/* movl $1, %edx */
-	movl	$SYS_futex, %eax
+	/* wake sleepers */
 	int	$0x80
 
-3:	LOCK
+	/* drop lock */
+	LOCK
 	decl	cond_lock-cond_nr_wakers(%ebx)
-	je,pt	4f
-
-	leal	cond_lock-cond_nr_wakers(%ebx), %eax
-	call	__lll_mutex_unlock_wake
+	jne	4f
 
-4:	popl	%ebx
+2:	popl	%ebx
 	popl	%esi
 	ret
-
-1:	movl	%ebx, %ecx
+	
+3:	subl	$cond_nr_wakers-cond_lock, %ecx
 	call	__lll_mutex_lock_wait
+	jmp	1b
+
+	
+4:	leal	cond_lock-cond_nr_wakers(%ebx), %eax
+	call	__lll_mutex_unlock_wake
 	jmp	2b
 
-5:	movl	$0x80000000, (%ebx)
-	jmp	6b
-	.size	__lll_cond_wake,.-__lll_cond_wake
+5:
+	/* wake everyone */
+	movl	$0x7fffffff, %edx	
+	int	$0x80
 
+	addl	$cond_nr_sleepers-cond_nr_wakers, %ebx
+	/* jump inside __lll_cond_wait_sleepers */
+	jmp	1f
+	.size	__lll_cond_wake,.-__lll_cond_wake
 
-	.global	__lll_cond_broadcast
-	.type	__lll_cond_broadcast,@function
-	.hidden	__lll_cond_broadcast
-	.align	16
-__lll_cond_broadcast:
-	pushl	%esi
-	pushl	%ebx
 
-	movl	%eax, %ebx
-	movl	$0x8000000, %edx
+	.global	__lll_cond_wait_sleepers
+	.type	__lll_cond_wait_sleepers,@function
+	.hidden	__lll_cond_wait_sleepers
+	.align	16	
+__lll_cond_wait_sleepers:
+	pushl	%esi	
+	pushl	%ebx	
+	leal	cond_nr_sleepers(%eax), %ebx
+	xorl	%esi, %esi
 
-	movl	$1, %eax
+	/* move nr_sleepers into want-wakeup area */
+1:	movl	$0x7fffffff, %edx
 	LOCK
-	xaddl	%eax, (%ebx)
-	testl	%eax, %eax
-	jne	1f
-
-2:	leal	cond_nr_wakers(%ebx), %ebx
-	cmpl	$0, cond_nr_sleepers-cond_nr_wakers(%ebx)
-	je	3f
+	xaddl	%edx, (%ebx)
+	jno	3f
+	addl	$0x7fffffff, %edx
 
-	orl	%edx, (%ebx)
+	xorl	%ecx, %ecx	/* movl $FUTEX_WAIT, %ecx */	
 
-6:	movl	$FUTEX_WAKE, %ecx
-	xorl	%esi, %esi
-	movl	$SYS_futex, %eax
+	/* wait until nr_sleepers < 0 */
+2:	movl	$SYS_futex, %eax
 	int	$0x80
 
-3:	LOCK
-	decl	cond_lock-cond_nr_wakers(%ebx)
-	je,pt	4f
+	movl	(%ebx), %edx
+	cmpl	$0x7fffffff, %edx
+	jne	2b
 
-	leal	cond_lock-cond_nr_wakers(%ebx), %eax
-	call	__lll_mutex_unlock_wake
+	/* set nr_sleepers and nr_wakers to 0 */
+3:	xorl	%eax, %eax
+	movl	%eax, cond_nr_wakers-cond_nr_sleepers(%ebx)
+	movl	%eax, (%ebx)
+
+	/* drop lock */
+	LOCK
+	decl	cond_lock-cond_nr_sleepers(%ebx)
+	jne	5f
 
 4:	popl	%ebx
 	popl	%esi
 	ret
 
-1:	movl	%ebx, %ecx
-	call	__lll_mutex_lock_wait
-	jmp	2b
-	.size	__lll_cond_broadcast,.-__lll_cond_broadcast
+	
+5:	leal	cond_lock-cond_nr_sleepers(%ebx), %eax
+	call	__lll_mutex_unlock_wake
+	jmp 4b	
+	.size	__lll_cond_wait_sleepers,.-__lll_cond_wait_sleepers
+	
diff --exclude-from=/home/ldb/src/linux-exclude -urNdp nptl/sysdeps/unix/sysv/linux/i386/lowlevellock.h nptl_ldb/sysdeps/unix/sysv/linux/i386/lowlevellock.h
--- nptl/sysdeps/unix/sysv/linux/i386/lowlevellock.h	2002-09-29 08:12:20.000000000 +0200
+++ nptl_ldb/sysdeps/unix/sysv/linux/i386/lowlevellock.h	2002-11-03 03:13:24.000000000 +0100
@@ -237,9 +237,9 @@ extern void __lll_cond_wait (pthread_con
 extern int __lll_cond_timedwait (pthread_cond_t *cond,
 				 const struct timespec *abstime)
      __attribute ((regparm (2))) attribute_hidden;
-extern void __lll_cond_wake (pthread_cond_t *cond)
-     __attribute ((regparm (1))) attribute_hidden;
-extern void __lll_cond_broadcast (pthread_cond_t *cond)
+extern void __lll_cond_wake (unsigned const_one, unsigned threads, void *cond_nr_waiters)
+     __attribute ((regparm (3))) attribute_hidden;
+extern void __lll_cond_wait_sleepers (pthread_cond_t *cond)
      __attribute ((regparm (1))) attribute_hidden;
 
 
@@ -248,9 +248,11 @@ extern void __lll_cond_broadcast (pthrea
 #define lll_cond_timedwait(cond, abstime) \
   __lll_cond_timedwait (cond, abstime)
 #define lll_cond_wake(cond) \
-  __lll_cond_wake (cond)
+  __lll_cond_wake (1, 1, &cond->__data.__nr_wakers)
 #define lll_cond_broadcast(cond) \
-  __lll_cond_broadcast (cond)
+  __lll_cond_wake (1, 0x7fffffff, &cond->__data.__nr_wakers)
+#define lll_cond_wait_sleepers(cond) \
+  __lll_cond_wait_sleepers (cond)
 
 
 #endif	/* lowlevellock.h */
diff --exclude-from=/home/ldb/src/linux-exclude -urNdp nptl/tst-cond7.c nptl_ldb/tst-cond7.c
--- nptl/tst-cond7.c	1970-01-01 01:00:00.000000000 +0100
+++ nptl_ldb/tst-cond7.c	2002-11-03 03:33:27.000000000 +0100
@@ -0,0 +1,113 @@
+/* Copyright (C) 2002 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper redhat com>, 2002.
+
+   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 <errno.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+
+
+static pthread_cond_t* pcond;
+static pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
+
+
+static void *
+tf (void *p)
+{
+  int err;
+
+  puts ("child: waiting");
+
+  pthread_cond_wait (pcond, &mut);
+
+  puts ("child: unlock");
+
+  err = pthread_mutex_unlock (&mut);
+  if (err != 0)
+    error (EXIT_FAILURE, err, "child: cannot unlock");
+
+  puts ("child: done");
+
+  return NULL;
+}
+
+
+static int
+do_test (void)
+{
+  pthread_t th;
+  int err;
+  size_t page_size;
+
+  page_size = sysconf(_SC_PAGESIZE);
+  
+  pcond = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+  pthread_cond_init(pcond, NULL);
+
+  printf ("&cond = %p\n&mut = %p\n", pcond, &mut);
+
+  puts ("parent: get mutex");
+
+  err = pthread_mutex_lock (&mut);
+  if (err != 0)
+    error (EXIT_FAILURE, err, "parent: cannot get mutex");
+
+  puts ("parent: create child");
+
+  err = pthread_create (&th, NULL, tf, NULL);
+  if (err != 0)
+    error (EXIT_FAILURE, err, "parent: cannot create thread");
+
+  puts ("parent: reget mutex");
+  
+  err = pthread_mutex_lock (&mut);
+  if (err != 0)
+    error (EXIT_FAILURE, err, "parent: cannot reget mutex");
+
+  err = pthread_mutex_unlock (&mut);
+  
+  puts ("parent: broadcast");
+
+  err = pthread_cond_broadcast (pcond);
+  if (err != 0)
+    error (EXIT_FAILURE, err, "parent: cannot broadcast");
+
+  pthread_cond_destroy(pcond);
+
+  /* if this causes a segfault in the child, libpthread is broken */
+  munmap(pcond, page_size);
+
+
+  puts ("joining");
+  pthread_join(th, NULL);
+  
+  puts ("done");
+
+  return 0;
+}
+
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"

Attachment: pgp00034.pgp
Description: PGP signature


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]