pthread_tsd.c revision 1.12 1 /* $NetBSD: pthread_tsd.c,v 1.12 2015/05/29 07:37:31 manu Exp $ */
2
3 /*-
4 * Copyright (c) 2001, 2007 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Nathan J. Williams, by Andrew Doran, and by Christos Zoulas.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 __RCSID("$NetBSD: pthread_tsd.c,v 1.12 2015/05/29 07:37:31 manu Exp $");
34
35 /* Functions and structures dealing with thread-specific data */
36 #include <errno.h>
37
38 #include "pthread.h"
39 #include "pthread_int.h"
40 #include "reentrant.h"
41
42 int pthread_keys_max;
43 static pthread_mutex_t tsd_mutex = PTHREAD_MUTEX_INITIALIZER;
44 static int nextkey;
45
46 PTQ_HEAD(pthread__tsd_list, pt_specific) *pthread__tsd_list = NULL;
47 void (**pthread__tsd_destructors)(void *) = NULL;
48
49 __strong_alias(__libc_thr_keycreate,pthread_key_create)
50 __strong_alias(__libc_thr_keydelete,pthread_key_delete)
51
52 static void
53 /*ARGSUSED*/
54 null_destructor(void *p)
55 {
56 }
57
58 #include <err.h>
59 #include <stdlib.h>
60 #include <stdio.h>
61
62 int
63 pthread_tsd_init(void)
64 {
65 char *pkm;
66 size_t len;
67
68 if ((pkm = getenv("PTHREAD_KEYS_MAX")) != NULL) {
69 pthread_keys_max = (int)strtol(pkm, NULL, 0);
70 if (pthread_keys_max < _POSIX_THREAD_KEYS_MAX)
71 pthread_keys_max = _POSIX_THREAD_KEYS_MAX;
72 } else {
73 pthread_keys_max = PTHREAD_KEYS_MAX;
74 }
75
76 len = sizeof(*pthread__tsd_list);
77 if ((pthread__tsd_list = calloc(pthread_keys_max, len)) == NULL)
78 goto out1;
79
80 len = sizeof(*pthread__tsd_destructors);
81 if ((pthread__tsd_destructors = calloc(pthread_keys_max, len)) == NULL)
82 goto out2;
83
84 return 0;
85
86 out2:
87 free(pthread__tsd_list);
88 out1:
89 pthread_keys_max = 0;
90
91 return -1;
92 }
93
94 int
95 pthread_key_create(pthread_key_t *key, void (*destructor)(void *))
96 {
97 int i;
98
99 if (__predict_false(__uselibcstub))
100 return __libc_thr_keycreate_stub(key, destructor);
101
102 /* Get a lock on the allocation list */
103 pthread_mutex_lock(&tsd_mutex);
104
105 /* Find an available slot:
106 * The condition for an available slot is one with the destructor
107 * not being NULL. If the desired destructor is NULL we set it to
108 * our own internal destructor to satisfy the non NULL condition.
109 */
110 /* 1. Search from "nextkey" to the end of the list. */
111 for (i = nextkey; i < pthread_keys_max; i++)
112 if (pthread__tsd_destructors[i] == NULL)
113 break;
114
115 if (i == pthread_keys_max) {
116 /* 2. If that didn't work, search from the start
117 * of the list back to "nextkey".
118 */
119 for (i = 0; i < nextkey; i++)
120 if (pthread__tsd_destructors[i] == NULL)
121 break;
122
123 if (i == nextkey) {
124 /* If we didn't find one here, there isn't one
125 * to be found.
126 */
127 pthread_mutex_unlock(&tsd_mutex);
128 return EAGAIN;
129 }
130 }
131
132 /* Got one. */
133 pthread__assert(PTQ_EMPTY(&pthread__tsd_list[i]));
134 pthread__tsd_destructors[i] = destructor ? destructor : null_destructor;
135
136 nextkey = (i + 1) % pthread_keys_max;
137 pthread_mutex_unlock(&tsd_mutex);
138 *key = i;
139
140 return 0;
141 }
142
143 /*
144 * Each thread holds an array of pthread_keys_max pt_specific list
145 * elements. When an element is used it is inserted into the appropriate
146 * key bucket of pthread__tsd_list. This means that ptqe_prev == NULL,
147 * means that the element is not threaded, ptqe_prev != NULL it is
148 * already part of the list. When we set to a NULL value we delete from the
149 * list if it was in the list, and when we set to non-NULL value, we insert
150 * in the list if it was not already there.
151 *
152 * We keep this global array of lists of threads that have called
153 * pthread_set_specific with non-null values, for each key so that
154 * we don't have to check all threads for non-NULL values in
155 * pthread_key_destroy
156 *
157 * We could keep an accounting of the number of specific used
158 * entries per thread, so that we can update pt_havespecific when we delete
159 * the last one, but we don't bother for now
160 */
161 int
162 pthread__add_specific(pthread_t self, pthread_key_t key, const void *value)
163 {
164 struct pt_specific *pt;
165
166 pthread__assert(key >= 0 && key < pthread_keys_max);
167
168 pthread_mutex_lock(&tsd_mutex);
169 pthread__assert(pthread__tsd_destructors[key] != NULL);
170 pt = &self->pt_specific[key];
171 self->pt_havespecific = 1;
172 if (value) {
173 if (pt->pts_next.ptqe_prev == NULL)
174 PTQ_INSERT_HEAD(&pthread__tsd_list[key], pt, pts_next);
175 } else {
176 if (pt->pts_next.ptqe_prev != NULL) {
177 PTQ_REMOVE(&pthread__tsd_list[key], pt, pts_next);
178 pt->pts_next.ptqe_prev = NULL;
179 }
180 }
181 pt->pts_value = __UNCONST(value);
182 pthread_mutex_unlock(&tsd_mutex);
183
184 return 0;
185 }
186
187 int
188 pthread_key_delete(pthread_key_t key)
189 {
190 /*
191 * This is tricky. The standard says of pthread_key_create()
192 * that new keys have the value NULL associated with them in
193 * all threads. According to people who were present at the
194 * standardization meeting, that requirement was written
195 * before pthread_key_delete() was introduced, and not
196 * reconsidered when it was.
197 *
198 * See David Butenhof's article in comp.programming.threads:
199 * Subject: Re: TSD key reusing issue
200 * Message-ID: <u97d8.29$fL6.200 (at) news.cpqcorp.net>
201 * Date: Thu, 21 Feb 2002 09:06:17 -0500
202 * http://groups.google.com/groups?\
203 * hl=en&selm=u97d8.29%24fL6.200%40news.cpqcorp.net
204 *
205 * Given:
206 *
207 * 1: Applications are not required to clear keys in all
208 * threads before calling pthread_key_delete().
209 * 2: Clearing pointers without running destructors is a
210 * memory leak.
211 * 3: The pthread_key_delete() function is expressly forbidden
212 * to run any destructors.
213 *
214 * Option 1: Make this function effectively a no-op and
215 * prohibit key reuse. This is a possible resource-exhaustion
216 * problem given that we have a static storage area for keys,
217 * but having a non-static storage area would make
218 * pthread_setspecific() expensive (might need to realloc the
219 * TSD array).
220 *
221 * Option 2: Ignore the specified behavior of
222 * pthread_key_create() and leave the old values. If an
223 * application deletes a key that still has non-NULL values in
224 * some threads... it's probably a memory leak and hence
225 * incorrect anyway, and we're within our rights to let the
226 * application lose. However, it's possible (if unlikely) that
227 * the application is storing pointers to non-heap data, or
228 * non-pointers that have been wedged into a void pointer, so
229 * we can't entirely write off such applications as incorrect.
230 * This could also lead to running (new) destructors on old
231 * data that was never supposed to be associated with that
232 * destructor.
233 *
234 * Option 3: Follow the specified behavior of
235 * pthread_key_create(). Either pthread_key_create() or
236 * pthread_key_delete() would then have to clear the values in
237 * every thread's slot for that key. In order to guarantee the
238 * visibility of the NULL value in other threads, there would
239 * have to be synchronization operations in both the clearer
240 * and pthread_getspecific(). Putting synchronization in
241 * pthread_getspecific() is a big performance lose. But in
242 * reality, only (buggy) reuse of an old key would require
243 * this synchronization; for a new key, there has to be a
244 * memory-visibility propagating event between the call to
245 * pthread_key_create() and pthread_getspecific() with that
246 * key, so setting the entries to NULL without synchronization
247 * will work, subject to problem (2) above. However, it's kind
248 * of slow.
249 *
250 * Note that the argument in option 3 only applies because we
251 * keep TSD in ordinary memory which follows the pthreads
252 * visibility rules. The visibility rules are not required by
253 * the standard to apply to TSD, so the argument doesn't
254 * apply in general, just to this implementation.
255 */
256
257 /*
258 * We do option 3; we find the list of all pt_specific structures
259 * threaded on the key we are deleting, unthread them, and set the
260 * pointer to NULL. Finally we unthread the entry, freeing it for
261 * further use.
262 *
263 * We don't call the destructor here, it is the responsibility
264 * of the application to cleanup the storage:
265 * http://pubs.opengroup.org/onlinepubs/9699919799/functions/\
266 * pthread_key_delete.html
267 */
268 struct pt_specific *pt;
269
270 if (__predict_false(__uselibcstub))
271 return __libc_thr_keydelete_stub(key);
272
273 pthread__assert(key >= 0 && key < pthread_keys_max);
274
275 pthread_mutex_lock(&tsd_mutex);
276
277 pthread__assert(pthread__tsd_destructors[key] != NULL);
278
279 while ((pt = PTQ_FIRST(&pthread__tsd_list[key])) != NULL) {
280 PTQ_REMOVE(&pthread__tsd_list[key], pt, pts_next);
281 pt->pts_value = NULL;
282 pt->pts_next.ptqe_prev = NULL;
283 }
284
285 pthread__tsd_destructors[key] = NULL;
286 pthread_mutex_unlock(&tsd_mutex);
287
288 return 0;
289 }
290
291 /* Perform thread-exit-time destruction of thread-specific data. */
292 void
293 pthread__destroy_tsd(pthread_t self)
294 {
295 int i, done, iterations;
296 void *val;
297 void (*destructor)(void *);
298
299 if (!self->pt_havespecific)
300 return;
301 pthread_mutex_unlock(&self->pt_lock);
302
303 /* Butenhof, section 5.4.2 (page 167):
304 *
305 * ``Also, Pthreads sets the thread-specific data value for a
306 * key to NULL before calling that key's destructor (passing
307 * the previous value of the key) when a thread terminates [*].
308 * ...
309 * [*] That is, unfortunately, not what the standard
310 * says. This is one of the problems with formal standards -
311 * they say what they say, not what they were intended to
312 * say. Somehow, an error crept in, and the sentence
313 * specifying that "the implementation clears the
314 * thread-specific data value before calling the destructor"
315 * was deleted. Nobody noticed, and the standard was approved
316 * with the error. So the standard says (by omission) that if
317 * you want to write a portable application using
318 * thread-specific data, that will not hang on thread
319 * termination, you must call pthread_setspecific within your
320 * destructor function to change the value to NULL. This would
321 * be silly, and any serious implementation of Pthreads will
322 * violate the standard in this respect. Of course, the
323 * standard will be fixed, probably by the 1003.1n amendment
324 * (assorted corrections to 1003.1c-1995), but that will take
325 * a while.''
326 */
327
328 iterations = 4; /* We're not required to try very hard */
329 do {
330 done = 1;
331 for (i = 0; i < pthread_keys_max; i++) {
332 struct pt_specific *pt = &self->pt_specific[i];
333 if (pt->pts_next.ptqe_prev == NULL)
334 continue;
335 pthread_mutex_lock(&tsd_mutex);
336
337 if (pt->pts_next.ptqe_prev != NULL) {
338 PTQ_REMOVE(&pthread__tsd_list[i], pt, pts_next);
339 val = pt->pts_value;
340 pt->pts_value = NULL;
341 pt->pts_next.ptqe_prev = NULL;
342 destructor = pthread__tsd_destructors[i];
343 } else
344 destructor = NULL;
345
346 pthread_mutex_unlock(&tsd_mutex);
347 if (destructor != NULL) {
348 done = 0;
349 (*destructor)(val);
350 }
351 }
352 } while (!done && iterations--);
353
354 self->pt_havespecific = 0;
355 pthread_mutex_lock(&self->pt_lock);
356 }
357