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