1 /* $OpenBSD: rthread_tls.c,v 1.4 2017/09/05 02:40:54 guenther Exp $ */ 2 /* 3 * Copyright (c) 2004,2005 Ted Unangst <tedu@openbsd.org> 4 * All Rights Reserved. 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 /* 19 * thread specific storage 20 */ 21 22 #include <errno.h> 23 #include <pthread.h> 24 #include <stdlib.h> 25 26 #include <pthread.h> 27 #include <stdlib.h> 28 29 #include "rthread.h" 30 31 32 struct rthread_key { 33 int used; 34 void (*destructor)(void *); 35 }; 36 37 static struct rthread_key rkeys[PTHREAD_KEYS_MAX]; 38 static _atomic_lock_t rkeyslock = _SPINLOCK_UNLOCKED; 39 40 int 41 pthread_key_create(pthread_key_t *key, void (*destructor)(void*)) 42 { 43 static int hint; 44 int i; 45 46 _spinlock(&rkeyslock); 47 if (rkeys[hint].used) { 48 for (i = 0; i < PTHREAD_KEYS_MAX; i++) { 49 if (!rkeys[i].used) 50 break; 51 } 52 if (i == PTHREAD_KEYS_MAX) { 53 _spinunlock(&rkeyslock); 54 return (EAGAIN); 55 } 56 hint = i; 57 } 58 rkeys[hint].used = 1; 59 rkeys[hint].destructor = destructor; 60 61 *key = hint++; 62 if (hint >= PTHREAD_KEYS_MAX) 63 hint = 0; 64 _spinunlock(&rkeyslock); 65 66 return (0); 67 } 68 DEF_STRONG(pthread_key_create); 69 70 int 71 pthread_key_delete(pthread_key_t key) 72 { 73 struct rthread_storage *rs; 74 int rv = 0; 75 76 if (key < 0 || key >= PTHREAD_KEYS_MAX) 77 return (EINVAL); 78 79 _spinlock(&rkeyslock); 80 if (!rkeys[key].used) { 81 rv = EINVAL; 82 goto out; 83 } 84 85 rkeys[key].used = 0; 86 rkeys[key].destructor = NULL; 87 if (_thread_cb.tc_thread_key_zero != NULL) 88 _thread_cb.tc_thread_key_zero(key); 89 else { 90 for (rs = _initial_thread.local_storage; rs; rs = rs->next) { 91 if (rs->keyid == key) 92 rs->data = NULL; 93 } 94 } 95 96 out: 97 _spinunlock(&rkeyslock); 98 return (rv); 99 } 100 101 static struct rthread_storage * 102 _rthread_findstorage(pthread_key_t key) 103 { 104 struct rthread_storage *rs; 105 pthread_t self; 106 107 if (!rkeys[key].used) { 108 rs = NULL; 109 goto out; 110 } 111 112 self = pthread_self(); 113 114 for (rs = self->local_storage; rs; rs = rs->next) { 115 if (rs->keyid == key) 116 break; 117 } 118 if (!rs) { 119 rs = calloc(1, sizeof(*rs)); 120 if (!rs) 121 goto out; 122 rs->keyid = key; 123 rs->data = NULL; 124 rs->next = self->local_storage; 125 self->local_storage = rs; 126 } 127 128 out: 129 return (rs); 130 } 131 132 void * 133 pthread_getspecific(pthread_key_t key) 134 { 135 struct rthread_storage *rs; 136 137 if (key < 0 || key >= PTHREAD_KEYS_MAX) 138 return (NULL); 139 140 rs = _rthread_findstorage(key); 141 if (!rs) 142 return (NULL); 143 144 return (rs->data); 145 } 146 DEF_STRONG(pthread_getspecific); 147 148 int 149 pthread_setspecific(pthread_key_t key, const void *data) 150 { 151 struct rthread_storage *rs; 152 153 if (key < 0 || key >= PTHREAD_KEYS_MAX) 154 return (EINVAL); 155 156 rs = _rthread_findstorage(key); 157 if (!rs) 158 return (ENOMEM); 159 rs->data = (void *)data; 160 161 return (0); 162 } 163 DEF_STRONG(pthread_setspecific); 164 165 void 166 _rthread_tls_destructors(pthread_t thread) 167 { 168 struct rthread_storage *rs; 169 int i; 170 171 _spinlock(&rkeyslock); 172 for (i = 0; i < PTHREAD_DESTRUCTOR_ITERATIONS; i++) { 173 for (rs = thread->local_storage; rs; rs = rs->next) { 174 if (!rs->data) 175 continue; 176 if (rkeys[rs->keyid].destructor) { 177 void (*destructor)(void *) = 178 rkeys[rs->keyid].destructor; 179 void *data = rs->data; 180 rs->data = NULL; 181 _spinunlock(&rkeyslock); 182 destructor(data); 183 _spinlock(&rkeyslock); 184 } 185 } 186 } 187 for (rs = thread->local_storage; rs; rs = thread->local_storage) { 188 thread->local_storage = rs->next; 189 free(rs); 190 } 191 _spinunlock(&rkeyslock); 192 } 193