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
29 struct rthread_key {
30 int used;
31 void (*destructor)(void *);
32 };
33
34 static struct rthread_key rkeys[PTHREAD_KEYS_MAX];
35 static _atomic_lock_t rkeyslock = _SPINLOCK_UNLOCKED;
36
37 int
pthread_key_create(pthread_key_t * key,void (* destructor)(void *))38 pthread_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 }
65 DEF_STRONG(pthread_key_create);
66
67 int
pthread_key_delete(pthread_key_t key)68 pthread_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
93 out:
94 _spinunlock(&rkeyslock);
95 return (rv);
96 }
97
98 static struct rthread_storage *
_rthread_findstorage(pthread_key_t key)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
125 out:
126 return (rs);
127 }
128
129 void *
pthread_getspecific(pthread_key_t key)130 pthread_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 }
143 DEF_STRONG(pthread_getspecific);
144
145 int
pthread_setspecific(pthread_key_t key,const void * data)146 pthread_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 }
160 DEF_STRONG(pthread_setspecific);
161
162 void
_rthread_tls_destructors(pthread_t thread)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