]> git.saurik.com Git - apple/libpthread.git/blame - src/pthread_tsd.c
libpthread-301.30.1.tar.gz
[apple/libpthread.git] / src / pthread_tsd.c
CommitLineData
f1a1da6c
A
1/*
2 * Copyright (c) 2000-2003, 2007, 2012 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
a0619f9c 5 *
f1a1da6c
A
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
a0619f9c 12 *
f1a1da6c
A
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
a0619f9c 20 *
f1a1da6c
A
21 * @APPLE_LICENSE_HEADER_END@
22 */
23/*
a0619f9c
A
24 * Copyright 1996 1995 by Open Software Foundation, Inc. 1997 1996 1995 1994 1993 1992 1991
25 * All Rights Reserved
26 *
27 * Permission to use, copy, modify, and distribute this software and
28 * its documentation for any purpose and without fee is hereby granted,
29 * provided that the above copyright notice appears in all copies and
30 * that both the copyright notice and this permission notice appear in
31 * supporting documentation.
32 *
33 * OSF DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
34 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
35 * FOR A PARTICULAR PURPOSE.
36 *
37 * IN NO EVENT SHALL OSF BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
38 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
39 * LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
40 * NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
41 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
42 *
f1a1da6c
A
43 */
44/*
45 * MkLinux
46 */
47
48/*
49 * POSIX Pthread Library
50 * Thread Specific Data support
51 * NB: pthread_getspecific() is in a separate assembly file
52 */
53
54#include "internal.h"
55#include <TargetConditionals.h>
56
57#if !VARIANT_DYLD
58// __pthread_tsd_first is first static key managed by libpthread.
59// __pthread_tsd_max is the (observed) end of static key destructors.
60// __pthread_tsd_start is the start of dynamic keys.
61// __pthread_tsd_end is the end of dynamic keys.
62
63static const int __pthread_tsd_first = __TSD_RESERVED_MAX + 1;
64static int __pthread_tsd_max = __pthread_tsd_first;
65static const int __pthread_tsd_start = _INTERNAL_POSIX_THREAD_KEYS_MAX;
66static const int __pthread_tsd_end = _INTERNAL_POSIX_THREAD_KEYS_END;
67
964d3577
A
68static int __pthread_key_legacy_behaviour = 0;
69static int __pthread_key_legacy_behaviour_log = 0;
70
f1a1da6c
A
71// Omit support for pthread key destructors in the static archive for dyld.
72// dyld does not create and destroy threads so these are not necessary.
73//
74// We store the bit-wise negation of the destructor so that a quick non-zero
75// test can be used to determine if the destructor has been set, even if it is
76// NULL. This means that a destructor of value ~0x0ull cannot be used. That
77// shouldn't be a problem in practice since it isn't a valid function address.
78
79static struct {
80 uintptr_t destructor;
81} _pthread_keys[_INTERNAL_POSIX_THREAD_KEYS_END];
82
2546420a 83static _pthread_lock tsd_lock = _PTHREAD_LOCK_INITIALIZER;
f1a1da6c 84
964d3577
A
85// The pthread_tsd destruction order can be reverted to the old (pre-10.11) order
86// by setting this environment variable.
87void
88_pthread_key_global_init(const char *envp[])
89{
90 __pthread_key_legacy_behaviour = _simple_getenv(envp, "PTHREAD_KEY_LEGACY_DESTRUCTOR_ORDER") ? 1 : 0;
91 __pthread_key_legacy_behaviour_log = _simple_getenv(envp, "PTHREAD_KEY_LEGACY_DESTRUCTOR_ORDER_LOG") ? 1 : 0;
92}
93
f1a1da6c
A
94// Returns true if successful, false if destructor was already set.
95static bool
96_pthread_key_set_destructor(pthread_key_t key, void (*destructor)(void *))
97{
98 uintptr_t *ptr = &_pthread_keys[key].destructor;
99 uintptr_t value = ~(uintptr_t)destructor;
100 if (*ptr == 0) {
101 *ptr = value;
102 return true;
103 }
104 return false;
105}
106
107// Returns true if successful, false if the destructor was not set.
108static bool
109_pthread_key_unset_destructor(pthread_key_t key)
110{
111 uintptr_t *ptr = &_pthread_keys[key].destructor;
112 if (*ptr != 0) {
113 *ptr = 0;
114 return true;
115 }
116 return false;
117}
118
119// Returns true if successful, false if the destructor was not set.
120static bool
121_pthread_key_get_destructor(pthread_key_t key, void (**destructor)(void *))
122{
123 uintptr_t value = _pthread_keys[key].destructor;
124 if (destructor) {
125 *destructor = (void (*)(void *))(~value);
126 }
127 return (value != 0);
128}
129
130int
131pthread_key_create(pthread_key_t *key, void (*destructor)(void *))
132{
133 int res = EAGAIN; // Returns EAGAIN if key cannot be allocated.
134 pthread_key_t k;
135
2546420a 136 _PTHREAD_LOCK(tsd_lock);
f1a1da6c
A
137 for (k = __pthread_tsd_start; k < __pthread_tsd_end; k++) {
138 if (_pthread_key_set_destructor(k, destructor)) {
139 *key = k;
140 res = 0;
141 break;
142 }
143 }
2546420a 144 _PTHREAD_UNLOCK(tsd_lock);
f1a1da6c
A
145
146 return res;
147}
148
149int
150pthread_key_delete(pthread_key_t key)
151{
152 int res = EINVAL; // Returns EINVAL if key is not allocated.
153
2546420a 154 _PTHREAD_LOCK(tsd_lock);
f1a1da6c
A
155 if (key >= __pthread_tsd_start && key < __pthread_tsd_end) {
156 if (_pthread_key_unset_destructor(key)) {
157 struct _pthread *p;
2546420a 158 _PTHREAD_LOCK(_pthread_list_lock);
f1a1da6c
A
159 TAILQ_FOREACH(p, &__pthread_head, plist) {
160 // No lock for word-sized write.
161 p->tsd[key] = 0;
162 }
2546420a 163 _PTHREAD_UNLOCK(_pthread_list_lock);
f1a1da6c
A
164 res = 0;
165 }
166 }
2546420a 167 _PTHREAD_UNLOCK(tsd_lock);
f1a1da6c
A
168
169 return res;
170}
171#endif // !VARIANT_DYLD
172
173int
174pthread_setspecific(pthread_key_t key, const void *value)
175{
176 int res = EINVAL;
177
178#if !VARIANT_DYLD
179 if (key >= __pthread_tsd_first && key < __pthread_tsd_end) {
180 bool created = _pthread_key_get_destructor(key, NULL);
181 if (key < __pthread_tsd_start || created) {
182 struct _pthread *self = pthread_self();
183 self->tsd[key] = (void *)value;
184 res = 0;
185
186 if (key < __pthread_tsd_start) {
187 // XXX: is this really necessary?
188 _pthread_key_set_destructor(key, NULL);
189 }
190 if (key > self->max_tsd_key) {
191 self->max_tsd_key = (int)key;
192 }
193 }
194 }
195#endif // !VARIANT_DYLD
196
197 return res;
198}
199
2546420a
A
200int
201_pthread_setspecific_static(pthread_key_t key, void *value)
202{
203 int res = EINVAL;
204
205#if !VARIANT_DYLD
206 if (key < __pthread_tsd_start) {
207 _pthread_setspecific_direct(key, value);
208 res = 0;
209 }
210#endif // !VARIANT_DYLD
211
212 return res;
213}
214
f1a1da6c
A
215void*
216pthread_getspecific(pthread_key_t key)
217{
218 return _pthread_getspecific_direct(key);
219}
220
221#if !VARIANT_DYLD
222static void
223_pthread_tsd_cleanup_key(pthread_t self, pthread_key_t key)
224{
225 void (*destructor)(void *);
226 if (_pthread_key_get_destructor(key, &destructor)) {
227 void **ptr = &self->tsd[key];
228 void *value = *ptr;
229 if (value) {
230 *ptr = NULL;
231 if (destructor) {
232 destructor(value);
233 }
234 }
235 }
236}
237#endif // !VARIANT_DYLD
238
964d3577
A
239#import <_simple.h>
240#import <dlfcn.h>
241
f1a1da6c 242#if !VARIANT_DYLD
964d3577
A
243static void
244_pthread_tsd_cleanup_new(pthread_t self)
245{
246 int j;
247
2546420a 248 // clean up all keys
964d3577
A
249 for (j = 0; j < PTHREAD_DESTRUCTOR_ITERATIONS; j++) {
250 pthread_key_t k;
251 for (k = __pthread_tsd_start; k <= self->max_tsd_key; k++) {
252 _pthread_tsd_cleanup_key(self, k);
253 }
254
255 for (k = __pthread_tsd_first; k <= __pthread_tsd_max; k++) {
964d3577
A
256 _pthread_tsd_cleanup_key(self, k);
257 }
258 }
259
260 self->max_tsd_key = 0;
964d3577
A
261}
262
263static void
264_pthread_tsd_behaviour_check(pthread_t self)
265{
266 // Iterate from dynamic-key start to dynamic-key end, if the key has both
267 // a desctructor and a value then _pthread_tsd_cleanup_key would cause
268 // us to re-trigger the destructor.
269 Dl_info i;
270 pthread_key_t k;
271
a0619f9c 272 for (k = __pthread_tsd_start; k < __pthread_tsd_end; k++) {
964d3577
A
273 void (*destructor)(void *);
274 if (_pthread_key_get_destructor(k, &destructor)) {
275 void **ptr = &self->tsd[k];
276 void *value = *ptr;
277 if (value && destructor) {
278 _simple_asl_log(ASL_LEVEL_ERR, "pthread_tsd",
279 "warning: dynamic tsd keys dirty after static key cleanup loop.");
280
281 if (dladdr(destructor, &i) == 0) {
282 _simple_asl_log(ASL_LEVEL_ERR, "pthread_tsd", i.dli_fname);
283 _simple_asl_log(ASL_LEVEL_ERR, "pthread_tsd", i.dli_saddr);
284 }
285 }
286 }
287 }
288
289}
290
291static void
292_pthread_tsd_cleanup_legacy(pthread_t self)
293{
f1a1da6c
A
294 int j;
295
296 // clean up dynamic keys first
297 for (j = 0; j < PTHREAD_DESTRUCTOR_ITERATIONS; j++) {
298 pthread_key_t k;
299 for (k = __pthread_tsd_start; k <= self->max_tsd_key; k++) {
300 _pthread_tsd_cleanup_key(self, k);
301 }
302 }
303
304 self->max_tsd_key = 0;
305
306 // clean up static keys
307 for (j = 0; j < PTHREAD_DESTRUCTOR_ITERATIONS; j++) {
308 pthread_key_t k;
309 for (k = __pthread_tsd_first; k <= __pthread_tsd_max; k++) {
310 _pthread_tsd_cleanup_key(self, k);
311 }
964d3577
A
312
313 if (__pthread_key_legacy_behaviour_log != 0 && self->max_tsd_key != 0) {
314 // max_tsd_key got dirtied, either by static or dynamic keys being
315 // reset. check for any dirty dynamic keys.
316 _pthread_tsd_behaviour_check(self);
317 }
318 }
319}
320#endif // !VARIANT_DYLD
321
322void
323_pthread_tsd_cleanup(pthread_t self)
324{
325#if !VARIANT_DYLD
326
327 // unless __pthread_key_legacy_behaviour == 1, use the new pthread key
328 // destructor order: (dynamic -> static) x5 -> (GC x5)
329
330 if (__pthread_key_legacy_behaviour == 0) {
331 _pthread_tsd_cleanup_new(self);
332 } else {
333 _pthread_tsd_cleanup_legacy(self);
f1a1da6c
A
334 }
335#endif // !VARIANT_DYLD
336}
337
338#if !VARIANT_DYLD
339// XXX: key should be pthread_key_t
340int
341pthread_key_init_np(int key, void (*destructor)(void *))
342{
343 int res = EINVAL; // Returns EINVAL if key is out of range.
344 if (key >= __pthread_tsd_first && key < __pthread_tsd_start) {
2546420a 345 _PTHREAD_LOCK(tsd_lock);
f1a1da6c
A
346 _pthread_key_set_destructor(key, destructor);
347 if (key > __pthread_tsd_max) {
348 __pthread_tsd_max = key;
349 }
2546420a 350 _PTHREAD_UNLOCK(tsd_lock);
f1a1da6c
A
351 res = 0;
352 }
353 return res;
354}
355#endif // !VARIANT_DYLD
356
357#undef pthread_self
358pthread_t
359pthread_self(void)
360{
361 return _pthread_getspecific_direct(_PTHREAD_TSD_SLOT_PTHREAD_SELF);
362}