]> git.saurik.com Git - apple/libpthread.git/blob - src/pthread_tsd.c
libpthread-330.250.2.tar.gz
[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 #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 const int __pthread_tsd_start = _INTERNAL_POSIX_THREAD_KEYS_MAX;
65 static const int __pthread_tsd_end = _INTERNAL_POSIX_THREAD_KEYS_END;
66
67 static int __pthread_tsd_max = __pthread_tsd_first;
68 static _pthread_lock __pthread_tsd_lock = _PTHREAD_LOCK_INITIALIZER;
69 static bool __pthread_key_legacy_behaviour = 0;
70 static bool __pthread_key_legacy_behaviour_log = 0;
71
72 // Omit support for pthread key destructors in the static archive for dyld.
73 // dyld does not create and destroy threads so these are not necessary.
74 //
75 // We store the bit-wise negation of the destructor so that a quick non-zero
76 // test can be used to determine if the destructor has been set, even if it is
77 // NULL. This means that a destructor of value ~0x0ull cannot be used. That
78 // shouldn't be a problem in practice since it isn't a valid function address.
79
80 static struct {
81 uintptr_t destructor;
82 } _pthread_keys[_INTERNAL_POSIX_THREAD_KEYS_END];
83
84 // The pthread_tsd destruction order can be reverted to the old (pre-10.11) order
85 // by setting this environment variable.
86 void
87 _pthread_key_global_init(const char *envp[])
88 {
89 if (_simple_getenv(envp, "PTHREAD_KEY_LEGACY_DESTRUCTOR_ORDER")) {
90 __pthread_key_legacy_behaviour = true;
91 }
92 if (_simple_getenv(envp, "PTHREAD_KEY_LEGACY_DESTRUCTOR_ORDER_LOG")) {
93 __pthread_key_legacy_behaviour_log = true;
94 }
95 }
96
97 // Returns true if successful, false if destructor was already set.
98 static bool
99 _pthread_key_set_destructor(pthread_key_t key, void (*destructor)(void *))
100 {
101 uintptr_t *ptr = &_pthread_keys[key].destructor;
102 uintptr_t value = ~(uintptr_t)destructor;
103 if (*ptr == 0) {
104 *ptr = value;
105 return true;
106 }
107 return false;
108 }
109
110 // Returns true if successful, false if the destructor was not set.
111 static bool
112 _pthread_key_unset_destructor(pthread_key_t key)
113 {
114 uintptr_t *ptr = &_pthread_keys[key].destructor;
115 if (*ptr != 0) {
116 *ptr = 0;
117 return true;
118 }
119 return false;
120 }
121
122 // Returns true if successful, false if the destructor was not set.
123 static bool
124 _pthread_key_get_destructor(pthread_key_t key, void (**destructor)(void *))
125 {
126 uintptr_t value = _pthread_keys[key].destructor;
127 if (destructor) {
128 *destructor = (void (*)(void *))(~value);
129 }
130 return (value != 0);
131 }
132
133 int
134 pthread_key_create(pthread_key_t *key, void (*destructor)(void *))
135 {
136 int res = EAGAIN; // Returns EAGAIN if key cannot be allocated.
137 pthread_key_t k;
138
139 _PTHREAD_LOCK(__pthread_tsd_lock);
140 for (k = __pthread_tsd_start; k < __pthread_tsd_end; k++) {
141 if (_pthread_key_set_destructor(k, destructor)) {
142 *key = k;
143 res = 0;
144 break;
145 }
146 }
147 _PTHREAD_UNLOCK(__pthread_tsd_lock);
148
149 return res;
150 }
151
152 int
153 pthread_key_delete(pthread_key_t key)
154 {
155 int res = EINVAL; // Returns EINVAL if key is not allocated.
156
157 _PTHREAD_LOCK(__pthread_tsd_lock);
158 if (key >= __pthread_tsd_start && key < __pthread_tsd_end) {
159 if (_pthread_key_unset_destructor(key)) {
160 struct _pthread *p;
161 _PTHREAD_LOCK(_pthread_list_lock);
162 TAILQ_FOREACH(p, &__pthread_head, tl_plist) {
163 // No lock for word-sized write.
164 p->tsd[key] = 0;
165 }
166 _PTHREAD_UNLOCK(_pthread_list_lock);
167 res = 0;
168 }
169 }
170 _PTHREAD_UNLOCK(__pthread_tsd_lock);
171
172 return res;
173 }
174 #endif // !VARIANT_DYLD
175
176 int
177 pthread_setspecific(pthread_key_t key, const void *value)
178 {
179 int res = EINVAL;
180
181 #if !VARIANT_DYLD
182 if (key >= __pthread_tsd_first && key < __pthread_tsd_end) {
183 bool created = _pthread_key_get_destructor(key, NULL);
184 if (key < __pthread_tsd_start || created) {
185 struct _pthread *self = pthread_self();
186 self->tsd[key] = (void *)value;
187 res = 0;
188
189 if (key < __pthread_tsd_start) {
190 // XXX: is this really necessary?
191 _pthread_key_set_destructor(key, NULL);
192 }
193 if (key > self->max_tsd_key) {
194 self->max_tsd_key = (uint16_t)key;
195 }
196 }
197 }
198 #endif // !VARIANT_DYLD
199
200 return res;
201 }
202
203 int
204 _pthread_setspecific_static(pthread_key_t key, void *value)
205 {
206 int res = EINVAL;
207
208 #if !VARIANT_DYLD
209 if (key < __pthread_tsd_start) {
210 _pthread_setspecific_direct(key, value);
211 res = 0;
212 }
213 #endif // !VARIANT_DYLD
214
215 return res;
216 }
217
218 void*
219 pthread_getspecific(pthread_key_t key)
220 {
221 return _pthread_getspecific_direct(key);
222 }
223
224 #if !VARIANT_DYLD
225 static void
226 _pthread_tsd_cleanup_key(pthread_t self, pthread_key_t key)
227 {
228 void (*destructor)(void *);
229 if (_pthread_key_get_destructor(key, &destructor)) {
230 void **ptr = &self->tsd[key];
231 void *value = *ptr;
232 if (value) {
233 *ptr = NULL;
234 if (destructor) {
235 destructor(value);
236 }
237 }
238 }
239 }
240 #endif // !VARIANT_DYLD
241
242 #import <_simple.h>
243 #import <dlfcn.h>
244
245 #if !VARIANT_DYLD
246 static void
247 _pthread_tsd_cleanup_new(pthread_t self)
248 {
249 int j;
250
251 // clean up all keys
252 for (j = 0; j < PTHREAD_DESTRUCTOR_ITERATIONS; j++) {
253 pthread_key_t k;
254 for (k = __pthread_tsd_start; k <= self->max_tsd_key; k++) {
255 _pthread_tsd_cleanup_key(self, k);
256 }
257
258 for (k = __pthread_tsd_first; k <= __pthread_tsd_max; k++) {
259 _pthread_tsd_cleanup_key(self, k);
260 }
261 }
262
263 self->max_tsd_key = 0;
264 }
265
266 static void
267 _pthread_tsd_behaviour_check(pthread_t self)
268 {
269 // Iterate from dynamic-key start to dynamic-key end, if the key has both
270 // a desctructor and a value then _pthread_tsd_cleanup_key would cause
271 // us to re-trigger the destructor.
272 Dl_info i;
273 pthread_key_t k;
274
275 for (k = __pthread_tsd_start; k < __pthread_tsd_end; k++) {
276 void (*destructor)(void *);
277 if (_pthread_key_get_destructor(k, &destructor)) {
278 void **ptr = &self->tsd[k];
279 void *value = *ptr;
280 if (value && destructor) {
281 _simple_asl_log(ASL_LEVEL_ERR, "pthread_tsd",
282 "warning: dynamic tsd keys dirty after static key cleanup loop.");
283
284 if (dladdr(destructor, &i) == 0) {
285 _simple_asl_log(ASL_LEVEL_ERR, "pthread_tsd", i.dli_fname);
286 _simple_asl_log(ASL_LEVEL_ERR, "pthread_tsd", i.dli_saddr);
287 }
288 }
289 }
290 }
291
292 }
293
294 static void
295 _pthread_tsd_cleanup_legacy(pthread_t self)
296 {
297 int j;
298
299 // clean up dynamic keys first
300 for (j = 0; j < PTHREAD_DESTRUCTOR_ITERATIONS; j++) {
301 pthread_key_t k;
302 for (k = __pthread_tsd_start; k <= self->max_tsd_key; k++) {
303 _pthread_tsd_cleanup_key(self, k);
304 }
305 }
306
307 self->max_tsd_key = 0;
308
309 // clean up static keys
310 for (j = 0; j < PTHREAD_DESTRUCTOR_ITERATIONS; j++) {
311 pthread_key_t k;
312 for (k = __pthread_tsd_first; k <= __pthread_tsd_max; k++) {
313 _pthread_tsd_cleanup_key(self, k);
314 }
315
316 if (__pthread_key_legacy_behaviour_log != 0 && self->max_tsd_key != 0) {
317 // max_tsd_key got dirtied, either by static or dynamic keys being
318 // reset. check for any dirty dynamic keys.
319 _pthread_tsd_behaviour_check(self);
320 }
321 }
322 }
323 #endif // !VARIANT_DYLD
324
325 void
326 _pthread_tsd_cleanup(pthread_t self)
327 {
328 #if !VARIANT_DYLD
329
330 // unless __pthread_key_legacy_behaviour == 1, use the new pthread key
331 // destructor order: (dynamic -> static) x5 -> (GC x5)
332
333 if (__pthread_key_legacy_behaviour == 0) {
334 _pthread_tsd_cleanup_new(self);
335 } else {
336 _pthread_tsd_cleanup_legacy(self);
337 }
338 #endif // !VARIANT_DYLD
339 }
340
341 #if !VARIANT_DYLD
342 // XXX: key should be pthread_key_t
343 int
344 pthread_key_init_np(int key, void (*destructor)(void *))
345 {
346 int res = EINVAL; // Returns EINVAL if key is out of range.
347 if (key >= __pthread_tsd_first && key < __pthread_tsd_start) {
348 _PTHREAD_LOCK(__pthread_tsd_lock);
349 _pthread_key_set_destructor(key, destructor);
350 if (key > __pthread_tsd_max) {
351 __pthread_tsd_max = key;
352 }
353 _PTHREAD_UNLOCK(__pthread_tsd_lock);
354 res = 0;
355 }
356 return res;
357 }
358 #endif // !VARIANT_DYLD
359
360 #undef pthread_self
361 pthread_t
362 pthread_self(void)
363 {
364 return _pthread_getspecific_direct(_PTHREAD_TSD_SLOT_PTHREAD_SELF);
365 }