]> git.saurik.com Git - apple/libpthread.git/blame - src/pthread_tsd.c
libpthread-454.100.8.tar.gz
[apple/libpthread.git] / src / pthread_tsd.c
CommitLineData
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
70static const int __pthread_tsd_first = __TSD_RESERVED_MAX + 1;
f1a1da6c
A
71static const int __pthread_tsd_start = _INTERNAL_POSIX_THREAD_KEYS_MAX;
72static const int __pthread_tsd_end = _INTERNAL_POSIX_THREAD_KEYS_END;
73
214d78a2
A
74static int __pthread_tsd_max = __pthread_tsd_first;
75static _pthread_lock __pthread_tsd_lock = _PTHREAD_LOCK_INITIALIZER;
c6e5f90c 76#if PTHREAD_KEY_LEGACY_SUPPORT
214d78a2
A
77static bool __pthread_key_legacy_behaviour = 0;
78static 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
92static 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.
98void
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.
112static 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.
125static 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.
137static 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
147int
148pthread_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
166int
167pthread_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
189static 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
214int
215pthread_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
224int
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
239void*
240pthread_getspecific(pthread_key_t key)
241{
242 return _pthread_getspecific_direct(key);
243}
244
245#if !VARIANT_DYLD
c1f56ec9
A
246int
247pthread_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
259void *
260pthread_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
270static 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
286static 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
309static 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
339static 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
371void
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
389int
390pthread_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
407pthread_t
408pthread_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
416pthread_t
417_pthread_self(void)
418{
419 return pthread_self();
420}