xref: /dflybsd-src/lib/libthread_xu/thread/thr_spec.c (revision 171835807871f68c36f75ff84e1d6f7df4683df0)
171b3fa15SDavid Xu /*
271b3fa15SDavid Xu  * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>.
371b3fa15SDavid Xu  * All rights reserved.
471b3fa15SDavid Xu  *
571b3fa15SDavid Xu  * Redistribution and use in source and binary forms, with or without
671b3fa15SDavid Xu  * modification, are permitted provided that the following conditions
771b3fa15SDavid Xu  * are met:
871b3fa15SDavid Xu  * 1. Redistributions of source code must retain the above copyright
971b3fa15SDavid Xu  *    notice, this list of conditions and the following disclaimer.
1071b3fa15SDavid Xu  * 2. Redistributions in binary form must reproduce the above copyright
1171b3fa15SDavid Xu  *    notice, this list of conditions and the following disclaimer in the
1271b3fa15SDavid Xu  *    documentation and/or other materials provided with the distribution.
13d3b15642Szrj  * 3. Neither the name of the author nor the names of any co-contributors
1471b3fa15SDavid Xu  *    may be used to endorse or promote products derived from this software
1571b3fa15SDavid Xu  *    without specific prior written permission.
1671b3fa15SDavid Xu  *
1771b3fa15SDavid Xu  * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
1871b3fa15SDavid Xu  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1971b3fa15SDavid Xu  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2071b3fa15SDavid Xu  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2171b3fa15SDavid Xu  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2271b3fa15SDavid Xu  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2371b3fa15SDavid Xu  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2471b3fa15SDavid Xu  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2571b3fa15SDavid Xu  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2671b3fa15SDavid Xu  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2771b3fa15SDavid Xu  * SUCH DAMAGE.
2871b3fa15SDavid Xu  *
2971b3fa15SDavid Xu  */
309e2ee207SJoerg Sonnenberger 
31fc71f871SDavid Xu #include "namespace.h"
329e2ee207SJoerg Sonnenberger #include <machine/tls.h>
3371b3fa15SDavid Xu #include <signal.h>
3471b3fa15SDavid Xu #include <stdlib.h>
3571b3fa15SDavid Xu #include <string.h>
3671b3fa15SDavid Xu #include <errno.h>
3771b3fa15SDavid Xu #include <pthread.h>
38fc71f871SDavid Xu #include "un-namespace.h"
39fc71f871SDavid Xu 
4071b3fa15SDavid Xu #include "thr_private.h"
4171b3fa15SDavid Xu 
4271b3fa15SDavid Xu struct pthread_key _thread_keytable[PTHREAD_KEYS_MAX];
43e8382b15SDavid Xu umtx_t	_keytable_lock;
448356845aSMatthew Dillon static size_t _pthread_specific_bytes;
4571b3fa15SDavid Xu 
4671b3fa15SDavid Xu int
_pthread_key_create(pthread_key_t * key,void (* destructor)(void *))4771b3fa15SDavid Xu _pthread_key_create(pthread_key_t *key, void (*destructor) (void *))
4871b3fa15SDavid Xu {
49940be950Szrj 	pthread_t curthread;
5071b3fa15SDavid Xu 	int i;
5171b3fa15SDavid Xu 
52a886e383Szrj 	/* User program might be preparing to call pthread_create() */
53a886e383Szrj 	_thr_check_init();
54a886e383Szrj 
55a886e383Szrj 	curthread = tls_get_curthread();
56a886e383Szrj 
5771b3fa15SDavid Xu 	/* Lock the key table: */
5871b3fa15SDavid Xu 	THR_LOCK_ACQUIRE(curthread, &_keytable_lock);
59*17183580SMatthew Dillon 	for (i = 1; i < PTHREAD_KEYS_MAX; i++) {
6071b3fa15SDavid Xu 		if (_thread_keytable[i].allocated == 0) {
6171b3fa15SDavid Xu 			_thread_keytable[i].allocated = 1;
6271b3fa15SDavid Xu 			_thread_keytable[i].destructor = destructor;
6371b3fa15SDavid Xu 			_thread_keytable[i].seqno++;
6471b3fa15SDavid Xu 
6571b3fa15SDavid Xu 			/* Unlock the key table: */
6671b3fa15SDavid Xu 			THR_LOCK_RELEASE(curthread, &_keytable_lock);
6771b3fa15SDavid Xu 			*key = i;
6871b3fa15SDavid Xu 			return (0);
6971b3fa15SDavid Xu 		}
7071b3fa15SDavid Xu 
7171b3fa15SDavid Xu 	}
7271b3fa15SDavid Xu 	/* Unlock the key table: */
7371b3fa15SDavid Xu 	THR_LOCK_RELEASE(curthread, &_keytable_lock);
7471b3fa15SDavid Xu 	return (EAGAIN);
7571b3fa15SDavid Xu }
7671b3fa15SDavid Xu 
7771b3fa15SDavid Xu int
_pthread_key_delete(pthread_key_t key)7871b3fa15SDavid Xu _pthread_key_delete(pthread_key_t key)
7971b3fa15SDavid Xu {
80940be950Szrj 	pthread_t curthread = tls_get_curthread();
8171b3fa15SDavid Xu 	int ret = 0;
8271b3fa15SDavid Xu 
8371b3fa15SDavid Xu 	if ((unsigned int)key < PTHREAD_KEYS_MAX) {
8471b3fa15SDavid Xu 		/* Lock the key table: */
8571b3fa15SDavid Xu 		THR_LOCK_ACQUIRE(curthread, &_keytable_lock);
8671b3fa15SDavid Xu 
8771b3fa15SDavid Xu 		if (_thread_keytable[key].allocated)
8871b3fa15SDavid Xu 			_thread_keytable[key].allocated = 0;
8971b3fa15SDavid Xu 		else
9071b3fa15SDavid Xu 			ret = EINVAL;
9171b3fa15SDavid Xu 
9271b3fa15SDavid Xu 		/* Unlock the key table: */
9371b3fa15SDavid Xu 		THR_LOCK_RELEASE(curthread, &_keytable_lock);
9471b3fa15SDavid Xu 	} else
9571b3fa15SDavid Xu 		ret = EINVAL;
9671b3fa15SDavid Xu 	return (ret);
9771b3fa15SDavid Xu }
9871b3fa15SDavid Xu 
9971b3fa15SDavid Xu void
_thread_cleanupspecific(void)10071b3fa15SDavid Xu _thread_cleanupspecific(void)
10171b3fa15SDavid Xu {
102940be950Szrj 	pthread_t	curthread = tls_get_curthread();
10371b3fa15SDavid Xu 	void		(*destructor)( void *);
104fc71f871SDavid Xu 	const void	*data = NULL;
10571b3fa15SDavid Xu 	int		key;
10671b3fa15SDavid Xu 	int		i;
10771b3fa15SDavid Xu 
10871b3fa15SDavid Xu 	if (curthread->specific == NULL)
10971b3fa15SDavid Xu 		return;
11071b3fa15SDavid Xu 
11171b3fa15SDavid Xu 	/* Lock the key table: */
11271b3fa15SDavid Xu 	THR_LOCK_ACQUIRE(curthread, &_keytable_lock);
11371b3fa15SDavid Xu 	for (i = 0; (i < PTHREAD_DESTRUCTOR_ITERATIONS) &&
11471b3fa15SDavid Xu 	    (curthread->specific_data_count > 0); i++) {
11571b3fa15SDavid Xu 		for (key = 0; (key < PTHREAD_KEYS_MAX) &&
11671b3fa15SDavid Xu 		    (curthread->specific_data_count > 0); key++) {
11771b3fa15SDavid Xu 			destructor = NULL;
11871b3fa15SDavid Xu 
11971b3fa15SDavid Xu 			if (_thread_keytable[key].allocated &&
12071b3fa15SDavid Xu 			    (curthread->specific[key].data != NULL)) {
12171b3fa15SDavid Xu 				if (curthread->specific[key].seqno ==
12271b3fa15SDavid Xu 				    _thread_keytable[key].seqno) {
12319451dc5Szrj 					data = curthread->specific[key].data;
12471b3fa15SDavid Xu 					destructor = _thread_keytable[key].destructor;
12571b3fa15SDavid Xu 				}
12671b3fa15SDavid Xu 				curthread->specific[key].data = NULL;
12771b3fa15SDavid Xu 				curthread->specific_data_count--;
12836c5717fSAycan iRiCAN 			} else if (curthread->specific[key].data != NULL) {
12936c5717fSAycan iRiCAN 				/*
13036c5717fSAycan iRiCAN 				 * This can happen if the key is deleted via
13136c5717fSAycan iRiCAN 				 * pthread_key_delete without first setting the value
13236c5717fSAycan iRiCAN 				 * to NULL in all threads. POSIX says that the
13336c5717fSAycan iRiCAN 				 * destructor is not invoked in this case.
13436c5717fSAycan iRiCAN 				 */
13536c5717fSAycan iRiCAN 				curthread->specific[key].data = NULL;
13636c5717fSAycan iRiCAN 				curthread->specific_data_count--;
13771b3fa15SDavid Xu 			}
13871b3fa15SDavid Xu 
13971b3fa15SDavid Xu 			/*
14036c5717fSAycan iRiCAN 			 * If there is a destructor, call it
14171b3fa15SDavid Xu 			 * with the key table entry unlocked:
14271b3fa15SDavid Xu 			 */
14371b3fa15SDavid Xu 			if (destructor != NULL) {
14471b3fa15SDavid Xu 				/*
14571b3fa15SDavid Xu 				 * Don't hold the lock while calling the
14671b3fa15SDavid Xu 				 * destructor:
14771b3fa15SDavid Xu 				 */
14871b3fa15SDavid Xu 				THR_LOCK_RELEASE(curthread, &_keytable_lock);
149fc71f871SDavid Xu 				destructor(__DECONST(void *, data));
15071b3fa15SDavid Xu 				THR_LOCK_ACQUIRE(curthread, &_keytable_lock);
15171b3fa15SDavid Xu 			}
15271b3fa15SDavid Xu 		}
15371b3fa15SDavid Xu 	}
15471b3fa15SDavid Xu 	THR_LOCK_RELEASE(curthread, &_keytable_lock);
1558356845aSMatthew Dillon 
1568356845aSMatthew Dillon 	munmap(curthread->specific, _pthread_specific_bytes);
15771b3fa15SDavid Xu 	curthread->specific = NULL;
1588356845aSMatthew Dillon 
1598356845aSMatthew Dillon 	if (curthread->specific_data_count > 0) {
16071b3fa15SDavid Xu 		stderr_debug("Thread %p has exited with leftover "
1618356845aSMatthew Dillon 			     "thread-specific data after %d destructor "
1628356845aSMatthew Dillon 			     "iterations\n",
16371b3fa15SDavid Xu 			     curthread, PTHREAD_DESTRUCTOR_ITERATIONS);
16471b3fa15SDavid Xu 	}
1658356845aSMatthew Dillon }
16671b3fa15SDavid Xu 
16771b3fa15SDavid Xu static inline struct pthread_specific_elem *
pthread_key_allocate_data(void)16871b3fa15SDavid Xu pthread_key_allocate_data(void)
16971b3fa15SDavid Xu {
17071b3fa15SDavid Xu 	struct pthread_specific_elem *new_data;
1718356845aSMatthew Dillon 	size_t bytes;
1728356845aSMatthew Dillon 	size_t pgmask;
17371b3fa15SDavid Xu 
1748356845aSMatthew Dillon 	bytes = _pthread_specific_bytes;
1758356845aSMatthew Dillon 	if (bytes == 0) {
1768356845aSMatthew Dillon 		pgmask = getpagesize() - 1;
1778356845aSMatthew Dillon 		bytes = sizeof(struct pthread_specific_elem) * PTHREAD_KEYS_MAX;
1788356845aSMatthew Dillon 		bytes = (bytes + pgmask) & ~pgmask;
1798356845aSMatthew Dillon 		_pthread_specific_bytes = bytes;
18071b3fa15SDavid Xu 	}
1818356845aSMatthew Dillon 	new_data = mmap(NULL, bytes, PROT_READ | PROT_WRITE,
1828356845aSMatthew Dillon 			MAP_ANON | MAP_PRIVATE, -1, 0);
1838356845aSMatthew Dillon 	if (new_data == MAP_FAILED)
1848356845aSMatthew Dillon 		new_data = NULL;
1858356845aSMatthew Dillon 
18671b3fa15SDavid Xu 	return (new_data);
18771b3fa15SDavid Xu }
18871b3fa15SDavid Xu 
18971b3fa15SDavid Xu int
_pthread_setspecific(pthread_key_t key,const void * value)19071b3fa15SDavid Xu _pthread_setspecific(pthread_key_t key, const void *value)
19171b3fa15SDavid Xu {
192940be950Szrj 	pthread_t	pthread;
19371b3fa15SDavid Xu 	int		ret = 0;
19471b3fa15SDavid Xu 
19571b3fa15SDavid Xu 	/* Point to the running thread: */
1969e2ee207SJoerg Sonnenberger 	pthread = tls_get_curthread();
19771b3fa15SDavid Xu 
1988356845aSMatthew Dillon 	if (pthread->specific ||
1998356845aSMatthew Dillon 	    (pthread->specific = pthread_key_allocate_data()) != NULL) {
20071b3fa15SDavid Xu 		if ((unsigned int)key < PTHREAD_KEYS_MAX) {
20171b3fa15SDavid Xu 			if (_thread_keytable[key].allocated) {
20271b3fa15SDavid Xu 				if (pthread->specific[key].data == NULL) {
20371b3fa15SDavid Xu 					if (value != NULL)
20471b3fa15SDavid Xu 						pthread->specific_data_count++;
20571b3fa15SDavid Xu 				} else if (value == NULL)
20671b3fa15SDavid Xu 					pthread->specific_data_count--;
20771b3fa15SDavid Xu 				pthread->specific[key].data = value;
20871b3fa15SDavid Xu 				pthread->specific[key].seqno =
20971b3fa15SDavid Xu 				    _thread_keytable[key].seqno;
21071b3fa15SDavid Xu 				ret = 0;
2118356845aSMatthew Dillon 			} else {
21271b3fa15SDavid Xu 				ret = EINVAL;
2138356845aSMatthew Dillon 			}
2148356845aSMatthew Dillon 		} else {
21571b3fa15SDavid Xu 			ret = EINVAL;
2168356845aSMatthew Dillon 		}
2178356845aSMatthew Dillon 	} else {
21871b3fa15SDavid Xu 		ret = ENOMEM;
2198356845aSMatthew Dillon 	}
22071b3fa15SDavid Xu 	return (ret);
22171b3fa15SDavid Xu }
22271b3fa15SDavid Xu 
22371b3fa15SDavid Xu void *
_pthread_getspecific(pthread_key_t key)22471b3fa15SDavid Xu _pthread_getspecific(pthread_key_t key)
22571b3fa15SDavid Xu {
226940be950Szrj 	pthread_t	pthread;
227fc71f871SDavid Xu 	const void	*data;
22871b3fa15SDavid Xu 
22971b3fa15SDavid Xu 	/* Point to the running thread: */
2309e2ee207SJoerg Sonnenberger 	pthread = tls_get_curthread();
23171b3fa15SDavid Xu 
23271b3fa15SDavid Xu 	/* Check if there is specific data: */
23371b3fa15SDavid Xu 	if (pthread->specific != NULL && (unsigned int)key < PTHREAD_KEYS_MAX) {
23471b3fa15SDavid Xu 		/* Check if this key has been used before: */
23571b3fa15SDavid Xu 		if (_thread_keytable[key].allocated &&
23671b3fa15SDavid Xu 		    (pthread->specific[key].seqno == _thread_keytable[key].seqno)) {
23771b3fa15SDavid Xu 			/* Return the value: */
238fc71f871SDavid Xu 			data = pthread->specific[key].data;
23971b3fa15SDavid Xu 		} else {
24071b3fa15SDavid Xu 			/*
24171b3fa15SDavid Xu 			 * This key has not been used before, so return NULL
24219451dc5Szrj 			 * instead.
24371b3fa15SDavid Xu 			 */
24471b3fa15SDavid Xu 			data = NULL;
24571b3fa15SDavid Xu 		}
24671b3fa15SDavid Xu 	} else
24771b3fa15SDavid Xu 		/* No specific data has been created, so just return NULL: */
24871b3fa15SDavid Xu 		data = NULL;
249fc71f871SDavid Xu 	return __DECONST(void *, data);
25071b3fa15SDavid Xu }
2515a1048c8SDavid Xu 
2525a1048c8SDavid Xu __strong_reference(_pthread_key_create, pthread_key_create);
2535a1048c8SDavid Xu __strong_reference(_pthread_key_delete, pthread_key_delete);
2545a1048c8SDavid Xu __strong_reference(_pthread_getspecific, pthread_getspecific);
2555a1048c8SDavid Xu __strong_reference(_pthread_setspecific, pthread_setspecific);
256