From 841339f07f0b8b16c2ded015d1f223d6e3c8b16a Mon Sep 17 00:00:00 2001 From: manu Date: Fri, 29 May 2015 07:37:31 +0000 Subject: [PATCH] Make PTHREAD_KEYS_MAX dynamically adjustable NetBSD's PTHREAD_KEYS_MAX is set to 256, which is low compared to other systems like Linux (1024) or MacOS X (512). As a result some setups tested on Linux will exhibit problems on NetBSD because of pthread_keys usage beyond the limit. This happens for instance on Apache with various module loaded, and in this case no particular developper can be blamed for going beyond the limit, since several modules from different sources contribute to the problem. This patch makes the limit conigurable through the PTHREAD_KEYS_MAX environement variable. If undefined, the default remains unchanged (256). In any case, the value cannot be lowered below POSIX-mandated _POSIX_THREAD_KEYS_MAX (128). While there: - use EXIT_FAILURE instead of 1 when calling err(3) in libpthread. - Reset _POSIX_THREAD_KEYS_MAX to POSIX mandated 128, instead of 256. --- include/limits.h | 12 +++--- lib/libpthread/pthread.c | 61 ++++++++++++++++++----------- lib/libpthread/pthread_int.h | 6 ++- lib/libpthread/pthread_key_create.3 | 9 ++++- lib/libpthread/pthread_tsd.c | 59 ++++++++++++++++++++++------ lib/libpthread_dbg/pthread_dbg.c | 6 +-- 6 files changed, 107 insertions(+), 46 deletions(-) diff --git a/include/limits.h b/include/limits.h index b8b024efea5c..f32b37102bed 100644 --- a/include/limits.h +++ b/include/limits.h @@ -1,4 +1,4 @@ -/* $NetBSD: limits.h,v 1.33 2012/11/18 17:41:53 manu Exp $ */ +/* $NetBSD: limits.h,v 1.34 2015/05/29 07:37:31 manu Exp $ */ /* * Copyright (c) 1988, 1993 @@ -75,18 +75,20 @@ */ /* - * The following 3 are not part of the standard - * but left here for compatibility + * The following 3 are defined in + * Open Group Base Specifications Issue 7 */ #define _POSIX_THREAD_DESTRUCTOR_ITERATIONS 4 -#define _POSIX_THREAD_KEYS_MAX 256 +#define _POSIX_THREAD_KEYS_MAX 128 #define _POSIX_THREAD_THREADS_MAX 64 /* * These are the correct names, defined in terms of the above + * except for PTHREAD_KEYS_MAX which is bigger than standard + * mandated minimum value _POSIX_THREAD_KEYS_MAX. */ #define PTHREAD_DESTRUCTOR_ITERATIONS _POSIX_THREAD_DESTRUCTOR_ITERATIONS -#define PTHREAD_KEYS_MAX _POSIX_THREAD_KEYS_MAX +#define PTHREAD_KEYS_MAX 256 /* Not yet: PTHREAD_STACK_MIN */ #define PTHREAD_THREADS_MAX _POSIX_THREAD_THREADS_MAX diff --git a/lib/libpthread/pthread.c b/lib/libpthread/pthread.c index 1730724d4ce1..9633dc02416b 100644 --- a/lib/libpthread/pthread.c +++ b/lib/libpthread/pthread.c @@ -1,4 +1,4 @@ -/* $NetBSD: pthread.c,v 1.145 2014/12/16 20:05:54 pooka Exp $ */ +/* $NetBSD: pthread.c,v 1.146 2015/05/29 07:37:31 manu Exp $ */ /*- * Copyright (c) 2001, 2002, 2003, 2006, 2007, 2008 The NetBSD Foundation, Inc. @@ -30,7 +30,7 @@ */ #include -__RCSID("$NetBSD: pthread.c,v 1.145 2014/12/16 20:05:54 pooka Exp $"); +__RCSID("$NetBSD: pthread.c,v 1.146 2015/05/29 07:37:31 manu Exp $"); #define __EXPOSE_STACK 1 @@ -116,7 +116,8 @@ int pthread__dbg; /* set by libpthread_dbg if active */ */ size_t pthread__stacksize; size_t pthread__pagesize; -static struct __pthread_st pthread__main; +static struct __pthread_st *pthread__main; +static size_t __pthread_st_size; int _sys___sigprocmask14(int, const sigset_t *, sigset_t *); @@ -165,6 +166,22 @@ pthread__init(void) int i; extern int __isthreaded; + /* + * Allocate pthread_keys descriptors before + * reseting __uselibcstub because otherwise + * malloc() will call pthread_keys_create() + * while pthread_keys descriptors are not + * yet allocated. + */ + if (pthread_tsd_init() != 0) + err(EXIT_FAILURE, "Cannot allocate pthread_keys descriptors"); + + __pthread_st_size = sizeof(*pthread__main) + + pthread_keys_max * sizeof(pthread__main->pt_specific[0]); + + if ((pthread__main = calloc(1, __pthread_st_size)) == NULL) + err(EXIT_FAILURE, "Cannot allocate pthread__specific"); + __uselibcstub = 0; pthread__pagesize = (size_t)sysconf(_SC_PAGESIZE); @@ -179,7 +196,7 @@ pthread__init(void) /* Fetch parameters. */ i = (int)_lwp_unpark_all(NULL, 0, NULL); if (i == -1) - err(1, "_lwp_unpark_all"); + err(EXIT_FAILURE, "_lwp_unpark_all"); if (i < pthread__unpark_max) pthread__unpark_max = i; @@ -200,7 +217,7 @@ pthread__init(void) (void)rb_tree_insert_node(&pthread__alltree, first); if (_lwp_ctl(LWPCTL_FEATURE_CURCPU, &first->pt_lwpctl) != 0) { - err(1, "_lwp_ctl"); + err(EXIT_FAILURE, "_lwp_ctl"); } /* Start subsystems */ @@ -241,7 +258,7 @@ pthread__fork_callback(void) /* lwpctl state is not copied across fork. */ if (_lwp_ctl(LWPCTL_FEATURE_CURCPU, &self->pt_lwpctl)) { - err(1, "_lwp_ctl"); + err(EXIT_FAILURE, "_lwp_ctl"); } self->pt_lid = _lwp_self(); } @@ -299,7 +316,6 @@ pthread__initthread(pthread_t t) pthread_mutex_init(&t->pt_lock, NULL); PTQ_INIT(&t->pt_cleanup_stack); pthread_cond_init(&t->pt_joiners, NULL); - memset(&t->pt_specific, 0, sizeof(t->pt_specific)); } static void @@ -452,7 +468,7 @@ pthread_create(pthread_t *thread, const pthread_attr_t *attr, * and initialize it. */ if (newthread == NULL) { - newthread = malloc(sizeof(*newthread)); + newthread = calloc(1, __pthread_st_size); if (newthread == NULL) { free(name); return ENOMEM; @@ -559,7 +575,7 @@ pthread__create_tramp(void *cookie) } if (_lwp_ctl(LWPCTL_FEATURE_CURCPU, &self->pt_lwpctl)) { - err(1, "_lwp_ctl"); + err(EXIT_FAILURE, "_lwp_ctl"); } retval = (*self->pt_func)(self->pt_arg); @@ -1274,17 +1290,18 @@ pthread__initmainstack(void) _DIAGASSERT(_dlauxinfo() != NULL); if (getrlimit(RLIMIT_STACK, &slimit) == -1) - err(1, "Couldn't get stack resource consumption limits"); + err(EXIT_FAILURE, + "Couldn't get stack resource consumption limits"); size = slimit.rlim_cur; - pthread__main.pt_stack.ss_size = size; + pthread__main->pt_stack.ss_size = size; for (aux = _dlauxinfo(); aux->a_type != AT_NULL; ++aux) { if (aux->a_type == AT_STACKBASE) { - pthread__main.pt_stack.ss_sp = (void *)aux->a_v; + pthread__main->pt_stack.ss_sp = (void *)aux->a_v; #ifdef __MACHINE_STACK_GROWS_UP - pthread__main.pt_stack.ss_sp = (void *)aux->a_v; + pthread__main->pt_stack.ss_sp = (void *)aux->a_v; #else - pthread__main.pt_stack.ss_sp = (char *)aux->a_v - size; + pthread__main->pt_stack.ss_sp = (char *)aux->a_v - size; #endif break; } @@ -1306,26 +1323,26 @@ pthread__initmain(pthread_t *newt) value = pthread__getenv("PTHREAD_STACKSIZE"); if (value != NULL) { pthread__stacksize = atoi(value) * 1024; - if (pthread__stacksize > pthread__main.pt_stack.ss_size) - pthread__stacksize = pthread__main.pt_stack.ss_size; + if (pthread__stacksize > pthread__main->pt_stack.ss_size) + pthread__stacksize = pthread__main->pt_stack.ss_size; } if (pthread__stacksize == 0) - pthread__stacksize = pthread__main.pt_stack.ss_size; + pthread__stacksize = pthread__main->pt_stack.ss_size; pthread__stacksize += pthread__pagesize - 1; pthread__stacksize &= ~(pthread__pagesize - 1); if (pthread__stacksize < 4 * pthread__pagesize) errx(1, "Stacksize limit is too low, minimum %zd kbyte.", 4 * pthread__pagesize / 1024); - *newt = &pthread__main; + *newt = pthread__main; #if defined(_PTHREAD_GETTCB_EXT) - pthread__main.pt_tls = _PTHREAD_GETTCB_EXT(); + pthread__main->pt_tls = _PTHREAD_GETTCB_EXT(); #elif defined(__HAVE___LWP_GETTCB_FAST) - pthread__main.pt_tls = __lwp_gettcb_fast(); + pthread__main->pt_tls = __lwp_gettcb_fast(); #else - pthread__main.pt_tls = _lwp_getprivate(); + pthread__main->pt_tls = _lwp_getprivate(); #endif - pthread__main.pt_tls->tcb_pthread = &pthread__main; + pthread__main->pt_tls->tcb_pthread = pthread__main; } static signed int diff --git a/lib/libpthread/pthread_int.h b/lib/libpthread/pthread_int.h index c76e2c393ee5..5c0ed7b70d36 100644 --- a/lib/libpthread/pthread_int.h +++ b/lib/libpthread/pthread_int.h @@ -1,4 +1,4 @@ -/* $NetBSD: pthread_int.h,v 1.90 2014/12/16 20:05:54 pooka Exp $ */ +/* $NetBSD: pthread_int.h,v 1.91 2015/05/29 07:37:31 manu Exp $ */ /*- * Copyright (c) 2001, 2002, 2003, 2006, 2007, 2008 The NetBSD Foundation, Inc. @@ -151,7 +151,7 @@ struct __pthread_st { struct pt_specific { void *pts_value; PTQ_ENTRY(pt_specific) pts_next; - } pt_specific[PTHREAD_KEYS_MAX]; + } pt_specific[]; }; /* Thread states */ @@ -181,6 +181,7 @@ extern int pthread__nspins; extern int pthread__concurrency; extern int pthread__osrev; extern int pthread__unpark_max; +extern int pthread_keys_max; extern int __uselibcstub; @@ -291,6 +292,7 @@ pthread__self(void) } \ } while (/*CONSTCOND*/0) +int pthread_tsd_init(void) PTHREAD_HIDE; void pthread__destroy_tsd(pthread_t) PTHREAD_HIDE; __dead void pthread__assertfunc(const char *, int, const char *, const char *) PTHREAD_HIDE; diff --git a/lib/libpthread/pthread_key_create.3 b/lib/libpthread/pthread_key_create.3 index 05f72eac51d6..9ee5c80412bd 100644 --- a/lib/libpthread/pthread_key_create.3 +++ b/lib/libpthread/pthread_key_create.3 @@ -1,4 +1,4 @@ -.\" $NetBSD: pthread_key_create.3,v 1.6 2010/07/09 10:55:11 wiz Exp $ +.\" $NetBSD: pthread_key_create.3,v 1.7 2015/05/29 07:37:31 manu Exp $ .\" .\" Copyright (c) 2002, 2010 The NetBSD Foundation, Inc. .\" All rights reserved. @@ -151,6 +151,13 @@ Also .Fn pthread_key_delete will return zero upon success. Upon failure both functions return an error number to indicate the cause. +.Sh ENVIRONMENT +.Bl -tag -width PTHREAD_KEYS_MAX +.It Ev PTHREAD_KEYS_MAX +Maximum per process thread-specific data keys. This cannot be set +below +.Dv _POSIX_THREAD_KEYS_MAX . +.El .Sh ERRORS The .Fn pthread_key_create diff --git a/lib/libpthread/pthread_tsd.c b/lib/libpthread/pthread_tsd.c index 87ea6540908d..7789bd04142c 100644 --- a/lib/libpthread/pthread_tsd.c +++ b/lib/libpthread/pthread_tsd.c @@ -1,4 +1,4 @@ -/* $NetBSD: pthread_tsd.c,v 1.11 2013/03/21 16:49:12 christos Exp $ */ +/* $NetBSD: pthread_tsd.c,v 1.12 2015/05/29 07:37:31 manu Exp $ */ /*- * Copyright (c) 2001, 2007 The NetBSD Foundation, Inc. @@ -30,7 +30,7 @@ */ #include -__RCSID("$NetBSD: pthread_tsd.c,v 1.11 2013/03/21 16:49:12 christos Exp $"); +__RCSID("$NetBSD: pthread_tsd.c,v 1.12 2015/05/29 07:37:31 manu Exp $"); /* Functions and structures dealing with thread-specific data */ #include @@ -39,13 +39,12 @@ __RCSID("$NetBSD: pthread_tsd.c,v 1.11 2013/03/21 16:49:12 christos Exp $"); #include "pthread_int.h" #include "reentrant.h" - +int pthread_keys_max; static pthread_mutex_t tsd_mutex = PTHREAD_MUTEX_INITIALIZER; static int nextkey; -PTQ_HEAD(pthread__tsd_list, pt_specific) - pthread__tsd_list[PTHREAD_KEYS_MAX]; -void (*pthread__tsd_destructors[PTHREAD_KEYS_MAX])(void *); +PTQ_HEAD(pthread__tsd_list, pt_specific) *pthread__tsd_list = NULL; +void (**pthread__tsd_destructors)(void *) = NULL; __strong_alias(__libc_thr_keycreate,pthread_key_create) __strong_alias(__libc_thr_keydelete,pthread_key_delete) @@ -58,6 +57,40 @@ null_destructor(void *p) #include #include +#include + +int +pthread_tsd_init(void) +{ + char *pkm; + size_t len; + + if ((pkm = getenv("PTHREAD_KEYS_MAX")) != NULL) { + pthread_keys_max = (int)strtol(pkm, NULL, 0); + if (pthread_keys_max < _POSIX_THREAD_KEYS_MAX) + pthread_keys_max = _POSIX_THREAD_KEYS_MAX; + } else { + pthread_keys_max = PTHREAD_KEYS_MAX; + } + + len = sizeof(*pthread__tsd_list); + if ((pthread__tsd_list = calloc(pthread_keys_max, len)) == NULL) + goto out1; + + len = sizeof(*pthread__tsd_destructors); + if ((pthread__tsd_destructors = calloc(pthread_keys_max, len)) == NULL) + goto out2; + + return 0; + +out2: + free(pthread__tsd_list); +out1: + pthread_keys_max = 0; + + return -1; +} + int pthread_key_create(pthread_key_t *key, void (*destructor)(void *)) { @@ -75,11 +108,11 @@ pthread_key_create(pthread_key_t *key, void (*destructor)(void *)) * 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++) + for (i = nextkey; i < pthread_keys_max; i++) if (pthread__tsd_destructors[i] == NULL) break; - if (i == PTHREAD_KEYS_MAX) { + if (i == pthread_keys_max) { /* 2. If that didn't work, search from the start * of the list back to "nextkey". */ @@ -100,7 +133,7 @@ pthread_key_create(pthread_key_t *key, void (*destructor)(void *)) pthread__assert(PTQ_EMPTY(&pthread__tsd_list[i])); pthread__tsd_destructors[i] = destructor ? destructor : null_destructor; - nextkey = (i + 1) % PTHREAD_KEYS_MAX; + nextkey = (i + 1) % pthread_keys_max; pthread_mutex_unlock(&tsd_mutex); *key = i; @@ -108,7 +141,7 @@ pthread_key_create(pthread_key_t *key, void (*destructor)(void *)) } /* - * Each thread holds an array of PTHREAD_KEYS_MAX pt_specific list + * 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 @@ -130,7 +163,7 @@ 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__assert(key >= 0 && key < pthread_keys_max); pthread_mutex_lock(&tsd_mutex); pthread__assert(pthread__tsd_destructors[key] != NULL); @@ -237,7 +270,7 @@ pthread_key_delete(pthread_key_t key) if (__predict_false(__uselibcstub)) return __libc_thr_keydelete_stub(key); - pthread__assert(key >= 0 && key < PTHREAD_KEYS_MAX); + pthread__assert(key >= 0 && key < pthread_keys_max); pthread_mutex_lock(&tsd_mutex); @@ -295,7 +328,7 @@ pthread__destroy_tsd(pthread_t self) iterations = 4; /* We're not required to try very hard */ do { done = 1; - for (i = 0; i < PTHREAD_KEYS_MAX; i++) { + for (i = 0; i < pthread_keys_max; i++) { struct pt_specific *pt = &self->pt_specific[i]; if (pt->pts_next.ptqe_prev == NULL) continue; diff --git a/lib/libpthread_dbg/pthread_dbg.c b/lib/libpthread_dbg/pthread_dbg.c index 720ce468bb3d..05886e9f2560 100644 --- a/lib/libpthread_dbg/pthread_dbg.c +++ b/lib/libpthread_dbg/pthread_dbg.c @@ -1,4 +1,4 @@ -/* $NetBSD: pthread_dbg.c,v 1.42 2013/01/20 18:45:19 christos Exp $ */ +/* $NetBSD: pthread_dbg.c,v 1.43 2015/05/29 07:37:32 manu Exp $ */ /*- * Copyright (c) 2002 Wasabi Systems, Inc. @@ -36,7 +36,7 @@ */ #include -__RCSID("$NetBSD: pthread_dbg.c,v 1.42 2013/01/20 18:45:19 christos Exp $"); +__RCSID("$NetBSD: pthread_dbg.c,v 1.43 2015/05/29 07:37:32 manu Exp $"); #define __EXPOSE_STACK 1 @@ -415,7 +415,7 @@ td_tsd_iter(td_proc_t *proc, void *allocated; void (*destructor)(void *); - for (i = 0; i < PTHREAD_KEYS_MAX; i++) { + for (i = 0; i < pthread_keys_max; i++) { val = READ(proc, proc->tsdlistaddr + i * sizeof(allocated), &allocated, sizeof(allocated)); if (val != 0)