xref: /illumos-gate/usr/src/lib/libc/port/threads/tsd.c (revision 1da57d55)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5cb620785Sraf  * Common Development and Distribution License (the "License").
6cb620785Sraf  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
21cb620785Sraf 
227c478bd9Sstevel@tonic-gate /*
238cd45542Sraf  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate #include "lint.h"
287c478bd9Sstevel@tonic-gate #include "thr_uberdata.h"
297c478bd9Sstevel@tonic-gate #include <stddef.h>
307c478bd9Sstevel@tonic-gate 
31*7257d1b4Sraf /*
32*7257d1b4Sraf  * These symbols should not be exported from libc, but
33*7257d1b4Sraf  * /lib/libm.so.2 references them.  libm needs to be fixed.
34*7257d1b4Sraf  * Also, some older versions of the Studio compiler/debugger
35*7257d1b4Sraf  * components reference them.  These need to be fixed, too.
36*7257d1b4Sraf  */
37*7257d1b4Sraf #pragma weak _thr_getspecific = thr_getspecific
38*7257d1b4Sraf #pragma weak _thr_keycreate = thr_keycreate
39*7257d1b4Sraf #pragma weak _thr_setspecific = thr_setspecific
40*7257d1b4Sraf 
417c478bd9Sstevel@tonic-gate /*
427c478bd9Sstevel@tonic-gate  * 128 million keys should be enough for anyone.
437c478bd9Sstevel@tonic-gate  * This allocates half a gigabyte of memory for the keys themselves and
447c478bd9Sstevel@tonic-gate  * half a gigabyte of memory for each thread that uses the largest key.
457c478bd9Sstevel@tonic-gate  */
467c478bd9Sstevel@tonic-gate #define	MAX_KEYS	0x08000000U
477c478bd9Sstevel@tonic-gate 
487c478bd9Sstevel@tonic-gate int
thr_keycreate(thread_key_t * pkey,void (* destructor)(void *))49*7257d1b4Sraf thr_keycreate(thread_key_t *pkey, void (*destructor)(void *))
507c478bd9Sstevel@tonic-gate {
517c478bd9Sstevel@tonic-gate 	tsd_metadata_t *tsdm = &curthread->ul_uberdata->tsd_metadata;
527c478bd9Sstevel@tonic-gate 	void (**old_data)(void *) = NULL;
537c478bd9Sstevel@tonic-gate 	void (**new_data)(void *);
547c478bd9Sstevel@tonic-gate 	uint_t old_nkeys;
557c478bd9Sstevel@tonic-gate 	uint_t new_nkeys;
567c478bd9Sstevel@tonic-gate 
577c478bd9Sstevel@tonic-gate 	lmutex_lock(&tsdm->tsdm_lock);
587c478bd9Sstevel@tonic-gate 
597c478bd9Sstevel@tonic-gate 	/*
607c478bd9Sstevel@tonic-gate 	 * Unfortunately, pthread_getspecific() specifies that a
617c478bd9Sstevel@tonic-gate 	 * pthread_getspecific() on an allocated key upon which the
627c478bd9Sstevel@tonic-gate 	 * calling thread has not performed a pthread_setspecifc()
637c478bd9Sstevel@tonic-gate 	 * must return NULL.  Consider the following sequence:
647c478bd9Sstevel@tonic-gate 	 *
657c478bd9Sstevel@tonic-gate 	 *	pthread_key_create(&key);
667c478bd9Sstevel@tonic-gate 	 *	pthread_setspecific(key, datum);
677c478bd9Sstevel@tonic-gate 	 *	pthread_key_delete(&key);
687c478bd9Sstevel@tonic-gate 	 *	pthread_key_create(&key);
697c478bd9Sstevel@tonic-gate 	 *	val = pthread_getspecific(key);
707c478bd9Sstevel@tonic-gate 	 *
717c478bd9Sstevel@tonic-gate 	 * According to POSIX, if the deleted key is reused for the new
727c478bd9Sstevel@tonic-gate 	 * key returned by the second pthread_key_create(), then the
737c478bd9Sstevel@tonic-gate 	 * pthread_getspecific() in the above example must return NULL
747c478bd9Sstevel@tonic-gate 	 * (and not the stale datum).  The implementation is thus left
757c478bd9Sstevel@tonic-gate 	 * with two alternatives:
767c478bd9Sstevel@tonic-gate 	 *
777c478bd9Sstevel@tonic-gate 	 *  (1)	Reuse deleted keys.  If this is to be implemented optimally,
787c478bd9Sstevel@tonic-gate 	 *	it requires that pthread_key_create() somehow associate
797c478bd9Sstevel@tonic-gate 	 *	the value NULL with the new (reused) key for each thread.
807c478bd9Sstevel@tonic-gate 	 *	Keeping the hot path fast and lock-free induces substantial
817c478bd9Sstevel@tonic-gate 	 *	complexity on the implementation.
827c478bd9Sstevel@tonic-gate 	 *
837c478bd9Sstevel@tonic-gate 	 *  (2)	Never reuse deleted keys. This allows the pthread_getspecific()
847c478bd9Sstevel@tonic-gate 	 *	implementation to simply perform a check against the number
857c478bd9Sstevel@tonic-gate 	 *	of keys set by the calling thread, returning NULL if the
867c478bd9Sstevel@tonic-gate 	 *	specified key is larger than the highest set key.  This has
877c478bd9Sstevel@tonic-gate 	 *	the disadvantage of wasting memory (a program which simply
887c478bd9Sstevel@tonic-gate 	 *	loops calling pthread_key_create()/pthread_key_delete()
897c478bd9Sstevel@tonic-gate 	 *	will ultimately run out of memory), but permits an optimal
907c478bd9Sstevel@tonic-gate 	 *	pthread_getspecific() while allowing for simple key creation
917c478bd9Sstevel@tonic-gate 	 *	and deletion.
927c478bd9Sstevel@tonic-gate 	 *
937c478bd9Sstevel@tonic-gate 	 * All Solaris implementations have opted for (2).  Given the
947c478bd9Sstevel@tonic-gate 	 * ~10 years that this has been in the field, it is safe to assume
957c478bd9Sstevel@tonic-gate 	 * that applications don't loop creating and destroying keys; we
967c478bd9Sstevel@tonic-gate 	 * stick with (2).
977c478bd9Sstevel@tonic-gate 	 */
987c478bd9Sstevel@tonic-gate 	if (tsdm->tsdm_nused == (old_nkeys = tsdm->tsdm_nkeys)) {
997c478bd9Sstevel@tonic-gate 		/*
1007c478bd9Sstevel@tonic-gate 		 * We need to allocate or double the number of keys.
1017c478bd9Sstevel@tonic-gate 		 * tsdm->tsdm_nused must always be a power of two.
1027c478bd9Sstevel@tonic-gate 		 */
1037c478bd9Sstevel@tonic-gate 		if ((new_nkeys = (old_nkeys << 1)) == 0)
1047c478bd9Sstevel@tonic-gate 			new_nkeys = 8;
1057c478bd9Sstevel@tonic-gate 
1067c478bd9Sstevel@tonic-gate 		if (new_nkeys > MAX_KEYS) {
1077c478bd9Sstevel@tonic-gate 			lmutex_unlock(&tsdm->tsdm_lock);
1087c478bd9Sstevel@tonic-gate 			return (EAGAIN);
1097c478bd9Sstevel@tonic-gate 		}
1107c478bd9Sstevel@tonic-gate 		if ((new_data = lmalloc(new_nkeys * sizeof (void *))) == NULL) {
1117c478bd9Sstevel@tonic-gate 			lmutex_unlock(&tsdm->tsdm_lock);
1127c478bd9Sstevel@tonic-gate 			return (ENOMEM);
1137c478bd9Sstevel@tonic-gate 		}
1147c478bd9Sstevel@tonic-gate 		if ((old_data = tsdm->tsdm_destro) == NULL) {
1157c478bd9Sstevel@tonic-gate 			/* key == 0 is always invalid */
1167c478bd9Sstevel@tonic-gate 			new_data[0] = TSD_UNALLOCATED;
1177c478bd9Sstevel@tonic-gate 			tsdm->tsdm_nused = 1;
1187c478bd9Sstevel@tonic-gate 		} else {
1198cd45542Sraf 			(void) memcpy(new_data, old_data,
1208cd45542Sraf 			    old_nkeys * sizeof (void *));
1217c478bd9Sstevel@tonic-gate 		}
1227c478bd9Sstevel@tonic-gate 		tsdm->tsdm_destro = new_data;
1237c478bd9Sstevel@tonic-gate 		tsdm->tsdm_nkeys = new_nkeys;
1247c478bd9Sstevel@tonic-gate 	}
1257c478bd9Sstevel@tonic-gate 
1267c478bd9Sstevel@tonic-gate 	*pkey = tsdm->tsdm_nused;
1277c478bd9Sstevel@tonic-gate 	tsdm->tsdm_destro[tsdm->tsdm_nused++] = destructor;
1287c478bd9Sstevel@tonic-gate 	lmutex_unlock(&tsdm->tsdm_lock);
1297c478bd9Sstevel@tonic-gate 
1307c478bd9Sstevel@tonic-gate 	if (old_data != NULL)
1317c478bd9Sstevel@tonic-gate 		lfree(old_data, old_nkeys * sizeof (void *));
1327c478bd9Sstevel@tonic-gate 
1337c478bd9Sstevel@tonic-gate 	return (0);
1347c478bd9Sstevel@tonic-gate }
1357c478bd9Sstevel@tonic-gate 
136*7257d1b4Sraf #pragma weak _pthread_key_create = pthread_key_create
137*7257d1b4Sraf int
pthread_key_create(pthread_key_t * pkey,void (* destructor)(void *))138*7257d1b4Sraf pthread_key_create(pthread_key_t *pkey, void (*destructor)(void *))
139*7257d1b4Sraf {
140*7257d1b4Sraf 	return (thr_keycreate(pkey, destructor));
141*7257d1b4Sraf }
142*7257d1b4Sraf 
143cb620785Sraf /*
144*7257d1b4Sraf  * Same as thr_keycreate(), above, except that the key creation
145cb620785Sraf  * is performed only once.  This relies upon the fact that a key
146cb620785Sraf  * value of THR_ONCE_KEY is invalid, and requires that the key be
147cb620785Sraf  * allocated with a value of THR_ONCE_KEY before calling here.
148cb620785Sraf  * THR_ONCE_KEY and PTHREAD_ONCE_KEY_NP, defined in <thread.h>
149cb620785Sraf  * and <pthread.h> respectively, must have the same value.
150cb620785Sraf  * Example:
151cb620785Sraf  *
152cb620785Sraf  *	static pthread_key_t key = PTHREAD_ONCE_KEY_NP;
153cb620785Sraf  *	...
154cb620785Sraf  *	pthread_key_create_once_np(&key, destructor);
155cb620785Sraf  */
156*7257d1b4Sraf #pragma weak pthread_key_create_once_np = thr_keycreate_once
157cb620785Sraf int
thr_keycreate_once(thread_key_t * keyp,void (* destructor)(void *))158*7257d1b4Sraf thr_keycreate_once(thread_key_t *keyp, void (*destructor)(void *))
159cb620785Sraf {
160cb620785Sraf 	static mutex_t key_lock = DEFAULTMUTEX;
161cb620785Sraf 	thread_key_t key;
162cb620785Sraf 	int error;
163cb620785Sraf 
164cb620785Sraf 	if (*keyp == THR_ONCE_KEY) {
165cb620785Sraf 		lmutex_lock(&key_lock);
166cb620785Sraf 		if (*keyp == THR_ONCE_KEY) {
167*7257d1b4Sraf 			error = thr_keycreate(&key, destructor);
168cb620785Sraf 			if (error) {
169cb620785Sraf 				lmutex_unlock(&key_lock);
170cb620785Sraf 				return (error);
171cb620785Sraf 			}
172*7257d1b4Sraf 			membar_producer();
173cb620785Sraf 			*keyp = key;
174cb620785Sraf 		}
175cb620785Sraf 		lmutex_unlock(&key_lock);
176cb620785Sraf 	}
177*7257d1b4Sraf 	membar_consumer();
178cb620785Sraf 
179cb620785Sraf 	return (0);
180cb620785Sraf }
181cb620785Sraf 
1827c478bd9Sstevel@tonic-gate int
pthread_key_delete(pthread_key_t key)183*7257d1b4Sraf pthread_key_delete(pthread_key_t key)
1847c478bd9Sstevel@tonic-gate {
1857c478bd9Sstevel@tonic-gate 	tsd_metadata_t *tsdm = &curthread->ul_uberdata->tsd_metadata;
1867c478bd9Sstevel@tonic-gate 
1877c478bd9Sstevel@tonic-gate 	lmutex_lock(&tsdm->tsdm_lock);
1887c478bd9Sstevel@tonic-gate 
1897c478bd9Sstevel@tonic-gate 	if (key >= tsdm->tsdm_nused ||
1907c478bd9Sstevel@tonic-gate 	    tsdm->tsdm_destro[key] == TSD_UNALLOCATED) {
1917c478bd9Sstevel@tonic-gate 		lmutex_unlock(&tsdm->tsdm_lock);
1927c478bd9Sstevel@tonic-gate 		return (EINVAL);
1937c478bd9Sstevel@tonic-gate 	}
1947c478bd9Sstevel@tonic-gate 
1957c478bd9Sstevel@tonic-gate 	tsdm->tsdm_destro[key] = TSD_UNALLOCATED;
1967c478bd9Sstevel@tonic-gate 	lmutex_unlock(&tsdm->tsdm_lock);
1977c478bd9Sstevel@tonic-gate 
1987c478bd9Sstevel@tonic-gate 	return (0);
1997c478bd9Sstevel@tonic-gate }
2007c478bd9Sstevel@tonic-gate 
2017c478bd9Sstevel@tonic-gate /*
2027c478bd9Sstevel@tonic-gate  * Blessedly, the pthread_getspecific() interface is much better than the
2037c478bd9Sstevel@tonic-gate  * thr_getspecific() interface in that it cannot return an error status.
2047c478bd9Sstevel@tonic-gate  * Thus, if the key specified is bogus, pthread_getspecific()'s behavior
2057c478bd9Sstevel@tonic-gate  * is undefined.  As an added bonus (and as an artificat of not returning
2067c478bd9Sstevel@tonic-gate  * an error code), the requested datum is returned rather than stored
2077c478bd9Sstevel@tonic-gate  * through a parameter -- thereby avoiding the unnecessary store/load pair
2087c478bd9Sstevel@tonic-gate  * incurred by thr_getspecific().  Every once in a while, the Standards
2097c478bd9Sstevel@tonic-gate  * get it right -- but usually by accident.
2107c478bd9Sstevel@tonic-gate  */
2117c478bd9Sstevel@tonic-gate void *
pthread_getspecific(pthread_key_t key)212*7257d1b4Sraf pthread_getspecific(pthread_key_t key)
2137c478bd9Sstevel@tonic-gate {
2147c478bd9Sstevel@tonic-gate 	tsd_t *stsd;
2157c478bd9Sstevel@tonic-gate 
2167c478bd9Sstevel@tonic-gate 	/*
2177c478bd9Sstevel@tonic-gate 	 * We are cycle-shaving in this function because some
2187c478bd9Sstevel@tonic-gate 	 * applications make heavy use of it and one machine cycle
2197c478bd9Sstevel@tonic-gate 	 * can make a measurable difference in performance.  This
2207c478bd9Sstevel@tonic-gate 	 * is why we waste a little memory and allocate a NULL value
2217c478bd9Sstevel@tonic-gate 	 * for the invalid key == 0 in curthread->ul_ftsd[0] rather
2227c478bd9Sstevel@tonic-gate 	 * than adjusting the key by subtracting one.
2237c478bd9Sstevel@tonic-gate 	 */
2247c478bd9Sstevel@tonic-gate 	if (key < TSD_NFAST)
2257c478bd9Sstevel@tonic-gate 		return (curthread->ul_ftsd[key]);
2267c478bd9Sstevel@tonic-gate 
2277c478bd9Sstevel@tonic-gate 	if ((stsd = curthread->ul_stsd) != NULL && key < stsd->tsd_nalloc)
2287c478bd9Sstevel@tonic-gate 		return (stsd->tsd_data[key]);
2297c478bd9Sstevel@tonic-gate 
2307c478bd9Sstevel@tonic-gate 	return (NULL);
2317c478bd9Sstevel@tonic-gate }
2327c478bd9Sstevel@tonic-gate 
2337c478bd9Sstevel@tonic-gate int
thr_getspecific(thread_key_t key,void ** valuep)234*7257d1b4Sraf thr_getspecific(thread_key_t key, void **valuep)
2357c478bd9Sstevel@tonic-gate {
2367c478bd9Sstevel@tonic-gate 	tsd_t *stsd;
2377c478bd9Sstevel@tonic-gate 
2387c478bd9Sstevel@tonic-gate 	/*
2397c478bd9Sstevel@tonic-gate 	 * Amazingly, some application code (and worse, some particularly
2407c478bd9Sstevel@tonic-gate 	 * fugly Solaris library code) _relies_ on the fact that 0 is always
2417c478bd9Sstevel@tonic-gate 	 * an invalid key.  To preserve this semantic, 0 is never returned
2427c478bd9Sstevel@tonic-gate 	 * as a key from thr_/pthread_key_create(); we explicitly check
2437c478bd9Sstevel@tonic-gate 	 * for it here and return EINVAL.
2447c478bd9Sstevel@tonic-gate 	 */
2457c478bd9Sstevel@tonic-gate 	if (key == 0)
2467c478bd9Sstevel@tonic-gate 		return (EINVAL);
2477c478bd9Sstevel@tonic-gate 
2487c478bd9Sstevel@tonic-gate 	if (key < TSD_NFAST)
2497c478bd9Sstevel@tonic-gate 		*valuep = curthread->ul_ftsd[key];
2507c478bd9Sstevel@tonic-gate 	else if ((stsd = curthread->ul_stsd) != NULL && key < stsd->tsd_nalloc)
2517c478bd9Sstevel@tonic-gate 		*valuep = stsd->tsd_data[key];
2527c478bd9Sstevel@tonic-gate 	else
2537c478bd9Sstevel@tonic-gate 		*valuep = NULL;
2547c478bd9Sstevel@tonic-gate 
2557c478bd9Sstevel@tonic-gate 	return (0);
2567c478bd9Sstevel@tonic-gate }
2577c478bd9Sstevel@tonic-gate 
2587c478bd9Sstevel@tonic-gate /*
259*7257d1b4Sraf  * We call thr_setspecific_slow() when the key specified
2607c478bd9Sstevel@tonic-gate  * is beyond the current thread's currently allocated range.
2617c478bd9Sstevel@tonic-gate  * This case is in a separate function because we want
2627c478bd9Sstevel@tonic-gate  * the compiler to optimize for the common case.
2637c478bd9Sstevel@tonic-gate  */
2647c478bd9Sstevel@tonic-gate static int
thr_setspecific_slow(thread_key_t key,void * value)265*7257d1b4Sraf thr_setspecific_slow(thread_key_t key, void *value)
2667c478bd9Sstevel@tonic-gate {
2677c478bd9Sstevel@tonic-gate 	ulwp_t *self = curthread;
2687c478bd9Sstevel@tonic-gate 	tsd_metadata_t *tsdm = &self->ul_uberdata->tsd_metadata;
2697c478bd9Sstevel@tonic-gate 	tsd_t *stsd;
2707c478bd9Sstevel@tonic-gate 	tsd_t *ntsd;
2717c478bd9Sstevel@tonic-gate 	uint_t nkeys;
2727c478bd9Sstevel@tonic-gate 
2737c478bd9Sstevel@tonic-gate 	/*
2747c478bd9Sstevel@tonic-gate 	 * It isn't necessary to grab locks in this path;
2757c478bd9Sstevel@tonic-gate 	 * tsdm->tsdm_nused can only increase.
2767c478bd9Sstevel@tonic-gate 	 */
2777c478bd9Sstevel@tonic-gate 	if (key >= tsdm->tsdm_nused)
2787c478bd9Sstevel@tonic-gate 		return (EINVAL);
2797c478bd9Sstevel@tonic-gate 
2807c478bd9Sstevel@tonic-gate 	/*
2817c478bd9Sstevel@tonic-gate 	 * We would like to test (tsdm->tsdm_destro[key] == TSD_UNALLOCATED)
2827c478bd9Sstevel@tonic-gate 	 * here but that would require acquiring tsdm->tsdm_lock and we
2837c478bd9Sstevel@tonic-gate 	 * want to avoid locks in this path.
2847c478bd9Sstevel@tonic-gate 	 *
2857c478bd9Sstevel@tonic-gate 	 * We have a key which is (or at least _was_) valid.  If this key
2867c478bd9Sstevel@tonic-gate 	 * is later deleted (or indeed, is deleted before we set the value),
2877c478bd9Sstevel@tonic-gate 	 * we don't care; such a condition would indicate an application
2887c478bd9Sstevel@tonic-gate 	 * race for which POSIX thankfully leaves the behavior unspecified.
2897c478bd9Sstevel@tonic-gate 	 *
2907c478bd9Sstevel@tonic-gate 	 * First, determine our new size.  To avoid allocating more than we
2917c478bd9Sstevel@tonic-gate 	 * have to, continue doubling our size only until the new key fits.
2927c478bd9Sstevel@tonic-gate 	 * stsd->tsd_nalloc must always be a power of two.
2937c478bd9Sstevel@tonic-gate 	 */
2947c478bd9Sstevel@tonic-gate 	nkeys = ((stsd = self->ul_stsd) != NULL)? stsd->tsd_nalloc : 8;
2957c478bd9Sstevel@tonic-gate 	for (; key >= nkeys; nkeys <<= 1)
2967c478bd9Sstevel@tonic-gate 		continue;
2977c478bd9Sstevel@tonic-gate 
2987c478bd9Sstevel@tonic-gate 	/*
2997c478bd9Sstevel@tonic-gate 	 * Allocate the new TSD.
3007c478bd9Sstevel@tonic-gate 	 */
3017c478bd9Sstevel@tonic-gate 	if ((ntsd = lmalloc(nkeys * sizeof (void *))) == NULL)
3027c478bd9Sstevel@tonic-gate 		return (ENOMEM);
3037c478bd9Sstevel@tonic-gate 
3047c478bd9Sstevel@tonic-gate 	if (stsd != NULL) {
3057c478bd9Sstevel@tonic-gate 		/*
3067c478bd9Sstevel@tonic-gate 		 * Copy the old TSD across to the new.
3077c478bd9Sstevel@tonic-gate 		 */
3088cd45542Sraf 		(void) memcpy(ntsd, stsd, stsd->tsd_nalloc * sizeof (void *));
3097c478bd9Sstevel@tonic-gate 		lfree(stsd, stsd->tsd_nalloc * sizeof (void *));
3107c478bd9Sstevel@tonic-gate 	}
3117c478bd9Sstevel@tonic-gate 
3127c478bd9Sstevel@tonic-gate 	ntsd->tsd_nalloc = nkeys;
3137c478bd9Sstevel@tonic-gate 	ntsd->tsd_data[key] = value;
3147c478bd9Sstevel@tonic-gate 	self->ul_stsd = ntsd;
3157c478bd9Sstevel@tonic-gate 
3167c478bd9Sstevel@tonic-gate 	return (0);
3177c478bd9Sstevel@tonic-gate }
3187c478bd9Sstevel@tonic-gate 
3197c478bd9Sstevel@tonic-gate int
thr_setspecific(thread_key_t key,void * value)320*7257d1b4Sraf thr_setspecific(thread_key_t key, void *value)
3217c478bd9Sstevel@tonic-gate {
3227c478bd9Sstevel@tonic-gate 	tsd_t *stsd;
3237c478bd9Sstevel@tonic-gate 	int ret;
3247c478bd9Sstevel@tonic-gate 	ulwp_t *self = curthread;
3257c478bd9Sstevel@tonic-gate 
3267c478bd9Sstevel@tonic-gate 	/*
327*7257d1b4Sraf 	 * See the comment in thr_getspecific(), above.
3287c478bd9Sstevel@tonic-gate 	 */
3297c478bd9Sstevel@tonic-gate 	if (key == 0)
3307c478bd9Sstevel@tonic-gate 		return (EINVAL);
3317c478bd9Sstevel@tonic-gate 
3327c478bd9Sstevel@tonic-gate 	if (key < TSD_NFAST) {
3337c478bd9Sstevel@tonic-gate 		curthread->ul_ftsd[key] = value;
3347c478bd9Sstevel@tonic-gate 		return (0);
3357c478bd9Sstevel@tonic-gate 	}
3367c478bd9Sstevel@tonic-gate 
3377c478bd9Sstevel@tonic-gate 	if ((stsd = curthread->ul_stsd) != NULL && key < stsd->tsd_nalloc) {
3387c478bd9Sstevel@tonic-gate 		stsd->tsd_data[key] = value;
3397c478bd9Sstevel@tonic-gate 		return (0);
3407c478bd9Sstevel@tonic-gate 	}
3417c478bd9Sstevel@tonic-gate 
3427c478bd9Sstevel@tonic-gate 	/*
3437c478bd9Sstevel@tonic-gate 	 * This is a critical region since we are dealing with memory
3447c478bd9Sstevel@tonic-gate 	 * allocation and free. Similar protection required in tsd_free().
3457c478bd9Sstevel@tonic-gate 	 */
3467c478bd9Sstevel@tonic-gate 	enter_critical(self);
347*7257d1b4Sraf 	ret = thr_setspecific_slow(key, value);
3487c478bd9Sstevel@tonic-gate 	exit_critical(self);
3497c478bd9Sstevel@tonic-gate 	return (ret);
3507c478bd9Sstevel@tonic-gate }
3517c478bd9Sstevel@tonic-gate 
352*7257d1b4Sraf int
pthread_setspecific(pthread_key_t key,const void * value)353*7257d1b4Sraf pthread_setspecific(pthread_key_t key, const void *value)
354*7257d1b4Sraf {
355*7257d1b4Sraf 	return (thr_setspecific(key, (void *)value));
356*7257d1b4Sraf }
357*7257d1b4Sraf 
3587c478bd9Sstevel@tonic-gate /*
3597c478bd9Sstevel@tonic-gate  * Contract-private interface for java.  See PSARC/2003/159
3607c478bd9Sstevel@tonic-gate  *
3617c478bd9Sstevel@tonic-gate  * If the key falls within the TSD_NFAST range, return a non-negative
3627c478bd9Sstevel@tonic-gate  * offset that can be used by the caller to fetch the TSD data value
3637c478bd9Sstevel@tonic-gate  * directly out of the thread structure using %g7 (sparc) or %gs (x86).
3647c478bd9Sstevel@tonic-gate  * With the advent of TLS, %g7 and %gs are part of the ABI, even though
3657c478bd9Sstevel@tonic-gate  * the definition of the thread structure itself (ulwp_t) is private.
3667c478bd9Sstevel@tonic-gate  *
3677c478bd9Sstevel@tonic-gate  * We guarantee that the offset returned on sparc will fit within
3687c478bd9Sstevel@tonic-gate  * a SIMM13 field (that is, it is less than 2048).
3697c478bd9Sstevel@tonic-gate  *
3707c478bd9Sstevel@tonic-gate  * On failure (key is not in the TSD_NFAST range), return -1.
3717c478bd9Sstevel@tonic-gate  */
3727c478bd9Sstevel@tonic-gate ptrdiff_t
_thr_slot_offset(thread_key_t key)3737c478bd9Sstevel@tonic-gate _thr_slot_offset(thread_key_t key)
3747c478bd9Sstevel@tonic-gate {
3757c478bd9Sstevel@tonic-gate 	if (key != 0 && key < TSD_NFAST)
3767c478bd9Sstevel@tonic-gate 		return ((ptrdiff_t)offsetof(ulwp_t, ul_ftsd[key]));
3777c478bd9Sstevel@tonic-gate 	return (-1);
3787c478bd9Sstevel@tonic-gate }
3797c478bd9Sstevel@tonic-gate 
3807c478bd9Sstevel@tonic-gate /*
3817c478bd9Sstevel@tonic-gate  * This is called by _thrp_exit() to apply destructors to the thread's tsd.
3827c478bd9Sstevel@tonic-gate  */
3837c478bd9Sstevel@tonic-gate void
tsd_exit()3847c478bd9Sstevel@tonic-gate tsd_exit()
3857c478bd9Sstevel@tonic-gate {
3867c478bd9Sstevel@tonic-gate 	ulwp_t *self = curthread;
3877c478bd9Sstevel@tonic-gate 	tsd_metadata_t *tsdm = &self->ul_uberdata->tsd_metadata;
3887c478bd9Sstevel@tonic-gate 	thread_key_t key;
3897c478bd9Sstevel@tonic-gate 	int recheck;
3907c478bd9Sstevel@tonic-gate 	void *val;
3917c478bd9Sstevel@tonic-gate 	void (*func)(void *);
3927c478bd9Sstevel@tonic-gate 
3937c478bd9Sstevel@tonic-gate 	lmutex_lock(&tsdm->tsdm_lock);
3947c478bd9Sstevel@tonic-gate 
3957c478bd9Sstevel@tonic-gate 	do {
3967c478bd9Sstevel@tonic-gate 		recheck = 0;
3977c478bd9Sstevel@tonic-gate 
3987c478bd9Sstevel@tonic-gate 		for (key = 1; key < TSD_NFAST &&
3997c478bd9Sstevel@tonic-gate 		    key < tsdm->tsdm_nused; key++) {
4007c478bd9Sstevel@tonic-gate 			if ((func = tsdm->tsdm_destro[key]) != NULL &&
4017c478bd9Sstevel@tonic-gate 			    func != TSD_UNALLOCATED &&
4027c478bd9Sstevel@tonic-gate 			    (val = self->ul_ftsd[key]) != NULL) {
4037c478bd9Sstevel@tonic-gate 				self->ul_ftsd[key] = NULL;
4047c478bd9Sstevel@tonic-gate 				lmutex_unlock(&tsdm->tsdm_lock);
4057c478bd9Sstevel@tonic-gate 				(*func)(val);
4067c478bd9Sstevel@tonic-gate 				lmutex_lock(&tsdm->tsdm_lock);
4077c478bd9Sstevel@tonic-gate 				recheck = 1;
4087c478bd9Sstevel@tonic-gate 			}
4097c478bd9Sstevel@tonic-gate 		}
4107c478bd9Sstevel@tonic-gate 
4117c478bd9Sstevel@tonic-gate 		if (self->ul_stsd == NULL)
4127c478bd9Sstevel@tonic-gate 			continue;
4137c478bd9Sstevel@tonic-gate 
4147c478bd9Sstevel@tonic-gate 		/*
4157c478bd9Sstevel@tonic-gate 		 * Any of these destructors could cause us to grow the number
4167c478bd9Sstevel@tonic-gate 		 * TSD keys in the slow TSD; we cannot cache the slow TSD
4177c478bd9Sstevel@tonic-gate 		 * pointer through this loop.
4187c478bd9Sstevel@tonic-gate 		 */
4197c478bd9Sstevel@tonic-gate 		for (; key < self->ul_stsd->tsd_nalloc &&
4207c478bd9Sstevel@tonic-gate 		    key < tsdm->tsdm_nused; key++) {
4217c478bd9Sstevel@tonic-gate 			if ((func = tsdm->tsdm_destro[key]) != NULL &&
4227c478bd9Sstevel@tonic-gate 			    func != TSD_UNALLOCATED &&
4237c478bd9Sstevel@tonic-gate 			    (val = self->ul_stsd->tsd_data[key]) != NULL) {
4247c478bd9Sstevel@tonic-gate 				self->ul_stsd->tsd_data[key] = NULL;
4257c478bd9Sstevel@tonic-gate 				lmutex_unlock(&tsdm->tsdm_lock);
4267c478bd9Sstevel@tonic-gate 				(*func)(val);
4277c478bd9Sstevel@tonic-gate 				lmutex_lock(&tsdm->tsdm_lock);
4287c478bd9Sstevel@tonic-gate 				recheck = 1;
4297c478bd9Sstevel@tonic-gate 			}
4307c478bd9Sstevel@tonic-gate 		}
4317c478bd9Sstevel@tonic-gate 	} while (recheck);
4327c478bd9Sstevel@tonic-gate 
4337c478bd9Sstevel@tonic-gate 	lmutex_unlock(&tsdm->tsdm_lock);
4347c478bd9Sstevel@tonic-gate 
4357c478bd9Sstevel@tonic-gate 	/*
4367c478bd9Sstevel@tonic-gate 	 * We're done; if we have slow TSD, we need to free it.
4377c478bd9Sstevel@tonic-gate 	 */
4387c478bd9Sstevel@tonic-gate 	tsd_free(self);
4397c478bd9Sstevel@tonic-gate }
4407c478bd9Sstevel@tonic-gate 
4417c478bd9Sstevel@tonic-gate void
tsd_free(ulwp_t * ulwp)4427c478bd9Sstevel@tonic-gate tsd_free(ulwp_t *ulwp)
4437c478bd9Sstevel@tonic-gate {
4447c478bd9Sstevel@tonic-gate 	tsd_t *stsd;
4457c478bd9Sstevel@tonic-gate 	ulwp_t *self = curthread;
4467c478bd9Sstevel@tonic-gate 
4477c478bd9Sstevel@tonic-gate 	enter_critical(self);
4487c478bd9Sstevel@tonic-gate 	if ((stsd = ulwp->ul_stsd) != NULL)
4497c478bd9Sstevel@tonic-gate 		lfree(stsd, stsd->tsd_nalloc * sizeof (void *));
4507c478bd9Sstevel@tonic-gate 	ulwp->ul_stsd = NULL;
4517c478bd9Sstevel@tonic-gate 	exit_critical(self);
4527c478bd9Sstevel@tonic-gate }
453