PERFORCE change 67972 for review

David Xu davidxu at FreeBSD.org
Fri Dec 31 06:46:51 PST 2004


http://perforce.freebsd.org/chv.cgi?CH=67972

Change 67972 by davidxu at davidxu_tiger on 2004/12/31 14:45:48

	Rewrite conditional variable code, full cancellation support, 
	this improves performance about 30%.

Affected files ...

.. //depot/projects/davidxu_thread/src/lib/libthread/thread/thr_cond.c#5 edit

Differences ...

==== //depot/projects/davidxu_thread/src/lib/libthread/thread/thr_cond.c#5 (text+ko) ====

@@ -1,36 +1,31 @@
 /*
- * Copyright (c) 1995 John Birrell <jb at cimlogic.com.au>.
+ * Copyright (c) 2005, David Xu <davidxu at freebsd.org>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
+ *    notice unmodified, this list of conditions, and the following
+ *    disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *	This product includes software developed by John Birrell.
- * 4. Neither the name of the author nor the names of any co-contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
  *
- * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: src/lib/libpthread/thread/thr_cond.c,v 1.51 2003/12/09 02:20:56 davidxu Exp $
+ * $FreeBSD$
  */
+
 #include <stdlib.h>
 #include <errno.h>
 #include <string.h>
@@ -42,12 +37,9 @@
 /*
  * Prototypes
  */
-static inline void	check_continuation(struct pthread *,
-			    struct pthread_cond *, pthread_mutex_t *);
-static int	init_static(struct pthread *thread,
-		    pthread_cond_t *cond);
+static int	init_static(struct pthread *thread, pthread_cond_t *cond);
 static int	cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex,
-		    const struct timespec *abstime);
+		    const struct timespec *abstime, int cancel);
 static int	cond_signal_common(pthread_cond_t *cond, int broadcast);
 
 /*
@@ -63,7 +55,6 @@
 __weak_reference(_pthread_cond_signal, pthread_cond_signal);
 __weak_reference(_pthread_cond_broadcast, pthread_cond_broadcast);
 
-
 int
 _pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr)
 {
@@ -81,7 +72,9 @@
 			 * Initialise the condition variable structure:
 			 */
 			umtx_init(&pcond->c_lock);
-			pcond->c_count = 0;
+			pcond->c_seqno = 0;
+			pcond->c_waiters = 0;
+			pcond->c_wakeups = 0;
 			pcond->c_flags = 0;
 			*cond = pcond;
 		}
@@ -96,7 +89,7 @@
 	int ret;
 
 	THR_LOCK_ACQUIRE(thread, &_cond_static_lock);
-	if (*cond == NULL)	
+	if (*cond == NULL)
 		ret = pthread_cond_init(cond, NULL);
 	else
 		ret = 0;
@@ -116,15 +109,12 @@
 		rval = EINVAL;
 	else {
 		/* Lock the condition variable structure: */
-		rval = UMTX_LOCK(&(*cond)->c_lock, curthread->tid);
-		if (rval)
-			return (rval);
-		while ((*cond)->c_count) {
-			rval = umtx_wake(&(*cond)->c_count, (*cond)->c_count);
-			if (rval <= 0)
-				break;
-			(*cond)->c_count -= rval;
+		THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock);
+		if ((*cond)->c_waiters + (*cond)->c_wakeups != 0) {
+			THR_LOCK_RELEASE(curthread, &(*cond)->c_lock);
+			return (EBUSY);
 		}
+
 		/*
 		 * NULL the caller's pointer now that the condition
 		 * variable has been destroyed:
@@ -133,7 +123,7 @@
 		*cond = NULL;
 
 		/* Unlock the condition variable structure: */
-		umtx_unlock(&cv->c_lock, curthread->tid);
+		THR_LOCK_RELEASE(curthread, &cv->c_lock);
 
 		/* Free the cond lock structure: */
 
@@ -148,56 +138,109 @@
 	return (rval);
 }
 
+struct cond_cancel_info
+{
+	pthread_mutex_t	*mutex;
+	pthread_cond_t	*cond;
+	long		seqno;
+};
+
+static void
+cond_cancel_handler(void *arg)
+{
+	struct pthread *curthread = _get_curthread();
+	struct cond_cancel_info *cci = (struct cond_cancel_info *)arg;
+	pthread_cond_t cv;
+
+	cv = *(cci->cond);
+	THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
+	if (cv->c_seqno != cci->seqno && cv->c_wakeups != 0) {
+		if (cv->c_waiters > 0) {
+			cv->c_seqno++;
+			umtx_wake((struct umtx *)&cv->c_seqno, 1);
+		} else
+			cv->c_wakeups--;
+	} else {
+		cv->c_waiters--;
+	}
+	THR_LOCK_RELEASE(curthread, &cv->c_lock);
+
+	_mutex_cv_lock(cci->mutex);
+}
+
 static int
 cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex,
-	const struct timespec *abstime)
+	const struct timespec *abstime, int cancel)
 {
 	struct pthread	*curthread = _get_curthread();
-	int	rval = 0;
+	struct cond_cancel_info cci;
+	pthread_cond_t  cv;
+	long		seq, oldseq;
+	int		oldcancel;
+	int		ret = 0;
 
 	/*
 	 * If the condition variable is statically initialized,
 	 * perform the dynamic initialization:
 	 */
 	if (__predict_false(*cond == NULL &&
-	    (rval = init_static(curthread, cond)) != 0))
-		return (rval);
+	    (ret = init_static(curthread, cond)) != 0))
+		return (ret);
+
+	cv = *cond;
+	THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
+	ret = _mutex_cv_unlock(mutex);
+	if (ret) {
+		THR_LOCK_RELEASE(curthread, &cv->c_lock);
+		return (ret);
+	}
+	oldseq = seq = cv->c_seqno;
+	cci.mutex = mutex;
+	cci.cond  = cond;
+	cci.seqno = oldseq;
 
-	if ((rval = UMTX_LOCK(&(*cond)->c_lock, curthread->tid)) == 0) {
-		rval = _mutex_cv_unlock(mutex);
-		if (__predict_false(rval)) {
-			umtx_unlock(&(*cond)->c_lock, curthread->tid);
-			return (rval);
+	cv->c_waiters++;
+	do {
+		if (cancel) {
+			THR_CLEANUP_PUSH(curthread, cond_cancel_handler, &cci);
+			THR_LOCK_RELEASE(curthread, &cv->c_lock);
+			oldcancel = _thr_cancel_enter(curthread);
+			ret = umtx_timedwait((struct umtx *)&cv->c_seqno,
+				seq, abstime);
+			_thr_cancel_leave(curthread, oldcancel);
+			THR_CLEANUP_POP(curthread, 0);
+		} else {
+			ret = umtx_timedwait((struct umtx *)&cv->c_seqno,
+				seq, abstime);
+		}
+		THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
+		seq = cv->c_seqno;
+		if (abstime != NULL && ret != 0) {
+			if (ret == EAGAIN || ret == EINTR)
+				ret = ETIMEDOUT;
+			break;
 		}
-
-		/* I don't think you may have INIT_MAX threads. */
-		if ((*cond)->c_count != INT_MAX)
-			(*cond)->c_count++;
-
-		rval = umtx_timedwait(&(*cond)->c_lock, curthread->tid,
-			&(*cond)->c_count, abstime);
-		if (rval == EINTR)
-			rval = 0;
-		else if (rval == EAGAIN) /* POSIX needs ETIMEDOUT */
-			rval = ETIMEDOUT;
-
 		/*
-		 * Note! we don't touch condition variable after resuming!
-		 * this makes it possible that waker can destroy the condition
-		 * variable after calling pthread_cond_broadcast(), please
-		 * see Single UNIX Specification Version 3 of
-		 * pthread_cond_destroy().
+		 * loop if we have never been told to wake up
+		 * or we lost a race.
 		 */
-		check_continuation(curthread, NULL, mutex);
-		_mutex_cv_lock(mutex);
+	} while (seq == oldseq || cv->c_wakeups == 0);
+	
+	if (seq != oldseq && cv->c_wakeups != 0) {
+		cv->c_wakeups--;
+		ret = 0;
+	} else {
+		cv->c_waiters--;
 	}
-	return (rval);
+	THR_LOCK_RELEASE(curthread, &cv->c_lock);
+	_mutex_cv_lock(mutex);
+	return (ret);
 }
 
 int
 _pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
 {
-	return cond_wait_common(cond, mutex, NULL);
+	return cond_wait_common(cond, mutex, NULL, 0);
 }
 
 __strong_reference(_pthread_cond_wait, _thr_cond_wait);
@@ -205,12 +248,9 @@
 int
 __pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
 {
-	struct pthread *curthread = _get_curthread();
 	int ret;
 
-	_thr_cancel_enter(curthread);
-	ret = cond_wait_common(cond, mutex, NULL);
-	_thr_cancel_leave(curthread, 1);
+	ret = cond_wait_common(cond, mutex, NULL, 1);
 	return (ret);
 }
 
@@ -218,30 +258,31 @@
 _pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
 		       const struct timespec * abstime)
 {
-	if (abstime == NULL)
+	if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
+	    abstime->tv_nsec >= 1000000000)
 		return (EINVAL);
-	return cond_wait_common(cond, mutex, abstime);
+
+	return cond_wait_common(cond, mutex, abstime, 0);
 }
 
+__strong_reference(_pthread_cond_timedwait, _thr_cond_timedwait);
+
 int
 __pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
 		       const struct timespec *abstime)
 {
-	struct pthread *curthread = _get_curthread();
-	int ret;
+	if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
+	    abstime->tv_nsec >= 1000000000)
+		return (EINVAL);
 
-	if (abstime == NULL)
-		return (EINVAL);
-	_thr_cancel_enter(curthread);
-	ret = _pthread_cond_timedwait(cond, mutex, abstime);
-	_thr_cancel_leave(curthread, 1);
-	return (ret);
+	return cond_wait_common(cond, mutex, abstime, 1);
 }
 
 static int
 cond_signal_common(pthread_cond_t *cond, int broadcast)
 {
 	struct pthread	*curthread = _get_curthread();
+	pthread_cond_t	cv;
 	int		rval = 0;
 
 	/*
@@ -252,30 +293,23 @@
 		(rval = init_static(curthread, cond)) != 0))
 		return (rval);
 
-	/* Lock the condition variable structure */
-	rval = UMTX_LOCK(&(*cond)->c_lock, curthread->tid);
-	if (__predict_false(rval))
-		return (rval);
-
-	while ((*cond)->c_count) {
-		/* umtx_wake returns number of threads resumed */
-		rval = umtx_wake(&(*cond)->c_count,
-			broadcast ? (*cond)->c_count : 1);
-		if (rval > 0) {
-			/* some threads were resumed. */
-			(*cond)->c_count -= rval;
-			rval = 0;
-		} else if (rval == 0) {
-			(*cond)->c_count = 0;	
-			break;
+	cv = *cond;
+	/* Lock the condition variable structure. */
+	THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
+	if (cv->c_waiters) {
+		if (!broadcast) {
+			cv->c_wakeups++;
+			cv->c_waiters--;
+			cv->c_seqno++;
+			umtx_wake((struct umtx *)&cv->c_seqno, 1);
 		} else {
-			rval = errno;
-			break;
+			cv->c_wakeups += cv->c_waiters;
+			cv->c_waiters = 0;
+			cv->c_seqno++;
+			umtx_wake((struct umtx *)&cv->c_seqno, INT_MAX);
 		}
-		if (!broadcast)
-			break;
 	}
-	umtx_unlock(&(*cond)->c_lock, curthread->tid);
+	THR_LOCK_RELEASE(curthread, &cv->c_lock);
 	return (rval);
 }
 
@@ -294,9 +328,3 @@
 }
 
 __strong_reference(_pthread_cond_broadcast, _thr_cond_broadcast);
-
-static inline void
-check_continuation(struct pthread *curthread, struct pthread_cond *cond,
-    pthread_mutex_t *mutex)
-{
-}


More information about the p4-projects mailing list