877ec6d904
Let the kernel keep pthread_internal_t::tid updated, including across forks and for the main thread. This then lets us fix pthread_join to only return after the thread has really exited. Also fix the thread attributes of the main thread so we don't unmap the main thread's stack (which is really owned by the dynamic linker and contains things like environment variables), which fixes crashes when joining with an exited main thread and also fixes problems reported publicly with accessing environment variables after the main thread exits (for which I've added a new unit test). In passing I also fixed a bug where if the clone(2) inside pthread_create(3) fails, we'd unmap the child's stack and TLS (which contains the mutex) and then try to unlock the mutex. Boom! It wasn't until after I'd uploaded the fix for this that I came across a new public bug reporting this exact failure. Bug: 8206355 Bug: 11693195 Bug: https://code.google.com/p/android/issues/detail?id=57421 Bug: https://code.google.com/p/android/issues/detail?id=62392 Change-Id: I2af9cf6e8ae510a67256ad93cad891794ed0580b
254 lines
8.8 KiB
C++
254 lines
8.8 KiB
C++
/*
|
|
* 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 <pthread.h>
|
|
|
|
#include "private/bionic_tls.h"
|
|
#include "pthread_internal.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 = __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_);
|
|
}
|
|
};
|
|
|
|
__LIBC_HIDDEN__ tls_map_t ScopedTlsMapAccess::s_tls_map_;
|
|
__LIBC_HIDDEN__ 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) {
|
|
// Skip zombie threads. They don't have a valid TLS area any more.
|
|
// Similarly, it is possible to have t->tls == NULL for threads that
|
|
// were just recently created through pthread_create() but whose
|
|
// startup trampoline (__pthread_start) hasn't been run yet by the
|
|
// scheduler. t->tls will also be NULL after a thread's stack has been
|
|
// unmapped but before the ongoing pthread_join() is finished.
|
|
if (t->tid == 0 || t->tls == NULL) {
|
|
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 __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;
|
|
}
|
|
|
|
__get_tls()[key] = const_cast<void*>(ptr);
|
|
return 0;
|
|
}
|