From 6260553d48f6fd87ca220270bea8bafdde5726ec Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Mon, 11 Feb 2013 20:18:16 +0000 Subject: [PATCH] Revert "Pull the pthread_key_t functions out of pthread.c." This reverts commit ad59322ae432d11ff36dcf046016af8cfe45fbe4 somehow my unfinished libm/Android.mk change got into here. Change-Id: I46be626c5269d60fb1ced9862f2ebaa380b4e0af --- libc/Android.mk | 9 +- libc/bionic/pthread.c | 304 ++++++++++++++++++++++++++++++++- libc/bionic/pthread_internal.h | 5 - libc/bionic/pthread_key.cpp | 280 ------------------------------ libc/bionic/sysconf.cpp | 12 +- libc/include/sys/limits.h | 6 - libc/private/bionic_tls.h | 24 +-- libm/Android.mk | 78 +++++---- tests/pthread_test.cpp | 19 --- 9 files changed, 367 insertions(+), 370 deletions(-) delete mode 100644 libc/bionic/pthread_key.cpp diff --git a/libc/Android.mk b/libc/Android.mk index d21878ae8..c7828cfd6 100644 --- a/libc/Android.mk +++ b/libc/Android.mk @@ -406,8 +406,7 @@ libc_common_src_files += \ bionic/ptrace.c.arm libc_static_common_src_files += \ - bionic/pthread.c.arm \ - bionic/pthread_key.cpp.arm \ + bionic/pthread.c.arm \ # these are used by the static and dynamic versions of the libc # respectively @@ -447,8 +446,7 @@ libc_common_src_files += \ bionic/ptrace.c libc_static_common_src_files += \ - bionic/pthread.c \ - bionic/pthread_key.cpp \ + bionic/pthread.c \ libc_arch_static_src_files := \ bionic/dl_iterate_phdr_static.c @@ -494,8 +492,7 @@ libc_common_src_files += \ bionic/ptrace.c libc_static_common_src_files += \ - bionic/pthread.c - bionic/pthread_key.cpp \ + bionic/pthread.c libc_arch_static_src_files := \ bionic/dl_iterate_phdr_static.c diff --git a/libc/bionic/pthread.c b/libc/bionic/pthread.c index e1ace7dc2..88a972d3c 100644 --- a/libc/bionic/pthread.c +++ b/libc/bionic/pthread.c @@ -101,8 +101,8 @@ static const pthread_attr_t gDefaultPthreadAttr = { .sched_priority = 0 }; -__LIBC_HIDDEN__ pthread_internal_t* gThreadList = NULL; -__LIBC_HIDDEN__ pthread_mutex_t gThreadListLock = PTHREAD_MUTEX_INITIALIZER; +static pthread_internal_t* gThreadList = NULL; +static 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,6 +550,9 @@ 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(); @@ -1777,6 +1780,303 @@ 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 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 , but it isn't extern int tgkill(int tgid, int tid, int sig); diff --git a/libc/bionic/pthread_internal.h b/libc/bionic/pthread_internal.h index 24b420cf7..bc682917d 100644 --- a/libc/bionic/pthread_internal.h +++ b/libc/bionic/pthread_internal.h @@ -60,11 +60,6 @@ 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 ) diff --git a/libc/bionic/pthread_key.cpp b/libc/bionic/pthread_key.cpp deleted file mode 100644 index 00dacba96..000000000 --- a/libc/bionic/pthread_key.cpp +++ /dev/null @@ -1,280 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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; -} diff --git a/libc/bionic/sysconf.cpp b/libc/bionic/sysconf.cpp index f4845e162..6555a66fa 100644 --- a/libc/bionic/sysconf.cpp +++ b/libc/bionic/sysconf.cpp @@ -55,6 +55,7 @@ /* 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 */ @@ -301,13 +302,10 @@ int sysconf(int name) { // GETPW_R_SIZE_MAX ? case _SC_LOGIN_NAME_MAX: return SYSTEM_LOGIN_NAME_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); - +#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_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; diff --git a/libc/include/sys/limits.h b/libc/include/sys/limits.h index 2d0d11ebc..5b127ebb2 100644 --- a/libc/include/sys/limits.h +++ b/libc/include/sys/limits.h @@ -175,12 +175,6 @@ #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 diff --git a/libc/private/bionic_tls.h b/libc/private/bionic_tls.h index edf878fb7..b983fbc95 100644 --- a/libc/private/bionic_tls.h +++ b/libc/private/bionic_tls.h @@ -47,25 +47,27 @@ __BEGIN_DECLS #define BIONIC_TLS_SLOTS 64 /* Well-known TLS slots. What data goes in which slot is arbitrary unless otherwise noted. */ -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_SELF 0 /* The kernel requires this specific slot for x86. */ +#define TLS_SLOT_THREAD_ID 1 +#define TLS_SLOT_ERRNO 2 - TLS_SLOT_FIRST_USER_SLOT /* Must come last! */ -}; +#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 /* 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 that isn't needed during libc startup. + * we reuse an existing location. */ #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); diff --git a/libm/Android.mk b/libm/Android.mk index 26eac2af2..0d2c843ae 100644 --- a/libm/Android.mk +++ b/libm/Android.mk @@ -1,9 +1,5 @@ LOCAL_PATH:= $(call my-dir) -# -# libm source files. -# - # TODO: these come from from upstream's libc, not libm! libm_common_src_files := \ digittoint.c \ @@ -14,10 +10,6 @@ libm_common_src_files := \ libm_common_src_files += \ sincos.c \ -# TODO: on Android, "long double" is just double. -libm_common_src_files += \ - fake_long_double.c - libm_common_src_files += \ upstream-freebsd/lib/msun/bsdsrc/b_exp.c \ upstream-freebsd/lib/msun/bsdsrc/b_log.c \ @@ -185,39 +177,58 @@ libm_common_src_files += \ upstream-freebsd/lib/msun/src/w_drem.c \ upstream-freebsd/lib/msun/src/w_dremf.c \ -# -# Architecture-specific assembly language overrides. -# +libm_common_src_files += fake_long_double.c -define override - $(subst upstream-freebsd/lib/msun/$1,upstream-freebsd/lib/msun/$2,$3) -endef +# TODO: on Android, "long double" is "double". +# upstream-freebsd/lib/msun/src/e_acosl.c \ +# upstream-freebsd/lib/msun/src/e_asinl.c \ +# upstream-freebsd/lib/msun/src/e_atan2l.c \ +# upstream-freebsd/lib/msun/src/e_fmodl.c \ +# upstream-freebsd/lib/msun/src/e_hypotl.c \ +# upstream-freebsd/lib/msun/src/e_remainderl.c \ +# upstream-freebsd/lib/msun/src/e_sqrtl.c \ +# upstream-freebsd/lib/msun/src/s_atanl.c \ +# upstream-freebsd/lib/msun/src/s_cbrtl.c \ +# upstream-freebsd/lib/msun/src/s_ceill.c \ +# upstream-freebsd/lib/msun/src/s_copysignl.c \ +# upstream-freebsd/lib/msun/src/s_cosl.c \ +# upstream-freebsd/lib/msun/src/s_fabsl.c \ +# upstream-freebsd/lib/msun/src/s_floorl.c \ +# upstream-freebsd/lib/msun/src/s_fmal.c \ +# upstream-freebsd/lib/msun/src/s_fmaxl.c \ +# upstream-freebsd/lib/msun/src/s_fminl.c \ +# upstream-freebsd/lib/msun/src/s_frexpl.c \ +# upstream-freebsd/lib/msun/src/s_ilogbl.c \ +# upstream-freebsd/lib/msun/src/s_llrintl.c \ +# upstream-freebsd/lib/msun/src/s_llroundl.c \ +# upstream-freebsd/lib/msun/src/s_logbl.c \ +# upstream-freebsd/lib/msun/src/s_lrintl.c \ +# upstream-freebsd/lib/msun/src/s_lroundl.c \ +# upstream-freebsd/lib/msun/src/s_modfl.c \ +# upstream-freebsd/lib/msun/src/s_nextafterl.c \ +# upstream-freebsd/lib/msun/src/s_nexttoward.c \ +# upstream-freebsd/lib/msun/src/s_remquol.c \ +# upstream-freebsd/lib/msun/src/s_rintl.c \ +# upstream-freebsd/lib/msun/src/s_roundl.c \ +# upstream-freebsd/lib/msun/src/s_scalbnl.c \ +# upstream-freebsd/lib/msun/src/s_sinl.c \ +# upstream-freebsd/lib/msun/src/s_tanl.c \ +# upstream-freebsd/lib/msun/src/s_truncl.c \ -libm_$(TARGET_ARCH)_src_files := $(call override,src/e_log10.c,i387/e_log10.S,$(libm_common_src_files)) -libm_$(TARGET_ARCH)_src_files := $(call override,src/e_sqrt.c,i387/e_sqrt.S,$(libm_$(TARGET_ARCH)_src_files)) -libm_$(TARGET_ARCH)_src_files := $(call override,src/s_logb.c,i387/s_logb.S,$(libm_$(TARGET_ARCH)_src_files)) -# libm_$(TARGET_ARCH)_src_files = $(libm_common_src_files) # Uncomment to force C-only! +# TODO: re-enable i387/e_sqrtf.S for x86, and maybe others. -$(warning $(libm_$(TARGET_ARCH)_src_files)) - -# TODO: handle fenv.c better; ARM and MIPS are identical, and the x86 one -# should come from upstream-freebsd. -libm_arm_src_files += arm/fenv.c -libm_x86_src_files += i387/fenv.c -libm_mips_src_files += mips/fenv.c - -# -# Architecture-specific flags and include directories. -# +libm_common_cflags := -DFLT_EVAL_METHOD=0 +libm_common_includes := $(LOCAL_PATH)/upstream-freebsd/lib/msun/src/ libm_arm_includes := $(LOCAL_PATH)/arm +libm_arm_src_files := arm/fenv.c libm_x86_includes := $(LOCAL_PATH)/i386 $(LOCAL_PATH)/i387 +libm_x86_src_files := i387/fenv.c libm_mips_cflags := -fno-builtin-rintf -fno-builtin-rint libm_mips_includes := $(LOCAL_PATH)/mips - -libm_common_cflags := -DFLT_EVAL_METHOD=0 +libm_mips_src_files := mips/fenv.c # # libm.a for target. @@ -226,10 +237,9 @@ include $(CLEAR_VARS) LOCAL_MODULE:= libm LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk LOCAL_ARM_MODE := arm -LOCAL_ASFLAGS := '-D__FBSDID(s)=' LOCAL_CFLAGS := $(libm_common_cflags) $(libm_$(TARGET_ARCH)_cflags) -LOCAL_C_INCLUDES += $(libm_$(TARGET_ARCH)_includes) -LOCAL_SRC_FILES := $(libm_$(TARGET_ARCH)_src_files) +LOCAL_C_INCLUDES += $(libm_common_includes) $(libm_$(TARGET_ARCH)_includes) +LOCAL_SRC_FILES := $(libm_common_src_files) $(libm_$(TARGET_ARCH)_src_files) LOCAL_SYSTEM_SHARED_LIBRARIES := libc include $(BUILD_STATIC_LIBRARY) diff --git a/tests/pthread_test.cpp b/tests/pthread_test.cpp index 2cf45f30d..3e144dbaa 100644 --- a/tests/pthread_test.cpp +++ b/tests/pthread_test.cpp @@ -28,25 +28,6 @@ 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 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; }