]>
Commit | Line | Data |
---|---|---|
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 | ||
63 | static const int __pthread_tsd_first = __TSD_RESERVED_MAX + 1; | |
64 | static int __pthread_tsd_max = __pthread_tsd_first; | |
65 | static const int __pthread_tsd_start = _INTERNAL_POSIX_THREAD_KEYS_MAX; | |
66 | static const int __pthread_tsd_end = _INTERNAL_POSIX_THREAD_KEYS_END; | |
67 | ||
964d3577 A |
68 | static int __pthread_key_legacy_behaviour = 0; |
69 | static 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 | ||
79 | static struct { | |
80 | uintptr_t destructor; | |
81 | } _pthread_keys[_INTERNAL_POSIX_THREAD_KEYS_END]; | |
82 | ||
2546420a | 83 | static _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. | |
87 | void | |
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. |
95 | static 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. | |
108 | static 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. | |
120 | static 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 | ||
130 | int | |
131 | pthread_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 | ||
149 | int | |
150 | pthread_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 | ||
173 | int | |
174 | pthread_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 |
200 | int |
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 |
215 | void* |
216 | pthread_getspecific(pthread_key_t key) | |
217 | { | |
218 | return _pthread_getspecific_direct(key); | |
219 | } | |
220 | ||
221 | #if !VARIANT_DYLD | |
222 | static 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 |
243 | static 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 | ||
263 | static 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 | ||
291 | static 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 | ||
322 | void | |
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 | |
340 | int | |
341 | pthread_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 | |
358 | pthread_t | |
359 | pthread_self(void) | |
360 | { | |
361 | return _pthread_getspecific_direct(_PTHREAD_TSD_SLOT_PTHREAD_SELF); | |
362 | } |