1/* $OpenBSD: rthread_tls.c,v 1.5 2023/04/19 12:30:09 jsg 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 "rthread.h" 27 28 29struct rthread_key { 30 int used; 31 void (*destructor)(void *); 32}; 33 34static struct rthread_key rkeys[PTHREAD_KEYS_MAX]; 35static _atomic_lock_t rkeyslock = _SPINLOCK_UNLOCKED; 36 37int 38pthread_key_create(pthread_key_t *key, void (*destructor)(void*)) 39{ 40 static int hint; 41 int i; 42 43 _spinlock(&rkeyslock); 44 if (rkeys[hint].used) { 45 for (i = 0; i < PTHREAD_KEYS_MAX; i++) { 46 if (!rkeys[i].used) 47 break; 48 } 49 if (i == PTHREAD_KEYS_MAX) { 50 _spinunlock(&rkeyslock); 51 return (EAGAIN); 52 } 53 hint = i; 54 } 55 rkeys[hint].used = 1; 56 rkeys[hint].destructor = destructor; 57 58 *key = hint++; 59 if (hint >= PTHREAD_KEYS_MAX) 60 hint = 0; 61 _spinunlock(&rkeyslock); 62 63 return (0); 64} 65DEF_STRONG(pthread_key_create); 66 67int 68pthread_key_delete(pthread_key_t key) 69{ 70 struct rthread_storage *rs; 71 int rv = 0; 72 73 if (key < 0 || key >= PTHREAD_KEYS_MAX) 74 return (EINVAL); 75 76 _spinlock(&rkeyslock); 77 if (!rkeys[key].used) { 78 rv = EINVAL; 79 goto out; 80 } 81 82 rkeys[key].used = 0; 83 rkeys[key].destructor = NULL; 84 if (_thread_cb.tc_thread_key_zero != NULL) 85 _thread_cb.tc_thread_key_zero(key); 86 else { 87 for (rs = _initial_thread.local_storage; rs; rs = rs->next) { 88 if (rs->keyid == key) 89 rs->data = NULL; 90 } 91 } 92 93out: 94 _spinunlock(&rkeyslock); 95 return (rv); 96} 97 98static struct rthread_storage * 99_rthread_findstorage(pthread_key_t key) 100{ 101 struct rthread_storage *rs; 102 pthread_t self; 103 104 if (!rkeys[key].used) { 105 rs = NULL; 106 goto out; 107 } 108 109 self = pthread_self(); 110 111 for (rs = self->local_storage; rs; rs = rs->next) { 112 if (rs->keyid == key) 113 break; 114 } 115 if (!rs) { 116 rs = calloc(1, sizeof(*rs)); 117 if (!rs) 118 goto out; 119 rs->keyid = key; 120 rs->data = NULL; 121 rs->next = self->local_storage; 122 self->local_storage = rs; 123 } 124 125out: 126 return (rs); 127} 128 129void * 130pthread_getspecific(pthread_key_t key) 131{ 132 struct rthread_storage *rs; 133 134 if (key < 0 || key >= PTHREAD_KEYS_MAX) 135 return (NULL); 136 137 rs = _rthread_findstorage(key); 138 if (!rs) 139 return (NULL); 140 141 return (rs->data); 142} 143DEF_STRONG(pthread_getspecific); 144 145int 146pthread_setspecific(pthread_key_t key, const void *data) 147{ 148 struct rthread_storage *rs; 149 150 if (key < 0 || key >= PTHREAD_KEYS_MAX) 151 return (EINVAL); 152 153 rs = _rthread_findstorage(key); 154 if (!rs) 155 return (ENOMEM); 156 rs->data = (void *)data; 157 158 return (0); 159} 160DEF_STRONG(pthread_setspecific); 161 162void 163_rthread_tls_destructors(pthread_t thread) 164{ 165 struct rthread_storage *rs; 166 int i; 167 168 _spinlock(&rkeyslock); 169 for (i = 0; i < PTHREAD_DESTRUCTOR_ITERATIONS; i++) { 170 for (rs = thread->local_storage; rs; rs = rs->next) { 171 if (!rs->data) 172 continue; 173 if (rkeys[rs->keyid].destructor) { 174 void (*destructor)(void *) = 175 rkeys[rs->keyid].destructor; 176 void *data = rs->data; 177 rs->data = NULL; 178 _spinunlock(&rkeyslock); 179 destructor(data); 180 _spinlock(&rkeyslock); 181 } 182 } 183 } 184 for (rs = thread->local_storage; rs; rs = thread->local_storage) { 185 thread->local_storage = rs->next; 186 free(rs); 187 } 188 _spinunlock(&rkeyslock); 189} 190