]> git.saurik.com Git - apple/libpthread.git/blob - src/pthread_tsd.c
c76bba69b05f1140b1372729a516630a6369ae70
[apple/libpthread.git] / src / pthread_tsd.c
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 pthread_t self = _pthread_self_direct();
381 _pthread_validate_signature(self);
382 return self;
383 }