am 6b73d13f: am 2d3e7233: Merge "Revert "Revert "Pull the pthread_key_t functions out of pthread.c."""

# Via Android Git Automerger (1) and others
* commit '6b73d13fa414afeecba6718bf724e8ac922bac39':
  Revert "Revert "Pull the pthread_key_t functions out of pthread.c.""
This commit is contained in:
Elliott Hughes 2013-02-11 17:34:32 -08:00 committed by Android Git Automerger
commit 5bb4f54b4d
8 changed files with 336 additions and 323 deletions

View file

@ -407,6 +407,7 @@ libc_common_src_files += \
libc_static_common_src_files += \ 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 # these are used by the static and dynamic versions of the libc
# respectively # respectively
@ -447,6 +448,7 @@ libc_common_src_files += \
libc_static_common_src_files += \ libc_static_common_src_files += \
bionic/pthread.c \ bionic/pthread.c \
bionic/pthread_key.cpp \
libc_arch_static_src_files := \ libc_arch_static_src_files := \
bionic/dl_iterate_phdr_static.c bionic/dl_iterate_phdr_static.c
@ -493,6 +495,7 @@ libc_common_src_files += \
libc_static_common_src_files += \ libc_static_common_src_files += \
bionic/pthread.c bionic/pthread.c
bionic/pthread_key.cpp \
libc_arch_static_src_files := \ libc_arch_static_src_files := \
bionic/dl_iterate_phdr_static.c bionic/dl_iterate_phdr_static.c

View file

@ -101,8 +101,8 @@ static const pthread_attr_t gDefaultPthreadAttr = {
.sched_priority = 0 .sched_priority = 0
}; };
static pthread_internal_t* gThreadList = NULL; __LIBC_HIDDEN__ pthread_internal_t* gThreadList = NULL;
static pthread_mutex_t gThreadListLock = PTHREAD_MUTEX_INITIALIZER; __LIBC_HIDDEN__ pthread_mutex_t gThreadListLock = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t gDebuggerNotificationLock = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t gDebuggerNotificationLock = PTHREAD_MUTEX_INITIALIZER;
static void _pthread_internal_remove_locked(pthread_internal_t* thread) { 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); 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) void pthread_exit(void * retval)
{ {
pthread_internal_t* thread = __get_thread(); 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 // man says this should be in <linux/unistd.h>, but it isn't
extern int tgkill(int tgid, int tid, int sig); extern int tgkill(int tgid, int tid, int sig);

View file

@ -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 ); void _pthread_internal_add( pthread_internal_t* thread );
pthread_internal_t* __get_thread(void); 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 */ /* needed by posix-timers.c */
static __inline__ void timespec_add( struct timespec* a, const struct timespec* b ) static __inline__ void timespec_add( struct timespec* a, const struct timespec* b )

280
libc/bionic/pthread_key.cpp Normal file
View 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;
}

View file

@ -55,7 +55,6 @@
/* the following depends on our implementation */ /* the following depends on our implementation */
#define SYSTEM_ATEXIT_MAX 65536 /* our implementation is unlimited */ #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_STACK_MIN 32768 /* lower values may be possible, but be conservative */
#define SYSTEM_THREAD_THREADS_MAX 2048 /* really unlimited */ #define SYSTEM_THREAD_THREADS_MAX 2048 /* really unlimited */
@ -302,10 +301,13 @@ int sysconf(int name) {
// GETPW_R_SIZE_MAX ? // GETPW_R_SIZE_MAX ?
case _SC_LOGIN_NAME_MAX: return SYSTEM_LOGIN_NAME_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; case _SC_THREAD_DESTRUCTOR_ITERATIONS:
#endif return _POSIX_THREAD_DESTRUCTOR_ITERATIONS;
case _SC_THREAD_KEYS_MAX: return SYSTEM_THREAD_KEYS_MAX;
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_STACK_MIN: return SYSTEM_THREAD_STACK_MIN;
case _SC_THREAD_THREADS_MAX: return SYSTEM_THREAD_THREADS_MAX; case _SC_THREAD_THREADS_MAX: return SYSTEM_THREAD_THREADS_MAX;
case _SC_TTY_NAME_MAX: return SYSTEM_TTY_NAME_MAX; case _SC_TTY_NAME_MAX: return SYSTEM_TTY_NAME_MAX;

View file

@ -175,6 +175,12 @@
#define _POSIX_SAVED_IDS 1 /* saved user ids is a Linux feature */ #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_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 #endif

View file

@ -47,27 +47,25 @@ __BEGIN_DECLS
#define BIONIC_TLS_SLOTS 64 #define BIONIC_TLS_SLOTS 64
/* Well-known TLS slots. What data goes in which slot is arbitrary unless otherwise noted. */ /* 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. */ enum {
#define TLS_SLOT_THREAD_ID 1 TLS_SLOT_SELF = 0, /* The kernel requires this specific slot for x86. */
#define TLS_SLOT_ERRNO 2 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 TLS_SLOT_FIRST_USER_SLOT /* Must come last! */
#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
/* This slot is only used to pass information from the dynamic linker to /* 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 * 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, * 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_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 */ /* set the Thread Local Storage, must contain at least BIONIC_TLS_SLOTS pointers */
extern void __init_tls(void** tls, void* thread_info); extern void __init_tls(void** tls, void* thread_info);

View file

@ -28,6 +28,25 @@ TEST(pthread, pthread_key_create) {
ASSERT_EQ(EINVAL, pthread_key_delete(key)); 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) { static void* IdFn(void* arg) {
return arg; return arg;
} }