Revert "Revert "Pull the pthread_key_t functions out of pthread.c.""
This reverts commit 6260553d48
(Removing the accidental libm/Android.mk change.)
Change-Id: I6cddd9857c31facc05636e8221505b3d2344cb75
This commit is contained in:
parent
8397cdba94
commit
44b53ad681
8 changed files with 336 additions and 323 deletions
|
@ -406,7 +406,8 @@ libc_common_src_files += \
|
|||
bionic/ptrace.c.arm
|
||||
|
||||
libc_static_common_src_files += \
|
||||
bionic/pthread.c.arm \
|
||||
bionic/pthread.c.arm \
|
||||
bionic/pthread_key.cpp.arm \
|
||||
|
||||
# these are used by the static and dynamic versions of the libc
|
||||
# respectively
|
||||
|
@ -446,7 +447,8 @@ libc_common_src_files += \
|
|||
bionic/ptrace.c
|
||||
|
||||
libc_static_common_src_files += \
|
||||
bionic/pthread.c \
|
||||
bionic/pthread.c \
|
||||
bionic/pthread_key.cpp \
|
||||
|
||||
libc_arch_static_src_files := \
|
||||
bionic/dl_iterate_phdr_static.c
|
||||
|
@ -492,7 +494,8 @@ libc_common_src_files += \
|
|||
bionic/ptrace.c
|
||||
|
||||
libc_static_common_src_files += \
|
||||
bionic/pthread.c
|
||||
bionic/pthread.c
|
||||
bionic/pthread_key.cpp \
|
||||
|
||||
libc_arch_static_src_files := \
|
||||
bionic/dl_iterate_phdr_static.c
|
||||
|
|
|
@ -101,8 +101,8 @@ static const pthread_attr_t gDefaultPthreadAttr = {
|
|||
.sched_priority = 0
|
||||
};
|
||||
|
||||
static pthread_internal_t* gThreadList = NULL;
|
||||
static pthread_mutex_t gThreadListLock = PTHREAD_MUTEX_INITIALIZER;
|
||||
__LIBC_HIDDEN__ pthread_internal_t* gThreadList = NULL;
|
||||
__LIBC_HIDDEN__ pthread_mutex_t gThreadListLock = PTHREAD_MUTEX_INITIALIZER;
|
||||
static pthread_mutex_t gDebuggerNotificationLock = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
static void _pthread_internal_remove_locked(pthread_internal_t* thread) {
|
||||
|
@ -550,9 +550,6 @@ void __pthread_cleanup_pop( __pthread_cleanup_t* c, int execute )
|
|||
c->__cleanup_routine(c->__cleanup_arg);
|
||||
}
|
||||
|
||||
/* used by pthread_exit() to clean all TLS keys of the current thread */
|
||||
static void pthread_key_clean_all(void);
|
||||
|
||||
void pthread_exit(void * retval)
|
||||
{
|
||||
pthread_internal_t* thread = __get_thread();
|
||||
|
@ -1780,303 +1777,6 @@ int pthread_cond_timeout_np(pthread_cond_t *cond,
|
|||
}
|
||||
|
||||
|
||||
|
||||
/* A technical note regarding our thread-local-storage (TLS) implementation:
|
||||
*
|
||||
* There can be up to TLSMAP_SIZE independent TLS keys in a given process,
|
||||
* though the first TLSMAP_START keys are reserved for Bionic to hold
|
||||
* special thread-specific variables like errno or a pointer to
|
||||
* the current thread's descriptor.
|
||||
*
|
||||
* while stored in the TLS area, these entries cannot be accessed through
|
||||
* pthread_getspecific() / pthread_setspecific() and pthread_key_delete()
|
||||
*
|
||||
* also, some entries in the key table are pre-allocated (see tlsmap_lock)
|
||||
* to greatly simplify and speedup some OpenGL-related operations. though the
|
||||
* initialy value will be NULL on all threads.
|
||||
*
|
||||
* you can use pthread_getspecific()/setspecific() on these, and in theory
|
||||
* you could also call pthread_key_delete() as well, though this would
|
||||
* probably break some apps.
|
||||
*
|
||||
* The 'tlsmap_t' type defined below implements a shared global map of
|
||||
* currently created/allocated TLS keys and the destructors associated
|
||||
* with them. You should use tlsmap_lock/unlock to access it to avoid
|
||||
* any race condition.
|
||||
*
|
||||
* the global TLS map simply contains a bitmap of allocated keys, and
|
||||
* an array of destructors.
|
||||
*
|
||||
* each thread has a TLS area that is a simple array of TLSMAP_SIZE void*
|
||||
* pointers. the TLS area of the main thread is stack-allocated in
|
||||
* __libc_init_common, while the TLS area of other threads is placed at
|
||||
* the top of their stack in pthread_create.
|
||||
*
|
||||
* when pthread_key_create() is called, it finds the first free key in the
|
||||
* bitmap, then set it to 1, saving the destructor altogether
|
||||
*
|
||||
* when pthread_key_delete() is called. it will erase the key's bitmap bit
|
||||
* and its destructor, and will also clear the key data in the TLS area of
|
||||
* all created threads. As mandated by Posix, it is the responsability of
|
||||
* the caller of pthread_key_delete() to properly reclaim the objects that
|
||||
* were pointed to by these data fields (either before or after the call).
|
||||
*
|
||||
*/
|
||||
|
||||
/* TLS Map implementation
|
||||
*/
|
||||
|
||||
#define TLSMAP_START (TLS_SLOT_MAX_WELL_KNOWN+1)
|
||||
#define TLSMAP_SIZE BIONIC_TLS_SLOTS
|
||||
#define TLSMAP_BITS 32
|
||||
#define TLSMAP_WORDS ((TLSMAP_SIZE+TLSMAP_BITS-1)/TLSMAP_BITS)
|
||||
#define TLSMAP_WORD(m,k) (m)->map[(k)/TLSMAP_BITS]
|
||||
#define TLSMAP_MASK(k) (1U << ((k)&(TLSMAP_BITS-1)))
|
||||
|
||||
/* this macro is used to quickly check that a key belongs to a reasonable range */
|
||||
#define TLSMAP_VALIDATE_KEY(key) \
|
||||
((key) >= TLSMAP_START && (key) < TLSMAP_SIZE)
|
||||
|
||||
/* the type of tls key destructor functions */
|
||||
typedef void (*tls_dtor_t)(void*);
|
||||
|
||||
typedef struct {
|
||||
int init; /* see comment in tlsmap_lock() */
|
||||
uint32_t map[TLSMAP_WORDS]; /* bitmap of allocated keys */
|
||||
tls_dtor_t dtors[TLSMAP_SIZE]; /* key destructors */
|
||||
} tlsmap_t;
|
||||
|
||||
static pthread_mutex_t _tlsmap_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
static tlsmap_t _tlsmap;
|
||||
|
||||
/* lock the global TLS map lock and return a handle to it */
|
||||
static __inline__ tlsmap_t* tlsmap_lock(void)
|
||||
{
|
||||
tlsmap_t* m = &_tlsmap;
|
||||
|
||||
pthread_mutex_lock(&_tlsmap_lock);
|
||||
/* we need to initialize the first entry of the 'map' array
|
||||
* with the value TLS_DEFAULT_ALLOC_MAP. doing it statically
|
||||
* when declaring _tlsmap is a bit awkward and is going to
|
||||
* produce warnings, so do it the first time we use the map
|
||||
* instead
|
||||
*/
|
||||
if (__unlikely(!m->init)) {
|
||||
TLSMAP_WORD(m,0) = TLS_DEFAULT_ALLOC_MAP;
|
||||
m->init = 1;
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
/* unlock the global TLS map */
|
||||
static __inline__ void tlsmap_unlock(tlsmap_t* m)
|
||||
{
|
||||
pthread_mutex_unlock(&_tlsmap_lock);
|
||||
(void)m; /* a good compiler is a happy compiler */
|
||||
}
|
||||
|
||||
/* test to see wether a key is allocated */
|
||||
static __inline__ int tlsmap_test(tlsmap_t* m, int key)
|
||||
{
|
||||
return (TLSMAP_WORD(m,key) & TLSMAP_MASK(key)) != 0;
|
||||
}
|
||||
|
||||
/* set the destructor and bit flag on a newly allocated key */
|
||||
static __inline__ void tlsmap_set(tlsmap_t* m, int key, tls_dtor_t dtor)
|
||||
{
|
||||
TLSMAP_WORD(m,key) |= TLSMAP_MASK(key);
|
||||
m->dtors[key] = dtor;
|
||||
}
|
||||
|
||||
/* clear the destructor and bit flag on an existing key */
|
||||
static __inline__ void tlsmap_clear(tlsmap_t* m, int key)
|
||||
{
|
||||
TLSMAP_WORD(m,key) &= ~TLSMAP_MASK(key);
|
||||
m->dtors[key] = NULL;
|
||||
}
|
||||
|
||||
/* allocate a new TLS key, return -1 if no room left */
|
||||
static int tlsmap_alloc(tlsmap_t* m, tls_dtor_t dtor)
|
||||
{
|
||||
int key;
|
||||
|
||||
for ( key = TLSMAP_START; key < TLSMAP_SIZE; key++ ) {
|
||||
if ( !tlsmap_test(m, key) ) {
|
||||
tlsmap_set(m, key, dtor);
|
||||
return key;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int pthread_key_create(pthread_key_t *key, void (*destructor_function)(void *))
|
||||
{
|
||||
uint32_t err = ENOMEM;
|
||||
tlsmap_t* map = tlsmap_lock();
|
||||
int k = tlsmap_alloc(map, destructor_function);
|
||||
|
||||
if (k >= 0) {
|
||||
*key = k;
|
||||
err = 0;
|
||||
}
|
||||
tlsmap_unlock(map);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/* This deletes a pthread_key_t. note that the standard mandates that this does
|
||||
* not call the destructor of non-NULL key values. Instead, it is the
|
||||
* responsibility of the caller to properly dispose of the corresponding data
|
||||
* and resources, using any means it finds suitable.
|
||||
*
|
||||
* On the other hand, this function will clear the corresponding key data
|
||||
* values in all known threads. this prevents later (invalid) calls to
|
||||
* pthread_getspecific() to receive invalid/stale values.
|
||||
*/
|
||||
int pthread_key_delete(pthread_key_t key)
|
||||
{
|
||||
uint32_t err;
|
||||
pthread_internal_t* thr;
|
||||
tlsmap_t* map;
|
||||
|
||||
if (!TLSMAP_VALIDATE_KEY(key)) {
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
map = tlsmap_lock();
|
||||
|
||||
if (!tlsmap_test(map, key)) {
|
||||
err = EINVAL;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
/* clear value in all threads */
|
||||
pthread_mutex_lock(&gThreadListLock);
|
||||
for ( thr = gThreadList; thr != NULL; thr = thr->next ) {
|
||||
/* avoid zombie threads with a negative 'join_count'. these are really
|
||||
* already dead and don't have a TLS area anymore.
|
||||
*
|
||||
* similarly, it is possible to have thr->tls == NULL for threads that
|
||||
* were just recently created through pthread_create() but whose
|
||||
* startup trampoline (__thread_entry) hasn't been run yet by the
|
||||
* scheduler. thr->tls will also be NULL after it's stack has been
|
||||
* unmapped but before the ongoing pthread_join() is finished.
|
||||
* so check for this too.
|
||||
*/
|
||||
if (thr->join_count < 0 || !thr->tls)
|
||||
continue;
|
||||
|
||||
thr->tls[key] = NULL;
|
||||
}
|
||||
tlsmap_clear(map, key);
|
||||
|
||||
pthread_mutex_unlock(&gThreadListLock);
|
||||
err = 0;
|
||||
|
||||
err1:
|
||||
tlsmap_unlock(map);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int pthread_setspecific(pthread_key_t key, const void *ptr)
|
||||
{
|
||||
int err = EINVAL;
|
||||
tlsmap_t* map;
|
||||
|
||||
if (TLSMAP_VALIDATE_KEY(key)) {
|
||||
/* check that we're trying to set data for an allocated key */
|
||||
map = tlsmap_lock();
|
||||
if (tlsmap_test(map, key)) {
|
||||
((uint32_t *)__get_tls())[key] = (uint32_t)ptr;
|
||||
err = 0;
|
||||
}
|
||||
tlsmap_unlock(map);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
void * pthread_getspecific(pthread_key_t key)
|
||||
{
|
||||
if (!TLSMAP_VALIDATE_KEY(key)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* for performance reason, we do not lock/unlock the global TLS map
|
||||
* to check that the key is properly allocated. if the key was not
|
||||
* allocated, the value read from the TLS should always be NULL
|
||||
* due to pthread_key_delete() clearing the values for all threads.
|
||||
*/
|
||||
return (void *)(((unsigned *)__get_tls())[key]);
|
||||
}
|
||||
|
||||
/* Posix mandates that this be defined in <limits.h> but we don't have
|
||||
* it just yet.
|
||||
*/
|
||||
#ifndef PTHREAD_DESTRUCTOR_ITERATIONS
|
||||
# define PTHREAD_DESTRUCTOR_ITERATIONS 4
|
||||
#endif
|
||||
|
||||
/* this function is called from pthread_exit() to remove all TLS key data
|
||||
* from this thread's TLS area. this must call the destructor of all keys
|
||||
* that have a non-NULL data value (and a non-NULL destructor).
|
||||
*
|
||||
* because destructors can do funky things like deleting/creating other
|
||||
* keys, we need to implement this in a loop
|
||||
*/
|
||||
static void pthread_key_clean_all(void)
|
||||
{
|
||||
tlsmap_t* map;
|
||||
void** tls = (void**)__get_tls();
|
||||
int rounds = PTHREAD_DESTRUCTOR_ITERATIONS;
|
||||
|
||||
map = tlsmap_lock();
|
||||
|
||||
for (rounds = PTHREAD_DESTRUCTOR_ITERATIONS; rounds > 0; rounds--)
|
||||
{
|
||||
int kk, count = 0;
|
||||
|
||||
for (kk = TLSMAP_START; kk < TLSMAP_SIZE; kk++) {
|
||||
if ( tlsmap_test(map, kk) )
|
||||
{
|
||||
void* data = tls[kk];
|
||||
tls_dtor_t dtor = map->dtors[kk];
|
||||
|
||||
if (data != NULL && dtor != NULL)
|
||||
{
|
||||
/* we need to clear the key data now, this will prevent the
|
||||
* destructor (or a later one) from seeing the old value if
|
||||
* it calls pthread_getspecific() for some odd reason
|
||||
*
|
||||
* we do not do this if 'dtor == NULL' just in case another
|
||||
* destructor function might be responsible for manually
|
||||
* releasing the corresponding data.
|
||||
*/
|
||||
tls[kk] = NULL;
|
||||
|
||||
/* because the destructor is free to call pthread_key_create
|
||||
* and/or pthread_key_delete, we need to temporarily unlock
|
||||
* the TLS map
|
||||
*/
|
||||
tlsmap_unlock(map);
|
||||
(*dtor)(data);
|
||||
map = tlsmap_lock();
|
||||
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* if we didn't call any destructor, there is no need to check the
|
||||
* TLS data again
|
||||
*/
|
||||
if (count == 0)
|
||||
break;
|
||||
}
|
||||
tlsmap_unlock(map);
|
||||
}
|
||||
|
||||
// man says this should be in <linux/unistd.h>, but it isn't
|
||||
extern int tgkill(int tgid, int tid, int sig);
|
||||
|
||||
|
|
|
@ -60,6 +60,11 @@ int _init_thread(pthread_internal_t* thread, pid_t kernel_id, const pthread_attr
|
|||
void _pthread_internal_add( pthread_internal_t* thread );
|
||||
pthread_internal_t* __get_thread(void);
|
||||
|
||||
__LIBC_HIDDEN__ void pthread_key_clean_all(void);
|
||||
|
||||
extern pthread_internal_t* gThreadList;
|
||||
extern pthread_mutex_t gThreadListLock;
|
||||
|
||||
/* needed by posix-timers.c */
|
||||
|
||||
static __inline__ void timespec_add( struct timespec* a, const struct timespec* b )
|
||||
|
|
280
libc/bionic/pthread_key.cpp
Normal file
280
libc/bionic/pthread_key.cpp
Normal file
|
@ -0,0 +1,280 @@
|
|||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <malloc.h>
|
||||
#include <memory.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/atomics.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "bionic_atomic_inline.h"
|
||||
#include "bionic_futex.h"
|
||||
#include "bionic_pthread.h"
|
||||
#include "bionic_ssp.h"
|
||||
#include "bionic_tls.h"
|
||||
#include "debug_format.h"
|
||||
#include "pthread_internal.h"
|
||||
#include "thread_private.h"
|
||||
|
||||
/* A technical note regarding our thread-local-storage (TLS) implementation:
|
||||
*
|
||||
* There can be up to BIONIC_TLS_SLOTS independent TLS keys in a given process,
|
||||
* The keys below TLS_SLOT_FIRST_USER_SLOT are reserved for Bionic to hold
|
||||
* special thread-specific variables like errno or a pointer to
|
||||
* the current thread's descriptor. These entries cannot be accessed through
|
||||
* pthread_getspecific() / pthread_setspecific() or pthread_key_delete()
|
||||
*
|
||||
* The 'tls_map_t' type defined below implements a shared global map of
|
||||
* currently created/allocated TLS keys and the destructors associated
|
||||
* with them.
|
||||
*
|
||||
* The global TLS map simply contains a bitmap of allocated keys, and
|
||||
* an array of destructors.
|
||||
*
|
||||
* Each thread has a TLS area that is a simple array of BIONIC_TLS_SLOTS void*
|
||||
* pointers. the TLS area of the main thread is stack-allocated in
|
||||
* __libc_init_common, while the TLS area of other threads is placed at
|
||||
* the top of their stack in pthread_create.
|
||||
*
|
||||
* When pthread_key_delete() is called it will erase the key's bitmap bit
|
||||
* and its destructor, and will also clear the key data in the TLS area of
|
||||
* all created threads. As mandated by Posix, it is the responsibility of
|
||||
* the caller of pthread_key_delete() to properly reclaim the objects that
|
||||
* were pointed to by these data fields (either before or after the call).
|
||||
*/
|
||||
|
||||
#define TLSMAP_BITS 32
|
||||
#define TLSMAP_WORDS ((BIONIC_TLS_SLOTS+TLSMAP_BITS-1)/TLSMAP_BITS)
|
||||
#define TLSMAP_WORD(m,k) (m).map[(k)/TLSMAP_BITS]
|
||||
#define TLSMAP_MASK(k) (1U << ((k)&(TLSMAP_BITS-1)))
|
||||
|
||||
static inline bool IsValidUserKey(pthread_key_t key) {
|
||||
return (key >= TLS_SLOT_FIRST_USER_SLOT && key < BIONIC_TLS_SLOTS);
|
||||
}
|
||||
|
||||
typedef void (*key_destructor_t)(void*);
|
||||
|
||||
struct tls_map_t {
|
||||
bool is_initialized;
|
||||
|
||||
/* bitmap of allocated keys */
|
||||
uint32_t map[TLSMAP_WORDS];
|
||||
|
||||
key_destructor_t key_destructors[BIONIC_TLS_SLOTS];
|
||||
};
|
||||
|
||||
class ScopedTlsMapAccess {
|
||||
public:
|
||||
ScopedTlsMapAccess() {
|
||||
Lock();
|
||||
|
||||
// If this is the first time the TLS map has been accessed,
|
||||
// mark the slots belonging to well-known keys as being in use.
|
||||
// This isn't currently necessary because the well-known keys
|
||||
// can only be accessed directly by bionic itself, do not have
|
||||
// destructors, and all the functions that touch the TLS map
|
||||
// start after the maximum well-known slot.
|
||||
if (!s_tls_map_.is_initialized) {
|
||||
for (pthread_key_t key = 0; key < TLS_SLOT_FIRST_USER_SLOT; ++key) {
|
||||
SetInUse(key, NULL);
|
||||
}
|
||||
s_tls_map_.is_initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
~ScopedTlsMapAccess() {
|
||||
Unlock();
|
||||
}
|
||||
|
||||
int CreateKey(pthread_key_t* result, void (*key_destructor)(void*)) {
|
||||
// Take the first unallocated key.
|
||||
for (int key = 0; key < BIONIC_TLS_SLOTS; ++key) {
|
||||
if (!IsInUse(key)) {
|
||||
SetInUse(key, key_destructor);
|
||||
*result = key;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// We hit PTHREAD_KEYS_MAX. POSIX says EAGAIN for this case.
|
||||
return EAGAIN;
|
||||
}
|
||||
|
||||
void DeleteKey(pthread_key_t key) {
|
||||
TLSMAP_WORD(s_tls_map_, key) &= ~TLSMAP_MASK(key);
|
||||
s_tls_map_.key_destructors[key] = NULL;
|
||||
}
|
||||
|
||||
bool IsInUse(pthread_key_t key) {
|
||||
return (TLSMAP_WORD(s_tls_map_, key) & TLSMAP_MASK(key)) != 0;
|
||||
}
|
||||
|
||||
void SetInUse(pthread_key_t key, void (*key_destructor)(void*)) {
|
||||
TLSMAP_WORD(s_tls_map_, key) |= TLSMAP_MASK(key);
|
||||
s_tls_map_.key_destructors[key] = key_destructor;
|
||||
}
|
||||
|
||||
// Called from pthread_exit() to remove all TLS key data
|
||||
// from this thread's TLS area. This must call the destructor of all keys
|
||||
// that have a non-NULL data value and a non-NULL destructor.
|
||||
void CleanAll() {
|
||||
void** tls = (void**)__get_tls();
|
||||
|
||||
// Because destructors can do funky things like deleting/creating other
|
||||
// keys, we need to implement this in a loop.
|
||||
for (int rounds = PTHREAD_DESTRUCTOR_ITERATIONS; rounds > 0; --rounds) {
|
||||
size_t called_destructor_count = 0;
|
||||
for (int key = 0; key < BIONIC_TLS_SLOTS; ++key) {
|
||||
if (IsInUse(key)) {
|
||||
void* data = tls[key];
|
||||
void (*key_destructor)(void*) = s_tls_map_.key_destructors[key];
|
||||
|
||||
if (data != NULL && key_destructor != NULL) {
|
||||
// we need to clear the key data now, this will prevent the
|
||||
// destructor (or a later one) from seeing the old value if
|
||||
// it calls pthread_getspecific() for some odd reason
|
||||
|
||||
// we do not do this if 'key_destructor == NULL' just in case another
|
||||
// destructor function might be responsible for manually
|
||||
// releasing the corresponding data.
|
||||
tls[key] = NULL;
|
||||
|
||||
// because the destructor is free to call pthread_key_create
|
||||
// and/or pthread_key_delete, we need to temporarily unlock
|
||||
// the TLS map
|
||||
Unlock();
|
||||
(*key_destructor)(data);
|
||||
Lock();
|
||||
++called_destructor_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we didn't call any destructors, there is no need to check the TLS data again.
|
||||
if (called_destructor_count == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static tls_map_t s_tls_map_;
|
||||
static pthread_mutex_t s_tls_map_lock_;
|
||||
|
||||
void Lock() {
|
||||
pthread_mutex_lock(&s_tls_map_lock_);
|
||||
}
|
||||
|
||||
void Unlock() {
|
||||
pthread_mutex_unlock(&s_tls_map_lock_);
|
||||
}
|
||||
};
|
||||
|
||||
tls_map_t ScopedTlsMapAccess::s_tls_map_;
|
||||
pthread_mutex_t ScopedTlsMapAccess::s_tls_map_lock_;
|
||||
|
||||
__LIBC_HIDDEN__ void pthread_key_clean_all() {
|
||||
ScopedTlsMapAccess tls_map;
|
||||
tls_map.CleanAll();
|
||||
}
|
||||
|
||||
int pthread_key_create(pthread_key_t* key, void (*key_destructor)(void*)) {
|
||||
ScopedTlsMapAccess tls_map;
|
||||
return tls_map.CreateKey(key, key_destructor);
|
||||
}
|
||||
|
||||
// Deletes a pthread_key_t. note that the standard mandates that this does
|
||||
// not call the destructors for non-NULL key values. Instead, it is the
|
||||
// responsibility of the caller to properly dispose of the corresponding data
|
||||
// and resources, using any means it finds suitable.
|
||||
int pthread_key_delete(pthread_key_t key) {
|
||||
ScopedTlsMapAccess tls_map;
|
||||
|
||||
if (!IsValidUserKey(key) || !tls_map.IsInUse(key)) {
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
// Clear value in all threads.
|
||||
pthread_mutex_lock(&gThreadListLock);
|
||||
for (pthread_internal_t* t = gThreadList; t != NULL; t = t->next) {
|
||||
// Avoid zombie threads with a negative 'join_count'. These are really
|
||||
// already dead and don't have a TLS area anymore.
|
||||
|
||||
// Similarly, it is possible to have t->tls == NULL for threads that
|
||||
// were just recently created through pthread_create() but whose
|
||||
// startup trampoline (__thread_entry) hasn't been run yet by the
|
||||
// scheduler. t->tls will also be NULL after it's stack has been
|
||||
// unmapped but before the ongoing pthread_join() is finished.
|
||||
// so check for this too.
|
||||
if (t->join_count < 0 || !t->tls) {
|
||||
continue;
|
||||
}
|
||||
|
||||
t->tls[key] = NULL;
|
||||
}
|
||||
tls_map.DeleteKey(key);
|
||||
|
||||
pthread_mutex_unlock(&gThreadListLock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void* pthread_getspecific(pthread_key_t key) {
|
||||
if (!IsValidUserKey(key)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// For performance reasons, we do not lock/unlock the global TLS map
|
||||
// to check that the key is properly allocated. If the key was not
|
||||
// allocated, the value read from the TLS should always be NULL
|
||||
// due to pthread_key_delete() clearing the values for all threads.
|
||||
return (void *)(((unsigned *)__get_tls())[key]);
|
||||
}
|
||||
|
||||
int pthread_setspecific(pthread_key_t key, const void* ptr) {
|
||||
ScopedTlsMapAccess tls_map;
|
||||
|
||||
if (!IsValidUserKey(key) || !tls_map.IsInUse(key)) {
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
((uint32_t *)__get_tls())[key] = (uint32_t)ptr;
|
||||
return 0;
|
||||
}
|
|
@ -55,7 +55,6 @@
|
|||
|
||||
/* the following depends on our implementation */
|
||||
#define SYSTEM_ATEXIT_MAX 65536 /* our implementation is unlimited */
|
||||
#define SYSTEM_THREAD_KEYS_MAX BIONIC_TLS_SLOTS
|
||||
#define SYSTEM_THREAD_STACK_MIN 32768 /* lower values may be possible, but be conservative */
|
||||
#define SYSTEM_THREAD_THREADS_MAX 2048 /* really unlimited */
|
||||
|
||||
|
@ -302,10 +301,13 @@ int sysconf(int name) {
|
|||
// GETPW_R_SIZE_MAX ?
|
||||
|
||||
case _SC_LOGIN_NAME_MAX: return SYSTEM_LOGIN_NAME_MAX;
|
||||
#ifdef _POSIX_THREAD_DESTRUCTOR_ITERATIONS
|
||||
case _SC_THREAD_DESTRUCTOR_ITERATIONS: return _POSIX_THREAD_DESTRUCTOR_ITERATIONS;
|
||||
#endif
|
||||
case _SC_THREAD_KEYS_MAX: return SYSTEM_THREAD_KEYS_MAX;
|
||||
|
||||
case _SC_THREAD_DESTRUCTOR_ITERATIONS:
|
||||
return _POSIX_THREAD_DESTRUCTOR_ITERATIONS;
|
||||
|
||||
case _SC_THREAD_KEYS_MAX:
|
||||
return (BIONIC_TLS_SLOTS - TLS_SLOT_FIRST_USER_SLOT);
|
||||
|
||||
case _SC_THREAD_STACK_MIN: return SYSTEM_THREAD_STACK_MIN;
|
||||
case _SC_THREAD_THREADS_MAX: return SYSTEM_THREAD_THREADS_MAX;
|
||||
case _SC_TTY_NAME_MAX: return SYSTEM_TTY_NAME_MAX;
|
||||
|
|
|
@ -175,6 +175,12 @@
|
|||
#define _POSIX_SAVED_IDS 1 /* saved user ids is a Linux feature */
|
||||
#define _POSIX_JOB_CONTROL 1 /* job control is a Linux feature */
|
||||
|
||||
#define _POSIX_THREAD_DESTRUCTOR_ITERATIONS 4 /* the minimum mandated by POSIX */
|
||||
#define PTHREAD_DESTRUCTOR_ITERATIONS 4
|
||||
#define _POSIX_THREAD_KEYS_MAX 128 /* the minimum mandated by POSIX */
|
||||
/* TODO: our PTHREAD_KEYS_MAX is currently too low to be posix compliant! */
|
||||
#define _POSIX_THREAD_THREADS_MAX 64 /* the minimum mandated by POSIX */
|
||||
#define PTHREAD_THREADS_MAX /* bionic has no specific limit */
|
||||
|
||||
|
||||
#endif
|
||||
|
|
|
@ -47,27 +47,25 @@ __BEGIN_DECLS
|
|||
#define BIONIC_TLS_SLOTS 64
|
||||
|
||||
/* Well-known TLS slots. What data goes in which slot is arbitrary unless otherwise noted. */
|
||||
#define TLS_SLOT_SELF 0 /* The kernel requires this specific slot for x86. */
|
||||
#define TLS_SLOT_THREAD_ID 1
|
||||
#define TLS_SLOT_ERRNO 2
|
||||
enum {
|
||||
TLS_SLOT_SELF = 0, /* The kernel requires this specific slot for x86. */
|
||||
TLS_SLOT_THREAD_ID,
|
||||
TLS_SLOT_ERRNO,
|
||||
TLS_SLOT_OPENGL_API = 3,
|
||||
TLS_SLOT_OPENGL = 4,
|
||||
TLS_SLOT_STACK_GUARD = 5, /* GCC requires this specific slot for x86. */
|
||||
TLS_SLOT_DLERROR,
|
||||
|
||||
#define TLS_SLOT_OPENGL_API 3
|
||||
#define TLS_SLOT_OPENGL 4
|
||||
|
||||
#define TLS_SLOT_STACK_GUARD 5 /* GCC requires this specific slot for x86. */
|
||||
#define TLS_SLOT_DLERROR 6
|
||||
|
||||
#define TLS_SLOT_MAX_WELL_KNOWN TLS_SLOT_DLERROR
|
||||
TLS_SLOT_FIRST_USER_SLOT /* Must come last! */
|
||||
};
|
||||
|
||||
/* This slot is only used to pass information from the dynamic linker to
|
||||
* libc.so when the C library is loaded in to memory. The C runtime init
|
||||
* function will then clear it. Since its use is extremely temporary,
|
||||
* we reuse an existing location.
|
||||
* we reuse an existing location that isn't needed during libc startup.
|
||||
*/
|
||||
#define TLS_SLOT_BIONIC_PREINIT TLS_SLOT_OPENGL_API
|
||||
|
||||
#define TLS_DEFAULT_ALLOC_MAP 0x0000001F
|
||||
|
||||
/* set the Thread Local Storage, must contain at least BIONIC_TLS_SLOTS pointers */
|
||||
extern void __init_tls(void** tls, void* thread_info);
|
||||
|
||||
|
|
|
@ -28,6 +28,25 @@ TEST(pthread, pthread_key_create) {
|
|||
ASSERT_EQ(EINVAL, pthread_key_delete(key));
|
||||
}
|
||||
|
||||
TEST(pthread, pthread_key_create_lots) {
|
||||
// We can allocate _SC_THREAD_KEYS_MAX keys.
|
||||
std::vector<pthread_key_t> keys;
|
||||
for (int i = 0; i < sysconf(_SC_THREAD_KEYS_MAX); ++i) {
|
||||
pthread_key_t key;
|
||||
ASSERT_EQ(0, pthread_key_create(&key, NULL));
|
||||
keys.push_back(key);
|
||||
}
|
||||
|
||||
// ...and that really is the maximum.
|
||||
pthread_key_t key;
|
||||
ASSERT_EQ(EAGAIN, pthread_key_create(&key, NULL));
|
||||
|
||||
// (Don't leak all those keys!)
|
||||
for (size_t i = 0; i < keys.size(); ++i) {
|
||||
ASSERT_EQ(0, pthread_key_delete(keys[i]));
|
||||
}
|
||||
}
|
||||
|
||||
static void* IdFn(void* arg) {
|
||||
return arg;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue