]>
Commit | Line | Data |
---|---|---|
f1a1da6c A |
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 | #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 | ||
83 | static pthread_lock_t tsd_lock = LOCK_INITIALIZER; | |
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 | ||
136 | LOCK(tsd_lock); | |
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 | } | |
144 | UNLOCK(tsd_lock); | |
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 | ||
154 | LOCK(tsd_lock); | |
155 | if (key >= __pthread_tsd_start && key < __pthread_tsd_end) { | |
156 | if (_pthread_key_unset_destructor(key)) { | |
157 | struct _pthread *p; | |
158 | LOCK(_pthread_list_lock); | |
159 | TAILQ_FOREACH(p, &__pthread_head, plist) { | |
160 | // No lock for word-sized write. | |
161 | p->tsd[key] = 0; | |
162 | } | |
163 | UNLOCK(_pthread_list_lock); | |
164 | res = 0; | |
165 | } | |
166 | } | |
167 | UNLOCK(tsd_lock); | |
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 | ||
200 | void* | |
201 | pthread_getspecific(pthread_key_t key) | |
202 | { | |
203 | return _pthread_getspecific_direct(key); | |
204 | } | |
205 | ||
206 | #if !VARIANT_DYLD | |
207 | static void | |
208 | _pthread_tsd_cleanup_key(pthread_t self, pthread_key_t key) | |
209 | { | |
210 | void (*destructor)(void *); | |
211 | if (_pthread_key_get_destructor(key, &destructor)) { | |
212 | void **ptr = &self->tsd[key]; | |
213 | void *value = *ptr; | |
214 | if (value) { | |
215 | *ptr = NULL; | |
216 | if (destructor) { | |
217 | destructor(value); | |
218 | } | |
219 | } | |
220 | } | |
221 | } | |
222 | #endif // !VARIANT_DYLD | |
223 | ||
964d3577 A |
224 | #import <_simple.h> |
225 | #import <dlfcn.h> | |
226 | ||
f1a1da6c | 227 | #if !VARIANT_DYLD |
964d3577 A |
228 | static void |
229 | _pthread_tsd_cleanup_new(pthread_t self) | |
230 | { | |
231 | int j; | |
232 | ||
233 | // clean up all keys except the garbage collection key | |
234 | for (j = 0; j < PTHREAD_DESTRUCTOR_ITERATIONS; j++) { | |
235 | pthread_key_t k; | |
236 | for (k = __pthread_tsd_start; k <= self->max_tsd_key; k++) { | |
237 | _pthread_tsd_cleanup_key(self, k); | |
238 | } | |
239 | ||
240 | for (k = __pthread_tsd_first; k <= __pthread_tsd_max; k++) { | |
241 | if (k >= __PTK_FRAMEWORK_GC_KEY0 && k <= __PTK_FRAMEWORK_GC_KEY9) { | |
242 | // GC must be cleaned up last | |
243 | continue; | |
244 | } | |
245 | _pthread_tsd_cleanup_key(self, k); | |
246 | } | |
247 | } | |
248 | ||
249 | self->max_tsd_key = 0; | |
250 | ||
251 | // clean up all the GC keys | |
252 | for (j = 0; j < PTHREAD_DESTRUCTOR_ITERATIONS; j++) { | |
253 | pthread_key_t k; | |
254 | for (k = __PTK_FRAMEWORK_GC_KEY0; k <= __PTK_FRAMEWORK_GC_KEY9; k++) { | |
255 | _pthread_tsd_cleanup_key(self, k); | |
256 | } | |
257 | } | |
258 | } | |
259 | ||
260 | static void | |
261 | _pthread_tsd_behaviour_check(pthread_t self) | |
262 | { | |
263 | // Iterate from dynamic-key start to dynamic-key end, if the key has both | |
264 | // a desctructor and a value then _pthread_tsd_cleanup_key would cause | |
265 | // us to re-trigger the destructor. | |
266 | Dl_info i; | |
267 | pthread_key_t k; | |
268 | ||
269 | for (k = __pthread_tsd_start; k <= __pthread_tsd_end; k++) { | |
270 | void (*destructor)(void *); | |
271 | if (_pthread_key_get_destructor(k, &destructor)) { | |
272 | void **ptr = &self->tsd[k]; | |
273 | void *value = *ptr; | |
274 | if (value && destructor) { | |
275 | _simple_asl_log(ASL_LEVEL_ERR, "pthread_tsd", | |
276 | "warning: dynamic tsd keys dirty after static key cleanup loop."); | |
277 | ||
278 | if (dladdr(destructor, &i) == 0) { | |
279 | _simple_asl_log(ASL_LEVEL_ERR, "pthread_tsd", i.dli_fname); | |
280 | _simple_asl_log(ASL_LEVEL_ERR, "pthread_tsd", i.dli_saddr); | |
281 | } | |
282 | } | |
283 | } | |
284 | } | |
285 | ||
286 | } | |
287 | ||
288 | static void | |
289 | _pthread_tsd_cleanup_legacy(pthread_t self) | |
290 | { | |
f1a1da6c A |
291 | int j; |
292 | ||
293 | // clean up dynamic keys first | |
294 | for (j = 0; j < PTHREAD_DESTRUCTOR_ITERATIONS; j++) { | |
295 | pthread_key_t k; | |
296 | for (k = __pthread_tsd_start; k <= self->max_tsd_key; k++) { | |
297 | _pthread_tsd_cleanup_key(self, k); | |
298 | } | |
299 | } | |
300 | ||
301 | self->max_tsd_key = 0; | |
302 | ||
303 | // clean up static keys | |
304 | for (j = 0; j < PTHREAD_DESTRUCTOR_ITERATIONS; j++) { | |
305 | pthread_key_t k; | |
306 | for (k = __pthread_tsd_first; k <= __pthread_tsd_max; k++) { | |
307 | _pthread_tsd_cleanup_key(self, k); | |
308 | } | |
964d3577 A |
309 | |
310 | if (__pthread_key_legacy_behaviour_log != 0 && self->max_tsd_key != 0) { | |
311 | // max_tsd_key got dirtied, either by static or dynamic keys being | |
312 | // reset. check for any dirty dynamic keys. | |
313 | _pthread_tsd_behaviour_check(self); | |
314 | } | |
315 | } | |
316 | } | |
317 | #endif // !VARIANT_DYLD | |
318 | ||
319 | void | |
320 | _pthread_tsd_cleanup(pthread_t self) | |
321 | { | |
322 | #if !VARIANT_DYLD | |
323 | ||
324 | // unless __pthread_key_legacy_behaviour == 1, use the new pthread key | |
325 | // destructor order: (dynamic -> static) x5 -> (GC x5) | |
326 | ||
327 | if (__pthread_key_legacy_behaviour == 0) { | |
328 | _pthread_tsd_cleanup_new(self); | |
329 | } else { | |
330 | _pthread_tsd_cleanup_legacy(self); | |
f1a1da6c A |
331 | } |
332 | #endif // !VARIANT_DYLD | |
333 | } | |
334 | ||
335 | #if !VARIANT_DYLD | |
336 | // XXX: key should be pthread_key_t | |
337 | int | |
338 | pthread_key_init_np(int key, void (*destructor)(void *)) | |
339 | { | |
340 | int res = EINVAL; // Returns EINVAL if key is out of range. | |
341 | if (key >= __pthread_tsd_first && key < __pthread_tsd_start) { | |
342 | LOCK(tsd_lock); | |
343 | _pthread_key_set_destructor(key, destructor); | |
344 | if (key > __pthread_tsd_max) { | |
345 | __pthread_tsd_max = key; | |
346 | } | |
347 | UNLOCK(tsd_lock); | |
348 | res = 0; | |
349 | } | |
350 | return res; | |
351 | } | |
352 | #endif // !VARIANT_DYLD | |
353 | ||
354 | #undef pthread_self | |
355 | pthread_t | |
356 | pthread_self(void) | |
357 | { | |
358 | return _pthread_getspecific_direct(_PTHREAD_TSD_SLOT_PTHREAD_SELF); | |
359 | } |