]> git.saurik.com Git - apple/libpthread.git/blame_incremental - src/pthread_tsd.c
libpthread-416.11.1.tar.gz
[apple/libpthread.git] / src / pthread_tsd.c
... / ...
CommitLineData
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
71static const int __pthread_tsd_first = __TSD_RESERVED_MAX + 1;
72static const int __pthread_tsd_start = _INTERNAL_POSIX_THREAD_KEYS_MAX;
73static const int __pthread_tsd_end = _INTERNAL_POSIX_THREAD_KEYS_END;
74
75static int __pthread_tsd_max = __pthread_tsd_first;
76static _pthread_lock __pthread_tsd_lock = _PTHREAD_LOCK_INITIALIZER;
77#if PTHREAD_KEY_LEGACY_SUPPORT
78static bool __pthread_key_legacy_behaviour = 0;
79static 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
93static 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.
99void
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.
113static 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.
126static 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.
138static 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
148int
149pthread_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
167int
168pthread_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
191int
192pthread_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
218int
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
233void*
234pthread_getspecific(pthread_key_t key)
235{
236 return _pthread_getspecific_direct(key);
237}
238
239#if !VARIANT_DYLD
240static 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
258static 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>
281static 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
309static 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
341void
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
359int
360pthread_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
377pthread_t
378pthread_self(void)
379{
380 return _pthread_getspecific_direct(_PTHREAD_TSD_SLOT_PTHREAD_SELF);
381}