Subject: libpthread hacks
To: None <tech-kern@netbsd.org, tech-userlevel@netbsd.org>
From: Charles M. Hannum <abuse@spamalicious.com>
List: tech-userlevel
Date: 01/06/2005 03:46:32
--Boundary-00=_YSL3BTxtJ6Qm03U
Content-Type: text/plain;
  charset="us-ascii"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

Here's a set of hacks to libpthread, that:

* add more assertions, and
* implement klugy workarounds for the two problems I've mentioned recently.

The mlock()s made Azureus stable for ~64 hours on a memory-starved machine 
where it only ran for ~10-15 minutes before.  I'm expecting the changed test 
in pthread__sched_bulk() to fix the infinite loop and deadlock problems I've 
seen with Azureus as well.

In addition, I am no longer seeing the UI lock up in the Furthur network 
client.

Obviously the mlock()s are a kluge.  There is still also potential for upcall 
exhaustion.  But if you need a quick fix, this might do it.

--Boundary-00=_YSL3BTxtJ6Qm03U
Content-Type: text/x-diff;
  charset="us-ascii";
  name="pthread.diff"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
	filename="pthread.diff"

Index: pthread.c
===================================================================
RCS file: /cvsroot/src/lib/libpthread/pthread.c,v
retrieving revision 1.36
diff -u -r1.36 pthread.c
--- pthread.c	12 Aug 2004 10:54:13 -0000	1.36
+++ pthread.c	6 Jan 2005 03:41:21 -0000
@@ -51,6 +51,7 @@
 #include <unistd.h>
 #include <sys/param.h>
 #include <sys/sysctl.h>
+#include <sys/mman.h>
 
 #include <sched.h>
 #include "pthread.h"
@@ -134,6 +135,7 @@
 	int i, mib[2], ncpu;
 	size_t len;
 	extern int __isthreaded;
+	int ret;
 
 	mib[0] = CTL_HW;
 	mib[1] = HW_NCPU; 
@@ -164,6 +166,8 @@
 	pthread_attr_init(&pthread_default_attr);
 	PTQ_INIT(&pthread__allqueue);
 	PTQ_INIT(&pthread__deadqueue);
+	ret = mlock(&pthread__deadqueue, sizeof(pthread__deadqueue));
+	pthread__assert(ret == 0);
 	PTQ_INIT(&pthread__runqueue);
 	PTQ_INIT(&pthread__idlequeue);
 	for (i = 0; i < pthread__maxconcurrency; i++)
@@ -360,6 +364,8 @@
 				free(name);
 			return ret;
 		}
+		ret = mlock(newthread, sizeof(struct __pthread_st));
+		pthread__assert(ret == 0);
 	}
 
 	/* 2. Set up state. */
@@ -1077,6 +1083,8 @@
 	char buf[1024];
 	int len;
 
+	SDPRINTF(("(af)\n"));
+
 	/*
 	 * snprintf should not acquire any locks, or we could
 	 * end up deadlocked if the assert caller held locks.
Index: pthread_debug.h
===================================================================
RCS file: /cvsroot/src/lib/libpthread/pthread_debug.h,v
retrieving revision 1.8
diff -u -r1.8 pthread_debug.h
--- pthread_debug.h	14 Mar 2004 01:19:42 -0000	1.8
+++ pthread_debug.h	6 Jan 2005 03:41:21 -0000
@@ -93,7 +93,7 @@
 }
 
 #define PTHREAD__DEBUG_SHMKEY	(0x000f)
-#define PTHREAD__DEBUG_SHMSIZE	(1<<18)
+#define PTHREAD__DEBUG_SHMSIZE	(1<<20)
 
 struct	pthread_msgbuf {
 #define BUF_MAGIC	0x090976
Index: pthread_lock.c
===================================================================
RCS file: /cvsroot/src/lib/libpthread/pthread_lock.c,v
retrieving revision 1.12
diff -u -r1.12 pthread_lock.c
--- pthread_lock.c	14 Mar 2004 12:49:31 -0000	1.12
+++ pthread_lock.c	6 Jan 2005 03:41:21 -0000
@@ -192,6 +192,7 @@
 		 */
 		if (thread->pt_next != NULL) {
 			PTHREADD_ADD(PTHREADD_SPINPREEMPT);
+			pthread__assert(thread->pt_blockgen == thread->pt_unblockgen);
 			pthread__switch(thread, thread->pt_next);
 		}
 		/* try again */
@@ -224,6 +225,7 @@
 		/* See above. */
 		if (thread->pt_next != NULL) {
 			PTHREADD_ADD(PTHREADD_SPINPREEMPT);
+			pthread__assert(thread->pt_blockgen == thread->pt_unblockgen);
 			pthread__switch(thread, thread->pt_next);
 		}
 	}
@@ -255,6 +257,7 @@
 	
 	if ((thread->pt_spinlocks == 0) && (thread->pt_next != NULL)) {
 		PTHREADD_ADD(PTHREADD_SPINPREEMPT);
+//		pthread__assert(thread->pt_blockgen == thread->pt_unblockgen);
 		pthread__switch(thread, thread->pt_next);
 	}
 }
Index: pthread_run.c
===================================================================
RCS file: /cvsroot/src/lib/libpthread/pthread_run.c,v
retrieving revision 1.17
diff -u -r1.17 pthread_run.c
--- pthread_run.c	14 Mar 2004 01:19:42 -0000	1.17
+++ pthread_run.c	6 Jan 2005 03:41:24 -0000
@@ -265,7 +265,7 @@
 		next = qhead->pt_next;
 		pthread__assert(qhead->pt_spinlocks == 0);
 		pthread__assert(qhead->pt_type != PT_THREAD_UPCALL);
-		if (qhead->pt_unblockgen & 1)
+		if (qhead->pt_unblockgen != qhead->pt_blockgen)
 			qhead->pt_unblockgen++;
 		if (qhead->pt_type == PT_THREAD_NORMAL) {
 			qhead->pt_state = PT_STATE_RUNNABLE;
Index: pthread_sa.c
===================================================================
RCS file: /cvsroot/src/lib/libpthread/pthread_sa.c,v
retrieving revision 1.33
diff -u -r1.33 pthread_sa.c
--- pthread_sa.c	12 Oct 2004 22:17:56 -0000	1.33
+++ pthread_sa.c	6 Jan 2005 03:41:25 -0000
@@ -134,6 +134,7 @@
 		pthread__assert(t->pt_vpid == sas[1]->sa_cpu);
 		t->pt_blockedlwp = sas[1]->sa_id;
 		t->pt_blockgen += 2;
+//		pthread__assert(t->pt_blockgen <= t->pt_unblockgen + 2);
 		if (t->pt_cancel)
 			_lwp_wakeup(t->pt_blockedlwp);
 #ifdef PTHREAD__DEBUG
@@ -181,6 +182,8 @@
 		 * a signal will be on a run queue, and not in upcall limbo.
 		 */
 		si = arg;
+		SDPRINTF(("(up %p) signal %d %p\n", self,
+			     ev ? sas[1]->sa_id : -1, si));
 		if (ev)
 			pthread__signal(self, pthread__sa_id(sas[1]), si);
 		else
@@ -220,6 +223,7 @@
 			     pthread__uc_pc(UC(self->pt_next))));
 		self->pt_switchtouc = UC(self);
 		self->pt_switchto = self;
+		pthread__assert(self->pt_blockgen == self->pt_unblockgen);
 		pthread__switch(self, self->pt_next);
 		/*NOTREACHED*/
 		pthread__abort();
@@ -272,7 +276,11 @@
 		}
 		if (victim->pt_type == PT_THREAD_UPCALL) {
 			/* Case 1: Upcall. Must be resumed. */
-				SDPRINTF((" upcall"));
+			SDPRINTF((" upcall"));
+			if (victim->pt_spinlocks > 0) {
+				SDPRINTF((" lockholder %d",
+				    victim->pt_spinlocks));
+			}
 			resume = 1;
 			if (victim->pt_next) {
 				/*
@@ -348,11 +356,15 @@
 			pthread__assert(victim->pt_next == NULL);
 			victim->pt_next = nextint;
 			nextint = victim;
+			for (; victim; victim = victim->pt_next)
+				pthread__assert(victim->pt_next != nextint);
 		} else {
 			pthread__assert(victim->pt_parent == NULL);
 			pthread__assert(victim->pt_next == NULL);
 			victim->pt_next = nextsched;
 			nextsched = victim;
+			for (; victim; victim = victim->pt_next)
+				pthread__assert(victim->pt_next != nextsched);
 		}
 		SDPRINTF(("\n"));
 	}
@@ -454,6 +466,8 @@
 				if (victim->pt_switchto) {
 					/* We're done with you. */
 					SDPRINTF((" recyclable"));
+					pthread__assert(victim != prev);
+					pthread__assert(!prev || prev->pt_next);
 					/*
 					 * Clear trap context, which is
 					 * no longer useful.
@@ -580,6 +594,7 @@
 				     self, intqueue, PUC(intqueue), 
 				     pthread__uc_pc(UC(intqueue)), 
 				     pthread__uc_sp(UC(intqueue))));
+			pthread__assert(self->pt_blockgen == self->pt_unblockgen);
 			pthread__switch(self, intqueue);
 			SDPRINTF(("(rl %p) returned from chain\n",
 			    self));
@@ -597,6 +612,7 @@
 				     PUC(self->pt_next),
 				     pthread__uc_pc(UC(self->pt_next)), 
 				     pthread__uc_sp(UC(self->pt_next))));
+			pthread__assert(self->pt_blockgen == self->pt_unblockgen);
 			pthread__switch(self, self->pt_next);
 		}
 

--Boundary-00=_YSL3BTxtJ6Qm03U--