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