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