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