Merge "Pull the pthread_key_t functions out of pthread.c."
This commit is contained in:
		@@ -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);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,9 @@
 | 
			
		||||
LOCAL_PATH:= $(call my-dir)
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# libm source files.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
# TODO: these come from from upstream's libc, not libm!
 | 
			
		||||
libm_common_src_files := \
 | 
			
		||||
    digittoint.c  \
 | 
			
		||||
@@ -10,6 +14,10 @@ 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 \
 | 
			
		||||
@@ -177,58 +185,39 @@ libm_common_src_files += \
 | 
			
		||||
    upstream-freebsd/lib/msun/src/w_drem.c \
 | 
			
		||||
    upstream-freebsd/lib/msun/src/w_dremf.c \
 | 
			
		||||
 | 
			
		||||
libm_common_src_files += fake_long_double.c
 | 
			
		||||
#
 | 
			
		||||
# Architecture-specific assembly language overrides.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
# 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 \
 | 
			
		||||
define override
 | 
			
		||||
  $(subst upstream-freebsd/lib/msun/$1,upstream-freebsd/lib/msun/$2,$3)
 | 
			
		||||
endef
 | 
			
		||||
 | 
			
		||||
# TODO: re-enable i387/e_sqrtf.S for x86, and maybe others.
 | 
			
		||||
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!
 | 
			
		||||
 | 
			
		||||
libm_common_cflags := -DFLT_EVAL_METHOD=0
 | 
			
		||||
libm_common_includes := $(LOCAL_PATH)/upstream-freebsd/lib/msun/src/
 | 
			
		||||
$(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_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_mips_src_files := mips/fenv.c
 | 
			
		||||
 | 
			
		||||
libm_common_cflags := -DFLT_EVAL_METHOD=0
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# libm.a for target.
 | 
			
		||||
@@ -237,9 +226,10 @@ 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_common_includes) $(libm_$(TARGET_ARCH)_includes)
 | 
			
		||||
LOCAL_SRC_FILES := $(libm_common_src_files) $(libm_$(TARGET_ARCH)_src_files)
 | 
			
		||||
LOCAL_C_INCLUDES += $(libm_$(TARGET_ARCH)_includes)
 | 
			
		||||
LOCAL_SRC_FILES := $(libm_$(TARGET_ARCH)_src_files)
 | 
			
		||||
LOCAL_SYSTEM_SHARED_LIBRARIES := libc
 | 
			
		||||
include $(BUILD_STATIC_LIBRARY)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user