2  * Copyright (c) 2000-2003, 2007, 2012 Apple Inc. All rights reserved. 
   4  * @APPLE_LICENSE_HEADER_START@ 
   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 
  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. 
  21  * @APPLE_LICENSE_HEADER_END@ 
  24  * Copyright 1996 1995 by Open Software Foundation, Inc. 1997 1996 1995 1994 1993 1992 1991   
  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.  
  33  * OSF DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE  
  34  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS  
  35  * FOR A PARTICULAR PURPOSE.  
  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.  
  49  * POSIX Pthread Library 
  50  *   Thread Specific Data support 
  51  *   NB: pthread_getspecific() is in a separate assembly file 
  55 #include <TargetConditionals.h> 
  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. 
  63 static const int __pthread_tsd_first 
= __TSD_RESERVED_MAX 
+ 1; 
  64 static int __pthread_tsd_max 
= __pthread_tsd_first
; 
  65 static const int __pthread_tsd_start 
= _INTERNAL_POSIX_THREAD_KEYS_MAX
; 
  66 static const int __pthread_tsd_end 
= _INTERNAL_POSIX_THREAD_KEYS_END
; 
  68 static int __pthread_key_legacy_behaviour 
= 0; 
  69 static int __pthread_key_legacy_behaviour_log 
= 0; 
  71 // Omit support for pthread key destructors in the static archive for dyld. 
  72 // dyld does not create and destroy threads so these are not necessary. 
  74 // We store the bit-wise negation of the destructor so that a quick non-zero 
  75 // test can be used to determine if the destructor has been set, even if it is 
  76 // NULL. This means that a destructor of value ~0x0ull cannot be used. That 
  77 // shouldn't be a problem in practice since it isn't a valid function address. 
  81 } _pthread_keys
[_INTERNAL_POSIX_THREAD_KEYS_END
]; 
  83 static _pthread_lock tsd_lock 
= _PTHREAD_LOCK_INITIALIZER
; 
  85 // The pthread_tsd destruction order can be reverted to the old (pre-10.11) order 
  86 // by setting this environment variable. 
  88 _pthread_key_global_init(const char *envp
[]) 
  90         __pthread_key_legacy_behaviour 
= _simple_getenv(envp
, "PTHREAD_KEY_LEGACY_DESTRUCTOR_ORDER") ? 1 : 0; 
  91         __pthread_key_legacy_behaviour_log 
= _simple_getenv(envp
, "PTHREAD_KEY_LEGACY_DESTRUCTOR_ORDER_LOG") ? 1 : 0; 
  94 // Returns true if successful, false if destructor was already set. 
  96 _pthread_key_set_destructor(pthread_key_t key
, void (*destructor
)(void *)) 
  98         uintptr_t *ptr 
= &_pthread_keys
[key
].destructor
; 
  99         uintptr_t value 
= ~(uintptr_t)destructor
; 
 107 // Returns true if successful, false if the destructor was not set. 
 109 _pthread_key_unset_destructor(pthread_key_t key
) 
 111         uintptr_t *ptr 
= &_pthread_keys
[key
].destructor
; 
 119 // Returns true if successful, false if the destructor was not set. 
 121 _pthread_key_get_destructor(pthread_key_t key
, void (**destructor
)(void *)) 
 123         uintptr_t value 
= _pthread_keys
[key
].destructor
; 
 125                 *destructor 
= (void (*)(void *))(~value
); 
 131 pthread_key_create(pthread_key_t 
*key
, void (*destructor
)(void *)) 
 133         int res 
= EAGAIN
; // Returns EAGAIN if key cannot be allocated. 
 136         _PTHREAD_LOCK(tsd_lock
); 
 137         for (k 
= __pthread_tsd_start
; k 
< __pthread_tsd_end
; k
++) { 
 138                 if (_pthread_key_set_destructor(k
, destructor
)) { 
 144         _PTHREAD_UNLOCK(tsd_lock
); 
 150 pthread_key_delete(pthread_key_t key
) 
 152         int res 
= EINVAL
; // Returns EINVAL if key is not allocated. 
 154         _PTHREAD_LOCK(tsd_lock
); 
 155         if (key 
>= __pthread_tsd_start 
&& key 
< __pthread_tsd_end
) { 
 156                 if (_pthread_key_unset_destructor(key
)) { 
 158                         _PTHREAD_LOCK(_pthread_list_lock
); 
 159                         TAILQ_FOREACH(p
, &__pthread_head
, plist
) { 
 160                                 // No lock for word-sized write. 
 163                         _PTHREAD_UNLOCK(_pthread_list_lock
); 
 167         _PTHREAD_UNLOCK(tsd_lock
); 
 171 #endif // !VARIANT_DYLD 
 174 pthread_setspecific(pthread_key_t key
, const void *value
) 
 179         if (key 
>= __pthread_tsd_first 
&& key 
< __pthread_tsd_end
) { 
 180                 bool created 
= _pthread_key_get_destructor(key
, NULL
); 
 181                 if (key 
< __pthread_tsd_start 
|| created
) { 
 182                         struct _pthread 
*self 
= pthread_self(); 
 183                         self
->tsd
[key
] = (void *)value
; 
 186                         if (key 
< __pthread_tsd_start
) { 
 187                                 // XXX: is this really necessary? 
 188                                 _pthread_key_set_destructor(key
, NULL
); 
 190                         if (key 
> self
->max_tsd_key
) { 
 191                                 self
->max_tsd_key 
= (int)key
; 
 195 #endif // !VARIANT_DYLD 
 201 _pthread_setspecific_static(pthread_key_t key
, void *value
) 
 206         if (key 
< __pthread_tsd_start
) { 
 207                 _pthread_setspecific_direct(key
, value
); 
 210 #endif // !VARIANT_DYLD 
 216 pthread_getspecific(pthread_key_t key
) 
 218         return _pthread_getspecific_direct(key
); 
 223 _pthread_tsd_cleanup_key(pthread_t self
, pthread_key_t key
) 
 225         void (*destructor
)(void *); 
 226         if (_pthread_key_get_destructor(key
, &destructor
)) { 
 227                 void **ptr 
= &self
->tsd
[key
]; 
 237 #endif // !VARIANT_DYLD 
 244 _pthread_tsd_cleanup_new(pthread_t self
) 
 249         for (j 
= 0; j 
< PTHREAD_DESTRUCTOR_ITERATIONS
; j
++) { 
 251                 for (k 
= __pthread_tsd_start
; k 
<= self
->max_tsd_key
; k
++) { 
 252                         _pthread_tsd_cleanup_key(self
, k
); 
 255                 for (k 
= __pthread_tsd_first
; k 
<= __pthread_tsd_max
; k
++) { 
 256                         _pthread_tsd_cleanup_key(self
, k
); 
 260         self
->max_tsd_key 
= 0; 
 264 _pthread_tsd_behaviour_check(pthread_t self
) 
 266         // Iterate from dynamic-key start to dynamic-key end, if the key has both 
 267         // a desctructor and a value then _pthread_tsd_cleanup_key would cause 
 268         // us to re-trigger the destructor. 
 272         for (k 
= __pthread_tsd_start
; k 
<= __pthread_tsd_end
; k
++) { 
 273                 void (*destructor
)(void *); 
 274                 if (_pthread_key_get_destructor(k
, &destructor
)) { 
 275                         void **ptr 
= &self
->tsd
[k
]; 
 277                         if (value 
&& destructor
) { 
 278                                 _simple_asl_log(ASL_LEVEL_ERR
, "pthread_tsd", 
 279                                                 "warning: dynamic tsd keys dirty after static key cleanup loop."); 
 281                                 if (dladdr(destructor
, &i
) == 0) { 
 282                                         _simple_asl_log(ASL_LEVEL_ERR
, "pthread_tsd", i
.dli_fname
); 
 283                                         _simple_asl_log(ASL_LEVEL_ERR
, "pthread_tsd", i
.dli_saddr
); 
 292 _pthread_tsd_cleanup_legacy(pthread_t self
) 
 296         // clean up dynamic keys first 
 297         for (j 
= 0; j 
< PTHREAD_DESTRUCTOR_ITERATIONS
; j
++) { 
 299                 for (k 
= __pthread_tsd_start
; k 
<= self
->max_tsd_key
; k
++) { 
 300                         _pthread_tsd_cleanup_key(self
, k
); 
 304         self
->max_tsd_key 
= 0; 
 306         // clean up static keys 
 307         for (j 
= 0; j 
< PTHREAD_DESTRUCTOR_ITERATIONS
; j
++) { 
 309                 for (k 
= __pthread_tsd_first
; k 
<= __pthread_tsd_max
; k
++) { 
 310                         _pthread_tsd_cleanup_key(self
, k
); 
 313                 if (__pthread_key_legacy_behaviour_log 
!= 0 && self
->max_tsd_key 
!= 0) { 
 314                         // max_tsd_key got dirtied, either by static or dynamic keys being 
 315                         // reset. check for any dirty dynamic keys. 
 316                         _pthread_tsd_behaviour_check(self
); 
 320 #endif // !VARIANT_DYLD 
 323 _pthread_tsd_cleanup(pthread_t self
) 
 327         // unless __pthread_key_legacy_behaviour == 1, use the new pthread key 
 328         // destructor order: (dynamic -> static) x5 -> (GC x5) 
 330         if (__pthread_key_legacy_behaviour 
== 0) { 
 331                 _pthread_tsd_cleanup_new(self
); 
 333                 _pthread_tsd_cleanup_legacy(self
); 
 335 #endif // !VARIANT_DYLD 
 339 // XXX: key should be pthread_key_t 
 341 pthread_key_init_np(int key
, void (*destructor
)(void *)) 
 343         int res 
= EINVAL
; // Returns EINVAL if key is out of range. 
 344         if (key 
>= __pthread_tsd_first 
&& key 
< __pthread_tsd_start
) { 
 345                 _PTHREAD_LOCK(tsd_lock
); 
 346                 _pthread_key_set_destructor(key
, destructor
); 
 347                 if (key 
> __pthread_tsd_max
) { 
 348                         __pthread_tsd_max 
= key
; 
 350                 _PTHREAD_UNLOCK(tsd_lock
); 
 355 #endif // !VARIANT_DYLD 
 361         return _pthread_getspecific_direct(_PTHREAD_TSD_SLOT_PTHREAD_SELF
);