Module Name: src Committed By: christos Date: Wed Nov 21 19:19:25 UTC 2012
Modified Files: src/lib/libpthread: pthread_int.h pthread_specific.c pthread_tsd.c Log Message: 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. To generate a diff of this commit: cvs rdiff -u -r1.87 -r1.88 src/lib/libpthread/pthread_int.h cvs rdiff -u -r1.23 -r1.24 src/lib/libpthread/pthread_specific.c cvs rdiff -u -r1.8 -r1.9 src/lib/libpthread/pthread_tsd.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/lib/libpthread/pthread_int.h diff -u src/lib/libpthread/pthread_int.h:1.87 src/lib/libpthread/pthread_int.h:1.88 --- src/lib/libpthread/pthread_int.h:1.87 Sat Nov 3 19:42:27 2012 +++ src/lib/libpthread/pthread_int.h Wed Nov 21 14:19:24 2012 @@ -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 @@ 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: src/lib/libpthread/pthread_specific.c diff -u src/lib/libpthread/pthread_specific.c:1.23 src/lib/libpthread/pthread_specific.c:1.24 --- src/lib/libpthread/pthread_specific.c:1.23 Wed Sep 12 10:55:48 2012 +++ src/lib/libpthread/pthread_specific.c Wed Nov 21 14:19:24 2012 @@ -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 @@ 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: src/lib/libpthread/pthread_tsd.c diff -u src/lib/libpthread/pthread_tsd.c:1.8 src/lib/libpthread/pthread_tsd.c:1.9 --- src/lib/libpthread/pthread_tsd.c:1.8 Fri Mar 2 13:11:53 2012 +++ src/lib/libpthread/pthread_tsd.c Wed Nov 21 14:19:24 2012 @@ -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 @@ __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,38 @@ 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; + 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 @@ 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--);