]>
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" | |
f1a1da6c | 55 | |
c6e5f90c A |
56 | #ifndef PTHREAD_KEY_LEGACY_SUPPORT |
57 | #if TARGET_OS_DRIVERKIT | |
58 | #define PTHREAD_KEY_LEGACY_SUPPORT 0 | |
59 | #else | |
60 | #define PTHREAD_KEY_LEGACY_SUPPORT 1 | |
61 | #endif // TARGET_OS_DRIVERKIT | |
62 | #endif // PTHREAD_KEY_LEGACY_SUPPORT | |
63 | ||
f1a1da6c A |
64 | #if !VARIANT_DYLD |
65 | // __pthread_tsd_first is first static key managed by libpthread. | |
66 | // __pthread_tsd_max is the (observed) end of static key destructors. | |
67 | // __pthread_tsd_start is the start of dynamic keys. | |
68 | // __pthread_tsd_end is the end of dynamic keys. | |
69 | ||
70 | static const int __pthread_tsd_first = __TSD_RESERVED_MAX + 1; | |
f1a1da6c A |
71 | static const int __pthread_tsd_start = _INTERNAL_POSIX_THREAD_KEYS_MAX; |
72 | static const int __pthread_tsd_end = _INTERNAL_POSIX_THREAD_KEYS_END; | |
73 | ||
214d78a2 A |
74 | static int __pthread_tsd_max = __pthread_tsd_first; |
75 | static _pthread_lock __pthread_tsd_lock = _PTHREAD_LOCK_INITIALIZER; | |
c6e5f90c | 76 | #if PTHREAD_KEY_LEGACY_SUPPORT |
214d78a2 A |
77 | static bool __pthread_key_legacy_behaviour = 0; |
78 | static bool __pthread_key_legacy_behaviour_log = 0; | |
c6e5f90c A |
79 | #else |
80 | #define __pthread_key_legacy_behaviour 0 | |
81 | #define _pthread_tsd_cleanup_legacy(...) | |
82 | #endif // PTHREAD_KEY_LEGACY_SUPPORT | |
964d3577 | 83 | |
f1a1da6c A |
84 | // Omit support for pthread key destructors in the static archive for dyld. |
85 | // dyld does not create and destroy threads so these are not necessary. | |
86 | // | |
87 | // We store the bit-wise negation of the destructor so that a quick non-zero | |
88 | // test can be used to determine if the destructor has been set, even if it is | |
89 | // NULL. This means that a destructor of value ~0x0ull cannot be used. That | |
90 | // shouldn't be a problem in practice since it isn't a valid function address. | |
91 | ||
92 | static struct { | |
93 | uintptr_t destructor; | |
94 | } _pthread_keys[_INTERNAL_POSIX_THREAD_KEYS_END]; | |
95 | ||
964d3577 A |
96 | // The pthread_tsd destruction order can be reverted to the old (pre-10.11) order |
97 | // by setting this environment variable. | |
98 | void | |
99 | _pthread_key_global_init(const char *envp[]) | |
100 | { | |
c6e5f90c | 101 | #if PTHREAD_KEY_LEGACY_SUPPORT |
214d78a2 A |
102 | if (_simple_getenv(envp, "PTHREAD_KEY_LEGACY_DESTRUCTOR_ORDER")) { |
103 | __pthread_key_legacy_behaviour = true; | |
104 | } | |
105 | if (_simple_getenv(envp, "PTHREAD_KEY_LEGACY_DESTRUCTOR_ORDER_LOG")) { | |
106 | __pthread_key_legacy_behaviour_log = true; | |
107 | } | |
c6e5f90c | 108 | #endif // PTHREAD_KEY_LEGACY_SUPPORT |
964d3577 A |
109 | } |
110 | ||
f1a1da6c A |
111 | // Returns true if successful, false if destructor was already set. |
112 | static bool | |
113 | _pthread_key_set_destructor(pthread_key_t key, void (*destructor)(void *)) | |
114 | { | |
115 | uintptr_t *ptr = &_pthread_keys[key].destructor; | |
116 | uintptr_t value = ~(uintptr_t)destructor; | |
117 | if (*ptr == 0) { | |
118 | *ptr = value; | |
119 | return true; | |
120 | } | |
121 | return false; | |
122 | } | |
123 | ||
124 | // Returns true if successful, false if the destructor was not set. | |
125 | static bool | |
126 | _pthread_key_unset_destructor(pthread_key_t key) | |
127 | { | |
128 | uintptr_t *ptr = &_pthread_keys[key].destructor; | |
129 | if (*ptr != 0) { | |
130 | *ptr = 0; | |
131 | return true; | |
132 | } | |
133 | return false; | |
134 | } | |
135 | ||
136 | // Returns true if successful, false if the destructor was not set. | |
137 | static bool | |
138 | _pthread_key_get_destructor(pthread_key_t key, void (**destructor)(void *)) | |
139 | { | |
140 | uintptr_t value = _pthread_keys[key].destructor; | |
141 | if (destructor) { | |
142 | *destructor = (void (*)(void *))(~value); | |
143 | } | |
144 | return (value != 0); | |
145 | } | |
146 | ||
147 | int | |
148 | pthread_key_create(pthread_key_t *key, void (*destructor)(void *)) | |
149 | { | |
150 | int res = EAGAIN; // Returns EAGAIN if key cannot be allocated. | |
151 | pthread_key_t k; | |
152 | ||
c1f56ec9 | 153 | _pthread_lock_lock(&__pthread_tsd_lock); |
f1a1da6c A |
154 | for (k = __pthread_tsd_start; k < __pthread_tsd_end; k++) { |
155 | if (_pthread_key_set_destructor(k, destructor)) { | |
156 | *key = k; | |
157 | res = 0; | |
158 | break; | |
159 | } | |
160 | } | |
c1f56ec9 | 161 | _pthread_lock_unlock(&__pthread_tsd_lock); |
f1a1da6c A |
162 | |
163 | return res; | |
164 | } | |
165 | ||
166 | int | |
167 | pthread_key_delete(pthread_key_t key) | |
168 | { | |
169 | int res = EINVAL; // Returns EINVAL if key is not allocated. | |
170 | ||
c1f56ec9 | 171 | _pthread_lock_lock(&__pthread_tsd_lock); |
f1a1da6c A |
172 | if (key >= __pthread_tsd_start && key < __pthread_tsd_end) { |
173 | if (_pthread_key_unset_destructor(key)) { | |
c1f56ec9 A |
174 | pthread_t p; |
175 | _pthread_lock_lock(&_pthread_list_lock); | |
214d78a2 | 176 | TAILQ_FOREACH(p, &__pthread_head, tl_plist) { |
f1a1da6c A |
177 | // No lock for word-sized write. |
178 | p->tsd[key] = 0; | |
179 | } | |
c1f56ec9 | 180 | _pthread_lock_unlock(&_pthread_list_lock); |
f1a1da6c A |
181 | res = 0; |
182 | } | |
183 | } | |
c1f56ec9 | 184 | _pthread_lock_unlock(&__pthread_tsd_lock); |
f1a1da6c A |
185 | |
186 | return res; | |
187 | } | |
f1a1da6c | 188 | |
c1f56ec9 A |
189 | static inline int |
190 | _pthread_setspecific(pthread_t thread, pthread_key_t key, const void *value) | |
f1a1da6c A |
191 | { |
192 | int res = EINVAL; | |
193 | ||
f1a1da6c A |
194 | if (key >= __pthread_tsd_first && key < __pthread_tsd_end) { |
195 | bool created = _pthread_key_get_destructor(key, NULL); | |
196 | if (key < __pthread_tsd_start || created) { | |
c1f56ec9 | 197 | thread->tsd[key] = (void *)value; |
f1a1da6c A |
198 | res = 0; |
199 | ||
200 | if (key < __pthread_tsd_start) { | |
201 | // XXX: is this really necessary? | |
202 | _pthread_key_set_destructor(key, NULL); | |
203 | } | |
c1f56ec9 A |
204 | if (key > thread->max_tsd_key) { |
205 | thread->max_tsd_key = (uint16_t)key; | |
f1a1da6c A |
206 | } |
207 | } | |
208 | } | |
f1a1da6c A |
209 | |
210 | return res; | |
211 | } | |
c1f56ec9 A |
212 | #endif // !VARIANT_DYLD |
213 | ||
214 | int | |
215 | pthread_setspecific(pthread_key_t key, const void *value) | |
216 | { | |
217 | #if VARIANT_DYLD | |
218 | return ENOTSUP; | |
219 | #else | |
220 | return _pthread_setspecific(pthread_self(), key, value); | |
221 | #endif // !VARIANT_DYLD | |
222 | } | |
f1a1da6c | 223 | |
2546420a A |
224 | int |
225 | _pthread_setspecific_static(pthread_key_t key, void *value) | |
226 | { | |
227 | int res = EINVAL; | |
228 | ||
229 | #if !VARIANT_DYLD | |
230 | if (key < __pthread_tsd_start) { | |
231 | _pthread_setspecific_direct(key, value); | |
232 | res = 0; | |
233 | } | |
234 | #endif // !VARIANT_DYLD | |
235 | ||
236 | return res; | |
237 | } | |
238 | ||
f1a1da6c A |
239 | void* |
240 | pthread_getspecific(pthread_key_t key) | |
241 | { | |
242 | return _pthread_getspecific_direct(key); | |
243 | } | |
244 | ||
245 | #if !VARIANT_DYLD | |
c1f56ec9 A |
246 | int |
247 | pthread_introspection_setspecific_np(pthread_t thread, | |
248 | pthread_key_t key, const void *value) | |
249 | { | |
250 | pthread_t self = _pthread_self(); | |
251 | if (os_unlikely(self->introspection != PTHREAD_INTROSPECTION_THREAD_CREATE)) { | |
252 | PTHREAD_CLIENT_CRASH(0, "Calling pthread_introspection_setspecific_np " | |
253 | "outside of a CREATE introspection hook"); | |
254 | } | |
255 | return _pthread_setspecific(thread, key, value); | |
256 | ||
257 | } | |
258 | ||
259 | void * | |
260 | pthread_introspection_getspecific_np(pthread_t thread, pthread_key_t key) | |
261 | { | |
262 | pthread_t self = _pthread_self(); | |
263 | if (os_unlikely(self->introspection != PTHREAD_INTROSPECTION_THREAD_DESTROY)) { | |
264 | PTHREAD_CLIENT_CRASH(0, "Calling pthread_introspection_getspecific_np " | |
265 | "outside of a DESTROY introspection hook"); | |
266 | } | |
267 | return thread->tsd[key]; | |
268 | } | |
269 | ||
f1a1da6c A |
270 | static void |
271 | _pthread_tsd_cleanup_key(pthread_t self, pthread_key_t key) | |
272 | { | |
273 | void (*destructor)(void *); | |
274 | if (_pthread_key_get_destructor(key, &destructor)) { | |
275 | void **ptr = &self->tsd[key]; | |
276 | void *value = *ptr; | |
277 | if (value) { | |
278 | *ptr = NULL; | |
279 | if (destructor) { | |
280 | destructor(value); | |
281 | } | |
282 | } | |
283 | } | |
284 | } | |
f1a1da6c | 285 | |
964d3577 A |
286 | static void |
287 | _pthread_tsd_cleanup_new(pthread_t self) | |
288 | { | |
289 | int j; | |
290 | ||
2546420a | 291 | // clean up all keys |
964d3577 A |
292 | for (j = 0; j < PTHREAD_DESTRUCTOR_ITERATIONS; j++) { |
293 | pthread_key_t k; | |
294 | for (k = __pthread_tsd_start; k <= self->max_tsd_key; k++) { | |
295 | _pthread_tsd_cleanup_key(self, k); | |
296 | } | |
297 | ||
298 | for (k = __pthread_tsd_first; k <= __pthread_tsd_max; k++) { | |
964d3577 A |
299 | _pthread_tsd_cleanup_key(self, k); |
300 | } | |
301 | } | |
302 | ||
303 | self->max_tsd_key = 0; | |
964d3577 A |
304 | } |
305 | ||
c6e5f90c A |
306 | #if PTHREAD_KEY_LEGACY_SUPPORT |
307 | #import <_simple.h> | |
308 | #import <dlfcn.h> | |
964d3577 A |
309 | static void |
310 | _pthread_tsd_behaviour_check(pthread_t self) | |
311 | { | |
312 | // Iterate from dynamic-key start to dynamic-key end, if the key has both | |
313 | // a desctructor and a value then _pthread_tsd_cleanup_key would cause | |
314 | // us to re-trigger the destructor. | |
964d3577 A |
315 | pthread_key_t k; |
316 | ||
a0619f9c | 317 | for (k = __pthread_tsd_start; k < __pthread_tsd_end; k++) { |
964d3577 A |
318 | void (*destructor)(void *); |
319 | if (_pthread_key_get_destructor(k, &destructor)) { | |
320 | void **ptr = &self->tsd[k]; | |
321 | void *value = *ptr; | |
322 | if (value && destructor) { | |
323 | _simple_asl_log(ASL_LEVEL_ERR, "pthread_tsd", | |
324 | "warning: dynamic tsd keys dirty after static key cleanup loop."); | |
c1f56ec9 A |
325 | #if 0 |
326 | // enable this for debugging | |
327 | Dl_info i; | |
964d3577 A |
328 | if (dladdr(destructor, &i) == 0) { |
329 | _simple_asl_log(ASL_LEVEL_ERR, "pthread_tsd", i.dli_fname); | |
330 | _simple_asl_log(ASL_LEVEL_ERR, "pthread_tsd", i.dli_saddr); | |
331 | } | |
c1f56ec9 | 332 | #endif |
964d3577 A |
333 | } |
334 | } | |
335 | } | |
336 | ||
337 | } | |
338 | ||
339 | static void | |
340 | _pthread_tsd_cleanup_legacy(pthread_t self) | |
341 | { | |
f1a1da6c A |
342 | int j; |
343 | ||
344 | // clean up dynamic keys first | |
345 | for (j = 0; j < PTHREAD_DESTRUCTOR_ITERATIONS; j++) { | |
346 | pthread_key_t k; | |
347 | for (k = __pthread_tsd_start; k <= self->max_tsd_key; k++) { | |
348 | _pthread_tsd_cleanup_key(self, k); | |
349 | } | |
350 | } | |
351 | ||
352 | self->max_tsd_key = 0; | |
353 | ||
354 | // clean up static keys | |
355 | for (j = 0; j < PTHREAD_DESTRUCTOR_ITERATIONS; j++) { | |
356 | pthread_key_t k; | |
357 | for (k = __pthread_tsd_first; k <= __pthread_tsd_max; k++) { | |
358 | _pthread_tsd_cleanup_key(self, k); | |
359 | } | |
964d3577 A |
360 | |
361 | if (__pthread_key_legacy_behaviour_log != 0 && self->max_tsd_key != 0) { | |
362 | // max_tsd_key got dirtied, either by static or dynamic keys being | |
363 | // reset. check for any dirty dynamic keys. | |
364 | _pthread_tsd_behaviour_check(self); | |
365 | } | |
366 | } | |
367 | } | |
c6e5f90c | 368 | #endif // PTHREAD_KEY_LEGACY_SUPPORT |
964d3577 A |
369 | #endif // !VARIANT_DYLD |
370 | ||
371 | void | |
372 | _pthread_tsd_cleanup(pthread_t self) | |
373 | { | |
374 | #if !VARIANT_DYLD | |
375 | ||
376 | // unless __pthread_key_legacy_behaviour == 1, use the new pthread key | |
377 | // destructor order: (dynamic -> static) x5 -> (GC x5) | |
378 | ||
379 | if (__pthread_key_legacy_behaviour == 0) { | |
380 | _pthread_tsd_cleanup_new(self); | |
381 | } else { | |
382 | _pthread_tsd_cleanup_legacy(self); | |
f1a1da6c A |
383 | } |
384 | #endif // !VARIANT_DYLD | |
385 | } | |
386 | ||
387 | #if !VARIANT_DYLD | |
388 | // XXX: key should be pthread_key_t | |
389 | int | |
390 | pthread_key_init_np(int key, void (*destructor)(void *)) | |
391 | { | |
392 | int res = EINVAL; // Returns EINVAL if key is out of range. | |
393 | if (key >= __pthread_tsd_first && key < __pthread_tsd_start) { | |
c1f56ec9 | 394 | _pthread_lock_lock(&__pthread_tsd_lock); |
f1a1da6c A |
395 | _pthread_key_set_destructor(key, destructor); |
396 | if (key > __pthread_tsd_max) { | |
397 | __pthread_tsd_max = key; | |
398 | } | |
c1f56ec9 | 399 | _pthread_lock_unlock(&__pthread_tsd_lock); |
f1a1da6c A |
400 | res = 0; |
401 | } | |
402 | return res; | |
403 | } | |
404 | #endif // !VARIANT_DYLD | |
405 | ||
406 | #undef pthread_self | |
407 | pthread_t | |
408 | pthread_self(void) | |
409 | { | |
e3ecba16 A |
410 | pthread_t self = _pthread_self_direct(); |
411 | _pthread_validate_signature(self); | |
412 | return self; | |
f1a1da6c | 413 | } |
c1f56ec9 A |
414 | |
415 | // rdar://57406917 | |
416 | pthread_t | |
417 | _pthread_self(void) | |
418 | { | |
419 | return pthread_self(); | |
420 | } |