more strangeness (Tim Wiess)
diff --git a/src/libthread/OpenBSD.c b/src/libthread/OpenBSD.c
index ea67884..80a5097 100644
--- a/src/libthread/OpenBSD.c
+++ b/src/libthread/OpenBSD.c
@@ -3,30 +3,18 @@
 
 #include <dlfcn.h>
 
-#define MIN(a,b)	((a) < (b) ? (a) : (b))
-#define MAX(a,b)	((a) > (b) ? (a) : (b))
-
-enum {
-	FD_READ = 1,
-	FD_WRITE,
-	FD_RDWR,
-	FDTBL_MAXSIZE = 128
+struct thread_tag {
+	struct thread_tag	*next;
+	spinlock_t		l;
+	volatile int		key;
+	void			*data;
 };
 
-struct fd_entry {
-	QLock	lock;
-	int	fd;
-	int	id;
-	short	rc;
-	short	wc;
-	short	ref;
-};
-
-static struct fd_entry fd_entry1 = { .fd -1 };
-static struct fd_entry *fd_table = nil;
-static spinlock_t fd_table_lock = { 0, 0, nil, 0 };
-static spinlock_t mlock = { 0, 0, nil, 0 };
-static spinlock_t dl_lock = { 0, 0, nil, 0 };
+static spinlock_t mlock;
+static spinlock_t dl_lock;
+static spinlock_t tag_lock;
+static struct thread_tag *thread_tag_store = nil;
+static uint nextkey = 0;
 
 void
 _thread_malloc_lock(void)
@@ -46,124 +34,8 @@
 }
 
 /*
- * Must set errno on failure because the return value
- * of _thread_fd_entry is propagated back to the caller
- * of the thread-wrapped libc function.
+ * for ld.so
  */
-static struct fd_entry *
-_thread_fd_lookup(int fd)
-{
-	struct fd_entry *t;
-	static int cursize;
-	int newsize;
-
-	if(fd >= FDTBL_MAXSIZE) {
-		errno = EBADF;
-		return nil;
-	}
-
-	/*
-	 * There are currently only a few libc functions using 
-	 * _thread_fd_*, which are rarely called by P9P programs.
-	 * So the contention for these locks is very small and so
-	 * far have usually been limited to a single fd. So
-	 * rather than malloc the fd_table everytime we just use
-	 * a single fd_entry until a lock request for another fd
-	 * comes in.
-	 */
-	if(fd_table == nil)
-		if(fd_entry1.fd == -1) {
-			fd_entry1.fd = fd;
-			return &fd_entry1;
-		} else if(fd_entry1.fd == fd)
-			return &fd_entry1;
-		else {
-			cursize = MAX(fd_entry1.fd, 16);
-			fd_table = malloc(cursize*sizeof(fd_table[0]));
-			if(fd_table == nil) {
-				errno = ENOMEM;
-				return nil;
-			}
-			memset(fd_table, 0, cursize*sizeof(fd_table[0]));
-			fd_table[fd_entry1.fd] = fd_entry1;
-		}
-	if(fd > cursize) {
-		newsize = MIN(cursize*2, FDTBL_MAXSIZE);
-		t = realloc(fd_table, newsize*sizeof(fd_table[0]));
-		if(t == nil) {
-			errno = ENOMEM;
-			return nil;
-		}
-		fd_table = t;
-		cursize = newsize; 
-		memset(fd_table, 0, cursize*sizeof(fd_table[0]));
-	}
-
-	return &fd_table[fd];
-}
-
-/*
- * Mutiple readers just share the lock by incrementing the read count.
- * Writers must obtain an exclusive lock.
- */
-int
-_thread_fd_lock(int fd, int type, struct timespec *time)
-{
-	struct fd_entry *fde;
-	int id;
-
-	_spinlock(&fd_table_lock);
-	fde = _thread_fd_lookup(fd);
-	if(fde == nil)
-		return -1;
-
-	if(type == FD_READ) {
-		if(fde->rc++ >= 1 && fde->wc == 0) {
-			_spinunlock(&fd_table_lock);
-			return 0;
-		}
-	} else
-		fde->wc++;
-	_spinunlock(&fd_table_lock);
-
-	/* handle recursion */
-	id = proc()->osprocid;
-	if(id == fde->id) {
-		fde->ref++;
-		return 0;
-	}
-
-	qlock(&fde->lock);
-	fde->id = id;
-	return 0;
-}
-
-void
-_thread_fd_unlock(int fd, int type)
-{
-	struct fd_entry *fde;
-	int id;
-
-	fde = _thread_fd_lookup(fd);
-	if(fde == nil) {
-		fprint(2, "_thread_fd_unlock: fd %d not in table!\n", fd);
-		return;
-	}
-
-	if(type == FD_READ && --fde->rc >= 1)
-		return;
-	else
-		fde->wc--;
-
-	id = proc()->osprocid;
-	if(id == fde->id && fde->ref > 0) {
-		fde->ref--;
-		return;
-	}
-	fde->id = 0;
-	qunlock(&fde->lock);
-}
-
 void
 _thread_dl_lock(int t)
 {
@@ -173,6 +45,97 @@
 		_spinlock(&dl_lock);
 }
 
+/*
+ * for libc
+ */
+static void
+_thread_tag_init(void **tag)
+{
+	struct thread_tag *t;
+
+	_spinlock(&tag_lock);
+	if(*tag == nil) {
+		t = malloc(sizeof (*t));
+		if(t != nil) {
+			memset(&t->l, 0, sizeof(t->l));
+			t->key = nextkey++;
+			*tag = t;
+		}
+	}
+	_spinunlock(&tag_lock);
+}
+
+void
+_thread_tag_lock(void **tag)
+{
+	struct thread_tag *t;
+
+	if(*tag == nil)
+		_thread_tag_init(tag);
+	t = *tag;
+	_spinlock(&t->l);
+}
+
+void
+_thread_tag_unlock(void **tag)
+{
+	struct thread_tag *t;
+
+	if(*tag == nil)
+		_thread_tag_init(tag);
+	t = *tag;
+	_spinunlock(&t->l);
+}
+
+static void *
+_thread_tag_insert(struct thread_tag *t, void *v)
+{
+	t->data = v;
+	t->next = thread_tag_store;
+	thread_tag_store = t;
+	return t;
+}
+
+static void *
+_thread_tag_lookup(struct thread_tag *tag, int size)
+{
+	struct thread_tag *t;
+	void *p;
+
+	_spinlock(&tag->l);
+	for(t = thread_tag_store; t != nil; t = t->next)
+		if(t->key == tag->key)
+			break;
+	if(t == nil) {
+		p = malloc(size);
+		if(p == nil) {
+			_spinunlock(&tag->l);
+			return nil;
+		}
+		_thread_tag_insert(tag, p);
+	}
+	_spinunlock(&tag->l);
+	return tag->data;
+}
+
+void *
+_thread_tag_storage(void **tag, void *storage, size_t n, void *err)
+{
+	struct thread_tag *t;
+	void *r;
+
+	if(*tag == nil)
+		_thread_tag_init(tag);
+	t = *tag;
+
+	r = _thread_tag_lookup(t, n);
+	if(r == nil)
+		r = err;
+	else
+		memcpy(r, storage, n);
+	return r;
+}
+
 void
 _pthreadinit(void)
 {