Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/lib/libpthread Replace the simple implementation of pthread_...



details:   https://anonhg.NetBSD.org/src/rev/45b87f84db9e
branches:  trunk
changeset: 782820:45b87f84db9e
user:      christos <christos%NetBSD.org@localhost>
date:      Wed Nov 21 19:19:24 2012 +0000

description:
Replace the simple implementation of pthread_key_{create,destroy}
and pthread_{g,s}etspecific functions, to one that invalidates
values of keys in other threads when pthread_key_delete() is called.
This fixes chromium, which expects pthread_key_delete() to do
cleanup in all threads.

diffstat:

 lib/libpthread/pthread_int.h      |    8 +-
 lib/libpthread/pthread_specific.c |   12 +--
 lib/libpthread/pthread_tsd.c      |  133 ++++++++++++++++++++++++++++++++-----
 3 files changed, 124 insertions(+), 29 deletions(-)

diffs (275 lines):

diff -r f7ed13b6f98e -r 45b87f84db9e lib/libpthread/pthread_int.h
--- a/lib/libpthread/pthread_int.h      Wed Nov 21 18:45:08 2012 +0000
+++ b/lib/libpthread/pthread_int.h      Wed Nov 21 19:19:24 2012 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: pthread_int.h,v 1.87 2012/11/03 23:42:27 rmind Exp $   */
+/*     $NetBSD: pthread_int.h,v 1.88 2012/11/21 19:19:24 christos Exp $        */
 
 /*-
  * Copyright (c) 2001, 2002, 2003, 2006, 2007, 2008 The NetBSD Foundation, Inc.
@@ -148,7 +148,10 @@
 
        /* 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 @@
 __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 */
diff -r f7ed13b6f98e -r 45b87f84db9e lib/libpthread/pthread_specific.c
--- a/lib/libpthread/pthread_specific.c Wed Nov 21 18:45:08 2012 +0000
+++ b/lib/libpthread/pthread_specific.c Wed Nov 21 19:19:24 2012 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: pthread_specific.c,v 1.23 2012/09/12 14:55:48 matt Exp $       */
+/*     $NetBSD: pthread_specific.c,v 1.24 2012/11/21 19:19:24 christos Exp $   */
 
 /*-
  * Copyright (c) 2001, 2007 The NetBSD Foundation, Inc.
@@ -30,7 +30,7 @@
  */
 
 #include <sys/cdefs.h>
-__RCSID("$NetBSD: pthread_specific.c,v 1.23 2012/09/12 14:55:48 matt Exp $");
+__RCSID("$NetBSD: pthread_specific.c,v 1.24 2012/11/21 19:19:24 christos Exp $");
 
 /* Functions and structures dealing with thread-specific data */
 
@@ -62,18 +62,14 @@
         * 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
diff -r f7ed13b6f98e -r 45b87f84db9e lib/libpthread/pthread_tsd.c
--- a/lib/libpthread/pthread_tsd.c      Wed Nov 21 18:45:08 2012 +0000
+++ b/lib/libpthread/pthread_tsd.c      Wed Nov 21 19:19:24 2012 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: pthread_tsd.c,v 1.8 2012/03/02 18:11:53 joerg Exp $    */
+/*     $NetBSD: pthread_tsd.c,v 1.9 2012/11/21 19:19:24 christos Exp $ */
 
 /*-
  * Copyright (c) 2001, 2007 The NetBSD Foundation, Inc.
@@ -30,7 +30,7 @@
  */
 
 #include <sys/cdefs.h>
-__RCSID("$NetBSD: pthread_tsd.c,v 1.8 2012/03/02 18:11:53 joerg Exp $");
+__RCSID("$NetBSD: pthread_tsd.c,v 1.9 2012/11/21 19:19:24 christos Exp $");
 
 /* Functions and structures dealing with thread-specific data */
 #include <errno.h>
@@ -38,14 +38,23 @@
 #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 @@
        /* 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 @@
                 *    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 @@
        }
 
        /* 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,38 @@
         * 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;
+               pt->pts_next.ptqe_prev = NULL;
+               if (destructor && v) {
+                       pthread_mutex_unlock(&tsd_mutex);
+                       (*destructor)(v);
+                       pthread_mutex_lock(&tsd_mutex);
+               }
+       }
+
        pthread__tsd_destructors[key] = NULL;
        pthread_mutex_unlock(&tsd_mutex);
 
@@ -206,17 +294,24 @@
        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--);



Home | Main Index | Thread Index | Old Index