1112918Sjeff/* 2112918Sjeff * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. 3112918Sjeff * All rights reserved. 4112918Sjeff * 5112918Sjeff * Redistribution and use in source and binary forms, with or without 6112918Sjeff * modification, are permitted provided that the following conditions 7112918Sjeff * are met: 8112918Sjeff * 1. Redistributions of source code must retain the above copyright 9112918Sjeff * notice, this list of conditions and the following disclaimer. 10112918Sjeff * 2. Redistributions in binary form must reproduce the above copyright 11112918Sjeff * notice, this list of conditions and the following disclaimer in the 12112918Sjeff * documentation and/or other materials provided with the distribution. 13165967Simp * 3. Neither the name of the author nor the names of any co-contributors 14112918Sjeff * may be used to endorse or promote products derived from this software 15112918Sjeff * without specific prior written permission. 16112918Sjeff * 17112918Sjeff * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND 18112918Sjeff * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19112918Sjeff * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20112918Sjeff * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21112918Sjeff * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22112918Sjeff * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23112918Sjeff * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24112918Sjeff * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25112918Sjeff * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26112918Sjeff * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27112918Sjeff * SUCH DAMAGE. 28112918Sjeff * 29112918Sjeff * $FreeBSD$ 30112918Sjeff */ 31144518Sdavidxu 32157457Sdavidxu#include "namespace.h" 33112918Sjeff#include <signal.h> 34112918Sjeff#include <stdlib.h> 35112918Sjeff#include <string.h> 36112918Sjeff#include <errno.h> 37112918Sjeff#include <pthread.h> 38157457Sdavidxu#include "un-namespace.h" 39211860Sdavidxu#include "libc_private.h" 40144518Sdavidxu 41112918Sjeff#include "thr_private.h" 42112918Sjeff 43112918Sjeff/* Static variables: */ 44144518Sdavidxustruct pthread_key _thread_keytable[PTHREAD_KEYS_MAX]; 45112918Sjeff 46112918Sjeff__weak_reference(_pthread_key_create, pthread_key_create); 47112918Sjeff__weak_reference(_pthread_key_delete, pthread_key_delete); 48112918Sjeff__weak_reference(_pthread_getspecific, pthread_getspecific); 49112918Sjeff__weak_reference(_pthread_setspecific, pthread_setspecific); 50112918Sjeff 51112918Sjeff 52112918Sjeffint 53144518Sdavidxu_pthread_key_create(pthread_key_t *key, void (*destructor) (void *)) 54112918Sjeff{ 55173394Smarius struct pthread *curthread; 56144518Sdavidxu int i; 57112918Sjeff 58173394Smarius _thr_check_init(); 59173394Smarius 60173394Smarius curthread = _get_curthread(); 61173394Smarius 62144518Sdavidxu /* Lock the key table: */ 63144518Sdavidxu THR_LOCK_ACQUIRE(curthread, &_keytable_lock); 64144518Sdavidxu for (i = 0; i < PTHREAD_KEYS_MAX; i++) { 65112918Sjeff 66144518Sdavidxu if (_thread_keytable[i].allocated == 0) { 67144518Sdavidxu _thread_keytable[i].allocated = 1; 68144518Sdavidxu _thread_keytable[i].destructor = destructor; 69144518Sdavidxu _thread_keytable[i].seqno++; 70144518Sdavidxu 71144518Sdavidxu /* Unlock the key table: */ 72144518Sdavidxu THR_LOCK_RELEASE(curthread, &_keytable_lock); 73262221Sjhb *key = i + 1; 74112918Sjeff return (0); 75112918Sjeff } 76112918Sjeff 77112918Sjeff } 78144518Sdavidxu /* Unlock the key table: */ 79144518Sdavidxu THR_LOCK_RELEASE(curthread, &_keytable_lock); 80112918Sjeff return (EAGAIN); 81112918Sjeff} 82112918Sjeff 83112918Sjeffint 84262221Sjhb_pthread_key_delete(pthread_key_t userkey) 85112918Sjeff{ 86144518Sdavidxu struct pthread *curthread = _get_curthread(); 87262221Sjhb int key = userkey - 1; 88112918Sjeff int ret = 0; 89112918Sjeff 90144518Sdavidxu if ((unsigned int)key < PTHREAD_KEYS_MAX) { 91144518Sdavidxu /* Lock the key table: */ 92144518Sdavidxu THR_LOCK_ACQUIRE(curthread, &_keytable_lock); 93112918Sjeff 94144518Sdavidxu if (_thread_keytable[key].allocated) 95144518Sdavidxu _thread_keytable[key].allocated = 0; 96112918Sjeff else 97112918Sjeff ret = EINVAL; 98112918Sjeff 99144518Sdavidxu /* Unlock the key table: */ 100144518Sdavidxu THR_LOCK_RELEASE(curthread, &_keytable_lock); 101112918Sjeff } else 102112918Sjeff ret = EINVAL; 103112918Sjeff return (ret); 104112918Sjeff} 105112918Sjeff 106112918Sjeffvoid 107112918Sjeff_thread_cleanupspecific(void) 108112918Sjeff{ 109144518Sdavidxu struct pthread *curthread = _get_curthread(); 110144518Sdavidxu void (*destructor)( void *); 111157457Sdavidxu const void *data = NULL; 112112918Sjeff int key; 113144518Sdavidxu int i; 114112918Sjeff 115144518Sdavidxu if (curthread->specific == NULL) 116144518Sdavidxu return; 117112918Sjeff 118144518Sdavidxu /* Lock the key table: */ 119144518Sdavidxu THR_LOCK_ACQUIRE(curthread, &_keytable_lock); 120144518Sdavidxu for (i = 0; (i < PTHREAD_DESTRUCTOR_ITERATIONS) && 121144518Sdavidxu (curthread->specific_data_count > 0); i++) { 122144518Sdavidxu for (key = 0; (key < PTHREAD_KEYS_MAX) && 123144518Sdavidxu (curthread->specific_data_count > 0); key++) { 124144518Sdavidxu destructor = NULL; 125144518Sdavidxu 126144518Sdavidxu if (_thread_keytable[key].allocated && 127144518Sdavidxu (curthread->specific[key].data != NULL)) { 128144518Sdavidxu if (curthread->specific[key].seqno == 129144518Sdavidxu _thread_keytable[key].seqno) { 130157457Sdavidxu data = curthread->specific[key].data; 131144518Sdavidxu destructor = _thread_keytable[key].destructor; 132112918Sjeff } 133144518Sdavidxu curthread->specific[key].data = NULL; 134144518Sdavidxu curthread->specific_data_count--; 135144518Sdavidxu } 136197477Sdavidxu else if (curthread->specific[key].data != NULL) { 137197477Sdavidxu /* 138197477Sdavidxu * This can happen if the key is deleted via 139197477Sdavidxu * pthread_key_delete without first setting the value 140197477Sdavidxu * to NULL in all threads. POSIX says that the 141197477Sdavidxu * destructor is not invoked in this case. 142197477Sdavidxu */ 143197477Sdavidxu curthread->specific[key].data = NULL; 144197477Sdavidxu curthread->specific_data_count--; 145197477Sdavidxu } 146112918Sjeff 147144518Sdavidxu /* 148197477Sdavidxu * If there is a destructor, call it 149144518Sdavidxu * with the key table entry unlocked: 150144518Sdavidxu */ 151144518Sdavidxu if (destructor != NULL) { 152112918Sjeff /* 153144518Sdavidxu * Don't hold the lock while calling the 154144518Sdavidxu * destructor: 155112918Sjeff */ 156144518Sdavidxu THR_LOCK_RELEASE(curthread, &_keytable_lock); 157157457Sdavidxu destructor(__DECONST(void *, data)); 158144518Sdavidxu THR_LOCK_ACQUIRE(curthread, &_keytable_lock); 159112918Sjeff } 160112918Sjeff } 161112918Sjeff } 162144518Sdavidxu THR_LOCK_RELEASE(curthread, &_keytable_lock); 163144518Sdavidxu free(curthread->specific); 164144518Sdavidxu curthread->specific = NULL; 165144518Sdavidxu if (curthread->specific_data_count > 0) 166144518Sdavidxu stderr_debug("Thread %p has exited with leftover " 167144518Sdavidxu "thread-specific data after %d destructor iterations\n", 168144518Sdavidxu curthread, PTHREAD_DESTRUCTOR_ITERATIONS); 169112918Sjeff} 170112918Sjeff 171112918Sjeffstatic inline struct pthread_specific_elem * 172112918Sjeffpthread_key_allocate_data(void) 173112918Sjeff{ 174112918Sjeff struct pthread_specific_elem *new_data; 175112918Sjeff 176112918Sjeff new_data = (struct pthread_specific_elem *) 177159090Sdelphij calloc(1, sizeof(struct pthread_specific_elem) * PTHREAD_KEYS_MAX); 178112918Sjeff return (new_data); 179112918Sjeff} 180112918Sjeff 181112918Sjeffint 182262221Sjhb_pthread_setspecific(pthread_key_t userkey, const void *value) 183112918Sjeff{ 184144518Sdavidxu struct pthread *pthread; 185262221Sjhb pthread_key_t key = userkey - 1; 186112918Sjeff int ret = 0; 187112918Sjeff 188144518Sdavidxu /* Point to the running thread: */ 189144518Sdavidxu pthread = _get_curthread(); 190144518Sdavidxu 191112918Sjeff if ((pthread->specific) || 192112918Sjeff (pthread->specific = pthread_key_allocate_data())) { 193144518Sdavidxu if ((unsigned int)key < PTHREAD_KEYS_MAX) { 194144518Sdavidxu if (_thread_keytable[key].allocated) { 195112918Sjeff if (pthread->specific[key].data == NULL) { 196112918Sjeff if (value != NULL) 197112918Sjeff pthread->specific_data_count++; 198144518Sdavidxu } else if (value == NULL) 199144518Sdavidxu pthread->specific_data_count--; 200112918Sjeff pthread->specific[key].data = value; 201112918Sjeff pthread->specific[key].seqno = 202144518Sdavidxu _thread_keytable[key].seqno; 203112918Sjeff ret = 0; 204112918Sjeff } else 205112918Sjeff ret = EINVAL; 206112918Sjeff } else 207112918Sjeff ret = EINVAL; 208112918Sjeff } else 209112918Sjeff ret = ENOMEM; 210112918Sjeff return (ret); 211112918Sjeff} 212112918Sjeff 213112918Sjeffvoid * 214262221Sjhb_pthread_getspecific(pthread_key_t userkey) 215112918Sjeff{ 216144518Sdavidxu struct pthread *pthread; 217262221Sjhb pthread_key_t key = userkey - 1; 218157457Sdavidxu const void *data; 219112918Sjeff 220144518Sdavidxu /* Point to the running thread: */ 221144518Sdavidxu pthread = _get_curthread(); 222144518Sdavidxu 223112918Sjeff /* Check if there is specific data: */ 224144518Sdavidxu if (pthread->specific != NULL && (unsigned int)key < PTHREAD_KEYS_MAX) { 225112918Sjeff /* Check if this key has been used before: */ 226144518Sdavidxu if (_thread_keytable[key].allocated && 227144518Sdavidxu (pthread->specific[key].seqno == _thread_keytable[key].seqno)) { 228112918Sjeff /* Return the value: */ 229157457Sdavidxu data = pthread->specific[key].data; 230112918Sjeff } else { 231112918Sjeff /* 232112918Sjeff * This key has not been used before, so return NULL 233112918Sjeff * instead: 234112918Sjeff */ 235112918Sjeff data = NULL; 236112918Sjeff } 237112918Sjeff } else 238112918Sjeff /* No specific data has been created, so just return NULL: */ 239112918Sjeff data = NULL; 240157457Sdavidxu return (__DECONST(void *, data)); 241112918Sjeff} 242211860Sdavidxu 243211860Sdavidxuvoid 244211860Sdavidxu_thr_tsd_unload(struct dl_phdr_info *phdr_info) 245211860Sdavidxu{ 246211860Sdavidxu struct pthread *curthread = _get_curthread(); 247211860Sdavidxu void (*destructor)(void *); 248211860Sdavidxu int key; 249211860Sdavidxu 250211860Sdavidxu THR_LOCK_ACQUIRE(curthread, &_keytable_lock); 251211860Sdavidxu for (key = 0; key < PTHREAD_KEYS_MAX; key++) { 252211860Sdavidxu if (_thread_keytable[key].allocated) { 253211860Sdavidxu destructor = _thread_keytable[key].destructor; 254211860Sdavidxu if (destructor != NULL) { 255211860Sdavidxu if (__elf_phdr_match_addr(phdr_info, destructor)) 256211860Sdavidxu _thread_keytable[key].destructor = NULL; 257211860Sdavidxu } 258211860Sdavidxu } 259211860Sdavidxu } 260211860Sdavidxu THR_LOCK_RELEASE(curthread, &_keytable_lock); 261211860Sdavidxu} 262