? o
? old
Index: pthread_int.h
===================================================================
RCS file: /cvsroot/src/lib/libpthread/pthread_int.h,v
retrieving revision 1.87
diff -u -p -u -r1.87 pthread_int.h
--- pthread_int.h	3 Nov 2012 23:42:27 -0000	1.87
+++ pthread_int.h	15 Nov 2012 17:41:25 -0000
@@ -148,7 +148,10 @@ struct	__pthread_st {
 
 	/* Thread-specific data.  Large so it sits close to the end. */
 	int		pt_havespecific;
-	void		*pt_specific[PTHREAD_KEYS_MAX];
+	struct pt_specific {
+		void *pts_value;
+		PTQ_ENTRY(pt_specific) pts_next;
+	} pt_specific[PTHREAD_KEYS_MAX];
 
 	/*
 	 * Context for thread creation.  At the end as it's cached
@@ -295,6 +298,7 @@ char	*pthread__getenv(const char *) PTHR
 __dead void	pthread__cancelled(void) PTHREAD_HIDE;
 void	pthread__mutex_deferwake(pthread_t, pthread_mutex_t *) PTHREAD_HIDE;
 int	pthread__checkpri(int) PTHREAD_HIDE;
+int	pthread__add_specific(pthread_t, pthread_key_t, const void *) PTHREAD_HIDE;
 
 #ifndef pthread__smt_pause
 #define	pthread__smt_pause()	/* nothing */
Index: pthread_specific.c
===================================================================
RCS file: /cvsroot/src/lib/libpthread/pthread_specific.c,v
retrieving revision 1.23
diff -u -p -u -r1.23 pthread_specific.c
--- pthread_specific.c	12 Sep 2012 14:55:48 -0000	1.23
+++ pthread_specific.c	15 Nov 2012 17:41:25 -0000
@@ -62,18 +62,14 @@ pthread_setspecific(pthread_key_t key, c
 	 * and return it from functions that are const void *, without
 	 * generating a warning. 
 	 */
-	/*LINTED const cast*/
-	self->pt_specific[key] = (void *) value;
-	self->pt_havespecific = 1;
-
-	return 0;
+	return pthread__add_specific(self, key, value);
 }
 
 void *
 pthread_getspecific(pthread_key_t key)
 {
 
-	return pthread__self()->pt_specific[key];
+	return pthread__self()->pt_specific[key].pts_value;
 }
 
 unsigned int
Index: pthread_tsd.c
===================================================================
RCS file: /cvsroot/src/lib/libpthread/pthread_tsd.c,v
retrieving revision 1.8
diff -u -p -u -r1.8 pthread_tsd.c
--- pthread_tsd.c	2 Mar 2012 18:11:53 -0000	1.8
+++ pthread_tsd.c	15 Nov 2012 17:41:25 -0000
@@ -38,14 +38,23 @@ __RCSID("$NetBSD: pthread_tsd.c,v 1.8 20
 #include "pthread.h"
 #include "pthread_int.h"
 
+
 static pthread_mutex_t tsd_mutex = PTHREAD_MUTEX_INITIALIZER;
 static int nextkey;
-void *pthread__tsd_alloc[PTHREAD_KEYS_MAX];
+
+PTQ_HEAD(pthread__tsd_list, pt_specific)
+    pthread__tsd_list[PTHREAD_KEYS_MAX];
 void (*pthread__tsd_destructors[PTHREAD_KEYS_MAX])(void *);
 
 __strong_alias(__libc_thr_keycreate,pthread_key_create)
 __strong_alias(__libc_thr_keydelete,pthread_key_delete)
 
+static void
+/*ARGSUSED*/
+null_destructor(void *p)
+{
+}
+
 int
 pthread_key_create(pthread_key_t *key, void (*destructor)(void *))
 {
@@ -54,10 +63,14 @@ pthread_key_create(pthread_key_t *key, v
 	/* Get a lock on the allocation list */
 	pthread_mutex_lock(&tsd_mutex);
 	
-	/* Find an available slot */
+	/* Find an available slot:
+	 * The condition for an available slot is one with the destructor
+	 * not being NULL. If the desired destructor is NULL we set it to 
+	 * our own internal destructor to satisfy the non NULL condition.
+	 */
 	/* 1. Search from "nextkey" to the end of the list. */
 	for (i = nextkey; i < PTHREAD_KEYS_MAX; i++)
-		if (pthread__tsd_alloc[i] == NULL)
+		if (pthread__tsd_destructors[i] == NULL)
 			break;
 
 	if (i == PTHREAD_KEYS_MAX) {
@@ -65,7 +78,7 @@ pthread_key_create(pthread_key_t *key, v
 		 *    of the list back to "nextkey".
 		 */
 		for (i = 0; i < nextkey; i++)
-			if (pthread__tsd_alloc[i] == NULL)
+			if (pthread__tsd_destructors[i] == NULL)
 				break;
 		
 		if (i == nextkey) {
@@ -78,15 +91,60 @@ pthread_key_create(pthread_key_t *key, v
 	}
 
 	/* Got one. */
-	pthread__tsd_alloc[i] = (void *)__builtin_return_address(0);
+	pthread__assert(PTQ_EMPTY(&pthread__tsd_list[i]));
+	pthread__tsd_destructors[i] = destructor ? destructor : null_destructor;
+
 	nextkey = (i + 1) % PTHREAD_KEYS_MAX;
-	pthread__tsd_destructors[i] = destructor;
 	pthread_mutex_unlock(&tsd_mutex);
 	*key = i;
 
 	return 0;
 }
 
+/*
+ * Each thread holds an array of PTHREAD_KEYS_MAX pt_specific list
+ * elements. When an element is used it is inserted into the appropriate
+ * key bucket of pthread__tsd_list. This means that ptqe_prev == NULL,
+ * means that the element is not threaded, ptqe_prev != NULL it is
+ * already part of the list. When we set to a NULL value we delete from the
+ * list if it was in the list, and when we set to non-NULL value, we insert
+ * in the list if it was not already there.
+ *
+ * We keep this global array of lists of threads that have called
+ * pthread_set_specific with non-null values, for each key so that
+ * we don't have to check all threads for non-NULL values in
+ * pthread_key_destroy
+ *
+ * We could keep an accounting of the number of specific used
+ * entries per thread, so that we can update pt_havespecific when we delete
+ * the last one, but we don't bother for now
+ */
+int
+pthread__add_specific(pthread_t self, pthread_key_t key, const void *value)
+{
+	struct pt_specific *pt;
+
+	pthread__assert(key >= 0 && key < PTHREAD_KEYS_MAX);
+
+	pthread_mutex_lock(&tsd_mutex);
+	pthread__assert(pthread__tsd_destructors[key] != NULL);
+	pt = &self->pt_specific[key];
+	self->pt_havespecific = 1;
+	if (value) {
+		if (pt->pts_next.ptqe_prev == NULL)
+			PTQ_INSERT_HEAD(&pthread__tsd_list[key], pt, pts_next);
+	} else {
+		if (pt->pts_next.ptqe_prev != NULL) {
+			PTQ_REMOVE(&pthread__tsd_list[key], pt, pts_next);
+			pt->pts_next.ptqe_prev = NULL;
+		}
+	}
+	pt->pts_value = __UNCONST(value);
+	pthread_mutex_unlock(&tsd_mutex);
+
+	return 0;
+}
+
 int
 pthread_key_delete(pthread_key_t key)
 {
@@ -157,8 +215,35 @@ pthread_key_delete(pthread_key_t key)
 	 * apply in general, just to this implementation.
 	 */
 
-	/* For the momemt, we're going with option 1. */
+	/*
+	 * We do option 3; we find the list of all pt_specific structures
+	 * threaded on the key we are deleting, unthread them, set the
+	 * pointer to NULL, and call the destructor on a saved pointer.
+	 * Finally we unthread the entry, freeing it from further use.
+	 */
+	struct pt_specific *pt;
+	void (*destructor)(void *);
+
+	pthread__assert(key >= 0 && key < PTHREAD_KEYS_MAX);
+
 	pthread_mutex_lock(&tsd_mutex);
+
+	pthread__assert(pthread__tsd_destructors[key] != NULL);
+
+	destructor = pthread__tsd_destructors[key];
+	if (destructor == null_destructor)
+		destructor = NULL;
+
+	while ((pt = PTQ_FIRST(&pthread__tsd_list[key])) != NULL) {
+		void *v;
+		PTQ_REMOVE(&pthread__tsd_list[key], pt, pts_next);
+		v = pt->pts_value;
+		pt->pts_value = NULL;
+		if (destructor && v)
+			(*destructor)(v);
+		pt->pts_next.ptqe_prev = NULL;
+	}
+
 	pthread__tsd_destructors[key] = NULL;
 	pthread_mutex_unlock(&tsd_mutex);
 
@@ -206,17 +291,24 @@ pthread__destroy_tsd(pthread_t self)
 	do {
 		done = 1;
 		for (i = 0; i < PTHREAD_KEYS_MAX; i++) {
-			if (self->pt_specific[i] != NULL) {
-				pthread_mutex_lock(&tsd_mutex);
+			struct pt_specific *pt = &self->pt_specific[i];
+			if (pt->pts_next.ptqe_prev == NULL)
+				continue;
+			pthread_mutex_lock(&tsd_mutex);
+
+			if (pt->pts_next.ptqe_prev != NULL)  {
+				PTQ_REMOVE(&pthread__tsd_list[i], pt, pts_next);
+				val = pt->pts_value;
+				pt->pts_value = NULL;
+				pt->pts_next.ptqe_prev = NULL;
 				destructor = pthread__tsd_destructors[i];
-				pthread_mutex_unlock(&tsd_mutex);
-				if (destructor != NULL) {
-					done = 0;
-					val = self->pt_specific[i];
-					/* See above */
-					self->pt_specific[i] = NULL;
-					(*destructor)(val);
-				}
+			} else
+				destructor = NULL;
+
+			pthread_mutex_unlock(&tsd_mutex);
+			if (destructor != NULL) {
+				done = 0;
+				(*destructor)(val);
 			}
 		}
 	} while (!done && iterations--);